diff options
author | sam <sam@FreeBSD.org> | 2006-03-07 05:47:04 +0000 |
---|---|---|
committer | sam <sam@FreeBSD.org> | 2006-03-07 05:47:04 +0000 |
commit | 8d55057fb42bf9070fd379acbcb6fc4ef793d2a7 (patch) | |
tree | dacc7977efdefefb8b105113edeb5215c0e01234 /contrib/hostapd | |
parent | d1a1fd4aa94cd9c5cb443c4c1337f91c8c46fde0 (diff) | |
download | FreeBSD-src-8d55057fb42bf9070fd379acbcb6fc4ef793d2a7.zip FreeBSD-src-8d55057fb42bf9070fd379acbcb6fc4ef793d2a7.tar.gz |
Import of hostapd 0.4.8
Diffstat (limited to 'contrib/hostapd')
88 files changed, 7716 insertions, 1679 deletions
diff --git a/contrib/hostapd/ChangeLog b/contrib/hostapd/ChangeLog index 9d15f75..f7bd410 100644 --- a/contrib/hostapd/ChangeLog +++ b/contrib/hostapd/ChangeLog @@ -1,18 +1,102 @@ ChangeLog for hostapd -2005-06-10 - v0.3.9 +2006-02-08 - v0.4.8 + * 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 + +2005-11-20 - v0.4.7 (beginning of 0.4.x stable releases) + * driver_wired: fixed EAPOL sending to optionally use PAE group address + as the destination instead of supplicant MAC address; this is + disabled by default, but should be enabled with use_pae_group_addr=1 + in configuration file if the wired interface is used by only one + device at the time (common switch configuration) + * driver_madwifi: configure driver to use TKIP countermeasures in order + to get correct behavior (IEEE 802.11 association failing; previously, + association succeeded, but hostpad forced disassociation immediately) + * driver_madwifi: added support for madwifi-ng + +2005-10-27 - v0.4.6 + * added support for replacing user identity from EAP with RADIUS + User-Name attribute from Access-Accept message, if that is included, + for the RADIUS accounting messages (e.g., for EAP-PEAP/TTLS to get + tunneled identity into accounting messages when the RADIUS server + does not support better way of doing this with Class attribute) + * driver_madwifi: fixed EAPOL packet receive for configuration where + ath# is part of a bridge interface + * added a configuration file and log analyzer script for logwatch + * fixed EAPOL state machine step function to process all state + transitions before processing new events; this resolves a race + condition in which EAPOL-Start message could trigger hostapd to send + two EAP-Response/Identity frames to the authentication server + +2005-09-25 - v0.4.5 + * added client CA list to the TLS certificate request in order to make + it easier for the client to select which certificate to use + * added experimental support for EAP-PSK + * added support for WE-19 (hostap, madwifi) + +2005-08-21 - v0.4.4 + * fixed build without CONFIG_RSN_PREAUTH + * fixed FreeBSD build + +2005-06-26 - v0.4.3 + * fixed PMKSA caching to copy User-Name and Class attributes so that + RADIUS accounting gets correct information + * start RADIUS accounting only after successful completion of WPA + 4-Way Handshake if WPA-PSK is used + * fixed PMKSA caching for the case where STA (re)associates without + first disassociating + +2005-06-12 - v0.4.2 + * EAP-PAX is now registered as EAP type 46 + * fixed EAP-PAX MAC calculation + * fixed EAP-PAX CK and ICK key derivation + * renamed eap_authenticator configuration variable to eap_server to + better match with RFC 3748 (EAP) terminology + * driver_test: added support for testing hostapd with wpa_supplicant + by using test driver interface without any kernel drivers or network + cards + +2005-05-22 - v0.4.1 + * fixed RADIUS server initialization when only auth or acct server + is configured and the other one is left empty + * driver_madwifi: added support for RADIUS accounting + * driver_madwifi: added preliminary support for compiling against 'BSD' + branch of madwifi CVS tree + * driver_madwifi: fixed pairwise key removal to allow WPA reauth + without disassociation + * added support for reading additional certificates from PKCS#12 files + and adding them to the certificate chain + * fixed RADIUS Class attribute processing to only use Access-Accept + packets to update Class; previously, other RADIUS authentication + packets could have cleared Class attribute + * added support for more than one Class attribute in RADIUS packets + * added support for verifying certificate revocation list (CRL) when + using integrated EAP authenticator for EAP-TLS; new hostapd.conf + options 'check_crl'; CRL must be included in the ca_cert file for now + +2005-04-25 - v0.4.0 (beginning of 0.4.x development releases) + * added support for including network information into + EAP-Request/Identity message (ASCII-0 (nul) in eap_message) + (e.g., to implement draft-adrange-eap-network-discovery-07.txt) * fixed a bug which caused some RSN pre-authentication cases to use freed memory and potentially crash hostapd * fixed private key loading for cases where passphrase is not set + * added support for sending TLS alerts and aborting authentication + when receiving a TLS alert * fixed WPA2 to add PMKSA cache entry when using integrated EAP authenticator - * driver_madwifi: fixed pairwise key removal to allow WPA reauth - without disassociation - * fixed RADIUS attribute Class processing to only use Access-Accept - packets to update Class; previously, other RADIUS authentication - packets could have cleared Class attribute * fixed PMKSA caching (EAP authentication was not skipped correctly with the new state machine changes from IEEE 802.1X draft) + * added support for RADIUS over IPv6; own_ip_addr, auth_server_addr, + and acct_server_addr can now be IPv6 addresses (CONFIG_IPV6=y needs + to be added to .config to include IPv6 support); for RADIUS server, + radius_server_ipv6=1 needs to be set in hostapd.conf and addresses + in RADIUS clients file can then use IPv6 format + * added experimental support for EAP-PAX + * replaced hostapd control interface library (hostapd_ctrl.[ch]) with + the same implementation that wpa_supplicant is using (wpa_ctrl.[ch]) 2005-02-12 - v0.3.7 (beginning of 0.3.x stable releases) diff --git a/contrib/hostapd/Makefile b/contrib/hostapd/Makefile index 0ea99c9..276baee 100644 --- a/contrib/hostapd/Makefile +++ b/contrib/hostapd/Makefile @@ -60,6 +60,7 @@ CFLAGS += -DCONFIG_DRIVER_BSD OBJS += driver_bsd.o CONFIG_L2_PACKET=y CONFIG_DNET_PCAP=y +CONFIG_L2_FREEBSD=y endif ifdef CONFIG_DRIVER_TEST @@ -68,13 +69,20 @@ OBJS += driver_test.o endif ifdef CONFIG_L2_PACKET -OBJS += $(DIR_WPA_SUPPLICANT)/l2_packet.o -endif - ifdef CONFIG_DNET_PCAP CFLAGS += -DUSE_DNET_PCAP -LIBS +=-ldnet -lpcap +ifdef CONFIG_L2_FREEBSD +LIBS += -lpcap +OBJS += $(DIR_WPA_SUPPLICANT)/l2_packet_freebsd.o +else +LIBS += -ldnet -lpcap +OBJS += $(DIR_WPA_SUPPLICANT)/l2_packet_pcap.o endif +else +OBJS += $(DIR_WPA_SUPPLICANT)/l2_packet_linux.o +endif +endif + ifdef CONFIG_EAP_MD5 CFLAGS += -DEAP_MD5 @@ -120,13 +128,23 @@ OBJS += eap_sim.o $(DIR_WPA_SUPPLICANT)/eap_sim_common.o OBJS += eap_sim_db.o endif +ifdef CONFIG_EAP_PAX +CFLAGS += -DEAP_PAX +OBJS += eap_pax.o $(DIR_WPA_SUPPLICANT)/eap_pax_common.o +endif + +ifdef CONFIG_EAP_PSK +CFLAGS += -DEAP_PSK +OBJS += eap_psk.o $(DIR_WPA_SUPPLICANT)/eap_psk_common.o +endif + ifdef CONFIG_EAP_TLV CFLAGS += -DEAP_TLV OBJS += eap_tlv.o endif ifdef CONFIG_EAP -CFLAGS += -DEAP_AUTHENTICATOR +CFLAGS += -DEAP_SERVER OBJS += eap.o eap_identity.o endif @@ -156,6 +174,10 @@ CFLAGS += -DRADIUS_SERVER OBJS += radius_server.o endif +ifdef CONFIG_IPV6 +CFLAGS += -DCONFIG_IPV6 +endif + ALL=hostapd hostapd_cli all: verify_config $(ALL) @@ -224,8 +246,8 @@ ifdef CONFIG_DRIVER_TEST endif echo '}' >> driver_conf.c -hostapd_cli: hostapd_cli.o hostapd_ctrl.o - $(CC) -o hostapd_cli hostapd_cli.o hostapd_ctrl.o +hostapd_cli: hostapd_cli.o $(DIR_WPA_SUPPLICANT)/wpa_ctrl.o + $(CC) -o hostapd_cli hostapd_cli.o $(DIR_WPA_SUPPLICANT)/wpa_ctrl.o clean: rm -f core *~ *.o hostapd hostapd_cli *.d driver_conf.c diff --git a/contrib/hostapd/README b/contrib/hostapd/README index 18fc179..d535462 100644 --- a/contrib/hostapd/README +++ b/contrib/hostapd/README @@ -2,14 +2,12 @@ hostapd - user space IEEE 802.11 AP and IEEE 802.1X/WPA/WPA2/EAP Authenticator and RADIUS authentication server ================================================================ -Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> and +Copyright (c) 2002-2006, Jouni Malinen <jkmaline@cc.hut.fi> and contributors All Rights Reserved. This program is dual-licensed under both the GPL version 2 and BSD -license. Either license may be used at your option. Please note that -some of the driver interface implementations (driver_*.c) may be -licensed under a different license. +license. Either license may be used at your option. @@ -77,7 +75,7 @@ and dynamic WEP keying, RADIUS accounting, WPA/WPA2 (IEEE 802.11i/RSN) Authenticator and dynamic TKIP/CCMP keying. The current version includes support for other drivers, an integrated -EAP authenticator (i.e., allow full authentication without requiring +EAP server (i.e., allow full authentication without requiring an external RADIUS authentication server), and RADIUS authentication server for EAP authentication. diff --git a/contrib/hostapd/accounting.c b/contrib/hostapd/accounting.c index 188f859..5ee3d75 100644 --- a/contrib/hostapd/accounting.c +++ b/contrib/hostapd/accounting.c @@ -47,6 +47,7 @@ static struct radius_msg * accounting_msg(hostapd *hapd, struct sta_info *sta, char buf[128]; u8 *val; size_t len; + int i; msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST, radius_client_get_id(hapd->radius)); @@ -99,12 +100,22 @@ static struct radius_msg * accounting_msg(hostapd *hapd, struct sta_info *sta, } } - if (!radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, - (u8 *) &hapd->conf->own_ip_addr, 4)) { + if (hapd->conf->own_ip_addr.af == AF_INET && + !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, + (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) { printf("Could not add NAS-IP-Address\n"); goto fail; } +#ifdef CONFIG_IPV6 + if (hapd->conf->own_ip_addr.af == AF_INET6 && + !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS, + (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) { + printf("Could not add NAS-IPv6-Address\n"); + goto fail; + } +#endif /* CONFIG_IPV6 */ + if (hapd->conf->nas_identifier && !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER, (u8 *) hapd->conf->nas_identifier, @@ -150,11 +161,17 @@ static struct radius_msg * accounting_msg(hostapd *hapd, struct sta_info *sta, goto fail; } - val = ieee802_1x_get_radius_class(sta->eapol_sm, &len); - if (val && - !radius_msg_add_attr(msg, RADIUS_ATTR_CLASS, val, len)) { - printf("Could not add Class\n"); - goto fail; + for (i = 0; ; i++) { + val = ieee802_1x_get_radius_class(sta->eapol_sm, &len, + i); + if (val == NULL) + break; + + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CLASS, + val, len)) { + printf("Could not add Class\n"); + goto fail; + } } } @@ -225,7 +242,7 @@ void accounting_sta_start(hostapd *hapd, struct sta_info *sta) sta->acct_input_gigawords = sta->acct_output_gigawords = 0; hostapd_sta_clear_stats(hapd, sta->addr); - if (!hapd->conf->acct_server) + if (!hapd->conf->radius->acct_server) return; if (sta->acct_interim_interval) @@ -250,7 +267,7 @@ void accounting_sta_report(hostapd *hapd, struct sta_info *sta, int stop) struct hostap_sta_driver_data data; u32 gigawords; - if (!hapd->conf->acct_server) + if (!hapd->conf->radius->acct_server) return; msg = accounting_msg(hapd, sta, @@ -380,8 +397,7 @@ accounting_receive(struct radius_msg *msg, struct radius_msg *req, return RADIUS_RX_UNKNOWN; } - if (radius_msg_verify_acct(msg, shared_secret, shared_secret_len, req)) - { + if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) { printf("Incoming RADIUS packet did not have correct " "Authenticator - dropped\n"); return RADIUS_RX_INVALID_AUTHENTICATOR; @@ -395,7 +411,7 @@ static void accounting_report_state(struct hostapd_data *hapd, int on) { struct radius_msg *msg; - if (!hapd->conf->acct_server || hapd->radius == NULL) + if (!hapd->conf->radius->acct_server || hapd->radius == NULL) return; /* Inform RADIUS server that accounting will start/stop so that the diff --git a/contrib/hostapd/aes.c b/contrib/hostapd/aes.c index eabebd0..ce94778 100644 --- a/contrib/hostapd/aes.c +++ b/contrib/hostapd/aes.c @@ -1,8 +1,15 @@ /* + * AES (Rijndael) cipher + * * Modifications to public domain implementation: * - support only 128-bit keys * - cleanup - * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi> + * - use C pre-processor to make it easier to change S table access + * - added option (AES_SMALL_TABLES) for reducing code size by about 8 kB at + * cost of reduced throughput (quite small difference on Pentium 4, + * 10-25% when using -O1 or -O2 optimization) + * + * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -14,7 +21,7 @@ * See README and COPYING for more details. */ -/** +/* * rijndael-alg-fst.c * * @version 3.0 (December 2000) @@ -40,7 +47,8 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define FULL_UNROLL +/* #define FULL_UNROLL */ +#define AES_SMALL_TABLES /* @@ -123,6 +131,7 @@ static const u32 Te0[256] = { 0x824141c3U, 0x299999b0U, 0x5a2d2d77U, 0x1e0f0f11U, 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU, }; +#ifndef AES_SMALL_TABLES static const u32 Te1[256] = { 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, @@ -388,6 +397,7 @@ static const u32 Te4[256] = { 0x41414141U, 0x99999999U, 0x2d2d2d2dU, 0x0f0f0f0fU, 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U, }; +#endif /* AES_SMALL_TABLES */ static const u32 Td0[256] = { 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U, 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U, @@ -454,6 +464,7 @@ static const u32 Td0[256] = { 0x39a80171U, 0x080cb3deU, 0xd8b4e49cU, 0x6456c190U, 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U, }; +#ifndef AES_SMALL_TABLES static const u32 Td1[256] = { 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU, 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U, @@ -724,6 +735,116 @@ static const u32 rcon[] = { 0x10000000, 0x20000000, 0x40000000, 0x80000000, 0x1B000000, 0x36000000, /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ }; +#else /* AES_SMALL_TABLES */ +static const u8 Td4s[256] = { + 0x52U, 0x09U, 0x6aU, 0xd5U, 0x30U, 0x36U, 0xa5U, 0x38U, + 0xbfU, 0x40U, 0xa3U, 0x9eU, 0x81U, 0xf3U, 0xd7U, 0xfbU, + 0x7cU, 0xe3U, 0x39U, 0x82U, 0x9bU, 0x2fU, 0xffU, 0x87U, + 0x34U, 0x8eU, 0x43U, 0x44U, 0xc4U, 0xdeU, 0xe9U, 0xcbU, + 0x54U, 0x7bU, 0x94U, 0x32U, 0xa6U, 0xc2U, 0x23U, 0x3dU, + 0xeeU, 0x4cU, 0x95U, 0x0bU, 0x42U, 0xfaU, 0xc3U, 0x4eU, + 0x08U, 0x2eU, 0xa1U, 0x66U, 0x28U, 0xd9U, 0x24U, 0xb2U, + 0x76U, 0x5bU, 0xa2U, 0x49U, 0x6dU, 0x8bU, 0xd1U, 0x25U, + 0x72U, 0xf8U, 0xf6U, 0x64U, 0x86U, 0x68U, 0x98U, 0x16U, + 0xd4U, 0xa4U, 0x5cU, 0xccU, 0x5dU, 0x65U, 0xb6U, 0x92U, + 0x6cU, 0x70U, 0x48U, 0x50U, 0xfdU, 0xedU, 0xb9U, 0xdaU, + 0x5eU, 0x15U, 0x46U, 0x57U, 0xa7U, 0x8dU, 0x9dU, 0x84U, + 0x90U, 0xd8U, 0xabU, 0x00U, 0x8cU, 0xbcU, 0xd3U, 0x0aU, + 0xf7U, 0xe4U, 0x58U, 0x05U, 0xb8U, 0xb3U, 0x45U, 0x06U, + 0xd0U, 0x2cU, 0x1eU, 0x8fU, 0xcaU, 0x3fU, 0x0fU, 0x02U, + 0xc1U, 0xafU, 0xbdU, 0x03U, 0x01U, 0x13U, 0x8aU, 0x6bU, + 0x3aU, 0x91U, 0x11U, 0x41U, 0x4fU, 0x67U, 0xdcU, 0xeaU, + 0x97U, 0xf2U, 0xcfU, 0xceU, 0xf0U, 0xb4U, 0xe6U, 0x73U, + 0x96U, 0xacU, 0x74U, 0x22U, 0xe7U, 0xadU, 0x35U, 0x85U, + 0xe2U, 0xf9U, 0x37U, 0xe8U, 0x1cU, 0x75U, 0xdfU, 0x6eU, + 0x47U, 0xf1U, 0x1aU, 0x71U, 0x1dU, 0x29U, 0xc5U, 0x89U, + 0x6fU, 0xb7U, 0x62U, 0x0eU, 0xaaU, 0x18U, 0xbeU, 0x1bU, + 0xfcU, 0x56U, 0x3eU, 0x4bU, 0xc6U, 0xd2U, 0x79U, 0x20U, + 0x9aU, 0xdbU, 0xc0U, 0xfeU, 0x78U, 0xcdU, 0x5aU, 0xf4U, + 0x1fU, 0xddU, 0xa8U, 0x33U, 0x88U, 0x07U, 0xc7U, 0x31U, + 0xb1U, 0x12U, 0x10U, 0x59U, 0x27U, 0x80U, 0xecU, 0x5fU, + 0x60U, 0x51U, 0x7fU, 0xa9U, 0x19U, 0xb5U, 0x4aU, 0x0dU, + 0x2dU, 0xe5U, 0x7aU, 0x9fU, 0x93U, 0xc9U, 0x9cU, 0xefU, + 0xa0U, 0xe0U, 0x3bU, 0x4dU, 0xaeU, 0x2aU, 0xf5U, 0xb0U, + 0xc8U, 0xebU, 0xbbU, 0x3cU, 0x83U, 0x53U, 0x99U, 0x61U, + 0x17U, 0x2bU, 0x04U, 0x7eU, 0xbaU, 0x77U, 0xd6U, 0x26U, + 0xe1U, 0x69U, 0x14U, 0x63U, 0x55U, 0x21U, 0x0cU, 0x7dU, +}; +static const u8 rcons[] = { + 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1B, 0x36 + /* for 128-bit blocks, Rijndael never uses more than 10 rcon values */ +}; +#endif /* AES_SMALL_TABLES */ + + +#ifndef AES_SMALL_TABLES + +#define RCON(i) rcon[(i)] + +#define TE0(i) Te0[((i) >> 24) & 0xff] +#define TE1(i) Te1[((i) >> 16) & 0xff] +#define TE2(i) Te2[((i) >> 8) & 0xff] +#define TE3(i) Te3[(i) & 0xff] +#define TE41(i) (Te4[((i) >> 24) & 0xff] & 0xff000000) +#define TE42(i) (Te4[((i) >> 16) & 0xff] & 0x00ff0000) +#define TE43(i) (Te4[((i) >> 8) & 0xff] & 0x0000ff00) +#define TE44(i) (Te4[(i) & 0xff] & 0x000000ff) +#define TE421(i) (Te4[((i) >> 16) & 0xff] & 0xff000000) +#define TE432(i) (Te4[((i) >> 8) & 0xff] & 0x00ff0000) +#define TE443(i) (Te4[(i) & 0xff] & 0x0000ff00) +#define TE414(i) (Te4[((i) >> 24) & 0xff] & 0x000000ff) +#define TE4(i) (Te4[(i)] & 0x000000ff) + +#define TD0(i) Td0[((i) >> 24) & 0xff] +#define TD1(i) Td1[((i) >> 16) & 0xff] +#define TD2(i) Td2[((i) >> 8) & 0xff] +#define TD3(i) Td3[(i) & 0xff] +#define TD41(i) (Td4[((i) >> 24) & 0xff] & 0xff000000) +#define TD42(i) (Td4[((i) >> 16) & 0xff] & 0x00ff0000) +#define TD43(i) (Td4[((i) >> 8) & 0xff] & 0x0000ff00) +#define TD44(i) (Td4[(i) & 0xff] & 0x000000ff) +#define TD0_(i) Td0[(i) & 0xff] +#define TD1_(i) Td1[(i) & 0xff] +#define TD2_(i) Td2[(i) & 0xff] +#define TD3_(i) Td3[(i) & 0xff] + +#else /* AES_SMALL_TABLES */ + +#define RCON(i) (rcons[(i)] << 24) + +static inline u32 rotr(u32 val, int bits) +{ + return (val >> bits) | (val << (32 - bits)); +} + +#define TE0(i) Te0[((i) >> 24) & 0xff] +#define TE1(i) rotr(Te0[((i) >> 16) & 0xff], 8) +#define TE2(i) rotr(Te0[((i) >> 8) & 0xff], 16) +#define TE3(i) rotr(Te0[(i) & 0xff], 24) +#define TE41(i) ((Te0[((i) >> 24) & 0xff] << 8) & 0xff000000) +#define TE42(i) (Te0[((i) >> 16) & 0xff] & 0x00ff0000) +#define TE43(i) (Te0[((i) >> 8) & 0xff] & 0x0000ff00) +#define TE44(i) ((Te0[(i) & 0xff] >> 8) & 0x000000ff) +#define TE421(i) ((Te0[((i) >> 16) & 0xff] << 8) & 0xff000000) +#define TE432(i) (Te0[((i) >> 8) & 0xff] & 0x00ff0000) +#define TE443(i) (Te0[(i) & 0xff] & 0x0000ff00) +#define TE414(i) ((Te0[((i) >> 24) & 0xff] >> 8) & 0x000000ff) +#define TE4(i) ((Te0[(i)] >> 8) & 0x000000ff) + +#define TD0(i) Td0[((i) >> 24) & 0xff] +#define TD1(i) rotr(Td0[((i) >> 16) & 0xff], 8) +#define TD2(i) rotr(Td0[((i) >> 8) & 0xff], 16) +#define TD3(i) rotr(Td0[(i) & 0xff], 24) +#define TD41(i) (Td4s[((i) >> 24) & 0xff] << 24) +#define TD42(i) (Td4s[((i) >> 16) & 0xff] << 16) +#define TD43(i) (Td4s[((i) >> 8) & 0xff] << 8) +#define TD44(i) (Td4s[(i) & 0xff]) +#define TD0_(i) Td0[(i) & 0xff] +#define TD1_(i) rotr(Td0[(i) & 0xff], 8) +#define TD2_(i) rotr(Td0[(i) & 0xff], 16) +#define TD3_(i) rotr(Td0[(i) & 0xff], 24) + +#endif /* AES_SMALL_TABLES */ #define SWAP(x) (_lrotl(x, 8) & 0x00ff00ff | _lrotr(x, 8) & 0xff00ff00) @@ -755,11 +876,8 @@ void rijndaelKeySetupEnc(u32 rk[/*44*/], const u8 cipherKey[]) for (i = 0; i < 10; i++) { temp = rk[3]; rk[4] = rk[0] ^ - (Te4[(temp >> 16) & 0xff] & 0xff000000) ^ - (Te4[(temp >> 8) & 0xff] & 0x00ff0000) ^ - (Te4[(temp ) & 0xff] & 0x0000ff00) ^ - (Te4[(temp >> 24) ] & 0x000000ff) ^ - rcon[i]; + TE421(temp) ^ TE432(temp) ^ TE443(temp) ^ TE414(temp) ^ + RCON(i); rk[5] = rk[1] ^ rk[4]; rk[6] = rk[2] ^ rk[5]; rk[7] = rk[3] ^ rk[6]; @@ -790,33 +908,19 @@ void rijndaelKeySetupDec(u32 rk[/*44*/], const u8 cipherKey[]) * first and the last: */ for (i = 1; i < Nr; i++) { rk += 4; - rk[0] = - Td0[Te4[(rk[0] >> 24) ] & 0xff] ^ - Td1[Te4[(rk[0] >> 16) & 0xff] & 0xff] ^ - Td2[Te4[(rk[0] >> 8) & 0xff] & 0xff] ^ - Td3[Te4[(rk[0] ) & 0xff] & 0xff]; - rk[1] = - Td0[Te4[(rk[1] >> 24) ] & 0xff] ^ - Td1[Te4[(rk[1] >> 16) & 0xff] & 0xff] ^ - Td2[Te4[(rk[1] >> 8) & 0xff] & 0xff] ^ - Td3[Te4[(rk[1] ) & 0xff] & 0xff]; - rk[2] = - Td0[Te4[(rk[2] >> 24) ] & 0xff] ^ - Td1[Te4[(rk[2] >> 16) & 0xff] & 0xff] ^ - Td2[Te4[(rk[2] >> 8) & 0xff] & 0xff] ^ - Td3[Te4[(rk[2] ) & 0xff] & 0xff]; - rk[3] = - Td0[Te4[(rk[3] >> 24) ] & 0xff] ^ - Td1[Te4[(rk[3] >> 16) & 0xff] & 0xff] ^ - Td2[Te4[(rk[3] >> 8) & 0xff] & 0xff] ^ - Td3[Te4[(rk[3] ) & 0xff] & 0xff]; + for (j = 0; j < 4; j++) { + rk[j] = TD0_(TE4((rk[j] >> 24) )) ^ + TD1_(TE4((rk[j] >> 16) & 0xff)) ^ + TD2_(TE4((rk[j] >> 8) & 0xff)) ^ + TD3_(TE4((rk[j] ) & 0xff)); + } } } void rijndaelEncrypt(const u32 rk[/*44*/], const u8 pt[16], u8 ct[16]) { u32 s0, s1, s2, s3, t0, t1, t2, t3; - int Nr = 10; + const int Nr = 10; #ifndef FULL_UNROLL int r; #endif /* ?FULL_UNROLL */ @@ -829,153 +933,61 @@ void rijndaelEncrypt(const u32 rk[/*44*/], const u8 pt[16], u8 ct[16]) s1 = GETU32(pt + 4) ^ rk[1]; s2 = GETU32(pt + 8) ^ rk[2]; s3 = GETU32(pt + 12) ^ rk[3]; + +#define ROUND(i,d,s) \ +d##0 = TE0(s##0) ^ TE1(s##1) ^ TE2(s##2) ^ TE3(s##3) ^ rk[4 * i]; \ +d##1 = TE0(s##1) ^ TE1(s##2) ^ TE2(s##3) ^ TE3(s##0) ^ rk[4 * i + 1]; \ +d##2 = TE0(s##2) ^ TE1(s##3) ^ TE2(s##0) ^ TE3(s##1) ^ rk[4 * i + 2]; \ +d##3 = TE0(s##3) ^ TE1(s##0) ^ TE2(s##1) ^ TE3(s##2) ^ rk[4 * i + 3] + #ifdef FULL_UNROLL - /* round 1: */ - t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[ 4]; - t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[ 5]; - t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[ 6]; - t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[ 7]; - /* round 2: */ - s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[ 8]; - s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[ 9]; - s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[10]; - s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[11]; - /* round 3: */ - t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[12]; - t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[13]; - t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[14]; - t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[15]; - /* round 4: */ - s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[16]; - s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[17]; - s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[18]; - s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[19]; - /* round 5: */ - t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[20]; - t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[21]; - t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[22]; - t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[23]; - /* round 6: */ - s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[24]; - s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[25]; - s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[26]; - s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[27]; - /* round 7: */ - t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[28]; - t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[29]; - t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[30]; - t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[31]; - /* round 8: */ - s0 = Te0[t0 >> 24] ^ Te1[(t1 >> 16) & 0xff] ^ Te2[(t2 >> 8) & 0xff] ^ Te3[t3 & 0xff] ^ rk[32]; - s1 = Te0[t1 >> 24] ^ Te1[(t2 >> 16) & 0xff] ^ Te2[(t3 >> 8) & 0xff] ^ Te3[t0 & 0xff] ^ rk[33]; - s2 = Te0[t2 >> 24] ^ Te1[(t3 >> 16) & 0xff] ^ Te2[(t0 >> 8) & 0xff] ^ Te3[t1 & 0xff] ^ rk[34]; - s3 = Te0[t3 >> 24] ^ Te1[(t0 >> 16) & 0xff] ^ Te2[(t1 >> 8) & 0xff] ^ Te3[t2 & 0xff] ^ rk[35]; - /* round 9: */ - t0 = Te0[s0 >> 24] ^ Te1[(s1 >> 16) & 0xff] ^ Te2[(s2 >> 8) & 0xff] ^ Te3[s3 & 0xff] ^ rk[36]; - t1 = Te0[s1 >> 24] ^ Te1[(s2 >> 16) & 0xff] ^ Te2[(s3 >> 8) & 0xff] ^ Te3[s0 & 0xff] ^ rk[37]; - t2 = Te0[s2 >> 24] ^ Te1[(s3 >> 16) & 0xff] ^ Te2[(s0 >> 8) & 0xff] ^ Te3[s1 & 0xff] ^ rk[38]; - t3 = Te0[s3 >> 24] ^ Te1[(s0 >> 16) & 0xff] ^ Te2[(s1 >> 8) & 0xff] ^ Te3[s2 & 0xff] ^ rk[39]; - rk += Nr << 2; + + ROUND(1,t,s); + ROUND(2,s,t); + ROUND(3,t,s); + ROUND(4,s,t); + ROUND(5,t,s); + ROUND(6,s,t); + ROUND(7,t,s); + ROUND(8,s,t); + ROUND(9,t,s); + + rk += Nr << 2; + #else /* !FULL_UNROLL */ - /* - * Nr - 1 full rounds: - */ - r = Nr >> 1; - for (;;) { - t0 = - Te0[(s0 >> 24) ] ^ - Te1[(s1 >> 16) & 0xff] ^ - Te2[(s2 >> 8) & 0xff] ^ - Te3[(s3 ) & 0xff] ^ - rk[4]; - t1 = - Te0[(s1 >> 24) ] ^ - Te1[(s2 >> 16) & 0xff] ^ - Te2[(s3 >> 8) & 0xff] ^ - Te3[(s0 ) & 0xff] ^ - rk[5]; - t2 = - Te0[(s2 >> 24) ] ^ - Te1[(s3 >> 16) & 0xff] ^ - Te2[(s0 >> 8) & 0xff] ^ - Te3[(s1 ) & 0xff] ^ - rk[6]; - t3 = - Te0[(s3 >> 24) ] ^ - Te1[(s0 >> 16) & 0xff] ^ - Te2[(s1 >> 8) & 0xff] ^ - Te3[(s2 ) & 0xff] ^ - rk[7]; - rk += 8; - if (--r == 0) { - break; - } + /* Nr - 1 full rounds: */ + r = Nr >> 1; + for (;;) { + ROUND(1,t,s); + rk += 8; + if (--r == 0) + break; + ROUND(0,s,t); + } - s0 = - Te0[(t0 >> 24) ] ^ - Te1[(t1 >> 16) & 0xff] ^ - Te2[(t2 >> 8) & 0xff] ^ - Te3[(t3 ) & 0xff] ^ - rk[0]; - s1 = - Te0[(t1 >> 24) ] ^ - Te1[(t2 >> 16) & 0xff] ^ - Te2[(t3 >> 8) & 0xff] ^ - Te3[(t0 ) & 0xff] ^ - rk[1]; - s2 = - Te0[(t2 >> 24) ] ^ - Te1[(t3 >> 16) & 0xff] ^ - Te2[(t0 >> 8) & 0xff] ^ - Te3[(t1 ) & 0xff] ^ - rk[2]; - s3 = - Te0[(t3 >> 24) ] ^ - Te1[(t0 >> 16) & 0xff] ^ - Te2[(t1 >> 8) & 0xff] ^ - Te3[(t2 ) & 0xff] ^ - rk[3]; - } #endif /* ?FULL_UNROLL */ - /* + +#undef ROUND + + /* * apply last round and * map cipher state to byte array block: */ - s0 = - (Te4[(t0 >> 24) ] & 0xff000000) ^ - (Te4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ - (Te4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ - (Te4[(t3 ) & 0xff] & 0x000000ff) ^ - rk[0]; + s0 = TE41(t0) ^ TE42(t1) ^ TE43(t2) ^ TE44(t3) ^ rk[0]; PUTU32(ct , s0); - s1 = - (Te4[(t1 >> 24) ] & 0xff000000) ^ - (Te4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ - (Te4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ - (Te4[(t0 ) & 0xff] & 0x000000ff) ^ - rk[1]; + s1 = TE41(t1) ^ TE42(t2) ^ TE43(t3) ^ TE44(t0) ^ rk[1]; PUTU32(ct + 4, s1); - s2 = - (Te4[(t2 >> 24) ] & 0xff000000) ^ - (Te4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ - (Te4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ - (Te4[(t1 ) & 0xff] & 0x000000ff) ^ - rk[2]; + s2 = TE41(t2) ^ TE42(t3) ^ TE43(t0) ^ TE44(t1) ^ rk[2]; PUTU32(ct + 8, s2); - s3 = - (Te4[(t3 >> 24) ] & 0xff000000) ^ - (Te4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ - (Te4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ - (Te4[(t2 ) & 0xff] & 0x000000ff) ^ - rk[3]; + s3 = TE41(t3) ^ TE42(t0) ^ TE43(t1) ^ TE44(t2) ^ rk[3]; PUTU32(ct + 12, s3); } void rijndaelDecrypt(const u32 rk[/*44*/], const u8 ct[16], u8 pt[16]) { u32 s0, s1, s2, s3, t0, t1, t2, t3; - int Nr = 10; + const int Nr = 10; #ifndef FULL_UNROLL int r; #endif /* ?FULL_UNROLL */ @@ -984,149 +996,110 @@ void rijndaelDecrypt(const u32 rk[/*44*/], const u8 ct[16], u8 pt[16]) * map byte array block to cipher state * and add initial round key: */ - s0 = GETU32(ct ) ^ rk[0]; - s1 = GETU32(ct + 4) ^ rk[1]; - s2 = GETU32(ct + 8) ^ rk[2]; - s3 = GETU32(ct + 12) ^ rk[3]; + s0 = GETU32(ct ) ^ rk[0]; + s1 = GETU32(ct + 4) ^ rk[1]; + s2 = GETU32(ct + 8) ^ rk[2]; + s3 = GETU32(ct + 12) ^ rk[3]; + +#define ROUND(i,d,s) \ +d##0 = TD0(s##0) ^ TD1(s##3) ^ TD2(s##2) ^ TD3(s##1) ^ rk[4 * i]; \ +d##1 = TD0(s##1) ^ TD1(s##0) ^ TD2(s##3) ^ TD3(s##2) ^ rk[4 * i + 1]; \ +d##2 = TD0(s##2) ^ TD1(s##1) ^ TD2(s##0) ^ TD3(s##3) ^ rk[4 * i + 2]; \ +d##3 = TD0(s##3) ^ TD1(s##2) ^ TD2(s##1) ^ TD3(s##0) ^ rk[4 * i + 3] + #ifdef FULL_UNROLL - /* round 1: */ - t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[ 4]; - t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[ 5]; - t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[ 6]; - t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[ 7]; - /* round 2: */ - s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[ 8]; - s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[ 9]; - s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[10]; - s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[11]; - /* round 3: */ - t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[12]; - t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[13]; - t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[14]; - t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[15]; - /* round 4: */ - s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[16]; - s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[17]; - s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[18]; - s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[19]; - /* round 5: */ - t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[20]; - t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[21]; - t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[22]; - t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[23]; - /* round 6: */ - s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[24]; - s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[25]; - s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[26]; - s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[27]; - /* round 7: */ - t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[28]; - t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[29]; - t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[30]; - t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[31]; - /* round 8: */ - s0 = Td0[t0 >> 24] ^ Td1[(t3 >> 16) & 0xff] ^ Td2[(t2 >> 8) & 0xff] ^ Td3[t1 & 0xff] ^ rk[32]; - s1 = Td0[t1 >> 24] ^ Td1[(t0 >> 16) & 0xff] ^ Td2[(t3 >> 8) & 0xff] ^ Td3[t2 & 0xff] ^ rk[33]; - s2 = Td0[t2 >> 24] ^ Td1[(t1 >> 16) & 0xff] ^ Td2[(t0 >> 8) & 0xff] ^ Td3[t3 & 0xff] ^ rk[34]; - s3 = Td0[t3 >> 24] ^ Td1[(t2 >> 16) & 0xff] ^ Td2[(t1 >> 8) & 0xff] ^ Td3[t0 & 0xff] ^ rk[35]; - /* round 9: */ - t0 = Td0[s0 >> 24] ^ Td1[(s3 >> 16) & 0xff] ^ Td2[(s2 >> 8) & 0xff] ^ Td3[s1 & 0xff] ^ rk[36]; - t1 = Td0[s1 >> 24] ^ Td1[(s0 >> 16) & 0xff] ^ Td2[(s3 >> 8) & 0xff] ^ Td3[s2 & 0xff] ^ rk[37]; - t2 = Td0[s2 >> 24] ^ Td1[(s1 >> 16) & 0xff] ^ Td2[(s0 >> 8) & 0xff] ^ Td3[s3 & 0xff] ^ rk[38]; - t3 = Td0[s3 >> 24] ^ Td1[(s2 >> 16) & 0xff] ^ Td2[(s1 >> 8) & 0xff] ^ Td3[s0 & 0xff] ^ rk[39]; + + ROUND(1,t,s); + ROUND(2,s,t); + ROUND(3,t,s); + ROUND(4,s,t); + ROUND(5,t,s); + ROUND(6,s,t); + ROUND(7,t,s); + ROUND(8,s,t); + ROUND(9,t,s); + rk += Nr << 2; + #else /* !FULL_UNROLL */ - /* - * Nr - 1 full rounds: - */ - r = Nr >> 1; - for (;;) { - t0 = - Td0[(s0 >> 24) ] ^ - Td1[(s3 >> 16) & 0xff] ^ - Td2[(s2 >> 8) & 0xff] ^ - Td3[(s1 ) & 0xff] ^ - rk[4]; - t1 = - Td0[(s1 >> 24) ] ^ - Td1[(s0 >> 16) & 0xff] ^ - Td2[(s3 >> 8) & 0xff] ^ - Td3[(s2 ) & 0xff] ^ - rk[5]; - t2 = - Td0[(s2 >> 24) ] ^ - Td1[(s1 >> 16) & 0xff] ^ - Td2[(s0 >> 8) & 0xff] ^ - Td3[(s3 ) & 0xff] ^ - rk[6]; - t3 = - Td0[(s3 >> 24) ] ^ - Td1[(s2 >> 16) & 0xff] ^ - Td2[(s1 >> 8) & 0xff] ^ - Td3[(s0 ) & 0xff] ^ - rk[7]; - rk += 8; - if (--r == 0) { - break; - } + /* Nr - 1 full rounds: */ + r = Nr >> 1; + for (;;) { + ROUND(1,t,s); + rk += 8; + if (--r == 0) + break; + ROUND(0,s,t); + } - s0 = - Td0[(t0 >> 24) ] ^ - Td1[(t3 >> 16) & 0xff] ^ - Td2[(t2 >> 8) & 0xff] ^ - Td3[(t1 ) & 0xff] ^ - rk[0]; - s1 = - Td0[(t1 >> 24) ] ^ - Td1[(t0 >> 16) & 0xff] ^ - Td2[(t3 >> 8) & 0xff] ^ - Td3[(t2 ) & 0xff] ^ - rk[1]; - s2 = - Td0[(t2 >> 24) ] ^ - Td1[(t1 >> 16) & 0xff] ^ - Td2[(t0 >> 8) & 0xff] ^ - Td3[(t3 ) & 0xff] ^ - rk[2]; - s3 = - Td0[(t3 >> 24) ] ^ - Td1[(t2 >> 16) & 0xff] ^ - Td2[(t1 >> 8) & 0xff] ^ - Td3[(t0 ) & 0xff] ^ - rk[3]; - } #endif /* ?FULL_UNROLL */ - /* + +#undef ROUND + + /* * apply last round and * map cipher state to byte array block: */ - s0 = - (Td4[(t0 >> 24) ] & 0xff000000) ^ - (Td4[(t3 >> 16) & 0xff] & 0x00ff0000) ^ - (Td4[(t2 >> 8) & 0xff] & 0x0000ff00) ^ - (Td4[(t1 ) & 0xff] & 0x000000ff) ^ - rk[0]; + s0 = TD41(t0) ^ TD42(t3) ^ TD43(t2) ^ TD44(t1) ^ rk[0]; PUTU32(pt , s0); - s1 = - (Td4[(t1 >> 24) ] & 0xff000000) ^ - (Td4[(t0 >> 16) & 0xff] & 0x00ff0000) ^ - (Td4[(t3 >> 8) & 0xff] & 0x0000ff00) ^ - (Td4[(t2 ) & 0xff] & 0x000000ff) ^ - rk[1]; + s1 = TD41(t1) ^ TD42(t0) ^ TD43(t3) ^ TD44(t2) ^ rk[1]; PUTU32(pt + 4, s1); - s2 = - (Td4[(t2 >> 24) ] & 0xff000000) ^ - (Td4[(t1 >> 16) & 0xff] & 0x00ff0000) ^ - (Td4[(t0 >> 8) & 0xff] & 0x0000ff00) ^ - (Td4[(t3 ) & 0xff] & 0x000000ff) ^ - rk[2]; + s2 = TD41(t2) ^ TD42(t1) ^ TD43(t0) ^ TD44(t3) ^ rk[2]; PUTU32(pt + 8, s2); - s3 = - (Td4[(t3 >> 24) ] & 0xff000000) ^ - (Td4[(t2 >> 16) & 0xff] & 0x00ff0000) ^ - (Td4[(t1 >> 8) & 0xff] & 0x0000ff00) ^ - (Td4[(t0 ) & 0xff] & 0x000000ff) ^ - rk[3]; + s3 = TD41(t3) ^ TD42(t2) ^ TD43(t1) ^ TD44(t0) ^ rk[3]; PUTU32(pt + 12, s3); } + + + +/* Generic wrapper functions for AES functions */ + +void * aes_encrypt_init(const u8 *key, size_t len) +{ + u32 *rk; + if (len != 16) + return NULL; + rk = malloc(4 * 44); + if (rk == NULL) + return NULL; + rijndaelKeySetupEnc(rk, key); + return rk; +} + + +void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +{ + rijndaelEncrypt(ctx, plain, crypt); +} + + +void aes_encrypt_deinit(void *ctx) +{ + free(ctx); +} + + +void * aes_decrypt_init(const u8 *key, size_t len) +{ + u32 *rk; + if (len != 16) + return NULL; + rk = malloc(4 * 44); + if (rk == NULL) + return NULL; + rijndaelKeySetupDec(rk, key); + return rk; +} + + +void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) +{ + rijndaelDecrypt(ctx, crypt, plain); +} + + +void aes_decrypt_deinit(void *ctx) +{ + free(ctx); +} diff --git a/contrib/hostapd/aes_wrap.c b/contrib/hostapd/aes_wrap.c index dbcc136..a5925ca 100644 --- a/contrib/hostapd/aes_wrap.c +++ b/contrib/hostapd/aes_wrap.c @@ -1,10 +1,13 @@ /* - * AES Key Wrap Algorithm (128-bit KEK) (RFC3394) - * One-Key CBC MAC (OMAC1) hash with AES-128 - * AES-128 CTR mode encryption - * AES-128 EAX mode encryption/decryption - * AES-128 CBC - * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi> + * AES-based functions + * + * - AES Key Wrap Algorithm (128-bit KEK) (RFC3394) + * - One-Key CBC MAC (OMAC1) hash with AES-128 + * - AES-128 CTR mode encryption + * - AES-128 EAX mode encryption/decryption + * - AES-128 CBC + * + * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -21,43 +24,26 @@ #include <string.h> #include "common.h" #include "aes_wrap.h" +#include "crypto.h" -#ifdef EAP_TLS_FUNCS - -#include <openssl/aes.h> - -#else /* EAP_TLS_FUNCS */ - +#ifndef EAP_TLS_FUNCS #include "aes.c" - -struct aes_key_st { - u32 rk[44]; -}; -typedef struct aes_key_st AES_KEY; - -#define AES_set_encrypt_key(userKey, bits, key) \ - rijndaelKeySetupEnc((key)->rk, (userKey)) -#define AES_set_decrypt_key(userKey, bits, key) \ - rijndaelKeySetupDec((key)->rk, (userKey)) -#define AES_encrypt(in, out, key) \ - rijndaelEncrypt((key)->rk, in, out) -#define AES_decrypt(in, out, key) \ - rijndaelDecrypt((key)->rk, in, out) - #endif /* EAP_TLS_FUNCS */ -/* - * @kek: key encryption key (KEK) - * @n: length of the wrapped key in 64-bit units; e.g., 2 = 128-bit = 16 bytes - * @plain: plaintext key to be wrapped, n * 64 bit - * @cipher: wrapped key, (n + 1) * 64 bit +/** + * aes_wrap - Wrap keys with AES Key Wrap Algorithm (128-bit KEK) (RFC3394) + * @kek: Key encryption key (KEK) + * @n: Length of the wrapped key in 64-bit units; e.g., 2 = 128-bit = 16 bytes + * @plain: Plaintext key to be wrapped, n * 64 bit + * @cipher: Wrapped key, (n + 1) * 64 bit + * Returns: 0 on success, -1 on failure */ -void aes_wrap(u8 *kek, int n, u8 *plain, u8 *cipher) +int aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher) { u8 *a, *r, b[16]; int i, j; - AES_KEY key; + void *ctx; a = cipher; r = cipher + 8; @@ -66,7 +52,9 @@ void aes_wrap(u8 *kek, int n, u8 *plain, u8 *cipher) memset(a, 0xa6, 8); memcpy(r, plain, 8 * n); - AES_set_encrypt_key(kek, 128, &key); + ctx = aes_encrypt_init(kek, 16); + if (ctx == NULL) + return -1; /* 2) Calculate intermediate values. * For j = 0 to 5 @@ -80,40 +68,47 @@ void aes_wrap(u8 *kek, int n, u8 *plain, u8 *cipher) for (i = 1; i <= n; i++) { memcpy(b, a, 8); memcpy(b + 8, r, 8); - AES_encrypt(b, b, &key); + aes_encrypt(ctx, b, b); memcpy(a, b, 8); a[7] ^= n * j + i; memcpy(r, b + 8, 8); r += 8; } } + aes_encrypt_deinit(ctx); /* 3) Output the results. * * These are already in @cipher due to the location of temporary * variables. */ + + return 0; } -/* - * @kek: key encryption key (KEK) - * @n: length of the wrapped key in 64-bit units; e.g., 2 = 128-bit = 16 bytes - * @cipher: wrapped key to be unwrapped, (n + 1) * 64 bit - * @plain: plaintext key, n * 64 bit +/** + * aes_unwrap - Unwrap key with AES Key Wrap Algorithm (128-bit KEK) (RFC3394) + * @kek: Key encryption key (KEK) + * @n: Length of the wrapped key in 64-bit units; e.g., 2 = 128-bit = 16 bytes + * @cipher: Wrapped key to be unwrapped, (n + 1) * 64 bit + * @plain: Plaintext key, n * 64 bit + * Returns: 0 on success, -1 on failure (e.g., integrity verification failed) */ -int aes_unwrap(u8 *kek, int n, u8 *cipher, u8 *plain) +int aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain) { u8 a[8], *r, b[16]; int i, j; - AES_KEY key; + void *ctx; /* 1) Initialize variables. */ memcpy(a, cipher, 8); r = plain; memcpy(r, cipher + 8, 8 * n); - AES_set_decrypt_key(kek, 128, &key); + ctx = aes_decrypt_init(kek, 16); + if (ctx == NULL) + return -1; /* 2) Compute intermediate values. * For j = 5 to 0 @@ -129,12 +124,13 @@ int aes_unwrap(u8 *kek, int n, u8 *cipher, u8 *plain) b[7] ^= n * j + i; memcpy(b + 8, r, 8); - AES_decrypt(b, b, &key); + aes_decrypt(ctx, b, b); memcpy(a, b, 8); memcpy(r, b + 8, 8); r -= 8; } } + aes_decrypt_deinit(ctx); /* 3) Output results. * @@ -165,27 +161,37 @@ static void gf_mulx(u8 *pad) } -void omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac) +/** + * omac1_aes_128 - One-Key CBC MAC (OMAC1) hash with AES-128 + * @key: Key for the hash operation + * @data: Data buffer for which a MAC is determined + * @data: Length of data buffer in bytes + * @mac: Buffer for MAC (128 bits, i.e., 16 bytes) + * Returns: 0 on success, -1 on failure + */ +int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac) { - AES_KEY akey; + void *ctx; u8 cbc[BLOCK_SIZE], pad[BLOCK_SIZE]; const u8 *pos = data; int i; size_t left = data_len; - AES_set_encrypt_key(key, 128, &akey); + ctx = aes_encrypt_init(key, 16); + if (ctx == NULL) + return -1; memset(cbc, 0, BLOCK_SIZE); while (left >= BLOCK_SIZE) { for (i = 0; i < BLOCK_SIZE; i++) cbc[i] ^= *pos++; if (left > BLOCK_SIZE) - AES_encrypt(cbc, cbc, &akey); + aes_encrypt(ctx, cbc, cbc); left -= BLOCK_SIZE; } memset(pad, 0, BLOCK_SIZE); - AES_encrypt(pad, pad, &akey); + aes_encrypt(ctx, pad, pad); gf_mulx(pad); if (left || data_len == 0) { @@ -197,32 +203,55 @@ void omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac) for (i = 0; i < BLOCK_SIZE; i++) pad[i] ^= cbc[i]; - AES_encrypt(pad, mac, &akey); + aes_encrypt(ctx, pad, mac); + aes_encrypt_deinit(ctx); + return 0; } -void aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out) +/** + * aes_128_encrypt_block - Perform one AES 128-bit block operation + * @key: Key for AES + * @in: Input data (16 bytes) + * @out: Output of the AES block operation (16 bytes) + * Returns: 0 on success, -1 on failure + */ +int aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out) { - AES_KEY akey; - AES_set_encrypt_key(key, 128, &akey); - AES_encrypt(in, out, &akey); + void *ctx; + ctx = aes_encrypt_init(key, 16); + if (ctx == NULL) + return -1; + aes_encrypt(ctx, in, out); + aes_encrypt_deinit(ctx); + return 0; } -void aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, - u8 *data, size_t data_len) +/** + * aes_128_ctr_encrypt - AES-128 CTR mode encryption + * @key: Key for encryption (16 bytes) + * @nonce: Nonce for counter mode (16 bytes) + * @data: Data to encrypt in-place + * @data_len: Length of data in bytes + * Returns: 0 on success, -1 on failure + */ +int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, + u8 *data, size_t data_len) { - AES_KEY akey; + void *ctx; size_t len, left = data_len; int i; u8 *pos = data; u8 counter[BLOCK_SIZE], buf[BLOCK_SIZE]; - AES_set_encrypt_key(key, 128, &akey); + ctx = aes_encrypt_init(key, 16); + if (ctx == NULL) + return -1; memcpy(counter, nonce, BLOCK_SIZE); while (left > 0) { - AES_encrypt(counter, buf, &akey); + aes_encrypt(ctx, counter, buf); len = (left < BLOCK_SIZE) ? left : BLOCK_SIZE; for (i = 0; i < len; i++) @@ -236,9 +265,23 @@ void aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, break; } } + aes_encrypt_deinit(ctx); + return 0; } +/** + * aes_128_eax_encrypt - AES-128 EAX mode encryption + * @key: Key for encryption (16 bytes) + * @nonce: Nonce for counter mode + * @nonce_len: Nonce length in bytes + * @hdr: Header data to be authenticity protected + * @hdr_len: Length of the header data bytes + * @data: Data to encrypt in-place + * @data_len: Length of data in bytes + * @tag: 16-byte tag value + * Returns: 0 on success, -1 on failure + */ int aes_128_eax_encrypt(const u8 *key, const u8 *nonce, size_t nonce_len, const u8 *hdr, size_t hdr_len, u8 *data, size_t data_len, u8 *tag) @@ -284,6 +327,18 @@ int aes_128_eax_encrypt(const u8 *key, const u8 *nonce, size_t nonce_len, } +/** + * aes_128_eax_decrypt - AES-128 EAX mode decryption + * @key: Key for decryption (16 bytes) + * @nonce: Nonce for counter mode + * @nonce_len: Nonce length in bytes + * @hdr: Header data to be authenticity protected + * @hdr_len: Length of the header data bytes + * @data: Data to encrypt in-place + * @data_len: Length of data in bytes + * @tag: 16-byte tag value + * Returns: 0 on success, -1 on failure, -2 if tag does not match + */ int aes_128_eax_decrypt(const u8 *key, const u8 *nonce, size_t nonce_len, const u8 *hdr, size_t hdr_len, u8 *data, size_t data_len, const u8 *tag) @@ -332,48 +387,70 @@ int aes_128_eax_decrypt(const u8 *key, const u8 *nonce, size_t nonce_len, } -void aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, - size_t data_len) +/** + * aes_128_cbc_encrypt - AES-128 CBC encryption + * @key: Encryption key + * @iv: Encryption IV for CBC mode (16 bytes) + * @data: Data to encrypt in-place + * @data_len: Length of data in bytes (must be divisible by 16) + * Returns: 0 on success, -1 on failure + */ +int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) { - AES_KEY akey; + void *ctx; u8 cbc[BLOCK_SIZE]; u8 *pos = data; int i, j, blocks; - AES_set_encrypt_key(key, 128, &akey); + ctx = aes_encrypt_init(key, 16); + if (ctx == NULL) + return -1; memcpy(cbc, iv, BLOCK_SIZE); blocks = data_len / BLOCK_SIZE; for (i = 0; i < blocks; i++) { for (j = 0; j < BLOCK_SIZE; j++) cbc[j] ^= pos[j]; - AES_encrypt(cbc, cbc, &akey); + aes_encrypt(ctx, cbc, cbc); memcpy(pos, cbc, BLOCK_SIZE); pos += BLOCK_SIZE; } + aes_encrypt_deinit(ctx); + return 0; } -void aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, - size_t data_len) +/** + * aes_128_cbc_decrypt - AES-128 CBC decryption + * @key: Decryption key + * @iv: Decryption IV for CBC mode (16 bytes) + * @data: Data to decrypt in-place + * @data_len: Length of data in bytes (must be divisible by 16) + * Returns: 0 on success, -1 on failure + */ +int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, size_t data_len) { - AES_KEY akey; + void *ctx; u8 cbc[BLOCK_SIZE], tmp[BLOCK_SIZE]; u8 *pos = data; int i, j, blocks; - AES_set_decrypt_key(key, 128, &akey); + ctx = aes_decrypt_init(key, 16); + if (ctx == NULL) + return -1; memcpy(cbc, iv, BLOCK_SIZE); blocks = data_len / BLOCK_SIZE; for (i = 0; i < blocks; i++) { memcpy(tmp, pos, BLOCK_SIZE); - AES_decrypt(pos, pos, &akey); + aes_decrypt(ctx, pos, pos); for (j = 0; j < BLOCK_SIZE; j++) pos[j] ^= cbc[j]; memcpy(cbc, tmp, BLOCK_SIZE); pos += BLOCK_SIZE; } + aes_decrypt_deinit(ctx); + return 0; } @@ -388,25 +465,28 @@ static void test_aes_perf(void) const int num_iters = 10; int i; unsigned int start, end; - AES_KEY akey; u8 key[16], pt[16], ct[16]; + void *ctx; printf("keySetupEnc:"); for (i = 0; i < num_iters; i++) { rdtscll(start); - AES_set_encrypt_key(key, 128, &akey); + ctx = aes_encrypt_init(key, 16); rdtscll(end); + aes_encrypt_deinit(ctx); printf(" %d", end - start); } printf("\n"); printf("Encrypt:"); + ctx = aes_encrypt_init(key, 16); for (i = 0; i < num_iters; i++) { rdtscll(start); - AES_encrypt(pt, ct, &akey); + aes_encrypt(ctx, pt, ct); rdtscll(end); printf(" %d", end - start); } + aes_encrypt_deinit(ctx); printf("\n"); } #endif /* __i386__ */ @@ -599,7 +679,10 @@ int main(int argc, char *argv[]) int ret = 0, i; struct omac1_test_vector *tv; - aes_wrap(kek, 2, plain, result); + if (aes_wrap(kek, 2, plain, result)) { + printf("AES-WRAP-128-128 reported failure\n"); + ret++; + } if (memcmp(result, crypt, 24) != 0) { printf("AES-WRAP-128-128 failed\n"); ret++; diff --git a/contrib/hostapd/aes_wrap.h b/contrib/hostapd/aes_wrap.h index 70e83ea..cb1a539 100644 --- a/contrib/hostapd/aes_wrap.h +++ b/contrib/hostapd/aes_wrap.h @@ -1,21 +1,42 @@ +/* + * AES-based functions + * + * - AES Key Wrap Algorithm (128-bit KEK) (RFC3394) + * - One-Key CBC MAC (OMAC1) hash with AES-128 + * - AES-128 CTR mode encryption + * - AES-128 EAX mode encryption/decryption + * - AES-128 CBC + * + * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + #ifndef AES_WRAP_H #define AES_WRAP_H -void aes_wrap(u8 *kek, int n, u8 *plain, u8 *cipher); -int aes_unwrap(u8 *kek, int n, u8 *cipher, u8 *plain); -void omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac); -void aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out); -void aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, - u8 *data, size_t data_len); +int aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher); +int aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain); +int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac); +int aes_128_encrypt_block(const u8 *key, const u8 *in, u8 *out); +int aes_128_ctr_encrypt(const u8 *key, const u8 *nonce, + u8 *data, size_t data_len); int aes_128_eax_encrypt(const u8 *key, const u8 *nonce, size_t nonce_len, const u8 *hdr, size_t hdr_len, u8 *data, size_t data_len, u8 *tag); int aes_128_eax_decrypt(const u8 *key, const u8 *nonce, size_t nonce_len, const u8 *hdr, size_t hdr_len, u8 *data, size_t data_len, const u8 *tag); -void aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, - size_t data_len); -void aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, - size_t data_len); +int aes_128_cbc_encrypt(const u8 *key, const u8 *iv, u8 *data, + size_t data_len); +int aes_128_cbc_decrypt(const u8 *key, const u8 *iv, u8 *data, + size_t data_len); #endif /* AES_WRAP_H */ diff --git a/contrib/hostapd/common.c b/contrib/hostapd/common.c index 071ffe8..4b756d8 100644 --- a/contrib/hostapd/common.c +++ b/contrib/hostapd/common.c @@ -1,6 +1,5 @@ /* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / common helper functions, etc. + * wpa_supplicant/hostapd / common helper functions, etc. * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> * * This program is free software; you can redistribute it and/or modify @@ -22,6 +21,10 @@ #include <ctype.h> #include <time.h> #include <sys/time.h> +#ifdef CONFIG_NATIVE_WINDOWS +#include <winsock2.h> +#include <wincrypt.h> +#endif /* CONFIG_NATIVE_WINDOWS */ #include "common.h" @@ -34,12 +37,17 @@ int wpa_debug_timestamp = 0; int hostapd_get_rand(u8 *buf, size_t len) { #ifdef CONFIG_NATIVE_WINDOWS - int i; - /* FIX: use more secure pseudo random number generator */ - for (i = 0; i < len; i++) { - buf[i] = rand(); - } - return 0; + HCRYPTPROV prov; + BOOL ret; + + if (!CryptAcquireContext(&prov, NULL, NULL, PROV_RSA_FULL, + CRYPT_VERIFYCONTEXT)) + return -1; + + ret = CryptGenRandom(prov, len, buf); + CryptReleaseContext(prov, 0); + + return ret ? 0 : -1; #else /* CONFIG_NATIVE_WINDOWS */ FILE *f; size_t rc; @@ -93,6 +101,12 @@ static int hex2byte(const char *hex) } +/** + * hwaddr_aton - Convert ASCII string to MAC address + * @txt: MAC address as a string (e.g., "00:11:22:33:44:55") + * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes) + * Returns: 0 on success, -1 on failure (e.g., string not a MAC address) + */ int hwaddr_aton(const char *txt, u8 *addr) { int i; @@ -115,6 +129,14 @@ int hwaddr_aton(const char *txt, u8 *addr) } +/** + * hexstr2bin - Convert ASCII hex string into binary data + * @hex: ASCII hex string (e.g., "01ab") + * @buf: Buffer for the binary data + * @len: Length of the text to convert in bytes (of buf); hex will be double + * this size + * Returns: 0 on success, -1 on failure (invalid hex string) + */ int hexstr2bin(const char *hex, u8 *buf, size_t len) { int i, a; @@ -171,6 +193,15 @@ char * rel2abs_path(const char *rel_path) } +/** + * inc_byte_array - Increment arbitrary length byte array by one + * @counter: Pointer to byte array + * @len: Length of the counter in bytes + * + * This function increments the last byte of the counter by one and continues + * rolling over to more significant bytes if the byte was incremented from + * 0xff to 0x00. + */ void inc_byte_array(u8 *counter, size_t len) { int pos = len - 1; @@ -201,7 +232,9 @@ void fprint_char(FILE *f, char c) } -static void wpa_debug_print_timestamp(void) +#ifndef CONFIG_NO_STDOUT_DEBUG + +void wpa_debug_print_timestamp(void) { struct timeval tv; char buf[16]; @@ -218,6 +251,17 @@ static void wpa_debug_print_timestamp(void) } +/** + * wpa_printf - conditional printf + * @level: priority level (MSG_*) of the message + * @fmt: printf format string, followed by optional arguments + * + * This function is used to print conditional debugging and error messages. The + * output may be directed to stdout, stderr, and/or syslog based on + * configuration. + * + * Note: New line '\n' is added to the end of the text when printing to stdout. + */ void wpa_printf(int level, char *fmt, ...) { va_list ap; @@ -240,7 +284,9 @@ static void _wpa_hexdump(int level, const char *title, const u8 *buf, return; wpa_debug_print_timestamp(); printf("%s - hexdump(len=%lu):", title, (unsigned long) len); - if (show) { + if (buf == NULL) { + printf(" [NULL]"); + } else if (show) { for (i = 0; i < len; i++) printf(" %02x", buf[i]); } else { @@ -276,6 +322,11 @@ static void _wpa_hexdump_ascii(int level, const char *title, const u8 *buf, title, (unsigned long) len); return; } + if (buf == NULL) { + printf("%s - hexdump_ascii(len=%lu): [NULL]\n", + title, (unsigned long) len); + return; + } printf("%s - hexdump_ascii(len=%lu):\n", title, (unsigned long) len); while (len) { llen = len > line_len ? line_len : len; @@ -312,6 +363,8 @@ void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf, _wpa_hexdump_ascii(level, title, buf, len, wpa_debug_show_keys); } +#endif /* CONFIG_NO_STDOUT_DEBUG */ + #ifdef CONFIG_NATIVE_WINDOWS diff --git a/contrib/hostapd/common.h b/contrib/hostapd/common.h index 0f154e9..4bece7f 100644 --- a/contrib/hostapd/common.h +++ b/contrib/hostapd/common.h @@ -1,11 +1,26 @@ +/* + * wpa_supplicant/hostapd / common helper functions, etc. + * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published 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 COMMON_H #define COMMON_H #ifdef __linux__ #include <endian.h> #include <byteswap.h> -#endif -#ifdef __FreeBSD__ +#endif /* __linux__ */ + +#if defined(__FreeBSD__) || defined(__NetBSD__) #include <sys/types.h> #include <sys/endian.h> #define __BYTE_ORDER _BYTE_ORDER @@ -14,10 +29,9 @@ #define bswap_16 bswap16 #define bswap_32 bswap32 #define bswap_64 bswap64 -#endif +#endif /* defined(__FreeBSD__) || defined(__NetBSD__) */ #ifdef CONFIG_NATIVE_WINDOWS -#include <winsock.h> #include <winsock2.h> static inline int daemon(int nochdir, int noclose) @@ -54,6 +68,18 @@ struct timezone { int gettimeofday(struct timeval *tv, struct timezone *tz); +static inline long int random(void) +{ + return rand(); +} + +typedef int gid_t; +typedef int socklen_t; + +#ifndef MSG_DONTWAIT +#define MSG_DONTWAIT 0 /* not supported */ +#endif + #endif /* CONFIG_NATIVE_WINDOWS */ #if defined(__CYGWIN__) || defined(CONFIG_NATIVE_WINDOWS) @@ -104,6 +130,21 @@ static inline unsigned int wpa_swap_32(unsigned int v) #endif /* __CYGWIN__ */ +/* Macros for handling unaligned 16-bit variables */ +#define WPA_GET_BE16(a) ((u16) (((a)[0] << 8) | (a)[1])) +#define WPA_PUT_BE16(a, val) \ + do { \ + (a)[0] = ((u16) (val)) >> 8; \ + (a)[1] = ((u16) (val)) & 0xff; \ + } while (0) + +#define WPA_GET_LE16(a) ((u16) (((a)[1] << 8) | (a)[0])) +#define WPA_PUT_LE16(a, val) \ + do { \ + (a)[1] = ((u16) (val)) >> 8; \ + (a)[0] = ((u16) (val)) & 0xff; \ + } while (0) + #ifndef ETH_ALEN #define ETH_ALEN 6 @@ -134,6 +175,26 @@ void fprint_char(FILE *f, char c); enum { MSG_MSGDUMP, MSG_DEBUG, MSG_INFO, MSG_WARNING, MSG_ERROR }; +#ifdef CONFIG_NO_STDOUT_DEBUG + +#define wpa_debug_print_timestamp() do { } while (0) +#define wpa_printf(args...) do { } while (0) +#define wpa_hexdump(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) + +#else /* CONFIG_NO_STDOUT_DEBUG */ + +/** + * wpa_debug_printf_timestamp - Print timestamp for debug output + * + * This function prints a timestamp in <seconds from 1970>.<microsoconds> + * format if debug output has been configured to include timestamps in debug + * messages. + */ +void wpa_debug_print_timestamp(void); + /** * wpa_printf - conditional printf * @level: priority level (MSG_*) of the message @@ -153,11 +214,11 @@ __attribute__ ((format (printf, 2, 3))); * @level: priority level (MSG_*) of the message * @title: title of for the message * @buf: data buffer to be dumped - * @len: length of the @buf + * @len: length of the buf * * This function is used to print conditional debugging and error messages. The * output may be directed to stdout, stderr, and/or syslog based on - * configuration. The contents of @buf is printed out has hex dump. + * configuration. The contents of buf is printed out has hex dump. */ void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len); @@ -166,11 +227,11 @@ void wpa_hexdump(int level, const char *title, const u8 *buf, size_t len); * @level: priority level (MSG_*) of the message * @title: title of for the message * @buf: data buffer to be dumped - * @len: length of the @buf + * @len: length of the buf * * This function is used to print conditional debugging and error messages. The * output may be directed to stdout, stderr, and/or syslog based on - * configuration. The contents of @buf is printed out has hex dump. This works + * configuration. The contents of buf is printed out has hex dump. This works * like wpa_hexdump(), but by default, does not include secret keys (passwords, * etc.) in debug output. */ @@ -181,11 +242,11 @@ void wpa_hexdump_key(int level, const char *title, const u8 *buf, size_t len); * @level: priority level (MSG_*) of the message * @title: title of for the message * @buf: data buffer to be dumped - * @len: length of the @buf + * @len: length of the buf * * This function is used to print conditional debugging and error messages. The * output may be directed to stdout, stderr, and/or syslog based on - * configuration. The contents of @buf is printed out has hex dump with both + * configuration. The contents of buf is printed out has hex dump with both * the hex numbers and ASCII characters (for printable range) are shown. 16 * bytes per line will be shown. */ @@ -197,11 +258,11 @@ void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, * @level: priority level (MSG_*) of the message * @title: title of for the message * @buf: data buffer to be dumped - * @len: length of the @buf + * @len: length of the buf * * This function is used to print conditional debugging and error messages. The * output may be directed to stdout, stderr, and/or syslog based on - * configuration. The contents of @buf is printed out has hex dump with both + * configuration. The contents of buf is printed out has hex dump with both * the hex numbers and ASCII characters (for printable range) are shown. 16 * bytes per line will be shown. This works like wpa_hexdump_ascii(), but by * default, does not include secret keys (passwords, etc.) in debug output. @@ -209,6 +270,9 @@ void wpa_hexdump_ascii(int level, const char *title, const u8 *buf, void wpa_hexdump_ascii_key(int level, const char *title, const u8 *buf, size_t len); +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + #ifdef EAPOL_TEST #define WPA_ASSERT(a) \ do { \ diff --git a/contrib/hostapd/config.c b/contrib/hostapd/config.c index 02ecd79..34e2256 100644 --- a/contrib/hostapd/config.c +++ b/contrib/hostapd/config.c @@ -28,18 +28,20 @@ #include "driver.h" #include "sha1.h" #include "eap.h" +#include "radius_client.h" static struct hostapd_config *hostapd_config_defaults(void) { struct hostapd_config *conf; - conf = malloc(sizeof(*conf)); + conf = malloc(sizeof(*conf) + sizeof(struct hostapd_radius_servers)); if (conf == NULL) { printf("Failed to allocate memory for configuration data.\n"); return NULL; } - memset(conf, 0, sizeof(*conf)); + 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"); @@ -71,6 +73,24 @@ static struct hostapd_config *hostapd_config_defaults(void) } +static int hostapd_parse_ip_addr(const char *txt, struct hostapd_ip_addr *addr) +{ + if (inet_aton(txt, &addr->u.v4)) { + addr->af = AF_INET; + return 0; + } + +#ifdef CONFIG_IPV6 + if (inet_pton(AF_INET6, txt, &addr->u.v6) > 0) { + addr->af = AF_INET6; + return 0; + } +#endif /* CONFIG_IPV6 */ + + return -1; +} + + static int mac_comp(const void *a, const void *b) { return memcmp(a, b, sizeof(macaddr)); @@ -269,12 +289,12 @@ int hostapd_setup_wpa_psk(struct hostapd_config *conf) } -#ifdef EAP_AUTHENTICATOR +#ifdef EAP_SERVER static int hostapd_config_read_eap_user(const char *fname, struct hostapd_config *conf) { FILE *f; - char buf[512], *pos, *start; + char buf[512], *pos, *start, *pos2; int line = 0, ret = 0, num_methods; struct hostapd_eap_user *user, *tail = NULL; @@ -410,30 +430,53 @@ static int hostapd_config_read_eap_user(const char *fname, goto done; } - if (*pos != '"') { - printf("Invalid EAP password (no \" in start) on " - "line %d in '%s'\n", line, fname); - goto failed; - } - pos++; - start = pos; - while (*pos != '"' && *pos != '\0') + if (*pos == '"') { pos++; - if (*pos == '\0') { - printf("Invalid EAP password (no \" in end) on " - "line %d in '%s'\n", line, fname); - goto failed; - } + start = pos; + while (*pos != '"' && *pos != '\0') + pos++; + if (*pos == '\0') { + printf("Invalid EAP password (no \" in end) " + "on line %d in '%s'\n", line, fname); + goto failed; + } - user->password = malloc(pos - start); - if (user->password == NULL) { - printf("Failed to allocate memory for EAP password\n"); - goto failed; + user->password = malloc(pos - start); + if (user->password == NULL) { + printf("Failed to allocate memory for EAP " + "password\n"); + goto failed; + } + memcpy(user->password, start, pos - start); + user->password_len = pos - start; + + pos++; + } else { + pos2 = pos; + while (*pos2 != '\0' && *pos2 != ' ' && + *pos2 != '\t' && *pos2 != '#') + pos2++; + if ((pos2 - pos) & 1) { + printf("Invalid hex password on line %d in " + "'%s'\n", line, fname); + goto failed; + } + user->password = malloc((pos2 - pos) / 2); + if (user->password == NULL) { + printf("Failed to allocate memory for EAP " + "password\n"); + goto failed; + } + if (hexstr2bin(pos, user->password, + (pos2 - pos) / 2) < 0) { + printf("Invalid hex password on line %d in " + "'%s'\n", line, fname); + goto failed; + } + user->password_len = (pos2 - pos) / 2; + pos = pos2; } - memcpy(user->password, start, pos - start); - user->password_len = pos - start; - pos++; while (*pos == ' ' || *pos == '\t') pos++; if (strncmp(pos, "[2]", 3) == 0) { @@ -462,7 +505,7 @@ static int hostapd_config_read_eap_user(const char *fname, return ret; } -#endif /* EAP_AUTHENTICATOR */ +#endif /* EAP_SERVER */ static int @@ -485,7 +528,7 @@ hostapd_config_read_radius_addr(struct hostapd_radius_server **server, memset(nserv, 0, sizeof(*nserv)); nserv->port = def_port; - ret = !inet_aton(val, &nserv->addr); + ret = hostapd_parse_ip_addr(val, &nserv->addr); nserv->index = server_index++; return ret; @@ -589,8 +632,8 @@ static int hostapd_config_parse_cipher(int line, const char *value) static int hostapd_config_check(struct hostapd_config *conf) { - if (conf->ieee802_1x && !conf->eap_authenticator && - !conf->auth_servers) { + if (conf->ieee802_1x && !conf->eap_server && + !conf->radius->auth_servers) { printf("Invalid IEEE 802.1X configuration (no EAP " "authenticator configured).\n"); return -1; @@ -616,9 +659,9 @@ struct hostapd_config * hostapd_config_read(const char *fname) int line = 0; int errors = 0; char *accept_mac_file = NULL, *deny_mac_file = NULL; -#ifdef EAP_AUTHENTICATOR +#ifdef EAP_SERVER char *eap_user_file = NULL; -#endif /* EAP_AUTHENTICATOR */ +#endif /* EAP_SERVER */ f = fopen(fname, "r"); if (f == NULL) { @@ -722,9 +765,13 @@ struct hostapd_config * hostapd_config_read(const char *fname) conf->assoc_ap = 1; } else if (strcmp(buf, "ieee8021x") == 0) { conf->ieee802_1x = atoi(pos); -#ifdef EAP_AUTHENTICATOR +#ifdef EAP_SERVER } else if (strcmp(buf, "eap_authenticator") == 0) { - conf->eap_authenticator = atoi(pos); + conf->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); } else if (strcmp(buf, "eap_user_file") == 0) { free(eap_user_file); eap_user_file = strdup(pos); @@ -744,14 +791,33 @@ struct hostapd_config * hostapd_config_read(const char *fname) } else if (strcmp(buf, "private_key_passwd") == 0) { free(conf->private_key_passwd); conf->private_key_passwd = strdup(pos); + } else if (strcmp(buf, "check_crl") == 0) { + conf->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); #endif /* EAP_SIM */ -#endif /* EAP_AUTHENTICATOR */ +#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) { + 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"); + 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--; + } } else if (strcmp(buf, "wep_key_len_broadcast") == 0) { conf->default_wep_key_len = atoi(pos); if (conf->default_wep_key_len > 13) { @@ -796,7 +862,7 @@ struct hostapd_config * hostapd_config_read(const char *fname) "%s", pos); #endif /* CONFIG_IAPP */ } else if (strcmp(buf, "own_ip_addr") == 0) { - if (!inet_aton(pos, &conf->own_ip_addr)) { + if (hostapd_parse_ip_addr(pos, &conf->own_ip_addr)) { printf("Line %d: invalid IP address '%s'\n", line, pos); errors++; @@ -805,17 +871,17 @@ struct hostapd_config * hostapd_config_read(const char *fname) conf->nas_identifier = strdup(pos); } else if (strcmp(buf, "auth_server_addr") == 0) { if (hostapd_config_read_radius_addr( - &conf->auth_servers, - &conf->num_auth_servers, pos, 1812, - &conf->auth_server)) { + &conf->radius->auth_servers, + &conf->radius->num_auth_servers, pos, 1812, + &conf->radius->auth_server)) { printf("Line %d: invalid IP address '%s'\n", line, pos); errors++; } - } else if (conf->auth_server && + } else if (conf->radius->auth_server && strcmp(buf, "auth_server_port") == 0) { - conf->auth_server->port = atoi(pos); - } else if (conf->auth_server && + conf->radius->auth_server->port = atoi(pos); + } else if (conf->radius->auth_server && strcmp(buf, "auth_server_shared_secret") == 0) { int len = strlen(pos); if (len == 0) { @@ -824,21 +890,22 @@ struct hostapd_config * hostapd_config_read(const char *fname) "allowed.\n", line); errors++; } - conf->auth_server->shared_secret = (u8 *) strdup(pos); - conf->auth_server->shared_secret_len = len; + conf->radius->auth_server->shared_secret = + (u8 *) strdup(pos); + conf->radius->auth_server->shared_secret_len = len; } else if (strcmp(buf, "acct_server_addr") == 0) { if (hostapd_config_read_radius_addr( - &conf->acct_servers, - &conf->num_acct_servers, pos, 1813, - &conf->acct_server)) { + &conf->radius->acct_servers, + &conf->radius->num_acct_servers, pos, 1813, + &conf->radius->acct_server)) { printf("Line %d: invalid IP address '%s'\n", line, pos); errors++; } - } else if (conf->acct_server && + } else if (conf->radius->acct_server && strcmp(buf, "acct_server_port") == 0) { - conf->acct_server->port = atoi(pos); - } else if (conf->acct_server && + conf->radius->acct_server->port = atoi(pos); + } else if (conf->radius->acct_server && strcmp(buf, "acct_server_shared_secret") == 0) { int len = strlen(pos); if (len == 0) { @@ -847,12 +914,13 @@ struct hostapd_config * hostapd_config_read(const char *fname) "allowed.\n", line); errors++; } - conf->acct_server->shared_secret = (u8 *) strdup(pos); - conf->acct_server->shared_secret_len = len; + conf->radius->acct_server->shared_secret = + (u8 *) strdup(pos); + conf->radius->acct_server->shared_secret_len = len; } else if (strcmp(buf, "radius_retry_primary_interval") == 0) { - conf->radius_retry_primary_interval = atoi(pos); + conf->radius->retry_primary_interval = atoi(pos); } else if (strcmp(buf, "radius_acct_interim_interval") == 0) { - conf->radius_acct_interim_interval = atoi(pos); + conf->radius->acct_interim_interval = atoi(pos); } else if (strcmp(buf, "auth_algs") == 0) { conf->auth_algs = atoi(pos); if (conf->auth_algs == 0) { @@ -944,6 +1012,7 @@ struct hostapd_config * hostapd_config_read(const char *fname) grp = getgrnam(group); if (grp) { conf->ctrl_interface_gid = grp->gr_gid; + conf->ctrl_interface_gid_set = 1; wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d" " (from group name '%s')", conf->ctrl_interface_gid, group); @@ -958,6 +1027,7 @@ struct hostapd_config * hostapd_config_read(const char *fname) errors++; continue; } + conf->ctrl_interface_gid_set = 1; wpa_printf(MSG_DEBUG, "ctrl_interface_group=%d", conf->ctrl_interface_gid); #ifdef RADIUS_SERVER @@ -966,7 +1036,14 @@ struct hostapd_config * hostapd_config_read(const char *fname) conf->radius_server_clients = strdup(pos); } else if (strcmp(buf, "radius_server_auth_port") == 0) { conf->radius_server_auth_port = atoi(pos); + } else if (strcmp(buf, "radius_server_ipv6") == 0) { + conf->radius_server_ipv6 = atoi(pos); #endif /* RADIUS_SERVER */ + } else if (strcmp(buf, "test_socket") == 0) { + free(conf->test_socket); + conf->test_socket = strdup(pos); + } else if (strcmp(buf, "use_pae_group_addr") == 0) { + conf->use_pae_group_addr = atoi(pos); } else { printf("Line %d: unknown configuration item '%s'\n", line, buf); @@ -985,14 +1062,14 @@ struct hostapd_config * hostapd_config_read(const char *fname) errors++; free(deny_mac_file); -#ifdef EAP_AUTHENTICATOR +#ifdef EAP_SERVER if (hostapd_config_read_eap_user(eap_user_file, conf)) errors++; free(eap_user_file); -#endif /* EAP_AUTHENTICATOR */ +#endif /* EAP_SERVER */ - conf->auth_server = conf->auth_servers; - conf->acct_server = conf->acct_servers; + conf->radius->auth_server = conf->radius->auth_servers; + conf->radius->acct_server = conf->radius->acct_servers; if (hostapd_config_check(conf)) errors++; @@ -1058,8 +1135,10 @@ void hostapd_config_free(struct hostapd_config *conf) free(conf->accept_mac); free(conf->deny_mac); free(conf->nas_identifier); - hostapd_config_free_radius(conf->auth_servers, conf->num_auth_servers); - hostapd_config_free_radius(conf->acct_servers, conf->num_acct_servers); + hostapd_config_free_radius(conf->radius->auth_servers, + conf->radius->num_auth_servers); + hostapd_config_free_radius(conf->radius->acct_servers, + conf->radius->num_acct_servers); free(conf->rsn_preauth_interfaces); free(conf->ctrl_interface); free(conf->ca_cert); @@ -1068,6 +1147,7 @@ void hostapd_config_free(struct hostapd_config *conf) free(conf->private_key_passwd); free(conf->eap_sim_db); free(conf->radius_server_clients); + free(conf->test_socket); free(conf); } diff --git a/contrib/hostapd/config.h b/contrib/hostapd/config.h index 0ffc3ad..c56f4e7 100644 --- a/contrib/hostapd/config.h +++ b/contrib/hostapd/config.h @@ -1,34 +1,11 @@ #ifndef CONFIG_H #define CONFIG_H +#include "config_types.h" + typedef u8 macaddr[ETH_ALEN]; -struct hostapd_radius_server { - /* MIB prefix for shared variables: - * @ = radiusAuth or radiusAcc depending on the type of the server */ - struct in_addr addr; /* @ServerAddress */ - int port; /* @ClientServerPortNumber */ - u8 *shared_secret; - size_t shared_secret_len; - - /* Dynamic (not from configuration file) MIB data */ - int index; /* @ServerIndex */ - int round_trip_time; /* @ClientRoundTripTime; in hundredths of a - * second */ - u32 requests; /* @Client{Access,}Requests */ - u32 retransmissions; /* @Client{Access,}Retransmissions */ - u32 access_accepts; /* radiusAuthClientAccessAccepts */ - u32 access_rejects; /* radiusAuthClientAccessRejects */ - u32 access_challenges; /* radiusAuthClientAccessChallenges */ - u32 responses; /* radiusAccClientResponses */ - u32 malformed_responses; /* @ClientMalformed{Access,}Responses */ - u32 bad_authenticators; /* @ClientBadAuthenticators */ - u32 timeouts; /* @ClientTimeouts */ - u32 unknown_types; /* @ClientUnknownTypes */ - u32 packets_dropped; /* @ClientPacketsDropped */ - /* @ClientPendingRequests: length of hapd->radius->msgs for matching - * msg_type */ -}; +struct hostapd_radius_servers; #define PMK_LEN 32 struct hostapd_wpa_psk { @@ -80,26 +57,21 @@ struct hostapd_config { char *dump_log_name; /* file name for state dump (SIGUSR1) */ int ieee802_1x; /* use IEEE 802.1X */ - int eap_authenticator; /* Use internal EAP authenticator instead of - * external RADIUS server */ + int eap_server; /* Use internal EAP server instead of external + * RADIUS server */ struct hostapd_eap_user *eap_user; char *eap_sim_db; - struct in_addr own_ip_addr; + struct hostapd_ip_addr own_ip_addr; char *nas_identifier; - /* RADIUS Authentication and Accounting servers in priority order */ - struct hostapd_radius_server *auth_servers, *auth_server; - int num_auth_servers; - struct hostapd_radius_server *acct_servers, *acct_server; - int num_acct_servers; - - int radius_retry_primary_interval; - int radius_acct_interim_interval; + struct hostapd_radius_servers *radius; + #define HOSTAPD_SSID_LEN 32 char ssid[HOSTAPD_SSID_LEN + 1]; size_t ssid_len; int ssid_set; 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; size_t default_wep_key_len; @@ -153,14 +125,24 @@ struct hostapd_config { char *ctrl_interface; /* directory for UNIX domain sockets */ gid_t ctrl_interface_gid; + int ctrl_interface_gid_set; char *ca_cert; char *server_cert; char *private_key; char *private_key_passwd; + int check_crl; char *radius_server_clients; int radius_server_auth_port; + int radius_server_ipv6; + + char *test_socket; /* UNIX domain socket path for driver_test */ + + int use_pae_group_addr; /* Whether to send EAPOL frames to PAE group + * address instead of individual address + * (for driver_wired.c). + */ }; diff --git a/contrib/hostapd/config_types.h b/contrib/hostapd/config_types.h new file mode 100644 index 0000000..12b57cb --- /dev/null +++ b/contrib/hostapd/config_types.h @@ -0,0 +1,14 @@ +#ifndef CONFIG_TYPES_H +#define CONFIG_TYPES_H + +struct hostapd_ip_addr { + union { + struct in_addr v4; +#ifdef CONFIG_IPV6 + struct in6_addr v6; +#endif /* CONFIG_IPV6 */ + } u; + int af; /* AF_INET / AF_INET6 */ +}; + +#endif /* CONFIG_TYPES_H */ diff --git a/contrib/hostapd/crypto.c b/contrib/hostapd/crypto.c index cd278e0..b4c8189 100644 --- a/contrib/hostapd/crypto.c +++ b/contrib/hostapd/crypto.c @@ -12,11 +12,17 @@ * See README and COPYING for more details. */ +#include <string.h> +#include <sys/types.h> + #include <openssl/md4.h> +#include <openssl/md5.h> +#include <openssl/sha.h> #include <openssl/des.h> +#include <openssl/aes.h> #include "common.h" - +#include "crypto.h" #if OPENSSL_VERSION_NUMBER < 0x00907000 #define DES_key_schedule des_key_schedule @@ -27,7 +33,7 @@ #endif /* openssl < 0.9.7 */ -void md4_vector(size_t num_elem, const u8 *addr[], size_t *len, u8 *mac) +void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { MD4_CTX ctx; int i; @@ -39,17 +45,6 @@ void md4_vector(size_t num_elem, const u8 *addr[], size_t *len, u8 *mac) } -void md4(const u8 *addr, size_t len, u8 *mac) -{ - md4_vector(1, &addr, &len, mac); -} - - -/** - * @clear: 8 octets (in) - * @key: 7 octets (in) (no parity bits included) - * @cypher: 8 octets (out) - */ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) { u8 pkey[8], next, tmp; @@ -69,3 +64,91 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) DES_ecb_encrypt((DES_cblock *) clear, (DES_cblock *) cypher, &ks, DES_ENCRYPT); } + + +#ifdef EAP_TLS_FUNCS +void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + MD5_CTX ctx; + int i; + + MD5_Init(&ctx); + for (i = 0; i < num_elem; i++) + MD5_Update(&ctx, addr[i], len[i]); + MD5_Final(mac, &ctx); +} + + +void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + SHA_CTX ctx; + int i; + + SHA1_Init(&ctx); + for (i = 0; i < num_elem; i++) + SHA1_Update(&ctx, addr[i], len[i]); + SHA1_Final(mac, &ctx); +} + + +void sha1_transform(u8 *state, const u8 data[64]) +{ + SHA_CTX context; + memset(&context, 0, sizeof(context)); + memcpy(&context.h0, state, 5 * 4); + SHA1_Transform(&context, data); + memcpy(state, &context.h0, 5 * 4); +} + + +void * aes_encrypt_init(const u8 *key, size_t len) +{ + AES_KEY *ak; + ak = malloc(sizeof(*ak)); + if (ak == NULL) + return NULL; + if (AES_set_encrypt_key(key, 8 * len, ak) < 0) { + free(ak); + return NULL; + } + return ak; +} + + +void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +{ + AES_encrypt(plain, crypt, ctx); +} + + +void aes_encrypt_deinit(void *ctx) +{ + free(ctx); +} + + +void * aes_decrypt_init(const u8 *key, size_t len) +{ + AES_KEY *ak; + ak = malloc(sizeof(*ak)); + if (ak == NULL) + return NULL; + if (AES_set_decrypt_key(key, 8 * len, ak) < 0) { + free(ak); + return NULL; + } + return ak; +} + + +void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) +{ + AES_decrypt(crypt, plain, ctx); +} + + +void aes_decrypt_deinit(void *ctx) +{ + free(ctx); +} +#endif /* EAP_TLS_FUNCS */ diff --git a/contrib/hostapd/crypto.h b/contrib/hostapd/crypto.h index 3e1a0e5..e664861 100644 --- a/contrib/hostapd/crypto.h +++ b/contrib/hostapd/crypto.h @@ -1,8 +1,123 @@ +/* + * WPA Supplicant / wrapper functions for crypto libraries + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * This file defines the cryptographic functions that need to be implemented + * for wpa_supplicant and hostapd. When TLS is not used, internal + * implementation of MD5, SHA1, and AES is used and no external libraries are + * required. When TLS is enabled (e.g., by enabling EAP-TLS or EAP-PEAP), the + * crypto library used by the TLS implementation is expected to be used for + * non-TLS needs, too, in order to save space by not implementing these + * functions twice. + * + * Wrapper code for using each crypto library is in its own file (crypto*.c) + * and one of these files is build and linked in to provide the functions + * defined here. + */ + #ifndef CRYPTO_H #define CRYPTO_H +/** + * md4_vector - MD4 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + */ void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); -void md4(const u8 *addr, size_t len, u8 *mac); + +/** + * md5_vector - MD5 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + */ +void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); + +/** + * sha1_vector - SHA-1 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + */ +void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac); + +/** + * sha1_transform - Perform one SHA-1 transform step + * @state: SHA-1 state + * @data: Input data for the SHA-1 transform + * + * This function is used to implement random number generation specified in + * NIST FIPS Publication 186-2 for EAP-SIM. This PRF uses a function that is + * similar to SHA-1, but has different message padding and as such, access to + * just part of the SHA-1 is needed. + */ +void sha1_transform(u8 *state, const u8 data[64]); + +/** + * des_encrypt - Encrypt one block with DES + * @clear: 8 octets (in) + * @key: 7 octets (in) (no parity bits included) + * @cypher: 8 octets (out) + */ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher); +/** + * aes_encrypt_init - Initialize AES for encryption + * @key: Encryption key + * @len: Key length in bytes (usually 16, i.e., 128 bits) + * Returns: Pointer to context data or %NULL on failure + */ +void * aes_encrypt_init(const u8 *key, size_t len); + +/** + * aes_encrypt - Encrypt one AES block + * @ctx: Context pointer from aes_encrypt_init() + * @plain: Plaintext data to be encrypted (16 bytes) + * @crypt: Buffer for the encrypted data (16 bytes) + */ +void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt); + +/** + * aes_encrypt_deinit - Deinitialize AES encryption + * @ctx: Context pointer from aes_encrypt_init() + */ +void aes_encrypt_deinit(void *ctx); + +/** + * aes_decrypt_init - Initialize AES for decryption + * @key: Decryption key + * @len: Key length in bytes (usually 16, i.e., 128 bits) + * Returns: Pointer to context data or %NULL on failure + */ +void * aes_decrypt_init(const u8 *key, size_t len); + +/** + * aes_decrypt - Decrypt one AES block + * @ctx: Context pointer from aes_encrypt_init() + * @crypt: Encrypted data (16 bytes) + * @plain: Buffer for the decrypted data (16 bytes) + */ +void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain); + +/** + * aes_decrypt_deinit - Deinitialize AES decryption + * @ctx: Context pointer from aes_encrypt_init() + */ +void aes_decrypt_deinit(void *ctx); + + #endif /* CRYPTO_H */ diff --git a/contrib/hostapd/ctrl_iface.c b/contrib/hostapd/ctrl_iface.c index 9ed109b..ff730d4 100644 --- a/contrib/hostapd/ctrl_iface.c +++ b/contrib/hostapd/ctrl_iface.c @@ -315,7 +315,8 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd) } } - if (chown(hapd->conf->ctrl_interface, 0, + if (hapd->conf->ctrl_interface_gid_set && + chown(hapd->conf->ctrl_interface, 0, hapd->conf->ctrl_interface_gid) < 0) { perror("chown[ctrl_interface]"); return -1; @@ -342,7 +343,8 @@ int hostapd_ctrl_iface_init(struct hostapd_data *hapd) goto fail; } - if (chown(fname, 0, hapd->conf->ctrl_interface_gid) < 0) { + if (hapd->conf->ctrl_interface_gid_set && + chown(fname, 0, hapd->conf->ctrl_interface_gid) < 0) { perror("chown[ctrl_interface/ifname]"); goto fail; } diff --git a/contrib/hostapd/defconfig b/contrib/hostapd/defconfig index b27c037..e8f4e4f 100644 --- a/contrib/hostapd/defconfig +++ b/contrib/hostapd/defconfig @@ -33,34 +33,43 @@ CONFIG_IAPP=y # WPA2/IEEE 802.11i RSN pre-authentication CONFIG_RSN_PREAUTH=y -# Integrated EAP authenticator +# Integrated EAP server CONFIG_EAP=y -# EAP-MD5 for the integrated EAP authenticator +# EAP-MD5 for the integrated EAP server CONFIG_EAP_MD5=y -# EAP-TLS for the integrated EAP authenticator +# EAP-TLS for the integrated EAP server CONFIG_EAP_TLS=y -# EAP-MSCHAPv2 for the integrated EAP authenticator +# EAP-MSCHAPv2 for the integrated EAP server CONFIG_EAP_MSCHAPV2=y -# EAP-PEAP for the integrated EAP authenticator +# EAP-PEAP for the integrated EAP server CONFIG_EAP_PEAP=y -# EAP-GTC for the integrated EAP authenticator +# EAP-GTC for the integrated EAP server CONFIG_EAP_GTC=y -# EAP-TTLS for the integrated EAP authenticator +# EAP-TTLS for the integrated EAP server CONFIG_EAP_TTLS=y -# EAP-SIM for the integrated EAP authenticator +# EAP-SIM for the integrated EAP server #CONFIG_EAP_SIM=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 + # 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 # RADIUS authentication server. This provides access to the integrated EAP -# authenticator from external hosts using RADIUS. +# server from external hosts using RADIUS. #CONFIG_RADIUS_SERVER=y + +# Build IPv6 support for RADIUS operations +CONFIG_IPV6=y diff --git a/contrib/hostapd/defs.h b/contrib/hostapd/defs.h index a5a515c..6f9881d 100644 --- a/contrib/hostapd/defs.h +++ b/contrib/hostapd/defs.h @@ -1,14 +1,131 @@ +/* + * WPA Supplicant - Common definitions + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + #ifndef DEFS_H #define DEFS_H -#ifdef CONFIG_NATIVE_WINDOWS #ifdef FALSE #undef FALSE #endif #ifdef TRUE #undef TRUE #endif -#endif /* CONFIG_NATIVE_WINDOWS */ typedef enum { FALSE = 0, TRUE = 1 } Boolean; + +typedef enum { WPA_ALG_NONE, WPA_ALG_WEP, WPA_ALG_TKIP, WPA_ALG_CCMP } wpa_alg; +typedef enum { CIPHER_NONE, CIPHER_WEP40, CIPHER_TKIP, CIPHER_CCMP, + CIPHER_WEP104 } wpa_cipher; +typedef enum { KEY_MGMT_802_1X, KEY_MGMT_PSK, KEY_MGMT_NONE, + KEY_MGMT_802_1X_NO_WPA, KEY_MGMT_WPA_NONE } wpa_key_mgmt; + +/** + * enum wpa_states - wpa_supplicant state + * + * These enumeration values are used to indicate the current wpa_supplicant + * state (wpa_s->wpa_state). The current state can be retrieved with + * wpa_supplicant_get_state() function and the state can be changed by calling + * wpa_supplicant_set_state(). In WPA state machine (wpa.c and preauth.c), the + * wrapper functions wpa_sm_get_state() and wpa_sm_set_state() should be used + * to access the state variable. + */ +typedef enum { + /** + * WPA_DISCONNECTED - Disconnected state + * + * This state indicates that client is not associated, but is likely to + * start looking for an access point. This state is entered when a + * connection is lost. + */ + WPA_DISCONNECTED, + + /** + * WPA_INACTIVE - Inactive state (wpa_supplicant disabled) + * + * This state is entered if there are no enabled networks in the + * configuration. wpa_supplicant is not trying to associate with a new + * network and external interaction (e.g., ctrl_iface call to add or + * enable a network) is needed to start association. + */ + WPA_INACTIVE, + + /** + * WPA_SCANNING - Scanning for a network + * + * This state is entered when wpa_supplicant starts scanning for a + * network. + */ + WPA_SCANNING, + + /** + * WPA_ASSOCIATING - Trying to associate with a BSS/SSID + * + * This state is entered when wpa_supplicant has found a suitable BSS + * to associate with and the driver is configured to try to associate + * with this BSS in ap_scan=1 mode. When using ap_scan=2 mode, this + * state is entered when the driver is configured to try to associate + * with a network using the configured SSID and security policy. + */ + WPA_ASSOCIATING, + + /** + * WPA_ASSOCIATED - Association completed + * + * This state is entered when the driver reports that association has + * been successfully completed with an AP. If IEEE 802.1X is used + * (with or without WPA/WPA2), wpa_supplicant remains in this state + * until the IEEE 802.1X/EAPOL authentication has been completed. + */ + WPA_ASSOCIATED, + + /** + * WPA_4WAY_HANDSHAKE - WPA 4-Way Key Handshake in progress + * + * This state is entered when WPA/WPA2 4-Way Handshake is started. In + * case of WPA-PSK, this happens when receiving the first EAPOL-Key + * frame after association. In case of WPA-EAP, this state is entered + * when the IEEE 802.1X/EAPOL authentication has been completed. + */ + WPA_4WAY_HANDSHAKE, + + /** + * WPA_GROUP_HANDSHAKE - WPA Group Key Handshake in progress + * + * This state is entered when 4-Way Key Handshake has been completed + * (i.e., when the supplicant sends out message 4/4) and when Group + * Key rekeying is started by the AP (i.e., when supplicant receives + * message 1/2). + */ + WPA_GROUP_HANDSHAKE, + + /** + * WPA_COMPLETED - All authentication completed + * + * This state is entered when the full authentication process is + * completed. In case of WPA2, this happens when the 4-Way Handshake is + * successfully completed. With WPA, this state is entered after the + * Group Key Handshake; with IEEE 802.1X (non-WPA) connection is + * completed after dynamic keys are received (or if not used, after + * the EAP authentication has been completed). With static WEP keys and + * plaintext connections, this state is entered when an association + * has been completed. + * + * This state indicates that the supplicant has completed its + * processing for the association phase and that data connection is + * fully configured. + */ + WPA_COMPLETED +} wpa_states; + #endif /* DEFS_H */ diff --git a/contrib/hostapd/driver.h b/contrib/hostapd/driver.h index 365deda..ed9ecbf 100644 --- a/contrib/hostapd/driver.h +++ b/contrib/hostapd/driver.h @@ -11,14 +11,14 @@ struct driver_ops { void (*wireless_event_deinit)(void *priv); /** - * set_8021x - enable/disable 802.1x support + * set_8021x - enable/disable IEEE 802.1X support * @priv: driver private data * @enabled: 1 = enable, 0 = disable * * Returns: 0 on success, -1 on failure * - * Configure the kernel driver to enable/disable 802.1x support. - * This may be an empty function if 802.1x support is always enabled. + * 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); @@ -49,6 +49,7 @@ struct driver_ops { 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); + 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); @@ -228,6 +229,14 @@ hostapd_set_assoc_ap(struct hostapd_data *hapd, u8 *addr) return hapd->driver->set_assoc_ap(hapd->driver, addr); } +static inline int +hostapd_set_countermeasures(struct hostapd_data *hapd, int enabled) +{ + if (hapd->driver == NULL || hapd->driver->set_countermeasures == NULL) + return 0; + return hapd->driver->set_countermeasures(hapd->driver, enabled); +} + static inline int hostapd_sta_add(struct hostapd_data *hapd, u8 *addr, u16 aid, u16 capability, u8 tx_supp_rates) diff --git a/contrib/hostapd/driver_test.c b/contrib/hostapd/driver_test.c index 612d1dc..6af9af2 100644 --- a/contrib/hostapd/driver_test.c +++ b/contrib/hostapd/driver_test.c @@ -1,7 +1,7 @@ /* * Host AP (software wireless LAN access point) user space daemon for * Host AP kernel driver / Driver interface for development testing - * Copyright (c) 2004, Jouni Malinen <jkmaline@cc.hut.fi> + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -23,22 +23,390 @@ #include <signal.h> #include <sys/types.h> #include <sys/socket.h> +#include <sys/un.h> +#include <sys/uio.h> #include "hostapd.h" #include "driver.h" +#include "sha1.h" +#include "eloop.h" +#include "ieee802_1x.h" +#include "sta_info.h" +#include "eapol_sm.h" +#include "wpa.h" +#include "accounting.h" +#include "radius.h" +#include "l2_packet.h" +#include "hostap_common.h" +struct test_client_socket { + struct test_client_socket *next; + u8 addr[ETH_ALEN]; + struct sockaddr_un un; + socklen_t unlen; +}; + struct test_driver_data { struct driver_ops ops; struct hostapd_data *hapd; + struct test_client_socket *cli; + int test_socket; + u8 *ie; + size_t ielen; }; static const struct driver_ops test_driver_ops; +static struct test_client_socket * +test_driver_get_cli(struct test_driver_data *drv, struct sockaddr_un *from, + socklen_t fromlen) +{ + struct test_client_socket *cli = drv->cli; + + while (cli) { + if (cli->unlen == fromlen && + strncmp(cli->un.sun_path, from->sun_path, fromlen) == 0) + return cli; + cli = cli->next; + } + + return NULL; +} + + +static int test_driver_send_eapol(void *priv, u8 *addr, u8 *data, + size_t data_len, int encrypt) +{ + struct test_driver_data *drv = priv; + struct test_client_socket *cli; + struct msghdr msg; + struct iovec io[3]; + struct l2_ethhdr eth; + + if (drv->test_socket < 0) + return -1; + + cli = drv->cli; + while (cli) { + if (memcmp(cli->addr, addr, ETH_ALEN) == 0) + break; + cli = cli->next; + } + + if (!cli) + return -1; + + memcpy(eth.h_dest, addr, ETH_ALEN); + memcpy(eth.h_source, drv->hapd->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_len = data_len; + + memset(&msg, 0, sizeof(msg)); + msg.msg_iov = io; + msg.msg_iovlen = 3; + msg.msg_name = &cli->un; + msg.msg_namelen = cli->unlen; + return sendmsg(drv->test_socket, &msg, 0); +} + + +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); + + 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]); + } + pos += snprintf(pos, end - pos, " "); + for (i = 0; i < drv->ielen; i++) { + pos += snprintf(pos, end - pos, "%02x", + drv->ie[i]); + } + + sendto(drv->test_socket, buf, pos - buf, 0, + (struct sockaddr *) from, fromlen); +} + + +static int test_driver_new_sta(struct test_driver_data *drv, const u8 *addr, + const u8 *ie, size_t ielen) +{ + struct hostapd_data *hapd = drv->hapd; + struct sta_info *sta; + int new_assoc, res; + + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "associated"); + + sta = ap_get_sta(hapd, addr); + if (sta) { + accounting_sta_stop(hapd, sta); + } else { + sta = ap_sta_add(hapd, addr); + if (sta == NULL) + return -1; + } + accounting_sta_get_id(hapd, sta); + + if (hapd->conf->wpa) { + if (ie == NULL || ielen == 0) { + 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 (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); + + hostapd_new_assoc_sta(hapd, sta, !new_assoc); + + ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); + + return 0; +} + + +static void test_driver_assoc(struct test_driver_data *drv, + struct sockaddr_un *from, socklen_t fromlen, + char *data) +{ + struct test_client_socket *cli; + u8 ie[256]; + size_t ielen; + char *pos, *pos2, cmd[50]; + + /* data: STA-addr SSID(hex) IEs(hex) */ + + cli = malloc(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); + free(cli); + return; + } + pos = data + 17; + while (*pos == ' ') + pos++; + pos2 = strchr(pos, ' '); + ielen = 0; + if (pos2) { + /* TODO: verify SSID */ + + pos = pos2 + 1; + ielen = strlen(pos) / 2; + if (ielen > sizeof(ie)) + ielen = sizeof(ie); + if (hexstr2bin(pos, ie, ielen) < 0) + ielen = 0; + } + + 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); + + snprintf(cmd, sizeof(cmd), "ASSOCRESP " MACSTR " 0", + MAC2STR(drv->hapd->own_addr)); + sendto(drv->test_socket, cmd, strlen(cmd), 0, + (struct sockaddr *) from, fromlen); + + if (test_driver_new_sta(drv, cli->addr, ie, ielen) < 0) { + wpa_printf(MSG_DEBUG, "test_driver: failed to add new STA"); + } +} + + +static void test_driver_disassoc(struct test_driver_data *drv, + struct sockaddr_un *from, socklen_t fromlen) +{ + struct test_client_socket *cli; + struct sta_info *sta; + + cli = test_driver_get_cli(drv, from, fromlen); + if (!cli) + return; + + hostapd_logger(drv->hapd, cli->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "disassociated"); + + sta = ap_get_sta(drv->hapd, cli->addr); + if (sta != NULL) { + sta->flags &= ~WLAN_STA_ASSOC; + wpa_sm_event(drv->hapd, sta, WPA_DISASSOC); + sta->acct_terminate_cause = + RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; + ieee802_1x_set_port_enabled(drv->hapd, sta, 0); + ap_free_sta(drv->hapd, sta); + } +} + + +static void test_driver_eapol(struct test_driver_data *drv, + struct sockaddr_un *from, socklen_t fromlen, + u8 *data, size_t datalen) +{ + struct test_client_socket *cli; + if (datalen > 14) { + /* Skip Ethernet header */ + data += 14; + datalen -= 14; + } + cli = test_driver_get_cli(drv, from, fromlen); + if (cli) + ieee802_1x_receive(drv->hapd, cli->addr, data, datalen); + else { + wpa_printf(MSG_DEBUG, "test_socket: EAPOL from unknown " + "client"); + } +} + + +static void test_driver_receive_unix(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct test_driver_data *drv = eloop_ctx; + char buf[2000]; + int res; + struct sockaddr_un from; + socklen_t fromlen = sizeof(from); + + res = recvfrom(sock, buf, sizeof(buf) - 1, 0, + (struct sockaddr *) &from, &fromlen); + if (res < 0) { + perror("recvfrom(test_socket)"); + return; + } + buf[res] = '\0'; + + wpa_printf(MSG_DEBUG, "test_driver: received %u bytes", res); + + if (strcmp(buf, "SCAN") == 0) { + test_driver_scan(drv, &from, fromlen); + } else if (strncmp(buf, "ASSOC ", 6) == 0) { + test_driver_assoc(drv, &from, fromlen, buf + 6); + } 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); + } else { + wpa_hexdump_ascii(MSG_DEBUG, "Unknown test_socket command", + (u8 *) buf, res); + } +} + + +static int test_driver_set_generic_elem(void *priv, + const u8 *elem, size_t elem_len) +{ + struct test_driver_data *drv = priv; + + 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; + } +} + + +static int test_driver_sta_deauth(void *priv, u8 *addr, int reason) +{ + struct test_driver_data *drv = priv; + struct test_client_socket *cli; + + if (drv->test_socket < 0) + return -1; + + cli = drv->cli; + while (cli) { + if (memcmp(cli->addr, addr, ETH_ALEN) == 0) + break; + cli = cli->next; + } + + if (!cli) + return -1; + + return sendto(drv->test_socket, "DEAUTH", 6, 0, + (struct sockaddr *) &cli->un, cli->unlen); +} + + +static int test_driver_sta_disassoc(void *priv, u8 *addr, int reason) +{ + struct test_driver_data *drv = priv; + struct test_client_socket *cli; + + if (drv->test_socket < 0) + return -1; + + cli = drv->cli; + while (cli) { + if (memcmp(cli->addr, addr, ETH_ALEN) == 0) + break; + cli = cli->next; + } + + if (!cli) + return -1; + + return sendto(drv->test_socket, "DISASSOC", 8, 0, + (struct sockaddr *) &cli->un, cli->unlen); +} + + 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)); if (drv == NULL) { @@ -50,6 +418,43 @@ static int test_driver_init(struct hostapd_data *hapd) 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), + "hostapd test bssid generation", + hapd->conf->ssid, hapd->conf->ssid_len, + hapd->own_addr + 1, ETH_ALEN - 1); + + if (hapd->conf->test_socket) { + if (strlen(hapd->conf->test_socket) >= sizeof(addr.sun_path)) { + printf("Too long test_socket path\n"); + free(drv); + return -1; + } + drv->test_socket = socket(PF_UNIX, SOCK_DGRAM, 0); + if (drv->test_socket < 0) { + perror("socket(PF_UNIX)"); + free(drv); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, hapd->conf->test_socket, + 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); + return -1; + } + eloop_register_read_sock(drv->test_socket, + test_driver_receive_unix, drv, NULL); + } else + drv->test_socket = -1; + hapd->driver = &drv->ops; return 0; } @@ -58,9 +463,24 @@ static int test_driver_init(struct hostapd_data *hapd) static void test_driver_deinit(void *priv) { struct test_driver_data *drv = priv; + struct test_client_socket *cli, *prev; + + cli = drv->cli; + while (cli) { + prev = cli; + cli = cli->next; + free(prev); + } + + if (drv->test_socket >= 0) { + eloop_unregister_read_sock(drv->test_socket); + close(drv->test_socket); + unlink(drv->hapd->conf->test_socket); + } drv->hapd->driver = NULL; + free(drv->ie); free(drv); } @@ -69,6 +489,10 @@ static const struct driver_ops test_driver_ops = { .name = "test", .init = test_driver_init, .deinit = test_driver_deinit, + .send_eapol = test_driver_send_eapol, + .set_generic_elem = test_driver_set_generic_elem, + .sta_deauth = test_driver_sta_deauth, + .sta_disassoc = test_driver_sta_disassoc, }; diff --git a/contrib/hostapd/driver_wired.c b/contrib/hostapd/driver_wired.c index 3e21268..6ef6b19 100644 --- a/contrib/hostapd/driver_wired.c +++ b/contrib/hostapd/driver_wired.c @@ -51,6 +51,7 @@ struct wired_driver_data { int sock; /* raw packet socket for driver access */ int dhcp_sock; /* socket for dhcp packets */ + int use_pae_group_addr; }; static const struct driver_ops wired_driver_ops; @@ -95,7 +96,7 @@ static void wired_possible_new_sta(struct hostapd_data *hapd, u8 *addr) MACSTR " - adding a new STA\n", MAC2STR(addr)); sta = ap_sta_add(hapd, addr); if (sta) { - hostapd_new_assoc_sta(hapd, sta); + hostapd_new_assoc_sta(hapd, sta, 0); accounting_sta_get_id(hapd, sta); } else { HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Failed to add STA entry " @@ -309,7 +310,7 @@ static int wired_send_eapol(void *priv, u8 *addr, u8 *data, size_t data_len, int encrypt) { struct wired_driver_data *drv = priv; - + u8 pae_group_addr[ETH_ALEN] = WIRED_EAPOL_MULTICAST_GROUP; struct ieee8023_hdr *hdr; size_t len; u8 *pos; @@ -324,7 +325,8 @@ static int wired_send_eapol(void *priv, u8 *addr, } memset(hdr, 0, len); - memcpy(hdr->dest, addr, ETH_ALEN); + memcpy(hdr->dest, drv->use_pae_group_addr ? pae_group_addr : addr, + ETH_ALEN); memcpy(hdr->src, drv->hapd->own_addr, ETH_ALEN); hdr->ethertype = htons(ETH_P_PAE); @@ -357,6 +359,7 @@ static int wired_driver_init(struct hostapd_data *hapd) 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)) return -1; diff --git a/contrib/hostapd/eap.c b/contrib/hostapd/eap.c index 7a21c80..a20147e 100644 --- a/contrib/hostapd/eap.c +++ b/contrib/hostapd/eap.c @@ -24,6 +24,7 @@ #include "sta_info.h" #include "eap_i.h" +#define EAP_MAX_AUTH_ROUNDS 50 extern const struct eap_method eap_method_identity; #ifdef EAP_MD5 @@ -50,6 +51,12 @@ extern const struct eap_method eap_method_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[] = { @@ -78,6 +85,12 @@ static const struct eap_method *eap_methods[] = #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])) @@ -197,6 +210,7 @@ int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len, SM_STATE(EAP, DISABLED) { SM_ENTRY(EAP, DISABLED); + sm->num_rounds = 0; } @@ -232,6 +246,7 @@ SM_STATE(EAP, INITIALIZE) sm->currentId = sm->respId; } } + sm->num_rounds = 0; } @@ -288,6 +303,7 @@ SM_STATE(EAP, RECEIVED) /* parse rxResp, respId, respMethod */ eap_sm_parseEapResp(sm, sm->eapRespData, sm->eapRespDataLen); + sm->num_rounds++; } @@ -503,7 +519,15 @@ SM_STEP(EAP) SM_ENTER_GLOBAL(EAP, INITIALIZE); else if (!eapol_get_bool(sm, EAPOL_portEnabled)) SM_ENTER_GLOBAL(EAP, DISABLED); - else switch (sm->EAP_state) { + else if (sm->num_rounds > EAP_MAX_AUTH_ROUNDS) { + if (sm->num_rounds == EAP_MAX_AUTH_ROUNDS + 1) { + wpa_printf(MSG_DEBUG, "EAP: more than %d " + "authentication rounds - abort", + EAP_MAX_AUTH_ROUNDS); + sm->num_rounds++; + SM_ENTER_GLOBAL(EAP, FAILURE); + } + } else switch (sm->EAP_state) { case EAP_INITIALIZE: if (sm->backend_auth) { if (!sm->rxResp) @@ -909,3 +933,12 @@ void eap_sm_deinit(struct eap_sm *sm) eap_user_free(sm->user); free(sm); } + + +void eap_sm_notify_cached(struct eap_sm *sm) +{ + if (sm == NULL) + return; + + sm->EAP_state = EAP_SUCCESS; +} diff --git a/contrib/hostapd/eap.h b/contrib/hostapd/eap.h index 01f47da..c5c62eb 100644 --- a/contrib/hostapd/eap.h +++ b/contrib/hostapd/eap.h @@ -30,6 +30,7 @@ struct eapol_callbacks { size_t eapKeyDataLen); int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len, int phase2, struct eap_user *user); + const char * (*get_eap_req_id_text)(void *ctx, size_t *len); }; struct eap_config { @@ -39,7 +40,7 @@ struct eap_config { }; -#ifdef EAP_AUTHENTICATOR +#ifdef EAP_SERVER struct eap_sm * eap_sm_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, struct eap_config *eap_conf); @@ -48,8 +49,9 @@ 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); -#else /* EAP_AUTHENTICATOR */ +#else /* EAP_SERVER */ static inline struct eap_sm * eap_sm_init(void *eapol_ctx, struct eapol_callbacks *eapol_cb, @@ -78,6 +80,10 @@ static inline void eap_set_eapRespData(struct eap_sm *sm, { } -#endif /* EAP_AUTHENTICATOR */ +static inline void eap_sm_notify_cached(struct eap_sm *sm) +{ +} + +#endif /* EAP_SERVER */ #endif /* EAP_H */ diff --git a/contrib/hostapd/eap_defs.h b/contrib/hostapd/eap_defs.h index effe665..9cd4490 100644 --- a/contrib/hostapd/eap_defs.h +++ b/contrib/hostapd/eap_defs.h @@ -1,3 +1,17 @@ +/* + * WPA Supplicant/hostapd / Shared EAP definitions + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + #ifndef EAP_DEFS_H #define EAP_DEFS_H @@ -6,7 +20,7 @@ struct eap_hdr { u8 code; u8 identifier; - u16 length; /* including code and identifier */ + u16 length; /* including code and identifier; network byte order */ /* followed by length-4 octets of data */ } __attribute__ ((packed)); @@ -18,12 +32,12 @@ enum { EAP_CODE_REQUEST = 1, EAP_CODE_RESPONSE = 2, EAP_CODE_SUCCESS = 3, typedef enum { EAP_TYPE_NONE = 0, - EAP_TYPE_IDENTITY = 1, - EAP_TYPE_NOTIFICATION = 2, - EAP_TYPE_NAK = 3 /* Response only */, - EAP_TYPE_MD5 = 4, - EAP_TYPE_OTP = 5 /* RFC 2284 */, - EAP_TYPE_GTC = 6, /* RFC 2284 */ + EAP_TYPE_IDENTITY = 1 /* RFC 3748 */, + EAP_TYPE_NOTIFICATION = 2 /* RFC 3748 */, + EAP_TYPE_NAK = 3 /* Response only, RFC 3748 */, + EAP_TYPE_MD5 = 4, /* RFC 3748 */ + EAP_TYPE_OTP = 5 /* RFC 3748 */, + EAP_TYPE_GTC = 6, /* RFC 3748 */ EAP_TYPE_TLS = 13 /* RFC 2716 */, EAP_TYPE_LEAP = 17 /* Cisco proprietary */, EAP_TYPE_SIM = 18 /* draft-haverinen-pppext-eap-sim-12.txt */, @@ -33,9 +47,10 @@ typedef enum { EAP_TYPE_MSCHAPV2 = 26 /* draft-kamath-pppext-eap-mschapv2-00.txt */, EAP_TYPE_TLV = 33 /* draft-josefsson-pppext-eap-tls-eap-07.txt */, EAP_TYPE_FAST = 43 /* draft-cam-winget-eap-fast-00.txt */, + EAP_TYPE_PAX = 46, /* draft-clancy-eap-pax-04.txt */ EAP_TYPE_EXPANDED_NAK = 254 /* RFC 3748 */, EAP_TYPE_PSK = 255 /* EXPERIMENTAL - type not yet allocated - * draft-bersani-eap-psk-03 */ + * draft-bersani-eap-psk-09 */ } EapType; #endif /* EAP_DEFS_H */ diff --git a/contrib/hostapd/eap_i.h b/contrib/hostapd/eap_i.h index 39b0257..4e803f9 100644 --- a/contrib/hostapd/eap_i.h +++ b/contrib/hostapd/eap_i.h @@ -100,6 +100,8 @@ struct eap_sm { void *eap_sim_db_priv; Boolean backend_auth; Boolean update_user; + + int num_rounds; }; const struct eap_method * eap_sm_get_eap_methods(int method); diff --git a/contrib/hostapd/eap_identity.c b/contrib/hostapd/eap_identity.c index 31aedc4..54efc47 100644 --- a/contrib/hostapd/eap_identity.c +++ b/contrib/hostapd/eap_identity.c @@ -66,8 +66,17 @@ static u8 * eap_identity_buildReq(struct eap_sm *sm, void *priv, int id, struct eap_identity_data *data = priv; struct eap_hdr *req; u8 *pos; + const char *req_data; + size_t req_data_len; - *reqDataLen = sizeof(*req) + 1; + if (sm->eapol_cb->get_eap_req_id_text) { + req_data = sm->eapol_cb->get_eap_req_id_text(sm->eapol_ctx, + &req_data_len); + } else { + req_data = NULL; + req_data_len = 0; + } + *reqDataLen = sizeof(*req) + 1 + req_data_len; req = malloc(*reqDataLen); if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-Identity: Failed to allocate " @@ -80,7 +89,9 @@ static u8 * eap_identity_buildReq(struct eap_sm *sm, void *priv, int id, req->identifier = id; req->length = htons(*reqDataLen); pos = (u8 *) (req + 1); - *pos = EAP_TYPE_IDENTITY; + *pos++ = EAP_TYPE_IDENTITY; + if (req_data) + memcpy(pos, req_data, req_data_len); return (u8 *) req; } diff --git a/contrib/hostapd/eap_md5.c b/contrib/hostapd/eap_md5.c index 5675c50..d776c8c 100644 --- a/contrib/hostapd/eap_md5.c +++ b/contrib/hostapd/eap_md5.c @@ -21,6 +21,7 @@ #include "common.h" #include "eap_i.h" #include "md5.h" +#include "crypto.h" #define CHALLENGE_LEN 16 @@ -122,7 +123,8 @@ static void eap_md5_process(struct eap_sm *sm, void *priv, struct eap_md5_data *data = priv; struct eap_hdr *resp; u8 *pos; - MD5_CTX context; + const u8 *addr[3]; + size_t len[3]; u8 hash[MD5_MAC_LEN]; if (sm->user == NULL || sm->user->password == NULL) { @@ -136,11 +138,13 @@ static void eap_md5_process(struct eap_sm *sm, void *priv, pos += 2; /* Skip type and len */ wpa_hexdump(MSG_MSGDUMP, "EAP-MD5: Response", pos, MD5_MAC_LEN); - MD5Init(&context); - MD5Update(&context, &resp->identifier, 1); - MD5Update(&context, sm->user->password, sm->user->password_len); - MD5Update(&context, data->challenge, CHALLENGE_LEN); - MD5Final(hash, &context); + addr[0] = &resp->identifier; + len[0] = 1; + addr[1] = sm->user->password; + len[1] = sm->user->password_len; + addr[2] = data->challenge; + len[2] = CHALLENGE_LEN; + md5_vector(3, addr, len, hash); if (memcmp(hash, pos, MD5_MAC_LEN) == 0) { wpa_printf(MSG_DEBUG, "EAP-MD5: Done - Success"); diff --git a/contrib/hostapd/eap_pax.c b/contrib/hostapd/eap_pax.c new file mode 100644 index 0000000..2fbec87 --- /dev/null +++ b/contrib/hostapd/eap_pax.c @@ -0,0 +1,519 @@ +/* + * hostapd / EAP-PAX (draft-clancy-eap-pax-04.txt) server + * Copyright (c) 2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <netinet/in.h> + +#include "hostapd.h" +#include "common.h" +#include "eap_i.h" +#include "eap_pax_common.h" + +/* + * Note: only PAX_STD subprotocol is currently supported + */ + +struct eap_pax_data { + enum { PAX_STD_1, PAX_STD_3, SUCCESS, FAILURE } state; + u8 mac_id; + union { + u8 e[2 * EAP_PAX_RAND_LEN]; + struct { + u8 x[EAP_PAX_RAND_LEN]; /* server rand */ + u8 y[EAP_PAX_RAND_LEN]; /* client rand */ + } r; + } rand; + u8 ak[EAP_PAX_AK_LEN]; + u8 mk[EAP_PAX_MK_LEN]; + u8 ck[EAP_PAX_CK_LEN]; + u8 ick[EAP_PAX_ICK_LEN]; + int keys_set; + char *cid; + size_t cid_len; +}; + + +static void * eap_pax_init(struct eap_sm *sm) +{ + struct eap_pax_data *data; + + data = malloc(sizeof(*data)); + if (data == NULL) + return data; + memset(data, 0, sizeof(*data)); + data->state = PAX_STD_1; + /* + * TODO: make this configurable once EAP_PAX_MAC_AES_CBC_MAC_128 is + * supported + */ + data->mac_id = EAP_PAX_MAC_HMAC_SHA1_128; + + return data; +} + + +static void eap_pax_reset(struct eap_sm *sm, void *priv) +{ + struct eap_pax_data *data = priv; + free(data->cid); + free(data); +} + + +static u8 * eap_pax_build_std_1(struct eap_sm *sm, + struct eap_pax_data *data, + int id, size_t *reqDataLen) +{ + struct eap_pax_hdr *req; + u8 *pos; + + wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-1 (sending)"); + + if (hostapd_get_rand(data->rand.r.x, EAP_PAX_RAND_LEN)) { + wpa_printf(MSG_ERROR, "EAP-PAX: Failed to get random data"); + data->state = FAILURE; + return NULL; + } + + *reqDataLen = sizeof(*req) + 2 + EAP_PAX_RAND_LEN + EAP_PAX_ICV_LEN; + req = malloc(*reqDataLen); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory " + "request"); + data->state = FAILURE; + return NULL; + } + + req->code = EAP_CODE_REQUEST; + req->identifier = id; + req->length = htons(*reqDataLen); + req->type = EAP_TYPE_PAX; + req->op_code = EAP_PAX_OP_STD_1; + req->flags = 0; + req->mac_id = data->mac_id; + req->dh_group_id = EAP_PAX_DH_GROUP_NONE; + req->public_key_id = EAP_PAX_PUBLIC_KEY_NONE; + pos = (u8 *) (req + 1); + *pos++ = 0; + *pos++ = EAP_PAX_RAND_LEN; + memcpy(pos, data->rand.r.x, EAP_PAX_RAND_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: A = X (server rand)", + pos, EAP_PAX_RAND_LEN); + pos += EAP_PAX_RAND_LEN; + + eap_pax_mac(data->mac_id, (u8 *) "", 0, + (u8 *) req, *reqDataLen - EAP_PAX_ICV_LEN, + NULL, 0, NULL, 0, pos); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); + pos += EAP_PAX_ICV_LEN; + + return (u8 *) req; +} + + +static u8 * eap_pax_build_std_3(struct eap_sm *sm, + struct eap_pax_data *data, + int id, size_t *reqDataLen) +{ + struct eap_pax_hdr *req; + u8 *pos; + + wpa_printf(MSG_DEBUG, "EAP-PAX: PAX_STD-3 (sending)"); + + *reqDataLen = sizeof(*req) + 2 + EAP_PAX_MAC_LEN + EAP_PAX_ICV_LEN; + req = malloc(*reqDataLen); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-PAX: Failed to allocate memory " + "request"); + data->state = FAILURE; + return NULL; + } + + req->code = EAP_CODE_REQUEST; + req->identifier = id; + req->length = htons(*reqDataLen); + req->type = EAP_TYPE_PAX; + req->op_code = EAP_PAX_OP_STD_3; + req->flags = 0; + req->mac_id = data->mac_id; + req->dh_group_id = EAP_PAX_DH_GROUP_NONE; + req->public_key_id = EAP_PAX_PUBLIC_KEY_NONE; + pos = (u8 *) (req + 1); + *pos++ = 0; + *pos++ = EAP_PAX_MAC_LEN; + eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, + data->rand.r.y, EAP_PAX_RAND_LEN, + (u8 *) data->cid, data->cid_len, NULL, 0, pos); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)", + pos, EAP_PAX_MAC_LEN); + pos += EAP_PAX_MAC_LEN; + + eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, + (u8 *) req, *reqDataLen - EAP_PAX_ICV_LEN, + NULL, 0, NULL, 0, pos); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); + pos += EAP_PAX_ICV_LEN; + + return (u8 *) req; +} + + +static u8 * eap_pax_buildReq(struct eap_sm *sm, void *priv, int id, + size_t *reqDataLen) +{ + struct eap_pax_data *data = priv; + + switch (data->state) { + case PAX_STD_1: + return eap_pax_build_std_1(sm, data, id, reqDataLen); + case PAX_STD_3: + return eap_pax_build_std_3(sm, data, id, reqDataLen); + default: + wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown state %d in buildReq", + data->state); + break; + } + return NULL; +} + + +static Boolean eap_pax_check(struct eap_sm *sm, void *priv, + u8 *respData, size_t respDataLen) +{ + struct eap_pax_data *data = priv; + struct eap_pax_hdr *resp; + size_t len; + u8 icvbuf[EAP_PAX_ICV_LEN], *icv; + + resp = (struct eap_pax_hdr *) respData; + if (respDataLen < sizeof(*resp) || resp->type != EAP_TYPE_PAX || + (len = ntohs(resp->length)) > respDataLen || + len < sizeof(*resp) + EAP_PAX_ICV_LEN) { + wpa_printf(MSG_INFO, "EAP-PAX: Invalid frame"); + return TRUE; + } + + wpa_printf(MSG_DEBUG, "EAP-PAX: received frame: op_code 0x%x " + "flags 0x%x mac_id 0x%x dh_group_id 0x%x " + "public_key_id 0x%x", + resp->op_code, resp->flags, resp->mac_id, resp->dh_group_id, + resp->public_key_id); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: received payload", + (u8 *) (resp + 1), len - sizeof(*resp) - EAP_PAX_ICV_LEN); + + if (data->state == PAX_STD_1 && + resp->op_code != EAP_PAX_OP_STD_2) { + wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX_STD-2 - " + "ignore op %d", resp->op_code); + return TRUE; + } + + if (data->state == PAX_STD_3 && + resp->op_code != EAP_PAX_OP_ACK) { + wpa_printf(MSG_DEBUG, "EAP-PAX: Expected PAX-ACK - " + "ignore op %d", resp->op_code); + return TRUE; + } + + if (resp->op_code != EAP_PAX_OP_STD_2 && + resp->op_code != EAP_PAX_OP_ACK) { + wpa_printf(MSG_DEBUG, "EAP-PAX: Unknown op_code 0x%x", + resp->op_code); + } + + if (data->mac_id != resp->mac_id) { + wpa_printf(MSG_DEBUG, "EAP-PAX: Expected MAC ID 0x%x, " + "received 0x%x", data->mac_id, resp->mac_id); + return TRUE; + } + + if (resp->dh_group_id != EAP_PAX_DH_GROUP_NONE) { + wpa_printf(MSG_INFO, "EAP-PAX: Expected DH Group ID 0x%x, " + "received 0x%x", EAP_PAX_DH_GROUP_NONE, + resp->dh_group_id); + return TRUE; + } + + if (resp->public_key_id != EAP_PAX_PUBLIC_KEY_NONE) { + wpa_printf(MSG_INFO, "EAP-PAX: Expected Public Key ID 0x%x, " + "received 0x%x", EAP_PAX_PUBLIC_KEY_NONE, + resp->public_key_id); + return TRUE; + } + + if (resp->flags & EAP_PAX_FLAGS_MF) { + /* TODO: add support for reassembling fragments */ + wpa_printf(MSG_INFO, "EAP-PAX: fragmentation not supported"); + return TRUE; + } + + if (resp->flags & EAP_PAX_FLAGS_CE) { + wpa_printf(MSG_INFO, "EAP-PAX: Unexpected CE flag"); + return TRUE; + } + + if (data->keys_set) { + if (len - sizeof(*resp) < EAP_PAX_ICV_LEN) { + wpa_printf(MSG_INFO, "EAP-PAX: No ICV in the packet"); + return TRUE; + } + icv = respData + len - EAP_PAX_ICV_LEN; + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN); + eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, + respData, len - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0, + icvbuf); + if (memcmp(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) { + wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV"); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV", + icvbuf, EAP_PAX_ICV_LEN); + return TRUE; + } + } + + return FALSE; +} + + +static void eap_pax_process_std_2(struct eap_sm *sm, + struct eap_pax_data *data, + u8 *respData, size_t respDataLen) +{ + struct eap_pax_hdr *resp; + u8 *pos, mac[EAP_PAX_MAC_LEN], icvbuf[EAP_PAX_ICV_LEN]; + size_t len, left; + int i; + + if (data->state != PAX_STD_1) + return; + + wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX_STD-2"); + + resp = (struct eap_pax_hdr *) respData; + len = ntohs(resp->length); + pos = (u8 *) (resp + 1); + left = len - sizeof(*resp); + + if (left < 2 + EAP_PAX_RAND_LEN || + ((pos[0] << 8) | pos[1]) != EAP_PAX_RAND_LEN) { + wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (B)"); + return; + } + pos += 2; + left -= 2; + memcpy(data->rand.r.y, pos, EAP_PAX_RAND_LEN); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Y (client rand)", + data->rand.r.y, EAP_PAX_RAND_LEN); + pos += EAP_PAX_RAND_LEN; + left -= EAP_PAX_RAND_LEN; + + if (left < 2 || 2 + ((pos[0] << 8) | pos[1]) > left) { + wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (CID)"); + return; + } + data->cid_len = (pos[0] << 8) | pos[1]; + free(data->cid); + data->cid = malloc(data->cid_len); + if (data->cid == NULL) { + wpa_printf(MSG_INFO, "EAP-PAX: Failed to allocate memory for " + "CID"); + return; + } + memcpy (data->cid, pos + 2, data->cid_len); + pos += 2 + data->cid_len; + left -= 2 + data->cid_len; + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PAX: CID", + (u8 *) data->cid, data->cid_len); + + if (left < 2 + EAP_PAX_MAC_LEN || + ((pos[0] << 8) | pos[1]) != EAP_PAX_MAC_LEN) { + wpa_printf(MSG_INFO, "EAP-PAX: Too short PAX_STD-2 (MAC_CK)"); + return; + } + pos += 2; + left -= 2; + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(A, B, CID)", + pos, EAP_PAX_MAC_LEN); + + if (eap_user_get(sm, (u8 *) data->cid, data->cid_len, 0) < 0) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: unknown CID", + (u8 *) data->cid, data->cid_len); + data->state = FAILURE; + return; + } + + for (i = 0; + i < EAP_MAX_METHODS && sm->user->methods[i] != EAP_TYPE_NONE; + i++) { + if (sm->user->methods[i] == EAP_TYPE_PAX) + break; + } + + if (sm->user->methods[i] != EAP_TYPE_PAX) { + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-PAX: EAP-PAX not enabled for CID", + (u8 *) data->cid, data->cid_len); + data->state = FAILURE; + return; + } + + if (sm->user->password == NULL || + sm->user->password_len != EAP_PAX_AK_LEN) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-PAX: invalid password in " + "user database for CID", + (u8 *) data->cid, data->cid_len); + data->state = FAILURE; + return; + } + memcpy(data->ak, sm->user->password, EAP_PAX_AK_LEN); + + if (eap_pax_initial_key_derivation(data->mac_id, data->ak, + data->rand.e, data->mk, data->ck, + data->ick) < 0) { + wpa_printf(MSG_INFO, "EAP-PAX: Failed to complete initial " + "key derivation"); + data->state = FAILURE; + return; + } + data->keys_set = 1; + + eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN, + data->rand.r.x, EAP_PAX_RAND_LEN, + data->rand.r.y, EAP_PAX_RAND_LEN, + (u8 *) data->cid, data->cid_len, mac); + if (memcmp(mac, pos, EAP_PAX_MAC_LEN) != 0) { + wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(A, B, CID) in " + "PAX_STD-2"); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected MAC_CK(A, B, CID)", + mac, EAP_PAX_MAC_LEN); + data->state = FAILURE; + return; + } + + pos += EAP_PAX_MAC_LEN; + left -= EAP_PAX_MAC_LEN; + + if (left < EAP_PAX_ICV_LEN) { + wpa_printf(MSG_INFO, "EAP-PAX: Too short ICV (%lu) in " + "PAX_STD-2", (unsigned long) left); + return; + } + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN); + eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN, + respData, len - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0, icvbuf); + if (memcmp(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) { + wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV in PAX_STD-2"); + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV", + icvbuf, EAP_PAX_ICV_LEN); + return; + } + pos += EAP_PAX_ICV_LEN; + left -= EAP_PAX_ICV_LEN; + + if (left > 0) { + wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ignored extra payload", + pos, left); + } + + data->state = PAX_STD_3; +} + + +static void eap_pax_process_ack(struct eap_sm *sm, + struct eap_pax_data *data, + u8 *respData, size_t respDataLen) +{ + if (data->state != PAX_STD_3) + return; + + wpa_printf(MSG_DEBUG, "EAP-PAX: Received PAX-ACK - authentication " + "completed successfully"); + data->state = SUCCESS; +} + + +static void eap_pax_process(struct eap_sm *sm, void *priv, + u8 *respData, size_t respDataLen) +{ + struct eap_pax_data *data = priv; + struct eap_pax_hdr *resp; + + if (sm->user == NULL || sm->user->password == NULL) { + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured"); + data->state = FAILURE; + return; + } + + resp = (struct eap_pax_hdr *) respData; + + switch (resp->op_code) { + case EAP_PAX_OP_STD_2: + eap_pax_process_std_2(sm, data, respData, respDataLen); + break; + case EAP_PAX_OP_ACK: + eap_pax_process_ack(sm, data, respData, respDataLen); + break; + } +} + + +static Boolean eap_pax_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_pax_data *data = priv; + return data->state == SUCCESS || data->state == FAILURE; +} + + +static u8 * eap_pax_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_pax_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = malloc(EAP_PAX_MSK_LEN); + if (key == NULL) + return NULL; + + *len = EAP_PAX_MSK_LEN; + eap_pax_kdf(data->mac_id, data->mk, EAP_PAX_MK_LEN, + "Master Session Key", data->rand.e, 2 * EAP_PAX_RAND_LEN, + EAP_PAX_MSK_LEN, key); + + return key; +} + + +static Boolean eap_pax_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_pax_data *data = priv; + return data->state == SUCCESS; +} + + +const struct eap_method eap_method_pax = +{ + .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, +}; diff --git a/contrib/hostapd/eap_pax_common.c b/contrib/hostapd/eap_pax_common.c new file mode 100644 index 0000000..d8f4016 --- /dev/null +++ b/contrib/hostapd/eap_pax_common.c @@ -0,0 +1,152 @@ +/* + * WPA Supplicant / EAP-PAX shared routines + * Copyright (c) 2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "sha1.h" +#include "eap_pax_common.h" + + +/** + * eap_pax_kdf - PAX Key Derivation Function + * @mac_id: MAC ID (EAP_PAX_MAC_*) / currently, only HMAC_SHA1_128 is supported + * @key: Secret key (X) + * @key_len: Length of the secret key in bytes + * @identifier: Public identifier for the key (Y) + * @entropy: Exchanged entropy to seed the KDF (Z) + * @entropy_len: Length of the entropy in bytes + * @output_len: Output len in bytes (W) + * @output: Buffer for the derived key + * Returns: 0 on success, -1 failed + * + * draft-clancy-eap-pax-04.txt, chap. 2.5: PAX-KDF-W(X, Y, Z) + */ +int eap_pax_kdf(u8 mac_id, const u8 *key, size_t key_len, + const char *identifier, + const u8 *entropy, size_t entropy_len, + size_t output_len, u8 *output) +{ + u8 mac[SHA1_MAC_LEN]; + u8 counter, *pos; + const u8 *addr[3]; + size_t len[3]; + size_t num_blocks, left; + + num_blocks = (output_len + EAP_PAX_MAC_LEN - 1) / EAP_PAX_MAC_LEN; + if (identifier == NULL || num_blocks >= 255) + return -1; + + /* TODO: add support for EAP_PAX_MAC_AES_CBC_MAC_128 */ + if (mac_id != EAP_PAX_MAC_HMAC_SHA1_128) + return -1; + + addr[0] = (const u8 *) identifier; + len[0] = strlen(identifier); + addr[1] = entropy; + len[1] = entropy_len; + addr[2] = &counter; + len[2] = 1; + + pos = output; + left = output_len; + for (counter = 1; counter <= (u8) num_blocks; counter++) { + size_t clen = left > EAP_PAX_MAC_LEN ? EAP_PAX_MAC_LEN : left; + hmac_sha1_vector(key, key_len, 3, addr, len, mac); + memcpy(pos, mac, clen); + pos += clen; + left -= clen; + } + + return 0; +} + + +/** + * eap_pax_mac - EAP-PAX MAC + * @mac_id: MAC ID (EAP_PAX_MAC_*) / currently, only HMAC_SHA1_128 is supported + * @key: Secret key + * @key_len: Length of the secret key in bytes + * @data1: Optional data, first block; %NULL if not used + * @data1_len: Length of data1 in bytes + * @data2: Optional data, second block; %NULL if not used + * @data2_len: Length of data2 in bytes + * @data3: Optional data, third block; %NULL if not used + * @data3_len: Length of data3 in bytes + * @mac: Buffer for the MAC value (EAP_PAX_MAC_LEN = 16 bytes) + * Returns: 0 on success, -1 on failure + * + * Wrapper function to calculate EAP-PAX MAC. + */ +int eap_pax_mac(u8 mac_id, const u8 *key, size_t key_len, + const u8 *data1, size_t data1_len, + const u8 *data2, size_t data2_len, + const u8 *data3, size_t data3_len, + u8 *mac) +{ + u8 hash[SHA1_MAC_LEN]; + const u8 *addr[3]; + size_t len[3]; + size_t count; + + /* TODO: add support for EAP_PAX_MAC_AES_CBC_MAC_128 */ + if (mac_id != EAP_PAX_MAC_HMAC_SHA1_128) + return -1; + + addr[0] = data1; + len[0] = data1_len; + addr[1] = data2; + len[1] = data2_len; + addr[2] = data3; + len[2] = data3_len; + + count = (data1 ? 1 : 0) + (data2 ? 1 : 0) + (data3 ? 1 : 0); + hmac_sha1_vector(key, key_len, count, addr, len, hash); + memcpy(mac, hash, EAP_PAX_MAC_LEN); + + return 0; +} + + +/** + * eap_pax_initial_key_derivation - EAP-PAX initial key derivation + * @mac_id: MAC ID (EAP_PAX_MAC_*) / currently, only HMAC_SHA1_128 is supported + * @ak: Authentication Key + * @e: Entropy + * @mk: Buffer for the derived Master Key + * @ck: Buffer for the derived Confirmation Key + * @ick: Buffer for the derived Integrity Check Key + * Returns: 0 on success, -1 on failure + */ +int eap_pax_initial_key_derivation(u8 mac_id, const u8 *ak, const u8 *e, + u8 *mk, u8 *ck, u8 *ick) +{ + wpa_printf(MSG_DEBUG, "EAP-PAX: initial key derivation"); + if (eap_pax_kdf(mac_id, ak, EAP_PAX_AK_LEN, "Master Key", + e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_MK_LEN, mk) || + eap_pax_kdf(mac_id, mk, EAP_PAX_MK_LEN, "Confirmation Key", + e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_CK_LEN, ck) || + eap_pax_kdf(mac_id, mk, EAP_PAX_MK_LEN, "Integrity Check Key", + e, 2 * EAP_PAX_RAND_LEN, EAP_PAX_ICK_LEN, ick)) + return -1; + + wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: AK", ak, EAP_PAX_AK_LEN); + wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: MK", mk, EAP_PAX_MK_LEN); + wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: CK", ck, EAP_PAX_CK_LEN); + wpa_hexdump_key(MSG_MSGDUMP, "EAP-PAX: ICK", ick, EAP_PAX_ICK_LEN); + + return 0; +} diff --git a/contrib/hostapd/eap_pax_common.h b/contrib/hostapd/eap_pax_common.h new file mode 100644 index 0000000..b5ad6af --- /dev/null +++ b/contrib/hostapd/eap_pax_common.h @@ -0,0 +1,84 @@ +/* + * WPA Supplicant / EAP-PAX shared routines + * Copyright (c) 2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef EAP_PAX_COMMON_H +#define EAP_PAX_COMMON_H + +struct eap_pax_hdr { + u8 code; + u8 identifier; + u16 length; /* including code, identifier, and length */ + u8 type; /* EAP_TYPE_PAX */ + u8 op_code; + u8 flags; + u8 mac_id; + u8 dh_group_id; + u8 public_key_id; + /* Followed by variable length payload and ICV */ +} __attribute__ ((packed)); + + +/* op_code: */ +enum { + EAP_PAX_OP_STD_1 = 0x01, + EAP_PAX_OP_STD_2 = 0x02, + EAP_PAX_OP_STD_3 = 0x03, + EAP_PAX_OP_SEC_1 = 0x11, + EAP_PAX_OP_SEC_2 = 0x12, + EAP_PAX_OP_SEC_3 = 0x13, + EAP_PAX_OP_SEC_4 = 0x14, + EAP_PAX_OP_SEC_5 = 0x15, + EAP_PAX_OP_ACK = 0x21 +}; + +/* flags: */ +#define EAP_PAX_FLAGS_MF 0x01 +#define EAP_PAX_FLAGS_CE 0x02 + +/* mac_id: */ +#define EAP_PAX_MAC_HMAC_SHA1_128 0x01 +#define EAP_PAX_MAC_AES_CBC_MAC_128 0x02 + +/* dh_group_id: */ +#define EAP_PAX_DH_GROUP_NONE 0x00 +#define EAP_PAX_DH_GROUP_3072_MODP 0x01 + +/* public_key_id: */ +#define EAP_PAX_PUBLIC_KEY_NONE 0x00 +#define EAP_PAX_PUBLIC_KEY_RSA_OAEP_2048 0x01 + + +#define EAP_PAX_RAND_LEN 32 +#define EAP_PAX_MSK_LEN 64 +#define EAP_PAX_MAC_LEN 16 +#define EAP_PAX_ICV_LEN 16 +#define EAP_PAX_AK_LEN 16 +#define EAP_PAX_MK_LEN 16 +#define EAP_PAX_CK_LEN 16 +#define EAP_PAX_ICK_LEN 16 + + +int eap_pax_kdf(u8 mac_id, const u8 *key, size_t key_len, + const char *identifier, + const u8 *entropy, size_t entropy_len, + size_t output_len, u8 *output); +int eap_pax_mac(u8 mac_id, const u8 *key, size_t key_len, + const u8 *data1, size_t data1_len, + const u8 *data2, size_t data2_len, + const u8 *data3, size_t data3_len, + u8 *mac); +int eap_pax_initial_key_derivation(u8 mac_id, const u8 *ak, const u8 *e, + u8 *mk, u8 *ck, u8 *ick); + +#endif /* EAP_PAX_COMMON_H */ diff --git a/contrib/hostapd/eap_peap.c b/contrib/hostapd/eap_peap.c index aa91976..9eb61a6 100644 --- a/contrib/hostapd/eap_peap.c +++ b/contrib/hostapd/eap_peap.c @@ -664,6 +664,12 @@ static void eap_peap_process(struct eap_sm *sm, void *priv, data->state, __func__); break; } + + if (tls_connection_get_write_alerts(sm->ssl_ctx, data->ssl.conn) > 1) { + wpa_printf(MSG_INFO, "EAP-PEAP: Locally detected fatal error " + "in TLS processing"); + eap_peap_state(data, FAILURE); + } } diff --git a/contrib/hostapd/eap_psk.c b/contrib/hostapd/eap_psk.c new file mode 100644 index 0000000..2f92d05 --- /dev/null +++ b/contrib/hostapd/eap_psk.c @@ -0,0 +1,458 @@ +/* + * hostapd / EAP-PSK (draft-bersani-eap-psk-09.txt) server + * Copyright (c) 2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * Note: EAP-PSK is an EAP authentication method and as such, completely + * different from WPA-PSK. This file is not needed for WPA-PSK functionality. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <netinet/in.h> + +#include "hostapd.h" +#include "common.h" +#include "eap_i.h" +#include "aes_wrap.h" +#include "eap_psk_common.h" + + +struct eap_psk_data { + enum { PSK_1, PSK_3, SUCCESS, FAILURE } state; + u8 rand_s[EAP_PSK_RAND_LEN]; + u8 rand_p[EAP_PSK_RAND_LEN]; + 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]; +}; + + +static void * eap_psk_init(struct eap_sm *sm) +{ + struct eap_psk_data *data; + + data = malloc(sizeof(*data)); + if (data == NULL) + return data; + memset(data, 0, sizeof(*data)); + data->state = PSK_1; + data->id_s = "hostapd"; + data->id_s_len = 7; + + return data; +} + + +static void eap_psk_reset(struct eap_sm *sm, void *priv) +{ + struct eap_psk_data *data = priv; + free(data->id_p); + free(data); +} + + +static u8 * eap_psk_build_1(struct eap_sm *sm, struct eap_psk_data *data, + int id, size_t *reqDataLen) +{ + struct eap_psk_hdr_1 *req; + + wpa_printf(MSG_DEBUG, "EAP-PSK: PSK-1 (sending)"); + + if (hostapd_get_rand(data->rand_s, EAP_PSK_RAND_LEN)) { + wpa_printf(MSG_ERROR, "EAP-PSK: Failed to get random data"); + data->state = FAILURE; + return NULL; + } + wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: RAND_S (server rand)", + data->rand_s, EAP_PSK_RAND_LEN); + + *reqDataLen = sizeof(*req) + data->id_s_len; + req = malloc(*reqDataLen); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory " + "request"); + data->state = FAILURE; + return NULL; + } + + req->code = EAP_CODE_REQUEST; + req->identifier = id; + req->length = htons(*reqDataLen); + req->type = EAP_TYPE_PSK; + req->flags = 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); + + return (u8 *) req; +} + + +static u8 * eap_psk_build_3(struct eap_sm *sm, struct eap_psk_data *data, + int id, size_t *reqDataLen) +{ + struct eap_psk_hdr_3 *req; + u8 *buf, *pchannel, nonce[16]; + size_t buflen; + + wpa_printf(MSG_DEBUG, "EAP-PSK: PSK-3 (sending)"); + + *reqDataLen = sizeof(*req) + 4 + 16 + 1; + req = malloc(*reqDataLen); + if (req == NULL) { + wpa_printf(MSG_ERROR, "EAP-PSK: Failed to allocate memory " + "request"); + data->state = FAILURE; + return NULL; + } + + req->code = EAP_CODE_REQUEST; + req->identifier = id; + req->length = htons(*reqDataLen); + req->type = EAP_TYPE_PSK; + req->flags = 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) { + data->state = FAILURE; + return NULL; + } + memcpy(buf, data->id_s, data->id_s_len); + memcpy(buf + data->id_s_len, data->rand_p, EAP_PSK_RAND_LEN); + omac1_aes_128(data->ak, buf, buflen, req->mac_s); + free(buf); + + eap_psk_derive_keys(data->kdk, data->rand_p, data->tek, data->msk); + 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); + + memset(nonce, 0, sizeof(nonce)); + pchannel = (u8 *) (req + 1); + memcpy(pchannel, nonce + 12, 4); + memset(pchannel + 4, 0, 16); /* Tag */ + pchannel[4 + 16] = EAP_PSK_R_FLAG_DONE_SUCCESS << 6; + wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL (plaintext)", + pchannel, 4 + 16 + 1); + aes_128_eax_encrypt(data->tek, nonce, sizeof(nonce), (u8 *) req, 22, + pchannel + 4 + 16, 1, pchannel + 4); + wpa_hexdump(MSG_DEBUG, "EAP-PSK: PCHANNEL (encrypted)", + pchannel, 4 + 16 + 1); + + return (u8 *) req; +} + + +static u8 * eap_psk_buildReq(struct eap_sm *sm, void *priv, int id, + size_t *reqDataLen) +{ + struct eap_psk_data *data = priv; + + switch (data->state) { + case PSK_1: + return eap_psk_build_1(sm, data, id, reqDataLen); + case PSK_3: + return eap_psk_build_3(sm, data, id, reqDataLen); + default: + wpa_printf(MSG_DEBUG, "EAP-PSK: Unknown state %d in buildReq", + data->state); + break; + } + return NULL; +} + + +static Boolean eap_psk_check(struct eap_sm *sm, void *priv, + u8 *respData, size_t respDataLen) +{ + struct eap_psk_data *data = priv; + struct eap_psk_hdr *resp; + size_t len; + u8 t; + + resp = (struct eap_psk_hdr *) respData; + if (respDataLen < sizeof(*resp) || resp->type != EAP_TYPE_PSK || + (len = ntohs(resp->length)) > respDataLen || + len < sizeof(*resp)) { + wpa_printf(MSG_INFO, "EAP-PSK: Invalid frame"); + return TRUE; + } + t = resp->flags & 0x03; + + wpa_printf(MSG_DEBUG, "EAP-PSK: received frame: T=%d", t); + + if (data->state == PSK_1 && t != 1) { + wpa_printf(MSG_DEBUG, "EAP-PSK: Expected PSK-2 - " + "ignore T=%d", t); + return TRUE; + } + + if (data->state == PSK_3 && t != 3) { + wpa_printf(MSG_DEBUG, "EAP-PSK: Expected PSK-4 - " + "ignore T=%d", t); + return TRUE; + } + + if ((t == 1 && len < sizeof(struct eap_psk_hdr_2)) || + (t == 3 && len < sizeof(struct eap_psk_hdr_4))) { + wpa_printf(MSG_DEBUG, "EAP-PSK: Too short frame"); + return TRUE; + } + + return FALSE; +} + + +static void eap_psk_process_2(struct eap_sm *sm, + struct eap_psk_data *data, + u8 *respData, size_t respDataLen) +{ + struct eap_psk_hdr_2 *resp; + u8 *pos, mac[EAP_PSK_MAC_LEN], *buf; + size_t len, left, buflen; + int i; + + if (data->state != PSK_1) + return; + + wpa_printf(MSG_DEBUG, "EAP-PSK: Received PSK-2"); + + resp = (struct eap_psk_hdr_2 *) respData; + len = ntohs(resp->length); + pos = (u8 *) (resp + 1); + left = len - sizeof(*resp); + + free(data->id_p); + data->id_p = malloc(left); + if (data->id_p == NULL) { + wpa_printf(MSG_INFO, "EAP-PSK: Failed to allocate memory for " + "ID_P"); + return; + } + memcpy(data->id_p, pos, left); + data->id_p_len = left; + wpa_hexdump_ascii(MSG_MSGDUMP, "EAP-PSK: ID_P", + data->id_p, data->id_p_len); + + if (eap_user_get(sm, data->id_p, data->id_p_len, 0) < 0) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: unknown ID_P", + data->id_p, data->id_p_len); + data->state = FAILURE; + return; + } + + for (i = 0; + i < EAP_MAX_METHODS && sm->user->methods[i] != EAP_TYPE_NONE; + i++) { + if (sm->user->methods[i] == EAP_TYPE_PSK) + break; + } + + if (sm->user->methods[i] != EAP_TYPE_PSK) { + wpa_hexdump_ascii(MSG_DEBUG, + "EAP-PSK: EAP-PSK not enabled for ID_P", + data->id_p, data->id_p_len); + data->state = FAILURE; + return; + } + + if (sm->user->password == NULL || + sm->user->password_len != EAP_PSK_PSK_LEN) { + wpa_hexdump_ascii(MSG_DEBUG, "EAP-PSK: invalid password in " + "user database for ID_P", + data->id_p, data->id_p_len); + data->state = FAILURE; + return; + } + eap_psk_key_setup(sm->user->password, data->ak, data->kdk); + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: AK", data->ak, EAP_PSK_AK_LEN); + wpa_hexdump_key(MSG_DEBUG, "EAP-PSK: KDK", data->kdk, EAP_PSK_KDK_LEN); + + wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: RAND_P (client rand)", + resp->rand_p, EAP_PSK_RAND_LEN); + memcpy(data->rand_p, resp->rand_p, EAP_PSK_RAND_LEN); + + /* MAC_P = OMAC1-AES-128(AK, ID_P||ID_S||RAND_S||RAND_P) */ + buflen = data->id_p_len + data->id_s_len + 2 * EAP_PSK_RAND_LEN; + buf = malloc(buflen); + if (buf == NULL) { + data->state = FAILURE; + return; + } + memcpy(buf, data->id_p, data->id_p_len); + pos = buf + data->id_p_len; + memcpy(pos, data->id_s, data->id_s_len); + pos += data->id_s_len; + memcpy(pos, data->rand_s, EAP_PSK_RAND_LEN); + pos += EAP_PSK_RAND_LEN; + memcpy(pos, data->rand_p, EAP_PSK_RAND_LEN); + omac1_aes_128(data->ak, buf, buflen, mac); + free(buf); + wpa_hexdump(MSG_DEBUG, "EAP-PSK: MAC_P", resp->mac_p, EAP_PSK_MAC_LEN); + if (memcmp(mac, resp->mac_p, EAP_PSK_MAC_LEN) != 0) { + wpa_printf(MSG_INFO, "EAP-PSK: Invalid MAC_P"); + wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: Expected MAC_P", + mac, EAP_PSK_MAC_LEN); + data->state = FAILURE; + return; + } + + data->state = PSK_3; +} + + +static void eap_psk_process_4(struct eap_sm *sm, + struct eap_psk_data *data, + u8 *respData, size_t respDataLen) +{ + struct eap_psk_hdr_4 *resp; + u8 *pos, *decrypted, nonce[16], *tag; + size_t left; + + if (data->state != PSK_3) + return; + + wpa_printf(MSG_DEBUG, "EAP-PSK: Received PSK-4"); + + resp = (struct eap_psk_hdr_4 *) respData; + pos = (u8 *) (resp + 1); + left = ntohs(resp->length) - sizeof(*resp); + + wpa_hexdump(MSG_MSGDUMP, "EAP-PSK: Encrypted PCHANNEL", pos, left); + + if (left < 4 + 16 + 1) { + wpa_printf(MSG_INFO, "EAP-PSK: Too short PCHANNEL data in " + "PSK-4 (len=%lu, expected 21)", + (unsigned long) left); + return; + } + + if (pos[0] == 0 && pos[1] == 0 && pos[2] == 0 && pos[3] == 0) { + wpa_printf(MSG_DEBUG, "EAP-PSK: Nonce did not increase"); + return; + } + + memset(nonce, 0, 12); + memcpy(nonce + 12, pos, 4); + pos += 4; + left -= 4; + tag = pos; + pos += 16; + left -= 16; + + decrypted = malloc(left); + if (decrypted == NULL) + return; + memcpy(decrypted, pos, left); + + if (aes_128_eax_decrypt(data->tek, nonce, sizeof(nonce), + respData, 22, decrypted, left, tag)) { + wpa_printf(MSG_WARNING, "EAP-PSK: PCHANNEL decryption failed"); + free(decrypted); + data->state = FAILURE; + return; + } + wpa_hexdump(MSG_DEBUG, "EAP-PSK: Decrypted PCHANNEL message", + decrypted, left); + + /* Verify R flag */ + switch (decrypted[0] >> 6) { + case EAP_PSK_R_FLAG_CONT: + wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - CONT - unsupported"); + data->state = FAILURE; + break; + case EAP_PSK_R_FLAG_DONE_SUCCESS: + wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_SUCCESS"); + data->state = SUCCESS; + break; + case EAP_PSK_R_FLAG_DONE_FAILURE: + wpa_printf(MSG_DEBUG, "EAP-PSK: R flag - DONE_FAILURE"); + data->state = FAILURE; + break; + } + free(decrypted); +} + + +static void eap_psk_process(struct eap_sm *sm, void *priv, + u8 *respData, size_t respDataLen) +{ + struct eap_psk_data *data = priv; + struct eap_psk_hdr *resp; + + if (sm->user == NULL || sm->user->password == NULL) { + wpa_printf(MSG_INFO, "EAP-MSCHAPV2: Password not configured"); + data->state = FAILURE; + return; + } + + resp = (struct eap_psk_hdr *) respData; + + switch (resp->flags & 0x03) { + case 1: + eap_psk_process_2(sm, data, respData, respDataLen); + break; + case 3: + eap_psk_process_4(sm, data, respData, respDataLen); + break; + } +} + + +static Boolean eap_psk_isDone(struct eap_sm *sm, void *priv) +{ + struct eap_psk_data *data = priv; + return data->state == SUCCESS || data->state == FAILURE; +} + + +static u8 * eap_psk_getKey(struct eap_sm *sm, void *priv, size_t *len) +{ + struct eap_psk_data *data = priv; + u8 *key; + + if (data->state != SUCCESS) + return NULL; + + key = malloc(EAP_PSK_MSK_LEN); + if (key == NULL) + return NULL; + memcpy(key, data->msk, EAP_PSK_MSK_LEN); + *len = EAP_PSK_MSK_LEN; + + return key; +} + + +static Boolean eap_psk_isSuccess(struct eap_sm *sm, void *priv) +{ + struct eap_psk_data *data = priv; + return data->state == SUCCESS; +} + + +const struct eap_method eap_method_psk = +{ + .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, +}; diff --git a/contrib/hostapd/eap_psk_common.c b/contrib/hostapd/eap_psk_common.c new file mode 100644 index 0000000..24de66c --- /dev/null +++ b/contrib/hostapd/eap_psk_common.c @@ -0,0 +1,57 @@ +/* + * WPA Supplicant / EAP-PSK shared routines + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "common.h" +#include "aes_wrap.h" +#include "eap_psk_common.h" + +#define aes_block_size 16 + + +void eap_psk_key_setup(const u8 *psk, u8 *ak, u8 *kdk) +{ + memset(ak, 0, aes_block_size); + aes_128_encrypt_block(psk, ak, ak); + memcpy(kdk, ak, aes_block_size); + ak[aes_block_size - 1] ^= 0x01; + kdk[aes_block_size - 1] ^= 0x02; + aes_128_encrypt_block(psk, ak, ak); + aes_128_encrypt_block(psk, kdk, kdk); +} + + +void eap_psk_derive_keys(const u8 *kdk, const u8 *rand_p, u8 *tek, u8 *msk) +{ + u8 hash[aes_block_size]; + u8 counter = 1; + int i; + + aes_128_encrypt_block(kdk, rand_p, hash); + + hash[aes_block_size - 1] ^= counter; + aes_128_encrypt_block(kdk, hash, tek); + hash[aes_block_size - 1] ^= counter; + counter++; + + for (i = 0; i < EAP_PSK_MSK_LEN / aes_block_size; i++) { + hash[aes_block_size - 1] ^= counter; + aes_128_encrypt_block(kdk, hash, &msk[i * aes_block_size]); + hash[aes_block_size - 1] ^= counter; + counter++; + } +} diff --git a/contrib/hostapd/eap_psk_common.h b/contrib/hostapd/eap_psk_common.h new file mode 100644 index 0000000..5dd3a10 --- /dev/null +++ b/contrib/hostapd/eap_psk_common.h @@ -0,0 +1,92 @@ +/* + * WPA Supplicant / EAP-PSK shared routines + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef EAP_PSK_COMMON_H +#define EAP_PSK_COMMON_H + + +#define EAP_PSK_RAND_LEN 16 +#define EAP_PSK_MAC_LEN 16 +#define EAP_PSK_TEK_LEN 16 +#define EAP_PSK_MSK_LEN 64 +#define EAP_PSK_PSK_LEN 16 +#define EAP_PSK_AK_LEN 16 +#define EAP_PSK_KDK_LEN 16 + +#define EAP_PSK_R_FLAG_CONT 1 +#define EAP_PSK_R_FLAG_DONE_SUCCESS 2 +#define EAP_PSK_R_FLAG_DONE_FAILURE 3 +#define EAP_PSK_E_FLAG 0x20 + +/* Shared prefix for all EAP-PSK frames */ +struct eap_psk_hdr { + u8 code; + u8 identifier; + u16 length; /* including code, identifier, and length */ + u8 type; /* EAP_TYPE_PSK */ + u8 flags; +} __attribute__ ((packed)); + +/* EAP-PSK First Message (AS -> Supplicant) */ +struct eap_psk_hdr_1 { + u8 code; + u8 identifier; + u16 length; /* including code, identifier, and length */ + u8 type; /* EAP_TYPE_PSK */ + u8 flags; + u8 rand_s[EAP_PSK_RAND_LEN]; + /* Followed by variable length ID_S */ +} __attribute__ ((packed)); + +/* EAP-PSK Second Message (Supplicant -> AS) */ +struct eap_psk_hdr_2 { + u8 code; + u8 identifier; + u16 length; /* including code, identifier, and length */ + u8 type; /* EAP_TYPE_PSK */ + u8 flags; + u8 rand_s[EAP_PSK_RAND_LEN]; + u8 rand_p[EAP_PSK_RAND_LEN]; + u8 mac_p[EAP_PSK_MAC_LEN]; + /* Followed by variable length ID_P */ +} __attribute__ ((packed)); + +/* EAP-PSK Third Message (AS -> Supplicant) */ +struct eap_psk_hdr_3 { + u8 code; + u8 identifier; + u16 length; /* including code, identifier, and length */ + u8 type; /* EAP_TYPE_PSK */ + u8 flags; + u8 rand_s[EAP_PSK_RAND_LEN]; + u8 mac_s[EAP_PSK_MAC_LEN]; + /* Followed by variable length PCHANNEL */ +} __attribute__ ((packed)); + +/* EAP-PSK Fourth Message (Supplicant -> AS) */ +struct eap_psk_hdr_4 { + u8 code; + u8 identifier; + u16 length; /* including code, identifier, and length */ + u8 type; /* EAP_TYPE_PSK */ + u8 flags; + u8 rand_s[EAP_PSK_RAND_LEN]; + /* Followed by variable length PCHANNEL */ +} __attribute__ ((packed)); + + +void eap_psk_key_setup(const u8 *psk, u8 *ak, u8 *kdk); +void eap_psk_derive_keys(const u8 *kdk, const u8 *rand_p, u8 *tek, u8 *msk); + +#endif /* EAP_PSK_COMMON_H */ diff --git a/contrib/hostapd/eap_sim.c b/contrib/hostapd/eap_sim.c index aade181..fa60cf5 100644 --- a/contrib/hostapd/eap_sim.c +++ b/contrib/hostapd/eap_sim.c @@ -19,7 +19,7 @@ #include "hostapd.h" #include "common.h" -#include "sha1.h" +#include "crypto.h" #include "eap_i.h" #include "eap_sim_common.h" #include "eap_sim_db.h" diff --git a/contrib/hostapd/eap_sim_common.c b/contrib/hostapd/eap_sim_common.c index 98f4fb7..75947b7 100644 --- a/contrib/hostapd/eap_sim_common.c +++ b/contrib/hostapd/eap_sim_common.c @@ -19,14 +19,11 @@ #include "common.h" #include "eap_i.h" #include "sha1.h" +#include "crypto.h" #include "aes_wrap.h" #include "eap_sim_common.h" -#define MSK_LEN 8 -#define EMSK_LEN 8 - - static void eap_sim_prf(const u8 *key, u8 *x, size_t xlen) { u8 xkey[64]; @@ -86,22 +83,17 @@ void eap_sim_derive_keys(const u8 *mk, u8 *k_encr, u8 *k_aut, u8 *msk) memcpy(k_aut, pos, EAP_SIM_K_AUT_LEN); pos += EAP_SIM_K_AUT_LEN; memcpy(msk, pos, EAP_SIM_KEYING_DATA_LEN); - pos += MSK_LEN; wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_encr", k_encr, EAP_SIM_K_ENCR_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: K_aut", k_aut, EAP_SIM_K_ENCR_LEN); - wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: MSK", - msk, MSK_LEN); - wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: Ext. MSK", - msk + MSK_LEN, EMSK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-SIM: keying material", - msk, EAP_SIM_KEYING_DATA_LEN); + msk, EAP_SIM_KEYING_DATA_LEN); } -void eap_sim_derive_keys_reauth(unsigned int _counter, +void eap_sim_derive_keys_reauth(u16 _counter, const u8 *identity, size_t identity_len, const u8 *nonce_s, const u8 *mk, u8 *msk) { @@ -119,8 +111,7 @@ void eap_sim_derive_keys_reauth(unsigned int _counter, addr[3] = mk; len[3] = EAP_SIM_MK_LEN; - counter[0] = _counter >> 8; - counter[1] = _counter & 0xff; + WPA_PUT_BE16(counter, _counter); wpa_printf(MSG_DEBUG, "EAP-SIM: Deriving keying data from reauth"); wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM: Identity", @@ -135,34 +126,37 @@ void eap_sim_derive_keys_reauth(unsigned int _counter, wpa_hexdump(MSG_DEBUG, "EAP-SIM: XKEY'", xkey, SHA1_MAC_LEN); eap_sim_prf(xkey, msk, EAP_SIM_KEYING_DATA_LEN); - wpa_hexdump(MSG_DEBUG, "EAP-SIM: MSK", msk, MSK_LEN); - wpa_hexdump(MSG_DEBUG, "EAP-SIM: Ext. MSK", msk + MSK_LEN, EMSK_LEN); wpa_hexdump(MSG_DEBUG, "EAP-SIM: keying material", msk, EAP_SIM_KEYING_DATA_LEN); } -int eap_sim_verify_mac(const u8 *k_aut, u8 *req, size_t req_len, u8 *mac, - u8 *extra, size_t extra_len) +int eap_sim_verify_mac(const u8 *k_aut, const u8 *req, size_t req_len, + const u8 *mac, const u8 *extra, size_t extra_len) { unsigned char hmac[SHA1_MAC_LEN]; const u8 *addr[2]; size_t len[2]; - u8 rx_mac[EAP_SIM_MAC_LEN]; + u8 *tmp; + + if (mac == NULL || req_len < EAP_SIM_MAC_LEN || mac < req || + mac > req + req_len - EAP_SIM_MAC_LEN) + return -1; - if (mac == NULL) + tmp = malloc(req_len); + if (tmp == NULL) return -1; - addr[0] = req; + addr[0] = tmp; len[0] = req_len; addr[1] = extra; len[1] = extra_len; /* HMAC-SHA1-128 */ - memcpy(rx_mac, mac, EAP_SIM_MAC_LEN); - memset(mac, 0, EAP_SIM_MAC_LEN); + memcpy(tmp, req, req_len); + memset(tmp + (mac - req), 0, EAP_SIM_MAC_LEN); hmac_sha1_vector(k_aut, EAP_SIM_K_AUT_LEN, 2, addr, len, hmac); - memcpy(mac, rx_mac, EAP_SIM_MAC_LEN); + free(tmp); return (memcmp(hmac, mac, EAP_SIM_MAC_LEN) == 0) ? 0 : 1; } @@ -187,10 +181,10 @@ void eap_sim_add_mac(const u8 *k_aut, u8 *msg, size_t msg_len, u8 *mac, } -int eap_sim_parse_attr(u8 *start, u8 *end, struct eap_sim_attrs *attr, int aka, - int encr) +int eap_sim_parse_attr(const u8 *start, const u8 *end, + struct eap_sim_attrs *attr, int aka, int encr) { - u8 *pos = start, *apos; + const u8 *pos = start, *apos; size_t alen, plen; int list_len, i; @@ -473,25 +467,35 @@ int eap_sim_parse_attr(u8 *start, u8 *end, struct eap_sim_attrs *attr, int aka, } -int eap_sim_parse_encr(const u8 *k_encr, u8 *encr_data, size_t encr_data_len, - const u8 *iv, struct eap_sim_attrs *attr, int aka) +u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data, + size_t encr_data_len, const u8 *iv, + struct eap_sim_attrs *attr, int aka) { + u8 *decrypted; + if (!iv) { wpa_printf(MSG_INFO, "EAP-SIM: Encrypted data, but no IV"); - return -1; + return NULL; } - aes_128_cbc_decrypt(k_encr, iv, encr_data, encr_data_len); + + decrypted = malloc(encr_data_len); + if (decrypted == NULL) + return NULL; + memcpy(decrypted, encr_data, encr_data_len); + + aes_128_cbc_decrypt(k_encr, iv, decrypted, encr_data_len); wpa_hexdump(MSG_MSGDUMP, "EAP-SIM: Decrypted AT_ENCR_DATA", - encr_data, encr_data_len); + decrypted, encr_data_len); - if (eap_sim_parse_attr(encr_data, encr_data + encr_data_len, attr, + if (eap_sim_parse_attr(decrypted, decrypted + encr_data_len, attr, aka, 1)) { wpa_printf(MSG_INFO, "EAP-SIM: (encr) Failed to parse " "decrypted AT_ENCR_DATA"); - return -1; + free(decrypted); + return NULL; } - return 0; + return decrypted; } @@ -628,8 +632,8 @@ u8 * eap_sim_msg_add(struct eap_sim_msg *msg, u8 attr, u16 value, start = pos = msg->buf + msg->used; *pos++ = attr; *pos++ = attr_len / 4; - *pos++ = value >> 8; - *pos++ = value & 0xff; + WPA_PUT_BE16(pos, value); + pos += 2; if (data) memcpy(pos, data, len); if (pad_len) { @@ -709,7 +713,9 @@ int eap_sim_msg_add_encr_end(struct eap_sim_msg *msg, u8 *k_encr, int attr_pad) void eap_sim_report_notification(void *msg_ctx, int notification, int aka) { +#ifndef CONFIG_NO_STDOUT_DEBUG const char *type = aka ? "AKA" : "SIM"; +#endif /* CONFIG_NO_STDOUT_DEBUG */ switch (notification) { case EAP_SIM_GENERAL_FAILURE_AFTER_AUTH: diff --git a/contrib/hostapd/eap_sim_common.h b/contrib/hostapd/eap_sim_common.h index c89e04e..6715c36 100644 --- a/contrib/hostapd/eap_sim_common.h +++ b/contrib/hostapd/eap_sim_common.h @@ -1,3 +1,17 @@ +/* + * WPA Supplicant / EAP-SIM/AKA shared routines + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + #ifndef EAP_SIM_COMMON_H #define EAP_SIM_COMMON_H @@ -16,11 +30,11 @@ #define AKA_AUTN_LEN 16 void eap_sim_derive_keys(const u8 *mk, u8 *k_encr, u8 *k_aut, u8 *msk); -void eap_sim_derive_keys_reauth(unsigned int _counter, +void eap_sim_derive_keys_reauth(u16 _counter, const u8 *identity, size_t identity_len, const u8 *nonce_s, const u8 *mk, u8 *msk); -int eap_sim_verify_mac(const u8 *k_aut, u8 *req, size_t req_len, u8 *mac, - u8 *extra, size_t extra_len); +int eap_sim_verify_mac(const u8 *k_aut, const u8 *req, size_t req_len, + const u8 *mac, const u8 *extra, size_t extra_len); void eap_sim_add_mac(const u8 *k_aut, u8 *msg, size_t msg_len, u8 *mac, const u8 *extra, size_t extra_len); @@ -65,19 +79,20 @@ enum eap_sim_id_req { struct eap_sim_attrs { - u8 *rand, *autn, *mac, *iv, *encr_data, *version_list, *nonce_s; - u8 *next_pseudonym, *next_reauth_id; - u8 *nonce_mt, *identity; + const u8 *rand, *autn, *mac, *iv, *encr_data, *version_list, *nonce_s; + const u8 *next_pseudonym, *next_reauth_id; + const u8 *nonce_mt, *identity; size_t num_chal, version_list_len, encr_data_len; size_t next_pseudonym_len, next_reauth_id_len, identity_len; enum eap_sim_id_req id_req; int notification, counter, selected_version, client_error_code; }; -int eap_sim_parse_attr(u8 *start, u8 *end, struct eap_sim_attrs *attr, - int aka, int encr); -int eap_sim_parse_encr(const u8 *k_encr, u8 *encr_data, size_t encr_data_len, - const u8 *iv, struct eap_sim_attrs *attr, int aka); +int eap_sim_parse_attr(const u8 *start, const u8 *end, + struct eap_sim_attrs *attr, int aka, int encr); +u8 * eap_sim_parse_encr(const u8 *k_encr, const u8 *encr_data, + size_t encr_data_len, const u8 *iv, + struct eap_sim_attrs *attr, int aka); struct eap_sim_msg; diff --git a/contrib/hostapd/eap_sim_db.c b/contrib/hostapd/eap_sim_db.c index 7f617ce8..a965fa4 100644 --- a/contrib/hostapd/eap_sim_db.c +++ b/contrib/hostapd/eap_sim_db.c @@ -108,6 +108,7 @@ int eap_sim_db_get_gsm_triplets(void *priv, const u8 *identity, if (identity_len < 2 || identity[0] != '1') { wpa_hexdump_ascii(MSG_DEBUG, "EAP-SIM DB: unexpected identity", identity, identity_len); + fclose(f); return -1; } identity++; diff --git a/contrib/hostapd/eap_tls.c b/contrib/hostapd/eap_tls.c index 58ab277..bf76f5a 100644 --- a/contrib/hostapd/eap_tls.c +++ b/contrib/hostapd/eap_tls.c @@ -191,6 +191,13 @@ static void eap_tls_process(struct eap_sm *sm, void *priv, data->state = FAILURE; return; } + + if (tls_connection_get_write_alerts(sm->ssl_ctx, data->ssl.conn) > 1) { + wpa_printf(MSG_INFO, "EAP-TLS: Locally detected fatal error " + "in TLS processing"); + data->state = FAILURE; + return; + } } diff --git a/contrib/hostapd/eap_tls_common.c b/contrib/hostapd/eap_tls_common.c index ca10eca..d573064 100644 --- a/contrib/hostapd/eap_tls_common.c +++ b/contrib/hostapd/eap_tls_common.c @@ -38,8 +38,7 @@ int eap_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, return -1; } - if (tls_connection_set_verify(sm->ssl_ctx, data->conn, verify_peer, - NULL)) { + if (tls_connection_set_verify(sm->ssl_ctx, data->conn, verify_peer)) { wpa_printf(MSG_INFO, "SSL: Failed to configure verification " "of TLS peer certificate"); tls_connection_deinit(sm->ssl_ctx, data->conn); @@ -185,6 +184,13 @@ int eap_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, wpa_printf(MSG_DEBUG, "SSL: No data to be sent out"); free(data->tls_out); data->tls_out = NULL; + + if (tls_connection_get_read_alerts(sm->ssl_ctx, data->conn)) { + wpa_printf(MSG_DEBUG, "SSL: Remote end sent a fatal " + "alert - abort handshake"); + return -1; + } + return 1; } diff --git a/contrib/hostapd/eap_ttls.c b/contrib/hostapd/eap_ttls.c index 1e4be75..569b1c3 100644 --- a/contrib/hostapd/eap_ttls.c +++ b/contrib/hostapd/eap_ttls.c @@ -23,6 +23,7 @@ #include "eap_tls_common.h" #include "ms_funcs.h" #include "md5.h" +#include "crypto.h" #include "tls.h" #include "eap_ttls.h" @@ -567,8 +568,9 @@ static void eap_ttls_process_phase2_chap(struct eap_sm *sm, const u8 *password, size_t password_len) { - MD5_CTX context; u8 *chal, hash[MD5_MAC_LEN]; + const u8 *addr[3]; + size_t len[3]; if (challenge == NULL || password == NULL || challenge_len != EAP_TTLS_CHAP_CHALLENGE_LEN || @@ -609,11 +611,13 @@ static void eap_ttls_process_phase2_chap(struct eap_sm *sm, free(chal); /* MD5(Ident + Password + Challenge) */ - MD5Init(&context); - MD5Update(&context, password, 1); - MD5Update(&context, sm->user->password, sm->user->password_len); - MD5Update(&context, challenge, challenge_len); - MD5Final(hash, &context); + addr[0] = password; + len[0] = 1; + addr[1] = sm->user->password; + len[1] = sm->user->password_len; + addr[2] = challenge; + len[2] = challenge_len; + md5_vector(3, addr, len, hash); if (memcmp(hash, password + 1, EAP_TTLS_CHAP_PASSWORD_LEN) == 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/CHAP: Correct user password"); @@ -1128,6 +1132,12 @@ static void eap_ttls_process(struct eap_sm *sm, void *priv, data->state, __func__); break; } + + if (tls_connection_get_write_alerts(sm->ssl_ctx, data->ssl.conn) > 1) { + wpa_printf(MSG_INFO, "EAP-TTLS: Locally detected fatal error " + "in TLS processing"); + eap_ttls_state(data, FAILURE); + } } diff --git a/contrib/hostapd/eap_ttls.h b/contrib/hostapd/eap_ttls.h index a187db4..f35f5a9 100644 --- a/contrib/hostapd/eap_ttls.h +++ b/contrib/hostapd/eap_ttls.h @@ -1,3 +1,17 @@ +/* + * WPA Supplicant / EAP-TTLS (draft-ietf-pppext-eap-ttls-03.txt) + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + #ifndef EAP_TTLS_H #define EAP_TTLS_H diff --git a/contrib/hostapd/eapol_sm.c b/contrib/hostapd/eapol_sm.c index fe710a6..f2d5ec7 100644 --- a/contrib/hostapd/eapol_sm.c +++ b/contrib/hostapd/eapol_sm.c @@ -66,6 +66,9 @@ static void sm_ ## machine ## _Step(struct eapol_state_machine *sm) #define SM_STEP_RUN(machine) sm_ ## machine ## _Step(sm) +static void eapol_sm_step_run(struct eapol_state_machine *sm); +static void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx); + /* Port Timers state machine - implemented as a function that will be called * once a second as a registered event loop timeout */ @@ -74,19 +77,34 @@ static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx) { struct eapol_state_machine *state = timeout_ctx; - if (state->aWhile > 0) + if (state->aWhile > 0) { state->aWhile--; - if (state->quietWhile > 0) + if (state->aWhile == 0) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR + " - aWhile --> 0", + MAC2STR(state->addr)); + } + } + + if (state->quietWhile > 0) { state->quietWhile--; - if (state->reAuthWhen > 0) - state->reAuthWhen--; + if (state->quietWhile == 0) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR + " - quietWhile --> 0", + MAC2STR(state->addr)); + } + } - if (state->hapd->conf->debug >= HOSTAPD_DEBUG_MSGDUMPS) - printf("IEEE 802.1X: " MACSTR " Port Timers TICK " - "(timers: %d %d %d)\n", MAC2STR(state->addr), - state->aWhile, state->quietWhile, state->reAuthWhen); + if (state->reAuthWhen > 0) { + state->reAuthWhen--; + if (state->reAuthWhen == 0) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR + " - reAuthWhen --> 0", + MAC2STR(state->addr)); + } + } - eapol_sm_step(state); + eapol_sm_step_run(state); eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx, state); } @@ -357,6 +375,19 @@ SM_STATE(BE_AUTH, REQUEST) txReq(); sm->be_auth.eapReq = FALSE; sm->be_auth.backendOtherRequestsToSupplicant++; + + /* + * Clearing eapolEap here is not specified in IEEE Std 802.1X-2004, but + * it looks like this would be logical thing to do there since the old + * EAP response would not be valid anymore after the new EAP request + * was sent out. + * + * A race condition has been reported, in which hostapd ended up + * sending out EAP-Response/Identity as a response to the first + * EAP-Request from the main EAP method. This can be avoided by + * clearing eapolEap here. + */ + sm->eapolEap = FALSE; } @@ -703,7 +734,7 @@ eapol_sm_alloc(hostapd *hapd, struct sta_info *sta) else sm->portValid = TRUE; - if (hapd->conf->eap_authenticator) { + if (hapd->conf->eap_server) { struct eap_config eap_conf; memset(&eap_conf, 0, sizeof(eap_conf)); eap_conf.ssl_ctx = hapd->ssl_ctx; @@ -727,6 +758,7 @@ void eapol_sm_free(struct eapol_state_machine *sm) return; eloop_cancel_timeout(eapol_port_timers_tick, sm->hapd, sm); + eloop_cancel_timeout(eapol_sm_step_cb, sm, NULL); if (sm->eap) eap_sm_deinit(sm->eap); free(sm); @@ -743,55 +775,63 @@ static int eapol_sm_sta_entry_alive(struct hostapd_data *hapd, u8 *addr) } -void eapol_sm_step(struct eapol_state_machine *sm) +static void eapol_sm_step_run(struct eapol_state_machine *sm) { struct hostapd_data *hapd = sm->hapd; u8 addr[ETH_ALEN]; int prev_auth_pae, prev_be_auth, prev_reauth_timer, prev_auth_key_tx, prev_key_rx, prev_ctrl_dir; - - /* FIX: could re-run eapol_sm_step from registered timeout (after - * 0 sec) to make sure that other possible timeouts/events are - * processed */ + int max_steps = 100; memcpy(addr, sm->sta->addr, ETH_ALEN); + + /* + * Allow EAPOL state machines to run as long as there are state + * changes, but exit and return here through event loop if more than + * 100 steps is needed as a precaution against infinite loops inside + * eloop callback. + */ restart: - do { - prev_auth_pae = sm->auth_pae.state; - prev_be_auth = sm->be_auth.state; - prev_reauth_timer = sm->reauth_timer.state; - prev_auth_key_tx = sm->auth_key_tx.state; - prev_key_rx = sm->key_rx.state; - prev_ctrl_dir = sm->ctrl_dir.state; - - SM_STEP_RUN(AUTH_PAE); - if (!sm->initializing && !eapol_sm_sta_entry_alive(hapd, addr)) - break; + prev_auth_pae = sm->auth_pae.state; + prev_be_auth = sm->be_auth.state; + prev_reauth_timer = sm->reauth_timer.state; + prev_auth_key_tx = sm->auth_key_tx.state; + prev_key_rx = sm->key_rx.state; + prev_ctrl_dir = sm->ctrl_dir.state; + + SM_STEP_RUN(AUTH_PAE); + if (sm->initializing || eapol_sm_sta_entry_alive(hapd, addr)) SM_STEP_RUN(BE_AUTH); - if (!sm->initializing && !eapol_sm_sta_entry_alive(hapd, addr)) - break; + if (sm->initializing || eapol_sm_sta_entry_alive(hapd, addr)) SM_STEP_RUN(REAUTH_TIMER); - if (!sm->initializing && !eapol_sm_sta_entry_alive(hapd, addr)) - break; + if (sm->initializing || eapol_sm_sta_entry_alive(hapd, addr)) SM_STEP_RUN(AUTH_KEY_TX); - if (!sm->initializing && !eapol_sm_sta_entry_alive(hapd, addr)) - break; + if (sm->initializing || eapol_sm_sta_entry_alive(hapd, addr)) SM_STEP_RUN(KEY_RX); - if (!sm->initializing && !eapol_sm_sta_entry_alive(hapd, addr)) - break; + if (sm->initializing || eapol_sm_sta_entry_alive(hapd, addr)) SM_STEP_RUN(CTRL_DIR); - if (!sm->initializing && !eapol_sm_sta_entry_alive(hapd, addr)) - break; - } while (prev_auth_pae != sm->auth_pae.state || - prev_be_auth != sm->be_auth.state || - prev_reauth_timer != sm->reauth_timer.state || - prev_auth_key_tx != sm->auth_key_tx.state || - prev_key_rx != sm->key_rx.state || - prev_ctrl_dir != sm->ctrl_dir.state); - if (eapol_sm_sta_entry_alive(hapd, addr) && sm->eap) { - if (eap_sm_step(sm->eap)) + if (prev_auth_pae != sm->auth_pae.state || + prev_be_auth != sm->be_auth.state || + prev_reauth_timer != sm->reauth_timer.state || + prev_auth_key_tx != sm->auth_key_tx.state || + prev_key_rx != sm->key_rx.state || + prev_ctrl_dir != sm->ctrl_dir.state) { + if (--max_steps > 0) goto restart; + /* Re-run from eloop timeout */ + eapol_sm_step(sm); + return; + } + + if (eapol_sm_sta_entry_alive(hapd, addr) && sm->eap) { + if (eap_sm_step(sm->eap)) { + if (--max_steps > 0) + goto restart; + /* Re-run from eloop timeout */ + eapol_sm_step(sm); + return; + } } if (eapol_sm_sta_entry_alive(hapd, addr)) @@ -799,15 +839,34 @@ restart: } +static void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx) +{ + struct eapol_state_machine *sm = eloop_ctx; + eapol_sm_step_run(sm); +} + + +void eapol_sm_step(struct eapol_state_machine *sm) +{ + /* + * Run eapol_sm_step_run from a registered timeout to make sure that + * other possible timeouts/events are processed and to avoid long + * function call chains. + */ + + eloop_register_timeout(0, 0, eapol_sm_step_cb, sm, NULL); +} + + void eapol_sm_initialize(struct eapol_state_machine *sm) { sm->initializing = TRUE; /* Initialize the state machines by asserting initialize and then * deasserting it after one step */ sm->initialize = TRUE; - eapol_sm_step(sm); + eapol_sm_step_run(sm); sm->initialize = FALSE; - eapol_sm_step(sm); + eapol_sm_step_run(sm); sm->initializing = FALSE; /* Start one second tick for port timers state machine */ @@ -1181,6 +1240,14 @@ static int eapol_sm_get_eap_user(void *ctx, const u8 *identity, } +static const char * eapol_sm_get_eap_req_id_text(void *ctx, size_t *len) +{ + struct eapol_state_machine *sm = ctx; + *len = sm->hapd->conf->eap_req_id_text_len; + return sm->hapd->conf->eap_req_id_text; +} + + static struct eapol_callbacks eapol_cb = { .get_bool = eapol_sm_get_bool, @@ -1188,4 +1255,5 @@ static struct eapol_callbacks eapol_cb = .set_eapReqData = eapol_sm_set_eapReqData, .set_eapKeyData = eapol_sm_set_eapKeyData, .get_eap_user = eapol_sm_get_eap_user, + .get_eap_req_id_text = eapol_sm_get_eap_req_id_text, }; diff --git a/contrib/hostapd/eapol_sm.h b/contrib/hostapd/eapol_sm.h index c89dd02..0c34b4f 100644 --- a/contrib/hostapd/eapol_sm.h +++ b/contrib/hostapd/eapol_sm.h @@ -110,6 +110,16 @@ struct eapol_ctrl_dir { struct eap_sm; +struct radius_attr_data { + u8 *data; + size_t len; +}; + +struct radius_class_data { + struct radius_attr_data *attr; + size_t count; +}; + struct eapol_state_machine { /* timers */ int aWhile; @@ -175,8 +185,7 @@ struct eapol_state_machine { size_t last_eap_radius_len; u8 *identity; size_t identity_len; - u8 *radius_class; - size_t radius_class_len; + struct radius_class_data radius_class; /* Keys for encrypting and signing EAPOL-Key frames */ u8 *eapol_key_sign; diff --git a/contrib/hostapd/eloop.c b/contrib/hostapd/eloop.c index 6071508..39fccb8 100644 --- a/contrib/hostapd/eloop.c +++ b/contrib/hostapd/eloop.c @@ -1,5 +1,5 @@ /* - * Event loop + * Event loop based on select() loop * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> * * This program is free software; you can redistribute it and/or modify @@ -294,10 +294,16 @@ int eloop_register_signal(int sig, void eloop_run(void) { - fd_set rfds; + fd_set *rfds; int i, res; struct timeval tv, now; + rfds = malloc(sizeof(*rfds)); + if (rfds == NULL) { + printf("eloop_run - malloc failed\n"); + return; + } + while (!eloop.terminate && (eloop.timeout || eloop.reader_count > 0)) { if (eloop.timeout) { @@ -312,13 +318,14 @@ void eloop_run(void) #endif } - FD_ZERO(&rfds); + FD_ZERO(rfds); for (i = 0; i < eloop.reader_count; i++) - FD_SET(eloop.readers[i].sock, &rfds); - res = select(eloop.max_sock + 1, &rfds, NULL, NULL, + FD_SET(eloop.readers[i].sock, rfds); + res = select(eloop.max_sock + 1, rfds, NULL, NULL, eloop.timeout ? &tv : NULL); if (res < 0 && errno != EINTR) { perror("select"); + free(rfds); return; } eloop_process_pending_signals(); @@ -342,7 +349,7 @@ void eloop_run(void) continue; for (i = 0; i < eloop.reader_count; i++) { - if (FD_ISSET(eloop.readers[i].sock, &rfds)) { + if (FD_ISSET(eloop.readers[i].sock, rfds)) { eloop.readers[i].handler( eloop.readers[i].sock, eloop.readers[i].eloop_data, @@ -350,6 +357,8 @@ void eloop_run(void) } } } + + free(rfds); } diff --git a/contrib/hostapd/eloop.h b/contrib/hostapd/eloop.h index f5b8847..c57e682 100644 --- a/contrib/hostapd/eloop.h +++ b/contrib/hostapd/eloop.h @@ -1,53 +1,152 @@ +/* + * Event loop + * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * This file defines an event loop interface that supports processing events + * from registered timeouts (i.e., do something after N seconds), sockets + * (e.g., a new packet available for reading), and signals. eloop.c is an + * implementation of this interface using select() and sockets. This is + * suitable for most UNIX/POSIX systems. When porting to other operating + * systems, it may be necessary to replace that implementation with OS specific + * mechanisms. + */ + #ifndef ELOOP_H #define ELOOP_H /* Magic number for eloop_cancel_timeout() */ #define ELOOP_ALL_CTX (void *) -1 -/* Initialize global event loop data - must be called before any other eloop_* - * function. user_data is a pointer to global data structure and will be passed - * as eloop_ctx to signal handlers. */ +/** + * eloop_init() - Initialize global event loop data + * @user_data: Pointer to global data passed as eloop_ctx to signal handlers + * + * This function must be called before any other eloop_* function. user_data + * can be used to configure a global (to the process) pointer that will be + * passed as eloop_ctx parameter to signal handlers. + */ void eloop_init(void *user_data); -/* Register handler for read event */ +/** + * eloop_register_read_sock - Register handler for read events + * @sock: File descriptor number for the socket + * @handler: Callback function to be called when data is available for reading + * @eloop_data: Callback context data (eloop_ctx) + * @user_data: Callback context data (sock_ctx) + * Returns: 0 on success, -1 on failure + * + * Register a read socket notifier for the given file descriptor. The handler + * function will be called whenever data is available for reading from the + * socket. + */ int eloop_register_read_sock(int sock, void (*handler)(int sock, void *eloop_ctx, void *sock_ctx), void *eloop_data, void *user_data); + +/** + * eloop_unregister_read_sock - Unregister handler for read events + * @sock: File descriptor number for the socket + * + * Unregister a read socket notifier that was previously registered with + * eloop_register_read_sock(). + */ void eloop_unregister_read_sock(int sock); -/* Register timeout */ +/** + * eloop_register_timeout - Register timeout + * @secs: Number of seconds to the timeout + * @usecs: Number of microseconds to the timeout + * @handler: Callback function to be called when timeout occurs + * @eloop_data: Callback context data (eloop_ctx) + * @user_data: Callback context data (sock_ctx) + * Returns: 0 on success, -1 on failure + * + * Register a timeout that will cause the handler function to be called after + * given time. + */ int eloop_register_timeout(unsigned int secs, unsigned int usecs, void (*handler)(void *eloop_ctx, void *timeout_ctx), void *eloop_data, void *user_data); -/* Cancel timeouts matching <handler,eloop_data,user_data>. - * ELOOP_ALL_CTX can be used as a wildcard for cancelling all timeouts - * regardless of eloop_data/user_data. */ +/** + * eloop_cancel_timeout - Cancel timeouts + * @handler: Matching callback function + * @eloop_data: Matching eloop_data or %ELOOP_ALL_CTX to match all + * @user_data: Matching user_data or %ELOOP_ALL_CTX to match all + * Returns: Number of cancelled timeouts + * + * Cancel matching <handler,eloop_data,user_data> timeouts registered with + * eloop_register_timeout(). ELOOP_ALL_CTX can be used as a wildcard for + * cancelling all timeouts regardless of eloop_data/user_data. + */ int eloop_cancel_timeout(void (*handler)(void *eloop_ctx, void *sock_ctx), void *eloop_data, void *user_data); -/* Register handler for signal. - * Note: signals are 'global' events and there is no local eloop_data pointer - * like with other handlers. The (global) pointer given to eloop_init() will be - * used as eloop_ctx for signal handlers. */ -int eloop_register_signal(int sock, +/** + * eloop_register_signal - Register handler for signals + * @sig: Signal number (e.g., SIGHUP) + * @handler: Callback function to be called when the signal is received + * @user_data: Callback context data (signal_ctx) + * Returns: 0 on success, -1 on failure + * + * Register a callback function that will be called when a signal is received. + * The calback function is actually called only after the system signal handler + * has returned. This means that the normal limits for sighandlers (i.e., only + * "safe functions" allowed) do not apply for the registered callback. + * + * Signals are 'global' events and there is no local eloop_data pointer like + * with other handlers. The global user_data pointer registered with + * eloop_init() will be used as eloop_ctx for signal handlers. + */ +int eloop_register_signal(int sig, void (*handler)(int sig, void *eloop_ctx, void *signal_ctx), void *user_data); -/* Start event loop and continue running as long as there are any registered - * event handlers. */ +/** + * eloop_run - Start the event loop + * + * Start the event loop and continue running as long as there are any + * registered event handlers. This function is run after event loop has been + * initialized with event_init() and one or more events have been registered. + */ void eloop_run(void); -/* Terminate event loop even if there are registered events. */ +/** + * eloop_terminate - Terminate event loop + * + * Terminate event loop even if there are registered events. This can be used + * to request the program to be terminated cleanly. + */ void eloop_terminate(void); -/* Free any reserved resources. After calling eloop_destoy(), other eloop_* - * functions must not be called before re-running eloop_init(). */ +/** + * eloop_destroy - Free any resources allocated for the event loop + * + * After calling eloop_destoy(), other eloop_* functions must not be called + * before re-running eloop_init(). + */ void eloop_destroy(void); -/* Check whether event loop has been terminated. */ +/** + * eloop_terminated - Check whether event loop has been terminated + * Returns: 1 = event loop terminate, 0 = event loop still running + * + * This function can be used to check whether eloop_terminate() has been called + * to request termination of the event loop. This is normally used to abort + * operations that may still be queued to be run when eloop_terminate() was + * called. + */ int eloop_terminated(void); #endif /* ELOOP_H */ diff --git a/contrib/hostapd/hostap_common.h b/contrib/hostapd/hostap_common.h index 003ad9a..78af287 100644 --- a/contrib/hostapd/hostap_common.h +++ b/contrib/hostapd/hostap_common.h @@ -423,6 +423,7 @@ enum { PRISM2_PARAM_PRIVACY_INVOKED = 37, PRISM2_PARAM_TKIP_COUNTERMEASURES = 38, PRISM2_PARAM_DROP_UNENCRYPTED = 39, + PRISM2_PARAM_SCAN_CHANNEL_MASK = 40, }; enum { HOSTAP_ANTSEL_DO_NOT_TOUCH = 0, HOSTAP_ANTSEL_DIVERSITY = 1, diff --git a/contrib/hostapd/hostapd.8 b/contrib/hostapd/hostapd.8 new file mode 100644 index 0000000..8b02497 --- /dev/null +++ b/contrib/hostapd/hostapd.8 @@ -0,0 +1,56 @@ +.TH HOSTAPD 8 "April 7, 2005" hostapd hostapd +.SH NAME +hostapd \- IEEE 802.11 AP, IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator +.SH SYNOPSIS +.B hostapd +[-hdBKt] <configuration file(s)> +.SH DESCRIPTION +This manual page documents briefly the +.B hostapd +daemon. +.PP +.B hostapd +is a user space daemon for access point and authentication servers. +It implements IEEE 802.11 access point management, IEEE 802.1X/WPA/WPA2/EAP Authenticators and RADIUS authentication server. +The current version supports Linux (Host AP, madwifi, Prism54 drivers) and FreeBSD (net80211). + +.B hostapd +is designed to be a "daemon" program that runs in the background and acts as the backend component controlling authentication. +.B hostapd +supports separate frontend programs and an example text-based frontend, +.BR hostapd_cli , +is included with +.BR hostapd . +.SH OPTIONS +A summary of options is included below. +For a complete description, run +.BR hostapd +from the command line. +.TP +.B \-h +Show usage. +.TP +.B \-d +Show more debug messages. +.TP +.B \-dd +Show even more debug messages. +.TP +.B \-B +Run daemon in the background. +.TP +.B \-K +Include key data in debug messages. +.TP +.B \-t +Include timestamps in some debug messages. +.TP +.B \-v +Show hostapd version. +.SH SEE ALSO +.BR hostapd_cli (1). +.SH AUTHOR +hostapd was written by Jouni Malinen <jkmaline@cc.hut.fi>. +.PP +This manual page was written by Faidon Liambotis <faidon@cube.gr>, +for the Debian project (but may be used by others). diff --git a/contrib/hostapd/hostapd.c b/contrib/hostapd/hostapd.c index 8819641..086af62 100644 --- a/contrib/hostapd/hostapd.c +++ b/contrib/hostapd/hostapd.c @@ -24,6 +24,7 @@ #include <stdarg.h> #include <sys/types.h> #include <sys/socket.h> +#include <arpa/inet.h> #include "eloop.h" #include "hostapd.h" @@ -43,6 +44,7 @@ #include "tls.h" #include "eap_sim_db.h" #include "version.h" +#include "hostap_common.h" struct hapd_interfaces { @@ -58,8 +60,8 @@ extern int wpa_debug_show_keys; extern int wpa_debug_timestamp; -void hostapd_logger(hostapd *hapd, u8 *addr, unsigned int module, int level, - char *fmt, ...) +void hostapd_logger(struct hostapd_data *hapd, const u8 *addr, + unsigned int module, int level, const char *fmt, ...) { char *format, *module_str; int maxlen; @@ -72,8 +74,6 @@ void hostapd_logger(hostapd *hapd, u8 *addr, unsigned int module, int level, if (!format) return; - va_start(ap, fmt); - if (hapd && hapd->conf) { conf_syslog_level = hapd->conf->logger_syslog_level; conf_stdout_level = hapd->conf->logger_stdout_level; @@ -125,7 +125,10 @@ void hostapd_logger(hostapd *hapd, u8 *addr, unsigned int module, int level, module_str, module_str ? ": " : "", fmt); if ((conf_stdout & module) && level >= conf_stdout_level) { + wpa_debug_print_timestamp(); + va_start(ap, fmt); vprintf(format, ap); + va_end(ap); printf("\n"); } @@ -149,12 +152,34 @@ void hostapd_logger(hostapd *hapd, u8 *addr, unsigned int module, int level, priority = LOG_INFO; break; } + va_start(ap, fmt); vsyslog(priority, format, ap); + va_end(ap); } free(format); +} + + +const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf, + size_t buflen) +{ + if (buflen == 0 || addr == NULL) + return NULL; + + if (addr->af == AF_INET) { + snprintf(buf, buflen, "%s", inet_ntoa(addr->u.v4)); + } else { + buf[0] = '\0'; + } +#ifdef CONFIG_IPV6 + if (addr->af == AF_INET6) { + if (inet_ntop(AF_INET6, &addr->u.v6, buf, buflen) == NULL) + buf[0] = '\0'; + } +#endif /* CONFIG_IPV6 */ - va_end(ap); + return buf; } @@ -175,20 +200,30 @@ static void hostapd_deauth_all_stas(hostapd *hapd) /* This function will be called whenever a station associates with the AP */ -void hostapd_new_assoc_sta(hostapd *hapd, struct sta_info *sta) +void hostapd_new_assoc_sta(hostapd *hapd, struct sta_info *sta, int reassoc) { + if (hapd->tkip_countermeasures) { + hostapd_sta_deauth(hapd, sta->addr, + WLAN_REASON_MICHAEL_MIC_FAILURE); + return; + } + /* IEEE 802.11F (IAPP) */ if (hapd->conf->ieee802_11f) iapp_new_station(hapd->iapp, sta); - /* Start accounting here, if IEEE 802.1X is not used. IEEE 802.1X code - * will start accounting after the station has been authorized. */ - if (!hapd->conf->ieee802_1x) + /* Start accounting here, if IEEE 802.1X and WPA are not used. + * IEEE 802.1X/WPA code will start accounting after the station has + * been authorized. */ + if (!hapd->conf->ieee802_1x && !hapd->conf->wpa) accounting_sta_start(hapd, sta); - /* Start IEEE 802.1x authentication process for new stations */ + /* Start IEEE 802.1X authentication process for new stations */ ieee802_1x_new_station(hapd, sta); - wpa_new_station(hapd, sta); + if (reassoc) + wpa_sm_event(hapd, sta, WPA_REAUTH); + else + wpa_new_station(hapd, sta); } @@ -438,7 +473,9 @@ static int hostapd_setup_interface(struct hostapd_data *hapd) return -1; } - hapd->radius = radius_client_init(hapd); + if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MSGDUMPS)) + conf->radius->msg_dumps = 1; + hapd->radius = radius_client_init(hapd, conf->radius); if (hapd->radius == NULL) { printf("RADIUS client initialization failed.\n"); return -1; @@ -451,6 +488,7 @@ static int hostapd_setup_interface(struct hostapd_data *hapd) srv.hostapd_conf = conf; srv.eap_sim_db_priv = hapd->eap_sim_db_priv; srv.ssl_ctx = hapd->ssl_ctx; + srv.ipv6 = conf->radius_server_ipv6; hapd->radius_srv = radius_server_init(&srv); if (hapd->radius_srv == NULL) { printf("RADIUS server initialization failed.\n"); @@ -580,8 +618,8 @@ static void show_version(void) { fprintf(stderr, "hostapd v" VERSION_STR "\n" - "Host AP user space daemon for management functionality of " - "Host AP kernel driver\n" + "User space daemon for IEEE 802.11 AP management,\n" + "IEEE 802.1X/WPA/WPA2/EAP/RADIUS Authenticator\n" "Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> " "and contributors\n"); } @@ -592,7 +630,7 @@ static void usage(void) show_version(); fprintf(stderr, "\n" - "usage: hostapd [-hdB] <configuration file(s)>\n" + "usage: hostapd [-hdBKt] <configuration file(s)>\n" "\n" "options:\n" " -h show this usage\n" @@ -634,9 +672,9 @@ static hostapd * hostapd_init(const char *config_file) } #ifdef EAP_TLS_FUNCS - if (hapd->conf->eap_authenticator && + if (hapd->conf->eap_server && (hapd->conf->ca_cert || hapd->conf->server_cert)) { - hapd->ssl_ctx = tls_init(); + hapd->ssl_ctx = tls_init(NULL); if (hapd->ssl_ctx == NULL) { printf("Failed to initialize TLS\n"); goto fail; @@ -657,6 +695,12 @@ static hostapd * hostapd_init(const char *config_file) hapd->conf->private_key_passwd)) { printf("Failed to load private key (%s)\n", hapd->conf->private_key); + goto fail; + } + if (tls_global_set_verify(hapd->ssl_ctx, + hapd->conf->check_crl)) { + printf("Failed to enable check_crl\n"); + goto fail; } } #endif /* EAP_TLS_FUNCS */ diff --git a/contrib/hostapd/hostapd.conf b/contrib/hostapd/hostapd.conf index bb792dc..ecd7663 100644 --- a/contrib/hostapd/hostapd.conf +++ b/contrib/hostapd/hostapd.conf @@ -2,9 +2,14 @@ # Empty lines and lines starting with # are ignored # AP netdevice name (without 'ap' prefix, i.e., wlan0 uses wlan0ap for -# management frames) +# management frames); ath0 for madwifi interface=wlan0 +# In case of madwifi driver, an additional configuration parameter, bridge, +# must be used to notify hostapd if the interface is included in a bridge. This +# parameter is not used with Host AP driver. +#bridge=br0 + # Driver interface type (hostap/wired/madwifi/prism54; default: hostap) # driver=hostap @@ -40,7 +45,7 @@ debug=0 # Dump file for state information (on SIGUSR1) dump_file=/tmp/hostapd.dump -# Interface for separate control program. If this is specified, wpa_supplicant +# Interface for separate control program. If this is specified, hostapd # will create this directory and a UNIX domain socket for listening to requests # from external programs (CLI/GUI, etc.) for status information and # configuration. The socket file will be named based on the interface name, so @@ -52,11 +57,11 @@ ctrl_interface=/var/run/hostapd # Access control for the control interface can be configured by setting the # directory to allow only members of a group to use sockets. This way, it is -# possible to run wpa_supplicant as root (since it needs to change network +# possible to run hostapd as root (since it needs to change network # configuration and open raw sockets) and still allow GUI/CLI components to be # run as non-root users. However, since the control interface can be used to # change the network configuration, this access needs to be protected in many -# cases. By default, wpa_supplicant is configured to use gid 0 (root). If you +# cases. By default, hostapd is configured to use gid 0 (root). If you # want to allow non-root users to use the contron interface, add a new group # and change this value to match with that group. Add users that should have # control interface access to this group. @@ -96,16 +101,54 @@ auth_algs=3 #assoc_ap_addr=00:12:34:56:78:9a -##### IEEE 802.1X (and IEEE 802.1aa/D4) related configuration ################# +##### IEEE 802.1X-2004 related configuration ################################## # Require IEEE 802.1X authorization #ieee8021x=1 -# Use integrated EAP authenticator instead of external RADIUS authentication -# server -eap_authenticator=0 +# Optional displayable message sent with EAP Request-Identity. The first \0 +# in this string will be converted to ASCII-0 (nul). This can be used to +# separate network info (comma separated list of attribute=value pairs); see, +# e.g., draft-adrangi-eap-network-discovery-07.txt. +#eap_message=hello +#eap_message=hello\0networkid=netw,nasid=foo,portid=0,NAIRealms=example.com + +# WEP rekeying (disabled if key lengths are not set or are set to 0) +# Key lengths for default/broadcast and individual/unicast keys: +# 5 = 40-bit WEP (also known as 64-bit WEP with 40 secret bits) +# 13 = 104-bit WEP (also known as 128-bit WEP with 104 secret bits) +#wep_key_len_broadcast=5 +#wep_key_len_unicast=5 +# Rekeying period in seconds. 0 = do not rekey (i.e., set keys only once) +#wep_rekey_period=300 + +# EAPOL-Key index workaround (set bit7) for WinXP Supplicant (needed only if +# only broadcast keys are used) +eapol_key_index_workaround=0 + +# EAP reauthentication period in seconds (default: 3600 seconds; 0 = disable +# reauthentication). +#eap_reauth_period=3600 + +# Use PAE group address (01:80:c2:00:00:03) instead of individual target +# address when sending EAPOL frames with driver=wired. This is the most common +# mechanism used in wired authentication, but it also requires that the port +# is only used by one station. +#use_pae_group_addr=1 + +##### Integrated EAP server ################################################### + +# Optionally, hostapd can be configured to use an integrated EAP server +# to process EAP authentication locally without need for an external RADIUS +# server. This functionality can be used both as a local authentication server +# for IEEE 802.1X/EAPOL and as a RADIUS server for other devices. -# Path for EAP authenticator user database +# Use integrated EAP server instead of external RADIUS authentication +# server. This is also needed if hostapd is configured to act as a RADIUS +# authentication server. +eap_server=0 + +# Path for EAP server user database #eap_user_file=/etc/hostapd.eap_user # CA certificate (PEM or DER file) for EAP-TLS/PEAP/TTLS @@ -124,31 +167,23 @@ eap_authenticator=0 # Passphrase for private key #private_key_passwd=secret passphrase +# Enable CRL verification. +# Note: hostapd does not yet support CRL downloading based on CDP. Thus, a +# valid CRL signed by the CA is required to be included in the ca_cert file. +# This can be done by using PEM format for CA certificate and CRL and +# concatenating these into one file. Whenever CRL changes, hostapd needs to be +# restarted to take the new CRL into use. +# 0 = do not verify CRLs (default) +# 1 = check the CRL of the user certificate +# 2 = check all CRLs in the certificate path +#check_crl=1 + # Configuration data for EAP-SIM database/authentication gateway interface. # This is a text string in implementation specific format. The example # implementation in eap_sim_db.c uses this as the file name for the GSM # authentication triplets. #eap_sim_db=/etc/hostapd.sim_db -# Optional displayable message sent with EAP Request-Identity -eap_message=hello - -# WEP rekeying (disabled if key lengths are not set or are set to 0) -# Key lengths for default/broadcast and individual/unicast keys: -# 5 = 40-bit WEP (also known as 64-bit WEP with 40 secret bits) -# 13 = 104-bit WEP (also known as 128-bit WEP with 104 secret bits) -#wep_key_len_broadcast=5 -#wep_key_len_unicast=5 -# Rekeying period in seconds. 0 = do not rekey (i.e., set keys only once) -#wep_rekey_period=300 - -# EAPOL-Key index workaround (set bit7) for WinXP Supplicant (needed only if -# only broadcast keys are used) -eapol_key_index_workaround=0 - -# EAP reauthentication period in seconds (default: 3600 seconds; 0 = disable -# reauthentication). -#eap_reauth_period=3600 ##### IEEE 802.11f - Inter-Access Point Protocol (IAPP) ####################### @@ -156,7 +191,7 @@ eapol_key_index_workaround=0 #iapp_interface=eth0 -##### RADIUS configuration #################################################### +##### RADIUS client configuration ############################################# # for IEEE 802.1X with external Authentication Server, IEEE 802.11 # authentication with external ACL for MAC addresses, and accounting @@ -208,6 +243,8 @@ own_ip_addr=127.0.0.1 #radius_acct_interim_interval=600 +##### RADIUS authentication server configuration ############################## + # hostapd can be used as a RADIUS authentication server for other hosts. This # requires that the integrated EAP authenticator is also enabled and both # authentication services are sharing the same configuration. @@ -219,6 +256,9 @@ own_ip_addr=127.0.0.1 # The UDP port number for the RADIUS authentication server #radius_server_auth_port=1812 +# Use IPv6 with RADIUS server (IPv4 will also be supported using IPv6 API) +#radius_server_ipv6=1 + ##### WPA/IEEE 802.11i configuration ########################################## diff --git a/contrib/hostapd/hostapd.eap_user b/contrib/hostapd/hostapd.eap_user index 529334a..fd7b420 100644 --- a/contrib/hostapd/hostapd.eap_user +++ b/contrib/hostapd/hostapd.eap_user @@ -9,7 +9,7 @@ # phase 1 and another with the same username for phase 2. # # EAP-TLS, EAP-PEAP, EAP-TTLS, and EAP-SIM do not use password option. -# EAP-MD5, EAP-MSCHAPV2, and EAP-GTC require a password. +# EAP-MD5, EAP-MSCHAPV2, EAP-GTC, EAP-PAX, and EAP-PSK require a password. # EAP-PEAP and EAP-TTLS require Phase 2 configuration. # # * can be used as a wildcard to match any user identity. The main purposes for @@ -33,6 +33,10 @@ "example user" TLS "DOMAIN\user" MSCHAPV2 "password" "gtc user" GTC "password" +"pax user" PAX "unknown" +"pax.user@example.com" PAX 0123456789abcdef0123456789abcdef +"psk user" PSK "unknown" +"psk.user@example.com" PSK 0123456789abcdef0123456789abcdef "ttls" TTLS "not anonymous" PEAP * PEAP,TTLS,TLS,SIM diff --git a/contrib/hostapd/hostapd.h b/contrib/hostapd/hostapd.h index 9e6da65..fdcb420 100644 --- a/contrib/hostapd/hostapd.h +++ b/contrib/hostapd/hostapd.h @@ -13,8 +13,19 @@ #ifndef ETH_P_ALL #define ETH_P_ALL 0x0003 #endif +#ifndef ETH_P_PAE +#define ETH_P_PAE 0x888E /* Port Access Entity (IEEE 802.1X) */ +#endif /* ETH_P_PAE */ + +#ifndef BIT +#define BIT(x) (1 << (x)) +#endif + +#ifndef MAC2STR +#define MAC2STR(a) (a)[0], (a)[1], (a)[2], (a)[3], (a)[4], (a)[5] +#define MACSTR "%02x:%02x:%02x:%02x:%02x:%02x" +#endif -#include "hostap_common.h" #include "config.h" struct ieee8023_hdr { @@ -118,9 +129,10 @@ struct hostapd_data { struct radius_server_data *radius_srv; }; -void hostapd_new_assoc_sta(hostapd *hapd, struct sta_info *sta); -void hostapd_logger(hostapd *hapd, u8 *addr, unsigned int module, int level, - char *fmt, ...) __attribute__ ((format (printf, 5, 6))); +void hostapd_new_assoc_sta(hostapd *hapd, struct sta_info *sta, int reassoc); +void hostapd_logger(struct hostapd_data *hapd, const u8 *addr, + unsigned int module, int level, const char *fmt, + ...) __attribute__ ((format (printf, 5, 6))); #define HOSTAPD_DEBUG(level, args...) \ @@ -131,4 +143,7 @@ do { \ #define HOSTAPD_DEBUG_COND(level) (hapd->conf->debug >= (level)) +const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf, + size_t buflen); + #endif /* HOSTAPD_H */ diff --git a/contrib/hostapd/hostapd_cli.1 b/contrib/hostapd/hostapd_cli.1 new file mode 100644 index 0000000..062fc78 --- /dev/null +++ b/contrib/hostapd/hostapd_cli.1 @@ -0,0 +1,83 @@ +.TH HOSTAPD_CLI 1 "April 7, 2005" hostapd_cli "hostapd command-line interface" +.SH NAME +hostapd_cli \- hostapd command-line interface +.SH SYNOPSIS +.B hostapd_cli +[-p<path>] [-i<ifname>] [-hv] [command..] +.SH DESCRIPTION +This manual page documents briefly the +.B hostapd_cli +utility. +.PP +.B hostapd_cli +is a command-line interface for the +.B hostapd +daemon. + +.B hostapd +is a user space daemon for access point and authentication servers. +It implements IEEE 802.11 access point management, IEEE 802.1X/WPA/WPA2/EAP Authenticators and RADIUS authentication server. +For more information about +.B hostapd +refer to the +.BR hostapd (8) +man page. +.SH OPTIONS +A summary of options is included below. +For a complete description, run +.BR hostapd_cli +from the command line. +.TP +.B \-p<path> +Path to find control sockets. + +Default: /var/run/hostapd +.TP +.B \-i<ifname> +Interface to listen on. + +Default: first interface found in socket path. +.TP +.B \-h +Show usage. +.TP +.B \-v +Show hostapd_cli version. +.SH COMMANDS +A summary of commands is included below. +For a complete description, run +.BR hostapd_cli +from the command line. +.TP +.B mib +Get MIB variables (dot1x, dot11, radius). +.TP +.B sta <addr> +Get MIB variables for one station. +.TP +.B all_sta +Get MIB variables for all stations. +.TP +.B help +Get usage help. +.TP +.B interface [ifname] +Show interfaces/select interface. +.TP +.B level <debug level> +Change debug level. +.TP +.B license +Show full +.B hostapd_cli +license. +.TP +.B quit +Exit hostapd_cli. +.SH SEE ALSO +.BR hostapd (8). +.SH AUTHOR +hostapd_cli was written by Jouni Malinen <jkmaline@cc.hut.fi>. +.PP +This manual page was written by Faidon Liambotis <faidon@cube.gr>, +for the Debian project (but may be used by others). diff --git a/contrib/hostapd/hostapd_cli.c b/contrib/hostapd/hostapd_cli.c index 5e305dc..d93c41d 100644 --- a/contrib/hostapd/hostapd_cli.c +++ b/contrib/hostapd/hostapd_cli.c @@ -19,7 +19,7 @@ #include <unistd.h> #include <dirent.h> -#include "hostapd_ctrl.h" +#include "wpa_ctrl.h" #include "version.h" @@ -81,17 +81,17 @@ static const char *hostapd_cli_full_license = "\n"; static const char *commands_help = -"commands:\n" -" mib = get MIB variables (dot1x, dot11, radius)\n" -" sta <addr> = get MIB vatiables for one station\n" -" all_sta = get MIB variables for all stations\n" -" help = show this usage help\n" -" interface [ifname] = show interfaces/select interface\n" -" level <debug level> = change debug level\n" -" license = show full hostapd_cli license\n" -" quit = exit hostapd_cli\n"; - -static struct hostapd_ctrl *ctrl_conn; +"Commands:\n" +" mib get MIB variables (dot1x, dot11, radius)\n" +" sta <addr> get MIB vatiables for one station\n" +" all_sta get MIB variables for all stations\n" +" help show this usage help\n" +" interface [ifname] show interfaces/select interface\n" +" level <debug level> change debug level\n" +" license show full hostapd_cli license\n" +" quit exit hostapd_cli\n"; + +static struct wpa_ctrl *ctrl_conn; static int hostapd_cli_quit = 0; static int hostapd_cli_attached = 0; static const char *ctrl_iface_dir = "/var/run/hostapd"; @@ -100,18 +100,26 @@ static char *ctrl_ifname = NULL; static void usage(void) { - printf("hostapd_cli [-p<path to ctrl sockets>] [-i<ifname>] [-hv] " - "[command..]\n" - " -h = help (show this usage text)\n" - " -v = shown version information\n" - " default path: /var/run/hostapd\n" - " default interface: first interface found in socket path\n" - "%s", - commands_help); + fprintf(stderr, "%s\n", hostapd_cli_version); + fprintf(stderr, + "\n" + "usage: hostapd_cli [-p<path>] [-i<ifname>] [-hv] " + "[command..]\n" + "\n" + "Options:\n" + " -h help (show this usage text)\n" + " -v shown version information\n" + " -p<path> path to find control sockets (default: " + "/var/run/hostapd)\n" + " -i<ifname> Interface to listen on (default: first " + "interface found in the\n" + " socket path)\n\n" + "%s", + commands_help); } -static struct hostapd_ctrl * hostapd_cli_open_connection(const char *ifname) +static struct wpa_ctrl * hostapd_cli_open_connection(const char *ifname) { char *cfile; int flen; @@ -125,7 +133,7 @@ static struct hostapd_ctrl * hostapd_cli_open_connection(const char *ifname) return NULL; snprintf(cfile, flen, "%s/%s", ctrl_iface_dir, ifname); - ctrl_conn = hostapd_ctrl_open(cfile); + ctrl_conn = wpa_ctrl_open(cfile); free(cfile); return ctrl_conn; } @@ -137,10 +145,10 @@ static void hostapd_cli_close_connection(void) return; if (hostapd_cli_attached) { - hostapd_ctrl_detach(ctrl_conn); + wpa_ctrl_detach(ctrl_conn); hostapd_cli_attached = 0; } - hostapd_ctrl_close(ctrl_conn); + wpa_ctrl_close(ctrl_conn); ctrl_conn = NULL; } @@ -151,8 +159,7 @@ static void hostapd_cli_msg_cb(char *msg, size_t len) } -static int _hostapd_ctrl_command(struct hostapd_ctrl *ctrl, char *cmd, - int print) +static int _wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd, int print) { char buf[4096]; size_t len; @@ -163,7 +170,7 @@ static int _hostapd_ctrl_command(struct hostapd_ctrl *ctrl, char *cmd, return -1; } len = sizeof(buf) - 1; - ret = hostapd_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len, + ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len, hostapd_cli_msg_cb); if (ret == -2) { printf("'%s' command timed out.\n", cmd); @@ -180,28 +187,25 @@ static int _hostapd_ctrl_command(struct hostapd_ctrl *ctrl, char *cmd, } -static inline int hostapd_ctrl_command(struct hostapd_ctrl *ctrl, char *cmd) +static inline int wpa_ctrl_command(struct wpa_ctrl *ctrl, char *cmd) { - return _hostapd_ctrl_command(ctrl, cmd, 1); + return _wpa_ctrl_command(ctrl, cmd, 1); } -static int hostapd_cli_cmd_ping(struct hostapd_ctrl *ctrl, int argc, - char *argv[]) +static int hostapd_cli_cmd_ping(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - return hostapd_ctrl_command(ctrl, "PING"); + return wpa_ctrl_command(ctrl, "PING"); } -static int hostapd_cli_cmd_mib(struct hostapd_ctrl *ctrl, int argc, - char *argv[]) +static int hostapd_cli_cmd_mib(struct wpa_ctrl *ctrl, int argc, char *argv[]) { - return hostapd_ctrl_command(ctrl, "MIB"); + return wpa_ctrl_command(ctrl, "MIB"); } -static int hostapd_cli_cmd_sta(struct hostapd_ctrl *ctrl, int argc, - char *argv[]) +static int hostapd_cli_cmd_sta(struct wpa_ctrl *ctrl, int argc, char *argv[]) { char buf[64]; if (argc != 1) { @@ -210,12 +214,12 @@ static int hostapd_cli_cmd_sta(struct hostapd_ctrl *ctrl, int argc, return -1; } snprintf(buf, sizeof(buf), "STA %s", argv[0]); - return hostapd_ctrl_command(ctrl, buf); + return wpa_ctrl_command(ctrl, buf); } -static int hostapd_ctrl_command_sta(struct hostapd_ctrl *ctrl, char *cmd, - char *addr, size_t addr_len) +static int wpa_ctrl_command_sta(struct wpa_ctrl *ctrl, char *cmd, + char *addr, size_t addr_len) { char buf[4096], *pos; size_t len; @@ -226,7 +230,7 @@ static int hostapd_ctrl_command_sta(struct hostapd_ctrl *ctrl, char *cmd, return -1; } len = sizeof(buf) - 1; - ret = hostapd_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len, + ret = wpa_ctrl_request(ctrl, cmd, strlen(cmd), buf, &len, hostapd_cli_msg_cb); if (ret == -2) { printf("'%s' command timed out.\n", cmd); @@ -250,30 +254,29 @@ static int hostapd_ctrl_command_sta(struct hostapd_ctrl *ctrl, char *cmd, } -static int hostapd_cli_cmd_all_sta(struct hostapd_ctrl *ctrl, int argc, - char *argv[]) +static int hostapd_cli_cmd_all_sta(struct wpa_ctrl *ctrl, int argc, + char *argv[]) { char addr[32], cmd[64]; - if (hostapd_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr))) + if (wpa_ctrl_command_sta(ctrl, "STA-FIRST", addr, sizeof(addr))) return 0; do { snprintf(cmd, sizeof(cmd), "STA-NEXT %s", addr); - } while (hostapd_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0); + } while (wpa_ctrl_command_sta(ctrl, cmd, addr, sizeof(addr)) == 0); return -1; } -static int hostapd_cli_cmd_help(struct hostapd_ctrl *ctrl, int argc, - char *argv[]) +static int hostapd_cli_cmd_help(struct wpa_ctrl *ctrl, int argc, char *argv[]) { printf("%s", commands_help); return 0; } -static int hostapd_cli_cmd_license(struct hostapd_ctrl *ctrl, int argc, +static int hostapd_cli_cmd_license(struct wpa_ctrl *ctrl, int argc, char *argv[]) { printf("%s\n\n%s\n", hostapd_cli_version, hostapd_cli_full_license); @@ -281,16 +284,14 @@ static int hostapd_cli_cmd_license(struct hostapd_ctrl *ctrl, int argc, } -static int hostapd_cli_cmd_quit(struct hostapd_ctrl *ctrl, int argc, - char *argv[]) +static int hostapd_cli_cmd_quit(struct wpa_ctrl *ctrl, int argc, char *argv[]) { hostapd_cli_quit = 1; return 0; } -static int hostapd_cli_cmd_level(struct hostapd_ctrl *ctrl, int argc, - char *argv[]) +static int hostapd_cli_cmd_level(struct wpa_ctrl *ctrl, int argc, char *argv[]) { char cmd[256]; if (argc != 1) { @@ -299,11 +300,11 @@ static int hostapd_cli_cmd_level(struct hostapd_ctrl *ctrl, int argc, return 0; } snprintf(cmd, sizeof(cmd), "LEVEL %s", argv[0]); - return hostapd_ctrl_command(ctrl, cmd); + return wpa_ctrl_command(ctrl, cmd); } -static void hostapd_cli_list_interfaces(struct hostapd_ctrl *ctrl) +static void hostapd_cli_list_interfaces(struct wpa_ctrl *ctrl) { struct dirent *dent; DIR *dir; @@ -326,7 +327,7 @@ static void hostapd_cli_list_interfaces(struct hostapd_ctrl *ctrl) } -static int hostapd_cli_cmd_interface(struct hostapd_ctrl *ctrl, int argc, +static int hostapd_cli_cmd_interface(struct wpa_ctrl *ctrl, int argc, char *argv[]) { if (argc < 1) { @@ -340,7 +341,7 @@ static int hostapd_cli_cmd_interface(struct hostapd_ctrl *ctrl, int argc, if (hostapd_cli_open_connection(ctrl_ifname)) { printf("Connected to interface '%s.\n", ctrl_ifname); - if (hostapd_ctrl_attach(ctrl_conn) == 0) { + if (wpa_ctrl_attach(ctrl_conn) == 0) { hostapd_cli_attached = 1; } else { printf("Warning: Failed to attach to " @@ -356,7 +357,7 @@ static int hostapd_cli_cmd_interface(struct hostapd_ctrl *ctrl, int argc, struct hostapd_cli_cmd { const char *cmd; - int (*handler)(struct hostapd_ctrl *ctrl, int argc, char *argv[]); + int (*handler)(struct wpa_ctrl *ctrl, int argc, char *argv[]); }; static struct hostapd_cli_cmd hostapd_cli_commands[] = { @@ -373,7 +374,7 @@ static struct hostapd_cli_cmd hostapd_cli_commands[] = { }; -static void wpa_request(struct hostapd_ctrl *ctrl, int argc, char *argv[]) +static void wpa_request(struct wpa_ctrl *ctrl, int argc, char *argv[]) { struct hostapd_cli_cmd *cmd, *match = NULL; int count; @@ -407,15 +408,15 @@ static void wpa_request(struct hostapd_ctrl *ctrl, int argc, char *argv[]) } -static void hostapd_cli_recv_pending(struct hostapd_ctrl *ctrl, int in_read) +static void hostapd_cli_recv_pending(struct wpa_ctrl *ctrl, int in_read) { int first = 1; if (ctrl_conn == NULL) return; - while (hostapd_ctrl_pending(ctrl)) { + while (wpa_ctrl_pending(ctrl)) { char buf[256]; size_t len = sizeof(buf) - 1; - if (hostapd_ctrl_recv(ctrl, buf, &len) == 0) { + if (wpa_ctrl_recv(ctrl, buf, &len) == 0) { buf[len] = '\0'; if (in_read && first) printf("\n"); @@ -484,7 +485,7 @@ static void hostapd_cli_terminate(int sig) static void hostapd_cli_alarm(int sig) { - if (ctrl_conn && _hostapd_ctrl_command(ctrl_conn, "PING", 0)) { + if (ctrl_conn && _wpa_ctrl_command(ctrl_conn, "PING", 0)) { printf("Connection to hostapd lost - trying to reconnect\n"); hostapd_cli_close_connection(); } @@ -492,7 +493,7 @@ static void hostapd_cli_alarm(int sig) ctrl_conn = hostapd_cli_open_connection(ctrl_ifname); if (ctrl_conn) { printf("Connection to hostapd re-established\n"); - if (hostapd_ctrl_attach(ctrl_conn) == 0) { + if (wpa_ctrl_attach(ctrl_conn) == 0) { hostapd_cli_attached = 1; } else { printf("Warning: Failed to attach to " @@ -568,7 +569,7 @@ int main(int argc, char *argv[]) if (!interactive) { perror("Failed to connect to hostapd - " - "hostapd_ctrl_open"); + "wpa_ctrl_open"); return -1; } @@ -585,7 +586,7 @@ int main(int argc, char *argv[]) signal(SIGALRM, hostapd_cli_alarm); if (interactive) { - if (hostapd_ctrl_attach(ctrl_conn) == 0) { + if (wpa_ctrl_attach(ctrl_conn) == 0) { hostapd_cli_attached = 1; } else { printf("Warning: Failed to attach to hostapd.\n"); diff --git a/contrib/hostapd/iapp.c b/contrib/hostapd/iapp.c index 792a471..f4dd557 100644 --- a/contrib/hostapd/iapp.c +++ b/contrib/hostapd/iapp.c @@ -53,6 +53,7 @@ #include "iapp.h" #include "eloop.h" #include "sta_info.h" +#include "hostap_common.h" #define IAPP_MULTICAST "224.0.1.178" diff --git a/contrib/hostapd/ieee802_11.c b/contrib/hostapd/ieee802_11.c index 16a3d0e..d5ad58c 100644 --- a/contrib/hostapd/ieee802_11.c +++ b/contrib/hostapd/ieee802_11.c @@ -18,6 +18,7 @@ #include <string.h> #include <sys/socket.h> #include <net/if.h> +#include <netinet/in.h> #include <arpa/inet.h> #include <time.h> @@ -34,6 +35,7 @@ #include "wpa.h" #include "accounting.h" #include "driver.h" +#include "hostap_common.h" static u8 * hostapd_eid_supp_rates(hostapd *hapd, u8 *eid) @@ -175,7 +177,7 @@ ParseRes ieee802_11_parse_elems(struct hostapd_data *hapd, u8 *start, } -static void ieee802_11_print_ssid(u8 *ssid, u8 len) +static void ieee802_11_print_ssid(const u8 *ssid, u8 len) { int i; for (i = 0; i < len; i++) { @@ -199,7 +201,8 @@ static void ieee802_11_sta_authenticate(void *eloop_ctx, void *timeout_ctx) printf("Authenticate with AP " MACSTR " SSID=", MAC2STR(hapd->conf->assoc_ap_addr)); - ieee802_11_print_ssid(hapd->assoc_ap_ssid, hapd->assoc_ap_ssid_len); + ieee802_11_print_ssid((u8 *) hapd->assoc_ap_ssid, + hapd->assoc_ap_ssid_len); printf(" (as station)\n"); memset(&mgmt, 0, sizeof(mgmt)); @@ -236,7 +239,8 @@ static void ieee802_11_sta_associate(void *eloop_ctx, void *timeout_ctx) printf("Associate with AP " MACSTR " SSID=", MAC2STR(hapd->conf->assoc_ap_addr)); - ieee802_11_print_ssid(hapd->assoc_ap_ssid, hapd->assoc_ap_ssid_len); + ieee802_11_print_ssid((u8 *) hapd->assoc_ap_ssid, + hapd->assoc_ap_ssid_len); printf(" (as station)\n"); memset(mgmt, 0, sizeof(*mgmt)); @@ -475,7 +479,7 @@ static void handle_auth(hostapd *hapd, struct ieee80211_mgmt *mgmt, size_t len) sta->flags &= ~WLAN_STA_PREAUTH; ieee802_1x_notify_pre_auth(sta->eapol_sm, 0); - if (hapd->conf->radius_acct_interim_interval == 0 && + if (hapd->conf->radius->acct_interim_interval == 0 && acct_interim_interval) sta->acct_interim_interval = acct_interim_interval; if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT) @@ -1091,10 +1095,7 @@ static void handle_assoc_cb(hostapd *hapd, struct ieee80211_mgmt *mgmt, } wpa_sm_event(hapd, sta, WPA_ASSOC); - if (new_assoc) - hostapd_new_assoc_sta(hapd, sta); - else - wpa_sm_event(hapd, sta, WPA_REAUTH); + hostapd_new_assoc_sta(hapd, sta, !new_assoc); ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); @@ -1139,6 +1140,7 @@ static void ieee80211_tkip_countermeasures_stop(void *eloop_ctx, { struct hostapd_data *hapd = eloop_ctx; hapd->tkip_countermeasures = 0; + hostapd_set_countermeasures(hapd, 0); hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, HOSTAPD_LEVEL_INFO, "TKIP countermeasures ended"); } @@ -1154,6 +1156,7 @@ static void ieee80211_tkip_countermeasures_start(struct hostapd_data *hapd) if (hapd->wpa_auth) hapd->wpa_auth->dot11RSNATKIPCounterMeasuresInvoked++; hapd->tkip_countermeasures = 1; + hostapd_set_countermeasures(hapd, 1); wpa_gtk_rekey(hapd); eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL); eloop_register_timeout(60, 0, ieee80211_tkip_countermeasures_stop, diff --git a/contrib/hostapd/ieee802_11_auth.c b/contrib/hostapd/ieee802_11_auth.c index 8dbcee1..296c640 100644 --- a/contrib/hostapd/ieee802_11_auth.c +++ b/contrib/hostapd/ieee802_11_auth.c @@ -17,6 +17,7 @@ #include <stdio.h> #include <string.h> #include <time.h> +#include <netinet/in.h> #include <arpa/inet.h> #include <sys/types.h> #include <sys/socket.h> @@ -27,6 +28,7 @@ #include "radius.h" #include "radius_client.h" #include "eloop.h" +#include "hostap_common.h" #define RADIUS_ACL_TIMEOUT 30 @@ -121,18 +123,28 @@ static int hostapd_radius_acl_query(hostapd *hapd, u8 *addr, if (!radius_msg_add_attr_user_password( msg, (u8 *) buf, strlen(buf), - hapd->conf->auth_server->shared_secret, - hapd->conf->auth_server->shared_secret_len)) { + hapd->conf->radius->auth_server->shared_secret, + hapd->conf->radius->auth_server->shared_secret_len)) { printf("Could not add User-Password\n"); goto fail; } - if (!radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, - (u8 *) &hapd->conf->own_ip_addr, 4)) { + if (hapd->conf->own_ip_addr.af == AF_INET && + !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, + (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) { printf("Could not add NAS-IP-Address\n"); goto fail; } +#ifdef CONFIG_IPV6 + if (hapd->conf->own_ip_addr.af == AF_INET6 && + !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS, + (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) { + printf("Could not add NAS-IPv6-Address\n"); + goto fail; + } +#endif /* CONFIG_IPV6 */ + if (hapd->conf->nas_identifier && !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER, (u8 *) hapd->conf->nas_identifier, @@ -221,7 +233,7 @@ int hostapd_allowed_address(hostapd *hapd, u8 *addr, u8 *msg, size_t len, query = query->next; } - if (!hapd->conf->auth_server) + if (!hapd->conf->radius->auth_server) return HOSTAPD_ACL_REJECT; /* No entry in the cache - query external RADIUS server */ @@ -356,8 +368,7 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Found matching Access-Request " "for RADIUS message (id=%d)\n", query->radius_id); - if (radius_msg_verify_acct(msg, shared_secret, shared_secret_len, - req)) { + if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) { printf("Incoming RADIUS packet did not have correct " "authenticator - dropped\n"); return RADIUS_RX_INVALID_AUTHENTICATOR; diff --git a/contrib/hostapd/ieee802_1x.c b/contrib/hostapd/ieee802_1x.c index 7f3fe0c..fa44d82 100644 --- a/contrib/hostapd/ieee802_1x.c +++ b/contrib/hostapd/ieee802_1x.c @@ -133,13 +133,13 @@ void ieee802_1x_request_identity(struct hostapd_data *hapd, { u8 *buf; struct eap_hdr *eap; - int extra, tlen; + int tlen; u8 *pos; struct eapol_state_machine *sm = sta->eapol_sm; - if (hapd->conf->eap_authenticator) { + if (hapd->conf->eap_server) { HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "IEEE 802.1X: Integrated EAP Authenticator in " + "IEEE 802.1X: Integrated EAP server in " "use - do not generate EAP-Request/Identity\n"); return; } @@ -149,12 +149,7 @@ void ieee802_1x_request_identity(struct hostapd_data *hapd, ieee802_1x_new_auth_session(hapd, sta); - if (hapd->conf->eap_req_id_text) - extra = strlen(hapd->conf->eap_req_id_text); - else - extra = 0; - - tlen = sizeof(*eap) + 1 + extra; + tlen = sizeof(*eap) + 1 + hapd->conf->eap_req_id_text_len; buf = malloc(tlen); if (buf == NULL) { @@ -170,8 +165,10 @@ void ieee802_1x_request_identity(struct hostapd_data *hapd, eap->length = htons(tlen); pos = (u8 *) (eap + 1); *pos++ = EAP_TYPE_IDENTITY; - if (hapd->conf->eap_req_id_text) - memcpy(pos, hapd->conf->eap_req_id_text, extra); + if (hapd->conf->eap_req_id_text) { + memcpy(pos, hapd->conf->eap_req_id_text, + hapd->conf->eap_req_id_text_len); + } sm->be_auth.eapReq = TRUE; free(sm->last_eap_radius); @@ -422,12 +419,22 @@ static void ieee802_1x_encapsulate_radius(hostapd *hapd, struct sta_info *sta, goto fail; } - if (!radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, - (u8 *) &hapd->conf->own_ip_addr, 4)) { + if (hapd->conf->own_ip_addr.af == AF_INET && + !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, + (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) { printf("Could not add NAS-IP-Address\n"); goto fail; } +#ifdef CONFIG_IPV6 + if (hapd->conf->own_ip_addr.af == AF_INET6 && + !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IPV6_ADDRESS, + (u8 *) &hapd->conf->own_ip_addr.u.v6, 16)) { + printf("Could not add NAS-IPv6-Address\n"); + goto fail; + } +#endif /* CONFIG_IPV6 */ + if (hapd->conf->nas_identifier && !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER, (u8 *) hapd->conf->nas_identifier, @@ -663,7 +670,8 @@ static void handle_eap(hostapd *hapd, struct sta_info *sta, u8 *buf, /* Process the EAPOL frames from the Supplicant */ -void ieee802_1x_receive(hostapd *hapd, u8 *sa, u8 *buf, size_t len) +void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, + size_t len) { struct sta_info *sta; struct ieee802_1x_hdr *hdr; @@ -798,20 +806,17 @@ void ieee802_1x_new_station(hostapd *hapd, struct sta_info *sta) if (!hapd->conf->ieee802_1x || sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) return; - if (sta->eapol_sm) { - sta->eapol_sm->portEnabled = TRUE; - eapol_sm_step(sta->eapol_sm); - return; - } - - hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_DEBUG, "start authentication"); - sta->eapol_sm = eapol_sm_alloc(hapd, sta); if (sta->eapol_sm == NULL) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, - HOSTAPD_LEVEL_INFO, "failed to allocate " - "state machine"); - return; + HOSTAPD_LEVEL_DEBUG, "start authentication"); + sta->eapol_sm = eapol_sm_alloc(hapd, sta); + if (sta->eapol_sm == NULL) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_INFO, + "failed to allocate state machine"); + return; + } } sta->eapol_sm->portEnabled = TRUE; @@ -827,7 +832,51 @@ void ieee802_1x_new_station(hostapd *hapd, struct sta_info *sta) sta->eapol_sm->auth_pae.state = AUTH_PAE_AUTHENTICATING; sta->eapol_sm->be_auth.state = BE_AUTH_SUCCESS; sta->eapol_sm->authSuccess = TRUE; + if (sta->eapol_sm->eap) + eap_sm_notify_cached(sta->eapol_sm->eap); + } else + eapol_sm_step(sta->eapol_sm); +} + + +void ieee802_1x_free_radius_class(struct radius_class_data *class) +{ + int i; + if (class == NULL) + return; + for (i = 0; i < class->count; i++) + free(class->attr[i].data); + free(class->attr); + class->attr = NULL; + class->count = 0; +} + + +int ieee802_1x_copy_radius_class(struct radius_class_data *dst, + struct radius_class_data *src) +{ + size_t i; + + if (src->attr == NULL) + return 0; + + dst->attr = malloc(src->count * sizeof(struct radius_attr_data)); + if (dst->attr == NULL) + return -1; + + memset(dst->attr, 0, src->count * sizeof(struct radius_attr_data)); + dst->count = 0; + + for (i = 0; i < src->count; i++) { + dst->attr[i].data = malloc(src->attr[i].len); + if (dst->attr[i].data == NULL) + break; + dst->count++; + memcpy(dst->attr[i].data, src->attr[i].data, src->attr[i].len); + dst->attr[i].len = src->attr[i].len; } + + return 0; } @@ -850,7 +899,7 @@ void ieee802_1x_free_station(struct sta_info *sta) free(sm->last_eap_supp); free(sm->last_eap_radius); free(sm->identity); - free(sm->radius_class); + ieee802_1x_free_radius_class(&sm->radius_class); free(sm->eapol_key_sign); free(sm->eapol_key_crypt); eapol_sm_free(sm); @@ -993,31 +1042,87 @@ static void ieee802_1x_store_radius_class(struct hostapd_data *hapd, u8 *class; size_t class_len; struct eapol_state_machine *sm = sta->eapol_sm; + int count, i; + struct radius_attr_data *nclass; + size_t nclass_count; - if (!hapd->conf->acct_server || hapd->radius == NULL || sm == NULL) + if (!hapd->conf->radius->acct_server || hapd->radius == NULL || + sm == NULL) return; - if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CLASS, &class, - &class_len) < 0 || - class_len < 1) { - free(sm->radius_class); - sm->radius_class = NULL; - sm->radius_class_len = 0; + ieee802_1x_free_radius_class(&sm->radius_class); + count = radius_msg_count_attr(msg, RADIUS_ATTR_CLASS, 1); + if (count <= 0) return; - } - if (sm->radius_class == NULL || - sm->radius_class_len < class_len) { - free(sm->radius_class); - sm->radius_class = malloc(class_len); - if (sm->radius_class == NULL) { - sm->radius_class_len = 0; - return; - } + nclass = malloc(count * sizeof(struct radius_attr_data)); + if (nclass == NULL) + return; + + nclass_count = 0; + memset(nclass, 0, count * sizeof(struct radius_attr_data)); + + class = NULL; + for (i = 0; i < count; i++) { + do { + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CLASS, + &class, &class_len, + class) < 0) { + i = count; + break; + } + } while (class_len < 1); + + nclass[nclass_count].data = malloc(class_len); + if (nclass[nclass_count].data == NULL) + break; + + memcpy(nclass[nclass_count].data, class, class_len); + nclass[nclass_count].len = class_len; + nclass_count++; } - memcpy(sm->radius_class, class, class_len); - sm->radius_class_len = class_len; + sm->radius_class.attr = nclass; + sm->radius_class.count = nclass_count; + HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "IEEE 802.1X: Stored %lu RADIUS " + "Class attributes for " MACSTR "\n", + (unsigned long) sm->radius_class.count, + MAC2STR(sta->addr)); +} + + +/* Update sta->identity based on User-Name attribute in Access-Accept */ +static void ieee802_1x_update_sta_identity(struct hostapd_data *hapd, + struct sta_info *sta, + struct radius_msg *msg) +{ + u8 *buf, *identity; + size_t len; + struct eapol_state_machine *sm = sta->eapol_sm; + + if (sm == NULL) + return; + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_USER_NAME, &buf, &len, + NULL) < 0) + return; + + identity = malloc(len + 1); + if (identity == NULL) + return; + + memcpy(identity, buf, len); + identity[len] = '\0'; + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "old identity '%s' updated with " + "User-Name from Access-Accept '%s'", + sm->identity ? (char *) sm->identity : "N/A", + (char *) identity); + + free(sm->identity); + sm->identity = identity; + sm->identity_len = len; } @@ -1062,7 +1167,7 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, { struct hostapd_data *hapd = data; struct sta_info *sta; - u32 session_timeout, termination_action, acct_interim_interval; + u32 session_timeout = 0, termination_action, acct_interim_interval; int session_timeout_set; int eap_timeout; struct eapol_state_machine *sm; @@ -1087,7 +1192,7 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, "Access-Reject without Message-Authenticator " "since it does not include EAP-Message\n"); } else if (radius_msg_verify(msg, shared_secret, shared_secret_len, - req)) { + req, 1)) { printf("Incoming RADIUS packet did not have correct " "Message-Authenticator - dropped\n"); return RADIUS_RX_INVALID_AUTHENTICATOR; @@ -1119,7 +1224,7 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, &termination_action)) termination_action = RADIUS_TERMINATION_ACTION_DEFAULT; - if (hapd->conf->radius_acct_interim_interval == 0 && + if (hapd->conf->radius->acct_interim_interval == 0 && msg->hdr->code == RADIUS_CODE_ACCESS_ACCEPT && radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL, &acct_interim_interval) == 0) { @@ -1148,12 +1253,13 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, override_eapReq = 1; ieee802_1x_get_keys(hapd, sta, msg, req, shared_secret, shared_secret_len); + ieee802_1x_store_radius_class(hapd, sta, msg); + ieee802_1x_update_sta_identity(hapd, sta, msg); if (sm->keyAvailable) { pmksa_cache_add(hapd, sta, sm->eapol_key_crypt, session_timeout_set ? session_timeout : -1); } - ieee802_1x_store_radius_class(hapd, sta, msg); break; case RADIUS_CODE_ACCESS_REJECT: sm->eapFail = TRUE; @@ -1195,7 +1301,7 @@ void ieee802_1x_send_resp_to_server(hostapd *hapd, struct sta_info *sta) if (sm == NULL) return; - if (hapd->conf->eap_authenticator) { + if (hapd->conf->eap_server) { eap_set_eapRespData(sm->eap, sm->last_eap_supp, sm->last_eap_supp_len); } else { @@ -1225,9 +1331,6 @@ void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta) free(sm->last_eap_radius); sm->last_eap_radius = NULL; sm->last_eap_radius_len = 0; - free(sm->radius_class); - sm->radius_class = NULL; - sm->radius_class_len = 0; } @@ -1478,13 +1581,15 @@ u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len) } -u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len) +u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len, + int idx) { - if (sm == NULL || sm->radius_class == NULL) + if (sm == NULL || sm->radius_class.attr == NULL || + idx >= sm->radius_class.count) return NULL; - *len = sm->radius_class_len; - return sm->radius_class; + *len = sm->radius_class.attr[idx].len; + return sm->radius_class.attr[idx].data; } @@ -1504,6 +1609,7 @@ void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm, if (sm == NULL) return; sm->portEnabled = enabled ? TRUE : FALSE; + eapol_sm_step(sm); } @@ -1513,6 +1619,7 @@ void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm, if (sm == NULL) return; sm->portValid = valid ? TRUE : FALSE; + eapol_sm_step(sm); } diff --git a/contrib/hostapd/ieee802_1x.h b/contrib/hostapd/ieee802_1x.h index c52d7bb..0ac06b2 100644 --- a/contrib/hostapd/ieee802_1x.h +++ b/contrib/hostapd/ieee802_1x.h @@ -47,7 +47,8 @@ enum { EAPOL_KEY_TYPE_RC4 = 1, EAPOL_KEY_TYPE_RSN = 2, EAPOL_KEY_TYPE_WPA = 254 }; -void ieee802_1x_receive(hostapd *hapd, u8 *sa, u8 *buf, size_t len); +void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, + size_t len); void ieee802_1x_new_station(hostapd *hapd, struct sta_info *sta); void ieee802_1x_free_station(struct sta_info *sta); @@ -69,7 +70,8 @@ void ieee802_1x_deinit(hostapd *hapd); int ieee802_1x_tx_status(hostapd *hapd, struct sta_info *sta, u8 *buf, size_t len, int ack); u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len); -u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len); +u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len, + int idx); u8 * ieee802_1x_get_key_crypt(struct eapol_state_machine *sm, size_t *len); void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm, int enabled); @@ -83,4 +85,10 @@ void hostapd_get_ntp_timestamp(u8 *buf); void ieee802_1x_finished(struct hostapd_data *hapd, struct sta_info *sta, int success); +struct radius_class_data; + +void ieee802_1x_free_radius_class(struct radius_class_data *class); +int ieee802_1x_copy_radius_class(struct radius_class_data *dst, + struct radius_class_data *src); + #endif /* IEEE802_1X_H */ diff --git a/contrib/hostapd/l2_packet.h b/contrib/hostapd/l2_packet.h index 3e3914c..eb966d3 100644 --- a/contrib/hostapd/l2_packet.h +++ b/contrib/hostapd/l2_packet.h @@ -1,3 +1,23 @@ +/* + * WPA Supplicant - Layer2 packet interface definition + * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * This file defines an interface for layer 2 (link layer) packet sending and + * receiving. l2_packet_linux.c is one implementation for such a layer 2 + * implementation using Linux packet sockets and l2_packet_pcap.c another one + * using libpcap and libdnet. When porting %wpa_supplicant to other operating + * systems, a new l2_packet implementation may need to be added. + */ + #ifndef L2_PACKET_H #define L2_PACKET_H @@ -12,6 +32,14 @@ #define ETH_P_RSN_PREAUTH 0x88c7 #endif +/** + * struct l2_packet_data - Internal l2_packet data structure + * + * This structure is used by the l2_packet implementation to store its private + * data. Other files use a pointer to this data when calling the l2_packet + * functions, but the contents of this structure should not be used directly + * outside l2_packet implementation. + */ struct l2_packet_data; struct l2_ethhdr { @@ -20,15 +48,86 @@ struct l2_ethhdr { u16 h_proto; } __attribute__ ((packed)); +/** + * l2_packet_init - Initialize l2_packet interface + * @ifname: Interface name + * @own_addr: Optional own MAC address if available from driver interface or + * %NULL if not available + * @protocol: Ethernet protocol number in host byte order + * @rx_callback: Callback function that will be called for each received packet + * @rx_callback_ctx: Callback data (ctx) for calls to rx_callback() + * @l2_hdr: 1 = include layer 2 header, 0 = do not include header + * Returns: Pointer to internal data or %NULL on failure + * + * rx_callback function will be called with src_addr pointing to the source + * address (MAC address) of the the packet. If l2_hdr is set to 0, buf + * points to len bytes of the payload after the layer 2 header and similarly, + * TX buffers start with payload. This behavior can be changed by setting + * l2_hdr=1 to include the layer 2 header in the data buffer. + */ struct l2_packet_data * l2_packet_init( const char *ifname, const u8 *own_addr, unsigned short protocol, - void (*rx_callback)(void *ctx, unsigned char *src_addr, - unsigned char *buf, size_t len), - void *rx_callback_ctx); + void (*rx_callback)(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len), + void *rx_callback_ctx, int l2_hdr); + +/** + * l2_packet_deinit - Deinitialize l2_packet interface + * @l2: Pointer to internal l2_packet data from l2_packet_init() + */ void l2_packet_deinit(struct l2_packet_data *l2); +/** + * l2_packet_get_own_addr - Get own layer 2 address + * @l2: Pointer to internal l2_packet data from l2_packet_init() + * @addr: Buffer for the own address (6 bytes) + * Returns: 0 on success, -1 on failure + */ int l2_packet_get_own_addr(struct l2_packet_data *l2, u8 *addr); -int l2_packet_send(struct l2_packet_data *l2, u8 *buf, size_t len); -void l2_packet_set_rx_l2_hdr(struct l2_packet_data *l2, int rx_l2_hdr); + +/** + * l2_packet_send - Send a packet + * @l2: Pointer to internal l2_packet data from l2_packet_init() + * @dst_addr: Destination address for the packet (only used if l2_hdr == 0) + * @proto: Protocol/ethertype for the packet in host byte order (only used if + * l2_hdr == 0) + * @buf: Packet contents to be sent; including layer 2 header if l2_hdr was + * set to 1 in l2_packet_init() call. Otherwise, only the payload of the packet + * is included. + * @len: Length of the buffer (including l2 header only if l2_hdr == 1) + * Returns: >=0 on success, <0 on failure + */ +int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, + const u8 *buf, size_t len); + +/** + * l2_packet_get_ip_addr - Get the current IP address from the interface + * @l2: Pointer to internal l2_packet data from l2_packet_init() + * @buf: Buffer for the IP address in text format + * @len: Maximum buffer length + * Returns: 0 on success, -1 on failure + * + * This function can be used to get the current IP address from the interface + * bound to the l2_packet. This is mainly for status information and the IP + * address will be stored as an ASCII string. This function is not essential + * for %wpa_supplicant operation, so full implementation is not required. + * l2_packet implementation will need to define the function, but it can return + * -1 if the IP address information is not available. + */ +int l2_packet_get_ip_addr(struct l2_packet_data *l2, char *buf, size_t len); + + +/** + * l2_packet_notify_auth_start - Notify l2_packet about start of authentication + * @l2: Pointer to internal l2_packet data from l2_packet_init() + * + * This function is called when authentication is expected to start, e.g., when + * association has been completed, in order to prepare l2_packet implementation + * for EAPOL frames. This function is used mainly if the l2_packet code needs + * to do polling in which case it can increasing polling frequency. This can + * also be an empty function if the l2_packet implementation does not benefit + * from knowing about the starting authentication. + */ +void l2_packet_notify_auth_start(struct l2_packet_data *l2); #endif /* L2_PACKET_H */ diff --git a/contrib/hostapd/logwatch/README b/contrib/hostapd/logwatch/README new file mode 100644 index 0000000..3cba511 --- /dev/null +++ b/contrib/hostapd/logwatch/README @@ -0,0 +1,9 @@ +Logwatch is a utility for analyzing system logs and provide a human +readable summary. This directory has a configuration file and a log +analyzer script for parsing hostapd system log entries for logwatch. +These files can be installed by copying them to following locations: + +/etc/log.d/conf/services/hostapd.conf +/etc/log.d/scripts/services/hostapd + +More information about logwatch is available from http://www.logwatch.org/ diff --git a/contrib/hostapd/logwatch/hostapd b/contrib/hostapd/logwatch/hostapd new file mode 100755 index 0000000..97b24ef --- /dev/null +++ b/contrib/hostapd/logwatch/hostapd @@ -0,0 +1,65 @@ +#!/usr/bin/perl -w +# +# Logwatch script for hostapd +# +# Copyright 2005 Henrik Brix Andersen <brix@gentoo.org> +# Distributed under the terms of the GNU General Public License v2 +# Alternatively, this file may be distributed under the terms of the BSD License + +use strict; + +my $debug = $ENV{'LOGWATCH_DEBUG'} || 0; +my $detail = $ENV{'LOGWATCH_DETAIL_LEVEL'} || 0; +my $debugcounter = 1; + +my %hostapd; +my @unmatched; + +if ($debug >= 5) { + print STDERR "\n\nDEBUG: Inside HOSTAPD Filter\n\n"; +} + +while (defined(my $line = <STDIN>)) { + if ($debug >= 5) { + print STDERR "DEBUG($debugcounter): $line"; + $debugcounter++; + } + chomp($line); + + if (my ($iface,$mac,$layer,$details) = ($line =~ /(.*?): STA (.*?) (.*?): (.*?)$/i)) { + unless ($detail == 10) { + # collapse association events + $details =~ s/^(associated) .*$/$1/i; + } + $hostapd{$iface}->{$mac}->{$layer}->{$details}++; + } else { + push @unmatched, "$line\n"; + } +} + +if (keys %hostapd) { + foreach my $iface (sort keys %hostapd) { + print "Interface $iface:\n"; + foreach my $mac (sort keys %{$hostapd{$iface}}) { + print " Client MAC Address $mac:\n"; + foreach my $layer (sort keys %{$hostapd{$iface}->{$mac}}) { + print " $layer:\n"; + foreach my $details (sort keys %{$hostapd{$iface}->{$mac}->{$layer}}) { + print " $details"; + my $count = $hostapd{$iface}->{$mac}->{$layer}->{$details}; + if ($count > 1) { + print ": " . $count . " Times"; + } + print "\n"; + } + } + } + } +} + +if ($#unmatched >= 0) { + print "\n**Unmatched Entries**\n"; + print @unmatched; +} + +exit(0); diff --git a/contrib/hostapd/logwatch/hostapd.conf b/contrib/hostapd/logwatch/hostapd.conf new file mode 100644 index 0000000..5bebe6a --- /dev/null +++ b/contrib/hostapd/logwatch/hostapd.conf @@ -0,0 +1,10 @@ +# Logwatch configuration for hostapd +# +# Copyright 2005 Henrik Brix Andersen <brix@gentoo.org> +# Distributed under the terms of the GNU General Public License v2 +# Alternatively, this file may be distributed under the terms of the BSD License + +Title = "hostapd" +LogFile = messages +*OnlyService = hostapd +*RemoveHeaders diff --git a/contrib/hostapd/madwifi.conf b/contrib/hostapd/madwifi.conf index f72750e..a9bf539 100644 --- a/contrib/hostapd/madwifi.conf +++ b/contrib/hostapd/madwifi.conf @@ -4,6 +4,10 @@ # AP netdevice name (without 'ap' prefix, i.e., wlan0 uses wlan0ap for # management frames) interface=ath0 + +# In case of madwifi driver, an additional configuration parameter, bridge, +# must be used to notify hostapd if the interface is included in a bridge. This +# parameter is not used with Host AP driver. bridge=br0 # Driver interface type (hostap/wired/madwifi; default: hostap) @@ -21,6 +25,7 @@ driver=madwifi # bit 2 (4) = RADIUS # bit 3 (8) = WPA # bit 4 (16) = driver interface +# bit 5 (32) = IAPP # # Levels (minimum value for logged events): # 0 = verbose debugging @@ -46,21 +51,17 @@ dump_file=/tmp/hostapd.dump # SSID to be used in IEEE 802.11 management frames ssid=wpa-test -##### IEEE 802.1X (and IEEE 802.1aa/D4) related configuration ################# +##### IEEE 802.1X-REV related configuration ################################### # Require IEEE 802.1X authorization #ieee8021x=1 -# Use internal minimal EAP Authentication Server for testing IEEE 802.1X. -# This should only be used for testing since it authorizes all users that -# support IEEE 802.1X without any keys or certificates. Please also note that -# the EAP method used with this minimal server does not generate any keying -# material and as such, it cannot be used with dynamic WEP keying -# (wep_key_len_broadcast and wep_key_len_unicast). -minimal_eap=0 - -# Optional displayable message sent with EAP Request-Identity -eap_message=hello +# Optional displayable message sent with EAP Request-Identity. The first \0 +# in this string will be converted to ASCII-0 (nul). This can be used to +# separate network info (comma separated list of attribute=value pairs); see, +# e.g., draft-adrangi-eap-network-discovery-07.txt. +#eap_message=hello +#eap_message=hello\0networkid=netw,nasid=foo,portid=0,NAIRealms=example.com # WEP rekeying (disabled if key lengths are not set or are set to 0) # Key lengths for default/broadcast and individual/unicast keys: @@ -79,13 +80,63 @@ eapol_key_index_workaround=0 # reauthentication). #eap_reauth_period=3600 + +##### Integrated EAP server ################################################### + +# Optionally, hostapd can be configured to use an integrated EAP server +# to process EAP authentication locally without need for an external RADIUS +# server. This functionality can be used both as a local authentication server +# for IEEE 802.1X/EAPOL and as a RADIUS server for other devices. + +# Use integrated EAP server instead of external RADIUS authentication +# server. This is also needed if hostapd is configured to act as a RADIUS +# authentication server. +eap_server=0 + +# Path for EAP server user database +#eap_user_file=/etc/hostapd.eap_user + +# CA certificate (PEM or DER file) for EAP-TLS/PEAP/TTLS +#ca_cert=/etc/hostapd.ca.pem + +# Server certificate (PEM or DER file) for EAP-TLS/PEAP/TTLS +#server_cert=/etc/hostapd.server.pem + +# Private key matching with the server certificate for EAP-TLS/PEAP/TTLS +# This may point to the same file as server_cert if both certificate and key +# are included in a single file. PKCS#12 (PFX) file (.p12/.pfx) can also be +# used by commenting out server_cert and specifying the PFX file as the +# private_key. +#private_key=/etc/hostapd.server.prv + +# Passphrase for private key +#private_key_passwd=secret passphrase + +# Enable CRL verification. +# Note: hostapd does not yet support CRL downloading based on CDP. Thus, a +# valid CRL signed by the CA is required to be included in the ca_cert file. +# This can be done by using PEM format for CA certificate and CRL and +# concatenating these into one file. Whenever CRL changes, hostapd needs to be +# restarted to take the new CRL into use. +# 0 = do not verify CRLs (default) +# 1 = check the CRL of the user certificate +# 2 = check all CRLs in the certificate path +#check_crl=1 + +# Configuration data for EAP-SIM database/authentication gateway interface. +# This is a text string in implementation specific format. The example +# implementation in eap_sim_db.c uses this as the file name for the GSM +# authentication triplets. +#eap_sim_db=/etc/hostapd.sim_db + + ##### IEEE 802.11f - Inter-Access Point Protocol (IAPP) ####################### # Interface to be used for IAPP broadcast packets #iapp_interface=eth0 -##### RADIUS configuration #################################################### +##### RADIUS client configuration ############################################# # for IEEE 802.1X with external Authentication Server, IEEE 802.11 # authentication with external ACL for MAC addresses, and accounting @@ -137,6 +188,23 @@ own_ip_addr=127.0.0.1 #radius_acct_interim_interval=600 +##### RADIUS authentication server configuration ############################## + +# hostapd can be used as a RADIUS authentication server for other hosts. This +# requires that the integrated EAP authenticator is also enabled and both +# authentication services are sharing the same configuration. + +# File name of the RADIUS clients configuration for the RADIUS server. If this +# commented out, RADIUS server is disabled. +#radius_server_clients=/etc/hostapd.radius_clients + +# The UDP port number for the RADIUS authentication server +#radius_server_auth_port=1812 + +# Use IPv6 with RADIUS server (IPv4 will also be supported using IPv6 API) +#radius_server_ipv6=1 + + ##### WPA/IEEE 802.11i configuration ########################################## # Enable WPA. Setting this variable configures the AP to require WPA (either @@ -148,13 +216,15 @@ own_ip_addr=127.0.0.1 # This field is a bit field that can be used to enable WPA (IEEE 802.11i/D3.0) # and/or WPA2 (full IEEE 802.11i/RSN): # bit0 = WPA -# bit1 = IEEE 802.11i/RSN (WPA2) +# bit1 = IEEE 802.11i/RSN (WPA2) (dot11RSNAEnabled) #wpa=1 # WPA pre-shared keys for WPA-PSK. This can be either entered as a 256-bit # secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase # (8..63 characters) that will be converted to PSK. This conversion uses SSID -# so the PSK changed when ASCII passphrase is used and the SSID is changed. +# so the PSK changes when ASCII passphrase is used and the SSID is changed. +# wpa_psk (dot11RSNAConfigPSKValue) +# wpa_passphrase (dot11RSNAConfigPSKPassPhrase) #wpa_psk=0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef #wpa_passphrase=secret passphrase @@ -166,6 +236,7 @@ own_ip_addr=127.0.0.1 # Set of accepted key management algorithms (WPA-PSK, WPA-EAP, or both). The # entries are separated with a space. +# (dot11RSNAConfigAuthenticationSuitesTable) #wpa_key_mgmt=WPA-PSK WPA-EAP # Set of accepted cipher suites (encryption algorithms) for pairwise keys @@ -176,12 +247,17 @@ own_ip_addr=127.0.0.1 # is automatically selected based on this configuration. If only CCMP is # allowed as the pairwise cipher, group cipher will also be CCMP. Otherwise, # TKIP will be used as the group cipher. +# (dot11RSNAConfigPairwiseCiphersTable) #wpa_pairwise=TKIP CCMP # Time interval for rekeying GTK (broadcast/multicast encryption keys) in -# seconds. +# seconds. (dot11RSNAConfigGroupRekeyTime) #wpa_group_rekey=600 +# Rekey GTK when any STA that possesses the current GTK is leaving the BSS. +# (dot11RSNAConfigGroupRekeyStrict) +#wpa_strict_rekey=1 + # Time interval for rekeying GMK (master key used internally to generate GTKs # (in seconds). #wpa_gmk_rekey=86400 @@ -189,6 +265,7 @@ own_ip_addr=127.0.0.1 # Enable IEEE 802.11i/RSN/WPA2 pre-authentication. This is used to speed up # roaming be pre-authenticating IEEE 802.1X/EAP part of the full RSN # authentication and key handshake before actually associating with a new AP. +# (dot11RSNAPreauthenticationEnabled) #rsn_preauth=1 # # Space separated list of interfaces from which pre-authentication frames are diff --git a/contrib/hostapd/md5.c b/contrib/hostapd/md5.c index 1564e8f..82388e0 100644 --- a/contrib/hostapd/md5.c +++ b/contrib/hostapd/md5.c @@ -1,6 +1,6 @@ /* * MD5 hash implementation and interface functions - * Copyright (c) 2003-2004, Jouni Malinen <jkmaline@cc.hut.fi> + * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -18,36 +18,38 @@ #include "common.h" #include "md5.h" +#include "crypto.h" -void md5_mac(const u8 *key, size_t key_len, const u8 *data, size_t data_len, - u8 *mac) -{ - MD5_CTX context; - MD5Init(&context); - MD5Update(&context, key, key_len); - MD5Update(&context, data, data_len); - MD5Update(&context, key, key_len); - MD5Final(mac, &context); -} - - -/* HMAC code is based on RFC 2104 */ +/** + * hmac_md5_vector - HMAC-MD5 over data vector (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash (16 bytes) + */ void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { - MD5_CTX context; - u8 k_ipad[65]; /* inner padding - key XORd with ipad */ - u8 k_opad[65]; /* outer padding - key XORd with opad */ + u8 k_pad[64]; /* padding - key XORd with ipad/opad */ u8 tk[16]; int i; + const u8 *_addr[6]; + size_t _len[6]; + + if (num_elem > 5) { + /* + * Fixed limit on the number of fragments to avoid having to + * allocate memory (which could fail). + */ + return; + } /* if key is longer than 64 bytes reset it to key = MD5(key) */ if (key_len > 64) { - MD5Init(&context); - MD5Update(&context, key, key_len); - MD5Final(tk, &context); - + md5_vector(1, &key, &key_len, tk); key = tk; key_len = 16; } @@ -61,35 +63,46 @@ void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, * opad is the byte 0x5c repeated 64 times * and text is the data being protected */ - /* start out by storing key in pads */ - memset(k_ipad, 0, sizeof(k_ipad)); - memset(k_opad, 0, sizeof(k_opad)); - memcpy(k_ipad, key, key_len); - memcpy(k_opad, key, key_len); + /* start out by storing key in ipad */ + memset(k_pad, 0, sizeof(k_pad)); + memcpy(k_pad, key, key_len); - /* XOR key with ipad and opad values */ - for (i = 0; i < 64; i++) { - k_ipad[i] ^= 0x36; - k_opad[i] ^= 0x5c; - } + /* XOR key with ipad values */ + for (i = 0; i < 64; i++) + k_pad[i] ^= 0x36; /* perform inner MD5 */ - MD5Init(&context); /* init context for 1st pass */ - MD5Update(&context, k_ipad, 64); /* start with inner pad */ - /* then text of datagram; all fragments */ + _addr[0] = k_pad; + _len[0] = 64; for (i = 0; i < num_elem; i++) { - MD5Update(&context, addr[i], len[i]); + _addr[i + 1] = addr[i]; + _len[i + 1] = len[i]; } - MD5Final(mac, &context); /* finish up 1st pass */ + md5_vector(1 + num_elem, _addr, _len, mac); + + memset(k_pad, 0, sizeof(k_pad)); + memcpy(k_pad, key, key_len); + /* XOR key with opad values */ + for (i = 0; i < 64; i++) + k_pad[i] ^= 0x5c; /* perform outer MD5 */ - MD5Init(&context); /* init context for 2nd pass */ - MD5Update(&context, k_opad, 64); /* start with outer pad */ - MD5Update(&context, mac, 16); /* then results of 1st hash */ - MD5Final(mac, &context); /* finish up 2nd pass */ + _addr[0] = k_pad; + _len[0] = 64; + _addr[1] = mac; + _len[1] = MD5_MAC_LEN; + md5_vector(2, _addr, _len, mac); } +/** + * hmac_md5 - HMAC-MD5 over data buffer (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @data: Pointers to the data area + * @data_len: Length of the data area + * @mac: Buffer for the hash (16 bytes) + */ void hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *mac) { @@ -99,6 +112,40 @@ void hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, #ifndef EAP_TLS_FUNCS +struct MD5Context { + u32 buf[4]; + u32 bits[2]; + u8 in[64]; +}; + +static void MD5Init(struct MD5Context *context); +static void MD5Update(struct MD5Context *context, unsigned char const *buf, + unsigned len); +static void MD5Final(unsigned char digest[16], struct MD5Context *context); +static void MD5Transform(u32 buf[4], u32 const in[16]); + +typedef struct MD5Context MD5_CTX; + + +/** + * md5_vector - MD5 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + */ +void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + MD5_CTX ctx; + int i; + + MD5Init(&ctx); + for (i = 0; i < num_elem; i++) + MD5Update(&ctx, addr[i], len[i]); + MD5Final(mac, &ctx); +} + + /* ===== start - public domain MD5 implementation ===== */ /* * This code implements the MD5 message-digest algorithm. @@ -120,13 +167,10 @@ void hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, #ifndef WORDS_BIGENDIAN #define byteReverse(buf, len) /* Nothing */ #else -void byteReverse(unsigned char *buf, unsigned longs); - -#ifndef ASM_MD5 /* * Note: this code is harmless on little-endian machines. */ -void byteReverse(unsigned char *buf, unsigned longs) +static void byteReverse(unsigned char *buf, unsigned longs) { u32 t; do { @@ -137,13 +181,12 @@ void byteReverse(unsigned char *buf, unsigned longs) } while (--longs); } #endif -#endif /* * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious * initialization constants. */ -void MD5Init(struct MD5Context *ctx) +static void MD5Init(struct MD5Context *ctx) { ctx->buf[0] = 0x67452301; ctx->buf[1] = 0xefcdab89; @@ -158,7 +201,8 @@ void MD5Init(struct MD5Context *ctx) * Update context to reflect the concatenation of another buffer full * of bytes. */ -void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) +static void MD5Update(struct MD5Context *ctx, unsigned char const *buf, + unsigned len) { u32 t; @@ -206,7 +250,7 @@ void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) * Final wrapup - pad to 64-byte boundary with the bit pattern * 1 0* (64-bit count of bits processed, MSB-first) */ -void MD5Final(unsigned char digest[16], struct MD5Context *ctx) +static void MD5Final(unsigned char digest[16], struct MD5Context *ctx) { unsigned count; unsigned char *p; @@ -247,8 +291,6 @@ void MD5Final(unsigned char digest[16], struct MD5Context *ctx) memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ } -#ifndef ASM_MD5 - /* The four core functions - F1 is optimized somewhat */ /* #define F1(x, y, z) (x & y | ~x & z) */ @@ -266,7 +308,7 @@ void MD5Final(unsigned char digest[16], struct MD5Context *ctx) * reflect the addition of 16 longwords of new data. MD5Update blocks * the data and converts bytes into longwords for this routine. */ -void MD5Transform(u32 buf[4], u32 const in[16]) +static void MD5Transform(u32 buf[4], u32 const in[16]) { register u32 a, b, c, d; @@ -348,8 +390,6 @@ void MD5Transform(u32 buf[4], u32 const in[16]) buf[2] += c; buf[3] += d; } - -#endif /* ===== end - public domain MD5 implementation ===== */ #endif /* !EAP_TLS_FUNCS */ diff --git a/contrib/hostapd/md5.h b/contrib/hostapd/md5.h index cc3eb95..a724804 100644 --- a/contrib/hostapd/md5.h +++ b/contrib/hostapd/md5.h @@ -1,40 +1,22 @@ +/* + * MD5 hash implementation and interface functions + * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + #ifndef MD5_H #define MD5_H -#ifdef EAP_TLS_FUNCS - -#include <openssl/md5.h> - -#define MD5Init MD5_Init -#define MD5Update MD5_Update -#define MD5Final MD5_Final -#define MD5Transform MD5_Transform - -#define MD5_MAC_LEN MD5_DIGEST_LENGTH - -#else /* EAP_TLS_FUNCS */ - #define MD5_MAC_LEN 16 -struct MD5Context { - u32 buf[4]; - u32 bits[2]; - u8 in[64]; -}; - -void MD5Init(struct MD5Context *context); -void MD5Update(struct MD5Context *context, unsigned char const *buf, - unsigned len); -void MD5Final(unsigned char digest[16], struct MD5Context *context); -void MD5Transform(u32 buf[4], u32 const in[16]); - -typedef struct MD5Context MD5_CTX; - -#endif /* EAP_TLS_FUNCS */ - - -void md5_mac(const u8 *key, size_t key_len, const u8 *data, size_t data_len, - u8 *mac); void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); void hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, diff --git a/contrib/hostapd/ms_funcs.c b/contrib/hostapd/ms_funcs.c index 590b589..c26cddf 100644 --- a/contrib/hostapd/ms_funcs.c +++ b/contrib/hostapd/ms_funcs.c @@ -20,10 +20,19 @@ #include "sha1.h" #include "ms_funcs.h" #include "crypto.h" +#include "rc4.h" -static void challenge_hash(u8 *peer_challenge, u8 *auth_challenge, - u8 *username, size_t username_len, +/** + * challenge_hash - ChallengeHash() - RFC 2759, Sect. 8.2 + * @peer_challenge: 16-octet PeerChallenge (IN) + * @auth_challenge: 16-octet AuthChallenge (IN) + * @username: 0-to-256-char UserName (IN) + * @username_len: Length of username + * @challenge: 8-octet Challenge (OUT) + */ +static void challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge, + const u8 *username, size_t username_len, u8 *challenge) { u8 hash[SHA1_MAC_LEN]; @@ -42,10 +51,18 @@ static void challenge_hash(u8 *peer_challenge, u8 *auth_challenge, } -void nt_password_hash(u8 *password, size_t password_len, u8 *password_hash) +/** + * nt_password_hash - NtPasswordHash() - RFC 2759, Sect. 8.3 + * @password: 0-to-256-unicode-char Password (IN) + * @password_len: Length of password + * @password_hash: 16-octet PasswordHash (OUT) + */ +void nt_password_hash(const u8 *password, size_t password_len, + u8 *password_hash) { u8 *buf; int i; + size_t len; /* Convert password into unicode */ buf = malloc(password_len * 2); @@ -55,18 +72,32 @@ void nt_password_hash(u8 *password, size_t password_len, u8 *password_hash) for (i = 0; i < password_len; i++) buf[2 * i] = password[i]; - md4(buf, password_len * 2, password_hash); + len = password_len * 2; + md4_vector(1, (const u8 **) &buf, &len, password_hash); free(buf); } -void hash_nt_password_hash(u8 *password_hash, u8 *password_hash_hash) +/** + * hash_nt_password_hash - HashNtPasswordHash() - RFC 2759, Sect. 8.4 + * @password_hash: 16-octet PasswordHash (IN) + * @password_hash_hash: 16-octet PaswordHashHash (OUT) + */ +void hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash) { - md4(password_hash, 16, password_hash_hash); + size_t len = 16; + md4_vector(1, &password_hash, &len, password_hash_hash); } -void challenge_response(u8 *challenge, u8 *password_hash, u8 *response) +/** + * challenge_response - ChallengeResponse() - RFC 2759, Sect. 8.5 + * @challenge: 8-octet Challenge (IN) + * @password_hash: 16-octet PasswordHash (IN) + * @response: 24-octet Response (OUT) + */ +void challenge_response(const u8 *challenge, const u8 *password_hash, + u8 *response) { u8 zpwd[7]; des_encrypt(challenge, password_hash, response); @@ -78,9 +109,19 @@ void challenge_response(u8 *challenge, u8 *password_hash, u8 *response) } -void generate_nt_response(u8 *auth_challenge, u8 *peer_challenge, - u8 *username, size_t username_len, - u8 *password, size_t password_len, +/** + * generate_nt_response - GenerateNTResponse() - RFC 2759, Sect. 8.1 + * @auth_challenge: 16-octet AuthenticatorChallenge (IN) + * @peer_hallenge: 16-octet PeerChallenge (IN) + * @username: 0-to-256-char UserName (IN) + * @username_len: Length of username + * @password: 0-to-256-unicode-char Password (IN) + * @password_len: Length of password + * @response: 24-octet Response (OUT) + */ +void generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, + const u8 *username, size_t username_len, + const u8 *password, size_t password_len, u8 *response) { u8 challenge[8]; @@ -93,11 +134,22 @@ void generate_nt_response(u8 *auth_challenge, u8 *peer_challenge, } -void generate_authenticator_response(u8 *password, size_t password_len, - u8 *peer_challenge, - u8 *auth_challenge, - u8 *username, size_t username_len, - u8 *nt_response, u8 *response) +/** + * generate_authenticator_response - GenerateAuthenticatorResponse() - RFC 2759, Sect. 8.7 + * @password: 0-to-256-unicode-char Password (IN) + * @password_len: Length of password + * @nt_response: 24-octet NT-Response (IN) + * @peer_challenge: 16-octet PeerChallenge (IN) + * @auth_challenge: 16-octet AuthenticatorChallenge (IN) + * @username: 0-to-256-char UserName (IN) + * @username_len: Length of username + * @response: 42-octet AuthenticatorResponse (OUT) + */ +void generate_authenticator_response(const u8 *password, size_t password_len, + const u8 *peer_challenge, + const u8 *auth_challenge, + const u8 *username, size_t username_len, + const u8 *nt_response, u8 *response) { static const u8 magic1[39] = { 0x4D, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76, @@ -137,8 +189,15 @@ void generate_authenticator_response(u8 *password, size_t password_len, } -void nt_challenge_response(u8 *challenge, u8 *password, size_t password_len, - u8 *response) +/** + * nt_challenge_response - NtChallengeResponse() - RFC 2433, Sect. A.5 + * @challenge: 8-octet Challenge (IN) + * @password: 0-to-256-unicode-char Password (IN) + * @password_len: Length of password + * @response: 24-octet Response (OUT) + */ +void nt_challenge_response(const u8 *challenge, const u8 *password, + size_t password_len, u8 *response) { u8 password_hash[16]; nt_password_hash(password, password_len, password_hash); @@ -146,8 +205,12 @@ void nt_challenge_response(u8 *challenge, u8 *password, size_t password_len, } -/* IN: 16-octet password_hash_hash and 24-octet nt_response - * OUT: 16-octet master_key */ +/** + * get_master_key - GetMasterKey() - RFC 3079, Sect. 3.4 + * @password_hash_hash: 16-octet PasswordHashHash (IN) + * @nt_response: 24-octet NTResponse (IN) + * @master_key: 16-octet MasterKey (OUT) + */ void get_master_key(const u8 *password_hash_hash, const u8 *nt_response, u8 *master_key) { @@ -169,6 +232,14 @@ void get_master_key(const u8 *password_hash_hash, const u8 *nt_response, } +/** + * get_asymetric_start_key - GetAsymetricStartKey() - RFC 3079, Sect. 3.4 + * @master_key: 16-octet MasterKey (IN) + * @session_key: 8-to-16 octet SessionKey (OUT) + * @session_key_len: SessionKeyLength (Length of session_key) + * @is_send: IsSend (IN, BOOLEAN) + * @is_server: IsServer (IN, BOOLEAN) + */ void get_asymetric_start_key(const u8 *master_key, u8 *session_key, size_t session_key_len, int is_send, int is_server) @@ -229,7 +300,98 @@ void get_asymetric_start_key(const u8 *master_key, u8 *session_key, } +#define PWBLOCK_LEN 516 + +/** + * encrypt_pw_block_with_password_hash - EncryptPwBlobkWithPasswordHash() - RFC 2759, Sect. 8.10 + * @password: 0-to-256-unicode-char Password (IN) + * @password_len: Length of password + * @password_hash: 16-octet PasswordHash (IN) + * @pw_block: 516-byte PwBlock (OUT) + */ +static void encrypt_pw_block_with_password_hash( + const u8 *password, size_t password_len, + const u8 *password_hash, u8 *pw_block) +{ + size_t i, offset; + u8 *pos; + + if (password_len > 256) + return; + + memset(pw_block, 0, PWBLOCK_LEN); + offset = (256 - password_len) * 2; + for (i = 0; i < password_len; i++) + pw_block[offset + i * 2] = password[i]; + pos = &pw_block[2 * 256]; + WPA_PUT_LE16(pos, password_len * 2); + rc4(pw_block, PWBLOCK_LEN, password_hash, 16); +} + + +/** + * new_password_encrypted_with_old_nt_password_hash - NewPasswordEncryptedWithOldNtPasswordHash() - RFC 2759, Sect. 8.9 + * @new_password: 0-to-256-unicode-char NewPassword (IN) + * @new_password_len: Length of new_password + * @old_password: 0-to-256-unicode-char OldPassword (IN) + * @old_password_len: Length of old_password + * @encrypted_pw_block: 516-octet EncryptedPwBlock (OUT) + */ +void new_password_encrypted_with_old_nt_password_hash( + const u8 *new_password, size_t new_password_len, + const u8 *old_password, size_t old_password_len, + u8 *encrypted_pw_block) +{ + u8 password_hash[16]; + + nt_password_hash(old_password, old_password_len, password_hash); + encrypt_pw_block_with_password_hash(new_password, new_password_len, + password_hash, encrypted_pw_block); +} + + +/** + * nt_password_hash_encrypted_with_block - NtPasswordHashEncryptedWithBlock() - RFC 2759, Sect 8.13 + * @password_hash: 16-octer PasswordHash (IN) + * @block: 16-octet Block (IN) + * @cypher: 16-octer Cypher (OUT) + */ +static void nt_password_hash_encrypted_with_block(const u8 *password_hash, + const u8 *block, + u8 *cypher) +{ + des_encrypt(password_hash, block, cypher); + des_encrypt(password_hash + 8, block + 7, cypher + 8); +} + + +/** + * old_nt_password_hash_encrypted_with_new_nt_password_hash - OldNtPasswordHashEncryptedWithNewNtPasswordHash() - RFC 2759, Sect. 8.12 + * @new_password: 0-to-256-unicode-char NewPassword (IN) + * @new_password_len: Length of new_password + * @old_password: 0-to-256-unicode-char OldPassword (IN) + * @old_password_len: Length of old_password + * @encrypted_password_ash: 16-octet EncryptedPasswordHash (OUT) + */ +void old_nt_password_hash_encrypted_with_new_nt_password_hash( + const u8 *new_password, size_t new_password_len, + const u8 *old_password, size_t old_password_len, + u8 *encrypted_password_hash) +{ + u8 old_password_hash[16], new_password_hash[16]; + + nt_password_hash(old_password, old_password_len, old_password_hash); + nt_password_hash(new_password, new_password_len, new_password_hash); + nt_password_hash_encrypted_with_block(old_password_hash, + new_password_hash, + encrypted_password_hash); +} + + #ifdef TEST_MAIN_MS_FUNCS + +#include "rc4.c" + int main(int argc, char *argv[]) { /* Test vector from RFC2759 example */ diff --git a/contrib/hostapd/ms_funcs.h b/contrib/hostapd/ms_funcs.h index a08ab06..38d1bd6 100644 --- a/contrib/hostapd/ms_funcs.h +++ b/contrib/hostapd/ms_funcs.h @@ -1,25 +1,50 @@ +/* + * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759 + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + #ifndef MS_FUNCS_H #define MS_FUNCS_H -void generate_nt_response(u8 *auth_challenge, u8 *peer_challenge, - u8 *username, size_t username_len, - u8 *password, size_t password_len, +void generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, + const u8 *username, size_t username_len, + const u8 *password, size_t password_len, u8 *response); -void generate_authenticator_response(u8 *password, size_t password_len, - u8 *peer_challenge, - u8 *auth_challenge, - u8 *username, size_t username_len, - u8 *nt_response, u8 *response); -void nt_challenge_response(u8 *challenge, u8 *password, size_t password_len, - u8 *response); +void generate_authenticator_response(const u8 *password, size_t password_len, + const u8 *peer_challenge, + const u8 *auth_challenge, + const u8 *username, size_t username_len, + const u8 *nt_response, u8 *response); +void nt_challenge_response(const u8 *challenge, const u8 *password, + size_t password_len, u8 *response); -void challenge_response(u8 *challenge, u8 *password_hash, u8 *response); -void nt_password_hash(u8 *password, size_t password_len, u8 *password_hash); -void hash_nt_password_hash(u8 *password_hash, u8 *password_hash_hash); +void challenge_response(const u8 *challenge, const u8 *password_hash, + u8 *response); +void nt_password_hash(const u8 *password, size_t password_len, + u8 *password_hash); +void hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash); void get_master_key(const u8 *password_hash_hash, const u8 *nt_response, u8 *master_key); void get_asymetric_start_key(const u8 *master_key, u8 *session_key, size_t session_key_len, int is_send, int is_server); +void new_password_encrypted_with_old_nt_password_hash( + const u8 *new_password, size_t new_password_len, + const u8 *old_password, size_t old_password_len, + u8 *encrypted_pw_block); +void old_nt_password_hash_encrypted_with_new_nt_password_hash( + const u8 *new_password, size_t new_password_len, + const u8 *old_password, size_t old_password_len, + u8 *encrypted_password_hash); + #endif /* MS_FUNCS_H */ diff --git a/contrib/hostapd/radius.c b/contrib/hostapd/radius.c index 5fd323d..ce79a62 100644 --- a/contrib/hostapd/radius.c +++ b/contrib/hostapd/radius.c @@ -16,18 +16,20 @@ #include <stdlib.h> #include <stdio.h> #include <unistd.h> -#include <netinet/in.h> #include <string.h> -#include <sys/ioctl.h> #include <signal.h> #include <sys/time.h> +#ifndef CONFIG_NATIVE_WINDOWS +#include <netinet/in.h> +#include <sys/ioctl.h> #include <sys/socket.h> #include <arpa/inet.h> - +#endif /* CONFIG_NATIVE_WINDOWS */ #include "common.h" #include "radius.h" #include "md5.h" +#include "crypto.h" struct radius_msg *radius_msg_new(u8 code, u8 identifier) @@ -124,8 +126,10 @@ static const char *radius_code_string(u8 code) struct radius_attr_type { u8 type; char *name; - enum { RADIUS_ATTR_UNDIST, RADIUS_ATTR_TEXT, RADIUS_ATTR_IP, - RADIUS_ATTR_HEXDUMP, RADIUS_ATTR_INT32 } data_type; + enum { + RADIUS_ATTR_UNDIST, RADIUS_ATTR_TEXT, RADIUS_ATTR_IP, + RADIUS_ATTR_HEXDUMP, RADIUS_ATTR_INT32, RADIUS_ATTR_IPV6 + } data_type; }; static struct radius_attr_type radius_attrs[] = @@ -179,8 +183,8 @@ static struct radius_attr_type radius_attrs[] = { RADIUS_ATTR_MESSAGE_AUTHENTICATOR, "Message-Authenticator", RADIUS_ATTR_UNDIST }, { RADIUS_ATTR_ACCT_INTERIM_INTERVAL, "Acct-Interim-Interval", - RADIUS_ATTR_INT32 } - + RADIUS_ATTR_INT32 }, + { RADIUS_ATTR_NAS_IPV6_ADDRESS, "NAS-IPv6-Address", RADIUS_ATTR_IPV6 }, }; #define RADIUS_ATTRS (sizeof(radius_attrs) / sizeof(radius_attrs[0])) @@ -231,6 +235,19 @@ static void radius_msg_dump_attr(struct radius_attr_hdr *hdr) printf(" Invalid IP address length %d\n", len); break; +#ifdef CONFIG_IPV6 + case RADIUS_ATTR_IPV6: + if (len == 16) { + char buf[128]; + const char *atxt; + struct in6_addr *addr = (struct in6_addr *) pos; + atxt = inet_ntop(AF_INET6, addr, buf, sizeof(buf)); + printf(" Value: %s\n", atxt ? atxt : "?"); + } else + printf(" Invalid IPv6 address length %d\n", len); + break; +#endif /* CONFIG_IPV6 */ + case RADIUS_ATTR_HEXDUMP: case RADIUS_ATTR_UNDIST: printf(" Value:"); @@ -242,7 +259,8 @@ static void radius_msg_dump_attr(struct radius_attr_hdr *hdr) case RADIUS_ATTR_INT32: if (len == 4) { u32 *val = (u32 *) pos; - printf(" Value: %d\n", ntohl(*val)); + printf(" Value: %u\n", + (unsigned int) ntohl(*val)); } else printf(" Invalid INT32 length %d\n", len); break; @@ -302,7 +320,8 @@ int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, { u8 auth[MD5_MAC_LEN]; struct radius_attr_hdr *attr; - MD5_CTX context; + const u8 *addr[4]; + size_t len[4]; memset(auth, 0, MD5_MAC_LEN); attr = radius_msg_add_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, @@ -318,13 +337,15 @@ int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, (u8 *) (attr + 1)); /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */ - MD5Init(&context); - MD5Update(&context, (u8 *) msg->hdr, 1 + 1 + 2); - MD5Update(&context, req_authenticator, MD5_MAC_LEN); - MD5Update(&context, (u8 *) (msg->hdr + 1), - msg->buf_used - sizeof(*msg->hdr)); - MD5Update(&context, secret, secret_len); - MD5Final(msg->hdr->authenticator, &context); + addr[0] = (u8 *) msg->hdr; + len[0] = 1 + 1 + 2; + addr[1] = req_authenticator; + len[1] = MD5_MAC_LEN; + addr[2] = (u8 *) (msg->hdr + 1); + len[2] = msg->buf_used - sizeof(*msg->hdr); + addr[3] = secret; + len[3] = secret_len; + md5_vector(4, addr, len, msg->hdr->authenticator); if (msg->buf_used > 0xffff) { printf("WARNING: too long RADIUS message (%lu)\n", @@ -338,14 +359,16 @@ int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, void radius_msg_finish_acct(struct radius_msg *msg, u8 *secret, size_t secret_len) { - MD5_CTX context; + const u8 *addr[2]; + size_t len[2]; msg->hdr->length = htons(msg->buf_used); memset(msg->hdr->authenticator, 0, MD5_MAC_LEN); - MD5Init(&context); - MD5Update(&context, msg->buf, msg->buf_used); - MD5Update(&context, secret, secret_len); - MD5Final(msg->hdr->authenticator, &context); + addr[0] = msg->buf; + len[0] = msg->buf_used; + addr[1] = secret; + len[1] = secret_len; + md5_vector(2, addr, len, msg->hdr->authenticator); if (msg->buf_used > 0xffff) { printf("WARNING: too long RADIUS messages (%lu)\n", @@ -378,7 +401,7 @@ static int radius_msg_add_attr_to_array(struct radius_msg *msg, struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type, - u8 *data, size_t data_len) + const u8 *data, size_t data_len) { size_t buf_needed; struct radius_attr_hdr *attr; @@ -493,9 +516,9 @@ struct radius_msg *radius_msg_parse(const u8 *data, size_t len) } -int radius_msg_add_eap(struct radius_msg *msg, u8 *data, size_t data_len) +int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, size_t data_len) { - u8 *pos = data; + const u8 *pos = data; size_t left = data_len; while (left > 0) { @@ -605,10 +628,11 @@ int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret, } -int radius_msg_verify(struct radius_msg *msg, u8 *secret, size_t secret_len, - struct radius_msg *sent_msg) +int radius_msg_verify(struct radius_msg *msg, const u8 *secret, + size_t secret_len, struct radius_msg *sent_msg, int auth) { - MD5_CTX context; + const u8 *addr[4]; + size_t len[4]; u8 hash[MD5_MAC_LEN]; if (sent_msg == NULL) { @@ -616,19 +640,22 @@ int radius_msg_verify(struct radius_msg *msg, u8 *secret, size_t secret_len, return 1; } - if (radius_msg_verify_msg_auth(msg, secret, secret_len, + if (auth && + radius_msg_verify_msg_auth(msg, secret, secret_len, sent_msg->hdr->authenticator)) { return 1; } /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */ - MD5Init(&context); - MD5Update(&context, (u8 *) msg->hdr, 1 + 1 + 2); - MD5Update(&context, sent_msg->hdr->authenticator, MD5_MAC_LEN); - MD5Update(&context, (u8 *) (msg->hdr + 1), - msg->buf_used - sizeof(*msg->hdr)); - MD5Update(&context, secret, secret_len); - MD5Final(hash, &context); + addr[0] = (u8 *) msg->hdr; + len[0] = 1 + 1 + 2; + addr[1] = sent_msg->hdr->authenticator; + len[1] = MD5_MAC_LEN; + addr[2] = (u8 *) (msg->hdr + 1); + len[2] = msg->buf_used - sizeof(*msg->hdr); + addr[3] = secret; + len[3] = secret_len; + md5_vector(4, addr, len, hash); if (memcmp(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) { printf("Response Authenticator invalid!\n"); return 1; @@ -639,29 +666,6 @@ int radius_msg_verify(struct radius_msg *msg, u8 *secret, size_t secret_len, } -int radius_msg_verify_acct(struct radius_msg *msg, u8 *secret, - size_t secret_len, struct radius_msg *sent_msg) -{ - MD5_CTX context; - u8 hash[MD5_MAC_LEN]; - - MD5Init(&context); - MD5Update(&context, msg->buf, 4); - MD5Update(&context, sent_msg->hdr->authenticator, MD5_MAC_LEN); - if (msg->buf_used > sizeof(struct radius_hdr)) - MD5Update(&context, msg->buf + sizeof(struct radius_hdr), - msg->buf_used - sizeof(struct radius_hdr)); - MD5Update(&context, secret, secret_len); - MD5Final(hash, &context); - if (memcmp(hash, msg->hdr->authenticator, MD5_MAC_LEN) != 0) { - printf("Response Authenticator invalid!\n"); - return 1; - } - - return 0; -} - - int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src, u8 type) { @@ -694,16 +698,19 @@ void radius_msg_make_authenticator(struct radius_msg *msg, u8 *data, size_t len) { struct timeval tv; - MD5_CTX context; long int l; + const u8 *addr[3]; + size_t elen[3]; gettimeofday(&tv, NULL); l = random(); - MD5Init(&context); - MD5Update(&context, (u8 *) &tv, sizeof(tv)); - MD5Update(&context, data, len); - MD5Update(&context, (u8 *) &l, sizeof(l)); - MD5Final(msg->hdr->authenticator, &context); + addr[0] = (u8 *) &tv; + elen[0] = sizeof(tv); + addr[1] = data; + elen[1] = len; + addr[2] = (u8 *) &l; + elen[2] = sizeof(l); + md5_vector(3, addr, elen, msg->hdr->authenticator); } @@ -780,8 +787,9 @@ static u8 * decrypt_ms_key(const u8 *key, size_t len, const u8 *pos; size_t left, plen; u8 hash[MD5_MAC_LEN]; - MD5_CTX context; int i, first = 1; + const u8 *addr[3]; + size_t elen[3]; /* key: 16-bit salt followed by encrypted key info */ @@ -804,15 +812,19 @@ static u8 * decrypt_ms_key(const u8 *key, size_t len, /* b(1) = MD5(Secret + Request-Authenticator + Salt) * b(i) = MD5(Secret + c(i - 1)) for i > 1 */ - MD5Init(&context); - MD5Update(&context, secret, secret_len); + addr[0] = secret; + elen[0] = secret_len; if (first) { - MD5Update(&context, req_authenticator, MD5_MAC_LEN); - MD5Update(&context, key, 2); /* Salt */ - first = 0; - } else - MD5Update(&context, pos - MD5_MAC_LEN, MD5_MAC_LEN); - MD5Final(hash, &context); + addr[1] = req_authenticator; + elen[1] = MD5_MAC_LEN; + addr[2] = key; + elen[2] = 2; /* Salt */ + } else { + addr[1] = pos - MD5_MAC_LEN; + elen[1] = MD5_MAC_LEN; + } + md5_vector(first ? 3 : 2, addr, elen, hash); + first = 0; for (i = 0; i < MD5_MAC_LEN; i++) *ppos++ = *pos++ ^ hash[i]; @@ -845,7 +857,8 @@ static void encrypt_ms_key(const u8 *key, size_t key_len, u16 salt, { int i, len, first = 1; u8 hash[MD5_MAC_LEN], saltbuf[2], *pos; - MD5_CTX context; + const u8 *addr[3]; + size_t _len[3]; saltbuf[0] = salt >> 8; saltbuf[1] = salt; @@ -864,16 +877,19 @@ static void encrypt_ms_key(const u8 *key, size_t key_len, u16 salt, while (len > 0) { /* b(1) = MD5(Secret + Request-Authenticator + Salt) * b(i) = MD5(Secret + c(i - 1)) for i > 1 */ - MD5Init(&context); - MD5Update(&context, secret, secret_len); + addr[0] = secret; + _len[0] = secret_len; if (first) { - MD5Update(&context, req_authenticator, MD5_MAC_LEN); - MD5Update(&context, saltbuf, sizeof(saltbuf)); - first = 0; + addr[1] = req_authenticator; + _len[1] = MD5_MAC_LEN; + addr[2] = saltbuf; + _len[2] = sizeof(saltbuf); } else { - MD5Update(&context, pos - MD5_MAC_LEN, MD5_MAC_LEN); + addr[1] = pos - MD5_MAC_LEN; + _len[1] = MD5_MAC_LEN; } - MD5Final(hash, &context); + md5_vector(first ? 3 : 2, addr, _len, hash); + first = 0; for (i = 0; i < MD5_MAC_LEN; i++) *pos++ ^= hash[i]; @@ -1037,8 +1053,9 @@ radius_msg_add_attr_user_password(struct radius_msg *msg, { u8 buf[128]; int padlen, i, pos; - MD5_CTX context; size_t buf_len; + const u8 *addr[2]; + size_t len[2]; u8 hash[16]; if (data_len > 128) @@ -1054,20 +1071,22 @@ radius_msg_add_attr_user_password(struct radius_msg *msg, buf_len += padlen; } - MD5Init(&context); - MD5Update(&context, secret, secret_len); - MD5Update(&context, msg->hdr->authenticator, 16); - MD5Final(hash, &context); + addr[0] = secret; + len[0] = secret_len; + addr[1] = msg->hdr->authenticator; + len[1] = 16; + md5_vector(2, addr, len, hash); for (i = 0; i < 16; i++) buf[i] ^= hash[i]; pos = 16; while (pos < buf_len) { - MD5Init(&context); - MD5Update(&context, secret, secret_len); - MD5Update(&context, &buf[pos - 16], 16); - MD5Final(hash, &context); + addr[0] = secret; + len[0] = secret_len; + addr[1] = &buf[pos - 16]; + len[1] = 16; + md5_vector(2, addr, len, hash); for (i = 0; i < 16; i++) buf[pos + i] ^= hash[i]; @@ -1104,13 +1123,14 @@ int radius_msg_get_attr(struct radius_msg *msg, u8 type, u8 *buf, size_t len) int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf, - size_t *len) + size_t *len, const u8 *start) { int i; struct radius_attr_hdr *attr = NULL; for (i = 0; i < msg->attr_used; i++) { - if (msg->attrs[i]->type == type) { + if (msg->attrs[i]->type == type && + (start == NULL || (u8 *) msg->attrs[i] > start)) { attr = msg->attrs[i]; break; } @@ -1123,3 +1143,18 @@ int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf, *len = attr->length - sizeof(*attr); return 0; } + + +int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len) +{ + int i, count; + + for (count = 0, i = 0; i < msg->attr_used; i++) { + if (msg->attrs[i]->type == type && + msg->attrs[i]->length >= + sizeof(struct radius_attr_hdr) + min_len) + count++; + } + + return count; +} diff --git a/contrib/hostapd/radius.h b/contrib/hostapd/radius.h index 318e0ad..b9f8977 100644 --- a/contrib/hostapd/radius.h +++ b/contrib/hostapd/radius.h @@ -63,7 +63,8 @@ enum { RADIUS_ATTR_USER_NAME = 1, RADIUS_ATTR_CONNECT_INFO = 77, RADIUS_ATTR_EAP_MESSAGE = 79, RADIUS_ATTR_MESSAGE_AUTHENTICATOR = 80, - RADIUS_ATTR_ACCT_INTERIM_INTERVAL = 85 + RADIUS_ATTR_ACCT_INTERIM_INTERVAL = 85, + RADIUS_ATTR_NAS_IPV6_ADDRESS = 95 }; @@ -168,16 +169,16 @@ int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, void radius_msg_finish_acct(struct radius_msg *msg, u8 *secret, size_t secret_len); struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type, - u8 *data, size_t data_len); + const u8 *data, size_t data_len); struct radius_msg *radius_msg_parse(const u8 *data, size_t len); -int radius_msg_add_eap(struct radius_msg *msg, u8 *data, size_t data_len); +int radius_msg_add_eap(struct radius_msg *msg, const u8 *data, + size_t data_len); u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *len); -int radius_msg_verify(struct radius_msg *msg, u8 *secret, size_t secret_len, - struct radius_msg *sent_msg); +int radius_msg_verify(struct radius_msg *msg, const u8 *secret, + size_t secret_len, struct radius_msg *sent_msg, + int auth); int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret, size_t secret_len, const u8 *req_auth); -int radius_msg_verify_acct(struct radius_msg *msg, u8 *secret, - size_t secret_len, struct radius_msg *sent_msg); int radius_msg_copy_attr(struct radius_msg *dst, struct radius_msg *src, u8 type); void radius_msg_make_authenticator(struct radius_msg *msg, @@ -219,6 +220,7 @@ static inline int radius_msg_get_attr_int32(struct radius_msg *msg, u8 type, return 0; } int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf, - size_t *len); + size_t *len, const u8 *start); +int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len); #endif /* RADIUS_H */ diff --git a/contrib/hostapd/radius_client.c b/contrib/hostapd/radius_client.c index dc69ca9..abc28bd 100644 --- a/contrib/hostapd/radius_client.c +++ b/contrib/hostapd/radius_client.c @@ -16,14 +16,16 @@ #include <stdlib.h> #include <stdio.h> #include <unistd.h> -#include <netinet/in.h> #include <string.h> #include <time.h> #include <sys/types.h> #include <sys/time.h> +#include <errno.h> +#ifndef CONFIG_NATIVE_WINDOWS +#include <netinet/in.h> #include <sys/socket.h> #include <arpa/inet.h> -#include <errno.h> +#endif /* CONFIG_NATIVE_WINDOWS */ #include "hostapd.h" #include "radius.h" @@ -74,10 +76,15 @@ struct radius_msg_list { struct radius_client_data { - struct hostapd_data *hapd; + void *ctx; + struct hostapd_radius_servers *conf; int auth_serv_sock; /* socket for authentication RADIUS messages */ int acct_serv_sock; /* socket for accounting RADIUS messages */ + int auth_serv_sock6; + int acct_serv_sock6; + int auth_sock; /* currently used socket */ + int acct_sock; /* currently used socket */ struct radius_rx_handler *auth_handlers; size_t num_auth_handlers; @@ -95,7 +102,7 @@ static int radius_change_server(struct radius_client_data *radius, struct hostapd_radius_server *nserv, struct hostapd_radius_server *oserv, - int sock, int auth); + int sock, int sock6, int auth); static int radius_client_init_acct(struct radius_client_data *radius); static int radius_client_init_auth(struct radius_client_data *radius); @@ -146,11 +153,11 @@ int radius_client_register(struct radius_client_data *radius, static void radius_client_handle_send_error(struct radius_client_data *radius, int s, RadiusType msg_type) { - struct hostapd_data *hapd = radius->hapd; +#ifndef CONFIG_NATIVE_WINDOWS int _errno = errno; perror("send[RADIUS]"); if (_errno == ENOTCONN || _errno == EDESTADDRREQ || _errno == EINVAL) { - hostapd_logger(hapd, NULL, HOSTAPD_MODULE_RADIUS, + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, "Send failed - maybe interface status changed -" " try to connect again"); @@ -161,38 +168,40 @@ static void radius_client_handle_send_error(struct radius_client_data *radius, else radius_client_init_auth(radius); } +#endif /* CONFIG_NATIVE_WINDOWS */ } static int radius_client_retransmit(struct radius_client_data *radius, struct radius_msg_list *entry, time_t now) { - struct hostapd_data *hapd = radius->hapd; + struct hostapd_radius_servers *conf = radius->conf; int s; if (entry->msg_type == RADIUS_ACCT || entry->msg_type == RADIUS_ACCT_INTERIM) { - s = radius->acct_serv_sock; + s = radius->acct_sock; if (entry->attempts == 0) - hapd->conf->acct_server->requests++; + conf->acct_server->requests++; else { - hapd->conf->acct_server->timeouts++; - hapd->conf->acct_server->retransmissions++; + conf->acct_server->timeouts++; + conf->acct_server->retransmissions++; } } else { - s = radius->auth_serv_sock; + s = radius->auth_sock; if (entry->attempts == 0) - hapd->conf->auth_server->requests++; + conf->auth_server->requests++; else { - hapd->conf->auth_server->timeouts++; - hapd->conf->auth_server->retransmissions++; + conf->auth_server->timeouts++; + conf->auth_server->retransmissions++; } } /* retransmit; remove entry if too many attempts */ entry->attempts++; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Resending RADIUS message (id=%d)" - "\n", entry->msg->hdr->identifier); + hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)", + entry->msg->hdr->identifier); gettimeofday(&entry->last_attempt, NULL); if (send(s, entry->msg->buf, entry->msg->buf_used, 0) < 0) @@ -215,10 +224,11 @@ static int radius_client_retransmit(struct radius_client_data *radius, static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) { struct radius_client_data *radius = eloop_ctx; - struct hostapd_data *hapd = radius->hapd; + struct hostapd_radius_servers *conf = radius->conf; time_t now, first; struct radius_msg_list *entry, *prev, *tmp; int auth_failover = 0, acct_failover = 0; + char abuf[50]; entry = radius->msgs; if (!entry) @@ -263,20 +273,21 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) first = now; eloop_register_timeout(first - now, 0, radius_client_timer, radius, NULL); - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Next RADIUS client " - "retransmit in %ld seconds\n", - (long int) (first - now)); - + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, "Next RADIUS client " + "retransmit in %ld seconds", + (long int) (first - now)); } - if (auth_failover && hapd->conf->num_auth_servers > 1) { + if (auth_failover && conf->num_auth_servers > 1) { struct hostapd_radius_server *next, *old; - old = hapd->conf->auth_server; - hostapd_logger(hapd, NULL, HOSTAPD_MODULE_RADIUS, + old = conf->auth_server; + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_NOTICE, "No response from Authentication server " "%s:%d - failover", - inet_ntoa(old->addr), old->port); + hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)), + old->port); for (entry = radius->msgs; entry; entry = entry->next) { if (entry->msg_type == RADIUS_AUTH) @@ -284,22 +295,23 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) } next = old + 1; - if (next > &(hapd->conf->auth_servers - [hapd->conf->num_auth_servers - 1])) - next = hapd->conf->auth_servers; - hapd->conf->auth_server = next; + if (next > &(conf->auth_servers[conf->num_auth_servers - 1])) + next = conf->auth_servers; + conf->auth_server = next; radius_change_server(radius, next, old, - radius->auth_serv_sock, 1); + radius->auth_serv_sock, + radius->auth_serv_sock6, 1); } - if (acct_failover && hapd->conf->num_acct_servers > 1) { + if (acct_failover && conf->num_acct_servers > 1) { struct hostapd_radius_server *next, *old; - old = hapd->conf->acct_server; - hostapd_logger(hapd, NULL, HOSTAPD_MODULE_RADIUS, + old = conf->acct_server; + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_NOTICE, "No response from Accounting server " "%s:%d - failover", - inet_ntoa(old->addr), old->port); + hostapd_ip_txt(&old->addr, abuf, sizeof(abuf)), + old->port); for (entry = radius->msgs; entry; entry = entry->next) { if (entry->msg_type == RADIUS_ACCT || @@ -308,19 +320,18 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx) } next = old + 1; - if (next > &hapd->conf->acct_servers - [hapd->conf->num_acct_servers - 1]) - next = hapd->conf->acct_servers; - hapd->conf->acct_server = next; + if (next > &conf->acct_servers[conf->num_acct_servers - 1]) + next = conf->acct_servers; + conf->acct_server = next; radius_change_server(radius, next, old, - radius->acct_serv_sock, 0); + radius->acct_serv_sock, + radius->acct_serv_sock6, 0); } } static void radius_client_update_timeout(struct radius_client_data *radius) { - struct hostapd_data *hapd = radius->hapd; time_t now, first; struct radius_msg_list *entry; @@ -341,8 +352,9 @@ static void radius_client_update_timeout(struct radius_client_data *radius) first = now; eloop_register_timeout(first - now, 0, radius_client_timer, radius, NULL); - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Next RADIUS client retransmit in" - " %ld seconds\n", (long int) (first - now)); + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, "Next RADIUS client retransmit in" + " %ld seconds\n", (long int) (first - now)); } @@ -405,7 +417,6 @@ static void radius_client_list_add(struct radius_client_data *radius, static void radius_client_list_del(struct radius_client_data *radius, RadiusType msg_type, u8 *addr) { - struct hostapd_data *hapd = radius->hapd; struct radius_msg_list *entry, *prev, *tmp; if (addr == NULL) @@ -422,9 +433,10 @@ static void radius_client_list_del(struct radius_client_data *radius, radius->msgs = entry->next; tmp = entry; entry = entry->next; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "Removing matching RADIUS message for " - MACSTR "\n", MAC2STR(addr)); + hostapd_logger(radius->ctx, addr, + HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, + "Removing matching RADIUS message"); radius_client_msg_free(tmp); radius->num_msgs--; continue; @@ -438,7 +450,7 @@ static void radius_client_list_del(struct radius_client_data *radius, int radius_client_send(struct radius_client_data *radius, struct radius_msg *msg, RadiusType msg_type, u8 *addr) { - struct hostapd_data *hapd = radius->hapd; + struct hostapd_radius_servers *conf = radius->conf; u8 *shared_secret; size_t shared_secret_len; char *name; @@ -450,24 +462,25 @@ int radius_client_send(struct radius_client_data *radius, } if (msg_type == RADIUS_ACCT || msg_type == RADIUS_ACCT_INTERIM) { - shared_secret = hapd->conf->acct_server->shared_secret; - shared_secret_len = hapd->conf->acct_server->shared_secret_len; + shared_secret = conf->acct_server->shared_secret; + shared_secret_len = conf->acct_server->shared_secret_len; radius_msg_finish_acct(msg, shared_secret, shared_secret_len); name = "accounting"; - s = radius->acct_serv_sock; - hapd->conf->acct_server->requests++; + s = radius->acct_sock; + conf->acct_server->requests++; } else { - shared_secret = hapd->conf->auth_server->shared_secret; - shared_secret_len = hapd->conf->auth_server->shared_secret_len; + shared_secret = conf->auth_server->shared_secret; + shared_secret_len = conf->auth_server->shared_secret_len; radius_msg_finish(msg, shared_secret, shared_secret_len); name = "authentication"; - s = radius->auth_serv_sock; - hapd->conf->auth_server->requests++; + s = radius->auth_sock; + conf->auth_server->requests++; } - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "Sending RADIUS message to %s server\n", name); - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MSGDUMPS)) + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, "Sending RADIUS message to %s " + "server", name); + if (conf->msg_dumps) radius_msg_dump(msg); res = send(s, msg->buf, msg->buf_used, 0); @@ -484,7 +497,7 @@ int radius_client_send(struct radius_client_data *radius, static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) { struct radius_client_data *radius = eloop_ctx; - struct hostapd_data *hapd = radius->hapd; + struct hostapd_radius_servers *conf = radius->conf; RadiusType msg_type = (RadiusType) sock_ctx; int len, i, roundtrip; unsigned char buf[3000]; @@ -499,11 +512,11 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) if (msg_type == RADIUS_ACCT) { handlers = radius->acct_handlers; num_handlers = radius->num_acct_handlers; - rconf = hapd->conf->acct_server; + rconf = conf->acct_server; } else { handlers = radius->auth_handlers; num_handlers = radius->num_auth_handlers; - rconf = hapd->conf->auth_server; + rconf = conf->auth_server; } len = recv(sock, buf, sizeof(buf), MSG_DONTWAIT); @@ -511,8 +524,9 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) perror("recv[RADIUS]"); return; } - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "Received %d bytes from RADIUS server\n", len); + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, "Received %d bytes from RADIUS " + "server", len); if (len == sizeof(buf)) { printf("Possibly too long UDP frame for our buffer - " "dropping it\n"); @@ -526,9 +540,9 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) return; } - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "Received RADIUS message\n"); - if (HOSTAPD_DEBUG_COND(HOSTAPD_DEBUG_MSGDUMPS)) + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, "Received RADIUS message"); + if (conf->msg_dumps) radius_msg_dump(msg); switch (msg->hdr->code) { @@ -562,19 +576,22 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) } if (req == NULL) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "No matching RADIUS request found (type=%d " - "id=%d) - dropping packet\n", - msg_type, msg->hdr->identifier); + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, + "No matching RADIUS request found (type=%d " + "id=%d) - dropping packet", + msg_type, msg->hdr->identifier); goto fail; } gettimeofday(&tv, NULL); roundtrip = (tv.tv_sec - req->last_attempt.tv_sec) * 100 + (tv.tv_usec - req->last_attempt.tv_usec) / 10000; - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, "Received RADIUS packet matched " - "with a pending request, round trip time %d.%02d sec\n", - roundtrip / 100, roundtrip % 100); + hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, + "Received RADIUS packet matched with a pending " + "request, round trip time %d.%02d sec", + roundtrip / 100, roundtrip % 100); rconf->round_trip_time = roundtrip; /* Remove ACKed RADIUS packet from retransmit list */ @@ -610,7 +627,7 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) rconf->bad_authenticators++; else rconf->unknown_types++; - hostapd_logger(hapd, req->addr, HOSTAPD_MODULE_RADIUS, + hostapd_logger(radius->ctx, req->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, "No RADIUS RX handler found " "(type=%d code=%d id=%d)%s - dropping packet", msg_type, msg->hdr->code, msg->hdr->identifier, @@ -626,7 +643,6 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) u8 radius_client_get_id(struct radius_client_data *radius) { - struct hostapd_data *hapd = radius->hapd; struct radius_msg_list *entry, *prev, *remove; u8 id = radius->next_radius_identifier++; @@ -636,9 +652,11 @@ u8 radius_client_get_id(struct radius_client_data *radius) prev = NULL; while (entry) { if (entry->msg->hdr->identifier == id) { - HOSTAPD_DEBUG(HOSTAPD_DEBUG_MINIMAL, - "Removing pending RADIUS message, since " - "its id (%d) is reused\n", id); + hostapd_logger(radius->ctx, entry->addr, + HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, + "Removing pending RADIUS message, " + "since its id (%d) is reused", id); if (prev) prev->next = entry->next; else @@ -681,15 +699,23 @@ static int radius_change_server(struct radius_client_data *radius, struct hostapd_radius_server *nserv, struct hostapd_radius_server *oserv, - int sock, int auth) + int sock, int sock6, int auth) { - struct hostapd_data *hapd = radius->hapd; struct sockaddr_in serv; - - hostapd_logger(hapd, NULL, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_INFO, +#ifdef CONFIG_IPV6 + struct sockaddr_in6 serv6; +#endif /* CONFIG_IPV6 */ + struct sockaddr *addr; + socklen_t addrlen; + char abuf[50]; + int sel_sock; + + hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, "%s server %s:%d", auth ? "Authentication" : "Accounting", - inet_ntoa(nserv->addr), nserv->port); + hostapd_ip_txt(&nserv->addr, abuf, sizeof(abuf)), + nserv->port); if (!oserv || nserv->shared_secret_len != oserv->shared_secret_len || memcmp(nserv->shared_secret, oserv->shared_secret, @@ -720,16 +746,42 @@ radius_change_server(struct radius_client_data *radius, } } - memset(&serv, 0, sizeof(serv)); - serv.sin_family = AF_INET; - serv.sin_addr.s_addr = nserv->addr.s_addr; - serv.sin_port = htons(nserv->port); + switch (nserv->addr.af) { + case AF_INET: + memset(&serv, 0, sizeof(serv)); + serv.sin_family = AF_INET; + serv.sin_addr.s_addr = nserv->addr.u.v4.s_addr; + serv.sin_port = htons(nserv->port); + addr = (struct sockaddr *) &serv; + addrlen = sizeof(serv); + sel_sock = sock; + break; +#ifdef CONFIG_IPV6 + case AF_INET6: + memset(&serv6, 0, sizeof(serv6)); + serv6.sin6_family = AF_INET6; + memcpy(&serv6.sin6_addr, &nserv->addr.u.v6, + sizeof(struct in6_addr)); + serv6.sin6_port = htons(nserv->port); + addr = (struct sockaddr *) &serv6; + addrlen = sizeof(serv6); + sel_sock = sock6; + break; +#endif /* CONFIG_IPV6 */ + default: + return -1; + } - if (connect(sock, (struct sockaddr *) &serv, sizeof(serv)) < 0) { + if (connect(sel_sock, addr, addrlen) < 0) { perror("connect[radius]"); return -1; } + if (auth) + radius->auth_sock = sel_sock; + else + radius->acct_sock = sel_sock; + return 0; } @@ -737,28 +789,29 @@ radius_change_server(struct radius_client_data *radius, static void radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx) { struct radius_client_data *radius = eloop_ctx; - struct hostapd_data *hapd = radius->hapd; + struct hostapd_radius_servers *conf = radius->conf; struct hostapd_radius_server *oserv; - if (radius->auth_serv_sock >= 0 && hapd->conf->auth_servers && - hapd->conf->auth_server != hapd->conf->auth_servers) { - oserv = hapd->conf->auth_server; - hapd->conf->auth_server = hapd->conf->auth_servers; - radius_change_server(radius, hapd->conf->auth_server, oserv, - radius->auth_serv_sock, 1); + if (radius->auth_sock >= 0 && conf->auth_servers && + conf->auth_server != conf->auth_servers) { + oserv = conf->auth_server; + conf->auth_server = conf->auth_servers; + radius_change_server(radius, conf->auth_server, oserv, + radius->auth_serv_sock, + radius->auth_serv_sock6, 1); } - if (radius->acct_serv_sock >= 0 && hapd->conf->acct_servers && - hapd->conf->acct_server != hapd->conf->acct_servers) { - oserv = hapd->conf->acct_server; - hapd->conf->acct_server = hapd->conf->acct_servers; - radius_change_server(radius, hapd->conf->acct_server, oserv, - radius->acct_serv_sock, 0); + if (radius->acct_sock >= 0 && conf->acct_servers && + conf->acct_server != conf->acct_servers) { + oserv = conf->acct_server; + conf->acct_server = conf->acct_servers; + radius_change_server(radius, conf->acct_server, oserv, + radius->acct_serv_sock, + radius->acct_serv_sock6, 0); } - if (hapd->conf->radius_retry_primary_interval) - eloop_register_timeout(hapd->conf-> - radius_retry_primary_interval, 0, + if (conf->retry_primary_interval) + eloop_register_timeout(conf->retry_primary_interval, 0, radius_retry_primary_timer, radius, NULL); } @@ -766,23 +819,49 @@ static void radius_retry_primary_timer(void *eloop_ctx, void *timeout_ctx) static int radius_client_init_auth(struct radius_client_data *radius) { - struct hostapd_data *hapd = radius->hapd; + struct hostapd_radius_servers *conf = radius->conf; + int ok = 0; + radius->auth_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); - if (radius->auth_serv_sock < 0) { + if (radius->auth_serv_sock < 0) perror("socket[PF_INET,SOCK_DGRAM]"); + else + ok++; + +#ifdef CONFIG_IPV6 + radius->auth_serv_sock6 = socket(PF_INET6, SOCK_DGRAM, 0); + if (radius->auth_serv_sock6 < 0) + perror("socket[PF_INET6,SOCK_DGRAM]"); + else + ok++; +#endif /* CONFIG_IPV6 */ + + if (ok == 0) return -1; - } - radius_change_server(radius, hapd->conf->auth_server, NULL, - radius->auth_serv_sock, 1); + radius_change_server(radius, conf->auth_server, NULL, + radius->auth_serv_sock, radius->auth_serv_sock6, + 1); + + if (radius->auth_serv_sock >= 0 && + eloop_register_read_sock(radius->auth_serv_sock, + radius_client_receive, radius, + (void *) RADIUS_AUTH)) { + printf("Could not register read socket for authentication " + "server\n"); + return -1; + } - if (eloop_register_read_sock(radius->auth_serv_sock, +#ifdef CONFIG_IPV6 + if (radius->auth_serv_sock6 >= 0 && + eloop_register_read_sock(radius->auth_serv_sock6, radius_client_receive, radius, (void *) RADIUS_AUTH)) { printf("Could not register read socket for authentication " "server\n"); return -1; } +#endif /* CONFIG_IPV6 */ return 0; } @@ -790,29 +869,45 @@ static int radius_client_init_auth(struct radius_client_data *radius) static int radius_client_init_acct(struct radius_client_data *radius) { - struct hostapd_data *hapd = radius->hapd; + struct hostapd_radius_servers *conf = radius->conf; + int ok = 0; + radius->acct_serv_sock = socket(PF_INET, SOCK_DGRAM, 0); - if (radius->acct_serv_sock < 0) { + if (radius->acct_serv_sock < 0) perror("socket[PF_INET,SOCK_DGRAM]"); + else + ok++; + + radius_change_server(radius, conf->acct_server, NULL, + radius->acct_serv_sock, radius->acct_serv_sock6, + 0); + + if (radius->acct_serv_sock >= 0 && + eloop_register_read_sock(radius->acct_serv_sock, + radius_client_receive, radius, + (void *) RADIUS_ACCT)) { + printf("Could not register read socket for accounting " + "server\n"); return -1; } - radius_change_server(radius, hapd->conf->acct_server, NULL, - radius->acct_serv_sock, 0); - - if (eloop_register_read_sock(radius->acct_serv_sock, +#ifdef CONFIG_IPV6 + if (radius->acct_serv_sock6 >= 0 && + eloop_register_read_sock(radius->acct_serv_sock6, radius_client_receive, radius, (void *) RADIUS_ACCT)) { printf("Could not register read socket for accounting " "server\n"); return -1; } +#endif /* CONFIG_IPV6 */ return 0; } -struct radius_client_data * radius_client_init(struct hostapd_data *hapd) +struct radius_client_data * +radius_client_init(void *ctx, struct hostapd_radius_servers *conf) { struct radius_client_data *radius; @@ -821,22 +916,24 @@ struct radius_client_data * radius_client_init(struct hostapd_data *hapd) return NULL; memset(radius, 0, sizeof(struct radius_client_data)); - radius->hapd = hapd; - radius->auth_serv_sock = radius->acct_serv_sock = -1; + radius->ctx = ctx; + radius->conf = conf; + radius->auth_serv_sock = radius->acct_serv_sock = + radius->auth_serv_sock6 = radius->acct_serv_sock6 = + radius->auth_sock = radius->acct_sock = -1; - if (hapd->conf->auth_server && radius_client_init_auth(radius)) { + if (conf->auth_server && radius_client_init_auth(radius)) { radius_client_deinit(radius); return NULL; } - if (hapd->conf->acct_server && radius_client_init_acct(radius)) { + if (conf->acct_server && radius_client_init_acct(radius)) { radius_client_deinit(radius); return NULL; } - if (hapd->conf->radius_retry_primary_interval) - eloop_register_timeout(hapd->conf-> - radius_retry_primary_interval, 0, + if (conf->retry_primary_interval) + eloop_register_timeout(conf->retry_primary_interval, 0, radius_retry_primary_timer, radius, NULL); @@ -860,7 +957,6 @@ void radius_client_deinit(struct radius_client_data *radius) void radius_client_flush_auth(struct radius_client_data *radius, u8 *addr) { - struct hostapd_data *hapd = radius->hapd; struct radius_msg_list *entry, *prev, *tmp; prev = NULL; @@ -868,7 +964,8 @@ void radius_client_flush_auth(struct radius_client_data *radius, u8 *addr) while (entry) { if (entry->msg_type == RADIUS_AUTH && memcmp(entry->addr, addr, ETH_ALEN) == 0) { - hostapd_logger(hapd, addr, HOSTAPD_MODULE_RADIUS, + hostapd_logger(radius->ctx, addr, + HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, "Removing pending RADIUS authentication" " message for removed client"); @@ -897,6 +994,7 @@ static int radius_client_dump_auth_server(char *buf, size_t buflen, { int pending = 0; struct radius_msg_list *msg; + char abuf[50]; if (cli) { for (msg = cli->msgs; msg; msg = msg->next) { @@ -922,7 +1020,7 @@ static int radius_client_dump_auth_server(char *buf, size_t buflen, "radiusAuthClientUnknownTypes=%u\n" "radiusAuthClientPacketsDropped=%u\n", serv->index, - inet_ntoa(serv->addr), + hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)), serv->port, serv->round_trip_time, serv->requests, @@ -945,6 +1043,7 @@ static int radius_client_dump_acct_server(char *buf, size_t buflen, { int pending = 0; struct radius_msg_list *msg; + char abuf[50]; if (cli) { for (msg = cli->msgs; msg; msg = msg->next) { @@ -969,7 +1068,7 @@ static int radius_client_dump_acct_server(char *buf, size_t buflen, "radiusAccClientUnknownTypes=%u\n" "radiusAccClientPacketsDropped=%u\n", serv->index, - inet_ntoa(serv->addr), + hostapd_ip_txt(&serv->addr, abuf, sizeof(abuf)), serv->port, serv->round_trip_time, serv->requests, @@ -987,27 +1086,27 @@ static int radius_client_dump_acct_server(char *buf, size_t buflen, int radius_client_get_mib(struct radius_client_data *radius, char *buf, size_t buflen) { - struct hostapd_data *hapd = radius->hapd; + struct hostapd_radius_servers *conf = radius->conf; int i; struct hostapd_radius_server *serv; int count = 0; - if (hapd->conf->auth_servers) { - for (i = 0; i < hapd->conf->num_auth_servers; i++) { - serv = &hapd->conf->auth_servers[i]; + if (conf->auth_servers) { + for (i = 0; i < conf->num_auth_servers; i++) { + serv = &conf->auth_servers[i]; count += radius_client_dump_auth_server( buf + count, buflen - count, serv, - serv == hapd->conf->auth_server ? + serv == conf->auth_server ? radius : NULL); } } - if (hapd->conf->acct_servers) { - for (i = 0; i < hapd->conf->num_acct_servers; i++) { - serv = &hapd->conf->acct_servers[i]; + if (conf->acct_servers) { + for (i = 0; i < conf->num_acct_servers; i++) { + serv = &conf->acct_servers[i]; count += radius_client_dump_acct_server( buf + count, buflen - count, serv, - serv == hapd->conf->acct_server ? + serv == conf->acct_server ? radius : NULL); } } diff --git a/contrib/hostapd/radius_client.h b/contrib/hostapd/radius_client.h index cff201a..d21ca83 100644 --- a/contrib/hostapd/radius_client.h +++ b/contrib/hostapd/radius_client.h @@ -1,6 +1,51 @@ #ifndef RADIUS_CLIENT_H #define RADIUS_CLIENT_H +#include "config_types.h" + +struct radius_msg; + +struct hostapd_radius_server { + /* MIB prefix for shared variables: + * @ = radiusAuth or radiusAcc depending on the type of the server */ + struct hostapd_ip_addr addr; /* @ServerAddress */ + int port; /* @ClientServerPortNumber */ + u8 *shared_secret; + size_t shared_secret_len; + + /* Dynamic (not from configuration file) MIB data */ + int index; /* @ServerIndex */ + int round_trip_time; /* @ClientRoundTripTime; in hundredths of a + * second */ + u32 requests; /* @Client{Access,}Requests */ + u32 retransmissions; /* @Client{Access,}Retransmissions */ + u32 access_accepts; /* radiusAuthClientAccessAccepts */ + u32 access_rejects; /* radiusAuthClientAccessRejects */ + u32 access_challenges; /* radiusAuthClientAccessChallenges */ + u32 responses; /* radiusAccClientResponses */ + u32 malformed_responses; /* @ClientMalformed{Access,}Responses */ + u32 bad_authenticators; /* @ClientBadAuthenticators */ + u32 timeouts; /* @ClientTimeouts */ + u32 unknown_types; /* @ClientUnknownTypes */ + u32 packets_dropped; /* @ClientPacketsDropped */ + /* @ClientPendingRequests: length of hapd->radius->msgs for matching + * msg_type */ +}; + +struct hostapd_radius_servers { + /* RADIUS Authentication and Accounting servers in priority order */ + struct hostapd_radius_server *auth_servers, *auth_server; + int num_auth_servers; + struct hostapd_radius_server *acct_servers, *acct_server; + int num_acct_servers; + + int retry_primary_interval; + int acct_interim_interval; + + int msg_dumps; +}; + + typedef enum { RADIUS_AUTH, RADIUS_ACCT, @@ -32,7 +77,8 @@ int radius_client_send(struct radius_client_data *radius, u8 radius_client_get_id(struct radius_client_data *radius); void radius_client_flush(struct radius_client_data *radius); -struct radius_client_data * radius_client_init(struct hostapd_data *hapd); +struct radius_client_data * +radius_client_init(void *ctx, struct hostapd_radius_servers *conf); void radius_client_deinit(struct radius_client_data *radius); void radius_client_flush_auth(struct radius_client_data *radius, u8 *addr); int radius_client_get_mib(struct radius_client_data *radius, char *buf, diff --git a/contrib/hostapd/radius_server.c b/contrib/hostapd/radius_server.c index d62648f..bda8bd0 100644 --- a/contrib/hostapd/radius_server.c +++ b/contrib/hostapd/radius_server.c @@ -53,6 +53,10 @@ struct radius_client { struct radius_client *next; struct in_addr addr; struct in_addr mask; +#ifdef CONFIG_IPV6 + struct in6_addr addr6; + struct in6_addr mask6; +#endif /* CONFIG_IPV6 */ char *shared_secret; int shared_secret_len; struct radius_session *sessions; @@ -67,6 +71,7 @@ struct radius_server_data { int num_sess; void *eap_sim_db_priv; void *ssl_ctx; + int ipv6; }; @@ -87,12 +92,33 @@ static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx); static struct radius_client * -radius_server_get_client(struct radius_server_data *data, struct in_addr *addr) +radius_server_get_client(struct radius_server_data *data, struct in_addr *addr, + int ipv6) { struct radius_client *client = data->clients; while (client) { - if ((client->addr.s_addr & client->mask.s_addr) == +#ifdef CONFIG_IPV6 + if (ipv6) { + struct in6_addr *addr6; + int i; + + addr6 = (struct in6_addr *) addr; + for (i = 0; i < 16; i++) { + if ((addr6->s6_addr[i] & + client->mask6.s6_addr[i]) != + (client->addr6.s6_addr[i] & + client->mask6.s6_addr[i])) { + i = 17; + break; + } + } + if (i == 16) { + break; + } + } +#endif /* CONFIG_IPV6 */ + if (!ipv6 && (client->addr.s_addr & client->mask.s_addr) == (addr->s_addr & client->mask.s_addr)) { break; } @@ -321,14 +347,15 @@ radius_server_encapsulate_eap(struct radius_server_data *data, static int radius_server_reject(struct radius_server_data *data, struct radius_client *client, struct radius_msg *request, - struct sockaddr_in *from) + struct sockaddr *from, socklen_t fromlen, + const char *from_addr, int from_port) { struct radius_msg *msg; int ret = 0; struct eap_hdr eapfail; RADIUS_DEBUG("Reject invalid request from %s:%d", - inet_ntoa(from->sin_addr), ntohs(from->sin_port)); + from_addr, from_port); msg = radius_msg_new(RADIUS_CODE_ACCESS_REJECT, request->hdr->identifier); @@ -371,8 +398,9 @@ static int radius_server_reject(struct radius_server_data *data, static int radius_server_request(struct radius_server_data *data, struct radius_msg *msg, - struct sockaddr_in *from, - struct radius_client *client) + struct sockaddr *from, socklen_t fromlen, + struct radius_client *client, + const char *from_addr, int from_port) { u8 *eap = NULL; size_t eap_len; @@ -400,13 +428,15 @@ static int radius_server_request(struct radius_server_data *data, RADIUS_DEBUG("Request for session 0x%x", sess->sess_id); } else if (state_included) { RADIUS_DEBUG("State attribute included but no session found"); - radius_server_reject(data, client, msg, from); + radius_server_reject(data, client, msg, from, fromlen, + from_addr, from_port); return -1; } else { sess = radius_server_get_new_session(data, client, msg); if (sess == NULL) { RADIUS_DEBUG("Could not create a new session"); - radius_server_reject(data, client, msg, from); + radius_server_reject(data, client, msg, from, fromlen, + from_addr, from_port); return -1; } } @@ -414,7 +444,7 @@ static int radius_server_request(struct radius_server_data *data, eap = radius_msg_get_eap(msg, &eap_len); if (eap == NULL) { RADIUS_DEBUG("No EAP-Message in RADIUS packet from %s", - inet_ntoa(from->sin_addr)); + from_addr); return -1; } @@ -426,6 +456,13 @@ static int radius_server_request(struct radius_server_data *data, resp_id = 0; } + /* FIX: if Code is Request, Success, or Failure, send Access-Reject; + * RFC3579 Sect. 2.6.2. + * Include EAP-Response/Nak with no preferred method if + * code == request. + * If code is not 1-4, discard the packet silently. + * Or is this already done by the EAP state machine? */ + eap_set_eapRespData(sess->eap, eap, eap_len); free(eap); eap = NULL; @@ -460,14 +497,13 @@ static int radius_server_request(struct radius_server_data *data, sess->eapReqDataLen = 0; if (reply) { - RADIUS_DEBUG("Reply to %s:%d", inet_ntoa(from->sin_addr), - ntohs(from->sin_port)); + RADIUS_DEBUG("Reply to %s:%d", from_addr, from_port); if (wpa_debug_level <= MSG_MSGDUMP) { radius_msg_dump(reply); } res = sendto(data->auth_sock, reply->buf, reply->buf_used, 0, - (struct sockaddr *) from, sizeof(*from)); + (struct sockaddr *) from, fromlen); if (res < 0) { perror("sendto[RADIUS SRV]"); } @@ -489,11 +525,13 @@ static void radius_server_receive_auth(int sock, void *eloop_ctx, { struct radius_server_data *data = eloop_ctx; u8 *buf = NULL; - struct sockaddr_in from; + struct sockaddr_storage from; socklen_t fromlen; int len; - struct radius_client *client; + struct radius_client *client = NULL; struct radius_msg *msg = NULL; + char abuf[50]; + int from_port = 0; buf = malloc(RADIUS_MAX_MSG_LEN); if (buf == NULL) { @@ -508,14 +546,36 @@ static void radius_server_receive_auth(int sock, void *eloop_ctx, goto fail; } - RADIUS_DEBUG("Received %d bytes from %s:%d", - len, inet_ntoa(from.sin_addr), ntohs(from.sin_port)); +#ifdef CONFIG_IPV6 + if (data->ipv6) { + struct sockaddr_in6 *from6 = (struct sockaddr_in6 *) &from; + if (inet_ntop(AF_INET6, &from6->sin6_addr, abuf, sizeof(abuf)) + == NULL) + abuf[0] = '\0'; + from_port = ntohs(from6->sin6_port); + RADIUS_DEBUG("Received %d bytes from %s:%d", + len, abuf, from_port); + + client = radius_server_get_client(data, + (struct in_addr *) + &from6->sin6_addr, 1); + } +#endif /* CONFIG_IPV6 */ + + if (!data->ipv6) { + struct sockaddr_in *from4 = (struct sockaddr_in *) &from; + snprintf(abuf, sizeof(abuf), "%s", inet_ntoa(from4->sin_addr)); + from_port = ntohs(from4->sin_port); + RADIUS_DEBUG("Received %d bytes from %s:%d", + len, abuf, from_port); + + client = radius_server_get_client(data, &from4->sin_addr, 0); + } + RADIUS_DUMP("Received data", buf, len); - client = radius_server_get_client(data, &from.sin_addr); if (client == NULL) { - RADIUS_DEBUG("Unknown client %s - packet ignored", - inet_ntoa(from.sin_addr)); + RADIUS_DEBUG("Unknown client %s - packet ignored", abuf); goto fail; } @@ -539,12 +599,12 @@ static void radius_server_receive_auth(int sock, void *eloop_ctx, if (radius_msg_verify_msg_auth(msg, (u8 *) client->shared_secret, client->shared_secret_len, NULL)) { - RADIUS_DEBUG("Invalid Message-Authenticator from %s", - inet_ntoa(from.sin_addr)); + RADIUS_DEBUG("Invalid Message-Authenticator from %s", abuf); goto fail; } - radius_server_request(data, msg, &from, client); + radius_server_request(data, msg, (struct sockaddr *) &from, fromlen, + client, abuf, from_port); fail: if (msg) { @@ -579,6 +639,33 @@ static int radius_server_open_socket(int port) } +#ifdef CONFIG_IPV6 +static int radius_server_open_socket6(int port) +{ + int s; + struct sockaddr_in6 addr; + + s = socket(PF_INET6, SOCK_DGRAM, 0); + if (s < 0) { + perror("socket[IPv6]"); + return -1; + } + + memset(&addr, 0, sizeof(addr)); + addr.sin6_family = AF_INET6; + memcpy(&addr.sin6_addr, &in6addr_any, sizeof(in6addr_any)); + addr.sin6_port = htons(port); + if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind"); + close(s); + return -1; + } + + return s; +} +#endif /* CONFIG_IPV6 */ + + static void radius_server_free_sessions(struct radius_server_data *data, struct radius_session *sessions) { @@ -611,7 +698,7 @@ static void radius_server_free_clients(struct radius_server_data *data, static struct radius_client * -radius_server_read_clients(const char *client_file) +radius_server_read_clients(const char *client_file, int ipv6) { FILE *f; const int buf_size = 1024; @@ -619,6 +706,9 @@ radius_server_read_clients(const char *client_file) struct radius_client *clients, *tail, *entry; int line = 0, mask, failed = 0, i; struct in_addr addr; +#ifdef CONFIG_IPV6 + struct in6_addr addr6; +#endif /* CONFIG_IPV6 */ unsigned int val; f = fopen(client_file, "r"); @@ -638,6 +728,7 @@ radius_server_read_clients(const char *client_file) /* Configuration file format: * 192.168.1.0/24 secret * 192.168.1.2 secret + * fe80::211:22ff:fe33:4455/64 secretipv6 */ line++; buf[buf_size - 1] = '\0'; @@ -650,7 +741,9 @@ radius_server_read_clients(const char *client_file) continue; pos = buf; - while ((*pos >= '0' && *pos <= '9') || *pos == '.') { + while ((*pos >= '0' && *pos <= '9') || *pos == '.' || + (*pos >= 'a' && *pos <= 'f') || *pos == ':' || + (*pos >= 'A' && *pos <= 'F')) { pos++; } @@ -663,20 +756,36 @@ radius_server_read_clients(const char *client_file) char *end; *pos++ = '\0'; mask = strtol(pos, &end, 10); - if ((pos == end) || (mask < 0 || mask > 32)) { + if ((pos == end) || + (mask < 0 || mask > (ipv6 ? 128 : 32))) { failed = 1; break; } pos = end; } else { - mask = 32; + mask = ipv6 ? 128 : 32; *pos++ = '\0'; } - if (inet_aton(buf, &addr) == 0) { + if (!ipv6 && inet_aton(buf, &addr) == 0) { failed = 1; break; } +#ifdef CONFIG_IPV6 + if (ipv6 && inet_pton(AF_INET6, buf, &addr6) <= 0) { + if (inet_pton(AF_INET, buf, &addr) <= 0) { + failed = 1; + break; + } + /* Convert IPv4 address to IPv6 */ + if (mask <= 32) + mask += (128 - 32); + memset(addr6.s6_addr, 0, 10); + addr6.s6_addr[10] = 0xff; + addr6.s6_addr[11] = 0xff; + memcpy(addr6.s6_addr + 12, (char *) &addr.s_addr, 4); + } +#endif /* CONFIG_IPV6 */ while (*pos == ' ' || *pos == '\t') { pos++; @@ -701,10 +810,25 @@ radius_server_read_clients(const char *client_file) } entry->shared_secret_len = strlen(entry->shared_secret); entry->addr.s_addr = addr.s_addr; - val = 0; - for (i = 0; i < mask; i++) - val |= 1 << (31 - i); - entry->mask.s_addr = htonl(val); + if (!ipv6) { + val = 0; + for (i = 0; i < mask; i++) + val |= 1 << (31 - i); + entry->mask.s_addr = htonl(val); + } +#ifdef CONFIG_IPV6 + if (ipv6) { + int offset = mask / 8; + + memcpy(entry->addr6.s6_addr, addr6.s6_addr, 16); + memset(entry->mask6.s6_addr, 0xff, offset); + val = 0; + for (i = 0; i < (mask % 8); i++) + val |= 1 << (7 - i); + if (offset < 16) + entry->mask6.s6_addr[offset] = val; + } +#endif /* CONFIG_IPV6 */ if (tail == NULL) { clients = tail = entry; @@ -732,6 +856,14 @@ radius_server_init(struct radius_server_conf *conf) { struct radius_server_data *data; +#ifndef CONFIG_IPV6 + if (conf->ipv6) { + fprintf(stderr, "RADIUS server compiled without IPv6 " + "support.\n"); + return NULL; + } +#endif /* CONFIG_IPV6 */ + data = malloc(sizeof(*data)); if (data == NULL) { return NULL; @@ -740,14 +872,21 @@ radius_server_init(struct radius_server_conf *conf) data->hostapd_conf = conf->hostapd_conf; data->eap_sim_db_priv = conf->eap_sim_db_priv; data->ssl_ctx = conf->ssl_ctx; + data->ipv6 = conf->ipv6; - data->clients = radius_server_read_clients(conf->client_file); + data->clients = radius_server_read_clients(conf->client_file, + conf->ipv6); if (data->clients == NULL) { printf("No RADIUS clients configured.\n"); radius_server_deinit(data); return NULL; } +#ifdef CONFIG_IPV6 + if (conf->ipv6) + data->auth_sock = radius_server_open_socket6(conf->auth_port); + else +#endif /* CONFIG_IPV6 */ data->auth_sock = radius_server_open_socket(conf->auth_port); if (data->auth_sock < 0) { printf("Failed to open UDP socket for RADIUS authentication " diff --git a/contrib/hostapd/radius_server.h b/contrib/hostapd/radius_server.h index a725f0a..5c4c39c 100644 --- a/contrib/hostapd/radius_server.h +++ b/contrib/hostapd/radius_server.h @@ -9,6 +9,7 @@ struct radius_server_conf { void *hostapd_conf; void *eap_sim_db_priv; void *ssl_ctx; + int ipv6; }; diff --git a/contrib/hostapd/rc4.c b/contrib/hostapd/rc4.c index 97ec1b0..4cf14d9 100644 --- a/contrib/hostapd/rc4.c +++ b/contrib/hostapd/rc4.c @@ -1,7 +1,6 @@ /* - * Host AP (software wireless LAN access point) user space daemon for - * Host AP kernel driver / RC4 - * Copyright (c) 2002-2004, Jouni Malinen <jkmaline@cc.hut.fi> + * RC4 stream cipher + * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -19,7 +18,20 @@ #define S_SWAP(a,b) do { u8 t = S[a]; S[a] = S[b]; S[b] = t; } while(0) -void rc4_skip(u8 *key, size_t keylen, size_t skip, u8 *data, size_t data_len) +/** + * rc4 - XOR RC4 stream to given data with skip-stream-start + * @key: RC4 key + * @keylen: RC4 key length + * @skip: number of bytes to skip from the beginning of the RC4 stream + * @data: data to be XOR'ed with RC4 stream + * @data_len: buf length + * + * Generate RC4 pseudo random stream for the given key, skip beginning of the + * stream, and XOR the end result with the data buffer to perform RC4 + * encryption/decryption. + */ +void rc4_skip(const u8 *key, size_t keylen, size_t skip, + u8 *data, size_t data_len) { u32 i, j, k; u8 S[256], *pos; @@ -57,7 +69,17 @@ void rc4_skip(u8 *key, size_t keylen, size_t skip, u8 *data, size_t data_len) } -void rc4(u8 *buf, size_t len, u8 *key, size_t key_len) +/** + * rc4 - XOR RC4 stream to given data + * @buf: data to be XOR'ed with RC4 stream + * @len: buf length + * @key: RC4 key + * @key_len: RC4 key length + * + * Generate RC4 pseudo random stream for the given key and XOR this with the + * data buffer to perform RC4 encryption/decryption. + */ +void rc4(u8 *buf, size_t len, const u8 *key, size_t key_len) { rc4_skip(key, key_len, 0, buf, len); } diff --git a/contrib/hostapd/rc4.h b/contrib/hostapd/rc4.h index 0e77b7e..3873240 100644 --- a/contrib/hostapd/rc4.h +++ b/contrib/hostapd/rc4.h @@ -1,7 +1,22 @@ +/* + * RC4 stream cipher + * Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + #ifndef RC4_H #define RC4_H -void rc4_skip(u8 *key, size_t keylen, size_t skip, u8 *data, size_t data_len); -void rc4(u8 *buf, size_t len, u8 *key, size_t key_len); +void rc4_skip(const u8 *key, size_t keylen, size_t skip, + u8 *data, size_t data_len); +void rc4(u8 *buf, size_t len, const u8 *key, size_t key_len); #endif /* RC4_H */ diff --git a/contrib/hostapd/sha1.c b/contrib/hostapd/sha1.c index 04943b5..7e32e31 100644 --- a/contrib/hostapd/sha1.c +++ b/contrib/hostapd/sha1.c @@ -19,36 +19,38 @@ #include "common.h" #include "sha1.h" #include "md5.h" +#include "crypto.h" -void sha1_mac(const u8 *key, size_t key_len, const u8 *data, size_t data_len, - u8 *mac) -{ - SHA1_CTX context; - SHA1Init(&context); - SHA1Update(&context, key, key_len); - SHA1Update(&context, data, data_len); - SHA1Update(&context, key, key_len); - SHA1Final(mac, &context); -} - - -/* HMAC code is based on RFC 2104 */ +/** + * hmac_sha1_vector - HMAC-SHA1 over data vector (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash (20 bytes) + */ void hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { - SHA1_CTX context; - unsigned char k_ipad[65]; /* inner padding - key XORd with ipad */ - unsigned char k_opad[65]; /* outer padding - key XORd with opad */ + unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */ unsigned char tk[20]; int i; + const u8 *_addr[6]; + size_t _len[6]; + + if (num_elem > 5) { + /* + * Fixed limit on the number of fragments to avoid having to + * allocate memory (which could fail). + */ + return; + } /* if key is longer than 64 bytes reset it to key = SHA1(key) */ if (key_len > 64) { - SHA1Init(&context); - SHA1Update(&context, key, key_len); - SHA1Final(tk, &context); - + sha1_vector(1, &key, &key_len, tk); key = tk; key_len = 20; } @@ -62,35 +64,45 @@ void hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, * opad is the byte 0x5c repeated 64 times * and text is the data being protected */ - /* start out by storing key in pads */ - memset(k_ipad, 0, sizeof(k_ipad)); - memset(k_opad, 0, sizeof(k_opad)); - memcpy(k_ipad, key, key_len); - memcpy(k_opad, key, key_len); - - /* XOR key with ipad and opad values */ - for (i = 0; i < 64; i++) { - k_ipad[i] ^= 0x36; - k_opad[i] ^= 0x5c; - } + /* start out by storing key in ipad */ + memset(k_pad, 0, sizeof(k_pad)); + memcpy(k_pad, key, key_len); + /* XOR key with ipad values */ + for (i = 0; i < 64; i++) + k_pad[i] ^= 0x36; /* perform inner SHA1 */ - SHA1Init(&context); /* init context for 1st pass */ - SHA1Update(&context, k_ipad, 64); /* start with inner pad */ - /* then text of datagram; all fragments */ + _addr[0] = k_pad; + _len[0] = 64; for (i = 0; i < num_elem; i++) { - SHA1Update(&context, addr[i], len[i]); + _addr[i + 1] = addr[i]; + _len[i + 1] = len[i]; } - SHA1Final(mac, &context); /* finish up 1st pass */ + sha1_vector(1 + num_elem, _addr, _len, mac); + + memset(k_pad, 0, sizeof(k_pad)); + memcpy(k_pad, key, key_len); + /* XOR key with opad values */ + for (i = 0; i < 64; i++) + k_pad[i] ^= 0x5c; /* perform outer SHA1 */ - SHA1Init(&context); /* init context for 2nd pass */ - SHA1Update(&context, k_opad, 64); /* start with outer pad */ - SHA1Update(&context, mac, 20); /* then results of 1st hash */ - SHA1Final(mac, &context); /* finish up 2nd pass */ + _addr[0] = k_pad; + _len[0] = 64; + _addr[1] = mac; + _len[1] = SHA1_MAC_LEN; + sha1_vector(2, _addr, _len, mac); } +/** + * hmac_sha1 - HMAC-SHA1 over data buffer (RFC 2104) + * @key: Key for HMAC operations + * @key_len: Length of the key in bytes + * @data: Pointers to the data area + * @data_len: Length of the data area + * @mac: Buffer for the hash (20 bytes) + */ void hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *mac) { @@ -98,6 +110,19 @@ void hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, } +/** + * sha1_prf - SHA1-based Pseudo-Random Function (PRF) (IEEE 802.11i, 8.5.1.1) + * @key: Key for PRF + * @key_len: Length of the key in bytes + * @label: A unique label for each purpose of the PRF + * @data: Extra data to bind into the key + * @data_len: Length of the data + * @buf: Buffer for the generated pseudo-random key + * @buf_len: Number of bytes of key to generate + * + * This function is used to derive new, cryptographically separate keys from a + * given key (e.g., PMK in IEEE 802.11i). + */ void sha1_prf(const u8 *key, size_t key_len, const char *label, const u8 *data, size_t data_len, u8 *buf, size_t buf_len) { @@ -135,7 +160,20 @@ void sha1_prf(const u8 *key, size_t key_len, const char *label, } -/* draft-cam-winget-eap-fast-00.txt */ +/** + * sha1_t_prf - EAP-FAST Pseudo-Random Function (T-PRF) + * @key: Key for PRF + * @key_len: Length of the key in bytes + * @label: A unique label for each purpose of the PRF + * @seed: Seed value to bind into the key + * @seed_len: Length of the seed + * @buf: Buffer for the generated pseudo-random key + * @buf_len: Number of bytes of key to generate + * + * This function is used to derive new, cryptographically separate keys from a + * given key for EAP-FAST. T-PRF is defined in + * draft-cam-winget-eap-fast-02.txt, Appendix B. + */ void sha1_t_prf(const u8 *key, size_t key_len, const char *label, const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len) { @@ -177,7 +215,19 @@ void sha1_t_prf(const u8 *key, size_t key_len, const char *label, } -/* RFC 2246 */ +/** + * tls_prf - Pseudo-Random Function for TLS (TLS-PRF, RFC 2246) + * @secret: Key for PRF + * @secret_len: Length of the key in bytes + * @label: A unique label for each purpose of the PRF + * @seed: Seed value to bind into the key + * @seed_len: Length of the seed + * @out: Buffer for the generated pseudo-random key + * @outlen: Number of bytes of key to generate +* + * This function is used to derive new, cryptographically separate keys from a + * given key in TLS. This PRF is defined in RFC 2246, Chapter 5. + */ int tls_prf(const u8 *secret, size_t secret_len, const char *label, const u8 *seed, size_t seed_len, u8 *out, size_t outlen) { @@ -285,6 +335,19 @@ static void pbkdf2_sha1_f(const char *passphrase, const char *ssid, } +/** + * pbkdf2_sha1 - SHA1-based key derivation function (PBKDF2) for IEEE 802.11i + * @passphrase: ASCII passphrase + * @ssid: SSID + * @ssid_len: SSID length in bytes + * @interations: Number of iterations to run + * @buf: Buffer for the generated key + * @buflen: Length of the buffer in bytes + * + * This function is used to derive PSK for WPA-PSK. For this protocol, + * iterations is set to 4096 and buflen to 32. This function is described in + * IEEE Std 802.11-2004, Clause H.4. The main construction is from PKCS#5 v2.0. + */ void pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len, int iterations, u8 *buf, size_t buflen) { @@ -305,20 +368,27 @@ void pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len, } -void sha1_transform(u8 *state, u8 data[64]) -{ -#ifdef EAP_TLS_FUNCS - SHA_CTX context; - memset(&context, 0, sizeof(context)); - memcpy(&context.h0, state, 5 * 4); - SHA1_Transform(&context, data); - memcpy(state, &context.h0, 5 * 4); -#else /* EAP_TLS_FUNCS */ - SHA1Transform((u32 *) state, data); -#endif /* EAP_TLS_FUNCS */ -} +#ifndef EAP_TLS_FUNCS + +typedef struct { + u32 state[5]; + u32 count[2]; + unsigned char buffer[64]; +} SHA1_CTX; + +static void SHA1Init(SHA1_CTX *context); +static void SHA1Update(SHA1_CTX *context, const void *data, u32 len); +static void SHA1Final(unsigned char digest[20], SHA1_CTX* context); +static void SHA1Transform(u32 state[5], const unsigned char buffer[64]); +/** + * sha1_vector - SHA-1 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + */ void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { @@ -332,7 +402,21 @@ void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, } -#ifndef EAP_TLS_FUNCS +/** + * sha1_transform - Perform one SHA-1 transform step + * @state: SHA-1 state + * @data: Input data for the SHA-1 transform + * + * This function is used to implement random number generation specified in + * NIST FIPS Publication 186-2 for EAP-SIM. This PRF uses a function that is + * similar to SHA-1, but has different message padding and as such, access to + * just part of the SHA-1 is needed. + */ +void sha1_transform(u8 *state, const u8 data[64]) +{ + SHA1Transform((u32 *) state, data); +} + /* ===== start - public domain SHA1 implementation ===== */ @@ -462,7 +546,7 @@ void SHAPrintContext(SHA1_CTX *context, char *msg) /* Hash a single 512-bit block. This is the core of the algorithm. */ -void SHA1Transform(u32 state[5], const unsigned char buffer[64]) +static void SHA1Transform(u32 state[5], const unsigned char buffer[64]) { u32 a, b, c, d, e; typedef union { @@ -520,7 +604,7 @@ void SHA1Transform(u32 state[5], const unsigned char buffer[64]) /* SHA1Init - Initialize new context */ -void SHA1Init(SHA1_CTX* context) +static void SHA1Init(SHA1_CTX* context) { /* SHA1 initialization constants */ context->state[0] = 0x67452301; @@ -534,7 +618,7 @@ void SHA1Init(SHA1_CTX* context) /* Run your data through this. */ -void SHA1Update(SHA1_CTX* context, const void *_data, u32 len) +static void SHA1Update(SHA1_CTX* context, const void *_data, u32 len) { u32 i, j; const unsigned char *data = _data; @@ -564,7 +648,7 @@ void SHA1Update(SHA1_CTX* context, const void *_data, u32 len) /* Add padding and return the message digest. */ -void SHA1Final(unsigned char digest[20], SHA1_CTX* context) +static void SHA1Final(unsigned char digest[20], SHA1_CTX* context) { u32 i; unsigned char finalcount[8]; diff --git a/contrib/hostapd/sha1.h b/contrib/hostapd/sha1.h index 186e3c1..3c6d915 100644 --- a/contrib/hostapd/sha1.h +++ b/contrib/hostapd/sha1.h @@ -1,36 +1,22 @@ +/* + * SHA1 hash implementation and interface functions + * Copyright (c) 2003-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + #ifndef SHA1_H #define SHA1_H -#ifdef EAP_TLS_FUNCS - -#include <openssl/sha.h> - -#define SHA1_CTX SHA_CTX -#define SHA1Init SHA1_Init -#define SHA1Update SHA1_Update -#define SHA1Final SHA1_Final -#define SHA1Transform SHA1_Transform -#define SHA1_MAC_LEN SHA_DIGEST_LENGTH - -#else /* EAP_TLS_FUNCS */ - #define SHA1_MAC_LEN 20 -typedef struct { - u32 state[5]; - u32 count[2]; - unsigned char buffer[64]; -} SHA1_CTX; - -void SHA1Init(SHA1_CTX *context); -void SHA1Update(SHA1_CTX *context, const void *data, u32 len); -void SHA1Final(unsigned char digest[20], SHA1_CTX* context); -void SHA1Transform(u32 state[5], const unsigned char buffer[64]); - -#endif /* EAP_TLS_FUNCS */ - -void sha1_mac(const u8 *key, size_t key_len, const u8 *data, size_t data_len, - u8 *mac); void hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); void hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, @@ -43,8 +29,5 @@ int tls_prf(const u8 *secret, size_t secret_len, const char *label, const u8 *seed, size_t seed_len, u8 *out, size_t outlen); void pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len, int iterations, u8 *buf, size_t buflen); -void sha1_transform(u8 *state, u8 data[64]); -void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, - u8 *mac); #endif /* SHA1_H */ diff --git a/contrib/hostapd/sta_info.c b/contrib/hostapd/sta_info.c index 9a4daa3..9eb0bca 100644 --- a/contrib/hostapd/sta_info.c +++ b/contrib/hostapd/sta_info.c @@ -31,6 +31,7 @@ #include "wpa.h" #include "radius_client.h" #include "driver.h" +#include "hostap_common.h" int ap_for_each_sta(struct hostapd_data *hapd, @@ -49,7 +50,7 @@ int ap_for_each_sta(struct hostapd_data *hapd, } -struct sta_info* ap_get_sta(hostapd *hapd, u8 *sta) +struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta) { struct sta_info *s; @@ -317,7 +318,7 @@ void ap_sta_no_session_timeout(hostapd *hapd, struct sta_info *sta) } -struct sta_info * ap_sta_add(struct hostapd_data *hapd, u8 *addr) +struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr) { struct sta_info *sta; @@ -339,7 +340,7 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, u8 *addr) return NULL; } memset(sta, 0, sizeof(struct sta_info)); - sta->acct_interim_interval = hapd->conf->radius_acct_interim_interval; + sta->acct_interim_interval = hapd->conf->radius->acct_interim_interval; /* initialize STA info data */ eloop_register_timeout(AP_MAX_INACTIVITY, 0, ap_handle_timer, diff --git a/contrib/hostapd/sta_info.h b/contrib/hostapd/sta_info.h index 417df71..e2d7b4e 100644 --- a/contrib/hostapd/sta_info.h +++ b/contrib/hostapd/sta_info.h @@ -5,7 +5,7 @@ int ap_for_each_sta(struct hostapd_data *hapd, int (*cb)(struct hostapd_data *hapd, struct sta_info *sta, void *ctx), void *ctx); -struct sta_info* ap_get_sta(hostapd *hapd, u8 *sta); +struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta); void ap_sta_hash_add(hostapd *hapd, struct sta_info *sta); void ap_free_sta(hostapd *hapd, struct sta_info *sta); void ap_free_sta(hostapd *hapd, struct sta_info *sta); @@ -14,6 +14,6 @@ void ap_handle_timer(void *eloop_ctx, void *timeout_ctx); void ap_sta_session_timeout(hostapd *hapd, struct sta_info *sta, u32 session_timeout); void ap_sta_no_session_timeout(hostapd *hapd, struct sta_info *sta); -struct sta_info * ap_sta_add(struct hostapd_data *hapd, u8 *addr); +struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr); #endif /* STA_INFO_H */ diff --git a/contrib/hostapd/tls.h b/contrib/hostapd/tls.h index 99c9b33..a6a8110 100644 --- a/contrib/hostapd/tls.h +++ b/contrib/hostapd/tls.h @@ -1,3 +1,17 @@ +/* + * WPA Supplicant / SSL/TLS interface definition + * Copyright (c) 2004-2006, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + #ifndef TLS_H #define TLS_H @@ -10,28 +24,115 @@ struct tls_keys { size_t client_random_len; const u8 *server_random; size_t server_random_len; + + /* + * If TLS library does not provide access to master_key, but only to + * EAP key block, this pointer can be set to point to the result of + * PRF(master_secret, "client EAP encryption", + * client_random + server_random). + */ + const u8 *eap_tls_prf; + size_t eap_tls_prf_len; +}; + +struct tls_config { + const char *opensc_engine_path; + const char *pkcs11_engine_path; + const char *pkcs11_module_path; }; /** - * tls_init - initialize TLS library - * - * Returns: Context data to be used as @tls_ctx in calls to other functions, + * struct tls_connection_params - Parameters for TLS connection + * @ca_cert: File or reference name for CA X.509 certificate in PEM or DER + * format + * @ca_cert_blob: ca_cert as inlined data or %NULL if not used + * @ca_cert_blob_len: ca_cert_blob length + * @ca_path: Path to CA certificates (OpenSSL specific) + * @subject_match: String to match in the subject of the peer certificate or + * %NULL to allow all subjects + * @altsubject_match: String to match in the alternative subject of the peer + * certificate or %NULL to allow all alternative subjects + * @client_cert: File or reference name for client X.509 certificate in PEM or + * DER format + * @client_cert_blob: client_cert as inlined data or %NULL if not used + * @client_cert_blob_len: client_cert_blob length + * @private_key: File or reference name for client private key in PEM or DER + * format (traditional format (RSA PRIVATE KEY) or PKCS#8 (PRIVATE KEY) + * @private_key_blob: private_key as inlined data or %NULL if not used + * @private_key_blob_len: private_key_blob length + * @private_key_passwd: Passphrase for decrypted private key, %NULL if no + * passphrase is used. + * @dh_file: File name for DH/DSA data in PEM format, or %NULL if not used + * @dh_blob: dh_file as inlined data or %NULL if not used + * @dh_blob_len: dh_blob length + * @engine: 1 = use engine (e.g., a smartcard) for private key operations + * (this is OpenSSL specific for now) + * @engine_id: engine id string (this is OpenSSL specific for now) + * @ppin: pointer to the pin variable in the configuration + * (this is OpenSSL specific for now) + * @key_id: the private key's key id (this is OpenSSL specific for now) + * + * TLS connection parameters to be configured with tls_connection_set_params(). + * + * Certificates and private key can be configured either as a reference name + * (file path or reference to certificate store) or by providing the same data + * as a pointer to the data in memory. Only one option will be used for each + * field. + */ +struct tls_connection_params { + const char *ca_cert; + const u8 *ca_cert_blob; + size_t ca_cert_blob_len; + const char *ca_path; + const char *subject_match; + const char *altsubject_match; + const char *client_cert; + const u8 *client_cert_blob; + size_t client_cert_blob_len; + const char *private_key; + const u8 *private_key_blob; + size_t private_key_blob_len; + const char *private_key_passwd; + const char *dh_file; + const u8 *dh_blob; + size_t dh_blob_len; + + /* OpenSSL specific variables */ + int engine; + const char *engine_id; + const char *pin; + const char *key_id; +}; + + +/** + * tls_init - Initialize TLS library + * @conf: Configuration data for TLS library + * Returns: Context data to be used as tls_ctx in calls to other functions, * or %NULL on failure. * - * Called once during program startup. + * Called once during program startup and once for each RSN pre-authentication + * session. In other words, there can be two concurrent TLS contexts. If global + * library initialization is needed (i.e., one that is shared between both + * authentication types), the TLS library wrapper should maintain a reference + * counter and do global initialization only when moving from 0 to 1 reference. */ -void * tls_init(void); +void * tls_init(const struct tls_config *conf); /** - * tls_deinit - deinitialize TLS library + * tls_deinit - Deinitialize TLS library * @tls_ctx: TLS context data from tls_init() * - * Called once during program shutdown. + * Called once during program shutdown and once for each RSN pre-authentication + * session. If global library deinitialization is needed (i.e., one that is + * shared between both authentication types), the TLS library wrapper should + * maintain a reference counter and do global deinitialization only when moving + * from 1 to 0 references. */ void tls_deinit(void *tls_ctx); /** - * tls_get_errors - process pending errors + * tls_get_errors - Process pending errors * @tls_ctx: TLS context data from tls_init() * * Returns: Number of found error, 0 if no errors detected. @@ -41,15 +142,15 @@ void tls_deinit(void *tls_ctx); int tls_get_errors(void *tls_ctx); /** - * tls_connection_init - initialize a new TLS connection + * tls_connection_init - Initialize a new TLS connection * @tls_ctx: TLS context data from tls_init() * - * Returns: Connection context data, @conn for other function calls + * Returns: Connection context data, conn for other function calls */ struct tls_connection * tls_connection_init(void *tls_ctx); /** - * tls_connection_deinit - free TLS connection data + * tls_connection_deinit - Free TLS connection data * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @@ -58,7 +159,7 @@ struct tls_connection * tls_connection_init(void *tls_ctx); void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn); /** - * tls_connection_established - has the TLS connection been completed? + * tls_connection_established - Has the TLS connection been completed? * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @@ -67,34 +168,40 @@ void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn); int tls_connection_established(void *tls_ctx, struct tls_connection *conn); /** - * tls_connection_shutdown - shutdown TLS connection data. + * tls_connection_shutdown - Shutdown TLS connection data. * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * * Returns: 0 on success, -1 on failure * * Shutdown current TLS connection without releasing all resources. New - * connection can be started by using the same @conn without having to call + * connection can be started by using the same conn without having to call * tls_connection_init() or setting certificates etc. again. The new * connection should try to use session resumption. */ int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn); +enum { + TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED = -3, + TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED = -2 +}; /** - * tls_connection_ca_cert - set trusted CA certificate for TLS connection + * tls_connection_set_params - Set TLS connection parameters * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() - * @ca_cert: File name for CA certificate in PEM or DER format - * @subject_match: String to match in the subject of the peer certificate or - * %NULL to allow all subjects + * @params: Connection parameters * - * Returns: 0 on success, -1 on failure + * Returns: 0 on success, -1 on failure, + * TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED (-2) on possible PIN error causing + * PKCS#11 engine failure, or + * TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED (-3) on failure to verify the + * PKCS#11 engine private key. */ -int tls_connection_ca_cert(void *tls_ctx, struct tls_connection *conn, - const char *ca_cert, const char *subject_match); +int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, + const struct tls_connection_params *params); /** - * tls_global_ca_cert - set trusted CA certificate for all TLS connections + * tls_global_ca_cert - Set trusted CA certificate for all TLS connections * @tls_ctx: TLS context data from tls_init() * @ca_cert: File name for CA certificate in PEM or DER format * %NULL to allow all subjects @@ -104,31 +211,28 @@ int tls_connection_ca_cert(void *tls_ctx, struct tls_connection *conn, int tls_global_ca_cert(void *tls_ctx, const char *ca_cert); /** - * tls_connection_ca_cert - set trusted CA certificate for TLS connection + * tls_global_set_verify - Set global certificate verification options * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * @verify_peer: 1 = verify peer certificate - * @subject_match: String to match in the subject of the peer certificate or - * %NULL to allow all subjects + * @check_crl: 0 = do not verify CRLs, 1 = verify CRL for the user certificate, + * 2 = verify CRL for all certificates * * Returns: 0 on success, -1 on failure */ -int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, - int verify_peer, const char *subject_match); +int tls_global_set_verify(void *tls_ctx, int check_crl); /** - * tls_connection_client_cert - set client certificate for TLS connection + * tls_connection_set_verify - Set certificate verification options * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() - * @client_cert: File name for client certificate in PEM or DER format + * @verify_peer: 1 = verify peer certificate * * Returns: 0 on success, -1 on failure */ -int tls_connection_client_cert(void *tls_ctx, struct tls_connection *conn, - const char *client_cert); +int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, + int verify_peer); /** - * tls_global_client_cert - set client certificate for all TLS connections + * tls_global_client_cert - Set client certificate for all TLS connections * @tls_ctx: TLS context data from tls_init() * @client_cert: File name for client certificate in PEM or DER format * @@ -137,21 +241,7 @@ int tls_connection_client_cert(void *tls_ctx, struct tls_connection *conn, int tls_global_client_cert(void *tls_ctx, const char *client_cert); /** - * tls_connection_private_key - set private key for TLS connection - * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * @private_key: File name for client private key in PEM or DER format - * @private_key_passwd: Passphrase for decrypted private key, %NULL if no - * passphrase is used. - * - * Returns: 0 on success, -1 on failure - */ -int tls_connection_private_key(void *tls_ctx, struct tls_connection *conn, - const char *private_key, - const char *private_key_passwd); - -/** - * tls_global_private_key - set private key for all TLS connections + * tls_global_private_key - Set private key for all TLS connections * @tls_ctx: TLS context data from tls_init() * @private_key: File name for client private key in PEM or DER format * @private_key_passwd: Passphrase for decrypted private key, %NULL if no @@ -163,18 +253,7 @@ int tls_global_private_key(void *tls_ctx, const char *private_key, const char *private_key_passwd); /** - * tls_connection_dh - set DH/DSA parameters for TLS connection - * @tls_ctx: TLS context data from tls_init() - * @conn: Connection context data from tls_connection_init() - * @dh_file: File name for DH/DSA data in PEM format. - * - * Returns: 0 on success, -1 on failure - */ -int tls_connection_dh(void *tls_ctx, struct tls_connection *conn, - const char *dh_file); - -/** - * tls_connection_get_keys - get master key and random data from TLS connection + * tls_connection_get_keys - Get master key and random data from TLS connection * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @keys: Structure of key/random data (filled on success) @@ -185,23 +264,37 @@ int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn, struct tls_keys *keys); /** - * tls_connection_handshake - process TLS handshake (client side) + * tls_connection_handshake - Process TLS handshake (client side) * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @in_data: Input data from TLS peer * @in_len: Input data length * @out_len: Length of the output buffer. * - * Returns: pointer to output data, %NULL on failure + * Returns: Pointer to output data, %NULL on failure * * Caller is responsible for freeing returned output data. + * + * This function is used during TLS handshake. The first call is done with + * in_data == %NULL and the library is expected to return ClientHello packet. + * This packet is then send to the server and a response from server is given + * to TLS library by calling this function again with in_data pointing to the + * TLS message from the server. + * + * If the TLS handshake fails, this function may return %NULL. However, if the + * TLS library has a TLS alert to send out, that should be returned as the + * output data. In this case, tls_connection_get_failed() must return failure + * (> 0). + * + * tls_connection_established() should return 1 once the TLS handshake has been + * completed successfully. */ u8 * tls_connection_handshake(void *tls_ctx, struct tls_connection *conn, const u8 *in_data, size_t in_len, size_t *out_len); /** - * tls_connection_servr_handshake - process TLS handshake (server side) + * tls_connection_server_handshake - Process TLS handshake (server side) * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @in_data: Input data from TLS peer @@ -218,37 +311,43 @@ u8 * tls_connection_server_handshake(void *tls_ctx, size_t *out_len); /** - * tls_connection_encrypt - encrypt data into TLS tunnel + * tls_connection_encrypt - Encrypt data into TLS tunnel * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @in_data: Pointer to plaintext data to be encrypted * @in_len: Input buffer length * @out_data: Pointer to output buffer (encrypted TLS data) - * @out_len: Maximum @out_data length + * @out_len: Maximum out_data length + * + * Returns: Number of bytes written to out_data, -1 on failure * - * Returns: Number of bytes written to @out_data, -1 on failure + * This function is used after TLS handshake has been completed successfully to + * send data in the encrypted tunnel. */ int tls_connection_encrypt(void *tls_ctx, struct tls_connection *conn, - u8 *in_data, size_t in_len, + const u8 *in_data, size_t in_len, u8 *out_data, size_t out_len); /** - * tls_connection_decrypt - decrypt data from TLS tunnel + * tls_connection_decrypt - Decrypt data from TLS tunnel * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @in_data: Pointer to input buffer (encrypted TLS data) * @in_len: Input buffer length * @out_data: Pointer to output buffer (decrypted data from TLS tunnel) - * @out_len: Maximum @out_data length + * @out_len: Maximum out_data length * - * Returns: Number of bytes written to @out_data, -1 on failure + * Returns: Number of bytes written to out_data, -1 on failure + * + * This function is used after TLS handshake has been completed successfully to + * receive data from the encrypted tunnel. */ int tls_connection_decrypt(void *tls_ctx, struct tls_connection *conn, - u8 *in_data, size_t in_len, + const u8 *in_data, size_t in_len, u8 *out_data, size_t out_len); /** - * tls_connection_resumed - was session resumption used + * tls_connection_resumed - Was session resumption used * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @@ -257,19 +356,19 @@ int tls_connection_decrypt(void *tls_ctx, struct tls_connection *conn, int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn); /** - * tls_connection_set_master_key - configure master secret for TLS connection + * tls_connection_set_master_key - Configure master secret for TLS connection * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @key: TLS pre-master-secret - * @key_len: length of @key in bytes + * @key_len: length of key in bytes * * Returns: 0 on success, -1 on failure */ -int tls_connection_set_master_key(void *ssl_ctx, struct tls_connection *conn, +int tls_connection_set_master_key(void *tls_ctx, struct tls_connection *conn, const u8 *key, size_t key_len); /** - * tls_connection_set_anon_dh - configure TLS connection to use anonymous DH + * tls_connection_set_anon_dh - Configure TLS connection to use anonymous DH * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @@ -278,22 +377,24 @@ int tls_connection_set_master_key(void *ssl_ctx, struct tls_connection *conn, * TODO: consider changing this to more generic routine for configuring allowed * ciphers */ -int tls_connection_set_anon_dh(void *ssl_ctx, struct tls_connection *conn); +int tls_connection_set_anon_dh(void *tls_ctx, struct tls_connection *conn); /** - * tls_get_cipher - get current cipher name + * tls_get_cipher - Get current cipher name * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() + * @buf: Buffer for the cipher name + * @buflen: buf size * * Returns: 0 on success, -1 on failure * * Get the name of the currently used cipher. */ -int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, +int tls_get_cipher(void *tls_ctx, struct tls_connection *conn, char *buf, size_t buflen); /** - * tls_connection_enable_workaround - enable TLS workaround options + * tls_connection_enable_workaround - Enable TLS workaround options * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @@ -302,11 +403,61 @@ int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, * This function is used to enable connection-specific workaround options for * buffer SSL/TLS implementations. */ -int tls_connection_enable_workaround(void *ssl_ctx, +int tls_connection_enable_workaround(void *tls_ctx, struct tls_connection *conn); -int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, +/** + * tls_connection_client_hello_ext - Set TLS extension for ClientHello + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * @ext_type: Extension type + * @data: Extension payload (NULL to remove extension) + * @data_len: Extension payload length + * + * Returns: 0 on success, -1 on failure + */ +int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn, int ext_type, const u8 *data, size_t data_len); +/** + * tls_connection_get_failed - Get connection failure status + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * + * Returns >0 if connection has failed, 0 if not. + */ +int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn); + +/** + * tls_connection_get_read_alerts - Get connection read alert status + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * + * Returns: Number of times a fatal read (remote end reported error) has + * happened during this connection. + */ +int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn); + +/** + * tls_connection_get_write_alerts - Get connection write alert status + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * + * Returns: Number of times a fatal write (locally detected error) has happened + * during this connection. + */ +int tls_connection_get_write_alerts(void *tls_ctx, + struct tls_connection *conn); + +/** + * tls_connection_get_keyblock_size - Get TLS key_block size + * @tls_ctx: TLS context data from tls_init() + * @conn: Connection context data from tls_connection_init() + * Returns: Size of the key_block for the negotiated cipher suite or -1 on + * failure + */ +int tls_connection_get_keyblock_size(void *tls_ctx, + struct tls_connection *conn); + #endif /* TLS_H */ diff --git a/contrib/hostapd/tls_none.c b/contrib/hostapd/tls_none.c index 2b3cafc..07fd879 100644 --- a/contrib/hostapd/tls_none.c +++ b/contrib/hostapd/tls_none.c @@ -12,7 +12,13 @@ * See README and COPYING for more details. */ -void * tls_init(void) +#include <stdlib.h> +#include <stdio.h> + +#include "common.h" +#include "tls.h" + +void * tls_init(const struct tls_config *conf) { return (void *) 1; } diff --git a/contrib/hostapd/tls_openssl.c b/contrib/hostapd/tls_openssl.c index 4e6ea53..08d5a80 100644 --- a/contrib/hostapd/tls_openssl.c +++ b/contrib/hostapd/tls_openssl.c @@ -1,6 +1,6 @@ /* * WPA Supplicant / SSL/TLS interface functions for openssl - * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * Copyright (c) 2004-2006, Jouni Malinen <jkmaline@cc.hut.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -15,21 +15,504 @@ #include <stdlib.h> #include <stdio.h> #include <string.h> + +#ifndef CONFIG_SMARTCARD +#ifndef OPENSSL_NO_ENGINE +#define OPENSSL_NO_ENGINE +#endif +#endif + #include <openssl/ssl.h> #include <openssl/err.h> #include <openssl/pkcs12.h> +#include <openssl/x509v3.h> +#ifndef OPENSSL_NO_ENGINE +#include <openssl/engine.h> +#endif /* OPENSSL_NO_ENGINE */ #include "common.h" #include "tls.h" +#if OPENSSL_VERSION_NUMBER >= 0x0090800fL +#define OPENSSL_d2i_TYPE const unsigned char ** +#else +#define OPENSSL_d2i_TYPE unsigned char ** +#endif + +static int tls_openssl_ref_count = 0; struct tls_connection { SSL *ssl; BIO *ssl_in, *ssl_out; - char *subject_match; +#ifndef OPENSSL_NO_ENGINE + ENGINE *engine; /* functional reference to the engine */ + EVP_PKEY *private_key; /* the private key if using engine */ +#endif /* OPENSSL_NO_ENGINE */ + char *subject_match, *altsubject_match; + int read_alerts, write_alerts, failed; + + u8 *pre_shared_secret; + size_t pre_shared_secret_len; }; +#ifdef CONFIG_NO_STDOUT_DEBUG + +static void _tls_show_errors(void) +{ + unsigned long err; + + while ((err = ERR_get_error())) { + /* Just ignore the errors, since stdout is disabled */ + } +} +#define tls_show_errors(l, f, t) _tls_show_errors() + +#else /* CONFIG_NO_STDOUT_DEBUG */ + +static void tls_show_errors(int level, const char *func, const char *txt) +{ + unsigned long err; + + wpa_printf(level, "OpenSSL: %s - %s %s", + func, txt, ERR_error_string(ERR_get_error(), NULL)); + + while ((err = ERR_get_error())) { + wpa_printf(MSG_INFO, "OpenSSL: pending error: %s", + ERR_error_string(err, NULL)); + } +} + +#endif /* CONFIG_NO_STDOUT_DEBUG */ + + +#ifdef CONFIG_NATIVE_WINDOWS + +/* Windows CryptoAPI and access to certificate stores */ +#include <wincrypt.h> + +#ifdef __MINGW32_VERSION +/* + * MinGW does not yet include all the needed definitions for CryptoAPI, so + * define here whatever extra is needed. + */ +#define CALG_SSL3_SHAMD5 (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_SSL3SHAMD5) +#define CERT_SYSTEM_STORE_CURRENT_USER (1 << 16) +#define CERT_STORE_READONLY_FLAG 0x00008000 +#define CERT_STORE_OPEN_EXISTING_FLAG 0x00004000 +#define CRYPT_ACQUIRE_COMPARE_KEY_FLAG 0x00000004 + +static BOOL WINAPI +(*CryptAcquireCertificatePrivateKey)(PCCERT_CONTEXT pCert, DWORD dwFlags, + void *pvReserved, HCRYPTPROV *phCryptProv, + DWORD *pdwKeySpec, BOOL *pfCallerFreeProv) += NULL; /* to be loaded from crypt32.dll */ + +static PCCERT_CONTEXT WINAPI +(*CertEnumCertificatesInStore)(HCERTSTORE hCertStore, + PCCERT_CONTEXT pPrevCertContext) += NULL; /* to be loaded from crypt32.dll */ + +static int mingw_load_crypto_func(void) +{ + HINSTANCE dll; + + /* MinGW does not yet have full CryptoAPI support, so load the needed + * function here. */ + + if (CryptAcquireCertificatePrivateKey) + return 0; + + dll = LoadLibrary("crypt32"); + if (dll == NULL) { + wpa_printf(MSG_DEBUG, "CryptoAPI: Could not load crypt32 " + "library"); + return -1; + } + + CryptAcquireCertificatePrivateKey = GetProcAddress( + dll, "CryptAcquireCertificatePrivateKey"); + if (CryptAcquireCertificatePrivateKey == NULL) { + wpa_printf(MSG_DEBUG, "CryptoAPI: Could not get " + "CryptAcquireCertificatePrivateKey() address from " + "crypt32 library"); + return -1; + } + + CertEnumCertificatesInStore = (void *) GetProcAddress( + dll, "CertEnumCertificatesInStore"); + if (CertEnumCertificatesInStore == NULL) { + wpa_printf(MSG_DEBUG, "CryptoAPI: Could not get " + "CertEnumCertificatesInStore() address from " + "crypt32 library"); + return -1; + } + + return 0; +} + +#else /* __MINGW32_VERSION */ + +static int mingw_load_crypto_func(void) +{ + return 0; +} + +#endif /* __MINGW32_VERSION */ + + +struct cryptoapi_rsa_data { + const CERT_CONTEXT *cert; + HCRYPTPROV crypt_prov; + DWORD key_spec; + BOOL free_crypt_prov; +}; + + +static void cryptoapi_error(const char *msg) +{ + wpa_printf(MSG_INFO, "CryptoAPI: %s; err=%u", + msg, (unsigned int) GetLastError()); +} + + +static int cryptoapi_rsa_pub_enc(int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, int padding) +{ + wpa_printf(MSG_DEBUG, "%s - not implemented", __func__); + return 0; +} + + +static int cryptoapi_rsa_pub_dec(int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, int padding) +{ + wpa_printf(MSG_DEBUG, "%s - not implemented", __func__); + return 0; +} + + +static int cryptoapi_rsa_priv_enc(int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, int padding) +{ + struct cryptoapi_rsa_data *priv = + (struct cryptoapi_rsa_data *) rsa->meth->app_data; + HCRYPTHASH hash; + DWORD hash_size, len, i; + unsigned char *buf = NULL; + int ret = 0; + + if (priv == NULL) { + RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, + ERR_R_PASSED_NULL_PARAMETER); + return 0; + } + + if (padding != RSA_PKCS1_PADDING) { + RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, + RSA_R_UNKNOWN_PADDING_TYPE); + return 0; + } + + if (flen != 16 /* MD5 */ + 20 /* SHA-1 */) { + wpa_printf(MSG_INFO, "%s - only MD5-SHA1 hash supported", + __func__); + RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, + RSA_R_INVALID_MESSAGE_LENGTH); + return 0; + } + + if (!CryptCreateHash(priv->crypt_prov, CALG_SSL3_SHAMD5, 0, 0, &hash)) + { + cryptoapi_error("CryptCreateHash failed"); + return 0; + } + + len = sizeof(hash_size); + if (!CryptGetHashParam(hash, HP_HASHSIZE, (BYTE *) &hash_size, &len, + 0)) { + cryptoapi_error("CryptGetHashParam failed"); + goto err; + } + + if (hash_size != flen) { + wpa_printf(MSG_INFO, "CryptoAPI: Invalid hash size (%u != %d)", + (unsigned) hash_size, flen); + RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, + RSA_R_INVALID_MESSAGE_LENGTH); + goto err; + } + if (!CryptSetHashParam(hash, HP_HASHVAL, (BYTE * ) from, 0)) { + cryptoapi_error("CryptSetHashParam failed"); + goto err; + } + + len = RSA_size(rsa); + buf = malloc(len); + if (buf == NULL) { + RSAerr(RSA_F_RSA_EAY_PRIVATE_ENCRYPT, ERR_R_MALLOC_FAILURE); + goto err; + } + + if (!CryptSignHash(hash, priv->key_spec, NULL, 0, buf, &len)) { + cryptoapi_error("CryptSignHash failed"); + goto err; + } + + for (i = 0; i < len; i++) + to[i] = buf[len - i - 1]; + ret = len; + +err: + free(buf); + CryptDestroyHash(hash); + + return ret; +} + + +static int cryptoapi_rsa_priv_dec(int flen, const unsigned char *from, + unsigned char *to, RSA *rsa, int padding) +{ + wpa_printf(MSG_DEBUG, "%s - not implemented", __func__); + return 0; +} + + +static void cryptoapi_free_data(struct cryptoapi_rsa_data *priv) +{ + if (priv == NULL) + return; + if (priv->crypt_prov && priv->free_crypt_prov) + CryptReleaseContext(priv->crypt_prov, 0); + if (priv->cert) + CertFreeCertificateContext(priv->cert); + free(priv); +} + + +static int cryptoapi_finish(RSA *rsa) +{ + cryptoapi_free_data((struct cryptoapi_rsa_data *) rsa->meth->app_data); + free((void *) rsa->meth); + rsa->meth = NULL; + return 1; +} + + +static const CERT_CONTEXT * cryptoapi_find_cert(const char *name, DWORD store) +{ + HCERTSTORE cs; + const CERT_CONTEXT *ret = NULL; + + cs = CertOpenStore((LPCSTR) CERT_STORE_PROV_SYSTEM, 0, 0, + store | CERT_STORE_OPEN_EXISTING_FLAG | + CERT_STORE_READONLY_FLAG, L"MY"); + if (cs == NULL) { + cryptoapi_error("Failed to open 'My system store'"); + return NULL; + } + + if (strncmp(name, "cert://", 7) == 0) { + unsigned short wbuf[255]; + MultiByteToWideChar(CP_ACP, 0, name + 7, -1, + wbuf, sizeof(wbuf)); + ret = CertFindCertificateInStore(cs, X509_ASN_ENCODING | + PKCS_7_ASN_ENCODING, + 0, CERT_FIND_SUBJECT_STR, + wbuf, NULL); + } else if (strncmp(name, "hash://", 7) == 0) { + CRYPT_HASH_BLOB blob; + int len; + const char *hash = name + 7; + unsigned char *buf; + + len = strlen(hash) / 2; + buf = malloc(len); + if (buf && hexstr2bin(hash, buf, len) == 0) { + blob.cbData = len; + blob.pbData = buf; + ret = CertFindCertificateInStore(cs, + X509_ASN_ENCODING | + PKCS_7_ASN_ENCODING, + 0, CERT_FIND_HASH, + &blob, NULL); + } + free(buf); + } + + CertCloseStore(cs, 0); + + return ret; +} + + +static int tls_cryptoapi_cert(SSL *ssl, const char *name) +{ + X509 *cert = NULL; + RSA *rsa = NULL, *pub_rsa; + struct cryptoapi_rsa_data *priv; + RSA_METHOD *rsa_meth; + + if (name == NULL || + (strncmp(name, "cert://", 7) != 0 && + strncmp(name, "hash://", 7) != 0)) + return -1; + + priv = malloc(sizeof(*priv)); + rsa_meth = malloc(sizeof(*rsa_meth)); + if (priv == NULL || rsa_meth == NULL) { + wpa_printf(MSG_WARNING, "CryptoAPI: Failed to allocate memory " + "for CryptoAPI RSA method"); + free(priv); + free(rsa_meth); + return -1; + } + memset(priv, 0, sizeof(*priv)); + memset(rsa_meth, 0, sizeof(*rsa_meth)); + + priv->cert = cryptoapi_find_cert(name, CERT_SYSTEM_STORE_CURRENT_USER); + if (priv->cert == NULL) { + priv->cert = cryptoapi_find_cert( + name, CERT_SYSTEM_STORE_LOCAL_MACHINE); + } + if (priv->cert == NULL) { + wpa_printf(MSG_INFO, "CryptoAPI: Could not find certificate " + "'%s'", name); + goto err; + } + + cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &priv->cert->pbCertEncoded, + priv->cert->cbCertEncoded); + if (cert == NULL) { + wpa_printf(MSG_INFO, "CryptoAPI: Could not process X509 DER " + "encoding"); + goto err; + } + + if (mingw_load_crypto_func()) + goto err; + + if (!CryptAcquireCertificatePrivateKey(priv->cert, + CRYPT_ACQUIRE_COMPARE_KEY_FLAG, + NULL, &priv->crypt_prov, + &priv->key_spec, + &priv->free_crypt_prov)) { + cryptoapi_error("Failed to acquire a private key for the " + "certificate"); + goto err; + } + + rsa_meth->name = "Microsoft CryptoAPI RSA Method"; + rsa_meth->rsa_pub_enc = cryptoapi_rsa_pub_enc; + rsa_meth->rsa_pub_dec = cryptoapi_rsa_pub_dec; + rsa_meth->rsa_priv_enc = cryptoapi_rsa_priv_enc; + rsa_meth->rsa_priv_dec = cryptoapi_rsa_priv_dec; + rsa_meth->finish = cryptoapi_finish; + rsa_meth->flags = RSA_METHOD_FLAG_NO_CHECK; + rsa_meth->app_data = (char *) priv; + + rsa = RSA_new(); + if (rsa == NULL) { + SSLerr(SSL_F_SSL_CTX_USE_CERTIFICATE_FILE, + ERR_R_MALLOC_FAILURE); + goto err; + } + + if (!SSL_use_certificate(ssl, cert)) + goto err; + pub_rsa = cert->cert_info->key->pkey->pkey.rsa; + X509_free(cert); + cert = NULL; + + rsa->n = BN_dup(pub_rsa->n); + rsa->e = BN_dup(pub_rsa->e); + if (!RSA_set_method(rsa, rsa_meth)) + goto err; + + if (!SSL_use_RSAPrivateKey(ssl, rsa)) + goto err; + RSA_free(rsa); + + return 0; + +err: + if (cert) + X509_free(cert); + if (rsa) + RSA_free(rsa); + else { + free(rsa_meth); + cryptoapi_free_data(priv); + } + return -1; +} + + +static int tls_cryptoapi_ca_cert(SSL_CTX *ssl_ctx, SSL *ssl, const char *name) +{ + HCERTSTORE cs; + PCCERT_CONTEXT ctx = NULL; + X509 *cert; + char buf[128]; + + if (mingw_load_crypto_func()) + return -1; + + if (name == NULL || strncmp(name, "cert_store://", 13) != 0) + return -1; + + cs = CertOpenSystemStore(0, name + 13); + if (cs == NULL) { + wpa_printf(MSG_DEBUG, "%s: failed to open system cert store " + "'%s': error=%d", __func__, name + 13, + (int) GetLastError()); + return -1; + } + + while ((ctx = CertEnumCertificatesInStore(cs, ctx))) { + cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &ctx->pbCertEncoded, + ctx->cbCertEncoded); + if (cert == NULL) { + wpa_printf(MSG_INFO, "CryptoAPI: Could not process " + "X509 DER encoding for CA cert"); + continue; + } + + X509_NAME_oneline(X509_get_subject_name(cert), buf, + sizeof(buf)); + wpa_printf(MSG_DEBUG, "OpenSSL: Loaded CA certificate for " + "system certificate store: subject='%s'", buf); + + if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) { + tls_show_errors(MSG_WARNING, __func__, + "Failed to add ca_cert to OpenSSL " + "certificate store"); + } + + X509_free(cert); + } + + if (!CertCloseStore(cs, 0)) { + wpa_printf(MSG_DEBUG, "%s: failed to close system cert store " + "'%s': error=%d", __func__, name + 13, + (int) GetLastError()); + } + + return 0; +} + + +#else /* CONFIG_NATIVE_WINDOWS */ + +static int tls_cryptoapi_cert(SSL *ssl, const char *name) +{ + return -1; +} + +#endif /* CONFIG_NATIVE_WINDOWS */ + + static void ssl_info_cb(const SSL *ssl, int where, int ret) { const char *str; @@ -50,10 +533,18 @@ static void ssl_info_cb(const SSL *ssl, int where, int ret) } else if (where & SSL_CB_ALERT) { wpa_printf(MSG_INFO, "SSL: SSL3 alert: %s:%s:%s", where & SSL_CB_READ ? - "read (authentication server reported an error)" : + "read (remote end reported an error)" : "write (local SSL3 detected an error)", SSL_alert_type_string_long(ret), SSL_alert_desc_string_long(ret)); + if ((ret >> 8) == SSL3_AL_FATAL) { + struct tls_connection *conn = + SSL_get_app_data((SSL *) ssl); + if (where & SSL_CB_READ) + conn->read_alerts++; + else + conn->write_alerts++; + } } else if (where & SSL_CB_EXIT && ret <= 0) { wpa_printf(MSG_DEBUG, "SSL: %s:%s in %s", str, ret == 0 ? "failed" : "error", @@ -62,18 +553,160 @@ static void ssl_info_cb(const SSL *ssl, int where, int ret) } -void * tls_init(void) +#ifndef OPENSSL_NO_ENGINE +/** + * tls_engine_load_dynamic_generic - load any openssl engine + * @pre: an array of commands and values that load an engine initialized + * in the engine specific function + * @post: an array of commands and values that initialize an already loaded + * engine (or %NULL if not required) + * @id: the engine id of the engine to load (only required if post is not %NULL + * + * This function is a generic function that loads any openssl engine. + * + * Returns: 0 on success, -1 on failure + */ +static int tls_engine_load_dynamic_generic(const char *pre[], + const char *post[], const char *id) +{ + ENGINE *engine; + const char *dynamic_id = "dynamic"; + + engine = ENGINE_by_id(id); + if (engine) { + ENGINE_free(engine); + wpa_printf(MSG_DEBUG, "ENGINE: engine '%s' is already " + "available", id); + return 0; + } + ERR_clear_error(); + + engine = ENGINE_by_id(dynamic_id); + if (engine == NULL) { + wpa_printf(MSG_INFO, "ENGINE: Can't find engine %s [%s]", + dynamic_id, + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + + /* Perform the pre commands. This will load the engine. */ + while (pre && pre[0]) { + wpa_printf(MSG_DEBUG, "ENGINE: '%s' '%s'", pre[0], pre[1]); + if (ENGINE_ctrl_cmd_string(engine, pre[0], pre[1], 0) == 0) { + wpa_printf(MSG_INFO, "ENGINE: ctrl cmd_string failed: " + "%s %s [%s]", pre[0], pre[1], + ERR_error_string(ERR_get_error(), NULL)); + ENGINE_free(engine); + return -1; + } + pre += 2; + } + + /* + * Free the reference to the "dynamic" engine. The loaded engine can + * now be looked up using ENGINE_by_id(). + */ + ENGINE_free(engine); + + engine = ENGINE_by_id(id); + if (engine == NULL) { + wpa_printf(MSG_INFO, "ENGINE: Can't find engine %s [%s]", + id, ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + + while (post && post[0]) { + wpa_printf(MSG_DEBUG, "ENGINE: '%s' '%s'", post[0], post[1]); + if (ENGINE_ctrl_cmd_string(engine, post[0], post[1], 0) == 0) { + wpa_printf(MSG_DEBUG, "ENGINE: ctrl cmd_string failed:" + " %s %s [%s]", post[0], post[1], + ERR_error_string(ERR_get_error(), NULL)); + ENGINE_remove(engine); + ENGINE_free(engine); + return -1; + } + post += 2; + } + ENGINE_free(engine); + + return 0; +} + + +/** + * tls_engine_load_dynamic_pkcs11 - load the pkcs11 engine provided by opensc + * @pkcs11_so_path: pksc11_so_path from the configuration + * @pcks11_module_path: pkcs11_module_path from the configuration + */ +static int tls_engine_load_dynamic_pkcs11(const char *pkcs11_so_path, + const char *pkcs11_module_path) +{ + char *engine_id = "pkcs11"; + const char *pre_cmd[] = { + "SO_PATH", pkcs11_so_path, + "ID", engine_id, + "LIST_ADD", "1", + /* "NO_VCHECK", "1", */ + "LOAD", NULL, + NULL, NULL + }; + const char *post_cmd[] = { + "MODULE_PATH", pkcs11_module_path, + NULL, NULL + }; + + if (!pkcs11_so_path || !pkcs11_module_path) + return 0; + + wpa_printf(MSG_DEBUG, "ENGINE: Loading pkcs11 Engine from %s", + pkcs11_so_path); + + return tls_engine_load_dynamic_generic(pre_cmd, post_cmd, engine_id); +} + + +/** + * tls_engine_load_dynamic_opensc - load the opensc engine provided by opensc + * @opensc_so_path: opensc_so_path from the configuration + */ +static int tls_engine_load_dynamic_opensc(const char *opensc_so_path) +{ + char *engine_id = "opensc"; + const char *pre_cmd[] = { + "SO_PATH", opensc_so_path, + "ID", engine_id, + "LIST_ADD", "1", + "LOAD", NULL, + NULL, NULL + }; + + if (!opensc_so_path) + return 0; + + wpa_printf(MSG_DEBUG, "ENGINE: Loading OpenSC Engine from %s", + opensc_so_path); + + return tls_engine_load_dynamic_generic(pre_cmd, NULL, engine_id); +} +#endif /* OPENSSL_NO_ENGINE */ + + +void * tls_init(const struct tls_config *conf) { SSL_CTX *ssl; - SSL_load_error_strings(); - SSL_library_init(); - /* TODO: if /dev/urandom is available, PRNG is seeded automatically. - * If this is not the case, random data should be added here. */ + if (tls_openssl_ref_count == 0) { + SSL_load_error_strings(); + SSL_library_init(); + /* TODO: if /dev/urandom is available, PRNG is seeded + * automatically. If this is not the case, random data should + * be added here. */ #ifdef PKCS12_FUNCS - PKCS12_PBE_add(); + PKCS12_PBE_add(); #endif /* PKCS12_FUNCS */ + } + tls_openssl_ref_count++; ssl = SSL_CTX_new(TLSv1_method()); if (ssl == NULL) @@ -81,6 +714,23 @@ void * tls_init(void) SSL_CTX_set_info_callback(ssl, ssl_info_cb); +#ifndef OPENSSL_NO_ENGINE + if (conf && + (conf->opensc_engine_path || conf->pkcs11_engine_path || + conf->pkcs11_module_path)) { + wpa_printf(MSG_DEBUG, "ENGINE: Loading dynamic engine"); + ERR_load_ENGINE_strings(); + ENGINE_load_dynamic(); + + if (tls_engine_load_dynamic_opensc(conf->opensc_engine_path) || + tls_engine_load_dynamic_pkcs11(conf->pkcs11_engine_path, + conf->pkcs11_module_path)) { + tls_deinit(ssl); + return NULL; + } + } +#endif /* OPENSSL_NO_ENGINE */ + return ssl; } @@ -89,8 +739,98 @@ void tls_deinit(void *ssl_ctx) { SSL_CTX *ssl = ssl_ctx; SSL_CTX_free(ssl); - ERR_free_strings(); - EVP_cleanup(); + + tls_openssl_ref_count--; + if (tls_openssl_ref_count == 0) { +#ifndef OPENSSL_NO_ENGINE + ENGINE_cleanup(); +#endif /* OPENSSL_NO_ENGINE */ + ERR_free_strings(); + EVP_cleanup(); + } +} + + +static int tls_engine_init(struct tls_connection *conn, const char *engine_id, + const char *pin, const char *key_id) +{ +#ifndef OPENSSL_NO_ENGINE + int ret = -1; + if (engine_id == NULL) { + wpa_printf(MSG_ERROR, "ENGINE: Engine ID not set"); + return -1; + } + if (pin == NULL) { + wpa_printf(MSG_ERROR, "ENGINE: Smartcard PIN not set"); + return -1; + } + if (key_id == NULL) { + wpa_printf(MSG_ERROR, "ENGINE: Key Id not set"); + return -1; + } + + ERR_clear_error(); + conn->engine = ENGINE_by_id(engine_id); + if (!conn->engine) { + wpa_printf(MSG_ERROR, "ENGINE: engine %s not available [%s]", + engine_id, ERR_error_string(ERR_get_error(), NULL)); + goto err; + } + if (ENGINE_init(conn->engine) != 1) { + wpa_printf(MSG_ERROR, "ENGINE: engine init failed " + "(engine: %s) [%s]", engine_id, + ERR_error_string(ERR_get_error(), NULL)); + goto err; + } + wpa_printf(MSG_DEBUG, "ENGINE: engine initialized"); + + if (ENGINE_ctrl_cmd_string(conn->engine, "PIN", pin, 0) == 0) { + wpa_printf(MSG_ERROR, "ENGINE: cannot set pin [%s]", + ERR_error_string(ERR_get_error(), NULL)); + goto err; + } + conn->private_key = ENGINE_load_private_key(conn->engine, + key_id, NULL, NULL); + if (!conn->private_key) { + wpa_printf(MSG_ERROR, "ENGINE: cannot load private key with id" + " '%s' [%s]", key_id, + ERR_error_string(ERR_get_error(), NULL)); + ret = TLS_SET_PARAMS_ENGINE_PRV_INIT_FAILED; + goto err; + } + return 0; + +err: + if (conn->engine) { + ENGINE_free(conn->engine); + conn->engine = NULL; + } + + if (conn->private_key) { + EVP_PKEY_free(conn->private_key); + conn->private_key = NULL; + } + + return ret; +#else /* OPENSSL_NO_ENGINE */ + return 0; +#endif /* OPENSSL_NO_ENGINE */ +} + + +static void tls_engine_deinit(struct tls_connection *conn) +{ +#ifndef OPENSSL_NO_ENGINE + wpa_printf(MSG_DEBUG, "ENGINE: engine deinit"); + if (conn->private_key) { + EVP_PKEY_free(conn->private_key); + conn->private_key = NULL; + } + if (conn->engine) { + ENGINE_finish(conn->engine); + conn->engine = NULL; + } +#endif /* OPENSSL_NO_ENGINE */ } @@ -119,22 +859,21 @@ struct tls_connection * tls_connection_init(void *ssl_ctx) memset(conn, 0, sizeof(*conn)); conn->ssl = SSL_new(ssl); if (conn->ssl == NULL) { - wpa_printf(MSG_INFO, "TLS: Failed to initialize new SSL " - "connection: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Failed to initialize new SSL connection"); free(conn); return NULL; } + SSL_set_app_data(conn->ssl, conn); SSL_set_options(conn->ssl, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 | SSL_OP_SINGLE_DH_USE); conn->ssl_in = BIO_new(BIO_s_mem()); if (!conn->ssl_in) { - wpa_printf(MSG_INFO, "SSL: Failed to create a new BIO for " - "ssl_in: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Failed to create a new BIO for ssl_in"); SSL_free(conn->ssl); free(conn); return NULL; @@ -142,9 +881,8 @@ struct tls_connection * tls_connection_init(void *ssl_ctx) conn->ssl_out = BIO_new(BIO_s_mem()); if (!conn->ssl_out) { - wpa_printf(MSG_INFO, "SSL: Failed to create a new BIO for " - "ssl_out: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Failed to create a new BIO for ssl_out"); SSL_free(conn->ssl); BIO_free(conn->ssl_in); free(conn); @@ -161,8 +899,11 @@ void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) { if (conn == NULL) return; + free(conn->pre_shared_secret); SSL_free(conn->ssl); + tls_engine_deinit(conn); free(conn->subject_match); + free(conn->altsubject_match); free(conn); } @@ -187,6 +928,55 @@ int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) } +static int tls_match_altsubject(X509 *cert, const char *match) +{ + GENERAL_NAME *gen; + char *field, *tmp; + void *ext; + int i, found = 0; + size_t len; + + ext = X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); + + for (i = 0; ext && i < sk_GENERAL_NAME_num(ext); i++) { + gen = sk_GENERAL_NAME_value(ext, i); + switch (gen->type) { + case GEN_EMAIL: + field = "EMAIL"; + break; + case GEN_DNS: + field = "DNS"; + break; + case GEN_URI: + field = "URI"; + break; + default: + field = NULL; + wpa_printf(MSG_DEBUG, "TLS: altSubjectName: " + "unsupported type=%d", gen->type); + break; + } + + if (!field) + continue; + + wpa_printf(MSG_DEBUG, "TLS: altSubjectName: %s:%s", + field, gen->d.ia5->data); + len = strlen(field) + 1 + strlen((char *) gen->d.ia5->data) + + 1; + tmp = malloc(len); + if (tmp == NULL) + continue; + snprintf(tmp, len, "%s:%s", field, gen->d.ia5->data); + if (strstr(tmp, match)) + found++; + free(tmp); + } + + return found; +} + + static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) { char buf[256]; @@ -194,17 +984,18 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) int err, depth; SSL *ssl; struct tls_connection *conn; - char *match; + char *match, *altmatch; err_cert = X509_STORE_CTX_get_current_cert(x509_ctx); err = X509_STORE_CTX_get_error(x509_ctx); depth = X509_STORE_CTX_get_error_depth(x509_ctx); ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); - X509_NAME_oneline(X509_get_subject_name(err_cert), buf, 256); + X509_NAME_oneline(X509_get_subject_name(err_cert), buf, sizeof(buf)); conn = SSL_get_app_data(ssl); match = conn ? conn->subject_match : NULL; + altmatch = conn ? conn->altsubject_match : NULL; if (!preverify_ok) { wpa_printf(MSG_WARNING, "TLS: Certificate verification failed," @@ -219,6 +1010,11 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not " "match with '%s'", buf, match); preverify_ok = 0; + } else if (depth == 0 && altmatch && + !tls_match_altsubject(err_cert, altmatch)) { + wpa_printf(MSG_WARNING, "TLS: altSubjectName match " + "'%s' not found", altmatch); + preverify_ok = 0; } } @@ -226,34 +1022,102 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) } -int tls_connection_ca_cert(void *ssl_ctx, struct tls_connection *conn, - const char *ca_cert, const char *subject_match) +#ifndef OPENSSL_NO_STDIO +static int tls_load_ca_der(void *_ssl_ctx, const char *ca_cert) { - if (conn == NULL) + SSL_CTX *ssl_ctx = _ssl_ctx; + X509_LOOKUP *lookup; + int ret = 0; + + lookup = X509_STORE_add_lookup(ssl_ctx->cert_store, + X509_LOOKUP_file()); + if (lookup == NULL) { + tls_show_errors(MSG_WARNING, __func__, + "Failed add lookup for X509 store"); return -1; + } - free(conn->subject_match); - conn->subject_match = NULL; - if (subject_match) { - conn->subject_match = strdup(subject_match); - if (conn->subject_match == NULL) - return -1; + if (!X509_LOOKUP_load_file(lookup, ca_cert, X509_FILETYPE_ASN1)) { + unsigned long err = ERR_peek_error(); + tls_show_errors(MSG_WARNING, __func__, + "Failed load CA in DER format"); + if (ERR_GET_LIB(err) == ERR_LIB_X509 && + ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE) { + wpa_printf(MSG_DEBUG, "OpenSSL: %s - ignoring " + "cert already in hash table error", + __func__); + } else + ret = -1; } - if (ca_cert) { - if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, NULL) != 1) - { - wpa_printf(MSG_WARNING, "TLS: Failed to load root " - "certificates: %s", - ERR_error_string(ERR_get_error(), NULL)); + return ret; +} +#endif /* OPENSSL_NO_STDIO */ + + +static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, + const char *ca_cert, const u8 *ca_cert_blob, + size_t ca_cert_blob_len, const char *ca_path) +{ + SSL_CTX *ssl_ctx = _ssl_ctx; + + if (ca_cert_blob) { + X509 *cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &ca_cert_blob, + ca_cert_blob_len); + if (cert == NULL) { + tls_show_errors(MSG_WARNING, __func__, + "Failed to parse ca_cert_blob"); + return -1; + } + + if (!X509_STORE_add_cert(ssl_ctx->cert_store, cert)) { + tls_show_errors(MSG_WARNING, __func__, + "Failed to add ca_cert_blob to " + "certificate store"); + X509_free(cert); return -1; + } + X509_free(cert); + wpa_printf(MSG_DEBUG, "OpenSSL: %s - added ca_cert_blob " + "to certificate store", __func__); + SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); + return 0; + } + +#ifdef CONFIG_NATIVE_WINDOWS + if (ca_cert && tls_cryptoapi_ca_cert(ssl_ctx, conn->ssl, ca_cert) == + 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: Added CA certificates from " + "system certificate store"); + SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); + return 0; + } +#endif /* CONFIG_NATIVE_WINDOWS */ + + if (ca_cert || ca_path) { +#ifndef OPENSSL_NO_STDIO + if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, ca_path) != + 1) { + tls_show_errors(MSG_WARNING, __func__, + "Failed to load root certificates"); + if (ca_cert && + tls_load_ca_der(ssl_ctx, ca_cert) == 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: %s - loaded " + "DER format CA certificate", + __func__); + } else + return -1; } else { wpa_printf(MSG_DEBUG, "TLS: Trusted root " "certificate(s) loaded"); tls_get_errors(ssl_ctx); } - SSL_set_app_data(conn->ssl, conn); SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); +#else /* OPENSSL_NO_STDIO */ + wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", + __func__); + return -1; +#endif /* OPENSSL_NO_STDIO */ } else { /* No ca_cert configured - do not try to verify server * certificate */ @@ -270,26 +1134,51 @@ int tls_global_ca_cert(void *_ssl_ctx, const char *ca_cert) if (ca_cert) { if (SSL_CTX_load_verify_locations(ssl_ctx, ca_cert, NULL) != 1) { - wpa_printf(MSG_WARNING, "TLS: Failed to load root " - "certificates: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_WARNING, __func__, + "Failed to load root certificates"); return -1; - } else { - wpa_printf(MSG_DEBUG, "TLS: Trusted root " - "certificate(s) loaded"); } + + wpa_printf(MSG_DEBUG, "TLS: Trusted root " + "certificate(s) loaded"); + +#ifndef OPENSSL_NO_STDIO + /* Add the same CAs to the client certificate requests */ + SSL_CTX_set_client_CA_list(ssl_ctx, + SSL_load_client_CA_file(ca_cert)); +#endif /* OPENSSL_NO_STDIO */ } return 0; } -int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, - int verify_peer, const char *subject_match) +int tls_global_set_verify(void *ssl_ctx, int check_crl) { - if (conn == NULL) - return -1; + int flags; + + if (check_crl) { + X509_STORE *cs = SSL_CTX_get_cert_store(ssl_ctx); + if (cs == NULL) { + tls_show_errors(MSG_INFO, __func__, "Failed to get " + "certificate store when enabling " + "check_crl"); + return -1; + } + flags = X509_V_FLAG_CRL_CHECK; + if (check_crl == 2) + flags |= X509_V_FLAG_CRL_CHECK_ALL; + X509_STORE_set_flags(cs, flags); + } + return 0; +} + +static int tls_connection_set_subject_match(void *ssl_ctx, + struct tls_connection *conn, + const char *subject_match, + const char *altsubject_match) +{ free(conn->subject_match); conn->subject_match = NULL; if (subject_match) { @@ -298,8 +1187,25 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, return -1; } + free(conn->altsubject_match); + conn->altsubject_match = NULL; + if (altsubject_match) { + conn->altsubject_match = strdup(altsubject_match); + if (conn->altsubject_match == NULL) + return -1; + } + + return 0; +} + + +int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, + int verify_peer) +{ + if (conn == NULL) + return -1; + if (verify_peer) { - SSL_set_app_data(conn->ssl, conn); SSL_set_verify(conn->ssl, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT | SSL_VERIFY_CLIENT_ONCE, tls_verify_cb); @@ -313,29 +1219,60 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, } -int tls_connection_client_cert(void *ssl_ctx, struct tls_connection *conn, - const char *client_cert) +static int tls_connection_client_cert(void *ssl_ctx, + struct tls_connection *conn, + const char *client_cert, + const u8 *client_cert_blob, + size_t client_cert_blob_len) { - if (client_cert == NULL) + if (client_cert == NULL && client_cert_blob == NULL) return 0; - if (conn == NULL) + + if (client_cert_blob && + SSL_use_certificate_ASN1(conn->ssl, (u8 *) client_cert_blob, + client_cert_blob_len) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_ASN1 --> " + "OK"); + return 0; + } else if (client_cert_blob) { + tls_show_errors(MSG_DEBUG, __func__, + "SSL_use_certificate_ASN1 failed"); + } + + if (client_cert == NULL) return -1; +#ifndef OPENSSL_NO_STDIO if (SSL_use_certificate_file(conn->ssl, client_cert, - SSL_FILETYPE_ASN1) != 1 && - SSL_use_certificate_file(conn->ssl, client_cert, - SSL_FILETYPE_PEM) != 1) { - wpa_printf(MSG_INFO, "TLS: Failed to load client " - "certificate: %s", - ERR_error_string(ERR_get_error(), NULL)); - return -1; + SSL_FILETYPE_ASN1) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_file (DER)" + " --> OK"); + return 0; + } else { + tls_show_errors(MSG_DEBUG, __func__, + "SSL_use_certificate_file (DER) failed"); } - return 0; + + if (SSL_use_certificate_file(conn->ssl, client_cert, + SSL_FILETYPE_PEM) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_file (PEM)" + " --> OK"); + return 0; + } else { + tls_show_errors(MSG_DEBUG, __func__, + "SSL_use_certificate_file (PEM) failed"); + } +#else /* OPENSSL_NO_STDIO */ + wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__); +#endif /* OPENSSL_NO_STDIO */ + + return -1; } int tls_global_client_cert(void *_ssl_ctx, const char *client_cert) { +#ifndef OPENSSL_NO_STDIO SSL_CTX *ssl_ctx = _ssl_ctx; if (client_cert == NULL) return 0; @@ -344,12 +1281,17 @@ int tls_global_client_cert(void *_ssl_ctx, const char *client_cert) SSL_FILETYPE_ASN1) != 1 && SSL_CTX_use_certificate_file(ssl_ctx, client_cert, SSL_FILETYPE_PEM) != 1) { - wpa_printf(MSG_INFO, "TLS: Failed to load client " - "certificate: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Failed to load client certificate"); return -1; } return 0; +#else /* OPENSSL_NO_STDIO */ + if (client_cert == NULL) + return 0; + wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", __func__); + return -1; +#endif /* OPENSSL_NO_STDIO */ } @@ -364,42 +1306,31 @@ static int tls_passwd_cb(char *buf, int size, int rwflag, void *password) } -static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key, - const char *passwd) -{ #ifdef PKCS12_FUNCS - FILE *f; - PKCS12 *p12; +static int tls_parse_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, PKCS12 *p12, + const char *passwd) +{ EVP_PKEY *pkey; X509 *cert; + STACK_OF(X509) *certs; int res = 0; - - f = fopen(private_key, "r"); - if (f == NULL) - return -1; - - p12 = d2i_PKCS12_fp(f, NULL); - if (p12 == NULL) { - wpa_printf(MSG_DEBUG, "TLS: Failed to read PKCS12 file '%s'", - private_key); - fclose(f); - return -1; - } - fclose(f); + char buf[256]; pkey = NULL; cert = NULL; - if (!PKCS12_parse(p12, passwd, &pkey, &cert, NULL)) { - wpa_printf(MSG_DEBUG, "TLS: Failed to parse PKCS12 file '%s': " - "%s", private_key, - ERR_error_string(ERR_get_error(), NULL)); + certs = NULL; + if (!PKCS12_parse(p12, passwd, &pkey, &cert, &certs)) { + tls_show_errors(MSG_DEBUG, __func__, + "Failed to parse PKCS12 file"); return -1; } - wpa_printf(MSG_DEBUG, "TLS: Successfully parsed PKCS12 file '%s'", - private_key); + wpa_printf(MSG_DEBUG, "TLS: Successfully parsed PKCS12 data"); if (cert) { - wpa_printf(MSG_DEBUG, "TLS: Got certificate from PKCS12"); + X509_NAME_oneline(X509_get_subject_name(cert), buf, + sizeof(buf)); + wpa_printf(MSG_DEBUG, "TLS: Got certificate from PKCS12: " + "subject='%s'", buf); if (ssl) { if (SSL_use_certificate(ssl, cert) != 1) res = -1; @@ -422,9 +1353,56 @@ static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key, EVP_PKEY_free(pkey); } + if (certs) { + while ((cert = sk_X509_pop(certs)) != NULL) { + X509_NAME_oneline(X509_get_subject_name(cert), buf, + sizeof(buf)); + wpa_printf(MSG_DEBUG, "TLS: additional certificate" + " from PKCS12: subject='%s'", buf); + /* + * There is no SSL equivalent for the chain cert - so + * always add it to the context... + */ + if (SSL_CTX_add_extra_chain_cert(ssl_ctx, cert) != 1) { + res = -1; + break; + } + } + sk_X509_free(certs); + } + PKCS12_free(p12); + if (res < 0) + tls_get_errors(ssl_ctx); + return res; +} +#endif /* PKCS12_FUNCS */ + + +static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key, + const char *passwd) +{ +#ifdef PKCS12_FUNCS + FILE *f; + PKCS12 *p12; + + f = fopen(private_key, "r"); + if (f == NULL) + return -1; + + p12 = d2i_PKCS12_fp(f, NULL); + fclose(f); + + if (p12 == NULL) { + tls_show_errors(MSG_INFO, __func__, + "Failed to use PKCS#12 file"); + return -1; + } + + return tls_parse_pkcs12(ssl_ctx, ssl, p12, passwd); + #else /* PKCS12_FUNCS */ wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot read " "p12/pfx files"); @@ -433,17 +1411,65 @@ static int tls_read_pkcs12(SSL_CTX *ssl_ctx, SSL *ssl, const char *private_key, } -int tls_connection_private_key(void *_ssl_ctx, struct tls_connection *conn, - const char *private_key, - const char *private_key_passwd) +static int tls_read_pkcs12_blob(SSL_CTX *ssl_ctx, SSL *ssl, + const u8 *blob, size_t len, const char *passwd) +{ +#ifdef PKCS12_FUNCS + PKCS12 *p12; + + p12 = d2i_PKCS12(NULL, (OPENSSL_d2i_TYPE) &blob, len); + if (p12 == NULL) { + tls_show_errors(MSG_INFO, __func__, + "Failed to use PKCS#12 blob"); + return -1; + } + + return tls_parse_pkcs12(ssl_ctx, ssl, p12, passwd); + +#else /* PKCS12_FUNCS */ + wpa_printf(MSG_INFO, "TLS: PKCS12 support disabled - cannot parse " + "p12/pfx blobs"); + return -1; +#endif /* PKCS12_FUNCS */ +} + + +static int tls_connection_engine_private_key(void *_ssl_ctx, + struct tls_connection *conn) +{ +#ifndef OPENSSL_NO_ENGINE + if (SSL_use_PrivateKey(conn->ssl, conn->private_key) != 1) { + tls_show_errors(MSG_ERROR, __func__, + "ENGINE: cannot use private key for TLS"); + return -1; + } + if (!SSL_check_private_key(conn->ssl)) { + tls_show_errors(MSG_INFO, __func__, + "Private key failed verification"); + return -1; + } + return 0; +#else /* OPENSSL_NO_ENGINE */ + wpa_printf(MSG_ERROR, "SSL: Configuration uses engine, but " + "engine support was not compiled in"); + return -1; +#endif /* OPENSSL_NO_ENGINE */ +} + + +static int tls_connection_private_key(void *_ssl_ctx, + struct tls_connection *conn, + const char *private_key, + const char *private_key_passwd, + const u8 *private_key_blob, + size_t private_key_blob_len) { SSL_CTX *ssl_ctx = _ssl_ctx; char *passwd; + int ok; - if (private_key == NULL) + if (private_key == NULL && private_key_blob == NULL) return 0; - if (conn == NULL) - return -1; if (private_key_passwd) { passwd = strdup(private_key_passwd); @@ -454,28 +1480,123 @@ int tls_connection_private_key(void *_ssl_ctx, struct tls_connection *conn, SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb); SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd); - if (SSL_use_PrivateKey_file(conn->ssl, private_key, - SSL_FILETYPE_ASN1) != 1 && - SSL_use_PrivateKey_file(conn->ssl, private_key, - SSL_FILETYPE_PEM) != 1 && - tls_read_pkcs12(ssl_ctx, conn->ssl, private_key, passwd)) { - wpa_printf(MSG_INFO, "SSL: Failed to load private key: %s", - ERR_error_string(ERR_get_error(), NULL)); + + ok = 0; + while (private_key_blob) { + if (SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA, conn->ssl, + (u8 *) private_key_blob, + private_key_blob_len) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_PrivateKey_" + "ASN1(EVP_PKEY_RSA) --> OK"); + ok = 1; + break; + } else { + tls_show_errors(MSG_DEBUG, __func__, + "SSL_use_PrivateKey_ASN1(EVP_PKEY_RSA)" + " failed"); + } + + if (SSL_use_PrivateKey_ASN1(EVP_PKEY_DSA, conn->ssl, + (u8 *) private_key_blob, + private_key_blob_len) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_PrivateKey_" + "ASN1(EVP_PKEY_DSA) --> OK"); + ok = 1; + break; + } else { + tls_show_errors(MSG_DEBUG, __func__, + "SSL_use_PrivateKey_ASN1(EVP_PKEY_DSA)" + " failed"); + } + + if (SSL_use_RSAPrivateKey_ASN1(conn->ssl, + (u8 *) private_key_blob, + private_key_blob_len) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: " + "SSL_use_RSAPrivateKey_ASN1 --> OK"); + ok = 1; + break; + } else { + tls_show_errors(MSG_DEBUG, __func__, + "SSL_use_RSAPrivateKey_ASN1 failed"); + } + + if (tls_read_pkcs12_blob(ssl_ctx, conn->ssl, private_key_blob, + private_key_blob_len, passwd) == 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: PKCS#12 as blob --> " + "OK"); + ok = 1; + break; + } + + break; + } + + while (!ok && private_key) { +#ifndef OPENSSL_NO_STDIO + if (SSL_use_PrivateKey_file(conn->ssl, private_key, + SSL_FILETYPE_ASN1) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: " + "SSL_use_PrivateKey_File (DER) --> OK"); + ok = 1; + break; + } else { + tls_show_errors(MSG_DEBUG, __func__, + "SSL_use_PrivateKey_File (DER) " + "failed"); + } + + if (SSL_use_PrivateKey_file(conn->ssl, private_key, + SSL_FILETYPE_PEM) == 1) { + wpa_printf(MSG_DEBUG, "OpenSSL: " + "SSL_use_PrivateKey_File (PEM) --> OK"); + ok = 1; + break; + } else { + tls_show_errors(MSG_DEBUG, __func__, + "SSL_use_PrivateKey_File (PEM) " + "failed"); + } +#else /* OPENSSL_NO_STDIO */ + wpa_printf(MSG_DEBUG, "OpenSSL: %s - OPENSSL_NO_STDIO", + __func__); +#endif /* OPENSSL_NO_STDIO */ + + if (tls_read_pkcs12(ssl_ctx, conn->ssl, private_key, passwd) + == 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: Reading PKCS#12 file " + "--> OK"); + ok = 1; + break; + } + + if (tls_cryptoapi_cert(conn->ssl, private_key) == 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: Using CryptoAPI to " + "access certificate store --> OK"); + ok = 1; + break; + } + + break; + } + + if (!ok) { + wpa_printf(MSG_INFO, "OpenSSL: Failed to load private key"); free(passwd); ERR_clear_error(); return -1; } ERR_clear_error(); - free(passwd); SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL); + free(passwd); if (!SSL_check_private_key(conn->ssl)) { - wpa_printf(MSG_INFO, "SSL: Private key failed " - "verification: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, "Private key failed " + "verification"); return -1; } + wpa_printf(MSG_DEBUG, "SSL: Private key loaded successfully"); return 0; } @@ -498,13 +1619,16 @@ int tls_global_private_key(void *_ssl_ctx, const char *private_key, SSL_CTX_set_default_passwd_cb(ssl_ctx, tls_passwd_cb); SSL_CTX_set_default_passwd_cb_userdata(ssl_ctx, passwd); - if (SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key, + if ( +#ifndef OPENSSL_NO_STDIO + SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key, SSL_FILETYPE_ASN1) != 1 && SSL_CTX_use_PrivateKey_file(ssl_ctx, private_key, SSL_FILETYPE_PEM) != 1 && +#endif /* OPENSSL_NO_STDIO */ tls_read_pkcs12(ssl_ctx, NULL, private_key, passwd)) { - wpa_printf(MSG_INFO, "SSL: Failed to load private key: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Failed to load private key"); free(passwd); ERR_clear_error(); return -1; @@ -514,9 +1638,8 @@ int tls_global_private_key(void *_ssl_ctx, const char *private_key, SSL_CTX_set_default_passwd_cb(ssl_ctx, NULL); if (!SSL_CTX_check_private_key(ssl_ctx)) { - wpa_printf(MSG_INFO, "SSL: Private key failed " - "verification: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Private key failed verification"); return -1; } @@ -524,7 +1647,7 @@ int tls_global_private_key(void *_ssl_ctx, const char *private_key, } -int tls_connection_dh(void *ssl_ctx, struct tls_connection *conn, +static int tls_connection_dh(void *ssl_ctx, struct tls_connection *conn, const char *dh_file) { #ifdef OPENSSL_NO_DH @@ -537,6 +1660,7 @@ int tls_connection_dh(void *ssl_ctx, struct tls_connection *conn, DH *dh; BIO *bio; + /* TODO: add support for dh_blob */ if (dh_file == NULL) return 0; if (conn == NULL) @@ -609,6 +1733,7 @@ int tls_connection_get_keys(void *ssl_ctx, struct tls_connection *conn, if (ssl == NULL || ssl->s3 == NULL || ssl->session == NULL) return -1; + memset(keys, 0, sizeof(*keys)); keys->master_key = ssl->session->master_key; keys->master_key_len = ssl->session->master_key_length; keys->client_random = ssl->s3->client_random; @@ -627,13 +1752,18 @@ u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, int res; u8 *out_data; + /* + * Give TLS handshake data from the server (if available) to OpenSSL + * for processing. + */ if (in_data && BIO_write(conn->ssl_in, in_data, in_len) < 0) { - wpa_printf(MSG_INFO, "TLS: Handshake failed - BIO_write: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Handshake failed - BIO_write"); return NULL; } + /* Initiate TLS handshake or continue the existing handshake */ res = SSL_connect(conn->ssl); if (res != 1) { int err = SSL_get_error(conn->ssl, res); @@ -644,27 +1774,33 @@ u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, wpa_printf(MSG_DEBUG, "SSL: SSL_connect - want to " "write"); else { - wpa_printf(MSG_INFO, "SSL: SSL_connect: %s", - ERR_error_string(ERR_get_error(), NULL)); - return NULL; + tls_show_errors(MSG_INFO, __func__, "SSL_connect"); + conn->failed++; } } + /* Get the TLS handshake data to be sent to the server */ res = BIO_ctrl_pending(conn->ssl_out); wpa_printf(MSG_DEBUG, "SSL: %d bytes pending from ssl_out", res); out_data = malloc(res == 0 ? 1 : res); if (out_data == NULL) { wpa_printf(MSG_DEBUG, "SSL: Failed to allocate memory for " "handshake output (%d bytes)", res); - BIO_reset(conn->ssl_out); + if (BIO_reset(conn->ssl_out) < 0) { + tls_show_errors(MSG_INFO, __func__, + "BIO_reset failed"); + } *out_len = 0; return NULL; } res = res == 0 ? 0 : BIO_read(conn->ssl_out, out_data, res); if (res < 0) { - wpa_printf(MSG_INFO, "TLS: Handshake failed - BIO_read: %s", - ERR_error_string(ERR_get_error(), NULL)); - BIO_reset(conn->ssl_out); + tls_show_errors(MSG_INFO, __func__, + "Handshake failed - BIO_read"); + if (BIO_reset(conn->ssl_out) < 0) { + tls_show_errors(MSG_INFO, __func__, + "BIO_reset failed"); + } *out_len = 0; return NULL; } @@ -684,8 +1820,8 @@ u8 * tls_connection_server_handshake(void *ssl_ctx, if (in_data && BIO_write(conn->ssl_in, in_data, in_len) < 0) { - wpa_printf(MSG_INFO, "TLS: Handshake failed - BIO_write: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Handshake failed - BIO_write"); return NULL; } @@ -701,15 +1837,21 @@ u8 * tls_connection_server_handshake(void *ssl_ctx, if (out_data == NULL) { wpa_printf(MSG_DEBUG, "SSL: Failed to allocate memory for " "handshake output (%d bytes)", res); - BIO_reset(conn->ssl_out); + if (BIO_reset(conn->ssl_out) < 0) { + tls_show_errors(MSG_INFO, __func__, + "BIO_reset failed"); + } *out_len = 0; return NULL; } res = res == 0 ? 0 : BIO_read(conn->ssl_out, out_data, res); if (res < 0) { - wpa_printf(MSG_INFO, "TLS: Handshake failed - BIO_read: %s", - ERR_error_string(ERR_get_error(), NULL)); - BIO_reset(conn->ssl_out); + tls_show_errors(MSG_INFO, __func__, + "Handshake failed - BIO_read"); + if (BIO_reset(conn->ssl_out) < 0) { + tls_show_errors(MSG_INFO, __func__, + "BIO_reset failed"); + } *out_len = 0; return NULL; } @@ -719,7 +1861,7 @@ u8 * tls_connection_server_handshake(void *ssl_ctx, int tls_connection_encrypt(void *ssl_ctx, struct tls_connection *conn, - u8 *in_data, size_t in_len, + const u8 *in_data, size_t in_len, u8 *out_data, size_t out_len) { int res; @@ -727,19 +1869,24 @@ int tls_connection_encrypt(void *ssl_ctx, struct tls_connection *conn, if (conn == NULL) return -1; - BIO_reset(conn->ssl_in); - BIO_reset(conn->ssl_out); + /* Give plaintext data for OpenSSL to encrypt into the TLS tunnel. */ + if ((res = BIO_reset(conn->ssl_in)) < 0 || + (res = BIO_reset(conn->ssl_out)) < 0) { + tls_show_errors(MSG_INFO, __func__, "BIO_reset failed"); + return res; + } res = SSL_write(conn->ssl, in_data, in_len); if (res < 0) { - wpa_printf(MSG_INFO, "TLS: Encryption failed - SSL_write: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Encryption failed - SSL_write"); return res; } + /* Read encrypted data to be sent to the server */ res = BIO_read(conn->ssl_out, out_data, out_len); if (res < 0) { - wpa_printf(MSG_INFO, "TLS: Encryption failed - BIO_read: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Encryption failed - BIO_read"); return res; } @@ -748,23 +1895,28 @@ int tls_connection_encrypt(void *ssl_ctx, struct tls_connection *conn, int tls_connection_decrypt(void *ssl_ctx, struct tls_connection *conn, - u8 *in_data, size_t in_len, + const u8 *in_data, size_t in_len, u8 *out_data, size_t out_len) { int res; + /* Give encrypted data from TLS tunnel for OpenSSL to decrypt. */ res = BIO_write(conn->ssl_in, in_data, in_len); if (res < 0) { - wpa_printf(MSG_INFO, "TLS: Decryption failed - BIO_write: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Decryption failed - BIO_write"); + return res; + } + if (BIO_reset(conn->ssl_out) < 0) { + tls_show_errors(MSG_INFO, __func__, "BIO_reset failed"); return res; } - BIO_reset(conn->ssl_out); + /* Read decrypted data for further processing */ res = SSL_read(conn->ssl, out_data, out_len); if (res < 0) { - wpa_printf(MSG_INFO, "TLS: Decryption failed - SSL_read: %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Decryption failed - SSL_read"); return res; } @@ -778,22 +1930,54 @@ int tls_connection_resumed(void *ssl_ctx, struct tls_connection *conn) } +#ifdef EAP_FAST +/* Pre-shared secred requires a patch to openssl, so this function is + * commented out unless explicitly needed for EAP-FAST in order to be able to + * build this file with unmodified openssl. */ + +static int tls_sess_sec_cb(SSL *s, void *secret, int *secret_len, + STACK_OF(SSL_CIPHER) *peer_ciphers, + SSL_CIPHER **cipher, void *arg) +{ + struct tls_connection *conn = arg; + + if (conn == NULL || conn->pre_shared_secret == 0) + return 0; + + memcpy(secret, conn->pre_shared_secret, conn->pre_shared_secret_len); + *secret_len = conn->pre_shared_secret_len; + + return 1; +} + + int tls_connection_set_master_key(void *ssl_ctx, struct tls_connection *conn, const u8 *key, size_t key_len) { - SSL *ssl; - - if (conn == NULL || key == NULL || key_len > SSL_MAX_MASTER_KEY_LENGTH) - return -1; - ssl = conn->ssl; - if (ssl == NULL || ssl->session == NULL) + if (conn == NULL || key_len > SSL_MAX_MASTER_KEY_LENGTH) return -1; - memcpy(ssl->session->master_key, key, key_len); - ssl->session->master_key_length = key_len; + free(conn->pre_shared_secret); + conn->pre_shared_secret = NULL; + conn->pre_shared_secret_len = 0; + + if (key) { + conn->pre_shared_secret = malloc(key_len); + if (conn->pre_shared_secret) { + memcpy(conn->pre_shared_secret, key, key_len); + conn->pre_shared_secret_len = key_len; + } + if (SSL_set_session_secret_cb(conn->ssl, tls_sess_sec_cb, + conn) != 1) + return -1; + } else { + if (SSL_set_session_secret_cb(conn->ssl, NULL, NULL) != 1) + return -1; + } return 0; } +#endif /* EAP_FAST */ int tls_connection_set_anon_dh(void *ssl_ctx, struct tls_connection *conn) @@ -802,8 +1986,8 @@ int tls_connection_set_anon_dh(void *ssl_ctx, struct tls_connection *conn) return -1; if (SSL_set_cipher_list(conn->ssl, "ADH-AES128-SHA") != 1) { - wpa_printf(MSG_INFO, "TLS: Anon DH configuration failed - %s", - ERR_error_string(ERR_get_error(), NULL)); + tls_show_errors(MSG_INFO, __func__, + "Anon DH configuration failed"); return -1; } @@ -844,36 +2028,116 @@ int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, int ext_type, const u8 *data, size_t data_len) { - struct tls_ext_hdr { - u16 extensions_len; - u16 extension_type; - u16 extension_len; - } *hdr; - if (conn == NULL || conn->ssl == NULL) return -1; - OPENSSL_free(conn->ssl->hello_extension); - if (data == NULL) { - conn->ssl->hello_extension = NULL; - conn->ssl->hello_extension_len = 0; - return 0; + + if (SSL_set_hello_extension(conn->ssl, ext_type, (void *) data, + data_len) != 1) + return -1; + + return 0; +} +#endif /* EAP_FAST */ + + +int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + return conn->failed; +} + + +int tls_connection_get_read_alerts(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + return conn->read_alerts; +} + + +int tls_connection_get_write_alerts(void *ssl_ctx, struct tls_connection *conn) +{ + if (conn == NULL) + return -1; + return conn->write_alerts; +} + + +int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, + const struct tls_connection_params *params) +{ + int ret; + unsigned long err; + + if (conn == NULL) + return -1; + + while ((err = ERR_get_error())) { + wpa_printf(MSG_INFO, "%s: Clearing pending SSL error: %s", + __func__, ERR_error_string(err, NULL)); } - if (data_len == 0) { - conn->ssl->hello_extension = OPENSSL_malloc(1); - conn->ssl->hello_extension_len = 0; - return 0; + + if (tls_connection_set_subject_match(tls_ctx, conn, + params->subject_match, + params->altsubject_match)) + return -1; + if (tls_connection_ca_cert(tls_ctx, conn, params->ca_cert, + params->ca_cert_blob, + params->ca_cert_blob_len, + params->ca_path)) + return -1; + if (tls_connection_client_cert(tls_ctx, conn, params->client_cert, + params->client_cert_blob, + params->client_cert_blob_len)) + return -1; + + if (params->engine) { + wpa_printf(MSG_DEBUG, "SSL: Initializing TLS engine"); + ret = tls_engine_init(conn, params->engine_id, params->pin, + params->key_id); + if (ret) + return ret; + if (tls_connection_engine_private_key(tls_ctx, conn)) + return TLS_SET_PARAMS_ENGINE_PRV_VERIFY_FAILED; + } else if (tls_connection_private_key(tls_ctx, conn, + params->private_key, + params->private_key_passwd, + params->private_key_blob, + params->private_key_blob_len)) { + wpa_printf(MSG_INFO, "TLS: Failed to load private key '%s'", + params->private_key); + return -1; } - conn->ssl->hello_extension = OPENSSL_malloc(sizeof(*hdr) + data_len); - if (conn->ssl->hello_extension == NULL) + + if (tls_connection_dh(tls_ctx, conn, params->dh_file)) { + wpa_printf(MSG_INFO, "TLS: Failed to load DH file '%s'", + params->dh_file); return -1; + } - hdr = (struct tls_ext_hdr *) conn->ssl->hello_extension; - hdr->extensions_len = host_to_be16(sizeof(*hdr) - 2 + data_len); - hdr->extension_type = host_to_be16(ext_type); - hdr->extension_len = host_to_be16(data_len); - memcpy(hdr + 1, data, data_len); - conn->ssl->hello_extension_len = sizeof(*hdr) + data_len; + tls_get_errors(tls_ctx); return 0; } -#endif /* EAP_FAST */ + + +int tls_connection_get_keyblock_size(void *tls_ctx, + struct tls_connection *conn) +{ + const EVP_CIPHER *c; + const EVP_MD *h; + + if (conn == NULL || conn->ssl == NULL || + conn->ssl->enc_read_ctx == NULL || + conn->ssl->enc_read_ctx->cipher == NULL || + conn->ssl->read_hash == NULL) + return -1; + + c = conn->ssl->enc_read_ctx->cipher; + h = conn->ssl->read_hash; + + return 2 * (EVP_CIPHER_key_length(c) + + EVP_MD_size(h) + + EVP_CIPHER_iv_length(c)); +} diff --git a/contrib/hostapd/version.h b/contrib/hostapd/version.h index b030f34..8f8eff8 100644 --- a/contrib/hostapd/version.h +++ b/contrib/hostapd/version.h @@ -1,6 +1,6 @@ #ifndef VERSION_H #define VERSION_H -#define VERSION_STR "0.3.9" +#define VERSION_STR "0.4.8" #endif /* VERSION_H */ diff --git a/contrib/hostapd/wired.conf b/contrib/hostapd/wired.conf index d1f3c4e..956f8c5 100644 --- a/contrib/hostapd/wired.conf +++ b/contrib/hostapd/wired.conf @@ -14,6 +14,8 @@ dump_file=/tmp/hostapd.dump ieee8021x=1 eap_reauth_period=3600 +use_pae_group_addr=1 + ##### RADIUS configuration #################################################### # for IEEE 802.1X with external Authentication Server, IEEE 802.11 diff --git a/contrib/hostapd/wpa.c b/contrib/hostapd/wpa.c index b0e42c2..4bac473 100644 --- a/contrib/hostapd/wpa.c +++ b/contrib/hostapd/wpa.c @@ -34,6 +34,8 @@ #include "eloop.h" #include "sta_info.h" #include "l2_packet.h" +#include "accounting.h" +#include "hostap_common.h" static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx); @@ -101,7 +103,7 @@ static const u8 RSN_KEY_DATA_PMKID[] = { 0x00, 0x0f, 0xac, 4 }; * Authenticated Key Management Suite Count (2 octets, little endian) * (default: 1) * Authenticated Key Management Suite List (4 * n octets) - * (default: unspec 802.1x) + * (default: unspec 802.1X) * WPA Capabilities (2 octets, little endian) (default: 0) */ @@ -123,7 +125,7 @@ struct wpa_ie_hdr { * Authenticated Key Management Suite Count (2 octets, little endian) * (default: 1) * Authenticated Key Management Suite List (4 * n octets) - * (default: unspec 802.1x) + * (default: unspec 802.1X) * RSN Capabilities (2 octets, little endian) (default: 0) * PMKID Count (2 octets) (default: 0) * PMKID List (16 * n octets) @@ -396,8 +398,8 @@ static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx) #ifdef CONFIG_RSN_PREAUTH -static void rsn_preauth_receive(void *ctx, unsigned char *src_addr, - unsigned char *buf, size_t len) +static void rsn_preauth_receive(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len) { struct rsn_preauth_interface *piface = ctx; struct hostapd_data *hapd = piface->hapd; @@ -480,13 +482,12 @@ static int rsn_preauth_iface_add(struct hostapd_data *hapd, const char *ifname) } piface->l2 = l2_packet_init(piface->ifname, NULL, ETH_P_PREAUTH, - rsn_preauth_receive, piface); + rsn_preauth_receive, piface, 1); if (piface->l2 == NULL) { printf("Failed to open register layer 2 access to " "ETH_P_PREAUTH\n"); goto fail2; } - l2_packet_set_rx_l2_hdr(piface->l2, 1); piface->next = hapd->preauth_iface; hapd->preauth_iface = piface; @@ -551,6 +552,16 @@ static int rsn_preauth_iface_init(struct hostapd_data *hapd) } +static void rsn_preauth_finished_cb(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + wpa_printf(MSG_DEBUG, "RSN: Removing pre-authentication STA entry for " + MACSTR, MAC2STR(sta->addr)); + ap_free_sta(hapd, sta); +} + + void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta, int success) { @@ -565,7 +576,11 @@ void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta, pmksa_cache_add(hapd, sta, key, dot11RSNAConfigPMKLifetime); } - ap_free_sta(hapd, sta); + /* + * Finish STA entry removal from timeout in order to avoid freeing + * STA data before the caller has finished processing. + */ + eloop_register_timeout(0, 0, rsn_preauth_finished_cb, hapd, sta); } @@ -598,8 +613,8 @@ void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta, ethhdr->h_proto = htons(ETH_P_PREAUTH); memcpy(ethhdr + 1, buf, len); - if (l2_packet_send(piface->l2, (u8 *) ethhdr, sizeof(*ethhdr) + len) < - 0) { + if (l2_packet_send(piface->l2, sta->addr, ETH_P_PREAUTH, (u8 *) ethhdr, + sizeof(*ethhdr) + len) < 0) { printf("Failed to send preauth packet using l2_packet_send\n"); } free(ethhdr); @@ -616,6 +631,10 @@ static inline void rsn_preauth_iface_deinit(struct hostapd_data *hapd) { } +static void rsn_preauth_finished_cb(void *eloop_ctx, void *timeout_ctx) +{ +} + void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta, int success) { @@ -807,6 +826,16 @@ static void rsn_pmkid(const u8 *pmk, const u8 *aa, const u8 *spa, u8 *pmkid) static void pmksa_cache_set_expiration(struct hostapd_data *hapd); +static void _pmksa_cache_free_entry(struct rsn_pmksa_cache *entry) +{ + if (entry == NULL) + return; + free(entry->identity); + ieee802_1x_free_radius_class(&entry->radius_class); + free(entry); +} + + static void pmksa_cache_free_entry(struct hostapd_data *hapd, struct rsn_pmksa_cache *entry) { @@ -846,7 +875,7 @@ static void pmksa_cache_free_entry(struct hostapd_data *hapd, prev = pos; pos = pos->next; } - free(entry); + _pmksa_cache_free_entry(entry); } @@ -882,6 +911,54 @@ static void pmksa_cache_set_expiration(struct hostapd_data *hapd) } +static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache *entry, + struct eapol_state_machine *eapol) +{ + if (eapol == NULL) + return; + + if (eapol->identity) { + entry->identity = malloc(eapol->identity_len); + if (entry->identity) { + entry->identity_len = eapol->identity_len; + memcpy(entry->identity, eapol->identity, + eapol->identity_len); + } + } + + ieee802_1x_copy_radius_class(&entry->radius_class, + &eapol->radius_class); +} + + +static void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache *entry, + struct eapol_state_machine *eapol) +{ + if (entry == NULL || eapol == NULL) + return; + + if (entry->identity) { + free(eapol->identity); + eapol->identity = malloc(entry->identity_len); + if (eapol->identity) { + eapol->identity_len = entry->identity_len; + memcpy(eapol->identity, entry->identity, + entry->identity_len); + } + wpa_hexdump_ascii(MSG_DEBUG, "STA identity from PMKSA", + eapol->identity, eapol->identity_len); + } + + ieee802_1x_free_radius_class(&eapol->radius_class); + ieee802_1x_copy_radius_class(&eapol->radius_class, + &entry->radius_class); + if (eapol->radius_class.attr) { + wpa_printf(MSG_DEBUG, "Copied %lu Class attribute(s) from " + "PMKSA", (unsigned long) eapol->radius_class.count); + } +} + + void pmksa_cache_add(struct hostapd_data *hapd, struct sta_info *sta, u8 *pmk, int session_timeout) { @@ -903,6 +980,7 @@ void pmksa_cache_add(struct hostapd_data *hapd, struct sta_info *sta, u8 *pmk, entry->expiration += dot11RSNAConfigPMKLifetime; entry->akmp = WPA_KEY_MGMT_IEEE8021X; memcpy(entry->spa, sta->addr, ETH_ALEN); + pmksa_cache_from_eapol_data(entry, sta->eapol_sm); /* Replace an old entry for the same STA (if found) with the new entry */ @@ -959,7 +1037,7 @@ static void pmksa_cache_free(struct hostapd_data *hapd) while (entry) { prev = entry; entry = entry->next; - free(prev); + _pmksa_cache_free_entry(prev); } eloop_cancel_timeout(pmksa_cache_expire, hapd, NULL); for (i = 0; i < PMKID_HASH_SIZE; i++) @@ -999,7 +1077,7 @@ struct wpa_ie_data { }; -static int wpa_parse_wpa_ie_wpa(u8 *wpa_ie, size_t wpa_ie_len, +static int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, struct wpa_ie_data *data) { struct wpa_ie_hdr *hdr; @@ -1078,7 +1156,7 @@ static int wpa_parse_wpa_ie_wpa(u8 *wpa_ie, size_t wpa_ie_len, } -static int wpa_parse_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, +static int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, struct wpa_ie_data *data) { struct rsn_ie_hdr *hdr; @@ -1172,7 +1250,7 @@ static int wpa_parse_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, int wpa_validate_wpa_ie(struct hostapd_data *hapd, struct sta_info *sta, - u8 *wpa_ie, size_t wpa_ie_len, int version) + const u8 *wpa_ie, size_t wpa_ie_len, int version) { struct wpa_ie_data data; int ciphers, key_mgmt, res, i; @@ -1372,6 +1450,7 @@ void wpa_free_station(struct sta_info *sta) eloop_cancel_timeout(wpa_send_eapol_timeout, sm->hapd, sta); eloop_cancel_timeout(wpa_sm_call_step, sm->hapd, sta->wpa_sm); + eloop_cancel_timeout(rsn_preauth_finished_cb, sm->hapd, sta); free(sm->last_rx_eapol_key); free(sm); sta->wpa_sm = NULL; @@ -2088,6 +2167,7 @@ SM_STATE(WPA_PTK, INITPMK) if (sm->sta->pmksa) { wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache"); memcpy(sm->PMK, sm->sta->pmksa->pmk, WPA_PMK_LEN); + pmksa_cache_to_eapol_data(sm->sta->pmksa, sm->sta->eapol_sm); } else if ((key = ieee802_1x_get_key_crypt(sm->sta->eapol_sm, &len))) { wpa_printf(MSG_DEBUG, "WPA: PMK from EAPOL state machine " "(len=%lu)", (unsigned long) len); @@ -2294,6 +2374,8 @@ SM_STATE(WPA_PTK, PTKINITDONE) HOSTAPD_LEVEL_INFO, "pairwise key handshake completed " "(%s)", sm->sta->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); + if (sm->sta->wpa_key_mgmt == WPA_KEY_MGMT_PSK) + accounting_sta_start(sm->hapd, sm->sta); } diff --git a/contrib/hostapd/wpa.h b/contrib/hostapd/wpa.h index 929e4a8..62159e7 100644 --- a/contrib/hostapd/wpa.h +++ b/contrib/hostapd/wpa.h @@ -16,6 +16,9 @@ struct rsn_pmksa_cache { time_t expiration; int akmp; /* WPA_KEY_MGMT_* */ u8 spa[ETH_ALEN]; + u8 *identity; + size_t identity_len; + struct radius_class_data radius_class; }; struct rsn_preauth_interface { @@ -167,7 +170,7 @@ enum { }; int wpa_validate_wpa_ie(struct hostapd_data *hapd, struct sta_info *sta, - u8 *wpa_ie, size_t wpa_ie_len, int version); + const u8 *wpa_ie, size_t wpa_ie_len, int version); void wpa_new_station(struct hostapd_data *hapd, struct sta_info *sta); void wpa_free_station(struct sta_info *sta); void wpa_receive(struct hostapd_data *hapd, struct sta_info *sta, diff --git a/contrib/hostapd/wpa_ctrl.c b/contrib/hostapd/wpa_ctrl.c new file mode 100644 index 0000000..98e0669 --- /dev/null +++ b/contrib/hostapd/wpa_ctrl.c @@ -0,0 +1,239 @@ +/* + * wpa_supplicant/hostapd control interface library + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <sys/types.h> +#include <sys/time.h> +#ifndef CONFIG_NATIVE_WINDOWS +#include <sys/socket.h> +#include <netinet/in.h> +#include <sys/un.h> +#endif /* CONFIG_NATIVE_WINDOWS */ + +#include "wpa_ctrl.h" +#ifdef CONFIG_NATIVE_WINDOWS +#include "common.h" +#endif /* CONFIG_NATIVE_WINDOWS */ + + +/** + * struct wpa_ctrl - Internal structure for control interface library + * + * This structure is used by the wpa_supplicant/hostapd control interface + * library to store internal data. Programs using the library should not touch + * this data directly. They can only use the pointer to the data structure as + * an identifier for the control interface connection and use this as one of + * the arguments for most of the control interface library functions. + */ +struct wpa_ctrl { + int s; +#ifdef CONFIG_CTRL_IFACE_UDP + struct sockaddr_in local; + struct sockaddr_in dest; +#else /* CONFIG_CTRL_IFACE_UDP */ + struct sockaddr_un local; + struct sockaddr_un dest; +#endif /* CONFIG_CTRL_IFACE_UDP */ +}; + + +struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path) +{ + struct wpa_ctrl *ctrl; +#ifndef CONFIG_CTRL_IFACE_UDP + static int counter = 0; +#endif /* CONFIG_CTRL_IFACE_UDP */ + + ctrl = malloc(sizeof(*ctrl)); + if (ctrl == NULL) + return NULL; + memset(ctrl, 0, sizeof(*ctrl)); + +#ifdef CONFIG_CTRL_IFACE_UDP + ctrl->s = socket(PF_INET, SOCK_DGRAM, 0); + if (ctrl->s < 0) { + perror("socket"); + free(ctrl); + return NULL; + } + + ctrl->local.sin_family = AF_INET; + ctrl->local.sin_addr.s_addr = htonl((127 << 24) | 1); + if (bind(ctrl->s, (struct sockaddr *) &ctrl->local, + sizeof(ctrl->local)) < 0) { + close(ctrl->s); + free(ctrl); + return NULL; + } + + ctrl->dest.sin_family = AF_INET; + ctrl->dest.sin_addr.s_addr = htonl((127 << 24) | 1); + ctrl->dest.sin_port = htons(WPA_CTRL_IFACE_PORT); + if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest, + sizeof(ctrl->dest)) < 0) { + perror("connect"); + close(ctrl->s); + free(ctrl); + return NULL; + } +#else /* CONFIG_CTRL_IFACE_UDP */ + ctrl->s = socket(PF_UNIX, SOCK_DGRAM, 0); + if (ctrl->s < 0) { + free(ctrl); + return NULL; + } + + ctrl->local.sun_family = AF_UNIX; + snprintf(ctrl->local.sun_path, sizeof(ctrl->local.sun_path), + "/tmp/wpa_ctrl_%d-%d", getpid(), counter++); + if (bind(ctrl->s, (struct sockaddr *) &ctrl->local, + sizeof(ctrl->local)) < 0) { + close(ctrl->s); + free(ctrl); + return NULL; + } + + ctrl->dest.sun_family = AF_UNIX; + snprintf(ctrl->dest.sun_path, sizeof(ctrl->dest.sun_path), "%s", + ctrl_path); + if (connect(ctrl->s, (struct sockaddr *) &ctrl->dest, + sizeof(ctrl->dest)) < 0) { + close(ctrl->s); + unlink(ctrl->local.sun_path); + free(ctrl); + return NULL; + } +#endif /* CONFIG_CTRL_IFACE_UDP */ + + return ctrl; +} + + +void wpa_ctrl_close(struct wpa_ctrl *ctrl) +{ +#ifndef CONFIG_CTRL_IFACE_UDP + unlink(ctrl->local.sun_path); +#endif /* CONFIG_CTRL_IFACE_UDP */ + close(ctrl->s); + free(ctrl); +} + + +int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, + char *reply, size_t *reply_len, + void (*msg_cb)(char *msg, size_t len)) +{ + struct timeval tv; + int res; + fd_set rfds; + + if (send(ctrl->s, cmd, cmd_len, 0) < 0) + return -1; + + for (;;) { + tv.tv_sec = 2; + tv.tv_usec = 0; + FD_ZERO(&rfds); + FD_SET(ctrl->s, &rfds); + res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv); + if (FD_ISSET(ctrl->s, &rfds)) { + res = recv(ctrl->s, reply, *reply_len, 0); + if (res < 0) + return res; + if (res > 0 && reply[0] == '<') { + /* This is an unsolicited message from + * wpa_supplicant, not the reply to the + * request. Use msg_cb to report this to the + * caller. */ + if (msg_cb) { + /* Make sure the message is nul + * terminated. */ + if ((size_t) res == *reply_len) + res = (*reply_len) - 1; + reply[res] = '\0'; + msg_cb(reply, res); + } + continue; + } + *reply_len = res; + break; + } else { + return -2; + } + } + return 0; +} + + +static int wpa_ctrl_attach_helper(struct wpa_ctrl *ctrl, int attach) +{ + char buf[10]; + int ret; + size_t len = 10; + + ret = wpa_ctrl_request(ctrl, attach ? "ATTACH" : "DETACH", 6, + buf, &len, NULL); + if (ret < 0) + return ret; + if (len == 3 && memcmp(buf, "OK\n", 3) == 0) + return 0; + return -1; +} + + +int wpa_ctrl_attach(struct wpa_ctrl *ctrl) +{ + return wpa_ctrl_attach_helper(ctrl, 1); +} + + +int wpa_ctrl_detach(struct wpa_ctrl *ctrl) +{ + return wpa_ctrl_attach_helper(ctrl, 0); +} + + +int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len) +{ + int res; + + res = recv(ctrl->s, reply, *reply_len, 0); + if (res < 0) + return res; + *reply_len = res; + return 0; +} + + +int wpa_ctrl_pending(struct wpa_ctrl *ctrl) +{ + struct timeval tv; + int res; + fd_set rfds; + tv.tv_sec = 0; + tv.tv_usec = 0; + FD_ZERO(&rfds); + FD_SET(ctrl->s, &rfds); + res = select(ctrl->s + 1, &rfds, NULL, NULL, &tv); + return FD_ISSET(ctrl->s, &rfds); +} + + +int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl) +{ + return ctrl->s; +} diff --git a/contrib/hostapd/wpa_ctrl.h b/contrib/hostapd/wpa_ctrl.h new file mode 100644 index 0000000..c8fa48d --- /dev/null +++ b/contrib/hostapd/wpa_ctrl.h @@ -0,0 +1,185 @@ +/* + * wpa_supplicant/hostapd control interface library + * Copyright (c) 2004-2005, Jouni Malinen <jkmaline@cc.hut.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPA_CTRL_H +#define WPA_CTRL_H + +#ifdef __cplusplus +extern "C" { +#endif + +/* wpa_supplicant control interface - fixed message prefixes */ + +/** Interactive request for identity/password/pin */ +#define WPA_CTRL_REQ "CTRL-REQ-" + +/** Response to identity/password/pin request */ +#define WPA_CTRL_RSP "CTRL-RSP-" + +/* Event messages with fixed prefix */ +/** Authentication completed successfully and data connection enabled */ +#define WPA_EVENT_CONNECTED "CTRL-EVENT-CONNECTED " +/** Disconnected, data connection is not available */ +#define WPA_EVENT_DISCONNECTED "CTRL-EVENT-DISCONNECTED " +/** wpa_supplicant is exiting */ +#define WPA_EVENT_TERMINATING "CTRL-EVENT-TERMINATING " +/** Password change was completed successfully */ +#define WPA_EVENT_PASSWORD_CHANGED "CTRL-EVENT-PASSWORD-CHANGED " +/** EAP-Request/Notification received */ +#define WPA_EVENT_EAP_NOTIFICATION "CTRL-EVENT-EAP-NOTIFICATION " +/** EAP authentication started (EAP-Request/Identity received) */ +#define WPA_EVENT_EAP_STARTED "CTRL-EVENT-EAP-STARTED " +/** EAP method selected */ +#define WPA_EVENT_EAP_METHOD "CTRL-EVENT-EAP-METHOD " +/** EAP authentication completed successfully */ +#define WPA_EVENT_EAP_SUCCESS "CTRL-EVENT-EAP-SUCCESS " +/** EAP authentication failed (EAP-Failure received) */ +#define WPA_EVENT_EAP_FAILURE "CTRL-EVENT-EAP-FAILURE " + + +/* wpa_supplicant/hostapd control interface access */ + +/** + * wpa_ctrl_open - Open a control interface to wpa_supplicant/hostapd + * @ctrl_path: Path for UNIX domain sockets; ignored if UDP sockets are used. + * Returns: Pointer to abstract control interface data or %NULL on failure + * + * This function is used to open a control interface to wpa_supplicant/hostapd. + * ctrl_path is usually /var/run/wpa_supplicant or /var/run/hostapd. This path + * is configured in wpa_supplicant/hostapd and other programs using the control + * interface need to use matching path configuration. + */ +struct wpa_ctrl * wpa_ctrl_open(const char *ctrl_path); + + +/** + * wpa_ctrl_close - Close a control interface to wpa_supplicant/hostapd + * @ctrl: Control interface data from wpa_ctrl_open() + * + * This function is used to close a control interface. + */ +void wpa_ctrl_close(struct wpa_ctrl *ctrl); + + +/** + * wpa_ctrl_request - Send a command to wpa_supplicant/hostapd + * @ctrl: Control interface data from wpa_ctrl_open() + * @cmd: Command; usually, ASCII text, e.g., "PING" + * @cmd_len: Length of the cmd in bytes + * @reply: Buffer for the response + * @reply_len: Reply buffer length + * @msg_cb: Callback function for unsolicited messages or %NULL if not used + * Returns: 0 on success, -1 on error (send or receive failed), -2 on timeout + * + * This function is used to send commands to wpa_supplicant/hostapd. Received + * response will be written to reply and reply_len is set to the actual length + * of the reply. This function will block for up to two seconds while waiting + * for the reply. If unsolicited messages are received, the blocking time may + * be longer. + * + * msg_cb can be used to register a callback function that will be called for + * unsolicited messages received while waiting for the command response. These + * messages may be received if wpa_ctrl_request() is called at the same time as + * wpa_supplicant/hostapd is sending such a message. This can happen only if + * the program has used wpa_ctrl_attach() to register itself as a monitor for + * event messages. Alternatively to msg_cb, programs can register two control + * interface connections and use one of them for commands and the other one for + * receiving event messages, in other words, call wpa_ctrl_attach() only for + * the control interface connection that will be used for event messages. + */ +int wpa_ctrl_request(struct wpa_ctrl *ctrl, const char *cmd, size_t cmd_len, + char *reply, size_t *reply_len, + void (*msg_cb)(char *msg, size_t len)); + + +/** + * wpa_ctrl_attach - Register as an event monitor for the control interface + * @ctrl: Control interface data from wpa_ctrl_open() + * Returns: 0 on success, -1 on failure, -2 on timeout + * + * This function registers the control interface connection as a monitor for + * wpa_supplicant/hostapd events. After a success wpa_ctrl_attach() call, the + * control interface connection starts receiving event messages that can be + * read with wpa_ctrl_recv(). + */ +int wpa_ctrl_attach(struct wpa_ctrl *ctrl); + + +/** + * wpa_ctrl_detach - Unregister event monitor from the control interface + * @ctrl: Control interface data from wpa_ctrl_open() + * Returns: 0 on success, -1 on failure, -2 on timeout + * + * This function unregisters the control interface connection as a monitor for + * wpa_supplicant/hostapd events, i.e., cancels the registration done with + * wpa_ctrl_attach(). + */ +int wpa_ctrl_detach(struct wpa_ctrl *ctrl); + + +/** + * wpa_ctrl_recv - Receive a pending control interface message + * @ctrl: Control interface data from wpa_ctrl_open() + * @reply: Buffer for the message data + * @reply_len: Length of the reply buffer + * Returns: 0 on success, -1 on failure + * + * This function will receive a pending control interface message. This + * function will block if no messages are available. The received response will + * be written to reply and reply_len is set to the actual length of the reply. + * wpa_ctrl_recv() is only used for event messages, i.e., wpa_ctrl_attach() + * must have been used to register the control interface as an event monitor. + */ +int wpa_ctrl_recv(struct wpa_ctrl *ctrl, char *reply, size_t *reply_len); + + +/** + * wpa_ctrl_pending - Check whether there are pending event messages + * @ctrl: Control interface data from wpa_ctrl_open() + * Returns: Non-zero if there are pending messages + * + * This function will check whether there are any pending control interface + * message available to be received with wpa_ctrl_recv(). wpa_ctrl_pending() is + * only used for event messages, i.e., wpa_ctrl_attach() must have been used to + * register the control interface as an event monitor. + */ +int wpa_ctrl_pending(struct wpa_ctrl *ctrl); + + +/** + * wpa_ctrl_get_fd - Get file descriptor used by the control interface + * @ctrl: Control interface data from wpa_ctrl_open() + * Returns: File descriptor used for the connection + * + * This function can be used to get the file descriptor that is used for the + * control interface connection. The returned value can be used, e.g., with + * select() while waiting for multiple events. + * + * The returned file descriptor must not be used directly for sending or + * receiving packets; instead, the library functions wpa_ctrl_request() and + * wpa_ctrl_recv() must be used for this. + */ +int wpa_ctrl_get_fd(struct wpa_ctrl *ctrl); + +#ifdef CONFIG_CTRL_IFACE_UDP +#define WPA_CTRL_IFACE_PORT 9877 +#define WPA_GLOBAL_CTRL_IFACE_PORT 9878 +#endif /* CONFIG_CTRL_IFACE_UDP */ + + +#ifdef __cplusplus +} +#endif + +#endif /* WPA_CTRL_H */ |