diff options
author | sam <sam@FreeBSD.org> | 2007-07-09 16:15:06 +0000 |
---|---|---|
committer | sam <sam@FreeBSD.org> | 2007-07-09 16:15:06 +0000 |
commit | 1bf2fd00c50865c26197a0fb9ce70f417b9fa121 (patch) | |
tree | c6f336fc28b042f00efc2373c71fceadfa394e52 /contrib/hostapd | |
parent | 620bfba12034be7d2ad4a357063d609ff5b6e63a (diff) | |
download | FreeBSD-src-1bf2fd00c50865c26197a0fb9ce70f417b9fa121.zip FreeBSD-src-1bf2fd00c50865c26197a0fb9ce70f417b9fa121.tar.gz |
Import of hostapd 0.5.8
Diffstat (limited to 'contrib/hostapd')
159 files changed, 32275 insertions, 6309 deletions
diff --git a/contrib/hostapd/COPYING b/contrib/hostapd/COPYING index 60549be..14f5453 100644 --- a/contrib/hostapd/COPYING +++ b/contrib/hostapd/COPYING @@ -2,7 +2,7 @@ Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. - 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. @@ -305,7 +305,7 @@ the "copyright" line and a pointer to where the full notice is found. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software - Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA Also add information on how to contact you by electronic and paper mail. diff --git a/contrib/hostapd/ChangeLog b/contrib/hostapd/ChangeLog index f7bd410..73c63a8 100644 --- a/contrib/hostapd/ChangeLog +++ b/contrib/hostapd/ChangeLog @@ -1,9 +1,136 @@ ChangeLog for hostapd -2006-02-08 - v0.4.8 +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 + * updated EAP-SAKE to RFC 4763 and the IANA-allocated EAP type 48 + * updated EAP-PSK to use the IANA-allocated EAP type 47 + * fixed EAP-PSK bit ordering of the Flags field + * fixed configuration reloading (SIGHUP) to re-initialize WPA PSKs + by reading wpa_psk_file [Bug 181] + * fixed EAP-TTLS AVP parser processing for too short AVP lengths + * fixed IPv6 connection to RADIUS accounting server + +2006-11-24 - v0.5.6 + * added support for configuring and controlling multiple BSSes per + radio interface (bss=<ifname> in hostapd.conf); this is only + available with Devicescape and test driver interfaces + * fixed PMKSA cache update in the end of successful RSN + pre-authentication + * added support for dynamic VLAN configuration (i.e., selecting VLAN-ID + for each STA based on RADIUS Access-Accept attributes); this requires + VLAN support from the kernel driver/802.11 stack and this is + currently only available with Devicescape and test driver interfaces + * driver_madwifi: fixed configuration of unencrypted modes (plaintext + and IEEE 802.1X without WEP) + * removed STAKey handshake since PeerKey handshake has replaced it in + IEEE 802.11ma and there are no known deployments of STAKey + * updated EAP Generalized Pre-Shared Key (EAP-GPSK) to use the latest + draft (draft-ietf-emu-eap-gpsk-01.txt) + * added preliminary implementation of IEEE 802.11w/D1.0 (management + frame protection) + (Note: this requires driver support to work properly.) + (Note2: IEEE 802.11w is an unapproved draft and subject to change.) + * hlr_auc_gw: added support for GSM-Milenage (for EAP-SIM) + * hlr_auc_gw: added support for reading per-IMSI Milenage keys and + parameters from a text file to make it possible to implement proper + GSM/UMTS authentication server for multiple SIM/USIM cards using + EAP-SIM/EAP-AKA + * fixed session timeout processing with drivers that do not use + ieee802_11.c (e.g., madwifi) + +2006-08-27 - v0.5.5 + * added 'hostapd_cli new_sta <addr>' command for adding a new STA into + hostapd (e.g., to initialize wired network authentication based on an + external signal) + * fixed hostapd to add PMKID KDE into 4-Way Handshake Message 1 when + using WPA2 even if PMKSA caching is not used + * added -P<pid file> argument for hostapd to write the current process + id into a file + * added support for RADIUS Authentication Server MIB (RFC 2619) + +2006-06-20 - v0.5.4 + * fixed nt_password_hash build [Bug 144] + * added PeerKey handshake implementation for IEEE 802.11e + direct link setup (DLS) to replace STAKey handshake + * added support for EAP Generalized Pre-Shared Key (EAP-GPSK, + draft-clancy-emu-eap-shared-secret-00.txt) + * fixed a segmentation fault when RSN pre-authentication was completed + successfully [Bug 152] + +2006-04-27 - v0.5.3 + * do not build nt_password_hash and hlr_auc_gw by default to avoid + requiring a TLS library for a successful build; these programs can be + build with 'make nt_password_hash' and 'make hlr_auc_gw' + * added a new configuration option, eapol_version, that can be used to + set EAPOL version to 1 (default is 2) to work around broken client + implementations that drop EAPOL frames which use version number 2 + [Bug 89] + * added support for EAP-SAKE (no EAP method number allocated yet, so + this is using the same experimental type 255 as EAP-PSK) + * fixed EAP-MSCHAPv2 message length validation + +2006-03-19 - v0.5.2 * fixed stdarg use in hostapd_logger(): if both stdout and syslog logging was enabled, hostapd could trigger a segmentation fault in vsyslog on some CPU -- C library combinations + * moved HLR/AuC gateway implementation for EAP-SIM/AKA into an external + program to make it easier to use for implementing real SS7 gateway; + eap_sim_db is not anymore used as a file name for GSM authentication + triplets; instead, it is path to UNIX domain socket that will be used + to communicate with the external gateway program (e.g., hlr_auc_gw) + * added example HLR/AuC gateway implementation, hlr_auc_gw, that uses + local information (GSM authentication triplets from a text file and + hardcoded AKA authentication data); this can be used to test EAP-SIM + and EAP-AKA + * added Milenage algorithm (example 3GPP AKA algorithm) to hlr_auc_gw + to make it possible to test EAP-AKA with real USIM cards (this is + disabled by default; define AKA_USE_MILENAGE when building hlr_auc_gw + to enable this) + * driver_madwifi: added support for getting station RSN IE from + madwifi-ng svn r1453 and newer; this fixes RSN that was apparently + broken with earlier change (r1357) in the driver + * changed EAP method registration to use a dynamic list of methods + instead of a static list generated at build time + * fixed WPA message 3/4 not to encrypt Key Data field (WPA IE) + [Bug 125] + * added ap_max_inactivity configuration parameter + +2006-01-29 - v0.5.1 + * driver_test: added better support for multiple APs and STAs by using + a directory with sockets that include MAC address for each device in + the name (test_socket=DIR:/tmp/test) + * added support for EAP expanded type (vendor specific EAP methods) + +2005-12-18 - v0.5.0 (beginning of 0.5.x development releases) + * added experimental STAKey handshake implementation for IEEE 802.11e + direct link setup (DLS); note: this is disabled by default in both + build and runtime configuration (can be enabled with CONFIG_STAKEY=y + and stakey=1) + * added support for EAP methods to use callbacks to external programs + by buffering a pending request and processing it after the EAP method + is ready to continue + * improved EAP-SIM database interface to allow external request to GSM + HLR/AuC without blocking hostapd process + * added support for using EAP-SIM pseudonyms and fast re-authentication + * added support for EAP-AKA in the integrated EAP authenticator + * added support for matching EAP identity prefixes (e.g., "1"*) in EAP + user database to allow EAP-SIM/AKA selection without extra roundtrip + for EAP-Nak negotiation + * added support for storing EAP user password as NtPasswordHash instead + of plaintext password when using MSCHAP or MSCHAPv2 for + authentication (hash:<16-octet hex value>); added nt_password_hash + tool for hashing password to generate NtPasswordHash 2005-11-20 - v0.4.7 (beginning of 0.4.x stable releases) * driver_wired: fixed EAPOL sending to optionally use PAE group address diff --git a/contrib/hostapd/FREEBSD-Xlist b/contrib/hostapd/FREEBSD-Xlist index cba8df0..1f54452 100644 --- a/contrib/hostapd/FREEBSD-Xlist +++ b/contrib/hostapd/FREEBSD-Xlist @@ -2,9 +2,16 @@ $FreeBSD$ .cvsignore driver.c driver_bsd.c +driver_devicescape.c driver_madwifi.c driver_prism54.c -l2_packet.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 index 5efe2f5..0892205 100644 --- a/contrib/hostapd/FREEBSD-upgrade +++ b/contrib/hostapd/FREEBSD-upgrade @@ -6,12 +6,12 @@ WPA/802.1x Authenticator For the import files and directories were pruned by: - tar -X FREEBSD-Xlist -zxf hostapd-0.3.7.tar.gz + tar -X FREEBSD-Xlist -zxf hostapd-0.5.8.tar.gz then imported by: - cvs import -m 'Import of hostapd 0.3.7' \ - src/contrib/hostapd MALINEN v0_3_7 + 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 @@ -21,4 +21,4 @@ All local changes should be submitted to Jouni Malinen for inclusion in the next vendor release. sam@FreeBSD.org -4-June-2005 +7-July-2007 diff --git a/contrib/hostapd/Makefile b/contrib/hostapd/Makefile index 276baee..c98922b 100644 --- a/contrib/hostapd/Makefile +++ b/contrib/hostapd/Makefile @@ -1,6 +1,5 @@ CC=gcc DIR_WPA_SUPPLICANT=. -DIR_HOSTAP=. ifndef CFLAGS CFLAGS = -MMD -O2 -Wall -g @@ -11,18 +10,41 @@ endif CFLAGS += -DHOSTAPD_DUMP_STATE # Include directories for CVS version -CFLAGS += -I. -I$(DIR_HOSTAP) -I../utils -I$(DIR_WPA_SUPPLICANT) +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 -OBJS = hostapd.o eloop.o ieee802_1x.o eapol_sm.o radius.o md5.o rc4.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 + +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 + 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 --include .config +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 @@ -34,6 +56,15 @@ 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 @@ -55,6 +86,11 @@ 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 @@ -70,7 +106,6 @@ endif ifdef CONFIG_L2_PACKET ifdef CONFIG_DNET_PCAP -CFLAGS += -DUSE_DNET_PCAP ifdef CONFIG_L2_FREEBSD LIBS += -lpcap OBJS += $(DIR_WPA_SUPPLICANT)/l2_packet_freebsd.o @@ -122,9 +157,21 @@ endif ifdef CONFIG_EAP_SIM CFLAGS += -DEAP_SIM -OBJS += eap_sim.o $(DIR_WPA_SUPPLICANT)/eap_sim_common.o -# Example EAP-SIM interface for GSM authentication. This can be replaced with -# another file implementating the interface specified in eap_sim_db.h. +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 @@ -138,6 +185,25 @@ 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 @@ -145,15 +211,34 @@ endif ifdef CONFIG_EAP CFLAGS += -DEAP_SERVER -OBJS += eap.o eap_identity.o +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 $(DIR_WPA_SUPPLICANT)/tls_openssl.o +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 @@ -163,10 +248,60 @@ 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 -OBJS += $(DIR_WPA_SUPPLICANT)/ms_funcs.o $(DIR_WPA_SUPPLICANT)/crypto.o +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 @@ -178,6 +313,12 @@ ifdef CONFIG_IPV6 CFLAGS += -DCONFIG_IPV6 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) @@ -201,10 +342,7 @@ driver_conf.c: Makefile .config rm -f driver_conf.c echo '/* THIS FILE AUTOMATICALLY GENERATED, DO NOT EDIT! */' \ > driver_conf.c - echo '#include <stdlib.h>' >> driver_conf.c - echo '#include <stdio.h>' >> driver_conf.c - echo '#include <sys/types.h>' >> driver_conf.c - echo '#include <netinet/in.h>' >> 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 @@ -219,6 +357,9 @@ 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 @@ -238,6 +379,9 @@ 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 @@ -249,7 +393,48 @@ endif 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 *.d driver_conf.c + 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/README b/contrib/hostapd/README index d535462..541fac4 100644 --- a/contrib/hostapd/README +++ b/contrib/hostapd/README @@ -2,8 +2,7 @@ hostapd - user space IEEE 802.11 AP and IEEE 802.1X/WPA/WPA2/EAP Authenticator and RADIUS authentication server ================================================================ -Copyright (c) 2002-2006, Jouni Malinen <jkmaline@cc.hut.fi> and -contributors +Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> and contributors All Rights Reserved. This program is dual-licensed under both the GPL version 2 and BSD @@ -27,13 +26,13 @@ GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software -Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +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 under the terms of BSD -license: +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 @@ -92,9 +91,9 @@ Current hardware/software requirements: madwifi driver for cards based on Atheros chip set (ar521x) (http://sourceforge.net/projects/madwifi/) - Please note that you will need to modify the hostapd Makefile - to use correct path for madwifi driver root directory - (CFLAGS += -I../head line in Makefile). + Please note that you will need to add the correct path for + madwifi driver root directory in .config (see defconfig file for + an example: CFLAGS += -I<path>) Prism54 driver for Intersil/Conexant Prism GT/Duette/Indigo (http://www.prism54.org/) @@ -158,14 +157,6 @@ receives 802.1X (EAPOL) frames from the Supplicant using the wlan#ap device that is also used with IEEE 802.11 management frames. The frames to the Supplicant are sent using the same device. -hostapd includes a minimal colocated Authentication Server for testing -purposes. It only requests the identity of the Supplicant and -authorizes any host that is able to send a valid EAP Response -frame. This can be used for quick testing since it does not require an -external Authentication Server, but it should not be used for any real -authentication purposes since no keys are required and anyone can -authenticate. - The normal configuration of the Authenticator would use an external Authentication Server. hostapd supports RADIUS encapsulation of EAP packets, so the Authentication Server should be a RADIUS server, like diff --git a/contrib/hostapd/accounting.c b/contrib/hostapd/accounting.c index 5ee3d75..b22347b 100644 --- a/contrib/hostapd/accounting.c +++ b/contrib/hostapd/accounting.c @@ -1,7 +1,6 @@ /* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / Accounting - * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * hostapd / RADIUS Accounting + * Copyright (c) 2002-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 @@ -13,18 +12,8 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <netinet/in.h> -#include <string.h> -#include <sys/ioctl.h> -#include <signal.h> +#include "includes.h" #include <assert.h> -#include <time.h> -#include <sys/time.h> -#include <sys/socket.h> - #include "hostapd.h" #include "radius.h" @@ -40,7 +29,13 @@ * input/output octets and updates Acct-{Input,Output}-Gigawords. */ #define ACCT_DEFAULT_UPDATE_INTERVAL 300 -static struct radius_msg * accounting_msg(hostapd *hapd, struct sta_info *sta, +/* 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 struct radius_msg * accounting_msg(struct hostapd_data *hapd, + struct sta_info *sta, int status_type) { struct radius_msg *msg; @@ -131,7 +126,7 @@ static struct radius_msg * accounting_msg(hostapd *hapd, struct sta_info *sta, } snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", - MAC2STR(hapd->own_addr), hapd->conf->ssid); + 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"); @@ -154,7 +149,10 @@ static struct radius_msg * accounting_msg(hostapd *hapd, struct sta_info *sta, goto fail; } - snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b"); + 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))) { printf("Could not add Connect-Info\n"); @@ -211,7 +209,7 @@ static int accounting_sta_update_stats(struct hostapd_data *hapd, static void accounting_interim_update(void *eloop_ctx, void *timeout_ctx) { - hostapd *hapd = eloop_ctx; + struct hostapd_data *hapd = eloop_ctx; struct sta_info *sta = timeout_ctx; int interval; @@ -229,11 +227,11 @@ static void accounting_interim_update(void *eloop_ctx, void *timeout_ctx) } -void accounting_sta_start(hostapd *hapd, struct sta_info *sta) +void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) { struct radius_msg *msg; int interval; - + if (sta->acct_session_started) return; @@ -260,7 +258,8 @@ void accounting_sta_start(hostapd *hapd, struct sta_info *sta) } -void accounting_sta_report(hostapd *hapd, struct sta_info *sta, int stop) +void accounting_sta_report(struct hostapd_data *hapd, struct sta_info *sta, + int stop) { struct radius_msg *msg; int cause = sta->acct_terminate_cause; @@ -360,14 +359,14 @@ void accounting_sta_report(hostapd *hapd, struct sta_info *sta, int stop) } -void accounting_sta_interim(hostapd *hapd, struct sta_info *sta) +void accounting_sta_interim(struct hostapd_data *hapd, struct sta_info *sta) { if (sta->acct_session_started) accounting_sta_report(hapd, sta, 0); } -void accounting_sta_stop(hostapd *hapd, struct sta_info *sta) +void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta) { if (sta->acct_session_started) { accounting_sta_report(hapd, sta, 1); @@ -435,7 +434,7 @@ static void accounting_report_state(struct hostapd_data *hapd, int on) } -int accounting_init(hostapd *hapd) +int accounting_init(struct hostapd_data *hapd) { /* Acct-Session-Id should be unique over reboots. If reliable clock is * not available, this could be replaced with reboot counter, etc. */ @@ -451,7 +450,18 @@ int accounting_init(hostapd *hapd) } -void accounting_deinit(hostapd *hapd) +void accounting_deinit(struct hostapd_data *hapd) { accounting_report_state(hapd, 0); } + + +int accounting_reconfig(struct hostapd_data *hapd, + struct hostapd_config *oldconf) +{ + if (!hapd->radius_client_reconfigured) + return 0; + + accounting_deinit(hapd); + return accounting_init(hapd); +} diff --git a/contrib/hostapd/accounting.h b/contrib/hostapd/accounting.h index 8af3eac..ee2ee64 100644 --- a/contrib/hostapd/accounting.h +++ b/contrib/hostapd/accounting.h @@ -1,13 +1,27 @@ +/* + * hostapd / RADIUS Accounting + * Copyright (c) 2002-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 ACCOUNTING_H #define ACCOUNTING_H - -void accounting_sta_start(hostapd *hapd, struct sta_info *sta); -void accounting_sta_interim(hostapd *hapd, struct sta_info *sta); -void accounting_sta_stop(hostapd *hapd, struct sta_info *sta); +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(hostapd *hapd); -void accounting_deinit(hostapd *hapd); - +int accounting_init(struct hostapd_data *hapd); +void accounting_deinit(struct hostapd_data *hapd); +int accounting_reconfig(struct hostapd_data *hapd, + struct hostapd_config *oldconf); #endif /* ACCOUNTING_H */ diff --git a/contrib/hostapd/aes.c b/contrib/hostapd/aes.c index ce94778..1a2459b 100644 --- a/contrib/hostapd/aes.c +++ b/contrib/hostapd/aes.c @@ -9,7 +9,7 @@ * cost of reduced throughput (quite small difference on Pentium 4, * 10-25% when using -O1 or -O2 optimization) * - * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * 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 @@ -21,6 +21,8 @@ * See README and COPYING for more details. */ +#include "includes.h" + /* * rijndael-alg-fst.c * @@ -1060,7 +1062,7 @@ void * aes_encrypt_init(const u8 *key, size_t len) u32 *rk; if (len != 16) return NULL; - rk = malloc(4 * 44); + rk = os_malloc(4 * 44); if (rk == NULL) return NULL; rijndaelKeySetupEnc(rk, key); @@ -1076,7 +1078,7 @@ void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) void aes_encrypt_deinit(void *ctx) { - free(ctx); + os_free(ctx); } @@ -1085,7 +1087,7 @@ void * aes_decrypt_init(const u8 *key, size_t len) u32 *rk; if (len != 16) return NULL; - rk = malloc(4 * 44); + rk = os_malloc(4 * 44); if (rk == NULL) return NULL; rijndaelKeySetupDec(rk, key); @@ -1101,5 +1103,5 @@ void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) void aes_decrypt_deinit(void *ctx) { - free(ctx); + os_free(ctx); } diff --git a/contrib/hostapd/aes.h b/contrib/hostapd/aes.h new file mode 100644 index 0000000..6b9f414 --- /dev/null +++ b/contrib/hostapd/aes.h @@ -0,0 +1,25 @@ +/* + * AES functions + * 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. + */ + +#ifndef AES_H +#define AES_H + +void * aes_encrypt_init(const u8 *key, size_t len); +void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt); +void aes_encrypt_deinit(void *ctx); +void * aes_decrypt_init(const u8 *key, size_t len); +void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain); +void aes_decrypt_deinit(void *ctx); + +#endif /* AES_H */ diff --git a/contrib/hostapd/aes_wrap.c b/contrib/hostapd/aes_wrap.c index a5925ca..c52e45a 100644 --- a/contrib/hostapd/aes_wrap.c +++ b/contrib/hostapd/aes_wrap.c @@ -7,7 +7,7 @@ * - AES-128 EAX mode encryption/decryption * - AES-128 CBC * - * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * 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 @@ -19,17 +19,18 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> +#include "includes.h" + #include "common.h" #include "aes_wrap.h" #include "crypto.h" -#ifndef EAP_TLS_FUNCS +#ifdef INTERNAL_AES #include "aes.c" -#endif /* EAP_TLS_FUNCS */ +#endif /* INTERNAL_AES */ + +#ifndef CONFIG_NO_AES_WRAP /** * aes_wrap - Wrap keys with AES Key Wrap Algorithm (128-bit KEK) (RFC3394) @@ -49,8 +50,8 @@ int aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher) r = cipher + 8; /* 1) Initialize variables. */ - memset(a, 0xa6, 8); - memcpy(r, plain, 8 * n); + os_memset(a, 0xa6, 8); + os_memcpy(r, plain, 8 * n); ctx = aes_encrypt_init(kek, 16); if (ctx == NULL) @@ -66,12 +67,12 @@ int aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher) for (j = 0; j <= 5; j++) { r = cipher + 8; for (i = 1; i <= n; i++) { - memcpy(b, a, 8); - memcpy(b + 8, r, 8); + os_memcpy(b, a, 8); + os_memcpy(b + 8, r, 8); aes_encrypt(ctx, b, b); - memcpy(a, b, 8); + os_memcpy(a, b, 8); a[7] ^= n * j + i; - memcpy(r, b + 8, 8); + os_memcpy(r, b + 8, 8); r += 8; } } @@ -86,6 +87,8 @@ int aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher) return 0; } +#endif /* CONFIG_NO_AES_WRAP */ + /** * aes_unwrap - Unwrap key with AES Key Wrap Algorithm (128-bit KEK) (RFC3394) @@ -102,9 +105,9 @@ int aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain) void *ctx; /* 1) Initialize variables. */ - memcpy(a, cipher, 8); + os_memcpy(a, cipher, 8); r = plain; - memcpy(r, cipher + 8, 8 * n); + os_memcpy(r, cipher + 8, 8 * n); ctx = aes_decrypt_init(kek, 16); if (ctx == NULL) @@ -120,13 +123,13 @@ int aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain) for (j = 5; j >= 0; j--) { r = plain + (n - 1) * 8; for (i = n; i >= 1; i--) { - memcpy(b, a, 8); + os_memcpy(b, a, 8); b[7] ^= n * j + i; - memcpy(b + 8, r, 8); + os_memcpy(b + 8, r, 8); aes_decrypt(ctx, b, b); - memcpy(a, b, 8); - memcpy(r, b + 8, 8); + os_memcpy(a, b, 8); + os_memcpy(r, b + 8, 8); r -= 8; } } @@ -148,6 +151,8 @@ int aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain) #define BLOCK_SIZE 16 +#ifndef CONFIG_NO_AES_OMAC1 + static void gf_mulx(u8 *pad) { int i, carry; @@ -162,8 +167,8 @@ static void gf_mulx(u8 *pad) /** - * omac1_aes_128 - One-Key CBC MAC (OMAC1) hash with AES-128 - * @key: Key for the hash operation + * omac1_aes_128 - One-Key CBC MAC (OMAC1) hash with AES-128 (aka AES-CMAC) + * @key: 128-bit key for the hash operation * @data: Data buffer for which a MAC is determined * @data: Length of data buffer in bytes * @mac: Buffer for MAC (128 bits, i.e., 16 bytes) @@ -174,13 +179,12 @@ int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac) void *ctx; u8 cbc[BLOCK_SIZE], pad[BLOCK_SIZE]; const u8 *pos = data; - int i; - size_t left = data_len; + size_t i, left = data_len; ctx = aes_encrypt_init(key, 16); if (ctx == NULL) return -1; - memset(cbc, 0, BLOCK_SIZE); + os_memset(cbc, 0, BLOCK_SIZE); while (left >= BLOCK_SIZE) { for (i = 0; i < BLOCK_SIZE; i++) @@ -190,7 +194,7 @@ int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac) left -= BLOCK_SIZE; } - memset(pad, 0, BLOCK_SIZE); + os_memset(pad, 0, BLOCK_SIZE); aes_encrypt(ctx, pad, pad); gf_mulx(pad); @@ -208,6 +212,8 @@ int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac) return 0; } +#endif /* CONFIG_NO_AES_OMAC1 */ + /** * aes_128_encrypt_block - Perform one AES 128-bit block operation @@ -228,6 +234,8 @@ int aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out) } +#ifndef CONFIG_NO_AES_CTR + /** * aes_128_ctr_encrypt - AES-128 CTR mode encryption * @key: Key for encryption (16 bytes) @@ -240,7 +248,7 @@ int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, u8 *data, size_t data_len) { void *ctx; - size_t len, left = data_len; + size_t j, len, left = data_len; int i; u8 *pos = data; u8 counter[BLOCK_SIZE], buf[BLOCK_SIZE]; @@ -248,14 +256,14 @@ int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, ctx = aes_encrypt_init(key, 16); if (ctx == NULL) return -1; - memcpy(counter, nonce, BLOCK_SIZE); + os_memcpy(counter, nonce, BLOCK_SIZE); while (left > 0) { aes_encrypt(ctx, counter, buf); len = (left < BLOCK_SIZE) ? left : BLOCK_SIZE; - for (i = 0; i < len; i++) - pos[i] ^= buf[i]; + for (j = 0; j < len; j++) + pos[j] ^= buf[j]; pos += len; left -= len; @@ -269,6 +277,10 @@ int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, return 0; } +#endif /* CONFIG_NO_AES_CTR */ + + +#ifndef CONFIG_NO_AES_EAX /** * aes_128_eax_encrypt - AES-128 EAX mode encryption @@ -299,26 +311,26 @@ int aes_128_eax_encrypt(const u8 *key, const u8 *nonce, size_t nonce_len, buf_len = hdr_len; buf_len += 16; - buf = malloc(buf_len); + buf = os_malloc(buf_len); if (buf == NULL) return -1; - memset(buf, 0, 15); + os_memset(buf, 0, 15); buf[15] = 0; - memcpy(buf + 16, nonce, nonce_len); + os_memcpy(buf + 16, nonce, nonce_len); omac1_aes_128(key, buf, 16 + nonce_len, nonce_mac); buf[15] = 1; - memcpy(buf + 16, hdr, hdr_len); + os_memcpy(buf + 16, hdr, hdr_len); omac1_aes_128(key, buf, 16 + hdr_len, hdr_mac); aes_128_ctr_encrypt(key, nonce_mac, data, data_len); buf[15] = 2; - memcpy(buf + 16, data, data_len); + os_memcpy(buf + 16, data, data_len); omac1_aes_128(key, buf, 16 + data_len, data_mac); - free(buf); + os_free(buf); for (i = 0; i < BLOCK_SIZE; i++) tag[i] = nonce_mac[i] ^ data_mac[i] ^ hdr_mac[i]; @@ -356,25 +368,25 @@ int aes_128_eax_decrypt(const u8 *key, const u8 *nonce, size_t nonce_len, buf_len = hdr_len; buf_len += 16; - buf = malloc(buf_len); + buf = os_malloc(buf_len); if (buf == NULL) return -1; - memset(buf, 0, 15); + os_memset(buf, 0, 15); buf[15] = 0; - memcpy(buf + 16, nonce, nonce_len); + os_memcpy(buf + 16, nonce, nonce_len); omac1_aes_128(key, buf, 16 + nonce_len, nonce_mac); buf[15] = 1; - memcpy(buf + 16, hdr, hdr_len); + os_memcpy(buf + 16, hdr, hdr_len); omac1_aes_128(key, buf, 16 + hdr_len, hdr_mac); buf[15] = 2; - memcpy(buf + 16, data, data_len); + os_memcpy(buf + 16, data, data_len); omac1_aes_128(key, buf, 16 + data_len, data_mac); - free(buf); + os_free(buf); for (i = 0; i < BLOCK_SIZE; i++) { if (tag[i] != (nonce_mac[i] ^ data_mac[i] ^ hdr_mac[i])) @@ -386,6 +398,10 @@ int aes_128_eax_decrypt(const u8 *key, const u8 *nonce, size_t nonce_len, return 0; } +#endif /* CONFIG_NO_AES_EAX */ + + +#ifndef CONFIG_NO_AES_CBC /** * aes_128_cbc_encrypt - AES-128 CBC encryption @@ -405,14 +421,14 @@ int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) ctx = aes_encrypt_init(key, 16); if (ctx == NULL) return -1; - memcpy(cbc, iv, BLOCK_SIZE); + os_memcpy(cbc, iv, BLOCK_SIZE); blocks = data_len / BLOCK_SIZE; for (i = 0; i < blocks; i++) { for (j = 0; j < BLOCK_SIZE; j++) cbc[j] ^= pos[j]; aes_encrypt(ctx, cbc, cbc); - memcpy(pos, cbc, BLOCK_SIZE); + os_memcpy(pos, cbc, BLOCK_SIZE); pos += BLOCK_SIZE; } aes_encrypt_deinit(ctx); @@ -438,288 +454,19 @@ int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) ctx = aes_decrypt_init(key, 16); if (ctx == NULL) return -1; - memcpy(cbc, iv, BLOCK_SIZE); + os_memcpy(cbc, iv, BLOCK_SIZE); blocks = data_len / BLOCK_SIZE; for (i = 0; i < blocks; i++) { - memcpy(tmp, pos, BLOCK_SIZE); + os_memcpy(tmp, pos, BLOCK_SIZE); aes_decrypt(ctx, pos, pos); for (j = 0; j < BLOCK_SIZE; j++) pos[j] ^= cbc[j]; - memcpy(cbc, tmp, BLOCK_SIZE); + os_memcpy(cbc, tmp, BLOCK_SIZE); pos += BLOCK_SIZE; } aes_decrypt_deinit(ctx); return 0; } - -#ifdef TEST_MAIN - -#ifdef __i386__ -#define rdtscll(val) \ - __asm__ __volatile__("rdtsc" : "=A" (val)) - -static void test_aes_perf(void) -{ - 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__ */ - - -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 i, ret = 0; - u8 *buf; - - 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 - */ - -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, 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) { - int i; - printf("AES-UNWRAP-128-128 failed\n"); - ret++; - for (i = 0; i < 16; i++) - printf(" %02x", result[i]); - printf("\n"); - } - -#ifdef __i386__ - test_aes_perf(); -#endif /* __i386__ */ - - 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++; - } - } - - ret += test_eax(); - - ret += test_cbc(); - - if (ret) - printf("FAILED!\n"); - - return ret; -} -#endif /* TEST_MAIN */ +#endif /* CONFIG_NO_AES_CBC */ diff --git a/contrib/hostapd/aes_wrap.h b/contrib/hostapd/aes_wrap.h index cb1a539..1bc6971 100644 --- a/contrib/hostapd/aes_wrap.h +++ b/contrib/hostapd/aes_wrap.h @@ -7,7 +7,7 @@ * - AES-128 EAX mode encryption/decryption * - AES-128 CBC * - * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * 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 diff --git a/contrib/hostapd/ap.h b/contrib/hostapd/ap.h index e874ffd..b73c5b4 100644 --- a/contrib/hostapd/ap.h +++ b/contrib/hostapd/ap.h @@ -1,3 +1,17 @@ +/* + * hostapd / Station table data structures + * Copyright (c) 2002-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. + */ + #ifndef AP_H #define AP_H @@ -9,16 +23,13 @@ #define WLAN_STA_PERM BIT(4) #define WLAN_STA_AUTHORIZED BIT(5) #define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */ -#define WLAN_STA_PREAUTH BIT(7) - -#define WLAN_RATE_1M BIT(0) -#define WLAN_RATE_2M BIT(1) -#define WLAN_RATE_5M5 BIT(2) -#define WLAN_RATE_11M BIT(3) -#define WLAN_RATE_COUNT 4 +#define WLAN_STA_SHORT_PREAMBLE BIT(7) +#define WLAN_STA_PREAUTH BIT(8) +#define WLAN_STA_WME BIT(9) +#define WLAN_STA_NONERP BIT(31) -/* Maximum size of Supported Rates info element. IEEE 802.11 has a limit of 8, - * but some pre-standard IEEE 802.11g products use longer elements. */ +/* Maximum number of supported rates (from both Supported Rates and Extended + * Supported Rates IEs). */ #define WLAN_SUPP_RATES_MAX 32 @@ -31,7 +42,14 @@ struct sta_info { u16 capability; u16 listen_interval; /* or beacon_int for APs */ u8 supported_rates[WLAN_SUPP_RATES_MAX]; - u8 tx_supp_rates; + int supported_rates_len; + + unsigned int nonerp_set:1; + unsigned int no_short_slot_time_set:1; + unsigned int no_short_preamble_set:1; + + u16 auth_alg; + u8 previous_ap[6]; enum { STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE @@ -57,26 +75,15 @@ struct sta_info { u8 *challenge; /* IEEE 802.11 Shared Key Authentication Challenge */ - int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */ - u8 *wpa_ie; - size_t wpa_ie_len; struct wpa_state_machine *wpa_sm; - 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 wpa_key_mgmt; /* the selected WPA_KEY_MGMT_* */ - struct rsn_pmksa_cache *pmksa; struct rsn_preauth_interface *preauth_iface; - u8 req_replay_counter[8 /* WPA_REPLAY_COUNTER_LEN */]; - int req_replay_counter_used; - u32 dot11RSNAStatsTKIPLocalMICFailures; - u32 dot11RSNAStatsTKIPRemoteMICFailures; -}; + struct hostapd_ssid *ssid; /* SSID selection based on (Re)AssocReq */ + struct hostapd_ssid *ssid_probe; /* SSID selection based on ProbeReq */ + + int vlan_id; +}; -#define MAX_STA_COUNT 1024 /* Maximum number of AIDs to use for STAs; must be 2007 or lower * (8802.11 limitation) */ @@ -95,5 +102,10 @@ struct sta_info { #define AP_MAX_INACTIVITY (5 * 60) #define AP_DISASSOC_DELAY (1) #define AP_DEAUTH_DELAY (1) +/* Number of seconds to keep STA entry with Authenticated flag after it has + * been disassociated. */ +#define AP_MAX_INACTIVITY_AFTER_DISASSOC (1 * 30) +/* Number of seconds to keep STA entry after it has been deauthenticated. */ +#define AP_MAX_INACTIVITY_AFTER_DEAUTH (1 * 5) #endif /* AP_H */ diff --git a/contrib/hostapd/ap_list.c b/contrib/hostapd/ap_list.c new file mode 100644 index 0000000..f2d3221 --- /dev/null +++ b/contrib/hostapd/ap_list.c @@ -0,0 +1,459 @@ +/* + * hostapd / AP table + * Copyright 2002-2003, Jouni Malinen <j@w1.fi> + * Copyright 2003-2004, Instant802 Networks, Inc. + * Copyright 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 "ieee802_11.h" +#include "eloop.h" +#include "ap_list.h" +#include "hw_features.h" +#include "beacon.h" + + +struct ieee80211_frame_info { + u32 version; + u32 length; + u64 mactime; + u64 hosttime; + u32 phytype; + u32 channel; + u32 datarate; + u32 antenna; + u32 priority; + u32 ssi_type; + u32 ssi_signal; + u32 ssi_noise; + u32 preamble; + u32 encoding; + + /* Note: this structure is otherwise identical to capture format used + * in linux-wlan-ng, but this additional field is used to provide meta + * data about the frame to hostapd. This was the easiest method for + * providing this information, but this might change in the future. */ + u32 msg_type; +} __attribute__ ((packed)); + + +enum ieee80211_phytype { + ieee80211_phytype_fhss_dot11_97 = 1, + ieee80211_phytype_dsss_dot11_97 = 2, + ieee80211_phytype_irbaseband = 3, + ieee80211_phytype_dsss_dot11_b = 4, + ieee80211_phytype_pbcc_dot11_b = 5, + ieee80211_phytype_ofdm_dot11_g = 6, + ieee80211_phytype_pbcc_dot11_g = 7, + ieee80211_phytype_ofdm_dot11_a = 8, + ieee80211_phytype_dsss_dot11_turbog = 255, + ieee80211_phytype_dsss_dot11_turbo = 256, +}; + + +/* AP list is a double linked list with head->prev pointing to the end of the + * list and tail->next = NULL. Entries are moved to the head of the list + * whenever a beacon has been received from the AP in question. The tail entry + * in this link will thus be the least recently used entry. */ + + +static void ap_list_new_ap(struct hostapd_iface *iface, struct ap_info *ap) +{ + wpa_printf(MSG_DEBUG, "New AP detected: " MACSTR, MAC2STR(ap->addr)); + + /* TODO: could send a notification message to an external program that + * would then determine whether a rogue AP has been detected */ +} + + +static void ap_list_expired_ap(struct hostapd_iface *iface, struct ap_info *ap) +{ + wpa_printf(MSG_DEBUG, "AP info expired: " MACSTR, MAC2STR(ap->addr)); + + /* TODO: could send a notification message to an external program */ +} + + +static int ap_list_beacon_olbc(struct hostapd_iface *iface, struct ap_info *ap) +{ + int i; + + if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G || + ap->phytype != ieee80211_phytype_pbcc_dot11_g || + iface->conf->channel != ap->channel) + return 0; + + if (ap->erp != -1 && (ap->erp & ERP_INFO_NON_ERP_PRESENT)) + return 1; + + for (i = 0; i < WLAN_SUPP_RATES_MAX; i++) { + int rate = (ap->supported_rates[i] & 0x7f) * 5; + if (rate == 60 || rate == 90 || rate > 110) + return 0; + } + + return 1; +} + + +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) + s = s->hnext; + return s; +} + + +static void ap_ap_list_add(struct hostapd_iface *iface, struct ap_info *ap) +{ + if (iface->ap_list) { + ap->prev = iface->ap_list->prev; + iface->ap_list->prev = ap; + } else + ap->prev = ap; + ap->next = iface->ap_list; + iface->ap_list = ap; +} + + +static void ap_ap_list_del(struct hostapd_iface *iface, struct ap_info *ap) +{ + if (iface->ap_list == ap) + iface->ap_list = ap->next; + else + ap->prev->next = ap->next; + + if (ap->next) + ap->next->prev = ap->prev; + else if (iface->ap_list) + iface->ap_list->prev = ap->prev; +} + + +static void ap_ap_iter_list_add(struct hostapd_iface *iface, + struct ap_info *ap) +{ + if (iface->ap_iter_list) { + ap->iter_prev = iface->ap_iter_list->iter_prev; + iface->ap_iter_list->iter_prev = ap; + } else + ap->iter_prev = ap; + ap->iter_next = iface->ap_iter_list; + iface->ap_iter_list = ap; +} + + +static void ap_ap_iter_list_del(struct hostapd_iface *iface, + struct ap_info *ap) +{ + if (iface->ap_iter_list == ap) + iface->ap_iter_list = ap->iter_next; + else + ap->iter_prev->iter_next = ap->iter_next; + + if (ap->iter_next) + ap->iter_next->iter_prev = ap->iter_prev; + else if (iface->ap_iter_list) + iface->ap_iter_list->iter_prev = ap->iter_prev; +} + + +static void ap_ap_hash_add(struct hostapd_iface *iface, struct ap_info *ap) +{ + ap->hnext = iface->ap_hash[STA_HASH(ap->addr)]; + iface->ap_hash[STA_HASH(ap->addr)] = ap; +} + + +static void ap_ap_hash_del(struct hostapd_iface *iface, struct ap_info *ap) +{ + struct ap_info *s; + + s = iface->ap_hash[STA_HASH(ap->addr)]; + if (s == NULL) return; + if (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) + s = s->hnext; + if (s->hnext != NULL) + s->hnext = s->hnext->hnext; + else + printf("AP: could not remove AP " MACSTR " from hash table\n", + MAC2STR(ap->addr)); +} + + +static void ap_free_ap(struct hostapd_iface *iface, struct ap_info *ap) +{ + ap_ap_hash_del(iface, ap); + ap_ap_list_del(iface, ap); + ap_ap_iter_list_del(iface, ap); + + iface->num_ap--; + free(ap); +} + + +static void hostapd_free_aps(struct hostapd_iface *iface) +{ + struct ap_info *ap, *prev; + + ap = iface->ap_list; + + while (ap) { + prev = ap; + ap = ap->next; + ap_free_ap(iface, prev); + } + + iface->ap_list = NULL; +} + + +int ap_ap_for_each(struct hostapd_iface *iface, + int (*func)(struct ap_info *s, void *data), void *data) +{ + struct ap_info *s; + int ret = 0; + + s = iface->ap_list; + + while (s) { + ret = func(s, data); + if (ret) + break; + s = s->next; + } + + return ret; +} + + +static struct ap_info * ap_ap_add(struct hostapd_iface *iface, u8 *addr) +{ + struct ap_info *ap; + + ap = wpa_zalloc(sizeof(struct ap_info)); + if (ap == NULL) + return NULL; + + /* initialize AP info data */ + memcpy(ap->addr, addr, ETH_ALEN); + ap_ap_list_add(iface, ap); + iface->num_ap++; + ap_ap_hash_add(iface, ap); + ap_ap_iter_list_add(iface, ap); + + if (iface->num_ap > iface->conf->ap_table_max_size && ap != ap->prev) { + wpa_printf(MSG_DEBUG, "Removing the least recently used AP " + MACSTR " from AP table", MAC2STR(ap->prev->addr)); + if (iface->conf->passive_scan_interval > 0) + ap_list_expired_ap(iface, ap->prev); + ap_free_ap(iface, ap->prev); + } + + return ap; +} + + +void ap_list_process_beacon(struct hostapd_iface *iface, + struct ieee80211_mgmt *mgmt, + struct ieee802_11_elems *elems, + struct hostapd_frame_info *fi) +{ + struct ap_info *ap; + int new_ap = 0; + size_t len; + + if (iface->conf->ap_table_max_size < 1) + return; + + ap = ap_get_ap(iface, mgmt->bssid); + if (!ap) { + ap = ap_ap_add(iface, mgmt->bssid); + if (!ap) { + printf("Failed to allocate AP information entry\n"); + return; + } + new_ap = 1; + } + + ap->beacon_int = le_to_host16(mgmt->u.beacon.beacon_int); + ap->capability = le_to_host16(mgmt->u.beacon.capab_info); + + if (elems->ssid) { + len = elems->ssid_len; + if (len >= sizeof(ap->ssid)) + len = sizeof(ap->ssid) - 1; + memcpy(ap->ssid, elems->ssid, len); + ap->ssid[len] = '\0'; + ap->ssid_len = len; + } + + 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); + } + if (elems->ext_supp_rates) { + int len2; + if (len + elems->ext_supp_rates_len > WLAN_SUPP_RATES_MAX) + len2 = WLAN_SUPP_RATES_MAX - len; + else + len2 = elems->ext_supp_rates_len; + memcpy(ap->supported_rates + len, elems->ext_supp_rates, len2); + } + + ap->wpa = elems->wpa_ie != NULL; + + if (elems->erp_info && elems->erp_info_len == 1) + ap->erp = elems->erp_info[0]; + else + ap->erp = -1; + + if (elems->ds_params && elems->ds_params_len == 1) + ap->channel = elems->ds_params[0]; + else if (fi) + ap->channel = fi->channel; + + ap->num_beacons++; + time(&ap->last_beacon); + if (fi) { + ap->phytype = fi->phytype; + ap->ssi_signal = fi->ssi_signal; + ap->datarate = fi->datarate; + } + + if (new_ap) { + if (iface->conf->passive_scan_interval > 0) + ap_list_new_ap(iface, ap); + } else if (ap != iface->ap_list) { + /* move AP entry into the beginning of the list so that the + * oldest entry is always in the end of the list */ + ap_ap_list_del(iface, ap); + ap_ap_list_add(iface, ap); + } + + if (!iface->olbc && + ap_list_beacon_olbc(iface, ap)) { + 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); + } +} + + +static void ap_list_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_iface *iface = eloop_ctx; + time_t now; + struct ap_info *ap; + + eloop_register_timeout(10, 0, ap_list_timer, iface, NULL); + + if (!iface->ap_list) + return; + + time(&now); + + /* FIX: it looks like jkm-Purina ended up in busy loop in this + * function. Apparently, something can still cause a loop in the AP + * list.. */ + + while (iface->ap_list) { + ap = iface->ap_list->prev; + if (ap->last_beacon + iface->conf->ap_table_expiration_time >= + now) + break; + + if (iface->conf->passive_scan_interval > 0) + ap_list_expired_ap(iface, ap); + ap_free_ap(iface, ap); + } + + if (iface->olbc) { + int olbc = 0; + ap = iface->ap_list; + while (ap) { + if (ap_list_beacon_olbc(iface, ap)) { + olbc = 1; + break; + } + ap = ap->next; + } + if (!olbc) { + struct hostapd_data *hapd = iface->bss[0]; + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, + "OLBC not detected anymore\n"); + iface->olbc = 0; + ieee802_11_set_beacons(hapd->iface); + } + } +} + + +int ap_list_init(struct hostapd_iface *iface) +{ + eloop_register_timeout(10, 0, ap_list_timer, iface, NULL); + return 0; +} + + +void ap_list_deinit(struct hostapd_iface *iface) +{ + eloop_cancel_timeout(ap_list_timer, iface, NULL); + hostapd_free_aps(iface); +} + + +int ap_list_reconfig(struct hostapd_iface *iface, + struct hostapd_config *oldconf) +{ + time_t now; + struct ap_info *ap; + + if (iface->conf->ap_table_max_size == oldconf->ap_table_max_size && + iface->conf->ap_table_expiration_time == + oldconf->ap_table_expiration_time) + return 0; + + time(&now); + + while (iface->ap_list) { + ap = iface->ap_list->prev; + if (iface->num_ap <= iface->conf->ap_table_max_size && + ap->last_beacon + iface->conf->ap_table_expiration_time >= + now) + break; + + if (iface->conf->passive_scan_interval > 0) + ap_list_expired_ap(iface, iface->ap_list->prev); + ap_free_ap(iface, iface->ap_list->prev); + } + + return 0; +} diff --git a/contrib/hostapd/ap_list.h b/contrib/hostapd/ap_list.h new file mode 100644 index 0000000..668d909 --- /dev/null +++ b/contrib/hostapd/ap_list.h @@ -0,0 +1,68 @@ +/* + * hostapd / AP table + * Copyright 2002-2003, Jouni Malinen <j@w1.fi> + * Copyright 2003-2004, Instant802 Networks, Inc. + * Copyright 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 AP_LIST_H +#define AP_LIST_H + +struct ap_info { + /* Note: next/prev pointers are updated whenever a new beacon is + * received because these are used to find the least recently used + * entries. iter_next/iter_prev are updated only when adding new BSSes + * and when removing old ones. These should be used when iterating + * through the table in a manner that allows beacons to be received + * during the iteration. */ + struct ap_info *next; /* next entry in AP list */ + struct ap_info *prev; /* previous entry in AP list */ + struct ap_info *hnext; /* next entry in hash table list */ + struct ap_info *iter_next; /* next entry in AP iteration list */ + struct ap_info *iter_prev; /* previous entry in AP iteration list */ + u8 addr[6]; + u16 beacon_int; + u16 capability; + u8 supported_rates[WLAN_SUPP_RATES_MAX]; + u8 ssid[33]; + size_t ssid_len; + int wpa; + int erp; /* ERP Info or -1 if ERP info element not present */ + + int phytype; /* .11a / .11b / .11g / Atheros Turbo */ + int channel; + int datarate; /* in 100 kbps */ + int ssi_signal; + + unsigned int num_beacons; /* number of beacon frames received */ + time_t last_beacon; + + int already_seen; /* whether API call AP-NEW has already fetched + * information about this AP */ +}; + +struct ieee802_11_elems; +struct hostapd_frame_info; + +struct ap_info * ap_get_ap(struct hostapd_iface *iface, u8 *sta); +int ap_ap_for_each(struct hostapd_iface *iface, + int (*func)(struct ap_info *s, void *data), void *data); +void ap_list_process_beacon(struct hostapd_iface *iface, + struct ieee80211_mgmt *mgmt, + struct ieee802_11_elems *elems, + struct hostapd_frame_info *fi); +int ap_list_init(struct hostapd_iface *iface); +void ap_list_deinit(struct hostapd_iface *iface); +int ap_list_reconfig(struct hostapd_iface *iface, + struct hostapd_config *oldconf); + +#endif /* AP_LIST_H */ diff --git a/contrib/hostapd/beacon.c b/contrib/hostapd/beacon.c new file mode 100644 index 0000000..7af2bc1 --- /dev/null +++ b/contrib/hostapd/beacon.c @@ -0,0 +1,419 @@ +/* + * hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response + * Copyright (c) 2002-2004, Instant802 Networks, Inc. + * Copyright (c) 2005-2006, Devicescape Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published 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 "ieee802_11.h" +#include "wpa.h" +#include "wme.h" +#include "beacon.h" +#include "hw_features.h" +#include "driver.h" +#include "sta_info.h" +#include "ieee802_11h.h" + + +static u8 ieee802_11_erp_info(struct hostapd_data *hapd) +{ + u8 erp = 0; + + if (hapd->iface == NULL || hapd->iface->current_mode == NULL || + hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G) + return 0; + + switch (hapd->iconf->cts_protection_type) { + case CTS_PROTECTION_FORCE_ENABLED: + erp |= ERP_INFO_NON_ERP_PRESENT | ERP_INFO_USE_PROTECTION; + break; + case CTS_PROTECTION_FORCE_DISABLED: + erp = 0; + break; + case CTS_PROTECTION_AUTOMATIC: + if (hapd->iface->olbc) + erp |= ERP_INFO_USE_PROTECTION; + /* continue */ + case CTS_PROTECTION_AUTOMATIC_NO_OLBC: + if (hapd->iface->num_sta_non_erp > 0) { + erp |= ERP_INFO_NON_ERP_PRESENT | + ERP_INFO_USE_PROTECTION; + } + break; + } + if (hapd->iface->num_sta_no_short_preamble > 0) + erp |= ERP_INFO_BARKER_PREAMBLE_MODE; + + return erp; +} + + +static u8 * hostapd_eid_ds_params(struct hostapd_data *hapd, u8 *eid) +{ + *eid++ = WLAN_EID_DS_PARAMS; + *eid++ = 1; + *eid++ = hapd->iconf->channel; + return eid; +} + + +static u8 * hostapd_eid_erp_info(struct hostapd_data *hapd, u8 *eid) +{ + if (hapd->iface == NULL || hapd->iface->current_mode == NULL || + hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G) + return eid; + + /* Set NonERP_present and use_protection bits if there + * are any associated NonERP stations. */ + /* TODO: use_protection bit can be set to zero even if + * there are NonERP stations present. This optimization + * might be useful if NonERP stations are "quiet". + * See 802.11g/D6 E-1 for recommended practice. + * In addition, Non ERP present might be set, if AP detects Non ERP + * operation on other APs. */ + + /* Add ERP Information element */ + *eid++ = WLAN_EID_ERP_INFO; + *eid++ = 1; + *eid++ = ieee802_11_erp_info(hapd); + + return eid; +} + + +static u8 * hostapd_eid_country(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) + return eid; + + *pos++ = WLAN_EID_COUNTRY; + pos++; /* length will be set later */ + memcpy(pos, hapd->iconf->country, 3); /* e.g., 'US ' */ + pos += 3; + left = max_len - 3; + + if ((pos - eid) & 1) { + if (left < 1) + return eid; + *pos++ = 0; /* pad for 16-bit alignment */ + left--; + } + + eid[1] = (pos - eid) - 2; + + return pos; +} + + +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) +{ + const u8 *ie; + size_t ielen; + + ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ielen); + if (ie == NULL || ielen > len) + return eid; + + memcpy(eid, ie, ielen); + return eid + ielen; +} + + +void handle_probe_req(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, + size_t len) +{ + struct ieee80211_mgmt *resp; + struct ieee802_11_elems elems; + char *ssid; + u8 *pos, *epos; + size_t ssid_len; + struct sta_info *sta = NULL; + + 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)); + return; + } + + ssid = NULL; + 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)); + 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)); + return; + } + + sta = ap_get_sta(hapd, mgmt->sa); + + if (elems.ssid_len == 0 || + (elems.ssid_len == hapd->conf->ssid.ssid_len && + 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) + sta->ssid_probe = &hapd->conf->ssid; + } + + 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"); + } + 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); + 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); + + memcpy(resp->bssid, hapd->own_addr, ETH_ALEN); + resp->u.probe_resp.beacon_int = + host_to_le16(hapd->iconf->beacon_int); + + /* hardware or low-level driver will setup seq_ctrl and timestamp */ + resp->u.probe_resp.capab_info = + host_to_le16(hostapd_own_capab_info(hapd, sta, 1)); + + pos = resp->u.probe_resp.variable; + *pos++ = WLAN_EID_SSID; + *pos++ = ssid_len; + memcpy(pos, ssid, ssid_len); + pos += ssid_len; + + /* Supported rates */ + pos = hostapd_eid_supp_rates(hapd, pos); + + /* DS Params */ + pos = hostapd_eid_ds_params(hapd, pos); + + pos = hostapd_eid_country(hapd, pos, epos - pos); + + pos = hostapd_eid_power_constraint(hapd, pos); + pos = hostapd_eid_tpc_report(hapd, pos); + + /* ERP Information element */ + pos = hostapd_eid_erp_info(hapd, pos); + + /* Extended supported rates */ + pos = hostapd_eid_ext_supp_rates(hapd, pos); + + 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); + + if (hostapd_send_mgmt_frame(hapd, resp, pos - (u8 *) resp, 0) < 0) + perror("handle_probe_req: send"); + + 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"); +} + + +void ieee802_11_set_beacon(struct hostapd_data *hapd) +{ + struct ieee80211_mgmt *head; + u8 *pos, *tail, *tailpos; + int preamble; + u16 capab_info; + size_t head_len, tail_len; + int cts_protection = ((ieee802_11_erp_info(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); + if (head == NULL || tail == NULL) { + printf("Failed to set beacon data\n"); + free(head); + 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); + + memcpy(head->sa, hapd->own_addr, ETH_ALEN); + memcpy(head->bssid, hapd->own_addr, ETH_ALEN); + head->u.beacon.beacon_int = + host_to_le16(hapd->iconf->beacon_int); + + /* hardware or low-level driver will setup seq_ctrl and timestamp */ + capab_info = hostapd_own_capab_info(hapd, NULL, 0); + head->u.beacon.capab_info = host_to_le16(capab_info); + pos = &head->u.beacon.variable[0]; + + /* SSID */ + *pos++ = WLAN_EID_SSID; + if (hapd->conf->ignore_broadcast_ssid == 2) { + /* clear the data, but keep the correct length of the SSID */ + *pos++ = hapd->conf->ssid.ssid_len; + 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); + pos += hapd->conf->ssid.ssid_len; + } + + /* Supported rates */ + pos = hostapd_eid_supp_rates(hapd, pos); + + /* DS Params */ + pos = hostapd_eid_ds_params(hapd, pos); + + head_len = pos - (u8 *) head; + + tailpos = hostapd_eid_country(hapd, tailpos, + tail + BEACON_TAIL_BUF_SIZE - tailpos); + + 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); + + /* Extended supported rates */ + tailpos = hostapd_eid_ext_supp_rates(hapd, tailpos); + + tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE - + tailpos, NULL); + + /* Wi-Fi Wireless Multimedia Extensions */ + if (hapd->conf->wme_enabled) + tailpos = hostapd_eid_wme(hapd, tailpos); + + 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"); + + free(tail); + free(head); + + if (hostapd_set_cts_protect(hapd, cts_protection)) + printf("Failed to set CTS protect in kernel driver\n"); + + if (hapd->iface && 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"); + + if (hapd->iface && 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"); +} + + +void ieee802_11_set_beacons(struct hostapd_iface *iface) +{ + size_t i; + for (i = 0; i < iface->num_bss; i++) + ieee802_11_set_beacon(iface->bss[i]); +} + +#endif /* CONFIG_NATIVE_WINDOWS */ diff --git a/contrib/hostapd/beacon.h b/contrib/hostapd/beacon.h new file mode 100644 index 0000000..18e0da2 --- /dev/null +++ b/contrib/hostapd/beacon.h @@ -0,0 +1,24 @@ +/* + * hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response + * Copyright (c) 2002-2004, Instant802 Networks, Inc. + * Copyright (c) 2005-2006, Devicescape Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published 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 BEACON_H +#define BEACON_H + +void handle_probe_req(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, + size_t len); +void ieee802_11_set_beacon(struct hostapd_data *hapd); +void ieee802_11_set_beacons(struct hostapd_iface *iface); + +#endif /* BEACON_H */ diff --git a/contrib/hostapd/build_config.h b/contrib/hostapd/build_config.h new file mode 100644 index 0000000..58bcda8 --- /dev/null +++ b/contrib/hostapd/build_config.h @@ -0,0 +1,50 @@ +/* + * wpa_supplicant/hostapd - Build time configuration defines + * 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 header file can be used to define configuration defines that were + * originally defined in Makefile. This is mainly meant for IDE use or for + * systems that do not have suitable 'make' tool. In these cases, it may be + * easier to have a single place for defining all the needed C pre-processor + * defines. + */ + +#ifndef BUILD_CONFIG_H +#define BUILD_CONFIG_H + +/* Insert configuration defines, e.g., #define EAP_MD5, here, if needed. */ + +#ifdef CONFIG_WIN32_DEFAULTS +#define CONFIG_NATIVE_WINDOWS +#define CONFIG_ANSI_C_EXTRA +#define CONFIG_WINPCAP +#define IEEE8021X_EAPOL +#define EAP_TLS_FUNCS +#define PKCS12_FUNCS +#define PCSC_FUNCS +#define CONFIG_CTRL_IFACE +#define CONFIG_CTRL_IFACE_NAMED_PIPE +#define CONFIG_DRIVER_NDIS +#define CONFIG_NDIS_EVENTS_INTEGRATED +#define CONFIG_DEBUG_FILE +#define EAP_MD5 +#define EAP_TLS +#define EAP_MSCHAPv2 +#define EAP_PEAP +#define EAP_TTLS +#define EAP_GTC +#define EAP_OTP +#define EAP_LEAP +#define _CRT_SECURE_NO_DEPRECATE +#endif /* CONFIG_WIN32_DEFAULTS */ + +#endif /* BUILD_CONFIG_H */ diff --git a/contrib/hostapd/common.c b/contrib/hostapd/common.c index 4b756d8..c8d6f13 100644 --- a/contrib/hostapd/common.c +++ b/contrib/hostapd/common.c @@ -1,6 +1,6 @@ /* * wpa_supplicant/hostapd / common helper functions, etc. - * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * 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 @@ -12,70 +12,20 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> -#include <errno.h> -#include <stdarg.h> -#include <ctype.h> -#include <time.h> -#include <sys/time.h> -#ifdef CONFIG_NATIVE_WINDOWS -#include <winsock2.h> -#include <wincrypt.h> -#endif /* CONFIG_NATIVE_WINDOWS */ +#include "includes.h" #include "common.h" +#ifdef CONFIG_DEBUG_FILE +static FILE *out_file = NULL; +#endif /* CONFIG_DEBUG_FILE */ +int wpa_debug_use_file = 0; int wpa_debug_level = MSG_INFO; int wpa_debug_show_keys = 0; int wpa_debug_timestamp = 0; -int hostapd_get_rand(u8 *buf, size_t len) -{ -#ifdef CONFIG_NATIVE_WINDOWS - HCRYPTPROV prov; - BOOL ret; - - if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, - CRYPT_VERIFYCONTEXT)) - return -1; - - ret = CryptGenRandom(prov, len, buf); - CryptReleaseContext(prov, 0); - - return ret ? 0 : -1; -#else /* CONFIG_NATIVE_WINDOWS */ - FILE *f; - size_t rc; - - f = fopen("/dev/urandom", "r"); - if (f == NULL) { - printf("Could not open /dev/urandom.\n"); - return -1; - } - - rc = fread(buf, 1, len, f); - fclose(f); - - return rc != len ? -1 : 0; -#endif /* CONFIG_NATIVE_WINDOWS */ -} - - -void hostapd_hexdump(const char *title, const u8 *buf, size_t len) -{ - size_t i; - printf("%s - hexdump(len=%lu):", title, (unsigned long) len); - for (i = 0; i < len; i++) - printf(" %02x", buf[i]); - printf("\n"); -} - - static int hex2num(char c) { if (c >= '0' && c <= '9') @@ -139,7 +89,8 @@ int hwaddr_aton(const char *txt, u8 *addr) */ int hexstr2bin(const char *hex, u8 *buf, size_t len) { - int i, a; + size_t i; + int a; const char *ipos = hex; u8 *opos = buf; @@ -154,45 +105,6 @@ int hexstr2bin(const char *hex, u8 *buf, size_t len) } -char * rel2abs_path(const char *rel_path) -{ - char *buf = NULL, *cwd, *ret; - size_t len = 128, cwd_len, rel_len, ret_len; - - if (rel_path[0] == '/') - return strdup(rel_path); - - for (;;) { - buf = malloc(len); - if (buf == NULL) - return NULL; - cwd = getcwd(buf, len); - if (cwd == NULL) { - free(buf); - if (errno != ERANGE) { - return NULL; - } - len *= 2; - } else { - break; - } - } - - cwd_len = strlen(cwd); - rel_len = strlen(rel_path); - ret_len = cwd_len + 1 + rel_len + 1; - ret = malloc(ret_len); - if (ret) { - memcpy(ret, cwd, cwd_len); - ret[cwd_len] = '/'; - memcpy(ret + cwd_len + 1, rel_path, rel_len); - ret[ret_len - 1] = '\0'; - } - free(buf); - return ret; -} - - /** * inc_byte_array - Increment arbitrary length byte array by one * @counter: Pointer to byte array @@ -214,40 +126,40 @@ void inc_byte_array(u8 *counter, size_t len) } -void print_char(char c) +void wpa_get_ntp_timestamp(u8 *buf) { - if (c >= 32 && c < 127) - printf("%c", c); - else - printf("<%02x>", c); + struct os_time now; + u32 sec, usec; + + /* 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 */ + /* 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); } -void fprint_char(FILE *f, char c) -{ - if (c >= 32 && c < 127) - fprintf(f, "%c", c); - else - fprintf(f, "<%02x>", c); -} - #ifndef CONFIG_NO_STDOUT_DEBUG void wpa_debug_print_timestamp(void) { - struct timeval tv; - char buf[16]; + struct os_time tv; if (!wpa_debug_timestamp) return; - gettimeofday(&tv, NULL); - if (strftime(buf, sizeof(buf), "%b %d %H:%M:%S", - localtime((const time_t *) &tv.tv_sec)) <= 0) { - snprintf(buf, sizeof(buf), "%u", (int) tv.tv_sec); - } - printf("%s.%06u: ", buf, (unsigned int) tv.tv_usec); + 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); } @@ -269,8 +181,17 @@ void wpa_printf(int level, char *fmt, ...) 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); } @@ -283,6 +204,21 @@ static void _wpa_hexdump(int level, const char *title, const u8 *buf, 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]"); @@ -293,6 +229,9 @@ static void _wpa_hexdump(int level, const char *title, const u8 *buf, 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) @@ -310,13 +249,51 @@ void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len) static void _wpa_hexdump_ascii(int level, const char *title, const u8 *buf, size_t len, int show) { - int i, llen; + size_t i, llen; const u8 *pos = buf; - const int line_len = 16; + 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); @@ -348,6 +325,9 @@ static void _wpa_hexdump_ascii(int level, const char *title, const u8 *buf, pos += llen; len -= llen; } +#ifdef CONFIG_DEBUG_FILE + } +#endif /* CONFIG_DEBUG_FILE */ } @@ -363,26 +343,261 @@ void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf, _wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys); } + +int wpa_debug_open_file(void) +{ +#ifdef CONFIG_DEBUG_FILE + static int count = 0; + char fname[64]; + if (!wpa_debug_use_file) + return 0; +#ifdef _WIN32 + os_snprintf(fname, sizeof(fname), "\\Temp\\wpa_supplicant-log-%d.txt", + count++); +#else /* _WIN32 */ + os_snprintf(fname, sizeof(fname), "/tmp/wpa_supplicant-log-%d.txt", + count++); +#endif /* _WIN32 */ + out_file = fopen(fname, "w"); + return out_file == NULL ? -1 : 0; +#else /* CONFIG_DEBUG_FILE */ + return 0; +#endif /* CONFIG_DEBUG_FILE */ +} + + +void wpa_debug_close_file(void) +{ +#ifdef CONFIG_DEBUG_FILE + if (!wpa_debug_use_file) + return; + fclose(out_file); + out_file = NULL; +#endif /* CONFIG_DEBUG_FILE */ +} + #endif /* CONFIG_NO_STDOUT_DEBUG */ -#ifdef CONFIG_NATIVE_WINDOWS +#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; -#define EPOCHFILETIME (116444736000000000ULL) + 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 */ -int gettimeofday(struct timeval *tv, struct timezone *tz) + +static inline int _wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, + size_t len, int uppercase) { - FILETIME ft; - LARGE_INTEGER li; - ULONGLONG t; + size_t i; + char *pos = buf, *end = buf + buf_size; + int ret; + if (buf_size == 0) + return 0; + for (i = 0; i < len; i++) { + ret = os_snprintf(pos, end - pos, uppercase ? "%02X" : "%02x", + data[i]); + if (ret < 0 || ret >= end - pos) { + end[-1] = '\0'; + return pos - buf; + } + pos += ret; + } + end[-1] = '\0'; + return pos - buf; +} - GetSystemTimeAsFileTime(&ft); - li.LowPart = ft.dwLowDateTime; - li.HighPart = ft.dwHighDateTime; - t = (li.QuadPart - EPOCHFILETIME) / 10; - tv->tv_sec = (long) (t / 1000000); - tv->tv_usec = (long) (t % 1000000); +/** + * wpa_snprintf_hex - Print data as a hex string into a buffer + * @buf: Memory area to use as the output buffer + * @buf_size: Maximum buffer size in bytes (should be at least 2 * len + 1) + * @data: Data to be printed + * @len: Length of data in bytes + * Returns: Number of bytes written + */ +int wpa_snprintf_hex(char *buf, size_t buf_size, const u8 *data, size_t len) +{ + return _wpa_snprintf_hex(buf, buf_size, data, len, 0); +} - return 0; + +/** + * wpa_snprintf_hex_uppercase - Print data as a upper case hex string into buf + * @buf: Memory area to use as the output buffer + * @buf_size: Maximum buffer size in bytes (should be at least 2 * len + 1) + * @data: Data to be printed + * @len: Length of data in bytes + * Returns: Number of bytes written + */ +int wpa_snprintf_hex_uppercase(char *buf, size_t buf_size, const u8 *data, + size_t len) +{ + return _wpa_snprintf_hex(buf, buf_size, data, len, 1); +} + + +#ifdef CONFIG_ANSI_C_EXTRA + +#ifdef _WIN32_WCE +void perror(const char *s) +{ + wpa_printf(MSG_ERROR, "%s: GetLastError: %d", + s, (int) GetLastError()); +} +#endif /* _WIN32_WCE */ + + +int optind = 1; +int optopt; +char *optarg; + +int getopt(int argc, char *const argv[], const char *optstring) +{ + static int optchr = 1; + char *cp; + + if (optchr == 1) { + if (optind >= argc) { + /* all arguments processed */ + return EOF; + } + + if (argv[optind][0] != '-' || argv[optind][1] == '\0') { + /* no option characters */ + return EOF; + } + } + + if (os_strcmp(argv[optind], "--") == 0) { + /* no more options */ + optind++; + return EOF; + } + + optopt = argv[optind][optchr]; + cp = os_strchr(optstring, optopt); + if (cp == NULL || optopt == ':') { + if (argv[optind][++optchr] == '\0') { + optchr = 1; + optind++; + } + return '?'; + } + + if (cp[1] == ':') { + /* Argument required */ + optchr = 1; + if (argv[optind][optchr + 1]) { + /* No space between option and argument */ + optarg = &argv[optind++][optchr + 1]; + } else if (++optind >= argc) { + /* option requires an argument */ + return '?'; + } else { + /* Argument in the next argv */ + optarg = argv[optind++]; + } + } else { + /* No argument */ + if (argv[optind][++optchr] == '\0') { + optchr = 1; + optind++; + } + optarg = NULL; + } + return *cp; +} +#endif /* CONFIG_ANSI_C_EXTRA */ + + +#ifdef CONFIG_NATIVE_WINDOWS +/** + * wpa_unicode2ascii_inplace - Convert unicode string into ASCII + * @str: Pointer to string to convert + * + * This function converts a unicode string to ASCII using the same + * buffer for output. If UNICODE is not set, the buffer is not + * modified. + */ +void wpa_unicode2ascii_inplace(TCHAR *str) +{ +#ifdef UNICODE + char *dst = (char *) str; + while (*str) + *dst++ = (char) *str++; + *dst = '\0'; +#endif /* UNICODE */ +} + + +TCHAR * wpa_strdup_tchar(const char *str) +{ +#ifdef UNICODE + TCHAR *buf; + buf = os_malloc((strlen(str) + 1) * sizeof(TCHAR)); + if (buf == NULL) + return NULL; + wsprintf(buf, L"%S", str); + return buf; +#else /* UNICODE */ + return os_strdup(str); +#endif /* UNICODE */ } #endif /* CONFIG_NATIVE_WINDOWS */ + + +/** + * wpa_ssid_txt - Convert SSID to a printable string + * @ssid: SSID (32-octet string) + * @ssid_len: Length of ssid in octets + * Returns: Pointer to a printable string + * + * This function can be used to convert SSIDs into printable form. In most + * cases, SSIDs do not use unprintable characters, but IEEE 802.11 standard + * does not limit the used character set, so anything could be used in an SSID. + * + * This function uses a static buffer, so only one call can be used at the + * time, i.e., this is not re-entrant and the returned buffer must be used + * before calling this again. + */ +const char * wpa_ssid_txt(u8 *ssid, size_t ssid_len) +{ + static char ssid_txt[33]; + char *pos; + + if (ssid_len > 32) + ssid_len = 32; + os_memcpy(ssid_txt, ssid, ssid_len); + ssid_txt[ssid_len] = '\0'; + for (pos = ssid_txt; *pos != '\0'; pos++) { + if ((u8) *pos < 32 || (u8) *pos >= 127) + *pos = '_'; + } + return ssid_txt; +} diff --git a/contrib/hostapd/common.h b/contrib/hostapd/common.h index 4bece7f..b200b58 100644 --- a/contrib/hostapd/common.h +++ b/contrib/hostapd/common.h @@ -1,6 +1,6 @@ /* * wpa_supplicant/hostapd / common helper functions, etc. - * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * 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 @@ -15,12 +15,14 @@ #ifndef COMMON_H #define COMMON_H +#include "os.h" + #ifdef __linux__ #include <endian.h> #include <byteswap.h> #endif /* __linux__ */ -#if defined(__FreeBSD__) || defined(__NetBSD__) +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) #include <sys/types.h> #include <sys/endian.h> #define __BYTE_ORDER _BYTE_ORDER @@ -29,51 +31,22 @@ #define bswap_16 bswap16 #define bswap_32 bswap32 #define bswap_64 bswap64 -#endif /* defined(__FreeBSD__) || defined(__NetBSD__) */ - -#ifdef CONFIG_NATIVE_WINDOWS -#include <winsock2.h> - -static inline int daemon(int nochdir, int noclose) -{ - printf("Windows - daemon() not supported yet\n"); - return -1; -} - -static inline void sleep(int seconds) -{ - Sleep(seconds * 1000); -} - -static inline void usleep(unsigned long usec) -{ - Sleep(usec / 1000); -} - -#ifndef timersub -#define timersub(a, b, res) do { \ - (res)->tv_sec = (a)->tv_sec - (b)->tv_sec; \ - (res)->tv_usec = (a)->tv_usec - (b)->tv_usec; \ - if ((res)->tv_usec < 0) { \ - (res)->tv_sec--; \ - (res)->tv_usec += 1000000; \ - } \ -} while (0) +#endif /* defined(__FreeBSD__) || defined(__NetBSD__) || + * defined(__DragonFly__) */ + +#ifdef CONFIG_TI_COMPILER +#define __BIG_ENDIAN 4321 +#define __LITTLE_ENDIAN 1234 +#ifdef __big_endian__ +#define __BYTE_ORDER __BIG_ENDIAN +#else +#define __BYTE_ORDER __LITTLE_ENDIAN #endif +#endif /* CONFIG_TI_COMPILER */ -struct timezone { - int tz_minuteswest; - int tz_dsttime; -}; - -int gettimeofday(struct timeval *tv, struct timezone *tz); - -static inline long int random(void) -{ - return rand(); -} +#ifdef CONFIG_NATIVE_WINDOWS +#include <winsock.h> -typedef int gid_t; typedef int socklen_t; #ifndef MSG_DONTWAIT @@ -84,6 +57,10 @@ typedef int socklen_t; #if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS) +#ifdef _MSC_VER +#define inline __inline +#endif /* _MSC_VER */ + static inline unsigned short wpa_swap_16(unsigned short v) { return ((v & 0xff) << 8) | (v >> 8); @@ -105,6 +82,18 @@ static inline unsigned int wpa_swap_32(unsigned int v) #else /* __CYGWIN__ */ +#ifndef __BYTE_ORDER +#ifndef __LITTLE_ENDIAN +#ifndef __BIG_ENDIAN +#define __LITTLE_ENDIAN 1234 +#define __BIG_ENDIAN 4321 +#if defined(sparc) +#define __BYTE_ORDER __BIG_ENDIAN +#endif +#endif /* __BIG_ENDIAN */ +#endif /* __LITTLE_ENDIAN */ +#endif /* __BYTE_ORDER */ + #if __BYTE_ORDER == __LITTLE_ENDIAN #define le_to_host16(n) (n) #define host_to_le16(n) (n) @@ -113,6 +102,10 @@ static inline unsigned int wpa_swap_32(unsigned int v) #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) #elif __BYTE_ORDER == __BIG_ENDIAN #define le_to_host16(n) bswap_16(n) #define host_to_le16(n) bswap_16(n) @@ -121,6 +114,10 @@ static inline unsigned int wpa_swap_32(unsigned int v) #define le_to_host32(n) bswap_32(n) #define be_to_host32(n) (n) #define host_to_be32(n) (n) +#define le_to_host64(n) bswap_64(n) +#define host_to_le64(n) bswap_64(n) +#define be_to_host64(n) (n) +#define host_to_be64(n) (n) #ifndef WORDS_BIGENDIAN #define WORDS_BIGENDIAN #endif @@ -145,12 +142,88 @@ static inline unsigned int wpa_swap_32(unsigned int v) (a)[0] = ((u16) (val)) & 0xff; \ } while (0) +#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); \ + } 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); \ + } while (0) + +#define WPA_PUT_BE64(a, val) \ + do { \ + (a)[0] = (u8) (((u64) (val)) >> 56); \ + (a)[1] = (u8) (((u64) (val)) >> 48); \ + (a)[2] = (u8) (((u64) (val)) >> 40); \ + (a)[3] = (u8) (((u64) (val)) >> 32); \ + (a)[4] = (u8) (((u64) (val)) >> 24); \ + (a)[5] = (u8) (((u64) (val)) >> 16); \ + (a)[6] = (u8) (((u64) (val)) >> 8); \ + (a)[7] = (u8) (((u64) (val)) & 0xff); \ + } while (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; @@ -159,19 +232,27 @@ typedef int64_t s64; typedef int32_t s32; typedef int16_t s16; typedef int8_t s8; +#define WPA_TYPES_DEFINED +#endif /* !WPA_TYPES_DEFINED */ -int hostapd_get_rand(u8 *buf, size_t len); -void hostapd_hexdump(const char *title, const u8 *buf, size_t len); +#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); -char * rel2abs_path(const char *rel_path); void inc_byte_array(u8 *counter, size_t len); -void print_char(char c); -void fprint_char(FILE *f, char c); +void wpa_get_ntp_timestamp(u8 *buf); + + +#ifdef __GNUC__ +#define PRINTF_FORMAT(a,b) __attribute__ ((format (printf, (a), (b)))) +#define STRUCT_PACKED __attribute__ ((packed)) +#else +#define PRINTF_FORMAT(a,b) +#define STRUCT_PACKED +#endif /* Debugging function - conditional printf and hex dump. Driver wrappers can - * use these for debugging purposes. */ + * use these for debugging purposes. */ enum { MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR }; @@ -179,13 +260,18 @@ enum { MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR }; #define wpa_debug_print_timestamp() do { } while (0) #define wpa_printf(args...) do { } while (0) -#define wpa_hexdump(args...) do { } while (0) -#define wpa_hexdump_key(args...) do { } while (0) -#define wpa_hexdump_ascii(args...) do { } while (0) -#define wpa_hexdump_ascii_key(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() do { } while (0) +#define wpa_debug_close_file() do { } while (0) #else /* CONFIG_NO_STDOUT_DEBUG */ +int wpa_debug_open_file(void); +void wpa_debug_close_file(void); + /** * wpa_debug_printf_timestamp - Print timestamp for debug output * @@ -207,7 +293,7 @@ void wpa_debug_print_timestamp(void); * Note: New line '\n' is added to the end of the text when printing to stdout. */ void wpa_printf(int level, char *fmt, ...) -__attribute__ ((format (printf, 2, 3))); +PRINTF_FORMAT(2, 3); /** * wpa_hexdump - conditional hex dump @@ -273,6 +359,42 @@ void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf, #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 { \ @@ -287,4 +409,84 @@ void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf, #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 +/* snprintf - used in number of places; sprintf() is _not_ a good replacement + * due to possible buffer overflow; see, e.g., + * http://www.ijs.si/software/snprintf/ for portable implementation of + * snprintf. */ +int snprintf(char *str, size_t size, const char *format, ...); + +/* vsnprintf - only used for wpa_msg() in wpa_supplicant.c */ +int vsnprintf(char *str, size_t size, const char *format, va_list ap); +#endif /* !defined(_MSC_VER) || _MSC_VER < 1400 */ + +/* getopt - only used in main.c */ +int getopt(int argc, char *const argv[], const char *optstring); +extern char *optarg; +extern int optind; + +#ifndef CONFIG_NO_SOCKLEN_T_TYPEDEF +#ifndef __socklen_t_defined +typedef int socklen_t; +#endif +#endif + +/* inline - define as __inline or just define it to be empty, if needed */ +#ifdef CONFIG_NO_INLINE +#define inline +#else +#define inline __inline +#endif + +#ifndef __func__ +#define __func__ "__func__ not defined" +#endif + +#ifndef bswap_16 +#define bswap_16(a) ((((u16) (a) << 8) & 0xff00) | (((u16) (a) >> 8) & 0xff)) +#endif + +#ifndef bswap_32 +#define bswap_32(a) ((((u32) (a) << 24) & 0xff000000) | \ + (((u32) (a) << 8) & 0xff0000) | \ + (((u32) (a) >> 8) & 0xff00) | \ + (((u32) (a) >> 24) & 0xff)) +#endif + +#ifndef MSG_DONTWAIT +#define MSG_DONTWAIT 0 +#endif + +#ifdef _WIN32_WCE +void perror(const char *s); +#endif /* _WIN32_WCE */ + +#endif /* CONFIG_ANSI_C_EXTRA */ + +#define wpa_zalloc(s) os_zalloc((s)) + +#ifdef CONFIG_NATIVE_WINDOWS +void wpa_unicode2ascii_inplace(TCHAR *str); +TCHAR * wpa_strdup_tchar(const char *str); +#else /* CONFIG_NATIVE_WINDOWS */ +#define wpa_unicode2ascii_inplace(s) do { } while (0) +#define wpa_strdup_tchar(s) strdup((s)) +#endif /* CONFIG_NATIVE_WINDOWS */ + +const char * wpa_ssid_txt(u8 *ssid, size_t ssid_len); + +typedef u32 __be32; +typedef u64 __be64; + #endif /* COMMON_H */ diff --git a/contrib/hostapd/config.c b/contrib/hostapd/config.c index 016d9b9..d1b2ba3 100644 --- a/contrib/hostapd/config.c +++ b/contrib/hostapd/config.c @@ -1,7 +1,6 @@ /* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / Configuration file - * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * 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 @@ -13,63 +12,230 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <netinet/in.h> -#include <string.h> -#include <sys/socket.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> +#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 "ieee802_1x.h" /* XXX for EAPOL_VERSION */ +#include "wpa_common.h" -static struct hostapd_config *hostapd_config_defaults(void) +#define MAX_STA_COUNT 2007 + + +static int hostapd_config_read_vlan_file(struct hostapd_bss_config *bss, + const char *fname) { - struct hostapd_config *conf; + FILE *f; + char buf[128], *pos, *pos2; + int line = 0, vlan_id; + struct hostapd_vlan *vlan; - conf = malloc(sizeof(*conf) + sizeof(struct hostapd_radius_servers)); - if (conf == NULL) { + 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; } - memset(conf, 0, sizeof(*conf) + sizeof(struct hostapd_radius_servers)); - conf->radius = (struct hostapd_radius_servers *) (conf + 1); /* 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; } - conf->wep_rekeying_period = 300; - conf->eap_reauth_period = 3600; + bss->radius = wpa_zalloc(sizeof(*bss->radius)); + if (bss->radius == NULL) { + free(conf); + free(bss); + return NULL; + } + + hostapd_config_defaults_bss(bss); - conf->logger_syslog_level = HOSTAPD_LEVEL_INFO; - conf->logger_stdout_level = HOSTAPD_LEVEL_INFO; - conf->logger_syslog = (unsigned int) -1; - conf->logger_stdout = (unsigned int) -1; + conf->num_bss = 1; + conf->bss = bss; - conf->auth_algs = HOSTAPD_AUTH_OPEN | HOSTAPD_AUTH_SHARED_KEY; - conf->eapol_version = EAPOL_VERSION; /* NB: default version */ + 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; - conf->wpa_group_rekey = 600; - conf->wpa_gmk_rekey = 86400; - conf->wpa_key_mgmt = WPA_KEY_MGMT_PSK; - conf->wpa_pairwise = WPA_CIPHER_TKIP; - conf->wpa_group = WPA_CIPHER_TKIP; + memcpy(conf->country, "US ", 3); - conf->radius_server_auth_port = 1812; + 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; } @@ -93,12 +259,19 @@ static int hostapd_parse_ip_addr(const char *txt, struct hostapd_ip_addr *addr) } -static int mac_comp(const void *a, const void *b) +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) { @@ -154,14 +327,14 @@ static int hostapd_config_read_maclist(const char *fname, macaddr **acl, fclose(f); - qsort(*acl, *num, sizeof(macaddr), mac_comp); + qsort(*acl, *num, sizeof(macaddr), hostapd_mac_comp); return 0; } static int hostapd_config_read_wpa_psk(const char *fname, - struct hostapd_config *conf) + struct hostapd_ssid *ssid) { FILE *f; char buf[128], *pos; @@ -201,13 +374,12 @@ static int hostapd_config_read_wpa_psk(const char *fname, break; } - psk = malloc(sizeof(*psk)); + psk = wpa_zalloc(sizeof(*psk)); if (psk == NULL) { printf("WPA PSK allocation failed\n"); ret = -1; break; } - memset(psk, 0, sizeof(*psk)); if (memcmp(addr, "\x00\x00\x00\x00\x00\x00", ETH_ALEN) == 0) psk->group = 1; else @@ -227,7 +399,7 @@ static int hostapd_config_read_wpa_psk(const char *fname, if (len == 64 && hexstr2bin(pos, psk->psk, PMK_LEN) == 0) ok = 1; else if (len >= 8 && len < 64) { - pbkdf2_sha1(pos, conf->ssid, conf->ssid_len, + pbkdf2_sha1(pos, ssid->ssid, ssid->ssid_len, 4096, psk->psk, PMK_LEN); ok = 1; } @@ -239,8 +411,8 @@ static int hostapd_config_read_wpa_psk(const char *fname, break; } - psk->next = conf->wpa_psk; - conf->wpa_psk = psk; + psk->next = ssid->wpa_psk; + ssid->wpa_psk = psk; } fclose(f); @@ -249,42 +421,45 @@ static int hostapd_config_read_wpa_psk(const char *fname, } -int hostapd_setup_wpa_psk(struct hostapd_config *conf) +int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf) { - if (conf->wpa_passphrase != NULL) { - if (conf->wpa_psk != NULL) { + 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(conf->wpa_psk); + free(ssid->wpa_psk); } - conf->wpa_psk = malloc(sizeof(struct hostapd_wpa_psk)); - if (conf->wpa_psk == NULL) { + 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 *) conf->ssid, conf->ssid_len); + (u8 *) ssid->ssid, ssid->ssid_len); wpa_hexdump_ascii(MSG_DEBUG, "PSK (ASCII passphrase)", - (u8 *) conf->wpa_passphrase, - strlen(conf->wpa_passphrase)); - memset(conf->wpa_psk, 0, sizeof(struct hostapd_wpa_psk)); - pbkdf2_sha1(conf->wpa_passphrase, - conf->ssid, conf->ssid_len, - 4096, conf->wpa_psk->psk, PMK_LEN); + (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)", - conf->wpa_psk->psk, PMK_LEN); - conf->wpa_psk->group = 1; + ssid->wpa_psk->psk, PMK_LEN); + ssid->wpa_psk->group = 1; - memset(conf->wpa_passphrase, 0, strlen(conf->wpa_passphrase)); - free(conf->wpa_passphrase); - conf->wpa_passphrase = 0; + memset(ssid->wpa_passphrase, 0, + strlen(ssid->wpa_passphrase)); + free(ssid->wpa_passphrase); + ssid->wpa_passphrase = NULL; } - if (conf->wpa_psk_file) { - if (hostapd_config_read_wpa_psk(conf->wpa_psk_file, conf)) + if (ssid->wpa_psk_file) { + if (hostapd_config_read_wpa_psk(ssid->wpa_psk_file, + &conf->ssid)) return -1; - free(conf->wpa_psk_file); - conf->wpa_psk_file = NULL; + free(ssid->wpa_psk_file); + ssid->wpa_psk_file = NULL; } return 0; @@ -293,7 +468,7 @@ int hostapd_setup_wpa_psk(struct hostapd_config *conf) #ifdef EAP_SERVER static int hostapd_config_read_eap_user(const char *fname, - struct hostapd_config *conf) + struct hostapd_bss_config *conf) { FILE *f; char buf[512], *pos, *start, *pos2; @@ -334,12 +509,11 @@ static int hostapd_config_read_eap_user(const char *fname, goto failed; } - user = malloc(sizeof(*user)); + user = wpa_zalloc(sizeof(*user)); if (user == NULL) { printf("EAP user allocation failed\n"); goto failed; } - memset(user, 0, sizeof(*user)); user->force_version = -1; if (buf[0] == '*') { @@ -363,6 +537,11 @@ static int hostapd_config_read_eap_user(const char *fname, } 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') @@ -385,12 +564,17 @@ static int hostapd_config_read_eap_user(const char *fname, } num_methods = 0; while (*start) { - char *pos2 = strchr(start, ','); - if (pos2) { - *pos2++ = '\0'; + char *pos3 = strchr(start, ','); + if (pos3) { + *pos3++ = '\0'; } - user->methods[num_methods] = eap_get_type(start); - if (user->methods[num_methods] == EAP_TYPE_NONE) { + 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; @@ -399,9 +583,9 @@ static int hostapd_config_read_eap_user(const char *fname, num_methods++; if (num_methods >= EAP_USER_MAX_METHODS) break; - if (pos2 == NULL) + if (pos3 == NULL) break; - start = pos2; + start = pos3; } if (num_methods == 0) { printf("No EAP types configured on line %d in '%s'\n", @@ -453,6 +637,31 @@ static int hostapd_config_read_eap_user(const char *fname, 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 != ' ' && @@ -496,6 +705,7 @@ static int hostapd_config_read_eap_user(const char *fname, failed: if (user) { + free(user->password); free(user->identity); free(user); } @@ -562,7 +772,8 @@ static int hostapd_config_parse_key_mgmt(int line, const char *value) else if (strcmp(start, "WPA-EAP") == 0) val |= WPA_KEY_MGMT_IEEE8021X; else { - printf("Line %d: invalid key_mgmt '%s'", line, start); + printf("Line %d: invalid key_mgmt '%s'\n", + line, start); free(buf); return -1; } @@ -574,7 +785,7 @@ static int hostapd_config_parse_key_mgmt(int line, const char *value) free(buf); if (val == 0) { - printf("Line %d: no key_mgmt values configured.", line); + printf("Line %d: no key_mgmt values configured.\n", line); return -1; } @@ -632,23 +843,308 @@ static int hostapd_config_parse_cipher(int line, const char *value) } -static int hostapd_config_check(struct hostapd_config *conf) +static int hostapd_config_check_bss(struct hostapd_bss_config *bss, + struct hostapd_config *conf) { - if (conf->ieee802_1x && !conf->eap_server && - !conf->radius->auth_servers) { + 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 (conf->wpa && (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) && - conf->wpa_psk == NULL && conf->wpa_passphrase == NULL && - conf->wpa_psk_file == NULL) { + 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; } @@ -656,14 +1152,12 @@ static int hostapd_config_check(struct hostapd_config *conf) 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; - char *accept_mac_file = NULL, *deny_mac_file = NULL; -#ifdef EAP_SERVER - char *eap_user_file = NULL; -#endif /* EAP_SERVER */ + size_t i; f = fopen(fname, "r"); if (f == NULL) { @@ -677,8 +1171,10 @@ struct hostapd_config * hostapd_config_read(const char *fname) fclose(f); return NULL; } + bss = conf->last_bss = conf->bss; while (fgets(buf, sizeof(buf), f)) { + bss = conf->last_bss; line++; if (buf[0] == '#') @@ -704,10 +1200,10 @@ struct hostapd_config * hostapd_config_read(const char *fname) pos++; if (strcmp(buf, "interface") == 0) { - snprintf(conf->iface, sizeof(conf->iface), "%s", pos); + snprintf(conf->bss[0].iface, + sizeof(conf->bss[0].iface), "%s", pos); } else if (strcmp(buf, "bridge") == 0) { - snprintf(conf->bridge, sizeof(conf->bridge), "%s", - pos); + snprintf(bss->bridge, sizeof(bss->bridge), "%s", pos); } else if (strcmp(buf, "driver") == 0) { conf->driver = driver_lookup(pos); if (conf->driver == NULL) { @@ -716,185 +1212,199 @@ struct hostapd_config * hostapd_config_read(const char *fname) errors++; } } else if (strcmp(buf, "debug") == 0) { - conf->debug = atoi(pos); + bss->debug = atoi(pos); } else if (strcmp(buf, "logger_syslog_level") == 0) { - conf->logger_syslog_level = atoi(pos); + bss->logger_syslog_level = atoi(pos); } else if (strcmp(buf, "logger_stdout_level") == 0) { - conf->logger_stdout_level = atoi(pos); + bss->logger_stdout_level = atoi(pos); } else if (strcmp(buf, "logger_syslog") == 0) { - conf->logger_syslog = atoi(pos); + bss->logger_syslog = atoi(pos); } else if (strcmp(buf, "logger_stdout") == 0) { - conf->logger_stdout = atoi(pos); + bss->logger_stdout = atoi(pos); } else if (strcmp(buf, "dump_file") == 0) { - conf->dump_log_name = strdup(pos); + bss->dump_log_name = strdup(pos); } else if (strcmp(buf, "ssid") == 0) { - conf->ssid_len = strlen(pos); - if (conf->ssid_len >= HOSTAPD_SSID_LEN || - conf->ssid_len < 1) { + 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; } - memcpy(conf->ssid, pos, conf->ssid_len); - conf->ssid[conf->ssid_len] = '\0'; - conf->ssid_set = 1; } else if (strcmp(buf, "macaddr_acl") == 0) { - conf->macaddr_acl = atoi(pos); - if (conf->macaddr_acl != ACCEPT_UNLESS_DENIED && - conf->macaddr_acl != DENY_UNLESS_ACCEPTED && - conf->macaddr_acl != USE_EXTERNAL_RADIUS_AUTH) { + 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, conf->macaddr_acl); + line, bss->macaddr_acl); } } else if (strcmp(buf, "accept_mac_file") == 0) { - accept_mac_file = strdup(pos); - if (!accept_mac_file) { - printf("Line %d: allocation failed\n", line); + 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) { - deny_mac_file = strdup(pos); - if (!deny_mac_file) { - printf("Line %d: allocation failed\n", line); + 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, conf->assoc_ap_addr)) { + if (hwaddr_aton(pos, bss->assoc_ap_addr)) { printf("Line %d: invalid MAC address '%s'\n", line, pos); errors++; } - conf->assoc_ap = 1; + bss->assoc_ap = 1; } else if (strcmp(buf, "ieee8021x") == 0) { - conf->ieee802_1x = atoi(pos); + 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) { - conf->eap_server = atoi(pos); + 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) { - conf->eap_server = atoi(pos); + bss->eap_server = atoi(pos); } else if (strcmp(buf, "eap_user_file") == 0) { - free(eap_user_file); - eap_user_file = strdup(pos); - if (!eap_user_file) { - printf("Line %d: allocation failed\n", line); + if (hostapd_config_read_eap_user(pos, bss)) errors++; - } } else if (strcmp(buf, "ca_cert") == 0) { - free(conf->ca_cert); - conf->ca_cert = strdup(pos); + free(bss->ca_cert); + bss->ca_cert = strdup(pos); } else if (strcmp(buf, "server_cert") == 0) { - free(conf->server_cert); - conf->server_cert = strdup(pos); + free(bss->server_cert); + bss->server_cert = strdup(pos); } else if (strcmp(buf, "private_key") == 0) { - free(conf->private_key); - conf->private_key = strdup(pos); + free(bss->private_key); + bss->private_key = strdup(pos); } else if (strcmp(buf, "private_key_passwd") == 0) { - free(conf->private_key_passwd); - conf->private_key_passwd = strdup(pos); + free(bss->private_key_passwd); + bss->private_key_passwd = strdup(pos); } else if (strcmp(buf, "check_crl") == 0) { - conf->check_crl = atoi(pos); + bss->check_crl = atoi(pos); #ifdef EAP_SIM } else if (strcmp(buf, "eap_sim_db") == 0) { - free(conf->eap_sim_db); - conf->eap_sim_db = strdup(pos); + 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; - conf->eap_req_id_text = strdup(pos); - if (conf->eap_req_id_text == NULL) { + 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; } - conf->eap_req_id_text_len = - strlen(conf->eap_req_id_text); - term = strstr(conf->eap_req_id_text, "\\0"); + 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, - conf->eap_req_id_text_len - - (term - conf->eap_req_id_text) - 1); - conf->eap_req_id_text_len--; + 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) { - conf->default_wep_key_len = atoi(pos); - if (conf->default_wep_key_len > 13) { + 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) - conf->default_wep_key_len, + bss->default_wep_key_len, (unsigned long) - conf->default_wep_key_len * 8); + bss->default_wep_key_len * 8); errors++; } } else if (strcmp(buf, "wep_key_len_unicast") == 0) { - conf->individual_wep_key_len = atoi(pos); - if (conf->individual_wep_key_len < 0 || - conf->individual_wep_key_len > 13) { + 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, - conf->individual_wep_key_len, - conf->individual_wep_key_len * 8); + bss->individual_wep_key_len, + bss->individual_wep_key_len * 8); errors++; } } else if (strcmp(buf, "wep_rekey_period") == 0) { - conf->wep_rekeying_period = atoi(pos); - if (conf->wep_rekeying_period < 0) { + bss->wep_rekeying_period = atoi(pos); + if (bss->wep_rekeying_period < 0) { printf("Line %d: invalid period %d\n", - line, conf->wep_rekeying_period); + line, bss->wep_rekeying_period); errors++; } } else if (strcmp(buf, "eap_reauth_period") == 0) { - conf->eap_reauth_period = atoi(pos); - if (conf->eap_reauth_period < 0) { + bss->eap_reauth_period = atoi(pos); + if (bss->eap_reauth_period < 0) { printf("Line %d: invalid period %d\n", - line, conf->eap_reauth_period); + line, bss->eap_reauth_period); errors++; } } 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); + bss->eapol_key_index_workaround = atoi(pos); #ifdef CONFIG_IAPP } else if (strcmp(buf, "iapp_interface") == 0) { - conf->ieee802_11f = 1; - snprintf(conf->iapp_iface, sizeof(conf->iapp_iface), + 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, &conf->own_ip_addr)) { + 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) { - conf->nas_identifier = strdup(pos); + bss->nas_identifier = strdup(pos); } else if (strcmp(buf, "auth_server_addr") == 0) { if (hostapd_config_read_radius_addr( - &conf->radius->auth_servers, - &conf->radius->num_auth_servers, pos, 1812, - &conf->radius->auth_server)) { + &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 (conf->radius->auth_server && + } else if (bss->radius->auth_server && strcmp(buf, "auth_server_port") == 0) { - conf->radius->auth_server->port = atoi(pos); - } else if (conf->radius->auth_server && + 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) { @@ -903,22 +1413,22 @@ struct hostapd_config * hostapd_config_read(const char *fname) "allowed.\n", line); errors++; } - conf->radius->auth_server->shared_secret = + bss->radius->auth_server->shared_secret = (u8 *) strdup(pos); - conf->radius->auth_server->shared_secret_len = len; + bss->radius->auth_server->shared_secret_len = len; } else if (strcmp(buf, "acct_server_addr") == 0) { if (hostapd_config_read_radius_addr( - &conf->radius->acct_servers, - &conf->radius->num_acct_servers, pos, 1813, - &conf->radius->acct_server)) { + &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 (conf->radius->acct_server && + } else if (bss->radius->acct_server && strcmp(buf, "acct_server_port") == 0) { - conf->radius->acct_server->port = atoi(pos); - } else if (conf->radius->acct_server && + 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) { @@ -927,29 +1437,38 @@ struct hostapd_config * hostapd_config_read(const char *fname) "allowed.\n", line); errors++; } - conf->radius->acct_server->shared_secret = + bss->radius->acct_server->shared_secret = (u8 *) strdup(pos); - conf->radius->acct_server->shared_secret_len = len; + bss->radius->acct_server->shared_secret_len = len; } else if (strcmp(buf, "radius_retry_primary_interval") == 0) { - conf->radius->retry_primary_interval = atoi(pos); + bss->radius->retry_primary_interval = atoi(pos); } else if (strcmp(buf, "radius_acct_interim_interval") == 0) { - conf->radius->acct_interim_interval = atoi(pos); + bss->radius->acct_interim_interval = atoi(pos); } else if (strcmp(buf, "auth_algs") == 0) { - conf->auth_algs = atoi(pos); - if (conf->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) { - conf->wpa = atoi(pos); + bss->wpa = atoi(pos); } else if (strcmp(buf, "wpa_group_rekey") == 0) { - conf->wpa_group_rekey = atoi(pos); + bss->wpa_group_rekey = atoi(pos); } else if (strcmp(buf, "wpa_strict_rekey") == 0) { - conf->wpa_strict_rekey = atoi(pos); + bss->wpa_strict_rekey = atoi(pos); } else if (strcmp(buf, "wpa_gmk_rekey") == 0) { - conf->wpa_gmk_rekey = atoi(pos); + bss->wpa_gmk_rekey = atoi(pos); } else if (strcmp(buf, "wpa_passphrase") == 0) { int len = strlen(pos); if (len < 8 || len > 63) { @@ -957,106 +1476,253 @@ struct hostapd_config * hostapd_config_read(const char *fname) " %d (expected 8..63)\n", line, len); errors++; } else { - free(conf->wpa_passphrase); - conf->wpa_passphrase = strdup(pos); + free(bss->ssid.wpa_passphrase); + bss->ssid.wpa_passphrase = strdup(pos); } } else if (strcmp(buf, "wpa_psk") == 0) { - free(conf->wpa_psk); - conf->wpa_psk = malloc(sizeof(struct hostapd_wpa_psk)); - if (conf->wpa_psk) { - memset(conf->wpa_psk, 0, - sizeof(struct hostapd_wpa_psk)); - } - if (conf->wpa_psk == NULL) + 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, conf->wpa_psk->psk, PMK_LEN) - || pos[PMK_LEN * 2] != '\0') { + 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 { - conf->wpa_psk->group = 1; + bss->ssid.wpa_psk->group = 1; } } else if (strcmp(buf, "wpa_psk_file") == 0) { - free(conf->wpa_psk_file); - conf->wpa_psk_file = strdup(pos); - if (!conf->wpa_psk_file) { + 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) { - conf->wpa_key_mgmt = + bss->wpa_key_mgmt = hostapd_config_parse_key_mgmt(line, pos); - if (conf->wpa_key_mgmt == -1) + if (bss->wpa_key_mgmt == -1) errors++; } else if (strcmp(buf, "wpa_pairwise") == 0) { - conf->wpa_pairwise = + bss->wpa_pairwise = hostapd_config_parse_cipher(line, pos); - if (conf->wpa_pairwise == -1 || - conf->wpa_pairwise == 0) + if (bss->wpa_pairwise == -1 || + bss->wpa_pairwise == 0) errors++; - else if (conf->wpa_pairwise & + else if (bss->wpa_pairwise & (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) { printf("Line %d: unsupported pairwise " "cipher suite '%s'\n", - conf->wpa_pairwise, pos); + bss->wpa_pairwise, pos); errors++; } else { - if (conf->wpa_pairwise & WPA_CIPHER_TKIP) - conf->wpa_group = WPA_CIPHER_TKIP; + if (bss->wpa_pairwise & WPA_CIPHER_TKIP) + bss->wpa_group = WPA_CIPHER_TKIP; else - conf->wpa_group = WPA_CIPHER_CCMP; + bss->wpa_group = WPA_CIPHER_CCMP; } #ifdef CONFIG_RSN_PREAUTH } else if (strcmp(buf, "rsn_preauth") == 0) { - conf->rsn_preauth = atoi(pos); + bss->rsn_preauth = atoi(pos); } else if (strcmp(buf, "rsn_preauth_interfaces") == 0) { - conf->rsn_preauth_interfaces = strdup(pos); + 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(conf->ctrl_interface); - conf->ctrl_interface = strdup(pos); + 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) { - conf->ctrl_interface_gid = grp->gr_gid; - conf->ctrl_interface_gid_set = 1; + 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')", - conf->ctrl_interface_gid, group); + bss->ctrl_interface_gid, group); continue; } /* Group name not found - try to parse this as gid */ - conf->ctrl_interface_gid = strtol(group, &endp, 10); + 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; } - conf->ctrl_interface_gid_set = 1; + bss->ctrl_interface_gid_set = 1; wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d", - conf->ctrl_interface_gid); + bss->ctrl_interface_gid); +#endif /* CONFIG_NATIVE_WINDOWS */ #ifdef RADIUS_SERVER } else if (strcmp(buf, "radius_server_clients") == 0) { - free(conf->radius_server_clients); - conf->radius_server_clients = strdup(pos); + free(bss->radius_server_clients); + bss->radius_server_clients = strdup(pos); } else if (strcmp(buf, "radius_server_auth_port") == 0) { - conf->radius_server_auth_port = atoi(pos); + bss->radius_server_auth_port = atoi(pos); } else if (strcmp(buf, "radius_server_ipv6") == 0) { - conf->radius_server_ipv6 = atoi(pos); + bss->radius_server_ipv6 = atoi(pos); #endif /* RADIUS_SERVER */ } else if (strcmp(buf, "test_socket") == 0) { - free(conf->test_socket); - conf->test_socket = strdup(pos); + free(bss->test_socket); + bss->test_socket = strdup(pos); } else if (strcmp(buf, "use_pae_group_addr") == 0) { - conf->use_pae_group_addr = atoi(pos); + 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); @@ -1066,23 +1732,30 @@ struct hostapd_config * hostapd_config_read(const char *fname) fclose(f); - if (hostapd_config_read_maclist(accept_mac_file, &conf->accept_mac, - &conf->num_accept_mac)) - errors++; - free(accept_mac_file); - if (hostapd_config_read_maclist(deny_mac_file, &conf->deny_mac, - &conf->num_deny_mac)) - errors++; - free(deny_mac_file); - -#ifdef EAP_SERVER - if (hostapd_config_read_eap_user(eap_user_file, conf)) - errors++; - free(eap_user_file); -#endif /* EAP_SERVER */ + 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; + } - conf->radius->auth_server = conf->radius->auth_servers; - conf->radius->acct_server = conf->radius->acct_servers; + 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++; @@ -1098,6 +1771,20 @@ struct hostapd_config * hostapd_config_read(const char *fname) } +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) { @@ -1118,7 +1805,17 @@ static void hostapd_config_free_eap_user(struct hostapd_eap_user *user) } -void hostapd_config_free(struct hostapd_config *conf) +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; @@ -1126,15 +1823,18 @@ void hostapd_config_free(struct hostapd_config *conf) if (conf == NULL) return; - psk = conf->wpa_psk; + psk = conf->ssid.wpa_psk; while (psk) { prev = psk; psk = psk->next; free(prev); } - free(conf->wpa_passphrase); - free(conf->wpa_psk_file); + 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) { @@ -1161,13 +1861,41 @@ void hostapd_config_free(struct hostapd_config *conf) 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, u8 *addr) +int hostapd_maclist_found(macaddr *list, int num_entries, const u8 *addr) { int start, end, middle, res; @@ -1189,13 +1917,40 @@ int hostapd_maclist_found(macaddr *list, int num_entries, u8 *addr) } -const u8 * hostapd_get_psk(const struct hostapd_config *conf, const u8 *addr, - const u8 *prev_psk) +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->wpa_psk; psk != NULL; psk = psk->next) { + 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; @@ -1209,7 +1964,7 @@ const u8 * hostapd_get_psk(const struct hostapd_config *conf, const u8 *addr, const struct hostapd_eap_user * -hostapd_get_eap_user(const struct hostapd_config *conf, const u8 *identity, +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; @@ -1219,6 +1974,15 @@ hostapd_get_eap_user(const struct hostapd_config *conf, const u8 *identity, /* 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) diff --git a/contrib/hostapd/config.h b/contrib/hostapd/config.h index 8754a84..fafe8e0 100644 --- a/contrib/hostapd/config.h +++ b/contrib/hostapd/config.h @@ -1,3 +1,17 @@ +/* + * 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. + */ + #ifndef CONFIG_H #define CONFIG_H @@ -7,6 +21,68 @@ typedef u8 macaddr[ETH_ALEN]; struct hostapd_radius_servers; +#define HOSTAPD_MAX_SSID_LEN 32 + +#define NUM_WEP_KEYS 4 +struct hostapd_wep_keys { + u8 idx; + u8 *key[NUM_WEP_KEYS]; + size_t len[NUM_WEP_KEYS]; + int keys_set; + size_t default_len; /* key length used for dynamic key generation */ +}; + +typedef enum hostap_security_policy { + SECURITY_PLAINTEXT = 0, + SECURITY_STATIC_WEP = 1, + SECURITY_IEEE_802_1X = 2, + SECURITY_WPA_PSK = 3, + SECURITY_WPA = 4 +} secpolicy; + +struct hostapd_ssid { + char ssid[HOSTAPD_MAX_SSID_LEN + 1]; + size_t ssid_len; + int ssid_set; + + char vlan[IFNAMSIZ + 1]; + secpolicy security_policy; + + struct hostapd_wpa_psk *wpa_psk; + char *wpa_passphrase; + char *wpa_psk_file; + + struct hostapd_wep_keys wep; + +#define DYNAMIC_VLAN_DISABLED 0 +#define DYNAMIC_VLAN_OPTIONAL 1 +#define DYNAMIC_VLAN_REQUIRED 2 + int dynamic_vlan; +#ifdef CONFIG_FULL_DYNAMIC_VLAN + char *vlan_tagged_interface; +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + struct hostapd_wep_keys **dyn_vlan_keys; + size_t max_dyn_vlan_keys; +}; + + +#define VLAN_ID_WILDCARD -1 + +struct hostapd_vlan { + struct hostapd_vlan *next; + int vlan_id; /* VLAN ID or -1 (VLAN_ID_WILDCARD) for wildcard entry */ + char ifname[IFNAMSIZ + 1]; + int dynamic_vlan; +#ifdef CONFIG_FULL_DYNAMIC_VLAN + +#define DVLAN_CLEAN_BR 0x1 +#define DVLAN_CLEAN_VLAN 0x2 +#define DVLAN_CLEAN_VLAN_PORT 0x4 +#define DVLAN_CLEAN_WLAN_PORT 0x8 + int clean; +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ +}; + #define PMK_LEN 32 struct hostapd_wpa_psk { struct hostapd_wpa_psk *next; @@ -20,19 +96,46 @@ struct hostapd_eap_user { struct hostapd_eap_user *next; u8 *identity; size_t identity_len; - u8 methods[EAP_USER_MAX_METHODS]; + struct { + int vendor; + u32 method; + } methods[EAP_USER_MAX_METHODS]; u8 *password; size_t password_len; int phase2; int force_version; + unsigned int wildcard_prefix:1; + unsigned int password_hash:1; /* whether password is hashed with + * nt_password_hash() */ }; -struct hostapd_config { + +#define NUM_TX_QUEUES 8 + +struct hostapd_tx_queue_params { + int aifs; + int cwmin; + int cwmax; + int burst; /* maximum burst time in 0.1 ms, i.e., 10 = 1 ms */ + int configured; +}; + +struct hostapd_wme_ac_params { + int cwmin; + int cwmax; + int aifs; + int txopLimit; /* in units of 32us */ + int admission_control_mandatory; +}; + + +/** + * struct hostapd_bss_config - Per-BSS configuration + */ +struct hostapd_bss_config { char iface[IFNAMSIZ + 1]; char bridge[IFNAMSIZ + 1]; - const struct driver_ops *driver; - enum { HOSTAPD_LEVEL_DEBUG_VERBOSE = 0, HOSTAPD_LEVEL_DEBUG = 1, @@ -47,6 +150,7 @@ struct hostapd_config { #define HOSTAPD_MODULE_WPA BIT(3) #define HOSTAPD_MODULE_DRIVER BIT(4) #define HOSTAPD_MODULE_IAPP BIT(5) +#define HOSTAPD_MODULE_MLME BIT(6) unsigned int logger_syslog; /* module bitfield */ unsigned int logger_stdout; /* module bitfield */ @@ -56,7 +160,12 @@ struct hostapd_config { 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 */ + + int dtim_period; + int ieee802_1x; /* use IEEE 802.1X */ + int eapol_version; int eap_server; /* Use internal EAP server instead of external * RADIUS server */ struct hostapd_eap_user *eap_user; @@ -65,19 +174,17 @@ struct hostapd_config { char *nas_identifier; struct hostapd_radius_servers *radius; -#define HOSTAPD_SSID_LEN 32 - char ssid[HOSTAPD_SSID_LEN + 1]; - size_t ssid_len; - int ssid_set; + struct hostapd_ssid ssid; + char *eap_req_id_text; /* optional displayable message sent with * EAP Request-Identity */ size_t eap_req_id_text_len; int eapol_key_index_workaround; - int eapol_version; size_t default_wep_key_len; int individual_wep_key_len; int wep_rekeying_period; + int broadcast_key_idx_min, broadcast_key_idx_max; int eap_reauth_period; int ieee802_11f; /* use IEEE 802.11f (IAPP) */ @@ -105,9 +212,6 @@ struct hostapd_config { #define HOSTAPD_WPA_VERSION_WPA BIT(0) #define HOSTAPD_WPA_VERSION_WPA2 BIT(1) int wpa; - struct hostapd_wpa_psk *wpa_psk; - char *wpa_passphrase; - char *wpa_psk_file; #define WPA_KEY_MGMT_IEEE8021X BIT(0) #define WPA_KEY_MGMT_PSK BIT(1) int wpa_key_mgmt; @@ -116,6 +220,14 @@ struct hostapd_config { #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; +#endif /* CONFIG_IEEE80211W */ int wpa_pairwise; int wpa_group; int wpa_group_rekey; @@ -123,6 +235,7 @@ struct hostapd_config { int wpa_gmk_rekey; int rsn_preauth; char *rsn_preauth_interfaces; + int peerkey; char *ctrl_interface; /* directory for UNIX domain sockets */ gid_t ctrl_interface_gid; @@ -144,17 +257,106 @@ struct hostapd_config { * address instead of individual address * (for driver_wired.c). */ + + int ap_max_inactivity; + int ignore_broadcast_ssid; + + int wme_enabled; + + struct hostapd_vlan *vlan, *vlan_tail; + + macaddr bssid; +}; + + +typedef enum { + HOSTAPD_MODE_IEEE80211B, + HOSTAPD_MODE_IEEE80211G, + HOSTAPD_MODE_IEEE80211A, + NUM_HOSTAPD_MODES +} hostapd_hw_mode; + + +/** + * struct hostapd_config - Per-radio interface configuration + */ +struct hostapd_config { + struct hostapd_bss_config *bss, *last_bss; + struct hostapd_radius_servers *radius; + size_t num_bss; + + u16 beacon_int; + int rts_threshold; + int fragm_threshold; + u8 send_probe_response; + u8 channel; + hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */ + enum { + LONG_PREAMBLE = 0, + SHORT_PREAMBLE = 1 + } preamble; + enum { + CTS_PROTECTION_AUTOMATIC = 0, + CTS_PROTECTION_FORCE_ENABLED = 1, + CTS_PROTECTION_FORCE_DISABLED = 2, + CTS_PROTECTION_AUTOMATIC_NO_OLBC = 3, + } cts_protection_type; + + int *supported_rates; + int *basic_rates; + + const struct driver_ops *driver; + + int passive_scan_interval; /* seconds, 0 = disabled */ + int passive_scan_listen; /* usec */ + int passive_scan_mode; + int ap_table_max_size; + int ap_table_expiration_time; + + char country[3]; /* first two octets: country code as described in + * ISO/IEC 3166-1. Third octet: + * ' ' (ascii 32): all environments + * 'O': Outdoor environemnt only + * 'I': Indoor environment only + */ + + int ieee80211d; + unsigned int ieee80211h; /* Enable/Disable 80211h */ + + struct hostapd_tx_queue_params tx_queue[NUM_TX_QUEUES]; + + /* + * WME AC parameters, in same order as 802.1D, i.e. + * 0 = BE (best effort) + * 1 = BK (background) + * 2 = VI (video) + * 3 = VO (voice) + */ + struct hostapd_wme_ac_params wme_ac_params[4]; + + enum { + INTERNAL_BRIDGE_DO_NOT_CONTROL = -1, + INTERNAL_BRIDGE_DISABLED = 0, + INTERNAL_BRIDGE_ENABLED = 1 + } bridge_packets; }; +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, u8 *addr); -const u8 * hostapd_get_psk(const struct hostapd_config *conf, const u8 *addr, - const u8 *prev_psk); -int hostapd_setup_wpa_psk(struct hostapd_config *conf); +int hostapd_maclist_found(macaddr *list, int num_entries, const u8 *addr); +int hostapd_rate_found(int *list, int rate); +int hostapd_wep_key_cmp(struct hostapd_wep_keys *a, + struct hostapd_wep_keys *b); +const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf, + const u8 *addr, const u8 *prev_psk); +int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf); +const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, + int vlan_id); const struct hostapd_eap_user * -hostapd_get_eap_user(const struct hostapd_config *conf, const u8 *identity, +hostapd_get_eap_user(const struct hostapd_bss_config *conf, const u8 *identity, size_t identity_len, int phase2); #endif /* CONFIG_H */ diff --git a/contrib/hostapd/config_types.h b/contrib/hostapd/config_types.h index 12b57cb..ffcffa3 100644 --- a/contrib/hostapd/config_types.h +++ b/contrib/hostapd/config_types.h @@ -1,3 +1,17 @@ +/* + * hostapd / Shared configuration file defines + * 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_TYPES_H #define CONFIG_TYPES_H diff --git a/contrib/hostapd/crypto.c b/contrib/hostapd/crypto.c index b4c8189..c5edd24 100644 --- a/contrib/hostapd/crypto.c +++ b/contrib/hostapd/crypto.c @@ -1,6 +1,6 @@ /* * WPA Supplicant / wrapper functions for libcrypto - * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * 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 @@ -12,9 +12,8 @@ * See README and COPYING for more details. */ -#include <string.h> -#include <sys/types.h> - +#include "includes.h" +#include <openssl/opensslv.h> #include <openssl/md4.h> #include <openssl/md5.h> #include <openssl/sha.h> @@ -36,7 +35,7 @@ void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { MD4_CTX ctx; - int i; + size_t i; MD4_Init(&ctx); for (i = 0; i < num_elem; i++) @@ -70,7 +69,7 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { MD5_CTX ctx; - int i; + size_t i; MD5_Init(&ctx); for (i = 0; i < num_elem; i++) @@ -82,7 +81,7 @@ void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { SHA_CTX ctx; - int i; + size_t i; SHA1_Init(&ctx); for (i = 0; i < num_elem; i++) @@ -91,24 +90,78 @@ void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) } -void sha1_transform(u8 *state, const u8 data[64]) +static void sha1_transform(u8 *state, const u8 data[64]) { SHA_CTX context; - memset(&context, 0, sizeof(context)); - memcpy(&context.h0, state, 5 * 4); + os_memset(&context, 0, sizeof(context)); + os_memcpy(&context.h0, state, 5 * 4); SHA1_Transform(&context, data); - memcpy(state, &context.h0, 5 * 4); + os_memcpy(state, &context.h0, 5 * 4); +} + + +int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) +{ + u8 xkey[64]; + u32 t[5], _t[5]; + int i, j, m, k; + u8 *xpos = x; + u32 carry; + + if (seed_len > sizeof(xkey)) + seed_len = sizeof(xkey); + + /* FIPS 186-2 + change notice 1 */ + + os_memcpy(xkey, seed, seed_len); + os_memset(xkey + seed_len, 0, 64 - seed_len); + t[0] = 0x67452301; + t[1] = 0xEFCDAB89; + t[2] = 0x98BADCFE; + t[3] = 0x10325476; + t[4] = 0xC3D2E1F0; + + m = xlen / 40; + for (j = 0; j < m; j++) { + /* XSEED_j = 0 */ + for (i = 0; i < 2; i++) { + /* XVAL = (XKEY + XSEED_j) mod 2^b */ + + /* w_i = G(t, XVAL) */ + os_memcpy(_t, t, 20); + sha1_transform((u8 *) _t, xkey); + _t[0] = host_to_be32(_t[0]); + _t[1] = host_to_be32(_t[1]); + _t[2] = host_to_be32(_t[2]); + _t[3] = host_to_be32(_t[3]); + _t[4] = host_to_be32(_t[4]); + os_memcpy(xpos, _t, 20); + + /* XKEY = (1 + XKEY + w_i) mod 2^b */ + carry = 1; + for (k = 19; k >= 0; k--) { + carry += xkey[k] + xpos[k]; + xkey[k] = carry & 0xff; + carry >>= 8; + } + + xpos += 20; + } + /* x_j = w_0|w_1 */ + } + + return 0; } void * aes_encrypt_init(const u8 *key, size_t len) { AES_KEY *ak; - ak = malloc(sizeof(*ak)); + ak = os_malloc(sizeof(*ak)); if (ak == NULL) return NULL; if (AES_set_encrypt_key(key, 8 * len, ak) < 0) { - free(ak); + os_free(ak); return NULL; } return ak; @@ -123,18 +176,18 @@ void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) void aes_encrypt_deinit(void *ctx) { - free(ctx); + os_free(ctx); } void * aes_decrypt_init(const u8 *key, size_t len) { AES_KEY *ak; - ak = malloc(sizeof(*ak)); + ak = os_malloc(sizeof(*ak)); if (ak == NULL) return NULL; if (AES_set_decrypt_key(key, 8 * len, ak) < 0) { - free(ak); + os_free(ak); return NULL; } return ak; @@ -149,6 +202,6 @@ void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) void aes_decrypt_deinit(void *ctx) { - free(ctx); + os_free(ctx); } #endif /* EAP_TLS_FUNCS */ diff --git a/contrib/hostapd/crypto.h b/contrib/hostapd/crypto.h index e664861..00b13b9 100644 --- a/contrib/hostapd/crypto.h +++ b/contrib/hostapd/crypto.h @@ -1,6 +1,6 @@ /* * WPA Supplicant / wrapper functions for crypto libraries - * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * 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 @@ -56,16 +56,28 @@ void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); /** - * sha1_transform - Perform one SHA-1 transform step - * @state: SHA-1 state - * @data: Input data for the SHA-1 transform + * fips186_2-prf - NIST FIPS Publication 186-2 change notice 1 PRF + * @seed: Seed/key for the PRF + * @seed_len: Seed length in bytes + * @x: Buffer for PRF output + * @xlen: Output length in bytes + * Returns: 0 on success, -1 on failure * - * This function is used to implement random number generation specified in - * NIST FIPS Publication 186-2 for EAP-SIM. This PRF uses a function that is - * similar to SHA-1, but has different message padding and as such, access to - * just part of the SHA-1 is needed. + * This function implements random number generation specified in NIST FIPS + * Publication 186-2 for EAP-SIM. This PRF uses a function that is similar to + * SHA-1, but has different message padding. */ -void sha1_transform(u8 *state, const u8 data[64]); +int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen); + +/** + * sha256_vector - SHA256 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + */ +void sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac); /** * des_encrypt - Encrypt one block with DES @@ -120,4 +132,282 @@ void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain); void aes_decrypt_deinit(void *ctx); +enum crypto_hash_alg { + CRYPTO_HASH_ALG_MD5, CRYPTO_HASH_ALG_SHA1, + CRYPTO_HASH_ALG_HMAC_MD5, CRYPTO_HASH_ALG_HMAC_SHA1 +}; + +struct crypto_hash; + +/** + * crypto_hash_init - Initialize hash/HMAC function + * @alg: Hash algorithm + * @key: Key for keyed hash (e.g., HMAC) or %NULL if not needed + * @key_len: Length of the key in bytes + * Returns: Pointer to hash context to use with other hash functions or %NULL + * on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +struct crypto_hash * crypto_hash_init(enum crypto_hash_alg alg, const u8 *key, + size_t key_len); + +/** + * crypto_hash_update - Add data to hash calculation + * @ctx: Context pointer from crypto_hash_init() + * @data: Data buffer to add + * @len: Length of the buffer + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +void crypto_hash_update(struct crypto_hash *ctx, const u8 *data, size_t len); + +/** + * crypto_hash_finish - Complete hash calculation + * @ctx: Context pointer from crypto_hash_init() + * @hash: Buffer for hash value or %NULL if caller is just freeing the hash + * context + * @len: Pointer to length of the buffer or %NULL if caller is just freeing the + * hash context; on return, this is set to the actual length of the hash value + * Returns: 0 on success, -1 if buffer is too small (len set to needed length), + * or -2 on other failures (including failed crypto_hash_update() operations) + * + * This function calculates the hash value and frees the context buffer that + * was used for hash calculation. + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int crypto_hash_finish(struct crypto_hash *ctx, u8 *hash, size_t *len); + + +enum crypto_cipher_alg { + CRYPTO_CIPHER_NULL = 0, CRYPTO_CIPHER_ALG_AES, CRYPTO_CIPHER_ALG_3DES, + CRYPTO_CIPHER_ALG_DES, CRYPTO_CIPHER_ALG_RC2, CRYPTO_CIPHER_ALG_RC4 +}; + +struct crypto_cipher; + +/** + * crypto_cipher_init - Initialize block/stream cipher function + * @alg: Cipher algorithm + * @iv: Initialization vector for block ciphers or %NULL for stream ciphers + * @key: Cipher key + * @key_len: Length of key in bytes + * Returns: Pointer to cipher context to use with other cipher functions or + * %NULL on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, + const u8 *iv, const u8 *key, + size_t key_len); + +/** + * crypto_cipher_encrypt - Cipher encrypt + * @ctx: Context pointer from crypto_cipher_init() + * @plain: Plaintext to cipher + * @crypt: Resulting ciphertext + * @len: Length of the plaintext + * Returns: 0 on success, -1 on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, + u8 *crypt, size_t len); + +/** + * crypto_cipher_decrypt - Cipher decrypt + * @ctx: Context pointer from crypto_cipher_init() + * @crypt: Ciphertext to decrypt + * @plain: Resulting plaintext + * @len: Length of the cipher text + * Returns: 0 on success, -1 on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, + u8 *plain, size_t len); + +/** + * crypto_cipher_decrypt - Free cipher context + * @ctx: Context pointer from crypto_cipher_init() + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +void crypto_cipher_deinit(struct crypto_cipher *ctx); + + +struct crypto_public_key; +struct crypto_private_key; + +/** + * crypto_public_key_import - Import an RSA public key + * @key: Key buffer (DER encoded RSA public key) + * @len: Key buffer length in bytes + * Returns: Pointer to the public key or %NULL on failure + * + * This function can just return %NULL if the crypto library supports X.509 + * parsing. In that case, crypto_public_key_from_cert() is used to import the + * public key from a certificate. + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len); + +/** + * crypto_private_key_import - Import an RSA private key + * @key: Key buffer (DER encoded RSA private key) + * @len: Key buffer length in bytes + * Returns: Pointer to the private key or %NULL on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +struct crypto_private_key * crypto_private_key_import(const u8 *key, + size_t len); + +/** + * crypto_public_key_from_cert - Import an RSA public key from a certificate + * @buf: DER encoded X.509 certificate + * @len: Certificate buffer length in bytes + * Returns: Pointer to public key or %NULL on failure + * + * This function can just return %NULL if the crypto library does not support + * X.509 parsing. In that case, internal code will be used to parse the + * certificate and public key is imported using crypto_public_key_import(). + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf, + size_t len); + +/** + * crypto_public_key_encrypt_pkcs1_v15 - Public key encryption (PKCS #1 v1.5) + * @key: Public key + * @in: Plaintext buffer + * @inlen: Length of plaintext buffer in bytes + * @out: Output buffer for encrypted data + * @outlen: Length of output buffer in bytes; set to used length on success + * Returns: 0 on success, -1 on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int 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_sign_pkcs1 - Sign with private key (PKCS #1) + * @key: Private key from crypto_private_key_import() + * @in: Plaintext buffer + * @inlen: Length of plaintext buffer in bytes + * @out: Output buffer for encrypted (signed) data + * @outlen: Length of output buffer in bytes; set to used length on success + * Returns: 0 on success, -1 on failure + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int crypto_private_key_sign_pkcs1(struct crypto_private_key *key, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen); + +/** + * crypto_public_key_free - Free public key + * @key: Public key + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +void crypto_public_key_free(struct crypto_public_key *key); + +/** + * crypto_private_key_free - Free private key + * @key: Private key from crypto_private_key_import() + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +void crypto_private_key_free(struct crypto_private_key *key); + +/** + * crypto_public_key_decrypt_pkcs1 - Decrypt PKCS #1 signature + * @key: Public key + * @crypt: Encrypted signature data (using the private key) + * @crypt_len: Encrypted signature data length + * @plain: Buffer for plaintext (at least crypt_len bytes) + * @plain_len: Plaintext length (max buffer size on input, real len on output); + * Returns: 0 on success, -1 on failure + */ +int crypto_public_key_decrypt_pkcs1(struct crypto_public_key *key, + const u8 *crypt, size_t crypt_len, + u8 *plain, size_t *plain_len); + +/** + * crypto_global_init - Initialize crypto wrapper + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int crypto_global_init(void); + +/** + * crypto_global_deinit - Deinitialize crypto wrapper + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +void crypto_global_deinit(void); + +/** + * crypto_mod_exp - Modular exponentiation of large integers + * @base: Base integer (big endian byte array) + * @base_len: Length of base integer in bytes + * @power: Power integer (big endian byte array) + * @power_len: Length of power integer in bytes + * @modulus: Modulus integer (big endian byte array) + * @modulus_len: Length of modulus integer in bytes + * @result: Buffer for the result + * @result_len: Result length (max buffer size on input, real len on output) + * Returns: 0 on success, -1 on failure + * + * This function calculates result = base ^ power mod modulus. modules_len is + * used as the maximum size of modulus buffer. It is set to the used size on + * success. + * + * This function is only used with internal TLSv1 implementation + * (CONFIG_TLS=internal). If that is not used, the crypto wrapper does not need + * to implement this. + */ +int 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/contrib/hostapd/ctrl_iface.c b/contrib/hostapd/ctrl_iface.c index ff730d4..9863782 100644 --- a/contrib/hostapd/ctrl_iface.c +++ b/contrib/hostapd/ctrl_iface.c @@ -1,7 +1,6 @@ /* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / UNIX domain socket -based control interface - * Copyright (c) 2004, Jouni Malinen <jkmaline@cc.hut.fi> + * hostapd / UNIX domain socket -based control interface + * 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 @@ -13,17 +12,12 @@ * 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 "includes.h" + +#ifndef CONFIG_NATIVE_WINDOWS + #include <sys/un.h> -#include <sys/uio.h> #include <sys/stat.h> -#include <errno.h> -#include <netinet/in.h> #include "hostapd.h" #include "eloop.h" @@ -35,6 +29,7 @@ #include "ieee802_11.h" #include "ctrl_iface.h" #include "sta_info.h" +#include "accounting.h" struct wpa_ctrl_dst { @@ -52,10 +47,9 @@ static int hostapd_ctrl_iface_attach(struct hostapd_data *hapd, { struct wpa_ctrl_dst *dst; - dst = malloc(sizeof(*dst)); + dst = wpa_zalloc(sizeof(*dst)); if (dst == NULL) return -1; - memset(dst, 0, sizeof(*dst)); memcpy(&dst->addr, from, sizeof(struct sockaddr_un)); dst->addrlen = fromlen; dst->debug_level = MSG_INFO; @@ -122,20 +116,26 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, struct sta_info *sta, char *buf, size_t buflen) { - int len, res; + int len, res, ret; if (sta == NULL) { - return snprintf(buf, buflen, "FAIL\n"); + ret = snprintf(buf, buflen, "FAIL\n"); + if (ret < 0 || (size_t) ret >= buflen) + return 0; + return ret; } len = 0; - len += snprintf(buf + len, buflen - len, MACSTR "\n", - MAC2STR(sta->addr)); + ret = snprintf(buf + len, buflen - len, MACSTR "\n", + MAC2STR(sta->addr)); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; res = ieee802_11_get_mib_sta(hapd, sta, buf + len, buflen - len); if (res >= 0) len += res; - res = wpa_get_mib_sta(hapd, sta, buf + len, buflen - len); + res = wpa_get_mib_sta(sta->wpa_sm, buf + len, buflen - len); if (res >= 0) len += res; res = ieee802_1x_get_mib_sta(hapd, sta, buf + len, buflen - len); @@ -158,9 +158,14 @@ static int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, char *buf, size_t buflen) { u8 addr[ETH_ALEN]; + int ret; - if (hwaddr_aton(txtaddr, addr)) - return snprintf(buf, buflen, "FAIL\n"); + if (hwaddr_aton(txtaddr, addr)) { + ret = snprintf(buf, buflen, "FAIL\n"); + if (ret < 0 || (size_t) ret >= buflen) + return 0; + return ret; + } return hostapd_ctrl_iface_sta_mib(hapd, ap_get_sta(hapd, addr), buf, buflen); } @@ -172,14 +177,46 @@ static int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, { u8 addr[ETH_ALEN]; struct sta_info *sta; + int ret; if (hwaddr_aton(txtaddr, addr) || - (sta = ap_get_sta(hapd, addr)) == NULL) - return snprintf(buf, buflen, "FAIL\n"); + (sta = ap_get_sta(hapd, addr)) == NULL) { + ret = snprintf(buf, buflen, "FAIL\n"); + if (ret < 0 || (size_t) ret >= buflen) + return 0; + return ret; + } return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen); } +static int hostapd_ctrl_iface_new_sta(struct hostapd_data *hapd, + const char *txtaddr) +{ + u8 addr[ETH_ALEN]; + struct sta_info *sta; + + wpa_printf(MSG_DEBUG, "CTRL_IFACE NEW_STA %s", txtaddr); + + if (hwaddr_aton(txtaddr, addr)) + return -1; + + sta = ap_get_sta(hapd, addr); + if (sta) + return 0; + + wpa_printf(MSG_DEBUG, "Add new STA " MACSTR " based on ctrl_iface " + "notification", MAC2STR(addr)); + sta = ap_sta_add(hapd, addr); + if (sta == NULL) + return -1; + + hostapd_new_assoc_sta(hapd, sta, 0); + accounting_sta_get_id(hapd, sta); + return 0; +} + + static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, void *sock_ctx) { @@ -217,7 +254,7 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, } else if (strcmp(buf, "MIB") == 0) { reply_len = ieee802_11_get_mib(hapd, reply, reply_size); if (reply_len >= 0) { - res = wpa_get_mib(hapd, reply + reply_len, + res = wpa_get_mib(hapd->wpa_auth, reply + reply_len, reply_size - reply_len); if (res < 0) reply_len = -1; @@ -260,6 +297,9 @@ static void hostapd_ctrl_iface_receive(int sock, void *eloop_ctx, if (hostapd_ctrl_iface_level(hapd, &from, fromlen, buf + 6)) reply_len = -1; + } else if (strncmp(buf, "NEW_STA ", 8) == 0) { + if (hostapd_ctrl_iface_new_sta(hapd, buf + 8)) + reply_len = -1; } else { memcpy(reply, "UNKNOWN COMMAND\n", 16); reply_len = 16; @@ -290,6 +330,7 @@ static char * hostapd_ctrl_iface_path(struct hostapd_data *hapd) snprintf(buf, len, "%s/%s", hapd->conf->ctrl_interface, hapd->conf->iface); + buf[len - 1] = '\0'; return buf; } @@ -454,3 +495,5 @@ void hostapd_ctrl_iface_send(struct hostapd_data *hapd, int level, dst = next; } } + +#endif /* CONFIG_NATIVE_WINDOWS */ diff --git a/contrib/hostapd/ctrl_iface.h b/contrib/hostapd/ctrl_iface.h index ef1a541..2ac2f3b 100644 --- a/contrib/hostapd/ctrl_iface.h +++ b/contrib/hostapd/ctrl_iface.h @@ -1,3 +1,17 @@ +/* + * hostapd / UNIX domain socket -based control interface + * 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. + */ + #ifndef CTRL_IFACE_H #define CTRL_IFACE_H diff --git a/contrib/hostapd/defconfig b/contrib/hostapd/defconfig index e8f4e4f..8fe4bf9 100644 --- a/contrib/hostapd/defconfig +++ b/contrib/hostapd/defconfig @@ -22,6 +22,15 @@ CONFIG_DRIVER_HOSTAP=y # 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 FreeBSD net80211 layer (e.g., Atheros driver) #CONFIG_DRIVER_BSD=y #CFLAGS += -I/usr/local/include @@ -33,6 +42,15 @@ CONFIG_IAPP=y # WPA2/IEEE 802.11i RSN pre-authentication CONFIG_RSN_PREAUTH=y +# PeerKey handshake for Station to Station Link (IEEE 802.11e DLS) +CONFIG_PEERKEY=y + +# IEEE 802.11w (management frame protection) +# This version is an experimental implementation based on IEEE 802.11w/D1.0 +# draft and is subject to change since the standard has not yet been finalized. +# Driver support is also needed for IEEE 802.11w. +#CONFIG_IEEE80211W=y + # Integrated EAP server CONFIG_EAP=y @@ -57,12 +75,23 @@ CONFIG_EAP_TTLS=y # EAP-SIM for the integrated EAP server #CONFIG_EAP_SIM=y +# EAP-AKA for the integrated EAP server +#CONFIG_EAP_AKA=y + # EAP-PAX for the integrated EAP server #CONFIG_EAP_PAX=y # EAP-PSK for the integrated EAP server (this is _not_ needed for WPA-PSK) #CONFIG_EAP_PSK=y +# EAP-SAKE for the integrated EAP server +#CONFIG_EAP_SAKE=y + +# EAP-GPSK for the integrated EAP server +#CONFIG_EAP_GPSK=y +# Include support for optional SHA256 cipher suite in EAP-GPSK +#CONFIG_EAP_GPSK_SHA256=y + # 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 diff --git a/contrib/hostapd/defs.h b/contrib/hostapd/defs.h index 6f9881d..603fc55 100644 --- a/contrib/hostapd/defs.h +++ b/contrib/hostapd/defs.h @@ -1,6 +1,6 @@ /* * WPA Supplicant - Common definitions - * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * 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 @@ -24,7 +24,8 @@ typedef enum { FALSE = 0, TRUE = 1 } Boolean; -typedef enum { WPA_ALG_NONE, WPA_ALG_WEP, WPA_ALG_TKIP, WPA_ALG_CCMP } wpa_alg; +typedef enum { WPA_ALG_NONE, WPA_ALG_WEP, WPA_ALG_TKIP, WPA_ALG_CCMP, + WPA_ALG_IGTK, WPA_ALG_DHV } 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, @@ -128,4 +129,12 @@ typedef enum { WPA_COMPLETED } wpa_states; +#define MLME_SETPROTECTION_PROTECT_TYPE_NONE 0 +#define MLME_SETPROTECTION_PROTECT_TYPE_RX 1 +#define MLME_SETPROTECTION_PROTECT_TYPE_TX 2 +#define MLME_SETPROTECTION_PROTECT_TYPE_RX_TX 3 + +#define MLME_SETPROTECTION_KEY_TYPE_GROUP 0 +#define MLME_SETPROTECTION_KEY_TYPE_PAIRWISE 1 + #endif /* DEFS_H */ diff --git a/contrib/hostapd/des.c b/contrib/hostapd/des.c new file mode 100644 index 0000000..8e0d56f --- /dev/null +++ b/contrib/hostapd/des.c @@ -0,0 +1,476 @@ +/* + * DES and 3DES-EDE ciphers + * + * Modifications to LibTomCrypt implementation: + * 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" + + +#ifdef INTERNAL_DES + +/* + * This implementation is based on a DES implementation included in + * LibTomCrypt. The version here is modified to fit in wpa_supplicant/hostapd + * coding style. + */ + +/* LibTomCrypt, modular cryptographic library -- Tom St Denis + * + * LibTomCrypt is a library that provides various cryptographic + * algorithms in a highly modular and flexible manner. + * + * The library is free for all purposes without any express + * guarantee it works. + * + * Tom St Denis, tomstdenis@gmail.com, http://libtomcrypt.com + */ + +/** + DES code submitted by Dobes Vandermeer +*/ + +#define ROLc(x, y) \ + ((((unsigned long) (x) << (unsigned long) ((y) & 31)) | \ + (((unsigned long) (x) & 0xFFFFFFFFUL) >> \ + (unsigned long) (32 - ((y) & 31)))) & 0xFFFFFFFFUL) +#define RORc(x, y) \ + (((((unsigned long) (x) & 0xFFFFFFFFUL) >> \ + (unsigned long) ((y) & 31)) | \ + ((unsigned long) (x) << (unsigned long) (32 - ((y) & 31)))) & \ + 0xFFFFFFFFUL) + + +static const u32 bytebit[8] = +{ + 0200, 0100, 040, 020, 010, 04, 02, 01 +}; + +static const u32 bigbyte[24] = +{ + 0x800000UL, 0x400000UL, 0x200000UL, 0x100000UL, + 0x80000UL, 0x40000UL, 0x20000UL, 0x10000UL, + 0x8000UL, 0x4000UL, 0x2000UL, 0x1000UL, + 0x800UL, 0x400UL, 0x200UL, 0x100UL, + 0x80UL, 0x40UL, 0x20UL, 0x10UL, + 0x8UL, 0x4UL, 0x2UL, 0x1L +}; + +/* Use the key schedule specific in the standard (ANSI X3.92-1981) */ + +static const u8 pc1[56] = { + 56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, + 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, + 62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, + 13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 +}; + +static const u8 totrot[16] = { + 1, 2, 4, 6, + 8, 10, 12, 14, + 15, 17, 19, 21, + 23, 25, 27, 28 +}; + +static const u8 pc2[48] = { + 13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9, + 22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1, + 40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47, + 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 +}; + + +static const u32 SP1[64] = +{ + 0x01010400UL, 0x00000000UL, 0x00010000UL, 0x01010404UL, + 0x01010004UL, 0x00010404UL, 0x00000004UL, 0x00010000UL, + 0x00000400UL, 0x01010400UL, 0x01010404UL, 0x00000400UL, + 0x01000404UL, 0x01010004UL, 0x01000000UL, 0x00000004UL, + 0x00000404UL, 0x01000400UL, 0x01000400UL, 0x00010400UL, + 0x00010400UL, 0x01010000UL, 0x01010000UL, 0x01000404UL, + 0x00010004UL, 0x01000004UL, 0x01000004UL, 0x00010004UL, + 0x00000000UL, 0x00000404UL, 0x00010404UL, 0x01000000UL, + 0x00010000UL, 0x01010404UL, 0x00000004UL, 0x01010000UL, + 0x01010400UL, 0x01000000UL, 0x01000000UL, 0x00000400UL, + 0x01010004UL, 0x00010000UL, 0x00010400UL, 0x01000004UL, + 0x00000400UL, 0x00000004UL, 0x01000404UL, 0x00010404UL, + 0x01010404UL, 0x00010004UL, 0x01010000UL, 0x01000404UL, + 0x01000004UL, 0x00000404UL, 0x00010404UL, 0x01010400UL, + 0x00000404UL, 0x01000400UL, 0x01000400UL, 0x00000000UL, + 0x00010004UL, 0x00010400UL, 0x00000000UL, 0x01010004UL +}; + +static const u32 SP2[64] = +{ + 0x80108020UL, 0x80008000UL, 0x00008000UL, 0x00108020UL, + 0x00100000UL, 0x00000020UL, 0x80100020UL, 0x80008020UL, + 0x80000020UL, 0x80108020UL, 0x80108000UL, 0x80000000UL, + 0x80008000UL, 0x00100000UL, 0x00000020UL, 0x80100020UL, + 0x00108000UL, 0x00100020UL, 0x80008020UL, 0x00000000UL, + 0x80000000UL, 0x00008000UL, 0x00108020UL, 0x80100000UL, + 0x00100020UL, 0x80000020UL, 0x00000000UL, 0x00108000UL, + 0x00008020UL, 0x80108000UL, 0x80100000UL, 0x00008020UL, + 0x00000000UL, 0x00108020UL, 0x80100020UL, 0x00100000UL, + 0x80008020UL, 0x80100000UL, 0x80108000UL, 0x00008000UL, + 0x80100000UL, 0x80008000UL, 0x00000020UL, 0x80108020UL, + 0x00108020UL, 0x00000020UL, 0x00008000UL, 0x80000000UL, + 0x00008020UL, 0x80108000UL, 0x00100000UL, 0x80000020UL, + 0x00100020UL, 0x80008020UL, 0x80000020UL, 0x00100020UL, + 0x00108000UL, 0x00000000UL, 0x80008000UL, 0x00008020UL, + 0x80000000UL, 0x80100020UL, 0x80108020UL, 0x00108000UL +}; + +static const u32 SP3[64] = +{ + 0x00000208UL, 0x08020200UL, 0x00000000UL, 0x08020008UL, + 0x08000200UL, 0x00000000UL, 0x00020208UL, 0x08000200UL, + 0x00020008UL, 0x08000008UL, 0x08000008UL, 0x00020000UL, + 0x08020208UL, 0x00020008UL, 0x08020000UL, 0x00000208UL, + 0x08000000UL, 0x00000008UL, 0x08020200UL, 0x00000200UL, + 0x00020200UL, 0x08020000UL, 0x08020008UL, 0x00020208UL, + 0x08000208UL, 0x00020200UL, 0x00020000UL, 0x08000208UL, + 0x00000008UL, 0x08020208UL, 0x00000200UL, 0x08000000UL, + 0x08020200UL, 0x08000000UL, 0x00020008UL, 0x00000208UL, + 0x00020000UL, 0x08020200UL, 0x08000200UL, 0x00000000UL, + 0x00000200UL, 0x00020008UL, 0x08020208UL, 0x08000200UL, + 0x08000008UL, 0x00000200UL, 0x00000000UL, 0x08020008UL, + 0x08000208UL, 0x00020000UL, 0x08000000UL, 0x08020208UL, + 0x00000008UL, 0x00020208UL, 0x00020200UL, 0x08000008UL, + 0x08020000UL, 0x08000208UL, 0x00000208UL, 0x08020000UL, + 0x00020208UL, 0x00000008UL, 0x08020008UL, 0x00020200UL +}; + +static const u32 SP4[64] = +{ + 0x00802001UL, 0x00002081UL, 0x00002081UL, 0x00000080UL, + 0x00802080UL, 0x00800081UL, 0x00800001UL, 0x00002001UL, + 0x00000000UL, 0x00802000UL, 0x00802000UL, 0x00802081UL, + 0x00000081UL, 0x00000000UL, 0x00800080UL, 0x00800001UL, + 0x00000001UL, 0x00002000UL, 0x00800000UL, 0x00802001UL, + 0x00000080UL, 0x00800000UL, 0x00002001UL, 0x00002080UL, + 0x00800081UL, 0x00000001UL, 0x00002080UL, 0x00800080UL, + 0x00002000UL, 0x00802080UL, 0x00802081UL, 0x00000081UL, + 0x00800080UL, 0x00800001UL, 0x00802000UL, 0x00802081UL, + 0x00000081UL, 0x00000000UL, 0x00000000UL, 0x00802000UL, + 0x00002080UL, 0x00800080UL, 0x00800081UL, 0x00000001UL, + 0x00802001UL, 0x00002081UL, 0x00002081UL, 0x00000080UL, + 0x00802081UL, 0x00000081UL, 0x00000001UL, 0x00002000UL, + 0x00800001UL, 0x00002001UL, 0x00802080UL, 0x00800081UL, + 0x00002001UL, 0x00002080UL, 0x00800000UL, 0x00802001UL, + 0x00000080UL, 0x00800000UL, 0x00002000UL, 0x00802080UL +}; + +static const u32 SP5[64] = +{ + 0x00000100UL, 0x02080100UL, 0x02080000UL, 0x42000100UL, + 0x00080000UL, 0x00000100UL, 0x40000000UL, 0x02080000UL, + 0x40080100UL, 0x00080000UL, 0x02000100UL, 0x40080100UL, + 0x42000100UL, 0x42080000UL, 0x00080100UL, 0x40000000UL, + 0x02000000UL, 0x40080000UL, 0x40080000UL, 0x00000000UL, + 0x40000100UL, 0x42080100UL, 0x42080100UL, 0x02000100UL, + 0x42080000UL, 0x40000100UL, 0x00000000UL, 0x42000000UL, + 0x02080100UL, 0x02000000UL, 0x42000000UL, 0x00080100UL, + 0x00080000UL, 0x42000100UL, 0x00000100UL, 0x02000000UL, + 0x40000000UL, 0x02080000UL, 0x42000100UL, 0x40080100UL, + 0x02000100UL, 0x40000000UL, 0x42080000UL, 0x02080100UL, + 0x40080100UL, 0x00000100UL, 0x02000000UL, 0x42080000UL, + 0x42080100UL, 0x00080100UL, 0x42000000UL, 0x42080100UL, + 0x02080000UL, 0x00000000UL, 0x40080000UL, 0x42000000UL, + 0x00080100UL, 0x02000100UL, 0x40000100UL, 0x00080000UL, + 0x00000000UL, 0x40080000UL, 0x02080100UL, 0x40000100UL +}; + +static const u32 SP6[64] = +{ + 0x20000010UL, 0x20400000UL, 0x00004000UL, 0x20404010UL, + 0x20400000UL, 0x00000010UL, 0x20404010UL, 0x00400000UL, + 0x20004000UL, 0x00404010UL, 0x00400000UL, 0x20000010UL, + 0x00400010UL, 0x20004000UL, 0x20000000UL, 0x00004010UL, + 0x00000000UL, 0x00400010UL, 0x20004010UL, 0x00004000UL, + 0x00404000UL, 0x20004010UL, 0x00000010UL, 0x20400010UL, + 0x20400010UL, 0x00000000UL, 0x00404010UL, 0x20404000UL, + 0x00004010UL, 0x00404000UL, 0x20404000UL, 0x20000000UL, + 0x20004000UL, 0x00000010UL, 0x20400010UL, 0x00404000UL, + 0x20404010UL, 0x00400000UL, 0x00004010UL, 0x20000010UL, + 0x00400000UL, 0x20004000UL, 0x20000000UL, 0x00004010UL, + 0x20000010UL, 0x20404010UL, 0x00404000UL, 0x20400000UL, + 0x00404010UL, 0x20404000UL, 0x00000000UL, 0x20400010UL, + 0x00000010UL, 0x00004000UL, 0x20400000UL, 0x00404010UL, + 0x00004000UL, 0x00400010UL, 0x20004010UL, 0x00000000UL, + 0x20404000UL, 0x20000000UL, 0x00400010UL, 0x20004010UL +}; + +static const u32 SP7[64] = +{ + 0x00200000UL, 0x04200002UL, 0x04000802UL, 0x00000000UL, + 0x00000800UL, 0x04000802UL, 0x00200802UL, 0x04200800UL, + 0x04200802UL, 0x00200000UL, 0x00000000UL, 0x04000002UL, + 0x00000002UL, 0x04000000UL, 0x04200002UL, 0x00000802UL, + 0x04000800UL, 0x00200802UL, 0x00200002UL, 0x04000800UL, + 0x04000002UL, 0x04200000UL, 0x04200800UL, 0x00200002UL, + 0x04200000UL, 0x00000800UL, 0x00000802UL, 0x04200802UL, + 0x00200800UL, 0x00000002UL, 0x04000000UL, 0x00200800UL, + 0x04000000UL, 0x00200800UL, 0x00200000UL, 0x04000802UL, + 0x04000802UL, 0x04200002UL, 0x04200002UL, 0x00000002UL, + 0x00200002UL, 0x04000000UL, 0x04000800UL, 0x00200000UL, + 0x04200800UL, 0x00000802UL, 0x00200802UL, 0x04200800UL, + 0x00000802UL, 0x04000002UL, 0x04200802UL, 0x04200000UL, + 0x00200800UL, 0x00000000UL, 0x00000002UL, 0x04200802UL, + 0x00000000UL, 0x00200802UL, 0x04200000UL, 0x00000800UL, + 0x04000002UL, 0x04000800UL, 0x00000800UL, 0x00200002UL +}; + +static const u32 SP8[64] = +{ + 0x10001040UL, 0x00001000UL, 0x00040000UL, 0x10041040UL, + 0x10000000UL, 0x10001040UL, 0x00000040UL, 0x10000000UL, + 0x00040040UL, 0x10040000UL, 0x10041040UL, 0x00041000UL, + 0x10041000UL, 0x00041040UL, 0x00001000UL, 0x00000040UL, + 0x10040000UL, 0x10000040UL, 0x10001000UL, 0x00001040UL, + 0x00041000UL, 0x00040040UL, 0x10040040UL, 0x10041000UL, + 0x00001040UL, 0x00000000UL, 0x00000000UL, 0x10040040UL, + 0x10000040UL, 0x10001000UL, 0x00041040UL, 0x00040000UL, + 0x00041040UL, 0x00040000UL, 0x10041000UL, 0x00001000UL, + 0x00000040UL, 0x10040040UL, 0x00001000UL, 0x00041040UL, + 0x10001000UL, 0x00000040UL, 0x10000040UL, 0x10040000UL, + 0x10040040UL, 0x10000000UL, 0x00040000UL, 0x10001040UL, + 0x00000000UL, 0x10041040UL, 0x00040040UL, 0x10000040UL, + 0x10040000UL, 0x10001000UL, 0x10001040UL, 0x00000000UL, + 0x10041040UL, 0x00041000UL, 0x00041000UL, 0x00001040UL, + 0x00001040UL, 0x00040040UL, 0x10000000UL, 0x10041000UL +}; + + +static void cookey(const u32 *raw1, u32 *keyout) +{ + u32 *cook; + const u32 *raw0; + u32 dough[32]; + int i; + + cook = dough; + for (i = 0; i < 16; i++, raw1++) { + raw0 = raw1++; + *cook = (*raw0 & 0x00fc0000L) << 6; + *cook |= (*raw0 & 0x00000fc0L) << 10; + *cook |= (*raw1 & 0x00fc0000L) >> 10; + *cook++ |= (*raw1 & 0x00000fc0L) >> 6; + *cook = (*raw0 & 0x0003f000L) << 12; + *cook |= (*raw0 & 0x0000003fL) << 16; + *cook |= (*raw1 & 0x0003f000L) >> 4; + *cook++ |= (*raw1 & 0x0000003fL); + } + + os_memcpy(keyout, dough, sizeof(dough)); +} + + +static void deskey(const u8 *key, int decrypt, u32 *keyout) +{ + u32 i, j, l, m, n, kn[32]; + u8 pc1m[56], pcr[56]; + + for (j = 0; j < 56; j++) { + l = (u32) pc1[j]; + m = l & 7; + pc1m[j] = (u8) + ((key[l >> 3U] & bytebit[m]) == bytebit[m] ? 1 : 0); + } + + for (i = 0; i < 16; i++) { + if (decrypt) + m = (15 - i) << 1; + else + m = i << 1; + n = m + 1; + kn[m] = kn[n] = 0L; + for (j = 0; j < 28; j++) { + l = j + (u32) totrot[i]; + if (l < 28) + pcr[j] = pc1m[l]; + else + pcr[j] = pc1m[l - 28]; + } + for (/* j = 28 */; j < 56; j++) { + l = j + (u32) totrot[i]; + if (l < 56) + pcr[j] = pc1m[l]; + else + pcr[j] = pc1m[l - 28]; + } + for (j = 0; j < 24; j++) { + if ((int) pcr[(int) pc2[j]] != 0) + kn[m] |= bigbyte[j]; + if ((int) pcr[(int) pc2[j + 24]] != 0) + kn[n] |= bigbyte[j]; + } + } + + cookey(kn, keyout); +} + + +static void desfunc(u32 *block, const u32 *keys) +{ + u32 work, right, leftt; + int cur_round; + + leftt = block[0]; + right = block[1]; + + work = ((leftt >> 4) ^ right) & 0x0f0f0f0fL; + right ^= work; + leftt ^= (work << 4); + + work = ((leftt >> 16) ^ right) & 0x0000ffffL; + right ^= work; + leftt ^= (work << 16); + + work = ((right >> 2) ^ leftt) & 0x33333333L; + leftt ^= work; + right ^= (work << 2); + + work = ((right >> 8) ^ leftt) & 0x00ff00ffL; + leftt ^= work; + right ^= (work << 8); + + right = ROLc(right, 1); + work = (leftt ^ right) & 0xaaaaaaaaL; + + leftt ^= work; + right ^= work; + leftt = ROLc(leftt, 1); + + for (cur_round = 0; cur_round < 8; cur_round++) { + work = RORc(right, 4) ^ *keys++; + leftt ^= SP7[work & 0x3fL] + ^ SP5[(work >> 8) & 0x3fL] + ^ SP3[(work >> 16) & 0x3fL] + ^ SP1[(work >> 24) & 0x3fL]; + work = right ^ *keys++; + leftt ^= SP8[ work & 0x3fL] + ^ SP6[(work >> 8) & 0x3fL] + ^ SP4[(work >> 16) & 0x3fL] + ^ SP2[(work >> 24) & 0x3fL]; + + work = RORc(leftt, 4) ^ *keys++; + right ^= SP7[ work & 0x3fL] + ^ SP5[(work >> 8) & 0x3fL] + ^ SP3[(work >> 16) & 0x3fL] + ^ SP1[(work >> 24) & 0x3fL]; + work = leftt ^ *keys++; + right ^= SP8[ work & 0x3fL] + ^ SP6[(work >> 8) & 0x3fL] + ^ SP4[(work >> 16) & 0x3fL] + ^ SP2[(work >> 24) & 0x3fL]; + } + + right = RORc(right, 1); + work = (leftt ^ right) & 0xaaaaaaaaL; + leftt ^= work; + right ^= work; + leftt = RORc(leftt, 1); + work = ((leftt >> 8) ^ right) & 0x00ff00ffL; + right ^= work; + leftt ^= (work << 8); + /* -- */ + work = ((leftt >> 2) ^ right) & 0x33333333L; + right ^= work; + leftt ^= (work << 2); + work = ((right >> 16) ^ leftt) & 0x0000ffffL; + leftt ^= work; + right ^= (work << 16); + work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL; + leftt ^= work; + right ^= (work << 4); + + block[0] = right; + block[1] = leftt; +} + + +/* wpa_supplicant/hostapd specific wrapper */ + +void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +{ + u8 pkey[8], next, tmp; + int i; + u32 ek[32], work[2]; + + /* Add parity bits to the key */ + next = 0; + for (i = 0; i < 7; i++) { + tmp = key[i]; + pkey[i] = (tmp >> i) | next | 1; + next = tmp << (7 - i); + } + pkey[i] = next | 1; + + deskey(pkey, 0, ek); + + work[0] = WPA_GET_BE32(clear); + work[1] = WPA_GET_BE32(clear + 4); + desfunc(work, ek); + WPA_PUT_BE32(cypher, work[0]); + WPA_PUT_BE32(cypher + 4, work[1]); +} + + +struct des3_key_s { + u32 ek[3][32]; + u32 dk[3][32]; +}; + +void des3_key_setup(const u8 *key, struct des3_key_s *dkey) +{ + deskey(key, 0, dkey->ek[0]); + deskey(key + 8, 1, dkey->ek[1]); + deskey(key + 16, 0, dkey->ek[2]); + + deskey(key, 1, dkey->dk[2]); + deskey(key + 8, 0, dkey->dk[1]); + deskey(key + 16, 1, dkey->dk[0]); +} + + +void des3_encrypt(const u8 *plain, const struct des3_key_s *key, u8 *crypt) +{ + u32 work[2]; + + work[0] = WPA_GET_BE32(plain); + work[1] = WPA_GET_BE32(plain + 4); + desfunc(work, key->ek[0]); + desfunc(work, key->ek[1]); + desfunc(work, key->ek[2]); + WPA_PUT_BE32(crypt, work[0]); + WPA_PUT_BE32(crypt + 4, work[1]); +} + + +void des3_decrypt(const u8 *crypt, const struct des3_key_s *key, u8 *plain) +{ + u32 work[2]; + + work[0] = WPA_GET_BE32(crypt); + work[1] = WPA_GET_BE32(crypt + 4); + desfunc(work, key->dk[0]); + desfunc(work, key->dk[1]); + desfunc(work, key->dk[2]); + WPA_PUT_BE32(plain, work[0]); + WPA_PUT_BE32(plain + 4, work[1]); +} + +#endif /* INTERNAL_DES */ diff --git a/contrib/hostapd/doc/code_structure.doxygen b/contrib/hostapd/doc/code_structure.doxygen new file mode 100644 index 0000000..fdcf725 --- /dev/null +++ b/contrib/hostapd/doc/code_structure.doxygen @@ -0,0 +1,5 @@ +/** +\page code_structure Structure of the source code + + +*/ diff --git a/contrib/hostapd/doc/ctrl_iface.doxygen b/contrib/hostapd/doc/ctrl_iface.doxygen new file mode 100644 index 0000000..76cfc6a --- /dev/null +++ b/contrib/hostapd/doc/ctrl_iface.doxygen @@ -0,0 +1,66 @@ +/** +\page ctrl_iface_page Control interface + +hostapd implements a control interface that can be used by +external programs to control the operations of the hostapd +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++. hostapd_cli.c +is an example program using this library. + +There are multiple mechanisms for inter-process communication. For +example, Linux version of hostapd is using UNIX domain sockets for the +control interface. 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 hostapd 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(). + +hostapd 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 +hostapd. These can be executed using wpa_ctrl_request(). +Unsolicited event messages are sent by hostapd 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 hostapd is replying +to the control interface commands. The expected reply is \c PONG if the +connection is open and hostapd is processing commands. + +*/ diff --git a/contrib/hostapd/doc/doxygen.fast b/contrib/hostapd/doc/doxygen.fast new file mode 100644 index 0000000..44760f4 --- /dev/null +++ b/contrib/hostapd/doc/doxygen.fast @@ -0,0 +1,233 @@ +# Doxyfile 1.4.4 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +PROJECT_NAME = hostapd +PROJECT_NUMBER = 0.5.x +OUTPUT_DIRECTORY = doc +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +USE_WINDOWS_ENCODING = NO +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +DETAILS_AT_TOP = NO +INHERIT_DOCS = YES +DISTRIBUTE_GROUP_DOC = NO +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 = NO +FILE_VERSION_FILTER = +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = YES +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = . \ + ../wpa_supplicant/eap_sim_common.c \ + ../wpa_supplicant/eap_sim_common.h +FILE_PATTERNS = *.c *.h *.doxygen +RECURSIVE = YES +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = * +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = doc +INPUT_FILTER = doc/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 = RADIUS_SERVER EAP_SERVER EAP_SIM +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = NO +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = NO +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = YES +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = NO +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +MAX_DOT_GRAPH_WIDTH = 1024 +MAX_DOT_GRAPH_HEIGHT = 1024 +MAX_DOT_GRAPH_DEPTH = 1000 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = NO diff --git a/contrib/hostapd/doc/doxygen.full b/contrib/hostapd/doc/doxygen.full new file mode 100644 index 0000000..619f977 --- /dev/null +++ b/contrib/hostapd/doc/doxygen.full @@ -0,0 +1,230 @@ +# Doxyfile 1.4.1 + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- +PROJECT_NAME = hostapd +PROJECT_NUMBER = 0.5.x +OUTPUT_DIRECTORY = doc +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +USE_WINDOWS_ENCODING = NO +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +DETAILS_AT_TOP = NO +INHERIT_DOCS = YES +DISTRIBUTE_GROUP_DOC = NO +TAB_SIZE = 8 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = YES +OPTIMIZE_OUTPUT_JAVA = NO +SUBGROUPING = YES +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = YES +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_BY_SCOPE_NAME = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_DIRECTORIES = NO +FILE_VERSION_FILTER = +#--------------------------------------------------------------------------- +# configuration options related to warning and progress messages +#--------------------------------------------------------------------------- +QUIET = NO +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = YES +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +#--------------------------------------------------------------------------- +# configuration options related to the input files +#--------------------------------------------------------------------------- +INPUT = . +FILE_PATTERNS = *.c *.h *.doxygen +RECURSIVE = YES +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = * +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = doc +INPUT_FILTER = kerneldoc2doxygen.pl +FILTER_PATTERNS = +FILTER_SOURCE_FILES = YES +#--------------------------------------------------------------------------- +# configuration options related to source browsing +#--------------------------------------------------------------------------- +SOURCE_BROWSER = YES +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = NO +REFERENCES_RELATION = NO +VERBATIM_HEADERS = NO +#--------------------------------------------------------------------------- +# configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- +ALPHABETICAL_INDEX = YES +COLS_IN_ALPHA_INDEX = 3 +IGNORE_PREFIX = +#--------------------------------------------------------------------------- +# configuration options related to the HTML output +#--------------------------------------------------------------------------- +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +GENERATE_HTMLHELP = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +BINARY_TOC = NO +TOC_EXPAND = NO +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = NO +TREEVIEW_WIDTH = 250 +#--------------------------------------------------------------------------- +# configuration options related to the LaTeX output +#--------------------------------------------------------------------------- +GENERATE_LATEX = YES +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = YES +USE_PDFLATEX = YES +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +#--------------------------------------------------------------------------- +# configuration options related to the RTF output +#--------------------------------------------------------------------------- +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +#--------------------------------------------------------------------------- +# configuration options related to the man page output +#--------------------------------------------------------------------------- +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +#--------------------------------------------------------------------------- +# configuration options related to the XML output +#--------------------------------------------------------------------------- +GENERATE_XML = NO +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES +#--------------------------------------------------------------------------- +# configuration options for the AutoGen Definitions output +#--------------------------------------------------------------------------- +GENERATE_AUTOGEN_DEF = NO +#--------------------------------------------------------------------------- +# configuration options related to the Perl module output +#--------------------------------------------------------------------------- +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +#--------------------------------------------------------------------------- +# Configuration options related to the preprocessor +#--------------------------------------------------------------------------- +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = RADIUS_SERVER EAP_SERVER EAP_SIM +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to external references +#--------------------------------------------------------------------------- +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +#--------------------------------------------------------------------------- +# Configuration options related to the dot tool +#--------------------------------------------------------------------------- +CLASS_DIAGRAMS = NO +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = YES +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = YES +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = NO +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +MAX_DOT_GRAPH_WIDTH = 1024 +MAX_DOT_GRAPH_HEIGHT = 1024 +MAX_DOT_GRAPH_DEPTH = 1000 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +#--------------------------------------------------------------------------- +# Configuration::additions related to the search engine +#--------------------------------------------------------------------------- +SEARCHENGINE = YES diff --git a/contrib/hostapd/doc/driver_wrapper.doxygen b/contrib/hostapd/doc/driver_wrapper.doxygen new file mode 100644 index 0000000..0ad196f --- /dev/null +++ b/contrib/hostapd/doc/driver_wrapper.doxygen @@ -0,0 +1,20 @@ +/** +\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 hostapd 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. + +*/ diff --git a/contrib/hostapd/doc/eap.doxygen b/contrib/hostapd/doc/eap.doxygen new file mode 100644 index 0000000..f0f135a --- /dev/null +++ b/contrib/hostapd/doc/eap.doxygen @@ -0,0 +1,56 @@ +/** +\page eap_module EAP server implementation + +Extensible Authentication Protocol (EAP) is an authentication framework +defined in RFC 3748. hostapd uses a separate code module for EAP server +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 server state +machine and methods. As such, this RFC provides useful information for +understanding the EAP server implementation in hostapd. + +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 server 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_server_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_server_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. + +*/ diff --git a/contrib/hostapd/doc/hostapd.fig b/contrib/hostapd/doc/hostapd.fig new file mode 100644 index 0000000..af3f0be --- /dev/null +++ b/contrib/hostapd/doc/hostapd.fig @@ -0,0 +1,264 @@ +#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 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 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 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 +6 3450 1200 4575 1500 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 3450 1200 4575 1200 4575 1500 3450 1500 3450 1200 +4 0 0 50 -1 0 12 0.0000 4 180 870 3600 1425 hostapd_cli\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 +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 +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 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 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 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 795 9375 5700 EAP-PAX\001 +-6 +6 8175 6600 9675 6900 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 8175 6600 9675 6600 9675 6900 8175 6900 8175 6600 +4 0 0 50 -1 0 12 0.0000 4 135 1365 8250 6825 EAP-MSCHAPv2\001 +-6 +6 8700 3450 9375 3750 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 8700 3450 9375 3450 9375 3750 8700 3750 8700 3450 +4 0 0 50 -1 0 12 0.0000 4 150 480 8775 3675 crypto\001 +-6 +6 9600 3450 10275 3750 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 9600 3450 10275 3450 10275 3750 9600 3750 9600 3450 +4 0 0 50 -1 0 12 0.0000 4 135 315 9750 3675 TLS\001 +-6 +6 6000 5775 7200 6300 +6 6000 5775 7200 6300 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 6000 5775 7200 5775 7200 6300 6000 6300 6000 5775 +4 0 0 50 -1 0 12 0.0000 4 135 690 6075 6000 RADIUS\001 +-6 +4 0 0 50 -1 0 12 0.0000 4 90 480 6075 6225 server\001 +-6 +6 8100 2250 8925 2775 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 8100 2250 8925 2250 8925 2775 8100 2775 8100 2250 +4 0 0 50 -1 0 12 0.0000 4 135 690 8175 2475 RADIUS\001 +4 0 0 50 -1 0 12 0.0000 4 135 420 8175 2700 client\001 +-6 +6 3150 5475 4425 5775 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 3150 5475 4425 5475 4425 5775 3150 5775 3150 5475 +4 0 0 50 -1 0 12 0.0000 4 135 990 3300 5700 driver events\001 +-6 +6 1950 5550 2625 6075 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 1950 5550 2625 5550 2625 6075 1950 6075 1950 5550 +4 0 0 50 -1 0 12 0.0000 4 135 540 2025 5775 Station\001 +4 0 0 50 -1 0 12 0.0000 4 135 375 2025 6000 table\001 +-6 +6 1875 4725 2925 5250 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 1875 4725 2925 4725 2925 5250 1875 5250 1875 4725 +4 0 0 50 -1 0 12 0.0000 4 135 960 1950 4950 IEEE 802.11\001 +4 0 0 50 -1 0 12 0.0000 4 135 555 1950 5175 MLME\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 3750 +2 1 0 1 2 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 7200 3000 8700 3525 +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 3675 +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 3750 +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 5475 +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 +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 +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 +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 +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 +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 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 8100 6975 10425 6975 10425 4425 8100 4425 8100 6975 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 6600 5475 6600 5775 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 5025 4425 6000 5775 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 3 + 4800 3900 5925 2550 8100 2550 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 7200 3900 8475 2775 +2 2 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 5 + 9450 2250 10425 2250 10425 2775 9450 2775 9450 2250 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 8925 2475 9450 2475 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 2325 5550 2325 5250 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 2 + 2925 4950 4350 4275 +2 1 0 1 0 7 50 -1 -1 0.000 0 0 -1 0 0 3 + 2850 4725 5775 2400 8100 2400 +4 0 0 50 -1 0 12 0.0000 4 135 915 375 3975 EAPOL and\001 +4 0 0 50 -1 0 12 0.0000 4 180 630 375 4200 pre-auth\001 +4 0 0 50 -1 0 12 0.0000 4 180 810 375 4425 ethertypes\001 +4 0 0 50 -1 0 12 0.0000 4 135 1050 375 4650 from/to kernel\001 +4 0 0 50 -1 0 12 0.0000 4 135 1920 3675 1875 frontend control interface\001 +4 0 0 50 -1 2 14 0.0000 4 195 720 1637 2371 hostapd\001 +4 0 0 50 -1 0 12 0.0000 4 180 600 3825 7125 prism54\001 +4 0 0 50 -1 0 12 0.0000 4 180 510 1875 7125 hostap\001 +4 0 0 50 -1 0 12 0.0000 4 135 600 2850 7125 madwifi\001 +4 0 0 50 -1 0 12 0.0000 4 135 270 4800 7125 bsd\001 +4 0 0 50 -1 0 12 0.0000 4 105 300 6750 7125 test\001 +4 0 0 50 -1 0 12 0.0000 4 135 420 5775 7125 wired\001 +4 0 0 50 -1 0 12 0.0000 4 135 1050 8700 4650 EAP methods\001 +4 0 0 50 -1 0 12 0.0000 4 135 690 9525 2475 RADIUS\001 +4 0 0 50 -1 0 12 0.0000 4 180 825 9525 2700 accounting\001 diff --git a/contrib/hostapd/doc/kerneldoc2doxygen.pl b/contrib/hostapd/doc/kerneldoc2doxygen.pl new file mode 100755 index 0000000..68835a1 --- /dev/null +++ b/contrib/hostapd/doc/kerneldoc2doxygen.pl @@ -0,0 +1,129 @@ +#!/usr/bin/perl -w +# +########################################################################## +# Convert kernel-doc style comments to Doxygen comments. +########################################################################## +# +# This script reads a C source file from stdin, and writes +# to stdout. Normal usage: +# +# $ mv file.c file.c.gtkdoc +# $ kerneldoc2doxygen.pl <file.c.gtkdoc >file.c +# +# Or to do the same thing with multiple files: +# $ perl -i.gtkdoc kerneldoc2doxygen.pl *.c *.h +# +# This script may also be suitable for use as a Doxygen input filter, +# but that has not been tested. +# +# Back up your source files before using this script!! +# +########################################################################## +# Copyright (C) 2003 Jonathan Foster <jon@jon-foster.co.uk> +# Copyright (C) 2005 Jouni Malinen <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]; + + # " * func: foo" --> "\brief foo\n" + # " * struct bar: foo" --> "\brief foo\n" + # If this fails, not a kernel-doc comment ==> return unmodified. + ($t =~ s/^[\t ]*\*[\t ]*(struct )?([^ \t\n]*) - ([^\n]*)/\\brief $3\n/s) + or return $t; + + # " * Returns: foo" --> "\return foo" + $t =~ s/\n[\t ]*\*[\t ]*Returns:/\n\\return/sig; + + # " * @foo: bar" --> "\param foo bar" + # Handle two common typos: No ":", or "," instead of ":". + $t =~ s/\n[\t ]*\*[\t ]*\@([^ :,]*)[:,]?[\t ]*/\n\\param $1 /sg; + + return $t; +} + +########################################################################## +# Start of main code + +# Read entire stdin into memory - one multi-line string. +$_ = do { local $/; <> }; + +s{^/\*\n \*}{/\*\* \\file\n\\brief}; +s{ \* Copyright}{\\par Copyright\nCopyright}; + +# Fix any comments like "/*************" so they don't match. +# "/***" ===> "/* *" +s{/\*\*\*}{/\* \*}gs; + +# The main comment-detection code. +s{ + ( # $1 = Open comment + /\*\* # Open comment + (?!\*) # Do not match /*** (redundant due to fixup above). + [\t ]*\n? # If 1st line is whitespace, match the lot (including the newline). + ) + (.*?) # $2 = Body of comment (multi-line) + ( # $3 = Close comment + ( # If possible, match the whitespace before the close-comment + (?<=\n) # This part only matches after a newline + [\t ]* # Eat whitespace + )? + \*/ # Close comment + ) + } + { + $1 . fixcomment($2) . $3 + }gesx; +# ^^^^ Modes: g - Global, match all occurances. +# e - Evaluate the replacement as an expression. +# s - Single-line - allows the pattern to match across newlines. +# x - eXtended pattern, ignore embedded whitespace +# and allow comments. + +# Write results to stdout +print $_; + diff --git a/contrib/hostapd/doc/mainpage.doxygen b/contrib/hostapd/doc/mainpage.doxygen new file mode 100644 index 0000000..7cf95de --- /dev/null +++ b/contrib/hostapd/doc/mainpage.doxygen @@ -0,0 +1,52 @@ +/** +\mainpage Developers' documentation for hostapd + +hostapd includes IEEE 802.11 access point management (authentication / +association), IEEE 802.1X/WPA/WPA2 Authenticator, EAP server, and +RADIUS authentication server functionality. It can be build with +various configuration option, e.g., a standalone AP management +solution or a RADIUS authentication server with support for number of +EAP methods. + +The goal of this documentation and comments in the source code is to +give enough information for other developers to understand how hostapd +has been implemented, how it can be modified, how new drivers can be +supported, and how hostapd 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 hostapd is licensed under dual license, GPLv2 or BSD at user's +choice. All contributions to hostapd are expected to use compatible +licensing terms. + +The source code and read-only access to hostapd CVS repository +is available from the project home page at +http://hostap.epitest.fi/hostapd/. This developers' documentation +is also available as a PDF file from +http://hostap.epitest.fi/hostapd/hostapd-devel.pdf . + +The design goal for hostapd 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 server implementation". +Similarly, RADIUS authentication server is in its own separate module. +Both IEEE 802.1X and RADIUS authentication server can use EAP server +functionality. + +hostapd implements a \ref ctrl_iface_page "control interface" that can +be used by external programs to control the operations of the hostapdt +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 hostapd.png "hostapd modules" +\image latex hostapd.eps "hostapd modules" width=15cm + +*/ diff --git a/contrib/hostapd/doc/porting.doxygen b/contrib/hostapd/doc/porting.doxygen new file mode 100644 index 0000000..0621791 --- /dev/null +++ b/contrib/hostapd/doc/porting.doxygen @@ -0,0 +1,5 @@ +/** +\page porting Porting to different target boards and operating systems + + +*/ diff --git a/contrib/hostapd/driver.h b/contrib/hostapd/driver.h index ed9ecbf..4fd262c 100644 --- a/contrib/hostapd/driver.h +++ b/contrib/hostapd/driver.h @@ -1,6 +1,10 @@ #ifndef DRIVER_H #define DRIVER_H +enum hostapd_driver_if_type { + HOSTAPD_IF_VLAN, HOSTAPD_IF_WDS +}; + struct driver_ops { const char *name; /* as appears in the config file */ @@ -12,6 +16,7 @@ struct driver_ops { /** * set_8021x - enable/disable IEEE 802.1X support + * @ifname: Interface name (for multi-SSID/VLAN support) * @priv: driver private data * @enabled: 1 = enable, 0 = disable * @@ -20,7 +25,7 @@ struct driver_ops { * Configure the kernel driver to enable/disable 802.1X support. * This may be an empty function if 802.1X support is always enabled. */ - int (*set_ieee8021x)(void *priv, int enabled); + int (*set_ieee8021x)(const char *ifname, void *priv, int enabled); /** * set_privacy - enable/disable privacy @@ -31,32 +36,111 @@ struct driver_ops { * * Configure privacy. */ - int (*set_privacy)(void *priv, int enabled); - - int (*set_encryption)(void *priv, const char *alg, u8 *addr, - int idx, u8 *key, size_t key_len); - int (*get_seqnum)(void *priv, u8 *addr, int idx, u8 *seq); + int (*set_privacy)(const char *ifname, void *priv, int enabled); + + int (*set_encryption)(const char *ifname, void *priv, const char *alg, + const u8 *addr, int idx, + const u8 *key, size_t key_len, int txkey); + int (*get_seqnum)(const char *ifname, void *priv, const u8 *addr, + int idx, u8 *seq); + int (*get_seqnum_igtk)(const char *ifname, void *priv, const u8 *addr, + int idx, u8 *seq); int (*flush)(void *priv); - int (*set_generic_elem)(void *priv, const u8 *elem, size_t elem_len); + int (*set_generic_elem)(const char *ifname, void *priv, const u8 *elem, + size_t elem_len); int (*read_sta_data)(void *priv, struct hostap_sta_driver_data *data, - u8 *addr); - int (*send_eapol)(void *priv, u8 *addr, u8 *data, size_t data_len, - int encrypt); - int (*set_sta_authorized)(void *driver, u8 *addr, int authorized); - int (*sta_deauth)(void *priv, u8 *addr, int reason); - int (*sta_disassoc)(void *priv, u8 *addr, int reason); - int (*sta_remove)(void *priv, u8 *addr); - int (*get_ssid)(void *priv, u8 *buf, int len); - int (*set_ssid)(void *priv, u8 *buf, int len); + const u8 *addr); + int (*send_eapol)(void *priv, const u8 *addr, const u8 *data, + size_t data_len, int encrypt, const u8 *own_addr); + int (*sta_deauth)(void *priv, const u8 *addr, int reason); + int (*sta_disassoc)(void *priv, const u8 *addr, int reason); + int (*sta_remove)(void *priv, const u8 *addr); + int (*get_ssid)(const char *ifname, void *priv, u8 *buf, int len); + int (*set_ssid)(const char *ifname, void *priv, const u8 *buf, + int len); int (*set_countermeasures)(void *priv, int enabled); int (*send_mgmt_frame)(void *priv, const void *msg, size_t len, int flags); - int (*set_assoc_ap)(void *priv, u8 *addr); - int (*sta_add)(void *priv, u8 *addr, u16 aid, u16 capability, - u8 tx_supp_rates); - int (*get_inact_sec)(void *priv, u8 *addr); - int (*sta_clear_stats)(void *priv, u8 *addr); + int (*set_assoc_ap)(void *priv, const u8 *addr); + 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 (*get_inact_sec)(void *priv, const u8 *addr); + int (*sta_clear_stats)(void *priv, const u8 *addr); + + int (*set_freq)(void *priv, int mode, int freq); + int (*set_rts)(void *priv, int rts); + int (*get_rts)(void *priv, int *rts); + int (*set_frag)(void *priv, int frag); + int (*get_frag)(void *priv, int *frag); + int (*set_retry)(void *priv, int short_retry, int long_retry); + 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 (*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); + int (*set_beacon)(const char *ifname, void *priv, + u8 *head, size_t head_len, + u8 *tail, size_t tail_len); + + /* Configure internal bridge: + * 0 = disabled, i.e., client separation is enabled (no bridging of + * packets between associated STAs + * 1 = enabled, i.e., bridge packets between associated STAs (default) + */ + int (*set_internal_bridge)(void *priv, int value); + int (*set_beacon_int)(void *priv, int value); + int (*set_dtim_period)(const char *ifname, void *priv, int value); + /* Configure broadcast SSID mode: + * 0 = include SSID in Beacon frames and reply to Probe Request frames + * that use broadcast SSID + * 1 = hide SSID from Beacon frames and ignore Probe Request frames for + * broadcast SSID + */ + int (*set_broadcast_ssid)(void *priv, int value); + int (*set_cts_protect)(void *priv, int value); + int (*set_key_tx_rx_threshold)(void *priv, int value); + int (*set_preamble)(void *priv, int value); + int (*set_short_slot_time)(void *priv, int value); + int (*set_tx_queue_params)(void *priv, int queue, int aifs, int cw_min, + int cw_max, int burst_time); + int (*bss_add)(void *priv, const char *ifname, const u8 *bssid); + int (*bss_remove)(void *priv, const char *ifname); + int (*valid_bss_mask)(void *priv, const u8 *addr, const u8 *mask); + int (*passive_scan)(void *priv, int now, int our_mode_only, + int interval, int _listen, int *channel, + int *last_rx); + struct hostapd_hw_modes * (*get_hw_feature_data)(void *priv, + u16 *num_modes, + u16 *flags); + int (*if_add)(const char *iface, void *priv, + enum hostapd_driver_if_type type, char *ifname, + const u8 *addr); + int (*if_update)(void *priv, enum hostapd_driver_if_type type, + char *ifname, const u8 *addr); + int (*if_remove)(void *priv, enum hostapd_driver_if_type type, + const char *ifname, const u8 *addr); + int (*set_sta_vlan)(void *priv, const u8 *addr, const char *ifname, + int vlan_id); + /** + * commit - Optional commit changes handler + * @priv: driver private data + * Returns: 0 on success, -1 on failure + * + * This optional handler function can be registered if the driver + * interface implementation needs to commit changes (e.g., by setting + * network interface up) at the end of initial configuration. If set, + * this handler will be called after initial setup has been completed. + */ + int (*commit)(void *priv); }; static inline int @@ -94,11 +178,12 @@ hostapd_wireless_event_deinit(struct hostapd_data *hapd) } static inline int -hostapd_set_ieee8021x(struct hostapd_data *hapd, int enabled) +hostapd_set_ieee8021x(const char *ifname, struct hostapd_data *hapd, + int enabled) { if (hapd->driver == NULL || hapd->driver->set_ieee8021x == NULL) return 0; - return hapd->driver->set_ieee8021x(hapd->driver, enabled); + return hapd->driver->set_ieee8021x(ifname, hapd->driver, enabled); } static inline int @@ -106,25 +191,38 @@ 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->driver, enabled); + return hapd->driver->set_privacy(hapd->conf->iface, hapd->driver, + enabled); } static inline int -hostapd_set_encryption(struct hostapd_data *hapd, const char *alg, u8 *addr, - int idx, u8 *key, size_t key_len) +hostapd_set_encryption(const char *ifname, struct hostapd_data *hapd, + const char *alg, const u8 *addr, int idx, + u8 *key, size_t key_len, int txkey) { if (hapd->driver == NULL || hapd->driver->set_encryption == NULL) return 0; - return hapd->driver->set_encryption(hapd->driver, alg, addr, idx, key, - key_len); + return hapd->driver->set_encryption(ifname, hapd->driver, alg, addr, + idx, key, key_len, txkey); } static inline int -hostapd_get_seqnum(struct hostapd_data *hapd, u8 *addr, int idx, u8 *seq) +hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd, + const u8 *addr, int idx, u8 *seq) { if (hapd->driver == NULL || hapd->driver->get_seqnum == NULL) return 0; - return hapd->driver->get_seqnum(hapd->driver, addr, idx, seq); + return hapd->driver->get_seqnum(ifname, hapd->driver, addr, idx, seq); +} + +static inline int +hostapd_get_seqnum_igtk(const char *ifname, struct hostapd_data *hapd, + const u8 *addr, int idx, u8 *seq) +{ + if (hapd->driver == NULL || hapd->driver->get_seqnum_igtk == NULL) + return -1; + return hapd->driver->get_seqnum_igtk(ifname, hapd->driver, addr, idx, + seq); } static inline int @@ -141,12 +239,13 @@ 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->driver, elem, elem_len); + return hapd->driver->set_generic_elem(hapd->conf->iface, hapd->driver, + elem, elem_len); } static inline int hostapd_read_sta_data(struct hostapd_data *hapd, - struct hostap_sta_driver_data *data, u8 *addr) + struct hostap_sta_driver_data *data, const u8 *addr) { if (hapd->driver == NULL || hapd->driver->read_sta_data == NULL) return -1; @@ -154,26 +253,17 @@ hostapd_read_sta_data(struct hostapd_data *hapd, } static inline int -hostapd_send_eapol(struct hostapd_data *hapd, u8 *addr, u8 *data, +hostapd_send_eapol(struct hostapd_data *hapd, const u8 *addr, const u8 *data, size_t data_len, int encrypt) { if (hapd->driver == NULL || hapd->driver->send_eapol == NULL) return 0; return hapd->driver->send_eapol(hapd->driver, addr, data, data_len, - encrypt); -} - -static inline int -hostapd_set_sta_authorized(struct hostapd_data *hapd, u8 *addr, int authorized) -{ - if (hapd->driver == NULL || hapd->driver->set_sta_authorized == NULL) - return 0; - return hapd->driver->set_sta_authorized(hapd->driver, addr, - authorized); + encrypt, hapd->own_addr); } static inline int -hostapd_sta_deauth(struct hostapd_data *hapd, u8 *addr, int reason) +hostapd_sta_deauth(struct hostapd_data *hapd, const u8 *addr, int reason) { if (hapd->driver == NULL || hapd->driver->sta_deauth == NULL) return 0; @@ -181,7 +271,7 @@ hostapd_sta_deauth(struct hostapd_data *hapd, u8 *addr, int reason) } static inline int -hostapd_sta_disassoc(struct hostapd_data *hapd, u8 *addr, int reason) +hostapd_sta_disassoc(struct hostapd_data *hapd, const u8 *addr, int reason) { if (hapd->driver == NULL || hapd->driver->sta_disassoc == NULL) return 0; @@ -189,7 +279,7 @@ hostapd_sta_disassoc(struct hostapd_data *hapd, u8 *addr, int reason) } static inline int -hostapd_sta_remove(struct hostapd_data *hapd, u8 *addr) +hostapd_sta_remove(struct hostapd_data *hapd, const u8 *addr) { if (hapd->driver == NULL || hapd->driver->sta_remove == NULL) return 0; @@ -201,15 +291,17 @@ 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->driver, buf, len); + return hapd->driver->get_ssid(hapd->conf->iface, hapd->driver, buf, + len); } static inline int -hostapd_set_ssid(struct hostapd_data *hapd, u8 *buf, size_t len) +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->driver, buf, len); + return hapd->driver->set_ssid(hapd->conf->iface, hapd->driver, buf, + len); } static inline int @@ -222,14 +314,14 @@ hostapd_send_mgmt_frame(struct hostapd_data *hapd, const void *msg, size_t len, } static inline int -hostapd_set_assoc_ap(struct hostapd_data *hapd, u8 *addr) +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); } -static inline int +static inline int hostapd_set_countermeasures(struct hostapd_data *hapd, int enabled) { if (hapd->driver == NULL || hapd->driver->set_countermeasures == NULL) @@ -238,34 +330,327 @@ hostapd_set_countermeasures(struct hostapd_data *hapd, int enabled) } static inline int -hostapd_sta_add(struct hostapd_data *hapd, u8 *addr, u16 aid, u16 capability, - u8 tx_supp_rates) +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) { if (hapd->driver == NULL || hapd->driver->sta_add == NULL) return 0; - return hapd->driver->sta_add(hapd->driver, addr, aid, capability, - tx_supp_rates); + return hapd->driver->sta_add(ifname, hapd->driver, addr, aid, + capability, supp_rates, supp_rates_len, + flags); } static inline int -hostapd_get_inact_sec(struct hostapd_data *hapd, u8 *addr) +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); } +static inline int +hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq) +{ + if (hapd->driver == NULL || hapd->driver->set_freq == NULL) + return 0; + return hapd->driver->set_freq(hapd->driver, mode, freq); +} + +static inline int +hostapd_set_rts(struct hostapd_data *hapd, int rts) +{ + if (hapd->driver == NULL || hapd->driver->set_rts == NULL) + return 0; + return hapd->driver->set_rts(hapd->driver, rts); +} + +static inline int +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); +} + +static inline int +hostapd_set_frag(struct hostapd_data *hapd, int frag) +{ + if (hapd->driver == NULL || hapd->driver->set_frag == NULL) + return 0; + return hapd->driver->set_frag(hapd->driver, frag); +} + +static inline int +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); +} + +static inline int +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); +} + +static inline int +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); +} + +static inline int +hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr, + 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); +} + +static inline int +hostapd_set_rate_sets(struct hostapd_data *hapd, int *supp_rates, + int *basic_rates, int mode) +{ + if (hapd->driver == NULL || hapd->driver->set_rate_sets == NULL) + return 0; + return hapd->driver->set_rate_sets(hapd->driver, 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); +} + +static inline int +hostapd_set_country(struct hostapd_data *hapd, const char *country) +{ + if (hapd->driver == NULL || + hapd->driver->set_country == NULL) + return 0; + return hapd->driver->set_country(hapd->driver, country); +} + +static inline int +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); +} 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, u8 *addr) +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); } +static inline int +hostapd_set_beacon(const char *ifname, struct hostapd_data *hapd, + u8 *head, size_t head_len, + u8 *tail, size_t tail_len) +{ + if (hapd->driver == NULL || hapd->driver->set_beacon == NULL) + return 0; + return hapd->driver->set_beacon(ifname, hapd->driver, head, head_len, + tail, tail_len); +} + +static inline int +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); +} + +static inline int +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); +} + +static inline int +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, + value); +} + +static inline int +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); +} + +static inline int +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); +} + +static inline int +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); +} + +static inline int +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); +} + +static inline int +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); +} + +static inline int +hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs, + int cw_min, int cw_max, int burst_time) +{ + if (hapd->driver == NULL || hapd->driver->set_tx_queue_params == NULL) + return 0; + return hapd->driver->set_tx_queue_params(hapd->driver, queue, aifs, + cw_min, cw_max, burst_time); +} + +static inline int +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); +} + +static inline int +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); +} + +static inline int +hostapd_valid_bss_mask(struct hostapd_data *hapd, const u8 *addr, + const u8 *mask) +{ + if (hapd->driver == NULL || hapd->driver->valid_bss_mask == NULL) + return 1; + return hapd->driver->valid_bss_mask(hapd->driver, addr, mask); +} + +static inline int +hostapd_if_add(struct hostapd_data *hapd, enum hostapd_driver_if_type type, + char *ifname, const u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->if_add == NULL) + return -1; + return hapd->driver->if_add(hapd->conf->iface, hapd->driver, type, + ifname, addr); +} + +static inline int +hostapd_if_update(struct hostapd_data *hapd, enum hostapd_driver_if_type type, + char *ifname, const u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->if_update == NULL) + return -1; + return hapd->driver->if_update(hapd->driver, type, ifname, addr); +} + +static inline int +hostapd_if_remove(struct hostapd_data *hapd, enum hostapd_driver_if_type type, + char *ifname, const u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->if_remove == NULL) + return -1; + return hapd->driver->if_remove(hapd->driver, type, ifname, addr); +} + +static inline int +hostapd_passive_scan(struct hostapd_data *hapd, int now, int our_mode_only, + int interval, int _listen, int *channel, + int *last_rx) +{ + if (hapd->driver == NULL || hapd->driver->passive_scan == NULL) + return -1; + return hapd->driver->passive_scan(hapd->driver, now, our_mode_only, + interval, _listen, channel, last_rx); +} + +static inline struct hostapd_hw_modes * +hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes, + u16 *flags) +{ + if (hapd->driver == NULL || hapd->driver->get_hw_feature_data == NULL) + return NULL; + return hapd->driver->get_hw_feature_data(hapd->driver, num_modes, + flags); +} + +static inline int +hostapd_set_sta_vlan(const char *ifname, struct hostapd_data *hapd, + const u8 *addr, int vlan_id) +{ + if (hapd->driver == NULL || hapd->driver->set_sta_vlan == NULL) + return 0; + return hapd->driver->set_sta_vlan(hapd->driver, addr, ifname, vlan_id); +} + +static inline int +hostapd_driver_commit(struct hostapd_data *hapd) +{ + if (hapd->driver == NULL || hapd->driver->commit == NULL) + return 0; + return hapd->driver->commit(hapd->driver); +} + #endif /* DRIVER_H */ diff --git a/contrib/hostapd/driver_test.c b/contrib/hostapd/driver_test.c index 6af9af2..d0b90d1 100644 --- a/contrib/hostapd/driver_test.c +++ b/contrib/hostapd/driver_test.c @@ -1,7 +1,6 @@ /* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / Driver interface for development testing - * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * hostapd / Driver interface for development testing + * 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 @@ -13,18 +12,10 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> -#include <errno.h> +#include "includes.h" #include <sys/ioctl.h> -#include <netinet/in.h> -#include <signal.h> -#include <sys/types.h> -#include <sys/socket.h> #include <sys/un.h> -#include <sys/uio.h> +#include <dirent.h> #include "hostapd.h" #include "driver.h" @@ -37,7 +28,8 @@ #include "accounting.h" #include "radius.h" #include "l2_packet.h" -#include "hostap_common.h" +#include "ieee802_11.h" +#include "hw_features.h" struct test_client_socket { @@ -45,6 +37,18 @@ struct test_client_socket { u8 addr[ETH_ALEN]; struct sockaddr_un un; socklen_t unlen; + struct test_driver_bss *bss; +}; + +struct test_driver_bss { + struct test_driver_bss *next; + char ifname[IFNAMSIZ + 1]; + u8 bssid[ETH_ALEN]; + u8 *ie; + size_t ielen; + u8 ssid[32]; + size_t ssid_len; + int privacy; }; struct test_driver_data { @@ -52,13 +56,40 @@ struct test_driver_data { struct hostapd_data *hapd; struct test_client_socket *cli; int test_socket; - u8 *ie; - size_t ielen; + struct test_driver_bss *bss; + char *socket_dir; + char *own_socket_path; }; static const struct driver_ops test_driver_ops; +static void test_driver_free_bss(struct test_driver_bss *bss) +{ + free(bss->ie); + free(bss); +} + + +static void test_driver_free_priv(struct test_driver_data *drv) +{ + struct test_driver_bss *bss, *prev; + + if (drv == NULL) + return; + + bss = drv->bss; + while (bss) { + prev = bss; + bss = bss->next; + test_driver_free_bss(prev); + } + free(drv->own_socket_path); + free(drv->socket_dir); + free(drv); +} + + static struct test_client_socket * test_driver_get_cli(struct test_driver_data *drv, struct sockaddr_un *from, socklen_t fromlen) @@ -67,7 +98,8 @@ test_driver_get_cli(struct test_driver_data *drv, struct sockaddr_un *from, while (cli) { if (cli->unlen == fromlen && - strncmp(cli->un.sun_path, from->sun_path, fromlen) == 0) + strncmp(cli->un.sun_path, from->sun_path, + fromlen - sizeof(cli->un.sun_family)) == 0) return cli; cli = cli->next; } @@ -76,8 +108,9 @@ test_driver_get_cli(struct test_driver_data *drv, struct sockaddr_un *from, } -static int test_driver_send_eapol(void *priv, u8 *addr, u8 *data, - size_t data_len, int encrypt) +static int test_driver_send_eapol(void *priv, const u8 *addr, const u8 *data, + size_t data_len, int encrypt, + const u8 *own_addr) { struct test_driver_data *drv = priv; struct test_client_socket *cli; @@ -95,18 +128,21 @@ static int test_driver_send_eapol(void *priv, u8 *addr, u8 *data, cli = cli->next; } - if (!cli) + if (!cli) { + wpa_printf(MSG_DEBUG, "%s: no destination client entry", + __func__); return -1; + } memcpy(eth.h_dest, addr, ETH_ALEN); - memcpy(eth.h_source, drv->hapd->own_addr, ETH_ALEN); + memcpy(eth.h_source, own_addr, ETH_ALEN); eth.h_proto = htons(ETH_P_EAPOL); io[0].iov_base = "EAPOL "; io[0].iov_len = 6; io[1].iov_base = ð io[1].iov_len = sizeof(eth); - io[2].iov_base = data; + io[2].iov_base = (u8 *) data; io[2].iov_len = data_len; memset(&msg, 0, sizeof(msg)); @@ -118,42 +154,168 @@ static int test_driver_send_eapol(void *priv, u8 *addr, u8 *data, } +static int test_driver_send_mgmt_frame(void *priv, const void *buf, + size_t len, int flags) +{ + struct test_driver_data *drv = priv; + struct msghdr msg; + struct iovec io[2]; + const u8 *dest; + int ret = 0, broadcast = 0; + char desttxt[30]; + struct sockaddr_un addr; + struct dirent *dent; + DIR *dir; + struct ieee80211_hdr *hdr; + u16 fc; + + if (drv->test_socket < 0 || len < 10 || drv->socket_dir == NULL) { + wpa_printf(MSG_DEBUG, "%s: invalid parameters (sock=%d len=%d " + "socket_dir=%p)", + __func__, drv->test_socket, len, drv->socket_dir); + return -1; + } + + dest = buf; + dest += 4; + broadcast = memcmp(dest, "\xff\xff\xff\xff\xff\xff", ETH_ALEN) == 0; + snprintf(desttxt, sizeof(desttxt), MACSTR, MAC2STR(dest)); + + io[0].iov_base = "MLME "; + io[0].iov_len = 5; + io[1].iov_base = (void *) buf; + io[1].iov_len = len; + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = io; + msg.msg_iovlen = 2; + + 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 management frame to %s", + __func__, dent->d_name); + + msg.msg_name = &addr; + msg.msg_namelen = sizeof(addr); + ret = sendmsg(drv->test_socket, &msg, 0); + if (ret < 0) + perror("driver_test: sendmsg"); + } + closedir(dir); + + hdr = (struct ieee80211_hdr *) buf; + fc = le_to_host16(hdr->frame_control); + ieee802_11_mgmt_cb(drv->hapd, (u8 *) buf, len, WLAN_FC_GET_STYPE(fc), + ret >= 0); + + return ret; +} + + static void test_driver_scan(struct test_driver_data *drv, struct sockaddr_un *from, socklen_t fromlen) { char buf[512], *pos, *end; - int i; - - pos = buf; - end = buf + sizeof(buf); + int ret; + struct test_driver_bss *bss; wpa_printf(MSG_DEBUG, "test_driver: SCAN"); - /* reply: SCANRESP BSSID SSID IEs */ - pos += snprintf(pos, end - pos, "SCANRESP " MACSTR " ", - MAC2STR(drv->hapd->own_addr)); - for (i = 0; i < drv->hapd->conf->ssid_len; i++) { - pos += snprintf(pos, end - pos, "%02x", - drv->hapd->conf->ssid[i]); + for (bss = drv->bss; bss; bss = bss->next) { + pos = buf; + end = buf + sizeof(buf); + + /* reply: SCANRESP BSSID SSID IEs */ + ret = snprintf(pos, end - pos, "SCANRESP " MACSTR " ", + MAC2STR(bss->bssid)); + if (ret < 0 || ret >= end - pos) + return; + pos += ret; + pos += wpa_snprintf_hex(pos, end - pos, + bss->ssid, bss->ssid_len); + ret = snprintf(pos, end - pos, " "); + if (ret < 0 || ret >= end - pos) + return; + pos += ret; + pos += wpa_snprintf_hex(pos, end - pos, bss->ie, bss->ielen); + + if (bss->privacy) { + ret = snprintf(pos, end - pos, " PRIVACY"); + if (ret < 0 || ret >= end - pos) + return; + pos += ret; + } + + sendto(drv->test_socket, buf, pos - buf, 0, + (struct sockaddr *) from, fromlen); } - pos += snprintf(pos, end - pos, " "); - for (i = 0; i < drv->ielen; i++) { - pos += snprintf(pos, end - pos, "%02x", - drv->ie[i]); +} + + +static struct hostapd_data * test_driver_get_hapd(struct test_driver_data *drv, + struct test_driver_bss *bss) +{ + struct hostapd_iface *iface = drv->hapd->iface; + struct hostapd_data *hapd = NULL; + size_t i; + + if (bss == NULL) { + wpa_printf(MSG_DEBUG, "%s: bss == NULL", __func__); + return NULL; } - sendto(drv->test_socket, buf, pos - buf, 0, - (struct sockaddr *) from, fromlen); + for (i = 0; i < iface->num_bss; i++) { + hapd = iface->bss[i]; + if (memcmp(hapd->own_addr, bss->bssid, ETH_ALEN) == 0) + break; + } + if (i == iface->num_bss) { + wpa_printf(MSG_DEBUG, "%s: no matching interface entry found " + "for BSSID " MACSTR, __func__, MAC2STR(bss->bssid)); + return NULL; + } + + return hapd; } -static int test_driver_new_sta(struct test_driver_data *drv, const u8 *addr, +static int test_driver_new_sta(struct test_driver_data *drv, + struct test_driver_bss *bss, const u8 *addr, const u8 *ie, size_t ielen) { - struct hostapd_data *hapd = drv->hapd; + struct hostapd_data *hapd; struct sta_info *sta; int new_assoc, res; + hapd = test_driver_get_hapd(drv, bss); + if (hapd == NULL) + return -1; + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "associated"); @@ -172,30 +334,26 @@ static int test_driver_new_sta(struct test_driver_data *drv, const u8 *addr, printf("test_driver: no IE from STA\n"); return -1; } - res = wpa_validate_wpa_ie(hapd, sta, ie, ielen, - ie[0] == WLAN_EID_RSN ? - HOSTAPD_WPA_VERSION_WPA2 : - HOSTAPD_WPA_VERSION_WPA); + if (sta->wpa_sm == NULL) + sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, + sta->addr); + if (sta->wpa_sm == NULL) { + printf("test_driver: Failed to initialize WPA state " + "machine\n"); + return -1; + } + res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, + ie, ielen); if (res != WPA_IE_OK) { printf("WPA/RSN information element rejected? " "(res %u)\n", res); return -1; } - free(sta->wpa_ie); - sta->wpa_ie = malloc(ielen); - if (sta->wpa_ie == NULL) - return -1; - memcpy(sta->wpa_ie, ie, ielen); - sta->wpa_ie_len = ielen; - } else { - free(sta->wpa_ie); - sta->wpa_ie = NULL; - sta->wpa_ie_len = 0; } new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0; - sta->flags |= WLAN_STA_ASSOC; - wpa_sm_event(hapd, sta, WPA_ASSOC); + 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); @@ -210,17 +368,17 @@ static void test_driver_assoc(struct test_driver_data *drv, char *data) { struct test_client_socket *cli; - u8 ie[256]; - size_t ielen; + u8 ie[256], ssid[32]; + size_t ielen, ssid_len = 0; char *pos, *pos2, cmd[50]; + struct test_driver_bss *bss; /* data: STA-addr SSID(hex) IEs(hex) */ - cli = malloc(sizeof(*cli)); + cli = wpa_zalloc(sizeof(*cli)); if (cli == NULL) return; - memset(cli, 0, sizeof(*cli)); if (hwaddr_aton(data, cli->addr)) { printf("test_socket: Invalid MAC address '%s' in ASSOC\n", data); @@ -233,7 +391,14 @@ static void test_driver_assoc(struct test_driver_data *drv, pos2 = strchr(pos, ' '); ielen = 0; if (pos2) { - /* TODO: verify SSID */ + ssid_len = (pos2 - pos) / 2; + if (hexstr2bin(pos, ssid, ssid_len) < 0) { + wpa_printf(MSG_DEBUG, "%s: Invalid SSID", __func__); + free(cli); + return; + } + wpa_hexdump_ascii(MSG_DEBUG, "test_driver_assoc: SSID", + ssid, ssid_len); pos = pos2 + 1; ielen = strlen(pos) / 2; @@ -243,19 +408,33 @@ static void test_driver_assoc(struct test_driver_data *drv, ielen = 0; } + for (bss = drv->bss; bss; bss = bss->next) { + if (bss->ssid_len == ssid_len && + memcmp(bss->ssid, ssid, ssid_len) == 0) + break; + } + if (bss == NULL) { + wpa_printf(MSG_DEBUG, "%s: No matching SSID found from " + "configured BSSes", __func__); + free(cli); + return; + } + + cli->bss = bss; memcpy(&cli->un, from, sizeof(cli->un)); cli->unlen = fromlen; cli->next = drv->cli; drv->cli = cli; wpa_hexdump_ascii(MSG_DEBUG, "test_socket: ASSOC sun_path", - cli->un.sun_path, cli->unlen); + (const u8 *) cli->un.sun_path, + cli->unlen - sizeof(cli->un.sun_family)); snprintf(cmd, sizeof(cmd), "ASSOCRESP " MACSTR " 0", - MAC2STR(drv->hapd->own_addr)); + MAC2STR(bss->bssid)); sendto(drv->test_socket, cmd, strlen(cmd), 0, (struct sockaddr *) from, fromlen); - if (test_driver_new_sta(drv, cli->addr, ie, ielen) < 0) { + if (test_driver_new_sta(drv, bss, cli->addr, ie, ielen) < 0) { wpa_printf(MSG_DEBUG, "test_driver: failed to add new STA"); } } @@ -277,10 +456,10 @@ static void test_driver_disassoc(struct test_driver_data *drv, sta = ap_get_sta(drv->hapd, cli->addr); if (sta != NULL) { sta->flags &= ~WLAN_STA_ASSOC; - wpa_sm_event(drv->hapd, sta, WPA_DISASSOC); + wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC); sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; - ieee802_1x_set_port_enabled(drv->hapd, sta, 0); + ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); ap_free_sta(drv->hapd, sta); } } @@ -292,20 +471,64 @@ 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]); data += 14; datalen -= 14; } cli = test_driver_get_cli(drv, from, fromlen); - if (cli) - ieee802_1x_receive(drv->hapd, cli->addr, data, datalen); - else { + if (cli) { + struct hostapd_data *hapd; + hapd = test_driver_get_hapd(drv, cli->bss); + if (hapd == NULL) + return; + ieee802_1x_receive(hapd, cli->addr, data, datalen); + } else { wpa_printf(MSG_DEBUG, "test_socket: EAPOL from unknown " "client"); } } +static void test_driver_mlme(struct test_driver_data *drv, + struct sockaddr_un *from, socklen_t fromlen, + u8 *data, size_t datalen) +{ + struct ieee80211_hdr *hdr; + u16 fc; + + hdr = (struct ieee80211_hdr *) data; + + if (test_driver_get_cli(drv, from, fromlen) == NULL && datalen >= 16) { + struct test_client_socket *cli; + cli = wpa_zalloc(sizeof(*cli)); + if (cli == NULL) + return; + wpa_printf(MSG_DEBUG, "Adding client entry for " MACSTR, + MAC2STR(hdr->addr2)); + memcpy(cli->addr, hdr->addr2, ETH_ALEN); + memcpy(&cli->un, from, sizeof(cli->un)); + cli->unlen = fromlen; + cli->next = drv->cli; + drv->cli = cli; + } + + wpa_hexdump(MSG_MSGDUMP, "test_driver_mlme: received frame", + data, datalen); + fc = le_to_host16(hdr->frame_control); + if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT) { + wpa_printf(MSG_ERROR, "%s: received non-mgmt frame", + __func__); + return; + } + ieee802_11_mgmt(drv->hapd, data, datalen, WLAN_FC_GET_STYPE(fc), NULL); +} + + static void test_driver_receive_unix(int sock, void *eloop_ctx, void *sock_ctx) { struct test_driver_data *drv = eloop_ctx; @@ -331,7 +554,10 @@ static void test_driver_receive_unix(int sock, void *eloop_ctx, void *sock_ctx) } else if (strcmp(buf, "DISASSOC") == 0) { test_driver_disassoc(drv, &from, fromlen); } else if (strncmp(buf, "EAPOL ", 6) == 0) { - test_driver_eapol(drv, &from, fromlen, buf + 6, res - 6); + test_driver_eapol(drv, &from, fromlen, (u8 *) buf + 6, + res - 6); + } else if (strncmp(buf, "MLME ", 5) == 0) { + test_driver_mlme(drv, &from, fromlen, (u8 *) buf + 5, res - 5); } else { wpa_hexdump_ascii(MSG_DEBUG, "Unknown test_socket command", (u8 *) buf, res); @@ -339,25 +565,40 @@ static void test_driver_receive_unix(int sock, void *eloop_ctx, void *sock_ctx) } -static int test_driver_set_generic_elem(void *priv, +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; - free(drv->ie); - drv->ie = malloc(elem_len); - if (drv->ie) { - memcpy(drv->ie, elem, elem_len); - drv->ielen = elem_len; - return 0; - } else { - drv->ielen = 0; - return -1; + for (bss = drv->bss; bss; bss = bss->next) { + if (strcmp(bss->ifname, ifname) != 0) + continue; + + free(bss->ie); + + 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; + } } + + return -1; } -static int test_driver_sta_deauth(void *priv, u8 *addr, int reason) +static int test_driver_sta_deauth(void *priv, const u8 *addr, int reason) { struct test_driver_data *drv = priv; struct test_client_socket *cli; @@ -380,7 +621,7 @@ static int test_driver_sta_deauth(void *priv, u8 *addr, int reason) } -static int test_driver_sta_disassoc(void *priv, u8 *addr, int reason) +static int test_driver_sta_disassoc(void *priv, const u8 *addr, int reason) { struct test_driver_data *drv = priv; struct test_client_socket *cli; @@ -403,51 +644,346 @@ static int test_driver_sta_disassoc(void *priv, u8 *addr, int reason) } +static struct hostapd_hw_modes * +test_driver_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags) +{ + struct hostapd_hw_modes *modes; + + *num_modes = 3; + *flags = 0; + modes = wpa_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)); + 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].rates[0].rate = 10; + modes[0].rates[0].flags = HOSTAPD_RATE_BASIC | HOSTAPD_RATE_SUPPORTED | + HOSTAPD_RATE_CCK | HOSTAPD_RATE_MANDATORY; + + 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)); + 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].rates[0].rate = 10; + modes[1].rates[0].flags = HOSTAPD_RATE_BASIC | HOSTAPD_RATE_SUPPORTED | + HOSTAPD_RATE_CCK | HOSTAPD_RATE_MANDATORY; + + 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)); + 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].rates[0].rate = 60; + modes[2].rates[0].flags = HOSTAPD_RATE_BASIC | HOSTAPD_RATE_SUPPORTED | + HOSTAPD_RATE_MANDATORY; + + return modes; +} + + +static int test_driver_bss_add(void *priv, const char *ifname, const u8 *bssid) +{ + struct test_driver_data *drv = priv; + struct test_driver_bss *bss; + + wpa_printf(MSG_DEBUG, "%s(ifname=%s bssid=" MACSTR ")", + __func__, ifname, MAC2STR(bssid)); + + bss = wpa_zalloc(sizeof(*bss)); + if (bss == NULL) + return -1; + + strncpy(bss->ifname, ifname, IFNAMSIZ); + memcpy(bss->bssid, bssid, ETH_ALEN); + + bss->next = drv->bss; + drv->bss = bss; + + return 0; +} + + +static int test_driver_bss_remove(void *priv, const char *ifname) +{ + struct test_driver_data *drv = priv; + struct test_driver_bss *bss, *prev; + struct test_client_socket *cli, *prev_c; + + wpa_printf(MSG_DEBUG, "%s(ifname=%s)", __func__, ifname); + + for (prev = NULL, bss = drv->bss; bss; prev = bss, bss = bss->next) { + if (strcmp(bss->ifname, ifname) != 0) + continue; + + if (prev) + prev->next = bss->next; + else + drv->bss = bss->next; + + for (prev_c = NULL, cli = drv->cli; cli; + prev_c = cli, cli = cli->next) { + if (cli->bss != bss) + continue; + if (prev_c) + prev_c->next = cli->next; + else + drv->cli = cli->next; + free(cli); + break; + } + + test_driver_free_bss(bss); + return 0; + } + + return -1; +} + + +static int test_driver_if_add(const char *iface, void *priv, + enum hostapd_driver_if_type type, char *ifname, + const u8 *addr) +{ + wpa_printf(MSG_DEBUG, "%s(iface=%s type=%d ifname=%s)", + __func__, iface, type, ifname); + return 0; +} + + +static int test_driver_if_update(void *priv, enum hostapd_driver_if_type type, + char *ifname, const u8 *addr) +{ + wpa_printf(MSG_DEBUG, "%s(type=%d ifname=%s)", __func__, type, ifname); + return 0; +} + + +static int test_driver_if_remove(void *priv, enum hostapd_driver_if_type type, + const char *ifname, const u8 *addr) +{ + wpa_printf(MSG_DEBUG, "%s(type=%d ifname=%s)", __func__, type, ifname); + return 0; +} + + +static int test_driver_valid_bss_mask(void *priv, const u8 *addr, + const u8 *mask) +{ + return 0; +} + + +static int test_driver_set_ssid(const char *ifname, void *priv, const u8 *buf, + int len) +{ + struct test_driver_data *drv = priv; + struct test_driver_bss *bss; + + wpa_printf(MSG_DEBUG, "%s(ifname=%s)", __func__, ifname); + wpa_hexdump_ascii(MSG_DEBUG, "test_driver_set_ssid: SSID", buf, len); + + for (bss = drv->bss; bss; bss = bss->next) { + if (strcmp(bss->ifname, ifname) != 0) + continue; + + if (len < 0 || (size_t) len > sizeof(bss->ssid)) + return -1; + + memcpy(bss->ssid, buf, len); + bss->ssid_len = len; + + return 0; + } + + return -1; +} + + +static int test_driver_set_privacy(const char *ifname, void *priv, int enabled) +{ + struct test_driver_data *drv = priv; + struct test_driver_bss *bss; + + wpa_printf(MSG_DEBUG, "%s(ifname=%s enabled=%d)", + __func__, ifname, enabled); + + for (bss = drv->bss; bss; bss = bss->next) { + if (strcmp(bss->ifname, ifname) != 0) + continue; + + bss->privacy = enabled; + + return 0; + } + + return -1; +} + + +static int test_driver_set_encryption(const char *iface, void *priv, + const char *alg, const u8 *addr, int idx, + const u8 *key, size_t key_len, int txkey) +{ + wpa_printf(MSG_DEBUG, "%s(iface=%s alg=%s idx=%d txkey=%d)", + __func__, iface, alg, idx, txkey); + if (addr) + wpa_printf(MSG_DEBUG, " addr=" MACSTR, MAC2STR(addr)); + if (key) + wpa_hexdump_key(MSG_DEBUG, " key", key, key_len); + return 0; +} + + +static int test_driver_set_sta_vlan(void *priv, const u8 *addr, + const char *ifname, int vlan_id) +{ + wpa_printf(MSG_DEBUG, "%s(addr=" MACSTR " ifname=%s vlan_id=%d)", + __func__, MAC2STR(addr), ifname, vlan_id); + return 0; +} + + +static int test_driver_sta_add(const char *ifname, void *priv, const u8 *addr, + u16 aid, u16 capability, u8 *supp_rates, + size_t supp_rates_len, int flags) +{ + 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); + wpa_hexdump(MSG_DEBUG, "test_driver_sta_add - supp_rates", + supp_rates, supp_rates_len); + + cli = drv->cli; + while (cli) { + if (memcmp(cli->addr, addr, ETH_ALEN) == 0) + break; + cli = cli->next; + } + if (!cli) { + wpa_printf(MSG_DEBUG, "%s: no matching client entry", + __func__); + return -1; + } + + for (bss = drv->bss; bss; bss = bss->next) { + if (strcmp(ifname, bss->ifname) == 0) + break; + } + if (bss == NULL) { + wpa_printf(MSG_DEBUG, "%s: No matching interface found from " + "configured BSSes", __func__); + return -1; + } + + cli->bss = bss; + + return 0; +} + + static int test_driver_init(struct hostapd_data *hapd) { struct test_driver_data *drv; struct sockaddr_un addr; - drv = malloc(sizeof(struct test_driver_data)); + drv = wpa_zalloc(sizeof(struct test_driver_data)); if (drv == NULL) { printf("Could not allocate memory for test driver data\n"); return -1; } + drv->bss = wpa_zalloc(sizeof(*drv->bss)); + if (drv->bss == NULL) { + printf("Could not allocate memory for test driver BSS data\n"); + free(drv); + return -1; + } - memset(drv, 0, sizeof(*drv)); drv->ops = test_driver_ops; drv->hapd = hapd; /* Generate a MAC address to help testing with multiple APs */ hapd->own_addr[0] = 0x02; /* locally administered */ - sha1_prf(hapd->conf->iface, strlen(hapd->conf->iface), + sha1_prf((const u8 *) hapd->conf->iface, strlen(hapd->conf->iface), "hostapd test bssid generation", - hapd->conf->ssid, hapd->conf->ssid_len, + (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); + memcpy(drv->bss->bssid, hapd->own_addr, ETH_ALEN); + if (hapd->conf->test_socket) { if (strlen(hapd->conf->test_socket) >= sizeof(addr.sun_path)) { printf("Too long test_socket path\n"); - free(drv); + test_driver_free_priv(drv); return -1; } + if (strncmp(hapd->conf->test_socket, "DIR:", 4) == 0) { + size_t len = strlen(hapd->conf->test_socket) + 30; + drv->socket_dir = strdup(hapd->conf->test_socket + 4); + drv->own_socket_path = malloc(len); + if (drv->own_socket_path) { + snprintf(drv->own_socket_path, len, + "%s/AP-" MACSTR, + hapd->conf->test_socket + 4, + MAC2STR(hapd->own_addr)); + } + } else { + drv->own_socket_path = strdup(hapd->conf->test_socket); + } + if (drv->own_socket_path == NULL) { + test_driver_free_priv(drv); + return -1; + } + drv->test_socket = socket(PF_UNIX, SOCK_DGRAM, 0); if (drv->test_socket < 0) { perror("socket(PF_UNIX)"); - free(drv); + test_driver_free_priv(drv); return -1; } memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_UNIX; - strncpy(addr.sun_path, hapd->conf->test_socket, + strncpy(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(hapd->conf->test_socket); - free(drv); + unlink(drv->own_socket_path); + test_driver_free_priv(drv); return -1; } eloop_register_read_sock(drv->test_socket, @@ -475,13 +1011,18 @@ 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->hapd->conf->test_socket); + unlink(drv->own_socket_path); } drv->hapd->driver = NULL; - free(drv->ie); - free(drv); + /* There should be only one BSS remaining at this point. */ + if (drv->bss == NULL) + wpa_printf(MSG_ERROR, "%s: drv->bss == NULL", __func__); + else if (drv->bss->next) + wpa_printf(MSG_ERROR, "%s: drv->bss->next != NULL", __func__); + + test_driver_free_priv(drv); } @@ -490,9 +1031,22 @@ static const struct driver_ops test_driver_ops = { .init = test_driver_init, .deinit = test_driver_deinit, .send_eapol = test_driver_send_eapol, + .send_mgmt_frame = test_driver_send_mgmt_frame, .set_generic_elem = test_driver_set_generic_elem, .sta_deauth = test_driver_sta_deauth, .sta_disassoc = test_driver_sta_disassoc, + .get_hw_feature_data = test_driver_get_hw_feature_data, + .bss_add = test_driver_bss_add, + .bss_remove = test_driver_bss_remove, + .if_add = test_driver_if_add, + .if_update = test_driver_if_update, + .if_remove = test_driver_if_remove, + .valid_bss_mask = test_driver_valid_bss_mask, + .set_ssid = test_driver_set_ssid, + .set_privacy = test_driver_set_privacy, + .set_encryption = test_driver_set_encryption, + .set_sta_vlan = test_driver_set_sta_vlan, + .sta_add = test_driver_sta_add, }; diff --git a/contrib/hostapd/driver_wired.c b/contrib/hostapd/driver_wired.c index 6ef6b19..9506681 100644 --- a/contrib/hostapd/driver_wired.c +++ b/contrib/hostapd/driver_wired.c @@ -1,7 +1,6 @@ /* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / Kernel driver communication - * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * hostapd / Kernel driver communication for wired (Ethernet) drivers + * Copyright (c) 2002-2005, 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 @@ -14,16 +13,8 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> -#include <errno.h> +#include "includes.h" #include <sys/ioctl.h> -#include <netinet/in.h> -#include <signal.h> -#include <sys/types.h> -#include <sys/socket.h> #ifdef USE_KERNEL_HEADERS #include <asm/types.h> @@ -105,7 +96,8 @@ static void wired_possible_new_sta(struct hostapd_data *hapd, u8 *addr) } -static void handle_data(struct hostapd_data *hapd, char *buf, size_t len) +static void handle_data(struct hostapd_data *hapd, unsigned char *buf, + size_t len) { struct ieee8023_hdr *hdr; u8 *pos, *sa; @@ -306,8 +298,9 @@ static int wired_init_sockets(struct wired_driver_data *drv) } -static int wired_send_eapol(void *priv, u8 *addr, - u8 *data, size_t data_len, int encrypt) +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; @@ -317,17 +310,16 @@ static int wired_send_eapol(void *priv, u8 *addr, int res; len = sizeof(*hdr) + data_len; - hdr = malloc(len); + hdr = wpa_zalloc(len); if (hdr == NULL) { printf("malloc() failed for wired_send_eapol(len=%lu)\n", (unsigned long) len); return -1; } - memset(hdr, 0, len); memcpy(hdr->dest, drv->use_pae_group_addr ? pae_group_addr : addr, ETH_ALEN); - memcpy(hdr->src, drv->hapd->own_addr, ETH_ALEN); + memcpy(hdr->src, own_addr, ETH_ALEN); hdr->ethertype = htons(ETH_P_PAE); pos = (u8 *) (hdr + 1); @@ -350,19 +342,20 @@ static int wired_driver_init(struct hostapd_data *hapd) { struct wired_driver_data *drv; - drv = malloc(sizeof(struct wired_driver_data)); + drv = wpa_zalloc(sizeof(struct wired_driver_data)); if (drv == NULL) { printf("Could not allocate memory for wired driver data\n"); return -1; } - memset(drv, 0, sizeof(*drv)); drv->ops = wired_driver_ops; drv->hapd = hapd; drv->use_pae_group_addr = hapd->conf->use_pae_group_addr; - if (wired_init_sockets(drv)) + if (wired_init_sockets(drv)) { + free(drv); return -1; + } hapd->driver = &drv->ops; return 0; diff --git a/contrib/hostapd/eap.c b/contrib/hostapd/eap.c index a20147e..da250f0 100644 --- a/contrib/hostapd/eap.c +++ b/contrib/hostapd/eap.c @@ -1,6 +1,6 @@ /* - * hostapd / EAP Standalone Authenticator state machine - * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * hostapd / EAP Standalone Authenticator state machine (RFC 4137) + * 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 @@ -12,103 +12,22 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <netinet/in.h> -#include <string.h> -#include <sys/socket.h> +#include "includes.h" #include "hostapd.h" -#include "eloop.h" #include "sta_info.h" #include "eap_i.h" +#include "state_machine.h" -#define EAP_MAX_AUTH_ROUNDS 50 +#define STATE_MACHINE_DATA struct eap_sm +#define STATE_MACHINE_DEBUG_PREFIX "EAP" -extern const struct eap_method eap_method_identity; -#ifdef EAP_MD5 -extern const struct eap_method eap_method_md5; -#endif /* EAP_MD5 */ -#ifdef EAP_TLS -extern const struct eap_method eap_method_tls; -#endif /* EAP_TLS */ -#ifdef EAP_MSCHAPv2 -extern const struct eap_method eap_method_mschapv2; -#endif /* EAP_MSCHAPv2 */ -#ifdef EAP_PEAP -extern const struct eap_method eap_method_peap; -#endif /* EAP_PEAP */ -#ifdef EAP_TLV -extern const struct eap_method eap_method_tlv; -#endif /* EAP_TLV */ -#ifdef EAP_GTC -extern const struct eap_method eap_method_gtc; -#endif /* EAP_GTC */ -#ifdef EAP_TTLS -extern const struct eap_method eap_method_ttls; -#endif /* EAP_TTLS */ -#ifdef EAP_SIM -extern const struct eap_method eap_method_sim; -#endif /* EAP_SIM */ -#ifdef EAP_PAX -extern const struct eap_method eap_method_pax; -#endif /* EAP_PAX */ -#ifdef EAP_PSK -extern const struct eap_method eap_method_psk; -#endif /* EAP_PSK */ - -static const struct eap_method *eap_methods[] = -{ - &eap_method_identity, -#ifdef EAP_MD5 - &eap_method_md5, -#endif /* EAP_MD5 */ -#ifdef EAP_TLS - &eap_method_tls, -#endif /* EAP_TLS */ -#ifdef EAP_MSCHAPv2 - &eap_method_mschapv2, -#endif /* EAP_MSCHAPv2 */ -#ifdef EAP_PEAP - &eap_method_peap, -#endif /* EAP_PEAP */ -#ifdef EAP_TTLS - &eap_method_ttls, -#endif /* EAP_TTLS */ -#ifdef EAP_TLV - &eap_method_tlv, -#endif /* EAP_TLV */ -#ifdef EAP_GTC - &eap_method_gtc, -#endif /* EAP_GTC */ -#ifdef EAP_SIM - &eap_method_sim, -#endif /* EAP_SIM */ -#ifdef EAP_PAX - &eap_method_pax, -#endif /* EAP_PAX */ -#ifdef EAP_PSK - &eap_method_psk, -#endif /* EAP_PSK */ -}; -#define NUM_EAP_METHODS (sizeof(eap_methods) / sizeof(eap_methods[0])) - - -const struct eap_method * eap_sm_get_eap_methods(int method) -{ - int i; - for (i = 0; i < NUM_EAP_METHODS; i++) { - if (eap_methods[i]->method == method) - return eap_methods[i]; - } - return NULL; -} +#define EAP_MAX_AUTH_ROUNDS 50 static void eap_user_free(struct eap_user *user); -/* EAP state machines are described in draft-ietf-eap-statemachine-05.txt */ +/* EAP state machines are described in RFC 4137 */ static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount, int eapSRTT, int eapRTTVAR, @@ -118,34 +37,11 @@ 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 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 EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm); +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); -/* Definitions for clarifying state machine implementation */ -#define SM_STATE(machine, state) \ -static void sm_ ## machine ## _ ## state ## _Enter(struct eap_sm *sm, \ - int global) - -#define SM_ENTRY(machine, state) \ -if (!global || sm->machine ## _state != machine ## _ ## state) { \ - sm->changed = TRUE; \ - wpa_printf(MSG_DEBUG, "EAP: " #machine " entering state " #state); \ -} \ -sm->machine ## _state = machine ## _ ## state; - -#define SM_ENTER(machine, state) \ -sm_ ## machine ## _ ## state ## _Enter(sm, 0) -#define SM_ENTER_GLOBAL(machine, state) \ -sm_ ## machine ## _ ## state ## _Enter(sm, 1) - -#define SM_STEP(machine) \ -static void sm_ ## machine ## _Step(struct eap_sm *sm) - -#define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm) - - static Boolean eapol_get_bool(struct eap_sm *sm, enum eapol_bool_var var) { return sm->eapol_cb->get_bool(sm->eapol_ctx, var); @@ -177,6 +73,19 @@ static void eapol_set_eapKeyData(struct eap_sm *sm, } +/** + * eap_user_get - Fetch user information from the database + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @identity: Identity (User-Name) of the user + * @identity_len: Length of identity in bytes + * @phase2: 0 = EAP phase1 user, 1 = EAP phase2 (tunneled) user + * Returns: 0 on success, or -1 on failure + * + * This function is used to fetch user information for EAP. The user will be + * selected based on the specified identity. sm->user and + * sm->user_eap_method_index are updated for the new user when a matching user + * is found. sm->user can be used to get user information (e.g., password). + */ int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len, int phase2) { @@ -189,10 +98,9 @@ int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len, eap_user_free(sm->user); sm->user = NULL; - user = malloc(sizeof(*user)); + user = wpa_zalloc(sizeof(*user)); if (user == NULL) return -1; - memset(user, 0, sizeof(*user)); if (sm->eapol_cb->get_eap_user(sm->eapol_ctx, identity, identity_len, phase2, user) != 0) { @@ -228,9 +136,11 @@ SM_STATE(EAP, INITIALIZE) /* eapKeyAvailable = FALSE */ eapol_set_bool(sm, EAPOL_eapRestart, FALSE); - /* This is not defined in draft-ietf-eap-statemachine-05.txt, but - * method state needs to be reseted here so that it does not remain in - * success state when re-authentication starts. */ + /* + * This is not defined in RFC 4137, but method state needs to be + * reseted here so that it does not remain in success state when + * re-authentication starts. + */ if (sm->m && sm->eap_method_priv) { sm->m->reset(sm, sm->eap_method_priv); sm->eap_method_priv = NULL; @@ -247,6 +157,7 @@ SM_STATE(EAP, INITIALIZE) } } sm->num_rounds = 0; + sm->method_pending = METHOD_PENDING_NONE; } @@ -260,7 +171,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(sm->currentMethod); + sm->m = eap_sm_get_eap_methods(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) { @@ -397,14 +309,21 @@ SM_STATE(EAP, METHOD_RESPONSE) SM_STATE(EAP, PROPOSE_METHOD) { + int vendor; + EapType type; + SM_ENTRY(EAP, PROPOSE_METHOD); - sm->currentMethod = eap_sm_Policy_getNextMethod(sm); + type = eap_sm_Policy_getNextMethod(sm, &vendor); + if (vendor == EAP_VENDOR_IETF) + sm->currentMethod = type; + else + sm->currentMethod = EAP_TYPE_EXPANDED; if (sm->m && sm->eap_method_priv) { sm->m->reset(sm, sm->eap_method_priv); sm->eap_method_priv = NULL; } - sm->m = eap_sm_get_eap_methods(sm->currentMethod); + sm->m = eap_sm_get_eap_methods(vendor, type); if (sm->m) { sm->eap_method_priv = sm->m->init(sm); if (sm->eap_method_priv == NULL) { @@ -534,7 +453,9 @@ SM_STEP(EAP) SM_ENTER(EAP, SELECT_ACTION); else if (sm->rxResp && (sm->respMethod == EAP_TYPE_NAK || - sm->respMethod == EAP_TYPE_EXPANDED_NAK)) + (sm->respMethod == EAP_TYPE_EXPANDED && + sm->respVendor == EAP_VENDOR_IETF && + sm->respVendorMethod == EAP_TYPE_NAK))) SM_ENTER(EAP, NAK); else SM_ENTER(EAP, PICK_UP_METHOD); @@ -568,14 +489,25 @@ SM_STEP(EAP) case EAP_RECEIVED: if (sm->rxResp && (sm->respId == sm->currentId) && (sm->respMethod == EAP_TYPE_NAK || - sm->respMethod == EAP_TYPE_EXPANDED_NAK) + (sm->respMethod == EAP_TYPE_EXPANDED && + sm->respVendor == EAP_VENDOR_IETF && + sm->respVendorMethod == EAP_TYPE_NAK)) && (sm->methodState == METHOD_PROPOSED)) SM_ENTER(EAP, NAK); else if (sm->rxResp && (sm->respId == sm->currentId) && - (sm->respMethod == sm->currentMethod)) + ((sm->respMethod == sm->currentMethod) || + (sm->respMethod == EAP_TYPE_EXPANDED && + sm->respVendor == EAP_VENDOR_IETF && + sm->respVendorMethod == sm->currentMethod))) SM_ENTER(EAP, INTEGRITY_CHECK); - else + else { + wpa_printf(MSG_DEBUG, "EAP: RECEIVED->DISCARD: " + "rxResp=%d respId=%d currentId=%d " + "respMethod=%d currentMethod=%d", + sm->rxResp, sm->respId, sm->currentId, + sm->respMethod, sm->currentMethod); SM_ENTER(EAP, DISCARD); + } break; case EAP_DISCARD: SM_ENTER(EAP, IDLE); @@ -593,13 +525,48 @@ SM_STEP(EAP) SM_ENTER(EAP, SEND_REQUEST); break; case EAP_METHOD_RESPONSE: + /* + * Note: Mechanism to allow EAP methods to wait while going + * through pending processing is an extension to RFC 4137 + * which only defines the transits to SELECT_ACTION and + * METHOD_REQUEST from this METHOD_RESPONSE state. + */ if (sm->methodState == METHOD_END) SM_ENTER(EAP, SELECT_ACTION); - else + else if (sm->method_pending == METHOD_PENDING_WAIT) { + wpa_printf(MSG_DEBUG, "EAP: Method has pending " + "processing - wait before proceeding to " + "METHOD_REQUEST state"); + } else if (sm->method_pending == METHOD_PENDING_CONT) { + wpa_printf(MSG_DEBUG, "EAP: Method has completed " + "pending processing - reprocess pending " + "EAP message"); + sm->method_pending = METHOD_PENDING_NONE; + SM_ENTER(EAP, METHOD_RESPONSE); + } else SM_ENTER(EAP, METHOD_REQUEST); break; case EAP_PROPOSE_METHOD: - SM_ENTER(EAP, METHOD_REQUEST); + /* + * Note: Mechanism to allow EAP methods to wait while going + * through pending processing is an extension to RFC 4137 + * which only defines the transit to METHOD_REQUEST from this + * PROPOSE_METHOD state. + */ + if (sm->method_pending == METHOD_PENDING_WAIT) { + wpa_printf(MSG_DEBUG, "EAP: Method has pending " + "processing - wait before proceeding to " + "METHOD_REQUEST state"); + if (sm->user_eap_method_index > 0) + sm->user_eap_method_index--; + } else if (sm->method_pending == METHOD_PENDING_CONT) { + wpa_printf(MSG_DEBUG, "EAP: Method has completed " + "pending processing - reprocess pending " + "EAP message"); + sm->method_pending = METHOD_PENDING_NONE; + SM_ENTER(EAP, PROPOSE_METHOD); + } else + SM_ENTER(EAP, METHOD_REQUEST); break; case EAP_NAK: SM_ENTER(EAP, SELECT_ACTION); @@ -642,6 +609,8 @@ static void eap_sm_parseEapResp(struct eap_sm *sm, u8 *resp, size_t len) sm->rxResp = FALSE; sm->respId = -1; sm->respMethod = EAP_TYPE_NONE; + sm->respVendor = EAP_VENDOR_IETF; + sm->respVendorMethod = EAP_TYPE_NONE; if (resp == NULL || len < sizeof(*hdr)) return; @@ -660,11 +629,26 @@ static void eap_sm_parseEapResp(struct eap_sm *sm, u8 *resp, size_t len) if (hdr->code == EAP_CODE_RESPONSE) sm->rxResp = TRUE; - if (len > sizeof(*hdr)) - sm->respMethod = *((u8 *) (hdr + 1)); + if (plen > sizeof(*hdr)) { + u8 *pos = (u8 *) (hdr + 1); + sm->respMethod = *pos++; + if (sm->respMethod == EAP_TYPE_EXPANDED) { + if (plen < sizeof(*hdr) + 8) { + wpa_printf(MSG_DEBUG, "EAP: Ignored truncated " + "expanded EAP-Packet (plen=%lu)", + (unsigned long) plen); + return; + } + sm->respVendor = WPA_GET_BE24(pos); + pos += 3; + sm->respVendorMethod = WPA_GET_BE32(pos); + } + } wpa_printf(MSG_DEBUG, "EAP: parseEapResp: rxResp=%d respId=%d " - "respMethod=%d", sm->rxResp, sm->respId, sm->respMethod); + "respMethod=%u respVendor=%u respVendorMethod=%u", + sm->rxResp, sm->respId, sm->respMethod, sm->respVendor, + sm->respVendorMethod); } @@ -705,7 +689,7 @@ static u8 * eap_sm_buildFailure(struct eap_sm *sm, int id, size_t *len) static int eap_sm_nextId(struct eap_sm *sm, int id) { if (id < 0) { - /* RFC 3748 Ch 4.1: recommended to initalize Identifier with a + /* RFC 3748 Ch 4.1: recommended to initialize Identifier with a * random number */ id = rand() & 0xff; if (id != sm->lastId) @@ -715,22 +699,40 @@ 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() + * @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) { - int i, j; + int i; + size_t j; + + if (sm->user == NULL) + return; wpa_printf(MSG_MSGDUMP, "EAP: processing NAK (current EAP method " "index %d)", sm->user_eap_method_index); wpa_hexdump(MSG_MSGDUMP, "EAP: configured methods", - sm->user->methods, EAP_MAX_METHODS); + (u8 *) sm->user->methods, + EAP_MAX_METHODS * sizeof(sm->user->methods[0])); wpa_hexdump(MSG_MSGDUMP, "EAP: list of methods supported by the peer", nak_list, len); i = sm->user_eap_method_index; - while (i < EAP_MAX_METHODS && sm->user->methods[i] != EAP_TYPE_NONE) { + while (i < EAP_MAX_METHODS && + (sm->user->methods[i].vendor != EAP_VENDOR_IETF || + sm->user->methods[i].method != EAP_TYPE_NONE)) { + if (sm->user->methods[i].vendor != EAP_VENDOR_IETF) + goto not_found; for (j = 0; j < len; j++) { - if (nak_list[j] == sm->user->methods[i]) { + if (nak_list[j] == sm->user->methods[i].method) { break; } } @@ -741,14 +743,19 @@ void eap_sm_process_nak(struct eap_sm *sm, u8 *nak_list, size_t len) continue; } + not_found: /* not found - remove from the list */ memmove(&sm->user->methods[i], &sm->user->methods[i + 1], - EAP_MAX_METHODS - i - 1); - sm->user->methods[EAP_MAX_METHODS - 1] = EAP_TYPE_NONE; + (EAP_MAX_METHODS - i - 1) * + sizeof(sm->user->methods[0])); + sm->user->methods[EAP_MAX_METHODS - 1].vendor = + EAP_VENDOR_IETF; + sm->user->methods[EAP_MAX_METHODS - 1].method = EAP_TYPE_NONE; } wpa_hexdump(MSG_MSGDUMP, "EAP: new list of configured methods", - sm->user->methods, EAP_MAX_METHODS); + (u8 *) sm->user->methods, EAP_MAX_METHODS * + sizeof(sm->user->methods[0])); } @@ -768,9 +775,10 @@ static void eap_sm_Policy_update(struct eap_sm *sm, u8 *nak_list, size_t len) } -static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm) +static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm, int *vendor) { EapType next; + int idx = sm->user_eap_method_index; /* In theory, there should be no problems with starting * re-authentication with something else than EAP-Request/Identity and @@ -780,16 +788,21 @@ static EapType eap_sm_Policy_getNextMethod(struct eap_sm *sm) * Re-auth sets currentId == -1, so that can be used here to select * whether Identity needs to be requested again. */ if (sm->identity == NULL || sm->currentId == -1) { + *vendor = EAP_VENDOR_IETF; next = EAP_TYPE_IDENTITY; sm->update_user = TRUE; - } else if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && - sm->user->methods[sm->user_eap_method_index] != - EAP_TYPE_NONE) { - next = sm->user->methods[sm->user_eap_method_index++]; + } else if (sm->user && idx < EAP_MAX_METHODS && + (sm->user->methods[idx].vendor != EAP_VENDOR_IETF || + sm->user->methods[idx].method != EAP_TYPE_NONE)) { + *vendor = sm->user->methods[idx].vendor; + next = sm->user->methods[idx].method; + sm->user_eap_method_index++; } else { + *vendor = EAP_VENDOR_IETF; next = EAP_TYPE_NONE; } - wpa_printf(MSG_DEBUG, "EAP: getNextMethod: type %d", next); + wpa_printf(MSG_DEBUG, "EAP: getNextMethod: vendor %d type %d", + *vendor, next); return next; } @@ -822,7 +835,10 @@ static int eap_sm_Policy_getDecision(struct eap_sm *sm) } if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && - sm->user->methods[sm->user_eap_method_index] != EAP_TYPE_NONE) { + (sm->user->methods[sm->user_eap_method_index].vendor != + EAP_VENDOR_IETF || + sm->user->methods[sm->user_eap_method_index].method != + EAP_TYPE_NONE)) { wpa_printf(MSG_DEBUG, "EAP: getDecision: another method " "available -> CONTINUE"); return DECISION_CONTINUE; @@ -846,6 +862,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() + * Returns: 1 if EAP state was changed or 0 if not + * + * This function advances EAP state machine to a new state to match with the + * current variables. This should be called whenever variables used by the EAP + * state machine have changed. + */ int eap_sm_step(struct eap_sm *sm) { int res = 0; @@ -859,17 +884,14 @@ int eap_sm_step(struct eap_sm *sm) } -u8 eap_get_type(const char *name) -{ - int i; - for (i = 0; i < NUM_EAP_METHODS; i++) { - if (strcmp(eap_methods[i]->name, name) == 0) - return eap_methods[i]->method; - } - return EAP_TYPE_NONE; -} - - +/** + * 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) { @@ -896,21 +918,29 @@ static void eap_user_free(struct eap_user *user) } +/** + * eap_sm_init - Allocate and initialize EAP state machine + * @eapol_ctx: Context data to be used with eapol_cb calls + * @eapol_cb: Pointer to EAPOL callback functions + * @conf: EAP configuration + * Returns: Pointer to the allocated EAP state machine or %NULL on failure + * + * This function allocates and initializes an EAP state machine. + */ struct eap_sm * eap_sm_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, - struct eap_config *eap_conf) + struct eap_config *conf) { struct eap_sm *sm; - sm = malloc(sizeof(*sm)); + sm = wpa_zalloc(sizeof(*sm)); if (sm == NULL) return NULL; - memset(sm, 0, sizeof(*sm)); sm->eapol_ctx = eapol_ctx; sm->eapol_cb = eapol_cb; sm->MaxRetrans = 10; - sm->ssl_ctx = eap_conf->ssl_ctx; - sm->eap_sim_db_priv = eap_conf->eap_sim_db_priv; - sm->backend_auth = eap_conf->backend_auth; + 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"); @@ -918,6 +948,13 @@ struct eap_sm * eap_sm_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, } +/** + * eap_sm_deinit - Deinitialize and free an EAP state machine + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * + * This function deinitializes EAP state machine and frees all allocated + * resources. + */ void eap_sm_deinit(struct eap_sm *sm) { if (sm == NULL) @@ -935,6 +972,13 @@ void eap_sm_deinit(struct eap_sm *sm) } +/** + * eap_sm_notify_cached - Notify EAP state machine of cached PMK + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * + * This function is called when PMKSA caching is used to skip EAP + * authentication. + */ void eap_sm_notify_cached(struct eap_sm *sm) { if (sm == NULL) @@ -942,3 +986,153 @@ void eap_sm_notify_cached(struct eap_sm *sm) sm->EAP_state = EAP_SUCCESS; } + + +/** + * eap_sm_pending_cb - EAP state machine callback for a pending EAP request + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * + * This function is called when data for a pending EAP-Request is received. + */ +void eap_sm_pending_cb(struct eap_sm *sm) +{ + if (sm == NULL) + return; + wpa_printf(MSG_DEBUG, "EAP: Callback for pending request received"); + if (sm->method_pending == METHOD_PENDING_WAIT) + sm->method_pending = METHOD_PENDING_CONT; +} + + +/** + * eap_sm_method_pending - Query whether EAP method is waiting for pending data + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * Returns: 1 if method is waiting for pending data or 0 if not + */ +int eap_sm_method_pending(struct eap_sm *sm) +{ + if (sm == NULL) + return 0; + return sm->method_pending == METHOD_PENDING_WAIT; +} + + +/** + * eap_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. + */ +const u8 * eap_hdr_validate(int vendor, EapType eap_type, + const u8 *msg, size_t msglen, size_t *plen) +{ + const struct eap_hdr *hdr; + const u8 *pos; + size_t len; + + hdr = (const struct eap_hdr *) msg; + + 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; + } +} + + +/** + * 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. + */ +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_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; +} diff --git a/contrib/hostapd/eap.h b/contrib/hostapd/eap.h index c5c62eb..1377191 100644 --- a/contrib/hostapd/eap.h +++ b/contrib/hostapd/eap.h @@ -1,16 +1,36 @@ +/* + * 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 { - u8 methods[EAP_MAX_METHODS]; + 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; }; @@ -46,10 +66,11 @@ 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); -u8 eap_get_type(const char *name); 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 */ @@ -69,10 +90,6 @@ static inline int eap_sm_step(struct eap_sm *sm) return 0; } -static inline u8 eap_get_type(const char *name) -{ - return EAP_TYPE_NONE; -} static inline void eap_set_eapRespData(struct eap_sm *sm, const u8 *eapRespData, @@ -84,6 +101,15 @@ 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_aka.c b/contrib/hostapd/eap_aka.c new file mode 100644 index 0000000..5db4cd3 --- /dev/null +++ b/contrib/hostapd/eap_aka.c @@ -0,0 +1,848 @@ +/* + * hostapd / EAP-AKA (RFC 4187) + * 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 "hostapd.h" +#include "common.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_encr[EAP_SIM_K_ENCR_LEN]; + u8 msk[EAP_SIM_KEYING_DATA_LEN]; + u8 emsk[EAP_EMSK_LEN]; + u8 rand[EAP_AKA_RAND_LEN]; + u8 autn[EAP_AKA_AUTN_LEN]; + u8 ck[EAP_AKA_CK_LEN]; + u8 ik[EAP_AKA_IK_LEN]; + u8 res[EAP_AKA_RES_MAX_LEN]; + size_t res_len; + enum { + IDENTITY, CHALLENGE, REAUTH, NOTIFICATION, SUCCESS, FAILURE + } state; + char *next_pseudonym; + char *next_reauth_id; + u16 counter; + struct eap_sim_reauth *reauth; + int auts_reported; /* whether the current AUTS has been reported to the + * eap_sim_db */ + u16 notification; +}; + + +static void eap_aka_determine_identity(struct eap_sm *sm, + struct eap_aka_data *data, + int before_identity, int after_reauth); + + +static const char * eap_aka_state_txt(int state) +{ + switch (state) { + case IDENTITY: + return "IDENTITY"; + case CHALLENGE: + return "CHALLENGE"; + case REAUTH: + return "REAUTH"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + case NOTIFICATION: + return "NOTIFICATION"; + default: + return "Unknown?!"; + } +} + + +static void eap_aka_state(struct eap_aka_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-AKA: %s -> %s", + eap_aka_state_txt(data->state), + eap_aka_state_txt(state)); + data->state = state; +} + + +static void * eap_aka_init(struct eap_sm *sm) +{ + struct eap_aka_data *data; + + if (sm->eap_sim_db_priv == NULL) { + wpa_printf(MSG_WARNING, "EAP-AKA: eap_sim_db not configured"); + return NULL; + } + + data = wpa_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = IDENTITY; + eap_aka_determine_identity(sm, data, 1, 0); + + return data; +} + + +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); +} + + +static u8 * eap_aka_build_identity(struct eap_sm *sm, + struct eap_aka_data *data, + int id, size_t *reqDataLen) +{ + struct eap_sim_msg *msg; + + wpa_printf(MSG_DEBUG, "EAP-AKA: Generating Identity"); + msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_AKA, + EAP_AKA_SUBTYPE_IDENTITY); + if (eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity, + sm->identity_len)) { + wpa_printf(MSG_DEBUG, " AT_PERMANENT_ID_REQ"); + eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0); + } + return eap_sim_msg_finish(msg, reqDataLen, NULL, NULL, 0); +} + + +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); + data->next_pseudonym = + eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 1); + 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); + } else { + wpa_printf(MSG_DEBUG, "EAP-AKA: Max fast re-authentication " + "count exceeded - force full authentication"); + data->next_reauth_id = NULL; + } + + if (data->next_pseudonym == NULL && data->next_reauth_id == NULL && + counter == 0 && nonce_s == NULL) + return 0; + + wpa_printf(MSG_DEBUG, " AT_IV"); + wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); + eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA); + + if (counter > 0) { + wpa_printf(MSG_DEBUG, " *AT_COUNTER (%u)", counter); + eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0); + } + + if (nonce_s) { + wpa_printf(MSG_DEBUG, " *AT_NONCE_S"); + eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_S, 0, nonce_s, + EAP_SIM_NONCE_S_LEN); + } + + if (data->next_pseudonym) { + wpa_printf(MSG_DEBUG, " *AT_NEXT_PSEUDONYM (%s)", + data->next_pseudonym); + eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_PSEUDONYM, + strlen(data->next_pseudonym), + (u8 *) data->next_pseudonym, + 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), + (u8 *) data->next_reauth_id, + strlen(data->next_reauth_id)); + } + + if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Failed to encrypt " + "AT_ENCR_DATA"); + return -1; + } + + return 0; +} + + +static u8 * eap_aka_build_challenge(struct eap_sm *sm, + struct eap_aka_data *data, + int id, size_t *reqDataLen) +{ + 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, + 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); + eap_sim_msg_add(msg, EAP_SIM_AT_AUTN, 0, data->autn, EAP_AKA_AUTN_LEN); + + if (eap_aka_build_encr(sm, data, msg, 0, NULL)) { + 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, reqDataLen, 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) +{ + 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)) + 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); + + msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_AKA, + EAP_AKA_SUBTYPE_REAUTHENTICATION); + + if (eap_aka_build_encr(sm, data, msg, data->counter, data->nonce_s)) { + 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, reqDataLen, 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) +{ + 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, + EAP_AKA_SUBTYPE_NOTIFICATION); + wpa_printf(MSG_DEBUG, " AT_NOTIFICATION"); + eap_sim_msg_add(msg, EAP_SIM_AT_NOTIFICATION, data->notification, + NULL, 0); + return eap_sim_msg_finish(msg, reqDataLen, NULL, NULL, 0); +} + + +static u8 * eap_aka_buildReq(struct eap_sm *sm, void *priv, int id, + size_t *reqDataLen) +{ + struct eap_aka_data *data = priv; + + data->auts_reported = 0; + switch (data->state) { + case IDENTITY: + return eap_aka_build_identity(sm, data, id, reqDataLen); + case CHALLENGE: + return eap_aka_build_challenge(sm, data, id, reqDataLen); + case REAUTH: + return eap_aka_build_reauth(sm, data, id, reqDataLen); + case NOTIFICATION: + return eap_aka_build_notification(sm, data, id, reqDataLen); + default: + wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown state %d in " + "buildReq", data->state); + break; + } + return NULL; +} + + +static Boolean eap_aka_check(struct eap_sm *sm, void *priv, + u8 *respData, size_t respDataLen) +{ + struct eap_hdr *resp; + u8 *pos; + + resp = (struct eap_hdr *) respData; + pos = (u8 *) (resp + 1); + if (respDataLen < sizeof(*resp) + 4 || *pos != EAP_TYPE_AKA || + (ntohs(resp->length)) > respDataLen) { + wpa_printf(MSG_INFO, "EAP-AKA: Invalid frame"); + return TRUE; + } + + return FALSE; +} + + +static Boolean eap_aka_subtype_ok(struct eap_aka_data *data, u8 subtype) +{ + if (subtype == EAP_AKA_SUBTYPE_CLIENT_ERROR || + subtype == EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT) + return FALSE; + + switch (data->state) { + case IDENTITY: + if (subtype != EAP_AKA_SUBTYPE_IDENTITY) { + wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response " + "subtype %d", subtype); + return TRUE; + } + break; + case CHALLENGE: + if (subtype != EAP_AKA_SUBTYPE_CHALLENGE && + subtype != EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE) { + wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response " + "subtype %d", subtype); + return TRUE; + } + break; + case REAUTH: + if (subtype != EAP_AKA_SUBTYPE_REAUTHENTICATION) { + wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response " + "subtype %d", subtype); + return TRUE; + } + break; + case NOTIFICATION: + if (subtype != EAP_AKA_SUBTYPE_NOTIFICATION) { + wpa_printf(MSG_INFO, "EAP-AKA: Unexpected response " + "subtype %d", subtype); + return TRUE; + } + break; + default: + wpa_printf(MSG_INFO, "EAP-AKA: Unexpected state (%d) for " + "processing a response", data->state); + return TRUE; + } + + return FALSE; +} + + +static void eap_aka_determine_identity(struct eap_sm *sm, + struct eap_aka_data *data, + int before_identity, int after_reauth) +{ + const u8 *identity; + size_t identity_len; + int res; + + identity = NULL; + identity_len = 0; + + if (after_reauth && data->reauth) { + identity = data->reauth->identity; + identity_len = data->reauth->identity_len; + } else if (sm->identity && sm->identity_len > 0 && + sm->identity[0] == EAP_AKA_PERMANENT_PREFIX) { + identity = sm->identity; + identity_len = sm->identity_len; + } else { + identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv, + sm->identity, + sm->identity_len, + &identity_len); + if (identity == NULL) { + data->reauth = eap_sim_db_get_reauth_entry( + sm->eap_sim_db_priv, sm->identity, + sm->identity_len); + 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 (identity == NULL || + eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity, + sm->identity_len) < 0) { + if (before_identity) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Permanent user name " + "not known - send AKA-Identity request"); + eap_aka_state(data, IDENTITY); + return; + } else { + wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown whether the " + "permanent user name is known; try to use " + "it"); + /* eap_sim_db_get_aka_auth() will report failure, if + * this identity is not known. */ + } + } + + wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity", + identity, identity_len); + + if (!after_reauth && data->reauth) { + eap_aka_state(data, REAUTH); + return; + } + + res = eap_sim_db_get_aka_auth(sm->eap_sim_db_priv, identity, + identity_len, data->rand, data->autn, + data->ik, data->ck, data->res, + &data->res_len, sm); + if (res == EAP_SIM_DB_PENDING) { + wpa_printf(MSG_DEBUG, "EAP-AKA: AKA authentication data " + "not yet available - pending request"); + sm->method_pending = METHOD_PENDING_WAIT; + return; + } + + data->reauth = NULL; + data->counter = 0; /* reset re-auth counter since this is full auth */ + + if (res != 0) { + wpa_printf(MSG_INFO, "EAP-AKA: Failed to get AKA " + "authentication data for the peer"); + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_aka_state(data, NOTIFICATION); + return; + } + if (sm->method_pending == METHOD_PENDING_WAIT) { + wpa_printf(MSG_DEBUG, "EAP-AKA: AKA authentication data " + "available - abort pending wait"); + sm->method_pending = METHOD_PENDING_NONE; + } + + wpa_hexdump_ascii(MSG_DEBUG, "EAP-AKA: Identity for MK derivation", + sm->identity, sm->identity_len); + + eap_aka_derive_mk(sm->identity, sm->identity_len, data->ik, data->ck, + data->mk); + eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk, + data->emsk); + + eap_aka_state(data, CHALLENGE); +} + + +static void eap_aka_process_identity(struct eap_sm *sm, + struct eap_aka_data *data, + u8 *respData, size_t respDataLen, + struct eap_sim_attrs *attr) +{ + wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Identity"); + + if (attr->mac || attr->iv || attr->encr_data) { + wpa_printf(MSG_WARNING, "EAP-AKA: Unexpected attribute " + "received in EAP-Response/AKA-Identity"); + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_aka_state(data, NOTIFICATION); + return; + } + + if (attr->identity) { + free(sm->identity); + sm->identity = malloc(attr->identity_len); + if (sm->identity) { + memcpy(sm->identity, attr->identity, + attr->identity_len); + sm->identity_len = attr->identity_len; + } + } + + eap_aka_determine_identity(sm, data, 0, 0); +} + + +static void eap_aka_process_challenge(struct eap_sm *sm, + struct eap_aka_data *data, + u8 *respData, size_t respDataLen, + struct eap_sim_attrs *attr) +{ + const u8 *identity; + size_t identity_len; + + wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Challenge"); + + if (attr->mac == NULL || + eap_sim_verify_mac(data->k_aut, respData, respDataLen, attr->mac, + NULL, 0)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message " + "did not include valid AT_MAC"); + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_aka_state(data, NOTIFICATION); + return; + } + + if (attr->res == NULL || attr->res_len != data->res_len || + memcmp(attr->res, data->res, data->res_len) != 0) { + wpa_printf(MSG_WARNING, "EAP-AKA: Challenge message did not " + "include valid AT_RES"); + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_aka_state(data, NOTIFICATION); + return; + } + + wpa_printf(MSG_DEBUG, "EAP-AKA: Challenge response includes the " + "correct AT_MAC"); + eap_aka_state(data, SUCCESS); + + identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv, sm->identity, + sm->identity_len, &identity_len); + if (identity == NULL) { + identity = sm->identity; + identity_len = sm->identity_len; + } + + if (data->next_pseudonym) { + eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity, + identity_len, + data->next_pseudonym); + 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); + data->next_reauth_id = NULL; + } +} + + +static void eap_aka_process_sync_failure(struct eap_sm *sm, + struct eap_aka_data *data, + u8 *respData, size_t respDataLen, + struct eap_sim_attrs *attr) +{ + wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Synchronization-Failure"); + + if (attr->auts == NULL) { + wpa_printf(MSG_WARNING, "EAP-AKA: Synchronization-Failure " + "message did not include valid AT_AUTS"); + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_aka_state(data, NOTIFICATION); + return; + } + + /* Avoid re-reporting AUTS when processing pending EAP packet by + * maintaining a local flag stating whether this AUTS has already been + * reported. */ + if (!data->auts_reported && + eap_sim_db_resynchronize(sm->eap_sim_db_priv, sm->identity, + sm->identity_len, attr->auts, + data->rand)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Resynchronization failed"); + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_aka_state(data, NOTIFICATION); + return; + } + data->auts_reported = 1; + + /* Try again after resynchronization */ + eap_aka_determine_identity(sm, data, 0, 0); +} + + +static void eap_aka_process_reauth(struct eap_sm *sm, + struct eap_aka_data *data, + u8 *respData, size_t respDataLen, + struct eap_sim_attrs *attr) +{ + struct eap_sim_attrs eattr; + u8 *decrypted = NULL; + const u8 *identity, *id2; + size_t identity_len, id2_len; + + 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)) { + wpa_printf(MSG_WARNING, "EAP-AKA: Re-authentication message " + "did not include valid AT_MAC"); + goto fail; + } + + if (attr->encr_data == NULL || attr->iv == NULL) { + wpa_printf(MSG_WARNING, "EAP-AKA: Reauthentication " + "message did not include encrypted data"); + goto fail; + } + + decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, + attr->encr_data_len, attr->iv, &eattr, + 0); + if (decrypted == NULL) { + wpa_printf(MSG_WARNING, "EAP-AKA: Failed to parse encrypted " + "data from reauthentication message"); + goto fail; + } + + if (eattr.counter != data->counter) { + wpa_printf(MSG_WARNING, "EAP-AKA: Re-authentication message " + "used incorrect counter %u, expected %u", + eattr.counter, data->counter); + goto fail; + } + free(decrypted); + decrypted = NULL; + + wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response includes " + "the correct AT_MAC"); + + if (eattr.counter_too_small) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Re-authentication response " + "included AT_COUNTER_TOO_SMALL - starting full " + "authentication"); + eap_aka_determine_identity(sm, data, 0, 1); + return; + } + + eap_aka_state(data, SUCCESS); + + if (data->reauth) { + identity = data->reauth->identity; + identity_len = data->reauth->identity_len; + } else { + identity = sm->identity; + identity_len = sm->identity_len; + } + + id2 = eap_sim_db_get_permanent(sm->eap_sim_db_priv, identity, + identity_len, &id2_len); + if (id2) { + identity = id2; + identity_len = id2_len; + } + + if (data->next_pseudonym) { + eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity, + identity_len, data->next_pseudonym); + 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); + data->next_reauth_id = NULL; + } else { + eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth); + data->reauth = NULL; + } + + return; + +fail: + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_aka_state(data, NOTIFICATION); + eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth); + data->reauth = NULL; + free(decrypted); +} + + +static void eap_aka_process_client_error(struct eap_sm *sm, + struct eap_aka_data *data, + u8 *respData, size_t respDataLen, + struct eap_sim_attrs *attr) +{ + wpa_printf(MSG_DEBUG, "EAP-AKA: Client reported error %d", + attr->client_error_code); + 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) +{ + wpa_printf(MSG_DEBUG, "EAP-AKA: Client rejected authentication"); + eap_aka_state(data, FAILURE); +} + + +static void eap_aka_process_notification(struct eap_sm *sm, + struct eap_aka_data *data, + u8 *respData, size_t respDataLen, + struct eap_sim_attrs *attr) +{ + wpa_printf(MSG_DEBUG, "EAP-AKA: Client replied to notification"); + eap_aka_state(data, FAILURE); +} + + +static void eap_aka_process(struct eap_sm *sm, void *priv, + u8 *respData, size_t respDataLen) +{ + struct eap_aka_data *data = priv; + struct eap_hdr *resp; + u8 *pos, subtype; + size_t len; + struct eap_sim_attrs attr; + + resp = (struct eap_hdr *) respData; + pos = (u8 *) (resp + 1); + subtype = pos[1]; + + if (eap_aka_subtype_ok(data, subtype)) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Unrecognized or unexpected " + "EAP-AKA Subtype in EAP Response"); + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_aka_state(data, NOTIFICATION); + return; + } + + len = be_to_host16(resp->length); + pos += 4; + + if (eap_sim_parse_attr(pos, respData + len, &attr, 1, 0)) { + wpa_printf(MSG_DEBUG, "EAP-AKA: Failed to parse attributes"); + data->notification = EAP_SIM_GENERAL_FAILURE_BEFORE_AUTH; + eap_aka_state(data, NOTIFICATION); + return; + } + + if (subtype == EAP_AKA_SUBTYPE_CLIENT_ERROR) { + eap_aka_process_client_error(sm, data, respData, len, &attr); + return; + } + + if (subtype == EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT) { + eap_aka_process_authentication_reject(sm, data, respData, len, + &attr); + return; + } + + switch (data->state) { + case IDENTITY: + eap_aka_process_identity(sm, data, respData, len, &attr); + break; + case CHALLENGE: + if (subtype == EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE) { + eap_aka_process_sync_failure(sm, data, respData, len, + &attr); + } else { + eap_aka_process_challenge(sm, data, respData, len, + &attr); + } + break; + case REAUTH: + eap_aka_process_reauth(sm, data, respData, len, &attr); + break; + case NOTIFICATION: + eap_aka_process_notification(sm, data, respData, len, &attr); + break; + default: + wpa_printf(MSG_DEBUG, "EAP-AKA: Unknown state %d in " + "process", data->state); + break; + } +} + + +static Boolean eap_aka_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_aka_data *data = priv; + return data->state == SUCCESS || data->state == FAILURE; +} + + +static u8 * eap_aka_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_aka_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = malloc(EAP_SIM_KEYING_DATA_LEN); + if (key == NULL) + return NULL; + memcpy(key, data->msk, EAP_SIM_KEYING_DATA_LEN); + *len = EAP_SIM_KEYING_DATA_LEN; + return key; +} + + +static u8 * eap_aka_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_aka_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + memcpy(key, data->emsk, EAP_EMSK_LEN); + *len = EAP_EMSK_LEN; + return key; +} + + +static Boolean eap_aka_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_aka_data *data = priv; + return data->state == SUCCESS; +} + + +int eap_server_aka_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_AKA, "AKA"); + if (eap == NULL) + return -1; + + eap->init = eap_aka_init; + eap->reset = eap_aka_reset; + eap->buildReq = eap_aka_buildReq; + eap->check = eap_aka_check; + eap->process = eap_aka_process; + eap->isDone = eap_aka_isDone; + eap->getKey = eap_aka_getKey; + eap->isSuccess = eap_aka_isSuccess; + eap->get_emsk = eap_aka_get_emsk; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/contrib/hostapd/eap_defs.h b/contrib/hostapd/eap_defs.h index 9cd4490..8ea923a 100644 --- a/contrib/hostapd/eap_defs.h +++ b/contrib/hostapd/eap_defs.h @@ -1,6 +1,6 @@ /* - * WPA Supplicant/hostapd / Shared EAP definitions - * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * EAP server/peer: Shared EAP definitions + * 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 @@ -17,12 +17,20 @@ /* RFC 3748 - Extensible Authentication Protocol (EAP) */ +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + struct eap_hdr { u8 code; u8 identifier; u16 length; /* including code and identifier; network byte order */ /* followed by length-4 octets of data */ -} __attribute__ ((packed)); +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ enum { EAP_CODE_REQUEST = 1, EAP_CODE_RESPONSE = 2, EAP_CODE_SUCCESS = 3, EAP_CODE_FAILURE = 4 }; @@ -40,17 +48,28 @@ typedef enum { EAP_TYPE_GTC = 6, /* RFC 3748 */ EAP_TYPE_TLS = 13 /* RFC 2716 */, EAP_TYPE_LEAP = 17 /* Cisco proprietary */, - EAP_TYPE_SIM = 18 /* draft-haverinen-pppext-eap-sim-12.txt */, + EAP_TYPE_SIM = 18 /* RFC 4186 */, EAP_TYPE_TTLS = 21 /* draft-ietf-pppext-eap-ttls-02.txt */, - EAP_TYPE_AKA = 23 /* draft-arkko-pppext-eap-aka-12.txt */, + 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-00.txt */, - EAP_TYPE_PAX = 46, /* draft-clancy-eap-pax-04.txt */ - EAP_TYPE_EXPANDED_NAK = 254 /* RFC 3748 */, - EAP_TYPE_PSK = 255 /* EXPERIMENTAL - type not yet allocated - * draft-bersani-eap-psk-09 */ + EAP_TYPE_FAST = 43 /* draft-cam-winget-eap-fast-05.txt */, + 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 */ } EapType; + +/* SMI Network Management Private Enterprise Code for vendor specific types */ +enum { + EAP_VENDOR_IETF = 0 +}; + +#define EAP_MSK_LEN 64 +#define EAP_EMSK_LEN 64 + #endif /* EAP_DEFS_H */ diff --git a/contrib/hostapd/eap_gpsk.c b/contrib/hostapd/eap_gpsk.c new file mode 100644 index 0000000..8ab70a1 --- /dev/null +++ b/contrib/hostapd/eap_gpsk.c @@ -0,0 +1,646 @@ +/* + * hostapd / EAP-GPSK (draft-ietf-emu-eap-gpsk-03.txt) server + * 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 "hostapd.h" +#include "common.h" +#include "eap_i.h" +#include "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_client[EAP_GPSK_RAND_LEN]; + u8 msk[EAP_MSK_LEN]; + u8 emsk[EAP_EMSK_LEN]; + u8 sk[EAP_GPSK_MAX_SK_LEN]; + size_t sk_len; + u8 pk[EAP_GPSK_MAX_PK_LEN]; + size_t pk_len; + u8 *id_client; + size_t id_client_len; + u8 *id_server; + size_t id_server_len; +#define MAX_NUM_CSUITES 2 + struct eap_gpsk_csuite csuite_list[MAX_NUM_CSUITES]; + size_t csuite_count; + int vendor; /* CSuite/Vendor */ + int specifier; /* CSuite/Specifier */ +}; + + +static const char * eap_gpsk_state_txt(int state) +{ + switch (state) { + case GPSK_1: + return "GPSK-1"; + case GPSK_3: + return "GPSK-3"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "?"; + } +} + + +static void eap_gpsk_state(struct eap_gpsk_data *data, int state) +{ + wpa_printf(MSG_DEBUG, "EAP-GPSK: %s -> %s", + eap_gpsk_state_txt(data->state), + eap_gpsk_state_txt(state)); + data->state = state; +} + + +static void * eap_gpsk_init(struct eap_sm *sm) +{ + struct eap_gpsk_data *data; + + data = wpa_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"); + if (data->id_server) + data->id_server_len = strlen((char *) data->id_server); + + data->csuite_count = 0; + if (eap_gpsk_supported_ciphersuite(EAP_GPSK_VENDOR_IETF, + EAP_GPSK_CIPHER_AES)) { + WPA_PUT_BE24(data->csuite_list[data->csuite_count].vendor, + EAP_GPSK_VENDOR_IETF); + WPA_PUT_BE24(data->csuite_list[data->csuite_count].specifier, + EAP_GPSK_CIPHER_AES); + data->csuite_count++; + } + if (eap_gpsk_supported_ciphersuite(EAP_GPSK_VENDOR_IETF, + EAP_GPSK_CIPHER_SHA256)) { + WPA_PUT_BE24(data->csuite_list[data->csuite_count].vendor, + EAP_GPSK_VENDOR_IETF); + WPA_PUT_BE24(data->csuite_list[data->csuite_count].specifier, + EAP_GPSK_CIPHER_SHA256); + data->csuite_count++; + } + + return data; +} + + +static void eap_gpsk_reset(struct eap_sm *sm, void *priv) +{ + struct eap_gpsk_data *data = priv; + free(data->id_server); + free(data->id_client); + free(data); +} + + +static u8 * eap_gpsk_build_gpsk_1(struct eap_sm *sm, + struct eap_gpsk_data *data, + int id, size_t *reqDataLen) +{ + u8 *pos; + size_t len; + struct eap_hdr *req; + + wpa_printf(MSG_DEBUG, "EAP-GPSK: Request/GPSK-1"); + + if (hostapd_get_rand(data->rand_server, EAP_GPSK_RAND_LEN)) { + wpa_printf(MSG_ERROR, "EAP-GPSK: Failed to get random data"); + eap_gpsk_state(data, FAILURE); + return NULL; + } + wpa_hexdump(MSG_MSGDUMP, "EAP-GPSK: RAND_Server", + data->rand_server, EAP_GPSK_RAND_LEN); + + len = 1 + 2 + 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); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-GPSK: Failed to allocate memory " + "for request/GPSK-1"); + eap_gpsk_state(data, FAILURE); + return NULL; + } + + *pos++ = EAP_GPSK_OPCODE_GPSK_1; + + 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; +} + + +static u8 * eap_gpsk_build_gpsk_3(struct eap_sm *sm, + struct eap_gpsk_data *data, + int id, size_t *reqDataLen) +{ + u8 *pos, *start; + size_t len, miclen; + struct eap_gpsk_csuite *csuite; + struct eap_hdr *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 + sizeof(struct eap_gpsk_csuite) + 2 + + miclen; + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GPSK, reqDataLen, + len, EAP_CODE_REQUEST, id, &pos); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-GPSK: Failed to allocate memory " + "for request/GPSK-3"); + eap_gpsk_state(data, FAILURE); + return NULL; + } + + *pos++ = EAP_GPSK_OPCODE_GPSK_3; + start = pos; + + memcpy(pos, data->rand_client, EAP_GPSK_RAND_LEN); + pos += EAP_GPSK_RAND_LEN; + memcpy(pos, data->rand_server, EAP_GPSK_RAND_LEN); + pos += EAP_GPSK_RAND_LEN; + csuite = (struct eap_gpsk_csuite *) pos; + WPA_PUT_BE24(csuite->vendor, data->vendor); + WPA_PUT_BE24(csuite->specifier, data->specifier); + pos += sizeof(*csuite); + + /* no PD_Payload_2 */ + WPA_PUT_BE16(pos, 0); + pos += 2; + + if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor, + data->specifier, start, pos - start, pos) < 0) + { + free(req); + eap_gpsk_state(data, FAILURE); + return NULL; + } + + return (u8 *) req; +} + + +static u8 * eap_gpsk_buildReq(struct eap_sm *sm, void *priv, int id, + size_t *reqDataLen) +{ + struct eap_gpsk_data *data = priv; + + switch (data->state) { + case GPSK_1: + return eap_gpsk_build_gpsk_1(sm, data, id, reqDataLen); + case GPSK_3: + return eap_gpsk_build_gpsk_3(sm, data, id, reqDataLen); + default: + wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown state %d in buildReq", + data->state); + break; + } + return NULL; +} + + +static Boolean eap_gpsk_check(struct eap_sm *sm, void *priv, + u8 *respData, size_t respDataLen) +{ + 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); + if (pos == NULL || len < 1) { + wpa_printf(MSG_INFO, "EAP-GPSK: Invalid frame"); + return TRUE; + } + + wpa_printf(MSG_DEBUG, "EAP-GPSK: Received frame: opcode=%d", *pos); + + if (data->state == GPSK_1 && *pos == EAP_GPSK_OPCODE_GPSK_2) + return FALSE; + + if (data->state == GPSK_3 && *pos == EAP_GPSK_OPCODE_GPSK_4) + return FALSE; + + wpa_printf(MSG_INFO, "EAP-GPSK: Unexpected opcode=%d in state=%d", + *pos, data->state); + + return TRUE; +} + + +static void eap_gpsk_process_gpsk_2(struct eap_sm *sm, + struct eap_gpsk_data *data, + u8 *respData, size_t respDataLen, + const u8 *payload, size_t payloadlen) +{ + const u8 *pos, *end; + u16 alen; + const struct eap_gpsk_csuite *csuite; + size_t i, miclen; + u8 mic[EAP_GPSK_MAX_MIC_LEN]; + + if (data->state != GPSK_1) + return; + + wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Response/GPSK-2"); + + pos = payload; + end = payload + payloadlen; + + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " + "ID_Client length"); + eap_gpsk_state(data, FAILURE); + return; + } + alen = WPA_GET_BE16(pos); + pos += 2; + if (end - pos < alen) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " + "ID_Client"); + eap_gpsk_state(data, FAILURE); + return; + } + free(data->id_client); + data->id_client = malloc(alen); + if (data->id_client == NULL) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Not enough memory to store " + "%d-octet ID_Client", alen); + return; + } + memcpy(data->id_client, pos, alen); + data->id_client_len = alen; + wpa_hexdump_ascii(MSG_DEBUG, "EAP-GPSK: ID_Client", + data->id_client, data->id_client_len); + pos += alen; + + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " + "ID_Server length"); + eap_gpsk_state(data, FAILURE); + return; + } + alen = WPA_GET_BE16(pos); + pos += 2; + if (end - pos < alen) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " + "ID_Server"); + eap_gpsk_state(data, FAILURE); + return; + } + if (alen != data->id_server_len || + 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); + return; + } + pos += alen; + + if (end - pos < EAP_GPSK_RAND_LEN) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " + "RAND_Client"); + eap_gpsk_state(data, FAILURE); + return; + } + memcpy(data->rand_client, pos, EAP_GPSK_RAND_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Client", + data->rand_client, EAP_GPSK_RAND_LEN); + pos += EAP_GPSK_RAND_LEN; + + if (end - pos < EAP_GPSK_RAND_LEN) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " + "RAND_Server"); + eap_gpsk_state(data, FAILURE); + return; + } + if (memcmp(data->rand_server, pos, EAP_GPSK_RAND_LEN) != 0) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1 and " + "GPSK-2 did not match"); + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-1", + data->rand_server, EAP_GPSK_RAND_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: RAND_Server in GPSK-2", + pos, EAP_GPSK_RAND_LEN); + eap_gpsk_state(data, FAILURE); + return; + } + pos += EAP_GPSK_RAND_LEN; + + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " + "CSuite_List length"); + eap_gpsk_state(data, FAILURE); + return; + } + alen = WPA_GET_BE16(pos); + pos += 2; + if (end - pos < alen) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " + "CSuite_List"); + eap_gpsk_state(data, FAILURE); + return; + } + if (alen != data->csuite_count * sizeof(struct eap_gpsk_csuite) || + memcmp(pos, data->csuite_list, alen) != 0) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_List in GPSK-1 and " + "GPSK-2 did not match"); + eap_gpsk_state(data, FAILURE); + return; + } + pos += alen; + + if (end - pos < (int) sizeof(*csuite)) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " + "CSuite_Sel"); + eap_gpsk_state(data, FAILURE); + return; + } + csuite = (const struct eap_gpsk_csuite *) pos; + for (i = 0; i < data->csuite_count; i++) { + if (memcmp(csuite, &data->csuite_list[i], sizeof(*csuite)) == + 0) + break; + } + if (i == data->csuite_count) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Peer selected unsupported " + "ciphersuite %d:%d", + WPA_GET_BE24(csuite->vendor), + WPA_GET_BE24(csuite->specifier)); + eap_gpsk_state(data, FAILURE); + return; + } + data->vendor = WPA_GET_BE24(csuite->vendor); + data->specifier = WPA_GET_BE24(csuite->specifier); + wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_Sel %d:%d", + data->vendor, data->specifier); + pos += sizeof(*csuite); + + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " + "PD_Payload_1 length"); + eap_gpsk_state(data, FAILURE); + return; + } + alen = WPA_GET_BE16(pos); + pos += 2; + if (end - pos < alen) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " + "PD_Payload_1"); + eap_gpsk_state(data, FAILURE); + return; + } + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: PD_Payload_1", pos, alen); + pos += alen; + + if (sm->user == NULL || sm->user->password == NULL) { + wpa_printf(MSG_INFO, "EAP-GPSK: No PSK/password configured " + "for the user"); + eap_gpsk_state(data, FAILURE); + return; + } + + if (eap_gpsk_derive_keys(sm->user->password, sm->user->password_len, + data->vendor, data->specifier, + data->rand_client, data->rand_server, + data->id_client, data->id_client_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); + return; + } + + miclen = eap_gpsk_mic_len(data->vendor, data->specifier); + if (end - pos < (int) miclen) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC " + "(left=%d miclen=%d)", end - pos, miclen); + eap_gpsk_state(data, FAILURE); + return; + } + if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor, + data->specifier, payload, pos - payload, mic) + < 0) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC"); + eap_gpsk_state(data, FAILURE); + return; + } + if (memcmp(mic, pos, miclen) != 0) { + wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-2"); + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen); + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen); + eap_gpsk_state(data, FAILURE); + return; + } + pos += miclen; + + if (pos != end) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %d bytes of extra " + "data in the end of GPSK-2", end - pos); + } + + eap_gpsk_state(data, GPSK_3); +} + + +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; + u16 alen; + size_t miclen; + u8 mic[EAP_GPSK_MAX_MIC_LEN]; + + if (data->state != GPSK_3) + return; + + wpa_printf(MSG_DEBUG, "EAP-GPSK: Received Response/GPSK-4"); + + pos = payload; + end = payload + payloadlen; + + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " + "PD_Payload_1 length"); + eap_gpsk_state(data, FAILURE); + return; + } + alen = WPA_GET_BE16(pos); + pos += 2; + if (end - pos < alen) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for " + "PD_Payload_1"); + eap_gpsk_state(data, FAILURE); + return; + } + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: PD_Payload_1", pos, alen); + pos += alen; + + miclen = eap_gpsk_mic_len(data->vendor, data->specifier); + if (end - pos < (int) miclen) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Message too short for MIC " + "(left=%d miclen=%d)", end - pos, miclen); + eap_gpsk_state(data, FAILURE); + return; + } + if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor, + data->specifier, payload, pos - payload, mic) + < 0) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to compute MIC"); + eap_gpsk_state(data, FAILURE); + return; + } + if (memcmp(mic, pos, miclen) != 0) { + wpa_printf(MSG_INFO, "EAP-GPSK: Incorrect MIC in GPSK-4"); + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Received MIC", pos, miclen); + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Computed MIC", mic, miclen); + eap_gpsk_state(data, FAILURE); + return; + } + pos += miclen; + + if (pos != end) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Ignored %d bytes of extra " + "data in the end of GPSK-4", end - pos); + } + + eap_gpsk_state(data, SUCCESS); +} + + +static void eap_gpsk_process(struct eap_sm *sm, void *priv, + u8 *respData, size_t respDataLen) +{ + 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); + 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); + break; + case EAP_GPSK_OPCODE_GPSK_4: + eap_gpsk_process_gpsk_4(sm, data, respData, respDataLen, + pos + 1, len - 1); + break; + } +} + + +static Boolean eap_gpsk_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_gpsk_data *data = priv; + return data->state == SUCCESS || data->state == FAILURE; +} + + +static u8 * eap_gpsk_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_gpsk_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = malloc(EAP_MSK_LEN); + if (key == NULL) + return NULL; + 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 = malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + memcpy(key, data->emsk, EAP_EMSK_LEN); + *len = EAP_EMSK_LEN; + + return key; +} + + +static Boolean eap_gpsk_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_gpsk_data *data = priv; + return data->state == SUCCESS; +} + + +int eap_server_gpsk_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_GPSK, "GPSK"); + if (eap == NULL) + return -1; + + eap->init = eap_gpsk_init; + eap->reset = eap_gpsk_reset; + eap->buildReq = eap_gpsk_buildReq; + eap->check = eap_gpsk_check; + eap->process = eap_gpsk_process; + eap->isDone = eap_gpsk_isDone; + eap->getKey = eap_gpsk_getKey; + eap->isSuccess = eap_gpsk_isSuccess; + eap->get_emsk = eap_gpsk_get_emsk; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/contrib/hostapd/eap_gpsk_common.c b/contrib/hostapd/eap_gpsk_common.c new file mode 100644 index 0000000..a72b5f3 --- /dev/null +++ b/contrib/hostapd/eap_gpsk_common.c @@ -0,0 +1,441 @@ +/* + * EAP server/peer: EAP-GPSK shared 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 "eap_defs.h" +#include "aes_wrap.h" +#include "crypto.h" +#include "sha1.h" +#include "sha256.h" +#include "eap_gpsk_common.h" + + +/** + * eap_gpsk_supported_ciphersuite - Check whether ciphersuite is supported + * @vendor: CSuite/Vendor + * @specifier: CSuite/Specifier + * Returns: 1 if ciphersuite is support, or 0 if not + */ +int eap_gpsk_supported_ciphersuite(int vendor, int specifier) +{ + if (vendor == EAP_GPSK_VENDOR_IETF && + specifier == EAP_GPSK_CIPHER_AES) + return 1; +#ifdef EAP_GPSK_SHA256 + if (vendor == EAP_GPSK_VENDOR_IETF && + specifier == EAP_GPSK_CIPHER_SHA256) + return 1; +#endif /* EAP_GPSK_SHA256 */ + return 0; +} + + +static int eap_gpsk_gkdf(const u8 *psk /* Y */, size_t psk_len, + const u8 *data /* Z */, size_t data_len, + u8 *buf, size_t len /* X */) +{ + u8 *opos; + size_t i, n, hashlen, left, clen; + u8 ibuf[2], hash[SHA1_MAC_LEN]; + const u8 *addr[3]; + size_t vlen[3]; + + hashlen = SHA1_MAC_LEN; + /* M_i = Hash-Function (i || Y || Z); */ + addr[0] = ibuf; + vlen[0] = sizeof(ibuf); + addr[1] = psk; + vlen[1] = psk_len; + addr[2] = data; + vlen[2] = data_len; + + opos = buf; + left = len; + n = (len + hashlen - 1) / hashlen; + for (i = 1; i <= n; i++) { + WPA_PUT_BE16(ibuf, i); + sha1_vector(3, addr, vlen, hash); + clen = left > hashlen ? hashlen : left; + os_memcpy(opos, hash, clen); + opos += clen; + left -= clen; + } + + return 0; +} + + +static int eap_gpsk_derive_keys_aes(const u8 *psk, size_t psk_len, + const u8 *seed, size_t seed_len, + u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len, + u8 *pk, size_t *pk_len) +{ +#define EAP_GPSK_SK_LEN_AES 16 +#define EAP_GPSK_PK_LEN_AES 16 + u8 zero_string[1], mk[32], *pos, *data; + u8 kdf_out[EAP_MSK_LEN + EAP_EMSK_LEN + EAP_GPSK_SK_LEN_AES + + EAP_GPSK_PK_LEN_AES]; + size_t data_len; + + /* + * inputString = RAND_Client || ID_Client || RAND_Server || ID_Server + * (= seed) + * KS = 16, PL = psk_len, CSuite_Sel = 0x000000 0x000001 + * MK = GKDF-32 (0x00, PL || PSK || CSuite_Sel || inputString) + * MSK = GKDF-160 (MK, inputString)[0..63] + * EMSK = GKDF-160 (MK, inputString)[64..127] + * SK = GKDF-160 (MK, inputString)[128..143] + * PK = GKDF-160 (MK, inputString)[144..159] + * MID = GKDF-16(0x00, "Method ID" || EAP_Method_Type || CSuite_Sel || + * inputString) + * Hash-Function = SHA-1 (see [RFC3174]) + * hashlen = 20 octets (160 bits) + */ + + os_memset(zero_string, 0, sizeof(zero_string)); + + data_len = 2 + psk_len + 6 + seed_len; + data = os_malloc(data_len); + if (data == NULL) + return -1; + pos = data; + WPA_PUT_BE16(pos, psk_len); + pos += 2; + os_memcpy(pos, psk, psk_len); + pos += psk_len; + WPA_PUT_BE24(pos, 0); /* CSuite/Vendor = IETF */ + pos += 3; + WPA_PUT_BE24(pos, EAP_GPSK_CIPHER_AES); /* CSuite/Specifier */ + pos += 3; + os_memcpy(pos, seed, seed_len); /* inputString */ + wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: Data to MK derivation (AES)", + data, data_len); + + if (eap_gpsk_gkdf(zero_string, sizeof(zero_string), data, data_len, + mk, sizeof(mk)) < 0) { + os_free(data); + return -1; + } + os_free(data); + wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MK", mk, sizeof(mk)); + + if (eap_gpsk_gkdf(mk, sizeof(mk), seed, seed_len, + kdf_out, sizeof(kdf_out)) < 0) + return -1; + + pos = kdf_out; + wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MSK", pos, EAP_MSK_LEN); + os_memcpy(msk, pos, EAP_MSK_LEN); + pos += EAP_MSK_LEN; + + wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: EMSK", pos, EAP_EMSK_LEN); + os_memcpy(emsk, pos, EAP_EMSK_LEN); + pos += EAP_EMSK_LEN; + + wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: SK", pos, EAP_GPSK_SK_LEN_AES); + os_memcpy(sk, pos, EAP_GPSK_SK_LEN_AES); + *sk_len = EAP_GPSK_SK_LEN_AES; + pos += EAP_GPSK_SK_LEN_AES; + + wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PK", pos, EAP_GPSK_PK_LEN_AES); + os_memcpy(pk, pos, EAP_GPSK_PK_LEN_AES); + *pk_len = EAP_GPSK_PK_LEN_AES; + + return 0; +} + + +#ifdef EAP_GPSK_SHA256 +static int eap_gpsk_gkdf_sha256(const u8 *psk /* Y */, size_t psk_len, + const u8 *data /* Z */, size_t data_len, + u8 *buf, size_t len /* X */) +{ + u8 *opos; + size_t i, n, hashlen, left, clen; + u8 ibuf[2], hash[SHA256_MAC_LEN]; + const u8 *addr[3]; + size_t vlen[3]; + + hashlen = SHA256_MAC_LEN; + /* M_i = Hash-Function (i || Y || Z); */ + addr[0] = ibuf; + vlen[0] = sizeof(ibuf); + addr[1] = psk; + vlen[1] = psk_len; + addr[2] = data; + vlen[2] = data_len; + + opos = buf; + left = len; + n = (len + hashlen - 1) / hashlen; + for (i = 1; i <= n; i++) { + WPA_PUT_BE16(ibuf, i); + sha256_vector(3, addr, vlen, hash); + clen = left > hashlen ? hashlen : left; + os_memcpy(opos, hash, clen); + opos += clen; + left -= clen; + } + + return 0; +} + + +static int eap_gpsk_derive_keys_sha256(const u8 *psk, size_t psk_len, + const u8 *seed, size_t seed_len, + u8 *msk, u8 *emsk, + u8 *sk, size_t *sk_len, + u8 *pk, size_t *pk_len) +{ +#define EAP_GPSK_SK_LEN_SHA256 SHA256_MAC_LEN +#define EAP_GPSK_PK_LEN_SHA256 SHA256_MAC_LEN + u8 mk[SHA256_MAC_LEN], zero_string[1], *pos, *data; + u8 kdf_out[EAP_MSK_LEN + EAP_EMSK_LEN + EAP_GPSK_SK_LEN_SHA256 + + EAP_GPSK_PK_LEN_SHA256]; + size_t data_len; + + /* + * inputString = RAND_Client || ID_Client || RAND_Server || ID_Server + * (= seed) + * KS = 32, PL = psk_len, CSuite_Sel = 0x000000 0x000002 + * MK = GKDF-32 (0x00, PL || PSK || CSuite_Sel || inputString) + * MSK = GKDF-192 (MK, inputString)[0..63] + * EMSK = GKDF-192 (MK, inputString)[64..127] + * SK = GKDF-192 (MK, inputString)[128..159] + * PK = GKDF-192 (MK, inputString)[160..191] + * MID = GKDF-16(0x00, "Method ID" || EAP_Method_Type || CSuite_Sel || + * inputString) + * Hash-Function = SHA256 (see [RFC4634]) + * hashlen = 32 octets (256 bits) + */ + + os_memset(zero_string, 0, sizeof(zero_string)); + + data_len = 2 + psk_len + 6 + seed_len; + data = os_malloc(data_len); + if (data == NULL) + return -1; + pos = data; + WPA_PUT_BE16(pos, psk_len); + pos += 2; + os_memcpy(pos, psk, psk_len); + pos += psk_len; + WPA_PUT_BE24(pos, 0); /* CSuite/Vendor = IETF */ + pos += 3; + WPA_PUT_BE24(pos, EAP_GPSK_CIPHER_SHA256); /* CSuite/Specifier */ + pos += 3; + os_memcpy(pos, seed, seed_len); /* inputString */ + wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: Data to MK derivation (SHA256)", + data, data_len); + + if (eap_gpsk_gkdf_sha256(zero_string, sizeof(zero_string), + data, data_len, mk, sizeof(mk)) < 0) { + os_free(data); + return -1; + } + os_free(data); + wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MK", mk, sizeof(mk)); + + if (eap_gpsk_gkdf_sha256(mk, sizeof(mk), seed, seed_len, + kdf_out, sizeof(kdf_out)) < 0) + return -1; + + pos = kdf_out; + wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: MSK", pos, EAP_MSK_LEN); + os_memcpy(msk, pos, EAP_MSK_LEN); + pos += EAP_MSK_LEN; + + wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: EMSK", pos, EAP_EMSK_LEN); + os_memcpy(emsk, pos, EAP_EMSK_LEN); + pos += EAP_EMSK_LEN; + + wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: SK", + pos, EAP_GPSK_SK_LEN_SHA256); + os_memcpy(sk, pos, EAP_GPSK_SK_LEN_SHA256); + *sk_len = EAP_GPSK_SK_LEN_AES; + pos += EAP_GPSK_SK_LEN_AES; + + wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PK", + pos, EAP_GPSK_PK_LEN_SHA256); + os_memcpy(pk, pos, EAP_GPSK_PK_LEN_SHA256); + *pk_len = EAP_GPSK_PK_LEN_SHA256; + + return 0; +} +#endif /* EAP_GPSK_SHA256 */ + + +/** + * eap_gpsk_derive_keys - Derive EAP-GPSK keys + * @psk: Pre-shared key (at least 16 bytes if AES is used) + * @psk_len: Length of psk in bytes + * @vendor: CSuite/Vendor + * @specifier: CSuite/Specifier + * @rand_client: 32-byte RAND_Client + * @rand_server: 32-byte RAND_Server + * @id_client: ID_Client + * @id_client_len: Length of ID_Client + * @id_server: ID_Server + * @id_server_len: Length of ID_Server + * @msk: Buffer for 64-byte MSK + * @emsk: Buffer for 64-byte EMSK + * @sk: Buffer for SK (at least EAP_GPSK_MAX_SK_LEN bytes) + * @sk_len: Buffer for returning length of SK + * @pk: Buffer for SK (at least EAP_GPSK_MAX_PK_LEN bytes) + * @pk_len: Buffer for returning length of PK + * Returns: 0 on success, -1 on failure + */ +int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor, + int specifier, + const u8 *rand_client, const u8 *rand_server, + const u8 *id_client, size_t id_client_len, + const u8 *id_server, size_t id_server_len, + u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len, + u8 *pk, size_t *pk_len) +{ + u8 *seed, *pos; + size_t seed_len; + int ret; + + wpa_printf(MSG_DEBUG, "EAP-GPSK: Deriving keys (%d:%d)", + vendor, specifier); + + if (vendor != EAP_GPSK_VENDOR_IETF) + return -1; + + wpa_hexdump_key(MSG_DEBUG, "EAP-GPSK: PSK", psk, psk_len); + + /* Seed = RAND_Client || ID_Client || RAND_Server || ID_Server */ + seed_len = 2 * EAP_GPSK_RAND_LEN + id_server_len + id_client_len; + seed = os_malloc(seed_len); + if (seed == NULL) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Failed to allocate memory " + "for key derivation"); + return -1; + } + + pos = seed; + os_memcpy(pos, rand_client, EAP_GPSK_RAND_LEN); + pos += EAP_GPSK_RAND_LEN; + os_memcpy(pos, id_client, id_client_len); + pos += id_client_len; + os_memcpy(pos, rand_server, EAP_GPSK_RAND_LEN); + pos += EAP_GPSK_RAND_LEN; + os_memcpy(pos, id_server, id_server_len); + pos += id_server_len; + wpa_hexdump(MSG_DEBUG, "EAP-GPSK: Seed", seed, seed_len); + + switch (specifier) { + case EAP_GPSK_CIPHER_AES: + ret = eap_gpsk_derive_keys_aes(psk, psk_len, seed, seed_len, + msk, emsk, sk, sk_len, + pk, pk_len); + break; +#ifdef EAP_GPSK_SHA256 + case EAP_GPSK_CIPHER_SHA256: + ret = eap_gpsk_derive_keys_sha256(psk, psk_len, seed, seed_len, + msk, emsk, sk, sk_len, + pk, pk_len); + break; +#endif /* EAP_GPSK_SHA256 */ + default: + wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d:%d used in " + "key derivation", vendor, specifier); + ret = -1; + break; + } + + os_free(seed); + + return ret; +} + + +/** + * eap_gpsk_mic_len - Get the length of the MIC + * @vendor: CSuite/Vendor + * @specifier: CSuite/Specifier + * Returns: MIC length in bytes + */ +size_t eap_gpsk_mic_len(int vendor, int specifier) +{ + if (vendor != EAP_GPSK_VENDOR_IETF) + return 0; + + switch (specifier) { + case EAP_GPSK_CIPHER_AES: + return 16; +#ifdef EAP_GPSK_SHA256 + case EAP_GPSK_CIPHER_SHA256: + return 32; +#endif /* EAP_GPSK_SHA256 */ + default: + return 0; + } +} + + +static int eap_gpsk_compute_mic_aes(const u8 *sk, size_t sk_len, + const u8 *data, size_t len, u8 *mic) +{ + if (sk_len != 16) { + wpa_printf(MSG_DEBUG, "EAP-GPSK: Invalid SK length %d for " + "AES-CMAC MIC", sk_len); + return -1; + } + + return omac1_aes_128(sk, data, len, mic); +} + + +/** + * eap_gpsk_compute_mic - Compute EAP-GPSK MIC for an EAP packet + * @sk: Session key SK from eap_gpsk_derive_keys() + * @sk_len: SK length in bytes from eap_gpsk_derive_keys() + * @vendor: CSuite/Vendor + * @specifier: CSuite/Specifier + * @data: Input data to MIC + * @len: Input data length in bytes + * @mic: Buffer for the computed MIC, eap_gpsk_mic_len(cipher) bytes + * Returns: 0 on success, -1 on failure + */ +int eap_gpsk_compute_mic(const u8 *sk, size_t sk_len, int vendor, + int specifier, const u8 *data, size_t len, u8 *mic) +{ + int ret; + + if (vendor != EAP_GPSK_VENDOR_IETF) + return -1; + + switch (specifier) { + case EAP_GPSK_CIPHER_AES: + ret = eap_gpsk_compute_mic_aes(sk, sk_len, data, len, mic); + break; +#ifdef EAP_GPSK_SHA256 + case EAP_GPSK_CIPHER_SHA256: + hmac_sha256(sk, sk_len, data, len, mic); + ret = 0; + break; +#endif /* EAP_GPSK_SHA256 */ + default: + wpa_printf(MSG_DEBUG, "EAP-GPSK: Unknown cipher %d:%d used in " + "MIC computation", vendor, specifier); + ret = -1; + break; + } + + return ret; +} diff --git a/contrib/hostapd/eap_gpsk_common.h b/contrib/hostapd/eap_gpsk_common.h new file mode 100644 index 0000000..c806b7f --- /dev/null +++ b/contrib/hostapd/eap_gpsk_common.h @@ -0,0 +1,66 @@ +/* + * EAP server/peer: EAP-GPSK shared 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. + */ + +#ifndef EAP_GPSK_COMMON_H +#define EAP_GPSK_COMMON_H + +#define EAP_GPSK_OPCODE_GPSK_1 1 +#define EAP_GPSK_OPCODE_GPSK_2 2 +#define EAP_GPSK_OPCODE_GPSK_3 3 +#define EAP_GPSK_OPCODE_GPSK_4 4 +#define EAP_GPSK_OPCODE_FAIL 5 +#define EAP_GPSK_OPCODE_PROTECTED_FAIL 6 + +/* Failure-Code in GPSK-Fail and GPSK-Protected-Fail */ +#define EAP_GPSK_FAIL_PSK_NOT_FOUND 0x00000001 +#define EAP_GPSK_FAIL_AUTHENTICATION_FAILURE 0x00000002 +#define EAP_GPSK_FAIL_AUTHORIZATION_FAILURE 0x00000003 + +#define EAP_GPSK_RAND_LEN 32 +#define EAP_GPSK_MAX_SK_LEN 32 +#define EAP_GPSK_MAX_PK_LEN 32 +#define EAP_GPSK_MAX_MIC_LEN 32 + +#define EAP_GPSK_VENDOR_IETF 0x000000 +#define EAP_GPSK_CIPHER_RESERVED 0x000000 +#define EAP_GPSK_CIPHER_AES 0x000001 +#define EAP_GPSK_CIPHER_SHA256 0x000002 + + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct eap_gpsk_csuite { + u8 vendor[3]; + u8 specifier[3]; +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + +int eap_gpsk_supported_ciphersuite(int vendor, int specifier); +int eap_gpsk_derive_keys(const u8 *psk, size_t psk_len, int vendor, + int specifier, + const u8 *rand_client, const u8 *rand_server, + const u8 *id_client, size_t id_client_len, + const u8 *id_server, size_t id_server_len, + u8 *msk, u8 *emsk, u8 *sk, size_t *sk_len, + u8 *pk, size_t *pk_len); +size_t eap_gpsk_mic_len(int vendor, int specifier); +int eap_gpsk_compute_mic(const u8 *sk, size_t sk_len, int vendor, + int specifier, const u8 *data, size_t len, u8 *mic); + +#endif /* EAP_GPSK_COMMON_H */ diff --git a/contrib/hostapd/eap_gtc.c b/contrib/hostapd/eap_gtc.c index 674f837..42e4cd3 100644 --- a/contrib/hostapd/eap_gtc.c +++ b/contrib/hostapd/eap_gtc.c @@ -1,6 +1,6 @@ /* * hostapd / EAP-GTC (RFC 3748) - * Copyright (c) 2004, Jouni Malinen <jkmaline@cc.hut.fi> + * 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 @@ -12,10 +12,7 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <netinet/in.h> +#include "includes.h" #include "hostapd.h" #include "common.h" @@ -31,10 +28,9 @@ static void * eap_gtc_init(struct eap_sm *sm) { struct eap_gtc_data *data; - data = malloc(sizeof(*data)); + data = wpa_zalloc(sizeof(*data)); if (data == NULL) - return data; - memset(data, 0, sizeof(*data)); + return NULL; data->state = CONTINUE; return data; @@ -58,8 +54,8 @@ static u8 * eap_gtc_buildReq(struct eap_sm *sm, void *priv, int id, size_t msg_len; msg_len = strlen(msg); - *reqDataLen = sizeof(*req) + 1 + msg_len; - req = malloc(*reqDataLen); + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_GTC, reqDataLen, + msg_len, EAP_CODE_REQUEST, id, &pos); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-GTC: Failed to allocate memory for " "request"); @@ -67,11 +63,6 @@ static u8 * eap_gtc_buildReq(struct eap_sm *sm, void *priv, int id, return NULL; } - req->code = EAP_CODE_REQUEST; - req->identifier = id; - req->length = htons(*reqDataLen); - pos = (u8 *) (req + 1); - *pos++ = EAP_TYPE_GTC; memcpy(pos, msg, msg_len); data->state = CONTINUE; @@ -83,14 +74,12 @@ static u8 * eap_gtc_buildReq(struct eap_sm *sm, void *priv, int id, static Boolean eap_gtc_check(struct eap_sm *sm, void *priv, u8 *respData, size_t respDataLen) { - 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_GTC || - (len = ntohs(resp->length)) > respDataLen) { + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_GTC, + respData, respDataLen, &len); + if (pos == NULL || len < 1) { wpa_printf(MSG_INFO, "EAP-GTC: Invalid frame"); return TRUE; } @@ -103,20 +92,22 @@ static void eap_gtc_process(struct eap_sm *sm, void *priv, u8 *respData, size_t respDataLen) { struct eap_gtc_data *data = priv; - struct eap_hdr *resp; - u8 *pos; + const u8 *pos; size_t rlen; - if (sm->user == NULL || sm->user->password == NULL) { - wpa_printf(MSG_INFO, "EAP-GTC: Password not configured"); + if (sm->user == NULL || sm->user->password == NULL || + sm->user->password_hash) { + wpa_printf(MSG_INFO, "EAP-GTC: Plaintext password not " + "configured"); data->state = FAILURE; return; } - resp = (struct eap_hdr *) respData; - pos = (u8 *) (resp + 1); - pos++; - rlen = ntohs(resp->length) - sizeof(*resp) - 1; + 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 || @@ -144,15 +135,26 @@ static Boolean eap_gtc_isSuccess(struct eap_sm *sm, void *priv) } -const struct eap_method eap_method_gtc = +int eap_server_gtc_register(void) { - .method = EAP_TYPE_GTC, - .name = "GTC", - .init = eap_gtc_init, - .reset = eap_gtc_reset, - .buildReq = eap_gtc_buildReq, - .check = eap_gtc_check, - .process = eap_gtc_process, - .isDone = eap_gtc_isDone, - .isSuccess = eap_gtc_isSuccess, -}; + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_GTC, "GTC"); + if (eap == NULL) + return -1; + + eap->init = eap_gtc_init; + eap->reset = eap_gtc_reset; + eap->buildReq = eap_gtc_buildReq; + eap->check = eap_gtc_check; + eap->process = eap_gtc_process; + eap->isDone = eap_gtc_isDone; + eap->isSuccess = eap_gtc_isSuccess; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/contrib/hostapd/eap_i.h b/contrib/hostapd/eap_i.h index 4e803f9..85b2c2d 100644 --- a/contrib/hostapd/eap_i.h +++ b/contrib/hostapd/eap_i.h @@ -1,11 +1,32 @@ +/* + * hostapd / EAP Authenticator state machine internal structures (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_I_H #define EAP_I_H #include "eap.h" -/* draft-ietf-eap-statemachine-05.pdf - EAP Standalone Authenticator */ +/* RFC 4137 - EAP Standalone Authenticator */ +/** + * struct eap_method - EAP method interface + * This structure defines the EAP method interface. Each method will need to + * register its own EAP type, EAP name, and set of function pointers for method + * specific operations. This interface is based on section 5.4 of RFC 4137. + */ struct eap_method { + int vendor; EapType method; const char *name; @@ -25,8 +46,58 @@ struct eap_method { /* isSuccess is not specified in draft-ietf-eap-statemachine-05.txt, * but it is useful in implementing Policy.getDecision() */ Boolean (*isSuccess)(struct eap_sm *sm, void *priv); + + /** + * free - Free EAP method data + * @method: Pointer to the method data registered with + * eap_server_method_register(). + * + * This function will be called when the EAP method is being + * unregistered. If the EAP method allocated resources during + * registration (e.g., allocated struct eap_method), they should be + * freed in this function. No other method functions will be called + * after this call. If this function is not defined (i.e., function + * pointer is %NULL), a default handler is used to release the method + * data with free(method). This is suitable for most cases. + */ + void (*free)(struct eap_method *method); + +#define EAP_SERVER_METHOD_INTERFACE_VERSION 1 + /** + * version - Version of the EAP server method interface + * + * The EAP server method implementation should set this variable to + * EAP_SERVER_METHOD_INTERFACE_VERSION. This is used to verify that the + * EAP method is using supported API version when using dynamically + * loadable EAP methods. + */ + int version; + + /** + * next - Pointer to the next EAP method + * + * This variable is used internally in the EAP method registration code + * to create a linked list of registered EAP methods. + */ + struct eap_method *next; + + /** + * get_emsk - Get EAP method specific keying extended material (EMSK) + * @sm: Pointer to EAP state machine allocated with eap_sm_init() + * @priv: Pointer to private EAP method data from eap_method::init() + * @len: Pointer to a variable to store EMSK length + * Returns: EMSK or %NULL if not available + * + * This function can be used to get the extended keying material from + * the EAP method. The key may already be stored in the method-specific + * private data or this function may derive the key. + */ + u8 * (*get_emsk)(struct eap_sm *sm, void *priv, size_t *len); }; +/** + * struct eap_sm - EAP server state machine data + */ struct eap_sm { enum { EAP_DISABLED, EAP_INITIALIZE, EAP_IDLE, EAP_RECEIVED, @@ -77,6 +148,8 @@ struct eap_sm { Boolean rxResp; int respId; EapType respMethod; + int respVendor; + u32 respVendorMethod; Boolean ignore; enum { DECISION_SUCCESS, DECISION_FAILURE, DECISION_CONTINUE @@ -102,11 +175,18 @@ struct eap_sm { Boolean update_user; int num_rounds; + enum { + METHOD_PENDING_NONE, METHOD_PENDING_WAIT, METHOD_PENDING_CONT + } method_pending; }; -const struct eap_method * eap_sm_get_eap_methods(int method); 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); #endif /* EAP_I_H */ diff --git a/contrib/hostapd/eap_identity.c b/contrib/hostapd/eap_identity.c index 54efc47..ab6f26b 100644 --- a/contrib/hostapd/eap_identity.c +++ b/contrib/hostapd/eap_identity.c @@ -1,6 +1,6 @@ /* * hostapd / EAP-Identity - * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * 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 @@ -12,10 +12,7 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <netinet/in.h> +#include "includes.h" #include "hostapd.h" #include "common.h" @@ -32,10 +29,9 @@ static void * eap_identity_init(struct eap_sm *sm) { struct eap_identity_data *data; - data = malloc(sizeof(*data)); + data = wpa_zalloc(sizeof(*data)); if (data == NULL) - return data; - memset(data, 0, sizeof(*data)); + return NULL; data->state = CONTINUE; return data; @@ -61,7 +57,7 @@ static void eap_identity_reset(struct eap_sm *sm, void *priv) static u8 * eap_identity_buildReq(struct eap_sm *sm, void *priv, int id, - size_t *reqDataLen) + size_t *reqDataLen) { struct eap_identity_data *data = priv; struct eap_hdr *req; @@ -76,8 +72,8 @@ static u8 * eap_identity_buildReq(struct eap_sm *sm, void *priv, int id, req_data = NULL; req_data_len = 0; } - *reqDataLen = sizeof(*req) + 1 + req_data_len; - req = malloc(*reqDataLen); + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, reqDataLen, + req_data_len, EAP_CODE_REQUEST, id, &pos); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-Identity: Failed to allocate " "memory for request"); @@ -85,11 +81,6 @@ static u8 * eap_identity_buildReq(struct eap_sm *sm, void *priv, int id, return NULL; } - req->code = EAP_CODE_REQUEST; - req->identifier = id; - req->length = htons(*reqDataLen); - pos = (u8 *) (req + 1); - *pos++ = EAP_TYPE_IDENTITY; if (req_data) memcpy(pos, req_data, req_data_len); @@ -100,14 +91,12 @@ static u8 * eap_identity_buildReq(struct eap_sm *sm, void *priv, int id, static Boolean eap_identity_check(struct eap_sm *sm, void *priv, u8 *respData, size_t respDataLen) { - 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) + 1 || *pos != EAP_TYPE_IDENTITY || - (len = ntohs(resp->length)) > respDataLen) { + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, + respData, respDataLen, &len); + if (pos == NULL) { wpa_printf(MSG_INFO, "EAP-Identity: Invalid frame"); return TRUE; } @@ -120,9 +109,8 @@ static void eap_identity_process(struct eap_sm *sm, void *priv, u8 *respData, size_t respDataLen) { struct eap_identity_data *data = priv; - struct eap_hdr *resp; - u8 *pos; - int len; + const u8 *pos; + size_t len; if (data->pick_up) { if (eap_identity_check(sm, data, respData, respDataLen)) { @@ -134,15 +122,11 @@ static void eap_identity_process(struct eap_sm *sm, void *priv, data->pick_up = 0; } - resp = (struct eap_hdr *) respData; - len = ntohs(resp->length); - pos = (u8 *) (resp + 1); - pos++; - len -= sizeof(*resp) + 1; - if (len < 0) { - data->state = FAILURE; - return; - } + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, + respData, respDataLen, &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); @@ -170,16 +154,28 @@ static Boolean eap_identity_isSuccess(struct eap_sm *sm, void *priv) } -const struct eap_method eap_method_identity = +int eap_server_identity_register(void) { - .method = EAP_TYPE_IDENTITY, - .name = "Identity", - .init = eap_identity_init, - .initPickUp = eap_identity_initPickUp, - .reset = eap_identity_reset, - .buildReq = eap_identity_buildReq, - .check = eap_identity_check, - .process = eap_identity_process, - .isDone = eap_identity_isDone, - .isSuccess = eap_identity_isSuccess, -}; + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_IDENTITY, + "Identity"); + if (eap == NULL) + return -1; + + eap->init = eap_identity_init; + eap->initPickUp = eap_identity_initPickUp; + eap->reset = eap_identity_reset; + eap->buildReq = eap_identity_buildReq; + eap->check = eap_identity_check; + eap->process = eap_identity_process; + eap->isDone = eap_identity_isDone; + eap->isSuccess = eap_identity_isSuccess; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/contrib/hostapd/eap_md5.c b/contrib/hostapd/eap_md5.c index d776c8c..234a319 100644 --- a/contrib/hostapd/eap_md5.c +++ b/contrib/hostapd/eap_md5.c @@ -1,6 +1,6 @@ /* * hostapd / EAP-MD5 server - * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * 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 @@ -12,10 +12,7 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <netinet/in.h> +#include "includes.h" #include "hostapd.h" #include "common.h" @@ -36,10 +33,9 @@ static void * eap_md5_init(struct eap_sm *sm) { struct eap_md5_data *data; - data = malloc(sizeof(*data)); + data = wpa_zalloc(sizeof(*data)); if (data == NULL) - return data; - memset(data, 0, sizeof(*data)); + return NULL; data->state = CONTINUE; return data; @@ -66,8 +62,8 @@ static u8 * eap_md5_buildReq(struct eap_sm *sm, void *priv, int id, return NULL; } - *reqDataLen = sizeof(*req) + 2 + CHALLENGE_LEN; - req = malloc(*reqDataLen); + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MD5, reqDataLen, + 1 + CHALLENGE_LEN, EAP_CODE_REQUEST, id, &pos); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-MD5: Failed to allocate memory for " "request"); @@ -75,11 +71,6 @@ static u8 * eap_md5_buildReq(struct eap_sm *sm, void *priv, int id, return NULL; } - req->code = EAP_CODE_REQUEST; - req->identifier = id; - req->length = htons(*reqDataLen); - pos = (u8 *) (req + 1); - *pos++ = EAP_TYPE_MD5; *pos++ = CHALLENGE_LEN; memcpy(pos, data->challenge, CHALLENGE_LEN); wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Challenge", pos, CHALLENGE_LEN); @@ -93,23 +84,19 @@ static u8 * eap_md5_buildReq(struct eap_sm *sm, void *priv, int id, static Boolean eap_md5_check(struct eap_sm *sm, void *priv, u8 *respData, size_t respDataLen) { - 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_MD5 || - (len = ntohs(resp->length)) > respDataLen) { + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MD5, + respData, respDataLen, &len); + if (pos == NULL || len < 1) { wpa_printf(MSG_INFO, "EAP-MD5: Invalid frame"); return TRUE; } - pos++; - if (*pos != MD5_MAC_LEN || - sizeof(*resp) + 2 + MD5_MAC_LEN > len) { + if (*pos != MD5_MAC_LEN || 1 + MD5_MAC_LEN > len) { wpa_printf(MSG_INFO, "EAP-MD5: Invalid response " - "(response_len=%d respDataLen=%lu", - *pos, (unsigned long) respDataLen); + "(response_len=%d payload_len=%lu", + *pos, (unsigned long) len); return TRUE; } @@ -122,22 +109,28 @@ static void eap_md5_process(struct eap_sm *sm, void *priv, { struct eap_md5_data *data = priv; struct eap_hdr *resp; - u8 *pos; + const u8 *pos; const u8 *addr[3]; - size_t len[3]; + size_t len[3], plen; u8 hash[MD5_MAC_LEN]; - if (sm->user == NULL || sm->user->password == NULL) { - wpa_printf(MSG_INFO, "EAP-MD5: Password not configured"); + if (sm->user == NULL || sm->user->password == NULL || + sm->user->password_hash) { + wpa_printf(MSG_INFO, "EAP-MD5: Plaintext password not " + "configured"); data->state = FAILURE; return; } - resp = (struct eap_hdr *) respData; - pos = (u8 *) (resp + 1); - pos += 2; /* Skip type and len */ + 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) + 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; @@ -170,15 +163,26 @@ static Boolean eap_md5_isSuccess(struct eap_sm *sm, void *priv) } -const struct eap_method eap_method_md5 = +int eap_server_md5_register(void) { - .method = EAP_TYPE_MD5, - .name = "MD5", - .init = eap_md5_init, - .reset = eap_md5_reset, - .buildReq = eap_md5_buildReq, - .check = eap_md5_check, - .process = eap_md5_process, - .isDone = eap_md5_isDone, - .isSuccess = eap_md5_isSuccess, -}; + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_MD5, "MD5"); + if (eap == NULL) + return -1; + + eap->init = eap_md5_init; + eap->reset = eap_md5_reset; + eap->buildReq = eap_md5_buildReq; + eap->check = eap_md5_check; + eap->process = eap_md5_process; + eap->isDone = eap_md5_isDone; + eap->isSuccess = eap_md5_isSuccess; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/contrib/hostapd/eap_methods.c b/contrib/hostapd/eap_methods.c new file mode 100644 index 0000000..671d548 --- /dev/null +++ b/contrib/hostapd/eap_methods.c @@ -0,0 +1,273 @@ +/* + * hostapd / EAP method registration + * 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 "eap_i.h" +#include "eap_methods.h" + + +static struct eap_method *eap_methods; + + +/** + * eap_sm_get_eap_methods - 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) +{ + struct eap_method *m; + for (m = eap_methods; m; m = m->next) { + if (m->vendor == vendor && m->method == method) + return m; + } + return NULL; +} + + +/** + * eap_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_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) { + *vendor = m->vendor; + return m->method; + } + } + *vendor = EAP_VENDOR_IETF; + return EAP_TYPE_NONE; +} + + +/** + * eap_server_method_alloc - Allocate EAP server method structure + * @version: Version of the EAP server method interface (set to + * EAP_SERVER_METHOD_INTERFACE_VERSION) + * @vendor: EAP Vendor-ID (EAP_VENDOR_*) (0 = IETF) + * @method: EAP type number (EAP_TYPE_*) + * name: Name of the method (e.g., "TLS") + * Returns: Allocated EAP method structure or %NULL on failure + * + * The returned structure should be freed with eap_server_method_free() when it + * is not needed anymore. + */ +struct eap_method * eap_server_method_alloc(int version, int vendor, + EapType method, const char *name) +{ + struct eap_method *eap; + eap = wpa_zalloc(sizeof(*eap)); + if (eap == NULL) + return NULL; + eap->version = version; + eap->vendor = vendor; + eap->method = method; + eap->name = name; + return eap; +} + + +/** + * eap_server_method_free - Free EAP server method structure + * @method: Method structure allocated with eap_server_method_alloc() + */ +void eap_server_method_free(struct eap_method *method) +{ + free(method); +} + + +/** + * eap_server_method_register - Register an EAP server method + * @method: EAP method to register + * Returns: 0 on success, -1 on invalid method, or -2 if a matching EAP method + * has already been registered + * + * Each EAP server method needs to call this function to register itself as a + * supported EAP method. + */ +int eap_server_method_register(struct eap_method *method) +{ + struct eap_method *m, *last = NULL; + + if (method == NULL || method->name == NULL || + method->version != EAP_SERVER_METHOD_INTERFACE_VERSION) + return -1; + + for (m = eap_methods; m; m = m->next) { + if ((m->vendor == method->vendor && + m->method == method->method) || + strcmp(m->name, method->name) == 0) + return -2; + last = m; + } + + if (last) + last->next = method; + else + eap_methods = method; + + return 0; +} + + +/** + * eap_server_register_methods - Register statically linked EAP server methods + * Returns: 0 on success, -1 on failure + * + * This function is called at program initialization to register all EAP server + * methods that were linked in statically. + */ +int eap_server_register_methods(void) +{ + int ret = 0; + + if (ret == 0) { + int eap_server_identity_register(void); + ret = eap_server_identity_register(); + } + +#ifdef EAP_MD5 + if (ret == 0) { + int eap_server_md5_register(void); + ret = eap_server_md5_register(); + } +#endif /* EAP_MD5 */ + +#ifdef EAP_TLS + if (ret == 0) { + int eap_server_tls_register(void); + ret = eap_server_tls_register(); + } +#endif /* EAP_TLS */ + +#ifdef EAP_MSCHAPv2 + if (ret == 0) { + int eap_server_mschapv2_register(void); + ret = eap_server_mschapv2_register(); + } +#endif /* EAP_MSCHAPv2 */ + +#ifdef EAP_PEAP + if (ret == 0) { + int eap_server_peap_register(void); + ret = eap_server_peap_register(); + } +#endif /* EAP_PEAP */ + +#ifdef EAP_TLV + if (ret == 0) { + int eap_server_tlv_register(void); + ret = eap_server_tlv_register(); + } +#endif /* EAP_TLV */ + +#ifdef EAP_GTC + if (ret == 0) { + int eap_server_gtc_register(void); + ret = eap_server_gtc_register(); + } +#endif /* EAP_GTC */ + +#ifdef EAP_TTLS + if (ret == 0) { + int eap_server_ttls_register(void); + ret = eap_server_ttls_register(); + } +#endif /* EAP_TTLS */ + +#ifdef EAP_SIM + if (ret == 0) { + int eap_server_sim_register(void); + ret = eap_server_sim_register(); + } +#endif /* EAP_SIM */ + +#ifdef EAP_AKA + if (ret == 0) { + int eap_server_aka_register(void); + ret = eap_server_aka_register(); + } +#endif /* EAP_AKA */ + +#ifdef EAP_PAX + if (ret == 0) { + int eap_server_pax_register(void); + ret = eap_server_pax_register(); + } +#endif /* EAP_PAX */ + +#ifdef EAP_PSK + if (ret == 0) { + int eap_server_psk_register(void); + ret = eap_server_psk_register(); + } +#endif /* EAP_PSK */ + +#ifdef EAP_SAKE + if (ret == 0) { + int eap_server_sake_register(void); + ret = eap_server_sake_register(); + } +#endif /* EAP_SAKE */ + +#ifdef EAP_GPSK + if (ret == 0) { + int eap_server_gpsk_register(void); + ret = eap_server_gpsk_register(); + } +#endif /* EAP_GPSK */ + +#ifdef EAP_VENDOR_TEST + if (ret == 0) { + int eap_server_vendor_test_register(void); + ret = eap_server_vendor_test_register(); + } +#endif /* EAP_VENDOR_TEST */ + + return ret; +} + + +/** + * eap_server_unregister_methods - Unregister EAP server methods + * + * This function is called at program termination to unregister all EAP server + * methods. + */ +void eap_server_unregister_methods(void) +{ + struct eap_method *m; + + while (eap_methods) { + m = eap_methods; + eap_methods = eap_methods->next; + + if (m->free) + m->free(m); + else + eap_server_method_free(m); + } +} diff --git a/contrib/hostapd/eap_methods.h b/contrib/hostapd/eap_methods.h new file mode 100644 index 0000000..cec8a57 --- /dev/null +++ b/contrib/hostapd/eap_methods.h @@ -0,0 +1,49 @@ +/* + * hostapd / EAP method registration + * 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_METHODS_H +#define EAP_METHODS_H + +const struct eap_method * eap_sm_get_eap_methods(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); +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/contrib/hostapd/eap_mschapv2.c index 5cbf6eb..bbd819b 100644 --- a/contrib/hostapd/eap_mschapv2.c +++ b/contrib/hostapd/eap_mschapv2.c @@ -1,6 +1,6 @@ /* * hostapd / EAP-MSCHAPv2 (draft-kamath-pppext-eap-mschapv2-00.txt) server - * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.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 @@ -12,10 +12,7 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <netinet/in.h> +#include "includes.h" #include "hostapd.h" #include "common.h" @@ -24,16 +21,12 @@ struct eap_mschapv2_hdr { - u8 code; - u8 identifier; - u16 length; /* including code, identifier, and length */ - u8 type; /* EAP_TYPE_MSCHAPV2 */ u8 op_code; /* MSCHAPV2_OP_* */ u8 mschapv2_id; /* must be changed for challenges, but not for * success/failure */ u8 ms_length[2]; /* Note: misaligned; length - 5 */ /* followed by data */ -} __attribute__ ((packed)); +} STRUCT_PACKED; #define MSCHAPV2_OP_CHALLENGE 1 #define MSCHAPV2_OP_RESPONSE 2 @@ -51,6 +44,7 @@ struct eap_mschapv2_hdr { #define ERROR_CHANGING_PASSWORD 709 #define PASSWD_CHANGE_CHAL_LEN 16 +#define MSCHAPV2_KEY_LEN 16 #define CHALLENGE_LEN 16 @@ -60,6 +54,8 @@ struct eap_mschapv2_data { u8 auth_response[20]; enum { CHALLENGE, SUCCESS_REQ, FAILURE_REQ, SUCCESS, FAILURE } state; u8 resp_mschapv2_id; + u8 master_key[16]; + int master_key_valid; }; @@ -67,10 +63,9 @@ static void * eap_mschapv2_init(struct eap_sm *sm) { struct eap_mschapv2_data *data; - data = malloc(sizeof(*data)); + data = wpa_zalloc(sizeof(*data)); if (data == NULL) - return data; - memset(data, 0, sizeof(*data)); + return NULL; data->state = CHALLENGE; return data; @@ -88,9 +83,11 @@ static u8 * eap_mschapv2_build_challenge(struct eap_sm *sm, struct eap_mschapv2_data *data, int id, size_t *reqDataLen) { - struct eap_mschapv2_hdr *req; + struct eap_hdr *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)) { wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to get random " @@ -99,8 +96,9 @@ static u8 * eap_mschapv2_build_challenge(struct eap_sm *sm, return NULL; } - *reqDataLen = sizeof(*req) + 1 + CHALLENGE_LEN + strlen(name); - req = malloc(*reqDataLen); + 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); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" " for request"); @@ -108,15 +106,12 @@ static u8 * eap_mschapv2_build_challenge(struct eap_sm *sm, return NULL; } - req->code = EAP_CODE_REQUEST; - req->identifier = id; - req->length = htons(*reqDataLen); - req->type = EAP_TYPE_MSCHAPV2; - req->op_code = MSCHAPV2_OP_CHALLENGE; - req->mschapv2_id = id; - req->ms_length[0] = (*reqDataLen - 5) >> 8; - req->ms_length[1] = (*reqDataLen - 5) & 0xff; - pos = (u8 *) (req + 1); + ms = (struct eap_mschapv2_hdr *) pos; + 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, @@ -132,15 +127,16 @@ static u8 * eap_mschapv2_build_success_req(struct eap_sm *sm, struct eap_mschapv2_data *data, int id, size_t *reqDataLen) { - struct eap_mschapv2_hdr *req; - u8 *pos, *msg, *end; + struct eap_hdr *req; + struct eap_mschapv2_hdr *ms; + u8 *pos, *msg; char *message = "OK"; - size_t msg_len; - int i; + size_t ms_len; - msg_len = 2 + 2 * sizeof(data->auth_response) + 3 + strlen(message); - *reqDataLen = sizeof(*req) + msg_len; - req = malloc(*reqDataLen + 1); + 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); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" " for request"); @@ -148,27 +144,25 @@ static u8 * eap_mschapv2_build_success_req(struct eap_sm *sm, return NULL; } - req->code = EAP_CODE_REQUEST; - req->identifier = id; - req->length = htons(*reqDataLen); - req->type = EAP_TYPE_MSCHAPV2; - req->op_code = MSCHAPV2_OP_SUCCESS; - req->mschapv2_id = data->resp_mschapv2_id; - req->ms_length[0] = (*reqDataLen - 5) >> 8; - req->ms_length[1] = (*reqDataLen - 5) & 0xff; - - msg = pos = (u8 *) (req + 1); - end = ((u8 *) req) + *reqDataLen + 1; - - pos += snprintf((char *) pos, end - pos, "S="); - for (i = 0; i < sizeof(data->auth_response); i++) { - pos += snprintf((char *) pos, end - pos, "%02X", - data->auth_response[i]); - } - pos += snprintf((char *) pos, end - pos, " M=%s", message); + ms = (struct eap_mschapv2_hdr *) pos; + 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)); wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Success Request Message", - msg, msg_len); + msg, ms_len - sizeof(*ms)); return (u8 *) req; } @@ -178,15 +172,16 @@ static u8 * eap_mschapv2_build_failure_req(struct eap_sm *sm, struct eap_mschapv2_data *data, int id, size_t *reqDataLen) { - struct eap_mschapv2_hdr *req; + struct eap_hdr *req; + struct eap_mschapv2_hdr *ms; u8 *pos; char *message = "E=691 R=0 C=00000000000000000000000000000000 V=3 " "M=FAILED"; - size_t msg_len; + size_t ms_len; - msg_len = strlen(message); - *reqDataLen = sizeof(*req) + msg_len; - req = malloc(*reqDataLen + 1); + ms_len = sizeof(*ms) + strlen(message); + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, reqDataLen, + ms_len, EAP_CODE_REQUEST, id, &pos); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to allocate memory" " for request"); @@ -194,20 +189,15 @@ static u8 * eap_mschapv2_build_failure_req(struct eap_sm *sm, return NULL; } - req->code = EAP_CODE_REQUEST; - req->identifier = id; - req->length = htons(*reqDataLen); - req->type = EAP_TYPE_MSCHAPV2; - req->op_code = MSCHAPV2_OP_FAILURE; - req->mschapv2_id = data->resp_mschapv2_id; - req->ms_length[0] = (*reqDataLen - 5) >> 8; - req->ms_length[1] = (*reqDataLen - 5) & 0xff; + ms = (struct eap_mschapv2_hdr *) pos; + ms->op_code = MSCHAPV2_OP_FAILURE; + ms->mschapv2_id = data->resp_mschapv2_id; + WPA_PUT_BE16(ms->ms_length, ms_len); - pos = (u8 *) (req + 1); - memcpy(pos, message, msg_len); + memcpy((u8 *) (ms + 1), message, strlen(message)); wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: Failure Request Message", - (u8 *) message, msg_len); + (u8 *) message, strlen(message)); return (u8 *) req; } @@ -241,17 +231,17 @@ static Boolean eap_mschapv2_check(struct eap_sm *sm, void *priv, { struct eap_mschapv2_data *data = priv; struct eap_mschapv2_hdr *resp; - u8 *pos; + const u8 *pos; size_t len; - resp = (struct eap_mschapv2_hdr *) respData; - pos = (u8 *) (resp + 1); - if (respDataLen < 6 || resp->type != EAP_TYPE_MSCHAPV2 || - (len = ntohs(resp->length)) > respDataLen) { + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, + respData, respDataLen, &len); + if (pos == NULL || len < 1) { wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Invalid frame"); return TRUE; } + resp = (struct eap_mschapv2_hdr *) pos; if (data->state == CHALLENGE && resp->op_code != MSCHAPV2_OP_RESPONSE) { wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Expected Response - " @@ -283,18 +273,23 @@ static void eap_mschapv2_process_response(struct eap_sm *sm, u8 *respData, size_t respDataLen) { struct eap_mschapv2_hdr *resp; - u8 *pos; - u8 *peer_challenge, *nt_response, flags, *name; - size_t name_len; + const u8 *pos, *end, *peer_challenge, *nt_response, *name; + u8 flags; + size_t len, name_len, i; u8 expected[24]; - int i; - u8 *username, *user; + const u8 *username, *user; size_t username_len, user_len; - resp = (struct eap_mschapv2_hdr *) respData; + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, + respData, respDataLen, &len); + if (pos == NULL || len < 1) + return; /* Should not happen - frame already validated */ + + end = pos + len; + resp = (struct eap_mschapv2_hdr *) pos; pos = (u8 *) (resp + 1); - if (respDataLen < sizeof(resp) + 1 + 49 || + if (len < sizeof(*resp) + 1 + 49 || resp->op_code != MSCHAPV2_OP_RESPONSE || pos[0] != 49) { wpa_hexdump(MSG_DEBUG, "EAP-MSCHAPV2: Invalid response", @@ -310,7 +305,7 @@ static void eap_mschapv2_process_response(struct eap_sm *sm, pos += 24; flags = *pos++; name = pos; - name_len = respData + respDataLen - name; + name_len = end - name; wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Peer-Challenge", peer_challenge, 16); @@ -355,26 +350,55 @@ static void eap_mschapv2_process_response(struct eap_sm *sm, wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-MSCHAPV2: User name", username, username_len); - generate_nt_response(data->auth_challenge, peer_challenge, - username, username_len, - sm->user->password, sm->user->password_len, - expected); + if (sm->user->password_hash) { + generate_nt_response_pwhash(data->auth_challenge, + peer_challenge, + username, username_len, + sm->user->password, + expected); + } else { + generate_nt_response(data->auth_challenge, peer_challenge, + username, username_len, + sm->user->password, + sm->user->password_len, + expected); + } if (memcmp(nt_response, expected, 24) == 0) { + const u8 *pw_hash; + u8 pw_hash_buf[16], pw_hash_hash[16]; + wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Correct NT-Response"); data->state = SUCCESS_REQ; - /* Authenticator response is not really needed yet, but * calculate it here so that peer_challenge and username need * not be saved. */ - generate_authenticator_response(sm->user->password, - sm->user->password_len, - peer_challenge, - data->auth_challenge, - username, username_len, - nt_response, - data->auth_response); + 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); + } + + hash_nt_password_hash(pw_hash, pw_hash_hash); + get_master_key(pw_hash_hash, nt_response, data->master_key); + data->master_key_valid = 1; + wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived Master Key", + data->master_key, MSCHAPV2_KEY_LEN); } else { wpa_hexdump(MSG_MSGDUMP, "EAP-MSCHAPV2: Expected NT-Response", expected, 24); @@ -389,8 +413,15 @@ static void eap_mschapv2_process_success_resp(struct eap_sm *sm, u8 *respData, size_t respDataLen) { struct eap_mschapv2_hdr *resp; + const u8 *pos; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, + respData, respDataLen, &len); + if (pos == NULL || len < 1) + return; /* Should not happen - frame already validated */ - resp = (struct eap_mschapv2_hdr *) respData; + resp = (struct eap_mschapv2_hdr *) pos; if (resp->op_code == MSCHAPV2_OP_SUCCESS) { wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Success Response" @@ -409,8 +440,15 @@ static void eap_mschapv2_process_failure_resp(struct eap_sm *sm, u8 *respData, size_t respDataLen) { struct eap_mschapv2_hdr *resp; + const u8 *pos; + size_t len; + + pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, + respData, respDataLen, &len); + if (pos == NULL || len < 1) + return; /* Should not happen - frame already validated */ - resp = (struct eap_mschapv2_hdr *) respData; + resp = (struct eap_mschapv2_hdr *) pos; if (resp->op_code == MSCHAPV2_OP_FAILURE) { wpa_printf(MSG_DEBUG, "EAP-MSCHAPV2: Received Failure Response" @@ -462,6 +500,27 @@ static Boolean eap_mschapv2_isDone(struct eap_sm *sm, void *priv) } +static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_mschapv2_data *data = priv; + u8 *key; + + if (data->state != SUCCESS || !data->master_key_valid) + return NULL; + + *len = 2 * MSCHAPV2_KEY_LEN; + key = malloc(*len); + if (key == NULL) + return NULL; + get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0, 0); + get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN, + MSCHAPV2_KEY_LEN, 1, 0); + wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, *len); + + return key; +} + + static Boolean eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv) { struct eap_mschapv2_data *data = priv; @@ -469,15 +528,28 @@ static Boolean eap_mschapv2_isSuccess(struct eap_sm *sm, void *priv) } -const struct eap_method eap_method_mschapv2 = +int eap_server_mschapv2_register(void) { - .method = EAP_TYPE_MSCHAPV2, - .name = "MSCHAPV2", - .init = eap_mschapv2_init, - .reset = eap_mschapv2_reset, - .buildReq = eap_mschapv2_buildReq, - .check = eap_mschapv2_check, - .process = eap_mschapv2_process, - .isDone = eap_mschapv2_isDone, - .isSuccess = eap_mschapv2_isSuccess, -}; + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, + "MSCHAPV2"); + if (eap == NULL) + return -1; + + eap->init = eap_mschapv2_init; + eap->reset = eap_mschapv2_reset; + eap->buildReq = eap_mschapv2_buildReq; + eap->check = eap_mschapv2_check; + eap->process = eap_mschapv2_process; + eap->isDone = eap_mschapv2_isDone; + eap->getKey = eap_mschapv2_getKey; + eap->isSuccess = eap_mschapv2_isSuccess; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/contrib/hostapd/eap_pax.c b/contrib/hostapd/eap_pax.c index 2fbec87..8daa8d3 100644 --- a/contrib/hostapd/eap_pax.c +++ b/contrib/hostapd/eap_pax.c @@ -1,6 +1,6 @@ /* - * hostapd / EAP-PAX (draft-clancy-eap-pax-04.txt) server - * Copyright (c) 2005, Jouni Malinen <jkmaline@cc.hut.fi> + * hostapd / EAP-PAX (RFC 4746) server + * 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 @@ -12,10 +12,7 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <netinet/in.h> +#include "includes.h" #include "hostapd.h" #include "common.h" @@ -24,6 +21,11 @@ /* * 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 { @@ -50,13 +52,12 @@ static void * eap_pax_init(struct eap_sm *sm) { struct eap_pax_data *data; - data = malloc(sizeof(*data)); + data = wpa_zalloc(sizeof(*data)); if (data == NULL) - return data; - memset(data, 0, sizeof(*data)); + return NULL; data->state = PAX_STD_1; /* - * TODO: make this configurable once EAP_PAX_MAC_AES_CBC_MAC_128 is + * TODO: make this configurable once EAP_PAX_HMAC_SHA256_128 is * supported */ data->mac_id = EAP_PAX_MAC_HMAC_SHA1_128; @@ -161,6 +162,8 @@ static u8 * eap_pax_build_std_3(struct eap_sm *sm, pos, EAP_PAX_MAC_LEN); pos += EAP_PAX_MAC_LEN; + /* Optional ADE could be added here, if needed */ + eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, (u8 *) req, *reqDataLen - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0, pos); @@ -319,7 +322,7 @@ static void eap_pax_process_std_2(struct eap_sm *sm, pos += EAP_PAX_RAND_LEN; left -= EAP_PAX_RAND_LEN; - if (left < 2 || 2 + ((pos[0] << 8) | pos[1]) > left) { + if (left < 2 || (size_t) 2 + ((pos[0] << 8) | pos[1]) > left) { wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (CID)"); return; } @@ -331,7 +334,7 @@ static void eap_pax_process_std_2(struct eap_sm *sm, "CID"); return; } - memcpy (data->cid, pos + 2, data->cid_len); + 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", @@ -355,13 +358,18 @@ static void eap_pax_process_std_2(struct eap_sm *sm, } for (i = 0; - i < EAP_MAX_METHODS && sm->user->methods[i] != EAP_TYPE_NONE; + i < EAP_MAX_METHODS && + (sm->user->methods[i].vendor != EAP_VENDOR_IETF || + sm->user->methods[i].method != EAP_TYPE_NONE); i++) { - if (sm->user->methods[i] == EAP_TYPE_PAX) + if (sm->user->methods[i].vendor == EAP_VENDOR_IETF && + sm->user->methods[i].method == EAP_TYPE_PAX) break; } - if (sm->user->methods[i] != EAP_TYPE_PAX) { + if (i >= EAP_MAX_METHODS || + sm->user->methods[i].vendor != EAP_VENDOR_IETF || + sm->user->methods[i].method != EAP_TYPE_PAX) { wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: EAP-PAX not enabled for CID", (u8 *) data->cid, data->cid_len); @@ -451,7 +459,8 @@ static void eap_pax_process(struct eap_sm *sm, void *priv, struct eap_pax_hdr *resp; if (sm->user == NULL || sm->user->password == NULL) { - wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured"); + wpa_printf(MSG_INFO, "EAP-PAX: Plaintext password not " + "configured"); data->state = FAILURE; return; } @@ -484,14 +493,36 @@ static u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = malloc(EAP_PAX_MSK_LEN); + key = malloc(EAP_MSK_LEN); if (key == NULL) return NULL; - *len = EAP_PAX_MSK_LEN; + *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_PAX_MSK_LEN, key); + EAP_MSK_LEN, key); + + return key; +} + + +static u8 * eap_pax_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_pax_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = 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; } @@ -504,16 +535,28 @@ static Boolean eap_pax_isSuccess(struct eap_sm *sm, void *priv) } -const struct eap_method eap_method_pax = +int eap_server_pax_register(void) { - .method = EAP_TYPE_PAX, - .name = "PAX", - .init = eap_pax_init, - .reset = eap_pax_reset, - .buildReq = eap_pax_buildReq, - .check = eap_pax_check, - .process = eap_pax_process, - .isDone = eap_pax_isDone, - .getKey = eap_pax_getKey, - .isSuccess = eap_pax_isSuccess, -}; + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_PAX, "PAX"); + if (eap == NULL) + return -1; + + eap->init = eap_pax_init; + eap->reset = eap_pax_reset; + eap->buildReq = eap_pax_buildReq; + eap->check = eap_pax_check; + eap->process = eap_pax_process; + eap->isDone = eap_pax_isDone; + eap->getKey = eap_pax_getKey; + eap->isSuccess = eap_pax_isSuccess; + eap->get_emsk = eap_pax_get_emsk; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/contrib/hostapd/eap_pax_common.c b/contrib/hostapd/eap_pax_common.c index d8f4016..8011046 100644 --- a/contrib/hostapd/eap_pax_common.c +++ b/contrib/hostapd/eap_pax_common.c @@ -1,6 +1,6 @@ /* - * WPA Supplicant / EAP-PAX shared routines - * Copyright (c) 2005, Jouni Malinen <jkmaline@cc.hut.fi> + * EAP server/peer: EAP-PAX shared routines + * 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 @@ -12,9 +12,7 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> +#include "includes.h" #include "common.h" #include "sha1.h" @@ -33,7 +31,7 @@ * @output: Buffer for the derived key * Returns: 0 on success, -1 failed * - * draft-clancy-eap-pax-04.txt, chap. 2.5: PAX-KDF-W(X, Y, Z) + * RFC 4746, Section 2.6: PAX-KDF-W(X, Y, Z) */ int eap_pax_kdf(u8 mac_id, const u8 *key, size_t key_len, const char *identifier, @@ -50,12 +48,12 @@ int eap_pax_kdf(u8 mac_id, const u8 *key, size_t key_len, if (identifier == NULL || num_blocks >= 255) return -1; - /* TODO: add support for EAP_PAX_MAC_AES_CBC_MAC_128 */ + /* TODO: add support for EAP_PAX_HMAC_SHA256_128 */ if (mac_id != EAP_PAX_MAC_HMAC_SHA1_128) return -1; addr[0] = (const u8 *) identifier; - len[0] = strlen(identifier); + len[0] = os_strlen(identifier); addr[1] = entropy; len[1] = entropy_len; addr[2] = &counter; @@ -66,7 +64,7 @@ int eap_pax_kdf(u8 mac_id, const u8 *key, size_t key_len, for (counter = 1; counter <= (u8) num_blocks; counter++) { size_t clen = left > EAP_PAX_MAC_LEN ? EAP_PAX_MAC_LEN : left; hmac_sha1_vector(key, key_len, 3, addr, len, mac); - memcpy(pos, mac, clen); + os_memcpy(pos, mac, clen); pos += clen; left -= clen; } @@ -102,7 +100,7 @@ int eap_pax_mac(u8 mac_id, const u8 *key, size_t key_len, size_t len[3]; size_t count; - /* TODO: add support for EAP_PAX_MAC_AES_CBC_MAC_128 */ + /* TODO: add support for EAP_PAX_HMAC_SHA256_128 */ if (mac_id != EAP_PAX_MAC_HMAC_SHA1_128) return -1; @@ -115,7 +113,7 @@ int eap_pax_mac(u8 mac_id, const u8 *key, size_t key_len, count = (data1 ? 1 : 0) + (data2 ? 1 : 0) + (data3 ? 1 : 0); hmac_sha1_vector(key, key_len, count, addr, len, hash); - memcpy(mac, hash, EAP_PAX_MAC_LEN); + os_memcpy(mac, hash, EAP_PAX_MAC_LEN); return 0; } diff --git a/contrib/hostapd/eap_pax_common.h b/contrib/hostapd/eap_pax_common.h index b5ad6af..bbad5e4 100644 --- a/contrib/hostapd/eap_pax_common.h +++ b/contrib/hostapd/eap_pax_common.h @@ -1,6 +1,6 @@ /* - * WPA Supplicant / EAP-PAX shared routines - * Copyright (c) 2005, Jouni Malinen <jkmaline@cc.hut.fi> + * EAP server/peer: EAP-PAX shared routines + * 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 @@ -15,6 +15,10 @@ #ifndef EAP_PAX_COMMON_H #define EAP_PAX_COMMON_H +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + struct eap_pax_hdr { u8 code; u8 identifier; @@ -26,7 +30,11 @@ struct eap_pax_hdr { u8 dh_group_id; u8 public_key_id; /* Followed by variable length payload and ICV */ -} __attribute__ ((packed)); +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ /* op_code: */ @@ -45,22 +53,31 @@ enum { /* flags: */ #define EAP_PAX_FLAGS_MF 0x01 #define EAP_PAX_FLAGS_CE 0x02 +#define EAP_PAX_FLAGS_AI 0x04 /* mac_id: */ #define EAP_PAX_MAC_HMAC_SHA1_128 0x01 -#define EAP_PAX_MAC_AES_CBC_MAC_128 0x02 +#define EAP_PAX_HMAC_SHA256_128 0x02 /* dh_group_id: */ #define EAP_PAX_DH_GROUP_NONE 0x00 -#define EAP_PAX_DH_GROUP_3072_MODP 0x01 +#define EAP_PAX_DH_GROUP_2048_MODP 0x01 +#define EAP_PAX_DH_GROUP_3072_MODP 0x02 +#define EAP_PAX_DH_GROUP_NIST_ECC_P_256 0x03 /* public_key_id: */ #define EAP_PAX_PUBLIC_KEY_NONE 0x00 -#define EAP_PAX_PUBLIC_KEY_RSA_OAEP_2048 0x01 +#define EAP_PAX_PUBLIC_KEY_RSAES_OAEP 0x01 +#define EAP_PAX_PUBLIC_KEY_RSA_PKCS1_V1_5 0x02 +#define EAP_PAX_PUBLIC_KEY_EL_GAMAL_NIST_ECC 0x03 + +/* ADE type: */ +#define EAP_PAX_ADE_VENDOR_SPECIFIC 0x01 +#define EAP_PAX_ADE_CLIENT_CHANNEL_BINDING 0x02 +#define EAP_PAX_ADE_SERVER_CHANNEL_BINDING 0x03 #define EAP_PAX_RAND_LEN 32 -#define EAP_PAX_MSK_LEN 64 #define EAP_PAX_MAC_LEN 16 #define EAP_PAX_ICV_LEN 16 #define EAP_PAX_AK_LEN 16 diff --git a/contrib/hostapd/eap_peap.c b/contrib/hostapd/eap_peap.c index 9eb61a6..6ea1f61 100644 --- a/contrib/hostapd/eap_peap.c +++ b/contrib/hostapd/eap_peap.c @@ -1,6 +1,6 @@ /* * hostapd / EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-07.txt) - * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.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 @@ -12,10 +12,7 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <netinet/in.h> +#include "includes.h" #include "hostapd.h" #include "common.h" @@ -132,10 +129,9 @@ static void * eap_peap_init(struct eap_sm *sm) { struct eap_peap_data *data; - data = malloc(sizeof(*data)); + data = wpa_zalloc(sizeof(*data)); if (data == NULL) - return data; - memset(data, 0, sizeof(*data)); + return NULL; data->peap_version = EAP_PEAP_VERSION; data->force_version = -1; if (sm->user && sm->user->force_version >= 0) { @@ -293,12 +289,10 @@ static u8 * eap_peap_build_phase2_term(struct eap_sm *sm, struct eap_hdr *hdr; req_len = sizeof(*hdr); - hdr = malloc(req_len); - if (hdr == NULL) { + hdr = wpa_zalloc(req_len); + if (hdr == NULL) return NULL; - } - memset(hdr, 0, req_len); hdr->code = success ? EAP_CODE_SUCCESS : EAP_CODE_FAILURE; hdr->identifier = id; hdr->length = htons(req_len); @@ -345,12 +339,11 @@ static Boolean eap_peap_check(struct eap_sm *sm, void *priv, { struct eap_hdr *resp; u8 *pos; - size_t len; resp = (struct eap_hdr *) respData; pos = (u8 *) (resp + 1); if (respDataLen < sizeof(*resp) + 2 || *pos != EAP_TYPE_PEAP || - (len = ntohs(resp->length)) > respDataLen) { + (ntohs(resp->length)) > respDataLen) { wpa_printf(MSG_INFO, "EAP-PEAP: Invalid frame"); return TRUE; } @@ -360,14 +353,15 @@ static Boolean eap_peap_check(struct eap_sm *sm, void *priv, static int eap_peap_phase2_init(struct eap_sm *sm, struct eap_peap_data *data, - u8 eap_type) + 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_type); + data->phase2_method = eap_sm_get_eap_methods(EAP_VENDOR_IETF, + eap_type); if (!data->phase2_method) return -1; @@ -395,17 +389,17 @@ static void eap_peap_process_phase2_response(struct eap_sm *sm, hdr = (struct eap_hdr *) in_data; pos = (u8 *) (hdr + 1); - left = in_len - sizeof(*hdr); 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] != + sm->user->methods[sm->user_eap_method_index].method != EAP_TYPE_NONE) { - next_type = - sm->user->methods[sm->user_eap_method_index++]; + next_type = sm->user->methods[ + sm->user_eap_method_index++].method; wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d", next_type); } else { @@ -447,7 +441,7 @@ static void eap_peap_process_phase2_response(struct eap_sm *sm, } eap_peap_state(data, PHASE2_METHOD); - next_type = sm->user->methods[0]; + 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; @@ -480,8 +474,9 @@ static void eap_peap_process_phase2(struct eap_sm *sm, u8 *in_data, size_t in_len) { u8 *in_decrypted; - int buf_len, len_decrypted, len, res; + 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); @@ -540,7 +535,7 @@ static void eap_peap_process_phase2(struct eap_sm *sm, in_decrypted = (u8 *) nhdr; } hdr = (struct eap_hdr *) in_decrypted; - if (len_decrypted < sizeof(*hdr)) { + 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); @@ -612,7 +607,6 @@ static void eap_peap_process(struct eap_sm *sm, void *priv, "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) { @@ -711,16 +705,27 @@ static Boolean eap_peap_isSuccess(struct eap_sm *sm, void *priv) } -const struct eap_method eap_method_peap = +int eap_server_peap_register(void) { - .method = EAP_TYPE_PEAP, - .name = "PEAP", - .init = eap_peap_init, - .reset = eap_peap_reset, - .buildReq = eap_peap_buildReq, - .check = eap_peap_check, - .process = eap_peap_process, - .isDone = eap_peap_isDone, - .getKey = eap_peap_getKey, - .isSuccess = eap_peap_isSuccess, -}; + 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/contrib/hostapd/eap_psk.c index 2f92d05..7408a52 100644 --- a/contrib/hostapd/eap_psk.c +++ b/contrib/hostapd/eap_psk.c @@ -1,6 +1,6 @@ /* - * hostapd / EAP-PSK (draft-bersani-eap-psk-09.txt) server - * Copyright (c) 2005, Jouni Malinen <jkmaline@cc.hut.fi> + * hostapd / EAP-PSK (RFC 4764) server + * 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 @@ -15,10 +15,7 @@ * different from WPA-PSK. This file is not needed for WPA-PSK functionality. */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <netinet/in.h> +#include "includes.h" #include "hostapd.h" #include "common.h" @@ -34,7 +31,8 @@ struct eap_psk_data { u8 *id_p, *id_s; size_t id_p_len, id_s_len; u8 ak[EAP_PSK_AK_LEN], kdk[EAP_PSK_KDK_LEN], tek[EAP_PSK_TEK_LEN]; - u8 msk[EAP_PSK_MSK_LEN]; + u8 msk[EAP_MSK_LEN]; + u8 emsk[EAP_EMSK_LEN]; }; @@ -42,12 +40,11 @@ static void * eap_psk_init(struct eap_sm *sm) { struct eap_psk_data *data; - data = malloc(sizeof(*data)); + data = wpa_zalloc(sizeof(*data)); if (data == NULL) - return data; - memset(data, 0, sizeof(*data)); + return NULL; data->state = PSK_1; - data->id_s = "hostapd"; + data->id_s = (u8 *) "hostapd"; data->id_s_len = 7; return data; @@ -90,7 +87,7 @@ static u8 * eap_psk_build_1(struct eap_sm *sm, struct eap_psk_data *data, req->identifier = id; req->length = htons(*reqDataLen); req->type = EAP_TYPE_PSK; - req->flags = 0; /* T=0 */ + 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); @@ -120,13 +117,14 @@ static u8 * eap_psk_build_3(struct eap_sm *sm, struct eap_psk_data *data, req->identifier = id; req->length = htons(*reqDataLen); req->type = EAP_TYPE_PSK; - req->flags = 2; /* T=2 */ + req->flags = EAP_PSK_FLAGS_SET_T(2); /* T=2 */ memcpy(req->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; } @@ -135,9 +133,11 @@ static u8 * eap_psk_build_3(struct eap_sm *sm, struct eap_psk_data *data, 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); + eap_psk_derive_keys(data->kdk, data->rand_p, data->tek, data->msk, + data->emsk); 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_PSK_MSK_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); @@ -189,7 +189,7 @@ static Boolean eap_psk_check(struct eap_sm *sm, void *priv, wpa_printf(MSG_INFO, "EAP-PSK: Invalid frame"); return TRUE; } - t = resp->flags & 0x03; + t = EAP_PSK_FLAGS_GET_T(resp->flags); wpa_printf(MSG_DEBUG, "EAP-PSK: received frame: T=%d", t); @@ -254,13 +254,18 @@ static void eap_psk_process_2(struct eap_sm *sm, } for (i = 0; - i < EAP_MAX_METHODS && sm->user->methods[i] != EAP_TYPE_NONE; + i < EAP_MAX_METHODS && + (sm->user->methods[i].vendor != EAP_VENDOR_IETF || + sm->user->methods[i].method != EAP_TYPE_NONE); i++) { - if (sm->user->methods[i] == EAP_TYPE_PSK) + if (sm->user->methods[i].vendor == EAP_VENDOR_IETF && + sm->user->methods[i].method == EAP_TYPE_PSK) break; } - if (sm->user->methods[i] != EAP_TYPE_PSK) { + if (i >= EAP_MAX_METHODS || + sm->user->methods[i].vendor != EAP_VENDOR_IETF || + sm->user->methods[i].method != EAP_TYPE_PSK) { wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: EAP-PSK not enabled for ID_P", data->id_p, data->id_p_len); @@ -393,14 +398,15 @@ static void eap_psk_process(struct eap_sm *sm, void *priv, struct eap_psk_hdr *resp; if (sm->user == NULL || sm->user->password == NULL) { - wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured"); + wpa_printf(MSG_INFO, "EAP-PSK: Plaintext password not " + "configured"); data->state = FAILURE; return; } resp = (struct eap_psk_hdr *) respData; - switch (resp->flags & 0x03) { + switch (EAP_PSK_FLAGS_GET_T(resp->flags)) { case 1: eap_psk_process_2(sm, data, respData, respDataLen); break; @@ -426,11 +432,29 @@ static u8 * eap_psk_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - key = malloc(EAP_PSK_MSK_LEN); + key = malloc(EAP_MSK_LEN); if (key == NULL) return NULL; - memcpy(key, data->msk, EAP_PSK_MSK_LEN); - *len = EAP_PSK_MSK_LEN; + memcpy(key, data->msk, EAP_MSK_LEN); + *len = EAP_MSK_LEN; + + return key; +} + + +static u8 * eap_psk_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_psk_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + memcpy(key, data->emsk, EAP_EMSK_LEN); + *len = EAP_EMSK_LEN; return key; } @@ -443,16 +467,28 @@ static Boolean eap_psk_isSuccess(struct eap_sm *sm, void *priv) } -const struct eap_method eap_method_psk = +int eap_server_psk_register(void) { - .method = EAP_TYPE_PSK, - .name = "PSK", - .init = eap_psk_init, - .reset = eap_psk_reset, - .buildReq = eap_psk_buildReq, - .check = eap_psk_check, - .process = eap_psk_process, - .isDone = eap_psk_isDone, - .getKey = eap_psk_getKey, - .isSuccess = eap_psk_isSuccess, -}; + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_PSK, "PSK"); + if (eap == NULL) + return -1; + + eap->init = eap_psk_init; + eap->reset = eap_psk_reset; + eap->buildReq = eap_psk_buildReq; + eap->check = eap_psk_check; + eap->process = eap_psk_process; + eap->isDone = eap_psk_isDone; + eap->getKey = eap_psk_getKey; + eap->isSuccess = eap_psk_isSuccess; + eap->get_emsk = eap_psk_get_emsk; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/contrib/hostapd/eap_psk_common.c b/contrib/hostapd/eap_psk_common.c index 24de66c..8d896ae 100644 --- a/contrib/hostapd/eap_psk_common.c +++ b/contrib/hostapd/eap_psk_common.c @@ -1,6 +1,6 @@ /* - * WPA Supplicant / EAP-PSK shared routines - * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * EAP server/peer: EAP-PSK shared routines + * 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 @@ -12,12 +12,11 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> +#include "includes.h" #include "common.h" #include "aes_wrap.h" +#include "eap_defs.h" #include "eap_psk_common.h" #define aes_block_size 16 @@ -25,9 +24,9 @@ void eap_psk_key_setup(const u8 *psk, u8 *ak, u8 *kdk) { - memset(ak, 0, aes_block_size); + os_memset(ak, 0, aes_block_size); aes_128_encrypt_block(psk, ak, ak); - memcpy(kdk, ak, aes_block_size); + 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); @@ -35,7 +34,8 @@ 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) +void 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; @@ -48,10 +48,17 @@ void eap_psk_derive_keys(const u8 *kdk, const u8 *rand_p, u8 *tek, u8 *msk) hash[aes_block_size - 1] ^= counter; counter++; - for (i = 0; i < EAP_PSK_MSK_LEN / aes_block_size; i++) { + 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]); 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]); + hash[aes_block_size - 1] ^= counter; + counter++; + } } diff --git a/contrib/hostapd/eap_psk_common.h b/contrib/hostapd/eap_psk_common.h index 5dd3a10..e1bdccf 100644 --- a/contrib/hostapd/eap_psk_common.h +++ b/contrib/hostapd/eap_psk_common.h @@ -1,6 +1,6 @@ /* - * WPA Supplicant / EAP-PSK shared routines - * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * EAP server/peer: EAP-PSK shared routines + * 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 @@ -19,7 +19,6 @@ #define EAP_PSK_RAND_LEN 16 #define EAP_PSK_MAC_LEN 16 #define EAP_PSK_TEK_LEN 16 -#define EAP_PSK_MSK_LEN 64 #define EAP_PSK_PSK_LEN 16 #define EAP_PSK_AK_LEN 16 #define EAP_PSK_KDK_LEN 16 @@ -29,6 +28,13 @@ #define EAP_PSK_R_FLAG_DONE_FAILURE 3 #define EAP_PSK_E_FLAG 0x20 +#define EAP_PSK_FLAGS_GET_T(flags) (((flags) & 0xc0) >> 6) +#define EAP_PSK_FLAGS_SET_T(t) ((u8) (t) << 6) + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + /* Shared prefix for all EAP-PSK frames */ struct eap_psk_hdr { u8 code; @@ -36,7 +42,7 @@ struct eap_psk_hdr { u16 length; /* including code, identifier, and length */ u8 type; /* EAP_TYPE_PSK */ u8 flags; -} __attribute__ ((packed)); +} STRUCT_PACKED; /* EAP-PSK First Message (AS -> Supplicant) */ struct eap_psk_hdr_1 { @@ -47,7 +53,7 @@ struct eap_psk_hdr_1 { u8 flags; u8 rand_s[EAP_PSK_RAND_LEN]; /* Followed by variable length ID_S */ -} __attribute__ ((packed)); +} STRUCT_PACKED; /* EAP-PSK Second Message (Supplicant -> AS) */ struct eap_psk_hdr_2 { @@ -60,7 +66,7 @@ struct eap_psk_hdr_2 { u8 rand_p[EAP_PSK_RAND_LEN]; u8 mac_p[EAP_PSK_MAC_LEN]; /* Followed by variable length ID_P */ -} __attribute__ ((packed)); +} STRUCT_PACKED; /* EAP-PSK Third Message (AS -> Supplicant) */ struct eap_psk_hdr_3 { @@ -72,7 +78,7 @@ struct eap_psk_hdr_3 { u8 rand_s[EAP_PSK_RAND_LEN]; u8 mac_s[EAP_PSK_MAC_LEN]; /* Followed by variable length PCHANNEL */ -} __attribute__ ((packed)); +} STRUCT_PACKED; /* EAP-PSK Fourth Message (Supplicant -> AS) */ struct eap_psk_hdr_4 { @@ -83,10 +89,15 @@ struct eap_psk_hdr_4 { u8 flags; u8 rand_s[EAP_PSK_RAND_LEN]; /* Followed by variable length PCHANNEL */ -} __attribute__ ((packed)); +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#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); +void 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.c b/contrib/hostapd/eap_sake.c new file mode 100644 index 0000000..e031f29 --- /dev/null +++ b/contrib/hostapd/eap_sake.c @@ -0,0 +1,547 @@ +/* + * hostapd / EAP-SAKE (RFC 4763) server + * 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 "hostapd.h" +#include "common.h" +#include "eap_i.h" +#include "eap_sake_common.h" + + +struct eap_sake_data { + enum { IDENTITY, CHALLENGE, CONFIRM, SUCCESS, FAILURE } state; + u8 rand_s[EAP_SAKE_RAND_LEN]; + u8 rand_p[EAP_SAKE_RAND_LEN]; + struct { + u8 auth[EAP_SAKE_TEK_AUTH_LEN]; + u8 cipher[EAP_SAKE_TEK_CIPHER_LEN]; + } tek; + u8 msk[EAP_MSK_LEN]; + u8 emsk[EAP_EMSK_LEN]; + u8 session_id; + u8 *peerid; + size_t peerid_len; + 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_init(struct eap_sm *sm) +{ + struct eap_sake_data *data; + + data = wpa_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = CHALLENGE; + + if (hostapd_get_rand(&data->session_id, 1)) { + wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data"); + os_free(data); + return NULL; + } + wpa_printf(MSG_DEBUG, "EAP-SAKE: Initialized Session ID %d", + data->session_id); + + /* TODO: add support for configuring SERVERID */ + data->serverid = (u8 *) strdup("hostapd"); + if (data->serverid) + data->serverid_len = strlen((char *) data->serverid); + + return data; +} + + +static void eap_sake_reset(struct eap_sm *sm, void *priv) +{ + struct eap_sake_data *data = priv; + os_free(data->serverid); + os_free(data->peerid); + os_free(data); +} + + +static u8 * eap_sake_build_msg(struct eap_sake_data *data, u8 **payload, + int id, size_t *length, u8 subtype) +{ + struct eap_sake_hdr *req; + u8 *msg; + + *length += sizeof(struct eap_sake_hdr); + + msg = wpa_zalloc(*length); + 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); + + return msg; +} + + +static u8 * eap_sake_build_identity(struct eap_sm *sm, + struct eap_sake_data *data, + int id, size_t *reqDataLen) +{ + u8 *msg, *pos; + + wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Identity"); + + *reqDataLen = 4; + if (data->serverid) + *reqDataLen += 2 + data->serverid_len; + msg = eap_sake_build_msg(data, &pos, id, reqDataLen, + 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; + + 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); + } + + return msg; +} + + +static u8 * eap_sake_build_challenge(struct eap_sm *sm, + struct eap_sake_data *data, + int id, size_t *reqDataLen) +{ + u8 *msg, *pos; + + wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Challenge"); + + if (hostapd_get_rand(data->rand_s, EAP_SAKE_RAND_LEN)) { + wpa_printf(MSG_ERROR, "EAP-SAKE: Failed to get random data"); + data->state = FAILURE; + return NULL; + } + wpa_hexdump(MSG_MSGDUMP, "EAP-SAKE: RAND_S (server rand)", + data->rand_s, EAP_SAKE_RAND_LEN); + + *reqDataLen = 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); + 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; + + 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); + } + + return msg; +} + + +static u8 * eap_sake_build_confirm(struct eap_sm *sm, + struct eap_sake_data *data, + int id, size_t *reqDataLen) +{ + u8 *msg, *pos; + + wpa_printf(MSG_DEBUG, "EAP-SAKE: Request/Confirm"); + + *reqDataLen = 2 + EAP_SAKE_MIC_LEN; + msg = eap_sake_build_msg(data, &pos, id, reqDataLen, + EAP_SAKE_SUBTYPE_CONFIRM); + if (msg == NULL) { + data->state = FAILURE; + return NULL; + } + + wpa_printf(MSG_DEBUG, "EAP-SAKE: * AT_MIC_S"); + *pos++ = EAP_SAKE_AT_MIC_S; + *pos++ = 2 + 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)) { + wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC"); + data->state = FAILURE; + os_free(msg); + return NULL; + } + + return msg; +} + + +static u8 * eap_sake_buildReq(struct eap_sm *sm, void *priv, int id, + size_t *reqDataLen) +{ + struct eap_sake_data *data = priv; + + switch (data->state) { + case IDENTITY: + return eap_sake_build_identity(sm, data, id, reqDataLen); + case CHALLENGE: + return eap_sake_build_challenge(sm, data, id, reqDataLen); + case CONFIRM: + return eap_sake_build_confirm(sm, data, id, reqDataLen); + default: + wpa_printf(MSG_DEBUG, "EAP-SAKE: Unknown state %d in buildReq", + data->state); + break; + } + return NULL; +} + + +static Boolean eap_sake_check(struct eap_sm *sm, void *priv, + u8 *respData, size_t respDataLen) +{ + struct eap_sake_data *data = priv; + struct eap_sake_hdr *resp; + size_t len; + u8 version, session_id, subtype; + + resp = (struct eap_sake_hdr *) respData; + if (respDataLen < sizeof(*resp) || + resp->type != EAP_TYPE_SAKE || + (len = ntohs(resp->length)) > respDataLen || + len < sizeof(*resp)) { + wpa_printf(MSG_INFO, "EAP-SAKE: Invalid frame"); + return TRUE; + } + version = resp->version; + session_id = resp->session_id; + subtype = resp->subtype; + + if (version != EAP_SAKE_VERSION) { + wpa_printf(MSG_INFO, "EAP-SAKE: Unknown version %d", version); + return TRUE; + } + + if (session_id != data->session_id) { + wpa_printf(MSG_INFO, "EAP-SAKE: Session ID mismatch (%d,%d)", + session_id, data->session_id); + return TRUE; + } + + wpa_printf(MSG_DEBUG, "EAP-SAKE: Received frame: subtype=%d", subtype); + + if (data->state == IDENTITY && subtype == EAP_SAKE_SUBTYPE_IDENTITY) + return FALSE; + + if (data->state == CHALLENGE && subtype == EAP_SAKE_SUBTYPE_CHALLENGE) + return FALSE; + + if (data->state == CONFIRM && subtype == EAP_SAKE_SUBTYPE_CONFIRM) + return FALSE; + + if (subtype == EAP_SAKE_SUBTYPE_AUTH_REJECT) + return FALSE; + + wpa_printf(MSG_INFO, "EAP-SAKE: Unexpected subtype=%d in state=%d", + subtype, data->state); + + return TRUE; +} + + +static void eap_sake_process_identity(struct eap_sm *sm, + struct eap_sake_data *data, + u8 *respData, size_t respDataLen, + u8 *payload, size_t payloadlen) +{ + if (data->state != IDENTITY) + return; + + wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Identity"); + /* TODO: update identity and select new user data */ + eap_sake_state(data, CHALLENGE); +} + + +static void eap_sake_process_challenge(struct eap_sm *sm, + struct eap_sake_data *data, + u8 *respData, size_t respDataLen, + u8 *payload, size_t payloadlen) +{ + struct eap_sake_parse_attr attr; + u8 mic_p[EAP_SAKE_MIC_LEN]; + + if (data->state != CHALLENGE) + return; + + wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Challenge"); + + if (eap_sake_parse_attributes(payload, payloadlen, &attr)) + return; + + if (!attr.rand_p || !attr.mic_p) { + wpa_printf(MSG_INFO, "EAP-SAKE: Response/Challenge did not " + "include AT_RAND_P or AT_MIC_P"); + return; + } + + os_memcpy(data->rand_p, attr.rand_p, EAP_SAKE_RAND_LEN); + + os_free(data->peerid); + data->peerid = NULL; + data->peerid_len = 0; + if (attr.peerid) { + data->peerid = os_malloc(attr.peerid_len); + if (data->peerid == NULL) + return; + os_memcpy(data->peerid, attr.peerid, attr.peerid_len); + data->peerid_len = attr.peerid_len; + } + + if (sm->user == NULL || sm->user->password == NULL || + sm->user->password_len != 2 * EAP_SAKE_ROOT_SECRET_LEN) { + wpa_printf(MSG_INFO, "EAP-SAKE: Plaintext password with " + "%d-byte key not configured", + 2 * EAP_SAKE_ROOT_SECRET_LEN); + data->state = FAILURE; + return; + } + eap_sake_derive_keys(sm->user->password, + sm->user->password + EAP_SAKE_ROOT_SECRET_LEN, + data->rand_s, data->rand_p, + (u8 *) &data->tek, data->msk, data->emsk); + + eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, + data->serverid, data->serverid_len, + data->peerid, data->peerid_len, 1, + respData, respDataLen, attr.mic_p, mic_p); + if (os_memcmp(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) { + wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P"); + eap_sake_state(data, FAILURE); + return; + } + + eap_sake_state(data, CONFIRM); +} + + +static void eap_sake_process_confirm(struct eap_sm *sm, + struct eap_sake_data *data, + u8 *respData, size_t respDataLen, + u8 *payload, size_t payloadlen) +{ + struct eap_sake_parse_attr attr; + u8 mic_p[EAP_SAKE_MIC_LEN]; + + if (data->state != CONFIRM) + return; + + wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Confirm"); + + if (eap_sake_parse_attributes(payload, payloadlen, &attr)) + return; + + if (!attr.mic_p) { + wpa_printf(MSG_INFO, "EAP-SAKE: Response/Confirm did not " + "include AT_MIC_P"); + return; + } + + eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p, + data->serverid, data->serverid_len, + data->peerid, data->peerid_len, 1, + respData, respDataLen, attr.mic_p, mic_p); + if (os_memcmp(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) { + wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P"); + eap_sake_state(data, FAILURE); + } else + eap_sake_state(data, SUCCESS); +} + + +static void eap_sake_process_auth_reject(struct eap_sm *sm, + struct eap_sake_data *data, + u8 *respData, size_t respDataLen, + u8 *payload, size_t payloadlen) +{ + wpa_printf(MSG_DEBUG, "EAP-SAKE: Received Response/Auth-Reject"); + eap_sake_state(data, FAILURE); +} + + +static void eap_sake_process(struct eap_sm *sm, void *priv, + u8 *respData, size_t respDataLen) +{ + struct eap_sake_data *data = priv; + struct eap_sake_hdr *resp; + u8 subtype, *pos, *end; + + resp = (struct eap_sake_hdr *) respData; + 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); + break; + case EAP_SAKE_SUBTYPE_CHALLENGE: + eap_sake_process_challenge(sm, data, respData, respDataLen, + pos, end - pos); + break; + case EAP_SAKE_SUBTYPE_CONFIRM: + eap_sake_process_confirm(sm, data, respData, respDataLen, pos, + end - pos); + break; + case EAP_SAKE_SUBTYPE_AUTH_REJECT: + eap_sake_process_auth_reject(sm, data, respData, respDataLen, + pos, end - pos); + break; + } +} + + +static Boolean eap_sake_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_sake_data *data = priv; + return data->state == SUCCESS || data->state == FAILURE; +} + + +static u8 * eap_sake_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_sake_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_MSK_LEN); + if (key == NULL) + return NULL; + os_memcpy(key, data->msk, EAP_MSK_LEN); + *len = EAP_MSK_LEN; + + return key; +} + + +static u8 * eap_sake_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_sake_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = os_malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + os_memcpy(key, data->emsk, EAP_EMSK_LEN); + *len = EAP_EMSK_LEN; + + return key; +} + + +static Boolean eap_sake_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_sake_data *data = priv; + return data->state == SUCCESS; +} + + +int eap_server_sake_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_SAKE, "SAKE"); + if (eap == NULL) + return -1; + + eap->init = eap_sake_init; + eap->reset = eap_sake_reset; + eap->buildReq = eap_sake_buildReq; + eap->check = eap_sake_check; + eap->process = eap_sake_process; + eap->isDone = eap_sake_isDone; + eap->getKey = eap_sake_getKey; + eap->isSuccess = eap_sake_isSuccess; + eap->get_emsk = eap_sake_get_emsk; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/contrib/hostapd/eap_sake_common.c b/contrib/hostapd/eap_sake_common.c new file mode 100644 index 0000000..4b5476f --- /dev/null +++ b/contrib/hostapd/eap_sake_common.c @@ -0,0 +1,380 @@ +/* + * EAP server/peer: EAP-SAKE shared 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 "sha1.h" +#include "eap_defs.h" +#include "eap_sake_common.h" + + +static int eap_sake_parse_add_attr(struct eap_sake_parse_attr *attr, + const u8 *pos) +{ + size_t i; + + switch (pos[0]) { + case EAP_SAKE_AT_RAND_S: + wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_RAND_S"); + if (pos[1] != 2 + EAP_SAKE_RAND_LEN) { + wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_RAND_S with " + "invalid length %d", pos[1]); + return -1; + } + attr->rand_s = pos + 2; + break; + case EAP_SAKE_AT_RAND_P: + wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_RAND_P"); + if (pos[1] != 2 + EAP_SAKE_RAND_LEN) { + wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_RAND_P with " + "invalid length %d", pos[1]); + return -1; + } + attr->rand_p = pos + 2; + break; + case EAP_SAKE_AT_MIC_S: + wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_MIC_S"); + if (pos[1] != 2 + EAP_SAKE_MIC_LEN) { + wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_MIC_S with " + "invalid length %d", pos[1]); + return -1; + } + attr->mic_s = pos + 2; + break; + case EAP_SAKE_AT_MIC_P: + wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_MIC_P"); + if (pos[1] != 2 + EAP_SAKE_MIC_LEN) { + wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_MIC_P with " + "invalid length %d", pos[1]); + return -1; + } + attr->mic_p = pos + 2; + break; + case EAP_SAKE_AT_SERVERID: + wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SERVERID"); + attr->serverid = pos + 2; + attr->serverid_len = pos[1] - 2; + break; + case EAP_SAKE_AT_PEERID: + wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PEERID"); + attr->peerid = pos + 2; + attr->peerid_len = pos[1] - 2; + break; + case EAP_SAKE_AT_SPI_S: + wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SPI_S"); + attr->spi_s = pos + 2; + attr->spi_s_len = pos[1] - 2; + break; + case EAP_SAKE_AT_SPI_P: + wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_SPI_P"); + attr->spi_p = pos + 2; + attr->spi_p_len = pos[1] - 2; + break; + case EAP_SAKE_AT_ANY_ID_REQ: + wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_ANY_ID_REQ"); + if (pos[1] != 4) { + wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid AT_ANY_ID_REQ" + " length %d", pos[1]); + return -1; + } + attr->any_id_req = pos + 2; + break; + case EAP_SAKE_AT_PERM_ID_REQ: + wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PERM_ID_REQ"); + if (pos[1] != 4) { + wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid " + "AT_PERM_ID_REQ length %d", pos[1]); + return -1; + } + attr->perm_id_req = pos + 2; + break; + case EAP_SAKE_AT_ENCR_DATA: + wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_ENCR_DATA"); + attr->encr_data = pos + 2; + attr->encr_data_len = pos[1] - 2; + break; + case EAP_SAKE_AT_IV: + wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_IV"); + attr->iv = pos + 2; + attr->iv_len = pos[1] - 2; + break; + case EAP_SAKE_AT_PADDING: + wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_PADDING"); + for (i = 2; i < pos[1]; i++) { + if (pos[i]) { + wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_PADDING " + "with non-zero pad byte"); + return -1; + } + } + break; + case EAP_SAKE_AT_NEXT_TMPID: + wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_NEXT_TMPID"); + attr->next_tmpid = pos + 2; + attr->next_tmpid_len = pos[1] - 2; + break; + case EAP_SAKE_AT_MSK_LIFE: + wpa_printf(MSG_DEBUG, "EAP-SAKE: Parse: AT_IV"); + if (pos[1] != 6) { + wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid " + "AT_MSK_LIFE length %d", pos[1]); + return -1; + } + attr->msk_life = pos + 2; + break; + default: + if (pos[0] < 128) { + wpa_printf(MSG_DEBUG, "EAP-SAKE: Unknown non-skippable" + " attribute %d", pos[0]); + return -1; + } + wpa_printf(MSG_DEBUG, "EAP-SAKE: Ignoring unknown skippable " + "attribute %d", pos[0]); + break; + } + + if (attr->iv && !attr->encr_data) { + wpa_printf(MSG_DEBUG, "EAP-SAKE: AT_IV included without " + "AT_ENCR_DATA"); + return -1; + } + + return 0; +} + + +/** + * eap_sake_parse_attributes - Parse EAP-SAKE attributes + * @buf: Packet payload (starting with the first attribute) + * @len: Payload length + * @attr: Structure to be filled with found attributes + * Returns: 0 on success or -1 on failure + */ +int eap_sake_parse_attributes(const u8 *buf, size_t len, + struct eap_sake_parse_attr *attr) +{ + const u8 *pos = buf, *end = buf + len; + + os_memset(attr, 0, sizeof(*attr)); + while (pos < end) { + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "EAP-SAKE: Too short attribute"); + return -1; + } + + if (pos[1] < 2) { + wpa_printf(MSG_DEBUG, "EAP-SAKE: Invalid attribute " + "length (%d)", pos[1]); + return -1; + } + + if (pos + pos[1] > end) { + wpa_printf(MSG_DEBUG, "EAP-SAKE: Attribute underflow"); + return -1; + } + + if (eap_sake_parse_add_attr(attr, pos)) + return -1; + + pos += pos[1]; + } + + return 0; +} + + +/** + * eap_sake_kdf - EAP-SAKE Key Derivation Function (KDF) + * @key: Key for KDF + * @key_len: Length of the key in bytes + * @label: A unique label for each purpose of the KDF + * @data: Extra data (start) to bind into the key + * @data_len: Length of the data + * @data2: Extra data (end) to bind into the key + * @data2_len: Length of the data2 + * @buf: Buffer for the generated pseudo-random key + * @buf_len: Number of bytes of key to generate + * + * This function is used to derive new, cryptographically separate keys from a + * given key (e.g., SMS). This is identical to the PRF used in IEEE 802.11i. + */ +static void eap_sake_kdf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, + const u8 *data2, size_t data2_len, + u8 *buf, size_t buf_len) +{ + u8 counter = 0; + size_t pos, plen; + u8 hash[SHA1_MAC_LEN]; + size_t label_len = os_strlen(label) + 1; + const unsigned char *addr[4]; + size_t len[4]; + + addr[0] = (u8 *) label; /* Label | Y */ + len[0] = label_len; + addr[1] = data; /* Msg[start] */ + len[1] = data_len; + addr[2] = data2; /* Msg[end] */ + len[2] = data2_len; + addr[3] = &counter; /* Length */ + len[3] = 1; + + pos = 0; + while (pos < buf_len) { + plen = buf_len - pos; + if (plen >= SHA1_MAC_LEN) { + hmac_sha1_vector(key, key_len, 4, addr, len, + &buf[pos]); + pos += SHA1_MAC_LEN; + } else { + hmac_sha1_vector(key, key_len, 4, addr, len, + hash); + os_memcpy(&buf[pos], hash, plen); + break; + } + counter++; + } +} + + +/** + * eap_sake_derive_keys - Derive EAP-SAKE keys + * @root_secret_a: 16-byte Root-Secret-A + * @root_secret_b: 16-byte Root-Secret-B + * @rand_s: 16-byte RAND_S + * @rand_p: 16-byte RAND_P + * @tek: Buffer for Temporary EAK Keys (TEK-Auth[16] | TEK-Cipher[16]) + * @msk: Buffer for 64-byte MSK + * @emsk: Buffer for 64-byte EMSK + * + * This function derives EAP-SAKE keys as defined in RFC 4763, section 3.2.6. + */ +void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b, + const u8 *rand_s, const u8 *rand_p, u8 *tek, u8 *msk, + u8 *emsk) +{ + u8 sms_a[EAP_SAKE_SMS_LEN]; + u8 sms_b[EAP_SAKE_SMS_LEN]; + u8 key_buf[EAP_MSK_LEN + EAP_EMSK_LEN]; + + wpa_printf(MSG_DEBUG, "EAP-SAKE: Deriving keys"); + + wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: Root-Secret-A", + root_secret_a, EAP_SAKE_ROOT_SECRET_LEN); + eap_sake_kdf(root_secret_a, EAP_SAKE_ROOT_SECRET_LEN, + "SAKE Master Secret A", + rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN, + sms_a, EAP_SAKE_SMS_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: SMS-A", sms_a, EAP_SAKE_SMS_LEN); + eap_sake_kdf(sms_a, EAP_SAKE_SMS_LEN, "Transient EAP Key", + rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN, + tek, EAP_SAKE_TEK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: TEK-Auth", + tek, EAP_SAKE_TEK_AUTH_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: TEK-Cipher", + tek + EAP_SAKE_TEK_AUTH_LEN, EAP_SAKE_TEK_CIPHER_LEN); + + wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: Root-Secret-B", + root_secret_b, EAP_SAKE_ROOT_SECRET_LEN); + eap_sake_kdf(root_secret_b, EAP_SAKE_ROOT_SECRET_LEN, + "SAKE Master Secret B", + rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN, + sms_b, EAP_SAKE_SMS_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: SMS-B", sms_b, EAP_SAKE_SMS_LEN); + eap_sake_kdf(sms_b, EAP_SAKE_SMS_LEN, "Master Session Key", + rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN, + key_buf, sizeof(key_buf)); + os_memcpy(msk, key_buf, EAP_MSK_LEN); + os_memcpy(emsk, key_buf + EAP_MSK_LEN, EAP_EMSK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: MSK", msk, EAP_MSK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: EMSK", emsk, EAP_EMSK_LEN); +} + + +/** + * eap_sake_compute_mic - Compute EAP-SAKE MIC for an EAP packet + * @tek_auth: 16-byte TEK-Auth + * @rand_s: 16-byte RAND_S + * @rand_p: 16-byte RAND_P + * @serverid: SERVERID + * @serverid_len: SERVERID length + * @peerid: PEERID + * @peerid_len: PEERID length + * @peer: MIC calculation for 0 = Server, 1 = Peer message + * @eap: EAP packet + * @eap_len: EAP packet length + * @mic_pos: MIC position in the EAP packet (must be [eap .. eap + eap_len]) + * @mic: Buffer for the computed 16-byte MIC + */ +int eap_sake_compute_mic(const u8 *tek_auth, + const u8 *rand_s, const u8 *rand_p, + const u8 *serverid, size_t serverid_len, + const u8 *peerid, size_t peerid_len, + int peer, const u8 *eap, size_t eap_len, + const u8 *mic_pos, u8 *mic) +{ + u8 _rand[2 * EAP_SAKE_RAND_LEN]; + u8 *tmp, *pos; + size_t tmplen; + + tmplen = serverid_len + 1 + peerid_len + 1 + eap_len; + tmp = os_malloc(tmplen); + if (tmp == NULL) + return -1; + pos = tmp; + if (peer) { + if (peerid) { + os_memcpy(pos, peerid, peerid_len); + pos += peerid_len; + } + *pos++ = 0x00; + if (serverid) { + os_memcpy(pos, serverid, serverid_len); + pos += serverid_len; + } + *pos++ = 0x00; + + os_memcpy(_rand, rand_s, EAP_SAKE_RAND_LEN); + os_memcpy(_rand + EAP_SAKE_RAND_LEN, rand_p, + EAP_SAKE_RAND_LEN); + } else { + if (serverid) { + os_memcpy(pos, serverid, serverid_len); + pos += serverid_len; + } + *pos++ = 0x00; + if (peerid) { + os_memcpy(pos, peerid, peerid_len); + pos += peerid_len; + } + *pos++ = 0x00; + + os_memcpy(_rand, rand_p, EAP_SAKE_RAND_LEN); + os_memcpy(_rand + EAP_SAKE_RAND_LEN, rand_s, + EAP_SAKE_RAND_LEN); + } + + os_memcpy(pos, eap, eap_len); + os_memset(pos + (mic_pos - eap), 0, EAP_SAKE_MIC_LEN); + + eap_sake_kdf(tek_auth, EAP_SAKE_TEK_AUTH_LEN, + peer ? "Peer MIC" : "Server MIC", + _rand, 2 * EAP_SAKE_RAND_LEN, tmp, tmplen, + mic, EAP_SAKE_MIC_LEN); + + os_free(tmp); + + return 0; +} diff --git a/contrib/hostapd/eap_sake_common.h b/contrib/hostapd/eap_sake_common.h new file mode 100644 index 0000000..ac6e819 --- /dev/null +++ b/contrib/hostapd/eap_sake_common.h @@ -0,0 +1,104 @@ +/* + * EAP server/peer: EAP-SAKE shared 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. + */ + +#ifndef EAP_SAKE_COMMON_H +#define EAP_SAKE_COMMON_H + +#define EAP_SAKE_VERSION 2 + +#define EAP_SAKE_SUBTYPE_CHALLENGE 1 +#define EAP_SAKE_SUBTYPE_CONFIRM 2 +#define EAP_SAKE_SUBTYPE_AUTH_REJECT 3 +#define EAP_SAKE_SUBTYPE_IDENTITY 4 + +#define EAP_SAKE_AT_RAND_S 1 +#define EAP_SAKE_AT_RAND_P 2 +#define EAP_SAKE_AT_MIC_S 3 +#define EAP_SAKE_AT_MIC_P 4 +#define EAP_SAKE_AT_SERVERID 5 +#define EAP_SAKE_AT_PEERID 6 +#define EAP_SAKE_AT_SPI_S 7 +#define EAP_SAKE_AT_SPI_P 8 +#define EAP_SAKE_AT_ANY_ID_REQ 9 +#define EAP_SAKE_AT_PERM_ID_REQ 10 +#define EAP_SAKE_AT_ENCR_DATA 128 +#define EAP_SAKE_AT_IV 129 +#define EAP_SAKE_AT_PADDING 130 +#define EAP_SAKE_AT_NEXT_TMPID 131 +#define EAP_SAKE_AT_MSK_LIFE 132 + +#define EAP_SAKE_RAND_LEN 16 +#define EAP_SAKE_MIC_LEN 16 +#define EAP_SAKE_ROOT_SECRET_LEN 16 +#define EAP_SAKE_SMS_LEN 16 +#define EAP_SAKE_TEK_AUTH_LEN 16 +#define EAP_SAKE_TEK_CIPHER_LEN 16 +#define EAP_SAKE_TEK_LEN (EAP_SAKE_TEK_AUTH_LEN + EAP_SAKE_TEK_CIPHER_LEN) + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct eap_sake_hdr { + u8 code; + u8 identifier; + u16 length; + u8 type; /* EAP_TYPE_SAKE */ + u8 version; /* EAP_SAKE_VERSION */ + u8 session_id; + u8 subtype; +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + + +struct eap_sake_parse_attr { + const u8 *rand_s; + const u8 *rand_p; + const u8 *mic_s; + const u8 *mic_p; + const u8 *serverid; + size_t serverid_len; + const u8 *peerid; + size_t peerid_len; + const u8 *spi_s; + size_t spi_s_len; + const u8 *spi_p; + size_t spi_p_len; + const u8 *any_id_req; + const u8 *perm_id_req; + const u8 *encr_data; + size_t encr_data_len; + const u8 *iv; + size_t iv_len; + const u8 *next_tmpid; + size_t next_tmpid_len; + const u8 *msk_life; +}; + +int eap_sake_parse_attributes(const u8 *buf, size_t len, + struct eap_sake_parse_attr *attr); +void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b, + const u8 *rand_s, const u8 *rand_p, + u8 *tek, u8 *msk, u8 *emsk); +int eap_sake_compute_mic(const u8 *tek_auth, + const u8 *rand_s, const u8 *rand_p, + const u8 *serverid, size_t serverid_len, + const u8 *peerid, size_t peerid_len, + int peer, const u8 *eap, size_t eap_len, + const u8 *mic_pos, u8 *mic); + +#endif /* EAP_SAKE_COMMON_H */ diff --git a/contrib/hostapd/eap_sim.c b/contrib/hostapd/eap_sim.c index fa60cf5..8c3c828 100644 --- a/contrib/hostapd/eap_sim.c +++ b/contrib/hostapd/eap_sim.c @@ -1,6 +1,6 @@ /* - * hostapd / EAP-SIM (draft-haverinen-pppext-eap-sim-15.txt) - * Copyright (c) 2005, Jouni Malinen <jkmaline@cc.hut.fi> + * hostapd / EAP-SIM (RFC 4186) + * 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 @@ -12,10 +12,7 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <netinet/in.h> +#include "includes.h" #include "hostapd.h" #include "common.h" @@ -25,38 +22,23 @@ #include "eap_sim_db.h" -#define EAP_SIM_VERSION 1 - -/* EAP-SIM Subtypes */ -#define EAP_SIM_SUBTYPE_START 10 -#define EAP_SIM_SUBTYPE_CHALLENGE 11 -#define EAP_SIM_SUBTYPE_NOTIFICATION 12 -#define EAP_SIM_SUBTYPE_REAUTHENTICATION 13 -#define EAP_SIM_SUBTYPE_CLIENT_ERROR 14 - -/* AT_CLIENT_ERROR_CODE error codes */ -#define EAP_SIM_UNABLE_TO_PROCESS_PACKET 0 -#define EAP_SIM_UNSUPPORTED_VERSION 1 -#define EAP_SIM_INSUFFICIENT_NUM_OF_CHAL 2 -#define EAP_SIM_RAND_NOT_FRESH 3 - -#define KC_LEN 8 -#define SRES_LEN 4 -#define EAP_SIM_MAX_FAST_REAUTHS 1000 - -#define EAP_SIM_MAX_CHAL 3 - struct eap_sim_data { u8 mk[EAP_SIM_MK_LEN]; u8 nonce_mt[EAP_SIM_NONCE_MT_LEN]; + u8 nonce_s[EAP_SIM_NONCE_S_LEN]; u8 k_aut[EAP_SIM_K_AUT_LEN]; u8 k_encr[EAP_SIM_K_ENCR_LEN]; u8 msk[EAP_SIM_KEYING_DATA_LEN]; - u8 kc[EAP_SIM_MAX_CHAL][KC_LEN]; - u8 sres[EAP_SIM_MAX_CHAL][SRES_LEN]; + u8 emsk[EAP_EMSK_LEN]; + u8 kc[EAP_SIM_MAX_CHAL][EAP_SIM_KC_LEN]; + u8 sres[EAP_SIM_MAX_CHAL][EAP_SIM_SRES_LEN]; u8 rand[EAP_SIM_MAX_CHAL][GSM_RAND_LEN]; int num_chal; - enum { START, CHALLENGE, SUCCESS, FAILURE } state; + enum { START, CHALLENGE, REAUTH, SUCCESS, FAILURE } state; + char *next_pseudonym; + char *next_reauth_id; + u16 counter; + struct eap_sim_reauth *reauth; }; @@ -67,6 +49,8 @@ static const char * eap_sim_state_txt(int state) return "START"; case CHALLENGE: return "CHALLENGE"; + case REAUTH: + return "REAUTH"; case SUCCESS: return "SUCCESS"; case FAILURE: @@ -79,7 +63,7 @@ static const char * eap_sim_state_txt(int state) static void eap_sim_state(struct eap_sim_data *data, int state) { - wpa_printf(MSG_DEBUG, "EAP-SIM %s -> %s", + wpa_printf(MSG_DEBUG, "EAP-SIM: %s -> %s", eap_sim_state_txt(data->state), eap_sim_state_txt(state)); data->state = state; @@ -95,10 +79,9 @@ static void * eap_sim_init(struct eap_sm *sm) return NULL; } - data = malloc(sizeof(*data)); + data = wpa_zalloc(sizeof(*data)); if (data == NULL) - return data; - memset(data, 0, sizeof(*data)); + return NULL; data->state = START; return data; @@ -108,6 +91,8 @@ 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); } @@ -118,12 +103,15 @@ static u8 * eap_sim_build_start(struct eap_sm *sm, struct eap_sim_data *data, struct eap_sim_msg *msg; u8 ver[2]; + wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Start"); msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM, EAP_SIM_SUBTYPE_START); if (eap_sim_db_identity_known(sm->eap_sim_db_priv, sm->identity, sm->identity_len)) { + wpa_printf(MSG_DEBUG, " AT_PERMANENT_ID_REQ"); eap_sim_msg_add(msg, EAP_SIM_AT_PERMANENT_ID_REQ, 0, NULL, 0); } + wpa_printf(MSG_DEBUG, " AT_VERSION_LIST"); ver[0] = 0; ver[1] = EAP_SIM_VERSION; eap_sim_msg_add(msg, EAP_SIM_AT_VERSION_LIST, sizeof(ver), @@ -132,22 +120,128 @@ static u8 * eap_sim_build_start(struct eap_sm *sm, struct eap_sim_data *data, } +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); + data->next_pseudonym = + eap_sim_db_get_next_pseudonym(sm->eap_sim_db_priv, 0); + 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); + } else { + wpa_printf(MSG_DEBUG, "EAP-SIM: Max fast re-authentication " + "count exceeded - force full authentication"); + data->next_reauth_id = NULL; + } + + if (data->next_pseudonym == NULL && data->next_reauth_id == NULL && + counter == 0 && nonce_s == NULL) + return 0; + + wpa_printf(MSG_DEBUG, " AT_IV"); + wpa_printf(MSG_DEBUG, " AT_ENCR_DATA"); + eap_sim_msg_add_encr_start(msg, EAP_SIM_AT_IV, EAP_SIM_AT_ENCR_DATA); + + if (counter > 0) { + wpa_printf(MSG_DEBUG, " *AT_COUNTER (%u)", counter); + eap_sim_msg_add(msg, EAP_SIM_AT_COUNTER, counter, NULL, 0); + } + + if (nonce_s) { + wpa_printf(MSG_DEBUG, " *AT_NONCE_S"); + eap_sim_msg_add(msg, EAP_SIM_AT_NONCE_S, 0, nonce_s, + EAP_SIM_NONCE_S_LEN); + } + + if (data->next_pseudonym) { + wpa_printf(MSG_DEBUG, " *AT_NEXT_PSEUDONYM (%s)", + data->next_pseudonym); + eap_sim_msg_add(msg, EAP_SIM_AT_NEXT_PSEUDONYM, + strlen(data->next_pseudonym), + (u8 *) data->next_pseudonym, + 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), + (u8 *) data->next_reauth_id, + strlen(data->next_reauth_id)); + } + + if (eap_sim_msg_add_encr_end(msg, data->k_encr, EAP_SIM_AT_PADDING)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Failed to encrypt " + "AT_ENCR_DATA"); + return -1; + } + + return 0; +} + + static u8 * eap_sim_build_challenge(struct eap_sm *sm, struct eap_sim_data *data, int id, size_t *reqDataLen) { struct eap_sim_msg *msg; + wpa_printf(MSG_DEBUG, "EAP-SIM: Generating Challenge"); msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM, EAP_SIM_SUBTYPE_CHALLENGE); + wpa_printf(MSG_DEBUG, " AT_RAND"); eap_sim_msg_add(msg, EAP_SIM_AT_RAND, 0, (u8 *) data->rand, data->num_chal * GSM_RAND_LEN); + + if (eap_sim_build_encr(sm, data, msg, 0, NULL)) { + eap_sim_msg_free(msg); + return NULL; + } + + 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, 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) +{ + 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)) + return NULL; + wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: NONCE_S", + data->nonce_s, EAP_SIM_NONCE_S_LEN); + + eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk, + data->emsk); + eap_sim_derive_keys_reauth(data->counter, sm->identity, + sm->identity_len, data->nonce_s, data->mk, + data->msk, data->emsk); + + msg = eap_sim_msg_init(EAP_CODE_REQUEST, id, EAP_TYPE_SIM, + EAP_SIM_SUBTYPE_REAUTHENTICATION); + + if (eap_sim_build_encr(sm, data, msg, data->counter, data->nonce_s)) { + eap_sim_msg_free(msg); + return NULL; + } + + 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); +} + + static u8 * eap_sim_buildReq(struct eap_sm *sm, void *priv, int id, size_t *reqDataLen) { @@ -158,6 +252,8 @@ static u8 * eap_sim_buildReq(struct eap_sm *sm, void *priv, int id, return eap_sim_build_start(sm, data, id, reqDataLen); case CHALLENGE: return eap_sim_build_challenge(sm, data, id, reqDataLen); + case REAUTH: + return eap_sim_build_reauth(sm, data, id, reqDataLen); default: wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown state %d in " "buildReq", data->state); @@ -173,12 +269,11 @@ static Boolean eap_sim_check(struct eap_sm *sm, void *priv, struct eap_sim_data *data = priv; struct eap_hdr *resp; u8 *pos, subtype; - size_t len; resp = (struct eap_hdr *) respData; pos = (u8 *) (resp + 1); if (respDataLen < sizeof(*resp) + 4 || *pos != EAP_TYPE_SIM || - (len = ntohs(resp->length)) > respDataLen) { + (ntohs(resp->length)) > respDataLen) { wpa_printf(MSG_INFO, "EAP-SIM: Invalid frame"); return TRUE; } @@ -202,6 +297,13 @@ static Boolean eap_sim_check(struct eap_sm *sm, void *priv, return TRUE; } break; + case REAUTH: + if (subtype != EAP_SIM_SUBTYPE_REAUTHENTICATION) { + wpa_printf(MSG_INFO, "EAP-SIM: Unexpected response " + "subtype %d", subtype); + return TRUE; + } + break; default: wpa_printf(MSG_INFO, "EAP-SIM: Unexpected state (%d) for " "processing a response", data->state); @@ -218,43 +320,15 @@ static int eap_sim_supported_ver(struct eap_sim_data *data, int version) } -static void eap_sim_derive_mk(struct eap_sim_data *data, - const u8 *identity, size_t identity_len, - const u8 *nonce_mt, int selected_version, - int num_chal, const u8 *kc) -{ - u8 sel_ver[2], ver_list[2]; - const unsigned char *addr[5]; - size_t len[5]; - - addr[0] = identity; - addr[1] = kc; - addr[2] = nonce_mt; - addr[3] = ver_list; - addr[4] = sel_ver; - - len[0] = identity_len; - len[1] = num_chal * KC_LEN; - len[2] = EAP_SIM_NONCE_MT_LEN; - len[3] = sizeof(ver_list); - len[4] = sizeof(sel_ver); - - ver_list[0] = 0; - ver_list[1] = EAP_SIM_VERSION; - sel_ver[0] = selected_version >> 8; - sel_ver[1] = selected_version & 0xff; - - /* MK = SHA1(Identity|n*Kc|NONCE_MT|Version List|Selected Version) */ - sha1_vector(5, addr, len, data->mk); - wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MK", data->mk, EAP_SIM_MK_LEN); -} - - static void eap_sim_process_start(struct eap_sm *sm, struct eap_sim_data *data, u8 *respData, size_t respDataLen, struct eap_sim_attrs *attr) { + const u8 *identity; + size_t identity_len; + u8 ver_list[2]; + wpa_printf(MSG_DEBUG, "EAP-SIM: Receive start response"); if (attr->nonce_mt == NULL || attr->selected_version < 0) { @@ -281,8 +355,35 @@ static void eap_sim_process_start(struct eap_sm *sm, } } - if (sm->identity == NULL || sm->identity_len < 1 || - sm->identity[0] != '1') { + identity = NULL; + identity_len = 0; + + if (sm->identity && sm->identity_len > 0 && + sm->identity[0] == EAP_SIM_PERMANENT_PREFIX) { + identity = sm->identity; + identity_len = sm->identity_len; + } else { + identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv, + sm->identity, + sm->identity_len, + &identity_len); + if (identity == NULL) { + data->reauth = eap_sim_db_get_reauth_entry( + sm->eap_sim_db_priv, sm->identity, + sm->identity_len); + if (data->reauth) { + wpa_printf(MSG_DEBUG, "EAP-SIM: 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 (identity == NULL) { wpa_printf(MSG_DEBUG, "EAP-SIM: Could not get proper permanent" " user name"); eap_sim_state(data, FAILURE); @@ -290,12 +391,26 @@ static void eap_sim_process_start(struct eap_sm *sm, } wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity", - sm->identity, sm->identity_len); + identity, identity_len); + + if (data->reauth) { + eap_sim_state(data, REAUTH); + return; + } + + data->counter = 0; /* reset re-auth counter since this is full auth */ + data->reauth = NULL; data->num_chal = eap_sim_db_get_gsm_triplets( - sm->eap_sim_db_priv, sm->identity, sm->identity_len, + sm->eap_sim_db_priv, identity, identity_len, EAP_SIM_MAX_CHAL, - (u8 *) data->rand, (u8 *) data->kc, (u8 *) data->sres); + (u8 *) data->rand, (u8 *) data->kc, (u8 *) data->sres, sm); + if (data->num_chal == EAP_SIM_DB_PENDING) { + wpa_printf(MSG_DEBUG, "EAP-SIM: GSM authentication triplets " + "not yet available - pending request"); + sm->method_pending = METHOD_PENDING_WAIT; + return; + } if (data->num_chal < 2) { wpa_printf(MSG_INFO, "EAP-SIM: Failed to get GSM " "authentication triplets for the peer"); @@ -303,11 +418,16 @@ static void eap_sim_process_start(struct eap_sm *sm, return; } + wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity for MK derivation", + sm->identity, sm->identity_len); + memcpy(data->nonce_mt, attr->nonce_mt, EAP_SIM_NONCE_MT_LEN); - eap_sim_derive_mk(data, sm->identity, sm->identity_len, attr->nonce_mt, - attr->selected_version, data->num_chal, - (u8 *) data->kc); - eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk); + WPA_PUT_BE16(ver_list, EAP_SIM_VERSION); + eap_sim_derive_mk(sm->identity, sm->identity_len, attr->nonce_mt, + attr->selected_version, ver_list, sizeof(ver_list), + data->num_chal, (const u8 *) data->kc, data->mk); + eap_sim_derive_keys(data->mk, data->k_encr, data->k_aut, data->msk, + data->emsk); eap_sim_state(data, CHALLENGE); } @@ -318,9 +438,13 @@ static void eap_sim_process_challenge(struct eap_sm *sm, u8 *respData, size_t respDataLen, 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, - (u8 *) data->sres, data->num_chal * SRES_LEN)) { + (u8 *) data->sres, + data->num_chal * EAP_SIM_SRES_LEN)) { wpa_printf(MSG_WARNING, "EAP-SIM: Challenge message " "did not include valid AT_MAC"); eap_sim_state(data, FAILURE); @@ -330,6 +454,113 @@ 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); + + identity = eap_sim_db_get_permanent(sm->eap_sim_db_priv, sm->identity, + sm->identity_len, &identity_len); + if (identity == NULL) { + identity = sm->identity; + identity_len = sm->identity_len; + } + + if (data->next_pseudonym) { + eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity, + identity_len, + data->next_pseudonym); + 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); + data->next_reauth_id = NULL; + } +} + + +static void eap_sim_process_reauth(struct eap_sm *sm, + struct eap_sim_data *data, + u8 *respData, size_t respDataLen, + struct eap_sim_attrs *attr) +{ + struct eap_sim_attrs eattr; + u8 *decrypted = NULL; + const u8 *identity, *id2; + 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)) { + wpa_printf(MSG_WARNING, "EAP-SIM: Re-authentication message " + "did not include valid AT_MAC"); + goto fail; + } + + if (attr->encr_data == NULL || attr->iv == NULL) { + wpa_printf(MSG_WARNING, "EAP-SIM: Reauthentication " + "message did not include encrypted data"); + goto fail; + } + + decrypted = eap_sim_parse_encr(data->k_encr, attr->encr_data, + attr->encr_data_len, attr->iv, &eattr, + 0); + if (decrypted == NULL) { + wpa_printf(MSG_WARNING, "EAP-SIM: Failed to parse encrypted " + "data from reauthentication message"); + goto fail; + } + + if (eattr.counter != data->counter) { + wpa_printf(MSG_WARNING, "EAP-SIM: Re-authentication message " + "used incorrect counter %u, expected %u", + eattr.counter, data->counter); + goto fail; + } + free(decrypted); + decrypted = NULL; + + wpa_printf(MSG_DEBUG, "EAP-SIM: Re-authentication response includes " + "the correct AT_MAC"); + eap_sim_state(data, SUCCESS); + + if (data->reauth) { + identity = data->reauth->identity; + identity_len = data->reauth->identity_len; + } else { + identity = sm->identity; + identity_len = sm->identity_len; + } + + id2 = eap_sim_db_get_permanent(sm->eap_sim_db_priv, identity, + identity_len, &id2_len); + if (id2) { + identity = id2; + identity_len = id2_len; + } + + if (data->next_pseudonym) { + eap_sim_db_add_pseudonym(sm->eap_sim_db_priv, identity, + identity_len, data->next_pseudonym); + 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); + data->next_reauth_id = NULL; + } else { + eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth); + data->reauth = NULL; + } + + return; + +fail: + eap_sim_state(data, FAILURE); + eap_sim_db_remove_reauth(sm->eap_sim_db_priv, data->reauth); + data->reauth = NULL; + free(decrypted); } @@ -377,6 +608,9 @@ static void eap_sim_process(struct eap_sm *sm, void *priv, case CHALLENGE: eap_sim_process_challenge(sm, data, respData, len, &attr); break; + case REAUTH: + eap_sim_process_reauth(sm, data, respData, len, &attr); + break; default: wpa_printf(MSG_DEBUG, "EAP-SIM: Unknown state %d in " "process", data->state); @@ -409,6 +643,23 @@ static u8 * eap_sim_getKey(struct eap_sm *sm, void *priv, size_t *len) } +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 = malloc(EAP_EMSK_LEN); + if (key == NULL) + return NULL; + memcpy(key, data->emsk, EAP_EMSK_LEN); + *len = EAP_EMSK_LEN; + return key; +} + + static Boolean eap_sim_isSuccess(struct eap_sm *sm, void *priv) { struct eap_sim_data *data = priv; @@ -416,16 +667,28 @@ static Boolean eap_sim_isSuccess(struct eap_sm *sm, void *priv) } -const struct eap_method eap_method_sim = +int eap_server_sim_register(void) { - .method = EAP_TYPE_SIM, - .name = "SIM", - .init = eap_sim_init, - .reset = eap_sim_reset, - .buildReq = eap_sim_buildReq, - .check = eap_sim_check, - .process = eap_sim_process, - .isDone = eap_sim_isDone, - .getKey = eap_sim_getKey, - .isSuccess = eap_sim_isSuccess, -}; + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_SIM, "SIM"); + if (eap == NULL) + return -1; + + eap->init = eap_sim_init; + eap->reset = eap_sim_reset; + eap->buildReq = eap_sim_buildReq; + eap->check = eap_sim_check; + eap->process = eap_sim_process; + eap->isDone = eap_sim_isDone; + eap->getKey = eap_sim_getKey; + eap->isSuccess = eap_sim_isSuccess; + eap->get_emsk = eap_sim_get_emsk; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/contrib/hostapd/eap_sim_common.c b/contrib/hostapd/eap_sim_common.c index 75947b7..dc8b2f6 100644 --- a/contrib/hostapd/eap_sim_common.c +++ b/contrib/hostapd/eap_sim_common.c @@ -1,6 +1,6 @@ /* - * WPA Supplicant / EAP-SIM/AKA shared routines - * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * EAP peer: EAP-SIM/AKA shared routines + * 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 @@ -12,9 +12,7 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> +#include "includes.h" #include "common.h" #include "eap_i.h" @@ -24,80 +22,98 @@ #include "eap_sim_common.h" -static void eap_sim_prf(const u8 *key, u8 *x, size_t xlen) +static int eap_sim_prf(const u8 *key, u8 *x, size_t xlen) { - u8 xkey[64]; - u32 t[5], _t[5]; - int i, j, m, k; - u8 *xpos = x; - u32 carry; - - /* FIPS 186-2 + change notice 1 */ - - memcpy(xkey, key, EAP_SIM_MK_LEN); - memset(xkey + EAP_SIM_MK_LEN, 0, 64 - EAP_SIM_MK_LEN); - t[0] = 0x67452301; - t[1] = 0xEFCDAB89; - t[2] = 0x98BADCFE; - t[3] = 0x10325476; - t[4] = 0xC3D2E1F0; - - m = xlen / 40; - for (j = 0; j < m; j++) { - /* XSEED_j = 0 */ - for (i = 0; i < 2; i++) { - /* XVAL = (XKEY + XSEED_j) mod 2^b */ - - /* w_i = G(t, XVAL) */ - memcpy(_t, t, 20); - sha1_transform((u8 *) _t, xkey); - _t[0] = host_to_be32(_t[0]); - _t[1] = host_to_be32(_t[1]); - _t[2] = host_to_be32(_t[2]); - _t[3] = host_to_be32(_t[3]); - _t[4] = host_to_be32(_t[4]); - memcpy(xpos, _t, 20); - - /* XKEY = (1 + XKEY + w_i) mod 2^b */ - carry = 1; - for (k = 19; k >= 0; k--) { - carry += xkey[k] + xpos[k]; - xkey[k] = carry & 0xff; - carry >>= 8; - } + return fips186_2_prf(key, EAP_SIM_MK_LEN, x, xlen); +} - xpos += SHA1_MAC_LEN; - } - /* x_j = w_0|w_1 */ - } + +void eap_sim_derive_mk(const u8 *identity, size_t identity_len, + const u8 *nonce_mt, u16 selected_version, + const u8 *ver_list, size_t ver_list_len, + int num_chal, const u8 *kc, u8 *mk) +{ + u8 sel_ver[2]; + const unsigned char *addr[5]; + size_t len[5]; + + addr[0] = identity; + len[0] = identity_len; + addr[1] = kc; + len[1] = num_chal * EAP_SIM_KC_LEN; + addr[2] = nonce_mt; + len[2] = EAP_SIM_NONCE_MT_LEN; + addr[3] = ver_list; + len[3] = ver_list_len; + addr[4] = sel_ver; + len[4] = 2; + + WPA_PUT_BE16(sel_ver, selected_version); + + /* MK = SHA1(Identity|n*Kc|NONCE_MT|Version List|Selected Version) */ + sha1_vector(5, addr, len, mk); + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MK", mk, EAP_SIM_MK_LEN); +} + + +void eap_aka_derive_mk(const u8 *identity, size_t identity_len, + const u8 *ik, const u8 *ck, u8 *mk) +{ + const u8 *addr[3]; + size_t len[3]; + + addr[0] = identity; + len[0] = identity_len; + addr[1] = ik; + len[1] = EAP_AKA_IK_LEN; + addr[2] = ck; + len[2] = EAP_AKA_CK_LEN; + + /* MK = SHA1(Identity|IK|CK) */ + sha1_vector(3, addr, len, mk); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: IK", ik, EAP_AKA_IK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: CK", ck, EAP_AKA_CK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-AKA: MK", mk, EAP_SIM_MK_LEN); } -void eap_sim_derive_keys(const u8 *mk, u8 *k_encr, u8 *k_aut, u8 *msk) +int eap_sim_derive_keys(const u8 *mk, u8 *k_encr, u8 *k_aut, u8 *msk, u8 *emsk) { - u8 buf[120], *pos; - eap_sim_prf(mk, buf, 120); + u8 buf[EAP_SIM_K_ENCR_LEN + EAP_SIM_K_AUT_LEN + + EAP_SIM_KEYING_DATA_LEN + EAP_EMSK_LEN], *pos; + if (eap_sim_prf(mk, buf, sizeof(buf)) < 0) { + wpa_printf(MSG_ERROR, "EAP-SIM: Failed to derive keys"); + return -1; + } pos = buf; - memcpy(k_encr, pos, EAP_SIM_K_ENCR_LEN); + os_memcpy(k_encr, pos, EAP_SIM_K_ENCR_LEN); pos += EAP_SIM_K_ENCR_LEN; - memcpy(k_aut, pos, EAP_SIM_K_AUT_LEN); + os_memcpy(k_aut, pos, EAP_SIM_K_AUT_LEN); pos += EAP_SIM_K_AUT_LEN; - memcpy(msk, pos, EAP_SIM_KEYING_DATA_LEN); + os_memcpy(msk, pos, EAP_SIM_KEYING_DATA_LEN); + pos += EAP_SIM_KEYING_DATA_LEN; + os_memcpy(emsk, pos, EAP_EMSK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_encr", k_encr, EAP_SIM_K_ENCR_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_aut", - k_aut, EAP_SIM_K_ENCR_LEN); - wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: keying material", + k_aut, EAP_SIM_K_AUT_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: keying material (MSK)", msk, EAP_SIM_KEYING_DATA_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: EMSK", emsk, EAP_EMSK_LEN); + os_memset(buf, 0, sizeof(buf)); + + return 0; } -void eap_sim_derive_keys_reauth(u16 _counter, - const u8 *identity, size_t identity_len, - const u8 *nonce_s, const u8 *mk, u8 *msk) +int eap_sim_derive_keys_reauth(u16 _counter, + const u8 *identity, size_t identity_len, + const u8 *nonce_s, const u8 *mk, u8 *msk, + u8 *emsk) { u8 xkey[SHA1_MAC_LEN]; + u8 buf[EAP_SIM_KEYING_DATA_LEN + EAP_EMSK_LEN + 32]; u8 counter[2]; const u8 *addr[4]; size_t len[4]; @@ -125,9 +141,22 @@ void eap_sim_derive_keys_reauth(u16 _counter, sha1_vector(4, addr, len, xkey); wpa_hexdump(MSG_DEBUG, "EAP-SIM: XKEY'", xkey, SHA1_MAC_LEN); - eap_sim_prf(xkey, msk, EAP_SIM_KEYING_DATA_LEN); - wpa_hexdump(MSG_DEBUG, "EAP-SIM: keying material", - msk, EAP_SIM_KEYING_DATA_LEN); + if (eap_sim_prf(xkey, buf, sizeof(buf)) < 0) { + wpa_printf(MSG_ERROR, "EAP-SIM: Failed to derive keys"); + return -1; + } + if (msk) { + os_memcpy(msk, buf, EAP_SIM_KEYING_DATA_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-SIM: keying material (MSK)", + msk, EAP_SIM_KEYING_DATA_LEN); + } + if (emsk) { + os_memcpy(emsk, buf + EAP_SIM_KEYING_DATA_LEN, EAP_EMSK_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-SIM: EMSK", emsk, EAP_EMSK_LEN); + } + os_memset(buf, 0, sizeof(buf)); + + return 0; } @@ -143,7 +172,7 @@ int eap_sim_verify_mac(const u8 *k_aut, const u8 *req, size_t req_len, mac > req + req_len - EAP_SIM_MAC_LEN) return -1; - tmp = malloc(req_len); + tmp = os_malloc(req_len); if (tmp == NULL) return -1; @@ -153,12 +182,19 @@ int eap_sim_verify_mac(const u8 *k_aut, const u8 *req, size_t req_len, len[1] = extra_len; /* HMAC-SHA1-128 */ - memcpy(tmp, req, req_len); - memset(tmp + (mac - req), 0, EAP_SIM_MAC_LEN); + 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); + wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC - extra data", + extra, extra_len); + wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: Verify MAC - K_aut", + k_aut, EAP_SIM_K_AUT_LEN); hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac); - free(tmp); + wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Verify MAC: MAC", + hmac, EAP_SIM_MAC_LEN); + os_free(tmp); - return (memcmp(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1; + return (os_memcmp(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1; } @@ -175,9 +211,16 @@ void eap_sim_add_mac(const u8 *k_aut, u8 *msg, size_t msg_len, u8 *mac, len[1] = extra_len; /* HMAC-SHA1-128 */ - memset(mac, 0, EAP_SIM_MAC_LEN); + os_memset(mac, 0, EAP_SIM_MAC_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC - msg", msg, msg_len); + wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC - extra data", + extra, extra_len); + wpa_hexdump_key(MSG_MSGDUMP, "EAP-SIM: Add MAC - K_aut", + k_aut, EAP_SIM_K_AUT_LEN); hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac); - memcpy(mac, hmac, EAP_SIM_MAC_LEN); + os_memcpy(mac, hmac, EAP_SIM_MAC_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Add MAC: MAC", + mac, EAP_SIM_MAC_LEN); } @@ -185,10 +228,9 @@ int eap_sim_parse_attr(const u8 *start, const u8 *end, struct eap_sim_attrs *attr, int aka, int encr) { const u8 *pos = start, *apos; - size_t alen, plen; - int list_len, i; + size_t alen, plen, i, list_len; - memset(attr, 0, sizeof(*attr)); + os_memset(attr, 0, sizeof(*attr)); attr->id_req = NO_ID_REQ; attr->notification = -1; attr->counter = -1; @@ -219,7 +261,7 @@ int eap_sim_parse_attr(const u8 *start, const u8 *end, apos += 2; alen -= 2; if ((!aka && (alen % GSM_RAND_LEN)) || - (aka && alen != AKA_RAND_LEN)) { + (aka && alen != EAP_AKA_RAND_LEN)) { wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_RAND" " (len %lu)", (unsigned long) alen); @@ -237,7 +279,7 @@ int eap_sim_parse_attr(const u8 *start, const u8 *end, } apos += 2; alen -= 2; - if (alen != AKA_AUTN_LEN) { + if (alen != EAP_AKA_AUTN_LEN) { wpa_printf(MSG_INFO, "EAP-AKA: Invalid AT_AUTN" " (len %lu)", (unsigned long) alen); @@ -316,8 +358,9 @@ int eap_sim_parse_attr(const u8 *start, const u8 *end, wpa_printf(MSG_DEBUG, "EAP-SIM: AT_VERSION_LIST"); if (list_len < 2 || list_len > alen - 2) { wpa_printf(MSG_WARNING, "EAP-SIM: Invalid " - "AT_VERSION_LIST (list_len=%d " - "attr_len=%lu)", list_len, + "AT_VERSION_LIST (list_len=%lu " + "attr_len=%lu)", + (unsigned long) list_len, (unsigned long) alen); return -1; } @@ -356,6 +399,22 @@ int eap_sim_parse_attr(const u8 *start, const u8 *end, wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) AT_COUNTER %d", attr->counter); break; + case EAP_SIM_AT_COUNTER_TOO_SMALL: + if (!encr) { + wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted " + "AT_COUNTER_TOO_SMALL"); + return -1; + } + if (alen != 2) { + wpa_printf(MSG_INFO, "EAP-SIM: (encr) Invalid " + "AT_COUNTER_TOO_SMALL (alen=%lu)", + (unsigned long) alen); + return -1; + } + wpa_printf(MSG_DEBUG, "EAP-SIM: (encr) " + "AT_COUNTER_TOO_SMALL"); + attr->counter_too_small = 1; + break; case EAP_SIM_AT_NONCE_S: if (!encr) { wpa_printf(MSG_ERROR, "EAP-SIM: Unencrypted " @@ -444,6 +503,35 @@ int eap_sim_parse_attr(const u8 *start, const u8 *end, attr->next_reauth_id = pos + 4; attr->next_reauth_id_len = plen; break; + case EAP_SIM_AT_RES: + wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RES"); + apos += 2; + alen -= 2; + if (!aka || alen < EAP_AKA_MIN_RES_LEN || + alen > EAP_AKA_MAX_RES_LEN) { + wpa_printf(MSG_INFO, "EAP-SIM: Invalid AT_RES " + "(len %lu)", + (unsigned long) alen); + return -1; + } + attr->res = apos; + attr->res_len = alen; + break; + case EAP_SIM_AT_AUTS: + wpa_printf(MSG_DEBUG, "EAP-AKA: AT_AUTS"); + if (!aka) { + wpa_printf(MSG_DEBUG, "EAP-SIM: " + "Unexpected AT_AUTS"); + return -1; + } + if (alen != EAP_AKA_AUTS_LEN) { + wpa_printf(MSG_INFO, "EAP-AKA: Invalid AT_AUTS" + " (len %lu)", + (unsigned long) alen); + return -1; + } + attr->auts = apos; + break; default: if (pos[0] < 128) { wpa_printf(MSG_INFO, "EAP-SIM: Unrecognized " @@ -478,10 +566,10 @@ u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data, return NULL; } - decrypted = malloc(encr_data_len); + decrypted = os_malloc(encr_data_len); if (decrypted == NULL) return NULL; - memcpy(decrypted, encr_data, encr_data_len); + os_memcpy(decrypted, encr_data, encr_data_len); aes_128_cbc_decrypt(k_encr, iv, decrypted, encr_data_len); wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Decrypted AT_ENCR_DATA", @@ -491,7 +579,7 @@ u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data, aka, 1)) { wpa_printf(MSG_INFO, "EAP-SIM: (encr) Failed to parse " "decrypted AT_ENCR_DATA"); - free(decrypted); + os_free(decrypted); return NULL; } @@ -514,17 +602,15 @@ struct eap_sim_msg * eap_sim_msg_init(int code, int id, int type, int subtype) struct eap_hdr *eap; u8 *pos; - msg = malloc(sizeof(*msg)); + msg = os_zalloc(sizeof(*msg)); if (msg == NULL) return NULL; - memset(msg, 0, sizeof(*msg)); - msg->buf = malloc(EAP_SIM_INIT_LEN); + msg->buf = os_zalloc(EAP_SIM_INIT_LEN); if (msg->buf == NULL) { - free(msg); + os_free(msg); return NULL; } - memset(msg->buf, 0, EAP_SIM_INIT_LEN); msg->buf_len = EAP_SIM_INIT_LEN; eap = (struct eap_hdr *) msg->buf; eap->code = code; @@ -561,7 +647,7 @@ u8 * eap_sim_msg_finish(struct eap_sim_msg *msg, size_t *len, const u8 *k_aut, *len = msg->used; buf = msg->buf; - free(msg); + os_free(msg); return buf; } @@ -569,8 +655,8 @@ 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) { - free(msg->buf); - free(msg); + os_free(msg->buf); + os_free(msg); } } @@ -578,7 +664,7 @@ void eap_sim_msg_free(struct eap_sim_msg *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 = realloc(msg->buf, msg->used + add_len); + u8 *nbuf = os_realloc(msg->buf, msg->used + add_len); if (nbuf == NULL) return -1; msg->buf = nbuf; @@ -605,10 +691,10 @@ u8 * eap_sim_msg_add_full(struct eap_sim_msg *msg, u8 attr, start = pos = msg->buf + msg->used; *pos++ = attr; *pos++ = attr_len / 4; - memcpy(pos, data, len); + os_memcpy(pos, data, len); if (pad_len) { pos += len; - memset(pos, 0, pad_len); + os_memset(pos, 0, pad_len); } msg->used += attr_len; return start; @@ -635,10 +721,10 @@ u8 * eap_sim_msg_add(struct eap_sim_msg *msg, u8 attr, u16 value, WPA_PUT_BE16(pos, value); pos += 2; if (data) - memcpy(pos, data, len); + os_memcpy(pos, data, len); if (pad_len) { pos += len; - memset(pos, 0, pad_len); + os_memset(pos, 0, pad_len); } msg->used += attr_len; return start; @@ -681,7 +767,7 @@ int eap_sim_msg_add_encr_end(struct eap_sim_msg *msg, u8 *k_encr, int attr_pad) { size_t encr_len; - if (k_encr == NULL || msg->iv == 0 || msg->encr == 0) + if (msg == NULL || k_encr == NULL || msg->iv == 0 || msg->encr == 0) return -1; encr_len = msg->used - msg->encr - 4; @@ -698,7 +784,7 @@ int eap_sim_msg_add_encr_end(struct eap_sim_msg *msg, u8 *k_encr, int attr_pad) pos = eap_sim_msg_add(msg, attr_pad, 0, NULL, pad_len - 4); if (pos == NULL) return -1; - memset(pos + 4, 0, pad_len - 4); + os_memset(pos + 4, 0, pad_len - 4); encr_len += pad_len; } wpa_printf(MSG_DEBUG, " (AT_ENCR_DATA data len %lu)", @@ -752,43 +838,3 @@ void eap_sim_report_notification(void *msg_ctx, int notification, int aka) } } } - - -#ifdef TEST_MAIN_EAP_SIM_COMMON -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; -} -#endif /* TEST_MAIN_EAP_SIM_COMMON */ diff --git a/contrib/hostapd/eap_sim_common.h b/contrib/hostapd/eap_sim_common.h index 6715c36..9c983a8 100644 --- a/contrib/hostapd/eap_sim_common.h +++ b/contrib/hostapd/eap_sim_common.h @@ -1,6 +1,6 @@ /* - * WPA Supplicant / EAP-SIM/AKA shared routines - * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * EAP peer: EAP-SIM/AKA shared routines + * 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 @@ -23,16 +23,65 @@ #define EAP_SIM_K_ENCR_LEN 16 #define EAP_SIM_KEYING_DATA_LEN 64 #define EAP_SIM_IV_LEN 16 +#define EAP_SIM_KC_LEN 8 +#define EAP_SIM_SRES_LEN 4 #define GSM_RAND_LEN 16 -#define AKA_RAND_LEN 16 -#define AKA_AUTN_LEN 16 - -void eap_sim_derive_keys(const u8 *mk, u8 *k_encr, u8 *k_aut, u8 *msk); -void eap_sim_derive_keys_reauth(u16 _counter, - const u8 *identity, size_t identity_len, - const u8 *nonce_s, const u8 *mk, u8 *msk); +#define EAP_SIM_VERSION 1 + +/* EAP-SIM Subtypes */ +#define EAP_SIM_SUBTYPE_START 10 +#define EAP_SIM_SUBTYPE_CHALLENGE 11 +#define EAP_SIM_SUBTYPE_NOTIFICATION 12 +#define EAP_SIM_SUBTYPE_REAUTHENTICATION 13 +#define EAP_SIM_SUBTYPE_CLIENT_ERROR 14 + +/* AT_CLIENT_ERROR_CODE error codes */ +#define EAP_SIM_UNABLE_TO_PROCESS_PACKET 0 +#define EAP_SIM_UNSUPPORTED_VERSION 1 +#define EAP_SIM_INSUFFICIENT_NUM_OF_CHAL 2 +#define EAP_SIM_RAND_NOT_FRESH 3 + +#define EAP_SIM_MAX_FAST_REAUTHS 1000 + +#define EAP_SIM_MAX_CHAL 3 + + +/* EAP-AKA Subtypes */ +#define EAP_AKA_SUBTYPE_CHALLENGE 1 +#define EAP_AKA_SUBTYPE_AUTHENTICATION_REJECT 2 +#define EAP_AKA_SUBTYPE_SYNCHRONIZATION_FAILURE 4 +#define EAP_AKA_SUBTYPE_IDENTITY 5 +#define EAP_AKA_SUBTYPE_NOTIFICATION 12 +#define EAP_AKA_SUBTYPE_REAUTHENTICATION 13 +#define EAP_AKA_SUBTYPE_CLIENT_ERROR 14 + +/* AT_CLIENT_ERROR_CODE error codes */ +#define EAP_AKA_UNABLE_TO_PROCESS_PACKET 0 + +#define EAP_AKA_RAND_LEN 16 +#define EAP_AKA_AUTN_LEN 16 +#define EAP_AKA_AUTS_LEN 14 +#define EAP_AKA_RES_MAX_LEN 16 +#define EAP_AKA_IK_LEN 16 +#define EAP_AKA_CK_LEN 16 +#define EAP_AKA_MAX_FAST_REAUTHS 1000 +#define EAP_AKA_MIN_RES_LEN 4 +#define EAP_AKA_MAX_RES_LEN 16 + +void eap_sim_derive_mk(const u8 *identity, size_t identity_len, + const u8 *nonce_mt, u16 selected_version, + const u8 *ver_list, size_t ver_list_len, + int num_chal, const u8 *kc, u8 *mk); +void eap_aka_derive_mk(const u8 *identity, size_t identity_len, + const u8 *ik, const u8 *ck, u8 *mk); +int eap_sim_derive_keys(const u8 *mk, u8 *k_encr, u8 *k_aut, u8 *msk, + u8 *emsk); +int eap_sim_derive_keys_reauth(u16 _counter, + const u8 *identity, size_t identity_len, + const u8 *nonce_s, const u8 *mk, u8 *msk, + u8 *emsk); int eap_sim_verify_mac(const u8 *k_aut, const u8 *req, size_t req_len, const u8 *mac, const u8 *extra, size_t extra_len); void eap_sim_add_mac(const u8 *k_aut, u8 *msg, size_t msg_len, u8 *mac, @@ -42,8 +91,8 @@ void eap_sim_add_mac(const u8 *k_aut, u8 *msg, size_t msg_len, u8 *mac, /* EAP-SIM/AKA Attributes (0..127 non-skippable) */ #define EAP_SIM_AT_RAND 1 #define EAP_SIM_AT_AUTN 2 /* only AKA */ -#define EAP_SIM_AT_RES 3 /* only AKA, only send */ -#define EAP_SIM_AT_AUTS 4 /* only AKA, only send */ +#define EAP_SIM_AT_RES 3 /* only AKA, only peer->server */ +#define EAP_SIM_AT_AUTS 4 /* only AKA, only peer->server */ #define EAP_SIM_AT_PADDING 6 /* only encrypted */ #define EAP_SIM_AT_NONCE_MT 7 /* only SIM, only send */ #define EAP_SIM_AT_PERMANENT_ID_REQ 10 @@ -81,11 +130,12 @@ enum eap_sim_id_req { 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; + const u8 *nonce_mt, *identity, *res, *auts; size_t num_chal, version_list_len, encr_data_len; - size_t next_pseudonym_len, next_reauth_id_len, identity_len; + size_t next_pseudonym_len, next_reauth_id_len, identity_len, res_len; enum eap_sim_id_req id_req; int notification, counter, selected_version, client_error_code; + int counter_too_small; }; int eap_sim_parse_attr(const u8 *start, const u8 *end, diff --git a/contrib/hostapd/eap_sim_db.c b/contrib/hostapd/eap_sim_db.c index a965fa4..93ade14 100644 --- a/contrib/hostapd/eap_sim_db.c +++ b/contrib/hostapd/eap_sim_db.c @@ -1,6 +1,6 @@ /* * hostapd / EAP-SIM database/authenticator gateway - * Copyright (c) 2005, Jouni Malinen <jkmaline@cc.hut.fi> + * 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 @@ -10,106 +10,555 @@ * license. * * See README and COPYING for more details. - */ - -/* This is an example implementation of the EAP-SIM database/authentication - * gateway interface that is expected to be replaced with an implementation of - * SS7 gateway to GSM authentication center (HLR/AuC) or a local - * implementation of SIM triplet generator. * - * The example implementation here reads triplets from a text file in - * IMSI:Kc:SRES:RAND format, IMSI in ASCII, other fields as hex strings. This - * is used to simulate an HLR/AuC. As such, it is not very useful for real life - * authentication, but it is useful both as an example implementation and for - * EAP-SIM testing. + * This is an example implementation of the EAP-SIM/AKA database/authentication + * gateway interface that is using an external program as an SS7 gateway to + * GSM/UMTS authentication center (HLR/AuC). hlr_auc_gw is an example + * implementation of such a gateway program. This eap_sim_db.c takes care of + * EAP-SIM/AKA pseudonyms and re-auth identities. It can be used with different + * gateway implementations for HLR/AuC access. Alternatively, it can also be + * completely replaced if the in-memory database of pseudonyms/re-auth + * identities is not suitable for some cases. */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> +#include "includes.h" +#include <sys/un.h> #include "common.h" #include "eap_sim_common.h" #include "eap_sim_db.h" +#include "eloop.h" +struct eap_sim_pseudonym { + struct eap_sim_pseudonym *next; + u8 *identity; + size_t identity_len; + char *pseudonym; +}; -/* TODO: add an alternative callback based version of the interface. This is - * needed to work better with the single threaded design of hostapd. For this, - * the EAP data has to be stored somewhere and eap_sim_db is given a context - * pointer for this and a callback function. The callback function will re-send - * the EAP data through normal operations which will eventually end up calling - * eap_sim_db_get_gsm_triplets() again for the same user. This time, eap_sim_db - * should have the triplets available immediately. */ - +struct eap_sim_db_pending { + struct eap_sim_db_pending *next; + u8 imsi[20]; + size_t imsi_len; + enum { PENDING, SUCCESS, FAILURE } state; + void *cb_session_ctx; + struct os_time timestamp; + int aka; + union { + struct { + u8 kc[EAP_SIM_MAX_CHAL][EAP_SIM_KC_LEN]; + u8 sres[EAP_SIM_MAX_CHAL][EAP_SIM_SRES_LEN]; + u8 rand[EAP_SIM_MAX_CHAL][GSM_RAND_LEN]; + int num_chal; + } sim; + struct { + u8 rand[EAP_AKA_RAND_LEN]; + u8 autn[EAP_AKA_AUTN_LEN]; + u8 ik[EAP_AKA_IK_LEN]; + u8 ck[EAP_AKA_CK_LEN]; + u8 res[EAP_AKA_RES_MAX_LEN]; + size_t res_len; + } aka; + } u; +}; struct eap_sim_db_data { + int sock; char *fname; + char *local_sock; + void (*get_complete_cb)(void *ctx, void *session_ctx); + void *ctx; + struct eap_sim_pseudonym *pseudonyms; + struct eap_sim_reauth *reauths; + struct eap_sim_db_pending *pending; }; -#define KC_LEN 8 -#define SRES_LEN 4 -#define RAND_LEN 16 +static struct eap_sim_db_pending * +eap_sim_db_get_pending(struct eap_sim_db_data *data, const u8 *imsi, + size_t imsi_len, int aka) +{ + struct eap_sim_db_pending *entry, *prev = NULL; + + entry = data->pending; + while (entry) { + if (entry->aka == aka && entry->imsi_len == imsi_len && + memcmp(entry->imsi, imsi, imsi_len) == 0) { + if (prev) + prev->next = entry->next; + else + data->pending = entry->next; + break; + } + prev = entry; + entry = entry->next; + } + return entry; +} -/* Initialize EAP-SIM database/authentication gateway interface. - * Returns pointer to a private data structure. */ -void * eap_sim_db_init(const char *config) + +static void eap_sim_db_add_pending(struct eap_sim_db_data *data, + struct eap_sim_db_pending *entry) +{ + entry->next = data->pending; + data->pending = entry; +} + + +static void eap_sim_db_sim_resp_auth(struct eap_sim_db_data *data, + const char *imsi, char *buf) +{ + char *start, *end, *pos; + struct eap_sim_db_pending *entry; + int num_chal; + + /* + * SIM-RESP-AUTH <IMSI> Kc(i):SRES(i):RAND(i) ... + * SIM-RESP-AUTH <IMSI> FAILURE + * (IMSI = ASCII string, Kc/SRES/RAND = hex string) + */ + + entry = eap_sim_db_get_pending(data, (u8 *) imsi, strlen(imsi), 0); + if (entry == NULL) { + wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the " + "received message found"); + return; + } + + start = buf; + if (strncmp(start, "FAILURE", 7) == 0) { + wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported " + "failure"); + entry->state = FAILURE; + eap_sim_db_add_pending(data, entry); + data->get_complete_cb(data->ctx, entry->cb_session_ctx); + return; + } + + num_chal = 0; + while (num_chal < EAP_SIM_MAX_CHAL) { + end = strchr(start, ' '); + if (end) + *end = '\0'; + + pos = strchr(start, ':'); + if (pos == NULL) + goto parse_fail; + *pos = '\0'; + if (hexstr2bin(start, entry->u.sim.kc[num_chal], + EAP_SIM_KC_LEN)) + goto parse_fail; + + start = pos + 1; + pos = strchr(start, ':'); + if (pos == NULL) + goto parse_fail; + *pos = '\0'; + if (hexstr2bin(start, entry->u.sim.sres[num_chal], + EAP_SIM_SRES_LEN)) + goto parse_fail; + + start = pos + 1; + if (hexstr2bin(start, entry->u.sim.rand[num_chal], + GSM_RAND_LEN)) + goto parse_fail; + + num_chal++; + if (end == NULL) + break; + else + start = end + 1; + } + entry->u.sim.num_chal = num_chal; + + entry->state = SUCCESS; + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Authentication data parsed " + "successfully - callback"); + eap_sim_db_add_pending(data, entry); + data->get_complete_cb(data->ctx, entry->cb_session_ctx); + return; + +parse_fail: + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string"); + free(entry); +} + + +static void eap_sim_db_aka_resp_auth(struct eap_sim_db_data *data, + const char *imsi, char *buf) +{ + char *start, *end; + struct eap_sim_db_pending *entry; + + /* + * AKA-RESP-AUTH <IMSI> <RAND> <AUTN> <IK> <CK> <RES> + * AKA-RESP-AUTH <IMSI> FAILURE + * (IMSI = ASCII string, RAND/AUTN/IK/CK/RES = hex string) + */ + + entry = eap_sim_db_get_pending(data, (u8 *) imsi, strlen(imsi), 1); + if (entry == NULL) { + wpa_printf(MSG_DEBUG, "EAP-SIM DB: No pending entry for the " + "received message found"); + return; + } + + start = buf; + if (strncmp(start, "FAILURE", 7) == 0) { + wpa_printf(MSG_DEBUG, "EAP-SIM DB: External server reported " + "failure"); + entry->state = FAILURE; + eap_sim_db_add_pending(data, entry); + data->get_complete_cb(data->ctx, entry->cb_session_ctx); + return; + } + + end = strchr(start, ' '); + if (end == NULL) + goto parse_fail; + *end = '\0'; + if (hexstr2bin(start, entry->u.aka.rand, EAP_AKA_RAND_LEN)) + goto parse_fail; + + start = end + 1; + end = strchr(start, ' '); + if (end == NULL) + goto parse_fail; + *end = '\0'; + if (hexstr2bin(start, entry->u.aka.autn, EAP_AKA_AUTN_LEN)) + goto parse_fail; + + start = end + 1; + end = strchr(start, ' '); + if (end == NULL) + goto parse_fail; + *end = '\0'; + if (hexstr2bin(start, entry->u.aka.ik, EAP_AKA_IK_LEN)) + goto parse_fail; + + start = end + 1; + end = strchr(start, ' '); + if (end == NULL) + goto parse_fail; + *end = '\0'; + if (hexstr2bin(start, entry->u.aka.ck, EAP_AKA_CK_LEN)) + goto parse_fail; + + start = end + 1; + end = strchr(start, ' '); + if (end) + *end = '\0'; + else { + end = start; + while (*end) + end++; + } + entry->u.aka.res_len = (end - start) / 2; + if (entry->u.aka.res_len > EAP_AKA_RES_MAX_LEN) { + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Too long RES"); + entry->u.aka.res_len = 0; + goto parse_fail; + } + if (hexstr2bin(start, entry->u.aka.res, entry->u.aka.res_len)) + goto parse_fail; + + entry->state = SUCCESS; + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Authentication data parsed " + "successfully - callback"); + eap_sim_db_add_pending(data, entry); + data->get_complete_cb(data->ctx, entry->cb_session_ctx); + return; + +parse_fail: + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string"); + free(entry); +} + + +static void eap_sim_db_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct eap_sim_db_data *data = eloop_ctx; + char buf[1000], *pos, *cmd, *imsi; + int res; + + res = recv(sock, buf, sizeof(buf), 0); + if (res < 0) + return; + wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-SIM DB: Received from an " + "external source", (u8 *) buf, res); + if (res == 0) + return; + if (res >= (int) sizeof(buf)) + res = sizeof(buf) - 1; + buf[res] = '\0'; + + if (data->get_complete_cb == NULL) { + wpa_printf(MSG_DEBUG, "EAP-SIM DB: No get_complete_cb " + "registered"); + return; + } + + /* <cmd> <IMSI> ... */ + + cmd = buf; + pos = strchr(cmd, ' '); + if (pos == NULL) + goto parse_fail; + *pos = '\0'; + imsi = pos + 1; + pos = 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) + eap_sim_db_sim_resp_auth(data, imsi, pos + 1); + else if (strcmp(cmd, "AKA-RESP-AUTH") == 0) + eap_sim_db_aka_resp_auth(data, imsi, pos + 1); + else + wpa_printf(MSG_INFO, "EAP-SIM DB: Unknown external response " + "'%s'", cmd); + return; + +parse_fail: + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failed to parse response string"); +} + + +static int eap_sim_db_open_socket(struct eap_sim_db_data *data) +{ + struct sockaddr_un addr; + static int counter = 0; + + if (strncmp(data->fname, "unix:", 5) != 0) + return -1; + + data->sock = socket(PF_UNIX, SOCK_DGRAM, 0); + if (data->sock < 0) { + perror("socket(eap_sim_db)"); + return -1; + } + + 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); + if (bind(data->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind(eap_sim_db)"); + close(data->sock); + data->sock = -1; + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", data->fname + 5); + 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)); + close(data->sock); + data->sock = -1; + return -1; + } + + eloop_register_read_sock(data->sock, eap_sim_db_receive, data, NULL); + + return 0; +} + + +static void eap_sim_db_close_socket(struct eap_sim_db_data *data) +{ + if (data->sock >= 0) { + eloop_unregister_read_sock(data->sock); + close(data->sock); + data->sock = -1; + } + if (data->local_sock) { + unlink(data->local_sock); + free(data->local_sock); + data->local_sock = NULL; + } +} + + +/** + * eap_sim_db_init - Initialize EAP-SIM DB / authentication gateway interface + * @config: Configuration data (e.g., file name) + * @get_complete_cb: Callback function for reporting availability of triplets + * @ctx: Context pointer for get_complete_cb + * Returns: Pointer to a private data structure or %NULL on failure + */ +void * eap_sim_db_init(const char *config, + void (*get_complete_cb)(void *ctx, void *session_ctx), + void *ctx) { struct eap_sim_db_data *data; - data = malloc(sizeof(*data)); - if (data == NULL) { + data = wpa_zalloc(sizeof(*data)); + if (data == NULL) return NULL; - } - memset(data, 0, sizeof(*data)); + data->sock = -1; + data->get_complete_cb = get_complete_cb; + data->ctx = ctx; data->fname = strdup(config); - if (data->fname == NULL) { - free(data); - return NULL; + if (data->fname == NULL) + goto fail; + + if (strncmp(data->fname, "unix:", 5) == 0) { + if (eap_sim_db_open_socket(data)) + goto fail; } return data; + +fail: + eap_sim_db_close_socket(data); + free(data->fname); + free(data); + return NULL; +} + + +static void eap_sim_db_free_pseudonym(struct eap_sim_pseudonym *p) +{ + free(p->identity); + free(p->pseudonym); + free(p); +} + + +static void eap_sim_db_free_reauth(struct eap_sim_reauth *r) +{ + free(r->identity); + free(r->reauth_id); + free(r); } -/* Deinitialize EAP-SIM database/authentication gateway interface. - * priv is the pointer from eap_sim_db_init(). */ + +/** + * eap_sim_db_deinit - Deinitialize EAP-SIM DB/authentication gw interface + * @priv: Private data pointer from eap_sim_db_init() + */ void eap_sim_db_deinit(void *priv) { struct eap_sim_db_data *data = priv; + struct eap_sim_pseudonym *p, *prev; + struct eap_sim_reauth *r, *prevr; + struct eap_sim_db_pending *pending, *prev_pending; + + eap_sim_db_close_socket(data); free(data->fname); + + p = data->pseudonyms; + while (p) { + prev = p; + p = p->next; + eap_sim_db_free_pseudonym(prev); + } + + r = data->reauths; + while (r) { + prevr = r; + r = r->next; + eap_sim_db_free_reauth(prevr); + } + + pending = data->pending; + while (pending) { + prev_pending = pending; + pending = pending->next; + free(prev_pending); + } + free(data); } -/* Get GSM triplets for user name identity (identity_len bytes). In most cases, - * the user name is '1' | IMSI, i.e., 1 followed by the IMSI in ASCII format. - * The identity may also include NAI realm (@realm). - * priv is the pointer from eap_sim_db_init(). - * Returns the number of triplets received (has to be less than or equal to - * max_chal) or -1 on error (e.g., user not found). rand, kc, and sres are - * pointers to data areas for the triplets. */ +static int eap_sim_db_send(struct eap_sim_db_data *data, const char *msg, + size_t len) +{ + int _errno = 0; + + if (send(data->sock, msg, len, 0) < 0) { + _errno = errno; + perror("send[EAP-SIM DB UNIX]"); + } + + if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL || + _errno == ECONNREFUSED) { + /* Try to reconnect */ + eap_sim_db_close_socket(data); + if (eap_sim_db_open_socket(data) < 0) + return -1; + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Reconnected to the " + "external server"); + if (send(data->sock, msg, len, 0) < 0) { + perror("send[EAP-SIM DB UNIX]"); + return -1; + } + } + + return 0; +} + + +static void eap_sim_db_expire_pending(struct eap_sim_db_data *data) +{ + /* TODO: add limit for maximum length for pending list; remove latest + * (i.e., last) entry from the list if the limit is reached; could also + * use timeout to expire pending entries */ +} + + +/** + * eap_sim_db_get_gsm_triplets - Get GSM triplets + * @priv: Private data pointer from eap_sim_db_init() + * @identity: User name identity + * @identity_len: Length of identity in bytes + * @max_chal: Maximum number of triplets + * @_rand: Buffer for RAND values + * @kc: Buffer for Kc values + * @sres: Buffer for SRES values + * @cb_session_ctx: Session callback context for get_complete_cb() + * Returns: Number of triplets received (has to be less than or equal to + * max_chal), -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not found), or + * -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this case, the + * callback function registered with eap_sim_db_init() will be called once the + * results become available. + * + * In most cases, the user name is '1' | IMSI, i.e., 1 followed by the IMSI in + * ASCII format. + * + * When using an external server for GSM triplets, this function can always + * start a request and return EAP_SIM_DB_PENDING immediately if authentication + * triplets are not available. Once the triplets are received, callback + * function registered with eap_sim_db_init() is called to notify EAP state + * machine to reprocess the message. This eap_sim_db_get_gsm_triplets() + * function will then be called again and the newly received triplets will then + * be given to the caller. + */ int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity, size_t identity_len, int max_chal, - u8 *rand, u8 *kc, u8 *sres) + u8 *_rand, u8 *kc, u8 *sres, + void *cb_session_ctx) { struct eap_sim_db_data *data = priv; - FILE *f; - int count, i; - char buf[80], *pos, *next; - - f = fopen(data->fname, "r"); - if (f == NULL) { - wpa_printf(MSG_DEBUG, "EAP-SIM DB: could not open triplet " - "file '%s'", data->fname); - return -1; - } + struct eap_sim_db_pending *entry; + int len, ret; + size_t i; + char msg[40]; - if (identity_len < 2 || identity[0] != '1') { + if (identity_len < 2 || identity[0] != EAP_SIM_PERMANENT_PREFIX || + identity_len + 1 > sizeof(entry->imsi)) { wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", identity, identity_len); - fclose(f); - return -1; + return EAP_SIM_DB_FAILURE; } identity++; identity_len--; @@ -119,90 +568,560 @@ int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity, break; } } - wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: get triplets for IMSI", + wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get GSM triplets for IMSI", identity, identity_len); - 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 (strlen(buf) != identity_len || - memcmp(buf, identity, identity_len) != 0) - continue; + entry = eap_sim_db_get_pending(data, identity, identity_len, 0); + if (entry) { + int num_chal; + if (entry->state == FAILURE) { + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> " + "failure"); + free(entry); + return EAP_SIM_DB_FAILURE; + } + + if (entry->state == PENDING) { + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> " + "still pending"); + eap_sim_db_add_pending(data, entry); + return EAP_SIM_DB_PENDING; + } + + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending entry -> " + "%d challenges", entry->u.sim.num_chal); + num_chal = entry->u.sim.num_chal; + if (num_chal > max_chal) + num_chal = max_chal; + 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); + return num_chal; + } + + if (data->sock < 0) { + if (eap_sim_db_open_socket(data) < 0) + return EAP_SIM_DB_FAILURE; + } + + len = 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); + len += identity_len; + ret = snprintf(msg + len, sizeof(msg) - len, " %d", max_chal); + if (ret < 0 || (size_t) ret >= sizeof(msg) - len) + return EAP_SIM_DB_FAILURE; + len += ret; + + wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting SIM authentication " + "data for IMSI", identity, identity_len); + if (eap_sim_db_send(data, msg, len) < 0) + return EAP_SIM_DB_FAILURE; + + entry = wpa_zalloc(sizeof(*entry)); + if (entry == NULL) + return EAP_SIM_DB_FAILURE; + + os_get_time(&entry->timestamp); + memcpy(entry->imsi, identity, identity_len); + entry->imsi_len = identity_len; + entry->cb_session_ctx = cb_session_ctx; + entry->state = PENDING; + eap_sim_db_add_pending(data, entry); + eap_sim_db_expire_pending(data); - next = strchr(pos, ':'); - if (next == NULL) - continue; - *next++ = '\0'; - if (hexstr2bin(pos, &kc[count * KC_LEN], KC_LEN) < 0) - continue; + return EAP_SIM_DB_PENDING; +} - pos = next; - next = strchr(pos, ':'); - if (next == NULL) - continue; - *next++ = '\0'; - if (hexstr2bin(pos, &sres[count * SRES_LEN], SRES_LEN) < 0) - continue; - if (hexstr2bin(next, &rand[count * RAND_LEN], RAND_LEN) < 0) - continue; +static struct eap_sim_pseudonym * +eap_sim_db_get_pseudonym(struct eap_sim_db_data *data, const u8 *identity, + size_t identity_len) +{ + char *pseudonym; + size_t len; + struct eap_sim_pseudonym *p; - count++; + if (identity_len == 0 || + (identity[0] != EAP_SIM_PSEUDONYM_PREFIX && + identity[0] != EAP_AKA_PSEUDONYM_PREFIX)) + return NULL; + + /* Remove possible realm from identity */ + len = 0; + while (len < identity_len) { + if (identity[len] == '@') + break; + len++; } - fclose(f); + pseudonym = malloc(len + 1); + if (pseudonym == NULL) + return NULL; + memcpy(pseudonym, identity, len); + pseudonym[len] = '\0'; - if (count == 0) { - wpa_printf(MSG_DEBUG, "EAP-SIM DB: no triplets found"); - count = -1; + p = data->pseudonyms; + while (p) { + if (strcmp(p->pseudonym, pseudonym) == 0) + break; + p = p->next; } - return count; + free(pseudonym); + + return p; } -/* Verify whether the given user identity (identity_len bytes) is known. In - * most cases, the user name is '1' | IMSI, i.e., 1 followed by the IMSI in - * ASCII format. - * priv is the pointer from eap_sim_db_init(). - * Returns 0 if the user is found and GSM triplets would be available for it or - * -1 on error (e.g., user not found or no triplets available). */ +static struct eap_sim_pseudonym * +eap_sim_db_get_pseudonym_id(struct eap_sim_db_data *data, const u8 *identity, + size_t identity_len) +{ + struct eap_sim_pseudonym *p; + + if (identity_len == 0 || + (identity[0] != EAP_SIM_PERMANENT_PREFIX && + identity[0] != EAP_AKA_PERMANENT_PREFIX)) + return NULL; + + p = data->pseudonyms; + while (p) { + if (identity_len == p->identity_len && + memcmp(p->identity, identity, identity_len) == 0) + break; + p = p->next; + } + + return p; +} + + +static struct eap_sim_reauth * +eap_sim_db_get_reauth(struct eap_sim_db_data *data, const u8 *identity, + size_t identity_len) +{ + char *reauth_id; + size_t len; + struct eap_sim_reauth *r; + + if (identity_len == 0 || + (identity[0] != EAP_SIM_REAUTH_ID_PREFIX && + identity[0] != EAP_AKA_REAUTH_ID_PREFIX)) + return NULL; + + /* Remove possible realm from identity */ + len = 0; + while (len < identity_len) { + if (identity[len] == '@') + break; + len++; + } + + reauth_id = malloc(len + 1); + if (reauth_id == NULL) + return NULL; + memcpy(reauth_id, identity, len); + reauth_id[len] = '\0'; + + r = data->reauths; + while (r) { + if (strcmp(r->reauth_id, reauth_id) == 0) + break; + r = r->next; + } + + free(reauth_id); + + return r; +} + + +static struct eap_sim_reauth * +eap_sim_db_get_reauth_id(struct eap_sim_db_data *data, const u8 *identity, + size_t identity_len) +{ + struct eap_sim_pseudonym *p; + struct eap_sim_reauth *r; + + if (identity_len == 0) + return NULL; + + p = eap_sim_db_get_pseudonym(data, identity, identity_len); + if (p == NULL) + p = eap_sim_db_get_pseudonym_id(data, identity, identity_len); + if (p) { + identity = p->identity; + identity_len = p->identity_len; + } + + r = data->reauths; + while (r) { + if (identity_len == r->identity_len && + memcmp(r->identity, identity, identity_len) == 0) + break; + r = r->next; + } + + return r; +} + + +/** + * eap_sim_db_identity_known - Verify whether the given identity is known + * @priv: Private data pointer from eap_sim_db_init() + * @identity: User name identity + * @identity_len: Length of identity in bytes + * Returns: 0 if the user is found or -1 on failure + * + * In most cases, the user name is ['0','1'] | IMSI, i.e., 1 followed by the + * IMSI in ASCII format, ['2','3'] | pseudonym, or ['4','5'] | reauth_id. + */ int eap_sim_db_identity_known(void *priv, const u8 *identity, size_t identity_len) { struct eap_sim_db_data *data = priv; - FILE *f; - char buf[80], *pos; - int i; - if (identity_len < 1 || identity[0] != '1') { + if (identity == NULL || identity_len < 2) + return -1; + + if (identity[0] == EAP_SIM_PSEUDONYM_PREFIX || + identity[0] == EAP_AKA_PSEUDONYM_PREFIX) { + struct eap_sim_pseudonym *p = + eap_sim_db_get_pseudonym(data, identity, identity_len); + return p ? 0 : -1; + } + + if (identity[0] == EAP_SIM_REAUTH_ID_PREFIX || + identity[0] == EAP_AKA_REAUTH_ID_PREFIX) { + struct eap_sim_reauth *r = + eap_sim_db_get_reauth(data, identity, identity_len); + return r ? 0 : -1; + } + + if (identity[0] != EAP_SIM_PERMANENT_PREFIX && + identity[0] != EAP_AKA_PERMANENT_PREFIX) { + /* Unknown identity prefix */ + return -1; + } + + /* TODO: Should consider asking HLR/AuC gateway whether this permanent + * identity is known. If it is, EAP-SIM/AKA can skip identity request. + * In case of EAP-AKA, this would reduce number of needed round-trips. + * Ideally, this would be done with one wait, i.e., just request + * authentication data and store it for the next use. This would then + * need to use similar pending-request functionality as the normal + * request for authentication data at later phase. + */ + return -1; +} + + +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))) + return NULL; + id = malloc(sizeof(buf) * 2 + 2); + if (id == NULL) + return NULL; + + pos = id; + end = id + sizeof(buf) * 2 + 2; + *pos++ = prefix; + pos += wpa_snprintf_hex(pos, end - pos, buf, sizeof(buf)); + + return id; +} + + +/** + * eap_sim_db_get_next_pseudonym - EAP-SIM DB: Get next pseudonym + * @priv: Private data pointer from eap_sim_db_init() + * @aka: Using EAP-AKA instead of EAP-SIM + * Returns: Next pseudonym (allocated string) or %NULL on failure + * + * This function is used to generate a pseudonym for EAP-SIM. The returned + * pseudonym is not added to database at this point; it will need to be added + * with eap_sim_db_add_pseudonym() once the authentication has been completed + * successfully. Caller is responsible for freeing the returned buffer. + */ +char * eap_sim_db_get_next_pseudonym(void *priv, int aka) +{ + struct eap_sim_db_data *data = priv; + return eap_sim_db_get_next(data, aka ? EAP_AKA_PSEUDONYM_PREFIX : + EAP_SIM_PSEUDONYM_PREFIX); +} + + +/** + * eap_sim_db_get_next_reauth_id - EAP-SIM DB: Get next reauth_id + * @priv: Private data pointer from eap_sim_db_init() + * @aka: Using EAP-AKA instead of EAP-SIM + * Returns: Next reauth_id (allocated string) or %NULL on failure + * + * This function is used to generate a fast re-authentication identity for + * EAP-SIM. The returned reauth_id is not added to database at this point; it + * will need to be added with eap_sim_db_add_reauth() once the authentication + * has been completed successfully. Caller is responsible for freeing the + * returned buffer. + */ +char * eap_sim_db_get_next_reauth_id(void *priv, int aka) +{ + struct eap_sim_db_data *data = priv; + return eap_sim_db_get_next(data, aka ? EAP_AKA_REAUTH_ID_PREFIX : + EAP_SIM_REAUTH_ID_PREFIX); +} + + +/** + * eap_sim_db_add_pseudonym - EAP-SIM DB: Add new pseudonym + * @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 + * @pseudonym: Pseudonym for this user. This needs to be an allocated buffer, + * e.g., return value from eap_sim_db_get_next_pseudonym(). Caller must not + * free it. + * Returns: 0 on success, -1 on failure + * + * This function adds a new pseudonym for EAP-SIM user. EAP-SIM DB is + * responsible of freeing pseudonym buffer once it is not needed anymore. + */ +int eap_sim_db_add_pseudonym(void *priv, const u8 *identity, + size_t identity_len, char *pseudonym) +{ + struct eap_sim_db_data *data = priv; + struct eap_sim_pseudonym *p; + wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Add pseudonym for identity", + identity, identity_len); + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pseudonym: %s", pseudonym); + + /* TODO: could store last two pseudonyms */ + p = eap_sim_db_get_pseudonym(data, identity, identity_len); + if (p == NULL) + p = eap_sim_db_get_pseudonym_id(data, identity, identity_len); + + if (p) { + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous " + "pseudonym: %s", p->pseudonym); + free(p->pseudonym); + p->pseudonym = pseudonym; + return 0; + } + + p = wpa_zalloc(sizeof(*p)); + if (p == NULL) { + free(pseudonym); return -1; } - f = fopen(data->fname, "r"); - if (f == NULL) { - wpa_printf(MSG_DEBUG, "EAP-SIM DB: could not open triplet " - "file '%s'", data->fname); + p->next = data->pseudonyms; + p->identity = malloc(identity_len); + if (p->identity == NULL) { + free(p); + free(pseudonym); return -1; } + memcpy(p->identity, identity, identity_len); + p->identity_len = identity_len; + p->pseudonym = pseudonym; + data->pseudonyms = p; + + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new pseudonym entry"); + return 0; +} + + +/** + * 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) +{ + 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); + + r = eap_sim_db_get_reauth(data, identity, identity_len); + if (r == NULL) + r = eap_sim_db_get_reauth_id(data, identity, identity_len); + + if (r) { + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Replacing previous " + "reauth_id: %s", r->reauth_id); + free(r->reauth_id); + r->reauth_id = reauth_id; + } else { + r = wpa_zalloc(sizeof(*r)); + if (r == NULL) { + free(reauth_id); + return -1; + } + + r->next = data->reauths; + r->identity = malloc(identity_len); + if (r->identity == NULL) { + free(r); + free(reauth_id); + return -1; + } + memcpy(r->identity, identity, identity_len); + r->identity_len = identity_len; + r->reauth_id = reauth_id; + data->reauths = r; + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Added new reauth entry"); + } + + r->counter = counter; + memcpy(r->mk, mk, EAP_SIM_MK_LEN); + + return 0; +} + + +/** + * eap_sim_db_get_permanent - EAP-SIM DB: Get permanent identity + * @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 + * @len: Buffer for length of the returned permanent identity + * Returns: Pointer to the permanent identity, or %NULL if not found + */ +const u8 * eap_sim_db_get_permanent(void *priv, const u8 *identity, + size_t identity_len, size_t *len) +{ + struct eap_sim_db_data *data = priv; + struct eap_sim_pseudonym *p; + + if (identity == NULL) + return NULL; + + p = eap_sim_db_get_pseudonym(data, identity, identity_len); + if (p == NULL) + p = eap_sim_db_get_pseudonym_id(data, identity, identity_len); + if (p == NULL) + return NULL; + + *len = p->identity_len; + return p->identity; +} + + +/** + * eap_sim_db_get_reauth_entry - EAP-SIM DB: Get re-authentication entry + * @priv: Private data pointer from eap_sim_db_init() + * @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 * +eap_sim_db_get_reauth_entry(void *priv, const u8 *identity, + size_t identity_len) +{ + struct eap_sim_db_data *data = priv; + struct eap_sim_reauth *r; + + if (identity == NULL) + return NULL; + r = eap_sim_db_get_reauth(data, identity, identity_len); + if (r == NULL) + r = eap_sim_db_get_reauth_id(data, identity, identity_len); + return r; +} + + +/** + * eap_sim_db_remove_reauth - EAP-SIM DB: Remove re-authentication entry + * @priv: Private data pointer from eap_sim_db_init() + * @reauth: Pointer to re-authentication entry from + * eap_sim_db_get_reauth_entry() + */ +void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth) +{ + struct eap_sim_db_data *data = priv; + struct eap_sim_reauth *r, *prev = NULL; + r = data->reauths; + while (r) { + if (r == reauth) { + if (prev) + prev->next = r->next; + else + data->reauths = r->next; + eap_sim_db_free_reauth(r); + return; + } + prev = r; + r = r->next; + } +} + + +/** + * eap_sim_db_get_aka_auth - Get AKA authentication values + * @priv: Private data pointer from eap_sim_db_init() + * @identity: User name identity + * @identity_len: Length of identity in bytes + * @_rand: Buffer for RAND value + * @autn: Buffer for AUTN value + * @ik: Buffer for IK value + * @ck: Buffer for CK value + * @res: Buffer for RES value + * @res_len: Buffer for RES length + * @cb_session_ctx: Session callback context for get_complete_cb() + * Returns: 0 on success, -1 (EAP_SIM_DB_FAILURE) on error (e.g., user not + * found), or -2 (EAP_SIM_DB_PENDING) if results are not yet available. In this + * case, the callback function registered with eap_sim_db_init() will be + * called once the results become available. + * + * In most cases, the user name is '0' | IMSI, i.e., 0 followed by the IMSI in + * ASCII format. + * + * When using an external server for AKA authentication, this function can + * always start a request and return EAP_SIM_DB_PENDING immediately if + * authentication triplets are not available. Once the authentication data are + * received, callback function registered with eap_sim_db_init() is called to + * notify EAP state machine to reprocess the message. This + * eap_sim_db_get_aka_auth() function will then be called again and the newly + * received triplets will then be given to the caller. + */ +int eap_sim_db_get_aka_auth(void *priv, const u8 *identity, + size_t identity_len, u8 *_rand, u8 *autn, u8 *ik, + u8 *ck, u8 *res, size_t *res_len, + void *cb_session_ctx) +{ + struct eap_sim_db_data *data = priv; + struct eap_sim_db_pending *entry; + int len; + size_t i; + char msg[40]; - if (identity_len < 2 || identity[0] != '1') { + if (identity_len < 2 || identity == NULL || + identity[0] != EAP_AKA_PERMANENT_PREFIX || + identity_len + 1 > sizeof(entry->imsi)) { wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", identity, identity_len); - return -1; + return EAP_SIM_DB_FAILURE; } identity++; identity_len--; @@ -212,32 +1131,123 @@ int eap_sim_db_identity_known(void *priv, const u8 *identity, break; } } + wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: Get AKA auth for IMSI", + identity, identity_len); - while (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; + entry = eap_sim_db_get_pending(data, identity, identity_len, 1); + if (entry) { + if (entry->state == FAILURE) { + free(entry); + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Failure"); + return EAP_SIM_DB_FAILURE; + } - pos = strchr(buf, ':'); - if (pos == NULL) - continue; - *pos++ = '\0'; - if (strlen(buf) != identity_len || - memcmp(buf, identity, identity_len) != 0) - continue; + if (entry->state == PENDING) { + eap_sim_db_add_pending(data, entry); + wpa_printf(MSG_DEBUG, "EAP-SIM DB: Pending"); + return EAP_SIM_DB_PENDING; + } - fclose(f); + 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); + *res_len = entry->u.aka.res_len; + free(entry); return 0; } - /* IMSI not found */ + if (data->sock < 0) { + if (eap_sim_db_open_socket(data) < 0) + return EAP_SIM_DB_FAILURE; + } - fclose(f); - return -1; + len = 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); + len += identity_len; + + wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: requesting AKA authentication " + "data for IMSI", identity, identity_len); + if (eap_sim_db_send(data, msg, len) < 0) + return EAP_SIM_DB_FAILURE; + + entry = wpa_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); + entry->imsi_len = identity_len; + entry->cb_session_ctx = cb_session_ctx; + entry->state = PENDING; + eap_sim_db_add_pending(data, entry); + eap_sim_db_expire_pending(data); + + return EAP_SIM_DB_PENDING; +} + + +/** + * eap_sim_db_resynchronize - Resynchronize AKA AUTN + * @priv: Private data pointer from eap_sim_db_init() + * @identity: User name identity + * @identity_len: Length of identity in bytes + * @auts: AUTS value from the peer + * @_rand: RAND value used in the rejected message + * Returns: 0 on success, -1 on failure + * + * This function is called when the peer reports synchronization failure in the + * AUTN value by sending AUTS. The AUTS and RAND values should be sent to + * HLR/AuC to allow it to resynchronize with the peer. After this, + * eap_sim_db_get_aka_auth() will be called again to to fetch updated + * RAND/AUTN values for the next challenge. + */ +int eap_sim_db_resynchronize(void *priv, const u8 *identity, + size_t identity_len, const u8 *auts, + const u8 *_rand) +{ + struct eap_sim_db_data *data = priv; + + if (identity_len < 2 || identity[0] != EAP_AKA_PERMANENT_PREFIX || + identity_len > 20) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", + identity, identity_len); + return -1; + } + + if (data->sock >= 0) { + char msg[100]; + int len, ret; + + len = snprintf(msg, sizeof(msg), "AKA-AUTS "); + if (len < 0 || len + identity_len - 1 >= sizeof(msg)) + return -1; + memcpy(msg + len, identity + 1, identity_len - 1); + len += identity_len - 1; + + ret = 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, " "); + if (ret < 0 || (size_t) ret >= sizeof(msg) - len) + return -1; + len += ret; + len += wpa_snprintf_hex(msg + len, sizeof(msg) - len, + _rand, EAP_AKA_RAND_LEN); + wpa_hexdump(MSG_DEBUG, "EAP-SIM DB: reporting AKA AUTS for " + "IMSI", identity + 1, identity_len - 1); + if (eap_sim_db_send(data, msg, len) < 0) + return -1; + } + + return 0; } diff --git a/contrib/hostapd/eap_sim_db.h b/contrib/hostapd/eap_sim_db.h index 57a9871..6754bc3 100644 --- a/contrib/hostapd/eap_sim_db.h +++ b/contrib/hostapd/eap_sim_db.h @@ -1,39 +1,94 @@ +/* + * hostapd / EAP-SIM database/authenticator gateway + * 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 EAP_SIM_DB_H #define EAP_SIM_DB_H #ifdef EAP_SIM -/* Initialize EAP-SIM database/authentication gateway interface. - * Returns pointer to a private data structure. */ -void * eap_sim_db_init(const char *config); +#include "eap_sim_common.h" + +/* Identity prefixes */ +#define EAP_SIM_PERMANENT_PREFIX '1' +#define EAP_SIM_PSEUDONYM_PREFIX '3' +#define EAP_SIM_REAUTH_ID_PREFIX '5' +#define EAP_AKA_PERMANENT_PREFIX '0' +#define EAP_AKA_PSEUDONYM_PREFIX '2' +#define EAP_AKA_REAUTH_ID_PREFIX '4' + +void * eap_sim_db_init(const char *config, + void (*get_complete_cb)(void *ctx, void *session_ctx), + void *ctx); -/* Deinitialize EAP-SIM database/authentication gateway interface. - * priv is the pointer from eap_sim_db_init(). */ void eap_sim_db_deinit(void *priv); -/* Get GSM triplets for user name identity (identity_len bytes). In most cases, - * the user name is '1' | IMSI, i.e., 1 followed by the IMSI in ASCII format. - * priv is the pointer from eap_sim_db_init(). - * Returns the number of triplets received (has to be less than or equal to - * max_chal) or -1 on error (e.g., user not found). rand, kc, and sres are - * pointers to data areas for the triplets. */ int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity, size_t identity_len, int max_chal, - u8 *rand, u8 *kc, u8 *sres); - -/* Verify whether the given user identity (identity_len bytes) is known. In - * most cases, the user name is '1' | IMSI, i.e., 1 followed by the IMSI in - * ASCII format. - * priv is the pointer from eap_sim_db_init(). - * Returns 0 if the user is found and GSM triplets would be available for it or - * -1 on error (e.g., user not found or no triplets available). */ + u8 *_rand, u8 *kc, u8 *sres, + void *cb_session_ctx); + +#define EAP_SIM_DB_FAILURE -1 +#define EAP_SIM_DB_PENDING -2 + int eap_sim_db_identity_known(void *priv, const u8 *identity, size_t identity_len); +char * eap_sim_db_get_next_pseudonym(void *priv, int aka); + +char * eap_sim_db_get_next_reauth_id(void *priv, int aka); + +int eap_sim_db_add_pseudonym(void *priv, const u8 *identity, + size_t identity_len, char *pseudonym); + +int eap_sim_db_add_reauth(void *priv, const u8 *identity, + size_t identity_len, char *reauth_id, u16 counter, + const u8 *mk); + +const u8 * eap_sim_db_get_permanent(void *priv, const u8 *identity, + size_t identity_len, size_t *len); + +struct eap_sim_reauth { + struct eap_sim_reauth *next; + u8 *identity; + size_t identity_len; + char *reauth_id; + u16 counter; + u8 mk[EAP_SIM_MK_LEN]; +}; + +struct eap_sim_reauth * +eap_sim_db_get_reauth_entry(void *priv, const u8 *identity, + size_t identity_len); + +void eap_sim_db_remove_reauth(void *priv, struct eap_sim_reauth *reauth); + +int eap_sim_db_get_aka_auth(void *priv, const u8 *identity, + size_t identity_len, u8 *_rand, u8 *autn, u8 *ik, + u8 *ck, u8 *res, size_t *res_len, + void *cb_session_ctx); + +int eap_sim_db_resynchronize(void *priv, const u8 *identity, + size_t identity_len, const u8 *auts, + const u8 *_rand); + #else /* EAP_SIM */ -static inline void * eap_sim_db_init(const char *config) +static inline void * +eap_sim_db_init(const char *config, + void (*get_complete_cb)(void *ctx, void *session_ctx), + void *ctx) { - return NULL; + return (void *) 1; } static inline void eap_sim_db_deinit(void *priv) diff --git a/contrib/hostapd/eap_tls.c b/contrib/hostapd/eap_tls.c index bf76f5a..fc85337 100644 --- a/contrib/hostapd/eap_tls.c +++ b/contrib/hostapd/eap_tls.c @@ -1,6 +1,6 @@ /* * hostapd / EAP-TLS (RFC 2716) - * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.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 @@ -12,10 +12,7 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <netinet/in.h> +#include "includes.h" #include "hostapd.h" #include "common.h" @@ -37,10 +34,9 @@ static void * eap_tls_init(struct eap_sm *sm) { struct eap_tls_data *data; - data = malloc(sizeof(*data)); + data = wpa_zalloc(sizeof(*data)); if (data == NULL) - return data; - memset(data, 0, sizeof(*data)); + return NULL; data->state = START; if (eap_tls_ssl_init(sm, &data->ssl, 1)) { @@ -134,12 +130,11 @@ static Boolean eap_tls_check(struct eap_sm *sm, void *priv, { struct eap_hdr *resp; u8 *pos; - size_t len; resp = (struct eap_hdr *) respData; pos = (u8 *) (resp + 1); if (respDataLen < sizeof(*resp) + 2 || *pos != EAP_TYPE_TLS || - (len = ntohs(resp->length)) > respDataLen) { + (ntohs(resp->length)) > respDataLen) { wpa_printf(MSG_INFO, "EAP-TLS: Invalid frame"); return TRUE; } @@ -231,6 +226,38 @@ static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len) } +static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_tls_data *data = priv; + u8 *eapKeyData, *emsk; + + if (data->state != SUCCESS) + return NULL; + + eapKeyData = eap_tls_derive_key(sm, &data->ssl, + "client EAP encryption", + EAP_TLS_KEY_LEN + EAP_EMSK_LEN); + if (eapKeyData) { + emsk = malloc(EAP_EMSK_LEN); + if (emsk) + memcpy(emsk, eapKeyData + EAP_TLS_KEY_LEN, + EAP_EMSK_LEN); + free(eapKeyData); + } else + emsk = NULL; + + if (emsk) { + *len = EAP_EMSK_LEN; + wpa_hexdump(MSG_DEBUG, "EAP-TLS: Derived EMSK", + emsk, EAP_EMSK_LEN); + } else { + wpa_printf(MSG_DEBUG, "EAP-TLS: Failed to derive EMSK"); + } + + return emsk; +} + + static Boolean eap_tls_isSuccess(struct eap_sm *sm, void *priv) { struct eap_tls_data *data = priv; @@ -238,16 +265,28 @@ static Boolean eap_tls_isSuccess(struct eap_sm *sm, void *priv) } -const struct eap_method eap_method_tls = +int eap_server_tls_register(void) { - .method = EAP_TYPE_TLS, - .name = "TLS", - .init = eap_tls_init, - .reset = eap_tls_reset, - .buildReq = eap_tls_buildReq, - .check = eap_tls_check, - .process = eap_tls_process, - .isDone = eap_tls_isDone, - .getKey = eap_tls_getKey, - .isSuccess = eap_tls_isSuccess, -}; + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_TLS, "TLS"); + if (eap == NULL) + return -1; + + eap->init = eap_tls_init; + eap->reset = eap_tls_reset; + eap->buildReq = eap_tls_buildReq; + eap->check = eap_tls_check; + eap->process = eap_tls_process; + eap->isDone = eap_tls_isDone; + eap->getKey = eap_tls_getKey; + eap->isSuccess = eap_tls_isSuccess; + eap->get_emsk = eap_tls_get_emsk; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/contrib/hostapd/eap_tls_common.c b/contrib/hostapd/eap_tls_common.c index d573064..2a089d4 100644 --- a/contrib/hostapd/eap_tls_common.c +++ b/contrib/hostapd/eap_tls_common.c @@ -1,6 +1,6 @@ /* * hostapd / EAP-TLS/PEAP/TTLS common functions - * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * 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 @@ -12,10 +12,7 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <netinet/in.h> +#include "includes.h" #include "hostapd.h" #include "common.h" @@ -71,31 +68,42 @@ u8 * eap_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, char *label, size_t len) { struct tls_keys keys; - u8 *random; - u8 *out; + u8 *rnd = NULL, *out; - if (tls_connection_get_keys(sm->ssl_ctx, data->conn, &keys)) - return NULL; out = malloc(len); - random = malloc(keys.client_random_len + keys.server_random_len); - if (out == NULL || random == NULL) { - free(out); - free(random); + if (out == NULL) return NULL; - } - memcpy(random, keys.client_random, keys.client_random_len); - memcpy(random + keys.client_random_len, keys.server_random, + + 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, random, keys.client_random_len + - keys.server_random_len, out, len)) { - free(random); - free(out); - return NULL; - } - free(random); + 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; } @@ -105,6 +113,16 @@ int eap_tls_data_reassemble(struct eap_sm *sm, struct eap_ssl_data *data, u8 *buf; if (data->tls_in_left > *in_len || data->tls_in) { + 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); diff --git a/contrib/hostapd/eap_tls_common.h b/contrib/hostapd/eap_tls_common.h index 659ee84..6c9d696 100644 --- a/contrib/hostapd/eap_tls_common.h +++ b/contrib/hostapd/eap_tls_common.h @@ -1,3 +1,17 @@ +/* + * 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 diff --git a/contrib/hostapd/eap_tlv.c b/contrib/hostapd/eap_tlv.c index b7609dc..b48e186 100644 --- a/contrib/hostapd/eap_tlv.c +++ b/contrib/hostapd/eap_tlv.c @@ -1,6 +1,6 @@ /* * hostapd / EAP-TLV (draft-josefsson-pppext-eap-tls-eap-07.txt) - * Copyright (c) 2004, Jouni Malinen <jkmaline@cc.hut.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 @@ -12,10 +12,7 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <netinet/in.h> +#include "includes.h" #include "hostapd.h" #include "common.h" @@ -45,10 +42,9 @@ static void * eap_tlv_init(struct eap_sm *sm) { struct eap_tlv_data *data; - data = malloc(sizeof(*data)); + data = wpa_zalloc(sizeof(*data)); if (data == NULL) - return data; - memset(data, 0, sizeof(*data)); + return NULL; data->state = CONTINUE; return data; @@ -103,12 +99,11 @@ static Boolean eap_tlv_check(struct eap_sm *sm, void *priv, { struct eap_hdr *resp; u8 *pos; - size_t len; resp = (struct eap_hdr *) respData; pos = (u8 *) (resp + 1); if (respDataLen < sizeof(*resp) + 1 || *pos != EAP_TYPE_TLV || - (len = ntohs(resp->length)) > respDataLen) { + (ntohs(resp->length)) > respDataLen) { wpa_printf(MSG_INFO, "EAP-TLV: Invalid frame"); return TRUE; } @@ -123,14 +118,12 @@ static void eap_tlv_process(struct eap_sm *sm, void *priv, struct eap_tlv_data *data = priv; struct eap_hdr *resp; u8 *pos; - int len; size_t left; u8 *result_tlv = NULL; size_t result_tlv_len = 0; int tlv_type, mandatory, tlv_len; resp = (struct eap_hdr *) respData; - len = ntohs(resp->length); pos = (u8 *) (resp + 1); /* Parse TLVs */ @@ -145,7 +138,7 @@ static void eap_tlv_process(struct eap_sm *sm, void *priv, tlv_len = ((int) pos[2] << 8) | pos[3]; pos += 4; left -= 4; - if (tlv_len > left) { + if ((size_t) tlv_len > left) { wpa_printf(MSG_DEBUG, "EAP-TLV: TLV underrun " "(tlv_len=%d left=%lu)", tlv_len, (unsigned long) left); @@ -234,15 +227,26 @@ static Boolean eap_tlv_isSuccess(struct eap_sm *sm, void *priv) } -const struct eap_method eap_method_tlv = +int eap_server_tlv_register(void) { - .method = EAP_TYPE_TLV, - .name = "TLV", - .init = eap_tlv_init, - .reset = eap_tlv_reset, - .buildReq = eap_tlv_buildReq, - .check = eap_tlv_check, - .process = eap_tlv_process, - .isDone = eap_tlv_isDone, - .isSuccess = eap_tlv_isSuccess, -}; + 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/eap_ttls.c b/contrib/hostapd/eap_ttls.c index 569b1c3..1c0f17e 100644 --- a/contrib/hostapd/eap_ttls.c +++ b/contrib/hostapd/eap_ttls.c @@ -1,6 +1,6 @@ /* * hostapd / EAP-TTLS (draft-ietf-pppext-eap-ttls-05.txt) - * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.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 @@ -12,10 +12,7 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <netinet/in.h> +#include "includes.h" #include "hostapd.h" #include "common.h" @@ -23,11 +20,20 @@ #include "eap_tls_common.h" #include "ms_funcs.h" #include "md5.h" +#include "sha1.h" #include "crypto.h" #include "tls.h" #include "eap_ttls.h" -#define EAP_TTLS_VERSION 0 + +/* Maximum supported PEAP version + * 0 = draft-ietf-pppext-eap-ttls-03.txt / draft-funk-eap-ttls-v0-00.txt + * 1 = draft-funk-eap-ttls-v1-00.txt + */ +#define EAP_TTLS_VERSION 0 /* TTLSv1 implementation is not yet complete */ + + +#define MSCHAPV2_KEY_LEN 16 static void eap_ttls_reset(struct eap_sm *sm, void *priv); @@ -37,15 +43,17 @@ struct eap_ttls_data { struct eap_ssl_data ssl; enum { START, PHASE1, PHASE2_START, PHASE2_METHOD, - PHASE2_MSCHAPV2_RESP, SUCCESS, FAILURE + PHASE2_MSCHAPV2_RESP, PHASE_FINISHED, SUCCESS, FAILURE } state; int ttls_version; + int force_version; const struct eap_method *phase2_method; void *phase2_priv; int mschapv2_resp_ok; u8 mschapv2_auth_response[20]; u8 mschapv2_ident; + int tls_ia_configured; }; @@ -62,6 +70,8 @@ static const char * eap_ttls_state_txt(int state) return "PHASE2_METHOD"; case PHASE2_MSCHAPV2_RESP: return "PHASE2_MSCHAPV2_RESP"; + case PHASE_FINISHED: + return "PHASE_FINISHED"; case SUCCESS: return "SUCCESS"; case FAILURE: @@ -173,11 +183,16 @@ static int eap_ttls_avp_parse(u8 *buf, size_t len, struct eap_ttls_avp *parse) 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) { + if ((int) avp_length > left) { wpa_printf(MSG_WARNING, "EAP-TTLS: AVP overflow " "(len=%d, left=%d) - dropped", (int) avp_length, left); - return -1; + goto fail; + } + if (avp_length < sizeof(*avp)) { + wpa_printf(MSG_WARNING, "EAP-TTLS: Invalid AVP length " + "%d", avp_length); + goto fail; } dpos = (u8 *) (avp + 1); dlen = avp_length - sizeof(*avp); @@ -185,7 +200,7 @@ static int eap_ttls_avp_parse(u8 *buf, size_t len, struct eap_ttls_avp *parse) if (dlen < 4) { wpa_printf(MSG_WARNING, "EAP-TTLS: vendor AVP " "underflow"); - return -1; + goto fail; } vendor_id = be_to_host32(* (u32 *) dpos); wpa_printf(MSG_DEBUG, "EAP-TTLS: AVP vendor_id %d", @@ -204,7 +219,7 @@ static int eap_ttls_avp_parse(u8 *buf, size_t len, struct eap_ttls_avp *parse) wpa_printf(MSG_WARNING, "EAP-TTLS: " "failed to allocate memory " "for Phase 2 EAP data"); - return -1; + goto fail; } memcpy(parse->eap, dpos, dlen); parse->eap_len = dlen; @@ -215,9 +230,7 @@ static int eap_ttls_avp_parse(u8 *buf, size_t len, struct eap_ttls_avp *parse) wpa_printf(MSG_WARNING, "EAP-TTLS: " "failed to allocate memory " "for Phase 2 EAP data"); - free(parse->eap); - parse->eap = NULL; - return -1; + goto fail; } memcpy(neweap + parse->eap_len, dpos, dlen); parse->eap = neweap; @@ -281,7 +294,7 @@ static int eap_ttls_avp_parse(u8 *buf, size_t len, struct eap_ttls_avp *parse) wpa_printf(MSG_WARNING, "EAP-TTLS: Unsupported " "mandatory AVP code %d vendor_id %d - " "dropped", (int) avp_code, (int) vendor_id); - return -1; + goto fail; } else { wpa_printf(MSG_DEBUG, "EAP-TTLS: Ignoring unsupported " "AVP code %d vendor_id %d", @@ -294,6 +307,65 @@ static int eap_ttls_avp_parse(u8 *buf, size_t len, struct eap_ttls_avp *parse) } return 0; + +fail: + free(parse->eap); + parse->eap = NULL; + return -1; +} + + +static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm, + struct eap_ttls_data *data, size_t len) +{ + struct tls_keys keys; + u8 *challenge, *rnd; + + if (data->ttls_version == 0) { + return eap_tls_derive_key(sm, &data->ssl, "ttls challenge", + len); + } + + 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 = malloc(keys.client_random_len + keys.server_random_len); + challenge = malloc(len); + if (rnd == NULL || challenge == NULL) { + wpa_printf(MSG_INFO, "EAP-TTLS: No memory for implicit " + "challenge derivation"); + free(rnd); + 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); + + 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"); + free(rnd); + free(challenge); + return NULL; + } + + free(rnd); + + wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived implicit challenge", + challenge, len); + + return challenge; } @@ -301,13 +373,31 @@ static void * eap_ttls_init(struct eap_sm *sm) { struct eap_ttls_data *data; - data = malloc(sizeof(*data)); + data = wpa_zalloc(sizeof(*data)); if (data == NULL) - return data; - memset(data, 0, sizeof(*data)); + return NULL; data->ttls_version = EAP_TTLS_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-TTLS: forcing version %d", + data->force_version); + data->ttls_version = data->force_version; + } data->state = START; + if (!(tls_capabilities(sm->ssl_ctx) & TLS_CAPABILITY_IA) && + data->ttls_version > 0) { + if (data->force_version > 0) { + wpa_printf(MSG_INFO, "EAP-TTLS: Forced TTLSv%d and " + "TLS library does not support TLS/IA.", + data->force_version); + eap_ttls_reset(sm, data); + return NULL; + } + data->ttls_version = 0; + } + if (eap_tls_ssl_init(sm, &data->ssl, 0)) { wpa_printf(MSG_INFO, "EAP-TTLS: Failed to initialize SSL."); eap_ttls_reset(sm, data); @@ -456,23 +546,24 @@ static u8 * eap_ttls_build_phase2_mschapv2(struct eap_sm *sm, int id, size_t *reqDataLen) { u8 *req, *encr_req, *pos, *end; + int ret; size_t req_len; - int i; pos = req = malloc(100); if (req == NULL) return NULL; - end = req + 200; + end = req + 100; if (data->mschapv2_resp_ok) { pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP2_SUCCESS, RADIUS_VENDOR_ID_MICROSOFT, 1, 43); *pos++ = data->mschapv2_ident; - pos += snprintf((char *) pos, end - pos, "S="); - for (i = 0; i < sizeof(data->mschapv2_auth_response); i++) { - pos += snprintf((char *) pos, end - pos, "%02X", - data->mschapv2_auth_response[i]); - } + ret = snprintf((char *) pos, end - pos, "S="); + if (ret >= 0 && ret < end - pos) + pos += ret; + pos += wpa_snprintf_hex_uppercase( + (char *) pos, end - pos, data->mschapv2_auth_response, + sizeof(data->mschapv2_auth_response)); } else { pos = eap_ttls_avp_hdr(pos, RADIUS_ATTR_MS_CHAP_ERROR, RADIUS_VENDOR_ID_MICROSOFT, 1, 6); @@ -492,6 +583,43 @@ static u8 * eap_ttls_build_phase2_mschapv2(struct eap_sm *sm, } +static u8 * eap_ttls_build_phase_finished(struct eap_sm *sm, + struct eap_ttls_data *data, + int id, int final, + size_t *reqDataLen) +{ + int len; + struct eap_hdr *req; + u8 *pos; + const int max_len = 300; + + len = sizeof(struct eap_hdr) + 2 + max_len; + req = malloc(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); + if (len < 0) { + free(req); + return NULL; + } + + *reqDataLen = sizeof(struct eap_hdr) + 2 + len; + req->length = host_to_be16(*reqDataLen); + + return (u8 *) req; +} + + static u8 * eap_ttls_buildReq(struct eap_sm *sm, void *priv, int id, size_t *reqDataLen) { @@ -507,6 +635,9 @@ static u8 * eap_ttls_buildReq(struct eap_sm *sm, void *priv, int id, case PHASE2_MSCHAPV2_RESP: return eap_ttls_build_phase2_mschapv2(sm, data, id, reqDataLen); + case PHASE_FINISHED: + return eap_ttls_build_phase_finished(sm, data, id, 1, + reqDataLen); default: wpa_printf(MSG_DEBUG, "EAP-TTLS: %s - unexpected state %d", __func__, data->state); @@ -520,12 +651,11 @@ static Boolean eap_ttls_check(struct eap_sm *sm, void *priv, { struct eap_hdr *resp; u8 *pos; - size_t len; resp = (struct eap_hdr *) respData; pos = (u8 *) (resp + 1); if (respDataLen < sizeof(*resp) + 2 || *pos != EAP_TYPE_TTLS || - (len = ntohs(resp->length)) > respDataLen) { + (ntohs(resp->length)) > respDataLen) { wpa_printf(MSG_INFO, "EAP-TTLS: Invalid frame"); return TRUE; } @@ -534,6 +664,37 @@ static Boolean eap_ttls_check(struct eap_sm *sm, void *priv, } +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 = malloc(buf_len); + if (buf == NULL) + return -1; + WPA_PUT_BE16(buf, key_len); + 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); + free(buf); + + return ret; +} + + static void eap_ttls_process_phase2_pap(struct eap_sm *sm, struct eap_ttls_data *data, const u8 *user_password, @@ -541,9 +702,9 @@ static void eap_ttls_process_phase2_pap(struct eap_sm *sm, { /* TODO: add support for verifying that the user entry accepts * EAP-TTLS/PAP. */ - if (!sm->user || !sm->user->password) { - wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: No user password " - "configured"); + if (!sm->user || !sm->user->password || sm->user->password_hash) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: No plaintext user " + "password configured"); eap_ttls_state(data, FAILURE); return; } @@ -557,7 +718,8 @@ static void eap_ttls_process_phase2_pap(struct eap_sm *sm, } wpa_printf(MSG_DEBUG, "EAP-TTLS/PAP: Correct user password"); - eap_ttls_state(data, SUCCESS); + eap_ttls_state(data, data->ttls_version > 0 ? PHASE_FINISHED : + SUCCESS); } @@ -585,15 +747,15 @@ static void eap_ttls_process_phase2_chap(struct eap_sm *sm, /* TODO: add support for verifying that the user entry accepts * EAP-TTLS/CHAP. */ - if (!sm->user || !sm->user->password) { - wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: No user password " - "configured"); + if (!sm->user || !sm->user->password || sm->user->password_hash) { + wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: No plaintext user " + "password configured"); eap_ttls_state(data, FAILURE); return; } - chal = eap_tls_derive_key(sm, &data->ssl, "ttls challenge", - EAP_TTLS_CHAP_CHALLENGE_LEN + 1); + chal = eap_ttls_implicit_challenge(sm, data, + EAP_TTLS_CHAP_CHALLENGE_LEN + 1); if (chal == NULL) { wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Failed to generate " "challenge from TLS data"); @@ -621,7 +783,8 @@ static void eap_ttls_process_phase2_chap(struct eap_sm *sm, if (memcmp(hash, password + 1, EAP_TTLS_CHAP_PASSWORD_LEN) == 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Correct user password"); - eap_ttls_state(data, SUCCESS); + eap_ttls_state(data, data->ttls_version > 0 ? PHASE_FINISHED : + SUCCESS); } else { wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Invalid user password"); eap_ttls_state(data, FAILURE); @@ -656,8 +819,8 @@ static void eap_ttls_process_phase2_mschap(struct eap_sm *sm, return; } - chal = eap_tls_derive_key(sm, &data->ssl, "ttls challenge", - EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1); + chal = eap_ttls_implicit_challenge(sm, data, + EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1); if (chal == NULL) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Failed to generate " "challenge from TLS data"); @@ -674,12 +837,16 @@ static void eap_ttls_process_phase2_mschap(struct eap_sm *sm, } free(chal); - nt_challenge_response(challenge, sm->user->password, - sm->user->password_len, nt_response); + if (sm->user->password_hash) + challenge_response(challenge, sm->user->password, nt_response); + else + nt_challenge_response(challenge, sm->user->password, + sm->user->password_len, nt_response); if (memcmp(nt_response, response + 2 + 24, 24) == 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Correct response"); - eap_ttls_state(data, SUCCESS); + eap_ttls_state(data, data->ttls_version > 0 ? PHASE_FINISHED : + SUCCESS); } else { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAP: Invalid NT-Response"); wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAP: Received", @@ -697,10 +864,9 @@ static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, size_t challenge_len, u8 *response, size_t response_len) { - u8 *chal, *username, nt_response[24], *pos, *rx_resp, *peer_challenge, + u8 *chal, *username, nt_response[24], *rx_resp, *peer_challenge, *auth_challenge; - size_t username_len; - int i; + size_t username_len, i; if (challenge == NULL || response == NULL || challenge_len != EAP_TTLS_MSCHAPV2_CHALLENGE_LEN || @@ -727,7 +893,6 @@ static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, * (if present). */ username = sm->identity; username_len = sm->identity_len; - pos = username; for (i = 0; i < username_len; i++) { if (username[i] == '\\') { username_len -= i + 1; @@ -736,8 +901,8 @@ static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, } } - chal = eap_tls_derive_key(sm, &data->ssl, "ttls challenge", - EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1); + chal = eap_ttls_implicit_challenge( + sm, data, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1); if (chal == NULL) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Failed to generate " "challenge from TLS data"); @@ -764,25 +929,62 @@ static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, wpa_hexdump(MSG_MSGDUMP, "EAP-TTLS/MSCHAPV2: peer_challenge", peer_challenge, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN); - generate_nt_response(auth_challenge, peer_challenge, - username, username_len, - sm->user->password, sm->user->password_len, - nt_response); + if (sm->user->password_hash) { + generate_nt_response_pwhash(auth_challenge, peer_challenge, + username, username_len, + sm->user->password, + nt_response); + } else { + generate_nt_response(auth_challenge, peer_challenge, + username, username_len, + sm->user->password, + sm->user->password_len, + nt_response); + } rx_resp = response + 2 + EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 8; if (memcmp(nt_response, rx_resp, 24) == 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Correct " "NT-Response"); data->mschapv2_resp_ok = 1; + if (data->ttls_version > 0) { + const u8 *pw_hash; + u8 pw_hash_buf[16], pw_hash_hash[16], master_key[16]; + u8 session_key[2 * MSCHAPV2_KEY_LEN]; + + if (sm->user->password_hash) + pw_hash = sm->user->password; + else { + nt_password_hash(sm->user->password, + sm->user->password_len, + pw_hash_buf); + pw_hash = pw_hash_buf; + } + hash_nt_password_hash(pw_hash, pw_hash_hash); + get_master_key(pw_hash_hash, nt_response, master_key); + get_asymetric_start_key(master_key, session_key, + MSCHAPV2_KEY_LEN, 0, 0); + get_asymetric_start_key(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)); + } - generate_authenticator_response(sm->user->password, - sm->user->password_len, - peer_challenge, - auth_challenge, - username, username_len, - nt_response, - data->mschapv2_auth_response); - + if (sm->user->password_hash) { + generate_authenticator_response_pwhash( + sm->user->password, + peer_challenge, auth_challenge, + username, username_len, nt_response, + data->mschapv2_auth_response); + } else { + generate_authenticator_response( + sm->user->password, sm->user->password_len, + peer_challenge, auth_challenge, + username, username_len, nt_response, + data->mschapv2_auth_response); + } } else { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Invalid " "NT-Response"); @@ -798,14 +1000,16 @@ static void eap_ttls_process_phase2_mschapv2(struct eap_sm *sm, static int eap_ttls_phase2_eap_init(struct eap_sm *sm, - struct eap_ttls_data *data, u8 eap_type) + struct eap_ttls_data *data, + EapType eap_type) { if (data->phase2_priv && data->phase2_method) { data->phase2_method->reset(sm, data->phase2_priv); data->phase2_method = NULL; data->phase2_priv = NULL; } - data->phase2_method = eap_sm_get_eap_methods(eap_type); + data->phase2_method = eap_sm_get_eap_methods(EAP_VENDOR_IETF, + eap_type); if (!data->phase2_method) return -1; @@ -824,6 +1028,8 @@ static void eap_ttls_process_phase2_eap_response(struct eap_sm *sm, struct eap_hdr *hdr; u8 *pos; size_t left; + const struct eap_method *m = data->phase2_method; + void *priv = data->phase2_priv; if (data->phase2_priv == NULL) { wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: %s - Phase2 not " @@ -833,17 +1039,17 @@ static void eap_ttls_process_phase2_eap_response(struct eap_sm *sm, hdr = (struct eap_hdr *) in_data; pos = (u8 *) (hdr + 1); - left = in_len - sizeof(*hdr); if (in_len > sizeof(*hdr) && *pos == EAP_TYPE_NAK) { + left = in_len - sizeof(*hdr); wpa_hexdump(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 type Nak'ed; " "allowed types", pos + 1, left - 1); eap_sm_process_nak(sm, pos + 1, left - 1); if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && - sm->user->methods[sm->user_eap_method_index] != + sm->user->methods[sm->user_eap_method_index].method != EAP_TYPE_NONE) { - next_type = - sm->user->methods[sm->user_eap_method_index++]; + next_type = sm->user->methods[ + 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); @@ -853,19 +1059,18 @@ static void eap_ttls_process_phase2_eap_response(struct eap_sm *sm, return; } - if (data->phase2_method->check(sm, data->phase2_priv, in_data, - in_len)) { + if (m->check(sm, priv, in_data, in_len)) { wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 check() asked to " "ignore the packet"); return; } - data->phase2_method->process(sm, data->phase2_priv, in_data, in_len); + m->process(sm, priv, in_data, in_len); - if (!data->phase2_method->isDone(sm, data->phase2_priv)) + if (!m->isDone(sm, priv)) return; - if (!data->phase2_method->isSuccess(sm, data->phase2_priv)) { + if (!m->isSuccess(sm, priv)) { wpa_printf(MSG_DEBUG, "EAP-TTLS/EAP: Phase2 method failed"); eap_ttls_state(data, FAILURE); return; @@ -883,12 +1088,22 @@ static void eap_ttls_process_phase2_eap_response(struct eap_sm *sm, } eap_ttls_state(data, PHASE2_METHOD); - next_type = sm->user->methods[0]; + next_type = sm->user->methods[0].method; sm->user_eap_method_index = 1; wpa_printf(MSG_DEBUG, "EAP-TTLS: try EAP type %d", next_type); break; case PHASE2_METHOD: - eap_ttls_state(data, SUCCESS); + if (data->ttls_version > 0) { + if (m->getKey) { + u8 *key; + size_t key_len; + key = m->getKey(sm, priv, &key_len); + eap_ttls_ia_permute_inner_secret(sm, data, + key, key_len); + } + eap_ttls_state(data, PHASE_FINISHED); + } else + eap_ttls_state(data, SUCCESS); break; case FAILURE: break; @@ -956,8 +1171,9 @@ static void eap_ttls_process_phase2(struct eap_sm *sm, u8 *in_data, size_t in_len) { u8 *in_decrypted; - int buf_len, len_decrypted, res; + int len_decrypted, res; struct eap_ttls_avp parse; + size_t buf_len; wpa_printf(MSG_DEBUG, "EAP-TTLS: received %lu bytes encrypted data for" " Phase 2", (unsigned long) in_len); @@ -993,6 +1209,23 @@ static void eap_ttls_process_phase2(struct eap_sm *sm, return; } + if (data->state == PHASE_FINISHED) { + if (len_decrypted == 0 && + tls_connection_ia_final_phase_finished(sm->ssl_ctx, + data->ssl.conn)) { + wpa_printf(MSG_DEBUG, "EAP-TTLS: FinalPhaseFinished " + "received"); + eap_ttls_state(data, SUCCESS); + } else { + wpa_printf(MSG_INFO, "EAP-TTLS: Did not receive valid " + "FinalPhaseFinished"); + eap_ttls_state(data, FAILURE); + } + + free(in_decrypted); + return; + } + wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 EAP", in_decrypted, len_decrypted); @@ -1075,8 +1308,18 @@ static void eap_ttls_process(struct eap_sm *sm, void *priv, "use version %d", peer_version, data->ttls_version, peer_version); data->ttls_version = peer_version; - } + + if (data->ttls_version > 0 && !data->tls_ia_configured) { + 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; + } + 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 " @@ -1109,13 +1352,15 @@ static void eap_ttls_process(struct eap_sm *sm, void *priv, break; case PHASE2_START: case PHASE2_METHOD: + case PHASE_FINISHED: eap_ttls_process_phase2(sm, data, resp, pos, left); break; case PHASE2_MSCHAPV2_RESP: if (data->mschapv2_resp_ok && left == 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer " "acknowledged response"); - eap_ttls_state(data, SUCCESS); + eap_ttls_state(data, data->ttls_version > 0 ? + PHASE_FINISHED : SUCCESS); } else if (!data->mschapv2_resp_ok) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer " "acknowledged error"); @@ -1148,6 +1393,54 @@ static Boolean eap_ttls_isDone(struct eap_sm *sm, void *priv) } +static u8 * eap_ttls_v1_derive_key(struct eap_sm *sm, + struct eap_ttls_data *data) +{ + struct tls_keys keys; + u8 *rnd, *key; + + 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 NULL; + } + + rnd = malloc(keys.client_random_len + keys.server_random_len); + key = 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); + return NULL; + } + 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.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); + return NULL; + } + + 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); + + free(rnd); + + return key; +} + + static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len) { struct eap_ttls_data *data = priv; @@ -1156,13 +1449,18 @@ static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len) if (data->state != SUCCESS) return NULL; - eapKeyData = eap_tls_derive_key(sm, &data->ssl, - "ttls keying material", - EAP_TLS_KEY_LEN); + if (data->ttls_version == 0) { + eapKeyData = eap_tls_derive_key(sm, &data->ssl, + "ttls keying material", + EAP_TLS_KEY_LEN); + } else { + eapKeyData = eap_ttls_v1_derive_key(sm, data); + } + if (eapKeyData) { *len = EAP_TLS_KEY_LEN; - wpa_hexdump(MSG_DEBUG, "EAP-TTLS: Derived key", - eapKeyData, EAP_TLS_KEY_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Derived key", + eapKeyData, EAP_TLS_KEY_LEN); } else { wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to derive key"); } @@ -1178,16 +1476,27 @@ static Boolean eap_ttls_isSuccess(struct eap_sm *sm, void *priv) } -const struct eap_method eap_method_ttls = +int eap_server_ttls_register(void) { - .method = EAP_TYPE_TTLS, - .name = "TTLS", - .init = eap_ttls_init, - .reset = eap_ttls_reset, - .buildReq = eap_ttls_buildReq, - .check = eap_ttls_check, - .process = eap_ttls_process, - .isDone = eap_ttls_isDone, - .getKey = eap_ttls_getKey, - .isSuccess = eap_ttls_isSuccess, -}; + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_IETF, EAP_TYPE_TTLS, "TTLS"); + if (eap == NULL) + return -1; + + eap->init = eap_ttls_init; + eap->reset = eap_ttls_reset; + eap->buildReq = eap_ttls_buildReq; + eap->check = eap_ttls_check; + eap->process = eap_ttls_process; + eap->isDone = eap_ttls_isDone; + eap->getKey = eap_ttls_getKey; + eap->isSuccess = eap_ttls_isSuccess; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/contrib/hostapd/eap_ttls.h b/contrib/hostapd/eap_ttls.h index f35f5a9..e0b2cbf 100644 --- a/contrib/hostapd/eap_ttls.h +++ b/contrib/hostapd/eap_ttls.h @@ -1,6 +1,6 @@ /* - * WPA Supplicant / EAP-TTLS (draft-ietf-pppext-eap-ttls-03.txt) - * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * EAP server/peer: EAP-TTLS (draft-ietf-pppext-eap-ttls-03.txt) + * 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 @@ -36,11 +36,11 @@ struct ttls_avp_vendor { #define AVP_PAD(start, pos) \ do { \ - int pad; \ - pad = (4 - (((pos) - (start)) & 3)) & 3; \ - memset((pos), 0, pad); \ - pos += pad; \ -} while(0) + int __pad; \ + __pad = (4 - (((pos) - (start)) & 3)) & 3; \ + os_memset((pos), 0, __pad); \ + pos += __pad; \ +} while (0) /* RFC 2865 */ diff --git a/contrib/hostapd/eap_vendor_test.c b/contrib/hostapd/eap_vendor_test.c new file mode 100644 index 0000000..4f9c14b --- /dev/null +++ b/contrib/hostapd/eap_vendor_test.c @@ -0,0 +1,228 @@ +/* + * hostapd / Test method for vendor specific (expanded) EAP type + * 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 "hostapd.h" +#include "common.h" +#include "eap_i.h" + + +#define EAP_VENDOR_ID 0xfffefd +#define EAP_VENDOR_TYPE 0xfcfbfaf9 + + +struct eap_vendor_test_data { + enum { INIT, CONFIRM, SUCCESS, FAILURE } state; +}; + + +static const char * eap_vendor_test_state_txt(int state) +{ + switch (state) { + case INIT: + return "INIT"; + case CONFIRM: + return "CONFIRM"; + case SUCCESS: + return "SUCCESS"; + case FAILURE: + return "FAILURE"; + default: + return "?"; + } +} + + +static void eap_vendor_test_state(struct eap_vendor_test_data *data, + int state) +{ + wpa_printf(MSG_DEBUG, "EAP-VENDOR-TEST: %s -> %s", + eap_vendor_test_state_txt(data->state), + eap_vendor_test_state_txt(state)); + data->state = state; +} + + +static void * eap_vendor_test_init(struct eap_sm *sm) +{ + struct eap_vendor_test_data *data; + + data = wpa_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->state = INIT; + + return data; +} + + +static void eap_vendor_test_reset(struct eap_sm *sm, void *priv) +{ + struct eap_vendor_test_data *data = priv; + free(data); +} + + +static u8 * eap_vendor_test_buildReq(struct eap_sm *sm, void *priv, int id, + size_t *reqDataLen) +{ + struct eap_vendor_test_data *data = priv; + struct eap_hdr *req; + u8 *pos; + + *reqDataLen = sizeof(*req) + 8 + 1; + req = malloc(*reqDataLen); + 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; +} + + +static Boolean eap_vendor_test_check(struct eap_sm *sm, void *priv, + u8 *respData, size_t respDataLen) +{ + struct eap_hdr *resp; + 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) { + 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 eap_vendor_test_data *data = priv; + struct eap_hdr *resp; + u8 *pos; + + resp = (struct eap_hdr *) respData; + pos = (u8 *) (resp + 1); + pos += 8; /* Skip expanded header */ + + if (data->state == INIT) { + if (*pos == 2) + eap_vendor_test_state(data, CONFIRM); + else + eap_vendor_test_state(data, FAILURE); + } else if (data->state == CONFIRM) { + if (*pos == 4) + eap_vendor_test_state(data, SUCCESS); + else + eap_vendor_test_state(data, FAILURE); + } else + eap_vendor_test_state(data, FAILURE); +} + + +static Boolean eap_vendor_test_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_vendor_test_data *data = priv; + return data->state == SUCCESS; +} + + +static u8 * eap_vendor_test_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_vendor_test_data *data = priv; + u8 *key; + const int key_len = 64; + + if (data->state != SUCCESS) + return NULL; + + key = malloc(key_len); + if (key == NULL) + return NULL; + + memset(key, 0x11, key_len / 2); + memset(key + key_len / 2, 0x22, key_len / 2); + *len = key_len; + + return key; +} + + +static Boolean eap_vendor_test_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_vendor_test_data *data = priv; + return data->state == SUCCESS; +} + + +int eap_server_vendor_test_register(void) +{ + struct eap_method *eap; + int ret; + + eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION, + EAP_VENDOR_ID, EAP_VENDOR_TYPE, + "VENDOR-TEST"); + if (eap == NULL) + return -1; + + eap->init = eap_vendor_test_init; + eap->reset = eap_vendor_test_reset; + eap->buildReq = eap_vendor_test_buildReq; + eap->check = eap_vendor_test_check; + eap->process = eap_vendor_test_process; + eap->isDone = eap_vendor_test_isDone; + eap->getKey = eap_vendor_test_getKey; + eap->isSuccess = eap_vendor_test_isSuccess; + + ret = eap_server_method_register(eap); + if (ret) + eap_server_method_free(eap); + return ret; +} diff --git a/contrib/hostapd/eapol_sm.c b/contrib/hostapd/eapol_sm.c index f2d5ec7..f4f5752 100644 --- a/contrib/hostapd/eapol_sm.c +++ b/contrib/hostapd/eapol_sm.c @@ -1,7 +1,6 @@ /* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / IEEE 802.1X Authenticator - EAPOL state machine - * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * hostapd / IEEE 802.1X Authenticator - EAPOL state machine + * Copyright (c) 2002-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 @@ -13,20 +12,21 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <netinet/in.h> -#include <string.h> -#include <sys/socket.h> +#include "includes.h" #include "hostapd.h" #include "ieee802_1x.h" #include "eapol_sm.h" #include "eloop.h" #include "wpa.h" +#include "preauth.h" #include "sta_info.h" #include "eap.h" +#include "state_machine.h" + +#define STATE_MACHINE_DATA struct eapol_state_machine +#define STATE_MACHINE_DEBUG_PREFIX "IEEE 802.1X" +#define STATE_MACHINE_ADDR sm->addr static struct eapol_callbacks eapol_cb; @@ -47,25 +47,6 @@ ieee802_1x_set_sta_authorized(sm->hapd, sm->sta, 0) #define processKey() do { } while (0) -/* Definitions for clarifying state machine implementation */ -#define SM_STATE(machine, state) \ -static void sm_ ## machine ## _ ## state ## _Enter(struct eapol_state_machine \ -*sm) - -#define SM_ENTRY(machine, _state, _data) \ -sm->_data.state = machine ## _ ## _state; \ -if (sm->hapd->conf->debug >= HOSTAPD_DEBUG_MINIMAL) \ - printf("IEEE 802.1X: " MACSTR " " #machine " entering state " #_state \ - "\n", MAC2STR(sm->addr)); - -#define SM_ENTER(machine, state) sm_ ## machine ## _ ## state ## _Enter(sm) - -#define SM_STEP(machine) \ -static void sm_ ## machine ## _Step(struct eapol_state_machine *sm) - -#define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm) - - static void eapol_sm_step_run(struct eapol_state_machine *sm); static void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx); @@ -115,8 +96,8 @@ static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx) SM_STATE(AUTH_PAE, INITIALIZE) { - SM_ENTRY(AUTH_PAE, INITIALIZE, auth_pae); - sm->auth_pae.portMode = Auto; + SM_ENTRY_MA(AUTH_PAE, INITIALIZE, auth_pae); + sm->portMode = Auto; sm->currentId = 255; } @@ -124,21 +105,21 @@ SM_STATE(AUTH_PAE, INITIALIZE) SM_STATE(AUTH_PAE, DISCONNECTED) { - int from_initialize = sm->auth_pae.state == AUTH_PAE_INITIALIZE; + int from_initialize = sm->auth_pae_state == AUTH_PAE_INITIALIZE; - if (sm->auth_pae.eapolLogoff) { - if (sm->auth_pae.state == AUTH_PAE_CONNECTING) - sm->auth_pae.authEapLogoffsWhileConnecting++; - else if (sm->auth_pae.state == AUTH_PAE_AUTHENTICATED) - sm->auth_pae.authAuthEapLogoffWhileAuthenticated++; + if (sm->eapolLogoff) { + if (sm->auth_pae_state == AUTH_PAE_CONNECTING) + sm->authEapLogoffsWhileConnecting++; + else if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATED) + sm->authAuthEapLogoffWhileAuthenticated++; } - SM_ENTRY(AUTH_PAE, DISCONNECTED, auth_pae); + SM_ENTRY_MA(AUTH_PAE, DISCONNECTED, auth_pae); sm->authPortStatus = Unauthorized; setPortUnauthorized(); - sm->auth_pae.reAuthCount = 0; - sm->auth_pae.eapolLogoff = FALSE; + sm->reAuthCount = 0; + sm->eapolLogoff = FALSE; if (!from_initialize) { if (sm->flags & EAPOL_SM_PREAUTH) rsn_preauth_finished(sm->hapd, sm->sta, 0); @@ -150,48 +131,58 @@ SM_STATE(AUTH_PAE, DISCONNECTED) SM_STATE(AUTH_PAE, RESTART) { - if (sm->auth_pae.state == AUTH_PAE_AUTHENTICATED) { + if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATED) { if (sm->reAuthenticate) - sm->auth_pae.authAuthReauthsWhileAuthenticated++; - if (sm->auth_pae.eapolStart) - sm->auth_pae.authAuthEapStartsWhileAuthenticated++; - if (sm->auth_pae.eapolLogoff) - sm->auth_pae.authAuthEapLogoffWhileAuthenticated++; + sm->authAuthReauthsWhileAuthenticated++; + if (sm->eapolStart) + sm->authAuthEapStartsWhileAuthenticated++; + if (sm->eapolLogoff) + sm->authAuthEapLogoffWhileAuthenticated++; } - SM_ENTRY(AUTH_PAE, RESTART, auth_pae); + SM_ENTRY_MA(AUTH_PAE, RESTART, auth_pae); - sm->auth_pae.eapRestart = TRUE; + sm->eapRestart = TRUE; ieee802_1x_request_identity(sm->hapd, sm->sta); } SM_STATE(AUTH_PAE, CONNECTING) { - if (sm->auth_pae.state != AUTH_PAE_CONNECTING) - sm->auth_pae.authEntersConnecting++; + if (sm->auth_pae_state != AUTH_PAE_CONNECTING) + sm->authEntersConnecting++; - SM_ENTRY(AUTH_PAE, CONNECTING, auth_pae); + SM_ENTRY_MA(AUTH_PAE, CONNECTING, auth_pae); sm->reAuthenticate = FALSE; - sm->auth_pae.reAuthCount++; + sm->reAuthCount++; } SM_STATE(AUTH_PAE, HELD) { - if (sm->auth_pae.state == AUTH_PAE_AUTHENTICATING && sm->authFail) - sm->auth_pae.authAuthFailWhileAuthenticating++; + if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING && sm->authFail) + sm->authAuthFailWhileAuthenticating++; - SM_ENTRY(AUTH_PAE, HELD, auth_pae); + SM_ENTRY_MA(AUTH_PAE, HELD, auth_pae); sm->authPortStatus = Unauthorized; setPortUnauthorized(); - sm->quietWhile = sm->auth_pae.quietPeriod; - sm->auth_pae.eapolLogoff = FALSE; + sm->quietWhile = sm->quietPeriod; + sm->eapolLogoff = FALSE; hostapd_logger(sm->hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_WARNING, "authentication failed"); + HOSTAPD_LEVEL_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)); + } if (sm->flags & EAPOL_SM_PREAUTH) rsn_preauth_finished(sm->hapd, sm->sta, 0); else @@ -201,16 +192,24 @@ SM_STATE(AUTH_PAE, HELD) SM_STATE(AUTH_PAE, AUTHENTICATED) { - if (sm->auth_pae.state == AUTH_PAE_AUTHENTICATING && sm->authSuccess) - sm->auth_pae.authAuthSuccessesWhileAuthenticating++; + char *extra = ""; + + if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING && sm->authSuccess) + sm->authAuthSuccessesWhileAuthenticating++; - SM_ENTRY(AUTH_PAE, AUTHENTICATED, auth_pae); + SM_ENTRY_MA(AUTH_PAE, AUTHENTICATED, auth_pae); sm->authPortStatus = Authorized; setPortAuthorized(); - sm->auth_pae.reAuthCount = 0; + sm->reAuthCount = 0; + if (sm->flags & EAPOL_SM_PREAUTH) + 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"); + 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 @@ -220,14 +219,14 @@ SM_STATE(AUTH_PAE, AUTHENTICATED) SM_STATE(AUTH_PAE, AUTHENTICATING) { - if (sm->auth_pae.state == AUTH_PAE_CONNECTING && sm->rx_identity) { - sm->auth_pae.authEntersAuthenticating++; + if (sm->auth_pae_state == AUTH_PAE_CONNECTING && sm->rx_identity) { + sm->authEntersAuthenticating++; sm->rx_identity = FALSE; } - SM_ENTRY(AUTH_PAE, AUTHENTICATING, auth_pae); + SM_ENTRY_MA(AUTH_PAE, AUTHENTICATING, auth_pae); - sm->auth_pae.eapolStart = FALSE; + sm->eapolStart = FALSE; sm->authSuccess = FALSE; sm->authFail = FALSE; sm->authTimeout = FALSE; @@ -239,16 +238,16 @@ SM_STATE(AUTH_PAE, AUTHENTICATING) SM_STATE(AUTH_PAE, ABORTING) { - if (sm->auth_pae.state == AUTH_PAE_AUTHENTICATING) { + if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING) { if (sm->authTimeout) - sm->auth_pae.authAuthTimeoutsWhileAuthenticating++; - if (sm->auth_pae.eapolStart) - sm->auth_pae.authAuthEapStartsWhileAuthenticating++; - if (sm->auth_pae.eapolLogoff) - sm->auth_pae.authAuthEapLogoffWhileAuthenticating++; + sm->authAuthTimeoutsWhileAuthenticating++; + if (sm->eapolStart) + sm->authAuthEapStartsWhileAuthenticating++; + if (sm->eapolLogoff) + sm->authAuthEapLogoffWhileAuthenticating++; } - SM_ENTRY(AUTH_PAE, ABORTING, auth_pae); + SM_ENTRY_MA(AUTH_PAE, ABORTING, auth_pae); sm->authAbort = TRUE; sm->keyRun = FALSE; @@ -258,44 +257,43 @@ SM_STATE(AUTH_PAE, ABORTING) SM_STATE(AUTH_PAE, FORCE_AUTH) { - SM_ENTRY(AUTH_PAE, FORCE_AUTH, auth_pae); + SM_ENTRY_MA(AUTH_PAE, FORCE_AUTH, auth_pae); sm->authPortStatus = Authorized; setPortAuthorized(); - sm->auth_pae.portMode = ForceAuthorized; - sm->auth_pae.eapolStart = FALSE; + sm->portMode = ForceAuthorized; + sm->eapolStart = FALSE; txCannedSuccess(); } SM_STATE(AUTH_PAE, FORCE_UNAUTH) { - SM_ENTRY(AUTH_PAE, FORCE_UNAUTH, auth_pae); + SM_ENTRY_MA(AUTH_PAE, FORCE_UNAUTH, auth_pae); sm->authPortStatus = Unauthorized; setPortUnauthorized(); - sm->auth_pae.portMode = ForceUnauthorized; - sm->auth_pae.eapolStart = FALSE; + sm->portMode = ForceUnauthorized; + sm->eapolStart = FALSE; txCannedFail(); } SM_STEP(AUTH_PAE) { - if ((sm->portControl == Auto && - sm->auth_pae.portMode != sm->portControl) || + if ((sm->portControl == Auto && sm->portMode != sm->portControl) || sm->initialize || !sm->portEnabled) SM_ENTER(AUTH_PAE, INITIALIZE); else if (sm->portControl == ForceAuthorized && - sm->auth_pae.portMode != sm->portControl && + sm->portMode != sm->portControl && !(sm->initialize || !sm->portEnabled)) SM_ENTER(AUTH_PAE, FORCE_AUTH); else if (sm->portControl == ForceUnauthorized && - sm->auth_pae.portMode != sm->portControl && + sm->portMode != sm->portControl && !(sm->initialize || !sm->portEnabled)) SM_ENTER(AUTH_PAE, FORCE_UNAUTH); else { - switch (sm->auth_pae.state) { + switch (sm->auth_pae_state) { case AUTH_PAE_INITIALIZE: SM_ENTER(AUTH_PAE, DISCONNECTED); break; @@ -303,7 +301,7 @@ SM_STEP(AUTH_PAE) SM_ENTER(AUTH_PAE, RESTART); break; case AUTH_PAE_RESTART: - if (!sm->auth_pae.eapRestart) + if (!sm->eapRestart) SM_ENTER(AUTH_PAE, CONNECTING); break; case AUTH_PAE_HELD: @@ -311,19 +309,17 @@ SM_STEP(AUTH_PAE) SM_ENTER(AUTH_PAE, RESTART); break; case AUTH_PAE_CONNECTING: - if (sm->auth_pae.eapolLogoff || - sm->auth_pae.reAuthCount > sm->auth_pae.reAuthMax) + if (sm->eapolLogoff || sm->reAuthCount > sm->reAuthMax) SM_ENTER(AUTH_PAE, DISCONNECTED); - else if ((sm->be_auth.eapReq && - sm->auth_pae.reAuthCount <= - sm->auth_pae.reAuthMax) || + else if ((sm->eapReq && + sm->reAuthCount <= sm->reAuthMax) || sm->eapSuccess || sm->eapFail) SM_ENTER(AUTH_PAE, AUTHENTICATING); break; case AUTH_PAE_AUTHENTICATED: - if (sm->auth_pae.eapolStart || sm->reAuthenticate) + if (sm->eapolStart || sm->reAuthenticate) SM_ENTER(AUTH_PAE, RESTART); - else if (sm->auth_pae.eapolLogoff || !sm->portValid) + else if (sm->eapolLogoff || !sm->portValid) SM_ENTER(AUTH_PAE, DISCONNECTED); break; case AUTH_PAE_AUTHENTICATING: @@ -332,22 +328,22 @@ SM_STEP(AUTH_PAE) else if (sm->authFail || (sm->keyDone && !sm->portValid)) SM_ENTER(AUTH_PAE, HELD); - else if (sm->auth_pae.eapolStart || - sm->auth_pae.eapolLogoff || sm->authTimeout) + else if (sm->eapolStart || sm->eapolLogoff || + sm->authTimeout) SM_ENTER(AUTH_PAE, ABORTING); break; case AUTH_PAE_ABORTING: - if (sm->auth_pae.eapolLogoff && !sm->authAbort) + if (sm->eapolLogoff && !sm->authAbort) SM_ENTER(AUTH_PAE, DISCONNECTED); - else if (!sm->auth_pae.eapolLogoff && !sm->authAbort) + else if (!sm->eapolLogoff && !sm->authAbort) SM_ENTER(AUTH_PAE, RESTART); break; case AUTH_PAE_FORCE_AUTH: - if (sm->auth_pae.eapolStart) + if (sm->eapolStart) SM_ENTER(AUTH_PAE, FORCE_AUTH); break; case AUTH_PAE_FORCE_UNAUTH: - if (sm->auth_pae.eapolStart) + if (sm->eapolStart) SM_ENTER(AUTH_PAE, FORCE_UNAUTH); break; } @@ -360,21 +356,21 @@ SM_STEP(AUTH_PAE) SM_STATE(BE_AUTH, INITIALIZE) { - SM_ENTRY(BE_AUTH, INITIALIZE, be_auth); + SM_ENTRY_MA(BE_AUTH, INITIALIZE, be_auth); abortAuth(); - sm->be_auth.eapNoReq = FALSE; + sm->eapNoReq = FALSE; sm->authAbort = FALSE; } SM_STATE(BE_AUTH, REQUEST) { - SM_ENTRY(BE_AUTH, REQUEST, be_auth); + SM_ENTRY_MA(BE_AUTH, REQUEST, be_auth); txReq(); - sm->be_auth.eapReq = FALSE; - sm->be_auth.backendOtherRequestsToSupplicant++; + sm->eapReq = FALSE; + sm->backendOtherRequestsToSupplicant++; /* * Clearing eapolEap here is not specified in IEEE Std 802.1X-2004, but @@ -393,21 +389,21 @@ SM_STATE(BE_AUTH, REQUEST) SM_STATE(BE_AUTH, RESPONSE) { - SM_ENTRY(BE_AUTH, RESPONSE, be_auth); + SM_ENTRY_MA(BE_AUTH, RESPONSE, be_auth); sm->authTimeout = FALSE; sm->eapolEap = FALSE; - sm->be_auth.eapNoReq = FALSE; - sm->aWhile = sm->be_auth.serverTimeout; - sm->be_auth.eapResp = TRUE; + sm->eapNoReq = FALSE; + sm->aWhile = sm->serverTimeout; + sm->eapResp = TRUE; sendRespToServer(); - sm->be_auth.backendResponses++; + sm->backendResponses++; } SM_STATE(BE_AUTH, SUCCESS) { - SM_ENTRY(BE_AUTH, SUCCESS, be_auth); + SM_ENTRY_MA(BE_AUTH, SUCCESS, be_auth); txReq(); sm->authSuccess = TRUE; @@ -417,7 +413,7 @@ SM_STATE(BE_AUTH, SUCCESS) SM_STATE(BE_AUTH, FAIL) { - SM_ENTRY(BE_AUTH, FAIL, be_auth); + 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 @@ -433,7 +429,7 @@ SM_STATE(BE_AUTH, FAIL) SM_STATE(BE_AUTH, TIMEOUT) { - SM_ENTRY(BE_AUTH, TIMEOUT, be_auth); + SM_ENTRY_MA(BE_AUTH, TIMEOUT, be_auth); sm->authTimeout = TRUE; } @@ -441,7 +437,7 @@ SM_STATE(BE_AUTH, TIMEOUT) SM_STATE(BE_AUTH, IDLE) { - SM_ENTRY(BE_AUTH, IDLE, be_auth); + SM_ENTRY_MA(BE_AUTH, IDLE, be_auth); sm->authStart = FALSE; } @@ -449,9 +445,9 @@ SM_STATE(BE_AUTH, IDLE) SM_STATE(BE_AUTH, IGNORE) { - SM_ENTRY(BE_AUTH, IGNORE, be_auth); + SM_ENTRY_MA(BE_AUTH, IGNORE, be_auth); - sm->be_auth.eapNoReq = FALSE; + sm->eapNoReq = FALSE; } @@ -462,31 +458,31 @@ SM_STEP(BE_AUTH) return; } - switch (sm->be_auth.state) { + switch (sm->be_auth_state) { case BE_AUTH_INITIALIZE: SM_ENTER(BE_AUTH, IDLE); break; case BE_AUTH_REQUEST: if (sm->eapolEap) SM_ENTER(BE_AUTH, RESPONSE); - else if (sm->be_auth.eapReq) + else if (sm->eapReq) SM_ENTER(BE_AUTH, REQUEST); else if (sm->eapTimeout) SM_ENTER(BE_AUTH, TIMEOUT); break; case BE_AUTH_RESPONSE: - if (sm->be_auth.eapNoReq) + if (sm->eapNoReq) SM_ENTER(BE_AUTH, IGNORE); - if (sm->be_auth.eapReq) { - sm->be_auth.backendAccessChallenges++; + if (sm->eapReq) { + sm->backendAccessChallenges++; SM_ENTER(BE_AUTH, REQUEST); } else if (sm->aWhile == 0) SM_ENTER(BE_AUTH, TIMEOUT); else if (sm->eapFail) { - sm->be_auth.backendAuthFails++; + sm->backendAuthFails++; SM_ENTER(BE_AUTH, FAIL); } else if (sm->eapSuccess) { - sm->be_auth.backendAuthSuccesses++; + sm->backendAuthSuccesses++; SM_ENTER(BE_AUTH, SUCCESS); } break; @@ -502,7 +498,7 @@ SM_STEP(BE_AUTH) case BE_AUTH_IDLE: if (sm->eapFail && sm->authStart) SM_ENTER(BE_AUTH, FAIL); - else if (sm->be_auth.eapReq && sm->authStart) + else if (sm->eapReq && sm->authStart) SM_ENTER(BE_AUTH, REQUEST); else if (sm->eapSuccess && sm->authStart) SM_ENTER(BE_AUTH, SUCCESS); @@ -510,7 +506,7 @@ SM_STEP(BE_AUTH) case BE_AUTH_IGNORE: if (sm->eapolEap) SM_ENTER(BE_AUTH, RESPONSE); - else if (sm->be_auth.eapReq) + else if (sm->eapReq) SM_ENTER(BE_AUTH, REQUEST); else if (sm->eapTimeout) SM_ENTER(BE_AUTH, TIMEOUT); @@ -524,31 +520,30 @@ SM_STEP(BE_AUTH) SM_STATE(REAUTH_TIMER, INITIALIZE) { - SM_ENTRY(REAUTH_TIMER, INITIALIZE, reauth_timer); + SM_ENTRY_MA(REAUTH_TIMER, INITIALIZE, reauth_timer); - sm->reAuthWhen = sm->reauth_timer.reAuthPeriod; + sm->reAuthWhen = sm->reAuthPeriod; } SM_STATE(REAUTH_TIMER, REAUTHENTICATE) { - SM_ENTRY(REAUTH_TIMER, REAUTHENTICATE, reauth_timer); + SM_ENTRY_MA(REAUTH_TIMER, REAUTHENTICATE, reauth_timer); sm->reAuthenticate = TRUE; - wpa_sm_event(sm->hapd, sm->sta, WPA_REAUTH_EAPOL); + wpa_auth_sm_event(sm->sta->wpa_sm, WPA_REAUTH_EAPOL); } SM_STEP(REAUTH_TIMER) { if (sm->portControl != Auto || sm->initialize || - sm->authPortStatus == Unauthorized || - !sm->reauth_timer.reAuthEnabled) { + sm->authPortStatus == Unauthorized || !sm->reAuthEnabled) { SM_ENTER(REAUTH_TIMER, INITIALIZE); return; } - switch (sm->reauth_timer.state) { + switch (sm->reauth_timer_state) { case REAUTH_TIMER_INITIALIZE: if (sm->reAuthWhen == 0) SM_ENTER(REAUTH_TIMER, REAUTHENTICATE); @@ -565,13 +560,13 @@ SM_STEP(REAUTH_TIMER) SM_STATE(AUTH_KEY_TX, NO_KEY_TRANSMIT) { - SM_ENTRY(AUTH_KEY_TX, NO_KEY_TRANSMIT, auth_key_tx); + SM_ENTRY_MA(AUTH_KEY_TX, NO_KEY_TRANSMIT, auth_key_tx); } SM_STATE(AUTH_KEY_TX, KEY_TRANSMIT) { - SM_ENTRY(AUTH_KEY_TX, KEY_TRANSMIT, auth_key_tx); + SM_ENTRY_MA(AUTH_KEY_TX, KEY_TRANSMIT, auth_key_tx); txKey(); sm->keyAvailable = FALSE; @@ -586,10 +581,10 @@ SM_STEP(AUTH_KEY_TX) return; } - switch (sm->auth_key_tx.state) { + switch (sm->auth_key_tx_state) { case AUTH_KEY_TX_NO_KEY_TRANSMIT: if (sm->keyTxEnabled && sm->keyAvailable && sm->keyRun && - !sm->sta->wpa) + !wpa_auth_sta_wpa_version(sm->sta->wpa_sm)) SM_ENTER(AUTH_KEY_TX, KEY_TRANSMIT); break; case AUTH_KEY_TX_KEY_TRANSMIT: @@ -607,16 +602,16 @@ SM_STEP(AUTH_KEY_TX) SM_STATE(KEY_RX, NO_KEY_RECEIVE) { - SM_ENTRY(KEY_RX, NO_KEY_RECEIVE, key_rx); + SM_ENTRY_MA(KEY_RX, NO_KEY_RECEIVE, key_rx); } SM_STATE(KEY_RX, KEY_RECEIVE) { - SM_ENTRY(KEY_RX, KEY_RECEIVE, key_rx); + SM_ENTRY_MA(KEY_RX, KEY_RECEIVE, key_rx); processKey(); - sm->key_rx.rxKey = FALSE; + sm->rxKey = FALSE; } @@ -627,13 +622,13 @@ SM_STEP(KEY_RX) return; } - switch (sm->key_rx.state) { + switch (sm->key_rx_state) { case KEY_RX_NO_KEY_RECEIVE: - if (sm->key_rx.rxKey) + if (sm->rxKey) SM_ENTER(KEY_RX, KEY_RECEIVE); break; case KEY_RX_KEY_RECEIVE: - if (sm->key_rx.rxKey) + if (sm->rxKey) SM_ENTER(KEY_RX, KEY_RECEIVE); break; } @@ -645,16 +640,15 @@ SM_STEP(KEY_RX) SM_STATE(CTRL_DIR, FORCE_BOTH) { - SM_ENTRY(CTRL_DIR, FORCE_BOTH, ctrl_dir); - sm->ctrl_dir.operControlledDirections = Both; + SM_ENTRY_MA(CTRL_DIR, FORCE_BOTH, ctrl_dir); + sm->operControlledDirections = Both; } SM_STATE(CTRL_DIR, IN_OR_BOTH) { - SM_ENTRY(CTRL_DIR, IN_OR_BOTH, ctrl_dir); - sm->ctrl_dir.operControlledDirections = - sm->ctrl_dir.adminControlledDirections; + SM_ENTRY_MA(CTRL_DIR, IN_OR_BOTH, ctrl_dir); + sm->operControlledDirections = sm->adminControlledDirections; } @@ -665,16 +659,16 @@ SM_STEP(CTRL_DIR) return; } - switch (sm->ctrl_dir.state) { + switch (sm->ctrl_dir_state) { case CTRL_DIR_FORCE_BOTH: - if (sm->portEnabled && sm->ctrl_dir.operEdge) + if (sm->portEnabled && sm->operEdge) SM_ENTER(CTRL_DIR, IN_OR_BOTH); break; case CTRL_DIR_IN_OR_BOTH: - if (sm->ctrl_dir.operControlledDirections != - sm->ctrl_dir.adminControlledDirections) + if (sm->operControlledDirections != + sm->adminControlledDirections) SM_ENTER(CTRL_DIR, IN_OR_BOTH); - if (!sm->portEnabled || !sm->ctrl_dir.operEdge) + if (!sm->portEnabled || !sm->operEdge) SM_ENTER(CTRL_DIR, FORCE_BOTH); break; } @@ -683,16 +677,15 @@ SM_STEP(CTRL_DIR) struct eapol_state_machine * -eapol_sm_alloc(hostapd *hapd, struct sta_info *sta) +eapol_sm_alloc(struct hostapd_data *hapd, struct sta_info *sta) { struct eapol_state_machine *sm; - sm = (struct eapol_state_machine *) malloc(sizeof(*sm)); + sm = wpa_zalloc(sizeof(*sm)); if (sm == NULL) { printf("IEEE 802.1X port state allocation failed\n"); return NULL; } - memset(sm, 0, sizeof(*sm)); sm->radius_identifier = -1; memcpy(sm->addr, sta->addr, ETH_ALEN); if (sta->flags & WLAN_STA_PREAUTH) @@ -702,23 +695,22 @@ eapol_sm_alloc(hostapd *hapd, struct sta_info *sta) sm->sta = sta; /* Set default values for state machine constants */ - sm->auth_pae.state = AUTH_PAE_INITIALIZE; - sm->auth_pae.quietPeriod = AUTH_PAE_DEFAULT_quietPeriod; - sm->auth_pae.reAuthMax = AUTH_PAE_DEFAULT_reAuthMax; + sm->auth_pae_state = AUTH_PAE_INITIALIZE; + sm->quietPeriod = AUTH_PAE_DEFAULT_quietPeriod; + sm->reAuthMax = AUTH_PAE_DEFAULT_reAuthMax; - sm->be_auth.state = BE_AUTH_INITIALIZE; - sm->be_auth.serverTimeout = BE_AUTH_DEFAULT_serverTimeout; + sm->be_auth_state = BE_AUTH_INITIALIZE; + sm->serverTimeout = BE_AUTH_DEFAULT_serverTimeout; - sm->reauth_timer.state = REAUTH_TIMER_INITIALIZE; - sm->reauth_timer.reAuthPeriod = hapd->conf->eap_reauth_period; - sm->reauth_timer.reAuthEnabled = hapd->conf->eap_reauth_period > 0 ? - TRUE : FALSE; + 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->auth_key_tx.state = AUTH_KEY_TX_NO_KEY_TRANSMIT; + sm->auth_key_tx_state = AUTH_KEY_TX_NO_KEY_TRANSMIT; - sm->key_rx.state = KEY_RX_NO_KEY_RECEIVE; + sm->key_rx_state = KEY_RX_NO_KEY_RECEIVE; - sm->ctrl_dir.state = CTRL_DIR_IN_OR_BOTH; + sm->ctrl_dir_state = CTRL_DIR_IN_OR_BOTH; sm->portEnabled = FALSE; sm->portControl = Auto; @@ -779,8 +771,8 @@ static void eapol_sm_step_run(struct eapol_state_machine *sm) { struct hostapd_data *hapd = sm->hapd; u8 addr[ETH_ALEN]; - int prev_auth_pae, prev_be_auth, prev_reauth_timer, prev_auth_key_tx, - prev_key_rx, prev_ctrl_dir; + 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); @@ -792,12 +784,12 @@ static void eapol_sm_step_run(struct eapol_state_machine *sm) * eloop callback. */ restart: - prev_auth_pae = sm->auth_pae.state; - prev_be_auth = sm->be_auth.state; - prev_reauth_timer = sm->reauth_timer.state; - prev_auth_key_tx = sm->auth_key_tx.state; - prev_key_rx = sm->key_rx.state; - prev_ctrl_dir = sm->ctrl_dir.state; + prev_auth_pae = sm->auth_pae_state; + prev_be_auth = sm->be_auth_state; + prev_reauth_timer = sm->reauth_timer_state; + prev_auth_key_tx = sm->auth_key_tx_state; + prev_key_rx = sm->key_rx_state; + prev_ctrl_dir = sm->ctrl_dir_state; SM_STEP_RUN(AUTH_PAE); if (sm->initializing || eapol_sm_sta_entry_alive(hapd, addr)) @@ -811,12 +803,12 @@ restart: if (sm->initializing || eapol_sm_sta_entry_alive(hapd, addr)) SM_STEP_RUN(CTRL_DIR); - if (prev_auth_pae != sm->auth_pae.state || - prev_be_auth != sm->be_auth.state || - prev_reauth_timer != sm->reauth_timer.state || - prev_auth_key_tx != sm->auth_key_tx.state || - prev_key_rx != sm->key_rx.state || - prev_ctrl_dir != sm->ctrl_dir.state) { + if (prev_auth_pae != sm->auth_pae_state || + prev_be_auth != sm->be_auth_state || + prev_reauth_timer != sm->reauth_timer_state || + prev_auth_key_tx != sm->auth_key_tx_state || + prev_key_rx != sm->key_rx_state || + prev_ctrl_dir != sm->ctrl_dir_state) { if (--max_steps > 0) goto restart; /* Re-run from eloop timeout */ @@ -835,7 +827,7 @@ restart: } if (eapol_sm_sta_entry_alive(hapd, addr)) - wpa_sm_notify(sm->hapd, sm->sta); + wpa_auth_sm_notify(sm->sta->wpa_sm); } @@ -1022,22 +1014,21 @@ void eapol_sm_dump_state(FILE *f, const char *prefix, "%s authAuthReauthsWhileAuthenticated=%d\n" "%s authAuthEapStartsWhileAuthenticated=%d\n" "%s authAuthEapLogoffWhileAuthenticated=%d\n", - prefix, prefix, auth_pae_state_txt(sm->auth_pae.state), prefix, - _SB(sm->auth_pae.eapolLogoff), _SB(sm->auth_pae.eapolStart), - _SB(sm->auth_pae.eapRestart), prefix, - port_type_txt(sm->auth_pae.portMode), sm->auth_pae.reAuthCount, - prefix, sm->auth_pae.quietPeriod, sm->auth_pae.reAuthMax, - prefix, sm->auth_pae.authEntersConnecting, - prefix, sm->auth_pae.authEapLogoffsWhileConnecting, - prefix, sm->auth_pae.authEntersAuthenticating, - prefix, sm->auth_pae.authAuthSuccessesWhileAuthenticating, - prefix, sm->auth_pae.authAuthTimeoutsWhileAuthenticating, - prefix, sm->auth_pae.authAuthFailWhileAuthenticating, - prefix, sm->auth_pae.authAuthEapStartsWhileAuthenticating, - prefix, sm->auth_pae.authAuthEapLogoffWhileAuthenticating, - prefix, sm->auth_pae.authAuthReauthsWhileAuthenticated, - prefix, sm->auth_pae.authAuthEapStartsWhileAuthenticated, - prefix, sm->auth_pae.authAuthEapLogoffWhileAuthenticated); + prefix, prefix, auth_pae_state_txt(sm->auth_pae_state), prefix, + _SB(sm->eapolLogoff), _SB(sm->eapolStart), _SB(sm->eapRestart), + prefix, port_type_txt(sm->portMode), sm->reAuthCount, + prefix, sm->quietPeriod, sm->reAuthMax, + prefix, sm->authEntersConnecting, + prefix, sm->authEapLogoffsWhileConnecting, + prefix, sm->authEntersAuthenticating, + prefix, sm->authAuthSuccessesWhileAuthenticating, + prefix, sm->authAuthTimeoutsWhileAuthenticating, + prefix, sm->authAuthFailWhileAuthenticating, + prefix, sm->authAuthEapStartsWhileAuthenticating, + prefix, sm->authAuthEapLogoffWhileAuthenticating, + prefix, sm->authAuthReauthsWhileAuthenticated, + prefix, sm->authAuthEapStartsWhileAuthenticated, + prefix, sm->authAuthEapLogoffWhileAuthenticated); fprintf(f, "%s Backend Authentication:\n" "%s state=%s\n" @@ -1049,42 +1040,39 @@ void eapol_sm_dump_state(FILE *f, const char *prefix, "%s backendAuthSuccesses=%d\n" "%s backendAuthFails=%d\n", prefix, prefix, - be_auth_state_txt(sm->be_auth.state), - prefix, _SB(sm->be_auth.eapNoReq), _SB(sm->be_auth.eapReq), - _SB(sm->be_auth.eapResp), - prefix, sm->be_auth.serverTimeout, - prefix, sm->be_auth.backendResponses, - prefix, sm->be_auth.backendAccessChallenges, - prefix, sm->be_auth.backendOtherRequestsToSupplicant, - prefix, sm->be_auth.backendAuthSuccesses, - prefix, sm->be_auth.backendAuthFails); + be_auth_state_txt(sm->be_auth_state), + prefix, _SB(sm->eapNoReq), _SB(sm->eapReq), _SB(sm->eapResp), + prefix, sm->serverTimeout, + prefix, sm->backendResponses, + prefix, sm->backendAccessChallenges, + prefix, sm->backendOtherRequestsToSupplicant, + prefix, sm->backendAuthSuccesses, + prefix, sm->backendAuthFails); fprintf(f, "%s Reauthentication Timer:\n" "%s state=%s\n" "%s reAuthPeriod=%d reAuthEnabled=%s\n", prefix, prefix, - reauth_timer_state_txt(sm->reauth_timer.state), prefix, - sm->reauth_timer.reAuthPeriod, - _SB(sm->reauth_timer.reAuthEnabled)); + reauth_timer_state_txt(sm->reauth_timer_state), prefix, + sm->reAuthPeriod, _SB(sm->reAuthEnabled)); fprintf(f, "%s Authenticator Key Transmit:\n" "%s state=%s\n", prefix, prefix, - auth_key_tx_state_txt(sm->auth_key_tx.state)); + auth_key_tx_state_txt(sm->auth_key_tx_state)); fprintf(f, "%s Key Receive:\n" "%s state=%s\n" "%s rxKey=%s\n", prefix, prefix, - key_rx_state_txt(sm->key_rx.state), - prefix, _SB(sm->key_rx.rxKey)); + key_rx_state_txt(sm->key_rx_state), prefix, _SB(sm->rxKey)); fprintf(f, "%s Controlled Directions:\n" "%s state=%s\n" "%s adminControlledDirections=%s " "operControlledDirections=%s\n" "%s operEdge=%s\n", prefix, prefix, - ctrl_dir_state_txt(sm->ctrl_dir.state), - prefix, ctrl_dir_txt(sm->ctrl_dir.adminControlledDirections), - ctrl_dir_txt(sm->ctrl_dir.operControlledDirections), - prefix, _SB(sm->ctrl_dir.operEdge)); + ctrl_dir_state_txt(sm->ctrl_dir_state), + prefix, ctrl_dir_txt(sm->adminControlledDirections), + ctrl_dir_txt(sm->operControlledDirections), + prefix, _SB(sm->operEdge)); #undef _SB } #endif /* HOSTAPD_DUMP_STATE */ @@ -1099,15 +1087,15 @@ static Boolean eapol_sm_get_bool(void *ctx, enum eapol_bool_var variable) case EAPOL_eapSuccess: return sm->eapSuccess; case EAPOL_eapRestart: - return sm->auth_pae.eapRestart; + return sm->eapRestart; case EAPOL_eapFail: return sm->eapFail; case EAPOL_eapResp: - return sm->be_auth.eapResp; + return sm->eapResp; case EAPOL_eapReq: - return sm->be_auth.eapReq; + return sm->eapReq; case EAPOL_eapNoReq: - return sm->be_auth.eapNoReq; + return sm->eapNoReq; case EAPOL_portEnabled: return sm->portEnabled; case EAPOL_eapTimeout: @@ -1128,19 +1116,19 @@ static void eapol_sm_set_bool(void *ctx, enum eapol_bool_var variable, sm->eapSuccess = value; break; case EAPOL_eapRestart: - sm->auth_pae.eapRestart = value; + sm->eapRestart = value; break; case EAPOL_eapFail: sm->eapFail = value; break; case EAPOL_eapResp: - sm->be_auth.eapResp = value; + sm->eapResp = value; break; case EAPOL_eapReq: - sm->be_auth.eapReq = value; + sm->eapReq = value; break; case EAPOL_eapNoReq: - sm->be_auth.eapNoReq = value; + sm->eapNoReq = value; break; case EAPOL_portEnabled: sm->portEnabled = value; @@ -1214,6 +1202,7 @@ static int eapol_sm_get_eap_user(void *ctx, const u8 *identity, { 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); @@ -1222,9 +1211,13 @@ static int eapol_sm_get_eap_user(void *ctx, const u8 *identity, memset(user, 0, sizeof(*user)); user->phase2 = phase2; - memcpy(user->methods, eap_user->methods, - EAP_USER_MAX_METHODS > EAP_MAX_METHODS ? - EAP_USER_MAX_METHODS : EAP_MAX_METHODS); + 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); @@ -1257,3 +1250,15 @@ static struct eapol_callbacks eapol_cb = .get_eap_user = eapol_sm_get_eap_user, .get_eap_req_id_text = eapol_sm_get_eap_req_id_text, }; + + +int eapol_sm_eap_pending_cb(struct eapol_state_machine *sm, void *ctx) +{ + if (sm == NULL || ctx != sm->eap) + return -1; + + eap_sm_pending_cb(sm->eap); + eapol_sm_step(sm); + + return 0; +} diff --git a/contrib/hostapd/eapol_sm.h b/contrib/hostapd/eapol_sm.h index 0c34b4f..dcb5ee9 100644 --- a/contrib/hostapd/eapol_sm.h +++ b/contrib/hostapd/eapol_sm.h @@ -1,9 +1,23 @@ +/* + * hostapd / IEEE 802.1X Authenticator - EAPOL state machine + * Copyright (c) 2002-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 EAPOL_SM_H #define EAPOL_SM_H #include "defs.h" -/* IEEE Std 802.1X-REV-d11, Ch. 8.2 */ +/* IEEE Std 802.1X-2004, Ch. 8.2 */ typedef enum { ForceUnauthorized = 1, ForceAuthorized = 3, Auto = 2 } PortTypes; @@ -11,22 +25,64 @@ typedef enum { Unauthorized = 2, Authorized = 1 } PortState; typedef enum { Both = 0, In = 1 } ControlledDirection; typedef unsigned int Counter; +struct eap_sm; + +struct radius_attr_data { + u8 *data; + size_t len; +}; + +struct radius_class_data { + struct radius_attr_data *attr; + size_t count; +}; + +struct eapol_state_machine { + /* timers */ + int aWhile; + int quietWhile; + int reAuthWhen; -/* Authenticator PAE state machine */ -struct eapol_auth_pae_sm { + /* global variables */ + Boolean authAbort; + Boolean authFail; + PortState authPortStatus; + 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; + + /* Port Timers state machine */ + /* 'Boolean tick' implicitly handled as registered timeout */ + + /* Authenticator PAE state machine */ + enum { AUTH_PAE_INITIALIZE, AUTH_PAE_DISCONNECTED, AUTH_PAE_CONNECTING, + AUTH_PAE_AUTHENTICATING, AUTH_PAE_AUTHENTICATED, + AUTH_PAE_ABORTING, AUTH_PAE_HELD, AUTH_PAE_FORCE_AUTH, + AUTH_PAE_FORCE_UNAUTH, AUTH_PAE_RESTART } auth_pae_state; /* variables */ Boolean eapolLogoff; Boolean eapolStart; Boolean eapRestart; PortTypes portMode; unsigned int reAuthCount; - /* constants */ unsigned int quietPeriod; /* default 60; 0..65535 */ #define AUTH_PAE_DEFAULT_quietPeriod 60 unsigned int reAuthMax; /* default 2 */ #define AUTH_PAE_DEFAULT_reAuthMax 2 - /* counters */ Counter authEntersConnecting; Counter authEapLogoffsWhileConnecting; @@ -40,24 +96,18 @@ struct eapol_auth_pae_sm { Counter authAuthEapStartsWhileAuthenticated; Counter authAuthEapLogoffWhileAuthenticated; - enum { AUTH_PAE_INITIALIZE, AUTH_PAE_DISCONNECTED, AUTH_PAE_CONNECTING, - AUTH_PAE_AUTHENTICATING, AUTH_PAE_AUTHENTICATED, - AUTH_PAE_ABORTING, AUTH_PAE_HELD, AUTH_PAE_FORCE_AUTH, - AUTH_PAE_FORCE_UNAUTH, AUTH_PAE_RESTART } state; -}; - - -/* Backend Authentication state machine */ -struct eapol_backend_auth_sm { + /* Backend Authentication state machine */ + enum { BE_AUTH_REQUEST, BE_AUTH_RESPONSE, BE_AUTH_SUCCESS, + BE_AUTH_FAIL, BE_AUTH_TIMEOUT, BE_AUTH_IDLE, BE_AUTH_INITIALIZE, + BE_AUTH_IGNORE + } be_auth_state; /* variables */ Boolean eapNoReq; Boolean eapReq; Boolean eapResp; - /* constants */ unsigned int serverTimeout; /* default 30; 1..X */ #define BE_AUTH_DEFAULT_serverTimeout 30 - /* counters */ Counter backendResponses; Counter backendAccessChallenges; @@ -65,98 +115,29 @@ struct eapol_backend_auth_sm { Counter backendAuthSuccesses; Counter backendAuthFails; - enum { BE_AUTH_REQUEST, BE_AUTH_RESPONSE, BE_AUTH_SUCCESS, - BE_AUTH_FAIL, BE_AUTH_TIMEOUT, BE_AUTH_IDLE, BE_AUTH_INITIALIZE, - BE_AUTH_IGNORE - } state; -}; - - -/* Reauthentication Timer state machine */ -struct eapol_reauth_timer_sm { + /* Reauthentication Timer state machine */ + enum { REAUTH_TIMER_INITIALIZE, REAUTH_TIMER_REAUTHENTICATE + } reauth_timer_state; /* constants */ unsigned int reAuthPeriod; /* default 3600 s */ Boolean reAuthEnabled; - enum { REAUTH_TIMER_INITIALIZE, REAUTH_TIMER_REAUTHENTICATE } state; -}; - + /* Authenticator Key Transmit state machine */ + enum { AUTH_KEY_TX_NO_KEY_TRANSMIT, AUTH_KEY_TX_KEY_TRANSMIT + } auth_key_tx_state; -/* Authenticator Key Transmit state machine */ -struct eapol_auth_key_tx { - enum { AUTH_KEY_TX_NO_KEY_TRANSMIT, AUTH_KEY_TX_KEY_TRANSMIT } state; -}; - - -/* Key Receive state machine */ -struct eapol_key_rx { + /* Key Receive state machine */ + enum { KEY_RX_NO_KEY_RECEIVE, KEY_RX_KEY_RECEIVE } key_rx_state; /* variables */ Boolean rxKey; - enum { KEY_RX_NO_KEY_RECEIVE, KEY_RX_KEY_RECEIVE } state; -}; - - -/* Controlled Directions state machine */ -struct eapol_ctrl_dir { + /* Controlled Directions state machine */ + enum { CTRL_DIR_FORCE_BOTH, CTRL_DIR_IN_OR_BOTH } ctrl_dir_state; /* variables */ ControlledDirection adminControlledDirections; ControlledDirection operControlledDirections; Boolean operEdge; - enum { CTRL_DIR_FORCE_BOTH, CTRL_DIR_IN_OR_BOTH } state; -}; - - -struct eap_sm; - -struct radius_attr_data { - u8 *data; - size_t len; -}; - -struct radius_class_data { - struct radius_attr_data *attr; - size_t count; -}; - -struct eapol_state_machine { - /* timers */ - int aWhile; - int quietWhile; - int reAuthWhen; - - /* global variables */ - Boolean authAbort; - Boolean authFail; - PortState authPortStatus; - Boolean authStart; - Boolean authTimeout; - Boolean authSuccess; - Boolean 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; - - /* Port Timers state machine */ - /* 'Boolean tick' implicitly handled as registered timeout */ - - struct eapol_auth_pae_sm auth_pae; - struct eapol_backend_auth_sm be_auth; - struct eapol_reauth_timer_sm reauth_timer; - struct eapol_auth_key_tx auth_key_tx; - struct eapol_key_rx key_rx; - struct eapol_ctrl_dir ctrl_dir; - /* Authenticator Statistics Table */ Counter dot1xAuthEapolFramesRx; Counter dot1xAuthEapolFramesTx; @@ -185,6 +166,9 @@ struct eapol_state_machine { size_t last_eap_radius_len; u8 *identity; size_t identity_len; + u8 eap_type_authsrv; /* EAP type of the last EAP packet from + * Authentication server */ + u8 eap_type_supp; /* EAP type of the last EAP packet from Supplicant */ struct radius_class_data radius_class; /* Keys for encrypting and signing EAPOL-Key frames */ @@ -205,6 +189,7 @@ struct eapol_state_machine { u8 currentId; Boolean initializing; /* in process of initializing state machines */ + Boolean changed; /* Somewhat nasty pointers to global hostapd and STA data to avoid * passing these to every function */ @@ -213,12 +198,13 @@ struct eapol_state_machine { }; -struct eapol_state_machine *eapol_sm_alloc(hostapd *hapd, +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); #endif /* EAPOL_SM_H */ diff --git a/contrib/hostapd/eapol_version.patch b/contrib/hostapd/eapol_version.patch new file mode 100644 index 0000000..c02e367 --- /dev/null +++ b/contrib/hostapd/eapol_version.patch @@ -0,0 +1,129 @@ +? 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/eloop.c b/contrib/hostapd/eloop.c index 39fccb8..232e753 100644 --- a/contrib/hostapd/eloop.c +++ b/contrib/hostapd/eloop.c @@ -1,6 +1,6 @@ /* * Event loop based on select() loop - * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * Copyright (c) 2002-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 @@ -12,19 +12,9 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <sys/time.h> -#include <sys/types.h> -#include <unistd.h> -#include <errno.h> -#include <signal.h> +#include "includes.h" -#ifdef CONFIG_NATIVE_WINDOWS #include "common.h" -#endif /* CONFIG_NATIVE_WINDOWS */ - #include "eloop.h" @@ -32,29 +22,38 @@ struct eloop_sock { int sock; void *eloop_data; void *user_data; - void (*handler)(int sock, void *eloop_ctx, void *sock_ctx); + eloop_sock_handler handler; }; struct eloop_timeout { - struct timeval time; + struct os_time time; void *eloop_data; void *user_data; - void (*handler)(void *eloop_ctx, void *sock_ctx); + eloop_timeout_handler handler; struct eloop_timeout *next; }; struct eloop_signal { int sig; void *user_data; - void (*handler)(int sig, void *eloop_ctx, void *signal_ctx); + eloop_signal_handler handler; int signaled; }; +struct eloop_sock_table { + int count; + struct eloop_sock *table; + int changed; +}; + struct eloop_data { void *user_data; - int max_sock, reader_count; - struct eloop_sock *readers; + int max_sock; + + struct eloop_sock_table readers; + struct eloop_sock_table writers; + struct eloop_sock_table exceptions; struct eloop_timeout *timeout; @@ -64,81 +63,181 @@ struct eloop_data { int pending_terminate; int terminate; + int reader_table_changed; }; static struct eloop_data eloop; -void eloop_init(void *user_data) +int eloop_init(void *user_data) { - memset(&eloop, 0, sizeof(eloop)); + os_memset(&eloop, 0, sizeof(eloop)); eloop.user_data = user_data; + return 0; } -int eloop_register_read_sock(int sock, - void (*handler)(int sock, void *eloop_ctx, - void *sock_ctx), - void *eloop_data, void *user_data) +static int eloop_sock_table_add_sock(struct eloop_sock_table *table, + int sock, eloop_sock_handler handler, + void *eloop_data, void *user_data) { struct eloop_sock *tmp; + if (table == NULL) + return -1; + tmp = (struct eloop_sock *) - realloc(eloop.readers, - (eloop.reader_count + 1) * sizeof(struct eloop_sock)); + os_realloc(table->table, + (table->count + 1) * sizeof(struct eloop_sock)); if (tmp == NULL) return -1; - tmp[eloop.reader_count].sock = sock; - tmp[eloop.reader_count].eloop_data = eloop_data; - tmp[eloop.reader_count].user_data = user_data; - tmp[eloop.reader_count].handler = handler; - eloop.reader_count++; - eloop.readers = tmp; + tmp[table->count].sock = sock; + tmp[table->count].eloop_data = eloop_data; + tmp[table->count].user_data = user_data; + tmp[table->count].handler = handler; + table->count++; + table->table = tmp; if (sock > eloop.max_sock) eloop.max_sock = sock; + table->changed = 1; return 0; } -void eloop_unregister_read_sock(int sock) +static void eloop_sock_table_remove_sock(struct eloop_sock_table *table, + int sock) { int i; - if (eloop.readers == NULL || eloop.reader_count == 0) + if (table == NULL || table->table == NULL || table->count == 0) return; - for (i = 0; i < eloop.reader_count; i++) { - if (eloop.readers[i].sock == sock) + for (i = 0; i < table->count; i++) { + if (table->table[i].sock == sock) break; } - if (i == eloop.reader_count) + if (i == table->count) return; - if (i != eloop.reader_count - 1) { - memmove(&eloop.readers[i], &eloop.readers[i + 1], - (eloop.reader_count - i - 1) * - sizeof(struct eloop_sock)); + if (i != table->count - 1) { + os_memmove(&table->table[i], &table->table[i + 1], + (table->count - i - 1) * + sizeof(struct eloop_sock)); + } + table->count--; + table->changed = 1; +} + + +static void eloop_sock_table_set_fds(struct eloop_sock_table *table, + fd_set *fds) +{ + int i; + + FD_ZERO(fds); + + if (table->table == NULL) + return; + + for (i = 0; i < table->count; i++) + FD_SET(table->table[i].sock, fds); +} + + +static void eloop_sock_table_dispatch(struct eloop_sock_table *table, + fd_set *fds) +{ + int i; + + if (table == NULL || table->table == NULL) + return; + + table->changed = 0; + for (i = 0; i < table->count; i++) { + if (FD_ISSET(table->table[i].sock, fds)) { + table->table[i].handler(table->table[i].sock, + table->table[i].eloop_data, + table->table[i].user_data); + if (table->changed) + break; + } + } +} + + +static void eloop_sock_table_destroy(struct eloop_sock_table *table) +{ + if (table) + os_free(table->table); +} + + +int eloop_register_read_sock(int sock, eloop_sock_handler handler, + void *eloop_data, void *user_data) +{ + return eloop_register_sock(sock, EVENT_TYPE_READ, handler, + eloop_data, user_data); +} + + +void eloop_unregister_read_sock(int sock) +{ + eloop_unregister_sock(sock, EVENT_TYPE_READ); +} + + +static struct eloop_sock_table *eloop_get_sock_table(eloop_event_type type) +{ + switch (type) { + case EVENT_TYPE_READ: + return &eloop.readers; + case EVENT_TYPE_WRITE: + return &eloop.writers; + case EVENT_TYPE_EXCEPTION: + return &eloop.exceptions; } - eloop.reader_count--; + + return NULL; +} + + +int eloop_register_sock(int sock, eloop_event_type type, + eloop_sock_handler handler, + void *eloop_data, void *user_data) +{ + struct eloop_sock_table *table; + + table = eloop_get_sock_table(type); + return eloop_sock_table_add_sock(table, sock, handler, + eloop_data, user_data); +} + + +void eloop_unregister_sock(int sock, eloop_event_type type) +{ + struct eloop_sock_table *table; + + table = eloop_get_sock_table(type); + eloop_sock_table_remove_sock(table, sock); } int eloop_register_timeout(unsigned int secs, unsigned int usecs, - void (*handler)(void *eloop_ctx, void *timeout_ctx), + eloop_timeout_handler handler, void *eloop_data, void *user_data) { struct eloop_timeout *timeout, *tmp, *prev; - timeout = (struct eloop_timeout *) malloc(sizeof(*timeout)); + timeout = os_malloc(sizeof(*timeout)); if (timeout == NULL) return -1; - gettimeofday(&timeout->time, NULL); - timeout->time.tv_sec += secs; - timeout->time.tv_usec += usecs; - while (timeout->time.tv_usec >= 1000000) { - timeout->time.tv_sec++; - timeout->time.tv_usec -= 1000000; + os_get_time(&timeout->time); + timeout->time.sec += secs; + timeout->time.usec += usecs; + while (timeout->time.usec >= 1000000) { + timeout->time.sec++; + timeout->time.usec -= 1000000; } timeout->eloop_data = eloop_data; timeout->user_data = user_data; @@ -153,7 +252,7 @@ int eloop_register_timeout(unsigned int secs, unsigned int usecs, prev = NULL; tmp = eloop.timeout; while (tmp != NULL) { - if (timercmp(&timeout->time, &tmp->time, <)) + if (os_time_before(&timeout->time, &tmp->time)) break; prev = tmp; tmp = tmp->next; @@ -171,7 +270,7 @@ int eloop_register_timeout(unsigned int secs, unsigned int usecs, } -int eloop_cancel_timeout(void (*handler)(void *eloop_ctx, void *sock_ctx), +int eloop_cancel_timeout(eloop_timeout_handler handler, void *eloop_data, void *user_data) { struct eloop_timeout *timeout, *prev, *next; @@ -191,7 +290,7 @@ int eloop_cancel_timeout(void (*handler)(void *eloop_ctx, void *sock_ctx), eloop.timeout = next; else prev->next = next; - free(timeout); + os_free(timeout); removed++; } else prev = timeout; @@ -266,17 +365,15 @@ static void eloop_process_pending_signals(void) } -int eloop_register_signal(int sig, - void (*handler)(int sig, void *eloop_ctx, - void *signal_ctx), +int eloop_register_signal(int sig, eloop_signal_handler handler, void *user_data) { struct eloop_signal *tmp; tmp = (struct eloop_signal *) - realloc(eloop.signals, - (eloop.signal_count + 1) * - sizeof(struct eloop_signal)); + os_realloc(eloop.signals, + (eloop.signal_count + 1) * + sizeof(struct eloop_signal)); if (tmp == NULL) return -1; @@ -292,41 +389,67 @@ int eloop_register_signal(int sig, } -void eloop_run(void) +int eloop_register_signal_terminate(eloop_signal_handler handler, + void *user_data) { - fd_set *rfds; - int i, res; - struct timeval tv, now; + int ret = eloop_register_signal(SIGINT, handler, user_data); + if (ret == 0) + ret = eloop_register_signal(SIGTERM, handler, user_data); + return ret; +} + - rfds = malloc(sizeof(*rfds)); - if (rfds == NULL) { +int eloop_register_signal_reconfig(eloop_signal_handler handler, + void *user_data) +{ +#ifdef CONFIG_NATIVE_WINDOWS + return 0; +#else /* CONFIG_NATIVE_WINDOWS */ + return eloop_register_signal(SIGHUP, handler, user_data); +#endif /* CONFIG_NATIVE_WINDOWS */ +} + + +void eloop_run(void) +{ + fd_set *rfds, *wfds, *efds; + int res; + struct timeval _tv; + struct os_time tv, now; + + rfds = os_malloc(sizeof(*rfds)); + wfds = os_malloc(sizeof(*wfds)); + efds = os_malloc(sizeof(*efds)); + if (rfds == NULL || wfds == NULL || efds == NULL) { printf("eloop_run - malloc failed\n"); - return; + goto out; } while (!eloop.terminate && - (eloop.timeout || eloop.reader_count > 0)) { + (eloop.timeout || eloop.readers.count > 0 || + eloop.writers.count > 0 || eloop.exceptions.count > 0)) { if (eloop.timeout) { - gettimeofday(&now, NULL); - if (timercmp(&now, &eloop.timeout->time, <)) - timersub(&eloop.timeout->time, &now, &tv); + os_get_time(&now); + if (os_time_before(&now, &eloop.timeout->time)) + os_time_sub(&eloop.timeout->time, &now, &tv); else - tv.tv_sec = tv.tv_usec = 0; + tv.sec = tv.usec = 0; #if 0 printf("next timeout in %lu.%06lu sec\n", - tv.tv_sec, tv.tv_usec); + tv.sec, tv.usec); #endif + _tv.tv_sec = tv.sec; + _tv.tv_usec = tv.usec; } - FD_ZERO(rfds); - for (i = 0; i < eloop.reader_count; i++) - FD_SET(eloop.readers[i].sock, rfds); - res = select(eloop.max_sock + 1, rfds, NULL, NULL, - eloop.timeout ? &tv : NULL); - if (res < 0 && errno != EINTR) { + eloop_sock_table_set_fds(&eloop.readers, rfds); + eloop_sock_table_set_fds(&eloop.writers, wfds); + eloop_sock_table_set_fds(&eloop.exceptions, efds); + res = select(eloop.max_sock + 1, rfds, wfds, efds, + eloop.timeout ? &_tv : NULL); + if (res < 0 && errno != EINTR && errno != 0) { perror("select"); - free(rfds); - return; + goto out; } eloop_process_pending_signals(); @@ -334,13 +457,13 @@ void eloop_run(void) if (eloop.timeout) { struct eloop_timeout *tmp; - gettimeofday(&now, NULL); - if (!timercmp(&now, &eloop.timeout->time, <)) { + os_get_time(&now); + if (!os_time_before(&now, &eloop.timeout->time)) { tmp = eloop.timeout; eloop.timeout = eloop.timeout->next; tmp->handler(tmp->eloop_data, tmp->user_data); - free(tmp); + os_free(tmp); } } @@ -348,17 +471,15 @@ void eloop_run(void) if (res <= 0) continue; - for (i = 0; i < eloop.reader_count; i++) { - if (FD_ISSET(eloop.readers[i].sock, rfds)) { - eloop.readers[i].handler( - eloop.readers[i].sock, - eloop.readers[i].eloop_data, - eloop.readers[i].user_data); - } - } + eloop_sock_table_dispatch(&eloop.readers, rfds); + eloop_sock_table_dispatch(&eloop.writers, wfds); + eloop_sock_table_dispatch(&eloop.exceptions, efds); } - free(rfds); +out: + os_free(rfds); + os_free(wfds); + os_free(efds); } @@ -376,10 +497,12 @@ void eloop_destroy(void) while (timeout != NULL) { prev = timeout; timeout = timeout->next; - free(prev); + os_free(prev); } - free(eloop.readers); - free(eloop.signals); + eloop_sock_table_destroy(&eloop.readers); + eloop_sock_table_destroy(&eloop.writers); + eloop_sock_table_destroy(&eloop.exceptions); + os_free(eloop.signals); } @@ -387,3 +510,22 @@ int eloop_terminated(void) { return eloop.terminate; } + + +void eloop_wait_for_read_sock(int sock) +{ + fd_set rfds; + + if (sock < 0) + return; + + FD_ZERO(&rfds); + FD_SET(sock, &rfds); + select(sock + 1, &rfds, NULL, NULL, NULL); +} + + +void * eloop_get_user_data(void) +{ + return eloop.user_data; +} diff --git a/contrib/hostapd/eloop.h b/contrib/hostapd/eloop.h index c57e682..4dd2871 100644 --- a/contrib/hostapd/eloop.h +++ b/contrib/hostapd/eloop.h @@ -1,6 +1,6 @@ /* * Event loop - * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * 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 @@ -23,18 +23,67 @@ #ifndef ELOOP_H #define ELOOP_H -/* Magic number for eloop_cancel_timeout() */ +/** + * ELOOP_ALL_CTX - eloop_cancel_timeout() magic number to match all timeouts + */ #define ELOOP_ALL_CTX (void *) -1 /** + * eloop_event_type - eloop socket event type for eloop_register_sock() + * @EVENT_TYPE_READ: Socket has data available for reading + * @EVENT_TYPE_WRITE: Socket has room for new data to be written + * @EVENT_TYPE_EXCEPTION: An exception has been reported + */ +typedef enum { + EVENT_TYPE_READ = 0, + EVENT_TYPE_WRITE, + EVENT_TYPE_EXCEPTION +} eloop_event_type; + +/** + * eloop_sock_handler - eloop socket event callback type + * @sock: File descriptor number for the socket + * @eloop_ctx: Registered callback context data (eloop_data) + * @sock_ctx: Registered callback context data (user_data) + */ +typedef void (*eloop_sock_handler)(int sock, void *eloop_ctx, void *sock_ctx); + +/** + * eloop_event_handler - eloop generic event callback type + * @eloop_ctx: Registered callback context data (eloop_data) + * @sock_ctx: Registered callback context data (user_data) + */ +typedef void (*eloop_event_handler)(void *eloop_data, void *user_ctx); + +/** + * eloop_timeout_handler - eloop timeout event callback type + * @eloop_ctx: Registered callback context data (eloop_data) + * @sock_ctx: Registered callback context data (user_data) + */ +typedef void (*eloop_timeout_handler)(void *eloop_data, void *user_ctx); + +/** + * eloop_signal_handler - eloop signal event callback type + * @sig: Signal number + * @eloop_ctx: Registered callback context data (global user_data from + * eloop_init() call) + * @signal_ctx: Registered callback context data (user_data from + * eloop_register_signal(), eloop_register_signal_terminate(), or + * eloop_register_signal_reconfig() call) + */ +typedef void (*eloop_signal_handler)(int sig, void *eloop_ctx, + void *signal_ctx); + +/** * eloop_init() - Initialize global event loop data * @user_data: Pointer to global data passed as eloop_ctx to signal handlers + * Returns: 0 on success, -1 on failure * * This function must be called before any other eloop_* function. user_data * can be used to configure a global (to the process) pointer that will be * passed as eloop_ctx parameter to signal handlers. */ -void eloop_init(void *user_data); +int eloop_init(void *user_data); /** * eloop_register_read_sock - Register handler for read events @@ -46,11 +95,11 @@ void eloop_init(void *user_data); * * Register a read socket notifier for the given file descriptor. The handler * function will be called whenever data is available for reading from the - * socket. + * socket. The handler function is responsible for clearing the event after + * having processed it in order to avoid eloop from calling the handler again + * for the same event. */ -int eloop_register_read_sock(int sock, - void (*handler)(int sock, void *eloop_ctx, - void *sock_ctx), +int eloop_register_read_sock(int sock, eloop_sock_handler handler, void *eloop_data, void *user_data); /** @@ -63,6 +112,71 @@ int eloop_register_read_sock(int sock, void eloop_unregister_read_sock(int sock); /** + * eloop_register_sock - Register handler for socket events + * @sock: File descriptor number for the socket + * @type: Type of event to wait for + * @handler: Callback function to be called when the event is triggered + * @eloop_data: Callback context data (eloop_ctx) + * @user_data: Callback context data (sock_ctx) + * Returns: 0 on success, -1 on failure + * + * Register an event notifier for the given socket's file descriptor. The + * handler function will be called whenever the that event is triggered for the + * socket. The handler function is responsible for clearing the event after + * having processed it in order to avoid eloop from calling the handler again + * for the same event. + */ +int eloop_register_sock(int sock, eloop_event_type type, + eloop_sock_handler handler, + void *eloop_data, void *user_data); + +/** + * eloop_unregister_sock - Unregister handler for socket events + * @sock: File descriptor number for the socket + * @type: Type of event for which sock was registered + * + * Unregister a socket event notifier that was previously registered with + * eloop_register_sock(). + */ +void eloop_unregister_sock(int sock, eloop_event_type type); + +/** + * eloop_register_event - Register handler for generic events + * @event: Event to wait (eloop implementation specific) + * @event_size: Size of event data + * @handler: Callback function to be called when event is triggered + * @eloop_data: Callback context data (eloop_data) + * @user_data: Callback context data (user_data) + * Returns: 0 on success, -1 on failure + * + * Register an event handler for the given event. This function is used to + * register eloop implementation specific events which are mainly targetted for + * operating system specific code (driver interface and l2_packet) since the + * portable code will not be able to use such an OS-specific call. The handler + * function will be called whenever the event is triggered. The handler + * function is responsible for clearing the event after having processed it in + * order to avoid eloop from calling the handler again for the same event. + * + * In case of Windows implementation (eloop_win.c), event pointer is of HANDLE + * type, i.e., void*. The callers are likely to have 'HANDLE h' type variable, + * and they would call this function with eloop_register_event(h, sizeof(h), + * ...). + */ +int eloop_register_event(void *event, size_t event_size, + eloop_event_handler handler, + void *eloop_data, void *user_data); + +/** + * eloop_unregister_event - Unregister handler for a generic event + * @event: Event to cancel (eloop implementation specific) + * @event_size: Size of event data + * + * Unregister a generic event notifier that was previously registered with + * eloop_register_event(). + */ +void eloop_unregister_event(void *event, size_t event_size); + +/** * eloop_register_timeout - Register timeout * @secs: Number of seconds to the timeout * @usecs: Number of microseconds to the timeout @@ -75,7 +189,7 @@ void eloop_unregister_read_sock(int sock); * given time. */ int eloop_register_timeout(unsigned int secs, unsigned int usecs, - void (*handler)(void *eloop_ctx, void *timeout_ctx), + eloop_timeout_handler handler, void *eloop_data, void *user_data); /** @@ -89,7 +203,7 @@ int eloop_register_timeout(unsigned int secs, unsigned int usecs, * eloop_register_timeout(). ELOOP_ALL_CTX can be used as a wildcard for * cancelling all timeouts regardless of eloop_data/user_data. */ -int eloop_cancel_timeout(void (*handler)(void *eloop_ctx, void *sock_ctx), +int eloop_cancel_timeout(eloop_timeout_handler handler, void *eloop_data, void *user_data); /** @@ -100,20 +214,67 @@ int eloop_cancel_timeout(void (*handler)(void *eloop_ctx, void *sock_ctx), * Returns: 0 on success, -1 on failure * * Register a callback function that will be called when a signal is received. - * The calback function is actually called only after the system signal handler - * has returned. This means that the normal limits for sighandlers (i.e., only - * "safe functions" allowed) do not apply for the registered callback. + * The callback function is actually called only after the system signal + * handler has returned. This means that the normal limits for sighandlers + * (i.e., only "safe functions" allowed) do not apply for the registered + * callback. * * Signals are 'global' events and there is no local eloop_data pointer like * with other handlers. The global user_data pointer registered with * eloop_init() will be used as eloop_ctx for signal handlers. */ -int eloop_register_signal(int sig, - void (*handler)(int sig, void *eloop_ctx, - void *signal_ctx), +int eloop_register_signal(int sig, eloop_signal_handler handler, void *user_data); /** + * eloop_register_signal_terminate - Register handler for terminate signals + * @handler: Callback function to be called when the signal is received + * @user_data: Callback context data (signal_ctx) + * Returns: 0 on success, -1 on failure + * + * Register a callback function that will be called when a process termination + * signal is received. The callback function is actually called only after the + * system signal handler has returned. This means that the normal limits for + * sighandlers (i.e., only "safe functions" allowed) do not apply for the + * registered callback. + * + * Signals are 'global' events and there is no local eloop_data pointer like + * with other handlers. The global user_data pointer registered with + * eloop_init() will be used as eloop_ctx for signal handlers. + * + * This function is a more portable version of eloop_register_signal() since + * the knowledge of exact details of the signals is hidden in eloop + * implementation. In case of operating systems using signal(), this function + * registers handlers for SIGINT and SIGTERM. + */ +int eloop_register_signal_terminate(eloop_signal_handler handler, + void *user_data); + +/** + * eloop_register_signal_reconfig - Register handler for reconfig signals + * @handler: Callback function to be called when the signal is received + * @user_data: Callback context data (signal_ctx) + * Returns: 0 on success, -1 on failure + * + * Register a callback function that will be called when a reconfiguration / + * hangup signal is received. The callback function is actually called only + * after the system signal handler has returned. This means that the normal + * limits for sighandlers (i.e., only "safe functions" allowed) do not apply + * for the registered callback. + * + * Signals are 'global' events and there is no local eloop_data pointer like + * with other handlers. The global user_data pointer registered with + * eloop_init() will be used as eloop_ctx for signal handlers. + * + * This function is a more portable version of eloop_register_signal() since + * the knowledge of exact details of the signals is hidden in eloop + * implementation. In case of operating systems using signal(), this function + * registers a handler for SIGHUP. + */ +int eloop_register_signal_reconfig(eloop_signal_handler handler, + void *user_data); + +/** * eloop_run - Start the event loop * * Start the event loop and continue running as long as there are any @@ -133,7 +294,7 @@ void eloop_terminate(void); /** * eloop_destroy - Free any resources allocated for the event loop * - * After calling eloop_destoy(), other eloop_* functions must not be called + * After calling eloop_destroy(), other eloop_* functions must not be called * before re-running eloop_init(). */ void eloop_destroy(void); @@ -149,4 +310,18 @@ void eloop_destroy(void); */ int eloop_terminated(void); +/** + * eloop_wait_for_read_sock - Wait for a single reader + * @sock: File descriptor number for the socket + * + * Do a blocking wait for a single read socket. + */ +void eloop_wait_for_read_sock(int sock); + +/** + * eloop_get_user_data - Get global user data + * Returns: user_data pointer that was registered with eloop_init() + */ +void * eloop_get_user_data(void); + #endif /* ELOOP_H */ diff --git a/contrib/hostapd/eloop_none.c b/contrib/hostapd/eloop_none.c new file mode 100644 index 0000000..6943109 --- /dev/null +++ b/contrib/hostapd/eloop_none.c @@ -0,0 +1,390 @@ +/* + * Event loop - empty template (basic structure, but no OS specific operations) + * Copyright (c) 2002-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" + + +struct eloop_sock { + int sock; + void *eloop_data; + void *user_data; + void (*handler)(int sock, void *eloop_ctx, void *sock_ctx); +}; + +struct eloop_timeout { + struct os_time time; + void *eloop_data; + void *user_data; + void (*handler)(void *eloop_ctx, void *sock_ctx); + struct eloop_timeout *next; +}; + +struct eloop_signal { + int sig; + void *user_data; + void (*handler)(int sig, void *eloop_ctx, void *signal_ctx); + int signaled; +}; + +struct eloop_data { + void *user_data; + + int max_sock, reader_count; + struct eloop_sock *readers; + + struct eloop_timeout *timeout; + + int signal_count; + struct eloop_signal *signals; + int signaled; + int pending_terminate; + + int terminate; + int reader_table_changed; +}; + +static struct eloop_data eloop; + + +int eloop_init(void *user_data) +{ + memset(&eloop, 0, sizeof(eloop)); + eloop.user_data = user_data; + return 0; +} + + +int eloop_register_read_sock(int sock, + void (*handler)(int sock, void *eloop_ctx, + void *sock_ctx), + void *eloop_data, void *user_data) +{ + struct eloop_sock *tmp; + + tmp = (struct eloop_sock *) + realloc(eloop.readers, + (eloop.reader_count + 1) * sizeof(struct eloop_sock)); + if (tmp == NULL) + return -1; + + tmp[eloop.reader_count].sock = sock; + tmp[eloop.reader_count].eloop_data = eloop_data; + tmp[eloop.reader_count].user_data = user_data; + tmp[eloop.reader_count].handler = handler; + eloop.reader_count++; + eloop.readers = tmp; + if (sock > eloop.max_sock) + eloop.max_sock = sock; + eloop.reader_table_changed = 1; + + return 0; +} + + +void eloop_unregister_read_sock(int sock) +{ + int i; + + if (eloop.readers == NULL || eloop.reader_count == 0) + return; + + for (i = 0; i < eloop.reader_count; i++) { + if (eloop.readers[i].sock == sock) + break; + } + if (i == eloop.reader_count) + return; + if (i != eloop.reader_count - 1) { + memmove(&eloop.readers[i], &eloop.readers[i + 1], + (eloop.reader_count - i - 1) * + sizeof(struct eloop_sock)); + } + eloop.reader_count--; + eloop.reader_table_changed = 1; +} + + +int eloop_register_timeout(unsigned int secs, unsigned int usecs, + void (*handler)(void *eloop_ctx, void *timeout_ctx), + void *eloop_data, void *user_data) +{ + struct eloop_timeout *timeout, *tmp, *prev; + + timeout = (struct eloop_timeout *) malloc(sizeof(*timeout)); + if (timeout == NULL) + return -1; + os_get_time(&timeout->time); + timeout->time.sec += secs; + timeout->time.usec += usecs; + while (timeout->time.usec >= 1000000) { + timeout->time.sec++; + timeout->time.usec -= 1000000; + } + timeout->eloop_data = eloop_data; + timeout->user_data = user_data; + timeout->handler = handler; + timeout->next = NULL; + + if (eloop.timeout == NULL) { + eloop.timeout = timeout; + return 0; + } + + prev = NULL; + tmp = eloop.timeout; + while (tmp != NULL) { + if (os_time_before(&timeout->time, &tmp->time)) + break; + prev = tmp; + tmp = tmp->next; + } + + if (prev == NULL) { + timeout->next = eloop.timeout; + eloop.timeout = timeout; + } else { + timeout->next = prev->next; + prev->next = timeout; + } + + return 0; +} + + +int eloop_cancel_timeout(void (*handler)(void *eloop_ctx, void *sock_ctx), + void *eloop_data, void *user_data) +{ + struct eloop_timeout *timeout, *prev, *next; + int removed = 0; + + prev = NULL; + timeout = eloop.timeout; + while (timeout != NULL) { + next = timeout->next; + + if (timeout->handler == handler && + (timeout->eloop_data == eloop_data || + eloop_data == ELOOP_ALL_CTX) && + (timeout->user_data == user_data || + user_data == ELOOP_ALL_CTX)) { + if (prev == NULL) + eloop.timeout = next; + else + prev->next = next; + free(timeout); + removed++; + } else + prev = timeout; + + timeout = next; + } + + return removed; +} + + +/* TODO: replace with suitable signal handler */ +#if 0 +static void eloop_handle_signal(int sig) +{ + int i; + + eloop.signaled++; + for (i = 0; i < eloop.signal_count; i++) { + if (eloop.signals[i].sig == sig) { + eloop.signals[i].signaled++; + break; + } + } +} +#endif + + +static void eloop_process_pending_signals(void) +{ + int i; + + if (eloop.signaled == 0) + return; + eloop.signaled = 0; + + if (eloop.pending_terminate) { + eloop.pending_terminate = 0; + } + + for (i = 0; i < eloop.signal_count; i++) { + if (eloop.signals[i].signaled) { + eloop.signals[i].signaled = 0; + eloop.signals[i].handler(eloop.signals[i].sig, + eloop.user_data, + eloop.signals[i].user_data); + } + } +} + + +int eloop_register_signal(int sig, + void (*handler)(int sig, void *eloop_ctx, + void *signal_ctx), + void *user_data) +{ + struct eloop_signal *tmp; + + tmp = (struct eloop_signal *) + realloc(eloop.signals, + (eloop.signal_count + 1) * + sizeof(struct eloop_signal)); + if (tmp == NULL) + return -1; + + tmp[eloop.signal_count].sig = sig; + tmp[eloop.signal_count].user_data = user_data; + tmp[eloop.signal_count].handler = handler; + tmp[eloop.signal_count].signaled = 0; + eloop.signal_count++; + eloop.signals = tmp; + + /* TODO: register signal handler */ + + return 0; +} + + +int eloop_register_signal_terminate(void (*handler)(int sig, void *eloop_ctx, + void *signal_ctx), + void *user_data) +{ +#if 0 + /* TODO: for example */ + int ret = eloop_register_signal(SIGINT, handler, user_data); + if (ret == 0) + ret = eloop_register_signal(SIGTERM, handler, user_data); + return ret; +#endif + return 0; +} + + +int eloop_register_signal_reconfig(void (*handler)(int sig, void *eloop_ctx, + void *signal_ctx), + void *user_data) +{ +#if 0 + /* TODO: for example */ + return eloop_register_signal(SIGHUP, handler, user_data); +#endif + return 0; +} + + +void eloop_run(void) +{ + int i; + struct os_time tv, now; + + while (!eloop.terminate && + (eloop.timeout || eloop.reader_count > 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; + } + + /* + * TODO: wait for any event (read socket ready, timeout (tv), + * signal + */ + os_sleep(1, 0); /* just a dummy wait for testing */ + + eloop_process_pending_signals(); + + /* check if some registered timeouts have occurred */ + if (eloop.timeout) { + struct eloop_timeout *tmp; + + os_get_time(&now); + if (!os_time_before(&now, &eloop.timeout->time)) { + tmp = eloop.timeout; + eloop.timeout = eloop.timeout->next; + tmp->handler(tmp->eloop_data, + tmp->user_data); + free(tmp); + } + + } + + eloop.reader_table_changed = 0; + for (i = 0; i < eloop.reader_count; i++) { + /* + * TODO: call each handler that has pending data to + * read + */ + if (0 /* TODO: eloop.readers[i].sock ready */) { + eloop.readers[i].handler( + eloop.readers[i].sock, + eloop.readers[i].eloop_data, + eloop.readers[i].user_data); + if (eloop.reader_table_changed) + break; + } + } + } +} + + +void eloop_terminate(void) +{ + eloop.terminate = 1; +} + + +void eloop_destroy(void) +{ + struct eloop_timeout *timeout, *prev; + + timeout = eloop.timeout; + while (timeout != NULL) { + prev = timeout; + timeout = timeout->next; + free(prev); + } + free(eloop.readers); + free(eloop.signals); +} + + +int eloop_terminated(void) +{ + return eloop.terminate; +} + + +void eloop_wait_for_read_sock(int sock) +{ + /* + * TODO: wait for the file descriptor to have something available for + * reading + */ +} + + +void * eloop_get_user_data(void) +{ + return eloop.user_data; +} diff --git a/contrib/hostapd/eloop_win.c b/contrib/hostapd/eloop_win.c new file mode 100644 index 0000000..73f0eaf --- /dev/null +++ b/contrib/hostapd/eloop_win.c @@ -0,0 +1,604 @@ +/* + * Event loop based on Windows events and WaitForMultipleObjects + * 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. + */ + +#include "includes.h" +#include <winsock2.h> + +#include "common.h" +#include "eloop.h" + + +struct eloop_sock { + int sock; + void *eloop_data; + void *user_data; + eloop_sock_handler handler; + WSAEVENT event; +}; + +struct eloop_event { + void *eloop_data; + void *user_data; + eloop_event_handler handler; + HANDLE event; +}; + +struct eloop_timeout { + struct os_time time; + void *eloop_data; + void *user_data; + eloop_timeout_handler handler; + struct eloop_timeout *next; +}; + +struct eloop_signal { + int sig; + void *user_data; + eloop_signal_handler handler; + int signaled; +}; + +struct eloop_data { + void *user_data; + + int max_sock; + size_t reader_count; + struct eloop_sock *readers; + + size_t event_count; + struct eloop_event *events; + + struct eloop_timeout *timeout; + + int signal_count; + struct eloop_signal *signals; + int signaled; + int pending_terminate; + + int terminate; + int reader_table_changed; + + struct eloop_signal term_signal; + HANDLE term_event; + + HANDLE *handles; + size_t num_handles; +}; + +static struct eloop_data eloop; + + +int eloop_init(void *user_data) +{ + os_memset(&eloop, 0, sizeof(eloop)); + eloop.user_data = user_data; + eloop.num_handles = 1; + eloop.handles = os_malloc(eloop.num_handles * + sizeof(eloop.handles[0])); + if (eloop.handles == NULL) + return -1; + + eloop.term_event = CreateEvent(NULL, FALSE, FALSE, NULL); + if (eloop.term_event == NULL) { + printf("CreateEvent() failed: %d\n", + (int) GetLastError()); + os_free(eloop.handles); + return -1; + } + + return 0; +} + + +static int eloop_prepare_handles(void) +{ + HANDLE *n; + + if (eloop.num_handles > eloop.reader_count + eloop.event_count + 8) + return 0; + n = os_realloc(eloop.handles, + eloop.num_handles * 2 * sizeof(eloop.handles[0])); + if (n == NULL) + return -1; + eloop.handles = n; + eloop.num_handles *= 2; + return 0; +} + + +int eloop_register_read_sock(int sock, eloop_sock_handler handler, + void *eloop_data, void *user_data) +{ + WSAEVENT event; + struct eloop_sock *tmp; + + if (eloop_prepare_handles()) + return -1; + + event = WSACreateEvent(); + if (event == WSA_INVALID_EVENT) { + printf("WSACreateEvent() failed: %d\n", WSAGetLastError()); + return -1; + } + + if (WSAEventSelect(sock, event, FD_READ)) { + printf("WSAEventSelect() failed: %d\n", WSAGetLastError()); + WSACloseEvent(event); + return -1; + } + tmp = os_realloc(eloop.readers, + (eloop.reader_count + 1) * sizeof(struct eloop_sock)); + if (tmp == NULL) { + WSAEventSelect(sock, event, 0); + WSACloseEvent(event); + return -1; + } + + tmp[eloop.reader_count].sock = sock; + tmp[eloop.reader_count].eloop_data = eloop_data; + tmp[eloop.reader_count].user_data = user_data; + tmp[eloop.reader_count].handler = handler; + tmp[eloop.reader_count].event = event; + eloop.reader_count++; + eloop.readers = tmp; + if (sock > eloop.max_sock) + eloop.max_sock = sock; + eloop.reader_table_changed = 1; + + return 0; +} + + +void eloop_unregister_read_sock(int sock) +{ + size_t i; + + if (eloop.readers == NULL || eloop.reader_count == 0) + return; + + for (i = 0; i < eloop.reader_count; i++) { + if (eloop.readers[i].sock == sock) + break; + } + if (i == eloop.reader_count) + return; + + WSAEventSelect(eloop.readers[i].sock, eloop.readers[i].event, 0); + WSACloseEvent(eloop.readers[i].event); + + if (i != eloop.reader_count - 1) { + os_memmove(&eloop.readers[i], &eloop.readers[i + 1], + (eloop.reader_count - i - 1) * + sizeof(struct eloop_sock)); + } + eloop.reader_count--; + eloop.reader_table_changed = 1; +} + + +int eloop_register_event(void *event, size_t event_size, + eloop_event_handler handler, + void *eloop_data, void *user_data) +{ + struct eloop_event *tmp; + HANDLE h = event; + + if (event_size != sizeof(HANDLE) || h == INVALID_HANDLE_VALUE) + return -1; + + if (eloop_prepare_handles()) + return -1; + + tmp = os_realloc(eloop.events, + (eloop.event_count + 1) * sizeof(struct eloop_event)); + if (tmp == NULL) + return -1; + + tmp[eloop.event_count].eloop_data = eloop_data; + tmp[eloop.event_count].user_data = user_data; + tmp[eloop.event_count].handler = handler; + tmp[eloop.event_count].event = h; + eloop.event_count++; + eloop.events = tmp; + + return 0; +} + + +void eloop_unregister_event(void *event, size_t event_size) +{ + size_t i; + HANDLE h = event; + + if (eloop.events == NULL || eloop.event_count == 0 || + event_size != sizeof(HANDLE)) + return; + + for (i = 0; i < eloop.event_count; i++) { + if (eloop.events[i].event == h) + break; + } + if (i == eloop.event_count) + return; + + if (i != eloop.event_count - 1) { + os_memmove(&eloop.events[i], &eloop.events[i + 1], + (eloop.event_count - i - 1) * + sizeof(struct eloop_event)); + } + eloop.event_count--; +} + + +int eloop_register_timeout(unsigned int secs, unsigned int usecs, + eloop_timeout_handler handler, + void *eloop_data, void *user_data) +{ + struct eloop_timeout *timeout, *tmp, *prev; + + timeout = os_malloc(sizeof(*timeout)); + if (timeout == NULL) + return -1; + os_get_time(&timeout->time); + timeout->time.sec += secs; + timeout->time.usec += usecs; + while (timeout->time.usec >= 1000000) { + timeout->time.sec++; + timeout->time.usec -= 1000000; + } + timeout->eloop_data = eloop_data; + timeout->user_data = user_data; + timeout->handler = handler; + timeout->next = NULL; + + if (eloop.timeout == NULL) { + eloop.timeout = timeout; + return 0; + } + + prev = NULL; + tmp = eloop.timeout; + while (tmp != NULL) { + if (os_time_before(&timeout->time, &tmp->time)) + break; + prev = tmp; + tmp = tmp->next; + } + + if (prev == NULL) { + timeout->next = eloop.timeout; + eloop.timeout = timeout; + } else { + timeout->next = prev->next; + prev->next = timeout; + } + + return 0; +} + + +int eloop_cancel_timeout(eloop_timeout_handler handler, + void *eloop_data, void *user_data) +{ + struct eloop_timeout *timeout, *prev, *next; + int removed = 0; + + prev = NULL; + timeout = eloop.timeout; + while (timeout != NULL) { + next = timeout->next; + + if (timeout->handler == handler && + (timeout->eloop_data == eloop_data || + eloop_data == ELOOP_ALL_CTX) && + (timeout->user_data == user_data || + user_data == ELOOP_ALL_CTX)) { + if (prev == NULL) + eloop.timeout = next; + else + prev->next = next; + os_free(timeout); + removed++; + } else + prev = timeout; + + timeout = next; + } + + return removed; +} + + +/* TODO: replace with suitable signal handler */ +#if 0 +static void eloop_handle_signal(int sig) +{ + int i; + + eloop.signaled++; + for (i = 0; i < eloop.signal_count; i++) { + if (eloop.signals[i].sig == sig) { + eloop.signals[i].signaled++; + break; + } + } +} +#endif + + +static void eloop_process_pending_signals(void) +{ + int i; + + if (eloop.signaled == 0) + return; + eloop.signaled = 0; + + if (eloop.pending_terminate) { + eloop.pending_terminate = 0; + } + + for (i = 0; i < eloop.signal_count; i++) { + if (eloop.signals[i].signaled) { + eloop.signals[i].signaled = 0; + eloop.signals[i].handler(eloop.signals[i].sig, + eloop.user_data, + eloop.signals[i].user_data); + } + } + + if (eloop.term_signal.signaled) { + eloop.term_signal.signaled = 0; + eloop.term_signal.handler(eloop.term_signal.sig, + eloop.user_data, + eloop.term_signal.user_data); + } +} + + +int eloop_register_signal(int sig, eloop_signal_handler handler, + void *user_data) +{ + struct eloop_signal *tmp; + + tmp = os_realloc(eloop.signals, + (eloop.signal_count + 1) * + sizeof(struct eloop_signal)); + if (tmp == NULL) + return -1; + + tmp[eloop.signal_count].sig = sig; + tmp[eloop.signal_count].user_data = user_data; + tmp[eloop.signal_count].handler = handler; + tmp[eloop.signal_count].signaled = 0; + eloop.signal_count++; + eloop.signals = tmp; + + /* TODO: register signal handler */ + + return 0; +} + + +#ifndef _WIN32_WCE +static BOOL eloop_handle_console_ctrl(DWORD type) +{ + switch (type) { + case CTRL_C_EVENT: + case CTRL_BREAK_EVENT: + eloop.signaled++; + eloop.term_signal.signaled++; + SetEvent(eloop.term_event); + return TRUE; + default: + return FALSE; + } +} +#endif /* _WIN32_WCE */ + + +int eloop_register_signal_terminate(eloop_signal_handler handler, + void *user_data) +{ +#ifndef _WIN32_WCE + if (SetConsoleCtrlHandler((PHANDLER_ROUTINE) eloop_handle_console_ctrl, + TRUE) == 0) { + printf("SetConsoleCtrlHandler() failed: %d\n", + (int) GetLastError()); + return -1; + } +#endif /* _WIN32_WCE */ + + eloop.term_signal.handler = handler; + eloop.term_signal.user_data = user_data; + + return 0; +} + + +int eloop_register_signal_reconfig(eloop_signal_handler handler, + void *user_data) +{ + /* TODO */ + return 0; +} + + +void eloop_run(void) +{ + struct os_time tv, now; + DWORD count, ret, timeout, err; + size_t i; + + while (!eloop.terminate && + (eloop.timeout || eloop.reader_count > 0 || + eloop.event_count > 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; + for (i = 0; i < eloop.event_count; i++) + eloop.handles[count++] = eloop.events[i].event; + + for (i = 0; i < eloop.reader_count; i++) + eloop.handles[count++] = eloop.readers[i].event; + + if (eloop.term_event) + eloop.handles[count++] = eloop.term_event; + + if (eloop.timeout) + timeout = tv.sec * 1000 + tv.usec / 1000; + else + timeout = INFINITE; + + if (count > MAXIMUM_WAIT_OBJECTS) { + printf("WaitForMultipleObjects: Too many events: " + "%d > %d (ignoring extra events)\n", + (int) count, MAXIMUM_WAIT_OBJECTS); + count = MAXIMUM_WAIT_OBJECTS; + } +#ifdef _WIN32_WCE + ret = WaitForMultipleObjects(count, eloop.handles, FALSE, + timeout); +#else /* _WIN32_WCE */ + ret = WaitForMultipleObjectsEx(count, eloop.handles, FALSE, + timeout, TRUE); +#endif /* _WIN32_WCE */ + err = GetLastError(); + + eloop_process_pending_signals(); + + /* check if some registered timeouts have occurred */ + if (eloop.timeout) { + struct eloop_timeout *tmp; + + os_get_time(&now); + if (!os_time_before(&now, &eloop.timeout->time)) { + tmp = eloop.timeout; + eloop.timeout = eloop.timeout->next; + tmp->handler(tmp->eloop_data, + tmp->user_data); + os_free(tmp); + } + + } + + if (ret == WAIT_FAILED) { + printf("WaitForMultipleObjects(count=%d) failed: %d\n", + (int) count, (int) err); + os_sleep(1, 0); + continue; + } + +#ifndef _WIN32_WCE + if (ret == WAIT_IO_COMPLETION) + continue; +#endif /* _WIN32_WCE */ + + if (ret == WAIT_TIMEOUT) + continue; + + while (ret >= WAIT_OBJECT_0 && + ret < WAIT_OBJECT_0 + eloop.event_count) { + eloop.events[ret].handler( + eloop.events[ret].eloop_data, + eloop.events[ret].user_data); + ret = WaitForMultipleObjects(eloop.event_count, + eloop.handles, FALSE, 0); + } + + eloop.reader_table_changed = 0; + for (i = 0; i < eloop.reader_count; i++) { + WSANETWORKEVENTS events; + if (WSAEnumNetworkEvents(eloop.readers[i].sock, + eloop.readers[i].event, + &events) == 0 && + (events.lNetworkEvents & FD_READ)) { + eloop.readers[i].handler( + eloop.readers[i].sock, + eloop.readers[i].eloop_data, + eloop.readers[i].user_data); + if (eloop.reader_table_changed) + break; + } + } + } +} + + +void eloop_terminate(void) +{ + eloop.terminate = 1; + SetEvent(eloop.term_event); +} + + +void eloop_destroy(void) +{ + struct eloop_timeout *timeout, *prev; + + timeout = eloop.timeout; + while (timeout != NULL) { + prev = timeout; + timeout = timeout->next; + os_free(prev); + } + os_free(eloop.readers); + os_free(eloop.signals); + if (eloop.term_event) + CloseHandle(eloop.term_event); + os_free(eloop.handles); + eloop.handles = NULL; + os_free(eloop.events); + eloop.events = NULL; +} + + +int eloop_terminated(void) +{ + return eloop.terminate; +} + + +void eloop_wait_for_read_sock(int sock) +{ + WSAEVENT event; + + event = WSACreateEvent(); + if (event == WSA_INVALID_EVENT) { + printf("WSACreateEvent() failed: %d\n", WSAGetLastError()); + return; + } + + if (WSAEventSelect(sock, event, FD_READ)) { + printf("WSAEventSelect() failed: %d\n", WSAGetLastError()); + WSACloseEvent(event); + return ; + } + + WaitForSingleObject(event, INFINITE); + WSAEventSelect(sock, event, 0); + WSACloseEvent(event); +} + + +void * eloop_get_user_data(void) +{ + return eloop.user_data; +} diff --git a/contrib/hostapd/hlr_auc_gw.c b/contrib/hostapd/hlr_auc_gw.c new file mode 100644 index 0000000..a587702 --- /dev/null +++ b/contrib/hostapd/hlr_auc_gw.c @@ -0,0 +1,588 @@ +/* + * HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator + * 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 is an example implementation of the EAP-SIM/AKA database/authentication + * gateway interface to HLR/AuC. It is expected to be replaced with an + * implementation of SS7 gateway to GSM/UMTS authentication center (HLR/AuC) or + * a local implementation of SIM triplet and AKA authentication data generator. + * + * hostapd will send SIM/AKA authentication queries over a UNIX domain socket + * to and external program, e.g., this hlr_auc_gw. This interface uses simple + * text-based format: + * + * EAP-SIM / GSM triplet query/response: + * SIM-REQ-AUTH <IMSI> <max_chal> + * SIM-RESP-AUTH <IMSI> Kc1:SRES1:RAND1 Kc2:SRES2:RAND2 [Kc3:SRES3:RAND3] + * SIM-RESP-AUTH <IMSI> FAILURE + * + * EAP-AKA / UMTS query/response: + * AKA-REQ-AUTH <IMSI> + * AKA-RESP-AUTH <IMSI> <RAND> <AUTN> <IK> <CK> <RES> + * AKA-RESP-AUTH <IMSI> FAILURE + * + * EAP-AKA / UMTS AUTS (re-synchronization): + * AKA-AUTS <IMSI> <AUTS> <RAND> + * + * IMSI and max_chal are sent as an ASCII string, + * Kc/SRES/RAND/AUTN/IK/CK/RES/AUTS as hex strings. + * + * The example implementation here reads GSM authentication triplets from a + * text file in IMSI:Kc:SRES:RAND format, IMSI in ASCII, other fields as hex + * strings. This is used to simulate an HLR/AuC. As such, it is not very useful + * for real life authentication, but it is useful both as an example + * implementation and for EAP-SIM 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 <sys/un.h> + +#include "common.h" +#include "milenage.h" + +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; + +/* OPc and AMF parameters for Milenage (Example algorithms for AKA). */ +struct milenage_parameters { + struct milenage_parameters *next; + char imsi[20]; + u8 ki[16]; + u8 opc[16]; + u8 amf[2]; + u8 sqn[6]; +}; + +static struct milenage_parameters *milenage_db = NULL; + +#define EAP_SIM_MAX_CHAL 3 + +#define EAP_AKA_RAND_LEN 16 +#define EAP_AKA_AUTN_LEN 16 +#define EAP_AKA_AUTS_LEN 14 +#define EAP_AKA_RES_MAX_LEN 16 +#define EAP_AKA_IK_LEN 16 +#define EAP_AKA_CK_LEN 16 + + +static int open_socket(const char *path) +{ + struct sockaddr_un addr; + int s; + + s = socket(PF_UNIX, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket(PF_UNIX)"); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, path, sizeof(addr.sun_path)); + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind(PF_UNIX)"); + close(s); + return -1; + } + + return s; +} + + +static int read_milenage(const char *fname) +{ + FILE *f; + char buf[200], *pos, *pos2; + struct milenage_parameters *m = NULL; + int line, ret = 0; + + if (fname == NULL) + return -1; + + f = fopen(fname, "r"); + if (f == NULL) { + printf("Could not open Milenage data file '%s'\n", fname); + return -1; + } + + line = 0; + while (fgets(buf, sizeof(buf), f)) { + line++; + + /* Parse IMSI Ki OPc AMF SQN */ + buf[sizeof(buf) - 1] = '\0'; + if (buf[0] == '#') + continue; + pos = buf; + while (*pos != '\0' && *pos != '\n') + pos++; + if (*pos == '\n') + *pos = '\0'; + pos = buf; + if (*pos == '\0') + continue; + + m = os_zalloc(sizeof(*m)); + if (m == NULL) { + ret = -1; + break; + } + + /* IMSI */ + pos2 = strchr(pos, ' '); + if (pos2 == NULL) { + printf("%s:%d - Invalid IMSI (%s)\n", + fname, line, pos); + ret = -1; + break; + } + *pos2 = '\0'; + if (strlen(pos) >= sizeof(m->imsi)) { + printf("%s:%d - Too long IMSI (%s)\n", + fname, line, pos); + ret = -1; + break; + } + strncpy(m->imsi, pos, sizeof(m->imsi)); + pos = pos2 + 1; + + /* Ki */ + pos2 = strchr(pos, ' '); + if (pos2 == NULL) { + printf("%s:%d - Invalid Ki (%s)\n", fname, line, pos); + ret = -1; + break; + } + *pos2 = '\0'; + if (strlen(pos) != 32 || hexstr2bin(pos, m->ki, 16)) { + printf("%s:%d - Invalid Ki (%s)\n", fname, line, pos); + ret = -1; + break; + } + pos = pos2 + 1; + + /* OPc */ + pos2 = strchr(pos, ' '); + if (pos2 == NULL) { + printf("%s:%d - Invalid OPc (%s)\n", fname, line, pos); + ret = -1; + break; + } + *pos2 = '\0'; + if (strlen(pos) != 32 || hexstr2bin(pos, m->opc, 16)) { + printf("%s:%d - Invalid OPc (%s)\n", fname, line, pos); + ret = -1; + break; + } + pos = pos2 + 1; + + /* AMF */ + pos2 = strchr(pos, ' '); + if (pos2 == NULL) { + printf("%s:%d - Invalid AMF (%s)\n", fname, line, pos); + ret = -1; + break; + } + *pos2 = '\0'; + if (strlen(pos) != 4 || hexstr2bin(pos, m->amf, 2)) { + printf("%s:%d - Invalid AMF (%s)\n", fname, line, pos); + ret = -1; + break; + } + pos = pos2 + 1; + + /* SQN */ + pos2 = strchr(pos, ' '); + if (pos2) + *pos2 = '\0'; + if (strlen(pos) != 12 || hexstr2bin(pos, m->sqn, 6)) { + printf("%s:%d - Invalid SEQ (%s)\n", fname, line, pos); + ret = -1; + break; + } + pos = pos2 + 1; + + m->next = milenage_db; + milenage_db = m; + m = NULL; + } + free(m); + + fclose(f); + + return ret; +} + + +static struct milenage_parameters * get_milenage(const char *imsi) +{ + struct milenage_parameters *m = milenage_db; + + while (m) { + if (strcmp(m->imsi, imsi) == 0) + break; + m = m->next; + } + + return m; +} + + +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 reply[1000], *rpos, *rend; + struct milenage_parameters *m; + + reply[0] = '\0'; + + pos = strchr(imsi, ' '); + if (pos) { + *pos++ = '\0'; + max_chal = atoi(pos); + if (max_chal < 1 || max_chal < EAP_SIM_MAX_CHAL) + max_chal = EAP_SIM_MAX_CHAL; + } else + max_chal = EAP_SIM_MAX_CHAL; + + rend = &reply[sizeof(reply)]; + rpos = reply; + ret = snprintf(rpos, rend - rpos, "SIM-RESP-AUTH %s", imsi); + if (ret < 0 || ret >= rend - rpos) + return; + rpos += ret; + + m = get_milenage(imsi); + if (m) { + u8 _rand[16], sres[4], kc[8]; + for (count = 0; count < max_chal; count++) { + os_get_random(_rand, 16); + gsm_milenage(m->opc, m->ki, _rand, sres, kc); + *rpos++ = ' '; + rpos += wpa_snprintf_hex(rpos, rend - rpos, kc, 8); + *rpos++ = ':'; + rpos += wpa_snprintf_hex(rpos, rend - rpos, sres, 4); + *rpos++ = ':'; + rpos += wpa_snprintf_hex(rpos, rend - rpos, _rand, 16); + } + *rpos = '\0'; + 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) + continue; + + ret = snprintf(rpos, rend - rpos, " %s", pos); + if (ret < 0 || ret >= rend - rpos) { + fclose(f); + return; + } + rpos += ret; + count++; + } + + fclose(f); + + if (count == 0) { + printf("No GSM triplets found for %s\n", imsi); + ret = snprintf(rpos, rend - rpos, " FAILURE"); + if (ret < 0 || ret >= rend - rpos) + return; + rpos += ret; + } + +send: + printf("Send: %s\n", reply); + if (sendto(s, reply, rpos - reply, 0, + (struct sockaddr *) from, fromlen) < 0) + perror("send"); +} + + +static void aka_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen, + char *imsi) +{ + /* AKA-RESP-AUTH <IMSI> <RAND> <AUTN> <IK> <CK> <RES> */ + char reply[1000], *pos, *end; + u8 _rand[EAP_AKA_RAND_LEN]; + u8 autn[EAP_AKA_AUTN_LEN]; + u8 ik[EAP_AKA_IK_LEN]; + u8 ck[EAP_AKA_CK_LEN]; + u8 res[EAP_AKA_RES_MAX_LEN]; + size_t res_len; + int ret; + struct milenage_parameters *m; + + m = get_milenage(imsi); + if (m) { + os_get_random(_rand, EAP_AKA_RAND_LEN); + 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", + m->sqn[0], m->sqn[1], m->sqn[2], + m->sqn[3], m->sqn[4], m->sqn[5]); + milenage_generate(m->opc, m->amf, m->ki, m->sqn, _rand, + autn, ik, ck, res, &res_len); + } else { + printf("Unknown IMSI: %s\n", imsi); +#ifdef AKA_USE_FIXED_TEST_VALUES + printf("Using fixed test values for AKA\n"); + memset(_rand, '0', EAP_AKA_RAND_LEN); + memset(autn, '1', EAP_AKA_AUTN_LEN); + memset(ik, '3', EAP_AKA_IK_LEN); + memset(ck, '4', EAP_AKA_CK_LEN); + memset(res, '2', EAP_AKA_RES_MAX_LEN); + res_len = EAP_AKA_RES_MAX_LEN; +#else /* AKA_USE_FIXED_TEST_VALUES */ + return; +#endif /* AKA_USE_FIXED_TEST_VALUES */ + } + + pos = reply; + end = &reply[sizeof(reply)]; + ret = snprintf(pos, end - pos, "AKA-RESP-AUTH %s ", imsi); + if (ret < 0 || ret >= end - pos) + return; + pos += ret; + pos += wpa_snprintf_hex(pos, end - pos, _rand, EAP_AKA_RAND_LEN); + *pos++ = ' '; + pos += wpa_snprintf_hex(pos, end - pos, autn, EAP_AKA_AUTN_LEN); + *pos++ = ' '; + pos += wpa_snprintf_hex(pos, end - pos, ik, EAP_AKA_IK_LEN); + *pos++ = ' '; + pos += wpa_snprintf_hex(pos, end - pos, ck, EAP_AKA_CK_LEN); + *pos++ = ' '; + pos += wpa_snprintf_hex(pos, end - pos, res, res_len); + + printf("Send: %s\n", reply); + + if (sendto(s, reply, pos - reply, 0, (struct sockaddr *) from, + fromlen) < 0) + perror("send"); +} + + +static void aka_auts(int s, struct sockaddr_un *from, socklen_t fromlen, + char *imsi) +{ + char *auts, *rand; + u8 _auts[EAP_AKA_AUTS_LEN], _rand[EAP_AKA_RAND_LEN], sqn[6]; + struct milenage_parameters *m; + + /* AKA-AUTS <IMSI> <AUTS> <RAND> */ + + auts = strchr(imsi, ' '); + if (auts == NULL) + return; + *auts++ = '\0'; + + rand = strchr(auts, ' '); + if (rand == NULL) + return; + *rand++ = '\0'; + + printf("AKA-AUTS: IMSI=%s AUTS=%s RAND=%s\n", imsi, auts, rand); + if (hexstr2bin(auts, _auts, EAP_AKA_AUTS_LEN) || + hexstr2bin(rand, _rand, EAP_AKA_RAND_LEN)) { + printf("Could not parse AUTS/RAND\n"); + return; + } + + m = get_milenage(imsi); + if (m == NULL) { + printf("Unknown IMSI: %s\n", imsi); + return; + } + + if (milenage_auts(m->opc, m->ki, _rand, _auts, sqn)) { + printf("AKA-AUTS: Incorrect MAC-S\n"); + } else { + memcpy(m->sqn, sqn, 6); + printf("AKA-AUTS: Re-synchronized: " + "SQN=%02x%02x%02x%02x%02x%02x\n", + sqn[0], sqn[1], sqn[2], sqn[3], sqn[4], sqn[5]); + } +} + + +static int process(int s) +{ + char buf[1000]; + struct sockaddr_un from; + socklen_t fromlen; + ssize_t res; + + fromlen = sizeof(from); + res = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr *) &from, + &fromlen); + if (res < 0) { + perror("recvfrom"); + return -1; + } + + if (res == 0) + return 0; + + if ((size_t) res >= sizeof(buf)) + res = sizeof(buf) - 1; + buf[res] = '\0'; + + printf("Received: %s\n", buf); + + if (strncmp(buf, "SIM-REQ-AUTH ", 13) == 0) + sim_req_auth(s, &from, fromlen, buf + 13); + else if (strncmp(buf, "AKA-REQ-AUTH ", 13) == 0) + aka_req_auth(s, &from, fromlen, buf + 13); + else if (strncmp(buf, "AKA-AUTS ", 9) == 0) + aka_auts(s, &from, fromlen, buf + 9); + else + printf("Unknown request: %s\n", buf); + + return 0; +} + + +static void cleanup(void) +{ + struct milenage_parameters *m, *prev; + + m = milenage_db; + while (m) { + prev = m; + m = m->next; + free(prev); + } + + close(serv_sock); + unlink(socket_path); +} + + +static void handle_term(int sig) +{ + printf("Signal %d - terminate\n", sig); + exit(0); +} + + +static void usage(void) +{ + printf("HLR/AuC testing gateway for hostapd EAP-SIM/AKA " + "database/authenticator\n" + "Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi>\n" + "\n" + "usage:\n" + "hlr_auc_gw [-h] [-s<socket path>] [-g<triplet file>] " + "[-m<milenage file>]\n" + "\n" + "options:\n" + " -h = show this usage help\n" + " -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); +} + + +int main(int argc, char *argv[]) +{ + int c; + char *milenage_file = NULL; + + socket_path = default_socket_path; + gsm_triplet_file = default_gsm_triplet_file; + + for (;;) { + c = getopt(argc, argv, "g:hm:s:"); + if (c < 0) + break; + switch (c) { + case 'g': + gsm_triplet_file = optarg; + break; + case 'h': + usage(); + return 0; + case 'm': + milenage_file = optarg; + break; + case 's': + socket_path = optarg; + break; + default: + usage(); + return -1; + } + } + + if (milenage_file && read_milenage(milenage_file) < 0) + return -1; + + serv_sock = open_socket(socket_path); + if (serv_sock < 0) + return -1; + + printf("Listening for requests on %s\n", socket_path); + + atexit(cleanup); + signal(SIGTERM, handle_term); + signal(SIGINT, handle_term); + + for (;;) + process(serv_sock); + + return 0; +} diff --git a/contrib/hostapd/hlr_auc_gw.milenage_db b/contrib/hostapd/hlr_auc_gw.milenage_db new file mode 100644 index 0000000..fa15d53 --- /dev/null +++ b/contrib/hostapd/hlr_auc_gw.milenage_db @@ -0,0 +1,9 @@ +# Parameters for Milenage (Example algorithms for AKA). +# The example Ki, OPc, and AMF values here are from 3GPP TS 35.208 v6.0.0 +# 4.3.20 Test Set 20. SQN is the last used SQN value. +# These values can be used for both UMTS (EAP-AKA) and GSM (EAP-SIM) +# authentication. In case of GSM/EAP-SIM, AMF and SQN values are not used, but +# dummy values will need to be included in this file. + +# IMSI Ki OPc AMF SQN +232010000000000 90dca4eda45b53cf0f12d7c9c3bc6a89 cb9cccc4b9258e6dca4760379fb82581 61df 000000000000 diff --git a/contrib/hostapd/hostap_common.h b/contrib/hostapd/hostap_common.h index 78af287..1e38df3 100644 --- a/contrib/hostapd/hostap_common.h +++ b/contrib/hostapd/hostap_common.h @@ -1,361 +1,20 @@ +/* + * hostapd / Kernel driver communication with Linux Host AP driver + * 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 HOSTAP_COMMON_H #define HOSTAP_COMMON_H -#define BIT(x) (1 << (x)) - -#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_PAE -#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ -#endif /* ETH_P_PAE */ - -#define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */ - - - -/* 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 - -/* 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 - -/* 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) - -/* 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 -/* 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.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_CHALLENGE 16 -#define WLAN_EID_RSN 48 -#define WLAN_EID_GENERIC 221 - - -/* HFA384X Configuration RIDs */ -#define HFA384X_RID_CNFPORTTYPE 0xFC00 -#define HFA384X_RID_CNFOWNMACADDR 0xFC01 -#define HFA384X_RID_CNFDESIREDSSID 0xFC02 -#define HFA384X_RID_CNFOWNCHANNEL 0xFC03 -#define HFA384X_RID_CNFOWNSSID 0xFC04 -#define HFA384X_RID_CNFOWNATIMWINDOW 0xFC05 -#define HFA384X_RID_CNFSYSTEMSCALE 0xFC06 -#define HFA384X_RID_CNFMAXDATALEN 0xFC07 -#define HFA384X_RID_CNFWDSADDRESS 0xFC08 -#define HFA384X_RID_CNFPMENABLED 0xFC09 -#define HFA384X_RID_CNFPMEPS 0xFC0A -#define HFA384X_RID_CNFMULTICASTRECEIVE 0xFC0B -#define HFA384X_RID_CNFMAXSLEEPDURATION 0xFC0C -#define HFA384X_RID_CNFPMHOLDOVERDURATION 0xFC0D -#define HFA384X_RID_CNFOWNNAME 0xFC0E -#define HFA384X_RID_CNFOWNDTIMPERIOD 0xFC10 -#define HFA384X_RID_CNFWDSADDRESS1 0xFC11 /* AP f/w only */ -#define HFA384X_RID_CNFWDSADDRESS2 0xFC12 /* AP f/w only */ -#define HFA384X_RID_CNFWDSADDRESS3 0xFC13 /* AP f/w only */ -#define HFA384X_RID_CNFWDSADDRESS4 0xFC14 /* AP f/w only */ -#define HFA384X_RID_CNFWDSADDRESS5 0xFC15 /* AP f/w only */ -#define HFA384X_RID_CNFWDSADDRESS6 0xFC16 /* AP f/w only */ -#define HFA384X_RID_CNFMULTICASTPMBUFFERING 0xFC17 /* AP f/w only */ -#define HFA384X_RID_UNKNOWN1 0xFC20 -#define HFA384X_RID_UNKNOWN2 0xFC21 -#define HFA384X_RID_CNFWEPDEFAULTKEYID 0xFC23 -#define HFA384X_RID_CNFDEFAULTKEY0 0xFC24 -#define HFA384X_RID_CNFDEFAULTKEY1 0xFC25 -#define HFA384X_RID_CNFDEFAULTKEY2 0xFC26 -#define HFA384X_RID_CNFDEFAULTKEY3 0xFC27 -#define HFA384X_RID_CNFWEPFLAGS 0xFC28 -#define HFA384X_RID_CNFWEPKEYMAPPINGTABLE 0xFC29 -#define HFA384X_RID_CNFAUTHENTICATION 0xFC2A -#define HFA384X_RID_CNFMAXASSOCSTA 0xFC2B /* AP f/w only */ -#define HFA384X_RID_CNFTXCONTROL 0xFC2C -#define HFA384X_RID_CNFROAMINGMODE 0xFC2D -#define HFA384X_RID_CNFHOSTAUTHENTICATION 0xFC2E /* AP f/w only */ -#define HFA384X_RID_CNFRCVCRCERROR 0xFC30 -#define HFA384X_RID_CNFMMLIFE 0xFC31 -#define HFA384X_RID_CNFALTRETRYCOUNT 0xFC32 -#define HFA384X_RID_CNFBEACONINT 0xFC33 -#define HFA384X_RID_CNFAPPCFINFO 0xFC34 /* AP f/w only */ -#define HFA384X_RID_CNFSTAPCFINFO 0xFC35 -#define HFA384X_RID_CNFPRIORITYQUSAGE 0xFC37 -#define HFA384X_RID_CNFTIMCTRL 0xFC40 -#define HFA384X_RID_UNKNOWN3 0xFC41 /* added in STA f/w 0.7.x */ -#define HFA384X_RID_CNFTHIRTY2TALLY 0xFC42 /* added in STA f/w 0.8.0 */ -#define HFA384X_RID_CNFENHSECURITY 0xFC43 /* AP f/w or STA f/w >= 1.6.3 */ -#define HFA384X_RID_CNFDBMADJUST 0xFC46 /* added in STA f/w 1.3.1 */ -#define HFA384X_RID_GENERICELEMENT 0xFC48 /* added in STA f/w 1.7.0; - * write only */ -#define HFA384X_RID_PROPAGATIONDELAY 0xFC49 /* added in STA f/w 1.7.6 */ -#define HFA384X_RID_GROUPADDRESSES 0xFC80 -#define HFA384X_RID_CREATEIBSS 0xFC81 -#define HFA384X_RID_FRAGMENTATIONTHRESHOLD 0xFC82 -#define HFA384X_RID_RTSTHRESHOLD 0xFC83 -#define HFA384X_RID_TXRATECONTROL 0xFC84 -#define HFA384X_RID_PROMISCUOUSMODE 0xFC85 -#define HFA384X_RID_FRAGMENTATIONTHRESHOLD0 0xFC90 /* AP f/w only */ -#define HFA384X_RID_FRAGMENTATIONTHRESHOLD1 0xFC91 /* AP f/w only */ -#define HFA384X_RID_FRAGMENTATIONTHRESHOLD2 0xFC92 /* AP f/w only */ -#define HFA384X_RID_FRAGMENTATIONTHRESHOLD3 0xFC93 /* AP f/w only */ -#define HFA384X_RID_FRAGMENTATIONTHRESHOLD4 0xFC94 /* AP f/w only */ -#define HFA384X_RID_FRAGMENTATIONTHRESHOLD5 0xFC95 /* AP f/w only */ -#define HFA384X_RID_FRAGMENTATIONTHRESHOLD6 0xFC96 /* AP f/w only */ -#define HFA384X_RID_RTSTHRESHOLD0 0xFC97 /* AP f/w only */ -#define HFA384X_RID_RTSTHRESHOLD1 0xFC98 /* AP f/w only */ -#define HFA384X_RID_RTSTHRESHOLD2 0xFC99 /* AP f/w only */ -#define HFA384X_RID_RTSTHRESHOLD3 0xFC9A /* AP f/w only */ -#define HFA384X_RID_RTSTHRESHOLD4 0xFC9B /* AP f/w only */ -#define HFA384X_RID_RTSTHRESHOLD5 0xFC9C /* AP f/w only */ -#define HFA384X_RID_RTSTHRESHOLD6 0xFC9D /* AP f/w only */ -#define HFA384X_RID_TXRATECONTROL0 0xFC9E /* AP f/w only */ -#define HFA384X_RID_TXRATECONTROL1 0xFC9F /* AP f/w only */ -#define HFA384X_RID_TXRATECONTROL2 0xFCA0 /* AP f/w only */ -#define HFA384X_RID_TXRATECONTROL3 0xFCA1 /* AP f/w only */ -#define HFA384X_RID_TXRATECONTROL4 0xFCA2 /* AP f/w only */ -#define HFA384X_RID_TXRATECONTROL5 0xFCA3 /* AP f/w only */ -#define HFA384X_RID_TXRATECONTROL6 0xFCA4 /* AP f/w only */ -#define HFA384X_RID_CNFSHORTPREAMBLE 0xFCB0 -#define HFA384X_RID_CNFEXCLUDELONGPREAMBLE 0xFCB1 -#define HFA384X_RID_CNFAUTHENTICATIONRSPTO 0xFCB2 -#define HFA384X_RID_CNFBASICRATES 0xFCB3 -#define HFA384X_RID_CNFSUPPORTEDRATES 0xFCB4 -#define HFA384X_RID_CNFFALLBACKCTRL 0xFCB5 /* added in STA f/w 1.3.1 */ -#define HFA384X_RID_WEPKEYDISABLE 0xFCB6 /* added in STA f/w 1.3.1 */ -#define HFA384X_RID_WEPKEYMAPINDEX 0xFCB7 /* ? */ -#define HFA384X_RID_BROADCASTKEYID 0xFCB8 /* ? */ -#define HFA384X_RID_ENTSECFLAGEYID 0xFCB9 /* ? */ -#define HFA384X_RID_CNFPASSIVESCANCTRL 0xFCBA /* added in STA f/w 1.5.0 */ -#define HFA384X_RID_SSNHANDLINGMODE 0xFCBB /* added in STA f/w 1.7.0 */ -#define HFA384X_RID_MDCCONTROL 0xFCBC /* added in STA f/w 1.7.0 */ -#define HFA384X_RID_MDCCOUNTRY 0xFCBD /* added in STA f/w 1.7.0 */ -#define HFA384X_RID_TXPOWERMAX 0xFCBE /* added in STA f/w 1.7.0 */ -#define HFA384X_RID_CNFLFOENABLED 0xFCBF /* added in STA f/w 1.6.3 */ -#define HFA384X_RID_CAPINFO 0xFCC0 /* added in STA f/w 1.7.0 */ -#define HFA384X_RID_LISTENINTERVAL 0xFCC1 /* added in STA f/w 1.7.0 */ -#define HFA384X_RID_SW_ANT_DIV 0xFCC2 /* added in STA f/w 1.7.0; Prism3 */ -#define HFA384X_RID_LED_CTRL 0xFCC4 /* added in STA f/w 1.7.6 */ -#define HFA384X_RID_HFODELAY 0xFCC5 /* added in STA f/w 1.7.6 */ -#define HFA384X_RID_DISALLOWEDBSSID 0xFCC6 /* added in STA f/w 1.8.0 */ -#define HFA384X_RID_TICKTIME 0xFCE0 -#define HFA384X_RID_SCANREQUEST 0xFCE1 -#define HFA384X_RID_JOINREQUEST 0xFCE2 -#define HFA384X_RID_AUTHENTICATESTATION 0xFCE3 /* AP f/w only */ -#define HFA384X_RID_CHANNELINFOREQUEST 0xFCE4 /* AP f/w only */ -#define HFA384X_RID_HOSTSCAN 0xFCE5 /* added in STA f/w 1.3.1 */ - -/* HFA384X Information RIDs */ -#define HFA384X_RID_MAXLOADTIME 0xFD00 -#define HFA384X_RID_DOWNLOADBUFFER 0xFD01 -#define HFA384X_RID_PRIID 0xFD02 -#define HFA384X_RID_PRISUPRANGE 0xFD03 -#define HFA384X_RID_CFIACTRANGES 0xFD04 -#define HFA384X_RID_NICSERNUM 0xFD0A -#define HFA384X_RID_NICID 0xFD0B -#define HFA384X_RID_MFISUPRANGE 0xFD0C -#define HFA384X_RID_CFISUPRANGE 0xFD0D -#define HFA384X_RID_CHANNELLIST 0xFD10 -#define HFA384X_RID_REGULATORYDOMAINS 0xFD11 -#define HFA384X_RID_TEMPTYPE 0xFD12 -#define HFA384X_RID_CIS 0xFD13 -#define HFA384X_RID_STAID 0xFD20 -#define HFA384X_RID_STASUPRANGE 0xFD21 -#define HFA384X_RID_MFIACTRANGES 0xFD22 -#define HFA384X_RID_CFIACTRANGES2 0xFD23 -#define HFA384X_RID_PRODUCTNAME 0xFD24 /* added in STA f/w 1.3.1; - * only Prism2.5(?) */ -#define HFA384X_RID_PORTSTATUS 0xFD40 -#define HFA384X_RID_CURRENTSSID 0xFD41 -#define HFA384X_RID_CURRENTBSSID 0xFD42 -#define HFA384X_RID_COMMSQUALITY 0xFD43 -#define HFA384X_RID_CURRENTTXRATE 0xFD44 -#define HFA384X_RID_CURRENTBEACONINTERVAL 0xFD45 -#define HFA384X_RID_CURRENTSCALETHRESHOLDS 0xFD46 -#define HFA384X_RID_PROTOCOLRSPTIME 0xFD47 -#define HFA384X_RID_SHORTRETRYLIMIT 0xFD48 -#define HFA384X_RID_LONGRETRYLIMIT 0xFD49 -#define HFA384X_RID_MAXTRANSMITLIFETIME 0xFD4A -#define HFA384X_RID_MAXRECEIVELIFETIME 0xFD4B -#define HFA384X_RID_CFPOLLABLE 0xFD4C -#define HFA384X_RID_AUTHENTICATIONALGORITHMS 0xFD4D -#define HFA384X_RID_PRIVACYOPTIONIMPLEMENTED 0xFD4F -#define HFA384X_RID_DBMCOMMSQUALITY 0xFD51 /* added in STA f/w 1.3.1 */ -#define HFA384X_RID_CURRENTTXRATE1 0xFD80 /* AP f/w only */ -#define HFA384X_RID_CURRENTTXRATE2 0xFD81 /* AP f/w only */ -#define HFA384X_RID_CURRENTTXRATE3 0xFD82 /* AP f/w only */ -#define HFA384X_RID_CURRENTTXRATE4 0xFD83 /* AP f/w only */ -#define HFA384X_RID_CURRENTTXRATE5 0xFD84 /* AP f/w only */ -#define HFA384X_RID_CURRENTTXRATE6 0xFD85 /* AP f/w only */ -#define HFA384X_RID_OWNMACADDR 0xFD86 /* AP f/w only */ -#define HFA384X_RID_SCANRESULTSTABLE 0xFD88 /* added in STA f/w 0.8.3 */ -#define HFA384X_RID_HOSTSCANRESULTS 0xFD89 /* added in STA f/w 1.3.1 */ -#define HFA384X_RID_AUTHENTICATIONUSED 0xFD8A /* added in STA f/w 1.3.4 */ -#define HFA384X_RID_CNFFAASWITCHCTRL 0xFD8B /* added in STA f/w 1.6.3 */ -#define HFA384X_RID_ASSOCIATIONFAILURE 0xFD8D /* added in STA f/w 1.8.0 */ -#define HFA384X_RID_PHYTYPE 0xFDC0 -#define HFA384X_RID_CURRENTCHANNEL 0xFDC1 -#define HFA384X_RID_CURRENTPOWERSTATE 0xFDC2 -#define HFA384X_RID_CCAMODE 0xFDC3 -#define HFA384X_RID_SUPPORTEDDATARATES 0xFDC6 -#define HFA384X_RID_LFO_VOLT_REG_TEST_RES 0xFDC7 /* added in STA f/w 1.7.1 */ -#define HFA384X_RID_BUILDSEQ 0xFFFE -#define HFA384X_RID_FWID 0xFFFF - - -struct hfa384x_comp_ident -{ - u16 id; - u16 variant; - u16 major; - u16 minor; -} __attribute__ ((packed)); - -#define HFA384X_COMP_ID_PRI 0x15 -#define HFA384X_COMP_ID_STA 0x1f -#define HFA384X_COMP_ID_FW_AP 0x14b - -struct hfa384x_sup_range -{ - u16 role; - u16 id; - u16 variant; - u16 bottom; - u16 top; -} __attribute__ ((packed)); - - -struct hfa384x_build_id -{ - u16 pri_seq; - u16 sec_seq; -} __attribute__ ((packed)); - -/* FD01 - Download Buffer */ -struct hfa384x_rid_download_buffer -{ - u16 page; - u16 offset; - u16 length; -} __attribute__ ((packed)); - -/* BSS connection quality (RID FD43 range, RID FD51 dBm-normalized) */ -struct hfa384x_comms_quality { - u16 comm_qual; /* 0 .. 92 */ - u16 signal_level; /* 27 .. 154 */ - u16 noise_level; /* 27 .. 154 */ -} __attribute__ ((packed)); - - /* netdevice private ioctls (used, e.g., with iwpriv from user space) */ /* New wireless extensions API - SET/GET convention (even ioctl numbers are @@ -554,5 +213,4 @@ struct prism2_hostapd_param { #define HOSTAP_CRYPT_ERR_TX_KEY_SET_FAILED 6 #define HOSTAP_CRYPT_ERR_CARD_CONF_FAILED 7 - #endif /* HOSTAP_COMMON_H */ diff --git a/contrib/hostapd/hostapd.8 b/contrib/hostapd/hostapd.8 index 8b02497..9258512 100644 --- a/contrib/hostapd/hostapd.8 +++ b/contrib/hostapd/hostapd.8 @@ -3,7 +3,7 @@ hostapd \- IEEE 802.11 AP, IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator .SH SYNOPSIS .B hostapd -[-hdBKt] <configuration file(s)> +[-hdBKtv] [-P <PID file>] <configuration file(s)> .SH DESCRIPTION This manual page documents briefly the .B hostapd @@ -39,6 +39,9 @@ Show even more debug messages. .B \-B Run daemon in the background. .TP +.B \-P <PID file> +Path to PID file. +.TP .B \-K Include key data in debug messages. .TP @@ -50,7 +53,7 @@ Show hostapd version. .SH SEE ALSO .BR hostapd_cli (1). .SH AUTHOR -hostapd was written by Jouni Malinen <jkmaline@cc.hut.fi>. +hostapd was written by Jouni Malinen <j@w1.fi>. .PP This manual page was written by Faidon Liambotis <faidon@cube.gr>, for the Debian project (but may be used by others). diff --git a/contrib/hostapd/hostapd.c b/contrib/hostapd/hostapd.c index 086af62..d4cd488e 100644 --- a/contrib/hostapd/hostapd.c +++ b/contrib/hostapd/hostapd.c @@ -1,7 +1,6 @@ /* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver - * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * hostapd / Initialization and configuration + * 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 @@ -13,43 +12,41 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <netinet/in.h> -#include <string.h> -#include <signal.h> -#include <time.h> +#include "includes.h" +#ifndef CONFIG_NATIVE_WINDOWS #include <syslog.h> -#include <stdarg.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <arpa/inet.h> +#endif /* CONFIG_NATIVE_WINDOWS */ #include "eloop.h" #include "hostapd.h" #include "ieee802_1x.h" #include "ieee802_11.h" +#include "beacon.h" +#include "hw_features.h" #include "accounting.h" #include "eapol_sm.h" #include "iapp.h" #include "ap.h" #include "ieee802_11_auth.h" +#include "ap_list.h" #include "sta_info.h" #include "driver.h" #include "radius_client.h" #include "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 "version.h" -#include "hostap_common.h" struct hapd_interfaces { - int count; - hostapd **hapd; + size_t count; + struct hostapd_iface **iface; }; unsigned char rfc1042_header[6] = { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; @@ -103,6 +100,9 @@ void hostapd_logger(struct hostapd_data *hapd, const u8 *addr, case HOSTAPD_MODULE_IAPP: module_str = "IAPP"; break; + case HOSTAPD_MODULE_MLME: + module_str = "MLME"; + break; default: module_str = NULL; break; @@ -132,6 +132,7 @@ void hostapd_logger(struct hostapd_data *hapd, const u8 *addr, printf("\n"); } +#ifndef CONFIG_NATIVE_WINDOWS if ((conf_syslog & module) && level >= conf_syslog_level) { int priority; switch (level) { @@ -156,6 +157,7 @@ void hostapd_logger(struct hostapd_data *hapd, const u8 *addr, vsyslog(priority, format, ap); va_end(ap); } +#endif /* CONFIG_NATIVE_WINDOWS */ free(format); } @@ -183,7 +185,32 @@ const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf, } -static void hostapd_deauth_all_stas(hostapd *hapd) +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; +} + + +static void hostapd_deauth_all_stas(struct hostapd_data *hapd) { #if 0 u8 addr[ETH_ALEN]; @@ -199,8 +226,51 @@ static void hostapd_deauth_all_stas(hostapd *hapd) } -/* This function will be called whenever a station associates with the AP */ -void hostapd_new_assoc_sta(hostapd *hapd, struct sta_info *sta, int reassoc) +/** + * hostapd_prune_associations - Remove extraneous associations + * @hapd: Pointer to BSS data for the most recent association + * @sta: Pointer to the associated STA data + * + * This function looks through all radios and BSS's for previous + * (stale) associations of STA. If any are found they are removed. + */ +static void hostapd_prune_associations(struct hostapd_data *hapd, + struct sta_info *sta) +{ + struct sta_info *osta; + 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 (ohapd == hapd) + continue; + osta = ap_get_sta(ohapd, sta->addr); + if (!osta) + continue; + + ap_sta_disassociate(ohapd, osta, + WLAN_REASON_UNSPECIFIED); + } + } +} + + +/** + * hostapd_new_assoc_sta - Notify that a new station associated with the AP + * @hapd: Pointer to BSS data + * @sta: Pointer to the associated STA data + * @reassoc: 1 to indicate this was a re-association; 0 = first association + * + * This function will be called whenever a station associates with the AP. It + * can be called for ieee802_11.c for drivers that export MLME to hostapd and + * from driver_*.c for drivers that take care of management frames (IEEE 802.11 + * authentication and association) internally. + */ +void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, + int reassoc) { if (hapd->tkip_countermeasures) { hostapd_sta_deauth(hapd, sta->addr, @@ -208,6 +278,8 @@ void hostapd_new_assoc_sta(hostapd *hapd, struct sta_info *sta, int reassoc) return; } + hostapd_prune_associations(hapd, sta); + /* IEEE 802.11F (IAPP) */ if (hapd->conf->ieee802_11f) iapp_new_station(hapd->iapp, sta); @@ -218,15 +290,36 @@ void hostapd_new_assoc_sta(hostapd *hapd, struct sta_info *sta, int reassoc) if (!hapd->conf->ieee802_1x && !hapd->conf->wpa) accounting_sta_start(hapd, sta); + hostapd_wme_sta_config(hapd, sta); + /* Start IEEE 802.1X authentication process for new stations */ ieee802_1x_new_station(hapd, sta); if (reassoc) - wpa_sm_event(hapd, sta, WPA_REAUTH); + wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH); else - wpa_new_station(hapd, sta); + wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm); } +#ifdef EAP_SERVER +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) + return 1; + return 0; +} + + +static void hostapd_sim_db_cb(void *ctx, void *session_ctx) +{ + struct hostapd_data *hapd = ctx; + if (ap_for_each_sta(hapd, hostapd_sim_db_cb_sta, session_ctx) == 0) + radius_server_eap_pending_cb(hapd->radius_srv, session_ctx); +} +#endif /* EAP_SERVER */ + + static void handle_term(int sig, void *eloop_ctx, void *signal_ctx) { printf("Signal %d received - terminating\n", sig); @@ -234,17 +327,39 @@ static void handle_term(int sig, void *eloop_ctx, void *signal_ctx) } +static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, + struct wpa_auth_config *wconf) +{ + wconf->wpa = conf->wpa; + wconf->wpa_key_mgmt = conf->wpa_key_mgmt; + wconf->wpa_pairwise = conf->wpa_pairwise; + wconf->wpa_group = conf->wpa_group; + wconf->wpa_group_rekey = conf->wpa_group_rekey; + wconf->wpa_strict_rekey = conf->wpa_strict_rekey; + wconf->wpa_gmk_rekey = conf->wpa_gmk_rekey; + wconf->rsn_preauth = conf->rsn_preauth; + wconf->eapol_version = conf->eapol_version; + wconf->peerkey = conf->peerkey; + wconf->wme_enabled = conf->wme_enabled; +#ifdef CONFIG_IEEE80211W + wconf->ieee80211w = conf->ieee80211w; +#endif /* CONFIG_IEEE80211W */ +} + + +#ifndef CONFIG_NATIVE_WINDOWS static void handle_reload(int sig, void *eloop_ctx, void *signal_ctx) { struct hapd_interfaces *hapds = (struct hapd_interfaces *) eloop_ctx; struct hostapd_config *newconf; - int i; + size_t i; + struct wpa_auth_config wpa_auth_conf; printf("Signal %d received - reloading configuration\n", sig); for (i = 0; i < hapds->count; i++) { - hostapd *hapd = hapds->hapd[i]; - newconf = hostapd_config_read(hapd->config_fname); + 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"); @@ -253,15 +368,26 @@ static void handle_reload(int sig, void *eloop_ctx, void *signal_ctx) /* 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); - hostapd_config_free(hapd->conf); - hapd->conf = newconf; + radius_client_flush(hapd->radius, 0); + hostapd_config_free(hapd->iconf); + + hostapd_wpa_auth_conf(&newconf->bss[0], &wpa_auth_conf); + wpa_reconfig(hapd->wpa_auth, &wpa_auth_conf); + + hapd->iconf = newconf; + hapd->conf = &newconf->bss[0]; + hapds->iface[i]->conf = newconf; + + if (hostapd_setup_wpa_psk(hapd->conf)) { + wpa_printf(MSG_ERROR, "Failed to re-configure WPA PSK " + "after reloading configuration"); + } } } #ifdef HOSTAPD_DUMP_STATE -static void hostapd_dump_state(hostapd *hapd) +static void hostapd_dump_state(struct hostapd_data *hapd) { FILE *f; time_t now; @@ -284,12 +410,18 @@ static void hostapd_dump_state(hostapd *hapd) time(&now); fprintf(f, "hostapd state dump - %s", ctime(&now)); + fprintf(f, "num_sta=%d num_sta_non_erp=%d " + "num_sta_no_short_slot_time=%d\n" + "num_sta_no_short_preamble=%d\n", + hapd->num_sta, hapd->iface->num_sta_non_erp, + hapd->iface->num_sta_no_short_slot_time, + hapd->iface->num_sta_no_short_preamble); for (sta = hapd->sta_list; sta != NULL; sta = sta->next) { fprintf(f, "\nSTA=" MACSTR "\n", MAC2STR(sta->addr)); fprintf(f, - " AID=%d flags=0x%x %s%s%s%s%s%s\n" + " AID=%d flags=0x%x %s%s%s%s%s%s%s%s%s%s\n" " capability=0x%x listen_interval=%d\n", sta->aid, sta->flags, @@ -300,18 +432,19 @@ static void hostapd_dump_state(hostapd *hapd) (sta->flags & WLAN_STA_PERM ? "[PERM]" : ""), (sta->flags & WLAN_STA_AUTHORIZED ? "[AUTHORIZED]" : ""), + (sta->flags & WLAN_STA_PENDING_POLL ? "[PENDING_POLL" : + ""), + (sta->flags & WLAN_STA_SHORT_PREAMBLE ? + "[SHORT_PREAMBLE]" : ""), + (sta->flags & WLAN_STA_PREAUTH ? "[PREAUTH]" : ""), + (sta->flags & WLAN_STA_NONERP ? "[NonERP]" : ""), sta->capability, sta->listen_interval); fprintf(f, " supported_rates="); - for (i = 0; i < sizeof(sta->supported_rates); i++) - if (sta->supported_rates[i] != 0) - fprintf(f, "%02x ", sta->supported_rates[i]); - fprintf(f, "%s%s%s%s\n", - (sta->tx_supp_rates & WLAN_RATE_1M ? "[1M]" : ""), - (sta->tx_supp_rates & WLAN_RATE_2M ? "[2M]" : ""), - (sta->tx_supp_rates & WLAN_RATE_5M5 ? "[5.5M]" : ""), - (sta->tx_supp_rates & WLAN_RATE_11M ? "[11M]" : "")); + for (i = 0; i < sta->supported_rates_len; i++) + fprintf(f, "%02x ", sta->supported_rates[i]); + fprintf(f, "\n"); fprintf(f, " timeout_next=%s\n", @@ -350,16 +483,92 @@ static void handle_dump_state(int sig, void *eloop_ctx, void *signal_ctx) { #ifdef HOSTAPD_DUMP_STATE struct hapd_interfaces *hapds = (struct hapd_interfaces *) eloop_ctx; - int i; + size_t i, j; for (i = 0; i < hapds->count; i++) { - hostapd *hapd = hapds->hapd[i]; - hostapd_dump_state(hapd); + struct hostapd_iface *hapd_iface = hapds->iface[i]; + for (j = 0; j < hapd_iface->num_bss; j++) + hostapd_dump_state(hapd_iface->bss[j]); } #endif /* HOSTAPD_DUMP_STATE */ } +#endif /* CONFIG_NATIVE_WINDOWS */ + +static void hostapd_broadcast_key_clear_iface(struct hostapd_data *hapd, + char *ifname) +{ + int i; + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (hostapd_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); + } + } +} + + +static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd) +{ + hostapd_broadcast_key_clear_iface(hapd, hapd->conf->iface); + return 0; +} + + +static int hostapd_broadcast_wep_set(struct hostapd_data *hapd) +{ + int errors = 0, idx; + struct hostapd_ssid *ssid = &hapd->conf->ssid; + + idx = ssid->wep.idx; + if (ssid->wep.default_len && + hostapd_set_encryption(hapd->conf->iface, + hapd, "WEP", NULL, idx, + ssid->wep.key[idx], + ssid->wep.len[idx], + idx == ssid->wep.idx)) { + printf("Could not set WEP encryption.\n"); + errors++; + } + + if (ssid->dyn_vlan_keys) { + size_t i; + for (i = 0; i <= ssid->max_dyn_vlan_keys; i++) { + const char *ifname; + struct hostapd_wep_keys *key = ssid->dyn_vlan_keys[i]; + if (key == NULL) + continue; + ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan, + i); + if (ifname == NULL) + continue; + + idx = key->idx; + if (hostapd_set_encryption(ifname, hapd, "WEP", NULL, + idx, key->key[idx], + key->len[idx], + idx == key->idx)) { + printf("Could not set dynamic VLAN WEP " + "encryption.\n"); + errors++; + } + } + } + return errors; +} + +/** + * hostapd_cleanup - Per-BSS cleanup (deinitialization) + * @hapd: Pointer to BSS data + * + * This function is used to free all per-BSS data structures and resources. + * This gets called in a loop for each BSS between calls to + * hostapd_cleanup_iface_pre() and hostapd_cleanup_iface() when an interface + * is deinitialized. Most of the modules that are initialized in + * hostapd_setup_bss() are deinitialized here. + */ static void hostapd_cleanup(struct hostapd_data *hapd) { hostapd_ctrl_iface_deinit(hapd); @@ -367,9 +576,27 @@ static void hostapd_cleanup(struct hostapd_data *hapd) free(hapd->default_wep_key); hapd->default_wep_key = NULL; iapp_deinit(hapd->iapp); + hapd->iapp = NULL; accounting_deinit(hapd); - wpa_deinit(hapd); + rsn_preauth_iface_deinit(hapd); + if (hapd->wpa_auth) { + wpa_deinit(hapd->wpa_auth); + hapd->wpa_auth = NULL; + + if (hostapd_set_privacy(hapd, 0)) { + wpa_printf(MSG_DEBUG, "Could not disable " + "PrivacyInvoked for interface %s", + hapd->conf->iface); + } + + if (hostapd_set_generic_elem(hapd, (u8 *) "", 0)) { + wpa_printf(MSG_DEBUG, "Could not remove generic " + "information element from interface %s", + hapd->conf->iface); + } + } ieee802_1x_deinit(hapd); + vlan_deinit(hapd); hostapd_acl_deinit(hapd); radius_client_deinit(hapd->radius); hapd->radius = NULL; @@ -378,14 +605,6 @@ static void hostapd_cleanup(struct hostapd_data *hapd) hostapd_wireless_event_deinit(hapd); - if (hapd->driver) - hostapd_driver_deinit(hapd); - - hostapd_config_free(hapd->conf); - hapd->conf = NULL; - - free(hapd->config_fname); - #ifdef EAP_TLS_FUNCS if (hapd->ssl_ctx) { tls_deinit(hapd->ssl_ctx); @@ -393,41 +612,449 @@ static void hostapd_cleanup(struct hostapd_data *hapd) } #endif /* EAP_TLS_FUNCS */ - if (hapd->eap_sim_db_priv) +#ifdef EAP_SERVER + if (hapd->eap_sim_db_priv) { eap_sim_db_deinit(hapd->eap_sim_db_priv); + hapd->eap_sim_db_priv = NULL; + } +#endif /* EAP_SERVER */ + + if (hapd->interface_added && + hostapd_bss_remove(hapd, hapd->conf->iface)) { + printf("Failed to remove BSS interface %s\n", + hapd->conf->iface); + } +} + + +/** + * hostapd_cleanup_iface_pre - Preliminary per-interface cleanup + * @iface: Pointer to interface data + * + * This function is called before per-BSS data structures are deinitialized + * with hostapd_cleanup(). + */ +static void hostapd_cleanup_iface_pre(struct hostapd_iface *iface) +{ +} + + +/** + * hostapd_cleanup_iface - Complete per-interface cleanup + * @iface: Pointer to interface data + * + * This function is called after per-BSS data structures are deinitialized + * with hostapd_cleanup(). + */ +static void hostapd_cleanup_iface(struct hostapd_iface *iface) +{ + hostapd_free_hw_features(iface->hw_features, iface->num_hw_features); + iface->hw_features = NULL; + 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); +} + + +static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd) +{ + int i; + + hostapd_broadcast_wep_set(hapd); + + if (hapd->conf->ssid.wep.default_len) + return 0; + + for (i = 0; i < 4; i++) { + if (hapd->conf->ssid.wep.key[i] && + hostapd_set_encryption(iface, hapd, "WEP", NULL, + 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"); + return -1; + } + if (hapd->conf->ssid.wep.key[i] && + i == hapd->conf->ssid.wep.idx) + hostapd_set_privacy(hapd, 1); + } + + return 0; } -static int hostapd_flush_old_stations(hostapd *hapd) +static int hostapd_flush_old_stations(struct hostapd_data *hapd) { int ret = 0; - printf("Flushing old station entries\n"); + wpa_printf(MSG_DEBUG, "Flushing old station entries"); if (hostapd_flush(hapd)) { printf("Could not connect to kernel driver.\n"); ret = -1; } - printf("Deauthenticate all stations\n"); + wpa_printf(MSG_DEBUG, "Deauthenticate all stations"); hostapd_deauth_all_stas(hapd); return ret; } -static int hostapd_setup_interface(struct hostapd_data *hapd) +static void hostapd_wpa_auth_logger(void *ctx, const u8 *addr, + logger_level level, const char *txt) { - struct hostapd_config *conf = hapd->conf; - u8 ssid[HOSTAPD_SSID_LEN + 1]; - int ssid_len, set_ssid; - int ret = 0; + struct hostapd_data *hapd = ctx; + int hlevel; - if (hostapd_driver_init(hapd)) { - printf("%s driver initialization failed.\n", - hapd->driver ? hapd->driver->name : "Unknown"); - hapd->driver = NULL; + switch (level) { + case LOGGER_WARNING: + hlevel = HOSTAPD_LEVEL_WARNING; + break; + case LOGGER_INFO: + hlevel = HOSTAPD_LEVEL_INFO; + break; + case LOGGER_DEBUG: + default: + hlevel = HOSTAPD_LEVEL_DEBUG; + break; + } + + hostapd_logger(hapd, addr, HOSTAPD_MODULE_WPA, hlevel, "%s", txt); +} + + +static void hostapd_wpa_auth_disconnect(void *ctx, const u8 *addr, + u16 reason) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + + wpa_printf(MSG_DEBUG, "%s: WPA authenticator requests disconnect: " + "STA " MACSTR " reason %d", + __func__, MAC2STR(addr), reason); + + sta = ap_get_sta(hapd, addr); + hostapd_sta_deauth(hapd, addr, reason); + if (sta == NULL) + return; + 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; +} + + +static void hostapd_wpa_auth_mic_failure_report(void *ctx, const u8 *addr) +{ + struct hostapd_data *hapd = ctx; + ieee80211_michael_mic_failure(hapd, addr, 0); +} + + +static void hostapd_wpa_auth_set_eapol(void *ctx, const u8 *addr, + wpa_eapol_variable var, int value) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta = ap_get_sta(hapd, addr); + if (sta == NULL) + return; + switch (var) { + case WPA_EAPOL_portEnabled: + ieee802_1x_notify_port_enabled(sta->eapol_sm, value); + break; + case WPA_EAPOL_portValid: + ieee802_1x_notify_port_valid(sta->eapol_sm, value); + break; + case WPA_EAPOL_authorized: + ieee802_1x_set_sta_authorized(hapd, sta, value); + break; + case WPA_EAPOL_portControl_Auto: + if (sta->eapol_sm) + sta->eapol_sm->portControl = Auto; + break; + case WPA_EAPOL_keyRun: + if (sta->eapol_sm) + sta->eapol_sm->keyRun = value ? TRUE : FALSE; + break; + case WPA_EAPOL_keyAvailable: + if (sta->eapol_sm) + sta->eapol_sm->keyAvailable = value ? TRUE : FALSE; + break; + case WPA_EAPOL_keyDone: + if (sta->eapol_sm) + sta->eapol_sm->keyDone = value ? TRUE : FALSE; + break; + case WPA_EAPOL_inc_EapolFramesTx: + if (sta->eapol_sm) + sta->eapol_sm->dot1xAuthEapolFramesTx++; + break; + } +} + + +static int hostapd_wpa_auth_get_eapol(void *ctx, const u8 *addr, + wpa_eapol_variable var) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta = ap_get_sta(hapd, addr); + if (sta == NULL || sta->eapol_sm == NULL) + return -1; + switch (var) { + case WPA_EAPOL_keyRun: + return sta->eapol_sm->keyRun; + case WPA_EAPOL_keyAvailable: + return sta->eapol_sm->keyAvailable; + default: + return -1; + } +} + + +static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr, + const u8 *prev_psk) +{ + struct hostapd_data *hapd = ctx; + return hostapd_get_psk(hapd->conf, addr, prev_psk); +} + + +static int hostapd_wpa_auth_get_pmk(void *ctx, const u8 *addr, u8 *pmk, + size_t *len) +{ + struct hostapd_data *hapd = ctx; + u8 *key; + size_t keylen; + struct sta_info *sta; + + sta = ap_get_sta(hapd, addr); + if (sta == NULL) + return -1; + + key = ieee802_1x_get_key_crypt(sta->eapol_sm, &keylen); + if (key == NULL) + return -1; + + if (keylen > *len) + keylen = WPA_PMK_LEN; + memcpy(pmk, key, keylen); + *len = keylen; + return 0; +} + + +static int hostapd_wpa_auth_set_key(void *ctx, int vlan_id, const char *alg, + const u8 *addr, int idx, u8 *key, + size_t key_len) +{ + struct hostapd_data *hapd = ctx; + const char *ifname = hapd->conf->iface; + + if (vlan_id > 0) { + ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan, vlan_id); + if (ifname == NULL) + return -1; + } + + return hostapd_set_encryption(ifname, hapd, alg, addr, idx, + key, key_len, 1); +} + + +static int hostapd_wpa_auth_get_seqnum(void *ctx, const u8 *addr, int idx, + u8 *seq) +{ + struct hostapd_data *hapd = ctx; + return hostapd_get_seqnum(hapd->conf->iface, hapd, addr, idx, seq); +} + + +static int hostapd_wpa_auth_get_seqnum_igtk(void *ctx, const u8 *addr, int idx, + u8 *seq) +{ + struct hostapd_data *hapd = ctx; + return hostapd_get_seqnum_igtk(hapd->conf->iface, hapd, addr, idx, + seq); +} + + +static int hostapd_wpa_auth_send_eapol(void *ctx, const u8 *addr, + const u8 *data, size_t data_len, + int encrypt) +{ + struct hostapd_data *hapd = ctx; + return hostapd_send_eapol(hapd, addr, data, data_len, encrypt); +} + + +static int hostapd_wpa_auth_for_each_sta( + void *ctx, int (*cb)(struct wpa_state_machine *sm, void *ctx), + void *cb_ctx) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (sta->wpa_sm && cb(sta->wpa_sm, cb_ctx)) + return 1; + } + return 0; +} + + +/** + * hostapd_validate_bssid_configuration - Validate BSSID configuration + * @iface: Pointer to interface data + * Returns: 0 on success, -1 on failure + * + * This function is used to validate that the configured BSSIDs are valid. + */ +static int hostapd_validate_bssid_configuration(struct hostapd_iface *iface) +{ + u8 mask[ETH_ALEN] = { 0 }; + struct hostapd_data *hapd = iface->bss[0]; + unsigned int i = iface->conf->num_bss, bits = 0, j; + int res; + + /* Generate BSSID mask that is large enough to cover the BSSIDs. */ + + /* Determine the bits necessary to cover the number of BSSIDs. */ + for (i--; i; i >>= 1) + bits++; + + /* Determine the bits necessary to any configured BSSIDs, + if they are higher than the number of BSSIDs. */ + for (j = 0; j < iface->conf->num_bss; j++) { + if (hostapd_mac_comp_empty(iface->conf->bss[j].bssid) == 0) + continue; + + for (i = 0; i < ETH_ALEN; i++) { + mask[i] |= + iface->conf->bss[j].bssid[i] ^ + hapd->own_addr[i]; + } + } + + for (i = 0; i < ETH_ALEN && mask[i] == 0; i++) + ; + j = 0; + if (i < ETH_ALEN) { + j = (5 - i) * 8; + + while (mask[i] != 0) { + mask[i] >>= 1; + j++; + } + } + + if (bits < j) + bits = j; + + if (bits > 40) + return -1; + + memset(mask, 0xff, ETH_ALEN); + j = bits / 8; + for (i = 5; i > 5 - j; i--) + mask[i] = 0; + j = bits % 8; + while (j--) + mask[i] <<= 1; + + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "BSS count %lu, BSSID mask " + MACSTR " (%d bits)\n", + (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)); 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)); + return -1; + } + } + + return 0; +} + + +static int mac_in_conf(struct hostapd_config *conf, const void *a) +{ + size_t i; + + for (i = 0; i < conf->num_bss; i++) { + if (hostapd_mac_comp(conf->bss[i].bssid, a) == 0) { + return 1; + } + } + + return 0; +} + + +/** + * hostapd_setup_bss - Per-BSS setup (initialization) + * @hapd: Pointer to BSS data + * @first: Whether this BSS is the first BSS of an interface + * + * This function is used to initialize all per-BSS data structures and + * resources. This gets called in a loop for each BSS when an interface is + * initialized. Most of the modules that are initialized here will be + * deinitialized in hostapd_cleanup(). + */ +static int hostapd_setup_bss(struct hostapd_data *hapd, int first) +{ + struct hostapd_bss_config *conf = hapd->conf; + u8 ssid[HOSTAPD_MAX_SSID_LEN + 1]; + int ssid_len, set_ssid; + + if (!first) { + if (hostapd_mac_comp_empty(hapd->conf->bssid) == 0) { + /* Allocate the next available BSSID. */ + do { + inc_byte_array(hapd->own_addr, ETH_ALEN); + } while (mac_in_conf(hapd->iconf, hapd->own_addr)); + } else { + /* Allocate the configured BSSID. */ + 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); + return -1; + } + } + + 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)); + return -1; + } + } + /* * Fetch the SSID from the system and use it or, * if one was specified in the config file, verify they @@ -438,37 +1065,51 @@ static int hostapd_setup_interface(struct hostapd_data *hapd) printf("Could not read SSID from system\n"); return -1; } - if (conf->ssid_set) { + if (conf->ssid.ssid_set) { /* * If SSID is specified in the config file and it differs * from what is being used then force installation of the * new SSID. */ - set_ssid = (conf->ssid_len != ssid_len || - memcmp(conf->ssid, ssid, ssid_len) != 0); + set_ssid = (conf->ssid.ssid_len != (size_t) ssid_len || + memcmp(conf->ssid.ssid, ssid, ssid_len) != 0); } else { /* * No SSID in the config file; just use the one we got * from the system. */ set_ssid = 0; - conf->ssid_len = ssid_len; - memcpy(conf->ssid, ssid, conf->ssid_len); - conf->ssid[conf->ssid_len] = '\0'; + conf->ssid.ssid_len = ssid_len; + 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); + hapd->conf->iface, MAC2STR(hapd->own_addr), + hapd->conf->ssid.ssid); if (hostapd_setup_wpa_psk(conf)) { printf("WPA-PSK setup failed.\n"); 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"); + return -1; + } + + if (hostapd_set_dtim_period(hapd, hapd->conf->dtim_period)) { + printf("Could not set DTIM period for kernel driver\n"); + return -1; + } + /* Set SSID for the kernel driver (to be used in beacon and probe * response frames) */ - if (set_ssid && hostapd_set_ssid(hapd, (u8 *) conf->ssid, - conf->ssid_len)) { + if (set_ssid && hostapd_set_ssid(hapd, (u8 *) conf->ssid.ssid, + conf->ssid.ssid_len)) { printf("Could not set SSID for kernel driver\n"); return -1; } @@ -480,21 +1121,7 @@ static int hostapd_setup_interface(struct hostapd_data *hapd) printf("RADIUS client initialization failed.\n"); 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 (hostapd_acl_init(hapd)) { printf("ACL initialization failed.\n"); return -1; @@ -504,9 +1131,51 @@ static int hostapd_setup_interface(struct hostapd_data *hapd) return -1; } - if (hapd->conf->wpa && wpa_init(hapd)) { - printf("WPA initialization failed.\n"); - 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 (accounting_init(hapd)) { @@ -520,21 +1189,308 @@ static int hostapd_setup_interface(struct hostapd_data *hapd) return -1; } - if (hostapd_wireless_event_init(hapd) < 0) + if (hostapd_ctrl_iface_init(hapd)) { + printf("Failed to setup control interface\n"); return -1; + } - if (hostapd_flush_old_stations(hapd)) + ieee802_11_set_beacon(hapd); + + if (vlan_init(hapd)) { + printf("VLAN initialization failed.\n"); return -1; + } - if (hostapd_ctrl_iface_init(hapd)) { - printf("Failed to setup control interface\n"); - ret = -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"); + return -1; + } + } + + 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"); + 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; + } + + 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; + } + + ap_list_init(iface); + + if (hostapd_driver_commit(hapd) < 0) { + wpa_printf(MSG_ERROR, "%s: Failed to commit driver " + "configuration", __func__); + return -1; } return ret; } +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) +{ + hostapd_iface_cb cb; + + if (!iface->setup_cb) + 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; + + iface->setup_cb = NULL; + + cb(iface, status); + + 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) +{ + struct hostapd_data *hapd = iface->bss[0]; + struct hostapd_bss_config *conf = hapd->conf; + size_t i; + char country[4]; + + /* + * 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"); + hapd->driver = NULL; + return -1; + } + for (i = 0; i < iface->num_bss; i++) + iface->bss[i]->driver = hapd->driver; + + 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); + 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"); + 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; + } + } + + /* TODO: merge with hostapd_driver_init() ? */ + if (hostapd_wireless_event_init(hapd) < 0) + return -1; + + if (hostapd_get_hw_features(iface)) { + /* Not all drivers support this yet, so continue without hw + * feature data. */ + } else { + return hostapd_select_hw_mode_start(iface, + setup_interface2_wrapper); + } + + 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; + + int ret; + + ret = setup_interface1(iface); + if (ret) + setup_interface_finalize(iface, ret); +} + + +/** + * 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); + 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; @@ -620,7 +1576,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-2005, Jouni Malinen <jkmaline@cc.hut.fi> " + "Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> " "and contributors\n"); } @@ -630,12 +1586,14 @@ static void usage(void) show_version(); fprintf(stderr, "\n" - "usage: hostapd [-hdBKt] <configuration file(s)>\n" + "usage: hostapd [-hdBKtv] [-P <PID file>] " + "<configuration file(s)>\n" "\n" "options:\n" " -h show this usage\n" " -d show more debug messages (-dd for even more)\n" " -B run daemon in the background\n" + " -P PID file\n" " -K include key data in debug messages\n" " -t include timestamps in some debug messages\n" " -v show hostapd version\n"); @@ -644,27 +1602,31 @@ static void usage(void) } -static hostapd * hostapd_init(const char *config_file) +/** + * hostapd_alloc_bss_data - Allocate and initialize per-BSS data + * @hapd_iface: Pointer to interface data + * @conf: Pointer to per-interface configuration + * @bss: Pointer to per-BSS configuration for this BSS + * Returns: Pointer to allocated BSS data + * + * This function is used to allocate per-BSS data structure. This data will be + * freed after hostapd_cleanup() is called for it during interface + * deinitialization. + */ +static struct hostapd_data * +hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface, + struct hostapd_config *conf, + struct hostapd_bss_config *bss) { - hostapd *hapd; - - hapd = malloc(sizeof(*hapd)); - if (hapd == NULL) { - printf("Could not allocate memory for hostapd data\n"); - goto fail; - } - memset(hapd, 0, sizeof(*hapd)); + struct hostapd_data *hapd; - hapd->config_fname = strdup(config_file); - if (hapd->config_fname == NULL) { - printf("Could not allocate memory for config_fname\n"); - goto fail; - } + hapd = wpa_zalloc(sizeof(*hapd)); + if (hapd == NULL) + return NULL; - hapd->conf = hostapd_config_read(hapd->config_fname); - if (hapd->conf == NULL) { - goto fail; - } + hapd->iconf = conf; + hapd->conf = bss; + hapd->iface = hapd_iface; if (hapd->conf->individual_wep_key_len > 0) { /* use key0 in individual key and key1 in broadcast key */ @@ -674,29 +1636,25 @@ static hostapd * hostapd_init(const char *config_file) #ifdef EAP_TLS_FUNCS if (hapd->conf->eap_server && (hapd->conf->ca_cert || hapd->conf->server_cert)) { + struct tls_connection_params params; + hapd->ssl_ctx = tls_init(NULL); if (hapd->ssl_ctx == NULL) { printf("Failed to initialize TLS\n"); goto fail; } - if (tls_global_ca_cert(hapd->ssl_ctx, hapd->conf->ca_cert)) { - printf("Failed to load CA certificate (%s)\n", - hapd->conf->ca_cert); - goto fail; - } - if (tls_global_client_cert(hapd->ssl_ctx, - hapd->conf->server_cert)) { - printf("Failed to load server certificate (%s)\n", - hapd->conf->server_cert); - goto fail; - } - if (tls_global_private_key(hapd->ssl_ctx, - hapd->conf->private_key, - hapd->conf->private_key_passwd)) { - printf("Failed to load private key (%s)\n", - hapd->conf->private_key); + + 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; + + if (tls_global_set_params(hapd->ssl_ctx, ¶ms)) { + printf("Failed to set TLS parameters\n"); goto fail; } + if (tls_global_set_verify(hapd->ssl_ctx, hapd->conf->check_crl)) { printf("Failed to enable check_crl\n"); @@ -705,47 +1663,135 @@ static hostapd * hostapd_init(const char *config_file) } #endif /* EAP_TLS_FUNCS */ +#ifdef EAP_SERVER if (hapd->conf->eap_sim_db) { hapd->eap_sim_db_priv = - eap_sim_db_init(hapd->conf->eap_sim_db); + 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"); 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->conf->driver; + hapd->driver = (struct driver_ops *) hapd->iconf->driver; return hapd; +#if defined(EAP_TLS_FUNCS) || defined(EAP_SERVER) +fail: +#endif + /* TODO: cleanup allocated resources(?) */ + free(hapd); + return NULL; +} + + +/** + * hostapd_init - Allocate and initialize per-interface data + * @config_file: Path to the configuration file + * Returns: Pointer to the allocated interface data or %NULL on failure + * + * This function is used to allocate main data structures for per-interface + * data. The allocated data buffer will be freed by calling + * hostapd_cleanup_iface(). + */ +static struct hostapd_iface * hostapd_init(const char *config_file) +{ + struct hostapd_iface *hapd_iface = NULL; + struct hostapd_config *conf = NULL; + struct hostapd_data *hapd; + size_t i; + + hapd_iface = wpa_zalloc(sizeof(*hapd_iface)); + if (hapd_iface == NULL) + goto fail; + + hapd_iface->config_fname = strdup(config_file); + if (hapd_iface->config_fname == NULL) + goto fail; + + conf = hostapd_config_read(hapd_iface->config_fname); + if (conf == NULL) + goto fail; + hapd_iface->conf = conf; + + hapd_iface->num_bss = conf->num_bss; + hapd_iface->bss = wpa_zalloc(conf->num_bss * + sizeof(struct hostapd_data *)); + if (hapd_iface->bss == NULL) + goto fail; + + for (i = 0; i < conf->num_bss; i++) { + hapd = hapd_iface->bss[i] = + hostapd_alloc_bss_data(hapd_iface, conf, + &conf->bss[i]); + if (hapd == NULL) + goto fail; + } + + return hapd_iface; + fail: - if (hapd) { - if (hapd->ssl_ctx) - tls_deinit(hapd->ssl_ctx); - if (hapd->conf) - hostapd_config_free(hapd->conf); - free(hapd->config_fname); - free(hapd); + if (conf) + hostapd_config_free(conf); + if (hapd_iface) { + for (i = 0; hapd_iface->bss && i < hapd_iface->num_bss; i++) { + hapd = hapd_iface->bss[i]; + if (hapd && hapd->ssl_ctx) + tls_deinit(hapd->ssl_ctx); + } + + free(hapd_iface->config_fname); + free(hapd_iface->bss); + 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, i, j; + int ret = 1, k; + size_t i, j; int c, debug = 0, daemonize = 0; + const char *pid_file = NULL; for (;;) { - c = getopt(argc, argv, "BdhKtv"); + c = getopt(argc, argv, "BdhKP:tv"); if (c < 0) break; switch (c) { @@ -754,6 +1800,8 @@ int main(int argc, char *argv[]) break; case 'd': debug++; + if (wpa_debug_level > 0) + wpa_debug_level--; break; case 'B': daemonize++; @@ -761,6 +1809,9 @@ int main(int argc, char *argv[]) case 'K': wpa_debug_show_keys++; break; + case 'P': + pid_file = optarg; + break; case 't': wpa_debug_timestamp++; break; @@ -780,67 +1831,106 @@ int main(int argc, char *argv[]) register_drivers(); /* NB: generated by Makefile */ + if (eap_server_register_methods()) { + wpa_printf(MSG_ERROR, "Failed to register EAP methods"); + return -1; + } + interfaces.count = argc - optind; - interfaces.hapd = malloc(interfaces.count * sizeof(hostapd *)); - if (interfaces.hapd == NULL) { + interfaces.iface = malloc(interfaces.count * + sizeof(struct hostapd_iface *)); + if (interfaces.iface == NULL) { printf("malloc failed\n"); exit(1); } - eloop_init(&interfaces); + if (eloop_init(&interfaces)) { + wpa_printf(MSG_ERROR, "Failed to initialize event loop"); + return -1; + } + +#ifndef CONFIG_NATIVE_WINDOWS eloop_register_signal(SIGHUP, handle_reload, NULL); - eloop_register_signal(SIGINT, handle_term, NULL); - eloop_register_signal(SIGTERM, handle_term, NULL); eloop_register_signal(SIGUSR1, handle_dump_state, NULL); +#endif /* CONFIG_NATIVE_WINDOWS */ + eloop_register_signal_terminate(handle_term, NULL); + /* Initialize interfaces */ for (i = 0; i < interfaces.count; i++) { printf("Configuration file: %s\n", argv[optind + i]); - interfaces.hapd[i] = hostapd_init(argv[optind + i]); - if (!interfaces.hapd[i]) + interfaces.iface[i] = hostapd_init(argv[optind + i]); + if (!interfaces.iface[i]) goto out; - for (j = 0; j < debug; j++) { - if (interfaces.hapd[i]->conf->logger_stdout_level > 0) - interfaces.hapd[i]->conf-> + for (k = 0; k < debug; k++) { + if (interfaces.iface[i]->bss[0]->conf-> + logger_stdout_level > 0) + interfaces.iface[i]->bss[0]->conf-> logger_stdout_level--; - interfaces.hapd[i]->conf->debug++; + interfaces.iface[i]->bss[0]->conf->debug++; } - if (hostapd_setup_interface(interfaces.hapd[i])) + + ret = hostapd_setup_interface_start(interfaces.iface[i], + setup_interface_done); + if (ret) goto out; - wpa_debug_level -= interfaces.hapd[0]->conf->debug; } - if (daemonize && daemon(0, 0)) { + if (daemonize && os_daemonize(pid_file)) { perror("daemon"); goto out; } +#ifndef CONFIG_NATIVE_WINDOWS openlog("hostapd", 0, LOG_DAEMON); +#endif /* CONFIG_NATIVE_WINDOWS */ eloop_run(); + /* Disconnect associated stations from all interfaces and BSSes */ for (i = 0; i < interfaces.count; i++) { - hostapd_free_stas(interfaces.hapd[i]); - hostapd_flush_old_stations(interfaces.hapd[i]); + for (j = 0; j < interfaces.iface[i]->num_bss; j++) { + struct hostapd_data *hapd = + interfaces.iface[i]->bss[j]; + hostapd_free_stas(hapd); + hostapd_flush_old_stations(hapd); + } } ret = 0; out: + /* Deinitialize all interfaces */ for (i = 0; i < interfaces.count; i++) { - if (!interfaces.hapd[i]) + if (!interfaces.iface[i]) continue; - - hostapd_cleanup(interfaces.hapd[i]); - free(interfaces.hapd[i]); + 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 = + interfaces.iface[i]->bss[j]; + hostapd_cleanup(hapd); + if (j == interfaces.iface[i]->num_bss - 1 && + hapd->driver) + hostapd_driver_deinit(hapd); + } + for (j = 0; j < interfaces.iface[i]->num_bss; j++) + free(interfaces.iface[i]->bss[j]); + hostapd_cleanup_iface(interfaces.iface[i]); } - free(interfaces.hapd); + free(interfaces.iface); eloop_destroy(); +#ifndef CONFIG_NATIVE_WINDOWS closelog(); +#endif /* CONFIG_NATIVE_WINDOWS */ + + eap_server_unregister_methods(); driver_unregister_all(); + os_daemonize_terminate(pid_file); + return ret; } diff --git a/contrib/hostapd/hostapd.conf b/contrib/hostapd/hostapd.conf index 3dd5aab..986ca26 100644 --- a/contrib/hostapd/hostapd.conf +++ b/contrib/hostapd/hostapd.conf @@ -1,7 +1,7 @@ ##### hostapd configuration file ############################################## # Empty lines and lines starting with # are ignored -# AP netdevice name (without 'ap' prefix, i.e., wlan0 uses wlan0ap for +# AP netdevice name (without 'ap' postfix, i.e., wlan0 uses wlan0ap for # management frames); ath0 for madwifi interface=wlan0 @@ -26,6 +26,7 @@ interface=wlan0 # bit 3 (8) = WPA # bit 4 (16) = driver interface # bit 5 (32) = IAPP +# bit 6 (64) = MLME # # Levels (minimum value for logged events): # 0 = verbose debugging @@ -76,7 +77,90 @@ 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. +# 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 +# channels and transmit power levels based on the regulatory limits. The +# country_code setting must be configured with the correct country for +# IEEE 802.11d functions. +# (default: 0 = disabled) +#ieee80211d=1 + +# Enable IEEE 802.11h. This enables 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) +channel=60 + +# Beacon interval in kus (1.024 ms) (default: 100; range 15..65535) +beacon_int=100 + +# DTIM (delivery trafic information message) period (range 1..255): +# number of beacons between DTIMs (1 = every beacon includes DTIM element) +# (default: 2) +dtim_period=2 + +# Maximum number of stations allowed in station table. New stations will be +# rejected after the station table is full. IEEE 802.11 has a limit of 2007 +# different association IDs, so this number should not be larger than that. +# (default: 2007) +max_num_sta=255 + +# RTS/CTS threshold; 2347 = disabled (default); range 0..2347 +# If this field is not included in hostapd.conf, hostapd will not control +# RTS threshold and 'iwconfig wlan# rts <val>' can be used to set it. +rts_threshold=2347 + +# Fragmentation threshold; 2346 = disabled (default); range 256..2346 +# If this field is not included in hostapd.conf, hostapd will not control +# fragmentation threshold and 'iwconfig wlan# frag <val>' can be used to set +# it. +fragm_threshold=2346 + +# Rate configuration +# Default is to enable all rates supported by the hardware. This configuration +# item allows this list be filtered so that only the listed rates will be left +# in the list. If the list is empty, all rates are used. This list can have +# entries that are not in the list of rates the hardware supports (such entries +# are ignored). The entries in this list are in 100 kbps, i.e., 11 Mbps = 110. +# If this item is present, at least one rate have to be matching with the rates +# hardware supports. +# default: use the most common supported rate setting for the selected +# hw_mode (i.e., this line can be removed from configuration file in most +# cases) +#supported_rates=10 20 55 110 60 90 120 180 240 360 480 540 + +# Basic rate set configuration +# List of rates (in 100 kbps) that are included in the basic rate set. +# If this item is not included, usually reasonable default set is used. +#basic_rates=10 20 +#basic_rates=10 20 55 110 +#basic_rates=60 120 240 + # 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. # 0 = accept unless in deny list # 1 = deny unless in accept list # 2 = use external RADIUS server (accept/deny lists are searched first) @@ -96,10 +180,197 @@ macaddr_acl=0 # bit 1 = Shared Key Authentication (requires WEP) auth_algs=3 +# Send empty SSID in beacons and ignore probe request frames that do not +# specify full SSID, i.e., require stations to know SSID. +# default: disabled (0) +# 1 = send empty (length=0) SSID in beacon and ignore probe request for +# broadcast SSID +# 2 = clear SSID (ASCII 0), but keep the original length (this may be required +# with some clients that do not support empty SSID) and ignore probe +# requests for broadcast SSID +ignore_broadcast_ssid=0 + +# TX queue parameters (EDCF / bursting) +# default for all these fields: not set, use hardware defaults +# tx_queue_<queue name>_<param> +# queues: data0, data1, data2, data3, after_beacon, beacon +# (data0 is the highest priority queue) +# parameters: +# aifs: AIFS (default 2) +# cwmin: cwMin (1, 3, 7, 15, 31, 63, 127, 255, 511, 1023) +# cwmax: cwMax (1, 3, 7, 15, 31, 63, 127, 255, 511, 1023); cwMax >= cwMin +# burst: maximum length (in milliseconds with precision of up to 0.1 ms) for +# bursting +# +# Default WMM parameters (IEEE 802.11 draft; 11-03-0504-03-000e): +# These parameters are used by the access point when transmitting frames +# to the clients. +# +# Low priority / AC_BK = background +#tx_queue_data3_aifs=7 +#tx_queue_data3_cwmin=15 +#tx_queue_data3_cwmax=1023 +#tx_queue_data3_burst=0 +# Note: for IEEE 802.11b mode: cWmin=31 cWmax=1023 burst=0 +# +# Normal priority / AC_BE = best effort +#tx_queue_data2_aifs=3 +#tx_queue_data2_cwmin=15 +#tx_queue_data2_cwmax=63 +#tx_queue_data2_burst=0 +# Note: for IEEE 802.11b mode: cWmin=31 cWmax=127 burst=0 +# +# High priority / AC_VI = video +#tx_queue_data1_aifs=1 +#tx_queue_data1_cwmin=7 +#tx_queue_data1_cwmax=15 +#tx_queue_data1_burst=3.0 +# Note: for IEEE 802.11b mode: cWmin=15 cWmax=31 burst=6.0 +# +# Highest priority / AC_VO = voice +#tx_queue_data0_aifs=1 +#tx_queue_data0_cwmin=3 +#tx_queue_data0_cwmax=7 +#tx_queue_data0_burst=1.5 +# Note: for IEEE 802.11b mode: cWmin=7 cWmax=15 burst=3.3 +# +# Special queues; normally not user configurable +# +#tx_queue_after_beacon_aifs=2 +#tx_queue_after_beacon_cwmin=15 +#tx_queue_after_beacon_cwmax=1023 +#tx_queue_after_beacon_burst=0 +# +#tx_queue_beacon_aifs=2 +#tx_queue_beacon_cwmin=3 +#tx_queue_beacon_cwmax=7 +#tx_queue_beacon_burst=1.5 + +# 802.1D Tag to AC mappings +# WMM specifies following mapping of data frames to different ACs. This mapping +# can be configured using Linux QoS/tc and sch_pktpri.o module. +# 802.1D Tag 802.1D Designation Access Category WMM Designation +# 1 BK AC_BK Background +# 2 - AC_BK Background +# 0 BE AC_BE Best Effort +# 3 EE AC_VI Video +# 4 CL AC_VI Video +# 5 VI AC_VI Video +# 6 VO AC_VO Voice +# 7 NC AC_VO Voice +# Data frames with no priority information: AC_BE +# Management frames: AC_VO +# PS-Poll frames: AC_BE + +# Default WMM parameters (IEEE 802.11 draft; 11-03-0504-03-000e): +# for 802.11a or 802.11g networks +# These parameters are sent to WMM clients when they associate. +# The parameters will be used by WMM clients for frames transmitted to the +# access point. +# +# note - txop_limit is in units of 32microseconds +# note - acm is admission control mandatory flag. 0 = admission control not +# required, 1 = mandatory +# note - here cwMin and cmMax are in exponent form. the actual cw value used +# will be (2^n)-1 where n is the value given here +# +wme_enabled=1 +# +# Low priority / AC_BK = background +wme_ac_bk_cwmin=4 +wme_ac_bk_cwmax=10 +wme_ac_bk_aifs=7 +wme_ac_bk_txop_limit=0 +wme_ac_bk_acm=0 +# Note: for IEEE 802.11b mode: cWmin=5 cWmax=10 +# +# Normal priority / AC_BE = best effort +wme_ac_be_aifs=3 +wme_ac_be_cwmin=4 +wme_ac_be_cwmax=10 +wme_ac_be_txop_limit=0 +wme_ac_be_acm=0 +# Note: for IEEE 802.11b mode: cWmin=5 cWmax=7 +# +# High priority / AC_VI = video +wme_ac_vi_aifs=2 +wme_ac_vi_cwmin=3 +wme_ac_vi_cwmax=4 +wme_ac_vi_txop_limit=94 +wme_ac_vi_acm=0 +# Note: for IEEE 802.11b mode: cWmin=4 cWmax=5 txop_limit=188 +# +# Highest priority / AC_VO = voice +wme_ac_vo_aifs=2 +wme_ac_vo_cwmin=2 +wme_ac_vo_cwmax=3 +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. +# It must be between 0 and 3, and the corresponding key must be set. +# default: not set +#wep_default_key=0 +# The WEP keys to use. +# A key may be a quoted string or unquoted hexadecimal digits. +# The key length should be 5, 13, or 16 characters, or 10, 26, or 32 +# digits, depending on whether 40-bit (64-bit), 104-bit (128-bit), or +# 128-bit (152-bit) WEP is used. +# Only the default key must be supplied; the others are optional. +# default: not set +#wep_key0=123456789a +#wep_key1="vwxyz" +#wep_key2=0102030405060708090a0b0c0d +#wep_key3=".2.4.6.8.0.23" + +# Station inactivity limit +# +# If a station does not send anything in ap_max_inactivity seconds, an +# empty data frame is sent to it in order to verify whether it is +# still in range. If this frame is not ACKed, the station will be +# disassociated and then deauthenticated. This feature is used to +# clear station table of old entries when the STAs move out of the +# range. +# +# The station can associate again with the AP if it is still in range; +# this inactivity poll is just used as a nicer way of verifying +# inactivity; i.e., client will not report broken connection because +# disassociation frame is not sent immediately without first polling +# the STA with a data frame. +# default: 300 (i.e., 5 minutes) +#ap_max_inactivity=300 + +# Enable/disable internal bridge for packets between associated stations. +# +# When IEEE 802.11 is used in managed mode, packets are usually send through +# the AP even if they are from a wireless station to another wireless station. +# This functionality requires that the AP has a bridge functionality that sends +# frames back to the same interface if their destination is another associated +# station. In addition, broadcast/multicast frames from wireless stations will +# be sent both to the host system net stack (e.g., to eventually wired network) +# and back to the wireless interface. +# +# The internal bridge is implemented within the wireless kernel module and it +# bypasses kernel filtering (netfilter/iptables/ebtables). If direct +# communication between the stations needs to be prevented, the internal +# bridge can be disabled by setting bridge_packets=0. +# +# Note: If this variable is not included in hostapd.conf, hostapd does not +# change the configuration and iwpriv can be used to set the value with +# 'iwpriv wlan# param 10 0' command. If the variable is in hostapd.conf, +# hostapd will override possible iwpriv configuration whenever configuration +# file is reloaded. +# +# default: do not control from hostapd (80211.o defaults to 1=enabled) +#bridge_packets=1 + ##### IEEE 802.1X-2004 related configuration ################################## @@ -108,7 +379,7 @@ auth_algs=3 # 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 +# version 2. However, there are many client implementations that do not handle # the new version number correctly (they seem to drop the frames completely). # In order to make hostapd interoperate with these clients, the version number # can be set to the older version (1) with this configuration value. @@ -188,9 +459,10 @@ eap_server=0 # 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 +# implementation in eap_sim_db.c uses this as the UNIX domain socket name for +# the HLR/AuC gateway (e.g., hlr_auc_gw). In this case, the path uses "unix:" +# prefix. +#eap_sim_db=unix:/tmp/hlr_auc_gw.sock ##### IEEE 802.11f - Inter-Access Point Protocol (IAPP) ####################### @@ -250,6 +522,32 @@ own_ip_addr=127.0.0.1 # 60 (1 minute). #radius_acct_interim_interval=600 +# Dynamic VLAN mode; allow RADIUS authentication server to decide which VLAN +# is used for the stations. This information is parsed from following RADIUS +# attributes based on RFC 3580 and RFC 2868: Tunnel-Type (value 13 = VLAN), +# Tunnel-Medium-Type (value 6 = IEEE 802), Tunnel-Private-Group-ID (value +# VLANID as a string). vlan_file option below must be configured if dynamic +# VLANs are used. +# 0 = disabled (default) +# 1 = option; use default interface if RADIUS server does not include VLAN ID +# 2 = required; reject authentication if RADIUS server does not include VLAN ID +#dynamic_vlan=0 + +# VLAN interface list for dynamic VLAN mode is read from a separate text file. +# This list is used to map VLAN ID from the RADIUS server to a network +# interface. Each station is bound to one interface in the same way as with +# multiple BSSIDs or SSIDs. Each line in this text file is defining a new +# interface and the line must include VLAN ID and interface name separated by +# white space (space or tab). +#vlan_file=/etc/hostapd.vlan + +# Interface where 802.1q tagged packets should appear when a RADIUS server is +# used to determine which VLAN a station is on. hostapd creates a bridge for +# each VLAN. Then hostapd adds a VLAN interface (associated with the interface +# indicated by 'vlan_tagged_interface') and the appropriate wireless interface +# to the bridge. +#vlan_tagged_interface=eth0 + ##### RADIUS authentication server configuration ############################## @@ -339,3 +637,79 @@ own_ip_addr=127.0.0.1 # pre-authentication is only used with APs other than the currently associated # one. #rsn_preauth_interfaces=eth0 + +# peerkey: Whether PeerKey negotiation for direct links (IEEE 802.11e) is +# allowed. This is only used with RSN/WPA2. +# 0 = disabled (default) +# 1 = enabled +#peerkey=1 + +# ieee80211w: Whether management frame protection is enabled +# 0 = disabled (default) +# 1 = optional +# 2 = required +#ieee80211w=0 + +##### Passive scanning ######################################################## +# Scan different channels every N seconds. 0 = disable passive scanning. +#passive_scan_interval=60 + +# Listen N usecs on each channel when doing passive scanning. +# This value plus the time needed for changing channels should be less than +# 32 milliseconds (i.e. 32000 usec) to avoid interruptions to normal +# operations. Time needed for channel changing varies based on the used wlan +# hardware. +# default: disabled (0) +#passive_scan_listen=10000 + +# Passive scanning mode: +# 0 = scan all supported modes (802.11a/b/g/Turbo) (default) +# 1 = scan only the mode that is currently used for normal operations +#passive_scan_mode=1 + +# Maximum number of entries kept in AP table (either for passive scanning or +# for detecting Overlapping Legacy BSS Condition). The oldest entry will be +# removed when adding a new entry that would make the list grow over this +# limit. Note! Wi-Fi certification for IEEE 802.11g requires that OLBC is +# enabled, so this field should not be set to 0 when using IEEE 802.11g. +# default: 255 +#ap_table_max_size=255 + +# Number of seconds of no frames received after which entries may be deleted +# from the AP table. Since passive scanning is not usually performed frequently +# this should not be set to very small value. In addition, there is no +# guarantee that every scan cycle will receive beacon frames from the +# neighboring APs. +# default: 60 +#ap_table_expiration_time=3600 + +# Multiple BSSID support +# +# Above configuration is using the default interface (wlan#, or multi-SSID VLAN +# interfaces). Other BSSIDs can be added by using separator 'bss' with +# default interface name to be allocated for the data packets of the new BSS. +# +# hostapd will generate BSSID mask based on the BSSIDs that are +# configured. hostapd will verify that dev_addr & MASK == dev_addr. If this is +# not the case, the MAC address of the radio must be changed before starting +# hostapd (ifconfig wlan0 hw ether <MAC addr>). +# +# BSSIDs are assigned in order to each BSS, unless an explicit BSSID is +# specified using the 'bssid' parameter. +# If an explicit BSSID is specified, it must be chosen such that it: +# - results in a valid MASK that covers it and the dev_addr +# - is not the same as the MAC address of the radio +# - is not the same as any other explicitly specified BSSID +# +# Please note that hostapd uses some of the values configured for the first BSS +# as the defaults for the following BSSes. However, it is recommended that all +# BSSes include explicit configuration of all relevant configuration items. +# +#bss=wlan0_0 +#ssid=test2 +# most of the above items can be used here (apart from radio interface specific +# items, like channel) + +#bss=wlan0_1 +#bssid=00:13:10:95:fe:0b +# ... diff --git a/contrib/hostapd/hostapd.eap_user b/contrib/hostapd/hostapd.eap_user index fd7b420..b9d7f8b 100644 --- a/contrib/hostapd/hostapd.eap_user +++ b/contrib/hostapd/hostapd.eap_user @@ -1,15 +1,24 @@ # hostapd user database for integrated EAP authenticator + # Each line must contain an identity, EAP method(s), and an optional password # separated with whitespace (space or tab). The identity and password must be -# double quoted ("user"). [2] flag in the end of the line can be used to mark -# users for tunneled phase 2 authentication (e.g., within EAP-PEAP). In these -# cases, an anonymous identity can be used in the unencrypted phase 1 and the -# real user identity is transmitted only within the encrypted tunnel in phase -# 2. If non-anonymous access is needed, two user entries is needed, one for -# phase 1 and another with the same username for phase 2. +# double quoted ("user"). Password can alternatively be stored as +# NtPasswordHash (16-byte MD4 hash of the unicode presentation of the password +# in unicode) if it is used for MSCHAP or MSCHAPv2 authentication. This means +# that the plaintext password does not need to be included in the user file. +# Password hash is stored as hash:<16-octets of hex data> without quotation +# marks. + +# [2] flag in the end of the line can be used to mark users for tunneled phase +# 2 authentication (e.g., within EAP-PEAP). In these cases, an anonymous +# identity can be used in the unencrypted phase 1 and the real user identity +# is transmitted only within the encrypted tunnel in phase 2. If non-anonymous +# access is needed, two user entries is needed, one for phase 1 and another +# with the same username for phase 2. # -# EAP-TLS, EAP-PEAP, EAP-TTLS, and EAP-SIM do not use password option. -# EAP-MD5, EAP-MSCHAPV2, EAP-GTC, EAP-PAX, and EAP-PSK require a password. +# EAP-TLS, EAP-PEAP, EAP-TTLS, 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. # # * can be used as a wildcard to match any user identity. The main purposes for @@ -18,6 +27,11 @@ # first matching entry is selected, so * should be used as the last phase 1 # user entry. # +# "prefix"* can be used to match the given prefix and anything after this. The +# main purpose for this is to be able to avoid EAP method negotiation when the +# method is using known prefix in identities (e.g., EAP-SIM and EAP-AKA). This +# is only allowed for phase 1 identities. +# # Multiple methods can be configured to make the authenticator try them one by # one until the peer accepts one. The method names are separated with a # comma (,). @@ -37,9 +51,19 @@ "pax.user@example.com" PAX 0123456789abcdef0123456789abcdef "psk user" PSK "unknown" "psk.user@example.com" PSK 0123456789abcdef0123456789abcdef +"sake.user@example.com" SAKE 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef "ttls" TTLS "not anonymous" PEAP -* PEAP,TTLS,TLS,SIM +# Default to EAP-SIM and EAP-AKA based on fixed identity prefixes +"0"* AKA,TTLS,TLS,PEAP,SIM +"1"* SIM,TTLS,TLS,PEAP,AKA +"2"* AKA,TTLS,TLS,PEAP,SIM +"3"* SIM,TTLS,TLS,PEAP,AKA +"4"* AKA,TTLS,TLS,PEAP,SIM +"5"* SIM,TTLS,TLS,PEAP,AKA + +# Wildcard for all other identities +* PEAP,TTLS,TLS,SIM,AKA # Phase 2 (tunnelled within EAP-PEAP or EAP-TTLS) users "t-md5" MD5 "password" [2] @@ -47,3 +71,4 @@ "t-gtc" GTC "password" [2] "not anonymous" MSCHAPV2 "password" [2] "user" MD5,GTC,MSCHAPV2 "password" [2] +"test user" MSCHAPV2 hash:000102030405060708090a0b0c0d0e0f [2] diff --git a/contrib/hostapd/hostapd.h b/contrib/hostapd/hostapd.h index fdcb420..70f802d 100644 --- a/contrib/hostapd/hostapd.h +++ b/contrib/hostapd/hostapd.h @@ -1,3 +1,18 @@ +/* + * hostapd / Initialization and configuration + * Host AP kernel driver + * 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 HOSTAPD_H #define HOSTAPD_H @@ -28,11 +43,17 @@ #include "config.h" +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +#define MAX_VLAN_ID 4094 + struct ieee8023_hdr { u8 dest[6]; u8 src[6]; u16 ethertype; -} __attribute__ ((packed)); +} STRUCT_PACKED; struct ieee80211_hdr { @@ -44,7 +65,11 @@ struct ieee80211_hdr { u16 seq_ctrl; /* followed by 'u8 addr4[6];' if ToDS and FromDS is set in data frame */ -} __attribute__ ((packed)); +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ #define IEEE80211_DA_FROMDS addr1 #define IEEE80211_BSSID_FROMDS addr2 @@ -61,21 +86,36 @@ struct ieee80211_hdr { extern unsigned char rfc1042_header[6]; -typedef struct hostapd_data hostapd; - struct hostap_sta_driver_data { unsigned long rx_packets, tx_packets, rx_bytes, tx_bytes; + unsigned long current_tx_rate; + unsigned long inactive_msec; + unsigned long flags; + unsigned long num_ps_buf_frames; + unsigned long tx_retry_failed; + unsigned long tx_retry_count; + int last_rssi; + int last_ack_rssi; }; struct driver_ops; struct wpa_ctrl_dst; struct radius_server_data; +#ifdef CONFIG_FULL_DYNAMIC_VLAN +struct full_dynamic_vlan; +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + +/** + * struct hostapd_data - hostapd per-BSS data structure + */ struct hostapd_data { - struct hostapd_config *conf; - char *config_fname; + struct hostapd_iface *iface; + struct hostapd_config *iconf; + struct hostapd_bss_config *conf; + int interface_added; /* virtual interface added for this BSS */ - u8 own_addr[6]; + u8 own_addr[ETH_ALEN]; int num_sta; /* number of entries in sta_list */ struct sta_info *sta_list; /* STA info list head */ @@ -93,6 +133,7 @@ struct hostapd_data { u8 default_wep_key_idx; struct radius_client_data *radius; + int radius_client_reconfigured; u32 acct_session_id_hi, acct_session_id_lo; struct iapp_data *iapp; @@ -106,16 +147,8 @@ struct hostapd_data { struct hostapd_cached_radius_acl *acl_cache; struct hostapd_acl_query_data *acl_queries; - u8 *wpa_ie; - size_t wpa_ie_len; struct wpa_authenticator *wpa_auth; -#define PMKID_HASH_SIZE 128 -#define PMKID_HASH(pmkid) (unsigned int) ((pmkid)[0] & 0x7f) - struct rsn_pmksa_cache *pmkid[PMKID_HASH_SIZE]; - struct rsn_pmksa_cache *pmksa; - int pmksa_count; - struct rsn_preauth_interface *preauth_iface; time_t michael_mic_failure; int michael_mic_failures; @@ -127,23 +160,96 @@ struct hostapd_data { void *ssl_ctx; void *eap_sim_db_priv; struct radius_server_data *radius_srv; + + int parameter_set_count; + +#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 hostapd_config_change; + +/** + * struct hostapd_iface - hostapd per-interface data structure + */ +struct hostapd_iface { + char *config_fname; + struct hostapd_config *conf; + + hostapd_iface_cb setup_cb; + + size_t num_bss; + struct hostapd_data **bss; + + int num_ap; /* number of entries in ap_list */ + struct ap_info *ap_list; /* AP info list head */ + struct ap_info *ap_hash[STA_HASH_SIZE]; + struct ap_info *ap_iter_list; + + struct hostapd_hw_modes *hw_features; + int num_hw_features; + struct hostapd_hw_modes *current_mode; + /* Rates that are currently used (i.e., filtered copy of + * current_mode->channels */ + int num_rates; + struct hostapd_rate_data *current_rates; + hostapd_iface_cb hw_mode_sel_cb; + + u16 hw_flags; + + /* Number of associated Non-ERP stations (i.e., stations using 802.11b + * in 802.11g BSS) */ + int num_sta_non_erp; + + /* Number of associated stations that do not support Short Slot Time */ + int num_sta_no_short_slot_time; + + /* Number of associated stations that do not support Short Preamble */ + int num_sta_no_short_preamble; + + int olbc; /* Overlapping Legacy BSS Condition */ + + int dfs_enable; + u8 pwr_const; + unsigned int tx_power; + unsigned int sta_max_power; + + unsigned int channel_switch; + + struct hostapd_config_change *change; + hostapd_iface_cb reload_iface_cb; + hostapd_iface_cb config_reload_cb; }; -void hostapd_new_assoc_sta(hostapd *hapd, struct sta_info *sta, int reassoc); +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, - ...) __attribute__ ((format (printf, 5, 6))); + ...) 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); #endif /* HOSTAPD_H */ diff --git a/contrib/hostapd/hostapd.vlan b/contrib/hostapd/hostapd.vlan new file mode 100644 index 0000000..98254fa --- /dev/null +++ b/contrib/hostapd/hostapd.vlan @@ -0,0 +1,9 @@ +# VLAN ID to network interface mapping +1 vlan1 +2 vlan2 +3 vlan3 +100 guest +# Optional wildcard entry matching all VLAN IDs. The first # in the interface +# name will be replaced with the VLAN ID. The network interfaces are created +# (and removed) dynamically based on the use. +* vlan# diff --git a/contrib/hostapd/hostapd_cli.1 b/contrib/hostapd/hostapd_cli.1 index 062fc78..8d6587f 100644 --- a/contrib/hostapd/hostapd_cli.1 +++ b/contrib/hostapd/hostapd_cli.1 @@ -77,7 +77,7 @@ Exit hostapd_cli. .SH SEE ALSO .BR hostapd (8). .SH AUTHOR -hostapd_cli was written by Jouni Malinen <jkmaline@cc.hut.fi>. +hostapd_cli was written by Jouni Malinen <j@w1.fi>. .PP This manual page was written by Faidon Liambotis <faidon@cube.gr>, for the Debian project (but may be used by others). diff --git a/contrib/hostapd/hostapd_cli.c b/contrib/hostapd/hostapd_cli.c index d93c41d..870f221 100644 --- a/contrib/hostapd/hostapd_cli.c +++ b/contrib/hostapd/hostapd_cli.c @@ -1,6 +1,6 @@ /* * hostapd - command line interface for hostapd daemon - * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.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 @@ -12,11 +12,7 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <signal.h> -#include <unistd.h> +#include "includes.h" #include <dirent.h> #include "wpa_ctrl.h" @@ -25,7 +21,7 @@ static const char *hostapd_cli_version = "hostapd_cli v" VERSION_STR "\n" -"Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> and contributors"; +"Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> and contributors"; static const char *hostapd_cli_license = @@ -47,7 +43,7 @@ static const char *hostapd_cli_full_license = "\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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA\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" @@ -83,8 +79,9 @@ static const char *hostapd_cli_full_license = static const char *commands_help = "Commands:\n" " mib get MIB variables (dot1x, dot11, radius)\n" -" sta <addr> get MIB vatiables for one station\n" +" 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" " help show this usage help\n" " interface [ifname] show interfaces/select interface\n" " level <debug level> change debug level\n" @@ -218,6 +215,20 @@ static int hostapd_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[]) } +static int hostapd_cli_cmd_new_sta(struct wpa_ctrl *ctrl, int argc, + char *argv[]) +{ + char buf[64]; + if (argc != 1) { + printf("Invalid 'new_sta' command - exactly one argument, STA " + "address, is required.\n"); + return -1; + } + snprintf(buf, sizeof(buf), "NEW_STA %s", argv[0]); + return wpa_ctrl_command(ctrl, buf); +} + + static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd, char *addr, size_t addr_len) { @@ -365,6 +376,7 @@ static struct hostapd_cli_cmd hostapd_cli_commands[] = { { "mib", hostapd_cli_cmd_mib }, { "sta", hostapd_cli_cmd_sta }, { "all_sta", hostapd_cli_cmd_all_sta }, + { "new_sta", hostapd_cli_cmd_new_sta }, { "help", hostapd_cli_cmd_help }, { "interface", hostapd_cli_cmd_interface }, { "level", hostapd_cli_cmd_level }, @@ -525,6 +537,7 @@ int main(int argc, char *argv[]) printf("%s\n", hostapd_cli_version); return 0; case 'i': + free(ctrl_ifname); ctrl_ifname = strdup(optarg); break; case 'p': diff --git a/contrib/hostapd/hw_features.c b/contrib/hostapd/hw_features.c new file mode 100644 index 0000000..484959f --- /dev/null +++ b/contrib/hostapd/hw_features.c @@ -0,0 +1,429 @@ +/* + * 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/hw_features.h b/contrib/hostapd/hw_features.h new file mode 100644 index 0000000..7e5d443 --- /dev/null +++ b/contrib/hostapd/hw_features.h @@ -0,0 +1,61 @@ +/* + * 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. + */ + +#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 + +struct hostapd_channel_data { + short chan; /* channel number (IEEE 802.11) */ + short freq; /* frequency in MHz */ + int flag; /* flag for hostapd use (HOSTAPD_CHAN_*) */ +}; + +#define HOSTAPD_RATE_ERP 0x00000001 +#define HOSTAPD_RATE_BASIC 0x00000002 +#define HOSTAPD_RATE_PREAMBLE2 0x00000004 +#define HOSTAPD_RATE_SUPPORTED 0x00000010 +#define HOSTAPD_RATE_OFDM 0x00000020 +#define HOSTAPD_RATE_CCK 0x00000040 +#define HOSTAPD_RATE_MANDATORY 0x00000100 + +struct hostapd_rate_data { + int rate; /* rate in 100 kbps */ + int flags; /* HOSTAPD_RATE_ flags */ +}; + +struct hostapd_hw_modes { + int mode; + int num_channels; + struct hostapd_channel_data *channels; + int num_rates; + struct hostapd_rate_data *rates; +}; + + +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); +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); + +#endif /* HW_FEATURES_H */ diff --git a/contrib/hostapd/iapp.c b/contrib/hostapd/iapp.c index f4dd557..5e027fb 100644 --- a/contrib/hostapd/iapp.c +++ b/contrib/hostapd/iapp.c @@ -1,7 +1,6 @@ /* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / IEEE 802.11F-2003 Inter-Access Point Protocol (IAPP) - * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * hostapd / IEEE 802.11F-2003 Inter-Access Point Protocol (IAPP) + * 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 @@ -11,6 +10,11 @@ * license. * * See README and COPYING for more details. + * + * Note: IEEE 802.11F-2003 was a experimental use specification. It has expired + * and IEEE has withdrawn it. In other words, it is likely better to look at + * using some other mechanism for AP-to-AP communication than extenting the + * implementation here. */ /* TODO: @@ -33,15 +37,9 @@ * - IEEE 802.11 context transfer */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <unistd.h> -#include <netinet/in.h> +#include "includes.h" #include <net/if.h> #include <sys/ioctl.h> -#include <sys/socket.h> -#include <arpa/inet.h> #ifdef USE_KERNEL_HEADERS #include <linux/if_packet.h> #else /* USE_KERNEL_HEADERS */ @@ -53,7 +51,6 @@ #include "iapp.h" #include "eloop.h" #include "sta_info.h" -#include "hostap_common.h" #define IAPP_MULTICAST "224.0.1.178" @@ -181,7 +178,7 @@ struct iapp_data { }; -static void iapp_send_add(struct iapp_data *iapp, u8 *macaddr, u16 seq_num) +static void iapp_send_add(struct iapp_data *iapp, u8 *mac_addr, u16 seq_num) { char buf[128]; struct iapp_hdr *hdr; @@ -200,7 +197,7 @@ static void iapp_send_add(struct iapp_data *iapp, u8 *macaddr, u16 seq_num) add = (struct iapp_add_notify *) (hdr + 1); add->addr_len = ETH_ALEN; add->reserved = 0; - memcpy(add->mac_addr, macaddr, ETH_ALEN); + memcpy(add->mac_addr, mac_addr, ETH_ALEN); add->seq_num = host_to_be16(seq_num); @@ -323,8 +320,10 @@ static void iapp_receive_udp(int sock, void *eloop_ctx, void *sock_ctx) fromlen = sizeof(from); len = recvfrom(iapp->udp_sock, buf, sizeof(buf), 0, (struct sockaddr *) &from, &fromlen); - if (len < 0) + if (len < 0) { perror("recvfrom"); + return; + } if (from.sin_addr.s_addr == iapp->own.s_addr) return; /* ignore own IAPP messages */ @@ -333,9 +332,9 @@ static void iapp_receive_udp(int sock, void *eloop_ctx, void *sock_ctx) HOSTAPD_LEVEL_DEBUG, "Received %d byte IAPP frame from %s%s\n", len, inet_ntoa(from.sin_addr), - len < sizeof(*hdr) ? " (too short)" : ""); + len < (int) sizeof(*hdr) ? " (too short)" : ""); - if (len < sizeof(*hdr)) + if (len < (int) sizeof(*hdr)) return; hdr = (struct iapp_hdr *) buf; @@ -387,10 +386,9 @@ struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface) struct iapp_data *iapp; struct ip_mreqn mreq; - iapp = malloc(sizeof(*iapp)); + iapp = wpa_zalloc(sizeof(*iapp)); if (iapp == NULL) return NULL; - memset(iapp, 0, sizeof(*iapp)); iapp->hapd = hapd; iapp->udp_sock = iapp->packet_sock = -1; @@ -459,12 +457,14 @@ struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface) if (setsockopt(iapp->udp_sock, SOL_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)) < 0) { perror("setsockopt[UDP,IP_ADD_MEMBERSHIP]"); + iapp_deinit(iapp); return NULL; } iapp->packet_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); if (iapp->packet_sock < 0) { perror("socket[PF_PACKET,SOCK_RAW]"); + iapp_deinit(iapp); return NULL; } @@ -474,12 +474,14 @@ struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface) if (bind(iapp->packet_sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { perror("bind[PACKET]"); + iapp_deinit(iapp); return NULL; } if (eloop_register_read_sock(iapp->udp_sock, iapp_receive_udp, iapp, NULL)) { printf("Could not register read socket for IAPP.\n"); + iapp_deinit(iapp); return NULL; } @@ -520,3 +522,23 @@ void iapp_deinit(struct iapp_data *iapp) } 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) { + + 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; + } + } + + return 0; +} diff --git a/contrib/hostapd/iapp.h b/contrib/hostapd/iapp.h index d6e8f65..86de592 100644 --- a/contrib/hostapd/iapp.h +++ b/contrib/hostapd/iapp.h @@ -1,3 +1,17 @@ +/* + * hostapd / IEEE 802.11F-2003 Inter-Access Point Protocol (IAPP) + * Copyright (c) 2002-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 IAPP_H #define IAPP_H @@ -8,6 +22,8 @@ struct iapp_data; void iapp_new_station(struct iapp_data *iapp, struct sta_info *sta); struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface); void iapp_deinit(struct iapp_data *iapp); +int iapp_reconfig(struct hostapd_data *hapd, struct hostapd_config *oldconf, + struct hostapd_bss_config *oldbss); #else /* CONFIG_IAPP */ @@ -26,6 +42,13 @@ static inline void iapp_deinit(struct iapp_data *iapp) { } +static inline int +iapp_reconfig(struct hostapd_data *hapd, struct hostapd_config *oldconf, + struct hostapd_bss_config *oldbss) +{ + return 0; +} + #endif /* CONFIG_IAPP */ #endif /* IAPP_H */ diff --git a/contrib/hostapd/ieee802_11.c b/contrib/hostapd/ieee802_11.c index d5ad58c..6dec289 100644 --- a/contrib/hostapd/ieee802_11.c +++ b/contrib/hostapd/ieee802_11.c @@ -1,7 +1,6 @@ /* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / IEEE 802.11 Management - * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * 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 @@ -13,18 +12,17 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <sys/socket.h> +#include "includes.h" + +#ifndef CONFIG_NATIVE_WINDOWS + #include <net/if.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <time.h> #include "eloop.h" #include "hostapd.h" #include "ieee802_11.h" +#include "beacon.h" +#include "hw_features.h" #include "radius.h" #include "radius_client.h" #include "ieee802_11_auth.h" @@ -33,39 +31,206 @@ #include "rc4.h" #include "ieee802_1x.h" #include "wpa.h" +#include "wme.h" +#include "ap_list.h" #include "accounting.h" #include "driver.h" -#include "hostap_common.h" +#include "ieee802_11h.h" +#include "mlme.h" -static u8 * hostapd_eid_supp_rates(hostapd *hapd, u8 *eid) +u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid) { u8 *pos = eid; + int i, num, count; + + if (hapd->iface->current_rates == NULL) + return eid; *pos++ = WLAN_EID_SUPP_RATES; - *pos++ = 4; /* len */ - *pos++ = 0x82; /* 1 Mbps, base set */ - *pos++ = 0x84; /* 2 Mbps, base set */ - *pos++ = 0x0b; /* 5.5 Mbps */ - *pos++ = 0x16; /* 11 Mbps */ + num = hapd->iface->num_rates; + if (num > 8) { + /* rest of the rates are encoded in Extended supported + * rates element */ + num = 8; + } + + *pos++ = num; + count = 0; + for (i = 0, count = 0; i < hapd->iface->num_rates && count < num; + i++) { + count++; + *pos = hapd->iface->current_rates[i].rate / 5; + if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC) + *pos |= 0x80; + pos++; + } + + return pos; +} + + +u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; + int i, num, count; + + if (hapd->iface->current_rates == NULL) + return eid; + + num = hapd->iface->num_rates; + if (num <= 8) + return eid; + num -= 8; + + *pos++ = WLAN_EID_EXT_SUPP_RATES; + *pos++ = num; + count = 0; + for (i = 0, count = 0; i < hapd->iface->num_rates && count < num + 8; + i++) { + count++; + if (count <= 8) + continue; /* already in SuppRates IE */ + *pos = hapd->iface->current_rates[i].rate / 5; + if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC) + *pos |= 0x80; + pos++; + } return pos; } -static u16 hostapd_own_capab_info(hostapd *hapd) +u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, + int probe) { int capab = WLAN_CAPABILITY_ESS; - if (hapd->conf->wpa || - (hapd->conf->ieee802_1x && - (hapd->conf->default_wep_key_len || - hapd->conf->individual_wep_key_len))) + int privacy; + + if (hapd->iface->num_sta_no_short_preamble == 0 && + hapd->iconf->preamble == SHORT_PREAMBLE) + capab |= WLAN_CAPABILITY_SHORT_PREAMBLE; + + privacy = hapd->conf->ssid.wep.keys_set; + + if (hapd->conf->ieee802_1x && + (hapd->conf->default_wep_key_len || + hapd->conf->individual_wep_key_len)) + privacy = 1; + + if (hapd->conf->wpa) + privacy = 1; + + if (sta) { + int policy, def_klen; + if (probe && sta->ssid_probe) { + policy = sta->ssid_probe->security_policy; + def_klen = sta->ssid_probe->wep.default_len; + } else { + policy = sta->ssid->security_policy; + def_klen = sta->ssid->wep.default_len; + } + privacy = policy != SECURITY_PLAINTEXT; + if (policy == SECURITY_IEEE_802_1X && def_klen == 0) + privacy = 0; + } + + if (privacy) capab |= WLAN_CAPABILITY_PRIVACY; + + if (hapd->iface && 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; } -static const u8 WPA_OUI_TYPE[] = { 0x00, 0x50, 0xf2, 1 }; +#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, @@ -92,8 +257,7 @@ ParseRes ieee802_11_parse_elems(struct hostapd_data *hapd, u8 *start, "failed (id=%d elen=%d " "left=%lu)\n", id, elen, (unsigned long) left); - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) - hostapd_hexdump("IEs", start, len); + wpa_hexdump(MSG_MSGDUMP, "IEs", start, len); } return ParseFailed; } @@ -131,30 +295,32 @@ ParseRes ieee802_11_parse_elems(struct hostapd_data *hapd, u8 *start, elems->challenge = pos; elems->challenge_len = elen; break; - case WLAN_EID_GENERIC: - if (elen > 4 && memcmp(pos, WPA_OUI_TYPE, 4) == 0) { - elems->wpa_ie = pos; - elems->wpa_ie_len = elen; - } else if (show_errors) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_EXCESSIVE, - "IEEE 802.11 element parse " - "ignored unknown generic element" - " (id=%d elen=%d OUI:type=" - "%02x-%02x-%02x:%d)\n", - id, elen, - elen >= 1 ? pos[0] : 0, - elen >= 2 ? pos[1] : 0, - elen >= 3 ? pos[2] : 0, - elen >= 4 ? pos[3] : 0); - unknown++; - } else { + 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) @@ -177,7 +343,7 @@ ParseRes ieee802_11_parse_elems(struct hostapd_data *hapd, u8 *start, } -static void ieee802_11_print_ssid(const u8 *ssid, u8 len) +void ieee802_11_print_ssid(const u8 *ssid, u8 len) { int i; for (i = 0; i < len; i++) { @@ -189,9 +355,31 @@ static void ieee802_11_print_ssid(const u8 *ssid, u8 len) } +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)); + 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); + mgmt.u.deauth.reason_code = host_to_le16(reason); + if (hostapd_send_mgmt_frame(hapd, &mgmt, IEEE80211_HDRLEN + + sizeof(mgmt.u.deauth), 0) < 0) + perror("ieee802_11_send_deauth: send"); +} + + static void ieee802_11_sta_authenticate(void *eloop_ctx, void *timeout_ctx) { - hostapd *hapd = eloop_ctx; + struct hostapd_data *hapd = eloop_ctx; struct ieee80211_mgmt mgmt; if (hapd->assoc_ap_state == WAIT_BEACON) @@ -227,7 +415,7 @@ static void ieee802_11_sta_authenticate(void *eloop_ctx, void *timeout_ctx) static void ieee802_11_sta_associate(void *eloop_ctx, void *timeout_ctx) { - hostapd *hapd = eloop_ctx; + struct hostapd_data *hapd = eloop_ctx; u8 buf[256]; struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) buf; u8 *p; @@ -261,6 +449,7 @@ static void ieee802_11_sta_associate(void *eloop_ctx, void *timeout_ctx) 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"); @@ -270,7 +459,7 @@ static void ieee802_11_sta_associate(void *eloop_ctx, void *timeout_ctx) } -static u16 auth_shared_key(hostapd *hapd, struct sta_info *sta, +static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta, u16 auth_transaction, u8 *challenge, int iswep) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, @@ -284,10 +473,9 @@ static u16 auth_shared_key(hostapd *hapd, struct sta_info *sta, u8 key[8]; time_t now; int r; - sta->challenge = malloc(WLAN_AUTH_CHALLENGE_LEN); - if (!sta->challenge) + sta->challenge = wpa_zalloc(WLAN_AUTH_CHALLENGE_LEN); + if (sta->challenge == NULL) return WLAN_STATUS_UNSPECIFIED_FAILURE; - memset(sta->challenge, 0, WLAN_AUTH_CHALLENGE_LEN); now = time(NULL); r = random(); @@ -320,7 +508,7 @@ static u16 auth_shared_key(hostapd *hapd, struct sta_info *sta, * authentication reply. */ #else sta->flags |= WLAN_STA_AUTH; - wpa_sm_event(hapd, sta, WPA_AUTH); + wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); #endif free(sta->challenge); sta->challenge = NULL; @@ -329,7 +517,8 @@ static u16 auth_shared_key(hostapd *hapd, struct sta_info *sta, } -static void send_auth_reply(hostapd *hapd, struct ieee80211_mgmt *mgmt, +static void send_auth_reply(struct hostapd_data *hapd, + struct ieee80211_mgmt *mgmt, u16 auth_alg, u16 auth_transaction, u16 resp, u8 *challenge) { @@ -372,7 +561,8 @@ static void send_auth_reply(hostapd *hapd, struct ieee80211_mgmt *mgmt, } -static void handle_auth(hostapd *hapd, struct ieee80211_mgmt *mgmt, size_t len) +static void handle_auth(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, + size_t len) { u16 auth_alg, auth_transaction, status_code; u16 resp = WLAN_STATUS_SUCCESS; @@ -381,6 +571,7 @@ static void handle_auth(hostapd *hapd, struct ieee80211_mgmt *mgmt, size_t len) u16 fc; u8 *challenge = NULL; u32 session_timeout, acct_interim_interval; + int vlan_id = 0; if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { printf("handle_auth - too short payload (len=%lu)\n", @@ -454,7 +645,7 @@ static void handle_auth(hostapd *hapd, struct ieee80211_mgmt *mgmt, size_t len) res = hostapd_allowed_address(hapd, mgmt->sa, (u8 *) mgmt, len, &session_timeout, - &acct_interim_interval); + &acct_interim_interval, &vlan_id); if (res == HOSTAPD_ACL_REJECT) { printf("Station " MACSTR " not allowed to authenticate.\n", MAC2STR(mgmt->sa)); @@ -476,6 +667,22 @@ static void handle_auth(hostapd *hapd, struct ieee80211_mgmt *mgmt, size_t len) resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } + + if (vlan_id > 0) { + if (hostapd_get_vlan_id_ifname(hapd->conf->vlan, + sta->vlan_id) == NULL) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, "Invalid VLAN ID " + "%d received from RADIUS server", + vlan_id); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + sta->vlan_id = vlan_id; + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id); + } + sta->flags &= ~WLAN_STA_PREAUTH; ieee802_1x_notify_pre_auth(sta->eapol_sm, 0); @@ -497,12 +704,16 @@ static void handle_auth(hostapd *hapd, struct ieee80211_mgmt *mgmt, size_t len) * authentication reply. */ #else sta->flags |= WLAN_STA_AUTH; - wpa_sm_event(hapd, sta, WPA_AUTH); + wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); + sta->auth_alg = WLAN_AUTH_OPEN; + mlme_authenticate_indication(hapd, sta); #endif break; case WLAN_AUTH_SHARED_KEY: resp = auth_shared_key(hapd, sta, auth_transaction, challenge, fc & WLAN_FC_ISWEP); + sta->auth_alg = WLAN_AUTH_SHARED_KEY; + mlme_authenticate_indication(hapd, sta); break; } @@ -512,8 +723,8 @@ static void handle_auth(hostapd *hapd, struct ieee80211_mgmt *mgmt, size_t len) } -static void handle_assoc(hostapd *hapd, struct ieee80211_mgmt *mgmt, - size_t len, int reassoc) +static void handle_assoc(struct hostapd_data *hapd, + struct ieee80211_mgmt *mgmt, size_t len, int reassoc) { u16 capab_info, listen_interval; u16 resp = WLAN_STATUS_SUCCESS; @@ -572,6 +783,11 @@ static void handle_assoc(hostapd *hapd, struct ieee80211_mgmt *mgmt, goto fail; } + if (reassoc) { + memcpy(sta->previous_ap, mgmt->u.reassoc_req.current_ap, + ETH_ALEN); + } + sta->capability = capab_info; /* followed by SSID and Supported rates */ @@ -583,8 +799,8 @@ static void handle_assoc(hostapd *hapd, struct ieee80211_mgmt *mgmt, goto fail; } - if (elems.ssid_len != hapd->conf->ssid_len || - memcmp(elems.ssid, hapd->conf->ssid, elems.ssid_len) != 0) { + if (elems.ssid_len != hapd->conf->ssid.ssid_len || + memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) != 0) { printf("Station " MACSTR " tried to associate with " "unknown SSID '", MAC2STR(sta->addr)); ieee802_11_print_ssid(elems.ssid, elems.ssid_len); @@ -593,32 +809,56 @@ static void handle_assoc(hostapd *hapd, struct ieee80211_mgmt *mgmt, goto fail; } - if (elems.supp_rates) { - if (elems.supp_rates_len > sizeof(sta->supported_rates)) { - printf("STA " MACSTR ": Invalid supported rates " - "element length %d\n", MAC2STR(sta->addr), + sta->flags &= ~WLAN_STA_WME; + if (elems.wme && hapd->conf->wme_enabled) { + if (hostapd_eid_wme_valid(hapd, elems.wme, elems.wme_len)) + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, + "invalid WME element in association " + "request"); + else + sta->flags |= WLAN_STA_WME; + } + + if (!elems.supp_rates) { + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "No supported rates element in AssocReq"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + if (elems.supp_rates_len > sizeof(sta->supported_rates)) { + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "Invalid supported rates element length %d", elems.supp_rates_len); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + memset(sta->supported_rates, 0, sizeof(sta->supported_rates)); + memcpy(sta->supported_rates, elems.supp_rates, elems.supp_rates_len); + sta->supported_rates_len = elems.supp_rates_len; + + if (elems.ext_supp_rates) { + if (elems.supp_rates_len + elems.ext_supp_rates_len > + sizeof(sta->supported_rates)) { + hostapd_logger(hapd, mgmt->sa, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "Invalid supported rates element length" + " %d+%d", elems.supp_rates_len, + elems.ext_supp_rates_len); resp = WLAN_STATUS_UNSPECIFIED_FAILURE; goto fail; } - memset(sta->supported_rates, 0, sizeof(sta->supported_rates)); - memcpy(sta->supported_rates, elems.supp_rates, - elems.supp_rates_len); - - sta->tx_supp_rates = 0; - for (i = 0; i < elems.supp_rates_len; i++) { - if ((sta->supported_rates[i] & 0x7f) == 2) - sta->tx_supp_rates |= WLAN_RATE_1M; - if ((sta->supported_rates[i] & 0x7f) == 4) - sta->tx_supp_rates |= WLAN_RATE_2M; - if ((sta->supported_rates[i] & 0x7f) == 11) - sta->tx_supp_rates |= WLAN_RATE_5M5; - if ((sta->supported_rates[i] & 0x7f) == 22) - sta->tx_supp_rates |= WLAN_RATE_11M; - } - } else - sta->tx_supp_rates = 0xff; + 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) { wpa_ie = elems.rsn_ie; @@ -642,28 +882,87 @@ static void handle_assoc(hostapd *hapd, struct ieee80211_mgmt *mgmt, int res; wpa_ie -= 2; wpa_ie_len += 2; - res = wpa_validate_wpa_ie(hapd, sta, wpa_ie, wpa_ie_len, - elems.rsn_ie ? - HOSTAPD_WPA_VERSION_WPA2 : - HOSTAPD_WPA_VERSION_WPA); + 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"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, + wpa_ie, wpa_ie_len); if (res == WPA_INVALID_GROUP) resp = WLAN_STATUS_GROUP_CIPHER_NOT_VALID; else if (res == WPA_INVALID_PAIRWISE) resp = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; else if (res == WPA_INVALID_AKMP) resp = WLAN_STATUS_AKMP_NOT_VALID; + else if (res == WPA_ALLOC_FAIL) + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; +#ifdef CONFIG_IEEE80211W + else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION) + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; /* FIX */ + else if (res == WPA_INVALID_MGMT_GROUP_CIPHER) + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; /* FIX */ +#endif /* CONFIG_IEEE80211W */ else if (res != WPA_IE_OK) resp = WLAN_STATUS_INVALID_IE; if (resp != WLAN_STATUS_SUCCESS) goto fail; - free(sta->wpa_ie); - sta->wpa_ie = malloc(wpa_ie_len); - if (sta->wpa_ie == NULL) { - resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + 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"); goto fail; } - sta->wpa_ie_len = wpa_ie_len; - memcpy(sta->wpa_ie, wpa_ie, wpa_ie_len); + } + + if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) + sta->flags |= WLAN_STA_NONERP; + for (i = 0; i < sta->supported_rates_len; i++) { + if ((sta->supported_rates[i] & 0x7f) > 22) { + sta->flags &= ~WLAN_STA_NONERP; + break; + } + } + if (sta->flags & WLAN_STA_NONERP && !sta->nonerp_set) { + sta->nonerp_set = 1; + hapd->iface->num_sta_non_erp++; + if (hapd->iface->num_sta_non_erp == 1) + ieee802_11_set_beacons(hapd->iface); + } + + if (!(sta->capability & WLAN_CAPABILITY_SHORT_SLOT_TIME) && + !sta->no_short_slot_time_set) { + sta->no_short_slot_time_set = 1; + hapd->iface->num_sta_no_short_slot_time++; + if (hapd->iface->current_mode->mode == + HOSTAPD_MODE_IEEE80211G && + hapd->iface->num_sta_no_short_slot_time == 1) + ieee802_11_set_beacons(hapd->iface); + } + + if (sta->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) + sta->flags |= WLAN_STA_SHORT_PREAMBLE; + else + sta->flags &= ~WLAN_STA_SHORT_PREAMBLE; + + if (!(sta->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) && + !sta->no_short_preamble_set) { + sta->no_short_preamble_set = 1; + hapd->iface->num_sta_no_short_preamble++; + if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G + && hapd->iface->num_sta_no_short_preamble == 1) + ieee802_11_set_beacons(hapd->iface); } /* get a unique AID */ @@ -723,12 +1022,16 @@ static void handle_assoc(hostapd *hapd, struct ieee80211_mgmt *mgmt, u8 *p; send_len += sizeof(mgmt->u.assoc_resp); mgmt->u.assoc_resp.capab_info = - host_to_le16(hostapd_own_capab_info(hapd)); + 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)); /* Supported rates */ p = hostapd_eid_supp_rates(hapd, mgmt->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 */ @@ -740,8 +1043,8 @@ static void handle_assoc(hostapd *hapd, struct ieee80211_mgmt *mgmt, } -static void handle_assoc_resp(hostapd *hapd, struct ieee80211_mgmt *mgmt, - size_t len) +static void handle_assoc_resp(struct hostapd_data *hapd, + struct ieee80211_mgmt *mgmt, size_t len) { u16 status_code, aid; @@ -791,8 +1094,8 @@ static void handle_assoc_resp(hostapd *hapd, struct ieee80211_mgmt *mgmt, } -static void handle_disassoc(hostapd *hapd, struct ieee80211_mgmt *mgmt, - size_t len) +static void handle_disassoc(struct hostapd_data *hapd, + struct ieee80211_mgmt *mgmt, size_t len) { struct sta_info *sta; @@ -828,11 +1131,11 @@ static void handle_disassoc(hostapd *hapd, struct ieee80211_mgmt *mgmt, } sta->flags &= ~WLAN_STA_ASSOC; - wpa_sm_event(hapd, sta, WPA_DISASSOC); + wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "disassociated"); sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; - ieee802_1x_set_port_enabled(hapd, sta, 0); + ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); /* Stop Accounting and IEEE 802.1X sessions, but leave the STA * authenticated. */ accounting_sta_stop(hapd, sta); @@ -846,11 +1149,14 @@ static void handle_disassoc(hostapd *hapd, struct ieee80211_mgmt *mgmt, eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer, hapd, sta); } + + mlme_disassociate_indication( + hapd, sta, le_to_host16(mgmt->u.disassoc.reason_code)); } -static void handle_deauth(hostapd *hapd, struct ieee80211_mgmt *mgmt, - size_t len) +static void handle_deauth(struct hostapd_data *hapd, + struct ieee80211_mgmt *mgmt, size_t len) { struct sta_info *sta; @@ -885,17 +1191,20 @@ static void handle_deauth(hostapd *hapd, struct ieee80211_mgmt *mgmt, } sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); - wpa_sm_event(hapd, sta, WPA_DEAUTH); + wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_DEBUG, "deauthenticated"); + mlme_deauthenticate_indication( + hapd, sta, le_to_host16(mgmt->u.deauth.reason_code)); sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; - ieee802_1x_set_port_enabled(hapd, sta, 0); + ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); ap_free_sta(hapd, sta); } -static void handle_beacon(hostapd *hapd, struct ieee80211_mgmt *mgmt, - size_t len) +static void handle_beacon(struct hostapd_data *hapd, + struct ieee80211_mgmt *mgmt, size_t len, + struct hostapd_frame_info *fi) { struct ieee802_11_elems elems; @@ -921,6 +1230,8 @@ static void handle_beacon(hostapd *hapd, struct ieee80211_mgmt *mgmt, ieee802_11_sta_authenticate(hapd, NULL); } + ap_list_process_beacon(hapd->iface, mgmt, &elems, fi); + if (!HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_EXCESSIVE)) return; @@ -936,17 +1247,79 @@ static void handle_beacon(hostapd *hapd, struct ieee80211_mgmt *mgmt, } -void ieee802_11_mgmt(struct hostapd_data *hapd, u8 *buf, size_t len, u16 stype) +static void handle_action(struct hostapd_data *hapd, + struct ieee80211_mgmt *mgmt, size_t len) +{ + if (len < IEEE80211_HDRLEN + 1) { + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "handle_action - too short payload (len=%lu)", + (unsigned long) len); + return; + } + + switch (mgmt->u.action.category) { + case WME_ACTION_CATEGORY: + hostapd_wme_action(hapd, mgmt, len); + return; + } + + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "handle_action - unknown action category %d", + mgmt->u.action.category); + if (!(mgmt->da[0] & 0x01) && !(mgmt->u.action.category & 0x80) && + !(mgmt->sa[0] & 0x01)) { + /* + * IEEE 802.11-REVma/D9.0 - 7.3.1.11 + * Return the Action frame to the source without change + * except that MSB of the Category set to 1. + */ + wpa_printf(MSG_DEBUG, "IEEE 802.11: Return unknown Action " + "frame back to sender"); + os_memcpy(mgmt->da, mgmt->sa, ETH_ALEN); + os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN); + mgmt->u.action.category |= 0x80; + + hostapd_send_mgmt_frame(hapd, mgmt, len, 0); + } +} + + +/** + * ieee802_11_mgmt - process incoming IEEE 802.11 management frames + * @hapd: hostapd BSS data structure (the BSS to which the management frame was + * sent to) + * @buf: management frame data (starting from IEEE 802.11 header) + * @len: length of frame data in octets + * @stype: management frame subtype from frame control field + * + * Process all incoming IEEE 802.11 management frames. This will be called for + * each frame received from the kernel driver through wlan#ap interface. In + * addition, it can be called to re-inserted pending frames (e.g., when using + * external RADIUS server as an MAC ACL). + */ +void ieee802_11_mgmt(struct hostapd_data *hapd, u8 *buf, size_t len, u16 stype, + struct hostapd_frame_info *fi) { struct ieee80211_mgmt *mgmt = (struct ieee80211_mgmt *) buf; + int broadcast; if (stype == WLAN_FC_STYPE_BEACON) { HOSTAPD_DEBUG(HOSTAPD_DEBUG_EXCESSIVE, "mgmt::beacon\n"); - handle_beacon(hapd, mgmt, len); + handle_beacon(hapd, mgmt, len, fi); return; } - if (memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0 && + if (fi && fi->passive_scan) + return; + + broadcast = mgmt->bssid[0] == 0xff && mgmt->bssid[1] == 0xff && + mgmt->bssid[2] == 0xff && mgmt->bssid[3] == 0xff && + mgmt->bssid[4] == 0xff && mgmt->bssid[5] == 0xff; + + if (!broadcast && 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)) { printf("MGMT: BSSID=" MACSTR " not our address\n", @@ -956,13 +1329,16 @@ void ieee802_11_mgmt(struct hostapd_data *hapd, u8 *buf, size_t len, u16 stype) if (stype == WLAN_FC_STYPE_PROBE_REQ) { - printf("mgmt::probe_req\n"); + 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) { - printf("MGMT: DA=" MACSTR " not our address\n", - MAC2STR(mgmt->da)); + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "MGMT: DA=" MACSTR " not our address", + MAC2STR(mgmt->da)); return; } @@ -991,14 +1367,21 @@ void ieee802_11_mgmt(struct hostapd_data *hapd, u8 *buf, size_t len, u16 stype) HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "mgmt::deauth\n"); handle_deauth(hapd, mgmt, len); break; + case WLAN_FC_STYPE_ACTION: + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "mgmt::action\n"); + handle_action(hapd, mgmt, len); + break; default: - printf("unknown mgmt frame subtype %d\n", stype); + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "unknown mgmt frame subtype %d", stype); break; } } -static void handle_auth_cb(hostapd *hapd, struct ieee80211_mgmt *mgmt, +static void handle_auth_cb(struct hostapd_data *hapd, + struct ieee80211_mgmt *mgmt, size_t len, int ok) { u16 auth_alg, auth_transaction, status_code; @@ -1038,7 +1421,8 @@ static void handle_auth_cb(hostapd *hapd, struct ieee80211_mgmt *mgmt, } -static void handle_assoc_cb(hostapd *hapd, struct ieee80211_mgmt *mgmt, +static void handle_assoc_cb(struct hostapd_data *hapd, + struct ieee80211_mgmt *mgmt, size_t len, int reassoc, int ok) { u16 status; @@ -1089,12 +1473,39 @@ static void handle_assoc_cb(hostapd *hapd, struct ieee80211_mgmt *mgmt, new_assoc = 0; sta->flags |= WLAN_STA_ASSOC; - if (hostapd_sta_add(hapd, sta->addr, sta->aid, sta->capability, - sta->tx_supp_rates)) { - printf("Could not add station to kernel driver.\n"); + if (reassoc) + mlme_reassociate_indication(hapd, sta); + else + mlme_associate_indication(hapd, sta); + + if (hostapd_sta_add(hapd->conf->iface, hapd, sta->addr, sta->aid, + sta->capability, sta->supported_rates, + sta->supported_rates_len, 0)) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_NOTICE, + "Could not add STA to kernel driver"); } - wpa_sm_event(hapd, sta, WPA_ASSOC); + if (sta->eapol_sm == NULL) { + /* + * This STA does not use RADIUS server for EAP authentication, + * so bind it to the selected VLAN interface now, since the + * interface selection is not going to change anymore. + */ + ap_sta_bind_vlan(hapd, sta, 0); + } else if (sta->vlan_id) { + /* VLAN ID already set (e.g., by PMKSA caching), so bind STA */ + ap_sta_bind_vlan(hapd, sta, 0); + } + if (sta->flags & WLAN_STA_SHORT_PREAMBLE) { + hostapd_sta_set_flags(hapd, sta->addr, + WLAN_STA_SHORT_PREAMBLE, ~0); + } else { + hostapd_sta_set_flags(hapd, sta->addr, + 0, ~WLAN_STA_SHORT_PREAMBLE); + } + + 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); @@ -1128,6 +1539,9 @@ void ieee802_11_mgmt_cb(struct hostapd_data *hapd, u8 *buf, size_t len, "mgmt::reassoc_resp cb\n"); handle_assoc_cb(hapd, mgmt, len, 1, ok); break; + case WLAN_FC_STYPE_PROBE_RESP: + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "mgmt::proberesp cb\n"); + break; default: printf("unknown mgmt cb frame subtype %d\n", stype); break; @@ -1153,11 +1567,10 @@ static void ieee80211_tkip_countermeasures_start(struct hostapd_data *hapd) hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "TKIP countermeasures initiated"); - if (hapd->wpa_auth) - hapd->wpa_auth->dot11RSNATKIPCounterMeasuresInvoked++; + wpa_auth_countermeasures_start(hapd->wpa_auth); hapd->tkip_countermeasures = 1; hostapd_set_countermeasures(hapd, 1); - wpa_gtk_rekey(hapd); + wpa_gtk_rekey(hapd->wpa_auth); eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL); eloop_register_timeout(60, 0, ieee80211_tkip_countermeasures_stop, hapd, NULL); @@ -1171,7 +1584,7 @@ static void ieee80211_tkip_countermeasures_start(struct hostapd_data *hapd) } -void ieee80211_michael_mic_failure(struct hostapd_data *hapd, u8 *addr, +void ieee80211_michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local) { time_t now; @@ -1179,11 +1592,12 @@ void ieee80211_michael_mic_failure(struct hostapd_data *hapd, u8 *addr, if (addr && local) { struct sta_info *sta = ap_get_sta(hapd, addr); if (sta != NULL) { - sta->dot11RSNAStatsTKIPLocalMICFailures++; + wpa_auth_sta_local_mic_failure_report(sta->wpa_sm); hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "Michael MIC failure detected in " "received frame"); + mlme_michaelmicfailure_indication(hapd, addr); } else { HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "MLME-MICHAELMICFAILURE.indication " @@ -1218,3 +1632,5 @@ int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, /* TODO */ return 0; } + +#endif /* CONFIG_NATIVE_WINDOWS */ diff --git a/contrib/hostapd/ieee802_11.h b/contrib/hostapd/ieee802_11.h index 39cc342..37bd711 100644 --- a/contrib/hostapd/ieee802_11.h +++ b/contrib/hostapd/ieee802_11.h @@ -1,6 +1,177 @@ +/* + * 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; @@ -44,6 +215,18 @@ struct ieee80211_mgmt { 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; @@ -51,10 +234,33 @@ struct ieee80211_mgmt { * 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; @@ -73,27 +279,55 @@ struct ieee802_11_elems { 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); + 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 ieee80211_michael_mic_failure(struct hostapd_data *hapd, u8 *addr, +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/ieee802_11_auth.c b/contrib/hostapd/ieee802_11_auth.c index 296c640..16a8517 100644 --- a/contrib/hostapd/ieee802_11_auth.c +++ b/contrib/hostapd/ieee802_11_auth.c @@ -1,7 +1,6 @@ /* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / IEEE 802.11 authentication (ACL) - * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * hostapd / IEEE 802.11 authentication (ACL) + * 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 @@ -13,14 +12,9 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <time.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <sys/types.h> -#include <sys/socket.h> +#include "includes.h" + +#ifndef CONFIG_NATIVE_WINDOWS #include "hostapd.h" #include "ieee802_11.h" @@ -28,7 +22,6 @@ #include "radius.h" #include "radius_client.h" #include "eloop.h" -#include "hostap_common.h" #define RADIUS_ACL_TIMEOUT 30 @@ -40,6 +33,7 @@ struct hostapd_cached_radius_acl { struct hostapd_cached_radius_acl *next; u32 session_timeout; u32 acct_interim_interval; + int vlan_id; }; @@ -65,9 +59,9 @@ static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache) } -static int hostapd_acl_cache_get(struct hostapd_data *hapd, u8 *addr, +static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr, u32 *session_timeout, - u32 *acct_interim_interval) + u32 *acct_interim_interval, int *vlan_id) { struct hostapd_cached_radius_acl *entry; time_t now; @@ -82,6 +76,8 @@ static int hostapd_acl_cache_get(struct hostapd_data *hapd, u8 *addr, if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT) *session_timeout = entry->session_timeout; *acct_interim_interval = entry->acct_interim_interval; + if (vlan_id) + *vlan_id = entry->vlan_id; return entry->accepted; } @@ -101,7 +97,7 @@ static void hostapd_acl_query_free(struct hostapd_acl_query_data *query) } -static int hostapd_radius_acl_query(hostapd *hapd, u8 *addr, +static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr, struct hostapd_acl_query_data *query) { struct radius_msg *msg; @@ -154,7 +150,7 @@ static int hostapd_radius_acl_query(hostapd *hapd, u8 *addr, } snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", - MAC2STR(hapd->own_addr), hapd->conf->ssid); + 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"); @@ -192,11 +188,14 @@ static int hostapd_radius_acl_query(hostapd *hapd, u8 *addr, } -int hostapd_allowed_address(hostapd *hapd, u8 *addr, u8 *msg, size_t len, - u32 *session_timeout, u32 *acct_interim_interval) +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) { *session_timeout = 0; *acct_interim_interval = 0; + if (vlan_id) + *vlan_id = 0; if (hostapd_maclist_found(hapd->conf->accept_mac, hapd->conf->num_accept_mac, addr)) @@ -216,7 +215,8 @@ int hostapd_allowed_address(hostapd *hapd, u8 *addr, u8 *msg, size_t len, /* Check whether ACL cache has an entry for this station */ int res = hostapd_acl_cache_get(hapd, addr, session_timeout, - acct_interim_interval); + acct_interim_interval, + vlan_id); if (res == HOSTAPD_ACL_ACCEPT || res == HOSTAPD_ACL_ACCEPT_TIMEOUT) return res; @@ -237,12 +237,11 @@ int hostapd_allowed_address(hostapd *hapd, u8 *addr, u8 *msg, size_t len, return HOSTAPD_ACL_REJECT; /* No entry in the cache - query external RADIUS server */ - query = malloc(sizeof(*query)); + query = wpa_zalloc(sizeof(*query)); if (query == NULL) { printf("malloc for query data failed\n"); return HOSTAPD_ACL_REJECT; } - memset(query, 0, sizeof(*query)); time(&query->timestamp); memcpy(query->addr, addr, ETH_ALEN); if (hostapd_radius_acl_query(hapd, addr, query)) { @@ -272,7 +271,7 @@ int hostapd_allowed_address(hostapd *hapd, u8 *addr, u8 *msg, size_t len, } -static void hostapd_acl_expire_cache(hostapd *hapd, time_t now) +static void hostapd_acl_expire_cache(struct hostapd_data *hapd, time_t now) { struct hostapd_cached_radius_acl *prev, *entry, *tmp; @@ -301,7 +300,7 @@ static void hostapd_acl_expire_cache(hostapd *hapd, time_t now) } -static void hostapd_acl_expire_queries(hostapd *hapd, time_t now) +static void hostapd_acl_expire_queries(struct hostapd_data *hapd, time_t now) { struct hostapd_acl_query_data *prev, *entry, *tmp; @@ -332,7 +331,7 @@ static void hostapd_acl_expire_queries(hostapd *hapd, time_t now) static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx) { - hostapd *hapd = eloop_ctx; + struct hostapd_data *hapd = eloop_ctx; time_t now; time(&now); @@ -382,12 +381,11 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, } /* Insert Accept/Reject info into ACL cache */ - cache = malloc(sizeof(*cache)); + cache = wpa_zalloc(sizeof(*cache)); if (cache == NULL) { printf("Failed to add ACL cache entry\n"); goto done; } - memset(cache, 0, sizeof(*cache)); time(&cache->timestamp); memcpy(cache->addr, query->addr, sizeof(cache->addr)); if (msg->hdr->code == RADIUS_CODE_ACCESS_ACCEPT) { @@ -408,6 +406,8 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, MAC2STR(query->addr)); cache->acct_interim_interval = 0; } + + cache->vlan_id = radius_msg_get_vlanid(msg); } else cache->accepted = HOSTAPD_ACL_REJECT; cache->next = hapd->acl_cache; @@ -417,7 +417,7 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Re-sending authentication frame " "after successful RADIUS ACL query\n"); ieee802_11_mgmt(hapd, query->auth_msg, query->auth_msg_len, - WLAN_FC_STYPE_AUTH); + WLAN_FC_STYPE_AUTH, NULL); done: if (prev == NULL) @@ -431,7 +431,7 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, } -int hostapd_acl_init(hostapd *hapd) +int hostapd_acl_init(struct hostapd_data *hapd) { if (radius_client_register(hapd->radius, RADIUS_AUTH, hostapd_acl_recv_radius, hapd)) @@ -443,10 +443,12 @@ int hostapd_acl_init(hostapd *hapd) } -void hostapd_acl_deinit(hostapd *hapd) +void hostapd_acl_deinit(struct hostapd_data *hapd) { struct hostapd_acl_query_data *query, *prev; + eloop_cancel_timeout(hostapd_acl_expire, hapd, NULL); + hostapd_acl_cache_free(hapd->acl_cache); query = hapd->acl_queries; @@ -456,3 +458,16 @@ void hostapd_acl_deinit(hostapd *hapd) hostapd_acl_query_free(prev); } } + + +int hostapd_acl_reconfig(struct hostapd_data *hapd, + struct hostapd_config *oldconf) +{ + if (!hapd->radius_client_reconfigured) + return 0; + + hostapd_acl_deinit(hapd); + return hostapd_acl_init(hapd); +} + +#endif /* CONFIG_NATIVE_WINDOWS */ diff --git a/contrib/hostapd/ieee802_11_auth.h b/contrib/hostapd/ieee802_11_auth.h index 90adc8f..0eed825 100644 --- a/contrib/hostapd/ieee802_11_auth.h +++ b/contrib/hostapd/ieee802_11_auth.h @@ -1,3 +1,17 @@ +/* + * hostapd / IEEE 802.11 authentication (ACL) + * 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 IEEE802_11_AUTH_H #define IEEE802_11_AUTH_H @@ -8,9 +22,12 @@ enum { HOSTAPD_ACL_ACCEPT_TIMEOUT = 3 }; -int hostapd_allowed_address(hostapd *hapd, u8 *addr, u8 *msg, size_t len, - u32 *session_timeout, u32 *acct_interim_interval); -int hostapd_acl_init(hostapd *hapd); -void hostapd_acl_deinit(hostapd *hapd); +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); +int hostapd_acl_init(struct hostapd_data *hapd); +void hostapd_acl_deinit(struct hostapd_data *hapd); +int hostapd_acl_reconfig(struct hostapd_data *hapd, + struct hostapd_config *oldconf); #endif /* IEEE802_11_AUTH_H */ diff --git a/contrib/hostapd/ieee802_11h.c b/contrib/hostapd/ieee802_11h.c new file mode 100644 index 0000000..215e377 --- /dev/null +++ b/contrib/hostapd/ieee802_11h.c @@ -0,0 +1,34 @@ +/* + * hostapd / IEEE 802.11h + * Copyright (c) 2005-2006, Devicescape Software, Inc. + * 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 "hostapd.h" + + +int hostapd_check_power_cap(struct hostapd_data *hapd, u8 *power, u8 len) +{ + unsigned int max_pwr; + + 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; +} diff --git a/contrib/hostapd/ieee802_11h.h b/contrib/hostapd/ieee802_11h.h new file mode 100644 index 0000000..b2bd549 --- /dev/null +++ b/contrib/hostapd/ieee802_11h.h @@ -0,0 +1,27 @@ +/* + * hostapd / IEEE 802.11h + * Copyright (c) 2005-2006, Devicescape Software, Inc. + * 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 IEEE802_11H_H +#define IEEE802_11H_H + +#define SPECT_LOOSE_BINDING 1 +#define SPECT_STRICT_BINDING 2 + +#define CHAN_SWITCH_MODE_NOISY 0 +#define CHAN_SWITCH_MODE_QUIET 1 + +int hostapd_check_power_cap(struct hostapd_data *hapd, u8 *power, u8 len); + +#endif /* IEEE802_11H_H */ diff --git a/contrib/hostapd/ieee802_1x.c b/contrib/hostapd/ieee802_1x.c index f3fc311..a7c7264 100644 --- a/contrib/hostapd/ieee802_1x.c +++ b/contrib/hostapd/ieee802_1x.c @@ -1,7 +1,6 @@ /* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / IEEE 802.1X Authenticator - * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * hostapd / IEEE 802.1X Authenticator + * 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 @@ -13,17 +12,8 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <netinet/in.h> -#include <string.h> -#include <sys/ioctl.h> -#include <signal.h> +#include "includes.h" #include <assert.h> -#include <time.h> -#include <sys/time.h> -#include <sys/socket.h> #include "hostapd.h" #include "ieee802_1x.h" @@ -36,7 +26,10 @@ #include "eloop.h" #include "sta_info.h" #include "wpa.h" +#include "preauth.h" +#include "pmksa_cache.h" #include "driver.h" +#include "hw_features.h" #include "eap.h" @@ -44,8 +37,8 @@ static void ieee802_1x_new_auth_session(struct hostapd_data *hapd, struct sta_info *sta); -static void ieee802_1x_send(hostapd *hapd, struct sta_info *sta, u8 type, - u8 *data, size_t datalen) +static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta, + u8 type, u8 *data, size_t datalen) { u8 *buf; struct ieee802_1x_hdr *xhdr; @@ -53,14 +46,13 @@ static void ieee802_1x_send(hostapd *hapd, struct sta_info *sta, u8 type, int encrypt = 0; len = sizeof(*xhdr) + datalen; - buf = malloc(len); + buf = wpa_zalloc(len); if (buf == NULL) { printf("malloc() failed for ieee802_1x_send(len=%lu)\n", (unsigned long) len); return; } - memset(buf, 0, len); #if 0 /* TODO: * According to IEEE 802.1aa/D4 EAPOL-Key should be sent before any @@ -81,7 +73,7 @@ static void ieee802_1x_send(hostapd *hapd, struct sta_info *sta, u8 type, if (datalen > 0 && data != NULL) memcpy(xhdr + 1, data, datalen); - if (sta->wpa_sm && sta->wpa_sm->pairwise_set) + if (wpa_auth_pairwise_set(sta->wpa_sm)) encrypt = 1; if (sta->flags & WLAN_STA_PREAUTH) { rsn_preauth_send(hapd, sta, buf, len); @@ -93,23 +85,33 @@ static void ieee802_1x_send(hostapd *hapd, struct sta_info *sta, u8 type, } -void ieee802_1x_set_sta_authorized(hostapd *hapd, struct sta_info *sta, - int authorized) +void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd, + struct sta_info *sta, int authorized) { + int res; + if (sta->flags & WLAN_STA_PREAUTH) return; if (authorized) { sta->flags |= WLAN_STA_AUTHORIZED; + res = hostapd_sta_set_flags(hapd, sta->addr, + 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, + 0, ~WLAN_STA_AUTHORIZED); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "unauthorizing port"); } - hostapd_set_sta_authorized(hapd, sta->addr, authorized); + if (res && errno != ENOENT) { + printf("Could not set station " MACSTR " flags for kernel " + "driver (errno=%d).\n", MAC2STR(sta->addr), errno); + } + if (authorized) accounting_sta_start(hapd, sta); } @@ -144,21 +146,19 @@ void ieee802_1x_request_identity(struct hostapd_data *hapd, return; } - if (sm == NULL || !sm->auth_pae.eapRestart) + 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 = malloc(tlen); + buf = wpa_zalloc(tlen); if (buf == NULL) { printf("Could not allocate memory for identity request\n"); return; } - memset(buf, 0, tlen); - eap = (struct eap_hdr *) buf; eap->code = EAP_CODE_REQUEST; eap->identifier = ++sm->currentId; @@ -170,7 +170,7 @@ void ieee802_1x_request_identity(struct hostapd_data *hapd, hapd->conf->eap_req_id_text_len); } - sm->be_auth.eapReq = TRUE; + sm->eapReq = TRUE; free(sm->last_eap_radius); sm->last_eap_radius = buf; sm->last_eap_radius_len = tlen; @@ -184,7 +184,7 @@ void ieee802_1x_request_identity(struct hostapd_data *hapd, " (identifier %d, timeout 30)\n", MAC2STR(sta->addr), eap->identifier); - sm->auth_pae.eapRestart = FALSE; + sm->eapRestart = FALSE; } @@ -248,24 +248,9 @@ void ieee802_1x_tx_req(struct hostapd_data *hapd, struct sta_info *sta) } -void hostapd_get_ntp_timestamp(u8 *buf) -{ - struct timeval now; - u32 sec, usec; - - /* 64-bit NTP timestamp (time from 1900-01-01 00:00:00) */ - gettimeofday(&now, NULL); - sec = htonl(now.tv_sec + 2208988800U); /* Epoch to 1900 */ - /* Estimate 2^32/10^6 = 4295 - 1/32 - 1/512 */ - usec = now.tv_usec; - usec = htonl(4295 * usec - (usec >> 5) - (usec >> 9)); - memcpy(buf, (u8 *) &sec, 4); - memcpy(buf + 4, (u8 *) &usec, 4); -} - - -static void ieee802_1x_tx_key_one(hostapd *hapd, struct sta_info *sta, - int index, int broadcast, +static void ieee802_1x_tx_key_one(struct hostapd_data *hapd, + struct sta_info *sta, + int idx, int broadcast, u8 *key_data, size_t key_len) { u8 *buf, *ekey; @@ -278,16 +263,15 @@ static void ieee802_1x_tx_key_one(hostapd *hapd, struct sta_info *sta, return; len = sizeof(*key) + key_len; - buf = malloc(sizeof(*hdr) + len); + buf = wpa_zalloc(sizeof(*hdr) + len); if (buf == NULL) return; - memset(buf, 0, sizeof(*hdr) + len); hdr = (struct ieee802_1x_hdr *) buf; key = (struct ieee802_1x_eapol_key *) (hdr + 1); key->type = EAPOL_KEY_TYPE_RC4; key->key_length = htons(key_len); - hostapd_get_ntp_timestamp(key->replay_counter); + 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"); @@ -295,7 +279,7 @@ static void ieee802_1x_tx_key_one(hostapd *hapd, struct sta_info *sta, return; } - key->key_index = index | (broadcast ? 0 : BIT(7)); + key->key_index = idx | (broadcast ? 0 : BIT(7)); if (hapd->conf->eapol_key_index_workaround) { /* According to some information, WinXP Supplicant seems to * interpret bit7 as an indication whether the key is to be @@ -332,7 +316,7 @@ static void ieee802_1x_tx_key_one(hostapd *hapd, struct sta_info *sta, HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "IEEE 802.1X: Sending EAPOL-Key to " MACSTR " (%s index=%d)\n", MAC2STR(sm->addr), - broadcast ? "broadcast" : "unicast", index); + 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++; @@ -340,9 +324,109 @@ static void ieee802_1x_tx_key_one(hostapd *hapd, struct sta_info *sta, } +static struct hostapd_wep_keys * +ieee802_1x_group_alloc(struct hostapd_data *hapd, const char *ifname) +{ + struct hostapd_wep_keys *key; + + key = wpa_zalloc(sizeof(*key)); + if (key == NULL) + return NULL; + + key->default_len = hapd->conf->default_wep_key_len; + + if (key->idx >= hapd->conf->broadcast_key_idx_max || + key->idx < hapd->conf->broadcast_key_idx_min) + key->idx = hapd->conf->broadcast_key_idx_min; + else + key->idx++; + + if (!key->key[key->idx]) + key->key[key->idx] = malloc(key->default_len); + if (key->key[key->idx] == NULL || + hostapd_get_rand(key->key[key->idx], key->default_len)) { + printf("Could not generate random WEP key (dynamic VLAN).\n"); + free(key->key[key->idx]); + key->key[key->idx] = NULL; + 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]); + } + + if (hostapd_set_encryption(ifname, hapd, "WEP", NULL, key->idx, + key->key[key->idx], key->len[key->idx], 1)) + printf("Could not set dynamic VLAN WEP encryption key.\n"); + + hostapd_set_ieee8021x(ifname, hapd, 1); + + return key; +} + + +static struct hostapd_wep_keys * +ieee802_1x_get_group(struct hostapd_data *hapd, struct hostapd_ssid *ssid, + size_t vlan_id) +{ + const char *ifname; + + if (vlan_id == 0) + return &ssid->wep; + + if (vlan_id <= ssid->max_dyn_vlan_keys && ssid->dyn_vlan_keys && + 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); + + 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); + 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); + if (ssid->dyn_vlan_keys == NULL) + return NULL; + ssid->max_dyn_vlan_keys = vlan_id; + } + + 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); + 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])); + ssid->max_dyn_vlan_keys = vlan_id; + } + + ssid->dyn_vlan_keys[vlan_id] = ieee802_1x_group_alloc(hapd, ifname); + + return ssid->dyn_vlan_keys[vlan_id]; +} + + void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) { + struct hostapd_wep_keys *key = NULL; struct eapol_state_machine *sm = sta->eapol_sm; + int vlan_id; if (sm == NULL || !sm->eapol_key_sign || !sm->eapol_key_crypt) return; @@ -351,10 +435,21 @@ void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) "IEEE 802.1X: Sending EAPOL-Key(s) to " MACSTR "\n", MAC2STR(sta->addr)); - if (hapd->default_wep_key) + vlan_id = sta->vlan_id; + if (vlan_id < 0 || vlan_id > MAX_VLAN_ID) + vlan_id = 0; + + if (vlan_id) { + key = ieee802_1x_get_group(hapd, sta->ssid, vlan_id); + if (key && key->key[key->idx]) + ieee802_1x_tx_key_one(hapd, sta, key->idx, 1, + key->key[key->idx], + key->len[key->idx]); + } else if (hapd->default_wep_key) { ieee802_1x_tx_key_one(hapd, sta, hapd->default_wep_key_idx, 1, hapd->default_wep_key, hapd->conf->default_wep_key_len); + } if (hapd->conf->individual_wep_key_len > 0) { u8 *ikey; @@ -368,19 +463,18 @@ void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) return; } - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) - hostapd_hexdump("Individual WEP key", - ikey, - hapd->conf->individual_wep_key_len); + wpa_hexdump_key(MSG_DEBUG, "Individual WEP key", + ikey, hapd->conf->individual_wep_key_len); ieee802_1x_tx_key_one(hapd, sta, 0, 0, ikey, hapd->conf->individual_wep_key_len); /* TODO: set encryption in TX callback, i.e., only after STA * has ACKed EAPOL-Key frame */ - if (hostapd_set_encryption(hapd, "WEP", sta->addr, 0, ikey, - hapd->conf-> - individual_wep_key_len)) { + if (hostapd_set_encryption(hapd->conf->iface, hapd, "WEP", + sta->addr, 0, ikey, + hapd->conf->individual_wep_key_len, + 1)) { printf("Could not set individual WEP encryption.\n"); } @@ -389,7 +483,38 @@ void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) } -static void ieee802_1x_encapsulate_radius(hostapd *hapd, struct sta_info *sta, +const char *radius_mode_txt(struct hostapd_data *hapd) +{ + if (hapd->iface->current_mode == NULL) + return "802.11"; + + switch (hapd->iface->current_mode->mode) { + case HOSTAPD_MODE_IEEE80211A: + return "802.11a"; + case HOSTAPD_MODE_IEEE80211G: + return "802.11g"; + case HOSTAPD_MODE_IEEE80211B: + default: + return "802.11b"; + } +} + + +int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta) +{ + int i; + u8 rate = 0; + + for (i = 0; i < sta->supported_rates_len; i++) + if ((sta->supported_rates[i] & 0x7f) > rate) + rate = sta->supported_rates[i] & 0x7f; + + return rate; +} + + +static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, + struct sta_info *sta, u8 *eap, size_t len) { struct radius_msg *msg; @@ -449,7 +574,8 @@ static void ieee802_1x_encapsulate_radius(hostapd *hapd, struct sta_info *sta, } snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", - MAC2STR(hapd->own_addr), hapd->conf->ssid); + 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))) { printf("Could not add Called-Station-Id\n"); @@ -458,6 +584,7 @@ static void ieee802_1x_encapsulate_radius(hostapd *hapd, struct sta_info *sta, 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))) { printf("Could not add Calling-Station-Id\n"); @@ -478,7 +605,15 @@ static void ieee802_1x_encapsulate_radius(hostapd *hapd, struct sta_info *sta, goto fail; } - snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b"); + if (sta->flags & WLAN_STA_PREAUTH) { + snprintf(buf, sizeof(buf), "IEEE 802.11i Pre-Authentication"); + } 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)); + } + buf[sizeof(buf) - 1] = '\0'; if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, (u8 *) buf, strlen(buf))) { printf("Could not add Connect-Info\n"); @@ -516,7 +651,7 @@ static void ieee802_1x_encapsulate_radius(hostapd *hapd, struct sta_info *sta, } -static char *eap_type_text(u8 type) +char *eap_type_text(u8 type) { switch (type) { case EAP_TYPE_IDENTITY: return "Identity"; @@ -528,13 +663,18 @@ static char *eap_type_text(u8 type) 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_FAST: return "FAST"; + case EAP_TYPE_SAKE: return "SAKE"; + case EAP_TYPE_PSK: return "PSK"; default: return "Unknown"; } } -static void handle_eap_response(hostapd *hapd, struct sta_info *sta, - struct eap_hdr *eap, u8 *data, size_t len) +static void handle_eap_response(struct hostapd_data *hapd, + struct sta_info *sta, struct eap_hdr *eap, + u8 *data, size_t len) { u8 type; struct eapol_state_machine *sm = sta->eapol_sm; @@ -567,7 +707,7 @@ static void handle_eap_response(hostapd *hapd, struct sta_info *sta, memcpy(sm->last_eap_supp, eap, sizeof(*eap)); memcpy(sm->last_eap_supp + sizeof(*eap), data, len); - type = data[0]; + sm->eap_type_supp = type = data[0]; data++; len--; @@ -579,7 +719,7 @@ static void handle_eap_response(hostapd *hapd, struct sta_info *sta, if (type == EAP_TYPE_IDENTITY) { char *buf, *pos; - int i; + size_t i; buf = malloc(4 * len + 1); if (buf) { pos = buf; @@ -618,8 +758,8 @@ static void handle_eap_response(hostapd *hapd, struct sta_info *sta, /* Process incoming EAP packet from Supplicant */ -static void handle_eap(hostapd *hapd, struct sta_info *sta, u8 *buf, - size_t len) +static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta, + u8 *buf, size_t len) { struct eap_hdr *eap; u16 eap_len; @@ -677,6 +817,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, struct ieee802_1x_hdr *hdr; struct ieee802_1x_eapol_key *key; u16 datalen; + struct rsn_pmksa_cache_entry *pmksa; if (!hapd->conf->ieee802_1x && !hapd->conf->wpa) return; @@ -724,11 +865,13 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, hdr->type == IEEE802_1X_TYPE_EAPOL_KEY && (key->type == EAPOL_KEY_TYPE_WPA || key->type == EAPOL_KEY_TYPE_RSN)) { - wpa_receive(hapd, sta, (u8 *) hdr, sizeof(*hdr) + datalen); + wpa_receive(hapd->wpa_auth, sta->wpa_sm, (u8 *) hdr, + sizeof(*hdr) + datalen); return; } - if (!hapd->conf->ieee802_1x || sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) + if (!hapd->conf->ieee802_1x || + wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_PSK) return; if (!sta->eapol_sm) { @@ -753,16 +896,17 @@ 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"); - if (sta->pmksa) { + pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm); + if (pmksa) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, HOSTAPD_LEVEL_DEBUG, "cached PMKSA " "available - ignore it since " "STA sent EAPOL-Start"); - sta->pmksa = NULL; + wpa_auth_sta_clear_pmksa(sta->wpa_sm, pmksa); } - sta->eapol_sm->auth_pae.eapolStart = TRUE; + sta->eapol_sm->eapolStart = TRUE; sta->eapol_sm->dot1xAuthEapolStartFramesRx++; - wpa_sm_event(hapd, sta, WPA_REAUTH_EAPOL); + wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH_EAPOL); break; case IEEE802_1X_TYPE_EAPOL_LOGOFF: @@ -771,7 +915,7 @@ 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; - sta->eapol_sm->auth_pae.eapolLogoff = TRUE; + sta->eapol_sm->eapolLogoff = TRUE; sta->eapol_sm->dot1xAuthEapolLogoffFramesRx++; break; @@ -801,9 +945,13 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, } -void ieee802_1x_new_station(hostapd *hapd, struct sta_info *sta) +void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) { - if (!hapd->conf->ieee802_1x || sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) + 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) return; if (sta->eapol_sm == NULL) { @@ -817,11 +965,15 @@ void ieee802_1x_new_station(hostapd *hapd, struct sta_info *sta) "failed to allocate state machine"); return; } + reassoc = 0; } sta->eapol_sm->portEnabled = TRUE; - if (sta->pmksa) { + pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm); + if (pmksa) { + int old_vlanid; + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "PMK from PMKSA cache - skip IEEE 802.1X/EAP"); @@ -829,19 +981,33 @@ void ieee802_1x_new_station(hostapd *hapd, struct sta_info *sta) * because of existing PMKSA information in the cache. */ sta->eapol_sm->keyRun = TRUE; sta->eapol_sm->keyAvailable = TRUE; - sta->eapol_sm->auth_pae.state = AUTH_PAE_AUTHENTICATING; - sta->eapol_sm->be_auth.state = BE_AUTH_SUCCESS; + sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING; + sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS; sta->eapol_sm->authSuccess = TRUE; if (sta->eapol_sm->eap) eap_sm_notify_cached(sta->eapol_sm->eap); - } else + old_vlanid = sta->vlan_id; + pmksa_cache_to_eapol_data(pmksa, sta->eapol_sm); + if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED) + sta->vlan_id = 0; + ap_sta_bind_vlan(hapd, sta, old_vlanid); + } else { + if (reassoc) { + /* + * Force EAPOL state machines to start + * re-authentication without having to wait for the + * Supplicant to send EAPOL-Start. + */ + sta->eapol_sm->reAuthenticate = TRUE; + } eapol_sm_step(sta->eapol_sm); + } } void ieee802_1x_free_radius_class(struct radius_class_data *class) { - int i; + size_t i; if (class == NULL) return; for (i = 0; i < class->count; i++) @@ -860,11 +1026,10 @@ int ieee802_1x_copy_radius_class(struct radius_class_data *dst, if (src->attr == NULL) return 0; - dst->attr = malloc(src->count * sizeof(struct radius_attr_data)); + dst->attr = wpa_zalloc(src->count * sizeof(struct radius_attr_data)); if (dst->attr == NULL) return -1; - memset(dst->attr, 0, src->count * sizeof(struct radius_attr_data)); dst->count = 0; for (i = 0; i < src->count; i++) { @@ -906,7 +1071,8 @@ void ieee802_1x_free_station(struct sta_info *sta) } -static void ieee802_1x_decapsulate_radius(hostapd *hapd, struct sta_info *sta) +static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd, + struct sta_info *sta) { u8 *eap; size_t len; @@ -918,7 +1084,7 @@ static void ieee802_1x_decapsulate_radius(hostapd *hapd, struct sta_info *sta) if (sm == NULL || sm->last_recv_radius == NULL) { if (sm) - sm->be_auth.eapNoReq = TRUE; + sm->eapNoReq = TRUE; return; } @@ -935,7 +1101,7 @@ static void ieee802_1x_decapsulate_radius(hostapd *hapd, struct sta_info *sta) free(sm->last_eap_radius); sm->last_eap_radius = NULL; sm->last_eap_radius_len = 0; - sm->be_auth.eapNoReq = TRUE; + sm->eapNoReq = TRUE; return; } @@ -944,7 +1110,7 @@ static void ieee802_1x_decapsulate_radius(hostapd *hapd, struct sta_info *sta) HOSTAPD_LEVEL_WARNING, "too short EAP packet " "received from authentication server"); free(eap); - sm->be_auth.eapNoReq = TRUE; + sm->eapNoReq = TRUE; return; } @@ -954,6 +1120,8 @@ static void ieee802_1x_decapsulate_radius(hostapd *hapd, struct sta_info *sta) hdr = (struct eap_hdr *) eap; switch (hdr->code) { 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); @@ -973,11 +1141,12 @@ static void ieee802_1x_decapsulate_radius(hostapd *hapd, struct sta_info *sta) snprintf(buf, sizeof(buf), "unknown EAP code"); 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->be_auth.eapReq = TRUE; + sm->eapReq = TRUE; free(sm->last_eap_radius); sm->last_eap_radius = eap; @@ -985,8 +1154,9 @@ static void ieee802_1x_decapsulate_radius(hostapd *hapd, struct sta_info *sta) } -static void ieee802_1x_get_keys(hostapd *hapd, struct sta_info *sta, - struct radius_msg *msg, struct radius_msg *req, +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) { struct radius_ms_mppe_keys *keys; @@ -998,21 +1168,13 @@ static void ieee802_1x_get_keys(hostapd *hapd, struct sta_info *sta, shared_secret_len); if (keys) { - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL) && keys->send) { - size_t i; - printf("MS-MPPE-Send-Key (len=%lu):", - (unsigned long) keys->send_len); - for (i = 0; i < keys->send_len; i++) - printf(" %02x", keys->send[i]); - printf("\n"); + if (keys->send) { + wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Send-Key", + keys->send, keys->send_len); } - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL) && keys->recv) { - size_t i; - printf("MS-MPPE-Recv-Key (len=%lu):", - (unsigned long) keys->recv_len); - for (i = 0; i < keys->recv_len; i++) - printf(" %02x", keys->recv[i]); - printf("\n"); + if (keys->recv) { + wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Recv-Key", + keys->recv, keys->recv_len); } if (keys->send && keys->recv) { @@ -1055,12 +1217,11 @@ static void ieee802_1x_store_radius_class(struct hostapd_data *hapd, if (count <= 0) return; - nclass = malloc(count * sizeof(struct radius_attr_data)); + nclass = wpa_zalloc(count * sizeof(struct radius_attr_data)); if (nclass == NULL) return; nclass_count = 0; - memset(nclass, 0, count * sizeof(struct radius_attr_data)); class = NULL; for (i = 0; i < count; i++) { @@ -1168,7 +1329,7 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, struct hostapd_data *hapd = data; struct sta_info *sta; u32 session_timeout = 0, termination_action, acct_interim_interval; - int session_timeout_set; + int session_timeout_set, old_vlanid = 0; int eap_timeout; struct eapol_state_machine *sm; int override_eapReq = 0; @@ -1242,10 +1403,35 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, switch (msg->hdr->code) { case RADIUS_CODE_ACCESS_ACCEPT: + if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED) + sta->vlan_id = 0; + else { + old_vlanid = sta->vlan_id; + sta->vlan_id = radius_msg_get_vlanid(msg); + } + if (sta->vlan_id > 0 && + hostapd_get_vlan_id_ifname(hapd->conf->vlan, + sta->vlan_id)) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, + "VLAN ID %d", sta->vlan_id); + } else if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_REQUIRED) { + sta->eapol_sm->authFail = TRUE; + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_INFO, "authentication " + "server did not include required VLAN " + "ID in Access-Accept"); + break; + } + + ap_sta_bind_vlan(hapd, sta, old_vlanid); + /* RFC 3580, Ch. 3.17 */ if (session_timeout_set && termination_action == RADIUS_TERMINATION_ACTION_RADIUS_REQUEST) { - sm->reauth_timer.reAuthPeriod = session_timeout; + sm->reAuthPeriod = session_timeout; } else if (session_timeout_set) ap_sta_session_timeout(hapd, sta, session_timeout); @@ -1255,10 +1441,13 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, shared_secret_len); ieee802_1x_store_radius_class(hapd, sta, msg); ieee802_1x_update_sta_identity(hapd, sta, msg); - if (sm->keyAvailable) { - pmksa_cache_add(hapd, sta, sm->eapol_key_crypt, - session_timeout_set ? - session_timeout : -1); + if (sm->keyAvailable && + wpa_auth_pmksa_add(sta->wpa_sm, sm->eapol_key_crypt, + session_timeout_set ? + (int) session_timeout : -1, sm) == 0) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, + "Added PMKSA cache entry"); } break; case RADIUS_CODE_ACCESS_REJECT: @@ -1285,7 +1474,7 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, ieee802_1x_decapsulate_radius(hapd, sta); if (override_eapReq) - sm->be_auth.eapReq = FALSE; + sm->eapReq = FALSE; eapol_sm_step(sm); @@ -1295,7 +1484,8 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, /* Handler for EAPOL Backend Authentication state machine sendRespToServer. * Forward the EAP Response from Supplicant to Authentication Server. */ -void ieee802_1x_send_resp_to_server(hostapd *hapd, struct sta_info *sta) +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) @@ -1334,21 +1524,16 @@ void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta) } -void ieee802_1x_set_port_enabled(hostapd *hapd, struct sta_info *sta, - int enabled) +#ifdef HOSTAPD_DUMP_STATE +static void fprint_char(FILE *f, char c) { - if (!sta->eapol_sm) - return; - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "IEEE 802.1X: station " MACSTR " port %s\n", - MAC2STR(sta->addr), enabled ? "enabled" : "disabled"); - sta->eapol_sm->portEnabled = enabled ? TRUE : FALSE; - eapol_sm_step(sta->eapol_sm); + if (c >= 32 && c < 127) + fprintf(f, "%c", c); + else + fprintf(f, "<%02x>", c); } -#ifdef HOSTAPD_DUMP_STATE void ieee802_1x_dump_state(FILE *f, const char *prefix, struct sta_info *sta) { struct eapol_state_machine *sm = sta->eapol_sm; @@ -1365,6 +1550,11 @@ void ieee802_1x_dump_state(FILE *f, const char *prefix, struct sta_info *sta) fprintf(f, "\n"); } + fprintf(f, "%slast EAP type: Authentication Server: %d (%s) " + "Supplicant: %d (%s)\n", prefix, + 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]" : "", @@ -1375,7 +1565,7 @@ void ieee802_1x_dump_state(FILE *f, const char *prefix, struct sta_info *sta) #endif /* HOSTAPD_DUMP_STATE */ -static int ieee802_1x_rekey_broadcast(hostapd *hapd) +static int ieee802_1x_rekey_broadcast(struct hostapd_data *hapd) { if (hapd->conf->default_wep_key_len < 1) return 0; @@ -1391,11 +1581,9 @@ static int ieee802_1x_rekey_broadcast(hostapd *hapd) return -1; } - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { - hostapd_hexdump("IEEE 802.1X: New default WEP key", - hapd->default_wep_key, - hapd->conf->default_wep_key_len); - } + wpa_hexdump_key(MSG_DEBUG, "IEEE 802.1X: New default WEP key", + hapd->default_wep_key, + hapd->conf->default_wep_key_len); return 0; } @@ -1436,10 +1624,10 @@ static void ieee802_1x_rekey(void *eloop_ctx, void *timeout_ctx) /* TODO: Could setup key for RX here, but change default TX keyid only * after new broadcast key has been sent to all stations. */ - if (hostapd_set_encryption(hapd, "WEP", NULL, + if (hostapd_set_encryption(hapd->conf->iface, hapd, "WEP", NULL, hapd->default_wep_key_idx, hapd->default_wep_key, - hapd->conf->default_wep_key_len)) { + hapd->conf->default_wep_key_len, 1)) { hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_WARNING, "failed to configure a " "new broadcast key"); @@ -1457,12 +1645,12 @@ static void ieee802_1x_rekey(void *eloop_ctx, void *timeout_ctx) } -int ieee802_1x_init(hostapd *hapd) +int ieee802_1x_init(struct hostapd_data *hapd) { int i; if ((hapd->conf->ieee802_1x || hapd->conf->wpa) && - hostapd_set_ieee8021x(hapd, 1)) + hostapd_set_ieee8021x(hapd->conf->iface, hapd, 1)) return -1; if (radius_client_register(hapd->radius, RADIUS_AUTH, @@ -1470,8 +1658,11 @@ int ieee802_1x_init(hostapd *hapd) return -1; if (hapd->conf->default_wep_key_len) { + hostapd_set_privacy(hapd, 1); + for (i = 0; i < 4; i++) - hostapd_set_encryption(hapd, "none", NULL, i, NULL, 0); + hostapd_set_encryption(hapd->conf->iface, hapd, + "none", NULL, i, NULL, 0, 0); ieee802_1x_rekey(hapd, NULL); @@ -1483,11 +1674,20 @@ int ieee802_1x_init(hostapd *hapd) } -void ieee802_1x_deinit(hostapd *hapd) +void ieee802_1x_deinit(struct hostapd_data *hapd) { if (hapd->driver != NULL && (hapd->conf->ieee802_1x || hapd->conf->wpa)) - hostapd_set_ieee8021x(hapd, 0); + hostapd_set_ieee8021x(hapd->conf->iface, hapd, 0); +} + + +int ieee802_1x_reconfig(struct hostapd_data *hapd, + struct hostapd_config *oldconf, + struct hostapd_bss_config *oldbss) +{ + ieee802_1x_deinit(hapd); + return ieee802_1x_init(hapd); } @@ -1513,8 +1713,8 @@ static void ieee802_1x_new_auth_session(struct hostapd_data *hapd, } -int ieee802_1x_tx_status(hostapd *hapd, struct sta_info *sta, u8 *buf, - size_t len, int ack) +int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta, + u8 *buf, size_t len, int ack) { struct ieee80211_hdr *hdr; struct ieee802_1x_hdr *xhdr; @@ -1585,7 +1785,7 @@ u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len, int idx) { if (sm == NULL || sm->radius_class.attr == NULL || - idx >= sm->radius_class.count) + idx >= (int) sm->radius_class.count) return NULL; *len = sm->radius_class.attr[idx].len; @@ -1650,124 +1850,140 @@ 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) { - int len = 0; + int len = 0, ret; struct eapol_state_machine *sm = sta->eapol_sm; if (sm == NULL) return 0; - len += snprintf(buf + len, buflen - len, - "dot1xPaePortNumber=%d\n" - "dot1xPaePortProtocolVersion=%d\n" - "dot1xPaePortCapabilities=1\n" - "dot1xPaePortInitialize=%d\n" - "dot1xPaePortReauthenticate=FALSE\n", - sta->aid, - hapd->conf->eapol_version, - sm->initialize); + 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); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; /* dot1xAuthConfigTable */ - len += 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->ctrl_dir.adminControlledDirections, - sm->ctrl_dir.operControlledDirections, - sm->authPortStatus, - sm->portControl, - sm->auth_pae.quietPeriod, - sm->be_auth.serverTimeout, - sm->reauth_timer.reAuthPeriod, - bool_txt(sm->reauth_timer.reAuthEnabled), - bool_txt(sm->keyTxEnabled)); + 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)); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; /* dot1xAuthStatsTable */ - len += 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 = 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 */ - len += 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->auth_pae.authEntersConnecting, - sm->auth_pae.authEapLogoffsWhileConnecting, - sm->auth_pae.authEntersAuthenticating, - sm->auth_pae.authAuthSuccessesWhileAuthenticating, - sm->auth_pae.authAuthTimeoutsWhileAuthenticating, - sm->auth_pae.authAuthFailWhileAuthenticating, - sm->auth_pae.authAuthEapStartsWhileAuthenticating, - sm->auth_pae.authAuthEapLogoffWhileAuthenticating, - sm->auth_pae.authAuthReauthsWhileAuthenticated, - sm->auth_pae.authAuthEapStartsWhileAuthenticated, - sm->auth_pae.authAuthEapLogoffWhileAuthenticated, - sm->be_auth.backendResponses, - sm->be_auth.backendAccessChallenges, - sm->be_auth.backendOtherRequestsToSupplicant, - sm->be_auth.backendAuthSuccesses, - sm->be_auth.backendAuthFails); + 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); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; /* dot1xAuthSessionStatsTable */ - len += 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, - sta->wpa_key_mgmt == WPA_KEY_MGMT_IEEE8021X ? 1 : 2, - (unsigned int) (time(NULL) - sta->acct_session_start), - sm->identity); + 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); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; return len; } @@ -1782,7 +1998,11 @@ void ieee802_1x_finished(struct hostapd_data *hapd, struct sta_info *sta, static const int dot11RSNAConfigPMKLifetime = 43200; key = ieee802_1x_get_key_crypt(sta->eapol_sm, &len); - if (success && key) { - pmksa_cache_add(hapd, sta, key, dot11RSNAConfigPMKLifetime); + if (success && key && + wpa_auth_pmksa_add(sta->wpa_sm, key, dot11RSNAConfigPMKLifetime, + sta->eapol_sm) == 0) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, + "Added PMKSA cache entry (IEEE 802.1X)"); } } diff --git a/contrib/hostapd/ieee802_1x.h b/contrib/hostapd/ieee802_1x.h index 0ac06b2..70b21de 100644 --- a/contrib/hostapd/ieee802_1x.h +++ b/contrib/hostapd/ieee802_1x.h @@ -1,24 +1,20 @@ +/* + * hostapd / IEEE 802.1X Authenticator + * Copyright (c) 2002-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 IEEE802_1X_H #define IEEE802_1X_H -/* IEEE Std 802.1X-REV-d11, 7.2 */ - -struct ieee802_1x_hdr { - u8 version; - u8 type; - u16 length; - /* followed by length octets of data */ -} __attribute__ ((packed)); - -#define EAPOL_VERSION 2 - -enum { IEEE802_1X_TYPE_EAP_PACKET = 0, - IEEE802_1X_TYPE_EAPOL_START = 1, - IEEE802_1X_TYPE_EAPOL_LOGOFF = 2, - IEEE802_1X_TYPE_EAPOL_KEY = 3, - IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT = 4 -}; - /* draft-congdon-radius-8021x-20.txt */ struct ieee802_1x_eapol_key { @@ -43,32 +39,31 @@ struct ieee802_1x_eapol_key { * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */ } __attribute__ ((packed)); -enum { EAPOL_KEY_TYPE_RC4 = 1, EAPOL_KEY_TYPE_RSN = 2, - EAPOL_KEY_TYPE_WPA = 254 }; - void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, size_t len); -void ieee802_1x_new_station(hostapd *hapd, struct sta_info *sta); +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(hostapd *hapd, struct sta_info *sta); +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(hostapd *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(hostapd *hapd, struct sta_info *sta, - int authorized); -void ieee802_1x_set_port_enabled(hostapd *hapd, struct sta_info *sta, - int enabled); +void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd, + struct sta_info *sta, int authorized); void ieee802_1x_dump_state(FILE *f, const char *prefix, struct sta_info *sta); -int ieee802_1x_init(hostapd *hapd); -void ieee802_1x_deinit(hostapd *hapd); -int ieee802_1x_tx_status(hostapd *hapd, struct sta_info *sta, u8 *buf, - size_t len, int ack); +int ieee802_1x_init(struct hostapd_data *hapd); +void ieee802_1x_deinit(struct hostapd_data *hapd); +int ieee802_1x_reconfig(struct hostapd_data *hapd, + struct hostapd_config *oldconf, + struct hostapd_bss_config *oldbss); +int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta, + u8 *buf, size_t len, int ack); u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len); u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len, int idx); @@ -84,6 +79,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, 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; diff --git a/contrib/hostapd/includes.h b/contrib/hostapd/includes.h new file mode 100644 index 0000000..84308c3 --- /dev/null +++ b/contrib/hostapd/includes.h @@ -0,0 +1,57 @@ +/* + * wpa_supplicant/hostapd - Default include files + * 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 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 + * having to have OS/C library specific selection in many files. + */ + +#ifndef INCLUDES_H +#define INCLUDES_H + +/* Include possible build time configuration before including anything else */ +#include "build_config.h" + +#include <stdlib.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#ifndef _WIN32_WCE +#ifndef CONFIG_TI_COMPILER +#include <signal.h> +#include <sys/types.h> +#endif /* CONFIG_TI_COMPILER */ +#include <errno.h> +#endif /* _WIN32_WCE */ +#include <ctype.h> +#include <time.h> + +#ifndef CONFIG_TI_COMPILER +#ifndef _MSC_VER +#include <unistd.h> +#endif /* _MSC_VER */ +#endif /* CONFIG_TI_COMPILER */ + +#ifndef CONFIG_NATIVE_WINDOWS +#ifndef CONFIG_TI_COMPILER +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#ifndef __vxworks +#include <sys/uio.h> +#include <sys/time.h> +#endif /* __vxworks */ +#endif /* CONFIG_TI_COMPILER */ +#endif /* CONFIG_NATIVE_WINDOWS */ + +#endif /* INCLUDES_H */ diff --git a/contrib/hostapd/l2_packet.h b/contrib/hostapd/l2_packet.h index eb966d3..540f0a1 100644 --- a/contrib/hostapd/l2_packet.h +++ b/contrib/hostapd/l2_packet.h @@ -1,6 +1,6 @@ /* * WPA Supplicant - Layer2 packet interface definition - * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * 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 @@ -42,11 +42,19 @@ */ struct l2_packet_data; +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + struct l2_ethhdr { u8 h_dest[ETH_ALEN]; u8 h_source[ETH_ALEN]; u16 h_proto; -} __attribute__ ((packed)); +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ /** * l2_packet_init - Initialize l2_packet interface diff --git a/contrib/hostapd/l2_packet_none.c b/contrib/hostapd/l2_packet_none.c new file mode 100644 index 0000000..2add5b7 --- /dev/null +++ b/contrib/hostapd/l2_packet_none.c @@ -0,0 +1,123 @@ +/* + * WPA Supplicant - Layer2 packet handling example with dummy 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. + * + * This file can be used as a starting point for layer2 packet implementation. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "l2_packet.h" + + +struct l2_packet_data { + char ifname[17]; + u8 own_addr[ETH_ALEN]; + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len); + void *rx_callback_ctx; + int l2_hdr; /* whether to include layer 2 (Ethernet) header data + * buffers */ + int fd; +}; + + +int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr) +{ + os_memcpy(addr, l2->own_addr, ETH_ALEN); + return 0; +} + + +int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, + const u8 *buf, size_t len) +{ + if (l2 == NULL) + return -1; + + /* + * TODO: Send frame (may need different implementation depending on + * whether l2->l2_hdr is set). + */ + + return 0; +} + + +static void l2_packet_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct l2_packet_data *l2 = eloop_ctx; + u8 buf[2300]; + int res; + + /* TODO: receive frame (e.g., recv() using sock */ + buf[0] = 0; + res = 0; + + l2->rx_callback(l2->rx_callback_ctx, NULL /* TODO: src addr */, + buf, res); +} + + +struct l2_packet_data * l2_packet_init( + const char *ifname, const u8 *own_addr, unsigned short protocol, + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + void *rx_callback_ctx, int l2_hdr) +{ + struct l2_packet_data *l2; + + l2 = os_zalloc(sizeof(struct l2_packet_data)); + if (l2 == NULL) + return NULL; + os_strncpy(l2->ifname, ifname, sizeof(l2->ifname)); + l2->rx_callback = rx_callback; + l2->rx_callback_ctx = rx_callback_ctx; + l2->l2_hdr = l2_hdr; + + /* + * TODO: open connection for receiving frames + */ + l2->fd = -1; + eloop_register_read_sock(l2->fd, l2_packet_receive, l2, NULL); + + return l2; +} + + +void l2_packet_deinit(struct l2_packet_data *l2) +{ + if (l2 == NULL) + return; + + if (l2->fd >= 0) { + eloop_unregister_read_sock(l2->fd); + /* TODO: close connection */ + } + + os_free(l2); +} + + +int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len) +{ + /* TODO: get interface IP address */ + return -1; +} + + +void l2_packet_notify_auth_start(struct l2_packet_data *l2) +{ + /* This function can be left empty */ +} diff --git a/contrib/hostapd/md4.c b/contrib/hostapd/md4.c new file mode 100644 index 0000000..41c84a3 --- /dev/null +++ b/contrib/hostapd/md4.c @@ -0,0 +1,282 @@ +/* + * MD4 hash implementation + * 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" + + +#ifdef INTERNAL_MD4 + +#define MD4_BLOCK_LENGTH 64 +#define MD4_DIGEST_LENGTH 16 + +typedef struct MD4Context { + u32 state[4]; /* state */ + u64 count; /* number of bits, mod 2^64 */ + u8 buffer[MD4_BLOCK_LENGTH]; /* input buffer */ +} MD4_CTX; + + +static void MD4Init(MD4_CTX *ctx); +static void MD4Update(MD4_CTX *ctx, const unsigned char *input, size_t len); +static void MD4Final(unsigned char digest[MD4_DIGEST_LENGTH], MD4_CTX *ctx); + + +void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + MD4_CTX ctx; + size_t i; + + MD4Init(&ctx); + for (i = 0; i < num_elem; i++) + MD4Update(&ctx, addr[i], len[i]); + MD4Final(mac, &ctx); +} + + +/* ===== start - public domain MD4 implementation ===== */ +/* $OpenBSD: md4.c,v 1.7 2005/08/08 08:05:35 espie Exp $ */ + +/* + * This code implements the MD4 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * Todd C. Miller modified the MD5 code to do MD4 based on RFC 1186. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD4Context structure, pass it to MD4Init, call MD4Update as + * needed on buffers full of bytes, and then call MD4Final, which + * will fill a supplied 16-byte array with the digest. + */ + +#define MD4_DIGEST_STRING_LENGTH (MD4_DIGEST_LENGTH * 2 + 1) + + +static void +MD4Transform(u32 state[4], const u8 block[MD4_BLOCK_LENGTH]); + +#define PUT_64BIT_LE(cp, value) do { \ + (cp)[7] = (value) >> 56; \ + (cp)[6] = (value) >> 48; \ + (cp)[5] = (value) >> 40; \ + (cp)[4] = (value) >> 32; \ + (cp)[3] = (value) >> 24; \ + (cp)[2] = (value) >> 16; \ + (cp)[1] = (value) >> 8; \ + (cp)[0] = (value); } while (0) + +#define PUT_32BIT_LE(cp, value) do { \ + (cp)[3] = (value) >> 24; \ + (cp)[2] = (value) >> 16; \ + (cp)[1] = (value) >> 8; \ + (cp)[0] = (value); } while (0) + +static u8 PADDING[MD4_BLOCK_LENGTH] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +/* + * Start MD4 accumulation. + * Set bit count to 0 and buffer to mysterious initialization constants. + */ +static void MD4Init(MD4_CTX *ctx) +{ + ctx->count = 0; + ctx->state[0] = 0x67452301; + ctx->state[1] = 0xefcdab89; + ctx->state[2] = 0x98badcfe; + ctx->state[3] = 0x10325476; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +static void MD4Update(MD4_CTX *ctx, const unsigned char *input, size_t len) +{ + size_t have, need; + + /* Check how many bytes we already have and how many more we need. */ + have = (size_t)((ctx->count >> 3) & (MD4_BLOCK_LENGTH - 1)); + need = MD4_BLOCK_LENGTH - have; + + /* Update bitcount */ + ctx->count += (u64)len << 3; + + if (len >= need) { + if (have != 0) { + os_memcpy(ctx->buffer + have, input, need); + MD4Transform(ctx->state, ctx->buffer); + input += need; + len -= need; + have = 0; + } + + /* Process data in MD4_BLOCK_LENGTH-byte chunks. */ + while (len >= MD4_BLOCK_LENGTH) { + MD4Transform(ctx->state, input); + input += MD4_BLOCK_LENGTH; + len -= MD4_BLOCK_LENGTH; + } + } + + /* Handle any remaining bytes of data. */ + if (len != 0) + os_memcpy(ctx->buffer + have, input, len); +} + +/* + * Pad pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +static void MD4Pad(MD4_CTX *ctx) +{ + u8 count[8]; + size_t padlen; + + /* Convert count to 8 bytes in little endian order. */ + PUT_64BIT_LE(count, ctx->count); + + /* Pad out to 56 mod 64. */ + padlen = MD4_BLOCK_LENGTH - + ((ctx->count >> 3) & (MD4_BLOCK_LENGTH - 1)); + if (padlen < 1 + 8) + padlen += MD4_BLOCK_LENGTH; + MD4Update(ctx, PADDING, padlen - 8); /* padlen - 8 <= 64 */ + MD4Update(ctx, count, 8); +} + +/* + * Final wrapup--call MD4Pad, fill in digest and zero out ctx. + */ +static void MD4Final(unsigned char digest[MD4_DIGEST_LENGTH], MD4_CTX *ctx) +{ + int i; + + MD4Pad(ctx); + if (digest != NULL) { + for (i = 0; i < 4; i++) + PUT_32BIT_LE(digest + i * 4, ctx->state[i]); + os_memset(ctx, 0, sizeof(*ctx)); + } +} + + +/* The three core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) ((x & y) | (x & z) | (y & z)) +#define F3(x, y, z) (x ^ y ^ z) + +/* This is the central step in the MD4 algorithm. */ +#define MD4STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<<s | w>>(32-s) ) + +/* + * The core of the MD4 algorithm, this alters an existing MD4 hash to + * reflect the addition of 16 longwords of new data. MD4Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void +MD4Transform(u32 state[4], const u8 block[MD4_BLOCK_LENGTH]) +{ + u32 a, b, c, d, in[MD4_BLOCK_LENGTH / 4]; + +#if BYTE_ORDER == LITTLE_ENDIAN + os_memcpy(in, block, sizeof(in)); +#else + for (a = 0; a < MD4_BLOCK_LENGTH / 4; a++) { + in[a] = (u32)( + (u32)(block[a * 4 + 0]) | + (u32)(block[a * 4 + 1]) << 8 | + (u32)(block[a * 4 + 2]) << 16 | + (u32)(block[a * 4 + 3]) << 24); + } +#endif + + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + + MD4STEP(F1, a, b, c, d, in[ 0], 3); + MD4STEP(F1, d, a, b, c, in[ 1], 7); + MD4STEP(F1, c, d, a, b, in[ 2], 11); + MD4STEP(F1, b, c, d, a, in[ 3], 19); + MD4STEP(F1, a, b, c, d, in[ 4], 3); + MD4STEP(F1, d, a, b, c, in[ 5], 7); + MD4STEP(F1, c, d, a, b, in[ 6], 11); + MD4STEP(F1, b, c, d, a, in[ 7], 19); + MD4STEP(F1, a, b, c, d, in[ 8], 3); + MD4STEP(F1, d, a, b, c, in[ 9], 7); + MD4STEP(F1, c, d, a, b, in[10], 11); + MD4STEP(F1, b, c, d, a, in[11], 19); + MD4STEP(F1, a, b, c, d, in[12], 3); + MD4STEP(F1, d, a, b, c, in[13], 7); + MD4STEP(F1, c, d, a, b, in[14], 11); + MD4STEP(F1, b, c, d, a, in[15], 19); + + MD4STEP(F2, a, b, c, d, in[ 0] + 0x5a827999, 3); + MD4STEP(F2, d, a, b, c, in[ 4] + 0x5a827999, 5); + MD4STEP(F2, c, d, a, b, in[ 8] + 0x5a827999, 9); + MD4STEP(F2, b, c, d, a, in[12] + 0x5a827999, 13); + MD4STEP(F2, a, b, c, d, in[ 1] + 0x5a827999, 3); + MD4STEP(F2, d, a, b, c, in[ 5] + 0x5a827999, 5); + MD4STEP(F2, c, d, a, b, in[ 9] + 0x5a827999, 9); + MD4STEP(F2, b, c, d, a, in[13] + 0x5a827999, 13); + MD4STEP(F2, a, b, c, d, in[ 2] + 0x5a827999, 3); + MD4STEP(F2, d, a, b, c, in[ 6] + 0x5a827999, 5); + MD4STEP(F2, c, d, a, b, in[10] + 0x5a827999, 9); + MD4STEP(F2, b, c, d, a, in[14] + 0x5a827999, 13); + MD4STEP(F2, a, b, c, d, in[ 3] + 0x5a827999, 3); + MD4STEP(F2, d, a, b, c, in[ 7] + 0x5a827999, 5); + MD4STEP(F2, c, d, a, b, in[11] + 0x5a827999, 9); + MD4STEP(F2, b, c, d, a, in[15] + 0x5a827999, 13); + + MD4STEP(F3, a, b, c, d, in[ 0] + 0x6ed9eba1, 3); + MD4STEP(F3, d, a, b, c, in[ 8] + 0x6ed9eba1, 9); + MD4STEP(F3, c, d, a, b, in[ 4] + 0x6ed9eba1, 11); + MD4STEP(F3, b, c, d, a, in[12] + 0x6ed9eba1, 15); + MD4STEP(F3, a, b, c, d, in[ 2] + 0x6ed9eba1, 3); + MD4STEP(F3, d, a, b, c, in[10] + 0x6ed9eba1, 9); + MD4STEP(F3, c, d, a, b, in[ 6] + 0x6ed9eba1, 11); + MD4STEP(F3, b, c, d, a, in[14] + 0x6ed9eba1, 15); + MD4STEP(F3, a, b, c, d, in[ 1] + 0x6ed9eba1, 3); + MD4STEP(F3, d, a, b, c, in[ 9] + 0x6ed9eba1, 9); + MD4STEP(F3, c, d, a, b, in[ 5] + 0x6ed9eba1, 11); + MD4STEP(F3, b, c, d, a, in[13] + 0x6ed9eba1, 15); + MD4STEP(F3, a, b, c, d, in[ 3] + 0x6ed9eba1, 3); + MD4STEP(F3, d, a, b, c, in[11] + 0x6ed9eba1, 9); + MD4STEP(F3, c, d, a, b, in[ 7] + 0x6ed9eba1, 11); + MD4STEP(F3, b, c, d, a, in[15] + 0x6ed9eba1, 15); + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; +} +/* ===== end - public domain MD4 implementation ===== */ + +#endif /* INTERNAL_MD4 */ diff --git a/contrib/hostapd/md5.c b/contrib/hostapd/md5.c index 82388e0..a7db7aa 100644 --- a/contrib/hostapd/md5.c +++ b/contrib/hostapd/md5.c @@ -1,6 +1,6 @@ /* * MD5 hash implementation and interface functions - * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * 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 @@ -12,9 +12,7 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> +#include "includes.h" #include "common.h" #include "md5.h" @@ -35,9 +33,8 @@ void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, { u8 k_pad[64]; /* padding - key XORd with ipad/opad */ u8 tk[16]; - int i; const u8 *_addr[6]; - size_t _len[6]; + size_t i, _len[6]; if (num_elem > 5) { /* @@ -64,8 +61,8 @@ void hmac_md5_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++) @@ -80,8 +77,8 @@ void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, } md5_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; @@ -110,7 +107,7 @@ void hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, } -#ifndef EAP_TLS_FUNCS +#ifdef INTERNAL_MD5 struct MD5Context { u32 buf[4]; @@ -118,12 +115,15 @@ struct MD5Context { u8 in[64]; }; +#ifndef CONFIG_CRYPTO_INTERNAL static void MD5Init(struct MD5Context *context); static void MD5Update(struct MD5Context *context, unsigned char const *buf, - unsigned len); + unsigned len); static void MD5Final(unsigned char digest[16], struct MD5Context *context); +#endif /* CONFIG_CRYPTO_INTERNAL */ static void MD5Transform(u32 buf[4], u32 const in[16]); + typedef struct MD5Context MD5_CTX; @@ -137,7 +137,7 @@ typedef struct MD5Context MD5_CTX; void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { MD5_CTX ctx; - int i; + size_t i; MD5Init(&ctx); for (i = 0; i < num_elem; i++) @@ -186,7 +186,7 @@ static void byteReverse(unsigned char *buf, unsigned longs) * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious * initialization constants. */ -static void MD5Init(struct MD5Context *ctx) +void MD5Init(struct MD5Context *ctx) { ctx->buf[0] = 0x67452301; ctx->buf[1] = 0xefcdab89; @@ -201,8 +201,7 @@ static void MD5Init(struct MD5Context *ctx) * Update context to reflect the concatenation of another buffer full * of bytes. */ -static void MD5Update(struct MD5Context *ctx, unsigned char const *buf, - unsigned len) +void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) { u32 t; @@ -222,10 +221,10 @@ static void MD5Update(struct MD5Context *ctx, unsigned char const *buf, t = 64 - t; if (len < t) { - memcpy(p, buf, len); + os_memcpy(p, buf, len); return; } - memcpy(p, buf, t); + os_memcpy(p, buf, t); byteReverse(ctx->in, 16); MD5Transform(ctx->buf, (u32 *) ctx->in); buf += t; @@ -234,7 +233,7 @@ static void MD5Update(struct MD5Context *ctx, unsigned char const *buf, /* Process data in 64-byte chunks */ while (len >= 64) { - memcpy(ctx->in, buf, 64); + os_memcpy(ctx->in, buf, 64); byteReverse(ctx->in, 16); MD5Transform(ctx->buf, (u32 *) ctx->in); buf += 64; @@ -243,14 +242,14 @@ static void MD5Update(struct MD5Context *ctx, unsigned char const *buf, /* Handle any remaining bytes of data. */ - memcpy(ctx->in, buf, len); + os_memcpy(ctx->in, buf, len); } /* * Final wrapup - pad to 64-byte boundary with the bit pattern * 1 0* (64-bit count of bits processed, MSB-first) */ -static void MD5Final(unsigned char digest[16], struct MD5Context *ctx) +void MD5Final(unsigned char digest[16], struct MD5Context *ctx) { unsigned count; unsigned char *p; @@ -269,15 +268,15 @@ static void MD5Final(unsigned char digest[16], struct MD5Context *ctx) /* Pad out to 56 mod 64 */ if (count < 8) { /* Two lots of padding: Pad the first block to 64 bytes */ - memset(p, 0, count); + os_memset(p, 0, count); byteReverse(ctx->in, 16); MD5Transform(ctx->buf, (u32 *) ctx->in); /* Now fill the next block with 56 bytes */ - memset(ctx->in, 0, 56); + os_memset(ctx->in, 0, 56); } else { /* Pad block to 56 bytes */ - memset(p, 0, count - 8); + os_memset(p, 0, count - 8); } byteReverse(ctx->in, 14); @@ -287,8 +286,8 @@ static void MD5Final(unsigned char digest[16], struct MD5Context *ctx) MD5Transform(ctx->buf, (u32 *) ctx->in); byteReverse((unsigned char *) ctx->buf, 4); - memcpy(digest, ctx->buf, 16); - memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ + os_memcpy(digest, ctx->buf, 16); + os_memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ } /* The four core functions - F1 is optimized somewhat */ @@ -392,4 +391,4 @@ static void MD5Transform(u32 buf[4], u32 const in[16]) } /* ===== end - public domain MD5 implementation ===== */ -#endif /* !EAP_TLS_FUNCS */ +#endif /* INTERNAL_MD5 */ diff --git a/contrib/hostapd/md5.h b/contrib/hostapd/md5.h index a724804..e82f396 100644 --- a/contrib/hostapd/md5.h +++ b/contrib/hostapd/md5.h @@ -1,6 +1,6 @@ /* * MD5 hash implementation and interface functions - * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * 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 @@ -22,4 +22,13 @@ void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, void hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *mac); +#ifdef CONFIG_CRYPTO_INTERNAL +struct MD5Context; + +void MD5Init(struct MD5Context *context); +void MD5Update(struct MD5Context *context, unsigned char const *buf, + unsigned len); +void MD5Final(unsigned char digest[16], struct MD5Context *context); +#endif /* CONFIG_CRYPTO_INTERNAL */ + #endif /* MD5_H */ diff --git a/contrib/hostapd/milenage.c b/contrib/hostapd/milenage.c new file mode 100644 index 0000000..ab8bbd5 --- /dev/null +++ b/contrib/hostapd/milenage.c @@ -0,0 +1,1053 @@ +/* + * 3GPP AKA - Milenage algorithm (3GPP TS 35.205, .206, .207, .208) + * Copyright (c) 2006 <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published 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 an example authentication algorithm defined for 3GPP + * AKA. This can be used to implement a simple HLR/AuC into hlr_auc_gw to allow + * EAP-AKA to be tested properly with real USIM cards. + * + * This implementations assumes that the r1..r5 and c1..c5 constants defined in + * TS 35.206 are used, i.e., r1=64, r2=0, r3=32, r4=64, r5=96, c1=00..00, + * c2=00..01, c3=00..02, c4=00..04, c5=00..08. The block cipher is assumed to + * be AES (Rijndael). + */ + +#include "includes.h" + +#include "common.h" +#include "milenage.h" +#include "aes_wrap.h" + + +/** + * milenage_f1 - Milenage f1 and f1* algorithms + * @opc: OPc = 128-bit value derived from OP and K + * @k: K = 128-bit subscriber key + * @_rand: RAND = 128-bit random challenge + * @sqn: SQN = 48-bit sequence number + * @amf: AMF = 16-bit authentication management field + * @mac_a: Buffer for MAC-A = 64-bit network authentication code, or %NULL + * @mac_s: Buffer for MAC-S = 64-bit resync authentication code, or %NULL + */ +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) +{ + u8 tmp1[16], tmp2[16], tmp3[16]; + int i; + + /* tmp1 = TEMP = E_K(RAND XOR OP_C) */ + for (i = 0; i < 16; i++) + tmp1[i] = _rand[i] ^ opc[i]; + aes_128_encrypt_block(k, tmp1, tmp1); + + /* tmp2 = IN1 = SQN || AMF || SQN || AMF */ + memcpy(tmp2, sqn, 6); + memcpy(tmp2 + 6, amf, 2); + memcpy(tmp2 + 8, tmp2, 8); + + /* OUT1 = E_K(TEMP XOR rot(IN1 XOR OP_C, r1) XOR c1) XOR OP_C */ + + /* rotate (tmp2 XOR OP_C) by r1 (= 0x40 = 8 bytes) */ + for (i = 0; i < 16; i++) + tmp3[(i + 8) % 16] = tmp2[i] ^ opc[i]; + /* XOR with TEMP = E_K(RAND XOR OP_C) */ + for (i = 0; i < 16; i++) + tmp3[i] ^= tmp1[i]; + /* XOR with c1 (= ..00, i.e., NOP) */ + + /* f1 || f1* = E_K(tmp3) XOR OP_c */ + aes_128_encrypt_block(k, tmp3, tmp1); + for (i = 0; i < 16; i++) + tmp1[i] ^= opc[i]; + if (mac_a) + memcpy(mac_a, tmp1, 8); /* f1 */ + if (mac_s) + memcpy(mac_s, tmp1 + 8, 8); /* f1* */ +} + + +/** + * milenage_f2345 - Milenage f2, f3, f4, f5, f5* algorithms + * @opc: OPc = 128-bit value derived from OP and K + * @k: K = 128-bit subscriber key + * @_rand: RAND = 128-bit random challenge + * @res: Buffer for RES = 64-bit signed response (f2), or %NULL + * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL + * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL + * @ak: Buffer for AK = 48-bit anonymity key (f5), or %NULL + * @akstar: Buffer for AK = 48-bit anonymity key (f5*), or %NULL + */ +static void milenage_f2345(const u8 *opc, const u8 *k, const u8 *_rand, + u8 *res, u8 *ck, u8 *ik, u8 *ak, u8 *akstar) +{ + u8 tmp1[16], tmp2[16], tmp3[16]; + int i; + + /* tmp2 = TEMP = E_K(RAND XOR OP_C) */ + for (i = 0; i < 16; i++) + tmp1[i] = _rand[i] ^ opc[i]; + aes_128_encrypt_block(k, tmp1, tmp2); + + /* OUT2 = E_K(rot(TEMP XOR OP_C, r2) XOR c2) XOR OP_C */ + /* OUT3 = E_K(rot(TEMP XOR OP_C, r3) XOR c3) XOR OP_C */ + /* OUT4 = E_K(rot(TEMP XOR OP_C, r4) XOR c4) XOR OP_C */ + /* OUT5 = E_K(rot(TEMP XOR OP_C, r5) XOR c5) XOR OP_C */ + + /* f2 and f5 */ + /* rotate by r2 (= 0, i.e., NOP) */ + for (i = 0; i < 16; i++) + tmp1[i] = tmp2[i] ^ opc[i]; + tmp1[15] ^= 1; /* XOR c2 (= ..01) */ + /* f5 || f2 = E_K(tmp1) XOR OP_c */ + aes_128_encrypt_block(k, tmp1, tmp3); + for (i = 0; i < 16; i++) + tmp3[i] ^= opc[i]; + if (res) + memcpy(res, tmp3 + 8, 8); /* f2 */ + if (ak) + memcpy(ak, tmp3, 6); /* f5 */ + + /* f3 */ + if (ck) { + /* rotate by r3 = 0x20 = 4 bytes */ + for (i = 0; i < 16; i++) + tmp1[(i + 12) % 16] = tmp2[i] ^ opc[i]; + tmp1[15] ^= 2; /* XOR c3 (= ..02) */ + aes_128_encrypt_block(k, tmp1, ck); + for (i = 0; i < 16; i++) + ck[i] ^= opc[i]; + } + + /* f4 */ + if (ik) { + /* rotate by r4 = 0x40 = 8 bytes */ + for (i = 0; i < 16; i++) + tmp1[(i + 8) % 16] = tmp2[i] ^ opc[i]; + tmp1[15] ^= 4; /* XOR c4 (= ..04) */ + aes_128_encrypt_block(k, tmp1, ik); + for (i = 0; i < 16; i++) + ik[i] ^= opc[i]; + } + + /* f5* */ + if (akstar) { + /* rotate by r5 = 0x60 = 12 bytes */ + for (i = 0; i < 16; i++) + tmp1[(i + 4) % 16] = tmp2[i] ^ opc[i]; + tmp1[15] ^= 8; /* XOR c5 (= ..08) */ + aes_128_encrypt_block(k, tmp1, tmp1); + for (i = 0; i < 6; i++) + akstar[i] = tmp1[i] ^ opc[i]; + } +} + + +/** + * milenage_generate - Generate AKA AUTN,IK,CK,RES + * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.) + * @amf: AMF = 16-bit authentication management field + * @k: K = 128-bit subscriber key + * @sqn: SQN = 48-bit sequence number + * @_rand: RAND = 128-bit random challenge + * @autn: Buffer for AUTN = 128-bit authentication token + * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL + * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL + * @res: Buffer for RES = 64-bit signed response (f2), or %NULL + * @res_len: Max length for res; set to used length or 0 on failure + */ +void milenage_generate(const u8 *opc, const u8 *amf, const u8 *k, + const u8 *sqn, const u8 *_rand, u8 *autn, u8 *ik, + u8 *ck, u8 *res, size_t *res_len) +{ + int i; + u8 mac_a[16], ak[6]; + + if (*res_len < 8) { + *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); +} + + +/** + * milenage_auts - Milenage AUTS validation + * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.) + * @k: K = 128-bit subscriber key + * @_rand: RAND = 128-bit random challenge + * @auts: AUTS = 112-bit authentication token from client + * @sqn: Buffer for SQN = 48-bit sequence number + * Returns: 0 = success (sqn filled), -1 on failure + */ +int milenage_auts(const u8 *opc, const u8 *k, const u8 *_rand, const u8 *auts, + u8 *sqn) +{ + u8 amf[2] = { 0x00, 0x00 }; /* TS 33.102 v7.0.0, 6.3.3 */ + u8 ak[6], mac_s[8]; + int i; + + milenage_f2345(opc, k, _rand, NULL, NULL, NULL, NULL, ak); + 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) + return -1; + return 0; +} + + +/** + * gsm_milenage - Generate GSM-Milenage (3GPP TS 55.205) authentication triplet + * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.) + * @k: K = 128-bit subscriber key + * @_rand: RAND = 128-bit random challenge + * @sres: Buffer for SRES = 32-bit SRES + * @kc: Buffer for Kc = 64-bit Kc + */ +void 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); + + 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); +#else /* GSM_MILENAGE_ALT_SRES */ + for (i = 0; i < 4; i++) + sres[i] = res[i] ^ res[i + 4]; +#endif /* GSM_MILENAGE_ALT_SRES */ +} + + +#ifdef TEST_MAIN_MILENAGE + +extern int wpa_debug_level; + + +/** + * milenage_opc - Determine OPc from OP and K + * @op: OP = 128-bit operator variant algorithm configuration field + * @k: K = 128-bit subscriber key + * @opc: Buffer for OPc = 128-bit value derived from OP and K + */ +static void milenage_opc(const u8 *op, const u8 *k, u8 *opc) +{ + int i; + /* OP_C = OP XOR E_K(OP) */ + aes_128_encrypt_block(k, op, opc); + for (i = 0; i < 16; i++) + opc[i] ^= op[i]; +} + + +struct gsm_milenage_test_set { + u8 ki[16]; + u8 rand[16]; + u8 opc[16]; + u8 sres1[4]; + u8 sres2[4]; + u8 kc[8]; +}; + +static const struct gsm_milenage_test_set gsm_test_sets[] = +{ + { + /* 3GPP TS 55.205 v6.0.0 - Test Set 1 */ + { 0x46, 0x5b, 0x5c, 0xe8, 0xb1, 0x99, 0xb4, 0x9f, + 0xaa, 0x5f, 0x0a, 0x2e, 0xe2, 0x38, 0xa6, 0xbc }, + { 0x23, 0x55, 0x3c, 0xbe, 0x96, 0x37, 0xa8, 0x9d, + 0x21, 0x8a, 0xe6, 0x4d, 0xae, 0x47, 0xbf, 0x35 }, + { 0xcd, 0x63, 0xcb, 0x71, 0x95, 0x4a, 0x9f, 0x4e, + 0x48, 0xa5, 0x99, 0x4e, 0x37, 0xa0, 0x2b, 0xaf }, + { 0x46, 0xf8, 0x41, 0x6a }, + { 0xa5, 0x42, 0x11, 0xd5 }, + { 0xea, 0xe4, 0xbe, 0x82, 0x3a, 0xf9, 0xa0, 0x8b } + }, { + /* 3GPP TS 55.205 v6.0.0 - Test Set 2 */ + { 0xfe, 0xc8, 0x6b, 0xa6, 0xeb, 0x70, 0x7e, 0xd0, + 0x89, 0x05, 0x75, 0x7b, 0x1b, 0xb4, 0x4b, 0x8f }, + { 0x9f, 0x7c, 0x8d, 0x02, 0x1a, 0xcc, 0xf4, 0xdb, + 0x21, 0x3c, 0xcf, 0xf0, 0xc7, 0xf7, 0x1a, 0x6a }, + { 0x10, 0x06, 0x02, 0x0f, 0x0a, 0x47, 0x8b, 0xf6, + 0xb6, 0x99, 0xf1, 0x5c, 0x06, 0x2e, 0x42, 0xb3 }, + { 0x8c, 0x30, 0x8a, 0x5e }, + { 0x80, 0x11, 0xc4, 0x8c }, + { 0xaa, 0x01, 0x73, 0x9b, 0x8c, 0xaa, 0x97, 0x6d } + }, { + /* 3GPP TS 55.205 v6.0.0 - Test Set 3 */ + { 0x9e, 0x59, 0x44, 0xae, 0xa9, 0x4b, 0x81, 0x16, + 0x5c, 0x82, 0xfb, 0xf9, 0xf3, 0x2d, 0xb7, 0x51 }, + { 0xce, 0x83, 0xdb, 0xc5, 0x4a, 0xc0, 0x27, 0x4a, + 0x15, 0x7c, 0x17, 0xf8, 0x0d, 0x01, 0x7b, 0xd6 }, + { 0xa6, 0x4a, 0x50, 0x7a, 0xe1, 0xa2, 0xa9, 0x8b, + 0xb8, 0x8e, 0xb4, 0x21, 0x01, 0x35, 0xdc, 0x87 }, + { 0xcf, 0xbc, 0xe3, 0xfe }, + { 0xf3, 0x65, 0xcd, 0x68 }, + { 0x9a, 0x8e, 0xc9, 0x5f, 0x40, 0x8c, 0xc5, 0x07 } + }, { + /* 3GPP TS 55.205 v6.0.0 - Test Set 4 */ + { 0x4a, 0xb1, 0xde, 0xb0, 0x5c, 0xa6, 0xce, 0xb0, + 0x51, 0xfc, 0x98, 0xe7, 0x7d, 0x02, 0x6a, 0x84 }, + { 0x74, 0xb0, 0xcd, 0x60, 0x31, 0xa1, 0xc8, 0x33, + 0x9b, 0x2b, 0x6c, 0xe2, 0xb8, 0xc4, 0xa1, 0x86 }, + { 0xdc, 0xf0, 0x7c, 0xbd, 0x51, 0x85, 0x52, 0x90, + 0xb9, 0x2a, 0x07, 0xa9, 0x89, 0x1e, 0x52, 0x3e }, + { 0x96, 0x55, 0xe2, 0x65 }, + { 0x58, 0x60, 0xfc, 0x1b }, + { 0xcd, 0xc1, 0xdc, 0x08, 0x41, 0xb8, 0x1a, 0x22 } + }, { + /* 3GPP TS 55.205 v6.0.0 - Test Set 5 */ + { 0x6c, 0x38, 0xa1, 0x16, 0xac, 0x28, 0x0c, 0x45, + 0x4f, 0x59, 0x33, 0x2e, 0xe3, 0x5c, 0x8c, 0x4f }, + { 0xee, 0x64, 0x66, 0xbc, 0x96, 0x20, 0x2c, 0x5a, + 0x55, 0x7a, 0xbb, 0xef, 0xf8, 0xba, 0xbf, 0x63 }, + { 0x38, 0x03, 0xef, 0x53, 0x63, 0xb9, 0x47, 0xc6, + 0xaa, 0xa2, 0x25, 0xe5, 0x8f, 0xae, 0x39, 0x34 }, + { 0x13, 0x68, 0x8f, 0x17 }, + { 0x16, 0xc8, 0x23, 0x3f }, + { 0xdf, 0x75, 0xbc, 0x5e, 0xa8, 0x99, 0x87, 0x9f } + }, { + /* 3GPP TS 55.205 v6.0.0 - Test Set 6 */ + { 0x2d, 0x60, 0x9d, 0x4d, 0xb0, 0xac, 0x5b, 0xf0, + 0xd2, 0xc0, 0xde, 0x26, 0x70, 0x14, 0xde, 0x0d }, + { 0x19, 0x4a, 0xa7, 0x56, 0x01, 0x38, 0x96, 0xb7, + 0x4b, 0x4a, 0x2a, 0x3b, 0x0a, 0xf4, 0x53, 0x9e }, + { 0xc3, 0x5a, 0x0a, 0xb0, 0xbc, 0xbf, 0xc9, 0x25, + 0x2c, 0xaf, 0xf1, 0x5f, 0x24, 0xef, 0xbd, 0xe0 }, + { 0x55, 0x3d, 0x00, 0xb3 }, + { 0x8c, 0x25, 0xa1, 0x6c }, + { 0x84, 0xb4, 0x17, 0xae, 0x3a, 0xea, 0xb4, 0xf3 } + }, { + /* 3GPP TS 55.205 v6.0.0 - Test Set 7 */ + { 0xa5, 0x30, 0xa7, 0xfe, 0x42, 0x8f, 0xad, 0x10, + 0x82, 0xc4, 0x5e, 0xdd, 0xfc, 0xe1, 0x38, 0x84 }, + { 0x3a, 0x4c, 0x2b, 0x32, 0x45, 0xc5, 0x0e, 0xb5, + 0xc7, 0x1d, 0x08, 0x63, 0x93, 0x95, 0x76, 0x4d }, + { 0x27, 0x95, 0x3e, 0x49, 0xbc, 0x8a, 0xf6, 0xdc, + 0xc6, 0xe7, 0x30, 0xeb, 0x80, 0x28, 0x6b, 0xe3 }, + { 0x59, 0xf1, 0xa4, 0x4a }, + { 0xa6, 0x32, 0x41, 0xe1 }, + { 0x3b, 0x4e, 0x24, 0x4c, 0xdc, 0x60, 0xce, 0x03 } + }, { + /* 3GPP TS 55.205 v6.0.0 - Test Set 8 */ + { 0xd9, 0x15, 0x1c, 0xf0, 0x48, 0x96, 0xe2, 0x58, + 0x30, 0xbf, 0x2e, 0x08, 0x26, 0x7b, 0x83, 0x60 }, + { 0xf7, 0x61, 0xe5, 0xe9, 0x3d, 0x60, 0x3f, 0xeb, + 0x73, 0x0e, 0x27, 0x55, 0x6c, 0xb8, 0xa2, 0xca }, + { 0xc4, 0xc9, 0x3e, 0xff, 0xe8, 0xa0, 0x81, 0x38, + 0xc2, 0x03, 0xd4, 0xc2, 0x7c, 0xe4, 0xe3, 0xd9 }, + { 0x50, 0x58, 0x88, 0x61 }, + { 0x4a, 0x90, 0xb2, 0x17 }, + { 0x8d, 0x4e, 0xc0, 0x1d, 0xe5, 0x97, 0xac, 0xfe } + }, { + /* 3GPP TS 55.205 v6.0.0 - Test Set 9 */ + { 0xa0, 0xe2, 0x97, 0x1b, 0x68, 0x22, 0xe8, 0xd3, + 0x54, 0xa1, 0x8c, 0xc2, 0x35, 0x62, 0x4e, 0xcb }, + { 0x08, 0xef, 0xf8, 0x28, 0xb1, 0x3f, 0xdb, 0x56, + 0x27, 0x22, 0xc6, 0x5c, 0x7f, 0x30, 0xa9, 0xb2 }, + { 0x82, 0xa2, 0x6f, 0x22, 0xbb, 0xa9, 0xe9, 0x48, + 0x8f, 0x94, 0x9a, 0x10, 0xd9, 0x8e, 0x9c, 0xc4 }, + { 0xcd, 0xe6, 0xb0, 0x27 }, + { 0x4b, 0xc2, 0x21, 0x2d }, + { 0xd8, 0xde, 0xbc, 0x4f, 0xfb, 0xcd, 0x60, 0xaa } + }, { + /* 3GPP TS 55.205 v6.0.0 - Test Set 10 */ + { 0x0d, 0xa6, 0xf7, 0xba, 0x86, 0xd5, 0xea, 0xc8, + 0xa1, 0x9c, 0xf5, 0x63, 0xac, 0x58, 0x64, 0x2d }, + { 0x67, 0x9a, 0xc4, 0xdb, 0xac, 0xd7, 0xd2, 0x33, + 0xff, 0x9d, 0x68, 0x06, 0xf4, 0x14, 0x9c, 0xe3 }, + { 0x0d, 0xb1, 0x07, 0x1f, 0x87, 0x67, 0x56, 0x2c, + 0xa4, 0x3a, 0x0a, 0x64, 0xc4, 0x1e, 0x8d, 0x08 }, + { 0x02, 0xd1, 0x3a, 0xcd }, + { 0x6f, 0xc3, 0x0f, 0xee }, + { 0xf0, 0xea, 0xa5, 0x0a, 0x1e, 0xdc, 0xeb, 0xb7 } + }, { + /* 3GPP TS 55.205 v6.0.0 - Test Set 11 */ + { 0x77, 0xb4, 0x58, 0x43, 0xc8, 0x8e, 0x58, 0xc1, + 0x0d, 0x20, 0x26, 0x84, 0x51, 0x5e, 0xd4, 0x30 }, + { 0x4c, 0x47, 0xeb, 0x30, 0x76, 0xdc, 0x55, 0xfe, + 0x51, 0x06, 0xcb, 0x20, 0x34, 0xb8, 0xcd, 0x78 }, + { 0xd4, 0x83, 0xaf, 0xae, 0x56, 0x24, 0x09, 0xa3, + 0x26, 0xb5, 0xbb, 0x0b, 0x20, 0xc4, 0xd7, 0x62 }, + { 0x44, 0x38, 0x9d, 0x01 }, + { 0xae, 0xfa, 0x35, 0x7b }, + { 0x82, 0xdb, 0xab, 0x7f, 0x83, 0xf0, 0x63, 0xda } + }, { + /* 3GPP TS 55.205 v6.0.0 - Test Set 12 */ + { 0x72, 0x9b, 0x17, 0x72, 0x92, 0x70, 0xdd, 0x87, + 0xcc, 0xdf, 0x1b, 0xfe, 0x29, 0xb4, 0xe9, 0xbb }, + { 0x31, 0x1c, 0x4c, 0x92, 0x97, 0x44, 0xd6, 0x75, + 0xb7, 0x20, 0xf3, 0xb7, 0xe9, 0xb1, 0xcb, 0xd0 }, + { 0x22, 0x8c, 0x2f, 0x2f, 0x06, 0xac, 0x32, 0x68, + 0xa9, 0xe6, 0x16, 0xee, 0x16, 0xdb, 0x4b, 0xa1 }, + { 0x03, 0xe0, 0xfd, 0x84 }, + { 0x98, 0xdb, 0xbd, 0x09 }, + { 0x3c, 0x66, 0xcb, 0x98, 0xca, 0xb2, 0xd3, 0x3d } + }, { + /* 3GPP TS 55.205 v6.0.0 - Test Set 13 */ + { 0xd3, 0x2d, 0xd2, 0x3e, 0x89, 0xdc, 0x66, 0x23, + 0x54, 0xca, 0x12, 0xeb, 0x79, 0xdd, 0x32, 0xfa }, + { 0xcf, 0x7d, 0x0a, 0xb1, 0xd9, 0x43, 0x06, 0x95, + 0x0b, 0xf1, 0x20, 0x18, 0xfb, 0xd4, 0x68, 0x87 }, + { 0xd2, 0x2a, 0x4b, 0x41, 0x80, 0xa5, 0x32, 0x57, + 0x08, 0xa5, 0xff, 0x70, 0xd9, 0xf6, 0x7e, 0xc7 }, + { 0xbe, 0x73, 0xb3, 0xdc }, + { 0xaf, 0x4a, 0x41, 0x1e }, + { 0x96, 0x12, 0xb5, 0xd8, 0x8a, 0x41, 0x30, 0xbb } + }, { + /* 3GPP TS 55.205 v6.0.0 - Test Set 14 */ + { 0xaf, 0x7c, 0x65, 0xe1, 0x92, 0x72, 0x21, 0xde, + 0x59, 0x11, 0x87, 0xa2, 0xc5, 0x98, 0x7a, 0x53 }, + { 0x1f, 0x0f, 0x85, 0x78, 0x46, 0x4f, 0xd5, 0x9b, + 0x64, 0xbe, 0xd2, 0xd0, 0x94, 0x36, 0xb5, 0x7a }, + { 0xa4, 0xcf, 0x5c, 0x81, 0x55, 0xc0, 0x8a, 0x7e, + 0xff, 0x41, 0x8e, 0x54, 0x43, 0xb9, 0x8e, 0x55 }, + { 0x8f, 0xe0, 0x19, 0xc7 }, + { 0x7b, 0xff, 0xa5, 0xc2 }, + { 0x75, 0xa1, 0x50, 0xdf, 0x3c, 0x6a, 0xed, 0x08 } + }, { + /* 3GPP TS 55.205 v6.0.0 - Test Set 15 */ + { 0x5b, 0xd7, 0xec, 0xd3, 0xd3, 0x12, 0x7a, 0x41, + 0xd1, 0x25, 0x39, 0xbe, 0xd4, 0xe7, 0xcf, 0x71 }, + { 0x59, 0xb7, 0x5f, 0x14, 0x25, 0x1c, 0x75, 0x03, + 0x1d, 0x0b, 0xcb, 0xac, 0x1c, 0x2c, 0x04, 0xc7 }, + { 0x76, 0x08, 0x9d, 0x3c, 0x0f, 0xf3, 0xef, 0xdc, + 0x6e, 0x36, 0x72, 0x1d, 0x4f, 0xce, 0xb7, 0x47 }, + { 0x27, 0x20, 0x2b, 0x82 }, + { 0x7e, 0x3f, 0x44, 0xc7 }, + { 0xb7, 0xf9, 0x2e, 0x42, 0x6a, 0x36, 0xfe, 0xc5 } + }, { + /* 3GPP TS 55.205 v6.0.0 - Test Set 16 */ + { 0x6c, 0xd1, 0xc6, 0xce, 0xb1, 0xe0, 0x1e, 0x14, + 0xf1, 0xb8, 0x23, 0x16, 0xa9, 0x0b, 0x7f, 0x3d }, + { 0xf6, 0x9b, 0x78, 0xf3, 0x00, 0xa0, 0x56, 0x8b, + 0xce, 0x9f, 0x0c, 0xb9, 0x3c, 0x4b, 0xe4, 0xc9 }, + { 0xa2, 0x19, 0xdc, 0x37, 0xf1, 0xdc, 0x7d, 0x66, + 0x73, 0x8b, 0x58, 0x43, 0xc7, 0x99, 0xf2, 0x06 }, + { 0xdd, 0xd7, 0xef, 0xe6 }, + { 0x70, 0xf6, 0xbd, 0xb9 }, + { 0x88, 0xd9, 0xde, 0x10, 0xa2, 0x20, 0x04, 0xc5 } + }, { + /* 3GPP TS 55.205 v6.0.0 - Test Set 17 */ + { 0xb7, 0x3a, 0x90, 0xcb, 0xcf, 0x3a, 0xfb, 0x62, + 0x2d, 0xba, 0x83, 0xc5, 0x8a, 0x84, 0x15, 0xdf }, + { 0xb1, 0x20, 0xf1, 0xc1, 0xa0, 0x10, 0x2a, 0x2f, + 0x50, 0x7d, 0xd5, 0x43, 0xde, 0x68, 0x28, 0x1f }, + { 0xdf, 0x0c, 0x67, 0x86, 0x8f, 0xa2, 0x5f, 0x74, + 0x8b, 0x70, 0x44, 0xc6, 0xe7, 0xc2, 0x45, 0xb8 }, + { 0x67, 0xe4, 0xff, 0x3f }, + { 0x47, 0x9d, 0xd2, 0x5c }, + { 0xa8, 0x19, 0xe5, 0x77, 0xa8, 0xd6, 0x17, 0x5b } + }, { + /* 3GPP TS 55.205 v6.0.0 - Test Set 18 */ + { 0x51, 0x22, 0x25, 0x02, 0x14, 0xc3, 0x3e, 0x72, + 0x3a, 0x5d, 0xd5, 0x23, 0xfc, 0x14, 0x5f, 0xc0 }, + { 0x81, 0xe9, 0x2b, 0x6c, 0x0e, 0xe0, 0xe1, 0x2e, + 0xbc, 0xeb, 0xa8, 0xd9, 0x2a, 0x99, 0xdf, 0xa5 }, + { 0x98, 0x1d, 0x46, 0x4c, 0x7c, 0x52, 0xeb, 0x6e, + 0x50, 0x36, 0x23, 0x49, 0x84, 0xad, 0x0b, 0xcf }, + { 0x8a, 0x3b, 0x8d, 0x17 }, + { 0x28, 0xd7, 0xb0, 0xf2 }, + { 0x9a, 0x8d, 0x0e, 0x88, 0x3f, 0xf0, 0x88, 0x7a } + }, { + /* 3GPP TS 55.205 v6.0.0 - Test Set 19 */ + { 0x90, 0xdc, 0xa4, 0xed, 0xa4, 0x5b, 0x53, 0xcf, + 0x0f, 0x12, 0xd7, 0xc9, 0xc3, 0xbc, 0x6a, 0x89 }, + { 0x9f, 0xdd, 0xc7, 0x20, 0x92, 0xc6, 0xad, 0x03, + 0x6b, 0x6e, 0x46, 0x47, 0x89, 0x31, 0x5b, 0x78 }, + { 0xcb, 0x9c, 0xcc, 0xc4, 0xb9, 0x25, 0x8e, 0x6d, + 0xca, 0x47, 0x60, 0x37, 0x9f, 0xb8, 0x25, 0x81 }, + { 0xdf, 0x58, 0x52, 0x2f }, + { 0xa9, 0x51, 0x00, 0xe2 }, + { 0xed, 0x29, 0xb2, 0xf1, 0xc2, 0x7f, 0x9f, 0x34 } + } +}; + +#define NUM_GSM_TESTS (sizeof(gsm_test_sets) / sizeof(gsm_test_sets[0])) + + +struct milenage_test_set { + u8 k[16]; + u8 rand[16]; + u8 sqn[6]; + u8 amf[2]; + u8 op[16]; + u8 opc[16]; + u8 f1[8]; + u8 f1star[8]; + u8 f2[8]; + u8 f3[16]; + u8 f4[16]; + u8 f5[6]; + u8 f5star[6]; +}; + +static const struct milenage_test_set test_sets[] = +{ + { + /* 3GPP TS 35.208 v6.0.0 - 4.3.1 Test Set 1 */ + { 0x46, 0x5b, 0x5c, 0xe8, 0xb1, 0x99, 0xb4, 0x9f, + 0xaa, 0x5f, 0x0a, 0x2e, 0xe2, 0x38, 0xa6, 0xbc }, + { 0x23, 0x55, 0x3c, 0xbe, 0x96, 0x37, 0xa8, 0x9d, + 0x21, 0x8a, 0xe6, 0x4d, 0xae, 0x47, 0xbf, 0x35 }, + { 0xff, 0x9b, 0xb4, 0xd0, 0xb6, 0x07 }, + { 0xb9, 0xb9 }, + { 0xcd, 0xc2, 0x02, 0xd5, 0x12, 0x3e, 0x20, 0xf6, + 0x2b, 0x6d, 0x67, 0x6a, 0xc7, 0x2c, 0xb3, 0x18 }, + { 0xcd, 0x63, 0xcb, 0x71, 0x95, 0x4a, 0x9f, 0x4e, + 0x48, 0xa5, 0x99, 0x4e, 0x37, 0xa0, 0x2b, 0xaf }, + { 0x4a, 0x9f, 0xfa, 0xc3, 0x54, 0xdf, 0xaf, 0xb3 }, + { 0x01, 0xcf, 0xaf, 0x9e, 0xc4, 0xe8, 0x71, 0xe9 }, + { 0xa5, 0x42, 0x11, 0xd5, 0xe3, 0xba, 0x50, 0xbf }, + { 0xb4, 0x0b, 0xa9, 0xa3, 0xc5, 0x8b, 0x2a, 0x05, + 0xbb, 0xf0, 0xd9, 0x87, 0xb2, 0x1b, 0xf8, 0xcb }, + { 0xf7, 0x69, 0xbc, 0xd7, 0x51, 0x04, 0x46, 0x04, + 0x12, 0x76, 0x72, 0x71, 0x1c, 0x6d, 0x34, 0x41 }, + { 0xaa, 0x68, 0x9c, 0x64, 0x83, 0x70 }, + { 0x45, 0x1e, 0x8b, 0xec, 0xa4, 0x3b } + }, { + /* 3GPP TS 35.208 v6.0.0 - 4.3.2 Test Set 2 */ + { 0x46, 0x5b, 0x5c, 0xe8, 0xb1, 0x99, 0xb4, 0x9f, + 0xaa, 0x5f, 0x0a, 0x2e, 0xe2, 0x38, 0xa6, 0xbc }, + { 0x23, 0x55, 0x3c, 0xbe, 0x96, 0x37, 0xa8, 0x9d, + 0x21, 0x8a, 0xe6, 0x4d, 0xae, 0x47, 0xbf, 0x35 }, + { 0xff, 0x9b, 0xb4, 0xd0, 0xb6, 0x07 }, + { 0xb9, 0xb9 }, + { 0xcd, 0xc2, 0x02, 0xd5, 0x12, 0x3e, 0x20, 0xf6, + 0x2b, 0x6d, 0x67, 0x6a, 0xc7, 0x2c, 0xb3, 0x18 }, + { 0xcd, 0x63, 0xcb, 0x71, 0x95, 0x4a, 0x9f, 0x4e, + 0x48, 0xa5, 0x99, 0x4e, 0x37, 0xa0, 0x2b, 0xaf }, + { 0x4a, 0x9f, 0xfa, 0xc3, 0x54, 0xdf, 0xaf, 0xb3 }, + { 0x01, 0xcf, 0xaf, 0x9e, 0xc4, 0xe8, 0x71, 0xe9 }, + { 0xa5, 0x42, 0x11, 0xd5, 0xe3, 0xba, 0x50, 0xbf }, + { 0xb4, 0x0b, 0xa9, 0xa3, 0xc5, 0x8b, 0x2a, 0x05, + 0xbb, 0xf0, 0xd9, 0x87, 0xb2, 0x1b, 0xf8, 0xcb }, + { 0xf7, 0x69, 0xbc, 0xd7, 0x51, 0x04, 0x46, 0x04, + 0x12, 0x76, 0x72, 0x71, 0x1c, 0x6d, 0x34, 0x41 }, + { 0xaa, 0x68, 0x9c, 0x64, 0x83, 0x70 }, + { 0x45, 0x1e, 0x8b, 0xec, 0xa4, 0x3b } + }, { + /* 3GPP TS 35.208 v6.0.0 - 4.3.3 Test Set 3 */ + { 0xfe, 0xc8, 0x6b, 0xa6, 0xeb, 0x70, 0x7e, 0xd0, + 0x89, 0x05, 0x75, 0x7b, 0x1b, 0xb4, 0x4b, 0x8f }, + { 0x9f, 0x7c, 0x8d, 0x02, 0x1a, 0xcc, 0xf4, 0xdb, + 0x21, 0x3c, 0xcf, 0xf0, 0xc7, 0xf7, 0x1a, 0x6a }, + { 0x9d, 0x02, 0x77, 0x59, 0x5f, 0xfc }, + { 0x72, 0x5c }, + { 0xdb, 0xc5, 0x9a, 0xdc, 0xb6, 0xf9, 0xa0, 0xef, + 0x73, 0x54, 0x77, 0xb7, 0xfa, 0xdf, 0x83, 0x74 }, + { 0x10, 0x06, 0x02, 0x0f, 0x0a, 0x47, 0x8b, 0xf6, + 0xb6, 0x99, 0xf1, 0x5c, 0x06, 0x2e, 0x42, 0xb3 }, + { 0x9c, 0xab, 0xc3, 0xe9, 0x9b, 0xaf, 0x72, 0x81 }, + { 0x95, 0x81, 0x4b, 0xa2, 0xb3, 0x04, 0x43, 0x24 }, + { 0x80, 0x11, 0xc4, 0x8c, 0x0c, 0x21, 0x4e, 0xd2 }, + { 0x5d, 0xbd, 0xbb, 0x29, 0x54, 0xe8, 0xf3, 0xcd, + 0xe6, 0x65, 0xb0, 0x46, 0x17, 0x9a, 0x50, 0x98 }, + { 0x59, 0xa9, 0x2d, 0x3b, 0x47, 0x6a, 0x04, 0x43, + 0x48, 0x70, 0x55, 0xcf, 0x88, 0xb2, 0x30, 0x7b }, + { 0x33, 0x48, 0x4d, 0xc2, 0x13, 0x6b }, + { 0xde, 0xac, 0xdd, 0x84, 0x8c, 0xc6 } + }, { + /* 3GPP TS 35.208 v6.0.0 - 4.3.4 Test Set 4 */ + { 0x9e, 0x59, 0x44, 0xae, 0xa9, 0x4b, 0x81, 0x16, + 0x5c, 0x82, 0xfb, 0xf9, 0xf3, 0x2d, 0xb7, 0x51 }, + { 0xce, 0x83, 0xdb, 0xc5, 0x4a, 0xc0, 0x27, 0x4a, + 0x15, 0x7c, 0x17, 0xf8, 0x0d, 0x01, 0x7b, 0xd6 }, + { 0x0b, 0x60, 0x4a, 0x81, 0xec, 0xa8 }, + { 0x9e, 0x09 }, + { 0x22, 0x30, 0x14, 0xc5, 0x80, 0x66, 0x94, 0xc0, + 0x07, 0xca, 0x1e, 0xee, 0xf5, 0x7f, 0x00, 0x4f }, + { 0xa6, 0x4a, 0x50, 0x7a, 0xe1, 0xa2, 0xa9, 0x8b, + 0xb8, 0x8e, 0xb4, 0x21, 0x01, 0x35, 0xdc, 0x87 }, + { 0x74, 0xa5, 0x82, 0x20, 0xcb, 0xa8, 0x4c, 0x49 }, + { 0xac, 0x2c, 0xc7, 0x4a, 0x96, 0x87, 0x18, 0x37 }, + { 0xf3, 0x65, 0xcd, 0x68, 0x3c, 0xd9, 0x2e, 0x96 }, + { 0xe2, 0x03, 0xed, 0xb3, 0x97, 0x15, 0x74, 0xf5, + 0xa9, 0x4b, 0x0d, 0x61, 0xb8, 0x16, 0x34, 0x5d }, + { 0x0c, 0x45, 0x24, 0xad, 0xea, 0xc0, 0x41, 0xc4, + 0xdd, 0x83, 0x0d, 0x20, 0x85, 0x4f, 0xc4, 0x6b }, + { 0xf0, 0xb9, 0xc0, 0x8a, 0xd0, 0x2e }, + { 0x60, 0x85, 0xa8, 0x6c, 0x6f, 0x63 } + }, { + /* 3GPP TS 35.208 v6.0.0 - 4.3.5 Test Set 5 */ + { 0x4a, 0xb1, 0xde, 0xb0, 0x5c, 0xa6, 0xce, 0xb0, + 0x51, 0xfc, 0x98, 0xe7, 0x7d, 0x02, 0x6a, 0x84 }, + { 0x74, 0xb0, 0xcd, 0x60, 0x31, 0xa1, 0xc8, 0x33, + 0x9b, 0x2b, 0x6c, 0xe2, 0xb8, 0xc4, 0xa1, 0x86 }, + { 0xe8, 0x80, 0xa1, 0xb5, 0x80, 0xb6 }, + { 0x9f, 0x07 }, + { 0x2d, 0x16, 0xc5, 0xcd, 0x1f, 0xdf, 0x6b, 0x22, + 0x38, 0x35, 0x84, 0xe3, 0xbe, 0xf2, 0xa8, 0xd8 }, + { 0xdc, 0xf0, 0x7c, 0xbd, 0x51, 0x85, 0x52, 0x90, + 0xb9, 0x2a, 0x07, 0xa9, 0x89, 0x1e, 0x52, 0x3e }, + { 0x49, 0xe7, 0x85, 0xdd, 0x12, 0x62, 0x6e, 0xf2 }, + { 0x9e, 0x85, 0x79, 0x03, 0x36, 0xbb, 0x3f, 0xa2 }, + { 0x58, 0x60, 0xfc, 0x1b, 0xce, 0x35, 0x1e, 0x7e }, + { 0x76, 0x57, 0x76, 0x6b, 0x37, 0x3d, 0x1c, 0x21, + 0x38, 0xf3, 0x07, 0xe3, 0xde, 0x92, 0x42, 0xf9 }, + { 0x1c, 0x42, 0xe9, 0x60, 0xd8, 0x9b, 0x8f, 0xa9, + 0x9f, 0x27, 0x44, 0xe0, 0x70, 0x8c, 0xcb, 0x53 }, + { 0x31, 0xe1, 0x1a, 0x60, 0x91, 0x18 }, + { 0xfe, 0x25, 0x55, 0xe5, 0x4a, 0xa9 } + }, { + /* 3GPP TS 35.208 v6.0.0 - 4.3.6 Test Set 6 */ + { 0x6c, 0x38, 0xa1, 0x16, 0xac, 0x28, 0x0c, 0x45, + 0x4f, 0x59, 0x33, 0x2e, 0xe3, 0x5c, 0x8c, 0x4f }, + { 0xee, 0x64, 0x66, 0xbc, 0x96, 0x20, 0x2c, 0x5a, + 0x55, 0x7a, 0xbb, 0xef, 0xf8, 0xba, 0xbf, 0x63 }, + { 0x41, 0x4b, 0x98, 0x22, 0x21, 0x81 }, + { 0x44, 0x64 }, + { 0x1b, 0xa0, 0x0a, 0x1a, 0x7c, 0x67, 0x00, 0xac, + 0x8c, 0x3f, 0xf3, 0xe9, 0x6a, 0xd0, 0x87, 0x25 }, + { 0x38, 0x03, 0xef, 0x53, 0x63, 0xb9, 0x47, 0xc6, + 0xaa, 0xa2, 0x25, 0xe5, 0x8f, 0xae, 0x39, 0x34 }, + { 0x07, 0x8a, 0xdf, 0xb4, 0x88, 0x24, 0x1a, 0x57 }, + { 0x80, 0x24, 0x6b, 0x8d, 0x01, 0x86, 0xbc, 0xf1 }, + { 0x16, 0xc8, 0x23, 0x3f, 0x05, 0xa0, 0xac, 0x28 }, + { 0x3f, 0x8c, 0x75, 0x87, 0xfe, 0x8e, 0x4b, 0x23, + 0x3a, 0xf6, 0x76, 0xae, 0xde, 0x30, 0xba, 0x3b }, + { 0xa7, 0x46, 0x6c, 0xc1, 0xe6, 0xb2, 0xa1, 0x33, + 0x7d, 0x49, 0xd3, 0xb6, 0x6e, 0x95, 0xd7, 0xb4 }, + { 0x45, 0xb0, 0xf6, 0x9a, 0xb0, 0x6c }, + { 0x1f, 0x53, 0xcd, 0x2b, 0x11, 0x13 } + }, { + /* 3GPP TS 35.208 v6.0.0 - 4.3.7 Test Set 7 */ + { 0x2d, 0x60, 0x9d, 0x4d, 0xb0, 0xac, 0x5b, 0xf0, + 0xd2, 0xc0, 0xde, 0x26, 0x70, 0x14, 0xde, 0x0d }, + { 0x19, 0x4a, 0xa7, 0x56, 0x01, 0x38, 0x96, 0xb7, + 0x4b, 0x4a, 0x2a, 0x3b, 0x0a, 0xf4, 0x53, 0x9e }, + { 0x6b, 0xf6, 0x94, 0x38, 0xc2, 0xe4 }, + { 0x5f, 0x67 }, + { 0x46, 0x0a, 0x48, 0x38, 0x54, 0x27, 0xaa, 0x39, + 0x26, 0x4a, 0xac, 0x8e, 0xfc, 0x9e, 0x73, 0xe8 }, + { 0xc3, 0x5a, 0x0a, 0xb0, 0xbc, 0xbf, 0xc9, 0x25, + 0x2c, 0xaf, 0xf1, 0x5f, 0x24, 0xef, 0xbd, 0xe0 }, + { 0xbd, 0x07, 0xd3, 0x00, 0x3b, 0x9e, 0x5c, 0xc3 }, + { 0xbc, 0xb6, 0xc2, 0xfc, 0xad, 0x15, 0x22, 0x50 }, + { 0x8c, 0x25, 0xa1, 0x6c, 0xd9, 0x18, 0xa1, 0xdf }, + { 0x4c, 0xd0, 0x84, 0x60, 0x20, 0xf8, 0xfa, 0x07, + 0x31, 0xdd, 0x47, 0xcb, 0xdc, 0x6b, 0xe4, 0x11 }, + { 0x88, 0xab, 0x80, 0xa4, 0x15, 0xf1, 0x5c, 0x73, + 0x71, 0x12, 0x54, 0xa1, 0xd3, 0x88, 0xf6, 0x96 }, + { 0x7e, 0x64, 0x55, 0xf3, 0x4c, 0xf3 }, + { 0xdc, 0x6d, 0xd0, 0x1e, 0x8f, 0x15 } + }, { + /* 3GPP TS 35.208 v6.0.0 - 4.3.8 Test Set 8 */ + { 0xa5, 0x30, 0xa7, 0xfe, 0x42, 0x8f, 0xad, 0x10, + 0x82, 0xc4, 0x5e, 0xdd, 0xfc, 0xe1, 0x38, 0x84 }, + { 0x3a, 0x4c, 0x2b, 0x32, 0x45, 0xc5, 0x0e, 0xb5, + 0xc7, 0x1d, 0x08, 0x63, 0x93, 0x95, 0x76, 0x4d }, + { 0xf6, 0x3f, 0x5d, 0x76, 0x87, 0x84 }, + { 0xb9, 0x0e }, + { 0x51, 0x1c, 0x6c, 0x4e, 0x83, 0xe3, 0x8c, 0x89, + 0xb1, 0xc5, 0xd8, 0xdd, 0xe6, 0x24, 0x26, 0xfa }, + { 0x27, 0x95, 0x3e, 0x49, 0xbc, 0x8a, 0xf6, 0xdc, + 0xc6, 0xe7, 0x30, 0xeb, 0x80, 0x28, 0x6b, 0xe3 }, + { 0x53, 0x76, 0x1f, 0xbd, 0x67, 0x9b, 0x0b, 0xad }, + { 0x21, 0xad, 0xfd, 0x33, 0x4a, 0x10, 0xe7, 0xce }, + { 0xa6, 0x32, 0x41, 0xe1, 0xff, 0xc3, 0xe5, 0xab }, + { 0x10, 0xf0, 0x5b, 0xab, 0x75, 0xa9, 0x9a, 0x5f, + 0xbb, 0x98, 0xa9, 0xc2, 0x87, 0x67, 0x9c, 0x3b }, + { 0xf9, 0xec, 0x08, 0x65, 0xeb, 0x32, 0xf2, 0x23, + 0x69, 0xca, 0xde, 0x40, 0xc5, 0x9c, 0x3a, 0x44 }, + { 0x88, 0x19, 0x6c, 0x47, 0x98, 0x6f }, + { 0xc9, 0x87, 0xa3, 0xd2, 0x31, 0x15 } + }, { + /* 3GPP TS 35.208 v6.0.0 - 4.3.9 Test Set 9 */ + { 0xd9, 0x15, 0x1c, 0xf0, 0x48, 0x96, 0xe2, 0x58, + 0x30, 0xbf, 0x2e, 0x08, 0x26, 0x7b, 0x83, 0x60 }, + { 0xf7, 0x61, 0xe5, 0xe9, 0x3d, 0x60, 0x3f, 0xeb, + 0x73, 0x0e, 0x27, 0x55, 0x6c, 0xb8, 0xa2, 0xca }, + { 0x47, 0xee, 0x01, 0x99, 0x82, 0x0a }, + { 0x91, 0x13 }, + { 0x75, 0xfc, 0x22, 0x33, 0xa4, 0x42, 0x94, 0xee, + 0x8e, 0x6d, 0xe2, 0x5c, 0x43, 0x53, 0xd2, 0x6b }, + { 0xc4, 0xc9, 0x3e, 0xff, 0xe8, 0xa0, 0x81, 0x38, + 0xc2, 0x03, 0xd4, 0xc2, 0x7c, 0xe4, 0xe3, 0xd9 }, + { 0x66, 0xcc, 0x4b, 0xe4, 0x48, 0x62, 0xaf, 0x1f }, + { 0x7a, 0x4b, 0x8d, 0x7a, 0x87, 0x53, 0xf2, 0x46 }, + { 0x4a, 0x90, 0xb2, 0x17, 0x1a, 0xc8, 0x3a, 0x76 }, + { 0x71, 0x23, 0x6b, 0x71, 0x29, 0xf9, 0xb2, 0x2a, + 0xb7, 0x7e, 0xa7, 0xa5, 0x4c, 0x96, 0xda, 0x22 }, + { 0x90, 0x52, 0x7e, 0xba, 0xa5, 0x58, 0x89, 0x68, + 0xdb, 0x41, 0x72, 0x73, 0x25, 0xa0, 0x4d, 0x9e }, + { 0x82, 0xa0, 0xf5, 0x28, 0x7a, 0x71 }, + { 0x52, 0x7d, 0xbf, 0x41, 0xf3, 0x5f } + }, { + /* 3GPP TS 35.208 v6.0.0 - 4.3.10 Test Set 10 */ + { 0xa0, 0xe2, 0x97, 0x1b, 0x68, 0x22, 0xe8, 0xd3, + 0x54, 0xa1, 0x8c, 0xc2, 0x35, 0x62, 0x4e, 0xcb }, + { 0x08, 0xef, 0xf8, 0x28, 0xb1, 0x3f, 0xdb, 0x56, + 0x27, 0x22, 0xc6, 0x5c, 0x7f, 0x30, 0xa9, 0xb2 }, + { 0xdb, 0x5c, 0x06, 0x64, 0x81, 0xe0 }, + { 0x71, 0x6b }, + { 0x32, 0x37, 0x92, 0xfa, 0xca, 0x21, 0xfb, 0x4d, + 0x5d, 0x6f, 0x13, 0xc1, 0x45, 0xa9, 0xd2, 0xc1 }, + { 0x82, 0xa2, 0x6f, 0x22, 0xbb, 0xa9, 0xe9, 0x48, + 0x8f, 0x94, 0x9a, 0x10, 0xd9, 0x8e, 0x9c, 0xc4 }, + { 0x94, 0x85, 0xfe, 0x24, 0x62, 0x1c, 0xb9, 0xf6 }, + { 0xbc, 0xe3, 0x25, 0xce, 0x03, 0xe2, 0xe9, 0xb9 }, + { 0x4b, 0xc2, 0x21, 0x2d, 0x86, 0x24, 0x91, 0x0a }, + { 0x08, 0xce, 0xf6, 0xd0, 0x04, 0xec, 0x61, 0x47, + 0x1a, 0x3c, 0x3c, 0xda, 0x04, 0x81, 0x37, 0xfa }, + { 0xed, 0x03, 0x18, 0xca, 0x5d, 0xeb, 0x92, 0x06, + 0x27, 0x2f, 0x6e, 0x8f, 0xa6, 0x4b, 0xa4, 0x11 }, + { 0xa2, 0xf8, 0x58, 0xaa, 0x9e, 0x5d }, + { 0x74, 0xe7, 0x6f, 0xbb, 0xec, 0x38 } + }, { + /* 3GPP TS 35.208 v6.0.0 - 4.3.11 Test Set 11 */ + { 0x0d, 0xa6, 0xf7, 0xba, 0x86, 0xd5, 0xea, 0xc8, + 0xa1, 0x9c, 0xf5, 0x63, 0xac, 0x58, 0x64, 0x2d }, + { 0x67, 0x9a, 0xc4, 0xdb, 0xac, 0xd7, 0xd2, 0x33, + 0xff, 0x9d, 0x68, 0x06, 0xf4, 0x14, 0x9c, 0xe3 }, + { 0x6e, 0x23, 0x31, 0xd6, 0x92, 0xad }, + { 0x22, 0x4a }, + { 0x4b, 0x9a, 0x26, 0xfa, 0x45, 0x9e, 0x3a, 0xcb, + 0xff, 0x36, 0xf4, 0x01, 0x5d, 0xe3, 0xbd, 0xc1 }, + { 0x0d, 0xb1, 0x07, 0x1f, 0x87, 0x67, 0x56, 0x2c, + 0xa4, 0x3a, 0x0a, 0x64, 0xc4, 0x1e, 0x8d, 0x08 }, + { 0x28, 0x31, 0xd7, 0xae, 0x90, 0x88, 0xe4, 0x92 }, + { 0x9b, 0x2e, 0x16, 0x95, 0x11, 0x35, 0xd5, 0x23 }, + { 0x6f, 0xc3, 0x0f, 0xee, 0x6d, 0x12, 0x35, 0x23 }, + { 0x69, 0xb1, 0xca, 0xe7, 0xc7, 0x42, 0x9d, 0x97, + 0x5e, 0x24, 0x5c, 0xac, 0xb0, 0x5a, 0x51, 0x7c }, + { 0x74, 0xf2, 0x4e, 0x8c, 0x26, 0xdf, 0x58, 0xe1, + 0xb3, 0x8d, 0x7d, 0xcd, 0x4f, 0x1b, 0x7f, 0xbd }, + { 0x4c, 0x53, 0x9a, 0x26, 0xe1, 0xfa }, + { 0x07, 0x86, 0x1e, 0x12, 0x69, 0x28 } + }, { + /* 3GPP TS 35.208 v6.0.0 - 4.3.12 Test Set 12 */ + { 0x77, 0xb4, 0x58, 0x43, 0xc8, 0x8e, 0x58, 0xc1, + 0x0d, 0x20, 0x26, 0x84, 0x51, 0x5e, 0xd4, 0x30 }, + { 0x4c, 0x47, 0xeb, 0x30, 0x76, 0xdc, 0x55, 0xfe, + 0x51, 0x06, 0xcb, 0x20, 0x34, 0xb8, 0xcd, 0x78 }, + { 0xfe, 0x1a, 0x87, 0x31, 0x00, 0x5d }, + { 0xad, 0x25 }, + { 0xbf, 0x32, 0x86, 0xc7, 0xa5, 0x14, 0x09, 0xce, + 0x95, 0x72, 0x4d, 0x50, 0x3b, 0xfe, 0x6e, 0x70 }, + { 0xd4, 0x83, 0xaf, 0xae, 0x56, 0x24, 0x09, 0xa3, + 0x26, 0xb5, 0xbb, 0x0b, 0x20, 0xc4, 0xd7, 0x62 }, + { 0x08, 0x33, 0x2d, 0x7e, 0x9f, 0x48, 0x45, 0x70 }, + { 0xed, 0x41, 0xb7, 0x34, 0x48, 0x9d, 0x52, 0x07 }, + { 0xae, 0xfa, 0x35, 0x7b, 0xea, 0xc2, 0xa8, 0x7a }, + { 0x90, 0x8c, 0x43, 0xf0, 0x56, 0x9c, 0xb8, 0xf7, + 0x4b, 0xc9, 0x71, 0xe7, 0x06, 0xc3, 0x6c, 0x5f }, + { 0xc2, 0x51, 0xdf, 0x0d, 0x88, 0x8d, 0xd9, 0x32, + 0x9b, 0xcf, 0x46, 0x65, 0x5b, 0x22, 0x6e, 0x40 }, + { 0x30, 0xff, 0x25, 0xcd, 0xad, 0xf6 }, + { 0xe8, 0x4e, 0xd0, 0xd4, 0x67, 0x7e } + }, { + /* 3GPP TS 35.208 v6.0.0 - 4.3.13 Test Set 13 */ + { 0x72, 0x9b, 0x17, 0x72, 0x92, 0x70, 0xdd, 0x87, + 0xcc, 0xdf, 0x1b, 0xfe, 0x29, 0xb4, 0xe9, 0xbb }, + { 0x31, 0x1c, 0x4c, 0x92, 0x97, 0x44, 0xd6, 0x75, + 0xb7, 0x20, 0xf3, 0xb7, 0xe9, 0xb1, 0xcb, 0xd0 }, + { 0xc8, 0x5c, 0x4c, 0xf6, 0x59, 0x16 }, + { 0x5b, 0xb2 }, + { 0xd0, 0x4c, 0x9c, 0x35, 0xbd, 0x22, 0x62, 0xfa, + 0x81, 0x0d, 0x29, 0x24, 0xd0, 0x36, 0xfd, 0x13 }, + { 0x22, 0x8c, 0x2f, 0x2f, 0x06, 0xac, 0x32, 0x68, + 0xa9, 0xe6, 0x16, 0xee, 0x16, 0xdb, 0x4b, 0xa1 }, + { 0xff, 0x79, 0x4f, 0xe2, 0xf8, 0x27, 0xeb, 0xf8 }, + { 0x24, 0xfe, 0x4d, 0xc6, 0x1e, 0x87, 0x4b, 0x52 }, + { 0x98, 0xdb, 0xbd, 0x09, 0x9b, 0x3b, 0x40, 0x8d }, + { 0x44, 0xc0, 0xf2, 0x3c, 0x54, 0x93, 0xcf, 0xd2, + 0x41, 0xe4, 0x8f, 0x19, 0x7e, 0x1d, 0x10, 0x12 }, + { 0x0c, 0x9f, 0xb8, 0x16, 0x13, 0x88, 0x4c, 0x25, + 0x35, 0xdd, 0x0e, 0xab, 0xf3, 0xb4, 0x40, 0xd8 }, + { 0x53, 0x80, 0xd1, 0x58, 0xcf, 0xe3 }, + { 0x87, 0xac, 0x3b, 0x55, 0x9f, 0xb6 } + }, { + /* 3GPP TS 35.208 v6.0.0 - 4.3.14 Test Set 14 */ + { 0xd3, 0x2d, 0xd2, 0x3e, 0x89, 0xdc, 0x66, 0x23, + 0x54, 0xca, 0x12, 0xeb, 0x79, 0xdd, 0x32, 0xfa }, + { 0xcf, 0x7d, 0x0a, 0xb1, 0xd9, 0x43, 0x06, 0x95, + 0x0b, 0xf1, 0x20, 0x18, 0xfb, 0xd4, 0x68, 0x87 }, + { 0x48, 0x41, 0x07, 0xe5, 0x6a, 0x43 }, + { 0xb5, 0xe6 }, + { 0xfe, 0x75, 0x90, 0x5b, 0x9d, 0xa4, 0x7d, 0x35, + 0x62, 0x36, 0xd0, 0x31, 0x4e, 0x09, 0xc3, 0x2e }, + { 0xd2, 0x2a, 0x4b, 0x41, 0x80, 0xa5, 0x32, 0x57, + 0x08, 0xa5, 0xff, 0x70, 0xd9, 0xf6, 0x7e, 0xc7 }, + { 0xcf, 0x19, 0xd6, 0x2b, 0x6a, 0x80, 0x98, 0x66 }, + { 0x5d, 0x26, 0x95, 0x37, 0xe4, 0x5e, 0x2c, 0xe6 }, + { 0xaf, 0x4a, 0x41, 0x1e, 0x11, 0x39, 0xf2, 0xc2 }, + { 0x5a, 0xf8, 0x6b, 0x80, 0xed, 0xb7, 0x0d, 0xf5, + 0x29, 0x2c, 0xc1, 0x12, 0x1c, 0xba, 0xd5, 0x0c }, + { 0x7f, 0x4d, 0x6a, 0xe7, 0x44, 0x0e, 0x18, 0x78, + 0x9a, 0x8b, 0x75, 0xad, 0x3f, 0x42, 0xf0, 0x3a }, + { 0x21, 0x7a, 0xf4, 0x92, 0x72, 0xad }, + { 0x90, 0x0e, 0x10, 0x1c, 0x67, 0x7e } + }, { + /* 3GPP TS 35.208 v6.0.0 - 4.3.15 Test Set 15 */ + { 0xaf, 0x7c, 0x65, 0xe1, 0x92, 0x72, 0x21, 0xde, + 0x59, 0x11, 0x87, 0xa2, 0xc5, 0x98, 0x7a, 0x53 }, + { 0x1f, 0x0f, 0x85, 0x78, 0x46, 0x4f, 0xd5, 0x9b, + 0x64, 0xbe, 0xd2, 0xd0, 0x94, 0x36, 0xb5, 0x7a }, + { 0x3d, 0x62, 0x7b, 0x01, 0x41, 0x8d }, + { 0x84, 0xf6 }, + { 0x0c, 0x7a, 0xcb, 0x8d, 0x95, 0xb7, 0xd4, 0xa3, + 0x1c, 0x5a, 0xca, 0x6d, 0x26, 0x34, 0x5a, 0x88 }, + { 0xa4, 0xcf, 0x5c, 0x81, 0x55, 0xc0, 0x8a, 0x7e, + 0xff, 0x41, 0x8e, 0x54, 0x43, 0xb9, 0x8e, 0x55 }, + { 0xc3, 0x7c, 0xae, 0x78, 0x05, 0x64, 0x20, 0x32 }, + { 0x68, 0xcd, 0x09, 0xa4, 0x52, 0xd8, 0xdb, 0x7c }, + { 0x7b, 0xff, 0xa5, 0xc2, 0xf4, 0x1f, 0xbc, 0x05 }, + { 0x3f, 0x8c, 0x3f, 0x3c, 0xcf, 0x76, 0x25, 0xbf, + 0x77, 0xfc, 0x94, 0xbc, 0xfd, 0x22, 0xfd, 0x26 }, + { 0xab, 0xcb, 0xae, 0x8f, 0xd4, 0x61, 0x15, 0xe9, + 0x96, 0x1a, 0x55, 0xd0, 0xda, 0x5f, 0x20, 0x78 }, + { 0x83, 0x7f, 0xd7, 0xb7, 0x44, 0x19 }, + { 0x56, 0xe9, 0x7a, 0x60, 0x90, 0xb1 } + }, { + /* 3GPP TS 35.208 v6.0.0 - 4.3.16 Test Set 16 */ + { 0x5b, 0xd7, 0xec, 0xd3, 0xd3, 0x12, 0x7a, 0x41, + 0xd1, 0x25, 0x39, 0xbe, 0xd4, 0xe7, 0xcf, 0x71 }, + { 0x59, 0xb7, 0x5f, 0x14, 0x25, 0x1c, 0x75, 0x03, + 0x1d, 0x0b, 0xcb, 0xac, 0x1c, 0x2c, 0x04, 0xc7 }, + { 0xa2, 0x98, 0xae, 0x89, 0x29, 0xdc }, + { 0xd0, 0x56 }, + { 0xf9, 0x67, 0xf7, 0x60, 0x38, 0xb9, 0x20, 0xa9, + 0xcd, 0x25, 0xe1, 0x0c, 0x08, 0xb4, 0x99, 0x24 }, + { 0x76, 0x08, 0x9d, 0x3c, 0x0f, 0xf3, 0xef, 0xdc, + 0x6e, 0x36, 0x72, 0x1d, 0x4f, 0xce, 0xb7, 0x47 }, + { 0xc3, 0xf2, 0x5c, 0xd9, 0x43, 0x09, 0x10, 0x7e }, + { 0xb0, 0xc8, 0xba, 0x34, 0x36, 0x65, 0xaf, 0xcc }, + { 0x7e, 0x3f, 0x44, 0xc7, 0x59, 0x1f, 0x6f, 0x45 }, + { 0xd4, 0x2b, 0x2d, 0x61, 0x5e, 0x49, 0xa0, 0x3a, + 0xc2, 0x75, 0xa5, 0xae, 0xf9, 0x7a, 0xf8, 0x92 }, + { 0x0b, 0x3f, 0x8d, 0x02, 0x4f, 0xe6, 0xbf, 0xaf, + 0xaa, 0x98, 0x2b, 0x8f, 0x82, 0xe3, 0x19, 0xc2 }, + { 0x5b, 0xe1, 0x14, 0x95, 0x52, 0x5d }, + { 0x4d, 0x6a, 0x34, 0xa1, 0xe4, 0xeb } + }, { + /* 3GPP TS 35.208 v6.0.0 - 4.3.17 Test Set 17 */ + { 0x6c, 0xd1, 0xc6, 0xce, 0xb1, 0xe0, 0x1e, 0x14, + 0xf1, 0xb8, 0x23, 0x16, 0xa9, 0x0b, 0x7f, 0x3d }, + { 0xf6, 0x9b, 0x78, 0xf3, 0x00, 0xa0, 0x56, 0x8b, + 0xce, 0x9f, 0x0c, 0xb9, 0x3c, 0x4b, 0xe4, 0xc9 }, + { 0xb4, 0xfc, 0xe5, 0xfe, 0xb0, 0x59 }, + { 0xe4, 0xbb }, + { 0x07, 0x8b, 0xfc, 0xa9, 0x56, 0x46, 0x59, 0xec, + 0xd8, 0x85, 0x1e, 0x84, 0xe6, 0xc5, 0x9b, 0x48 }, + { 0xa2, 0x19, 0xdc, 0x37, 0xf1, 0xdc, 0x7d, 0x66, + 0x73, 0x8b, 0x58, 0x43, 0xc7, 0x99, 0xf2, 0x06 }, + { 0x69, 0xa9, 0x08, 0x69, 0xc2, 0x68, 0xcb, 0x7b }, + { 0x2e, 0x0f, 0xdc, 0xf9, 0xfd, 0x1c, 0xfa, 0x6a }, + { 0x70, 0xf6, 0xbd, 0xb9, 0xad, 0x21, 0x52, 0x5f }, + { 0x6e, 0xda, 0xf9, 0x9e, 0x5b, 0xd9, 0xf8, 0x5d, + 0x5f, 0x36, 0xd9, 0x1c, 0x12, 0x72, 0xfb, 0x4b }, + { 0xd6, 0x1c, 0x85, 0x3c, 0x28, 0x0d, 0xd9, 0xc4, + 0x6f, 0x29, 0x7b, 0xae, 0xc3, 0x86, 0xde, 0x17 }, + { 0x1c, 0x40, 0x8a, 0x85, 0x8b, 0x3e }, + { 0xaa, 0x4a, 0xe5, 0x2d, 0xaa, 0x30 } + }, { + /* 3GPP TS 35.208 v6.0.0 - 4.3.18 Test Set 18 */ + { 0xb7, 0x3a, 0x90, 0xcb, 0xcf, 0x3a, 0xfb, 0x62, + 0x2d, 0xba, 0x83, 0xc5, 0x8a, 0x84, 0x15, 0xdf }, + { 0xb1, 0x20, 0xf1, 0xc1, 0xa0, 0x10, 0x2a, 0x2f, + 0x50, 0x7d, 0xd5, 0x43, 0xde, 0x68, 0x28, 0x1f }, + { 0xf1, 0xe8, 0xa5, 0x23, 0xa3, 0x6d }, + { 0x47, 0x1b }, + { 0xb6, 0x72, 0x04, 0x7e, 0x00, 0x3b, 0xb9, 0x52, + 0xdc, 0xa6, 0xcb, 0x8a, 0xf0, 0xe5, 0xb7, 0x79 }, + { 0xdf, 0x0c, 0x67, 0x86, 0x8f, 0xa2, 0x5f, 0x74, + 0x8b, 0x70, 0x44, 0xc6, 0xe7, 0xc2, 0x45, 0xb8 }, + { 0xeb, 0xd7, 0x03, 0x41, 0xbc, 0xd4, 0x15, 0xb0 }, + { 0x12, 0x35, 0x9f, 0x5d, 0x82, 0x22, 0x0c, 0x14 }, + { 0x47, 0x9d, 0xd2, 0x5c, 0x20, 0x79, 0x2d, 0x63 }, + { 0x66, 0x19, 0x5d, 0xbe, 0xd0, 0x31, 0x32, 0x74, + 0xc5, 0xca, 0x77, 0x66, 0x61, 0x5f, 0xa2, 0x5e }, + { 0x66, 0xbe, 0xc7, 0x07, 0xeb, 0x2a, 0xfc, 0x47, + 0x6d, 0x74, 0x08, 0xa8, 0xf2, 0x92, 0x7b, 0x36 }, + { 0xae, 0xfd, 0xaa, 0x5d, 0xdd, 0x99 }, + { 0x12, 0xec, 0x2b, 0x87, 0xfb, 0xb1 } + }, { + /* 3GPP TS 35.208 v6.0.0 - 4.3.19 Test Set 19 */ + { 0x51, 0x22, 0x25, 0x02, 0x14, 0xc3, 0x3e, 0x72, + 0x3a, 0x5d, 0xd5, 0x23, 0xfc, 0x14, 0x5f, 0xc0 }, + { 0x81, 0xe9, 0x2b, 0x6c, 0x0e, 0xe0, 0xe1, 0x2e, + 0xbc, 0xeb, 0xa8, 0xd9, 0x2a, 0x99, 0xdf, 0xa5 }, + { 0x16, 0xf3, 0xb3, 0xf7, 0x0f, 0xc2 }, + { 0xc3, 0xab }, + { 0xc9, 0xe8, 0x76, 0x32, 0x86, 0xb5, 0xb9, 0xff, + 0xbd, 0xf5, 0x6e, 0x12, 0x97, 0xd0, 0x88, 0x7b }, + { 0x98, 0x1d, 0x46, 0x4c, 0x7c, 0x52, 0xeb, 0x6e, + 0x50, 0x36, 0x23, 0x49, 0x84, 0xad, 0x0b, 0xcf }, + { 0x2a, 0x5c, 0x23, 0xd1, 0x5e, 0xe3, 0x51, 0xd5 }, + { 0x62, 0xda, 0xe3, 0x85, 0x3f, 0x3a, 0xf9, 0xd2 }, + { 0x28, 0xd7, 0xb0, 0xf2, 0xa2, 0xec, 0x3d, 0xe5 }, + { 0x53, 0x49, 0xfb, 0xe0, 0x98, 0x64, 0x9f, 0x94, + 0x8f, 0x5d, 0x2e, 0x97, 0x3a, 0x81, 0xc0, 0x0f }, + { 0x97, 0x44, 0x87, 0x1a, 0xd3, 0x2b, 0xf9, 0xbb, + 0xd1, 0xdd, 0x5c, 0xe5, 0x4e, 0x3e, 0x2e, 0x5a }, + { 0xad, 0xa1, 0x5a, 0xeb, 0x7b, 0xb8 }, + { 0xd4, 0x61, 0xbc, 0x15, 0x47, 0x5d } + }, { + /* 3GPP TS 35.208 v6.0.0 - 4.3.20 Test Set 20 */ + { 0x90, 0xdc, 0xa4, 0xed, 0xa4, 0x5b, 0x53, 0xcf, + 0x0f, 0x12, 0xd7, 0xc9, 0xc3, 0xbc, 0x6a, 0x89 }, + { 0x9f, 0xdd, 0xc7, 0x20, 0x92, 0xc6, 0xad, 0x03, + 0x6b, 0x6e, 0x46, 0x47, 0x89, 0x31, 0x5b, 0x78 }, + { 0x20, 0xf8, 0x13, 0xbd, 0x41, 0x41 }, + { 0x61, 0xdf }, + { 0x3f, 0xfc, 0xfe, 0x5b, 0x7b, 0x11, 0x11, 0x58, + 0x99, 0x20, 0xd3, 0x52, 0x8e, 0x84, 0xe6, 0x55 }, + { 0xcb, 0x9c, 0xcc, 0xc4, 0xb9, 0x25, 0x8e, 0x6d, + 0xca, 0x47, 0x60, 0x37, 0x9f, 0xb8, 0x25, 0x81 }, + { 0x09, 0xdb, 0x94, 0xea, 0xb4, 0xf8, 0x14, 0x9e }, + { 0xa2, 0x94, 0x68, 0xaa, 0x97, 0x75, 0xb5, 0x27 }, + { 0xa9, 0x51, 0x00, 0xe2, 0x76, 0x09, 0x52, 0xcd }, + { 0xb5, 0xf2, 0xda, 0x03, 0x88, 0x3b, 0x69, 0xf9, + 0x6b, 0xf5, 0x2e, 0x02, 0x9e, 0xd9, 0xac, 0x45 }, + { 0xb4, 0x72, 0x13, 0x68, 0xbc, 0x16, 0xea, 0x67, + 0x87, 0x5c, 0x55, 0x98, 0x68, 0x8b, 0xb0, 0xef }, + { 0x83, 0xcf, 0xd5, 0x4d, 0xb9, 0x13 }, + { 0x4f, 0x20, 0x39, 0x39, 0x2d, 0xdc } + } +}; + +#define NUM_TESTS (sizeof(test_sets) / sizeof(test_sets[0])) + + +int main(int argc, char *argv[]) +{ + u8 buf[16], buf2[16], buf3[16], buf4[16], buf5[16], opc[16]; + u8 auts[14], sqn[6], _rand[16]; + int ret = 0, res, i; + const struct milenage_test_set *t; + size_t res_len; + + wpa_debug_level = 0; + + printf("Milenage test sets\n"); + for (i = 0; i < NUM_TESTS; i++) { + t = &test_sets[i]; + printf("Test Set %d\n", i + 1); + + milenage_opc(t->op, t->k, opc); + if (memcmp(opc, t->opc, 16) != 0) { + printf("- milenage_opc failed\n"); + ret++; + } + + milenage_f1(opc, t->k, t->rand, t->sqn, t->amf, buf, buf2); + if (memcmp(buf, t->f1, 8) != 0) { + printf("- milenage_f1 failed\n"); + ret++; + } + if (memcmp(buf2, t->f1star, 8) != 0) { + printf("- milenage_f1* failed\n"); + ret++; + } + + milenage_f2345(opc, t->k, t->rand, buf, buf2, buf3, buf4, + buf5); + if (memcmp(buf, t->f2, 8) != 0) { + printf("- milenage_f2 failed\n"); + ret++; + } + if (memcmp(buf2, t->f3, 16) != 0) { + printf("- milenage_f3 failed\n"); + ret++; + } + if (memcmp(buf3, t->f4, 16) != 0) { + printf("- milenage_f4 failed\n"); + ret++; + } + if (memcmp(buf4, t->f5, 6) != 0) { + printf("- milenage_f5 failed\n"); + ret++; + } + if (memcmp(buf5, t->f5star, 6) != 0) { + printf("- milenage_f5* failed\n"); + ret++; + } + } + + 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); + 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); + 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]); + if (res) + 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); + res_len = 8; + milenage_generate(t->opc, t->amf, t->k, sqn, _rand, buf, buf2, buf3, + buf4, &res_len); + wpa_hexdump(MSG_DEBUG, "SQN", sqn, 6); + wpa_hexdump(MSG_DEBUG, "RAND", _rand, 16); + wpa_hexdump(MSG_DEBUG, "AUTN", buf, 16); + wpa_hexdump(MSG_DEBUG, "IK", buf2, 16); + wpa_hexdump(MSG_DEBUG, "CK", buf3, 16); + wpa_hexdump(MSG_DEBUG, "RES", buf4, res_len); + + printf("GSM-Milenage test sets\n"); + for (i = 0; i < NUM_GSM_TESTS; i++) { + const struct gsm_milenage_test_set *g; + u8 sres[4], kc[8]; + g = &gsm_test_sets[i]; + printf("Test Set %d\n", i + 1); + gsm_milenage(g->opc, g->ki, g->rand, sres, kc); + if (memcmp(g->kc, kc, 8) != 0) { + printf("- gsm_milenage Kc failed\n"); + ret++; + } +#ifdef GSM_MILENAGE_ALT_SRES + if (memcmp(g->sres2, sres, 4) != 0) { + printf("- gsm_milenage SRES#2 failed\n"); + ret++; + } +#else /* GSM_MILENAGE_ALT_SRES */ + if (memcmp(g->sres1, sres, 4) != 0) { + printf("- gsm_milenage SRES#1 failed\n"); + ret++; + } +#endif /* GSM_MILENAGE_ALT_SRES */ + } + + if (ret) + printf("Something failed\n"); + else + printf("OK\n"); + + return ret; +} +#endif /* TEST_MAIN_MILENAGE */ diff --git a/contrib/hostapd/milenage.h b/contrib/hostapd/milenage.h new file mode 100644 index 0000000..cc184f0 --- /dev/null +++ b/contrib/hostapd/milenage.h @@ -0,0 +1,26 @@ +/* + * UMTS AKA - Milenage algorithm (3GPP TS 35.205, .206, .207, .208) + * Copyright (c) 2006 <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published 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 MILENAGE_H +#define MILENAGE_H + +void milenage_generate(const u8 *opc, const u8 *amf, const u8 *k, + const u8 *sqn, const u8 *_rand, u8 *autn, u8 *ik, + u8 *ck, u8 *res, size_t *res_len); +int milenage_auts(const u8 *opc, const u8 *k, const u8 *_rand, const u8 *auts, + u8 *sqn); +void gsm_milenage(const u8 *opc, const u8 *k, const u8 *_rand, u8 *sres, + u8 *kc); + +#endif /* MILENAGE_H */ diff --git a/contrib/hostapd/mlme.c b/contrib/hostapd/mlme.c new file mode 100644 index 0000000..3d4cf21 --- /dev/null +++ b/contrib/hostapd/mlme.c @@ -0,0 +1,176 @@ +/* + * hostapd / IEEE 802.11 MLME + * Copyright 2003-2006, Jouni Malinen <j@w1.fi> + * Copyright 2003-2004, 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 "ieee802_11.h" +#include "sta_info.h" +#include "wpa.h" +#include "mlme.h" + + +static const char * mlme_auth_alg_str(int alg) +{ + switch (alg) { + case WLAN_AUTH_OPEN: + return "OPEN_SYSTEM"; + case WLAN_AUTH_SHARED_KEY: + return "SHARED_KEY"; + } + + return "unknown"; +} + + +/** + * mlme_authenticate_indication - Report the establishment of an authentication + * relationship with a specific peer MAC entity + * @hapd: BSS data + * @sta: peer STA data + * + * MLME calls this function as a result of the establishment of an + * authentication relationship with a specific peer MAC entity that + * resulted from an authentication procedure that was initiated by + * that specific peer MAC entity. + * + * PeerSTAAddress = sta->addr + * AuthenticationType = sta->auth_alg (WLAN_AUTH_OPEN / WLAN_AUTH_SHARED_KEY) + */ +void mlme_authenticate_indication(struct hostapd_data *hapd, + struct sta_info *sta) +{ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME, + HOSTAPD_LEVEL_DEBUG, + "MLME-AUTHENTICATE.indication(" MACSTR ", %s)", + MAC2STR(sta->addr), mlme_auth_alg_str(sta->auth_alg)); + mlme_deletekeys_request(hapd, sta); +} + + +/** + * mlme_deauthenticate_indication - Report the invalidation of an + * authentication relationship with a specific peer MAC entity + * @hapd: BSS data + * @sta: Peer STA data + * @reason_code: ReasonCode from Deauthentication frame + * + * MLME calls this function as a result of the invalidation of an + * authentication relationship with a specific peer MAC entity. + * + * PeerSTAAddress = sta->addr + */ +void mlme_deauthenticate_indication(struct hostapd_data *hapd, + struct sta_info *sta, u16 reason_code) +{ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME, + HOSTAPD_LEVEL_DEBUG, + "MLME-DEAUTHENTICATE.indication(" MACSTR ", %d)", + MAC2STR(sta->addr), reason_code); + mlme_deletekeys_request(hapd, sta); +} + + +/** + * mlme_associate_indication - Report the establishment of an association with + * a specific peer MAC entity + * @hapd: BSS data + * @sta: peer STA data + * + * MLME calls this function as a result of the establishment of an + * association with a specific peer MAC entity that resulted from an + * association procedure that was initiated by that specific peer MAC entity. + * + * PeerSTAAddress = sta->addr + */ +void mlme_associate_indication(struct hostapd_data *hapd, struct sta_info *sta) +{ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME, + HOSTAPD_LEVEL_DEBUG, + "MLME-ASSOCIATE.indication(" MACSTR ")", + MAC2STR(sta->addr)); + mlme_deletekeys_request(hapd, sta); +} + + +/** + * mlme_reassociate_indication - Report the establishment of an reassociation + * with a specific peer MAC entity + * @hapd: BSS data + * @sta: peer STA data + * + * MLME calls this function as a result of the establishment of an + * reassociation with a specific peer MAC entity that resulted from a + * reassociation procedure that was initiated by that specific peer MAC entity. + * + * PeerSTAAddress = sta->addr + * + * sta->previous_ap contains the "Current AP" information from ReassocReq. + */ +void mlme_reassociate_indication(struct hostapd_data *hapd, + struct sta_info *sta) +{ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME, + HOSTAPD_LEVEL_DEBUG, + "MLME-REASSOCIATE.indication(" MACSTR ")", + MAC2STR(sta->addr)); + mlme_deletekeys_request(hapd, sta); +} + + +/** + * mlme_disassociate_indication - Report disassociation with a specific peer + * MAC entity + * @hapd: BSS data + * @sta: Peer STA data + * @reason_code: ReasonCode from Disassociation frame + * + * MLME calls this function as a result of the invalidation of an association + * relationship with a specific peer MAC entity. + * + * PeerSTAAddress = sta->addr + */ +void mlme_disassociate_indication(struct hostapd_data *hapd, + struct sta_info *sta, u16 reason_code) +{ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME, + HOSTAPD_LEVEL_DEBUG, + "MLME-DISASSOCIATE.indication(" MACSTR ", %d)", + MAC2STR(sta->addr), reason_code); + mlme_deletekeys_request(hapd, sta); +} + + +void mlme_michaelmicfailure_indication(struct hostapd_data *hapd, + const u8 *addr) +{ + hostapd_logger(hapd, addr, HOSTAPD_MODULE_MLME, + HOSTAPD_LEVEL_DEBUG, + "MLME-MichaelMICFailure.indication(" MACSTR ")", + MAC2STR(addr)); +} + + +void mlme_deletekeys_request(struct hostapd_data *hapd, struct sta_info *sta) +{ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME, + HOSTAPD_LEVEL_DEBUG, + "MLME-DELETEKEYS.request(" MACSTR ")", + MAC2STR(sta->addr)); + + if (sta->wpa_sm) + wpa_remove_ptk(sta->wpa_sm); +} diff --git a/contrib/hostapd/mlme.h b/contrib/hostapd/mlme.h new file mode 100644 index 0000000..c77a939 --- /dev/null +++ b/contrib/hostapd/mlme.h @@ -0,0 +1,40 @@ +/* + * hostapd / IEEE 802.11 MLME + * Copyright 2003, Jouni Malinen <j@w1.fi> + * Copyright 2003-2004, 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. + */ + +#ifndef MLME_H +#define MLME_H + +void mlme_authenticate_indication(struct hostapd_data *hapd, + struct sta_info *sta); + +void mlme_deauthenticate_indication(struct hostapd_data *hapd, + struct sta_info *sta, u16 reason_code); + +void mlme_associate_indication(struct hostapd_data *hapd, + struct sta_info *sta); + +void mlme_reassociate_indication(struct hostapd_data *hapd, + struct sta_info *sta); + +void mlme_disassociate_indication(struct hostapd_data *hapd, + struct sta_info *sta, u16 reason_code); + +void mlme_michaelmicfailure_indication(struct hostapd_data *hapd, + const u8 *addr); + +void mlme_deletekeys_request(struct hostapd_data *hapd, struct sta_info *sta); + +#endif /* MLME_H */ diff --git a/contrib/hostapd/ms_funcs.c b/contrib/hostapd/ms_funcs.c index c26cddf..d723179 100644 --- a/contrib/hostapd/ms_funcs.c +++ b/contrib/hostapd/ms_funcs.c @@ -1,6 +1,6 @@ /* * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759 - * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * 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 @@ -12,9 +12,7 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> +#include "includes.h" #include "common.h" #include "sha1.h" @@ -26,7 +24,7 @@ /** * challenge_hash - ChallengeHash() - RFC 2759, Sect. 8.2 * @peer_challenge: 16-octet PeerChallenge (IN) - * @auth_challenge: 16-octet AuthChallenge (IN) + * @auth_challenge: 16-octet AuthenticatorChallenge (IN) * @username: 0-to-256-char UserName (IN) * @username_len: Length of username * @challenge: 8-octet Challenge (OUT) @@ -47,41 +45,41 @@ static void challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge, len[2] = username_len; sha1_vector(3, addr, len, hash); - memcpy(challenge, hash, 8); + os_memcpy(challenge, hash, 8); } /** * nt_password_hash - NtPasswordHash() - RFC 2759, Sect. 8.3 - * @password: 0-to-256-unicode-char Password (IN) + * @password: 0-to-256-unicode-char Password (IN; ASCII) * @password_len: Length of password * @password_hash: 16-octet PasswordHash (OUT) */ void nt_password_hash(const u8 *password, size_t password_len, u8 *password_hash) { - u8 *buf; - int i; - size_t len; + u8 buf[512], *pos; + size_t i, len; - /* Convert password into unicode */ - buf = malloc(password_len * 2); - if (buf == NULL) + if (password_len > 256) return; - memset(buf, 0, password_len * 2); - for (i = 0; i < password_len; i++) + + /* Convert password into unicode */ + for (i = 0; i < password_len; i++) { buf[2 * i] = password[i]; + buf[2 * i + 1] = 0; + } len = password_len * 2; - md4_vector(1, (const u8 **) &buf, &len, password_hash); - free(buf); + pos = buf; + md4_vector(1, (const u8 **) &pos, &len, password_hash); } /** * hash_nt_password_hash - HashNtPasswordHash() - RFC 2759, Sect. 8.4 * @password_hash: 16-octet PasswordHash (IN) - * @password_hash_hash: 16-octet PaswordHashHash (OUT) + * @password_hash_hash: 16-octet PasswordHashHash (OUT) */ void hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash) { @@ -104,7 +102,7 @@ void challenge_response(const u8 *challenge, const u8 *password_hash, des_encrypt(challenge, password_hash + 7, response + 8); zpwd[0] = password_hash[14]; zpwd[1] = password_hash[15]; - memset(zpwd + 2, 0, 5); + os_memset(zpwd + 2, 0, 5); des_encrypt(challenge, zpwd, response + 16); } @@ -115,7 +113,7 @@ void challenge_response(const u8 *challenge, const u8 *password_hash, * @peer_hallenge: 16-octet PeerChallenge (IN) * @username: 0-to-256-char UserName (IN) * @username_len: Length of username - * @password: 0-to-256-unicode-char Password (IN) + * @password: 0-to-256-unicode-char Password (IN; ASCII) * @password_len: Length of password * @response: 24-octet Response (OUT) */ @@ -135,21 +133,44 @@ void generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, /** - * generate_authenticator_response - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7 - * @password: 0-to-256-unicode-char Password (IN) - * @password_len: Length of password + * generate_nt_response_pwhash - GenerateNTResponse() - RFC 2759, Sect. 8.1 + * @auth_challenge: 16-octet AuthenticatorChallenge (IN) + * @peer_hallenge: 16-octet PeerChallenge (IN) + * @username: 0-to-256-char UserName (IN) + * @username_len: Length of username + * @password_hash: 16-octet PasswordHash (IN) + * @response: 24-octet Response (OUT) + */ +void generate_nt_response_pwhash(const u8 *auth_challenge, + const u8 *peer_challenge, + const u8 *username, size_t username_len, + const u8 *password_hash, + u8 *response) +{ + u8 challenge[8]; + + challenge_hash(peer_challenge, auth_challenge, username, username_len, + challenge); + challenge_response(challenge, password_hash, response); +} + + +/** + * generate_authenticator_response_pwhash - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7 + * @password_hash: 16-octet PasswordHash (IN) * @nt_response: 24-octet NT-Response (IN) * @peer_challenge: 16-octet PeerChallenge (IN) * @auth_challenge: 16-octet AuthenticatorChallenge (IN) * @username: 0-to-256-char UserName (IN) * @username_len: Length of username - * @response: 42-octet AuthenticatorResponse (OUT) + * @response: 20-octet AuthenticatorResponse (OUT) (note: this value is usually + * 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, - const u8 *auth_challenge, - const u8 *username, size_t username_len, - const u8 *nt_response, u8 *response) +void generate_authenticator_response_pwhash( + const u8 *password_hash, + const u8 *peer_challenge, const u8 *auth_challenge, + const u8 *username, size_t username_len, + const u8 *nt_response, u8 *response) { static const u8 magic1[39] = { 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76, @@ -165,7 +186,7 @@ void generate_authenticator_response(const u8 *password, size_t password_len, 0x6E }; - u8 password_hash[16], password_hash_hash[16], challenge[8]; + u8 password_hash_hash[16], challenge[8]; const unsigned char *addr1[3]; const size_t len1[3] = { 16, 24, sizeof(magic1) }; const unsigned char *addr2[3]; @@ -179,7 +200,6 @@ void generate_authenticator_response(const u8 *password, size_t password_len, addr2[1] = challenge; addr2[2] = magic2; - nt_password_hash(password, password_len, password_hash); hash_nt_password_hash(password_hash, password_hash_hash); sha1_vector(3, addr1, len1, response); @@ -190,9 +210,36 @@ void generate_authenticator_response(const u8 *password, size_t password_len, /** + * generate_authenticator_response - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7 + * @password: 0-to-256-unicode-char Password (IN; ASCII) + * @password_len: Length of password + * @nt_response: 24-octet NT-Response (IN) + * @peer_challenge: 16-octet PeerChallenge (IN) + * @auth_challenge: 16-octet AuthenticatorChallenge (IN) + * @username: 0-to-256-char UserName (IN) + * @username_len: Length of username + * @response: 20-octet AuthenticatorResponse (OUT) (note: this value is usually + * encoded as a 42-octet ASCII string (S=<hexdump of response>) + */ +void generate_authenticator_response(const u8 *password, size_t password_len, + const u8 *peer_challenge, + const u8 *auth_challenge, + const u8 *username, size_t username_len, + const u8 *nt_response, u8 *response) +{ + u8 password_hash[16]; + nt_password_hash(password, password_len, password_hash); + generate_authenticator_response_pwhash(password_hash, + peer_challenge, auth_challenge, + username, username_len, + nt_response, response); +} + + +/** * nt_challenge_response - NtChallengeResponse() - RFC 2433, Sect. A.5 * @challenge: 8-octet Challenge (IN) - * @password: 0-to-256-unicode-char Password (IN) + * @password: 0-to-256-unicode-char Password (IN; ASCII) * @password_len: Length of password * @response: 24-octet Response (OUT) */ @@ -228,7 +275,7 @@ void get_master_key(const u8 *password_hash_hash, const u8 *nt_response, addr[2] = magic1; sha1_vector(3, addr, len, hash); - memcpy(master_key, hash, 16); + os_memcpy(master_key, hash, 16); } @@ -236,7 +283,7 @@ void get_master_key(const u8 *password_hash_hash, const u8 *nt_response, * get_asymetric_start_key - GetAsymetricStartKey() - RFC 3079, Sect. 3.4 * @master_key: 16-octet MasterKey (IN) * @session_key: 8-to-16 octet SessionKey (OUT) - * @session_key_len: SessionKeyLength (Length of session_key) + * @session_key_len: SessionKeyLength (Length of session_key) (IN) * @is_send: IsSend (IN, BOOLEAN) * @is_server: IsServer (IN, BOOLEAN) */ @@ -296,15 +343,15 @@ void get_asymetric_start_key(const u8 *master_key, u8 *session_key, if (session_key_len > SHA1_MAC_LEN) session_key_len = SHA1_MAC_LEN; - memcpy(session_key, digest, session_key_len); + os_memcpy(session_key, digest, session_key_len); } #define PWBLOCK_LEN 516 /** - * encrypt_pw_block_with_password_hash - EncryptPwBlobkWithPasswordHash() - RFC 2759, Sect. 8.10 - * @password: 0-to-256-unicode-char Password (IN) + * encrypt_pw_block_with_password_hash - EncryptPwBlockWithPasswordHash() - RFC 2759, Sect. 8.10 + * @password: 0-to-256-unicode-char Password (IN; ASCII) * @password_len: Length of password * @password_hash: 16-octet PasswordHash (IN) * @pw_block: 516-byte PwBlock (OUT) @@ -319,10 +366,15 @@ static void encrypt_pw_block_with_password_hash( if (password_len > 256) return; - memset(pw_block, 0, PWBLOCK_LEN); + os_memset(pw_block, 0, PWBLOCK_LEN); offset = (256 - password_len) * 2; + os_get_random(pw_block, offset); for (i = 0; i < password_len; i++) pw_block[offset + i * 2] = password[i]; + /* + * PasswordLength is 4 octets, but since the maximum password length is + * 256, only first two (in little endian byte order) can be non-zero. + */ pos = &pw_block[2 * 256]; WPA_PUT_LE16(pos, password_len * 2); rc4(pw_block, PWBLOCK_LEN, password_hash, 16); @@ -331,9 +383,9 @@ static void encrypt_pw_block_with_password_hash( /** * new_password_encrypted_with_old_nt_password_hash - NewPasswordEncryptedWithOldNtPasswordHash() - RFC 2759, Sect. 8.9 - * @new_password: 0-to-256-unicode-char NewPassword (IN) + * @new_password: 0-to-256-unicode-char NewPassword (IN; ASCII) * @new_password_len: Length of new_password - * @old_password: 0-to-256-unicode-char OldPassword (IN) + * @old_password: 0-to-256-unicode-char OldPassword (IN; ASCII) * @old_password_len: Length of old_password * @encrypted_pw_block: 516-octet EncryptedPwBlock (OUT) */ @@ -367,9 +419,9 @@ static void nt_password_hash_encrypted_with_block(const u8 *password_hash, /** * old_nt_password_hash_encrypted_with_new_nt_password_hash - OldNtPasswordHashEncryptedWithNewNtPasswordHash() - RFC 2759, Sect. 8.12 - * @new_password: 0-to-256-unicode-char NewPassword (IN) + * @new_password: 0-to-256-unicode-char NewPassword (IN; ASCII) * @new_password_len: Length of new_password - * @old_password: 0-to-256-unicode-char OldPassword (IN) + * @old_password: 0-to-256-unicode-char OldPassword (IN; ASCII) * @old_password_len: Length of old_password * @encrypted_password_ash: 16-octet EncryptedPasswordHash (OUT) */ @@ -386,112 +438,3 @@ void old_nt_password_hash_encrypted_with_new_nt_password_hash( new_password_hash, encrypted_password_hash); } - - -#ifdef TEST_MAIN_MS_FUNCS - -#include "rc4.c" - -int main(int argc, char *argv[]) -{ - /* Test vector from RFC2759 example */ - 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; -} -#endif /* TEST_MAIN_MS_FUNCS */ diff --git a/contrib/hostapd/ms_funcs.h b/contrib/hostapd/ms_funcs.h index 38d1bd6..8067c09 100644 --- a/contrib/hostapd/ms_funcs.h +++ b/contrib/hostapd/ms_funcs.h @@ -1,6 +1,6 @@ /* * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759 - * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * 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 @@ -19,11 +19,21 @@ void generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, const u8 *username, size_t username_len, const u8 *password, size_t password_len, u8 *response); +void generate_nt_response_pwhash(const u8 *auth_challenge, + const u8 *peer_challenge, + const u8 *username, size_t username_len, + const u8 *password_hash, + u8 *response); void generate_authenticator_response(const u8 *password, size_t password_len, const u8 *peer_challenge, const u8 *auth_challenge, const u8 *username, size_t username_len, const u8 *nt_response, u8 *response); +void generate_authenticator_response_pwhash( + const u8 *password_hash, + const u8 *peer_challenge, const u8 *auth_challenge, + const u8 *username, size_t username_len, + const u8 *nt_response, u8 *response); void nt_challenge_response(const u8 *challenge, const u8 *password, size_t password_len, u8 *response); @@ -46,5 +56,4 @@ void old_nt_password_hash_encrypted_with_new_nt_password_hash( const u8 *old_password, size_t old_password_len, u8 *encrypted_password_hash); - #endif /* MS_FUNCS_H */ diff --git a/contrib/hostapd/os.h b/contrib/hostapd/os.h new file mode 100644 index 0000000..4931adb --- /dev/null +++ b/contrib/hostapd/os.h @@ -0,0 +1,485 @@ +/* + * wpa_supplicant/hostapd / OS specific functions + * 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 OS_H +#define OS_H + +typedef long os_time_t; + +/** + * os_sleep - Sleep (sec, usec) + * @sec: Number of seconds to sleep + * @usec: Number of microseconds to sleep + */ +void os_sleep(os_time_t sec, os_time_t usec); + +struct os_time { + os_time_t sec; + os_time_t usec; +}; + +/** + * os_get_time - Get current time (sec, usec) + * @t: Pointer to buffer for the time + * Returns: 0 on success, -1 on failure + */ +int os_get_time(struct os_time *t); + + +/* Helper macros for handling struct os_time */ + +#define os_time_before(a, b) \ + ((a)->sec < (b)->sec || \ + ((a)->sec == (b)->sec && (a)->usec < (b)->usec)) + +#define os_time_sub(a, b, res) do { \ + (res)->sec = (a)->sec - (b)->sec; \ + (res)->usec = (a)->usec - (b)->usec; \ + if ((res)->usec < 0) { \ + (res)->sec--; \ + (res)->usec += 1000000; \ + } \ +} while (0) + +/** + * os_mktime - Convert broken-down time into seconds since 1970-01-01 + * @year: Four digit year + * @month: Month (1 .. 12) + * @day: Day of month (1 .. 31) + * @hour: Hour (0 .. 23) + * @min: Minute (0 .. 59) + * @sec: Second (0 .. 60) + * @t: Buffer for returning calendar time representation (seconds since + * 1970-01-01 00:00:00) + * Returns: 0 on success, -1 on failure + */ +int os_mktime(int year, int month, int day, int hour, int min, int sec, + os_time_t *t); + + +/** + * os_daemonize - Run in the background (detach from the controlling terminal) + * @pid_file: File name to write the process ID to or %NULL to skip this + * Returns: 0 on success, -1 on failure + */ +int os_daemonize(const char *pid_file); + +/** + * os_daemonize_terminate - Stop running in the background (remove pid file) + * @pid_file: File name to write the process ID to or %NULL to skip this + */ +void os_daemonize_terminate(const char *pid_file); + +/** + * os_get_random - Get cryptographically strong pseudo random data + * @buf: Buffer for pseudo random data + * @len: Length of the buffer + * Returns: 0 on success, -1 on failure + */ +int os_get_random(unsigned char *buf, size_t len); + +/** + * os_random - Get pseudo random value (not necessarily very strong) + * Returns: Pseudo random value + */ +unsigned long os_random(void); + +/** + * os_rel2abs_path - Get an absolute path for a file + * @rel_path: Relative path to a file + * Returns: Absolute path for the file or %NULL on failure + * + * This function tries to convert a relative path of a file to an absolute path + * in order for the file to be found even if current working directory has + * changed. The returned value is allocated and caller is responsible for + * freeing it. It is acceptable to just return the same path in an allocated + * buffer, e.g., return strdup(rel_path). This function is only used to find + * configuration files when os_daemonize() may have changed the current working + * directory and relative path would be pointing to a different location. + */ +char * os_rel2abs_path(const char *rel_path); + +/** + * os_program_init - Program initialization (called at start) + * Returns: 0 on success, -1 on failure + * + * This function is called when a programs starts. If there are any OS specific + * processing that is needed, it can be placed here. It is also acceptable to + * just return 0 if not special processing is needed. + */ +int os_program_init(void); + +/** + * os_program_deinit - Program deinitialization (called just before exit) + * + * This function is called just before a program exists. If there are any OS + * specific processing, e.g., freeing resourced allocated in os_program_init(), + * it should be done here. It is also acceptable for this function to do + * nothing. + */ +void os_program_deinit(void); + +/** + * os_setenv - Set environment variable + * @name: Name of the variable + * @value: Value to set to the variable + * @overwrite: Whether existing variable should be overwritten + * Returns: 0 on success, -1 on error + * + * This function is only used for wpa_cli action scripts. OS wrapper does not + * need to implement this if such functionality is not needed. + */ +int os_setenv(const char *name, const char *value, int overwrite); + +/** + * os_unsetenv - Delete environent variable + * @name: Name of the variable + * Returns: 0 on success, -1 on error + * + * This function is only used for wpa_cli action scripts. OS wrapper does not + * need to implement this if such functionality is not needed. + */ +int os_unsetenv(const char *name); + +/** + * os_readfile - Read a file to an allocated memory buffer + * @name: Name of the file to read + * @len: For returning the length of the allocated buffer + * Returns: Pointer to the allocated buffer or %NULL on failure + * + * This function allocates memory and reads the given file to this buffer. Both + * binary and text files can be read with this function. The caller is + * responsible for freeing the returned buffer with os_free(). + */ +char * os_readfile(const char *name, size_t *len); + +/** + * os_zalloc - Allocate and zero memory + * @size: Number of bytes to allocate + * Returns: Pointer to allocated and zeroed memory or %NULL on failure + * + * Caller is responsible for freeing the returned buffer with os_free(). + */ +void * os_zalloc(size_t size); + + +/* + * The following functions are wrapper for standard ANSI C or POSIX functions. + * By default, they are just defined to use the standard function name and no + * os_*.c implementation is needed for them. This avoids extra function calls + * by allowing the C pre-processor take care of the function name mapping. + * + * If the target system uses a C library that does not provide these functions, + * build_config.h can be used to define the wrappers to use a different + * function name. This can be done on function-by-function basis since the + * defines here are only used if build_config.h does not define the os_* name. + * If needed, os_*.c file can be used to implement the functions that are not + * included in the C library on the target system. Alternatively, + * OS_NO_C_LIB_DEFINES can be defined to skip all defines here in which case + * these functions need to be implemented in os_*.c file for the target system. + */ + +#ifdef OS_NO_C_LIB_DEFINES + +/** + * os_malloc - Allocate dynamic memory + * @size: Size of the buffer to allocate + * Returns: Allocated buffer or %NULL on failure + * + * Caller is responsible for freeing the returned buffer with os_free(). + */ +void * os_malloc(size_t size); + +/** + * os_realloc - Re-allocate dynamic memory + * @ptr: Old buffer from os_malloc() or os_realloc() + * @size: Size of the new buffer + * Returns: Allocated buffer or %NULL on failure + * + * Caller is responsible for freeing the returned buffer with os_free(). + * If re-allocation fails, %NULL is returned and the original buffer (ptr) is + * not freed and caller is still responsible for freeing it. + */ +void * os_realloc(void *ptr, size_t size); + +/** + * os_free - Free dynamic memory + * @ptr: Old buffer from os_malloc() or os_realloc(); can be %NULL + */ +void os_free(void *ptr); + +/** + * os_memcpy - Copy memory area + * @dest: Destination + * @src: Source + * @n: Number of bytes to copy + * Returns: dest + * + * The memory areas src and dst must not overlap. os_memmove() can be used with + * overlapping memory. + */ +void * os_memcpy(void *dest, const void *src, size_t n); + +/** + * os_memmove - Copy memory area + * @dest: Destination + * @src: Source + * @n: Number of bytes to copy + * Returns: dest + * + * The memory areas src and dst may overlap. + */ +void * os_memmove(void *dest, const void *src, size_t n); + +/** + * os_memset - Fill memory with a constant byte + * @s: Memory area to be filled + * @c: Constant byte + * @n: Number of bytes started from s to fill with c + * Returns: s + */ +void * os_memset(void *s, int c, size_t n); + +/** + * os_memcmp - Compare memory areas + * @s1: First buffer + * @s2: Second buffer + * @n: Maximum numbers of octets to compare + * Returns: An integer less than, equal to, or greater than zero if s1 is + * found to be less than, to match, or be greater than s2. Only first n + * characters will be compared. + */ +int os_memcmp(const void *s1, const void *s2, size_t n); + +/** + * os_strdup - Duplicate a string + * @s: Source string + * Returns: Allocated buffer with the string copied into it or %NULL on failure + * + * Caller is responsible for freeing the returned buffer with os_free(). + */ +char * os_strdup(const char *s); + +/** + * os_strlen - Calculate the length of a string + * @s: '\0' terminated string + * Returns: Number of characters in s (not counting the '\0' terminator) + */ +size_t os_strlen(const char *s); + +/** + * os_strcasecmp - Compare two strings ignoring case + * @s1: First string + * @s2: Second string + * Returns: An integer less than, equal to, or greater than zero if s1 is + * found to be less than, to match, or be greatred than s2 + */ +int os_strcasecmp(const char *s1, const char *s2); + +/** + * os_strncasecmp - Compare two strings ignoring case + * @s1: First string + * @s2: Second string + * @n: Maximum numbers of characters to compare + * Returns: An integer less than, equal to, or greater than zero if s1 is + * found to be less than, to match, or be greater than s2. Only first n + * characters will be compared. + */ +int os_strncasecmp(const char *s1, const char *s2, size_t n); + +/** + * os_strchr - Locate the first occurrence of a character in string + * @s: String + * @c: Character to search for + * Returns: Pointer to the matched character or %NULL if not found + */ +char * os_strchr(const char *s, int c); + +/** + * os_strrchr - Locate the last occurrence of a character in string + * @s: String + * @c: Character to search for + * Returns: Pointer to the matched character or %NULL if not found + */ +char * os_strrchr(const char *s, int c); + +/** + * os_strcmp - Compare two strings + * @s1: First string + * @s2: Second string + * Returns: An integer less than, equal to, or greater than zero if s1 is + * found to be less than, to match, or be greatred than s2 + */ +int os_strcmp(const char *s1, const char *s2); + +/** + * os_strncmp - Compare two strings + * @s1: First string + * @s2: Second string + * @n: Maximum numbers of characters to compare + * Returns: An integer less than, equal to, or greater than zero if s1 is + * found to be less than, to match, or be greater than s2. Only first n + * characters will be compared. + */ +int os_strncmp(const char *s1, const char *s2, size_t n); + +/** + * os_strncpy - Copy a string + * @dest: Destination + * @src: Source + * @n: Maximum number of characters to copy + * Returns: dest + */ +char * os_strncpy(char *dest, const char *src, size_t n); + +/** + * os_strstr - Locate a substring + * @haystack: String (haystack) to search from + * @needle: Needle to search from haystack + * Returns: Pointer to the beginning of the substring or %NULL if not found + */ +char * os_strstr(const char *haystack, const char *needle); + +/** + * os_snprintf - Print to a memory buffer + * @str: Memory buffer to print into + * @size: Maximum length of the str buffer + * @format: printf format + * Returns: Number of characters printed (not including trailing '\0'). + * + * If the output buffer is truncated, number of characters which would have + * been written is returned. Since some C libraries return -1 in such a case, + * the caller must be prepared on that value, too, to indicate truncation. + * + * Note: Some C library implementations of snprintf() may not guarantee null + * termination in case the output is truncated. The OS wrapper function of + * os_snprintf() should provide this guarantee, i.e., to null terminate the + * output buffer if a C library version of the function is used and if that + * function does not guarantee null termination. + * + * If the target system does not include snprintf(), see, e.g., + * http://www.ijs.si/software/snprintf/ for an example of a portable + * implementation of snprintf. + */ +int os_snprintf(char *str, size_t size, const char *format, ...); + +#else /* OS_NO_C_LIB_DEFINES */ + +#ifndef os_malloc +#define os_malloc(s) malloc((s)) +#endif +#ifndef os_realloc +#define os_realloc(p, s) realloc((p), (s)) +#endif +#ifndef os_free +#define os_free(p) free((p)) +#endif + +#ifndef os_memcpy +#define os_memcpy(d, s, n) memcpy((d), (s), (n)) +#endif +#ifndef os_memmove +#define os_memmove(d, s, n) memmove((d), (s), (n)) +#endif +#ifndef os_memset +#define os_memset(s, c, n) memset(s, c, n) +#endif +#ifndef os_memcmp +#define os_memcmp(s1, s2, n) memcmp((s1), (s2), (n)) +#endif + +#ifndef os_strdup +#ifdef _MSC_VER +#define os_strdup(s) _strdup(s) +#else +#define os_strdup(s) strdup(s) +#endif +#endif +#ifndef os_strlen +#define os_strlen(s) strlen(s) +#endif +#ifndef os_strcasecmp +#ifdef _MSC_VER +#define os_strcasecmp(s1, s2) _stricmp((s1), (s2)) +#else +#define os_strcasecmp(s1, s2) strcasecmp((s1), (s2)) +#endif +#endif +#ifndef os_strncasecmp +#ifdef _MSC_VER +#define os_strncasecmp(s1, s2, n) _strnicmp((s1), (s2), (n)) +#else +#define os_strncasecmp(s1, s2, n) strncasecmp((s1), (s2), (n)) +#endif +#endif +#ifndef os_strchr +#define os_strchr(s, c) strchr((s), (c)) +#endif +#ifndef os_strcmp +#define os_strcmp(s1, s2) strcmp((s1), (s2)) +#endif +#ifndef os_strncmp +#define os_strncmp(s1, s2, n) strncmp((s1), (s2), (n)) +#endif +#ifndef os_strncpy +#define os_strncpy(d, s, n) strncpy((d), (s), (n)) +#endif +#ifndef os_strrchr +#define os_strrchr(s, c) strrchr((s), (c)) +#endif +#ifndef os_strstr +#define os_strstr(h, n) strstr((h), (n)) +#endif + +#ifndef os_snprintf +#ifdef _MSC_VER +#define os_snprintf _snprintf +#else +#define os_snprintf snprintf +#endif +#endif + +#endif /* OS_NO_C_LIB_DEFINES */ + + +#ifdef OS_REJECT_C_LIB_FUNCTIONS +#define malloc OS_DO_NOT_USE_malloc +#define realloc OS_DO_NOT_USE_realloc +#define free OS_DO_NOT_USE_free +#define memcpy OS_DO_NOT_USE_memcpy +#define memmove OS_DO_NOT_USE_memmove +#define memset OS_DO_NOT_USE_memset +#define memcmp OS_DO_NOT_USE_memcmp +#undef strdup +#define strdup OS_DO_NOT_USE_strdup +#define strlen OS_DO_NOT_USE_strlen +#define strcasecmp OS_DO_NOT_USE_strcasecmp +#define strncasecmp OS_DO_NOT_USE_strncasecmp +#undef strchr +#define strchr OS_DO_NOT_USE_strchr +#undef strcmp +#define strcmp OS_DO_NOT_USE_strcmp +#undef strncmp +#define strncmp OS_DO_NOT_USE_strncmp +#undef strncpy +#define strncpy OS_DO_NOT_USE_strncpy +#define strrchr OS_DO_NOT_USE_strrchr +#define strstr OS_DO_NOT_USE_strstr +#undef snprintf +#define snprintf OS_DO_NOT_USE_snprintf + +#define strcpy OS_DO_NOT_USE_strcpy +#endif /* OS_REJECT_C_LIB_FUNCTIONS */ + +#endif /* OS_H */ diff --git a/contrib/hostapd/os_internal.c b/contrib/hostapd/os_internal.c new file mode 100644 index 0000000..b2183ea --- /dev/null +++ b/contrib/hostapd/os_internal.c @@ -0,0 +1,441 @@ +/* + * wpa_supplicant/hostapd / Internal implementation of OS specific functions + * 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 is an example of operating system specific wrapper functions. + * This version implements many of the functions internally, so it can be used + * to fill in missing functions from the target system C libraries. + * + * Some of the functions are using standard C library calls in order to keep + * this file in working condition to allow the functions to be tested on a + * Linux target. Please note that OS_NO_C_LIB_DEFINES needs to be defined for + * this file to work correctly. Note that these implementations are only + * examples and are not optimized for speed. + */ + +#include "includes.h" + +#undef OS_REJECT_C_LIB_FUNCTIONS +#include "os.h" + +void os_sleep(os_time_t sec, os_time_t usec) +{ + if (sec) + sleep(sec); + if (usec) + usleep(usec); +} + + +int os_get_time(struct os_time *t) +{ + int res; + struct timeval tv; + res = gettimeofday(&tv, NULL); + t->sec = tv.tv_sec; + t->usec = tv.tv_usec; + return res; +} + + +int os_mktime(int year, int month, int day, int hour, int min, int sec, + os_time_t *t) +{ + struct tm tm; + + if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 || + hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 || + sec > 60) + return -1; + + os_memset(&tm, 0, sizeof(tm)); + tm.tm_year = year - 1900; + tm.tm_mon = month - 1; + tm.tm_mday = day; + tm.tm_hour = hour; + tm.tm_min = min; + tm.tm_sec = sec; + + *t = (os_time_t) mktime(&tm); + return 0; +} + + +int os_daemonize(const char *pid_file) +{ + if (daemon(0, 0)) { + perror("daemon"); + return -1; + } + + if (pid_file) { + FILE *f = fopen(pid_file, "w"); + if (f) { + fprintf(f, "%u\n", getpid()); + fclose(f); + } + } + + return -0; +} + + +void os_daemonize_terminate(const char *pid_file) +{ + if (pid_file) + unlink(pid_file); +} + + +int os_get_random(unsigned char *buf, size_t len) +{ + FILE *f; + size_t rc; + + f = fopen("/dev/urandom", "rb"); + if (f == NULL) { + printf("Could not open /dev/urandom.\n"); + return -1; + } + + rc = fread(buf, 1, len, f); + fclose(f); + + return rc != len ? -1 : 0; +} + + +unsigned long os_random(void) +{ + return random(); +} + + +char * os_rel2abs_path(const char *rel_path) +{ + char *buf = NULL, *cwd, *ret; + size_t len = 128, cwd_len, rel_len, ret_len; + + if (rel_path[0] == '/') + return os_strdup(rel_path); + + for (;;) { + buf = os_malloc(len); + if (buf == NULL) + return NULL; + cwd = getcwd(buf, len); + if (cwd == NULL) { + os_free(buf); + if (errno != ERANGE) { + return NULL; + } + len *= 2; + } else { + break; + } + } + + cwd_len = strlen(cwd); + rel_len = strlen(rel_path); + ret_len = cwd_len + 1 + rel_len + 1; + ret = os_malloc(ret_len); + if (ret) { + os_memcpy(ret, cwd, cwd_len); + ret[cwd_len] = '/'; + os_memcpy(ret + cwd_len + 1, rel_path, rel_len); + ret[ret_len - 1] = '\0'; + } + os_free(buf); + return ret; +} + + +int os_program_init(void) +{ + return 0; +} + + +void os_program_deinit(void) +{ +} + + +int os_setenv(const char *name, const char *value, int overwrite) +{ + return setenv(name, value, overwrite); +} + + +int os_unsetenv(const char *name) +{ +#if defined(__FreeBSD__) || defined(__NetBSD__) + unsetenv(name); + return 0; +#else + return unsetenv(name); +#endif +} + + +char * os_readfile(const char *name, size_t *len) +{ + FILE *f; + char *buf; + + f = fopen(name, "rb"); + if (f == NULL) + return NULL; + + fseek(f, 0, SEEK_END); + *len = ftell(f); + fseek(f, 0, SEEK_SET); + + buf = os_malloc(*len); + if (buf == NULL) { + fclose(f); + return NULL; + } + + fread(buf, 1, *len, f); + fclose(f); + + return buf; +} + + +void * os_zalloc(size_t size) +{ + void *n = os_malloc(size); + if (n) + os_memset(n, 0, size); + return n; +} + + +void * os_malloc(size_t size) +{ + return malloc(size); +} + + +void * os_realloc(void *ptr, size_t size) +{ + return realloc(ptr, size); +} + + +void os_free(void *ptr) +{ + free(ptr); +} + + +void * os_memcpy(void *dest, const void *src, size_t n) +{ + char *d = dest; + const char *s = src; + while (n--) + *d++ = *s++; + return dest; +} + + +void * os_memmove(void *dest, const void *src, size_t n) +{ + if (dest < src) + os_memcpy(dest, src, n); + else { + /* overlapping areas */ + char *d = (char *) dest + n; + const char *s = (const char *) src + n; + while (n--) + *--d = *--s; + } + return dest; +} + + +void * os_memset(void *s, int c, size_t n) +{ + char *p = s; + while (n--) + *p++ = c; + return s; +} + + +int os_memcmp(const void *s1, const void *s2, size_t n) +{ + const unsigned char *p1 = s1, *p2 = s2; + + if (n == 0) + return 0; + + while (*p1 == *p2) { + p1++; + p2++; + n--; + if (n == 0) + return 0; + } + + return *p1 - *p2; +} + + +char * os_strdup(const char *s) +{ + char *res; + size_t len; + if (s == NULL) + return NULL; + len = os_strlen(s); + res = os_malloc(len + 1); + if (res) + os_memcpy(res, s, len + 1); + return res; +} + + +size_t os_strlen(const char *s) +{ + const char *p = s; + while (*p) + p++; + return p - s; +} + + +int os_strcasecmp(const char *s1, const char *s2) +{ + /* + * Ignoring case is not required for main functionality, so just use + * the case sensitive version of the function. + */ + return os_strcmp(s1, s2); +} + + +int os_strncasecmp(const char *s1, const char *s2, size_t n) +{ + /* + * Ignoring case is not required for main functionality, so just use + * the case sensitive version of the function. + */ + return os_strncmp(s1, s2, n); +} + + +char * os_strchr(const char *s, int c) +{ + while (*s) { + if (*s == c) + return (char *) s; + s++; + } + return NULL; +} + + +char * os_strrchr(const char *s, int c) +{ + const char *p = s; + while (*p) + p++; + p--; + while (p >= s) { + if (*p == c) + return (char *) p; + p--; + } + return NULL; +} + + +int os_strcmp(const char *s1, const char *s2) +{ + while (*s1 == *s2) { + if (*s1 == '\0') + break; + s1++; + s2++; + } + + return *s1 - *s2; +} + + +int os_strncmp(const char *s1, const char *s2, size_t n) +{ + if (n == 0) + return 0; + + while (*s1 == *s2) { + if (*s1 == '\0') + break; + s1++; + s2++; + n--; + if (n == 0) + return 0; + } + + return *s1 - *s2; +} + + +char * os_strncpy(char *dest, const char *src, size_t n) +{ + char *d = dest; + + while (n--) { + *d = *src; + if (*src == '\0') + break; + d++; + src++; + } + + return dest; +} + + +char * os_strstr(const char *haystack, const char *needle) +{ + size_t len = os_strlen(needle); + while (*haystack) { + if (os_strncmp(haystack, needle, len) == 0) + return (char *) haystack; + haystack++; + } + + return NULL; +} + + +int os_snprintf(char *str, size_t size, const char *format, ...) +{ + va_list ap; + int ret; + + /* See http://www.ijs.si/software/snprintf/ for portable + * implementation of snprintf. + */ + + va_start(ap, format); + ret = vsnprintf(str, size, format, ap); + va_end(ap); + if (size > 0) + str[size - 1] = '\0'; + return ret; +} diff --git a/contrib/hostapd/os_none.c b/contrib/hostapd/os_none.c new file mode 100644 index 0000000..7404e22 --- /dev/null +++ b/contrib/hostapd/os_none.c @@ -0,0 +1,220 @@ +/* + * wpa_supplicant/hostapd / Empty OS specific functions + * 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 can be used as a starting point when adding a new OS target. The + * functions here do not really work as-is since they are just empty or only + * return an error value. os_internal.c can be used as another starting point + * or reference since it has example implementation of many of these functions. + */ + +#include "includes.h" + +#include "os.h" + +void os_sleep(os_time_t sec, os_time_t usec) +{ +} + + +int os_get_time(struct os_time *t) +{ + return -1; +} + + +int os_mktime(int year, int month, int day, int hour, int min, int sec, + os_time_t *t) +{ + return -1; +} + + +int os_daemonize(const char *pid_file) +{ + return -1; +} + + +void os_daemonize_terminate(const char *pid_file) +{ +} + + +int os_get_random(unsigned char *buf, size_t len) +{ + return -1; +} + + +unsigned long os_random(void) +{ + return 0; +} + + +char * os_rel2abs_path(const char *rel_path) +{ + return NULL; /* strdup(rel_path) can be used here */ +} + + +int os_program_init(void) +{ + return 0; +} + + +void os_program_deinit(void) +{ +} + + +int os_setenv(const char *name, const char *value, int overwrite) +{ + return -1; +} + + +int os_unsetenv(const char *name) +{ + return -1; +} + + +char * os_readfile(const char *name, size_t *len) +{ + return NULL; +} + + +void * os_zalloc(size_t size) +{ + return NULL; +} + + +#ifdef OS_NO_C_LIB_DEFINES +void * os_malloc(size_t size) +{ + return NULL; +} + + +void * os_realloc(void *ptr, size_t size) +{ + return NULL; +} + + +void os_free(void *ptr) +{ +} + + +void * os_memcpy(void *dest, const void *src, size_t n) +{ + return dest; +} + + +void * os_memmove(void *dest, const void *src, size_t n) +{ + return dest; +} + + +void * os_memset(void *s, int c, size_t n) +{ + return s; +} + + +int os_memcmp(const void *s1, const void *s2, size_t n) +{ + return 0; +} + + +char * os_strdup(const char *s) +{ + return NULL; +} + + +size_t os_strlen(const char *s) +{ + return 0; +} + + +int os_strcasecmp(const char *s1, const char *s2) +{ + /* + * Ignoring case is not required for main functionality, so just use + * the case sensitive version of the function. + */ + return os_strcmp(s1, s2); +} + + +int os_strncasecmp(const char *s1, const char *s2, size_t n) +{ + /* + * Ignoring case is not required for main functionality, so just use + * the case sensitive version of the function. + */ + return os_strncmp(s1, s2, n); +} + + +char * os_strchr(const char *s, int c) +{ + return NULL; +} + + +char * os_strrchr(const char *s, int c) +{ + return NULL; +} + + +int os_strcmp(const char *s1, const char *s2) +{ + return 0; +} + + +int os_strncmp(const char *s1, const char *s2, size_t n) +{ + return 0; +} + + +char * os_strncpy(char *dest, const char *src, size_t n) +{ + return dest; +} + + +char * os_strstr(const char *haystack, const char *needle) +{ + return NULL; +} + + +int os_snprintf(char *str, size_t size, const char *format, ...) +{ + return 0; +} +#endif /* OS_NO_C_LIB_DEFINES */ diff --git a/contrib/hostapd/os_unix.c b/contrib/hostapd/os_unix.c new file mode 100644 index 0000000..fb8149a --- /dev/null +++ b/contrib/hostapd/os_unix.c @@ -0,0 +1,212 @@ +/* + * wpa_supplicant/hostapd / OS specific functions for UNIX/POSIX 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 "os.h" + +void os_sleep(os_time_t sec, os_time_t usec) +{ + if (sec) + sleep(sec); + if (usec) + usleep(usec); +} + + +int os_get_time(struct os_time *t) +{ + int res; + struct timeval tv; + res = gettimeofday(&tv, NULL); + t->sec = tv.tv_sec; + t->usec = tv.tv_usec; + return res; +} + + +int os_mktime(int year, int month, int day, int hour, int min, int sec, + os_time_t *t) +{ + struct tm tm; + + if (year < 1970 || month < 1 || month > 12 || day < 1 || day > 31 || + hour < 0 || hour > 23 || min < 0 || min > 59 || sec < 0 || + sec > 60) + return -1; + + memset(&tm, 0, sizeof(tm)); + tm.tm_year = year - 1900; + tm.tm_mon = month - 1; + tm.tm_mday = day; + tm.tm_hour = hour; + tm.tm_min = min; + tm.tm_sec = sec; + + *t = (os_time_t) mktime(&tm); + return 0; +} + + +int os_daemonize(const char *pid_file) +{ + if (daemon(0, 0)) { + perror("daemon"); + return -1; + } + + if (pid_file) { + FILE *f = fopen(pid_file, "w"); + if (f) { + fprintf(f, "%u\n", getpid()); + fclose(f); + } + } + + return -0; +} + + +void os_daemonize_terminate(const char *pid_file) +{ + if (pid_file) + unlink(pid_file); +} + + +int os_get_random(unsigned char *buf, size_t len) +{ + FILE *f; + size_t rc; + + f = fopen("/dev/urandom", "rb"); + if (f == NULL) { + printf("Could not open /dev/urandom.\n"); + return -1; + } + + rc = fread(buf, 1, len, f); + fclose(f); + + return rc != len ? -1 : 0; +} + + +unsigned long os_random(void) +{ + return random(); +} + + +char * os_rel2abs_path(const char *rel_path) +{ + char *buf = NULL, *cwd, *ret; + size_t len = 128, cwd_len, rel_len, ret_len; + int last_errno; + + if (rel_path[0] == '/') + return strdup(rel_path); + + for (;;) { + buf = malloc(len); + if (buf == NULL) + return NULL; + cwd = getcwd(buf, len); + if (cwd == NULL) { + last_errno = errno; + free(buf); + if (last_errno != ERANGE) + return NULL; + len *= 2; + if (len > 2000) + return NULL; + } else { + buf[len - 1] = '\0'; + break; + } + } + + cwd_len = strlen(cwd); + rel_len = strlen(rel_path); + ret_len = cwd_len + 1 + rel_len + 1; + ret = malloc(ret_len); + if (ret) { + memcpy(ret, cwd, cwd_len); + ret[cwd_len] = '/'; + memcpy(ret + cwd_len + 1, rel_path, rel_len); + ret[ret_len - 1] = '\0'; + } + free(buf); + return ret; +} + + +int os_program_init(void) +{ + return 0; +} + + +void os_program_deinit(void) +{ +} + + +int os_setenv(const char *name, const char *value, int overwrite) +{ + return setenv(name, value, overwrite); +} + + +int os_unsetenv(const char *name) +{ +#if defined(__FreeBSD__) || defined(__NetBSD__) + unsetenv(name); + return 0; +#else + return unsetenv(name); +#endif +} + + +char * os_readfile(const char *name, size_t *len) +{ + FILE *f; + char *buf; + + f = fopen(name, "rb"); + if (f == NULL) + return NULL; + + fseek(f, 0, SEEK_END); + *len = ftell(f); + fseek(f, 0, SEEK_SET); + + buf = 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); +} diff --git a/contrib/hostapd/pmksa_cache.c b/contrib/hostapd/pmksa_cache.c new file mode 100644 index 0000000..0cb8523 --- /dev/null +++ b/contrib/hostapd/pmksa_cache.c @@ -0,0 +1,366 @@ +/* + * hostapd - PMKSA cache for IEEE 802.11i RSN + * 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 "wpa.h" +#include "eloop.h" +#include "sha1.h" +#include "ieee802_1x.h" +#include "eapol_sm.h" +#include "pmksa_cache.h" + + +static const int pmksa_cache_max_entries = 1024; +static const int dot11RSNAConfigPMKLifetime = 43200; + +struct rsn_pmksa_cache { +#define PMKID_HASH_SIZE 128 +#define PMKID_HASH(pmkid) (unsigned int) ((pmkid)[0] & 0x7f) + struct rsn_pmksa_cache_entry *pmkid[PMKID_HASH_SIZE]; + struct rsn_pmksa_cache_entry *pmksa; + int pmksa_count; + + void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx); + void *ctx; +}; + + +/** + * rsn_pmkid - Calculate PMK identifier + * @pmk: Pairwise master key + * @pmk_len: Length of pmk in bytes + * @aa: Authenticator address + * @spa: Supplicant address + * + * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy + * PMKID = HMAC-SHA1-128(PMK, "PMK Name" || AA || SPA) + */ +void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, + u8 *pmkid) +{ + char *title = "PMK Name"; + const u8 *addr[3]; + const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN }; + unsigned char hash[SHA1_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); +} + + +static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa); + + +static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry) +{ + if (entry == NULL) + return; + free(entry->identity); + ieee802_1x_free_radius_class(&entry->radius_class); + free(entry); +} + + +static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry) +{ + struct rsn_pmksa_cache_entry *pos, *prev; + + pmksa->pmksa_count--; + pmksa->free_cb(entry, pmksa->ctx); + pos = pmksa->pmkid[PMKID_HASH(entry->pmkid)]; + prev = NULL; + while (pos) { + if (pos == entry) { + if (prev != NULL) { + prev->hnext = pos->hnext; + } else { + pmksa->pmkid[PMKID_HASH(entry->pmkid)] = + pos->hnext; + } + break; + } + prev = pos; + pos = pos->hnext; + } + + pos = pmksa->pmksa; + prev = NULL; + while (pos) { + if (pos == entry) { + if (prev != NULL) + prev->next = pos->next; + else + pmksa->pmksa = pos->next; + break; + } + prev = pos; + pos = pos->next; + } + _pmksa_cache_free_entry(entry); +} + + +static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) +{ + struct rsn_pmksa_cache *pmksa = eloop_ctx; + struct os_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->spa)); + pmksa_cache_free_entry(pmksa, entry); + } + + pmksa_cache_set_expiration(pmksa); +} + + +static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) +{ + int sec; + struct os_time now; + + eloop_cancel_timeout(pmksa_cache_expire, 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); +} + + +static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry, + struct eapol_state_machine *eapol) +{ + if (eapol == NULL) + return; + + if (eapol->identity) { + entry->identity = malloc(eapol->identity_len); + if (entry->identity) { + entry->identity_len = eapol->identity_len; + memcpy(entry->identity, eapol->identity, + eapol->identity_len); + } + } + + ieee802_1x_copy_radius_class(&entry->radius_class, + &eapol->radius_class); + + entry->eap_type_authsrv = eapol->eap_type_authsrv; + entry->vlan_id = eapol->sta->vlan_id; +} + + +void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry, + struct eapol_state_machine *eapol) +{ + if (entry == NULL || eapol == NULL) + return; + + if (entry->identity) { + free(eapol->identity); + eapol->identity = malloc(entry->identity_len); + if (eapol->identity) { + eapol->identity_len = entry->identity_len; + memcpy(eapol->identity, entry->identity, + entry->identity_len); + } + wpa_hexdump_ascii(MSG_DEBUG, "STA identity from PMKSA", + eapol->identity, eapol->identity_len); + } + + ieee802_1x_free_radius_class(&eapol->radius_class); + ieee802_1x_copy_radius_class(&eapol->radius_class, + &entry->radius_class); + if (eapol->radius_class.attr) { + wpa_printf(MSG_DEBUG, "Copied %lu Class attribute(s) from " + "PMKSA", (unsigned long) eapol->radius_class.count); + } + + eapol->eap_type_authsrv = entry->eap_type_authsrv; + eapol->sta->vlan_id = entry->vlan_id; +} + + +/** + * 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 + * @session_timeout: Session timeout + * @eapol: Pointer to EAPOL state machine data + * Returns: Pointer to the added PMKSA cache entry or %NULL on error + * + * This function create a PMKSA entry for a new PMK and adds it to the PMKSA + * cache. If an old entry is already in the cache for the same Supplicant, + * this entry will be replaced with the new entry. PMKID will be calculated + * based on the PMK. + */ +struct rsn_pmksa_cache_entry * +pmksa_cache_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 rsn_pmksa_cache_entry *entry, *pos, *prev; + struct os_time now; + + if (pmk_len > PMK_LEN) + return NULL; + + entry = wpa_zalloc(sizeof(*entry)); + if (entry == NULL) + return NULL; + memcpy(entry->pmk, pmk, pmk_len); + entry->pmk_len = pmk_len; + rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid); + 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); + pmksa_cache_from_eapol_data(entry, eapol); + + /* Replace an old entry for the same STA (if found) with the new entry + */ + pos = pmksa_cache_get(pmksa, spa, NULL); + if (pos) + pmksa_cache_free_entry(pmksa, pos); + + if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) { + /* Remove the oldest entry to make room for the new entry */ + wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache " + "entry (for " MACSTR ") to make room for new one", + MAC2STR(pmksa->pmksa->spa)); + pmksa_cache_free_entry(pmksa, pmksa->pmksa); + } + + /* 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); + + 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; + int i; + + if (pmksa == NULL) + return; + + entry = pmksa->pmksa; + while (entry) { + prev = entry; + entry = entry->next; + _pmksa_cache_free_entry(prev); + } + eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL); + for (i = 0; i < PMKID_HASH_SIZE; i++) + pmksa->pmkid[i] = NULL; + free(pmksa); +} + + +/** + * pmksa_cache_get - Fetch a PMKSA cache entry + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() + * @spa: Supplicant address or %NULL to match any + * @pmkid: PMKID or %NULL to match any + * Returns: Pointer to PMKSA cache entry or %NULL if no match was found + */ +struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, + const u8 *spa, const u8 *pmkid) +{ + struct rsn_pmksa_cache_entry *entry; + + if (pmkid) + entry = pmksa->pmkid[PMKID_HASH(pmkid)]; + else + entry = pmksa->pmksa; + while (entry) { + if ((spa == NULL || memcmp(entry->spa, spa, ETH_ALEN) == 0) && + (pmkid == NULL || + memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)) + return entry; + entry = pmkid ? entry->hnext : 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 + * 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), void *ctx) +{ + struct rsn_pmksa_cache *pmksa; + + pmksa = wpa_zalloc(sizeof(*pmksa)); + if (pmksa) { + pmksa->free_cb = free_cb; + pmksa->ctx = ctx; + } + + return pmksa; +} diff --git a/contrib/hostapd/pmksa_cache.h b/contrib/hostapd/pmksa_cache.h new file mode 100644 index 0000000..dd9074e --- /dev/null +++ b/contrib/hostapd/pmksa_cache.h @@ -0,0 +1,54 @@ +/* + * hostapd - PMKSA cache for IEEE 802.11i RSN + * 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 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, *hnext; + u8 pmkid[PMKID_LEN]; + u8 pmk[PMK_LEN]; + size_t pmk_len; + os_time_t expiration; + int akmp; /* WPA_KEY_MGMT_* */ + u8 spa[ETH_ALEN]; + + u8 *identity; + size_t identity_len; + struct radius_class_data radius_class; + u8 eap_type_authsrv; + int vlan_id; +}; + +struct rsn_pmksa_cache; + +struct rsn_pmksa_cache * +pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, + void *ctx), void *ctx); +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_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); +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); + +#endif /* PMKSA_CACHE_H */ diff --git a/contrib/hostapd/preauth.c b/contrib/hostapd/preauth.c new file mode 100644 index 0000000..d992881 --- /dev/null +++ b/contrib/hostapd/preauth.c @@ -0,0 +1,276 @@ +/* + * hostapd - Authenticator for IEEE 802.11i RSN pre-authentication + * 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" + +#ifdef CONFIG_RSN_PREAUTH + +#include "hostapd.h" +#include "l2_packet.h" +#include "ieee802_1x.h" +#include "eloop.h" +#include "sta_info.h" +#include "wpa_common.h" +#include "eapol_sm.h" +#include "wpa.h" +#include "preauth.h" + +#ifndef ETH_P_PREAUTH +#define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */ +#endif /* ETH_P_PREAUTH */ + +static const int dot11RSNAConfigPMKLifetime = 43200; + +struct rsn_preauth_interface { + struct rsn_preauth_interface *next; + struct hostapd_data *hapd; + struct l2_packet_data *l2; + char *ifname; + int ifindex; +}; + + +static void rsn_preauth_receive(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct rsn_preauth_interface *piface = ctx; + struct hostapd_data *hapd = piface->hapd; + struct ieee802_1x_hdr *hdr; + struct sta_info *sta; + struct l2_ethhdr *ethhdr; + + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN: receive pre-auth packet " + "from interface '%s'\n", 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); + 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)); + 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)); + return; + } + if (!sta && hdr->type == IEEE802_1X_TYPE_EAPOL_START) { + sta = ap_sta_add(hapd, ethhdr->h_source); + if (sta == NULL) + return; + sta->flags = WLAN_STA_PREAUTH; + + ieee802_1x_new_station(hapd, sta); + if (sta->eapol_sm == NULL) { + ap_free_sta(hapd, sta); + sta = NULL; + } else { + sta->eapol_sm->radius_identifier = -1; + sta->eapol_sm->portValid = TRUE; + sta->eapol_sm->flags |= EAPOL_SM_PREAUTH; + } + } + if (sta == NULL) + return; + sta->preauth_iface = piface; + ieee802_1x_receive(hapd, ethhdr->h_source, (u8 *) (ethhdr + 1), + len - sizeof(*ethhdr)); +} + + +static int rsn_preauth_iface_add(struct hostapd_data *hapd, const char *ifname) +{ + struct rsn_preauth_interface *piface; + + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN pre-auth interface '%s'\n", + ifname); + + piface = wpa_zalloc(sizeof(*piface)); + if (piface == NULL) + return -1; + piface->hapd = hapd; + + piface->ifname = strdup(ifname); + if (piface->ifname == NULL) { + goto fail1; + } + + piface->l2 = l2_packet_init(piface->ifname, NULL, ETH_P_PREAUTH, + rsn_preauth_receive, piface, 1); + if (piface->l2 == NULL) { + printf("Failed to open register layer 2 access to " + "ETH_P_PREAUTH\n"); + goto fail2; + } + + piface->next = hapd->preauth_iface; + hapd->preauth_iface = piface; + return 0; + +fail2: + free(piface->ifname); +fail1: + free(piface); + return -1; +} + + +void rsn_preauth_iface_deinit(struct hostapd_data *hapd) +{ + struct rsn_preauth_interface *piface, *prev; + + piface = hapd->preauth_iface; + hapd->preauth_iface = NULL; + while (piface) { + prev = piface; + piface = piface->next; + l2_packet_deinit(prev->l2); + free(prev->ifname); + free(prev); + } +} + + +int rsn_preauth_iface_init(struct hostapd_data *hapd) +{ + char *tmp, *start, *end; + + if (hapd->conf->rsn_preauth_interfaces == NULL) + return 0; + + tmp = strdup(hapd->conf->rsn_preauth_interfaces); + if (tmp == NULL) + return -1; + start = tmp; + for (;;) { + while (*start == ' ') + start++; + if (*start == '\0') + break; + end = strchr(start, ' '); + if (end) + *end = '\0'; + + if (rsn_preauth_iface_add(hapd, start)) { + rsn_preauth_iface_deinit(hapd); + return -1; + } + + if (end) + start = end + 1; + else + break; + } + free(tmp); + return 0; +} + + +static void rsn_preauth_finished_cb(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + wpa_printf(MSG_DEBUG, "RSN: Removing pre-authentication STA entry for " + MACSTR, MAC2STR(sta->addr)); + ap_free_sta(hapd, sta); +} + + +void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta, + int success) +{ + 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); + if (success && key) { + if (wpa_auth_pmksa_add_preauth(hapd->wpa_auth, key, len, + sta->addr, + dot11RSNAConfigPMKLifetime, + sta->eapol_sm) == 0) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, + "added PMKSA cache entry (pre-auth)"); + } else { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, + "failed to add PMKSA cache entry " + "(pre-auth)"); + } + } + + /* + * Finish STA entry removal from timeout in order to avoid freeing + * STA data before the caller has finished processing. + */ + eloop_register_timeout(0, 0, rsn_preauth_finished_cb, hapd, sta); +} + + +void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta, + u8 *buf, size_t len) +{ + struct rsn_preauth_interface *piface; + struct l2_ethhdr *ethhdr; + + piface = hapd->preauth_iface; + while (piface) { + if (piface == sta->preauth_iface) + break; + piface = piface->next; + } + + if (piface == NULL) { + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN: Could not find " + "pre-authentication interface for " MACSTR "\n", + MAC2STR(sta->addr)); + return; + } + + ethhdr = 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); + ethhdr->h_proto = htons(ETH_P_PREAUTH); + 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"); + } + free(ethhdr); +} + + +void rsn_preauth_free_station(struct hostapd_data *hapd, struct sta_info *sta) +{ + eloop_cancel_timeout(rsn_preauth_finished_cb, hapd, sta); +} + +#endif /* CONFIG_RSN_PREAUTH */ diff --git a/contrib/hostapd/preauth.h b/contrib/hostapd/preauth.h new file mode 100644 index 0000000..5348bee --- /dev/null +++ b/contrib/hostapd/preauth.h @@ -0,0 +1,58 @@ +/* + * hostapd - Authenticator for IEEE 802.11i RSN pre-authentication + * 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 PREAUTH_H +#define PREAUTH_H + +#ifdef CONFIG_RSN_PREAUTH + +int rsn_preauth_iface_init(struct hostapd_data *hapd); +void rsn_preauth_iface_deinit(struct hostapd_data *hapd); +void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta, + int success); +void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta, + u8 *buf, size_t len); +void rsn_preauth_free_station(struct hostapd_data *hapd, struct sta_info *sta); + +#else /* CONFIG_RSN_PREAUTH */ + +static inline int rsn_preauth_iface_init(struct hostapd_data *hapd) +{ + return 0; +} + +static inline void rsn_preauth_iface_deinit(struct hostapd_data *hapd) +{ +} + +static inline void rsn_preauth_finished(struct hostapd_data *hapd, + struct sta_info *sta, + int success) +{ +} + +static inline void rsn_preauth_send(struct hostapd_data *hapd, + struct sta_info *sta, + u8 *buf, size_t len) +{ +} + +static inline void rsn_preauth_free_station(struct hostapd_data *hapd, + struct sta_info *sta) +{ +} + +#endif /* CONFIG_RSN_PREAUTH */ + +#endif /* PREAUTH_H */ diff --git a/contrib/hostapd/radius.c b/contrib/hostapd/radius.c index ce79a62..bf39b2f 100644 --- a/contrib/hostapd/radius.c +++ b/contrib/hostapd/radius.c @@ -1,7 +1,6 @@ /* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / RADIUS client - * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * hostapd / RADIUS message processing + * Copyright (c) 2002-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 @@ -13,18 +12,7 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <string.h> -#include <signal.h> -#include <sys/time.h> -#ifndef CONFIG_NATIVE_WINDOWS -#include <netinet/in.h> -#include <sys/ioctl.h> -#include <sys/socket.h> -#include <arpa/inet.h> -#endif /* CONFIG_NATIVE_WINDOWS */ +#include "includes.h" #include "common.h" #include "radius.h" @@ -36,12 +24,12 @@ struct radius_msg *radius_msg_new(u8 code, u8 identifier) { struct radius_msg *msg; - msg = (struct radius_msg *) malloc(sizeof(*msg)); + msg = os_malloc(sizeof(*msg)); if (msg == NULL) return NULL; if (radius_msg_initialize(msg, RADIUS_DEFAULT_MSG_SIZE)) { - free(msg); + os_free(msg); return NULL; } @@ -56,20 +44,19 @@ int radius_msg_initialize(struct radius_msg *msg, size_t init_len) if (msg == NULL || init_len < sizeof(struct radius_hdr)) return -1; - memset(msg, 0, sizeof(*msg)); - msg->buf = (unsigned char *) malloc(init_len); + os_memset(msg, 0, sizeof(*msg)); + msg->buf = wpa_zalloc(init_len); if (msg->buf == NULL) return -1; - memset(msg->buf, 0, init_len); msg->buf_size = init_len; msg->hdr = (struct radius_hdr *) msg->buf; msg->buf_used = sizeof(*msg->hdr); - msg->attrs = (struct radius_attr_hdr **) - malloc(RADIUS_DEFAULT_ATTR_COUNT * sizeof(*msg->attrs)); + msg->attrs = + os_malloc(RADIUS_DEFAULT_ATTR_COUNT * sizeof(*msg->attrs)); if (msg->attrs == NULL) { - free(msg->buf); + os_free(msg->buf); msg->buf = NULL; msg->hdr = NULL; return -1; @@ -92,14 +79,14 @@ 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) { - free(msg->buf); + os_free(msg->buf); msg->buf = NULL; msg->hdr = NULL; } msg->buf_size = msg->buf_used = 0; if (msg->attrs != NULL) { - free(msg->attrs); + os_free(msg->attrs); msg->attrs = NULL; } msg->attr_size = msg->attr_used = 0; @@ -178,10 +165,15 @@ static struct radius_attr_type radius_attrs[] = { RADIUS_ATTR_EVENT_TIMESTAMP, "Event-Timestamp", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_NAS_PORT_TYPE, "NAS-Port-Type", RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_TUNNEL_TYPE, "Tunnel-Type", RADIUS_ATTR_HEXDUMP }, + { RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, "Tunnel-Medium-Type", + RADIUS_ATTR_HEXDUMP }, { RADIUS_ATTR_CONNECT_INFO, "Connect-Info", RADIUS_ATTR_TEXT }, { RADIUS_ATTR_EAP_MESSAGE, "EAP-Message", RADIUS_ATTR_UNDIST }, { RADIUS_ATTR_MESSAGE_AUTHENTICATOR, "Message-Authenticator", RADIUS_ATTR_UNDIST }, + { RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID, "Tunnel-Private-Group-Id", + RADIUS_ATTR_HEXDUMP }, { RADIUS_ATTR_ACCT_INTERIM_INTERVAL, "Acct-Interim-Interval", RADIUS_ATTR_INT32 }, { RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 }, @@ -191,7 +183,7 @@ static struct radius_attr_type radius_attrs[] = static struct radius_attr_type *radius_get_attr_type(u8 type) { - int i; + size_t i; for (i = 0; i < RADIUS_ATTRS; i++) { if (type == radius_attrs[i].type) @@ -202,6 +194,15 @@ static struct radius_attr_type *radius_get_attr_type(u8 type) } +static void print_char(char c) +{ + if (c >= 32 && c < 127) + printf("%c", c); + else + printf("<%02x>", c); +} + + static void radius_msg_dump_attr(struct radius_attr_hdr *hdr) { struct radius_attr_type *attr; @@ -257,11 +258,9 @@ static void radius_msg_dump_attr(struct radius_attr_hdr *hdr) break; case RADIUS_ATTR_INT32: - if (len == 4) { - u32 *val = (u32 *) pos; - printf(" Value: %u\n", - (unsigned int) ntohl(*val)); - } else + if (len == 4) + printf(" Value: %u\n", WPA_GET_BE32(pos)); + else printf(" Invalid INT32 length %d\n", len); break; @@ -273,7 +272,7 @@ static void radius_msg_dump_attr(struct radius_attr_hdr *hdr) void radius_msg_dump(struct radius_msg *msg) { - int i; + size_t i; printf("RADIUS message: code=%d (%s) identifier=%d length=%d\n", msg->hdr->code, radius_code_string(msg->hdr->code), @@ -291,7 +290,7 @@ int radius_msg_finish(struct radius_msg *msg, u8 *secret, size_t secret_len) u8 auth[MD5_MAC_LEN]; struct radius_attr_hdr *attr; - memset(auth, 0, MD5_MAC_LEN); + os_memset(auth, 0, MD5_MAC_LEN); attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, auth, MD5_MAC_LEN); @@ -323,7 +322,7 @@ int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, const u8 *addr[4]; size_t len[4]; - memset(auth, 0, MD5_MAC_LEN); + os_memset(auth, 0, MD5_MAC_LEN); attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, auth, MD5_MAC_LEN); if (attr == NULL) { @@ -331,8 +330,8 @@ int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, return -1; } msg->hdr->length = htons(msg->buf_used); - memcpy(msg->hdr->authenticator, req_authenticator, - sizeof(msg->hdr->authenticator)); + os_memcpy(msg->hdr->authenticator, req_authenticator, + sizeof(msg->hdr->authenticator)); hmac_md5(secret, secret_len, msg->buf, msg->buf_used, (u8 *) (attr + 1)); @@ -363,7 +362,7 @@ void radius_msg_finish_acct(struct radius_msg *msg, u8 *secret, size_t len[2]; msg->hdr->length = htons(msg->buf_used); - memset(msg->hdr->authenticator, 0, MD5_MAC_LEN); + os_memset(msg->hdr->authenticator, 0, MD5_MAC_LEN); addr[0] = msg->buf; len[0] = msg->buf_used; addr[1] = secret; @@ -384,9 +383,7 @@ static int radius_msg_add_attr_to_array(struct radius_msg *msg, struct radius_attr_hdr **nattrs; int nlen = msg->attr_size * 2; - nattrs = (struct radius_attr_hdr **) - realloc(msg->attrs, nlen * sizeof(*msg->attrs)); - + nattrs = os_realloc(msg->attrs, nlen * sizeof(*msg->attrs)); if (nattrs == NULL) return -1; @@ -417,12 +414,12 @@ 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; - int nlen = msg->buf_size; - int diff, i; + size_t i, nlen = msg->buf_size; + int diff; while (nlen < buf_needed) nlen *= 2; - nbuf = (unsigned char *) realloc(msg->buf, nlen); + nbuf = os_realloc(msg->buf, nlen); if (nbuf == NULL) return NULL; diff = nbuf - msg->buf; @@ -432,7 +429,7 @@ struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type, for (i = 0; i < msg->attr_used; i++) msg->attrs[i] = (struct radius_attr_hdr *) (((u8 *) msg->attrs[i]) + diff); - memset(msg->buf + msg->buf_size, 0, nlen - msg->buf_size); + os_memset(msg->buf + msg->buf_size, 0, nlen - msg->buf_size); msg->buf_size = nlen; } @@ -440,7 +437,7 @@ struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type, attr->type = type; attr->length = sizeof(*attr) + data_len; if (data_len > 0) - memcpy(attr + 1, data, data_len); + os_memcpy(attr + 1, data, data_len); msg->buf_used += sizeof(*attr) + data_len; @@ -475,23 +472,23 @@ struct radius_msg *radius_msg_parse(const u8 *data, size_t len) (unsigned long) len - msg_len); } - msg = (struct radius_msg *) malloc(sizeof(*msg)); + msg = os_malloc(sizeof(*msg)); if (msg == NULL) return NULL; if (radius_msg_initialize(msg, msg_len)) { - free(msg); + os_free(msg); return NULL; } - memcpy(msg->buf, data, msg_len); + os_memcpy(msg->buf, data, msg_len); msg->buf_size = msg->buf_used = msg_len; /* parse attributes */ pos = (unsigned char *) (msg->hdr + 1); end = msg->buf + msg->buf_used; while (pos < end) { - if (end - pos < sizeof(*attr)) + if ((size_t) (end - pos) < sizeof(*attr)) goto fail; attr = (struct radius_attr_hdr *) pos; @@ -511,7 +508,7 @@ struct radius_msg *radius_msg_parse(const u8 *data, size_t len) fail: radius_msg_free(msg); - free(msg); + os_free(msg); return NULL; } @@ -543,8 +540,7 @@ int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, size_t data_len) u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *eap_len) { u8 *eap, *pos; - size_t len; - int i; + size_t len, i; if (msg == NULL) return NULL; @@ -559,7 +555,7 @@ u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *eap_len) if (len == 0) return NULL; - eap = malloc(len); + eap = os_malloc(len); if (eap == NULL) return NULL; @@ -568,7 +564,7 @@ u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *eap_len) if (msg->attrs[i]->type == RADIUS_ATTR_EAP_MESSAGE) { struct radius_attr_hdr *attr = msg->attrs[i]; int flen = attr->length - sizeof(*attr); - memcpy(pos, attr + 1, flen); + os_memcpy(pos, attr + 1, flen); pos += flen; } } @@ -586,7 +582,7 @@ 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; - int i; + size_t i; for (i = 0; i < msg->attr_used; i++) { if (msg->attrs[i]->type == RADIUS_ATTR_MESSAGE_AUTHENTICATOR) { @@ -604,22 +600,22 @@ int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret, return 1; } - memcpy(orig, attr + 1, MD5_MAC_LEN); - memset(attr + 1, 0, MD5_MAC_LEN); + os_memcpy(orig, attr + 1, MD5_MAC_LEN); + os_memset(attr + 1, 0, MD5_MAC_LEN); if (req_auth) { - memcpy(orig_authenticator, msg->hdr->authenticator, - sizeof(orig_authenticator)); - memcpy(msg->hdr->authenticator, req_auth, - sizeof(msg->hdr->authenticator)); + os_memcpy(orig_authenticator, msg->hdr->authenticator, + sizeof(orig_authenticator)); + os_memcpy(msg->hdr->authenticator, req_auth, + sizeof(msg->hdr->authenticator)); } hmac_md5(secret, secret_len, msg->buf, msg->buf_used, auth); - memcpy(attr + 1, orig, MD5_MAC_LEN); + os_memcpy(attr + 1, orig, MD5_MAC_LEN); if (req_auth) { - memcpy(msg->hdr->authenticator, orig_authenticator, - sizeof(orig_authenticator)); + os_memcpy(msg->hdr->authenticator, orig_authenticator, + sizeof(orig_authenticator)); } - if (memcmp(orig, auth, MD5_MAC_LEN) != 0) { + if (os_memcmp(orig, auth, MD5_MAC_LEN) != 0) { printf("Invalid Message-Authenticator!\n"); return 1; } @@ -656,13 +652,12 @@ int radius_msg_verify(struct radius_msg *msg, const u8 *secret, addr[3] = secret; len[3] = secret_len; md5_vector(4, addr, len, hash); - if (memcmp(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) { + if (os_memcmp(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) { printf("Response Authenticator invalid!\n"); return 1; } return 0; - } @@ -670,7 +665,7 @@ int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src, u8 type) { struct radius_attr_hdr *attr = NULL; - int i; + size_t i; for (i = 0; i < src->attr_used; i++) { if (src->attrs[i]->type == type) { @@ -695,15 +690,15 @@ int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src, * Use one-way MD5 hash calculated from current timestamp and some data given * by the caller. */ void radius_msg_make_authenticator(struct radius_msg *msg, - u8 *data, size_t len) + const u8 *data, size_t len) { - struct timeval tv; + struct os_time tv; long int l; const u8 *addr[3]; size_t elen[3]; - gettimeofday(&tv, NULL); - l = random(); + os_get_time(&tv); + l = os_random(); addr[0] = (u8 *) &tv; elen[0] = sizeof(tv); addr[1] = data; @@ -717,21 +712,21 @@ void radius_msg_make_authenticator(struct radius_msg *msg, /* Get Vendor-specific RADIUS Attribute from a parsed RADIUS message. * Returns the Attribute payload and sets alen to indicate the length of the * payload if a vendor attribute with subtype is found, otherwise returns NULL. - * The returned payload is allocated with malloc() and caller must free it. + * The returned payload is allocated with os_malloc() and caller must free it + * by calling os_free(). */ static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor, u8 subtype, size_t *alen) { u8 *data, *pos; - int i; - size_t len; + size_t i, len; if (msg == NULL) return NULL; for (i = 0; i < msg->attr_used; i++) { struct radius_attr_hdr *attr = msg->attrs[i]; - int left; + size_t left; u32 vendor_id; struct radius_attr_vendor *vhdr; @@ -744,7 +739,7 @@ static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor, pos = (u8 *) (attr + 1); - memcpy(&vendor_id, pos, 4); + os_memcpy(&vendor_id, pos, 4); pos += 4; left -= 4; @@ -765,10 +760,10 @@ static u8 *radius_msg_get_vendor_attr(struct radius_msg *msg, u32 vendor, } len = vhdr->vendor_length - sizeof(*vhdr); - data = malloc(len); + data = os_malloc(len); if (data == NULL) return NULL; - memcpy(data, pos + sizeof(*vhdr), len); + os_memcpy(data, pos + sizeof(*vhdr), len); if (alen) *alen = len; return data; @@ -804,7 +799,7 @@ static u8 * decrypt_ms_key(const u8 *key, size_t len, } plen = left; - ppos = plain = malloc(plen); + ppos = plain = os_malloc(plen); if (plain == NULL) return NULL; @@ -833,19 +828,19 @@ static u8 * decrypt_ms_key(const u8 *key, size_t len, if (plain[0] > plen - 1) { printf("Failed to decrypt MPPE key\n"); - free(plain); + os_free(plain); return NULL; } - res = malloc(plain[0]); + res = os_malloc(plain[0]); if (res == NULL) { - free(plain); + os_free(plain); return NULL; } - memcpy(res, plain + 1, plain[0]); + os_memcpy(res, plain + 1, plain[0]); if (reslen) *reslen = plain[0]; - free(plain); + os_free(plain); return res; } @@ -867,9 +862,9 @@ static void encrypt_ms_key(const u8 *key, size_t key_len, u16 salt, if (len & 0x0f) { len = (len & 0xf0) + 16; } - memset(ebuf, 0, len); + os_memset(ebuf, 0, len); ebuf[0] = key_len; - memcpy(ebuf + 1, key, key_len); + os_memcpy(ebuf + 1, key, key_len); *elen = len; @@ -910,12 +905,10 @@ radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg, if (msg == NULL || sent_msg == NULL) return NULL; - keys = (struct radius_ms_mppe_keys *) malloc(sizeof(*keys)); + keys = wpa_zalloc(sizeof(*keys)); if (keys == NULL) return NULL; - memset(keys, 0, sizeof(*keys)); - key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT, RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY, &keylen); @@ -924,7 +917,7 @@ radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg, sent_msg->hdr->authenticator, secret, secret_len, &keys->send_len); - free(key); + os_free(key); } key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_MICROSOFT, @@ -935,7 +928,7 @@ radius_msg_get_ms_keys(struct radius_msg *msg, struct radius_msg *sent_msg, sent_msg->hdr->authenticator, secret, secret_len, &keys->recv_len); - free(key); + os_free(key); } return keys; @@ -953,21 +946,20 @@ radius_msg_get_cisco_keys(struct radius_msg *msg, struct radius_msg *sent_msg, if (msg == NULL || sent_msg == NULL) return NULL; - keys = (struct radius_ms_mppe_keys *) malloc(sizeof(*keys)); + keys = wpa_zalloc(sizeof(*keys)); if (keys == NULL) return NULL; - memset(keys, 0, sizeof(*keys)); - key = radius_msg_get_vendor_attr(msg, RADIUS_VENDOR_ID_CISCO, RADIUS_CISCO_AV_PAIR, &keylen); - if (key && keylen == 51 && memcmp(key, "leap:session-key=", 17) == 0) { + if (key && keylen == 51 && + os_memcmp(key, "leap:session-key=", 17) == 0) { keys->recv = decrypt_ms_key(key + 17, keylen - 17, sent_msg->hdr->authenticator, secret, secret_len, &keys->recv_len); - free(key); } + os_free(key); return keys; } @@ -991,17 +983,17 @@ int radius_msg_add_mppe_keys(struct radius_msg *msg, hlen = sizeof(vendor_id) + sizeof(*vhdr) + 2; /* MS-MPPE-Send-Key */ - buf = malloc(hlen + send_key_len + 16); + buf = os_malloc(hlen + send_key_len + 16); if (buf == NULL) { return 0; } pos = buf; - memcpy(pos, &vendor_id, sizeof(vendor_id)); + os_memcpy(pos, &vendor_id, sizeof(vendor_id)); pos += sizeof(vendor_id); vhdr = (struct radius_attr_vendor *) pos; vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY; pos = (u8 *) (vhdr + 1); - salt = random() | 0x8000; + salt = os_random() | 0x8000; *pos++ = salt >> 8; *pos++ = salt; encrypt_ms_key(send_key, send_key_len, salt, req_authenticator, secret, @@ -1010,18 +1002,18 @@ int radius_msg_add_mppe_keys(struct radius_msg *msg, attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, buf, hlen + elen); - free(buf); + os_free(buf); if (attr == NULL) { return 0; } /* MS-MPPE-Recv-Key */ - buf = malloc(hlen + send_key_len + 16); + buf = os_malloc(hlen + send_key_len + 16); if (buf == NULL) { return 0; } pos = buf; - memcpy(pos, &vendor_id, sizeof(vendor_id)); + os_memcpy(pos, &vendor_id, sizeof(vendor_id)); pos += sizeof(vendor_id); vhdr = (struct radius_attr_vendor *) pos; vhdr->vendor_type = RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY; @@ -1035,7 +1027,7 @@ int radius_msg_add_mppe_keys(struct radius_msg *msg, attr = radius_msg_add_attr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, buf, hlen + elen); - free(buf); + os_free(buf); if (attr == NULL) { return 0; } @@ -1052,8 +1044,8 @@ radius_msg_add_attr_user_password(struct radius_msg *msg, u8 *secret, size_t secret_len) { u8 buf[128]; - int padlen, i, pos; - size_t buf_len; + int padlen, i; + size_t buf_len, pos; const u8 *addr[2]; size_t len[2]; u8 hash[16]; @@ -1061,13 +1053,13 @@ radius_msg_add_attr_user_password(struct radius_msg *msg, if (data_len > 128) return NULL; - memcpy(buf, data, data_len); + os_memcpy(buf, data, data_len); buf_len = data_len; padlen = data_len % 16; if (padlen) { padlen = 16 - padlen; - memset(buf + data_len, 0, padlen); + os_memset(buf + data_len, 0, padlen); buf_len += padlen; } @@ -1101,9 +1093,8 @@ 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) { - int i; struct radius_attr_hdr *attr = NULL; - size_t dlen; + size_t i, dlen; for (i = 0; i < msg->attr_used; i++) { if (msg->attrs[i]->type == type) { @@ -1117,7 +1108,7 @@ int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len) dlen = attr->length - sizeof(*attr); if (buf) - memcpy(buf, (attr + 1), dlen > len ? len : dlen); + os_memcpy(buf, (attr + 1), dlen > len ? len : dlen); return dlen; } @@ -1125,7 +1116,7 @@ int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len) int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf, size_t *len, const u8 *start) { - int i; + size_t i; struct radius_attr_hdr *attr = NULL; for (i = 0; i < msg->attr_used; i++) { @@ -1147,7 +1138,8 @@ int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf, int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len) { - int i, count; + size_t i; + int count; for (count = 0, i = 0; i < msg->attr_used; i++) { if (msg->attrs[i]->type == type && @@ -1158,3 +1150,80 @@ int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len) return count; } + + +struct radius_tunnel_attrs { + int tag_used; + int type; /* Tunnel-Type */ + int medium_type; /* Tunnel-Medium-Type */ + int vlanid; +}; + + +/** + * radius_msg_get_vlanid - Parse RADIUS attributes for VLAN tunnel information + * @msg: RADIUS message + * Returns: VLAN ID for the first tunnel configuration of -1 if none is found + */ +int radius_msg_get_vlanid(struct radius_msg *msg) +{ + struct radius_tunnel_attrs tunnel[RADIUS_TUNNEL_TAGS], *tun; + size_t i; + struct radius_attr_hdr *attr = NULL; + const u8 *data; + char buf[10]; + size_t dlen; + + os_memset(&tunnel, 0, sizeof(tunnel)); + + for (i = 0; i < msg->attr_used; i++) { + attr = msg->attrs[i]; + data = (const u8 *) (attr + 1); + dlen = attr->length - sizeof(*attr); + if (attr->length < 3) + continue; + if (data[0] >= RADIUS_TUNNEL_TAGS) + tun = &tunnel[0]; + else + tun = &tunnel[data[0]]; + + switch (attr->type) { + case RADIUS_ATTR_TUNNEL_TYPE: + if (attr->length != 6) + break; + tun->tag_used++; + tun->type = (data[1] << 16) | (data[2] << 8) | data[3]; + 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]; + break; + case RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID: + if (data[0] < RADIUS_TUNNEL_TAGS) { + data++; + dlen--; + } + if (dlen >= sizeof(buf)) + break; + os_memcpy(buf, data, dlen); + buf[dlen] = '\0'; + tun->tag_used++; + tun->vlanid = atoi(buf); + break; + } + } + + for (i = 0; i < RADIUS_TUNNEL_TAGS; i++) { + tun = &tunnel[i]; + if (tun->tag_used && + tun->type == RADIUS_TUNNEL_TYPE_VLAN && + tun->medium_type == RADIUS_TUNNEL_MEDIUM_TYPE_802 && + tun->vlanid > 0) + return tun->vlanid; + } + + return -1; +} diff --git a/contrib/hostapd/radius.h b/contrib/hostapd/radius.h index b9f8977..d1b909d 100644 --- a/contrib/hostapd/radius.h +++ b/contrib/hostapd/radius.h @@ -1,15 +1,33 @@ +/* + * hostapd / RADIUS message processing + * Copyright (c) 2002-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 RADIUS_H #define RADIUS_H /* RFC 2865 - RADIUS */ +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + struct radius_hdr { u8 code; u8 identifier; u16 length; /* including this header */ u8 authenticator[16]; /* followed by length-20 octets of attributes */ -} __attribute__ ((packed)); +} STRUCT_PACKED; enum { RADIUS_CODE_ACCESS_REQUEST = 1, RADIUS_CODE_ACCESS_ACCEPT = 2, @@ -26,7 +44,7 @@ struct radius_attr_hdr { u8 type; u8 length; /* including this header */ /* followed by length-2 octets of attribute value */ -} __attribute__ ((packed)); +} STRUCT_PACKED; #define RADIUS_MAX_ATTR_LEN (255 - sizeof(struct radius_attr_hdr)) @@ -60,9 +78,12 @@ enum { RADIUS_ATTR_USER_NAME = 1, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS = 53, RADIUS_ATTR_EVENT_TIMESTAMP = 55, RADIUS_ATTR_NAS_PORT_TYPE = 61, + RADIUS_ATTR_TUNNEL_TYPE = 64, + RADIUS_ATTR_TUNNEL_MEDIUM_TYPE = 65, RADIUS_ATTR_CONNECT_INFO = 77, RADIUS_ATTR_EAP_MESSAGE = 79, RADIUS_ATTR_MESSAGE_AUTHENTICATOR = 80, + RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID = 81, RADIUS_ATTR_ACCT_INTERIM_INTERVAL = 85, RADIUS_ATTR_NAS_IPV6_ADDRESS = 95 }; @@ -107,11 +128,25 @@ enum { RADIUS_ATTR_USER_NAME = 1, #define RADIUS_ACCT_TERMINATE_CAUSE_USER_ERROR 17 #define RADIUS_ACCT_TERMINATE_CAUSE_HOST_REQUEST 18 +#define RADIUS_TUNNEL_TAGS 32 + +/* Tunnel-Type */ +#define RADIUS_TUNNEL_TYPE_PPTP 1 +#define RADIUS_TUNNEL_TYPE_L2TP 3 +#define RADIUS_TUNNEL_TYPE_IPIP 7 +#define RADIUS_TUNNEL_TYPE_GRE 10 +#define RADIUS_TUNNEL_TYPE_VLAN 13 + +/* Tunnel-Medium-Type */ +#define RADIUS_TUNNEL_MEDIUM_TYPE_IPV4 1 +#define RADIUS_TUNNEL_MEDIUM_TYPE_IPV6 2 +#define RADIUS_TUNNEL_MEDIUM_TYPE_802 6 + struct radius_attr_vendor { u8 vendor_type; u8 vendor_length; -} __attribute__ ((packed)); +} STRUCT_PACKED; #define RADIUS_VENDOR_ID_CISCO 9 #define RADIUS_CISCO_AV_PAIR 1 @@ -123,6 +158,10 @@ enum { RADIUS_VENDOR_ATTR_MS_MPPE_SEND_KEY = 16, RADIUS_VENDOR_ATTR_MS_MPPE_RECV_KEY = 17 }; +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + struct radius_ms_mppe_keys { u8 *send; size_t send_len; @@ -182,7 +221,7 @@ int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret, int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src, u8 type); void radius_msg_make_authenticator(struct radius_msg *msg, - u8 *data, size_t len); + 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); @@ -199,6 +238,7 @@ radius_msg_add_attr_user_password(struct radius_msg *msg, u8 *data, size_t data_len, 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); static inline int radius_msg_add_attr_int32(struct radius_msg *msg, u8 type, u32 value) diff --git a/contrib/hostapd/radius_client.c b/contrib/hostapd/radius_client.c index abc28bd..5b00bbe 100644 --- a/contrib/hostapd/radius_client.c +++ b/contrib/hostapd/radius_client.c @@ -1,7 +1,6 @@ /* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / RADIUS client - * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * hostapd / RADIUS client + * Copyright (c) 2002-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 @@ -13,19 +12,7 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <string.h> -#include <time.h> -#include <sys/types.h> -#include <sys/time.h> -#include <errno.h> -#ifndef CONFIG_NATIVE_WINDOWS -#include <netinet/in.h> -#include <sys/socket.h> -#include <arpa/inet.h> -#endif /* CONFIG_NATIVE_WINDOWS */ +#include "includes.h" #include "hostapd.h" #include "radius.h" @@ -60,11 +47,11 @@ struct radius_msg_list { * for the same STA. */ struct radius_msg *msg; RadiusType msg_type; - time_t first_try; - time_t next_try; + os_time_t first_try; + os_time_t next_try; int attempts; int next_wait; - struct timeval last_attempt; + struct os_time last_attempt; u8 *shared_secret; size_t shared_secret_len; @@ -110,8 +97,8 @@ static int radius_client_init_auth(struct radius_client_data *radius); static void radius_client_msg_free(struct radius_msg_list *req) { radius_msg_free(req->msg); - free(req->msg); - free(req); + os_free(req->msg); + os_free(req); } @@ -135,9 +122,8 @@ int radius_client_register(struct radius_client_data *radius, num = &radius->num_auth_handlers; } - newh = (struct radius_rx_handler *) - realloc(*handlers, - (*num + 1) * sizeof(struct radius_rx_handler)); + newh = os_realloc(*handlers, + (*num + 1) * sizeof(struct radius_rx_handler)); if (newh == NULL) return -1; @@ -173,7 +159,8 @@ static void radius_client_handle_send_error(struct radius_client_data *radius, static int radius_client_retransmit(struct radius_client_data *radius, - struct radius_msg_list *entry, time_t now) + struct radius_msg_list *entry, + os_time_t now) { struct hostapd_radius_servers *conf = radius->conf; int s; @@ -203,7 +190,7 @@ static int radius_client_retransmit(struct radius_client_data *radius, HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)", entry->msg->hdr->identifier); - gettimeofday(&entry->last_attempt, NULL); + os_get_time(&entry->last_attempt); if (send(s, entry->msg->buf, entry->msg->buf_used, 0) < 0) radius_client_handle_send_error(radius, s, entry->msg_type); @@ -225,7 +212,8 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) { struct radius_client_data *radius = eloop_ctx; struct hostapd_radius_servers *conf = radius->conf; - time_t now, first; + struct os_time now; + os_time_t first; struct radius_msg_list *entry, *prev, *tmp; int auth_failover = 0, acct_failover = 0; char abuf[50]; @@ -234,13 +222,13 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) if (!entry) return; - time(&now); + os_get_time(&now); first = 0; prev = NULL; while (entry) { - if (now >= entry->next_try && - radius_client_retransmit(radius, entry, now)) { + if (now.sec >= entry->next_try && + radius_client_retransmit(radius, entry, now.sec)) { if (prev) prev->next = entry->next; else @@ -269,14 +257,14 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) } if (radius->msgs) { - if (first < now) - first = now; - eloop_register_timeout(first - now, 0, + if (first < now.sec) + first = now.sec; + eloop_register_timeout(first - now.sec, 0, radius_client_timer, radius, NULL); hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, "Next RADIUS client " "retransmit in %ld seconds", - (long int) (first - now)); + (long int) (first - now.sec)); } if (auth_failover && conf->num_auth_servers > 1) { @@ -332,7 +320,8 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) static void radius_client_update_timeout(struct radius_client_data *radius) { - time_t now, first; + struct os_time now; + os_time_t first; struct radius_msg_list *entry; eloop_cancel_timeout(radius_client_timer, radius, NULL); @@ -347,21 +336,21 @@ static void radius_client_update_timeout(struct radius_client_data *radius) first = entry->next_try; } - time(&now); - if (first < now) - first = now; - eloop_register_timeout(first - now, 0, radius_client_timer, radius, + os_get_time(&now); + if (first < now.sec) + first = now.sec; + eloop_register_timeout(first - now.sec, 0, radius_client_timer, radius, NULL); hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, "Next RADIUS client retransmit in" - " %ld seconds\n", (long int) (first - now)); + " %ld seconds\n", (long int) (first - now.sec)); } static void radius_client_list_add(struct radius_client_data *radius, struct radius_msg *msg, RadiusType msg_type, u8 *shared_secret, - size_t shared_secret_len, u8 *addr) + size_t shared_secret_len, const u8 *addr) { struct radius_msg_list *entry, *prev; @@ -369,29 +358,28 @@ static void radius_client_list_add(struct radius_client_data *radius, /* No point in adding entries to retransmit queue since event * loop has already been terminated. */ radius_msg_free(msg); - free(msg); + os_free(msg); return; } - entry = malloc(sizeof(*entry)); + entry = wpa_zalloc(sizeof(*entry)); if (entry == NULL) { printf("Failed to add RADIUS packet into retransmit list\n"); radius_msg_free(msg); - free(msg); + os_free(msg); return; } - memset(entry, 0, sizeof(*entry)); if (addr) - memcpy(entry->addr, addr, ETH_ALEN); + os_memcpy(entry->addr, addr, ETH_ALEN); entry->msg = msg; entry->msg_type = msg_type; entry->shared_secret = shared_secret; entry->shared_secret_len = shared_secret_len; - time(&entry->first_try); + os_get_time(&entry->last_attempt); + entry->first_try = entry->last_attempt.sec; entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT; entry->attempts = 1; - gettimeofday(&entry->last_attempt, NULL); entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2; entry->next = radius->msgs; radius->msgs = entry; @@ -415,7 +403,7 @@ static void radius_client_list_add(struct radius_client_data *radius, static void radius_client_list_del(struct radius_client_data *radius, - RadiusType msg_type, u8 *addr) + RadiusType msg_type, const u8 *addr) { struct radius_msg_list *entry, *prev, *tmp; @@ -426,7 +414,7 @@ static void radius_client_list_del(struct radius_client_data *radius, prev = NULL; while (entry) { if (entry->msg_type == msg_type && - memcmp(entry->addr, addr, ETH_ALEN) == 0) { + os_memcmp(entry->addr, addr, ETH_ALEN) == 0) { if (prev) prev->next = entry->next; else @@ -448,7 +436,8 @@ static void radius_client_list_del(struct radius_client_data *radius, int radius_client_send(struct radius_client_data *radius, - struct radius_msg *msg, RadiusType msg_type, u8 *addr) + struct radius_msg *msg, RadiusType msg_type, + const u8 *addr) { struct hostapd_radius_servers *conf = radius->conf; u8 *shared_secret; @@ -499,13 +488,13 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) struct radius_client_data *radius = eloop_ctx; struct hostapd_radius_servers *conf = radius->conf; RadiusType msg_type = (RadiusType) sock_ctx; - int len, i, roundtrip; + int len, roundtrip; unsigned char buf[3000]; struct radius_msg *msg; struct radius_rx_handler *handlers; - size_t num_handlers; + size_t num_handlers, i; struct radius_msg_list *req, *prev_req; - struct timeval tv; + struct os_time now; struct hostapd_radius_server *rconf; int invalid_authenticator = 0; @@ -584,9 +573,9 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) goto fail; } - gettimeofday(&tv, NULL); - roundtrip = (tv.tv_sec - req->last_attempt.tv_sec) * 100 + - (tv.tv_usec - req->last_attempt.tv_usec) / 10000; + os_get_time(&now); + roundtrip = (now.sec - req->last_attempt.sec) * 100 + + (now.usec - req->last_attempt.usec) / 10000; hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, "Received RADIUS packet matched with a pending " @@ -609,7 +598,7 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) switch (res) { case RADIUS_RX_PROCESSED: radius_msg_free(msg); - free(msg); + os_free(msg); /* continue */ case RADIUS_RX_QUEUED: radius_client_msg_free(req); @@ -637,13 +626,13 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) fail: radius_msg_free(msg); - free(msg); + os_free(msg); } u8 radius_client_get_id(struct radius_client_data *radius) { - struct radius_msg_list *entry, *prev, *remove; + struct radius_msg_list *entry, *prev, *_remove; u8 id = radius->next_radius_identifier++; /* remove entries with matching id from retransmit list to avoid @@ -661,36 +650,69 @@ u8 radius_client_get_id(struct radius_client_data *radius) prev->next = entry->next; else radius->msgs = entry->next; - remove = entry; - } else - remove = NULL; - prev = entry; + _remove = entry; + } else { + _remove = NULL; + prev = entry; + } entry = entry->next; - if (remove) - radius_client_msg_free(remove); + if (_remove) + radius_client_msg_free(_remove); } return id; } -void radius_client_flush(struct radius_client_data *radius) +void radius_client_flush(struct radius_client_data *radius, int only_auth) { - struct radius_msg_list *entry, *prev; + struct radius_msg_list *entry, *prev, *tmp; if (!radius) return; - eloop_cancel_timeout(radius_client_timer, radius, NULL); - + prev = NULL; entry = radius->msgs; - radius->msgs = NULL; - radius->num_msgs = 0; + while (entry) { - prev = entry; - entry = entry->next; - radius_client_msg_free(prev); + if (!only_auth || entry->msg_type == RADIUS_AUTH) { + if (prev) + prev->next = entry->next; + else + radius->msgs = entry->next; + + tmp = entry; + entry = entry->next; + radius_client_msg_free(tmp); + radius->num_msgs--; + } else { + prev = entry; + entry = entry->next; + } + } + + if (radius->msgs == NULL) + eloop_cancel_timeout(radius_client_timer, radius, NULL); +} + + +void radius_client_update_acct_msgs(struct radius_client_data *radius, + u8 *shared_secret, + size_t shared_secret_len) +{ + struct radius_msg_list *entry; + + if (!radius) + return; + + for (entry = radius->msgs; entry; entry = entry->next) { + if (entry->msg_type == RADIUS_ACCT) { + entry->shared_secret = shared_secret; + entry->shared_secret_len = shared_secret_len; + radius_msg_finish_acct(entry->msg, shared_secret, + shared_secret_len); + } } } @@ -709,6 +731,7 @@ radius_change_server(struct radius_client_data *radius, socklen_t addrlen; char abuf[50]; int sel_sock; + struct radius_msg_list *entry; hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, @@ -718,37 +741,43 @@ radius_change_server(struct radius_client_data *radius, nserv->port); if (!oserv || nserv->shared_secret_len != oserv->shared_secret_len || - memcmp(nserv->shared_secret, oserv->shared_secret, - nserv->shared_secret_len) != 0) { - /* Pending RADIUS packets used different shared - * secret, so they would need to be modified. Could - * update all message authenticators and - * User-Passwords, etc. and retry with new server. For - * now, just drop all pending packets. */ - radius_client_flush(radius); - } else { - /* Reset retry counters for the new server */ - struct radius_msg_list *entry; - entry = radius->msgs; - while (entry) { - entry->next_try = entry->first_try + - RADIUS_CLIENT_FIRST_WAIT; - entry->attempts = 0; - entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2; - entry = entry->next; - } - if (radius->msgs) { - eloop_cancel_timeout(radius_client_timer, radius, - NULL); - eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0, - radius_client_timer, radius, - NULL); + os_memcmp(nserv->shared_secret, oserv->shared_secret, + nserv->shared_secret_len) != 0) { + /* Pending RADIUS packets used different shared secret, so + * they need to be modified. Update accounting message + * authenticators here. Authentication messages are removed + * since they would require more changes and the new RADIUS + * server may not be prepared to receive them anyway due to + * missing state information. Client will likely retry + * authentication, so this should not be an issue. */ + if (auth) + radius_client_flush(radius, 1); + else { + radius_client_update_acct_msgs( + radius, nserv->shared_secret, + nserv->shared_secret_len); } } + /* Reset retry counters for the new server */ + for (entry = radius->msgs; entry; entry = entry->next) { + if ((auth && entry->msg_type != RADIUS_AUTH) || + (!auth && entry->msg_type != RADIUS_ACCT)) + continue; + entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT; + entry->attempts = 0; + entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2; + } + + if (radius->msgs) { + eloop_cancel_timeout(radius_client_timer, radius, NULL); + eloop_register_timeout(RADIUS_CLIENT_FIRST_WAIT, 0, + radius_client_timer, radius, NULL); + } + switch (nserv->addr.af) { case AF_INET: - memset(&serv, 0, sizeof(serv)); + os_memset(&serv, 0, sizeof(serv)); serv.sin_family = AF_INET; serv.sin_addr.s_addr = nserv->addr.u.v4.s_addr; serv.sin_port = htons(nserv->port); @@ -758,10 +787,10 @@ radius_change_server(struct radius_client_data *radius, break; #ifdef CONFIG_IPV6 case AF_INET6: - memset(&serv6, 0, sizeof(serv6)); + os_memset(&serv6, 0, sizeof(serv6)); serv6.sin6_family = AF_INET6; - memcpy(&serv6.sin6_addr, &nserv->addr.u.v6, - sizeof(struct in6_addr)); + os_memcpy(&serv6.sin6_addr, &nserv->addr.u.v6, + sizeof(struct in6_addr)); serv6.sin6_port = htons(nserv->port); addr = (struct sockaddr *) &serv6; addrlen = sizeof(serv6); @@ -878,6 +907,17 @@ static int radius_client_init_acct(struct radius_client_data *radius) else ok++; +#ifdef CONFIG_IPV6 + radius->acct_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0); + if (radius->acct_serv_sock6 < 0) + perror("socket[PF_INET6,SOCK_DGRAM]"); + else + ok++; +#endif /* CONFIG_IPV6 */ + + if (ok == 0) + return -1; + radius_change_server(radius, conf->acct_server, NULL, radius->acct_serv_sock, radius->acct_serv_sock6, 0); @@ -911,11 +951,10 @@ radius_client_init(void *ctx, struct hostapd_radius_servers *conf) { struct radius_client_data *radius; - radius = malloc(sizeof(struct radius_client_data)); + radius = wpa_zalloc(sizeof(struct radius_client_data)); if (radius == NULL) return NULL; - memset(radius, 0, sizeof(struct radius_client_data)); radius->ctx = ctx; radius->conf = conf; radius->auth_serv_sock = radius->acct_serv_sock = @@ -946,12 +985,17 @@ void radius_client_deinit(struct radius_client_data *radius) if (!radius) return; + if (radius->auth_serv_sock >= 0) + eloop_unregister_read_sock(radius->auth_serv_sock); + if (radius->acct_serv_sock >= 0) + eloop_unregister_read_sock(radius->acct_serv_sock); + eloop_cancel_timeout(radius_retry_primary_timer, radius, NULL); - radius_client_flush(radius); - free(radius->auth_handlers); - free(radius->acct_handlers); - free(radius); + radius_client_flush(radius, 0); + os_free(radius->auth_handlers); + os_free(radius->acct_handlers); + os_free(radius); } @@ -963,7 +1007,7 @@ void radius_client_flush_auth(struct radius_client_data *radius, u8 *addr) entry = radius->msgs; while (entry) { if (entry->msg_type == RADIUS_AUTH && - memcmp(entry->addr, addr, ETH_ALEN) == 0) { + os_memcmp(entry->addr, addr, ETH_ALEN) == 0) { hostapd_logger(radius->ctx, addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, @@ -1003,37 +1047,37 @@ static int radius_client_dump_auth_server(char *buf, size_t buflen, } } - return snprintf(buf, buflen, - "radiusAuthServerIndex=%d\n" - "radiusAuthServerAddress=%s\n" - "radiusAuthClientServerPortNumber=%d\n" - "radiusAuthClientRoundTripTime=%d\n" - "radiusAuthClientAccessRequests=%u\n" - "radiusAuthClientAccessRetransmissions=%u\n" - "radiusAuthClientAccessAccepts=%u\n" - "radiusAuthClientAccessRejects=%u\n" - "radiusAuthClientAccessChallenges=%u\n" - "radiusAuthClientMalformedAccessResponses=%u\n" - "radiusAuthClientBadAuthenticators=%u\n" - "radiusAuthClientPendingRequests=%u\n" - "radiusAuthClientTimeouts=%u\n" - "radiusAuthClientUnknownTypes=%u\n" - "radiusAuthClientPacketsDropped=%u\n", - serv->index, - hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)), - serv->port, - serv->round_trip_time, - serv->requests, - serv->retransmissions, - serv->access_accepts, - serv->access_rejects, - serv->access_challenges, - serv->malformed_responses, - serv->bad_authenticators, - pending, - serv->timeouts, - serv->unknown_types, - serv->packets_dropped); + return os_snprintf(buf, buflen, + "radiusAuthServerIndex=%d\n" + "radiusAuthServerAddress=%s\n" + "radiusAuthClientServerPortNumber=%d\n" + "radiusAuthClientRoundTripTime=%d\n" + "radiusAuthClientAccessRequests=%u\n" + "radiusAuthClientAccessRetransmissions=%u\n" + "radiusAuthClientAccessAccepts=%u\n" + "radiusAuthClientAccessRejects=%u\n" + "radiusAuthClientAccessChallenges=%u\n" + "radiusAuthClientMalformedAccessResponses=%u\n" + "radiusAuthClientBadAuthenticators=%u\n" + "radiusAuthClientPendingRequests=%u\n" + "radiusAuthClientTimeouts=%u\n" + "radiusAuthClientUnknownTypes=%u\n" + "radiusAuthClientPacketsDropped=%u\n", + serv->index, + hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)), + serv->port, + serv->round_trip_time, + serv->requests, + serv->retransmissions, + serv->access_accepts, + serv->access_rejects, + serv->access_challenges, + serv->malformed_responses, + serv->bad_authenticators, + pending, + serv->timeouts, + serv->unknown_types, + serv->packets_dropped); } @@ -1053,33 +1097,33 @@ static int radius_client_dump_acct_server(char *buf, size_t buflen, } } - return snprintf(buf, buflen, - "radiusAccServerIndex=%d\n" - "radiusAccServerAddress=%s\n" - "radiusAccClientServerPortNumber=%d\n" - "radiusAccClientRoundTripTime=%d\n" - "radiusAccClientRequests=%u\n" - "radiusAccClientRetransmissions=%u\n" - "radiusAccClientResponses=%u\n" - "radiusAccClientMalformedResponses=%u\n" - "radiusAccClientBadAuthenticators=%u\n" - "radiusAccClientPendingRequests=%u\n" - "radiusAccClientTimeouts=%u\n" - "radiusAccClientUnknownTypes=%u\n" - "radiusAccClientPacketsDropped=%u\n", - serv->index, - hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)), - serv->port, - serv->round_trip_time, - serv->requests, - serv->retransmissions, - serv->responses, - serv->malformed_responses, - serv->bad_authenticators, - pending, - serv->timeouts, - serv->unknown_types, - serv->packets_dropped); + return os_snprintf(buf, buflen, + "radiusAccServerIndex=%d\n" + "radiusAccServerAddress=%s\n" + "radiusAccClientServerPortNumber=%d\n" + "radiusAccClientRoundTripTime=%d\n" + "radiusAccClientRequests=%u\n" + "radiusAccClientRetransmissions=%u\n" + "radiusAccClientResponses=%u\n" + "radiusAccClientMalformedResponses=%u\n" + "radiusAccClientBadAuthenticators=%u\n" + "radiusAccClientPendingRequests=%u\n" + "radiusAccClientTimeouts=%u\n" + "radiusAccClientUnknownTypes=%u\n" + "radiusAccClientPacketsDropped=%u\n", + serv->index, + hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)), + serv->port, + serv->round_trip_time, + serv->requests, + serv->retransmissions, + serv->responses, + serv->malformed_responses, + serv->bad_authenticators, + pending, + serv->timeouts, + serv->unknown_types, + serv->packets_dropped); } @@ -1113,3 +1157,48 @@ int radius_client_get_mib(struct radius_client_data *radius, char *buf, return count; } + + +static int radius_servers_diff(struct hostapd_radius_server *nserv, + struct hostapd_radius_server *oserv, + int num) +{ + int i; + + for (i = 0; i < num; i++) { + 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) + return 1; + } + + return 0; +} + + +struct radius_client_data * +radius_client_reconfig(struct radius_client_data *old, void *ctx, + struct hostapd_radius_servers *oldconf, + struct hostapd_radius_servers *newconf) +{ + radius_client_flush(old, 0); + + if (newconf->retry_primary_interval != + oldconf->retry_primary_interval || + newconf->num_auth_servers != oldconf->num_auth_servers || + newconf->num_acct_servers != oldconf->num_acct_servers || + radius_servers_diff(newconf->auth_servers, oldconf->auth_servers, + newconf->num_auth_servers) || + radius_servers_diff(newconf->acct_servers, oldconf->acct_servers, + newconf->num_acct_servers)) { + hostapd_logger(ctx, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, + "Reconfiguring RADIUS client"); + radius_client_deinit(old); + return radius_client_init(ctx, newconf); + } + + return old; +} diff --git a/contrib/hostapd/radius_client.h b/contrib/hostapd/radius_client.h index d21ca83..df1ba1f 100644 --- a/contrib/hostapd/radius_client.h +++ b/contrib/hostapd/radius_client.h @@ -1,3 +1,17 @@ +/* + * hostapd / RADIUS client + * Copyright (c) 2002-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 RADIUS_CLIENT_H #define RADIUS_CLIENT_H @@ -73,15 +87,19 @@ int radius_client_register(struct radius_client_data *radius, void *data); int radius_client_send(struct radius_client_data *radius, struct radius_msg *msg, - RadiusType msg_type, u8 *addr); + RadiusType msg_type, const u8 *addr); u8 radius_client_get_id(struct radius_client_data *radius); -void radius_client_flush(struct radius_client_data *radius); +void radius_client_flush(struct radius_client_data *radius, int only_auth); struct radius_client_data * radius_client_init(void *ctx, struct hostapd_radius_servers *conf); void radius_client_deinit(struct radius_client_data *radius); void radius_client_flush_auth(struct radius_client_data *radius, u8 *addr); int radius_client_get_mib(struct radius_client_data *radius, char *buf, size_t buflen); +struct radius_client_data * +radius_client_reconfig(struct radius_client_data *old, void *ctx, + struct hostapd_radius_servers *oldconf, + struct hostapd_radius_servers *newconf); #endif /* RADIUS_CLIENT_H */ diff --git a/contrib/hostapd/radius_server.c b/contrib/hostapd/radius_server.c index bda8bd0..bb78f75 100644 --- a/contrib/hostapd/radius_server.c +++ b/contrib/hostapd/radius_server.c @@ -1,6 +1,6 @@ /* * hostapd / RADIUS authentication server - * Copyright (c) 2005, Jouni Malinen <jkmaline@cc.hut.fi> + * 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 @@ -12,13 +12,7 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <unistd.h> -#include <string.h> -#include <netinet/in.h> -#include <sys/socket.h> -#include <arpa/inet.h> +#include "includes.h" #include <net/if.h> #include "common.h" @@ -37,6 +31,19 @@ static struct eapol_callbacks radius_server_eapol_cb; struct radius_client; struct radius_server_data; +struct radius_server_counters { + u32 access_requests; + u32 invalid_requests; + u32 dup_access_requests; + u32 access_accepts; + u32 access_rejects; + u32 access_challenges; + u32 malformed_access_requests; + u32 bad_authenticators; + u32 packets_dropped; + u32 unknown_types; +}; + struct radius_session { struct radius_session *next; struct radius_client *client; @@ -47,6 +54,15 @@ struct radius_session { size_t eapKeyDataLen, eapReqDataLen; Boolean eapSuccess, eapRestart, eapFail, eapResp, eapReq, eapNoReq; Boolean portEnabled, eapTimeout; + + struct radius_msg *last_msg; + char *last_from_addr; + int last_from_port; + struct sockaddr_storage last_from; + socklen_t last_fromlen; + u8 last_identifier; + struct radius_msg *last_reply; + u8 last_authenticator[16]; }; struct radius_client { @@ -60,18 +76,20 @@ struct radius_client { char *shared_secret; int shared_secret_len; struct radius_session *sessions; + struct radius_server_counters counters; }; struct radius_server_data { int auth_sock; struct radius_client *clients; - struct radius_server_session *sessions; unsigned int next_sess_id; void *hostapd_conf; int num_sess; void *eap_sim_db_priv; void *ssl_ctx; int ipv6; + struct os_time start_time; + struct radius_server_counters counters; }; @@ -153,17 +171,31 @@ static void radius_server_session_free(struct radius_server_data *data, free(sess->eapKeyData); free(sess->eapReqData); eap_sm_deinit(sess->eap); + if (sess->last_msg) { + radius_msg_free(sess->last_msg); + free(sess->last_msg); + } + free(sess->last_from_addr); + if (sess->last_reply) { + radius_msg_free(sess->last_reply); + free(sess->last_reply); + } free(sess); data->num_sess--; } +static void radius_server_session_remove_timeout(void *eloop_ctx, + void *timeout_ctx); + static void radius_server_session_remove(struct radius_server_data *data, struct radius_session *sess) { struct radius_client *client = sess->client; struct radius_session *session, *prev; + eloop_cancel_timeout(radius_server_session_remove_timeout, data, sess); + prev = NULL; session = client->sessions; while (session) { @@ -182,6 +214,16 @@ static void radius_server_session_remove(struct radius_server_data *data, } +static void radius_server_session_remove_timeout(void *eloop_ctx, + void *timeout_ctx) +{ + struct radius_server_data *data = eloop_ctx; + struct radius_session *sess = timeout_ctx; + RADIUS_DEBUG("Removing completed session 0x%x", sess->sess_id); + radius_server_session_remove(data, sess); +} + + static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx) { struct radius_server_data *data = eloop_ctx; @@ -204,11 +246,10 @@ radius_server_new_session(struct radius_server_data *data, return NULL; } - sess = malloc(sizeof(*sess)); - if (sess == NULL) { + sess = wpa_zalloc(sizeof(*sess)); + if (sess == NULL) return NULL; - } - memset(sess, 0, sizeof(*sess)); + sess->server = data; sess->client = client; sess->sess_id = data->next_sess_id++; @@ -303,6 +344,7 @@ radius_server_encapsulate_eap(struct radius_server_data *data, msg = radius_msg_new(code, request->hdr->identifier); if (msg == NULL) { + RADIUS_DEBUG("Failed to allocate reply message"); return NULL; } @@ -383,6 +425,8 @@ static int radius_server_reject(struct radius_server_data *data, radius_msg_dump(msg); } + data->counters.access_rejects++; + client->counters.access_rejects++; if (sendto(data->auth_sock, msg->buf, msg->buf_used, 0, (struct sockaddr *) from, sizeof(*from)) < 0) { perror("sendto[RADIUS SRV]"); @@ -400,28 +444,31 @@ static int radius_server_request(struct radius_server_data *data, struct radius_msg *msg, struct sockaddr *from, socklen_t fromlen, struct radius_client *client, - const char *from_addr, int from_port) + const char *from_addr, int from_port, + struct radius_session *force_sess) { u8 *eap = NULL; size_t eap_len; - int res, state_included; + int res, state_included = 0; u8 statebuf[4], resp_id; unsigned int state; struct radius_session *sess; struct radius_msg *reply; struct eap_hdr *hdr; - /* TODO: Implement duplicate packet processing */ - - res = radius_msg_get_attr(msg, RADIUS_ATTR_STATE, statebuf, - sizeof(statebuf)); - state_included = res >= 0; - if (res == sizeof(statebuf)) { - state = (statebuf[0] << 24) | (statebuf[1] << 16) | - (statebuf[2] << 8) | statebuf[3]; - sess = radius_server_get_session(client, state); - } else { - sess = NULL; + if (force_sess) + sess = force_sess; + else { + res = radius_msg_get_attr(msg, RADIUS_ATTR_STATE, statebuf, + sizeof(statebuf)); + state_included = res >= 0; + if (res == sizeof(statebuf)) { + state = (statebuf[0] << 24) | (statebuf[1] << 16) | + (statebuf[2] << 8) | statebuf[3]; + sess = radius_server_get_session(client, state); + } else { + sess = NULL; + } } if (sess) { @@ -441,10 +488,35 @@ static int radius_server_request(struct radius_server_data *data, } } + if (sess->last_from_port == from_port && + sess->last_identifier == msg->hdr->identifier && + os_memcmp(sess->last_authenticator, msg->hdr->authenticator, 16) == + 0) { + RADIUS_DEBUG("Duplicate message from %s", from_addr); + data->counters.dup_access_requests++; + client->counters.dup_access_requests++; + + if (sess->last_reply) { + res = sendto(data->auth_sock, sess->last_reply->buf, + sess->last_reply->buf_used, 0, + (struct sockaddr *) from, fromlen); + if (res < 0) { + perror("sendto[RADIUS SRV]"); + } + return 0; + } + + RADIUS_DEBUG("No previous reply available for duplicate " + "message"); + return -1; + } + eap = radius_msg_get_eap(msg, &eap_len); if (eap == NULL) { RADIUS_DEBUG("No EAP-Message in RADIUS packet from %s", from_addr); + data->counters.packets_dropped++; + client->counters.packets_dropped++; return -1; } @@ -475,18 +547,31 @@ static int radius_server_request(struct radius_server_data *data, } else if (sess->eapFail) { RADIUS_DEBUG("No EAP data from the state machine, but eapFail " "set - generate EAP-Failure"); - hdr = malloc(sizeof(*hdr)); + hdr = wpa_zalloc(sizeof(*hdr)); if (hdr) { - memset(hdr, 0, sizeof(*hdr)); hdr->identifier = resp_id; hdr->length = htons(sizeof(*hdr)); sess->eapReqData = (u8 *) hdr; sess->eapReqDataLen = sizeof(*hdr); } + } else if (eap_sm_method_pending(sess->eap)) { + if (sess->last_msg) { + radius_msg_free(sess->last_msg); + 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); + sess->last_fromlen = fromlen; + memcpy(&sess->last_from, from, fromlen); + return -2; } else { RADIUS_DEBUG("No EAP data from the state machine - ignore this" " Access-Request silently (assuming it was a " "duplicate)"); + data->counters.packets_dropped++; + client->counters.packets_dropped++; return -1; } @@ -502,18 +587,47 @@ static int radius_server_request(struct radius_server_data *data, radius_msg_dump(reply); } + switch (reply->hdr->code) { + case RADIUS_CODE_ACCESS_ACCEPT: + data->counters.access_accepts++; + client->counters.access_accepts++; + break; + case RADIUS_CODE_ACCESS_REJECT: + data->counters.access_rejects++; + client->counters.access_rejects++; + break; + case RADIUS_CODE_ACCESS_CHALLENGE: + data->counters.access_challenges++; + client->counters.access_challenges++; + break; + } res = sendto(data->auth_sock, reply->buf, reply->buf_used, 0, (struct sockaddr *) from, fromlen); if (res < 0) { perror("sendto[RADIUS SRV]"); } - radius_msg_free(reply); - free(reply); + if (sess->last_reply) { + radius_msg_free(sess->last_reply); + free(sess->last_reply); + } + sess->last_reply = reply; + sess->last_from_port = from_port; + sess->last_identifier = msg->hdr->identifier; + os_memcpy(sess->last_authenticator, msg->hdr->authenticator, + 16); + } else { + data->counters.packets_dropped++; + client->counters.packets_dropped++; } if (sess->eapSuccess || sess->eapFail) { - RADIUS_DEBUG("Removing completed session 0x%x", sess->sess_id); - radius_server_session_remove(data, sess); + RADIUS_DEBUG("Removing completed session 0x%x after timeout", + sess->sess_id); + eloop_cancel_timeout(radius_server_session_remove_timeout, + data, sess); + eloop_register_timeout(10, 0, + radius_server_session_remove_timeout, + data, sess); } return 0; @@ -576,12 +690,15 @@ static void radius_server_receive_auth(int sock, void *eloop_ctx, if (client == NULL) { RADIUS_DEBUG("Unknown client %s - packet ignored", abuf); + data->counters.invalid_requests++; goto fail; } msg = radius_msg_parse(buf, len); if (msg == NULL) { RADIUS_DEBUG("Parsing incoming RADIUS frame failed"); + data->counters.malformed_access_requests++; + client->counters.malformed_access_requests++; goto fail; } @@ -594,17 +711,26 @@ static void radius_server_receive_auth(int sock, void *eloop_ctx, if (msg->hdr->code != RADIUS_CODE_ACCESS_REQUEST) { RADIUS_DEBUG("Unexpected RADIUS code %d", msg->hdr->code); + data->counters.unknown_types++; + client->counters.unknown_types++; goto fail; } + data->counters.access_requests++; + client->counters.access_requests++; + if (radius_msg_verify_msg_auth(msg, (u8 *) client->shared_secret, client->shared_secret_len, NULL)) { RADIUS_DEBUG("Invalid Message-Authenticator from %s", abuf); + data->counters.bad_authenticators++; + client->counters.bad_authenticators++; goto fail; } - radius_server_request(data, msg, (struct sockaddr *) &from, fromlen, - client, abuf, from_port); + if (radius_server_request(data, msg, (struct sockaddr *) &from, + fromlen, client, abuf, from_port, NULL) == + -2) + return; /* msg was stored with the session */ fail: if (msg) { @@ -796,12 +922,11 @@ radius_server_read_clients(const char *client_file, int ipv6) break; } - entry = malloc(sizeof(*entry)); + entry = wpa_zalloc(sizeof(*entry)); if (entry == NULL) { failed = 1; break; } - memset(entry, 0, sizeof(*entry)); entry->shared_secret = strdup(pos); if (entry->shared_secret == NULL) { failed = 1; @@ -864,11 +989,11 @@ radius_server_init(struct radius_server_conf *conf) } #endif /* CONFIG_IPV6 */ - data = malloc(sizeof(*data)); - if (data == NULL) { + data = wpa_zalloc(sizeof(*data)); + if (data == NULL) return NULL; - } - memset(data, 0, sizeof(*data)); + + os_get_time(&data->start_time); data->hostapd_conf = conf->hostapd_conf; data->eap_sim_db_priv = conf->eap_sim_db_priv; data->ssl_ctx = conf->ssl_ctx; @@ -924,8 +1049,113 @@ void radius_server_deinit(struct radius_server_data *data) int radius_server_get_mib(struct radius_server_data *data, char *buf, size_t buflen) { - /* TODO: add support for RADIUS authentication server MIB */ - return 0; + int ret, uptime; + unsigned int idx; + char *end, *pos; + struct os_time now; + struct radius_client *cli; + + /* RFC 2619 - RADIUS Authentication Server MIB */ + + if (data == NULL || buflen == 0) + return 0; + + pos = buf; + end = buf + buflen; + + os_get_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); + 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); + if (ret < 0 || ret >= end - pos) { + *pos = '\0'; + return pos - buf; + } + pos += ret; + + for (cli = data->clients, idx = 0; cli; cli = cli->next, idx++) { + char abuf[50], mbuf[50]; +#ifdef CONFIG_IPV6 + if (data->ipv6) { + if (inet_ntop(AF_INET6, &cli->addr6, abuf, + sizeof(abuf)) == NULL) + abuf[0] = '\0'; + if (inet_ntop(AF_INET6, &cli->mask6, abuf, + sizeof(mbuf)) == NULL) + mbuf[0] = '\0'; + } +#endif /* CONFIG_IPV6 */ + if (!data->ipv6) { + snprintf(abuf, sizeof(abuf), "%s", + inet_ntoa(cli->addr)); + snprintf(mbuf, sizeof(mbuf), "%s", + inet_ntoa(cli->mask)); + } + + 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); + if (ret < 0 || ret >= end - pos) { + *pos = '\0'; + return pos - buf; + } + pos += ret; + } + + return pos - buf; } @@ -1039,6 +1269,7 @@ static int radius_server_get_eap_user(void *ctx, const u8 *identity, { 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); @@ -1046,9 +1277,13 @@ static int radius_server_get_eap_user(void *ctx, const u8 *identity, return -1; memset(user, 0, sizeof(*user)); - memcpy(user->methods, eap_user->methods, - EAP_USER_MAX_METHODS > EAP_MAX_METHODS ? - EAP_USER_MAX_METHODS : EAP_MAX_METHODS); + 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); @@ -1057,6 +1292,7 @@ static int radius_server_get_eap_user(void *ctx, const u8 *identity, 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; @@ -1072,3 +1308,45 @@ static struct eapol_callbacks radius_server_eapol_cb = .set_eapKeyData = radius_server_set_eapKeyData, .get_eap_user = radius_server_get_eap_user, }; + + +void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx) +{ + struct radius_client *cli; + struct radius_session *s, *sess = NULL; + struct radius_msg *msg; + + if (data == NULL) + return; + + for (cli = data->clients; cli; cli = cli->next) { + for (s = cli->sessions; s; s = s->next) { + if (s->eap == ctx && s->last_msg) { + sess = s; + break; + } + if (sess) + break; + } + if (sess) + break; + } + + if (sess == NULL) { + RADIUS_DEBUG("No session matched callback ctx"); + return; + } + + msg = sess->last_msg; + sess->last_msg = NULL; + eap_sm_pending_cb(sess->eap); + if (radius_server_request(data, msg, + (struct sockaddr *) &sess->last_from, + sess->last_fromlen, cli, + sess->last_from_addr, + sess->last_from_port, sess) == -2) + return; /* msg was stored with the session */ + + radius_msg_free(msg); + free(msg); +} diff --git a/contrib/hostapd/radius_server.h b/contrib/hostapd/radius_server.h index 5c4c39c..9c9315e 100644 --- a/contrib/hostapd/radius_server.h +++ b/contrib/hostapd/radius_server.h @@ -1,3 +1,17 @@ +/* + * hostapd / RADIUS authentication server + * 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 RADIUS_SERVER_H #define RADIUS_SERVER_H @@ -23,6 +37,8 @@ void radius_server_deinit(struct radius_server_data *data); int radius_server_get_mib(struct radius_server_data *data, char *buf, size_t buflen); +void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx); + #else /* RADIUS_SERVER */ static inline struct radius_server_data * @@ -41,6 +57,11 @@ static inline int radius_server_get_mib(struct radius_server_data *data, return 0; } +static inline void +radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx) +{ +} + #endif /* RADIUS_SERVER */ #endif /* RADIUS_SERVER_H */ diff --git a/contrib/hostapd/rc4.c b/contrib/hostapd/rc4.c index 4cf14d9..8480cc5 100644 --- a/contrib/hostapd/rc4.c +++ b/contrib/hostapd/rc4.c @@ -1,6 +1,6 @@ /* * RC4 stream cipher - * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * Copyright (c) 2002-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 @@ -12,7 +12,8 @@ * See README and COPYING for more details. */ -#include <stdio.h> +#include "includes.h" + #include "common.h" #include "rc4.h" @@ -35,7 +36,7 @@ void rc4_skip(const u8 *key, size_t keylen, size_t skip, { u32 i, j, k; u8 S[256], *pos; - int kpos; + size_t kpos; /* Setup RC4 state */ for (i = 0; i < 256; i++) diff --git a/contrib/hostapd/rc4.h b/contrib/hostapd/rc4.h index 3873240..01f1383 100644 --- a/contrib/hostapd/rc4.h +++ b/contrib/hostapd/rc4.h @@ -1,6 +1,6 @@ /* * RC4 stream cipher - * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * Copyright (c) 2002-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 diff --git a/contrib/hostapd/reconfig.c b/contrib/hostapd/reconfig.c new file mode 100644 index 0000000..a0d6156 --- /dev/null +++ b/contrib/hostapd/reconfig.c @@ -0,0 +1,714 @@ +/* + * 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/sha1.c b/contrib/hostapd/sha1.c index 7e32e31..194db16 100644 --- a/contrib/hostapd/sha1.c +++ b/contrib/hostapd/sha1.c @@ -1,6 +1,6 @@ /* * SHA1 hash implementation and interface functions - * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * 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 @@ -12,9 +12,7 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> +#include "includes.h" #include "common.h" #include "sha1.h" @@ -36,9 +34,8 @@ void hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, { unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */ unsigned char tk[20]; - int i; const u8 *_addr[6]; - size_t _len[6]; + size_t _len[6], i; if (num_elem > 5) { /* @@ -65,8 +62,8 @@ void hmac_sha1_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; @@ -80,8 +77,8 @@ void hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, } sha1_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; @@ -129,7 +126,7 @@ void sha1_prf(const u8 *key, size_t key_len, const char *label, u8 zero = 0, counter = 0; size_t pos, plen; u8 hash[SHA1_MAC_LEN]; - size_t label_len = strlen(label); + size_t label_len = os_strlen(label); const unsigned char *addr[4]; size_t len[4]; @@ -152,7 +149,7 @@ void sha1_prf(const u8 *key, size_t key_len, const char *label, } else { hmac_sha1_vector(key, key_len, 4, addr, len, hash); - memcpy(&buf[pos], hash, plen); + os_memcpy(&buf[pos], hash, plen); break; } counter++; @@ -180,7 +177,7 @@ void sha1_t_prf(const u8 *key, size_t key_len, const char *label, unsigned char counter = 0; size_t pos, plen; u8 hash[SHA1_MAC_LEN]; - size_t label_len = strlen(label); + size_t label_len = os_strlen(label); u8 output_len[2]; const unsigned char *addr[5]; size_t len[5]; @@ -204,10 +201,10 @@ void sha1_t_prf(const u8 *key, size_t key_len, const char *label, plen = buf_len - pos; hmac_sha1_vector(key, key_len, 5, addr, len, hash); if (plen >= SHA1_MAC_LEN) { - memcpy(&buf[pos], hash, SHA1_MAC_LEN); + os_memcpy(&buf[pos], hash, SHA1_MAC_LEN); pos += SHA1_MAC_LEN; } else { - memcpy(&buf[pos], hash, plen); + os_memcpy(&buf[pos], hash, plen); break; } len[0] = SHA1_MAC_LEN; @@ -224,18 +221,19 @@ void sha1_t_prf(const u8 *key, size_t key_len, const char *label, * @seed_len: Length of the seed * @out: Buffer for the generated pseudo-random key * @outlen: Number of bytes of key to generate -* + * Returns: 0 on success, -1 on failure. + * * This function is used to derive new, cryptographically separate keys from a * given key in TLS. This PRF is defined in RFC 2246, Chapter 5. */ int tls_prf(const u8 *secret, size_t secret_len, const char *label, const u8 *seed, size_t seed_len, u8 *out, size_t outlen) { - size_t L_S1, L_S2; + size_t L_S1, L_S2, i; const u8 *S1, *S2; u8 A_MD5[MD5_MAC_LEN], A_SHA1[SHA1_MAC_LEN]; u8 P_MD5[MD5_MAC_LEN], P_SHA1[SHA1_MAC_LEN]; - int i, MD5_pos, SHA1_pos; + int MD5_pos, SHA1_pos; const u8 *MD5_addr[3]; size_t MD5_len[3]; const unsigned char *SHA1_addr[3]; @@ -247,14 +245,14 @@ int tls_prf(const u8 *secret, size_t secret_len, const char *label, MD5_addr[0] = A_MD5; MD5_len[0] = MD5_MAC_LEN; MD5_addr[1] = (unsigned char *) label; - MD5_len[1] = strlen(label); + MD5_len[1] = os_strlen(label); MD5_addr[2] = seed; MD5_len[2] = seed_len; SHA1_addr[0] = A_SHA1; SHA1_len[0] = SHA1_MAC_LEN; SHA1_addr[1] = (unsigned char *) label; - SHA1_len[1] = strlen(label); + SHA1_len[1] = os_strlen(label); SHA1_addr[2] = seed; SHA1_len[2] = seed_len; @@ -297,7 +295,7 @@ int tls_prf(const u8 *secret, size_t secret_len, const char *label, static void pbkdf2_sha1_f(const char *passphrase, const char *ssid, - size_t ssid_len, int iterations, int count, + size_t ssid_len, int iterations, unsigned int count, u8 *digest) { unsigned char tmp[SHA1_MAC_LEN], tmp2[SHA1_MAC_LEN]; @@ -305,7 +303,7 @@ static void pbkdf2_sha1_f(const char *passphrase, const char *ssid, unsigned char count_buf[4]; const u8 *addr[2]; size_t len[2]; - size_t passphrase_len = strlen(passphrase); + size_t passphrase_len = os_strlen(passphrase); addr[0] = (u8 *) ssid; len[0] = ssid_len; @@ -323,12 +321,12 @@ static void pbkdf2_sha1_f(const char *passphrase, const char *ssid, count_buf[2] = (count >> 8) & 0xff; count_buf[3] = count & 0xff; hmac_sha1_vector((u8 *) passphrase, passphrase_len, 2, addr, len, tmp); - memcpy(digest, tmp, SHA1_MAC_LEN); + os_memcpy(digest, tmp, SHA1_MAC_LEN); for (i = 1; i < iterations; i++) { hmac_sha1((u8 *) passphrase, passphrase_len, tmp, SHA1_MAC_LEN, tmp2); - memcpy(tmp, tmp2, SHA1_MAC_LEN); + os_memcpy(tmp, tmp2, SHA1_MAC_LEN); for (j = 0; j < SHA1_MAC_LEN; j++) digest[j] ^= tmp2[j]; } @@ -351,7 +349,7 @@ static void pbkdf2_sha1_f(const char *passphrase, const char *ssid, void pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len, int iterations, u8 *buf, size_t buflen) { - int count = 0; + unsigned int count = 0; unsigned char *pos = buf; size_t left = buflen, plen; unsigned char digest[SHA1_MAC_LEN]; @@ -361,24 +359,28 @@ void pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len, pbkdf2_sha1_f(passphrase, ssid, ssid_len, iterations, count, digest); plen = left > SHA1_MAC_LEN ? SHA1_MAC_LEN : left; - memcpy(pos, digest, plen); + os_memcpy(pos, digest, plen); pos += plen; left -= plen; } } -#ifndef EAP_TLS_FUNCS +#ifdef INTERNAL_SHA1 -typedef struct { +struct SHA1Context { u32 state[5]; u32 count[2]; unsigned char buffer[64]; -} SHA1_CTX; +}; + +typedef struct SHA1Context SHA1_CTX; -static void SHA1Init(SHA1_CTX *context); -static void SHA1Update(SHA1_CTX *context, const void *data, u32 len); -static void SHA1Final(unsigned char digest[20], SHA1_CTX* context); +#ifndef CONFIG_CRYPTO_INTERNAL +static void SHA1Init(struct SHA1Context *context); +static void SHA1Update(struct SHA1Context *context, const void *data, u32 len); +static void SHA1Final(unsigned char digest[20], struct SHA1Context *context); +#endif /* CONFIG_CRYPTO_INTERNAL */ static void SHA1Transform(u32 state[5], const unsigned char buffer[64]); @@ -393,7 +395,7 @@ void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { SHA1_CTX ctx; - int i; + size_t i; SHA1Init(&ctx); for (i = 0; i < num_elem; i++) @@ -402,19 +404,57 @@ void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, } -/** - * sha1_transform - Perform one SHA-1 transform step - * @state: SHA-1 state - * @data: Input data for the SHA-1 transform - * - * This function is used to implement random number generation specified in - * NIST FIPS Publication 186-2 for EAP-SIM. This PRF uses a function that is - * similar to SHA-1, but has different message padding and as such, access to - * just part of the SHA-1 is needed. - */ -void sha1_transform(u8 *state, const u8 data[64]) +int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) { - SHA1Transform((u32 *) state, data); + u8 xkey[64]; + u32 t[5], _t[5]; + int i, j, m, k; + u8 *xpos = x; + u32 carry; + + if (seed_len > sizeof(xkey)) + seed_len = sizeof(xkey); + + /* FIPS 186-2 + change notice 1 */ + + os_memcpy(xkey, seed, seed_len); + os_memset(xkey + seed_len, 0, 64 - seed_len); + t[0] = 0x67452301; + t[1] = 0xEFCDAB89; + t[2] = 0x98BADCFE; + t[3] = 0x10325476; + t[4] = 0xC3D2E1F0; + + m = xlen / 40; + for (j = 0; j < m; j++) { + /* XSEED_j = 0 */ + for (i = 0; i < 2; i++) { + /* XVAL = (XKEY + XSEED_j) mod 2^b */ + + /* w_i = G(t, XVAL) */ + os_memcpy(_t, t, 20); + SHA1Transform(_t, xkey); + _t[0] = host_to_be32(_t[0]); + _t[1] = host_to_be32(_t[1]); + _t[2] = host_to_be32(_t[2]); + _t[3] = host_to_be32(_t[3]); + _t[4] = host_to_be32(_t[4]); + os_memcpy(xpos, _t, 20); + + /* XKEY = (1 + XKEY + w_i) mod 2^b */ + carry = 1; + for (k = 19; k >= 0; k--) { + carry += xkey[k] + xpos[k]; + xkey[k] = carry & 0xff; + carry >>= 8; + } + + xpos += SHA1_MAC_LEN; + } + /* x_j = w_0|w_1 */ + } + + return 0; } @@ -479,11 +519,11 @@ Modified to run on Compaq Alpha hardware. ----------------- Modified 4/01 -By Jouni Malinen <jkmaline@cc.hut.fi> +By Jouni Malinen <j@w1.fi> Minor changes to match the coding style used in Dynamics. Modified September 24, 2004 -By Jouni Malinen <jkmaline@cc.hut.fi> +By Jouni Malinen <j@w1.fi> Fixed alignment issue in SHA1Transform when SHA1HANDSOFF is defined. */ @@ -557,7 +597,7 @@ static void SHA1Transform(u32 state[5], const unsigned char buffer[64]) #ifdef SHA1HANDSOFF u32 workspace[16]; block = (CHAR64LONG16 *) workspace; - memcpy(block, buffer, 64); + os_memcpy(block, buffer, 64); #else block = (CHAR64LONG16 *) buffer; #endif @@ -597,14 +637,14 @@ static void SHA1Transform(u32 state[5], const unsigned char buffer[64]) /* Wipe variables */ a = b = c = d = e = 0; #ifdef SHA1HANDSOFF - memset(block, 0, 64); + os_memset(block, 0, 64); #endif } /* SHA1Init - Initialize new context */ -static void SHA1Init(SHA1_CTX* context) +void SHA1Init(SHA1_CTX* context) { /* SHA1 initialization constants */ context->state[0] = 0x67452301; @@ -618,7 +658,7 @@ static void SHA1Init(SHA1_CTX* context) /* Run your data through this. */ -static void SHA1Update(SHA1_CTX* context, const void *_data, u32 len) +void SHA1Update(SHA1_CTX* context, const void *_data, u32 len) { u32 i, j; const unsigned char *data = _data; @@ -631,7 +671,7 @@ static void SHA1Update(SHA1_CTX* context, const void *_data, u32 len) context->count[1]++; context->count[1] += (len >> 29); if ((j + len) > 63) { - memcpy(&context->buffer[j], data, (i = 64-j)); + os_memcpy(&context->buffer[j], data, (i = 64-j)); SHA1Transform(context->state, context->buffer); for ( ; i + 63 < len; i += 64) { SHA1Transform(context->state, &data[i]); @@ -639,7 +679,7 @@ static void SHA1Update(SHA1_CTX* context, const void *_data, u32 len) j = 0; } else i = 0; - memcpy(&context->buffer[j], &data[i], len - i); + os_memcpy(&context->buffer[j], &data[i], len - i); #ifdef VERBOSE SHAPrintContext(context, "after "); #endif @@ -648,7 +688,7 @@ static void SHA1Update(SHA1_CTX* context, const void *_data, u32 len) /* Add padding and return the message digest. */ -static void SHA1Final(unsigned char digest[20], SHA1_CTX* context) +void SHA1Final(unsigned char digest[20], SHA1_CTX* context) { u32 i; unsigned char finalcount[8]; @@ -671,324 +711,12 @@ static void SHA1Final(unsigned char digest[20], SHA1_CTX* context) } /* Wipe variables */ i = 0; - memset(context->buffer, 0, 64); - memset(context->state, 0, 20); - memset(context->count, 0, 8); - memset(finalcount, 0, 8); + os_memset(context->buffer, 0, 64); + os_memset(context->state, 0, 20); + os_memset(context->count, 0, 8); + os_memset(finalcount, 0, 8); } /* ===== end - public domain SHA1 implementation ===== */ -#endif /* EAP_TLS_FUNCS */ - - -#ifdef TEST_MAIN - -#include "md5.c" - -static int test_eap_fast(void) -{ - /* draft-cam-winget-eap-fast-01.txt */ - 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 - }; - 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", - "", 0, buf, sizeof(msk)); - if (memcmp(msk, buf, sizeof(msk)) != 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, 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; -} -#endif /* TEST_MAIN */ +#endif /* INTERNAL_SHA1 */ diff --git a/contrib/hostapd/sha1.h b/contrib/hostapd/sha1.h index 3c6d915..97affa1 100644 --- a/contrib/hostapd/sha1.h +++ b/contrib/hostapd/sha1.h @@ -1,6 +1,6 @@ /* * SHA1 hash implementation and interface functions - * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * 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 @@ -30,4 +30,12 @@ int tls_prf(const u8 *secret, size_t secret_len, const char *label, void pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len, int iterations, u8 *buf, size_t buflen); +#ifdef CONFIG_CRYPTO_INTERNAL +struct SHA1Context; + +void SHA1Init(struct SHA1Context *context); +void SHA1Update(struct SHA1Context *context, const void *data, u32 len); +void SHA1Final(unsigned char digest[20], struct SHA1Context *context); +#endif /* CONFIG_CRYPTO_INTERNAL */ + #endif /* SHA1_H */ diff --git a/contrib/hostapd/sha256.c b/contrib/hostapd/sha256.c new file mode 100644 index 0000000..175ec8b --- /dev/null +++ b/contrib/hostapd/sha256.c @@ -0,0 +1,379 @@ +/* + * SHA-256 hash implementation and interface functions + * 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 "sha256.h" +#include "crypto.h" + + +/** + * hmac_sha256_vector - HMAC-SHA256 over data vector (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash (32 bytes) + */ +void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */ + unsigned char tk[32]; + const u8 *_addr[6]; + size_t _len[6], i; + + if (num_elem > 5) { + /* + * Fixed limit on the number of fragments to avoid having to + * allocate memory (which could fail). + */ + return; + } + + /* if key is longer than 64 bytes reset it to key = SHA256(key) */ + if (key_len > 64) { + sha256_vector(1, &key, &key_len, tk); + key = tk; + key_len = 32; + } + + /* the HMAC_SHA256 transform looks like: + * + * SHA256(K XOR opad, SHA256(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected */ + + /* start out by storing key in ipad */ + memset(k_pad, 0, sizeof(k_pad)); + memcpy(k_pad, key, key_len); + /* XOR key with ipad values */ + for (i = 0; i < 64; i++) + k_pad[i] ^= 0x36; + + /* perform inner SHA256 */ + _addr[0] = k_pad; + _len[0] = 64; + for (i = 0; i < num_elem; i++) { + _addr[i + 1] = addr[i]; + _len[i + 1] = len[i]; + } + sha256_vector(1 + num_elem, _addr, _len, mac); + + memset(k_pad, 0, sizeof(k_pad)); + memcpy(k_pad, key, key_len); + /* XOR key with opad values */ + for (i = 0; i < 64; i++) + k_pad[i] ^= 0x5c; + + /* perform outer SHA256 */ + _addr[0] = k_pad; + _len[0] = 64; + _addr[1] = mac; + _len[1] = SHA256_MAC_LEN; + sha256_vector(2, _addr, _len, mac); +} + + +/** + * hmac_sha256 - HMAC-SHA256 over data buffer (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @data: Pointers to the data area + * @data_len: Length of the data area + * @mac: Buffer for the hash (20 bytes) + */ +void hmac_sha256(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + hmac_sha256_vector(key, key_len, 1, &data, &data_len, mac); +} + + +/** + * sha256_prf - SHA256-based Pseudo-Random Function (IEEE 802.11r, 8.5A.3) + * @key: Key for PRF + * @key_len: Length of the key in bytes + * @label: A unique label for each purpose of the PRF + * @data: Extra data to bind into the key + * @data_len: Length of the data + * @buf: Buffer for the generated pseudo-random key + * @buf_len: Number of bytes of key to generate + * + * This function is used to derive new, cryptographically separate keys from a + * given key. + */ +void sha256_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len) +{ + u16 counter = 0; + size_t pos, plen; + u8 hash[SHA256_MAC_LEN]; + const u8 *addr[3]; + size_t len[3]; + u8 counter_le[2]; + + addr[0] = counter_le; + len[0] = 2; + addr[1] = (u8 *) label; + len[1] = strlen(label) + 1; + addr[2] = data; + len[2] = data_len; + + 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, + &buf[pos]); + pos += SHA256_MAC_LEN; + } else { + hmac_sha256_vector(key, key_len, 3, addr, len, hash); + memcpy(&buf[pos], hash, plen); + break; + } + counter++; + } +} + + +#ifdef INTERNAL_SHA256 + +struct sha256_state { + u64 length; + u32 state[8], curlen; + u8 buf[64]; +}; + +static void sha256_init(struct sha256_state *md); +static int sha256_process(struct sha256_state *md, const unsigned char *in, + unsigned long inlen); +static int sha256_done(struct sha256_state *md, unsigned char *out); + + +/** + * sha256_vector - SHA256 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + */ +void sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + struct sha256_state ctx; + size_t i; + + sha256_init(&ctx); + for (i = 0; i < num_elem; i++) + sha256_process(&ctx, addr[i], len[i]); + sha256_done(&ctx, mac); +} + + +/* ===== start - public domain SHA256 implementation ===== */ + +/* This is based on SHA256 implementation in LibTomCrypt that was released into + * public domain by Tom St Denis. */ + +/* the K array */ +static const unsigned long K[64] = { + 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL, + 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL, + 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, + 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, + 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL, + 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL, + 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, + 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, + 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL, + 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL, + 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, + 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, + 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL +}; + + +/* Various logical functions */ +#define RORc(x, y) \ +( ((((unsigned long) (x) & 0xFFFFFFFFUL) >> (unsigned long) ((y) & 31)) | \ + ((unsigned long) (x) << (unsigned long) (32 - ((y) & 31)))) & 0xFFFFFFFFUL) +#define Ch(x,y,z) (z ^ (x & (y ^ z))) +#define Maj(x,y,z) (((x | y) & z) | (x & y)) +#define S(x, n) RORc((x), (n)) +#define R(x, n) (((x)&0xFFFFFFFFUL)>>(n)) +#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22)) +#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25)) +#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3)) +#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10)) +#ifndef MIN +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#endif + +/* compress 512-bits */ +static int sha256_compress(struct sha256_state *md, unsigned char *buf) +{ + u32 S[8], W[64], t0, t1; + u32 t; + int i; + + /* copy state into S */ + for (i = 0; i < 8; i++) { + S[i] = md->state[i]; + } + + /* copy the state into 512-bits into W[0..15] */ + for (i = 0; i < 16; i++) + W[i] = WPA_GET_BE32(buf + (4 * i)); + + /* fill W[16..63] */ + for (i = 16; i < 64; i++) { + W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + + W[i - 16]; + } + + /* Compress */ +#define RND(a,b,c,d,e,f,g,h,i) \ + t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ + t1 = Sigma0(a) + Maj(a, b, c); \ + d += t0; \ + h = t0 + t1; + + for (i = 0; i < 64; ++i) { + RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i); + t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4]; + S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t; + } + + /* feedback */ + for (i = 0; i < 8; i++) { + md->state[i] = md->state[i] + S[i]; + } + return 0; +} + + +/* Initialize the hash state */ +static void sha256_init(struct sha256_state *md) +{ + md->curlen = 0; + md->length = 0; + md->state[0] = 0x6A09E667UL; + md->state[1] = 0xBB67AE85UL; + md->state[2] = 0x3C6EF372UL; + md->state[3] = 0xA54FF53AUL; + md->state[4] = 0x510E527FUL; + md->state[5] = 0x9B05688CUL; + md->state[6] = 0x1F83D9ABUL; + md->state[7] = 0x5BE0CD19UL; +} + +/** + Process a block of memory though the hash + @param md The hash state + @param in The data to hash + @param inlen The length of the data (octets) + @return CRYPT_OK if successful +*/ +static int sha256_process(struct sha256_state *md, const unsigned char *in, + unsigned long inlen) +{ + unsigned long n; +#define block_size 64 + + if (md->curlen > sizeof(md->buf)) + return -1; + + while (inlen > 0) { + if (md->curlen == 0 && inlen >= block_size) { + if (sha256_compress(md, (unsigned char *) in) < 0) + return -1; + md->length += block_size * 8; + in += block_size; + inlen -= block_size; + } else { + n = MIN(inlen, (block_size - md->curlen)); + memcpy(md->buf + md->curlen, in, n); + md->curlen += n; + in += n; + inlen -= n; + if (md->curlen == block_size) { + if (sha256_compress(md, md->buf) < 0) + return -1; + md->length += 8 * block_size; + md->curlen = 0; + } + } + } + + return 0; +} + + +/** + Terminate the hash to get the digest + @param md The hash state + @param out [out] The destination of the hash (32 bytes) + @return CRYPT_OK if successful +*/ +static int sha256_done(struct sha256_state *md, unsigned char *out) +{ + int i; + + if (md->curlen >= sizeof(md->buf)) + return -1; + + /* increase the length of the message */ + md->length += md->curlen * 8; + + /* append the '1' bit */ + md->buf[md->curlen++] = (unsigned char) 0x80; + + /* if the length is currently above 56 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (md->curlen > 56) { + while (md->curlen < 64) { + md->buf[md->curlen++] = (unsigned char) 0; + } + sha256_compress(md, md->buf); + md->curlen = 0; + } + + /* pad upto 56 bytes of zeroes */ + while (md->curlen < 56) { + md->buf[md->curlen++] = (unsigned char) 0; + } + + /* store length */ + WPA_PUT_BE64(md->buf + 56, md->length); + sha256_compress(md, md->buf); + + /* copy output */ + for (i = 0; i < 8; i++) + WPA_PUT_BE32(out + (4 * i), md->state[i]); + + return 0; +} + +/* ===== end - public domain SHA256 implementation ===== */ + +#endif /* INTERNAL_SHA256 */ diff --git a/contrib/hostapd/sha256.h b/contrib/hostapd/sha256.h new file mode 100644 index 0000000..dc597f0 --- /dev/null +++ b/contrib/hostapd/sha256.h @@ -0,0 +1,27 @@ +/* + * SHA256 hash implementation and interface functions + * 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. + */ + +#ifndef SHA256_H +#define SHA256_H + +#define SHA256_MAC_LEN 32 + +void hmac_sha256_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac); +void hmac_sha256(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac); +void sha256_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len); + +#endif /* SHA256_H */ diff --git a/contrib/hostapd/sta_info.c b/contrib/hostapd/sta_info.c index 9eb0bca..dbb7f6c 100644 --- a/contrib/hostapd/sta_info.c +++ b/contrib/hostapd/sta_info.c @@ -1,7 +1,6 @@ /* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / Station table - * Copyright (c) 2002-2004, Jouni Malinen <jkmaline@cc.hut.fi> + * hostapd / Station table + * 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 @@ -13,12 +12,7 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <netinet/in.h> -#include <sys/types.h> -#include <sys/socket.h> +#include "includes.h" #include "hostapd.h" #include "sta_info.h" @@ -29,10 +23,17 @@ #include "radius.h" #include "eapol_sm.h" #include "wpa.h" +#include "preauth.h" #include "radius_client.h" #include "driver.h" -#include "hostap_common.h" +#include "beacon.h" +#include "hw_features.h" +#include "mlme.h" +#include "vlan_init.h" +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); int ap_for_each_sta(struct hostapd_data *hapd, int (*cb)(struct hostapd_data *hapd, struct sta_info *sta, @@ -61,7 +62,7 @@ struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta) } -static void ap_sta_list_del(hostapd *hapd, struct sta_info *sta) +static void ap_sta_list_del(struct hostapd_data *hapd, struct sta_info *sta) { struct sta_info *tmp; @@ -81,14 +82,14 @@ static void ap_sta_list_del(hostapd *hapd, struct sta_info *sta) } -void ap_sta_hash_add(hostapd *hapd, struct sta_info *sta) +void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta) { sta->hnext = hapd->sta_hash[STA_HASH(sta->addr)]; hapd->sta_hash[STA_HASH(sta->addr)] = sta; } -static void ap_sta_hash_del(hostapd *hapd, struct sta_info *sta) +static void ap_sta_hash_del(struct hostapd_data *hapd, struct sta_info *sta) { struct sta_info *s; @@ -109,10 +110,14 @@ static void ap_sta_hash_del(hostapd *hapd, struct sta_info *sta) } -void ap_free_sta(hostapd *hapd, struct sta_info *sta) +void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) { + int set_beacon = 0; + accounting_sta_stop(hapd, sta); - if (!(sta->flags & WLAN_STA_PREAUTH)) + + if (!ap_sta_in_other_bss(hapd, sta, WLAN_STA_ASSOC) && + !(sta->flags & WLAN_STA_PREAUTH)) hostapd_sta_remove(hapd, sta->addr); ap_sta_hash_del(hapd, sta); @@ -122,23 +127,50 @@ void ap_free_sta(hostapd *hapd, struct sta_info *sta) hapd->sta_aid[sta->aid - 1] = NULL; hapd->num_sta--; + if (sta->nonerp_set) { + sta->nonerp_set = 0; + hapd->iface->num_sta_non_erp--; + if (hapd->iface->num_sta_non_erp == 0) + set_beacon++; + } + + if (sta->no_short_slot_time_set) { + sta->no_short_slot_time_set = 0; + hapd->iface->num_sta_no_short_slot_time--; + if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G + && hapd->iface->num_sta_no_short_slot_time == 0) + set_beacon++; + } + + if (sta->no_short_preamble_set) { + sta->no_short_preamble_set = 0; + hapd->iface->num_sta_no_short_preamble--; + if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G + && hapd->iface->num_sta_no_short_preamble == 0) + set_beacon++; + } + + if (set_beacon) + ieee802_11_set_beacons(hapd->iface); + eloop_cancel_timeout(ap_handle_timer, hapd, sta); + eloop_cancel_timeout(ap_handle_session_timer, hapd, sta); ieee802_1x_free_station(sta); - wpa_free_station(sta); + wpa_auth_sta_deinit(sta->wpa_sm); + rsn_preauth_free_station(hapd, sta); radius_client_flush_auth(hapd->radius, sta->addr); if (sta->last_assoc_req) free(sta->last_assoc_req); free(sta->challenge); - free(sta->wpa_ie); free(sta); } -void hostapd_free_stas(hostapd *hapd) +void hostapd_free_stas(struct hostapd_data *hapd) { struct sta_info *sta, *prev; @@ -146,6 +178,10 @@ void hostapd_free_stas(hostapd *hapd) while (sta) { prev = sta; + if (sta->flags & WLAN_STA_AUTH) { + mlme_deauthenticate_indication( + hapd, sta, WLAN_REASON_UNSPECIFIED); + } sta = sta->next; printf("Removing station " MACSTR "\n", MAC2STR(prev->addr)); ap_free_sta(hapd, prev); @@ -155,7 +191,7 @@ void hostapd_free_stas(hostapd *hapd) void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) { - hostapd *hapd = eloop_ctx; + struct hostapd_data *hapd = eloop_ctx; struct sta_info *sta = timeout_ctx; unsigned long next_time = 0; @@ -179,13 +215,14 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) printf(" Could not get station info from kernel " "driver for " MACSTR ".\n", MAC2STR(sta->addr)); - } else if (inactive_sec < AP_MAX_INACTIVITY && + } 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"); sta->timeout_next = STA_NULLFUNC; - next_time = AP_MAX_INACTIVITY - inactive_sec; + next_time = hapd->conf->ap_max_inactivity - + inactive_sec; } } @@ -197,7 +234,7 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) /* data nullfunc frame poll did not produce TX errors; assume * station ACKed it */ sta->timeout_next = STA_NULLFUNC; - next_time = AP_MAX_INACTIVITY; + next_time = hapd->conf->ap_max_inactivity; } if (next_time) { @@ -216,6 +253,7 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) " Polling STA with data frame\n"); 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 */ @@ -230,6 +268,7 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) if (hostapd_send_mgmt_frame(hapd, &hdr, sizeof(hdr), 0) < 0) perror("ap_handle_timer: send"); +#endif /* CONFIG_NATIVE_WINDOWS */ } else if (sta->timeout_next != STA_REMOVE) { int deauth = sta->timeout_next == STA_DEAUTH; @@ -255,7 +294,7 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) break; case STA_DISASSOC: sta->flags &= ~WLAN_STA_ASSOC; - ieee802_1x_set_port_enabled(hapd, sta, 0); + ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); if (!sta->acct_terminate_cause) sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT; @@ -267,6 +306,8 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) sta->timeout_next = STA_DEAUTH; eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer, hapd, sta); + mlme_disassociate_indication( + hapd, sta, WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); break; case STA_DEAUTH: case STA_REMOVE: @@ -276,31 +317,38 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) if (!sta->acct_terminate_cause) sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT; + mlme_deauthenticate_indication( + hapd, sta, + WLAN_REASON_PREV_AUTH_NOT_VALID); ap_free_sta(hapd, sta); break; } } -void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx) +static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx) { - hostapd *hapd = eloop_ctx; + struct hostapd_data *hapd = eloop_ctx; struct sta_info *sta = timeout_ctx; + u8 addr[ETH_ALEN]; if (!(sta->flags & WLAN_STA_AUTH)) return; - hostapd_sta_deauth(hapd, sta->addr, WLAN_REASON_PREV_AUTH_NOT_VALID); + mlme_deauthenticate_indication(hapd, sta, + WLAN_REASON_PREV_AUTH_NOT_VALID); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "deauthenticated due to " "session timeout"); sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT; + memcpy(addr, sta->addr, ETH_ALEN); ap_free_sta(hapd, sta); + hostapd_sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID); } -void ap_sta_session_timeout(hostapd *hapd, struct sta_info *sta, +void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta, u32 session_timeout) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, @@ -312,7 +360,7 @@ void ap_sta_session_timeout(hostapd *hapd, struct sta_info *sta, } -void ap_sta_no_session_timeout(hostapd *hapd, struct sta_info *sta) +void ap_sta_no_session_timeout(struct hostapd_data *hapd, struct sta_info *sta) { eloop_cancel_timeout(ap_handle_session_timer, hapd, sta); } @@ -327,29 +375,211 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr) return sta; HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, " New STA\n"); - if (hapd->num_sta >= MAX_STA_COUNT) { + 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, MAX_STA_COUNT); + hapd->num_sta, hapd->conf->max_num_sta); return NULL; } - sta = (struct sta_info *) malloc(sizeof(struct sta_info)); + sta = wpa_zalloc(sizeof(struct sta_info)); if (sta == NULL) { printf(" malloc failed\n"); return NULL; } - memset(sta, 0, sizeof(struct sta_info)); sta->acct_interim_interval = hapd->conf->radius->acct_interim_interval; /* initialize STA info data */ - eloop_register_timeout(AP_MAX_INACTIVITY, 0, ap_handle_timer, - hapd, sta); + eloop_register_timeout(hapd->conf->ap_max_inactivity, 0, + ap_handle_timer, hapd, sta); memcpy(sta->addr, addr, ETH_ALEN); sta->next = hapd->sta_list; hapd->sta_list = sta; hapd->num_sta++; ap_sta_hash_add(hapd, sta); + sta->ssid = &hapd->conf->ssid; return sta; } + + +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)); + 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)); + return -1; + } + return 0; +} + + +static int ap_sta_in_other_bss(struct hostapd_data *hapd, + struct sta_info *sta, u32 flags) +{ + struct hostapd_iface *iface = hapd->iface; + size_t i; + + for (i = 0; i < iface->num_bss; i++) { + struct hostapd_data *bss = iface->bss[i]; + struct sta_info *sta2; + /* bss should always be set during operation, but it may be + * NULL during reconfiguration. Assume the STA is not + * associated to another BSS in that case to avoid NULL pointer + * dereferences. */ + if (bss == hapd || bss == NULL) + continue; + sta2 = ap_get_sta(bss, sta->addr); + if (sta2 && ((sta2->flags & flags) == flags)) + return 1; + } + + return 0; +} + + +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)); + sta->flags &= ~WLAN_STA_ASSOC; + if (!ap_sta_in_other_bss(hapd, sta, WLAN_STA_ASSOC)) + ap_sta_remove(hapd, sta); + sta->timeout_next = STA_DEAUTH; + eloop_cancel_timeout(ap_handle_timer, hapd, sta); + eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DISASSOC, 0, + ap_handle_timer, hapd, sta); + accounting_sta_stop(hapd, sta); + ieee802_1x_free_station(sta); + + mlme_disassociate_indication(hapd, sta, reason); +} + + +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)); + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); + if (!ap_sta_in_other_bss(hapd, sta, WLAN_STA_ASSOC)) + ap_sta_remove(hapd, sta); + sta->timeout_next = STA_REMOVE; + eloop_cancel_timeout(ap_handle_timer, hapd, sta); + eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0, + ap_handle_timer, hapd, sta); + accounting_sta_stop(hapd, sta); + ieee802_1x_free_station(sta); + + mlme_deauthenticate_indication(hapd, sta, reason); +} + + +int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, + int old_vlanid) +{ + const char *iface; + struct hostapd_vlan *vlan = NULL; + + /* + * Do not proceed furthur if the vlan id remains same. We do not want + * duplicate dynamic vlan entries. + */ + if (sta->vlan_id == old_vlanid) + return 0; + + /* + * During 1x reauth, if the vlan id changes, then remove the old id and + * proceed furthur to add the new one. + */ + if (old_vlanid > 0) + vlan_remove_dynamic(hapd, old_vlanid); + + iface = hapd->conf->iface; + if (sta->ssid->vlan[0]) + iface = sta->ssid->vlan; + + if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED) + sta->vlan_id = 0; + else if (sta->vlan_id > 0) { + vlan = hapd->conf->vlan; + while (vlan) { + if (vlan->vlan_id == sta->vlan_id || + vlan->vlan_id == VLAN_ID_WILDCARD) { + iface = vlan->ifname; + break; + } + vlan = vlan->next; + } + } + + if (sta->vlan_id > 0 && vlan == NULL) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "could not find VLAN for " + "binding station to (vlan_id=%d)", + sta->vlan_id); + return -1; + } else if (sta->vlan_id > 0 && vlan->vlan_id == VLAN_ID_WILDCARD) { + vlan = vlan_add_dynamic(hapd, vlan, sta->vlan_id); + if (vlan == NULL) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "could not add " + "dynamic VLAN interface for vlan_id=%d", + sta->vlan_id); + return -1; + } + + iface = vlan->ifname; + if (vlan_setup_encryption_dyn(hapd, sta->ssid, iface) != 0) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "could not " + "configure encryption for dynamic VLAN " + "interface for vlan_id=%d", + sta->vlan_id); + } + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "added new dynamic VLAN " + "interface '%s'", iface); + } else if (vlan && vlan->vlan_id == sta->vlan_id) { + if (sta->vlan_id > 0) { + vlan->dynamic_vlan++; + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "updated existing " + "dynamic VLAN interface '%s'", iface); + } + + /* + * Update encryption configuration for statically generated + * VLAN interface. This is only used for static WEP + * configuration for the case where hostapd did not yet know + * which keys are to be used when the interface was added. + */ + if (vlan_setup_encryption_dyn(hapd, sta->ssid, iface) != 0) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "could not " + "configure encryption for VLAN " + "interface for vlan_id=%d", + sta->vlan_id); + } + } + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "binding station to interface " + "'%s'", iface); + + if (wpa_auth_sta_set_vlan(sta->wpa_sm, sta->vlan_id) < 0) + wpa_printf(MSG_INFO, "Failed to update VLAN-ID for WPA"); + + return hostapd_set_sta_vlan(iface, hapd, sta->addr, sta->vlan_id); +} diff --git a/contrib/hostapd/sta_info.h b/contrib/hostapd/sta_info.h index e2d7b4e..1d9ab96 100644 --- a/contrib/hostapd/sta_info.h +++ b/contrib/hostapd/sta_info.h @@ -1,3 +1,17 @@ +/* + * hostapd / Station table + * Copyright (c) 2002-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. + */ + #ifndef STA_INFO_H #define STA_INFO_H @@ -6,14 +20,21 @@ int ap_for_each_sta(struct hostapd_data *hapd, void *ctx), void *ctx); struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta); -void ap_sta_hash_add(hostapd *hapd, struct sta_info *sta); -void ap_free_sta(hostapd *hapd, struct sta_info *sta); -void ap_free_sta(hostapd *hapd, struct sta_info *sta); -void hostapd_free_stas(hostapd *hapd); +void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta); +void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta); +void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta); +void hostapd_free_stas(struct hostapd_data *hapd); void ap_handle_timer(void *eloop_ctx, void *timeout_ctx); -void ap_sta_session_timeout(hostapd *hapd, struct sta_info *sta, +void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta, u32 session_timeout); -void ap_sta_no_session_timeout(hostapd *hapd, struct sta_info *sta); +void ap_sta_no_session_timeout(struct hostapd_data *hapd, + struct sta_info *sta); struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr); +void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta, + u16 reason); +void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta, + u16 reason); +int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, + int old_vlanid); #endif /* STA_INFO_H */ diff --git a/contrib/hostapd/state_machine.h b/contrib/hostapd/state_machine.h new file mode 100644 index 0000000..62766bf --- /dev/null +++ b/contrib/hostapd/state_machine.h @@ -0,0 +1,144 @@ +/* + * wpa_supplicant/hostapd - State machine definitions + * Copyright (c) 2002-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 includes a set of pre-processor macros that can be used to + * implement a state machine. In addition to including this header file, each + * file implementing a state machine must define STATE_MACHINE_DATA to be the + * data structure including state variables (enum <machine>_state, + * Boolean changed), and STATE_MACHINE_DEBUG_PREFIX to be a string that is used + * as a prefix for all debug messages. If SM_ENTRY_MA macro is used to define + * a group of state machines with shared data structure, STATE_MACHINE_ADDR + * needs to be defined to point to the MAC address used in debug output. + * SM_ENTRY_M macro can be used to define similar group of state machines + * without this additional debug info. + */ + +#ifndef STATE_MACHINE_H +#define STATE_MACHINE_H + +/** + * SM_STATE - Declaration of a state machine function + * @machine: State machine name + * @state: State machine state + * + * This macro is used to declare a state machine function. It is used in place + * of a C function definition to declare functions to be run when the state is + * entered by calling SM_ENTER or SM_ENTER_GLOBAL. + */ +#define SM_STATE(machine, state) \ +static void sm_ ## machine ## _ ## state ## _Enter(STATE_MACHINE_DATA *sm, \ + int global) + +/** + * SM_ENTRY - State machine function entry point + * @machine: State machine name + * @state: State machine state + * + * This macro is used inside each state machine function declared with + * SM_STATE. SM_ENTRY should be in the beginning of the function body, but + * after declaration of possible local variables. This macro prints debug + * information about state transition and update the state machine state. + */ +#define SM_ENTRY(machine, state) \ +if (!global || sm->machine ## _state != machine ## _ ## state) { \ + sm->changed = TRUE; \ + wpa_printf(MSG_DEBUG, STATE_MACHINE_DEBUG_PREFIX ": " #machine \ + " entering state " #state); \ +} \ +sm->machine ## _state = machine ## _ ## state; + +/** + * SM_ENTRY_M - State machine function entry point for state machine group + * @machine: State machine name + * @_state: State machine state + * @data: State variable prefix (full variable: <prefix>_state) + * + * This macro is like SM_ENTRY, but for state machine groups that use a shared + * data structure for more than one state machine. Both machine and prefix + * parameters are set to "sub-state machine" name. prefix is used to allow more + * than one state variable to be stored in the same data structure. + */ +#define SM_ENTRY_M(machine, _state, data) \ +if (!global || sm->data ## _ ## state != machine ## _ ## _state) { \ + sm->changed = TRUE; \ + wpa_printf(MSG_DEBUG, STATE_MACHINE_DEBUG_PREFIX ": " \ + #machine " entering state " #_state); \ +} \ +sm->data ## _ ## state = machine ## _ ## _state; + +/** + * SM_ENTRY_MA - State machine function entry point for state machine group + * @machine: State machine name + * @_state: State machine state + * @data: State variable prefix (full variable: <prefix>_state) + * + * This macro is like SM_ENTRY_M, but a MAC address is included in debug + * output. STATE_MACHINE_ADDR has to be defined to point to the MAC address to + * be included in debug. + */ +#define SM_ENTRY_MA(machine, _state, data) \ +if (!global || sm->data ## _ ## state != machine ## _ ## _state) { \ + sm->changed = TRUE; \ + wpa_printf(MSG_DEBUG, STATE_MACHINE_DEBUG_PREFIX ": " MACSTR " " \ + #machine " entering state " #_state, \ + MAC2STR(STATE_MACHINE_ADDR)); \ +} \ +sm->data ## _ ## state = machine ## _ ## _state; + +/** + * SM_ENTER - Enter a new state machine state + * @machine: State machine name + * @state: State machine state + * + * This macro expands to a function call to a state machine function defined + * with SM_STATE macro. SM_ENTER is used in a state machine step function to + * move the state machine to a new state. + */ +#define SM_ENTER(machine, state) \ +sm_ ## machine ## _ ## state ## _Enter(sm, 0) + +/** + * SM_ENTER_GLOBAL - Enter a new state machine state based on global rule + * @machine: State machine name + * @state: State machine state + * + * This macro is like SM_ENTER, but this is used when entering a new state + * based on a global (not specific to any particular state) rule. A separate + * macro is used to avoid unwanted debug message floods when the same global + * rule is forcing a state machine to remain in on state. + */ +#define SM_ENTER_GLOBAL(machine, state) \ +sm_ ## machine ## _ ## state ## _Enter(sm, 1) + +/** + * SM_STEP - Declaration of a state machine step function + * @machine: State machine name + * + * This macro is used to declare a state machine step function. It is used in + * place of a C function definition to declare a function that is used to move + * state machine to a new state based on state variables. This function uses + * SM_ENTER and SM_ENTER_GLOBAL macros to enter new state. + */ +#define SM_STEP(machine) \ +static void sm_ ## machine ## _Step(STATE_MACHINE_DATA *sm) + +/** + * SM_STEP_RUN - Call the state machine step function + * @machine: State machine name + * + * This macro expands to a function call to a state machine step function + * defined with SM_STEP macro. + */ +#define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm) + +#endif /* STATE_MACHINE_H */ diff --git a/contrib/hostapd/tls.h b/contrib/hostapd/tls.h index a6a8110..30d8842 100644 --- a/contrib/hostapd/tls.h +++ b/contrib/hostapd/tls.h @@ -1,6 +1,6 @@ /* * WPA Supplicant / SSL/TLS interface definition - * Copyright (c) 2004-2006, Jouni Malinen <jkmaline@cc.hut.fi> + * 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 @@ -18,21 +18,14 @@ struct tls_connection; struct tls_keys { - const u8 *master_key; + const u8 *master_key; /* TLS master secret */ size_t master_key_len; const u8 *client_random; size_t client_random_len; const u8 *server_random; size_t server_random_len; - - /* - * If TLS library does not provide access to master_key, but only to - * EAP key block, this pointer can be set to point to the result of - * PRF(master_secret, "client EAP encryption", - * client_random + server_random). - */ - const u8 *eap_tls_prf; - size_t eap_tls_prf_len; + const u8 *inner_secret; /* TLS/IA inner secret */ + size_t inner_secret_len; }; struct tls_config { @@ -71,8 +64,10 @@ struct tls_config { * @ppin: pointer to the pin variable in the configuration * (this is OpenSSL specific for now) * @key_id: the private key's key id (this is OpenSSL specific for now) + * @tls_ia: Whether to enable TLS/IA (for EAP-TTLSv1) * - * TLS connection parameters to be configured with tls_connection_set_params(). + * TLS connection parameters to be configured with tls_connection_set_params() + * and tls_global_set_params(). * * Certificates and private key can be configured either as a reference name * (file path or reference to certificate store) or by providing the same data @@ -96,6 +91,7 @@ struct tls_connection_params { const char *dh_file; const u8 *dh_blob; size_t dh_blob_len; + int tls_ia; /* OpenSSL specific variables */ int engine; @@ -134,7 +130,6 @@ void tls_deinit(void *tls_ctx); /** * tls_get_errors - Process pending errors * @tls_ctx: TLS context data from tls_init() - * * Returns: Number of found error, 0 if no errors detected. * * Process all pending TLS errors. @@ -144,7 +139,6 @@ int tls_get_errors(void *tls_ctx); /** * tls_connection_init - Initialize a new TLS connection * @tls_ctx: TLS context data from tls_init() - * * Returns: Connection context data, conn for other function calls */ struct tls_connection * tls_connection_init(void *tls_ctx); @@ -162,16 +156,14 @@ void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn); * tls_connection_established - Has the TLS connection been completed? * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() - * * Returns: 1 if TLS connection has been completed, 0 if not. */ int tls_connection_established(void *tls_ctx, struct tls_connection *conn); /** - * tls_connection_shutdown - Shutdown TLS connection data. + * tls_connection_shutdown - Shutdown TLS connection * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() - * * Returns: 0 on success, -1 on failure * * Shutdown current TLS connection without releasing all resources. New @@ -185,12 +177,12 @@ enum { TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED = -3, TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED = -2 }; + /** * tls_connection_set_params - Set TLS connection parameters * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @params: Connection parameters - * * Returns: 0 on success, -1 on failure, * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on possible PIN error causing * PKCS#11 engine failure, or @@ -201,21 +193,23 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, const struct tls_connection_params *params); /** - * tls_global_ca_cert - Set trusted CA certificate for all TLS connections + * tls_global_set_params - Set TLS parameters for all TLS connection * @tls_ctx: TLS context data from tls_init() - * @ca_cert: File name for CA certificate in PEM or DER format - * %NULL to allow all subjects - * - * Returns: 0 on success, -1 on failure + * @params: Global TLS parameters + * Returns: 0 on success, -1 on failure, + * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on possible PIN error causing + * PKCS#11 engine failure, or + * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the + * PKCS#11 engine private key. */ -int tls_global_ca_cert(void *tls_ctx, const char *ca_cert); +int tls_global_set_params(void *tls_ctx, + const struct tls_connection_params *params); /** * tls_global_set_verify - Set global certificate verification options * @tls_ctx: TLS context data from tls_init() * @check_crl: 0 = do not verify CRLs, 1 = verify CRL for the user certificate, * 2 = verify CRL for all certificates - * * Returns: 0 on success, -1 on failure */ int tls_global_set_verify(void *tls_ctx, int check_crl); @@ -225,55 +219,73 @@ int tls_global_set_verify(void *tls_ctx, int check_crl); * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @verify_peer: 1 = verify peer certificate - * * Returns: 0 on success, -1 on failure */ int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, int verify_peer); /** - * tls_global_client_cert - Set client certificate for all TLS connections + * tls_connection_set_ia - Set TLS/IA parameters * @tls_ctx: TLS context data from tls_init() - * @client_cert: File name for client certificate in PEM or DER format - * + * @conn: Connection context data from tls_connection_init() + * @tls_ia: 1 = enable TLS/IA * Returns: 0 on success, -1 on failure - */ -int tls_global_client_cert(void *tls_ctx, const char *client_cert); - -/** - * tls_global_private_key - Set private key for all TLS connections - * @tls_ctx: TLS context data from tls_init() - * @private_key: File name for client private key in PEM or DER format - * @private_key_passwd: Passphrase for decrypted private key, %NULL if no - * passphrase is used. * - * Returns: 0 on success, -1 on failure + * This function is used to configure TLS/IA in server mode where + * tls_connection_set_params() is not used. */ -int tls_global_private_key(void *tls_ctx, const char *private_key, - const char *private_key_passwd); +int 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 * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @keys: Structure of key/random data (filled on success) - * * Returns: 0 on success, -1 on failure */ int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn, struct tls_keys *keys); /** + * tls_connection_prf - Use TLS-PRF to derive keying material + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @label: Label (e.g., description of the key) for PRF + * @server_random_first: seed is 0 = client_random|server_random, + * 1 = server_random|client_random + * @out: Buffer for output data from TLS-PRF + * @out_len: Length of the output buffer + * Returns: 0 on success, -1 on failure + * + * This function is optional to implement if tls_connection_get_keys() provides + * access to master secret and server/client random values. If these values are + * not exported from the TLS library, tls_connection_prf() is required so that + * further keying material can be derived from the master secret. If not + * implemented, the function will still need to be defined, but it can just + * return -1. Example implementation of this function is in tls_prf() function + * 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); + +/** * tls_connection_handshake - Process TLS handshake (client side) * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @in_data: Input data from TLS peer * @in_len: Input data length * @out_len: Length of the output buffer. - * + * @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 * - * Caller is responsible for freeing returned output data. + * Caller is responsible for freeing returned output data. If the final + * handshake message includes application data, this is decrypted and + * appl_data (if not %NULL) is set to point this data. Caller is responsible + * for freeing appl_data. * * This function is used during TLS handshake. The first call is done with * in_data == %NULL and the library is expected to return ClientHello packet. @@ -291,7 +303,8 @@ int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn, */ u8 * tls_connection_handshake(void *tls_ctx, struct tls_connection *conn, const u8 *in_data, size_t in_len, - size_t *out_len); + size_t *out_len, u8 **appl_data, + size_t *appl_data_len); /** * tls_connection_server_handshake - Process TLS handshake (server side) @@ -300,7 +313,6 @@ u8 * tls_connection_handshake(void *tls_ctx, struct tls_connection *conn, * @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 * * Caller is responsible for freeing returned output data. @@ -318,7 +330,6 @@ u8 * tls_connection_server_handshake(void *tls_ctx, * @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 @@ -336,7 +347,6 @@ int tls_connection_encrypt(void *tls_ctx, struct tls_connection *conn, * @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 @@ -350,7 +360,6 @@ int tls_connection_decrypt(void *tls_ctx, struct tls_connection *conn, * tls_connection_resumed - Was session resumption used * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() - * * Returns: 1 if current session used session resumption, 0 if not */ int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn); @@ -361,23 +370,29 @@ int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn); * @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 */, + TLS_CIPHER_AES128_SHA /* 0x002f */, + TLS_CIPHER_RSA_DHE_AES128_SHA /* 0x0031 */, + TLS_CIPHER_ANON_DH_AES128_SHA /* 0x0034 */ +}; + /** - * tls_connection_set_anon_dh - Configure TLS connection to use anonymous DH + * tls_connection_set_cipher_list - Configure acceptable cipher suites * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() - * + * @ciphers: Zero (TLS_CIPHER_NONE) terminated list of allowed ciphers + * (TLS_CIPHER_*). * Returns: 0 on success, -1 on failure - * - * TODO: consider changing this to more generic routine for configuring allowed - * ciphers */ -int tls_connection_set_anon_dh(void *tls_ctx, struct tls_connection *conn); +int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, + u8 *ciphers); /** * tls_get_cipher - Get current cipher name @@ -385,7 +400,6 @@ int tls_connection_set_anon_dh(void *tls_ctx, struct tls_connection *conn); * @conn: Connection context data from tls_connection_init() * @buf: Buffer for the cipher name * @buflen: buf size - * * Returns: 0 on success, -1 on failure * * Get the name of the currently used cipher. @@ -397,7 +411,6 @@ int tls_get_cipher(void *tls_ctx, struct tls_connection *conn, * tls_connection_enable_workaround - Enable TLS workaround options * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() - * * Returns: 0 on success, -1 on failure * * This function is used to enable connection-specific workaround options for @@ -411,9 +424,8 @@ int tls_connection_enable_workaround(void *tls_ctx, * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @ext_type: Extension type - * @data: Extension payload (NULL to remove extension) + * @data: Extension payload (%NULL to remove extension) * @data_len: Extension payload length - * * Returns: 0 on success, -1 on failure */ int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn, @@ -433,7 +445,6 @@ int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn); * tls_connection_get_read_alerts - Get connection read alert status * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() - * * Returns: Number of times a fatal read (remote end reported error) has * happened during this connection. */ @@ -443,7 +454,6 @@ int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn); * tls_connection_get_write_alerts - Get connection write alert status * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() - * * Returns: Number of times a fatal write (locally detected error) has happened * during this connection. */ @@ -460,4 +470,52 @@ int tls_connection_get_write_alerts(void *tls_ctx, int tls_connection_get_keyblock_size(void *tls_ctx, struct tls_connection *conn); +#define TLS_CAPABILITY_IA 0x0001 /* TLS Inner Application (TLS/IA) */ +/** + * tls_capabilities - Get supported TLS capabilities + * @tls_ctx: TLS context data from tls_init() + * Returns: Bit field of supported TLS capabilities (TLS_CAPABILITY_*) + */ +unsigned int tls_capabilities(void *tls_ctx); + +/** + * tls_connection_ia_send_phase_finished - Send a TLS/IA PhaseFinished message + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @final: 1 = FinalPhaseFinished, 0 = IntermediatePhaseFinished + * @out_data: Pointer to output buffer (encrypted TLS/IA data) + * @out_len: Maximum out_data length + * Returns: Number of bytes written to out_data on success, -1 on failure + * + * 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); + +/** + * tls_connection_ia_final_phase_finished - Has final phase been completed + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * 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); + +/** + * tls_connection_ia_permute_inner_secret - Permute TLS/IA inner secret + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @key: Session key material (session_key vectors with 2-octet length), or + * %NULL if no session key was generating in the current phase + * @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); + #endif /* TLS_H */ diff --git a/contrib/hostapd/tls_gnutls.c b/contrib/hostapd/tls_gnutls.c new file mode 100644 index 0000000..0789398 --- /dev/null +++ b/contrib/hostapd/tls_gnutls.c @@ -0,0 +1,1370 @@ +/* + * WPA Supplicant / SSL/TLS interface functions for openssl + * 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 <gnutls/gnutls.h> +#include <gnutls/x509.h> +#ifdef PKCS12_FUNCS +#include <gnutls/pkcs12.h> +#endif /* PKCS12_FUNCS */ + +#ifdef CONFIG_GNUTLS_EXTRA +#if LIBGNUTLS_VERSION_NUMBER >= 0x010302 +#define GNUTLS_IA +#include <gnutls/extra.h> +#if LIBGNUTLS_VERSION_NUMBER == 0x010302 +/* This function is not included in the current gnutls/extra.h even though it + * should be, so define it here as a workaround for the time being. */ +int gnutls_ia_verify_endphase(gnutls_session_t session, char *checksum); +#endif /* LIBGNUTLS_VERSION_NUMBER == 0x010302 */ +#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */ +#endif /* CONFIG_GNUTLS_EXTRA */ + +#include "common.h" +#include "tls.h" + + +#define TLS_RANDOM_SIZE 32 +#define TLS_MASTER_SIZE 48 + + +#if LIBGNUTLS_VERSION_NUMBER < 0x010302 +/* GnuTLS 1.3.2 added functions for using master secret. Older versions require + * use of internal structures to get the master_secret and + * {server,client}_random. + */ +#define GNUTLS_INTERNAL_STRUCTURE_HACK +#endif /* LIBGNUTLS_VERSION_NUMBER < 0x010302 */ + + +#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK +/* + * It looks like gnutls does not provide access to client/server_random and + * master_key. This is somewhat unfortunate since these are needed for key + * derivation in EAP-{TLS,TTLS,PEAP,FAST}. Workaround for now is a horrible + * hack that copies the gnutls_session_int definition from gnutls_int.h so that + * we can get the needed information. + */ + +typedef u8 uint8; +typedef unsigned char opaque; +typedef struct { + uint8 suite[2]; +} cipher_suite_st; + +typedef struct { + gnutls_connection_end_t entity; + gnutls_kx_algorithm_t kx_algorithm; + gnutls_cipher_algorithm_t read_bulk_cipher_algorithm; + gnutls_mac_algorithm_t read_mac_algorithm; + gnutls_compression_method_t read_compression_algorithm; + gnutls_cipher_algorithm_t write_bulk_cipher_algorithm; + gnutls_mac_algorithm_t write_mac_algorithm; + gnutls_compression_method_t write_compression_algorithm; + cipher_suite_st current_cipher_suite; + opaque master_secret[TLS_MASTER_SIZE]; + opaque client_random[TLS_RANDOM_SIZE]; + opaque server_random[TLS_RANDOM_SIZE]; + /* followed by stuff we are not interested in */ +} security_parameters_st; + +struct gnutls_session_int { + security_parameters_st security_parameters; + /* followed by things we are not interested in */ +}; +#endif /* LIBGNUTLS_VERSION_NUMBER < 0x010302 */ + +static int tls_gnutls_ref_count = 0; + +struct tls_global { + /* Data for session resumption */ + void *session_data; + size_t session_data_size; + + int server; + + int params_set; + gnutls_certificate_credentials_t xcred; +}; + +struct tls_connection { + gnutls_session session; + char *subject_match, *altsubject_match; + int read_alerts, write_alerts, failed; + + u8 *pre_shared_secret; + size_t pre_shared_secret_len; + int established; + int verify_peer; + + u8 *push_buf, *pull_buf, *pull_buf_offset; + size_t push_buf_len, pull_buf_len; + + int params_set; + gnutls_certificate_credentials_t xcred; + + int tls_ia; + int final_phase_finished; + +#ifdef GNUTLS_IA + gnutls_ia_server_credentials_t iacred_srv; + gnutls_ia_client_credentials_t iacred_cli; + + /* Session keys generated in the current phase for inner secret + * permutation before generating/verifying PhaseFinished. */ + u8 *session_keys; + size_t session_keys_len; + + u8 inner_secret[TLS_MASTER_SIZE]; +#endif /* GNUTLS_IA */ +}; + + +static void tls_log_func(int level, const char *msg) +{ + char *s, *pos; + if (level == 6 || level == 7) { + /* These levels seem to be mostly I/O debug and msg dumps */ + return; + } + + s = os_strdup(msg); + if (s == NULL) + return; + + pos = s; + while (*pos != '\0') { + if (*pos == '\n') { + *pos = '\0'; + break; + } + pos++; + } + wpa_printf(level > 3 ? MSG_MSGDUMP : MSG_DEBUG, + "gnutls<%d> %s", level, s); + os_free(s); +} + + +extern int wpa_debug_show_keys; + +void * tls_init(const struct tls_config *conf) +{ + struct tls_global *global; + +#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK + /* Because of the horrible hack to get master_secret and client/server + * random, we need to make sure that the gnutls version is something + * that is expected to have same structure definition for the session + * data.. */ + const char *ver; + const char *ok_ver[] = { "1.2.3", "1.2.4", "1.2.5", "1.2.6", "1.2.9", + "1.3.2", + NULL }; + int i; +#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */ + + global = os_zalloc(sizeof(*global)); + if (global == NULL) + return NULL; + + if (tls_gnutls_ref_count == 0 && gnutls_global_init() < 0) { + os_free(global); + return NULL; + } + tls_gnutls_ref_count++; + +#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK + ver = gnutls_check_version(NULL); + if (ver == NULL) { + tls_deinit(global); + return NULL; + } + wpa_printf(MSG_DEBUG, "%s - gnutls version %s", __func__, ver); + for (i = 0; ok_ver[i]; i++) { + if (strcmp(ok_ver[i], ver) == 0) + break; + } + if (ok_ver[i] == NULL) { + wpa_printf(MSG_INFO, "Untested gnutls version %s - this needs " + "to be tested and enabled in tls_gnutls.c", ver); + tls_deinit(global); + return NULL; + } +#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */ + + gnutls_global_set_log_function(tls_log_func); + if (wpa_debug_show_keys) + gnutls_global_set_log_level(11); + return global; +} + + +void tls_deinit(void *ssl_ctx) +{ + struct tls_global *global = ssl_ctx; + if (global) { + if (global->params_set) + gnutls_certificate_free_credentials(global->xcred); + os_free(global->session_data); + os_free(global); + } + + tls_gnutls_ref_count--; + if (tls_gnutls_ref_count == 0) + gnutls_global_deinit(); +} + + +int tls_get_errors(void *ssl_ctx) +{ + return 0; +} + + +static ssize_t tls_pull_func(gnutls_transport_ptr ptr, void *buf, + size_t len) +{ + struct tls_connection *conn = (struct tls_connection *) ptr; + u8 *end; + if (conn->pull_buf == NULL) { + errno = EWOULDBLOCK; + return -1; + } + + end = conn->pull_buf + conn->pull_buf_len; + if ((size_t) (end - conn->pull_buf_offset) < len) + len = end - conn->pull_buf_offset; + os_memcpy(buf, conn->pull_buf_offset, len); + conn->pull_buf_offset += len; + if (conn->pull_buf_offset == end) { + wpa_printf(MSG_DEBUG, "%s - pull_buf consumed", __func__); + os_free(conn->pull_buf); + conn->pull_buf = conn->pull_buf_offset = NULL; + conn->pull_buf_len = 0; + } else { + wpa_printf(MSG_DEBUG, "%s - %d bytes remaining in pull_buf", + __func__, end - conn->pull_buf_offset); + } + return len; +} + + +static ssize_t tls_push_func(gnutls_transport_ptr ptr, const void *buf, + size_t len) +{ + struct tls_connection *conn = (struct tls_connection *) ptr; + u8 *nbuf; + + nbuf = os_realloc(conn->push_buf, conn->push_buf_len + len); + if (nbuf == NULL) { + errno = ENOMEM; + return -1; + } + os_memcpy(nbuf + conn->push_buf_len, buf, len); + conn->push_buf = nbuf; + conn->push_buf_len += len; + + return len; +} + + +static int tls_gnutls_init_session(struct tls_global *global, + struct tls_connection *conn) +{ + const int cert_types[2] = { GNUTLS_CRT_X509, 0 }; + const int protos[2] = { GNUTLS_TLS1, 0 }; + int ret; + + ret = gnutls_init(&conn->session, + global->server ? GNUTLS_SERVER : GNUTLS_CLIENT); + if (ret < 0) { + wpa_printf(MSG_INFO, "TLS: Failed to initialize new TLS " + "connection: %s", gnutls_strerror(ret)); + return -1; + } + + ret = gnutls_set_default_priority(conn->session); + if (ret < 0) + goto fail; + + ret = gnutls_certificate_type_set_priority(conn->session, cert_types); + if (ret < 0) + goto fail; + + ret = gnutls_protocol_set_priority(conn->session, protos); + if (ret < 0) + goto fail; + + gnutls_transport_set_pull_function(conn->session, tls_pull_func); + gnutls_transport_set_push_function(conn->session, tls_push_func); + gnutls_transport_set_ptr(conn->session, (gnutls_transport_ptr) conn); + + return 0; + +fail: + wpa_printf(MSG_INFO, "TLS: Failed to setup new TLS connection: %s", + gnutls_strerror(ret)); + gnutls_deinit(conn->session); + return -1; +} + + +struct tls_connection * tls_connection_init(void *ssl_ctx) +{ + struct tls_global *global = ssl_ctx; + struct tls_connection *conn; + int ret; + + conn = os_zalloc(sizeof(*conn)); + if (conn == NULL) + return NULL; + + if (tls_gnutls_init_session(global, conn)) { + os_free(conn); + return NULL; + } + + if (global->params_set) { + ret = gnutls_credentials_set(conn->session, + GNUTLS_CRD_CERTIFICATE, + global->xcred); + if (ret < 0) { + wpa_printf(MSG_INFO, "Failed to configure " + "credentials: %s", gnutls_strerror(ret)); + os_free(conn); + return NULL; + } + } + + if (gnutls_certificate_allocate_credentials(&conn->xcred)) { + os_free(conn); + return NULL; + } + + return conn; +} + + +void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return; + +#ifdef GNUTLS_IA + if (conn->iacred_srv) + gnutls_ia_free_server_credentials(conn->iacred_srv); + if (conn->iacred_cli) + gnutls_ia_free_client_credentials(conn->iacred_cli); + if (conn->session_keys) { + os_memset(conn->session_keys, 0, conn->session_keys_len); + os_free(conn->session_keys); + } +#endif /* GNUTLS_IA */ + + gnutls_certificate_free_credentials(conn->xcred); + gnutls_deinit(conn->session); + os_free(conn->pre_shared_secret); + os_free(conn->subject_match); + os_free(conn->altsubject_match); + os_free(conn->push_buf); + os_free(conn->pull_buf); + os_free(conn); +} + + +int tls_connection_established(void *ssl_ctx, struct tls_connection *conn) +{ + return conn ? conn->established : 0; +} + + +int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) +{ + struct tls_global *global = ssl_ctx; + int ret; + + if (conn == NULL) + return -1; + + /* Shutdown previous TLS connection without notifying the peer + * because the connection was already terminated in practice + * and "close notify" shutdown alert would confuse AS. */ + gnutls_bye(conn->session, GNUTLS_SHUT_RDWR); + os_free(conn->push_buf); + conn->push_buf = NULL; + conn->push_buf_len = 0; + conn->established = 0; + conn->final_phase_finished = 0; +#ifdef GNUTLS_IA + if (conn->session_keys) { + os_memset(conn->session_keys, 0, conn->session_keys_len); + os_free(conn->session_keys); + } + conn->session_keys_len = 0; +#endif /* GNUTLS_IA */ + + gnutls_deinit(conn->session); + if (tls_gnutls_init_session(global, conn)) { + wpa_printf(MSG_INFO, "GnuTLS: Failed to preparare new session " + "for session resumption use"); + return -1; + } + + ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE, + conn->params_set ? conn->xcred : + global->xcred); + if (ret < 0) { + wpa_printf(MSG_INFO, "GnuTLS: Failed to configure credentials " + "for session resumption: %s", gnutls_strerror(ret)); + return -1; + } + + if (global->session_data) { + ret = gnutls_session_set_data(conn->session, + global->session_data, + global->session_data_size); + if (ret < 0) { + wpa_printf(MSG_INFO, "GnuTLS: Failed to set session " + "data: %s", gnutls_strerror(ret)); + return -1; + } + } + + return 0; +} + + +#if 0 +static int tls_match_altsubject(X509 *cert, const char *match) +{ + GENERAL_NAME *gen; + char *field, *tmp; + void *ext; + int i, found = 0; + size_t len; + + ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); + + for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) { + gen = sk_GENERAL_NAME_value(ext, i); + switch (gen->type) { + case GEN_EMAIL: + field = "EMAIL"; + break; + case GEN_DNS: + field = "DNS"; + break; + case GEN_URI: + field = "URI"; + break; + default: + field = NULL; + wpa_printf(MSG_DEBUG, "TLS: altSubjectName: " + "unsupported type=%d", gen->type); + break; + } + + if (!field) + continue; + + wpa_printf(MSG_DEBUG, "TLS: altSubjectName: %s:%s", + field, gen->d.ia5->data); + len = os_strlen(field) + 1 + + strlen((char *) gen->d.ia5->data) + 1; + tmp = os_malloc(len); + if (tmp == NULL) + continue; + snprintf(tmp, len, "%s:%s", field, gen->d.ia5->data); + if (strstr(tmp, match)) + found++; + os_free(tmp); + } + + return found; +} +#endif + + +#if 0 +static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) +{ + char buf[256]; + X509 *err_cert; + int err, depth; + SSL *ssl; + struct tls_connection *conn; + char *match, *altmatch; + + err_cert = X509_STORE_CTX_get_current_cert(x509_ctx); + err = X509_STORE_CTX_get_error(x509_ctx); + depth = X509_STORE_CTX_get_error_depth(x509_ctx); + ssl = X509_STORE_CTX_get_ex_data(x509_ctx, + SSL_get_ex_data_X509_STORE_CTX_idx()); + X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf)); + + conn = SSL_get_app_data(ssl); + match = conn ? conn->subject_match : NULL; + altmatch = conn ? conn->altsubject_match : NULL; + + if (!preverify_ok) { + wpa_printf(MSG_WARNING, "TLS: Certificate verification failed," + " error %d (%s) depth %d for '%s'", err, + X509_verify_cert_error_string(err), depth, buf); + } else { + wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - " + "preverify_ok=%d err=%d (%s) depth=%d buf='%s'", + preverify_ok, err, + X509_verify_cert_error_string(err), depth, buf); + if (depth == 0 && match && strstr(buf, match) == NULL) { + wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not " + "match with '%s'", buf, match); + preverify_ok = 0; + } else if (depth == 0 && altmatch && + !tls_match_altsubject(err_cert, altmatch)) { + wpa_printf(MSG_WARNING, "TLS: altSubjectName match " + "'%s' not found", altmatch); + preverify_ok = 0; + } + } + + return preverify_ok; +} +#endif + + +int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, + const struct tls_connection_params *params) +{ + int ret; + + if (conn == NULL || params == NULL) + return -1; + + os_free(conn->subject_match); + conn->subject_match = NULL; + if (params->subject_match) { + conn->subject_match = os_strdup(params->subject_match); + if (conn->subject_match == NULL) + return -1; + } + + os_free(conn->altsubject_match); + conn->altsubject_match = NULL; + if (params->altsubject_match) { + conn->altsubject_match = os_strdup(params->altsubject_match); + if (conn->altsubject_match == NULL) + return -1; + } + + /* TODO: gnutls_certificate_set_verify_flags(xcred, flags); + * to force peer validation(?) */ + + if (params->ca_cert) { + conn->verify_peer = 1; + ret = gnutls_certificate_set_x509_trust_file( + conn->xcred, params->ca_cert, GNUTLS_X509_FMT_PEM); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "Failed to read CA cert '%s' " + "in PEM format: %s", params->ca_cert, + gnutls_strerror(ret)); + ret = gnutls_certificate_set_x509_trust_file( + conn->xcred, params->ca_cert, + GNUTLS_X509_FMT_DER); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "Failed to read CA cert " + "'%s' in DER format: %s", + params->ca_cert, + gnutls_strerror(ret)); + return -1; + } + } + } + + if (params->client_cert && params->private_key) { + /* TODO: private_key_passwd? */ + ret = gnutls_certificate_set_x509_key_file( + conn->xcred, params->client_cert, params->private_key, + GNUTLS_X509_FMT_PEM); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "Failed to read client cert/key " + "in PEM format: %s", gnutls_strerror(ret)); + ret = gnutls_certificate_set_x509_key_file( + conn->xcred, params->client_cert, + params->private_key, GNUTLS_X509_FMT_DER); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "Failed to read client " + "cert/key in DER format: %s", + gnutls_strerror(ret)); + return ret; + } + } + } else if (params->private_key) { + int pkcs12_ok = 0; +#ifdef PKCS12_FUNCS + /* Try to load in PKCS#12 format */ +#if LIBGNUTLS_VERSION_NUMBER >= 0x010302 + ret = gnutls_certificate_set_x509_simple_pkcs12_file( + conn->xcred, params->private_key, GNUTLS_X509_FMT_DER, + params->private_key_passwd); + if (ret != 0) { + wpa_printf(MSG_DEBUG, "Failed to load private_key in " + "PKCS#12 format: %s", gnutls_strerror(ret)); + return -1; + } else + pkcs12_ok = 1; +#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */ +#endif /* PKCS12_FUNCS */ + + if (!pkcs12_ok) { + wpa_printf(MSG_DEBUG, "GnuTLS: PKCS#12 support not " + "included"); + return -1; + } + } + + conn->tls_ia = params->tls_ia; + conn->params_set = 1; + + ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_CERTIFICATE, + conn->xcred); + if (ret < 0) { + wpa_printf(MSG_INFO, "Failed to configure credentials: %s", + gnutls_strerror(ret)); + } + +#ifdef GNUTLS_IA + if (conn->iacred_cli) + gnutls_ia_free_client_credentials(conn->iacred_cli); + + ret = gnutls_ia_allocate_client_credentials(&conn->iacred_cli); + if (ret) { + wpa_printf(MSG_DEBUG, "Failed to allocate IA credentials: %s", + gnutls_strerror(ret)); + return -1; + } + + ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_IA, + conn->iacred_cli); + if (ret) { + wpa_printf(MSG_DEBUG, "Failed to configure IA credentials: %s", + gnutls_strerror(ret)); + gnutls_ia_free_client_credentials(conn->iacred_cli); + conn->iacred_cli = NULL; + return -1; + } +#endif /* GNUTLS_IE */ + + return ret; +} + + +int tls_global_set_params(void *tls_ctx, + const struct tls_connection_params *params) +{ + struct tls_global *global = tls_ctx; + int ret; + + /* Currently, global parameters are only set when running in server + * mode. */ + global->server = 1; + + if (global->params_set) { + gnutls_certificate_free_credentials(global->xcred); + global->params_set = 0; + } + + ret = gnutls_certificate_allocate_credentials(&global->xcred); + if (ret) { + wpa_printf(MSG_DEBUG, "Failed to allocate global credentials " + "%s", gnutls_strerror(ret)); + return -1; + } + + if (params->ca_cert) { + ret = gnutls_certificate_set_x509_trust_file( + global->xcred, params->ca_cert, GNUTLS_X509_FMT_PEM); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "Failed to read CA cert '%s' " + "in PEM format: %s", params->ca_cert, + gnutls_strerror(ret)); + ret = gnutls_certificate_set_x509_trust_file( + global->xcred, params->ca_cert, + GNUTLS_X509_FMT_DER); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "Failed to read CA cert " + "'%s' in DER format: %s", + params->ca_cert, + gnutls_strerror(ret)); + goto fail; + } + } + } + + if (params->client_cert && params->private_key) { + /* TODO: private_key_passwd? */ + ret = gnutls_certificate_set_x509_key_file( + global->xcred, params->client_cert, + params->private_key, GNUTLS_X509_FMT_PEM); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "Failed to read client cert/key " + "in PEM format: %s", gnutls_strerror(ret)); + ret = gnutls_certificate_set_x509_key_file( + global->xcred, params->client_cert, + params->private_key, GNUTLS_X509_FMT_DER); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "Failed to read client " + "cert/key in DER format: %s", + gnutls_strerror(ret)); + goto fail; + } + } + } else if (params->private_key) { + int pkcs12_ok = 0; +#ifdef PKCS12_FUNCS + /* Try to load in PKCS#12 format */ +#if LIBGNUTLS_VERSION_NUMBER >= 0x010302 + ret = gnutls_certificate_set_x509_simple_pkcs12_file( + global->xcred, params->private_key, + GNUTLS_X509_FMT_DER, params->private_key_passwd); + if (ret != 0) { + wpa_printf(MSG_DEBUG, "Failed to load private_key in " + "PKCS#12 format: %s", gnutls_strerror(ret)); + goto fail; + } else + pkcs12_ok = 1; +#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */ +#endif /* PKCS12_FUNCS */ + + if (!pkcs12_ok) { + wpa_printf(MSG_DEBUG, "GnuTLS: PKCS#12 support not " + "included"); + goto fail; + } + } + + global->params_set = 1; + + return 0; + +fail: + gnutls_certificate_free_credentials(global->xcred); + return -1; +} + + +int tls_global_set_verify(void *ssl_ctx, int check_crl) +{ + /* TODO */ + return 0; +} + + +int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, + int verify_peer) +{ + if (conn == NULL || conn->session == NULL) + return -1; + + conn->verify_peer = verify_peer; + gnutls_certificate_server_set_request(conn->session, + verify_peer ? GNUTLS_CERT_REQUIRE + : GNUTLS_CERT_REQUEST); + + return 0; +} + + +int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, + struct tls_keys *keys) +{ +#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK + security_parameters_st *sec; +#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */ + + if (conn == NULL || conn->session == NULL || keys == NULL) + return -1; + + os_memset(keys, 0, sizeof(*keys)); + +#ifdef GNUTLS_INTERNAL_STRUCTURE_HACK + sec = &conn->session->security_parameters; + keys->master_key = sec->master_secret; + keys->master_key_len = TLS_MASTER_SIZE; + keys->client_random = sec->client_random; + keys->server_random = sec->server_random; +#else /* GNUTLS_INTERNAL_STRUCTURE_HACK */ + keys->client_random = + (u8 *) gnutls_session_get_client_random(conn->session); + keys->server_random = + (u8 *) gnutls_session_get_server_random(conn->session); + /* No access to master_secret */ +#endif /* GNUTLS_INTERNAL_STRUCTURE_HACK */ + +#ifdef GNUTLS_IA + gnutls_ia_extract_inner_secret(conn->session, + (char *) conn->inner_secret); + keys->inner_secret = conn->inner_secret; + keys->inner_secret_len = TLS_MASTER_SIZE; +#endif /* GNUTLS_IA */ + + keys->client_random_len = TLS_RANDOM_SIZE; + keys->server_random_len = TLS_RANDOM_SIZE; + + return 0; +} + + +int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, + const char *label, int server_random_first, + u8 *out, size_t out_len) +{ +#if LIBGNUTLS_VERSION_NUMBER >= 0x010302 + if (conn == NULL || conn->session == NULL) + return -1; + + return gnutls_prf(conn->session, os_strlen(label), label, + server_random_first, 0, NULL, out_len, (char *) out); +#else /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */ + return -1; +#endif /* LIBGNUTLS_VERSION_NUMBER >= 0x010302 */ +} + + +static int tls_connection_verify_peer(struct tls_connection *conn) +{ + unsigned int status, num_certs, i; + struct os_time now; + const gnutls_datum_t *certs; + gnutls_x509_crt_t cert; + + if (gnutls_certificate_verify_peers2(conn->session, &status) < 0) { + wpa_printf(MSG_INFO, "TLS: Failed to verify peer " + "certificate chain"); + return -1; + } + + if (conn->verify_peer && (status & GNUTLS_CERT_INVALID)) { + wpa_printf(MSG_INFO, "TLS: Peer certificate not trusted"); + return -1; + } + + if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) { + wpa_printf(MSG_INFO, "TLS: Peer certificate does not have a " + "known issuer"); + return -1; + } + + if (status & GNUTLS_CERT_REVOKED) { + wpa_printf(MSG_INFO, "TLS: Peer certificate has been revoked"); + return -1; + } + + os_get_time(&now); + + certs = gnutls_certificate_get_peers(conn->session, &num_certs); + if (certs == NULL) { + wpa_printf(MSG_INFO, "TLS: No peer certificate chain " + "received"); + return -1; + } + + for (i = 0; i < num_certs; i++) { + char *buf; + size_t len; + if (gnutls_x509_crt_init(&cert) < 0) { + wpa_printf(MSG_INFO, "TLS: Certificate initialization " + "failed"); + return -1; + } + + if (gnutls_x509_crt_import(cert, &certs[i], + GNUTLS_X509_FMT_DER) < 0) { + wpa_printf(MSG_INFO, "TLS: Could not parse peer " + "certificate %d/%d", i + 1, num_certs); + gnutls_x509_crt_deinit(cert); + return -1; + } + + gnutls_x509_crt_get_dn(cert, NULL, &len); + len++; + buf = os_malloc(len + 1); + if (buf) { + buf[0] = buf[len] = '\0'; + gnutls_x509_crt_get_dn(cert, buf, &len); + } + wpa_printf(MSG_DEBUG, "TLS: Peer cert chain %d/%d: %s", + i + 1, num_certs, buf); + + if (i == 0) { + /* TODO: validate subject_match and altsubject_match */ + } + + os_free(buf); + + if (gnutls_x509_crt_get_expiration_time(cert) < now.sec || + gnutls_x509_crt_get_activation_time(cert) > now.sec) { + wpa_printf(MSG_INFO, "TLS: Peer certificate %d/%d is " + "not valid at this time", + i + 1, num_certs); + gnutls_x509_crt_deinit(cert); + return -1; + } + + gnutls_x509_crt_deinit(cert); + } + + return 0; +} + + +u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, + const u8 *in_data, size_t in_len, + size_t *out_len, u8 **appl_data, + size_t *appl_data_len) +{ + struct tls_global *global = ssl_ctx; + u8 *out_data; + int ret; + + if (appl_data) + *appl_data = NULL; + + 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); + os_free(conn->pull_buf); + } + conn->pull_buf = os_malloc(in_len); + if (conn->pull_buf == NULL) + return NULL; + os_memcpy(conn->pull_buf, in_data, in_len); + conn->pull_buf_offset = conn->pull_buf; + conn->pull_buf_len = in_len; + } + + ret = gnutls_handshake(conn->session); + if (ret < 0) { + switch (ret) { + case GNUTLS_E_AGAIN: + if (global->server && conn->established && + conn->push_buf == NULL) { + /* Need to return something to trigger + * completion of EAP-TLS. */ + conn->push_buf = os_malloc(1); + } + break; + case GNUTLS_E_FATAL_ALERT_RECEIVED: + wpa_printf(MSG_DEBUG, "%s - received fatal '%s' alert", + __func__, gnutls_alert_get_name( + gnutls_alert_get(conn->session))); + conn->read_alerts++; + /* continue */ + default: + wpa_printf(MSG_DEBUG, "%s - gnutls_handshake failed " + "-> %s", __func__, gnutls_strerror(ret)); + conn->failed++; + } + } else { + size_t size; + + if (conn->verify_peer && tls_connection_verify_peer(conn)) { + wpa_printf(MSG_INFO, "TLS: Peer certificate chain " + "failed validation"); + conn->failed++; + return NULL; + } + + if (conn->tls_ia && !gnutls_ia_handshake_p(conn->session)) { + wpa_printf(MSG_INFO, "TLS: No TLS/IA negotiation"); + conn->failed++; + return NULL; + } + + if (conn->tls_ia) + wpa_printf(MSG_DEBUG, "TLS: Start TLS/IA handshake"); + else { + wpa_printf(MSG_DEBUG, "TLS: Handshake completed " + "successfully"); + } + conn->established = 1; + if (conn->push_buf == NULL) { + /* Need to return something to get final TLS ACK. */ + conn->push_buf = os_malloc(1); + } + + gnutls_session_get_data(conn->session, NULL, &size); + if (global->session_data == NULL || + global->session_data_size < size) { + os_free(global->session_data); + global->session_data = os_malloc(size); + } + if (global->session_data) { + global->session_data_size = size; + gnutls_session_get_data(conn->session, + global->session_data, + &global->session_data_size); + } + } + + out_data = conn->push_buf; + *out_len = conn->push_buf_len; + conn->push_buf = NULL; + conn->push_buf_len = 0; + return out_data; +} + + +u8 * tls_connection_server_handshake(void *ssl_ctx, + struct tls_connection *conn, + const u8 *in_data, size_t in_len, + size_t *out_len) +{ + return tls_connection_handshake(ssl_ctx, conn, in_data, in_len, + out_len, NULL, NULL); +} + + +int tls_connection_encrypt(void *ssl_ctx, struct tls_connection *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len) +{ + ssize_t res; + +#ifdef GNUTLS_IA + if (conn->tls_ia) + res = gnutls_ia_send(conn->session, (char *) in_data, in_len); + else +#endif /* GNUTLS_IA */ + res = gnutls_record_send(conn->session, in_data, in_len); + if (res < 0) { + wpa_printf(MSG_INFO, "%s: Encryption failed: %s", + __func__, gnutls_strerror(res)); + return -1; + } + if (conn->push_buf == NULL) + return -1; + if (conn->push_buf_len < out_len) + out_len = conn->push_buf_len; + os_memcpy(out_data, conn->push_buf, out_len); + os_free(conn->push_buf); + conn->push_buf = NULL; + conn->push_buf_len = 0; + return out_len; +} + + +int tls_connection_decrypt(void *ssl_ctx, struct tls_connection *conn, + const u8 *in_data, size_t in_len, + u8 *out_data, size_t out_len) +{ + ssize_t res; + + if (conn->pull_buf) { + wpa_printf(MSG_DEBUG, "%s - %d bytes remaining in " + "pull_buf", __func__, conn->pull_buf_len); + os_free(conn->pull_buf); + } + conn->pull_buf = os_malloc(in_len); + if (conn->pull_buf == NULL) + return -1; + os_memcpy(conn->pull_buf, in_data, in_len); + conn->pull_buf_offset = conn->pull_buf; + conn->pull_buf_len = in_len; + +#ifdef GNUTLS_IA + if (conn->tls_ia) { + res = gnutls_ia_recv(conn->session, (char *) out_data, + out_len); + if (out_len >= 12 && + (res == GNUTLS_E_WARNING_IA_IPHF_RECEIVED || + res == GNUTLS_E_WARNING_IA_FPHF_RECEIVED)) { + int final = res == GNUTLS_E_WARNING_IA_FPHF_RECEIVED; + wpa_printf(MSG_DEBUG, "%s: Received %sPhaseFinished", + __func__, final ? "Final" : "Intermediate"); + + res = gnutls_ia_permute_inner_secret( + conn->session, conn->session_keys_len, + (char *) conn->session_keys); + if (conn->session_keys) { + os_memset(conn->session_keys, 0, + conn->session_keys_len); + os_free(conn->session_keys); + } + conn->session_keys = NULL; + conn->session_keys_len = 0; + if (res) { + wpa_printf(MSG_DEBUG, "%s: Failed to permute " + "inner secret: %s", + __func__, gnutls_strerror(res)); + return -1; + } + + res = gnutls_ia_verify_endphase(conn->session, + (char *) out_data); + if (res == 0) { + wpa_printf(MSG_DEBUG, "%s: Correct endphase " + "checksum", __func__); + } else { + wpa_printf(MSG_INFO, "%s: Endphase " + "verification failed: %s", + __func__, gnutls_strerror(res)); + return -1; + } + + if (final) + conn->final_phase_finished = 1; + + return 0; + } + + if (res < 0) { + wpa_printf(MSG_DEBUG, "%s - gnutls_ia_recv failed: %d " + "(%s)", __func__, res, + gnutls_strerror(res)); + } + return res; + } +#endif /* GNUTLS_IA */ + + res = gnutls_record_recv(conn->session, out_data, out_len); + if (res < 0) { + wpa_printf(MSG_DEBUG, "%s - gnutls_record_recv failed: %d " + "(%s)", __func__, res, gnutls_strerror(res)); + } + + return res; +} + + +int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return 0; + return gnutls_session_is_resumed(conn->session); +} + + +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) +{ + /* TODO */ + return -1; +} + + +int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, + char *buf, size_t buflen) +{ + /* TODO */ + buf[0] = '\0'; + return 0; +} + + +int tls_connection_enable_workaround(void *ssl_ctx, + struct tls_connection *conn) +{ + /* TODO: set SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS */ + return 0; +} + + +int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, + int ext_type, const u8 *data, + size_t data_len) +{ + /* TODO */ + return -1; +} + + +int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + return conn->failed; +} + + +int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + return conn->read_alerts; +} + + +int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + return conn->write_alerts; +} + + +int tls_connection_get_keyblock_size(void *tls_ctx, + struct tls_connection *conn) +{ + /* TODO */ + return -1; +} + + +unsigned int tls_capabilities(void *tls_ctx) +{ + unsigned int capa = 0; + +#ifdef GNUTLS_IA + capa |= TLS_CAPABILITY_IA; +#endif /* GNUTLS_IA */ + + return capa; +} + + +int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn, + int tls_ia) +{ +#ifdef GNUTLS_IA + int ret; + + if (conn == NULL) + return -1; + + conn->tls_ia = tls_ia; + if (!tls_ia) + return 0; + + ret = gnutls_ia_allocate_server_credentials(&conn->iacred_srv); + if (ret) { + wpa_printf(MSG_DEBUG, "Failed to allocate IA credentials: %s", + gnutls_strerror(ret)); + return -1; + } + + ret = gnutls_credentials_set(conn->session, GNUTLS_CRD_IA, + conn->iacred_srv); + if (ret) { + wpa_printf(MSG_DEBUG, "Failed to configure IA credentials: %s", + gnutls_strerror(ret)); + gnutls_ia_free_server_credentials(conn->iacred_srv); + conn->iacred_srv = NULL; + return -1; + } + + return 0; +#else /* GNUTLS_IA */ + return -1; +#endif /* GNUTLS_IA */ +} + + +int tls_connection_ia_send_phase_finished(void *tls_ctx, + struct tls_connection *conn, + int final, + u8 *out_data, size_t out_len) +{ +#ifdef GNUTLS_IA + int ret; + + if (conn == NULL || conn->session == NULL || !conn->tls_ia) + return -1; + + ret = gnutls_ia_permute_inner_secret(conn->session, + conn->session_keys_len, + (char *) conn->session_keys); + if (conn->session_keys) { + os_memset(conn->session_keys, 0, conn->session_keys_len); + os_free(conn->session_keys); + } + conn->session_keys = NULL; + conn->session_keys_len = 0; + if (ret) { + wpa_printf(MSG_DEBUG, "%s: Failed to permute inner secret: %s", + __func__, gnutls_strerror(ret)); + return -1; + } + + ret = gnutls_ia_endphase_send(conn->session, final); + if (ret) { + wpa_printf(MSG_DEBUG, "%s: Failed to send endphase: %s", + __func__, gnutls_strerror(ret)); + return -1; + } + + if (conn->push_buf == NULL) + return -1; + if (conn->push_buf_len < out_len) + out_len = conn->push_buf_len; + os_memcpy(out_data, conn->push_buf, out_len); + os_free(conn->push_buf); + conn->push_buf = NULL; + conn->push_buf_len = 0; + return out_len; +#else /* GNUTLS_IA */ + return -1; +#endif /* GNUTLS_IA */ +} + + +int tls_connection_ia_final_phase_finished(void *tls_ctx, + struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + + return conn->final_phase_finished; +} + + +int tls_connection_ia_permute_inner_secret(void *tls_ctx, + struct tls_connection *conn, + const u8 *key, size_t key_len) +{ +#ifdef GNUTLS_IA + if (conn == NULL || !conn->tls_ia) + return -1; + + if (conn->session_keys) { + os_memset(conn->session_keys, 0, conn->session_keys_len); + os_free(conn->session_keys); + } + conn->session_keys_len = 0; + + if (key) { + conn->session_keys = os_malloc(key_len); + if (conn->session_keys == NULL) + return -1; + os_memcpy(conn->session_keys, key, key_len); + conn->session_keys_len = key_len; + } else { + conn->session_keys = NULL; + conn->session_keys_len = 0; + } + + return 0; +#else /* GNUTLS_IA */ + return -1; +#endif /* GNUTLS_IA */ +} diff --git a/contrib/hostapd/tls_none.c b/contrib/hostapd/tls_none.c index 07fd879..ad08d50 100644 --- a/contrib/hostapd/tls_none.c +++ b/contrib/hostapd/tls_none.c @@ -1,6 +1,6 @@ /* * WPA Supplicant / SSL/TLS interface functions for no TLS case - * Copyright (c) 2004, Jouni Malinen <jkmaline@cc.hut.fi> + * 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 @@ -12,8 +12,7 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> +#include "includes.h" #include "common.h" #include "tls.h" @@ -26,3 +25,217 @@ void * tls_init(const struct tls_config *conf) void tls_deinit(void *ssl_ctx) { } + + +#ifdef EAP_TLS_NONE + +int tls_get_errors(void *tls_ctx) +{ + return 0; +} + + +struct tls_connection * tls_connection_init(void *tls_ctx) +{ + return NULL; +} + + +void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn) +{ +} + + +int tls_connection_established(void *tls_ctx, struct tls_connection *conn) +{ + return -1; +} + + +int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn) +{ + return -1; +} + + +int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, + const struct tls_connection_params *params) +{ + return -1; +} + + +int tls_global_set_params(void *tls_ctx, + const struct tls_connection_params *params) +{ + return -1; +} + + +int tls_global_set_verify(void *tls_ctx, int check_crl) +{ + return -1; +} + + +int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, + int verify_peer) +{ + return -1; +} + + +int tls_connection_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) +{ + return -1; +} + + +int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, + const char *label, int server_random_first, + u8 *out, size_t out_len) +{ + return -1; +} + + +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) +{ + return NULL; +} + + +u8 * tls_connection_server_handshake(void *tls_ctx, + struct tls_connection *conn, + const u8 *in_data, size_t in_len, + size_t *out_len) +{ + return NULL; +} + + +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) +{ + 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) +{ + return -1; +} + + +int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn) +{ + return 0; +} + + +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) +{ + return -1; +} + + +int tls_get_cipher(void *tls_ctx, struct tls_connection *conn, + char *buf, size_t buflen) +{ + return -1; +} + + +int tls_connection_enable_workaround(void *tls_ctx, + struct tls_connection *conn) +{ + return -1; +} + + +int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn, + int ext_type, const u8 *data, + size_t data_len) +{ + return -1; +} + + +int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn) +{ + return 0; +} + + +int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn) +{ + return 0; +} + + +int tls_connection_get_write_alerts(void *tls_ctx, + struct tls_connection *conn) +{ + return 0; +} + + +int tls_connection_get_keyblock_size(void *tls_ctx, + struct tls_connection *conn) +{ + return -1; +} + + +unsigned int tls_capabilities(void *tls_ctx) +{ + return 0; +} + + +int tls_connection_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; +} + +#endif /* EAP_TLS_NONE */ diff --git a/contrib/hostapd/tls_openssl.c b/contrib/hostapd/tls_openssl.c index 08d5a80..c8d941f 100644 --- a/contrib/hostapd/tls_openssl.c +++ b/contrib/hostapd/tls_openssl.c @@ -1,6 +1,6 @@ /* * WPA Supplicant / SSL/TLS interface functions for openssl - * Copyright (c) 2004-2006, Jouni Malinen <jkmaline@cc.hut.fi> + * 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 @@ -12,9 +12,7 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> +#include "includes.h" #ifndef CONFIG_SMARTCARD #ifndef OPENSSL_NO_ENGINE @@ -235,7 +233,7 @@ static int cryptoapi_rsa_priv_enc(int flen, const unsigned char *from, goto err; } - if (hash_size != flen) { + if ((int) hash_size != flen) { wpa_printf(MSG_INFO, "CryptoAPI: Invalid hash size (%u != %d)", (unsigned) hash_size, flen); RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, @@ -248,7 +246,7 @@ static int cryptoapi_rsa_priv_enc(int flen, const unsigned char *from, } len = RSA_size(rsa); - buf = malloc(len); + buf = os_malloc(len); if (buf == NULL) { RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, ERR_R_MALLOC_FAILURE); goto err; @@ -264,7 +262,7 @@ static int cryptoapi_rsa_priv_enc(int flen, const unsigned char *from, ret = len; err: - free(buf); + os_free(buf); CryptDestroyHash(hash); return ret; @@ -287,14 +285,14 @@ static void cryptoapi_free_data(struct cryptoapi_rsa_data *priv) CryptReleaseContext(priv->crypt_prov, 0); if (priv->cert) CertFreeCertificateContext(priv->cert); - free(priv); + os_free(priv); } static int cryptoapi_finish(RSA *rsa) { cryptoapi_free_data((struct cryptoapi_rsa_data *) rsa->meth->app_data); - free((void *) rsa->meth); + os_free((void *) rsa->meth); rsa->meth = NULL; return 1; } @@ -315,8 +313,7 @@ static const CERT_CONTEXT * cryptoapi_find_cert(const char *name, DWORD store) if (strncmp(name, "cert://", 7) == 0) { unsigned short wbuf[255]; - MultiByteToWideChar(CP_ACP, 0, name + 7, -1, - wbuf, sizeof(wbuf)); + MultiByteToWideChar(CP_ACP, 0, name + 7, -1, wbuf, 255); ret = CertFindCertificateInStore(cs, X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 0, CERT_FIND_SUBJECT_STR, @@ -327,8 +324,8 @@ static const CERT_CONTEXT * cryptoapi_find_cert(const char *name, DWORD store) const char *hash = name + 7; unsigned char *buf; - len = strlen(hash) / 2; - buf = malloc(len); + len = os_strlen(hash) / 2; + buf = os_malloc(len); if (buf && hexstr2bin(hash, buf, len) == 0) { blob.cbData = len; blob.pbData = buf; @@ -338,7 +335,7 @@ static const CERT_CONTEXT * cryptoapi_find_cert(const char *name, DWORD store) 0, CERT_FIND_HASH, &blob, NULL); } - free(buf); + os_free(buf); } CertCloseStore(cs, 0); @@ -359,17 +356,15 @@ static int tls_cryptoapi_cert(SSL *ssl, const char *name) strncmp(name, "hash://", 7) != 0)) return -1; - priv = malloc(sizeof(*priv)); - rsa_meth = malloc(sizeof(*rsa_meth)); + priv = os_zalloc(sizeof(*priv)); + rsa_meth = os_zalloc(sizeof(*rsa_meth)); if (priv == NULL || rsa_meth == NULL) { wpa_printf(MSG_WARNING, "CryptoAPI: Failed to allocate memory " "for CryptoAPI RSA method"); - free(priv); - free(rsa_meth); + os_free(priv); + os_free(rsa_meth); return -1; } - memset(priv, 0, sizeof(*priv)); - memset(rsa_meth, 0, sizeof(*rsa_meth)); priv->cert = cryptoapi_find_cert(name, CERT_SYSTEM_STORE_CURRENT_USER); if (priv->cert == NULL) { @@ -419,8 +414,11 @@ static int tls_cryptoapi_cert(SSL *ssl, const char *name) goto err; } - if (!SSL_use_certificate(ssl, cert)) + if (!SSL_use_certificate(ssl, cert)) { + RSA_free(rsa); + rsa = NULL; goto err; + } pub_rsa = cert->cert_info->key->pkey->pkey.rsa; X509_free(cert); cert = NULL; @@ -442,7 +440,7 @@ err: if (rsa) RSA_free(rsa); else { - free(rsa_meth); + os_free(rsa_meth); cryptoapi_free_data(priv); } return -1; @@ -455,6 +453,10 @@ static int tls_cryptoapi_ca_cert(SSL_CTX *ssl_ctx, SSL *ssl, const char *name) PCCERT_CONTEXT ctx = NULL; X509 *cert; char buf[128]; + const char *store; +#ifdef UNICODE + WCHAR *wstore; +#endif /* UNICODE */ if (mingw_load_crypto_func()) return -1; @@ -462,10 +464,20 @@ static int tls_cryptoapi_ca_cert(SSL_CTX *ssl_ctx, SSL *ssl, const char *name) if (name == NULL || strncmp(name, "cert_store://", 13) != 0) return -1; - cs = CertOpenSystemStore(0, name + 13); + store = name + 13; +#ifdef UNICODE + wstore = os_malloc((os_strlen(store) + 1) * sizeof(WCHAR)); + if (wstore == NULL) + return -1; + wsprintf(wstore, L"%S", store); + cs = CertOpenSystemStore(0, wstore); + os_free(wstore); +#else /* UNICODE */ + cs = CertOpenSystemStore(0, store); +#endif /* UNICODE */ if (cs == NULL) { wpa_printf(MSG_DEBUG, "%s: failed to open system cert store " - "'%s': error=%d", __func__, name + 13, + "'%s': error=%d", __func__, store, (int) GetLastError()); return -1; } @@ -643,21 +655,25 @@ static int tls_engine_load_dynamic_pkcs11(const char *pkcs11_so_path, { char *engine_id = "pkcs11"; const char *pre_cmd[] = { - "SO_PATH", pkcs11_so_path, - "ID", engine_id, + "SO_PATH", NULL /* pkcs11_so_path */, + "ID", NULL /* engine_id */, "LIST_ADD", "1", /* "NO_VCHECK", "1", */ "LOAD", NULL, NULL, NULL }; const char *post_cmd[] = { - "MODULE_PATH", pkcs11_module_path, + "MODULE_PATH", NULL /* pkcs11_module_path */, NULL, NULL }; if (!pkcs11_so_path || !pkcs11_module_path) return 0; + pre_cmd[1] = pkcs11_so_path; + pre_cmd[3] = engine_id; + post_cmd[1] = pkcs11_module_path; + wpa_printf(MSG_DEBUG, "ENGINE: Loading pkcs11 Engine from %s", pkcs11_so_path); @@ -673,8 +689,8 @@ static int tls_engine_load_dynamic_opensc(const char *opensc_so_path) { char *engine_id = "opensc"; const char *pre_cmd[] = { - "SO_PATH", opensc_so_path, - "ID", engine_id, + "SO_PATH", NULL /* opensc_so_path */, + "ID", NULL /* engine_id */, "LIST_ADD", "1", "LOAD", NULL, NULL, NULL @@ -683,6 +699,9 @@ static int tls_engine_load_dynamic_opensc(const char *opensc_so_path) if (!opensc_so_path) return 0; + pre_cmd[1] = opensc_so_path; + pre_cmd[3] = engine_id; + wpa_printf(MSG_DEBUG, "ENGINE: Loading OpenSC Engine from %s", opensc_so_path); @@ -853,15 +872,14 @@ struct tls_connection * tls_connection_init(void *ssl_ctx) SSL_CTX *ssl = ssl_ctx; struct tls_connection *conn; - conn = malloc(sizeof(*conn)); + conn = os_zalloc(sizeof(*conn)); if (conn == NULL) return NULL; - memset(conn, 0, sizeof(*conn)); conn->ssl = SSL_new(ssl); if (conn->ssl == NULL) { tls_show_errors(MSG_INFO, __func__, "Failed to initialize new SSL connection"); - free(conn); + os_free(conn); return NULL; } @@ -875,7 +893,7 @@ struct tls_connection * tls_connection_init(void *ssl_ctx) tls_show_errors(MSG_INFO, __func__, "Failed to create a new BIO for ssl_in"); SSL_free(conn->ssl); - free(conn); + os_free(conn); return NULL; } @@ -885,7 +903,7 @@ struct tls_connection * tls_connection_init(void *ssl_ctx) "Failed to create a new BIO for ssl_out"); SSL_free(conn->ssl); BIO_free(conn->ssl_in); - free(conn); + os_free(conn); return NULL; } @@ -899,12 +917,12 @@ void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) { if (conn == NULL) return; - free(conn->pre_shared_secret); + os_free(conn->pre_shared_secret); SSL_free(conn->ssl); tls_engine_deinit(conn); - free(conn->subject_match); - free(conn->altsubject_match); - free(conn); + os_free(conn->subject_match); + os_free(conn->altsubject_match); + os_free(conn); } @@ -928,55 +946,71 @@ int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) } -static int tls_match_altsubject(X509 *cert, const char *match) +static int tls_match_altsubject_component(X509 *cert, int type, + const char *value, size_t len) { GENERAL_NAME *gen; - char *field, *tmp; void *ext; int i, found = 0; - size_t len; ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) { gen = sk_GENERAL_NAME_value(ext, i); - switch (gen->type) { - case GEN_EMAIL: - field = "EMAIL"; - break; - case GEN_DNS: - field = "DNS"; - break; - case GEN_URI: - field = "URI"; - break; - default: - field = NULL; - wpa_printf(MSG_DEBUG, "TLS: altSubjectName: " - "unsupported type=%d", gen->type); - break; - } - - if (!field) - continue; - - wpa_printf(MSG_DEBUG, "TLS: altSubjectName: %s:%s", - field, gen->d.ia5->data); - len = strlen(field) + 1 + strlen((char *) gen->d.ia5->data) + - 1; - tmp = malloc(len); - if (tmp == NULL) + if (gen->type != type) continue; - snprintf(tmp, len, "%s:%s", field, gen->d.ia5->data); - if (strstr(tmp, match)) + if (os_strlen((char *) gen->d.ia5->data) == len && + os_memcmp(value, gen->d.ia5->data, len) == 0) found++; - free(tmp); } return found; } +static int tls_match_altsubject(X509 *cert, const char *match) +{ + int type; + const char *pos, *end; + size_t len; + + pos = match; + do { + if (os_strncmp(pos, "EMAIL:", 6) == 0) { + type = GEN_EMAIL; + pos += 6; + } else if (os_strncmp(pos, "DNS:", 4) == 0) { + type = GEN_DNS; + pos += 4; + } else if (os_strncmp(pos, "URI:", 4) == 0) { + type = GEN_URI; + pos += 4; + } else { + wpa_printf(MSG_INFO, "TLS: Invalid altSubjectName " + "match '%s'", pos); + return 0; + } + end = os_strchr(pos, ';'); + while (end) { + if (os_strncmp(end + 1, "EMAIL:", 6) == 0 || + os_strncmp(end + 1, "DNS:", 4) == 0 || + os_strncmp(end + 1, "URI:", 4) == 0) + break; + end = os_strchr(end + 1, ';'); + } + if (end) + len = end - pos; + else + len = os_strlen(pos); + if (tls_match_altsubject_component(cert, type, pos, len) > 0) + return 1; + pos = end + 1; + } while (end); + + return 0; +} + + static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) { char buf[256]; @@ -1006,7 +1040,7 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) "preverify_ok=%d err=%d (%s) depth=%d buf='%s'", preverify_ok, err, X509_verify_cert_error_string(err), depth, buf); - if (depth == 0 && match && strstr(buf, match) == NULL) { + if (depth == 0 && match && os_strstr(buf, match) == NULL) { wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not " "match with '%s'", buf, match); preverify_ok = 0; @@ -1071,11 +1105,20 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, } if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) { + unsigned long err = ERR_peek_error(); tls_show_errors(MSG_WARNING, __func__, "Failed to add ca_cert_blob to " "certificate store"); - X509_free(cert); - return -1; + if (ERR_GET_LIB(err) == ERR_LIB_X509 && + ERR_GET_REASON(err) == + X509_R_CERT_ALREADY_IN_HASH_TABLE) { + wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring " + "cert already in hash table error", + __func__); + } else { + X509_free(cert); + return -1; + } } X509_free(cert); wpa_printf(MSG_DEBUG, "OpenSSL: %s - added ca_cert_blob " @@ -1128,9 +1171,8 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, } -int tls_global_ca_cert(void *_ssl_ctx, const char *ca_cert) +static int tls_global_ca_cert(SSL_CTX *ssl_ctx, const char *ca_cert) { - SSL_CTX *ssl_ctx = _ssl_ctx; if (ca_cert) { if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, NULL) != 1) { @@ -1174,23 +1216,22 @@ int tls_global_set_verify(void *ssl_ctx, int check_crl) } -static int tls_connection_set_subject_match(void *ssl_ctx, - struct tls_connection *conn, +static int tls_connection_set_subject_match(struct tls_connection *conn, const char *subject_match, const char *altsubject_match) { - free(conn->subject_match); + os_free(conn->subject_match); conn->subject_match = NULL; if (subject_match) { - conn->subject_match = strdup(subject_match); + conn->subject_match = os_strdup(subject_match); if (conn->subject_match == NULL) return -1; } - free(conn->altsubject_match); + os_free(conn->altsubject_match); conn->altsubject_match = NULL; if (altsubject_match) { - conn->altsubject_match = strdup(altsubject_match); + conn->altsubject_match = os_strdup(altsubject_match); if (conn->altsubject_match == NULL) return -1; } @@ -1219,8 +1260,7 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, } -static int tls_connection_client_cert(void *ssl_ctx, - struct tls_connection *conn, +static int tls_connection_client_cert(struct tls_connection *conn, const char *client_cert, const u8 *client_cert_blob, size_t client_cert_blob_len) @@ -1270,10 +1310,9 @@ static int tls_connection_client_cert(void *ssl_ctx, } -int tls_global_client_cert(void *_ssl_ctx, const char *client_cert) +static int tls_global_client_cert(SSL_CTX *ssl_ctx, const char *client_cert) { #ifndef OPENSSL_NO_STDIO - SSL_CTX *ssl_ctx = _ssl_ctx; if (client_cert == NULL) return 0; @@ -1300,9 +1339,9 @@ static int tls_passwd_cb(char *buf, int size, int rwflag, void *password) if (password == NULL) { return 0; } - strncpy(buf, (char *) password, size); + os_strncpy(buf, (char *) password, size); buf[size - 1] = '\0'; - return strlen(buf); + return os_strlen(buf); } @@ -1322,6 +1361,7 @@ static int tls_parse_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, PKCS12 *p12, if (!PKCS12_parse(p12, passwd, &pkey, &cert, &certs)) { tls_show_errors(MSG_DEBUG, __func__, "Failed to parse PKCS12 file"); + PKCS12_free(p12); return -1; } wpa_printf(MSG_DEBUG, "TLS: Successfully parsed PKCS12 data"); @@ -1388,7 +1428,7 @@ static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key, FILE *f; PKCS12 *p12; - f = fopen(private_key, "r"); + f = fopen(private_key, "rb"); if (f == NULL) return -1; @@ -1434,8 +1474,7 @@ static int tls_read_pkcs12_blob(SSL_CTX *ssl_ctx, SSL *ssl, } -static int tls_connection_engine_private_key(void *_ssl_ctx, - struct tls_connection *conn) +static int tls_connection_engine_private_key(struct tls_connection *conn) { #ifndef OPENSSL_NO_ENGINE if (SSL_use_PrivateKey(conn->ssl, conn->private_key) != 1) { @@ -1472,7 +1511,7 @@ static int tls_connection_private_key(void *_ssl_ctx, return 0; if (private_key_passwd) { - passwd = strdup(private_key_passwd); + passwd = os_strdup(private_key_passwd); if (passwd == NULL) return -1; } else @@ -1582,13 +1621,13 @@ static int tls_connection_private_key(void *_ssl_ctx, if (!ok) { wpa_printf(MSG_INFO, "OpenSSL: Failed to load private key"); - free(passwd); + os_free(passwd); ERR_clear_error(); return -1; } ERR_clear_error(); SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL); - free(passwd); + os_free(passwd); if (!SSL_check_private_key(conn->ssl)) { tls_show_errors(MSG_INFO, __func__, "Private key failed " @@ -1601,17 +1640,16 @@ static int tls_connection_private_key(void *_ssl_ctx, } -int tls_global_private_key(void *_ssl_ctx, const char *private_key, - const char *private_key_passwd) +static int tls_global_private_key(SSL_CTX *ssl_ctx, const char *private_key, + const char *private_key_passwd) { - SSL_CTX *ssl_ctx = _ssl_ctx; char *passwd; if (private_key == NULL) return 0; if (private_key_passwd) { - passwd = strdup(private_key_passwd); + passwd = os_strdup(private_key_passwd); if (passwd == NULL) return -1; } else @@ -1629,11 +1667,11 @@ int tls_global_private_key(void *_ssl_ctx, const char *private_key, tls_read_pkcs12(ssl_ctx, NULL, private_key, passwd)) { tls_show_errors(MSG_INFO, __func__, "Failed to load private key"); - free(passwd); + os_free(passwd); ERR_clear_error(); return -1; } - free(passwd); + os_free(passwd); ERR_clear_error(); SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL); @@ -1647,8 +1685,7 @@ int tls_global_private_key(void *_ssl_ctx, const char *private_key, } -static int tls_connection_dh(void *ssl_ctx, struct tls_connection *conn, - const char *dh_file) +static int tls_connection_dh(struct tls_connection *conn, const char *dh_file) { #ifdef OPENSSL_NO_DH if (dh_file == NULL) @@ -1733,7 +1770,7 @@ int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL) return -1; - memset(keys, 0, sizeof(*keys)); + os_memset(keys, 0, sizeof(*keys)); keys->master_key = ssl->session->master_key; keys->master_key_len = ssl->session->master_key_length; keys->client_random = ssl->s3->client_random; @@ -1745,13 +1782,25 @@ int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, } +int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, + const char *label, int server_random_first, + u8 *out, size_t out_len) +{ + return -1; +} + + u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, const u8 *in_data, size_t in_len, - size_t *out_len) + size_t *out_len, u8 **appl_data, + size_t *appl_data_len) { int res; u8 *out_data; + if (appl_data) + *appl_data = NULL; + /* * Give TLS handshake data from the server (if available) to OpenSSL * for processing. @@ -1782,7 +1831,7 @@ u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, /* Get the TLS handshake data to be sent to the server */ res = BIO_ctrl_pending(conn->ssl_out); wpa_printf(MSG_DEBUG, "SSL: %d bytes pending from ssl_out", res); - out_data = malloc(res == 0 ? 1 : res); + out_data = os_malloc(res == 0 ? 1 : res); if (out_data == NULL) { wpa_printf(MSG_DEBUG, "SSL: Failed to allocate memory for " "handshake output (%d bytes)", res); @@ -1805,6 +1854,26 @@ u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, return NULL; } *out_len = res; + + if (SSL_is_init_finished(conn->ssl) && appl_data) { + *appl_data = os_malloc(in_len); + if (*appl_data) { + res = SSL_read(conn->ssl, *appl_data, in_len); + if (res < 0) { + tls_show_errors(MSG_INFO, __func__, + "Failed to read possible " + "Application Data"); + os_free(*appl_data); + *appl_data = NULL; + } else { + *appl_data_len = res; + wpa_hexdump_key(MSG_MSGDUMP, "SSL: Application" + " Data in Finish message", + *appl_data, *appl_data_len); + } + } + } + return out_data; } @@ -1833,7 +1902,7 @@ u8 * tls_connection_server_handshake(void *ssl_ctx, res = BIO_ctrl_pending(conn->ssl_out); wpa_printf(MSG_DEBUG, "SSL: %d bytes pending from ssl_out", res); - out_data = malloc(res == 0 ? 1 : res); + out_data = os_malloc(res == 0 ? 1 : res); if (out_data == NULL) { wpa_printf(MSG_DEBUG, "SSL: Failed to allocate memory for " "handshake output (%d bytes)", res); @@ -1930,7 +1999,7 @@ int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn) } -#ifdef EAP_FAST +#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. */ @@ -1944,7 +2013,8 @@ static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len, if (conn == NULL || conn->pre_shared_secret == 0) return 0; - memcpy(secret, conn->pre_shared_secret, conn->pre_shared_secret_len); + os_memcpy(secret, conn->pre_shared_secret, + conn->pre_shared_secret_len); *secret_len = conn->pre_shared_secret_len; return 1; @@ -1957,14 +2027,14 @@ int tls_connection_set_master_key(void *ssl_ctx, struct tls_connection *conn, if (conn == NULL || key_len > SSL_MAX_MASTER_KEY_LENGTH) return -1; - free(conn->pre_shared_secret); + os_free(conn->pre_shared_secret); conn->pre_shared_secret = NULL; conn->pre_shared_secret_len = 0; if (key) { - conn->pre_shared_secret = malloc(key_len); + conn->pre_shared_secret = os_malloc(key_len); if (conn->pre_shared_secret) { - memcpy(conn->pre_shared_secret, key, key_len); + 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, @@ -1977,17 +2047,58 @@ int tls_connection_set_master_key(void *ssl_ctx, struct tls_connection *conn, return 0; } -#endif /* EAP_FAST */ +#endif /* EAP_FAST || EAP_FAST_DYNAMIC */ -int tls_connection_set_anon_dh(void *ssl_ctx, struct tls_connection *conn) +int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, + u8 *ciphers) { - if (conn == NULL || conn->ssl == NULL) + char buf[100], *pos, *end; + u8 *c; + int ret; + + if (conn == NULL || conn->ssl == NULL || ciphers == NULL) return -1; - if (SSL_set_cipher_list(conn->ssl, "ADH-AES128-SHA") != 1) { + buf[0] = '\0'; + pos = buf; + end = pos + sizeof(buf); + + c = ciphers; + while (*c != TLS_CIPHER_NONE) { + const char *suite; + + switch (*c) { + case TLS_CIPHER_RC4_SHA: + suite = "RC4-SHA"; + break; + case TLS_CIPHER_AES128_SHA: + suite = "AES128-SHA"; + break; + case TLS_CIPHER_RSA_DHE_AES128_SHA: + suite = "DHE-RSA-AES128-SHA"; + break; + case TLS_CIPHER_ANON_DH_AES128_SHA: + suite = "ADH-AES128-SHA"; + break; + default: + wpa_printf(MSG_DEBUG, "TLS: Unsupported " + "cipher selection: %d", *c); + return -1; + } + ret = os_snprintf(pos, end - pos, ":%s", suite); + if (ret < 0 || ret >= end - pos) + break; + pos += ret; + + c++; + } + + wpa_printf(MSG_DEBUG, "OpenSSL: cipher suites: %s", buf + 1); + + if (SSL_set_cipher_list(conn->ssl, buf + 1) != 1) { tls_show_errors(MSG_INFO, __func__, - "Anon DH configuration failed"); + "Cipher suite configuration failed"); return -1; } @@ -2006,7 +2117,8 @@ int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, if (name == NULL) return -1; - snprintf(buf, buflen, "%s", name); + os_snprintf(buf, buflen, "%s", name); + buf[buflen - 1] = '\0'; return 0; } @@ -2020,7 +2132,7 @@ int tls_connection_enable_workaround(void *ssl_ctx, } -#ifdef EAP_FAST +#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) /* ClientHello TLS extensions require a patch to openssl, so this function is * commented out unless explicitly needed for EAP-FAST in order to be able to * build this file with unmodified openssl. */ @@ -2037,7 +2149,7 @@ int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, return 0; } -#endif /* EAP_FAST */ +#endif /* EAP_FAST || EAP_FAST_DYNAMIC */ int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn) @@ -2078,7 +2190,7 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, __func__, ERR_error_string(err, NULL)); } - if (tls_connection_set_subject_match(tls_ctx, conn, + if (tls_connection_set_subject_match(conn, params->subject_match, params->altsubject_match)) return -1; @@ -2087,7 +2199,7 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, params->ca_cert_blob_len, params->ca_path)) return -1; - if (tls_connection_client_cert(tls_ctx, conn, params->client_cert, + if (tls_connection_client_cert(conn, params->client_cert, params->client_cert_blob, params->client_cert_blob_len)) return -1; @@ -2098,7 +2210,7 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, params->key_id); if (ret) return ret; - if (tls_connection_engine_private_key(tls_ctx, conn)) + if (tls_connection_engine_private_key(conn)) return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED; } else if (tls_connection_private_key(tls_ctx, conn, params->private_key, @@ -2110,7 +2222,7 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, return -1; } - if (tls_connection_dh(tls_ctx, conn, params->dh_file)) { + if (tls_connection_dh(conn, params->dh_file)) { wpa_printf(MSG_INFO, "TLS: Failed to load DH file '%s'", params->dh_file); return -1; @@ -2122,6 +2234,31 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, } +int tls_global_set_params(void *tls_ctx, + const struct tls_connection_params *params) +{ + SSL_CTX *ssl_ctx = tls_ctx; + unsigned long err; + + while ((err = ERR_get_error())) { + wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s", + __func__, ERR_error_string(err, NULL)); + } + + if (tls_global_ca_cert(ssl_ctx, params->ca_cert)) + return -1; + + if (tls_global_client_cert(ssl_ctx, params->client_cert)) + return -1; + + if (tls_global_private_key(ssl_ctx, params->private_key, + params->private_key_passwd)) + return -1; + + return 0; +} + + int tls_connection_get_keyblock_size(void *tls_ctx, struct tls_connection *conn) { @@ -2141,3 +2278,40 @@ int tls_connection_get_keyblock_size(void *tls_ctx, EVP_MD_size(h) + EVP_CIPHER_iv_length(c)); } + + +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/contrib/hostapd/version.h b/contrib/hostapd/version.h index 8f8eff8..2eed290 100644 --- a/contrib/hostapd/version.h +++ b/contrib/hostapd/version.h @@ -1,6 +1,6 @@ #ifndef VERSION_H #define VERSION_H -#define VERSION_STR "0.4.8" +#define VERSION_STR "0.5.8" #endif /* VERSION_H */ diff --git a/contrib/hostapd/vlan_init.c b/contrib/hostapd/vlan_init.c new file mode 100644 index 0000000..d546c74 --- /dev/null +++ b/contrib/hostapd/vlan_init.c @@ -0,0 +1,835 @@ +/* + * hostapd / VLAN initialization + * Copyright 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 "driver.h" +#include "vlan_init.h" + + +#ifdef CONFIG_FULL_DYNAMIC_VLAN + +#include <net/if.h> +#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" +#include "eloop.h" + + +struct full_dynamic_vlan { + int s; /* socket on which to listen for new/removed interfaces. */ +}; + + +static int ifconfig_helper(const char *if_name, int up) +{ + int fd; + struct ifreq ifr; + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket[AF_INET,SOCK_STREAM]"); + return -1; + } + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, if_name, IFNAMSIZ); + + if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) { + perror("ioctl[SIOCGIFFLAGS]"); + close(fd); + return -1; + } + + if (up) + ifr.ifr_flags |= IFF_UP; + else + ifr.ifr_flags &= ~IFF_UP; + + if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) { + perror("ioctl[SIOCSIFFLAGS]"); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +static int ifconfig_up(const char *if_name) +{ + return ifconfig_helper(if_name, 1); +} + + +static int ifconfig_down(const char *if_name) +{ + return ifconfig_helper(if_name, 0); +} + + +/* + * These are only available in recent linux headers (without the leading + * underscore). + */ +#define _GET_VLAN_REALDEV_NAME_CMD 8 +#define _GET_VLAN_VID_CMD 9 + +/* This value should be 256 ONLY. If it is something else, then hostapd + * might crash!, as this value has been hard-coded in 2.4.x kernel + * bridging code. + */ +#define MAX_BR_PORTS 256 + +static int br_delif(const char *br_name, const char *if_name) +{ + int fd; + struct ifreq ifr; + unsigned long args[2]; + int if_index; + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket[AF_INET,SOCK_STREAM]"); + return -1; + } + + if_index = if_nametoindex(if_name); + + if (if_index == 0) { + printf("Failure determining interface index for '%s'\n", + if_name); + close(fd); + return -1; + } + + args[0] = BRCTL_DEL_IF; + args[1] = if_index; + + strncpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); + ifr.ifr_data = (__caddr_t) args; + + if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0 && errno != EINVAL) { + /* No error if interface already removed. */ + perror("ioctl[SIOCDEVPRIVATE,BRCTL_DEL_IF]"); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +/* + Add interface 'if_name' to the bridge 'br_name' + + returns -1 on error + returns 1 if the interface is already part of the bridge + returns 0 otherwise +*/ +static int br_addif(const char *br_name, const char *if_name) +{ + int fd; + struct ifreq ifr; + unsigned long args[2]; + int if_index; + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket[AF_INET,SOCK_STREAM]"); + return -1; + } + + if_index = if_nametoindex(if_name); + + if (if_index == 0) { + printf("Failure determining interface index for '%s'\n", + if_name); + close(fd); + return -1; + } + + args[0] = BRCTL_ADD_IF; + args[1] = if_index; + + strncpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); + ifr.ifr_data = (__caddr_t) args; + + if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { + if (errno == EBUSY) { + /* The interface is already added. */ + close(fd); + return 1; + } + + perror("ioctl[SIOCDEVPRIVATE,BRCTL_ADD_IF]"); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +static int br_delbr(const char *br_name) +{ + int fd; + unsigned long arg[2]; + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket[AF_INET,SOCK_STREAM]"); + return -1; + } + + arg[0] = BRCTL_DEL_BRIDGE; + arg[1] = (unsigned long) br_name; + + if (ioctl(fd, SIOCGIFBR, arg) < 0 && errno != ENXIO) { + /* No error if bridge already removed. */ + perror("ioctl[BRCTL_DEL_BRIDGE]"); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +/* + Add a bridge with the name 'br_name'. + + returns -1 on error + returns 1 if the bridge already exists + returns 0 otherwise +*/ +static int br_addbr(const char *br_name) +{ + int fd; + unsigned long arg[2]; + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket[AF_INET,SOCK_STREAM]"); + return -1; + } + + arg[0] = BRCTL_ADD_BRIDGE; + arg[1] = (unsigned long) br_name; + + if (ioctl(fd, SIOCGIFBR, arg) < 0) { + if (errno == EEXIST) { + /* The bridge is already added. */ + close(fd); + return 1; + } else { + perror("ioctl[BRCTL_ADD_BRIDGE]"); + close(fd); + return -1; + } + } + + close(fd); + return 0; +} + + +static int br_getnumports(const char *br_name) +{ + int fd; + int i; + int port_cnt = 0; + unsigned long arg[4]; + int ifindices[MAX_BR_PORTS]; + struct ifreq ifr; + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket[AF_INET,SOCK_STREAM]"); + return -1; + } + + arg[0] = BRCTL_GET_PORT_LIST; + arg[1] = (unsigned long) ifindices; + arg[2] = MAX_BR_PORTS; + arg[3] = 0; + + memset(ifindices, 0, sizeof(ifindices)); + strncpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); + ifr.ifr_data = (__caddr_t) arg; + + if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { + perror("ioctl[SIOCDEVPRIVATE,BRCTL_GET_PORT_LIST]"); + close(fd); + return -1; + } + + for (i = 1; i < MAX_BR_PORTS; i++) { + if (ifindices[i] > 0) { + port_cnt++; + } + } + + close(fd); + return port_cnt; +} + + +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)) { + fprintf(stderr, "Interface name to long.\n"); + return -1; + } + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket[AF_INET,SOCK_STREAM]"); + return -1; + } + + memset(&if_request, 0, sizeof(if_request)); + + strcpy(if_request.device1, if_name); + if_request.cmd = DEL_VLAN_CMD; + + if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { + perror("ioctl[SIOCSIFVLAN,DEL_VLAN_CMD]"); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +/* + Add a vlan interface with VLAN ID 'vid' and tagged interface + 'if_name'. + + returns -1 on error + returns 1 if the interface already exists + returns 0 otherwise +*/ +static int vlan_add(const char *if_name, int vid) +{ + int fd; + struct vlan_ioctl_args if_request; + + ifconfig_up(if_name); + + if ((strlen(if_name) + 1) > sizeof(if_request.device1)) { + fprintf(stderr, "Interface name to long.\n"); + return -1; + } + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket[AF_INET,SOCK_STREAM]"); + return -1; + } + + 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); + + if_request.cmd = _GET_VLAN_VID_CMD; + + if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0) { + + if (if_request.u.VID == vid) { + if_request.cmd = _GET_VLAN_REALDEV_NAME_CMD; + + if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 + && strncmp(if_request.u.device2, if_name, + sizeof(if_request.u.device2)) == 0) { + close(fd); + return 1; + } + } + } + + /* A suitable vlan device does not already exist, add one. */ + + memset(&if_request, 0, sizeof(if_request)); + strcpy(if_request.device1, if_name); + if_request.u.VID = vid; + if_request.cmd = ADD_VLAN_CMD; + + if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { + perror("ioctl[SIOCSIFVLAN,ADD_VLAN_CMD]"); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +static int vlan_set_name_type(unsigned int name_type) +{ + int fd; + struct vlan_ioctl_args if_request; + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + perror("socket[AF_INET,SOCK_STREAM]"); + return -1; + } + + memset(&if_request, 0, sizeof(if_request)); + + if_request.u.name_type = name_type; + if_request.cmd = SET_VLAN_NAME_TYPE_CMD; + if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { + perror("ioctl[SIOCSIFVLAN,SET_VLAN_NAME_TYPE_CMD]"); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +static void vlan_newlink(char *ifname, struct hostapd_data *hapd) +{ + char vlan_ifname[IFNAMSIZ]; + char br_name[IFNAMSIZ]; + struct hostapd_vlan *vlan = hapd->conf->vlan; + char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; + + while (vlan) { + if (strcmp(ifname, vlan->ifname) == 0) { + + snprintf(br_name, sizeof(br_name), "brvlan%d", + vlan->vlan_id); + + if (!br_addbr(br_name)) + vlan->clean |= DVLAN_CLEAN_BR; + + ifconfig_up(br_name); + + if (tagged_interface) { + + if (!vlan_add(tagged_interface, vlan->vlan_id)) + vlan->clean |= DVLAN_CLEAN_VLAN; + + snprintf(vlan_ifname, sizeof(vlan_ifname), + "vlan%d", vlan->vlan_id); + + if (!br_addif(br_name, vlan_ifname)) + vlan->clean |= DVLAN_CLEAN_VLAN_PORT; + + ifconfig_up(vlan_ifname); + } + + if (!br_addif(br_name, ifname)) + vlan->clean |= DVLAN_CLEAN_WLAN_PORT; + + ifconfig_up(ifname); + + break; + } + vlan = vlan->next; + } +} + + +static void vlan_dellink(char *ifname, struct hostapd_data *hapd) +{ + char vlan_ifname[IFNAMSIZ]; + char br_name[IFNAMSIZ]; + struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan; + char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; + int numports; + + first = prev = vlan; + + while (vlan) { + if (strcmp(ifname, vlan->ifname) == 0) { + 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); + + numports = br_getnumports(br_name); + if (numports == 1) { + br_delif(br_name, vlan_ifname); + + vlan_rem(vlan_ifname); + + ifconfig_down(br_name); + br_delbr(br_name); + } + } + + if (vlan == first) { + hapd->conf->vlan = vlan->next; + } else { + prev->next = vlan->next; + } + free(vlan); + + break; + } + prev = vlan; + vlan = vlan->next; + } +} + + +static void +vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del, + struct hostapd_data *hapd) +{ + struct ifinfomsg *ifi; + int attrlen, nlmsg_len, rta_len; + struct rtattr *attr; + + if (len < sizeof(*ifi)) + return; + + ifi = NLMSG_DATA(h); + + nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + + attrlen = h->nlmsg_len - nlmsg_len; + if (attrlen < 0) + return; + + attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + char ifname[IFNAMSIZ + 1]; + + if (attr->rta_type == IFLA_IFNAME) { + int n = attr->rta_len - rta_len; + if (n < 0) + break; + + memset(ifname, 0, sizeof(ifname)); + + if ((size_t) n > sizeof(ifname)) + n = sizeof(ifname); + memcpy(ifname, ((char *) attr) + rta_len, n); + + if (del) + vlan_dellink(ifname, hapd); + else + vlan_newlink(ifname, hapd); + } + + attr = RTA_NEXT(attr, attrlen); + } +} + + +static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + char buf[8192]; + int left; + struct sockaddr_nl from; + socklen_t fromlen; + struct nlmsghdr *h; + struct hostapd_data *hapd = eloop_ctx; + + fromlen = sizeof(from); + left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, + (struct sockaddr *) &from, &fromlen); + if (left < 0) { + if (errno != EINTR && errno != EAGAIN) + 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", len, left, plen); + break; + } + + switch (h->nlmsg_type) { + case RTM_NEWLINK: + vlan_read_ifnames(h, plen, 0, hapd); + break; + case RTM_DELLINK: + vlan_read_ifnames(h, plen, 1, hapd); + break; + } + + len = NLMSG_ALIGN(len); + left -= len; + h = (struct nlmsghdr *) ((char *) h + len); + } + + if (left > 0) { + printf("%d extra bytes in the end of netlink message", + left); + } +} + + +static struct full_dynamic_vlan * +full_dynamic_vlan_init(struct hostapd_data *hapd) +{ + struct sockaddr_nl local; + struct full_dynamic_vlan *priv; + + priv = malloc(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); + return NULL; + } + + 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); + return NULL; + } + + if (eloop_register_read_sock(priv->s, vlan_event_receive, hapd, NULL)) + { + close(priv->s); + free(priv); + return NULL; + } + + return priv; +} + + +static void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv) +{ + if (priv == NULL) + return; + eloop_unregister_read_sock(priv->s); + close(priv->s); + free(priv); +} +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + + +int vlan_setup_encryption_dyn(struct hostapd_data *hapd, + struct hostapd_ssid *mssid, const char *dyn_vlan) +{ + int i; + + if (dyn_vlan == NULL) + return 0; + + /* Static WEP keys are set here; IEEE 802.1X and WPA uses their own + * functions for setting up dynamic broadcast keys. */ + for (i = 0; i < 4; i++) { + if (mssid->wep.key[i] && + hostapd_set_encryption(dyn_vlan, hapd, "WEP", NULL, + i, mssid->wep.key[i], + mssid->wep.len[i], + i == mssid->wep.idx)) { + printf("VLAN: Could not set WEP encryption for " + "dynamic VLAN.\n"); + return -1; + } + } + + return 0; +} + + +static int vlan_dynamic_add(struct hostapd_data *hapd, + struct hostapd_vlan *vlan) +{ + while (vlan) { + if (vlan->vlan_id != VLAN_ID_WILDCARD && + hostapd_if_add(hapd, HOSTAPD_IF_VLAN, vlan->ifname, NULL)) + { + if (errno != EEXIST) { + printf("Could not add VLAN iface: %s: %s\n", + vlan->ifname, strerror(errno)); + return -1; + } + } + + vlan = vlan->next; + } + + return 0; +} + + +static void vlan_dynamic_remove(struct hostapd_data *hapd, + struct hostapd_vlan *vlan) +{ + struct hostapd_vlan *next; + + while (vlan) { + next = vlan->next; + + if (vlan->vlan_id != VLAN_ID_WILDCARD && + hostapd_if_remove(hapd, HOSTAPD_IF_VLAN, vlan->ifname, + NULL)) { + printf("Could not remove VLAN iface: %s: %s\n", + vlan->ifname, strerror(errno)); + } +#ifdef CONFIG_FULL_DYNAMIC_VLAN + if (vlan->clean) + vlan_dellink(vlan->ifname, hapd); +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + + vlan = next; + } +} + + +int vlan_init(struct hostapd_data *hapd) +{ + if (vlan_dynamic_add(hapd, hapd->conf->vlan)) + return -1; + +#ifdef CONFIG_FULL_DYNAMIC_VLAN + hapd->full_dynamic_vlan = full_dynamic_vlan_init(hapd); +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + + return 0; +} + + +void vlan_deinit(struct hostapd_data *hapd) +{ + vlan_dynamic_remove(hapd, hapd->conf->vlan); + +#ifdef CONFIG_FULL_DYNAMIC_VLAN + full_dynamic_vlan_deinit(hapd->full_dynamic_vlan); +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ +} + + +int vlan_reconfig(struct hostapd_data *hapd, struct hostapd_config *oldconf, + struct hostapd_bss_config *oldbss) +{ + vlan_dynamic_remove(hapd, oldbss->vlan); + if (vlan_dynamic_add(hapd, hapd->conf->vlan)) + return -1; + + return 0; +} + + +struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, + struct hostapd_vlan *vlan, + int vlan_id) +{ + struct hostapd_vlan *n; + char *ifname, *pos; + + if (vlan == NULL || vlan_id <= 0 || vlan_id > MAX_VLAN_ID || + vlan->vlan_id != VLAN_ID_WILDCARD) + return NULL; + + ifname = strdup(vlan->ifname); + if (ifname == NULL) + return NULL; + pos = strchr(ifname, '#'); + if (pos == NULL) { + free(ifname); + return NULL; + } + *pos++ = '\0'; + + n = malloc(sizeof(*n)); + if (n == NULL) { + 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); + + if (hostapd_if_add(hapd, HOSTAPD_IF_VLAN, n->ifname, NULL)) { + free(n); + return NULL; + } + + n->next = hapd->conf->vlan; + hapd->conf->vlan = n; + + return n; +} + + +int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id) +{ + struct hostapd_vlan *vlan; + + if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID) + return 1; + + vlan = hapd->conf->vlan; + while (vlan) { + if (vlan->vlan_id == vlan_id && vlan->dynamic_vlan > 0) { + vlan->dynamic_vlan--; + break; + } + vlan = vlan->next; + } + + if (vlan == NULL) + return 1; + + if (vlan->dynamic_vlan == 0) + hostapd_if_remove(hapd, HOSTAPD_IF_VLAN, vlan->ifname, NULL); + + return 0; +} diff --git a/contrib/hostapd/vlan_init.h b/contrib/hostapd/vlan_init.h new file mode 100644 index 0000000..cf55ac2 --- /dev/null +++ b/contrib/hostapd/vlan_init.h @@ -0,0 +1,31 @@ +/* + * hostapd / VLAN initialization + * Copyright 2003, Instant802 Networks, Inc. + * Copyright 2005, Devicescape Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef VLAN_INIT_H +#define VLAN_INIT_H + +int vlan_init(struct hostapd_data *hapd); +void vlan_deinit(struct hostapd_data *hapd); +int vlan_reconfig(struct hostapd_data *hapd, struct hostapd_config *oldconf, + struct hostapd_bss_config *oldbss); +struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, + struct hostapd_vlan *vlan, + int vlan_id); +int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id); +int vlan_setup_encryption_dyn(struct hostapd_data *hapd, + struct hostapd_ssid *mssid, + const char *dyn_vlan); + +#endif /* VLAN_INIT_H */ diff --git a/contrib/hostapd/wme.c b/contrib/hostapd/wme.c new file mode 100644 index 0000000..8be740f --- /dev/null +++ b/contrib/hostapd/wme.c @@ -0,0 +1,260 @@ +/* + * hostapd / WMM (Wi-Fi Multimedia) + * 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 "ieee802_11.h" +#include "wme.h" +#include "sta_info.h" +#include "driver.h" + + +/* TODO: maintain separate sequence and fragment numbers for each AC + * TODO: IGMP snooping to track which multicasts to forward - and use QOS-DATA + * if only WME stations are receiving a certain group */ + + +static u8 wme_oui[3] = { 0x00, 0x50, 0xf2 }; + + +/* Add WME Parameter Element to Beacon and Probe Response frames. */ +u8 * hostapd_eid_wme(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; + struct wme_parameter_element *wme = + (struct wme_parameter_element *) (pos + 2); + int e; + + if (!hapd->conf->wme_enabled) + return eid; + eid[0] = WLAN_EID_VENDOR_SPECIFIC; + wme->oui[0] = 0x00; + wme->oui[1] = 0x50; + wme->oui[2] = 0xf2; + wme->oui_type = WME_OUI_TYPE; + wme->oui_subtype = WME_OUI_SUBTYPE_PARAMETER_ELEMENT; + wme->version = WME_VERSION; + wme->acInfo = hapd->parameter_set_count & 0xf; + + /* fill in a parameter set record for each AC */ + for (e = 0; e < 4; e++) { + struct wme_ac_parameter *ac = &wme->ac[e]; + struct hostapd_wme_ac_params *acp = + &hapd->iconf->wme_ac_params[e]; + + ac->aifsn = acp->aifs; + ac->acm = acp->admission_control_mandatory; + ac->aci = e; + ac->reserved = 0; + ac->eCWmin = acp->cwmin; + ac->eCWmax = acp->cwmax; + ac->txopLimit = host_to_le16(acp->txopLimit); + } + + pos = (u8 *) (wme + 1); + eid[1] = pos - eid - 2; /* element length */ + + return pos; +} + + +/* This function is called when a station sends an association request with + * WME info element. The function returns zero on success or non-zero on any + * error in WME element. eid does not include Element ID and Length octets. */ +int hostapd_eid_wme_valid(struct hostapd_data *hapd, u8 *eid, size_t len) +{ + struct wme_information_element *wme; + + 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); + 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 || + 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"); + return -1; + } + + return 0; +} + + +/* This function is called when a station sends an ACK frame for an AssocResp + * frame (status=success) and the matching AssocReq contained a WME element. + */ +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); + else + hostapd_sta_set_flags(hapd, sta->addr, 0, ~WLAN_STA_WME); + + return 0; +} + + +static void wme_send_action(struct hostapd_data *hapd, const u8 *addr, + const struct wme_tspec_info_element *tspec, + u8 action_code, u8 dialogue_token, u8 status_code) +{ + u8 buf[256]; + struct ieee80211_mgmt *m = (struct ieee80211_mgmt *) buf; + struct wme_tspec_info_element *t = + (struct wme_tspec_info_element *) + m->u.action.u.wme_action.variable; + int len; + + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "action response - reason %d", status_code); + 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; + 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)); + len = ((u8 *) (t + 1)) - buf; + + if (hostapd_send_mgmt_frame(hapd, m, len, 0) < 0) + perror("wme_send_action: send"); +} + + +/* given frame data payload size in bytes, and data_rate in bits per second + * returns time to complete frame exchange */ +/* FIX: should not use floating point types */ +static double wme_frame_exchange_time(int bytes, int data_rate, int encryption, + int cts_protection) +{ + /* TODO: account for MAC/PHY headers correctly */ + /* TODO: account for encryption headers */ + /* TODO: account for WDS headers */ + /* TODO: account for CTS protection */ + /* TODO: account for SIFS + ACK at minimum PHY rate */ + return (bytes + 400) * 8.0 / data_rate; +} + + +static void wme_setup_request(struct hostapd_data *hapd, + struct ieee80211_mgmt *mgmt, + struct wme_tspec_info_element *tspec, size_t len) +{ + /* FIX: should not use floating point types */ + double medium_time, pps; + + /* TODO: account for airtime and answer no to tspec setup requests + * when none left!! */ + + pps = (tspec->mean_data_rate / 8.0) / tspec->nominal_msdu_size; + medium_time = (tspec->surplus_bandwidth_allowance / 8) * pps * + wme_frame_exchange_time(tspec->nominal_msdu_size, + tspec->minimum_phy_rate, 0, 0); + tspec->medium_time = medium_time * 1000000.0 / 32.0; + + wme_send_action(hapd, mgmt->sa, tspec, WME_ACTION_CODE_SETUP_RESPONSE, + mgmt->u.action.u.wme_action.dialog_token, + WME_SETUP_RESPONSE_STATUS_ADMISSION_ACCEPTED); +} + + +void hostapd_wme_action(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, + size_t len) +{ + int action_code; + int left = len - IEEE80211_HDRLEN - 4; + u8 *pos = ((u8 *) mgmt) + IEEE80211_HDRLEN + 4; + struct ieee802_11_elems elems; + struct sta_info *sta = ap_get_sta(hapd, mgmt->sa); + + /* check that the request comes from a valid station */ + if (!sta || + (sta->flags & (WLAN_STA_ASSOC | WLAN_STA_WME)) != + (WLAN_STA_ASSOC | WLAN_STA_WME)) { + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "wme action received is not from associated wme" + " station"); + /* TODO: respond with action frame refused status code */ + return; + } + + /* extract the tspec info element */ + if (ieee802_11_parse_elems(hapd, pos, left, &elems, 1) == ParseFailed) + { + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "hostapd_wme_action - could not parse wme " + "action"); + /* TODO: respond with action frame invalid parameters status + * code */ + return; + } + + if (!elems.wme_tspec || + elems.wme_tspec_len != (sizeof(struct wme_tspec_info_element) - 2)) + { + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "hostapd_wme_action - missing or wrong length " + "tspec"); + /* TODO: respond with action frame invalid parameters status + * code */ + return; + } + + /* TODO: check the request is for an AC with ACM set, if not, refuse + * request */ + + action_code = mgmt->u.action.u.wme_action.action_code; + switch (action_code) { + case WME_ACTION_CODE_SETUP_REQUEST: + wme_setup_request(hapd, mgmt, (struct wme_tspec_info_element *) + elems.wme_tspec, len); + return; +#if 0 + /* TODO: needed for client implementation */ + case WME_ACTION_CODE_SETUP_RESPONSE: + wme_setup_request(hapd, mgmt, len); + return; + /* TODO: handle station teardown requests */ + case WME_ACTION_CODE_TEARDOWN: + wme_teardown(hapd, mgmt, len); + return; +#endif + } + + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "hostapd_wme_action - unknown action code %d", + action_code); +} diff --git a/contrib/hostapd/wme.h b/contrib/hostapd/wme.h new file mode 100644 index 0000000..8c20883 --- /dev/null +++ b/contrib/hostapd/wme.h @@ -0,0 +1,146 @@ +/* + * hostapd / WMM (Wi-Fi Multimedia) + * 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. + */ + +#ifndef WME_H +#define WME_H + +#ifdef __linux__ +#include <endian.h> +#endif /* __linux__ */ + +#if defined(__FreeBSD__) || defined(__NetBSD__) || defined(__DragonFly__) +#include <sys/types.h> +#include <sys/endian.h> +#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) +{ + return (tag1d << 11) | (contention_based << 7) | (direction << 5) | + (tag1d << 1); +} + + +struct wme_information_element { + /* required fields for WME version 1 */ + u8 oui[3]; + u8 oui_type; + u8 oui_subtype; + u8 version; + u8 acInfo; + +} __attribute__ ((packed)); + +struct wme_ac_parameter { +#if __BYTE_ORDER == __LITTLE_ENDIAN + /* byte 1 */ + u8 aifsn:4, + acm:1, + aci:2, + reserved:1; + + /* byte 2 */ + u8 eCWmin:4, + eCWmax:4; +#elif __BYTE_ORDER == __BIG_ENDIAN + /* byte 1 */ + u8 reserved:1, + aci:2, + acm:1, + aifsn:4; + + /* byte 2 */ + u8 eCWmax:4, + eCWmin:4; +#else +#error "Please fix <endian.h>" +#endif + + /* bytes 3 & 4 */ + u16 txopLimit; +} __attribute__ ((packed)); + +struct wme_parameter_element { + /* required fields for WME version 1 */ + u8 oui[3]; + u8 oui_type; + u8 oui_subtype; + u8 version; + u8 acInfo; + u8 reserved; + struct wme_ac_parameter ac[4]; + +} __attribute__ ((packed)); + +struct wme_tspec_info_element { + u8 eid; + u8 length; + u8 oui[3]; + u8 oui_type; + u8 oui_subtype; + u8 version; + u16 ts_info; + u16 nominal_msdu_size; + u16 maximum_msdu_size; + u32 minimum_service_interval; + u32 maximum_service_interval; + u32 inactivity_interval; + u32 start_time; + u32 minimum_data_rate; + u32 mean_data_rate; + u32 maximum_burst_size; + u32 minimum_phy_rate; + u32 peak_data_rate; + u32 delay_bound; + u16 surplus_bandwidth_allowance; + u16 medium_time; +} __attribute__ ((packed)); + + +/* Access Categories */ +enum { + WME_AC_BK = 1, + WME_AC_BE = 0, + WME_AC_VI = 2, + WME_AC_VO = 3 +}; + + +u8 * hostapd_eid_wme(struct hostapd_data *hapd, u8 *eid); +int hostapd_eid_wme_valid(struct hostapd_data *hapd, u8 *eid, size_t len); +int hostapd_wme_sta_config(struct hostapd_data *hapd, struct sta_info *sta); +void hostapd_wme_action(struct hostapd_data *hapd, struct ieee80211_mgmt *mgmt, + size_t len); + +#endif /* WME_H */ diff --git a/contrib/hostapd/wpa.c b/contrib/hostapd/wpa.c index 98eef20..1f66a3e 100644 --- a/contrib/hostapd/wpa.c +++ b/contrib/hostapd/wpa.c @@ -1,7 +1,6 @@ /* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / WPA Authenticator - * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * 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 @@ -13,40 +12,194 @@ * See README and COPYING for more details. */ -#include <stdlib.h> -#include <stdio.h> -#include <string.h> -#include <netinet/in.h> -#include <sys/time.h> -#include <time.h> -#include <unistd.h> +#include "includes.h" + +#ifndef CONFIG_NATIVE_WINDOWS #include "hostapd.h" #include "eapol_sm.h" #include "wpa.h" -#include "driver.h" +#include "wme.h" #include "sha1.h" #include "md5.h" #include "rc4.h" #include "aes_wrap.h" -#include "ieee802_1x.h" -#include "ieee802_11.h" +#include "crypto.h" #include "eloop.h" -#include "sta_info.h" -#include "l2_packet.h" -#include "accounting.h" -#include "hostap_common.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 sta_counted: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 GNoStations; + 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 hostapd_data *hapd); -static void pmksa_cache_free(struct hostapd_data *hapd); -static struct rsn_pmksa_cache * pmksa_cache_get(struct hostapd_data *hapd, - u8 *spa, u8 *pmkid); - +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. */ @@ -59,7 +212,6 @@ static const u32 dot11RSNAConfigPairwiseUpdateCount = 3; static const int dot11RSNAConfigPMKLifetime = 43200; static const int dot11RSNAConfigPMKReauthThreshold = 70; static const int dot11RSNAConfigSATimeout = 60; -static const int pmksa_cache_max_entries = 1024; static const int WPA_SELECTOR_LEN = 4; @@ -73,6 +225,9 @@ 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; @@ -86,12 +241,50 @@ 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 STAKey require encryption, otherwise, encryption is optional. + * 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) @@ -113,7 +306,7 @@ struct wpa_ie_hdr { u8 oui[3]; u8 oui_type; u16 version; -} __attribute__ ((packed)); +} STRUCT_PACKED; /* RSN IE version 1 @@ -129,16 +322,171 @@ struct wpa_ie_hdr { * 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; -} __attribute__ ((packed)); +} 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 hostapd_data *hapd, u8 *buf, size_t len) +static int wpa_write_wpa_ie(struct wpa_auth_config *conf, u8 *buf, size_t len) { struct wpa_ie_hdr *hdr; int num_suites; @@ -150,16 +498,17 @@ static int wpa_write_wpa_ie(struct hostapd_data *hapd, u8 *buf, size_t len) hdr->version = host_to_le16(WPA_VERSION); pos = (u8 *) (hdr + 1); - if (hapd->conf->wpa_group == WPA_CIPHER_CCMP) { + if (conf->wpa_group == WPA_CIPHER_CCMP) { memcpy(pos, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN); - } else if (hapd->conf->wpa_group == WPA_CIPHER_TKIP) { + } else if (conf->wpa_group == WPA_CIPHER_TKIP) { memcpy(pos, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN); - } else if (hapd->conf->wpa_group == WPA_CIPHER_WEP104) { + } else if (conf->wpa_group == WPA_CIPHER_WEP104) { memcpy(pos, WPA_CIPHER_SUITE_WEP104, WPA_SELECTOR_LEN); - } else if (hapd->conf->wpa_group == WPA_CIPHER_WEP40) { + } else if (conf->wpa_group == WPA_CIPHER_WEP40) { memcpy(pos, WPA_CIPHER_SUITE_WEP40, WPA_SELECTOR_LEN); } else { - printf("Invalid group cipher (%d).\n", hapd->conf->wpa_group); + wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).", + conf->wpa_group); return -1; } pos += WPA_SELECTOR_LEN; @@ -168,25 +517,25 @@ static int wpa_write_wpa_ie(struct hostapd_data *hapd, u8 *buf, size_t len) count = pos; pos += 2; - if (hapd->conf->wpa_pairwise & WPA_CIPHER_CCMP) { + if (conf->wpa_pairwise & WPA_CIPHER_CCMP) { memcpy(pos, WPA_CIPHER_SUITE_CCMP, WPA_SELECTOR_LEN); pos += WPA_SELECTOR_LEN; num_suites++; } - if (hapd->conf->wpa_pairwise & WPA_CIPHER_TKIP) { + if (conf->wpa_pairwise & WPA_CIPHER_TKIP) { memcpy(pos, WPA_CIPHER_SUITE_TKIP, WPA_SELECTOR_LEN); pos += WPA_SELECTOR_LEN; num_suites++; } - if (hapd->conf->wpa_pairwise & WPA_CIPHER_NONE) { + 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) { - printf("Invalid pairwise cipher (%d).\n", - hapd->conf->wpa_pairwise); + wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).", + conf->wpa_pairwise); return -1; } *count++ = num_suites & 0xff; @@ -196,12 +545,12 @@ static int wpa_write_wpa_ie(struct hostapd_data *hapd, u8 *buf, size_t len) count = pos; pos += 2; - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { + 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 (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { + 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; @@ -209,8 +558,8 @@ static int wpa_write_wpa_ie(struct hostapd_data *hapd, u8 *buf, size_t len) } if (num_suites == 0) { - printf("Invalid key management type (%d).\n", - hapd->conf->wpa_key_mgmt); + wpa_printf(MSG_DEBUG, "Invalid key management type (%d).", + conf->wpa_key_mgmt); return -1; } *count++ = num_suites & 0xff; @@ -224,11 +573,12 @@ static int wpa_write_wpa_ie(struct hostapd_data *hapd, u8 *buf, size_t len) } -static int wpa_write_rsn_ie(struct hostapd_data *hapd, u8 *buf, size_t len) +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; @@ -237,16 +587,17 @@ static int wpa_write_rsn_ie(struct hostapd_data *hapd, u8 *buf, size_t len) *pos++ = RSN_VERSION >> 8; pos = (u8 *) (hdr + 1); - if (hapd->conf->wpa_group == WPA_CIPHER_CCMP) { + if (conf->wpa_group == WPA_CIPHER_CCMP) { memcpy(pos, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN); - } else if (hapd->conf->wpa_group == WPA_CIPHER_TKIP) { + } else if (conf->wpa_group == WPA_CIPHER_TKIP) { memcpy(pos, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN); - } else if (hapd->conf->wpa_group == WPA_CIPHER_WEP104) { + } else if (conf->wpa_group == WPA_CIPHER_WEP104) { memcpy(pos, RSN_CIPHER_SUITE_WEP104, RSN_SELECTOR_LEN); - } else if (hapd->conf->wpa_group == WPA_CIPHER_WEP40) { + } else if (conf->wpa_group == WPA_CIPHER_WEP40) { memcpy(pos, RSN_CIPHER_SUITE_WEP40, RSN_SELECTOR_LEN); } else { - printf("Invalid group cipher (%d).\n", hapd->conf->wpa_group); + wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).", + conf->wpa_group); return -1; } pos += RSN_SELECTOR_LEN; @@ -255,25 +606,25 @@ static int wpa_write_rsn_ie(struct hostapd_data *hapd, u8 *buf, size_t len) count = pos; pos += 2; - if (hapd->conf->wpa_pairwise & WPA_CIPHER_CCMP) { + if (conf->wpa_pairwise & WPA_CIPHER_CCMP) { memcpy(pos, RSN_CIPHER_SUITE_CCMP, RSN_SELECTOR_LEN); pos += RSN_SELECTOR_LEN; num_suites++; } - if (hapd->conf->wpa_pairwise & WPA_CIPHER_TKIP) { + if (conf->wpa_pairwise & WPA_CIPHER_TKIP) { memcpy(pos, RSN_CIPHER_SUITE_TKIP, RSN_SELECTOR_LEN); pos += RSN_SELECTOR_LEN; num_suites++; } - if (hapd->conf->wpa_pairwise & WPA_CIPHER_NONE) { + 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) { - printf("Invalid pairwise cipher (%d).\n", - hapd->conf->wpa_pairwise); + wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).", + conf->wpa_pairwise); return -1; } *count++ = num_suites & 0xff; @@ -283,12 +634,12 @@ static int wpa_write_rsn_ie(struct hostapd_data *hapd, u8 *buf, size_t len) count = pos; pos += 2; - if (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { + 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 (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { + 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; @@ -296,16 +647,43 @@ static int wpa_write_rsn_ie(struct hostapd_data *hapd, u8 *buf, size_t len) } if (num_suites == 0) { - printf("Invalid key management type (%d).\n", - hapd->conf->wpa_key_mgmt); + 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 */ - *pos++ = hapd->conf->rsn_preauth ? BIT(0) : 0; - *pos++ = 0; + 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; @@ -313,443 +691,260 @@ static int wpa_write_rsn_ie(struct hostapd_data *hapd, u8 *buf, size_t len) } -static int wpa_gen_wpa_ie(struct hostapd_data *hapd) +static int wpa_gen_wpa_ie(struct wpa_authenticator *wpa_auth) { u8 *pos, buf[100]; int res; pos = buf; - if (hapd->conf->wpa & HOSTAPD_WPA_VERSION_WPA2) { - res = wpa_write_rsn_ie(hapd, pos, buf + sizeof(buf) - pos); + 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 (hapd->conf->wpa & HOSTAPD_WPA_VERSION_WPA) { - res = wpa_write_wpa_ie(hapd, pos, buf + sizeof(buf) - pos); + 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(hapd->wpa_ie); - hapd->wpa_ie = malloc(pos - buf); - if (hapd->wpa_ie == NULL) + free(wpa_auth->wpa_ie); + wpa_auth->wpa_ie = malloc(pos - buf); + if (wpa_auth->wpa_ie == NULL) return -1; - memcpy(hapd->wpa_ie, buf, pos - buf); - hapd->wpa_ie_len = pos - buf; + memcpy(wpa_auth->wpa_ie, buf, pos - buf); + wpa_auth->wpa_ie_len = pos - buf; return 0; } -static void wpa_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta) +static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth, + const u8 *addr) { - 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; + 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 hostapd_data *hapd = eloop_ctx; + struct wpa_authenticator *wpa_auth = eloop_ctx; - if (hapd->wpa_auth) { - if (hostapd_get_rand(hapd->wpa_auth->GMK, WPA_GMK_LEN)) { - printf("Failed to get random data for WPA " - "initialization.\n"); - } else { - hostapd_logger(hapd, NULL, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, - "GMK rekeyd"); - } + 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 (hapd->conf->wpa_gmk_rekey) { - eloop_register_timeout(hapd->conf->wpa_gmk_rekey, 0, - wpa_rekey_gmk, hapd, NULL); + 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 hostapd_data *hapd = eloop_ctx; + struct wpa_authenticator *wpa_auth = eloop_ctx; + struct wpa_group *group; - if (hapd->wpa_auth) { - hostapd_logger(hapd, NULL, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, "rekeying GTK"); - hapd->wpa_auth->GTKReKey = TRUE; + wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "rekeying GTK"); + for (group = wpa_auth->group; group; group = group->next) { + group->GTKReKey = TRUE; do { - hapd->wpa_auth->changed = FALSE; - wpa_group_sm_step(hapd); - } while (hapd->wpa_auth->changed); - } - if (hapd->conf->wpa_group_rekey) { - eloop_register_timeout(hapd->conf->wpa_group_rekey, 0, - wpa_rekey_gtk, hapd, NULL); - } -} - - -#ifdef CONFIG_RSN_PREAUTH - -static void rsn_preauth_receive(void *ctx, const u8 *src_addr, - const u8 *buf, size_t len) -{ - struct rsn_preauth_interface *piface = ctx; - struct hostapd_data *hapd = piface->hapd; - struct ieee802_1x_hdr *hdr; - struct sta_info *sta; - struct l2_ethhdr *ethhdr; - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN: receive pre-auth packet " - "from interface '%s'\n", 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); - 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)); - 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)); - return; - } - if (!sta && hdr->type == IEEE802_1X_TYPE_EAPOL_START) { - sta = (struct sta_info *) malloc(sizeof(struct sta_info)); - if (sta == NULL) - return; - memset(sta, 0, sizeof(*sta)); - memcpy(sta->addr, ethhdr->h_source, ETH_ALEN); - sta->flags = WLAN_STA_PREAUTH; - sta->next = hapd->sta_list; - sta->wpa = WPA_VERSION_WPA2; - hapd->sta_list = sta; - hapd->num_sta++; - ap_sta_hash_add(hapd, sta); - - ieee802_1x_new_station(hapd, sta); - if (sta->eapol_sm == NULL) { - ap_free_sta(hapd, sta); - sta = NULL; - } else { - sta->eapol_sm->radius_identifier = -1; - sta->eapol_sm->portValid = TRUE; - sta->eapol_sm->flags |= EAPOL_SM_PREAUTH; - } - } - if (sta == NULL) - return; - sta->preauth_iface = piface; - ieee802_1x_receive(hapd, ethhdr->h_source, (u8 *) (ethhdr + 1), - len - sizeof(*ethhdr)); -} - - -static int rsn_preauth_iface_add(struct hostapd_data *hapd, const char *ifname) -{ - struct rsn_preauth_interface *piface; - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN pre-auth interface '%s'\n", - ifname); - - piface = malloc(sizeof(*piface)); - if (piface == NULL) - return -1; - memset(piface, 0, sizeof(*piface)); - piface->hapd = hapd; - - piface->ifname = strdup(ifname); - if (piface->ifname == NULL) { - goto fail1; - } - - piface->l2 = l2_packet_init(piface->ifname, NULL, ETH_P_PREAUTH, - rsn_preauth_receive, piface, 1); - if (piface->l2 == NULL) { - printf("Failed to open register layer 2 access to " - "ETH_P_PREAUTH\n"); - goto fail2; - } - - piface->next = hapd->preauth_iface; - hapd->preauth_iface = piface; - return 0; - -fail2: - free(piface->ifname); -fail1: - free(piface); - return -1; -} - - -static void rsn_preauth_iface_deinit(struct hostapd_data *hapd) -{ - struct rsn_preauth_interface *piface, *prev; - - piface = hapd->preauth_iface; - hapd->preauth_iface = NULL; - while (piface) { - prev = piface; - piface = piface->next; - l2_packet_deinit(prev->l2); - free(prev->ifname); - free(prev); - } -} - - -static int rsn_preauth_iface_init(struct hostapd_data *hapd) -{ - char *tmp, *start, *end; - - if (hapd->conf->rsn_preauth_interfaces == NULL) - return 0; - - tmp = strdup(hapd->conf->rsn_preauth_interfaces); - if (tmp == NULL) - return -1; - start = tmp; - for (;;) { - while (*start == ' ') - start++; - if (*start == '\0') - break; - end = strchr(start, ' '); - if (end) - *end = '\0'; - - if (rsn_preauth_iface_add(hapd, start)) { - rsn_preauth_iface_deinit(hapd); - return -1; - } - - if (end) - start = end + 1; - else - break; - } - free(tmp); - return 0; -} - - -static void rsn_preauth_finished_cb(void *eloop_ctx, void *timeout_ctx) -{ - struct hostapd_data *hapd = eloop_ctx; - struct sta_info *sta = timeout_ctx; - wpa_printf(MSG_DEBUG, "RSN: Removing pre-authentication STA entry for " - MACSTR, MAC2STR(sta->addr)); - ap_free_sta(hapd, sta); -} - - -void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta, - int success) -{ - 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); - if (success && key) { - pmksa_cache_add(hapd, sta, key, dot11RSNAConfigPMKLifetime); - } - - /* - * Finish STA entry removal from timeout in order to avoid freeing - * STA data before the caller has finished processing. - */ - eloop_register_timeout(0, 0, rsn_preauth_finished_cb, hapd, sta); -} - - -void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta, - u8 *buf, size_t len) -{ - struct rsn_preauth_interface *piface; - struct l2_ethhdr *ethhdr; - - piface = hapd->preauth_iface; - while (piface) { - if (piface == sta->preauth_iface) - break; - piface = piface->next; + group->changed = FALSE; + wpa_group_sm_step(wpa_auth, group); + } while (group->changed); } - if (piface == NULL) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "RSN: Could not find " - "pre-authentication interface for " MACSTR "\n", - MAC2STR(sta->addr)); - return; + if (wpa_auth->conf.wpa_group_rekey) { + eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, + 0, wpa_rekey_gtk, wpa_auth, NULL); } - - ethhdr = 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); - ethhdr->h_proto = htons(ETH_P_PREAUTH); - 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"); - } - free(ethhdr); } -#else /* CONFIG_RSN_PREAUTH */ -static inline int rsn_preauth_iface_init(struct hostapd_data *hapd) +static int wpa_auth_pmksa_clear_cb(struct wpa_state_machine *sm, void *ctx) { + if (sm->pmksa == ctx) + sm->pmksa = NULL; return 0; } -static inline void rsn_preauth_iface_deinit(struct hostapd_data *hapd) -{ -} - -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) -{ -} -void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta, - u8 *buf, size_t len) +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); } -#endif /* CONFIG_RSN_PREAUTH */ - -int wpa_init(struct hostapd_data *hapd) +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]; - u8 buf[ETH_ALEN + 8]; - if (rsn_preauth_iface_init(hapd)) - return -1; + group = wpa_zalloc(sizeof(struct wpa_group)); + if (group == NULL) + return NULL; - if (hostapd_set_privacy(hapd, 1)) { - printf("Could not set PrivacyInvoked for interface %s\n", - hapd->conf->iface); - return -1; - } - - if (wpa_gen_wpa_ie(hapd)) { - printf("Could not generate WPA IE.\n"); - return -1; - } - - if (hostapd_set_generic_elem(hapd, hapd->wpa_ie, hapd->wpa_ie_len)) { - printf("Failed to configure WPA IE for the kernel driver.\n"); - return -1; - } + group->GTKAuthenticator = TRUE; + group->vlan_id = vlan_id; - hapd->wpa_auth = malloc(sizeof(struct wpa_authenticator)); - if (hapd->wpa_auth == NULL) - return -1; - memset(hapd->wpa_auth, 0, sizeof(struct wpa_authenticator)); - hapd->wpa_auth->GTKAuthenticator = TRUE; - switch (hapd->conf->wpa_group) { + switch (wpa_auth->conf.wpa_group) { case WPA_CIPHER_CCMP: - hapd->wpa_auth->GTK_len = 16; + group->GTK_len = 16; break; case WPA_CIPHER_TKIP: - hapd->wpa_auth->GTK_len = 32; + group->GTK_len = 32; break; case WPA_CIPHER_WEP104: - hapd->wpa_auth->GTK_len = 13; + group->GTK_len = 13; break; case WPA_CIPHER_WEP40: - hapd->wpa_auth->GTK_len = 5; + group->GTK_len = 5; break; } /* Counter = PRF-256(Random number, "Init Counter", * Local MAC Address || Time) */ - memcpy(buf, hapd->own_addr, ETH_ALEN); - hostapd_get_ntp_timestamp(buf + ETH_ALEN); + 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(hapd->wpa_auth->GMK, WPA_GMK_LEN)) { - printf("Failed to get random data for WPA initialization.\n"); - free(hapd->wpa_auth); - hapd->wpa_auth = NULL; - return -1; + 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), - hapd->wpa_auth->Counter, WPA_NONCE_LEN); + group->Counter, WPA_NONCE_LEN); - if (hapd->conf->wpa_gmk_rekey) { - eloop_register_timeout(hapd->conf->wpa_gmk_rekey, 0, - wpa_rekey_gmk, hapd, NULL); + 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; } - if (hapd->conf->wpa_group_rekey) { - eloop_register_timeout(hapd->conf->wpa_group_rekey, 0, - wpa_rekey_gtk, hapd, 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; } - hapd->wpa_auth->GInit = TRUE; - wpa_group_sm_step(hapd); - hapd->wpa_auth->GInit = FALSE; - wpa_group_sm_step(hapd); + 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; + } - return 0; + 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; } -void wpa_deinit(struct hostapd_data *hapd) +/** + * wpa_deinit - Deinitialize WPA authenticator + * @wpa_auth: Pointer to WPA authenticator data from wpa_init() + */ +void wpa_deinit(struct wpa_authenticator *wpa_auth) { - rsn_preauth_iface_deinit(hapd); + struct wpa_group *group, *prev; - eloop_cancel_timeout(wpa_rekey_gmk, hapd, NULL); - eloop_cancel_timeout(wpa_rekey_gtk, hapd, NULL); + eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL); + eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); - if (hostapd_set_privacy(hapd, 0)) { - printf("Could not disable PrivacyInvoked for interface %s\n", - hapd->conf->iface); - } + 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); - if (hostapd_set_generic_elem(hapd, (u8 *) "", 0)) { - printf("Could not remove generic information element from " - "interface %s\n", hapd->conf->iface); + group = wpa_auth->group; + while (group) { + prev = group; + group = group->next; + free(prev); } - free(hapd->wpa_ie); - hapd->wpa_ie = NULL; - free(hapd->wpa_auth); - hapd->wpa_auth = NULL; + 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; - pmksa_cache_free(hapd); + memcpy(&wpa_auth->conf, conf, sizeof(*conf)); + /* + * TODO: + * Disassociate stations if configuration changed + * Update WPA/RSN IE + */ + return 0; } @@ -792,6 +987,10 @@ static int rsn_selector_to_bitfield(u8 *s) 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; } @@ -807,263 +1006,20 @@ static int rsn_key_mgmt_to_bitfield(u8 *s) } -static void rsn_pmkid(const u8 *pmk, const u8 *aa, const u8 *spa, u8 *pmkid) -{ - char *title = "PMK Name"; - const u8 *addr[3]; - const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN }; - unsigned char hash[SHA1_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); -} - - -static void pmksa_cache_set_expiration(struct hostapd_data *hapd); - - -static void _pmksa_cache_free_entry(struct rsn_pmksa_cache *entry) -{ - if (entry == NULL) - return; - free(entry->identity); - ieee802_1x_free_radius_class(&entry->radius_class); - free(entry); -} - - -static void pmksa_cache_free_entry(struct hostapd_data *hapd, - struct rsn_pmksa_cache *entry) -{ - struct sta_info *sta; - struct rsn_pmksa_cache *pos, *prev; - hapd->pmksa_count--; - for (sta = hapd->sta_list; sta != NULL; sta = sta->next) { - if (sta->pmksa == entry) - sta->pmksa = NULL; - } - pos = hapd->pmkid[PMKID_HASH(entry->pmkid)]; - prev = NULL; - while (pos) { - if (pos == entry) { - if (prev != NULL) { - prev->hnext = pos->hnext; - } else { - hapd->pmkid[PMKID_HASH(entry->pmkid)] = - pos->hnext; - } - break; - } - prev = pos; - pos = pos->hnext; - } - - pos = hapd->pmksa; - prev = NULL; - while (pos) { - if (pos == entry) { - if (prev != NULL) - prev->next = pos->next; - else - hapd->pmksa = pos->next; - break; - } - prev = pos; - pos = pos->next; - } - _pmksa_cache_free_entry(entry); -} - - -static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) -{ - struct hostapd_data *hapd = eloop_ctx; - time_t now; - - time(&now); - while (hapd->pmksa && hapd->pmksa->expiration <= now) { - struct rsn_pmksa_cache *entry = hapd->pmksa; - hapd->pmksa = entry->next; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "RSN: expired PMKSA cache entry for " - MACSTR, MAC2STR(entry->spa)); - pmksa_cache_free_entry(hapd, entry); - } - - pmksa_cache_set_expiration(hapd); -} - - -static void pmksa_cache_set_expiration(struct hostapd_data *hapd) -{ - int sec; - eloop_cancel_timeout(pmksa_cache_expire, hapd, NULL); - if (hapd->pmksa == NULL) - return; - sec = hapd->pmksa->expiration - time(NULL); - if (sec < 0) - sec = 0; - eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, hapd, NULL); -} - - -static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache *entry, - struct eapol_state_machine *eapol) -{ - if (eapol == NULL) - return; - - if (eapol->identity) { - entry->identity = malloc(eapol->identity_len); - if (entry->identity) { - entry->identity_len = eapol->identity_len; - memcpy(entry->identity, eapol->identity, - eapol->identity_len); - } - } - - ieee802_1x_copy_radius_class(&entry->radius_class, - &eapol->radius_class); -} - - -static void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache *entry, - struct eapol_state_machine *eapol) -{ - if (entry == NULL || eapol == NULL) - return; - - if (entry->identity) { - free(eapol->identity); - eapol->identity = malloc(entry->identity_len); - if (eapol->identity) { - eapol->identity_len = entry->identity_len; - memcpy(eapol->identity, entry->identity, - entry->identity_len); - } - wpa_hexdump_ascii(MSG_DEBUG, "STA identity from PMKSA", - eapol->identity, eapol->identity_len); - } - - ieee802_1x_free_radius_class(&eapol->radius_class); - ieee802_1x_copy_radius_class(&eapol->radius_class, - &entry->radius_class); - if (eapol->radius_class.attr) { - wpa_printf(MSG_DEBUG, "Copied %lu Class attribute(s) from " - "PMKSA", (unsigned long) eapol->radius_class.count); - } -} - - -void pmksa_cache_add(struct hostapd_data *hapd, struct sta_info *sta, u8 *pmk, - int session_timeout) -{ - struct rsn_pmksa_cache *entry, *pos, *prev; - - if (sta->wpa != WPA_VERSION_WPA2) - return; - - entry = malloc(sizeof(*entry)); - if (entry == NULL) - return; - memset(entry, 0, sizeof(*entry)); - memcpy(entry->pmk, pmk, PMK_LEN); - rsn_pmkid(pmk, hapd->own_addr, sta->addr, entry->pmkid); - time(&entry->expiration); - if (session_timeout > 0) - entry->expiration += session_timeout; - else - entry->expiration += dot11RSNAConfigPMKLifetime; - entry->akmp = WPA_KEY_MGMT_IEEE8021X; - memcpy(entry->spa, sta->addr, ETH_ALEN); - pmksa_cache_from_eapol_data(entry, sta->eapol_sm); - - /* Replace an old entry for the same STA (if found) with the new entry - */ - pos = pmksa_cache_get(hapd, sta->addr, NULL); - if (pos) - pmksa_cache_free_entry(hapd, pos); - - if (hapd->pmksa_count >= pmksa_cache_max_entries && hapd->pmksa) { - /* Remove the oldest entry to make room for the new entry */ - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "RSN: removed the oldest PMKSA cache entry (for " - MACSTR ") to make room for new one", - MAC2STR(hapd->pmksa->spa)); - pmksa_cache_free_entry(hapd, hapd->pmksa); - } - - /* Add the new entry; order by expiration time */ - pos = hapd->pmksa; - prev = NULL; - while (pos) { - if (pos->expiration > entry->expiration) - break; - prev = pos; - pos = pos->next; - } - if (prev == NULL) { - entry->next = hapd->pmksa; - hapd->pmksa = entry; - } else { - entry->next = prev->next; - prev->next = entry; - } - entry->hnext = hapd->pmkid[PMKID_HASH(entry->pmkid)]; - hapd->pmkid[PMKID_HASH(entry->pmkid)] = entry; - - hapd->pmksa_count++; - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, - "added PMKSA cache entry"); - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { - hostapd_hexdump("RSN: added PMKID", entry->pmkid, PMKID_LEN); - } -} - - -static void pmksa_cache_free(struct hostapd_data *hapd) +static u8 * wpa_add_kde(u8 *pos, const u8 *kde, const u8 *data, + size_t data_len, const u8 *data2, size_t data2_len) { - struct rsn_pmksa_cache *entry, *prev; - int i; - struct sta_info *sta; - - entry = hapd->pmksa; - hapd->pmksa = NULL; - while (entry) { - prev = entry; - entry = entry->next; - _pmksa_cache_free_entry(prev); - } - eloop_cancel_timeout(pmksa_cache_expire, hapd, NULL); - for (i = 0; i < PMKID_HASH_SIZE; i++) - hapd->pmkid[i] = NULL; - for (sta = hapd->sta_list; sta; sta = sta->next) - sta->pmksa = NULL; -} - - -static struct rsn_pmksa_cache * pmksa_cache_get(struct hostapd_data *hapd, - u8 *spa, u8 *pmkid) -{ - struct rsn_pmksa_cache *entry; - - if (pmkid) - entry = hapd->pmkid[PMKID_HASH(pmkid)]; - else - entry = hapd->pmksa; - while (entry) { - if ((spa == NULL || memcmp(entry->spa, spa, ETH_ALEN) == 0) && - (pmkid == NULL || - memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)) - return entry; - entry = pmkid ? entry->hnext : entry->next; + *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 NULL; + return pos; } @@ -1074,6 +1030,7 @@ struct wpa_ie_data { int capabilities; size_t num_pmkid; u8 *pmkid; + int mgmt_group_cipher; }; @@ -1089,6 +1046,7 @@ static int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, 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; @@ -1168,6 +1126,11 @@ static int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, 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; @@ -1230,10 +1193,10 @@ static int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, data->num_pmkid = pos[0] | (pos[1] << 8); pos += 2; left -= 2; - if (left < data->num_pmkid * PMKID_LEN) { - printf("RSN: too short RSN IE for PMKIDs " - "(num=%lu, left=%d)\n", - (unsigned long) data->num_pmkid, left); + 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; @@ -1241,6 +1204,20 @@ static int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_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; } @@ -1249,12 +1226,25 @@ static int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, } -int wpa_validate_wpa_ie(struct hostapd_data *hapd, struct sta_info *sta, - const u8 *wpa_ie, size_t wpa_ie_len, int version) +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, i; + 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); @@ -1264,7 +1254,7 @@ int wpa_validate_wpa_ie(struct hostapd_data *hapd, struct sta_info *sta, 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(hapd->wpa_auth->dot11RSNAAuthenticationSuiteSelected, + memcpy(wpa_auth->dot11RSNAAuthenticationSuiteSelected, selector, RSN_SELECTOR_LEN); selector = RSN_CIPHER_SUITE_CCMP; @@ -1278,7 +1268,7 @@ int wpa_validate_wpa_ie(struct hostapd_data *hapd, struct sta_info *sta, selector = RSN_CIPHER_SUITE_WEP40; else if (data.pairwise_cipher & WPA_CIPHER_NONE) selector = RSN_CIPHER_SUITE_NONE; - memcpy(hapd->wpa_auth->dot11RSNAPairwiseCipherSelected, + memcpy(wpa_auth->dot11RSNAPairwiseCipherSelected, selector, RSN_SELECTOR_LEN); selector = RSN_CIPHER_SUITE_CCMP; @@ -1292,7 +1282,7 @@ int wpa_validate_wpa_ie(struct hostapd_data *hapd, struct sta_info *sta, selector = RSN_CIPHER_SUITE_WEP40; else if (data.group_cipher & WPA_CIPHER_NONE) selector = RSN_CIPHER_SUITE_NONE; - memcpy(hapd->wpa_auth->dot11RSNAGroupCipherSelected, + memcpy(wpa_auth->dot11RSNAGroupCipherSelected, selector, RSN_SELECTOR_LEN); } else { res = wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, &data); @@ -1302,7 +1292,7 @@ int wpa_validate_wpa_ie(struct hostapd_data *hapd, struct sta_info *sta, 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(hapd->wpa_auth->dot11RSNAAuthenticationSuiteSelected, + memcpy(wpa_auth->dot11RSNAAuthenticationSuiteSelected, selector, WPA_SELECTOR_LEN); selector = WPA_CIPHER_SUITE_TKIP; @@ -1316,7 +1306,7 @@ int wpa_validate_wpa_ie(struct hostapd_data *hapd, struct sta_info *sta, selector = WPA_CIPHER_SUITE_WEP40; else if (data.pairwise_cipher & WPA_CIPHER_NONE) selector = WPA_CIPHER_SUITE_NONE; - memcpy(hapd->wpa_auth->dot11RSNAPairwiseCipherSelected, + memcpy(wpa_auth->dot11RSNAPairwiseCipherSelected, selector, WPA_SELECTOR_LEN); selector = WPA_CIPHER_SUITE_TKIP; @@ -1330,99 +1320,291 @@ int wpa_validate_wpa_ie(struct hostapd_data *hapd, struct sta_info *sta, selector = WPA_CIPHER_SUITE_WEP40; else if (data.group_cipher & WPA_CIPHER_NONE) selector = WPA_CIPHER_SUITE_NONE; - memcpy(hapd->wpa_auth->dot11RSNAGroupCipherSelected, + memcpy(wpa_auth->dot11RSNAGroupCipherSelected, selector, WPA_SELECTOR_LEN); } if (res) { - printf("Failed to parse WPA/RSN IE from " MACSTR " (res=%d)\n", - MAC2STR(sta->addr), res); - hostapd_hexdump("WPA/RSN IE", wpa_ie, wpa_ie_len); + 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 != hapd->conf->wpa_group) { - printf("Invalid WPA group cipher (0x%x) from " MACSTR "\n", - data.group_cipher, MAC2STR(sta->addr)); + 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 & hapd->conf->wpa_key_mgmt; + key_mgmt = data.key_mgmt & wpa_auth->conf.wpa_key_mgmt; if (!key_mgmt) { - printf("Invalid WPA key mgmt (0x%x) from " MACSTR "\n", - data.key_mgmt, MAC2STR(sta->addr)); + 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) - sta->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X; + sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X; else - sta->wpa_key_mgmt = WPA_KEY_MGMT_PSK; + sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK; - ciphers = data.pairwise_cipher & hapd->conf->wpa_pairwise; + ciphers = data.pairwise_cipher & wpa_auth->conf.wpa_pairwise; if (!ciphers) { - printf("Invalid WPA pairwise cipher (0x%x) from " MACSTR "\n", - data.pairwise_cipher, MAC2STR(sta->addr)); + 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) - sta->pairwise = WPA_CIPHER_CCMP; + sm->pairwise = WPA_CIPHER_CCMP; else - sta->pairwise = WPA_CIPHER_TKIP; + sm->pairwise = WPA_CIPHER_TKIP; /* TODO: clear WPA/WPA2 state if STA changes from one to another */ if (wpa_ie[0] == WLAN_EID_RSN) - sta->wpa = WPA_VERSION_WPA2; + sm->wpa = WPA_VERSION_WPA2; else - sta->wpa = WPA_VERSION_WPA; + sm->wpa = WPA_VERSION_WPA; for (i = 0; i < data.num_pmkid; i++) { - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { - hostapd_hexdump("RSN IE: STA PMKID", - &data.pmkid[i * PMKID_LEN], PMKID_LEN); - } - sta->pmksa = pmksa_cache_get(hapd, sta->addr, - &data.pmkid[i * PMKID_LEN]); - if (sta->pmksa) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, - "PMKID found from PMKSA cache"); - if (hapd->wpa_auth) { - memcpy(hapd->wpa_auth->dot11RSNAPMKIDUsed, - sta->pmksa->pmkid, PMKID_LEN); - } + 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; } -void wpa_new_station(struct hostapd_data *hapd, struct sta_info *sta) +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; - if (!hapd->conf->wpa) + 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 (sta->wpa_sm) { - sm = sta->wpa_sm; + if (sm->started) { memset(sm->key_replay_counter, 0, WPA_REPLAY_COUNTER_LEN); sm->ReAuthenticationRequest = TRUE; wpa_sm_step(sm); return; } - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, "start authentication"); - sm = malloc(sizeof(struct wpa_state_machine)); - if (sm == NULL) - return; - memset(sm, 0, sizeof(struct wpa_state_machine)); - - sm->hapd = hapd; - sm->sta = sta; - sta->wpa_sm = sm; + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "start authentication"); + sm->started = 1; sm->Init = TRUE; wpa_sm_step(sm); @@ -1432,36 +1614,43 @@ void wpa_new_station(struct hostapd_data *hapd, struct sta_info *sta) } -void wpa_free_station(struct sta_info *sta) +static void wpa_free_sta_sm(struct wpa_state_machine *sm) { - struct wpa_state_machine *sm = sta->wpa_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->hapd->conf->wpa_strict_rekey && sm->has_GTK) { - hostapd_logger(sm->hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, "strict rekeying - force " - "GTK rekey since STA is leaving"); - eloop_cancel_timeout(wpa_rekey_gtk, sm->hapd, NULL); - eloop_register_timeout(0, 500000, wpa_rekey_gtk, sm->hapd, + 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->hapd, sta); - eloop_cancel_timeout(wpa_sm_call_step, sm->hapd, sta->wpa_sm); - eloop_cancel_timeout(rsn_preauth_finished_cb, sm->hapd, sta); - free(sm->last_rx_eapol_key); - free(sm); - sta->wpa_sm = 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 hostapd_data *hapd, - struct sta_info *sta) +static void wpa_request_new_ptk(struct wpa_state_machine *sm) { - struct wpa_state_machine *sm = sta->wpa_sm; - if (sm == NULL) return; @@ -1470,20 +1659,400 @@ static void wpa_request_new_ptk(struct hostapd_data *hapd, } -void wpa_receive(struct hostapd_data *hapd, struct sta_info *sta, +#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 wpa_state_machine *sm = sta->wpa_sm; struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; u16 key_info, key_data_length; - enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST } msg; + enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST, + SMK_M1, SMK_M3, SMK_ERROR } msg; char *msgtxt; + struct wpa_eapol_ie_parse kde; - if (!hapd->conf->wpa) - return; - - if (sm == NULL) + if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL) return; if (data_len < sizeof(*hdr) + sizeof(*key)) @@ -1505,7 +2074,19 @@ void wpa_receive(struct hostapd_data *hapd, struct sta_info *sta, /* FIX: verify that the EAPOL-Key frame was encrypted if pairwise keys * are set */ - if (key_info & WPA_KEY_INFO_REQUEST) { + 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)) { @@ -1520,13 +2101,12 @@ void wpa_receive(struct hostapd_data *hapd, struct sta_info *sta, } if (key_info & WPA_KEY_INFO_REQUEST) { - if (sta->req_replay_counter_used && - memcmp(key->replay_counter, sta->req_replay_counter, + if (sm->req_replay_counter_used && + memcmp(key->replay_counter, sm->req_replay_counter, WPA_REPLAY_COUNTER_LEN) <= 0) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_WARNING, - "received EAPOL-Key request with " - "replayed counter"); + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, + "received EAPOL-Key request with " + "replayed counter"); return; } } @@ -1535,18 +2115,13 @@ void wpa_receive(struct hostapd_data *hapd, struct sta_info *sta, (!sm->key_replay_counter_valid || memcmp(key->replay_counter, sm->key_replay_counter, WPA_REPLAY_COUNTER_LEN) != 0)) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key %s with unexpected replay " - "counter", msgtxt); - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { - hostapd_hexdump("expected replay counter", - sm->key_replay_counter, - WPA_REPLAY_COUNTER_LEN); - hostapd_hexdump("received replay counter", - key->replay_counter, - WPA_REPLAY_COUNTER_LEN); - } + 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; } @@ -1554,142 +2129,170 @@ void wpa_receive(struct hostapd_data *hapd, struct sta_info *sta, case PAIRWISE_2: if (sm->wpa_ptk_state != WPA_PTK_PTKSTART && sm->wpa_ptk_state != WPA_PTK_PTKCALCNEGOTIATING) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key msg 2/4 in invalid" - " state (%d) - dropped", - sm->wpa_ptk_state); + 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 (sta->wpa_ie == NULL || - sta->wpa_ie_len != key_data_length || - memcmp(sta->wpa_ie, key + 1, key_data_length) != 0) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "WPA IE from (Re)AssocReq did not match" - " with msg 2/4"); - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { - if (sta->wpa_ie) { - hostapd_hexdump("WPA IE in AssocReq", - sta->wpa_ie, - sta->wpa_ie_len); - } - hostapd_hexdump("WPA IE in msg 2/4", - (u8 *) (key + 1), - key_data_length); + 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(hapd, sta); + wpa_sta_disconnect(wpa_auth, sm->addr); return; } break; case PAIRWISE_4: if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING || !sm->PTK_valid) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key msg 4/4 in invalid" - " state (%d) - dropped", - sm->wpa_ptk_state); + 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) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key msg 2/2 in invalid" - " state (%d) - dropped", - sm->wpa_ptk_group_state); + 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; } - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, "received EAPOL-Key frame (%s)", - msgtxt); + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "received EAPOL-Key frame (%s)", msgtxt); if (key_info & WPA_KEY_INFO_ACK) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received invalid EAPOL-Key: Key Ack set"); + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received invalid EAPOL-Key: Key Ack set"); return; } if (!(key_info & WPA_KEY_INFO_MIC)) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received invalid EAPOL-Key: Key MIC not set"); + 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)) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key with invalid MIC"); + 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, sta->wpa_sm->hapd, - sta); + eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm); } if (key_info & WPA_KEY_INFO_REQUEST) { if (sm->MICVerified) { - sta->req_replay_counter_used = 1; - memcpy(sta->req_replay_counter, key->replay_counter, + sm->req_replay_counter_used = 1; + memcpy(sm->req_replay_counter, key->replay_counter, WPA_REPLAY_COUNTER_LEN); } else { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key request with " - "invalid MIC"); + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key request with " + "invalid MIC"); return; } - if (key_info & WPA_KEY_INFO_ERROR) { + /* + * 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 */ - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key Error Request " - "(STA detected Michael MIC failure)"); - ieee80211_michael_mic_failure(hapd, sta->addr, 0); - sta->dot11RSNAStatsTKIPRemoteMICFailures++; - hapd->wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++; + 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(hapd, sta); + wpa_request_new_ptk(sm); } else if (key_info & WPA_KEY_INFO_KEY_TYPE) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key Request for new " - "4-Way Handshake"); - wpa_request_new_ptk(hapd, sta); + 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 { - /* TODO: this could also be a request for STAKey - * if Key Data fields contains peer MAC address KDE. - * STAKey request should have 0xdd <len> 00-0F-AC:2 in - * the beginning of Key Data */ - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "received EAPOL-Key Request for GTK " - "rekeying"); - wpa_request_new_ptk(hapd, sta); - eloop_cancel_timeout(wpa_rekey_gtk, hapd, NULL); - wpa_rekey_gtk(hapd, NULL); + 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) @@ -1705,8 +2308,7 @@ void wpa_receive(struct hostapd_data *hapd, struct sta_info *sta, } -static void wpa_pmk_to_ptk(struct hostapd_data *hapd, const u8 *pmk, - const u8 *addr1, const u8 *addr2, +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) { @@ -1737,15 +2339,13 @@ static void wpa_pmk_to_ptk(struct hostapd_data *hapd, const u8 *pmk, sha1_prf(pmk, WPA_PMK_LEN, "Pairwise key expansion", data, sizeof(data), ptk, ptk_len); - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { - hostapd_hexdump("PMK", pmk, WPA_PMK_LEN); - hostapd_hexdump("PTK", 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(struct hostapd_data *hapd, u8 *gmk, - u8 *addr, u8 *gnonce, u8 *gtk, size_t gtk_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]; @@ -1756,25 +2356,19 @@ static void wpa_gmk_to_gtk(struct hostapd_data *hapd, u8 *gmk, sha1_prf(gmk, WPA_GMK_LEN, "Group key expansion", data, sizeof(data), gtk, gtk_len); - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { - hostapd_hexdump("GMK", gmk, WPA_GMK_LEN); - hostapd_hexdump("GTK", 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 hostapd_data *hapd = eloop_ctx; - struct sta_info *sta = timeout_ctx; - - if (!sta->wpa_sm || !(sta->flags & WLAN_STA_ASSOC)) - return; + struct wpa_authenticator *wpa_auth = eloop_ctx; + struct wpa_state_machine *sm = timeout_ctx; - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, "EAPOL-Key timeout"); - sta->wpa_sm->TimeoutEvt = TRUE; - wpa_sm_step(sta->wpa_sm); + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "EAPOL-Key timeout"); + sm->TimeoutEvt = TRUE; + wpa_sm_step(sm); } @@ -1798,88 +2392,68 @@ static int wpa_calc_eapol_key_mic(int ver, u8 *key, u8 *data, size_t len, } -static void wpa_send_eapol(struct hostapd_data *hapd, struct sta_info *sta, - int secure, int mic, int ack, int install, - int pairwise, u8 *key_rsc, u8 *nonce, - u8 *ie, size_t ie_len, u8 *gtk, size_t gtk_len, - int keyidx) +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 wpa_state_machine *sm = sta->wpa_sm; struct ieee802_1x_hdr *hdr; struct wpa_eapol_key *key; size_t len; - int key_info, alg; - int timeout_ms; + int alg; int key_data_len, pad_len = 0; u8 *buf, *pos; - - if (sm == NULL) - return; + int version, pairwise; len = sizeof(struct ieee802_1x_hdr) + sizeof(struct wpa_eapol_key); - if (sta->wpa == WPA_VERSION_WPA2) { - key_data_len = ie_len + gtk_len; - if (gtk_len) - key_data_len += 2 + RSN_SELECTOR_LEN + 2; - } else { - if (pairwise) { - /* WPA does not include GTK in 4-Way Handshake */ - gtk = NULL; - gtk_len = 0; - - /* key_rsc is for group key, so mask it out in case of - * WPA Pairwise key negotiation. */ - key_rsc = NULL; - } - key_data_len = ie_len + gtk_len; - } + 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; - if (sta->pairwise == WPA_CIPHER_CCMP) { - key_info = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; - if (gtk) { - pad_len = key_data_len % 8; - if (pad_len) - pad_len = 8 - pad_len; - key_data_len += pad_len + 8; - } - } else { - key_info = 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 = malloc(len); + hdr = wpa_zalloc(len); if (hdr == NULL) return; - memset(hdr, 0, len); - hdr->version = hapd->conf->eapol_version; + 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 = sta->wpa == WPA_VERSION_WPA2 ? + key->type = sm->wpa == WPA_VERSION_WPA2 ? EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; - if (secure) - key_info |= WPA_KEY_INFO_SECURE; - if (mic) - key_info |= WPA_KEY_INFO_MIC; - if (ack) - key_info |= WPA_KEY_INFO_ACK; - if (install) - key_info |= WPA_KEY_INFO_INSTALL; - if (pairwise) - key_info |= WPA_KEY_INFO_KEY_TYPE; - if (gtk && sta->wpa == WPA_VERSION_WPA2) + key_info |= version; + if (encr && sm->wpa == WPA_VERSION_WPA2) key_info |= WPA_KEY_INFO_ENCR_KEY_DATA; - if (sta->wpa != WPA_VERSION_WPA2) { - if (pairwise) - keyidx = 0; + if (sm->wpa != WPA_VERSION_WPA2) key_info |= keyidx << WPA_KEY_INFO_KEY_INDEX_SHIFT; - } key->key_info = htons(key_info); - alg = pairwise ? sta->pairwise : hapd->conf->wpa_group; + alg = pairwise ? sm->pairwise : wpa_auth->conf.wpa_group; switch (alg) { case WPA_CIPHER_CCMP: key->key_length = htons(16); @@ -1894,7 +2468,10 @@ static void wpa_send_eapol(struct hostapd_data *hapd, struct sta_info *sta, 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); @@ -1906,48 +2483,33 @@ static void wpa_send_eapol(struct hostapd_data *hapd, struct sta_info *sta, if (key_rsc) memcpy(key->key_rsc, key_rsc, WPA_KEY_RSC_LEN); - if (ie && !gtk) { - memcpy(key + 1, ie, ie_len); - key->key_data_length = htons(ie_len); - } else if (gtk) { - buf = malloc(key_data_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; } - memset(buf, 0, key_data_len); pos = buf; - if (ie) { - memcpy(pos, ie, ie_len); - pos += ie_len; - } - if (sta->wpa == WPA_VERSION_WPA2) { - *pos++ = WLAN_EID_GENERIC; - *pos++ = RSN_SELECTOR_LEN + 2 + gtk_len; - memcpy(pos, RSN_KEY_DATA_GROUPKEY, RSN_SELECTOR_LEN); - pos += RSN_SELECTOR_LEN; - *pos++ = keyidx & 0x03; - *pos++ = 0; - } - memcpy(pos, gtk, gtk_len); - pos += gtk_len; + memcpy(pos, kde, kde_len); + pos += kde_len; + if (pad_len) *pos++ = 0xdd; - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MINIMAL)) { - hostapd_hexdump("Plaintext EAPOL-Key Key Data", - buf, key_data_len); - } - if (key_info & WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { + 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, - hapd->wpa_auth->Counter + WPA_NONCE_LEN - 16, - 16); - inc_byte_array(hapd->wpa_auth->Counter, WPA_NONCE_LEN); + 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); @@ -1957,28 +2519,46 @@ static void wpa_send_eapol(struct hostapd_data *hapd, struct sta_info *sta, free(buf); } - if (mic) { + if (key_info & WPA_KEY_INFO_MIC) { if (!sm->PTK_valid) { - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, "PTK not valid " - "when sending EAPOL-Key frame"); + 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(key_info & WPA_KEY_INFO_TYPE_MASK, + wpa_calc_eapol_key_mic(version, sm->PTK.mic_key, (u8 *) hdr, len, key->key_mic); } - if (sta->eapol_sm) - sta->eapol_sm->dot1xAuthEapolFramesTx++; - hostapd_send_eapol(hapd, sta->addr, (u8 *) hdr, len, sm->pairwise_set); + 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, hapd, sta); + wpa_send_eapol_timeout, wpa_auth, sm); } @@ -1987,7 +2567,7 @@ 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 type, ret = 0; + int ret = 0; u8 mic[16]; if (data_len < sizeof(*hdr) + sizeof(*key)) @@ -1996,7 +2576,6 @@ static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len) hdr = (struct ieee802_1x_hdr *) data; key = (struct wpa_eapol_key *) (hdr + 1); key_info = ntohs(key->key_info); - type = key_info & WPA_KEY_INFO_TYPE_MASK; 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, @@ -2008,16 +2587,23 @@ static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len) } -void wpa_sm_event(struct hostapd_data *hapd, struct sta_info *sta, - wpa_event event) +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) { - struct wpa_state_machine *sm = sta->wpa_sm; - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, - "event %d notification", 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: @@ -2035,11 +2621,8 @@ void wpa_sm_event(struct hostapd_data *hapd, struct sta_info *sta, sm->PTK_valid = FALSE; memset(&sm->PTK, 0, sizeof(sm->PTK)); - if (event != WPA_REAUTH_EAPOL) { - sm->pairwise_set = FALSE; - hostapd_set_encryption(sm->hapd, "none", sm->sta->addr, 0, - (u8 *) "", 0); - } + if (event != WPA_REAUTH_EAPOL) + wpa_remove_ptk(sm); wpa_sm_step(sm); } @@ -2061,31 +2644,9 @@ static const char * wpa_alg_txt(int alg) } -/* Definitions for clarifying state machine implementation */ -#define SM_STATE(machine, state) \ -static void sm_ ## machine ## _ ## state ## _Enter(struct wpa_state_machine \ -*sm) - -#define SM_ENTRY(machine, _state, _data) \ -sm->changed = TRUE; \ -sm->_data ## _ ## state = machine ## _ ## _state; \ -if (sm->hapd->conf->debug >= HOSTAPD_DEBUG_MINIMAL) \ - printf("WPA: " MACSTR " " #machine " entering state " #_state \ - "\n", MAC2STR(sm->sta->addr)); - -#define SM_ENTER(machine, state) sm_ ## machine ## _ ## state ## _Enter(sm) - -#define SM_STEP(machine) \ -static void sm_ ## machine ## _Step(struct wpa_state_machine *sm) - -#define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm) - - SM_STATE(WPA_PTK, INITIALIZE) { - struct hostapd_data *hapd = sm->hapd; - - SM_ENTRY(WPA_PTK, INITIALIZE, wpa_ptk); + 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. */ @@ -2094,64 +2655,75 @@ SM_STATE(WPA_PTK, INITIALIZE) sm->keycount = 0; if (sm->GUpdateStationKeys) - hapd->wpa_auth->GKeyDoneStations--; + sm->group->GKeyDoneStations--; sm->GUpdateStationKeys = FALSE; - if (sm->sta->wpa == WPA_VERSION_WPA) + 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; } - ieee802_1x_notify_port_enabled(sm->sta->eapol_sm, 0); - hostapd_set_encryption(sm->hapd, "none", sm->sta->addr, 0, (u8 *) "", - 0); - sm->pairwise_set = FALSE; - sm->PTK_valid = FALSE; - memset(&sm->PTK, 0, sizeof(sm->PTK)); - ieee802_1x_notify_port_valid(sm->sta->eapol_sm, 0); + 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->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) - ieee802_1x_set_sta_authorized(sm->hapd, sm->sta, 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(WPA_PTK, DISCONNECT, wpa_ptk); + SM_ENTRY_MA(WPA_PTK, DISCONNECT, wpa_ptk); sm->Disconnect = FALSE; - wpa_sta_disconnect(sm->hapd, sm->sta); + wpa_sta_disconnect(sm->wpa_auth, sm->addr); } SM_STATE(WPA_PTK, DISCONNECTED) { - SM_ENTRY(WPA_PTK, DISCONNECTED, wpa_ptk); - sm->hapd->wpa_auth->GNoStations--; + SM_ENTRY_MA(WPA_PTK, DISCONNECTED, wpa_ptk); + if (sm->sta_counted) { + sm->group->GNoStations--; + sm->sta_counted = 0; + } else { + wpa_printf(MSG_DEBUG, "WPA: WPA_PTK::DISCONNECTED - did not " + "decrease GNoStations (STA " MACSTR ")", + MAC2STR(sm->addr)); + } sm->DeauthenticationRequest = FALSE; } SM_STATE(WPA_PTK, AUTHENTICATION) { - SM_ENTRY(WPA_PTK, AUTHENTICATION, wpa_ptk); - sm->hapd->wpa_auth->GNoStations++; + SM_ENTRY_MA(WPA_PTK, AUTHENTICATION, wpa_ptk); + if (!sm->sta_counted) { + sm->group->GNoStations++; + sm->sta_counted = 1; + } else { + wpa_printf(MSG_DEBUG, "WPA: WPA_PTK::DISCONNECTED - did not " + "increase GNoStations (STA " MACSTR ")", + MAC2STR(sm->addr)); + } memset(&sm->PTK, 0, sizeof(sm->PTK)); sm->PTK_valid = FALSE; - if (sm->sta->eapol_sm) { - sm->sta->eapol_sm->portControl = Auto; - sm->sta->eapol_sm->portEnabled = TRUE; - } + 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(WPA_PTK, AUTHENTICATION2, wpa_ptk); - memcpy(sm->ANonce, sm->hapd->wpa_auth->Counter, WPA_NONCE_LEN); - inc_byte_array(sm->hapd->wpa_auth->Counter, WPA_NONCE_LEN); + 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/D9.0 does not clear TimeoutCtr here, but this is more + /* 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. */ @@ -2161,24 +2733,22 @@ SM_STATE(WPA_PTK, AUTHENTICATION2) SM_STATE(WPA_PTK, INITPMK) { - u8 *key; - size_t len; - SM_ENTRY(WPA_PTK, INITPMK, wpa_ptk); - if (sm->sta->pmksa) { + 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->sta->pmksa->pmk, WPA_PMK_LEN); - pmksa_cache_to_eapol_data(sm->sta->pmksa, sm->sta->eapol_sm); - } else if ((key = ieee802_1x_get_key_crypt(sm->sta->eapol_sm, &len))) { + 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); - if (len > WPA_PMK_LEN) - len = WPA_PMK_LEN; - memcpy(sm->PMK, key, len); } else { wpa_printf(MSG_DEBUG, "WPA: Could not get PMK"); } - sm->sta->req_replay_counter_used = 0; - /* IEEE 802.11i/D9.0 does not set keyRun to FALSE, but not doing this + + 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 @@ -2186,45 +2756,57 @@ SM_STATE(WPA_PTK, INITPMK) * 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. */ - if (sm->sta->eapol_sm) - sm->sta->eapol_sm->keyRun = FALSE; + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyRun, 0); } SM_STATE(WPA_PTK, INITPSK) { const u8 *psk; - SM_ENTRY(WPA_PTK, INITPSK, wpa_ptk); - psk = hostapd_get_psk(sm->hapd->conf, sm->sta->addr, NULL); + 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->sta->req_replay_counter_used = 0; + sm->req_replay_counter_used = 0; } SM_STATE(WPA_PTK, PTKSTART) { - u8 *pmkid = NULL; + u8 buf[2 + RSN_SELECTOR_LEN + PMKID_LEN], *pmkid = NULL; size_t pmkid_len = 0; - SM_ENTRY(WPA_PTK, PTKSTART, wpa_ptk); + SM_ENTRY_MA(WPA_PTK, PTKSTART, wpa_ptk); sm->PTKRequest = FALSE; sm->TimeoutEvt = FALSE; - hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, - "sending 1/4 msg of 4-Way Handshake"); - if (sm->sta->pmksa && - (pmkid = malloc(2 + RSN_SELECTOR_LEN + PMKID_LEN))) { + 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); - memcpy(&pmkid[2 + RSN_SELECTOR_LEN], sm->sta->pmksa->pmkid, - PMKID_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->hapd, sm->sta, 0, 0, 1, 0, 1, NULL, sm->ANonce, - pmkid, pmkid_len, NULL, 0, 0); - free(pmkid); + 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++; } @@ -2235,23 +2817,22 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) int ok = 0; const u8 *pmk = NULL; - SM_ENTRY(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk); + 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->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) { - pmk = hostapd_get_psk(sm->hapd->conf, sm->sta->addr, - pmk); + 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(sm->hapd, pmk, sm->hapd->own_addr, - sm->sta->addr, sm->ANonce, sm->SNonce, + 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, @@ -2260,20 +2841,19 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) break; } - if (sm->sta->wpa_key_mgmt != WPA_KEY_MGMT_PSK) + if (sm->wpa_key_mgmt != WPA_KEY_MGMT_PSK) break; } if (!ok) { - hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, "invalid MIC in msg 2/4 " - "of 4-Way Handshake"); + 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->hapd, sm->sta); + eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); - if (sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) { + 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. */ @@ -2289,99 +2869,199 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) SM_STATE(WPA_PTK, PTKCALCNEGOTIATING2) { - SM_ENTRY(WPA_PTK, PTKCALCNEGOTIATING2, wpa_ptk); + 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]; - struct wpa_authenticator *gsm = sm->hapd->wpa_auth; + 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; + int wpa_ie_len, secure, keyidx, encr = 0; - SM_ENTRY(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk); + 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); - hostapd_get_seqnum(sm->hapd, NULL, gsm->GN, rsc); - wpa_ie = sm->hapd->wpa_ie; - wpa_ie_len = sm->hapd->wpa_ie_len; - if (sm->sta->wpa == WPA_VERSION_WPA && - (sm->hapd->conf->wpa & HOSTAPD_WPA_VERSION_WPA2) && + 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; } - hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, - "sending 3/4 msg of 4-Way Handshake"); - wpa_send_eapol(sm->hapd, sm->sta, - sm->sta->wpa == WPA_VERSION_WPA2 ? 1 : 0, - 1, 1, 1, 1, rsc, sm->ANonce, - wpa_ie, wpa_ie_len, - gsm->GTK[gsm->GN - 1], gsm->GTK_len, gsm->GN); + 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(WPA_PTK, PTKINITDONE, wpa_ptk); + SM_ENTRY_MA(WPA_PTK, PTKINITDONE, wpa_ptk); sm->EAPOLKeyReceived = FALSE; if (sm->Pair) { char *alg; int klen; - if (sm->sta->pairwise == WPA_CIPHER_TKIP) { + if (sm->pairwise == WPA_CIPHER_TKIP) { alg = "TKIP"; klen = 32; } else { alg = "CCMP"; klen = 16; } - if (hostapd_set_encryption(sm->hapd, alg, sm->sta->addr, 0, - sm->PTK.tk1, klen)) { - wpa_sta_disconnect(sm->hapd, sm->sta); + 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->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) - ieee802_1x_set_sta_authorized(sm->hapd, sm->sta, 1); + 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) { - ieee802_1x_notify_port_valid(sm->sta->eapol_sm, 1); + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_portValid, 1); } } else { - ieee802_1x_notify_port_valid(sm->sta->eapol_sm, 1); - } - if (sm->sta->eapol_sm) { - sm->sta->eapol_sm->keyAvailable = FALSE; - sm->sta->eapol_sm->keyDone = TRUE; + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, + 1); } - if (sm->sta->wpa == WPA_VERSION_WPA) + 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; - hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, "pairwise key handshake completed " - "(%s)", - sm->sta->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); - if (sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) - accounting_sta_start(sm->hapd, sm->sta); + 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->hapd->wpa_auth; + struct wpa_authenticator *wpa_auth = sm->wpa_auth; if (sm->Init) SM_ENTER(WPA_PTK, INITIALIZE); @@ -2409,15 +3089,17 @@ SM_STEP(WPA_PTK) SM_ENTER(WPA_PTK, AUTHENTICATION2); break; case WPA_PTK_AUTHENTICATION2: - if ((sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_IEEE8021X) && - sm->sta->eapol_sm && sm->sta->eapol_sm->keyRun) + 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->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) + 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 (sm->sta->eapol_sm && sm->sta->eapol_sm->keyAvailable) + if (wpa_auth_get_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_keyAvailable) > 0) SM_ENTER(WPA_PTK, PTKSTART); else { wpa_auth->dot11RSNA4WayHandshakeFailures++; @@ -2425,13 +3107,11 @@ SM_STEP(WPA_PTK) } break; case WPA_PTK_INITPSK: - if (hostapd_get_psk(sm->hapd->conf, sm->sta->addr, NULL)) + if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, NULL)) SM_ENTER(WPA_PTK, PTKSTART); else { - hostapd_logger(sm->hapd, sm->sta->addr, - HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, - "no PSK configured for the STA"); + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "no PSK configured for the STA"); wpa_auth->dot11RSNA4WayHandshakeFailures++; SM_ENTER(WPA_PTK, DISCONNECT); } @@ -2440,7 +3120,8 @@ SM_STEP(WPA_PTK) if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && sm->EAPOLKeyPairwise) SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); - else if (sm->TimeoutCtr > dot11RSNAConfigPairwiseUpdateCount) { + else if (sm->TimeoutCtr > + (int) dot11RSNAConfigPairwiseUpdateCount) { wpa_auth->dot11RSNA4WayHandshakeFailures++; SM_ENTER(WPA_PTK, DISCONNECT); } else if (sm->TimeoutEvt) @@ -2462,7 +3143,8 @@ SM_STEP(WPA_PTK) if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && sm->EAPOLKeyPairwise && sm->MICVerified) SM_ENTER(WPA_PTK, PTKINITDONE); - else if (sm->TimeoutCtr > dot11RSNAConfigPairwiseUpdateCount) { + else if (sm->TimeoutCtr > + (int) dot11RSNAConfigPairwiseUpdateCount) { wpa_auth->dot11RSNA4WayHandshakeFailures++; SM_ENTER(WPA_PTK, DISCONNECT); } else if (sm->TimeoutEvt) @@ -2476,7 +3158,7 @@ SM_STEP(WPA_PTK) SM_STATE(WPA_PTK_GROUP, IDLE) { - SM_ENTRY(WPA_PTK_GROUP, IDLE, wpa_ptk_group); + 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. */ @@ -2489,46 +3171,69 @@ SM_STATE(WPA_PTK_GROUP, IDLE) SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) { u8 rsc[WPA_KEY_RSC_LEN]; - struct wpa_authenticator *gsm = sm->hapd->wpa_auth; + struct wpa_group *gsm = sm->group; + u8 *kde, *pos, hdr[2]; + size_t kde_len; - SM_ENTRY(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group); - if (sm->sta->wpa == WPA_VERSION_WPA) + 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) - hostapd_get_seqnum(sm->hapd, NULL, gsm->GN, rsc); - hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_DEBUG, - "sending 1/2 msg of Group Key Handshake"); - wpa_send_eapol(sm->hapd, sm->sta, 1, 1, 1, !sm->Pair, 0, rsc, - gsm->GNonce, NULL, 0, - gsm->GTK[gsm->GN - 1], gsm->GTK_len, gsm->GN); + 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(WPA_PTK_GROUP, REKEYESTABLISHED, wpa_ptk_group); + SM_ENTRY_MA(WPA_PTK_GROUP, REKEYESTABLISHED, wpa_ptk_group); sm->EAPOLKeyReceived = FALSE; sm->GUpdateStationKeys = FALSE; - sm->hapd->wpa_auth->GKeyDoneStations--; + sm->group->GKeyDoneStations--; sm->GTimeoutCtr = 0; /* FIX: MLME.SetProtection.Request(TA, Tx_Rx) */ - hostapd_logger(sm->hapd, sm->sta->addr, HOSTAPD_MODULE_WPA, - HOSTAPD_LEVEL_INFO, "group key handshake completed " - "(%s)", - sm->sta->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); + 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(WPA_PTK_GROUP, KEYERROR, wpa_ptk_group); - sm->hapd->wpa_auth->GKeyDoneStations--; + SM_ENTRY_MA(WPA_PTK_GROUP, KEYERROR, wpa_ptk_group); + sm->group->GKeyDoneStations--; sm->GUpdateStationKeys = FALSE; sm->Disconnect = TRUE; } @@ -2541,14 +3246,15 @@ SM_STEP(WPA_PTK_GROUP) else switch (sm->wpa_ptk_group_state) { case WPA_PTK_GROUP_IDLE: if (sm->GUpdateStationKeys || - (sm->sta->wpa == WPA_VERSION_WPA && sm->PInitAKeys)) + (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 > dot11RSNAConfigGroupUpdateCount) + else if (sm->GTimeoutCtr > + (int) dot11RSNAConfigGroupUpdateCount) SM_ENTER(WPA_PTK_GROUP, KEYERROR); else if (sm->TimeoutEvt) SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING); @@ -2563,159 +3269,185 @@ SM_STEP(WPA_PTK_GROUP) } -static void wpa_group_gtk_init(struct hostapd_data *hapd) +static void wpa_gtk_update(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) { - struct wpa_authenticator *sm = hapd->wpa_auth; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "WPA: group state machine " - "entering state GTK_INIT\n"); - sm->changed = FALSE; /* GInit is not cleared here; avoid loop */ - sm->wpa_group_state = WPA_GROUP_GTK_INIT; + /* 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(sm->GTK, 0, sizeof(sm->GTK)); - sm->GN = 1; - sm->GM = 2; + memset(group->GTK, 0, sizeof(group->GTK)); + group->GN = 1; + group->GM = 2; /* GTK[GN] = CalcGTK() */ - /* FIX: is this the correct way of getting GNonce? */ - memcpy(sm->GNonce, sm->Counter, WPA_NONCE_LEN); - inc_byte_array(sm->Counter, WPA_NONCE_LEN); - wpa_gmk_to_gtk(hapd, sm->GMK, hapd->own_addr, sm->GNonce, - sm->GTK[sm->GN - 1], sm->GTK_len); + wpa_gtk_update(wpa_auth, group); } -static void wpa_group_setkeys(struct hostapd_data *hapd) +static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx) { - struct wpa_authenticator *sm = hapd->wpa_auth; - struct sta_info *sta; - int tmp; - - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "WPA: group state machine " - "entering state SETKEYS\n"); - sm->changed = TRUE; - sm->wpa_group_state = WPA_GROUP_SETKEYS; - sm->GTKReKey = FALSE; - tmp = sm->GM; - sm->GM = sm->GN; - sm->GN = tmp; - sm->GKeyDoneStations = sm->GNoStations; - /* FIX: is this the correct way of getting GNonce? */ - memcpy(sm->GNonce, sm->Counter, WPA_NONCE_LEN); - inc_byte_array(sm->Counter, WPA_NONCE_LEN); - wpa_gmk_to_gtk(hapd, sm->GMK, hapd->own_addr, sm->GNonce, - sm->GTK[sm->GN - 1], sm->GTK_len); - - sta = hapd->sta_list; - while (sta) { - if (sta->wpa_sm) { - sta->wpa_sm->GUpdateStationKeys = TRUE; - wpa_sm_step(sta->wpa_sm); - } - sta = sta->next; - } + sm->GUpdateStationKeys = TRUE; + wpa_sm_step(sm); + return 0; } -static void wpa_group_setkeysdone(struct hostapd_data *hapd) +static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) { - struct wpa_authenticator *sm = hapd->wpa_auth; + 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; + group->GKeyDoneStations = group->GNoStations; + wpa_gtk_update(wpa_auth, group); - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "WPA: group state machine " - "entering state SETKEYSDONE\n"); - sm->changed = TRUE; - sm->wpa_group_state = WPA_GROUP_SETKEYSDONE; - hostapd_set_encryption(hapd, wpa_alg_txt(hapd->conf->wpa_group), - NULL, sm->GN, sm->GTK[sm->GN - 1], sm->GTK_len); + wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, NULL); } -static void wpa_group_sm_step(struct hostapd_data *hapd) +static void wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) { - struct wpa_authenticator *sm = hapd->wpa_auth; + 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); - if (sm->GInit) { - wpa_group_gtk_init(hapd); - } else if (sm->wpa_group_state == WPA_GROUP_GTK_INIT && - sm->GTKAuthenticator) { - wpa_group_setkeysdone(hapd); - } else if (sm->wpa_group_state == WPA_GROUP_SETKEYSDONE && - sm->GTKReKey) { - wpa_group_setkeys(hapd); - } else if (sm->wpa_group_state == WPA_GROUP_SETKEYS) { - if (sm->GKeyDoneStations == 0) - wpa_group_setkeysdone(hapd); - else if (sm->GTKReKey) - wpa_group_setkeys(hapd); +#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 int wpa_sm_sta_entry_alive(struct hostapd_data *hapd, u8 *addr) +static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) { - struct sta_info *sta; - sta = ap_get_sta(hapd, addr); - if (sta == NULL || sta->wpa_sm == NULL) - return 0; - return 1; + 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) { - struct hostapd_data *hapd = sm->hapd; - u8 addr[6]; - if (sm == NULL || sm->sta == NULL || sm->sta->wpa_sm == NULL) + 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; + } - memcpy(addr, sm->sta->addr, 6); + sm->in_step_loop = 1; do { + if (sm->pending_deinit) + break; + sm->changed = FALSE; - sm->hapd->wpa_auth->changed = FALSE; + sm->wpa_auth->group->changed = FALSE; SM_STEP_RUN(WPA_PTK); - if (!wpa_sm_sta_entry_alive(hapd, addr)) + if (sm->pending_deinit) break; SM_STEP_RUN(WPA_PTK_GROUP); - if (!wpa_sm_sta_entry_alive(hapd, addr)) - break; - wpa_group_sm_step(sm->hapd); - if (!wpa_sm_sta_entry_alive(hapd, addr)) + if (sm->pending_deinit) break; - } while (sm->changed || sm->hapd->wpa_auth->changed); + 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 = timeout_ctx; + struct wpa_state_machine *sm = eloop_ctx; wpa_sm_step(sm); } -void wpa_sm_notify(struct hostapd_data *hapd, struct sta_info *sta) +void wpa_auth_sm_notify(struct wpa_state_machine *sm) { - if (sta->wpa_sm == NULL) + if (sm == NULL) return; - eloop_register_timeout(0, 0, wpa_sm_call_step, hapd, sta->wpa_sm); + eloop_register_timeout(0, 0, wpa_sm_call_step, sm, NULL); } -void wpa_gtk_rekey(struct hostapd_data *hapd) +void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth) { - struct wpa_authenticator *sm = hapd->wpa_auth; int tmp, i; + struct wpa_group *group; - if (sm == NULL) + if (wpa_auth == NULL) return; + group = wpa_auth->group; + for (i = 0; i < 2; i++) { - tmp = sm->GM; - sm->GM = sm->GN; - sm->GN = tmp; - memcpy(sm->GNonce, sm->Counter, WPA_NONCE_LEN); - inc_byte_array(sm->Counter, WPA_NONCE_LEN); - wpa_gmk_to_gtk(hapd, sm->GMK, hapd->own_addr, sm->GNonce, - sm->GTK[sm->GN - 1], sm->GTK_len); + tmp = group->GM; + group->GM = group->GN; + group->GN = tmp; + wpa_gtk_update(wpa_auth, group); } } @@ -2746,151 +3478,317 @@ static int wpa_cipher_bits(int cipher) #define RSN_SUITE "%02x-%02x-%02x-%d" #define RSN_SUITE_ARG(s) (s)[0], (s)[1], (s)[2], (s)[3] -int wpa_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen) +int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen) { - int len = 0, i; - char pmkid_txt[PMKID_LEN * 2 + 1], *pos; - - len += snprintf(buf + len, buflen - len, - "dot11RSNAOptionImplemented=TRUE\n" - "dot11RSNAPreauthenticationImplemented=TRUE\n" - "dot11RSNAEnabled=%s\n" - "dot11RSNAPreauthenticationEnabled=%s\n", - wpa_bool_txt(hapd->conf->wpa & - HOSTAPD_WPA_VERSION_WPA2), - wpa_bool_txt(hapd->conf->rsn_preauth)); + int len = 0, ret; + char pmkid_txt[PMKID_LEN * 2 + 1]; - if (hapd->wpa_auth == NULL) + if (wpa_auth == NULL) return len; - pos = pmkid_txt; - for (i = 0; i < PMKID_LEN; i++) { - pos += sprintf(pos, "%02x", - hapd->wpa_auth->dot11RSNAPMKIDUsed[i]); - } - - len += 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, - !!hapd->conf->wpa_strict_rekey, - dot11RSNAConfigGroupUpdateCount, - dot11RSNAConfigPairwiseUpdateCount, - wpa_cipher_bits(hapd->conf->wpa_group), - dot11RSNAConfigPMKLifetime, - dot11RSNAConfigPMKReauthThreshold, - dot11RSNAConfigSATimeout, - RSN_SUITE_ARG(hapd->wpa_auth-> - dot11RSNAAuthenticationSuiteSelected), - RSN_SUITE_ARG(hapd->wpa_auth-> - dot11RSNAPairwiseCipherSelected), - RSN_SUITE_ARG(hapd->wpa_auth-> - dot11RSNAGroupCipherSelected), - pmkid_txt, - RSN_SUITE_ARG(hapd->wpa_auth-> - dot11RSNAAuthenticationSuiteRequested), - RSN_SUITE_ARG(hapd->wpa_auth-> - dot11RSNAPairwiseCipherRequested), - RSN_SUITE_ARG(hapd->wpa_auth-> - dot11RSNAGroupCipherRequested), - hapd->wpa_auth->dot11RSNATKIPCounterMeasuresInvoked, - hapd->wpa_auth->dot11RSNA4WayHandshakeFailures); + 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 */ - len += snprintf(buf + len, buflen - len, - "hostapdWPAGroupState=%d\n", - hapd->wpa_auth->wpa_group_state); + 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 hostapd_data *hapd, struct sta_info *sta, - char *buf, size_t buflen) +int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen) { - int len = 0; + 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 (sta->wpa == WPA_VERSION_WPA) { - if (sta->pairwise == WPA_CIPHER_CCMP) + if (sm->wpa == WPA_VERSION_WPA) { + if (sm->pairwise == WPA_CIPHER_CCMP) pairwise = WPA_CIPHER_SUITE_CCMP; - else if (sta->pairwise == WPA_CIPHER_TKIP) + else if (sm->pairwise == WPA_CIPHER_TKIP) pairwise = WPA_CIPHER_SUITE_TKIP; - else if (sta->pairwise == WPA_CIPHER_WEP104) + else if (sm->pairwise == WPA_CIPHER_WEP104) pairwise = WPA_CIPHER_SUITE_WEP104; - else if (sta->pairwise == WPA_CIPHER_WEP40) + else if (sm->pairwise == WPA_CIPHER_WEP40) pairwise = WPA_CIPHER_SUITE_WEP40; - else if (sta->pairwise == WPA_CIPHER_NONE) + else if (sm->pairwise == WPA_CIPHER_NONE) pairwise = WPA_CIPHER_SUITE_NONE; - } else if (sta->wpa == WPA_VERSION_WPA2) { - if (sta->pairwise == WPA_CIPHER_CCMP) + } else if (sm->wpa == WPA_VERSION_WPA2) { + if (sm->pairwise == WPA_CIPHER_CCMP) pairwise = RSN_CIPHER_SUITE_CCMP; - else if (sta->pairwise == WPA_CIPHER_TKIP) + else if (sm->pairwise == WPA_CIPHER_TKIP) pairwise = RSN_CIPHER_SUITE_TKIP; - else if (sta->pairwise == WPA_CIPHER_WEP104) + else if (sm->pairwise == WPA_CIPHER_WEP104) pairwise = RSN_CIPHER_SUITE_WEP104; - else if (sta->pairwise == WPA_CIPHER_WEP40) + else if (sm->pairwise == WPA_CIPHER_WEP40) pairwise = RSN_CIPHER_SUITE_WEP40; - else if (sta->pairwise == WPA_CIPHER_NONE) + else if (sm->pairwise == WPA_CIPHER_NONE) pairwise = RSN_CIPHER_SUITE_NONE; } else return 0; - len += 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(sta->addr), - RSN_SUITE_ARG(pairwise), - sta->dot11RSNAStatsTKIPLocalMICFailures, - sta->dot11RSNAStatsTKIPRemoteMICFailures); - - if (sta->wpa_sm == NULL) + 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 */ - len += snprintf(buf + len, buflen - len, - "hostapdWPAPTKState=%d\n" - "hostapdWPAPTKGroupState=%d\n", - sta->wpa_sm->wpa_ptk_state, - sta->wpa_sm->wpa_ptk_group_state); + 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); + + if (sm->group && sm->group != group && sm->sta_counted) { + sm->group->GNoStations--; + sm->sta_counted = 0; + wpa_printf(MSG_DEBUG, "WLA: Decreased GNoStations for the " + "previously used group state machine"); + } + + sm->group = group; + return 0; +} + +#endif /* CONFIG_NATIVE_WINDOWS */ diff --git a/contrib/hostapd/wpa.h b/contrib/hostapd/wpa.h index 62159e7..633b2c5 100644 --- a/contrib/hostapd/wpa.h +++ b/contrib/hostapd/wpa.h @@ -1,33 +1,30 @@ +/* + * 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 -#define WPA_NONCE_LEN 32 +#include "wpa_common.h" + #define WPA_PMK_LEN PMK_LEN -#define WPA_REPLAY_COUNTER_LEN 8 #define WPA_GMK_LEN 32 #define WPA_GTK_MAX_LEN 32 -#define WPA_KEY_RSC_LEN 8 #define PMKID_LEN 16 -struct rsn_pmksa_cache { - struct rsn_pmksa_cache *next, *hnext; - u8 pmkid[PMKID_LEN]; - u8 pmk[PMK_LEN]; - time_t expiration; - int akmp; /* WPA_KEY_MGMT_* */ - u8 spa[ETH_ALEN]; - u8 *identity; - size_t identity_len; - struct radius_class_data radius_class; -}; - -struct rsn_preauth_interface { - struct rsn_preauth_interface *next; - struct hostapd_data *hapd; - struct l2_packet_data *l2; - char *ifname; - int ifindex; -}; +#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; @@ -58,6 +55,7 @@ struct wpa_eapol_key { #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 */ @@ -75,122 +73,114 @@ struct wpa_ptk { } u; } __attribute__ ((packed)); -struct wpa_state_machine { - struct hostapd_data *hapd; - struct sta_info *sta; - +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_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/D8 */ - 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; - - Boolean changed; + WPA_NO_IEEE80211W = 0, + WPA_IEEE80211W_OPTIONAL = 1, + WPA_IEEE80211W_REQUIRED = 2 + } ieee80211w; +#endif /* CONFIG_IEEE80211W */ }; -/* per authenticator data */ -struct wpa_authenticator { - Boolean GInit; - int GNoStations; - int GKeyDoneStations; - Boolean GTKReKey; - int GTK_len; - int GN, GM; - Boolean GTKAuthenticator; - u8 Counter[WPA_NONCE_LEN]; +typedef enum { + LOGGER_DEBUG, LOGGER_INFO, LOGGER_WARNING +} logger_level; - 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; - - 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; +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); }; - -int wpa_init(struct hostapd_data *hapd); -void wpa_deinit(struct hostapd_data *hapd); +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_INVALID_AKMP, WPA_NOT_ENABLED, WPA_ALLOC_FAIL, + WPA_MGMT_FRAME_PROTECTION_VIOLATION, WPA_INVALID_MGMT_GROUP_CIPHER }; -int wpa_validate_wpa_ie(struct hostapd_data *hapd, struct sta_info *sta, - const u8 *wpa_ie, size_t wpa_ie_len, int version); -void wpa_new_station(struct hostapd_data *hapd, struct sta_info *sta); -void wpa_free_station(struct sta_info *sta); -void wpa_receive(struct hostapd_data *hapd, struct sta_info *sta, +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_sm_event(struct hostapd_data *hapd, struct sta_info *sta, - wpa_event event); -void wpa_sm_notify(struct hostapd_data *hapd, struct sta_info *sta); -void pmksa_cache_add(struct hostapd_data *hapd, struct sta_info *sta, u8 *pmk, - int session_timeout); -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 wpa_gtk_rekey(struct hostapd_data *hapd); -int wpa_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen); -int wpa_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, - char *buf, size_t buflen); +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/contrib/hostapd/wpa_common.h b/contrib/hostapd/wpa_common.h new file mode 100644 index 0000000..6d0316c --- /dev/null +++ b/contrib/hostapd/wpa_common.h @@ -0,0 +1,58 @@ +/* + * WPA definitions shared between hostapd and wpa_supplicant + * Copyright (c) 2002-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 WPA_COMMON_H +#define WPA_COMMON_H + +#define WPA_REPLAY_COUNTER_LEN 8 +#define WPA_NONCE_LEN 32 +#define WPA_KEY_RSC_LEN 8 + + +/* IEEE Std 802.1X-2004 */ + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct ieee802_1x_hdr { + u8 version; + u8 type; + u16 length; + /* followed by length octets of data */ +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + +#define EAPOL_VERSION 2 + +enum { IEEE802_1X_TYPE_EAP_PACKET = 0, + IEEE802_1X_TYPE_EAPOL_START = 1, + IEEE802_1X_TYPE_EAPOL_LOGOFF = 2, + IEEE802_1X_TYPE_EAPOL_KEY = 3, + IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT = 4 +}; + +enum { EAPOL_KEY_TYPE_RC4 = 1, EAPOL_KEY_TYPE_RSN = 2, + EAPOL_KEY_TYPE_WPA = 254 }; + +#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 */ diff --git a/contrib/hostapd/wpa_ctrl.c b/contrib/hostapd/wpa_ctrl.c index 98e0669..dcec537 100644 --- a/contrib/hostapd/wpa_ctrl.c +++ b/contrib/hostapd/wpa_ctrl.c @@ -1,6 +1,6 @@ /* * wpa_supplicant/hostapd control interface library - * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.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 @@ -12,22 +12,21 @@ * 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/time.h> -#ifndef CONFIG_NATIVE_WINDOWS -#include <sys/socket.h> -#include <netinet/in.h> +#include "includes.h" + +#ifdef CONFIG_CTRL_IFACE + +#ifdef CONFIG_CTRL_IFACE_UNIX #include <sys/un.h> -#endif /* CONFIG_NATIVE_WINDOWS */ +#endif /* CONFIG_CTRL_IFACE_UNIX */ #include "wpa_ctrl.h" -#ifdef CONFIG_NATIVE_WINDOWS #include "common.h" -#endif /* CONFIG_NATIVE_WINDOWS */ + + +#if defined(CONFIG_CTRL_IFACE_UNIX) || defined(CONFIG_CTRL_IFACE_UDP) +#define CTRL_IFACE_SOCKET +#endif /* CONFIG_CTRL_IFACE_UNIX || CONFIG_CTRL_IFACE_UDP */ /** @@ -40,84 +39,121 @@ * the arguments for most of the control interface library functions. */ struct wpa_ctrl { - int s; #ifdef CONFIG_CTRL_IFACE_UDP + int s; struct sockaddr_in local; struct sockaddr_in dest; -#else /* CONFIG_CTRL_IFACE_UDP */ + char *cookie; +#endif /* CONFIG_CTRL_IFACE_UDP */ +#ifdef CONFIG_CTRL_IFACE_UNIX + int s; struct sockaddr_un local; struct sockaddr_un dest; -#endif /* CONFIG_CTRL_IFACE_UDP */ +#endif /* CONFIG_CTRL_IFACE_UNIX */ +#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE + HANDLE pipe; +#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ }; +#ifdef CONFIG_CTRL_IFACE_UNIX + struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) { struct wpa_ctrl *ctrl; -#ifndef CONFIG_CTRL_IFACE_UDP static int counter = 0; -#endif /* CONFIG_CTRL_IFACE_UDP */ - ctrl = malloc(sizeof(*ctrl)); + ctrl = os_malloc(sizeof(*ctrl)); if (ctrl == NULL) return NULL; - memset(ctrl, 0, sizeof(*ctrl)); + os_memset(ctrl, 0, sizeof(*ctrl)); -#ifdef CONFIG_CTRL_IFACE_UDP - ctrl->s = socket(PF_INET, SOCK_DGRAM, 0); + ctrl->s = socket(PF_UNIX, SOCK_DGRAM, 0); if (ctrl->s < 0) { - perror("socket"); - free(ctrl); + os_free(ctrl); return NULL; } - ctrl->local.sin_family = AF_INET; - ctrl->local.sin_addr.s_addr = htonl((127 << 24) | 1); + ctrl->local.sun_family = AF_UNIX; + os_snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path), + "/tmp/wpa_ctrl_%d-%d", getpid(), counter++); if (bind(ctrl->s, (struct sockaddr *) &ctrl->local, - sizeof(ctrl->local)) < 0) { + sizeof(ctrl->local)) < 0) { close(ctrl->s); - free(ctrl); + os_free(ctrl); return NULL; } - ctrl->dest.sin_family = AF_INET; - ctrl->dest.sin_addr.s_addr = htonl((127 << 24) | 1); - ctrl->dest.sin_port = htons(WPA_CTRL_IFACE_PORT); + ctrl->dest.sun_family = AF_UNIX; + os_snprintf(ctrl->dest.sun_path, sizeof(ctrl->dest.sun_path), "%s", + ctrl_path); if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest, sizeof(ctrl->dest)) < 0) { - perror("connect"); close(ctrl->s); - free(ctrl); + unlink(ctrl->local.sun_path); + os_free(ctrl); return NULL; } -#else /* CONFIG_CTRL_IFACE_UDP */ - ctrl->s = socket(PF_UNIX, SOCK_DGRAM, 0); + + return ctrl; +} + + +void wpa_ctrl_close(struct wpa_ctrl *ctrl) +{ + unlink(ctrl->local.sun_path); + close(ctrl->s); + os_free(ctrl); +} + +#endif /* CONFIG_CTRL_IFACE_UNIX */ + + +#ifdef CONFIG_CTRL_IFACE_UDP + +struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) +{ + struct wpa_ctrl *ctrl; + char buf[128]; + size_t len; + + ctrl = os_malloc(sizeof(*ctrl)); + if (ctrl == NULL) + return NULL; + os_memset(ctrl, 0, sizeof(*ctrl)); + + ctrl->s = socket(PF_INET, SOCK_DGRAM, 0); if (ctrl->s < 0) { - free(ctrl); + perror("socket"); + os_free(ctrl); return NULL; } - ctrl->local.sun_family = AF_UNIX; - snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path), - "/tmp/wpa_ctrl_%d-%d", getpid(), counter++); + ctrl->local.sin_family = AF_INET; + ctrl->local.sin_addr.s_addr = htonl((127 << 24) | 1); if (bind(ctrl->s, (struct sockaddr *) &ctrl->local, - sizeof(ctrl->local)) < 0) { + sizeof(ctrl->local)) < 0) { close(ctrl->s); - free(ctrl); + os_free(ctrl); return NULL; } - ctrl->dest.sun_family = AF_UNIX; - snprintf(ctrl->dest.sun_path, sizeof(ctrl->dest.sun_path), "%s", - ctrl_path); + ctrl->dest.sin_family = AF_INET; + ctrl->dest.sin_addr.s_addr = htonl((127 << 24) | 1); + ctrl->dest.sin_port = htons(WPA_CTRL_IFACE_PORT); if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest, sizeof(ctrl->dest)) < 0) { + perror("connect"); close(ctrl->s); - unlink(ctrl->local.sun_path); - free(ctrl); + os_free(ctrl); return NULL; } -#endif /* CONFIG_CTRL_IFACE_UDP */ + + len = sizeof(buf) - 1; + if (wpa_ctrl_request(ctrl, "GET_COOKIE", 10, buf, &len, NULL) == 0) { + buf[len] = '\0'; + ctrl->cookie = strdup(buf); + } return ctrl; } @@ -125,14 +161,15 @@ struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) void wpa_ctrl_close(struct wpa_ctrl *ctrl) { -#ifndef CONFIG_CTRL_IFACE_UDP - unlink(ctrl->local.sun_path); -#endif /* CONFIG_CTRL_IFACE_UDP */ close(ctrl->s); - free(ctrl); + os_free(ctrl->cookie); + os_free(ctrl); } +#endif /* CONFIG_CTRL_IFACE_UDP */ + +#ifdef CTRL_IFACE_SOCKET int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, char *reply, size_t *reply_len, void (*msg_cb)(char *msg, size_t len)) @@ -140,9 +177,35 @@ int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, struct timeval tv; int res; fd_set rfds; + const char *_cmd; + char *cmd_buf = NULL; + size_t _cmd_len; - if (send(ctrl->s, cmd, cmd_len, 0) < 0) +#ifdef CONFIG_CTRL_IFACE_UDP + if (ctrl->cookie) { + char *pos; + _cmd_len = 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); + *pos++ = ' '; + memcpy(pos, cmd, cmd_len); + } else +#endif /* CONFIG_CTRL_IFACE_UDP */ + { + _cmd = cmd; + _cmd_len = cmd_len; + } + + if (send(ctrl->s, _cmd, _cmd_len, 0) < 0) { + os_free(cmd_buf); return -1; + } + os_free(cmd_buf); for (;;) { tv.tv_sec = 2; @@ -177,6 +240,7 @@ int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, } return 0; } +#endif /* CTRL_IFACE_SOCKET */ static int wpa_ctrl_attach_helper(struct wpa_ctrl *ctrl, int attach) @@ -189,7 +253,7 @@ static int wpa_ctrl_attach_helper(struct wpa_ctrl *ctrl, int attach) buf, &len, NULL); if (ret < 0) return ret; - if (len == 3 && memcmp(buf, "OK\n", 3) == 0) + if (len == 3 && os_memcmp(buf, "OK\n", 3) == 0) return 0; return -1; } @@ -207,6 +271,8 @@ int wpa_ctrl_detach(struct wpa_ctrl *ctrl) } +#ifdef CTRL_IFACE_SOCKET + int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len) { int res; @@ -222,13 +288,12 @@ int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len) int wpa_ctrl_pending(struct wpa_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); + select(ctrl->s + 1, &rfds, NULL, NULL, &tv); return FD_ISSET(ctrl->s, &rfds); } @@ -237,3 +302,124 @@ int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl) { return ctrl->s; } + +#endif /* CTRL_IFACE_SOCKET */ + + +#ifdef CONFIG_CTRL_IFACE_NAMED_PIPE + +#ifndef WPA_SUPPLICANT_NAMED_PIPE +#define WPA_SUPPLICANT_NAMED_PIPE "WpaSupplicant" +#endif +#define NAMED_PIPE_PREFIX TEXT("\\\\.\\pipe\\") TEXT(WPA_SUPPLICANT_NAMED_PIPE) + +struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) +{ + struct wpa_ctrl *ctrl; + DWORD mode; + TCHAR name[256]; + int i; + + ctrl = os_malloc(sizeof(*ctrl)); + if (ctrl == NULL) + return NULL; + os_memset(ctrl, 0, sizeof(*ctrl)); + +#ifdef UNICODE + if (ctrl_path == NULL) + _snwprintf(name, 256, NAMED_PIPE_PREFIX); + else + _snwprintf(name, 256, NAMED_PIPE_PREFIX TEXT("-%S"), + ctrl_path); +#else /* UNICODE */ + if (ctrl_path == NULL) + os_snprintf(name, 256, NAMED_PIPE_PREFIX); + else + os_snprintf(name, 256, NAMED_PIPE_PREFIX "-%s", + ctrl_path); +#endif /* UNICODE */ + + for (i = 0; i < 10; i++) { + ctrl->pipe = CreateFile(name, GENERIC_READ | GENERIC_WRITE, 0, + NULL, OPEN_EXISTING, 0, NULL); + /* + * Current named pipe server side in wpa_supplicant is + * re-opening the pipe for new clients only after the previous + * one is taken into use. This leaves a small window for race + * conditions when two connections are being opened at almost + * the same time. Retry if that was the case. + */ + if (ctrl->pipe != INVALID_HANDLE_VALUE || + GetLastError() != ERROR_PIPE_BUSY) + break; + WaitNamedPipe(name, 1000); + } + if (ctrl->pipe == INVALID_HANDLE_VALUE) { + os_free(ctrl); + return NULL; + } + + mode = PIPE_READMODE_MESSAGE; + if (!SetNamedPipeHandleState(ctrl->pipe, &mode, NULL, NULL)) { + CloseHandle(ctrl->pipe); + os_free(ctrl); + return NULL; + } + + return ctrl; +} + + +void wpa_ctrl_close(struct wpa_ctrl *ctrl) +{ + CloseHandle(ctrl->pipe); + os_free(ctrl); +} + + +int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, + char *reply, size_t *reply_len, + void (*msg_cb)(char *msg, size_t len)) +{ + DWORD written; + DWORD readlen = *reply_len; + + if (!WriteFile(ctrl->pipe, cmd, cmd_len, &written, NULL)) + return -1; + + if (!ReadFile(ctrl->pipe, reply, *reply_len, &readlen, NULL)) + return -1; + *reply_len = readlen; + + return 0; +} + + +int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len) +{ + DWORD len = *reply_len; + if (!ReadFile(ctrl->pipe, reply, *reply_len, &len, NULL)) + return -1; + *reply_len = len; + return 0; +} + + +int wpa_ctrl_pending(struct wpa_ctrl *ctrl) +{ + DWORD left; + + if (!PeekNamedPipe(ctrl->pipe, NULL, 0, NULL, &left, NULL)) + return -1; + return left ? 1 : 0; +} + + +int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl) +{ + return -1; +} + +#endif /* CONFIG_CTRL_IFACE_NAMED_PIPE */ + +#endif /* CONFIG_CTRL_IFACE */ diff --git a/contrib/hostapd/wpa_ctrl.h b/contrib/hostapd/wpa_ctrl.h index c8fa48d..6166fda 100644 --- a/contrib/hostapd/wpa_ctrl.h +++ b/contrib/hostapd/wpa_ctrl.h @@ -1,6 +1,6 @@ /* * wpa_supplicant/hostapd control interface library - * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * 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 @@ -147,7 +147,7 @@ int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len); /** * wpa_ctrl_pending - Check whether there are pending event messages * @ctrl: Control interface data from wpa_ctrl_open() - * Returns: Non-zero if there are pending messages + * Returns: 1 if there are pending messages, 0 if no, or -1 on error * * This function will check whether there are any pending control interface * message available to be received with wpa_ctrl_recv(). wpa_ctrl_pending() is |