diff options
author | Sam Leffler <sam@FreeBSD.org> | 2007-07-09 16:15:06 +0000 |
---|---|---|
committer | Sam Leffler <sam@FreeBSD.org> | 2007-07-09 16:15:06 +0000 |
commit | 75cbf10273fddabf1de109ac241e64defd66eb38 (patch) | |
tree | c6f336fc28b042f00efc2373c71fceadfa394e52 /contrib | |
parent | b449aee71d92a35e5293f3df4f0a564f77fbd02b (diff) | |
download | src-75cbf10273fddabf1de109ac241e64defd66eb38.tar.gz src-75cbf10273fddabf1de109ac241e64defd66eb38.zip |
Import of hostapd 0.5.8
Notes
Notes:
svn path=/vendor/hostapd/dist/; revision=171322
Diffstat (limited to 'contrib')
159 files changed, 32275 insertions, 6309 deletions
diff --git a/contrib/hostapd/COPYING b/contrib/hostapd/COPYING index 60549be514af..14f5453722a8 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 f7bd4102d094..73c63a86522d 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 cba8df0ea7bd..1f544522a1b5 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 5efe2f50c64f..08922051fe55 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 276baeeed8c5..c98922b31aaf 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 d5354624b5da..541fac428515 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 5ee3d750f9f2..b22347b2ac2e 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 8af3eac59cb5..ee2ee64e3b02 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 ce94778dd62e..1a2459b3e013 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 000000000000..6b9f4147afb1 --- /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 a5925ca2ec47..c52e45af27c5 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 cb1a53967761..1bc6971eff8e 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 e874ffd2b237..b73c5b47a6fb 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 000000000000..f2d322125fc8 --- /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 000000000000..668d9096341c --- /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 000000000000..7af2bc1754e2 --- /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 000000000000..18e0da2e89f6 --- /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 000000000000..58bcda825345 --- /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 4b756d8f9292..c8d6f130a960 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 4bece7f6ecd9..b200b580d5df 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 016d9b9c959d..d1b2ba3fa5e0 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 8754a84884aa..fafe8e041106 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 12b57cb4acf1..ffcffa3c0cd4 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 b4c81892c66e..c5edd24c4b55 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 e664861afe71..00b13b91c48e 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 ff730d4a95d3..9863782ed148 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 ef1a541c94c8..2ac2f3b29931 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 e8f4e4fc53ec..8fe4bf9f5bfa 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 6f9881d71d03..603fc55c110a 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 000000000000..8e0d56fc6af1 --- /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 000000000000..fdcf725b5d7f --- /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 000000000000..76cfc6a6b79c --- /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 000000000000..44760f4204b3 --- /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 000000000000..619f977303da --- /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 000000000000..0ad196f2d6b6 --- /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 000000000000..f0f135aa9598 --- /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 000000000000..af3f0be19a9b --- /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 000000000000..68835a1ddd31 --- /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 000000000000..7cf95de584af --- /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 000000000000..0621791c0be0 --- /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 ed9ecbfdbc8c..4fd262c1ff14 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 6af9af256262..d0b90d1c9e5f 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 6ef6b19f53ff..950668183f93 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 a20147e79e78..da250f04c783 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 c5c62eb57c72..137719189dfa 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 000000000000..5db4cd3f7260 --- /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 9cd44905153d..8ea923a8f9d0 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 000000000000..8ab70a1ac3f3 --- /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 000000000000..a72b5f3da15f --- /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 000000000000..c806b7f85217 --- /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 674f83735f1f..42e4cd35cd74 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 4e803f905d04..85b2c2d2fac8 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 54efc47961aa..ab6f26b1fea5 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 d776c8c82749..234a319b6909 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 000000000000..671d5486c316 --- /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 000000000000..cec8a570e2a6 --- /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 5cbf6eb71289..bbd819bbe12b 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 2fbec870c9f1..8daa8d3adb04 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 d8f4016a6a09..80110469dcc3 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 b5ad6af1f2b9..bbad5e4052ff 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 9eb61a6b132f..6ea1f618a1d8 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 2f92d0577161..7408a522e328 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 24de66cf0cb7..8d896ae639d5 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 5dd3a1042da3..e1bdccf5916a 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 000000000000..e031f29fb436 --- /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 000000000000..4b5476f101c8 --- /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 000000000000..ac6e8199d66b --- /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 fa60cf54d1c7..8c3c82870036 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 75947b7995a0..dc8b2f6f4b10 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 6715c369c0b6..9c983a864e3e 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 a965fa461d26..93ade144fa4f 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 |