diff options
author | rpaulo <rpaulo@FreeBSD.org> | 2010-11-03 10:43:38 +0000 |
---|---|---|
committer | rpaulo <rpaulo@FreeBSD.org> | 2010-11-03 10:43:38 +0000 |
commit | e0d70c33109c041ff2046fe529eb8e3c0f2acec1 (patch) | |
tree | bf6082c2ad69b06fcb45c37be7157b66ffbd4fc7 /contrib/wpa/src | |
parent | 6ddde2168bc79a10ab0937ba69afe0a74559eea0 (diff) | |
parent | 14ab4e9475c66439a7a011706ada056765de1555 (diff) | |
download | FreeBSD-src-e0d70c33109c041ff2046fe529eb8e3c0f2acec1.zip FreeBSD-src-e0d70c33109c041ff2046fe529eb8e3c0f2acec1.tar.gz |
Merge wpa_supplicant and hostapd 0.7.3.
Diffstat (limited to 'contrib/wpa/src')
334 files changed, 45535 insertions, 10056 deletions
diff --git a/contrib/wpa/src/Makefile b/contrib/wpa/src/Makefile index bd1c160..f47da7b 100644 --- a/contrib/wpa/src/Makefile +++ b/contrib/wpa/src/Makefile @@ -1,7 +1,7 @@ -SUBDIRS=common crypto drivers hlr_auc_gw eapol_supp eap_common eap_peer eap_server l2_packet radius rsn_supp tls utils wps +SUBDIRS=ap common crypto drivers eapol_auth eapol_supp eap_common eap_peer eap_server l2_packet radius rsn_supp tls utils wps all: - @echo Nothing to be made. + for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d; done clean: for d in $(SUBDIRS); do [ -d $$d ] && $(MAKE) -C $$d clean; done diff --git a/contrib/wpa/src/hlr_auc_gw/Makefile b/contrib/wpa/src/ap/Makefile index cffba62..9c41962 100644 --- a/contrib/wpa/src/hlr_auc_gw/Makefile +++ b/contrib/wpa/src/ap/Makefile @@ -2,7 +2,6 @@ all: @echo Nothing to be made. clean: - for d in $(SUBDIRS); do make -C $$d clean; done rm -f *~ *.o *.d install: diff --git a/contrib/wpa/src/ap/accounting.c b/contrib/wpa/src/ap/accounting.c new file mode 100644 index 0000000..7939c68 --- /dev/null +++ b/contrib/wpa/src/ap/accounting.c @@ -0,0 +1,499 @@ +/* + * hostapd / RADIUS Accounting + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published 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 "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "drivers/driver.h" +#include "radius/radius.h" +#include "radius/radius_client.h" +#include "hostapd.h" +#include "ieee802_1x.h" +#include "ap_config.h" +#include "sta_info.h" +#include "accounting.h" + + +/* Default interval in seconds for polling TX/RX octets from the driver if + * STA is not using interim accounting. This detects wrap arounds for + * input/output octets and updates Acct-{Input,Output}-Gigawords. */ +#define ACCT_DEFAULT_UPDATE_INTERVAL 300 + +static void accounting_sta_get_id(struct hostapd_data *hapd, + struct sta_info *sta); + + +static struct radius_msg * accounting_msg(struct hostapd_data *hapd, + struct sta_info *sta, + int status_type) +{ + struct radius_msg *msg; + char buf[128]; + u8 *val; + size_t len; + int i; + + msg = radius_msg_new(RADIUS_CODE_ACCOUNTING_REQUEST, + radius_client_get_id(hapd->radius)); + if (msg == NULL) { + printf("Could not create net RADIUS packet\n"); + return NULL; + } + + if (sta) { + radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta)); + + os_snprintf(buf, sizeof(buf), "%08X-%08X", + sta->acct_session_id_hi, sta->acct_session_id_lo); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID, + (u8 *) buf, os_strlen(buf))) { + printf("Could not add Acct-Session-Id\n"); + goto fail; + } + } else { + radius_msg_make_authenticator(msg, (u8 *) hapd, sizeof(*hapd)); + } + + if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_STATUS_TYPE, + status_type)) { + printf("Could not add Acct-Status-Type\n"); + goto fail; + } + + if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_AUTHENTIC, + hapd->conf->ieee802_1x ? + RADIUS_ACCT_AUTHENTIC_RADIUS : + RADIUS_ACCT_AUTHENTIC_LOCAL)) { + printf("Could not add Acct-Authentic\n"); + goto fail; + } + + if (sta) { + val = ieee802_1x_get_identity(sta->eapol_sm, &len); + if (!val) { + os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, + MAC2STR(sta->addr)); + val = (u8 *) buf; + len = os_strlen(buf); + } + + if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, val, + len)) { + printf("Could not add User-Name\n"); + goto fail; + } + } + + if (hapd->conf->own_ip_addr.af == AF_INET && + !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, + (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) { + printf("Could not add NAS-IP-Address\n"); + 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, + os_strlen(hapd->conf->nas_identifier))) { + printf("Could not add NAS-Identifier\n"); + goto fail; + } + + if (sta && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) { + printf("Could not add NAS-Port\n"); + goto fail; + } + + os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", + MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, + (u8 *) buf, os_strlen(buf))) { + printf("Could not add Called-Station-Id\n"); + goto fail; + } + + if (sta) { + os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, + MAC2STR(sta->addr)); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, + (u8 *) buf, os_strlen(buf))) { + printf("Could not add Calling-Station-Id\n"); + goto fail; + } + + if (!radius_msg_add_attr_int32( + msg, RADIUS_ATTR_NAS_PORT_TYPE, + RADIUS_NAS_PORT_TYPE_IEEE_802_11)) { + printf("Could not add NAS-Port-Type\n"); + goto fail; + } + + os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s", + radius_sta_rate(hapd, sta) / 2, + (radius_sta_rate(hapd, sta) & 1) ? ".5" : "", + radius_mode_txt(hapd)); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, + (u8 *) buf, os_strlen(buf))) { + printf("Could not add Connect-Info\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; + } + } + } + + return msg; + + fail: + radius_msg_free(msg); + return NULL; +} + + +static int accounting_sta_update_stats(struct hostapd_data *hapd, + struct sta_info *sta, + struct hostap_sta_driver_data *data) +{ + if (hapd->drv.read_sta_data(hapd, data, sta->addr)) + return -1; + + if (sta->last_rx_bytes > data->rx_bytes) + sta->acct_input_gigawords++; + if (sta->last_tx_bytes > data->tx_bytes) + sta->acct_output_gigawords++; + sta->last_rx_bytes = data->rx_bytes; + sta->last_tx_bytes = data->tx_bytes; + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_DEBUG, "updated TX/RX stats: " + "Acct-Input-Octets=%lu Acct-Input-Gigawords=%u " + "Acct-Output-Octets=%lu Acct-Output-Gigawords=%u", + sta->last_rx_bytes, sta->acct_input_gigawords, + sta->last_tx_bytes, sta->acct_output_gigawords); + + return 0; +} + + +static void accounting_interim_update(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + int interval; + + if (sta->acct_interim_interval) { + accounting_sta_interim(hapd, sta); + interval = sta->acct_interim_interval; + } else { + struct hostap_sta_driver_data data; + accounting_sta_update_stats(hapd, sta, &data); + interval = ACCT_DEFAULT_UPDATE_INTERVAL; + } + + eloop_register_timeout(interval, 0, accounting_interim_update, + hapd, sta); +} + + +/** + * accounting_sta_start - Start STA accounting + * @hapd: hostapd BSS data + * @sta: The station + */ +void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta) +{ + struct radius_msg *msg; + int interval; + + if (sta->acct_session_started) + return; + + accounting_sta_get_id(hapd, sta); + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, + "starting accounting session %08X-%08X", + sta->acct_session_id_hi, sta->acct_session_id_lo); + + time(&sta->acct_session_start); + sta->last_rx_bytes = sta->last_tx_bytes = 0; + sta->acct_input_gigawords = sta->acct_output_gigawords = 0; + hapd->drv.sta_clear_stats(hapd, sta->addr); + + if (!hapd->conf->radius->acct_server) + return; + + if (sta->acct_interim_interval) + interval = sta->acct_interim_interval; + else + interval = ACCT_DEFAULT_UPDATE_INTERVAL; + eloop_register_timeout(interval, 0, accounting_interim_update, + hapd, sta); + + msg = accounting_msg(hapd, sta, RADIUS_ACCT_STATUS_TYPE_START); + if (msg) + radius_client_send(hapd->radius, msg, RADIUS_ACCT, sta->addr); + + sta->acct_session_started = 1; +} + + +static void accounting_sta_report(struct hostapd_data *hapd, + struct sta_info *sta, int stop) +{ + struct radius_msg *msg; + int cause = sta->acct_terminate_cause; + struct hostap_sta_driver_data data; + u32 gigawords; + + if (!hapd->conf->radius->acct_server) + return; + + msg = accounting_msg(hapd, sta, + stop ? RADIUS_ACCT_STATUS_TYPE_STOP : + RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE); + if (!msg) { + printf("Could not create RADIUS Accounting message\n"); + return; + } + + if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_SESSION_TIME, + time(NULL) - sta->acct_session_start)) { + printf("Could not add Acct-Session-Time\n"); + goto fail; + } + + if (accounting_sta_update_stats(hapd, sta, &data) == 0) { + if (!radius_msg_add_attr_int32(msg, + RADIUS_ATTR_ACCT_INPUT_PACKETS, + data.rx_packets)) { + printf("Could not add Acct-Input-Packets\n"); + goto fail; + } + if (!radius_msg_add_attr_int32(msg, + RADIUS_ATTR_ACCT_OUTPUT_PACKETS, + data.tx_packets)) { + printf("Could not add Acct-Output-Packets\n"); + goto fail; + } + if (!radius_msg_add_attr_int32(msg, + RADIUS_ATTR_ACCT_INPUT_OCTETS, + data.rx_bytes)) { + printf("Could not add Acct-Input-Octets\n"); + goto fail; + } + gigawords = sta->acct_input_gigawords; +#if __WORDSIZE == 64 + gigawords += data.rx_bytes >> 32; +#endif + if (gigawords && + !radius_msg_add_attr_int32( + msg, RADIUS_ATTR_ACCT_INPUT_GIGAWORDS, + gigawords)) { + printf("Could not add Acct-Input-Gigawords\n"); + goto fail; + } + if (!radius_msg_add_attr_int32(msg, + RADIUS_ATTR_ACCT_OUTPUT_OCTETS, + data.tx_bytes)) { + printf("Could not add Acct-Output-Octets\n"); + goto fail; + } + gigawords = sta->acct_output_gigawords; +#if __WORDSIZE == 64 + gigawords += data.tx_bytes >> 32; +#endif + if (gigawords && + !radius_msg_add_attr_int32( + msg, RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS, + gigawords)) { + printf("Could not add Acct-Output-Gigawords\n"); + goto fail; + } + } + + if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_EVENT_TIMESTAMP, + time(NULL))) { + printf("Could not add Event-Timestamp\n"); + goto fail; + } + + if (eloop_terminated()) + cause = RADIUS_ACCT_TERMINATE_CAUSE_ADMIN_REBOOT; + + if (stop && cause && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE, + cause)) { + printf("Could not add Acct-Terminate-Cause\n"); + goto fail; + } + + radius_client_send(hapd->radius, msg, + stop ? RADIUS_ACCT : RADIUS_ACCT_INTERIM, + sta->addr); + return; + + fail: + radius_msg_free(msg); +} + + +/** + * accounting_sta_interim - Send a interim STA accounting report + * @hapd: hostapd BSS data + * @sta: The station + */ +void accounting_sta_interim(struct hostapd_data *hapd, struct sta_info *sta) +{ + if (sta->acct_session_started) + accounting_sta_report(hapd, sta, 0); +} + + +/** + * accounting_sta_stop - Stop STA accounting + * @hapd: hostapd BSS data + * @sta: The station + */ +void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta) +{ + if (sta->acct_session_started) { + accounting_sta_report(hapd, sta, 1); + eloop_cancel_timeout(accounting_interim_update, hapd, sta); + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, + "stopped accounting session %08X-%08X", + sta->acct_session_id_hi, + sta->acct_session_id_lo); + sta->acct_session_started = 0; + } +} + + +static void accounting_sta_get_id(struct hostapd_data *hapd, + struct sta_info *sta) +{ + sta->acct_session_id_lo = hapd->acct_session_id_lo++; + if (hapd->acct_session_id_lo == 0) { + hapd->acct_session_id_hi++; + } + sta->acct_session_id_hi = hapd->acct_session_id_hi; +} + + +/** + * accounting_receive - Process the RADIUS frames from Accounting Server + * @msg: RADIUS response message + * @req: RADIUS request message + * @shared_secret: RADIUS shared secret + * @shared_secret_len: Length of shared_secret in octets + * @data: Context data (struct hostapd_data *) + * Returns: Processing status + */ +static RadiusRxResult +accounting_receive(struct radius_msg *msg, struct radius_msg *req, + const u8 *shared_secret, size_t shared_secret_len, + void *data) +{ + if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCOUNTING_RESPONSE) { + printf("Unknown RADIUS message code\n"); + return RADIUS_RX_UNKNOWN; + } + + 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; + } + + return RADIUS_RX_PROCESSED; +} + + +static void accounting_report_state(struct hostapd_data *hapd, int on) +{ + struct radius_msg *msg; + + if (!hapd->conf->radius->acct_server || hapd->radius == NULL) + return; + + /* Inform RADIUS server that accounting will start/stop so that the + * server can close old accounting sessions. */ + msg = accounting_msg(hapd, NULL, + on ? RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON : + RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF); + if (!msg) + return; + + if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_ACCT_TERMINATE_CAUSE, + RADIUS_ACCT_TERMINATE_CAUSE_NAS_REBOOT)) + { + printf("Could not add Acct-Terminate-Cause\n"); + radius_msg_free(msg); + return; + } + + radius_client_send(hapd->radius, msg, RADIUS_ACCT, NULL); +} + + +/** + * accounting_init: Initialize accounting + * @hapd: hostapd BSS data + * Returns: 0 on success, -1 on failure + */ +int accounting_init(struct hostapd_data *hapd) +{ + /* Acct-Session-Id should be unique over reboots. If reliable clock is + * not available, this could be replaced with reboot counter, etc. */ + hapd->acct_session_id_hi = time(NULL); + + if (radius_client_register(hapd->radius, RADIUS_ACCT, + accounting_receive, hapd)) + return -1; + + accounting_report_state(hapd, 1); + + return 0; +} + + +/** + * accounting_deinit: Deinitilize accounting + * @hapd: hostapd BSS data + */ +void accounting_deinit(struct hostapd_data *hapd) +{ + accounting_report_state(hapd, 0); +} diff --git a/contrib/wpa/src/ap/accounting.h b/contrib/wpa/src/ap/accounting.h new file mode 100644 index 0000000..f3d60f0 --- /dev/null +++ b/contrib/wpa/src/ap/accounting.h @@ -0,0 +1,45 @@ +/* + * hostapd / RADIUS Accounting + * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef ACCOUNTING_H +#define ACCOUNTING_H + +void accounting_sta_interim(struct hostapd_data *hapd, struct sta_info *sta); +#ifdef CONFIG_NO_ACCOUNTING +static inline void accounting_sta_start(struct hostapd_data *hapd, + struct sta_info *sta) +{ +} + +static inline void accounting_sta_stop(struct hostapd_data *hapd, + struct sta_info *sta) +{ +} + +static inline int accounting_init(struct hostapd_data *hapd) +{ + return 0; +} + +static inline void accounting_deinit(struct hostapd_data *hapd) +{ +} +#else /* CONFIG_NO_ACCOUNTING */ +void accounting_sta_start(struct hostapd_data *hapd, struct sta_info *sta); +void accounting_sta_stop(struct hostapd_data *hapd, struct sta_info *sta); +int accounting_init(struct hostapd_data *hapd); +void accounting_deinit(struct hostapd_data *hapd); +#endif /* CONFIG_NO_ACCOUNTING */ + +#endif /* ACCOUNTING_H */ diff --git a/contrib/wpa/src/ap/ap_config.c b/contrib/wpa/src/ap/ap_config.c new file mode 100644 index 0000000..5996993 --- /dev/null +++ b/contrib/wpa/src/ap/ap_config.c @@ -0,0 +1,605 @@ +/* + * hostapd / Configuration helper functions + * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "crypto/sha1.h" +#include "radius/radius_client.h" +#include "common/ieee802_11_defs.h" +#include "common/eapol_common.h" +#include "eap_common/eap_wsc_common.h" +#include "eap_server/eap.h" +#include "wpa_auth.h" +#include "sta_info.h" +#include "ap_config.h" + + +static void hostapd_config_free_vlan(struct hostapd_bss_config *bss) +{ + struct hostapd_vlan *vlan, *prev; + + vlan = bss->vlan; + prev = NULL; + while (vlan) { + prev = vlan; + vlan = vlan->next; + os_free(prev); + } + + bss->vlan = NULL; +} + + +void hostapd_config_defaults_bss(struct hostapd_bss_config *bss) +{ + bss->logger_syslog_level = HOSTAPD_LEVEL_INFO; + bss->logger_stdout_level = HOSTAPD_LEVEL_INFO; + bss->logger_syslog = (unsigned int) -1; + bss->logger_stdout = (unsigned int) -1; + + bss->auth_algs = WPA_AUTH_ALG_OPEN | WPA_AUTH_ALG_SHARED; + + bss->wep_rekeying_period = 300; + /* use key0 in individual key and key1 in broadcast key */ + bss->broadcast_key_idx_min = 1; + bss->broadcast_key_idx_max = 2; + bss->eap_reauth_period = 3600; + + bss->wpa_group_rekey = 600; + bss->wpa_gmk_rekey = 86400; + bss->wpa_key_mgmt = WPA_KEY_MGMT_PSK; + bss->wpa_pairwise = WPA_CIPHER_TKIP; + bss->wpa_group = WPA_CIPHER_TKIP; + bss->rsn_pairwise = 0; + + bss->max_num_sta = MAX_STA_COUNT; + + bss->dtim_period = 2; + + bss->radius_server_auth_port = 1812; + bss->ap_max_inactivity = AP_MAX_INACTIVITY; + bss->eapol_version = EAPOL_VERSION; + + bss->max_listen_interval = 65535; + +#ifdef CONFIG_IEEE80211W + bss->assoc_sa_query_max_timeout = 1000; + bss->assoc_sa_query_retry_timeout = 201; +#endif /* CONFIG_IEEE80211W */ +#ifdef EAP_SERVER_FAST + /* both anonymous and authenticated provisioning */ + bss->eap_fast_prov = 3; + bss->pac_key_lifetime = 7 * 24 * 60 * 60; + bss->pac_key_refresh_time = 1 * 24 * 60 * 60; +#endif /* EAP_SERVER_FAST */ +} + + +struct hostapd_config * hostapd_config_defaults(void) +{ + struct hostapd_config *conf; + struct hostapd_bss_config *bss; + int i; + const int aCWmin = 4, aCWmax = 10; + const struct hostapd_wmm_ac_params ac_bk = + { aCWmin, aCWmax, 7, 0, 0 }; /* background traffic */ + const struct hostapd_wmm_ac_params ac_be = + { aCWmin, aCWmax, 3, 0, 0 }; /* best effort traffic */ + const struct hostapd_wmm_ac_params ac_vi = /* video traffic */ + { aCWmin - 1, aCWmin, 2, 3000 / 32, 1 }; + const struct hostapd_wmm_ac_params ac_vo = /* voice traffic */ + { aCWmin - 2, aCWmin - 1, 2, 1500 / 32, 1 }; + + conf = os_zalloc(sizeof(*conf)); + bss = os_zalloc(sizeof(*bss)); + if (conf == NULL || bss == NULL) { + wpa_printf(MSG_ERROR, "Failed to allocate memory for " + "configuration data."); + os_free(conf); + os_free(bss); + return NULL; + } + + bss->radius = os_zalloc(sizeof(*bss->radius)); + if (bss->radius == NULL) { + os_free(conf); + os_free(bss); + return NULL; + } + + hostapd_config_defaults_bss(bss); + + conf->num_bss = 1; + conf->bss = bss; + + conf->beacon_int = 100; + conf->rts_threshold = -1; /* use driver default: 2347 */ + conf->fragm_threshold = -1; /* user driver default: 2346 */ + conf->send_probe_response = 1; + + for (i = 0; i < NUM_TX_QUEUES; i++) + conf->tx_queue[i].aifs = -1; /* use hw default */ + + conf->wmm_ac_params[0] = ac_be; + conf->wmm_ac_params[1] = ac_bk; + conf->wmm_ac_params[2] = ac_vi; + conf->wmm_ac_params[3] = ac_vo; + + conf->ht_capab = HT_CAP_INFO_SMPS_DISABLED; + + return conf; +} + + +int hostapd_mac_comp(const void *a, const void *b) +{ + return os_memcmp(a, b, sizeof(macaddr)); +} + + +int hostapd_mac_comp_empty(const void *a) +{ + macaddr empty = { 0 }; + return os_memcmp(a, empty, sizeof(macaddr)); +} + + +static int hostapd_config_read_wpa_psk(const char *fname, + struct hostapd_ssid *ssid) +{ + FILE *f; + char buf[128], *pos; + int line = 0, ret = 0, len, ok; + u8 addr[ETH_ALEN]; + struct hostapd_wpa_psk *psk; + + if (!fname) + return 0; + + f = fopen(fname, "r"); + if (!f) { + wpa_printf(MSG_ERROR, "WPA PSK file '%s' not found.", fname); + return -1; + } + + while (fgets(buf, sizeof(buf), f)) { + line++; + + if (buf[0] == '#') + continue; + pos = buf; + while (*pos != '\0') { + if (*pos == '\n') { + *pos = '\0'; + break; + } + pos++; + } + if (buf[0] == '\0') + continue; + + if (hwaddr_aton(buf, addr)) { + wpa_printf(MSG_ERROR, "Invalid MAC address '%s' on " + "line %d in '%s'", buf, line, fname); + ret = -1; + break; + } + + psk = os_zalloc(sizeof(*psk)); + if (psk == NULL) { + wpa_printf(MSG_ERROR, "WPA PSK allocation failed"); + ret = -1; + break; + } + if (is_zero_ether_addr(addr)) + psk->group = 1; + else + os_memcpy(psk->addr, addr, ETH_ALEN); + + pos = buf + 17; + if (*pos == '\0') { + wpa_printf(MSG_ERROR, "No PSK on line %d in '%s'", + line, fname); + os_free(psk); + ret = -1; + break; + } + pos++; + + ok = 0; + len = os_strlen(pos); + if (len == 64 && hexstr2bin(pos, psk->psk, PMK_LEN) == 0) + ok = 1; + else if (len >= 8 && len < 64) { + pbkdf2_sha1(pos, ssid->ssid, ssid->ssid_len, + 4096, psk->psk, PMK_LEN); + ok = 1; + } + if (!ok) { + wpa_printf(MSG_ERROR, "Invalid PSK '%s' on line %d in " + "'%s'", pos, line, fname); + os_free(psk); + ret = -1; + break; + } + + psk->next = ssid->wpa_psk; + ssid->wpa_psk = psk; + } + + fclose(f); + + return ret; +} + + +static int hostapd_derive_psk(struct hostapd_ssid *ssid) +{ + ssid->wpa_psk = os_zalloc(sizeof(struct hostapd_wpa_psk)); + if (ssid->wpa_psk == NULL) { + wpa_printf(MSG_ERROR, "Unable to alloc space for PSK"); + return -1; + } + wpa_hexdump_ascii(MSG_DEBUG, "SSID", + (u8 *) ssid->ssid, ssid->ssid_len); + wpa_hexdump_ascii_key(MSG_DEBUG, "PSK (ASCII passphrase)", + (u8 *) ssid->wpa_passphrase, + os_strlen(ssid->wpa_passphrase)); + pbkdf2_sha1(ssid->wpa_passphrase, + ssid->ssid, ssid->ssid_len, + 4096, ssid->wpa_psk->psk, PMK_LEN); + wpa_hexdump_key(MSG_DEBUG, "PSK (from passphrase)", + ssid->wpa_psk->psk, PMK_LEN); + return 0; +} + + +int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf) +{ + struct hostapd_ssid *ssid = &conf->ssid; + + if (ssid->wpa_passphrase != NULL) { + if (ssid->wpa_psk != NULL) { + wpa_printf(MSG_DEBUG, "Using pre-configured WPA PSK " + "instead of passphrase"); + } else { + wpa_printf(MSG_DEBUG, "Deriving WPA PSK based on " + "passphrase"); + if (hostapd_derive_psk(ssid) < 0) + return -1; + } + ssid->wpa_psk->group = 1; + } + + if (ssid->wpa_psk_file) { + if (hostapd_config_read_wpa_psk(ssid->wpa_psk_file, + &conf->ssid)) + return -1; + } + + return 0; +} + + +int hostapd_wep_key_cmp(struct hostapd_wep_keys *a, struct hostapd_wep_keys *b) +{ + int i; + + if (a->idx != b->idx || a->default_len != b->default_len) + return 1; + for (i = 0; i < NUM_WEP_KEYS; i++) + if (a->len[i] != b->len[i] || + os_memcmp(a->key[i], b->key[i], a->len[i]) != 0) + return 1; + return 0; +} + + +static void hostapd_config_free_radius(struct hostapd_radius_server *servers, + int num_servers) +{ + int i; + + for (i = 0; i < num_servers; i++) { + os_free(servers[i].shared_secret); + } + os_free(servers); +} + + +static void hostapd_config_free_eap_user(struct hostapd_eap_user *user) +{ + os_free(user->identity); + os_free(user->password); + os_free(user); +} + + +static void hostapd_config_free_wep(struct hostapd_wep_keys *keys) +{ + int i; + for (i = 0; i < NUM_WEP_KEYS; i++) { + os_free(keys->key[i]); + keys->key[i] = NULL; + } +} + + +static void hostapd_config_free_bss(struct hostapd_bss_config *conf) +{ + struct hostapd_wpa_psk *psk, *prev; + struct hostapd_eap_user *user, *prev_user; + + if (conf == NULL) + return; + + psk = conf->ssid.wpa_psk; + while (psk) { + prev = psk; + psk = psk->next; + os_free(prev); + } + + os_free(conf->ssid.wpa_passphrase); + os_free(conf->ssid.wpa_psk_file); + hostapd_config_free_wep(&conf->ssid.wep); +#ifdef CONFIG_FULL_DYNAMIC_VLAN + os_free(conf->ssid.vlan_tagged_interface); +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + + user = conf->eap_user; + while (user) { + prev_user = user; + user = user->next; + hostapd_config_free_eap_user(prev_user); + } + + os_free(conf->dump_log_name); + os_free(conf->eap_req_id_text); + os_free(conf->accept_mac); + os_free(conf->deny_mac); + os_free(conf->nas_identifier); + hostapd_config_free_radius(conf->radius->auth_servers, + conf->radius->num_auth_servers); + hostapd_config_free_radius(conf->radius->acct_servers, + conf->radius->num_acct_servers); + os_free(conf->rsn_preauth_interfaces); + os_free(conf->ctrl_interface); + os_free(conf->ca_cert); + os_free(conf->server_cert); + os_free(conf->private_key); + os_free(conf->private_key_passwd); + os_free(conf->dh_file); + os_free(conf->pac_opaque_encr_key); + os_free(conf->eap_fast_a_id); + os_free(conf->eap_fast_a_id_info); + os_free(conf->eap_sim_db); + os_free(conf->radius_server_clients); + os_free(conf->test_socket); + os_free(conf->radius); + hostapd_config_free_vlan(conf); + if (conf->ssid.dyn_vlan_keys) { + struct hostapd_ssid *ssid = &conf->ssid; + size_t i; + for (i = 0; i <= ssid->max_dyn_vlan_keys; i++) { + if (ssid->dyn_vlan_keys[i] == NULL) + continue; + hostapd_config_free_wep(ssid->dyn_vlan_keys[i]); + os_free(ssid->dyn_vlan_keys[i]); + } + os_free(ssid->dyn_vlan_keys); + ssid->dyn_vlan_keys = NULL; + } + +#ifdef CONFIG_IEEE80211R + { + struct ft_remote_r0kh *r0kh, *r0kh_prev; + struct ft_remote_r1kh *r1kh, *r1kh_prev; + + r0kh = conf->r0kh_list; + conf->r0kh_list = NULL; + while (r0kh) { + r0kh_prev = r0kh; + r0kh = r0kh->next; + os_free(r0kh_prev); + } + + r1kh = conf->r1kh_list; + conf->r1kh_list = NULL; + while (r1kh) { + r1kh_prev = r1kh; + r1kh = r1kh->next; + os_free(r1kh_prev); + } + } +#endif /* CONFIG_IEEE80211R */ + +#ifdef CONFIG_WPS + os_free(conf->wps_pin_requests); + os_free(conf->device_name); + os_free(conf->manufacturer); + os_free(conf->model_name); + os_free(conf->model_number); + os_free(conf->serial_number); + os_free(conf->device_type); + os_free(conf->config_methods); + os_free(conf->ap_pin); + os_free(conf->extra_cred); + os_free(conf->ap_settings); + os_free(conf->upnp_iface); + os_free(conf->friendly_name); + os_free(conf->manufacturer_url); + os_free(conf->model_description); + os_free(conf->model_url); + os_free(conf->upc); +#endif /* CONFIG_WPS */ +} + + +/** + * hostapd_config_free - Free hostapd configuration + * @conf: Configuration data from hostapd_config_read(). + */ +void hostapd_config_free(struct hostapd_config *conf) +{ + size_t i; + + if (conf == NULL) + return; + + for (i = 0; i < conf->num_bss; i++) + hostapd_config_free_bss(&conf->bss[i]); + os_free(conf->bss); + os_free(conf->supported_rates); + os_free(conf->basic_rates); + + os_free(conf); +} + + +/** + * hostapd_maclist_found - Find a MAC address from a list + * @list: MAC address list + * @num_entries: Number of addresses in the list + * @addr: Address to search for + * @vlan_id: Buffer for returning VLAN ID or %NULL if not needed + * Returns: 1 if address is in the list or 0 if not. + * + * Perform a binary search for given MAC address from a pre-sorted list. + */ +int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries, + const u8 *addr, int *vlan_id) +{ + int start, end, middle, res; + + start = 0; + end = num_entries - 1; + + while (start <= end) { + middle = (start + end) / 2; + res = os_memcmp(list[middle].addr, addr, ETH_ALEN); + if (res == 0) { + if (vlan_id) + *vlan_id = list[middle].vlan_id; + return 1; + } + if (res < 0) + start = middle + 1; + else + end = middle - 1; + } + + return 0; +} + + +int hostapd_rate_found(int *list, int rate) +{ + int i; + + if (list == NULL) + return 0; + + for (i = 0; list[i] >= 0; i++) + if (list[i] == rate) + return 1; + + return 0; +} + + +const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id) +{ + struct hostapd_vlan *v = vlan; + while (v) { + if (v->vlan_id == vlan_id || v->vlan_id == VLAN_ID_WILDCARD) + return v->ifname; + v = v->next; + } + return NULL; +} + + +const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf, + const u8 *addr, const u8 *prev_psk) +{ + struct hostapd_wpa_psk *psk; + int next_ok = prev_psk == NULL; + + for (psk = conf->ssid.wpa_psk; psk != NULL; psk = psk->next) { + if (next_ok && + (psk->group || os_memcmp(psk->addr, addr, ETH_ALEN) == 0)) + return psk->psk; + + if (psk->psk == prev_psk) + next_ok = 1; + } + + return NULL; +} + + +const struct hostapd_eap_user * +hostapd_get_eap_user(const struct hostapd_bss_config *conf, const u8 *identity, + size_t identity_len, int phase2) +{ + struct hostapd_eap_user *user = conf->eap_user; + +#ifdef CONFIG_WPS + if (conf->wps_state && identity_len == WSC_ID_ENROLLEE_LEN && + os_memcmp(identity, WSC_ID_ENROLLEE, WSC_ID_ENROLLEE_LEN) == 0) { + static struct hostapd_eap_user wsc_enrollee; + os_memset(&wsc_enrollee, 0, sizeof(wsc_enrollee)); + wsc_enrollee.methods[0].method = eap_server_get_type( + "WSC", &wsc_enrollee.methods[0].vendor); + return &wsc_enrollee; + } + + if (conf->wps_state && identity_len == WSC_ID_REGISTRAR_LEN && + os_memcmp(identity, WSC_ID_REGISTRAR, WSC_ID_REGISTRAR_LEN) == 0) { + static struct hostapd_eap_user wsc_registrar; + os_memset(&wsc_registrar, 0, sizeof(wsc_registrar)); + wsc_registrar.methods[0].method = eap_server_get_type( + "WSC", &wsc_registrar.methods[0].vendor); + wsc_registrar.password = (u8 *) conf->ap_pin; + wsc_registrar.password_len = conf->ap_pin ? + os_strlen(conf->ap_pin) : 0; + return &wsc_registrar; + } +#endif /* CONFIG_WPS */ + + while (user) { + if (!phase2 && user->identity == NULL) { + /* Wildcard match */ + break; + } + + if (user->phase2 == !!phase2 && user->wildcard_prefix && + identity_len >= user->identity_len && + os_memcmp(user->identity, identity, user->identity_len) == + 0) { + /* Wildcard prefix match */ + break; + } + + if (user->phase2 == !!phase2 && + user->identity_len == identity_len && + os_memcmp(user->identity, identity, identity_len) == 0) + break; + user = user->next; + } + + return user; +} diff --git a/contrib/wpa/src/ap/ap_config.h b/contrib/wpa/src/ap/ap_config.h new file mode 100644 index 0000000..f509b5b --- /dev/null +++ b/contrib/wpa/src/ap/ap_config.h @@ -0,0 +1,396 @@ +/* + * hostapd / Configuration definitions and helpers functions + * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef HOSTAPD_CONFIG_H +#define HOSTAPD_CONFIG_H + +#include "common/defs.h" +#include "ip_addr.h" +#include "common/wpa_common.h" + +#define MAX_STA_COUNT 2007 +#define MAX_VLAN_ID 4094 + +typedef u8 macaddr[ETH_ALEN]; + +struct mac_acl_entry { + macaddr addr; + int vlan_id; +}; + +struct hostapd_radius_servers; +struct ft_remote_r0kh; +struct ft_remote_r1kh; + +#define HOSTAPD_MAX_SSID_LEN 32 + +#define NUM_WEP_KEYS 4 +struct hostapd_wep_keys { + u8 idx; + u8 *key[NUM_WEP_KEYS]; + size_t len[NUM_WEP_KEYS]; + int keys_set; + size_t default_len; /* key length used for dynamic key generation */ +}; + +typedef enum hostap_security_policy { + SECURITY_PLAINTEXT = 0, + SECURITY_STATIC_WEP = 1, + SECURITY_IEEE_802_1X = 2, + SECURITY_WPA_PSK = 3, + SECURITY_WPA = 4 +} secpolicy; + +struct hostapd_ssid { + char ssid[HOSTAPD_MAX_SSID_LEN + 1]; + size_t ssid_len; + int ssid_set; + + char vlan[IFNAMSIZ + 1]; + secpolicy security_policy; + + struct hostapd_wpa_psk *wpa_psk; + char *wpa_passphrase; + char *wpa_psk_file; + + struct hostapd_wep_keys wep; + +#define DYNAMIC_VLAN_DISABLED 0 +#define DYNAMIC_VLAN_OPTIONAL 1 +#define DYNAMIC_VLAN_REQUIRED 2 + int dynamic_vlan; +#ifdef CONFIG_FULL_DYNAMIC_VLAN + char *vlan_tagged_interface; +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + struct hostapd_wep_keys **dyn_vlan_keys; + size_t max_dyn_vlan_keys; +}; + + +#define VLAN_ID_WILDCARD -1 + +struct hostapd_vlan { + struct hostapd_vlan *next; + int vlan_id; /* VLAN ID or -1 (VLAN_ID_WILDCARD) for wildcard entry */ + char ifname[IFNAMSIZ + 1]; + int dynamic_vlan; +#ifdef CONFIG_FULL_DYNAMIC_VLAN + +#define DVLAN_CLEAN_BR 0x1 +#define DVLAN_CLEAN_VLAN 0x2 +#define DVLAN_CLEAN_VLAN_PORT 0x4 +#define DVLAN_CLEAN_WLAN_PORT 0x8 + int clean; +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ +}; + +#define PMK_LEN 32 +struct hostapd_wpa_psk { + struct hostapd_wpa_psk *next; + int group; + u8 psk[PMK_LEN]; + u8 addr[ETH_ALEN]; +}; + +#define EAP_USER_MAX_METHODS 8 +struct hostapd_eap_user { + struct hostapd_eap_user *next; + u8 *identity; + size_t identity_len; + struct { + int vendor; + u32 method; + } methods[EAP_USER_MAX_METHODS]; + u8 *password; + size_t password_len; + int phase2; + int force_version; + unsigned int wildcard_prefix:1; + unsigned int password_hash:1; /* whether password is hashed with + * nt_password_hash() */ + int ttls_auth; /* EAP_TTLS_AUTH_* bitfield */ +}; + + +#define NUM_TX_QUEUES 8 + +struct hostapd_tx_queue_params { + int aifs; + int cwmin; + int cwmax; + int burst; /* maximum burst time in 0.1 ms, i.e., 10 = 1 ms */ + int configured; +}; + +struct hostapd_wmm_ac_params { + int cwmin; + int cwmax; + int aifs; + int txop_limit; /* in units of 32us */ + int admission_control_mandatory; +}; + + +/** + * struct hostapd_bss_config - Per-BSS configuration + */ +struct hostapd_bss_config { + char iface[IFNAMSIZ + 1]; + char bridge[IFNAMSIZ + 1]; + + enum hostapd_logger_level logger_syslog_level, logger_stdout_level; + + unsigned int logger_syslog; /* module bitfield */ + unsigned int logger_stdout; /* module bitfield */ + + char *dump_log_name; /* file name for state dump (SIGUSR1) */ + + int max_num_sta; /* maximum number of STAs in station table */ + + int dtim_period; + + int ieee802_1x; /* use IEEE 802.1X */ + int eapol_version; + int eap_server; /* Use internal EAP server instead of external + * RADIUS server */ + struct hostapd_eap_user *eap_user; + char *eap_sim_db; + struct hostapd_ip_addr own_ip_addr; + char *nas_identifier; + struct hostapd_radius_servers *radius; + int acct_interim_interval; + + struct hostapd_ssid ssid; + + char *eap_req_id_text; /* optional displayable message sent with + * EAP Request-Identity */ + size_t eap_req_id_text_len; + int eapol_key_index_workaround; + + size_t default_wep_key_len; + int individual_wep_key_len; + int wep_rekeying_period; + int broadcast_key_idx_min, broadcast_key_idx_max; + int eap_reauth_period; + + int ieee802_11f; /* use IEEE 802.11f (IAPP) */ + char iapp_iface[IFNAMSIZ + 1]; /* interface used with IAPP broadcast + * frames */ + + enum { + ACCEPT_UNLESS_DENIED = 0, + DENY_UNLESS_ACCEPTED = 1, + USE_EXTERNAL_RADIUS_AUTH = 2 + } macaddr_acl; + struct mac_acl_entry *accept_mac; + int num_accept_mac; + struct mac_acl_entry *deny_mac; + int num_deny_mac; + int wds_sta; + + int auth_algs; /* bitfield of allowed IEEE 802.11 authentication + * algorithms, WPA_AUTH_ALG_{OPEN,SHARED,LEAP} */ + + int wpa; /* bitfield of WPA_PROTO_WPA, WPA_PROTO_RSN */ + int wpa_key_mgmt; +#ifdef CONFIG_IEEE80211W + enum mfp_options ieee80211w; + /* dot11AssociationSAQueryMaximumTimeout (in TUs) */ + unsigned int assoc_sa_query_max_timeout; + /* dot11AssociationSAQueryRetryTimeout (in TUs) */ + int assoc_sa_query_retry_timeout; +#endif /* CONFIG_IEEE80211W */ + int wpa_pairwise; + int wpa_group; + int wpa_group_rekey; + int wpa_strict_rekey; + int wpa_gmk_rekey; + int wpa_ptk_rekey; + int rsn_pairwise; + int rsn_preauth; + char *rsn_preauth_interfaces; + int peerkey; + +#ifdef CONFIG_IEEE80211R + /* IEEE 802.11r - Fast BSS Transition */ + u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; + u8 r1_key_holder[FT_R1KH_ID_LEN]; + u32 r0_key_lifetime; + u32 reassociation_deadline; + struct ft_remote_r0kh *r0kh_list; + struct ft_remote_r1kh *r1kh_list; + int pmk_r1_push; +#endif /* CONFIG_IEEE80211R */ + + char *ctrl_interface; /* directory for UNIX domain sockets */ +#ifndef CONFIG_NATIVE_WINDOWS + gid_t ctrl_interface_gid; +#endif /* CONFIG_NATIVE_WINDOWS */ + int ctrl_interface_gid_set; + + char *ca_cert; + char *server_cert; + char *private_key; + char *private_key_passwd; + int check_crl; + char *dh_file; + u8 *pac_opaque_encr_key; + u8 *eap_fast_a_id; + size_t eap_fast_a_id_len; + char *eap_fast_a_id_info; + int eap_fast_prov; + int pac_key_lifetime; + int pac_key_refresh_time; + int eap_sim_aka_result_ind; + int tnc; + + char *radius_server_clients; + int radius_server_auth_port; + 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). + */ + + int ap_max_inactivity; + int ignore_broadcast_ssid; + + int wmm_enabled; + int wmm_uapsd; + + struct hostapd_vlan *vlan, *vlan_tail; + + macaddr bssid; + + /* + * Maximum listen interval that STAs can use when associating with this + * BSS. If a STA tries to use larger value, the association will be + * denied with status code 51. + */ + u16 max_listen_interval; + + int okc; /* Opportunistic Key Caching */ + + int wps_state; +#ifdef CONFIG_WPS + int ap_setup_locked; + u8 uuid[16]; + char *wps_pin_requests; + char *device_name; + char *manufacturer; + char *model_name; + char *model_number; + char *serial_number; + char *device_type; + char *config_methods; + u8 os_version[4]; + char *ap_pin; + int skip_cred_build; + u8 *extra_cred; + size_t extra_cred_len; + int wps_cred_processing; + u8 *ap_settings; + size_t ap_settings_len; + char *upnp_iface; + char *friendly_name; + char *manufacturer_url; + char *model_description; + char *model_url; + char *upc; +#endif /* CONFIG_WPS */ +}; + + +/** + * struct hostapd_config - Per-radio interface configuration + */ +struct hostapd_config { + struct hostapd_bss_config *bss, *last_bss; + size_t num_bss; + + u16 beacon_int; + int rts_threshold; + int fragm_threshold; + u8 send_probe_response; + u8 channel; + enum hostapd_hw_mode hw_mode; /* HOSTAPD_MODE_IEEE80211A, .. */ + enum { + LONG_PREAMBLE = 0, + SHORT_PREAMBLE = 1 + } preamble; + enum { + CTS_PROTECTION_AUTOMATIC = 0, + CTS_PROTECTION_FORCE_ENABLED = 1, + CTS_PROTECTION_FORCE_DISABLED = 2, + CTS_PROTECTION_AUTOMATIC_NO_OLBC = 3, + } cts_protection_type; + + int *supported_rates; + int *basic_rates; + + const struct wpa_driver_ops *driver; + + int ap_table_max_size; + int ap_table_expiration_time; + + char country[3]; /* first two octets: country code as described in + * ISO/IEC 3166-1. Third octet: + * ' ' (ascii 32): all environments + * 'O': Outdoor environemnt only + * 'I': Indoor environment only + */ + + int ieee80211d; + + struct hostapd_tx_queue_params tx_queue[NUM_TX_QUEUES]; + + /* + * WMM AC parameters, in same order as 802.1D, i.e. + * 0 = BE (best effort) + * 1 = BK (background) + * 2 = VI (video) + * 3 = VO (voice) + */ + struct hostapd_wmm_ac_params wmm_ac_params[4]; + + int ht_op_mode_fixed; + u16 ht_capab; + int ieee80211n; + int secondary_channel; +}; + + +int hostapd_mac_comp(const void *a, const void *b); +int hostapd_mac_comp_empty(const void *a); +struct hostapd_config * hostapd_config_defaults(void); +void hostapd_config_defaults_bss(struct hostapd_bss_config *bss); +void hostapd_config_free(struct hostapd_config *conf); +int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries, + const u8 *addr, int *vlan_id); +int hostapd_rate_found(int *list, int rate); +int hostapd_wep_key_cmp(struct hostapd_wep_keys *a, + struct hostapd_wep_keys *b); +const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf, + const u8 *addr, const u8 *prev_psk); +int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf); +const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, + int vlan_id); +const struct hostapd_eap_user * +hostapd_get_eap_user(const struct hostapd_bss_config *conf, const u8 *identity, + size_t identity_len, int phase2); + +#endif /* HOSTAPD_CONFIG_H */ diff --git a/contrib/wpa/src/ap/ap_drv_ops.c b/contrib/wpa/src/ap/ap_drv_ops.c new file mode 100644 index 0000000..f264a3e --- /dev/null +++ b/contrib/wpa/src/ap/ap_drv_ops.c @@ -0,0 +1,621 @@ +/* + * hostapd - Driver operations + * Copyright (c) 2009-2010, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published 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 "utils/includes.h" + +#include "utils/common.h" +#include "drivers/driver.h" +#include "common/ieee802_11_defs.h" +#include "hostapd.h" +#include "ieee802_11.h" +#include "sta_info.h" +#include "ap_config.h" +#include "ap_drv_ops.h" + + +static int hostapd_sta_flags_to_drv(int flags) +{ + int res = 0; + if (flags & WLAN_STA_AUTHORIZED) + res |= WPA_STA_AUTHORIZED; + if (flags & WLAN_STA_WMM) + res |= WPA_STA_WMM; + if (flags & WLAN_STA_SHORT_PREAMBLE) + res |= WPA_STA_SHORT_PREAMBLE; + if (flags & WLAN_STA_MFP) + res |= WPA_STA_MFP; + return res; +} + + +static int hostapd_set_ap_wps_ie(struct hostapd_data *hapd) +{ + struct wpabuf *beacon, *proberesp; + int ret; + + if (hapd->driver == NULL || hapd->driver->set_ap_wps_ie == NULL) + return 0; + + beacon = hapd->wps_beacon_ie; + proberesp = hapd->wps_probe_resp_ie; + + ret = hapd->driver->set_ap_wps_ie(hapd->drv_priv, beacon, proberesp); + + return ret; +} + + +static int hostapd_send_mgmt_frame(struct hostapd_data *hapd, const void *msg, + size_t len) +{ + if (hapd->driver == NULL || hapd->driver->send_mlme == NULL) + return 0; + return hapd->driver->send_mlme(hapd->drv_priv, msg, len); +} + + +static int hostapd_send_eapol(struct hostapd_data *hapd, const u8 *addr, + const u8 *data, size_t data_len, int encrypt) +{ + if (hapd->driver == NULL || hapd->driver->hapd_send_eapol == NULL) + return 0; + return hapd->driver->hapd_send_eapol(hapd->drv_priv, addr, data, + data_len, encrypt, + hapd->own_addr); +} + + +static int hostapd_set_authorized(struct hostapd_data *hapd, + struct sta_info *sta, int authorized) +{ + if (authorized) { + return hostapd_sta_set_flags(hapd, sta->addr, + hostapd_sta_flags_to_drv( + sta->flags), + WPA_STA_AUTHORIZED, ~0); + } + + return hostapd_sta_set_flags(hapd, sta->addr, + hostapd_sta_flags_to_drv(sta->flags), + 0, ~WPA_STA_AUTHORIZED); +} + + +static int hostapd_set_key(const char *ifname, struct hostapd_data *hapd, + enum wpa_alg alg, const u8 *addr, int key_idx, + int set_tx, const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len) +{ + if (hapd->driver == NULL || hapd->driver->set_key == NULL) + return 0; + return hapd->driver->set_key(ifname, hapd->drv_priv, alg, addr, + key_idx, set_tx, seq, seq_len, key, + key_len); +} + + +static int hostapd_read_sta_data(struct hostapd_data *hapd, + struct hostap_sta_driver_data *data, + const u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->read_sta_data == NULL) + return -1; + return hapd->driver->read_sta_data(hapd->drv_priv, data, addr); +} + + +static int hostapd_sta_clear_stats(struct hostapd_data *hapd, const u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->sta_clear_stats == NULL) + return 0; + return hapd->driver->sta_clear_stats(hapd->drv_priv, addr); +} + + +static int hostapd_set_sta_flags(struct hostapd_data *hapd, + struct sta_info *sta) +{ + int set_flags, total_flags, flags_and, flags_or; + total_flags = hostapd_sta_flags_to_drv(sta->flags); + set_flags = WPA_STA_SHORT_PREAMBLE | WPA_STA_WMM | WPA_STA_MFP; + if (((!hapd->conf->ieee802_1x && !hapd->conf->wpa) || + sta->auth_alg == WLAN_AUTH_FT) && + sta->flags & WLAN_STA_AUTHORIZED) + set_flags |= WPA_STA_AUTHORIZED; + flags_or = total_flags & set_flags; + flags_and = total_flags | ~set_flags; + return hostapd_sta_set_flags(hapd, sta->addr, total_flags, + flags_or, flags_and); +} + + +static int hostapd_set_drv_ieee8021x(struct hostapd_data *hapd, + const char *ifname, int enabled) +{ + struct wpa_bss_params params; + os_memset(¶ms, 0, sizeof(params)); + params.ifname = ifname; + params.enabled = enabled; + if (enabled) { + params.wpa = hapd->conf->wpa; + params.ieee802_1x = hapd->conf->ieee802_1x; + params.wpa_group = hapd->conf->wpa_group; + params.wpa_pairwise = hapd->conf->wpa_pairwise; + params.wpa_key_mgmt = hapd->conf->wpa_key_mgmt; + params.rsn_preauth = hapd->conf->rsn_preauth; + } + return hostapd_set_ieee8021x(hapd, ¶ms); +} + + +static int hostapd_set_radius_acl_auth(struct hostapd_data *hapd, + const u8 *mac, int accepted, + u32 session_timeout) +{ + if (hapd->driver == NULL || hapd->driver->set_radius_acl_auth == NULL) + return 0; + return hapd->driver->set_radius_acl_auth(hapd->drv_priv, mac, accepted, + session_timeout); +} + + +static int hostapd_set_radius_acl_expire(struct hostapd_data *hapd, + const u8 *mac) +{ + if (hapd->driver == NULL || + hapd->driver->set_radius_acl_expire == NULL) + return 0; + return hapd->driver->set_radius_acl_expire(hapd->drv_priv, mac); +} + + +static int hostapd_set_bss_params(struct hostapd_data *hapd, + int use_protection) +{ + int ret = 0; + int preamble; +#ifdef CONFIG_IEEE80211N + u8 buf[60], *ht_capab, *ht_oper, *pos; + + pos = buf; + ht_capab = pos; + pos = hostapd_eid_ht_capabilities(hapd, pos); + ht_oper = pos; + pos = hostapd_eid_ht_operation(hapd, pos); + if (pos > ht_oper && ht_oper > ht_capab && + hostapd_set_ht_params(hapd, ht_capab + 2, ht_capab[1], + ht_oper + 2, ht_oper[1])) { + wpa_printf(MSG_ERROR, "Could not set HT capabilities " + "for kernel driver"); + ret = -1; + } + +#endif /* CONFIG_IEEE80211N */ + + if (hostapd_set_cts_protect(hapd, use_protection)) { + wpa_printf(MSG_ERROR, "Failed to set CTS protect in kernel " + "driver"); + ret = -1; + } + + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G && + hostapd_set_short_slot_time(hapd, + hapd->iface->num_sta_no_short_slot_time + > 0 ? 0 : 1)) { + wpa_printf(MSG_ERROR, "Failed to set Short Slot Time option " + "in kernel driver"); + ret = -1; + } + + if (hapd->iface->num_sta_no_short_preamble == 0 && + hapd->iconf->preamble == SHORT_PREAMBLE) + preamble = SHORT_PREAMBLE; + else + preamble = LONG_PREAMBLE; + if (hostapd_set_preamble(hapd, preamble)) { + wpa_printf(MSG_ERROR, "Could not set preamble for kernel " + "driver"); + ret = -1; + } + + return ret; +} + + +static int hostapd_set_beacon(struct hostapd_data *hapd, + const u8 *head, size_t head_len, + const u8 *tail, size_t tail_len, int dtim_period, + int beacon_int) +{ + if (hapd->driver == NULL || hapd->driver->set_beacon == NULL) + return 0; + return hapd->driver->set_beacon(hapd->drv_priv, + head, head_len, tail, tail_len, + dtim_period, beacon_int); +} + + +static int hostapd_vlan_if_add(struct hostapd_data *hapd, const char *ifname) +{ + char force_ifname[IFNAMSIZ]; + u8 if_addr[ETH_ALEN]; + return hostapd_if_add(hapd, WPA_IF_AP_VLAN, ifname, NULL, NULL, NULL, + force_ifname, if_addr); +} + +static int hostapd_vlan_if_remove(struct hostapd_data *hapd, + const char *ifname) +{ + return hostapd_if_remove(hapd, WPA_IF_AP_VLAN, ifname); +} + + +static int hostapd_set_wds_sta(struct hostapd_data *hapd, const u8 *addr, + int aid, int val) +{ + if (hapd->driver == NULL || hapd->driver->set_wds_sta == NULL) + return 0; + return hapd->driver->set_wds_sta(hapd->drv_priv, addr, aid, val); +} + + +static int hostapd_set_sta_vlan(const char *ifname, struct hostapd_data *hapd, + const u8 *addr, int vlan_id) +{ + if (hapd->driver == NULL || hapd->driver->set_sta_vlan == NULL) + return 0; + return hapd->driver->set_sta_vlan(hapd->drv_priv, addr, ifname, + vlan_id); +} + + +static int hostapd_get_inact_sec(struct hostapd_data *hapd, const u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->get_inact_sec == NULL) + return 0; + return hapd->driver->get_inact_sec(hapd->drv_priv, addr); +} + + +static int hostapd_sta_deauth(struct hostapd_data *hapd, const u8 *addr, + int reason) +{ + if (hapd->driver == NULL || hapd->driver->sta_deauth == NULL) + return 0; + return hapd->driver->sta_deauth(hapd->drv_priv, hapd->own_addr, addr, + reason); +} + + +static int hostapd_sta_disassoc(struct hostapd_data *hapd, const u8 *addr, + int reason) +{ + if (hapd->driver == NULL || hapd->driver->sta_disassoc == NULL) + return 0; + return hapd->driver->sta_disassoc(hapd->drv_priv, hapd->own_addr, addr, + reason); +} + + +static int hostapd_sta_add(struct hostapd_data *hapd, + const u8 *addr, u16 aid, u16 capability, + const u8 *supp_rates, size_t supp_rates_len, + u16 listen_interval, + const struct ieee80211_ht_capabilities *ht_capab) +{ + struct hostapd_sta_add_params params; + + if (hapd->driver == NULL) + return 0; + if (hapd->driver->sta_add == NULL) + return 0; + + os_memset(¶ms, 0, sizeof(params)); + params.addr = addr; + params.aid = aid; + params.capability = capability; + params.supp_rates = supp_rates; + params.supp_rates_len = supp_rates_len; + params.listen_interval = listen_interval; + params.ht_capabilities = ht_capab; + return hapd->driver->sta_add(hapd->drv_priv, ¶ms); +} + + +static int hostapd_sta_remove(struct hostapd_data *hapd, const u8 *addr) +{ + if (hapd->driver == NULL || hapd->driver->sta_remove == NULL) + return 0; + return hapd->driver->sta_remove(hapd->drv_priv, addr); +} + + +static int hostapd_set_countermeasures(struct hostapd_data *hapd, int enabled) +{ + if (hapd->driver == NULL || + hapd->driver->hapd_set_countermeasures == NULL) + return 0; + return hapd->driver->hapd_set_countermeasures(hapd->drv_priv, enabled); +} + + +void hostapd_set_driver_ops(struct hostapd_driver_ops *ops) +{ + ops->set_ap_wps_ie = hostapd_set_ap_wps_ie; + ops->send_mgmt_frame = hostapd_send_mgmt_frame; + ops->send_eapol = hostapd_send_eapol; + ops->set_authorized = hostapd_set_authorized; + ops->set_key = hostapd_set_key; + ops->read_sta_data = hostapd_read_sta_data; + ops->sta_clear_stats = hostapd_sta_clear_stats; + ops->set_sta_flags = hostapd_set_sta_flags; + ops->set_drv_ieee8021x = hostapd_set_drv_ieee8021x; + ops->set_radius_acl_auth = hostapd_set_radius_acl_auth; + ops->set_radius_acl_expire = hostapd_set_radius_acl_expire; + ops->set_bss_params = hostapd_set_bss_params; + ops->set_beacon = hostapd_set_beacon; + ops->vlan_if_add = hostapd_vlan_if_add; + ops->vlan_if_remove = hostapd_vlan_if_remove; + ops->set_wds_sta = hostapd_set_wds_sta; + ops->set_sta_vlan = hostapd_set_sta_vlan; + ops->get_inact_sec = hostapd_get_inact_sec; + ops->sta_deauth = hostapd_sta_deauth; + ops->sta_disassoc = hostapd_sta_disassoc; + ops->sta_add = hostapd_sta_add; + ops->sta_remove = hostapd_sta_remove; + ops->set_countermeasures = hostapd_set_countermeasures; +} + + +int hostapd_set_privacy(struct hostapd_data *hapd, int enabled) +{ + if (hapd->driver == NULL || hapd->driver->set_privacy == NULL) + return 0; + return hapd->driver->set_privacy(hapd->drv_priv, enabled); +} + + +int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem, + size_t elem_len) +{ + if (hapd->driver == NULL || hapd->driver->set_generic_elem == NULL) + return 0; + return hapd->driver->set_generic_elem(hapd->drv_priv, elem, elem_len); +} + + +int hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len) +{ + if (hapd->driver == NULL || hapd->driver->hapd_get_ssid == NULL) + return 0; + return hapd->driver->hapd_get_ssid(hapd->drv_priv, buf, len); +} + + +int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len) +{ + if (hapd->driver == NULL || hapd->driver->hapd_set_ssid == NULL) + return 0; + return hapd->driver->hapd_set_ssid(hapd->drv_priv, buf, len); +} + + +int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type, + const char *ifname, const u8 *addr, void *bss_ctx, + void **drv_priv, char *force_ifname, u8 *if_addr) +{ + if (hapd->driver == NULL || hapd->driver->if_add == NULL) + return -1; + return hapd->driver->if_add(hapd->drv_priv, type, ifname, addr, + bss_ctx, drv_priv, force_ifname, if_addr); +} + + +int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type, + const char *ifname) +{ + if (hapd->driver == NULL || hapd->driver->if_remove == NULL) + return -1; + return hapd->driver->if_remove(hapd->drv_priv, type, ifname); +} + + +int hostapd_set_ieee8021x(struct hostapd_data *hapd, + struct wpa_bss_params *params) +{ + if (hapd->driver == NULL || hapd->driver->set_ieee8021x == NULL) + return 0; + return hapd->driver->set_ieee8021x(hapd->drv_priv, params); +} + + +int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd, + const u8 *addr, int idx, u8 *seq) +{ + if (hapd->driver == NULL || hapd->driver->get_seqnum == NULL) + return 0; + return hapd->driver->get_seqnum(ifname, hapd->drv_priv, addr, idx, + seq); +} + + +int hostapd_flush(struct hostapd_data *hapd) +{ + if (hapd->driver == NULL || hapd->driver->flush == NULL) + return 0; + return hapd->driver->flush(hapd->drv_priv); +} + + +int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq, + int channel, int ht_enabled, int sec_channel_offset) +{ + struct hostapd_freq_params data; + if (hapd->driver == NULL) + return 0; + if (hapd->driver->set_freq == NULL) + return 0; + os_memset(&data, 0, sizeof(data)); + data.mode = mode; + data.freq = freq; + data.channel = channel; + data.ht_enabled = ht_enabled; + data.sec_channel_offset = sec_channel_offset; + return hapd->driver->set_freq(hapd->drv_priv, &data); +} + +int hostapd_set_rts(struct hostapd_data *hapd, int rts) +{ + if (hapd->driver == NULL || hapd->driver->set_rts == NULL) + return 0; + return hapd->driver->set_rts(hapd->drv_priv, rts); +} + + +int hostapd_set_frag(struct hostapd_data *hapd, int frag) +{ + if (hapd->driver == NULL || hapd->driver->set_frag == NULL) + return 0; + return hapd->driver->set_frag(hapd->drv_priv, frag); +} + + +int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr, + int total_flags, int flags_or, int flags_and) +{ + if (hapd->driver == NULL || hapd->driver->sta_set_flags == NULL) + return 0; + return hapd->driver->sta_set_flags(hapd->drv_priv, addr, total_flags, + flags_or, flags_and); +} + + +int hostapd_set_rate_sets(struct hostapd_data *hapd, int *supp_rates, + int *basic_rates, int mode) +{ + if (hapd->driver == NULL || hapd->driver->set_rate_sets == NULL) + return 0; + return hapd->driver->set_rate_sets(hapd->drv_priv, supp_rates, + basic_rates, mode); +} + + +int hostapd_set_country(struct hostapd_data *hapd, const char *country) +{ + if (hapd->driver == NULL || + hapd->driver->set_country == NULL) + return 0; + return hapd->driver->set_country(hapd->drv_priv, country); +} + + +int hostapd_set_cts_protect(struct hostapd_data *hapd, int value) +{ + if (hapd->driver == NULL || hapd->driver->set_cts_protect == NULL) + return 0; + return hapd->driver->set_cts_protect(hapd->drv_priv, value); +} + + +int hostapd_set_preamble(struct hostapd_data *hapd, int value) +{ + if (hapd->driver == NULL || hapd->driver->set_preamble == NULL) + return 0; + return hapd->driver->set_preamble(hapd->drv_priv, value); +} + + +int hostapd_set_short_slot_time(struct hostapd_data *hapd, int value) +{ + if (hapd->driver == NULL || hapd->driver->set_short_slot_time == NULL) + return 0; + return hapd->driver->set_short_slot_time(hapd->drv_priv, value); +} + + +int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs, + int cw_min, int cw_max, int burst_time) +{ + if (hapd->driver == NULL || hapd->driver->set_tx_queue_params == NULL) + return 0; + return hapd->driver->set_tx_queue_params(hapd->drv_priv, queue, aifs, + cw_min, cw_max, burst_time); +} + + +int hostapd_valid_bss_mask(struct hostapd_data *hapd, const u8 *addr, + const u8 *mask) +{ + if (hapd->driver == NULL || hapd->driver->valid_bss_mask == NULL) + return 1; + return hapd->driver->valid_bss_mask(hapd->drv_priv, addr, mask); +} + + +struct hostapd_hw_modes * +hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes, + u16 *flags) +{ + if (hapd->driver == NULL || + hapd->driver->get_hw_feature_data == NULL) + return NULL; + return hapd->driver->get_hw_feature_data(hapd->drv_priv, num_modes, + flags); +} + + +int hostapd_driver_commit(struct hostapd_data *hapd) +{ + if (hapd->driver == NULL || hapd->driver->commit == NULL) + return 0; + return hapd->driver->commit(hapd->drv_priv); +} + + +int hostapd_set_ht_params(struct hostapd_data *hapd, + const u8 *ht_capab, size_t ht_capab_len, + const u8 *ht_oper, size_t ht_oper_len) +{ + if (hapd->driver == NULL || hapd->driver->set_ht_params == NULL || + ht_capab == NULL || ht_oper == NULL) + return 0; + return hapd->driver->set_ht_params(hapd->drv_priv, + ht_capab, ht_capab_len, + ht_oper, ht_oper_len); +} + + +int hostapd_drv_none(struct hostapd_data *hapd) +{ + return hapd->driver && os_strcmp(hapd->driver->name, "none") == 0; +} + + +int hostapd_driver_scan(struct hostapd_data *hapd, + struct wpa_driver_scan_params *params) +{ + if (hapd->driver && hapd->driver->scan2) + return hapd->driver->scan2(hapd->drv_priv, params); + return -1; +} + + +struct wpa_scan_results * hostapd_driver_get_scan_results( + struct hostapd_data *hapd) +{ + if (hapd->driver && hapd->driver->get_scan_results2) + return hapd->driver->get_scan_results2(hapd->drv_priv); + return NULL; +} diff --git a/contrib/wpa/src/ap/ap_drv_ops.h b/contrib/wpa/src/ap/ap_drv_ops.h new file mode 100644 index 0000000..9b75d09 --- /dev/null +++ b/contrib/wpa/src/ap/ap_drv_ops.h @@ -0,0 +1,67 @@ +/* + * hostapd - Driver operations + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef AP_DRV_OPS +#define AP_DRV_OPS + +enum wpa_driver_if_type; +struct wpa_bss_params; +struct wpa_driver_scan_params; + +void hostapd_set_driver_ops(struct hostapd_driver_ops *ops); +int hostapd_set_privacy(struct hostapd_data *hapd, int enabled); +int hostapd_set_generic_elem(struct hostapd_data *hapd, const u8 *elem, + size_t elem_len); +int hostapd_get_ssid(struct hostapd_data *hapd, u8 *buf, size_t len); +int hostapd_set_ssid(struct hostapd_data *hapd, const u8 *buf, size_t len); +int hostapd_if_add(struct hostapd_data *hapd, enum wpa_driver_if_type type, + const char *ifname, const u8 *addr, void *bss_ctx, + void **drv_priv, char *force_ifname, u8 *if_addr); +int hostapd_if_remove(struct hostapd_data *hapd, enum wpa_driver_if_type type, + const char *ifname); +int hostapd_set_ieee8021x(struct hostapd_data *hapd, + struct wpa_bss_params *params); +int hostapd_get_seqnum(const char *ifname, struct hostapd_data *hapd, + const u8 *addr, int idx, u8 *seq); +int hostapd_flush(struct hostapd_data *hapd); +int hostapd_set_freq(struct hostapd_data *hapd, int mode, int freq, + int channel, int ht_enabled, int sec_channel_offset); +int hostapd_set_rts(struct hostapd_data *hapd, int rts); +int hostapd_set_frag(struct hostapd_data *hapd, int frag); +int hostapd_sta_set_flags(struct hostapd_data *hapd, u8 *addr, + int total_flags, int flags_or, int flags_and); +int hostapd_set_rate_sets(struct hostapd_data *hapd, int *supp_rates, + int *basic_rates, int mode); +int hostapd_set_country(struct hostapd_data *hapd, const char *country); +int hostapd_set_cts_protect(struct hostapd_data *hapd, int value); +int hostapd_set_preamble(struct hostapd_data *hapd, int value); +int hostapd_set_short_slot_time(struct hostapd_data *hapd, int value); +int hostapd_set_tx_queue_params(struct hostapd_data *hapd, int queue, int aifs, + int cw_min, int cw_max, int burst_time); +int hostapd_valid_bss_mask(struct hostapd_data *hapd, const u8 *addr, + const u8 *mask); +struct hostapd_hw_modes * +hostapd_get_hw_feature_data(struct hostapd_data *hapd, u16 *num_modes, + u16 *flags); +int hostapd_driver_commit(struct hostapd_data *hapd); +int hostapd_set_ht_params(struct hostapd_data *hapd, + const u8 *ht_capab, size_t ht_capab_len, + const u8 *ht_oper, size_t ht_oper_len); +int hostapd_drv_none(struct hostapd_data *hapd); +int hostapd_driver_scan(struct hostapd_data *hapd, + struct wpa_driver_scan_params *params); +struct wpa_scan_results * hostapd_driver_get_scan_results( + struct hostapd_data *hapd); + +#endif /* AP_DRV_OPS */ diff --git a/contrib/wpa/src/ap/ap_list.c b/contrib/wpa/src/ap/ap_list.c new file mode 100644 index 0000000..5297dbf --- /dev/null +++ b/contrib/wpa/src/ap/ap_list.c @@ -0,0 +1,397 @@ +/* + * hostapd / AP table + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2004, Instant802 Networks, Inc. + * Copyright (c) 2006, Devicescape Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "drivers/driver.h" +#include "hostapd.h" +#include "ap_config.h" +#include "ieee802_11.h" +#include "sta_info.h" +#include "beacon.h" +#include "ap_list.h" + + +/* AP list is a double linked list with head->prev pointing to the end of the + * list and tail->next = NULL. Entries are moved to the head of the list + * whenever a beacon has been received from the AP in question. The tail entry + * in this link will thus be the least recently used entry. */ + + +static int ap_list_beacon_olbc(struct hostapd_iface *iface, struct ap_info *ap) +{ + int i; + + if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G || + iface->conf->channel != ap->channel) + return 0; + + if (ap->erp != -1 && (ap->erp & ERP_INFO_NON_ERP_PRESENT)) + return 1; + + for (i = 0; i < WLAN_SUPP_RATES_MAX; i++) { + int rate = (ap->supported_rates[i] & 0x7f) * 5; + if (rate == 60 || rate == 90 || rate > 110) + return 0; + } + + return 1; +} + + +struct ap_info * ap_get_ap(struct hostapd_iface *iface, const u8 *ap) +{ + struct ap_info *s; + + s = iface->ap_hash[STA_HASH(ap)]; + while (s != NULL && os_memcmp(s->addr, ap, ETH_ALEN) != 0) + s = s->hnext; + return s; +} + + +static void ap_ap_list_add(struct hostapd_iface *iface, struct ap_info *ap) +{ + if (iface->ap_list) { + ap->prev = iface->ap_list->prev; + iface->ap_list->prev = ap; + } else + ap->prev = ap; + ap->next = iface->ap_list; + iface->ap_list = ap; +} + + +static void ap_ap_list_del(struct hostapd_iface *iface, struct ap_info *ap) +{ + if (iface->ap_list == ap) + iface->ap_list = ap->next; + else + ap->prev->next = ap->next; + + if (ap->next) + ap->next->prev = ap->prev; + else if (iface->ap_list) + iface->ap_list->prev = ap->prev; +} + + +static void ap_ap_iter_list_add(struct hostapd_iface *iface, + struct ap_info *ap) +{ + if (iface->ap_iter_list) { + ap->iter_prev = iface->ap_iter_list->iter_prev; + iface->ap_iter_list->iter_prev = ap; + } else + ap->iter_prev = ap; + ap->iter_next = iface->ap_iter_list; + iface->ap_iter_list = ap; +} + + +static void ap_ap_iter_list_del(struct hostapd_iface *iface, + struct ap_info *ap) +{ + if (iface->ap_iter_list == ap) + iface->ap_iter_list = ap->iter_next; + else + ap->iter_prev->iter_next = ap->iter_next; + + if (ap->iter_next) + ap->iter_next->iter_prev = ap->iter_prev; + else if (iface->ap_iter_list) + iface->ap_iter_list->iter_prev = ap->iter_prev; +} + + +static void ap_ap_hash_add(struct hostapd_iface *iface, struct ap_info *ap) +{ + ap->hnext = iface->ap_hash[STA_HASH(ap->addr)]; + iface->ap_hash[STA_HASH(ap->addr)] = ap; +} + + +static void ap_ap_hash_del(struct hostapd_iface *iface, struct ap_info *ap) +{ + struct ap_info *s; + + s = iface->ap_hash[STA_HASH(ap->addr)]; + if (s == NULL) return; + if (os_memcmp(s->addr, ap->addr, ETH_ALEN) == 0) { + iface->ap_hash[STA_HASH(ap->addr)] = s->hnext; + return; + } + + while (s->hnext != NULL && + os_memcmp(s->hnext->addr, ap->addr, ETH_ALEN) != 0) + s = s->hnext; + if (s->hnext != NULL) + s->hnext = s->hnext->hnext; + else + printf("AP: could not remove AP " MACSTR " from hash table\n", + MAC2STR(ap->addr)); +} + + +static void ap_free_ap(struct hostapd_iface *iface, struct ap_info *ap) +{ + ap_ap_hash_del(iface, ap); + ap_ap_list_del(iface, ap); + ap_ap_iter_list_del(iface, ap); + + iface->num_ap--; + os_free(ap); +} + + +static void hostapd_free_aps(struct hostapd_iface *iface) +{ + struct ap_info *ap, *prev; + + ap = iface->ap_list; + + while (ap) { + prev = ap; + ap = ap->next; + ap_free_ap(iface, prev); + } + + iface->ap_list = NULL; +} + + +int ap_ap_for_each(struct hostapd_iface *iface, + int (*func)(struct ap_info *s, void *data), void *data) +{ + struct ap_info *s; + int ret = 0; + + s = iface->ap_list; + + while (s) { + ret = func(s, data); + if (ret) + break; + s = s->next; + } + + return ret; +} + + +static struct ap_info * ap_ap_add(struct hostapd_iface *iface, const u8 *addr) +{ + struct ap_info *ap; + + ap = os_zalloc(sizeof(struct ap_info)); + if (ap == NULL) + return NULL; + + /* initialize AP info data */ + os_memcpy(ap->addr, addr, ETH_ALEN); + ap_ap_list_add(iface, ap); + iface->num_ap++; + ap_ap_hash_add(iface, ap); + ap_ap_iter_list_add(iface, ap); + + if (iface->num_ap > iface->conf->ap_table_max_size && ap != ap->prev) { + wpa_printf(MSG_DEBUG, "Removing the least recently used AP " + MACSTR " from AP table", MAC2STR(ap->prev->addr)); + ap_free_ap(iface, ap->prev); + } + + return ap; +} + + +void ap_list_process_beacon(struct hostapd_iface *iface, + const struct ieee80211_mgmt *mgmt, + struct ieee802_11_elems *elems, + struct hostapd_frame_info *fi) +{ + struct ap_info *ap; + int new_ap = 0; + size_t len; + int set_beacon = 0; + + if (iface->conf->ap_table_max_size < 1) + return; + + ap = ap_get_ap(iface, mgmt->bssid); + if (!ap) { + ap = ap_ap_add(iface, mgmt->bssid); + if (!ap) { + printf("Failed to allocate AP information entry\n"); + return; + } + new_ap = 1; + } + + ap->beacon_int = le_to_host16(mgmt->u.beacon.beacon_int); + ap->capability = le_to_host16(mgmt->u.beacon.capab_info); + + if (elems->ssid) { + len = elems->ssid_len; + if (len >= sizeof(ap->ssid)) + len = sizeof(ap->ssid) - 1; + os_memcpy(ap->ssid, elems->ssid, len); + ap->ssid[len] = '\0'; + ap->ssid_len = len; + } + + os_memset(ap->supported_rates, 0, WLAN_SUPP_RATES_MAX); + len = 0; + if (elems->supp_rates) { + len = elems->supp_rates_len; + if (len > WLAN_SUPP_RATES_MAX) + len = WLAN_SUPP_RATES_MAX; + os_memcpy(ap->supported_rates, elems->supp_rates, len); + } + if (elems->ext_supp_rates) { + int len2; + if (len + elems->ext_supp_rates_len > WLAN_SUPP_RATES_MAX) + len2 = WLAN_SUPP_RATES_MAX - len; + else + len2 = elems->ext_supp_rates_len; + os_memcpy(ap->supported_rates + len, elems->ext_supp_rates, + len2); + } + + ap->wpa = elems->wpa_ie != NULL; + + if (elems->erp_info && elems->erp_info_len == 1) + ap->erp = elems->erp_info[0]; + else + ap->erp = -1; + + if (elems->ds_params && elems->ds_params_len == 1) + ap->channel = elems->ds_params[0]; + else if (fi) + ap->channel = fi->channel; + + if (elems->ht_capabilities) + ap->ht_support = 1; + else + ap->ht_support = 0; + + ap->num_beacons++; + time(&ap->last_beacon); + if (fi) { + ap->ssi_signal = fi->ssi_signal; + ap->datarate = fi->datarate; + } + + if (!new_ap && ap != iface->ap_list) { + /* move AP entry into the beginning of the list so that the + * oldest entry is always in the end of the list */ + ap_ap_list_del(iface, ap); + ap_ap_list_add(iface, ap); + } + + if (!iface->olbc && + ap_list_beacon_olbc(iface, ap)) { + iface->olbc = 1; + wpa_printf(MSG_DEBUG, "OLBC AP detected: " MACSTR " - enable " + "protection", MAC2STR(ap->addr)); + set_beacon++; + } + +#ifdef CONFIG_IEEE80211N + if (!iface->olbc_ht && !ap->ht_support) { + iface->olbc_ht = 1; + hostapd_ht_operation_update(iface); + wpa_printf(MSG_DEBUG, "OLBC HT AP detected: " MACSTR + " - enable protection", MAC2STR(ap->addr)); + set_beacon++; + } +#endif /* CONFIG_IEEE80211N */ + + if (set_beacon) + ieee802_11_set_beacons(iface); +} + + +static void ap_list_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_iface *iface = eloop_ctx; + time_t now; + struct ap_info *ap; + int set_beacon = 0; + + eloop_register_timeout(10, 0, ap_list_timer, iface, NULL); + + if (!iface->ap_list) + return; + + time(&now); + + while (iface->ap_list) { + ap = iface->ap_list->prev; + if (ap->last_beacon + iface->conf->ap_table_expiration_time >= + now) + break; + + ap_free_ap(iface, ap); + } + + if (iface->olbc || iface->olbc_ht) { + int olbc = 0; + int olbc_ht = 0; + + ap = iface->ap_list; + while (ap && (olbc == 0 || olbc_ht == 0)) { + if (ap_list_beacon_olbc(iface, ap)) + olbc = 1; + if (!ap->ht_support) + olbc_ht = 1; + ap = ap->next; + } + if (!olbc && iface->olbc) { + wpa_printf(MSG_DEBUG, "OLBC not detected anymore"); + iface->olbc = 0; + set_beacon++; + } +#ifdef CONFIG_IEEE80211N + if (!olbc_ht && iface->olbc_ht) { + wpa_printf(MSG_DEBUG, "OLBC HT not detected anymore"); + iface->olbc_ht = 0; + hostapd_ht_operation_update(iface); + set_beacon++; + } +#endif /* CONFIG_IEEE80211N */ + } + + if (set_beacon) + ieee802_11_set_beacons(iface); +} + + +int ap_list_init(struct hostapd_iface *iface) +{ + eloop_register_timeout(10, 0, ap_list_timer, iface, NULL); + return 0; +} + + +void ap_list_deinit(struct hostapd_iface *iface) +{ + eloop_cancel_timeout(ap_list_timer, iface, NULL); + hostapd_free_aps(iface); +} diff --git a/contrib/wpa/src/ap/ap_list.h b/contrib/wpa/src/ap/ap_list.h new file mode 100644 index 0000000..f49f58b --- /dev/null +++ b/contrib/wpa/src/ap/ap_list.h @@ -0,0 +1,78 @@ +/* + * hostapd / AP table + * Copyright (c) 2002-2003, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2004, Instant802 Networks, Inc. + * Copyright (c) 2006, Devicescape Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef AP_LIST_H +#define AP_LIST_H + +struct ap_info { + /* Note: next/prev pointers are updated whenever a new beacon is + * received because these are used to find the least recently used + * entries. iter_next/iter_prev are updated only when adding new BSSes + * and when removing old ones. These should be used when iterating + * through the table in a manner that allows beacons to be received + * during the iteration. */ + struct ap_info *next; /* next entry in AP list */ + struct ap_info *prev; /* previous entry in AP list */ + struct ap_info *hnext; /* next entry in hash table list */ + struct ap_info *iter_next; /* next entry in AP iteration list */ + struct ap_info *iter_prev; /* previous entry in AP iteration list */ + u8 addr[6]; + u16 beacon_int; + u16 capability; + u8 supported_rates[WLAN_SUPP_RATES_MAX]; + u8 ssid[33]; + size_t ssid_len; + int wpa; + int erp; /* ERP Info or -1 if ERP info element not present */ + + int channel; + int datarate; /* in 100 kbps */ + int ssi_signal; + + int ht_support; + + unsigned int num_beacons; /* number of beacon frames received */ + time_t last_beacon; + + int already_seen; /* whether API call AP-NEW has already fetched + * information about this AP */ +}; + +struct ieee802_11_elems; +struct hostapd_frame_info; + +struct ap_info * ap_get_ap(struct hostapd_iface *iface, const u8 *sta); +int ap_ap_for_each(struct hostapd_iface *iface, + int (*func)(struct ap_info *s, void *data), void *data); +void ap_list_process_beacon(struct hostapd_iface *iface, + const struct ieee80211_mgmt *mgmt, + struct ieee802_11_elems *elems, + struct hostapd_frame_info *fi); +#ifdef NEED_AP_MLME +int ap_list_init(struct hostapd_iface *iface); +void ap_list_deinit(struct hostapd_iface *iface); +#else /* NEED_AP_MLME */ +static inline int ap_list_init(struct hostapd_iface *iface) +{ + return 0; +} + +static inline void ap_list_deinit(struct hostapd_iface *iface) +{ +} +#endif /* NEED_AP_MLME */ + +#endif /* AP_LIST_H */ diff --git a/contrib/wpa/src/ap/ap_mlme.c b/contrib/wpa/src/ap/ap_mlme.c new file mode 100644 index 0000000..2b09b11 --- /dev/null +++ b/contrib/wpa/src/ap/ap_mlme.c @@ -0,0 +1,184 @@ +/* + * hostapd / IEEE 802.11 MLME + * Copyright 2003-2006, Jouni Malinen <j@w1.fi> + * Copyright 2003-2004, Instant802 Networks, Inc. + * Copyright 2005-2006, Devicescape Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "ieee802_11.h" +#include "wpa_auth.h" +#include "sta_info.h" +#include "ap_mlme.h" + + +#ifndef CONFIG_NO_HOSTAPD_LOGGER +static const char * mlme_auth_alg_str(int alg) +{ + switch (alg) { + case WLAN_AUTH_OPEN: + return "OPEN_SYSTEM"; + case WLAN_AUTH_SHARED_KEY: + return "SHARED_KEY"; + case WLAN_AUTH_FT: + return "FT"; + } + + return "unknown"; +} +#endif /* CONFIG_NO_HOSTAPD_LOGGER */ + + +/** + * mlme_authenticate_indication - Report the establishment of an authentication + * relationship with a specific peer MAC entity + * @hapd: BSS data + * @sta: peer STA data + * + * MLME calls this function as a result of the establishment of an + * authentication relationship with a specific peer MAC entity that + * resulted from an authentication procedure that was initiated by + * that specific peer MAC entity. + * + * PeerSTAAddress = sta->addr + * AuthenticationType = sta->auth_alg (WLAN_AUTH_OPEN / WLAN_AUTH_SHARED_KEY) + */ +void mlme_authenticate_indication(struct hostapd_data *hapd, + struct sta_info *sta) +{ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME, + HOSTAPD_LEVEL_DEBUG, + "MLME-AUTHENTICATE.indication(" MACSTR ", %s)", + MAC2STR(sta->addr), mlme_auth_alg_str(sta->auth_alg)); + if (sta->auth_alg != WLAN_AUTH_FT && !(sta->flags & WLAN_STA_MFP)) + mlme_deletekeys_request(hapd, sta); +} + + +/** + * mlme_deauthenticate_indication - Report the invalidation of an + * authentication relationship with a specific peer MAC entity + * @hapd: BSS data + * @sta: Peer STA data + * @reason_code: ReasonCode from Deauthentication frame + * + * MLME calls this function as a result of the invalidation of an + * authentication relationship with a specific peer MAC entity. + * + * PeerSTAAddress = sta->addr + */ +void mlme_deauthenticate_indication(struct hostapd_data *hapd, + struct sta_info *sta, u16 reason_code) +{ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME, + HOSTAPD_LEVEL_DEBUG, + "MLME-DEAUTHENTICATE.indication(" MACSTR ", %d)", + MAC2STR(sta->addr), reason_code); + mlme_deletekeys_request(hapd, sta); +} + + +/** + * mlme_associate_indication - Report the establishment of an association with + * a specific peer MAC entity + * @hapd: BSS data + * @sta: peer STA data + * + * MLME calls this function as a result of the establishment of an + * association with a specific peer MAC entity that resulted from an + * association procedure that was initiated by that specific peer MAC entity. + * + * PeerSTAAddress = sta->addr + */ +void mlme_associate_indication(struct hostapd_data *hapd, struct sta_info *sta) +{ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME, + HOSTAPD_LEVEL_DEBUG, + "MLME-ASSOCIATE.indication(" MACSTR ")", + MAC2STR(sta->addr)); + if (sta->auth_alg != WLAN_AUTH_FT) + mlme_deletekeys_request(hapd, sta); +} + + +/** + * mlme_reassociate_indication - Report the establishment of an reassociation + * with a specific peer MAC entity + * @hapd: BSS data + * @sta: peer STA data + * + * MLME calls this function as a result of the establishment of an + * reassociation with a specific peer MAC entity that resulted from a + * reassociation procedure that was initiated by that specific peer MAC entity. + * + * PeerSTAAddress = sta->addr + * + * sta->previous_ap contains the "Current AP" information from ReassocReq. + */ +void mlme_reassociate_indication(struct hostapd_data *hapd, + struct sta_info *sta) +{ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME, + HOSTAPD_LEVEL_DEBUG, + "MLME-REASSOCIATE.indication(" MACSTR ")", + MAC2STR(sta->addr)); + if (sta->auth_alg != WLAN_AUTH_FT) + mlme_deletekeys_request(hapd, sta); +} + + +/** + * mlme_disassociate_indication - Report disassociation with a specific peer + * MAC entity + * @hapd: BSS data + * @sta: Peer STA data + * @reason_code: ReasonCode from Disassociation frame + * + * MLME calls this function as a result of the invalidation of an association + * relationship with a specific peer MAC entity. + * + * PeerSTAAddress = sta->addr + */ +void mlme_disassociate_indication(struct hostapd_data *hapd, + struct sta_info *sta, u16 reason_code) +{ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME, + HOSTAPD_LEVEL_DEBUG, + "MLME-DISASSOCIATE.indication(" MACSTR ", %d)", + MAC2STR(sta->addr), reason_code); + mlme_deletekeys_request(hapd, sta); +} + + +void mlme_michaelmicfailure_indication(struct hostapd_data *hapd, + const u8 *addr) +{ + hostapd_logger(hapd, addr, HOSTAPD_MODULE_MLME, + HOSTAPD_LEVEL_DEBUG, + "MLME-MichaelMICFailure.indication(" MACSTR ")", + MAC2STR(addr)); +} + + +void mlme_deletekeys_request(struct hostapd_data *hapd, struct sta_info *sta) +{ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_MLME, + HOSTAPD_LEVEL_DEBUG, + "MLME-DELETEKEYS.request(" MACSTR ")", + MAC2STR(sta->addr)); + + if (sta->wpa_sm) + wpa_remove_ptk(sta->wpa_sm); +} diff --git a/contrib/wpa/src/ap/ap_mlme.h b/contrib/wpa/src/ap/ap_mlme.h new file mode 100644 index 0000000..c77a939 --- /dev/null +++ b/contrib/wpa/src/ap/ap_mlme.h @@ -0,0 +1,40 @@ +/* + * hostapd / IEEE 802.11 MLME + * Copyright 2003, Jouni Malinen <j@w1.fi> + * Copyright 2003-2004, Instant802 Networks, Inc. + * Copyright 2005-2006, Devicescape Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef MLME_H +#define MLME_H + +void mlme_authenticate_indication(struct hostapd_data *hapd, + struct sta_info *sta); + +void mlme_deauthenticate_indication(struct hostapd_data *hapd, + struct sta_info *sta, u16 reason_code); + +void mlme_associate_indication(struct hostapd_data *hapd, + struct sta_info *sta); + +void mlme_reassociate_indication(struct hostapd_data *hapd, + struct sta_info *sta); + +void mlme_disassociate_indication(struct hostapd_data *hapd, + struct sta_info *sta, u16 reason_code); + +void mlme_michaelmicfailure_indication(struct hostapd_data *hapd, + const u8 *addr); + +void mlme_deletekeys_request(struct hostapd_data *hapd, struct sta_info *sta); + +#endif /* MLME_H */ diff --git a/contrib/wpa/src/ap/authsrv.c b/contrib/wpa/src/ap/authsrv.c new file mode 100644 index 0000000..0ab0668 --- /dev/null +++ b/contrib/wpa/src/ap/authsrv.c @@ -0,0 +1,216 @@ +/* + * Authentication server setup + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published 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 "utils/includes.h" + +#include "utils/common.h" +#include "crypto/tls.h" +#include "eap_server/eap.h" +#include "eap_server/eap_sim_db.h" +#include "eapol_auth/eapol_auth_sm.h" +#include "radius/radius_server.h" +#include "hostapd.h" +#include "ap_config.h" +#include "sta_info.h" +#include "authsrv.h" + + +#if defined(EAP_SERVER_SIM) || defined(EAP_SERVER_AKA) +#define EAP_SIM_DB +#endif /* EAP_SERVER_SIM || EAP_SERVER_AKA */ + + +#ifdef EAP_SIM_DB +static int hostapd_sim_db_cb_sta(struct hostapd_data *hapd, + struct sta_info *sta, void *ctx) +{ + if (eapol_auth_eap_pending_cb(sta->eapol_sm, ctx) == 0) + return 1; + return 0; +} + + +static void hostapd_sim_db_cb(void *ctx, void *session_ctx) +{ + struct hostapd_data *hapd = ctx; + if (ap_for_each_sta(hapd, hostapd_sim_db_cb_sta, session_ctx) == 0) { +#ifdef RADIUS_SERVER + radius_server_eap_pending_cb(hapd->radius_srv, session_ctx); +#endif /* RADIUS_SERVER */ + } +} +#endif /* EAP_SIM_DB */ + + +#ifdef RADIUS_SERVER + +static int hostapd_radius_get_eap_user(void *ctx, const u8 *identity, + size_t identity_len, int phase2, + struct eap_user *user) +{ + const struct hostapd_eap_user *eap_user; + int i, count; + + eap_user = hostapd_get_eap_user(ctx, identity, identity_len, phase2); + if (eap_user == NULL) + return -1; + + if (user == NULL) + return 0; + + os_memset(user, 0, sizeof(*user)); + count = EAP_USER_MAX_METHODS; + if (count > EAP_MAX_METHODS) + count = EAP_MAX_METHODS; + for (i = 0; i < count; i++) { + user->methods[i].vendor = eap_user->methods[i].vendor; + user->methods[i].method = eap_user->methods[i].method; + } + + if (eap_user->password) { + user->password = os_malloc(eap_user->password_len); + if (user->password == NULL) + return -1; + os_memcpy(user->password, eap_user->password, + eap_user->password_len); + user->password_len = eap_user->password_len; + user->password_hash = eap_user->password_hash; + } + user->force_version = eap_user->force_version; + user->ttls_auth = eap_user->ttls_auth; + + return 0; +} + + +static int hostapd_setup_radius_srv(struct hostapd_data *hapd) +{ + struct radius_server_conf srv; + struct hostapd_bss_config *conf = hapd->conf; + os_memset(&srv, 0, sizeof(srv)); + srv.client_file = conf->radius_server_clients; + srv.auth_port = conf->radius_server_auth_port; + srv.conf_ctx = conf; + srv.eap_sim_db_priv = hapd->eap_sim_db_priv; + srv.ssl_ctx = hapd->ssl_ctx; + srv.msg_ctx = hapd->msg_ctx; + srv.pac_opaque_encr_key = conf->pac_opaque_encr_key; + srv.eap_fast_a_id = conf->eap_fast_a_id; + srv.eap_fast_a_id_len = conf->eap_fast_a_id_len; + srv.eap_fast_a_id_info = conf->eap_fast_a_id_info; + srv.eap_fast_prov = conf->eap_fast_prov; + srv.pac_key_lifetime = conf->pac_key_lifetime; + srv.pac_key_refresh_time = conf->pac_key_refresh_time; + srv.eap_sim_aka_result_ind = conf->eap_sim_aka_result_ind; + srv.tnc = conf->tnc; + srv.wps = hapd->wps; + srv.ipv6 = conf->radius_server_ipv6; + srv.get_eap_user = hostapd_radius_get_eap_user; + srv.eap_req_id_text = conf->eap_req_id_text; + srv.eap_req_id_text_len = conf->eap_req_id_text_len; + + hapd->radius_srv = radius_server_init(&srv); + if (hapd->radius_srv == NULL) { + wpa_printf(MSG_ERROR, "RADIUS server initialization failed."); + return -1; + } + + return 0; +} + +#endif /* RADIUS_SERVER */ + + +int authsrv_init(struct hostapd_data *hapd) +{ +#ifdef EAP_TLS_FUNCS + if (hapd->conf->eap_server && + (hapd->conf->ca_cert || hapd->conf->server_cert || + hapd->conf->dh_file)) { + struct tls_connection_params params; + + hapd->ssl_ctx = tls_init(NULL); + if (hapd->ssl_ctx == NULL) { + wpa_printf(MSG_ERROR, "Failed to initialize TLS"); + authsrv_deinit(hapd); + return -1; + } + + os_memset(¶ms, 0, sizeof(params)); + params.ca_cert = hapd->conf->ca_cert; + params.client_cert = hapd->conf->server_cert; + params.private_key = hapd->conf->private_key; + params.private_key_passwd = hapd->conf->private_key_passwd; + params.dh_file = hapd->conf->dh_file; + + if (tls_global_set_params(hapd->ssl_ctx, ¶ms)) { + wpa_printf(MSG_ERROR, "Failed to set TLS parameters"); + authsrv_deinit(hapd); + return -1; + } + + if (tls_global_set_verify(hapd->ssl_ctx, + hapd->conf->check_crl)) { + wpa_printf(MSG_ERROR, "Failed to enable check_crl"); + authsrv_deinit(hapd); + return -1; + } + } +#endif /* EAP_TLS_FUNCS */ + +#ifdef EAP_SIM_DB + if (hapd->conf->eap_sim_db) { + hapd->eap_sim_db_priv = + eap_sim_db_init(hapd->conf->eap_sim_db, + hostapd_sim_db_cb, hapd); + if (hapd->eap_sim_db_priv == NULL) { + wpa_printf(MSG_ERROR, "Failed to initialize EAP-SIM " + "database interface"); + authsrv_deinit(hapd); + return -1; + } + } +#endif /* EAP_SIM_DB */ + +#ifdef RADIUS_SERVER + if (hapd->conf->radius_server_clients && + hostapd_setup_radius_srv(hapd)) + return -1; +#endif /* RADIUS_SERVER */ + + return 0; +} + + +void authsrv_deinit(struct hostapd_data *hapd) +{ +#ifdef RADIUS_SERVER + radius_server_deinit(hapd->radius_srv); + hapd->radius_srv = NULL; +#endif /* RADIUS_SERVER */ + +#ifdef EAP_TLS_FUNCS + if (hapd->ssl_ctx) { + tls_deinit(hapd->ssl_ctx); + hapd->ssl_ctx = NULL; + } +#endif /* EAP_TLS_FUNCS */ + +#ifdef EAP_SIM_DB + if (hapd->eap_sim_db_priv) { + eap_sim_db_deinit(hapd->eap_sim_db_priv); + hapd->eap_sim_db_priv = NULL; + } +#endif /* EAP_SIM_DB */ +} diff --git a/contrib/wpa/src/crypto/rc4.h b/contrib/wpa/src/ap/authsrv.h index 35c7e41..be3051e 100644 --- a/contrib/wpa/src/crypto/rc4.h +++ b/contrib/wpa/src/ap/authsrv.h @@ -1,6 +1,6 @@ /* - * RC4 stream cipher - * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi> + * Authentication server setup + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -12,10 +12,10 @@ * See README and COPYING for more details. */ -#ifndef RC4_H -#define RC4_H +#ifndef AUTHSRV_H +#define AUTHSRV_H -void rc4_skip(const u8 *key, size_t keylen, size_t skip, - u8 *data, size_t data_len); +int authsrv_init(struct hostapd_data *hapd); +void authsrv_deinit(struct hostapd_data *hapd); -#endif /* RC4_H */ +#endif /* AUTHSRV_H */ diff --git a/contrib/wpa/src/ap/beacon.c b/contrib/wpa/src/ap/beacon.c new file mode 100644 index 0000000..004cc8a --- /dev/null +++ b/contrib/wpa/src/ap/beacon.c @@ -0,0 +1,456 @@ +/* + * hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response + * Copyright (c) 2002-2004, Instant802 Networks, Inc. + * Copyright (c) 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "utils/includes.h" + +#ifndef CONFIG_NATIVE_WINDOWS + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "drivers/driver.h" +#include "hostapd.h" +#include "ieee802_11.h" +#include "wpa_auth.h" +#include "wmm.h" +#include "ap_config.h" +#include "sta_info.h" +#include "beacon.h" + + +static u8 ieee802_11_erp_info(struct hostapd_data *hapd) +{ + u8 erp = 0; + + if (hapd->iface->current_mode == NULL || + hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G) + return 0; + + switch (hapd->iconf->cts_protection_type) { + case CTS_PROTECTION_FORCE_ENABLED: + erp |= ERP_INFO_NON_ERP_PRESENT | ERP_INFO_USE_PROTECTION; + break; + case CTS_PROTECTION_FORCE_DISABLED: + erp = 0; + break; + case CTS_PROTECTION_AUTOMATIC: + if (hapd->iface->olbc) + erp |= ERP_INFO_USE_PROTECTION; + /* continue */ + case CTS_PROTECTION_AUTOMATIC_NO_OLBC: + if (hapd->iface->num_sta_non_erp > 0) { + erp |= ERP_INFO_NON_ERP_PRESENT | + ERP_INFO_USE_PROTECTION; + } + break; + } + if (hapd->iface->num_sta_no_short_preamble > 0 || + hapd->iconf->preamble == LONG_PREAMBLE) + erp |= ERP_INFO_BARKER_PREAMBLE_MODE; + + return erp; +} + + +static u8 * hostapd_eid_ds_params(struct hostapd_data *hapd, u8 *eid) +{ + *eid++ = WLAN_EID_DS_PARAMS; + *eid++ = 1; + *eid++ = hapd->iconf->channel; + return eid; +} + + +static u8 * hostapd_eid_erp_info(struct hostapd_data *hapd, u8 *eid) +{ + if (hapd->iface->current_mode == NULL || + hapd->iface->current_mode->mode != HOSTAPD_MODE_IEEE80211G) + return eid; + + /* Set NonERP_present and use_protection bits if there + * are any associated NonERP stations. */ + /* TODO: use_protection bit can be set to zero even if + * there are NonERP stations present. This optimization + * might be useful if NonERP stations are "quiet". + * See 802.11g/D6 E-1 for recommended practice. + * In addition, Non ERP present might be set, if AP detects Non ERP + * operation on other APs. */ + + /* Add ERP Information element */ + *eid++ = WLAN_EID_ERP_INFO; + *eid++ = 1; + *eid++ = ieee802_11_erp_info(hapd); + + return eid; +} + + +static u8 * hostapd_eid_country_add(u8 *pos, u8 *end, int chan_spacing, + struct hostapd_channel_data *start, + struct hostapd_channel_data *prev) +{ + if (end - pos < 3) + return pos; + + /* first channel number */ + *pos++ = start->chan; + /* number of channels */ + *pos++ = (prev->chan - start->chan) / chan_spacing + 1; + /* maximum transmit power level */ + *pos++ = start->max_tx_power; + + return pos; +} + + +static u8 * hostapd_eid_country(struct hostapd_data *hapd, u8 *eid, + int max_len) +{ + u8 *pos = eid; + u8 *end = eid + max_len; + int i; + struct hostapd_hw_modes *mode; + struct hostapd_channel_data *start, *prev; + int chan_spacing = 1; + + if (!hapd->iconf->ieee80211d || max_len < 6 || + hapd->iface->current_mode == NULL) + return eid; + + *pos++ = WLAN_EID_COUNTRY; + pos++; /* length will be set later */ + os_memcpy(pos, hapd->iconf->country, 3); /* e.g., 'US ' */ + pos += 3; + + mode = hapd->iface->current_mode; + if (mode->mode == HOSTAPD_MODE_IEEE80211A) + chan_spacing = 4; + + start = prev = NULL; + for (i = 0; i < mode->num_channels; i++) { + struct hostapd_channel_data *chan = &mode->channels[i]; + if (chan->flag & HOSTAPD_CHAN_DISABLED) + continue; + if (start && prev && + prev->chan + chan_spacing == chan->chan && + start->max_tx_power == chan->max_tx_power) { + prev = chan; + continue; /* can use same entry */ + } + + if (start) { + pos = hostapd_eid_country_add(pos, end, chan_spacing, + start, prev); + start = NULL; + } + + /* Start new group */ + start = prev = chan; + } + + if (start) { + pos = hostapd_eid_country_add(pos, end, chan_spacing, + start, prev); + } + + if ((pos - eid) & 1) { + if (end - pos < 1) + return eid; + *pos++ = 0; /* pad for 16-bit alignment */ + } + + eid[1] = (pos - eid) - 2; + + return pos; +} + + +static u8 * hostapd_eid_wpa(struct hostapd_data *hapd, u8 *eid, size_t len, + struct sta_info *sta) +{ + const u8 *ie; + size_t ielen; + + ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &ielen); + if (ie == NULL || ielen > len) + return eid; + + os_memcpy(eid, ie, ielen); + return eid + ielen; +} + + +void handle_probe_req(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len) +{ + struct ieee80211_mgmt *resp; + struct ieee802_11_elems elems; + char *ssid; + u8 *pos, *epos; + const u8 *ie; + size_t ssid_len, ie_len; + struct sta_info *sta = NULL; + size_t buflen; + size_t i; + + ie = mgmt->u.probe_req.variable; + ie_len = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); + + for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) + if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx, + mgmt->sa, ie, ie_len) > 0) + return; + + if (!hapd->iconf->send_probe_response) + return; + + if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) { + wpa_printf(MSG_DEBUG, "Could not parse ProbeReq from " MACSTR, + MAC2STR(mgmt->sa)); + return; + } + + ssid = NULL; + ssid_len = 0; + + if ((!elems.ssid || !elems.supp_rates)) { + wpa_printf(MSG_DEBUG, "STA " MACSTR " sent probe request " + "without SSID or supported rates element", + MAC2STR(mgmt->sa)); + return; + } + + if (hapd->conf->ignore_broadcast_ssid && elems.ssid_len == 0) { + wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR " for " + "broadcast SSID ignored", MAC2STR(mgmt->sa)); + return; + } + + sta = ap_get_sta(hapd, mgmt->sa); + + if (elems.ssid_len == 0 || + (elems.ssid_len == hapd->conf->ssid.ssid_len && + os_memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) == + 0)) { + ssid = hapd->conf->ssid.ssid; + ssid_len = hapd->conf->ssid.ssid_len; + if (sta) + sta->ssid_probe = &hapd->conf->ssid; + } + + if (!ssid) { + if (!(mgmt->da[0] & 0x01)) { + char ssid_txt[33]; + ieee802_11_print_ssid(ssid_txt, elems.ssid, + elems.ssid_len); + wpa_printf(MSG_MSGDUMP, "Probe Request from " MACSTR + " for foreign SSID '%s'", + MAC2STR(mgmt->sa), ssid_txt); + } + return; + } + + /* TODO: verify that supp_rates contains at least one matching rate + * with AP configuration */ +#define MAX_PROBERESP_LEN 768 + buflen = MAX_PROBERESP_LEN; +#ifdef CONFIG_WPS + if (hapd->wps_probe_resp_ie) + buflen += wpabuf_len(hapd->wps_probe_resp_ie); +#endif /* CONFIG_WPS */ + resp = os_zalloc(buflen); + if (resp == NULL) + return; + epos = ((u8 *) resp) + MAX_PROBERESP_LEN; + + resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_PROBE_RESP); + os_memcpy(resp->da, mgmt->sa, ETH_ALEN); + os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN); + + os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN); + resp->u.probe_resp.beacon_int = + host_to_le16(hapd->iconf->beacon_int); + + /* hardware or low-level driver will setup seq_ctrl and timestamp */ + resp->u.probe_resp.capab_info = + host_to_le16(hostapd_own_capab_info(hapd, sta, 1)); + + pos = resp->u.probe_resp.variable; + *pos++ = WLAN_EID_SSID; + *pos++ = ssid_len; + os_memcpy(pos, ssid, ssid_len); + pos += ssid_len; + + /* Supported rates */ + pos = hostapd_eid_supp_rates(hapd, pos); + + /* DS Params */ + pos = hostapd_eid_ds_params(hapd, pos); + + pos = hostapd_eid_country(hapd, pos, epos - pos); + + /* ERP Information element */ + pos = hostapd_eid_erp_info(hapd, pos); + + /* Extended supported rates */ + pos = hostapd_eid_ext_supp_rates(hapd, pos); + + /* RSN, MDIE, WPA */ + pos = hostapd_eid_wpa(hapd, pos, epos - pos, sta); + +#ifdef CONFIG_IEEE80211N + pos = hostapd_eid_ht_capabilities(hapd, pos); + pos = hostapd_eid_ht_operation(hapd, pos); +#endif /* CONFIG_IEEE80211N */ + + /* Wi-Fi Alliance WMM */ + pos = hostapd_eid_wmm(hapd, pos); + +#ifdef CONFIG_WPS + if (hapd->conf->wps_state && hapd->wps_probe_resp_ie) { + os_memcpy(pos, wpabuf_head(hapd->wps_probe_resp_ie), + wpabuf_len(hapd->wps_probe_resp_ie)); + pos += wpabuf_len(hapd->wps_probe_resp_ie); + } +#endif /* CONFIG_WPS */ + + if (hapd->drv.send_mgmt_frame(hapd, resp, pos - (u8 *) resp) < 0) + perror("handle_probe_req: send"); + + os_free(resp); + + wpa_printf(MSG_MSGDUMP, "STA " MACSTR " sent probe request for %s " + "SSID", MAC2STR(mgmt->sa), + elems.ssid_len == 0 ? "broadcast" : "our"); +} + + +void ieee802_11_set_beacon(struct hostapd_data *hapd) +{ + struct ieee80211_mgmt *head; + u8 *pos, *tail, *tailpos; + u16 capab_info; + size_t head_len, tail_len; + +#define BEACON_HEAD_BUF_SIZE 256 +#define BEACON_TAIL_BUF_SIZE 512 + head = os_zalloc(BEACON_HEAD_BUF_SIZE); + tail_len = BEACON_TAIL_BUF_SIZE; +#ifdef CONFIG_WPS + if (hapd->conf->wps_state && hapd->wps_beacon_ie) + tail_len += wpabuf_len(hapd->wps_beacon_ie); +#endif /* CONFIG_WPS */ + tailpos = tail = os_malloc(tail_len); + if (head == NULL || tail == NULL) { + wpa_printf(MSG_ERROR, "Failed to set beacon data"); + os_free(head); + os_free(tail); + return; + } + + head->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_BEACON); + head->duration = host_to_le16(0); + os_memset(head->da, 0xff, ETH_ALEN); + + os_memcpy(head->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(head->bssid, hapd->own_addr, ETH_ALEN); + head->u.beacon.beacon_int = + host_to_le16(hapd->iconf->beacon_int); + + /* hardware or low-level driver will setup seq_ctrl and timestamp */ + capab_info = hostapd_own_capab_info(hapd, NULL, 0); + head->u.beacon.capab_info = host_to_le16(capab_info); + pos = &head->u.beacon.variable[0]; + + /* SSID */ + *pos++ = WLAN_EID_SSID; + if (hapd->conf->ignore_broadcast_ssid == 2) { + /* clear the data, but keep the correct length of the SSID */ + *pos++ = hapd->conf->ssid.ssid_len; + os_memset(pos, 0, hapd->conf->ssid.ssid_len); + pos += hapd->conf->ssid.ssid_len; + } else if (hapd->conf->ignore_broadcast_ssid) { + *pos++ = 0; /* empty SSID */ + } else { + *pos++ = hapd->conf->ssid.ssid_len; + os_memcpy(pos, hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len); + pos += hapd->conf->ssid.ssid_len; + } + + /* Supported rates */ + pos = hostapd_eid_supp_rates(hapd, pos); + + /* DS Params */ + pos = hostapd_eid_ds_params(hapd, pos); + + head_len = pos - (u8 *) head; + + tailpos = hostapd_eid_country(hapd, tailpos, + tail + BEACON_TAIL_BUF_SIZE - tailpos); + + /* ERP Information element */ + tailpos = hostapd_eid_erp_info(hapd, tailpos); + + /* Extended supported rates */ + tailpos = hostapd_eid_ext_supp_rates(hapd, tailpos); + + /* RSN, MDIE, WPA */ + tailpos = hostapd_eid_wpa(hapd, tailpos, tail + BEACON_TAIL_BUF_SIZE - + tailpos, NULL); + +#ifdef CONFIG_IEEE80211N + tailpos = hostapd_eid_ht_capabilities(hapd, tailpos); + tailpos = hostapd_eid_ht_operation(hapd, tailpos); +#endif /* CONFIG_IEEE80211N */ + + /* Wi-Fi Alliance WMM */ + tailpos = hostapd_eid_wmm(hapd, tailpos); + +#ifdef CONFIG_WPS + if (hapd->conf->wps_state && hapd->wps_beacon_ie) { + os_memcpy(tailpos, wpabuf_head(hapd->wps_beacon_ie), + wpabuf_len(hapd->wps_beacon_ie)); + tailpos += wpabuf_len(hapd->wps_beacon_ie); + } +#endif /* CONFIG_WPS */ + + tail_len = tailpos > tail ? tailpos - tail : 0; + + if (hapd->drv.set_beacon(hapd, (u8 *) head, head_len, + tail, tail_len, hapd->conf->dtim_period, + hapd->iconf->beacon_int)) + wpa_printf(MSG_ERROR, "Failed to set beacon head/tail or DTIM " + "period"); + + os_free(tail); + os_free(head); + + hapd->drv.set_bss_params(hapd, !!(ieee802_11_erp_info(hapd) & + ERP_INFO_USE_PROTECTION)); +} + + +void ieee802_11_set_beacons(struct hostapd_iface *iface) +{ + size_t i; + for (i = 0; i < iface->num_bss; i++) + ieee802_11_set_beacon(iface->bss[i]); +} + +#endif /* CONFIG_NATIVE_WINDOWS */ diff --git a/contrib/wpa/src/ap/beacon.h b/contrib/wpa/src/ap/beacon.h new file mode 100644 index 0000000..c1510e1 --- /dev/null +++ b/contrib/wpa/src/ap/beacon.h @@ -0,0 +1,36 @@ +/* + * hostapd / IEEE 802.11 Management: Beacon and Probe Request/Response + * Copyright (c) 2002-2004, Instant802 Networks, Inc. + * Copyright (c) 2005-2006, Devicescape Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef BEACON_H +#define BEACON_H + +struct ieee80211_mgmt; + +void handle_probe_req(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len); +#ifdef NEED_AP_MLME +void ieee802_11_set_beacon(struct hostapd_data *hapd); +void ieee802_11_set_beacons(struct hostapd_iface *iface); +#else /* NEED_AP_MLME */ +static inline void ieee802_11_set_beacon(struct hostapd_data *hapd) +{ +} + +static inline void ieee802_11_set_beacons(struct hostapd_iface *iface) +{ +} +#endif /* NEED_AP_MLME */ + +#endif /* BEACON_H */ diff --git a/contrib/wpa/src/ap/ctrl_iface_ap.c b/contrib/wpa/src/ap/ctrl_iface_ap.c new file mode 100644 index 0000000..e50b0a7 --- /dev/null +++ b/contrib/wpa/src/ap/ctrl_iface_ap.c @@ -0,0 +1,104 @@ +/* + * Control interface for shared AP commands + * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "hostapd.h" +#include "ieee802_1x.h" +#include "wpa_auth.h" +#include "ieee802_11.h" +#include "sta_info.h" +#include "wps_hostapd.h" +#include "ctrl_iface_ap.h" + + +static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd, + struct sta_info *sta, + char *buf, size_t buflen) +{ + int len, res, ret; + + if (sta == NULL) { + ret = os_snprintf(buf, buflen, "FAIL\n"); + if (ret < 0 || (size_t) ret >= buflen) + return 0; + return ret; + } + + len = 0; + ret = os_snprintf(buf + len, buflen - len, MACSTR "\n", + MAC2STR(sta->addr)); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + res = ieee802_11_get_mib_sta(hapd, sta, buf + len, buflen - len); + if (res >= 0) + len += res; + res = wpa_get_mib_sta(sta->wpa_sm, buf + len, buflen - len); + if (res >= 0) + len += res; + res = ieee802_1x_get_mib_sta(hapd, sta, buf + len, buflen - len); + if (res >= 0) + len += res; + res = hostapd_wps_get_mib_sta(hapd, sta->addr, buf + len, + buflen - len); + if (res >= 0) + len += res; + + return len; +} + + +int hostapd_ctrl_iface_sta_first(struct hostapd_data *hapd, + char *buf, size_t buflen) +{ + return hostapd_ctrl_iface_sta_mib(hapd, hapd->sta_list, buf, buflen); +} + + +int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr, + char *buf, size_t buflen) +{ + u8 addr[ETH_ALEN]; + int ret; + + if (hwaddr_aton(txtaddr, addr)) { + ret = os_snprintf(buf, buflen, "FAIL\n"); + if (ret < 0 || (size_t) ret >= buflen) + return 0; + return ret; + } + return hostapd_ctrl_iface_sta_mib(hapd, ap_get_sta(hapd, addr), + buf, buflen); +} + + +int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr, + char *buf, size_t buflen) +{ + u8 addr[ETH_ALEN]; + struct sta_info *sta; + int ret; + + if (hwaddr_aton(txtaddr, addr) || + (sta = ap_get_sta(hapd, addr)) == NULL) { + ret = os_snprintf(buf, buflen, "FAIL\n"); + if (ret < 0 || (size_t) ret >= buflen) + return 0; + return ret; + } + return hostapd_ctrl_iface_sta_mib(hapd, sta->next, buf, buflen); +} diff --git a/contrib/wpa/src/ap/ctrl_iface_ap.h b/contrib/wpa/src/ap/ctrl_iface_ap.h new file mode 100644 index 0000000..8690bea --- /dev/null +++ b/contrib/wpa/src/ap/ctrl_iface_ap.h @@ -0,0 +1,25 @@ +/* + * Control interface for shared AP commands + * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef CTRL_IFACE_AP_H +#define CTRL_IFACE_AP_H + +int hostapd_ctrl_iface_sta_first(struct hostapd_data *hapd, + char *buf, size_t buflen); +int hostapd_ctrl_iface_sta(struct hostapd_data *hapd, const char *txtaddr, + char *buf, size_t buflen); +int hostapd_ctrl_iface_sta_next(struct hostapd_data *hapd, const char *txtaddr, + char *buf, size_t buflen); + +#endif /* CTRL_IFACE_AP_H */ diff --git a/contrib/wpa/src/ap/drv_callbacks.c b/contrib/wpa/src/ap/drv_callbacks.c new file mode 100644 index 0000000..26ef584 --- /dev/null +++ b/contrib/wpa/src/ap/drv_callbacks.c @@ -0,0 +1,457 @@ +/* + * hostapd / Callback functions for driver wrappers + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published 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 "utils/includes.h" + +#include "utils/common.h" +#include "radius/radius.h" +#include "drivers/driver.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "common/wpa_ctrl.h" +#include "hostapd.h" +#include "ieee802_11.h" +#include "sta_info.h" +#include "accounting.h" +#include "tkip_countermeasures.h" +#include "iapp.h" +#include "ieee802_1x.h" +#include "wpa_auth.h" +#include "wmm.h" +#include "wps_hostapd.h" +#include "ap_config.h" + + +int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, + const u8 *ie, size_t ielen) +{ + struct sta_info *sta; + int new_assoc, res; + struct ieee802_11_elems elems; + + if (addr == NULL) { + /* + * This could potentially happen with unexpected event from the + * driver wrapper. This was seen at least in one case where the + * driver ended up being set to station mode while hostapd was + * running, so better make sure we stop processing such an + * event here. + */ + wpa_printf(MSG_DEBUG, "hostapd_notif_assoc: Skip event with " + "no address"); + return -1; + } + + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "associated"); + + ieee802_11_parse_elems(ie, ielen, &elems, 0); + if (elems.wps_ie) { + ie = elems.wps_ie - 2; + ielen = elems.wps_ie_len + 2; + wpa_printf(MSG_DEBUG, "STA included WPS IE in (Re)AssocReq"); + } else if (elems.rsn_ie) { + ie = elems.rsn_ie - 2; + ielen = elems.rsn_ie_len + 2; + wpa_printf(MSG_DEBUG, "STA included RSN IE in (Re)AssocReq"); + } else if (elems.wpa_ie) { + ie = elems.wpa_ie - 2; + ielen = elems.wpa_ie_len + 2; + wpa_printf(MSG_DEBUG, "STA included WPA IE in (Re)AssocReq"); + } else { + ie = NULL; + ielen = 0; + wpa_printf(MSG_DEBUG, "STA did not include WPS/RSN/WPA IE in " + "(Re)AssocReq"); + } + + 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; + } + sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS); + + if (hapd->conf->wpa) { + if (ie == NULL || ielen == 0) { + if (hapd->conf->wps_state) { + wpa_printf(MSG_DEBUG, "STA did not include " + "WPA/RSN IE in (Re)Association " + "Request - possible WPS use"); + sta->flags |= WLAN_STA_MAYBE_WPS; + goto skip_wpa_check; + } + + wpa_printf(MSG_DEBUG, "No WPA/RSN IE from STA"); + return -1; + } + if (hapd->conf->wps_state && ie[0] == 0xdd && ie[1] >= 4 && + os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) { + sta->flags |= WLAN_STA_WPS; + goto skip_wpa_check; + } + + if (sta->wpa_sm == NULL) + sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, + sta->addr); + if (sta->wpa_sm == NULL) { + wpa_printf(MSG_ERROR, "Failed to initialize WPA state " + "machine"); + return -1; + } + res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, + ie, ielen, NULL, 0); + if (res != WPA_IE_OK) { + int resp; + wpa_printf(MSG_DEBUG, "WPA/RSN information element " + "rejected? (res %u)", res); + wpa_hexdump(MSG_DEBUG, "IE", ie, ielen); + if (res == WPA_INVALID_GROUP) + resp = WLAN_REASON_GROUP_CIPHER_NOT_VALID; + else if (res == WPA_INVALID_PAIRWISE) + resp = WLAN_REASON_PAIRWISE_CIPHER_NOT_VALID; + else if (res == WPA_INVALID_AKMP) + resp = WLAN_REASON_AKMP_NOT_VALID; +#ifdef CONFIG_IEEE80211W + else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION) + resp = WLAN_REASON_INVALID_IE; + else if (res == WPA_INVALID_MGMT_GROUP_CIPHER) + resp = WLAN_REASON_GROUP_CIPHER_NOT_VALID; +#endif /* CONFIG_IEEE80211W */ + else + resp = WLAN_REASON_INVALID_IE; + hapd->drv.sta_disassoc(hapd, sta->addr, resp); + ap_free_sta(hapd, sta); + return -1; + } + } else if (hapd->conf->wps_state) { + if (ie && ielen > 4 && ie[0] == 0xdd && ie[1] >= 4 && + os_memcmp(ie + 2, "\x00\x50\xf2\x04", 4) == 0) { + sta->flags |= WLAN_STA_WPS; + } else + sta->flags |= WLAN_STA_MAYBE_WPS; + } +skip_wpa_check: + + new_assoc = (sta->flags & WLAN_STA_ASSOC) == 0; + sta->flags |= WLAN_STA_AUTH | WLAN_STA_ASSOC; + wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC); + + hostapd_new_assoc_sta(hapd, sta, !new_assoc); + + ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); + + return 0; +} + + +void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr) +{ + struct sta_info *sta; + + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "disassociated"); + + sta = ap_get_sta(hapd, addr); + if (sta == NULL) { + wpa_printf(MSG_DEBUG, "Disassociation notification for " + "unknown STA " MACSTR, MAC2STR(addr)); + return; + } + + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED MACSTR, + MAC2STR(sta->addr)); + wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC); + sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; + ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); + ap_free_sta(hapd, sta); +} + + +#ifdef HOSTAPD + +#ifdef NEED_AP_MLME + +static const u8 * get_hdr_bssid(const struct ieee80211_hdr *hdr, size_t len) +{ + u16 fc, type, stype; + + /* + * PS-Poll frames are 16 bytes. All other frames are + * 24 bytes or longer. + */ + if (len < 16) + return NULL; + + fc = le_to_host16(hdr->frame_control); + type = WLAN_FC_GET_TYPE(fc); + stype = WLAN_FC_GET_STYPE(fc); + + switch (type) { + case WLAN_FC_TYPE_DATA: + if (len < 24) + return NULL; + switch (fc & (WLAN_FC_FROMDS | WLAN_FC_TODS)) { + case WLAN_FC_FROMDS | WLAN_FC_TODS: + case WLAN_FC_TODS: + return hdr->addr1; + case WLAN_FC_FROMDS: + return hdr->addr2; + default: + return NULL; + } + case WLAN_FC_TYPE_CTRL: + if (stype != WLAN_FC_STYPE_PSPOLL) + return NULL; + return hdr->addr1; + case WLAN_FC_TYPE_MGMT: + return hdr->addr3; + default: + return NULL; + } +} + + +#define HAPD_BROADCAST ((struct hostapd_data *) -1) + +static struct hostapd_data * get_hapd_bssid(struct hostapd_iface *iface, + const u8 *bssid) +{ + size_t i; + + if (bssid == NULL) + return NULL; + if (bssid[0] == 0xff && bssid[1] == 0xff && bssid[2] == 0xff && + bssid[3] == 0xff && bssid[4] == 0xff && bssid[5] == 0xff) + return HAPD_BROADCAST; + + for (i = 0; i < iface->num_bss; i++) { + if (os_memcmp(bssid, iface->bss[i]->own_addr, ETH_ALEN) == 0) + return iface->bss[i]; + } + + return NULL; +} + + +static void hostapd_rx_from_unknown_sta(struct hostapd_data *hapd, + const u8 *frame, size_t len) +{ + const struct ieee80211_hdr *hdr = (const struct ieee80211_hdr *) frame; + u16 fc = le_to_host16(hdr->frame_control); + hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len)); + if (hapd == NULL || hapd == HAPD_BROADCAST) + return; + + ieee802_11_rx_from_unknown(hapd, hdr->addr2, + (fc & (WLAN_FC_TODS | WLAN_FC_FROMDS)) == + (WLAN_FC_TODS | WLAN_FC_FROMDS)); +} + + +static void hostapd_mgmt_rx(struct hostapd_data *hapd, struct rx_mgmt *rx_mgmt) +{ + struct hostapd_iface *iface = hapd->iface; + const struct ieee80211_hdr *hdr; + const u8 *bssid; + struct hostapd_frame_info fi; + + hdr = (const struct ieee80211_hdr *) rx_mgmt->frame; + bssid = get_hdr_bssid(hdr, rx_mgmt->frame_len); + if (bssid == NULL) + return; + + hapd = get_hapd_bssid(iface, bssid); + if (hapd == NULL) { + u16 fc; + fc = le_to_host16(hdr->frame_control); + + /* + * Drop frames to unknown BSSIDs except for Beacon frames which + * could be used to update neighbor information. + */ + if (WLAN_FC_GET_TYPE(fc) == WLAN_FC_TYPE_MGMT && + WLAN_FC_GET_STYPE(fc) == WLAN_FC_STYPE_BEACON) + hapd = iface->bss[0]; + else + return; + } + + os_memset(&fi, 0, sizeof(fi)); + fi.datarate = rx_mgmt->datarate; + fi.ssi_signal = rx_mgmt->ssi_signal; + + if (hapd == HAPD_BROADCAST) { + size_t i; + for (i = 0; i < iface->num_bss; i++) + ieee802_11_mgmt(iface->bss[i], rx_mgmt->frame, + rx_mgmt->frame_len, &fi); + } else + ieee802_11_mgmt(hapd, rx_mgmt->frame, rx_mgmt->frame_len, &fi); +} + + +static void hostapd_mgmt_tx_cb(struct hostapd_data *hapd, const u8 *buf, + size_t len, u16 stype, int ok) +{ + struct ieee80211_hdr *hdr; + hdr = (struct ieee80211_hdr *) buf; + hapd = get_hapd_bssid(hapd->iface, get_hdr_bssid(hdr, len)); + if (hapd == NULL || hapd == HAPD_BROADCAST) + return; + ieee802_11_mgmt_cb(hapd, buf, len, stype, ok); +} + +#endif /* NEED_AP_MLME */ + + +static int hostapd_probe_req_rx(struct hostapd_data *hapd, const u8 *sa, + const u8 *ie, size_t ie_len) +{ + size_t i; + int ret = 0; + + for (i = 0; hapd->probereq_cb && i < hapd->num_probereq_cb; i++) { + if (hapd->probereq_cb[i].cb(hapd->probereq_cb[i].ctx, + sa, ie, ie_len) > 0) { + ret = 1; + break; + } + } + return ret; +} + + +static int hostapd_event_new_sta(struct hostapd_data *hapd, const u8 *addr) +{ + struct sta_info *sta = ap_get_sta(hapd, addr); + if (sta) + return 0; + + wpa_printf(MSG_DEBUG, "Data frame from unknown STA " MACSTR + " - adding a new STA", MAC2STR(addr)); + sta = ap_sta_add(hapd, addr); + if (sta) { + hostapd_new_assoc_sta(hapd, sta, 0); + } else { + wpa_printf(MSG_DEBUG, "Failed to add STA entry for " MACSTR, + MAC2STR(addr)); + return -1; + } + + return 0; +} + + +static void hostapd_event_eapol_rx(struct hostapd_data *hapd, const u8 *src, + const u8 *data, size_t data_len) +{ + struct hostapd_iface *iface = hapd->iface; + size_t j; + + for (j = 0; j < iface->num_bss; j++) { + if (ap_get_sta(iface->bss[j], src)) { + hapd = iface->bss[j]; + break; + } + } + + ieee802_1x_receive(hapd, src, data, data_len); +} + + +void wpa_supplicant_event(void *ctx, enum wpa_event_type event, + union wpa_event_data *data) +{ + struct hostapd_data *hapd = ctx; + + switch (event) { + case EVENT_MICHAEL_MIC_FAILURE: + michael_mic_failure(hapd, data->michael_mic_failure.src, 1); + break; + case EVENT_SCAN_RESULTS: + if (hapd->iface->scan_cb) + hapd->iface->scan_cb(hapd->iface); + break; +#ifdef CONFIG_IEEE80211R + case EVENT_FT_RRB_RX: + wpa_ft_rrb_rx(hapd->wpa_auth, data->ft_rrb_rx.src, + data->ft_rrb_rx.data, data->ft_rrb_rx.data_len); + break; +#endif /* CONFIG_IEEE80211R */ + case EVENT_WPS_BUTTON_PUSHED: + hostapd_wps_button_pushed(hapd); + break; +#ifdef NEED_AP_MLME + case EVENT_TX_STATUS: + switch (data->tx_status.type) { + case WLAN_FC_TYPE_MGMT: + hostapd_mgmt_tx_cb(hapd, data->tx_status.data, + data->tx_status.data_len, + data->tx_status.stype, + data->tx_status.ack); + break; + case WLAN_FC_TYPE_DATA: + hostapd_tx_status(hapd, data->tx_status.dst, + data->tx_status.data, + data->tx_status.data_len, + data->tx_status.ack); + break; + } + break; + case EVENT_RX_FROM_UNKNOWN: + hostapd_rx_from_unknown_sta(hapd, data->rx_from_unknown.frame, + data->rx_from_unknown.len); + break; + case EVENT_RX_MGMT: + hostapd_mgmt_rx(hapd, &data->rx_mgmt); + break; +#endif /* NEED_AP_MLME */ + case EVENT_RX_PROBE_REQ: + hostapd_probe_req_rx(hapd, data->rx_probe_req.sa, + data->rx_probe_req.ie, + data->rx_probe_req.ie_len); + break; + case EVENT_NEW_STA: + hostapd_event_new_sta(hapd, data->new_sta.addr); + break; + case EVENT_EAPOL_RX: + hostapd_event_eapol_rx(hapd, data->eapol_rx.src, + data->eapol_rx.data, + data->eapol_rx.data_len); + break; + case EVENT_ASSOC: + hostapd_notif_assoc(hapd, data->assoc_info.addr, + data->assoc_info.req_ies, + data->assoc_info.req_ies_len); + break; + case EVENT_DISASSOC: + if (data) + hostapd_notif_disassoc(hapd, data->disassoc_info.addr); + break; + case EVENT_DEAUTH: + if (data) + hostapd_notif_disassoc(hapd, data->deauth_info.addr); + break; + default: + wpa_printf(MSG_DEBUG, "Unknown event %d", event); + break; + } +} + +#endif /* HOSTAPD */ diff --git a/contrib/wpa/src/ap/hostapd.c b/contrib/wpa/src/ap/hostapd.c new file mode 100644 index 0000000..841f9c5 --- /dev/null +++ b/contrib/wpa/src/ap/hostapd.c @@ -0,0 +1,887 @@ +/* + * hostapd / Initialization and configuration + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published 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 "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "common/ieee802_11_defs.h" +#include "radius/radius_client.h" +#include "drivers/driver.h" +#include "hostapd.h" +#include "authsrv.h" +#include "sta_info.h" +#include "accounting.h" +#include "ap_list.h" +#include "beacon.h" +#include "iapp.h" +#include "ieee802_1x.h" +#include "ieee802_11_auth.h" +#include "vlan_init.h" +#include "wpa_auth.h" +#include "wps_hostapd.h" +#include "hw_features.h" +#include "wpa_auth_glue.h" +#include "ap_drv_ops.h" +#include "ap_config.h" + + +static int hostapd_flush_old_stations(struct hostapd_data *hapd); +static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd); + +extern int wpa_debug_level; + + +int hostapd_reload_config(struct hostapd_iface *iface) +{ + struct hostapd_data *hapd = iface->bss[0]; + struct hostapd_config *newconf, *oldconf; + size_t j; + + if (iface->config_read_cb == NULL) + return -1; + newconf = iface->config_read_cb(iface->config_fname); + if (newconf == NULL) + return -1; + + /* + * Deauthenticate all stations since the new configuration may not + * allow them to use the BSS anymore. + */ + for (j = 0; j < iface->num_bss; j++) + hostapd_flush_old_stations(iface->bss[j]); + +#ifndef CONFIG_NO_RADIUS + /* TODO: update dynamic data based on changed configuration + * items (e.g., open/close sockets, etc.) */ + radius_client_flush(hapd->radius, 0); +#endif /* CONFIG_NO_RADIUS */ + + oldconf = hapd->iconf; + hapd->iconf = newconf; + hapd->conf = &newconf->bss[0]; + iface->conf = newconf; + + if (hostapd_setup_wpa_psk(hapd->conf)) { + wpa_printf(MSG_ERROR, "Failed to re-configure WPA PSK " + "after reloading configuration"); + } + + if (hapd->conf->ieee802_1x || hapd->conf->wpa) + hapd->drv.set_drv_ieee8021x(hapd, hapd->conf->iface, 1); + else + hapd->drv.set_drv_ieee8021x(hapd, hapd->conf->iface, 0); + + if (hapd->conf->wpa && hapd->wpa_auth == NULL) + hostapd_setup_wpa(hapd); + else if (hapd->conf->wpa) { + const u8 *wpa_ie; + size_t wpa_ie_len; + hostapd_reconfig_wpa(hapd); + wpa_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &wpa_ie_len); + if (hostapd_set_generic_elem(hapd, wpa_ie, wpa_ie_len)) + wpa_printf(MSG_ERROR, "Failed to configure WPA IE for " + "the kernel driver."); + } else if (hapd->wpa_auth) { + wpa_deinit(hapd->wpa_auth); + hapd->wpa_auth = NULL; + hostapd_set_privacy(hapd, 0); + hostapd_setup_encryption(hapd->conf->iface, hapd); + hostapd_set_generic_elem(hapd, (u8 *) "", 0); + } + + ieee802_11_set_beacon(hapd); + hostapd_update_wps(hapd); + + if (hapd->conf->ssid.ssid_set && + hostapd_set_ssid(hapd, (u8 *) hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len)) { + wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver"); + /* try to continue */ + } + + hostapd_config_free(oldconf); + + wpa_printf(MSG_DEBUG, "Reconfigured interface %s", hapd->conf->iface); + + return 0; +} + + +static void hostapd_broadcast_key_clear_iface(struct hostapd_data *hapd, + char *ifname) +{ + int i; + + for (i = 0; i < NUM_WEP_KEYS; i++) { + if (hapd->drv.set_key(ifname, hapd, WPA_ALG_NONE, NULL, i, + i == 0 ? 1 : 0, NULL, 0, NULL, 0)) { + wpa_printf(MSG_DEBUG, "Failed to clear default " + "encryption keys (ifname=%s keyidx=%d)", + ifname, i); + } + } +#ifdef CONFIG_IEEE80211W + if (hapd->conf->ieee80211w) { + for (i = NUM_WEP_KEYS; i < NUM_WEP_KEYS + 2; i++) { + if (hapd->drv.set_key(ifname, hapd, WPA_ALG_NONE, NULL, + i, i == 0 ? 1 : 0, NULL, 0, + NULL, 0)) { + wpa_printf(MSG_DEBUG, "Failed to clear " + "default mgmt encryption keys " + "(ifname=%s keyidx=%d)", ifname, i); + } + } + } +#endif /* CONFIG_IEEE80211W */ +} + + +static int hostapd_broadcast_wep_clear(struct hostapd_data *hapd) +{ + hostapd_broadcast_key_clear_iface(hapd, hapd->conf->iface); + return 0; +} + + +static int hostapd_broadcast_wep_set(struct hostapd_data *hapd) +{ + int errors = 0, idx; + struct hostapd_ssid *ssid = &hapd->conf->ssid; + + idx = ssid->wep.idx; + if (ssid->wep.default_len && + hapd->drv.set_key(hapd->conf->iface, + hapd, WPA_ALG_WEP, NULL, idx, + idx == ssid->wep.idx, + NULL, 0, ssid->wep.key[idx], + ssid->wep.len[idx])) { + wpa_printf(MSG_WARNING, "Could not set WEP encryption."); + errors++; + } + + if (ssid->dyn_vlan_keys) { + size_t i; + for (i = 0; i <= ssid->max_dyn_vlan_keys; i++) { + const char *ifname; + struct hostapd_wep_keys *key = ssid->dyn_vlan_keys[i]; + if (key == NULL) + continue; + ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan, + i); + if (ifname == NULL) + continue; + + idx = key->idx; + if (hapd->drv.set_key(ifname, hapd, WPA_ALG_WEP, NULL, + idx, idx == key->idx, NULL, 0, + key->key[idx], key->len[idx])) { + wpa_printf(MSG_WARNING, "Could not set " + "dynamic VLAN WEP encryption."); + errors++; + } + } + } + + return errors; +} + +/** + * hostapd_cleanup - Per-BSS cleanup (deinitialization) + * @hapd: Pointer to BSS data + * + * This function is used to free all per-BSS data structures and resources. + * This gets called in a loop for each BSS between calls to + * hostapd_cleanup_iface_pre() and hostapd_cleanup_iface() when an interface + * is deinitialized. Most of the modules that are initialized in + * hostapd_setup_bss() are deinitialized here. + */ +static void hostapd_cleanup(struct hostapd_data *hapd) +{ + if (hapd->iface->ctrl_iface_deinit) + hapd->iface->ctrl_iface_deinit(hapd); + + iapp_deinit(hapd->iapp); + hapd->iapp = NULL; + accounting_deinit(hapd); + hostapd_deinit_wpa(hapd); + vlan_deinit(hapd); + hostapd_acl_deinit(hapd); +#ifndef CONFIG_NO_RADIUS + radius_client_deinit(hapd->radius); + hapd->radius = NULL; +#endif /* CONFIG_NO_RADIUS */ + + hostapd_deinit_wps(hapd); + + authsrv_deinit(hapd); + + if (hapd->interface_added && + hostapd_if_remove(hapd, WPA_IF_AP_BSS, hapd->conf->iface)) { + wpa_printf(MSG_WARNING, "Failed to remove BSS interface %s", + hapd->conf->iface); + } + + os_free(hapd->probereq_cb); + hapd->probereq_cb = NULL; +} + + +/** + * hostapd_cleanup_iface_pre - Preliminary per-interface cleanup + * @iface: Pointer to interface data + * + * This function is called before per-BSS data structures are deinitialized + * with hostapd_cleanup(). + */ +static void hostapd_cleanup_iface_pre(struct hostapd_iface *iface) +{ +} + + +/** + * hostapd_cleanup_iface - Complete per-interface cleanup + * @iface: Pointer to interface data + * + * This function is called after per-BSS data structures are deinitialized + * with hostapd_cleanup(). + */ +static void hostapd_cleanup_iface(struct hostapd_iface *iface) +{ + hostapd_free_hw_features(iface->hw_features, iface->num_hw_features); + iface->hw_features = NULL; + os_free(iface->current_rates); + iface->current_rates = NULL; + ap_list_deinit(iface); + hostapd_config_free(iface->conf); + iface->conf = NULL; + + os_free(iface->config_fname); + os_free(iface->bss); + os_free(iface); +} + + +static int hostapd_setup_encryption(char *iface, struct hostapd_data *hapd) +{ + int i; + + hostapd_broadcast_wep_set(hapd); + + if (hapd->conf->ssid.wep.default_len) { + hostapd_set_privacy(hapd, 1); + return 0; + } + + for (i = 0; i < 4; i++) { + if (hapd->conf->ssid.wep.key[i] && + hapd->drv.set_key(iface, hapd, WPA_ALG_WEP, NULL, i, + i == hapd->conf->ssid.wep.idx, NULL, 0, + hapd->conf->ssid.wep.key[i], + hapd->conf->ssid.wep.len[i])) { + wpa_printf(MSG_WARNING, "Could not set WEP " + "encryption."); + return -1; + } + if (hapd->conf->ssid.wep.key[i] && + i == hapd->conf->ssid.wep.idx) + hostapd_set_privacy(hapd, 1); + } + + return 0; +} + + +static int hostapd_flush_old_stations(struct hostapd_data *hapd) +{ + int ret = 0; + + if (hostapd_drv_none(hapd) || hapd->drv_priv == NULL) + return 0; + + wpa_printf(MSG_DEBUG, "Flushing old station entries"); + if (hostapd_flush(hapd)) { + wpa_printf(MSG_WARNING, "Could not connect to kernel driver."); + ret = -1; + } + wpa_printf(MSG_DEBUG, "Deauthenticate all stations"); + + /* New Prism2.5/3 STA firmware versions seem to have issues with this + * broadcast deauth frame. This gets the firmware in odd state where + * nothing works correctly, so let's skip sending this for the hostap + * driver. */ + if (hapd->driver && os_strcmp(hapd->driver->name, "hostap") != 0) { + u8 addr[ETH_ALEN]; + os_memset(addr, 0xff, ETH_ALEN); + hapd->drv.sta_deauth(hapd, addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); + } + + return ret; +} + + +/** + * hostapd_validate_bssid_configuration - Validate BSSID configuration + * @iface: Pointer to interface data + * Returns: 0 on success, -1 on failure + * + * This function is used to validate that the configured BSSIDs are valid. + */ +static int hostapd_validate_bssid_configuration(struct hostapd_iface *iface) +{ + u8 mask[ETH_ALEN] = { 0 }; + struct hostapd_data *hapd = iface->bss[0]; + unsigned int i = iface->conf->num_bss, bits = 0, j; + int res; + int auto_addr = 0; + + if (hostapd_drv_none(hapd)) + return 0; + + /* Generate BSSID mask that is large enough to cover the BSSIDs. */ + + /* Determine the bits necessary to cover the number of BSSIDs. */ + for (i--; i; i >>= 1) + bits++; + + /* Determine the bits necessary to any configured BSSIDs, + if they are higher than the number of BSSIDs. */ + for (j = 0; j < iface->conf->num_bss; j++) { + if (hostapd_mac_comp_empty(iface->conf->bss[j].bssid) == 0) { + if (j) + auto_addr++; + continue; + } + + for (i = 0; i < ETH_ALEN; i++) { + mask[i] |= + iface->conf->bss[j].bssid[i] ^ + hapd->own_addr[i]; + } + } + + if (!auto_addr) + goto skip_mask_ext; + + for (i = 0; i < ETH_ALEN && mask[i] == 0; i++) + ; + j = 0; + if (i < ETH_ALEN) { + j = (5 - i) * 8; + + while (mask[i] != 0) { + mask[i] >>= 1; + j++; + } + } + + if (bits < j) + bits = j; + + if (bits > 40) { + wpa_printf(MSG_ERROR, "Too many bits in the BSSID mask (%u)", + bits); + return -1; + } + + os_memset(mask, 0xff, ETH_ALEN); + j = bits / 8; + for (i = 5; i > 5 - j; i--) + mask[i] = 0; + j = bits % 8; + while (j--) + mask[i] <<= 1; + +skip_mask_ext: + wpa_printf(MSG_DEBUG, "BSS count %lu, BSSID mask " MACSTR " (%d bits)", + (unsigned long) iface->conf->num_bss, MAC2STR(mask), bits); + + res = hostapd_valid_bss_mask(hapd, hapd->own_addr, mask); + if (res == 0) + return 0; + + if (res < 0) { + wpa_printf(MSG_ERROR, "Driver did not accept BSSID mask " + MACSTR " for start address " MACSTR ".", + MAC2STR(mask), MAC2STR(hapd->own_addr)); + return -1; + } + + if (!auto_addr) + return 0; + + for (i = 0; i < ETH_ALEN; i++) { + if ((hapd->own_addr[i] & mask[i]) != hapd->own_addr[i]) { + wpa_printf(MSG_ERROR, "Invalid BSSID mask " MACSTR + " for start address " MACSTR ".", + MAC2STR(mask), MAC2STR(hapd->own_addr)); + wpa_printf(MSG_ERROR, "Start address must be the " + "first address in the block (i.e., addr " + "AND mask == addr)."); + return -1; + } + } + + return 0; +} + + +static int mac_in_conf(struct hostapd_config *conf, const void *a) +{ + size_t i; + + for (i = 0; i < conf->num_bss; i++) { + if (hostapd_mac_comp(conf->bss[i].bssid, a) == 0) { + return 1; + } + } + + return 0; +} + + + + +/** + * hostapd_setup_bss - Per-BSS setup (initialization) + * @hapd: Pointer to BSS data + * @first: Whether this BSS is the first BSS of an interface + * + * This function is used to initialize all per-BSS data structures and + * resources. This gets called in a loop for each BSS when an interface is + * initialized. Most of the modules that are initialized here will be + * deinitialized in hostapd_cleanup(). + */ +static int hostapd_setup_bss(struct hostapd_data *hapd, int first) +{ + struct hostapd_bss_config *conf = hapd->conf; + u8 ssid[HOSTAPD_MAX_SSID_LEN + 1]; + int ssid_len, set_ssid; + char force_ifname[IFNAMSIZ]; + u8 if_addr[ETH_ALEN]; + + if (!first) { + if (hostapd_mac_comp_empty(hapd->conf->bssid) == 0) { + /* Allocate the next available BSSID. */ + do { + inc_byte_array(hapd->own_addr, ETH_ALEN); + } while (mac_in_conf(hapd->iconf, hapd->own_addr)); + } else { + /* Allocate the configured BSSID. */ + os_memcpy(hapd->own_addr, hapd->conf->bssid, ETH_ALEN); + + if (hostapd_mac_comp(hapd->own_addr, + hapd->iface->bss[0]->own_addr) == + 0) { + wpa_printf(MSG_ERROR, "BSS '%s' may not have " + "BSSID set to the MAC address of " + "the radio", hapd->conf->iface); + return -1; + } + } + + hapd->interface_added = 1; + if (hostapd_if_add(hapd->iface->bss[0], WPA_IF_AP_BSS, + hapd->conf->iface, hapd->own_addr, hapd, + &hapd->drv_priv, force_ifname, if_addr)) { + wpa_printf(MSG_ERROR, "Failed to add BSS (BSSID=" + MACSTR ")", MAC2STR(hapd->own_addr)); + return -1; + } + } + + hostapd_flush_old_stations(hapd); + hostapd_set_privacy(hapd, 0); + + hostapd_broadcast_wep_clear(hapd); + if (hostapd_setup_encryption(hapd->conf->iface, hapd)) + return -1; + + /* + * Fetch the SSID from the system and use it or, + * if one was specified in the config file, verify they + * match. + */ + ssid_len = hostapd_get_ssid(hapd, ssid, sizeof(ssid)); + if (ssid_len < 0) { + wpa_printf(MSG_ERROR, "Could not read SSID from system"); + return -1; + } + if (conf->ssid.ssid_set) { + /* + * If SSID is specified in the config file and it differs + * from what is being used then force installation of the + * new SSID. + */ + set_ssid = (conf->ssid.ssid_len != (size_t) ssid_len || + os_memcmp(conf->ssid.ssid, ssid, ssid_len) != 0); + } else { + /* + * No SSID in the config file; just use the one we got + * from the system. + */ + set_ssid = 0; + conf->ssid.ssid_len = ssid_len; + os_memcpy(conf->ssid.ssid, ssid, conf->ssid.ssid_len); + conf->ssid.ssid[conf->ssid.ssid_len] = '\0'; + } + + if (!hostapd_drv_none(hapd)) { + wpa_printf(MSG_ERROR, "Using interface %s with hwaddr " MACSTR + " and ssid '%s'", + hapd->conf->iface, MAC2STR(hapd->own_addr), + hapd->conf->ssid.ssid); + } + + if (hostapd_setup_wpa_psk(conf)) { + wpa_printf(MSG_ERROR, "WPA-PSK setup failed."); + return -1; + } + + /* Set SSID for the kernel driver (to be used in beacon and probe + * response frames) */ + if (set_ssid && hostapd_set_ssid(hapd, (u8 *) conf->ssid.ssid, + conf->ssid.ssid_len)) { + wpa_printf(MSG_ERROR, "Could not set SSID for kernel driver"); + return -1; + } + + if (wpa_debug_level == MSG_MSGDUMP) + conf->radius->msg_dumps = 1; +#ifndef CONFIG_NO_RADIUS + hapd->radius = radius_client_init(hapd, conf->radius); + if (hapd->radius == NULL) { + wpa_printf(MSG_ERROR, "RADIUS client initialization failed."); + return -1; + } +#endif /* CONFIG_NO_RADIUS */ + + if (hostapd_acl_init(hapd)) { + wpa_printf(MSG_ERROR, "ACL initialization failed."); + return -1; + } + if (hostapd_init_wps(hapd, conf)) + return -1; + + if (authsrv_init(hapd) < 0) + return -1; + + if (ieee802_1x_init(hapd)) { + wpa_printf(MSG_ERROR, "IEEE 802.1X initialization failed."); + return -1; + } + + if (hapd->conf->wpa && hostapd_setup_wpa(hapd)) + return -1; + + if (accounting_init(hapd)) { + wpa_printf(MSG_ERROR, "Accounting initialization failed."); + return -1; + } + + if (hapd->conf->ieee802_11f && + (hapd->iapp = iapp_init(hapd, hapd->conf->iapp_iface)) == NULL) { + wpa_printf(MSG_ERROR, "IEEE 802.11F (IAPP) initialization " + "failed."); + return -1; + } + + if (hapd->iface->ctrl_iface_init && + hapd->iface->ctrl_iface_init(hapd)) { + wpa_printf(MSG_ERROR, "Failed to setup control interface"); + return -1; + } + + if (!hostapd_drv_none(hapd) && vlan_init(hapd)) { + wpa_printf(MSG_ERROR, "VLAN initialization failed."); + return -1; + } + + ieee802_11_set_beacon(hapd); + + return 0; +} + + +static void hostapd_tx_queue_params(struct hostapd_iface *iface) +{ + struct hostapd_data *hapd = iface->bss[0]; + int i; + struct hostapd_tx_queue_params *p; + + for (i = 0; i < NUM_TX_QUEUES; i++) { + p = &iface->conf->tx_queue[i]; + + if (!p->configured) + continue; + + if (hostapd_set_tx_queue_params(hapd, i, p->aifs, p->cwmin, + p->cwmax, p->burst)) { + wpa_printf(MSG_DEBUG, "Failed to set TX queue " + "parameters for queue %d.", i); + /* Continue anyway */ + } + } +} + + +static int setup_interface(struct hostapd_iface *iface) +{ + struct hostapd_data *hapd = iface->bss[0]; + size_t i; + char country[4]; + + /* + * Make sure that all BSSes get configured with a pointer to the same + * driver interface. + */ + for (i = 1; i < iface->num_bss; i++) { + iface->bss[i]->driver = hapd->driver; + iface->bss[i]->drv_priv = hapd->drv_priv; + } + + if (hostapd_validate_bssid_configuration(iface)) + return -1; + + if (hapd->iconf->country[0] && hapd->iconf->country[1]) { + os_memcpy(country, hapd->iconf->country, 3); + country[3] = '\0'; + if (hostapd_set_country(hapd, country) < 0) { + wpa_printf(MSG_ERROR, "Failed to set country code"); + return -1; + } + } + + if (hostapd_get_hw_features(iface)) { + /* Not all drivers support this yet, so continue without hw + * feature data. */ + } else { + int ret = hostapd_select_hw_mode(iface); + if (ret < 0) { + wpa_printf(MSG_ERROR, "Could not select hw_mode and " + "channel. (%d)", ret); + return -1; + } + ret = hostapd_check_ht_capab(iface); + if (ret < 0) + return -1; + if (ret == 1) { + wpa_printf(MSG_DEBUG, "Interface initialization will " + "be completed in a callback"); + return 0; + } + } + return hostapd_setup_interface_complete(iface, 0); +} + + +int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err) +{ + struct hostapd_data *hapd = iface->bss[0]; + size_t j; + u8 *prev_addr; + + if (err) { + wpa_printf(MSG_ERROR, "Interface initialization failed"); + eloop_terminate(); + return -1; + } + + wpa_printf(MSG_DEBUG, "Completing interface initialization"); + if (hapd->iconf->channel) { + iface->freq = hostapd_hw_get_freq(hapd, hapd->iconf->channel); + wpa_printf(MSG_DEBUG, "Mode: %s Channel: %d " + "Frequency: %d MHz", + hostapd_hw_mode_txt(hapd->iconf->hw_mode), + hapd->iconf->channel, iface->freq); + + if (hostapd_set_freq(hapd, hapd->iconf->hw_mode, iface->freq, + hapd->iconf->channel, + hapd->iconf->ieee80211n, + hapd->iconf->secondary_channel)) { + wpa_printf(MSG_ERROR, "Could not set channel for " + "kernel driver"); + return -1; + } + } + + if (hapd->iconf->rts_threshold > -1 && + hostapd_set_rts(hapd, hapd->iconf->rts_threshold)) { + wpa_printf(MSG_ERROR, "Could not set RTS threshold for " + "kernel driver"); + return -1; + } + + if (hapd->iconf->fragm_threshold > -1 && + hostapd_set_frag(hapd, hapd->iconf->fragm_threshold)) { + wpa_printf(MSG_ERROR, "Could not set fragmentation threshold " + "for kernel driver"); + return -1; + } + + prev_addr = hapd->own_addr; + + for (j = 0; j < iface->num_bss; j++) { + hapd = iface->bss[j]; + if (j) + os_memcpy(hapd->own_addr, prev_addr, ETH_ALEN); + if (hostapd_setup_bss(hapd, j == 0)) + return -1; + if (hostapd_mac_comp_empty(hapd->conf->bssid) == 0) + prev_addr = hapd->own_addr; + } + + hostapd_tx_queue_params(iface); + + ap_list_init(iface); + + if (hostapd_driver_commit(hapd) < 0) { + wpa_printf(MSG_ERROR, "%s: Failed to commit driver " + "configuration", __func__); + return -1; + } + + wpa_printf(MSG_DEBUG, "%s: Setup of interface done.", + iface->bss[0]->conf->iface); + + return 0; +} + + +/** + * hostapd_setup_interface - Setup of an interface + * @iface: Pointer to interface data. + * Returns: 0 on success, -1 on failure + * + * Initializes the driver interface, validates the configuration, + * and sets driver parameters based on the configuration. + * Flushes old stations, sets the channel, encryption, + * beacons, and WDS links based on the configuration. + */ +int hostapd_setup_interface(struct hostapd_iface *iface) +{ + int ret; + + ret = setup_interface(iface); + if (ret) { + wpa_printf(MSG_ERROR, "%s: Unable to setup interface.", + iface->bss[0]->conf->iface); + return -1; + } + + return 0; +} + + +/** + * hostapd_alloc_bss_data - Allocate and initialize per-BSS data + * @hapd_iface: Pointer to interface data + * @conf: Pointer to per-interface configuration + * @bss: Pointer to per-BSS configuration for this BSS + * Returns: Pointer to allocated BSS data + * + * This function is used to allocate per-BSS data structure. This data will be + * freed after hostapd_cleanup() is called for it during interface + * deinitialization. + */ +struct hostapd_data * +hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface, + struct hostapd_config *conf, + struct hostapd_bss_config *bss) +{ + struct hostapd_data *hapd; + + hapd = os_zalloc(sizeof(*hapd)); + if (hapd == NULL) + return NULL; + + hostapd_set_driver_ops(&hapd->drv); + hapd->new_assoc_sta_cb = hostapd_new_assoc_sta; + hapd->iconf = conf; + hapd->conf = bss; + hapd->iface = hapd_iface; + hapd->driver = hapd->iconf->driver; + + return hapd; +} + + +void hostapd_interface_deinit(struct hostapd_iface *iface) +{ + size_t j; + + if (iface == NULL) + return; + + hostapd_cleanup_iface_pre(iface); + for (j = 0; j < iface->num_bss; j++) { + struct hostapd_data *hapd = iface->bss[j]; + hostapd_free_stas(hapd); + hostapd_flush_old_stations(hapd); + hostapd_cleanup(hapd); + } +} + + +void hostapd_interface_free(struct hostapd_iface *iface) +{ + size_t j; + for (j = 0; j < iface->num_bss; j++) + os_free(iface->bss[j]); + hostapd_cleanup_iface(iface); +} + + +/** + * hostapd_new_assoc_sta - Notify that a new station associated with the AP + * @hapd: Pointer to BSS data + * @sta: Pointer to the associated STA data + * @reassoc: 1 to indicate this was a re-association; 0 = first association + * + * This function will be called whenever a station associates with the AP. It + * can be called from ieee802_11.c for drivers that export MLME to hostapd and + * from drv_callbacks.c based on driver events for drivers that take care of + * management frames (IEEE 802.11 authentication and association) internally. + */ +void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, + int reassoc) +{ + if (hapd->tkip_countermeasures) { + hapd->drv.sta_deauth(hapd, sta->addr, + WLAN_REASON_MICHAEL_MIC_FAILURE); + return; + } + + hostapd_prune_associations(hapd, sta->addr); + + /* IEEE 802.11F (IAPP) */ + if (hapd->conf->ieee802_11f) + iapp_new_station(hapd->iapp, sta); + + /* 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 */ + ieee802_1x_new_station(hapd, sta); + if (reassoc) { + if (sta->auth_alg != WLAN_AUTH_FT && + !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) + wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH); + } else + wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm); +} diff --git a/contrib/wpa/src/ap/hostapd.h b/contrib/wpa/src/ap/hostapd.h new file mode 100644 index 0000000..d0d67c8 --- /dev/null +++ b/contrib/wpa/src/ap/hostapd.h @@ -0,0 +1,276 @@ +/* + * hostapd / Initialization and configuration + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef HOSTAPD_H +#define HOSTAPD_H + +#include "common/defs.h" + +struct wpa_driver_ops; +struct wpa_ctrl_dst; +struct radius_server_data; +struct upnp_wps_device_sm; +struct hapd_interfaces; +struct hostapd_data; +struct sta_info; +struct hostap_sta_driver_data; +struct ieee80211_ht_capabilities; +struct full_dynamic_vlan; + +struct hostapd_probereq_cb { + int (*cb)(void *ctx, const u8 *sa, const u8 *ie, size_t ie_len); + void *ctx; +}; + +#define HOSTAPD_RATE_BASIC 0x00000001 + +struct hostapd_rate_data { + int rate; /* rate in 100 kbps */ + int flags; /* HOSTAPD_RATE_ flags */ +}; + +struct hostapd_frame_info { + u32 channel; + u32 datarate; + u32 ssi_signal; +}; + + +struct hostapd_driver_ops { + int (*set_ap_wps_ie)(struct hostapd_data *hapd); + int (*send_mgmt_frame)(struct hostapd_data *hapd, const void *msg, + size_t len); + int (*send_eapol)(struct hostapd_data *hapd, const u8 *addr, + const u8 *data, size_t data_len, int encrypt); + int (*set_authorized)(struct hostapd_data *hapd, struct sta_info *sta, + int authorized); + int (*set_key)(const char *ifname, struct hostapd_data *hapd, + enum wpa_alg alg, const u8 *addr, int key_idx, + int set_tx, const u8 *seq, size_t seq_len, + const u8 *key, size_t key_len); + int (*read_sta_data)(struct hostapd_data *hapd, + struct hostap_sta_driver_data *data, + const u8 *addr); + int (*sta_clear_stats)(struct hostapd_data *hapd, const u8 *addr); + int (*set_sta_flags)(struct hostapd_data *hapd, struct sta_info *sta); + int (*set_drv_ieee8021x)(struct hostapd_data *hapd, const char *ifname, + int enabled); + int (*set_radius_acl_auth)(struct hostapd_data *hapd, + const u8 *mac, int accepted, + u32 session_timeout); + int (*set_radius_acl_expire)(struct hostapd_data *hapd, + const u8 *mac); + int (*set_bss_params)(struct hostapd_data *hapd, int use_protection); + int (*set_beacon)(struct hostapd_data *hapd, + const u8 *head, size_t head_len, + const u8 *tail, size_t tail_len, int dtim_period, + int beacon_int); + int (*vlan_if_add)(struct hostapd_data *hapd, const char *ifname); + int (*vlan_if_remove)(struct hostapd_data *hapd, const char *ifname); + int (*set_wds_sta)(struct hostapd_data *hapd, const u8 *addr, int aid, + int val); + int (*set_sta_vlan)(const char *ifname, struct hostapd_data *hapd, + const u8 *addr, int vlan_id); + int (*get_inact_sec)(struct hostapd_data *hapd, const u8 *addr); + int (*sta_deauth)(struct hostapd_data *hapd, const u8 *addr, + int reason); + int (*sta_disassoc)(struct hostapd_data *hapd, const u8 *addr, + int reason); + int (*sta_add)(struct hostapd_data *hapd, + const u8 *addr, u16 aid, u16 capability, + const u8 *supp_rates, size_t supp_rates_len, + u16 listen_interval, + const struct ieee80211_ht_capabilities *ht_capab); + int (*sta_remove)(struct hostapd_data *hapd, const u8 *addr); + int (*set_countermeasures)(struct hostapd_data *hapd, int enabled); +}; + +/** + * struct hostapd_data - hostapd per-BSS data structure + */ +struct hostapd_data { + struct hostapd_iface *iface; + struct hostapd_config *iconf; + struct hostapd_bss_config *conf; + int interface_added; /* virtual interface added for this BSS */ + + u8 own_addr[ETH_ALEN]; + + int num_sta; /* number of entries in sta_list */ + struct sta_info *sta_list; /* STA info list head */ +#define STA_HASH_SIZE 256 +#define STA_HASH(sta) (sta[5]) + struct sta_info *sta_hash[STA_HASH_SIZE]; + + /* + * Bitfield for indicating which AIDs are allocated. Only AID values + * 1-2007 are used and as such, the bit at index 0 corresponds to AID + * 1. + */ +#define AID_WORDS ((2008 + 31) / 32) + u32 sta_aid[AID_WORDS]; + + const struct wpa_driver_ops *driver; + void *drv_priv; + struct hostapd_driver_ops drv; + + void (*new_assoc_sta_cb)(struct hostapd_data *hapd, + struct sta_info *sta, int reassoc); + + void *msg_ctx; /* ctx for wpa_msg() calls */ + + struct radius_client_data *radius; + u32 acct_session_id_hi, acct_session_id_lo; + + struct iapp_data *iapp; + + struct hostapd_cached_radius_acl *acl_cache; + struct hostapd_acl_query_data *acl_queries; + + struct wpa_authenticator *wpa_auth; + struct eapol_authenticator *eapol_auth; + + struct rsn_preauth_interface *preauth_iface; + time_t michael_mic_failure; + int michael_mic_failures; + int tkip_countermeasures; + + int ctrl_sock; + struct wpa_ctrl_dst *ctrl_dst; + + void *ssl_ctx; + void *eap_sim_db_priv; + struct radius_server_data *radius_srv; + + int parameter_set_count; + +#ifdef CONFIG_FULL_DYNAMIC_VLAN + struct full_dynamic_vlan *full_dynamic_vlan; +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + + struct l2_packet_data *l2; + struct wps_context *wps; + + struct wpabuf *wps_beacon_ie; + struct wpabuf *wps_probe_resp_ie; +#ifdef CONFIG_WPS + unsigned int ap_pin_failures; + struct upnp_wps_device_sm *wps_upnp; + unsigned int ap_pin_lockout_time; +#endif /* CONFIG_WPS */ + + struct hostapd_probereq_cb *probereq_cb; + size_t num_probereq_cb; + + void (*public_action_cb)(void *ctx, const u8 *buf, size_t len, + int freq); + void *public_action_cb_ctx; + + void (*wps_reg_success_cb)(void *ctx, const u8 *mac_addr, + const u8 *uuid_e); + void *wps_reg_success_cb_ctx; +}; + + +/** + * struct hostapd_iface - hostapd per-interface data structure + */ +struct hostapd_iface { + struct hapd_interfaces *interfaces; + void *owner; + int (*reload_config)(struct hostapd_iface *iface); + struct hostapd_config * (*config_read_cb)(const char *config_fname); + char *config_fname; + struct hostapd_config *conf; + + size_t num_bss; + struct hostapd_data **bss; + + int num_ap; /* number of entries in ap_list */ + struct ap_info *ap_list; /* AP info list head */ + struct ap_info *ap_hash[STA_HASH_SIZE]; + struct ap_info *ap_iter_list; + + struct hostapd_hw_modes *hw_features; + int num_hw_features; + struct hostapd_hw_modes *current_mode; + /* Rates that are currently used (i.e., filtered copy of + * current_mode->channels */ + int num_rates; + struct hostapd_rate_data *current_rates; + int freq; + + u16 hw_flags; + + /* Number of associated Non-ERP stations (i.e., stations using 802.11b + * in 802.11g BSS) */ + int num_sta_non_erp; + + /* Number of associated stations that do not support Short Slot Time */ + int num_sta_no_short_slot_time; + + /* Number of associated stations that do not support Short Preamble */ + int num_sta_no_short_preamble; + + int olbc; /* Overlapping Legacy BSS Condition */ + + /* Number of HT associated stations that do not support greenfield */ + int num_sta_ht_no_gf; + + /* Number of associated non-HT stations */ + int num_sta_no_ht; + + /* Number of HT associated stations 20 MHz */ + int num_sta_ht_20mhz; + + /* Overlapping BSS information */ + int olbc_ht; + + u16 ht_op_mode; + void (*scan_cb)(struct hostapd_iface *iface); + + int (*ctrl_iface_init)(struct hostapd_data *hapd); + void (*ctrl_iface_deinit)(struct hostapd_data *hapd); + + int (*for_each_interface)(struct hapd_interfaces *interfaces, + int (*cb)(struct hostapd_iface *iface, + void *ctx), void *ctx); +}; + +/* hostapd.c */ +int hostapd_reload_config(struct hostapd_iface *iface); +struct hostapd_data * +hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface, + struct hostapd_config *conf, + struct hostapd_bss_config *bss); +int hostapd_setup_interface(struct hostapd_iface *iface); +int hostapd_setup_interface_complete(struct hostapd_iface *iface, int err); +void hostapd_interface_deinit(struct hostapd_iface *iface); +void hostapd_interface_free(struct hostapd_iface *iface); +void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta, + int reassoc); + +/* utils.c */ +int hostapd_register_probereq_cb(struct hostapd_data *hapd, + int (*cb)(void *ctx, const u8 *sa, + const u8 *ie, size_t ie_len), + void *ctx); +void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr); + +/* drv_callbacks.c (TODO: move to somewhere else?) */ +int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr, + const u8 *ie, size_t ielen); +void hostapd_notif_disassoc(struct hostapd_data *hapd, const u8 *addr); + +#endif /* HOSTAPD_H */ diff --git a/contrib/wpa/src/ap/hw_features.c b/contrib/wpa/src/ap/hw_features.c new file mode 100644 index 0000000..0159c72 --- /dev/null +++ b/contrib/wpa/src/ap/hw_features.c @@ -0,0 +1,731 @@ +/* + * hostapd / Hardware feature query and different modes + * Copyright 2002-2003, Instant802 Networks, Inc. + * Copyright 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "drivers/driver.h" +#include "hostapd.h" +#include "ap_config.h" +#include "ap_drv_ops.h" +#include "hw_features.h" + + +void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features, + size_t num_hw_features) +{ + size_t i; + + if (hw_features == NULL) + return; + + for (i = 0; i < num_hw_features; i++) { + os_free(hw_features[i].channels); + os_free(hw_features[i].rates); + } + + os_free(hw_features); +} + + +int hostapd_get_hw_features(struct hostapd_iface *iface) +{ + struct hostapd_data *hapd = iface->bss[0]; + int ret = 0, i, j; + u16 num_modes, flags; + struct hostapd_hw_modes *modes; + + if (hostapd_drv_none(hapd)) + return -1; + modes = hostapd_get_hw_feature_data(hapd, &num_modes, &flags); + if (modes == NULL) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "Fetching hardware channel/rate support not " + "supported."); + return -1; + } + + iface->hw_flags = flags; + + hostapd_free_hw_features(iface->hw_features, iface->num_hw_features); + iface->hw_features = modes; + iface->num_hw_features = num_modes; + + for (i = 0; i < num_modes; i++) { + struct hostapd_hw_modes *feature = &modes[i]; + /* set flag for channels we can use in current regulatory + * domain */ + for (j = 0; j < feature->num_channels; j++) { + /* + * Disable all channels that are marked not to allow + * IBSS operation or active scanning. In addition, + * disable all channels that require radar detection, + * since that (in addition to full DFS) is not yet + * supported. + */ + if (feature->channels[j].flag & + (HOSTAPD_CHAN_NO_IBSS | + HOSTAPD_CHAN_PASSIVE_SCAN | + HOSTAPD_CHAN_RADAR)) + feature->channels[j].flag |= + HOSTAPD_CHAN_DISABLED; + if (feature->channels[j].flag & HOSTAPD_CHAN_DISABLED) + continue; + wpa_printf(MSG_MSGDUMP, "Allowed channel: mode=%d " + "chan=%d freq=%d MHz max_tx_power=%d dBm", + feature->mode, + feature->channels[j].chan, + feature->channels[j].freq, + feature->channels[j].max_tx_power); + } + } + + return ret; +} + + +static int hostapd_prepare_rates(struct hostapd_data *hapd, + struct hostapd_hw_modes *mode) +{ + int i, num_basic_rates = 0; + int basic_rates_a[] = { 60, 120, 240, -1 }; + int basic_rates_b[] = { 10, 20, -1 }; + int basic_rates_g[] = { 10, 20, 55, 110, -1 }; + int *basic_rates; + + if (hapd->iconf->basic_rates) + basic_rates = hapd->iconf->basic_rates; + else switch (mode->mode) { + case HOSTAPD_MODE_IEEE80211A: + basic_rates = basic_rates_a; + break; + case HOSTAPD_MODE_IEEE80211B: + basic_rates = basic_rates_b; + break; + case HOSTAPD_MODE_IEEE80211G: + basic_rates = basic_rates_g; + break; + default: + return -1; + } + + if (hostapd_set_rate_sets(hapd, hapd->iconf->supported_rates, + basic_rates, mode->mode)) { + wpa_printf(MSG_ERROR, "Failed to update rate sets in kernel " + "module"); + } + + os_free(hapd->iface->current_rates); + hapd->iface->num_rates = 0; + + hapd->iface->current_rates = + os_zalloc(mode->num_rates * sizeof(struct hostapd_rate_data)); + if (!hapd->iface->current_rates) { + wpa_printf(MSG_ERROR, "Failed to allocate memory for rate " + "table."); + return -1; + } + + for (i = 0; i < mode->num_rates; i++) { + struct hostapd_rate_data *rate; + + if (hapd->iconf->supported_rates && + !hostapd_rate_found(hapd->iconf->supported_rates, + mode->rates[i])) + continue; + + rate = &hapd->iface->current_rates[hapd->iface->num_rates]; + rate->rate = mode->rates[i]; + if (hostapd_rate_found(basic_rates, rate->rate)) { + rate->flags |= HOSTAPD_RATE_BASIC; + num_basic_rates++; + } + wpa_printf(MSG_DEBUG, "RATE[%d] rate=%d flags=0x%x", + hapd->iface->num_rates, rate->rate, rate->flags); + hapd->iface->num_rates++; + } + + if (hapd->iface->num_rates == 0 || num_basic_rates == 0) { + wpa_printf(MSG_ERROR, "No rates remaining in supported/basic " + "rate sets (%d,%d).", + hapd->iface->num_rates, num_basic_rates); + return -1; + } + + return 0; +} + + +#ifdef CONFIG_IEEE80211N +static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface) +{ + int sec_chan, ok, j, first; + int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149, 157, + 184, 192 }; + size_t k; + + if (!iface->conf->secondary_channel) + return 1; /* HT40 not used */ + + sec_chan = iface->conf->channel + iface->conf->secondary_channel * 4; + wpa_printf(MSG_DEBUG, "HT40: control channel: %d " + "secondary channel: %d", + iface->conf->channel, sec_chan); + + /* Verify that HT40 secondary channel is an allowed 20 MHz + * channel */ + ok = 0; + for (j = 0; j < iface->current_mode->num_channels; j++) { + struct hostapd_channel_data *chan = + &iface->current_mode->channels[j]; + if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && + chan->chan == sec_chan) { + ok = 1; + break; + } + } + if (!ok) { + wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed", + sec_chan); + return 0; + } + + /* + * Verify that HT40 primary,secondary channel pair is allowed per + * IEEE 802.11n Annex J. This is only needed for 5 GHz band since + * 2.4 GHz rules allow all cases where the secondary channel fits into + * the list of allowed channels (already checked above). + */ + if (iface->current_mode->mode != HOSTAPD_MODE_IEEE80211A) + return 1; + + if (iface->conf->secondary_channel > 0) + first = iface->conf->channel; + else + first = sec_chan; + + ok = 0; + for (k = 0; k < sizeof(allowed) / sizeof(allowed[0]); k++) { + if (first == allowed[k]) { + ok = 1; + break; + } + } + if (!ok) { + wpa_printf(MSG_ERROR, "HT40 channel pair (%d, %d) not allowed", + iface->conf->channel, + iface->conf->secondary_channel); + return 0; + } + + return 1; +} + + +static void ieee80211n_switch_pri_sec(struct hostapd_iface *iface) +{ + if (iface->conf->secondary_channel > 0) { + iface->conf->channel += 4; + iface->conf->secondary_channel = -1; + } else { + iface->conf->channel -= 4; + iface->conf->secondary_channel = 1; + } +} + + +static void ieee80211n_get_pri_sec_chan(struct wpa_scan_res *bss, + int *pri_chan, int *sec_chan) +{ + struct ieee80211_ht_operation *oper; + struct ieee802_11_elems elems; + + *pri_chan = *sec_chan = 0; + + ieee802_11_parse_elems((u8 *) (bss + 1), bss->ie_len, &elems, 0); + if (elems.ht_operation && + elems.ht_operation_len >= sizeof(*oper)) { + oper = (struct ieee80211_ht_operation *) elems.ht_operation; + *pri_chan = oper->control_chan; + if (oper->ht_param & HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH) { + if (oper->ht_param & + HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE) + *sec_chan = *pri_chan + 4; + else if (oper->ht_param & + HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW) + *sec_chan = *pri_chan - 4; + } + } +} + + +static int ieee80211n_check_40mhz_5g(struct hostapd_iface *iface, + struct wpa_scan_results *scan_res) +{ + int pri_chan, sec_chan, pri_freq, sec_freq, pri_bss, sec_bss; + int bss_pri_chan, bss_sec_chan; + size_t i; + int match; + + pri_chan = iface->conf->channel; + sec_chan = iface->conf->secondary_channel * 4; + pri_freq = hostapd_hw_get_freq(iface->bss[0], pri_chan); + if (iface->conf->secondary_channel > 0) + sec_freq = pri_freq + 20; + else + sec_freq = pri_freq - 20; + + /* + * Switch PRI/SEC channels if Beacons were detected on selected SEC + * channel, but not on selected PRI channel. + */ + pri_bss = sec_bss = 0; + for (i = 0; i < scan_res->num; i++) { + struct wpa_scan_res *bss = scan_res->res[i]; + if (bss->freq == pri_freq) + pri_bss++; + else if (bss->freq == sec_freq) + sec_bss++; + } + if (sec_bss && !pri_bss) { + wpa_printf(MSG_INFO, "Switch own primary and secondary " + "channel to get secondary channel with no Beacons " + "from other BSSes"); + ieee80211n_switch_pri_sec(iface); + } + + /* + * Match PRI/SEC channel with any existing HT40 BSS on the same + * channels that we are about to use (if already mixed order in + * existing BSSes, use own preference). + */ + match = 0; + for (i = 0; i < scan_res->num; i++) { + struct wpa_scan_res *bss = scan_res->res[i]; + ieee80211n_get_pri_sec_chan(bss, &bss_pri_chan, &bss_sec_chan); + if (pri_chan == bss_pri_chan && + sec_chan == bss_sec_chan) { + match = 1; + break; + } + } + if (!match) { + for (i = 0; i < scan_res->num; i++) { + struct wpa_scan_res *bss = scan_res->res[i]; + ieee80211n_get_pri_sec_chan(bss, &bss_pri_chan, + &bss_sec_chan); + if (pri_chan == bss_sec_chan && + sec_chan == bss_pri_chan) { + wpa_printf(MSG_INFO, "Switch own primary and " + "secondary channel due to BSS " + "overlap with " MACSTR, + MAC2STR(bss->bssid)); + ieee80211n_switch_pri_sec(iface); + break; + } + } + } + + return 1; +} + + +static int ieee80211n_check_40mhz_2g4(struct hostapd_iface *iface, + struct wpa_scan_results *scan_res) +{ + int pri_freq, sec_freq; + int affected_start, affected_end; + size_t i; + + pri_freq = hostapd_hw_get_freq(iface->bss[0], iface->conf->channel); + if (iface->conf->secondary_channel > 0) + sec_freq = pri_freq + 20; + else + sec_freq = pri_freq - 20; + affected_start = (pri_freq + sec_freq) / 2 - 25; + affected_end = (pri_freq + sec_freq) / 2 + 25; + wpa_printf(MSG_DEBUG, "40 MHz affected channel range: [%d,%d] MHz", + affected_start, affected_end); + for (i = 0; i < scan_res->num; i++) { + struct wpa_scan_res *bss = scan_res->res[i]; + int pri = bss->freq; + int sec = pri; + int sec_chan, pri_chan; + + ieee80211n_get_pri_sec_chan(bss, &pri_chan, &sec_chan); + + if (sec_chan) { + if (sec_chan < pri_chan) + sec = pri - 20; + else + sec = pri + 20; + } + + if ((pri < affected_start || pri > affected_end) && + (sec < affected_start || sec > affected_end)) + continue; /* not within affected channel range */ + + wpa_printf(MSG_DEBUG, "Neighboring BSS: " MACSTR + " freq=%d pri=%d sec=%d", + MAC2STR(bss->bssid), bss->freq, pri_chan, sec_chan); + + if (sec_chan) { + if (pri_freq != pri || sec_freq != sec) { + wpa_printf(MSG_DEBUG, "40 MHz pri/sec " + "mismatch with BSS " MACSTR + " <%d,%d> (chan=%d%c) vs. <%d,%d>", + MAC2STR(bss->bssid), + pri, sec, pri_chan, + sec > pri ? '+' : '-', + pri_freq, sec_freq); + return 0; + } + } + + /* TODO: 40 MHz intolerant */ + } + + return 1; +} + + +static void wpa_scan_results_free(struct wpa_scan_results *res) +{ + size_t i; + + if (res == NULL) + return; + + for (i = 0; i < res->num; i++) + os_free(res->res[i]); + os_free(res->res); + os_free(res); +} + + +static void ieee80211n_check_scan(struct hostapd_iface *iface) +{ + struct wpa_scan_results *scan_res; + int oper40; + + /* Check list of neighboring BSSes (from scan) to see whether 40 MHz is + * allowed per IEEE 802.11n/D7.0, 11.14.3.2 */ + + iface->scan_cb = NULL; + + scan_res = hostapd_driver_get_scan_results(iface->bss[0]); + if (scan_res == NULL) { + hostapd_setup_interface_complete(iface, 1); + return; + } + + if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A) + oper40 = ieee80211n_check_40mhz_5g(iface, scan_res); + else + oper40 = ieee80211n_check_40mhz_2g4(iface, scan_res); + wpa_scan_results_free(scan_res); + + if (!oper40) { + wpa_printf(MSG_INFO, "20/40 MHz operation not permitted on " + "channel pri=%d sec=%d based on overlapping BSSes", + iface->conf->channel, + iface->conf->channel + + iface->conf->secondary_channel * 4); + iface->conf->secondary_channel = 0; + iface->conf->ht_capab &= ~HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET; + } + + hostapd_setup_interface_complete(iface, 0); +} + + +static int ieee80211n_check_40mhz(struct hostapd_iface *iface) +{ + struct wpa_driver_scan_params params; + + if (!iface->conf->secondary_channel) + return 0; /* HT40 not used */ + + wpa_printf(MSG_DEBUG, "Scan for neighboring BSSes prior to enabling " + "40 MHz channel"); + os_memset(¶ms, 0, sizeof(params)); + /* TODO: scan only the needed frequency */ + if (hostapd_driver_scan(iface->bss[0], ¶ms) < 0) { + wpa_printf(MSG_ERROR, "Failed to request a scan of " + "neighboring BSSes"); + return -1; + } + + iface->scan_cb = ieee80211n_check_scan; + return 1; +} + + +static int ieee80211n_supported_ht_capab(struct hostapd_iface *iface) +{ + u16 hw = iface->current_mode->ht_capab; + u16 conf = iface->conf->ht_capab; + + if (!iface->conf->ieee80211n) + return 1; + + if ((conf & HT_CAP_INFO_LDPC_CODING_CAP) && + !(hw & HT_CAP_INFO_LDPC_CODING_CAP)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [LDPC]"); + return 0; + } + + if ((conf & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) && + !(hw & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [HT40*]"); + return 0; + } + + if ((conf & HT_CAP_INFO_SMPS_MASK) != (hw & HT_CAP_INFO_SMPS_MASK) && + (conf & HT_CAP_INFO_SMPS_MASK) != HT_CAP_INFO_SMPS_DISABLED) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [SMPS-*]"); + return 0; + } + + if ((conf & HT_CAP_INFO_GREEN_FIELD) && + !(hw & HT_CAP_INFO_GREEN_FIELD)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [GF]"); + return 0; + } + + if ((conf & HT_CAP_INFO_SHORT_GI20MHZ) && + !(hw & HT_CAP_INFO_SHORT_GI20MHZ)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [SHORT-GI-20]"); + return 0; + } + + if ((conf & HT_CAP_INFO_SHORT_GI40MHZ) && + !(hw & HT_CAP_INFO_SHORT_GI40MHZ)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [SHORT-GI-40]"); + return 0; + } + + if ((conf & HT_CAP_INFO_TX_STBC) && !(hw & HT_CAP_INFO_TX_STBC)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [TX-STBC]"); + return 0; + } + + if ((conf & HT_CAP_INFO_RX_STBC_MASK) > + (hw & HT_CAP_INFO_RX_STBC_MASK)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [RX-STBC*]"); + return 0; + } + + if ((conf & HT_CAP_INFO_DELAYED_BA) && + !(hw & HT_CAP_INFO_DELAYED_BA)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [DELAYED-BA]"); + return 0; + } + + if ((conf & HT_CAP_INFO_MAX_AMSDU_SIZE) && + !(hw & HT_CAP_INFO_MAX_AMSDU_SIZE)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [MAX-AMSDU-7935]"); + return 0; + } + + if ((conf & HT_CAP_INFO_DSSS_CCK40MHZ) && + !(hw & HT_CAP_INFO_DSSS_CCK40MHZ)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [DSSS_CCK-40]"); + return 0; + } + + if ((conf & HT_CAP_INFO_PSMP_SUPP) && !(hw & HT_CAP_INFO_PSMP_SUPP)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [PSMP]"); + return 0; + } + + if ((conf & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT) && + !(hw & HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT)) { + wpa_printf(MSG_ERROR, "Driver does not support configured " + "HT capability [LSIG-TXOP-PROT]"); + return 0; + } + + return 1; +} + +#endif /* CONFIG_IEEE80211N */ + + +int hostapd_check_ht_capab(struct hostapd_iface *iface) +{ +#ifdef CONFIG_IEEE80211N + int ret; + ret = ieee80211n_check_40mhz(iface); + if (ret) + return ret; + if (!ieee80211n_allowed_ht40_channel_pair(iface)) + return -1; + if (!ieee80211n_supported_ht_capab(iface)) + return -1; +#endif /* CONFIG_IEEE80211N */ + + return 0; +} + + +/** + * hostapd_select_hw_mode - Select the hardware mode + * @iface: Pointer to interface data. + * Returns: 0 on success, -1 on failure + * + * Sets up the hardware mode, channel, rates, and passive scanning + * based on the configuration. + */ +int hostapd_select_hw_mode(struct hostapd_iface *iface) +{ + int i, j, ok; + + if (iface->num_hw_features < 1) + return -1; + + iface->current_mode = NULL; + for (i = 0; i < iface->num_hw_features; i++) { + struct hostapd_hw_modes *mode = &iface->hw_features[i]; + if (mode->mode == iface->conf->hw_mode) { + iface->current_mode = mode; + break; + } + } + + if (iface->current_mode == NULL) { + wpa_printf(MSG_ERROR, "Hardware does not support configured " + "mode"); + hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "Hardware does not support configured mode " + "(%d)", (int) iface->conf->hw_mode); + return -1; + } + + ok = 0; + for (j = 0; j < iface->current_mode->num_channels; j++) { + struct hostapd_channel_data *chan = + &iface->current_mode->channels[j]; + if (!(chan->flag & HOSTAPD_CHAN_DISABLED) && + (chan->chan == iface->conf->channel)) { + ok = 1; + break; + } + } + if (iface->conf->channel == 0) { + /* TODO: could request a scan of neighboring BSSes and select + * the channel automatically */ + wpa_printf(MSG_ERROR, "Channel not configured " + "(hw_mode/channel in hostapd.conf)"); + return -1; + } + if (ok == 0 && iface->conf->channel != 0) { + hostapd_logger(iface->bss[0], NULL, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "Configured channel (%d) not found from the " + "channel list of current mode (%d) %s", + iface->conf->channel, + iface->current_mode->mode, + hostapd_hw_mode_txt(iface->current_mode->mode)); + iface->current_mode = NULL; + } + + if (iface->current_mode == NULL) { + hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "Hardware does not support configured channel"); + return -1; + } + + if (hostapd_prepare_rates(iface->bss[0], iface->current_mode)) { + wpa_printf(MSG_ERROR, "Failed to prepare rates table."); + hostapd_logger(iface->bss[0], NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_WARNING, + "Failed to prepare rates table."); + return -1; + } + + return 0; +} + + +const char * hostapd_hw_mode_txt(int mode) +{ + switch (mode) { + case HOSTAPD_MODE_IEEE80211A: + return "IEEE 802.11a"; + case HOSTAPD_MODE_IEEE80211B: + return "IEEE 802.11b"; + case HOSTAPD_MODE_IEEE80211G: + return "IEEE 802.11g"; + default: + return "UNKNOWN"; + } +} + + +int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan) +{ + int i; + + if (!hapd->iface->current_mode) + return 0; + + for (i = 0; i < hapd->iface->current_mode->num_channels; i++) { + struct hostapd_channel_data *ch = + &hapd->iface->current_mode->channels[i]; + if (ch->chan == chan) + return ch->freq; + } + + return 0; +} + + +int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq) +{ + int i; + + if (!hapd->iface->current_mode) + return 0; + + for (i = 0; i < hapd->iface->current_mode->num_channels; i++) { + struct hostapd_channel_data *ch = + &hapd->iface->current_mode->channels[i]; + if (ch->freq == freq) + return ch->chan; + } + + return 0; +} diff --git a/contrib/wpa/src/ap/hw_features.h b/contrib/wpa/src/ap/hw_features.h new file mode 100644 index 0000000..0295549 --- /dev/null +++ b/contrib/wpa/src/ap/hw_features.h @@ -0,0 +1,62 @@ +/* + * hostapd / Hardware feature query and different modes + * Copyright 2002-2003, Instant802 Networks, Inc. + * Copyright 2005-2006, Devicescape Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef HW_FEATURES_H +#define HW_FEATURES_H + +#ifdef NEED_AP_MLME +void hostapd_free_hw_features(struct hostapd_hw_modes *hw_features, + size_t num_hw_features); +int hostapd_get_hw_features(struct hostapd_iface *iface); +int hostapd_select_hw_mode(struct hostapd_iface *iface); +const char * hostapd_hw_mode_txt(int mode); +int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan); +int hostapd_hw_get_channel(struct hostapd_data *hapd, int freq); +int hostapd_check_ht_capab(struct hostapd_iface *iface); +#else /* NEED_AP_MLME */ +static inline void +hostapd_free_hw_features(struct hostapd_hw_modes *hw_features, + size_t num_hw_features) +{ +} + +static inline int hostapd_get_hw_features(struct hostapd_iface *iface) +{ + return -1; +} + +static inline int hostapd_select_hw_mode(struct hostapd_iface *iface) +{ + return -1; +} + +static inline const char * hostapd_hw_mode_txt(int mode) +{ + return NULL; +} + +static inline int hostapd_hw_get_freq(struct hostapd_data *hapd, int chan) +{ + return -1; +} + +static inline int hostapd_check_ht_capab(struct hostapd_iface *iface) +{ + return 0; +} + +#endif /* NEED_AP_MLME */ + +#endif /* HW_FEATURES_H */ diff --git a/contrib/wpa/src/ap/iapp.c b/contrib/wpa/src/ap/iapp.c new file mode 100644 index 0000000..115d91e --- /dev/null +++ b/contrib/wpa/src/ap/iapp.c @@ -0,0 +1,535 @@ +/* + * hostapd / IEEE 802.11F-2003 Inter-Access Point Protocol (IAPP) + * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published 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: IEEE 802.11F-2003 was a experimental use specification. It has expired + * and IEEE has withdrawn it. In other words, it is likely better to look at + * using some other mechanism for AP-to-AP communication than extending the + * implementation here. + */ + +/* TODO: + * Level 1: no administrative or security support + * (e.g., static BSSID to IP address mapping in each AP) + * Level 2: support for dynamic mapping of BSSID to IP address + * Level 3: support for encryption and authentication of IAPP messages + * - add support for MOVE-notify and MOVE-response (this requires support for + * finding out IP address for previous AP using RADIUS) + * - add support for Send- and ACK-Security-Block to speedup IEEE 802.1X during + * reassociation to another AP + * - implement counters etc. for IAPP MIB + * - verify endianness of fields in IAPP messages; are they big-endian as + * used here? + * - RADIUS connection for AP registration and BSSID to IP address mapping + * - TCP connection for IAPP MOVE, CACHE + * - broadcast ESP for IAPP ADD-notify + * - ESP for IAPP MOVE messages + * - security block sending/processing + * - IEEE 802.11 context transfer + */ + +#include "utils/includes.h" +#include <net/if.h> +#include <sys/ioctl.h> +#ifdef USE_KERNEL_HEADERS +#include <linux/if_packet.h> +#else /* USE_KERNEL_HEADERS */ +#include <netpacket/packet.h> +#endif /* USE_KERNEL_HEADERS */ + +#include "utils/common.h" +#include "utils/eloop.h" +#include "common/ieee802_11_defs.h" +#include "hostapd.h" +#include "ap_config.h" +#include "ieee802_11.h" +#include "sta_info.h" +#include "iapp.h" + + +#define IAPP_MULTICAST "224.0.1.178" +#define IAPP_UDP_PORT 3517 +#define IAPP_TCP_PORT 3517 + +struct iapp_hdr { + u8 version; + u8 command; + be16 identifier; + be16 length; + /* followed by length-6 octets of data */ +} __attribute__ ((packed)); + +#define IAPP_VERSION 0 + +enum IAPP_COMMAND { + IAPP_CMD_ADD_notify = 0, + IAPP_CMD_MOVE_notify = 1, + IAPP_CMD_MOVE_response = 2, + IAPP_CMD_Send_Security_Block = 3, + IAPP_CMD_ACK_Security_Block = 4, + IAPP_CMD_CACHE_notify = 5, + IAPP_CMD_CACHE_response = 6, +}; + + +/* ADD-notify - multicast UDP on the local LAN */ +struct iapp_add_notify { + u8 addr_len; /* ETH_ALEN */ + u8 reserved; + u8 mac_addr[ETH_ALEN]; + be16 seq_num; +} __attribute__ ((packed)); + + +/* Layer 2 Update frame (802.2 Type 1 LLC XID Update response) */ +struct iapp_layer2_update { + u8 da[ETH_ALEN]; /* broadcast */ + u8 sa[ETH_ALEN]; /* STA addr */ + be16 len; /* 6 */ + u8 dsap; /* null DSAP address */ + u8 ssap; /* null SSAP address, CR=Response */ + u8 control; + u8 xid_info[3]; +} __attribute__ ((packed)); + + +/* MOVE-notify - unicast TCP */ +struct iapp_move_notify { + u8 addr_len; /* ETH_ALEN */ + u8 reserved; + u8 mac_addr[ETH_ALEN]; + u16 seq_num; + u16 ctx_block_len; + /* followed by ctx_block_len bytes */ +} __attribute__ ((packed)); + + +/* MOVE-response - unicast TCP */ +struct iapp_move_response { + u8 addr_len; /* ETH_ALEN */ + u8 status; + u8 mac_addr[ETH_ALEN]; + u16 seq_num; + u16 ctx_block_len; + /* followed by ctx_block_len bytes */ +} __attribute__ ((packed)); + +enum { + IAPP_MOVE_SUCCESSFUL = 0, + IAPP_MOVE_DENIED = 1, + IAPP_MOVE_STALE_MOVE = 2, +}; + + +/* CACHE-notify */ +struct iapp_cache_notify { + u8 addr_len; /* ETH_ALEN */ + u8 reserved; + u8 mac_addr[ETH_ALEN]; + u16 seq_num; + u8 current_ap[ETH_ALEN]; + u16 ctx_block_len; + /* ctx_block_len bytes of context block followed by 16-bit context + * timeout */ +} __attribute__ ((packed)); + + +/* CACHE-response - unicast TCP */ +struct iapp_cache_response { + u8 addr_len; /* ETH_ALEN */ + u8 status; + u8 mac_addr[ETH_ALEN]; + u16 seq_num; +} __attribute__ ((packed)); + +enum { + IAPP_CACHE_SUCCESSFUL = 0, + IAPP_CACHE_STALE_CACHE = 1, +}; + + +/* Send-Security-Block - unicast TCP */ +struct iapp_send_security_block { + u8 iv[8]; + u16 sec_block_len; + /* followed by sec_block_len bytes of security block */ +} __attribute__ ((packed)); + + +/* ACK-Security-Block - unicast TCP */ +struct iapp_ack_security_block { + u8 iv[8]; + u8 new_ap_ack_authenticator[48]; +} __attribute__ ((packed)); + + +struct iapp_data { + struct hostapd_data *hapd; + u16 identifier; /* next IAPP identifier */ + struct in_addr own, multicast; + int udp_sock; + int packet_sock; +}; + + +static void iapp_send_add(struct iapp_data *iapp, u8 *mac_addr, u16 seq_num) +{ + char buf[128]; + struct iapp_hdr *hdr; + struct iapp_add_notify *add; + struct sockaddr_in addr; + + /* Send IAPP ADD-notify to remove possible association from other APs + */ + + hdr = (struct iapp_hdr *) buf; + hdr->version = IAPP_VERSION; + hdr->command = IAPP_CMD_ADD_notify; + hdr->identifier = host_to_be16(iapp->identifier++); + hdr->length = host_to_be16(sizeof(*hdr) + sizeof(*add)); + + add = (struct iapp_add_notify *) (hdr + 1); + add->addr_len = ETH_ALEN; + add->reserved = 0; + os_memcpy(add->mac_addr, mac_addr, ETH_ALEN); + + add->seq_num = host_to_be16(seq_num); + + os_memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = iapp->multicast.s_addr; + addr.sin_port = htons(IAPP_UDP_PORT); + if (sendto(iapp->udp_sock, buf, (char *) (add + 1) - buf, 0, + (struct sockaddr *) &addr, sizeof(addr)) < 0) + perror("sendto[IAPP-ADD]"); +} + + +static void iapp_send_layer2_update(struct iapp_data *iapp, u8 *addr) +{ + struct iapp_layer2_update msg; + + /* Send Level 2 Update Frame to update forwarding tables in layer 2 + * bridge devices */ + + /* 802.2 Type 1 Logical Link Control (LLC) Exchange Identifier (XID) + * Update response frame; IEEE Std 802.2-1998, 5.4.1.2.1 */ + + os_memset(msg.da, 0xff, ETH_ALEN); + os_memcpy(msg.sa, addr, ETH_ALEN); + msg.len = host_to_be16(6); + msg.dsap = 0; /* NULL DSAP address */ + msg.ssap = 0x01; /* NULL SSAP address, CR Bit: Response */ + msg.control = 0xaf; /* XID response lsb.1111F101. + * F=0 (no poll command; unsolicited frame) */ + msg.xid_info[0] = 0x81; /* XID format identifier */ + msg.xid_info[1] = 1; /* LLC types/classes: Type 1 LLC */ + msg.xid_info[2] = 1 << 1; /* XID sender's receive window size (RW) + * FIX: what is correct RW with 802.11? */ + + if (send(iapp->packet_sock, &msg, sizeof(msg), 0) < 0) + perror("send[L2 Update]"); +} + + +/** + * iapp_new_station - IAPP processing for a new STA + * @iapp: IAPP data + * @sta: The associated station + */ +void iapp_new_station(struct iapp_data *iapp, struct sta_info *sta) +{ + struct ieee80211_mgmt *assoc; + u16 seq; + + if (iapp == NULL) + return; + + assoc = sta->last_assoc_req; + seq = assoc ? WLAN_GET_SEQ_SEQ(le_to_host16(assoc->seq_ctrl)) : 0; + + /* IAPP-ADD.request(MAC Address, Sequence Number, Timeout) */ + hostapd_logger(iapp->hapd, sta->addr, HOSTAPD_MODULE_IAPP, + HOSTAPD_LEVEL_DEBUG, "IAPP-ADD.request(seq=%d)", seq); + iapp_send_layer2_update(iapp, sta->addr); + iapp_send_add(iapp, sta->addr, seq); + + if (assoc && WLAN_FC_GET_STYPE(le_to_host16(assoc->frame_control)) == + WLAN_FC_STYPE_REASSOC_REQ) { + /* IAPP-MOVE.request(MAC Address, Sequence Number, Old AP, + * Context Block, Timeout) + */ + /* TODO: Send IAPP-MOVE to the old AP; Map Old AP BSSID to + * IP address */ + } +} + + +static void iapp_process_add_notify(struct iapp_data *iapp, + struct sockaddr_in *from, + struct iapp_hdr *hdr, int len) +{ + struct iapp_add_notify *add = (struct iapp_add_notify *) (hdr + 1); + struct sta_info *sta; + + if (len != sizeof(*add)) { + printf("Invalid IAPP-ADD packet length %d (expected %lu)\n", + len, (unsigned long) sizeof(*add)); + return; + } + + sta = ap_get_sta(iapp->hapd, add->mac_addr); + + /* IAPP-ADD.indication(MAC Address, Sequence Number) */ + hostapd_logger(iapp->hapd, add->mac_addr, HOSTAPD_MODULE_IAPP, + HOSTAPD_LEVEL_INFO, + "Received IAPP ADD-notify (seq# %d) from %s:%d%s", + be_to_host16(add->seq_num), + inet_ntoa(from->sin_addr), ntohs(from->sin_port), + sta ? "" : " (STA not found)"); + + if (!sta) + return; + + /* TODO: could use seq_num to try to determine whether last association + * to this AP is newer than the one advertised in IAPP-ADD. Although, + * this is not really a reliable verification. */ + + hostapd_logger(iapp->hapd, add->mac_addr, HOSTAPD_MODULE_IAPP, + HOSTAPD_LEVEL_DEBUG, + "Removing STA due to IAPP ADD-notify"); + ap_sta_disconnect(iapp->hapd, sta, NULL, 0); +} + + +/** + * iapp_receive_udp - Process IAPP UDP frames + * @sock: File descriptor for the socket + * @eloop_ctx: IAPP data (struct iapp_data *) + * @sock_ctx: Not used + */ +static void iapp_receive_udp(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct iapp_data *iapp = eloop_ctx; + int len, hlen; + unsigned char buf[128]; + struct sockaddr_in from; + socklen_t fromlen; + struct iapp_hdr *hdr; + + /* Handle incoming IAPP frames (over UDP/IP) */ + + fromlen = sizeof(from); + len = recvfrom(iapp->udp_sock, buf, sizeof(buf), 0, + (struct sockaddr *) &from, &fromlen); + if (len < 0) { + perror("recvfrom"); + return; + } + + if (from.sin_addr.s_addr == iapp->own.s_addr) + return; /* ignore own IAPP messages */ + + hostapd_logger(iapp->hapd, NULL, HOSTAPD_MODULE_IAPP, + HOSTAPD_LEVEL_DEBUG, + "Received %d byte IAPP frame from %s%s\n", + len, inet_ntoa(from.sin_addr), + len < (int) sizeof(*hdr) ? " (too short)" : ""); + + if (len < (int) sizeof(*hdr)) + return; + + hdr = (struct iapp_hdr *) buf; + hlen = be_to_host16(hdr->length); + hostapd_logger(iapp->hapd, NULL, HOSTAPD_MODULE_IAPP, + HOSTAPD_LEVEL_DEBUG, + "RX: version=%d command=%d id=%d len=%d\n", + hdr->version, hdr->command, + be_to_host16(hdr->identifier), hlen); + if (hdr->version != IAPP_VERSION) { + printf("Dropping IAPP frame with unknown version %d\n", + hdr->version); + return; + } + if (hlen > len) { + printf("Underflow IAPP frame (hlen=%d len=%d)\n", hlen, len); + return; + } + if (hlen < len) { + printf("Ignoring %d extra bytes from IAPP frame\n", + len - hlen); + len = hlen; + } + + switch (hdr->command) { + case IAPP_CMD_ADD_notify: + iapp_process_add_notify(iapp, &from, hdr, hlen - sizeof(*hdr)); + break; + case IAPP_CMD_MOVE_notify: + /* TODO: MOVE is using TCP; so move this to TCP handler once it + * is implemented.. */ + /* IAPP-MOVE.indication(MAC Address, New BSSID, + * Sequence Number, AP Address, Context Block) */ + /* TODO: process */ + break; + default: + printf("Unknown IAPP command %d\n", hdr->command); + break; + } +} + + +struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface) +{ + struct ifreq ifr; + struct sockaddr_ll addr; + int ifindex; + struct sockaddr_in *paddr, uaddr; + struct iapp_data *iapp; + struct ip_mreqn mreq; + + iapp = os_zalloc(sizeof(*iapp)); + if (iapp == NULL) + return NULL; + iapp->hapd = hapd; + iapp->udp_sock = iapp->packet_sock = -1; + + /* TODO: + * open socket for sending and receiving IAPP frames over TCP + */ + + iapp->udp_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (iapp->udp_sock < 0) { + perror("socket[PF_INET,SOCK_DGRAM]"); + iapp_deinit(iapp); + return NULL; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, iface, sizeof(ifr.ifr_name)); + if (ioctl(iapp->udp_sock, SIOCGIFINDEX, &ifr) != 0) { + perror("ioctl(SIOCGIFINDEX)"); + iapp_deinit(iapp); + return NULL; + } + ifindex = ifr.ifr_ifindex; + + if (ioctl(iapp->udp_sock, SIOCGIFADDR, &ifr) != 0) { + perror("ioctl(SIOCGIFADDR)"); + iapp_deinit(iapp); + return NULL; + } + paddr = (struct sockaddr_in *) &ifr.ifr_addr; + if (paddr->sin_family != AF_INET) { + printf("Invalid address family %i (SIOCGIFADDR)\n", + paddr->sin_family); + iapp_deinit(iapp); + return NULL; + } + iapp->own.s_addr = paddr->sin_addr.s_addr; + + if (ioctl(iapp->udp_sock, SIOCGIFBRDADDR, &ifr) != 0) { + perror("ioctl(SIOCGIFBRDADDR)"); + iapp_deinit(iapp); + return NULL; + } + paddr = (struct sockaddr_in *) &ifr.ifr_addr; + if (paddr->sin_family != AF_INET) { + printf("Invalid address family %i (SIOCGIFBRDADDR)\n", + paddr->sin_family); + iapp_deinit(iapp); + return NULL; + } + inet_aton(IAPP_MULTICAST, &iapp->multicast); + + os_memset(&uaddr, 0, sizeof(uaddr)); + uaddr.sin_family = AF_INET; + uaddr.sin_port = htons(IAPP_UDP_PORT); + if (bind(iapp->udp_sock, (struct sockaddr *) &uaddr, + sizeof(uaddr)) < 0) { + perror("bind[UDP]"); + iapp_deinit(iapp); + return NULL; + } + + os_memset(&mreq, 0, sizeof(mreq)); + mreq.imr_multiaddr = iapp->multicast; + mreq.imr_address.s_addr = INADDR_ANY; + mreq.imr_ifindex = 0; + if (setsockopt(iapp->udp_sock, SOL_IP, IP_ADD_MEMBERSHIP, &mreq, + sizeof(mreq)) < 0) { + perror("setsockopt[UDP,IP_ADD_MEMBERSHIP]"); + iapp_deinit(iapp); + return NULL; + } + + iapp->packet_sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_ALL)); + if (iapp->packet_sock < 0) { + perror("socket[PF_PACKET,SOCK_RAW]"); + iapp_deinit(iapp); + return NULL; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sll_family = AF_PACKET; + addr.sll_ifindex = ifindex; + if (bind(iapp->packet_sock, (struct sockaddr *) &addr, + sizeof(addr)) < 0) { + perror("bind[PACKET]"); + iapp_deinit(iapp); + return NULL; + } + + if (eloop_register_read_sock(iapp->udp_sock, iapp_receive_udp, + iapp, NULL)) { + printf("Could not register read socket for IAPP.\n"); + iapp_deinit(iapp); + return NULL; + } + + printf("IEEE 802.11F (IAPP) using interface %s\n", iface); + + /* TODO: For levels 2 and 3: send RADIUS Initiate-Request, receive + * RADIUS Initiate-Accept or Initiate-Reject. IAPP port should actually + * be openned only after receiving Initiate-Accept. If Initiate-Reject + * is received, IAPP is not started. */ + + return iapp; +} + + +void iapp_deinit(struct iapp_data *iapp) +{ + struct ip_mreqn mreq; + + if (iapp == NULL) + return; + + if (iapp->udp_sock >= 0) { + os_memset(&mreq, 0, sizeof(mreq)); + mreq.imr_multiaddr = iapp->multicast; + mreq.imr_address.s_addr = INADDR_ANY; + mreq.imr_ifindex = 0; + if (setsockopt(iapp->udp_sock, SOL_IP, IP_DROP_MEMBERSHIP, + &mreq, sizeof(mreq)) < 0) { + perror("setsockopt[UDP,IP_DEL_MEMBERSHIP]"); + } + + eloop_unregister_read_sock(iapp->udp_sock); + close(iapp->udp_sock); + } + if (iapp->packet_sock >= 0) { + eloop_unregister_read_sock(iapp->packet_sock); + close(iapp->packet_sock); + } + os_free(iapp); +} diff --git a/contrib/wpa/src/ap/iapp.h b/contrib/wpa/src/ap/iapp.h new file mode 100644 index 0000000..5fc01cb --- /dev/null +++ b/contrib/wpa/src/ap/iapp.h @@ -0,0 +1,45 @@ +/* + * hostapd / IEEE 802.11F-2003 Inter-Access Point Protocol (IAPP) + * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef IAPP_H +#define IAPP_H + +struct iapp_data; + +#ifdef CONFIG_IAPP + +void iapp_new_station(struct iapp_data *iapp, struct sta_info *sta); +struct iapp_data * iapp_init(struct hostapd_data *hapd, const char *iface); +void iapp_deinit(struct iapp_data *iapp); + +#else /* CONFIG_IAPP */ + +static inline void iapp_new_station(struct iapp_data *iapp, + struct sta_info *sta) +{ +} + +static inline struct iapp_data * iapp_init(struct hostapd_data *hapd, + const char *iface) +{ + return NULL; +} + +static inline void iapp_deinit(struct iapp_data *iapp) +{ +} + +#endif /* CONFIG_IAPP */ + +#endif /* IAPP_H */ diff --git a/contrib/wpa/src/ap/ieee802_11.c b/contrib/wpa/src/ap/ieee802_11.c new file mode 100644 index 0000000..3375aa2 --- /dev/null +++ b/contrib/wpa/src/ap/ieee802_11.c @@ -0,0 +1,1755 @@ +/* + * hostapd / IEEE 802.11 Management + * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published 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 "utils/includes.h" + +#ifndef CONFIG_NATIVE_WINDOWS + +#include "utils/common.h" +#include "utils/eloop.h" +#include "crypto/crypto.h" +#include "drivers/driver.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "common/wpa_ctrl.h" +#include "radius/radius.h" +#include "radius/radius_client.h" +#include "wps/wps.h" +#include "hostapd.h" +#include "beacon.h" +#include "ieee802_11_auth.h" +#include "sta_info.h" +#include "ieee802_1x.h" +#include "wpa_auth.h" +#include "wmm.h" +#include "ap_list.h" +#include "accounting.h" +#include "ap_config.h" +#include "ap_mlme.h" +#include "ieee802_11.h" + + +u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; + int i, num, count; + + if (hapd->iface->current_rates == NULL) + return eid; + + *pos++ = WLAN_EID_SUPP_RATES; + num = hapd->iface->num_rates; + if (num > 8) { + /* rest of the rates are encoded in Extended supported + * rates element */ + num = 8; + } + + *pos++ = num; + count = 0; + for (i = 0, count = 0; i < hapd->iface->num_rates && count < num; + i++) { + count++; + *pos = hapd->iface->current_rates[i].rate / 5; + if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC) + *pos |= 0x80; + pos++; + } + + return pos; +} + + +u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; + int i, num, count; + + if (hapd->iface->current_rates == NULL) + return eid; + + num = hapd->iface->num_rates; + if (num <= 8) + return eid; + num -= 8; + + *pos++ = WLAN_EID_EXT_SUPP_RATES; + *pos++ = num; + count = 0; + for (i = 0, count = 0; i < hapd->iface->num_rates && count < num + 8; + i++) { + count++; + if (count <= 8) + continue; /* already in SuppRates IE */ + *pos = hapd->iface->current_rates[i].rate / 5; + if (hapd->iface->current_rates[i].flags & HOSTAPD_RATE_BASIC) + *pos |= 0x80; + pos++; + } + + return pos; +} + + +u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, + int probe) +{ + int capab = WLAN_CAPABILITY_ESS; + int privacy; + + if (hapd->iface->num_sta_no_short_preamble == 0 && + hapd->iconf->preamble == SHORT_PREAMBLE) + capab |= WLAN_CAPABILITY_SHORT_PREAMBLE; + + privacy = hapd->conf->ssid.wep.keys_set; + + if (hapd->conf->ieee802_1x && + (hapd->conf->default_wep_key_len || + hapd->conf->individual_wep_key_len)) + privacy = 1; + + if (hapd->conf->wpa) + privacy = 1; + + if (sta) { + int policy, def_klen; + if (probe && sta->ssid_probe) { + policy = sta->ssid_probe->security_policy; + def_klen = sta->ssid_probe->wep.default_len; + } else { + policy = sta->ssid->security_policy; + def_klen = sta->ssid->wep.default_len; + } + privacy = policy != SECURITY_PLAINTEXT; + if (policy == SECURITY_IEEE_802_1X && def_klen == 0) + privacy = 0; + } + + if (privacy) + capab |= WLAN_CAPABILITY_PRIVACY; + + if (hapd->iface->current_mode && + hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G && + hapd->iface->num_sta_no_short_slot_time == 0) + capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME; + + return capab; +} + + +#ifdef CONFIG_IEEE80211W +static u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd, + struct sta_info *sta, u8 *eid) +{ + u8 *pos = eid; + u32 timeout, tu; + struct os_time now, passed; + + *pos++ = WLAN_EID_TIMEOUT_INTERVAL; + *pos++ = 5; + *pos++ = WLAN_TIMEOUT_ASSOC_COMEBACK; + os_get_time(&now); + os_time_sub(&now, &sta->sa_query_start, &passed); + tu = (passed.sec * 1000000 + passed.usec) / 1024; + if (hapd->conf->assoc_sa_query_max_timeout > tu) + timeout = hapd->conf->assoc_sa_query_max_timeout - tu; + else + timeout = 0; + if (timeout < hapd->conf->assoc_sa_query_max_timeout) + timeout++; /* add some extra time for local timers */ + WPA_PUT_LE32(pos, timeout); + pos += 4; + + return pos; +} +#endif /* CONFIG_IEEE80211W */ + + +void ieee802_11_print_ssid(char *buf, const u8 *ssid, u8 len) +{ + int i; + if (len > HOSTAPD_MAX_SSID_LEN) + len = HOSTAPD_MAX_SSID_LEN; + for (i = 0; i < len; i++) { + if (ssid[i] >= 32 && ssid[i] < 127) + buf[i] = ssid[i]; + else + buf[i] = '.'; + } + buf[len] = '\0'; +} + + +static u16 auth_shared_key(struct hostapd_data *hapd, struct sta_info *sta, + u16 auth_transaction, const u8 *challenge, + int iswep) +{ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "authentication (shared key, transaction %d)", + auth_transaction); + + if (auth_transaction == 1) { + if (!sta->challenge) { + /* Generate a pseudo-random challenge */ + u8 key[8]; + time_t now; + int r; + sta->challenge = os_zalloc(WLAN_AUTH_CHALLENGE_LEN); + if (sta->challenge == NULL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + now = time(NULL); + r = random(); + os_memcpy(key, &now, 4); + os_memcpy(key + 4, &r, 4); + rc4_skip(key, sizeof(key), 0, + sta->challenge, WLAN_AUTH_CHALLENGE_LEN); + } + return 0; + } + + if (auth_transaction != 3) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + /* Transaction 3 */ + if (!iswep || !sta->challenge || !challenge || + os_memcmp(sta->challenge, challenge, WLAN_AUTH_CHALLENGE_LEN)) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "shared key authentication - invalid " + "challenge-response"); + return WLAN_STATUS_CHALLENGE_FAIL; + } + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "authentication OK (shared key)"); +#ifdef IEEE80211_REQUIRE_AUTH_ACK + /* Station will be marked authenticated if it ACKs the + * authentication reply. */ +#else + sta->flags |= WLAN_STA_AUTH; + wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); +#endif + os_free(sta->challenge); + sta->challenge = NULL; + + return 0; +} + + +static void send_auth_reply(struct hostapd_data *hapd, + const u8 *dst, const u8 *bssid, + u16 auth_alg, u16 auth_transaction, u16 resp, + const u8 *ies, size_t ies_len) +{ + struct ieee80211_mgmt *reply; + u8 *buf; + size_t rlen; + + rlen = IEEE80211_HDRLEN + sizeof(reply->u.auth) + ies_len; + buf = os_zalloc(rlen); + if (buf == NULL) + return; + + reply = (struct ieee80211_mgmt *) buf; + reply->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_AUTH); + os_memcpy(reply->da, dst, ETH_ALEN); + os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(reply->bssid, bssid, ETH_ALEN); + + reply->u.auth.auth_alg = host_to_le16(auth_alg); + reply->u.auth.auth_transaction = host_to_le16(auth_transaction); + reply->u.auth.status_code = host_to_le16(resp); + + if (ies && ies_len) + os_memcpy(reply->u.auth.variable, ies, ies_len); + + wpa_printf(MSG_DEBUG, "authentication reply: STA=" MACSTR + " auth_alg=%d auth_transaction=%d resp=%d (IE len=%lu)", + MAC2STR(dst), auth_alg, auth_transaction, + resp, (unsigned long) ies_len); + if (hapd->drv.send_mgmt_frame(hapd, reply, rlen) < 0) + perror("send_auth_reply: send"); + + os_free(buf); +} + + +#ifdef CONFIG_IEEE80211R +static void handle_auth_ft_finish(void *ctx, const u8 *dst, const u8 *bssid, + u16 auth_transaction, u16 status, + const u8 *ies, size_t ies_len) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + + send_auth_reply(hapd, dst, bssid, WLAN_AUTH_FT, auth_transaction, + status, ies, ies_len); + + if (status != WLAN_STATUS_SUCCESS) + return; + + sta = ap_get_sta(hapd, dst); + if (sta == NULL) + return; + + hostapd_logger(hapd, dst, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "authentication OK (FT)"); + sta->flags |= WLAN_STA_AUTH; + mlme_authenticate_indication(hapd, sta); +} +#endif /* CONFIG_IEEE80211R */ + + +static void handle_auth(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len) +{ + u16 auth_alg, auth_transaction, status_code; + u16 resp = WLAN_STATUS_SUCCESS; + struct sta_info *sta = NULL; + int res; + u16 fc; + const u8 *challenge = NULL; + u32 session_timeout, acct_interim_interval; + int vlan_id = 0; + u8 resp_ies[2 + WLAN_AUTH_CHALLENGE_LEN]; + size_t resp_ies_len = 0; + + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { + printf("handle_auth - too short payload (len=%lu)\n", + (unsigned long) len); + return; + } + + auth_alg = le_to_host16(mgmt->u.auth.auth_alg); + auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); + status_code = le_to_host16(mgmt->u.auth.status_code); + fc = le_to_host16(mgmt->frame_control); + + if (len >= IEEE80211_HDRLEN + sizeof(mgmt->u.auth) + + 2 + WLAN_AUTH_CHALLENGE_LEN && + mgmt->u.auth.variable[0] == WLAN_EID_CHALLENGE && + mgmt->u.auth.variable[1] == WLAN_AUTH_CHALLENGE_LEN) + challenge = &mgmt->u.auth.variable[2]; + + wpa_printf(MSG_DEBUG, "authentication: STA=" MACSTR " auth_alg=%d " + "auth_transaction=%d status_code=%d wep=%d%s", + MAC2STR(mgmt->sa), auth_alg, auth_transaction, + status_code, !!(fc & WLAN_FC_ISWEP), + challenge ? " challenge" : ""); + + if (hapd->tkip_countermeasures) { + resp = WLAN_REASON_MICHAEL_MIC_FAILURE; + goto fail; + } + + if (!(((hapd->conf->auth_algs & WPA_AUTH_ALG_OPEN) && + auth_alg == WLAN_AUTH_OPEN) || +#ifdef CONFIG_IEEE80211R + (hapd->conf->wpa && + (hapd->conf->wpa_key_mgmt & + (WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_FT_PSK)) && + auth_alg == WLAN_AUTH_FT) || +#endif /* CONFIG_IEEE80211R */ + ((hapd->conf->auth_algs & WPA_AUTH_ALG_SHARED) && + auth_alg == WLAN_AUTH_SHARED_KEY))) { + printf("Unsupported authentication algorithm (%d)\n", + auth_alg); + resp = WLAN_STATUS_NOT_SUPPORTED_AUTH_ALG; + goto fail; + } + + if (!(auth_transaction == 1 || + (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 3))) { + printf("Unknown authentication transaction number (%d)\n", + auth_transaction); + resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION; + goto fail; + } + + if (os_memcmp(mgmt->sa, hapd->own_addr, ETH_ALEN) == 0) { + printf("Station " MACSTR " not allowed to authenticate.\n", + MAC2STR(mgmt->sa)); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + res = hostapd_allowed_address(hapd, mgmt->sa, (u8 *) mgmt, len, + &session_timeout, + &acct_interim_interval, &vlan_id); + if (res == HOSTAPD_ACL_REJECT) { + printf("Station " MACSTR " not allowed to authenticate.\n", + MAC2STR(mgmt->sa)); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + if (res == HOSTAPD_ACL_PENDING) { + wpa_printf(MSG_DEBUG, "Authentication frame from " MACSTR + " waiting for an external authentication", + MAC2STR(mgmt->sa)); + /* Authentication code will re-send the authentication frame + * after it has received (and cached) information from the + * external source. */ + return; + } + + sta = ap_sta_add(hapd, mgmt->sa); + if (!sta) { + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + + if (vlan_id > 0) { + if (hostapd_get_vlan_id_ifname(hapd->conf->vlan, + vlan_id) == NULL) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, "Invalid VLAN ID " + "%d received from RADIUS server", + vlan_id); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + sta->vlan_id = vlan_id; + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, "VLAN ID %d", sta->vlan_id); + } + + sta->flags &= ~WLAN_STA_PREAUTH; + ieee802_1x_notify_pre_auth(sta->eapol_sm, 0); + + if (hapd->conf->acct_interim_interval == 0 && acct_interim_interval) + sta->acct_interim_interval = acct_interim_interval; + if (res == HOSTAPD_ACL_ACCEPT_TIMEOUT) + ap_sta_session_timeout(hapd, sta, session_timeout); + else + ap_sta_no_session_timeout(hapd, sta); + + switch (auth_alg) { + case WLAN_AUTH_OPEN: + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "authentication OK (open system)"); +#ifdef IEEE80211_REQUIRE_AUTH_ACK + /* Station will be marked authenticated if it ACKs the + * authentication reply. */ +#else + sta->flags |= WLAN_STA_AUTH; + wpa_auth_sm_event(sta->wpa_sm, WPA_AUTH); + sta->auth_alg = WLAN_AUTH_OPEN; + mlme_authenticate_indication(hapd, sta); +#endif + break; + case WLAN_AUTH_SHARED_KEY: + resp = auth_shared_key(hapd, sta, auth_transaction, challenge, + fc & WLAN_FC_ISWEP); + sta->auth_alg = WLAN_AUTH_SHARED_KEY; + mlme_authenticate_indication(hapd, sta); + if (sta->challenge && auth_transaction == 1) { + resp_ies[0] = WLAN_EID_CHALLENGE; + resp_ies[1] = WLAN_AUTH_CHALLENGE_LEN; + os_memcpy(resp_ies + 2, sta->challenge, + WLAN_AUTH_CHALLENGE_LEN); + resp_ies_len = 2 + WLAN_AUTH_CHALLENGE_LEN; + } + break; +#ifdef CONFIG_IEEE80211R + case WLAN_AUTH_FT: + sta->auth_alg = WLAN_AUTH_FT; + if (sta->wpa_sm == NULL) + sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, + sta->addr); + if (sta->wpa_sm == NULL) { + wpa_printf(MSG_DEBUG, "FT: Failed to initialize WPA " + "state machine"); + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; + goto fail; + } + wpa_ft_process_auth(sta->wpa_sm, mgmt->bssid, + auth_transaction, mgmt->u.auth.variable, + len - IEEE80211_HDRLEN - + sizeof(mgmt->u.auth), + handle_auth_ft_finish, hapd); + /* handle_auth_ft_finish() callback will complete auth. */ + return; +#endif /* CONFIG_IEEE80211R */ + } + + fail: + send_auth_reply(hapd, mgmt->sa, mgmt->bssid, auth_alg, + auth_transaction + 1, resp, resp_ies, resp_ies_len); +} + + +static int hostapd_get_aid(struct hostapd_data *hapd, struct sta_info *sta) +{ + int i, j = 32, aid; + + /* get a unique AID */ + if (sta->aid > 0) { + wpa_printf(MSG_DEBUG, " old AID %d", sta->aid); + return 0; + } + + for (i = 0; i < AID_WORDS; i++) { + if (hapd->sta_aid[i] == (u32) -1) + continue; + for (j = 0; j < 32; j++) { + if (!(hapd->sta_aid[i] & BIT(j))) + break; + } + if (j < 32) + break; + } + if (j == 32) + return -1; + aid = i * 32 + j + 1; + if (aid > 2007) + return -1; + + sta->aid = aid; + hapd->sta_aid[i] |= BIT(j); + wpa_printf(MSG_DEBUG, " new AID %d", sta->aid); + return 0; +} + + +static u16 check_ssid(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *ssid_ie, size_t ssid_ie_len) +{ + if (ssid_ie == NULL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + if (ssid_ie_len != hapd->conf->ssid.ssid_len || + os_memcmp(ssid_ie, hapd->conf->ssid.ssid, ssid_ie_len) != 0) { + char ssid_txt[33]; + ieee802_11_print_ssid(ssid_txt, ssid_ie, ssid_ie_len); + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "Station tried to associate with unknown SSID " + "'%s'", ssid_txt); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + return WLAN_STATUS_SUCCESS; +} + + +static u16 check_wmm(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *wmm_ie, size_t wmm_ie_len) +{ + sta->flags &= ~WLAN_STA_WMM; + if (wmm_ie && hapd->conf->wmm_enabled) { + if (hostapd_eid_wmm_valid(hapd, wmm_ie, wmm_ie_len)) + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, + "invalid WMM element in association " + "request"); + else + sta->flags |= WLAN_STA_WMM; + } + return WLAN_STATUS_SUCCESS; +} + + +static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta, + struct ieee802_11_elems *elems) +{ + if (!elems->supp_rates) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "No supported rates element in AssocReq"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (elems->supp_rates_len > sizeof(sta->supported_rates)) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "Invalid supported rates element length %d", + elems->supp_rates_len); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + os_memset(sta->supported_rates, 0, sizeof(sta->supported_rates)); + os_memcpy(sta->supported_rates, elems->supp_rates, + elems->supp_rates_len); + sta->supported_rates_len = elems->supp_rates_len; + + if (elems->ext_supp_rates) { + if (elems->supp_rates_len + elems->ext_supp_rates_len > + sizeof(sta->supported_rates)) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "Invalid supported rates element length" + " %d+%d", elems->supp_rates_len, + elems->ext_supp_rates_len); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + os_memcpy(sta->supported_rates + elems->supp_rates_len, + elems->ext_supp_rates, elems->ext_supp_rates_len); + sta->supported_rates_len += elems->ext_supp_rates_len; + } + + return WLAN_STATUS_SUCCESS; +} + + +static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *ies, size_t ies_len, int reassoc) +{ + struct ieee802_11_elems elems; + u16 resp; + const u8 *wpa_ie; + size_t wpa_ie_len; + + if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) == ParseFailed) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "Station sent an invalid " + "association request"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + resp = check_ssid(hapd, sta, elems.ssid, elems.ssid_len); + if (resp != WLAN_STATUS_SUCCESS) + return resp; + resp = check_wmm(hapd, sta, elems.wmm, elems.wmm_len); + if (resp != WLAN_STATUS_SUCCESS) + return resp; + resp = copy_supp_rates(hapd, sta, &elems); + if (resp != WLAN_STATUS_SUCCESS) + return resp; +#ifdef CONFIG_IEEE80211N + resp = copy_sta_ht_capab(sta, elems.ht_capabilities, + elems.ht_capabilities_len); + if (resp != WLAN_STATUS_SUCCESS) + return resp; +#endif /* CONFIG_IEEE80211N */ + + if ((hapd->conf->wpa & WPA_PROTO_RSN) && elems.rsn_ie) { + wpa_ie = elems.rsn_ie; + wpa_ie_len = elems.rsn_ie_len; + } else if ((hapd->conf->wpa & WPA_PROTO_WPA) && + elems.wpa_ie) { + wpa_ie = elems.wpa_ie; + wpa_ie_len = elems.wpa_ie_len; + } else { + wpa_ie = NULL; + wpa_ie_len = 0; + } + +#ifdef CONFIG_WPS + sta->flags &= ~(WLAN_STA_WPS | WLAN_STA_MAYBE_WPS); + if (hapd->conf->wps_state && elems.wps_ie) { + wpa_printf(MSG_DEBUG, "STA included WPS IE in (Re)Association " + "Request - assume WPS is used"); + sta->flags |= WLAN_STA_WPS; + wpabuf_free(sta->wps_ie); + sta->wps_ie = ieee802_11_vendor_ie_concat(ies, ies_len, + WPS_IE_VENDOR_TYPE); + wpa_ie = NULL; + wpa_ie_len = 0; + } else if (hapd->conf->wps_state && wpa_ie == NULL) { + wpa_printf(MSG_DEBUG, "STA did not include WPA/RSN IE in " + "(Re)Association Request - possible WPS use"); + sta->flags |= WLAN_STA_MAYBE_WPS; + } else +#endif /* CONFIG_WPS */ + if (hapd->conf->wpa && wpa_ie == NULL) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "No WPA/RSN IE in association request"); + return WLAN_STATUS_INVALID_IE; + } + + if (hapd->conf->wpa && wpa_ie) { + int res; + wpa_ie -= 2; + wpa_ie_len += 2; + if (sta->wpa_sm == NULL) + sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, + sta->addr); + if (sta->wpa_sm == NULL) { + wpa_printf(MSG_WARNING, "Failed to initialize WPA " + "state machine"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, + wpa_ie, wpa_ie_len, + elems.mdie, elems.mdie_len); + if (res == WPA_INVALID_GROUP) + resp = WLAN_STATUS_GROUP_CIPHER_NOT_VALID; + else if (res == WPA_INVALID_PAIRWISE) + resp = WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID; + else if (res == WPA_INVALID_AKMP) + resp = WLAN_STATUS_AKMP_NOT_VALID; + else if (res == WPA_ALLOC_FAIL) + resp = WLAN_STATUS_UNSPECIFIED_FAILURE; +#ifdef CONFIG_IEEE80211W + else if (res == WPA_MGMT_FRAME_PROTECTION_VIOLATION) + resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION; + else if (res == WPA_INVALID_MGMT_GROUP_CIPHER) + resp = WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION; +#endif /* CONFIG_IEEE80211W */ + else if (res == WPA_INVALID_MDIE) + resp = WLAN_STATUS_INVALID_MDIE; + else if (res != WPA_IE_OK) + resp = WLAN_STATUS_INVALID_IE; + if (resp != WLAN_STATUS_SUCCESS) + return resp; +#ifdef CONFIG_IEEE80211W + if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out && + sta->sa_query_count > 0) + ap_check_sa_query_timeout(hapd, sta); + if ((sta->flags & WLAN_STA_MFP) && !sta->sa_query_timed_out && + (!reassoc || sta->auth_alg != WLAN_AUTH_FT)) { + /* + * STA has already been associated with MFP and SA + * Query timeout has not been reached. Reject the + * association attempt temporarily and start SA Query, + * if one is not pending. + */ + + if (sta->sa_query_count == 0) + ap_sta_start_sa_query(hapd, sta); + + return WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY; + } + + if (wpa_auth_uses_mfp(sta->wpa_sm)) + sta->flags |= WLAN_STA_MFP; + else + sta->flags &= ~WLAN_STA_MFP; +#endif /* CONFIG_IEEE80211W */ + +#ifdef CONFIG_IEEE80211R + if (sta->auth_alg == WLAN_AUTH_FT) { + if (!reassoc) { + wpa_printf(MSG_DEBUG, "FT: " MACSTR " tried " + "to use association (not " + "re-association) with FT auth_alg", + MAC2STR(sta->addr)); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + resp = wpa_ft_validate_reassoc(sta->wpa_sm, ies, + ies_len); + if (resp != WLAN_STATUS_SUCCESS) + return resp; + } +#endif /* CONFIG_IEEE80211R */ + +#ifdef CONFIG_IEEE80211N + if ((sta->flags & WLAN_STA_HT) && + wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "Station tried to use TKIP with HT " + "association"); + return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY; + } +#endif /* CONFIG_IEEE80211N */ + } else + wpa_auth_sta_no_wpa(sta->wpa_sm); + + return WLAN_STATUS_SUCCESS; +} + + +static void send_deauth(struct hostapd_data *hapd, const u8 *addr, + u16 reason_code) +{ + int send_len; + struct ieee80211_mgmt reply; + + os_memset(&reply, 0, sizeof(reply)); + reply.frame_control = + IEEE80211_FC(WLAN_FC_TYPE_MGMT, WLAN_FC_STYPE_DEAUTH); + os_memcpy(reply.da, addr, ETH_ALEN); + os_memcpy(reply.sa, hapd->own_addr, ETH_ALEN); + os_memcpy(reply.bssid, hapd->own_addr, ETH_ALEN); + + send_len = IEEE80211_HDRLEN + sizeof(reply.u.deauth); + reply.u.deauth.reason_code = host_to_le16(reason_code); + + if (hapd->drv.send_mgmt_frame(hapd, &reply, send_len) < 0) + wpa_printf(MSG_INFO, "Failed to send deauth: %s", + strerror(errno)); +} + + +static void send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta, + u16 status_code, int reassoc, const u8 *ies, + size_t ies_len) +{ + int send_len; + u8 buf[sizeof(struct ieee80211_mgmt) + 1024]; + struct ieee80211_mgmt *reply; + u8 *p; + + os_memset(buf, 0, sizeof(buf)); + reply = (struct ieee80211_mgmt *) buf; + reply->frame_control = + IEEE80211_FC(WLAN_FC_TYPE_MGMT, + (reassoc ? WLAN_FC_STYPE_REASSOC_RESP : + WLAN_FC_STYPE_ASSOC_RESP)); + os_memcpy(reply->da, sta->addr, ETH_ALEN); + os_memcpy(reply->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(reply->bssid, hapd->own_addr, ETH_ALEN); + + send_len = IEEE80211_HDRLEN; + send_len += sizeof(reply->u.assoc_resp); + reply->u.assoc_resp.capab_info = + host_to_le16(hostapd_own_capab_info(hapd, sta, 0)); + reply->u.assoc_resp.status_code = host_to_le16(status_code); + reply->u.assoc_resp.aid = host_to_le16((sta ? sta->aid : 0) + | BIT(14) | BIT(15)); + /* Supported rates */ + p = hostapd_eid_supp_rates(hapd, reply->u.assoc_resp.variable); + /* Extended supported rates */ + p = hostapd_eid_ext_supp_rates(hapd, p); + +#ifdef CONFIG_IEEE80211R + if (status_code == WLAN_STATUS_SUCCESS) { + /* IEEE 802.11r: Mobility Domain Information, Fast BSS + * Transition Information, RSN, [RIC Response] */ + p = wpa_sm_write_assoc_resp_ies(sta->wpa_sm, p, + buf + sizeof(buf) - p, + sta->auth_alg, ies, ies_len); + } +#endif /* CONFIG_IEEE80211R */ + +#ifdef CONFIG_IEEE80211W + if (status_code == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY) + p = hostapd_eid_assoc_comeback_time(hapd, sta, p); +#endif /* CONFIG_IEEE80211W */ + +#ifdef CONFIG_IEEE80211N + p = hostapd_eid_ht_capabilities(hapd, p); + p = hostapd_eid_ht_operation(hapd, p); +#endif /* CONFIG_IEEE80211N */ + + if (sta->flags & WLAN_STA_WMM) + p = hostapd_eid_wmm(hapd, p); + +#ifdef CONFIG_WPS + if (sta->flags & WLAN_STA_WPS) { + struct wpabuf *wps = wps_build_assoc_resp_ie(); + if (wps) { + os_memcpy(p, wpabuf_head(wps), wpabuf_len(wps)); + p += wpabuf_len(wps); + wpabuf_free(wps); + } + } +#endif /* CONFIG_WPS */ + + send_len += p - reply->u.assoc_resp.variable; + + if (hapd->drv.send_mgmt_frame(hapd, reply, send_len) < 0) + wpa_printf(MSG_INFO, "Failed to send assoc resp: %s", + strerror(errno)); +} + + +static void handle_assoc(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len, + int reassoc) +{ + u16 capab_info, listen_interval; + u16 resp = WLAN_STATUS_SUCCESS; + const u8 *pos; + int left, i; + struct sta_info *sta; + + if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_req) : + sizeof(mgmt->u.assoc_req))) { + printf("handle_assoc(reassoc=%d) - too short payload (len=%lu)" + "\n", reassoc, (unsigned long) len); + return; + } + + if (reassoc) { + capab_info = le_to_host16(mgmt->u.reassoc_req.capab_info); + listen_interval = le_to_host16( + mgmt->u.reassoc_req.listen_interval); + wpa_printf(MSG_DEBUG, "reassociation request: STA=" MACSTR + " capab_info=0x%02x listen_interval=%d current_ap=" + MACSTR, + MAC2STR(mgmt->sa), capab_info, listen_interval, + MAC2STR(mgmt->u.reassoc_req.current_ap)); + left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.reassoc_req)); + pos = mgmt->u.reassoc_req.variable; + } else { + capab_info = le_to_host16(mgmt->u.assoc_req.capab_info); + listen_interval = le_to_host16( + mgmt->u.assoc_req.listen_interval); + wpa_printf(MSG_DEBUG, "association request: STA=" MACSTR + " capab_info=0x%02x listen_interval=%d", + MAC2STR(mgmt->sa), capab_info, listen_interval); + left = len - (IEEE80211_HDRLEN + sizeof(mgmt->u.assoc_req)); + pos = mgmt->u.assoc_req.variable; + } + + sta = ap_get_sta(hapd, mgmt->sa); +#ifdef CONFIG_IEEE80211R + if (sta && sta->auth_alg == WLAN_AUTH_FT && + (sta->flags & WLAN_STA_AUTH) == 0) { + wpa_printf(MSG_DEBUG, "FT: Allow STA " MACSTR " to associate " + "prior to authentication since it is using " + "over-the-DS FT", MAC2STR(mgmt->sa)); + } else +#endif /* CONFIG_IEEE80211R */ + if (sta == NULL || (sta->flags & WLAN_STA_AUTH) == 0) { + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "Station tried to " + "associate before authentication " + "(aid=%d flags=0x%x)", + sta ? sta->aid : -1, + sta ? sta->flags : 0); + send_deauth(hapd, mgmt->sa, + WLAN_REASON_CLASS2_FRAME_FROM_NONAUTH_STA); + return; + } + + if (hapd->tkip_countermeasures) { + resp = WLAN_REASON_MICHAEL_MIC_FAILURE; + goto fail; + } + + if (listen_interval > hapd->conf->max_listen_interval) { + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "Too large Listen Interval (%d)", + listen_interval); + resp = WLAN_STATUS_ASSOC_DENIED_LISTEN_INT_TOO_LARGE; + goto fail; + } + + /* followed by SSID and Supported rates; and HT capabilities if 802.11n + * is used */ + resp = check_assoc_ies(hapd, sta, pos, left, reassoc); + if (resp != WLAN_STATUS_SUCCESS) + goto fail; + + if (hostapd_get_aid(hapd, sta) < 0) { + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "No room for more AIDs"); + resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA; + goto fail; + } + + sta->capability = capab_info; + sta->listen_interval = listen_interval; + + if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G) + sta->flags |= WLAN_STA_NONERP; + for (i = 0; i < sta->supported_rates_len; i++) { + if ((sta->supported_rates[i] & 0x7f) > 22) { + sta->flags &= ~WLAN_STA_NONERP; + break; + } + } + if (sta->flags & WLAN_STA_NONERP && !sta->nonerp_set) { + sta->nonerp_set = 1; + hapd->iface->num_sta_non_erp++; + if (hapd->iface->num_sta_non_erp == 1) + ieee802_11_set_beacons(hapd->iface); + } + + if (!(sta->capability & WLAN_CAPABILITY_SHORT_SLOT_TIME) && + !sta->no_short_slot_time_set) { + sta->no_short_slot_time_set = 1; + hapd->iface->num_sta_no_short_slot_time++; + if (hapd->iface->current_mode->mode == + HOSTAPD_MODE_IEEE80211G && + hapd->iface->num_sta_no_short_slot_time == 1) + ieee802_11_set_beacons(hapd->iface); + } + + if (sta->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) + sta->flags |= WLAN_STA_SHORT_PREAMBLE; + else + sta->flags &= ~WLAN_STA_SHORT_PREAMBLE; + + if (!(sta->capability & WLAN_CAPABILITY_SHORT_PREAMBLE) && + !sta->no_short_preamble_set) { + sta->no_short_preamble_set = 1; + hapd->iface->num_sta_no_short_preamble++; + if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G + && hapd->iface->num_sta_no_short_preamble == 1) + ieee802_11_set_beacons(hapd->iface); + } + +#ifdef CONFIG_IEEE80211N + update_ht_state(hapd, sta); +#endif /* CONFIG_IEEE80211N */ + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "association OK (aid %d)", sta->aid); + /* Station will be marked associated, after it acknowledges AssocResp + */ + +#ifdef CONFIG_IEEE80211W + if ((sta->flags & WLAN_STA_MFP) && sta->sa_query_timed_out) { + wpa_printf(MSG_DEBUG, "Allowing %sassociation after timed out " + "SA Query procedure", reassoc ? "re" : ""); + /* TODO: Send a protected Disassociate frame to the STA using + * the old key and Reason Code "Previous Authentication no + * longer valid". Make sure this is only sent protected since + * unprotected frame would be received by the STA that is now + * trying to associate. + */ + } +#endif /* CONFIG_IEEE80211W */ + + if (reassoc) { + os_memcpy(sta->previous_ap, mgmt->u.reassoc_req.current_ap, + ETH_ALEN); + } + + if (sta->last_assoc_req) + os_free(sta->last_assoc_req); + sta->last_assoc_req = os_malloc(len); + if (sta->last_assoc_req) + os_memcpy(sta->last_assoc_req, mgmt, len); + + /* Make sure that the previously registered inactivity timer will not + * remove the STA immediately. */ + sta->timeout_next = STA_NULLFUNC; + + fail: + send_assoc_resp(hapd, sta, resp, reassoc, pos, left); +} + + +static void handle_disassoc(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len) +{ + struct sta_info *sta; + + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.disassoc)) { + printf("handle_disassoc - too short payload (len=%lu)\n", + (unsigned long) len); + return; + } + + wpa_printf(MSG_DEBUG, "disassocation: STA=" MACSTR " reason_code=%d", + MAC2STR(mgmt->sa), + le_to_host16(mgmt->u.disassoc.reason_code)); + + sta = ap_get_sta(hapd, mgmt->sa); + if (sta == NULL) { + printf("Station " MACSTR " trying to disassociate, but it " + "is not associated.\n", MAC2STR(mgmt->sa)); + return; + } + + sta->flags &= ~WLAN_STA_ASSOC; + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED MACSTR, + MAC2STR(sta->addr)); + wpa_auth_sm_event(sta->wpa_sm, WPA_DISASSOC); + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "disassociated"); + sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; + ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); + /* Stop Accounting and IEEE 802.1X sessions, but leave the STA + * authenticated. */ + accounting_sta_stop(hapd, sta); + ieee802_1x_free_station(sta); + hapd->drv.sta_remove(hapd, sta->addr); + + if (sta->timeout_next == STA_NULLFUNC || + sta->timeout_next == STA_DISASSOC) { + sta->timeout_next = STA_DEAUTH; + eloop_cancel_timeout(ap_handle_timer, hapd, sta); + eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer, + hapd, sta); + } + + mlme_disassociate_indication( + hapd, sta, le_to_host16(mgmt->u.disassoc.reason_code)); +} + + +static void handle_deauth(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len) +{ + struct sta_info *sta; + + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.deauth)) { + printf("handle_deauth - too short payload (len=%lu)\n", + (unsigned long) len); + return; + } + + wpa_printf(MSG_DEBUG, "deauthentication: STA=" MACSTR + " reason_code=%d", + MAC2STR(mgmt->sa), + le_to_host16(mgmt->u.deauth.reason_code)); + + sta = ap_get_sta(hapd, mgmt->sa); + if (sta == NULL) { + printf("Station " MACSTR " trying to deauthenticate, but it " + "is not authenticated.\n", MAC2STR(mgmt->sa)); + return; + } + + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); + wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED MACSTR, + MAC2STR(sta->addr)); + wpa_auth_sm_event(sta->wpa_sm, WPA_DEAUTH); + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "deauthenticated"); + mlme_deauthenticate_indication( + hapd, sta, le_to_host16(mgmt->u.deauth.reason_code)); + sta->acct_terminate_cause = RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; + ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); + ap_free_sta(hapd, sta); +} + + +static void handle_beacon(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len, + struct hostapd_frame_info *fi) +{ + struct ieee802_11_elems elems; + + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.beacon)) { + printf("handle_beacon - too short payload (len=%lu)\n", + (unsigned long) len); + return; + } + + (void) ieee802_11_parse_elems(mgmt->u.beacon.variable, + len - (IEEE80211_HDRLEN + + sizeof(mgmt->u.beacon)), &elems, + 0); + + ap_list_process_beacon(hapd->iface, mgmt, &elems, fi); +} + + +#ifdef CONFIG_IEEE80211W + +/* MLME-SAQuery.request */ +void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, + const u8 *addr, const u8 *trans_id) +{ + struct ieee80211_mgmt mgmt; + u8 *end; + + wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to " + MACSTR, MAC2STR(addr)); + wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", + trans_id, WLAN_SA_QUERY_TR_ID_LEN); + + os_memset(&mgmt, 0, sizeof(mgmt)); + mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memcpy(mgmt.da, addr, ETH_ALEN); + os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN); + os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN); + mgmt.u.action.category = WLAN_ACTION_SA_QUERY; + mgmt.u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST; + os_memcpy(mgmt.u.action.u.sa_query_req.trans_id, trans_id, + WLAN_SA_QUERY_TR_ID_LEN); + end = mgmt.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN; + if (hapd->drv.send_mgmt_frame(hapd, &mgmt, end - (u8 *) &mgmt) < 0) + perror("ieee802_11_send_sa_query_req: send"); +} + + +static void hostapd_sa_query_request(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt) +{ + struct sta_info *sta; + struct ieee80211_mgmt resp; + u8 *end; + + wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from " + MACSTR, MAC2STR(mgmt->sa)); + wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", + mgmt->u.action.u.sa_query_resp.trans_id, + WLAN_SA_QUERY_TR_ID_LEN); + + sta = ap_get_sta(hapd, mgmt->sa); + if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) { + wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignore SA Query Request " + "from unassociated STA " MACSTR, MAC2STR(mgmt->sa)); + return; + } + + wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to " + MACSTR, MAC2STR(mgmt->sa)); + + os_memset(&resp, 0, sizeof(resp)); + resp.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memcpy(resp.da, mgmt->sa, ETH_ALEN); + os_memcpy(resp.sa, hapd->own_addr, ETH_ALEN); + os_memcpy(resp.bssid, hapd->own_addr, ETH_ALEN); + resp.u.action.category = WLAN_ACTION_SA_QUERY; + resp.u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE; + os_memcpy(resp.u.action.u.sa_query_req.trans_id, + mgmt->u.action.u.sa_query_req.trans_id, + WLAN_SA_QUERY_TR_ID_LEN); + end = resp.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN; + if (hapd->drv.send_mgmt_frame(hapd, &resp, end - (u8 *) &resp) < 0) + perror("hostapd_sa_query_request: send"); +} + + +static void hostapd_sa_query_action(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, + size_t len) +{ + struct sta_info *sta; + const u8 *end; + int i; + + end = mgmt->u.action.u.sa_query_resp.trans_id + + WLAN_SA_QUERY_TR_ID_LEN; + if (((u8 *) mgmt) + len < end) { + wpa_printf(MSG_DEBUG, "IEEE 802.11: Too short SA Query Action " + "frame (len=%lu)", (unsigned long) len); + return; + } + + if (mgmt->u.action.u.sa_query_resp.action == WLAN_SA_QUERY_REQUEST) { + hostapd_sa_query_request(hapd, mgmt); + return; + } + + if (mgmt->u.action.u.sa_query_resp.action != WLAN_SA_QUERY_RESPONSE) { + wpa_printf(MSG_DEBUG, "IEEE 802.11: Unexpected SA Query " + "Action %d", mgmt->u.action.u.sa_query_resp.action); + return; + } + + wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Response from " + MACSTR, MAC2STR(mgmt->sa)); + wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID", + mgmt->u.action.u.sa_query_resp.trans_id, + WLAN_SA_QUERY_TR_ID_LEN); + + /* MLME-SAQuery.confirm */ + + sta = ap_get_sta(hapd, mgmt->sa); + if (sta == NULL || sta->sa_query_trans_id == NULL) { + wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with " + "pending SA Query request found"); + return; + } + + for (i = 0; i < sta->sa_query_count; i++) { + if (os_memcmp(sta->sa_query_trans_id + + i * WLAN_SA_QUERY_TR_ID_LEN, + mgmt->u.action.u.sa_query_resp.trans_id, + WLAN_SA_QUERY_TR_ID_LEN) == 0) + break; + } + + if (i >= sta->sa_query_count) { + wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching SA Query " + "transaction identifier found"); + return; + } + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "Reply to pending SA Query received"); + ap_sta_stop_sa_query(hapd, sta); +} + + +static int robust_action_frame(u8 category) +{ + return category != WLAN_ACTION_PUBLIC && + category != WLAN_ACTION_HT; +} +#endif /* CONFIG_IEEE80211W */ + + +static void handle_action(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len) +{ + struct sta_info *sta; + + if (len < IEEE80211_HDRLEN + 1) { + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "handle_action - too short payload (len=%lu)", + (unsigned long) len); + return; + } + + sta = ap_get_sta(hapd, mgmt->sa); +#ifdef CONFIG_IEEE80211W + if (sta && (sta->flags & WLAN_STA_MFP) && + !(mgmt->frame_control & host_to_le16(WLAN_FC_ISWEP) && + robust_action_frame(mgmt->u.action.category))) { + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "Dropped unprotected Robust Action frame from " + "an MFP STA"); + return; + } +#endif /* CONFIG_IEEE80211W */ + + switch (mgmt->u.action.category) { +#ifdef CONFIG_IEEE80211R + case WLAN_ACTION_FT: + { + if (sta == NULL || !(sta->flags & WLAN_STA_ASSOC)) { + wpa_printf(MSG_DEBUG, "IEEE 802.11: Ignored FT Action " + "frame from unassociated STA " MACSTR, + MAC2STR(mgmt->sa)); + return; + } + + if (wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action, + len - IEEE80211_HDRLEN)) + break; + + return; + } +#endif /* CONFIG_IEEE80211R */ + case WLAN_ACTION_WMM: + hostapd_wmm_action(hapd, mgmt, len); + return; +#ifdef CONFIG_IEEE80211W + case WLAN_ACTION_SA_QUERY: + hostapd_sa_query_action(hapd, mgmt, len); + return; +#endif /* CONFIG_IEEE80211W */ + case WLAN_ACTION_PUBLIC: + if (hapd->public_action_cb) { + hapd->public_action_cb(hapd->public_action_cb_ctx, + (u8 *) mgmt, len, + hapd->iface->freq); + return; + } + break; + } + + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "handle_action - unknown action category %d or invalid " + "frame", + mgmt->u.action.category); + if (!(mgmt->da[0] & 0x01) && !(mgmt->u.action.category & 0x80) && + !(mgmt->sa[0] & 0x01)) { + struct ieee80211_mgmt *resp; + + /* + * IEEE 802.11-REVma/D9.0 - 7.3.1.11 + * Return the Action frame to the source without change + * except that MSB of the Category set to 1. + */ + wpa_printf(MSG_DEBUG, "IEEE 802.11: Return unknown Action " + "frame back to sender"); + resp = os_malloc(len); + if (resp == NULL) + return; + os_memcpy(resp, mgmt, len); + os_memcpy(resp->da, resp->sa, ETH_ALEN); + os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN); + resp->u.action.category |= 0x80; + + hapd->drv.send_mgmt_frame(hapd, resp, len); + os_free(resp); + } +} + + +/** + * ieee802_11_mgmt - process incoming IEEE 802.11 management frames + * @hapd: hostapd BSS data structure (the BSS to which the management frame was + * sent to) + * @buf: management frame data (starting from IEEE 802.11 header) + * @len: length of frame data in octets + * @fi: meta data about received frame (signal level, etc.) + * + * Process all incoming IEEE 802.11 management frames. This will be called for + * each frame received from the kernel driver through wlan#ap interface. In + * addition, it can be called to re-inserted pending frames (e.g., when using + * external RADIUS server as an MAC ACL). + */ +void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, + struct hostapd_frame_info *fi) +{ + struct ieee80211_mgmt *mgmt; + int broadcast; + u16 fc, stype; + + mgmt = (struct ieee80211_mgmt *) buf; + fc = le_to_host16(mgmt->frame_control); + stype = WLAN_FC_GET_STYPE(fc); + + if (stype == WLAN_FC_STYPE_BEACON) { + handle_beacon(hapd, mgmt, len, fi); + return; + } + + broadcast = mgmt->bssid[0] == 0xff && mgmt->bssid[1] == 0xff && + mgmt->bssid[2] == 0xff && mgmt->bssid[3] == 0xff && + mgmt->bssid[4] == 0xff && mgmt->bssid[5] == 0xff; + + if (!broadcast && + os_memcmp(mgmt->bssid, hapd->own_addr, ETH_ALEN) != 0) { + printf("MGMT: BSSID=" MACSTR " not our address\n", + MAC2STR(mgmt->bssid)); + return; + } + + + if (stype == WLAN_FC_STYPE_PROBE_REQ) { + handle_probe_req(hapd, mgmt, len); + return; + } + + if (os_memcmp(mgmt->da, hapd->own_addr, ETH_ALEN) != 0) { + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "MGMT: DA=" MACSTR " not our address", + MAC2STR(mgmt->da)); + return; + } + + switch (stype) { + case WLAN_FC_STYPE_AUTH: + wpa_printf(MSG_DEBUG, "mgmt::auth"); + handle_auth(hapd, mgmt, len); + break; + case WLAN_FC_STYPE_ASSOC_REQ: + wpa_printf(MSG_DEBUG, "mgmt::assoc_req"); + handle_assoc(hapd, mgmt, len, 0); + break; + case WLAN_FC_STYPE_REASSOC_REQ: + wpa_printf(MSG_DEBUG, "mgmt::reassoc_req"); + handle_assoc(hapd, mgmt, len, 1); + break; + case WLAN_FC_STYPE_DISASSOC: + wpa_printf(MSG_DEBUG, "mgmt::disassoc"); + handle_disassoc(hapd, mgmt, len); + break; + case WLAN_FC_STYPE_DEAUTH: + wpa_printf(MSG_DEBUG, "mgmt::deauth"); + handle_deauth(hapd, mgmt, len); + break; + case WLAN_FC_STYPE_ACTION: + wpa_printf(MSG_DEBUG, "mgmt::action"); + handle_action(hapd, mgmt, len); + break; + default: + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "unknown mgmt frame subtype %d", stype); + break; + } +} + + +static void handle_auth_cb(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, + size_t len, int ok) +{ + u16 auth_alg, auth_transaction, status_code; + struct sta_info *sta; + + if (!ok) { + hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_NOTICE, + "did not acknowledge authentication response"); + return; + } + + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.auth)) { + printf("handle_auth_cb - too short payload (len=%lu)\n", + (unsigned long) len); + return; + } + + auth_alg = le_to_host16(mgmt->u.auth.auth_alg); + auth_transaction = le_to_host16(mgmt->u.auth.auth_transaction); + status_code = le_to_host16(mgmt->u.auth.status_code); + + sta = ap_get_sta(hapd, mgmt->da); + if (!sta) { + printf("handle_auth_cb: STA " MACSTR " not found\n", + MAC2STR(mgmt->da)); + return; + } + + if (status_code == WLAN_STATUS_SUCCESS && + ((auth_alg == WLAN_AUTH_OPEN && auth_transaction == 2) || + (auth_alg == WLAN_AUTH_SHARED_KEY && auth_transaction == 4))) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "authenticated"); + sta->flags |= WLAN_STA_AUTH; + } +} + + +static void handle_assoc_cb(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, + size_t len, int reassoc, int ok) +{ + u16 status; + struct sta_info *sta; + int new_assoc = 1; + struct ieee80211_ht_capabilities ht_cap; + + if (!ok) { + hostapd_logger(hapd, mgmt->da, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "did not acknowledge association response"); + return; + } + + if (len < IEEE80211_HDRLEN + (reassoc ? sizeof(mgmt->u.reassoc_resp) : + sizeof(mgmt->u.assoc_resp))) { + printf("handle_assoc_cb(reassoc=%d) - too short payload " + "(len=%lu)\n", reassoc, (unsigned long) len); + return; + } + + if (reassoc) + status = le_to_host16(mgmt->u.reassoc_resp.status_code); + else + status = le_to_host16(mgmt->u.assoc_resp.status_code); + + sta = ap_get_sta(hapd, mgmt->da); + if (!sta) { + printf("handle_assoc_cb: STA " MACSTR " not found\n", + MAC2STR(mgmt->da)); + return; + } + + if (status != WLAN_STATUS_SUCCESS) + goto fail; + + /* Stop previous accounting session, if one is started, and allocate + * new session id for the new session. */ + accounting_sta_stop(hapd, sta); + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "associated (aid %d)", + sta->aid); + + if (sta->flags & WLAN_STA_ASSOC) + new_assoc = 0; + sta->flags |= WLAN_STA_ASSOC; + if ((!hapd->conf->ieee802_1x && !hapd->conf->wpa) || + sta->auth_alg == WLAN_AUTH_FT) { + /* + * Open, static WEP, or FT protocol; no separate authorization + * step. + */ + sta->flags |= WLAN_STA_AUTHORIZED; + wpa_msg(hapd->msg_ctx, MSG_INFO, + AP_STA_CONNECTED MACSTR, MAC2STR(sta->addr)); + } + + if (reassoc) + mlme_reassociate_indication(hapd, sta); + else + mlme_associate_indication(hapd, sta); + +#ifdef CONFIG_IEEE80211W + sta->sa_query_timed_out = 0; +#endif /* CONFIG_IEEE80211W */ + + /* + * Remove the STA entry in order to make sure the STA PS state gets + * cleared and configuration gets updated in case of reassociation back + * to the same AP. + */ + hapd->drv.sta_remove(hapd, sta->addr); + +#ifdef CONFIG_IEEE80211N + if (sta->flags & WLAN_STA_HT) + hostapd_get_ht_capab(hapd, sta->ht_capabilities, &ht_cap); +#endif /* CONFIG_IEEE80211N */ + + if (hapd->drv.sta_add(hapd, sta->addr, sta->aid, sta->capability, + sta->supported_rates, sta->supported_rates_len, + sta->listen_interval, + sta->flags & WLAN_STA_HT ? &ht_cap : NULL)) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_NOTICE, + "Could not add STA to kernel driver"); + } + + if (sta->eapol_sm == NULL) { + /* + * This STA does not use RADIUS server for EAP authentication, + * so bind it to the selected VLAN interface now, since the + * interface selection is not going to change anymore. + */ + if (ap_sta_bind_vlan(hapd, sta, 0) < 0) + goto fail; + } else if (sta->vlan_id) { + /* VLAN ID already set (e.g., by PMKSA caching), so bind STA */ + if (ap_sta_bind_vlan(hapd, sta, 0) < 0) + goto fail; + } + + hapd->drv.set_sta_flags(hapd, sta); + + if (sta->auth_alg == WLAN_AUTH_FT) + wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC_FT); + else + wpa_auth_sm_event(sta->wpa_sm, WPA_ASSOC); + hapd->new_assoc_sta_cb(hapd, sta, !new_assoc); + + ieee802_1x_notify_port_enabled(sta->eapol_sm, 1); + + fail: + /* Copy of the association request is not needed anymore */ + if (sta->last_assoc_req) { + os_free(sta->last_assoc_req); + sta->last_assoc_req = NULL; + } +} + + +/** + * ieee802_11_mgmt_cb - Process management frame TX status callback + * @hapd: hostapd BSS data structure (the BSS from which the management frame + * was sent from) + * @buf: management frame data (starting from IEEE 802.11 header) + * @len: length of frame data in octets + * @stype: management frame subtype from frame control field + * @ok: Whether the frame was ACK'ed + */ +void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len, + u16 stype, int ok) +{ + const struct ieee80211_mgmt *mgmt; + mgmt = (const struct ieee80211_mgmt *) buf; + + switch (stype) { + case WLAN_FC_STYPE_AUTH: + wpa_printf(MSG_DEBUG, "mgmt::auth cb"); + handle_auth_cb(hapd, mgmt, len, ok); + break; + case WLAN_FC_STYPE_ASSOC_RESP: + wpa_printf(MSG_DEBUG, "mgmt::assoc_resp cb"); + handle_assoc_cb(hapd, mgmt, len, 0, ok); + break; + case WLAN_FC_STYPE_REASSOC_RESP: + wpa_printf(MSG_DEBUG, "mgmt::reassoc_resp cb"); + handle_assoc_cb(hapd, mgmt, len, 1, ok); + break; + case WLAN_FC_STYPE_PROBE_RESP: + wpa_printf(MSG_DEBUG, "mgmt::proberesp cb"); + break; + case WLAN_FC_STYPE_DEAUTH: + /* ignore */ + break; + case WLAN_FC_STYPE_ACTION: + wpa_printf(MSG_DEBUG, "mgmt::action cb"); + break; + default: + printf("unknown mgmt cb frame subtype %d\n", stype); + break; + } +} + + +int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen) +{ + /* TODO */ + return 0; +} + + +int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, + char *buf, size_t buflen) +{ + /* TODO */ + return 0; +} + + +void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr, + const u8 *buf, size_t len, int ack) +{ + struct sta_info *sta; + struct hostapd_iface *iface = hapd->iface; + + sta = ap_get_sta(hapd, addr); + if (sta == NULL && iface->num_bss > 1) { + size_t j; + for (j = 0; j < iface->num_bss; j++) { + hapd = iface->bss[j]; + sta = ap_get_sta(hapd, addr); + if (sta) + break; + } + } + if (sta == NULL) + return; + if (sta->flags & WLAN_STA_PENDING_POLL) { + wpa_printf(MSG_DEBUG, "STA " MACSTR " %s pending " + "activity poll", MAC2STR(sta->addr), + ack ? "ACKed" : "did not ACK"); + if (ack) + sta->flags &= ~WLAN_STA_PENDING_POLL; + } + + ieee802_1x_tx_status(hapd, sta, buf, len, ack); +} + + +void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src, + int wds) +{ + struct sta_info *sta; + + sta = ap_get_sta(hapd, src); + if (sta && (sta->flags & WLAN_STA_ASSOC)) { + if (wds && !(sta->flags & WLAN_STA_WDS)) { + wpa_printf(MSG_DEBUG, "Enable 4-address WDS mode for " + "STA " MACSTR " (aid %u)", + MAC2STR(sta->addr), sta->aid); + sta->flags |= WLAN_STA_WDS; + hapd->drv.set_wds_sta(hapd, sta->addr, sta->aid, 1); + } + return; + } + + wpa_printf(MSG_DEBUG, "Data/PS-poll frame from not associated STA " + MACSTR, MAC2STR(src)); + if (sta && (sta->flags & WLAN_STA_AUTH)) + hapd->drv.sta_disassoc( + hapd, src, + WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); + else + hapd->drv.sta_deauth( + hapd, src, + WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA); +} + + +#endif /* CONFIG_NATIVE_WINDOWS */ diff --git a/contrib/wpa/src/ap/ieee802_11.h b/contrib/wpa/src/ap/ieee802_11.h new file mode 100644 index 0000000..cfc069c --- /dev/null +++ b/contrib/wpa/src/ap/ieee802_11.h @@ -0,0 +1,67 @@ +/* + * hostapd / IEEE 802.11 Management + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef IEEE802_11_H +#define IEEE802_11_H + +struct hostapd_iface; +struct hostapd_data; +struct sta_info; +struct hostapd_frame_info; +struct ieee80211_ht_capabilities; + +void ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len, + struct hostapd_frame_info *fi); +void ieee802_11_mgmt_cb(struct hostapd_data *hapd, const u8 *buf, size_t len, + u16 stype, int ok); +void ieee802_11_print_ssid(char *buf, const u8 *ssid, u8 len); +#ifdef NEED_AP_MLME +int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen); +int ieee802_11_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, + char *buf, size_t buflen); +#else /* NEED_AP_MLME */ +static inline int ieee802_11_get_mib(struct hostapd_data *hapd, char *buf, + size_t buflen) +{ + return 0; +} + +static inline int ieee802_11_get_mib_sta(struct hostapd_data *hapd, + struct sta_info *sta, + char *buf, size_t buflen) +{ + return 0; +} +#endif /* NEED_AP_MLME */ +u16 hostapd_own_capab_info(struct hostapd_data *hapd, struct sta_info *sta, + int probe); +u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_ext_supp_rates(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid); +u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid); +int hostapd_ht_operation_update(struct hostapd_iface *iface); +void ieee802_11_send_sa_query_req(struct hostapd_data *hapd, + const u8 *addr, const u8 *trans_id); +void hostapd_get_ht_capab(struct hostapd_data *hapd, + struct ieee80211_ht_capabilities *ht_cap, + struct ieee80211_ht_capabilities *neg_ht_cap); +u16 copy_sta_ht_capab(struct sta_info *sta, const u8 *ht_capab, + size_t ht_capab_len); +void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta); +void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr, + const u8 *buf, size_t len, int ack); +void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src, + int wds); + +#endif /* IEEE802_11_H */ diff --git a/contrib/wpa/src/ap/ieee802_11_auth.c b/contrib/wpa/src/ap/ieee802_11_auth.c new file mode 100644 index 0000000..dec56d1 --- /dev/null +++ b/contrib/wpa/src/ap/ieee802_11_auth.c @@ -0,0 +1,524 @@ +/* + * hostapd / IEEE 802.11 authentication (ACL) + * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * Access control list for IEEE 802.11 authentication can uses statically + * configured ACL from configuration files or an external RADIUS server. + * Results from external RADIUS queries are cached to allow faster + * authentication frame processing. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "radius/radius.h" +#include "radius/radius_client.h" +#include "hostapd.h" +#include "ap_config.h" +#include "ieee802_11.h" +#include "ieee802_11_auth.h" + +#define RADIUS_ACL_TIMEOUT 30 + + +struct hostapd_cached_radius_acl { + time_t timestamp; + macaddr addr; + int accepted; /* HOSTAPD_ACL_* */ + struct hostapd_cached_radius_acl *next; + u32 session_timeout; + u32 acct_interim_interval; + int vlan_id; +}; + + +struct hostapd_acl_query_data { + time_t timestamp; + u8 radius_id; + macaddr addr; + u8 *auth_msg; /* IEEE 802.11 authentication frame from station */ + size_t auth_msg_len; + struct hostapd_acl_query_data *next; +}; + + +#ifndef CONFIG_NO_RADIUS +static void hostapd_acl_cache_free(struct hostapd_cached_radius_acl *acl_cache) +{ + struct hostapd_cached_radius_acl *prev; + + while (acl_cache) { + prev = acl_cache; + acl_cache = acl_cache->next; + os_free(prev); + } +} + + +static int hostapd_acl_cache_get(struct hostapd_data *hapd, const u8 *addr, + u32 *session_timeout, + u32 *acct_interim_interval, int *vlan_id) +{ + struct hostapd_cached_radius_acl *entry; + time_t now; + + time(&now); + entry = hapd->acl_cache; + + while (entry) { + if (os_memcmp(entry->addr, addr, ETH_ALEN) == 0) { + if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) + return -1; /* entry has expired */ + if (entry->accepted == HOSTAPD_ACL_ACCEPT_TIMEOUT) + if (session_timeout) + *session_timeout = + entry->session_timeout; + if (acct_interim_interval) + *acct_interim_interval = + entry->acct_interim_interval; + if (vlan_id) + *vlan_id = entry->vlan_id; + return entry->accepted; + } + + entry = entry->next; + } + + return -1; +} +#endif /* CONFIG_NO_RADIUS */ + + +static void hostapd_acl_query_free(struct hostapd_acl_query_data *query) +{ + if (query == NULL) + return; + os_free(query->auth_msg); + os_free(query); +} + + +#ifndef CONFIG_NO_RADIUS +static int hostapd_radius_acl_query(struct hostapd_data *hapd, const u8 *addr, + struct hostapd_acl_query_data *query) +{ + struct radius_msg *msg; + char buf[128]; + + query->radius_id = radius_client_get_id(hapd->radius); + msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, query->radius_id); + if (msg == NULL) + return -1; + + radius_msg_make_authenticator(msg, addr, ETH_ALEN); + + os_snprintf(buf, sizeof(buf), RADIUS_ADDR_FORMAT, MAC2STR(addr)); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, (u8 *) buf, + os_strlen(buf))) { + wpa_printf(MSG_DEBUG, "Could not add User-Name"); + goto fail; + } + + if (!radius_msg_add_attr_user_password( + msg, (u8 *) buf, os_strlen(buf), + hapd->conf->radius->auth_server->shared_secret, + hapd->conf->radius->auth_server->shared_secret_len)) { + wpa_printf(MSG_DEBUG, "Could not add User-Password"); + goto fail; + } + + if (hapd->conf->own_ip_addr.af == AF_INET && + !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, + (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) { + wpa_printf(MSG_DEBUG, "Could not add NAS-IP-Address"); + 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)) { + wpa_printf(MSG_DEBUG, "Could not add NAS-IPv6-Address"); + goto fail; + } +#endif /* CONFIG_IPV6 */ + + if (hapd->conf->nas_identifier && + !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IDENTIFIER, + (u8 *) hapd->conf->nas_identifier, + os_strlen(hapd->conf->nas_identifier))) { + wpa_printf(MSG_DEBUG, "Could not add NAS-Identifier"); + goto fail; + } + + os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", + MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, + (u8 *) buf, os_strlen(buf))) { + wpa_printf(MSG_DEBUG, "Could not add Called-Station-Id"); + goto fail; + } + + os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, + MAC2STR(addr)); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, + (u8 *) buf, os_strlen(buf))) { + wpa_printf(MSG_DEBUG, "Could not add Calling-Station-Id"); + goto fail; + } + + if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE, + RADIUS_NAS_PORT_TYPE_IEEE_802_11)) { + wpa_printf(MSG_DEBUG, "Could not add NAS-Port-Type"); + goto fail; + } + + os_snprintf(buf, sizeof(buf), "CONNECT 11Mbps 802.11b"); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, + (u8 *) buf, os_strlen(buf))) { + wpa_printf(MSG_DEBUG, "Could not add Connect-Info"); + goto fail; + } + + radius_client_send(hapd->radius, msg, RADIUS_AUTH, addr); + return 0; + + fail: + radius_msg_free(msg); + return -1; +} +#endif /* CONFIG_NO_RADIUS */ + + +/** + * hostapd_allowed_address - Check whether a specified STA can be authenticated + * @hapd: hostapd BSS data + * @addr: MAC address of the STA + * @msg: Authentication message + * @len: Length of msg in octets + * @session_timeout: Buffer for returning session timeout (from RADIUS) + * @acct_interim_interval: Buffer for returning account interval (from RADIUS) + * @vlan_id: Buffer for returning VLAN ID + * Returns: HOSTAPD_ACL_ACCEPT, HOSTAPD_ACL_REJECT, or HOSTAPD_ACL_PENDING + */ +int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, + const u8 *msg, size_t len, u32 *session_timeout, + u32 *acct_interim_interval, int *vlan_id) +{ + if (session_timeout) + *session_timeout = 0; + if (acct_interim_interval) + *acct_interim_interval = 0; + if (vlan_id) + *vlan_id = 0; + + if (hostapd_maclist_found(hapd->conf->accept_mac, + hapd->conf->num_accept_mac, addr, vlan_id)) + return HOSTAPD_ACL_ACCEPT; + + if (hostapd_maclist_found(hapd->conf->deny_mac, + hapd->conf->num_deny_mac, addr, vlan_id)) + return HOSTAPD_ACL_REJECT; + + if (hapd->conf->macaddr_acl == ACCEPT_UNLESS_DENIED) + return HOSTAPD_ACL_ACCEPT; + if (hapd->conf->macaddr_acl == DENY_UNLESS_ACCEPTED) + return HOSTAPD_ACL_REJECT; + + if (hapd->conf->macaddr_acl == USE_EXTERNAL_RADIUS_AUTH) { +#ifdef CONFIG_NO_RADIUS + return HOSTAPD_ACL_REJECT; +#else /* CONFIG_NO_RADIUS */ + struct hostapd_acl_query_data *query; + + /* Check whether ACL cache has an entry for this station */ + int res = hostapd_acl_cache_get(hapd, addr, session_timeout, + acct_interim_interval, + vlan_id); + if (res == HOSTAPD_ACL_ACCEPT || + res == HOSTAPD_ACL_ACCEPT_TIMEOUT) + return res; + if (res == HOSTAPD_ACL_REJECT) + return HOSTAPD_ACL_REJECT; + + query = hapd->acl_queries; + while (query) { + if (os_memcmp(query->addr, addr, ETH_ALEN) == 0) { + /* pending query in RADIUS retransmit queue; + * do not generate a new one */ + return HOSTAPD_ACL_PENDING; + } + query = query->next; + } + + if (!hapd->conf->radius->auth_server) + return HOSTAPD_ACL_REJECT; + + /* No entry in the cache - query external RADIUS server */ + query = os_zalloc(sizeof(*query)); + if (query == NULL) { + wpa_printf(MSG_ERROR, "malloc for query data failed"); + return HOSTAPD_ACL_REJECT; + } + time(&query->timestamp); + os_memcpy(query->addr, addr, ETH_ALEN); + if (hostapd_radius_acl_query(hapd, addr, query)) { + wpa_printf(MSG_DEBUG, "Failed to send Access-Request " + "for ACL query."); + hostapd_acl_query_free(query); + return HOSTAPD_ACL_REJECT; + } + + query->auth_msg = os_malloc(len); + if (query->auth_msg == NULL) { + wpa_printf(MSG_ERROR, "Failed to allocate memory for " + "auth frame."); + hostapd_acl_query_free(query); + return HOSTAPD_ACL_REJECT; + } + os_memcpy(query->auth_msg, msg, len); + query->auth_msg_len = len; + query->next = hapd->acl_queries; + hapd->acl_queries = query; + + /* Queued data will be processed in hostapd_acl_recv_radius() + * when RADIUS server replies to the sent Access-Request. */ + return HOSTAPD_ACL_PENDING; +#endif /* CONFIG_NO_RADIUS */ + } + + return HOSTAPD_ACL_REJECT; +} + + +#ifndef CONFIG_NO_RADIUS +static void hostapd_acl_expire_cache(struct hostapd_data *hapd, time_t now) +{ + struct hostapd_cached_radius_acl *prev, *entry, *tmp; + + prev = NULL; + entry = hapd->acl_cache; + + while (entry) { + if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) { + wpa_printf(MSG_DEBUG, "Cached ACL entry for " MACSTR + " has expired.", MAC2STR(entry->addr)); + if (prev) + prev->next = entry->next; + else + hapd->acl_cache = entry->next; +#ifdef CONFIG_DRIVER_RADIUS_ACL + hapd->drv.set_radius_acl_expire(hapd, entry->addr); +#endif /* CONFIG_DRIVER_RADIUS_ACL */ + tmp = entry; + entry = entry->next; + os_free(tmp); + continue; + } + + prev = entry; + entry = entry->next; + } +} + + +static void hostapd_acl_expire_queries(struct hostapd_data *hapd, time_t now) +{ + struct hostapd_acl_query_data *prev, *entry, *tmp; + + prev = NULL; + entry = hapd->acl_queries; + + while (entry) { + if (now - entry->timestamp > RADIUS_ACL_TIMEOUT) { + wpa_printf(MSG_DEBUG, "ACL query for " MACSTR + " has expired.", MAC2STR(entry->addr)); + if (prev) + prev->next = entry->next; + else + hapd->acl_queries = entry->next; + + tmp = entry; + entry = entry->next; + hostapd_acl_query_free(tmp); + continue; + } + + prev = entry; + entry = entry->next; + } +} + + +/** + * hostapd_acl_expire - ACL cache expiration callback + * @eloop_ctx: struct hostapd_data * + * @timeout_ctx: Not used + */ +static void hostapd_acl_expire(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + time_t now; + + time(&now); + hostapd_acl_expire_cache(hapd, now); + hostapd_acl_expire_queries(hapd, now); + + eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL); +} + + +/** + * hostapd_acl_recv_radius - Process incoming RADIUS Authentication messages + * @msg: RADIUS response message + * @req: RADIUS request message + * @shared_secret: RADIUS shared secret + * @shared_secret_len: Length of shared_secret in octets + * @data: Context data (struct hostapd_data *) + * Returns: RADIUS_RX_PROCESSED if RADIUS message was a reply to ACL query (and + * was processed here) or RADIUS_RX_UNKNOWN if not. + */ +static RadiusRxResult +hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req, + const u8 *shared_secret, size_t shared_secret_len, + void *data) +{ + struct hostapd_data *hapd = data; + struct hostapd_acl_query_data *query, *prev; + struct hostapd_cached_radius_acl *cache; + struct radius_hdr *hdr = radius_msg_get_hdr(msg); + + query = hapd->acl_queries; + prev = NULL; + while (query) { + if (query->radius_id == hdr->identifier) + break; + prev = query; + query = query->next; + } + if (query == NULL) + return RADIUS_RX_UNKNOWN; + + wpa_printf(MSG_DEBUG, "Found matching Access-Request for RADIUS " + "message (id=%d)", query->radius_id); + + if (radius_msg_verify(msg, shared_secret, shared_secret_len, req, 0)) { + wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have " + "correct authenticator - dropped\n"); + return RADIUS_RX_INVALID_AUTHENTICATOR; + } + + if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT && + hdr->code != RADIUS_CODE_ACCESS_REJECT) { + wpa_printf(MSG_DEBUG, "Unknown RADIUS message code %d to ACL " + "query", hdr->code); + return RADIUS_RX_UNKNOWN; + } + + /* Insert Accept/Reject info into ACL cache */ + cache = os_zalloc(sizeof(*cache)); + if (cache == NULL) { + wpa_printf(MSG_DEBUG, "Failed to add ACL cache entry"); + goto done; + } + time(&cache->timestamp); + os_memcpy(cache->addr, query->addr, sizeof(cache->addr)); + if (hdr->code == RADIUS_CODE_ACCESS_ACCEPT) { + if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT, + &cache->session_timeout) == 0) + cache->accepted = HOSTAPD_ACL_ACCEPT_TIMEOUT; + else + cache->accepted = HOSTAPD_ACL_ACCEPT; + + if (radius_msg_get_attr_int32( + msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL, + &cache->acct_interim_interval) == 0 && + cache->acct_interim_interval < 60) { + wpa_printf(MSG_DEBUG, "Ignored too small " + "Acct-Interim-Interval %d for STA " MACSTR, + cache->acct_interim_interval, + MAC2STR(query->addr)); + cache->acct_interim_interval = 0; + } + + cache->vlan_id = radius_msg_get_vlanid(msg); + } else + cache->accepted = HOSTAPD_ACL_REJECT; + cache->next = hapd->acl_cache; + hapd->acl_cache = cache; + +#ifdef CONFIG_DRIVER_RADIUS_ACL + hapd->drv.set_radius_acl_auth(hapd, query->addr, cache->accepted, + cache->session_timeout); +#else /* CONFIG_DRIVER_RADIUS_ACL */ +#ifdef NEED_AP_MLME + /* Re-send original authentication frame for 802.11 processing */ + wpa_printf(MSG_DEBUG, "Re-sending authentication frame after " + "successful RADIUS ACL query"); + ieee802_11_mgmt(hapd, query->auth_msg, query->auth_msg_len, NULL); +#endif /* NEED_AP_MLME */ +#endif /* CONFIG_DRIVER_RADIUS_ACL */ + + done: + if (prev == NULL) + hapd->acl_queries = query->next; + else + prev->next = query->next; + + hostapd_acl_query_free(query); + + return RADIUS_RX_PROCESSED; +} +#endif /* CONFIG_NO_RADIUS */ + + +/** + * hostapd_acl_init: Initialize IEEE 802.11 ACL + * @hapd: hostapd BSS data + * Returns: 0 on success, -1 on failure + */ +int hostapd_acl_init(struct hostapd_data *hapd) +{ +#ifndef CONFIG_NO_RADIUS + if (radius_client_register(hapd->radius, RADIUS_AUTH, + hostapd_acl_recv_radius, hapd)) + return -1; + + eloop_register_timeout(10, 0, hostapd_acl_expire, hapd, NULL); +#endif /* CONFIG_NO_RADIUS */ + + return 0; +} + + +/** + * hostapd_acl_deinit - Deinitialize IEEE 802.11 ACL + * @hapd: hostapd BSS data + */ +void hostapd_acl_deinit(struct hostapd_data *hapd) +{ + struct hostapd_acl_query_data *query, *prev; + +#ifndef CONFIG_NO_RADIUS + eloop_cancel_timeout(hostapd_acl_expire, hapd, NULL); + + hostapd_acl_cache_free(hapd->acl_cache); +#endif /* CONFIG_NO_RADIUS */ + + query = hapd->acl_queries; + while (query) { + prev = query; + query = query->next; + hostapd_acl_query_free(prev); + } +} diff --git a/contrib/wpa/src/ap/ieee802_11_auth.h b/contrib/wpa/src/ap/ieee802_11_auth.h new file mode 100644 index 0000000..b2971e5 --- /dev/null +++ b/contrib/wpa/src/ap/ieee802_11_auth.h @@ -0,0 +1,31 @@ +/* + * hostapd / IEEE 802.11 authentication (ACL) + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef IEEE802_11_AUTH_H +#define IEEE802_11_AUTH_H + +enum { + HOSTAPD_ACL_REJECT = 0, + HOSTAPD_ACL_ACCEPT = 1, + HOSTAPD_ACL_PENDING = 2, + HOSTAPD_ACL_ACCEPT_TIMEOUT = 3 +}; + +int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr, + const u8 *msg, size_t len, u32 *session_timeout, + u32 *acct_interim_interval, int *vlan_id); +int hostapd_acl_init(struct hostapd_data *hapd); +void hostapd_acl_deinit(struct hostapd_data *hapd); + +#endif /* IEEE802_11_AUTH_H */ diff --git a/contrib/wpa/src/ap/ieee802_11_ht.c b/contrib/wpa/src/ap/ieee802_11_ht.c new file mode 100644 index 0000000..7541b83 --- /dev/null +++ b/contrib/wpa/src/ap/ieee802_11_ht.c @@ -0,0 +1,270 @@ +/* + * hostapd / IEEE 802.11n HT + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-2008, Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "drivers/driver.h" +#include "hostapd.h" +#include "ap_config.h" +#include "sta_info.h" +#include "beacon.h" +#include "ieee802_11.h" + + +u8 * hostapd_eid_ht_capabilities(struct hostapd_data *hapd, u8 *eid) +{ + struct ieee80211_ht_capabilities *cap; + u8 *pos = eid; + + if (!hapd->iconf->ieee80211n || !hapd->iface->current_mode) + return eid; + + *pos++ = WLAN_EID_HT_CAP; + *pos++ = sizeof(*cap); + + cap = (struct ieee80211_ht_capabilities *) pos; + os_memset(cap, 0, sizeof(*cap)); + cap->ht_capabilities_info = host_to_le16(hapd->iconf->ht_capab); + cap->a_mpdu_params = hapd->iface->current_mode->a_mpdu_params; + os_memcpy(cap->supported_mcs_set, hapd->iface->current_mode->mcs_set, + 16); + + /* TODO: ht_extended_capabilities (now fully disabled) */ + /* TODO: tx_bf_capability_info (now fully disabled) */ + /* TODO: asel_capabilities (now fully disabled) */ + + pos += sizeof(*cap); + + return pos; +} + + +u8 * hostapd_eid_ht_operation(struct hostapd_data *hapd, u8 *eid) +{ + struct ieee80211_ht_operation *oper; + u8 *pos = eid; + + if (!hapd->iconf->ieee80211n) + return eid; + + *pos++ = WLAN_EID_HT_OPERATION; + *pos++ = sizeof(*oper); + + oper = (struct ieee80211_ht_operation *) pos; + os_memset(oper, 0, sizeof(*oper)); + + oper->control_chan = hapd->iconf->channel; + oper->operation_mode = host_to_le16(hapd->iface->ht_op_mode); + if (hapd->iconf->secondary_channel == 1) + oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE | + HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH; + if (hapd->iconf->secondary_channel == -1) + oper->ht_param |= HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW | + HT_INFO_HT_PARAM_REC_TRANS_CHNL_WIDTH; + + pos += sizeof(*oper); + + return pos; +} + + +/* +op_mode +Set to 0 (HT pure) under the followign conditions + - all STAs in the BSS are 20/40 MHz HT in 20/40 MHz BSS or + - all STAs in the BSS are 20 MHz HT in 20 MHz BSS +Set to 1 (HT non-member protection) if there may be non-HT STAs + in both the primary and the secondary channel +Set to 2 if only HT STAs are associated in BSS, + however and at least one 20 MHz HT STA is associated +Set to 3 (HT mixed mode) when one or more non-HT STAs are associated + (currently non-GF HT station is considered as non-HT STA also) +*/ +int hostapd_ht_operation_update(struct hostapd_iface *iface) +{ + u16 cur_op_mode, new_op_mode; + int op_mode_changes = 0; + + if (!iface->conf->ieee80211n || iface->conf->ht_op_mode_fixed) + return 0; + + wpa_printf(MSG_DEBUG, "%s current operation mode=0x%X", + __func__, iface->ht_op_mode); + + if (!(iface->ht_op_mode & HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT) + && iface->num_sta_ht_no_gf) { + iface->ht_op_mode |= + HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT; + op_mode_changes++; + } else if ((iface->ht_op_mode & + HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT) && + iface->num_sta_ht_no_gf == 0) { + iface->ht_op_mode &= + ~HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT; + op_mode_changes++; + } + + if (!(iface->ht_op_mode & HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT) && + (iface->num_sta_no_ht || iface->olbc_ht)) { + iface->ht_op_mode |= HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT; + op_mode_changes++; + } else if ((iface->ht_op_mode & + HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT) && + (iface->num_sta_no_ht == 0 && !iface->olbc_ht)) { + iface->ht_op_mode &= + ~HT_INFO_OPERATION_MODE_NON_HT_STA_PRESENT; + op_mode_changes++; + } + + /* Note: currently we switch to the MIXED op mode if HT non-greenfield + * station is associated. Probably it's a theoretical case, since + * it looks like all known HT STAs support greenfield. + */ + new_op_mode = 0; + if (iface->num_sta_no_ht || + (iface->ht_op_mode & HT_INFO_OPERATION_MODE_NON_GF_DEVS_PRESENT)) + new_op_mode = OP_MODE_MIXED; + else if ((iface->conf->ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) + && iface->num_sta_ht_20mhz) + new_op_mode = OP_MODE_20MHZ_HT_STA_ASSOCED; + else if (iface->olbc_ht) + new_op_mode = OP_MODE_MAY_BE_LEGACY_STAS; + else + new_op_mode = OP_MODE_PURE; + + cur_op_mode = iface->ht_op_mode & HT_INFO_OPERATION_MODE_OP_MODE_MASK; + if (cur_op_mode != new_op_mode) { + iface->ht_op_mode &= ~HT_INFO_OPERATION_MODE_OP_MODE_MASK; + iface->ht_op_mode |= new_op_mode; + op_mode_changes++; + } + + wpa_printf(MSG_DEBUG, "%s new operation mode=0x%X changes=%d", + __func__, iface->ht_op_mode, op_mode_changes); + + return op_mode_changes; +} + + +u16 copy_sta_ht_capab(struct sta_info *sta, const u8 *ht_capab, + size_t ht_capab_len) +{ + if (!ht_capab || + ht_capab_len < sizeof(struct ieee80211_ht_capabilities)) { + sta->flags &= ~WLAN_STA_HT; + os_free(sta->ht_capabilities); + sta->ht_capabilities = NULL; + return WLAN_STATUS_SUCCESS; + } + + if (sta->ht_capabilities == NULL) { + sta->ht_capabilities = + os_zalloc(sizeof(struct ieee80211_ht_capabilities)); + if (sta->ht_capabilities == NULL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + sta->flags |= WLAN_STA_HT; + os_memcpy(sta->ht_capabilities, ht_capab, + sizeof(struct ieee80211_ht_capabilities)); + + return WLAN_STATUS_SUCCESS; +} + + +static void update_sta_ht(struct hostapd_data *hapd, struct sta_info *sta) +{ + u16 ht_capab; + + ht_capab = le_to_host16(sta->ht_capabilities->ht_capabilities_info); + wpa_printf(MSG_DEBUG, "HT: STA " MACSTR " HT Capabilities Info: " + "0x%04x", MAC2STR(sta->addr), ht_capab); + if ((ht_capab & HT_CAP_INFO_GREEN_FIELD) == 0) { + if (!sta->no_ht_gf_set) { + sta->no_ht_gf_set = 1; + hapd->iface->num_sta_ht_no_gf++; + } + wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - no greenfield, num " + "of non-gf stations %d", + __func__, MAC2STR(sta->addr), + hapd->iface->num_sta_ht_no_gf); + } + if ((ht_capab & HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET) == 0) { + if (!sta->ht_20mhz_set) { + sta->ht_20mhz_set = 1; + hapd->iface->num_sta_ht_20mhz++; + } + wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - 20 MHz HT, num of " + "20MHz HT STAs %d", + __func__, MAC2STR(sta->addr), + hapd->iface->num_sta_ht_20mhz); + } +} + + +static void update_sta_no_ht(struct hostapd_data *hapd, struct sta_info *sta) +{ + if (!sta->no_ht_set) { + sta->no_ht_set = 1; + hapd->iface->num_sta_no_ht++; + } + if (hapd->iconf->ieee80211n) { + wpa_printf(MSG_DEBUG, "%s STA " MACSTR " - no HT, num of " + "non-HT stations %d", + __func__, MAC2STR(sta->addr), + hapd->iface->num_sta_no_ht); + } +} + + +void update_ht_state(struct hostapd_data *hapd, struct sta_info *sta) +{ + if ((sta->flags & WLAN_STA_HT) && sta->ht_capabilities) + update_sta_ht(hapd, sta); + else + update_sta_no_ht(hapd, sta); + + if (hostapd_ht_operation_update(hapd->iface) > 0) + ieee802_11_set_beacons(hapd->iface); +} + + +void hostapd_get_ht_capab(struct hostapd_data *hapd, + struct ieee80211_ht_capabilities *ht_cap, + struct ieee80211_ht_capabilities *neg_ht_cap) +{ + u16 cap; + + if (ht_cap == NULL) + return; + os_memcpy(neg_ht_cap, ht_cap, sizeof(*neg_ht_cap)); + cap = le_to_host16(neg_ht_cap->ht_capabilities_info); + cap &= hapd->iconf->ht_capab; + cap |= (hapd->iconf->ht_capab & HT_CAP_INFO_SMPS_DISABLED); + + /* + * STBC needs to be handled specially + * if we don't support RX STBC, mask out TX STBC in the STA's HT caps + * if we don't support TX STBC, mask out RX STBC in the STA's HT caps + */ + if (!(hapd->iconf->ht_capab & HT_CAP_INFO_RX_STBC_MASK)) + cap &= ~HT_CAP_INFO_TX_STBC; + if (!(hapd->iconf->ht_capab & HT_CAP_INFO_TX_STBC)) + cap &= ~HT_CAP_INFO_RX_STBC_MASK; + + neg_ht_cap->ht_capabilities_info = host_to_le16(cap); +} diff --git a/contrib/wpa/src/ap/ieee802_1x.c b/contrib/wpa/src/ap/ieee802_1x.c new file mode 100644 index 0000000..eb160f8 --- /dev/null +++ b/contrib/wpa/src/ap/ieee802_1x.c @@ -0,0 +1,2025 @@ +/* + * hostapd / IEEE 802.1X-2004 Authenticator + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published 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 "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "crypto/md5.h" +#include "crypto/crypto.h" +#include "common/ieee802_11_defs.h" +#include "common/wpa_ctrl.h" +#include "radius/radius.h" +#include "radius/radius_client.h" +#include "eap_server/eap.h" +#include "eap_common/eap_wsc_common.h" +#include "eapol_auth/eapol_auth_sm.h" +#include "eapol_auth/eapol_auth_sm_i.h" +#include "hostapd.h" +#include "accounting.h" +#include "sta_info.h" +#include "wpa_auth.h" +#include "preauth_auth.h" +#include "pmksa_cache_auth.h" +#include "ap_config.h" +#include "ieee802_1x.h" + + +static void ieee802_1x_finished(struct hostapd_data *hapd, + struct sta_info *sta, int success); + + +static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta, + u8 type, const u8 *data, size_t datalen) +{ + u8 *buf; + struct ieee802_1x_hdr *xhdr; + size_t len; + int encrypt = 0; + + len = sizeof(*xhdr) + datalen; + buf = os_zalloc(len); + if (buf == NULL) { + wpa_printf(MSG_ERROR, "malloc() failed for " + "ieee802_1x_send(len=%lu)", + (unsigned long) len); + return; + } + + xhdr = (struct ieee802_1x_hdr *) buf; + xhdr->version = hapd->conf->eapol_version; + xhdr->type = type; + xhdr->length = host_to_be16(datalen); + + if (datalen > 0 && data != NULL) + os_memcpy(xhdr + 1, data, datalen); + + if (wpa_auth_pairwise_set(sta->wpa_sm)) + encrypt = 1; + if (sta->flags & WLAN_STA_PREAUTH) { + rsn_preauth_send(hapd, sta, buf, len); + } else { + hapd->drv.send_eapol(hapd, sta->addr, buf, len, encrypt); + } + + os_free(buf); +} + + +void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd, + struct sta_info *sta, int authorized) +{ + int res; + + if (sta->flags & WLAN_STA_PREAUTH) + return; + + if (authorized) { + if (!(sta->flags & WLAN_STA_AUTHORIZED)) + wpa_msg(hapd->msg_ctx, MSG_INFO, + AP_STA_CONNECTED MACSTR, MAC2STR(sta->addr)); + sta->flags |= WLAN_STA_AUTHORIZED; + res = hapd->drv.set_authorized(hapd, sta, 1); + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "authorizing port"); + } else { + if ((sta->flags & (WLAN_STA_AUTHORIZED | WLAN_STA_ASSOC)) == + (WLAN_STA_AUTHORIZED | WLAN_STA_ASSOC)) + wpa_msg(hapd->msg_ctx, MSG_INFO, + AP_STA_DISCONNECTED MACSTR, + MAC2STR(sta->addr)); + sta->flags &= ~WLAN_STA_AUTHORIZED; + res = hapd->drv.set_authorized(hapd, sta, 0); + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "unauthorizing port"); + } + + if (res && errno != ENOENT) { + printf("Could not set station " MACSTR " flags for kernel " + "driver (errno=%d).\n", MAC2STR(sta->addr), errno); + } + + if (authorized) + accounting_sta_start(hapd, sta); +} + + +static void ieee802_1x_tx_key_one(struct hostapd_data *hapd, + struct sta_info *sta, + int idx, int broadcast, + u8 *key_data, size_t key_len) +{ + u8 *buf, *ekey; + struct ieee802_1x_hdr *hdr; + struct ieee802_1x_eapol_key *key; + size_t len, ekey_len; + struct eapol_state_machine *sm = sta->eapol_sm; + + if (sm == NULL) + return; + + len = sizeof(*key) + key_len; + buf = os_zalloc(sizeof(*hdr) + len); + if (buf == NULL) + return; + + hdr = (struct ieee802_1x_hdr *) buf; + key = (struct ieee802_1x_eapol_key *) (hdr + 1); + key->type = EAPOL_KEY_TYPE_RC4; + key->key_length = htons(key_len); + wpa_get_ntp_timestamp(key->replay_counter); + + if (os_get_random(key->key_iv, sizeof(key->key_iv))) { + wpa_printf(MSG_ERROR, "Could not get random numbers"); + os_free(buf); + return; + } + + key->key_index = idx | (broadcast ? 0 : BIT(7)); + if (hapd->conf->eapol_key_index_workaround) { + /* According to some information, WinXP Supplicant seems to + * interpret bit7 as an indication whether the key is to be + * activated, so make it possible to enable workaround that + * sets this bit for all keys. */ + key->key_index |= BIT(7); + } + + /* Key is encrypted using "Key-IV + MSK[0..31]" as the RC4-key and + * MSK[32..63] is used to sign the message. */ + if (sm->eap_if->eapKeyData == NULL || sm->eap_if->eapKeyDataLen < 64) { + wpa_printf(MSG_ERROR, "No eapKeyData available for encrypting " + "and signing EAPOL-Key"); + os_free(buf); + return; + } + os_memcpy((u8 *) (key + 1), key_data, key_len); + ekey_len = sizeof(key->key_iv) + 32; + ekey = os_malloc(ekey_len); + if (ekey == NULL) { + wpa_printf(MSG_ERROR, "Could not encrypt key"); + os_free(buf); + return; + } + os_memcpy(ekey, key->key_iv, sizeof(key->key_iv)); + os_memcpy(ekey + sizeof(key->key_iv), sm->eap_if->eapKeyData, 32); + rc4_skip(ekey, ekey_len, 0, (u8 *) (key + 1), key_len); + os_free(ekey); + + /* This header is needed here for HMAC-MD5, but it will be regenerated + * in ieee802_1x_send() */ + hdr->version = hapd->conf->eapol_version; + hdr->type = IEEE802_1X_TYPE_EAPOL_KEY; + hdr->length = host_to_be16(len); + hmac_md5(sm->eap_if->eapKeyData + 32, 32, buf, sizeof(*hdr) + len, + key->key_signature); + + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Sending EAPOL-Key to " MACSTR + " (%s index=%d)", MAC2STR(sm->addr), + broadcast ? "broadcast" : "unicast", idx); + ieee802_1x_send(hapd, sta, IEEE802_1X_TYPE_EAPOL_KEY, (u8 *) key, len); + if (sta->eapol_sm) + sta->eapol_sm->dot1xAuthEapolFramesTx++; + os_free(buf); +} + + +#ifndef CONFIG_NO_VLAN +static struct hostapd_wep_keys * +ieee802_1x_group_alloc(struct hostapd_data *hapd, const char *ifname) +{ + struct hostapd_wep_keys *key; + + key = os_zalloc(sizeof(*key)); + if (key == NULL) + return NULL; + + key->default_len = hapd->conf->default_wep_key_len; + + if (key->idx >= hapd->conf->broadcast_key_idx_max || + key->idx < hapd->conf->broadcast_key_idx_min) + key->idx = hapd->conf->broadcast_key_idx_min; + else + key->idx++; + + if (!key->key[key->idx]) + key->key[key->idx] = os_malloc(key->default_len); + if (key->key[key->idx] == NULL || + os_get_random(key->key[key->idx], key->default_len)) { + printf("Could not generate random WEP key (dynamic VLAN).\n"); + os_free(key->key[key->idx]); + key->key[key->idx] = NULL; + os_free(key); + return NULL; + } + key->len[key->idx] = key->default_len; + + wpa_printf(MSG_DEBUG, "%s: Default WEP idx %d for dynamic VLAN\n", + ifname, key->idx); + wpa_hexdump_key(MSG_DEBUG, "Default WEP key (dynamic VLAN)", + key->key[key->idx], key->len[key->idx]); + + if (hapd->drv.set_key(ifname, hapd, WPA_ALG_WEP, NULL, key->idx, 1, + NULL, 0, key->key[key->idx], key->len[key->idx])) + printf("Could not set dynamic VLAN WEP encryption key.\n"); + + hapd->drv.set_drv_ieee8021x(hapd, ifname, 1); + + return key; +} + + +static struct hostapd_wep_keys * +ieee802_1x_get_group(struct hostapd_data *hapd, struct hostapd_ssid *ssid, + size_t vlan_id) +{ + const char *ifname; + + if (vlan_id == 0) + return &ssid->wep; + + if (vlan_id <= ssid->max_dyn_vlan_keys && ssid->dyn_vlan_keys && + ssid->dyn_vlan_keys[vlan_id]) + return ssid->dyn_vlan_keys[vlan_id]; + + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Creating new group " + "state machine for VLAN ID %lu", + (unsigned long) vlan_id); + + ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan, vlan_id); + if (ifname == NULL) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Unknown VLAN ID %lu - " + "cannot create group key state machine", + (unsigned long) vlan_id); + return NULL; + } + + if (ssid->dyn_vlan_keys == NULL) { + int size = (vlan_id + 1) * sizeof(ssid->dyn_vlan_keys[0]); + ssid->dyn_vlan_keys = os_zalloc(size); + if (ssid->dyn_vlan_keys == NULL) + return NULL; + ssid->max_dyn_vlan_keys = vlan_id; + } + + if (ssid->max_dyn_vlan_keys < vlan_id) { + struct hostapd_wep_keys **na; + int size = (vlan_id + 1) * sizeof(ssid->dyn_vlan_keys[0]); + na = os_realloc(ssid->dyn_vlan_keys, size); + if (na == NULL) + return NULL; + ssid->dyn_vlan_keys = na; + os_memset(&ssid->dyn_vlan_keys[ssid->max_dyn_vlan_keys + 1], 0, + (vlan_id - ssid->max_dyn_vlan_keys) * + sizeof(ssid->dyn_vlan_keys[0])); + ssid->max_dyn_vlan_keys = vlan_id; + } + + ssid->dyn_vlan_keys[vlan_id] = ieee802_1x_group_alloc(hapd, ifname); + + return ssid->dyn_vlan_keys[vlan_id]; +} +#endif /* CONFIG_NO_VLAN */ + + +void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) +{ + struct eapol_authenticator *eapol = hapd->eapol_auth; + struct eapol_state_machine *sm = sta->eapol_sm; +#ifndef CONFIG_NO_VLAN + struct hostapd_wep_keys *key = NULL; + int vlan_id; +#endif /* CONFIG_NO_VLAN */ + + if (sm == NULL || !sm->eap_if->eapKeyData) + return; + + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Sending EAPOL-Key(s) to " MACSTR, + MAC2STR(sta->addr)); + +#ifndef CONFIG_NO_VLAN + vlan_id = sta->vlan_id; + if (vlan_id < 0 || vlan_id > MAX_VLAN_ID) + vlan_id = 0; + + if (vlan_id) { + key = ieee802_1x_get_group(hapd, sta->ssid, vlan_id); + if (key && key->key[key->idx]) + ieee802_1x_tx_key_one(hapd, sta, key->idx, 1, + key->key[key->idx], + key->len[key->idx]); + } else +#endif /* CONFIG_NO_VLAN */ + if (eapol->default_wep_key) { + ieee802_1x_tx_key_one(hapd, sta, eapol->default_wep_key_idx, 1, + eapol->default_wep_key, + hapd->conf->default_wep_key_len); + } + + if (hapd->conf->individual_wep_key_len > 0) { + u8 *ikey; + ikey = os_malloc(hapd->conf->individual_wep_key_len); + if (ikey == NULL || + os_get_random(ikey, hapd->conf->individual_wep_key_len)) { + wpa_printf(MSG_ERROR, "Could not generate random " + "individual WEP key."); + os_free(ikey); + return; + } + + wpa_hexdump_key(MSG_DEBUG, "Individual WEP key", + ikey, hapd->conf->individual_wep_key_len); + + ieee802_1x_tx_key_one(hapd, sta, 0, 0, ikey, + hapd->conf->individual_wep_key_len); + + /* TODO: set encryption in TX callback, i.e., only after STA + * has ACKed EAPOL-Key frame */ + if (hapd->drv.set_key(hapd->conf->iface, hapd, WPA_ALG_WEP, + sta->addr, 0, 1, NULL, 0, ikey, + hapd->conf->individual_wep_key_len)) { + wpa_printf(MSG_ERROR, "Could not set individual WEP " + "encryption."); + } + + os_free(ikey); + } +} + + +const char *radius_mode_txt(struct hostapd_data *hapd) +{ + switch (hapd->iface->conf->hw_mode) { + case HOSTAPD_MODE_IEEE80211A: + return "802.11a"; + case HOSTAPD_MODE_IEEE80211G: + return "802.11g"; + case HOSTAPD_MODE_IEEE80211B: + default: + return "802.11b"; + } +} + + +int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta) +{ + int i; + u8 rate = 0; + + for (i = 0; i < sta->supported_rates_len; i++) + if ((sta->supported_rates[i] & 0x7f) > rate) + rate = sta->supported_rates[i] & 0x7f; + + return rate; +} + + +#ifndef CONFIG_NO_RADIUS +static void ieee802_1x_learn_identity(struct hostapd_data *hapd, + struct eapol_state_machine *sm, + const u8 *eap, size_t len) +{ + const u8 *identity; + size_t identity_len; + + if (len <= sizeof(struct eap_hdr) || + eap[sizeof(struct eap_hdr)] != EAP_TYPE_IDENTITY) + return; + + identity = eap_get_identity(sm->eap, &identity_len); + if (identity == NULL) + return; + + /* Save station identity for future RADIUS packets */ + os_free(sm->identity); + sm->identity = os_malloc(identity_len + 1); + if (sm->identity == NULL) { + sm->identity_len = 0; + return; + } + + os_memcpy(sm->identity, identity, identity_len); + sm->identity_len = identity_len; + sm->identity[identity_len] = '\0'; + hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "STA identity '%s'", sm->identity); + sm->dot1xAuthEapolRespIdFramesRx++; +} + + +static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *eap, size_t len) +{ + struct radius_msg *msg; + char buf[128]; + struct eapol_state_machine *sm = sta->eapol_sm; + + if (sm == NULL) + return; + + ieee802_1x_learn_identity(hapd, sm, eap, len); + + wpa_printf(MSG_DEBUG, "Encapsulating EAP message into a RADIUS " + "packet"); + + sm->radius_identifier = radius_client_get_id(hapd->radius); + msg = radius_msg_new(RADIUS_CODE_ACCESS_REQUEST, + sm->radius_identifier); + if (msg == NULL) { + printf("Could not create net RADIUS packet\n"); + return; + } + + radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta)); + + if (sm->identity && + !radius_msg_add_attr(msg, RADIUS_ATTR_USER_NAME, + sm->identity, sm->identity_len)) { + printf("Could not add User-Name\n"); + goto fail; + } + + if (hapd->conf->own_ip_addr.af == AF_INET && + !radius_msg_add_attr(msg, RADIUS_ATTR_NAS_IP_ADDRESS, + (u8 *) &hapd->conf->own_ip_addr.u.v4, 4)) { + printf("Could not add NAS-IP-Address\n"); + 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, + os_strlen(hapd->conf->nas_identifier))) { + printf("Could not add NAS-Identifier\n"); + goto fail; + } + + if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) { + printf("Could not add NAS-Port\n"); + goto fail; + } + + os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", + MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid); + buf[sizeof(buf) - 1] = '\0'; + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, + (u8 *) buf, os_strlen(buf))) { + printf("Could not add Called-Station-Id\n"); + goto fail; + } + + os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, + MAC2STR(sta->addr)); + buf[sizeof(buf) - 1] = '\0'; + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLING_STATION_ID, + (u8 *) buf, os_strlen(buf))) { + printf("Could not add Calling-Station-Id\n"); + goto fail; + } + + /* TODO: should probably check MTU from driver config; 2304 is max for + * IEEE 802.11, but use 1400 to avoid problems with too large packets + */ + if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) { + printf("Could not add Framed-MTU\n"); + goto fail; + } + + if (!radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE, + RADIUS_NAS_PORT_TYPE_IEEE_802_11)) { + printf("Could not add NAS-Port-Type\n"); + goto fail; + } + + if (sta->flags & WLAN_STA_PREAUTH) { + os_strlcpy(buf, "IEEE 802.11i Pre-Authentication", + sizeof(buf)); + } else { + os_snprintf(buf, sizeof(buf), "CONNECT %d%sMbps %s", + radius_sta_rate(hapd, sta) / 2, + (radius_sta_rate(hapd, sta) & 1) ? ".5" : "", + radius_mode_txt(hapd)); + buf[sizeof(buf) - 1] = '\0'; + } + if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, + (u8 *) buf, os_strlen(buf))) { + printf("Could not add Connect-Info\n"); + goto fail; + } + + if (eap && !radius_msg_add_eap(msg, eap, len)) { + printf("Could not add EAP-Message\n"); + goto fail; + } + + /* State attribute must be copied if and only if this packet is + * Access-Request reply to the previous Access-Challenge */ + if (sm->last_recv_radius && + radius_msg_get_hdr(sm->last_recv_radius)->code == + RADIUS_CODE_ACCESS_CHALLENGE) { + int res = radius_msg_copy_attr(msg, sm->last_recv_radius, + RADIUS_ATTR_STATE); + if (res < 0) { + printf("Could not copy State attribute from previous " + "Access-Challenge\n"); + goto fail; + } + if (res > 0) { + wpa_printf(MSG_DEBUG, "Copied RADIUS State Attribute"); + } + } + + radius_client_send(hapd->radius, msg, RADIUS_AUTH, sta->addr); + return; + + fail: + radius_msg_free(msg); +} +#endif /* CONFIG_NO_RADIUS */ + + +static void handle_eap_response(struct hostapd_data *hapd, + struct sta_info *sta, struct eap_hdr *eap, + size_t len) +{ + u8 type, *data; + struct eapol_state_machine *sm = sta->eapol_sm; + if (sm == NULL) + return; + + data = (u8 *) (eap + 1); + + if (len < sizeof(*eap) + 1) { + printf("handle_eap_response: too short response data\n"); + return; + } + + sm->eap_type_supp = type = data[0]; + + hostapd_logger(hapd, sm->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "received EAP packet (code=%d " + "id=%d len=%d) from STA: EAP Response-%s (%d)", + eap->code, eap->identifier, be_to_host16(eap->length), + eap_server_get_name(0, type), type); + + sm->dot1xAuthEapolRespFramesRx++; + + wpabuf_free(sm->eap_if->eapRespData); + sm->eap_if->eapRespData = wpabuf_alloc_copy(eap, len); + sm->eapolEap = TRUE; +} + + +/* Process incoming EAP packet from Supplicant */ +static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta, + u8 *buf, size_t len) +{ + struct eap_hdr *eap; + u16 eap_len; + + if (len < sizeof(*eap)) { + printf(" too short EAP packet\n"); + return; + } + + eap = (struct eap_hdr *) buf; + + eap_len = be_to_host16(eap->length); + wpa_printf(MSG_DEBUG, "EAP: code=%d identifier=%d length=%d", + eap->code, eap->identifier, eap_len); + if (eap_len < sizeof(*eap)) { + wpa_printf(MSG_DEBUG, " Invalid EAP length"); + return; + } else if (eap_len > len) { + wpa_printf(MSG_DEBUG, " Too short frame to contain this EAP " + "packet"); + return; + } else if (eap_len < len) { + wpa_printf(MSG_DEBUG, " Ignoring %lu extra bytes after EAP " + "packet", (unsigned long) len - eap_len); + } + + switch (eap->code) { + case EAP_CODE_REQUEST: + wpa_printf(MSG_DEBUG, " (request)"); + return; + case EAP_CODE_RESPONSE: + wpa_printf(MSG_DEBUG, " (response)"); + handle_eap_response(hapd, sta, eap, eap_len); + break; + case EAP_CODE_SUCCESS: + wpa_printf(MSG_DEBUG, " (success)"); + return; + case EAP_CODE_FAILURE: + wpa_printf(MSG_DEBUG, " (failure)"); + return; + default: + wpa_printf(MSG_DEBUG, " (unknown code)"); + return; + } +} + + +static struct eapol_state_machine * +ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta) +{ + int flags = 0; + if (sta->flags & WLAN_STA_PREAUTH) + flags |= EAPOL_SM_PREAUTH; + if (sta->wpa_sm) { + flags |= EAPOL_SM_USES_WPA; + if (wpa_auth_sta_get_pmksa(sta->wpa_sm)) + flags |= EAPOL_SM_FROM_PMKSA_CACHE; + } + return eapol_auth_alloc(hapd->eapol_auth, sta->addr, flags, + sta->wps_ie, sta); +} + + +/** + * ieee802_1x_receive - Process the EAPOL frames from the Supplicant + * @hapd: hostapd BSS data + * @sa: Source address (sender of the EAPOL frame) + * @buf: EAPOL frame + * @len: Length of buf in octets + * + * This function is called for each incoming EAPOL frame from the interface + */ +void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, + size_t len) +{ + struct sta_info *sta; + struct ieee802_1x_hdr *hdr; + struct ieee802_1x_eapol_key *key; + u16 datalen; + struct rsn_pmksa_cache_entry *pmksa; + + if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && + !hapd->conf->wps_state) + return; + + wpa_printf(MSG_DEBUG, "IEEE 802.1X: %lu bytes from " MACSTR, + (unsigned long) len, MAC2STR(sa)); + sta = ap_get_sta(hapd, sa); + if (!sta || !(sta->flags & WLAN_STA_ASSOC)) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X data frame from not " + "associated STA"); + return; + } + + if (len < sizeof(*hdr)) { + printf(" too short IEEE 802.1X packet\n"); + return; + } + + hdr = (struct ieee802_1x_hdr *) buf; + datalen = be_to_host16(hdr->length); + wpa_printf(MSG_DEBUG, " IEEE 802.1X: version=%d type=%d length=%d", + hdr->version, hdr->type, datalen); + + if (len - sizeof(*hdr) < datalen) { + printf(" frame too short for this IEEE 802.1X packet\n"); + if (sta->eapol_sm) + sta->eapol_sm->dot1xAuthEapLengthErrorFramesRx++; + return; + } + if (len - sizeof(*hdr) > datalen) { + wpa_printf(MSG_DEBUG, " ignoring %lu extra octets after " + "IEEE 802.1X packet", + (unsigned long) len - sizeof(*hdr) - datalen); + } + + if (sta->eapol_sm) { + sta->eapol_sm->dot1xAuthLastEapolFrameVersion = hdr->version; + sta->eapol_sm->dot1xAuthEapolFramesRx++; + } + + key = (struct ieee802_1x_eapol_key *) (hdr + 1); + if (datalen >= sizeof(struct ieee802_1x_eapol_key) && + hdr->type == IEEE802_1X_TYPE_EAPOL_KEY && + (key->type == EAPOL_KEY_TYPE_WPA || + key->type == EAPOL_KEY_TYPE_RSN)) { + wpa_receive(hapd->wpa_auth, sta->wpa_sm, (u8 *) hdr, + sizeof(*hdr) + datalen); + return; + } + + if ((!hapd->conf->ieee802_1x && + !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) || + wpa_key_mgmt_wpa_psk(wpa_auth_sta_key_mgmt(sta->wpa_sm))) + return; + + if (!sta->eapol_sm) { + sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta); + if (!sta->eapol_sm) + return; + +#ifdef CONFIG_WPS + if (!hapd->conf->ieee802_1x && + ((sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) == + WLAN_STA_MAYBE_WPS)) { + /* + * Delay EAPOL frame transmission until a possible WPS + * STA initiates the handshake with EAPOL-Start. + */ + sta->eapol_sm->flags |= EAPOL_SM_WAIT_START; + } +#endif /* CONFIG_WPS */ + + sta->eapol_sm->eap_if->portEnabled = TRUE; + } + + /* since we support version 1, we can ignore version field and proceed + * as specified in version 1 standard [IEEE Std 802.1X-2001, 7.5.5] */ + /* TODO: actually, we are not version 1 anymore.. However, Version 2 + * does not change frame contents, so should be ok to process frames + * more or less identically. Some changes might be needed for + * verification of fields. */ + + switch (hdr->type) { + case IEEE802_1X_TYPE_EAP_PACKET: + handle_eap(hapd, sta, (u8 *) (hdr + 1), datalen); + break; + + case IEEE802_1X_TYPE_EAPOL_START: + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "received EAPOL-Start " + "from STA"); + sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START; + pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm); + if (pmksa) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, "cached PMKSA " + "available - ignore it since " + "STA sent EAPOL-Start"); + wpa_auth_sta_clear_pmksa(sta->wpa_sm, pmksa); + } + sta->eapol_sm->eapolStart = TRUE; + sta->eapol_sm->dot1xAuthEapolStartFramesRx++; + wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH_EAPOL); + break; + + case IEEE802_1X_TYPE_EAPOL_LOGOFF: + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "received EAPOL-Logoff " + "from STA"); + sta->acct_terminate_cause = + RADIUS_ACCT_TERMINATE_CAUSE_USER_REQUEST; + accounting_sta_stop(hapd, sta); + sta->eapol_sm->eapolLogoff = TRUE; + sta->eapol_sm->dot1xAuthEapolLogoffFramesRx++; + break; + + case IEEE802_1X_TYPE_EAPOL_KEY: + wpa_printf(MSG_DEBUG, " EAPOL-Key"); + if (!(sta->flags & WLAN_STA_AUTHORIZED)) { + wpa_printf(MSG_DEBUG, " Dropped key data from " + "unauthorized Supplicant"); + break; + } + break; + + case IEEE802_1X_TYPE_EAPOL_ENCAPSULATED_ASF_ALERT: + wpa_printf(MSG_DEBUG, " EAPOL-Encapsulated-ASF-Alert"); + /* TODO: implement support for this; show data */ + break; + + default: + wpa_printf(MSG_DEBUG, " unknown IEEE 802.1X packet type"); + sta->eapol_sm->dot1xAuthInvalidEapolFramesRx++; + break; + } + + eapol_auth_step(sta->eapol_sm); +} + + +/** + * ieee802_1x_new_station - Start IEEE 802.1X authentication + * @hapd: hostapd BSS data + * @sta: The station + * + * This function is called to start IEEE 802.1X authentication when a new + * station completes IEEE 802.11 association. + */ +void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) +{ + struct rsn_pmksa_cache_entry *pmksa; + int reassoc = 1; + int force_1x = 0; + +#ifdef CONFIG_WPS + if (hapd->conf->wps_state && hapd->conf->wpa && + (sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) { + /* + * Need to enable IEEE 802.1X/EAPOL state machines for possible + * WPS handshake even if IEEE 802.1X/EAPOL is not used for + * authentication in this BSS. + */ + force_1x = 1; + } +#endif /* CONFIG_WPS */ + + if ((!force_1x && !hapd->conf->ieee802_1x) || + wpa_key_mgmt_wpa_psk(wpa_auth_sta_key_mgmt(sta->wpa_sm))) + return; + + if (sta->eapol_sm == NULL) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "start authentication"); + sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta); + if (sta->eapol_sm == NULL) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_INFO, + "failed to allocate state machine"); + return; + } + reassoc = 0; + } + +#ifdef CONFIG_WPS + sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START; + if (!hapd->conf->ieee802_1x && !(sta->flags & WLAN_STA_WPS)) { + /* + * Delay EAPOL frame transmission until a possible WPS + * initiates the handshake with EAPOL-Start. + */ + sta->eapol_sm->flags |= EAPOL_SM_WAIT_START; + } +#endif /* CONFIG_WPS */ + + sta->eapol_sm->eap_if->portEnabled = TRUE; + + pmksa = wpa_auth_sta_get_pmksa(sta->wpa_sm); + if (pmksa) { + int old_vlanid; + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, + "PMK from PMKSA cache - skip IEEE 802.1X/EAP"); + /* Setup EAPOL state machines to already authenticated state + * because of existing PMKSA information in the cache. */ + sta->eapol_sm->keyRun = TRUE; + sta->eapol_sm->eap_if->eapKeyAvailable = TRUE; + sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING; + sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS; + sta->eapol_sm->authSuccess = TRUE; + if (sta->eapol_sm->eap) + eap_sm_notify_cached(sta->eapol_sm->eap); + old_vlanid = sta->vlan_id; + pmksa_cache_to_eapol_data(pmksa, sta->eapol_sm); + if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED) + sta->vlan_id = 0; + ap_sta_bind_vlan(hapd, sta, old_vlanid); + } else { + if (reassoc) { + /* + * Force EAPOL state machines to start + * re-authentication without having to wait for the + * Supplicant to send EAPOL-Start. + */ + sta->eapol_sm->reAuthenticate = TRUE; + } + eapol_auth_step(sta->eapol_sm); + } +} + + +void ieee802_1x_free_station(struct sta_info *sta) +{ + struct eapol_state_machine *sm = sta->eapol_sm; + + if (sm == NULL) + return; + + sta->eapol_sm = NULL; + +#ifndef CONFIG_NO_RADIUS + radius_msg_free(sm->last_recv_radius); + radius_free_class(&sm->radius_class); +#endif /* CONFIG_NO_RADIUS */ + + os_free(sm->identity); + eapol_auth_free(sm); +} + + +#ifndef CONFIG_NO_RADIUS +static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd, + struct sta_info *sta) +{ + u8 *eap; + size_t len; + struct eap_hdr *hdr; + int eap_type = -1; + char buf[64]; + struct radius_msg *msg; + struct eapol_state_machine *sm = sta->eapol_sm; + + if (sm == NULL || sm->last_recv_radius == NULL) { + if (sm) + sm->eap_if->aaaEapNoReq = TRUE; + return; + } + + msg = sm->last_recv_radius; + + eap = radius_msg_get_eap(msg, &len); + if (eap == NULL) { + /* RFC 3579, Chap. 2.6.3: + * RADIUS server SHOULD NOT send Access-Reject/no EAP-Message + * attribute */ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_WARNING, "could not extract " + "EAP-Message from RADIUS message"); + sm->eap_if->aaaEapNoReq = TRUE; + return; + } + + if (len < sizeof(*hdr)) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_WARNING, "too short EAP packet " + "received from authentication server"); + os_free(eap); + sm->eap_if->aaaEapNoReq = TRUE; + return; + } + + if (len > sizeof(*hdr)) + eap_type = eap[sizeof(*hdr)]; + + hdr = (struct eap_hdr *) eap; + switch (hdr->code) { + case EAP_CODE_REQUEST: + if (eap_type >= 0) + sm->eap_type_authsrv = eap_type; + os_snprintf(buf, sizeof(buf), "EAP-Request-%s (%d)", + eap_type >= 0 ? eap_server_get_name(0, eap_type) : + "??", + eap_type); + break; + case EAP_CODE_RESPONSE: + os_snprintf(buf, sizeof(buf), "EAP Response-%s (%d)", + eap_type >= 0 ? eap_server_get_name(0, eap_type) : + "??", + eap_type); + break; + case EAP_CODE_SUCCESS: + os_strlcpy(buf, "EAP Success", sizeof(buf)); + break; + case EAP_CODE_FAILURE: + os_strlcpy(buf, "EAP Failure", sizeof(buf)); + break; + default: + os_strlcpy(buf, "unknown EAP code", sizeof(buf)); + break; + } + buf[sizeof(buf) - 1] = '\0'; + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "decapsulated EAP packet (code=%d " + "id=%d len=%d) from RADIUS server: %s", + hdr->code, hdr->identifier, be_to_host16(hdr->length), + buf); + sm->eap_if->aaaEapReq = TRUE; + + wpabuf_free(sm->eap_if->aaaEapReqData); + sm->eap_if->aaaEapReqData = wpabuf_alloc_ext_data(eap, len); +} + + +static void ieee802_1x_get_keys(struct hostapd_data *hapd, + struct sta_info *sta, struct radius_msg *msg, + struct radius_msg *req, + const u8 *shared_secret, + size_t shared_secret_len) +{ + struct radius_ms_mppe_keys *keys; + struct eapol_state_machine *sm = sta->eapol_sm; + if (sm == NULL) + return; + + keys = radius_msg_get_ms_keys(msg, req, shared_secret, + shared_secret_len); + + if (keys && keys->send && keys->recv) { + size_t len = keys->send_len + keys->recv_len; + wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Send-Key", + keys->send, keys->send_len); + wpa_hexdump_key(MSG_DEBUG, "MS-MPPE-Recv-Key", + keys->recv, keys->recv_len); + + os_free(sm->eap_if->aaaEapKeyData); + sm->eap_if->aaaEapKeyData = os_malloc(len); + if (sm->eap_if->aaaEapKeyData) { + os_memcpy(sm->eap_if->aaaEapKeyData, keys->recv, + keys->recv_len); + os_memcpy(sm->eap_if->aaaEapKeyData + keys->recv_len, + keys->send, keys->send_len); + sm->eap_if->aaaEapKeyDataLen = len; + sm->eap_if->aaaEapKeyAvailable = TRUE; + } + } + + if (keys) { + os_free(keys->send); + os_free(keys->recv); + os_free(keys); + } +} + + +static void ieee802_1x_store_radius_class(struct hostapd_data *hapd, + struct sta_info *sta, + struct radius_msg *msg) +{ + 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->radius->acct_server || hapd->radius == NULL || + sm == NULL) + return; + + radius_free_class(&sm->radius_class); + count = radius_msg_count_attr(msg, RADIUS_ATTR_CLASS, 1); + if (count <= 0) + return; + + nclass = os_zalloc(count * sizeof(struct radius_attr_data)); + if (nclass == NULL) + return; + + nclass_count = 0; + + 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 = os_malloc(class_len); + if (nclass[nclass_count].data == NULL) + break; + + os_memcpy(nclass[nclass_count].data, class, class_len); + nclass[nclass_count].len = class_len; + nclass_count++; + } + + sm->radius_class.attr = nclass; + sm->radius_class.count = nclass_count; + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Stored %lu RADIUS Class " + "attributes for " MACSTR, + (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 = os_malloc(len + 1); + if (identity == NULL) + return; + + os_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); + + os_free(sm->identity); + sm->identity = identity; + sm->identity_len = len; +} + + +struct sta_id_search { + u8 identifier; + struct eapol_state_machine *sm; +}; + + +static int ieee802_1x_select_radius_identifier(struct hostapd_data *hapd, + struct sta_info *sta, + void *ctx) +{ + struct sta_id_search *id_search = ctx; + struct eapol_state_machine *sm = sta->eapol_sm; + + if (sm && sm->radius_identifier >= 0 && + sm->radius_identifier == id_search->identifier) { + id_search->sm = sm; + return 1; + } + return 0; +} + + +static struct eapol_state_machine * +ieee802_1x_search_radius_identifier(struct hostapd_data *hapd, u8 identifier) +{ + struct sta_id_search id_search; + id_search.identifier = identifier; + id_search.sm = NULL; + ap_for_each_sta(hapd, ieee802_1x_select_radius_identifier, &id_search); + return id_search.sm; +} + + +/** + * ieee802_1x_receive_auth - Process RADIUS frames from Authentication Server + * @msg: RADIUS response message + * @req: RADIUS request message + * @shared_secret: RADIUS shared secret + * @shared_secret_len: Length of shared_secret in octets + * @data: Context data (struct hostapd_data *) + * Returns: Processing status + */ +static RadiusRxResult +ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, + const u8 *shared_secret, size_t shared_secret_len, + void *data) +{ + struct hostapd_data *hapd = data; + struct sta_info *sta; + u32 session_timeout = 0, termination_action, acct_interim_interval; + int session_timeout_set, old_vlanid = 0; + struct eapol_state_machine *sm; + int override_eapReq = 0; + struct radius_hdr *hdr = radius_msg_get_hdr(msg); + + sm = ieee802_1x_search_radius_identifier(hapd, hdr->identifier); + if (sm == NULL) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Could not find matching " + "station for this RADIUS message"); + return RADIUS_RX_UNKNOWN; + } + sta = sm->sta; + + /* RFC 2869, Ch. 5.13: valid Message-Authenticator attribute MUST be + * present when packet contains an EAP-Message attribute */ + if (hdr->code == RADIUS_CODE_ACCESS_REJECT && + radius_msg_get_attr(msg, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, NULL, + 0) < 0 && + radius_msg_get_attr(msg, RADIUS_ATTR_EAP_MESSAGE, NULL, 0) < 0) { + wpa_printf(MSG_DEBUG, "Allowing RADIUS Access-Reject without " + "Message-Authenticator since it does not include " + "EAP-Message"); + } else if (radius_msg_verify(msg, shared_secret, shared_secret_len, + req, 1)) { + printf("Incoming RADIUS packet did not have correct " + "Message-Authenticator - dropped\n"); + return RADIUS_RX_INVALID_AUTHENTICATOR; + } + + if (hdr->code != RADIUS_CODE_ACCESS_ACCEPT && + hdr->code != RADIUS_CODE_ACCESS_REJECT && + hdr->code != RADIUS_CODE_ACCESS_CHALLENGE) { + printf("Unknown RADIUS message code\n"); + return RADIUS_RX_UNKNOWN; + } + + sm->radius_identifier = -1; + wpa_printf(MSG_DEBUG, "RADIUS packet matching with station " MACSTR, + MAC2STR(sta->addr)); + + radius_msg_free(sm->last_recv_radius); + sm->last_recv_radius = msg; + + session_timeout_set = + !radius_msg_get_attr_int32(msg, RADIUS_ATTR_SESSION_TIMEOUT, + &session_timeout); + if (radius_msg_get_attr_int32(msg, RADIUS_ATTR_TERMINATION_ACTION, + &termination_action)) + termination_action = RADIUS_TERMINATION_ACTION_DEFAULT; + + if (hapd->conf->acct_interim_interval == 0 && + hdr->code == RADIUS_CODE_ACCESS_ACCEPT && + radius_msg_get_attr_int32(msg, RADIUS_ATTR_ACCT_INTERIM_INTERVAL, + &acct_interim_interval) == 0) { + if (acct_interim_interval < 60) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_INFO, + "ignored too small " + "Acct-Interim-Interval %d", + acct_interim_interval); + } else + sta->acct_interim_interval = acct_interim_interval; + } + + + switch (hdr->code) { + case RADIUS_CODE_ACCESS_ACCEPT: + if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED) + sta->vlan_id = 0; +#ifndef CONFIG_NO_VLAN + else { + old_vlanid = sta->vlan_id; + sta->vlan_id = radius_msg_get_vlanid(msg); + } + if (sta->vlan_id > 0 && + hostapd_get_vlan_id_ifname(hapd->conf->vlan, + sta->vlan_id)) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, + "VLAN ID %d", sta->vlan_id); + } else if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_REQUIRED) { + sta->eapol_sm->authFail = TRUE; + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_INFO, "authentication " + "server did not include required VLAN " + "ID in Access-Accept"); + break; + } +#endif /* CONFIG_NO_VLAN */ + + if (ap_sta_bind_vlan(hapd, sta, old_vlanid) < 0) + break; + + /* RFC 3580, Ch. 3.17 */ + if (session_timeout_set && termination_action == + RADIUS_TERMINATION_ACTION_RADIUS_REQUEST) { + sm->reAuthPeriod = session_timeout; + } else if (session_timeout_set) + ap_sta_session_timeout(hapd, sta, session_timeout); + + sm->eap_if->aaaSuccess = TRUE; + override_eapReq = 1; + ieee802_1x_get_keys(hapd, sta, msg, req, shared_secret, + shared_secret_len); + ieee802_1x_store_radius_class(hapd, sta, msg); + ieee802_1x_update_sta_identity(hapd, sta, msg); + if (sm->eap_if->eapKeyAvailable && + wpa_auth_pmksa_add(sta->wpa_sm, sm->eapol_key_crypt, + session_timeout_set ? + (int) session_timeout : -1, sm) == 0) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, + "Added PMKSA cache entry"); + } + break; + case RADIUS_CODE_ACCESS_REJECT: + sm->eap_if->aaaFail = TRUE; + override_eapReq = 1; + break; + case RADIUS_CODE_ACCESS_CHALLENGE: + sm->eap_if->aaaEapReq = TRUE; + if (session_timeout_set) { + /* RFC 2869, Ch. 2.3.2; RFC 3580, Ch. 3.17 */ + sm->eap_if->aaaMethodTimeout = session_timeout; + hostapd_logger(hapd, sm->addr, + HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, + "using EAP timeout of %d seconds (from " + "RADIUS)", + sm->eap_if->aaaMethodTimeout); + } else { + /* + * Use dynamic retransmission behavior per EAP + * specification. + */ + sm->eap_if->aaaMethodTimeout = 0; + } + break; + } + + ieee802_1x_decapsulate_radius(hapd, sta); + if (override_eapReq) + sm->eap_if->aaaEapReq = FALSE; + + eapol_auth_step(sm); + + return RADIUS_RX_QUEUED; +} +#endif /* CONFIG_NO_RADIUS */ + + +void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta) +{ + struct eapol_state_machine *sm = sta->eapol_sm; + if (sm == NULL) + return; + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "aborting authentication"); + +#ifndef CONFIG_NO_RADIUS + radius_msg_free(sm->last_recv_radius); + sm->last_recv_radius = NULL; +#endif /* CONFIG_NO_RADIUS */ + + if (sm->eap_if->eapTimeout) { + /* + * Disconnect the STA since it did not reply to the last EAP + * request and we cannot continue EAP processing (EAP-Failure + * could only be sent if the EAP peer actually replied). + */ + sm->eap_if->portEnabled = FALSE; + ap_sta_disconnect(hapd, sta, sta->addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); + } +} + + +static int ieee802_1x_rekey_broadcast(struct hostapd_data *hapd) +{ + struct eapol_authenticator *eapol = hapd->eapol_auth; + + if (hapd->conf->default_wep_key_len < 1) + return 0; + + os_free(eapol->default_wep_key); + eapol->default_wep_key = os_malloc(hapd->conf->default_wep_key_len); + if (eapol->default_wep_key == NULL || + os_get_random(eapol->default_wep_key, + hapd->conf->default_wep_key_len)) { + printf("Could not generate random WEP key.\n"); + os_free(eapol->default_wep_key); + eapol->default_wep_key = NULL; + return -1; + } + + wpa_hexdump_key(MSG_DEBUG, "IEEE 802.1X: New default WEP key", + eapol->default_wep_key, + hapd->conf->default_wep_key_len); + + return 0; +} + + +static int ieee802_1x_sta_key_available(struct hostapd_data *hapd, + struct sta_info *sta, void *ctx) +{ + if (sta->eapol_sm) { + sta->eapol_sm->eap_if->eapKeyAvailable = TRUE; + eapol_auth_step(sta->eapol_sm); + } + return 0; +} + + +static void ieee802_1x_rekey(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct eapol_authenticator *eapol = hapd->eapol_auth; + + if (eapol->default_wep_key_idx >= 3) + eapol->default_wep_key_idx = + hapd->conf->individual_wep_key_len > 0 ? 1 : 0; + else + eapol->default_wep_key_idx++; + + wpa_printf(MSG_DEBUG, "IEEE 802.1X: New default WEP key index %d", + eapol->default_wep_key_idx); + + if (ieee802_1x_rekey_broadcast(hapd)) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_WARNING, "failed to generate a " + "new broadcast key"); + os_free(eapol->default_wep_key); + eapol->default_wep_key = NULL; + return; + } + + /* TODO: Could setup key for RX here, but change default TX keyid only + * after new broadcast key has been sent to all stations. */ + if (hapd->drv.set_key(hapd->conf->iface, hapd, WPA_ALG_WEP, NULL, + eapol->default_wep_key_idx, 1, NULL, 0, + eapol->default_wep_key, + hapd->conf->default_wep_key_len)) { + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_WARNING, "failed to configure a " + "new broadcast key"); + os_free(eapol->default_wep_key); + eapol->default_wep_key = NULL; + return; + } + + ap_for_each_sta(hapd, ieee802_1x_sta_key_available, NULL); + + if (hapd->conf->wep_rekeying_period > 0) { + eloop_register_timeout(hapd->conf->wep_rekeying_period, 0, + ieee802_1x_rekey, hapd, NULL); + } +} + + +static void ieee802_1x_eapol_send(void *ctx, void *sta_ctx, u8 type, + const u8 *data, size_t datalen) +{ +#ifdef CONFIG_WPS + struct sta_info *sta = sta_ctx; + + if ((sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) == + WLAN_STA_MAYBE_WPS) { + const u8 *identity; + size_t identity_len; + struct eapol_state_machine *sm = sta->eapol_sm; + + identity = eap_get_identity(sm->eap, &identity_len); + if (identity && + ((identity_len == WSC_ID_ENROLLEE_LEN && + os_memcmp(identity, WSC_ID_ENROLLEE, + WSC_ID_ENROLLEE_LEN) == 0) || + (identity_len == WSC_ID_REGISTRAR_LEN && + os_memcmp(identity, WSC_ID_REGISTRAR, + WSC_ID_REGISTRAR_LEN) == 0))) { + wpa_printf(MSG_DEBUG, "WPS: WLAN_STA_MAYBE_WPS -> " + "WLAN_STA_WPS"); + sta->flags |= WLAN_STA_WPS; + } + } +#endif /* CONFIG_WPS */ + + ieee802_1x_send(ctx, sta_ctx, type, data, datalen); +} + + +static void ieee802_1x_aaa_send(void *ctx, void *sta_ctx, + const u8 *data, size_t datalen) +{ +#ifndef CONFIG_NO_RADIUS + struct hostapd_data *hapd = ctx; + struct sta_info *sta = sta_ctx; + + ieee802_1x_encapsulate_radius(hapd, sta, data, datalen); +#endif /* CONFIG_NO_RADIUS */ +} + + +static void _ieee802_1x_finished(void *ctx, void *sta_ctx, int success, + int preauth) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta = sta_ctx; + if (preauth) + rsn_preauth_finished(hapd, sta, success); + else + ieee802_1x_finished(hapd, sta, success); +} + + +static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity, + size_t identity_len, int phase2, + struct eap_user *user) +{ + struct hostapd_data *hapd = ctx; + const struct hostapd_eap_user *eap_user; + int i, count; + + eap_user = hostapd_get_eap_user(hapd->conf, identity, + identity_len, phase2); + if (eap_user == NULL) + return -1; + + os_memset(user, 0, sizeof(*user)); + user->phase2 = phase2; + count = EAP_USER_MAX_METHODS; + if (count > EAP_MAX_METHODS) + count = EAP_MAX_METHODS; + for (i = 0; i < count; i++) { + user->methods[i].vendor = eap_user->methods[i].vendor; + user->methods[i].method = eap_user->methods[i].method; + } + + if (eap_user->password) { + user->password = os_malloc(eap_user->password_len); + if (user->password == NULL) + return -1; + os_memcpy(user->password, eap_user->password, + eap_user->password_len); + user->password_len = eap_user->password_len; + } + user->force_version = eap_user->force_version; + user->ttls_auth = eap_user->ttls_auth; + + return 0; +} + + +static int ieee802_1x_sta_entry_alive(void *ctx, const u8 *addr) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + sta = ap_get_sta(hapd, addr); + if (sta == NULL || sta->eapol_sm == NULL) + return 0; + return 1; +} + + +static void ieee802_1x_logger(void *ctx, const u8 *addr, + eapol_logger_level level, const char *txt) +{ +#ifndef CONFIG_NO_HOSTAPD_LOGGER + struct hostapd_data *hapd = ctx; + int hlevel; + + switch (level) { + case EAPOL_LOGGER_WARNING: + hlevel = HOSTAPD_LEVEL_WARNING; + break; + case EAPOL_LOGGER_INFO: + hlevel = HOSTAPD_LEVEL_INFO; + break; + case EAPOL_LOGGER_DEBUG: + default: + hlevel = HOSTAPD_LEVEL_DEBUG; + break; + } + + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE8021X, hlevel, "%s", + txt); +#endif /* CONFIG_NO_HOSTAPD_LOGGER */ +} + + +static void ieee802_1x_set_port_authorized(void *ctx, void *sta_ctx, + int authorized) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta = sta_ctx; + ieee802_1x_set_sta_authorized(hapd, sta, authorized); +} + + +static void _ieee802_1x_abort_auth(void *ctx, void *sta_ctx) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta = sta_ctx; + ieee802_1x_abort_auth(hapd, sta); +} + + +static void _ieee802_1x_tx_key(void *ctx, void *sta_ctx) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta = sta_ctx; + ieee802_1x_tx_key(hapd, sta); +} + + +static void ieee802_1x_eapol_event(void *ctx, void *sta_ctx, + enum eapol_event type) +{ + /* struct hostapd_data *hapd = ctx; */ + struct sta_info *sta = sta_ctx; + switch (type) { + case EAPOL_AUTH_SM_CHANGE: + wpa_auth_sm_notify(sta->wpa_sm); + break; + case EAPOL_AUTH_REAUTHENTICATE: + wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH_EAPOL); + break; + } +} + + +int ieee802_1x_init(struct hostapd_data *hapd) +{ + int i; + struct eapol_auth_config conf; + struct eapol_auth_cb cb; + + os_memset(&conf, 0, sizeof(conf)); + conf.ctx = hapd; + conf.eap_reauth_period = hapd->conf->eap_reauth_period; + conf.wpa = hapd->conf->wpa; + conf.individual_wep_key_len = hapd->conf->individual_wep_key_len; + conf.eap_server = hapd->conf->eap_server; + conf.ssl_ctx = hapd->ssl_ctx; + conf.msg_ctx = hapd->msg_ctx; + conf.eap_sim_db_priv = hapd->eap_sim_db_priv; + conf.eap_req_id_text = hapd->conf->eap_req_id_text; + conf.eap_req_id_text_len = hapd->conf->eap_req_id_text_len; + conf.pac_opaque_encr_key = hapd->conf->pac_opaque_encr_key; + conf.eap_fast_a_id = hapd->conf->eap_fast_a_id; + conf.eap_fast_a_id_len = hapd->conf->eap_fast_a_id_len; + conf.eap_fast_a_id_info = hapd->conf->eap_fast_a_id_info; + conf.eap_fast_prov = hapd->conf->eap_fast_prov; + conf.pac_key_lifetime = hapd->conf->pac_key_lifetime; + conf.pac_key_refresh_time = hapd->conf->pac_key_refresh_time; + conf.eap_sim_aka_result_ind = hapd->conf->eap_sim_aka_result_ind; + conf.tnc = hapd->conf->tnc; + conf.wps = hapd->wps; + + os_memset(&cb, 0, sizeof(cb)); + cb.eapol_send = ieee802_1x_eapol_send; + cb.aaa_send = ieee802_1x_aaa_send; + cb.finished = _ieee802_1x_finished; + cb.get_eap_user = ieee802_1x_get_eap_user; + cb.sta_entry_alive = ieee802_1x_sta_entry_alive; + cb.logger = ieee802_1x_logger; + cb.set_port_authorized = ieee802_1x_set_port_authorized; + cb.abort_auth = _ieee802_1x_abort_auth; + cb.tx_key = _ieee802_1x_tx_key; + cb.eapol_event = ieee802_1x_eapol_event; + + hapd->eapol_auth = eapol_auth_init(&conf, &cb); + if (hapd->eapol_auth == NULL) + return -1; + + if ((hapd->conf->ieee802_1x || hapd->conf->wpa) && + hapd->drv.set_drv_ieee8021x(hapd, hapd->conf->iface, 1)) + return -1; + +#ifndef CONFIG_NO_RADIUS + if (radius_client_register(hapd->radius, RADIUS_AUTH, + ieee802_1x_receive_auth, hapd)) + return -1; +#endif /* CONFIG_NO_RADIUS */ + + if (hapd->conf->default_wep_key_len) { + for (i = 0; i < 4; i++) + hapd->drv.set_key(hapd->conf->iface, hapd, + WPA_ALG_NONE, NULL, i, 0, NULL, 0, + NULL, 0); + + ieee802_1x_rekey(hapd, NULL); + + if (hapd->eapol_auth->default_wep_key == NULL) + return -1; + } + + return 0; +} + + +void ieee802_1x_deinit(struct hostapd_data *hapd) +{ + eloop_cancel_timeout(ieee802_1x_rekey, hapd, NULL); + + if (hapd->driver != NULL && + (hapd->conf->ieee802_1x || hapd->conf->wpa)) + hapd->drv.set_drv_ieee8021x(hapd, hapd->conf->iface, 0); + + eapol_auth_deinit(hapd->eapol_auth); + hapd->eapol_auth = NULL; +} + + +int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *buf, size_t len, int ack) +{ + struct ieee80211_hdr *hdr; + struct ieee802_1x_hdr *xhdr; + struct ieee802_1x_eapol_key *key; + u8 *pos; + const unsigned char rfc1042_hdr[ETH_ALEN] = + { 0xaa, 0xaa, 0x03, 0x00, 0x00, 0x00 }; + + if (sta == NULL) + return -1; + if (len < sizeof(*hdr) + sizeof(rfc1042_hdr) + 2 + sizeof(*xhdr)) + return 0; + + hdr = (struct ieee80211_hdr *) buf; + pos = (u8 *) (hdr + 1); + if (os_memcmp(pos, rfc1042_hdr, sizeof(rfc1042_hdr)) != 0) + return 0; + pos += sizeof(rfc1042_hdr); + if (WPA_GET_BE16(pos) != ETH_P_PAE) + return 0; + pos += 2; + + xhdr = (struct ieee802_1x_hdr *) pos; + pos += sizeof(*xhdr); + + wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR " TX status - version=%d " + "type=%d length=%d - ack=%d", + MAC2STR(sta->addr), xhdr->version, xhdr->type, + be_to_host16(xhdr->length), ack); + + /* EAPOL EAP-Packet packets are eventually re-sent by either Supplicant + * or Authenticator state machines, but EAPOL-Key packets are not + * retransmitted in case of failure. Try to re-sent failed EAPOL-Key + * packets couple of times because otherwise STA keys become + * unsynchronized with AP. */ + if (xhdr->type == IEEE802_1X_TYPE_EAPOL_KEY && !ack && + pos + sizeof(*key) <= buf + len) { + key = (struct ieee802_1x_eapol_key *) pos; + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, "did not Ack EAPOL-Key " + "frame (%scast index=%d)", + key->key_index & BIT(7) ? "uni" : "broad", + key->key_index & ~BIT(7)); + /* TODO: re-send EAPOL-Key couple of times (with short delay + * between them?). If all attempt fail, report error and + * deauthenticate STA so that it will get new keys when + * authenticating again (e.g., after returning in range). + * Separate limit/transmit state needed both for unicast and + * broadcast keys(?) */ + } + /* TODO: could move unicast key configuration from ieee802_1x_tx_key() + * to here and change the key only if the EAPOL-Key packet was Acked. + */ + + return 1; +} + + +u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len) +{ + if (sm == NULL || sm->identity == NULL) + return NULL; + + *len = sm->identity_len; + return sm->identity; +} + + +u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len, + int idx) +{ + if (sm == NULL || sm->radius_class.attr == NULL || + idx >= (int) sm->radius_class.count) + return NULL; + + *len = sm->radius_class.attr[idx].len; + return sm->radius_class.attr[idx].data; +} + + +const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len) +{ + if (sm == NULL) + return NULL; + + *len = sm->eap_if->eapKeyDataLen; + return sm->eap_if->eapKeyData; +} + + +void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm, + int enabled) +{ + if (sm == NULL) + return; + sm->eap_if->portEnabled = enabled ? TRUE : FALSE; + eapol_auth_step(sm); +} + + +void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm, + int valid) +{ + if (sm == NULL) + return; + sm->portValid = valid ? TRUE : FALSE; + eapol_auth_step(sm); +} + + +void ieee802_1x_notify_pre_auth(struct eapol_state_machine *sm, int pre_auth) +{ + if (sm == NULL) + return; + if (pre_auth) + sm->flags |= EAPOL_SM_PREAUTH; + else + sm->flags &= ~EAPOL_SM_PREAUTH; +} + + +static const char * bool_txt(Boolean bool) +{ + return bool ? "TRUE" : "FALSE"; +} + + +int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen) +{ + /* TODO */ + return 0; +} + + +int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, + char *buf, size_t buflen) +{ + int len = 0, ret; + struct eapol_state_machine *sm = sta->eapol_sm; + + if (sm == NULL) + return 0; + + ret = os_snprintf(buf + len, buflen - len, + "dot1xPaePortNumber=%d\n" + "dot1xPaePortProtocolVersion=%d\n" + "dot1xPaePortCapabilities=1\n" + "dot1xPaePortInitialize=%d\n" + "dot1xPaePortReauthenticate=FALSE\n", + sta->aid, + EAPOL_VERSION, + sm->initialize); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + /* dot1xAuthConfigTable */ + ret = os_snprintf(buf + len, buflen - len, + "dot1xAuthPaeState=%d\n" + "dot1xAuthBackendAuthState=%d\n" + "dot1xAuthAdminControlledDirections=%d\n" + "dot1xAuthOperControlledDirections=%d\n" + "dot1xAuthAuthControlledPortStatus=%d\n" + "dot1xAuthAuthControlledPortControl=%d\n" + "dot1xAuthQuietPeriod=%u\n" + "dot1xAuthServerTimeout=%u\n" + "dot1xAuthReAuthPeriod=%u\n" + "dot1xAuthReAuthEnabled=%s\n" + "dot1xAuthKeyTxEnabled=%s\n", + sm->auth_pae_state + 1, + sm->be_auth_state + 1, + sm->adminControlledDirections, + sm->operControlledDirections, + sm->authPortStatus, + sm->portControl, + sm->quietPeriod, + sm->serverTimeout, + sm->reAuthPeriod, + bool_txt(sm->reAuthEnabled), + bool_txt(sm->keyTxEnabled)); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + /* dot1xAuthStatsTable */ + ret = os_snprintf(buf + len, buflen - len, + "dot1xAuthEapolFramesRx=%u\n" + "dot1xAuthEapolFramesTx=%u\n" + "dot1xAuthEapolStartFramesRx=%u\n" + "dot1xAuthEapolLogoffFramesRx=%u\n" + "dot1xAuthEapolRespIdFramesRx=%u\n" + "dot1xAuthEapolRespFramesRx=%u\n" + "dot1xAuthEapolReqIdFramesTx=%u\n" + "dot1xAuthEapolReqFramesTx=%u\n" + "dot1xAuthInvalidEapolFramesRx=%u\n" + "dot1xAuthEapLengthErrorFramesRx=%u\n" + "dot1xAuthLastEapolFrameVersion=%u\n" + "dot1xAuthLastEapolFrameSource=" MACSTR "\n", + sm->dot1xAuthEapolFramesRx, + sm->dot1xAuthEapolFramesTx, + sm->dot1xAuthEapolStartFramesRx, + sm->dot1xAuthEapolLogoffFramesRx, + sm->dot1xAuthEapolRespIdFramesRx, + sm->dot1xAuthEapolRespFramesRx, + sm->dot1xAuthEapolReqIdFramesTx, + sm->dot1xAuthEapolReqFramesTx, + sm->dot1xAuthInvalidEapolFramesRx, + sm->dot1xAuthEapLengthErrorFramesRx, + sm->dot1xAuthLastEapolFrameVersion, + MAC2STR(sm->addr)); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + /* dot1xAuthDiagTable */ + ret = os_snprintf(buf + len, buflen - len, + "dot1xAuthEntersConnecting=%u\n" + "dot1xAuthEapLogoffsWhileConnecting=%u\n" + "dot1xAuthEntersAuthenticating=%u\n" + "dot1xAuthAuthSuccessesWhileAuthenticating=%u\n" + "dot1xAuthAuthTimeoutsWhileAuthenticating=%u\n" + "dot1xAuthAuthFailWhileAuthenticating=%u\n" + "dot1xAuthAuthEapStartsWhileAuthenticating=%u\n" + "dot1xAuthAuthEapLogoffWhileAuthenticating=%u\n" + "dot1xAuthAuthReauthsWhileAuthenticated=%u\n" + "dot1xAuthAuthEapStartsWhileAuthenticated=%u\n" + "dot1xAuthAuthEapLogoffWhileAuthenticated=%u\n" + "dot1xAuthBackendResponses=%u\n" + "dot1xAuthBackendAccessChallenges=%u\n" + "dot1xAuthBackendOtherRequestsToSupplicant=%u\n" + "dot1xAuthBackendAuthSuccesses=%u\n" + "dot1xAuthBackendAuthFails=%u\n", + sm->authEntersConnecting, + sm->authEapLogoffsWhileConnecting, + sm->authEntersAuthenticating, + sm->authAuthSuccessesWhileAuthenticating, + sm->authAuthTimeoutsWhileAuthenticating, + sm->authAuthFailWhileAuthenticating, + sm->authAuthEapStartsWhileAuthenticating, + sm->authAuthEapLogoffWhileAuthenticating, + sm->authAuthReauthsWhileAuthenticated, + sm->authAuthEapStartsWhileAuthenticated, + sm->authAuthEapLogoffWhileAuthenticated, + sm->backendResponses, + sm->backendAccessChallenges, + sm->backendOtherRequestsToSupplicant, + sm->backendAuthSuccesses, + sm->backendAuthFails); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + /* dot1xAuthSessionStatsTable */ + ret = os_snprintf(buf + len, buflen - len, + /* TODO: dot1xAuthSessionOctetsRx */ + /* TODO: dot1xAuthSessionOctetsTx */ + /* TODO: dot1xAuthSessionFramesRx */ + /* TODO: dot1xAuthSessionFramesTx */ + "dot1xAuthSessionId=%08X-%08X\n" + "dot1xAuthSessionAuthenticMethod=%d\n" + "dot1xAuthSessionTime=%u\n" + "dot1xAuthSessionTerminateCause=999\n" + "dot1xAuthSessionUserName=%s\n", + sta->acct_session_id_hi, sta->acct_session_id_lo, + (wpa_key_mgmt_wpa_ieee8021x( + wpa_auth_sta_key_mgmt(sta->wpa_sm))) ? + 1 : 2, + (unsigned int) (time(NULL) - + sta->acct_session_start), + sm->identity); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + return len; +} + + +static void ieee802_1x_finished(struct hostapd_data *hapd, + struct sta_info *sta, int success) +{ + const u8 *key; + size_t len; + /* TODO: get PMKLifetime from WPA parameters */ + static const int dot11RSNAConfigPMKLifetime = 43200; + + key = ieee802_1x_get_key(sta->eapol_sm, &len); + if (success && key && len >= PMK_LEN && + wpa_auth_pmksa_add(sta->wpa_sm, key, dot11RSNAConfigPMKLifetime, + sta->eapol_sm) == 0) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, + "Added PMKSA cache entry (IEEE 802.1X)"); + } + +#ifdef CONFIG_WPS + if (!success && (sta->flags & WLAN_STA_WPS)) { + /* + * Many devices require deauthentication after WPS provisioning + * and some may not be be able to do that themselves, so + * disconnect the client here. + */ + wpa_printf(MSG_DEBUG, "WPS: Force disconnection after " + "EAP-Failure"); + /* Add a small sleep to increase likelihood of previously + * requested EAP-Failure TX getting out before this should the + * driver reorder operations. + */ + os_sleep(0, 10000); + ap_sta_disconnect(hapd, sta, sta->addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); + } +#endif /* CONFIG_WPS */ +} diff --git a/contrib/wpa/src/ap/ieee802_1x.h b/contrib/wpa/src/ap/ieee802_1x.h new file mode 100644 index 0000000..1a4d2eb --- /dev/null +++ b/contrib/wpa/src/ap/ieee802_1x.h @@ -0,0 +1,89 @@ +/* + * hostapd / IEEE 802.1X-2004 Authenticator + * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef IEEE802_1X_H +#define IEEE802_1X_H + +struct hostapd_data; +struct sta_info; +struct eapol_state_machine; +struct hostapd_config; +struct hostapd_bss_config; + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +/* RFC 3580, 4. RC4 EAPOL-Key Frame */ + +struct ieee802_1x_eapol_key { + u8 type; + u16 key_length; + u8 replay_counter[8]; /* does not repeat within the life of the keying + * material used to encrypt the Key field; + * 64-bit NTP timestamp MAY be used here */ + u8 key_iv[16]; /* cryptographically random number */ + u8 key_index; /* key flag in the most significant bit: + * 0 = broadcast (default key), + * 1 = unicast (key mapping key); key index is in the + * 7 least significant bits */ + u8 key_signature[16]; /* HMAC-MD5 message integrity check computed with + * MS-MPPE-Send-Key as the key */ + + /* followed by key: if packet body length = 44 + key length, then the + * key field (of key_length bytes) contains the key in encrypted form; + * if packet body length = 44, key field is absent and key_length + * represents the number of least significant octets from + * MS-MPPE-Send-Key attribute to be used as the keying material; + * RC4 key used in encryption = Key-IV + MS-MPPE-Recv-Key */ +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + + +void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, + size_t len); +void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta); +void ieee802_1x_free_station(struct sta_info *sta); + +void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta); +void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta); +void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd, + struct sta_info *sta, int authorized); +void ieee802_1x_dump_state(FILE *f, const char *prefix, struct sta_info *sta); +int ieee802_1x_init(struct hostapd_data *hapd); +void ieee802_1x_deinit(struct hostapd_data *hapd); +int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *buf, size_t len, int ack); +u8 * ieee802_1x_get_identity(struct eapol_state_machine *sm, size_t *len); +u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len, + int idx); +const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len); +void ieee802_1x_notify_port_enabled(struct eapol_state_machine *sm, + int enabled); +void ieee802_1x_notify_port_valid(struct eapol_state_machine *sm, + int valid); +void ieee802_1x_notify_pre_auth(struct eapol_state_machine *sm, int pre_auth); +int ieee802_1x_get_mib(struct hostapd_data *hapd, char *buf, size_t buflen); +int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, + char *buf, size_t buflen); +void hostapd_get_ntp_timestamp(u8 *buf); +char *eap_type_text(u8 type); + +const char *radius_mode_txt(struct hostapd_data *hapd); +int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta); + +#endif /* IEEE802_1X_H */ diff --git a/contrib/wpa/src/ap/peerkey_auth.c b/contrib/wpa/src/ap/peerkey_auth.c new file mode 100644 index 0000000..f68c479 --- /dev/null +++ b/contrib/wpa/src/ap/peerkey_auth.c @@ -0,0 +1,401 @@ +/* + * hostapd - PeerKey for Direct Link Setup (DLS) + * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" +#include "wpa_auth.h" +#include "wpa_auth_i.h" +#include "wpa_auth_ie.h" + +#ifdef CONFIG_PEERKEY + +static void wpa_stsl_step(void *eloop_ctx, void *timeout_ctx) +{ +#if 0 + struct wpa_authenticator *wpa_auth = eloop_ctx; + struct wpa_stsl_negotiation *neg = timeout_ctx; +#endif + + /* TODO: ? */ +} + + +struct wpa_stsl_search { + const u8 *addr; + struct wpa_state_machine *sm; +}; + + +static int wpa_stsl_select_sta(struct wpa_state_machine *sm, void *ctx) +{ + struct wpa_stsl_search *search = ctx; + if (os_memcmp(search->addr, sm->addr, ETH_ALEN) == 0) { + search->sm = sm; + return 1; + } + return 0; +} + + +static void wpa_smk_send_error(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, const u8 *peer, + u16 mui, u16 error_type) +{ + u8 kde[2 + RSN_SELECTOR_LEN + ETH_ALEN + + 2 + RSN_SELECTOR_LEN + sizeof(struct rsn_error_kde)]; + u8 *pos; + struct rsn_error_kde error; + + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "Sending SMK Error"); + + pos = kde; + + if (peer) { + pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, + NULL, 0); + } + + error.mui = host_to_be16(mui); + error.error_type = host_to_be16(error_type); + pos = wpa_add_kde(pos, RSN_KEY_DATA_ERROR, + (u8 *) &error, sizeof(error), NULL, 0); + + __wpa_send_eapol(wpa_auth, sm, + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_ERROR, + NULL, NULL, kde, pos - kde, 0, 0, 0); +} + + +void wpa_smk_m1(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, struct wpa_eapol_key *key) +{ + struct wpa_eapol_ie_parse kde; + struct wpa_stsl_search search; + u8 *buf, *pos; + size_t buf_len; + + if (wpa_parse_kde_ies((const u8 *) (key + 1), + WPA_GET_BE16(key->key_data_length), &kde) < 0) { + wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M1"); + return; + } + + if (kde.rsn_ie == NULL || kde.mac_addr == NULL || + kde.mac_addr_len < ETH_ALEN) { + wpa_printf(MSG_INFO, "RSN: No RSN IE or MAC address KDE in " + "SMK M1"); + return; + } + + /* Initiator = sm->addr; Peer = kde.mac_addr */ + + search.addr = kde.mac_addr; + search.sm = NULL; + if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == + 0 || search.sm == NULL) { + wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR + " aborted - STA not associated anymore", + MAC2STR(kde.mac_addr)); + wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK, + STK_ERR_STA_NR); + /* FIX: wpa_stsl_remove(wpa_auth, neg); */ + return; + } + + buf_len = kde.rsn_ie_len + 2 + RSN_SELECTOR_LEN + ETH_ALEN; + buf = os_malloc(buf_len); + if (buf == NULL) + return; + /* Initiator RSN IE */ + os_memcpy(buf, kde.rsn_ie, kde.rsn_ie_len); + pos = buf + kde.rsn_ie_len; + /* Initiator MAC Address */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, sm->addr, ETH_ALEN, + NULL, 0); + + /* SMK M2: + * EAPOL-Key(S=1, M=1, A=1, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, + * MIC=MIC, DataKDs=(RSNIE_I, MAC_I KDE) + */ + + wpa_auth_logger(wpa_auth, search.sm->addr, LOGGER_DEBUG, + "Sending SMK M2"); + + __wpa_send_eapol(wpa_auth, search.sm, + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_ACK | WPA_KEY_INFO_SMK_MESSAGE, + NULL, key->key_nonce, buf, pos - buf, 0, 0, 0); + + os_free(buf); +} + + +static void wpa_send_smk_m4(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + struct wpa_eapol_key *key, + struct wpa_eapol_ie_parse *kde, + const u8 *smk) +{ + u8 *buf, *pos; + size_t buf_len; + u32 lifetime; + + /* SMK M4: + * EAPOL-Key(S=1, M=1, A=0, I=1, K=0, SM=1, KeyRSC=0, Nonce=PNonce, + * MIC=MIC, DataKDs=(MAC_I KDE, INonce KDE, SMK KDE, + * Lifetime KDE) + */ + + buf_len = 2 + RSN_SELECTOR_LEN + ETH_ALEN + + 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN + + 2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN + + 2 + RSN_SELECTOR_LEN + sizeof(lifetime); + pos = buf = os_malloc(buf_len); + if (buf == NULL) + return; + + /* Initiator MAC Address */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, kde->mac_addr, ETH_ALEN, + NULL, 0); + + /* Initiator Nonce */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, kde->nonce, WPA_NONCE_LEN, + NULL, 0); + + /* SMK with PNonce */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN, + key->key_nonce, WPA_NONCE_LEN); + + /* Lifetime */ + lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, + (u8 *) &lifetime, sizeof(lifetime), NULL, 0); + + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "Sending SMK M4"); + + __wpa_send_eapol(wpa_auth, sm, + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_INSTALL | WPA_KEY_INFO_SMK_MESSAGE, + NULL, key->key_nonce, buf, pos - buf, 0, 1, 0); + + os_free(buf); +} + + +static void wpa_send_smk_m5(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + struct wpa_eapol_key *key, + struct wpa_eapol_ie_parse *kde, + const u8 *smk, const u8 *peer) +{ + u8 *buf, *pos; + size_t buf_len; + u32 lifetime; + + /* SMK M5: + * EAPOL-Key(S=1, M=1, A=0, I=0, K=0, SM=1, KeyRSC=0, Nonce=INonce, + * MIC=MIC, DataKDs=(RSNIE_P, MAC_P KDE, PNonce, SMK KDE, + * Lifetime KDE)) + */ + + buf_len = kde->rsn_ie_len + + 2 + RSN_SELECTOR_LEN + ETH_ALEN + + 2 + RSN_SELECTOR_LEN + WPA_NONCE_LEN + + 2 + RSN_SELECTOR_LEN + PMK_LEN + WPA_NONCE_LEN + + 2 + RSN_SELECTOR_LEN + sizeof(lifetime); + pos = buf = os_malloc(buf_len); + if (buf == NULL) + return; + + /* Peer RSN IE */ + os_memcpy(buf, kde->rsn_ie, kde->rsn_ie_len); + pos = buf + kde->rsn_ie_len; + + /* Peer MAC Address */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_MAC_ADDR, peer, ETH_ALEN, NULL, 0); + + /* PNonce */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_NONCE, key->key_nonce, + WPA_NONCE_LEN, NULL, 0); + + /* SMK and INonce */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_SMK, smk, PMK_LEN, + kde->nonce, WPA_NONCE_LEN); + + /* Lifetime */ + lifetime = htonl(43200); /* dot11RSNAConfigSMKLifetime */ + pos = wpa_add_kde(pos, RSN_KEY_DATA_LIFETIME, + (u8 *) &lifetime, sizeof(lifetime), NULL, 0); + + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "Sending SMK M5"); + + __wpa_send_eapol(wpa_auth, sm, + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_SMK_MESSAGE, + NULL, kde->nonce, buf, pos - buf, 0, 1, 0); + + os_free(buf); +} + + +void wpa_smk_m3(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, struct wpa_eapol_key *key) +{ + struct wpa_eapol_ie_parse kde; + struct wpa_stsl_search search; + u8 smk[32], buf[ETH_ALEN + 8 + 2 * WPA_NONCE_LEN], *pos; + + if (wpa_parse_kde_ies((const u8 *) (key + 1), + WPA_GET_BE16(key->key_data_length), &kde) < 0) { + wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK M3"); + return; + } + + if (kde.rsn_ie == NULL || + kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN || + kde.nonce == NULL || kde.nonce_len < WPA_NONCE_LEN) { + wpa_printf(MSG_INFO, "RSN: No RSN IE, MAC address KDE, or " + "Nonce KDE in SMK M3"); + return; + } + + /* Peer = sm->addr; Initiator = kde.mac_addr; + * Peer Nonce = key->key_nonce; Initiator Nonce = kde.nonce */ + + search.addr = kde.mac_addr; + search.sm = NULL; + if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == + 0 || search.sm == NULL) { + wpa_printf(MSG_DEBUG, "RSN: SMK handshake with " MACSTR + " aborted - STA not associated anymore", + MAC2STR(kde.mac_addr)); + wpa_smk_send_error(wpa_auth, sm, kde.mac_addr, STK_MUI_SMK, + STK_ERR_STA_NR); + /* FIX: wpa_stsl_remove(wpa_auth, neg); */ + return; + } + + if (os_get_random(smk, PMK_LEN)) { + wpa_printf(MSG_DEBUG, "RSN: Failed to generate SMK"); + return; + } + + /* SMK = PRF-256(Random number, "SMK Derivation", + * AA || Time || INonce || PNonce) + */ + os_memcpy(buf, wpa_auth->addr, ETH_ALEN); + pos = buf + ETH_ALEN; + wpa_get_ntp_timestamp(pos); + pos += 8; + os_memcpy(pos, kde.nonce, WPA_NONCE_LEN); + pos += WPA_NONCE_LEN; + os_memcpy(pos, key->key_nonce, WPA_NONCE_LEN); +#ifdef CONFIG_IEEE80211W + sha256_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf), + smk, PMK_LEN); +#else /* CONFIG_IEEE80211W */ + sha1_prf(smk, PMK_LEN, "SMK Derivation", buf, sizeof(buf), + smk, PMK_LEN); +#endif /* CONFIG_IEEE80211W */ + + wpa_hexdump_key(MSG_DEBUG, "RSN: SMK", smk, PMK_LEN); + + wpa_send_smk_m4(wpa_auth, sm, key, &kde, smk); + wpa_send_smk_m5(wpa_auth, search.sm, key, &kde, smk, sm->addr); + + /* Authenticator does not need SMK anymore and it is required to forget + * it. */ + os_memset(smk, 0, sizeof(*smk)); +} + + +void wpa_smk_error(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, struct wpa_eapol_key *key) +{ + struct wpa_eapol_ie_parse kde; + struct wpa_stsl_search search; + struct rsn_error_kde error; + u16 mui, error_type; + + if (wpa_parse_kde_ies((const u8 *) (key + 1), + WPA_GET_BE16(key->key_data_length), &kde) < 0) { + wpa_printf(MSG_INFO, "RSN: Failed to parse KDEs in SMK Error"); + return; + } + + if (kde.mac_addr == NULL || kde.mac_addr_len < ETH_ALEN || + kde.error == NULL || kde.error_len < sizeof(error)) { + wpa_printf(MSG_INFO, "RSN: No MAC address or Error KDE in " + "SMK Error"); + return; + } + + search.addr = kde.mac_addr; + search.sm = NULL; + if (wpa_auth_for_each_sta(wpa_auth, wpa_stsl_select_sta, &search) == + 0 || search.sm == NULL) { + wpa_printf(MSG_DEBUG, "RSN: Peer STA " MACSTR " not " + "associated for SMK Error message from " MACSTR, + MAC2STR(kde.mac_addr), MAC2STR(sm->addr)); + return; + } + + os_memcpy(&error, kde.error, sizeof(error)); + mui = be_to_host16(error.mui); + error_type = be_to_host16(error.error_type); + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "STA reported SMK Error: Peer " MACSTR + " MUI %d Error Type %d", + MAC2STR(kde.mac_addr), mui, error_type); + + wpa_smk_send_error(wpa_auth, search.sm, sm->addr, mui, error_type); +} + + +int wpa_stsl_remove(struct wpa_authenticator *wpa_auth, + struct wpa_stsl_negotiation *neg) +{ + struct wpa_stsl_negotiation *pos, *prev; + + if (wpa_auth == NULL) + return -1; + pos = wpa_auth->stsl_negotiations; + prev = NULL; + while (pos) { + if (pos == neg) { + if (prev) + prev->next = pos->next; + else + wpa_auth->stsl_negotiations = pos->next; + + eloop_cancel_timeout(wpa_stsl_step, wpa_auth, pos); + os_free(pos); + return 0; + } + prev = pos; + pos = pos->next; + } + + return -1; +} + +#endif /* CONFIG_PEERKEY */ diff --git a/contrib/wpa/src/ap/pmksa_cache_auth.c b/contrib/wpa/src/ap/pmksa_cache_auth.c new file mode 100644 index 0000000..22f44b7 --- /dev/null +++ b/contrib/wpa/src/ap/pmksa_cache_auth.c @@ -0,0 +1,425 @@ +/* + * hostapd - PMKSA cache for IEEE 802.11i RSN + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "eapol_auth/eapol_auth_sm.h" +#include "eapol_auth/eapol_auth_sm_i.h" +#include "sta_info.h" +#include "ap_config.h" +#include "pmksa_cache_auth.h" + + +static const int pmksa_cache_max_entries = 1024; +static const int dot11RSNAConfigPMKLifetime = 43200; + +struct rsn_pmksa_cache { +#define PMKID_HASH_SIZE 128 +#define PMKID_HASH(pmkid) (unsigned int) ((pmkid)[0] & 0x7f) + struct rsn_pmksa_cache_entry *pmkid[PMKID_HASH_SIZE]; + struct rsn_pmksa_cache_entry *pmksa; + int pmksa_count; + + void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx); + void *ctx; +}; + + +static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa); + + +static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry) +{ + if (entry == NULL) + return; + os_free(entry->identity); +#ifndef CONFIG_NO_RADIUS + radius_free_class(&entry->radius_class); +#endif /* CONFIG_NO_RADIUS */ + os_free(entry); +} + + +static void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry) +{ + struct rsn_pmksa_cache_entry *pos, *prev; + + pmksa->pmksa_count--; + pmksa->free_cb(entry, pmksa->ctx); + pos = pmksa->pmkid[PMKID_HASH(entry->pmkid)]; + prev = NULL; + while (pos) { + if (pos == entry) { + if (prev != NULL) { + prev->hnext = pos->hnext; + } else { + pmksa->pmkid[PMKID_HASH(entry->pmkid)] = + pos->hnext; + } + break; + } + prev = pos; + pos = pos->hnext; + } + + pos = pmksa->pmksa; + prev = NULL; + while (pos) { + if (pos == entry) { + if (prev != NULL) + prev->next = pos->next; + else + pmksa->pmksa = pos->next; + break; + } + prev = pos; + pos = pos->next; + } + _pmksa_cache_free_entry(entry); +} + + +static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx) +{ + struct rsn_pmksa_cache *pmksa = eloop_ctx; + struct os_time now; + + os_get_time(&now); + while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) { + struct rsn_pmksa_cache_entry *entry = pmksa->pmksa; + pmksa->pmksa = entry->next; + wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for " + MACSTR, MAC2STR(entry->spa)); + pmksa_cache_free_entry(pmksa, entry); + } + + pmksa_cache_set_expiration(pmksa); +} + + +static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa) +{ + int sec; + struct os_time now; + + eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL); + if (pmksa->pmksa == NULL) + return; + os_get_time(&now); + sec = pmksa->pmksa->expiration - now.sec; + if (sec < 0) + sec = 0; + eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL); +} + + +static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry, + struct eapol_state_machine *eapol) +{ + if (eapol == NULL) + return; + + if (eapol->identity) { + entry->identity = os_malloc(eapol->identity_len); + if (entry->identity) { + entry->identity_len = eapol->identity_len; + os_memcpy(entry->identity, eapol->identity, + eapol->identity_len); + } + } + +#ifndef CONFIG_NO_RADIUS + radius_copy_class(&entry->radius_class, &eapol->radius_class); +#endif /* CONFIG_NO_RADIUS */ + + entry->eap_type_authsrv = eapol->eap_type_authsrv; + entry->vlan_id = ((struct sta_info *) eapol->sta)->vlan_id; +} + + +void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry, + struct eapol_state_machine *eapol) +{ + if (entry == NULL || eapol == NULL) + return; + + if (entry->identity) { + os_free(eapol->identity); + eapol->identity = os_malloc(entry->identity_len); + if (eapol->identity) { + eapol->identity_len = entry->identity_len; + os_memcpy(eapol->identity, entry->identity, + entry->identity_len); + } + wpa_hexdump_ascii(MSG_DEBUG, "STA identity from PMKSA", + eapol->identity, eapol->identity_len); + } + +#ifndef CONFIG_NO_RADIUS + radius_free_class(&eapol->radius_class); + radius_copy_class(&eapol->radius_class, &entry->radius_class); +#endif /* CONFIG_NO_RADIUS */ + if (eapol->radius_class.attr) { + wpa_printf(MSG_DEBUG, "Copied %lu Class attribute(s) from " + "PMKSA", (unsigned long) eapol->radius_class.count); + } + + eapol->eap_type_authsrv = entry->eap_type_authsrv; + ((struct sta_info *) eapol->sta)->vlan_id = entry->vlan_id; +} + + +static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa, + struct rsn_pmksa_cache_entry *entry) +{ + struct rsn_pmksa_cache_entry *pos, *prev; + + /* Add the new entry; order by expiration time */ + pos = pmksa->pmksa; + prev = NULL; + while (pos) { + if (pos->expiration > entry->expiration) + break; + prev = pos; + pos = pos->next; + } + if (prev == NULL) { + entry->next = pmksa->pmksa; + pmksa->pmksa = entry; + } else { + entry->next = prev->next; + prev->next = entry; + } + entry->hnext = pmksa->pmkid[PMKID_HASH(entry->pmkid)]; + pmksa->pmkid[PMKID_HASH(entry->pmkid)] = entry; + + pmksa->pmksa_count++; + wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR, + MAC2STR(entry->spa)); + wpa_hexdump(MSG_DEBUG, "RSN: added PMKID", entry->pmkid, PMKID_LEN); +} + + +/** + * pmksa_cache_auth_add - Add a PMKSA cache entry + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() + * @pmk: The new pairwise master key + * @pmk_len: PMK length in bytes, usually PMK_LEN (32) + * @aa: Authenticator address + * @spa: Supplicant address + * @session_timeout: Session timeout + * @eapol: Pointer to EAPOL state machine data + * @akmp: WPA_KEY_MGMT_* used in key derivation + * Returns: Pointer to the added PMKSA cache entry or %NULL on error + * + * This function create a PMKSA entry for a new PMK and adds it to the PMKSA + * cache. If an old entry is already in the cache for the same Supplicant, + * this entry will be replaced with the new entry. PMKID will be calculated + * based on the PMK. + */ +struct rsn_pmksa_cache_entry * +pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa, + const u8 *pmk, size_t pmk_len, + const u8 *aa, const u8 *spa, int session_timeout, + struct eapol_state_machine *eapol, int akmp) +{ + struct rsn_pmksa_cache_entry *entry, *pos; + struct os_time now; + + if (pmk_len > PMK_LEN) + return NULL; + + entry = os_zalloc(sizeof(*entry)); + if (entry == NULL) + return NULL; + os_memcpy(entry->pmk, pmk, pmk_len); + entry->pmk_len = pmk_len; + rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, + wpa_key_mgmt_sha256(akmp)); + os_get_time(&now); + entry->expiration = now.sec; + if (session_timeout > 0) + entry->expiration += session_timeout; + else + entry->expiration += dot11RSNAConfigPMKLifetime; + entry->akmp = akmp; + os_memcpy(entry->spa, spa, ETH_ALEN); + pmksa_cache_from_eapol_data(entry, eapol); + + /* Replace an old entry for the same STA (if found) with the new entry + */ + pos = pmksa_cache_auth_get(pmksa, spa, NULL); + if (pos) + pmksa_cache_free_entry(pmksa, pos); + + if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) { + /* Remove the oldest entry to make room for the new entry */ + wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache " + "entry (for " MACSTR ") to make room for new one", + MAC2STR(pmksa->pmksa->spa)); + pmksa_cache_free_entry(pmksa, pmksa->pmksa); + } + + pmksa_cache_link_entry(pmksa, entry); + + return entry; +} + + +struct rsn_pmksa_cache_entry * +pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa, + const struct rsn_pmksa_cache_entry *old_entry, + const u8 *aa, const u8 *pmkid) +{ + struct rsn_pmksa_cache_entry *entry; + + entry = os_zalloc(sizeof(*entry)); + if (entry == NULL) + return NULL; + os_memcpy(entry->pmkid, pmkid, PMKID_LEN); + os_memcpy(entry->pmk, old_entry->pmk, old_entry->pmk_len); + entry->pmk_len = old_entry->pmk_len; + entry->expiration = old_entry->expiration; + entry->akmp = old_entry->akmp; + os_memcpy(entry->spa, old_entry->spa, ETH_ALEN); + entry->opportunistic = 1; + if (old_entry->identity) { + entry->identity = os_malloc(old_entry->identity_len); + if (entry->identity) { + entry->identity_len = old_entry->identity_len; + os_memcpy(entry->identity, old_entry->identity, + old_entry->identity_len); + } + } +#ifndef CONFIG_NO_RADIUS + radius_copy_class(&entry->radius_class, &old_entry->radius_class); +#endif /* CONFIG_NO_RADIUS */ + entry->eap_type_authsrv = old_entry->eap_type_authsrv; + entry->vlan_id = old_entry->vlan_id; + entry->opportunistic = 1; + + pmksa_cache_link_entry(pmksa, entry); + + return entry; +} + + +/** + * pmksa_cache_auth_deinit - Free all entries in PMKSA cache + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() + */ +void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa) +{ + struct rsn_pmksa_cache_entry *entry, *prev; + int i; + + if (pmksa == NULL) + return; + + entry = pmksa->pmksa; + while (entry) { + prev = entry; + entry = entry->next; + _pmksa_cache_free_entry(prev); + } + eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL); + for (i = 0; i < PMKID_HASH_SIZE; i++) + pmksa->pmkid[i] = NULL; + os_free(pmksa); +} + + +/** + * pmksa_cache_auth_get - Fetch a PMKSA cache entry + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() + * @spa: Supplicant address or %NULL to match any + * @pmkid: PMKID or %NULL to match any + * Returns: Pointer to PMKSA cache entry or %NULL if no match was found + */ +struct rsn_pmksa_cache_entry * +pmksa_cache_auth_get(struct rsn_pmksa_cache *pmksa, + const u8 *spa, const u8 *pmkid) +{ + struct rsn_pmksa_cache_entry *entry; + + if (pmkid) + entry = pmksa->pmkid[PMKID_HASH(pmkid)]; + else + entry = pmksa->pmksa; + while (entry) { + if ((spa == NULL || + os_memcmp(entry->spa, spa, ETH_ALEN) == 0) && + (pmkid == NULL || + os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)) + return entry; + entry = pmkid ? entry->hnext : entry->next; + } + return NULL; +} + + +/** + * pmksa_cache_get_okc - Fetch a PMKSA cache entry using OKC + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init() + * @aa: Authenticator address + * @spa: Supplicant address + * @pmkid: PMKID + * Returns: Pointer to PMKSA cache entry or %NULL if no match was found + * + * Use opportunistic key caching (OKC) to find a PMK for a supplicant. + */ +struct rsn_pmksa_cache_entry * pmksa_cache_get_okc( + struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *spa, + const u8 *pmkid) +{ + struct rsn_pmksa_cache_entry *entry; + u8 new_pmkid[PMKID_LEN]; + + entry = pmksa->pmksa; + while (entry) { + if (os_memcmp(entry->spa, spa, ETH_ALEN) != 0) + continue; + rsn_pmkid(entry->pmk, entry->pmk_len, aa, spa, new_pmkid, + wpa_key_mgmt_sha256(entry->akmp)); + if (os_memcmp(new_pmkid, pmkid, PMKID_LEN) == 0) + return entry; + entry = entry->next; + } + return NULL; +} + + +/** + * pmksa_cache_auth_init - Initialize PMKSA cache + * @free_cb: Callback function to be called when a PMKSA cache entry is freed + * @ctx: Context pointer for free_cb function + * Returns: Pointer to PMKSA cache data or %NULL on failure + */ +struct rsn_pmksa_cache * +pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, + void *ctx), void *ctx) +{ + struct rsn_pmksa_cache *pmksa; + + pmksa = os_zalloc(sizeof(*pmksa)); + if (pmksa) { + pmksa->free_cb = free_cb; + pmksa->ctx = ctx; + } + + return pmksa; +} diff --git a/contrib/wpa/src/ap/pmksa_cache_auth.h b/contrib/wpa/src/ap/pmksa_cache_auth.h new file mode 100644 index 0000000..9628b13 --- /dev/null +++ b/contrib/wpa/src/ap/pmksa_cache_auth.h @@ -0,0 +1,64 @@ +/* + * hostapd - PMKSA cache for IEEE 802.11i RSN + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef PMKSA_CACHE_H +#define PMKSA_CACHE_H + +#include "radius/radius.h" + +/** + * struct rsn_pmksa_cache_entry - PMKSA cache entry + */ +struct rsn_pmksa_cache_entry { + struct rsn_pmksa_cache_entry *next, *hnext; + u8 pmkid[PMKID_LEN]; + u8 pmk[PMK_LEN]; + size_t pmk_len; + os_time_t expiration; + int akmp; /* WPA_KEY_MGMT_* */ + u8 spa[ETH_ALEN]; + + u8 *identity; + size_t identity_len; + struct radius_class_data radius_class; + u8 eap_type_authsrv; + int vlan_id; + int opportunistic; +}; + +struct rsn_pmksa_cache; + +struct rsn_pmksa_cache * +pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, + void *ctx), void *ctx); +void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa); +struct rsn_pmksa_cache_entry * +pmksa_cache_auth_get(struct rsn_pmksa_cache *pmksa, + const u8 *spa, const u8 *pmkid); +struct rsn_pmksa_cache_entry * pmksa_cache_get_okc( + struct rsn_pmksa_cache *pmksa, const u8 *spa, const u8 *aa, + const u8 *pmkid); +struct rsn_pmksa_cache_entry * +pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa, + const u8 *pmk, size_t pmk_len, + const u8 *aa, const u8 *spa, int session_timeout, + struct eapol_state_machine *eapol, int akmp); +struct rsn_pmksa_cache_entry * +pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa, + const struct rsn_pmksa_cache_entry *old_entry, + const u8 *aa, const u8 *pmkid); +void pmksa_cache_to_eapol_data(struct rsn_pmksa_cache_entry *entry, + struct eapol_state_machine *eapol); + +#endif /* PMKSA_CACHE_H */ diff --git a/contrib/wpa/src/ap/preauth_auth.c b/contrib/wpa/src/ap/preauth_auth.c new file mode 100644 index 0000000..8e13315 --- /dev/null +++ b/contrib/wpa/src/ap/preauth_auth.c @@ -0,0 +1,279 @@ +/* + * hostapd - Authenticator for IEEE 802.11i RSN pre-authentication + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "utils/includes.h" + +#ifdef CONFIG_RSN_PREAUTH + +#include "utils/common.h" +#include "utils/eloop.h" +#include "l2_packet/l2_packet.h" +#include "common/wpa_common.h" +#include "eapol_auth/eapol_auth_sm.h" +#include "eapol_auth/eapol_auth_sm_i.h" +#include "hostapd.h" +#include "ap_config.h" +#include "ieee802_1x.h" +#include "sta_info.h" +#include "wpa_auth.h" +#include "preauth_auth.h" + +#ifndef ETH_P_PREAUTH +#define ETH_P_PREAUTH 0x88C7 /* IEEE 802.11i pre-authentication */ +#endif /* ETH_P_PREAUTH */ + +static const int dot11RSNAConfigPMKLifetime = 43200; + +struct rsn_preauth_interface { + struct rsn_preauth_interface *next; + struct hostapd_data *hapd; + struct l2_packet_data *l2; + char *ifname; + int ifindex; +}; + + +static void rsn_preauth_receive(void *ctx, const u8 *src_addr, + const u8 *buf, size_t len) +{ + struct rsn_preauth_interface *piface = ctx; + struct hostapd_data *hapd = piface->hapd; + struct ieee802_1x_hdr *hdr; + struct sta_info *sta; + struct l2_ethhdr *ethhdr; + + wpa_printf(MSG_DEBUG, "RSN: receive pre-auth packet " + "from interface '%s'", piface->ifname); + if (len < sizeof(*ethhdr) + sizeof(*hdr)) { + wpa_printf(MSG_DEBUG, "RSN: too short pre-auth packet " + "(len=%lu)", (unsigned long) len); + return; + } + + ethhdr = (struct l2_ethhdr *) buf; + hdr = (struct ieee802_1x_hdr *) (ethhdr + 1); + + if (os_memcmp(ethhdr->h_dest, hapd->own_addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "RSN: pre-auth for foreign address " + MACSTR, MAC2STR(ethhdr->h_dest)); + return; + } + + sta = ap_get_sta(hapd, ethhdr->h_source); + if (sta && (sta->flags & WLAN_STA_ASSOC)) { + wpa_printf(MSG_DEBUG, "RSN: pre-auth for already association " + "STA " MACSTR, MAC2STR(sta->addr)); + return; + } + if (!sta && hdr->type == IEEE802_1X_TYPE_EAPOL_START) { + sta = ap_sta_add(hapd, ethhdr->h_source); + if (sta == NULL) + return; + sta->flags = WLAN_STA_PREAUTH; + + ieee802_1x_new_station(hapd, sta); + if (sta->eapol_sm == NULL) { + ap_free_sta(hapd, sta); + sta = NULL; + } else { + sta->eapol_sm->radius_identifier = -1; + sta->eapol_sm->portValid = TRUE; + sta->eapol_sm->flags |= EAPOL_SM_PREAUTH; + } + } + if (sta == NULL) + return; + sta->preauth_iface = piface; + ieee802_1x_receive(hapd, ethhdr->h_source, (u8 *) (ethhdr + 1), + len - sizeof(*ethhdr)); +} + + +static int rsn_preauth_iface_add(struct hostapd_data *hapd, const char *ifname) +{ + struct rsn_preauth_interface *piface; + + wpa_printf(MSG_DEBUG, "RSN pre-auth interface '%s'", ifname); + + piface = os_zalloc(sizeof(*piface)); + if (piface == NULL) + return -1; + piface->hapd = hapd; + + piface->ifname = os_strdup(ifname); + if (piface->ifname == NULL) { + goto fail1; + } + + piface->l2 = l2_packet_init(piface->ifname, NULL, ETH_P_PREAUTH, + rsn_preauth_receive, piface, 1); + if (piface->l2 == NULL) { + wpa_printf(MSG_ERROR, "Failed to open register layer 2 access " + "to ETH_P_PREAUTH"); + goto fail2; + } + + piface->next = hapd->preauth_iface; + hapd->preauth_iface = piface; + return 0; + +fail2: + os_free(piface->ifname); +fail1: + os_free(piface); + return -1; +} + + +void rsn_preauth_iface_deinit(struct hostapd_data *hapd) +{ + struct rsn_preauth_interface *piface, *prev; + + piface = hapd->preauth_iface; + hapd->preauth_iface = NULL; + while (piface) { + prev = piface; + piface = piface->next; + l2_packet_deinit(prev->l2); + os_free(prev->ifname); + os_free(prev); + } +} + + +int rsn_preauth_iface_init(struct hostapd_data *hapd) +{ + char *tmp, *start, *end; + + if (hapd->conf->rsn_preauth_interfaces == NULL) + return 0; + + tmp = os_strdup(hapd->conf->rsn_preauth_interfaces); + if (tmp == NULL) + return -1; + start = tmp; + for (;;) { + while (*start == ' ') + start++; + if (*start == '\0') + break; + end = os_strchr(start, ' '); + if (end) + *end = '\0'; + + if (rsn_preauth_iface_add(hapd, start)) { + rsn_preauth_iface_deinit(hapd); + os_free(tmp); + return -1; + } + + if (end) + start = end + 1; + else + break; + } + os_free(tmp); + return 0; +} + + +static void rsn_preauth_finished_cb(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + wpa_printf(MSG_DEBUG, "RSN: Removing pre-authentication STA entry for " + MACSTR, MAC2STR(sta->addr)); + ap_free_sta(hapd, sta); +} + + +void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta, + int success) +{ + const u8 *key; + size_t len; + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_INFO, "pre-authentication %s", + success ? "succeeded" : "failed"); + + key = ieee802_1x_get_key(sta->eapol_sm, &len); + if (len > PMK_LEN) + len = PMK_LEN; + if (success && key) { + if (wpa_auth_pmksa_add_preauth(hapd->wpa_auth, key, len, + sta->addr, + dot11RSNAConfigPMKLifetime, + sta->eapol_sm) == 0) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, + "added PMKSA cache entry (pre-auth)"); + } else { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_WPA, + HOSTAPD_LEVEL_DEBUG, + "failed to add PMKSA cache entry " + "(pre-auth)"); + } + } + + /* + * Finish STA entry removal from timeout in order to avoid freeing + * STA data before the caller has finished processing. + */ + eloop_register_timeout(0, 0, rsn_preauth_finished_cb, hapd, sta); +} + + +void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta, + u8 *buf, size_t len) +{ + struct rsn_preauth_interface *piface; + struct l2_ethhdr *ethhdr; + + piface = hapd->preauth_iface; + while (piface) { + if (piface == sta->preauth_iface) + break; + piface = piface->next; + } + + if (piface == NULL) { + wpa_printf(MSG_DEBUG, "RSN: Could not find pre-authentication " + "interface for " MACSTR, MAC2STR(sta->addr)); + return; + } + + ethhdr = os_malloc(sizeof(*ethhdr) + len); + if (ethhdr == NULL) + return; + + os_memcpy(ethhdr->h_dest, sta->addr, ETH_ALEN); + os_memcpy(ethhdr->h_source, hapd->own_addr, ETH_ALEN); + ethhdr->h_proto = host_to_be16(ETH_P_PREAUTH); + os_memcpy(ethhdr + 1, buf, len); + + if (l2_packet_send(piface->l2, sta->addr, ETH_P_PREAUTH, (u8 *) ethhdr, + sizeof(*ethhdr) + len) < 0) { + wpa_printf(MSG_ERROR, "Failed to send preauth packet using " + "l2_packet_send\n"); + } + os_free(ethhdr); +} + + +void rsn_preauth_free_station(struct hostapd_data *hapd, struct sta_info *sta) +{ + eloop_cancel_timeout(rsn_preauth_finished_cb, hapd, sta); +} + +#endif /* CONFIG_RSN_PREAUTH */ diff --git a/contrib/wpa/src/ap/preauth_auth.h b/contrib/wpa/src/ap/preauth_auth.h new file mode 100644 index 0000000..5348bee --- /dev/null +++ b/contrib/wpa/src/ap/preauth_auth.h @@ -0,0 +1,58 @@ +/* + * hostapd - Authenticator for IEEE 802.11i RSN pre-authentication + * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef PREAUTH_H +#define PREAUTH_H + +#ifdef CONFIG_RSN_PREAUTH + +int rsn_preauth_iface_init(struct hostapd_data *hapd); +void rsn_preauth_iface_deinit(struct hostapd_data *hapd); +void rsn_preauth_finished(struct hostapd_data *hapd, struct sta_info *sta, + int success); +void rsn_preauth_send(struct hostapd_data *hapd, struct sta_info *sta, + u8 *buf, size_t len); +void rsn_preauth_free_station(struct hostapd_data *hapd, struct sta_info *sta); + +#else /* CONFIG_RSN_PREAUTH */ + +static inline int rsn_preauth_iface_init(struct hostapd_data *hapd) +{ + return 0; +} + +static inline void rsn_preauth_iface_deinit(struct hostapd_data *hapd) +{ +} + +static inline void rsn_preauth_finished(struct hostapd_data *hapd, + struct sta_info *sta, + int success) +{ +} + +static inline void rsn_preauth_send(struct hostapd_data *hapd, + struct sta_info *sta, + u8 *buf, size_t len) +{ +} + +static inline void rsn_preauth_free_station(struct hostapd_data *hapd, + struct sta_info *sta) +{ +} + +#endif /* CONFIG_RSN_PREAUTH */ + +#endif /* PREAUTH_H */ diff --git a/contrib/wpa/src/ap/sta_info.c b/contrib/wpa/src/ap/sta_info.c new file mode 100644 index 0000000..335c9a5 --- /dev/null +++ b/contrib/wpa/src/ap/sta_info.c @@ -0,0 +1,751 @@ +/* + * hostapd / Station table + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published 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 "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "common/ieee802_11_defs.h" +#include "radius/radius.h" +#include "radius/radius_client.h" +#include "drivers/driver.h" +#include "hostapd.h" +#include "accounting.h" +#include "ieee802_1x.h" +#include "ieee802_11.h" +#include "wpa_auth.h" +#include "preauth_auth.h" +#include "ap_config.h" +#include "beacon.h" +#include "ap_mlme.h" +#include "vlan_init.h" +#include "sta_info.h" + +static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd, + struct sta_info *sta); +static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx); +#ifdef CONFIG_IEEE80211W +static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx); +#endif /* CONFIG_IEEE80211W */ + +int ap_for_each_sta(struct hostapd_data *hapd, + int (*cb)(struct hostapd_data *hapd, struct sta_info *sta, + void *ctx), + void *ctx) +{ + struct sta_info *sta; + + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (cb(hapd, sta, ctx)) + return 1; + } + + return 0; +} + + +struct sta_info * ap_get_sta(struct hostapd_data *hapd, const u8 *sta) +{ + struct sta_info *s; + + s = hapd->sta_hash[STA_HASH(sta)]; + while (s != NULL && os_memcmp(s->addr, sta, 6) != 0) + s = s->hnext; + return s; +} + + +static void ap_sta_list_del(struct hostapd_data *hapd, struct sta_info *sta) +{ + struct sta_info *tmp; + + if (hapd->sta_list == sta) { + hapd->sta_list = sta->next; + return; + } + + tmp = hapd->sta_list; + while (tmp != NULL && tmp->next != sta) + tmp = tmp->next; + if (tmp == NULL) { + wpa_printf(MSG_DEBUG, "Could not remove STA " MACSTR " from " + "list.", MAC2STR(sta->addr)); + } else + tmp->next = sta->next; +} + + +void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta) +{ + sta->hnext = hapd->sta_hash[STA_HASH(sta->addr)]; + hapd->sta_hash[STA_HASH(sta->addr)] = sta; +} + + +static void ap_sta_hash_del(struct hostapd_data *hapd, struct sta_info *sta) +{ + struct sta_info *s; + + s = hapd->sta_hash[STA_HASH(sta->addr)]; + if (s == NULL) return; + if (os_memcmp(s->addr, sta->addr, 6) == 0) { + hapd->sta_hash[STA_HASH(sta->addr)] = s->hnext; + return; + } + + while (s->hnext != NULL && + os_memcmp(s->hnext->addr, sta->addr, ETH_ALEN) != 0) + s = s->hnext; + if (s->hnext != NULL) + s->hnext = s->hnext->hnext; + else + wpa_printf(MSG_DEBUG, "AP: could not remove STA " MACSTR + " from hash table", MAC2STR(sta->addr)); +} + + +void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta) +{ + int set_beacon = 0; + + accounting_sta_stop(hapd, sta); + + if (sta->flags & WLAN_STA_WDS) + hapd->drv.set_wds_sta(hapd, sta->addr, sta->aid, 0); + + if (!(sta->flags & WLAN_STA_PREAUTH)) + hapd->drv.sta_remove(hapd, sta->addr); + + ap_sta_hash_del(hapd, sta); + ap_sta_list_del(hapd, sta); + + if (sta->aid > 0) + hapd->sta_aid[(sta->aid - 1) / 32] &= + ~BIT((sta->aid - 1) % 32); + + hapd->num_sta--; + if (sta->nonerp_set) { + sta->nonerp_set = 0; + hapd->iface->num_sta_non_erp--; + if (hapd->iface->num_sta_non_erp == 0) + set_beacon++; + } + + if (sta->no_short_slot_time_set) { + sta->no_short_slot_time_set = 0; + hapd->iface->num_sta_no_short_slot_time--; + if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G + && hapd->iface->num_sta_no_short_slot_time == 0) + set_beacon++; + } + + if (sta->no_short_preamble_set) { + sta->no_short_preamble_set = 0; + hapd->iface->num_sta_no_short_preamble--; + if (hapd->iface->current_mode->mode == HOSTAPD_MODE_IEEE80211G + && hapd->iface->num_sta_no_short_preamble == 0) + set_beacon++; + } + + if (sta->no_ht_gf_set) { + sta->no_ht_gf_set = 0; + hapd->iface->num_sta_ht_no_gf--; + } + + if (sta->no_ht_set) { + sta->no_ht_set = 0; + hapd->iface->num_sta_no_ht--; + } + + if (sta->ht_20mhz_set) { + sta->ht_20mhz_set = 0; + hapd->iface->num_sta_ht_20mhz--; + } + +#if defined(NEED_AP_MLME) && defined(CONFIG_IEEE80211N) + if (hostapd_ht_operation_update(hapd->iface) > 0) + set_beacon++; +#endif /* NEED_AP_MLME && CONFIG_IEEE80211N */ + + if (set_beacon) + ieee802_11_set_beacons(hapd->iface); + + eloop_cancel_timeout(ap_handle_timer, hapd, sta); + eloop_cancel_timeout(ap_handle_session_timer, hapd, sta); + + ieee802_1x_free_station(sta); + wpa_auth_sta_deinit(sta->wpa_sm); + rsn_preauth_free_station(hapd, sta); +#ifndef CONFIG_NO_RADIUS + radius_client_flush_auth(hapd->radius, sta->addr); +#endif /* CONFIG_NO_RADIUS */ + + os_free(sta->last_assoc_req); + os_free(sta->challenge); + +#ifdef CONFIG_IEEE80211W + os_free(sta->sa_query_trans_id); + eloop_cancel_timeout(ap_sa_query_timer, hapd, sta); +#endif /* CONFIG_IEEE80211W */ + + wpabuf_free(sta->wps_ie); + + os_free(sta->ht_capabilities); + + os_free(sta); +} + + +void hostapd_free_stas(struct hostapd_data *hapd) +{ + struct sta_info *sta, *prev; + + sta = hapd->sta_list; + + while (sta) { + prev = sta; + if (sta->flags & WLAN_STA_AUTH) { + mlme_deauthenticate_indication( + hapd, sta, WLAN_REASON_UNSPECIFIED); + } + sta = sta->next; + wpa_printf(MSG_DEBUG, "Removing station " MACSTR, + MAC2STR(prev->addr)); + ap_free_sta(hapd, prev); + } +} + + +/** + * ap_handle_timer - Per STA timer handler + * @eloop_ctx: struct hostapd_data * + * @timeout_ctx: struct sta_info * + * + * This function is called to check station activity and to remove inactive + * stations. + */ +void ap_handle_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + unsigned long next_time = 0; + + if (sta->timeout_next == STA_REMOVE) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "deauthenticated due to " + "local deauth request"); + ap_free_sta(hapd, sta); + return; + } + + if ((sta->flags & WLAN_STA_ASSOC) && + (sta->timeout_next == STA_NULLFUNC || + sta->timeout_next == STA_DISASSOC)) { + int inactive_sec; + wpa_printf(MSG_DEBUG, "Checking STA " MACSTR " inactivity:", + MAC2STR(sta->addr)); + inactive_sec = hapd->drv.get_inact_sec(hapd, sta->addr); + if (inactive_sec == -1) { + wpa_printf(MSG_DEBUG, "Could not get station info " + "from kernel driver for " MACSTR ".", + MAC2STR(sta->addr)); + } else if (inactive_sec < hapd->conf->ap_max_inactivity && + sta->flags & WLAN_STA_ASSOC) { + /* station activity detected; reset timeout state */ + wpa_printf(MSG_DEBUG, " Station has been active"); + sta->timeout_next = STA_NULLFUNC; + next_time = hapd->conf->ap_max_inactivity - + inactive_sec; + } + } + + if ((sta->flags & WLAN_STA_ASSOC) && + sta->timeout_next == STA_DISASSOC && + !(sta->flags & WLAN_STA_PENDING_POLL)) { + wpa_printf(MSG_DEBUG, " Station has ACKed data poll"); + /* data nullfunc frame poll did not produce TX errors; assume + * station ACKed it */ + sta->timeout_next = STA_NULLFUNC; + next_time = hapd->conf->ap_max_inactivity; + } + + if (next_time) { + eloop_register_timeout(next_time, 0, ap_handle_timer, hapd, + sta); + return; + } + + if (sta->timeout_next == STA_NULLFUNC && + (sta->flags & WLAN_STA_ASSOC)) { +#ifndef CONFIG_NATIVE_WINDOWS + /* send data frame to poll STA and check whether this frame + * is ACKed */ + struct ieee80211_hdr hdr; + + wpa_printf(MSG_DEBUG, " Polling STA with data frame"); + sta->flags |= WLAN_STA_PENDING_POLL; + + os_memset(&hdr, 0, sizeof(hdr)); + if (hapd->driver && + os_strcmp(hapd->driver->name, "hostap") == 0) { + /* + * WLAN_FC_STYPE_NULLFUNC would be more appropriate, + * but it is apparently not retried so TX Exc events + * are not received for it. + */ + hdr.frame_control = + IEEE80211_FC(WLAN_FC_TYPE_DATA, + WLAN_FC_STYPE_DATA); + } else { + hdr.frame_control = + IEEE80211_FC(WLAN_FC_TYPE_DATA, + WLAN_FC_STYPE_NULLFUNC); + } + + hdr.frame_control |= host_to_le16(WLAN_FC_FROMDS); + os_memcpy(hdr.IEEE80211_DA_FROMDS, sta->addr, ETH_ALEN); + os_memcpy(hdr.IEEE80211_BSSID_FROMDS, hapd->own_addr, + ETH_ALEN); + os_memcpy(hdr.IEEE80211_SA_FROMDS, hapd->own_addr, ETH_ALEN); + + if (hapd->drv.send_mgmt_frame(hapd, &hdr, sizeof(hdr)) < 0) + perror("ap_handle_timer: send"); +#endif /* CONFIG_NATIVE_WINDOWS */ + } else if (sta->timeout_next != STA_REMOVE) { + int deauth = sta->timeout_next == STA_DEAUTH; + + wpa_printf(MSG_DEBUG, "Sending %s info to STA " MACSTR, + deauth ? "deauthentication" : "disassociation", + MAC2STR(sta->addr)); + + if (deauth) { + hapd->drv.sta_deauth(hapd, sta->addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); + } else { + hapd->drv.sta_disassoc( + hapd, sta->addr, + WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); + } + } + + switch (sta->timeout_next) { + case STA_NULLFUNC: + sta->timeout_next = STA_DISASSOC; + eloop_register_timeout(AP_DISASSOC_DELAY, 0, ap_handle_timer, + hapd, sta); + break; + case STA_DISASSOC: + sta->flags &= ~WLAN_STA_ASSOC; + ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); + if (!sta->acct_terminate_cause) + sta->acct_terminate_cause = + RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT; + accounting_sta_stop(hapd, sta); + ieee802_1x_free_station(sta); + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "disassociated due to " + "inactivity"); + sta->timeout_next = STA_DEAUTH; + eloop_register_timeout(AP_DEAUTH_DELAY, 0, ap_handle_timer, + hapd, sta); + mlme_disassociate_indication( + hapd, sta, WLAN_REASON_DISASSOC_DUE_TO_INACTIVITY); + break; + case STA_DEAUTH: + case STA_REMOVE: + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "deauthenticated due to " + "inactivity"); + if (!sta->acct_terminate_cause) + sta->acct_terminate_cause = + RADIUS_ACCT_TERMINATE_CAUSE_IDLE_TIMEOUT; + mlme_deauthenticate_indication( + hapd, sta, + WLAN_REASON_PREV_AUTH_NOT_VALID); + ap_free_sta(hapd, sta); + break; + } +} + + +static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + u8 addr[ETH_ALEN]; + + if (!(sta->flags & WLAN_STA_AUTH)) + return; + + mlme_deauthenticate_indication(hapd, sta, + WLAN_REASON_PREV_AUTH_NOT_VALID); + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "deauthenticated due to " + "session timeout"); + sta->acct_terminate_cause = + RADIUS_ACCT_TERMINATE_CAUSE_SESSION_TIMEOUT; + os_memcpy(addr, sta->addr, ETH_ALEN); + ap_free_sta(hapd, sta); + hapd->drv.sta_deauth(hapd, addr, WLAN_REASON_PREV_AUTH_NOT_VALID); +} + + +void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta, + u32 session_timeout) +{ + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "setting session timeout to %d " + "seconds", session_timeout); + eloop_cancel_timeout(ap_handle_session_timer, hapd, sta); + eloop_register_timeout(session_timeout, 0, ap_handle_session_timer, + hapd, sta); +} + + +void ap_sta_no_session_timeout(struct hostapd_data *hapd, struct sta_info *sta) +{ + eloop_cancel_timeout(ap_handle_session_timer, hapd, sta); +} + + +struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr) +{ + struct sta_info *sta; + + sta = ap_get_sta(hapd, addr); + if (sta) + return sta; + + wpa_printf(MSG_DEBUG, " New STA"); + if (hapd->num_sta >= hapd->conf->max_num_sta) { + /* FIX: might try to remove some old STAs first? */ + wpa_printf(MSG_DEBUG, "no more room for new STAs (%d/%d)", + hapd->num_sta, hapd->conf->max_num_sta); + return NULL; + } + + sta = os_zalloc(sizeof(struct sta_info)); + if (sta == NULL) { + wpa_printf(MSG_ERROR, "malloc failed"); + return NULL; + } + sta->acct_interim_interval = hapd->conf->acct_interim_interval; + + /* initialize STA info data */ + eloop_register_timeout(hapd->conf->ap_max_inactivity, 0, + ap_handle_timer, hapd, sta); + os_memcpy(sta->addr, addr, ETH_ALEN); + sta->next = hapd->sta_list; + hapd->sta_list = sta; + hapd->num_sta++; + ap_sta_hash_add(hapd, sta); + sta->ssid = &hapd->conf->ssid; + ap_sta_remove_in_other_bss(hapd, sta); + + return sta; +} + + +static int ap_sta_remove(struct hostapd_data *hapd, struct sta_info *sta) +{ + ieee802_1x_notify_port_enabled(sta->eapol_sm, 0); + + wpa_printf(MSG_DEBUG, "Removing STA " MACSTR " from kernel driver", + MAC2STR(sta->addr)); + if (hapd->drv.sta_remove(hapd, sta->addr) && + sta->flags & WLAN_STA_ASSOC) { + wpa_printf(MSG_DEBUG, "Could not remove station " MACSTR + " from kernel driver.", MAC2STR(sta->addr)); + return -1; + } + return 0; +} + + +static void ap_sta_remove_in_other_bss(struct hostapd_data *hapd, + struct sta_info *sta) +{ + struct hostapd_iface *iface = hapd->iface; + size_t i; + + for (i = 0; i < iface->num_bss; i++) { + struct hostapd_data *bss = iface->bss[i]; + struct sta_info *sta2; + /* bss should always be set during operation, but it may be + * NULL during reconfiguration. Assume the STA is not + * associated to another BSS in that case to avoid NULL pointer + * dereferences. */ + if (bss == hapd || bss == NULL) + continue; + sta2 = ap_get_sta(bss, sta->addr); + if (!sta2) + continue; + + ap_sta_disconnect(bss, sta2, sta2->addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); + } +} + + +void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta, + u16 reason) +{ + wpa_printf(MSG_DEBUG, "%s: disassociate STA " MACSTR, + hapd->conf->iface, MAC2STR(sta->addr)); + sta->flags &= ~WLAN_STA_ASSOC; + ap_sta_remove(hapd, sta); + sta->timeout_next = STA_DEAUTH; + eloop_cancel_timeout(ap_handle_timer, hapd, sta); + eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DISASSOC, 0, + ap_handle_timer, hapd, sta); + accounting_sta_stop(hapd, sta); + ieee802_1x_free_station(sta); + + mlme_disassociate_indication(hapd, sta, reason); +} + + +void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta, + u16 reason) +{ + wpa_printf(MSG_DEBUG, "%s: deauthenticate STA " MACSTR, + hapd->conf->iface, MAC2STR(sta->addr)); + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC); + ap_sta_remove(hapd, sta); + sta->timeout_next = STA_REMOVE; + eloop_cancel_timeout(ap_handle_timer, hapd, sta); + eloop_register_timeout(AP_MAX_INACTIVITY_AFTER_DEAUTH, 0, + ap_handle_timer, hapd, sta); + accounting_sta_stop(hapd, sta); + ieee802_1x_free_station(sta); + + mlme_deauthenticate_indication(hapd, sta, reason); +} + + +int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, + int old_vlanid) +{ +#ifndef CONFIG_NO_VLAN + const char *iface; + struct hostapd_vlan *vlan = NULL; + int ret; + + /* + * Do not proceed furthur if the vlan id remains same. We do not want + * duplicate dynamic vlan entries. + */ + if (sta->vlan_id == old_vlanid) + return 0; + + /* + * During 1x reauth, if the vlan id changes, then remove the old id and + * proceed furthur to add the new one. + */ + if (old_vlanid > 0) + vlan_remove_dynamic(hapd, old_vlanid); + + iface = hapd->conf->iface; + if (sta->ssid->vlan[0]) + iface = sta->ssid->vlan; + + if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED) + sta->vlan_id = 0; + else if (sta->vlan_id > 0) { + vlan = hapd->conf->vlan; + while (vlan) { + if (vlan->vlan_id == sta->vlan_id || + vlan->vlan_id == VLAN_ID_WILDCARD) { + iface = vlan->ifname; + break; + } + vlan = vlan->next; + } + } + + if (sta->vlan_id > 0 && vlan == NULL) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "could not find VLAN for " + "binding station to (vlan_id=%d)", + sta->vlan_id); + return -1; + } else if (sta->vlan_id > 0 && vlan->vlan_id == VLAN_ID_WILDCARD) { + vlan = vlan_add_dynamic(hapd, vlan, sta->vlan_id); + if (vlan == NULL) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "could not add " + "dynamic VLAN interface for vlan_id=%d", + sta->vlan_id); + return -1; + } + + iface = vlan->ifname; + if (vlan_setup_encryption_dyn(hapd, sta->ssid, iface) != 0) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "could not " + "configure encryption for dynamic VLAN " + "interface for vlan_id=%d", + sta->vlan_id); + } + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "added new dynamic VLAN " + "interface '%s'", iface); + } else if (vlan && vlan->vlan_id == sta->vlan_id) { + if (sta->vlan_id > 0) { + vlan->dynamic_vlan++; + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "updated existing " + "dynamic VLAN interface '%s'", iface); + } + + /* + * Update encryption configuration for statically generated + * VLAN interface. This is only used for static WEP + * configuration for the case where hostapd did not yet know + * which keys are to be used when the interface was added. + */ + if (vlan_setup_encryption_dyn(hapd, sta->ssid, iface) != 0) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "could not " + "configure encryption for VLAN " + "interface for vlan_id=%d", + sta->vlan_id); + } + } + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "binding station to interface " + "'%s'", iface); + + if (wpa_auth_sta_set_vlan(sta->wpa_sm, sta->vlan_id) < 0) + wpa_printf(MSG_INFO, "Failed to update VLAN-ID for WPA"); + + ret = hapd->drv.set_sta_vlan(iface, hapd, sta->addr, sta->vlan_id); + if (ret < 0) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, "could not bind the STA " + "entry to vlan_id=%d", sta->vlan_id); + } + return ret; +#else /* CONFIG_NO_VLAN */ + return 0; +#endif /* CONFIG_NO_VLAN */ +} + + +#ifdef CONFIG_IEEE80211W + +int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta) +{ + u32 tu; + struct os_time now, passed; + os_get_time(&now); + os_time_sub(&now, &sta->sa_query_start, &passed); + tu = (passed.sec * 1000000 + passed.usec) / 1024; + if (hapd->conf->assoc_sa_query_max_timeout < tu) { + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "association SA Query timed out"); + sta->sa_query_timed_out = 1; + os_free(sta->sa_query_trans_id); + sta->sa_query_trans_id = NULL; + sta->sa_query_count = 0; + eloop_cancel_timeout(ap_sa_query_timer, hapd, sta); + return 1; + } + + return 0; +} + + +static void ap_sa_query_timer(void *eloop_ctx, void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + struct sta_info *sta = timeout_ctx; + unsigned int timeout, sec, usec; + u8 *trans_id, *nbuf; + + if (sta->sa_query_count > 0 && + ap_check_sa_query_timeout(hapd, sta)) + return; + + nbuf = os_realloc(sta->sa_query_trans_id, + (sta->sa_query_count + 1) * WLAN_SA_QUERY_TR_ID_LEN); + if (nbuf == NULL) + return; + if (sta->sa_query_count == 0) { + /* Starting a new SA Query procedure */ + os_get_time(&sta->sa_query_start); + } + trans_id = nbuf + sta->sa_query_count * WLAN_SA_QUERY_TR_ID_LEN; + sta->sa_query_trans_id = nbuf; + sta->sa_query_count++; + + os_get_random(trans_id, WLAN_SA_QUERY_TR_ID_LEN); + + timeout = hapd->conf->assoc_sa_query_retry_timeout; + sec = ((timeout / 1000) * 1024) / 1000; + usec = (timeout % 1000) * 1024; + eloop_register_timeout(sec, usec, ap_sa_query_timer, hapd, sta); + + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "association SA Query attempt %d", sta->sa_query_count); + +#ifdef NEED_AP_MLME + ieee802_11_send_sa_query_req(hapd, sta->addr, trans_id); +#endif /* NEED_AP_MLME */ +} + + +void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta) +{ + ap_sa_query_timer(hapd, sta); +} + + +void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta) +{ + eloop_cancel_timeout(ap_sa_query_timer, hapd, sta); + os_free(sta->sa_query_trans_id); + sta->sa_query_trans_id = NULL; + sta->sa_query_count = 0; +} + +#endif /* CONFIG_IEEE80211W */ + + +void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *addr, u16 reason) +{ + + if (sta == NULL && addr) + sta = ap_get_sta(hapd, addr); + + if (addr) + hapd->drv.sta_deauth(hapd, addr, reason); + + if (sta == NULL) + return; + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | WLAN_STA_AUTHORIZED); + eloop_cancel_timeout(ap_handle_timer, hapd, sta); + eloop_register_timeout(0, 0, ap_handle_timer, hapd, sta); + sta->timeout_next = STA_REMOVE; +} diff --git a/contrib/wpa/src/ap/sta_info.h b/contrib/wpa/src/ap/sta_info.h new file mode 100644 index 0000000..55faa5a --- /dev/null +++ b/contrib/wpa/src/ap/sta_info.h @@ -0,0 +1,155 @@ +/* + * hostapd / Station table + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef STA_INFO_H +#define STA_INFO_H + +/* STA flags */ +#define WLAN_STA_AUTH BIT(0) +#define WLAN_STA_ASSOC BIT(1) +#define WLAN_STA_PS BIT(2) +#define WLAN_STA_TIM BIT(3) +#define WLAN_STA_PERM BIT(4) +#define WLAN_STA_AUTHORIZED BIT(5) +#define WLAN_STA_PENDING_POLL BIT(6) /* pending activity poll not ACKed */ +#define WLAN_STA_SHORT_PREAMBLE BIT(7) +#define WLAN_STA_PREAUTH BIT(8) +#define WLAN_STA_WMM BIT(9) +#define WLAN_STA_MFP BIT(10) +#define WLAN_STA_HT BIT(11) +#define WLAN_STA_WPS BIT(12) +#define WLAN_STA_MAYBE_WPS BIT(13) +#define WLAN_STA_WDS BIT(14) +#define WLAN_STA_NONERP BIT(31) + +/* Maximum number of supported rates (from both Supported Rates and Extended + * Supported Rates IEs). */ +#define WLAN_SUPP_RATES_MAX 32 + + +struct sta_info { + struct sta_info *next; /* next entry in sta list */ + struct sta_info *hnext; /* next entry in hash table list */ + u8 addr[6]; + u16 aid; /* STA's unique AID (1 .. 2007) or 0 if not yet assigned */ + u32 flags; /* Bitfield of WLAN_STA_* */ + u16 capability; + u16 listen_interval; /* or beacon_int for APs */ + u8 supported_rates[WLAN_SUPP_RATES_MAX]; + int supported_rates_len; + + unsigned int nonerp_set:1; + unsigned int no_short_slot_time_set:1; + unsigned int no_short_preamble_set:1; + unsigned int no_ht_gf_set:1; + unsigned int no_ht_set:1; + unsigned int ht_20mhz_set:1; + + u16 auth_alg; + u8 previous_ap[6]; + + enum { + STA_NULLFUNC = 0, STA_DISASSOC, STA_DEAUTH, STA_REMOVE + } timeout_next; + + /* IEEE 802.1X related data */ + struct eapol_state_machine *eapol_sm; + + /* IEEE 802.11f (IAPP) related data */ + struct ieee80211_mgmt *last_assoc_req; + + u32 acct_session_id_hi; + u32 acct_session_id_lo; + time_t acct_session_start; + int acct_session_started; + int acct_terminate_cause; /* Acct-Terminate-Cause */ + int acct_interim_interval; /* Acct-Interim-Interval */ + + unsigned long last_rx_bytes; + unsigned long last_tx_bytes; + u32 acct_input_gigawords; /* Acct-Input-Gigawords */ + u32 acct_output_gigawords; /* Acct-Output-Gigawords */ + + u8 *challenge; /* IEEE 802.11 Shared Key Authentication Challenge */ + + struct wpa_state_machine *wpa_sm; + struct rsn_preauth_interface *preauth_iface; + + struct hostapd_ssid *ssid; /* SSID selection based on (Re)AssocReq */ + struct hostapd_ssid *ssid_probe; /* SSID selection based on ProbeReq */ + + int vlan_id; + + struct ieee80211_ht_capabilities *ht_capabilities; + +#ifdef CONFIG_IEEE80211W + int sa_query_count; /* number of pending SA Query requests; + * 0 = no SA Query in progress */ + int sa_query_timed_out; + u8 *sa_query_trans_id; /* buffer of WLAN_SA_QUERY_TR_ID_LEN * + * sa_query_count octets of pending SA Query + * transaction identifiers */ + struct os_time sa_query_start; +#endif /* CONFIG_IEEE80211W */ + + struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */ +}; + + +/* Default value for maximum station inactivity. After AP_MAX_INACTIVITY has + * passed since last received frame from the station, a nullfunc data frame is + * sent to the station. If this frame is not acknowledged and no other frames + * have been received, the station will be disassociated after + * AP_DISASSOC_DELAY seconds. Similarily, the station will be deauthenticated + * after AP_DEAUTH_DELAY seconds has passed after disassociation. */ +#define AP_MAX_INACTIVITY (5 * 60) +#define AP_DISASSOC_DELAY (1) +#define AP_DEAUTH_DELAY (1) +/* Number of seconds to keep STA entry with Authenticated flag after it has + * been disassociated. */ +#define AP_MAX_INACTIVITY_AFTER_DISASSOC (1 * 30) +/* Number of seconds to keep STA entry after it has been deauthenticated. */ +#define AP_MAX_INACTIVITY_AFTER_DEAUTH (1 * 5) + + +struct hostapd_data; + +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(struct hostapd_data *hapd, const u8 *sta); +void ap_sta_hash_add(struct hostapd_data *hapd, struct sta_info *sta); +void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta); +void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta); +void hostapd_free_stas(struct hostapd_data *hapd); +void ap_handle_timer(void *eloop_ctx, void *timeout_ctx); +void ap_sta_session_timeout(struct hostapd_data *hapd, struct sta_info *sta, + u32 session_timeout); +void ap_sta_no_session_timeout(struct hostapd_data *hapd, + struct sta_info *sta); +struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr); +void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta, + u16 reason); +void ap_sta_deauthenticate(struct hostapd_data *hapd, struct sta_info *sta, + u16 reason); +int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta, + int old_vlanid); +void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta); +void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta); +int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta); +void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *addr, u16 reason); + +#endif /* STA_INFO_H */ diff --git a/contrib/wpa/src/ap/tkip_countermeasures.c b/contrib/wpa/src/ap/tkip_countermeasures.c new file mode 100644 index 0000000..9690348 --- /dev/null +++ b/contrib/wpa/src/ap/tkip_countermeasures.c @@ -0,0 +1,93 @@ +/* + * hostapd / TKIP countermeasures + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published 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 "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "common/ieee802_11_defs.h" +#include "hostapd.h" +#include "sta_info.h" +#include "ap_mlme.h" +#include "wpa_auth.h" +#include "tkip_countermeasures.h" + + +static void ieee80211_tkip_countermeasures_stop(void *eloop_ctx, + void *timeout_ctx) +{ + struct hostapd_data *hapd = eloop_ctx; + hapd->tkip_countermeasures = 0; + hapd->drv.set_countermeasures(hapd, 0); + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "TKIP countermeasures ended"); +} + + +static void ieee80211_tkip_countermeasures_start(struct hostapd_data *hapd) +{ + struct sta_info *sta; + + hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, "TKIP countermeasures initiated"); + + wpa_auth_countermeasures_start(hapd->wpa_auth); + hapd->tkip_countermeasures = 1; + hapd->drv.set_countermeasures(hapd, 1); + wpa_gtk_rekey(hapd->wpa_auth); + eloop_cancel_timeout(ieee80211_tkip_countermeasures_stop, hapd, NULL); + eloop_register_timeout(60, 0, ieee80211_tkip_countermeasures_stop, + hapd, NULL); + for (sta = hapd->sta_list; sta != NULL; sta = sta->next) { + hapd->drv.sta_deauth(hapd, sta->addr, + WLAN_REASON_MICHAEL_MIC_FAILURE); + sta->flags &= ~(WLAN_STA_AUTH | WLAN_STA_ASSOC | + WLAN_STA_AUTHORIZED); + hapd->drv.sta_remove(hapd, sta->addr); + } +} + + +void michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local) +{ + time_t now; + + if (addr && local) { + struct sta_info *sta = ap_get_sta(hapd, addr); + if (sta != NULL) { + wpa_auth_sta_local_mic_failure_report(sta->wpa_sm); + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_INFO, + "Michael MIC failure detected in " + "received frame"); + mlme_michaelmicfailure_indication(hapd, addr); + } else { + wpa_printf(MSG_DEBUG, + "MLME-MICHAELMICFAILURE.indication " + "for not associated STA (" MACSTR + ") ignored", MAC2STR(addr)); + return; + } + } + + time(&now); + if (now > hapd->michael_mic_failure + 60) { + hapd->michael_mic_failures = 1; + } else { + hapd->michael_mic_failures++; + if (hapd->michael_mic_failures > 1) + ieee80211_tkip_countermeasures_start(hapd); + } + hapd->michael_mic_failure = now; +} diff --git a/contrib/wpa/src/ap/tkip_countermeasures.h b/contrib/wpa/src/ap/tkip_countermeasures.h new file mode 100644 index 0000000..5a1afce --- /dev/null +++ b/contrib/wpa/src/ap/tkip_countermeasures.h @@ -0,0 +1,20 @@ +/* + * hostapd / TKIP countermeasures + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published 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 TKIP_COUNTERMEASURES_H +#define TKIP_COUNTERMEASURES_H + +void michael_mic_failure(struct hostapd_data *hapd, const u8 *addr, int local); + +#endif /* TKIP_COUNTERMEASURES_H */ diff --git a/contrib/wpa/src/ap/utils.c b/contrib/wpa/src/ap/utils.c new file mode 100644 index 0000000..0ff48ae --- /dev/null +++ b/contrib/wpa/src/ap/utils.c @@ -0,0 +1,88 @@ +/* + * AP mode helper functions + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "common/ieee802_11_defs.h" +#include "sta_info.h" +#include "hostapd.h" + + +int hostapd_register_probereq_cb(struct hostapd_data *hapd, + int (*cb)(void *ctx, const u8 *sa, + const u8 *ie, size_t ie_len), + void *ctx) +{ + struct hostapd_probereq_cb *n; + + n = os_realloc(hapd->probereq_cb, (hapd->num_probereq_cb + 1) * + sizeof(struct hostapd_probereq_cb)); + if (n == NULL) + return -1; + + hapd->probereq_cb = n; + n = &hapd->probereq_cb[hapd->num_probereq_cb]; + hapd->num_probereq_cb++; + + n->cb = cb; + n->ctx = ctx; + + return 0; +} + + +struct prune_data { + struct hostapd_data *hapd; + const u8 *addr; +}; + +static int prune_associations(struct hostapd_iface *iface, void *ctx) +{ + struct prune_data *data = ctx; + struct sta_info *osta; + struct hostapd_data *ohapd; + size_t j; + + for (j = 0; j < iface->num_bss; j++) { + ohapd = iface->bss[j]; + if (ohapd == data->hapd) + continue; + osta = ap_get_sta(ohapd, data->addr); + if (!osta) + continue; + + ap_sta_disassociate(ohapd, osta, WLAN_REASON_UNSPECIFIED); + } + + return 0; +} + +/** + * hostapd_prune_associations - Remove extraneous associations + * @hapd: Pointer to BSS data for the most recent association + * @addr: Associated STA address + * + * This function looks through all radios and BSS's for previous + * (stale) associations of STA. If any are found they are removed. + */ +void hostapd_prune_associations(struct hostapd_data *hapd, const u8 *addr) +{ + struct prune_data data; + data.hapd = hapd; + data.addr = addr; + if (hapd->iface->for_each_interface) + hapd->iface->for_each_interface(hapd->iface->interfaces, + prune_associations, &data); +} diff --git a/contrib/wpa/src/ap/vlan_init.c b/contrib/wpa/src/ap/vlan_init.c new file mode 100644 index 0000000..c9d166a --- /dev/null +++ b/contrib/wpa/src/ap/vlan_init.c @@ -0,0 +1,903 @@ +/* + * hostapd / VLAN initialization + * Copyright 2003, Instant802 Networks, Inc. + * Copyright 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "hostapd.h" +#include "ap_config.h" +#include "vlan_init.h" + + +#ifdef CONFIG_FULL_DYNAMIC_VLAN + +#include <net/if.h> +#include <sys/ioctl.h> +#include <linux/sockios.h> +#include <linux/if_vlan.h> +#include <linux/if_bridge.h> + +#include "drivers/priv_netlink.h" +#include "utils/eloop.h" + + +struct full_dynamic_vlan { + int s; /* socket on which to listen for new/removed interfaces. */ +}; + + +static int ifconfig_helper(const char *if_name, int up) +{ + int fd; + struct ifreq ifr; + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, if_name, IFNAMSIZ); + + if (ioctl(fd, SIOCGIFFLAGS, &ifr) != 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCGIFFLAGS) failed " + "for interface %s: %s", + __func__, if_name, strerror(errno)); + close(fd); + return -1; + } + + if (up) + ifr.ifr_flags |= IFF_UP; + else + ifr.ifr_flags &= ~IFF_UP; + + if (ioctl(fd, SIOCSIFFLAGS, &ifr) != 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: ioctl(SIOCSIFFLAGS) failed " + "for interface %s (up=%d): %s", + __func__, if_name, up, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +static int ifconfig_up(const char *if_name) +{ + wpa_printf(MSG_DEBUG, "VLAN: Set interface %s up", if_name); + return ifconfig_helper(if_name, 1); +} + + +static int ifconfig_down(const char *if_name) +{ + wpa_printf(MSG_DEBUG, "VLAN: Set interface %s down", if_name); + return ifconfig_helper(if_name, 0); +} + + +/* + * These are only available in recent linux headers (without the leading + * underscore). + */ +#define _GET_VLAN_REALDEV_NAME_CMD 8 +#define _GET_VLAN_VID_CMD 9 + +/* This value should be 256 ONLY. If it is something else, then hostapd + * might crash!, as this value has been hard-coded in 2.4.x kernel + * bridging code. + */ +#define MAX_BR_PORTS 256 + +static int br_delif(const char *br_name, const char *if_name) +{ + int fd; + struct ifreq ifr; + unsigned long args[2]; + int if_index; + + wpa_printf(MSG_DEBUG, "VLAN: br_delif(%s, %s)", br_name, if_name); + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + if_index = if_nametoindex(if_name); + + if (if_index == 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining " + "interface index for '%s'", + __func__, if_name); + close(fd); + return -1; + } + + args[0] = BRCTL_DEL_IF; + args[1] = if_index; + + os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); + ifr.ifr_data = (__caddr_t) args; + + if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0 && errno != EINVAL) { + /* No error if interface already removed. */ + wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE," + "BRCTL_DEL_IF] failed for br_name=%s if_name=%s: " + "%s", __func__, br_name, if_name, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +/* + Add interface 'if_name' to the bridge 'br_name' + + returns -1 on error + returns 1 if the interface is already part of the bridge + returns 0 otherwise +*/ +static int br_addif(const char *br_name, const char *if_name) +{ + int fd; + struct ifreq ifr; + unsigned long args[2]; + int if_index; + + wpa_printf(MSG_DEBUG, "VLAN: br_addif(%s, %s)", br_name, if_name); + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + if_index = if_nametoindex(if_name); + + if (if_index == 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: Failure determining " + "interface index for '%s'", + __func__, if_name); + close(fd); + return -1; + } + + args[0] = BRCTL_ADD_IF; + args[1] = if_index; + + os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); + ifr.ifr_data = (__caddr_t) args; + + if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { + if (errno == EBUSY) { + /* The interface is already added. */ + close(fd); + return 1; + } + + wpa_printf(MSG_ERROR, "VLAN: %s: ioctl[SIOCDEVPRIVATE," + "BRCTL_ADD_IF] failed for br_name=%s if_name=%s: " + "%s", __func__, br_name, if_name, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +static int br_delbr(const char *br_name) +{ + int fd; + unsigned long arg[2]; + + wpa_printf(MSG_DEBUG, "VLAN: br_delbr(%s)", br_name); + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + arg[0] = BRCTL_DEL_BRIDGE; + arg[1] = (unsigned long) br_name; + + if (ioctl(fd, SIOCGIFBR, arg) < 0 && errno != ENXIO) { + /* No error if bridge already removed. */ + wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_DEL_BRIDGE failed for " + "%s: %s", __func__, br_name, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +/* + Add a bridge with the name 'br_name'. + + returns -1 on error + returns 1 if the bridge already exists + returns 0 otherwise +*/ +static int br_addbr(const char *br_name) +{ + int fd; + unsigned long arg[4]; + struct ifreq ifr; + + wpa_printf(MSG_DEBUG, "VLAN: br_addbr(%s)", br_name); + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + arg[0] = BRCTL_ADD_BRIDGE; + arg[1] = (unsigned long) br_name; + + if (ioctl(fd, SIOCGIFBR, arg) < 0) { + if (errno == EEXIST) { + /* The bridge is already added. */ + close(fd); + return 1; + } else { + wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_ADD_BRIDGE " + "failed for %s: %s", + __func__, br_name, strerror(errno)); + close(fd); + return -1; + } + } + + /* Decrease forwarding delay to avoid EAPOL timeouts. */ + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, br_name, IFNAMSIZ); + arg[0] = BRCTL_SET_BRIDGE_FORWARD_DELAY; + arg[1] = 1; + arg[2] = 0; + arg[3] = 0; + ifr.ifr_data = (char *) &arg; + if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: " + "BRCTL_SET_BRIDGE_FORWARD_DELAY (1 sec) failed for " + "%s: %s", __func__, br_name, strerror(errno)); + /* Continue anyway */ + } + + close(fd); + return 0; +} + + +static int br_getnumports(const char *br_name) +{ + int fd; + int i; + int port_cnt = 0; + unsigned long arg[4]; + int ifindices[MAX_BR_PORTS]; + struct ifreq ifr; + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + arg[0] = BRCTL_GET_PORT_LIST; + arg[1] = (unsigned long) ifindices; + arg[2] = MAX_BR_PORTS; + arg[3] = 0; + + os_memset(ifindices, 0, sizeof(ifindices)); + os_strlcpy(ifr.ifr_name, br_name, sizeof(ifr.ifr_name)); + ifr.ifr_data = (__caddr_t) arg; + + if (ioctl(fd, SIOCDEVPRIVATE, &ifr) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: BRCTL_GET_PORT_LIST " + "failed for %s: %s", + __func__, br_name, strerror(errno)); + close(fd); + return -1; + } + + for (i = 1; i < MAX_BR_PORTS; i++) { + if (ifindices[i] > 0) { + port_cnt++; + } + } + + close(fd); + return port_cnt; +} + + +static int vlan_rem(const char *if_name) +{ + int fd; + struct vlan_ioctl_args if_request; + + wpa_printf(MSG_DEBUG, "VLAN: vlan_rem(%s)", if_name); + if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) { + wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'", + if_name); + return -1; + } + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + os_memset(&if_request, 0, sizeof(if_request)); + + os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1)); + if_request.cmd = DEL_VLAN_CMD; + + if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: DEL_VLAN_CMD failed for %s: " + "%s", __func__, if_name, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +/* + Add a vlan interface with VLAN ID 'vid' and tagged interface + 'if_name'. + + returns -1 on error + returns 1 if the interface already exists + returns 0 otherwise +*/ +static int vlan_add(const char *if_name, int vid) +{ + int fd; + struct vlan_ioctl_args if_request; + + wpa_printf(MSG_DEBUG, "VLAN: vlan_add(if_name=%s, vid=%d)", + if_name, vid); + ifconfig_up(if_name); + + if ((os_strlen(if_name) + 1) > sizeof(if_request.device1)) { + wpa_printf(MSG_ERROR, "VLAN: Interface name too long: '%s'", + if_name); + return -1; + } + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + os_memset(&if_request, 0, sizeof(if_request)); + + /* Determine if a suitable vlan device already exists. */ + + os_snprintf(if_request.device1, sizeof(if_request.device1), "vlan%d", + vid); + + if_request.cmd = _GET_VLAN_VID_CMD; + + if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0) { + + if (if_request.u.VID == vid) { + if_request.cmd = _GET_VLAN_REALDEV_NAME_CMD; + + if (ioctl(fd, SIOCSIFVLAN, &if_request) == 0 && + os_strncmp(if_request.u.device2, if_name, + sizeof(if_request.u.device2)) == 0) { + close(fd); + wpa_printf(MSG_DEBUG, "VLAN: vlan_add: " + "if_name %s exists already", + if_request.device1); + return 1; + } + } + } + + /* A suitable vlan device does not already exist, add one. */ + + os_memset(&if_request, 0, sizeof(if_request)); + os_strlcpy(if_request.device1, if_name, sizeof(if_request.device1)); + if_request.u.VID = vid; + if_request.cmd = ADD_VLAN_CMD; + + if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: ADD_VLAN_CMD failed for %s: " + "%s", + __func__, if_request.device1, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +static int vlan_set_name_type(unsigned int name_type) +{ + int fd; + struct vlan_ioctl_args if_request; + + wpa_printf(MSG_DEBUG, "VLAN: vlan_set_name_type(name_type=%u)", + name_type); + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(AF_INET,SOCK_STREAM) " + "failed: %s", __func__, strerror(errno)); + return -1; + } + + os_memset(&if_request, 0, sizeof(if_request)); + + if_request.u.name_type = name_type; + if_request.cmd = SET_VLAN_NAME_TYPE_CMD; + if (ioctl(fd, SIOCSIFVLAN, &if_request) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: SET_VLAN_NAME_TYPE_CMD " + "name_type=%u failed: %s", + __func__, name_type, strerror(errno)); + close(fd); + return -1; + } + + close(fd); + return 0; +} + + +static void vlan_newlink(char *ifname, struct hostapd_data *hapd) +{ + char vlan_ifname[IFNAMSIZ]; + char br_name[IFNAMSIZ]; + struct hostapd_vlan *vlan = hapd->conf->vlan; + char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; + + wpa_printf(MSG_DEBUG, "VLAN: vlan_newlink(%s)", ifname); + + while (vlan) { + if (os_strcmp(ifname, vlan->ifname) == 0) { + + os_snprintf(br_name, sizeof(br_name), "brvlan%d", + vlan->vlan_id); + + if (!br_addbr(br_name)) + vlan->clean |= DVLAN_CLEAN_BR; + + ifconfig_up(br_name); + + if (tagged_interface) { + + if (!vlan_add(tagged_interface, vlan->vlan_id)) + vlan->clean |= DVLAN_CLEAN_VLAN; + + os_snprintf(vlan_ifname, sizeof(vlan_ifname), + "vlan%d", vlan->vlan_id); + + if (!br_addif(br_name, vlan_ifname)) + vlan->clean |= DVLAN_CLEAN_VLAN_PORT; + + ifconfig_up(vlan_ifname); + } + + if (!br_addif(br_name, ifname)) + vlan->clean |= DVLAN_CLEAN_WLAN_PORT; + + ifconfig_up(ifname); + + break; + } + vlan = vlan->next; + } +} + + +static void vlan_dellink(char *ifname, struct hostapd_data *hapd) +{ + char vlan_ifname[IFNAMSIZ]; + char br_name[IFNAMSIZ]; + struct hostapd_vlan *first, *prev, *vlan = hapd->conf->vlan; + char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface; + + wpa_printf(MSG_DEBUG, "VLAN: vlan_dellink(%s)", ifname); + + first = prev = vlan; + + while (vlan) { + if (os_strcmp(ifname, vlan->ifname) == 0) { + os_snprintf(br_name, sizeof(br_name), "brvlan%d", + vlan->vlan_id); + + if (vlan->clean & DVLAN_CLEAN_WLAN_PORT) + br_delif(br_name, vlan->ifname); + + if (tagged_interface) { + os_snprintf(vlan_ifname, sizeof(vlan_ifname), + "vlan%d", vlan->vlan_id); + if (vlan->clean & DVLAN_CLEAN_VLAN_PORT) + br_delif(br_name, vlan_ifname); + ifconfig_down(vlan_ifname); + + if (vlan->clean & DVLAN_CLEAN_VLAN) + vlan_rem(vlan_ifname); + } + + if ((vlan->clean & DVLAN_CLEAN_BR) && + br_getnumports(br_name) == 0) { + ifconfig_down(br_name); + br_delbr(br_name); + } + + if (vlan == first) { + hapd->conf->vlan = vlan->next; + } else { + prev->next = vlan->next; + } + os_free(vlan); + + break; + } + prev = vlan; + vlan = vlan->next; + } +} + + +static void +vlan_read_ifnames(struct nlmsghdr *h, size_t len, int del, + struct hostapd_data *hapd) +{ + struct ifinfomsg *ifi; + int attrlen, nlmsg_len, rta_len; + struct rtattr *attr; + + if (len < sizeof(*ifi)) + return; + + ifi = NLMSG_DATA(h); + + nlmsg_len = NLMSG_ALIGN(sizeof(struct ifinfomsg)); + + attrlen = h->nlmsg_len - nlmsg_len; + if (attrlen < 0) + return; + + attr = (struct rtattr *) (((char *) ifi) + nlmsg_len); + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + char ifname[IFNAMSIZ + 1]; + + if (attr->rta_type == IFLA_IFNAME) { + int n = attr->rta_len - rta_len; + if (n < 0) + break; + + os_memset(ifname, 0, sizeof(ifname)); + + if ((size_t) n > sizeof(ifname)) + n = sizeof(ifname); + os_memcpy(ifname, ((char *) attr) + rta_len, n); + + if (del) + vlan_dellink(ifname, hapd); + else + vlan_newlink(ifname, hapd); + } + + attr = RTA_NEXT(attr, attrlen); + } +} + + +static void vlan_event_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + char buf[8192]; + int left; + struct sockaddr_nl from; + socklen_t fromlen; + struct nlmsghdr *h; + struct hostapd_data *hapd = eloop_ctx; + + fromlen = sizeof(from); + left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, + (struct sockaddr *) &from, &fromlen); + if (left < 0) { + if (errno != EINTR && errno != EAGAIN) + wpa_printf(MSG_ERROR, "VLAN: %s: recvfrom failed: %s", + __func__, strerror(errno)); + return; + } + + h = (struct nlmsghdr *) buf; + while (left >= (int) sizeof(*h)) { + int len, plen; + + len = h->nlmsg_len; + plen = len - sizeof(*h); + if (len > left || plen < 0) { + wpa_printf(MSG_DEBUG, "VLAN: Malformed netlink " + "message: len=%d left=%d plen=%d", + len, left, plen); + break; + } + + switch (h->nlmsg_type) { + case RTM_NEWLINK: + vlan_read_ifnames(h, plen, 0, hapd); + break; + case RTM_DELLINK: + vlan_read_ifnames(h, plen, 1, hapd); + break; + } + + len = NLMSG_ALIGN(len); + left -= len; + h = (struct nlmsghdr *) ((char *) h + len); + } + + if (left > 0) { + wpa_printf(MSG_DEBUG, "VLAN: %s: %d extra bytes in the end of " + "netlink message", __func__, left); + } +} + + +static struct full_dynamic_vlan * +full_dynamic_vlan_init(struct hostapd_data *hapd) +{ + struct sockaddr_nl local; + struct full_dynamic_vlan *priv; + + priv = os_zalloc(sizeof(*priv)); + if (priv == NULL) + return NULL; + + vlan_set_name_type(VLAN_NAME_TYPE_PLUS_VID_NO_PAD); + + priv->s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (priv->s < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: socket(PF_NETLINK,SOCK_RAW," + "NETLINK_ROUTE) failed: %s", + __func__, strerror(errno)); + os_free(priv); + return NULL; + } + + os_memset(&local, 0, sizeof(local)); + local.nl_family = AF_NETLINK; + local.nl_groups = RTMGRP_LINK; + if (bind(priv->s, (struct sockaddr *) &local, sizeof(local)) < 0) { + wpa_printf(MSG_ERROR, "VLAN: %s: bind(netlink) failed: %s", + __func__, strerror(errno)); + close(priv->s); + os_free(priv); + return NULL; + } + + if (eloop_register_read_sock(priv->s, vlan_event_receive, hapd, NULL)) + { + close(priv->s); + os_free(priv); + return NULL; + } + + return priv; +} + + +static void full_dynamic_vlan_deinit(struct full_dynamic_vlan *priv) +{ + if (priv == NULL) + return; + eloop_unregister_read_sock(priv->s); + close(priv->s); + os_free(priv); +} +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + + +int vlan_setup_encryption_dyn(struct hostapd_data *hapd, + struct hostapd_ssid *mssid, const char *dyn_vlan) +{ + int i; + + if (dyn_vlan == NULL) + return 0; + + /* Static WEP keys are set here; IEEE 802.1X and WPA uses their own + * functions for setting up dynamic broadcast keys. */ + for (i = 0; i < 4; i++) { + if (mssid->wep.key[i] && + hapd->drv.set_key(dyn_vlan, hapd, WPA_ALG_WEP, NULL, i, + i == mssid->wep.idx, NULL, 0, + mssid->wep.key[i], mssid->wep.len[i])) { + wpa_printf(MSG_ERROR, "VLAN: Could not set WEP " + "encryption for dynamic VLAN"); + return -1; + } + } + + return 0; +} + + +static int vlan_dynamic_add(struct hostapd_data *hapd, + struct hostapd_vlan *vlan) +{ + while (vlan) { + if (vlan->vlan_id != VLAN_ID_WILDCARD) { + if (hapd->drv.vlan_if_add(hapd, vlan->ifname)) { + if (errno != EEXIST) { + wpa_printf(MSG_ERROR, "VLAN: Could " + "not add VLAN %s: %s", + vlan->ifname, + strerror(errno)); + return -1; + } + } +#ifdef CONFIG_FULL_DYNAMIC_VLAN + ifconfig_up(vlan->ifname); +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + } + + vlan = vlan->next; + } + + return 0; +} + + +static void vlan_dynamic_remove(struct hostapd_data *hapd, + struct hostapd_vlan *vlan) +{ + struct hostapd_vlan *next; + + while (vlan) { + next = vlan->next; + + if (vlan->vlan_id != VLAN_ID_WILDCARD && + hapd->drv.vlan_if_remove(hapd, vlan->ifname)) { + wpa_printf(MSG_ERROR, "VLAN: Could not remove VLAN " + "iface: %s: %s", + vlan->ifname, strerror(errno)); + } +#ifdef CONFIG_FULL_DYNAMIC_VLAN + if (vlan->clean) + vlan_dellink(vlan->ifname, hapd); +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + + vlan = next; + } +} + + +int vlan_init(struct hostapd_data *hapd) +{ +#ifdef CONFIG_FULL_DYNAMIC_VLAN + hapd->full_dynamic_vlan = full_dynamic_vlan_init(hapd); +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + + if (vlan_dynamic_add(hapd, hapd->conf->vlan)) + return -1; + + return 0; +} + + +void vlan_deinit(struct hostapd_data *hapd) +{ + vlan_dynamic_remove(hapd, hapd->conf->vlan); + +#ifdef CONFIG_FULL_DYNAMIC_VLAN + full_dynamic_vlan_deinit(hapd->full_dynamic_vlan); +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ +} + + +struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, + struct hostapd_vlan *vlan, + int vlan_id) +{ + struct hostapd_vlan *n; + char *ifname, *pos; + + if (vlan == NULL || vlan_id <= 0 || vlan_id > MAX_VLAN_ID || + vlan->vlan_id != VLAN_ID_WILDCARD) + return NULL; + + wpa_printf(MSG_DEBUG, "VLAN: %s(vlan_id=%d ifname=%s)", + __func__, vlan_id, vlan->ifname); + ifname = os_strdup(vlan->ifname); + if (ifname == NULL) + return NULL; + pos = os_strchr(ifname, '#'); + if (pos == NULL) { + os_free(ifname); + return NULL; + } + *pos++ = '\0'; + + n = os_zalloc(sizeof(*n)); + if (n == NULL) { + os_free(ifname); + return NULL; + } + + n->vlan_id = vlan_id; + n->dynamic_vlan = 1; + + os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id, + pos); + os_free(ifname); + + if (hapd->drv.vlan_if_add(hapd, n->ifname)) { + os_free(n); + return NULL; + } + + n->next = hapd->conf->vlan; + hapd->conf->vlan = n; + +#ifdef CONFIG_FULL_DYNAMIC_VLAN + ifconfig_up(n->ifname); +#endif /* CONFIG_FULL_DYNAMIC_VLAN */ + + return n; +} + + +int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id) +{ + struct hostapd_vlan *vlan; + + if (vlan_id <= 0 || vlan_id > MAX_VLAN_ID) + return 1; + + wpa_printf(MSG_DEBUG, "VLAN: %s(vlan_id=%d)", __func__, vlan_id); + + vlan = hapd->conf->vlan; + while (vlan) { + if (vlan->vlan_id == vlan_id && vlan->dynamic_vlan > 0) { + vlan->dynamic_vlan--; + break; + } + vlan = vlan->next; + } + + if (vlan == NULL) + return 1; + + if (vlan->dynamic_vlan == 0) + hapd->drv.vlan_if_remove(hapd, vlan->ifname); + + return 0; +} diff --git a/contrib/wpa/src/ap/vlan_init.h b/contrib/wpa/src/ap/vlan_init.h new file mode 100644 index 0000000..382d5de --- /dev/null +++ b/contrib/wpa/src/ap/vlan_init.h @@ -0,0 +1,59 @@ +/* + * hostapd / VLAN initialization + * Copyright 2003, Instant802 Networks, Inc. + * Copyright 2005, Devicescape Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef VLAN_INIT_H +#define VLAN_INIT_H + +#ifndef CONFIG_NO_VLAN +int vlan_init(struct hostapd_data *hapd); +void vlan_deinit(struct hostapd_data *hapd); +struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, + struct hostapd_vlan *vlan, + int vlan_id); +int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id); +int vlan_setup_encryption_dyn(struct hostapd_data *hapd, + struct hostapd_ssid *mssid, + const char *dyn_vlan); +#else /* CONFIG_NO_VLAN */ +static inline int vlan_init(struct hostapd_data *hapd) +{ + return 0; +} + +static inline void vlan_deinit(struct hostapd_data *hapd) +{ +} + +static inline struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd, + struct hostapd_vlan *vlan, + int vlan_id) +{ + return NULL; +} + +static inline int vlan_remove_dynamic(struct hostapd_data *hapd, int vlan_id) +{ + return -1; +} + +static inline int vlan_setup_encryption_dyn(struct hostapd_data *hapd, + struct hostapd_ssid *mssid, + const char *dyn_vlan) +{ + return -1; +} +#endif /* CONFIG_NO_VLAN */ + +#endif /* VLAN_INIT_H */ diff --git a/contrib/wpa/src/ap/wmm.c b/contrib/wpa/src/ap/wmm.c new file mode 100644 index 0000000..3668130 --- /dev/null +++ b/contrib/wpa/src/ap/wmm.c @@ -0,0 +1,324 @@ +/* + * hostapd / WMM (Wi-Fi Multimedia) + * Copyright 2002-2003, Instant802 Networks, Inc. + * Copyright 2005-2006, Devicescape Software, Inc. + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "hostapd.h" +#include "ieee802_11.h" +#include "sta_info.h" +#include "ap_config.h" +#include "wmm.h" + + +/* TODO: maintain separate sequence and fragment numbers for each AC + * TODO: IGMP snooping to track which multicasts to forward - and use QOS-DATA + * if only WMM stations are receiving a certain group */ + + +static inline u8 wmm_aci_aifsn(int aifsn, int acm, int aci) +{ + u8 ret; + ret = (aifsn << WMM_AC_AIFNS_SHIFT) & WMM_AC_AIFSN_MASK; + if (acm) + ret |= WMM_AC_ACM; + ret |= (aci << WMM_AC_ACI_SHIFT) & WMM_AC_ACI_MASK; + return ret; +} + + +static inline u8 wmm_ecw(int ecwmin, int ecwmax) +{ + return ((ecwmin << WMM_AC_ECWMIN_SHIFT) & WMM_AC_ECWMIN_MASK) | + ((ecwmax << WMM_AC_ECWMAX_SHIFT) & WMM_AC_ECWMAX_MASK); +} + + +/* + * Add WMM Parameter Element to Beacon, Probe Response, and (Re)Association + * Response frames. + */ +u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid) +{ + u8 *pos = eid; + struct wmm_parameter_element *wmm = + (struct wmm_parameter_element *) (pos + 2); + int e; + + if (!hapd->conf->wmm_enabled) + return eid; + eid[0] = WLAN_EID_VENDOR_SPECIFIC; + wmm->oui[0] = 0x00; + wmm->oui[1] = 0x50; + wmm->oui[2] = 0xf2; + wmm->oui_type = WMM_OUI_TYPE; + wmm->oui_subtype = WMM_OUI_SUBTYPE_PARAMETER_ELEMENT; + wmm->version = WMM_VERSION; + wmm->qos_info = hapd->parameter_set_count & 0xf; + + if (hapd->conf->wmm_uapsd) + wmm->qos_info |= 0x80; + + /* fill in a parameter set record for each AC */ + for (e = 0; e < 4; e++) { + struct wmm_ac_parameter *ac = &wmm->ac[e]; + struct hostapd_wmm_ac_params *acp = + &hapd->iconf->wmm_ac_params[e]; + + ac->aci_aifsn = wmm_aci_aifsn(acp->aifs, + acp->admission_control_mandatory, + e); + ac->cw = wmm_ecw(acp->cwmin, acp->cwmax); + ac->txop_limit = host_to_le16(acp->txop_limit); + } + + pos = (u8 *) (wmm + 1); + eid[1] = pos - eid - 2; /* element length */ + + return pos; +} + + +/* This function is called when a station sends an association request with + * WMM info element. The function returns zero on success or non-zero on any + * error in WMM element. eid does not include Element ID and Length octets. */ +int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid, size_t len) +{ + struct wmm_information_element *wmm; + + wpa_hexdump(MSG_MSGDUMP, "WMM IE", eid, len); + + if (len < sizeof(struct wmm_information_element)) { + wpa_printf(MSG_DEBUG, "Too short WMM IE (len=%lu)", + (unsigned long) len); + return -1; + } + + wmm = (struct wmm_information_element *) eid; + wpa_printf(MSG_DEBUG, "Validating WMM IE: OUI %02x:%02x:%02x " + "OUI type %d OUI sub-type %d version %d QoS info 0x%x", + wmm->oui[0], wmm->oui[1], wmm->oui[2], wmm->oui_type, + wmm->oui_subtype, wmm->version, wmm->qos_info); + if (wmm->oui_subtype != WMM_OUI_SUBTYPE_INFORMATION_ELEMENT || + wmm->version != WMM_VERSION) { + wpa_printf(MSG_DEBUG, "Unsupported WMM IE Subtype/Version"); + return -1; + } + + return 0; +} + + +static void wmm_send_action(struct hostapd_data *hapd, const u8 *addr, + const struct wmm_tspec_element *tspec, + u8 action_code, u8 dialogue_token, u8 status_code) +{ + u8 buf[256]; + struct ieee80211_mgmt *m = (struct ieee80211_mgmt *) buf; + struct wmm_tspec_element *t = (struct wmm_tspec_element *) + m->u.action.u.wmm_action.variable; + int len; + + hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "action response - reason %d", status_code); + os_memset(buf, 0, sizeof(buf)); + m->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memcpy(m->da, addr, ETH_ALEN); + os_memcpy(m->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN); + m->u.action.category = WLAN_ACTION_WMM; + m->u.action.u.wmm_action.action_code = action_code; + m->u.action.u.wmm_action.dialog_token = dialogue_token; + m->u.action.u.wmm_action.status_code = status_code; + os_memcpy(t, tspec, sizeof(struct wmm_tspec_element)); + len = ((u8 *) (t + 1)) - buf; + + if (hapd->drv.send_mgmt_frame(hapd, m, len) < 0) + perror("wmm_send_action: send"); +} + + +int wmm_process_tspec(struct wmm_tspec_element *tspec) +{ + int medium_time, pps, duration; + int up, psb, dir, tid; + u16 val, surplus; + + up = (tspec->ts_info[1] >> 3) & 0x07; + psb = (tspec->ts_info[1] >> 2) & 0x01; + dir = (tspec->ts_info[0] >> 5) & 0x03; + tid = (tspec->ts_info[0] >> 1) & 0x0f; + wpa_printf(MSG_DEBUG, "WMM: TS Info: UP=%d PSB=%d Direction=%d TID=%d", + up, psb, dir, tid); + val = le_to_host16(tspec->nominal_msdu_size); + wpa_printf(MSG_DEBUG, "WMM: Nominal MSDU Size: %d%s", + val & 0x7fff, val & 0x8000 ? " (fixed)" : ""); + wpa_printf(MSG_DEBUG, "WMM: Mean Data Rate: %u bps", + le_to_host32(tspec->mean_data_rate)); + wpa_printf(MSG_DEBUG, "WMM: Minimum PHY Rate: %u bps", + le_to_host32(tspec->minimum_phy_rate)); + val = le_to_host16(tspec->surplus_bandwidth_allowance); + wpa_printf(MSG_DEBUG, "WMM: Surplus Bandwidth Allowance: %u.%04u", + val >> 13, 10000 * (val & 0x1fff) / 0x2000); + + val = le_to_host16(tspec->nominal_msdu_size); + if (val == 0) { + wpa_printf(MSG_DEBUG, "WMM: Invalid Nominal MSDU Size (0)"); + return WMM_ADDTS_STATUS_INVALID_PARAMETERS; + } + /* pps = Ceiling((Mean Data Rate / 8) / Nominal MSDU Size) */ + pps = ((le_to_host32(tspec->mean_data_rate) / 8) + val - 1) / val; + wpa_printf(MSG_DEBUG, "WMM: Packets-per-second estimate for TSPEC: %d", + pps); + + if (le_to_host32(tspec->minimum_phy_rate) < 1000000) { + wpa_printf(MSG_DEBUG, "WMM: Too small Minimum PHY Rate"); + return WMM_ADDTS_STATUS_INVALID_PARAMETERS; + } + + duration = (le_to_host16(tspec->nominal_msdu_size) & 0x7fff) * 8 / + (le_to_host32(tspec->minimum_phy_rate) / 1000000) + + 50 /* FIX: proper SIFS + ACK duration */; + + /* unsigned binary number with an implicit binary point after the + * leftmost 3 bits, i.e., 0x2000 = 1.0 */ + surplus = le_to_host16(tspec->surplus_bandwidth_allowance); + if (surplus <= 0x2000) { + wpa_printf(MSG_DEBUG, "WMM: Surplus Bandwidth Allowance not " + "greater than unity"); + return WMM_ADDTS_STATUS_INVALID_PARAMETERS; + } + + medium_time = surplus * pps * duration / 0x2000; + wpa_printf(MSG_DEBUG, "WMM: Estimated medium time: %u", medium_time); + + /* + * TODO: store list of granted (and still active) TSPECs and check + * whether there is available medium time for this request. For now, + * just refuse requests that would by themselves take very large + * portion of the available bandwidth. + */ + if (medium_time > 750000) { + wpa_printf(MSG_DEBUG, "WMM: Refuse TSPEC request for over " + "75%% of available bandwidth"); + return WMM_ADDTS_STATUS_REFUSED; + } + + /* Convert to 32 microseconds per second unit */ + tspec->medium_time = host_to_le16(medium_time / 32); + + return WMM_ADDTS_STATUS_ADMISSION_ACCEPTED; +} + + +static void wmm_addts_req(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, + struct wmm_tspec_element *tspec, size_t len) +{ + const u8 *end = ((const u8 *) mgmt) + len; + int res; + + if ((const u8 *) (tspec + 1) > end) { + wpa_printf(MSG_DEBUG, "WMM: TSPEC overflow in ADDTS Request"); + return; + } + + wpa_printf(MSG_DEBUG, "WMM: ADDTS Request (Dialog Token %d) for TSPEC " + "from " MACSTR, + mgmt->u.action.u.wmm_action.dialog_token, + MAC2STR(mgmt->sa)); + + res = wmm_process_tspec(tspec); + wpa_printf(MSG_DEBUG, "WMM: ADDTS processing result: %d", res); + + wmm_send_action(hapd, mgmt->sa, tspec, WMM_ACTION_CODE_ADDTS_RESP, + mgmt->u.action.u.wmm_action.dialog_token, res); +} + + +void hostapd_wmm_action(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len) +{ + int action_code; + int left = len - IEEE80211_HDRLEN - 4; + const u8 *pos = ((const u8 *) mgmt) + IEEE80211_HDRLEN + 4; + struct ieee802_11_elems elems; + struct sta_info *sta = ap_get_sta(hapd, mgmt->sa); + + /* check that the request comes from a valid station */ + if (!sta || + (sta->flags & (WLAN_STA_ASSOC | WLAN_STA_WMM)) != + (WLAN_STA_ASSOC | WLAN_STA_WMM)) { + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "wmm action received is not from associated wmm" + " station"); + /* TODO: respond with action frame refused status code */ + return; + } + + /* extract the tspec info element */ + if (ieee802_11_parse_elems(pos, left, &elems, 1) == ParseFailed) { + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "hostapd_wmm_action - could not parse wmm " + "action"); + /* TODO: respond with action frame invalid parameters status + * code */ + return; + } + + if (!elems.wmm_tspec || + elems.wmm_tspec_len != (sizeof(struct wmm_tspec_element) - 2)) { + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "hostapd_wmm_action - missing or wrong length " + "tspec"); + /* TODO: respond with action frame invalid parameters status + * code */ + return; + } + + /* TODO: check the request is for an AC with ACM set, if not, refuse + * request */ + + action_code = mgmt->u.action.u.wmm_action.action_code; + switch (action_code) { + case WMM_ACTION_CODE_ADDTS_REQ: + wmm_addts_req(hapd, mgmt, (struct wmm_tspec_element *) + (elems.wmm_tspec - 2), len); + return; +#if 0 + /* TODO: needed for client implementation */ + case WMM_ACTION_CODE_ADDTS_RESP: + wmm_setup_request(hapd, mgmt, len); + return; + /* TODO: handle station teardown requests */ + case WMM_ACTION_CODE_DELTS: + wmm_teardown(hapd, mgmt, len); + return; +#endif + } + + hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211, + HOSTAPD_LEVEL_DEBUG, + "hostapd_wmm_action - unknown action code %d", + action_code); +} diff --git a/contrib/wpa/src/ap/wmm.h b/contrib/wpa/src/ap/wmm.h new file mode 100644 index 0000000..96b04e8 --- /dev/null +++ b/contrib/wpa/src/ap/wmm.h @@ -0,0 +1,29 @@ +/* + * hostapd / WMM (Wi-Fi Multimedia) + * Copyright 2002-2003, Instant802 Networks, Inc. + * Copyright 2005-2006, Devicescape Software, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WME_H +#define WME_H + +struct ieee80211_mgmt; +struct wmm_tspec_element; + +u8 * hostapd_eid_wmm(struct hostapd_data *hapd, u8 *eid); +int hostapd_eid_wmm_valid(struct hostapd_data *hapd, const u8 *eid, + size_t len); +void hostapd_wmm_action(struct hostapd_data *hapd, + const struct ieee80211_mgmt *mgmt, size_t len); +int wmm_process_tspec(struct wmm_tspec_element *tspec); + +#endif /* WME_H */ diff --git a/contrib/wpa/src/ap/wpa_auth.c b/contrib/wpa/src/ap/wpa_auth.c new file mode 100644 index 0000000..36cb0f4 --- /dev/null +++ b/contrib/wpa/src/ap/wpa_auth.c @@ -0,0 +1,2651 @@ +/* + * hostapd - IEEE 802.11i-2004 / WPA Authenticator + * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "utils/state_machine.h" +#include "common/ieee802_11_defs.h" +#include "crypto/aes_wrap.h" +#include "crypto/crypto.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" +#include "eapol_auth/eapol_auth_sm.h" +#include "ap_config.h" +#include "ieee802_11.h" +#include "wpa_auth.h" +#include "pmksa_cache_auth.h" +#include "wpa_auth_i.h" +#include "wpa_auth_ie.h" + +#define STATE_MACHINE_DATA struct wpa_state_machine +#define STATE_MACHINE_DEBUG_PREFIX "WPA" +#define STATE_MACHINE_ADDR sm->addr + + +static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx); +static int wpa_sm_step(struct wpa_state_machine *sm); +static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len); +static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx); +static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, + struct wpa_group *group); +static void wpa_request_new_ptk(struct wpa_state_machine *sm); +static int wpa_gtk_update(struct wpa_authenticator *wpa_auth, + struct wpa_group *group); + +static const u32 dot11RSNAConfigGroupUpdateCount = 4; +static const u32 dot11RSNAConfigPairwiseUpdateCount = 4; +static const u32 eapol_key_timeout_first = 100; /* ms */ +static const u32 eapol_key_timeout_subseq = 1000; /* ms */ + +/* TODO: make these configurable */ +static const int dot11RSNAConfigPMKLifetime = 43200; +static const int dot11RSNAConfigPMKReauthThreshold = 70; +static const int dot11RSNAConfigSATimeout = 60; + + +static inline void wpa_auth_mic_failure_report( + struct wpa_authenticator *wpa_auth, const u8 *addr) +{ + if (wpa_auth->cb.mic_failure_report) + wpa_auth->cb.mic_failure_report(wpa_auth->cb.ctx, addr); +} + + +static inline void wpa_auth_set_eapol(struct wpa_authenticator *wpa_auth, + const u8 *addr, wpa_eapol_variable var, + int value) +{ + if (wpa_auth->cb.set_eapol) + wpa_auth->cb.set_eapol(wpa_auth->cb.ctx, addr, var, value); +} + + +static inline int wpa_auth_get_eapol(struct wpa_authenticator *wpa_auth, + const u8 *addr, wpa_eapol_variable var) +{ + if (wpa_auth->cb.get_eapol == NULL) + return -1; + return wpa_auth->cb.get_eapol(wpa_auth->cb.ctx, addr, var); +} + + +static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth, + const u8 *addr, const u8 *prev_psk) +{ + if (wpa_auth->cb.get_psk == NULL) + return NULL; + return wpa_auth->cb.get_psk(wpa_auth->cb.ctx, addr, prev_psk); +} + + +static inline int wpa_auth_get_msk(struct wpa_authenticator *wpa_auth, + const u8 *addr, u8 *msk, size_t *len) +{ + if (wpa_auth->cb.get_msk == NULL) + return -1; + return wpa_auth->cb.get_msk(wpa_auth->cb.ctx, addr, msk, len); +} + + +static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth, + int vlan_id, + enum wpa_alg alg, const u8 *addr, int idx, + u8 *key, size_t key_len) +{ + if (wpa_auth->cb.set_key == NULL) + return -1; + return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx, + key, key_len); +} + + +static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth, + const u8 *addr, int idx, u8 *seq) +{ + if (wpa_auth->cb.get_seqnum == NULL) + return -1; + return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq); +} + + +static inline int +wpa_auth_send_eapol(struct wpa_authenticator *wpa_auth, const u8 *addr, + const u8 *data, size_t data_len, int encrypt) +{ + if (wpa_auth->cb.send_eapol == NULL) + return -1; + return wpa_auth->cb.send_eapol(wpa_auth->cb.ctx, addr, data, data_len, + encrypt); +} + + +int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth, + int (*cb)(struct wpa_state_machine *sm, void *ctx), + void *cb_ctx) +{ + if (wpa_auth->cb.for_each_sta == NULL) + return 0; + return wpa_auth->cb.for_each_sta(wpa_auth->cb.ctx, cb, cb_ctx); +} + + +int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth, + int (*cb)(struct wpa_authenticator *a, void *ctx), + void *cb_ctx) +{ + if (wpa_auth->cb.for_each_auth == NULL) + return 0; + return wpa_auth->cb.for_each_auth(wpa_auth->cb.ctx, cb, cb_ctx); +} + + +void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr, + logger_level level, const char *txt) +{ + if (wpa_auth->cb.logger == NULL) + return; + wpa_auth->cb.logger(wpa_auth->cb.ctx, addr, level, txt); +} + + +void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr, + logger_level level, const char *fmt, ...) +{ + char *format; + int maxlen; + va_list ap; + + if (wpa_auth->cb.logger == NULL) + return; + + maxlen = os_strlen(fmt) + 100; + format = os_malloc(maxlen); + if (!format) + return; + + va_start(ap, fmt); + vsnprintf(format, maxlen, fmt, ap); + va_end(ap); + + wpa_auth_logger(wpa_auth, addr, level, format); + + os_free(format); +} + + +static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth, + const u8 *addr) +{ + if (wpa_auth->cb.disconnect == NULL) + return; + wpa_auth->cb.disconnect(wpa_auth->cb.ctx, addr, + WLAN_REASON_PREV_AUTH_NOT_VALID); +} + + +static int wpa_use_aes_cmac(struct wpa_state_machine *sm) +{ + int ret = 0; +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) + ret = 1; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + if (wpa_key_mgmt_sha256(sm->wpa_key_mgmt)) + ret = 1; +#endif /* CONFIG_IEEE80211W */ + return ret; +} + + +static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_authenticator *wpa_auth = eloop_ctx; + + if (os_get_random(wpa_auth->group->GMK, WPA_GMK_LEN)) { + wpa_printf(MSG_ERROR, "Failed to get random data for WPA " + "initialization."); + } else { + wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "GMK rekeyd"); + } + + if (wpa_auth->conf.wpa_gmk_rekey) { + eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0, + wpa_rekey_gmk, wpa_auth, NULL); + } +} + + +static void wpa_rekey_gtk(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_authenticator *wpa_auth = eloop_ctx; + struct wpa_group *group; + + wpa_auth_logger(wpa_auth, NULL, LOGGER_DEBUG, "rekeying GTK"); + for (group = wpa_auth->group; group; group = group->next) { + group->GTKReKey = TRUE; + do { + group->changed = FALSE; + wpa_group_sm_step(wpa_auth, group); + } while (group->changed); + } + + if (wpa_auth->conf.wpa_group_rekey) { + eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, + 0, wpa_rekey_gtk, wpa_auth, NULL); + } +} + + +static void wpa_rekey_ptk(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_authenticator *wpa_auth = eloop_ctx; + struct wpa_state_machine *sm = timeout_ctx; + + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "rekeying PTK"); + wpa_request_new_ptk(sm); + wpa_sm_step(sm); +} + + +static int wpa_auth_pmksa_clear_cb(struct wpa_state_machine *sm, void *ctx) +{ + if (sm->pmksa == ctx) + sm->pmksa = NULL; + return 0; +} + + +static void wpa_auth_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry, + void *ctx) +{ + struct wpa_authenticator *wpa_auth = ctx; + wpa_auth_for_each_sta(wpa_auth, wpa_auth_pmksa_clear_cb, entry); +} + + +static void wpa_group_set_key_len(struct wpa_group *group, int cipher) +{ + switch (cipher) { + case WPA_CIPHER_CCMP: + group->GTK_len = 16; + break; + case WPA_CIPHER_TKIP: + group->GTK_len = 32; + break; + case WPA_CIPHER_WEP104: + group->GTK_len = 13; + break; + case WPA_CIPHER_WEP40: + group->GTK_len = 5; + break; + } +} + + +static struct wpa_group * wpa_group_init(struct wpa_authenticator *wpa_auth, + int vlan_id) +{ + struct wpa_group *group; + u8 buf[ETH_ALEN + 8 + sizeof(group)]; + u8 rkey[32]; + + group = os_zalloc(sizeof(struct wpa_group)); + if (group == NULL) + return NULL; + + group->GTKAuthenticator = TRUE; + group->vlan_id = vlan_id; + + wpa_group_set_key_len(group, wpa_auth->conf.wpa_group); + + /* Counter = PRF-256(Random number, "Init Counter", + * Local MAC Address || Time) + */ + os_memcpy(buf, wpa_auth->addr, ETH_ALEN); + wpa_get_ntp_timestamp(buf + ETH_ALEN); + os_memcpy(buf + ETH_ALEN + 8, &group, sizeof(group)); + if (os_get_random(rkey, sizeof(rkey)) || + os_get_random(group->GMK, WPA_GMK_LEN)) { + wpa_printf(MSG_ERROR, "Failed to get random data for WPA " + "initialization."); + os_free(group); + return NULL; + } + + sha1_prf(rkey, sizeof(rkey), "Init Counter", buf, sizeof(buf), + group->Counter, WPA_NONCE_LEN); + + group->GInit = TRUE; + wpa_group_sm_step(wpa_auth, group); + group->GInit = FALSE; + wpa_group_sm_step(wpa_auth, group); + + return group; +} + + +/** + * wpa_init - Initialize WPA authenticator + * @addr: Authenticator address + * @conf: Configuration for WPA authenticator + * @cb: Callback functions for WPA authenticator + * Returns: Pointer to WPA authenticator data or %NULL on failure + */ +struct wpa_authenticator * wpa_init(const u8 *addr, + struct wpa_auth_config *conf, + struct wpa_auth_callbacks *cb) +{ + struct wpa_authenticator *wpa_auth; + + wpa_auth = os_zalloc(sizeof(struct wpa_authenticator)); + if (wpa_auth == NULL) + return NULL; + os_memcpy(wpa_auth->addr, addr, ETH_ALEN); + os_memcpy(&wpa_auth->conf, conf, sizeof(*conf)); + os_memcpy(&wpa_auth->cb, cb, sizeof(*cb)); + + if (wpa_auth_gen_wpa_ie(wpa_auth)) { + wpa_printf(MSG_ERROR, "Could not generate WPA IE."); + os_free(wpa_auth); + return NULL; + } + + wpa_auth->group = wpa_group_init(wpa_auth, 0); + if (wpa_auth->group == NULL) { + os_free(wpa_auth->wpa_ie); + os_free(wpa_auth); + return NULL; + } + + wpa_auth->pmksa = pmksa_cache_auth_init(wpa_auth_pmksa_free_cb, + wpa_auth); + if (wpa_auth->pmksa == NULL) { + wpa_printf(MSG_ERROR, "PMKSA cache initialization failed."); + os_free(wpa_auth->wpa_ie); + os_free(wpa_auth); + return NULL; + } + +#ifdef CONFIG_IEEE80211R + wpa_auth->ft_pmk_cache = wpa_ft_pmk_cache_init(); + if (wpa_auth->ft_pmk_cache == NULL) { + wpa_printf(MSG_ERROR, "FT PMK cache initialization failed."); + os_free(wpa_auth->wpa_ie); + pmksa_cache_auth_deinit(wpa_auth->pmksa); + os_free(wpa_auth); + return NULL; + } +#endif /* CONFIG_IEEE80211R */ + + if (wpa_auth->conf.wpa_gmk_rekey) { + eloop_register_timeout(wpa_auth->conf.wpa_gmk_rekey, 0, + wpa_rekey_gmk, wpa_auth, NULL); + } + + if (wpa_auth->conf.wpa_group_rekey) { + eloop_register_timeout(wpa_auth->conf.wpa_group_rekey, 0, + wpa_rekey_gtk, wpa_auth, NULL); + } + + return wpa_auth; +} + + +/** + * wpa_deinit - Deinitialize WPA authenticator + * @wpa_auth: Pointer to WPA authenticator data from wpa_init() + */ +void wpa_deinit(struct wpa_authenticator *wpa_auth) +{ + struct wpa_group *group, *prev; + + eloop_cancel_timeout(wpa_rekey_gmk, wpa_auth, NULL); + eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); + +#ifdef CONFIG_PEERKEY + while (wpa_auth->stsl_negotiations) + wpa_stsl_remove(wpa_auth, wpa_auth->stsl_negotiations); +#endif /* CONFIG_PEERKEY */ + + pmksa_cache_auth_deinit(wpa_auth->pmksa); + +#ifdef CONFIG_IEEE80211R + wpa_ft_pmk_cache_deinit(wpa_auth->ft_pmk_cache); + wpa_auth->ft_pmk_cache = NULL; +#endif /* CONFIG_IEEE80211R */ + + os_free(wpa_auth->wpa_ie); + + group = wpa_auth->group; + while (group) { + prev = group; + group = group->next; + os_free(prev); + } + + os_free(wpa_auth); +} + + +/** + * wpa_reconfig - Update WPA authenticator configuration + * @wpa_auth: Pointer to WPA authenticator data from wpa_init() + * @conf: Configuration for WPA authenticator + */ +int wpa_reconfig(struct wpa_authenticator *wpa_auth, + struct wpa_auth_config *conf) +{ + struct wpa_group *group; + if (wpa_auth == NULL) + return 0; + + os_memcpy(&wpa_auth->conf, conf, sizeof(*conf)); + if (wpa_auth_gen_wpa_ie(wpa_auth)) { + wpa_printf(MSG_ERROR, "Could not generate WPA IE."); + return -1; + } + + /* + * Reinitialize GTK to make sure it is suitable for the new + * configuration. + */ + group = wpa_auth->group; + wpa_group_set_key_len(group, wpa_auth->conf.wpa_group); + group->GInit = TRUE; + wpa_group_sm_step(wpa_auth, group); + group->GInit = FALSE; + wpa_group_sm_step(wpa_auth, group); + + return 0; +} + + +struct wpa_state_machine * +wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr) +{ + struct wpa_state_machine *sm; + + sm = os_zalloc(sizeof(struct wpa_state_machine)); + if (sm == NULL) + return NULL; + os_memcpy(sm->addr, addr, ETH_ALEN); + + sm->wpa_auth = wpa_auth; + sm->group = wpa_auth->group; + + return sm; +} + + +int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm) +{ + if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL) + return -1; + +#ifdef CONFIG_IEEE80211R + if (sm->ft_completed) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "FT authentication already completed - do not " + "start 4-way handshake"); + return 0; + } +#endif /* CONFIG_IEEE80211R */ + + if (sm->started) { + os_memset(&sm->key_replay, 0, sizeof(sm->key_replay)); + sm->ReAuthenticationRequest = TRUE; + return wpa_sm_step(sm); + } + + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "start authentication"); + sm->started = 1; + + sm->Init = TRUE; + if (wpa_sm_step(sm) == 1) + return 1; /* should not really happen */ + sm->Init = FALSE; + sm->AuthenticationRequest = TRUE; + return wpa_sm_step(sm); +} + + +void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm) +{ + /* WPA/RSN was not used - clear WPA state. This is needed if the STA + * reassociates back to the same AP while the previous entry for the + * STA has not yet been removed. */ + if (sm == NULL) + return; + + sm->wpa_key_mgmt = 0; +} + + +static void wpa_free_sta_sm(struct wpa_state_machine *sm) +{ +#ifdef CONFIG_IEEE80211R + os_free(sm->assoc_resp_ftie); +#endif /* CONFIG_IEEE80211R */ + os_free(sm->last_rx_eapol_key); + os_free(sm->wpa_ie); + os_free(sm); +} + + +void wpa_auth_sta_deinit(struct wpa_state_machine *sm) +{ + if (sm == NULL) + return; + + if (sm->wpa_auth->conf.wpa_strict_rekey && sm->has_GTK) { + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "strict rekeying - force GTK rekey since STA " + "is leaving"); + eloop_cancel_timeout(wpa_rekey_gtk, sm->wpa_auth, NULL); + eloop_register_timeout(0, 500000, wpa_rekey_gtk, sm->wpa_auth, + NULL); + } + + eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); + eloop_cancel_timeout(wpa_sm_call_step, sm, NULL); + eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); + if (sm->in_step_loop) { + /* Must not free state machine while wpa_sm_step() is running. + * Freeing will be completed in the end of wpa_sm_step(). */ + wpa_printf(MSG_DEBUG, "WPA: Registering pending STA state " + "machine deinit for " MACSTR, MAC2STR(sm->addr)); + sm->pending_deinit = 1; + } else + wpa_free_sta_sm(sm); +} + + +static void wpa_request_new_ptk(struct wpa_state_machine *sm) +{ + if (sm == NULL) + return; + + sm->PTKRequest = TRUE; + sm->PTK_valid = 0; +} + + +static int wpa_replay_counter_valid(struct wpa_state_machine *sm, + const u8 *replay_counter) +{ + int i; + for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) { + if (!sm->key_replay[i].valid) + break; + if (os_memcmp(replay_counter, sm->key_replay[i].counter, + WPA_REPLAY_COUNTER_LEN) == 0) + return 1; + } + return 0; +} + + +#ifdef CONFIG_IEEE80211R +static int ft_check_msg_2_of_4(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + struct wpa_eapol_ie_parse *kde) +{ + struct wpa_ie_data ie; + struct rsn_mdie *mdie; + + if (wpa_parse_wpa_ie_rsn(kde->rsn_ie, kde->rsn_ie_len, &ie) < 0 || + ie.num_pmkid != 1 || ie.pmkid == NULL) { + wpa_printf(MSG_DEBUG, "FT: No PMKR1Name in " + "FT 4-way handshake message 2/4"); + return -1; + } + + os_memcpy(sm->sup_pmk_r1_name, ie.pmkid, PMKID_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from Supplicant", + sm->sup_pmk_r1_name, PMKID_LEN); + + if (!kde->mdie || !kde->ftie) { + wpa_printf(MSG_DEBUG, "FT: No %s in FT 4-way handshake " + "message 2/4", kde->mdie ? "FTIE" : "MDIE"); + return -1; + } + + mdie = (struct rsn_mdie *) (kde->mdie + 2); + if (kde->mdie[1] < sizeof(struct rsn_mdie) || + os_memcmp(wpa_auth->conf.mobility_domain, mdie->mobility_domain, + MOBILITY_DOMAIN_ID_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: MDIE mismatch"); + return -1; + } + + if (sm->assoc_resp_ftie && + (kde->ftie[1] != sm->assoc_resp_ftie[1] || + os_memcmp(kde->ftie, sm->assoc_resp_ftie, + 2 + sm->assoc_resp_ftie[1]) != 0)) { + wpa_printf(MSG_DEBUG, "FT: FTIE mismatch"); + wpa_hexdump(MSG_DEBUG, "FT: FTIE in EAPOL-Key msg 2/4", + kde->ftie, kde->ftie_len); + wpa_hexdump(MSG_DEBUG, "FT: FTIE in (Re)AssocResp", + sm->assoc_resp_ftie, 2 + sm->assoc_resp_ftie[1]); + return -1; + } + + return 0; +} +#endif /* CONFIG_IEEE80211R */ + + +void wpa_receive(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + u8 *data, size_t data_len) +{ + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *key; + u16 key_info, key_data_length; + enum { PAIRWISE_2, PAIRWISE_4, GROUP_2, REQUEST, + SMK_M1, SMK_M3, SMK_ERROR } msg; + char *msgtxt; + struct wpa_eapol_ie_parse kde; + int ft; + const u8 *eapol_key_ie; + size_t eapol_key_ie_len; + + if (wpa_auth == NULL || !wpa_auth->conf.wpa || sm == NULL) + return; + + if (data_len < sizeof(*hdr) + sizeof(*key)) + return; + + hdr = (struct ieee802_1x_hdr *) data; + key = (struct wpa_eapol_key *) (hdr + 1); + key_info = WPA_GET_BE16(key->key_info); + key_data_length = WPA_GET_BE16(key->key_data_length); + if (key_data_length > data_len - sizeof(*hdr) - sizeof(*key)) { + wpa_printf(MSG_INFO, "WPA: Invalid EAPOL-Key frame - " + "key_data overflow (%d > %lu)", + key_data_length, + (unsigned long) (data_len - sizeof(*hdr) - + sizeof(*key))); + return; + } + + if (sm->wpa == WPA_VERSION_WPA2) { + if (key->type != EAPOL_KEY_TYPE_RSN) { + wpa_printf(MSG_DEBUG, "Ignore EAPOL-Key with " + "unexpected type %d in RSN mode", + key->type); + return; + } + } else { + if (key->type != EAPOL_KEY_TYPE_WPA) { + wpa_printf(MSG_DEBUG, "Ignore EAPOL-Key with " + "unexpected type %d in WPA mode", + key->type); + return; + } + } + + /* FIX: verify that the EAPOL-Key frame was encrypted if pairwise keys + * are set */ + + if ((key_info & (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) == + (WPA_KEY_INFO_SMK_MESSAGE | WPA_KEY_INFO_REQUEST)) { + if (key_info & WPA_KEY_INFO_ERROR) { + msg = SMK_ERROR; + msgtxt = "SMK Error"; + } else { + msg = SMK_M1; + msgtxt = "SMK M1"; + } + } else if (key_info & WPA_KEY_INFO_SMK_MESSAGE) { + msg = SMK_M3; + msgtxt = "SMK M3"; + } else if (key_info & WPA_KEY_INFO_REQUEST) { + msg = REQUEST; + msgtxt = "Request"; + } else if (!(key_info & WPA_KEY_INFO_KEY_TYPE)) { + msg = GROUP_2; + msgtxt = "2/2 Group"; + } else if (key_data_length == 0) { + msg = PAIRWISE_4; + msgtxt = "4/4 Pairwise"; + } else { + msg = PAIRWISE_2; + msgtxt = "2/4 Pairwise"; + } + + /* TODO: key_info type validation for PeerKey */ + if (msg == REQUEST || msg == PAIRWISE_2 || msg == PAIRWISE_4 || + msg == GROUP_2) { + u16 ver = key_info & WPA_KEY_INFO_TYPE_MASK; + if (sm->pairwise == WPA_CIPHER_CCMP) { + if (wpa_use_aes_cmac(sm) && + ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) { + wpa_auth_logger(wpa_auth, sm->addr, + LOGGER_WARNING, + "advertised support for " + "AES-128-CMAC, but did not " + "use it"); + return; + } + + if (!wpa_use_aes_cmac(sm) && + ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { + wpa_auth_logger(wpa_auth, sm->addr, + LOGGER_WARNING, + "did not use HMAC-SHA1-AES " + "with CCMP"); + return; + } + } + } + + if (key_info & WPA_KEY_INFO_REQUEST) { + if (sm->req_replay_counter_used && + os_memcmp(key->replay_counter, sm->req_replay_counter, + WPA_REPLAY_COUNTER_LEN) <= 0) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_WARNING, + "received EAPOL-Key request with " + "replayed counter"); + return; + } + } + + if (!(key_info & WPA_KEY_INFO_REQUEST) && + !wpa_replay_counter_valid(sm, key->replay_counter)) { + int i; + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key %s with unexpected " + "replay counter", msgtxt); + for (i = 0; i < RSNA_MAX_EAPOL_RETRIES; i++) { + if (!sm->key_replay[i].valid) + break; + wpa_hexdump(MSG_DEBUG, "pending replay counter", + sm->key_replay[i].counter, + WPA_REPLAY_COUNTER_LEN); + } + wpa_hexdump(MSG_DEBUG, "received replay counter", + key->replay_counter, WPA_REPLAY_COUNTER_LEN); + return; + } + + switch (msg) { + case PAIRWISE_2: + if (sm->wpa_ptk_state != WPA_PTK_PTKSTART && + sm->wpa_ptk_state != WPA_PTK_PTKCALCNEGOTIATING) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key msg 2/4 in " + "invalid state (%d) - dropped", + sm->wpa_ptk_state); + return; + } + if (wpa_parse_kde_ies((u8 *) (key + 1), key_data_length, + &kde) < 0) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key msg 2/4 with " + "invalid Key Data contents"); + return; + } + if (kde.rsn_ie) { + eapol_key_ie = kde.rsn_ie; + eapol_key_ie_len = kde.rsn_ie_len; + } else { + eapol_key_ie = kde.wpa_ie; + eapol_key_ie_len = kde.wpa_ie_len; + } + ft = sm->wpa == WPA_VERSION_WPA2 && + wpa_key_mgmt_ft(sm->wpa_key_mgmt); + if (sm->wpa_ie == NULL || + wpa_compare_rsn_ie(ft, + sm->wpa_ie, sm->wpa_ie_len, + eapol_key_ie, eapol_key_ie_len)) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "WPA IE from (Re)AssocReq did not " + "match with msg 2/4"); + if (sm->wpa_ie) { + wpa_hexdump(MSG_DEBUG, "WPA IE in AssocReq", + sm->wpa_ie, sm->wpa_ie_len); + } + wpa_hexdump(MSG_DEBUG, "WPA IE in msg 2/4", + eapol_key_ie, eapol_key_ie_len); + /* MLME-DEAUTHENTICATE.request */ + wpa_sta_disconnect(wpa_auth, sm->addr); + return; + } +#ifdef CONFIG_IEEE80211R + if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) { + wpa_sta_disconnect(wpa_auth, sm->addr); + return; + } +#endif /* CONFIG_IEEE80211R */ + break; + case PAIRWISE_4: + if (sm->wpa_ptk_state != WPA_PTK_PTKINITNEGOTIATING || + !sm->PTK_valid) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key msg 4/4 in " + "invalid state (%d) - dropped", + sm->wpa_ptk_state); + return; + } + break; + case GROUP_2: + if (sm->wpa_ptk_group_state != WPA_PTK_GROUP_REKEYNEGOTIATING + || !sm->PTK_valid) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key msg 2/2 in " + "invalid state (%d) - dropped", + sm->wpa_ptk_group_state); + return; + } + break; +#ifdef CONFIG_PEERKEY + case SMK_M1: + case SMK_M3: + case SMK_ERROR: + if (!wpa_auth->conf.peerkey) { + wpa_printf(MSG_DEBUG, "RSN: SMK M1/M3/Error, but " + "PeerKey use disabled - ignoring message"); + return; + } + if (!sm->PTK_valid) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key msg SMK in " + "invalid state - dropped"); + return; + } + break; +#else /* CONFIG_PEERKEY */ + case SMK_M1: + case SMK_M3: + case SMK_ERROR: + return; /* STSL disabled - ignore SMK messages */ +#endif /* CONFIG_PEERKEY */ + case REQUEST: + break; + } + + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "received EAPOL-Key frame (%s)", msgtxt); + + if (key_info & WPA_KEY_INFO_ACK) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received invalid EAPOL-Key: Key Ack set"); + return; + } + + if (!(key_info & WPA_KEY_INFO_MIC)) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received invalid EAPOL-Key: Key MIC not set"); + return; + } + + sm->MICVerified = FALSE; + if (sm->PTK_valid) { + if (wpa_verify_key_mic(&sm->PTK, data, data_len)) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key with invalid MIC"); + return; + } + sm->MICVerified = TRUE; + eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm); + } + + if (key_info & WPA_KEY_INFO_REQUEST) { + if (sm->MICVerified) { + sm->req_replay_counter_used = 1; + os_memcpy(sm->req_replay_counter, key->replay_counter, + WPA_REPLAY_COUNTER_LEN); + } else { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key request with " + "invalid MIC"); + return; + } + + /* + * TODO: should decrypt key data field if encryption was used; + * even though MAC address KDE is not normally encrypted, + * supplicant is allowed to encrypt it. + */ + if (msg == SMK_ERROR) { +#ifdef CONFIG_PEERKEY + wpa_smk_error(wpa_auth, sm, key); +#endif /* CONFIG_PEERKEY */ + return; + } else if (key_info & WPA_KEY_INFO_ERROR) { + /* Supplicant reported a Michael MIC error */ + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key Error Request " + "(STA detected Michael MIC failure)"); + wpa_auth_mic_failure_report(wpa_auth, sm->addr); + sm->dot11RSNAStatsTKIPRemoteMICFailures++; + wpa_auth->dot11RSNAStatsTKIPRemoteMICFailures++; + /* Error report is not a request for a new key + * handshake, but since Authenticator may do it, let's + * change the keys now anyway. */ + wpa_request_new_ptk(sm); + } else if (key_info & WPA_KEY_INFO_KEY_TYPE) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key Request for new " + "4-Way Handshake"); + wpa_request_new_ptk(sm); +#ifdef CONFIG_PEERKEY + } else if (msg == SMK_M1) { + wpa_smk_m1(wpa_auth, sm, key); +#endif /* CONFIG_PEERKEY */ + } else if (key_data_length > 0 && + wpa_parse_kde_ies((const u8 *) (key + 1), + key_data_length, &kde) == 0 && + kde.mac_addr) { + } else { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO, + "received EAPOL-Key Request for GTK " + "rekeying"); + /* FIX: why was this triggering PTK rekeying for the + * STA that requested Group Key rekeying?? */ + /* wpa_request_new_ptk(sta->wpa_sm); */ + eloop_cancel_timeout(wpa_rekey_gtk, wpa_auth, NULL); + wpa_rekey_gtk(wpa_auth, NULL); + } + } else { + /* Do not allow the same key replay counter to be reused. This + * does also invalidate all other pending replay counters if + * retransmissions were used, i.e., we will only process one of + * the pending replies and ignore rest if more than one is + * received. */ + sm->key_replay[0].valid = FALSE; + } + +#ifdef CONFIG_PEERKEY + if (msg == SMK_M3) { + wpa_smk_m3(wpa_auth, sm, key); + return; + } +#endif /* CONFIG_PEERKEY */ + + os_free(sm->last_rx_eapol_key); + sm->last_rx_eapol_key = os_malloc(data_len); + if (sm->last_rx_eapol_key == NULL) + return; + os_memcpy(sm->last_rx_eapol_key, data, data_len); + sm->last_rx_eapol_key_len = data_len; + + sm->EAPOLKeyReceived = TRUE; + sm->EAPOLKeyPairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE); + sm->EAPOLKeyRequest = !!(key_info & WPA_KEY_INFO_REQUEST); + os_memcpy(sm->SNonce, key->key_nonce, WPA_NONCE_LEN); + wpa_sm_step(sm); +} + + +static void wpa_gmk_to_gtk(const u8 *gmk, const u8 *addr, const u8 *gnonce, + u8 *gtk, size_t gtk_len) +{ + u8 data[ETH_ALEN + WPA_NONCE_LEN]; + + /* GTK = PRF-X(GMK, "Group key expansion", AA || GNonce) */ + os_memcpy(data, addr, ETH_ALEN); + os_memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN); + +#ifdef CONFIG_IEEE80211W + sha256_prf(gmk, WPA_GMK_LEN, "Group key expansion", + data, sizeof(data), gtk, gtk_len); +#else /* CONFIG_IEEE80211W */ + sha1_prf(gmk, WPA_GMK_LEN, "Group key expansion", + data, sizeof(data), gtk, gtk_len); +#endif /* CONFIG_IEEE80211W */ + + wpa_hexdump_key(MSG_DEBUG, "GMK", gmk, WPA_GMK_LEN); + wpa_hexdump_key(MSG_DEBUG, "GTK", gtk, gtk_len); +} + + +static void wpa_send_eapol_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_authenticator *wpa_auth = eloop_ctx; + struct wpa_state_machine *sm = timeout_ctx; + + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, "EAPOL-Key timeout"); + sm->TimeoutEvt = TRUE; + wpa_sm_step(sm); +} + + +void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, int key_info, + const u8 *key_rsc, const u8 *nonce, + const u8 *kde, size_t kde_len, + int keyidx, int encr, int force_version) +{ + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *key; + size_t len; + int alg; + int key_data_len, pad_len = 0; + u8 *buf, *pos; + int version, pairwise; + int i; + + len = sizeof(struct ieee802_1x_hdr) + sizeof(struct wpa_eapol_key); + + if (force_version) + version = force_version; + else if (wpa_use_aes_cmac(sm)) + version = WPA_KEY_INFO_TYPE_AES_128_CMAC; + else if (sm->pairwise == WPA_CIPHER_CCMP) + version = WPA_KEY_INFO_TYPE_HMAC_SHA1_AES; + else + version = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4; + + pairwise = key_info & WPA_KEY_INFO_KEY_TYPE; + + wpa_printf(MSG_DEBUG, "WPA: Send EAPOL(version=%d secure=%d mic=%d " + "ack=%d install=%d pairwise=%d kde_len=%lu keyidx=%d " + "encr=%d)", + version, + (key_info & WPA_KEY_INFO_SECURE) ? 1 : 0, + (key_info & WPA_KEY_INFO_MIC) ? 1 : 0, + (key_info & WPA_KEY_INFO_ACK) ? 1 : 0, + (key_info & WPA_KEY_INFO_INSTALL) ? 1 : 0, + pairwise, (unsigned long) kde_len, keyidx, encr); + + key_data_len = kde_len; + + if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || + version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) { + pad_len = key_data_len % 8; + if (pad_len) + pad_len = 8 - pad_len; + key_data_len += pad_len + 8; + } + + len += key_data_len; + + hdr = os_zalloc(len); + if (hdr == NULL) + return; + hdr->version = wpa_auth->conf.eapol_version; + hdr->type = IEEE802_1X_TYPE_EAPOL_KEY; + hdr->length = host_to_be16(len - sizeof(*hdr)); + key = (struct wpa_eapol_key *) (hdr + 1); + + key->type = sm->wpa == WPA_VERSION_WPA2 ? + EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; + key_info |= version; + if (encr && sm->wpa == WPA_VERSION_WPA2) + key_info |= WPA_KEY_INFO_ENCR_KEY_DATA; + if (sm->wpa != WPA_VERSION_WPA2) + key_info |= keyidx << WPA_KEY_INFO_KEY_INDEX_SHIFT; + WPA_PUT_BE16(key->key_info, key_info); + + alg = pairwise ? sm->pairwise : wpa_auth->conf.wpa_group; + switch (alg) { + case WPA_CIPHER_CCMP: + WPA_PUT_BE16(key->key_length, 16); + break; + case WPA_CIPHER_TKIP: + WPA_PUT_BE16(key->key_length, 32); + break; + case WPA_CIPHER_WEP40: + WPA_PUT_BE16(key->key_length, 5); + break; + case WPA_CIPHER_WEP104: + WPA_PUT_BE16(key->key_length, 13); + break; + } + if (key_info & WPA_KEY_INFO_SMK_MESSAGE) + WPA_PUT_BE16(key->key_length, 0); + + /* FIX: STSL: what to use as key_replay_counter? */ + for (i = RSNA_MAX_EAPOL_RETRIES - 1; i > 0; i--) { + sm->key_replay[i].valid = sm->key_replay[i - 1].valid; + os_memcpy(sm->key_replay[i].counter, + sm->key_replay[i - 1].counter, + WPA_REPLAY_COUNTER_LEN); + } + inc_byte_array(sm->key_replay[0].counter, WPA_REPLAY_COUNTER_LEN); + os_memcpy(key->replay_counter, sm->key_replay[0].counter, + WPA_REPLAY_COUNTER_LEN); + sm->key_replay[0].valid = TRUE; + + if (nonce) + os_memcpy(key->key_nonce, nonce, WPA_NONCE_LEN); + + if (key_rsc) + os_memcpy(key->key_rsc, key_rsc, WPA_KEY_RSC_LEN); + + if (kde && !encr) { + os_memcpy(key + 1, kde, kde_len); + WPA_PUT_BE16(key->key_data_length, kde_len); + } else if (encr && kde) { + buf = os_zalloc(key_data_len); + if (buf == NULL) { + os_free(hdr); + return; + } + pos = buf; + os_memcpy(pos, kde, kde_len); + pos += kde_len; + + if (pad_len) + *pos++ = 0xdd; + + wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data", + buf, key_data_len); + if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || + version == WPA_KEY_INFO_TYPE_AES_128_CMAC) { + if (aes_wrap(sm->PTK.kek, (key_data_len - 8) / 8, buf, + (u8 *) (key + 1))) { + os_free(hdr); + os_free(buf); + return; + } + WPA_PUT_BE16(key->key_data_length, key_data_len); + } else { + u8 ek[32]; + os_memcpy(key->key_iv, + sm->group->Counter + WPA_NONCE_LEN - 16, 16); + inc_byte_array(sm->group->Counter, WPA_NONCE_LEN); + os_memcpy(ek, key->key_iv, 16); + os_memcpy(ek + 16, sm->PTK.kek, 16); + os_memcpy(key + 1, buf, key_data_len); + rc4_skip(ek, 32, 256, (u8 *) (key + 1), key_data_len); + WPA_PUT_BE16(key->key_data_length, key_data_len); + } + os_free(buf); + } + + if (key_info & WPA_KEY_INFO_MIC) { + if (!sm->PTK_valid) { + wpa_auth_logger(wpa_auth, sm->addr, LOGGER_DEBUG, + "PTK not valid when sending EAPOL-Key " + "frame"); + os_free(hdr); + return; + } + wpa_eapol_key_mic(sm->PTK.kck, version, (u8 *) hdr, len, + key->key_mic); + } + + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_inc_EapolFramesTx, + 1); + wpa_auth_send_eapol(wpa_auth, sm->addr, (u8 *) hdr, len, + sm->pairwise_set); + os_free(hdr); +} + + +static void wpa_send_eapol(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, int key_info, + const u8 *key_rsc, const u8 *nonce, + const u8 *kde, size_t kde_len, + int keyidx, int encr) +{ + int timeout_ms; + int pairwise = key_info & WPA_KEY_INFO_KEY_TYPE; + int ctr; + + if (sm == NULL) + return; + + __wpa_send_eapol(wpa_auth, sm, key_info, key_rsc, nonce, kde, kde_len, + keyidx, encr, 0); + + ctr = pairwise ? sm->TimeoutCtr : sm->GTimeoutCtr; + if (ctr == 1) + timeout_ms = eapol_key_timeout_first; + else + timeout_ms = eapol_key_timeout_subseq; + eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000, + wpa_send_eapol_timeout, wpa_auth, sm); +} + + +static int wpa_verify_key_mic(struct wpa_ptk *PTK, u8 *data, size_t data_len) +{ + struct ieee802_1x_hdr *hdr; + struct wpa_eapol_key *key; + u16 key_info; + int ret = 0; + u8 mic[16]; + + if (data_len < sizeof(*hdr) + sizeof(*key)) + return -1; + + hdr = (struct ieee802_1x_hdr *) data; + key = (struct wpa_eapol_key *) (hdr + 1); + key_info = WPA_GET_BE16(key->key_info); + os_memcpy(mic, key->key_mic, 16); + os_memset(key->key_mic, 0, 16); + if (wpa_eapol_key_mic(PTK->kck, key_info & WPA_KEY_INFO_TYPE_MASK, + data, data_len, key->key_mic) || + os_memcmp(mic, key->key_mic, 16) != 0) + ret = -1; + os_memcpy(key->key_mic, mic, 16); + return ret; +} + + +void wpa_remove_ptk(struct wpa_state_machine *sm) +{ + sm->PTK_valid = FALSE; + os_memset(&sm->PTK, 0, sizeof(sm->PTK)); + wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 0, (u8 *) "", + 0); + sm->pairwise_set = FALSE; + eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); +} + + +int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event) +{ + int remove_ptk = 1; + + if (sm == NULL) + return -1; + + wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "event %d notification", event); + + switch (event) { + case WPA_AUTH: + case WPA_ASSOC: + break; + case WPA_DEAUTH: + case WPA_DISASSOC: + sm->DeauthenticationRequest = TRUE; + break; + case WPA_REAUTH: + case WPA_REAUTH_EAPOL: + if (!sm->started) { + /* + * When using WPS, we may end up here if the STA + * manages to re-associate without the previous STA + * entry getting removed. Consequently, we need to make + * sure that the WPA state machines gets initialized + * properly at this point. + */ + wpa_printf(MSG_DEBUG, "WPA state machine had not been " + "started - initialize now"); + sm->started = 1; + sm->Init = TRUE; + if (wpa_sm_step(sm) == 1) + return 1; /* should not really happen */ + sm->Init = FALSE; + sm->AuthenticationRequest = TRUE; + break; + } + if (sm->GUpdateStationKeys) { + /* + * Reauthentication cancels the pending group key + * update for this STA. + */ + sm->group->GKeyDoneStations--; + sm->GUpdateStationKeys = FALSE; + sm->PtkGroupInit = TRUE; + } + sm->ReAuthenticationRequest = TRUE; + break; + case WPA_ASSOC_FT: +#ifdef CONFIG_IEEE80211R + wpa_printf(MSG_DEBUG, "FT: Retry PTK configuration " + "after association"); + wpa_ft_install_ptk(sm); + + /* Using FT protocol, not WPA auth state machine */ + sm->ft_completed = 1; + return 0; +#else /* CONFIG_IEEE80211R */ + break; +#endif /* CONFIG_IEEE80211R */ + } + +#ifdef CONFIG_IEEE80211R + sm->ft_completed = 0; +#endif /* CONFIG_IEEE80211R */ + +#ifdef CONFIG_IEEE80211W + if (sm->mgmt_frame_prot && event == WPA_AUTH) + remove_ptk = 0; +#endif /* CONFIG_IEEE80211W */ + + if (remove_ptk) { + sm->PTK_valid = FALSE; + os_memset(&sm->PTK, 0, sizeof(sm->PTK)); + + if (event != WPA_REAUTH_EAPOL) + wpa_remove_ptk(sm); + } + + return wpa_sm_step(sm); +} + + +static enum wpa_alg wpa_alg_enum(int alg) +{ + switch (alg) { + case WPA_CIPHER_CCMP: + return WPA_ALG_CCMP; + case WPA_CIPHER_TKIP: + return WPA_ALG_TKIP; + case WPA_CIPHER_WEP104: + case WPA_CIPHER_WEP40: + return WPA_ALG_WEP; + default: + return WPA_ALG_NONE; + } +} + + +SM_STATE(WPA_PTK, INITIALIZE) +{ + SM_ENTRY_MA(WPA_PTK, INITIALIZE, wpa_ptk); + if (sm->Init) { + /* Init flag is not cleared here, so avoid busy + * loop by claiming nothing changed. */ + sm->changed = FALSE; + } + + sm->keycount = 0; + if (sm->GUpdateStationKeys) + sm->group->GKeyDoneStations--; + sm->GUpdateStationKeys = FALSE; + if (sm->wpa == WPA_VERSION_WPA) + sm->PInitAKeys = FALSE; + if (1 /* Unicast cipher supported AND (ESS OR ((IBSS or WDS) and + * Local AA > Remote AA)) */) { + sm->Pair = TRUE; + } + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 0); + wpa_remove_ptk(sm); + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, 0); + sm->TimeoutCtr = 0; + if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_authorized, 0); + } +} + + +SM_STATE(WPA_PTK, DISCONNECT) +{ + SM_ENTRY_MA(WPA_PTK, DISCONNECT, wpa_ptk); + sm->Disconnect = FALSE; + wpa_sta_disconnect(sm->wpa_auth, sm->addr); +} + + +SM_STATE(WPA_PTK, DISCONNECTED) +{ + SM_ENTRY_MA(WPA_PTK, DISCONNECTED, wpa_ptk); + sm->DeauthenticationRequest = FALSE; +} + + +SM_STATE(WPA_PTK, AUTHENTICATION) +{ + SM_ENTRY_MA(WPA_PTK, AUTHENTICATION, wpa_ptk); + os_memset(&sm->PTK, 0, sizeof(sm->PTK)); + sm->PTK_valid = FALSE; + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portControl_Auto, + 1); + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portEnabled, 1); + sm->AuthenticationRequest = FALSE; +} + + +SM_STATE(WPA_PTK, AUTHENTICATION2) +{ + SM_ENTRY_MA(WPA_PTK, AUTHENTICATION2, wpa_ptk); + os_memcpy(sm->ANonce, sm->group->Counter, WPA_NONCE_LEN); + inc_byte_array(sm->group->Counter, WPA_NONCE_LEN); + sm->ReAuthenticationRequest = FALSE; + /* IEEE 802.11i does not clear TimeoutCtr here, but this is more + * logical place than INITIALIZE since AUTHENTICATION2 can be + * re-entered on ReAuthenticationRequest without going through + * INITIALIZE. */ + sm->TimeoutCtr = 0; +} + + +SM_STATE(WPA_PTK, INITPMK) +{ + u8 msk[2 * PMK_LEN]; + size_t len = 2 * PMK_LEN; + + SM_ENTRY_MA(WPA_PTK, INITPMK, wpa_ptk); +#ifdef CONFIG_IEEE80211R + sm->xxkey_len = 0; +#endif /* CONFIG_IEEE80211R */ + if (sm->pmksa) { + wpa_printf(MSG_DEBUG, "WPA: PMK from PMKSA cache"); + os_memcpy(sm->PMK, sm->pmksa->pmk, PMK_LEN); + } else if (wpa_auth_get_msk(sm->wpa_auth, sm->addr, msk, &len) == 0) { + wpa_printf(MSG_DEBUG, "WPA: PMK from EAPOL state machine " + "(len=%lu)", (unsigned long) len); + os_memcpy(sm->PMK, msk, PMK_LEN); +#ifdef CONFIG_IEEE80211R + if (len >= 2 * PMK_LEN) { + os_memcpy(sm->xxkey, msk + PMK_LEN, PMK_LEN); + sm->xxkey_len = PMK_LEN; + } +#endif /* CONFIG_IEEE80211R */ + } else { + wpa_printf(MSG_DEBUG, "WPA: Could not get PMK"); + } + + sm->req_replay_counter_used = 0; + /* IEEE 802.11i does not set keyRun to FALSE, but not doing this + * will break reauthentication since EAPOL state machines may not be + * get into AUTHENTICATING state that clears keyRun before WPA state + * machine enters AUTHENTICATION2 state and goes immediately to INITPMK + * state and takes PMK from the previously used AAA Key. This will + * eventually fail in 4-Way Handshake because Supplicant uses PMK + * derived from the new AAA Key. Setting keyRun = FALSE here seems to + * be good workaround for this issue. */ + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyRun, 0); +} + + +SM_STATE(WPA_PTK, INITPSK) +{ + const u8 *psk; + SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk); + psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, NULL); + if (psk) { + os_memcpy(sm->PMK, psk, PMK_LEN); +#ifdef CONFIG_IEEE80211R + os_memcpy(sm->xxkey, psk, PMK_LEN); + sm->xxkey_len = PMK_LEN; +#endif /* CONFIG_IEEE80211R */ + } + sm->req_replay_counter_used = 0; +} + + +SM_STATE(WPA_PTK, PTKSTART) +{ + u8 buf[2 + RSN_SELECTOR_LEN + PMKID_LEN], *pmkid = NULL; + size_t pmkid_len = 0; + + SM_ENTRY_MA(WPA_PTK, PTKSTART, wpa_ptk); + sm->PTKRequest = FALSE; + sm->TimeoutEvt = FALSE; + + sm->TimeoutCtr++; + if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { + /* No point in sending the EAPOL-Key - we will disconnect + * immediately following this. */ + return; + } + + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "sending 1/4 msg of 4-Way Handshake"); + /* + * TODO: Could add PMKID even with WPA2-PSK, but only if there is only + * one possible PSK for this STA. + */ + if (sm->wpa == WPA_VERSION_WPA2 && + wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt)) { + pmkid = buf; + pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN; + pmkid[0] = WLAN_EID_VENDOR_SPECIFIC; + pmkid[1] = RSN_SELECTOR_LEN + PMKID_LEN; + RSN_SELECTOR_PUT(&pmkid[2], RSN_KEY_DATA_PMKID); + if (sm->pmksa) + os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN], + sm->pmksa->pmkid, PMKID_LEN); + else { + /* + * Calculate PMKID since no PMKSA cache entry was + * available with pre-calculated PMKID. + */ + rsn_pmkid(sm->PMK, PMK_LEN, sm->wpa_auth->addr, + sm->addr, &pmkid[2 + RSN_SELECTOR_LEN], + wpa_key_mgmt_sha256(sm->wpa_key_mgmt)); + } + } + wpa_send_eapol(sm->wpa_auth, sm, + WPA_KEY_INFO_ACK | WPA_KEY_INFO_KEY_TYPE, NULL, + sm->ANonce, pmkid, pmkid_len, 0, 0); +} + + +static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *pmk, + struct wpa_ptk *ptk) +{ + size_t ptk_len = sm->pairwise == WPA_CIPHER_CCMP ? 48 : 64; +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) + return wpa_auth_derive_ptk_ft(sm, pmk, ptk, ptk_len); +#endif /* CONFIG_IEEE80211R */ + + wpa_pmk_to_ptk(pmk, PMK_LEN, "Pairwise key expansion", + sm->wpa_auth->addr, sm->addr, sm->ANonce, sm->SNonce, + (u8 *) ptk, ptk_len, + wpa_key_mgmt_sha256(sm->wpa_key_mgmt)); + + return 0; +} + + +SM_STATE(WPA_PTK, PTKCALCNEGOTIATING) +{ + struct wpa_ptk PTK; + int ok = 0; + const u8 *pmk = NULL; + + SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk); + sm->EAPOLKeyReceived = FALSE; + + /* WPA with IEEE 802.1X: use the derived PMK from EAP + * WPA-PSK: iterate through possible PSKs and select the one matching + * the packet */ + for (;;) { + if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { + pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, pmk); + if (pmk == NULL) + break; + } else + pmk = sm->PMK; + + wpa_derive_ptk(sm, pmk, &PTK); + + if (wpa_verify_key_mic(&PTK, sm->last_rx_eapol_key, + sm->last_rx_eapol_key_len) == 0) { + ok = 1; + break; + } + + if (!wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) + break; + } + + if (!ok) { + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "invalid MIC in msg 2/4 of 4-Way Handshake"); + return; + } + +#ifdef CONFIG_IEEE80211R + if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { + /* + * Verify that PMKR1Name from EAPOL-Key message 2/4 matches + * with the value we derived. + */ + if (os_memcmp(sm->sup_pmk_r1_name, sm->pmk_r1_name, + WPA_PMK_NAME_LEN) != 0) { + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "PMKR1Name mismatch in FT 4-way " + "handshake"); + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from " + "Supplicant", + sm->sup_pmk_r1_name, WPA_PMK_NAME_LEN); + wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name", + sm->pmk_r1_name, WPA_PMK_NAME_LEN); + return; + } + } +#endif /* CONFIG_IEEE80211R */ + + eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm); + + if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { + /* PSK may have changed from the previous choice, so update + * state machine data based on whatever PSK was selected here. + */ + os_memcpy(sm->PMK, pmk, PMK_LEN); + } + + sm->MICVerified = TRUE; + + os_memcpy(&sm->PTK, &PTK, sizeof(PTK)); + sm->PTK_valid = TRUE; +} + + +SM_STATE(WPA_PTK, PTKCALCNEGOTIATING2) +{ + SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING2, wpa_ptk); + sm->TimeoutCtr = 0; +} + + +#ifdef CONFIG_IEEE80211W + +static int ieee80211w_kde_len(struct wpa_state_machine *sm) +{ + if (sm->mgmt_frame_prot) { + return 2 + RSN_SELECTOR_LEN + sizeof(struct wpa_igtk_kde); + } + + return 0; +} + + +static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) +{ + struct wpa_igtk_kde igtk; + struct wpa_group *gsm = sm->group; + + if (!sm->mgmt_frame_prot) + return pos; + + igtk.keyid[0] = gsm->GN_igtk; + igtk.keyid[1] = 0; + if (gsm->wpa_group_state != WPA_GROUP_SETKEYSDONE || + wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, igtk.pn) < 0) + os_memset(igtk.pn, 0, sizeof(igtk.pn)); + os_memcpy(igtk.igtk, gsm->IGTK[gsm->GN_igtk - 4], WPA_IGTK_LEN); + pos = wpa_add_kde(pos, RSN_KEY_DATA_IGTK, + (const u8 *) &igtk, sizeof(igtk), NULL, 0); + + return pos; +} + +#else /* CONFIG_IEEE80211W */ + +static int ieee80211w_kde_len(struct wpa_state_machine *sm) +{ + return 0; +} + + +static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos) +{ + return pos; +} + +#endif /* CONFIG_IEEE80211W */ + + +SM_STATE(WPA_PTK, PTKINITNEGOTIATING) +{ + u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos; + size_t gtk_len, kde_len; + struct wpa_group *gsm = sm->group; + u8 *wpa_ie; + int wpa_ie_len, secure, keyidx, encr = 0; + + SM_ENTRY_MA(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk); + sm->TimeoutEvt = FALSE; + + sm->TimeoutCtr++; + if (sm->TimeoutCtr > (int) dot11RSNAConfigPairwiseUpdateCount) { + /* No point in sending the EAPOL-Key - we will disconnect + * immediately following this. */ + return; + } + + /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE], + GTK[GN], IGTK, [FTIE], [TIE * 2]) + */ + os_memset(rsc, 0, WPA_KEY_RSC_LEN); + wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc); + /* If FT is used, wpa_auth->wpa_ie includes both RSNIE and MDIE */ + wpa_ie = sm->wpa_auth->wpa_ie; + wpa_ie_len = sm->wpa_auth->wpa_ie_len; + if (sm->wpa == WPA_VERSION_WPA && + (sm->wpa_auth->conf.wpa & WPA_PROTO_RSN) && + wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) { + /* WPA-only STA, remove RSN IE */ + wpa_ie = wpa_ie + wpa_ie[1] + 2; + wpa_ie_len = wpa_ie[1] + 2; + } + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "sending 3/4 msg of 4-Way Handshake"); + if (sm->wpa == WPA_VERSION_WPA2) { + /* WPA2 send GTK in the 4-way handshake */ + secure = 1; + gtk = gsm->GTK[gsm->GN - 1]; + gtk_len = gsm->GTK_len; + keyidx = gsm->GN; + _rsc = rsc; + encr = 1; + } else { + /* WPA does not include GTK in msg 3/4 */ + secure = 0; + gtk = NULL; + gtk_len = 0; + keyidx = 0; + _rsc = NULL; + } + + kde_len = wpa_ie_len + ieee80211w_kde_len(sm); + if (gtk) + kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len; +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { + kde_len += 2 + PMKID_LEN; /* PMKR1Name into RSN IE */ + kde_len += 300; /* FTIE + 2 * TIE */ + } +#endif /* CONFIG_IEEE80211R */ + kde = os_malloc(kde_len); + if (kde == NULL) + return; + + pos = kde; + os_memcpy(pos, wpa_ie, wpa_ie_len); + pos += wpa_ie_len; +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { + int res = wpa_insert_pmkid(kde, pos - kde, sm->pmk_r1_name); + if (res < 0) { + wpa_printf(MSG_ERROR, "FT: Failed to insert " + "PMKR1Name into RSN IE in EAPOL-Key data"); + os_free(kde); + return; + } + pos += res; + } +#endif /* CONFIG_IEEE80211R */ + if (gtk) { + u8 hdr[2]; + hdr[0] = keyidx & 0x03; + hdr[1] = 0; + pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, + gtk, gtk_len); + } + pos = ieee80211w_kde_add(sm, pos); + +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { + int res; + struct wpa_auth_config *conf; + + conf = &sm->wpa_auth->conf; + res = wpa_write_ftie(conf, conf->r0_key_holder, + conf->r0_key_holder_len, + NULL, NULL, pos, kde + kde_len - pos, + NULL, 0); + if (res < 0) { + wpa_printf(MSG_ERROR, "FT: Failed to insert FTIE " + "into EAPOL-Key Key Data"); + os_free(kde); + return; + } + pos += res; + + /* TIE[ReassociationDeadline] (TU) */ + *pos++ = WLAN_EID_TIMEOUT_INTERVAL; + *pos++ = 5; + *pos++ = WLAN_TIMEOUT_REASSOC_DEADLINE; + WPA_PUT_LE32(pos, conf->reassociation_deadline); + pos += 4; + + /* TIE[KeyLifetime] (seconds) */ + *pos++ = WLAN_EID_TIMEOUT_INTERVAL; + *pos++ = 5; + *pos++ = WLAN_TIMEOUT_KEY_LIFETIME; + WPA_PUT_LE32(pos, conf->r0_key_lifetime * 60); + pos += 4; + } +#endif /* CONFIG_IEEE80211R */ + + wpa_send_eapol(sm->wpa_auth, sm, + (secure ? WPA_KEY_INFO_SECURE : 0) | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_ACK | WPA_KEY_INFO_INSTALL | + WPA_KEY_INFO_KEY_TYPE, + _rsc, sm->ANonce, kde, pos - kde, keyidx, encr); + os_free(kde); +} + + +SM_STATE(WPA_PTK, PTKINITDONE) +{ + SM_ENTRY_MA(WPA_PTK, PTKINITDONE, wpa_ptk); + sm->EAPOLKeyReceived = FALSE; + if (sm->Pair) { + enum wpa_alg alg; + int klen; + if (sm->pairwise == WPA_CIPHER_TKIP) { + alg = WPA_ALG_TKIP; + klen = 32; + } else { + alg = WPA_ALG_CCMP; + klen = 16; + } + if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0, + sm->PTK.tk1, klen)) { + wpa_sta_disconnect(sm->wpa_auth, sm->addr); + return; + } + /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */ + sm->pairwise_set = TRUE; + + if (sm->wpa_auth->conf.wpa_ptk_rekey) { + eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm); + eloop_register_timeout(sm->wpa_auth->conf. + wpa_ptk_rekey, 0, wpa_rekey_ptk, + sm->wpa_auth, sm); + } + + if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) { + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_authorized, 1); + } + } + + if (0 /* IBSS == TRUE */) { + sm->keycount++; + if (sm->keycount == 2) { + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_portValid, 1); + } + } else { + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_portValid, + 1); + } + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyAvailable, 0); + wpa_auth_set_eapol(sm->wpa_auth, sm->addr, WPA_EAPOL_keyDone, 1); + if (sm->wpa == WPA_VERSION_WPA) + sm->PInitAKeys = TRUE; + else + sm->has_GTK = TRUE; + wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "pairwise key handshake completed (%s)", + sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); + +#ifdef CONFIG_IEEE80211R + wpa_ft_push_pmk_r1(sm->wpa_auth, sm->addr); +#endif /* CONFIG_IEEE80211R */ +} + + +SM_STEP(WPA_PTK) +{ + struct wpa_authenticator *wpa_auth = sm->wpa_auth; + + if (sm->Init) + SM_ENTER(WPA_PTK, INITIALIZE); + else if (sm->Disconnect + /* || FIX: dot11RSNAConfigSALifetime timeout */) + SM_ENTER(WPA_PTK, DISCONNECT); + else if (sm->DeauthenticationRequest) + SM_ENTER(WPA_PTK, DISCONNECTED); + else if (sm->AuthenticationRequest) + SM_ENTER(WPA_PTK, AUTHENTICATION); + else if (sm->ReAuthenticationRequest) + SM_ENTER(WPA_PTK, AUTHENTICATION2); + else if (sm->PTKRequest) + SM_ENTER(WPA_PTK, PTKSTART); + else switch (sm->wpa_ptk_state) { + case WPA_PTK_INITIALIZE: + break; + case WPA_PTK_DISCONNECT: + SM_ENTER(WPA_PTK, DISCONNECTED); + break; + case WPA_PTK_DISCONNECTED: + SM_ENTER(WPA_PTK, INITIALIZE); + break; + case WPA_PTK_AUTHENTICATION: + SM_ENTER(WPA_PTK, AUTHENTICATION2); + break; + case WPA_PTK_AUTHENTICATION2: + if (wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) && + wpa_auth_get_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_keyRun) > 0) + SM_ENTER(WPA_PTK, INITPMK); + else if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) + /* FIX: && 802.1X::keyRun */) + SM_ENTER(WPA_PTK, INITPSK); + break; + case WPA_PTK_INITPMK: + if (wpa_auth_get_eapol(sm->wpa_auth, sm->addr, + WPA_EAPOL_keyAvailable) > 0) + SM_ENTER(WPA_PTK, PTKSTART); + else { + wpa_auth->dot11RSNA4WayHandshakeFailures++; + SM_ENTER(WPA_PTK, DISCONNECT); + } + break; + case WPA_PTK_INITPSK: + if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, NULL)) + SM_ENTER(WPA_PTK, PTKSTART); + else { + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "no PSK configured for the STA"); + wpa_auth->dot11RSNA4WayHandshakeFailures++; + SM_ENTER(WPA_PTK, DISCONNECT); + } + break; + case WPA_PTK_PTKSTART: + if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && + sm->EAPOLKeyPairwise) + SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); + else if (sm->TimeoutCtr > + (int) dot11RSNAConfigPairwiseUpdateCount) { + wpa_auth->dot11RSNA4WayHandshakeFailures++; + SM_ENTER(WPA_PTK, DISCONNECT); + } else if (sm->TimeoutEvt) + SM_ENTER(WPA_PTK, PTKSTART); + break; + case WPA_PTK_PTKCALCNEGOTIATING: + if (sm->MICVerified) + SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING2); + else if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && + sm->EAPOLKeyPairwise) + SM_ENTER(WPA_PTK, PTKCALCNEGOTIATING); + else if (sm->TimeoutEvt) + SM_ENTER(WPA_PTK, PTKSTART); + break; + case WPA_PTK_PTKCALCNEGOTIATING2: + SM_ENTER(WPA_PTK, PTKINITNEGOTIATING); + break; + case WPA_PTK_PTKINITNEGOTIATING: + if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && + sm->EAPOLKeyPairwise && sm->MICVerified) + SM_ENTER(WPA_PTK, PTKINITDONE); + else if (sm->TimeoutCtr > + (int) dot11RSNAConfigPairwiseUpdateCount) { + wpa_auth->dot11RSNA4WayHandshakeFailures++; + SM_ENTER(WPA_PTK, DISCONNECT); + } else if (sm->TimeoutEvt) + SM_ENTER(WPA_PTK, PTKINITNEGOTIATING); + break; + case WPA_PTK_PTKINITDONE: + break; + } +} + + +SM_STATE(WPA_PTK_GROUP, IDLE) +{ + SM_ENTRY_MA(WPA_PTK_GROUP, IDLE, wpa_ptk_group); + if (sm->Init) { + /* Init flag is not cleared here, so avoid busy + * loop by claiming nothing changed. */ + sm->changed = FALSE; + } + sm->GTimeoutCtr = 0; +} + + +SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING) +{ + u8 rsc[WPA_KEY_RSC_LEN]; + struct wpa_group *gsm = sm->group; + u8 *kde, *pos, hdr[2]; + size_t kde_len; + + SM_ENTRY_MA(WPA_PTK_GROUP, REKEYNEGOTIATING, wpa_ptk_group); + + sm->GTimeoutCtr++; + if (sm->GTimeoutCtr > (int) dot11RSNAConfigGroupUpdateCount) { + /* No point in sending the EAPOL-Key - we will disconnect + * immediately following this. */ + return; + } + + if (sm->wpa == WPA_VERSION_WPA) + sm->PInitAKeys = FALSE; + sm->TimeoutEvt = FALSE; + /* Send EAPOL(1, 1, 1, !Pair, G, RSC, GNonce, MIC(PTK), GTK[GN]) */ + os_memset(rsc, 0, WPA_KEY_RSC_LEN); + if (gsm->wpa_group_state == WPA_GROUP_SETKEYSDONE) + wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, rsc); + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "sending 1/2 msg of Group Key Handshake"); + + if (sm->wpa == WPA_VERSION_WPA2) { + kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len + + ieee80211w_kde_len(sm); + kde = os_malloc(kde_len); + if (kde == NULL) + return; + + pos = kde; + hdr[0] = gsm->GN & 0x03; + hdr[1] = 0; + pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2, + gsm->GTK[gsm->GN - 1], gsm->GTK_len); + pos = ieee80211w_kde_add(sm, pos); + } else { + kde = gsm->GTK[gsm->GN - 1]; + pos = kde + gsm->GTK_len; + } + + wpa_send_eapol(sm->wpa_auth, sm, + WPA_KEY_INFO_SECURE | WPA_KEY_INFO_MIC | + WPA_KEY_INFO_ACK | + (!sm->Pair ? WPA_KEY_INFO_INSTALL : 0), + rsc, gsm->GNonce, kde, pos - kde, gsm->GN, 1); + if (sm->wpa == WPA_VERSION_WPA2) + os_free(kde); +} + + +SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED) +{ + SM_ENTRY_MA(WPA_PTK_GROUP, REKEYESTABLISHED, wpa_ptk_group); + sm->EAPOLKeyReceived = FALSE; + if (sm->GUpdateStationKeys) + sm->group->GKeyDoneStations--; + sm->GUpdateStationKeys = FALSE; + sm->GTimeoutCtr = 0; + /* FIX: MLME.SetProtection.Request(TA, Tx_Rx) */ + wpa_auth_vlogger(sm->wpa_auth, sm->addr, LOGGER_INFO, + "group key handshake completed (%s)", + sm->wpa == WPA_VERSION_WPA ? "WPA" : "RSN"); + sm->has_GTK = TRUE; +} + + +SM_STATE(WPA_PTK_GROUP, KEYERROR) +{ + SM_ENTRY_MA(WPA_PTK_GROUP, KEYERROR, wpa_ptk_group); + if (sm->GUpdateStationKeys) + sm->group->GKeyDoneStations--; + sm->GUpdateStationKeys = FALSE; + sm->Disconnect = TRUE; +} + + +SM_STEP(WPA_PTK_GROUP) +{ + if (sm->Init || sm->PtkGroupInit) { + SM_ENTER(WPA_PTK_GROUP, IDLE); + sm->PtkGroupInit = FALSE; + } else switch (sm->wpa_ptk_group_state) { + case WPA_PTK_GROUP_IDLE: + if (sm->GUpdateStationKeys || + (sm->wpa == WPA_VERSION_WPA && sm->PInitAKeys)) + SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING); + break; + case WPA_PTK_GROUP_REKEYNEGOTIATING: + if (sm->EAPOLKeyReceived && !sm->EAPOLKeyRequest && + !sm->EAPOLKeyPairwise && sm->MICVerified) + SM_ENTER(WPA_PTK_GROUP, REKEYESTABLISHED); + else if (sm->GTimeoutCtr > + (int) dot11RSNAConfigGroupUpdateCount) + SM_ENTER(WPA_PTK_GROUP, KEYERROR); + else if (sm->TimeoutEvt) + SM_ENTER(WPA_PTK_GROUP, REKEYNEGOTIATING); + break; + case WPA_PTK_GROUP_KEYERROR: + SM_ENTER(WPA_PTK_GROUP, IDLE); + break; + case WPA_PTK_GROUP_REKEYESTABLISHED: + SM_ENTER(WPA_PTK_GROUP, IDLE); + break; + } +} + + +static int wpa_gtk_update(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + int ret = 0; + + /* FIX: is this the correct way of getting GNonce? */ + os_memcpy(group->GNonce, group->Counter, WPA_NONCE_LEN); + inc_byte_array(group->Counter, WPA_NONCE_LEN); + wpa_gmk_to_gtk(group->GMK, wpa_auth->addr, group->GNonce, + group->GTK[group->GN - 1], group->GTK_len); + +#ifdef CONFIG_IEEE80211W + if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) { + if (os_get_random(group->IGTK[group->GN_igtk - 4], + WPA_IGTK_LEN) < 0) { + wpa_printf(MSG_INFO, "RSN: Failed to get new random " + "IGTK"); + ret = -1; + } + wpa_hexdump_key(MSG_DEBUG, "IGTK", + group->IGTK[group->GN_igtk - 4], WPA_IGTK_LEN); + } +#endif /* CONFIG_IEEE80211W */ + + return ret; +} + + +static void wpa_group_gtk_init(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " + "GTK_INIT (VLAN-ID %d)", group->vlan_id); + group->changed = FALSE; /* GInit is not cleared here; avoid loop */ + group->wpa_group_state = WPA_GROUP_GTK_INIT; + + /* GTK[0..N] = 0 */ + os_memset(group->GTK, 0, sizeof(group->GTK)); + group->GN = 1; + group->GM = 2; +#ifdef CONFIG_IEEE80211W + group->GN_igtk = 4; + group->GM_igtk = 5; +#endif /* CONFIG_IEEE80211W */ + /* GTK[GN] = CalcGTK() */ + wpa_gtk_update(wpa_auth, group); +} + + +static int wpa_group_update_sta(struct wpa_state_machine *sm, void *ctx) +{ + if (sm->wpa_ptk_state != WPA_PTK_PTKINITDONE) { + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "Not in PTKINITDONE; skip Group Key update"); + return 0; + } + if (sm->GUpdateStationKeys) { + /* + * This should not really happen, but just in case, make sure + * we do not count the same STA twice in GKeyDoneStations. + */ + wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG, + "GUpdateStationKeys already set - do not " + "increment GKeyDoneStations"); + } else { + sm->group->GKeyDoneStations++; + sm->GUpdateStationKeys = TRUE; + } + wpa_sm_step(sm); + return 0; +} + + +static void wpa_group_setkeys(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + int tmp; + + wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " + "SETKEYS (VLAN-ID %d)", group->vlan_id); + group->changed = TRUE; + group->wpa_group_state = WPA_GROUP_SETKEYS; + group->GTKReKey = FALSE; + tmp = group->GM; + group->GM = group->GN; + group->GN = tmp; +#ifdef CONFIG_IEEE80211W + tmp = group->GM_igtk; + group->GM_igtk = group->GN_igtk; + group->GN_igtk = tmp; +#endif /* CONFIG_IEEE80211W */ + /* "GKeyDoneStations = GNoStations" is done in more robust way by + * counting the STAs that are marked with GUpdateStationKeys instead of + * including all STAs that could be in not-yet-completed state. */ + wpa_gtk_update(wpa_auth, group); + + wpa_auth_for_each_sta(wpa_auth, wpa_group_update_sta, NULL); + wpa_printf(MSG_DEBUG, "wpa_group_setkeys: GKeyDoneStations=%d", + group->GKeyDoneStations); +} + + +static void wpa_group_setkeysdone(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + wpa_printf(MSG_DEBUG, "WPA: group state machine entering state " + "SETKEYSDONE (VLAN-ID %d)", group->vlan_id); + group->changed = TRUE; + group->wpa_group_state = WPA_GROUP_SETKEYSDONE; + wpa_auth_set_key(wpa_auth, group->vlan_id, + wpa_alg_enum(wpa_auth->conf.wpa_group), + NULL, group->GN, group->GTK[group->GN - 1], + group->GTK_len); + +#ifdef CONFIG_IEEE80211W + if (wpa_auth->conf.ieee80211w != NO_MGMT_FRAME_PROTECTION) { + wpa_auth_set_key(wpa_auth, group->vlan_id, WPA_ALG_IGTK, + NULL, group->GN_igtk, + group->IGTK[group->GN_igtk - 4], + WPA_IGTK_LEN); + } +#endif /* CONFIG_IEEE80211W */ +} + + +static void wpa_group_sm_step(struct wpa_authenticator *wpa_auth, + struct wpa_group *group) +{ + if (group->GInit) { + wpa_group_gtk_init(wpa_auth, group); + } else if (group->wpa_group_state == WPA_GROUP_GTK_INIT && + group->GTKAuthenticator) { + wpa_group_setkeysdone(wpa_auth, group); + } else if (group->wpa_group_state == WPA_GROUP_SETKEYSDONE && + group->GTKReKey) { + wpa_group_setkeys(wpa_auth, group); + } else if (group->wpa_group_state == WPA_GROUP_SETKEYS) { + if (group->GKeyDoneStations == 0) + wpa_group_setkeysdone(wpa_auth, group); + else if (group->GTKReKey) + wpa_group_setkeys(wpa_auth, group); + } +} + + +static int wpa_sm_step(struct wpa_state_machine *sm) +{ + if (sm == NULL) + return 0; + + if (sm->in_step_loop) { + /* This should not happen, but if it does, make sure we do not + * end up freeing the state machine too early by exiting the + * recursive call. */ + wpa_printf(MSG_ERROR, "WPA: wpa_sm_step() called recursively"); + return 0; + } + + sm->in_step_loop = 1; + do { + if (sm->pending_deinit) + break; + + sm->changed = FALSE; + sm->wpa_auth->group->changed = FALSE; + + SM_STEP_RUN(WPA_PTK); + if (sm->pending_deinit) + break; + SM_STEP_RUN(WPA_PTK_GROUP); + if (sm->pending_deinit) + break; + wpa_group_sm_step(sm->wpa_auth, sm->group); + } while (sm->changed || sm->wpa_auth->group->changed); + sm->in_step_loop = 0; + + if (sm->pending_deinit) { + wpa_printf(MSG_DEBUG, "WPA: Completing pending STA state " + "machine deinit for " MACSTR, MAC2STR(sm->addr)); + wpa_free_sta_sm(sm); + return 1; + } + return 0; +} + + +static void wpa_sm_call_step(void *eloop_ctx, void *timeout_ctx) +{ + struct wpa_state_machine *sm = eloop_ctx; + wpa_sm_step(sm); +} + + +void wpa_auth_sm_notify(struct wpa_state_machine *sm) +{ + if (sm == NULL) + return; + eloop_register_timeout(0, 0, wpa_sm_call_step, sm, NULL); +} + + +void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth) +{ + int tmp, i; + struct wpa_group *group; + + if (wpa_auth == NULL) + return; + + group = wpa_auth->group; + + for (i = 0; i < 2; i++) { + tmp = group->GM; + group->GM = group->GN; + group->GN = tmp; +#ifdef CONFIG_IEEE80211W + tmp = group->GM_igtk; + group->GM_igtk = group->GN_igtk; + group->GN_igtk = tmp; +#endif /* CONFIG_IEEE80211W */ + wpa_gtk_update(wpa_auth, group); + } +} + + +static const char * wpa_bool_txt(int bool) +{ + return bool ? "TRUE" : "FALSE"; +} + + +static int wpa_cipher_bits(int cipher) +{ + switch (cipher) { + case WPA_CIPHER_CCMP: + return 128; + case WPA_CIPHER_TKIP: + return 256; + case WPA_CIPHER_WEP104: + return 104; + case WPA_CIPHER_WEP40: + return 40; + default: + return 0; + } +} + + +#define RSN_SUITE "%02x-%02x-%02x-%d" +#define RSN_SUITE_ARG(s) \ +((s) >> 24) & 0xff, ((s) >> 16) & 0xff, ((s) >> 8) & 0xff, (s) & 0xff + +int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen) +{ + int len = 0, ret; + char pmkid_txt[PMKID_LEN * 2 + 1]; + + if (wpa_auth == NULL) + return len; + + ret = os_snprintf(buf + len, buflen - len, + "dot11RSNAOptionImplemented=TRUE\n" +#ifdef CONFIG_RSN_PREAUTH + "dot11RSNAPreauthenticationImplemented=TRUE\n" +#else /* CONFIG_RSN_PREAUTH */ + "dot11RSNAPreauthenticationImplemented=FALSE\n" +#endif /* CONFIG_RSN_PREAUTH */ + "dot11RSNAEnabled=%s\n" + "dot11RSNAPreauthenticationEnabled=%s\n", + wpa_bool_txt(wpa_auth->conf.wpa & WPA_PROTO_RSN), + wpa_bool_txt(wpa_auth->conf.rsn_preauth)); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + wpa_snprintf_hex(pmkid_txt, sizeof(pmkid_txt), + wpa_auth->dot11RSNAPMKIDUsed, PMKID_LEN); + + ret = os_snprintf( + buf + len, buflen - len, + "dot11RSNAConfigVersion=%u\n" + "dot11RSNAConfigPairwiseKeysSupported=9999\n" + /* FIX: dot11RSNAConfigGroupCipher */ + /* FIX: dot11RSNAConfigGroupRekeyMethod */ + /* FIX: dot11RSNAConfigGroupRekeyTime */ + /* FIX: dot11RSNAConfigGroupRekeyPackets */ + "dot11RSNAConfigGroupRekeyStrict=%u\n" + "dot11RSNAConfigGroupUpdateCount=%u\n" + "dot11RSNAConfigPairwiseUpdateCount=%u\n" + "dot11RSNAConfigGroupCipherSize=%u\n" + "dot11RSNAConfigPMKLifetime=%u\n" + "dot11RSNAConfigPMKReauthThreshold=%u\n" + "dot11RSNAConfigNumberOfPTKSAReplayCounters=0\n" + "dot11RSNAConfigSATimeout=%u\n" + "dot11RSNAAuthenticationSuiteSelected=" RSN_SUITE "\n" + "dot11RSNAPairwiseCipherSelected=" RSN_SUITE "\n" + "dot11RSNAGroupCipherSelected=" RSN_SUITE "\n" + "dot11RSNAPMKIDUsed=%s\n" + "dot11RSNAAuthenticationSuiteRequested=" RSN_SUITE "\n" + "dot11RSNAPairwiseCipherRequested=" RSN_SUITE "\n" + "dot11RSNAGroupCipherRequested=" RSN_SUITE "\n" + "dot11RSNATKIPCounterMeasuresInvoked=%u\n" + "dot11RSNA4WayHandshakeFailures=%u\n" + "dot11RSNAConfigNumberOfGTKSAReplayCounters=0\n", + RSN_VERSION, + !!wpa_auth->conf.wpa_strict_rekey, + dot11RSNAConfigGroupUpdateCount, + dot11RSNAConfigPairwiseUpdateCount, + wpa_cipher_bits(wpa_auth->conf.wpa_group), + dot11RSNAConfigPMKLifetime, + dot11RSNAConfigPMKReauthThreshold, + dot11RSNAConfigSATimeout, + RSN_SUITE_ARG(wpa_auth->dot11RSNAAuthenticationSuiteSelected), + RSN_SUITE_ARG(wpa_auth->dot11RSNAPairwiseCipherSelected), + RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherSelected), + pmkid_txt, + RSN_SUITE_ARG(wpa_auth->dot11RSNAAuthenticationSuiteRequested), + RSN_SUITE_ARG(wpa_auth->dot11RSNAPairwiseCipherRequested), + RSN_SUITE_ARG(wpa_auth->dot11RSNAGroupCipherRequested), + wpa_auth->dot11RSNATKIPCounterMeasuresInvoked, + wpa_auth->dot11RSNA4WayHandshakeFailures); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + /* TODO: dot11RSNAConfigPairwiseCiphersTable */ + /* TODO: dot11RSNAConfigAuthenticationSuitesTable */ + + /* Private MIB */ + ret = os_snprintf(buf + len, buflen - len, "hostapdWPAGroupState=%d\n", + wpa_auth->group->wpa_group_state); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + return len; +} + + +int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen) +{ + int len = 0, ret; + u32 pairwise = 0; + + if (sm == NULL) + return 0; + + /* TODO: FF-FF-FF-FF-FF-FF entry for broadcast/multicast stats */ + + /* dot11RSNAStatsEntry */ + + if (sm->wpa == WPA_VERSION_WPA) { + if (sm->pairwise == WPA_CIPHER_CCMP) + pairwise = WPA_CIPHER_SUITE_CCMP; + else if (sm->pairwise == WPA_CIPHER_TKIP) + pairwise = WPA_CIPHER_SUITE_TKIP; + else if (sm->pairwise == WPA_CIPHER_WEP104) + pairwise = WPA_CIPHER_SUITE_WEP104; + else if (sm->pairwise == WPA_CIPHER_WEP40) + pairwise = WPA_CIPHER_SUITE_WEP40; + else if (sm->pairwise == WPA_CIPHER_NONE) + pairwise = WPA_CIPHER_SUITE_NONE; + } else if (sm->wpa == WPA_VERSION_WPA2) { + if (sm->pairwise == WPA_CIPHER_CCMP) + pairwise = RSN_CIPHER_SUITE_CCMP; + else if (sm->pairwise == WPA_CIPHER_TKIP) + pairwise = RSN_CIPHER_SUITE_TKIP; + else if (sm->pairwise == WPA_CIPHER_WEP104) + pairwise = RSN_CIPHER_SUITE_WEP104; + else if (sm->pairwise == WPA_CIPHER_WEP40) + pairwise = RSN_CIPHER_SUITE_WEP40; + else if (sm->pairwise == WPA_CIPHER_NONE) + pairwise = RSN_CIPHER_SUITE_NONE; + } else + return 0; + + ret = os_snprintf( + buf + len, buflen - len, + /* TODO: dot11RSNAStatsIndex */ + "dot11RSNAStatsSTAAddress=" MACSTR "\n" + "dot11RSNAStatsVersion=1\n" + "dot11RSNAStatsSelectedPairwiseCipher=" RSN_SUITE "\n" + /* TODO: dot11RSNAStatsTKIPICVErrors */ + "dot11RSNAStatsTKIPLocalMICFailures=%u\n" + "dot11RSNAStatsTKIPRemoveMICFailures=%u\n" + /* TODO: dot11RSNAStatsCCMPReplays */ + /* TODO: dot11RSNAStatsCCMPDecryptErrors */ + /* TODO: dot11RSNAStatsTKIPReplays */, + MAC2STR(sm->addr), + RSN_SUITE_ARG(pairwise), + sm->dot11RSNAStatsTKIPLocalMICFailures, + sm->dot11RSNAStatsTKIPRemoteMICFailures); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + /* Private MIB */ + ret = os_snprintf(buf + len, buflen - len, + "hostapdWPAPTKState=%d\n" + "hostapdWPAPTKGroupState=%d\n", + sm->wpa_ptk_state, + sm->wpa_ptk_group_state); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + return len; +} + + +void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth) +{ + if (wpa_auth) + wpa_auth->dot11RSNATKIPCounterMeasuresInvoked++; +} + + +int wpa_auth_pairwise_set(struct wpa_state_machine *sm) +{ + return sm && sm->pairwise_set; +} + + +int wpa_auth_get_pairwise(struct wpa_state_machine *sm) +{ + return sm->pairwise; +} + + +int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm) +{ + if (sm == NULL) + return -1; + return sm->wpa_key_mgmt; +} + + +int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm) +{ + if (sm == NULL) + return 0; + return sm->wpa; +} + + +int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm, + struct rsn_pmksa_cache_entry *entry) +{ + if (sm == NULL || sm->pmksa != entry) + return -1; + sm->pmksa = NULL; + return 0; +} + + +struct rsn_pmksa_cache_entry * +wpa_auth_sta_get_pmksa(struct wpa_state_machine *sm) +{ + return sm ? sm->pmksa : NULL; +} + + +void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm) +{ + if (sm) + sm->dot11RSNAStatsTKIPLocalMICFailures++; +} + + +const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, size_t *len) +{ + if (wpa_auth == NULL) + return NULL; + *len = wpa_auth->wpa_ie_len; + return wpa_auth->wpa_ie; +} + + +int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, + int session_timeout, struct eapol_state_machine *eapol) +{ + if (sm == NULL || sm->wpa != WPA_VERSION_WPA2) + return -1; + + if (pmksa_cache_auth_add(sm->wpa_auth->pmksa, pmk, PMK_LEN, + sm->wpa_auth->addr, sm->addr, session_timeout, + eapol, sm->wpa_key_mgmt)) + return 0; + + return -1; +} + + +int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, + const u8 *pmk, size_t len, const u8 *sta_addr, + int session_timeout, + struct eapol_state_machine *eapol) +{ + if (wpa_auth == NULL) + return -1; + + if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, len, wpa_auth->addr, + sta_addr, session_timeout, eapol, + WPA_KEY_MGMT_IEEE8021X)) + return 0; + + return -1; +} + + +static struct wpa_group * +wpa_auth_add_group(struct wpa_authenticator *wpa_auth, int vlan_id) +{ + struct wpa_group *group; + + if (wpa_auth == NULL || wpa_auth->group == NULL) + return NULL; + + wpa_printf(MSG_DEBUG, "WPA: Add group state machine for VLAN-ID %d", + vlan_id); + group = wpa_group_init(wpa_auth, vlan_id); + if (group == NULL) + return NULL; + + group->next = wpa_auth->group->next; + wpa_auth->group->next = group; + + return group; +} + + +int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id) +{ + struct wpa_group *group; + + if (sm == NULL || sm->wpa_auth == NULL) + return 0; + + group = sm->wpa_auth->group; + while (group) { + if (group->vlan_id == vlan_id) + break; + group = group->next; + } + + if (group == NULL) { + group = wpa_auth_add_group(sm->wpa_auth, vlan_id); + if (group == NULL) + return -1; + } + + if (sm->group == group) + return 0; + + wpa_printf(MSG_DEBUG, "WPA: Moving STA " MACSTR " to use group state " + "machine for VLAN ID %d", MAC2STR(sm->addr), vlan_id); + + sm->group = group; + return 0; +} diff --git a/contrib/wpa/src/ap/wpa_auth.h b/contrib/wpa/src/ap/wpa_auth.h new file mode 100644 index 0000000..d0136c7 --- /dev/null +++ b/contrib/wpa/src/ap/wpa_auth.h @@ -0,0 +1,281 @@ +/* + * hostapd - IEEE 802.11i-2004 / WPA Authenticator + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPA_AUTH_H +#define WPA_AUTH_H + +#include "common/defs.h" +#include "common/eapol_common.h" +#include "common/wpa_common.h" + +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +/* IEEE Std 802.11r-2008, 11A.10.3 - Remote request/response frame definition + */ +struct ft_rrb_frame { + u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ + u8 packet_type; /* FT_PACKET_REQUEST/FT_PACKET_RESPONSE */ + le16 action_length; /* little endian length of action_frame */ + u8 ap_address[ETH_ALEN]; + /* + * Followed by action_length bytes of FT Action frame (from Category + * field to the end of Action Frame body. + */ +} STRUCT_PACKED; + +#define RSN_REMOTE_FRAME_TYPE_FT_RRB 1 + +#define FT_PACKET_REQUEST 0 +#define FT_PACKET_RESPONSE 1 +/* Vendor-specific types for R0KH-R1KH protocol; not defined in 802.11r */ +#define FT_PACKET_R0KH_R1KH_PULL 200 +#define FT_PACKET_R0KH_R1KH_RESP 201 +#define FT_PACKET_R0KH_R1KH_PUSH 202 + +#define FT_R0KH_R1KH_PULL_DATA_LEN 44 +#define FT_R0KH_R1KH_RESP_DATA_LEN 76 +#define FT_R0KH_R1KH_PUSH_DATA_LEN 88 + +struct ft_r0kh_r1kh_pull_frame { + u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ + u8 packet_type; /* FT_PACKET_R0KH_R1KH_PULL */ + le16 data_length; /* little endian length of data (44) */ + u8 ap_address[ETH_ALEN]; + + u8 nonce[16]; + u8 pmk_r0_name[WPA_PMK_NAME_LEN]; + u8 r1kh_id[FT_R1KH_ID_LEN]; + u8 s1kh_id[ETH_ALEN]; + u8 pad[4]; /* 8-octet boundary for AES key wrap */ + u8 key_wrap_extra[8]; +} STRUCT_PACKED; + +struct ft_r0kh_r1kh_resp_frame { + u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ + u8 packet_type; /* FT_PACKET_R0KH_R1KH_RESP */ + le16 data_length; /* little endian length of data (76) */ + u8 ap_address[ETH_ALEN]; + + u8 nonce[16]; /* copied from pull */ + u8 r1kh_id[FT_R1KH_ID_LEN]; /* copied from pull */ + u8 s1kh_id[ETH_ALEN]; /* copied from pull */ + u8 pmk_r1[PMK_LEN]; + u8 pmk_r1_name[WPA_PMK_NAME_LEN]; + le16 pairwise; + u8 pad[2]; /* 8-octet boundary for AES key wrap */ + u8 key_wrap_extra[8]; +} STRUCT_PACKED; + +struct ft_r0kh_r1kh_push_frame { + u8 frame_type; /* RSN_REMOTE_FRAME_TYPE_FT_RRB */ + u8 packet_type; /* FT_PACKET_R0KH_R1KH_PUSH */ + le16 data_length; /* little endian length of data (88) */ + u8 ap_address[ETH_ALEN]; + + /* Encrypted with AES key-wrap */ + u8 timestamp[4]; /* current time in seconds since unix epoch, little + * endian */ + u8 r1kh_id[FT_R1KH_ID_LEN]; + u8 s1kh_id[ETH_ALEN]; + u8 pmk_r0_name[WPA_PMK_NAME_LEN]; + u8 pmk_r1[PMK_LEN]; + u8 pmk_r1_name[WPA_PMK_NAME_LEN]; + le16 pairwise; + u8 pad[6]; /* 8-octet boundary for AES key wrap */ + u8 key_wrap_extra[8]; +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ + + +/* per STA state machine data */ + +struct wpa_authenticator; +struct wpa_state_machine; +struct rsn_pmksa_cache_entry; +struct eapol_state_machine; + + +struct ft_remote_r0kh { + struct ft_remote_r0kh *next; + u8 addr[ETH_ALEN]; + u8 id[FT_R0KH_ID_MAX_LEN]; + size_t id_len; + u8 key[16]; +}; + + +struct ft_remote_r1kh { + struct ft_remote_r1kh *next; + u8 addr[ETH_ALEN]; + u8 id[FT_R1KH_ID_LEN]; + u8 key[16]; +}; + + +struct wpa_auth_config { + int wpa; + int wpa_key_mgmt; + int wpa_pairwise; + int wpa_group; + int wpa_group_rekey; + int wpa_strict_rekey; + int wpa_gmk_rekey; + int wpa_ptk_rekey; + int rsn_pairwise; + int rsn_preauth; + int eapol_version; + int peerkey; + int wmm_enabled; + int wmm_uapsd; + int okc; +#ifdef CONFIG_IEEE80211W + enum mfp_options ieee80211w; +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_IEEE80211R +#define SSID_LEN 32 + u8 ssid[SSID_LEN]; + size_t ssid_len; + u8 mobility_domain[MOBILITY_DOMAIN_ID_LEN]; + u8 r0_key_holder[FT_R0KH_ID_MAX_LEN]; + size_t r0_key_holder_len; + u8 r1_key_holder[FT_R1KH_ID_LEN]; + u32 r0_key_lifetime; + u32 reassociation_deadline; + struct ft_remote_r0kh *r0kh_list; + struct ft_remote_r1kh *r1kh_list; + int pmk_r1_push; +#endif /* CONFIG_IEEE80211R */ +}; + +typedef enum { + LOGGER_DEBUG, LOGGER_INFO, LOGGER_WARNING +} logger_level; + +typedef enum { + WPA_EAPOL_portEnabled, WPA_EAPOL_portValid, WPA_EAPOL_authorized, + WPA_EAPOL_portControl_Auto, WPA_EAPOL_keyRun, WPA_EAPOL_keyAvailable, + WPA_EAPOL_keyDone, WPA_EAPOL_inc_EapolFramesTx +} wpa_eapol_variable; + +struct wpa_auth_callbacks { + void *ctx; + void (*logger)(void *ctx, const u8 *addr, logger_level level, + const char *txt); + void (*disconnect)(void *ctx, const u8 *addr, u16 reason); + void (*mic_failure_report)(void *ctx, const u8 *addr); + void (*set_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var, + int value); + int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var); + const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *prev_psk); + int (*get_msk)(void *ctx, const u8 *addr, u8 *msk, size_t *len); + int (*set_key)(void *ctx, int vlan_id, enum wpa_alg alg, + const u8 *addr, int idx, u8 *key, size_t key_len); + int (*get_seqnum)(void *ctx, const u8 *addr, int idx, u8 *seq); + int (*send_eapol)(void *ctx, const u8 *addr, const u8 *data, + size_t data_len, int encrypt); + int (*for_each_sta)(void *ctx, int (*cb)(struct wpa_state_machine *sm, + void *ctx), void *cb_ctx); + int (*for_each_auth)(void *ctx, int (*cb)(struct wpa_authenticator *a, + void *ctx), void *cb_ctx); + int (*send_ether)(void *ctx, const u8 *dst, u16 proto, const u8 *data, + size_t data_len); +#ifdef CONFIG_IEEE80211R + struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr); + int (*send_ft_action)(void *ctx, const u8 *dst, + const u8 *data, size_t data_len); +#endif /* CONFIG_IEEE80211R */ +}; + +struct wpa_authenticator * wpa_init(const u8 *addr, + struct wpa_auth_config *conf, + struct wpa_auth_callbacks *cb); +void wpa_deinit(struct wpa_authenticator *wpa_auth); +int wpa_reconfig(struct wpa_authenticator *wpa_auth, + struct wpa_auth_config *conf); + +enum { + WPA_IE_OK, WPA_INVALID_IE, WPA_INVALID_GROUP, WPA_INVALID_PAIRWISE, + WPA_INVALID_AKMP, WPA_NOT_ENABLED, WPA_ALLOC_FAIL, + WPA_MGMT_FRAME_PROTECTION_VIOLATION, WPA_INVALID_MGMT_GROUP_CIPHER, + WPA_INVALID_MDIE, WPA_INVALID_PROTO +}; + +int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + const u8 *wpa_ie, size_t wpa_ie_len, + const u8 *mdie, size_t mdie_len); +int wpa_auth_uses_mfp(struct wpa_state_machine *sm); +struct wpa_state_machine * +wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr); +int wpa_auth_sta_associated(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm); +void wpa_auth_sta_no_wpa(struct wpa_state_machine *sm); +void wpa_auth_sta_deinit(struct wpa_state_machine *sm); +void wpa_receive(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + u8 *data, size_t data_len); +typedef enum { + WPA_AUTH, WPA_ASSOC, WPA_DISASSOC, WPA_DEAUTH, WPA_REAUTH, + WPA_REAUTH_EAPOL, WPA_ASSOC_FT +} wpa_event; +void wpa_remove_ptk(struct wpa_state_machine *sm); +int wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event); +void wpa_auth_sm_notify(struct wpa_state_machine *sm); +void wpa_gtk_rekey(struct wpa_authenticator *wpa_auth); +int wpa_get_mib(struct wpa_authenticator *wpa_auth, char *buf, size_t buflen); +int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen); +void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth); +int wpa_auth_pairwise_set(struct wpa_state_machine *sm); +int wpa_auth_get_pairwise(struct wpa_state_machine *sm); +int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm); +int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm); +int wpa_auth_sta_clear_pmksa(struct wpa_state_machine *sm, + struct rsn_pmksa_cache_entry *entry); +struct rsn_pmksa_cache_entry * +wpa_auth_sta_get_pmksa(struct wpa_state_machine *sm); +void wpa_auth_sta_local_mic_failure_report(struct wpa_state_machine *sm); +const u8 * wpa_auth_get_wpa_ie(struct wpa_authenticator *wpa_auth, + size_t *len); +int wpa_auth_pmksa_add(struct wpa_state_machine *sm, const u8 *pmk, + int session_timeout, struct eapol_state_machine *eapol); +int wpa_auth_pmksa_add_preauth(struct wpa_authenticator *wpa_auth, + const u8 *pmk, size_t len, const u8 *sta_addr, + int session_timeout, + struct eapol_state_machine *eapol); +int wpa_auth_sta_set_vlan(struct wpa_state_machine *sm, int vlan_id); + +#ifdef CONFIG_IEEE80211R +u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, + size_t max_len, int auth_alg, + const u8 *req_ies, size_t req_ies_len); +void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid, + u16 auth_transaction, const u8 *ies, size_t ies_len, + void (*cb)(void *ctx, const u8 *dst, const u8 *bssid, + u16 auth_transaction, u16 resp, + const u8 *ies, size_t ies_len), + void *ctx); +u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, + size_t ies_len); +int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len); +int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, + const u8 *data, size_t data_len); +void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr); +#endif /* CONFIG_IEEE80211R */ + +#endif /* WPA_AUTH_H */ diff --git a/contrib/wpa/src/ap/wpa_auth_ft.c b/contrib/wpa/src/ap/wpa_auth_ft.c new file mode 100644 index 0000000..c9871d9 --- /dev/null +++ b/contrib/wpa/src/ap/wpa_auth_ft.c @@ -0,0 +1,1776 @@ +/* + * hostapd - IEEE 802.11r - Fast BSS Transition + * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "crypto/aes_wrap.h" +#include "ap_config.h" +#include "ieee802_11.h" +#include "wmm.h" +#include "wpa_auth.h" +#include "wpa_auth_i.h" +#include "wpa_auth_ie.h" + + +#ifdef CONFIG_IEEE80211R + +struct wpa_ft_ies { + const u8 *mdie; + size_t mdie_len; + const u8 *ftie; + size_t ftie_len; + const u8 *r1kh_id; + const u8 *gtk; + size_t gtk_len; + const u8 *r0kh_id; + size_t r0kh_id_len; + const u8 *rsn; + size_t rsn_len; + const u8 *rsn_pmkid; + const u8 *ric; + size_t ric_len; +}; + + +static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, + struct wpa_ft_ies *parse); + + +static int wpa_ft_rrb_send(struct wpa_authenticator *wpa_auth, const u8 *dst, + const u8 *data, size_t data_len) +{ + if (wpa_auth->cb.send_ether == NULL) + return -1; + wpa_printf(MSG_DEBUG, "FT: RRB send to " MACSTR, MAC2STR(dst)); + return wpa_auth->cb.send_ether(wpa_auth->cb.ctx, dst, ETH_P_RRB, + data, data_len); +} + + +static int wpa_ft_action_send(struct wpa_authenticator *wpa_auth, + const u8 *dst, const u8 *data, size_t data_len) +{ + if (wpa_auth->cb.send_ft_action == NULL) + return -1; + return wpa_auth->cb.send_ft_action(wpa_auth->cb.ctx, dst, + data, data_len); +} + + +static struct wpa_state_machine * +wpa_ft_add_sta(struct wpa_authenticator *wpa_auth, const u8 *sta_addr) +{ + if (wpa_auth->cb.add_sta == NULL) + return NULL; + return wpa_auth->cb.add_sta(wpa_auth->cb.ctx, sta_addr); +} + + +int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len) +{ + u8 *pos = buf; + u8 capab; + if (len < 2 + sizeof(struct rsn_mdie)) + return -1; + + *pos++ = WLAN_EID_MOBILITY_DOMAIN; + *pos++ = MOBILITY_DOMAIN_ID_LEN + 1; + os_memcpy(pos, conf->mobility_domain, MOBILITY_DOMAIN_ID_LEN); + pos += MOBILITY_DOMAIN_ID_LEN; + capab = RSN_FT_CAPAB_FT_OVER_DS; + *pos++ = capab; + + return pos - buf; +} + + +int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id, + size_t r0kh_id_len, + const u8 *anonce, const u8 *snonce, + u8 *buf, size_t len, const u8 *subelem, + size_t subelem_len) +{ + u8 *pos = buf, *ielen; + struct rsn_ftie *hdr; + + if (len < 2 + sizeof(*hdr) + 2 + FT_R1KH_ID_LEN + 2 + r0kh_id_len + + subelem_len) + return -1; + + *pos++ = WLAN_EID_FAST_BSS_TRANSITION; + ielen = pos++; + + hdr = (struct rsn_ftie *) pos; + os_memset(hdr, 0, sizeof(*hdr)); + pos += sizeof(*hdr); + WPA_PUT_LE16(hdr->mic_control, 0); + if (anonce) + os_memcpy(hdr->anonce, anonce, WPA_NONCE_LEN); + if (snonce) + os_memcpy(hdr->snonce, snonce, WPA_NONCE_LEN); + + /* Optional Parameters */ + *pos++ = FTIE_SUBELEM_R1KH_ID; + *pos++ = FT_R1KH_ID_LEN; + os_memcpy(pos, conf->r1_key_holder, FT_R1KH_ID_LEN); + pos += FT_R1KH_ID_LEN; + + if (r0kh_id) { + *pos++ = FTIE_SUBELEM_R0KH_ID; + *pos++ = r0kh_id_len; + os_memcpy(pos, r0kh_id, r0kh_id_len); + pos += r0kh_id_len; + } + + if (subelem) { + os_memcpy(pos, subelem, subelem_len); + pos += subelem_len; + } + + *ielen = pos - buf - 2; + + return pos - buf; +} + + +struct wpa_ft_pmk_r0_sa { + struct wpa_ft_pmk_r0_sa *next; + u8 pmk_r0[PMK_LEN]; + u8 pmk_r0_name[WPA_PMK_NAME_LEN]; + u8 spa[ETH_ALEN]; + int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */ + /* TODO: expiration, identity, radius_class, EAP type, VLAN ID */ + int pmk_r1_pushed; +}; + +struct wpa_ft_pmk_r1_sa { + struct wpa_ft_pmk_r1_sa *next; + u8 pmk_r1[PMK_LEN]; + u8 pmk_r1_name[WPA_PMK_NAME_LEN]; + u8 spa[ETH_ALEN]; + int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */ + /* TODO: expiration, identity, radius_class, EAP type, VLAN ID */ +}; + +struct wpa_ft_pmk_cache { + struct wpa_ft_pmk_r0_sa *pmk_r0; + struct wpa_ft_pmk_r1_sa *pmk_r1; +}; + +struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void) +{ + struct wpa_ft_pmk_cache *cache; + + cache = os_zalloc(sizeof(*cache)); + + return cache; +} + + +void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache) +{ + struct wpa_ft_pmk_r0_sa *r0, *r0prev; + struct wpa_ft_pmk_r1_sa *r1, *r1prev; + + r0 = cache->pmk_r0; + while (r0) { + r0prev = r0; + r0 = r0->next; + os_memset(r0prev->pmk_r0, 0, PMK_LEN); + os_free(r0prev); + } + + r1 = cache->pmk_r1; + while (r1) { + r1prev = r1; + r1 = r1->next; + os_memset(r1prev->pmk_r1, 0, PMK_LEN); + os_free(r1prev); + } + + os_free(cache); +} + + +static int wpa_ft_store_pmk_r0(struct wpa_authenticator *wpa_auth, + const u8 *spa, const u8 *pmk_r0, + const u8 *pmk_r0_name, int pairwise) +{ + struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; + struct wpa_ft_pmk_r0_sa *r0; + + /* TODO: add expiration and limit on number of entries in cache */ + + r0 = os_zalloc(sizeof(*r0)); + if (r0 == NULL) + return -1; + + os_memcpy(r0->pmk_r0, pmk_r0, PMK_LEN); + os_memcpy(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN); + os_memcpy(r0->spa, spa, ETH_ALEN); + r0->pairwise = pairwise; + + r0->next = cache->pmk_r0; + cache->pmk_r0 = r0; + + return 0; +} + + +static int wpa_ft_fetch_pmk_r0(struct wpa_authenticator *wpa_auth, + const u8 *spa, const u8 *pmk_r0_name, + u8 *pmk_r0, int *pairwise) +{ + struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; + struct wpa_ft_pmk_r0_sa *r0; + + r0 = cache->pmk_r0; + while (r0) { + if (os_memcmp(r0->spa, spa, ETH_ALEN) == 0 && + os_memcmp(r0->pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN) + == 0) { + os_memcpy(pmk_r0, r0->pmk_r0, PMK_LEN); + if (pairwise) + *pairwise = r0->pairwise; + return 0; + } + + r0 = r0->next; + } + + return -1; +} + + +static int wpa_ft_store_pmk_r1(struct wpa_authenticator *wpa_auth, + const u8 *spa, const u8 *pmk_r1, + const u8 *pmk_r1_name, int pairwise) +{ + struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; + struct wpa_ft_pmk_r1_sa *r1; + + /* TODO: add expiration and limit on number of entries in cache */ + + r1 = os_zalloc(sizeof(*r1)); + if (r1 == NULL) + return -1; + + os_memcpy(r1->pmk_r1, pmk_r1, PMK_LEN); + os_memcpy(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN); + os_memcpy(r1->spa, spa, ETH_ALEN); + r1->pairwise = pairwise; + + r1->next = cache->pmk_r1; + cache->pmk_r1 = r1; + + return 0; +} + + +static int wpa_ft_fetch_pmk_r1(struct wpa_authenticator *wpa_auth, + const u8 *spa, const u8 *pmk_r1_name, + u8 *pmk_r1, int *pairwise) +{ + struct wpa_ft_pmk_cache *cache = wpa_auth->ft_pmk_cache; + struct wpa_ft_pmk_r1_sa *r1; + + r1 = cache->pmk_r1; + while (r1) { + if (os_memcmp(r1->spa, spa, ETH_ALEN) == 0 && + os_memcmp(r1->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN) + == 0) { + os_memcpy(pmk_r1, r1->pmk_r1, PMK_LEN); + if (pairwise) + *pairwise = r1->pairwise; + return 0; + } + + r1 = r1->next; + } + + return -1; +} + + +static int wpa_ft_pull_pmk_r1(struct wpa_authenticator *wpa_auth, + const u8 *s1kh_id, const u8 *r0kh_id, + size_t r0kh_id_len, const u8 *pmk_r0_name) +{ + struct ft_remote_r0kh *r0kh; + struct ft_r0kh_r1kh_pull_frame frame, f; + + r0kh = wpa_auth->conf.r0kh_list; + while (r0kh) { + if (r0kh->id_len == r0kh_id_len && + os_memcmp(r0kh->id, r0kh_id, r0kh_id_len) == 0) + break; + r0kh = r0kh->next; + } + if (r0kh == NULL) + return -1; + + wpa_printf(MSG_DEBUG, "FT: Send PMK-R1 pull request to remote R0KH " + "address " MACSTR, MAC2STR(r0kh->addr)); + + os_memset(&frame, 0, sizeof(frame)); + frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; + frame.packet_type = FT_PACKET_R0KH_R1KH_PULL; + frame.data_length = host_to_le16(FT_R0KH_R1KH_PULL_DATA_LEN); + os_memcpy(frame.ap_address, wpa_auth->addr, ETH_ALEN); + + /* aes_wrap() does not support inplace encryption, so use a temporary + * buffer for the data. */ + if (os_get_random(f.nonce, sizeof(f.nonce))) { + wpa_printf(MSG_DEBUG, "FT: Failed to get random data for " + "nonce"); + return -1; + } + os_memcpy(f.pmk_r0_name, pmk_r0_name, WPA_PMK_NAME_LEN); + os_memcpy(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN); + os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN); + + if (aes_wrap(r0kh->key, (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, + f.nonce, frame.nonce) < 0) + return -1; + + wpa_ft_rrb_send(wpa_auth, r0kh->addr, (u8 *) &frame, sizeof(frame)); + + return 0; +} + + +int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, + struct wpa_ptk *ptk, size_t ptk_len) +{ + u8 pmk_r0[PMK_LEN], pmk_r0_name[WPA_PMK_NAME_LEN]; + u8 pmk_r1[PMK_LEN]; + u8 ptk_name[WPA_PMK_NAME_LEN]; + const u8 *mdid = sm->wpa_auth->conf.mobility_domain; + const u8 *r0kh = sm->wpa_auth->conf.r0_key_holder; + size_t r0kh_len = sm->wpa_auth->conf.r0_key_holder_len; + const u8 *r1kh = sm->wpa_auth->conf.r1_key_holder; + const u8 *ssid = sm->wpa_auth->conf.ssid; + size_t ssid_len = sm->wpa_auth->conf.ssid_len; + + + if (sm->xxkey_len == 0) { + wpa_printf(MSG_DEBUG, "FT: XXKey not available for key " + "derivation"); + return -1; + } + + wpa_derive_pmk_r0(sm->xxkey, sm->xxkey_len, ssid, ssid_len, mdid, + r0kh, r0kh_len, sm->addr, pmk_r0, pmk_r0_name); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R0", pmk_r0, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", pmk_r0_name, WPA_PMK_NAME_LEN); + wpa_ft_store_pmk_r0(sm->wpa_auth, sm->addr, pmk_r0, pmk_r0_name, + sm->pairwise); + + wpa_derive_pmk_r1(pmk_r0, pmk_r0_name, r1kh, sm->addr, + pmk_r1, sm->pmk_r1_name); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", pmk_r1, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name, + WPA_PMK_NAME_LEN); + wpa_ft_store_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1, sm->pmk_r1_name, + sm->pairwise); + + wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr, + sm->wpa_auth->addr, sm->pmk_r1_name, + (u8 *) ptk, ptk_len, ptk_name); + wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, ptk_len); + wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); + + return 0; +} + + +static inline int wpa_auth_get_seqnum(struct wpa_authenticator *wpa_auth, + const u8 *addr, int idx, u8 *seq) +{ + if (wpa_auth->cb.get_seqnum == NULL) + return -1; + return wpa_auth->cb.get_seqnum(wpa_auth->cb.ctx, addr, idx, seq); +} + + +static u8 * wpa_ft_gtk_subelem(struct wpa_state_machine *sm, size_t *len) +{ + u8 *subelem; + struct wpa_group *gsm = sm->group; + size_t subelem_len, pad_len; + const u8 *key; + size_t key_len; + u8 keybuf[32]; + + key_len = gsm->GTK_len; + if (key_len > sizeof(keybuf)) + return NULL; + + /* + * Pad key for AES Key Wrap if it is not multiple of 8 bytes or is less + * than 16 bytes. + */ + pad_len = key_len % 8; + if (pad_len) + pad_len = 8 - pad_len; + if (key_len + pad_len < 16) + pad_len += 8; + if (pad_len) { + os_memcpy(keybuf, gsm->GTK[gsm->GN - 1], key_len); + os_memset(keybuf + key_len, 0, pad_len); + keybuf[key_len] = 0xdd; + key_len += pad_len; + key = keybuf; + } else + key = gsm->GTK[gsm->GN - 1]; + + /* + * Sub-elem ID[1] | Length[1] | Key Info[2] | Key Length[1] | RSC[8] | + * Key[5..32]. + */ + subelem_len = 13 + key_len + 8; + subelem = os_zalloc(subelem_len); + if (subelem == NULL) + return NULL; + + subelem[0] = FTIE_SUBELEM_GTK; + subelem[1] = 11 + key_len + 8; + /* Key ID in B0-B1 of Key Info */ + WPA_PUT_LE16(&subelem[2], gsm->GN & 0x03); + subelem[4] = gsm->GTK_len; + wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN, subelem + 5); + if (aes_wrap(sm->PTK.kek, key_len / 8, key, subelem + 13)) { + os_free(subelem); + return NULL; + } + + *len = subelem_len; + return subelem; +} + + +#ifdef CONFIG_IEEE80211W +static u8 * wpa_ft_igtk_subelem(struct wpa_state_machine *sm, size_t *len) +{ + u8 *subelem, *pos; + struct wpa_group *gsm = sm->group; + size_t subelem_len; + + /* Sub-elem ID[1] | Length[1] | KeyID[2] | IPN[6] | Key Length[1] | + * Key[16+8] */ + subelem_len = 1 + 1 + 2 + 6 + 1 + WPA_IGTK_LEN + 8; + subelem = os_zalloc(subelem_len); + if (subelem == NULL) + return NULL; + + pos = subelem; + *pos++ = FTIE_SUBELEM_IGTK; + *pos++ = subelem_len - 2; + WPA_PUT_LE16(pos, gsm->GN_igtk); + pos += 2; + wpa_auth_get_seqnum(sm->wpa_auth, NULL, gsm->GN_igtk, pos); + pos += 6; + *pos++ = WPA_IGTK_LEN; + if (aes_wrap(sm->PTK.kek, WPA_IGTK_LEN / 8, + gsm->IGTK[gsm->GN_igtk - 4], pos)) { + os_free(subelem); + return NULL; + } + + *len = subelem_len; + return subelem; +} +#endif /* CONFIG_IEEE80211W */ + + +static u8 * wpa_ft_process_rdie(u8 *pos, u8 *end, u8 id, u8 descr_count, + const u8 *ies, size_t ies_len) +{ + struct ieee802_11_elems parse; + struct rsn_rdie *rdie; + + wpa_printf(MSG_DEBUG, "FT: Resource Request: id=%d descr_count=%d", + id, descr_count); + wpa_hexdump(MSG_MSGDUMP, "FT: Resource descriptor IE(s)", + ies, ies_len); + + if (end - pos < (int) sizeof(*rdie)) { + wpa_printf(MSG_ERROR, "FT: Not enough room for response RDIE"); + return pos; + } + + *pos++ = WLAN_EID_RIC_DATA; + *pos++ = sizeof(*rdie); + rdie = (struct rsn_rdie *) pos; + rdie->id = id; + rdie->descr_count = 0; + rdie->status_code = host_to_le16(WLAN_STATUS_SUCCESS); + pos += sizeof(*rdie); + + if (ieee802_11_parse_elems((u8 *) ies, ies_len, &parse, 1) == + ParseFailed) { + wpa_printf(MSG_DEBUG, "FT: Failed to parse request IEs"); + rdie->status_code = + host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE); + return pos; + } + +#ifdef NEED_AP_MLME + if (parse.wmm_tspec) { + struct wmm_tspec_element *tspec; + int res; + + if (parse.wmm_tspec_len + 2 < (int) sizeof(*tspec)) { + wpa_printf(MSG_DEBUG, "FT: Too short WMM TSPEC IE " + "(%d)", (int) parse.wmm_tspec_len); + rdie->status_code = + host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE); + return pos; + } + if (end - pos < (int) sizeof(*tspec)) { + wpa_printf(MSG_ERROR, "FT: Not enough room for " + "response TSPEC"); + rdie->status_code = + host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE); + return pos; + } + tspec = (struct wmm_tspec_element *) pos; + os_memcpy(tspec, parse.wmm_tspec - 2, sizeof(*tspec)); + res = wmm_process_tspec(tspec); + wpa_printf(MSG_DEBUG, "FT: ADDTS processing result: %d", res); + if (res == WMM_ADDTS_STATUS_INVALID_PARAMETERS) + rdie->status_code = + host_to_le16(WLAN_STATUS_INVALID_PARAMETERS); + else if (res == WMM_ADDTS_STATUS_REFUSED) + rdie->status_code = + host_to_le16(WLAN_STATUS_REQUEST_DECLINED); + else { + /* TSPEC accepted; include updated TSPEC in response */ + rdie->descr_count = 1; + pos += sizeof(*tspec); + } + return pos; + } +#endif /* NEED_AP_MLME */ + + wpa_printf(MSG_DEBUG, "FT: No supported resource requested"); + rdie->status_code = host_to_le16(WLAN_STATUS_UNSPECIFIED_FAILURE); + return pos; +} + + +static u8 * wpa_ft_process_ric(u8 *pos, u8 *end, const u8 *ric, size_t ric_len) +{ + const u8 *rpos, *start; + const struct rsn_rdie *rdie; + + wpa_hexdump(MSG_MSGDUMP, "FT: RIC Request", ric, ric_len); + + rpos = ric; + while (rpos + sizeof(*rdie) < ric + ric_len) { + if (rpos[0] != WLAN_EID_RIC_DATA || rpos[1] < sizeof(*rdie) || + rpos + 2 + rpos[1] > ric + ric_len) + break; + rdie = (const struct rsn_rdie *) (rpos + 2); + rpos += 2 + rpos[1]; + start = rpos; + + while (rpos + 2 <= ric + ric_len && + rpos + 2 + rpos[1] <= ric + ric_len) { + if (rpos[0] == WLAN_EID_RIC_DATA) + break; + rpos += 2 + rpos[1]; + } + pos = wpa_ft_process_rdie(pos, end, rdie->id, + rdie->descr_count, + start, rpos - start); + } + + return pos; +} + + +u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos, + size_t max_len, int auth_alg, + const u8 *req_ies, size_t req_ies_len) +{ + u8 *end, *mdie, *ftie, *rsnie = NULL, *r0kh_id, *subelem = NULL; + size_t mdie_len, ftie_len, rsnie_len = 0, r0kh_id_len, subelem_len = 0; + int res; + struct wpa_auth_config *conf; + struct rsn_ftie *_ftie; + struct wpa_ft_ies parse; + u8 *ric_start; + u8 *anonce, *snonce; + + if (sm == NULL) + return pos; + + conf = &sm->wpa_auth->conf; + + if (sm->wpa_key_mgmt != WPA_KEY_MGMT_FT_IEEE8021X && + sm->wpa_key_mgmt != WPA_KEY_MGMT_FT_PSK) + return pos; + + end = pos + max_len; + + if (auth_alg == WLAN_AUTH_FT) { + /* + * RSN (only present if this is a Reassociation Response and + * part of a fast BSS transition) + */ + res = wpa_write_rsn_ie(conf, pos, end - pos, sm->pmk_r1_name); + if (res < 0) + return pos; + rsnie = pos; + rsnie_len = res; + pos += res; + } + + /* Mobility Domain Information */ + res = wpa_write_mdie(conf, pos, end - pos); + if (res < 0) + return pos; + mdie = pos; + mdie_len = res; + pos += res; + + /* Fast BSS Transition Information */ + if (auth_alg == WLAN_AUTH_FT) { + subelem = wpa_ft_gtk_subelem(sm, &subelem_len); + r0kh_id = sm->r0kh_id; + r0kh_id_len = sm->r0kh_id_len; + anonce = sm->ANonce; + snonce = sm->SNonce; +#ifdef CONFIG_IEEE80211W + if (sm->mgmt_frame_prot) { + u8 *igtk; + size_t igtk_len; + u8 *nbuf; + igtk = wpa_ft_igtk_subelem(sm, &igtk_len); + if (igtk == NULL) { + os_free(subelem); + return pos; + } + nbuf = os_realloc(subelem, subelem_len + igtk_len); + if (nbuf == NULL) { + os_free(subelem); + os_free(igtk); + return pos; + } + subelem = nbuf; + os_memcpy(subelem + subelem_len, igtk, igtk_len); + subelem_len += igtk_len; + os_free(igtk); + } +#endif /* CONFIG_IEEE80211W */ + } else { + r0kh_id = conf->r0_key_holder; + r0kh_id_len = conf->r0_key_holder_len; + anonce = NULL; + snonce = NULL; + } + res = wpa_write_ftie(conf, r0kh_id, r0kh_id_len, anonce, snonce, pos, + end - pos, subelem, subelem_len); + os_free(subelem); + if (res < 0) + return pos; + ftie = pos; + ftie_len = res; + pos += res; + + os_free(sm->assoc_resp_ftie); + sm->assoc_resp_ftie = os_malloc(ftie_len); + if (sm->assoc_resp_ftie) + os_memcpy(sm->assoc_resp_ftie, ftie, ftie_len); + + _ftie = (struct rsn_ftie *) (ftie + 2); + if (auth_alg == WLAN_AUTH_FT) + _ftie->mic_control[1] = 3; /* Information element count */ + + ric_start = pos; + if (wpa_ft_parse_ies(req_ies, req_ies_len, &parse) == 0 && parse.ric) { + pos = wpa_ft_process_ric(pos, end, parse.ric, parse.ric_len); + if (auth_alg == WLAN_AUTH_FT) + _ftie->mic_control[1] += + ieee802_11_ie_count(ric_start, + pos - ric_start); + } + if (ric_start == pos) + ric_start = NULL; + + if (auth_alg == WLAN_AUTH_FT && + wpa_ft_mic(sm->PTK.kck, sm->addr, sm->wpa_auth->addr, 6, + mdie, mdie_len, ftie, ftie_len, + rsnie, rsnie_len, + ric_start, ric_start ? pos - ric_start : 0, + _ftie->mic) < 0) + wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC"); + + return pos; +} + + +static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len, + struct wpa_ft_ies *parse) +{ + const u8 *end, *pos; + + parse->ftie = ie; + parse->ftie_len = ie_len; + + pos = ie + sizeof(struct rsn_ftie); + end = ie + ie_len; + + while (pos + 2 <= end && pos + 2 + pos[1] <= end) { + switch (pos[0]) { + case FTIE_SUBELEM_R1KH_ID: + if (pos[1] != FT_R1KH_ID_LEN) { + wpa_printf(MSG_DEBUG, "FT: Invalid R1KH-ID " + "length in FTIE: %d", pos[1]); + return -1; + } + parse->r1kh_id = pos + 2; + break; + case FTIE_SUBELEM_GTK: + parse->gtk = pos + 2; + parse->gtk_len = pos[1]; + break; + case FTIE_SUBELEM_R0KH_ID: + if (pos[1] < 1 || pos[1] > FT_R0KH_ID_MAX_LEN) { + wpa_printf(MSG_DEBUG, "FT: Invalid R0KH-ID " + "length in FTIE: %d", pos[1]); + return -1; + } + parse->r0kh_id = pos + 2; + parse->r0kh_id_len = pos[1]; + break; + } + + pos += 2 + pos[1]; + } + + return 0; +} + + +static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, + struct wpa_ft_ies *parse) +{ + const u8 *end, *pos; + struct wpa_ie_data data; + int ret; + const struct rsn_ftie *ftie; + int prot_ie_count = 0; + + os_memset(parse, 0, sizeof(*parse)); + if (ies == NULL) + return 0; + + pos = ies; + end = ies + ies_len; + while (pos + 2 <= end && pos + 2 + pos[1] <= end) { + switch (pos[0]) { + case WLAN_EID_RSN: + parse->rsn = pos + 2; + parse->rsn_len = pos[1]; + ret = wpa_parse_wpa_ie_rsn(parse->rsn - 2, + parse->rsn_len + 2, + &data); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to parse " + "RSN IE: %d", ret); + return -1; + } + if (data.num_pmkid == 1 && data.pmkid) + parse->rsn_pmkid = data.pmkid; + break; + case WLAN_EID_MOBILITY_DOMAIN: + parse->mdie = pos + 2; + parse->mdie_len = pos[1]; + break; + case WLAN_EID_FAST_BSS_TRANSITION: + if (pos[1] < sizeof(*ftie)) + return -1; + ftie = (const struct rsn_ftie *) (pos + 2); + prot_ie_count = ftie->mic_control[1]; + if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0) + return -1; + break; + case WLAN_EID_RIC_DATA: + if (parse->ric == NULL) + parse->ric = pos; + } + + pos += 2 + pos[1]; + } + + if (prot_ie_count == 0) + return 0; /* no MIC */ + + /* + * Check that the protected IE count matches with IEs included in the + * frame. + */ + if (parse->rsn) + prot_ie_count--; + if (parse->mdie) + prot_ie_count--; + if (parse->ftie) + prot_ie_count--; + if (prot_ie_count < 0) { + wpa_printf(MSG_DEBUG, "FT: Some required IEs not included in " + "the protected IE count"); + return -1; + } + + if (prot_ie_count == 0 && parse->ric) { + wpa_printf(MSG_DEBUG, "FT: RIC IE(s) in the frame, but not " + "included in protected IE count"); + return -1; + } + + /* Determine the end of the RIC IE(s) */ + pos = parse->ric; + while (pos && pos + 2 <= end && pos + 2 + pos[1] <= end && + prot_ie_count) { + prot_ie_count--; + pos += 2 + pos[1]; + } + parse->ric_len = pos - parse->ric; + if (prot_ie_count) { + wpa_printf(MSG_DEBUG, "FT: %d protected IEs missing from " + "frame", (int) prot_ie_count); + return -1; + } + + return 0; +} + + +static inline int wpa_auth_set_key(struct wpa_authenticator *wpa_auth, + int vlan_id, + enum wpa_alg alg, const u8 *addr, int idx, + u8 *key, size_t key_len) +{ + if (wpa_auth->cb.set_key == NULL) + return -1; + return wpa_auth->cb.set_key(wpa_auth->cb.ctx, vlan_id, alg, addr, idx, + key, key_len); +} + + +void wpa_ft_install_ptk(struct wpa_state_machine *sm) +{ + enum wpa_alg alg; + int klen; + + /* MLME-SETKEYS.request(PTK) */ + if (sm->pairwise == WPA_CIPHER_TKIP) { + alg = WPA_ALG_TKIP; + klen = 32; + } else if (sm->pairwise == WPA_CIPHER_CCMP) { + alg = WPA_ALG_CCMP; + klen = 16; + } else { + wpa_printf(MSG_DEBUG, "FT: Unknown pairwise alg 0x%x - skip " + "PTK configuration", sm->pairwise); + return; + } + + /* FIX: add STA entry to kernel/driver here? The set_key will fail + * most likely without this.. At the moment, STA entry is added only + * after association has been completed. This function will be called + * again after association to get the PTK configured, but that could be + * optimized by adding the STA entry earlier. + */ + if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0, + sm->PTK.tk1, klen)) + return; + + /* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */ + sm->pairwise_set = TRUE; +} + + +static u16 wpa_ft_process_auth_req(struct wpa_state_machine *sm, + const u8 *ies, size_t ies_len, + u8 **resp_ies, size_t *resp_ies_len) +{ + struct rsn_mdie *mdie; + struct rsn_ftie *ftie; + u8 pmk_r1[PMK_LEN], pmk_r1_name[WPA_PMK_NAME_LEN]; + u8 ptk_name[WPA_PMK_NAME_LEN]; + struct wpa_auth_config *conf; + struct wpa_ft_ies parse; + size_t buflen, ptk_len; + int ret; + u8 *pos, *end; + int pairwise; + + *resp_ies = NULL; + *resp_ies_len = 0; + + sm->pmk_r1_name_valid = 0; + conf = &sm->wpa_auth->conf; + + wpa_hexdump(MSG_DEBUG, "FT: Received authentication frame IEs", + ies, ies_len); + + if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + mdie = (struct rsn_mdie *) parse.mdie; + if (mdie == NULL || parse.mdie_len < sizeof(*mdie) || + os_memcmp(mdie->mobility_domain, + sm->wpa_auth->conf.mobility_domain, + MOBILITY_DOMAIN_ID_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: Invalid MDIE"); + return WLAN_STATUS_INVALID_MDIE; + } + + ftie = (struct rsn_ftie *) parse.ftie; + if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); + return WLAN_STATUS_INVALID_FTIE; + } + + os_memcpy(sm->SNonce, ftie->snonce, WPA_NONCE_LEN); + + if (parse.r0kh_id == NULL) { + wpa_printf(MSG_DEBUG, "FT: Invalid FTIE - no R0KH-ID"); + return WLAN_STATUS_INVALID_FTIE; + } + + wpa_hexdump(MSG_DEBUG, "FT: STA R0KH-ID", + parse.r0kh_id, parse.r0kh_id_len); + os_memcpy(sm->r0kh_id, parse.r0kh_id, parse.r0kh_id_len); + sm->r0kh_id_len = parse.r0kh_id_len; + + if (parse.rsn_pmkid == NULL) { + wpa_printf(MSG_DEBUG, "FT: No PMKID in RSNIE"); + return WLAN_STATUS_INVALID_PMKID; + } + + wpa_hexdump(MSG_DEBUG, "FT: Requested PMKR0Name", + parse.rsn_pmkid, WPA_PMK_NAME_LEN); + wpa_derive_pmk_r1_name(parse.rsn_pmkid, + sm->wpa_auth->conf.r1_key_holder, sm->addr, + pmk_r1_name); + wpa_hexdump(MSG_DEBUG, "FT: Derived requested PMKR1Name", + pmk_r1_name, WPA_PMK_NAME_LEN); + + if (wpa_ft_fetch_pmk_r1(sm->wpa_auth, sm->addr, pmk_r1_name, pmk_r1, + &pairwise) < 0) { + if (wpa_ft_pull_pmk_r1(sm->wpa_auth, sm->addr, sm->r0kh_id, + sm->r0kh_id_len, parse.rsn_pmkid) < 0) { + wpa_printf(MSG_DEBUG, "FT: Did not have matching " + "PMK-R1 and unknown R0KH-ID"); + return WLAN_STATUS_INVALID_PMKID; + } + + /* + * TODO: Should return "status pending" (and the caller should + * not send out response now). The real response will be sent + * once the response from R0KH is received. + */ + return WLAN_STATUS_INVALID_PMKID; + } + + wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, PMK_LEN); + sm->pmk_r1_name_valid = 1; + os_memcpy(sm->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN); + + if (os_get_random(sm->ANonce, WPA_NONCE_LEN)) { + wpa_printf(MSG_DEBUG, "FT: Failed to get random data for " + "ANonce"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + wpa_hexdump(MSG_DEBUG, "FT: Received SNonce", + sm->SNonce, WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: Generated ANonce", + sm->ANonce, WPA_NONCE_LEN); + + ptk_len = pairwise != WPA_CIPHER_CCMP ? 64 : 48; + wpa_pmk_r1_to_ptk(pmk_r1, sm->SNonce, sm->ANonce, sm->addr, + sm->wpa_auth->addr, pmk_r1_name, + (u8 *) &sm->PTK, ptk_len, ptk_name); + wpa_hexdump_key(MSG_DEBUG, "FT: PTK", + (u8 *) &sm->PTK, ptk_len); + wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); + + sm->pairwise = pairwise; + wpa_ft_install_ptk(sm); + + buflen = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) + + 2 + FT_R1KH_ID_LEN + 200; + *resp_ies = os_zalloc(buflen); + if (*resp_ies == NULL) { + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + pos = *resp_ies; + end = *resp_ies + buflen; + + ret = wpa_write_rsn_ie(conf, pos, end - pos, parse.rsn_pmkid); + if (ret < 0) { + os_free(*resp_ies); + *resp_ies = NULL; + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + pos += ret; + + ret = wpa_write_mdie(conf, pos, end - pos); + if (ret < 0) { + os_free(*resp_ies); + *resp_ies = NULL; + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + pos += ret; + + ret = wpa_write_ftie(conf, parse.r0kh_id, parse.r0kh_id_len, + sm->ANonce, sm->SNonce, pos, end - pos, NULL, 0); + if (ret < 0) { + os_free(*resp_ies); + *resp_ies = NULL; + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + pos += ret; + + *resp_ies_len = pos - *resp_ies; + + return WLAN_STATUS_SUCCESS; +} + + +void wpa_ft_process_auth(struct wpa_state_machine *sm, const u8 *bssid, + u16 auth_transaction, const u8 *ies, size_t ies_len, + void (*cb)(void *ctx, const u8 *dst, const u8 *bssid, + u16 auth_transaction, u16 status, + const u8 *ies, size_t ies_len), + void *ctx) +{ + u16 status; + u8 *resp_ies; + size_t resp_ies_len; + + if (sm == NULL) { + wpa_printf(MSG_DEBUG, "FT: Received authentication frame, but " + "WPA SM not available"); + return; + } + + wpa_printf(MSG_DEBUG, "FT: Received authentication frame: STA=" MACSTR + " BSSID=" MACSTR " transaction=%d", + MAC2STR(sm->addr), MAC2STR(bssid), auth_transaction); + status = wpa_ft_process_auth_req(sm, ies, ies_len, &resp_ies, + &resp_ies_len); + + wpa_printf(MSG_DEBUG, "FT: FT authentication response: dst=" MACSTR + " auth_transaction=%d status=%d", + MAC2STR(sm->addr), auth_transaction + 1, status); + wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len); + cb(ctx, sm->addr, bssid, auth_transaction + 1, status, + resp_ies, resp_ies_len); + os_free(resp_ies); +} + + +u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies, + size_t ies_len) +{ + struct wpa_ft_ies parse; + struct rsn_mdie *mdie; + struct rsn_ftie *ftie; + u8 mic[16]; + unsigned int count; + + if (sm == NULL) + return WLAN_STATUS_UNSPECIFIED_FAILURE; + + wpa_hexdump(MSG_DEBUG, "FT: Reassoc Req IEs", ies, ies_len); + + if (wpa_ft_parse_ies(ies, ies_len, &parse) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to parse FT IEs"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (parse.rsn == NULL) { + wpa_printf(MSG_DEBUG, "FT: No RSNIE in Reassoc Req"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (parse.rsn_pmkid == NULL) { + wpa_printf(MSG_DEBUG, "FT: No PMKID in RSNIE"); + return WLAN_STATUS_INVALID_PMKID; + } + + if (os_memcmp(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0) + { + wpa_printf(MSG_DEBUG, "FT: PMKID in Reassoc Req did not match " + "with the PMKR1Name derived from auth request"); + return WLAN_STATUS_INVALID_PMKID; + } + + mdie = (struct rsn_mdie *) parse.mdie; + if (mdie == NULL || parse.mdie_len < sizeof(*mdie) || + os_memcmp(mdie->mobility_domain, + sm->wpa_auth->conf.mobility_domain, + MOBILITY_DOMAIN_ID_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: Invalid MDIE"); + return WLAN_STATUS_INVALID_MDIE; + } + + ftie = (struct rsn_ftie *) parse.ftie; + if (ftie == NULL || parse.ftie_len < sizeof(*ftie)) { + wpa_printf(MSG_DEBUG, "FT: Invalid FTIE"); + return WLAN_STATUS_INVALID_FTIE; + } + + if (os_memcmp(ftie->snonce, sm->SNonce, WPA_NONCE_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE"); + wpa_hexdump(MSG_DEBUG, "FT: Received SNonce", + ftie->snonce, WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce", + sm->SNonce, WPA_NONCE_LEN); + return -1; + } + + if (os_memcmp(ftie->anonce, sm->ANonce, WPA_NONCE_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE"); + wpa_hexdump(MSG_DEBUG, "FT: Received ANonce", + ftie->anonce, WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce", + sm->ANonce, WPA_NONCE_LEN); + return -1; + } + + + if (parse.r0kh_id == NULL) { + wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE"); + return -1; + } + + if (parse.r0kh_id_len != sm->r0kh_id_len || + os_memcmp(parse.r0kh_id, sm->r0kh_id, parse.r0kh_id_len) != 0) { + wpa_printf(MSG_DEBUG, "FT: R0KH-ID in FTIE did not match with " + "the current R0KH-ID"); + wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID in FTIE", + parse.r0kh_id, parse.r0kh_id_len); + wpa_hexdump(MSG_DEBUG, "FT: The current R0KH-ID", + sm->r0kh_id, sm->r0kh_id_len); + return -1; + } + + if (parse.r1kh_id == NULL) { + wpa_printf(MSG_DEBUG, "FT: No R1KH-ID subelem in FTIE"); + return -1; + } + + if (os_memcmp(parse.r1kh_id, sm->wpa_auth->conf.r1_key_holder, + FT_R1KH_ID_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: Unknown R1KH-ID used in " + "ReassocReq"); + wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID in FTIE", + parse.r1kh_id, FT_R1KH_ID_LEN); + wpa_hexdump(MSG_DEBUG, "FT: Expected R1KH-ID", + sm->wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN); + return -1; + } + + if (parse.rsn_pmkid == NULL || + os_memcmp(parse.rsn_pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN)) { + wpa_printf(MSG_DEBUG, "FT: No matching PMKR1Name (PMKID) in " + "RSNIE (pmkid=%d)", !!parse.rsn_pmkid); + return -1; + } + + count = 3; + if (parse.ric) + count++; + if (ftie->mic_control[1] != count) { + wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC " + "Control: received %u expected %u", + ftie->mic_control[1], count); + return -1; + } + + if (wpa_ft_mic(sm->PTK.kck, sm->addr, sm->wpa_auth->addr, 5, + parse.mdie - 2, parse.mdie_len + 2, + parse.ftie - 2, parse.ftie_len + 2, + parse.rsn - 2, parse.rsn_len + 2, + parse.ric, parse.ric_len, + mic) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC"); + return WLAN_STATUS_UNSPECIFIED_FAILURE; + } + + if (os_memcmp(mic, ftie->mic, 16) != 0) { + wpa_printf(MSG_DEBUG, "FT: Invalid MIC in FTIE"); + wpa_hexdump(MSG_MSGDUMP, "FT: Received MIC", ftie->mic, 16); + wpa_hexdump(MSG_MSGDUMP, "FT: Calculated MIC", mic, 16); + return WLAN_STATUS_INVALID_FTIE; + } + + return WLAN_STATUS_SUCCESS; +} + + +int wpa_ft_action_rx(struct wpa_state_machine *sm, const u8 *data, size_t len) +{ + const u8 *sta_addr, *target_ap; + const u8 *ies; + size_t ies_len; + u8 action; + struct ft_rrb_frame *frame; + + if (sm == NULL) + return -1; + + /* + * data: Category[1] Action[1] STA_Address[6] Target_AP_Address[6] + * FT Request action frame body[variable] + */ + + if (len < 14) { + wpa_printf(MSG_DEBUG, "FT: Too short FT Action frame " + "(len=%lu)", (unsigned long) len); + return -1; + } + + action = data[1]; + sta_addr = data + 2; + target_ap = data + 8; + ies = data + 14; + ies_len = len - 14; + + wpa_printf(MSG_DEBUG, "FT: Received FT Action frame (STA=" MACSTR + " Target AP=" MACSTR " Action=%d)", + MAC2STR(sta_addr), MAC2STR(target_ap), action); + + if (os_memcmp(sta_addr, sm->addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: Mismatch in FT Action STA address: " + "STA=" MACSTR " STA-Address=" MACSTR, + MAC2STR(sm->addr), MAC2STR(sta_addr)); + return -1; + } + + /* + * Do some sanity checking on the target AP address (not own and not + * broadcast. This could be extended to filter based on a list of known + * APs in the MD (if such a list were configured). + */ + if ((target_ap[0] & 0x01) || + os_memcmp(target_ap, sm->wpa_auth->addr, ETH_ALEN) == 0) { + wpa_printf(MSG_DEBUG, "FT: Invalid Target AP in FT Action " + "frame"); + return -1; + } + + wpa_hexdump(MSG_MSGDUMP, "FT: Action frame body", ies, ies_len); + + /* RRB - Forward action frame to the target AP */ + frame = os_malloc(sizeof(*frame) + len); + frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; + frame->packet_type = FT_PACKET_REQUEST; + frame->action_length = host_to_le16(len); + os_memcpy(frame->ap_address, sm->wpa_auth->addr, ETH_ALEN); + os_memcpy(frame + 1, data, len); + + wpa_ft_rrb_send(sm->wpa_auth, target_ap, (u8 *) frame, + sizeof(*frame) + len); + os_free(frame); + + return 0; +} + + +static int wpa_ft_rrb_rx_request(struct wpa_authenticator *wpa_auth, + const u8 *current_ap, const u8 *sta_addr, + const u8 *body, size_t len) +{ + struct wpa_state_machine *sm; + u16 status; + u8 *resp_ies, *pos; + size_t resp_ies_len, rlen; + struct ft_rrb_frame *frame; + + sm = wpa_ft_add_sta(wpa_auth, sta_addr); + if (sm == NULL) { + wpa_printf(MSG_DEBUG, "FT: Failed to add new STA based on " + "RRB Request"); + return -1; + } + + wpa_hexdump(MSG_MSGDUMP, "FT: RRB Request Frame body", body, len); + + status = wpa_ft_process_auth_req(sm, body, len, &resp_ies, + &resp_ies_len); + + wpa_printf(MSG_DEBUG, "FT: RRB authentication response: STA=" MACSTR + " CurrentAP=" MACSTR " status=%d", + MAC2STR(sm->addr), MAC2STR(current_ap), status); + wpa_hexdump(MSG_DEBUG, "FT: Response IEs", resp_ies, resp_ies_len); + + /* RRB - Forward action frame response to the Current AP */ + + /* + * data: Category[1] Action[1] STA_Address[6] Target_AP_Address[6] + * Status_Code[2] FT Request action frame body[variable] + */ + rlen = 2 + 2 * ETH_ALEN + 2 + resp_ies_len; + + frame = os_malloc(sizeof(*frame) + rlen); + frame->frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; + frame->packet_type = FT_PACKET_RESPONSE; + frame->action_length = host_to_le16(rlen); + os_memcpy(frame->ap_address, wpa_auth->addr, ETH_ALEN); + pos = (u8 *) (frame + 1); + *pos++ = WLAN_ACTION_FT; + *pos++ = 2; /* Action: Response */ + os_memcpy(pos, sta_addr, ETH_ALEN); + pos += ETH_ALEN; + os_memcpy(pos, wpa_auth->addr, ETH_ALEN); + pos += ETH_ALEN; + WPA_PUT_LE16(pos, status); + pos += 2; + if (resp_ies) { + os_memcpy(pos, resp_ies, resp_ies_len); + os_free(resp_ies); + } + + wpa_ft_rrb_send(wpa_auth, current_ap, (u8 *) frame, + sizeof(*frame) + rlen); + os_free(frame); + + return 0; +} + + +static int wpa_ft_rrb_rx_pull(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, + const u8 *data, size_t data_len) +{ + struct ft_r0kh_r1kh_pull_frame *frame, f; + struct ft_remote_r1kh *r1kh; + struct ft_r0kh_r1kh_resp_frame resp, r; + u8 pmk_r0[PMK_LEN]; + int pairwise; + + wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull"); + + if (data_len < sizeof(*frame)) + return -1; + + r1kh = wpa_auth->conf.r1kh_list; + while (r1kh) { + if (os_memcmp(r1kh->addr, src_addr, ETH_ALEN) == 0) + break; + r1kh = r1kh->next; + } + if (r1kh == NULL) { + wpa_printf(MSG_DEBUG, "FT: No matching R1KH address found for " + "PMK-R1 pull source address " MACSTR, + MAC2STR(src_addr)); + return -1; + } + + frame = (struct ft_r0kh_r1kh_pull_frame *) data; + /* aes_unwrap() does not support inplace decryption, so use a temporary + * buffer for the data. */ + if (aes_unwrap(r1kh->key, (FT_R0KH_R1KH_PULL_DATA_LEN + 7) / 8, + frame->nonce, f.nonce) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull " + "request from " MACSTR, MAC2STR(src_addr)); + return -1; + } + + wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce", + f.nonce, sizeof(f.nonce)); + wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR0Name", + f.pmk_r0_name, WPA_PMK_NAME_LEN); + wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR "S1KH-ID=" + MACSTR, MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id)); + + os_memset(&resp, 0, sizeof(resp)); + resp.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; + resp.packet_type = FT_PACKET_R0KH_R1KH_RESP; + resp.data_length = host_to_le16(FT_R0KH_R1KH_RESP_DATA_LEN); + os_memcpy(resp.ap_address, wpa_auth->addr, ETH_ALEN); + + /* aes_wrap() does not support inplace encryption, so use a temporary + * buffer for the data. */ + os_memcpy(r.nonce, f.nonce, sizeof(f.nonce)); + os_memcpy(r.r1kh_id, f.r1kh_id, FT_R1KH_ID_LEN); + os_memcpy(r.s1kh_id, f.s1kh_id, ETH_ALEN); + if (wpa_ft_fetch_pmk_r0(wpa_auth, f.s1kh_id, f.pmk_r0_name, pmk_r0, + &pairwise) < 0) { + wpa_printf(MSG_DEBUG, "FT: No matching PMKR0Name found for " + "PMK-R1 pull"); + return -1; + } + + wpa_derive_pmk_r1(pmk_r0, f.pmk_r0_name, f.r1kh_id, f.s1kh_id, + r.pmk_r1, r.pmk_r1_name); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", r.pmk_r1, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", r.pmk_r1_name, + WPA_PMK_NAME_LEN); + r.pairwise = host_to_le16(pairwise); + + if (aes_wrap(r1kh->key, (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, + r.nonce, resp.nonce) < 0) { + os_memset(pmk_r0, 0, PMK_LEN); + return -1; + } + + os_memset(pmk_r0, 0, PMK_LEN); + + wpa_ft_rrb_send(wpa_auth, src_addr, (u8 *) &resp, sizeof(resp)); + + return 0; +} + + +static int wpa_ft_rrb_rx_resp(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, + const u8 *data, size_t data_len) +{ + struct ft_r0kh_r1kh_resp_frame *frame, f; + struct ft_remote_r0kh *r0kh; + int pairwise; + + wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 pull response"); + + if (data_len < sizeof(*frame)) + return -1; + + r0kh = wpa_auth->conf.r0kh_list; + while (r0kh) { + if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0) + break; + r0kh = r0kh->next; + } + if (r0kh == NULL) { + wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for " + "PMK-R0 pull response source address " MACSTR, + MAC2STR(src_addr)); + return -1; + } + + frame = (struct ft_r0kh_r1kh_resp_frame *) data; + /* aes_unwrap() does not support inplace decryption, so use a temporary + * buffer for the data. */ + if (aes_unwrap(r0kh->key, (FT_R0KH_R1KH_RESP_DATA_LEN + 7) / 8, + frame->nonce, f.nonce) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 pull " + "response from " MACSTR, MAC2STR(src_addr)); + return -1; + } + + if (os_memcmp(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN) + != 0) { + wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull response did not use a " + "matching R1KH-ID"); + return -1; + } + + /* TODO: verify that <nonce,s1kh_id> matches with a pending request + * and call this requests callback function to finish request + * processing */ + + pairwise = le_to_host16(f.pairwise); + wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - nonce", + f.nonce, sizeof(f.nonce)); + wpa_printf(MSG_DEBUG, "FT: PMK-R1 pull - R1KH-ID=" MACSTR "S1KH-ID=" + MACSTR " pairwise=0x%x", + MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 pull - PMK-R1", + f.pmk_r1, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 pull - PMKR1Name", + f.pmk_r1_name, WPA_PMK_NAME_LEN); + + wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name, + pairwise); + os_memset(f.pmk_r1, 0, PMK_LEN); + + return 0; +} + + +static int wpa_ft_rrb_rx_push(struct wpa_authenticator *wpa_auth, + const u8 *src_addr, + const u8 *data, size_t data_len) +{ + struct ft_r0kh_r1kh_push_frame *frame, f; + struct ft_remote_r0kh *r0kh; + struct os_time now; + os_time_t tsend; + int pairwise; + + wpa_printf(MSG_DEBUG, "FT: Received PMK-R1 push"); + + if (data_len < sizeof(*frame)) + return -1; + + r0kh = wpa_auth->conf.r0kh_list; + while (r0kh) { + if (os_memcmp(r0kh->addr, src_addr, ETH_ALEN) == 0) + break; + r0kh = r0kh->next; + } + if (r0kh == NULL) { + wpa_printf(MSG_DEBUG, "FT: No matching R0KH address found for " + "PMK-R0 push source address " MACSTR, + MAC2STR(src_addr)); + return -1; + } + + frame = (struct ft_r0kh_r1kh_push_frame *) data; + /* aes_unwrap() does not support inplace decryption, so use a temporary + * buffer for the data. */ + if (aes_unwrap(r0kh->key, (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, + frame->timestamp, f.timestamp) < 0) { + wpa_printf(MSG_DEBUG, "FT: Failed to decrypt PMK-R1 push from " + MACSTR, MAC2STR(src_addr)); + return -1; + } + + os_get_time(&now); + tsend = WPA_GET_LE32(f.timestamp); + if ((now.sec > tsend && now.sec - tsend > 60) || + (now.sec < tsend && tsend - now.sec > 60)) { + wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not have a valid " + "timestamp: sender time %d own time %d\n", + (int) tsend, (int) now.sec); + return -1; + } + + if (os_memcmp(f.r1kh_id, wpa_auth->conf.r1_key_holder, FT_R1KH_ID_LEN) + != 0) { + wpa_printf(MSG_DEBUG, "FT: PMK-R1 push did not use a matching " + "R1KH-ID (received " MACSTR " own " MACSTR ")", + MAC2STR(f.r1kh_id), + MAC2STR(wpa_auth->conf.r1_key_holder)); + return -1; + } + + pairwise = le_to_host16(f.pairwise); + wpa_printf(MSG_DEBUG, "FT: PMK-R1 push - R1KH-ID=" MACSTR " S1KH-ID=" + MACSTR " pairwise=0x%x", + MAC2STR(f.r1kh_id), MAC2STR(f.s1kh_id), pairwise); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1 push - PMK-R1", + f.pmk_r1, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMK-R1 push - PMKR1Name", + f.pmk_r1_name, WPA_PMK_NAME_LEN); + + wpa_ft_store_pmk_r1(wpa_auth, f.s1kh_id, f.pmk_r1, f.pmk_r1_name, + pairwise); + os_memset(f.pmk_r1, 0, PMK_LEN); + + return 0; +} + + +int wpa_ft_rrb_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr, + const u8 *data, size_t data_len) +{ + struct ft_rrb_frame *frame; + u16 alen; + const u8 *pos, *end, *start; + u8 action; + const u8 *sta_addr, *target_ap_addr; + + wpa_printf(MSG_DEBUG, "FT: RRB received frame from remote AP " MACSTR, + MAC2STR(src_addr)); + + if (data_len < sizeof(*frame)) { + wpa_printf(MSG_DEBUG, "FT: Too short RRB frame (data_len=%lu)", + (unsigned long) data_len); + return -1; + } + + pos = data; + frame = (struct ft_rrb_frame *) pos; + pos += sizeof(*frame); + + alen = le_to_host16(frame->action_length); + wpa_printf(MSG_DEBUG, "FT: RRB frame - frame_type=%d packet_type=%d " + "action_length=%d ap_address=" MACSTR, + frame->frame_type, frame->packet_type, alen, + MAC2STR(frame->ap_address)); + + if (frame->frame_type != RSN_REMOTE_FRAME_TYPE_FT_RRB) { + /* Discard frame per IEEE Std 802.11r-2008, 11A.10.3 */ + wpa_printf(MSG_DEBUG, "FT: RRB discarded frame with " + "unrecognized type %d", frame->frame_type); + return -1; + } + + if (alen > data_len - sizeof(*frame)) { + wpa_printf(MSG_DEBUG, "FT: RRB frame too short for action " + "frame"); + return -1; + } + + if (frame->packet_type == FT_PACKET_R0KH_R1KH_PULL) + return wpa_ft_rrb_rx_pull(wpa_auth, src_addr, data, data_len); + if (frame->packet_type == FT_PACKET_R0KH_R1KH_RESP) + return wpa_ft_rrb_rx_resp(wpa_auth, src_addr, data, data_len); + if (frame->packet_type == FT_PACKET_R0KH_R1KH_PUSH) + return wpa_ft_rrb_rx_push(wpa_auth, src_addr, data, data_len); + + wpa_hexdump(MSG_MSGDUMP, "FT: RRB - FT Action frame", pos, alen); + + if (alen < 1 + 1 + 2 * ETH_ALEN) { + wpa_printf(MSG_DEBUG, "FT: Too short RRB frame (not enough " + "room for Action Frame body); alen=%lu", + (unsigned long) alen); + return -1; + } + start = pos; + end = pos + alen; + + if (*pos != WLAN_ACTION_FT) { + wpa_printf(MSG_DEBUG, "FT: Unexpected Action frame category " + "%d", *pos); + return -1; + } + + pos++; + action = *pos++; + sta_addr = pos; + pos += ETH_ALEN; + target_ap_addr = pos; + pos += ETH_ALEN; + wpa_printf(MSG_DEBUG, "FT: RRB Action Frame: action=%d sta_addr=" + MACSTR " target_ap_addr=" MACSTR, + action, MAC2STR(sta_addr), MAC2STR(target_ap_addr)); + + if (frame->packet_type == FT_PACKET_REQUEST) { + wpa_printf(MSG_DEBUG, "FT: FT Packet Type - Request"); + + if (action != 1) { + wpa_printf(MSG_DEBUG, "FT: Unexpected Action %d in " + "RRB Request", action); + return -1; + } + + if (os_memcmp(target_ap_addr, wpa_auth->addr, ETH_ALEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: Target AP address in the " + "RRB Request does not match with own " + "address"); + return -1; + } + + if (wpa_ft_rrb_rx_request(wpa_auth, frame->ap_address, + sta_addr, pos, end - pos) < 0) + return -1; + } else if (frame->packet_type == FT_PACKET_RESPONSE) { + u16 status_code; + + if (end - pos < 2) { + wpa_printf(MSG_DEBUG, "FT: Not enough room for status " + "code in RRB Response"); + return -1; + } + status_code = WPA_GET_LE16(pos); + pos += 2; + + wpa_printf(MSG_DEBUG, "FT: FT Packet Type - Response " + "(status_code=%d)", status_code); + + if (wpa_ft_action_send(wpa_auth, sta_addr, start, alen) < 0) + return -1; + } else { + wpa_printf(MSG_DEBUG, "FT: RRB discarded frame with unknown " + "packet_type %d", frame->packet_type); + return -1; + } + + return 0; +} + + +static void wpa_ft_generate_pmk_r1(struct wpa_authenticator *wpa_auth, + struct wpa_ft_pmk_r0_sa *pmk_r0, + struct ft_remote_r1kh *r1kh, + const u8 *s1kh_id, int pairwise) +{ + struct ft_r0kh_r1kh_push_frame frame, f; + struct os_time now; + + os_memset(&frame, 0, sizeof(frame)); + frame.frame_type = RSN_REMOTE_FRAME_TYPE_FT_RRB; + frame.packet_type = FT_PACKET_R0KH_R1KH_PUSH; + frame.data_length = host_to_le16(FT_R0KH_R1KH_PUSH_DATA_LEN); + os_memcpy(frame.ap_address, wpa_auth->addr, ETH_ALEN); + + /* aes_wrap() does not support inplace encryption, so use a temporary + * buffer for the data. */ + os_memcpy(f.r1kh_id, r1kh->id, FT_R1KH_ID_LEN); + os_memcpy(f.s1kh_id, s1kh_id, ETH_ALEN); + os_memcpy(f.pmk_r0_name, pmk_r0->pmk_r0_name, WPA_PMK_NAME_LEN); + wpa_derive_pmk_r1(pmk_r0->pmk_r0, pmk_r0->pmk_r0_name, r1kh->id, + s1kh_id, f.pmk_r1, f.pmk_r1_name); + wpa_printf(MSG_DEBUG, "FT: R1KH-ID " MACSTR, MAC2STR(r1kh->id)); + wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", f.pmk_r1, PMK_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", f.pmk_r1_name, + WPA_PMK_NAME_LEN); + os_get_time(&now); + WPA_PUT_LE32(f.timestamp, now.sec); + f.pairwise = host_to_le16(pairwise); + if (aes_wrap(r1kh->key, (FT_R0KH_R1KH_PUSH_DATA_LEN + 7) / 8, + f.timestamp, frame.timestamp) < 0) + return; + + wpa_ft_rrb_send(wpa_auth, r1kh->addr, (u8 *) &frame, sizeof(frame)); +} + + +void wpa_ft_push_pmk_r1(struct wpa_authenticator *wpa_auth, const u8 *addr) +{ + struct wpa_ft_pmk_r0_sa *r0; + struct ft_remote_r1kh *r1kh; + + if (!wpa_auth->conf.pmk_r1_push) + return; + + r0 = wpa_auth->ft_pmk_cache->pmk_r0; + while (r0) { + if (os_memcmp(r0->spa, addr, ETH_ALEN) == 0) + break; + r0 = r0->next; + } + + if (r0 == NULL || r0->pmk_r1_pushed) + return; + r0->pmk_r1_pushed = 1; + + wpa_printf(MSG_DEBUG, "FT: Deriving and pushing PMK-R1 keys to R1KHs " + "for STA " MACSTR, MAC2STR(addr)); + + r1kh = wpa_auth->conf.r1kh_list; + while (r1kh) { + wpa_ft_generate_pmk_r1(wpa_auth, r0, r1kh, addr, r0->pairwise); + r1kh = r1kh->next; + } +} + +#endif /* CONFIG_IEEE80211R */ diff --git a/contrib/wpa/src/ap/wpa_auth_glue.c b/contrib/wpa/src/ap/wpa_auth_glue.c new file mode 100644 index 0000000..afa13a6 --- /dev/null +++ b/contrib/wpa/src/ap/wpa_auth_glue.c @@ -0,0 +1,545 @@ +/* + * hostapd / WPA authenticator glue code + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published 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 "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "eapol_auth/eapol_auth_sm.h" +#include "eapol_auth/eapol_auth_sm_i.h" +#include "eap_server/eap.h" +#include "l2_packet/l2_packet.h" +#include "drivers/driver.h" +#include "hostapd.h" +#include "ieee802_1x.h" +#include "preauth_auth.h" +#include "sta_info.h" +#include "tkip_countermeasures.h" +#include "ap_drv_ops.h" +#include "ap_config.h" +#include "wpa_auth.h" + + +#ifdef CONFIG_IEEE80211R +static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len); +#endif /* CONFIG_IEEE80211R */ + + +static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf, + struct wpa_auth_config *wconf) +{ + wconf->wpa = conf->wpa; + wconf->wpa_key_mgmt = conf->wpa_key_mgmt; + wconf->wpa_pairwise = conf->wpa_pairwise; + wconf->wpa_group = conf->wpa_group; + wconf->wpa_group_rekey = conf->wpa_group_rekey; + wconf->wpa_strict_rekey = conf->wpa_strict_rekey; + wconf->wpa_gmk_rekey = conf->wpa_gmk_rekey; + wconf->wpa_ptk_rekey = conf->wpa_ptk_rekey; + wconf->rsn_pairwise = conf->rsn_pairwise; + wconf->rsn_preauth = conf->rsn_preauth; + wconf->eapol_version = conf->eapol_version; + wconf->peerkey = conf->peerkey; + wconf->wmm_enabled = conf->wmm_enabled; + wconf->wmm_uapsd = conf->wmm_uapsd; + wconf->okc = conf->okc; +#ifdef CONFIG_IEEE80211W + wconf->ieee80211w = conf->ieee80211w; +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_IEEE80211R + wconf->ssid_len = conf->ssid.ssid_len; + if (wconf->ssid_len > SSID_LEN) + wconf->ssid_len = SSID_LEN; + os_memcpy(wconf->ssid, conf->ssid.ssid, wconf->ssid_len); + os_memcpy(wconf->mobility_domain, conf->mobility_domain, + MOBILITY_DOMAIN_ID_LEN); + if (conf->nas_identifier && + os_strlen(conf->nas_identifier) <= FT_R0KH_ID_MAX_LEN) { + wconf->r0_key_holder_len = os_strlen(conf->nas_identifier); + os_memcpy(wconf->r0_key_holder, conf->nas_identifier, + wconf->r0_key_holder_len); + } + os_memcpy(wconf->r1_key_holder, conf->r1_key_holder, FT_R1KH_ID_LEN); + wconf->r0_key_lifetime = conf->r0_key_lifetime; + wconf->reassociation_deadline = conf->reassociation_deadline; + wconf->r0kh_list = conf->r0kh_list; + wconf->r1kh_list = conf->r1kh_list; + wconf->pmk_r1_push = conf->pmk_r1_push; +#endif /* CONFIG_IEEE80211R */ +} + + +static void hostapd_wpa_auth_logger(void *ctx, const u8 *addr, + logger_level level, const char *txt) +{ +#ifndef CONFIG_NO_HOSTAPD_LOGGER + struct hostapd_data *hapd = ctx; + int hlevel; + + switch (level) { + case LOGGER_WARNING: + hlevel = HOSTAPD_LEVEL_WARNING; + break; + case LOGGER_INFO: + hlevel = HOSTAPD_LEVEL_INFO; + break; + case LOGGER_DEBUG: + default: + hlevel = HOSTAPD_LEVEL_DEBUG; + break; + } + + hostapd_logger(hapd, addr, HOSTAPD_MODULE_WPA, hlevel, "%s", txt); +#endif /* CONFIG_NO_HOSTAPD_LOGGER */ +} + + +static void hostapd_wpa_auth_disconnect(void *ctx, const u8 *addr, + u16 reason) +{ + struct hostapd_data *hapd = ctx; + wpa_printf(MSG_DEBUG, "%s: WPA authenticator requests disconnect: " + "STA " MACSTR " reason %d", + __func__, MAC2STR(addr), reason); + ap_sta_disconnect(hapd, NULL, addr, reason); +} + + +static void hostapd_wpa_auth_mic_failure_report(void *ctx, const u8 *addr) +{ + struct hostapd_data *hapd = ctx; + michael_mic_failure(hapd, addr, 0); +} + + +static void hostapd_wpa_auth_set_eapol(void *ctx, const u8 *addr, + wpa_eapol_variable var, int value) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta = ap_get_sta(hapd, addr); + if (sta == NULL) + return; + switch (var) { + case WPA_EAPOL_portEnabled: + ieee802_1x_notify_port_enabled(sta->eapol_sm, value); + break; + case WPA_EAPOL_portValid: + ieee802_1x_notify_port_valid(sta->eapol_sm, value); + break; + case WPA_EAPOL_authorized: + ieee802_1x_set_sta_authorized(hapd, sta, value); + break; + case WPA_EAPOL_portControl_Auto: + if (sta->eapol_sm) + sta->eapol_sm->portControl = Auto; + break; + case WPA_EAPOL_keyRun: + if (sta->eapol_sm) + sta->eapol_sm->keyRun = value ? TRUE : FALSE; + break; + case WPA_EAPOL_keyAvailable: + if (sta->eapol_sm) + sta->eapol_sm->eap_if->eapKeyAvailable = + value ? TRUE : FALSE; + break; + case WPA_EAPOL_keyDone: + if (sta->eapol_sm) + sta->eapol_sm->keyDone = value ? TRUE : FALSE; + break; + case WPA_EAPOL_inc_EapolFramesTx: + if (sta->eapol_sm) + sta->eapol_sm->dot1xAuthEapolFramesTx++; + break; + } +} + + +static int hostapd_wpa_auth_get_eapol(void *ctx, const u8 *addr, + wpa_eapol_variable var) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta = ap_get_sta(hapd, addr); + if (sta == NULL || sta->eapol_sm == NULL) + return -1; + switch (var) { + case WPA_EAPOL_keyRun: + return sta->eapol_sm->keyRun; + case WPA_EAPOL_keyAvailable: + return sta->eapol_sm->eap_if->eapKeyAvailable; + default: + return -1; + } +} + + +static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr, + const u8 *prev_psk) +{ + struct hostapd_data *hapd = ctx; + return hostapd_get_psk(hapd->conf, addr, prev_psk); +} + + +static int hostapd_wpa_auth_get_msk(void *ctx, const u8 *addr, u8 *msk, + size_t *len) +{ + struct hostapd_data *hapd = ctx; + const u8 *key; + size_t keylen; + struct sta_info *sta; + + sta = ap_get_sta(hapd, addr); + if (sta == NULL) + return -1; + + key = ieee802_1x_get_key(sta->eapol_sm, &keylen); + if (key == NULL) + return -1; + + if (keylen > *len) + keylen = *len; + os_memcpy(msk, key, keylen); + *len = keylen; + + return 0; +} + + +static int hostapd_wpa_auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg, + const u8 *addr, int idx, u8 *key, + size_t key_len) +{ + struct hostapd_data *hapd = ctx; + const char *ifname = hapd->conf->iface; + + if (vlan_id > 0) { + ifname = hostapd_get_vlan_id_ifname(hapd->conf->vlan, vlan_id); + if (ifname == NULL) + return -1; + } + + return hapd->drv.set_key(ifname, hapd, alg, addr, idx, 1, NULL, 0, + key, key_len); +} + + +static int hostapd_wpa_auth_get_seqnum(void *ctx, const u8 *addr, int idx, + u8 *seq) +{ + struct hostapd_data *hapd = ctx; + return hostapd_get_seqnum(hapd->conf->iface, hapd, addr, idx, seq); +} + + +static int hostapd_wpa_auth_send_eapol(void *ctx, const u8 *addr, + const u8 *data, size_t data_len, + int encrypt) +{ + struct hostapd_data *hapd = ctx; + return hapd->drv.send_eapol(hapd, addr, data, data_len, encrypt); +} + + +static int hostapd_wpa_auth_for_each_sta( + void *ctx, int (*cb)(struct wpa_state_machine *sm, void *ctx), + void *cb_ctx) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (sta->wpa_sm && cb(sta->wpa_sm, cb_ctx)) + return 1; + } + return 0; +} + + +struct wpa_auth_iface_iter_data { + int (*cb)(struct wpa_authenticator *sm, void *ctx); + void *cb_ctx; +}; + +static int wpa_auth_iface_iter(struct hostapd_iface *iface, void *ctx) +{ + struct wpa_auth_iface_iter_data *data = ctx; + size_t i; + for (i = 0; i < iface->num_bss; i++) { + if (iface->bss[i]->wpa_auth && + data->cb(iface->bss[i]->wpa_auth, data->cb_ctx)) + return 1; + } + return 0; +} + + +static int hostapd_wpa_auth_for_each_auth( + void *ctx, int (*cb)(struct wpa_authenticator *sm, void *ctx), + void *cb_ctx) +{ + struct hostapd_data *hapd = ctx; + struct wpa_auth_iface_iter_data data; + if (hapd->iface->for_each_interface == NULL) + return -1; + data.cb = cb; + data.cb_ctx = cb_ctx; + return hapd->iface->for_each_interface(hapd->iface->interfaces, + wpa_auth_iface_iter, &data); +} + + +#ifdef CONFIG_IEEE80211R + +struct wpa_auth_ft_iface_iter_data { + struct hostapd_data *src_hapd; + const u8 *dst; + const u8 *data; + size_t data_len; +}; + + +static int hostapd_wpa_auth_ft_iter(struct hostapd_iface *iface, void *ctx) +{ + struct wpa_auth_ft_iface_iter_data *idata = ctx; + struct hostapd_data *hapd; + size_t j; + + for (j = 0; j < iface->num_bss; j++) { + hapd = iface->bss[j]; + if (hapd == idata->src_hapd) + continue; + if (os_memcmp(hapd->own_addr, idata->dst, ETH_ALEN) == 0) { + wpa_printf(MSG_DEBUG, "FT: Send RRB data directly to " + "locally managed BSS " MACSTR "@%s -> " + MACSTR "@%s", + MAC2STR(idata->src_hapd->own_addr), + idata->src_hapd->conf->iface, + MAC2STR(hapd->own_addr), hapd->conf->iface); + hostapd_rrb_receive(hapd, idata->src_hapd->own_addr, + idata->data, idata->data_len); + return 1; + } + } + + return 0; +} + +#endif /* CONFIG_IEEE80211R */ + + +static int hostapd_wpa_auth_send_ether(void *ctx, const u8 *dst, u16 proto, + const u8 *data, size_t data_len) +{ + struct hostapd_data *hapd = ctx; + +#ifdef CONFIG_IEEE80211R + if (proto == ETH_P_RRB && hapd->iface->for_each_interface) { + int res; + struct wpa_auth_ft_iface_iter_data idata; + idata.src_hapd = hapd; + idata.dst = dst; + idata.data = data; + idata.data_len = data_len; + res = hapd->iface->for_each_interface(hapd->iface->interfaces, + hostapd_wpa_auth_ft_iter, + &idata); + if (res == 1) + return data_len; + } +#endif /* CONFIG_IEEE80211R */ + + if (hapd->driver && hapd->driver->send_ether) + return hapd->driver->send_ether(hapd->drv_priv, dst, + hapd->own_addr, proto, + data, data_len); + if (hapd->l2 == NULL) + return -1; + return l2_packet_send(hapd->l2, dst, proto, data, data_len); +} + + +#ifdef CONFIG_IEEE80211R + +static int hostapd_wpa_auth_send_ft_action(void *ctx, const u8 *dst, + const u8 *data, size_t data_len) +{ + struct hostapd_data *hapd = ctx; + int res; + struct ieee80211_mgmt *m; + size_t mlen; + struct sta_info *sta; + + sta = ap_get_sta(hapd, dst); + if (sta == NULL || sta->wpa_sm == NULL) + return -1; + + m = os_zalloc(sizeof(*m) + data_len); + if (m == NULL) + return -1; + mlen = ((u8 *) &m->u - (u8 *) m) + data_len; + m->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT, + WLAN_FC_STYPE_ACTION); + os_memcpy(m->da, dst, ETH_ALEN); + os_memcpy(m->sa, hapd->own_addr, ETH_ALEN); + os_memcpy(m->bssid, hapd->own_addr, ETH_ALEN); + os_memcpy(&m->u, data, data_len); + + res = hapd->drv.send_mgmt_frame(hapd, (u8 *) m, mlen); + os_free(m); + return res; +} + + +static struct wpa_state_machine * +hostapd_wpa_auth_add_sta(void *ctx, const u8 *sta_addr) +{ + struct hostapd_data *hapd = ctx; + struct sta_info *sta; + + sta = ap_sta_add(hapd, sta_addr); + if (sta == NULL) + return NULL; + if (sta->wpa_sm) { + sta->auth_alg = WLAN_AUTH_FT; + return sta->wpa_sm; + } + + sta->wpa_sm = wpa_auth_sta_init(hapd->wpa_auth, sta->addr); + if (sta->wpa_sm == NULL) { + ap_free_sta(hapd, sta); + return NULL; + } + sta->auth_alg = WLAN_AUTH_FT; + + return sta->wpa_sm; +} + + +static void hostapd_rrb_receive(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ + struct hostapd_data *hapd = ctx; + wpa_ft_rrb_rx(hapd->wpa_auth, src_addr, buf, len); +} + +#endif /* CONFIG_IEEE80211R */ + + +int hostapd_setup_wpa(struct hostapd_data *hapd) +{ + struct wpa_auth_config _conf; + struct wpa_auth_callbacks cb; + const u8 *wpa_ie; + size_t wpa_ie_len; + + hostapd_wpa_auth_conf(hapd->conf, &_conf); + os_memset(&cb, 0, sizeof(cb)); + cb.ctx = hapd; + cb.logger = hostapd_wpa_auth_logger; + cb.disconnect = hostapd_wpa_auth_disconnect; + cb.mic_failure_report = hostapd_wpa_auth_mic_failure_report; + cb.set_eapol = hostapd_wpa_auth_set_eapol; + cb.get_eapol = hostapd_wpa_auth_get_eapol; + cb.get_psk = hostapd_wpa_auth_get_psk; + cb.get_msk = hostapd_wpa_auth_get_msk; + cb.set_key = hostapd_wpa_auth_set_key; + cb.get_seqnum = hostapd_wpa_auth_get_seqnum; + cb.send_eapol = hostapd_wpa_auth_send_eapol; + cb.for_each_sta = hostapd_wpa_auth_for_each_sta; + cb.for_each_auth = hostapd_wpa_auth_for_each_auth; + cb.send_ether = hostapd_wpa_auth_send_ether; +#ifdef CONFIG_IEEE80211R + cb.send_ft_action = hostapd_wpa_auth_send_ft_action; + cb.add_sta = hostapd_wpa_auth_add_sta; +#endif /* CONFIG_IEEE80211R */ + hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb); + if (hapd->wpa_auth == NULL) { + wpa_printf(MSG_ERROR, "WPA initialization failed."); + return -1; + } + + if (hostapd_set_privacy(hapd, 1)) { + wpa_printf(MSG_ERROR, "Could not set PrivacyInvoked " + "for interface %s", hapd->conf->iface); + return -1; + } + + wpa_ie = wpa_auth_get_wpa_ie(hapd->wpa_auth, &wpa_ie_len); + if (hostapd_set_generic_elem(hapd, wpa_ie, wpa_ie_len)) { + wpa_printf(MSG_ERROR, "Failed to configure WPA IE for " + "the kernel driver."); + return -1; + } + + if (rsn_preauth_iface_init(hapd)) { + wpa_printf(MSG_ERROR, "Initialization of RSN " + "pre-authentication failed."); + return -1; + } + +#ifdef CONFIG_IEEE80211R + if (!hostapd_drv_none(hapd)) { + hapd->l2 = l2_packet_init(hapd->conf->bridge[0] ? + hapd->conf->bridge : + hapd->conf->iface, NULL, ETH_P_RRB, + hostapd_rrb_receive, hapd, 0); + if (hapd->l2 == NULL && + (hapd->driver == NULL || + hapd->driver->send_ether == NULL)) { + wpa_printf(MSG_ERROR, "Failed to open l2_packet " + "interface"); + return -1; + } + } +#endif /* CONFIG_IEEE80211R */ + + return 0; + +} + + +void hostapd_reconfig_wpa(struct hostapd_data *hapd) +{ + struct wpa_auth_config wpa_auth_conf; + hostapd_wpa_auth_conf(hapd->conf, &wpa_auth_conf); + wpa_reconfig(hapd->wpa_auth, &wpa_auth_conf); +} + + +void hostapd_deinit_wpa(struct hostapd_data *hapd) +{ + rsn_preauth_iface_deinit(hapd); + if (hapd->wpa_auth) { + wpa_deinit(hapd->wpa_auth); + hapd->wpa_auth = NULL; + + if (hostapd_set_privacy(hapd, 0)) { + wpa_printf(MSG_DEBUG, "Could not disable " + "PrivacyInvoked for interface %s", + hapd->conf->iface); + } + + if (hostapd_set_generic_elem(hapd, (u8 *) "", 0)) { + wpa_printf(MSG_DEBUG, "Could not remove generic " + "information element from interface %s", + hapd->conf->iface); + } + } + ieee802_1x_deinit(hapd); + +#ifdef CONFIG_IEEE80211R + l2_packet_deinit(hapd->l2); +#endif /* CONFIG_IEEE80211R */ +} diff --git a/contrib/wpa/src/ap/wpa_auth_glue.h b/contrib/wpa/src/ap/wpa_auth_glue.h new file mode 100644 index 0000000..79d7e05 --- /dev/null +++ b/contrib/wpa/src/ap/wpa_auth_glue.h @@ -0,0 +1,22 @@ +/* + * hostapd / WPA authenticator glue code + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPA_AUTH_GLUE_H +#define WPA_AUTH_GLUE_H + +int hostapd_setup_wpa(struct hostapd_data *hapd); +void hostapd_reconfig_wpa(struct hostapd_data *hapd); +void hostapd_deinit_wpa(struct hostapd_data *hapd); + +#endif /* WPA_AUTH_GLUE_H */ diff --git a/contrib/wpa/src/ap/wpa_auth_i.h b/contrib/wpa/src/ap/wpa_auth_i.h new file mode 100644 index 0000000..b69129f --- /dev/null +++ b/contrib/wpa/src/ap/wpa_auth_i.h @@ -0,0 +1,230 @@ +/* + * hostapd - IEEE 802.11i-2004 / WPA Authenticator: Internal definitions + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPA_AUTH_I_H +#define WPA_AUTH_I_H + +/* max(dot11RSNAConfigGroupUpdateCount,dot11RSNAConfigPairwiseUpdateCount) */ +#define RSNA_MAX_EAPOL_RETRIES 4 + +struct wpa_group; + +struct wpa_stsl_negotiation { + struct wpa_stsl_negotiation *next; + u8 initiator[ETH_ALEN]; + u8 peer[ETH_ALEN]; +}; + + +struct wpa_state_machine { + struct wpa_authenticator *wpa_auth; + struct wpa_group *group; + + u8 addr[ETH_ALEN]; + + enum { + WPA_PTK_INITIALIZE, WPA_PTK_DISCONNECT, WPA_PTK_DISCONNECTED, + WPA_PTK_AUTHENTICATION, WPA_PTK_AUTHENTICATION2, + WPA_PTK_INITPMK, WPA_PTK_INITPSK, WPA_PTK_PTKSTART, + WPA_PTK_PTKCALCNEGOTIATING, WPA_PTK_PTKCALCNEGOTIATING2, + WPA_PTK_PTKINITNEGOTIATING, WPA_PTK_PTKINITDONE + } wpa_ptk_state; + + enum { + WPA_PTK_GROUP_IDLE = 0, + WPA_PTK_GROUP_REKEYNEGOTIATING, + WPA_PTK_GROUP_REKEYESTABLISHED, + WPA_PTK_GROUP_KEYERROR + } wpa_ptk_group_state; + + Boolean Init; + Boolean DeauthenticationRequest; + Boolean AuthenticationRequest; + Boolean ReAuthenticationRequest; + Boolean Disconnect; + int TimeoutCtr; + int GTimeoutCtr; + Boolean TimeoutEvt; + Boolean EAPOLKeyReceived; + Boolean EAPOLKeyPairwise; + Boolean EAPOLKeyRequest; + Boolean MICVerified; + Boolean GUpdateStationKeys; + u8 ANonce[WPA_NONCE_LEN]; + u8 SNonce[WPA_NONCE_LEN]; + u8 PMK[PMK_LEN]; + struct wpa_ptk PTK; + Boolean PTK_valid; + Boolean pairwise_set; + int keycount; + Boolean Pair; + struct { + u8 counter[WPA_REPLAY_COUNTER_LEN]; + Boolean valid; + } key_replay[RSNA_MAX_EAPOL_RETRIES]; + Boolean PInitAKeys; /* WPA only, not in IEEE 802.11i */ + Boolean PTKRequest; /* not in IEEE 802.11i state machine */ + Boolean has_GTK; + Boolean PtkGroupInit; /* init request for PTK Group state machine */ + + u8 *last_rx_eapol_key; /* starting from IEEE 802.1X header */ + size_t last_rx_eapol_key_len; + + unsigned int changed:1; + unsigned int in_step_loop:1; + unsigned int pending_deinit:1; + unsigned int started:1; + unsigned int mgmt_frame_prot:1; +#ifdef CONFIG_IEEE80211R + unsigned int ft_completed:1; + unsigned int pmk_r1_name_valid:1; +#endif /* CONFIG_IEEE80211R */ + + u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN]; + int req_replay_counter_used; + + u8 *wpa_ie; + size_t wpa_ie_len; + + enum { + WPA_VERSION_NO_WPA = 0 /* WPA not used */, + WPA_VERSION_WPA = 1 /* WPA / IEEE 802.11i/D3.0 */, + WPA_VERSION_WPA2 = 2 /* WPA2 / IEEE 802.11i */ + } wpa; + int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */ + int wpa_key_mgmt; /* the selected WPA_KEY_MGMT_* */ + struct rsn_pmksa_cache_entry *pmksa; + + u32 dot11RSNAStatsTKIPLocalMICFailures; + u32 dot11RSNAStatsTKIPRemoteMICFailures; + +#ifdef CONFIG_IEEE80211R + u8 xxkey[PMK_LEN]; /* PSK or the second 256 bits of MSK */ + size_t xxkey_len; + u8 pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name derived from FT Auth + * Request */ + u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; /* R0KH-ID from FT Auth Request */ + size_t r0kh_id_len; + u8 sup_pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name from EAPOL-Key + * message 2/4 */ + u8 *assoc_resp_ftie; +#endif /* CONFIG_IEEE80211R */ +}; + + +/* per group key state machine data */ +struct wpa_group { + struct wpa_group *next; + int vlan_id; + + Boolean GInit; + int GKeyDoneStations; + Boolean GTKReKey; + int GTK_len; + int GN, GM; + Boolean GTKAuthenticator; + u8 Counter[WPA_NONCE_LEN]; + + enum { + WPA_GROUP_GTK_INIT = 0, + WPA_GROUP_SETKEYS, WPA_GROUP_SETKEYSDONE + } wpa_group_state; + + u8 GMK[WPA_GMK_LEN]; + u8 GTK[2][WPA_GTK_MAX_LEN]; + u8 GNonce[WPA_NONCE_LEN]; + Boolean changed; +#ifdef CONFIG_IEEE80211W + u8 IGTK[2][WPA_IGTK_LEN]; + int GN_igtk, GM_igtk; +#endif /* CONFIG_IEEE80211W */ +}; + + +struct wpa_ft_pmk_cache; + +/* per authenticator data */ +struct wpa_authenticator { + struct wpa_group *group; + + unsigned int dot11RSNAStatsTKIPRemoteMICFailures; + u32 dot11RSNAAuthenticationSuiteSelected; + u32 dot11RSNAPairwiseCipherSelected; + u32 dot11RSNAGroupCipherSelected; + u8 dot11RSNAPMKIDUsed[PMKID_LEN]; + u32 dot11RSNAAuthenticationSuiteRequested; /* FIX: update */ + u32 dot11RSNAPairwiseCipherRequested; /* FIX: update */ + u32 dot11RSNAGroupCipherRequested; /* FIX: update */ + unsigned int dot11RSNATKIPCounterMeasuresInvoked; + unsigned int dot11RSNA4WayHandshakeFailures; + + struct wpa_stsl_negotiation *stsl_negotiations; + + struct wpa_auth_config conf; + struct wpa_auth_callbacks cb; + + u8 *wpa_ie; + size_t wpa_ie_len; + + u8 addr[ETH_ALEN]; + + struct rsn_pmksa_cache *pmksa; + struct wpa_ft_pmk_cache *ft_pmk_cache; +}; + + +int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, + const u8 *pmkid); +void wpa_auth_logger(struct wpa_authenticator *wpa_auth, const u8 *addr, + logger_level level, const char *txt); +void wpa_auth_vlogger(struct wpa_authenticator *wpa_auth, const u8 *addr, + logger_level level, const char *fmt, ...); +void __wpa_send_eapol(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, int key_info, + const u8 *key_rsc, const u8 *nonce, + const u8 *kde, size_t kde_len, + int keyidx, int encr, int force_version); +int wpa_auth_for_each_sta(struct wpa_authenticator *wpa_auth, + int (*cb)(struct wpa_state_machine *sm, void *ctx), + void *cb_ctx); +int wpa_auth_for_each_auth(struct wpa_authenticator *wpa_auth, + int (*cb)(struct wpa_authenticator *a, void *ctx), + void *cb_ctx); + +#ifdef CONFIG_PEERKEY +int wpa_stsl_remove(struct wpa_authenticator *wpa_auth, + struct wpa_stsl_negotiation *neg); +void wpa_smk_error(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, struct wpa_eapol_key *key); +void wpa_smk_m1(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, struct wpa_eapol_key *key); +void wpa_smk_m3(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, struct wpa_eapol_key *key); +#endif /* CONFIG_PEERKEY */ + +#ifdef CONFIG_IEEE80211R +int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len); +int wpa_write_ftie(struct wpa_auth_config *conf, const u8 *r0kh_id, + size_t r0kh_id_len, + const u8 *anonce, const u8 *snonce, + u8 *buf, size_t len, const u8 *subelem, + size_t subelem_len); +int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk, + struct wpa_ptk *ptk, size_t ptk_len); +struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void); +void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache); +void wpa_ft_install_ptk(struct wpa_state_machine *sm); +#endif /* CONFIG_IEEE80211R */ + +#endif /* WPA_AUTH_I_H */ diff --git a/contrib/wpa/src/ap/wpa_auth_ie.c b/contrib/wpa/src/ap/wpa_auth_ie.c new file mode 100644 index 0000000..f8a1804 --- /dev/null +++ b/contrib/wpa/src/ap/wpa_auth_ie.c @@ -0,0 +1,868 @@ +/* + * hostapd - WPA/RSN IE and KDE definitions + * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "utils/includes.h" + +#include "utils/common.h" +#include "common/ieee802_11_defs.h" +#include "eapol_auth/eapol_auth_sm.h" +#include "ap_config.h" +#include "ieee802_11.h" +#include "wpa_auth.h" +#include "pmksa_cache_auth.h" +#include "wpa_auth_ie.h" +#include "wpa_auth_i.h" + + +static int wpa_write_wpa_ie(struct wpa_auth_config *conf, u8 *buf, size_t len) +{ + struct wpa_ie_hdr *hdr; + int num_suites; + u8 *pos, *count; + + hdr = (struct wpa_ie_hdr *) buf; + hdr->elem_id = WLAN_EID_VENDOR_SPECIFIC; + RSN_SELECTOR_PUT(hdr->oui, WPA_OUI_TYPE); + WPA_PUT_LE16(hdr->version, WPA_VERSION); + pos = (u8 *) (hdr + 1); + + if (conf->wpa_group == WPA_CIPHER_CCMP) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP); + } else if (conf->wpa_group == WPA_CIPHER_TKIP) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP); + } else if (conf->wpa_group == WPA_CIPHER_WEP104) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP104); + } else if (conf->wpa_group == WPA_CIPHER_WEP40) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_WEP40); + } else { + wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).", + conf->wpa_group); + return -1; + } + pos += WPA_SELECTOR_LEN; + + num_suites = 0; + count = pos; + pos += 2; + + if (conf->wpa_pairwise & WPA_CIPHER_CCMP) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_CCMP); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_pairwise & WPA_CIPHER_TKIP) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_TKIP); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_pairwise & WPA_CIPHER_NONE) { + RSN_SELECTOR_PUT(pos, WPA_CIPHER_SUITE_NONE); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + + if (num_suites == 0) { + wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).", + conf->wpa_pairwise); + return -1; + } + WPA_PUT_LE16(count, num_suites); + + num_suites = 0; + count = pos; + pos += 2; + + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { + RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_UNSPEC_802_1X); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { + RSN_SELECTOR_PUT(pos, WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X); + pos += WPA_SELECTOR_LEN; + num_suites++; + } + + if (num_suites == 0) { + wpa_printf(MSG_DEBUG, "Invalid key management type (%d).", + conf->wpa_key_mgmt); + return -1; + } + WPA_PUT_LE16(count, num_suites); + + /* WPA Capabilities; use defaults, so no need to include it */ + + hdr->len = (pos - buf) - 2; + + return pos - buf; +} + + +int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len, + const u8 *pmkid) +{ + struct rsn_ie_hdr *hdr; + int num_suites; + u8 *pos, *count; + u16 capab; + + hdr = (struct rsn_ie_hdr *) buf; + hdr->elem_id = WLAN_EID_RSN; + WPA_PUT_LE16(hdr->version, RSN_VERSION); + pos = (u8 *) (hdr + 1); + + if (conf->wpa_group == WPA_CIPHER_CCMP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + } else if (conf->wpa_group == WPA_CIPHER_TKIP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); + } else if (conf->wpa_group == WPA_CIPHER_WEP104) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP104); + } else if (conf->wpa_group == WPA_CIPHER_WEP40) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_WEP40); + } else { + wpa_printf(MSG_DEBUG, "Invalid group cipher (%d).", + conf->wpa_group); + return -1; + } + pos += RSN_SELECTOR_LEN; + + num_suites = 0; + count = pos; + pos += 2; + + if (conf->rsn_pairwise & WPA_CIPHER_CCMP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_CCMP); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (conf->rsn_pairwise & WPA_CIPHER_TKIP) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_TKIP); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (conf->rsn_pairwise & WPA_CIPHER_NONE) { + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_NONE); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + + if (num_suites == 0) { + wpa_printf(MSG_DEBUG, "Invalid pairwise cipher (%d).", + conf->rsn_pairwise); + return -1; + } + WPA_PUT_LE16(count, num_suites); + + num_suites = 0; + count = pos; + pos += 2; + + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_UNSPEC_802_1X); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#ifdef CONFIG_IEEE80211R + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_802_1X); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_FT_PSK) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_FT_PSK); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_802_1X_SHA256); + pos += RSN_SELECTOR_LEN; + num_suites++; + } + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK_SHA256) { + RSN_SELECTOR_PUT(pos, RSN_AUTH_KEY_MGMT_PSK_SHA256); + pos += RSN_SELECTOR_LEN; + num_suites++; + } +#endif /* CONFIG_IEEE80211W */ + + if (num_suites == 0) { + wpa_printf(MSG_DEBUG, "Invalid key management type (%d).", + conf->wpa_key_mgmt); + return -1; + } + WPA_PUT_LE16(count, num_suites); + + /* RSN Capabilities */ + capab = 0; + if (conf->rsn_preauth) + capab |= WPA_CAPABILITY_PREAUTH; + if (conf->peerkey) + capab |= WPA_CAPABILITY_PEERKEY_ENABLED; + if (conf->wmm_enabled) { + /* 4 PTKSA replay counters when using WMM */ + capab |= (RSN_NUM_REPLAY_COUNTERS_16 << 2); + } +#ifdef CONFIG_IEEE80211W + if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { + capab |= WPA_CAPABILITY_MFPC; + if (conf->ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) + capab |= WPA_CAPABILITY_MFPR; + } +#endif /* CONFIG_IEEE80211W */ + WPA_PUT_LE16(pos, capab); + pos += 2; + + if (pmkid) { + if (pos + 2 + PMKID_LEN > buf + len) + return -1; + /* PMKID Count */ + WPA_PUT_LE16(pos, 1); + pos += 2; + os_memcpy(pos, pmkid, PMKID_LEN); + pos += PMKID_LEN; + } + +#ifdef CONFIG_IEEE80211W + if (conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { + if (pos + 2 + 4 > buf + len) + return -1; + if (pmkid == NULL) { + /* PMKID Count */ + WPA_PUT_LE16(pos, 0); + pos += 2; + } + + /* Management Group Cipher Suite */ + RSN_SELECTOR_PUT(pos, RSN_CIPHER_SUITE_AES_128_CMAC); + pos += RSN_SELECTOR_LEN; + } +#endif /* CONFIG_IEEE80211W */ + + hdr->len = (pos - buf) - 2; + + return pos - buf; +} + + +int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth) +{ + u8 *pos, buf[128]; + int res; + + pos = buf; + + if (wpa_auth->conf.wpa & WPA_PROTO_RSN) { + res = wpa_write_rsn_ie(&wpa_auth->conf, + pos, buf + sizeof(buf) - pos, NULL); + if (res < 0) + return res; + pos += res; + } +#ifdef CONFIG_IEEE80211R + if (wpa_auth->conf.wpa_key_mgmt & + (WPA_KEY_MGMT_FT_IEEE8021X | WPA_KEY_MGMT_FT_PSK)) { + res = wpa_write_mdie(&wpa_auth->conf, pos, + buf + sizeof(buf) - pos); + if (res < 0) + return res; + pos += res; + } +#endif /* CONFIG_IEEE80211R */ + if (wpa_auth->conf.wpa & WPA_PROTO_WPA) { + res = wpa_write_wpa_ie(&wpa_auth->conf, + pos, buf + sizeof(buf) - pos); + if (res < 0) + return res; + pos += res; + } + + os_free(wpa_auth->wpa_ie); + wpa_auth->wpa_ie = os_malloc(pos - buf); + if (wpa_auth->wpa_ie == NULL) + return -1; + os_memcpy(wpa_auth->wpa_ie, buf, pos - buf); + wpa_auth->wpa_ie_len = pos - buf; + + return 0; +} + + +u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len, + const u8 *data2, size_t data2_len) +{ + *pos++ = WLAN_EID_VENDOR_SPECIFIC; + *pos++ = RSN_SELECTOR_LEN + data_len + data2_len; + RSN_SELECTOR_PUT(pos, kde); + pos += RSN_SELECTOR_LEN; + os_memcpy(pos, data, data_len); + pos += data_len; + if (data2) { + os_memcpy(pos, data2, data2_len); + pos += data2_len; + } + return pos; +} + + +static int wpa_selector_to_bitfield(const u8 *s) +{ + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_NONE) + return WPA_CIPHER_NONE; + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP40) + return WPA_CIPHER_WEP40; + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_TKIP) + return WPA_CIPHER_TKIP; + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_CCMP) + return WPA_CIPHER_CCMP; + if (RSN_SELECTOR_GET(s) == WPA_CIPHER_SUITE_WEP104) + return WPA_CIPHER_WEP104; + return 0; +} + + +static int wpa_key_mgmt_to_bitfield(const u8 *s) +{ + if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_UNSPEC_802_1X) + return WPA_KEY_MGMT_IEEE8021X; + if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X) + return WPA_KEY_MGMT_PSK; + if (RSN_SELECTOR_GET(s) == WPA_AUTH_KEY_MGMT_NONE) + return WPA_KEY_MGMT_WPA_NONE; + return 0; +} + + +static int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len, + struct wpa_ie_data *data) +{ + const struct wpa_ie_hdr *hdr; + const u8 *pos; + int left; + int i, count; + + os_memset(data, 0, sizeof(*data)); + data->pairwise_cipher = WPA_CIPHER_TKIP; + data->group_cipher = WPA_CIPHER_TKIP; + data->key_mgmt = WPA_KEY_MGMT_IEEE8021X; + data->mgmt_group_cipher = 0; + + if (wpa_ie_len < sizeof(struct wpa_ie_hdr)) + return -1; + + hdr = (const struct wpa_ie_hdr *) wpa_ie; + + if (hdr->elem_id != WLAN_EID_VENDOR_SPECIFIC || + hdr->len != wpa_ie_len - 2 || + RSN_SELECTOR_GET(hdr->oui) != WPA_OUI_TYPE || + WPA_GET_LE16(hdr->version) != WPA_VERSION) { + return -2; + } + + pos = (const u8 *) (hdr + 1); + left = wpa_ie_len - sizeof(*hdr); + + if (left >= WPA_SELECTOR_LEN) { + data->group_cipher = wpa_selector_to_bitfield(pos); + pos += WPA_SELECTOR_LEN; + left -= WPA_SELECTOR_LEN; + } else if (left > 0) + return -3; + + if (left >= 2) { + data->pairwise_cipher = 0; + count = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + if (count == 0 || left < count * WPA_SELECTOR_LEN) + return -4; + for (i = 0; i < count; i++) { + data->pairwise_cipher |= wpa_selector_to_bitfield(pos); + pos += WPA_SELECTOR_LEN; + left -= WPA_SELECTOR_LEN; + } + } else if (left == 1) + return -5; + + if (left >= 2) { + data->key_mgmt = 0; + count = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + if (count == 0 || left < count * WPA_SELECTOR_LEN) + return -6; + for (i = 0; i < count; i++) { + data->key_mgmt |= wpa_key_mgmt_to_bitfield(pos); + pos += WPA_SELECTOR_LEN; + left -= WPA_SELECTOR_LEN; + } + } else if (left == 1) + return -7; + + if (left >= 2) { + data->capabilities = WPA_GET_LE16(pos); + pos += 2; + left -= 2; + } + + if (left > 0) { + return -8; + } + + return 0; +} + + +struct wpa_auth_okc_iter_data { + struct rsn_pmksa_cache_entry *pmksa; + const u8 *aa; + const u8 *spa; + const u8 *pmkid; +}; + + +static int wpa_auth_okc_iter(struct wpa_authenticator *a, void *ctx) +{ + struct wpa_auth_okc_iter_data *data = ctx; + data->pmksa = pmksa_cache_get_okc(a->pmksa, data->aa, data->spa, + data->pmkid); + if (data->pmksa) + return 1; + return 0; +} + + +int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth, + struct wpa_state_machine *sm, + const u8 *wpa_ie, size_t wpa_ie_len, + const u8 *mdie, size_t mdie_len) +{ + struct wpa_ie_data data; + int ciphers, key_mgmt, res, version; + u32 selector; + size_t i; + const u8 *pmkid = NULL; + + if (wpa_auth == NULL || sm == NULL) + return WPA_NOT_ENABLED; + + if (wpa_ie == NULL || wpa_ie_len < 1) + return WPA_INVALID_IE; + + if (wpa_ie[0] == WLAN_EID_RSN) + version = WPA_PROTO_RSN; + else + version = WPA_PROTO_WPA; + + if (!(wpa_auth->conf.wpa & version)) { + wpa_printf(MSG_DEBUG, "Invalid WPA proto (%d) from " MACSTR, + version, MAC2STR(sm->addr)); + return WPA_INVALID_PROTO; + } + + if (version == WPA_PROTO_RSN) { + res = wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, &data); + + selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; + if (0) { + } +#ifdef CONFIG_IEEE80211R + else if (data.key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) + selector = RSN_AUTH_KEY_MGMT_FT_802_1X; + else if (data.key_mgmt & WPA_KEY_MGMT_FT_PSK) + selector = RSN_AUTH_KEY_MGMT_FT_PSK; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) + selector = RSN_AUTH_KEY_MGMT_802_1X_SHA256; + else if (data.key_mgmt & WPA_KEY_MGMT_PSK_SHA256) + selector = RSN_AUTH_KEY_MGMT_PSK_SHA256; +#endif /* CONFIG_IEEE80211W */ + else if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) + selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X; + else if (data.key_mgmt & WPA_KEY_MGMT_PSK) + selector = RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X; + wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector; + + selector = RSN_CIPHER_SUITE_CCMP; + if (data.pairwise_cipher & WPA_CIPHER_CCMP) + selector = RSN_CIPHER_SUITE_CCMP; + else if (data.pairwise_cipher & WPA_CIPHER_TKIP) + selector = RSN_CIPHER_SUITE_TKIP; + else if (data.pairwise_cipher & WPA_CIPHER_WEP104) + selector = RSN_CIPHER_SUITE_WEP104; + else if (data.pairwise_cipher & WPA_CIPHER_WEP40) + selector = RSN_CIPHER_SUITE_WEP40; + else if (data.pairwise_cipher & WPA_CIPHER_NONE) + selector = RSN_CIPHER_SUITE_NONE; + wpa_auth->dot11RSNAPairwiseCipherSelected = selector; + + selector = RSN_CIPHER_SUITE_CCMP; + if (data.group_cipher & WPA_CIPHER_CCMP) + selector = RSN_CIPHER_SUITE_CCMP; + else if (data.group_cipher & WPA_CIPHER_TKIP) + selector = RSN_CIPHER_SUITE_TKIP; + else if (data.group_cipher & WPA_CIPHER_WEP104) + selector = RSN_CIPHER_SUITE_WEP104; + else if (data.group_cipher & WPA_CIPHER_WEP40) + selector = RSN_CIPHER_SUITE_WEP40; + else if (data.group_cipher & WPA_CIPHER_NONE) + selector = RSN_CIPHER_SUITE_NONE; + wpa_auth->dot11RSNAGroupCipherSelected = selector; + } else { + res = wpa_parse_wpa_ie_wpa(wpa_ie, wpa_ie_len, &data); + + selector = WPA_AUTH_KEY_MGMT_UNSPEC_802_1X; + if (data.key_mgmt & WPA_KEY_MGMT_IEEE8021X) + selector = WPA_AUTH_KEY_MGMT_UNSPEC_802_1X; + else if (data.key_mgmt & WPA_KEY_MGMT_PSK) + selector = WPA_AUTH_KEY_MGMT_PSK_OVER_802_1X; + wpa_auth->dot11RSNAAuthenticationSuiteSelected = selector; + + selector = WPA_CIPHER_SUITE_TKIP; + if (data.pairwise_cipher & WPA_CIPHER_CCMP) + selector = WPA_CIPHER_SUITE_CCMP; + else if (data.pairwise_cipher & WPA_CIPHER_TKIP) + selector = WPA_CIPHER_SUITE_TKIP; + else if (data.pairwise_cipher & WPA_CIPHER_WEP104) + selector = WPA_CIPHER_SUITE_WEP104; + else if (data.pairwise_cipher & WPA_CIPHER_WEP40) + selector = WPA_CIPHER_SUITE_WEP40; + else if (data.pairwise_cipher & WPA_CIPHER_NONE) + selector = WPA_CIPHER_SUITE_NONE; + wpa_auth->dot11RSNAPairwiseCipherSelected = selector; + + selector = WPA_CIPHER_SUITE_TKIP; + if (data.group_cipher & WPA_CIPHER_CCMP) + selector = WPA_CIPHER_SUITE_CCMP; + else if (data.group_cipher & WPA_CIPHER_TKIP) + selector = WPA_CIPHER_SUITE_TKIP; + else if (data.group_cipher & WPA_CIPHER_WEP104) + selector = WPA_CIPHER_SUITE_WEP104; + else if (data.group_cipher & WPA_CIPHER_WEP40) + selector = WPA_CIPHER_SUITE_WEP40; + else if (data.group_cipher & WPA_CIPHER_NONE) + selector = WPA_CIPHER_SUITE_NONE; + wpa_auth->dot11RSNAGroupCipherSelected = selector; + } + if (res) { + wpa_printf(MSG_DEBUG, "Failed to parse WPA/RSN IE from " + MACSTR " (res=%d)", MAC2STR(sm->addr), res); + wpa_hexdump(MSG_DEBUG, "WPA/RSN IE", wpa_ie, wpa_ie_len); + return WPA_INVALID_IE; + } + + if (data.group_cipher != wpa_auth->conf.wpa_group) { + wpa_printf(MSG_DEBUG, "Invalid WPA group cipher (0x%x) from " + MACSTR, data.group_cipher, MAC2STR(sm->addr)); + return WPA_INVALID_GROUP; + } + + key_mgmt = data.key_mgmt & wpa_auth->conf.wpa_key_mgmt; + if (!key_mgmt) { + wpa_printf(MSG_DEBUG, "Invalid WPA key mgmt (0x%x) from " + MACSTR, data.key_mgmt, MAC2STR(sm->addr)); + return WPA_INVALID_AKMP; + } + if (0) { + } +#ifdef CONFIG_IEEE80211R + else if (key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_IEEE8021X; + else if (key_mgmt & WPA_KEY_MGMT_FT_PSK) + sm->wpa_key_mgmt = WPA_KEY_MGMT_FT_PSK; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X_SHA256) + sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X_SHA256; + else if (key_mgmt & WPA_KEY_MGMT_PSK_SHA256) + sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK_SHA256; +#endif /* CONFIG_IEEE80211W */ + else if (key_mgmt & WPA_KEY_MGMT_IEEE8021X) + sm->wpa_key_mgmt = WPA_KEY_MGMT_IEEE8021X; + else + sm->wpa_key_mgmt = WPA_KEY_MGMT_PSK; + + if (version == WPA_PROTO_RSN) + ciphers = data.pairwise_cipher & wpa_auth->conf.rsn_pairwise; + else + ciphers = data.pairwise_cipher & wpa_auth->conf.wpa_pairwise; + if (!ciphers) { + wpa_printf(MSG_DEBUG, "Invalid %s pairwise cipher (0x%x) " + "from " MACSTR, + version == WPA_PROTO_RSN ? "RSN" : "WPA", + data.pairwise_cipher, MAC2STR(sm->addr)); + return WPA_INVALID_PAIRWISE; + } + +#ifdef CONFIG_IEEE80211W + if (wpa_auth->conf.ieee80211w == MGMT_FRAME_PROTECTION_REQUIRED) { + if (!(data.capabilities & WPA_CAPABILITY_MFPC)) { + wpa_printf(MSG_DEBUG, "Management frame protection " + "required, but client did not enable it"); + return WPA_MGMT_FRAME_PROTECTION_VIOLATION; + } + + if (ciphers & WPA_CIPHER_TKIP) { + wpa_printf(MSG_DEBUG, "Management frame protection " + "cannot use TKIP"); + return WPA_MGMT_FRAME_PROTECTION_VIOLATION; + } + + if (data.mgmt_group_cipher != WPA_CIPHER_AES_128_CMAC) { + wpa_printf(MSG_DEBUG, "Unsupported management group " + "cipher %d", data.mgmt_group_cipher); + return WPA_INVALID_MGMT_GROUP_CIPHER; + } + } + + if (wpa_auth->conf.ieee80211w == NO_MGMT_FRAME_PROTECTION || + !(data.capabilities & WPA_CAPABILITY_MFPC)) + sm->mgmt_frame_prot = 0; + else + sm->mgmt_frame_prot = 1; +#endif /* CONFIG_IEEE80211W */ + +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) { + if (mdie == NULL || mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) { + wpa_printf(MSG_DEBUG, "RSN: Trying to use FT, but " + "MDIE not included"); + return WPA_INVALID_MDIE; + } + if (os_memcmp(mdie, wpa_auth->conf.mobility_domain, + MOBILITY_DOMAIN_ID_LEN) != 0) { + wpa_hexdump(MSG_DEBUG, "RSN: Attempted to use unknown " + "MDIE", mdie, MOBILITY_DOMAIN_ID_LEN); + return WPA_INVALID_MDIE; + } + } +#endif /* CONFIG_IEEE80211R */ + + if (ciphers & WPA_CIPHER_CCMP) + sm->pairwise = WPA_CIPHER_CCMP; + else + sm->pairwise = WPA_CIPHER_TKIP; + + /* TODO: clear WPA/WPA2 state if STA changes from one to another */ + if (wpa_ie[0] == WLAN_EID_RSN) + sm->wpa = WPA_VERSION_WPA2; + else + sm->wpa = WPA_VERSION_WPA; + + sm->pmksa = NULL; + for (i = 0; i < data.num_pmkid; i++) { + wpa_hexdump(MSG_DEBUG, "RSN IE: STA PMKID", + &data.pmkid[i * PMKID_LEN], PMKID_LEN); + sm->pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sm->addr, + &data.pmkid[i * PMKID_LEN]); + if (sm->pmksa) { + pmkid = sm->pmksa->pmkid; + break; + } + } + for (i = 0; sm->pmksa == NULL && wpa_auth->conf.okc && + i < data.num_pmkid; i++) { + struct wpa_auth_okc_iter_data idata; + idata.pmksa = NULL; + idata.aa = wpa_auth->addr; + idata.spa = sm->addr; + idata.pmkid = &data.pmkid[i * PMKID_LEN]; + wpa_auth_for_each_auth(wpa_auth, wpa_auth_okc_iter, &idata); + if (idata.pmksa) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "OKC match for PMKID"); + sm->pmksa = pmksa_cache_add_okc(wpa_auth->pmksa, + idata.pmksa, + wpa_auth->addr, + idata.pmkid); + pmkid = idata.pmkid; + break; + } + } + if (sm->pmksa) { + wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG, + "PMKID found from PMKSA cache " + "eap_type=%d vlan_id=%d", + sm->pmksa->eap_type_authsrv, + sm->pmksa->vlan_id); + os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmkid, PMKID_LEN); + } + + if (sm->wpa_ie == NULL || sm->wpa_ie_len < wpa_ie_len) { + os_free(sm->wpa_ie); + sm->wpa_ie = os_malloc(wpa_ie_len); + if (sm->wpa_ie == NULL) + return WPA_ALLOC_FAIL; + } + os_memcpy(sm->wpa_ie, wpa_ie, wpa_ie_len); + sm->wpa_ie_len = wpa_ie_len; + + return WPA_IE_OK; +} + + +/** + * wpa_parse_generic - Parse EAPOL-Key Key Data Generic IEs + * @pos: Pointer to the IE header + * @end: Pointer to the end of the Key Data buffer + * @ie: Pointer to parsed IE data + * Returns: 0 on success, 1 if end mark is found, -1 on failure + */ +static int wpa_parse_generic(const u8 *pos, const u8 *end, + struct wpa_eapol_ie_parse *ie) +{ + if (pos[1] == 0) + return 1; + + if (pos[1] >= 6 && + RSN_SELECTOR_GET(pos + 2) == WPA_OUI_TYPE && + pos[2 + WPA_SELECTOR_LEN] == 1 && + pos[2 + WPA_SELECTOR_LEN + 1] == 0) { + ie->wpa_ie = pos; + ie->wpa_ie_len = pos[1] + 2; + return 0; + } + + if (pos + 1 + RSN_SELECTOR_LEN < end && + pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) { + ie->pmkid = pos + 2 + RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_GROUPKEY) { + ie->gtk = pos + 2 + RSN_SELECTOR_LEN; + ie->gtk_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_MAC_ADDR) { + ie->mac_addr = pos + 2 + RSN_SELECTOR_LEN; + ie->mac_addr_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + +#ifdef CONFIG_PEERKEY + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) { + ie->smk = pos + 2 + RSN_SELECTOR_LEN; + ie->smk_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) { + ie->nonce = pos + 2 + RSN_SELECTOR_LEN; + ie->nonce_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) { + ie->lifetime = pos + 2 + RSN_SELECTOR_LEN; + ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } + + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) { + ie->error = pos + 2 + RSN_SELECTOR_LEN; + ie->error_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } +#endif /* CONFIG_PEERKEY */ + +#ifdef CONFIG_IEEE80211W + if (pos[1] > RSN_SELECTOR_LEN + 2 && + RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) { + ie->igtk = pos + 2 + RSN_SELECTOR_LEN; + ie->igtk_len = pos[1] - RSN_SELECTOR_LEN; + return 0; + } +#endif /* CONFIG_IEEE80211W */ + + return 0; +} + + +/** + * wpa_parse_kde_ies - Parse EAPOL-Key Key Data IEs + * @buf: Pointer to the Key Data buffer + * @len: Key Data Length + * @ie: Pointer to parsed IE data + * Returns: 0 on success, -1 on failure + */ +int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie) +{ + const u8 *pos, *end; + int ret = 0; + + os_memset(ie, 0, sizeof(*ie)); + for (pos = buf, end = pos + len; pos + 1 < end; pos += 2 + pos[1]) { + if (pos[0] == 0xdd && + ((pos == buf + len - 1) || pos[1] == 0)) { + /* Ignore padding */ + break; + } + if (pos + 2 + pos[1] > end) { + wpa_printf(MSG_DEBUG, "WPA: EAPOL-Key Key Data " + "underflow (ie=%d len=%d pos=%d)", + pos[0], pos[1], (int) (pos - buf)); + wpa_hexdump_key(MSG_DEBUG, "WPA: Key Data", + buf, len); + ret = -1; + break; + } + if (*pos == WLAN_EID_RSN) { + ie->rsn_ie = pos; + ie->rsn_ie_len = pos[1] + 2; +#ifdef CONFIG_IEEE80211R + } else if (*pos == WLAN_EID_MOBILITY_DOMAIN) { + ie->mdie = pos; + ie->mdie_len = pos[1] + 2; + } else if (*pos == WLAN_EID_FAST_BSS_TRANSITION) { + ie->ftie = pos; + ie->ftie_len = pos[1] + 2; +#endif /* CONFIG_IEEE80211R */ + } else if (*pos == WLAN_EID_VENDOR_SPECIFIC) { + ret = wpa_parse_generic(pos, end, ie); + if (ret < 0) + break; + if (ret > 0) { + ret = 0; + break; + } + } else { + wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized EAPOL-Key " + "Key Data IE", pos, 2 + pos[1]); + } + } + + return ret; +} + + +int wpa_auth_uses_mfp(struct wpa_state_machine *sm) +{ + return sm ? sm->mgmt_frame_prot : 0; +} diff --git a/contrib/wpa/src/ap/wpa_auth_ie.h b/contrib/wpa/src/ap/wpa_auth_ie.h new file mode 100644 index 0000000..61d4cb4 --- /dev/null +++ b/contrib/wpa/src/ap/wpa_auth_ie.h @@ -0,0 +1,56 @@ +/* + * hostapd - WPA/RSN IE and KDE definitions + * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPA_AUTH_IE_H +#define WPA_AUTH_IE_H + +struct wpa_eapol_ie_parse { + const u8 *wpa_ie; + size_t wpa_ie_len; + const u8 *rsn_ie; + size_t rsn_ie_len; + const u8 *pmkid; + const u8 *gtk; + size_t gtk_len; + const u8 *mac_addr; + size_t mac_addr_len; +#ifdef CONFIG_PEERKEY + const u8 *smk; + size_t smk_len; + const u8 *nonce; + size_t nonce_len; + const u8 *lifetime; + size_t lifetime_len; + const u8 *error; + size_t error_len; +#endif /* CONFIG_PEERKEY */ +#ifdef CONFIG_IEEE80211W + const u8 *igtk; + size_t igtk_len; +#endif /* CONFIG_IEEE80211W */ +#ifdef CONFIG_IEEE80211R + const u8 *mdie; + size_t mdie_len; + const u8 *ftie; + size_t ftie_len; +#endif /* CONFIG_IEEE80211R */ +}; + +int wpa_parse_kde_ies(const u8 *buf, size_t len, + struct wpa_eapol_ie_parse *ie); +u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len, + const u8 *data2, size_t data2_len); +int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth); + +#endif /* WPA_AUTH_IE_H */ diff --git a/contrib/wpa/src/ap/wps_hostapd.c b/contrib/wpa/src/ap/wps_hostapd.c new file mode 100644 index 0000000..a6ffd4d --- /dev/null +++ b/contrib/wpa/src/ap/wps_hostapd.c @@ -0,0 +1,1020 @@ +/* + * hostapd / WPS integration + * Copyright (c) 2008-2010, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published 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 "utils/includes.h" + +#include "utils/common.h" +#include "utils/eloop.h" +#include "utils/uuid.h" +#include "crypto/dh_groups.h" +#include "common/wpa_ctrl.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" +#include "eapol_auth/eapol_auth_sm.h" +#include "eapol_auth/eapol_auth_sm_i.h" +#include "wps/wps.h" +#include "wps/wps_defs.h" +#include "wps/wps_dev_attr.h" +#include "hostapd.h" +#include "ap_config.h" +#include "beacon.h" +#include "sta_info.h" +#include "wps_hostapd.h" + + +#ifdef CONFIG_WPS_UPNP +#include "wps/wps_upnp.h" +static int hostapd_wps_upnp_init(struct hostapd_data *hapd, + struct wps_context *wps); +static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd); +#endif /* CONFIG_WPS_UPNP */ + +static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, + const u8 *ie, size_t ie_len); +static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx); + + +static int hostapd_wps_new_psk_cb(void *ctx, const u8 *mac_addr, const u8 *psk, + size_t psk_len) +{ + struct hostapd_data *hapd = ctx; + struct hostapd_wpa_psk *p; + struct hostapd_ssid *ssid = &hapd->conf->ssid; + + wpa_printf(MSG_DEBUG, "Received new WPA/WPA2-PSK from WPS for STA " + MACSTR, MAC2STR(mac_addr)); + wpa_hexdump_key(MSG_DEBUG, "Per-device PSK", psk, psk_len); + + if (psk_len != PMK_LEN) { + wpa_printf(MSG_DEBUG, "Unexpected PSK length %lu", + (unsigned long) psk_len); + return -1; + } + + /* Add the new PSK to runtime PSK list */ + p = os_zalloc(sizeof(*p)); + if (p == NULL) + return -1; + os_memcpy(p->addr, mac_addr, ETH_ALEN); + os_memcpy(p->psk, psk, PMK_LEN); + + p->next = ssid->wpa_psk; + ssid->wpa_psk = p; + + if (ssid->wpa_psk_file) { + FILE *f; + char hex[PMK_LEN * 2 + 1]; + /* Add the new PSK to PSK list file */ + f = fopen(ssid->wpa_psk_file, "a"); + if (f == NULL) { + wpa_printf(MSG_DEBUG, "Failed to add the PSK to " + "'%s'", ssid->wpa_psk_file); + return -1; + } + + wpa_snprintf_hex(hex, sizeof(hex), psk, psk_len); + fprintf(f, MACSTR " %s\n", MAC2STR(mac_addr), hex); + fclose(f); + } + + return 0; +} + + +static int hostapd_wps_set_ie_cb(void *ctx, struct wpabuf *beacon_ie, + struct wpabuf *probe_resp_ie) +{ + struct hostapd_data *hapd = ctx; + wpabuf_free(hapd->wps_beacon_ie); + hapd->wps_beacon_ie = beacon_ie; + wpabuf_free(hapd->wps_probe_resp_ie); + hapd->wps_probe_resp_ie = probe_resp_ie; + ieee802_11_set_beacon(hapd); + return hapd->drv.set_ap_wps_ie(hapd); +} + + +static void hostapd_wps_pin_needed_cb(void *ctx, const u8 *uuid_e, + const struct wps_device_data *dev) +{ + struct hostapd_data *hapd = ctx; + char uuid[40], txt[400]; + int len; + char devtype[WPS_DEV_TYPE_BUFSIZE]; + if (uuid_bin2str(uuid_e, uuid, sizeof(uuid))) + return; + wpa_printf(MSG_DEBUG, "WPS: PIN needed for E-UUID %s", uuid); + len = os_snprintf(txt, sizeof(txt), WPS_EVENT_PIN_NEEDED + "%s " MACSTR " [%s|%s|%s|%s|%s|%s]", + uuid, MAC2STR(dev->mac_addr), dev->device_name, + dev->manufacturer, dev->model_name, + dev->model_number, dev->serial_number, + wps_dev_type_bin2str(dev->pri_dev_type, devtype, + sizeof(devtype))); + if (len > 0 && len < (int) sizeof(txt)) + wpa_msg(hapd->msg_ctx, MSG_INFO, "%s", txt); + + if (hapd->conf->wps_pin_requests) { + FILE *f; + struct os_time t; + f = fopen(hapd->conf->wps_pin_requests, "a"); + if (f == NULL) + return; + os_get_time(&t); + fprintf(f, "%ld\t%s\t" MACSTR "\t%s\t%s\t%s\t%s\t%s" + "\t%s\n", + t.sec, uuid, MAC2STR(dev->mac_addr), dev->device_name, + dev->manufacturer, dev->model_name, dev->model_number, + dev->serial_number, + wps_dev_type_bin2str(dev->pri_dev_type, devtype, + sizeof(devtype))); + fclose(f); + } +} + + +static void hostapd_wps_reg_success_cb(void *ctx, const u8 *mac_addr, + const u8 *uuid_e) +{ + struct hostapd_data *hapd = ctx; + char uuid[40]; + if (uuid_bin2str(uuid_e, uuid, sizeof(uuid))) + return; + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_REG_SUCCESS MACSTR " %s", + MAC2STR(mac_addr), uuid); + if (hapd->wps_reg_success_cb) + hapd->wps_reg_success_cb(hapd->wps_reg_success_cb_ctx, + mac_addr, uuid_e); +} + + +static void hostapd_wps_enrollee_seen_cb(void *ctx, const u8 *addr, + const u8 *uuid_e, + const u8 *pri_dev_type, + u16 config_methods, + u16 dev_password_id, u8 request_type, + const char *dev_name) +{ + struct hostapd_data *hapd = ctx; + char uuid[40]; + char devtype[WPS_DEV_TYPE_BUFSIZE]; + if (uuid_bin2str(uuid_e, uuid, sizeof(uuid))) + return; + if (dev_name == NULL) + dev_name = ""; + wpa_msg_ctrl(hapd->msg_ctx, MSG_INFO, WPS_EVENT_ENROLLEE_SEEN MACSTR + " %s %s 0x%x %u %u [%s]", + MAC2STR(addr), uuid, + wps_dev_type_bin2str(pri_dev_type, devtype, + sizeof(devtype)), + config_methods, dev_password_id, request_type, dev_name); +} + + +static int str_starts(const char *str, const char *start) +{ + return os_strncmp(str, start, os_strlen(start)) == 0; +} + + +static void wps_reload_config(void *eloop_data, void *user_ctx) +{ + struct hostapd_iface *iface = eloop_data; + + wpa_printf(MSG_DEBUG, "WPS: Reload configuration data"); + if (iface->reload_config(iface) < 0) { + wpa_printf(MSG_WARNING, "WPS: Failed to reload the updated " + "configuration"); + } +} + + +static int hostapd_wps_cred_cb(void *ctx, const struct wps_credential *cred) +{ + struct hostapd_data *hapd = ctx; + FILE *oconf, *nconf; + size_t len, i; + char *tmp_fname; + char buf[1024]; + int multi_bss; + int wpa; + + wpa_hexdump_key(MSG_DEBUG, "WPS: Received Credential attribute", + cred->cred_attr, cred->cred_attr_len); + + wpa_printf(MSG_DEBUG, "WPS: Received new AP Settings"); + wpa_hexdump_ascii(MSG_DEBUG, "WPS: SSID", cred->ssid, cred->ssid_len); + wpa_printf(MSG_DEBUG, "WPS: Authentication Type 0x%x", + cred->auth_type); + wpa_printf(MSG_DEBUG, "WPS: Encryption Type 0x%x", cred->encr_type); + wpa_printf(MSG_DEBUG, "WPS: Network Key Index %d", cred->key_idx); + wpa_hexdump_key(MSG_DEBUG, "WPS: Network Key", + cred->key, cred->key_len); + wpa_printf(MSG_DEBUG, "WPS: MAC Address " MACSTR, + MAC2STR(cred->mac_addr)); + + if ((hapd->conf->wps_cred_processing == 1 || + hapd->conf->wps_cred_processing == 2) && cred->cred_attr) { + size_t blen = cred->cred_attr_len * 2 + 1; + char *_buf = os_malloc(blen); + if (_buf) { + wpa_snprintf_hex(_buf, blen, + cred->cred_attr, cred->cred_attr_len); + wpa_msg(hapd->msg_ctx, MSG_INFO, "%s%s", + WPS_EVENT_NEW_AP_SETTINGS, _buf); + os_free(_buf); + } + } else + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_NEW_AP_SETTINGS); + + if (hapd->conf->wps_cred_processing == 1) + return 0; + + os_memcpy(hapd->wps->ssid, cred->ssid, cred->ssid_len); + hapd->wps->ssid_len = cred->ssid_len; + hapd->wps->encr_types = cred->encr_type; + hapd->wps->auth_types = cred->auth_type; + if (cred->key_len == 0) { + os_free(hapd->wps->network_key); + hapd->wps->network_key = NULL; + hapd->wps->network_key_len = 0; + } else { + if (hapd->wps->network_key == NULL || + hapd->wps->network_key_len < cred->key_len) { + hapd->wps->network_key_len = 0; + os_free(hapd->wps->network_key); + hapd->wps->network_key = os_malloc(cred->key_len); + if (hapd->wps->network_key == NULL) + return -1; + } + hapd->wps->network_key_len = cred->key_len; + os_memcpy(hapd->wps->network_key, cred->key, cred->key_len); + } + hapd->wps->wps_state = WPS_STATE_CONFIGURED; + + len = os_strlen(hapd->iface->config_fname) + 5; + tmp_fname = os_malloc(len); + if (tmp_fname == NULL) + return -1; + os_snprintf(tmp_fname, len, "%s-new", hapd->iface->config_fname); + + oconf = fopen(hapd->iface->config_fname, "r"); + if (oconf == NULL) { + wpa_printf(MSG_WARNING, "WPS: Could not open current " + "configuration file"); + os_free(tmp_fname); + return -1; + } + + nconf = fopen(tmp_fname, "w"); + if (nconf == NULL) { + wpa_printf(MSG_WARNING, "WPS: Could not write updated " + "configuration file"); + os_free(tmp_fname); + fclose(oconf); + return -1; + } + + fprintf(nconf, "# WPS configuration - START\n"); + + fprintf(nconf, "wps_state=2\n"); + + fprintf(nconf, "ssid="); + for (i = 0; i < cred->ssid_len; i++) + fputc(cred->ssid[i], nconf); + fprintf(nconf, "\n"); + + if ((cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) && + (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK))) + wpa = 3; + else if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA2PSK)) + wpa = 2; + else if (cred->auth_type & (WPS_AUTH_WPA | WPS_AUTH_WPAPSK)) + wpa = 1; + else + wpa = 0; + + if (wpa) { + char *prefix; + fprintf(nconf, "wpa=%d\n", wpa); + + fprintf(nconf, "wpa_key_mgmt="); + prefix = ""; + if (cred->auth_type & (WPS_AUTH_WPA2 | WPS_AUTH_WPA)) { + fprintf(nconf, "WPA-EAP"); + prefix = " "; + } + if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) + fprintf(nconf, "%sWPA-PSK", prefix); + fprintf(nconf, "\n"); + + fprintf(nconf, "wpa_pairwise="); + prefix = ""; + if (cred->encr_type & WPS_ENCR_AES) { + fprintf(nconf, "CCMP"); + prefix = " "; + } + if (cred->encr_type & WPS_ENCR_TKIP) { + fprintf(nconf, "%sTKIP", prefix); + } + fprintf(nconf, "\n"); + + if (cred->key_len >= 8 && cred->key_len < 64) { + fprintf(nconf, "wpa_passphrase="); + for (i = 0; i < cred->key_len; i++) + fputc(cred->key[i], nconf); + fprintf(nconf, "\n"); + } else if (cred->key_len == 64) { + fprintf(nconf, "wpa_psk="); + for (i = 0; i < cred->key_len; i++) + fputc(cred->key[i], nconf); + fprintf(nconf, "\n"); + } else { + wpa_printf(MSG_WARNING, "WPS: Invalid key length %lu " + "for WPA/WPA2", + (unsigned long) cred->key_len); + } + + fprintf(nconf, "auth_algs=1\n"); + } else { + if ((cred->auth_type & WPS_AUTH_OPEN) && + (cred->auth_type & WPS_AUTH_SHARED)) + fprintf(nconf, "auth_algs=3\n"); + else if (cred->auth_type & WPS_AUTH_SHARED) + fprintf(nconf, "auth_algs=2\n"); + else + fprintf(nconf, "auth_algs=1\n"); + + if (cred->encr_type & WPS_ENCR_WEP && cred->key_idx <= 4) { + int key_idx = cred->key_idx; + if (key_idx) + key_idx--; + fprintf(nconf, "wep_default_key=%d\n", key_idx); + fprintf(nconf, "wep_key%d=", key_idx); + if (cred->key_len == 10 || cred->key_len == 26) { + /* WEP key as a hex string */ + for (i = 0; i < cred->key_len; i++) + fputc(cred->key[i], nconf); + } else { + /* Raw WEP key; convert to hex */ + for (i = 0; i < cred->key_len; i++) + fprintf(nconf, "%02x", cred->key[i]); + } + fprintf(nconf, "\n"); + } + } + + fprintf(nconf, "# WPS configuration - END\n"); + + multi_bss = 0; + while (fgets(buf, sizeof(buf), oconf)) { + if (os_strncmp(buf, "bss=", 4) == 0) + multi_bss = 1; + if (!multi_bss && + (str_starts(buf, "ssid=") || + str_starts(buf, "auth_algs=") || + str_starts(buf, "wps_state=") || + str_starts(buf, "wpa=") || + str_starts(buf, "wpa_psk=") || + str_starts(buf, "wpa_pairwise=") || + str_starts(buf, "rsn_pairwise=") || + str_starts(buf, "wpa_key_mgmt=") || + str_starts(buf, "wpa_passphrase="))) { + fprintf(nconf, "#WPS# %s", buf); + } else + fprintf(nconf, "%s", buf); + } + + fclose(nconf); + fclose(oconf); + + if (rename(tmp_fname, hapd->iface->config_fname) < 0) { + wpa_printf(MSG_WARNING, "WPS: Failed to rename the updated " + "configuration file: %s", strerror(errno)); + os_free(tmp_fname); + return -1; + } + + os_free(tmp_fname); + + /* Schedule configuration reload after short period of time to allow + * EAP-WSC to be finished. + */ + eloop_register_timeout(0, 100000, wps_reload_config, hapd->iface, + NULL); + + /* TODO: dualband AP may need to update multiple configuration files */ + + wpa_printf(MSG_DEBUG, "WPS: AP configuration updated"); + + return 0; +} + + +static void hostapd_wps_reenable_ap_pin(void *eloop_data, void *user_ctx) +{ + struct hostapd_data *hapd = eloop_data; + + if (hapd->conf->ap_setup_locked) + return; + + wpa_printf(MSG_DEBUG, "WPS: Re-enable AP PIN"); + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED); + hapd->wps->ap_setup_locked = 0; + wps_registrar_update_ie(hapd->wps->registrar); +} + + +static void hostapd_pwd_auth_fail(struct hostapd_data *hapd, + struct wps_event_pwd_auth_fail *data) +{ + if (!data->enrollee || hapd->conf->ap_pin == NULL) + return; + + /* + * Registrar failed to prove its knowledge of the AP PIN. Lock AP setup + * for some time if this happens multiple times to slow down brute + * force attacks. + */ + hapd->ap_pin_failures++; + wpa_printf(MSG_DEBUG, "WPS: AP PIN authentication failure number %u", + hapd->ap_pin_failures); + if (hapd->ap_pin_failures < 3) + return; + + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_LOCKED); + hapd->wps->ap_setup_locked = 1; + + wps_registrar_update_ie(hapd->wps->registrar); + + if (!hapd->conf->ap_setup_locked) { + if (hapd->ap_pin_lockout_time == 0) + hapd->ap_pin_lockout_time = 60; + else if (hapd->ap_pin_lockout_time < 365 * 24 * 60 * 60 && + (hapd->ap_pin_failures % 3) == 0) + hapd->ap_pin_lockout_time *= 2; + + wpa_printf(MSG_DEBUG, "WPS: Disable AP PIN for %u seconds", + hapd->ap_pin_lockout_time); + eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL); + eloop_register_timeout(hapd->ap_pin_lockout_time, 0, + hostapd_wps_reenable_ap_pin, hapd, + NULL); + } + + /* TODO: dualband AP may need to update other interfaces */ +} + + +static void hostapd_wps_event_cb(void *ctx, enum wps_event event, + union wps_event_data *data) +{ + struct hostapd_data *hapd = ctx; + + if (event == WPS_EV_PWD_AUTH_FAIL) + hostapd_pwd_auth_fail(hapd, &data->pwd_auth_fail); +} + + +static void hostapd_wps_clear_ies(struct hostapd_data *hapd) +{ + wpabuf_free(hapd->wps_beacon_ie); + hapd->wps_beacon_ie = NULL; + + wpabuf_free(hapd->wps_probe_resp_ie); + hapd->wps_probe_resp_ie = NULL; + + hapd->drv.set_ap_wps_ie(hapd); +} + + +int hostapd_init_wps(struct hostapd_data *hapd, + struct hostapd_bss_config *conf) +{ + struct wps_context *wps; + struct wps_registrar_config cfg; + + if (conf->wps_state == 0) { + hostapd_wps_clear_ies(hapd); + return 0; + } + + wps = os_zalloc(sizeof(*wps)); + if (wps == NULL) + return -1; + + wps->cred_cb = hostapd_wps_cred_cb; + wps->event_cb = hostapd_wps_event_cb; + wps->cb_ctx = hapd; + + os_memset(&cfg, 0, sizeof(cfg)); + wps->wps_state = hapd->conf->wps_state; + wps->ap_setup_locked = hapd->conf->ap_setup_locked; + if (is_nil_uuid(hapd->conf->uuid)) { + uuid_gen_mac_addr(hapd->own_addr, wps->uuid); + wpa_hexdump(MSG_DEBUG, "WPS: UUID based on MAC address", + wps->uuid, UUID_LEN); + } else + os_memcpy(wps->uuid, hapd->conf->uuid, UUID_LEN); + wps->ssid_len = hapd->conf->ssid.ssid_len; + os_memcpy(wps->ssid, hapd->conf->ssid.ssid, wps->ssid_len); + wps->ap = 1; + os_memcpy(wps->dev.mac_addr, hapd->own_addr, ETH_ALEN); + wps->dev.device_name = hapd->conf->device_name ? + os_strdup(hapd->conf->device_name) : NULL; + wps->dev.manufacturer = hapd->conf->manufacturer ? + os_strdup(hapd->conf->manufacturer) : NULL; + wps->dev.model_name = hapd->conf->model_name ? + os_strdup(hapd->conf->model_name) : NULL; + wps->dev.model_number = hapd->conf->model_number ? + os_strdup(hapd->conf->model_number) : NULL; + wps->dev.serial_number = hapd->conf->serial_number ? + os_strdup(hapd->conf->serial_number) : NULL; + wps->config_methods = + wps_config_methods_str2bin(hapd->conf->config_methods); + if (hapd->conf->device_type && + wps_dev_type_str2bin(hapd->conf->device_type, + wps->dev.pri_dev_type) < 0) { + wpa_printf(MSG_ERROR, "WPS: Invalid device_type"); + os_free(wps); + return -1; + } + wps->dev.os_version = WPA_GET_BE32(hapd->conf->os_version); + wps->dev.rf_bands = hapd->iconf->hw_mode == HOSTAPD_MODE_IEEE80211A ? + WPS_RF_50GHZ : WPS_RF_24GHZ; /* FIX: dualband AP */ + + if (conf->wpa & WPA_PROTO_RSN) { + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) + wps->auth_types |= WPS_AUTH_WPA2PSK; + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) + wps->auth_types |= WPS_AUTH_WPA2; + + if (conf->rsn_pairwise & WPA_CIPHER_CCMP) + wps->encr_types |= WPS_ENCR_AES; + if (conf->rsn_pairwise & WPA_CIPHER_TKIP) + wps->encr_types |= WPS_ENCR_TKIP; + } + + if (conf->wpa & WPA_PROTO_WPA) { + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) + wps->auth_types |= WPS_AUTH_WPAPSK; + if (conf->wpa_key_mgmt & WPA_KEY_MGMT_IEEE8021X) + wps->auth_types |= WPS_AUTH_WPA; + + if (conf->wpa_pairwise & WPA_CIPHER_CCMP) + wps->encr_types |= WPS_ENCR_AES; + if (conf->wpa_pairwise & WPA_CIPHER_TKIP) + wps->encr_types |= WPS_ENCR_TKIP; + } + + if (conf->ssid.security_policy == SECURITY_PLAINTEXT) { + wps->encr_types |= WPS_ENCR_NONE; + wps->auth_types |= WPS_AUTH_OPEN; + } else if (conf->ssid.security_policy == SECURITY_STATIC_WEP) { + wps->encr_types |= WPS_ENCR_WEP; + if (conf->auth_algs & WPA_AUTH_ALG_OPEN) + wps->auth_types |= WPS_AUTH_OPEN; + if (conf->auth_algs & WPA_AUTH_ALG_SHARED) + wps->auth_types |= WPS_AUTH_SHARED; + } else if (conf->ssid.security_policy == SECURITY_IEEE_802_1X) { + wps->auth_types |= WPS_AUTH_OPEN; + if (conf->default_wep_key_len) + wps->encr_types |= WPS_ENCR_WEP; + else + wps->encr_types |= WPS_ENCR_NONE; + } + + if (conf->ssid.wpa_psk_file) { + /* Use per-device PSKs */ + } else if (conf->ssid.wpa_passphrase) { + wps->network_key = (u8 *) os_strdup(conf->ssid.wpa_passphrase); + wps->network_key_len = os_strlen(conf->ssid.wpa_passphrase); + } else if (conf->ssid.wpa_psk) { + wps->network_key = os_malloc(2 * PMK_LEN + 1); + if (wps->network_key == NULL) { + os_free(wps); + return -1; + } + wpa_snprintf_hex((char *) wps->network_key, 2 * PMK_LEN + 1, + conf->ssid.wpa_psk->psk, PMK_LEN); + wps->network_key_len = 2 * PMK_LEN; + } else if (conf->ssid.wep.keys_set && conf->ssid.wep.key[0]) { + wps->network_key = os_malloc(conf->ssid.wep.len[0]); + if (wps->network_key == NULL) { + os_free(wps); + return -1; + } + os_memcpy(wps->network_key, conf->ssid.wep.key[0], + conf->ssid.wep.len[0]); + wps->network_key_len = conf->ssid.wep.len[0]; + } + + if (conf->ssid.wpa_psk) { + os_memcpy(wps->psk, conf->ssid.wpa_psk->psk, PMK_LEN); + wps->psk_set = 1; + } + + if (conf->wps_state == WPS_STATE_NOT_CONFIGURED) { + /* Override parameters to enable security by default */ + wps->auth_types = WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK; + wps->encr_types = WPS_ENCR_AES | WPS_ENCR_TKIP; + } + + wps->ap_settings = conf->ap_settings; + wps->ap_settings_len = conf->ap_settings_len; + + cfg.new_psk_cb = hostapd_wps_new_psk_cb; + cfg.set_ie_cb = hostapd_wps_set_ie_cb; + cfg.pin_needed_cb = hostapd_wps_pin_needed_cb; + cfg.reg_success_cb = hostapd_wps_reg_success_cb; + cfg.enrollee_seen_cb = hostapd_wps_enrollee_seen_cb; + cfg.cb_ctx = hapd; + cfg.skip_cred_build = conf->skip_cred_build; + cfg.extra_cred = conf->extra_cred; + cfg.extra_cred_len = conf->extra_cred_len; + cfg.disable_auto_conf = (hapd->conf->wps_cred_processing == 1) && + conf->skip_cred_build; + if (conf->ssid.security_policy == SECURITY_STATIC_WEP) + cfg.static_wep_only = 1; + + wps->registrar = wps_registrar_init(wps, &cfg); + if (wps->registrar == NULL) { + printf("Failed to initialize WPS Registrar\n"); + os_free(wps->network_key); + os_free(wps); + return -1; + } + +#ifdef CONFIG_WPS_UPNP + wps->friendly_name = hapd->conf->friendly_name; + wps->manufacturer_url = hapd->conf->manufacturer_url; + wps->model_description = hapd->conf->model_description; + wps->model_url = hapd->conf->model_url; + wps->upc = hapd->conf->upc; + + if (hostapd_wps_upnp_init(hapd, wps) < 0) { + wpa_printf(MSG_ERROR, "Failed to initialize WPS UPnP"); + wps_registrar_deinit(wps->registrar); + os_free(wps->network_key); + os_free(wps); + return -1; + } +#endif /* CONFIG_WPS_UPNP */ + + hostapd_register_probereq_cb(hapd, hostapd_wps_probe_req_rx, hapd); + + hapd->wps = wps; + + return 0; +} + + +void hostapd_deinit_wps(struct hostapd_data *hapd) +{ + eloop_cancel_timeout(hostapd_wps_reenable_ap_pin, hapd, NULL); + eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL); + if (hapd->wps == NULL) + return; +#ifdef CONFIG_WPS_UPNP + hostapd_wps_upnp_deinit(hapd); +#endif /* CONFIG_WPS_UPNP */ + wps_registrar_deinit(hapd->wps->registrar); + os_free(hapd->wps->network_key); + wps_device_data_free(&hapd->wps->dev); + wpabuf_free(hapd->wps->dh_pubkey); + wpabuf_free(hapd->wps->dh_privkey); + wpabuf_free(hapd->wps->oob_conf.pubkey_hash); + wpabuf_free(hapd->wps->oob_conf.dev_password); + wps_free_pending_msgs(hapd->wps->upnp_msgs); + os_free(hapd->wps); + hapd->wps = NULL; + hostapd_wps_clear_ies(hapd); +} + + +void hostapd_update_wps(struct hostapd_data *hapd) +{ + if (hapd->wps == NULL) + return; + if (hapd->conf->wps_state) + wps_registrar_update_ie(hapd->wps->registrar); + else + hostapd_deinit_wps(hapd); +} + + +int hostapd_wps_add_pin(struct hostapd_data *hapd, const char *uuid, + const char *pin, int timeout) +{ + u8 u[UUID_LEN]; + int any = 0; + + if (hapd->wps == NULL) + return -1; + if (os_strcmp(uuid, "any") == 0) + any = 1; + else if (uuid_str2bin(uuid, u)) + return -1; + return wps_registrar_add_pin(hapd->wps->registrar, any ? NULL : u, + (const u8 *) pin, os_strlen(pin), + timeout); +} + + +int hostapd_wps_button_pushed(struct hostapd_data *hapd) +{ + if (hapd->wps == NULL) + return -1; + return wps_registrar_button_pushed(hapd->wps->registrar); +} + + +#ifdef CONFIG_WPS_OOB +int hostapd_wps_start_oob(struct hostapd_data *hapd, char *device_type, + char *path, char *method, char *name) +{ + struct wps_context *wps = hapd->wps; + struct oob_device_data *oob_dev; + + oob_dev = wps_get_oob_device(device_type); + if (oob_dev == NULL) + return -1; + oob_dev->device_path = path; + oob_dev->device_name = name; + wps->oob_conf.oob_method = wps_get_oob_method(method); + + if (wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_R) { + /* + * Use pre-configured DH keys in order to be able to write the + * key hash into the OOB file. + */ + wpabuf_free(wps->dh_pubkey); + wpabuf_free(wps->dh_privkey); + wps->dh_privkey = NULL; + wps->dh_pubkey = dh_init(dh_groups_get(WPS_DH_GROUP), + &wps->dh_privkey); + wps->dh_pubkey = wpabuf_zeropad(wps->dh_pubkey, 192); + if (wps->dh_pubkey == NULL) { + wpa_printf(MSG_ERROR, "WPS: Failed to initialize " + "Diffie-Hellman handshake"); + return -1; + } + } + + if (wps_process_oob(wps, oob_dev, 1) < 0) + goto error; + + if ((wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E || + wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_R) && + hostapd_wps_add_pin(hapd, "any", + wpabuf_head(wps->oob_conf.dev_password), 0) < + 0) + goto error; + + return 0; + +error: + wpabuf_free(wps->dh_pubkey); + wps->dh_pubkey = NULL; + wpabuf_free(wps->dh_privkey); + wps->dh_privkey = NULL; + return -1; +} +#endif /* CONFIG_WPS_OOB */ + + +static int hostapd_wps_probe_req_rx(void *ctx, const u8 *addr, + const u8 *ie, size_t ie_len) +{ + struct hostapd_data *hapd = ctx; + struct wpabuf *wps_ie; + struct ieee802_11_elems elems; + + if (hapd->wps == NULL) + return 0; + + if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) { + wpa_printf(MSG_DEBUG, "WPS: Could not parse ProbeReq from " + MACSTR, MAC2STR(addr)); + return 0; + } + + if (elems.ssid && elems.ssid_len > 0 && + (elems.ssid_len != hapd->conf->ssid.ssid_len || + os_memcmp(elems.ssid, hapd->conf->ssid.ssid, elems.ssid_len) != + 0)) + return 0; /* Not for us */ + + wps_ie = ieee802_11_vendor_ie_concat(ie, ie_len, WPS_DEV_OUI_WFA); + if (wps_ie == NULL) + return 0; + + if (wpabuf_len(wps_ie) > 0) { + wps_registrar_probe_req_rx(hapd->wps->registrar, addr, wps_ie); +#ifdef CONFIG_WPS_UPNP + /* FIX: what exactly should be included in the WLANEvent? + * WPS attributes? Full ProbeReq frame? */ + upnp_wps_device_send_wlan_event(hapd->wps_upnp, addr, + UPNP_WPS_WLANEVENT_TYPE_PROBE, + wps_ie); +#endif /* CONFIG_WPS_UPNP */ + } + + wpabuf_free(wps_ie); + + return 0; +} + + +#ifdef CONFIG_WPS_UPNP + +static int hostapd_rx_req_put_wlan_response( + void *priv, enum upnp_wps_wlanevent_type ev_type, + const u8 *mac_addr, const struct wpabuf *msg, + enum wps_msg_type msg_type) +{ + struct hostapd_data *hapd = priv; + struct sta_info *sta; + struct upnp_pending_message *p; + + wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse ev_type=%d mac_addr=" + MACSTR, ev_type, MAC2STR(mac_addr)); + wpa_hexdump(MSG_MSGDUMP, "WPS UPnP: PutWLANResponse NewMessage", + wpabuf_head(msg), wpabuf_len(msg)); + if (ev_type != UPNP_WPS_WLANEVENT_TYPE_EAP) { + wpa_printf(MSG_DEBUG, "WPS UPnP: Ignored unexpected " + "PutWLANResponse WLANEventType %d", ev_type); + return -1; + } + + /* + * EAP response to ongoing to WPS Registration. Send it to EAP-WSC + * server implementation for delivery to the peer. + */ + + sta = ap_get_sta(hapd, mac_addr); + if (!sta) { + /* + * Workaround - Intel wsccmd uses bogus NewWLANEventMAC: + * Pick STA that is in an ongoing WPS registration without + * checking the MAC address. + */ + wpa_printf(MSG_DEBUG, "WPS UPnP: No matching STA found based " + "on NewWLANEventMAC; try wildcard match"); + for (sta = hapd->sta_list; sta; sta = sta->next) { + if (sta->eapol_sm && (sta->flags & WLAN_STA_WPS)) + break; + } + } + + if (!sta) { + wpa_printf(MSG_DEBUG, "WPS UPnP: No matching STA found"); + return 0; + } + + p = os_zalloc(sizeof(*p)); + if (p == NULL) + return -1; + os_memcpy(p->addr, sta->addr, ETH_ALEN); + p->msg = wpabuf_dup(msg); + p->type = msg_type; + p->next = hapd->wps->upnp_msgs; + hapd->wps->upnp_msgs = p; + + return eapol_auth_eap_pending_cb(sta->eapol_sm, sta->eapol_sm->eap); +} + + +static int hostapd_wps_upnp_init(struct hostapd_data *hapd, + struct wps_context *wps) +{ + struct upnp_wps_device_ctx *ctx; + + if (!hapd->conf->upnp_iface) + return 0; + ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return -1; + + ctx->rx_req_put_wlan_response = hostapd_rx_req_put_wlan_response; + if (hapd->conf->ap_pin) + ctx->ap_pin = os_strdup(hapd->conf->ap_pin); + + hapd->wps_upnp = upnp_wps_device_init(ctx, wps, hapd); + if (hapd->wps_upnp == NULL) { + os_free(ctx); + return -1; + } + wps->wps_upnp = hapd->wps_upnp; + + if (upnp_wps_device_start(hapd->wps_upnp, hapd->conf->upnp_iface)) { + upnp_wps_device_deinit(hapd->wps_upnp); + hapd->wps_upnp = NULL; + return -1; + } + + return 0; +} + + +static void hostapd_wps_upnp_deinit(struct hostapd_data *hapd) +{ + upnp_wps_device_deinit(hapd->wps_upnp); +} + +#endif /* CONFIG_WPS_UPNP */ + + +int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr, + char *buf, size_t buflen) +{ + if (hapd->wps == NULL) + return 0; + return wps_registrar_get_info(hapd->wps->registrar, addr, buf, buflen); +} + + +static void hostapd_wps_ap_pin_timeout(void *eloop_data, void *user_ctx) +{ + struct hostapd_data *hapd = eloop_data; + wpa_printf(MSG_DEBUG, "WPS: AP PIN timed out"); + hostapd_wps_ap_pin_disable(hapd); +} + + +static void hostapd_wps_ap_pin_enable(struct hostapd_data *hapd, int timeout) +{ + wpa_printf(MSG_DEBUG, "WPS: Enabling AP PIN (timeout=%d)", timeout); + hapd->ap_pin_failures = 0; + hapd->conf->ap_setup_locked = 0; + if (hapd->wps->ap_setup_locked) { + wpa_msg(hapd->msg_ctx, MSG_INFO, WPS_EVENT_AP_SETUP_UNLOCKED); + hapd->wps->ap_setup_locked = 0; + wps_registrar_update_ie(hapd->wps->registrar); + } + eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL); + if (timeout > 0) + eloop_register_timeout(timeout, 0, + hostapd_wps_ap_pin_timeout, hapd, NULL); +} + + +void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd) +{ + wpa_printf(MSG_DEBUG, "WPS: Disabling AP PIN"); + os_free(hapd->conf->ap_pin); + hapd->conf->ap_pin = NULL; +#ifdef CONFIG_WPS_UPNP + upnp_wps_set_ap_pin(hapd->wps_upnp, NULL); +#endif /* CONFIG_WPS_UPNP */ + eloop_cancel_timeout(hostapd_wps_ap_pin_timeout, hapd, NULL); +} + + +const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout) +{ + unsigned int pin; + char pin_txt[9]; + + pin = wps_generate_pin(); + os_snprintf(pin_txt, sizeof(pin_txt), "%u", pin); + os_free(hapd->conf->ap_pin); + hapd->conf->ap_pin = os_strdup(pin_txt); +#ifdef CONFIG_WPS_UPNP + upnp_wps_set_ap_pin(hapd->wps_upnp, pin_txt); +#endif /* CONFIG_WPS_UPNP */ + hostapd_wps_ap_pin_enable(hapd, timeout); + return hapd->conf->ap_pin; +} + + +const char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd) +{ + return hapd->conf->ap_pin; +} + + +int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin, + int timeout) +{ + os_free(hapd->conf->ap_pin); + hapd->conf->ap_pin = os_strdup(pin); + if (hapd->conf->ap_pin == NULL) + return -1; +#ifdef CONFIG_WPS_UPNP + upnp_wps_set_ap_pin(hapd->wps_upnp, hapd->conf->ap_pin); +#endif /* CONFIG_WPS_UPNP */ + hostapd_wps_ap_pin_enable(hapd, timeout); + return 0; +} diff --git a/contrib/wpa/src/ap/wps_hostapd.h b/contrib/wpa/src/ap/wps_hostapd.h new file mode 100644 index 0000000..e978a1c --- /dev/null +++ b/contrib/wpa/src/ap/wps_hostapd.h @@ -0,0 +1,67 @@ +/* + * hostapd / WPS integration + * Copyright (c) 2008-2010, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPS_HOSTAPD_H +#define WPS_HOSTAPD_H + +#ifdef CONFIG_WPS + +int hostapd_init_wps(struct hostapd_data *hapd, + struct hostapd_bss_config *conf); +void hostapd_deinit_wps(struct hostapd_data *hapd); +void hostapd_update_wps(struct hostapd_data *hapd); +int hostapd_wps_add_pin(struct hostapd_data *hapd, const char *uuid, + const char *pin, int timeout); +int hostapd_wps_button_pushed(struct hostapd_data *hapd); +int hostapd_wps_start_oob(struct hostapd_data *hapd, char *device_type, + char *path, char *method, char *name); +int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, const u8 *addr, + char *buf, size_t buflen); +void hostapd_wps_ap_pin_disable(struct hostapd_data *hapd); +const char * hostapd_wps_ap_pin_random(struct hostapd_data *hapd, int timeout); +const char * hostapd_wps_ap_pin_get(struct hostapd_data *hapd); +int hostapd_wps_ap_pin_set(struct hostapd_data *hapd, const char *pin, + int timeout); + +#else /* CONFIG_WPS */ + +static inline int hostapd_init_wps(struct hostapd_data *hapd, + struct hostapd_bss_config *conf) +{ + return 0; +} + +static inline void hostapd_deinit_wps(struct hostapd_data *hapd) +{ +} + +static inline void hostapd_update_wps(struct hostapd_data *hapd) +{ +} + +static inline int hostapd_wps_get_mib_sta(struct hostapd_data *hapd, + const u8 *addr, + char *buf, size_t buflen) +{ + return 0; +} + +static inline int hostapd_wps_button_pushed(struct hostapd_data *hapd) +{ + return 0; +} + +#endif /* CONFIG_WPS */ + +#endif /* WPS_HOSTAPD_H */ diff --git a/contrib/wpa/src/common/Makefile b/contrib/wpa/src/common/Makefile index cffba62..9c41962 100644 --- a/contrib/wpa/src/common/Makefile +++ b/contrib/wpa/src/common/Makefile @@ -2,7 +2,6 @@ all: @echo Nothing to be made. clean: - for d in $(SUBDIRS); do make -C $$d clean; done rm -f *~ *.o *.d install: diff --git a/contrib/wpa/src/common/defs.h b/contrib/wpa/src/common/defs.h index 4930e73..173bbd1 100644 --- a/contrib/wpa/src/common/defs.h +++ b/contrib/wpa/src/common/defs.h @@ -77,18 +77,44 @@ static inline int wpa_key_mgmt_sha256(int akm) #define WPA_AUTH_ALG_OPEN BIT(0) #define WPA_AUTH_ALG_SHARED BIT(1) #define WPA_AUTH_ALG_LEAP BIT(2) +#define WPA_AUTH_ALG_FT BIT(3) -typedef enum { WPA_ALG_NONE, WPA_ALG_WEP, WPA_ALG_TKIP, WPA_ALG_CCMP, - WPA_ALG_IGTK, WPA_ALG_PMK } wpa_alg; -typedef enum { CIPHER_NONE, CIPHER_WEP40, CIPHER_TKIP, CIPHER_CCMP, - CIPHER_WEP104 } wpa_cipher; -typedef enum { KEY_MGMT_802_1X, KEY_MGMT_PSK, KEY_MGMT_NONE, - KEY_MGMT_802_1X_NO_WPA, KEY_MGMT_WPA_NONE, - KEY_MGMT_FT_802_1X, KEY_MGMT_FT_PSK, - KEY_MGMT_802_1X_SHA256, KEY_MGMT_PSK_SHA256, - KEY_MGMT_WPS -} wpa_key_mgmt; +enum wpa_alg { + WPA_ALG_NONE, + WPA_ALG_WEP, + WPA_ALG_TKIP, + WPA_ALG_CCMP, + WPA_ALG_IGTK, + WPA_ALG_PMK +}; + +/** + * enum wpa_cipher - Cipher suites + */ +enum wpa_cipher { + CIPHER_NONE, + CIPHER_WEP40, + CIPHER_TKIP, + CIPHER_CCMP, + CIPHER_WEP104 +}; + +/** + * enum wpa_key_mgmt - Key management suites + */ +enum wpa_key_mgmt { + KEY_MGMT_802_1X, + KEY_MGMT_PSK, + KEY_MGMT_NONE, + KEY_MGMT_802_1X_NO_WPA, + KEY_MGMT_WPA_NONE, + KEY_MGMT_FT_802_1X, + KEY_MGMT_FT_PSK, + KEY_MGMT_802_1X_SHA256, + KEY_MGMT_PSK_SHA256, + KEY_MGMT_WPS +}; /** * enum wpa_states - wpa_supplicant state @@ -100,7 +126,7 @@ typedef enum { KEY_MGMT_802_1X, KEY_MGMT_PSK, KEY_MGMT_NONE, * wrapper functions wpa_sm_get_state() and wpa_sm_set_state() should be used * to access the state variable. */ -typedef enum { +enum wpa_states { /** * WPA_DISCONNECTED - Disconnected state * @@ -129,6 +155,16 @@ typedef enum { WPA_SCANNING, /** + * WPA_AUTHENTICATING - Trying to authenticate with a BSS/SSID + * + * This state is entered when wpa_supplicant has found a suitable BSS + * to authenticate with and the driver is configured to try to + * authenticate with this BSS. This state is used only with drivers + * that use wpa_supplicant as the SME. + */ + WPA_AUTHENTICATING, + + /** * WPA_ASSOCIATING - Trying to associate with a BSS/SSID * * This state is entered when wpa_supplicant has found a suitable BSS @@ -186,7 +222,7 @@ typedef enum { * fully configured. */ WPA_COMPLETED -} wpa_states; +}; #define MLME_SETPROTECTION_PROTECT_TYPE_NONE 0 #define MLME_SETPROTECTION_PROTECT_TYPE_RX 1 @@ -196,4 +232,24 @@ typedef enum { #define MLME_SETPROTECTION_KEY_TYPE_GROUP 0 #define MLME_SETPROTECTION_KEY_TYPE_PAIRWISE 1 + +/** + * enum mfp_options - Management frame protection (IEEE 802.11w) options + */ +enum mfp_options { + NO_MGMT_FRAME_PROTECTION = 0, + MGMT_FRAME_PROTECTION_OPTIONAL = 1, + MGMT_FRAME_PROTECTION_REQUIRED = 2 +}; + +/** + * enum hostapd_hw_mode - Hardware mode + */ +enum hostapd_hw_mode { + HOSTAPD_MODE_IEEE80211B, + HOSTAPD_MODE_IEEE80211G, + HOSTAPD_MODE_IEEE80211A, + NUM_HOSTAPD_MODES +}; + #endif /* DEFS_H */ diff --git a/contrib/wpa/src/common/ieee802_11_common.c b/contrib/wpa/src/common/ieee802_11_common.c index 242f933..96ef5b6 100644 --- a/contrib/wpa/src/common/ieee802_11_common.c +++ b/contrib/wpa/src/common/ieee802_11_common.c @@ -1,6 +1,6 @@ /* * IEEE 802.11 Common routines - * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -19,7 +19,7 @@ #include "ieee802_11_common.h" -static int ieee802_11_parse_vendor_specific(u8 *pos, size_t elen, +static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen, struct ieee802_11_elems *elems, int show_errors) { @@ -131,12 +131,12 @@ static int ieee802_11_parse_vendor_specific(u8 *pos, size_t elen, * @show_errors: Whether to show parsing errors in debug log * Returns: Parsing result */ -ParseRes ieee802_11_parse_elems(u8 *start, size_t len, +ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, struct ieee802_11_elems *elems, int show_errors) { size_t left = len; - u8 *pos = start; + const u8 *pos = start; int unknown = 0; os_memset(elems, 0, sizeof(*elems)); @@ -257,3 +257,70 @@ ParseRes ieee802_11_parse_elems(u8 *start, size_t len, return unknown ? ParseUnknown : ParseOK; } + + +int ieee802_11_ie_count(const u8 *ies, size_t ies_len) +{ + int count = 0; + const u8 *pos, *end; + + if (ies == NULL) + return 0; + + pos = ies; + end = ies + ies_len; + + while (pos + 2 <= end) { + if (pos + 2 + pos[1] > end) + break; + count++; + pos += 2 + pos[1]; + } + + return count; +} + + +struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len, + u32 oui_type) +{ + struct wpabuf *buf; + const u8 *end, *pos, *ie; + + pos = ies; + end = ies + ies_len; + ie = NULL; + + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + return NULL; + if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && + WPA_GET_BE32(&pos[2]) == oui_type) { + ie = pos; + break; + } + pos += 2 + pos[1]; + } + + if (ie == NULL) + return NULL; /* No specified vendor IE found */ + + buf = wpabuf_alloc(ies_len); + if (buf == NULL) + return NULL; + + /* + * There may be multiple vendor IEs in the message, so need to + * concatenate their data fields. + */ + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && + WPA_GET_BE32(&pos[2]) == oui_type) + wpabuf_put_data(buf, pos + 6, pos[1] - 4); + pos += 2 + pos[1]; + } + + return buf; +} diff --git a/contrib/wpa/src/common/ieee802_11_common.h b/contrib/wpa/src/common/ieee802_11_common.h index b7e497b..4a4f5a7 100644 --- a/contrib/wpa/src/common/ieee802_11_common.h +++ b/contrib/wpa/src/common/ieee802_11_common.h @@ -1,6 +1,6 @@ /* * IEEE 802.11 Common routines - * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -17,58 +17,62 @@ /* Parsed Information Elements */ struct ieee802_11_elems { - u8 *ssid; + const u8 *ssid; + const u8 *supp_rates; + const u8 *fh_params; + const u8 *ds_params; + const u8 *cf_params; + const u8 *tim; + const u8 *ibss_params; + const u8 *challenge; + const u8 *erp_info; + const u8 *ext_supp_rates; + const u8 *wpa_ie; + const u8 *rsn_ie; + const u8 *wmm; /* WMM Information or Parameter Element */ + const u8 *wmm_tspec; + const u8 *wps_ie; + const u8 *power_cap; + const u8 *supp_channels; + const u8 *mdie; + const u8 *ftie; + const u8 *timeout_int; + const u8 *ht_capabilities; + const u8 *ht_operation; + const u8 *vendor_ht_cap; + u8 ssid_len; - u8 *supp_rates; u8 supp_rates_len; - u8 *fh_params; u8 fh_params_len; - u8 *ds_params; u8 ds_params_len; - u8 *cf_params; u8 cf_params_len; - u8 *tim; u8 tim_len; - u8 *ibss_params; u8 ibss_params_len; - u8 *challenge; u8 challenge_len; - u8 *erp_info; u8 erp_info_len; - u8 *ext_supp_rates; u8 ext_supp_rates_len; - u8 *wpa_ie; u8 wpa_ie_len; - u8 *rsn_ie; u8 rsn_ie_len; - u8 *wmm; /* WMM Information or Parameter Element */ u8 wmm_len; /* 7 = WMM Information; 24 = WMM Parameter */ - u8 *wmm_tspec; u8 wmm_tspec_len; - u8 *wps_ie; u8 wps_ie_len; - u8 *power_cap; u8 power_cap_len; - u8 *supp_channels; u8 supp_channels_len; - u8 *mdie; u8 mdie_len; - u8 *ftie; u8 ftie_len; - u8 *timeout_int; u8 timeout_int_len; - u8 *ht_capabilities; u8 ht_capabilities_len; - u8 *ht_operation; u8 ht_operation_len; - u8 *vendor_ht_cap; u8 vendor_ht_cap_len; }; typedef enum { ParseOK = 0, ParseUnknown = 1, ParseFailed = -1 } ParseRes; -ParseRes ieee802_11_parse_elems(u8 *start, size_t len, +ParseRes ieee802_11_parse_elems(const u8 *start, size_t len, struct ieee802_11_elems *elems, int show_errors); +int ieee802_11_ie_count(const u8 *ies, size_t ies_len); +struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len, + u32 oui_type); #endif /* IEEE802_11_COMMON_H */ diff --git a/contrib/wpa/src/common/ieee802_11_defs.h b/contrib/wpa/src/common/ieee802_11_defs.h index d9e54a9..4881e39 100644 --- a/contrib/wpa/src/common/ieee802_11_defs.h +++ b/contrib/wpa/src/common/ieee802_11_defs.h @@ -1,6 +1,6 @@ /* * IEEE 802.11 Frame type definitions - * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> * Copyright (c) 2007-2008 Intel Corporation * * This program is free software; you can redistribute it and/or modify @@ -116,9 +116,13 @@ #define WLAN_STATUS_ASSOC_DENIED_NO_SHORT_SLOT_TIME 25 #define WLAN_STATUS_ASSOC_DENIED_NO_ER_PBCC 26 #define WLAN_STATUS_ASSOC_DENIED_NO_DSSS_OFDM 27 +#define WLAN_STATUS_R0KH_UNREACHABLE 28 /* IEEE 802.11w */ #define WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY 30 #define WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION 31 +#define WLAN_STATUS_UNSPECIFIED_QOS_FAILURE 32 +#define WLAN_STATUS_REQUEST_DECLINED 37 +#define WLAN_STATUS_INVALID_PARAMETERS 38 /* IEEE 802.11i */ #define WLAN_STATUS_INVALID_IE 40 #define WLAN_STATUS_GROUP_CIPHER_NOT_VALID 41 @@ -233,6 +237,25 @@ #pragma pack(push, 1) #endif /* _MSC_VER */ +struct ieee80211_hdr { + le16 frame_control; + le16 duration_id; + u8 addr1[6]; + u8 addr2[6]; + u8 addr3[6]; + le16 seq_ctrl; + /* followed by 'u8 addr4[6];' if ToDS and FromDS is set in data frame + */ +} STRUCT_PACKED; + +#define IEEE80211_DA_FROMDS addr1 +#define IEEE80211_BSSID_FROMDS addr2 +#define IEEE80211_SA_FROMDS addr3 + +#define IEEE80211_HDRLEN (sizeof(struct ieee80211_hdr)) + +#define IEEE80211_FC(type, stype) host_to_le16((type << 2) | (stype << 4)) + struct ieee80211_mgmt { le16 frame_control; le16 duration; @@ -337,45 +360,14 @@ struct ieee80211_mgmt { } u; } STRUCT_PACKED; -#ifdef _MSC_VER -#pragma pack(pop) -#endif /* _MSC_VER */ - -#define ERP_INFO_NON_ERP_PRESENT BIT(0) -#define ERP_INFO_USE_PROTECTION BIT(1) -#define ERP_INFO_BARKER_PREAMBLE_MODE BIT(2) - - -/* HT Capability element */ - -enum { - MAX_RX_AMPDU_FACTOR_8KB = 0, - MAX_RX_AMPDU_FACTOR_16KB, - MAX_RX_AMPDU_FACTOR_32KB, - MAX_RX_AMPDU_FACTOR_64KB -}; - -enum { - CALIBRATION_NOT_SUPPORTED = 0, - CALIBRATION_CANNOT_INIT, - CALIBRATION_CAN_INIT, - CALIBRATION_FULL_SUPPORT -}; -enum { - MCS_FEEDBACK_NOT_PROVIDED = 0, - MCS_FEEDBACK_UNSOLICITED, - MCS_FEEDBACK_MRQ_RESPONSE -}; - - -struct ieee80211_ht_capability { - le16 capabilities_info; - u8 mac_ht_params_info; +struct ieee80211_ht_capabilities { + le16 ht_capabilities_info; + u8 a_mpdu_params; u8 supported_mcs_set[16]; - le16 extended_ht_capability_info; - le32 tx_BF_capability_info; - u8 antenna_selection_info; + le16 ht_extended_capabilities; + le32 tx_bf_capability_info; + u8 asel_capabilities; } STRUCT_PACKED; @@ -387,47 +379,13 @@ struct ieee80211_ht_operation { u8 basic_set[16]; } STRUCT_PACKED; -/* auxiliary bit manipulation macros FIXME: move it to common later... */ -#define SET_2BIT_U8(_ptr_, _shift_, _val_) \ - ((*(_ptr_) &= ~(3 << (_shift_))), \ - (*(_ptr_) |= (*(_ptr_) & (((u8)3) << (_shift_))) | \ - (((u8)(_val_) & 3) << _shift_))) - -#define GET_2BIT_U8(_var_, _shift_) \ - (((_var_) & (((u8)3) << (_shift_))) >> (_shift_)) - -#define SET_2BIT_LE16(_u16ptr_, _shift_, _val_) \ - ((*(_u16ptr_) &= ~(3 << (_shift_))), \ - (*(_u16ptr_) |= \ - (((*(_u16ptr_)) & (((u16)3) << ((u16)_shift_))) | \ - (((u16)(_val_) & (u16)3) << (u16)(_shift_))))) - -#define GET_2BIT_LE16(_var_, _shift_) \ - (((_var_) & (((u16)3) << (_shift_))) >> (_shift_)) - -#define SET_2BIT_LE32(_u32ptr_, _shift_, _val_) \ - ((*(_u32ptr_) &= ~(3 << (_shift_))), \ - (*(_u32ptr_) |= (((*(_u32ptr_)) & (((u32)3) << (_shift_))) | \ - (((u32)(_val_) & 3) << _shift_)))) - -#define GET_2BIT_LE32(_var_, _shift_) \ - (((_var_) & (((u32)3) << (_shift_))) >> (_shift_)) - -#define SET_3BIT_LE16(_u16ptr_, _shift_, _val_) \ - ((*(_u16ptr_) &= ~(7 << (_shift_))), \ - (*(_u16ptr_) |= (((*(_u16ptr_)) & (((u16)7) << (_shift_))) | \ - (((u16)(_val_) & 7) << _shift_)))) - -#define GET_3BIT_LE16(_var_, _shift_) \ - (((_var_) & (((u16)7) << (_shift_))) >> (_shift_)) - -#define SET_3BIT_LE32(_u32ptr_, _shift_, _val_) \ - ((*(_u32ptr_) &= ~(7 << (_shift_))), \ - (*(_u32ptr_) |= (((*(_u32ptr_)) & (((u32)7) << (_shift_))) | \ - (((u32)(_val_) & 7) << _shift_)))) +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ -#define GET_3BIT_LE32(_var_, _shift_) \ - (((_var_) & (((u32)7) << (_shift_))) >> (_shift_)) +#define ERP_INFO_NON_ERP_PRESENT BIT(0) +#define ERP_INFO_USE_PROTECTION BIT(1) +#define ERP_INFO_BARKER_PREAMBLE_MODE BIT(2) #define HT_CAP_INFO_LDPC_CODING_CAP ((u16) BIT(0)) @@ -452,9 +410,6 @@ struct ieee80211_ht_operation { #define HT_CAP_INFO_LSIG_TXOP_PROTECT_SUPPORT ((u16) BIT(15)) -#define MAC_HT_PARAM_INFO_MAX_RX_AMPDU_FACTOR_OFFSET 0 -#define MAC_HT_PARAM_INFO_MAX_MPDU_DENSITY_OFFSET 2 - #define EXT_HT_CAP_INFO_PCO ((u16) BIT(0)) #define EXT_HT_CAP_INFO_TRANS_TIME_OFFSET 1 #define EXT_HT_CAP_INFO_MCS_FEEDBACK_OFFSET 8 @@ -490,22 +445,6 @@ struct ieee80211_ht_operation { #define ASEL_CAPABILITY_RX_AS_CAP ((u8) BIT(5)) #define ASEL_CAPABILITY_TX_SOUND_PPDUS_CAP ((u8) BIT(6)) - -struct ht_cap_ie { - u8 id; - u8 length; - struct ieee80211_ht_capability data; -} STRUCT_PACKED; - - -#define REC_TRANS_CHNL_WIDTH_20 0 -#define REC_TRANS_CHNL_WIDTH_ANY 1 - -#define OP_MODE_PURE 0 -#define OP_MODE_MAY_BE_LEGACY_STAS 1 -#define OP_MODE_20MHZ_HT_STA_ASSOCED 2 -#define OP_MODE_MIXED 3 - #define HT_INFO_HT_PARAM_SECONDARY_CHNL_OFF_MASK ((u8) BIT(0) | BIT(1)) #define HT_INFO_HT_PARAM_SECONDARY_CHNL_ABOVE ((u8) BIT(0)) #define HT_INFO_HT_PARAM_SECONDARY_CHNL_BELOW ((u8) BIT(0) | BIT(1)) @@ -514,6 +453,12 @@ struct ht_cap_ie { #define HT_INFO_HT_PARAM_CTRL_ACCESS_ONLY ((u8) BIT(4)) #define HT_INFO_HT_PARAM_SRV_INTERVAL_GRANULARITY ((u8) BIT(5)) + +#define OP_MODE_PURE 0 +#define OP_MODE_MAY_BE_LEGACY_STAS 1 +#define OP_MODE_20MHZ_HT_STA_ASSOCED 2 +#define OP_MODE_MIXED 3 + #define HT_INFO_OPERATION_MODE_OP_MODE_MASK \ ((le16) (0x0001 | 0x0002)) #define HT_INFO_OPERATION_MODE_OP_MODE_OFFSET 0 @@ -529,39 +474,10 @@ struct ht_cap_ie { #define HT_INFO_STBC_PARAM_PCO_PHASE ((u16) BIT(11)) -/* Secondary channel offset element */ -#define SECONDARY_CHANNEL_OFFSET_NONE 0 -#define SECONDARY_CHANNEL_OFFSET_ABOVE 1 -#define SECONDARY_CHANNEL_OFFSET_BELOW 3 -struct secondary_channel_offset_ie { - u8 id; - u8 length; - u8 secondary_offset_offset; -} STRUCT_PACKED; - - -/* body of Recommended Transmit Channel Width action frame */ -#define CHANNEL_WIDTH_20 0 -#define CHANNEL_WIDTH_ANY 1 -struct recommended_tx_channel_width_action { - u8 category; - u8 action; - u8 channel_width; -} STRUCT_PACKED; - -/* body of MIMO Power Save action frame */ -#define PWR_SAVE_MODE_STATIC 0 -#define PWR_SAVE_MODE_DYNAMIC 1 -struct mimo_pwr_save_action { - u8 category; - u8 action; - u8 enable; - u8 mode; -} STRUCT_PACKED; - - #define OUI_MICROSOFT 0x0050f2 /* Microsoft (also used in Wi-Fi specs) * 00:50:F2 */ +#define WPA_IE_VENDOR_TYPE 0x0050f201 +#define WPS_IE_VENDOR_TYPE 0x0050f204 #define WMM_OUI_TYPE 2 #define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0 @@ -585,9 +501,107 @@ struct mimo_pwr_save_action { /* 2 - Reserved */ #define WMM_TSPEC_DIRECTION_BI_DIRECTIONAL 3 +/* + * WMM Information Element (used in (Re)Association Request frames; may also be + * used in Beacon frames) + */ +struct wmm_information_element { + /* Element ID: 221 (0xdd); Length: 7 */ + /* required fields for WMM version 1 */ + u8 oui[3]; /* 00:50:f2 */ + u8 oui_type; /* 2 */ + u8 oui_subtype; /* 0 */ + u8 version; /* 1 for WMM version 1.0 */ + u8 qos_info; /* AP/STA specific QoS info */ + +} STRUCT_PACKED; + +#define WMM_AC_AIFSN_MASK 0x0f +#define WMM_AC_AIFNS_SHIFT 0 +#define WMM_AC_ACM 0x10 +#define WMM_AC_ACI_MASK 0x60 +#define WMM_AC_ACI_SHIFT 5 + +#define WMM_AC_ECWMIN_MASK 0x0f +#define WMM_AC_ECWMIN_SHIFT 0 +#define WMM_AC_ECWMAX_MASK 0xf0 +#define WMM_AC_ECWMAX_SHIFT 4 + +struct wmm_ac_parameter { + u8 aci_aifsn; /* AIFSN, ACM, ACI */ + u8 cw; /* ECWmin, ECWmax (CW = 2^ECW - 1) */ + le16 txop_limit; +} STRUCT_PACKED; + +/* + * WMM Parameter Element (used in Beacon, Probe Response, and (Re)Association + * Response frmaes) + */ +struct wmm_parameter_element { + /* Element ID: 221 (0xdd); Length: 24 */ + /* required fields for WMM version 1 */ + u8 oui[3]; /* 00:50:f2 */ + u8 oui_type; /* 2 */ + u8 oui_subtype; /* 1 */ + u8 version; /* 1 for WMM version 1.0 */ + u8 qos_info; /* AP/STA specif QoS info */ + u8 reserved; /* 0 */ + struct wmm_ac_parameter ac[4]; /* AC_BE, AC_BK, AC_VI, AC_VO */ + +} STRUCT_PACKED; + +/* WMM TSPEC Element */ +struct wmm_tspec_element { + u8 eid; /* 221 = 0xdd */ + u8 length; /* 6 + 55 = 61 */ + u8 oui[3]; /* 00:50:f2 */ + u8 oui_type; /* 2 */ + u8 oui_subtype; /* 2 */ + u8 version; /* 1 */ + /* WMM TSPEC body (55 octets): */ + u8 ts_info[3]; + le16 nominal_msdu_size; + le16 maximum_msdu_size; + le32 minimum_service_interval; + le32 maximum_service_interval; + le32 inactivity_interval; + le32 suspension_interval; + le32 service_start_time; + le32 minimum_data_rate; + le32 mean_data_rate; + le32 peak_data_rate; + le32 maximum_burst_size; + le32 delay_bound; + le32 minimum_phy_rate; + le16 surplus_bandwidth_allowance; + le16 medium_time; +} STRUCT_PACKED; + + +/* Access Categories / ACI to AC coding */ +enum { + WMM_AC_BE = 0 /* Best Effort */, + WMM_AC_BK = 1 /* Background */, + WMM_AC_VI = 2 /* Video */, + WMM_AC_VO = 3 /* Voice */ +}; + #define OUI_BROADCOM 0x00904c /* Broadcom (Epigram) */ #define VENDOR_HT_CAPAB_OUI_TYPE 0x33 /* 00-90-4c:0x33 */ +/* cipher suite selectors */ +#define WLAN_CIPHER_SUITE_USE_GROUP 0x000FAC00 +#define WLAN_CIPHER_SUITE_WEP40 0x000FAC01 +#define WLAN_CIPHER_SUITE_TKIP 0x000FAC02 +/* reserved: 0x000FAC03 */ +#define WLAN_CIPHER_SUITE_CCMP 0x000FAC04 +#define WLAN_CIPHER_SUITE_WEP104 0x000FAC05 +#define WLAN_CIPHER_SUITE_AES_CMAC 0x000FAC06 + +/* AKM suite selectors */ +#define WLAN_AKM_SUITE_8021X 0x000FAC01 +#define WLAN_AKM_SUITE_PSK 0x000FAC02 + #endif /* IEEE802_11_DEFS_H */ diff --git a/contrib/wpa/src/common/privsep_commands.h b/contrib/wpa/src/common/privsep_commands.h index 81b7f54..cc900be 100644 --- a/contrib/wpa/src/common/privsep_commands.h +++ b/contrib/wpa/src/common/privsep_commands.h @@ -18,7 +18,6 @@ enum privsep_cmd { PRIVSEP_CMD_REGISTER, PRIVSEP_CMD_UNREGISTER, - PRIVSEP_CMD_SET_WPA, PRIVSEP_CMD_SCAN, PRIVSEP_CMD_GET_SCAN_RESULTS, PRIVSEP_CMD_ASSOCIATE, @@ -30,7 +29,6 @@ enum privsep_cmd { PRIVSEP_CMD_L2_UNREGISTER, PRIVSEP_CMD_L2_NOTIFY_AUTH_START, PRIVSEP_CMD_L2_SEND, - PRIVSEP_CMD_SET_MODE, PRIVSEP_CMD_SET_COUNTRY, }; @@ -72,7 +70,6 @@ enum privsep_event { PRIVSEP_EVENT_STKSTART, PRIVSEP_EVENT_FT_RESPONSE, PRIVSEP_EVENT_RX_EAPOL, - PRIVSEP_EVENT_STA_RX, }; #endif /* PRIVSEP_COMMANDS_H */ diff --git a/contrib/wpa/src/common/version.h b/contrib/wpa/src/common/version.h index b79c494..02f34be 100644 --- a/contrib/wpa/src/common/version.h +++ b/contrib/wpa/src/common/version.h @@ -1,6 +1,6 @@ #ifndef VERSION_H #define VERSION_H -#define VERSION_STR "0.6.10" +#define VERSION_STR "0.7.3" #endif /* VERSION_H */ diff --git a/contrib/wpa/src/common/wpa_common.c b/contrib/wpa/src/common/wpa_common.c index 074cb80..b295f31 100644 --- a/contrib/wpa/src/common/wpa_common.c +++ b/contrib/wpa/src/common/wpa_common.c @@ -15,11 +15,11 @@ #include "includes.h" #include "common.h" -#include "md5.h" -#include "sha1.h" -#include "sha256.h" -#include "aes_wrap.h" -#include "crypto.h" +#include "crypto/md5.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" +#include "crypto/aes_wrap.h" +#include "crypto/crypto.h" #include "ieee802_11_defs.h" #include "defs.h" #include "wpa_common.h" @@ -50,10 +50,10 @@ int wpa_eapol_key_mic(const u8 *key, int ver, const u8 *buf, size_t len, switch (ver) { case WPA_KEY_INFO_TYPE_HMAC_MD5_RC4: - hmac_md5(key, 16, buf, len, mic); - break; + return hmac_md5(key, 16, buf, len, mic); case WPA_KEY_INFO_TYPE_HMAC_SHA1_AES: - hmac_sha1(key, 16, buf, len, hash); + if (hmac_sha1(key, 16, buf, len, hash)) + return -1; os_memcpy(mic, hash, MD5_MAC_LEN); break; #if defined(CONFIG_IEEE80211R) || defined(CONFIG_IEEE80211W) @@ -568,3 +568,220 @@ void wpa_pmk_r1_to_ptk(const u8 *pmk_r1, const u8 *snonce, const u8 *anonce, } #endif /* CONFIG_IEEE80211R */ + + +/** + * rsn_pmkid - Calculate PMK identifier + * @pmk: Pairwise master key + * @pmk_len: Length of pmk in bytes + * @aa: Authenticator address + * @spa: Supplicant address + * @pmkid: Buffer for PMKID + * @use_sha256: Whether to use SHA256-based KDF + * + * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy + * PMKID = HMAC-SHA1-128(PMK, "PMK Name" || AA || SPA) + */ +void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, + u8 *pmkid, int use_sha256) +{ + char *title = "PMK Name"; + const u8 *addr[3]; + const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN }; + unsigned char hash[SHA256_MAC_LEN]; + + addr[0] = (u8 *) title; + addr[1] = aa; + addr[2] = spa; + +#ifdef CONFIG_IEEE80211W + if (use_sha256) + hmac_sha256_vector(pmk, pmk_len, 3, addr, len, hash); + else +#endif /* CONFIG_IEEE80211W */ + hmac_sha1_vector(pmk, pmk_len, 3, addr, len, hash); + os_memcpy(pmkid, hash, PMKID_LEN); +} + + +/** + * wpa_cipher_txt - Convert cipher suite to a text string + * @cipher: Cipher suite (WPA_CIPHER_* enum) + * Returns: Pointer to a text string of the cipher suite name + */ +const char * wpa_cipher_txt(int cipher) +{ + switch (cipher) { + case WPA_CIPHER_NONE: + return "NONE"; + case WPA_CIPHER_WEP40: + return "WEP-40"; + case WPA_CIPHER_WEP104: + return "WEP-104"; + case WPA_CIPHER_TKIP: + return "TKIP"; + case WPA_CIPHER_CCMP: + return "CCMP"; + case WPA_CIPHER_CCMP | WPA_CIPHER_TKIP: + return "CCMP+TKIP"; + default: + return "UNKNOWN"; + } +} + + +/** + * wpa_key_mgmt_txt - Convert key management suite to a text string + * @key_mgmt: Key management suite (WPA_KEY_MGMT_* enum) + * @proto: WPA/WPA2 version (WPA_PROTO_*) + * Returns: Pointer to a text string of the key management suite name + */ +const char * wpa_key_mgmt_txt(int key_mgmt, int proto) +{ + switch (key_mgmt) { + case WPA_KEY_MGMT_IEEE8021X: + if (proto == (WPA_PROTO_RSN | WPA_PROTO_WPA)) + return "WPA2+WPA/IEEE 802.1X/EAP"; + return proto == WPA_PROTO_RSN ? + "WPA2/IEEE 802.1X/EAP" : "WPA/IEEE 802.1X/EAP"; + case WPA_KEY_MGMT_PSK: + if (proto == (WPA_PROTO_RSN | WPA_PROTO_WPA)) + return "WPA2-PSK+WPA-PSK"; + return proto == WPA_PROTO_RSN ? + "WPA2-PSK" : "WPA-PSK"; + case WPA_KEY_MGMT_NONE: + return "NONE"; + case WPA_KEY_MGMT_IEEE8021X_NO_WPA: + return "IEEE 802.1X (no WPA)"; +#ifdef CONFIG_IEEE80211R + case WPA_KEY_MGMT_FT_IEEE8021X: + return "FT-EAP"; + case WPA_KEY_MGMT_FT_PSK: + return "FT-PSK"; +#endif /* CONFIG_IEEE80211R */ +#ifdef CONFIG_IEEE80211W + case WPA_KEY_MGMT_IEEE8021X_SHA256: + return "WPA2-EAP-SHA256"; + case WPA_KEY_MGMT_PSK_SHA256: + return "WPA2-PSK-SHA256"; +#endif /* CONFIG_IEEE80211W */ + default: + return "UNKNOWN"; + } +} + + +int wpa_compare_rsn_ie(int ft_initial_assoc, + const u8 *ie1, size_t ie1len, + const u8 *ie2, size_t ie2len) +{ + if (ie1 == NULL || ie2 == NULL) + return -1; + + if (ie1len == ie2len && os_memcmp(ie1, ie2, ie1len) == 0) + return 0; /* identical IEs */ + +#ifdef CONFIG_IEEE80211R + if (ft_initial_assoc) { + struct wpa_ie_data ie1d, ie2d; + /* + * The PMKID-List in RSN IE is different between Beacon/Probe + * Response/(Re)Association Request frames and EAPOL-Key + * messages in FT initial mobility domain association. Allow + * for this, but verify that other parts of the RSN IEs are + * identical. + */ + if (wpa_parse_wpa_ie_rsn(ie1, ie1len, &ie1d) < 0 || + wpa_parse_wpa_ie_rsn(ie2, ie2len, &ie2d) < 0) + return -1; + if (ie1d.proto == ie2d.proto && + ie1d.pairwise_cipher == ie2d.pairwise_cipher && + ie1d.group_cipher == ie2d.group_cipher && + ie1d.key_mgmt == ie2d.key_mgmt && + ie1d.capabilities == ie2d.capabilities && + ie1d.mgmt_group_cipher == ie2d.mgmt_group_cipher) + return 0; + } +#endif /* CONFIG_IEEE80211R */ + + return -1; +} + + +#ifdef CONFIG_IEEE80211R +int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid) +{ + u8 *start, *end, *rpos, *rend; + int added = 0; + + start = ies; + end = ies + ies_len; + + while (start < end) { + if (*start == WLAN_EID_RSN) + break; + start += 2 + start[1]; + } + if (start >= end) { + wpa_printf(MSG_ERROR, "FT: Could not find RSN IE in " + "IEs data"); + return -1; + } + wpa_hexdump(MSG_DEBUG, "FT: RSN IE before modification", + start, 2 + start[1]); + + /* Find start of PMKID-Count */ + rpos = start + 2; + rend = rpos + start[1]; + + /* Skip Version and Group Data Cipher Suite */ + rpos += 2 + 4; + /* Skip Pairwise Cipher Suite Count and List */ + rpos += 2 + WPA_GET_LE16(rpos) * RSN_SELECTOR_LEN; + /* Skip AKM Suite Count and List */ + rpos += 2 + WPA_GET_LE16(rpos) * RSN_SELECTOR_LEN; + + if (rpos == rend) { + /* Add RSN Capabilities */ + os_memmove(rpos + 2, rpos, end - rpos); + *rpos++ = 0; + *rpos++ = 0; + } else { + /* Skip RSN Capabilities */ + rpos += 2; + if (rpos > rend) { + wpa_printf(MSG_ERROR, "FT: Could not parse RSN IE in " + "IEs data"); + return -1; + } + } + + if (rpos == rend) { + /* No PMKID-Count field included; add it */ + os_memmove(rpos + 2 + PMKID_LEN, rpos, end - rpos); + WPA_PUT_LE16(rpos, 1); + rpos += 2; + os_memcpy(rpos, pmkid, PMKID_LEN); + added += 2 + PMKID_LEN; + start[1] += 2 + PMKID_LEN; + } else { + /* PMKID-Count was included; use it */ + if (WPA_GET_LE16(rpos) != 0) { + wpa_printf(MSG_ERROR, "FT: Unexpected PMKID " + "in RSN IE in EAPOL-Key data"); + return -1; + } + WPA_PUT_LE16(rpos, 1); + rpos += 2; + os_memmove(rpos + PMKID_LEN, rpos, end - rpos); + os_memcpy(rpos, pmkid, PMKID_LEN); + added += PMKID_LEN; + start[1] += PMKID_LEN; + } + + wpa_hexdump(MSG_DEBUG, "FT: RSN IE after modification " + "(PMKID inserted)", start, 2 + start[1]); + + return added; +} +#endif /* CONFIG_IEEE80211R */ diff --git a/contrib/wpa/src/common/wpa_common.h b/contrib/wpa/src/common/wpa_common.h index 3074cd4..fd8a79f 100644 --- a/contrib/wpa/src/common/wpa_common.h +++ b/contrib/wpa/src/common/wpa_common.h @@ -282,6 +282,12 @@ struct rsn_ftie { #define FTIE_SUBELEM_R0KH_ID 3 #define FTIE_SUBELEM_IGTK 4 +struct rsn_rdie { + u8 id; + u8 descr_count; + le16 status_code; +} STRUCT_PACKED; + #endif /* CONFIG_IEEE80211R */ #ifdef _MSC_VER @@ -332,4 +338,14 @@ struct wpa_ie_data { int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len, struct wpa_ie_data *data); +void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, + u8 *pmkid, int use_sha256); + +const char * wpa_cipher_txt(int cipher); +const char * wpa_key_mgmt_txt(int key_mgmt, int proto); +int wpa_compare_rsn_ie(int ft_initial_assoc, + const u8 *ie1, size_t ie1len, + const u8 *ie2, size_t ie2len); +int wpa_insert_pmkid(u8 *ies, size_t ies_len, const u8 *pmkid); + #endif /* WPA_COMMON_H */ diff --git a/contrib/wpa/src/common/wpa_ctrl.h b/contrib/wpa/src/common/wpa_ctrl.h index 1bfc0d6..d770fd4 100644 --- a/contrib/wpa/src/common/wpa_ctrl.h +++ b/contrib/wpa/src/common/wpa_ctrl.h @@ -40,14 +40,24 @@ extern "C" { #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 proposed by the server */ +#define WPA_EVENT_EAP_PROPOSED_METHOD "CTRL-EVENT-EAP-PROPOSED-METHOD " /** EAP method selected */ #define WPA_EVENT_EAP_METHOD "CTRL-EVENT-EAP-METHOD " +/** EAP peer certificate from TLS */ +#define WPA_EVENT_EAP_PEER_CERT "CTRL-EVENT-EAP-PEER-CERT " +/** EAP TLS certificate chain validation error */ +#define WPA_EVENT_EAP_TLS_CERT_ERROR "CTRL-EVENT-EAP-TLS-CERT-ERROR " /** 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 " /** New scan results available */ #define WPA_EVENT_SCAN_RESULTS "CTRL-EVENT-SCAN-RESULTS " +/** A new BSS entry was added (followed by BSS entry id and BSSID) */ +#define WPA_EVENT_BSS_ADDED "CTRL-EVENT-BSS-ADDED " +/** A BSS entry was removed (followed by BSS entry id and BSSID) */ +#define WPA_EVENT_BSS_REMOVED "CTRL-EVENT-BSS-REMOVED " /** WPS overlap detected in PBC mode */ #define WPS_EVENT_OVERLAP "WPS-OVERLAP-DETECTED " @@ -69,11 +79,24 @@ extern "C" { /** WPS enrollment attempt timed out and was terminated */ #define WPS_EVENT_TIMEOUT "WPS-TIMEOUT " +#define WPS_EVENT_ENROLLEE_SEEN "WPS-ENROLLEE-SEEN " + +/* WPS ER events */ +#define WPS_EVENT_ER_AP_ADD "WPS-ER-AP-ADD " +#define WPS_EVENT_ER_AP_REMOVE "WPS-ER-AP-REMOVE " +#define WPS_EVENT_ER_ENROLLEE_ADD "WPS-ER-ENROLLEE-ADD " +#define WPS_EVENT_ER_ENROLLEE_REMOVE "WPS-ER-ENROLLEE-REMOVE " + /* hostapd control interface - fixed message prefixes */ #define WPS_EVENT_PIN_NEEDED "WPS-PIN-NEEDED " #define WPS_EVENT_NEW_AP_SETTINGS "WPS-NEW-AP-SETTINGS " #define WPS_EVENT_REG_SUCCESS "WPS-REG-SUCCESS " #define WPS_EVENT_AP_SETUP_LOCKED "WPS-AP-SETUP-LOCKED " +#define WPS_EVENT_AP_SETUP_UNLOCKED "WPS-AP-SETUP-UNLOCKED " +#define WPS_EVENT_AP_PIN_ENABLED "WPS-AP-PIN-ENABLED " +#define WPS_EVENT_AP_PIN_DISABLED "WPS-AP-PIN-DISABLED " +#define AP_STA_CONNECTED "AP-STA-CONNECTED " +#define AP_STA_DISCONNECTED "AP-STA-DISCONNECTED " /* wpa_supplicant/hostapd control interface access */ diff --git a/contrib/wpa/src/crypto/.gitignore b/contrib/wpa/src/crypto/.gitignore new file mode 100644 index 0000000..ee60604 --- /dev/null +++ b/contrib/wpa/src/crypto/.gitignore @@ -0,0 +1 @@ +libcrypto.a diff --git a/contrib/wpa/src/crypto/Makefile b/contrib/wpa/src/crypto/Makefile index cffba62..69aa16a 100644 --- a/contrib/wpa/src/crypto/Makefile +++ b/contrib/wpa/src/crypto/Makefile @@ -1,9 +1,56 @@ -all: - @echo Nothing to be made. +all: libcrypto.a clean: - for d in $(SUBDIRS); do make -C $$d clean; done - rm -f *~ *.o *.d + rm -f *~ *.o *.d libcrypto.a install: @echo Nothing to be made. + + +include ../lib.rules + +CFLAGS += -DCONFIG_TLS_INTERNAL_CLIENT +CFLAGS += -DCONFIG_TLS_INTERNAL_SERVER +#CFLAGS += -DALL_DH_GROUPS + +LIB_OBJS= \ + aes-cbc.o \ + aes-ctr.o \ + aes-eax.o \ + aes-encblock.o \ + aes-internal.o \ + aes-internal-dec.o \ + aes-internal-enc.o \ + aes-omac1.o \ + aes-unwrap.o \ + aes-wrap.o \ + des-internal.o \ + dh_group5.o \ + dh_groups.o \ + md4-internal.o \ + md5.o \ + md5-internal.o \ + md5-non-fips.o \ + milenage.o \ + ms_funcs.o \ + rc4.o \ + sha1.o \ + sha1-internal.o \ + sha1-pbkdf2.o \ + sha1-tlsprf.o \ + sha1-tprf.o \ + sha256.o \ + sha256-internal.o + +LIB_OBJS += crypto_internal.o +LIB_OBJS += crypto_internal-cipher.o +LIB_OBJS += crypto_internal-modexp.o +LIB_OBJS += crypto_internal-rsa.o +LIB_OBJS += tls_internal.o +LIB_OBJS += fips_prf_internal.o + + +libcrypto.a: $(LIB_OBJS) + $(AR) crT $@ $? + +-include $(OBJS:%.o=%.d) diff --git a/contrib/wpa/src/crypto/aes-cbc.c b/contrib/wpa/src/crypto/aes-cbc.c new file mode 100644 index 0000000..bd74769 --- /dev/null +++ b/contrib/wpa/src/crypto/aes-cbc.c @@ -0,0 +1,86 @@ +/* + * AES-128 CBC + * + * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "aes.h" +#include "aes_wrap.h" + +/** + * 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) +{ + void *ctx; + u8 cbc[AES_BLOCK_SIZE]; + u8 *pos = data; + int i, j, blocks; + + ctx = aes_encrypt_init(key, 16); + if (ctx == NULL) + return -1; + os_memcpy(cbc, iv, AES_BLOCK_SIZE); + + blocks = data_len / AES_BLOCK_SIZE; + for (i = 0; i < blocks; i++) { + for (j = 0; j < AES_BLOCK_SIZE; j++) + cbc[j] ^= pos[j]; + aes_encrypt(ctx, cbc, cbc); + os_memcpy(pos, cbc, AES_BLOCK_SIZE); + pos += AES_BLOCK_SIZE; + } + aes_encrypt_deinit(ctx); + return 0; +} + + +/** + * 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) +{ + void *ctx; + u8 cbc[AES_BLOCK_SIZE], tmp[AES_BLOCK_SIZE]; + u8 *pos = data; + int i, j, blocks; + + ctx = aes_decrypt_init(key, 16); + if (ctx == NULL) + return -1; + os_memcpy(cbc, iv, AES_BLOCK_SIZE); + + blocks = data_len / AES_BLOCK_SIZE; + for (i = 0; i < blocks; i++) { + os_memcpy(tmp, pos, AES_BLOCK_SIZE); + aes_decrypt(ctx, pos, pos); + for (j = 0; j < AES_BLOCK_SIZE; j++) + pos[j] ^= cbc[j]; + os_memcpy(cbc, tmp, AES_BLOCK_SIZE); + pos += AES_BLOCK_SIZE; + } + aes_decrypt_deinit(ctx); + return 0; +} diff --git a/contrib/wpa/src/crypto/aes-ctr.c b/contrib/wpa/src/crypto/aes-ctr.c new file mode 100644 index 0000000..468f877 --- /dev/null +++ b/contrib/wpa/src/crypto/aes-ctr.c @@ -0,0 +1,61 @@ +/* + * AES-128 CTR + * + * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "aes.h" +#include "aes_wrap.h" + +/** + * 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) +{ + void *ctx; + size_t j, len, left = data_len; + int i; + u8 *pos = data; + u8 counter[AES_BLOCK_SIZE], buf[AES_BLOCK_SIZE]; + + ctx = aes_encrypt_init(key, 16); + if (ctx == NULL) + return -1; + os_memcpy(counter, nonce, AES_BLOCK_SIZE); + + while (left > 0) { + aes_encrypt(ctx, counter, buf); + + len = (left < AES_BLOCK_SIZE) ? left : AES_BLOCK_SIZE; + for (j = 0; j < len; j++) + pos[j] ^= buf[j]; + pos += len; + left -= len; + + for (i = AES_BLOCK_SIZE - 1; i >= 0; i--) { + counter[i]++; + if (counter[i]) + break; + } + } + aes_encrypt_deinit(ctx); + return 0; +} diff --git a/contrib/wpa/src/crypto/aes-eax.c b/contrib/wpa/src/crypto/aes-eax.c new file mode 100644 index 0000000..d5c3971 --- /dev/null +++ b/contrib/wpa/src/crypto/aes-eax.c @@ -0,0 +1,151 @@ +/* + * AES-128 EAX + * + * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "aes.h" +#include "aes_wrap.h" + +/** + * 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) +{ + u8 *buf; + size_t buf_len; + u8 nonce_mac[AES_BLOCK_SIZE], hdr_mac[AES_BLOCK_SIZE], + data_mac[AES_BLOCK_SIZE]; + int i, ret = -1; + + if (nonce_len > data_len) + buf_len = nonce_len; + else + buf_len = data_len; + if (hdr_len > buf_len) + buf_len = hdr_len; + buf_len += 16; + + buf = os_malloc(buf_len); + if (buf == NULL) + return -1; + + os_memset(buf, 0, 15); + + buf[15] = 0; + os_memcpy(buf + 16, nonce, nonce_len); + if (omac1_aes_128(key, buf, 16 + nonce_len, nonce_mac)) + goto fail; + + buf[15] = 1; + os_memcpy(buf + 16, hdr, hdr_len); + if (omac1_aes_128(key, buf, 16 + hdr_len, hdr_mac)) + goto fail; + + if (aes_128_ctr_encrypt(key, nonce_mac, data, data_len)) + goto fail; + buf[15] = 2; + os_memcpy(buf + 16, data, data_len); + if (omac1_aes_128(key, buf, 16 + data_len, data_mac)) + goto fail; + + for (i = 0; i < AES_BLOCK_SIZE; i++) + tag[i] = nonce_mac[i] ^ data_mac[i] ^ hdr_mac[i]; + + ret = 0; +fail: + os_free(buf); + + return ret; +} + + +/** + * 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) +{ + u8 *buf; + size_t buf_len; + u8 nonce_mac[AES_BLOCK_SIZE], hdr_mac[AES_BLOCK_SIZE], + data_mac[AES_BLOCK_SIZE]; + int i; + + if (nonce_len > data_len) + buf_len = nonce_len; + else + buf_len = data_len; + if (hdr_len > buf_len) + buf_len = hdr_len; + buf_len += 16; + + buf = os_malloc(buf_len); + if (buf == NULL) + return -1; + + os_memset(buf, 0, 15); + + buf[15] = 0; + os_memcpy(buf + 16, nonce, nonce_len); + if (omac1_aes_128(key, buf, 16 + nonce_len, nonce_mac)) { + os_free(buf); + return -1; + } + + buf[15] = 1; + os_memcpy(buf + 16, hdr, hdr_len); + if (omac1_aes_128(key, buf, 16 + hdr_len, hdr_mac)) { + os_free(buf); + return -1; + } + + buf[15] = 2; + os_memcpy(buf + 16, data, data_len); + if (omac1_aes_128(key, buf, 16 + data_len, data_mac)) { + os_free(buf); + return -1; + } + + os_free(buf); + + for (i = 0; i < AES_BLOCK_SIZE; i++) { + if (tag[i] != (nonce_mac[i] ^ data_mac[i] ^ hdr_mac[i])) + return -2; + } + + return aes_128_ctr_encrypt(key, nonce_mac, data, data_len); +} diff --git a/contrib/wpa/src/crypto/aes-encblock.c b/contrib/wpa/src/crypto/aes-encblock.c new file mode 100644 index 0000000..8f35caa --- /dev/null +++ b/contrib/wpa/src/crypto/aes-encblock.c @@ -0,0 +1,38 @@ +/* + * AES encrypt_block + * + * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "aes.h" +#include "aes_wrap.h" + +/** + * 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) +{ + void *ctx; + ctx = aes_encrypt_init(key, 16); + if (ctx == NULL) + return -1; + aes_encrypt(ctx, in, out); + aes_encrypt_deinit(ctx); + return 0; +} diff --git a/contrib/wpa/src/crypto/aes-internal-dec.c b/contrib/wpa/src/crypto/aes-internal-dec.c new file mode 100644 index 0000000..2d32c03 --- /dev/null +++ b/contrib/wpa/src/crypto/aes-internal-dec.c @@ -0,0 +1,151 @@ +/* + * AES (Rijndael) cipher - decrypt + * + * Modifications to public domain implementation: + * - support only 128-bit keys + * - cleanup + * - 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 <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto.h" +#include "aes_i.h" + +/** + * Expand the cipher key into the decryption key schedule. + * + * @return the number of rounds for the given cipher key size. + */ +void rijndaelKeySetupDec(u32 rk[/*44*/], const u8 cipherKey[]) +{ + int Nr = 10, i, j; + u32 temp; + + /* expand the cipher key: */ + rijndaelKeySetupEnc(rk, cipherKey); + /* invert the order of the round keys: */ + for (i = 0, j = 4*Nr; i < j; i += 4, j -= 4) { + temp = rk[i ]; rk[i ] = rk[j ]; rk[j ] = temp; + temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp; + temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp; + temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp; + } + /* apply the inverse MixColumn transform to all round keys but the + * first and the last: */ + for (i = 1; i < Nr; i++) { + rk += 4; + 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 * aes_decrypt_init(const u8 *key, size_t len) +{ + u32 *rk; + if (len != 16) + return NULL; + rk = os_malloc(AES_PRIV_SIZE); + if (rk == NULL) + return NULL; + rijndaelKeySetupDec(rk, key); + return rk; +} + +static void rijndaelDecrypt(const u32 rk[/*44*/], const u8 ct[16], u8 pt[16]) +{ + u32 s0, s1, s2, s3, t0, t1, t2, t3; + const int Nr = 10; +#ifndef FULL_UNROLL + int r; +#endif /* ?FULL_UNROLL */ + + /* + * 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]; + +#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,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 (;;) { + ROUND(1,t,s); + rk += 8; + if (--r == 0) + break; + ROUND(0,s,t); + } + +#endif /* ?FULL_UNROLL */ + +#undef ROUND + + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = TD41(t0) ^ TD42(t3) ^ TD43(t2) ^ TD44(t1) ^ rk[0]; + PUTU32(pt , s0); + s1 = TD41(t1) ^ TD42(t0) ^ TD43(t3) ^ TD44(t2) ^ rk[1]; + PUTU32(pt + 4, s1); + s2 = TD41(t2) ^ TD42(t1) ^ TD43(t0) ^ TD44(t3) ^ rk[2]; + PUTU32(pt + 8, s2); + s3 = TD41(t3) ^ TD42(t2) ^ TD43(t1) ^ TD44(t0) ^ rk[3]; + PUTU32(pt + 12, s3); +} + +void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) +{ + rijndaelDecrypt(ctx, crypt, plain); +} + + +void aes_decrypt_deinit(void *ctx) +{ + os_memset(ctx, 0, AES_PRIV_SIZE); + os_free(ctx); +} diff --git a/contrib/wpa/src/crypto/aes-internal-enc.c b/contrib/wpa/src/crypto/aes-internal-enc.c new file mode 100644 index 0000000..2f19826 --- /dev/null +++ b/contrib/wpa/src/crypto/aes-internal-enc.c @@ -0,0 +1,121 @@ +/* + * AES (Rijndael) cipher - encrypt + * + * Modifications to public domain implementation: + * - support only 128-bit keys + * - cleanup + * - 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 <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto.h" +#include "aes_i.h" + +void rijndaelEncrypt(const u32 rk[/*44*/], const u8 pt[16], u8 ct[16]) +{ + u32 s0, s1, s2, s3, t0, t1, t2, t3; + const int Nr = 10; +#ifndef FULL_UNROLL + int r; +#endif /* ?FULL_UNROLL */ + + /* + * map byte array block to cipher state + * and add initial round key: + */ + s0 = GETU32(pt ) ^ rk[0]; + 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,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 (;;) { + ROUND(1,t,s); + rk += 8; + if (--r == 0) + break; + ROUND(0,s,t); + } + +#endif /* ?FULL_UNROLL */ + +#undef ROUND + + /* + * apply last round and + * map cipher state to byte array block: + */ + s0 = TE41(t0) ^ TE42(t1) ^ TE43(t2) ^ TE44(t3) ^ rk[0]; + PUTU32(ct , s0); + s1 = TE41(t1) ^ TE42(t2) ^ TE43(t3) ^ TE44(t0) ^ rk[1]; + PUTU32(ct + 4, s1); + s2 = TE41(t2) ^ TE42(t3) ^ TE43(t0) ^ TE44(t1) ^ rk[2]; + PUTU32(ct + 8, s2); + s3 = TE41(t3) ^ TE42(t0) ^ TE43(t1) ^ TE44(t2) ^ rk[3]; + PUTU32(ct + 12, s3); +} + + +void * aes_encrypt_init(const u8 *key, size_t len) +{ + u32 *rk; + if (len != 16) + return NULL; + rk = os_malloc(AES_PRIV_SIZE); + 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) +{ + os_memset(ctx, 0, AES_PRIV_SIZE); + os_free(ctx); +} diff --git a/contrib/wpa/src/crypto/aes.c b/contrib/wpa/src/crypto/aes-internal.c index 8b8f2a0..4161220 100644 --- a/contrib/wpa/src/crypto/aes.c +++ b/contrib/wpa/src/crypto/aes-internal.c @@ -24,10 +24,8 @@ #include "includes.h" #include "common.h" - -#ifdef INTERNAL_AES - #include "crypto.h" +#include "aes_i.h" /* * rijndael-alg-fst.c @@ -55,9 +53,6 @@ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -/* #define FULL_UNROLL */ -#define AES_SMALL_TABLES - /* Te0[x] = S [x].[02, 01, 01, 03]; @@ -73,7 +68,7 @@ Td3[x] = Si[x].[09, 0d, 0b, 0e]; Td4[x] = Si[x].[01, 01, 01, 01]; */ -static const u32 Te0[256] = { +const u32 Te0[256] = { 0xc66363a5U, 0xf87c7c84U, 0xee777799U, 0xf67b7b8dU, 0xfff2f20dU, 0xd66b6bbdU, 0xde6f6fb1U, 0x91c5c554U, 0x60303050U, 0x02010103U, 0xce6767a9U, 0x562b2b7dU, @@ -140,7 +135,7 @@ static const u32 Te0[256] = { 0x7bb0b0cbU, 0xa85454fcU, 0x6dbbbbd6U, 0x2c16163aU, }; #ifndef AES_SMALL_TABLES -static const u32 Te1[256] = { +const u32 Te1[256] = { 0xa5c66363U, 0x84f87c7cU, 0x99ee7777U, 0x8df67b7bU, 0x0dfff2f2U, 0xbdd66b6bU, 0xb1de6f6fU, 0x5491c5c5U, 0x50603030U, 0x03020101U, 0xa9ce6767U, 0x7d562b2bU, @@ -206,7 +201,7 @@ static const u32 Te1[256] = { 0xc3824141U, 0xb0299999U, 0x775a2d2dU, 0x111e0f0fU, 0xcb7bb0b0U, 0xfca85454U, 0xd66dbbbbU, 0x3a2c1616U, }; -static const u32 Te2[256] = { +const u32 Te2[256] = { 0x63a5c663U, 0x7c84f87cU, 0x7799ee77U, 0x7b8df67bU, 0xf20dfff2U, 0x6bbdd66bU, 0x6fb1de6fU, 0xc55491c5U, 0x30506030U, 0x01030201U, 0x67a9ce67U, 0x2b7d562bU, @@ -272,7 +267,7 @@ static const u32 Te2[256] = { 0x41c38241U, 0x99b02999U, 0x2d775a2dU, 0x0f111e0fU, 0xb0cb7bb0U, 0x54fca854U, 0xbbd66dbbU, 0x163a2c16U, }; -static const u32 Te3[256] = { +const u32 Te3[256] = { 0x6363a5c6U, 0x7c7c84f8U, 0x777799eeU, 0x7b7b8df6U, 0xf2f20dffU, 0x6b6bbdd6U, 0x6f6fb1deU, 0xc5c55491U, @@ -339,7 +334,7 @@ static const u32 Te3[256] = { 0x4141c382U, 0x9999b029U, 0x2d2d775aU, 0x0f0f111eU, 0xb0b0cb7bU, 0x5454fca8U, 0xbbbbd66dU, 0x16163a2cU, }; -static const u32 Te4[256] = { +const u32 Te4[256] = { 0x63636363U, 0x7c7c7c7cU, 0x77777777U, 0x7b7b7b7bU, 0xf2f2f2f2U, 0x6b6b6b6bU, 0x6f6f6f6fU, 0xc5c5c5c5U, 0x30303030U, 0x01010101U, 0x67676767U, 0x2b2b2b2bU, @@ -406,7 +401,7 @@ static const u32 Te4[256] = { 0xb0b0b0b0U, 0x54545454U, 0xbbbbbbbbU, 0x16161616U, }; #endif /* AES_SMALL_TABLES */ -static const u32 Td0[256] = { +const u32 Td0[256] = { 0x51f4a750U, 0x7e416553U, 0x1a17a4c3U, 0x3a275e96U, 0x3bab6bcbU, 0x1f9d45f1U, 0xacfa58abU, 0x4be30393U, 0x2030fa55U, 0xad766df6U, 0x88cc7691U, 0xf5024c25U, @@ -473,7 +468,7 @@ static const u32 Td0[256] = { 0x7bcb8461U, 0xd532b670U, 0x486c5c74U, 0xd0b85742U, }; #ifndef AES_SMALL_TABLES -static const u32 Td1[256] = { +const u32 Td1[256] = { 0x5051f4a7U, 0x537e4165U, 0xc31a17a4U, 0x963a275eU, 0xcb3bab6bU, 0xf11f9d45U, 0xabacfa58U, 0x934be303U, 0x552030faU, 0xf6ad766dU, 0x9188cc76U, 0x25f5024cU, @@ -539,7 +534,7 @@ static const u32 Td1[256] = { 0x7139a801U, 0xde080cb3U, 0x9cd8b4e4U, 0x906456c1U, 0x617bcb84U, 0x70d532b6U, 0x74486c5cU, 0x42d0b857U, }; -static const u32 Td2[256] = { +const u32 Td2[256] = { 0xa75051f4U, 0x65537e41U, 0xa4c31a17U, 0x5e963a27U, 0x6bcb3babU, 0x45f11f9dU, 0x58abacfaU, 0x03934be3U, 0xfa552030U, 0x6df6ad76U, 0x769188ccU, 0x4c25f502U, @@ -606,7 +601,7 @@ static const u32 Td2[256] = { 0x017139a8U, 0xb3de080cU, 0xe49cd8b4U, 0xc1906456U, 0x84617bcbU, 0xb670d532U, 0x5c74486cU, 0x5742d0b8U, }; -static const u32 Td3[256] = { +const u32 Td3[256] = { 0xf4a75051U, 0x4165537eU, 0x17a4c31aU, 0x275e963aU, 0xab6bcb3bU, 0x9d45f11fU, 0xfa58abacU, 0xe303934bU, 0x30fa5520U, 0x766df6adU, 0xcc769188U, 0x024c25f5U, @@ -672,7 +667,7 @@ static const u32 Td3[256] = { 0xa8017139U, 0x0cb3de08U, 0xb4e49cd8U, 0x56c19064U, 0xcb84617bU, 0x32b670d5U, 0x6c5c7448U, 0xb85742d0U, }; -static const u32 Td4[256] = { +const u32 Td4[256] = { 0x52525252U, 0x09090909U, 0x6a6a6a6aU, 0xd5d5d5d5U, 0x30303030U, 0x36363636U, 0xa5a5a5a5U, 0x38383838U, 0xbfbfbfbfU, 0x40404040U, 0xa3a3a3a3U, 0x9e9e9e9eU, @@ -738,13 +733,13 @@ static const u32 Td4[256] = { 0xe1e1e1e1U, 0x69696969U, 0x14141414U, 0x63636363U, 0x55555555U, 0x21212121U, 0x0c0c0c0cU, 0x7d7d7d7dU, }; -static const u32 rcon[] = { +const u32 rcon[] = { 0x01000000, 0x02000000, 0x04000000, 0x08000000, 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] = { +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, @@ -778,95 +773,11 @@ static const u8 Td4s[256] = { 0x17U, 0x2bU, 0x04U, 0x7eU, 0xbaU, 0x77U, 0xd6U, 0x26U, 0xe1U, 0x69U, 0x14U, 0x63U, 0x55U, 0x21U, 0x0cU, 0x7dU, }; -static const u8 rcons[] = { +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) - -#ifdef _MSC_VER -#define GETU32(p) SWAP(*((u32 *)(p))) -#define PUTU32(ct, st) { *((u32 *)(ct)) = SWAP((st)); } -#else -#define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ \ -((u32)(pt)[2] << 8) ^ ((u32)(pt)[3])) -#define PUTU32(ct, st) { \ -(ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); \ -(ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); } -#endif - /** * Expand the cipher key into the encryption key schedule. * @@ -892,236 +803,3 @@ void rijndaelKeySetupEnc(u32 rk[/*44*/], const u8 cipherKey[]) rk += 4; } } - -#ifndef CONFIG_NO_AES_DECRYPT -/** - * Expand the cipher key into the decryption key schedule. - * - * @return the number of rounds for the given cipher key size. - */ -void rijndaelKeySetupDec(u32 rk[/*44*/], const u8 cipherKey[]) -{ - int Nr = 10, i, j; - u32 temp; - - /* expand the cipher key: */ - rijndaelKeySetupEnc(rk, cipherKey); - /* invert the order of the round keys: */ - for (i = 0, j = 4*Nr; i < j; i += 4, j -= 4) { - temp = rk[i ]; rk[i ] = rk[j ]; rk[j ] = temp; - temp = rk[i + 1]; rk[i + 1] = rk[j + 1]; rk[j + 1] = temp; - temp = rk[i + 2]; rk[i + 2] = rk[j + 2]; rk[j + 2] = temp; - temp = rk[i + 3]; rk[i + 3] = rk[j + 3]; rk[j + 3] = temp; - } - /* apply the inverse MixColumn transform to all round keys but the - * first and the last: */ - for (i = 1; i < Nr; i++) { - rk += 4; - 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)); - } - } -} -#endif /* CONFIG_NO_AES_DECRYPT */ - -#ifndef CONFIG_NO_AES_ENCRYPT -void rijndaelEncrypt(const u32 rk[/*44*/], const u8 pt[16], u8 ct[16]) -{ - u32 s0, s1, s2, s3, t0, t1, t2, t3; - const int Nr = 10; -#ifndef FULL_UNROLL - int r; -#endif /* ?FULL_UNROLL */ - - /* - * map byte array block to cipher state - * and add initial round key: - */ - s0 = GETU32(pt ) ^ rk[0]; - 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,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 (;;) { - ROUND(1,t,s); - rk += 8; - if (--r == 0) - break; - ROUND(0,s,t); - } - -#endif /* ?FULL_UNROLL */ - -#undef ROUND - - /* - * apply last round and - * map cipher state to byte array block: - */ - s0 = TE41(t0) ^ TE42(t1) ^ TE43(t2) ^ TE44(t3) ^ rk[0]; - PUTU32(ct , s0); - s1 = TE41(t1) ^ TE42(t2) ^ TE43(t3) ^ TE44(t0) ^ rk[1]; - PUTU32(ct + 4, s1); - s2 = TE41(t2) ^ TE42(t3) ^ TE43(t0) ^ TE44(t1) ^ rk[2]; - PUTU32(ct + 8, s2); - s3 = TE41(t3) ^ TE42(t0) ^ TE43(t1) ^ TE44(t2) ^ rk[3]; - PUTU32(ct + 12, s3); -} -#endif /* CONFIG_NO_AES_ENCRYPT */ - -void rijndaelDecrypt(const u32 rk[/*44*/], const u8 ct[16], u8 pt[16]) -{ - u32 s0, s1, s2, s3, t0, t1, t2, t3; - const int Nr = 10; -#ifndef FULL_UNROLL - int r; -#endif /* ?FULL_UNROLL */ - - /* - * 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]; - -#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,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 (;;) { - ROUND(1,t,s); - rk += 8; - if (--r == 0) - break; - ROUND(0,s,t); - } - -#endif /* ?FULL_UNROLL */ - -#undef ROUND - - /* - * apply last round and - * map cipher state to byte array block: - */ - s0 = TD41(t0) ^ TD42(t3) ^ TD43(t2) ^ TD44(t1) ^ rk[0]; - PUTU32(pt , s0); - s1 = TD41(t1) ^ TD42(t0) ^ TD43(t3) ^ TD44(t2) ^ rk[1]; - PUTU32(pt + 4, s1); - s2 = TD41(t2) ^ TD42(t1) ^ TD43(t0) ^ TD44(t3) ^ rk[2]; - PUTU32(pt + 8, s2); - s3 = TD41(t3) ^ TD42(t2) ^ TD43(t1) ^ TD44(t0) ^ rk[3]; - PUTU32(pt + 12, s3); -} - - - -/* Generic wrapper functions for AES functions */ - -#define AES_PRIV_SIZE (4 * 44) - -#ifndef CONFIG_NO_AES_ENCRYPT -void * aes_encrypt_init(const u8 *key, size_t len) -{ - u32 *rk; - if (len != 16) - return NULL; - rk = os_malloc(AES_PRIV_SIZE); - 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) -{ - os_memset(ctx, 0, AES_PRIV_SIZE); - os_free(ctx); -} -#endif /* CONFIG_NO_AES_ENCRYPT */ - - -#ifndef CONFIG_NO_AES_DECRYPT -void * aes_decrypt_init(const u8 *key, size_t len) -{ - u32 *rk; - if (len != 16) - return NULL; - rk = os_malloc(AES_PRIV_SIZE); - 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) -{ - os_memset(ctx, 0, AES_PRIV_SIZE); - os_free(ctx); -} -#endif /* CONFIG_NO_AES_DECRYPT */ - -#endif /* INTERNAL_AES */ diff --git a/contrib/wpa/src/crypto/aes-omac1.c b/contrib/wpa/src/crypto/aes-omac1.c new file mode 100644 index 0000000..f775296 --- /dev/null +++ b/contrib/wpa/src/crypto/aes-omac1.c @@ -0,0 +1,124 @@ +/* + * One-key CBC MAC (OMAC1) hash with AES-128 + * + * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "aes.h" +#include "aes_wrap.h" + +static void gf_mulx(u8 *pad) +{ + int i, carry; + + carry = pad[0] & 0x80; + for (i = 0; i < AES_BLOCK_SIZE - 1; i++) + pad[i] = (pad[i] << 1) | (pad[i + 1] >> 7); + pad[AES_BLOCK_SIZE - 1] <<= 1; + if (carry) + pad[AES_BLOCK_SIZE - 1] ^= 0x87; +} + + +/** + * omac1_aes_128_vector - One-Key CBC MAC (OMAC1) hash with AES-128 + * @key: 128-bit key for the hash operation + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for MAC (128 bits, i.e., 16 bytes) + * Returns: 0 on success, -1 on failure + * + * This is a mode for using block cipher (AES in this case) for authentication. + * OMAC1 was standardized with the name CMAC by NIST in a Special Publication + * (SP) 800-38B. + */ +int omac1_aes_128_vector(const u8 *key, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) +{ + void *ctx; + u8 cbc[AES_BLOCK_SIZE], pad[AES_BLOCK_SIZE]; + const u8 *pos, *end; + size_t i, e, left, total_len; + + ctx = aes_encrypt_init(key, 16); + if (ctx == NULL) + return -1; + os_memset(cbc, 0, AES_BLOCK_SIZE); + + total_len = 0; + for (e = 0; e < num_elem; e++) + total_len += len[e]; + left = total_len; + + e = 0; + pos = addr[0]; + end = pos + len[0]; + + while (left >= AES_BLOCK_SIZE) { + for (i = 0; i < AES_BLOCK_SIZE; i++) { + cbc[i] ^= *pos++; + if (pos >= end) { + e++; + pos = addr[e]; + end = pos + len[e]; + } + } + if (left > AES_BLOCK_SIZE) + aes_encrypt(ctx, cbc, cbc); + left -= AES_BLOCK_SIZE; + } + + os_memset(pad, 0, AES_BLOCK_SIZE); + aes_encrypt(ctx, pad, pad); + gf_mulx(pad); + + if (left || total_len == 0) { + for (i = 0; i < left; i++) { + cbc[i] ^= *pos++; + if (pos >= end) { + e++; + pos = addr[e]; + end = pos + len[e]; + } + } + cbc[left] ^= 0x80; + gf_mulx(pad); + } + + for (i = 0; i < AES_BLOCK_SIZE; i++) + pad[i] ^= cbc[i]; + aes_encrypt(ctx, pad, mac); + aes_encrypt_deinit(ctx); + return 0; +} + + +/** + * omac1_aes_128 - One-Key CBC MAC (OMAC1) hash with AES-128 (aka AES-CMAC) + * @key: 128-bit key for the hash operation + * @data: Data buffer for which a MAC is determined + * @data_len: Length of data buffer in bytes + * @mac: Buffer for MAC (128 bits, i.e., 16 bytes) + * Returns: 0 on success, -1 on failure + * + * This is a mode for using block cipher (AES in this case) for authentication. + * OMAC1 was standardized with the name CMAC by NIST in a Special Publication + * (SP) 800-38B. + */ +int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac) +{ + return omac1_aes_128_vector(key, 1, &data, &data_len, mac); +} diff --git a/contrib/wpa/src/crypto/aes-unwrap.c b/contrib/wpa/src/crypto/aes-unwrap.c new file mode 100644 index 0000000..f233ffa --- /dev/null +++ b/contrib/wpa/src/crypto/aes-unwrap.c @@ -0,0 +1,79 @@ +/* + * AES key unwrap (128-bit KEK, RFC3394) + * + * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "aes.h" +#include "aes_wrap.h" + +/** + * aes_unwrap - Unwrap key with AES Key Wrap Algorithm (128-bit KEK) (RFC3394) + * @kek: Key encryption key (KEK) + * @n: Length of the plaintext key in 64-bit units; e.g., 2 = 128-bit = 16 + * bytes + * @cipher: Wrapped key to be unwrapped, (n + 1) * 64 bits + * @plain: Plaintext key, n * 64 bits + * Returns: 0 on success, -1 on failure (e.g., integrity verification failed) + */ +int aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain) +{ + u8 a[8], *r, b[16]; + int i, j; + void *ctx; + + /* 1) Initialize variables. */ + os_memcpy(a, cipher, 8); + r = plain; + os_memcpy(r, cipher + 8, 8 * n); + + ctx = aes_decrypt_init(kek, 16); + if (ctx == NULL) + return -1; + + /* 2) Compute intermediate values. + * For j = 5 to 0 + * For i = n to 1 + * B = AES-1(K, (A ^ t) | R[i]) where t = n*j+i + * A = MSB(64, B) + * R[i] = LSB(64, B) + */ + for (j = 5; j >= 0; j--) { + r = plain + (n - 1) * 8; + for (i = n; i >= 1; i--) { + os_memcpy(b, a, 8); + b[7] ^= n * j + i; + + os_memcpy(b + 8, r, 8); + aes_decrypt(ctx, b, b); + os_memcpy(a, b, 8); + os_memcpy(r, b + 8, 8); + r -= 8; + } + } + aes_decrypt_deinit(ctx); + + /* 3) Output results. + * + * These are already in @plain due to the location of temporary + * variables. Just verify that the IV matches with the expected value. + */ + for (i = 0; i < 8; i++) { + if (a[i] != 0xa6) + return -1; + } + + return 0; +} diff --git a/contrib/wpa/src/crypto/aes-wrap.c b/contrib/wpa/src/crypto/aes-wrap.c new file mode 100644 index 0000000..28d0c89 --- /dev/null +++ b/contrib/wpa/src/crypto/aes-wrap.c @@ -0,0 +1,76 @@ +/* + * AES Key Wrap Algorithm (128-bit KEK) (RFC3394) + * + * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "aes.h" +#include "aes_wrap.h" + +/** + * aes_wrap - Wrap keys with AES Key Wrap Algorithm (128-bit KEK) (RFC3394) + * @kek: 16-octet Key encryption key (KEK) + * @n: Length of the plaintext key in 64-bit units; e.g., 2 = 128-bit = 16 + * bytes + * @plain: Plaintext key to be wrapped, n * 64 bits + * @cipher: Wrapped key, (n + 1) * 64 bits + * Returns: 0 on success, -1 on failure + */ +int aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher) +{ + u8 *a, *r, b[16]; + int i, j; + void *ctx; + + a = cipher; + r = cipher + 8; + + /* 1) Initialize variables. */ + os_memset(a, 0xa6, 8); + os_memcpy(r, plain, 8 * n); + + ctx = aes_encrypt_init(kek, 16); + if (ctx == NULL) + return -1; + + /* 2) Calculate intermediate values. + * For j = 0 to 5 + * For i=1 to n + * B = AES(K, A | R[i]) + * A = MSB(64, B) ^ t where t = (n*j)+i + * R[i] = LSB(64, B) + */ + for (j = 0; j <= 5; j++) { + r = cipher + 8; + for (i = 1; i <= n; i++) { + os_memcpy(b, a, 8); + os_memcpy(b + 8, r, 8); + aes_encrypt(ctx, b, b); + os_memcpy(a, b, 8); + a[7] ^= n * j + i; + os_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; +} diff --git a/contrib/wpa/src/crypto/aes.h b/contrib/wpa/src/crypto/aes.h index 6b9f414..ba384a9 100644 --- a/contrib/wpa/src/crypto/aes.h +++ b/contrib/wpa/src/crypto/aes.h @@ -15,6 +15,8 @@ #ifndef AES_H #define AES_H +#define AES_BLOCK_SIZE 16 + void * aes_encrypt_init(const u8 *key, size_t len); void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt); void aes_encrypt_deinit(void *ctx); diff --git a/contrib/wpa/src/crypto/aes_i.h b/contrib/wpa/src/crypto/aes_i.h new file mode 100644 index 0000000..6b40bc7 --- /dev/null +++ b/contrib/wpa/src/crypto/aes_i.h @@ -0,0 +1,122 @@ +/* + * AES (Rijndael) cipher + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef AES_I_H +#define AES_I_H + +#include "aes.h" + +/* #define FULL_UNROLL */ +#define AES_SMALL_TABLES + +extern const u32 Te0[256]; +extern const u32 Te1[256]; +extern const u32 Te2[256]; +extern const u32 Te3[256]; +extern const u32 Te4[256]; +extern const u32 Td0[256]; +extern const u32 Td1[256]; +extern const u32 Td2[256]; +extern const u32 Td3[256]; +extern const u32 Td4[256]; +extern const u32 rcon[10]; +extern const u8 Td4s[256]; +extern const u8 rcons[10]; + +#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 */ + +#ifdef _MSC_VER +#define SWAP(x) (_lrotl(x, 8) & 0x00ff00ff | _lrotr(x, 8) & 0xff00ff00) +#define GETU32(p) SWAP(*((u32 *)(p))) +#define PUTU32(ct, st) { *((u32 *)(ct)) = SWAP((st)); } +#else +#define GETU32(pt) (((u32)(pt)[0] << 24) ^ ((u32)(pt)[1] << 16) ^ \ +((u32)(pt)[2] << 8) ^ ((u32)(pt)[3])) +#define PUTU32(ct, st) { \ +(ct)[0] = (u8)((st) >> 24); (ct)[1] = (u8)((st) >> 16); \ +(ct)[2] = (u8)((st) >> 8); (ct)[3] = (u8)(st); } +#endif + +#define AES_PRIV_SIZE (4 * 44) + +void rijndaelKeySetupEnc(u32 rk[/*44*/], const u8 cipherKey[]); + +#endif /* AES_I_H */ diff --git a/contrib/wpa/src/crypto/aes_wrap.c b/contrib/wpa/src/crypto/aes_wrap.c deleted file mode 100644 index b1448b0..0000000 --- a/contrib/wpa/src/crypto/aes_wrap.c +++ /dev/null @@ -1,533 +0,0 @@ -/* - * AES-based functions - * - * - AES Key Wrap Algorithm (128-bit KEK) (RFC3394) - * - One-Key CBC MAC (OMAC1, i.e., CMAC) hash with AES-128 - * - AES-128 CTR mode encryption - * - AES-128 EAX mode encryption/decryption - * - AES-128 CBC - * - * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" - -#include "common.h" -#include "aes_wrap.h" -#include "crypto.h" - -#ifndef CONFIG_NO_AES_WRAP - -/** - * aes_wrap - Wrap keys with AES Key Wrap Algorithm (128-bit KEK) (RFC3394) - * @kek: 16-octet Key encryption key (KEK) - * @n: Length of the plaintext key in 64-bit units; e.g., 2 = 128-bit = 16 - * bytes - * @plain: Plaintext key to be wrapped, n * 64 bits - * @cipher: Wrapped key, (n + 1) * 64 bits - * Returns: 0 on success, -1 on failure - */ -int aes_wrap(const u8 *kek, int n, const u8 *plain, u8 *cipher) -{ - u8 *a, *r, b[16]; - int i, j; - void *ctx; - - a = cipher; - r = cipher + 8; - - /* 1) Initialize variables. */ - os_memset(a, 0xa6, 8); - os_memcpy(r, plain, 8 * n); - - ctx = aes_encrypt_init(kek, 16); - if (ctx == NULL) - return -1; - - /* 2) Calculate intermediate values. - * For j = 0 to 5 - * For i=1 to n - * B = AES(K, A | R[i]) - * A = MSB(64, B) ^ t where t = (n*j)+i - * R[i] = LSB(64, B) - */ - for (j = 0; j <= 5; j++) { - r = cipher + 8; - for (i = 1; i <= n; i++) { - os_memcpy(b, a, 8); - os_memcpy(b + 8, r, 8); - aes_encrypt(ctx, b, b); - os_memcpy(a, b, 8); - a[7] ^= n * j + i; - os_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; -} - -#endif /* CONFIG_NO_AES_WRAP */ - - -#ifndef CONFIG_NO_AES_UNWRAP - -/** - * aes_unwrap - Unwrap key with AES Key Wrap Algorithm (128-bit KEK) (RFC3394) - * @kek: Key encryption key (KEK) - * @n: Length of the plaintext key in 64-bit units; e.g., 2 = 128-bit = 16 - * bytes - * @cipher: Wrapped key to be unwrapped, (n + 1) * 64 bits - * @plain: Plaintext key, n * 64 bits - * Returns: 0 on success, -1 on failure (e.g., integrity verification failed) - */ -int aes_unwrap(const u8 *kek, int n, const u8 *cipher, u8 *plain) -{ - u8 a[8], *r, b[16]; - int i, j; - void *ctx; - - /* 1) Initialize variables. */ - os_memcpy(a, cipher, 8); - r = plain; - os_memcpy(r, cipher + 8, 8 * n); - - ctx = aes_decrypt_init(kek, 16); - if (ctx == NULL) - return -1; - - /* 2) Compute intermediate values. - * For j = 5 to 0 - * For i = n to 1 - * B = AES-1(K, (A ^ t) | R[i]) where t = n*j+i - * A = MSB(64, B) - * R[i] = LSB(64, B) - */ - for (j = 5; j >= 0; j--) { - r = plain + (n - 1) * 8; - for (i = n; i >= 1; i--) { - os_memcpy(b, a, 8); - b[7] ^= n * j + i; - - os_memcpy(b + 8, r, 8); - aes_decrypt(ctx, b, b); - os_memcpy(a, b, 8); - os_memcpy(r, b + 8, 8); - r -= 8; - } - } - aes_decrypt_deinit(ctx); - - /* 3) Output results. - * - * These are already in @plain due to the location of temporary - * variables. Just verify that the IV matches with the expected value. - */ - for (i = 0; i < 8; i++) { - if (a[i] != 0xa6) - return -1; - } - - return 0; -} - -#endif /* CONFIG_NO_AES_UNWRAP */ - - -#define BLOCK_SIZE 16 - -#ifndef CONFIG_NO_AES_OMAC1 - -static void gf_mulx(u8 *pad) -{ - int i, carry; - - carry = pad[0] & 0x80; - for (i = 0; i < BLOCK_SIZE - 1; i++) - pad[i] = (pad[i] << 1) | (pad[i + 1] >> 7); - pad[BLOCK_SIZE - 1] <<= 1; - if (carry) - pad[BLOCK_SIZE - 1] ^= 0x87; -} - - -/** - * omac1_aes_128_vector - One-Key CBC MAC (OMAC1) hash with AES-128 - * @key: 128-bit key for the hash operation - * @num_elem: Number of elements in the data vector - * @addr: Pointers to the data areas - * @len: Lengths of the data blocks - * @mac: Buffer for MAC (128 bits, i.e., 16 bytes) - * Returns: 0 on success, -1 on failure - * - * This is a mode for using block cipher (AES in this case) for authentication. - * OMAC1 was standardized with the name CMAC by NIST in a Special Publication - * (SP) 800-38B. - */ -int omac1_aes_128_vector(const u8 *key, size_t num_elem, - const u8 *addr[], const size_t *len, u8 *mac) -{ - void *ctx; - u8 cbc[BLOCK_SIZE], pad[BLOCK_SIZE]; - const u8 *pos, *end; - size_t i, e, left, total_len; - - ctx = aes_encrypt_init(key, 16); - if (ctx == NULL) - return -1; - os_memset(cbc, 0, BLOCK_SIZE); - - total_len = 0; - for (e = 0; e < num_elem; e++) - total_len += len[e]; - left = total_len; - - e = 0; - pos = addr[0]; - end = pos + len[0]; - - while (left >= BLOCK_SIZE) { - for (i = 0; i < BLOCK_SIZE; i++) { - cbc[i] ^= *pos++; - if (pos >= end) { - e++; - pos = addr[e]; - end = pos + len[e]; - } - } - if (left > BLOCK_SIZE) - aes_encrypt(ctx, cbc, cbc); - left -= BLOCK_SIZE; - } - - os_memset(pad, 0, BLOCK_SIZE); - aes_encrypt(ctx, pad, pad); - gf_mulx(pad); - - if (left || total_len == 0) { - for (i = 0; i < left; i++) { - cbc[i] ^= *pos++; - if (pos >= end) { - e++; - pos = addr[e]; - end = pos + len[e]; - } - } - cbc[left] ^= 0x80; - gf_mulx(pad); - } - - for (i = 0; i < BLOCK_SIZE; i++) - pad[i] ^= cbc[i]; - aes_encrypt(ctx, pad, mac); - aes_encrypt_deinit(ctx); - return 0; -} - - -/** - * omac1_aes_128 - One-Key CBC MAC (OMAC1) hash with AES-128 (aka AES-CMAC) - * @key: 128-bit key for the hash operation - * @data: Data buffer for which a MAC is determined - * @data_len: Length of data buffer in bytes - * @mac: Buffer for MAC (128 bits, i.e., 16 bytes) - * Returns: 0 on success, -1 on failure - * - * This is a mode for using block cipher (AES in this case) for authentication. - * OMAC1 was standardized with the name CMAC by NIST in a Special Publication - * (SP) 800-38B. - */ -int omac1_aes_128(const u8 *key, const u8 *data, size_t data_len, u8 *mac) -{ - return omac1_aes_128_vector(key, 1, &data, &data_len, mac); -} - -#endif /* CONFIG_NO_AES_OMAC1 */ - - -#ifndef CONFIG_NO_AES_ENCRYPT_BLOCK -/** - * aes_128_encrypt_block - Perform one AES 128-bit block operation - * @key: Key for AES - * @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) -{ - void *ctx; - ctx = aes_encrypt_init(key, 16); - if (ctx == NULL) - return -1; - aes_encrypt(ctx, in, out); - aes_encrypt_deinit(ctx); - return 0; -} -#endif /* CONFIG_NO_AES_ENCRYPT_BLOCK */ - - -#ifndef CONFIG_NO_AES_CTR - -/** - * 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) -{ - void *ctx; - size_t j, len, left = data_len; - int i; - u8 *pos = data; - u8 counter[BLOCK_SIZE], buf[BLOCK_SIZE]; - - ctx = aes_encrypt_init(key, 16); - if (ctx == NULL) - return -1; - os_memcpy(counter, nonce, BLOCK_SIZE); - - while (left > 0) { - aes_encrypt(ctx, counter, buf); - - len = (left < BLOCK_SIZE) ? left : BLOCK_SIZE; - for (j = 0; j < len; j++) - pos[j] ^= buf[j]; - pos += len; - left -= len; - - for (i = BLOCK_SIZE - 1; i >= 0; i--) { - counter[i]++; - if (counter[i]) - break; - } - } - aes_encrypt_deinit(ctx); - return 0; -} - -#endif /* CONFIG_NO_AES_CTR */ - - -#ifndef CONFIG_NO_AES_EAX - -/** - * 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) -{ - u8 *buf; - size_t buf_len; - u8 nonce_mac[BLOCK_SIZE], hdr_mac[BLOCK_SIZE], data_mac[BLOCK_SIZE]; - int i, ret = -1; - - if (nonce_len > data_len) - buf_len = nonce_len; - else - buf_len = data_len; - if (hdr_len > buf_len) - buf_len = hdr_len; - buf_len += 16; - - buf = os_malloc(buf_len); - if (buf == NULL) - return -1; - - os_memset(buf, 0, 15); - - buf[15] = 0; - os_memcpy(buf + 16, nonce, nonce_len); - if (omac1_aes_128(key, buf, 16 + nonce_len, nonce_mac)) - goto fail; - - buf[15] = 1; - os_memcpy(buf + 16, hdr, hdr_len); - if (omac1_aes_128(key, buf, 16 + hdr_len, hdr_mac)) - goto fail; - - if (aes_128_ctr_encrypt(key, nonce_mac, data, data_len)) - goto fail; - buf[15] = 2; - os_memcpy(buf + 16, data, data_len); - if (omac1_aes_128(key, buf, 16 + data_len, data_mac)) - goto fail; - - for (i = 0; i < BLOCK_SIZE; i++) - tag[i] = nonce_mac[i] ^ data_mac[i] ^ hdr_mac[i]; - - ret = 0; -fail: - os_free(buf); - - return ret; -} - - -/** - * 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) -{ - u8 *buf; - size_t buf_len; - u8 nonce_mac[BLOCK_SIZE], hdr_mac[BLOCK_SIZE], data_mac[BLOCK_SIZE]; - int i; - - if (nonce_len > data_len) - buf_len = nonce_len; - else - buf_len = data_len; - if (hdr_len > buf_len) - buf_len = hdr_len; - buf_len += 16; - - buf = os_malloc(buf_len); - if (buf == NULL) - return -1; - - os_memset(buf, 0, 15); - - buf[15] = 0; - os_memcpy(buf + 16, nonce, nonce_len); - if (omac1_aes_128(key, buf, 16 + nonce_len, nonce_mac)) { - os_free(buf); - return -1; - } - - buf[15] = 1; - os_memcpy(buf + 16, hdr, hdr_len); - if (omac1_aes_128(key, buf, 16 + hdr_len, hdr_mac)) { - os_free(buf); - return -1; - } - - buf[15] = 2; - os_memcpy(buf + 16, data, data_len); - if (omac1_aes_128(key, buf, 16 + data_len, data_mac)) { - os_free(buf); - return -1; - } - - os_free(buf); - - for (i = 0; i < BLOCK_SIZE; i++) { - if (tag[i] != (nonce_mac[i] ^ data_mac[i] ^ hdr_mac[i])) - return -2; - } - - return aes_128_ctr_encrypt(key, nonce_mac, data, data_len); -} - -#endif /* CONFIG_NO_AES_EAX */ - - -#ifndef CONFIG_NO_AES_CBC - -/** - * 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) -{ - void *ctx; - u8 cbc[BLOCK_SIZE]; - u8 *pos = data; - int i, j, blocks; - - ctx = aes_encrypt_init(key, 16); - if (ctx == NULL) - return -1; - os_memcpy(cbc, iv, BLOCK_SIZE); - - blocks = data_len / BLOCK_SIZE; - for (i = 0; i < blocks; i++) { - for (j = 0; j < BLOCK_SIZE; j++) - cbc[j] ^= pos[j]; - aes_encrypt(ctx, cbc, cbc); - os_memcpy(pos, cbc, BLOCK_SIZE); - pos += BLOCK_SIZE; - } - aes_encrypt_deinit(ctx); - return 0; -} - - -/** - * 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) -{ - void *ctx; - u8 cbc[BLOCK_SIZE], tmp[BLOCK_SIZE]; - u8 *pos = data; - int i, j, blocks; - - ctx = aes_decrypt_init(key, 16); - if (ctx == NULL) - return -1; - os_memcpy(cbc, iv, BLOCK_SIZE); - - blocks = data_len / BLOCK_SIZE; - for (i = 0; i < blocks; i++) { - os_memcpy(tmp, pos, BLOCK_SIZE); - aes_decrypt(ctx, pos, pos); - for (j = 0; j < BLOCK_SIZE; j++) - pos[j] ^= cbc[j]; - os_memcpy(cbc, tmp, BLOCK_SIZE); - pos += BLOCK_SIZE; - } - aes_decrypt_deinit(ctx); - return 0; -} - -#endif /* CONFIG_NO_AES_CBC */ diff --git a/contrib/wpa/src/crypto/crypto.h b/contrib/wpa/src/crypto/crypto.h index a5129bb..587b5a9 100644 --- a/contrib/wpa/src/crypto/crypto.h +++ b/contrib/wpa/src/crypto/crypto.h @@ -1,6 +1,6 @@ /* * WPA Supplicant / wrapper functions for crypto libraries - * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -33,8 +33,9 @@ * @addr: Pointers to the data areas * @len: Lengths of the data blocks * @mac: Buffer for the hash + * Returns: 0 on success, -1 on failure */ -void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); +int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); /** * md5_vector - MD5 hash for data vector @@ -42,8 +43,25 @@ void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); * @addr: Pointers to the data areas * @len: Lengths of the data blocks * @mac: Buffer for the hash + * Returns: 0 on success, -1 on failure + */ +int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); + +#ifdef CONFIG_FIPS +/** + * md5_vector_non_fips_allow - MD5 hash for data vector (non-FIPS use allowed) + * @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 + * Returns: 0 on success, -1 on failure */ -void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); +int md5_vector_non_fips_allow(size_t num_elem, const u8 *addr[], + const size_t *len, u8 *mac); +#else /* CONFIG_FIPS */ +#define md5_vector_non_fips_allow md5_vector +#endif /* CONFIG_FIPS */ + /** * sha1_vector - SHA-1 hash for data vector @@ -51,9 +69,10 @@ void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac); * @addr: Pointers to the data areas * @len: Lengths of the data blocks * @mac: Buffer for the hash + * Returns: 0 on success, -1 on failure */ -void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, - u8 *mac); +int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac); /** * fips186_2-prf - NIST FIPS Publication 186-2 change notice 1 PRF @@ -76,9 +95,10 @@ int __must_check fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, * @addr: Pointers to the data areas * @len: Lengths of the data blocks * @mac: Buffer for the hash + * Returns: 0 on success, -1 on failure */ -void sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, - u8 *mac); +int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac); /** * des_encrypt - Encrypt one block with DES @@ -275,6 +295,7 @@ struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len); * crypto_private_key_import - Import an RSA private key * @key: Key buffer (DER encoded RSA private key) * @len: Key buffer length in bytes + * @passwd: Key encryption password or %NULL if key is not encrypted * Returns: Pointer to the private key or %NULL on failure * * This function is only used with internal TLSv1 implementation @@ -282,7 +303,8 @@ struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len); * to implement this. */ struct crypto_private_key * crypto_private_key_import(const u8 *key, - size_t len); + size_t len, + const char *passwd); /** * crypto_public_key_from_cert - Import an RSA public key from a certificate @@ -428,4 +450,20 @@ int __must_check crypto_mod_exp(const u8 *base, size_t base_len, const u8 *modulus, size_t modulus_len, u8 *result, size_t *result_len); +/** + * rc4_skip - 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 + * Returns: 0 on success, -1 on failure + * + * 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. + */ +int rc4_skip(const u8 *key, size_t keylen, size_t skip, + u8 *data, size_t data_len); + #endif /* CRYPTO_H */ diff --git a/contrib/wpa/src/crypto/crypto_cryptoapi.c b/contrib/wpa/src/crypto/crypto_cryptoapi.c index 45333dd..2a8d200 100644 --- a/contrib/wpa/src/crypto/crypto_cryptoapi.c +++ b/contrib/wpa/src/crypto/crypto_cryptoapi.c @@ -33,7 +33,6 @@ L"Microsoft Enhanced RSA and AES Cryptographic Provider (Prototype)" #define CALG_HMAC (ALG_CLASS_HASH | ALG_TYPE_ANY | ALG_SID_HMAC) #endif -#ifdef CONFIG_TLS_INTERNAL #ifdef __MINGW32_VERSION /* * MinGW does not yet include all the needed definitions for CryptoAPI, so @@ -83,7 +82,6 @@ static int mingw_load_crypto_func(void) } #endif /* __MINGW32_VERSION */ -#endif /* CONFIG_TLS_INTERNAL */ static void cryptoapi_report_error(const char *msg) @@ -152,9 +150,9 @@ int cryptoapi_hash_vector(ALG_ID alg, size_t hash_len, size_t num_elem, } -void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { - cryptoapi_hash_vector(CALG_MD4, 16, num_elem, addr, len, mac); + return cryptoapi_hash_vector(CALG_MD4, 16, num_elem, addr, len, mac); } @@ -223,16 +221,15 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) } -#ifdef EAP_TLS_FUNCS -void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { - cryptoapi_hash_vector(CALG_MD5, 16, num_elem, addr, len, mac); + return cryptoapi_hash_vector(CALG_MD5, 16, num_elem, addr, len, mac); } -void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { - cryptoapi_hash_vector(CALG_SHA, 20, num_elem, addr, len, mac); + return cryptoapi_hash_vector(CALG_SHA, 20, num_elem, addr, len, mac); } @@ -349,7 +346,6 @@ void aes_decrypt_deinit(void *ctx) aes_encrypt_deinit(ctx); } -#ifdef CONFIG_TLS_INTERNAL struct crypto_hash { enum crypto_hash_alg alg; @@ -657,7 +653,8 @@ struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len) struct crypto_private_key * crypto_private_key_import(const u8 *key, - size_t len) + size_t len, + const char *passwd) { /* TODO */ return NULL; @@ -781,6 +778,12 @@ void crypto_global_deinit(void) { } -#endif /* CONFIG_TLS_INTERNAL */ -#endif /* EAP_TLS_FUNCS */ +int crypto_mod_exp(const u8 *base, size_t base_len, + const u8 *power, size_t power_len, + const u8 *modulus, size_t modulus_len, + u8 *result, size_t *result_len) +{ + /* TODO */ + return -1; +} diff --git a/contrib/wpa/src/crypto/crypto_gnutls.c b/contrib/wpa/src/crypto/crypto_gnutls.c index 8f8611c..0998cca 100644 --- a/contrib/wpa/src/crypto/crypto_gnutls.c +++ b/contrib/wpa/src/crypto/crypto_gnutls.c @@ -18,20 +18,21 @@ #include "common.h" #include "crypto.h" -void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { gcry_md_hd_t hd; unsigned char *p; size_t i; if (gcry_md_open(&hd, GCRY_MD_MD4, 0) != GPG_ERR_NO_ERROR) - return; + return -1; for (i = 0; i < num_elem; i++) gcry_md_write(hd, addr[i], len[i]); p = gcry_md_read(hd, GCRY_MD_MD4); if (p) memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_MD4)); gcry_md_close(hd); + return 0; } @@ -57,49 +58,42 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) } -void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { gcry_md_hd_t hd; unsigned char *p; size_t i; if (gcry_md_open(&hd, GCRY_MD_MD5, 0) != GPG_ERR_NO_ERROR) - return; + return -1; for (i = 0; i < num_elem; i++) gcry_md_write(hd, addr[i], len[i]); p = gcry_md_read(hd, GCRY_MD_MD5); if (p) memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_MD5)); gcry_md_close(hd); + return 0; } -void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { gcry_md_hd_t hd; unsigned char *p; size_t i; if (gcry_md_open(&hd, GCRY_MD_SHA1, 0) != GPG_ERR_NO_ERROR) - return; + return -1; for (i = 0; i < num_elem; i++) gcry_md_write(hd, addr[i], len[i]); p = gcry_md_read(hd, GCRY_MD_SHA1); if (p) memcpy(mac, p, gcry_md_get_algo_dlen(GCRY_MD_SHA1)); gcry_md_close(hd); + return 0; } -#ifndef CONFIG_NO_FIPS186_2_PRF -int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) -{ - /* FIX: how to do this with libgcrypt? */ - return -1; -} -#endif /* CONFIG_NO_FIPS186_2_PRF */ - - void * aes_encrypt_init(const u8 *key, size_t len) { gcry_cipher_hd_t hd; diff --git a/contrib/wpa/src/crypto/crypto_internal-cipher.c b/contrib/wpa/src/crypto/crypto_internal-cipher.c new file mode 100644 index 0000000..75134f0 --- /dev/null +++ b/contrib/wpa/src/crypto/crypto_internal-cipher.c @@ -0,0 +1,256 @@ +/* + * Crypto wrapper for internal crypto implementation - Cipher wrappers + * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto.h" +#include "aes.h" +#include "des_i.h" + + +struct crypto_cipher { + enum crypto_cipher_alg alg; + union { + struct { + size_t used_bytes; + u8 key[16]; + size_t keylen; + } rc4; + struct { + u8 cbc[32]; + size_t block_size; + void *ctx_enc; + void *ctx_dec; + } aes; + struct { + struct des3_key_s key; + u8 cbc[8]; + } des3; + struct { + u32 ek[32]; + u32 dk[32]; + u8 cbc[8]; + } des; + } u; +}; + + +struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, + const u8 *iv, const u8 *key, + size_t key_len) +{ + struct crypto_cipher *ctx; + + ctx = os_zalloc(sizeof(*ctx)); + if (ctx == NULL) + return NULL; + + ctx->alg = alg; + + switch (alg) { + case CRYPTO_CIPHER_ALG_RC4: + if (key_len > sizeof(ctx->u.rc4.key)) { + os_free(ctx); + return NULL; + } + ctx->u.rc4.keylen = key_len; + os_memcpy(ctx->u.rc4.key, key, key_len); + break; + case CRYPTO_CIPHER_ALG_AES: + if (key_len > sizeof(ctx->u.aes.cbc)) { + os_free(ctx); + return NULL; + } + ctx->u.aes.ctx_enc = aes_encrypt_init(key, key_len); + if (ctx->u.aes.ctx_enc == NULL) { + os_free(ctx); + return NULL; + } + ctx->u.aes.ctx_dec = aes_decrypt_init(key, key_len); + if (ctx->u.aes.ctx_dec == NULL) { + aes_encrypt_deinit(ctx->u.aes.ctx_enc); + os_free(ctx); + return NULL; + } + ctx->u.aes.block_size = key_len; + os_memcpy(ctx->u.aes.cbc, iv, ctx->u.aes.block_size); + break; + case CRYPTO_CIPHER_ALG_3DES: + if (key_len != 24) { + os_free(ctx); + return NULL; + } + des3_key_setup(key, &ctx->u.des3.key); + os_memcpy(ctx->u.des3.cbc, iv, 8); + break; + case CRYPTO_CIPHER_ALG_DES: + if (key_len != 8) { + os_free(ctx); + return NULL; + } + des_key_setup(key, ctx->u.des.ek, ctx->u.des.dk); + os_memcpy(ctx->u.des.cbc, iv, 8); + break; + default: + os_free(ctx); + return NULL; + } + + return ctx; +} + + +int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, + u8 *crypt, size_t len) +{ + size_t i, j, blocks; + + switch (ctx->alg) { + case CRYPTO_CIPHER_ALG_RC4: + if (plain != crypt) + os_memcpy(crypt, plain, len); + rc4_skip(ctx->u.rc4.key, ctx->u.rc4.keylen, + ctx->u.rc4.used_bytes, crypt, len); + ctx->u.rc4.used_bytes += len; + break; + case CRYPTO_CIPHER_ALG_AES: + if (len % ctx->u.aes.block_size) + return -1; + blocks = len / ctx->u.aes.block_size; + for (i = 0; i < blocks; i++) { + for (j = 0; j < ctx->u.aes.block_size; j++) + ctx->u.aes.cbc[j] ^= plain[j]; + aes_encrypt(ctx->u.aes.ctx_enc, ctx->u.aes.cbc, + ctx->u.aes.cbc); + os_memcpy(crypt, ctx->u.aes.cbc, + ctx->u.aes.block_size); + plain += ctx->u.aes.block_size; + crypt += ctx->u.aes.block_size; + } + break; + case CRYPTO_CIPHER_ALG_3DES: + if (len % 8) + return -1; + blocks = len / 8; + for (i = 0; i < blocks; i++) { + for (j = 0; j < 8; j++) + ctx->u.des3.cbc[j] ^= plain[j]; + des3_encrypt(ctx->u.des3.cbc, &ctx->u.des3.key, + ctx->u.des3.cbc); + os_memcpy(crypt, ctx->u.des3.cbc, 8); + plain += 8; + crypt += 8; + } + break; + case CRYPTO_CIPHER_ALG_DES: + if (len % 8) + return -1; + blocks = len / 8; + for (i = 0; i < blocks; i++) { + for (j = 0; j < 8; j++) + ctx->u.des3.cbc[j] ^= plain[j]; + des_block_encrypt(ctx->u.des.cbc, ctx->u.des.ek, + ctx->u.des.cbc); + os_memcpy(crypt, ctx->u.des.cbc, 8); + plain += 8; + crypt += 8; + } + break; + default: + return -1; + } + + return 0; +} + + +int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, + u8 *plain, size_t len) +{ + size_t i, j, blocks; + u8 tmp[32]; + + switch (ctx->alg) { + case CRYPTO_CIPHER_ALG_RC4: + if (plain != crypt) + os_memcpy(plain, crypt, len); + rc4_skip(ctx->u.rc4.key, ctx->u.rc4.keylen, + ctx->u.rc4.used_bytes, plain, len); + ctx->u.rc4.used_bytes += len; + break; + case CRYPTO_CIPHER_ALG_AES: + if (len % ctx->u.aes.block_size) + return -1; + blocks = len / ctx->u.aes.block_size; + for (i = 0; i < blocks; i++) { + os_memcpy(tmp, crypt, ctx->u.aes.block_size); + aes_decrypt(ctx->u.aes.ctx_dec, crypt, plain); + for (j = 0; j < ctx->u.aes.block_size; j++) + plain[j] ^= ctx->u.aes.cbc[j]; + os_memcpy(ctx->u.aes.cbc, tmp, ctx->u.aes.block_size); + plain += ctx->u.aes.block_size; + crypt += ctx->u.aes.block_size; + } + break; + case CRYPTO_CIPHER_ALG_3DES: + if (len % 8) + return -1; + blocks = len / 8; + for (i = 0; i < blocks; i++) { + os_memcpy(tmp, crypt, 8); + des3_decrypt(crypt, &ctx->u.des3.key, plain); + for (j = 0; j < 8; j++) + plain[j] ^= ctx->u.des3.cbc[j]; + os_memcpy(ctx->u.des3.cbc, tmp, 8); + plain += 8; + crypt += 8; + } + break; + case CRYPTO_CIPHER_ALG_DES: + if (len % 8) + return -1; + blocks = len / 8; + for (i = 0; i < blocks; i++) { + os_memcpy(tmp, crypt, 8); + des_block_decrypt(crypt, ctx->u.des.dk, plain); + for (j = 0; j < 8; j++) + plain[j] ^= ctx->u.des.cbc[j]; + os_memcpy(ctx->u.des.cbc, tmp, 8); + plain += 8; + crypt += 8; + } + break; + default: + return -1; + } + + return 0; +} + + +void crypto_cipher_deinit(struct crypto_cipher *ctx) +{ + switch (ctx->alg) { + case CRYPTO_CIPHER_ALG_AES: + aes_encrypt_deinit(ctx->u.aes.ctx_enc); + aes_decrypt_deinit(ctx->u.aes.ctx_dec); + break; + case CRYPTO_CIPHER_ALG_3DES: + break; + default: + break; + } + os_free(ctx); +} diff --git a/contrib/wpa/src/crypto/crypto_internal-modexp.c b/contrib/wpa/src/crypto/crypto_internal-modexp.c new file mode 100644 index 0000000..3124742 --- /dev/null +++ b/contrib/wpa/src/crypto/crypto_internal-modexp.c @@ -0,0 +1,55 @@ +/* + * Crypto wrapper for internal crypto implementation - modexp + * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "tls/bignum.h" +#include "crypto.h" + + +int crypto_mod_exp(const u8 *base, size_t base_len, + const u8 *power, size_t power_len, + const u8 *modulus, size_t modulus_len, + u8 *result, size_t *result_len) +{ + struct bignum *bn_base, *bn_exp, *bn_modulus, *bn_result; + int ret = -1; + + bn_base = bignum_init(); + bn_exp = bignum_init(); + bn_modulus = bignum_init(); + bn_result = bignum_init(); + + if (bn_base == NULL || bn_exp == NULL || bn_modulus == NULL || + bn_result == NULL) + goto error; + + if (bignum_set_unsigned_bin(bn_base, base, base_len) < 0 || + bignum_set_unsigned_bin(bn_exp, power, power_len) < 0 || + bignum_set_unsigned_bin(bn_modulus, modulus, modulus_len) < 0) + goto error; + + if (bignum_exptmod(bn_base, bn_exp, bn_modulus, bn_result) < 0) + goto error; + + ret = bignum_get_unsigned_bin(bn_result, result, result_len); + +error: + bignum_deinit(bn_base); + bignum_deinit(bn_exp); + bignum_deinit(bn_modulus); + bignum_deinit(bn_result); + return ret; +} diff --git a/contrib/wpa/src/crypto/crypto_internal-rsa.c b/contrib/wpa/src/crypto/crypto_internal-rsa.c new file mode 100644 index 0000000..205042c --- /dev/null +++ b/contrib/wpa/src/crypto/crypto_internal-rsa.c @@ -0,0 +1,115 @@ +/* + * Crypto wrapper for internal crypto implementation - RSA parts + * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto.h" +#include "tls/rsa.h" +#include "tls/bignum.h" +#include "tls/pkcs1.h" +#include "tls/pkcs8.h" + +/* Dummy structures; these are just typecast to struct crypto_rsa_key */ +struct crypto_public_key; +struct crypto_private_key; + + +struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len) +{ + return (struct crypto_public_key *) + crypto_rsa_import_public_key(key, len); +} + + +struct crypto_private_key * crypto_private_key_import(const u8 *key, + size_t len, + const char *passwd) +{ + struct crypto_private_key *res; + + /* First, check for possible PKCS #8 encoding */ + res = pkcs8_key_import(key, len); + if (res) + return res; + + if (passwd) { + /* Try to parse as encrypted PKCS #8 */ + res = pkcs8_enc_key_import(key, len, passwd); + if (res) + return res; + } + + /* Not PKCS#8, so try to import PKCS #1 encoded RSA private key */ + wpa_printf(MSG_DEBUG, "Trying to parse PKCS #1 encoded RSA private " + "key"); + return (struct crypto_private_key *) + crypto_rsa_import_private_key(key, len); +} + + +struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf, + size_t len) +{ + /* No X.509 support in crypto_internal.c */ + return NULL; +} + + +int crypto_public_key_encrypt_pkcs1_v15(struct crypto_public_key *key, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen) +{ + return pkcs1_encrypt(2, (struct crypto_rsa_key *) key, + 0, in, inlen, out, outlen); +} + + +int crypto_private_key_decrypt_pkcs1_v15(struct crypto_private_key *key, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen) +{ + return pkcs1_v15_private_key_decrypt((struct crypto_rsa_key *) key, + in, inlen, out, outlen); +} + + +int crypto_private_key_sign_pkcs1(struct crypto_private_key *key, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen) +{ + return pkcs1_encrypt(1, (struct crypto_rsa_key *) key, + 1, in, inlen, out, outlen); +} + + +void crypto_public_key_free(struct crypto_public_key *key) +{ + crypto_rsa_free((struct crypto_rsa_key *) key); +} + + +void crypto_private_key_free(struct crypto_private_key *key) +{ + crypto_rsa_free((struct crypto_rsa_key *) key); +} + + +int crypto_public_key_decrypt_pkcs1(struct crypto_public_key *key, + const u8 *crypt, size_t crypt_len, + u8 *plain, size_t *plain_len) +{ + return pkcs1_decrypt_public_key((struct crypto_rsa_key *) key, + crypt, crypt_len, plain, plain_len); +} diff --git a/contrib/wpa/src/crypto/crypto_internal.c b/contrib/wpa/src/crypto/crypto_internal.c index cddfb4d..8fdba65 100644 --- a/contrib/wpa/src/crypto/crypto_internal.c +++ b/contrib/wpa/src/crypto/crypto_internal.c @@ -1,6 +1,6 @@ /* - * WPA Supplicant / Crypto wrapper for internal crypto implementation - * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * Crypto wrapper for internal crypto implementation + * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -16,42 +16,8 @@ #include "common.h" #include "crypto.h" -#include "md5.h" -#include "sha1.h" -#include "rc4.h" -#include "aes.h" -#include "tls/rsa.h" -#include "tls/bignum.h" -#include "tls/asn1.h" - - -#ifdef CONFIG_CRYPTO_INTERNAL - -#ifdef CONFIG_TLS_INTERNAL - -/* from des.c */ -struct des3_key_s { - u32 ek[3][32]; - u32 dk[3][32]; -}; - -void des3_key_setup(const u8 *key, struct des3_key_s *dkey); -void des3_encrypt(const u8 *plain, const struct des3_key_s *key, u8 *crypt); -void des3_decrypt(const u8 *crypt, const struct des3_key_s *key, u8 *plain); - - -struct MD5Context { - u32 buf[4]; - u32 bits[2]; - u8 in[64]; -}; - -struct SHA1Context { - u32 state[5]; - u32 count[2]; - unsigned char buffer[64]; -}; - +#include "sha1_i.h" +#include "md5_i.h" struct crypto_hash { enum crypto_hash_alg alg; @@ -228,559 +194,6 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len) } -struct crypto_cipher { - enum crypto_cipher_alg alg; - union { - struct { - size_t used_bytes; - u8 key[16]; - size_t keylen; - } rc4; - struct { - u8 cbc[32]; - size_t block_size; - void *ctx_enc; - void *ctx_dec; - } aes; - struct { - struct des3_key_s key; - u8 cbc[8]; - } des3; - } u; -}; - - -struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, - const u8 *iv, const u8 *key, - size_t key_len) -{ - struct crypto_cipher *ctx; - - ctx = os_zalloc(sizeof(*ctx)); - if (ctx == NULL) - return NULL; - - ctx->alg = alg; - - switch (alg) { - case CRYPTO_CIPHER_ALG_RC4: - if (key_len > sizeof(ctx->u.rc4.key)) { - os_free(ctx); - return NULL; - } - ctx->u.rc4.keylen = key_len; - os_memcpy(ctx->u.rc4.key, key, key_len); - break; - case CRYPTO_CIPHER_ALG_AES: - if (key_len > sizeof(ctx->u.aes.cbc)) { - os_free(ctx); - return NULL; - } - ctx->u.aes.ctx_enc = aes_encrypt_init(key, key_len); - if (ctx->u.aes.ctx_enc == NULL) { - os_free(ctx); - return NULL; - } - ctx->u.aes.ctx_dec = aes_decrypt_init(key, key_len); - if (ctx->u.aes.ctx_dec == NULL) { - aes_encrypt_deinit(ctx->u.aes.ctx_enc); - os_free(ctx); - return NULL; - } - ctx->u.aes.block_size = key_len; - os_memcpy(ctx->u.aes.cbc, iv, ctx->u.aes.block_size); - break; - case CRYPTO_CIPHER_ALG_3DES: - if (key_len != 24) { - os_free(ctx); - return NULL; - } - des3_key_setup(key, &ctx->u.des3.key); - os_memcpy(ctx->u.des3.cbc, iv, 8); - break; - default: - os_free(ctx); - return NULL; - } - - return ctx; -} - - -int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, - u8 *crypt, size_t len) -{ - size_t i, j, blocks; - - switch (ctx->alg) { - case CRYPTO_CIPHER_ALG_RC4: - if (plain != crypt) - os_memcpy(crypt, plain, len); - rc4_skip(ctx->u.rc4.key, ctx->u.rc4.keylen, - ctx->u.rc4.used_bytes, crypt, len); - ctx->u.rc4.used_bytes += len; - break; - case CRYPTO_CIPHER_ALG_AES: - if (len % ctx->u.aes.block_size) - return -1; - blocks = len / ctx->u.aes.block_size; - for (i = 0; i < blocks; i++) { - for (j = 0; j < ctx->u.aes.block_size; j++) - ctx->u.aes.cbc[j] ^= plain[j]; - aes_encrypt(ctx->u.aes.ctx_enc, ctx->u.aes.cbc, - ctx->u.aes.cbc); - os_memcpy(crypt, ctx->u.aes.cbc, - ctx->u.aes.block_size); - plain += ctx->u.aes.block_size; - crypt += ctx->u.aes.block_size; - } - break; - case CRYPTO_CIPHER_ALG_3DES: - if (len % 8) - return -1; - blocks = len / 8; - for (i = 0; i < blocks; i++) { - for (j = 0; j < 8; j++) - ctx->u.des3.cbc[j] ^= plain[j]; - des3_encrypt(ctx->u.des3.cbc, &ctx->u.des3.key, - ctx->u.des3.cbc); - os_memcpy(crypt, ctx->u.des3.cbc, 8); - plain += 8; - crypt += 8; - } - break; - default: - return -1; - } - - return 0; -} - - -int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, - u8 *plain, size_t len) -{ - size_t i, j, blocks; - u8 tmp[32]; - - switch (ctx->alg) { - case CRYPTO_CIPHER_ALG_RC4: - if (plain != crypt) - os_memcpy(plain, crypt, len); - rc4_skip(ctx->u.rc4.key, ctx->u.rc4.keylen, - ctx->u.rc4.used_bytes, plain, len); - ctx->u.rc4.used_bytes += len; - break; - case CRYPTO_CIPHER_ALG_AES: - if (len % ctx->u.aes.block_size) - return -1; - blocks = len / ctx->u.aes.block_size; - for (i = 0; i < blocks; i++) { - os_memcpy(tmp, crypt, ctx->u.aes.block_size); - aes_decrypt(ctx->u.aes.ctx_dec, crypt, plain); - for (j = 0; j < ctx->u.aes.block_size; j++) - plain[j] ^= ctx->u.aes.cbc[j]; - os_memcpy(ctx->u.aes.cbc, tmp, ctx->u.aes.block_size); - plain += ctx->u.aes.block_size; - crypt += ctx->u.aes.block_size; - } - break; - case CRYPTO_CIPHER_ALG_3DES: - if (len % 8) - return -1; - blocks = len / 8; - for (i = 0; i < blocks; i++) { - os_memcpy(tmp, crypt, 8); - des3_decrypt(crypt, &ctx->u.des3.key, plain); - for (j = 0; j < 8; j++) - plain[j] ^= ctx->u.des3.cbc[j]; - os_memcpy(ctx->u.des3.cbc, tmp, 8); - plain += 8; - crypt += 8; - } - break; - default: - return -1; - } - - return 0; -} - - -void crypto_cipher_deinit(struct crypto_cipher *ctx) -{ - switch (ctx->alg) { - case CRYPTO_CIPHER_ALG_AES: - aes_encrypt_deinit(ctx->u.aes.ctx_enc); - aes_decrypt_deinit(ctx->u.aes.ctx_dec); - break; - case CRYPTO_CIPHER_ALG_3DES: - break; - default: - break; - } - os_free(ctx); -} - - -/* Dummy structures; these are just typecast to struct crypto_rsa_key */ -struct crypto_public_key; -struct crypto_private_key; - - -struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len) -{ - return (struct crypto_public_key *) - crypto_rsa_import_public_key(key, len); -} - - -#ifdef EAP_TLS_FUNCS -static struct crypto_private_key * -crypto_pkcs8_key_import(const u8 *buf, size_t len) -{ - struct asn1_hdr hdr; - const u8 *pos, *end; - struct bignum *zero; - struct asn1_oid oid; - char obuf[80]; - - /* PKCS #8, Chapter 6 */ - - /* PrivateKeyInfo ::= SEQUENCE */ - if (asn1_get_next(buf, len, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_SEQUENCE) { - wpa_printf(MSG_DEBUG, "PKCS #8: Does not start with PKCS #8 " - "header (SEQUENCE); assume PKCS #8 not used"); - return NULL; - } - pos = hdr.payload; - end = pos + hdr.length; - - /* version Version (Version ::= INTEGER) */ - if (asn1_get_next(pos, end - pos, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) { - wpa_printf(MSG_DEBUG, "PKCS #8: Expected INTEGER - found " - "class %d tag 0x%x; assume PKCS #8 not used", - hdr.class, hdr.tag); - return NULL; - } - - zero = bignum_init(); - if (zero == NULL) - return NULL; - - if (bignum_set_unsigned_bin(zero, hdr.payload, hdr.length) < 0) { - wpa_printf(MSG_DEBUG, "PKCS #8: Failed to parse INTEGER"); - bignum_deinit(zero); - return NULL; - } - pos = hdr.payload + hdr.length; - - if (bignum_cmp_d(zero, 0) != 0) { - wpa_printf(MSG_DEBUG, "PKCS #8: Expected zero INTEGER in the " - "beginning of private key; not found; assume " - "PKCS #8 not used"); - bignum_deinit(zero); - return NULL; - } - bignum_deinit(zero); - - /* privateKeyAlgorithm PrivateKeyAlgorithmIdentifier - * (PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier) */ - if (asn1_get_next(pos, len, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_SEQUENCE) { - wpa_printf(MSG_DEBUG, "PKCS #8: Expected SEQUENCE " - "(AlgorithmIdentifier) - found class %d tag 0x%x; " - "assume PKCS #8 not used", - hdr.class, hdr.tag); - return NULL; - } - - if (asn1_get_oid(hdr.payload, hdr.length, &oid, &pos)) { - wpa_printf(MSG_DEBUG, "PKCS #8: Failed to parse OID " - "(algorithm); assume PKCS #8 not used"); - return NULL; - } - - asn1_oid_to_str(&oid, obuf, sizeof(obuf)); - wpa_printf(MSG_DEBUG, "PKCS #8: algorithm=%s", obuf); - - if (oid.len != 7 || - oid.oid[0] != 1 /* iso */ || - oid.oid[1] != 2 /* member-body */ || - oid.oid[2] != 840 /* us */ || - oid.oid[3] != 113549 /* rsadsi */ || - oid.oid[4] != 1 /* pkcs */ || - oid.oid[5] != 1 /* pkcs-1 */ || - oid.oid[6] != 1 /* rsaEncryption */) { - wpa_printf(MSG_DEBUG, "PKCS #8: Unsupported private key " - "algorithm %s", obuf); - return NULL; - } - - pos = hdr.payload + hdr.length; - - /* privateKey PrivateKey (PrivateKey ::= OCTET STRING) */ - if (asn1_get_next(pos, end - pos, &hdr) < 0 || - hdr.class != ASN1_CLASS_UNIVERSAL || - hdr.tag != ASN1_TAG_OCTETSTRING) { - wpa_printf(MSG_DEBUG, "PKCS #8: Expected OCTETSTRING " - "(privateKey) - found class %d tag 0x%x", - hdr.class, hdr.tag); - return NULL; - } - wpa_printf(MSG_DEBUG, "PKCS #8: Try to parse RSAPrivateKey"); - - return (struct crypto_private_key *) - crypto_rsa_import_private_key(hdr.payload, hdr.length); -} -#endif /* EAP_TLS_FUNCS */ - - -struct crypto_private_key * crypto_private_key_import(const u8 *key, - size_t len) -{ - struct crypto_private_key *res; - - /* First, check for possible PKCS #8 encoding */ - res = crypto_pkcs8_key_import(key, len); - if (res) - return res; - - /* Not PKCS#8, so try to import PKCS #1 encoded RSA private key */ - wpa_printf(MSG_DEBUG, "Trying to parse PKCS #1 encoded RSA private " - "key"); - return (struct crypto_private_key *) - crypto_rsa_import_private_key(key, len); -} - - -struct crypto_public_key * crypto_public_key_from_cert(const u8 *buf, - size_t len) -{ - /* No X.509 support in crypto_internal.c */ - return NULL; -} - - -static int pkcs1_generate_encryption_block(u8 block_type, size_t modlen, - const u8 *in, size_t inlen, - u8 *out, size_t *outlen) -{ - size_t ps_len; - u8 *pos; - - /* - * PKCS #1 v1.5, 8.1: - * - * EB = 00 || BT || PS || 00 || D - * BT = 00 or 01 for private-key operation; 02 for public-key operation - * PS = k-3-||D||; at least eight octets - * (BT=0: PS=0x00, BT=1: PS=0xff, BT=2: PS=pseudorandom non-zero) - * k = length of modulus in octets (modlen) - */ - - if (modlen < 12 || modlen > *outlen || inlen > modlen - 11) { - wpa_printf(MSG_DEBUG, "PKCS #1: %s - Invalid buffer " - "lengths (modlen=%lu outlen=%lu inlen=%lu)", - __func__, (unsigned long) modlen, - (unsigned long) *outlen, - (unsigned long) inlen); - return -1; - } - - pos = out; - *pos++ = 0x00; - *pos++ = block_type; /* BT */ - ps_len = modlen - inlen - 3; - switch (block_type) { - case 0: - os_memset(pos, 0x00, ps_len); - pos += ps_len; - break; - case 1: - os_memset(pos, 0xff, ps_len); - pos += ps_len; - break; - case 2: - if (os_get_random(pos, ps_len) < 0) { - wpa_printf(MSG_DEBUG, "PKCS #1: %s - Failed to get " - "random data for PS", __func__); - return -1; - } - while (ps_len--) { - if (*pos == 0x00) - *pos = 0x01; - pos++; - } - break; - default: - wpa_printf(MSG_DEBUG, "PKCS #1: %s - Unsupported block type " - "%d", __func__, block_type); - return -1; - } - *pos++ = 0x00; - os_memcpy(pos, in, inlen); /* D */ - - return 0; -} - - -static int crypto_rsa_encrypt_pkcs1(int block_type, struct crypto_rsa_key *key, - int use_private, - const u8 *in, size_t inlen, - u8 *out, size_t *outlen) -{ - size_t modlen; - - modlen = crypto_rsa_get_modulus_len(key); - - if (pkcs1_generate_encryption_block(block_type, modlen, in, inlen, - out, outlen) < 0) - return -1; - - return crypto_rsa_exptmod(out, modlen, out, outlen, key, use_private); -} - - -int crypto_public_key_encrypt_pkcs1_v15(struct crypto_public_key *key, - const u8 *in, size_t inlen, - u8 *out, size_t *outlen) -{ - return crypto_rsa_encrypt_pkcs1(2, (struct crypto_rsa_key *) key, - 0, in, inlen, out, outlen); -} - - -int crypto_private_key_decrypt_pkcs1_v15(struct crypto_private_key *key, - const u8 *in, size_t inlen, - u8 *out, size_t *outlen) -{ - struct crypto_rsa_key *rkey = (struct crypto_rsa_key *) key; - int res; - u8 *pos, *end; - - res = crypto_rsa_exptmod(in, inlen, out, outlen, rkey, 1); - if (res) - return res; - - if (*outlen < 2 || out[0] != 0 || out[1] != 2) - return -1; - - /* Skip PS (pseudorandom non-zero octets) */ - pos = out + 2; - end = out + *outlen; - while (*pos && pos < end) - pos++; - if (pos == end) - return -1; - pos++; - - *outlen -= pos - out; - - /* Strip PKCS #1 header */ - os_memmove(out, pos, *outlen); - - return 0; -} - - -int crypto_private_key_sign_pkcs1(struct crypto_private_key *key, - const u8 *in, size_t inlen, - u8 *out, size_t *outlen) -{ - return crypto_rsa_encrypt_pkcs1(1, (struct crypto_rsa_key *) key, - 1, in, inlen, out, outlen); -} - - -void crypto_public_key_free(struct crypto_public_key *key) -{ - crypto_rsa_free((struct crypto_rsa_key *) key); -} - - -void crypto_private_key_free(struct crypto_private_key *key) -{ - crypto_rsa_free((struct crypto_rsa_key *) key); -} - - -int crypto_public_key_decrypt_pkcs1(struct crypto_public_key *key, - const u8 *crypt, size_t crypt_len, - u8 *plain, size_t *plain_len) -{ - size_t len; - u8 *pos; - - len = *plain_len; - if (crypto_rsa_exptmod(crypt, crypt_len, plain, &len, - (struct crypto_rsa_key *) key, 0) < 0) - return -1; - - /* - * PKCS #1 v1.5, 8.1: - * - * EB = 00 || BT || PS || 00 || D - * BT = 00 or 01 - * PS = k-3-||D|| times (00 if BT=00) or (FF if BT=01) - * k = length of modulus in octets - */ - - if (len < 3 + 8 + 16 /* min hash len */ || - plain[0] != 0x00 || (plain[1] != 0x00 && plain[1] != 0x01)) { - wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB " - "structure"); - return -1; - } - - pos = plain + 3; - if (plain[1] == 0x00) { - /* BT = 00 */ - if (plain[2] != 0x00) { - wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature " - "PS (BT=00)"); - return -1; - } - while (pos + 1 < plain + len && *pos == 0x00 && pos[1] == 0x00) - pos++; - } else { - /* BT = 01 */ - if (plain[2] != 0xff) { - wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature " - "PS (BT=01)"); - return -1; - } - while (pos < plain + len && *pos == 0xff) - pos++; - } - - if (pos - plain - 2 < 8) { - /* PKCS #1 v1.5, 8.1: At least eight octets long PS */ - wpa_printf(MSG_INFO, "LibTomCrypt: Too short signature " - "padding"); - return -1; - } - - if (pos + 16 /* min hash len */ >= plain + len || *pos != 0x00) { - wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB " - "structure (2)"); - return -1; - } - pos++; - len -= pos - plain; - - /* Strip PKCS #1 header */ - os_memmove(plain, pos, len); - *plain_len = len; - - return 0; -} - - int crypto_global_init(void) { return 0; @@ -790,47 +203,3 @@ int crypto_global_init(void) void crypto_global_deinit(void) { } -#endif /* CONFIG_TLS_INTERNAL */ - - -#if defined(EAP_FAST) || defined(CONFIG_WPS) - -int crypto_mod_exp(const u8 *base, size_t base_len, - const u8 *power, size_t power_len, - const u8 *modulus, size_t modulus_len, - u8 *result, size_t *result_len) -{ - struct bignum *bn_base, *bn_exp, *bn_modulus, *bn_result; - int ret = -1; - - bn_base = bignum_init(); - bn_exp = bignum_init(); - bn_modulus = bignum_init(); - bn_result = bignum_init(); - - if (bn_base == NULL || bn_exp == NULL || bn_modulus == NULL || - bn_result == NULL) - goto error; - - if (bignum_set_unsigned_bin(bn_base, base, base_len) < 0 || - bignum_set_unsigned_bin(bn_exp, power, power_len) < 0 || - bignum_set_unsigned_bin(bn_modulus, modulus, modulus_len) < 0) - goto error; - - if (bignum_exptmod(bn_base, bn_exp, bn_modulus, bn_result) < 0) - goto error; - - ret = bignum_get_unsigned_bin(bn_result, result, result_len); - -error: - bignum_deinit(bn_base); - bignum_deinit(bn_exp); - bignum_deinit(bn_modulus); - bignum_deinit(bn_result); - return ret; -} - -#endif /* EAP_FAST || CONFIG_WPS */ - - -#endif /* CONFIG_CRYPTO_INTERNAL */ diff --git a/contrib/wpa/src/crypto/crypto_libtomcrypt.c b/contrib/wpa/src/crypto/crypto_libtomcrypt.c index e82097f..52b67a7 100644 --- a/contrib/wpa/src/crypto/crypto_libtomcrypt.c +++ b/contrib/wpa/src/crypto/crypto_libtomcrypt.c @@ -16,7 +16,6 @@ #include <tomcrypt.h> #include "common.h" -#include "rc4.h" #include "crypto.h" #ifndef mp_init_multi @@ -29,7 +28,7 @@ #endif -void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { hash_state md; size_t i; @@ -38,6 +37,7 @@ void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) for (i = 0; i < num_elem; i++) md4_process(&md, addr[i], len[i]); md4_done(&md, mac); + return 0; } @@ -62,8 +62,7 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) } -#ifdef EAP_TLS_FUNCS -void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { hash_state md; size_t i; @@ -72,10 +71,11 @@ void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) for (i = 0; i < num_elem; i++) md5_process(&md, addr[i], len[i]); md5_done(&md, mac); + return 0; } -void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { hash_state md; size_t i; @@ -84,6 +84,7 @@ void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) for (i = 0; i < num_elem; i++) sha1_process(&md, addr[i], len[i]); sha1_done(&md, mac); + return 0; } @@ -145,8 +146,6 @@ void aes_decrypt_deinit(void *ctx) } -#ifdef CONFIG_TLS_INTERNAL - struct crypto_hash { enum crypto_hash_alg alg; int error; @@ -451,7 +450,8 @@ struct crypto_public_key * crypto_public_key_import(const u8 *key, size_t len) struct crypto_private_key * crypto_private_key_import(const u8 *key, - size_t len) + size_t len, + const char *passwd) { int res; struct crypto_private_key *pk; @@ -697,7 +697,7 @@ void crypto_global_deinit(void) } -#ifdef EAP_FAST +#ifdef CONFIG_MODEXP int crypto_mod_exp(const u8 *base, size_t base_len, const u8 *power, size_t power_len, @@ -729,8 +729,4 @@ fail: return -1; } -#endif /* EAP_FAST */ - -#endif /* CONFIG_TLS_INTERNAL */ - -#endif /* EAP_TLS_FUNCS */ +#endif /* CONFIG_MODEXP */ diff --git a/contrib/wpa/src/crypto/crypto_none.c b/contrib/wpa/src/crypto/crypto_none.c index f18c2a8..9f43775 100644 --- a/contrib/wpa/src/crypto/crypto_none.c +++ b/contrib/wpa/src/crypto/crypto_none.c @@ -18,8 +18,9 @@ #include "crypto.h" -void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { + return 0; } diff --git a/contrib/wpa/src/crypto/crypto_nss.c b/contrib/wpa/src/crypto/crypto_nss.c new file mode 100644 index 0000000..fee4195 --- /dev/null +++ b/contrib/wpa/src/crypto/crypto_nss.c @@ -0,0 +1,213 @@ +/* + * Crypto wrapper functions for NSS + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <nspr/prtypes.h> +#include <nspr/plarenas.h> +#include <nspr/plhash.h> +#include <nspr/prtime.h> +#include <nspr/prinrval.h> +#include <nspr/prclist.h> +#include <nspr/prlock.h> +#include <nss/sechash.h> +#include <nss/pk11pub.h> + +#include "common.h" +#include "crypto.h" + + +static int nss_hash(HASH_HashType type, unsigned int max_res_len, + size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + HASHContext *ctx; + size_t i; + unsigned int reslen; + + ctx = HASH_Create(type); + if (ctx == NULL) + return -1; + + HASH_Begin(ctx); + for (i = 0; i < num_elem; i++) + HASH_Update(ctx, addr[i], len[i]); + HASH_End(ctx, mac, &reslen, max_res_len); + HASH_Destroy(ctx); + + return 0; +} + + +void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) +{ + PK11Context *ctx = NULL; + PK11SlotInfo *slot; + SECItem *param = NULL; + PK11SymKey *symkey = NULL; + SECItem item; + int olen; + u8 pkey[8], next, tmp; + int i; + + /* Add parity bits to the key */ + next = 0; + for (i = 0; i < 7; i++) { + tmp = key[i]; + pkey[i] = (tmp >> i) | next | 1; + next = tmp << (7 - i); + } + pkey[i] = next | 1; + + slot = PK11_GetBestSlot(CKM_DES_ECB, NULL); + if (slot == NULL) { + wpa_printf(MSG_ERROR, "NSS: PK11_GetBestSlot failed"); + goto out; + } + + item.type = siBuffer; + item.data = pkey; + item.len = 8; + symkey = PK11_ImportSymKey(slot, CKM_DES_ECB, PK11_OriginDerive, + CKA_ENCRYPT, &item, NULL); + if (symkey == NULL) { + wpa_printf(MSG_ERROR, "NSS: PK11_ImportSymKey failed"); + goto out; + } + + param = PK11_GenerateNewParam(CKM_DES_ECB, symkey); + if (param == NULL) { + wpa_printf(MSG_ERROR, "NSS: PK11_GenerateNewParam failed"); + goto out; + } + + ctx = PK11_CreateContextBySymKey(CKM_DES_ECB, CKA_ENCRYPT, + symkey, param); + if (ctx == NULL) { + wpa_printf(MSG_ERROR, "NSS: PK11_CreateContextBySymKey(" + "CKM_DES_ECB) failed"); + goto out; + } + + if (PK11_CipherOp(ctx, cypher, &olen, 8, (void *) clear, 8) != + SECSuccess) { + wpa_printf(MSG_ERROR, "NSS: PK11_CipherOp failed"); + goto out; + } + +out: + if (ctx) + PK11_DestroyContext(ctx, PR_TRUE); + if (symkey) + PK11_FreeSymKey(symkey); + if (param) + SECITEM_FreeItem(param, PR_TRUE); +} + + +int rc4_skip(const u8 *key, size_t keylen, size_t skip, + u8 *data, size_t data_len) +{ + return -1; +} + + +int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return nss_hash(HASH_AlgMD5, 16, num_elem, addr, len, mac); +} + + +int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return nss_hash(HASH_AlgSHA1, 20, num_elem, addr, len, mac); +} + + +int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + return nss_hash(HASH_AlgSHA256, 32, num_elem, addr, len, mac); +} + + +void * aes_encrypt_init(const u8 *key, size_t len) +{ + return NULL; +} + + +void aes_encrypt(void *ctx, const u8 *plain, u8 *crypt) +{ +} + + +void aes_encrypt_deinit(void *ctx) +{ +} + + +void * aes_decrypt_init(const u8 *key, size_t len) +{ + return NULL; +} + + +void aes_decrypt(void *ctx, const u8 *crypt, u8 *plain) +{ +} + + +void aes_decrypt_deinit(void *ctx) +{ +} + + +int crypto_mod_exp(const u8 *base, size_t base_len, + const u8 *power, size_t power_len, + const u8 *modulus, size_t modulus_len, + u8 *result, size_t *result_len) +{ + return -1; +} + + +struct crypto_cipher { +}; + + +struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, + const u8 *iv, const u8 *key, + size_t key_len) +{ + return NULL; +} + + +int crypto_cipher_encrypt(struct crypto_cipher *ctx, const u8 *plain, + u8 *crypt, size_t len) +{ + return -1; +} + + +int crypto_cipher_decrypt(struct crypto_cipher *ctx, const u8 *crypt, + u8 *plain, size_t len) +{ + return -1; +} + + +void crypto_cipher_deinit(struct crypto_cipher *ctx) +{ +} diff --git a/contrib/wpa/src/crypto/crypto_openssl.c b/contrib/wpa/src/crypto/crypto_openssl.c index a4c3415..08c98af 100644 --- a/contrib/wpa/src/crypto/crypto_openssl.c +++ b/contrib/wpa/src/crypto/crypto_openssl.c @@ -1,6 +1,6 @@ /* * WPA Supplicant / wrapper functions for libcrypto - * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -14,15 +14,16 @@ #include "includes.h" #include <openssl/opensslv.h> -#include <openssl/md4.h> -#include <openssl/md5.h> -#include <openssl/sha.h> +#include <openssl/err.h> #include <openssl/des.h> #include <openssl/aes.h> #include <openssl/bn.h> #include <openssl/evp.h> +#include <openssl/dh.h> #include "common.h" +#include "wpabuf.h" +#include "dh_group5.h" #include "crypto.h" #if OPENSSL_VERSION_NUMBER < 0x00907000 @@ -33,16 +34,87 @@ des_ecb_encrypt((input), (output), *(ks), (enc)) #endif /* openssl < 0.9.7 */ +static BIGNUM * get_group5_prime(void) +{ +#if OPENSSL_VERSION_NUMBER < 0x00908000 + static const unsigned char RFC3526_PRIME_1536[] = { + 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2, + 0x21,0x68,0xC2,0x34,0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1, + 0x29,0x02,0x4E,0x08,0x8A,0x67,0xCC,0x74,0x02,0x0B,0xBE,0xA6, + 0x3B,0x13,0x9B,0x22,0x51,0x4A,0x08,0x79,0x8E,0x34,0x04,0xDD, + 0xEF,0x95,0x19,0xB3,0xCD,0x3A,0x43,0x1B,0x30,0x2B,0x0A,0x6D, + 0xF2,0x5F,0x14,0x37,0x4F,0xE1,0x35,0x6D,0x6D,0x51,0xC2,0x45, + 0xE4,0x85,0xB5,0x76,0x62,0x5E,0x7E,0xC6,0xF4,0x4C,0x42,0xE9, + 0xA6,0x37,0xED,0x6B,0x0B,0xFF,0x5C,0xB6,0xF4,0x06,0xB7,0xED, + 0xEE,0x38,0x6B,0xFB,0x5A,0x89,0x9F,0xA5,0xAE,0x9F,0x24,0x11, + 0x7C,0x4B,0x1F,0xE6,0x49,0x28,0x66,0x51,0xEC,0xE4,0x5B,0x3D, + 0xC2,0x00,0x7C,0xB8,0xA1,0x63,0xBF,0x05,0x98,0xDA,0x48,0x36, + 0x1C,0x55,0xD3,0x9A,0x69,0x16,0x3F,0xA8,0xFD,0x24,0xCF,0x5F, + 0x83,0x65,0x5D,0x23,0xDC,0xA3,0xAD,0x96,0x1C,0x62,0xF3,0x56, + 0x20,0x85,0x52,0xBB,0x9E,0xD5,0x29,0x07,0x70,0x96,0x96,0x6D, + 0x67,0x0C,0x35,0x4E,0x4A,0xBC,0x98,0x04,0xF1,0x74,0x6C,0x08, + 0xCA,0x23,0x73,0x27,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, + }; + return BN_bin2bn(RFC3526_PRIME_1536, sizeof(RFC3526_PRIME_1536), NULL); +#else /* openssl < 0.9.8 */ + return get_rfc3526_prime_1536(NULL); +#endif /* openssl < 0.9.8 */ +} + +#if OPENSSL_VERSION_NUMBER < 0x00908000 +#ifndef OPENSSL_NO_SHA256 +#ifndef OPENSSL_FIPS +#define NO_SHA256_WRAPPER +#endif +#endif + +#endif /* openssl < 0.9.8 */ -void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +#ifdef OPENSSL_NO_SHA256 +#define NO_SHA256_WRAPPER +#endif + +static int openssl_digest_vector(const EVP_MD *type, int non_fips, + size_t num_elem, const u8 *addr[], + const size_t *len, u8 *mac) { - MD4_CTX ctx; + EVP_MD_CTX ctx; size_t i; + unsigned int mac_len; + + EVP_MD_CTX_init(&ctx); +#ifdef CONFIG_FIPS +#ifdef OPENSSL_FIPS + if (non_fips) + EVP_MD_CTX_set_flags(&ctx, EVP_MD_CTX_FLAG_NON_FIPS_ALLOW); +#endif /* OPENSSL_FIPS */ +#endif /* CONFIG_FIPS */ + if (!EVP_DigestInit_ex(&ctx, type, NULL)) { + wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestInit_ex failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + for (i = 0; i < num_elem; i++) { + if (!EVP_DigestUpdate(&ctx, addr[i], len[i])) { + wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestUpdate " + "failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + } + if (!EVP_DigestFinal(&ctx, mac, &mac_len)) { + wpa_printf(MSG_ERROR, "OpenSSL: EVP_DigestFinal failed: %s", + ERR_error_string(ERR_get_error(), NULL)); + return -1; + } + + return 0; +} - MD4_Init(&ctx); - for (i = 0; i < num_elem; i++) - MD4_Update(&ctx, addr[i], len[i]); - MD4_Final(mac, &ctx); + +int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + return openssl_digest_vector(EVP_md4(), 0, num_elem, addr, len, mac); } @@ -67,94 +139,72 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) } -void md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +int rc4_skip(const u8 *key, size_t keylen, size_t skip, + u8 *data, size_t data_len) { - MD5_CTX ctx; - size_t i; +#ifdef OPENSSL_NO_RC4 + return -1; +#else /* OPENSSL_NO_RC4 */ + EVP_CIPHER_CTX ctx; + int outl; + int res = -1; + unsigned char skip_buf[16]; + + EVP_CIPHER_CTX_init(&ctx); + if (!EVP_CIPHER_CTX_set_padding(&ctx, 0) || + !EVP_CipherInit_ex(&ctx, EVP_rc4(), NULL, NULL, NULL, 1) || + !EVP_CIPHER_CTX_set_key_length(&ctx, keylen) || + !EVP_CipherInit_ex(&ctx, NULL, NULL, key, NULL, 1)) + goto out; + + while (skip >= sizeof(skip_buf)) { + size_t len = skip; + if (len > sizeof(skip_buf)) + len = sizeof(skip_buf); + if (!EVP_CipherUpdate(&ctx, skip_buf, &outl, skip_buf, len)) + goto out; + skip -= len; + } + + if (EVP_CipherUpdate(&ctx, data, &outl, data, data_len)) + res = 0; - MD5_Init(&ctx); - for (i = 0; i < num_elem; i++) - MD5_Update(&ctx, addr[i], len[i]); - MD5_Final(mac, &ctx); +out: + EVP_CIPHER_CTX_cleanup(&ctx); + return res; +#endif /* OPENSSL_NO_RC4 */ } -void sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { - SHA_CTX ctx; - size_t i; - - SHA1_Init(&ctx); - for (i = 0; i < num_elem; i++) - SHA1_Update(&ctx, addr[i], len[i]); - SHA1_Final(mac, &ctx); + return openssl_digest_vector(EVP_md5(), 0, num_elem, addr, len, mac); } -#ifndef CONFIG_NO_FIPS186_2_PRF -static void sha1_transform(u8 *state, const u8 data[64]) +#ifdef CONFIG_FIPS +int md5_vector_non_fips_allow(size_t num_elem, const u8 *addr[], + const size_t *len, u8 *mac) { - SHA_CTX context; - os_memset(&context, 0, sizeof(context)); - os_memcpy(&context.h0, state, 5 * 4); - SHA1_Transform(&context, data); - os_memcpy(state, &context.h0, 5 * 4); + return openssl_digest_vector(EVP_md5(), 1, num_elem, addr, len, mac); } +#endif /* CONFIG_FIPS */ -int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) +int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { - u8 xkey[64]; - u32 t[5], _t[5]; - int i, j, m, k; - u8 *xpos = x; - u32 carry; - - if (seed_len > sizeof(xkey)) - seed_len = sizeof(xkey); - - /* FIPS 186-2 + change notice 1 */ - - os_memcpy(xkey, seed, seed_len); - os_memset(xkey + seed_len, 0, 64 - seed_len); - t[0] = 0x67452301; - t[1] = 0xEFCDAB89; - t[2] = 0x98BADCFE; - t[3] = 0x10325476; - t[4] = 0xC3D2E1F0; - - m = xlen / 40; - for (j = 0; j < m; j++) { - /* XSEED_j = 0 */ - for (i = 0; i < 2; i++) { - /* XVAL = (XKEY + XSEED_j) mod 2^b */ - - /* w_i = G(t, XVAL) */ - os_memcpy(_t, t, 20); - sha1_transform((u8 *) _t, xkey); - _t[0] = host_to_be32(_t[0]); - _t[1] = host_to_be32(_t[1]); - _t[2] = host_to_be32(_t[2]); - _t[3] = host_to_be32(_t[3]); - _t[4] = host_to_be32(_t[4]); - os_memcpy(xpos, _t, 20); - - /* XKEY = (1 + XKEY + w_i) mod 2^b */ - carry = 1; - for (k = 19; k >= 0; k--) { - carry += xkey[k] + xpos[k]; - xkey[k] = carry & 0xff; - carry >>= 8; - } - - xpos += 20; - } - /* x_j = w_0|w_1 */ - } + return openssl_digest_vector(EVP_sha1(), 0, num_elem, addr, len, mac); +} - return 0; + +#ifndef NO_SHA256_WRAPPER +int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + return openssl_digest_vector(EVP_sha256(), 0, num_elem, addr, len, + mac); } -#endif /* CONFIG_NO_FIPS186_2_PRF */ +#endif /* NO_SHA256_WRAPPER */ void * aes_encrypt_init(const u8 *key, size_t len) @@ -310,7 +360,7 @@ struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, EVP_CIPHER_CTX_set_padding(&ctx->enc, 0); if (!EVP_EncryptInit_ex(&ctx->enc, cipher, NULL, NULL, NULL) || !EVP_CIPHER_CTX_set_key_length(&ctx->enc, key_len) || - !EVP_EncryptInit_ex(&ctx->enc, cipher, NULL, key, iv)) { + !EVP_EncryptInit_ex(&ctx->enc, NULL, NULL, key, iv)) { EVP_CIPHER_CTX_cleanup(&ctx->enc); os_free(ctx); return NULL; @@ -320,7 +370,7 @@ struct crypto_cipher * crypto_cipher_init(enum crypto_cipher_alg alg, EVP_CIPHER_CTX_set_padding(&ctx->dec, 0); if (!EVP_DecryptInit_ex(&ctx->dec, cipher, NULL, NULL, NULL) || !EVP_CIPHER_CTX_set_key_length(&ctx->dec, key_len) || - !EVP_DecryptInit_ex(&ctx->dec, cipher, NULL, key, iv)) { + !EVP_DecryptInit_ex(&ctx->dec, NULL, NULL, key, iv)) { EVP_CIPHER_CTX_cleanup(&ctx->enc); EVP_CIPHER_CTX_cleanup(&ctx->dec); os_free(ctx); @@ -358,3 +408,98 @@ void crypto_cipher_deinit(struct crypto_cipher *ctx) EVP_CIPHER_CTX_cleanup(&ctx->dec); os_free(ctx); } + + +void * dh5_init(struct wpabuf **priv, struct wpabuf **publ) +{ + DH *dh; + struct wpabuf *pubkey = NULL, *privkey = NULL; + size_t publen, privlen; + + *priv = NULL; + *publ = NULL; + + dh = DH_new(); + if (dh == NULL) + return NULL; + + dh->g = BN_new(); + if (dh->g == NULL || BN_set_word(dh->g, 2) != 1) + goto err; + + dh->p = get_group5_prime(); + if (dh->p == NULL) + goto err; + + if (DH_generate_key(dh) != 1) + goto err; + + publen = BN_num_bytes(dh->pub_key); + pubkey = wpabuf_alloc(publen); + if (pubkey == NULL) + goto err; + privlen = BN_num_bytes(dh->priv_key); + privkey = wpabuf_alloc(privlen); + if (privkey == NULL) + goto err; + + BN_bn2bin(dh->pub_key, wpabuf_put(pubkey, publen)); + BN_bn2bin(dh->priv_key, wpabuf_put(privkey, privlen)); + + *priv = privkey; + *publ = pubkey; + return dh; + +err: + wpabuf_free(pubkey); + wpabuf_free(privkey); + DH_free(dh); + return NULL; +} + + +struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public, + const struct wpabuf *own_private) +{ + BIGNUM *pub_key; + struct wpabuf *res = NULL; + size_t rlen; + DH *dh = ctx; + int keylen; + + if (ctx == NULL) + return NULL; + + pub_key = BN_bin2bn(wpabuf_head(peer_public), wpabuf_len(peer_public), + NULL); + if (pub_key == NULL) + return NULL; + + rlen = DH_size(dh); + res = wpabuf_alloc(rlen); + if (res == NULL) + goto err; + + keylen = DH_compute_key(wpabuf_mhead(res), pub_key, dh); + if (keylen < 0) + goto err; + wpabuf_put(res, keylen); + BN_free(pub_key); + + return res; + +err: + BN_free(pub_key); + wpabuf_free(res); + return NULL; +} + + +void dh5_free(void *ctx) +{ + DH *dh; + if (ctx == NULL) + return; + dh = ctx; + DH_free(dh); +} diff --git a/contrib/wpa/src/crypto/des.c b/contrib/wpa/src/crypto/des-internal.c index 103e592..ccea950 100644 --- a/contrib/wpa/src/crypto/des.c +++ b/contrib/wpa/src/crypto/des-internal.c @@ -2,7 +2,7 @@ * DES and 3DES-EDE ciphers * * Modifications to LibTomCrypt implementation: - * Copyright (c) 2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -18,9 +18,7 @@ #include "common.h" #include "crypto.h" - - -#ifdef INTERNAL_DES +#include "des_i.h" /* * This implementation is based on a DES implementation included in @@ -432,10 +430,34 @@ void des_encrypt(const u8 *clear, const u8 *key, u8 *cypher) } -struct des3_key_s { - u32 ek[3][32]; - u32 dk[3][32]; -}; +void des_key_setup(const u8 *key, u32 *ek, u32 *dk) +{ + deskey(key, 0, ek); + deskey(key, 1, dk); +} + + +void des_block_encrypt(const u8 *plain, const u32 *ek, u8 *crypt) +{ + u32 work[2]; + work[0] = WPA_GET_BE32(plain); + work[1] = WPA_GET_BE32(plain + 4); + desfunc(work, ek); + WPA_PUT_BE32(crypt, work[0]); + WPA_PUT_BE32(crypt + 4, work[1]); +} + + +void des_block_decrypt(const u8 *crypt, const u32 *dk, u8 *plain) +{ + u32 work[2]; + work[0] = WPA_GET_BE32(crypt); + work[1] = WPA_GET_BE32(crypt + 4); + desfunc(work, dk); + WPA_PUT_BE32(plain, work[0]); + WPA_PUT_BE32(plain + 4, work[1]); +} + void des3_key_setup(const u8 *key, struct des3_key_s *dkey) { @@ -475,5 +497,3 @@ void des3_decrypt(const u8 *crypt, const struct des3_key_s *key, u8 *plain) WPA_PUT_BE32(plain, work[0]); WPA_PUT_BE32(plain + 4, work[1]); } - -#endif /* INTERNAL_DES */ diff --git a/contrib/wpa/src/crypto/des_i.h b/contrib/wpa/src/crypto/des_i.h new file mode 100644 index 0000000..6f27414 --- /dev/null +++ b/contrib/wpa/src/crypto/des_i.h @@ -0,0 +1,31 @@ +/* + * DES and 3DES-EDE ciphers + * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef DES_I_H +#define DES_I_H + +struct des3_key_s { + u32 ek[3][32]; + u32 dk[3][32]; +}; + +void des_key_setup(const u8 *key, u32 *ek, u32 *dk); +void des_block_encrypt(const u8 *plain, const u32 *ek, u8 *crypt); +void des_block_decrypt(const u8 *crypt, const u32 *dk, u8 *plain); + +void des3_key_setup(const u8 *key, struct des3_key_s *dkey); +void des3_encrypt(const u8 *plain, const struct des3_key_s *key, u8 *crypt); +void des3_decrypt(const u8 *crypt, const struct des3_key_s *key, u8 *plain); + +#endif /* DES_I_H */ diff --git a/contrib/wpa/src/crypto/dh_group5.c b/contrib/wpa/src/crypto/dh_group5.c new file mode 100644 index 0000000..8c475bf --- /dev/null +++ b/contrib/wpa/src/crypto/dh_group5.c @@ -0,0 +1,40 @@ +/* + * Diffie-Hellman group 5 operations + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "dh_groups.h" +#include "dh_group5.h" + + +void * dh5_init(struct wpabuf **priv, struct wpabuf **publ) +{ + *publ = dh_init(dh_groups_get(5), priv); + if (*publ == 0) + return NULL; + return (void *) 1; +} + + +struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public, + const struct wpabuf *own_private) +{ + return dh_derive_shared(peer_public, own_private, dh_groups_get(5)); +} + + +void dh5_free(void *ctx) +{ +} diff --git a/contrib/wpa/src/crypto/dh_group5.h b/contrib/wpa/src/crypto/dh_group5.h new file mode 100644 index 0000000..595f111 --- /dev/null +++ b/contrib/wpa/src/crypto/dh_group5.h @@ -0,0 +1,23 @@ +/* + * Diffie-Hellman group 5 operations + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef DH_GROUP5_H +#define DH_GROUP5_H + +void * dh5_init(struct wpabuf **priv, struct wpabuf **publ); +struct wpabuf * dh5_derive_shared(void *ctx, const struct wpabuf *peer_public, + const struct wpabuf *own_private); +void dh5_free(void *ctx); + +#endif /* DH_GROUP5_H */ diff --git a/contrib/wpa/src/crypto/dh_groups.c b/contrib/wpa/src/crypto/dh_groups.c index 5f6008a..7bd2fb7 100644 --- a/contrib/wpa/src/crypto/dh_groups.c +++ b/contrib/wpa/src/crypto/dh_groups.c @@ -619,11 +619,12 @@ struct wpabuf * dh_derive_shared(const struct wpabuf *peer_public, if (crypto_mod_exp(wpabuf_head(peer_public), wpabuf_len(peer_public), wpabuf_head(own_private), wpabuf_len(own_private), dh->prime, dh->prime_len, - wpabuf_put(shared, shared_len), &shared_len) < 0) { + wpabuf_mhead(shared), &shared_len) < 0) { wpabuf_free(shared); wpa_printf(MSG_INFO, "DH: crypto_mod_exp failed"); return NULL; } + wpabuf_put(shared, shared_len); wpa_hexdump_buf_key(MSG_DEBUG, "DH: shared key", shared); return shared; diff --git a/contrib/wpa/src/crypto/fips_prf_cryptoapi.c b/contrib/wpa/src/crypto/fips_prf_cryptoapi.c new file mode 100644 index 0000000..17d3116 --- /dev/null +++ b/contrib/wpa/src/crypto/fips_prf_cryptoapi.c @@ -0,0 +1,25 @@ +/* + * FIPS 186-2 PRF for Microsoft CryptoAPI + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto.h" + + +int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) +{ + /* FIX: how to do this with CryptoAPI? */ + return -1; +} diff --git a/contrib/wpa/src/crypto/fips_prf_gnutls.c b/contrib/wpa/src/crypto/fips_prf_gnutls.c new file mode 100644 index 0000000..f742e98 --- /dev/null +++ b/contrib/wpa/src/crypto/fips_prf_gnutls.c @@ -0,0 +1,26 @@ +/* + * FIPS 186-2 PRF for libgcrypt + * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <gcrypt.h> + +#include "common.h" +#include "crypto.h" + + +int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) +{ + /* FIX: how to do this with libgcrypt? */ + return -1; +} diff --git a/contrib/wpa/src/crypto/fips_prf_internal.c b/contrib/wpa/src/crypto/fips_prf_internal.c new file mode 100644 index 0000000..a85cb14 --- /dev/null +++ b/contrib/wpa/src/crypto/fips_prf_internal.c @@ -0,0 +1,74 @@ +/* + * FIPS 186-2 PRF for internal crypto implementation + * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha1.h" +#include "sha1_i.h" +#include "crypto.h" + + +int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) +{ + u8 xkey[64]; + u32 t[5], _t[5]; + int i, j, m, k; + u8 *xpos = x; + u32 carry; + + if (seed_len > sizeof(xkey)) + seed_len = sizeof(xkey); + + /* FIPS 186-2 + change notice 1 */ + + os_memcpy(xkey, seed, seed_len); + os_memset(xkey + seed_len, 0, 64 - seed_len); + t[0] = 0x67452301; + t[1] = 0xEFCDAB89; + t[2] = 0x98BADCFE; + t[3] = 0x10325476; + t[4] = 0xC3D2E1F0; + + m = xlen / 40; + for (j = 0; j < m; j++) { + /* XSEED_j = 0 */ + for (i = 0; i < 2; i++) { + /* XVAL = (XKEY + XSEED_j) mod 2^b */ + + /* w_i = G(t, XVAL) */ + os_memcpy(_t, t, 20); + SHA1Transform(_t, xkey); + _t[0] = host_to_be32(_t[0]); + _t[1] = host_to_be32(_t[1]); + _t[2] = host_to_be32(_t[2]); + _t[3] = host_to_be32(_t[3]); + _t[4] = host_to_be32(_t[4]); + os_memcpy(xpos, _t, 20); + + /* XKEY = (1 + XKEY + w_i) mod 2^b */ + carry = 1; + for (k = 19; k >= 0; k--) { + carry += xkey[k] + xpos[k]; + xkey[k] = carry & 0xff; + carry >>= 8; + } + + xpos += SHA1_MAC_LEN; + } + /* x_j = w_0|w_1 */ + } + + return 0; +} diff --git a/contrib/wpa/src/crypto/fips_prf_nss.c b/contrib/wpa/src/crypto/fips_prf_nss.c new file mode 100644 index 0000000..f941983 --- /dev/null +++ b/contrib/wpa/src/crypto/fips_prf_nss.c @@ -0,0 +1,25 @@ +/* + * FIPS 186-2 PRF for NSS + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <openssl/sha.h> + +#include "common.h" +#include "crypto.h" + + +int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) +{ + return -1; +} diff --git a/contrib/wpa/src/crypto/fips_prf_openssl.c b/contrib/wpa/src/crypto/fips_prf_openssl.c new file mode 100644 index 0000000..d0af983 --- /dev/null +++ b/contrib/wpa/src/crypto/fips_prf_openssl.c @@ -0,0 +1,83 @@ +/* + * FIPS 186-2 PRF for libcrypto + * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <openssl/sha.h> + +#include "common.h" +#include "crypto.h" + + +static void sha1_transform(u8 *state, const u8 data[64]) +{ + SHA_CTX context; + os_memset(&context, 0, sizeof(context)); + os_memcpy(&context.h0, state, 5 * 4); + SHA1_Transform(&context, data); + os_memcpy(state, &context.h0, 5 * 4); +} + + +int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) +{ + u8 xkey[64]; + u32 t[5], _t[5]; + int i, j, m, k; + u8 *xpos = x; + u32 carry; + + if (seed_len > sizeof(xkey)) + seed_len = sizeof(xkey); + + /* FIPS 186-2 + change notice 1 */ + + os_memcpy(xkey, seed, seed_len); + os_memset(xkey + seed_len, 0, 64 - seed_len); + t[0] = 0x67452301; + t[1] = 0xEFCDAB89; + t[2] = 0x98BADCFE; + t[3] = 0x10325476; + t[4] = 0xC3D2E1F0; + + m = xlen / 40; + for (j = 0; j < m; j++) { + /* XSEED_j = 0 */ + for (i = 0; i < 2; i++) { + /* XVAL = (XKEY + XSEED_j) mod 2^b */ + + /* w_i = G(t, XVAL) */ + os_memcpy(_t, t, 20); + sha1_transform((u8 *) _t, xkey); + _t[0] = host_to_be32(_t[0]); + _t[1] = host_to_be32(_t[1]); + _t[2] = host_to_be32(_t[2]); + _t[3] = host_to_be32(_t[3]); + _t[4] = host_to_be32(_t[4]); + os_memcpy(xpos, _t, 20); + + /* XKEY = (1 + XKEY + w_i) mod 2^b */ + carry = 1; + for (k = 19; k >= 0; k--) { + carry += xkey[k] + xpos[k]; + xkey[k] = carry & 0xff; + carry >>= 8; + } + + xpos += 20; + } + /* x_j = w_0|w_1 */ + } + + return 0; +} diff --git a/contrib/wpa/src/crypto/md4.c b/contrib/wpa/src/crypto/md4-internal.c index 41c84a3..d9f499f 100644 --- a/contrib/wpa/src/crypto/md4.c +++ b/contrib/wpa/src/crypto/md4-internal.c @@ -17,9 +17,6 @@ #include "common.h" #include "crypto.h" - -#ifdef INTERNAL_MD4 - #define MD4_BLOCK_LENGTH 64 #define MD4_DIGEST_LENGTH 16 @@ -35,7 +32,7 @@ static void MD4Update(MD4_CTX *ctx, const unsigned char *input, size_t len); static void MD4Final(unsigned char digest[MD4_DIGEST_LENGTH], MD4_CTX *ctx); -void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +int md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) { MD4_CTX ctx; size_t i; @@ -44,6 +41,7 @@ void md4_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) for (i = 0; i < num_elem; i++) MD4Update(&ctx, addr[i], len[i]); MD4Final(mac, &ctx); + return 0; } @@ -278,5 +276,3 @@ MD4Transform(u32 state[4], const u8 block[MD4_BLOCK_LENGTH]) state[3] += d; } /* ===== end - public domain MD4 implementation ===== */ - -#endif /* INTERNAL_MD4 */ diff --git a/contrib/wpa/src/crypto/md5-internal.c b/contrib/wpa/src/crypto/md5-internal.c new file mode 100644 index 0000000..f8692a9 --- /dev/null +++ b/contrib/wpa/src/crypto/md5-internal.c @@ -0,0 +1,293 @@ +/* + * MD5 hash implementation and interface functions + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "md5.h" +#include "md5_i.h" +#include "crypto.h" + + +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 + * Returns: 0 on success, -1 of failure + */ +int md5_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + MD5_CTX ctx; + size_t i; + + MD5Init(&ctx); + for (i = 0; i < num_elem; i++) + MD5Update(&ctx, addr[i], len[i]); + MD5Final(mac, &ctx); + return 0; +} + + +/* ===== start - public domain MD5 implementation ===== */ +/* + * This code implements the MD5 message-digest algorithm. + * The algorithm is due to Ron Rivest. This code was + * written by Colin Plumb in 1993, no copyright is claimed. + * This code is in the public domain; do with it what you wish. + * + * Equivalent code is available from RSA Data Security, Inc. + * This code has been tested against that, and is equivalent, + * except that you don't need to include two pages of legalese + * with every copy. + * + * To compute the message digest of a chunk of bytes, declare an + * MD5Context structure, pass it to MD5Init, call MD5Update as + * needed on buffers full of bytes, and then call MD5Final, which + * will fill a supplied 16-byte array with the digest. + */ + +#ifndef WORDS_BIGENDIAN +#define byteReverse(buf, len) /* Nothing */ +#else +/* + * Note: this code is harmless on little-endian machines. + */ +static void byteReverse(unsigned char *buf, unsigned longs) +{ + u32 t; + do { + t = (u32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | + ((unsigned) buf[1] << 8 | buf[0]); + *(u32 *) buf = t; + buf += 4; + } while (--longs); +} +#endif + +/* + * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious + * initialization constants. + */ +void MD5Init(struct MD5Context *ctx) +{ + ctx->buf[0] = 0x67452301; + ctx->buf[1] = 0xefcdab89; + ctx->buf[2] = 0x98badcfe; + ctx->buf[3] = 0x10325476; + + ctx->bits[0] = 0; + ctx->bits[1] = 0; +} + +/* + * Update context to reflect the concatenation of another buffer full + * of bytes. + */ +void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) +{ + u32 t; + + /* Update bitcount */ + + t = ctx->bits[0]; + if ((ctx->bits[0] = t + ((u32) len << 3)) < t) + ctx->bits[1]++; /* Carry from low to high */ + ctx->bits[1] += len >> 29; + + t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ + + /* Handle any leading odd-sized chunks */ + + if (t) { + unsigned char *p = (unsigned char *) ctx->in + t; + + t = 64 - t; + if (len < t) { + os_memcpy(p, buf, len); + return; + } + os_memcpy(p, buf, t); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (u32 *) ctx->in); + buf += t; + len -= t; + } + /* Process data in 64-byte chunks */ + + while (len >= 64) { + os_memcpy(ctx->in, buf, 64); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (u32 *) ctx->in); + buf += 64; + len -= 64; + } + + /* Handle any remaining bytes of data. */ + + os_memcpy(ctx->in, buf, len); +} + +/* + * Final wrapup - pad to 64-byte boundary with the bit pattern + * 1 0* (64-bit count of bits processed, MSB-first) + */ +void MD5Final(unsigned char digest[16], struct MD5Context *ctx) +{ + unsigned count; + unsigned char *p; + + /* Compute number of bytes mod 64 */ + count = (ctx->bits[0] >> 3) & 0x3F; + + /* Set the first char of padding to 0x80. This is safe since there is + always at least one byte free */ + p = ctx->in + count; + *p++ = 0x80; + + /* Bytes of padding needed to make 64 bytes */ + count = 64 - 1 - count; + + /* Pad out to 56 mod 64 */ + if (count < 8) { + /* Two lots of padding: Pad the first block to 64 bytes */ + os_memset(p, 0, count); + byteReverse(ctx->in, 16); + MD5Transform(ctx->buf, (u32 *) ctx->in); + + /* Now fill the next block with 56 bytes */ + os_memset(ctx->in, 0, 56); + } else { + /* Pad block to 56 bytes */ + os_memset(p, 0, count - 8); + } + byteReverse(ctx->in, 14); + + /* Append length in bits and transform */ + ((u32 *) ctx->in)[14] = ctx->bits[0]; + ((u32 *) ctx->in)[15] = ctx->bits[1]; + + MD5Transform(ctx->buf, (u32 *) ctx->in); + byteReverse((unsigned char *) ctx->buf, 4); + os_memcpy(digest, ctx->buf, 16); + os_memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ +} + +/* The four core functions - F1 is optimized somewhat */ + +/* #define F1(x, y, z) (x & y | ~x & z) */ +#define F1(x, y, z) (z ^ (x & (y ^ z))) +#define F2(x, y, z) F1(z, x, y) +#define F3(x, y, z) (x ^ y ^ z) +#define F4(x, y, z) (y ^ (x | ~z)) + +/* This is the central step in the MD5 algorithm. */ +#define MD5STEP(f, w, x, y, z, data, s) \ + ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x ) + +/* + * The core of the MD5 algorithm, this alters an existing MD5 hash to + * reflect the addition of 16 longwords of new data. MD5Update blocks + * the data and converts bytes into longwords for this routine. + */ +static void MD5Transform(u32 buf[4], u32 const in[16]) +{ + register u32 a, b, c, d; + + a = buf[0]; + b = buf[1]; + c = buf[2]; + d = buf[3]; + + MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); + MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); + MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); + MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); + MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); + MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); + MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); + MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); + MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); + MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); + MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); + MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); + MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); + MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); + MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); + MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); + + MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); + MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); + MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); + MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); + MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); + MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); + MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); + MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); + MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); + MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); + MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); + MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); + MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); + MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); + MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); + MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); + + MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); + MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); + MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); + MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); + MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); + MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); + MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); + MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); + MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); + MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); + MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); + MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); + MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); + MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); + MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); + MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); + + MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); + MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); + MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); + MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); + MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); + MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); + MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); + MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); + MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); + MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); + MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); + MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); + MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); + MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); + MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); + MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); + + buf[0] += a; + buf[1] += b; + buf[2] += c; + buf[3] += d; +} +/* ===== end - public domain MD5 implementation ===== */ diff --git a/contrib/wpa/src/crypto/md5-non-fips.c b/contrib/wpa/src/crypto/md5-non-fips.c new file mode 100644 index 0000000..6f29201 --- /dev/null +++ b/contrib/wpa/src/crypto/md5-non-fips.c @@ -0,0 +1,113 @@ +/* + * MD5 hash implementation and interface functions (non-FIPS allowed cases) + * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "md5.h" +#include "crypto.h" + + +/** + * hmac_md5_vector_non_fips_allow - 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) + * Returns: 0 on success, -1 on failure + */ +int hmac_md5_vector_non_fips_allow(const u8 *key, size_t key_len, + size_t num_elem, const u8 *addr[], + const size_t *len, u8 *mac) +{ + u8 k_pad[64]; /* padding - key XORd with ipad/opad */ + u8 tk[16]; + const u8 *_addr[6]; + size_t i, _len[6]; + + if (num_elem > 5) { + /* + * Fixed limit on the number of fragments to avoid having to + * allocate memory (which could fail). + */ + return -1; + } + + /* if key is longer than 64 bytes reset it to key = MD5(key) */ + if (key_len > 64) { + if (md5_vector_non_fips_allow(1, &key, &key_len, tk)) + return -1; + key = tk; + key_len = 16; + } + + /* the HMAC_MD5 transform looks like: + * + * MD5(K XOR opad, MD5(K XOR ipad, text)) + * + * where K is an n byte key + * ipad is the byte 0x36 repeated 64 times + * opad is the byte 0x5c repeated 64 times + * and text is the data being protected */ + + /* start out by storing key in ipad */ + os_memset(k_pad, 0, sizeof(k_pad)); + os_memcpy(k_pad, key, key_len); + + /* XOR key with ipad values */ + for (i = 0; i < 64; i++) + k_pad[i] ^= 0x36; + + /* perform inner MD5 */ + _addr[0] = k_pad; + _len[0] = 64; + for (i = 0; i < num_elem; i++) { + _addr[i + 1] = addr[i]; + _len[i + 1] = len[i]; + } + if (md5_vector_non_fips_allow(1 + num_elem, _addr, _len, mac)) + return -1; + + os_memset(k_pad, 0, sizeof(k_pad)); + os_memcpy(k_pad, key, key_len); + /* XOR key with opad values */ + for (i = 0; i < 64; i++) + k_pad[i] ^= 0x5c; + + /* perform outer MD5 */ + _addr[0] = k_pad; + _len[0] = 64; + _addr[1] = mac; + _len[1] = MD5_MAC_LEN; + return md5_vector_non_fips_allow(2, _addr, _len, mac); +} + + +/** + * hmac_md5_non_fips_allow - 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) + * Returns: 0 on success, -1 on failure + */ +int hmac_md5_non_fips_allow(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac) +{ + return hmac_md5_vector_non_fips_allow(key, key_len, 1, &data, + &data_len, mac); +} diff --git a/contrib/wpa/src/crypto/md5.c b/contrib/wpa/src/crypto/md5.c index a7db7aa..7f14e9b 100644 --- a/contrib/wpa/src/crypto/md5.c +++ b/contrib/wpa/src/crypto/md5.c @@ -27,9 +27,10 @@ * @addr: Pointers to the data areas * @len: Lengths of the data blocks * @mac: Buffer for the hash (16 bytes) + * Returns: 0 on success, -1 on failure */ -void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, - const u8 *addr[], const size_t *len, u8 *mac) +int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) { u8 k_pad[64]; /* padding - key XORd with ipad/opad */ u8 tk[16]; @@ -41,12 +42,13 @@ void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, * Fixed limit on the number of fragments to avoid having to * allocate memory (which could fail). */ - return; + return -1; } /* if key is longer than 64 bytes reset it to key = MD5(key) */ if (key_len > 64) { - md5_vector(1, &key, &key_len, tk); + if (md5_vector(1, &key, &key_len, tk)) + return -1; key = tk; key_len = 16; } @@ -75,7 +77,8 @@ void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, _addr[i + 1] = addr[i]; _len[i + 1] = len[i]; } - md5_vector(1 + num_elem, _addr, _len, mac); + if (md5_vector(1 + num_elem, _addr, _len, mac)) + return -1; os_memset(k_pad, 0, sizeof(k_pad)); os_memcpy(k_pad, key, key_len); @@ -88,7 +91,7 @@ void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, _len[0] = 64; _addr[1] = mac; _len[1] = MD5_MAC_LEN; - md5_vector(2, _addr, _len, mac); + return md5_vector(2, _addr, _len, mac); } @@ -99,296 +102,10 @@ void hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, * @data: Pointers to the data area * @data_len: Length of the data area * @mac: Buffer for the hash (16 bytes) + * Returns: 0 on success, -1 on failure */ -void hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, +int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *mac) { - hmac_md5_vector(key, key_len, 1, &data, &data_len, mac); + return hmac_md5_vector(key, key_len, 1, &data, &data_len, mac); } - - -#ifdef INTERNAL_MD5 - -struct MD5Context { - u32 buf[4]; - u32 bits[2]; - u8 in[64]; -}; - -#ifndef CONFIG_CRYPTO_INTERNAL -static void MD5Init(struct MD5Context *context); -static void MD5Update(struct MD5Context *context, unsigned char const *buf, - unsigned len); -static void MD5Final(unsigned char digest[16], struct MD5Context *context); -#endif /* CONFIG_CRYPTO_INTERNAL */ -static void MD5Transform(u32 buf[4], u32 const in[16]); - - -typedef struct MD5Context MD5_CTX; - - -/** - * 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; - size_t 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. - * The algorithm is due to Ron Rivest. This code was - * written by Colin Plumb in 1993, no copyright is claimed. - * This code is in the public domain; do with it what you wish. - * - * Equivalent code is available from RSA Data Security, Inc. - * This code has been tested against that, and is equivalent, - * except that you don't need to include two pages of legalese - * with every copy. - * - * To compute the message digest of a chunk of bytes, declare an - * MD5Context structure, pass it to MD5Init, call MD5Update as - * needed on buffers full of bytes, and then call MD5Final, which - * will fill a supplied 16-byte array with the digest. - */ - -#ifndef WORDS_BIGENDIAN -#define byteReverse(buf, len) /* Nothing */ -#else -/* - * Note: this code is harmless on little-endian machines. - */ -static void byteReverse(unsigned char *buf, unsigned longs) -{ - u32 t; - do { - t = (u32) ((unsigned) buf[3] << 8 | buf[2]) << 16 | - ((unsigned) buf[1] << 8 | buf[0]); - *(u32 *) buf = t; - buf += 4; - } while (--longs); -} -#endif - -/* - * Start MD5 accumulation. Set bit count to 0 and buffer to mysterious - * initialization constants. - */ -void MD5Init(struct MD5Context *ctx) -{ - ctx->buf[0] = 0x67452301; - ctx->buf[1] = 0xefcdab89; - ctx->buf[2] = 0x98badcfe; - ctx->buf[3] = 0x10325476; - - ctx->bits[0] = 0; - ctx->bits[1] = 0; -} - -/* - * Update context to reflect the concatenation of another buffer full - * of bytes. - */ -void MD5Update(struct MD5Context *ctx, unsigned char const *buf, unsigned len) -{ - u32 t; - - /* Update bitcount */ - - t = ctx->bits[0]; - if ((ctx->bits[0] = t + ((u32) len << 3)) < t) - ctx->bits[1]++; /* Carry from low to high */ - ctx->bits[1] += len >> 29; - - t = (t >> 3) & 0x3f; /* Bytes already in shsInfo->data */ - - /* Handle any leading odd-sized chunks */ - - if (t) { - unsigned char *p = (unsigned char *) ctx->in + t; - - t = 64 - t; - if (len < t) { - os_memcpy(p, buf, len); - return; - } - os_memcpy(p, buf, t); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (u32 *) ctx->in); - buf += t; - len -= t; - } - /* Process data in 64-byte chunks */ - - while (len >= 64) { - os_memcpy(ctx->in, buf, 64); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (u32 *) ctx->in); - buf += 64; - len -= 64; - } - - /* Handle any remaining bytes of data. */ - - os_memcpy(ctx->in, buf, len); -} - -/* - * Final wrapup - pad to 64-byte boundary with the bit pattern - * 1 0* (64-bit count of bits processed, MSB-first) - */ -void MD5Final(unsigned char digest[16], struct MD5Context *ctx) -{ - unsigned count; - unsigned char *p; - - /* Compute number of bytes mod 64 */ - count = (ctx->bits[0] >> 3) & 0x3F; - - /* Set the first char of padding to 0x80. This is safe since there is - always at least one byte free */ - p = ctx->in + count; - *p++ = 0x80; - - /* Bytes of padding needed to make 64 bytes */ - count = 64 - 1 - count; - - /* Pad out to 56 mod 64 */ - if (count < 8) { - /* Two lots of padding: Pad the first block to 64 bytes */ - os_memset(p, 0, count); - byteReverse(ctx->in, 16); - MD5Transform(ctx->buf, (u32 *) ctx->in); - - /* Now fill the next block with 56 bytes */ - os_memset(ctx->in, 0, 56); - } else { - /* Pad block to 56 bytes */ - os_memset(p, 0, count - 8); - } - byteReverse(ctx->in, 14); - - /* Append length in bits and transform */ - ((u32 *) ctx->in)[14] = ctx->bits[0]; - ((u32 *) ctx->in)[15] = ctx->bits[1]; - - MD5Transform(ctx->buf, (u32 *) ctx->in); - byteReverse((unsigned char *) ctx->buf, 4); - os_memcpy(digest, ctx->buf, 16); - os_memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ -} - -/* The four core functions - F1 is optimized somewhat */ - -/* #define F1(x, y, z) (x & y | ~x & z) */ -#define F1(x, y, z) (z ^ (x & (y ^ z))) -#define F2(x, y, z) F1(z, x, y) -#define F3(x, y, z) (x ^ y ^ z) -#define F4(x, y, z) (y ^ (x | ~z)) - -/* This is the central step in the MD5 algorithm. */ -#define MD5STEP(f, w, x, y, z, data, s) \ - ( w += f(x, y, z) + data, w = w<<s | w>>(32-s), w += x ) - -/* - * The core of the MD5 algorithm, this alters an existing MD5 hash to - * reflect the addition of 16 longwords of new data. MD5Update blocks - * the data and converts bytes into longwords for this routine. - */ -static void MD5Transform(u32 buf[4], u32 const in[16]) -{ - register u32 a, b, c, d; - - a = buf[0]; - b = buf[1]; - c = buf[2]; - d = buf[3]; - - MD5STEP(F1, a, b, c, d, in[0] + 0xd76aa478, 7); - MD5STEP(F1, d, a, b, c, in[1] + 0xe8c7b756, 12); - MD5STEP(F1, c, d, a, b, in[2] + 0x242070db, 17); - MD5STEP(F1, b, c, d, a, in[3] + 0xc1bdceee, 22); - MD5STEP(F1, a, b, c, d, in[4] + 0xf57c0faf, 7); - MD5STEP(F1, d, a, b, c, in[5] + 0x4787c62a, 12); - MD5STEP(F1, c, d, a, b, in[6] + 0xa8304613, 17); - MD5STEP(F1, b, c, d, a, in[7] + 0xfd469501, 22); - MD5STEP(F1, a, b, c, d, in[8] + 0x698098d8, 7); - MD5STEP(F1, d, a, b, c, in[9] + 0x8b44f7af, 12); - MD5STEP(F1, c, d, a, b, in[10] + 0xffff5bb1, 17); - MD5STEP(F1, b, c, d, a, in[11] + 0x895cd7be, 22); - MD5STEP(F1, a, b, c, d, in[12] + 0x6b901122, 7); - MD5STEP(F1, d, a, b, c, in[13] + 0xfd987193, 12); - MD5STEP(F1, c, d, a, b, in[14] + 0xa679438e, 17); - MD5STEP(F1, b, c, d, a, in[15] + 0x49b40821, 22); - - MD5STEP(F2, a, b, c, d, in[1] + 0xf61e2562, 5); - MD5STEP(F2, d, a, b, c, in[6] + 0xc040b340, 9); - MD5STEP(F2, c, d, a, b, in[11] + 0x265e5a51, 14); - MD5STEP(F2, b, c, d, a, in[0] + 0xe9b6c7aa, 20); - MD5STEP(F2, a, b, c, d, in[5] + 0xd62f105d, 5); - MD5STEP(F2, d, a, b, c, in[10] + 0x02441453, 9); - MD5STEP(F2, c, d, a, b, in[15] + 0xd8a1e681, 14); - MD5STEP(F2, b, c, d, a, in[4] + 0xe7d3fbc8, 20); - MD5STEP(F2, a, b, c, d, in[9] + 0x21e1cde6, 5); - MD5STEP(F2, d, a, b, c, in[14] + 0xc33707d6, 9); - MD5STEP(F2, c, d, a, b, in[3] + 0xf4d50d87, 14); - MD5STEP(F2, b, c, d, a, in[8] + 0x455a14ed, 20); - MD5STEP(F2, a, b, c, d, in[13] + 0xa9e3e905, 5); - MD5STEP(F2, d, a, b, c, in[2] + 0xfcefa3f8, 9); - MD5STEP(F2, c, d, a, b, in[7] + 0x676f02d9, 14); - MD5STEP(F2, b, c, d, a, in[12] + 0x8d2a4c8a, 20); - - MD5STEP(F3, a, b, c, d, in[5] + 0xfffa3942, 4); - MD5STEP(F3, d, a, b, c, in[8] + 0x8771f681, 11); - MD5STEP(F3, c, d, a, b, in[11] + 0x6d9d6122, 16); - MD5STEP(F3, b, c, d, a, in[14] + 0xfde5380c, 23); - MD5STEP(F3, a, b, c, d, in[1] + 0xa4beea44, 4); - MD5STEP(F3, d, a, b, c, in[4] + 0x4bdecfa9, 11); - MD5STEP(F3, c, d, a, b, in[7] + 0xf6bb4b60, 16); - MD5STEP(F3, b, c, d, a, in[10] + 0xbebfbc70, 23); - MD5STEP(F3, a, b, c, d, in[13] + 0x289b7ec6, 4); - MD5STEP(F3, d, a, b, c, in[0] + 0xeaa127fa, 11); - MD5STEP(F3, c, d, a, b, in[3] + 0xd4ef3085, 16); - MD5STEP(F3, b, c, d, a, in[6] + 0x04881d05, 23); - MD5STEP(F3, a, b, c, d, in[9] + 0xd9d4d039, 4); - MD5STEP(F3, d, a, b, c, in[12] + 0xe6db99e5, 11); - MD5STEP(F3, c, d, a, b, in[15] + 0x1fa27cf8, 16); - MD5STEP(F3, b, c, d, a, in[2] + 0xc4ac5665, 23); - - MD5STEP(F4, a, b, c, d, in[0] + 0xf4292244, 6); - MD5STEP(F4, d, a, b, c, in[7] + 0x432aff97, 10); - MD5STEP(F4, c, d, a, b, in[14] + 0xab9423a7, 15); - MD5STEP(F4, b, c, d, a, in[5] + 0xfc93a039, 21); - MD5STEP(F4, a, b, c, d, in[12] + 0x655b59c3, 6); - MD5STEP(F4, d, a, b, c, in[3] + 0x8f0ccc92, 10); - MD5STEP(F4, c, d, a, b, in[10] + 0xffeff47d, 15); - MD5STEP(F4, b, c, d, a, in[1] + 0x85845dd1, 21); - MD5STEP(F4, a, b, c, d, in[8] + 0x6fa87e4f, 6); - MD5STEP(F4, d, a, b, c, in[15] + 0xfe2ce6e0, 10); - MD5STEP(F4, c, d, a, b, in[6] + 0xa3014314, 15); - MD5STEP(F4, b, c, d, a, in[13] + 0x4e0811a1, 21); - MD5STEP(F4, a, b, c, d, in[4] + 0xf7537e82, 6); - MD5STEP(F4, d, a, b, c, in[11] + 0xbd3af235, 10); - MD5STEP(F4, c, d, a, b, in[2] + 0x2ad7d2bb, 15); - MD5STEP(F4, b, c, d, a, in[9] + 0xeb86d391, 21); - - buf[0] += a; - buf[1] += b; - buf[2] += c; - buf[3] += d; -} -/* ===== end - public domain MD5 implementation ===== */ - -#endif /* INTERNAL_MD5 */ diff --git a/contrib/wpa/src/crypto/md5.h b/contrib/wpa/src/crypto/md5.h index e82f396..8952590 100644 --- a/contrib/wpa/src/crypto/md5.h +++ b/contrib/wpa/src/crypto/md5.h @@ -1,6 +1,6 @@ /* * MD5 hash implementation and interface functions - * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -17,18 +17,19 @@ #define MD5_MAC_LEN 16 -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, - u8 *mac); - -#ifdef CONFIG_CRYPTO_INTERNAL -struct MD5Context; - -void MD5Init(struct MD5Context *context); -void MD5Update(struct MD5Context *context, unsigned char const *buf, - unsigned len); -void MD5Final(unsigned char digest[16], struct MD5Context *context); -#endif /* CONFIG_CRYPTO_INTERNAL */ +int hmac_md5_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac); +int hmac_md5(const u8 *key, size_t key_len, const u8 *data, size_t data_len, + u8 *mac); +#ifdef CONFIG_FIPS +int hmac_md5_vector_non_fips_allow(const u8 *key, size_t key_len, + size_t num_elem, const u8 *addr[], + const size_t *len, u8 *mac); +int hmac_md5_non_fips_allow(const u8 *key, size_t key_len, const u8 *data, + size_t data_len, u8 *mac); +#else /* CONFIG_FIPS */ +#define hmac_md5_vector_non_fips_allow hmac_md5_vector +#define hmac_md5_non_fips_allow hmac_md5 +#endif /* CONFIG_FIPS */ #endif /* MD5_H */ diff --git a/contrib/wpa/src/crypto/md5_i.h b/contrib/wpa/src/crypto/md5_i.h new file mode 100644 index 0000000..b7f6596 --- /dev/null +++ b/contrib/wpa/src/crypto/md5_i.h @@ -0,0 +1,29 @@ +/* + * MD5 internal definitions + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef MD5_I_H +#define MD5_I_H + +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); + +#endif /* MD5_I_H */ diff --git a/contrib/wpa/src/crypto/milenage.c b/contrib/wpa/src/crypto/milenage.c new file mode 100644 index 0000000..cf0c60e --- /dev/null +++ b/contrib/wpa/src/crypto/milenage.c @@ -0,0 +1,329 @@ +/* + * 3GPP AKA - Milenage algorithm (3GPP TS 35.205, .206, .207, .208) + * Copyright (c) 2006-2007 <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + * + * This file implements an example authentication algorithm defined for 3GPP + * AKA. This can be used to implement a simple HLR/AuC into hlr_auc_gw to allow + * EAP-AKA to be tested properly with real USIM cards. + * + * This implementations assumes that the r1..r5 and c1..c5 constants defined in + * TS 35.206 are used, i.e., r1=64, r2=0, r3=32, r4=64, r5=96, c1=00..00, + * c2=00..01, c3=00..02, c4=00..04, c5=00..08. The block cipher is assumed to + * be AES (Rijndael). + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/aes_wrap.h" +#include "milenage.h" + + +/** + * milenage_f1 - Milenage f1 and f1* algorithms + * @opc: OPc = 128-bit value derived from OP and K + * @k: K = 128-bit subscriber key + * @_rand: RAND = 128-bit random challenge + * @sqn: SQN = 48-bit sequence number + * @amf: AMF = 16-bit authentication management field + * @mac_a: Buffer for MAC-A = 64-bit network authentication code, or %NULL + * @mac_s: Buffer for MAC-S = 64-bit resync authentication code, or %NULL + * Returns: 0 on success, -1 on failure + */ +int milenage_f1(const u8 *opc, const u8 *k, const u8 *_rand, + const u8 *sqn, const u8 *amf, u8 *mac_a, u8 *mac_s) +{ + u8 tmp1[16], tmp2[16], tmp3[16]; + int i; + + /* tmp1 = TEMP = E_K(RAND XOR OP_C) */ + for (i = 0; i < 16; i++) + tmp1[i] = _rand[i] ^ opc[i]; + if (aes_128_encrypt_block(k, tmp1, tmp1)) + return -1; + + /* tmp2 = IN1 = SQN || AMF || SQN || AMF */ + os_memcpy(tmp2, sqn, 6); + os_memcpy(tmp2 + 6, amf, 2); + os_memcpy(tmp2 + 8, tmp2, 8); + + /* OUT1 = E_K(TEMP XOR rot(IN1 XOR OP_C, r1) XOR c1) XOR OP_C */ + + /* rotate (tmp2 XOR OP_C) by r1 (= 0x40 = 8 bytes) */ + for (i = 0; i < 16; i++) + tmp3[(i + 8) % 16] = tmp2[i] ^ opc[i]; + /* XOR with TEMP = E_K(RAND XOR OP_C) */ + for (i = 0; i < 16; i++) + tmp3[i] ^= tmp1[i]; + /* XOR with c1 (= ..00, i.e., NOP) */ + + /* f1 || f1* = E_K(tmp3) XOR OP_c */ + if (aes_128_encrypt_block(k, tmp3, tmp1)) + return -1; + for (i = 0; i < 16; i++) + tmp1[i] ^= opc[i]; + if (mac_a) + os_memcpy(mac_a, tmp1, 8); /* f1 */ + if (mac_s) + os_memcpy(mac_s, tmp1 + 8, 8); /* f1* */ + return 0; +} + + +/** + * milenage_f2345 - Milenage f2, f3, f4, f5, f5* algorithms + * @opc: OPc = 128-bit value derived from OP and K + * @k: K = 128-bit subscriber key + * @_rand: RAND = 128-bit random challenge + * @res: Buffer for RES = 64-bit signed response (f2), or %NULL + * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL + * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL + * @ak: Buffer for AK = 48-bit anonymity key (f5), or %NULL + * @akstar: Buffer for AK = 48-bit anonymity key (f5*), or %NULL + * Returns: 0 on success, -1 on failure + */ +int milenage_f2345(const u8 *opc, const u8 *k, const u8 *_rand, + u8 *res, u8 *ck, u8 *ik, u8 *ak, u8 *akstar) +{ + u8 tmp1[16], tmp2[16], tmp3[16]; + int i; + + /* tmp2 = TEMP = E_K(RAND XOR OP_C) */ + for (i = 0; i < 16; i++) + tmp1[i] = _rand[i] ^ opc[i]; + if (aes_128_encrypt_block(k, tmp1, tmp2)) + return -1; + + /* OUT2 = E_K(rot(TEMP XOR OP_C, r2) XOR c2) XOR OP_C */ + /* OUT3 = E_K(rot(TEMP XOR OP_C, r3) XOR c3) XOR OP_C */ + /* OUT4 = E_K(rot(TEMP XOR OP_C, r4) XOR c4) XOR OP_C */ + /* OUT5 = E_K(rot(TEMP XOR OP_C, r5) XOR c5) XOR OP_C */ + + /* f2 and f5 */ + /* rotate by r2 (= 0, i.e., NOP) */ + for (i = 0; i < 16; i++) + tmp1[i] = tmp2[i] ^ opc[i]; + tmp1[15] ^= 1; /* XOR c2 (= ..01) */ + /* f5 || f2 = E_K(tmp1) XOR OP_c */ + if (aes_128_encrypt_block(k, tmp1, tmp3)) + return -1; + for (i = 0; i < 16; i++) + tmp3[i] ^= opc[i]; + if (res) + os_memcpy(res, tmp3 + 8, 8); /* f2 */ + if (ak) + os_memcpy(ak, tmp3, 6); /* f5 */ + + /* f3 */ + if (ck) { + /* rotate by r3 = 0x20 = 4 bytes */ + for (i = 0; i < 16; i++) + tmp1[(i + 12) % 16] = tmp2[i] ^ opc[i]; + tmp1[15] ^= 2; /* XOR c3 (= ..02) */ + if (aes_128_encrypt_block(k, tmp1, ck)) + return -1; + for (i = 0; i < 16; i++) + ck[i] ^= opc[i]; + } + + /* f4 */ + if (ik) { + /* rotate by r4 = 0x40 = 8 bytes */ + for (i = 0; i < 16; i++) + tmp1[(i + 8) % 16] = tmp2[i] ^ opc[i]; + tmp1[15] ^= 4; /* XOR c4 (= ..04) */ + if (aes_128_encrypt_block(k, tmp1, ik)) + return -1; + for (i = 0; i < 16; i++) + ik[i] ^= opc[i]; + } + + /* f5* */ + if (akstar) { + /* rotate by r5 = 0x60 = 12 bytes */ + for (i = 0; i < 16; i++) + tmp1[(i + 4) % 16] = tmp2[i] ^ opc[i]; + tmp1[15] ^= 8; /* XOR c5 (= ..08) */ + if (aes_128_encrypt_block(k, tmp1, tmp1)) + return -1; + for (i = 0; i < 6; i++) + akstar[i] = tmp1[i] ^ opc[i]; + } + + return 0; +} + + +/** + * milenage_generate - Generate AKA AUTN,IK,CK,RES + * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.) + * @amf: AMF = 16-bit authentication management field + * @k: K = 128-bit subscriber key + * @sqn: SQN = 48-bit sequence number + * @_rand: RAND = 128-bit random challenge + * @autn: Buffer for AUTN = 128-bit authentication token + * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL + * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL + * @res: Buffer for RES = 64-bit signed response (f2), or %NULL + * @res_len: Max length for res; set to used length or 0 on failure + */ +void milenage_generate(const u8 *opc, const u8 *amf, const u8 *k, + const u8 *sqn, const u8 *_rand, u8 *autn, u8 *ik, + u8 *ck, u8 *res, size_t *res_len) +{ + int i; + u8 mac_a[8], ak[6]; + + if (*res_len < 8) { + *res_len = 0; + return; + } + if (milenage_f1(opc, k, _rand, sqn, amf, mac_a, NULL) || + milenage_f2345(opc, k, _rand, res, ck, ik, ak, NULL)) { + *res_len = 0; + return; + } + *res_len = 8; + + /* AUTN = (SQN ^ AK) || AMF || MAC */ + for (i = 0; i < 6; i++) + autn[i] = sqn[i] ^ ak[i]; + os_memcpy(autn + 6, amf, 2); + os_memcpy(autn + 8, mac_a, 8); +} + + +/** + * milenage_auts - Milenage AUTS validation + * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.) + * @k: K = 128-bit subscriber key + * @_rand: RAND = 128-bit random challenge + * @auts: AUTS = 112-bit authentication token from client + * @sqn: Buffer for SQN = 48-bit sequence number + * Returns: 0 = success (sqn filled), -1 on failure + */ +int milenage_auts(const u8 *opc, const u8 *k, const u8 *_rand, const u8 *auts, + u8 *sqn) +{ + u8 amf[2] = { 0x00, 0x00 }; /* TS 33.102 v7.0.0, 6.3.3 */ + u8 ak[6], mac_s[8]; + int i; + + if (milenage_f2345(opc, k, _rand, NULL, NULL, NULL, NULL, ak)) + return -1; + for (i = 0; i < 6; i++) + sqn[i] = auts[i] ^ ak[i]; + if (milenage_f1(opc, k, _rand, sqn, amf, NULL, mac_s) || + memcmp(mac_s, auts + 6, 8) != 0) + return -1; + return 0; +} + + +/** + * gsm_milenage - Generate GSM-Milenage (3GPP TS 55.205) authentication triplet + * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.) + * @k: K = 128-bit subscriber key + * @_rand: RAND = 128-bit random challenge + * @sres: Buffer for SRES = 32-bit SRES + * @kc: Buffer for Kc = 64-bit Kc + * Returns: 0 on success, -1 on failure + */ +int gsm_milenage(const u8 *opc, const u8 *k, const u8 *_rand, u8 *sres, u8 *kc) +{ + u8 res[8], ck[16], ik[16]; + int i; + + if (milenage_f2345(opc, k, _rand, res, ck, ik, NULL, NULL)) + return -1; + + for (i = 0; i < 8; i++) + kc[i] = ck[i] ^ ck[i + 8] ^ ik[i] ^ ik[i + 8]; + +#ifdef GSM_MILENAGE_ALT_SRES + os_memcpy(sres, res, 4); +#else /* GSM_MILENAGE_ALT_SRES */ + for (i = 0; i < 4; i++) + sres[i] = res[i] ^ res[i + 4]; +#endif /* GSM_MILENAGE_ALT_SRES */ + return 0; +} + + +/** + * milenage_generate - Generate AKA AUTN,IK,CK,RES + * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.) + * @k: K = 128-bit subscriber key + * @sqn: SQN = 48-bit sequence number + * @_rand: RAND = 128-bit random challenge + * @autn: AUTN = 128-bit authentication token + * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL + * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL + * @res: Buffer for RES = 64-bit signed response (f2), or %NULL + * @res_len: Variable that will be set to RES length + * @auts: 112-bit buffer for AUTS + * Returns: 0 on success, -1 on failure, or -2 on synchronization failure + */ +int milenage_check(const u8 *opc, const u8 *k, const u8 *sqn, const u8 *_rand, + const u8 *autn, u8 *ik, u8 *ck, u8 *res, size_t *res_len, + u8 *auts) +{ + int i; + u8 mac_a[8], ak[6], rx_sqn[6]; + const u8 *amf; + + wpa_hexdump(MSG_DEBUG, "Milenage: AUTN", autn, 16); + wpa_hexdump(MSG_DEBUG, "Milenage: RAND", _rand, 16); + + if (milenage_f2345(opc, k, _rand, res, ck, ik, ak, NULL)) + return -1; + + *res_len = 8; + wpa_hexdump_key(MSG_DEBUG, "Milenage: RES", res, *res_len); + wpa_hexdump_key(MSG_DEBUG, "Milenage: CK", ck, 16); + wpa_hexdump_key(MSG_DEBUG, "Milenage: IK", ik, 16); + wpa_hexdump_key(MSG_DEBUG, "Milenage: AK", ak, 6); + + /* AUTN = (SQN ^ AK) || AMF || MAC */ + for (i = 0; i < 6; i++) + rx_sqn[i] = autn[i] ^ ak[i]; + wpa_hexdump(MSG_DEBUG, "Milenage: SQN", rx_sqn, 6); + + if (os_memcmp(rx_sqn, sqn, 6) <= 0) { + u8 auts_amf[2] = { 0x00, 0x00 }; /* TS 33.102 v7.0.0, 6.3.3 */ + if (milenage_f2345(opc, k, _rand, NULL, NULL, NULL, NULL, ak)) + return -1; + wpa_hexdump_key(MSG_DEBUG, "Milenage: AK*", ak, 6); + for (i = 0; i < 6; i++) + auts[i] = sqn[i] ^ ak[i]; + if (milenage_f1(opc, k, _rand, sqn, auts_amf, NULL, auts + 6)) + return -1; + wpa_hexdump(MSG_DEBUG, "Milenage: AUTS", auts, 14); + return -2; + } + + amf = autn + 6; + wpa_hexdump(MSG_DEBUG, "Milenage: AMF", amf, 2); + if (milenage_f1(opc, k, _rand, rx_sqn, amf, mac_a, NULL)) + return -1; + + wpa_hexdump(MSG_DEBUG, "Milenage: MAC_A", mac_a, 8); + + if (os_memcmp(mac_a, autn + 8, 8) != 0) { + wpa_printf(MSG_DEBUG, "Milenage: MAC mismatch"); + wpa_hexdump(MSG_DEBUG, "Milenage: Received MAC_A", + autn + 8, 8); + return -1; + } + + return 0; +} diff --git a/contrib/wpa/src/hlr_auc_gw/milenage.h b/contrib/wpa/src/crypto/milenage.h index b35603c..d5054d6 100644 --- a/contrib/wpa/src/hlr_auc_gw/milenage.h +++ b/contrib/wpa/src/crypto/milenage.h @@ -25,5 +25,9 @@ int gsm_milenage(const u8 *opc, const u8 *k, const u8 *_rand, u8 *sres, int milenage_check(const u8 *opc, const u8 *k, const u8 *sqn, const u8 *_rand, const u8 *autn, u8 *ik, u8 *ck, u8 *res, size_t *res_len, u8 *auts); +int milenage_f1(const u8 *opc, const u8 *k, const u8 *_rand, + const u8 *sqn, const u8 *amf, u8 *mac_a, u8 *mac_s); +int milenage_f2345(const u8 *opc, const u8 *k, const u8 *_rand, + u8 *res, u8 *ck, u8 *ik, u8 *ak, u8 *akstar); #endif /* MILENAGE_H */ diff --git a/contrib/wpa/src/crypto/ms_funcs.c b/contrib/wpa/src/crypto/ms_funcs.c index 7e2f0fa..dae15ab 100644 --- a/contrib/wpa/src/crypto/ms_funcs.c +++ b/contrib/wpa/src/crypto/ms_funcs.c @@ -1,6 +1,6 @@ /* * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759 - * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -18,7 +18,6 @@ #include "sha1.h" #include "ms_funcs.h" #include "crypto.h" -#include "rc4.h" /** @@ -28,10 +27,11 @@ * @username: 0-to-256-char UserName (IN) * @username_len: Length of username * @challenge: 8-octet Challenge (OUT) + * Returns: 0 on success, -1 on failure */ -static void challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge, - const u8 *username, size_t username_len, - u8 *challenge) +static int challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge, + const u8 *username, size_t username_len, + u8 *challenge) { u8 hash[SHA1_MAC_LEN]; const unsigned char *addr[3]; @@ -44,8 +44,10 @@ static void challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge, addr[2] = username; len[2] = username_len; - sha1_vector(3, addr, len, hash); + if (sha1_vector(3, addr, len, hash)) + return -1; os_memcpy(challenge, hash, 8); + return 0; } @@ -54,8 +56,9 @@ static void challenge_hash(const u8 *peer_challenge, const u8 *auth_challenge, * @password: 0-to-256-unicode-char Password (IN; ASCII) * @password_len: Length of password * @password_hash: 16-octet PasswordHash (OUT) + * Returns: 0 on success, -1 on failure */ -void nt_password_hash(const u8 *password, size_t password_len, +int nt_password_hash(const u8 *password, size_t password_len, u8 *password_hash) { u8 buf[512], *pos; @@ -72,7 +75,7 @@ void nt_password_hash(const u8 *password, size_t password_len, len = password_len * 2; pos = buf; - md4_vector(1, (const u8 **) &pos, &len, password_hash); + return md4_vector(1, (const u8 **) &pos, &len, password_hash); } @@ -80,11 +83,12 @@ void nt_password_hash(const u8 *password, size_t password_len, * hash_nt_password_hash - HashNtPasswordHash() - RFC 2759, Sect. 8.4 * @password_hash: 16-octet PasswordHash (IN) * @password_hash_hash: 16-octet PasswordHashHash (OUT) + * Returns: 0 on success, -1 on failure */ -void hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash) +int hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash) { size_t len = 16; - md4_vector(1, &password_hash, &len, password_hash_hash); + return md4_vector(1, &password_hash, &len, password_hash_hash); } @@ -116,19 +120,22 @@ void challenge_response(const u8 *challenge, const u8 *password_hash, * @password: 0-to-256-unicode-char Password (IN; ASCII) * @password_len: Length of password * @response: 24-octet Response (OUT) + * Returns: 0 on success, -1 on failure */ -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) +int 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]; u8 password_hash[16]; challenge_hash(peer_challenge, auth_challenge, username, username_len, challenge); - nt_password_hash(password, password_len, password_hash); + if (nt_password_hash(password, password_len, password_hash)) + return -1; challenge_response(challenge, password_hash, response); + return 0; } @@ -140,18 +147,22 @@ void generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, * @username_len: Length of username * @password_hash: 16-octet PasswordHash (IN) * @response: 24-octet Response (OUT) + * Returns: 0 on success, -1 on failure */ -void generate_nt_response_pwhash(const u8 *auth_challenge, - const u8 *peer_challenge, - const u8 *username, size_t username_len, - const u8 *password_hash, - u8 *response) +int generate_nt_response_pwhash(const u8 *auth_challenge, + const u8 *peer_challenge, + const u8 *username, size_t username_len, + const u8 *password_hash, + u8 *response) { u8 challenge[8]; - challenge_hash(peer_challenge, auth_challenge, username, username_len, - challenge); + if (challenge_hash(peer_challenge, auth_challenge, + username, username_len, + challenge)) + return -1; challenge_response(challenge, password_hash, response); + return 0; } @@ -165,8 +176,9 @@ void generate_nt_response_pwhash(const u8 *auth_challenge, * @username_len: Length of username * @response: 20-octet AuthenticatorResponse (OUT) (note: this value is usually * encoded as a 42-octet ASCII string (S=hexdump_of_response) + * Returns: 0 on success, -1 on failure */ -void generate_authenticator_response_pwhash( +int generate_authenticator_response_pwhash( const u8 *password_hash, const u8 *peer_challenge, const u8 *auth_challenge, const u8 *username, size_t username_len, @@ -200,12 +212,14 @@ void generate_authenticator_response_pwhash( addr2[1] = challenge; addr2[2] = magic2; - hash_nt_password_hash(password_hash, password_hash_hash); - sha1_vector(3, addr1, len1, response); + if (hash_nt_password_hash(password_hash, password_hash_hash)) + return -1; + if (sha1_vector(3, addr1, len1, response)) + return -1; challenge_hash(peer_challenge, auth_challenge, username, username_len, challenge); - sha1_vector(3, addr2, len2, response); + return sha1_vector(3, addr2, len2, response); } @@ -220,19 +234,20 @@ void generate_authenticator_response_pwhash( * @username_len: Length of username * @response: 20-octet AuthenticatorResponse (OUT) (note: this value is usually * encoded as a 42-octet ASCII string (S=hexdump_of_response) + * Returns: 0 on success, -1 on failure */ -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) +int generate_authenticator_response(const u8 *password, size_t password_len, + const u8 *peer_challenge, + const u8 *auth_challenge, + const u8 *username, size_t username_len, + const u8 *nt_response, u8 *response) { u8 password_hash[16]; - nt_password_hash(password, password_len, password_hash); - generate_authenticator_response_pwhash(password_hash, - peer_challenge, auth_challenge, - username, username_len, - nt_response, response); + if (nt_password_hash(password, password_len, password_hash)) + return -1; + return generate_authenticator_response_pwhash( + password_hash, peer_challenge, auth_challenge, + username, username_len, nt_response, response); } @@ -242,13 +257,16 @@ void generate_authenticator_response(const u8 *password, size_t password_len, * @password: 0-to-256-unicode-char Password (IN; ASCII) * @password_len: Length of password * @response: 24-octet Response (OUT) + * Returns: 0 on success, -1 on failure */ -void nt_challenge_response(const u8 *challenge, const u8 *password, - size_t password_len, u8 *response) +int 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); + if (nt_password_hash(password, password_len, password_hash)) + return -1; challenge_response(challenge, password_hash, response); + return 0; } @@ -257,9 +275,10 @@ void nt_challenge_response(const u8 *challenge, const u8 *password, * @password_hash_hash: 16-octet PasswordHashHash (IN) * @nt_response: 24-octet NTResponse (IN) * @master_key: 16-octet MasterKey (OUT) + * Returns: 0 on success, -1 on failure */ -void get_master_key(const u8 *password_hash_hash, const u8 *nt_response, - u8 *master_key) +int get_master_key(const u8 *password_hash_hash, const u8 *nt_response, + u8 *master_key) { static const u8 magic1[27] = { 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x74, @@ -274,8 +293,10 @@ void get_master_key(const u8 *password_hash_hash, const u8 *nt_response, addr[1] = nt_response; addr[2] = magic1; - sha1_vector(3, addr, len, hash); + if (sha1_vector(3, addr, len, hash)) + return -1; os_memcpy(master_key, hash, 16); + return 0; } @@ -286,10 +307,11 @@ void get_master_key(const u8 *password_hash_hash, const u8 *nt_response, * @session_key_len: SessionKeyLength (Length of session_key) (IN) * @is_send: IsSend (IN, BOOLEAN) * @is_server: IsServer (IN, BOOLEAN) + * Returns: 0 on success, -1 on failure */ -void get_asymetric_start_key(const u8 *master_key, u8 *session_key, - size_t session_key_len, int is_send, - int is_server) +int get_asymetric_start_key(const u8 *master_key, u8 *session_key, + size_t session_key_len, int is_send, + int is_server) { static const u8 magic2[84] = { 0x4f, 0x6e, 0x20, 0x74, 0x68, 0x65, 0x20, 0x63, 0x6c, 0x69, @@ -339,11 +361,13 @@ void get_asymetric_start_key(const u8 *master_key, u8 *session_key, } addr[3] = shs_pad2; - sha1_vector(4, addr, len, digest); + if (sha1_vector(4, addr, len, digest)) + return -1; if (session_key_len > SHA1_MAC_LEN) session_key_len = SHA1_MAC_LEN; os_memcpy(session_key, digest, session_key_len); + return 0; } @@ -400,7 +424,8 @@ int new_password_encrypted_with_old_nt_password_hash( { u8 password_hash[16]; - nt_password_hash(old_password, old_password_len, password_hash); + if (nt_password_hash(old_password, old_password_len, password_hash)) + return -1; if (encrypt_pw_block_with_password_hash(new_password, new_password_len, password_hash, encrypted_pw_block)) @@ -430,17 +455,22 @@ void nt_password_hash_encrypted_with_block(const u8 *password_hash, * @old_password: 0-to-256-unicode-char OldPassword (IN; ASCII) * @old_password_len: Length of old_password * @encrypted_password_hash: 16-octet EncryptedPasswordHash (OUT) + * Returns: 0 on success, -1 on failure */ -void old_nt_password_hash_encrypted_with_new_nt_password_hash( +int 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); + if (nt_password_hash(old_password, old_password_len, + old_password_hash) || + nt_password_hash(new_password, new_password_len, + new_password_hash)) + return -1; nt_password_hash_encrypted_with_block(old_password_hash, new_password_hash, encrypted_password_hash); + return 0; } diff --git a/contrib/wpa/src/crypto/ms_funcs.h b/contrib/wpa/src/crypto/ms_funcs.h index 6205bf6..298dbcf 100644 --- a/contrib/wpa/src/crypto/ms_funcs.h +++ b/contrib/wpa/src/crypto/ms_funcs.h @@ -1,6 +1,6 @@ /* * WPA Supplicant / shared MSCHAPV2 helper functions / RFC 2433 / RFC 2759 - * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -15,38 +15,38 @@ #ifndef MS_FUNCS_H #define MS_FUNCS_H -void generate_nt_response(const u8 *auth_challenge, const u8 *peer_challenge, - const u8 *username, size_t username_len, - const u8 *password, size_t password_len, - u8 *response); -void generate_nt_response_pwhash(const u8 *auth_challenge, - const u8 *peer_challenge, - const u8 *username, size_t username_len, - const u8 *password_hash, - u8 *response); -void generate_authenticator_response(const u8 *password, size_t password_len, - const u8 *peer_challenge, - const u8 *auth_challenge, - const u8 *username, size_t username_len, - const u8 *nt_response, u8 *response); -void generate_authenticator_response_pwhash( +int 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); +int generate_nt_response_pwhash(const u8 *auth_challenge, + const u8 *peer_challenge, + const u8 *username, size_t username_len, + const u8 *password_hash, + u8 *response); +int 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); +int generate_authenticator_response_pwhash( const u8 *password_hash, const u8 *peer_challenge, const u8 *auth_challenge, const u8 *username, size_t username_len, const u8 *nt_response, u8 *response); -void nt_challenge_response(const u8 *challenge, const u8 *password, - size_t password_len, u8 *response); +int nt_challenge_response(const u8 *challenge, const u8 *password, + size_t password_len, u8 *response); 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); +int nt_password_hash(const u8 *password, size_t password_len, + u8 *password_hash); +int hash_nt_password_hash(const u8 *password_hash, u8 *password_hash_hash); +int get_master_key(const u8 *password_hash_hash, const u8 *nt_response, + u8 *master_key); +int get_asymetric_start_key(const u8 *master_key, u8 *session_key, + size_t session_key_len, int is_send, + int is_server); int __must_check encrypt_pw_block_with_password_hash( const u8 *password, size_t password_len, const u8 *password_hash, u8 *pw_block); @@ -56,7 +56,7 @@ int __must_check new_password_encrypted_with_old_nt_password_hash( u8 *encrypted_pw_block); void nt_password_hash_encrypted_with_block(const u8 *password_hash, const u8 *block, u8 *cypher); -void old_nt_password_hash_encrypted_with_new_nt_password_hash( +int 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); diff --git a/contrib/wpa/src/crypto/rc4.c b/contrib/wpa/src/crypto/rc4.c index 70c790e..5ab1be1 100644 --- a/contrib/wpa/src/crypto/rc4.c +++ b/contrib/wpa/src/crypto/rc4.c @@ -15,24 +15,12 @@ #include "includes.h" #include "common.h" -#include "rc4.h" +#include "crypto.h" #define S_SWAP(a,b) do { u8 t = S[a]; S[a] = S[b]; S[b] = t; } while(0) -/** - * 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) +int 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; @@ -67,4 +55,6 @@ void rc4_skip(const u8 *key, size_t keylen, size_t skip, S_SWAP(i, j); *pos++ ^= S[(S[i] + S[j]) & 0xff]; } + + return 0; } diff --git a/contrib/wpa/src/crypto/sha1-internal.c b/contrib/wpa/src/crypto/sha1-internal.c new file mode 100644 index 0000000..3f05ca1 --- /dev/null +++ b/contrib/wpa/src/crypto/sha1-internal.c @@ -0,0 +1,308 @@ +/* + * SHA1 hash implementation and interface functions + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha1.h" +#include "sha1_i.h" +#include "md5.h" +#include "crypto.h" + +typedef struct SHA1Context SHA1_CTX; + +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 + * Returns: 0 on success, -1 of failure + */ +int sha1_vector(size_t num_elem, const u8 *addr[], const size_t *len, u8 *mac) +{ + SHA1_CTX ctx; + size_t i; + + SHA1Init(&ctx); + for (i = 0; i < num_elem; i++) + SHA1Update(&ctx, addr[i], len[i]); + SHA1Final(mac, &ctx); + return 0; +} + + +/* ===== start - public domain SHA1 implementation ===== */ + +/* +SHA-1 in C +By Steve Reid <sreid@sea-to-sky.net> +100% Public Domain + +----------------- +Modified 7/98 +By James H. Brown <jbrown@burgoyne.com> +Still 100% Public Domain + +Corrected a problem which generated improper hash values on 16 bit machines +Routine SHA1Update changed from + void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int +len) +to + void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned +long len) + +The 'len' parameter was declared an int which works fine on 32 bit machines. +However, on 16 bit machines an int is too small for the shifts being done +against +it. This caused the hash function to generate incorrect values if len was +greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update(). + +Since the file IO in main() reads 16K at a time, any file 8K or larger would +be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million +"a"s). + +I also changed the declaration of variables i & j in SHA1Update to +unsigned long from unsigned int for the same reason. + +These changes should make no difference to any 32 bit implementations since +an +int and a long are the same size in those environments. + +-- +I also corrected a few compiler warnings generated by Borland C. +1. Added #include <process.h> for exit() prototype +2. Removed unused variable 'j' in SHA1Final +3. Changed exit(0) to return(0) at end of main. + +ALL changes I made can be located by searching for comments containing 'JHB' +----------------- +Modified 8/98 +By Steve Reid <sreid@sea-to-sky.net> +Still 100% public domain + +1- Removed #include <process.h> and used return() instead of exit() +2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall) +3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net + +----------------- +Modified 4/01 +By Saul Kravitz <Saul.Kravitz@celera.com> +Still 100% PD +Modified to run on Compaq Alpha hardware. + +----------------- +Modified 4/01 +By Jouni Malinen <j@w1.fi> +Minor changes to match the coding style used in Dynamics. + +Modified September 24, 2004 +By Jouni Malinen <j@w1.fi> +Fixed alignment issue in SHA1Transform when SHA1HANDSOFF is defined. + +*/ + +/* +Test Vectors (from FIPS PUB 180-1) +"abc" + A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D +"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 +A million repetitions of "a" + 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F +*/ + +#define SHA1HANDSOFF + +#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) + +/* blk0() and blk() perform the initial expand. */ +/* I got the idea of expanding during the round function from SSLeay */ +#ifndef WORDS_BIGENDIAN +#define blk0(i) (block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | \ + (rol(block->l[i], 8) & 0x00FF00FF)) +#else +#define blk0(i) block->l[i] +#endif +#define blk(i) (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ \ + block->l[(i + 8) & 15] ^ block->l[(i + 2) & 15] ^ block->l[i & 15], 1)) + +/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ +#define R0(v,w,x,y,z,i) \ + z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \ + w = rol(w, 30); +#define R1(v,w,x,y,z,i) \ + z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \ + w = rol(w, 30); +#define R2(v,w,x,y,z,i) \ + z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); w = rol(w, 30); +#define R3(v,w,x,y,z,i) \ + z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \ + w = rol(w, 30); +#define R4(v,w,x,y,z,i) \ + z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \ + w=rol(w, 30); + + +#ifdef VERBOSE /* SAK */ +void SHAPrintContext(SHA1_CTX *context, char *msg) +{ + printf("%s (%d,%d) %x %x %x %x %x\n", + msg, + context->count[0], context->count[1], + context->state[0], + context->state[1], + context->state[2], + context->state[3], + context->state[4]); +} +#endif + +/* Hash a single 512-bit block. This is the core of the algorithm. */ + +void SHA1Transform(u32 state[5], const unsigned char buffer[64]) +{ + u32 a, b, c, d, e; + typedef union { + unsigned char c[64]; + u32 l[16]; + } CHAR64LONG16; + CHAR64LONG16* block; +#ifdef SHA1HANDSOFF + CHAR64LONG16 workspace; + block = &workspace; + os_memcpy(block, buffer, 64); +#else + block = (CHAR64LONG16 *) buffer; +#endif + /* Copy context->state[] to working vars */ + a = state[0]; + b = state[1]; + c = state[2]; + d = state[3]; + e = state[4]; + /* 4 rounds of 20 operations each. Loop unrolled. */ + R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); + R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); + R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); + R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); + R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); + R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); + R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); + R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); + R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); + R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); + R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); + R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); + R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); + R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); + R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); + R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); + R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); + R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); + R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); + R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); + /* Add the working vars back into context.state[] */ + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + state[4] += e; + /* Wipe variables */ + a = b = c = d = e = 0; +#ifdef SHA1HANDSOFF + os_memset(block, 0, 64); +#endif +} + + +/* SHA1Init - Initialize new context */ + +void SHA1Init(SHA1_CTX* context) +{ + /* SHA1 initialization constants */ + context->state[0] = 0x67452301; + context->state[1] = 0xEFCDAB89; + context->state[2] = 0x98BADCFE; + context->state[3] = 0x10325476; + context->state[4] = 0xC3D2E1F0; + context->count[0] = context->count[1] = 0; +} + + +/* Run your data through this. */ + +void SHA1Update(SHA1_CTX* context, const void *_data, u32 len) +{ + u32 i, j; + const unsigned char *data = _data; + +#ifdef VERBOSE + SHAPrintContext(context, "before"); +#endif + j = (context->count[0] >> 3) & 63; + if ((context->count[0] += len << 3) < (len << 3)) + context->count[1]++; + context->count[1] += (len >> 29); + if ((j + len) > 63) { + os_memcpy(&context->buffer[j], data, (i = 64-j)); + SHA1Transform(context->state, context->buffer); + for ( ; i + 63 < len; i += 64) { + SHA1Transform(context->state, &data[i]); + } + j = 0; + } + else i = 0; + os_memcpy(&context->buffer[j], &data[i], len - i); +#ifdef VERBOSE + SHAPrintContext(context, "after "); +#endif +} + + +/* Add padding and return the message digest. */ + +void SHA1Final(unsigned char digest[20], SHA1_CTX* context) +{ + u32 i; + unsigned char finalcount[8]; + + for (i = 0; i < 8; i++) { + finalcount[i] = (unsigned char) + ((context->count[(i >= 4 ? 0 : 1)] >> + ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ + } + SHA1Update(context, (unsigned char *) "\200", 1); + while ((context->count[0] & 504) != 448) { + SHA1Update(context, (unsigned char *) "\0", 1); + } + SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() + */ + for (i = 0; i < 20; i++) { + digest[i] = (unsigned char) + ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & + 255); + } + /* Wipe variables */ + i = 0; + os_memset(context->buffer, 0, 64); + os_memset(context->state, 0, 20); + os_memset(context->count, 0, 8); + os_memset(finalcount, 0, 8); +} + +/* ===== end - public domain SHA1 implementation ===== */ diff --git a/contrib/wpa/src/crypto/sha1-pbkdf2.c b/contrib/wpa/src/crypto/sha1-pbkdf2.c new file mode 100644 index 0000000..11323de --- /dev/null +++ b/contrib/wpa/src/crypto/sha1-pbkdf2.c @@ -0,0 +1,100 @@ +/* + * SHA1-based key derivation function (PBKDF2) for IEEE 802.11i + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha1.h" +#include "md5.h" +#include "crypto.h" + +static int pbkdf2_sha1_f(const char *passphrase, const char *ssid, + size_t ssid_len, int iterations, unsigned int count, + u8 *digest) +{ + unsigned char tmp[SHA1_MAC_LEN], tmp2[SHA1_MAC_LEN]; + int i, j; + unsigned char count_buf[4]; + const u8 *addr[2]; + size_t len[2]; + size_t passphrase_len = os_strlen(passphrase); + + addr[0] = (u8 *) ssid; + len[0] = ssid_len; + addr[1] = count_buf; + len[1] = 4; + + /* F(P, S, c, i) = U1 xor U2 xor ... Uc + * U1 = PRF(P, S || i) + * U2 = PRF(P, U1) + * Uc = PRF(P, Uc-1) + */ + + count_buf[0] = (count >> 24) & 0xff; + count_buf[1] = (count >> 16) & 0xff; + count_buf[2] = (count >> 8) & 0xff; + count_buf[3] = count & 0xff; + if (hmac_sha1_vector((u8 *) passphrase, passphrase_len, 2, addr, len, + tmp)) + return -1; + os_memcpy(digest, tmp, SHA1_MAC_LEN); + + for (i = 1; i < iterations; i++) { + if (hmac_sha1((u8 *) passphrase, passphrase_len, tmp, + SHA1_MAC_LEN, tmp2)) + return -1; + os_memcpy(tmp, tmp2, SHA1_MAC_LEN); + for (j = 0; j < SHA1_MAC_LEN; j++) + digest[j] ^= tmp2[j]; + } + + return 0; +} + + +/** + * pbkdf2_sha1 - SHA1-based key derivation function (PBKDF2) for IEEE 802.11i + * @passphrase: ASCII passphrase + * @ssid: SSID + * @ssid_len: SSID length in bytes + * @iterations: Number of iterations to run + * @buf: Buffer for the generated key + * @buflen: Length of the buffer in bytes + * Returns: 0 on success, -1 of failure + * + * 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. + */ +int pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len, + int iterations, u8 *buf, size_t buflen) +{ + unsigned int count = 0; + unsigned char *pos = buf; + size_t left = buflen, plen; + unsigned char digest[SHA1_MAC_LEN]; + + while (left > 0) { + count++; + if (pbkdf2_sha1_f(passphrase, ssid, ssid_len, iterations, + count, digest)) + return -1; + plen = left > SHA1_MAC_LEN ? SHA1_MAC_LEN : left; + os_memcpy(pos, digest, plen); + pos += plen; + left -= plen; + } + + return 0; +} diff --git a/contrib/wpa/src/crypto/sha1-tlsprf.c b/contrib/wpa/src/crypto/sha1-tlsprf.c new file mode 100644 index 0000000..2c8c029 --- /dev/null +++ b/contrib/wpa/src/crypto/sha1-tlsprf.c @@ -0,0 +1,109 @@ +/* + * TLS PRF (SHA1 + MD5) + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha1.h" +#include "md5.h" +#include "crypto.h" + + +/** + * 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 + * Returns: 0 on success, -1 on failure. + * + * This function is used to derive new, cryptographically separate keys from a + * given key in TLS. This PRF is defined in RFC 2246, Chapter 5. + */ +int tls_prf(const u8 *secret, size_t secret_len, const char *label, + const u8 *seed, size_t seed_len, u8 *out, size_t outlen) +{ + size_t L_S1, L_S2, i; + const u8 *S1, *S2; + u8 A_MD5[MD5_MAC_LEN], A_SHA1[SHA1_MAC_LEN]; + u8 P_MD5[MD5_MAC_LEN], P_SHA1[SHA1_MAC_LEN]; + int MD5_pos, SHA1_pos; + const u8 *MD5_addr[3]; + size_t MD5_len[3]; + const unsigned char *SHA1_addr[3]; + size_t SHA1_len[3]; + + if (secret_len & 1) + return -1; + + MD5_addr[0] = A_MD5; + MD5_len[0] = MD5_MAC_LEN; + MD5_addr[1] = (unsigned char *) label; + MD5_len[1] = os_strlen(label); + MD5_addr[2] = seed; + MD5_len[2] = seed_len; + + SHA1_addr[0] = A_SHA1; + SHA1_len[0] = SHA1_MAC_LEN; + SHA1_addr[1] = (unsigned char *) label; + SHA1_len[1] = os_strlen(label); + SHA1_addr[2] = seed; + SHA1_len[2] = seed_len; + + /* RFC 2246, Chapter 5 + * A(0) = seed, A(i) = HMAC(secret, A(i-1)) + * P_hash = HMAC(secret, A(1) + seed) + HMAC(secret, A(2) + seed) + .. + * PRF = P_MD5(S1, label + seed) XOR P_SHA-1(S2, label + seed) + */ + + L_S1 = L_S2 = (secret_len + 1) / 2; + S1 = secret; + S2 = secret + L_S1; + if (secret_len & 1) { + /* The last byte of S1 will be shared with S2 */ + S2--; + } + + hmac_md5_vector_non_fips_allow(S1, L_S1, 2, &MD5_addr[1], &MD5_len[1], + A_MD5); + hmac_sha1_vector(S2, L_S2, 2, &SHA1_addr[1], &SHA1_len[1], A_SHA1); + + MD5_pos = MD5_MAC_LEN; + SHA1_pos = SHA1_MAC_LEN; + for (i = 0; i < outlen; i++) { + if (MD5_pos == MD5_MAC_LEN) { + hmac_md5_vector_non_fips_allow(S1, L_S1, 3, MD5_addr, + MD5_len, P_MD5); + MD5_pos = 0; + hmac_md5_non_fips_allow(S1, L_S1, A_MD5, MD5_MAC_LEN, + A_MD5); + } + if (SHA1_pos == SHA1_MAC_LEN) { + hmac_sha1_vector(S2, L_S2, 3, SHA1_addr, SHA1_len, + P_SHA1); + SHA1_pos = 0; + hmac_sha1(S2, L_S2, A_SHA1, SHA1_MAC_LEN, A_SHA1); + } + + out[i] = P_MD5[MD5_pos] ^ P_SHA1[SHA1_pos]; + + MD5_pos++; + SHA1_pos++; + } + + return 0; +} diff --git a/contrib/wpa/src/crypto/sha1-tprf.c b/contrib/wpa/src/crypto/sha1-tprf.c new file mode 100644 index 0000000..4a80e96 --- /dev/null +++ b/contrib/wpa/src/crypto/sha1-tprf.c @@ -0,0 +1,76 @@ +/* + * SHA1 T-PRF for EAP-FAST + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha1.h" +#include "crypto.h" + +/** + * 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 + * Returns: 0 on success, -1 of failure + * + * This function is used to derive new, cryptographically separate keys from a + * given key for EAP-FAST. T-PRF is defined in RFC 4851, Section 5.5. + */ +int 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) +{ + unsigned char counter = 0; + size_t pos, plen; + u8 hash[SHA1_MAC_LEN]; + size_t label_len = os_strlen(label); + u8 output_len[2]; + const unsigned char *addr[5]; + size_t len[5]; + + addr[0] = hash; + len[0] = 0; + addr[1] = (unsigned char *) label; + len[1] = label_len + 1; + addr[2] = seed; + len[2] = seed_len; + addr[3] = output_len; + len[3] = 2; + addr[4] = &counter; + len[4] = 1; + + output_len[0] = (buf_len >> 8) & 0xff; + output_len[1] = buf_len & 0xff; + pos = 0; + while (pos < buf_len) { + counter++; + plen = buf_len - pos; + if (hmac_sha1_vector(key, key_len, 5, addr, len, hash)) + return -1; + if (plen >= SHA1_MAC_LEN) { + os_memcpy(&buf[pos], hash, SHA1_MAC_LEN); + pos += SHA1_MAC_LEN; + } else { + os_memcpy(&buf[pos], hash, plen); + break; + } + len[0] = SHA1_MAC_LEN; + } + + return 0; +} diff --git a/contrib/wpa/src/crypto/sha1.c b/contrib/wpa/src/crypto/sha1.c index 141e4f4..fe00bdb 100644 --- a/contrib/wpa/src/crypto/sha1.c +++ b/contrib/wpa/src/crypto/sha1.c @@ -16,7 +16,6 @@ #include "common.h" #include "sha1.h" -#include "md5.h" #include "crypto.h" @@ -28,9 +27,10 @@ * @addr: Pointers to the data areas * @len: Lengths of the data blocks * @mac: Buffer for the hash (20 bytes) + * Returns: 0 on success, -1 on failure */ -void hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, - const u8 *addr[], const size_t *len, u8 *mac) +int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac) { unsigned char k_pad[64]; /* padding - key XORd with ipad/opad */ unsigned char tk[20]; @@ -42,12 +42,13 @@ void hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, * Fixed limit on the number of fragments to avoid having to * allocate memory (which could fail). */ - return; + return -1; } /* if key is longer than 64 bytes reset it to key = SHA1(key) */ if (key_len > 64) { - sha1_vector(1, &key, &key_len, tk); + if (sha1_vector(1, &key, &key_len, tk)) + return -1; key = tk; key_len = 20; } @@ -75,7 +76,8 @@ void hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, _addr[i + 1] = addr[i]; _len[i + 1] = len[i]; } - sha1_vector(1 + num_elem, _addr, _len, mac); + if (sha1_vector(1 + num_elem, _addr, _len, mac)) + return -1; os_memset(k_pad, 0, sizeof(k_pad)); os_memcpy(k_pad, key, key_len); @@ -88,7 +90,7 @@ void hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, _len[0] = 64; _addr[1] = mac; _len[1] = SHA1_MAC_LEN; - sha1_vector(2, _addr, _len, mac); + return sha1_vector(2, _addr, _len, mac); } @@ -99,11 +101,12 @@ void hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, * @data: Pointers to the data area * @data_len: Length of the data area * @mac: Buffer for the hash (20 bytes) + * Returns: 0 on success, -1 of failure */ -void hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, +int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *mac) { - hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac); + return hmac_sha1_vector(key, key_len, 1, &data, &data_len, mac); } @@ -116,12 +119,13 @@ void hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, * @data_len: Length of the data * @buf: Buffer for the generated pseudo-random key * @buf_len: Number of bytes of key to generate + * Returns: 0 on success, -1 of failure * * 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) +int sha1_prf(const u8 *key, size_t key_len, const char *label, + const u8 *data, size_t data_len, u8 *buf, size_t buf_len) { u8 counter = 0; size_t pos, plen; @@ -141,593 +145,19 @@ void sha1_prf(const u8 *key, size_t key_len, const char *label, while (pos < buf_len) { plen = buf_len - pos; if (plen >= SHA1_MAC_LEN) { - hmac_sha1_vector(key, key_len, 3, addr, len, - &buf[pos]); + if (hmac_sha1_vector(key, key_len, 3, addr, len, + &buf[pos])) + return -1; pos += SHA1_MAC_LEN; } else { - hmac_sha1_vector(key, key_len, 3, addr, len, - hash); + if (hmac_sha1_vector(key, key_len, 3, addr, len, + hash)) + return -1; os_memcpy(&buf[pos], hash, plen); break; } counter++; } -} - - -#ifndef CONFIG_NO_T_PRF -/** - * 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 RFC 4851, Section 5.5. - */ -void sha1_t_prf(const u8 *key, size_t key_len, const char *label, - const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len) -{ - unsigned char counter = 0; - size_t pos, plen; - u8 hash[SHA1_MAC_LEN]; - size_t label_len = os_strlen(label); - u8 output_len[2]; - const unsigned char *addr[5]; - size_t len[5]; - - addr[0] = hash; - len[0] = 0; - addr[1] = (unsigned char *) label; - len[1] = label_len + 1; - addr[2] = seed; - len[2] = seed_len; - addr[3] = output_len; - len[3] = 2; - addr[4] = &counter; - len[4] = 1; - - output_len[0] = (buf_len >> 8) & 0xff; - output_len[1] = buf_len & 0xff; - pos = 0; - while (pos < buf_len) { - counter++; - plen = buf_len - pos; - hmac_sha1_vector(key, key_len, 5, addr, len, hash); - if (plen >= SHA1_MAC_LEN) { - os_memcpy(&buf[pos], hash, SHA1_MAC_LEN); - pos += SHA1_MAC_LEN; - } else { - os_memcpy(&buf[pos], hash, plen); - break; - } - len[0] = SHA1_MAC_LEN; - } -} -#endif /* CONFIG_NO_T_PRF */ - - -#ifndef CONFIG_NO_TLS_PRF -/** - * tls_prf - Pseudo-Random Function for TLS (TLS-PRF, RFC 2246) - * @secret: Key for PRF - * @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 - * Returns: 0 on success, -1 on failure. - * - * This function is used to derive new, cryptographically separate keys from a - * given key in TLS. This PRF is defined in RFC 2246, Chapter 5. - */ -int tls_prf(const u8 *secret, size_t secret_len, const char *label, - const u8 *seed, size_t seed_len, u8 *out, size_t outlen) -{ - size_t L_S1, L_S2, i; - const u8 *S1, *S2; - u8 A_MD5[MD5_MAC_LEN], A_SHA1[SHA1_MAC_LEN]; - u8 P_MD5[MD5_MAC_LEN], P_SHA1[SHA1_MAC_LEN]; - int MD5_pos, SHA1_pos; - const u8 *MD5_addr[3]; - size_t MD5_len[3]; - const unsigned char *SHA1_addr[3]; - size_t SHA1_len[3]; - - if (secret_len & 1) - return -1; - - MD5_addr[0] = A_MD5; - MD5_len[0] = MD5_MAC_LEN; - MD5_addr[1] = (unsigned char *) label; - MD5_len[1] = os_strlen(label); - MD5_addr[2] = seed; - MD5_len[2] = seed_len; - - SHA1_addr[0] = A_SHA1; - SHA1_len[0] = SHA1_MAC_LEN; - SHA1_addr[1] = (unsigned char *) label; - SHA1_len[1] = os_strlen(label); - SHA1_addr[2] = seed; - SHA1_len[2] = seed_len; - - /* RFC 2246, Chapter 5 - * A(0) = seed, A(i) = HMAC(secret, A(i-1)) - * P_hash = HMAC(secret, A(1) + seed) + HMAC(secret, A(2) + seed) + .. - * PRF = P_MD5(S1, label + seed) XOR P_SHA-1(S2, label + seed) - */ - - L_S1 = L_S2 = (secret_len + 1) / 2; - S1 = secret; - S2 = secret + L_S1; - if (secret_len & 1) { - /* The last byte of S1 will be shared with S2 */ - S2--; - } - - hmac_md5_vector(S1, L_S1, 2, &MD5_addr[1], &MD5_len[1], A_MD5); - hmac_sha1_vector(S2, L_S2, 2, &SHA1_addr[1], &SHA1_len[1], A_SHA1); - - MD5_pos = MD5_MAC_LEN; - SHA1_pos = SHA1_MAC_LEN; - for (i = 0; i < outlen; i++) { - if (MD5_pos == MD5_MAC_LEN) { - hmac_md5_vector(S1, L_S1, 3, MD5_addr, MD5_len, P_MD5); - MD5_pos = 0; - hmac_md5(S1, L_S1, A_MD5, MD5_MAC_LEN, A_MD5); - } - if (SHA1_pos == SHA1_MAC_LEN) { - hmac_sha1_vector(S2, L_S2, 3, SHA1_addr, SHA1_len, - P_SHA1); - SHA1_pos = 0; - hmac_sha1(S2, L_S2, A_SHA1, SHA1_MAC_LEN, A_SHA1); - } - - out[i] = P_MD5[MD5_pos] ^ P_SHA1[SHA1_pos]; - - MD5_pos++; - SHA1_pos++; - } - - return 0; -} -#endif /* CONFIG_NO_TLS_PRF */ - - -#ifndef CONFIG_NO_PBKDF2 - -static void pbkdf2_sha1_f(const char *passphrase, const char *ssid, - size_t ssid_len, int iterations, unsigned int count, - u8 *digest) -{ - unsigned char tmp[SHA1_MAC_LEN], tmp2[SHA1_MAC_LEN]; - int i, j; - unsigned char count_buf[4]; - const u8 *addr[2]; - size_t len[2]; - size_t passphrase_len = os_strlen(passphrase); - - addr[0] = (u8 *) ssid; - len[0] = ssid_len; - addr[1] = count_buf; - len[1] = 4; - - /* F(P, S, c, i) = U1 xor U2 xor ... Uc - * U1 = PRF(P, S || i) - * U2 = PRF(P, U1) - * Uc = PRF(P, Uc-1) - */ - - count_buf[0] = (count >> 24) & 0xff; - count_buf[1] = (count >> 16) & 0xff; - count_buf[2] = (count >> 8) & 0xff; - count_buf[3] = count & 0xff; - hmac_sha1_vector((u8 *) passphrase, passphrase_len, 2, addr, len, tmp); - os_memcpy(digest, tmp, SHA1_MAC_LEN); - - for (i = 1; i < iterations; i++) { - hmac_sha1((u8 *) passphrase, passphrase_len, tmp, SHA1_MAC_LEN, - tmp2); - os_memcpy(tmp, tmp2, SHA1_MAC_LEN); - for (j = 0; j < SHA1_MAC_LEN; j++) - digest[j] ^= tmp2[j]; - } -} - - -/** - * pbkdf2_sha1 - SHA1-based key derivation function (PBKDF2) for IEEE 802.11i - * @passphrase: ASCII passphrase - * @ssid: SSID - * @ssid_len: SSID length in bytes - * @iterations: 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) -{ - unsigned int count = 0; - unsigned char *pos = buf; - size_t left = buflen, plen; - unsigned char digest[SHA1_MAC_LEN]; - - while (left > 0) { - count++; - pbkdf2_sha1_f(passphrase, ssid, ssid_len, iterations, count, - digest); - plen = left > SHA1_MAC_LEN ? SHA1_MAC_LEN : left; - os_memcpy(pos, digest, plen); - pos += plen; - left -= plen; - } -} - -#endif /* CONFIG_NO_PBKDF2 */ - - -#ifdef INTERNAL_SHA1 - -struct SHA1Context { - u32 state[5]; - u32 count[2]; - unsigned char buffer[64]; -}; - -typedef struct SHA1Context SHA1_CTX; - -#ifndef CONFIG_CRYPTO_INTERNAL -static void SHA1Init(struct SHA1Context *context); -static void SHA1Update(struct SHA1Context *context, const void *data, u32 len); -static void SHA1Final(unsigned char digest[20], struct SHA1Context *context); -#endif /* CONFIG_CRYPTO_INTERNAL */ -static void SHA1Transform(u32 state[5], const unsigned char buffer[64]); - - -/** - * 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_CTX ctx; - size_t i; - - SHA1Init(&ctx); - for (i = 0; i < num_elem; i++) - SHA1Update(&ctx, addr[i], len[i]); - SHA1Final(mac, &ctx); -} - - -#ifndef CONFIG_NO_FIPS186_2_PRF -int fips186_2_prf(const u8 *seed, size_t seed_len, u8 *x, size_t xlen) -{ - u8 xkey[64]; - u32 t[5], _t[5]; - int i, j, m, k; - u8 *xpos = x; - u32 carry; - - if (seed_len > sizeof(xkey)) - seed_len = sizeof(xkey); - - /* FIPS 186-2 + change notice 1 */ - - os_memcpy(xkey, seed, seed_len); - os_memset(xkey + seed_len, 0, 64 - seed_len); - t[0] = 0x67452301; - t[1] = 0xEFCDAB89; - t[2] = 0x98BADCFE; - t[3] = 0x10325476; - t[4] = 0xC3D2E1F0; - - m = xlen / 40; - for (j = 0; j < m; j++) { - /* XSEED_j = 0 */ - for (i = 0; i < 2; i++) { - /* XVAL = (XKEY + XSEED_j) mod 2^b */ - - /* w_i = G(t, XVAL) */ - os_memcpy(_t, t, 20); - SHA1Transform(_t, xkey); - _t[0] = host_to_be32(_t[0]); - _t[1] = host_to_be32(_t[1]); - _t[2] = host_to_be32(_t[2]); - _t[3] = host_to_be32(_t[3]); - _t[4] = host_to_be32(_t[4]); - os_memcpy(xpos, _t, 20); - - /* XKEY = (1 + XKEY + w_i) mod 2^b */ - carry = 1; - for (k = 19; k >= 0; k--) { - carry += xkey[k] + xpos[k]; - xkey[k] = carry & 0xff; - carry >>= 8; - } - - xpos += SHA1_MAC_LEN; - } - /* x_j = w_0|w_1 */ - } return 0; } -#endif /* CONFIG_NO_FIPS186_2_PRF */ - - -/* ===== start - public domain SHA1 implementation ===== */ - -/* -SHA-1 in C -By Steve Reid <sreid@sea-to-sky.net> -100% Public Domain - ------------------ -Modified 7/98 -By James H. Brown <jbrown@burgoyne.com> -Still 100% Public Domain - -Corrected a problem which generated improper hash values on 16 bit machines -Routine SHA1Update changed from - void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned int -len) -to - void SHA1Update(SHA1_CTX* context, unsigned char* data, unsigned -long len) - -The 'len' parameter was declared an int which works fine on 32 bit machines. -However, on 16 bit machines an int is too small for the shifts being done -against -it. This caused the hash function to generate incorrect values if len was -greater than 8191 (8K - 1) due to the 'len << 3' on line 3 of SHA1Update(). - -Since the file IO in main() reads 16K at a time, any file 8K or larger would -be guaranteed to generate the wrong hash (e.g. Test Vector #3, a million -"a"s). - -I also changed the declaration of variables i & j in SHA1Update to -unsigned long from unsigned int for the same reason. - -These changes should make no difference to any 32 bit implementations since -an -int and a long are the same size in those environments. - --- -I also corrected a few compiler warnings generated by Borland C. -1. Added #include <process.h> for exit() prototype -2. Removed unused variable 'j' in SHA1Final -3. Changed exit(0) to return(0) at end of main. - -ALL changes I made can be located by searching for comments containing 'JHB' ------------------ -Modified 8/98 -By Steve Reid <sreid@sea-to-sky.net> -Still 100% public domain - -1- Removed #include <process.h> and used return() instead of exit() -2- Fixed overwriting of finalcount in SHA1Final() (discovered by Chris Hall) -3- Changed email address from steve@edmweb.com to sreid@sea-to-sky.net - ------------------ -Modified 4/01 -By Saul Kravitz <Saul.Kravitz@celera.com> -Still 100% PD -Modified to run on Compaq Alpha hardware. - ------------------ -Modified 4/01 -By Jouni Malinen <j@w1.fi> -Minor changes to match the coding style used in Dynamics. - -Modified September 24, 2004 -By Jouni Malinen <j@w1.fi> -Fixed alignment issue in SHA1Transform when SHA1HANDSOFF is defined. - -*/ - -/* -Test Vectors (from FIPS PUB 180-1) -"abc" - A9993E36 4706816A BA3E2571 7850C26C 9CD0D89D -"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" - 84983E44 1C3BD26E BAAE4AA1 F95129E5 E54670F1 -A million repetitions of "a" - 34AA973C D4C4DAA4 F61EEB2B DBAD2731 6534016F -*/ - -#define SHA1HANDSOFF - -#define rol(value, bits) (((value) << (bits)) | ((value) >> (32 - (bits)))) - -/* blk0() and blk() perform the initial expand. */ -/* I got the idea of expanding during the round function from SSLeay */ -#ifndef WORDS_BIGENDIAN -#define blk0(i) (block->l[i] = (rol(block->l[i], 24) & 0xFF00FF00) | \ - (rol(block->l[i], 8) & 0x00FF00FF)) -#else -#define blk0(i) block->l[i] -#endif -#define blk(i) (block->l[i & 15] = rol(block->l[(i + 13) & 15] ^ \ - block->l[(i + 8) & 15] ^ block->l[(i + 2) & 15] ^ block->l[i & 15], 1)) - -/* (R0+R1), R2, R3, R4 are the different operations used in SHA1 */ -#define R0(v,w,x,y,z,i) \ - z += ((w & (x ^ y)) ^ y) + blk0(i) + 0x5A827999 + rol(v, 5); \ - w = rol(w, 30); -#define R1(v,w,x,y,z,i) \ - z += ((w & (x ^ y)) ^ y) + blk(i) + 0x5A827999 + rol(v, 5); \ - w = rol(w, 30); -#define R2(v,w,x,y,z,i) \ - z += (w ^ x ^ y) + blk(i) + 0x6ED9EBA1 + rol(v, 5); w = rol(w, 30); -#define R3(v,w,x,y,z,i) \ - z += (((w | x) & y) | (w & x)) + blk(i) + 0x8F1BBCDC + rol(v, 5); \ - w = rol(w, 30); -#define R4(v,w,x,y,z,i) \ - z += (w ^ x ^ y) + blk(i) + 0xCA62C1D6 + rol(v, 5); \ - w=rol(w, 30); - - -#ifdef VERBOSE /* SAK */ -void SHAPrintContext(SHA1_CTX *context, char *msg) -{ - printf("%s (%d,%d) %x %x %x %x %x\n", - msg, - context->count[0], context->count[1], - context->state[0], - context->state[1], - context->state[2], - context->state[3], - context->state[4]); -} -#endif - -/* Hash a single 512-bit block. This is the core of the algorithm. */ - -static void SHA1Transform(u32 state[5], const unsigned char buffer[64]) -{ - u32 a, b, c, d, e; - typedef union { - unsigned char c[64]; - u32 l[16]; - } CHAR64LONG16; - CHAR64LONG16* block; -#ifdef SHA1HANDSOFF - CHAR64LONG16 workspace; - block = &workspace; - os_memcpy(block, buffer, 64); -#else - block = (CHAR64LONG16 *) buffer; -#endif - /* Copy context->state[] to working vars */ - a = state[0]; - b = state[1]; - c = state[2]; - d = state[3]; - e = state[4]; - /* 4 rounds of 20 operations each. Loop unrolled. */ - R0(a,b,c,d,e, 0); R0(e,a,b,c,d, 1); R0(d,e,a,b,c, 2); R0(c,d,e,a,b, 3); - R0(b,c,d,e,a, 4); R0(a,b,c,d,e, 5); R0(e,a,b,c,d, 6); R0(d,e,a,b,c, 7); - R0(c,d,e,a,b, 8); R0(b,c,d,e,a, 9); R0(a,b,c,d,e,10); R0(e,a,b,c,d,11); - R0(d,e,a,b,c,12); R0(c,d,e,a,b,13); R0(b,c,d,e,a,14); R0(a,b,c,d,e,15); - R1(e,a,b,c,d,16); R1(d,e,a,b,c,17); R1(c,d,e,a,b,18); R1(b,c,d,e,a,19); - R2(a,b,c,d,e,20); R2(e,a,b,c,d,21); R2(d,e,a,b,c,22); R2(c,d,e,a,b,23); - R2(b,c,d,e,a,24); R2(a,b,c,d,e,25); R2(e,a,b,c,d,26); R2(d,e,a,b,c,27); - R2(c,d,e,a,b,28); R2(b,c,d,e,a,29); R2(a,b,c,d,e,30); R2(e,a,b,c,d,31); - R2(d,e,a,b,c,32); R2(c,d,e,a,b,33); R2(b,c,d,e,a,34); R2(a,b,c,d,e,35); - R2(e,a,b,c,d,36); R2(d,e,a,b,c,37); R2(c,d,e,a,b,38); R2(b,c,d,e,a,39); - R3(a,b,c,d,e,40); R3(e,a,b,c,d,41); R3(d,e,a,b,c,42); R3(c,d,e,a,b,43); - R3(b,c,d,e,a,44); R3(a,b,c,d,e,45); R3(e,a,b,c,d,46); R3(d,e,a,b,c,47); - R3(c,d,e,a,b,48); R3(b,c,d,e,a,49); R3(a,b,c,d,e,50); R3(e,a,b,c,d,51); - R3(d,e,a,b,c,52); R3(c,d,e,a,b,53); R3(b,c,d,e,a,54); R3(a,b,c,d,e,55); - R3(e,a,b,c,d,56); R3(d,e,a,b,c,57); R3(c,d,e,a,b,58); R3(b,c,d,e,a,59); - R4(a,b,c,d,e,60); R4(e,a,b,c,d,61); R4(d,e,a,b,c,62); R4(c,d,e,a,b,63); - R4(b,c,d,e,a,64); R4(a,b,c,d,e,65); R4(e,a,b,c,d,66); R4(d,e,a,b,c,67); - R4(c,d,e,a,b,68); R4(b,c,d,e,a,69); R4(a,b,c,d,e,70); R4(e,a,b,c,d,71); - R4(d,e,a,b,c,72); R4(c,d,e,a,b,73); R4(b,c,d,e,a,74); R4(a,b,c,d,e,75); - R4(e,a,b,c,d,76); R4(d,e,a,b,c,77); R4(c,d,e,a,b,78); R4(b,c,d,e,a,79); - /* Add the working vars back into context.state[] */ - state[0] += a; - state[1] += b; - state[2] += c; - state[3] += d; - state[4] += e; - /* Wipe variables */ - a = b = c = d = e = 0; -#ifdef SHA1HANDSOFF - os_memset(block, 0, 64); -#endif -} - - -/* SHA1Init - Initialize new context */ - -void SHA1Init(SHA1_CTX* context) -{ - /* SHA1 initialization constants */ - context->state[0] = 0x67452301; - context->state[1] = 0xEFCDAB89; - context->state[2] = 0x98BADCFE; - context->state[3] = 0x10325476; - context->state[4] = 0xC3D2E1F0; - context->count[0] = context->count[1] = 0; -} - - -/* Run your data through this. */ - -void SHA1Update(SHA1_CTX* context, const void *_data, u32 len) -{ - u32 i, j; - const unsigned char *data = _data; - -#ifdef VERBOSE - SHAPrintContext(context, "before"); -#endif - j = (context->count[0] >> 3) & 63; - if ((context->count[0] += len << 3) < (len << 3)) - context->count[1]++; - context->count[1] += (len >> 29); - if ((j + len) > 63) { - os_memcpy(&context->buffer[j], data, (i = 64-j)); - SHA1Transform(context->state, context->buffer); - for ( ; i + 63 < len; i += 64) { - SHA1Transform(context->state, &data[i]); - } - j = 0; - } - else i = 0; - os_memcpy(&context->buffer[j], &data[i], len - i); -#ifdef VERBOSE - SHAPrintContext(context, "after "); -#endif -} - - -/* Add padding and return the message digest. */ - -void SHA1Final(unsigned char digest[20], SHA1_CTX* context) -{ - u32 i; - unsigned char finalcount[8]; - - for (i = 0; i < 8; i++) { - finalcount[i] = (unsigned char) - ((context->count[(i >= 4 ? 0 : 1)] >> - ((3-(i & 3)) * 8) ) & 255); /* Endian independent */ - } - SHA1Update(context, (unsigned char *) "\200", 1); - while ((context->count[0] & 504) != 448) { - SHA1Update(context, (unsigned char *) "\0", 1); - } - SHA1Update(context, finalcount, 8); /* Should cause a SHA1Transform() - */ - for (i = 0; i < 20; i++) { - digest[i] = (unsigned char) - ((context->state[i >> 2] >> ((3 - (i & 3)) * 8)) & - 255); - } - /* Wipe variables */ - i = 0; - os_memset(context->buffer, 0, 64); - os_memset(context->state, 0, 20); - os_memset(context->count, 0, 8); - os_memset(finalcount, 0, 8); -} - -/* ===== end - public domain SHA1 implementation ===== */ - -#endif /* INTERNAL_SHA1 */ diff --git a/contrib/wpa/src/crypto/sha1.h b/contrib/wpa/src/crypto/sha1.h index 9c365e2..c1a6233 100644 --- a/contrib/wpa/src/crypto/sha1.h +++ b/contrib/wpa/src/crypto/sha1.h @@ -1,6 +1,6 @@ /* * SHA1 hash implementation and interface functions - * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -17,26 +17,17 @@ #define SHA1_MAC_LEN 20 -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, +int hmac_sha1_vector(const u8 *key, size_t key_len, size_t num_elem, + const u8 *addr[], const size_t *len, u8 *mac); +int hmac_sha1(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *mac); -void sha1_prf(const u8 *key, size_t key_len, const char *label, - const u8 *data, size_t data_len, u8 *buf, size_t buf_len); -void sha1_t_prf(const u8 *key, size_t key_len, const char *label, - const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len); +int 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); +int sha1_t_prf(const u8 *key, size_t key_len, const char *label, + const u8 *seed, size_t seed_len, u8 *buf, size_t buf_len); int __must_check tls_prf(const u8 *secret, size_t secret_len, const char *label, const u8 *seed, size_t seed_len, u8 *out, size_t outlen); -void pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len, - int iterations, u8 *buf, size_t buflen); - -#ifdef CONFIG_CRYPTO_INTERNAL -struct SHA1Context; - -void SHA1Init(struct SHA1Context *context); -void SHA1Update(struct SHA1Context *context, const void *data, u32 len); -void SHA1Final(unsigned char digest[20], struct SHA1Context *context); -#endif /* CONFIG_CRYPTO_INTERNAL */ - +int pbkdf2_sha1(const char *passphrase, const char *ssid, size_t ssid_len, + int iterations, u8 *buf, size_t buflen); #endif /* SHA1_H */ diff --git a/contrib/wpa/src/crypto/sha1_i.h b/contrib/wpa/src/crypto/sha1_i.h new file mode 100644 index 0000000..ec2f82f --- /dev/null +++ b/contrib/wpa/src/crypto/sha1_i.h @@ -0,0 +1,29 @@ +/* + * SHA1 internal definitions + * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef SHA1_I_H +#define SHA1_I_H + +struct SHA1Context { + u32 state[5]; + u32 count[2]; + unsigned char buffer[64]; +}; + +void SHA1Init(struct SHA1Context *context); +void SHA1Update(struct SHA1Context *context, const void *data, u32 len); +void SHA1Final(unsigned char digest[20], struct SHA1Context *context); +void SHA1Transform(u32 state[5], const unsigned char buffer[64]); + +#endif /* SHA1_I_H */ diff --git a/contrib/wpa/src/crypto/sha256-internal.c b/contrib/wpa/src/crypto/sha256-internal.c new file mode 100644 index 0000000..b061373 --- /dev/null +++ b/contrib/wpa/src/crypto/sha256-internal.c @@ -0,0 +1,243 @@ +/* + * SHA-256 hash implementation and interface functions + * Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "sha256.h" +#include "crypto.h" + +struct sha256_state { + u64 length; + u32 state[8], curlen; + u8 buf[64]; +}; + +static void sha256_init(struct sha256_state *md); +static int sha256_process(struct sha256_state *md, const unsigned char *in, + unsigned long inlen); +static int sha256_done(struct sha256_state *md, unsigned char *out); + + +/** + * sha256_vector - SHA256 hash for data vector + * @num_elem: Number of elements in the data vector + * @addr: Pointers to the data areas + * @len: Lengths of the data blocks + * @mac: Buffer for the hash + * Returns: 0 on success, -1 of failure + */ +int sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, + u8 *mac) +{ + struct sha256_state ctx; + size_t i; + + sha256_init(&ctx); + for (i = 0; i < num_elem; i++) + if (sha256_process(&ctx, addr[i], len[i])) + return -1; + if (sha256_done(&ctx, mac)) + return -1; + return 0; +} + + +/* ===== start - public domain SHA256 implementation ===== */ + +/* This is based on SHA256 implementation in LibTomCrypt that was released into + * public domain by Tom St Denis. */ + +/* the K array */ +static const unsigned long K[64] = { + 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL, + 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL, + 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, + 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, + 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL, + 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL, + 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, + 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, + 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL, + 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL, + 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, + 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, + 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL +}; + + +/* Various logical functions */ +#define RORc(x, y) \ +( ((((unsigned long) (x) & 0xFFFFFFFFUL) >> (unsigned long) ((y) & 31)) | \ + ((unsigned long) (x) << (unsigned long) (32 - ((y) & 31)))) & 0xFFFFFFFFUL) +#define Ch(x,y,z) (z ^ (x & (y ^ z))) +#define Maj(x,y,z) (((x | y) & z) | (x & y)) +#define S(x, n) RORc((x), (n)) +#define R(x, n) (((x)&0xFFFFFFFFUL)>>(n)) +#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22)) +#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25)) +#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3)) +#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10)) +#ifndef MIN +#define MIN(x, y) (((x) < (y)) ? (x) : (y)) +#endif + +/* compress 512-bits */ +static int sha256_compress(struct sha256_state *md, unsigned char *buf) +{ + u32 S[8], W[64], t0, t1; + u32 t; + int i; + + /* copy state into S */ + for (i = 0; i < 8; i++) { + S[i] = md->state[i]; + } + + /* copy the state into 512-bits into W[0..15] */ + for (i = 0; i < 16; i++) + W[i] = WPA_GET_BE32(buf + (4 * i)); + + /* fill W[16..63] */ + for (i = 16; i < 64; i++) { + W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + + W[i - 16]; + } + + /* Compress */ +#define RND(a,b,c,d,e,f,g,h,i) \ + t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ + t1 = Sigma0(a) + Maj(a, b, c); \ + d += t0; \ + h = t0 + t1; + + for (i = 0; i < 64; ++i) { + RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i); + t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4]; + S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t; + } + + /* feedback */ + for (i = 0; i < 8; i++) { + md->state[i] = md->state[i] + S[i]; + } + return 0; +} + + +/* Initialize the hash state */ +static void sha256_init(struct sha256_state *md) +{ + md->curlen = 0; + md->length = 0; + md->state[0] = 0x6A09E667UL; + md->state[1] = 0xBB67AE85UL; + md->state[2] = 0x3C6EF372UL; + md->state[3] = 0xA54FF53AUL; + md->state[4] = 0x510E527FUL; + md->state[5] = 0x9B05688CUL; + md->state[6] = 0x1F83D9ABUL; + md->state[7] = 0x5BE0CD19UL; +} + +/** + Process a block of memory though the hash + @param md The hash state + @param in The data to hash + @param inlen The length of the data (octets) + @return CRYPT_OK if successful +*/ +static int sha256_process(struct sha256_state *md, const unsigned char *in, + unsigned long inlen) +{ + unsigned long n; +#define block_size 64 + + if (md->curlen > sizeof(md->buf)) + return -1; + + while (inlen > 0) { + if (md->curlen == 0 && inlen >= block_size) { + if (sha256_compress(md, (unsigned char *) in) < 0) + return -1; + md->length += block_size * 8; + in += block_size; + inlen -= block_size; + } else { + n = MIN(inlen, (block_size - md->curlen)); + os_memcpy(md->buf + md->curlen, in, n); + md->curlen += n; + in += n; + inlen -= n; + if (md->curlen == block_size) { + if (sha256_compress(md, md->buf) < 0) + return -1; + md->length += 8 * block_size; + md->curlen = 0; + } + } + } + + return 0; +} + + +/** + Terminate the hash to get the digest + @param md The hash state + @param out [out] The destination of the hash (32 bytes) + @return CRYPT_OK if successful +*/ +static int sha256_done(struct sha256_state *md, unsigned char *out) +{ + int i; + + if (md->curlen >= sizeof(md->buf)) + return -1; + + /* increase the length of the message */ + md->length += md->curlen * 8; + + /* append the '1' bit */ + md->buf[md->curlen++] = (unsigned char) 0x80; + + /* if the length is currently above 56 bytes we append zeros + * then compress. Then we can fall back to padding zeros and length + * encoding like normal. + */ + if (md->curlen > 56) { + while (md->curlen < 64) { + md->buf[md->curlen++] = (unsigned char) 0; + } + sha256_compress(md, md->buf); + md->curlen = 0; + } + + /* pad upto 56 bytes of zeroes */ + while (md->curlen < 56) { + md->buf[md->curlen++] = (unsigned char) 0; + } + + /* store length */ + WPA_PUT_BE64(md->buf + 56, md->length); + sha256_compress(md, md->buf); + + /* copy output */ + for (i = 0; i < 8; i++) + WPA_PUT_BE32(out + (4 * i), md->state[i]); + + return 0; +} + +/* ===== end - public domain SHA256 implementation ===== */ diff --git a/contrib/wpa/src/crypto/sha256.c b/contrib/wpa/src/crypto/sha256.c index 96dac0e..7f320f9 100644 --- a/contrib/wpa/src/crypto/sha256.c +++ b/contrib/wpa/src/crypto/sha256.c @@ -155,228 +155,3 @@ void sha256_prf(const u8 *key, size_t key_len, const char *label, counter++; } } - - -#ifdef INTERNAL_SHA256 - -struct sha256_state { - u64 length; - u32 state[8], curlen; - u8 buf[64]; -}; - -static void sha256_init(struct sha256_state *md); -static int sha256_process(struct sha256_state *md, const unsigned char *in, - unsigned long inlen); -static int sha256_done(struct sha256_state *md, unsigned char *out); - - -/** - * sha256_vector - SHA256 hash for data vector - * @num_elem: Number of elements in the data vector - * @addr: Pointers to the data areas - * @len: Lengths of the data blocks - * @mac: Buffer for the hash - */ -void sha256_vector(size_t num_elem, const u8 *addr[], const size_t *len, - u8 *mac) -{ - struct sha256_state ctx; - size_t i; - - sha256_init(&ctx); - for (i = 0; i < num_elem; i++) - sha256_process(&ctx, addr[i], len[i]); - sha256_done(&ctx, mac); -} - - -/* ===== start - public domain SHA256 implementation ===== */ - -/* This is based on SHA256 implementation in LibTomCrypt that was released into - * public domain by Tom St Denis. */ - -/* the K array */ -static const unsigned long K[64] = { - 0x428a2f98UL, 0x71374491UL, 0xb5c0fbcfUL, 0xe9b5dba5UL, 0x3956c25bUL, - 0x59f111f1UL, 0x923f82a4UL, 0xab1c5ed5UL, 0xd807aa98UL, 0x12835b01UL, - 0x243185beUL, 0x550c7dc3UL, 0x72be5d74UL, 0x80deb1feUL, 0x9bdc06a7UL, - 0xc19bf174UL, 0xe49b69c1UL, 0xefbe4786UL, 0x0fc19dc6UL, 0x240ca1ccUL, - 0x2de92c6fUL, 0x4a7484aaUL, 0x5cb0a9dcUL, 0x76f988daUL, 0x983e5152UL, - 0xa831c66dUL, 0xb00327c8UL, 0xbf597fc7UL, 0xc6e00bf3UL, 0xd5a79147UL, - 0x06ca6351UL, 0x14292967UL, 0x27b70a85UL, 0x2e1b2138UL, 0x4d2c6dfcUL, - 0x53380d13UL, 0x650a7354UL, 0x766a0abbUL, 0x81c2c92eUL, 0x92722c85UL, - 0xa2bfe8a1UL, 0xa81a664bUL, 0xc24b8b70UL, 0xc76c51a3UL, 0xd192e819UL, - 0xd6990624UL, 0xf40e3585UL, 0x106aa070UL, 0x19a4c116UL, 0x1e376c08UL, - 0x2748774cUL, 0x34b0bcb5UL, 0x391c0cb3UL, 0x4ed8aa4aUL, 0x5b9cca4fUL, - 0x682e6ff3UL, 0x748f82eeUL, 0x78a5636fUL, 0x84c87814UL, 0x8cc70208UL, - 0x90befffaUL, 0xa4506cebUL, 0xbef9a3f7UL, 0xc67178f2UL -}; - - -/* Various logical functions */ -#define RORc(x, y) \ -( ((((unsigned long) (x) & 0xFFFFFFFFUL) >> (unsigned long) ((y) & 31)) | \ - ((unsigned long) (x) << (unsigned long) (32 - ((y) & 31)))) & 0xFFFFFFFFUL) -#define Ch(x,y,z) (z ^ (x & (y ^ z))) -#define Maj(x,y,z) (((x | y) & z) | (x & y)) -#define S(x, n) RORc((x), (n)) -#define R(x, n) (((x)&0xFFFFFFFFUL)>>(n)) -#define Sigma0(x) (S(x, 2) ^ S(x, 13) ^ S(x, 22)) -#define Sigma1(x) (S(x, 6) ^ S(x, 11) ^ S(x, 25)) -#define Gamma0(x) (S(x, 7) ^ S(x, 18) ^ R(x, 3)) -#define Gamma1(x) (S(x, 17) ^ S(x, 19) ^ R(x, 10)) -#ifndef MIN -#define MIN(x, y) (((x) < (y)) ? (x) : (y)) -#endif - -/* compress 512-bits */ -static int sha256_compress(struct sha256_state *md, unsigned char *buf) -{ - u32 S[8], W[64], t0, t1; - u32 t; - int i; - - /* copy state into S */ - for (i = 0; i < 8; i++) { - S[i] = md->state[i]; - } - - /* copy the state into 512-bits into W[0..15] */ - for (i = 0; i < 16; i++) - W[i] = WPA_GET_BE32(buf + (4 * i)); - - /* fill W[16..63] */ - for (i = 16; i < 64; i++) { - W[i] = Gamma1(W[i - 2]) + W[i - 7] + Gamma0(W[i - 15]) + - W[i - 16]; - } - - /* Compress */ -#define RND(a,b,c,d,e,f,g,h,i) \ - t0 = h + Sigma1(e) + Ch(e, f, g) + K[i] + W[i]; \ - t1 = Sigma0(a) + Maj(a, b, c); \ - d += t0; \ - h = t0 + t1; - - for (i = 0; i < 64; ++i) { - RND(S[0], S[1], S[2], S[3], S[4], S[5], S[6], S[7], i); - t = S[7]; S[7] = S[6]; S[6] = S[5]; S[5] = S[4]; - S[4] = S[3]; S[3] = S[2]; S[2] = S[1]; S[1] = S[0]; S[0] = t; - } - - /* feedback */ - for (i = 0; i < 8; i++) { - md->state[i] = md->state[i] + S[i]; - } - return 0; -} - - -/* Initialize the hash state */ -static void sha256_init(struct sha256_state *md) -{ - md->curlen = 0; - md->length = 0; - md->state[0] = 0x6A09E667UL; - md->state[1] = 0xBB67AE85UL; - md->state[2] = 0x3C6EF372UL; - md->state[3] = 0xA54FF53AUL; - md->state[4] = 0x510E527FUL; - md->state[5] = 0x9B05688CUL; - md->state[6] = 0x1F83D9ABUL; - md->state[7] = 0x5BE0CD19UL; -} - -/** - Process a block of memory though the hash - @param md The hash state - @param in The data to hash - @param inlen The length of the data (octets) - @return CRYPT_OK if successful -*/ -static int sha256_process(struct sha256_state *md, const unsigned char *in, - unsigned long inlen) -{ - unsigned long n; -#define block_size 64 - - if (md->curlen > sizeof(md->buf)) - return -1; - - while (inlen > 0) { - if (md->curlen == 0 && inlen >= block_size) { - if (sha256_compress(md, (unsigned char *) in) < 0) - return -1; - md->length += block_size * 8; - in += block_size; - inlen -= block_size; - } else { - n = MIN(inlen, (block_size - md->curlen)); - os_memcpy(md->buf + md->curlen, in, n); - md->curlen += n; - in += n; - inlen -= n; - if (md->curlen == block_size) { - if (sha256_compress(md, md->buf) < 0) - return -1; - md->length += 8 * block_size; - md->curlen = 0; - } - } - } - - return 0; -} - - -/** - Terminate the hash to get the digest - @param md The hash state - @param out [out] The destination of the hash (32 bytes) - @return CRYPT_OK if successful -*/ -static int sha256_done(struct sha256_state *md, unsigned char *out) -{ - int i; - - if (md->curlen >= sizeof(md->buf)) - return -1; - - /* increase the length of the message */ - md->length += md->curlen * 8; - - /* append the '1' bit */ - md->buf[md->curlen++] = (unsigned char) 0x80; - - /* if the length is currently above 56 bytes we append zeros - * then compress. Then we can fall back to padding zeros and length - * encoding like normal. - */ - if (md->curlen > 56) { - while (md->curlen < 64) { - md->buf[md->curlen++] = (unsigned char) 0; - } - sha256_compress(md, md->buf); - md->curlen = 0; - } - - /* pad upto 56 bytes of zeroes */ - while (md->curlen < 56) { - md->buf[md->curlen++] = (unsigned char) 0; - } - - /* store length */ - WPA_PUT_BE64(md->buf + 56, md->length); - sha256_compress(md, md->buf); - - /* copy output */ - for (i = 0; i < 8; i++) - WPA_PUT_BE32(out + (4 * i), md->state[i]); - - return 0; -} - -/* ===== end - public domain SHA256 implementation ===== */ - -#endif /* INTERNAL_SHA256 */ diff --git a/contrib/wpa/src/crypto/tls.h b/contrib/wpa/src/crypto/tls.h index aafb7999..0928b5b 100644 --- a/contrib/wpa/src/crypto/tls.h +++ b/contrib/wpa/src/crypto/tls.h @@ -1,6 +1,6 @@ /* - * WPA Supplicant / SSL/TLS interface definition - * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * SSL/TLS interface definition + * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -28,10 +28,54 @@ struct tls_keys { size_t inner_secret_len; }; +enum tls_event { + TLS_CERT_CHAIN_FAILURE, + TLS_PEER_CERTIFICATE +}; + +/* + * Note: These are used as identifier with external programs and as such, the + * values must not be changed. + */ +enum tls_fail_reason { + TLS_FAIL_UNSPECIFIED = 0, + TLS_FAIL_UNTRUSTED = 1, + TLS_FAIL_REVOKED = 2, + TLS_FAIL_NOT_YET_VALID = 3, + TLS_FAIL_EXPIRED = 4, + TLS_FAIL_SUBJECT_MISMATCH = 5, + TLS_FAIL_ALTSUBJECT_MISMATCH = 6, + TLS_FAIL_BAD_CERTIFICATE = 7, + TLS_FAIL_SERVER_CHAIN_PROBE = 8 +}; + +union tls_event_data { + struct { + int depth; + const char *subject; + enum tls_fail_reason reason; + const char *reason_txt; + const struct wpabuf *cert; + } cert_fail; + + struct { + int depth; + const char *subject; + const struct wpabuf *cert; + const u8 *hash; + size_t hash_len; + } peer_cert; +}; + struct tls_config { const char *opensc_engine_path; const char *pkcs11_engine_path; const char *pkcs11_module_path; + int fips_mode; + + void (*event_cb)(void *ctx, enum tls_event ev, + union tls_event_data *data); + void *cb_ctx; }; #define TLS_CONN_ALLOW_SIGN_RSA_MD5 BIT(0) @@ -292,17 +336,14 @@ int __must_check tls_connection_prf(void *tls_ctx, * 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. + * @in_data: Input data from TLS server * @appl_data: Pointer to application data pointer, or %NULL if dropped - * @appl_data_len: Pointer to variable that is set to appl_data length - * Returns: Pointer to output data, %NULL on failure + * Returns: Output data, %NULL on failure * - * Caller is responsible for freeing returned output data. If the final + * The caller is responsible for freeing the returned output data. If the final * handshake message includes application data, this is decrypted and - * appl_data (if not %NULL) is set to point this data. Caller is responsible - * for freeing appl_data. + * appl_data (if not %NULL) is set to point this data. The caller is + * responsible for freeing appl_data. * * This function is used during TLS handshake. The first call is done with * in_data == %NULL and the library is expected to return ClientHello packet. @@ -318,62 +359,55 @@ int __must_check tls_connection_prf(void *tls_ctx, * 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, u8 **appl_data, - size_t *appl_data_len); +struct wpabuf * tls_connection_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data); /** * 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 - * @in_len: Input data length - * @out_len: Length of the output buffer. - * Returns: pointer to output data, %NULL on failure + * @appl_data: Pointer to application data pointer, or %NULL if dropped + * Returns: Output data, %NULL on failure * - * Caller is responsible for freeing returned output data. + * The caller is responsible for freeing the returned output data. */ -u8 * tls_connection_server_handshake(void *tls_ctx, - struct tls_connection *conn, - const u8 *in_data, size_t in_len, - size_t *out_len); +struct wpabuf * tls_connection_server_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data); /** * 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 - * Returns: Number of bytes written to out_data, -1 on failure + * @in_data: Plaintext data to be encrypted + * Returns: Encrypted TLS data or %NULL on failure * * This function is used after TLS handshake has been completed successfully to - * send data in the encrypted tunnel. + * send data in the encrypted tunnel. The caller is responsible for freeing the + * returned output data. */ -int __must_check tls_connection_encrypt(void *tls_ctx, - struct tls_connection *conn, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len); +struct wpabuf * tls_connection_encrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data); /** * 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 - * Returns: Number of bytes written to out_data, -1 on failure + * @in_data: Encrypted TLS data + * Returns: Decrypted TLS data or %NULL on failure * * This function is used after TLS handshake has been completed successfully to - * receive data from the encrypted tunnel. + * receive data from the encrypted tunnel. The caller is responsible for + * freeing the returned output data. */ -int __must_check tls_connection_decrypt(void *tls_ctx, - struct tls_connection *conn, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len); +struct wpabuf * tls_connection_decrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data); /** * tls_connection_resumed - Was session resumption used @@ -493,16 +527,13 @@ unsigned int tls_capabilities(void *tls_ctx); * @tls_ctx: TLS context data from tls_init() * @conn: Connection context data from tls_connection_init() * @final: 1 = FinalPhaseFinished, 0 = IntermediatePhaseFinished - * @out_data: Pointer to output buffer (encrypted TLS/IA data) - * @out_len: Maximum out_data length - * Returns: Number of bytes written to out_data on success, -1 on failure + * Returns: Encrypted TLS/IA data, %NULL on failure * * This function is used to send the TLS/IA end phase message, e.g., when the * EAP server completes EAP-TTLSv1. */ -int __must_check tls_connection_ia_send_phase_finished( - void *tls_ctx, struct tls_connection *conn, int final, - u8 *out_data, size_t out_len); +struct wpabuf * tls_connection_ia_send_phase_finished( + void *tls_ctx, struct tls_connection *conn, int final); /** * tls_connection_ia_final_phase_finished - Has final phase been completed diff --git a/contrib/wpa/src/crypto/tls_gnutls.c b/contrib/wpa/src/crypto/tls_gnutls.c index 2c5c5a2..c3a7358 100644 --- a/contrib/wpa/src/crypto/tls_gnutls.c +++ b/contrib/wpa/src/crypto/tls_gnutls.c @@ -1,6 +1,6 @@ /* - * WPA Supplicant / SSL/TLS interface functions for openssl - * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * SSL/TLS interface functions for GnuTLS + * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -112,8 +112,9 @@ struct tls_connection { int established; int verify_peer; - u8 *push_buf, *pull_buf, *pull_buf_offset; - size_t push_buf_len, pull_buf_len; + struct wpabuf *push_buf; + struct wpabuf *pull_buf; + const u8 *pull_buf_offset; int params_set; gnutls_certificate_credentials_t xcred; @@ -241,22 +242,22 @@ static ssize_t tls_pull_func(gnutls_transport_ptr ptr, void *buf, size_t len) { struct tls_connection *conn = (struct tls_connection *) ptr; - u8 *end; + const u8 *end; if (conn->pull_buf == NULL) { errno = EWOULDBLOCK; return -1; } - end = conn->pull_buf + conn->pull_buf_len; + end = wpabuf_head_u8(conn->pull_buf) + wpabuf_len(conn->pull_buf); if ((size_t) (end - conn->pull_buf_offset) < len) len = end - conn->pull_buf_offset; os_memcpy(buf, conn->pull_buf_offset, len); conn->pull_buf_offset += len; if (conn->pull_buf_offset == end) { wpa_printf(MSG_DEBUG, "%s - pull_buf consumed", __func__); - os_free(conn->pull_buf); - conn->pull_buf = conn->pull_buf_offset = NULL; - conn->pull_buf_len = 0; + wpabuf_free(conn->pull_buf); + conn->pull_buf = NULL; + conn->pull_buf_offset = NULL; } else { wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in pull_buf", __func__, @@ -270,16 +271,12 @@ static ssize_t tls_push_func(gnutls_transport_ptr ptr, const void *buf, size_t len) { struct tls_connection *conn = (struct tls_connection *) ptr; - u8 *nbuf; - nbuf = os_realloc(conn->push_buf, conn->push_buf_len + len); - if (nbuf == NULL) { + if (wpabuf_resize(&conn->push_buf, len) < 0) { errno = ENOMEM; return -1; } - os_memcpy(nbuf + conn->push_buf_len, buf, len); - conn->push_buf = nbuf; - conn->push_buf_len += len; + wpabuf_put_data(conn->push_buf, buf, len); return len; } @@ -383,8 +380,8 @@ void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn) os_free(conn->pre_shared_secret); os_free(conn->subject_match); os_free(conn->altsubject_match); - os_free(conn->push_buf); - os_free(conn->pull_buf); + wpabuf_free(conn->push_buf); + wpabuf_free(conn->pull_buf); os_free(conn); } @@ -407,9 +404,8 @@ int tls_connection_shutdown(void *ssl_ctx, struct tls_connection *conn) * because the connection was already terminated in practice * and "close notify" shutdown alert would confuse AS. */ gnutls_bye(conn->session, GNUTLS_SHUT_RDWR); - os_free(conn->push_buf); + wpabuf_free(conn->push_buf); conn->push_buf = NULL; - conn->push_buf_len = 0; conn->established = 0; conn->final_phase_finished = 0; #ifdef GNUTLS_IA @@ -979,31 +975,56 @@ static int tls_connection_verify_peer(struct tls_connection *conn, } -u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - size_t *out_len, u8 **appl_data, - size_t *appl_data_len) +static struct wpabuf * gnutls_get_appl_data(struct tls_connection *conn) { - struct tls_global *global = ssl_ctx; - u8 *out_data; + int res; + struct wpabuf *ad; + wpa_printf(MSG_DEBUG, "GnuTLS: Check for possible Application Data"); + ad = wpabuf_alloc((wpabuf_len(conn->pull_buf) + 500) * 3); + if (ad == NULL) + return NULL; + + res = gnutls_record_recv(conn->session, wpabuf_mhead(ad), + wpabuf_size(ad)); + wpa_printf(MSG_DEBUG, "GnuTLS: gnutls_record_recv: %d", res); + if (res < 0) { + wpa_printf(MSG_DEBUG, "%s - gnutls_ia_recv failed: %d " + "(%s)", __func__, (int) res, + gnutls_strerror(res)); + wpabuf_free(ad); + return NULL; + } + + wpabuf_put(ad, res); + wpa_printf(MSG_DEBUG, "GnuTLS: Received %d bytes of Application Data", + res); + return ad; +} + + +struct wpabuf * tls_connection_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data) +{ + struct tls_global *global = tls_ctx; + struct wpabuf *out_data; int ret; if (appl_data) *appl_data = NULL; - if (in_data && in_len) { + if (in_data && wpabuf_len(in_data) > 0) { if (conn->pull_buf) { wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in " "pull_buf", __func__, - (unsigned long) conn->pull_buf_len); - os_free(conn->pull_buf); + (unsigned long) wpabuf_len(conn->pull_buf)); + wpabuf_free(conn->pull_buf); } - conn->pull_buf = os_malloc(in_len); + conn->pull_buf = wpabuf_dup(in_data); if (conn->pull_buf == NULL) return NULL; - os_memcpy(conn->pull_buf, in_data, in_len); - conn->pull_buf_offset = conn->pull_buf; - conn->pull_buf_len = in_len; + conn->pull_buf_offset = wpabuf_head(conn->pull_buf); } ret = gnutls_handshake(conn->session); @@ -1014,7 +1035,7 @@ u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, conn->push_buf == NULL) { /* Need to return something to trigger * completion of EAP-TLS. */ - conn->push_buf = os_malloc(1); + conn->push_buf = wpabuf_alloc(0); } break; case GNUTLS_E_FATAL_ALERT_RECEIVED: @@ -1058,7 +1079,7 @@ u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, conn->established = 1; if (conn->push_buf == NULL) { /* Need to return something to get final TLS ACK. */ - conn->push_buf = os_malloc(1); + conn->push_buf = wpabuf_alloc(0); } gnutls_session_get_data(conn->session, NULL, &size); @@ -1073,90 +1094,88 @@ u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, global->session_data, &global->session_data_size); } + + if (conn->pull_buf && appl_data) + *appl_data = gnutls_get_appl_data(conn); } out: out_data = conn->push_buf; - *out_len = conn->push_buf_len; conn->push_buf = NULL; - conn->push_buf_len = 0; return out_data; } -u8 * tls_connection_server_handshake(void *ssl_ctx, - struct tls_connection *conn, - const u8 *in_data, size_t in_len, - size_t *out_len) +struct wpabuf * tls_connection_server_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data) { - return tls_connection_handshake(ssl_ctx, conn, in_data, in_len, - out_len, NULL, NULL); + return tls_connection_handshake(tls_ctx, conn, in_data, appl_data); } -int tls_connection_encrypt(void *ssl_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len) +struct wpabuf * tls_connection_encrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data) { ssize_t res; + struct wpabuf *buf; #ifdef GNUTLS_IA if (conn->tls_ia) - res = gnutls_ia_send(conn->session, (char *) in_data, in_len); + res = gnutls_ia_send(conn->session, wpabuf_head(in_data), + wpabuf_len(in_data)); else #endif /* GNUTLS_IA */ - res = gnutls_record_send(conn->session, in_data, in_len); + res = gnutls_record_send(conn->session, wpabuf_head(in_data), + wpabuf_len(in_data)); if (res < 0) { wpa_printf(MSG_INFO, "%s: Encryption failed: %s", __func__, gnutls_strerror(res)); - return -1; - } - if (conn->push_buf == NULL) - return -1; - if (conn->push_buf_len < out_len) - out_len = conn->push_buf_len; - else if (conn->push_buf_len > out_len) { - wpa_printf(MSG_INFO, "GnuTLS: Not enough buffer space for " - "encrypted message (in_len=%lu push_buf_len=%lu " - "out_len=%lu", - (unsigned long) in_len, - (unsigned long) conn->push_buf_len, - (unsigned long) out_len); + return NULL; } - os_memcpy(out_data, conn->push_buf, out_len); - os_free(conn->push_buf); + + buf = conn->push_buf; conn->push_buf = NULL; - conn->push_buf_len = 0; - return out_len; + return buf; } -int tls_connection_decrypt(void *ssl_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len) +struct wpabuf * tls_connection_decrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data) { ssize_t res; + struct wpabuf *out; if (conn->pull_buf) { wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in " "pull_buf", __func__, - (unsigned long) conn->pull_buf_len); - os_free(conn->pull_buf); + (unsigned long) wpabuf_len(conn->pull_buf)); + wpabuf_free(conn->pull_buf); } - conn->pull_buf = os_malloc(in_len); + conn->pull_buf = wpabuf_dup(in_data); if (conn->pull_buf == NULL) - return -1; - os_memcpy(conn->pull_buf, in_data, in_len); - conn->pull_buf_offset = conn->pull_buf; - conn->pull_buf_len = in_len; + return NULL; + conn->pull_buf_offset = wpabuf_head(conn->pull_buf); + + /* + * Even though we try to disable TLS compression, it is possible that + * this cannot be done with all TLS libraries. Add extra buffer space + * to handle the possibility of the decrypted data being longer than + * input data. + */ + out = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3); + if (out == NULL) + return NULL; #ifdef GNUTLS_IA if (conn->tls_ia) { - res = gnutls_ia_recv(conn->session, (char *) out_data, - out_len); - if (out_len >= 12 && - (res == GNUTLS_E_WARNING_IA_IPHF_RECEIVED || - res == GNUTLS_E_WARNING_IA_FPHF_RECEIVED)) { + res = gnutls_ia_recv(conn->session, wpabuf_mhead(out), + wpabuf_size(out)); + if (res == GNUTLS_E_WARNING_IA_IPHF_RECEIVED || + res == GNUTLS_E_WARNING_IA_FPHF_RECEIVED) { int final = res == GNUTLS_E_WARNING_IA_FPHF_RECEIVED; wpa_printf(MSG_DEBUG, "%s: Received %sPhaseFinished", __func__, final ? "Final" : "Intermediate"); @@ -1175,11 +1194,12 @@ int tls_connection_decrypt(void *ssl_ctx, struct tls_connection *conn, wpa_printf(MSG_DEBUG, "%s: Failed to permute " "inner secret: %s", __func__, gnutls_strerror(res)); - return -1; + wpabuf_free(out); + return NULL; } res = gnutls_ia_verify_endphase(conn->session, - (char *) out_data); + wpabuf_head(out)); if (res == 0) { wpa_printf(MSG_DEBUG, "%s: Correct endphase " "checksum", __func__); @@ -1187,31 +1207,39 @@ int tls_connection_decrypt(void *ssl_ctx, struct tls_connection *conn, wpa_printf(MSG_INFO, "%s: Endphase " "verification failed: %s", __func__, gnutls_strerror(res)); - return -1; + wpabuf_free(out); + return NULL; } if (final) conn->final_phase_finished = 1; - return 0; + return out; } if (res < 0) { wpa_printf(MSG_DEBUG, "%s - gnutls_ia_recv failed: %d " "(%s)", __func__, (int) res, gnutls_strerror(res)); + wpabuf_free(out); + return NULL; } - return res; + wpabuf_put(out, res); + return out; } #endif /* GNUTLS_IA */ - res = gnutls_record_recv(conn->session, out_data, out_len); + res = gnutls_record_recv(conn->session, wpabuf_mhead(out), + wpabuf_size(out)); if (res < 0) { wpa_printf(MSG_DEBUG, "%s - gnutls_record_recv failed: %d " "(%s)", __func__, (int) res, gnutls_strerror(res)); + wpabuf_free(out); + return NULL; } + wpabuf_put(out, res); - return res; + return out; } @@ -1243,7 +1271,7 @@ int tls_get_cipher(void *ssl_ctx, struct tls_connection *conn, int tls_connection_enable_workaround(void *ssl_ctx, struct tls_connection *conn) { - /* TODO: set SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS */ + gnutls_record_disable_padding(conn->session); return 0; } @@ -1338,16 +1366,15 @@ int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn, } -int tls_connection_ia_send_phase_finished(void *tls_ctx, - struct tls_connection *conn, - int final, - u8 *out_data, size_t out_len) +struct wpabuf * tls_connection_ia_send_phase_finished( + void *tls_ctx, struct tls_connection *conn, int final) { #ifdef GNUTLS_IA int ret; + struct wpabuf *buf; if (conn == NULL || conn->session == NULL || !conn->tls_ia) - return -1; + return NULL; ret = gnutls_ia_permute_inner_secret(conn->session, conn->session_keys_len, @@ -1361,27 +1388,21 @@ int tls_connection_ia_send_phase_finished(void *tls_ctx, if (ret) { wpa_printf(MSG_DEBUG, "%s: Failed to permute inner secret: %s", __func__, gnutls_strerror(ret)); - return -1; + return NULL; } ret = gnutls_ia_endphase_send(conn->session, final); if (ret) { wpa_printf(MSG_DEBUG, "%s: Failed to send endphase: %s", __func__, gnutls_strerror(ret)); - return -1; + return NULL; } - if (conn->push_buf == NULL) - return -1; - if (conn->push_buf_len < out_len) - out_len = conn->push_buf_len; - os_memcpy(out_data, conn->push_buf, out_len); - os_free(conn->push_buf); + buf = conn->push_buf; conn->push_buf = NULL; - conn->push_buf_len = 0; - return out_len; + return buf; #else /* GNUTLS_IA */ - return -1; + return NULL; #endif /* GNUTLS_IA */ } @@ -1426,3 +1447,11 @@ int tls_connection_ia_permute_inner_secret(void *tls_ctx, return -1; #endif /* GNUTLS_IA */ } + + +int tls_connection_set_session_ticket_cb(void *tls_ctx, + struct tls_connection *conn, + tls_session_ticket_cb cb, void *ctx) +{ + return -1; +} diff --git a/contrib/wpa/src/crypto/tls_internal.c b/contrib/wpa/src/crypto/tls_internal.c index 42120c8..64124d8 100644 --- a/contrib/wpa/src/crypto/tls_internal.c +++ b/contrib/wpa/src/crypto/tls_internal.c @@ -1,6 +1,6 @@ /* - * WPA Supplicant / TLS interface functions and an internal TLS implementation - * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * TLS interface functions and an internal TLS implementation + * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -331,45 +331,77 @@ int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, } -u8 * tls_connection_handshake(void *tls_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - size_t *out_len, u8 **appl_data, - size_t *appl_data_len) +struct wpabuf * tls_connection_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data) { #ifdef CONFIG_TLS_INTERNAL_CLIENT + u8 *res, *ad; + size_t res_len, ad_len; + struct wpabuf *out; + if (conn->client == NULL) return NULL; - if (appl_data) - *appl_data = NULL; + ad = NULL; + res = tlsv1_client_handshake(conn->client, + in_data ? wpabuf_head(in_data) : NULL, + in_data ? wpabuf_len(in_data) : 0, + &res_len, &ad, &ad_len); + if (res == NULL) + return NULL; + out = wpabuf_alloc_ext_data(res, res_len); + if (out == NULL) { + os_free(res); + os_free(ad); + return NULL; + } + if (appl_data) { + if (ad) { + *appl_data = wpabuf_alloc_ext_data(ad, ad_len); + if (*appl_data == NULL) + os_free(ad); + } else + *appl_data = NULL; + } else + os_free(ad); - wpa_printf(MSG_DEBUG, "TLS: %s(in_data=%p in_len=%lu)", - __func__, in_data, (unsigned long) in_len); - return tlsv1_client_handshake(conn->client, in_data, in_len, out_len, - appl_data, appl_data_len); + return out; #else /* CONFIG_TLS_INTERNAL_CLIENT */ return NULL; #endif /* CONFIG_TLS_INTERNAL_CLIENT */ } -u8 * tls_connection_server_handshake(void *tls_ctx, - struct tls_connection *conn, - const u8 *in_data, size_t in_len, - size_t *out_len) +struct wpabuf * tls_connection_server_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data) { #ifdef CONFIG_TLS_INTERNAL_SERVER - u8 *out; + u8 *res; + size_t res_len; + struct wpabuf *out; + if (conn->server == NULL) return NULL; - wpa_printf(MSG_DEBUG, "TLS: %s(in_data=%p in_len=%lu)", - __func__, in_data, (unsigned long) in_len); - out = tlsv1_server_handshake(conn->server, in_data, in_len, out_len); - if (out == NULL && tlsv1_server_established(conn->server)) { - out = os_malloc(1); - *out_len = 0; + if (appl_data) + *appl_data = NULL; + + res = tlsv1_server_handshake(conn->server, wpabuf_head(in_data), + wpabuf_len(in_data), &res_len); + if (res == NULL && tlsv1_server_established(conn->server)) + return wpabuf_alloc(0); + if (res == NULL) + return NULL; + out = wpabuf_alloc_ext_data(res, res_len); + if (out == NULL) { + os_free(res); + return NULL; } + return out; #else /* CONFIG_TLS_INTERNAL_SERVER */ return NULL; @@ -377,43 +409,95 @@ u8 * tls_connection_server_handshake(void *tls_ctx, } -int tls_connection_encrypt(void *tls_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len) +struct wpabuf * tls_connection_encrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data) { #ifdef CONFIG_TLS_INTERNAL_CLIENT if (conn->client) { - return tlsv1_client_encrypt(conn->client, in_data, in_len, - out_data, out_len); + struct wpabuf *buf; + int res; + buf = wpabuf_alloc(wpabuf_len(in_data) + 300); + if (buf == NULL) + return NULL; + res = tlsv1_client_encrypt(conn->client, wpabuf_head(in_data), + wpabuf_len(in_data), + wpabuf_mhead(buf), + wpabuf_size(buf)); + if (res < 0) { + wpabuf_free(buf); + return NULL; + } + wpabuf_put(buf, res); + return buf; } #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER if (conn->server) { - return tlsv1_server_encrypt(conn->server, in_data, in_len, - out_data, out_len); + struct wpabuf *buf; + int res; + buf = wpabuf_alloc(wpabuf_len(in_data) + 300); + if (buf == NULL) + return NULL; + res = tlsv1_server_encrypt(conn->server, wpabuf_head(in_data), + wpabuf_len(in_data), + wpabuf_mhead(buf), + wpabuf_size(buf)); + if (res < 0) { + wpabuf_free(buf); + return NULL; + } + wpabuf_put(buf, res); + return buf; } #endif /* CONFIG_TLS_INTERNAL_SERVER */ - return -1; + return NULL; } -int tls_connection_decrypt(void *tls_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len) +struct wpabuf * tls_connection_decrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data) { #ifdef CONFIG_TLS_INTERNAL_CLIENT if (conn->client) { - return tlsv1_client_decrypt(conn->client, in_data, in_len, - out_data, out_len); + struct wpabuf *buf; + int res; + buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3); + if (buf == NULL) + return NULL; + res = tlsv1_client_decrypt(conn->client, wpabuf_head(in_data), + wpabuf_len(in_data), + wpabuf_mhead(buf), + wpabuf_size(buf)); + if (res < 0) { + wpabuf_free(buf); + return NULL; + } + wpabuf_put(buf, res); + return buf; } #endif /* CONFIG_TLS_INTERNAL_CLIENT */ #ifdef CONFIG_TLS_INTERNAL_SERVER if (conn->server) { - return tlsv1_server_decrypt(conn->server, in_data, in_len, - out_data, out_len); + struct wpabuf *buf; + int res; + buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3); + if (buf == NULL) + return NULL; + res = tlsv1_server_decrypt(conn->server, wpabuf_head(in_data), + wpabuf_len(in_data), + wpabuf_mhead(buf), + wpabuf_size(buf)); + if (res < 0) { + wpabuf_free(buf); + return NULL; + } + wpabuf_put(buf, res); + return buf; } #endif /* CONFIG_TLS_INTERNAL_SERVER */ - return -1; + return NULL; } @@ -524,12 +608,10 @@ unsigned int tls_capabilities(void *tls_ctx) } -int tls_connection_ia_send_phase_finished(void *tls_ctx, - struct tls_connection *conn, - int final, - u8 *out_data, size_t out_len) +struct wpabuf * tls_connection_ia_send_phase_finished( + void *tls_ctx, struct tls_connection *conn, int final) { - return -1; + return NULL; } diff --git a/contrib/wpa/src/crypto/tls_none.c b/contrib/wpa/src/crypto/tls_none.c index f731628..0c836bb 100644 --- a/contrib/wpa/src/crypto/tls_none.c +++ b/contrib/wpa/src/crypto/tls_none.c @@ -1,6 +1,6 @@ /* - * WPA Supplicant / SSL/TLS interface functions for no TLS case - * Copyright (c) 2004, Jouni Malinen <j@w1.fi> + * SSL/TLS interface functions for no TLS case + * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -22,13 +22,12 @@ void * tls_init(const struct tls_config *conf) return (void *) 1; } + void tls_deinit(void *ssl_ctx) { } -#ifdef EAP_TLS_NONE - int tls_get_errors(void *tls_ctx) { return 0; @@ -107,37 +106,37 @@ int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, } -u8 * tls_connection_handshake(void *tls_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - size_t *out_len, u8 **appl_data, - size_t *appl_data_len) +struct wpabuf * tls_connection_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data) { return NULL; } -u8 * tls_connection_server_handshake(void *tls_ctx, - struct tls_connection *conn, - const u8 *in_data, size_t in_len, - size_t *out_len) +struct wpabuf * tls_connection_server_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data) { return NULL; } -int tls_connection_encrypt(void *tls_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len) +struct wpabuf * tls_connection_encrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data) { - return -1; + return NULL; } -int tls_connection_decrypt(void *tls_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len) +struct wpabuf * tls_connection_decrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data) { - return -1; + return NULL; } @@ -208,12 +207,10 @@ unsigned int tls_capabilities(void *tls_ctx) } -int tls_connection_ia_send_phase_finished(void *tls_ctx, - struct tls_connection *conn, - int final, - u8 *out_data, size_t out_len) +struct wpabuf * tls_connection_ia_send_phase_finished( + void *tls_ctx, struct tls_connection *conn, int final) { - return -1; + return NULL; } @@ -230,5 +227,3 @@ int tls_connection_ia_permute_inner_secret(void *tls_ctx, { return -1; } - -#endif /* EAP_TLS_NONE */ diff --git a/contrib/wpa/src/crypto/tls_nss.c b/contrib/wpa/src/crypto/tls_nss.c new file mode 100644 index 0000000..ad834b6 --- /dev/null +++ b/contrib/wpa/src/crypto/tls_nss.c @@ -0,0 +1,680 @@ +/* + * SSL/TLS interface functions for NSS + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <nspr/prtypes.h> +#include <nspr/plarenas.h> +#include <nspr/plhash.h> +#include <nspr/prio.h> +#include <nspr/prclist.h> +#include <nspr/prlock.h> +#include <nspr/prinit.h> +#include <nspr/prerror.h> +#include <nspr/prmem.h> +#include <nss/nss.h> +#include <nss/nssilckt.h> +#include <nss/ssl.h> +#include <nss/pk11func.h> +#include <nss/secerr.h> + +#include "common.h" +#include "tls.h" + +static int tls_nss_ref_count = 0; + +static PRDescIdentity nss_layer_id; + + +struct tls_connection { + PRFileDesc *fd; + + int established; + int verify_peer; + u8 *push_buf, *pull_buf, *pull_buf_offset; + size_t push_buf_len, pull_buf_len; +}; + + +static PRStatus nss_io_close(PRFileDesc *fd) +{ + wpa_printf(MSG_DEBUG, "NSS: I/O close"); + return PR_SUCCESS; +} + + +static PRInt32 nss_io_read(PRFileDesc *fd, void *buf, PRInt32 amount) +{ + wpa_printf(MSG_DEBUG, "NSS: I/O read(%d)", amount); + return PR_FAILURE; +} + + +static PRInt32 nss_io_write(PRFileDesc *fd, const void *buf, PRInt32 amount) +{ + wpa_printf(MSG_DEBUG, "NSS: I/O write(%d)", amount); + return PR_FAILURE; +} + + +static PRInt32 nss_io_writev(PRFileDesc *fd, const PRIOVec *iov, + PRInt32 iov_size, PRIntervalTime timeout) +{ + wpa_printf(MSG_DEBUG, "NSS: I/O writev(%d)", iov_size); + return PR_FAILURE; +} + + +static PRInt32 nss_io_recv(PRFileDesc *fd, void *buf, PRInt32 amount, + PRIntn flags, PRIntervalTime timeout) +{ + struct tls_connection *conn = (struct tls_connection *) fd->secret; + u8 *end; + + wpa_printf(MSG_DEBUG, "NSS: I/O recv(%d)", amount); + + if (conn->pull_buf == NULL) { + wpa_printf(MSG_DEBUG, "NSS: No data available to be read yet"); + return PR_FAILURE; + } + + end = conn->pull_buf + conn->pull_buf_len; + if (end - conn->pull_buf_offset < amount) + amount = end - conn->pull_buf_offset; + os_memcpy(buf, conn->pull_buf_offset, amount); + conn->pull_buf_offset += amount; + if (conn->pull_buf_offset == end) { + wpa_printf(MSG_DEBUG, "%s - pull_buf consumed", __func__); + os_free(conn->pull_buf); + conn->pull_buf = conn->pull_buf_offset = NULL; + conn->pull_buf_len = 0; + } else { + wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in pull_buf", + __func__, + (unsigned long) (end - conn->pull_buf_offset)); + } + return amount; +} + + +static PRInt32 nss_io_send(PRFileDesc *fd, const void *buf, PRInt32 amount, + PRIntn flags, PRIntervalTime timeout) +{ + struct tls_connection *conn = (struct tls_connection *) fd->secret; + u8 *nbuf; + + wpa_printf(MSG_DEBUG, "NSS: I/O %s", __func__); + wpa_hexdump(MSG_MSGDUMP, "NSS: I/O send data", buf, amount); + + nbuf = os_realloc(conn->push_buf, conn->push_buf_len + amount); + if (nbuf == NULL) { + wpa_printf(MSG_ERROR, "NSS: Failed to allocate memory for the " + "data to be sent"); + return PR_FAILURE; + } + os_memcpy(nbuf + conn->push_buf_len, buf, amount); + conn->push_buf = nbuf; + conn->push_buf_len += amount; + + return amount; +} + + +static PRInt32 nss_io_recvfrom(PRFileDesc *fd, void *buf, PRInt32 amount, + PRIntn flags, PRNetAddr *addr, + PRIntervalTime timeout) +{ + wpa_printf(MSG_DEBUG, "NSS: I/O %s", __func__); + return PR_FAILURE; +} + + +static PRInt32 nss_io_sendto(PRFileDesc *fd, const void *buf, PRInt32 amount, + PRIntn flags, const PRNetAddr *addr, + PRIntervalTime timeout) +{ + wpa_printf(MSG_DEBUG, "NSS: I/O %s", __func__); + return PR_FAILURE; +} + + +static PRStatus nss_io_getpeername(PRFileDesc *fd, PRNetAddr *addr) +{ + wpa_printf(MSG_DEBUG, "NSS: I/O getpeername"); + + /* + * It Looks like NSS only supports IPv4 and IPv6 TCP sockets. Provide a + * fake IPv4 address to work around this even though we are not really + * using TCP. + */ + os_memset(addr, 0, sizeof(*addr)); + addr->inet.family = PR_AF_INET; + + return PR_SUCCESS; +} + + +static PRStatus nss_io_getsocketoption(PRFileDesc *fd, + PRSocketOptionData *data) +{ + switch (data->option) { + case PR_SockOpt_Nonblocking: + wpa_printf(MSG_DEBUG, "NSS: I/O getsocketoption(Nonblocking)"); + data->value.non_blocking = PR_TRUE; + return PR_SUCCESS; + default: + wpa_printf(MSG_DEBUG, "NSS: I/O getsocketoption(%d)", + data->option); + return PR_FAILURE; + } +} + + +static const PRIOMethods nss_io = { + PR_DESC_LAYERED, + nss_io_close, + nss_io_read, + nss_io_write, + NULL /* available */, + NULL /* available64 */, + NULL /* fsync */, + NULL /* fseek */, + NULL /* fseek64 */, + NULL /* fileinfo */, + NULL /* fileinfo64 */, + nss_io_writev, + NULL /* connect */, + NULL /* accept */, + NULL /* bind */, + NULL /* listen */, + NULL /* shutdown */, + nss_io_recv, + nss_io_send, + nss_io_recvfrom, + nss_io_sendto, + NULL /* poll */, + NULL /* acceptread */, + NULL /* transmitfile */, + NULL /* getsockname */, + nss_io_getpeername, + NULL /* reserved_fn_6 */, + NULL /* reserved_fn_5 */, + nss_io_getsocketoption, + NULL /* setsocketoption */, + NULL /* sendfile */, + NULL /* connectcontinue */, + NULL /* reserved_fn_3 */, + NULL /* reserved_fn_2 */, + NULL /* reserved_fn_1 */, + NULL /* reserved_fn_0 */ +}; + + +static char * nss_password_cb(PK11SlotInfo *slot, PRBool retry, void *arg) +{ + wpa_printf(MSG_ERROR, "NSS: TODO - %s", __func__); + return NULL; +} + + +void * tls_init(const struct tls_config *conf) +{ + char *dir; + + tls_nss_ref_count++; + if (tls_nss_ref_count > 1) + return (void *) 1; + + PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1); + + nss_layer_id = PR_GetUniqueIdentity("wpa_supplicant"); + + PK11_SetPasswordFunc(nss_password_cb); + + dir = getenv("SSL_DIR"); + if (dir) { + if (NSS_Init(dir) != SECSuccess) { + wpa_printf(MSG_ERROR, "NSS: NSS_Init(cert_dir=%s) " + "failed", dir); + return NULL; + } + } else { + if (NSS_NoDB_Init(NULL) != SECSuccess) { + wpa_printf(MSG_ERROR, "NSS: NSS_NoDB_Init(NULL) " + "failed"); + return NULL; + } + } + + if (SSL_OptionSetDefault(SSL_V2_COMPATIBLE_HELLO, PR_FALSE) != + SECSuccess || + SSL_OptionSetDefault(SSL_ENABLE_SSL3, PR_FALSE) != SECSuccess || + SSL_OptionSetDefault(SSL_ENABLE_SSL2, PR_FALSE) != SECSuccess || + SSL_OptionSetDefault(SSL_ENABLE_TLS, PR_TRUE) != SECSuccess) { + wpa_printf(MSG_ERROR, "NSS: SSL_OptionSetDefault failed"); + return NULL; + } + + if (NSS_SetDomesticPolicy() != SECSuccess) { + wpa_printf(MSG_ERROR, "NSS: NSS_SetDomesticPolicy() failed"); + return NULL; + } + + return (void *) 1; +} + +void tls_deinit(void *ssl_ctx) +{ + tls_nss_ref_count--; + if (tls_nss_ref_count == 0) { + if (NSS_Shutdown() != SECSuccess) + wpa_printf(MSG_ERROR, "NSS: NSS_Shutdown() failed"); + } +} + + +int tls_get_errors(void *tls_ctx) +{ + return 0; +} + + +static SECStatus nss_bad_cert_cb(void *arg, PRFileDesc *fd) +{ + struct tls_connection *conn = arg; + SECStatus res = SECSuccess; + PRErrorCode err; + CERTCertificate *cert; + char *subject, *issuer; + + err = PR_GetError(); + if (IS_SEC_ERROR(err)) + wpa_printf(MSG_DEBUG, "NSS: Bad Server Certificate (sec err " + "%d)", err - SEC_ERROR_BASE); + else + wpa_printf(MSG_DEBUG, "NSS: Bad Server Certificate (err %d)", + err); + cert = SSL_PeerCertificate(fd); + subject = CERT_NameToAscii(&cert->subject); + issuer = CERT_NameToAscii(&cert->issuer); + wpa_printf(MSG_DEBUG, "NSS: Peer certificate subject='%s' issuer='%s'", + subject, issuer); + CERT_DestroyCertificate(cert); + PR_Free(subject); + PR_Free(issuer); + if (conn->verify_peer) + res = SECFailure; + + return res; +} + + +static void nss_handshake_cb(PRFileDesc *fd, void *client_data) +{ + struct tls_connection *conn = client_data; + wpa_printf(MSG_DEBUG, "NSS: Handshake completed"); + conn->established = 1; +} + + +struct tls_connection * tls_connection_init(void *tls_ctx) +{ + struct tls_connection *conn; + + conn = os_zalloc(sizeof(*conn)); + if (conn == NULL) + return NULL; + + conn->fd = PR_CreateIOLayerStub(nss_layer_id, &nss_io); + if (conn->fd == NULL) { + os_free(conn); + return NULL; + } + conn->fd->secret = (void *) conn; + + conn->fd = SSL_ImportFD(NULL, conn->fd); + if (conn->fd == NULL) { + os_free(conn); + return NULL; + } + + if (SSL_OptionSet(conn->fd, SSL_SECURITY, PR_TRUE) != SECSuccess || + SSL_OptionSet(conn->fd, SSL_HANDSHAKE_AS_CLIENT, PR_TRUE) != + SECSuccess || + SSL_OptionSet(conn->fd, SSL_HANDSHAKE_AS_SERVER, PR_FALSE) != + SECSuccess || + SSL_OptionSet(conn->fd, SSL_ENABLE_TLS, PR_TRUE) != SECSuccess || + SSL_BadCertHook(conn->fd, nss_bad_cert_cb, conn) != SECSuccess || + SSL_HandshakeCallback(conn->fd, nss_handshake_cb, conn) != + SECSuccess) { + wpa_printf(MSG_ERROR, "NSS: Failed to set options"); + PR_Close(conn->fd); + os_free(conn); + return NULL; + } + + SSL_ResetHandshake(conn->fd, PR_FALSE); + + return conn; +} + + +void tls_connection_deinit(void *tls_ctx, struct tls_connection *conn) +{ + PR_Close(conn->fd); + os_free(conn->push_buf); + os_free(conn->pull_buf); + os_free(conn); +} + + +int tls_connection_established(void *tls_ctx, struct tls_connection *conn) +{ + return conn->established; +} + + +int tls_connection_shutdown(void *tls_ctx, struct tls_connection *conn) +{ + return -1; +} + + +int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn, + const struct tls_connection_params *params) +{ + wpa_printf(MSG_ERROR, "NSS: TODO - %s", __func__); + return 0; +} + + +int tls_global_set_params(void *tls_ctx, + const struct tls_connection_params *params) +{ + return -1; +} + + +int tls_global_set_verify(void *tls_ctx, int check_crl) +{ + return -1; +} + + +int tls_connection_set_verify(void *tls_ctx, struct tls_connection *conn, + int verify_peer) +{ + conn->verify_peer = verify_peer; + return 0; +} + + +int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn, + int tls_ia) +{ + return -1; +} + + +int tls_connection_get_keys(void *tls_ctx, struct tls_connection *conn, + struct tls_keys *keys) +{ + /* NSS does not export master secret or client/server random. */ + return -1; +} + + +int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, + const char *label, int server_random_first, + u8 *out, size_t out_len) +{ + if (conn == NULL || server_random_first) { + wpa_printf(MSG_INFO, "NSS: Unsupported PRF request " + "(server_random_first=%d)", + server_random_first); + return -1; + } + + if (SSL_ExportKeyingMaterial(conn->fd, label, NULL, 0, out, out_len) != + SECSuccess) { + wpa_printf(MSG_INFO, "NSS: Failed to use TLS extractor " + "(label='%s' out_len=%d", label, (int) out_len); + return -1; + } + + return 0; +} + + +struct wpabuf * tls_connection_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data) +{ + struct wpabuf *out_data; + + wpa_printf(MSG_DEBUG, "NSS: handshake: in_len=%u", + in_data ? (unsigned int) wpabuf_len(in_data) : 0); + + if (appl_data) + *appl_data = NULL; + + if (in_data && wpabuf_len(in_data) > 0) { + if (conn->pull_buf) { + wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in " + "pull_buf", __func__, + (unsigned long) conn->pull_buf_len); + os_free(conn->pull_buf); + } + conn->pull_buf = os_malloc(wpabuf_len(in_data)); + if (conn->pull_buf == NULL) + return NULL; + os_memcpy(conn->pull_buf, wpabuf_head(in_data), + wpabuf_len(in_data)); + conn->pull_buf_offset = conn->pull_buf; + conn->pull_buf_len = wpabuf_len(in_data); + } + + SSL_ForceHandshake(conn->fd); + + if (conn->established && conn->push_buf == NULL) { + /* Need to return something to get final TLS ACK. */ + conn->push_buf = os_malloc(1); + } + + if (conn->push_buf == NULL) + return NULL; + out_data = wpabuf_alloc_ext_data(conn->push_buf, conn->push_buf_len); + if (out_data == NULL) + os_free(conn->push_buf); + conn->push_buf = NULL; + conn->push_buf_len = 0; + return out_data; +} + + +struct wpabuf * tls_connection_server_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data) +{ + return NULL; +} + + +struct wpabuf * tls_connection_encrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data) +{ + PRInt32 res; + struct wpabuf *buf; + + wpa_printf(MSG_DEBUG, "NSS: encrypt %d bytes", + (int) wpabuf_len(in_data)); + res = PR_Send(conn->fd, wpabuf_head(in_data), wpabuf_len(in_data), 0, + 0); + if (res < 0) { + wpa_printf(MSG_ERROR, "NSS: Encryption failed"); + return NULL; + } + if (conn->push_buf == NULL) + return NULL; + buf = wpabuf_alloc_ext_data(conn->push_buf, conn->push_buf_len); + if (buf == NULL) + os_free(conn->push_buf); + conn->push_buf = NULL; + conn->push_buf_len = 0; + return buf; +} + + +struct wpabuf * tls_connection_decrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data) +{ + PRInt32 res; + struct wpabuf *out; + + wpa_printf(MSG_DEBUG, "NSS: decrypt %d bytes", + (int) wpabuf_len(in_data)); + if (conn->pull_buf) { + wpa_printf(MSG_DEBUG, "%s - %lu bytes remaining in " + "pull_buf", __func__, + (unsigned long) conn->pull_buf_len); + os_free(conn->pull_buf); + } + conn->pull_buf = os_malloc(wpabuf_len(in_data)); + if (conn->pull_buf == NULL) + return NULL; + os_memcpy(conn->pull_buf, wpabuf_head(in_data), wpabuf_len(in_data)); + conn->pull_buf_offset = conn->pull_buf; + conn->pull_buf_len = wpabuf_len(in_data); + + /* + * Even though we try to disable TLS compression, it is possible that + * this cannot be done with all TLS libraries. Add extra buffer space + * to handle the possibility of the decrypted data being longer than + * input data. + */ + out = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3); + if (out == NULL) + return NULL; + + res = PR_Recv(conn->fd, wpabuf_mhead(out), wpabuf_size(out), 0, 0); + wpa_printf(MSG_DEBUG, "NSS: PR_Recv: %d", res); + if (res < 0) { + wpabuf_free(out); + return NULL; + } + wpabuf_put(out, res); + + return out; +} + + +int tls_connection_resumed(void *tls_ctx, struct tls_connection *conn) +{ + return 0; +} + + +int tls_connection_set_cipher_list(void *tls_ctx, struct tls_connection *conn, + u8 *ciphers) +{ + return -1; +} + + +int tls_get_cipher(void *tls_ctx, struct tls_connection *conn, + char *buf, size_t buflen) +{ + return -1; +} + + +int tls_connection_enable_workaround(void *tls_ctx, + struct tls_connection *conn) +{ + return -1; +} + + +int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn, + int ext_type, const u8 *data, + size_t data_len) +{ + return -1; +} + + +int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn) +{ + return 0; +} + + +int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn) +{ + return 0; +} + + +int tls_connection_get_write_alerts(void *tls_ctx, + struct tls_connection *conn) +{ + return 0; +} + + +int tls_connection_get_keyblock_size(void *tls_ctx, + struct tls_connection *conn) +{ + return -1; +} + + +unsigned int tls_capabilities(void *tls_ctx) +{ + return 0; +} + + +struct wpabuf * tls_connection_ia_send_phase_finished( + void *tls_ctx, struct tls_connection *conn, int final) +{ + return NULL; +} + + +int tls_connection_ia_final_phase_finished(void *tls_ctx, + struct tls_connection *conn) +{ + return -1; +} + + +int tls_connection_ia_permute_inner_secret(void *tls_ctx, + struct tls_connection *conn, + const u8 *key, size_t key_len) +{ + return -1; +} + + +int tls_connection_set_session_ticket_cb(void *tls_ctx, + struct tls_connection *conn, + tls_session_ticket_cb cb, + void *ctx) +{ + return -1; +} diff --git a/contrib/wpa/src/crypto/tls_openssl.c b/contrib/wpa/src/crypto/tls_openssl.c index b5a1d64..c0a40f9 100644 --- a/contrib/wpa/src/crypto/tls_openssl.c +++ b/contrib/wpa/src/crypto/tls_openssl.c @@ -1,6 +1,6 @@ /* - * WPA Supplicant / SSL/TLS interface functions for openssl - * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * SSL/TLS interface functions for OpenSSL + * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -29,6 +29,7 @@ #endif /* OPENSSL_NO_ENGINE */ #include "common.h" +#include "crypto.h" #include "tls.h" #if OPENSSL_VERSION_NUMBER >= 0x0090800fL @@ -49,6 +50,15 @@ static int tls_openssl_ref_count = 0; +struct tls_global { + void (*event_cb)(void *ctx, enum tls_event ev, + union tls_event_data *data); + void *cb_ctx; +}; + +static struct tls_global *tls_global = NULL; + + struct tls_connection { SSL *ssl; BIO *ssl_in, *ssl_out; @@ -65,6 +75,12 @@ struct tls_connection { /* SessionTicket received from OpenSSL hello_extension_cb (server) */ u8 *session_ticket; size_t session_ticket_len; + + unsigned int ca_cert_verify:1; + unsigned int cert_probe:1; + unsigned int server_cert_only:1; + + u8 srv_cert_hash[32]; }; @@ -665,6 +681,34 @@ void * tls_init(const struct tls_config *conf) SSL_CTX *ssl; if (tls_openssl_ref_count == 0) { + tls_global = os_zalloc(sizeof(*tls_global)); + if (tls_global == NULL) + return NULL; + if (conf) { + tls_global->event_cb = conf->event_cb; + tls_global->cb_ctx = conf->cb_ctx; + } + +#ifdef CONFIG_FIPS +#ifdef OPENSSL_FIPS + if (conf && conf->fips_mode) { + if (!FIPS_mode_set(1)) { + wpa_printf(MSG_ERROR, "Failed to enable FIPS " + "mode"); + ERR_load_crypto_strings(); + ERR_print_errors_fp(stderr); + return NULL; + } else + wpa_printf(MSG_INFO, "Running in FIPS mode"); + } +#else /* OPENSSL_FIPS */ + if (conf && conf->fips_mode) { + wpa_printf(MSG_ERROR, "FIPS mode requested, but not " + "supported"); + return NULL; + } +#endif /* OPENSSL_FIPS */ +#endif /* CONFIG_FIPS */ SSL_load_error_strings(); SSL_library_init(); #ifndef OPENSSL_NO_SHA256 @@ -730,6 +774,8 @@ void tls_deinit(void *ssl_ctx) ERR_remove_state(0); ERR_free_strings(); EVP_cleanup(); + os_free(tls_global); + tls_global = NULL; } } @@ -996,6 +1042,124 @@ static int tls_match_altsubject(X509 *cert, const char *match) } +static enum tls_fail_reason openssl_tls_fail_reason(int err) +{ + switch (err) { + case X509_V_ERR_CERT_REVOKED: + return TLS_FAIL_REVOKED; + case X509_V_ERR_CERT_NOT_YET_VALID: + case X509_V_ERR_CRL_NOT_YET_VALID: + return TLS_FAIL_NOT_YET_VALID; + case X509_V_ERR_CERT_HAS_EXPIRED: + case X509_V_ERR_CRL_HAS_EXPIRED: + return TLS_FAIL_EXPIRED; + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: + case X509_V_ERR_UNABLE_TO_GET_CRL: + case X509_V_ERR_UNABLE_TO_GET_CRL_ISSUER: + case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: + case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: + case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: + case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE: + case X509_V_ERR_CERT_CHAIN_TOO_LONG: + case X509_V_ERR_PATH_LENGTH_EXCEEDED: + case X509_V_ERR_INVALID_CA: + return TLS_FAIL_UNTRUSTED; + case X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE: + case X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE: + case X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY: + case X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD: + case X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD: + case X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD: + case X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD: + case X509_V_ERR_CERT_UNTRUSTED: + case X509_V_ERR_CERT_REJECTED: + return TLS_FAIL_BAD_CERTIFICATE; + default: + return TLS_FAIL_UNSPECIFIED; + } +} + + +static struct wpabuf * get_x509_cert(X509 *cert) +{ + struct wpabuf *buf; + u8 *tmp; + + int cert_len = i2d_X509(cert, NULL); + if (cert_len <= 0) + return NULL; + + buf = wpabuf_alloc(cert_len); + if (buf == NULL) + return NULL; + + tmp = wpabuf_put(buf, cert_len); + i2d_X509(cert, &tmp); + return buf; +} + + +static void openssl_tls_fail_event(struct tls_connection *conn, + X509 *err_cert, int err, int depth, + const char *subject, const char *err_str, + enum tls_fail_reason reason) +{ + union tls_event_data ev; + struct wpabuf *cert = NULL; + + if (tls_global->event_cb == NULL) + return; + + cert = get_x509_cert(err_cert); + os_memset(&ev, 0, sizeof(ev)); + ev.cert_fail.reason = reason != TLS_FAIL_UNSPECIFIED ? + reason : openssl_tls_fail_reason(err); + ev.cert_fail.depth = depth; + ev.cert_fail.subject = subject; + ev.cert_fail.reason_txt = err_str; + ev.cert_fail.cert = cert; + tls_global->event_cb(tls_global->cb_ctx, TLS_CERT_CHAIN_FAILURE, &ev); + wpabuf_free(cert); +} + + +static void openssl_tls_cert_event(struct tls_connection *conn, + X509 *err_cert, int depth, + const char *subject) +{ + struct wpabuf *cert = NULL; + union tls_event_data ev; +#ifdef CONFIG_SHA256 + u8 hash[32]; +#endif /* CONFIG_SHA256 */ + + if (tls_global->event_cb == NULL) + return; + + os_memset(&ev, 0, sizeof(ev)); + if (conn->cert_probe) { + cert = get_x509_cert(err_cert); + ev.peer_cert.cert = cert; + } +#ifdef CONFIG_SHA256 + if (cert) { + const u8 *addr[1]; + size_t len[1]; + addr[0] = wpabuf_head(cert); + len[0] = wpabuf_len(cert); + if (sha256_vector(1, addr, len, hash) == 0) { + ev.peer_cert.hash = hash; + ev.peer_cert.hash_len = sizeof(hash); + } + } +#endif /* CONFIG_SHA256 */ + ev.peer_cert.depth = depth; + ev.peer_cert.subject = subject; + tls_global->event_cb(tls_global->cb_ctx, TLS_PEER_CERTIFICATE, &ev); + wpabuf_free(cert); +} + + static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) { char buf[256]; @@ -1004,6 +1168,7 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) SSL *ssl; struct tls_connection *conn; char *match, *altmatch; + const char *err_str; err_cert = X509_STORE_CTX_get_current_cert(x509_ctx); err = X509_STORE_CTX_get_error(x509_ctx); @@ -1016,26 +1181,77 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) match = conn ? conn->subject_match : NULL; altmatch = conn ? conn->altsubject_match : NULL; - if (!preverify_ok) { - wpa_printf(MSG_WARNING, "TLS: Certificate verification failed," - " error %d (%s) depth %d for '%s'", err, - X509_verify_cert_error_string(err), depth, buf); - } else { - wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - " - "preverify_ok=%d err=%d (%s) depth=%d buf='%s'", - preverify_ok, err, - X509_verify_cert_error_string(err), depth, buf); - if (depth == 0 && match && os_strstr(buf, match) == NULL) { - wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not " - "match with '%s'", buf, match); - preverify_ok = 0; - } else if (depth == 0 && altmatch && - !tls_match_altsubject(err_cert, altmatch)) { - wpa_printf(MSG_WARNING, "TLS: altSubjectName match " - "'%s' not found", altmatch); + if (!preverify_ok && !conn->ca_cert_verify) + preverify_ok = 1; + if (!preverify_ok && depth > 0 && conn->server_cert_only) + preverify_ok = 1; + + err_str = X509_verify_cert_error_string(err); + +#ifdef CONFIG_SHA256 + if (preverify_ok && depth == 0 && conn->server_cert_only) { + struct wpabuf *cert; + cert = get_x509_cert(err_cert); + if (!cert) { + wpa_printf(MSG_DEBUG, "OpenSSL: Could not fetch " + "server certificate data"); preverify_ok = 0; + } else { + u8 hash[32]; + const u8 *addr[1]; + size_t len[1]; + addr[0] = wpabuf_head(cert); + len[0] = wpabuf_len(cert); + if (sha256_vector(1, addr, len, hash) < 0 || + os_memcmp(conn->srv_cert_hash, hash, 32) != 0) { + err_str = "Server certificate mismatch"; + err = X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN; + preverify_ok = 0; + } + wpabuf_free(cert); } } +#endif /* CONFIG_SHA256 */ + + if (!preverify_ok) { + wpa_printf(MSG_WARNING, "TLS: Certificate verification failed," + " error %d (%s) depth %d for '%s'", err, err_str, + depth, buf); + openssl_tls_fail_event(conn, err_cert, err, depth, buf, + err_str, TLS_FAIL_UNSPECIFIED); + return preverify_ok; + } + + wpa_printf(MSG_DEBUG, "TLS: tls_verify_cb - preverify_ok=%d " + "err=%d (%s) ca_cert_verify=%d depth=%d buf='%s'", + preverify_ok, err, err_str, + conn->ca_cert_verify, depth, buf); + if (depth == 0 && match && os_strstr(buf, match) == NULL) { + wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not " + "match with '%s'", buf, match); + preverify_ok = 0; + openssl_tls_fail_event(conn, err_cert, err, depth, buf, + "Subject mismatch", + TLS_FAIL_SUBJECT_MISMATCH); + } 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; + openssl_tls_fail_event(conn, err_cert, err, depth, buf, + "AltSubject mismatch", + TLS_FAIL_ALTSUBJECT_MISMATCH); + } else + openssl_tls_cert_event(conn, err_cert, depth, buf); + + if (conn->cert_probe && preverify_ok && depth == 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: Reject server certificate " + "on probe-only run"); + preverify_ok = 0; + openssl_tls_fail_event(conn, err_cert, err, depth, buf, + "Server certificate chain probe", + TLS_FAIL_SERVER_CHAIN_PROBE); + } return preverify_ok; } @@ -1092,6 +1308,47 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, return -1; } + SSL_set_verify(conn->ssl, SSL_VERIFY_PEER, tls_verify_cb); + conn->ca_cert_verify = 1; + + if (ca_cert && os_strncmp(ca_cert, "probe://", 8) == 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: Probe for server certificate " + "chain"); + conn->cert_probe = 1; + conn->ca_cert_verify = 0; + return 0; + } + + if (ca_cert && os_strncmp(ca_cert, "hash://", 7) == 0) { +#ifdef CONFIG_SHA256 + const char *pos = ca_cert + 7; + if (os_strncmp(pos, "server/sha256/", 14) != 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: Unsupported ca_cert " + "hash value '%s'", ca_cert); + return -1; + } + pos += 14; + if (os_strlen(pos) != 32 * 2) { + wpa_printf(MSG_DEBUG, "OpenSSL: Unexpected SHA256 " + "hash length in ca_cert '%s'", ca_cert); + return -1; + } + if (hexstr2bin(pos, conn->srv_cert_hash, 32) < 0) { + wpa_printf(MSG_DEBUG, "OpenSSL: Invalid SHA256 hash " + "value in ca_cert '%s'", ca_cert); + return -1; + } + conn->server_cert_only = 1; + wpa_printf(MSG_DEBUG, "OpenSSL: Checking only server " + "certificate match"); + return 0; +#else /* CONFIG_SHA256 */ + wpa_printf(MSG_INFO, "No SHA256 included in the build - " + "cannot validate server certificate hash"); + return -1; +#endif /* CONFIG_SHA256 */ + } + if (ca_cert_blob) { X509 *cert = d2i_X509(NULL, (OPENSSL_d2i_TYPE) &ca_cert_blob, ca_cert_blob_len); @@ -1120,7 +1377,6 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, 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; } @@ -1129,7 +1385,6 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, 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 */ @@ -1152,7 +1407,6 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, "certificate(s) loaded"); tls_get_errors(ssl_ctx); } - 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__); @@ -1161,7 +1415,7 @@ static int tls_connection_ca_cert(void *_ssl_ctx, struct tls_connection *conn, } else { /* No ca_cert configured - do not try to verify server * certificate */ - SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL); + conn->ca_cert_verify = 0; } return 0; @@ -1246,10 +1500,12 @@ int tls_connection_set_verify(void *ssl_ctx, struct tls_connection *conn, return -1; if (verify_peer) { + conn->ca_cert_verify = 1; SSL_set_verify(conn->ssl, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT | SSL_VERIFY_CLIENT_ONCE, tls_verify_cb); } else { + conn->ca_cert_verify = 0; SSL_set_verify(conn->ssl, SSL_VERIFY_NONE, NULL); } @@ -1979,30 +2235,30 @@ int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, } -u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - size_t *out_len, u8 **appl_data, - size_t *appl_data_len) +static struct wpabuf * +openssl_handshake(struct tls_connection *conn, const struct wpabuf *in_data, + int server) { int res; - u8 *out_data; - - if (appl_data) - *appl_data = NULL; + struct wpabuf *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) { + BIO_write(conn->ssl_in, wpabuf_head(in_data), wpabuf_len(in_data)) + < 0) { 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 (server) + res = SSL_accept(conn->ssl); + else + res = SSL_connect(conn->ssl); if (res != 1) { int err = SSL_get_error(conn->ssl, res); if (err == SSL_ERROR_WANT_READ) @@ -2020,7 +2276,7 @@ u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, /* Get the TLS handshake data to be sent to the server */ res = BIO_ctrl_pending(conn->ssl_out); wpa_printf(MSG_DEBUG, "SSL: %d bytes pending from ssl_out", res); - out_data = os_malloc(res == 0 ? 1 : res); + out_data = wpabuf_alloc(res); if (out_data == NULL) { wpa_printf(MSG_DEBUG, "SSL: Failed to allocate memory for " "handshake output (%d bytes)", res); @@ -2028,10 +2284,10 @@ u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, 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); + res = res == 0 ? 0 : BIO_read(conn->ssl_out, wpabuf_mhead(out_data), + res); if (res < 0) { tls_show_errors(MSG_INFO, __func__, "Handshake failed - BIO_read"); @@ -2039,169 +2295,169 @@ u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, tls_show_errors(MSG_INFO, __func__, "BIO_reset failed"); } - *out_len = 0; + wpabuf_free(out_data); return NULL; } - *out_len = res; - - if (SSL_is_init_finished(conn->ssl) && appl_data) { - *appl_data = os_malloc(in_len); - if (*appl_data) { - res = SSL_read(conn->ssl, *appl_data, in_len); - if (res < 0) { - int err = SSL_get_error(conn->ssl, res); - if (err == SSL_ERROR_WANT_READ || - err == SSL_ERROR_WANT_WRITE) { - wpa_printf(MSG_DEBUG, - "SSL: No Application Data " - "included"); - } else { - tls_show_errors(MSG_INFO, __func__, - "Failed to read " - "possible " - "Application Data"); - } - os_free(*appl_data); - *appl_data = NULL; - } else { - *appl_data_len = res; - wpa_hexdump_key(MSG_MSGDUMP, "SSL: Application" - " Data in Finish message", - *appl_data, *appl_data_len); - } - } - } + wpabuf_put(out_data, res); return out_data; } -u8 * tls_connection_server_handshake(void *ssl_ctx, - struct tls_connection *conn, - const u8 *in_data, size_t in_len, - size_t *out_len) +static struct wpabuf * +openssl_get_appl_data(struct tls_connection *conn, size_t max_len) { + struct wpabuf *appl_data; int res; - u8 *out_data; - /* - * Give TLS handshake data from the client (if available) to OpenSSL - * for processing. - */ - if (in_data && - BIO_write(conn->ssl_in, in_data, in_len) < 0) { - tls_show_errors(MSG_INFO, __func__, - "Handshake failed - BIO_write"); + appl_data = wpabuf_alloc(max_len + 100); + if (appl_data == NULL) return NULL; - } - /* Initiate TLS handshake or continue the existing handshake */ - res = SSL_accept(conn->ssl); - if (res != 1) { + res = SSL_read(conn->ssl, wpabuf_mhead(appl_data), + wpabuf_size(appl_data)); + if (res < 0) { int err = SSL_get_error(conn->ssl, res); - if (err == SSL_ERROR_WANT_READ) - wpa_printf(MSG_DEBUG, "SSL: SSL_accept - want " - "more data"); - else if (err == SSL_ERROR_WANT_WRITE) - wpa_printf(MSG_DEBUG, "SSL: SSL_accept - want to " - "write"); - else { - tls_show_errors(MSG_INFO, __func__, "SSL_accept"); - return NULL; - } - } - - /* Get the TLS handshake data to be sent to the client */ - res = BIO_ctrl_pending(conn->ssl_out); - wpa_printf(MSG_DEBUG, "SSL: %d bytes pending from ssl_out", res); - out_data = os_malloc(res == 0 ? 1 : res); - if (out_data == NULL) { - wpa_printf(MSG_DEBUG, "SSL: Failed to allocate memory for " - "handshake output (%d bytes)", res); - if (BIO_reset(conn->ssl_out) < 0) { + if (err == SSL_ERROR_WANT_READ || + err == SSL_ERROR_WANT_WRITE) { + wpa_printf(MSG_DEBUG, "SSL: No Application Data " + "included"); + } else { tls_show_errors(MSG_INFO, __func__, - "BIO_reset failed"); + "Failed to read possible " + "Application Data"); } - *out_len = 0; + wpabuf_free(appl_data); return NULL; } - res = res == 0 ? 0 : BIO_read(conn->ssl_out, out_data, res); - if (res < 0) { - 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; + + wpabuf_put(appl_data, res); + wpa_hexdump_buf_key(MSG_MSGDUMP, "SSL: Application Data in Finished " + "message", appl_data); + + return appl_data; +} + + +static struct wpabuf * +openssl_connection_handshake(struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data, int server) +{ + struct wpabuf *out_data; + + if (appl_data) + *appl_data = NULL; + + out_data = openssl_handshake(conn, in_data, server); + if (out_data == NULL) return NULL; - } - *out_len = res; + + if (SSL_is_init_finished(conn->ssl) && appl_data && in_data) + *appl_data = openssl_get_appl_data(conn, wpabuf_len(in_data)); + return out_data; } -int tls_connection_encrypt(void *ssl_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len) +struct wpabuf * +tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data) +{ + return openssl_connection_handshake(conn, in_data, appl_data, 0); +} + + +struct wpabuf * tls_connection_server_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data) +{ + return openssl_connection_handshake(conn, in_data, appl_data, 1); +} + + +struct wpabuf * tls_connection_encrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data) { int res; + struct wpabuf *buf; if (conn == NULL) - return -1; + return NULL; /* 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; + return NULL; } - res = SSL_write(conn->ssl, in_data, in_len); + res = SSL_write(conn->ssl, wpabuf_head(in_data), wpabuf_len(in_data)); if (res < 0) { tls_show_errors(MSG_INFO, __func__, "Encryption failed - SSL_write"); - return res; + return NULL; } /* Read encrypted data to be sent to the server */ - res = BIO_read(conn->ssl_out, out_data, out_len); + buf = wpabuf_alloc(wpabuf_len(in_data) + 300); + if (buf == NULL) + return NULL; + res = BIO_read(conn->ssl_out, wpabuf_mhead(buf), wpabuf_size(buf)); if (res < 0) { tls_show_errors(MSG_INFO, __func__, "Encryption failed - BIO_read"); - return res; + wpabuf_free(buf); + return NULL; } + wpabuf_put(buf, res); - return res; + return buf; } -int tls_connection_decrypt(void *ssl_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len) +struct wpabuf * tls_connection_decrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data) { int res; + struct wpabuf *buf; /* Give encrypted data from TLS tunnel for OpenSSL to decrypt. */ - res = BIO_write(conn->ssl_in, in_data, in_len); + res = BIO_write(conn->ssl_in, wpabuf_head(in_data), + wpabuf_len(in_data)); if (res < 0) { tls_show_errors(MSG_INFO, __func__, "Decryption failed - BIO_write"); - return res; + return NULL; } if (BIO_reset(conn->ssl_out) < 0) { tls_show_errors(MSG_INFO, __func__, "BIO_reset failed"); - return res; + return NULL; } /* Read decrypted data for further processing */ - res = SSL_read(conn->ssl, out_data, out_len); + /* + * Even though we try to disable TLS compression, it is possible that + * this cannot be done with all TLS libraries. Add extra buffer space + * to handle the possibility of the decrypted data being longer than + * input data. + */ + buf = wpabuf_alloc((wpabuf_len(in_data) + 500) * 3); + if (buf == NULL) + return NULL; + res = SSL_read(conn->ssl, wpabuf_mhead(buf), wpabuf_size(buf)); if (res < 0) { tls_show_errors(MSG_INFO, __func__, "Decryption failed - SSL_read"); - return res; + wpabuf_free(buf); + return NULL; } + wpabuf_put(buf, res); - return res; + return buf; } @@ -2292,7 +2548,7 @@ int tls_connection_enable_workaround(void *ssl_ctx, } -#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) +#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) /* ClientHello TLS extensions require a patch to openssl, so this function is * commented out unless explicitly needed for EAP-FAST in order to be able to * build this file with unmodified openssl. */ @@ -2315,7 +2571,7 @@ int tls_connection_client_hello_ext(void *ssl_ctx, struct tls_connection *conn, return 0; } -#endif /* EAP_FAST || EAP_FAST_DYNAMIC */ +#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ int tls_connection_get_failed(void *ssl_ctx, struct tls_connection *conn) @@ -2482,12 +2738,10 @@ int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn, } -int tls_connection_ia_send_phase_finished(void *tls_ctx, - struct tls_connection *conn, - int final, - u8 *out_data, size_t out_len) +struct wpabuf * tls_connection_ia_send_phase_finished( + void *tls_ctx, struct tls_connection *conn, int final) { - return -1; + return NULL; } @@ -2506,7 +2760,7 @@ int tls_connection_ia_permute_inner_secret(void *tls_ctx, } -#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) +#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_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. */ @@ -2619,7 +2873,7 @@ static int tls_hello_ext_cb(SSL *s, TLS_EXTENSION *ext, void *arg) } #endif /* SSL_OP_NO_TICKET */ #endif /* CONFIG_OPENSSL_TICKET_OVERRIDE */ -#endif /* EAP_FAST || EAP_FAST_DYNAMIC */ +#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ int tls_connection_set_session_ticket_cb(void *tls_ctx, @@ -2627,7 +2881,7 @@ int tls_connection_set_session_ticket_cb(void *tls_ctx, tls_session_ticket_cb cb, void *ctx) { -#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) +#if defined(EAP_FAST) || defined(EAP_FAST_DYNAMIC) || defined(EAP_SERVER_FAST) conn->session_ticket_cb = cb; conn->session_ticket_cb_ctx = ctx; @@ -2665,7 +2919,7 @@ int tls_connection_set_session_ticket_cb(void *tls_ctx, } return 0; -#else /* EAP_FAST || EAP_FAST_DYNAMIC */ +#else /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ return -1; -#endif /* EAP_FAST || EAP_FAST_DYNAMIC */ +#endif /* EAP_FAST || EAP_FAST_DYNAMIC || EAP_SERVER_FAST */ } diff --git a/contrib/wpa/src/crypto/tls_schannel.c b/contrib/wpa/src/crypto/tls_schannel.c index 87e7435..4a94e99 100644 --- a/contrib/wpa/src/crypto/tls_schannel.c +++ b/contrib/wpa/src/crypto/tls_schannel.c @@ -1,6 +1,6 @@ /* - * WPA Supplicant / SSL/TLS interface functions for Microsoft Schannel - * Copyright (c) 2005, Jouni Malinen <j@w1.fi> + * SSL/TLS interface functions for Microsoft Schannel + * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -215,9 +215,8 @@ int tls_connection_prf(void *tls_ctx, struct tls_connection *conn, } -static u8 * tls_conn_hs_clienthello(struct tls_global *global, - struct tls_connection *conn, - size_t *out_len) +static struct wpabuf * tls_conn_hs_clienthello(struct tls_global *global, + struct tls_connection *conn) { DWORD sspi_flags, sspi_flags_out; SecBufferDesc outbuf; @@ -260,15 +259,14 @@ static u8 * tls_conn_hs_clienthello(struct tls_global *global, } if (outbufs[0].cbBuffer != 0 && outbufs[0].pvBuffer) { - u8 *buf; + struct wpabuf *buf; wpa_hexdump(MSG_MSGDUMP, "SChannel - ClientHello", outbufs[0].pvBuffer, outbufs[0].cbBuffer); conn->start = 0; - *out_len = outbufs[0].cbBuffer; - buf = os_malloc(*out_len); + buf = wpabuf_alloc_copy(outbufs[0].pvBuffer, + outbufs[0].cbBuffer); if (buf == NULL) return NULL; - os_memcpy(buf, outbufs[0].pvBuffer, *out_len); global->sspi->FreeContextBuffer(outbufs[0].pvBuffer); return buf; } @@ -316,28 +314,27 @@ static int tls_get_eap(struct tls_global *global, struct tls_connection *conn) } -u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - size_t *out_len, u8 **appl_data, - size_t *appl_data_len) +struct wpabuf * tls_connection_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data) { - struct tls_global *global = ssl_ctx; + struct tls_global *global = tls_ctx; DWORD sspi_flags, sspi_flags_out; SecBufferDesc inbuf, outbuf; SecBuffer inbufs[2], outbufs[1]; SECURITY_STATUS status; TimeStamp ts_expiry; - u8 *out_buf = NULL; + struct wpabuf *out_buf = NULL; if (appl_data) *appl_data = NULL; - if (conn->start) { - return tls_conn_hs_clienthello(global, conn, out_len); - } + if (conn->start) + return tls_conn_hs_clienthello(global, conn); wpa_printf(MSG_DEBUG, "SChannel: %d bytes handshake data to process", - in_len); + (int) wpabuf_len(in_data)); sspi_flags = ISC_REQ_REPLAY_DETECT | ISC_REQ_CONFIDENTIALITY | @@ -346,8 +343,8 @@ u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, ISC_REQ_MANUAL_CRED_VALIDATION; /* Input buffer for Schannel */ - inbufs[0].pvBuffer = (u8 *) in_data; - inbufs[0].cbBuffer = in_len; + inbufs[0].pvBuffer = (u8 *) wpabuf_head(in_data); + inbufs[0].cbBuffer = wpabuf_len(in_data); inbufs[0].BufferType = SECBUFFER_TOKEN; /* Place for leftover data from Schannel */ @@ -392,11 +389,8 @@ u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, if (outbufs[0].cbBuffer != 0 && outbufs[0].pvBuffer) { wpa_hexdump(MSG_MSGDUMP, "SChannel - output", outbufs[0].pvBuffer, outbufs[0].cbBuffer); - *out_len = outbufs[0].cbBuffer; - out_buf = os_malloc(*out_len); - if (out_buf) - os_memcpy(out_buf, outbufs[0].pvBuffer, - *out_len); + out_buf = wpabuf_alloc_copy(outbufs[0].pvBuffer, + outbufs[0].cbBuffer); global->sspi->FreeContextBuffer(outbufs[0].pvBuffer); outbufs[0].pvBuffer = NULL; if (out_buf == NULL) @@ -420,19 +414,16 @@ u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, /* Need to return something to get final TLS ACK. */ if (out_buf == NULL) - out_buf = os_malloc(1); + out_buf = wpabuf_alloc(0); if (inbufs[1].BufferType == SECBUFFER_EXTRA) { wpa_hexdump(MSG_MSGDUMP, "SChannel - Encrypted " "application data", inbufs[1].pvBuffer, inbufs[1].cbBuffer); if (appl_data) { - *appl_data_len = outbufs[1].cbBuffer; - appl_data = os_malloc(*appl_data_len); - if (appl_data) - os_memcpy(appl_data, - outbufs[1].pvBuffer, - *appl_data_len); + *appl_data = wpabuf_alloc_copy( + outbufs[1].pvBuffer, + outbufs[1].cbBuffer); } global->sspi->FreeContextBuffer(inbufs[1].pvBuffer); inbufs[1].pvBuffer = NULL; @@ -470,26 +461,26 @@ u8 * tls_connection_handshake(void *ssl_ctx, struct tls_connection *conn, } -u8 * tls_connection_server_handshake(void *ssl_ctx, - struct tls_connection *conn, - const u8 *in_data, size_t in_len, - size_t *out_len) +struct wpabuf * tls_connection_server_handshake(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data, + struct wpabuf **appl_data) { return NULL; } -int tls_connection_encrypt(void *ssl_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len) +struct wpabuf * tls_connection_encrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data) { - struct tls_global *global = ssl_ctx; + struct tls_global *global = tls_ctx; SECURITY_STATUS status; SecBufferDesc buf; SecBuffer bufs[4]; SecPkgContext_StreamSizes sizes; int i; - size_t total_len; + struct wpabuf *out; status = global->sspi->QueryContextAttributes(&conn->context, SECPKG_ATTR_STREAM_SIZES, @@ -497,34 +488,27 @@ int tls_connection_encrypt(void *ssl_ctx, struct tls_connection *conn, if (status != SEC_E_OK) { wpa_printf(MSG_DEBUG, "%s: QueryContextAttributes failed", __func__); - return -1; + return NULL; } wpa_printf(MSG_DEBUG, "%s: Stream sizes: header=%u trailer=%u", __func__, (unsigned int) sizes.cbHeader, (unsigned int) sizes.cbTrailer); - total_len = sizes.cbHeader + in_len + sizes.cbTrailer; - - if (out_len < total_len) { - wpa_printf(MSG_DEBUG, "%s: too short out_data (out_len=%lu " - "in_len=%lu total_len=%lu)", __func__, - (unsigned long) out_len, (unsigned long) in_len, - (unsigned long) total_len); - return -1; - } + out = wpabuf_alloc(sizes.cbHeader + wpabuf_len(in_data) + + sizes.cbTrailer); os_memset(&bufs, 0, sizeof(bufs)); - bufs[0].pvBuffer = out_data; + bufs[0].pvBuffer = wpabuf_put(out, sizes.cbHeader); bufs[0].cbBuffer = sizes.cbHeader; bufs[0].BufferType = SECBUFFER_STREAM_HEADER; - os_memcpy(out_data + sizes.cbHeader, in_data, in_len); - bufs[1].pvBuffer = out_data + sizes.cbHeader; - bufs[1].cbBuffer = in_len; + bufs[1].pvBuffer = wpabuf_put(out, 0); + wpabuf_put_buf(out, in_data); + bufs[1].cbBuffer = wpabuf_len(in_data); bufs[1].BufferType = SECBUFFER_DATA; - bufs[2].pvBuffer = out_data + sizes.cbHeader + in_len; + bufs[2].pvBuffer = wpabuf_put(out, sizes.cbTrailer); bufs[2].cbBuffer = sizes.cbTrailer; bufs[2].BufferType = SECBUFFER_STREAM_TRAILER; @@ -543,7 +527,7 @@ int tls_connection_encrypt(void *ssl_ctx, struct tls_connection *conn, (int) bufs[2].cbBuffer, (int) bufs[2].BufferType); wpa_printf(MSG_MSGDUMP, "Schannel: EncryptMessage pointers: " "out_data=%p bufs %p %p %p", - out_data, bufs[0].pvBuffer, bufs[1].pvBuffer, + wpabuf_head(out), bufs[0].pvBuffer, bufs[1].pvBuffer, bufs[2].pvBuffer); for (i = 0; i < 3; i++) { @@ -556,39 +540,37 @@ int tls_connection_encrypt(void *ssl_ctx, struct tls_connection *conn, if (status == SEC_E_OK) { wpa_printf(MSG_DEBUG, "%s: SEC_E_OK", __func__); - wpa_hexdump_key(MSG_MSGDUMP, "Schannel: Encrypted data from " - "EncryptMessage", out_data, total_len); - return total_len; + wpa_hexdump_buf_key(MSG_MSGDUMP, "Schannel: Encrypted data " + "from EncryptMessage", out); + return out; } wpa_printf(MSG_DEBUG, "%s: Failed - status=%d", __func__, (int) status); - return -1; + wpabuf_free(out); + return NULL; } -int tls_connection_decrypt(void *ssl_ctx, struct tls_connection *conn, - const u8 *in_data, size_t in_len, - u8 *out_data, size_t out_len) +struct wpabuf * tls_connection_decrypt(void *tls_ctx, + struct tls_connection *conn, + const struct wpabuf *in_data) { - struct tls_global *global = ssl_ctx; + struct tls_global *global = tls_ctx; SECURITY_STATUS status; SecBufferDesc buf; SecBuffer bufs[4]; int i; + struct wpabuf *out, *tmp; - if (out_len < in_len) { - wpa_printf(MSG_DEBUG, "%s: out_len=%lu < in_len=%lu", __func__, - (unsigned long) out_len, (unsigned long) in_len); - return -1; - } - - wpa_hexdump(MSG_MSGDUMP, "Schannel: Encrypted data to DecryptMessage", - in_data, in_len); + wpa_hexdump_buf(MSG_MSGDUMP, + "Schannel: Encrypted data to DecryptMessage", in_data); os_memset(&bufs, 0, sizeof(bufs)); - os_memcpy(out_data, in_data, in_len); - bufs[0].pvBuffer = out_data; - bufs[0].cbBuffer = in_len; + tmp = wpabuf_dup(in_data); + if (tmp == NULL) + return NULL; + bufs[0].pvBuffer = wpabuf_mhead(tmp); + bufs[0].cbBuffer = wpabuf_len(in_data); bufs[0].BufferType = SECBUFFER_DATA; bufs[1].BufferType = SECBUFFER_EMPTY; @@ -611,7 +593,7 @@ int tls_connection_decrypt(void *ssl_ctx, struct tls_connection *conn, (int) bufs[3].cbBuffer, (int) bufs[3].BufferType); wpa_printf(MSG_MSGDUMP, "Schannel: DecryptMessage pointers: " "out_data=%p bufs %p %p %p %p", - out_data, bufs[0].pvBuffer, bufs[1].pvBuffer, + wpabuf_head(tmp), bufs[0].pvBuffer, bufs[1].pvBuffer, bufs[2].pvBuffer, bufs[3].pvBuffer); switch (status) { @@ -628,23 +610,21 @@ int tls_connection_decrypt(void *ssl_ctx, struct tls_connection *conn, if (i == 4) { wpa_printf(MSG_DEBUG, "%s: No output data from " "DecryptMessage", __func__); - return -1; + wpabuf_free(tmp); + return NULL; } wpa_hexdump_key(MSG_MSGDUMP, "Schannel: Decrypted data from " "DecryptMessage", bufs[i].pvBuffer, bufs[i].cbBuffer); - if (bufs[i].cbBuffer > out_len) { - wpa_printf(MSG_DEBUG, "%s: Too long output data", - __func__); - return -1; - } - os_memmove(out_data, bufs[i].pvBuffer, bufs[i].cbBuffer); - return bufs[i].cbBuffer; + out = wpabuf_alloc_copy(bufs[i].pvBuffer, bufs[i].cbBuffer); + wpabuf_free(tmp); + return out; } wpa_printf(MSG_DEBUG, "%s: Failed - status=%d", __func__, (int) status); - return -1; + wpabuf_free(tmp); + return NULL; } @@ -765,12 +745,10 @@ int tls_connection_set_ia(void *tls_ctx, struct tls_connection *conn, } -int tls_connection_ia_send_phase_finished(void *tls_ctx, - struct tls_connection *conn, - int final, - u8 *out_data, size_t out_len) +struct wpabuf * tls_connection_ia_send_phase_finished( + void *tls_ctx, struct tls_connection *conn, int final); { - return -1; + return NULL; } diff --git a/contrib/wpa/src/drivers/.gitignore b/contrib/wpa/src/drivers/.gitignore new file mode 100644 index 0000000..1d9e0e6 --- /dev/null +++ b/contrib/wpa/src/drivers/.gitignore @@ -0,0 +1,2 @@ +build.wpa_supplicant +build.hostapd diff --git a/contrib/wpa/src/drivers/Makefile b/contrib/wpa/src/drivers/Makefile index cffba62..07600e5 100644 --- a/contrib/wpa/src/drivers/Makefile +++ b/contrib/wpa/src/drivers/Makefile @@ -2,8 +2,8 @@ all: @echo Nothing to be made. clean: - for d in $(SUBDIRS); do make -C $$d clean; done rm -f *~ *.o *.d + rm -f build.wpa_supplicant build.hostapd install: @echo Nothing to be made. diff --git a/contrib/wpa/src/drivers/driver.h b/contrib/wpa/src/drivers/driver.h index c2975d2..fa49da4 100644 --- a/contrib/wpa/src/drivers/driver.h +++ b/contrib/wpa/src/drivers/driver.h @@ -1,6 +1,6 @@ /* - * WPA Supplicant - driver interface definition - * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> + * Driver interface definition + * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -10,79 +10,117 @@ * license. * * See README and COPYING for more details. + * + * This file defines a driver interface used by both %wpa_supplicant and + * hostapd. The first part of the file defines data structures used in various + * driver operations. This is followed by the struct wpa_driver_ops that each + * driver wrapper will beed to define with callback functions for requesting + * driver operations. After this, there are definitions for driver event + * reporting with wpa_supplicant_event() and some convenience helper functions + * that can be used to report events. */ #ifndef DRIVER_H #define DRIVER_H -#define WPA_SUPPLICANT_DRIVER_VERSION 3 +#define WPA_SUPPLICANT_DRIVER_VERSION 4 + +#include "common/defs.h" + +#define HOSTAPD_CHAN_DISABLED 0x00000001 +#define HOSTAPD_CHAN_PASSIVE_SCAN 0x00000002 +#define HOSTAPD_CHAN_NO_IBSS 0x00000004 +#define HOSTAPD_CHAN_RADAR 0x00000008 + +/** + * struct hostapd_channel_data - Channel information + */ +struct hostapd_channel_data { + /** + * chan - Channel number (IEEE 802.11) + */ + short chan; + + /** + * freq - Frequency in MHz + */ + short freq; + + /** + * flag - Channel flags (HOSTAPD_CHAN_*) + */ + int flag; + + /** + * max_tx_power - maximum transmit power in dBm + */ + u8 max_tx_power; +}; + +/** + * struct hostapd_hw_modes - Supported hardware mode information + */ +struct hostapd_hw_modes { + /** + * mode - Hardware mode + */ + enum hostapd_hw_mode mode; -#include "defs.h" + /** + * num_channels - Number of entries in the channels array + */ + int num_channels; + + /** + * channels - Array of supported channels + */ + struct hostapd_channel_data *channels; + + /** + * num_rates - Number of entries in the rates array + */ + int num_rates; + + /** + * rates - Array of supported rates in 100 kbps units + */ + int *rates; + + /** + * ht_capab - HT (IEEE 802.11n) capabilities + */ + u16 ht_capab; + + /** + * mcs_set - MCS (IEEE 802.11n) rate parameters + */ + u8 mcs_set[16]; + + /** + * a_mpdu_params - A-MPDU (IEEE 802.11n) parameters + */ + u8 a_mpdu_params; +}; -#define AUTH_ALG_OPEN_SYSTEM 0x01 -#define AUTH_ALG_SHARED_KEY 0x02 -#define AUTH_ALG_LEAP 0x04 #define IEEE80211_MODE_INFRA 0 #define IEEE80211_MODE_IBSS 1 +#define IEEE80211_MODE_AP 2 #define IEEE80211_CAP_ESS 0x0001 #define IEEE80211_CAP_IBSS 0x0002 #define IEEE80211_CAP_PRIVACY 0x0010 -#define SSID_MAX_WPA_IE_LEN 40 -/** - * struct wpa_scan_result - Scan results (old structure) - * @bssid: BSSID - * @ssid: SSID - * @ssid_len: length of the ssid - * @wpa_ie: WPA IE - * @wpa_ie_len: length of the wpa_ie - * @rsn_ie: RSN IE - * @rsn_ie_len: length of the RSN IE - * @freq: frequency of the channel in MHz (e.g., 2412 = channel 1) - * @caps: capability information field in host byte order - * @qual: signal quality - * @noise: noise level - * @level: signal level - * @maxrate: maximum supported rate - * @mdie_present: Whether MDIE was included in Beacon/ProbeRsp frame - * @mdie: Mobility domain identifier IE (IEEE 802.11r MDIE) (starting from - * IE type field) - * @tsf: Timestamp - * - * This structure is used as a generic format for scan results from the - * driver. Each driver interface implementation is responsible for converting - * the driver or OS specific scan results into this format. - * - * This structure is the old data structure used for scan results. It is - * obsoleted by the new struct wpa_scan_res structure and the old version is - * only included for backwards compatibility with existing driver wrapper - * implementations. New implementations are encouraged to implement for struct - * wpa_scan_res. The old structure will be removed at some point. - */ -struct wpa_scan_result { - u8 bssid[ETH_ALEN]; - u8 ssid[32]; - size_t ssid_len; - u8 wpa_ie[SSID_MAX_WPA_IE_LEN]; - size_t wpa_ie_len; - u8 rsn_ie[SSID_MAX_WPA_IE_LEN]; - size_t rsn_ie_len; - int freq; - u16 caps; - int qual; - int noise; - int level; - int maxrate; - int mdie_present; - u8 mdie[5]; - u64 tsf; -}; - +#define WPA_SCAN_QUAL_INVALID BIT(0) +#define WPA_SCAN_NOISE_INVALID BIT(1) +#define WPA_SCAN_LEVEL_INVALID BIT(2) +#define WPA_SCAN_LEVEL_DBM BIT(3) +#define WPA_SCAN_AUTHENTICATED BIT(4) +#define WPA_SCAN_ASSOCIATED BIT(5) /** * struct wpa_scan_res - Scan result for an BSS/IBSS + * @flags: information flags about the BSS/IBSS (WPA_SCAN_*) * @bssid: BSSID * @freq: frequency of the channel in MHz (e.g., 2412 = channel 1) * @beacon_int: beacon interval in TUs (host byte order) @@ -91,7 +129,10 @@ struct wpa_scan_result { * @noise: noise level * @level: signal level * @tsf: Timestamp + * @age: Age of the information in milliseconds (i.e., how many milliseconds + * ago the last Beacon or Probe Response frame was received) * @ie_len: length of the following IE field in octets + * @beacon_ie_len: length of the following Beacon IE field in octets * * This structure is used as a generic format for scan results from the * driver. Each driver interface implementation is responsible for converting @@ -103,6 +144,7 @@ struct wpa_scan_result { * report all IEs to make it easier to support future additions. */ struct wpa_scan_res { + unsigned int flags; u8 bssid[ETH_ALEN]; int freq; u16 beacon_int; @@ -111,8 +153,16 @@ struct wpa_scan_res { int noise; int level; u64 tsf; + unsigned int age; size_t ie_len; - /* followed by ie_len octets of IEs */ + size_t beacon_ie_len; + /* + * Followed by ie_len octets of IEs from Probe Response frame (or if + * the driver does not indicate source of IEs, these may also be from + * Beacon frame). After the first set of IEs, another set of IEs may + * follow (with beacon_ie_len octets of data) if the driver provides + * both IE sets. + */ }; /** @@ -142,6 +192,95 @@ struct wpa_interface_info { const char *drv_name; }; +#define WPAS_MAX_SCAN_SSIDS 4 + +/** + * struct wpa_driver_scan_params - Scan parameters + * Data for struct wpa_driver_ops::scan2(). + */ +struct wpa_driver_scan_params { + /** + * ssids - SSIDs to scan for + */ + struct wpa_driver_scan_ssid { + /** + * ssid - specific SSID to scan for (ProbeReq) + * %NULL or zero-length SSID is used to indicate active scan + * with wildcard SSID. + */ + const u8 *ssid; + /** + * ssid_len: Length of the SSID in octets + */ + size_t ssid_len; + } ssids[WPAS_MAX_SCAN_SSIDS]; + + /** + * num_ssids - Number of entries in ssids array + * Zero indicates a request for a passive scan. + */ + size_t num_ssids; + + /** + * extra_ies - Extra IE(s) to add into Probe Request or %NULL + */ + const u8 *extra_ies; + + /** + * extra_ies_len - Length of extra_ies in octets + */ + size_t extra_ies_len; + + /** + * freqs - Array of frequencies to scan or %NULL for all frequencies + * + * The frequency is set in MHz. The array is zero-terminated. + */ + int *freqs; + + /** + * filter_ssids - Filter for reporting SSIDs + * + * This optional parameter can be used to request the driver wrapper to + * filter scan results to include only the specified SSIDs. %NULL + * indicates that no filtering is to be done. This can be used to + * reduce memory needs for scan results in environments that have large + * number of APs with different SSIDs. + * + * The driver wrapper is allowed to take this allocated buffer into its + * own use by setting the pointer to %NULL. In that case, the driver + * wrapper is responsible for freeing the buffer with os_free() once it + * is not needed anymore. + */ + struct wpa_driver_scan_filter { + u8 ssid[32]; + size_t ssid_len; + } *filter_ssids; + + /** + * num_filter_ssids - Number of entries in filter_ssids array + */ + size_t num_filter_ssids; +}; + +/** + * struct wpa_driver_auth_params - Authentication parameters + * Data for struct wpa_driver_ops::authenticate(). + */ +struct wpa_driver_auth_params { + int freq; + const u8 *bssid; + const u8 *ssid; + size_t ssid_len; + int auth_alg; + const u8 *ie; + size_t ie_len; + const u8 *wep_key[4]; + size_t wep_key_len[4]; + int wep_tx_keyidx; + int local_state_change; +}; + /** * struct wpa_driver_associate_params - Association parameters * Data for struct wpa_driver_ops::associate(). @@ -157,6 +296,10 @@ struct wpa_driver_associate_params { * ssid - The selected SSID */ const u8 *ssid; + + /** + * ssid_len - Length of the SSID (1..32) + */ size_t ssid_len; /** @@ -185,20 +328,36 @@ struct wpa_driver_associate_params { * When using WPS, wpa_ie is used for WPS IE instead of WPA/RSN IE. */ const u8 *wpa_ie; + /** * wpa_ie_len - length of the wpa_ie */ size_t wpa_ie_len; - /* The selected pairwise/group cipher and key management - * suites. These are usually ignored if @wpa_ie is used. */ - wpa_cipher pairwise_suite; - wpa_cipher group_suite; - wpa_key_mgmt key_mgmt_suite; + /** + * pairwise_suite - Selected pairwise cipher suite + * + * This is usually ignored if @wpa_ie is used. + */ + enum wpa_cipher pairwise_suite; + + /** + * group_suite - Selected group cipher suite + * + * This is usually ignored if @wpa_ie is used. + */ + enum wpa_cipher group_suite; + + /** + * key_mgmt_suite - Selected key management suite + * + * This is usually ignored if @wpa_ie is used. + */ + enum wpa_key_mgmt key_mgmt_suite; /** * auth_alg - Allowed authentication algorithms - * Bit field of AUTH_ALG_* + * Bit field of WPA_AUTH_ALG_* */ int auth_alg; @@ -225,11 +384,7 @@ struct wpa_driver_associate_params { /** * mgmt_frame_protection - IEEE 802.11w management frame protection */ - enum { - NO_MGMT_FRAME_PROTECTION, - MGMT_FRAME_PROTECTION_OPTIONAL, - MGMT_FRAME_PROTECTION_REQUIRED - } mgmt_frame_protection; + enum mfp_options mgmt_frame_protection; /** * ft_ies - IEEE 802.11r / FT information elements @@ -288,6 +443,23 @@ struct wpa_driver_associate_params { * be prepared to handle %NULL value as an error. */ const u8 *psk; + + /** + * drop_unencrypted - Enable/disable unencrypted frame filtering + * + * Configure the driver to drop all non-EAPOL frames (both receive and + * transmit paths). Unencrypted EAPOL frames (ethertype 0x888e) must + * still be allowed for key negotiation. + */ + int drop_unencrypted; + + /** + * prev_bssid - Previously used BSSID in this ESS + * + * When not %NULL, this is a request to use reassociation instead of + * association. + */ + const u8 *prev_bssid; }; /** @@ -316,59 +488,119 @@ struct wpa_driver_capa { /* Driver generated WPA/RSN IE */ #define WPA_DRIVER_FLAGS_DRIVER_IE 0x00000001 +/* Driver needs static WEP key setup after association command */ #define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC 0x00000002 #define WPA_DRIVER_FLAGS_USER_SPACE_MLME 0x00000004 /* Driver takes care of RSN 4-way handshake internally; PMK is configured with * struct wpa_driver_ops::set_key using alg = WPA_ALG_PMK */ #define WPA_DRIVER_FLAGS_4WAY_HANDSHAKE 0x00000008 +#define WPA_DRIVER_FLAGS_WIRED 0x00000010 +/* Driver provides separate commands for authentication and association (SME in + * wpa_supplicant). */ +#define WPA_DRIVER_FLAGS_SME 0x00000020 +/* Driver supports AP mode */ +#define WPA_DRIVER_FLAGS_AP 0x00000040 +/* Driver needs static WEP key setup after association has been completed */ +#define WPA_DRIVER_FLAGS_SET_KEYS_AFTER_ASSOC_DONE 0x00000080 unsigned int flags; + + int max_scan_ssids; + + /** + * max_remain_on_chan - Maximum remain-on-channel duration in msec + */ + unsigned int max_remain_on_chan; }; -#define WPA_CHAN_W_SCAN 0x00000001 -#define WPA_CHAN_W_ACTIVE_SCAN 0x00000002 -#define WPA_CHAN_W_IBSS 0x00000004 +struct hostapd_data; -struct wpa_channel_data { - short chan; /* channel number (IEEE 802.11) */ - short freq; /* frequency in MHz */ - int flag; /* flag for user space use (WPA_CHAN_*) */ +struct hostap_sta_driver_data { + unsigned long rx_packets, tx_packets, rx_bytes, tx_bytes; + unsigned long current_tx_rate; + unsigned long inactive_msec; + unsigned long flags; + unsigned long num_ps_buf_frames; + unsigned long tx_retry_failed; + unsigned long tx_retry_count; + int last_rssi; + int last_ack_rssi; }; -#define WPA_RATE_ERP 0x00000001 -#define WPA_RATE_BASIC 0x00000002 -#define WPA_RATE_PREAMBLE2 0x00000004 -#define WPA_RATE_SUPPORTED 0x00000010 -#define WPA_RATE_OFDM 0x00000020 -#define WPA_RATE_CCK 0x00000040 -#define WPA_RATE_MANDATORY 0x00000100 - -struct wpa_rate_data { - int rate; /* rate in 100 kbps */ - int flags; /* WPA_RATE_ flags */ +struct hostapd_sta_add_params { + const u8 *addr; + u16 aid; + u16 capability; + const u8 *supp_rates; + size_t supp_rates_len; + u16 listen_interval; + const struct ieee80211_ht_capabilities *ht_capabilities; }; -typedef enum { - WPA_MODE_IEEE80211B, - WPA_MODE_IEEE80211G, - WPA_MODE_IEEE80211A, - NUM_WPA_MODES -} wpa_hw_mode; +struct hostapd_freq_params { + int mode; + int freq; + int channel; + int ht_enabled; + int sec_channel_offset; /* 0 = HT40 disabled, -1 = HT40 enabled, + * secondary channel below primary, 1 = HT40 + * enabled, secondary channel above primary */ +}; -struct wpa_hw_modes { - wpa_hw_mode mode; - int num_channels; - struct wpa_channel_data *channels; - int num_rates; - struct wpa_rate_data *rates; +enum wpa_driver_if_type { + /** + * WPA_IF_STATION - Station mode interface + */ + WPA_IF_STATION, + + /** + * WPA_IF_AP_VLAN - AP mode VLAN interface + * + * This interface shares its address and Beacon frame with the main + * BSS. + */ + WPA_IF_AP_VLAN, + + /** + * WPA_IF_AP_BSS - AP mode BSS interface + * + * This interface has its own address and Beacon frame. + */ + WPA_IF_AP_BSS, +}; + +struct wpa_init_params { + const u8 *bssid; + const char *ifname; + const u8 *ssid; + size_t ssid_len; + const char *test_socket; + int use_pae_group_addr; + char **bridge; + size_t num_bridge; + + u8 *own_addr; /* buffer for writing own MAC address */ }; -struct ieee80211_rx_status { - int channel; - int ssi; +struct wpa_bss_params { + /** Interface name (for multi-SSID/VLAN support) */ + const char *ifname; + /** Whether IEEE 802.1X or WPA/WPA2 is enabled */ + int enabled; + + int wpa; + int ieee802_1x; + int wpa_group; + int wpa_pairwise; + int wpa_key_mgmt; + int rsn_preauth; }; +#define WPA_STA_AUTHORIZED BIT(0) +#define WPA_STA_WMM BIT(1) +#define WPA_STA_SHORT_PREAMBLE BIT(2) +#define WPA_STA_MFP BIT(3) /** * struct wpa_driver_ops - Driver interface API definition @@ -414,31 +646,8 @@ struct wpa_driver_ops { int (*get_ssid)(void *priv, u8 *ssid); /** - * set_wpa - Enable/disable WPA support (OBSOLETE) - * @priv: private driver interface data - * @enabled: 1 = enable, 0 = disable - * - * Returns: 0 on success, -1 on failure - * - * Note: This function is included for backwards compatibility. This is - * called only just after init and just before deinit, so these - * functions can be used to implement same functionality and the driver - * interface need not define this function. - * - * Configure the kernel driver to enable/disable WPA support. This may - * be empty function, if WPA support is always enabled. Common - * configuration items are WPA IE (clearing it when WPA support is - * disabled), Privacy flag configuration for capability field (note: - * this the value need to set in associate handler to allow plaintext - * mode to be used) when trying to associate with, roaming mode (can - * allow wpa_supplicant to control roaming if ap_scan=1 is used; - * however, drivers can also implement roaming if desired, especially - * ap_scan=2 mode is used for this). - */ - int (*set_wpa)(void *priv, int enabled); - - /** * set_key - Configure encryption key + * @ifname: Interface name (for multi-SSID/VLAN support) * @priv: private driver interface data * @alg: encryption algorithm (%WPA_ALG_NONE, %WPA_ALG_WEP, * %WPA_ALG_TKIP, %WPA_ALG_CCMP, %WPA_ALG_IGTK, %WPA_ALG_PMK); @@ -480,8 +689,9 @@ struct wpa_driver_ops { * in driver_*.c set_key() implementation, see driver_ndis.c for an * example on how this can be done. */ - int (*set_key)(void *priv, wpa_alg alg, const u8 *addr, - int key_idx, int set_tx, const u8 *seq, size_t seq_len, + int (*set_key)(const char *ifname, void *priv, enum wpa_alg alg, + const u8 *addr, int key_idx, int set_tx, + const u8 *seq, size_t seq_len, const u8 *key, size_t key_len); /** @@ -543,55 +753,6 @@ struct wpa_driver_ops { int (*set_countermeasures)(void *priv, int enabled); /** - * set_drop_unencrypted - Enable/disable unencrypted frame filtering - * @priv: private driver interface data - * @enabled: 1 = unencrypted Tx/Rx frames will be dropped, 0 = disabled - * - * Returns: 0 on success, -1 on failure - * - * Configure the driver to drop all non-EAPOL frames (both receive and - * transmit paths). Unencrypted EAPOL frames (ethertype 0x888e) must - * still be allowed for key negotiation. - */ - int (*set_drop_unencrypted)(void *priv, int enabled); - - /** - * scan - Request the driver to initiate scan - * @priv: private driver interface data - * @ssid: specific SSID to scan for (ProbeReq) or %NULL to scan for - * all SSIDs (either active scan with broadcast SSID or passive - * scan - * @ssid_len: length of the SSID - * - * Returns: 0 on success, -1 on failure - * - * Once the scan results are ready, the driver should report scan - * results event for wpa_supplicant which will eventually request the - * results with wpa_driver_get_scan_results(). - */ - int (*scan)(void *priv, const u8 *ssid, size_t ssid_len); - - /** - * get_scan_results - Fetch the latest scan results (old version) - * @priv: private driver interface data - * @results: pointer to buffer for scan results - * @max_size: maximum number of entries (buffer size) - * - * Returns: Number of scan result entries used on success, -1 on - * failure - * - * If scan results include more than max_size BSSes, max_size will be - * returned and the remaining entries will not be included in the - * buffer. - * - * This function is depracated. New driver wrapper implementations - * should implement support for get_scan_results2(). - */ - int (*get_scan_results)(void *priv, - struct wpa_scan_result *results, - size_t max_size); - - /** * deauthenticate - Request driver to deauthenticate * @priv: private driver interface data * @addr: peer address (BSSID of the AP) @@ -624,27 +785,6 @@ struct wpa_driver_ops { struct wpa_driver_associate_params *params); /** - * set_auth_alg - Set IEEE 802.11 authentication algorithm - * @priv: private driver interface data - * @auth_alg: bit field of AUTH_ALG_* - * - * If the driver supports more than one authentication algorithm at the - * same time, it should configure all supported algorithms. If not, one - * algorithm needs to be selected arbitrarily. Open System - * authentication should be ok for most cases and it is recommended to - * be used if other options are not supported. Static WEP configuration - * may also use Shared Key authentication and LEAP requires its own - * algorithm number. For LEAP, user can make sure that only one - * algorithm is used at a time by configuring LEAP as the only - * supported EAP method. This information is also available in - * associate() params, so set_auth_alg may not be needed in case of - * most drivers. - * - * Returns: 0 on success, -1 on failure - */ - int (*set_auth_alg)(void *priv, int auth_alg); - - /** * add_pmkid - Add PMKSA cache entry to the driver * @priv: private driver interface data * @bssid: BSSID for the PMKSA cache entry @@ -764,9 +904,9 @@ struct wpa_driver_ops { * with driver specific functionality. If this function pointer is set, * l2_packet module is not used at all and the driver interface code is * responsible for receiving and sending all EAPOL packets. The - * received EAPOL packets are sent to core code by calling - * wpa_supplicant_rx_eapol(). The driver interface is required to - * implement get_mac_addr() handler if send_eapol() is used. + * received EAPOL packets are sent to core code with EVENT_EAPOL_RX + * event. The driver interface is required to implement get_mac_addr() + * handler if send_eapol() is used. */ int (*send_eapol)(void *priv, const u8 *dest, u16 proto, const u8 *data, size_t data_len); @@ -813,16 +953,16 @@ struct wpa_driver_ops { * failure. Caller is responsible for freeing this. * * This function is only needed for drivers that export MLME - * (management frame processing) to wpa_supplicant. + * (management frame processing) to %wpa_supplicant or hostapd. */ - struct wpa_hw_modes * (*get_hw_feature_data)(void *priv, - u16 *num_modes, - u16 *flags); + struct hostapd_hw_modes * (*get_hw_feature_data)(void *priv, + u16 *num_modes, + u16 *flags); /** * set_channel - Set channel * @priv: Private driver interface data - * @phymode: WPA_MODE_IEEE80211B, .. + * @phymode: HOSTAPD_MODE_IEEE80211B, .. * @chan: IEEE 802.11 channel number * @freq: Frequency of the channel in MHz * Returns: 0 on success, -1 on failure @@ -830,7 +970,7 @@ struct wpa_driver_ops { * This function is only needed for drivers that export MLME * (management frame processing) to wpa_supplicant. */ - int (*set_channel)(void *priv, wpa_hw_mode phymode, int chan, + int (*set_channel)(void *priv, enum hostapd_hw_mode phymode, int chan, int freq); /** @@ -941,29 +1081,6 @@ struct wpa_driver_ops { struct wpa_scan_results * (*get_scan_results2)(void *priv); /** - * set_probe_req_ie - Set information element(s) for Probe Request - * @priv: private driver interface data - * @ies: Information elements to append or %NULL to remove extra IEs - * @ies_len: Length of the IE buffer in octets - * Returns: 0 on success, -1 on failure - */ - int (*set_probe_req_ie)(void *priv, const u8 *ies, size_t ies_len); - - /** - * set_mode - Request driver to set the operating mode - * @priv: private driver interface data - * @mode: Operation mode (infra/ibss) IEEE80211_MODE_* - * - * This handler will be called before any key configuration and call to - * associate() handler in order to allow the operation mode to be - * configured as early as possible. This information is also available - * in associate() params and as such, some driver wrappers may not need - * to implement set_mode() handler. - * Returns: 0 on success, -1 on failure - */ - int (*set_mode)(void *priv, int mode); - - /** * set_country - Set country * @priv: Private driver interface data * @alpha2: country to which to switch to @@ -1017,19 +1134,644 @@ struct wpa_driver_ops { * failure */ struct wpa_interface_info * (*get_interfaces)(void *global_priv); + + /** + * scan2 - Request the driver to initiate scan + * @priv: private driver interface data + * @params: Scan parameters + * + * Returns: 0 on success, -1 on failure + * + * Once the scan results are ready, the driver should report scan + * results event for wpa_supplicant which will eventually request the + * results with wpa_driver_get_scan_results2(). + */ + int (*scan2)(void *priv, struct wpa_driver_scan_params *params); + + /** + * authenticate - Request driver to authenticate + * @priv: private driver interface data + * @params: authentication parameters + * Returns: 0 on success, -1 on failure + * + * This is an optional function that can be used with drivers that + * support separate authentication and association steps, i.e., when + * wpa_supplicant can act as the SME. If not implemented, associate() + * function is expected to take care of IEEE 802.11 authentication, + * too. + */ + int (*authenticate)(void *priv, + struct wpa_driver_auth_params *params); + + /** + * set_beacon - Set Beacon frame template + * @priv: Private driver interface data + * @head: Beacon head from IEEE 802.11 header to IEs before TIM IE + * @head_len: Length of the head buffer in octets + * @tail: Beacon tail following TIM IE + * @tail_len: Length of the tail buffer in octets + * @dtim_period: DTIM period + * @beacon_int: Beacon interval + * Returns: 0 on success, -1 on failure + * + * This function is used to configure Beacon template for the driver in + * AP mode. The driver is responsible for building the full Beacon + * frame by concatenating the head part with TIM IE generated by the + * driver/firmware and finishing with the tail part. + */ + int (*set_beacon)(void *priv, const u8 *head, size_t head_len, + const u8 *tail, size_t tail_len, int dtim_period, + int beacon_int); + + /** + * hapd_init - Initialize driver interface (hostapd only) + * @hapd: Pointer to hostapd context + * @params: Configuration for the driver wrapper + * Returns: Pointer to private data, %NULL on failure + * + * This function is used instead of init() or init2() when the driver + * wrapper is used withh hostapd. + */ + void * (*hapd_init)(struct hostapd_data *hapd, + struct wpa_init_params *params); + + /** + * hapd_deinit - Deinitialize driver interface (hostapd only) + * @priv: Private driver interface data from hapd_init() + */ + void (*hapd_deinit)(void *priv); + + /** + * set_ieee8021x - Enable/disable IEEE 802.1X support (AP only) + * @priv: Private driver interface data + * @params: BSS parameters + * Returns: 0 on success, -1 on failure + * + * This is an optional function to configure the kernel driver to + * enable/disable IEEE 802.1X support and set WPA/WPA2 parameters. This + * can be left undefined (set to %NULL) if IEEE 802.1X support is + * always enabled and the driver uses set_beacon() to set WPA/RSN IE + * for Beacon frames. + */ + int (*set_ieee8021x)(void *priv, struct wpa_bss_params *params); + + /** + * set_privacy - Enable/disable privacy (AP only) + * @priv: Private driver interface data + * @enabled: 1 = privacy enabled, 0 = disabled + * Returns: 0 on success, -1 on failure + * + * This is an optional function to configure privacy field in the + * kernel driver for Beacon frames. This can be left undefined (set to + * %NULL) if the driver uses the Beacon template from set_beacon(). + */ + int (*set_privacy)(void *priv, int enabled); + + /** + * get_seqnum - Fetch the current TSC/packet number (AP only) + * @ifname: The interface name (main or virtual) + * @priv: Private driver interface data + * @addr: MAC address of the station or %NULL for group keys + * @idx: Key index + * @seq: Buffer for returning the latest used TSC/packet number + * Returns: 0 on success, -1 on failure + * + * This function is used to fetch the last used TSC/packet number for + * a TKIP, CCMP, or BIP/IGTK key. It is mainly used with group keys, so + * there is no strict requirement on implementing support for unicast + * keys (i.e., addr != %NULL). + */ + int (*get_seqnum)(const char *ifname, void *priv, const u8 *addr, + int idx, u8 *seq); + + /** + * flush - Flush all association stations (AP only) + * @priv: Private driver interface data + * Returns: 0 on success, -1 on failure + * + * This function requests the driver to disassociate all associated + * stations. This function does not need to be implemented if the + * driver does not process association frames internally. + */ + int (*flush)(void *priv); + + /** + * set_generic_elem - Add IEs into Beacon/Probe Response frames (AP) + * @priv: Private driver interface data + * @elem: Information elements + * @elem_len: Length of the elem buffer in octets + * Returns: 0 on success, -1 on failure + * + * This is an optional function to add information elements in the + * kernel driver for Beacon and Probe Response frames. This can be left + * undefined (set to %NULL) if the driver uses the Beacon template from + * set_beacon(). + */ + int (*set_generic_elem)(void *priv, const u8 *elem, size_t elem_len); + + /** + * read_sta_data - Fetch station data (AP only) + * @priv: Private driver interface data + * @data: Buffer for returning station information + * @addr: MAC address of the station + * Returns: 0 on success, -1 on failure + */ + int (*read_sta_data)(void *priv, struct hostap_sta_driver_data *data, + const u8 *addr); + + /** + * hapd_send_eapol - Send an EAPOL packet (AP only) + * @priv: private driver interface data + * @addr: Destination MAC address + * @data: EAPOL packet starting with IEEE 802.1X header + * @data_len: Length of the EAPOL packet in octets + * @encrypt: Whether the frame should be encrypted + * @own_addr: Source MAC address + * + * Returns: 0 on success, -1 on failure + */ + int (*hapd_send_eapol)(void *priv, const u8 *addr, const u8 *data, + size_t data_len, int encrypt, + const u8 *own_addr); + + /** + * sta_deauth - Deauthenticate a station (AP only) + * @priv: Private driver interface data + * @own_addr: Source address and BSSID for the Deauthentication frame + * @addr: MAC address of the station to deauthenticate + * @reason: Reason code for the Deauthentiation frame + * Returns: 0 on success, -1 on failure + * + * This function requests a specific station to be deauthenticated and + * a Deauthentication frame to be sent to it. + */ + int (*sta_deauth)(void *priv, const u8 *own_addr, const u8 *addr, + int reason); + + /** + * sta_disassoc - Disassociate a station (AP only) + * @priv: Private driver interface data + * @own_addr: Source address and BSSID for the Disassociation frame + * @addr: MAC address of the station to disassociate + * @reason: Reason code for the Disassociation frame + * Returns: 0 on success, -1 on failure + * + * This function requests a specific station to be disassociated and + * a Disassociation frame to be sent to it. + */ + int (*sta_disassoc)(void *priv, const u8 *own_addr, const u8 *addr, + int reason); + + /** + * sta_remove - Remove a station entry (AP only) + * @priv: Private driver interface data + * @addr: MAC address of the station to be removed + * Returns: 0 on success, -1 on failure + */ + int (*sta_remove)(void *priv, const u8 *addr); + + /** + * hapd_get_ssid - Get the current SSID (AP only) + * @priv: Private driver interface data + * @buf: Buffer for returning the SSID + * @len: Maximum length of the buffer + * Returns: Length of the SSID on success, -1 on failure + * + * This function need not be implemented if the driver uses Beacon + * template from set_beacon() and does not reply to Probe Request + * frames. + */ + int (*hapd_get_ssid)(void *priv, u8 *buf, int len); + + /** + * hapd_set_ssid - Set SSID (AP only) + * @priv: Private driver interface data + * @buf: SSID + * @len: Length of the SSID in octets + * Returns: 0 on success, -1 on failure + */ + int (*hapd_set_ssid)(void *priv, const u8 *buf, int len); + + /** + * hapd_set_countermeasures - Enable/disable TKIP countermeasures (AP) + * @priv: Private driver interface data + * @enabled: 1 = countermeasures enabled, 0 = disabled + * Returns: 0 on success, -1 on failure + * + * This need not be implemented if the driver does not take care of + * association processing. + */ + int (*hapd_set_countermeasures)(void *priv, int enabled); + + /** + * sta_add - Add a station entry + * @priv: Private driver interface data + * @params: Station parameters + * Returns: 0 on success, -1 on failure + * + * This function is used to add a station entry to the driver once the + * station has completed association. This is only used if the driver + * does not take care of association processing. + */ + int (*sta_add)(void *priv, struct hostapd_sta_add_params *params); + + /** + * get_inact_sec - Get station inactivity duration (AP only) + * @priv: Private driver interface data + * @addr: Station address + * Returns: Number of seconds station has been inactive, -1 on failure + */ + int (*get_inact_sec)(void *priv, const u8 *addr); + + /** + * sta_clear_stats - Clear station statistics (AP only) + * @priv: Private driver interface data + * @addr: Station address + * Returns: 0 on success, -1 on failure + */ + int (*sta_clear_stats)(void *priv, const u8 *addr); + + /** + * set_freq - Set channel/frequency (AP only) + * @priv: Private driver interface data + * @freq: Channel parameters + * Returns: 0 on success, -1 on failure + */ + int (*set_freq)(void *priv, struct hostapd_freq_params *freq); + + /** + * set_rts - Set RTS threshold + * @priv: Private driver interface data + * @rts: RTS threshold in octets + * Returns: 0 on success, -1 on failure + */ + int (*set_rts)(void *priv, int rts); + + /** + * set_frag - Set fragmentation threshold + * @priv: Private driver interface data + * @frag: Fragmentation threshold in octets + * Returns: 0 on success, -1 on failure + */ + int (*set_frag)(void *priv, int frag); + + /** + * sta_set_flags - Set station flags (AP only) + * @priv: Private driver interface data + * @addr: Station address + * @total_flags: Bitmap of all WPA_STA_* flags currently set + * @flags_or: Bitmap of WPA_STA_* flags to add + * @flags_and: Bitmap of WPA_STA_* flags to us as a mask + * Returns: 0 on success, -1 on failure + */ + int (*sta_set_flags)(void *priv, const u8 *addr, + int total_flags, int flags_or, int flags_and); + + /** + * set_rate_sets - Set supported and basic rate sets (AP only) + * @priv: Private driver interface data + * @supp_rates: -1 terminated array of supported rates in 100 kbps + * @basic_rates: -1 terminated array of basic rates in 100 kbps + * @mode: hardware mode (HOSTAPD_MODE_*) + * Returns: 0 on success, -1 on failure + */ + int (*set_rate_sets)(void *priv, int *supp_rates, int *basic_rates, + int mode); + + /** + * set_cts_protect - Set CTS protection mode (AP only) + * @priv: Private driver interface data + * @value: Whether CTS protection is enabled + * Returns: 0 on success, -1 on failure + */ + int (*set_cts_protect)(void *priv, int value); + + /** + * set_preamble - Set preamble mode (AP only) + * @priv: Private driver interface data + * @value: Whether short preamble is enabled + * Returns: 0 on success, -1 on failure + */ + int (*set_preamble)(void *priv, int value); + + /** + * set_short_slot_time - Set short slot time (AP only) + * @priv: Private driver interface data + * @value: Whether short slot time is enabled + * Returns: 0 on success, -1 on failure + */ + int (*set_short_slot_time)(void *priv, int value); + + /** + * set_tx_queue_params - Set TX queue parameters + * @priv: Private driver interface data + * @queue: Queue number + * @aifs: AIFS + * @cw_min: cwMin + * @cw_max: cwMax + * @burst_time: Maximum length for bursting in 0.1 msec units + */ + int (*set_tx_queue_params)(void *priv, int queue, int aifs, int cw_min, + int cw_max, int burst_time); + + /** + * valid_bss_mask - Validate BSSID mask + * @priv: Private driver interface data + * @addr: Address + * @mask: Mask + * Returns: 0 if mask is valid, -1 if mask is not valid, 1 if mask can + * be used, but the main interface address must be the first address in + * the block if mask is applied + */ + int (*valid_bss_mask)(void *priv, const u8 *addr, const u8 *mask); + + /** + * if_add - Add a virtual interface + * @priv: Private driver interface data + * @type: Interface type + * @ifname: Interface name for the new virtual interface + * @addr: Local address to use for the interface or %NULL to use the + * parent interface address + * @bss_ctx: BSS context for %WPA_IF_AP_BSS interfaces + * @drv_priv: Pointer for overwriting the driver context or %NULL if + * not allowed (applies only to %WPA_IF_AP_BSS type) + * @force_ifname: Buffer for returning an interface name that the + * driver ended up using if it differs from the requested ifname + * @if_addr: Buffer for returning the allocated interface address + * (this may differ from the requested addr if the driver cannot + * change interface address) + * Returns: 0 on success, -1 on failure + */ + int (*if_add)(void *priv, enum wpa_driver_if_type type, + const char *ifname, const u8 *addr, void *bss_ctx, + void **drv_priv, char *force_ifname, u8 *if_addr); + + /** + * if_remove - Remove a virtual interface + * @priv: Private driver interface data + * @type: Interface type + * @ifname: Interface name of the virtual interface to be removed + * Returns: 0 on success, -1 on failure + */ + int (*if_remove)(void *priv, enum wpa_driver_if_type type, + const char *ifname); + + /** + * set_sta_vlan - Bind a station into a specific interface (AP only) + * @priv: Private driver interface data + * @ifname: Interface (main or virtual BSS or VLAN) + * @addr: MAC address of the associated station + * @vlan_id: VLAN ID + * Returns: 0 on success, -1 on failure + * + * This function is used to bind a station to a specific virtual + * interface. It is only used if when virtual interfaces are supported, + * e.g., to assign stations to different VLAN interfaces based on + * information from a RADIUS server. This allows separate broadcast + * domains to be used with a single BSS. + */ + int (*set_sta_vlan)(void *priv, const u8 *addr, const char *ifname, + int vlan_id); + + /** + * commit - Optional commit changes handler (AP only) + * @priv: driver private data + * Returns: 0 on success, -1 on failure + * + * This optional handler function can be registered if the driver + * interface implementation needs to commit changes (e.g., by setting + * network interface up) at the end of initial configuration. If set, + * this handler will be called after initial setup has been completed. + */ + int (*commit)(void *priv); + + /** + * send_ether - Send an ethernet packet (AP only) + * @priv: private driver interface data + * @dst: Destination MAC address + * @src: Source MAC address + * @proto: Ethertype + * @data: EAPOL packet starting with IEEE 802.1X header + * @data_len: Length of the EAPOL packet in octets + * Returns: 0 on success, -1 on failure + */ + int (*send_ether)(void *priv, const u8 *dst, const u8 *src, u16 proto, + const u8 *data, size_t data_len); + + /** + * set_radius_acl_auth - Notification of RADIUS ACL change + * @priv: Private driver interface data + * @mac: MAC address of the station + * @accepted: Whether the station was accepted + * @session_timeout: Session timeout for the station + * Returns: 0 on success, -1 on failure + */ + int (*set_radius_acl_auth)(void *priv, const u8 *mac, int accepted, + u32 session_timeout); + + /** + * set_radius_acl_expire - Notification of RADIUS ACL expiration + * @priv: Private driver interface data + * @mac: MAC address of the station + * Returns: 0 on success, -1 on failure + */ + int (*set_radius_acl_expire)(void *priv, const u8 *mac); + + /** + * set_ht_params - Set HT parameters (AP only) + * @priv: Private driver interface data + * @ht_capab: HT Capabilities IE + * @ht_capab_len: Length of ht_capab in octets + * @ht_oper: HT Operation IE + * @ht_oper_len: Length of ht_oper in octets + * Returns: 0 on success, -1 on failure + */ + int (*set_ht_params)(void *priv, + const u8 *ht_capab, size_t ht_capab_len, + const u8 *ht_oper, size_t ht_oper_len); + + /** + * set_ap_wps_ie - Add WPS IE(s) into Beacon/Probe Response frames (AP) + * @priv: Private driver interface data + * @beacon: WPS IE(s) for Beacon frames or %NULL to remove extra IE(s) + * @proberesp: WPS IE(s) for Probe Response frames or %NULL to remove + * extra IE(s) + * Returns: 0 on success, -1 on failure + * + * This is an optional function to add WPS IE in the kernel driver for + * Beacon and Probe Response frames. This can be left undefined (set + * to %NULL) if the driver uses the Beacon template from set_beacon() + * and does not process Probe Request frames. + */ + int (*set_ap_wps_ie)(void *priv, const struct wpabuf *beacon, + const struct wpabuf *proberesp); + + /** + * set_supp_port - Set IEEE 802.1X Supplicant Port status + * @priv: Private driver interface data + * @authorized: Whether the port is authorized + * Returns: 0 on success, -1 on failure + */ + int (*set_supp_port)(void *priv, int authorized); + + /** + * set_wds_sta - Bind a station into a 4-address WDS (AP only) + * @priv: Private driver interface data + * @addr: MAC address of the associated station + * @aid: Association ID + * @val: 1 = bind to 4-address WDS; 0 = unbind + * Returns: 0 on success, -1 on failure + */ + int (*set_wds_sta)(void *priv, const u8 *addr, int aid, int val); + + /** + * send_action - Transmit an Action frame + * @priv: Private driver interface data + * @freq: Frequency (in MHz) of the channel + * @dst: Destination MAC address (Address 1) + * @src: Source MAC address (Address 2) + * @bssid: BSSID (Address 3) + * @data: Frame body + * @data_len: data length in octets + * Returns: 0 on success, -1 on failure + * + * This command can be used to request the driver to transmit an action + * frame to the specified destination. If a remain-on-channel duration + * is in progress, the frame is transmitted on that channel. Otherwise, + * the frame is transmitted on the current operational channel if in + * associated state in station mode or if operating as an AP. If none + * of these conditions is in effect, send_action() cannot be used. + */ + int (*send_action)(void *priv, unsigned int freq, + const u8 *dst, const u8 *src, const u8 *bssid, + const u8 *data, size_t data_len); + + /** + * remain_on_channel - Remain awake on a channel + * @priv: Private driver interface data + * @freq: Frequency (in MHz) of the channel + * @duration: Duration in milliseconds + * Returns: 0 on success, -1 on failure + * + * This command is used to request the driver to remain awake on the + * specified channel for the specified duration and report received + * Action frames with EVENT_RX_ACTION events. Optionally, received + * Probe Request frames may also be requested to be reported by calling + * probe_req_report(). These will be reported with EVENT_RX_PROBE_REQ. + * + * The driver may not be at the requested channel when this function + * returns, i.e., the return code is only indicating whether the + * request was accepted. The caller will need to wait until the + * EVENT_REMAIN_ON_CHANNEL event indicates that the driver has + * completed the channel change. This may take some time due to other + * need for the radio and the caller should be prepared to timing out + * its wait since there are no guarantees on when this request can be + * executed. + */ + int (*remain_on_channel)(void *priv, unsigned int freq, + unsigned int duration); + + /** + * cancel_remain_on_channel - Cancel remain-on-channel operation + * @priv: Private driver interface data + * + * This command can be used to cancel a remain-on-channel operation + * before its originally requested duration has passed. This could be + * used, e.g., when remain_on_channel() is used to request extra time + * to receive a response to an Action frame and the response is + * received when there is still unneeded time remaining on the + * remain-on-channel operation. + */ + int (*cancel_remain_on_channel)(void *priv); + + /** + * probe_req_report - Request Probe Request frames to be indicated + * @priv: Private driver interface data + * @report: Whether to report received Probe Request frames + * Returns: 0 on success, -1 on failure (or if not supported) + * + * This command can be used to request the driver to indicate when + * Probe Request frames are received with EVENT_RX_PROBE_REQ events. + * Since this operation may require extra resources, e.g., due to less + * optimal hardware/firmware RX filtering, many drivers may disable + * Probe Request reporting at least in station mode. This command is + * used to notify the driver when the Probe Request frames need to be + * reported, e.g., during remain-on-channel operations. + */ + int (*probe_req_report)(void *priv, int report); + + /** + * disable_11b_rates - Set whether IEEE 802.11b rates are used for TX + * @priv: Private driver interface data + * @disabled: Whether IEEE 802.11b rates are disabled + * Returns: 0 on success, -1 on failure (or if not supported) + * + * This command is used to disable IEEE 802.11b rates (1, 2, 5.5, and + * 11 Mbps) as TX rates for data and management frames. This can be + * used to optimize channel use when there is no need to support IEEE + * 802.11b-only devices. + */ + int (*disable_11b_rates)(void *priv, int disabled); + + /** + * deinit_ap - Deinitialize AP mode + * @priv: Private driver interface data + * Returns: 0 on success, -1 on failure (or if not supported) + * + * This optional function can be used to disable AP mode related + * configuration and change the driver mode to station mode to allow + * normal station operations like scanning to be completed. + */ + int (*deinit_ap)(void *priv); + + /** + * suspend - Notification on system suspend/hibernate event + * @priv: Private driver interface data + */ + void (*suspend)(void *priv); + + /** + * resume - Notification on system resume/thaw event + * @priv: Private driver interface data + */ + void (*resume)(void *priv); + + /** + * signal_monitor - Set signal monitoring parameters + * @priv: Private driver interface data + * @threshold: Threshold value for signal change events; 0 = disabled + * @hysteresis: Minimum change in signal strength before indicating a + * new event + * Returns: 0 on success, -1 on failure (or if not supported) + * + * This function can be used to configure monitoring of signal strength + * with the current AP. Whenever signal strength drops below the + * %threshold value or increases above it, EVENT_SIGNAL_CHANGE event + * should be generated assuming the signal strength has changed at + * least %hysteresis from the previously indicated signal change event. + */ + int (*signal_monitor)(void *priv, int threshold, int hysteresis); + + /** + * send_frame - Send IEEE 802.11 frame (testing use only) + * @priv: Private driver interface data + * @data: IEEE 802.11 frame with IEEE 802.11 header + * @data_len: Size of the frame + * @encrypt: Whether to encrypt the frame (if keys are set) + * Returns: 0 on success, -1 on failure + * + * This function is only used for debugging purposes and is not + * required to be implemented for normal operations. + */ + int (*send_frame)(void *priv, const u8 *data, size_t data_len, + int encrypt); }; -/* Function to check whether a driver is for wired connections */ -static inline int IS_WIRED(const struct wpa_driver_ops *drv) -{ - return os_strcmp(drv->name, "wired") == 0 || - os_strcmp(drv->name, "roboswitch") == 0; -} /** * enum wpa_event_type - Event type for wpa_supplicant_event() calls */ -typedef enum wpa_event_type { +enum wpa_event_type { /** * EVENT_ASSOC - Association completed * @@ -1049,7 +1791,10 @@ typedef enum wpa_event_type { * * This event should be called when association is lost either due to * receiving deauthenticate or disassociate frame from the AP or when - * sending either of these frames to the current AP. + * sending either of these frames to the current AP. If the driver + * supports separate deauthentication event, EVENT_DISASSOC should only + * be used for disassociation and EVENT_DEAUTH for deauthentication. + * In AP mode, union wpa_event_data::disassoc_info is required. */ EVENT_DISASSOC, @@ -1075,7 +1820,8 @@ typedef enum wpa_event_type { * EVENT_SCAN_RESULTS call. If such event is not available from the * driver, the driver wrapper code is expected to use a registered * timeout to generate EVENT_SCAN_RESULTS call after the time that the - * scan is expected to be completed. + * scan is expected to be completed. Optional information about + * completed scan can be provided with union wpa_event_data::scan_info. */ EVENT_SCAN_RESULTS, @@ -1147,8 +1893,161 @@ typedef enum wpa_event_type { * FT authentication sequence from the AP. The FT IEs are included in * the extra information in union wpa_event_data::ft_ies. */ - EVENT_FT_RESPONSE -} wpa_event_type; + EVENT_FT_RESPONSE, + + /** + * EVENT_IBSS_RSN_START - Request RSN authentication in IBSS + * + * The driver can use this event to inform wpa_supplicant about a STA + * in an IBSS with which protected frames could be exchanged. This + * event starts RSN authentication with the other STA to authenticate + * the STA and set up encryption keys with it. + */ + EVENT_IBSS_RSN_START, + + /** + * EVENT_AUTH - Authentication result + * + * This event should be called when authentication attempt has been + * completed. This is only used if the driver supports separate + * authentication step (struct wpa_driver_ops::authenticate). + * Information about authentication result is included in + * union wpa_event_data::auth. + */ + EVENT_AUTH, + + /** + * EVENT_DEAUTH - Authentication lost + * + * This event should be called when authentication is lost either due + * to receiving deauthenticate frame from the AP or when sending that + * frame to the current AP. + * In AP mode, union wpa_event_data::deauth_info is required. + */ + EVENT_DEAUTH, + + /** + * EVENT_ASSOC_REJECT - Association rejected + * + * This event should be called when (re)association attempt has been + * rejected by the AP. Information about authentication result is + * included in union wpa_event_data::assoc_reject. + */ + EVENT_ASSOC_REJECT, + + /** + * EVENT_AUTH_TIMED_OUT - Authentication timed out + */ + EVENT_AUTH_TIMED_OUT, + + /** + * EVENT_ASSOC_TIMED_OUT - Association timed out + */ + EVENT_ASSOC_TIMED_OUT, + + /** + * EVENT_FT_RRB_RX - FT (IEEE 802.11r) RRB frame received + */ + EVENT_FT_RRB_RX, + + /** + * EVENT_WPS_BUTTON_PUSHED - Report hardware push button press for WPS + */ + EVENT_WPS_BUTTON_PUSHED, + + /** + * EVENT_TX_STATUS - Report TX status + */ + EVENT_TX_STATUS, + + /** + * EVENT_RX_FROM_UNKNOWN - Report RX from unknown STA + */ + EVENT_RX_FROM_UNKNOWN, + + /** + * EVENT_RX_MGMT - Report RX of a management frame + */ + EVENT_RX_MGMT, + + /** + * EVENT_RX_ACTION - Action frame received + * + * This event is used to indicate when an Action frame has been + * received. Information about the received frame is included in + * union wpa_event_data::rx_action. + */ + EVENT_RX_ACTION, + + /** + * EVENT_REMAIN_ON_CHANNEL - Remain-on-channel duration started + * + * This event is used to indicate when the driver has started the + * requested remain-on-channel duration. Information about the + * operation is included in union wpa_event_data::remain_on_channel. + */ + EVENT_REMAIN_ON_CHANNEL, + + /** + * EVENT_CANCEL_REMAIN_ON_CHANNEL - Remain-on-channel timed out + * + * This event is used to indicate when the driver has completed + * remain-on-channel duration, i.e., may noot be available on the + * requested channel anymore. Information about the + * operation is included in union wpa_event_data::remain_on_channel. + */ + EVENT_CANCEL_REMAIN_ON_CHANNEL, + + /** + * EVENT_MLME_RX - Report reception of frame for MLME (test use only) + * + * This event is used only by driver_test.c and userspace MLME. + */ + EVENT_MLME_RX, + + /** + * EVENT_RX_PROBE_REQ - Indicate received Probe Request frame + * + * This event is used to indicate when a Probe Request frame has been + * received. Information about the received frame is included in + * union wpa_event_data::rx_probe_req. The driver is required to report + * these events only after successfully completed probe_req_report() + * commands to request the events (i.e., report parameter is non-zero) + * in station mode. In AP mode, Probe Request frames should always be + * reported. + */ + EVENT_RX_PROBE_REQ, + + /** + * EVENT_NEW_STA - New wired device noticed + * + * This event is used to indicate that a new device has been detected + * in a network that does not use association-like functionality (i.e., + * mainly wired Ethernet). This can be used to start EAPOL + * authenticator when receiving a frame from a device. The address of + * the device is included in union wpa_event_data::new_sta. + */ + EVENT_NEW_STA, + + /** + * EVENT_EAPOL_RX - Report received EAPOL frame + * + * When in AP mode with hostapd, this event is required to be used to + * deliver the receive EAPOL frames from the driver. With + * %wpa_supplicant, this event is used only if the send_eapol() handler + * is used to override the use of l2_packet for EAPOL frame TX. + */ + EVENT_EAPOL_RX, + + /** + * EVENT_SIGNAL_CHANGE - Indicate change in signal strength + * + * This event is used to indicate changes in the signal strength + * observed in frames received from the current AP if signal strength + * monitoring has been enabled with signal_monitor(). + */ + EVENT_SIGNAL_CHANGE +}; /** @@ -1175,7 +2074,7 @@ union wpa_event_data { * This should start with the first IE (fixed fields before IEs * are not included). */ - u8 *req_ies; + const u8 *req_ies; /** * req_ies_len - Length of req_ies in bytes @@ -1193,7 +2092,7 @@ union wpa_event_data { * This should start with the first IE (fixed fields before IEs * are not included). */ - u8 *resp_ies; + const u8 *resp_ies; /** * resp_ies_len - Length of resp_ies in bytes @@ -1216,18 +2115,61 @@ union wpa_event_data { * This should start with the first IE (fixed fields before IEs * are not included). */ - u8 *beacon_ies; + const u8 *beacon_ies; /** * beacon_ies_len - Length of beacon_ies */ size_t beacon_ies_len; + + /** + * freq - Frequency of the operational channel in MHz + */ + unsigned int freq; + + /** + * addr - Station address (for AP mode) + */ + const u8 *addr; } assoc_info; /** + * struct disassoc_info - Data for EVENT_DISASSOC events + */ + struct disassoc_info { + /** + * addr - Station address (for AP mode) + */ + const u8 *addr; + + /** + * reason_code - Reason Code (host byte order) used in + * Deauthentication frame + */ + u16 reason_code; + } disassoc_info; + + /** + * struct deauth_info - Data for EVENT_DEAUTH events + */ + struct deauth_info { + /** + * addr - Station address (for AP mode) + */ + const u8 *addr; + + /** + * reason_code - Reason Code (host byte order) used in + * Deauthentication frame + */ + u16 reason_code; + } deauth_info; + + /** * struct michael_mic_failure - Data for EVENT_MICHAEL_MIC_FAILURE */ struct michael_mic_failure { int unicast; + const u8 *src; } michael_mic_failure; /** @@ -1273,7 +2215,228 @@ union wpa_event_data { size_t ies_len; int ft_action; u8 target_ap[ETH_ALEN]; + /** Optional IE(s), e.g., WMM TSPEC(s), for RIC-Request */ + const u8 *ric_ies; + /** Length of ric_ies buffer in octets */ + size_t ric_ies_len; } ft_ies; + + /** + * struct ibss_rsn_start - Data for EVENT_IBSS_RSN_START + */ + struct ibss_rsn_start { + u8 peer[ETH_ALEN]; + } ibss_rsn_start; + + /** + * struct auth_info - Data for EVENT_AUTH events + */ + struct auth_info { + u8 peer[ETH_ALEN]; + u16 auth_type; + u16 status_code; + const u8 *ies; + size_t ies_len; + } auth; + + /** + * struct assoc_reject - Data for EVENT_ASSOC_REJECT events + */ + struct assoc_reject { + /** + * resp_ies - (Re)Association Response IEs + * + * Optional association data from the driver. This data is not + * required WPA, but may be useful for some protocols and as + * such, should be reported if this is available to the driver + * interface. + * + * This should start with the first IE (fixed fields before IEs + * are not included). + */ + u8 *resp_ies; + + /** + * resp_ies_len - Length of resp_ies in bytes + */ + size_t resp_ies_len; + + /** + * status_code - Status Code from (Re)association Response + */ + u16 status_code; + } assoc_reject; + + struct timeout_event { + u8 addr[ETH_ALEN]; + } timeout_event; + + /** + * struct ft_rrb_rx - Data for EVENT_FT_RRB_RX events + */ + struct ft_rrb_rx { + const u8 *src; + const u8 *data; + size_t data_len; + } ft_rrb_rx; + + /** + * struct tx_status - Data for EVENT_TX_STATUS events + */ + struct tx_status { + u16 type; + u16 stype; + const u8 *dst; + const u8 *data; + size_t data_len; + int ack; + } tx_status; + + /** + * struct rx_from_unknown - Data for EVENT_RX_FROM_UNKNOWN events + */ + struct rx_from_unknown { + const u8 *frame; + size_t len; + } rx_from_unknown; + + /** + * struct rx_mgmt - Data for EVENT_RX_MGMT events + */ + struct rx_mgmt { + const u8 *frame; + size_t frame_len; + u32 datarate; + u32 ssi_signal; + } rx_mgmt; + + /** + * struct rx_action - Data for EVENT_RX_ACTION events + */ + struct rx_action { + /** + * da - Destination address of the received Action frame + */ + const u8 *da; + + /** + * sa - Source address of the received Action frame + */ + const u8 *sa; + + /** + * bssid - Address 3 of the received Action frame + */ + const u8 *bssid; + + /** + * category - Action frame category + */ + u8 category; + + /** + * data - Action frame body after category field + */ + const u8 *data; + + /** + * len - Length of data in octets + */ + size_t len; + + /** + * freq - Frequency (in MHz) on which the frame was received + */ + int freq; + } rx_action; + + /** + * struct remain_on_channel - Data for EVENT_REMAIN_ON_CHANNEL events + * + * This is also used with EVENT_CANCEL_REMAIN_ON_CHANNEL events. + */ + struct remain_on_channel { + /** + * freq - Channel frequency in MHz + */ + unsigned int freq; + + /** + * duration - Duration to remain on the channel in milliseconds + */ + unsigned int duration; + } remain_on_channel; + + /** + * struct scan_info - Optional data for EVENT_SCAN_RESULTS events + * @aborted: Whether the scan was aborted + * @freqs: Scanned frequencies in MHz (%NULL = all channels scanned) + * @num_freqs: Number of entries in freqs array + * @ssids: Scanned SSIDs (%NULL or zero-length SSID indicates wildcard + * SSID) + * @num_ssids: Number of entries in ssids array + */ + struct scan_info { + int aborted; + const int *freqs; + size_t num_freqs; + struct wpa_driver_scan_ssid ssids[WPAS_MAX_SCAN_SSIDS]; + size_t num_ssids; + } scan_info; + + /** + * struct mlme_rx - Data for EVENT_MLME_RX events + */ + struct mlme_rx { + const u8 *buf; + size_t len; + int freq; + int channel; + int ssi; + } mlme_rx; + + /** + * struct rx_probe_req - Data for EVENT_RX_PROBE_REQ events + */ + struct rx_probe_req { + /** + * sa - Source address of the received Probe Request frame + */ + const u8 *sa; + + /** + * ie - IEs from the Probe Request body + */ + const u8 *ie; + + /** + * ie_len - Length of ie buffer in octets + */ + size_t ie_len; + } rx_probe_req; + + /** + * struct new_sta - Data for EVENT_NEW_STA events + */ + struct new_sta { + const u8 *addr; + } new_sta; + + /** + * struct eapol_rx - Data for EVENT_EAPOL_RX events + */ + struct eapol_rx { + const u8 *src; + const u8 *data; + size_t data_len; + } eapol_rx; + + /** + * struct signal_change - Data for EVENT_SIGNAL_CHANGE events + */ + struct signal_change { + int above_threshold; + } signal_change; }; /** @@ -1286,40 +2449,43 @@ union wpa_event_data { * Driver wrapper code should call this function whenever an event is received * from the driver. */ -void wpa_supplicant_event(void *ctx, wpa_event_type event, +void wpa_supplicant_event(void *ctx, enum wpa_event_type event, union wpa_event_data *data); -/** - * wpa_supplicant_rx_eapol - Deliver a received EAPOL frame to wpa_supplicant - * @ctx: Context pointer (wpa_s); this is the ctx variable registered - * with struct wpa_driver_ops::init() - * @src_addr: Source address of the EAPOL frame - * @buf: EAPOL data starting from the EAPOL header (i.e., no Ethernet header) - * @len: Length of the EAPOL data - * - * This function is called for each received EAPOL frame. Most driver - * interfaces rely on more generic OS mechanism for receiving frames through - * l2_packet, but if such a mechanism is not available, the driver wrapper may - * take care of received EAPOL frames and deliver them to the core supplicant - * code by calling this function. + +/* + * The following inline functions are provided for convenience to simplify + * event indication for some of the common events. */ -void wpa_supplicant_rx_eapol(void *ctx, const u8 *src_addr, - const u8 *buf, size_t len); - -void wpa_supplicant_sta_rx(void *ctx, const u8 *buf, size_t len, - struct ieee80211_rx_status *rx_status); -void wpa_supplicant_sta_free_hw_features(struct wpa_hw_modes *hw_features, - size_t num_hw_features); - -const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie); -#define WPA_IE_VENDOR_TYPE 0x0050f201 -#define WPS_IE_VENDOR_TYPE 0x0050f204 -const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res, - u32 vendor_type); -struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res, - u32 vendor_type); -int wpa_scan_get_max_rate(const struct wpa_scan_res *res); -void wpa_scan_results_free(struct wpa_scan_results *res); -void wpa_scan_sort_results(struct wpa_scan_results *res); + +static inline void drv_event_assoc(void *ctx, const u8 *addr, const u8 *ie, + size_t ielen) +{ + union wpa_event_data event; + os_memset(&event, 0, sizeof(event)); + event.assoc_info.req_ies = ie; + event.assoc_info.req_ies_len = ielen; + event.assoc_info.addr = addr; + wpa_supplicant_event(ctx, EVENT_ASSOC, &event); +} + +static inline void drv_event_disassoc(void *ctx, const u8 *addr) +{ + union wpa_event_data event; + os_memset(&event, 0, sizeof(event)); + event.disassoc_info.addr = addr; + wpa_supplicant_event(ctx, EVENT_DISASSOC, &event); +} + +static inline void drv_event_eapol_rx(void *ctx, const u8 *src, const u8 *data, + size_t data_len) +{ + union wpa_event_data event; + os_memset(&event, 0, sizeof(event)); + event.eapol_rx.src = src; + event.eapol_rx.data = data; + event.eapol_rx.data_len = data_len; + wpa_supplicant_event(ctx, EVENT_EAPOL_RX, &event); +} #endif /* DRIVER_H */ diff --git a/contrib/wpa/src/drivers/driver_atheros.c b/contrib/wpa/src/drivers/driver_atheros.c new file mode 100644 index 0000000..5c25f00 --- /dev/null +++ b/contrib/wpa/src/drivers/driver_atheros.c @@ -0,0 +1,1298 @@ +/* + * hostapd / Driver interaction with Atheros driver + * Copyright (c) 2004, Sam Leffler <sam@errno.com> + * Copyright (c) 2004, Video54 Technologies + * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2009, Atheros Communications + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <net/if.h> +#include <sys/ioctl.h> + +#include "common.h" +#ifndef _BYTE_ORDER +#ifdef WORDS_BIGENDIAN +#define _BYTE_ORDER _BIG_ENDIAN +#else +#define _BYTE_ORDER _LITTLE_ENDIAN +#endif +#endif /* _BYTE_ORDER */ + +/* + * Note, the ATH_WPS_IE setting must match with the driver build.. If the + * driver does not include this, the IEEE80211_IOCTL_GETWPAIE ioctl will fail. + */ +#define ATH_WPS_IE + +#include "os/linux/include/ieee80211_external.h" + + +#ifdef CONFIG_WPS +#include <netpacket/packet.h> + +#ifndef ETH_P_80211_RAW +#define ETH_P_80211_RAW 0x0019 +#endif +#endif /* CONFIG_WPS */ + +#include "wireless_copy.h" + +#include "driver.h" +#include "eloop.h" +#include "priv_netlink.h" +#include "l2_packet/l2_packet.h" +#include "common/ieee802_11_defs.h" +#include "netlink.h" +#include "linux_ioctl.h" + + +struct madwifi_driver_data { + struct hostapd_data *hapd; /* back pointer */ + + char iface[IFNAMSIZ + 1]; + int ifindex; + struct l2_packet_data *sock_xmit; /* raw packet xmit socket */ + struct l2_packet_data *sock_recv; /* raw packet recv socket */ + int ioctl_sock; /* socket for ioctl() use */ + struct netlink_data *netlink; + int we_version; + u8 acct_mac[ETH_ALEN]; + struct hostap_sta_driver_data acct_data; + + struct l2_packet_data *sock_raw; /* raw 802.11 management frames */ +}; + +static int madwifi_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, + int reason_code); +static int madwifi_set_privacy(void *priv, int enabled); + +static const char * athr_get_ioctl_name(int op) +{ + switch (op) { + case IEEE80211_IOCTL_SETPARAM: + return "SETPARAM"; + case IEEE80211_IOCTL_GETPARAM: + return "GETPARAM"; + case IEEE80211_IOCTL_SETKEY: + return "SETKEY"; + case IEEE80211_IOCTL_SETWMMPARAMS: + return "SETWMMPARAMS"; + case IEEE80211_IOCTL_DELKEY: + return "DELKEY"; + case IEEE80211_IOCTL_GETWMMPARAMS: + return "GETWMMPARAMS"; + case IEEE80211_IOCTL_SETMLME: + return "SETMLME"; + case IEEE80211_IOCTL_GETCHANINFO: + return "GETCHANINFO"; + case IEEE80211_IOCTL_SETOPTIE: + return "SETOPTIE"; + case IEEE80211_IOCTL_GETOPTIE: + return "GETOPTIE"; + case IEEE80211_IOCTL_ADDMAC: + return "ADDMAC"; + case IEEE80211_IOCTL_DELMAC: + return "DELMAC"; + case IEEE80211_IOCTL_GETCHANLIST: + return "GETCHANLIST"; + case IEEE80211_IOCTL_SETCHANLIST: + return "SETCHANLIST"; + case IEEE80211_IOCTL_KICKMAC: + return "KICKMAC"; + case IEEE80211_IOCTL_CHANSWITCH: + return "CHANSWITCH"; + case IEEE80211_IOCTL_GETMODE: + return "GETMODE"; + case IEEE80211_IOCTL_SETMODE: + return "SETMODE"; + case IEEE80211_IOCTL_GET_APPIEBUF: + return "GET_APPIEBUF"; + case IEEE80211_IOCTL_SET_APPIEBUF: + return "SET_APPIEBUF"; + case IEEE80211_IOCTL_SET_ACPARAMS: + return "SET_ACPARAMS"; + case IEEE80211_IOCTL_FILTERFRAME: + return "FILTERFRAME"; + case IEEE80211_IOCTL_SET_RTPARAMS: + return "SET_RTPARAMS"; + case IEEE80211_IOCTL_SENDADDBA: + return "SENDADDBA"; + case IEEE80211_IOCTL_GETADDBASTATUS: + return "GETADDBASTATUS"; + case IEEE80211_IOCTL_SENDDELBA: + return "SENDDELBA"; + case IEEE80211_IOCTL_SET_MEDENYENTRY: + return "SET_MEDENYENTRY"; + case IEEE80211_IOCTL_SET_ADDBARESP: + return "SET_ADDBARESP"; + case IEEE80211_IOCTL_GET_MACADDR: + return "GET_MACADDR"; + case IEEE80211_IOCTL_SET_HBRPARAMS: + return "SET_HBRPARAMS"; + case IEEE80211_IOCTL_SET_RXTIMEOUT: + return "SET_RXTIMEOUT"; + case IEEE80211_IOCTL_STA_STATS: + return "STA_STATS"; + case IEEE80211_IOCTL_GETWPAIE: + return "GETWPAIE"; + default: + return "??"; + } +} + + +static const char * athr_get_param_name(int op) +{ + switch (op) { + case IEEE80211_IOC_MCASTCIPHER: + return "MCASTCIPHER"; + case IEEE80211_PARAM_MCASTKEYLEN: + return "MCASTKEYLEN"; + case IEEE80211_PARAM_UCASTCIPHERS: + return "UCASTCIPHERS"; + case IEEE80211_PARAM_KEYMGTALGS: + return "KEYMGTALGS"; + case IEEE80211_PARAM_RSNCAPS: + return "RSNCAPS"; + case IEEE80211_PARAM_WPA: + return "WPA"; + case IEEE80211_PARAM_AUTHMODE: + return "AUTHMODE"; + case IEEE80211_PARAM_PRIVACY: + return "PRIVACY"; + case IEEE80211_PARAM_COUNTERMEASURES: + return "COUNTERMEASURES"; + default: + return "??"; + } +} + + +static int +set80211priv(struct madwifi_driver_data *drv, int op, void *data, int len) +{ + struct iwreq iwr; + int do_inline = len < IFNAMSIZ; + + /* Certain ioctls must use the non-inlined method */ + if (op == IEEE80211_IOCTL_SET_APPIEBUF || + op == IEEE80211_IOCTL_FILTERFRAME) + do_inline = 0; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + if (do_inline) { + /* + * Argument data fits inline; put it there. + */ + memcpy(iwr.u.name, data, len); + } else { + /* + * Argument data too big for inline transfer; setup a + * parameter block instead; the kernel will transfer + * the data for the driver. + */ + iwr.u.data.pointer = data; + iwr.u.data.length = len; + } + + if (ioctl(drv->ioctl_sock, op, &iwr) < 0) { + wpa_printf(MSG_DEBUG, "atheros: %s: %s: ioctl op=0x%x " + "(%s) len=%d failed: %d (%s)", + __func__, drv->iface, op, + athr_get_ioctl_name(op), + len, errno, strerror(errno)); + return -1; + } + return 0; +} + +static int +set80211param(struct madwifi_driver_data *drv, int op, int arg) +{ + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.mode = op; + memcpy(iwr.u.name+sizeof(__u32), &arg, sizeof(arg)); + + if (ioctl(drv->ioctl_sock, IEEE80211_IOCTL_SETPARAM, &iwr) < 0) { + perror("ioctl[IEEE80211_IOCTL_SETPARAM]"); + wpa_printf(MSG_DEBUG, "%s: %s: Failed to set parameter (op %d " + "(%s) arg %d)", __func__, drv->iface, op, + athr_get_param_name(op), arg); + return -1; + } + return 0; +} + +#ifndef CONFIG_NO_STDOUT_DEBUG +static const char * +ether_sprintf(const u8 *addr) +{ + static char buf[sizeof(MACSTR)]; + + if (addr != NULL) + snprintf(buf, sizeof(buf), MACSTR, MAC2STR(addr)); + else + snprintf(buf, sizeof(buf), MACSTR, 0,0,0,0,0,0); + return buf; +} +#endif /* CONFIG_NO_STDOUT_DEBUG */ + +/* + * Configure WPA parameters. + */ +static int +madwifi_configure_wpa(struct madwifi_driver_data *drv, + struct wpa_bss_params *params) +{ + int v; + + switch (params->wpa_group) { + case WPA_CIPHER_CCMP: + v = IEEE80211_CIPHER_AES_CCM; + break; + case WPA_CIPHER_TKIP: + v = IEEE80211_CIPHER_TKIP; + break; + case WPA_CIPHER_WEP104: + v = IEEE80211_CIPHER_WEP; + break; + case WPA_CIPHER_WEP40: + v = IEEE80211_CIPHER_WEP; + break; + case WPA_CIPHER_NONE: + v = IEEE80211_CIPHER_NONE; + break; + default: + wpa_printf(MSG_ERROR, "Unknown group key cipher %u", + params->wpa_group); + return -1; + } + wpa_printf(MSG_DEBUG, "%s: group key cipher=%d", __func__, v); + if (set80211param(drv, IEEE80211_PARAM_MCASTCIPHER, v)) { + printf("Unable to set group key cipher to %u\n", v); + return -1; + } + if (v == IEEE80211_CIPHER_WEP) { + /* key length is done only for specific ciphers */ + v = (params->wpa_group == WPA_CIPHER_WEP104 ? 13 : 5); + if (set80211param(drv, IEEE80211_PARAM_MCASTKEYLEN, v)) { + printf("Unable to set group key length to %u\n", v); + return -1; + } + } + + v = 0; + if (params->wpa_pairwise & WPA_CIPHER_CCMP) + v |= 1<<IEEE80211_CIPHER_AES_CCM; + if (params->wpa_pairwise & WPA_CIPHER_TKIP) + v |= 1<<IEEE80211_CIPHER_TKIP; + if (params->wpa_pairwise & WPA_CIPHER_NONE) + v |= 1<<IEEE80211_CIPHER_NONE; + wpa_printf(MSG_DEBUG, "%s: pairwise key ciphers=0x%x", __func__, v); + if (set80211param(drv, IEEE80211_PARAM_UCASTCIPHERS, v)) { + printf("Unable to set pairwise key ciphers to 0x%x\n", v); + return -1; + } + + wpa_printf(MSG_DEBUG, "%s: key management algorithms=0x%x", + __func__, params->wpa_key_mgmt); + if (set80211param(drv, IEEE80211_PARAM_KEYMGTALGS, + params->wpa_key_mgmt)) { + printf("Unable to set key management algorithms to 0x%x\n", + params->wpa_key_mgmt); + return -1; + } + + v = 0; + if (params->rsn_preauth) + v |= BIT(0); + wpa_printf(MSG_DEBUG, "%s: rsn capabilities=0x%x", + __func__, params->rsn_preauth); + if (set80211param(drv, IEEE80211_PARAM_RSNCAPS, v)) { + printf("Unable to set RSN capabilities to 0x%x\n", v); + return -1; + } + + wpa_printf(MSG_DEBUG, "%s: enable WPA=0x%x", __func__, params->wpa); + if (set80211param(drv, IEEE80211_PARAM_WPA, params->wpa)) { + printf("Unable to set WPA to %u\n", params->wpa); + return -1; + } + return 0; +} + +static int +madwifi_set_ieee8021x(void *priv, struct wpa_bss_params *params) +{ + struct madwifi_driver_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, params->enabled); + + if (!params->enabled) { + /* XXX restore state */ + if (set80211param(priv, IEEE80211_PARAM_AUTHMODE, + IEEE80211_AUTH_AUTO) < 0) + return -1; + /* IEEE80211_AUTH_AUTO ends up enabling Privacy; clear that */ + return madwifi_set_privacy(drv, 0); + } + if (!params->wpa && !params->ieee802_1x) { + hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER, + HOSTAPD_LEVEL_WARNING, "No 802.1X or WPA enabled!"); + return -1; + } + if (params->wpa && madwifi_configure_wpa(drv, params) != 0) { + hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER, + HOSTAPD_LEVEL_WARNING, "Error configuring WPA state!"); + return -1; + } + if (set80211param(priv, IEEE80211_PARAM_AUTHMODE, + (params->wpa ? IEEE80211_AUTH_WPA : IEEE80211_AUTH_8021X))) { + hostapd_logger(drv->hapd, NULL, HOSTAPD_MODULE_DRIVER, + HOSTAPD_LEVEL_WARNING, "Error enabling WPA/802.1X!"); + return -1; + } + + return 0; +} + +static int +madwifi_set_privacy(void *priv, int enabled) +{ + struct madwifi_driver_data *drv = priv; + + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); + + return set80211param(drv, IEEE80211_PARAM_PRIVACY, enabled); +} + +static int +madwifi_set_sta_authorized(void *priv, const u8 *addr, int authorized) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s authorized=%d", + __func__, ether_sprintf(addr), authorized); + + if (authorized) + mlme.im_op = IEEE80211_MLME_AUTHORIZE; + else + mlme.im_op = IEEE80211_MLME_UNAUTHORIZE; + mlme.im_reason = 0; + memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to %sauthorize STA " MACSTR, + __func__, authorized ? "" : "un", MAC2STR(addr)); + } + + return ret; +} + +static int +madwifi_sta_set_flags(void *priv, const u8 *addr, + int total_flags, int flags_or, int flags_and) +{ + /* For now, only support setting Authorized flag */ + if (flags_or & WPA_STA_AUTHORIZED) + return madwifi_set_sta_authorized(priv, addr, 1); + if (!(flags_and & WPA_STA_AUTHORIZED)) + return madwifi_set_sta_authorized(priv, addr, 0); + return 0; +} + +static int +madwifi_del_key(void *priv, const u8 *addr, int key_idx) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_del_key wk; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s key_idx=%d", + __func__, ether_sprintf(addr), key_idx); + + memset(&wk, 0, sizeof(wk)); + if (addr != NULL) { + memcpy(wk.idk_macaddr, addr, IEEE80211_ADDR_LEN); + wk.idk_keyix = (u8) IEEE80211_KEYIX_NONE; + } else { + wk.idk_keyix = key_idx; + } + + ret = set80211priv(drv, IEEE80211_IOCTL_DELKEY, &wk, sizeof(wk)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to delete key (addr %s" + " key_idx %d)", __func__, ether_sprintf(addr), + key_idx); + } + + return ret; +} + +static int +madwifi_set_key(const char *ifname, void *priv, enum wpa_alg alg, + const u8 *addr, int key_idx, int set_tx, const u8 *seq, + size_t seq_len, const u8 *key, size_t key_len) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_key wk; + u_int8_t cipher; + int ret; + + if (alg == WPA_ALG_NONE) + return madwifi_del_key(drv, addr, key_idx); + + wpa_printf(MSG_DEBUG, "%s: alg=%d addr=%s key_idx=%d", + __func__, alg, ether_sprintf(addr), key_idx); + + switch (alg) { + case WPA_ALG_WEP: + cipher = IEEE80211_CIPHER_WEP; + break; + case WPA_ALG_TKIP: + cipher = IEEE80211_CIPHER_TKIP; + break; + case WPA_ALG_CCMP: + cipher = IEEE80211_CIPHER_AES_CCM; + break; + default: + printf("%s: unknown/unsupported algorithm %d\n", + __func__, alg); + return -1; + } + + if (key_len > sizeof(wk.ik_keydata)) { + printf("%s: key length %lu too big\n", __func__, + (unsigned long) key_len); + return -3; + } + + memset(&wk, 0, sizeof(wk)); + wk.ik_type = cipher; + wk.ik_flags = IEEE80211_KEY_RECV | IEEE80211_KEY_XMIT; + if (addr == NULL) { + memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); + wk.ik_keyix = key_idx; + wk.ik_flags |= IEEE80211_KEY_DEFAULT; + } else { + memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); + wk.ik_keyix = IEEE80211_KEYIX_NONE; + } + wk.ik_keylen = key_len; + memcpy(wk.ik_keydata, key, key_len); + + ret = set80211priv(drv, IEEE80211_IOCTL_SETKEY, &wk, sizeof(wk)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to set key (addr %s" + " key_idx %d alg %d key_len %lu set_tx %d)", + __func__, ether_sprintf(wk.ik_macaddr), key_idx, + alg, (unsigned long) key_len, set_tx); + } + + return ret; +} + + +static int +madwifi_get_seqnum(const char *ifname, void *priv, const u8 *addr, int idx, + u8 *seq) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_key wk; + + wpa_printf(MSG_DEBUG, "%s: addr=%s idx=%d", + __func__, ether_sprintf(addr), idx); + + memset(&wk, 0, sizeof(wk)); + if (addr == NULL) + memset(wk.ik_macaddr, 0xff, IEEE80211_ADDR_LEN); + else + memcpy(wk.ik_macaddr, addr, IEEE80211_ADDR_LEN); + wk.ik_keyix = idx; + + if (set80211priv(drv, IEEE80211_IOCTL_GETKEY, &wk, sizeof(wk))) { + wpa_printf(MSG_DEBUG, "%s: Failed to get encryption data " + "(addr " MACSTR " key_idx %d)", + __func__, MAC2STR(wk.ik_macaddr), idx); + return -1; + } + +#ifdef WORDS_BIGENDIAN + { + /* + * wk.ik_keytsc is in host byte order (big endian), need to + * swap it to match with the byte order used in WPA. + */ + int i; +#ifndef WPA_KEY_RSC_LEN +#define WPA_KEY_RSC_LEN 8 +#endif + u8 tmp[WPA_KEY_RSC_LEN]; + memcpy(tmp, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); + for (i = 0; i < WPA_KEY_RSC_LEN; i++) { + seq[i] = tmp[WPA_KEY_RSC_LEN - i - 1]; + } + } +#else /* WORDS_BIGENDIAN */ + memcpy(seq, &wk.ik_keytsc, sizeof(wk.ik_keytsc)); +#endif /* WORDS_BIGENDIAN */ + return 0; +} + + +static int +madwifi_flush(void *priv) +{ + u8 allsta[IEEE80211_ADDR_LEN]; + memset(allsta, 0xff, IEEE80211_ADDR_LEN); + return madwifi_sta_deauth(priv, NULL, allsta, + IEEE80211_REASON_AUTH_LEAVE); +} + + +static int +madwifi_read_sta_driver_data(void *priv, struct hostap_sta_driver_data *data, + const u8 *addr) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_sta_stats stats; + + memset(data, 0, sizeof(*data)); + + /* + * Fetch statistics for station from the system. + */ + memset(&stats, 0, sizeof(stats)); + memcpy(stats.is_u.macaddr, addr, IEEE80211_ADDR_LEN); + if (set80211priv(drv, IEEE80211_IOCTL_STA_STATS, + &stats, sizeof(stats))) { + wpa_printf(MSG_DEBUG, "%s: Failed to fetch STA stats (addr " + MACSTR ")", __func__, MAC2STR(addr)); + if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) { + memcpy(data, &drv->acct_data, sizeof(*data)); + return 0; + } + + printf("Failed to get station stats information element.\n"); + return -1; + } + + data->rx_packets = stats.is_stats.ns_rx_data; + data->rx_bytes = stats.is_stats.ns_rx_bytes; + data->tx_packets = stats.is_stats.ns_tx_data; + data->tx_bytes = stats.is_stats.ns_tx_bytes; + return 0; +} + + +static int +madwifi_sta_clear_stats(void *priv, const u8 *addr) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s", __func__, ether_sprintf(addr)); + + mlme.im_op = IEEE80211_MLME_CLEAR_STATS; + memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, + sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to clear STA stats (addr " + MACSTR ")", __func__, MAC2STR(addr)); + } + + return ret; +} + + +static int +madwifi_set_opt_ie(void *priv, const u8 *ie, size_t ie_len) +{ + /* + * Do nothing; we setup parameters at startup that define the + * contents of the beacon information element. + */ + return 0; +} + +static int +madwifi_sta_deauth(void *priv, const u8 *own_addr, const u8 *addr, + int reason_code) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d", + __func__, ether_sprintf(addr), reason_code); + + mlme.im_op = IEEE80211_MLME_DEAUTH; + mlme.im_reason = reason_code; + memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to deauth STA (addr " MACSTR + " reason %d)", + __func__, MAC2STR(addr), reason_code); + } + + return ret; +} + +static int +madwifi_sta_disassoc(void *priv, const u8 *own_addr, const u8 *addr, + int reason_code) +{ + struct madwifi_driver_data *drv = priv; + struct ieee80211req_mlme mlme; + int ret; + + wpa_printf(MSG_DEBUG, "%s: addr=%s reason_code=%d", + __func__, ether_sprintf(addr), reason_code); + + mlme.im_op = IEEE80211_MLME_DISASSOC; + mlme.im_reason = reason_code; + memcpy(mlme.im_macaddr, addr, IEEE80211_ADDR_LEN); + ret = set80211priv(drv, IEEE80211_IOCTL_SETMLME, &mlme, sizeof(mlme)); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "%s: Failed to disassoc STA (addr " + MACSTR " reason %d)", + __func__, MAC2STR(addr), reason_code); + } + + return ret; +} + +#ifdef CONFIG_WPS +static void madwifi_raw_receive(void *ctx, const u8 *src_addr, const u8 *buf, + size_t len) +{ + struct madwifi_driver_data *drv = ctx; + const struct ieee80211_mgmt *mgmt; + u16 fc; + union wpa_event_data event; + + /* Send Probe Request information to WPS processing */ + + if (len < IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)) + return; + mgmt = (const struct ieee80211_mgmt *) buf; + + fc = le_to_host16(mgmt->frame_control); + if (WLAN_FC_GET_TYPE(fc) != WLAN_FC_TYPE_MGMT || + WLAN_FC_GET_STYPE(fc) != WLAN_FC_STYPE_PROBE_REQ) + return; + + os_memset(&event, 0, sizeof(event)); + event.rx_probe_req.sa = mgmt->sa; + event.rx_probe_req.ie = mgmt->u.probe_req.variable; + event.rx_probe_req.ie_len = + len - (IEEE80211_HDRLEN + sizeof(mgmt->u.probe_req)); + wpa_supplicant_event(drv->hapd, EVENT_RX_PROBE_REQ, &event); +} +#endif /* CONFIG_WPS */ + +static int madwifi_receive_probe_req(struct madwifi_driver_data *drv) +{ + int ret = 0; +#ifdef CONFIG_WPS + struct ieee80211req_set_filter filt; + + wpa_printf(MSG_DEBUG, "%s Enter", __func__); + filt.app_filterype = IEEE80211_FILTER_TYPE_PROBE_REQ; + + ret = set80211priv(drv, IEEE80211_IOCTL_FILTERFRAME, &filt, + sizeof(struct ieee80211req_set_filter)); + if (ret) + return ret; + + drv->sock_raw = l2_packet_init(drv->iface, NULL, ETH_P_80211_RAW, + madwifi_raw_receive, drv, 1); + if (drv->sock_raw == NULL) + return -1; +#endif /* CONFIG_WPS */ + return ret; +} + +#ifdef CONFIG_WPS +static int +madwifi_set_wps_ie(void *priv, const u8 *ie, size_t len, u32 frametype) +{ + struct madwifi_driver_data *drv = priv; + u8 buf[256]; + struct ieee80211req_getset_appiebuf *beac_ie; + + wpa_printf(MSG_DEBUG, "%s buflen = %lu", __func__, + (unsigned long) len); + + beac_ie = (struct ieee80211req_getset_appiebuf *) buf; + beac_ie->app_frmtype = frametype; + beac_ie->app_buflen = len; + memcpy(&(beac_ie->app_buf[0]), ie, len); + + return set80211priv(drv, IEEE80211_IOCTL_SET_APPIEBUF, beac_ie, + sizeof(struct ieee80211req_getset_appiebuf) + len); +} + +static int +madwifi_set_ap_wps_ie(void *priv, const struct wpabuf *beacon, + const struct wpabuf *proberesp) +{ + if (madwifi_set_wps_ie(priv, beacon ? wpabuf_head(beacon) : NULL, + beacon ? wpabuf_len(beacon) : 0, + IEEE80211_APPIE_FRAME_BEACON)) + return -1; + return madwifi_set_wps_ie(priv, + proberesp ? wpabuf_head(proberesp) : NULL, + proberesp ? wpabuf_len(proberesp): 0, + IEEE80211_APPIE_FRAME_PROBE_RESP); +} +#else /* CONFIG_WPS */ +#define madwifi_set_ap_wps_ie NULL +#endif /* CONFIG_WPS */ + +static void +madwifi_new_sta(struct madwifi_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN]) +{ + struct hostapd_data *hapd = drv->hapd; + struct ieee80211req_wpaie ie; + int ielen = 0; + u8 *iebuf = NULL; + + /* + * Fetch negotiated WPA/RSN parameters from the system. + */ + memset(&ie, 0, sizeof(ie)); + memcpy(ie.wpa_macaddr, addr, IEEE80211_ADDR_LEN); + if (set80211priv(drv, IEEE80211_IOCTL_GETWPAIE, &ie, sizeof(ie))) { + /* + * See ATH_WPS_IE comment in the beginning of the file for a + * possible cause for the failure.. + */ + wpa_printf(MSG_DEBUG, "%s: Failed to get WPA/RSN IE: %s", + __func__, strerror(errno)); + goto no_ie; + } + wpa_hexdump(MSG_MSGDUMP, "madwifi req WPA IE", + ie.wpa_ie, IEEE80211_MAX_OPT_IE); + wpa_hexdump(MSG_MSGDUMP, "madwifi req RSN IE", + ie.rsn_ie, IEEE80211_MAX_OPT_IE); + iebuf = ie.wpa_ie; + /* madwifi seems to return some random data if WPA/RSN IE is not set. + * Assume the IE was not included if the IE type is unknown. */ + if (iebuf[0] != WLAN_EID_VENDOR_SPECIFIC) + iebuf[1] = 0; + if (iebuf[1] == 0 && ie.rsn_ie[1] > 0) { + /* madwifi-ng svn #1453 added rsn_ie. Use it, if wpa_ie was not + * set. This is needed for WPA2. */ + iebuf = ie.rsn_ie; + if (iebuf[0] != WLAN_EID_RSN) + iebuf[1] = 0; + } + + ielen = iebuf[1]; + if (ielen == 0) + iebuf = NULL; + else + ielen += 2; + +no_ie: + drv_event_assoc(hapd, addr, iebuf, ielen); + + if (memcmp(addr, drv->acct_mac, ETH_ALEN) == 0) { + /* Cached accounting data is not valid anymore. */ + memset(drv->acct_mac, 0, ETH_ALEN); + memset(&drv->acct_data, 0, sizeof(drv->acct_data)); + } +} + +static void +madwifi_wireless_event_wireless_custom(struct madwifi_driver_data *drv, + char *custom, char *end) +{ + wpa_printf(MSG_DEBUG, "Custom wireless event: '%s'", custom); + + if (strncmp(custom, "MLME-MICHAELMICFAILURE.indication", 33) == 0) { + char *pos; + u8 addr[ETH_ALEN]; + pos = strstr(custom, "addr="); + if (pos == NULL) { + wpa_printf(MSG_DEBUG, + "MLME-MICHAELMICFAILURE.indication " + "without sender address ignored"); + return; + } + pos += 5; + if (hwaddr_aton(pos, addr) == 0) { + union wpa_event_data data; + os_memset(&data, 0, sizeof(data)); + data.michael_mic_failure.unicast = 1; + data.michael_mic_failure.src = addr; + wpa_supplicant_event(drv->hapd, + EVENT_MICHAEL_MIC_FAILURE, &data); + } else { + wpa_printf(MSG_DEBUG, + "MLME-MICHAELMICFAILURE.indication " + "with invalid MAC address"); + } + } else if (strncmp(custom, "STA-TRAFFIC-STAT", 16) == 0) { + char *key, *value; + u32 val; + key = custom; + while ((key = strchr(key, '\n')) != NULL) { + key++; + value = strchr(key, '='); + if (value == NULL) + continue; + *value++ = '\0'; + val = strtoul(value, NULL, 10); + if (strcmp(key, "mac") == 0) + hwaddr_aton(value, drv->acct_mac); + else if (strcmp(key, "rx_packets") == 0) + drv->acct_data.rx_packets = val; + else if (strcmp(key, "tx_packets") == 0) + drv->acct_data.tx_packets = val; + else if (strcmp(key, "rx_bytes") == 0) + drv->acct_data.rx_bytes = val; + else if (strcmp(key, "tx_bytes") == 0) + drv->acct_data.tx_bytes = val; + key = value; + } +#ifdef CONFIG_WPS + } else if (strncmp(custom, "PUSH-BUTTON.indication", 22) == 0) { + /* Some atheros kernels send push button as a wireless event */ + /* PROBLEM! this event is received for ALL BSSs ... + * so all are enabled for WPS... ugh. + */ + wpa_supplicant_event(drv->hapd, EVENT_WPS_BUTTON_PUSHED, NULL); + } else if (strncmp(custom, "Manage.prob_req ", 16) == 0) { + /* + * Atheros driver uses a hack to pass Probe Request frames as a + * binary data in the custom wireless event. The old way (using + * packet sniffing) didn't work when bridging. + * Format: "Manage.prob_req <frame len>" | zero padding | frame + */ +#define WPS_FRAM_TAG_SIZE 30 /* hardcoded in driver */ + int len = atoi(custom + 16); + if (len < 0 || custom + WPS_FRAM_TAG_SIZE + len > end) { + wpa_printf(MSG_DEBUG, "Invalid Manage.prob_req event " + "length %d", len); + return; + } + madwifi_raw_receive(drv, NULL, + (u8 *) custom + WPS_FRAM_TAG_SIZE, len); +#endif /* CONFIG_WPS */ + } +} + +static void +madwifi_wireless_event_wireless(struct madwifi_driver_data *drv, + char *data, int len) +{ + struct iw_event iwe_buf, *iwe = &iwe_buf; + char *pos, *end, *custom, *buf; + + pos = data; + end = data + len; + + while (pos + IW_EV_LCP_LEN <= end) { + /* Event data may be unaligned, so make a local, aligned copy + * before processing. */ + memcpy(&iwe_buf, pos, IW_EV_LCP_LEN); + wpa_printf(MSG_MSGDUMP, "Wireless event: cmd=0x%x len=%d", + iwe->cmd, iwe->len); + if (iwe->len <= IW_EV_LCP_LEN) + return; + + custom = pos + IW_EV_POINT_LEN; + if (drv->we_version > 18 && + (iwe->cmd == IWEVMICHAELMICFAILURE || + iwe->cmd == IWEVASSOCREQIE || + iwe->cmd == IWEVCUSTOM)) { + /* WE-19 removed the pointer from struct iw_point */ + char *dpos = (char *) &iwe_buf.u.data.length; + int dlen = dpos - (char *) &iwe_buf; + memcpy(dpos, pos + IW_EV_LCP_LEN, + sizeof(struct iw_event) - dlen); + } else { + memcpy(&iwe_buf, pos, sizeof(struct iw_event)); + custom += IW_EV_POINT_OFF; + } + + switch (iwe->cmd) { + case IWEVEXPIRED: + drv_event_disassoc(drv->hapd, + (u8 *) iwe->u.addr.sa_data); + break; + case IWEVREGISTERED: + madwifi_new_sta(drv, (u8 *) iwe->u.addr.sa_data); + break; + case IWEVASSOCREQIE: + /* Driver hack.. Use IWEVASSOCREQIE to bypass + * IWEVCUSTOM size limitations. Need to handle this + * just like IWEVCUSTOM. + */ + case IWEVCUSTOM: + if (custom + iwe->u.data.length > end) + return; + buf = malloc(iwe->u.data.length + 1); + if (buf == NULL) + return; /* XXX */ + memcpy(buf, custom, iwe->u.data.length); + buf[iwe->u.data.length] = '\0'; + madwifi_wireless_event_wireless_custom( + drv, buf, buf + iwe->u.data.length); + free(buf); + break; + } + + pos += iwe->len; + } +} + + +static void +madwifi_wireless_event_rtm_newlink(void *ctx, + struct ifinfomsg *ifi, u8 *buf, size_t len) +{ + struct madwifi_driver_data *drv = ctx; + int attrlen, rta_len; + struct rtattr *attr; + + if (ifi->ifi_index != drv->ifindex) + return; + + attrlen = len; + attr = (struct rtattr *) buf; + + rta_len = RTA_ALIGN(sizeof(struct rtattr)); + while (RTA_OK(attr, attrlen)) { + if (attr->rta_type == IFLA_WIRELESS) { + madwifi_wireless_event_wireless( + drv, ((char *) attr) + rta_len, + attr->rta_len - rta_len); + } + attr = RTA_NEXT(attr, attrlen); + } +} + + +static int +madwifi_get_we_version(struct madwifi_driver_data *drv) +{ + struct iw_range *range; + struct iwreq iwr; + int minlen; + size_t buflen; + + drv->we_version = 0; + + /* + * Use larger buffer than struct iw_range in order to allow the + * structure to grow in the future. + */ + buflen = sizeof(struct iw_range) + 500; + range = os_zalloc(buflen); + if (range == NULL) + return -1; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.data.pointer = (caddr_t) range; + iwr.u.data.length = buflen; + + minlen = ((char *) &range->enc_capa) - (char *) range + + sizeof(range->enc_capa); + + if (ioctl(drv->ioctl_sock, SIOCGIWRANGE, &iwr) < 0) { + perror("ioctl[SIOCGIWRANGE]"); + free(range); + return -1; + } else if (iwr.u.data.length >= minlen && + range->we_version_compiled >= 18) { + wpa_printf(MSG_DEBUG, "SIOCGIWRANGE: WE(compiled)=%d " + "WE(source)=%d enc_capa=0x%x", + range->we_version_compiled, + range->we_version_source, + range->enc_capa); + drv->we_version = range->we_version_compiled; + } + + free(range); + return 0; +} + + +static int +madwifi_wireless_event_init(struct madwifi_driver_data *drv) +{ + struct netlink_config *cfg; + + madwifi_get_we_version(drv); + + cfg = os_zalloc(sizeof(*cfg)); + if (cfg == NULL) + return -1; + cfg->ctx = drv; + cfg->newlink_cb = madwifi_wireless_event_rtm_newlink; + drv->netlink = netlink_init(cfg); + if (drv->netlink == NULL) { + os_free(cfg); + return -1; + } + + return 0; +} + + +static int +madwifi_send_eapol(void *priv, const u8 *addr, const u8 *data, size_t data_len, + int encrypt, const u8 *own_addr) +{ + struct madwifi_driver_data *drv = priv; + unsigned char buf[3000]; + unsigned char *bp = buf; + struct l2_ethhdr *eth; + size_t len; + int status; + + /* + * Prepend the Ethernet header. If the caller left us + * space at the front we could just insert it but since + * we don't know we copy to a local buffer. Given the frequency + * and size of frames this probably doesn't matter. + */ + len = data_len + sizeof(struct l2_ethhdr); + if (len > sizeof(buf)) { + bp = malloc(len); + if (bp == NULL) { + printf("EAPOL frame discarded, cannot malloc temp " + "buffer of size %lu!\n", (unsigned long) len); + return -1; + } + } + eth = (struct l2_ethhdr *) bp; + memcpy(eth->h_dest, addr, ETH_ALEN); + memcpy(eth->h_source, own_addr, ETH_ALEN); + eth->h_proto = host_to_be16(ETH_P_EAPOL); + memcpy(eth+1, data, data_len); + + wpa_hexdump(MSG_MSGDUMP, "TX EAPOL", bp, len); + + status = l2_packet_send(drv->sock_xmit, addr, ETH_P_EAPOL, bp, len); + + if (bp != buf) + free(bp); + return status; +} + +static void +handle_read(void *ctx, const u8 *src_addr, const u8 *buf, size_t len) +{ + struct madwifi_driver_data *drv = ctx; + drv_event_eapol_rx(drv->hapd, src_addr, buf + sizeof(struct l2_ethhdr), + len - sizeof(struct l2_ethhdr)); +} + +static void * +madwifi_init(struct hostapd_data *hapd, struct wpa_init_params *params) +{ + struct madwifi_driver_data *drv; + struct ifreq ifr; + struct iwreq iwr; + char brname[IFNAMSIZ]; + + drv = os_zalloc(sizeof(struct madwifi_driver_data)); + if (drv == NULL) { + printf("Could not allocate memory for madwifi driver data\n"); + return NULL; + } + + drv->hapd = hapd; + drv->ioctl_sock = socket(PF_INET, SOCK_DGRAM, 0); + if (drv->ioctl_sock < 0) { + perror("socket[PF_INET,SOCK_DGRAM]"); + goto bad; + } + memcpy(drv->iface, params->ifname, sizeof(drv->iface)); + + memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->iface, sizeof(ifr.ifr_name)); + if (ioctl(drv->ioctl_sock, SIOCGIFINDEX, &ifr) != 0) { + perror("ioctl(SIOCGIFINDEX)"); + goto bad; + } + drv->ifindex = ifr.ifr_ifindex; + + drv->sock_xmit = l2_packet_init(drv->iface, NULL, ETH_P_EAPOL, + handle_read, drv, 1); + if (drv->sock_xmit == NULL) + goto bad; + if (l2_packet_get_own_addr(drv->sock_xmit, params->own_addr)) + goto bad; + if (params->bridge[0]) { + wpa_printf(MSG_DEBUG, "Configure bridge %s for EAPOL traffic.", + params->bridge[0]); + drv->sock_recv = l2_packet_init(params->bridge[0], NULL, + ETH_P_EAPOL, handle_read, drv, + 1); + if (drv->sock_recv == NULL) + goto bad; + } else if (linux_br_get(brname, drv->iface) == 0) { + wpa_printf(MSG_DEBUG, "Interface in bridge %s; configure for " + "EAPOL receive", brname); + drv->sock_recv = l2_packet_init(brname, NULL, ETH_P_EAPOL, + handle_read, drv, 1); + if (drv->sock_recv == NULL) + goto bad; + } else + drv->sock_recv = drv->sock_xmit; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + + iwr.u.mode = IW_MODE_MASTER; + + if (ioctl(drv->ioctl_sock, SIOCSIWMODE, &iwr) < 0) { + perror("ioctl[SIOCSIWMODE]"); + printf("Could not set interface to master mode!\n"); + goto bad; + } + + /* mark down during setup */ + linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0); + madwifi_set_privacy(drv, 0); /* default to no privacy */ + + madwifi_receive_probe_req(drv); + + if (madwifi_wireless_event_init(drv)) + goto bad; + + return drv; +bad: + if (drv->sock_recv != NULL && drv->sock_recv != drv->sock_xmit) + l2_packet_deinit(drv->sock_recv); + if (drv->sock_xmit != NULL) + l2_packet_deinit(drv->sock_xmit); + if (drv->ioctl_sock >= 0) + close(drv->ioctl_sock); + if (drv != NULL) + free(drv); + return NULL; +} + + +static void +madwifi_deinit(void *priv) +{ + struct madwifi_driver_data *drv = priv; + + netlink_deinit(drv->netlink); + (void) linux_set_iface_flags(drv->ioctl_sock, drv->iface, 0); + if (drv->ioctl_sock >= 0) + close(drv->ioctl_sock); + if (drv->sock_recv != NULL && drv->sock_recv != drv->sock_xmit) + l2_packet_deinit(drv->sock_recv); + if (drv->sock_xmit != NULL) + l2_packet_deinit(drv->sock_xmit); + if (drv->sock_raw) + l2_packet_deinit(drv->sock_raw); + free(drv); +} + +static int +madwifi_set_ssid(void *priv, const u8 *buf, int len) +{ + struct madwifi_driver_data *drv = priv; + struct iwreq iwr; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.essid.flags = 1; /* SSID active */ + iwr.u.essid.pointer = (caddr_t) buf; + iwr.u.essid.length = len + 1; + + if (ioctl(drv->ioctl_sock, SIOCSIWESSID, &iwr) < 0) { + perror("ioctl[SIOCSIWESSID]"); + printf("len=%d\n", len); + return -1; + } + return 0; +} + +static int +madwifi_get_ssid(void *priv, u8 *buf, int len) +{ + struct madwifi_driver_data *drv = priv; + struct iwreq iwr; + int ret = 0; + + memset(&iwr, 0, sizeof(iwr)); + os_strlcpy(iwr.ifr_name, drv->iface, IFNAMSIZ); + iwr.u.essid.pointer = (caddr_t) buf; + iwr.u.essid.length = len; + + if (ioctl(drv->ioctl_sock, SIOCGIWESSID, &iwr) < 0) { + perror("ioctl[SIOCGIWESSID]"); + ret = -1; + } else + ret = iwr.u.essid.length; + + return ret; +} + +static int +madwifi_set_countermeasures(void *priv, int enabled) +{ + struct madwifi_driver_data *drv = priv; + wpa_printf(MSG_DEBUG, "%s: enabled=%d", __FUNCTION__, enabled); + return set80211param(drv, IEEE80211_PARAM_COUNTERMEASURES, enabled); +} + +static int +madwifi_commit(void *priv) +{ + struct madwifi_driver_data *drv = priv; + return linux_set_iface_flags(drv->ioctl_sock, drv->iface, 1); +} + +const struct wpa_driver_ops wpa_driver_atheros_ops = { + .name = "atheros", + .hapd_init = madwifi_init, + .hapd_deinit = madwifi_deinit, + .set_ieee8021x = madwifi_set_ieee8021x, + .set_privacy = madwifi_set_privacy, + .set_key = madwifi_set_key, + .get_seqnum = madwifi_get_seqnum, + .flush = madwifi_flush, + .set_generic_elem = madwifi_set_opt_ie, + .sta_set_flags = madwifi_sta_set_flags, + .read_sta_data = madwifi_read_sta_driver_data, + .hapd_send_eapol = madwifi_send_eapol, + .sta_disassoc = madwifi_sta_disassoc, + .sta_deauth = madwifi_sta_deauth, + .hapd_set_ssid = madwifi_set_ssid, + .hapd_get_ssid = madwifi_get_ssid, + .set_countermeasures = madwifi_set_countermeasures, + .sta_clear_stats = madwifi_sta_clear_stats, + .commit = madwifi_commit, + .set_ap_wps_ie = madwifi_set_ap_wps_ie, +}; diff --git a/contrib/wpa/src/drivers/driver_ndis.c b/contrib/wpa/src/drivers/driver_ndis.c index 0bda20a..9344a99 100644 --- a/contrib/wpa/src/drivers/driver_ndis.c +++ b/contrib/wpa/src/drivers/driver_ndis.c @@ -40,7 +40,7 @@ int close(int fd); #include "common.h" #include "driver.h" #include "eloop.h" -#include "ieee802_11_defs.h" +#include "common/ieee802_11_defs.h" #include "driver_ndis.h" int wpa_driver_register_event_cb(struct wpa_driver_ndis_data *drv); @@ -354,6 +354,47 @@ typedef struct NDIS_802_11_PMKID_CANDIDATE_LIST { #endif /* OID_802_11_CAPABILITY */ +#ifndef OID_DOT11_CURRENT_OPERATION_MODE +/* Native 802.11 OIDs */ +#define OID_DOT11_NDIS_START 0x0D010300 +#define OID_DOT11_CURRENT_OPERATION_MODE (OID_DOT11_NDIS_START + 8) +#define OID_DOT11_SCAN_REQUEST (OID_DOT11_NDIS_START + 11) + +typedef enum _DOT11_BSS_TYPE { + dot11_BSS_type_infrastructure = 1, + dot11_BSS_type_independent = 2, + dot11_BSS_type_any = 3 +} DOT11_BSS_TYPE, * PDOT11_BSS_TYPE; + +typedef UCHAR DOT11_MAC_ADDRESS[6]; +typedef DOT11_MAC_ADDRESS * PDOT11_MAC_ADDRESS; + +typedef enum _DOT11_SCAN_TYPE { + dot11_scan_type_active = 1, + dot11_scan_type_passive = 2, + dot11_scan_type_auto = 3, + dot11_scan_type_forced = 0x80000000 +} DOT11_SCAN_TYPE, * PDOT11_SCAN_TYPE; + +typedef struct _DOT11_SCAN_REQUEST_V2 { + DOT11_BSS_TYPE dot11BSSType; + DOT11_MAC_ADDRESS dot11BSSID; + DOT11_SCAN_TYPE dot11ScanType; + BOOLEAN bRestrictedScan; + ULONG udot11SSIDsOffset; + ULONG uNumOfdot11SSIDs; + BOOLEAN bUseRequestIE; + ULONG uRequestIDsOffset; + ULONG uNumOfRequestIDs; + ULONG uPhyTypeInfosOffset; + ULONG uNumOfPhyTypeInfos; + ULONG uIEsOffset; + ULONG uIEsLength; + UCHAR ucBuffer[1]; +} DOT11_SCAN_REQUEST_V2, * PDOT11_SCAN_REQUEST_V2; + +#endif /* OID_DOT11_CURRENT_OPERATION_MODE */ + #ifdef CONFIG_USE_NDISUIO #ifndef _WIN32_WCE #ifdef __MINGW32_VERSION @@ -699,25 +740,42 @@ static int wpa_driver_ndis_disassociate(void *priv, const u8 *addr, } -static int wpa_driver_ndis_set_wpa(void *priv, int enabled) +static void wpa_driver_ndis_scan_timeout(void *eloop_ctx, void *timeout_ctx) { - wpa_printf(MSG_DEBUG, "%s: enabled=%d", __func__, enabled); - return 0; + wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); + wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); } -static void wpa_driver_ndis_scan_timeout(void *eloop_ctx, void *timeout_ctx) +static int wpa_driver_ndis_scan_native80211( + struct wpa_driver_ndis_data *drv, + struct wpa_driver_scan_params *params) { - wpa_printf(MSG_DEBUG, "Scan timeout - try to get results"); - wpa_supplicant_event(timeout_ctx, EVENT_SCAN_RESULTS, NULL); + DOT11_SCAN_REQUEST_V2 req; + int res; + + os_memset(&req, 0, sizeof(req)); + req.dot11BSSType = dot11_BSS_type_any; + os_memset(req.dot11BSSID, 0xff, ETH_ALEN); + req.dot11ScanType = dot11_scan_type_auto; + res = ndis_set_oid(drv, OID_DOT11_SCAN_REQUEST, (char *) &req, + sizeof(req)); + eloop_cancel_timeout(wpa_driver_ndis_scan_timeout, drv, drv->ctx); + eloop_register_timeout(7, 0, wpa_driver_ndis_scan_timeout, drv, + drv->ctx); + return res; } -static int wpa_driver_ndis_scan(void *priv, const u8 *ssid, size_t ssid_len) +static int wpa_driver_ndis_scan(void *priv, + struct wpa_driver_scan_params *params) { struct wpa_driver_ndis_data *drv = priv; int res; + if (drv->native80211) + return wpa_driver_ndis_scan_native80211(drv, params); + if (!drv->radio_enabled) { wpa_printf(MSG_DEBUG, "NDIS: turning radio on before the first" " scan"); @@ -735,6 +793,25 @@ static int wpa_driver_ndis_scan(void *priv, const u8 *ssid, size_t ssid_len) } +static const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie) +{ + const u8 *end, *pos; + + pos = (const u8 *) (res + 1); + end = pos + res->ie_len; + + while (pos + 1 < end) { + if (pos + 2 + pos[1] > end) + break; + if (pos[0] == ie) + return pos; + pos += 2 + pos[1]; + } + + return NULL; +} + + static struct wpa_scan_res * wpa_driver_ndis_add_scan_ssid( struct wpa_scan_res *r, NDIS_802_11_SSID *ssid) { @@ -913,7 +990,8 @@ static int wpa_driver_ndis_add_wep(struct wpa_driver_ndis_data *drv, } -static int wpa_driver_ndis_set_key(void *priv, wpa_alg alg, const u8 *addr, +static int wpa_driver_ndis_set_key(const char *ifname, void *priv, + enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) @@ -1021,7 +1099,8 @@ wpa_driver_ndis_associate(void *priv, continue; wpa_printf(MSG_DEBUG, "NDIS: Re-setting static WEP " "key %d", i); - wpa_driver_ndis_set_key(drv, WPA_ALG_WEP, bcast, i, + wpa_driver_ndis_set_key(drv->ifname, drv, WPA_ALG_WEP, + bcast, i, i == params->wep_tx_keyidx, NULL, 0, params->wep_key[i], params->wep_key_len[i]); @@ -1029,8 +1108,8 @@ wpa_driver_ndis_associate(void *priv, } if (params->wpa_ie == NULL || params->wpa_ie_len == 0) { - if (params->auth_alg & AUTH_ALG_SHARED_KEY) { - if (params->auth_alg & AUTH_ALG_OPEN_SYSTEM) + if (params->auth_alg & WPA_AUTH_ALG_SHARED) { + if (params->auth_alg & WPA_AUTH_ALG_OPEN) auth_mode = Ndis802_11AuthModeAutoSwitch; else auth_mode = Ndis802_11AuthModeShared; @@ -2802,16 +2881,31 @@ static void * wpa_driver_ndis_init(void *ctx, const char *ifname) mode = Ndis802_11Infrastructure; if (ndis_set_oid(drv, OID_802_11_INFRASTRUCTURE_MODE, (char *) &mode, sizeof(mode)) < 0) { + char buf[8]; + int res; wpa_printf(MSG_DEBUG, "NDIS: Failed to set " "OID_802_11_INFRASTRUCTURE_MODE (%d)", (int) mode); /* Try to continue anyway */ - if (!drv->has_capability && drv->capa.enc == 0) { + res = ndis_get_oid(drv, OID_DOT11_CURRENT_OPERATION_MODE, buf, + sizeof(buf)); + if (res > 0) { + wpa_printf(MSG_INFO, "NDIS: The driver seems to use " + "Native 802.11 OIDs. These are not yet " + "fully supported."); + drv->native80211 = 1; + } else if (!drv->has_capability || drv->capa.enc == 0) { + /* + * Note: This will also happen with NDIS 6 drivers with + * Vista. + */ wpa_printf(MSG_DEBUG, "NDIS: Driver did not provide " "any wireless capabilities - assume it is " "a wired interface"); drv->wired = 1; + drv->capa.flags |= WPA_DRIVER_FLAGS_WIRED; + drv->has_capability = 1; ndis_add_multicast(drv); } } @@ -3097,19 +3191,14 @@ const struct wpa_driver_ops wpa_driver_ndis_ops = { "Windows NDIS driver", wpa_driver_ndis_get_bssid, wpa_driver_ndis_get_ssid, - wpa_driver_ndis_set_wpa, wpa_driver_ndis_set_key, wpa_driver_ndis_init, wpa_driver_ndis_deinit, NULL /* set_param */, NULL /* set_countermeasures */, - NULL /* set_drop_unencrypted */, - wpa_driver_ndis_scan, - NULL /* get_scan_results */, wpa_driver_ndis_deauthenticate, wpa_driver_ndis_disassociate, wpa_driver_ndis_associate, - NULL /* set_auth_alg */, wpa_driver_ndis_add_pmkid, wpa_driver_ndis_remove_pmkid, wpa_driver_ndis_flush_pmkid, @@ -3130,11 +3219,61 @@ const struct wpa_driver_ops wpa_driver_ndis_ops = { NULL /* update_ft_ies */, NULL /* send_ft_action */, wpa_driver_ndis_get_scan_results, - NULL /* set_probe_req_ie */, - NULL /* set_mode */, NULL /* set_country */, NULL /* global_init */, NULL /* global_deinit */, NULL /* init2 */, - wpa_driver_ndis_get_interfaces + wpa_driver_ndis_get_interfaces, + wpa_driver_ndis_scan, + NULL /* authenticate */, + NULL /* set_beacon */, + NULL /* hapd_init */, + NULL /* hapd_deinit */, + NULL /* set_ieee8021x */, + NULL /* set_privacy */, + NULL /* get_seqnum */, + NULL /* flush */, + NULL /* set_generic_elem */, + NULL /* read_sta_data */, + NULL /* hapd_send_eapol */, + NULL /* sta_deauth */, + NULL /* sta_disassoc */, + NULL /* sta_remove */, + NULL /* hapd_get_ssid */, + NULL /* hapd_set_ssid */, + NULL /* hapd_set_countermeasures */, + NULL /* sta_add */, + NULL /* get_inact_sec */, + NULL /* sta_clear_stats */, + NULL /* set_freq */, + NULL /* set_rts */, + NULL /* set_frag */, + NULL /* sta_set_flags */, + NULL /* set_rate_sets */, + NULL /* set_cts_protect */, + NULL /* set_preamble */, + NULL /* set_short_slot_time */, + NULL /* set_tx_queue_params */, + NULL /* valid_bss_mask */, + NULL /* if_add */, + NULL /* if_remove */, + NULL /* set_sta_vlan */, + NULL /* commit */, + NULL /* send_ether */, + NULL /* set_radius_acl_auth */, + NULL /* set_radius_acl_expire */, + NULL /* set_ht_params */, + NULL /* set_ap_wps_ie */, + NULL /* set_supp_port */, + NULL /* set_wds_sta */, + NULL /* send_action */, + NULL /* remain_on_channel */, + NULL /* cancel_remain_on_channel */, + NULL /* probe_req_report */, + NULL /* disable_11b_rates */, + NULL /* deinit_ap */, + NULL /* suspend */, + NULL /* resume */, + NULL /* signal_monitor */, + NULL /* send_frame */ }; diff --git a/contrib/wpa/src/drivers/driver_ndis.h b/contrib/wpa/src/drivers/driver_ndis.h index cdce4ba..f263f0e 100644 --- a/contrib/wpa/src/drivers/driver_ndis.h +++ b/contrib/wpa/src/drivers/driver_ndis.h @@ -52,6 +52,7 @@ struct wpa_driver_ndis_data { struct ndis_pmkid_entry *pmkid; char *adapter_desc; int wired; + int native80211; int mode; int wzc_disabled; int oid_bssid_set; diff --git a/contrib/wpa/src/drivers/driver_ndiswrapper.c b/contrib/wpa/src/drivers/driver_ndiswrapper.c index b5c534a..cd2f61e 100644 --- a/contrib/wpa/src/drivers/driver_ndiswrapper.c +++ b/contrib/wpa/src/drivers/driver_ndiswrapper.c @@ -33,9 +33,8 @@ struct wpa_driver_ndiswrapper_data { }; -struct wpa_key -{ - wpa_alg alg; +struct wpa_key { + enum wpa_alg alg; const u8 *addr; int key_index; int set_tx; @@ -45,17 +44,16 @@ struct wpa_key size_t key_len; }; -struct wpa_assoc_info -{ +struct wpa_assoc_info { const u8 *bssid; const u8 *ssid; size_t ssid_len; int freq; const u8 *wpa_ie; size_t wpa_ie_len; - wpa_cipher pairwise_suite; - wpa_cipher group_suite; - wpa_key_mgmt key_mgmt_suite; + enum wpa_cipher pairwise_suite; + enum wpa_cipher group_suite; + enum wpa_key_mgmt key_mgmt_suite; int auth_alg; int mode; }; @@ -73,6 +71,8 @@ struct wpa_assoc_info #define WPA_DEINIT SIOCIWFIRSTPRIV+10 #define WPA_GET_CAPA SIOCIWFIRSTPRIV+11 +static int wpa_ndiswrapper_set_auth_alg(void *priv, int auth_alg); + static int get_socket(void) { static const int families[] = { @@ -111,7 +111,8 @@ static int wpa_ndiswrapper_set_wpa(void *priv, int enabled) return ret; } -static int wpa_ndiswrapper_set_key(void *priv, wpa_alg alg, const u8 *addr, +static int wpa_ndiswrapper_set_key(const char *ifname, void *priv, + enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) @@ -146,8 +147,8 @@ static int wpa_ndiswrapper_set_key(void *priv, wpa_alg alg, const u8 *addr, * did not associate. Try to make sure the keys are cleared so * that plaintext APs can be used in all cases. */ - wpa_driver_wext_set_key(drv->wext, alg, addr, key_idx, set_tx, - seq, seq_len, key, key_len); + wpa_driver_wext_set_key(ifname, drv->wext, alg, addr, key_idx, + set_tx, seq, seq_len, key, key_len); } return ret; @@ -223,6 +224,12 @@ wpa_ndiswrapper_associate(void *priv, struct wpa_assoc_info wpa_assoc_info; struct iwreq priv_req; + if (wpa_ndiswrapper_set_drop_unencrypted(drv, + params->drop_unencrypted) < 0) + ret = -1; + if (wpa_ndiswrapper_set_auth_alg(drv, params->auth_alg) < 0) + ret = -1; + os_memset(&priv_req, 0, sizeof(priv_req)); os_memset(&wpa_assoc_info, 0, sizeof(wpa_assoc_info)); @@ -274,10 +281,11 @@ static int wpa_ndiswrapper_get_ssid(void *priv, u8 *ssid) } -static int wpa_ndiswrapper_scan(void *priv, const u8 *ssid, size_t ssid_len) +static int wpa_ndiswrapper_scan(void *priv, + struct wpa_driver_scan_params *params) { struct wpa_driver_ndiswrapper_data *drv = priv; - return wpa_driver_wext_scan(drv->wext, ssid, ssid_len); + return wpa_driver_wext_scan(drv->wext, params); } @@ -334,6 +342,8 @@ static void * wpa_ndiswrapper_init(void *ctx, const char *ifname) return NULL; } + wpa_ndiswrapper_set_wpa(drv, 1); + return drv; } @@ -341,6 +351,7 @@ static void * wpa_ndiswrapper_init(void *ctx, const char *ifname) static void wpa_ndiswrapper_deinit(void *priv) { struct wpa_driver_ndiswrapper_data *drv = priv; + wpa_ndiswrapper_set_wpa(drv, 0); wpa_driver_wext_deinit(drv->wext); close(drv->sock); os_free(drv); @@ -350,18 +361,15 @@ static void wpa_ndiswrapper_deinit(void *priv) const struct wpa_driver_ops wpa_driver_ndiswrapper_ops = { .name = "ndiswrapper", .desc = "Linux ndiswrapper (deprecated; use wext)", - .set_wpa = wpa_ndiswrapper_set_wpa, .set_key = wpa_ndiswrapper_set_key, .set_countermeasures = wpa_ndiswrapper_set_countermeasures, - .set_drop_unencrypted = wpa_ndiswrapper_set_drop_unencrypted, .deauthenticate = wpa_ndiswrapper_deauthenticate, .disassociate = wpa_ndiswrapper_disassociate, .associate = wpa_ndiswrapper_associate, - .set_auth_alg = wpa_ndiswrapper_set_auth_alg, .get_bssid = wpa_ndiswrapper_get_bssid, .get_ssid = wpa_ndiswrapper_get_ssid, - .scan = wpa_ndiswrapper_scan, + .scan2 = wpa_ndiswrapper_scan, .get_scan_results2 = wpa_ndiswrapper_get_scan_results, .init = wpa_ndiswrapper_init, .deinit = wpa_ndiswrapper_deinit, diff --git a/contrib/wpa/src/drivers/driver_none.c b/contrib/wpa/src/drivers/driver_none.c new file mode 100644 index 0000000..aaeacd6 --- /dev/null +++ b/contrib/wpa/src/drivers/driver_none.c @@ -0,0 +1,99 @@ +/* + * Driver interface for RADIUS server or WPS ER only (no driver) + * Copyright (c) 2008, Atheros Communications + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "driver.h" + + +struct none_driver_data { + struct hostapd_data *hapd; + void *ctx; +}; + + +static void * none_driver_hapd_init(struct hostapd_data *hapd, + struct wpa_init_params *params) +{ + struct none_driver_data *drv; + + drv = os_zalloc(sizeof(struct none_driver_data)); + if (drv == NULL) { + wpa_printf(MSG_ERROR, "Could not allocate memory for none " + "driver data"); + return NULL; + } + drv->hapd = hapd; + + return drv; +} + + +static void none_driver_hapd_deinit(void *priv) +{ + struct none_driver_data *drv = priv; + + os_free(drv); +} + + +static int none_driver_send_ether(void *priv, const u8 *dst, const u8 *src, + u16 proto, const u8 *data, size_t data_len) +{ + return 0; +} + + +static void * none_driver_init(void *ctx, const char *ifname) +{ + struct none_driver_data *drv; + + drv = os_zalloc(sizeof(struct none_driver_data)); + if (drv == NULL) { + wpa_printf(MSG_ERROR, "Could not allocate memory for none " + "driver data"); + return NULL; + } + drv->ctx = ctx; + + return drv; +} + + +static void none_driver_deinit(void *priv) +{ + struct none_driver_data *drv = priv; + + os_free(drv); +} + + +static int none_driver_send_eapol(void *priv, const u8 *dest, u16 proto, + const u8 *data, size_t data_len) +{ + return -1; +} + + +const struct wpa_driver_ops wpa_driver_none_ops = { + .name = "none", + .desc = "no driver (RADIUS server/WPS ER)", + .hapd_init = none_driver_hapd_init, + .hapd_deinit = none_driver_hapd_deinit, + .send_ether = none_driver_send_ether, + .init = none_driver_init, + .deinit = none_driver_deinit, + .send_eapol = none_driver_send_eapol, +}; diff --git a/contrib/wpa/src/drivers/driver_wired.c b/contrib/wpa/src/drivers/driver_wired.c index 098991a..2b197f0 100644 --- a/contrib/wpa/src/drivers/driver_wired.c +++ b/contrib/wpa/src/drivers/driver_wired.c @@ -1,6 +1,7 @@ /* - * WPA Supplicant - wired Ethernet driver interface - * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi> + * Wired Ethernet driver interface + * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004, Gunter Burchardt <tira@isx.de> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -17,27 +18,374 @@ #include <net/if.h> #ifdef __linux__ #include <netpacket/packet.h> +#include <net/if_arp.h> +#include <net/if.h> #endif /* __linux__ */ -#if defined(__FreeBSD__) || defined(__DragonFly__) +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) #include <net/if_dl.h> -#endif /* defined(__FreeBSD__) || defined(__DragonFly__) */ +#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) */ #include "common.h" +#include "eloop.h" #include "driver.h" +#ifdef _MSC_VER +#pragma pack(push, 1) +#endif /* _MSC_VER */ + +struct ieee8023_hdr { + u8 dest[6]; + u8 src[6]; + u16 ethertype; +} STRUCT_PACKED; + +#ifdef _MSC_VER +#pragma pack(pop) +#endif /* _MSC_VER */ static const u8 pae_group_addr[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; struct wpa_driver_wired_data { + char ifname[IFNAMSIZ + 1]; void *ctx; + + int sock; /* raw packet socket for driver access */ + int dhcp_sock; /* socket for dhcp packets */ + int use_pae_group_addr; + int pf_sock; - char ifname[IFNAMSIZ + 1]; int membership, multi, iff_allmulti, iff_up; }; +/* TODO: detecting new devices should eventually be changed from using DHCP + * snooping to trigger on any packet from a new layer 2 MAC address, e.g., + * based on ebtables, etc. */ + +struct dhcp_message { + u_int8_t op; + u_int8_t htype; + u_int8_t hlen; + u_int8_t hops; + u_int32_t xid; + u_int16_t secs; + u_int16_t flags; + u_int32_t ciaddr; + u_int32_t yiaddr; + u_int32_t siaddr; + u_int32_t giaddr; + u_int8_t chaddr[16]; + u_int8_t sname[64]; + u_int8_t file[128]; + u_int32_t cookie; + u_int8_t options[308]; /* 312 - cookie */ +}; + + +static int wired_multicast_membership(int sock, int ifindex, + const u8 *addr, int add) +{ +#ifdef __linux__ + struct packet_mreq mreq; + + if (sock < 0) + return -1; + + os_memset(&mreq, 0, sizeof(mreq)); + mreq.mr_ifindex = ifindex; + mreq.mr_type = PACKET_MR_MULTICAST; + mreq.mr_alen = ETH_ALEN; + os_memcpy(mreq.mr_address, addr, ETH_ALEN); + + if (setsockopt(sock, SOL_PACKET, + add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP, + &mreq, sizeof(mreq)) < 0) { + perror("setsockopt"); + return -1; + } + return 0; +#else /* __linux__ */ + return -1; +#endif /* __linux__ */ +} + + +#ifdef __linux__ +static void handle_data(void *ctx, unsigned char *buf, size_t len) +{ +#ifdef HOSTAPD + struct ieee8023_hdr *hdr; + u8 *pos, *sa; + size_t left; + union wpa_event_data event; + + /* must contain at least ieee8023_hdr 6 byte source, 6 byte dest, + * 2 byte ethertype */ + if (len < 14) { + wpa_printf(MSG_MSGDUMP, "handle_data: too short (%lu)", + (unsigned long) len); + return; + } + + hdr = (struct ieee8023_hdr *) buf; + + switch (ntohs(hdr->ethertype)) { + case ETH_P_PAE: + wpa_printf(MSG_MSGDUMP, "Received EAPOL packet"); + sa = hdr->src; + os_memset(&event, 0, sizeof(event)); + event.new_sta.addr = sa; + wpa_supplicant_event(ctx, EVENT_NEW_STA, &event); + + pos = (u8 *) (hdr + 1); + left = len - sizeof(*hdr); + drv_event_eapol_rx(ctx, sa, pos, left); + break; + + default: + wpa_printf(MSG_DEBUG, "Unknown ethertype 0x%04x in data frame", + ntohs(hdr->ethertype)); + break; + } +#endif /* HOSTAPD */ +} + + +static void handle_read(int sock, void *eloop_ctx, void *sock_ctx) +{ + int len; + unsigned char buf[3000]; + + len = recv(sock, buf, sizeof(buf), 0); + if (len < 0) { + perror("recv"); + return; + } + + handle_data(eloop_ctx, buf, len); +} + + +static void handle_dhcp(int sock, void *eloop_ctx, void *sock_ctx) +{ + int len; + unsigned char buf[3000]; + struct dhcp_message *msg; + u8 *mac_address; + union wpa_event_data event; + + len = recv(sock, buf, sizeof(buf), 0); + if (len < 0) { + perror("recv"); + return; + } + + /* must contain at least dhcp_message->chaddr */ + if (len < 44) { + wpa_printf(MSG_MSGDUMP, "handle_dhcp: too short (%d)", len); + return; + } + + msg = (struct dhcp_message *) buf; + mac_address = (u8 *) &(msg->chaddr); + + wpa_printf(MSG_MSGDUMP, "Got DHCP broadcast packet from " MACSTR, + MAC2STR(mac_address)); + + os_memset(&event, 0, sizeof(event)); + event.new_sta.addr = mac_address; + wpa_supplicant_event(eloop_ctx, EVENT_NEW_STA, &event); +} +#endif /* __linux__ */ + + +static int wired_init_sockets(struct wpa_driver_wired_data *drv, u8 *own_addr) +{ +#ifdef __linux__ + struct ifreq ifr; + struct sockaddr_ll addr; + struct sockaddr_in addr2; + int n = 1; + + drv->sock = socket(PF_PACKET, SOCK_RAW, htons(ETH_P_PAE)); + if (drv->sock < 0) { + perror("socket[PF_PACKET,SOCK_RAW]"); + return -1; + } + + if (eloop_register_read_sock(drv->sock, handle_read, drv->ctx, NULL)) { + printf("Could not register read socket\n"); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); + if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) { + perror("ioctl(SIOCGIFINDEX)"); + return -1; + } + + os_memset(&addr, 0, sizeof(addr)); + addr.sll_family = AF_PACKET; + addr.sll_ifindex = ifr.ifr_ifindex; + wpa_printf(MSG_DEBUG, "Opening raw packet socket for ifindex %d", + addr.sll_ifindex); + + if (bind(drv->sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("bind"); + return -1; + } + + /* filter multicast address */ + if (wired_multicast_membership(drv->sock, ifr.ifr_ifindex, + pae_group_addr, 1) < 0) { + wpa_printf(MSG_ERROR, "wired: Failed to add multicast group " + "membership"); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, drv->ifname, sizeof(ifr.ifr_name)); + if (ioctl(drv->sock, SIOCGIFHWADDR, &ifr) != 0) { + perror("ioctl(SIOCGIFHWADDR)"); + return -1; + } + + if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { + printf("Invalid HW-addr family 0x%04x\n", + ifr.ifr_hwaddr.sa_family); + return -1; + } + os_memcpy(own_addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); + + /* setup dhcp listen socket for sta detection */ + if ((drv->dhcp_sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { + perror("socket call failed for dhcp"); + return -1; + } + + if (eloop_register_read_sock(drv->dhcp_sock, handle_dhcp, drv->ctx, + NULL)) { + printf("Could not register read socket\n"); + return -1; + } + + os_memset(&addr2, 0, sizeof(addr2)); + addr2.sin_family = AF_INET; + addr2.sin_port = htons(67); + addr2.sin_addr.s_addr = INADDR_ANY; + + if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_REUSEADDR, (char *) &n, + sizeof(n)) == -1) { + perror("setsockopt[SOL_SOCKET,SO_REUSEADDR]"); + return -1; + } + if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BROADCAST, (char *) &n, + sizeof(n)) == -1) { + perror("setsockopt[SOL_SOCKET,SO_BROADCAST]"); + return -1; + } + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_ifrn.ifrn_name, drv->ifname, IFNAMSIZ); + if (setsockopt(drv->dhcp_sock, SOL_SOCKET, SO_BINDTODEVICE, + (char *) &ifr, sizeof(ifr)) < 0) { + perror("setsockopt[SOL_SOCKET,SO_BINDTODEVICE]"); + return -1; + } + + if (bind(drv->dhcp_sock, (struct sockaddr *) &addr2, + sizeof(struct sockaddr)) == -1) { + perror("bind"); + return -1; + } + + return 0; +#else /* __linux__ */ + return -1; +#endif /* __linux__ */ +} + + +static int wired_send_eapol(void *priv, const u8 *addr, + const u8 *data, size_t data_len, int encrypt, + const u8 *own_addr) +{ + struct wpa_driver_wired_data *drv = priv; + struct ieee8023_hdr *hdr; + size_t len; + u8 *pos; + int res; + + len = sizeof(*hdr) + data_len; + hdr = os_zalloc(len); + if (hdr == NULL) { + printf("malloc() failed for wired_send_eapol(len=%lu)\n", + (unsigned long) len); + return -1; + } + + os_memcpy(hdr->dest, drv->use_pae_group_addr ? pae_group_addr : addr, + ETH_ALEN); + os_memcpy(hdr->src, own_addr, ETH_ALEN); + hdr->ethertype = htons(ETH_P_PAE); + + pos = (u8 *) (hdr + 1); + os_memcpy(pos, data, data_len); + + res = send(drv->sock, (u8 *) hdr, len, 0); + os_free(hdr); + + if (res < 0) { + perror("wired_send_eapol: send"); + printf("wired_send_eapol - packet len: %lu - failed\n", + (unsigned long) len); + } + + return res; +} + + +static void * wired_driver_hapd_init(struct hostapd_data *hapd, + struct wpa_init_params *params) +{ + struct wpa_driver_wired_data *drv; + + drv = os_zalloc(sizeof(struct wpa_driver_wired_data)); + if (drv == NULL) { + printf("Could not allocate memory for wired driver data\n"); + return NULL; + } + + drv->ctx = hapd; + os_strlcpy(drv->ifname, params->ifname, sizeof(drv->ifname)); + drv->use_pae_group_addr = params->use_pae_group_addr; + + if (wired_init_sockets(drv, params->own_addr)) { + os_free(drv); + return NULL; + } + + return drv; +} + + +static void wired_driver_hapd_deinit(void *priv) +{ + struct wpa_driver_wired_data *drv = priv; + + if (drv->sock >= 0) + close(drv->sock); + + if (drv->dhcp_sock >= 0) + close(drv->dhcp_sock); + + os_free(drv); +} + + static int wpa_driver_wired_get_ssid(void *priv, u8 *ssid) { ssid[0] = 0; @@ -53,6 +401,14 @@ static int wpa_driver_wired_get_bssid(void *priv, u8 *bssid) } +static int wpa_driver_wired_get_capa(void *priv, struct wpa_driver_capa *capa) +{ + os_memset(capa, 0, sizeof(*capa)); + capa->flags = WPA_DRIVER_FLAGS_WIRED; + return 0; +} + + static int wpa_driver_wired_get_ifflags(const char *ifname, int *flags) { struct ifreq ifr; @@ -118,7 +474,7 @@ static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add) ifr.ifr_hwaddr.sa_family = AF_UNSPEC; os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN); #endif /* __linux__ */ -#if defined(__FreeBSD__) || defined(__DragonFly__) +#if defined(__FreeBSD__) || defined(__DragonFly__) || defined(__FreeBSD_kernel__) { struct sockaddr_dl *dlp; dlp = (struct sockaddr_dl *) &ifr.ifr_addr; @@ -128,9 +484,9 @@ static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add) dlp->sdl_nlen = 0; dlp->sdl_alen = ETH_ALEN; dlp->sdl_slen = 0; - os_memcpy(LLADDR(dlp), addr, ETH_ALEN); + os_memcpy(LLADDR(dlp), addr, ETH_ALEN); } -#endif /* defined(__FreeBSD__) || defined(__DragonFly__) */ +#endif /* defined(__FreeBSD__) || defined(__DragonFly__) || defined(FreeBSD_kernel__) */ #if defined(__NetBSD__) || defined(__OpenBSD__) || defined(__APPLE__) { struct sockaddr *sap; @@ -151,34 +507,6 @@ static int wpa_driver_wired_multi(const char *ifname, const u8 *addr, int add) } -static int wpa_driver_wired_membership(struct wpa_driver_wired_data *drv, - const u8 *addr, int add) -{ -#ifdef __linux__ - struct packet_mreq mreq; - - if (drv->pf_sock == -1) - return -1; - - os_memset(&mreq, 0, sizeof(mreq)); - mreq.mr_ifindex = if_nametoindex(drv->ifname); - mreq.mr_type = PACKET_MR_MULTICAST; - mreq.mr_alen = ETH_ALEN; - os_memcpy(mreq.mr_address, addr, ETH_ALEN); - - if (setsockopt(drv->pf_sock, SOL_PACKET, - add ? PACKET_ADD_MEMBERSHIP : PACKET_DROP_MEMBERSHIP, - &mreq, sizeof(mreq)) < 0) { - perror("setsockopt"); - return -1; - } - return 0; -#else /* __linux__ */ - return -1; -#endif /* __linux__ */ -} - - static void * wpa_driver_wired_init(void *ctx, const char *ifname) { struct wpa_driver_wired_data *drv; @@ -195,16 +523,18 @@ static void * wpa_driver_wired_init(void *ctx, const char *ifname) if (drv->pf_sock < 0) perror("socket(PF_PACKET)"); #else /* __linux__ */ - drv->pf_sock = -1; + drv->pf_sock = -1; #endif /* __linux__ */ - + if (wpa_driver_wired_get_ifflags(ifname, &flags) == 0 && !(flags & IFF_UP) && wpa_driver_wired_set_ifflags(ifname, flags | IFF_UP) == 0) { drv->iff_up = 1; } - if (wpa_driver_wired_membership(drv, pae_group_addr, 1) == 0) { + if (wired_multicast_membership(drv->pf_sock, + if_nametoindex(drv->ifname), + pae_group_addr, 1) == 0) { wpa_printf(MSG_DEBUG, "%s: Added multicast membership with " "packet socket", __func__); drv->membership = 1; @@ -242,7 +572,9 @@ static void wpa_driver_wired_deinit(void *priv) int flags; if (drv->membership && - wpa_driver_wired_membership(drv, pae_group_addr, 0) < 0) { + wired_multicast_membership(drv->pf_sock, + if_nametoindex(drv->ifname), + pae_group_addr, 0) < 0) { wpa_printf(MSG_DEBUG, "%s: Failed to remove PAE multicast " "group (PACKET)", __func__); } @@ -271,16 +603,20 @@ static void wpa_driver_wired_deinit(void *priv) if (drv->pf_sock != -1) close(drv->pf_sock); - + os_free(drv); } const struct wpa_driver_ops wpa_driver_wired_ops = { .name = "wired", - .desc = "wpa_supplicant wired Ethernet driver", + .desc = "Wired Ethernet driver", + .hapd_init = wired_driver_hapd_init, + .hapd_deinit = wired_driver_hapd_deinit, + .hapd_send_eapol = wired_send_eapol, .get_ssid = wpa_driver_wired_get_ssid, .get_bssid = wpa_driver_wired_get_bssid, + .get_capa = wpa_driver_wired_get_capa, .init = wpa_driver_wired_init, .deinit = wpa_driver_wired_deinit, }; diff --git a/contrib/wpa/src/drivers/drivers.c b/contrib/wpa/src/drivers/drivers.c index d278797..bffbbde 100644 --- a/contrib/wpa/src/drivers/drivers.c +++ b/contrib/wpa/src/drivers/drivers.c @@ -1,5 +1,5 @@ /* - * WPA Supplicant / driver interface list + * Driver interface list * Copyright (c) 2004-2005, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify @@ -24,9 +24,6 @@ extern struct wpa_driver_ops wpa_driver_nl80211_ops; /* driver_nl80211.c */ #ifdef CONFIG_DRIVER_HOSTAP extern struct wpa_driver_ops wpa_driver_hostap_ops; /* driver_hostap.c */ #endif /* CONFIG_DRIVER_HOSTAP */ -#ifdef CONFIG_DRIVER_PRISM54 -extern struct wpa_driver_ops wpa_driver_prism54_ops; /* driver_prism54.c */ -#endif /* CONFIG_DRIVER_PRISM54 */ #ifdef CONFIG_DRIVER_HERMES extern struct wpa_driver_ops wpa_driver_hermes_ops; /* driver_hermes.c */ #endif /* CONFIG_DRIVER_HERMES */ @@ -64,9 +61,6 @@ extern struct wpa_driver_ops wpa_driver_ralink_ops; /* driver_ralink.c */ #ifdef CONFIG_DRIVER_OSX extern struct wpa_driver_ops wpa_driver_osx_ops; /* driver_osx.m */ #endif /* CONFIG_DRIVER_OSX */ -#ifdef CONFIG_DRIVER_PS3 -extern struct wpa_driver_ops wpa_driver_ps3_ops; /* driver_ps3.c */ -#endif /* CONFIG_DRIVER_PS3 */ #ifdef CONFIG_DRIVER_IPHONE extern struct wpa_driver_ops wpa_driver_iphone_ops; /* driver_iphone.m */ #endif /* CONFIG_DRIVER_IPHONE */ @@ -74,9 +68,15 @@ extern struct wpa_driver_ops wpa_driver_iphone_ops; /* driver_iphone.m */ /* driver_roboswitch.c */ extern struct wpa_driver_ops wpa_driver_roboswitch_ops; #endif /* CONFIG_DRIVER_ROBOSWITCH */ +#ifdef CONFIG_DRIVER_ATHEROS +extern struct wpa_driver_ops wpa_driver_atheros_ops; /* driver_atheros.c */ +#endif /* CONFIG_DRIVER_ATHEROS */ +#ifdef CONFIG_DRIVER_NONE +extern struct wpa_driver_ops wpa_driver_none_ops; /* driver_none.c */ +#endif /* CONFIG_DRIVER_NONE */ -struct wpa_driver_ops *wpa_supplicant_drivers[] = +struct wpa_driver_ops *wpa_drivers[] = { #ifdef CONFIG_DRIVER_WEXT &wpa_driver_wext_ops, @@ -87,9 +87,6 @@ struct wpa_driver_ops *wpa_supplicant_drivers[] = #ifdef CONFIG_DRIVER_HOSTAP &wpa_driver_hostap_ops, #endif /* CONFIG_DRIVER_HOSTAP */ -#ifdef CONFIG_DRIVER_PRISM54 - &wpa_driver_prism54_ops, -#endif /* CONFIG_DRIVER_PRISM54 */ #ifdef CONFIG_DRIVER_HERMES &wpa_driver_hermes_ops, #endif /* CONFIG_DRIVER_HERMES */ @@ -126,14 +123,17 @@ struct wpa_driver_ops *wpa_supplicant_drivers[] = #ifdef CONFIG_DRIVER_OSX &wpa_driver_osx_ops, #endif /* CONFIG_DRIVER_OSX */ -#ifdef CONFIG_DRIVER_PS3 - &wpa_driver_ps3_ops, -#endif /* CONFIG_DRIVER_PS3 */ #ifdef CONFIG_DRIVER_IPHONE &wpa_driver_iphone_ops, #endif /* CONFIG_DRIVER_IPHONE */ #ifdef CONFIG_DRIVER_ROBOSWITCH &wpa_driver_roboswitch_ops, #endif /* CONFIG_DRIVER_ROBOSWITCH */ +#ifdef CONFIG_DRIVER_ATHEROS + &wpa_driver_atheros_ops, +#endif /* CONFIG_DRIVER_ATHEROS */ +#ifdef CONFIG_DRIVER_NONE + &wpa_driver_none_ops, +#endif /* CONFIG_DRIVER_NONE */ NULL }; diff --git a/contrib/wpa/src/drivers/drivers.mak b/contrib/wpa/src/drivers/drivers.mak new file mode 100644 index 0000000..b76b229 --- /dev/null +++ b/contrib/wpa/src/drivers/drivers.mak @@ -0,0 +1,181 @@ +##### COMMON DRIVERS + +ifdef CONFIG_DRIVER_HOSTAP +DRV_CFLAGS += -DCONFIG_DRIVER_HOSTAP +DRV_OBJS += ../src/drivers/driver_hostap.o +CONFIG_WIRELESS_EXTENSION=y +NEED_AP_MLME=y +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +endif + +ifdef CONFIG_DRIVER_WIRED +DRV_CFLAGS += -DCONFIG_DRIVER_WIRED +DRV_OBJS += ../src/drivers/driver_wired.o +endif + +ifdef CONFIG_DRIVER_MADWIFI +DRV_CFLAGS += -DCONFIG_DRIVER_MADWIFI +DRV_OBJS += ../src/drivers/driver_madwifi.o +CONFIG_WIRELESS_EXTENSION=y +CONFIG_L2_PACKET=linux +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +endif + +ifdef CONFIG_DRIVER_NL80211 +DRV_CFLAGS += -DCONFIG_DRIVER_NL80211 +DRV_OBJS += ../src/drivers/driver_nl80211.o +DRV_OBJS += ../src/utils/radiotap.o +NEED_SME=y +NEED_AP_MLME=y +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +DRV_LIBS += -lnl + +ifdef CONFIG_LIBNL20 +DRV_LIBS += -lnl-genl +DRV_CFLAGS += -DCONFIG_LIBNL20 +endif +endif + +ifdef CONFIG_DRIVER_BSD +ifndef CONFIG_L2_PACKET +CONFIG_L2_PACKET=freebsd +endif +DRV_CFLAGS += -DCONFIG_DRIVER_BSD +DRV_OBJS += ../src/drivers/driver_bsd.o +CONFIG_L2_FREEBSD=y +CONFIG_DNET_PCAP=y +endif + +ifdef CONFIG_DRIVER_TEST +DRV_CFLAGS += -DCONFIG_DRIVER_TEST +DRV_OBJS += ../src/drivers/driver_test.o +NEED_AP_MLME=y +endif + +ifdef CONFIG_DRIVER_NONE +DRV_CFLAGS += -DCONFIG_DRIVER_NONE +DRV_OBJS += ../src/drivers/driver_none.o +endif + +##### PURE AP DRIVERS + +ifdef CONFIG_DRIVER_ATHEROS +DRV_AP_CFLAGS += -DCONFIG_DRIVER_ATHEROS +DRV_AP_OBJS += ../src/drivers/driver_atheros.o +CONFIG_L2_PACKET=linux +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +endif + +##### PURE CLIENT DRIVERS + +ifdef CONFIG_DRIVER_WEXT +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_WEXT +CONFIG_WIRELESS_EXTENSION=y +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +endif + +ifdef CONFIG_DRIVER_HERMES +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_HERMES +DRV_WPA_OBJS += ../src/drivers/driver_hermes.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_ATMEL +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_ATMEL +DRV_WPA_OBJS += ../src/drivers/driver_atmel.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_NDISWRAPPER +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_NDISWRAPPER +DRV_WPA_OBJS += ../src/drivers/driver_ndiswrapper.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_RALINK +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_RALINK +DRV_WPA_OBJS += ../src/drivers/driver_ralink.o +NEED_NETLINK=y +NEED_LINUX_IOCTL=y +endif + +ifdef CONFIG_DRIVER_BROADCOM +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_BROADCOM +DRV_WPA_OBJS += ../src/drivers/driver_broadcom.o +endif + +ifdef CONFIG_DRIVER_IPW +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_IPW +DRV_WPA_OBJS += ../src/drivers/driver_ipw.o +CONFIG_WIRELESS_EXTENSION=y +endif + +ifdef CONFIG_DRIVER_NDIS +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_NDIS +DRV_WPA_OBJS += ../src/drivers/driver_ndis.o +ifdef CONFIG_NDIS_EVENTS_INTEGRATED +DRV_WPA_OBJS += ../src/drivers/driver_ndis_.o +endif +ifndef CONFIG_L2_PACKET +CONFIG_L2_PACKET=pcap +endif +CONFIG_WINPCAP=y +ifdef CONFIG_USE_NDISUIO +DRV_WPA_CFLAGS += -DCONFIG_USE_NDISUIO +endif +endif + +ifdef CONFIG_DRIVER_OSX +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_OSX +DRV_WPA_OBJS += ../src/drivers/driver_osx.o +DRV_WPA_LDFLAGS += -framework CoreFoundation +DRV_WPA_LDFLAGS += -F/System/Library/PrivateFrameworks -framework Apple80211 +endif + +ifdef CONFIG_DRIVER_IPHONE +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_IPHONE +DRV_WPA_OBJS += ../src/drivers/driver_iphone.o +DRV_WPA_OBJS += ../src/drivers/MobileApple80211.o +DRV_WPA_LDFLAGS += -framework CoreFoundation +endif + +ifdef CONFIG_DRIVER_ROBOSWITCH +DRV_WPA_CFLAGS += -DCONFIG_DRIVER_ROBOSWITCH +DRV_WPA_OBJS += ../src/drivers/driver_roboswitch.o +endif + +ifdef CONFIG_WIRELESS_EXTENSION +DRV_WPA_CFLAGS += -DCONFIG_WIRELESS_EXTENSION +DRV_WPA_OBJS += ../src/drivers/driver_wext.o +endif + +ifdef NEED_NETLINK +DRV_OBJS += ../src/drivers/netlink.o +endif + +ifdef NEED_LINUX_IOCTL +DRV_OBJS += ../src/drivers/linux_ioctl.o +endif + + +##### COMMON VARS +DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS) +DRV_WPA_CFLAGS += $(DRV_CFLAGS) +DRV_AP_CFLAGS += $(DRV_CFLAGS) + +DRV_BOTH_LIBS := $(DRV_LIBS) $(DRV_WPA_LIBS) $(DRV_AP_LIBS) +DRV_WPA_LIBS += $(DRV_LIBS) +DRV_AP_LIBS += $(DRV_LIBS) + +DRV_BOTH_OBJS := $(DRV_OBJS) $(DRV_WPA_OBJS) $(DRV_AP_OBJS) +DRV_WPA_OBJS += $(DRV_OBJS) +DRV_AP_OBJS += $(DRV_OBJS) + +DRV_BOTH_LDFLAGS := $(DRV_LDFLAGS) $(DRV_WPA_LDFLAGS) $(DRV_AP_LDFLAGS) +DRV_WPA_LDFLAGS += $(DRV_LDFLAGS) +DRV_AP_LDFLAGS += $(DRV_LDFLAGS) diff --git a/contrib/wpa/src/drivers/linux_ioctl.c b/contrib/wpa/src/drivers/linux_ioctl.c new file mode 100644 index 0000000..0d6cf54 --- /dev/null +++ b/contrib/wpa/src/drivers/linux_ioctl.c @@ -0,0 +1,198 @@ +/* + * Linux ioctl helper functions for driver wrappers + * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published 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 "utils/includes.h" +#include <sys/ioctl.h> +#include <net/if.h> +#include <net/if_arp.h> + +#include "utils/common.h" +#include "linux_ioctl.h" + + +int linux_set_iface_flags(int sock, const char *ifname, int dev_up) +{ + struct ifreq ifr; + + if (sock < 0) + return -1; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + + if (ioctl(sock, SIOCGIFFLAGS, &ifr) != 0) { + wpa_printf(MSG_ERROR, "Could not read interface %s flags: %s", + ifname, strerror(errno)); + return -1; + } + + if (dev_up) { + if (ifr.ifr_flags & IFF_UP) + return 0; + ifr.ifr_flags |= IFF_UP; + } else { + if (!(ifr.ifr_flags & IFF_UP)) + return 0; + ifr.ifr_flags &= ~IFF_UP; + } + + if (ioctl(sock, SIOCSIFFLAGS, &ifr) != 0) { + wpa_printf(MSG_ERROR, "Could not set interface %s flags: %s", + ifname, strerror(errno)); + return -1; + } + + return 0; +} + + +int linux_get_ifhwaddr(int sock, const char *ifname, u8 *addr) +{ + struct ifreq ifr; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + if (ioctl(sock, SIOCGIFHWADDR, &ifr)) { + wpa_printf(MSG_ERROR, "Could not get interface %s hwaddr: %s", + ifname, strerror(errno)); + return -1; + } + + if (ifr.ifr_hwaddr.sa_family != ARPHRD_ETHER) { + wpa_printf(MSG_ERROR, "%s: Invalid HW-addr family 0x%04x", + ifname, ifr.ifr_hwaddr.sa_family); + return -1; + } + os_memcpy(addr, ifr.ifr_hwaddr.sa_data, ETH_ALEN); + + return 0; +} + + +int linux_set_ifhwaddr(int sock, const char *ifname, const u8 *addr) +{ + struct ifreq ifr; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, ifname, IFNAMSIZ); + os_memcpy(ifr.ifr_hwaddr.sa_data, addr, ETH_ALEN); + ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; + + if (ioctl(sock, SIOCSIFHWADDR, &ifr)) { + wpa_printf(MSG_DEBUG, "Could not set interface %s hwaddr: %s", + ifname, strerror(errno)); + return -1; + } + + return 0; +} + + +#ifndef SIOCBRADDBR +#define SIOCBRADDBR 0x89a0 +#endif +#ifndef SIOCBRDELBR +#define SIOCBRDELBR 0x89a1 +#endif +#ifndef SIOCBRADDIF +#define SIOCBRADDIF 0x89a2 +#endif +#ifndef SIOCBRDELIF +#define SIOCBRDELIF 0x89a3 +#endif + + +int linux_br_add(int sock, const char *brname) +{ + if (ioctl(sock, SIOCBRADDBR, brname) < 0) { + wpa_printf(MSG_DEBUG, "Could not add bridge %s: %s", + brname, strerror(errno)); + return -1; + } + + return 0; +} + + +int linux_br_del(int sock, const char *brname) +{ + if (ioctl(sock, SIOCBRDELBR, brname) < 0) { + wpa_printf(MSG_DEBUG, "Could not remove bridge %s: %s", + brname, strerror(errno)); + return -1; + } + + return 0; +} + + +int linux_br_add_if(int sock, const char *brname, const char *ifname) +{ + struct ifreq ifr; + int ifindex; + + ifindex = if_nametoindex(ifname); + if (ifindex == 0) + return -1; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, brname, IFNAMSIZ); + ifr.ifr_ifindex = ifindex; + if (ioctl(sock, SIOCBRADDIF, &ifr) < 0) { + wpa_printf(MSG_DEBUG, "Could not add interface %s into bridge " + "%s: %s", ifname, brname, strerror(errno)); + return -1; + } + + return 0; +} + + +int linux_br_del_if(int sock, const char *brname, const char *ifname) +{ + struct ifreq ifr; + int ifindex; + + ifindex = if_nametoindex(ifname); + if (ifindex == 0) + return -1; + + os_memset(&ifr, 0, sizeof(ifr)); + os_strlcpy(ifr.ifr_name, brname, IFNAMSIZ); + ifr.ifr_ifindex = ifindex; + if (ioctl(sock, SIOCBRDELIF, &ifr) < 0) { + wpa_printf(MSG_DEBUG, "Could not remove interface %s from " + "bridge %s: %s", ifname, brname, strerror(errno)); + return -1; + } + + return 0; +} + + +int linux_br_get(char *brname, const char *ifname) +{ + char path[128], brlink[128], *pos; + os_snprintf(path, sizeof(path), "/sys/class/net/%s/brport/bridge", + ifname); + os_memset(brlink, 0, sizeof(brlink)); + if (readlink(path, brlink, sizeof(brlink) - 1) < 0) + return -1; + pos = os_strrchr(brlink, '/'); + if (pos == NULL) + return -1; + pos++; + os_strlcpy(brname, pos, IFNAMSIZ); + return 0; +} diff --git a/contrib/wpa/src/drivers/linux_ioctl.h b/contrib/wpa/src/drivers/linux_ioctl.h new file mode 100644 index 0000000..a555738 --- /dev/null +++ b/contrib/wpa/src/drivers/linux_ioctl.h @@ -0,0 +1,27 @@ +/* + * Linux ioctl helper functions for driver wrappers + * Copyright (c) 2002-2010, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published 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 LINUX_IOCTL_H +#define LINUX_IOCTL_H + +int linux_set_iface_flags(int sock, const char *ifname, int dev_up); +int linux_get_ifhwaddr(int sock, const char *ifname, u8 *addr); +int linux_set_ifhwaddr(int sock, const char *ifname, const u8 *addr); +int linux_br_add(int sock, const char *brname); +int linux_br_del(int sock, const char *brname); +int linux_br_add_if(int sock, const char *brname, const char *ifname); +int linux_br_del_if(int sock, const char *brname, const char *ifname); +int linux_br_get(char *brname, const char *ifname); + +#endif /* LINUX_IOCTL_H */ diff --git a/contrib/wpa/src/drivers/netlink.c b/contrib/wpa/src/drivers/netlink.c new file mode 100644 index 0000000..ad15b1d --- /dev/null +++ b/contrib/wpa/src/drivers/netlink.c @@ -0,0 +1,204 @@ +/* + * Netlink helper functions for driver wrappers + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "priv_netlink.h" +#include "netlink.h" + + +struct netlink_data { + struct netlink_config *cfg; + int sock; +}; + + +static void netlink_receive_link(struct netlink_data *netlink, + void (*cb)(void *ctx, struct ifinfomsg *ifi, + u8 *buf, size_t len), + struct nlmsghdr *h) +{ + if (cb == NULL || NLMSG_PAYLOAD(h, 0) < sizeof(struct ifinfomsg)) + return; + cb(netlink->cfg->ctx, NLMSG_DATA(h), + NLMSG_DATA(h) + NLMSG_ALIGN(sizeof(struct ifinfomsg)), + NLMSG_PAYLOAD(h, sizeof(struct ifinfomsg))); +} + + +static void netlink_receive(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct netlink_data *netlink = eloop_ctx; + char buf[8192]; + int left; + struct sockaddr_nl from; + socklen_t fromlen; + struct nlmsghdr *h; + int max_events = 10; + +try_again: + fromlen = sizeof(from); + left = recvfrom(sock, buf, sizeof(buf), MSG_DONTWAIT, + (struct sockaddr *) &from, &fromlen); + if (left < 0) { + if (errno != EINTR && errno != EAGAIN) + wpa_printf(MSG_INFO, "netlink: recvfrom failed: %s", + strerror(errno)); + return; + } + + h = (struct nlmsghdr *) buf; + while (NLMSG_OK(h, left)) { + switch (h->nlmsg_type) { + case RTM_NEWLINK: + netlink_receive_link(netlink, netlink->cfg->newlink_cb, + h); + break; + case RTM_DELLINK: + netlink_receive_link(netlink, netlink->cfg->dellink_cb, + h); + break; + } + + h = NLMSG_NEXT(h, left); + } + + if (left > 0) { + wpa_printf(MSG_DEBUG, "netlink: %d extra bytes in the end of " + "netlink message", left); + } + + if (--max_events > 0) { + /* + * Try to receive all events in one eloop call in order to + * limit race condition on cases where AssocInfo event, Assoc + * event, and EAPOL frames are received more or less at the + * same time. We want to process the event messages first + * before starting EAPOL processing. + */ + goto try_again; + } +} + + +struct netlink_data * netlink_init(struct netlink_config *cfg) +{ + struct netlink_data *netlink; + struct sockaddr_nl local; + + netlink = os_zalloc(sizeof(*netlink)); + if (netlink == NULL) + return NULL; + + netlink->cfg = cfg; + + netlink->sock = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); + if (netlink->sock < 0) { + wpa_printf(MSG_ERROR, "netlink: Failed to open netlink " + "socket: %s", strerror(errno)); + netlink_deinit(netlink); + return NULL; + } + + os_memset(&local, 0, sizeof(local)); + local.nl_family = AF_NETLINK; + local.nl_groups = RTMGRP_LINK; + if (bind(netlink->sock, (struct sockaddr *) &local, sizeof(local)) < 0) + { + wpa_printf(MSG_ERROR, "netlink: Failed to bind netlink " + "socket: %s", strerror(errno)); + netlink_deinit(netlink); + return NULL; + } + + eloop_register_read_sock(netlink->sock, netlink_receive, netlink, + NULL); + + return netlink; +} + + +void netlink_deinit(struct netlink_data *netlink) +{ + if (netlink == NULL) + return; + if (netlink->sock >= 0) { + eloop_unregister_read_sock(netlink->sock); + close(netlink->sock); + } + os_free(netlink->cfg); + os_free(netlink); +} + +int netlink_send_oper_ifla(struct netlink_data *netlink, int ifindex, + int linkmode, int operstate) +{ + struct { + struct nlmsghdr hdr; + struct ifinfomsg ifinfo; + char opts[16]; + } req; + struct rtattr *rta; + static int nl_seq; + ssize_t ret; + + os_memset(&req, 0, sizeof(req)); + + req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req.hdr.nlmsg_type = RTM_SETLINK; + req.hdr.nlmsg_flags = NLM_F_REQUEST; + req.hdr.nlmsg_seq = ++nl_seq; + req.hdr.nlmsg_pid = 0; + + req.ifinfo.ifi_family = AF_UNSPEC; + req.ifinfo.ifi_type = 0; + req.ifinfo.ifi_index = ifindex; + req.ifinfo.ifi_flags = 0; + req.ifinfo.ifi_change = 0; + + if (linkmode != -1) { + rta = aliasing_hide_typecast( + ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)), + struct rtattr); + rta->rta_type = IFLA_LINKMODE; + rta->rta_len = RTA_LENGTH(sizeof(char)); + *((char *) RTA_DATA(rta)) = linkmode; + req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + + RTA_LENGTH(sizeof(char)); + } + if (operstate != -1) { + rta = aliasing_hide_typecast( + ((char *) &req + NLMSG_ALIGN(req.hdr.nlmsg_len)), + struct rtattr); + rta->rta_type = IFLA_OPERSTATE; + rta->rta_len = RTA_LENGTH(sizeof(char)); + *((char *) RTA_DATA(rta)) = operstate; + req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + + RTA_LENGTH(sizeof(char)); + } + + wpa_printf(MSG_DEBUG, "netlink: Operstate: linkmode=%d, operstate=%d", + linkmode, operstate); + + ret = send(netlink->sock, &req, req.hdr.nlmsg_len, 0); + if (ret < 0) { + wpa_printf(MSG_DEBUG, "netlink: Sending operstate IFLA " + "failed: %s (assume operstate is not supported)", + strerror(errno)); + } + + return ret < 0 ? -1 : 0; +} diff --git a/contrib/wpa/src/drivers/netlink.h b/contrib/wpa/src/drivers/netlink.h new file mode 100644 index 0000000..bcbfbb5 --- /dev/null +++ b/contrib/wpa/src/drivers/netlink.h @@ -0,0 +1,33 @@ +/* + * Netlink helper functions for driver wrappers + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published 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 NETLINK_H +#define NETLINK_H + +struct netlink_data; + +struct netlink_config { + void *ctx; + void (*newlink_cb)(void *ctx, struct ifinfomsg *ifi, u8 *buf, + size_t len); + void (*dellink_cb)(void *ctx, struct ifinfomsg *ifi, u8 *buf, + size_t len); +}; + +struct netlink_data * netlink_init(struct netlink_config *cfg); +void netlink_deinit(struct netlink_data *netlink); +int netlink_send_oper_ifla(struct netlink_data *netlink, int ifindex, + int linkmode, int operstate); + +#endif /* NETLINK_H */ diff --git a/contrib/wpa/src/common/nl80211_copy.h b/contrib/wpa/src/drivers/nl80211_copy.h index 45db17f..2ea3ede 100644 --- a/contrib/wpa/src/common/nl80211_copy.h +++ b/contrib/wpa/src/drivers/nl80211_copy.h @@ -3,7 +3,7 @@ /* * 802.11 netlink interface public header * - * Copyright 2006, 2007, 2008 Johannes Berg <johannes@sipsolutions.net> + * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net> * Copyright 2008 Michael Wu <flamingice@sourmilk.net> * Copyright 2008 Luis Carlos Cobo <luisca@cozybit.com> * Copyright 2008 Michael Buesch <mb@bu3sch.de> @@ -270,6 +270,66 @@ * @NL80211_CMD_SET_WIPHY_NETNS: Set a wiphy's netns. Note that all devices * associated with this wiphy must be down and will follow. * + * @NL80211_CMD_REMAIN_ON_CHANNEL: Request to remain awake on the specified + * channel for the specified amount of time. This can be used to do + * off-channel operations like transmit a Public Action frame and wait for + * a response while being associated to an AP on another channel. + * %NL80211_ATTR_WIPHY or %NL80211_ATTR_IFINDEX is used to specify which + * radio is used. %NL80211_ATTR_WIPHY_FREQ is used to specify the + * frequency for the operation and %NL80211_ATTR_WIPHY_CHANNEL_TYPE may be + * optionally used to specify additional channel parameters. + * %NL80211_ATTR_DURATION is used to specify the duration in milliseconds + * to remain on the channel. This command is also used as an event to + * notify when the requested duration starts (it may take a while for the + * driver to schedule this time due to other concurrent needs for the + * radio). + * When called, this operation returns a cookie (%NL80211_ATTR_COOKIE) + * that will be included with any events pertaining to this request; + * the cookie is also used to cancel the request. + * @NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL: This command can be used to cancel a + * pending remain-on-channel duration if the desired operation has been + * completed prior to expiration of the originally requested duration. + * %NL80211_ATTR_WIPHY or %NL80211_ATTR_IFINDEX is used to specify the + * radio. The %NL80211_ATTR_COOKIE attribute must be given as well to + * uniquely identify the request. + * This command is also used as an event to notify when a requested + * remain-on-channel duration has expired. + * + * @NL80211_CMD_SET_TX_BITRATE_MASK: Set the mask of rates to be used in TX + * rate selection. %NL80211_ATTR_IFINDEX is used to specify the interface + * and @NL80211_ATTR_TX_RATES the set of allowed rates. + * + * @NL80211_CMD_REGISTER_ACTION: Register for receiving certain action frames + * (via @NL80211_CMD_ACTION) for processing in userspace. This command + * requires an interface index and a match attribute containing the first + * few bytes of the frame that should match, e.g. a single byte for only + * a category match or four bytes for vendor frames including the OUI. + * The registration cannot be dropped, but is removed automatically + * when the netlink socket is closed. Multiple registrations can be made. + * @NL80211_CMD_ACTION: Action frame TX request and RX notification. This + * command is used both as a request to transmit an Action frame and as an + * event indicating reception of an Action frame that was not processed in + * kernel code, but is for us (i.e., which may need to be processed in a + * user space application). %NL80211_ATTR_FRAME is used to specify the + * frame contents (including header). %NL80211_ATTR_WIPHY_FREQ (and + * optionally %NL80211_ATTR_WIPHY_CHANNEL_TYPE) is used to indicate on + * which channel the frame is to be transmitted or was received. This + * channel has to be the current channel (remain-on-channel or the + * operational channel). When called, this operation returns a cookie + * (%NL80211_ATTR_COOKIE) that will be included with the TX status event + * pertaining to the TX request. + * @NL80211_CMD_ACTION_TX_STATUS: Report TX status of an Action frame + * transmitted with %NL80211_CMD_ACTION. %NL80211_ATTR_COOKIE identifies + * the TX command and %NL80211_ATTR_FRAME includes the contents of the + * frame. %NL80211_ATTR_ACK flag is included if the recipient acknowledged + * the frame. + * @NL80211_CMD_SET_CQM: Connection quality monitor configuration. This command + * is used to configure connection quality monitoring notification trigger + * levels. + * @NL80211_CMD_NOTIFY_CQM: Connection quality monitor notification. This + * command is used as an event to indicate the that a trigger level was + * reached. + * * @NL80211_CMD_MAX: highest used command number * @__NL80211_CMD_AFTER_LAST: internal use */ @@ -349,6 +409,25 @@ enum nl80211_commands { NL80211_CMD_GET_SURVEY, NL80211_CMD_NEW_SURVEY_RESULTS, + NL80211_CMD_SET_PMKSA, + NL80211_CMD_DEL_PMKSA, + NL80211_CMD_FLUSH_PMKSA, + + NL80211_CMD_REMAIN_ON_CHANNEL, + NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, + + NL80211_CMD_SET_TX_BITRATE_MASK, + + NL80211_CMD_REGISTER_ACTION, + NL80211_CMD_ACTION, + NL80211_CMD_ACTION_TX_STATUS, + + NL80211_CMD_SET_POWER_SAVE, + NL80211_CMD_GET_POWER_SAVE, + + NL80211_CMD_SET_CQM, + NL80211_CMD_NOTIFY_CQM, + /* add new commands above here */ /* used to define NL80211_CMD_MAX below */ @@ -398,6 +477,8 @@ enum nl80211_commands { * @NL80211_ATTR_WIPHY_RTS_THRESHOLD: RTS threshold (TX frames with length * larger than or equal to this use RTS/CTS handshake); allowed range: * 0..65536, disable with (u32)-1; dot11RTSThreshold; u32 + * @NL80211_ATTR_WIPHY_COVERAGE_CLASS: Coverage Class as defined by IEEE 802.11 + * section 7.3.2.9; dot11CoverageClass; u8 * * @NL80211_ATTR_IFINDEX: network interface index of the device to operate on * @NL80211_ATTR_IFNAME: network interface name @@ -598,6 +679,36 @@ enum nl80211_commands { * the survey response for %NL80211_CMD_GET_SURVEY, nested attribute * containing info as possible, see &enum survey_info. * + * @NL80211_ATTR_PMKID: PMK material for PMKSA caching. + * @NL80211_ATTR_MAX_NUM_PMKIDS: maximum number of PMKIDs a firmware can + * cache, a wiphy attribute. + * + * @NL80211_ATTR_DURATION: Duration of an operation in milliseconds, u32. + * + * @NL80211_ATTR_COOKIE: Generic 64-bit cookie to identify objects. + * + * @NL80211_ATTR_TX_RATES: Nested set of attributes + * (enum nl80211_tx_rate_attributes) describing TX rates per band. The + * enum nl80211_band value is used as the index (nla_type() of the nested + * data. If a band is not included, it will be configured to allow all + * rates based on negotiated supported rates information. This attribute + * is used with %NL80211_CMD_SET_TX_BITRATE_MASK. + * + * @NL80211_ATTR_FRAME_MATCH: A binary attribute which typically must contain + * at least one byte, currently used with @NL80211_CMD_REGISTER_ACTION. + * + * @NL80211_ATTR_ACK: Flag attribute indicating that the frame was + * acknowledged by the recipient. + * + * @NL80211_ATTR_CQM: connection quality monitor configuration in a + * nested attribute with %NL80211_ATTR_CQM_* sub-attributes. + * + * @NL80211_ATTR_LOCAL_STATE_CHANGE: Flag attribute to indicate that a command + * is requesting a local authentication/association state change without + * invoking actual management frame exchange. This can be used with + * NL80211_CMD_AUTHENTICATE, NL80211_CMD_DEAUTHENTICATE, + * NL80211_CMD_DISASSOCIATE. + * * @NL80211_ATTR_MAX: highest attribute number currently defined * @__NL80211_ATTR_AFTER_LAST: internal use */ @@ -732,6 +843,27 @@ enum nl80211_attrs { NL80211_ATTR_SURVEY_INFO, + NL80211_ATTR_PMKID, + NL80211_ATTR_MAX_NUM_PMKIDS, + + NL80211_ATTR_DURATION, + + NL80211_ATTR_COOKIE, + + NL80211_ATTR_WIPHY_COVERAGE_CLASS, + + NL80211_ATTR_TX_RATES, + + NL80211_ATTR_FRAME_MATCH, + + NL80211_ATTR_ACK, + + NL80211_ATTR_PS_STATE, + + NL80211_ATTR_CQM, + + NL80211_ATTR_LOCAL_STATE_CHANGE, + /* add attributes here, update the policy in nl80211.c */ __NL80211_ATTR_AFTER_LAST, @@ -1312,13 +1444,20 @@ enum nl80211_channel_type { * @NL80211_BSS_BEACON_INTERVAL: beacon interval of the (I)BSS (u16) * @NL80211_BSS_CAPABILITY: capability field (CPU order, u16) * @NL80211_BSS_INFORMATION_ELEMENTS: binary attribute containing the - * raw information elements from the probe response/beacon (bin) + * raw information elements from the probe response/beacon (bin); + * if the %NL80211_BSS_BEACON_IES attribute is present, the IEs here are + * from a Probe Response frame; otherwise they are from a Beacon frame. + * However, if the driver does not indicate the source of the IEs, these + * IEs may be from either frame subtype. * @NL80211_BSS_SIGNAL_MBM: signal strength of probe response/beacon * in mBm (100 * dBm) (s32) * @NL80211_BSS_SIGNAL_UNSPEC: signal strength of the probe response/beacon * in unspecified units, scaled to 0..100 (u8) * @NL80211_BSS_STATUS: status, if this BSS is "used" * @NL80211_BSS_SEEN_MS_AGO: age of this BSS entry in ms + * @NL80211_BSS_BEACON_IES: binary attribute containing the raw information + * elements from a Beacon frame (bin); not present if no Beacon frame has + * yet been received * @__NL80211_BSS_AFTER_LAST: internal * @NL80211_BSS_MAX: highest BSS attribute */ @@ -1334,6 +1473,7 @@ enum nl80211_bss { NL80211_BSS_SIGNAL_UNSPEC, NL80211_BSS_STATUS, NL80211_BSS_SEEN_MS_AGO, + NL80211_BSS_BEACON_IES, /* keep last */ __NL80211_BSS_AFTER_LAST, @@ -1431,4 +1571,74 @@ enum nl80211_key_attributes { NL80211_KEY_MAX = __NL80211_KEY_AFTER_LAST - 1 }; +/** + * enum nl80211_tx_rate_attributes - TX rate set attributes + * @__NL80211_TXRATE_INVALID: invalid + * @NL80211_TXRATE_LEGACY: Legacy (non-MCS) rates allowed for TX rate selection + * in an array of rates as defined in IEEE 802.11 7.3.2.2 (u8 values with + * 1 = 500 kbps) but without the IE length restriction (at most + * %NL80211_MAX_SUPP_RATES in a single array). + * @__NL80211_TXRATE_AFTER_LAST: internal + * @NL80211_TXRATE_MAX: highest TX rate attribute + */ +enum nl80211_tx_rate_attributes { + __NL80211_TXRATE_INVALID, + NL80211_TXRATE_LEGACY, + + /* keep last */ + __NL80211_TXRATE_AFTER_LAST, + NL80211_TXRATE_MAX = __NL80211_TXRATE_AFTER_LAST - 1 +}; + +/** + * enum nl80211_band - Frequency band + * @NL80211_BAND_2GHZ - 2.4 GHz ISM band + * @NL80211_BAND_5GHZ - around 5 GHz band (4.9 - 5.7 GHz) + */ +enum nl80211_band { + NL80211_BAND_2GHZ, + NL80211_BAND_5GHZ, +}; + +enum nl80211_ps_state { + NL80211_PS_DISABLED, + NL80211_PS_ENABLED, +}; + +/** + * enum nl80211_attr_cqm - connection quality monitor attributes + * @__NL80211_ATTR_CQM_INVALID: invalid + * @NL80211_ATTR_CQM_RSSI_THOLD: RSSI threshold in dBm. This value specifies + * the threshold for the RSSI level at which an event will be sent. Zero + * to disable. + * @NL80211_ATTR_CQM_RSSI_HYST: RSSI hysteresis in dBm. This value specifies + * the minimum amount the RSSI level must change after an event before a + * new event may be issued (to reduce effects of RSSI oscillation). + * @NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT: RSSI threshold event + * @__NL80211_ATTR_CQM_AFTER_LAST: internal + * @NL80211_ATTR_CQM_MAX: highest key attribute + */ +enum nl80211_attr_cqm { + __NL80211_ATTR_CQM_INVALID, + NL80211_ATTR_CQM_RSSI_THOLD, + NL80211_ATTR_CQM_RSSI_HYST, + NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT, + + /* keep last */ + __NL80211_ATTR_CQM_AFTER_LAST, + NL80211_ATTR_CQM_MAX = __NL80211_ATTR_CQM_AFTER_LAST - 1 +}; + +/** + * enum nl80211_cqm_rssi_threshold_event - RSSI threshold event + * @NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW - The RSSI level is lower than the + * configured threshold + * @NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH - The RSSI is higher than the + * configured threshold + */ +enum nl80211_cqm_rssi_threshold_event { + NL80211_CQM_RSSI_THRESHOLD_EVENT_LOW, + NL80211_CQM_RSSI_THRESHOLD_EVENT_HIGH, +}; + #endif /* __LINUX_NL80211_H */ diff --git a/contrib/wpa/src/drivers/scan_helpers.c b/contrib/wpa/src/drivers/scan_helpers.c deleted file mode 100644 index 6338770..0000000 --- a/contrib/wpa/src/drivers/scan_helpers.c +++ /dev/null @@ -1,182 +0,0 @@ -/* - * WPA Supplicant - Helper functions for scan result processing - * Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" - -#include "common.h" -#include "drivers/driver.h" -#include "ieee802_11_defs.h" - - -const u8 * wpa_scan_get_ie(const struct wpa_scan_res *res, u8 ie) -{ - const u8 *end, *pos; - - pos = (const u8 *) (res + 1); - end = pos + res->ie_len; - - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) - break; - if (pos[0] == ie) - return pos; - pos += 2 + pos[1]; - } - - return NULL; -} - - -const u8 * wpa_scan_get_vendor_ie(const struct wpa_scan_res *res, - u32 vendor_type) -{ - const u8 *end, *pos; - - pos = (const u8 *) (res + 1); - end = pos + res->ie_len; - - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) - break; - if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && - vendor_type == WPA_GET_BE32(&pos[2])) - return pos; - pos += 2 + pos[1]; - } - - return NULL; -} - - -struct wpabuf * wpa_scan_get_vendor_ie_multi(const struct wpa_scan_res *res, - u32 vendor_type) -{ - struct wpabuf *buf; - const u8 *end, *pos; - - buf = wpabuf_alloc(res->ie_len); - if (buf == NULL) - return NULL; - - pos = (const u8 *) (res + 1); - end = pos + res->ie_len; - - while (pos + 1 < end) { - if (pos + 2 + pos[1] > end) - break; - if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 && - vendor_type == WPA_GET_BE32(&pos[2])) - wpabuf_put_data(buf, pos + 2 + 4, pos[1] - 4); - pos += 2 + pos[1]; - } - - if (wpabuf_len(buf) == 0) { - wpabuf_free(buf); - buf = NULL; - } - - return buf; -} - - -int wpa_scan_get_max_rate(const struct wpa_scan_res *res) -{ - int rate = 0; - const u8 *ie; - int i; - - ie = wpa_scan_get_ie(res, WLAN_EID_SUPP_RATES); - for (i = 0; ie && i < ie[1]; i++) { - if ((ie[i + 2] & 0x7f) > rate) - rate = ie[i + 2] & 0x7f; - } - - ie = wpa_scan_get_ie(res, WLAN_EID_EXT_SUPP_RATES); - for (i = 0; ie && i < ie[1]; i++) { - if ((ie[i + 2] & 0x7f) > rate) - rate = ie[i + 2] & 0x7f; - } - - return rate; -} - - -void wpa_scan_results_free(struct wpa_scan_results *res) -{ - size_t i; - - if (res == NULL) - return; - - for (i = 0; i < res->num; i++) - os_free(res->res[i]); - os_free(res->res); - os_free(res); -} - - -/* Compare function for sorting scan results. Return >0 if @b is considered - * better. */ -static int wpa_scan_result_compar(const void *a, const void *b) -{ - struct wpa_scan_res **_wa = (void *) a; - struct wpa_scan_res **_wb = (void *) b; - struct wpa_scan_res *wa = *_wa; - struct wpa_scan_res *wb = *_wb; - int wpa_a, wpa_b, maxrate_a, maxrate_b; - - /* WPA/WPA2 support preferred */ - wpa_a = wpa_scan_get_vendor_ie(wa, WPA_IE_VENDOR_TYPE) != NULL || - wpa_scan_get_ie(wa, WLAN_EID_RSN) != NULL; - wpa_b = wpa_scan_get_vendor_ie(wb, WPA_IE_VENDOR_TYPE) != NULL || - wpa_scan_get_ie(wb, WLAN_EID_RSN) != NULL; - - if (wpa_b && !wpa_a) - return 1; - if (!wpa_b && wpa_a) - return -1; - - /* privacy support preferred */ - if ((wa->caps & IEEE80211_CAP_PRIVACY) == 0 && - (wb->caps & IEEE80211_CAP_PRIVACY)) - return 1; - if ((wa->caps & IEEE80211_CAP_PRIVACY) && - (wb->caps & IEEE80211_CAP_PRIVACY) == 0) - return -1; - - /* best/max rate preferred if signal level close enough XXX */ - if ((wa->level && wb->level && abs(wb->level - wa->level) < 5) || - (wa->qual && wb->qual && abs(wb->qual - wa->qual) < 10)) { - maxrate_a = wpa_scan_get_max_rate(wa); - maxrate_b = wpa_scan_get_max_rate(wb); - if (maxrate_a != maxrate_b) - return maxrate_b - maxrate_a; - } - - /* use freq for channel preference */ - - /* all things being equal, use signal level; if signal levels are - * identical, use quality values since some drivers may only report - * that value and leave the signal level zero */ - if (wb->level == wa->level) - return wb->qual - wa->qual; - return wb->level - wa->level; -} - - -void wpa_scan_sort_results(struct wpa_scan_results *res) -{ - qsort(res->res, res->num, sizeof(struct wpa_scan_res *), - wpa_scan_result_compar); -} diff --git a/contrib/wpa/src/common/wireless_copy.h b/contrib/wpa/src/drivers/wireless_copy.h index ad76466..ad76466 100644 --- a/contrib/wpa/src/common/wireless_copy.h +++ b/contrib/wpa/src/drivers/wireless_copy.h diff --git a/contrib/wpa/src/eap_common/Makefile b/contrib/wpa/src/eap_common/Makefile index cffba62..9c41962 100644 --- a/contrib/wpa/src/eap_common/Makefile +++ b/contrib/wpa/src/eap_common/Makefile @@ -2,7 +2,6 @@ all: @echo Nothing to be made. clean: - for d in $(SUBDIRS); do make -C $$d clean; done rm -f *~ *.o *.d install: diff --git a/contrib/wpa/src/eap_common/chap.c b/contrib/wpa/src/eap_common/chap.c index a088aff..60bfc1c 100644 --- a/contrib/wpa/src/eap_common/chap.c +++ b/contrib/wpa/src/eap_common/chap.c @@ -1,6 +1,6 @@ /* * CHAP-MD5 (RFC 1994) - * Copyright (c) 2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -15,11 +15,10 @@ #include "includes.h" #include "common.h" -#include "md5.h" -#include "crypto.h" +#include "crypto/crypto.h" #include "chap.h" -void chap_md5(u8 id, const u8 *secret, size_t secret_len, const u8 *challenge, +int chap_md5(u8 id, const u8 *secret, size_t secret_len, const u8 *challenge, size_t challenge_len, u8 *response) { const u8 *addr[3]; @@ -31,5 +30,5 @@ void chap_md5(u8 id, const u8 *secret, size_t secret_len, const u8 *challenge, len[1] = secret_len; addr[2] = challenge; len[2] = challenge_len; - md5_vector(3, addr, len, response); + return md5_vector(3, addr, len, response); } diff --git a/contrib/wpa/src/eap_common/chap.h b/contrib/wpa/src/eap_common/chap.h index 209dc8a..b9c400c 100644 --- a/contrib/wpa/src/eap_common/chap.h +++ b/contrib/wpa/src/eap_common/chap.h @@ -1,6 +1,6 @@ /* * CHAP-MD5 (RFC 1994) - * Copyright (c) 2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -17,7 +17,7 @@ #define CHAP_MD5_LEN 16 -void chap_md5(u8 id, const u8 *secret, size_t secret_len, const u8 *challenge, - size_t challenge_len, u8 *response); +int chap_md5(u8 id, const u8 *secret, size_t secret_len, const u8 *challenge, + size_t challenge_len, u8 *response); #endif /* CHAP_H */ diff --git a/contrib/wpa/src/eap_common/eap_fast_common.c b/contrib/wpa/src/eap_common/eap_fast_common.c index 4d3deaf..4de34a8 100644 --- a/contrib/wpa/src/eap_common/eap_fast_common.c +++ b/contrib/wpa/src/eap_common/eap_fast_common.c @@ -15,8 +15,8 @@ #include "includes.h" #include "common.h" -#include "sha1.h" -#include "tls.h" +#include "crypto/sha1.h" +#include "crypto/tls.h" #include "eap_defs.h" #include "eap_tlv_common.h" #include "eap_fast_common.h" diff --git a/contrib/wpa/src/eap_common/eap_gpsk_common.c b/contrib/wpa/src/eap_common/eap_gpsk_common.c index 414610c..4076262 100644 --- a/contrib/wpa/src/eap_common/eap_gpsk_common.c +++ b/contrib/wpa/src/eap_common/eap_gpsk_common.c @@ -15,12 +15,9 @@ #include "includes.h" #include "common.h" +#include "crypto/aes_wrap.h" +#include "crypto/sha256.h" #include "eap_defs.h" -#include "aes_wrap.h" -#include "crypto.h" -#ifdef EAP_GPSK_SHA256 -#include "sha256.h" -#endif /* EAP_GPSK_SHA256 */ #include "eap_gpsk_common.h" diff --git a/contrib/wpa/src/eap_common/eap_pax_common.c b/contrib/wpa/src/eap_common/eap_pax_common.c index 8011046..32dc80c 100644 --- a/contrib/wpa/src/eap_common/eap_pax_common.c +++ b/contrib/wpa/src/eap_common/eap_pax_common.c @@ -15,7 +15,7 @@ #include "includes.h" #include "common.h" -#include "sha1.h" +#include "crypto/sha1.h" #include "eap_pax_common.h" diff --git a/contrib/wpa/src/eap_common/eap_peap_common.c b/contrib/wpa/src/eap_common/eap_peap_common.c index 14625f9..3a64b8e 100644 --- a/contrib/wpa/src/eap_common/eap_peap_common.c +++ b/contrib/wpa/src/eap_common/eap_peap_common.c @@ -15,7 +15,7 @@ #include "includes.h" #include "common.h" -#include "sha1.h" +#include "crypto/sha1.h" #include "eap_peap_common.h" void peap_prfplus(int version, const u8 *key, size_t key_len, diff --git a/contrib/wpa/src/eap_common/eap_psk_common.c b/contrib/wpa/src/eap_common/eap_psk_common.c index 0def3e8..7417d5c 100644 --- a/contrib/wpa/src/eap_common/eap_psk_common.c +++ b/contrib/wpa/src/eap_common/eap_psk_common.c @@ -15,7 +15,7 @@ #include "includes.h" #include "common.h" -#include "aes_wrap.h" +#include "crypto/aes_wrap.h" #include "eap_defs.h" #include "eap_psk_common.h" diff --git a/contrib/wpa/src/eap_common/eap_sake_common.c b/contrib/wpa/src/eap_common/eap_sake_common.c index eafad1d..9002b0c 100644 --- a/contrib/wpa/src/eap_common/eap_sake_common.c +++ b/contrib/wpa/src/eap_common/eap_sake_common.c @@ -15,8 +15,8 @@ #include "includes.h" #include "common.h" -#include "sha1.h" #include "wpabuf.h" +#include "crypto/sha1.h" #include "eap_defs.h" #include "eap_sake_common.h" diff --git a/contrib/wpa/src/eap_common/eap_sim_common.c b/contrib/wpa/src/eap_common/eap_sim_common.c index fccda02..56b4ded 100644 --- a/contrib/wpa/src/eap_common/eap_sim_common.c +++ b/contrib/wpa/src/eap_common/eap_sim_common.c @@ -15,12 +15,12 @@ #include "includes.h" #include "common.h" -#include "eap_common/eap_defs.h" -#include "sha1.h" -#include "sha256.h" -#include "crypto.h" -#include "aes_wrap.h" #include "wpabuf.h" +#include "crypto/aes_wrap.h" +#include "crypto/crypto.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" +#include "eap_common/eap_defs.h" #include "eap_common/eap_sim_common.h" @@ -233,7 +233,7 @@ void eap_sim_add_mac(const u8 *k_aut, const u8 *msg, size_t msg_len, u8 *mac, } -#ifdef EAP_AKA_PRIME +#if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME) static void prf_prime(const u8 *k, const char *seed1, const u8 *seed2, size_t seed2_len, const u8 *seed3, size_t seed3_len, @@ -496,7 +496,7 @@ void eap_aka_prime_derive_ck_ik_prime(u8 *ck, u8 *ik, const u8 *sqn_ak, wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': CK'", ck, EAP_AKA_CK_LEN); wpa_hexdump_key(MSG_DEBUG, "EAP-AKA': IK'", ik, EAP_AKA_IK_LEN); } -#endif /* EAP_AKA_PRIME */ +#endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */ int eap_sim_parse_attr(const u8 *start, const u8 *end, @@ -858,7 +858,7 @@ int eap_sim_parse_attr(const u8 *start, const u8 *end, wpa_printf(MSG_DEBUG, "EAP-SIM: AT_RESULT_IND"); attr->result_ind = 1; break; -#ifdef EAP_AKA_PRIME +#if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME) case EAP_SIM_AT_KDF_INPUT: if (aka != 2) { wpa_printf(MSG_INFO, "EAP-AKA: Unexpected " @@ -913,7 +913,7 @@ int eap_sim_parse_attr(const u8 *start, const u8 *end, } attr->bidding = apos; break; -#endif /* EAP_AKA_PRIME */ +#endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */ default: if (pos[0] < 128) { wpa_printf(MSG_INFO, "EAP-SIM: Unrecognized " @@ -1023,14 +1023,14 @@ struct wpabuf * eap_sim_msg_finish(struct eap_sim_msg *msg, const u8 *k_aut, eap = wpabuf_mhead(msg->buf); eap->length = host_to_be16(wpabuf_len(msg->buf)); -#ifdef EAP_AKA_PRIME +#if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME) if (k_aut && msg->mac && msg->type == EAP_TYPE_AKA_PRIME) { eap_sim_add_mac_sha256(k_aut, (u8 *) wpabuf_head(msg->buf), wpabuf_len(msg->buf), (u8 *) wpabuf_mhead(msg->buf) + msg->mac, extra, extra_len); } else -#endif /* EAP_AKA_PRIME */ +#endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */ if (k_aut && msg->mac) { eap_sim_add_mac(k_aut, (u8 *) wpabuf_head(msg->buf), wpabuf_len(msg->buf), diff --git a/contrib/wpa/src/eap_common/eap_sim_common.h b/contrib/wpa/src/eap_common/eap_sim_common.h index a8080e2..48c8eaa 100644 --- a/contrib/wpa/src/eap_common/eap_sim_common.h +++ b/contrib/wpa/src/eap_common/eap_sim_common.h @@ -94,7 +94,7 @@ int eap_sim_verify_mac(const u8 *k_aut, const struct wpabuf *req, void eap_sim_add_mac(const u8 *k_aut, const u8 *msg, size_t msg_len, u8 *mac, const u8 *extra, size_t extra_len); -#ifdef EAP_AKA_PRIME +#if defined(EAP_AKA_PRIME) || defined(EAP_SERVER_AKA_PRIME) void eap_aka_prime_derive_keys(const u8 *identity, size_t identity_len, const u8 *ik, const u8 *ck, u8 *k_encr, u8 *k_aut, u8 *k_re, u8 *msk, u8 *emsk); @@ -110,7 +110,7 @@ void eap_sim_add_mac_sha256(const u8 *k_aut, const u8 *msg, size_t msg_len, void eap_aka_prime_derive_ck_ik_prime(u8 *ck, u8 *ik, const u8 *sqn_ak, const u8 *network_name, size_t network_name_len); -#else /* EAP_AKA_PRIME */ +#else /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */ static inline void eap_aka_prime_derive_keys(const u8 *identity, size_t identity_len, const u8 *ik, const u8 *ck, @@ -135,7 +135,7 @@ static inline int eap_sim_verify_mac_sha256(const u8 *k_aut, { return -1; } -#endif /* EAP_AKA_PRIME */ +#endif /* EAP_AKA_PRIME || EAP_SERVER_AKA_PRIME */ /* EAP-SIM/AKA Attributes (0..127 non-skippable) */ diff --git a/contrib/wpa/src/eap_common/ikev2_common.c b/contrib/wpa/src/eap_common/ikev2_common.c index 818b5bd..67754d8 100644 --- a/contrib/wpa/src/eap_common/ikev2_common.c +++ b/contrib/wpa/src/eap_common/ikev2_common.c @@ -15,9 +15,9 @@ #include "includes.h" #include "common.h" -#include "sha1.h" -#include "md5.h" -#include "crypto.h" +#include "crypto/crypto.h" +#include "crypto/md5.h" +#include "crypto/sha1.h" #include "ikev2_common.h" diff --git a/contrib/wpa/src/eap_peer/Makefile b/contrib/wpa/src/eap_peer/Makefile index d9449a2..3651056 100644 --- a/contrib/wpa/src/eap_peer/Makefile +++ b/contrib/wpa/src/eap_peer/Makefile @@ -2,7 +2,6 @@ all: @echo Nothing to be made. clean: - for d in $(SUBDIRS); do make -C $$d clean; done rm -f *~ *.o *.so *.d install: diff --git a/contrib/wpa/src/eap_peer/eap.c b/contrib/wpa/src/eap_peer/eap.c index e8e504a..b9f186b 100644 --- a/contrib/wpa/src/eap_peer/eap.c +++ b/contrib/wpa/src/eap_peer/eap.c @@ -1,6 +1,6 @@ /* * EAP peer state machines (RFC 4137) - * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -24,14 +24,14 @@ #include "includes.h" #include "common.h" -#include "eap_i.h" -#include "eap_config.h" -#include "tls.h" -#include "crypto.h" #include "pcsc_funcs.h" -#include "wpa_ctrl.h" #include "state_machine.h" +#include "crypto/crypto.h" +#include "crypto/tls.h" +#include "common/wpa_ctrl.h" #include "eap_common/eap_wsc_common.h" +#include "eap_i.h" +#include "eap_config.h" #define STATE_MACHINE_DATA struct eap_sm #define STATE_MACHINE_DEBUG_PREFIX "EAP" @@ -228,9 +228,15 @@ SM_STATE(EAP, GET_METHOD) if (!eap_sm_allowMethod(sm, sm->reqVendor, method)) { wpa_printf(MSG_DEBUG, "EAP: vendor %u method %u not allowed", sm->reqVendor, method); + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD + "vendor=%u method=%u -> NAK", + sm->reqVendor, method); goto nak; } + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD + "vendor=%u method=%u", sm->reqVendor, method); + /* * RFC 4137 does not define specific operation for fast * re-authentication (session resumption). The design here is to allow @@ -1154,6 +1160,60 @@ static void eap_sm_parseEapReq(struct eap_sm *sm, const struct wpabuf *req) } +static void eap_peer_sm_tls_event(void *ctx, enum tls_event ev, + union tls_event_data *data) +{ + struct eap_sm *sm = ctx; + char *hash_hex = NULL; + char *cert_hex = NULL; + + switch (ev) { + case TLS_CERT_CHAIN_FAILURE: + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_TLS_CERT_ERROR + "reason=%d depth=%d subject='%s' err='%s'", + data->cert_fail.reason, + data->cert_fail.depth, + data->cert_fail.subject, + data->cert_fail.reason_txt); + break; + case TLS_PEER_CERTIFICATE: + if (data->peer_cert.hash) { + size_t len = data->peer_cert.hash_len * 2 + 1; + hash_hex = os_malloc(len); + if (hash_hex) { + wpa_snprintf_hex(hash_hex, len, + data->peer_cert.hash, + data->peer_cert.hash_len); + } + } + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PEER_CERT + "depth=%d subject='%s'%s%s", + data->peer_cert.depth, data->peer_cert.subject, + hash_hex ? " hash=" : "", hash_hex ? hash_hex : ""); + + if (data->peer_cert.cert) { + size_t len = wpabuf_len(data->peer_cert.cert) * 2 + 1; + cert_hex = os_malloc(len); + if (cert_hex == NULL) + break; + wpa_snprintf_hex(cert_hex, len, + wpabuf_head(data->peer_cert.cert), + wpabuf_len(data->peer_cert.cert)); + wpa_msg_ctrl(sm->msg_ctx, MSG_INFO, + WPA_EVENT_EAP_PEER_CERT + "depth=%d subject='%s' cert=%s", + data->peer_cert.depth, + data->peer_cert.subject, + cert_hex); + } + break; + } + + os_free(hash_hex); + os_free(cert_hex); +} + + /** * eap_peer_sm_init - Allocate and initialize EAP peer state machine * @eapol_ctx: Context data to be used with eapol_cb calls @@ -1188,6 +1248,11 @@ struct eap_sm * eap_peer_sm_init(void *eapol_ctx, tlsconf.opensc_engine_path = conf->opensc_engine_path; tlsconf.pkcs11_engine_path = conf->pkcs11_engine_path; tlsconf.pkcs11_module_path = conf->pkcs11_module_path; +#ifdef CONFIG_FIPS + tlsconf.fips_mode = 1; +#endif /* CONFIG_FIPS */ + tlsconf.event_cb = eap_peer_sm_tls_event; + tlsconf.cb_ctx = sm; sm->ssl_ctx = tls_init(&tlsconf); if (sm->ssl_ctx == NULL) { wpa_printf(MSG_WARNING, "SSL: Failed to initialize TLS " diff --git a/contrib/wpa/src/eap_peer/eap.h b/contrib/wpa/src/eap_peer/eap.h index d7a5628..40d0b69 100644 --- a/contrib/wpa/src/eap_peer/eap.h +++ b/contrib/wpa/src/eap_peer/eap.h @@ -15,7 +15,7 @@ #ifndef EAP_H #define EAP_H -#include "defs.h" +#include "common/defs.h" #include "eap_common/eap_defs.h" #include "eap_peer/eap_methods.h" diff --git a/contrib/wpa/src/eap_peer/eap_aka.c b/contrib/wpa/src/eap_peer/eap_aka.c index f237141..182f01a 100644 --- a/contrib/wpa/src/eap_peer/eap_aka.c +++ b/contrib/wpa/src/eap_peer/eap_aka.c @@ -15,16 +15,14 @@ #include "includes.h" #include "common.h" -#include "eap_peer/eap_i.h" #include "pcsc_funcs.h" +#include "crypto/crypto.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" +#include "crypto/milenage.h" #include "eap_common/eap_sim_common.h" -#include "sha1.h" -#include "sha256.h" -#include "crypto.h" -#include "eap_peer/eap_config.h" -#ifdef CONFIG_USIM_SIMULATOR -#include "hlr_auc_gw/milenage.h" -#endif /* CONFIG_USIM_SIMULATOR */ +#include "eap_config.h" +#include "eap_i.h" struct eap_aka_data { diff --git a/contrib/wpa/src/eap_peer/eap_config.h b/contrib/wpa/src/eap_peer/eap_config.h index 94245c3..b64b68f 100644 --- a/contrib/wpa/src/eap_peer/eap_config.h +++ b/contrib/wpa/src/eap_peer/eap_config.h @@ -85,6 +85,15 @@ struct eap_peer_config { * Alternatively, a named configuration blob can be used by setting * this to blob://blob_name. * + * Alternatively, this can be used to only perform matching of the + * server certificate (SHA-256 hash of the DER encoded X.509 + * certificate). In this case, the possible CA certificates in the + * server certificate chain are ignored and only the server certificate + * is verified. This is configured with the following format: + * hash:://server/sha256/cert_hash_in_hex + * For example: "hash://server/sha256/ + * 5a1bc1296205e6fdbe3979728efe3920798885c1c4590b5f90f43222d239ca6a" + * * On Windows, trusted CA certificates can be loaded from the system * certificate store by setting this to cert_store://name, e.g., * ca_cert="cert_store://CA" or ca_cert="cert_store://ROOT". diff --git a/contrib/wpa/src/eap_peer/eap_fast.c b/contrib/wpa/src/eap_peer/eap_fast.c index d008670..5d3e69d 100644 --- a/contrib/wpa/src/eap_peer/eap_fast.c +++ b/contrib/wpa/src/eap_peer/eap_fast.c @@ -15,12 +15,12 @@ #include "includes.h" #include "common.h" +#include "crypto/tls.h" +#include "crypto/sha1.h" +#include "eap_common/eap_tlv_common.h" #include "eap_i.h" #include "eap_tls_common.h" #include "eap_config.h" -#include "tls.h" -#include "eap_common/eap_tlv_common.h" -#include "sha1.h" #include "eap_fast_pac.h" #ifdef EAP_FAST_DYNAMIC @@ -1445,9 +1445,9 @@ static int eap_fast_process_start(struct eap_sm *sm, /* EAP-FAST Version negotiation (section 3.1) */ wpa_printf(MSG_DEBUG, "EAP-FAST: Start (server ver=%d, own ver=%d)", - flags & EAP_PEAP_VERSION_MASK, data->fast_version); - if ((flags & EAP_PEAP_VERSION_MASK) < data->fast_version) - data->fast_version = flags & EAP_PEAP_VERSION_MASK; + flags & EAP_TLS_VERSION_MASK, data->fast_version); + if ((flags & EAP_TLS_VERSION_MASK) < data->fast_version) + data->fast_version = flags & EAP_TLS_VERSION_MASK; wpa_printf(MSG_DEBUG, "EAP-FAST: Using FAST version %d", data->fast_version); diff --git a/contrib/wpa/src/eap_peer/eap_fast_pac.c b/contrib/wpa/src/eap_peer/eap_fast_pac.c index 77893d6..541cce5 100644 --- a/contrib/wpa/src/eap_peer/eap_fast_pac.c +++ b/contrib/wpa/src/eap_peer/eap_fast_pac.c @@ -480,8 +480,10 @@ static void eap_fast_write(char **buf, char **pos, size_t *buf_len, { size_t i, need; int ret; + char *end; - if (data == NULL || *buf == NULL) + if (data == NULL || buf == NULL || *buf == NULL || + pos == NULL || *pos == NULL || *pos < *buf) return; need = os_strlen(field) + len * 2 + 30; @@ -498,32 +500,31 @@ static void eap_fast_write(char **buf, char **pos, size_t *buf_len, *buf = nbuf; *buf_len += need; } + end = *buf + *buf_len; - ret = os_snprintf(*pos, *buf + *buf_len - *pos, "%s=", field); - if (ret < 0 || ret >= *buf + *buf_len - *pos) + ret = os_snprintf(*pos, end - *pos, "%s=", field); + if (ret < 0 || ret >= end - *pos) return; *pos += ret; - *pos += wpa_snprintf_hex(*pos, *buf + *buf_len - *pos, data, len); - ret = os_snprintf(*pos, *buf + *buf_len - *pos, "\n"); - if (ret < 0 || ret >= *buf + *buf_len - *pos) + *pos += wpa_snprintf_hex(*pos, end - *pos, data, len); + ret = os_snprintf(*pos, end - *pos, "\n"); + if (ret < 0 || ret >= end - *pos) return; *pos += ret; if (txt) { - ret = os_snprintf(*pos, *buf + *buf_len - *pos, - "%s-txt=", field); - if (ret < 0 || ret >= *buf + *buf_len - *pos) + ret = os_snprintf(*pos, end - *pos, "%s-txt=", field); + if (ret < 0 || ret >= end - *pos) return; *pos += ret; for (i = 0; i < len; i++) { - ret = os_snprintf(*pos, *buf + *buf_len - *pos, - "%c", data[i]); - if (ret < 0 || ret >= *buf + *buf_len - *pos) + ret = os_snprintf(*pos, end - *pos, "%c", data[i]); + if (ret < 0 || ret >= end - *pos) return; *pos += ret; } - ret = os_snprintf(*pos, *buf + *buf_len - *pos, "\n"); - if (ret < 0 || ret >= *buf + *buf_len - *pos) + ret = os_snprintf(*pos, end - *pos, "\n"); + if (ret < 0 || ret >= end - *pos) return; *pos += ret; } diff --git a/contrib/wpa/src/eap_peer/eap_leap.c b/contrib/wpa/src/eap_peer/eap_leap.c index 01c1f16..a7c94a4 100644 --- a/contrib/wpa/src/eap_peer/eap_leap.c +++ b/contrib/wpa/src/eap_peer/eap_leap.c @@ -15,9 +15,9 @@ #include "includes.h" #include "common.h" +#include "crypto/ms_funcs.h" +#include "crypto/crypto.h" #include "eap_i.h" -#include "ms_funcs.h" -#include "crypto.h" #define LEAP_VERSION 1 #define LEAP_CHALLENGE_LEN 8 @@ -233,10 +233,16 @@ static struct wpabuf * eap_leap_process_response(struct eap_sm *sm, void *priv, os_memcpy(data->ap_response, pos, LEAP_RESPONSE_LEN); if (pwhash) { - hash_nt_password_hash(password, pw_hash_hash); + if (hash_nt_password_hash(password, pw_hash_hash)) { + ret->ignore = TRUE; + return NULL; + } } else { - nt_password_hash(password, password_len, pw_hash); - hash_nt_password_hash(pw_hash, pw_hash_hash); + if (nt_password_hash(password, password_len, pw_hash) || + hash_nt_password_hash(pw_hash, pw_hash_hash)) { + ret->ignore = TRUE; + return NULL; + } } challenge_response(data->ap_challenge, pw_hash_hash, expected); @@ -345,11 +351,17 @@ static u8 * eap_leap_getKey(struct eap_sm *sm, void *priv, size_t *len) if (key == NULL) return NULL; - if (pwhash) - hash_nt_password_hash(password, pw_hash_hash); - else { - nt_password_hash(password, password_len, pw_hash); - hash_nt_password_hash(pw_hash, pw_hash_hash); + if (pwhash) { + if (hash_nt_password_hash(password, pw_hash_hash)) { + os_free(key); + return NULL; + } + } else { + if (nt_password_hash(password, password_len, pw_hash) || + hash_nt_password_hash(pw_hash, pw_hash_hash)) { + os_free(key); + return NULL; + } } wpa_hexdump_key(MSG_DEBUG, "EAP-LEAP: pw_hash_hash", pw_hash_hash, 16); diff --git a/contrib/wpa/src/eap_peer/eap_md5.c b/contrib/wpa/src/eap_peer/eap_md5.c index 7961143..0edbae8 100644 --- a/contrib/wpa/src/eap_peer/eap_md5.c +++ b/contrib/wpa/src/eap_peer/eap_md5.c @@ -76,7 +76,7 @@ static struct wpabuf * eap_md5_process(struct eap_sm *sm, void *priv, wpa_printf(MSG_DEBUG, "EAP-MD5: Generating Challenge Response"); ret->methodState = METHOD_DONE; - ret->decision = DECISION_UNCOND_SUCC; + ret->decision = DECISION_COND_SUCC; ret->allowNotifications = TRUE; resp = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_MD5, 1 + CHAP_MD5_LEN, diff --git a/contrib/wpa/src/eap_peer/eap_methods.c b/contrib/wpa/src/eap_peer/eap_methods.c index 2374e5e..3b0af05 100644 --- a/contrib/wpa/src/eap_peer/eap_methods.c +++ b/contrib/wpa/src/eap_peer/eap_methods.c @@ -340,161 +340,6 @@ int eap_peer_method_register(struct eap_method *method) /** - * eap_peer_register_methods - Register statically linked EAP peer methods - * Returns: 0 on success, -1 on failure - * - * This function is called at program initialization to register all EAP peer - * methods that were linked in statically. - */ -int eap_peer_register_methods(void) -{ - int ret = 0; - -#ifdef EAP_MD5 - if (ret == 0) { - int eap_peer_md5_register(void); - ret = eap_peer_md5_register(); - } -#endif /* EAP_MD5 */ - -#ifdef EAP_TLS - if (ret == 0) { - int eap_peer_tls_register(void); - ret = eap_peer_tls_register(); - } -#endif /* EAP_TLS */ - -#ifdef EAP_MSCHAPv2 - if (ret == 0) { - int eap_peer_mschapv2_register(void); - ret = eap_peer_mschapv2_register(); - } -#endif /* EAP_MSCHAPv2 */ - -#ifdef EAP_PEAP - if (ret == 0) { - int eap_peer_peap_register(void); - ret = eap_peer_peap_register(); - } -#endif /* EAP_PEAP */ - -#ifdef EAP_TTLS - if (ret == 0) { - int eap_peer_ttls_register(void); - ret = eap_peer_ttls_register(); - } -#endif /* EAP_TTLS */ - -#ifdef EAP_GTC - if (ret == 0) { - int eap_peer_gtc_register(void); - ret = eap_peer_gtc_register(); - } -#endif /* EAP_GTC */ - -#ifdef EAP_OTP - if (ret == 0) { - int eap_peer_otp_register(void); - ret = eap_peer_otp_register(); - } -#endif /* EAP_OTP */ - -#ifdef EAP_SIM - if (ret == 0) { - int eap_peer_sim_register(void); - ret = eap_peer_sim_register(); - } -#endif /* EAP_SIM */ - -#ifdef EAP_LEAP - if (ret == 0) { - int eap_peer_leap_register(void); - ret = eap_peer_leap_register(); - } -#endif /* EAP_LEAP */ - -#ifdef EAP_PSK - if (ret == 0) { - int eap_peer_psk_register(void); - ret = eap_peer_psk_register(); - } -#endif /* EAP_PSK */ - -#ifdef EAP_AKA - if (ret == 0) { - int eap_peer_aka_register(void); - ret = eap_peer_aka_register(); - } -#endif /* EAP_AKA */ - -#ifdef EAP_AKA_PRIME - if (ret == 0) { - int eap_peer_aka_prime_register(void); - ret = eap_peer_aka_prime_register(); - } -#endif /* EAP_AKA_PRIME */ - -#ifdef EAP_FAST - if (ret == 0) { - int eap_peer_fast_register(void); - ret = eap_peer_fast_register(); - } -#endif /* EAP_FAST */ - -#ifdef EAP_PAX - if (ret == 0) { - int eap_peer_pax_register(void); - ret = eap_peer_pax_register(); - } -#endif /* EAP_PAX */ - -#ifdef EAP_SAKE - if (ret == 0) { - int eap_peer_sake_register(void); - ret = eap_peer_sake_register(); - } -#endif /* EAP_SAKE */ - -#ifdef EAP_GPSK - if (ret == 0) { - int eap_peer_gpsk_register(void); - ret = eap_peer_gpsk_register(); - } -#endif /* EAP_GPSK */ - -#ifdef EAP_WSC - if (ret == 0) { - int eap_peer_wsc_register(void); - ret = eap_peer_wsc_register(); - } -#endif /* EAP_WSC */ - -#ifdef EAP_IKEV2 - if (ret == 0) { - int eap_peer_ikev2_register(void); - ret = eap_peer_ikev2_register(); - } -#endif /* EAP_IKEV2 */ - -#ifdef EAP_VENDOR_TEST - if (ret == 0) { - int eap_peer_vendor_test_register(void); - ret = eap_peer_vendor_test_register(); - } -#endif /* EAP_VENDOR_TEST */ - -#ifdef EAP_TNC - if (ret == 0) { - int eap_peer_tnc_register(void); - ret = eap_peer_tnc_register(); - } -#endif /* EAP_TNC */ - - return ret; -} - - -/** * eap_peer_unregister_methods - Unregister EAP peer methods * * This function is called at program termination to unregister all EAP peer diff --git a/contrib/wpa/src/eap_peer/eap_methods.h b/contrib/wpa/src/eap_peer/eap_methods.h index 9fd9b51..384c61b 100644 --- a/contrib/wpa/src/eap_peer/eap_methods.h +++ b/contrib/wpa/src/eap_peer/eap_methods.h @@ -32,7 +32,6 @@ EapType eap_peer_get_type(const char *name, int *vendor); const char * eap_get_name(int vendor, EapType type); size_t eap_get_names(char *buf, size_t buflen); char ** eap_get_names_as_string_array(size_t *num); -int eap_peer_register_methods(void); void eap_peer_unregister_methods(void); #else /* IEEE8021X_EAPOL */ @@ -89,4 +88,26 @@ static inline int eap_peer_method_unload(struct eap_method *method) #endif /* CONFIG_DYNAMIC_EAP_METHODS */ +/* EAP peer method registration calls for statically linked in methods */ +int eap_peer_md5_register(void); +int eap_peer_tls_register(void); +int eap_peer_mschapv2_register(void); +int eap_peer_peap_register(void); +int eap_peer_ttls_register(void); +int eap_peer_gtc_register(void); +int eap_peer_otp_register(void); +int eap_peer_sim_register(void); +int eap_peer_leap_register(void); +int eap_peer_psk_register(void); +int eap_peer_aka_register(void); +int eap_peer_aka_prime_register(void); +int eap_peer_fast_register(void); +int eap_peer_pax_register(void); +int eap_peer_sake_register(void); +int eap_peer_gpsk_register(void); +int eap_peer_wsc_register(void); +int eap_peer_ikev2_register(void); +int eap_peer_vendor_test_register(void); +int eap_peer_tnc_register(void); + #endif /* EAP_METHODS_H */ diff --git a/contrib/wpa/src/eap_peer/eap_mschapv2.c b/contrib/wpa/src/eap_peer/eap_mschapv2.c index b0c3ab7..cd410d9 100644 --- a/contrib/wpa/src/eap_peer/eap_mschapv2.c +++ b/contrib/wpa/src/eap_peer/eap_mschapv2.c @@ -22,11 +22,11 @@ #include "includes.h" #include "common.h" +#include "crypto/ms_funcs.h" +#include "common/wpa_ctrl.h" +#include "mschapv2.h" #include "eap_i.h" #include "eap_config.h" -#include "ms_funcs.h" -#include "wpa_ctrl.h" -#include "mschapv2.h" #ifdef _MSC_VER @@ -209,10 +209,15 @@ static struct wpabuf * eap_mschapv2_challenge_reply( "in Phase 1"); auth_challenge = data->auth_challenge; } - mschapv2_derive_response(identity, identity_len, password, - password_len, pwhash, auth_challenge, - peer_challenge, r->nt_response, - data->auth_response, data->master_key); + if (mschapv2_derive_response(identity, identity_len, password, + password_len, pwhash, auth_challenge, + peer_challenge, r->nt_response, + data->auth_response, data->master_key)) { + wpa_printf(MSG_ERROR, "EAP-MSCHAPV2: Failed to derive " + "response"); + wpabuf_free(resp); + return NULL; + } data->auth_response_valid = 1; data->master_key_valid = 1; diff --git a/contrib/wpa/src/eap_peer/eap_pax.c b/contrib/wpa/src/eap_peer/eap_pax.c index afd56dd..2e04831 100644 --- a/contrib/wpa/src/eap_peer/eap_pax.c +++ b/contrib/wpa/src/eap_peer/eap_pax.c @@ -15,10 +15,8 @@ #include "includes.h" #include "common.h" -#include "eap_peer/eap_i.h" #include "eap_common/eap_pax_common.h" -#include "sha1.h" -#include "crypto.h" +#include "eap_i.h" /* * Note: only PAX_STD subprotocol is currently supported diff --git a/contrib/wpa/src/eap_peer/eap_peap.c b/contrib/wpa/src/eap_peer/eap_peap.c index 894fc63..2b72084 100644 --- a/contrib/wpa/src/eap_peer/eap_peap.c +++ b/contrib/wpa/src/eap_peer/eap_peap.c @@ -16,12 +16,12 @@ #include "common.h" #include "crypto/sha1.h" +#include "crypto/tls.h" +#include "eap_common/eap_tlv_common.h" +#include "eap_common/eap_peap_common.h" #include "eap_i.h" #include "eap_tls_common.h" #include "eap_config.h" -#include "tls.h" -#include "eap_common/eap_tlv_common.h" -#include "eap_common/eap_peap_common.h" #include "tncc.h" @@ -1048,10 +1048,10 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv, if (flags & EAP_TLS_FLAGS_START) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Start (server ver=%d, own " - "ver=%d)", flags & EAP_PEAP_VERSION_MASK, + "ver=%d)", flags & EAP_TLS_VERSION_MASK, data->peap_version); - if ((flags & EAP_PEAP_VERSION_MASK) < data->peap_version) - data->peap_version = flags & EAP_PEAP_VERSION_MASK; + if ((flags & EAP_TLS_VERSION_MASK) < data->peap_version) + data->peap_version = flags & EAP_TLS_VERSION_MASK; if (data->force_peap_version >= 0 && data->force_peap_version != data->peap_version) { wpa_printf(MSG_WARNING, "EAP-PEAP: Failed to select " diff --git a/contrib/wpa/src/eap_peer/eap_psk.c b/contrib/wpa/src/eap_peer/eap_psk.c index 1ce63566..ccf871e 100644 --- a/contrib/wpa/src/eap_peer/eap_psk.c +++ b/contrib/wpa/src/eap_peer/eap_psk.c @@ -18,9 +18,9 @@ #include "includes.h" #include "common.h" -#include "eap_peer/eap_i.h" -#include "aes_wrap.h" +#include "crypto/aes_wrap.h" #include "eap_common/eap_psk_common.h" +#include "eap_i.h" struct eap_psk_data { diff --git a/contrib/wpa/src/eap_peer/eap_sim.c b/contrib/wpa/src/eap_peer/eap_sim.c index 5e30d1f..3d8afb2 100644 --- a/contrib/wpa/src/eap_peer/eap_sim.c +++ b/contrib/wpa/src/eap_peer/eap_sim.c @@ -15,13 +15,11 @@ #include "includes.h" #include "common.h" +#include "pcsc_funcs.h" +#include "crypto/milenage.h" #include "eap_peer/eap_i.h" #include "eap_config.h" -#include "pcsc_funcs.h" #include "eap_common/eap_sim_common.h" -#ifdef CONFIG_SIM_SIMULATOR -#include "hlr_auc_gw/milenage.h" -#endif /* CONFIG_SIM_SIMULATOR */ struct eap_sim_data { diff --git a/contrib/wpa/src/eap_peer/eap_tls.c b/contrib/wpa/src/eap_peer/eap_tls.c index 31344a9..20b2212 100644 --- a/contrib/wpa/src/eap_peer/eap_tls.c +++ b/contrib/wpa/src/eap_peer/eap_tls.c @@ -15,10 +15,10 @@ #include "includes.h" #include "common.h" +#include "crypto/tls.h" #include "eap_i.h" #include "eap_tls_common.h" #include "eap_config.h" -#include "tls.h" static void eap_tls_deinit(struct eap_sm *sm, void *priv); diff --git a/contrib/wpa/src/eap_peer/eap_tls_common.c b/contrib/wpa/src/eap_peer/eap_tls_common.c index 186feaa..7bd50f6 100644 --- a/contrib/wpa/src/eap_peer/eap_tls_common.c +++ b/contrib/wpa/src/eap_peer/eap_tls_common.c @@ -1,6 +1,6 @@ /* * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions - * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -15,11 +15,11 @@ #include "includes.h" #include "common.h" +#include "crypto/sha1.h" +#include "crypto/tls.h" #include "eap_i.h" #include "eap_tls_common.h" #include "eap_config.h" -#include "sha1.h" -#include "tls.h" static int eap_tls_check_blob(struct eap_sm *sm, const char **name, @@ -169,10 +169,14 @@ static int eap_tls_init_connection(struct eap_sm *sm, config->pin = NULL; eap_sm_request_pin(sm); sm->ignore = TRUE; + tls_connection_deinit(sm->ssl_ctx, data->conn); + data->conn = NULL; return -1; } else if (res) { wpa_printf(MSG_INFO, "TLS: Failed to set TLS connection " "parameters"); + tls_connection_deinit(sm->ssl_ctx, data->conn); + data->conn = NULL; return -1; } @@ -310,27 +314,29 @@ fail: * eap_peer_tls_reassemble_fragment - Reassemble a received fragment * @data: Data for TLS processing * @in_data: Next incoming TLS segment - * @in_len: Length of in_data * Returns: 0 on success, 1 if more data is needed for the full message, or * -1 on error */ static int eap_peer_tls_reassemble_fragment(struct eap_ssl_data *data, - const u8 *in_data, size_t in_len) + const struct wpabuf *in_data) { - u8 *buf; + size_t tls_in_len, in_len; + + tls_in_len = data->tls_in ? wpabuf_len(data->tls_in) : 0; + in_len = in_data ? wpabuf_len(in_data) : 0; - if (data->tls_in_len + in_len == 0) { + if (tls_in_len + in_len == 0) { /* No message data received?! */ wpa_printf(MSG_WARNING, "SSL: Invalid reassembly state: " "tls_in_left=%lu tls_in_len=%lu in_len=%lu", (unsigned long) data->tls_in_left, - (unsigned long) data->tls_in_len, + (unsigned long) tls_in_len, (unsigned long) in_len); eap_peer_tls_reset_input(data); return -1; } - if (data->tls_in_len + in_len > 65536) { + if (tls_in_len + in_len > 65536) { /* * Limit length to avoid rogue servers from causing large * memory allocations. @@ -349,16 +355,13 @@ static int eap_peer_tls_reassemble_fragment(struct eap_ssl_data *data, return -1; } - buf = os_realloc(data->tls_in, data->tls_in_len + in_len); - if (buf == NULL) { + if (wpabuf_resize(&data->tls_in, in_len) < 0) { wpa_printf(MSG_INFO, "SSL: Could not allocate memory for TLS " "data"); eap_peer_tls_reset_input(data); return -1; } - os_memcpy(buf + data->tls_in_len, in_data, in_len); - data->tls_in = buf; - data->tls_in_len += in_len; + wpabuf_put_buf(data->tls_in, in_data); data->tls_in_left -= in_len; if (data->tls_in_left > 0) { @@ -375,8 +378,6 @@ static int eap_peer_tls_reassemble_fragment(struct eap_ssl_data *data, * eap_peer_tls_data_reassemble - Reassemble TLS data * @data: Data for TLS processing * @in_data: Next incoming TLS segment - * @in_len: Length of in_data - * @out_len: Variable for returning length of the reassembled message * @need_more_input: Variable for returning whether more input data is needed * to reassemble this TLS packet * Returns: Pointer to output data, %NULL on error or when more data is needed @@ -385,16 +386,15 @@ static int eap_peer_tls_reassemble_fragment(struct eap_ssl_data *data, * This function reassembles TLS fragments. Caller must not free the returned * data buffer since an internal pointer to it is maintained. */ -const u8 * eap_peer_tls_data_reassemble( - struct eap_ssl_data *data, const u8 *in_data, size_t in_len, - size_t *out_len, int *need_more_input) +static const struct wpabuf * eap_peer_tls_data_reassemble( + struct eap_ssl_data *data, const struct wpabuf *in_data, + int *need_more_input) { *need_more_input = 0; - if (data->tls_in_left > in_len || data->tls_in) { + if (data->tls_in_left > wpabuf_len(in_data) || data->tls_in) { /* Message has fragments */ - int res = eap_peer_tls_reassemble_fragment(data, in_data, - in_len); + int res = eap_peer_tls_reassemble_fragment(data, in_data); if (res) { if (res == 1) *need_more_input = 1; @@ -405,14 +405,11 @@ const u8 * eap_peer_tls_data_reassemble( } else { /* No fragments in this message, so just make a copy of it. */ data->tls_in_left = 0; - data->tls_in = os_malloc(in_len ? in_len : 1); + data->tls_in = wpabuf_dup(in_data); if (data->tls_in == NULL) return NULL; - os_memcpy(data->tls_in, in_data, in_len); - data->tls_in_len = in_len; } - *out_len = data->tls_in_len; return data->tls_in; } @@ -431,14 +428,13 @@ static int eap_tls_process_input(struct eap_sm *sm, struct eap_ssl_data *data, const u8 *in_data, size_t in_len, struct wpabuf **out_data) { - const u8 *msg; - size_t msg_len; + const struct wpabuf *msg; int need_more_input; - u8 *appl_data; - size_t appl_data_len; + struct wpabuf *appl_data; + struct wpabuf buf; - msg = eap_peer_tls_data_reassemble(data, in_data, in_len, - &msg_len, &need_more_input); + wpabuf_set(&buf, in_data, in_len); + msg = eap_peer_tls_data_reassemble(data, &buf, &need_more_input); if (msg == NULL) return need_more_input ? 1 : -1; @@ -447,31 +443,25 @@ static int eap_tls_process_input(struct eap_sm *sm, struct eap_ssl_data *data, /* This should not happen.. */ wpa_printf(MSG_INFO, "SSL: eap_tls_process_input - pending " "tls_out data even though tls_out_len = 0"); - os_free(data->tls_out); + wpabuf_free(data->tls_out); WPA_ASSERT(data->tls_out == NULL); } appl_data = NULL; data->tls_out = tls_connection_handshake(sm->ssl_ctx, data->conn, - msg, msg_len, - &data->tls_out_len, - &appl_data, &appl_data_len); + msg, &appl_data); eap_peer_tls_reset_input(data); if (appl_data && tls_connection_established(sm->ssl_ctx, data->conn) && !tls_connection_get_failed(sm->ssl_ctx, data->conn)) { - wpa_hexdump_key(MSG_MSGDUMP, "SSL: Application data", - appl_data, appl_data_len); - *out_data = wpabuf_alloc_ext_data(appl_data, appl_data_len); - if (*out_data == NULL) { - os_free(appl_data); - return -1; - } + wpa_hexdump_buf_key(MSG_MSGDUMP, "SSL: Application data", + appl_data); + *out_data = appl_data; return 2; } - os_free(appl_data); + wpabuf_free(appl_data); return 0; } @@ -494,11 +484,14 @@ static int eap_tls_process_output(struct eap_ssl_data *data, EapType eap_type, size_t len; u8 *flags; int more_fragments, length_included; - - len = data->tls_out_len - data->tls_out_pos; + + if (data->tls_out == NULL) + return -1; + len = wpabuf_len(data->tls_out) - data->tls_out_pos; wpa_printf(MSG_DEBUG, "SSL: %lu bytes left to be sent out (of total " "%lu bytes)", - (unsigned long) len, (unsigned long) data->tls_out_len); + (unsigned long) len, + (unsigned long) wpabuf_len(data->tls_out)); /* * Limit outgoing message to the configured maximum size. Fragment @@ -513,7 +506,7 @@ static int eap_tls_process_output(struct eap_ssl_data *data, EapType eap_type, more_fragments = 0; length_included = data->tls_out_pos == 0 && - (data->tls_out_len > data->tls_out_limit || + (wpabuf_len(data->tls_out) > data->tls_out_limit || data->include_tls_length); if (!length_included && eap_type == EAP_TYPE_PEAP && peap_version == 0 && @@ -539,10 +532,12 @@ static int eap_tls_process_output(struct eap_ssl_data *data, EapType eap_type, *flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS; if (length_included) { *flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED; - wpabuf_put_be32(*out_data, data->tls_out_len); + wpabuf_put_be32(*out_data, wpabuf_len(data->tls_out)); } - wpabuf_put_data(*out_data, &data->tls_out[data->tls_out_pos], len); + wpabuf_put_data(*out_data, + wpabuf_head_u8(data->tls_out) + data->tls_out_pos, + len); data->tls_out_pos += len; if (!more_fragments) @@ -590,13 +585,13 @@ int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, *out_data = NULL; - if (data->tls_out_len > 0 && in_len > 0) { + if (data->tls_out && wpabuf_len(data->tls_out) > 0 && in_len > 0) { wpa_printf(MSG_DEBUG, "SSL: Received non-ACK when output " "fragments are waiting to be sent out"); return -1; } - if (data->tls_out_len == 0) { + if (data->tls_out == NULL || wpabuf_len(data->tls_out) == 0) { /* * No more data to send out - expect to receive more data from * the AS. @@ -635,14 +630,14 @@ int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, /* TODO: clean pin if engine used? */ } - if (data->tls_out_len == 0) { + if (data->tls_out == NULL || wpabuf_len(data->tls_out) == 0) { /* * TLS negotiation should now be complete since all other cases * needing more data should have been caught above based on * the TLS Message Length field. */ wpa_printf(MSG_DEBUG, "SSL: No data to be sent out"); - os_free(data->tls_out); + wpabuf_free(data->tls_out); data->tls_out = NULL; return 1; } @@ -794,9 +789,8 @@ const u8 * eap_peer_tls_process_init(struct eap_sm *sm, if (data->tls_in_left == 0) { data->tls_in_total = tls_msg_len; data->tls_in_left = tls_msg_len; - os_free(data->tls_in); + wpabuf_free(data->tls_in); data->tls_in = NULL; - data->tls_in_len = 0; } pos += 4; left -= 4; @@ -821,8 +815,8 @@ const u8 * eap_peer_tls_process_init(struct eap_sm *sm, */ void eap_peer_tls_reset_input(struct eap_ssl_data *data) { - data->tls_in_left = data->tls_in_total = data->tls_in_len = 0; - os_free(data->tls_in); + data->tls_in_left = data->tls_in_total = 0; + wpabuf_free(data->tls_in); data->tls_in = NULL; } @@ -836,9 +830,8 @@ void eap_peer_tls_reset_input(struct eap_ssl_data *data) */ void eap_peer_tls_reset_output(struct eap_ssl_data *data) { - data->tls_out_len = 0; data->tls_out_pos = 0; - os_free(data->tls_out); + wpabuf_free(data->tls_out); data->tls_out = NULL; } @@ -855,44 +848,19 @@ int eap_peer_tls_decrypt(struct eap_sm *sm, struct eap_ssl_data *data, const struct wpabuf *in_data, struct wpabuf **in_decrypted) { - int res; - const u8 *msg; - size_t msg_len, buf_len; + const struct wpabuf *msg; int need_more_input; - msg = eap_peer_tls_data_reassemble(data, wpabuf_head(in_data), - wpabuf_len(in_data), &msg_len, - &need_more_input); + msg = eap_peer_tls_data_reassemble(data, in_data, &need_more_input); if (msg == NULL) return need_more_input ? 1 : -1; - buf_len = wpabuf_len(in_data); - if (data->tls_in_total > buf_len) - buf_len = data->tls_in_total; - /* - * Even though we try to disable TLS compression, it is possible that - * this cannot be done with all TLS libraries. Add extra buffer space - * to handle the possibility of the decrypted data being longer than - * input data. - */ - buf_len += 500; - buf_len *= 3; - *in_decrypted = wpabuf_alloc(buf_len ? buf_len : 1); - if (*in_decrypted == NULL) { - eap_peer_tls_reset_input(data); - wpa_printf(MSG_WARNING, "SSL: Failed to allocate memory for " - "decryption"); - return -1; - } - - res = tls_connection_decrypt(sm->ssl_ctx, data->conn, msg, msg_len, - wpabuf_mhead(*in_decrypted), buf_len); + *in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->conn, msg); eap_peer_tls_reset_input(data); - if (res < 0) { + if (*in_decrypted == NULL) { wpa_printf(MSG_INFO, "SSL: Failed to decrypt Phase 2 data"); return -1; } - wpabuf_put(*in_decrypted, res); return 0; } @@ -913,29 +881,17 @@ int eap_peer_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data, const struct wpabuf *in_data, struct wpabuf **out_data) { - int res; - size_t len; - if (in_data) { eap_peer_tls_reset_output(data); - len = wpabuf_len(in_data) + 300; - data->tls_out = os_malloc(len); - if (data->tls_out == NULL) - return -1; - - res = tls_connection_encrypt(sm->ssl_ctx, data->conn, - wpabuf_head(in_data), - wpabuf_len(in_data), - data->tls_out, len); - if (res < 0) { + data->tls_out = tls_connection_encrypt(sm->ssl_ctx, data->conn, + in_data); + if (data->tls_out == NULL) { wpa_printf(MSG_INFO, "SSL: Failed to encrypt Phase 2 " "data (in_len=%lu)", (unsigned long) wpabuf_len(in_data)); eap_peer_tls_reset_output(data); return -1; } - - data->tls_out_len = res; } return eap_tls_process_output(data, eap_type, peap_version, id, 0, diff --git a/contrib/wpa/src/eap_peer/eap_tls_common.h b/contrib/wpa/src/eap_peer/eap_tls_common.h index 2c87427..e9e0998 100644 --- a/contrib/wpa/src/eap_peer/eap_tls_common.h +++ b/contrib/wpa/src/eap_peer/eap_tls_common.h @@ -1,6 +1,6 @@ /* * EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions - * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -27,12 +27,7 @@ struct eap_ssl_data { /** * tls_out - TLS message to be sent out in fragments */ - u8 *tls_out; - - /** - * tls_out_len - Total length of the outgoing TLS message - */ - size_t tls_out_len; + struct wpabuf *tls_out; /** * tls_out_pos - The current position in the outgoing TLS message @@ -47,12 +42,7 @@ struct eap_ssl_data { /** * tls_in - Received TLS message buffer for re-assembly */ - u8 *tls_in; - - /** - * tls_in_len - Number of bytes of the received TLS message in tls_in - */ - size_t tls_in_len; + struct wpabuf *tls_in; /** * tls_in_left - Number of remaining bytes in the incoming TLS message @@ -81,7 +71,7 @@ struct eap_ssl_data { int tls_ia; /** - * eap - Pointer to EAP state machine allocated with eap_peer_sm_init() + * eap - EAP state machine allocated with eap_peer_sm_init() */ struct eap_sm *eap; }; @@ -91,7 +81,7 @@ struct eap_ssl_data { #define EAP_TLS_FLAGS_LENGTH_INCLUDED 0x80 #define EAP_TLS_FLAGS_MORE_FRAGMENTS 0x40 #define EAP_TLS_FLAGS_START 0x20 -#define EAP_PEAP_VERSION_MASK 0x07 +#define EAP_TLS_VERSION_MASK 0x07 /* could be up to 128 bytes, but only the first 64 bytes are used */ #define EAP_TLS_KEY_LEN 64 @@ -102,9 +92,6 @@ int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data); u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data, const char *label, size_t len); -const u8 * eap_peer_tls_data_reassemble( - struct eap_ssl_data *data, const u8 *in_data, size_t in_len, - size_t *out_len, int *need_more_input); int eap_peer_tls_process_helper(struct eap_sm *sm, struct eap_ssl_data *data, EapType eap_type, int peap_version, u8 id, const u8 *in_data, size_t in_len, diff --git a/contrib/wpa/src/eap_peer/eap_tnc.c b/contrib/wpa/src/eap_peer/eap_tnc.c index c560015..6c95f72 100644 --- a/contrib/wpa/src/eap_peer/eap_tnc.c +++ b/contrib/wpa/src/eap_peer/eap_tnc.c @@ -73,12 +73,13 @@ static struct wpabuf * eap_tnc_build_frag_ack(u8 id, u8 code) { struct wpabuf *msg; - msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 0, code, id); + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, code, id); if (msg == NULL) { wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory " "for fragment ack"); return NULL; } + wpabuf_put_u8(msg, EAP_TNC_VERSION); /* Flags */ wpa_printf(MSG_DEBUG, "EAP-TNC: Send fragment ack"); @@ -262,7 +263,7 @@ static struct wpabuf * eap_tnc_process(struct eap_sm *sm, void *priv, "Message Length %u", flags, message_length); if (data->state == WAIT_FRAG_ACK) { - if (len != 0) { + if (len > 1) { wpa_printf(MSG_DEBUG, "EAP-TNC: Unexpected payload in " "WAIT_FRAG_ACK state"); ret->ignore = TRUE; diff --git a/contrib/wpa/src/eap_peer/eap_ttls.c b/contrib/wpa/src/eap_peer/eap_ttls.c index 0851f8b..2573780 100644 --- a/contrib/wpa/src/eap_peer/eap_ttls.c +++ b/contrib/wpa/src/eap_peer/eap_ttls.c @@ -15,15 +15,15 @@ #include "includes.h" #include "common.h" -#include "eap_peer/eap_i.h" -#include "eap_peer/eap_tls_common.h" -#include "eap_peer/eap_config.h" -#include "ms_funcs.h" -#include "sha1.h" +#include "crypto/ms_funcs.h" +#include "crypto/sha1.h" +#include "crypto/tls.h" #include "eap_common/chap.h" -#include "tls.h" -#include "mschapv2.h" #include "eap_common/eap_ttls.h" +#include "mschapv2.h" +#include "eap_i.h" +#include "eap_tls_common.h" +#include "eap_config.h" /* Maximum supported TTLS version @@ -691,10 +691,15 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm, pos += EAP_TTLS_MSCHAPV2_CHALLENGE_LEN; os_memset(pos, 0, 8); /* Reserved, must be zero */ pos += 8; - mschapv2_derive_response(identity, identity_len, password, - password_len, pwhash, challenge, - peer_challenge, pos, data->auth_response, - data->master_key); + if (mschapv2_derive_response(identity, identity_len, password, + password_len, pwhash, challenge, + peer_challenge, pos, data->auth_response, + data->master_key)) { + wpabuf_free(msg); + wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive " + "response"); + return -1; + } data->auth_response_valid = 1; eap_ttlsv1_permute_inner(sm, data); @@ -1026,27 +1031,25 @@ static int eap_ttls_phase2_request(struct eap_sm *sm, static struct wpabuf * eap_ttls_build_phase_finished( struct eap_sm *sm, struct eap_ttls_data *data, int id, int final) { - int len; - struct wpabuf *req; - u8 *pos; - const int max_len = 300; + struct wpabuf *req, *buf; - req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TTLS, 1 + max_len, - EAP_CODE_RESPONSE, id); - if (req == NULL) + buf = tls_connection_ia_send_phase_finished(sm->ssl_ctx, + data->ssl.conn, + final); + if (buf == NULL) return NULL; - wpabuf_put_u8(req, data->ttls_version); - - pos = wpabuf_put(req, 0); - len = tls_connection_ia_send_phase_finished(sm->ssl_ctx, - data->ssl.conn, - final, pos, max_len); - if (len < 0) { - wpabuf_free(req); + req = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TTLS, + 1 + wpabuf_len(buf), + EAP_CODE_RESPONSE, id); + if (req == NULL) { + wpabuf_free(buf); return NULL; } - wpabuf_put(req, len); + + wpabuf_put_u8(req, data->ttls_version); + wpabuf_put_buf(req, buf); + wpabuf_free(buf); eap_update_len(req); return req; @@ -1666,10 +1669,10 @@ static int eap_ttls_process_start(struct eap_sm *sm, struct eap_peer_config *config = eap_get_config(sm); wpa_printf(MSG_DEBUG, "EAP-TTLS: Start (server ver=%d, own ver=%d)", - flags & EAP_PEAP_VERSION_MASK, data->ttls_version); + flags & EAP_TLS_VERSION_MASK, data->ttls_version); #if EAP_TTLS_VERSION > 0 - if ((flags & EAP_PEAP_VERSION_MASK) < data->ttls_version) - data->ttls_version = flags & EAP_PEAP_VERSION_MASK; + if ((flags & EAP_TLS_VERSION_MASK) < data->ttls_version) + data->ttls_version = flags & EAP_TLS_VERSION_MASK; if (data->force_ttls_version >= 0 && data->force_ttls_version != data->ttls_version) { wpa_printf(MSG_WARNING, "EAP-TTLS: Failed to select " diff --git a/contrib/wpa/src/eap_peer/eap_wsc.c b/contrib/wpa/src/eap_peer/eap_wsc.c index 7c8ad2f..8317f72 100644 --- a/contrib/wpa/src/eap_peer/eap_wsc.c +++ b/contrib/wpa/src/eap_peer/eap_wsc.c @@ -1,6 +1,6 @@ /* * EAP-WSC peer for Wi-Fi Protected Setup - * Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -65,6 +65,72 @@ static void eap_wsc_state(struct eap_wsc_data *data, int state) } +static int eap_wsc_new_ap_settings(struct wps_credential *cred, + const char *params) +{ + const char *pos, *end; + size_t len; + + os_memset(cred, 0, sizeof(*cred)); + + pos = os_strstr(params, "new_ssid="); + if (pos == NULL) + return 0; + pos += 9; + end = os_strchr(pos, ' '); + if (end == NULL) + len = os_strlen(pos); + else + len = end - pos; + if ((len & 1) || len > 2 * sizeof(cred->ssid) || + hexstr2bin(pos, cred->ssid, len / 2)) + return -1; + cred->ssid_len = len / 2; + + pos = os_strstr(params, "new_auth="); + if (pos == NULL) + return -1; + if (os_strncmp(pos + 9, "OPEN", 4) == 0) + cred->auth_type = WPS_AUTH_OPEN; + else if (os_strncmp(pos + 9, "WPAPSK", 6) == 0) + cred->auth_type = WPS_AUTH_WPAPSK; + else if (os_strncmp(pos + 9, "WPA2PSK", 7) == 0) + cred->auth_type = WPS_AUTH_WPA2PSK; + else + return -1; + + pos = os_strstr(params, "new_encr="); + if (pos == NULL) + return -1; + if (os_strncmp(pos + 9, "NONE", 4) == 0) + cred->encr_type = WPS_ENCR_NONE; + else if (os_strncmp(pos + 9, "WEP", 3) == 0) + cred->encr_type = WPS_ENCR_WEP; + else if (os_strncmp(pos + 9, "TKIP", 4) == 0) + cred->encr_type = WPS_ENCR_TKIP; + else if (os_strncmp(pos + 9, "CCMP", 4) == 0) + cred->encr_type = WPS_ENCR_AES; + else + return -1; + + pos = os_strstr(params, "new_key="); + if (pos == NULL) + return 0; + pos += 8; + end = os_strchr(pos, ' '); + if (end == NULL) + len = os_strlen(pos); + else + len = end - pos; + if ((len & 1) || len > 2 * sizeof(cred->key) || + hexstr2bin(pos, cred->key, len / 2)) + return -1; + cred->key_len = len / 2; + + return 1; +} + + static void * eap_wsc_init(struct eap_sm *sm) { struct eap_wsc_data *data; @@ -75,6 +141,8 @@ static void * eap_wsc_init(struct eap_sm *sm) const char *pos; const char *phase1; struct wps_context *wps; + struct wps_credential new_ap_settings; + int res; wps = sm->wps; if (wps == NULL) { @@ -135,6 +203,17 @@ static void * eap_wsc_init(struct eap_sm *sm) return NULL; } + res = eap_wsc_new_ap_settings(&new_ap_settings, phase1); + if (res < 0) { + os_free(data); + return NULL; + } + if (res == 1) { + wpa_printf(MSG_DEBUG, "EAP-WSC: Provide new AP settings for " + "WPS"); + cfg.new_ap_settings = &new_ap_settings; + } + data->wps = wps_init(&cfg); if (data->wps == NULL) { os_free(data); @@ -147,6 +226,10 @@ static void * eap_wsc_init(struct eap_sm *sm) cfg.pin, cfg.pin_len, 0); } + /* Use reduced client timeout for WPS to avoid long wait */ + if (sm->ClientTimeout > 30) + sm->ClientTimeout = 30; + return data; } @@ -302,6 +385,7 @@ static struct wpabuf * eap_wsc_process(struct eap_sm *sm, void *priv, u16 message_length = 0; enum wps_process_res res; struct wpabuf tmpbuf; + struct wpabuf *r; pos = eap_hdr_validate(EAP_VENDOR_WFA, EAP_VENDOR_TYPE_WSC, reqData, &len); @@ -427,7 +511,13 @@ send_msg: } eap_wsc_state(data, MESG); - return eap_wsc_build_msg(data, ret, id); + r = eap_wsc_build_msg(data, ret, id); + if (data->state == FAIL && ret->methodState == METHOD_DONE) { + /* Use reduced client timeout for WPS to avoid long wait */ + if (sm->ClientTimeout > 2) + sm->ClientTimeout = 2; + } + return r; } diff --git a/contrib/wpa/src/eap_peer/ikev2.c b/contrib/wpa/src/eap_peer/ikev2.c index 9172e1f..309a331 100644 --- a/contrib/wpa/src/eap_peer/ikev2.c +++ b/contrib/wpa/src/eap_peer/ikev2.c @@ -15,7 +15,7 @@ #include "includes.h" #include "common.h" -#include "dh_groups.h" +#include "crypto/dh_groups.h" #include "ikev2.h" diff --git a/contrib/wpa/src/eap_peer/mschapv2.c b/contrib/wpa/src/eap_peer/mschapv2.c index 01c22d8..b8fb075 100644 --- a/contrib/wpa/src/eap_peer/mschapv2.c +++ b/contrib/wpa/src/eap_peer/mschapv2.c @@ -15,7 +15,7 @@ #include "includes.h" #include "common.h" -#include "ms_funcs.h" +#include "crypto/ms_funcs.h" #include "mschapv2.h" const u8 * mschapv2_remove_domain(const u8 *username, size_t *len) @@ -39,13 +39,13 @@ const u8 * mschapv2_remove_domain(const u8 *username, size_t *len) } -void mschapv2_derive_response(const u8 *identity, size_t identity_len, - const u8 *password, size_t password_len, - int pwhash, - const u8 *auth_challenge, - const u8 *peer_challenge, - u8 *nt_response, u8 *auth_response, - u8 *master_key) +int mschapv2_derive_response(const u8 *identity, size_t identity_len, + const u8 *password, size_t password_len, + int pwhash, + const u8 *auth_challenge, + const u8 *peer_challenge, + u8 *nt_response, u8 *auth_response, + u8 *master_key) { const u8 *username; size_t username_len; @@ -93,14 +93,18 @@ void mschapv2_derive_response(const u8 *identity, size_t identity_len, /* Generate master_key here since we have the needed data available. */ if (pwhash) { - hash_nt_password_hash(password, password_hash_hash); + if (hash_nt_password_hash(password, password_hash_hash)) + return -1; } else { - nt_password_hash(password, password_len, password_hash); - hash_nt_password_hash(password_hash, password_hash_hash); + if (nt_password_hash(password, password_len, password_hash) || + hash_nt_password_hash(password_hash, password_hash_hash)) + return -1; } get_master_key(password_hash_hash, nt_response, master_key); wpa_hexdump_key(MSG_DEBUG, "MSCHAPV2: Master Key", master_key, MSCHAPV2_MASTER_KEY_LEN); + + return 0; } diff --git a/contrib/wpa/src/eap_peer/mschapv2.h b/contrib/wpa/src/eap_peer/mschapv2.h index c7c36f7..90dad31 100644 --- a/contrib/wpa/src/eap_peer/mschapv2.h +++ b/contrib/wpa/src/eap_peer/mschapv2.h @@ -21,13 +21,13 @@ #define MSCHAPV2_MASTER_KEY_LEN 16 const u8 * mschapv2_remove_domain(const u8 *username, size_t *len); -void mschapv2_derive_response(const u8 *username, size_t username_len, - const u8 *password, size_t password_len, - int pwhash, - const u8 *auth_challenge, - const u8 *peer_challenge, - u8 *nt_response, u8 *auth_response, - u8 *master_key); +int mschapv2_derive_response(const u8 *username, size_t username_len, + const u8 *password, size_t password_len, + int pwhash, + const u8 *auth_challenge, + const u8 *peer_challenge, + u8 *nt_response, u8 *auth_response, + u8 *master_key); int mschapv2_verify_auth_response(const u8 *auth_response, const u8 *buf, size_t buf_len); diff --git a/contrib/wpa/src/eap_server/Makefile b/contrib/wpa/src/eap_server/Makefile index cffba62..9c41962 100644 --- a/contrib/wpa/src/eap_server/Makefile +++ b/contrib/wpa/src/eap_server/Makefile @@ -2,7 +2,6 @@ all: @echo Nothing to be made. clean: - for d in $(SUBDIRS); do make -C $$d clean; done rm -f *~ *.o *.d install: diff --git a/contrib/wpa/src/eap_server/eap.h b/contrib/wpa/src/eap_server/eap.h index 6a20da4..92400a5 100644 --- a/contrib/wpa/src/eap_server/eap.h +++ b/contrib/wpa/src/eap_server/eap.h @@ -15,7 +15,7 @@ #ifndef EAP_H #define EAP_H -#include "defs.h" +#include "common/defs.h" #include "eap_common/eap_defs.h" #include "eap_server/eap_methods.h" #include "wpabuf.h" @@ -91,6 +91,7 @@ struct eapol_callbacks { struct eap_config { void *ssl_ctx; + void *msg_ctx; void *eap_sim_db_priv; Boolean backend_auth; int eap_server; @@ -105,6 +106,7 @@ struct eap_config { int tnc; struct wps_context *wps; const struct wpabuf *assoc_wps_ie; + const u8 *peer_addr; }; diff --git a/contrib/wpa/src/eap_server/eap_i.h b/contrib/wpa/src/eap_server/eap_i.h index d52b86f..4269a8c 100644 --- a/contrib/wpa/src/eap_server/eap_i.h +++ b/contrib/wpa/src/eap_server/eap_i.h @@ -185,6 +185,8 @@ struct eap_sm { struct wpabuf *assoc_wps_ie; Boolean start_reauth; + + u8 peer_addr[ETH_ALEN]; }; int eap_user_get(struct eap_sm *sm, const u8 *identity, size_t identity_len, diff --git a/contrib/wpa/src/eap_server/eap_methods.h b/contrib/wpa/src/eap_server/eap_methods.h index 0fd5390..5d4d92c 100644 --- a/contrib/wpa/src/eap_server/eap_methods.h +++ b/contrib/wpa/src/eap_server/eap_methods.h @@ -1,6 +1,6 @@ /* - * hostapd / EAP method registration - * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * EAP server method registration + * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -12,8 +12,10 @@ * See README and COPYING for more details. */ -#ifndef EAP_METHODS_H -#define EAP_METHODS_H +#ifndef EAP_SERVER_METHODS_H +#define EAP_SERVER_METHODS_H + +#include "eap_common/eap_defs.h" const struct eap_method * eap_server_get_eap_method(int vendor, EapType method); @@ -23,7 +25,29 @@ void eap_server_method_free(struct eap_method *method); int eap_server_method_register(struct eap_method *method); EapType eap_server_get_type(const char *name, int *vendor); -int eap_server_register_methods(void); void eap_server_unregister_methods(void); +const char * eap_server_get_name(int vendor, EapType type); + +/* EAP server method registration calls for statically linked in methods */ +int eap_server_identity_register(void); +int eap_server_md5_register(void); +int eap_server_tls_register(void); +int eap_server_mschapv2_register(void); +int eap_server_peap_register(void); +int eap_server_tlv_register(void); +int eap_server_gtc_register(void); +int eap_server_ttls_register(void); +int eap_server_sim_register(void); +int eap_server_aka_register(void); +int eap_server_aka_prime_register(void); +int eap_server_pax_register(void); +int eap_server_psk_register(void); +int eap_server_sake_register(void); +int eap_server_gpsk_register(void); +int eap_server_vendor_test_register(void); +int eap_server_fast_register(void); +int eap_server_wsc_register(void); +int eap_server_ikev2_register(void); +int eap_server_tnc_register(void); -#endif /* EAP_METHODS_H */ +#endif /* EAP_SERVER_METHODS_H */ diff --git a/contrib/wpa/src/eap_server/eap.c b/contrib/wpa/src/eap_server/eap_server.c index 897adc3..fdc26f9 100644 --- a/contrib/wpa/src/eap_server/eap.c +++ b/contrib/wpa/src/eap_server/eap_server.c @@ -23,6 +23,7 @@ #include "common.h" #include "eap_i.h" #include "state_machine.h" +#include "common/wpa_ctrl.h" #define STATE_MACHINE_DATA struct eap_sm #define STATE_MACHINE_DEBUG_PREFIX "EAP" @@ -167,6 +168,9 @@ SM_STATE(EAP, INITIALIZE) } sm->num_rounds = 0; sm->method_pending = METHOD_PENDING_NONE; + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_STARTED + MACSTR, MAC2STR(sm->peer_addr)); } @@ -196,6 +200,9 @@ SM_STATE(EAP, PICK_UP_METHOD) sm->currentMethod = EAP_TYPE_NONE; } } + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD + "method=%u", sm->currentMethod); } @@ -350,6 +357,9 @@ SM_STATE(EAP, PROPOSE_METHOD) sm->methodState = METHOD_CONTINUE; else sm->methodState = METHOD_PROPOSED; + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_PROPOSED_METHOD + "vendor=%u method=%u", vendor, sm->currentMethod); } @@ -410,6 +420,9 @@ SM_STATE(EAP, FAILURE) wpabuf_free(sm->lastReqData); sm->lastReqData = NULL; sm->eap_if.eapFail = TRUE; + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_FAILURE + MACSTR, MAC2STR(sm->peer_addr)); } @@ -424,6 +437,9 @@ SM_STATE(EAP, SUCCESS) if (sm->eap_if.eapKeyData) sm->eap_if.eapKeyAvailable = TRUE; sm->eap_if.eapSuccess = TRUE; + + wpa_msg(sm->msg_ctx, MSG_INFO, WPA_EVENT_EAP_SUCCESS + MACSTR, MAC2STR(sm->peer_addr)); } @@ -1210,6 +1226,7 @@ struct eap_sm * eap_server_sm_init(void *eapol_ctx, sm->eapol_cb = eapol_cb; sm->MaxRetrans = 5; /* RFC 3748: max 3-5 retransmissions suggested */ sm->ssl_ctx = conf->ssl_ctx; + sm->msg_ctx = conf->msg_ctx; sm->eap_sim_db_priv = conf->eap_sim_db_priv; sm->backend_auth = conf->backend_auth; sm->eap_server = conf->eap_server; @@ -1238,6 +1255,8 @@ struct eap_sm * eap_server_sm_init(void *eapol_ctx, sm->wps = conf->wps; if (conf->assoc_wps_ie) sm->assoc_wps_ie = wpabuf_dup(conf->assoc_wps_ie); + if (conf->peer_addr) + os_memcpy(sm->peer_addr, conf->peer_addr, ETH_ALEN); wpa_printf(MSG_DEBUG, "EAP: Server state machine created"); diff --git a/contrib/wpa/src/eap_server/eap_aka.c b/contrib/wpa/src/eap_server/eap_server_aka.c index aad52fd..4e7db48 100644 --- a/contrib/wpa/src/eap_server/eap_aka.c +++ b/contrib/wpa/src/eap_server/eap_server_aka.c @@ -15,12 +15,11 @@ #include "includes.h" #include "common.h" -#include "eap_server/eap_i.h" +#include "crypto/sha256.h" +#include "crypto/crypto.h" #include "eap_common/eap_sim_common.h" +#include "eap_server/eap_i.h" #include "eap_server/eap_sim_db.h" -#include "sha1.h" -#include "sha256.h" -#include "crypto.h" struct eap_aka_data { @@ -116,7 +115,7 @@ static void * eap_aka_init(struct eap_sm *sm) } -#ifdef EAP_AKA_PRIME +#ifdef EAP_SERVER_AKA_PRIME static void * eap_aka_prime_init(struct eap_sm *sm) { struct eap_aka_data *data; @@ -148,7 +147,7 @@ static void * eap_aka_prime_init(struct eap_sm *sm) return data; } -#endif /* EAP_AKA_PRIME */ +#endif /* EAP_SERVER_AKA_PRIME */ static void eap_aka_reset(struct eap_sm *sm, void *priv) @@ -399,7 +398,7 @@ static struct wpabuf * eap_aka_build_challenge(struct eap_sm *sm, eap_sim_msg_add(msg, EAP_SIM_AT_RESULT_IND, 0, NULL, 0); } -#ifdef EAP_AKA_PRIME +#ifdef EAP_SERVER_AKA_PRIME if (data->eap_method == EAP_TYPE_AKA) { u16 flags = 0; int i; @@ -426,7 +425,7 @@ static struct wpabuf * eap_aka_build_challenge(struct eap_sm *sm, flags |= EAP_AKA_BIDDING_FLAG_D; eap_sim_msg_add(msg, EAP_SIM_AT_BIDDING, flags, NULL, 0); } -#endif /* EAP_AKA_PRIME */ +#endif /* EAP_SERVER_AKA_PRIME */ wpa_printf(MSG_DEBUG, " AT_MAC"); eap_sim_msg_add_mac(msg, EAP_SIM_AT_MAC); @@ -701,7 +700,7 @@ static void eap_aka_determine_identity(struct eap_sm *sm, return; } -#ifdef EAP_AKA_PRIME +#ifdef EAP_SERVER_AKA_PRIME if (data->eap_method == EAP_TYPE_AKA_PRIME) { /* Note: AUTN = (SQN ^ AK) || AMF || MAC which gives us the * needed 6-octet SQN ^AK for CK',IK' derivation */ @@ -710,7 +709,7 @@ static void eap_aka_determine_identity(struct eap_sm *sm, data->network_name, data->network_name_len); } -#endif /* EAP_AKA_PRIME */ +#endif /* EAP_SERVER_AKA_PRIME */ data->reauth = NULL; data->counter = 0; /* reset re-auth counter since this is full auth */ @@ -807,7 +806,7 @@ static void eap_aka_process_challenge(struct eap_sm *sm, wpa_printf(MSG_DEBUG, "EAP-AKA: Processing Challenge"); -#ifdef EAP_AKA_PRIME +#ifdef EAP_SERVER_AKA_PRIME #if 0 /* KDF negotiation; to be enabled only after more than one KDF is * supported */ @@ -830,7 +829,7 @@ static void eap_aka_process_challenge(struct eap_sm *sm, return; } #endif -#endif /* EAP_AKA_PRIME */ +#endif /* EAP_SERVER_AKA_PRIME */ if (attr->checkcode && eap_aka_verify_checkcode(data, attr->checkcode, @@ -892,7 +891,7 @@ static void eap_aka_process_challenge(struct eap_sm *sm, } if (data->next_reauth_id) { if (data->eap_method == EAP_TYPE_AKA_PRIME) { -#ifdef EAP_AKA_PRIME +#ifdef EAP_SERVER_AKA_PRIME eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv, identity, identity_len, @@ -900,7 +899,7 @@ static void eap_aka_process_challenge(struct eap_sm *sm, data->counter + 1, data->k_encr, data->k_aut, data->k_re); -#endif /* EAP_AKA_PRIME */ +#endif /* EAP_SERVER_AKA_PRIME */ } else { eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity, identity_len, @@ -1031,7 +1030,7 @@ static void eap_aka_process_reauth(struct eap_sm *sm, } if (data->next_reauth_id) { if (data->eap_method == EAP_TYPE_AKA_PRIME) { -#ifdef EAP_AKA_PRIME +#ifdef EAP_SERVER_AKA_PRIME eap_sim_db_add_reauth_prime(sm->eap_sim_db_priv, identity, identity_len, @@ -1039,7 +1038,7 @@ static void eap_aka_process_reauth(struct eap_sm *sm, data->counter + 1, data->k_encr, data->k_aut, data->k_re); -#endif /* EAP_AKA_PRIME */ +#endif /* EAP_SERVER_AKA_PRIME */ } else { eap_sim_db_add_reauth(sm->eap_sim_db_priv, identity, identity_len, @@ -1247,7 +1246,7 @@ int eap_server_aka_register(void) } -#ifdef EAP_AKA_PRIME +#ifdef EAP_SERVER_AKA_PRIME int eap_server_aka_prime_register(void) { struct eap_method *eap; @@ -1275,4 +1274,4 @@ int eap_server_aka_prime_register(void) return ret; } -#endif /* EAP_AKA_PRIME */ +#endif /* EAP_SERVER_AKA_PRIME */ diff --git a/contrib/wpa/src/eap_server/eap_fast.c b/contrib/wpa/src/eap_server/eap_server_fast.c index c06f396..39beb33 100644 --- a/contrib/wpa/src/eap_server/eap_fast.c +++ b/contrib/wpa/src/eap_server/eap_server_fast.c @@ -15,13 +15,13 @@ #include "includes.h" #include "common.h" -#include "aes_wrap.h" -#include "sha1.h" -#include "eap_i.h" -#include "eap_tls_common.h" -#include "tls.h" +#include "crypto/aes_wrap.h" +#include "crypto/sha1.h" +#include "crypto/tls.h" #include "eap_common/eap_tlv_common.h" #include "eap_common/eap_fast_common.h" +#include "eap_i.h" +#include "eap_tls_common.h" static void eap_fast_reset(struct eap_sm *sm, void *priv); @@ -791,6 +791,11 @@ static struct wpabuf * eap_fast_build_pac(struct eap_sm *sm, /* Note: headers may be misaligned after A-ID */ + if (sm->identity) { + eap_fast_put_tlv(buf, PAC_TYPE_I_ID, sm->identity, + sm->identity_len); + } + /* A-ID-Info (inside PAC-Info) */ eap_fast_put_tlv(buf, PAC_TYPE_A_ID_INFO, data->srv_id_info, srv_id_info_len); @@ -816,29 +821,28 @@ static int eap_fast_encrypt_phase2(struct eap_sm *sm, wpa_hexdump_buf_key(MSG_DEBUG, "EAP-FAST: Encrypting Phase 2 TLVs", plain); - encr = eap_server_tls_encrypt(sm, &data->ssl, wpabuf_mhead(plain), - wpabuf_len(plain)); + encr = eap_server_tls_encrypt(sm, &data->ssl, plain); wpabuf_free(plain); - if (data->ssl.out_buf && piggyback) { + if (data->ssl.tls_out && piggyback) { wpa_printf(MSG_DEBUG, "EAP-FAST: Piggyback Phase 2 data " "(len=%d) with last Phase 1 Message (len=%d " "used=%d)", (int) wpabuf_len(encr), - (int) wpabuf_len(data->ssl.out_buf), - (int) data->ssl.out_used); - if (wpabuf_resize(&data->ssl.out_buf, wpabuf_len(encr)) < 0) { + (int) wpabuf_len(data->ssl.tls_out), + (int) data->ssl.tls_out_pos); + if (wpabuf_resize(&data->ssl.tls_out, wpabuf_len(encr)) < 0) { wpa_printf(MSG_WARNING, "EAP-FAST: Failed to resize " "output buffer"); wpabuf_free(encr); return -1; } - wpabuf_put_buf(data->ssl.out_buf, encr); + wpabuf_put_buf(data->ssl.tls_out, encr); wpabuf_free(encr); } else { - wpabuf_free(data->ssl.out_buf); - data->ssl.out_used = 0; - data->ssl.out_buf = encr; + wpabuf_free(data->ssl.tls_out); + data->ssl.tls_out_pos = 0; + data->ssl.tls_out = encr; } return 0; @@ -987,7 +991,7 @@ static void eap_fast_process_phase2_response(struct eap_sm *sm, left = in_len - sizeof(*hdr); wpa_hexdump(MSG_DEBUG, "EAP-FAST: Phase2 type Nak'ed; " "allowed types", pos + 1, left - 1); -#ifdef EAP_TNC +#ifdef EAP_SERVER_TNC if (m && m->vendor == EAP_VENDOR_IETF && m->method == EAP_TYPE_TNC) { wpa_printf(MSG_DEBUG, "EAP-FAST: Peer Nak'ed required " @@ -996,7 +1000,7 @@ static void eap_fast_process_phase2_response(struct eap_sm *sm, eap_fast_phase2_init(sm, data, next_type); return; } -#endif /* EAP_TNC */ +#endif /* EAP_SERVER_TNC */ eap_sm_process_nak(sm, pos + 1, left - 1); if (sm->user && sm->user_eap_method_index < EAP_MAX_METHODS && sm->user->methods[sm->user_eap_method_index].method != @@ -1064,13 +1068,13 @@ static void eap_fast_process_phase2_response(struct eap_sm *sm, eap_fast_state(data, CRYPTO_BINDING); data->eap_seq++; next_type = EAP_TYPE_NONE; -#ifdef EAP_TNC +#ifdef EAP_SERVER_TNC if (sm->tnc && !data->tnc_started) { wpa_printf(MSG_DEBUG, "EAP-FAST: Initialize TNC"); next_type = EAP_TYPE_TNC; data->tnc_started = 1; } -#endif /* EAP_TNC */ +#endif /* EAP_SERVER_TNC */ break; case FAILURE: break; @@ -1121,7 +1125,7 @@ static void eap_fast_process_phase2_eap(struct eap_sm *sm, } -static int eap_fast_parse_tlvs(u8 *data, size_t data_len, +static int eap_fast_parse_tlvs(struct wpabuf *data, struct eap_fast_tlv_parse *tlv) { int mandatory, tlv_type, len, res; @@ -1129,8 +1133,8 @@ static int eap_fast_parse_tlvs(u8 *data, size_t data_len, os_memset(tlv, 0, sizeof(*tlv)); - pos = data; - end = data + data_len; + pos = wpabuf_mhead(data); + end = pos + wpabuf_len(data); while (pos + 4 < end) { mandatory = pos[0] & 0x80; tlv_type = WPA_GET_BE16(pos) & 0x3fff; @@ -1241,12 +1245,12 @@ static int eap_fast_pac_type(u8 *pac, size_t len, u16 type) static void eap_fast_process_phase2_tlvs(struct eap_sm *sm, struct eap_fast_data *data, - u8 *in_data, size_t in_len) + struct wpabuf *in_data) { struct eap_fast_tlv_parse tlv; int check_crypto_binding = data->state == CRYPTO_BINDING; - if (eap_fast_parse_tlvs(in_data, in_len, &tlv) < 0) { + if (eap_fast_parse_tlvs(in_data, &tlv) < 0) { wpa_printf(MSG_DEBUG, "EAP-FAST: Failed to parse received " "Phase 2 TLVs"); return; @@ -1373,70 +1377,44 @@ static void eap_fast_process_phase2(struct eap_sm *sm, struct eap_fast_data *data, struct wpabuf *in_buf) { - u8 *in_decrypted; - int len_decrypted; - size_t buf_len; - u8 *in_data; - size_t in_len; - - in_data = wpabuf_mhead(in_buf); - in_len = wpabuf_len(in_buf); + struct wpabuf *in_decrypted; wpa_printf(MSG_DEBUG, "EAP-FAST: Received %lu bytes encrypted data for" - " Phase 2", (unsigned long) in_len); + " Phase 2", (unsigned long) wpabuf_len(in_buf)); if (data->pending_phase2_resp) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 response - " "skip decryption and use old data"); - eap_fast_process_phase2_tlvs( - sm, data, wpabuf_mhead(data->pending_phase2_resp), - wpabuf_len(data->pending_phase2_resp)); + eap_fast_process_phase2_tlvs(sm, data, + data->pending_phase2_resp); wpabuf_free(data->pending_phase2_resp); data->pending_phase2_resp = NULL; return; } - buf_len = in_len; - /* - * Even though we try to disable TLS compression, it is possible that - * this cannot be done with all TLS libraries. Add extra buffer space - * to handle the possibility of the decrypted data being longer than - * input data. - */ - buf_len += 500; - buf_len *= 3; - in_decrypted = os_malloc(buf_len); + in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, + in_buf); if (in_decrypted == NULL) { - wpa_printf(MSG_WARNING, "EAP-FAST: Failed to allocate memory " - "for decryption"); - return; - } - - len_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, - in_data, in_len, - in_decrypted, buf_len); - if (len_decrypted < 0) { wpa_printf(MSG_INFO, "EAP-FAST: Failed to decrypt Phase 2 " "data"); - os_free(in_decrypted); eap_fast_state(data, FAILURE); return; } - wpa_hexdump_key(MSG_DEBUG, "EAP-FAST: Decrypted Phase 2 TLVs", - in_decrypted, len_decrypted); + wpa_hexdump_buf_key(MSG_DEBUG, "EAP-FAST: Decrypted Phase 2 TLVs", + in_decrypted); - eap_fast_process_phase2_tlvs(sm, data, in_decrypted, len_decrypted); + eap_fast_process_phase2_tlvs(sm, data, in_decrypted); if (sm->method_pending == METHOD_PENDING_WAIT) { wpa_printf(MSG_DEBUG, "EAP-FAST: Phase2 method is in " "pending wait state - save decrypted response"); wpabuf_free(data->pending_phase2_resp); - data->pending_phase2_resp = wpabuf_alloc_copy(in_decrypted, - len_decrypted); + data->pending_phase2_resp = in_decrypted; + return; } - os_free(in_decrypted); + wpabuf_free(in_decrypted); } @@ -1475,7 +1453,7 @@ static int eap_fast_process_phase1(struct eap_sm *sm, } if (!tls_connection_established(sm->ssl_ctx, data->ssl.conn) || - wpabuf_len(data->ssl.out_buf) > 0) + wpabuf_len(data->ssl.tls_out) > 0) return 1; /* @@ -1541,7 +1519,7 @@ static void eap_fast_process_msg(struct eap_sm *sm, void *priv, case PHASE2_METHOD: case CRYPTO_BINDING: case REQUEST_PAC: - eap_fast_process_phase2(sm, data, data->ssl.in_buf); + eap_fast_process_phase2(sm, data, data->ssl.tls_in); break; default: wpa_printf(MSG_DEBUG, "EAP-FAST: Unexpected state %d in %s", diff --git a/contrib/wpa/src/eap_server/eap_gpsk.c b/contrib/wpa/src/eap_server/eap_server_gpsk.c index d0c7559..d0c7559 100644 --- a/contrib/wpa/src/eap_server/eap_gpsk.c +++ b/contrib/wpa/src/eap_server/eap_server_gpsk.c diff --git a/contrib/wpa/src/eap_server/eap_gtc.c b/contrib/wpa/src/eap_server/eap_server_gtc.c index 97e328b..79b9696 100644 --- a/contrib/wpa/src/eap_server/eap_gtc.c +++ b/contrib/wpa/src/eap_server/eap_server_gtc.c @@ -33,14 +33,14 @@ static void * eap_gtc_init(struct eap_sm *sm) return NULL; data->state = CONTINUE; -#ifdef EAP_FAST +#ifdef EAP_SERVER_FAST if (sm->m && sm->m->vendor == EAP_VENDOR_IETF && sm->m->method == EAP_TYPE_FAST) { wpa_printf(MSG_DEBUG, "EAP-GTC: EAP-FAST tunnel - use prefix " "with challenge/response"); data->prefix = 1; } -#endif /* EAP_FAST */ +#endif /* EAP_SERVER_FAST */ return data; } @@ -109,7 +109,7 @@ static void eap_gtc_process(struct eap_sm *sm, void *priv, wpa_hexdump_ascii_key(MSG_MSGDUMP, "EAP-GTC: Response", pos, rlen); -#ifdef EAP_FAST +#ifdef EAP_SERVER_FAST if (data->prefix) { const u8 *pos2, *end; /* "RESPONSE=<user>\0<password>" */ @@ -170,7 +170,7 @@ static void eap_gtc_process(struct eap_sm *sm, void *priv, "EAP-GTC: Response password", pos, rlen); } -#endif /* EAP_FAST */ +#endif /* EAP_SERVER_FAST */ if (sm->user == NULL || sm->user->password == NULL || sm->user->password_hash) { diff --git a/contrib/wpa/src/eap_server/eap_identity.c b/contrib/wpa/src/eap_server/eap_server_identity.c index cd8da2a..cd8da2a 100644 --- a/contrib/wpa/src/eap_server/eap_identity.c +++ b/contrib/wpa/src/eap_server/eap_server_identity.c diff --git a/contrib/wpa/src/eap_server/eap_ikev2.c b/contrib/wpa/src/eap_server/eap_server_ikev2.c index 06074ee..06074ee 100644 --- a/contrib/wpa/src/eap_server/eap_ikev2.c +++ b/contrib/wpa/src/eap_server/eap_server_ikev2.c diff --git a/contrib/wpa/src/eap_server/eap_md5.c b/contrib/wpa/src/eap_server/eap_server_md5.c index dee2dc5..dee2dc5 100644 --- a/contrib/wpa/src/eap_server/eap_md5.c +++ b/contrib/wpa/src/eap_server/eap_server_md5.c diff --git a/contrib/wpa/src/eap_server/eap_methods.c b/contrib/wpa/src/eap_server/eap_server_methods.c index 4092d67..900a5dd 100644 --- a/contrib/wpa/src/eap_server/eap_methods.c +++ b/contrib/wpa/src/eap_server/eap_server_methods.c @@ -1,6 +1,6 @@ /* - * hostapd / EAP method registration - * Copyright (c) 2004-2006, Jouni Malinen <j@w1.fi> + * EAP server method registration + * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -134,159 +134,6 @@ int eap_server_method_register(struct eap_method *method) /** - * eap_server_register_methods - Register statically linked EAP server methods - * Returns: 0 on success, -1 on failure - * - * This function is called at program initialization to register all EAP server - * methods that were linked in statically. - */ -int eap_server_register_methods(void) -{ - int ret = 0; - - if (ret == 0) { - int eap_server_identity_register(void); - ret = eap_server_identity_register(); - } - -#ifdef EAP_MD5 - if (ret == 0) { - int eap_server_md5_register(void); - ret = eap_server_md5_register(); - } -#endif /* EAP_MD5 */ - -#ifdef EAP_TLS - if (ret == 0) { - int eap_server_tls_register(void); - ret = eap_server_tls_register(); - } -#endif /* EAP_TLS */ - -#ifdef EAP_MSCHAPv2 - if (ret == 0) { - int eap_server_mschapv2_register(void); - ret = eap_server_mschapv2_register(); - } -#endif /* EAP_MSCHAPv2 */ - -#ifdef EAP_PEAP - if (ret == 0) { - int eap_server_peap_register(void); - ret = eap_server_peap_register(); - } -#endif /* EAP_PEAP */ - -#ifdef EAP_TLV - if (ret == 0) { - int eap_server_tlv_register(void); - ret = eap_server_tlv_register(); - } -#endif /* EAP_TLV */ - -#ifdef EAP_GTC - if (ret == 0) { - int eap_server_gtc_register(void); - ret = eap_server_gtc_register(); - } -#endif /* EAP_GTC */ - -#ifdef EAP_TTLS - if (ret == 0) { - int eap_server_ttls_register(void); - ret = eap_server_ttls_register(); - } -#endif /* EAP_TTLS */ - -#ifdef EAP_SIM - if (ret == 0) { - int eap_server_sim_register(void); - ret = eap_server_sim_register(); - } -#endif /* EAP_SIM */ - -#ifdef EAP_AKA - if (ret == 0) { - int eap_server_aka_register(void); - ret = eap_server_aka_register(); - } -#endif /* EAP_AKA */ - -#ifdef EAP_AKA_PRIME - if (ret == 0) { - int eap_server_aka_prime_register(void); - ret = eap_server_aka_prime_register(); - } -#endif /* EAP_AKA_PRIME */ - -#ifdef EAP_PAX - if (ret == 0) { - int eap_server_pax_register(void); - ret = eap_server_pax_register(); - } -#endif /* EAP_PAX */ - -#ifdef EAP_PSK - if (ret == 0) { - int eap_server_psk_register(void); - ret = eap_server_psk_register(); - } -#endif /* EAP_PSK */ - -#ifdef EAP_SAKE - if (ret == 0) { - int eap_server_sake_register(void); - ret = eap_server_sake_register(); - } -#endif /* EAP_SAKE */ - -#ifdef EAP_GPSK - if (ret == 0) { - int eap_server_gpsk_register(void); - ret = eap_server_gpsk_register(); - } -#endif /* EAP_GPSK */ - -#ifdef EAP_VENDOR_TEST - if (ret == 0) { - int eap_server_vendor_test_register(void); - ret = eap_server_vendor_test_register(); - } -#endif /* EAP_VENDOR_TEST */ - -#ifdef EAP_FAST - if (ret == 0) { - int eap_server_fast_register(void); - ret = eap_server_fast_register(); - } -#endif /* EAP_FAST */ - -#ifdef EAP_WSC - if (ret == 0) { - int eap_server_wsc_register(void); - ret = eap_server_wsc_register(); - } -#endif /* EAP_WSC */ - -#ifdef EAP_IKEV2 - if (ret == 0) { - int eap_server_ikev2_register(void); - ret = eap_server_ikev2_register(); - } -#endif /* EAP_IKEV2 */ - -#ifdef EAP_TNC - if (ret == 0) { - int eap_server_tnc_register(void); - ret = eap_server_tnc_register(); - } -#endif /* EAP_TNC */ - - return ret; -} - - -/** * eap_server_unregister_methods - Unregister EAP server methods * * This function is called at program termination to unregister all EAP server @@ -306,3 +153,23 @@ void eap_server_unregister_methods(void) eap_server_method_free(m); } } + + +/** + * eap_server_get_name - Get EAP method name for the given EAP type + * @vendor: EAP Vendor-Id (0 = IETF) + * @type: EAP method type + * Returns: EAP method name, e.g., TLS, or %NULL if not found + * + * This function maps EAP type numbers into EAP type names based on the list of + * EAP methods included in the build. + */ +const char * eap_server_get_name(int vendor, EapType type) +{ + struct eap_method *m; + for (m = eap_methods; m; m = m->next) { + if (m->vendor == vendor && m->method == type) + return m->name; + } + return NULL; +} diff --git a/contrib/wpa/src/eap_server/eap_mschapv2.c b/contrib/wpa/src/eap_server/eap_server_mschapv2.c index 20e7ade..39d1c6e 100644 --- a/contrib/wpa/src/eap_server/eap_mschapv2.c +++ b/contrib/wpa/src/eap_server/eap_server_mschapv2.c @@ -15,8 +15,8 @@ #include "includes.h" #include "common.h" +#include "crypto/ms_funcs.h" #include "eap_i.h" -#include "ms_funcs.h" struct eap_mschapv2_hdr { @@ -295,6 +295,7 @@ static void eap_mschapv2_process_response(struct eap_sm *sm, u8 expected[24]; const u8 *username, *user; size_t username_len, user_len; + int res; pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_MSCHAPV2, respData, &len); @@ -372,17 +373,22 @@ static void eap_mschapv2_process_response(struct eap_sm *sm, username, username_len); if (sm->user->password_hash) { - generate_nt_response_pwhash(data->auth_challenge, - peer_challenge, - username, username_len, - sm->user->password, - expected); + res = generate_nt_response_pwhash(data->auth_challenge, + peer_challenge, + username, username_len, + sm->user->password, + expected); } else { - generate_nt_response(data->auth_challenge, peer_challenge, - username, username_len, - sm->user->password, - sm->user->password_len, - expected); + res = generate_nt_response(data->auth_challenge, + peer_challenge, + username, username_len, + sm->user->password, + sm->user->password_len, + expected); + } + if (res) { + data->state = FAILURE; + return; } if (os_memcmp(nt_response, expected, 24) == 0) { diff --git a/contrib/wpa/src/eap_server/eap_pax.c b/contrib/wpa/src/eap_server/eap_server_pax.c index 1dc023b..1dc023b 100644 --- a/contrib/wpa/src/eap_server/eap_pax.c +++ b/contrib/wpa/src/eap_server/eap_server_pax.c diff --git a/contrib/wpa/src/eap_server/eap_peap.c b/contrib/wpa/src/eap_server/eap_server_peap.c index 4b2d5a5..674ecd2 100644 --- a/contrib/wpa/src/eap_server/eap_peap.c +++ b/contrib/wpa/src/eap_server/eap_server_peap.c @@ -15,12 +15,12 @@ #include "includes.h" #include "common.h" -#include "sha1.h" +#include "crypto/sha1.h" +#include "crypto/tls.h" #include "eap_i.h" #include "eap_tls_common.h" #include "eap_common/eap_tlv_common.h" #include "eap_common/eap_peap_common.h" -#include "tls.h" #include "tncs.h" @@ -235,7 +235,7 @@ static struct wpabuf * eap_peap_build_phase2_req(struct eap_sm *sm, struct eap_peap_data *data, u8 id) { - struct wpabuf *buf, *encr_req; + struct wpabuf *buf, *encr_req, msgbuf; const u8 *req; size_t req_len; @@ -260,19 +260,20 @@ static struct wpabuf * eap_peap_build_phase2_req(struct eap_sm *sm, req_len -= sizeof(struct eap_hdr); } - encr_req = eap_server_tls_encrypt(sm, &data->ssl, req, req_len); + wpabuf_set(&msgbuf, req, req_len); + encr_req = eap_server_tls_encrypt(sm, &data->ssl, &msgbuf); wpabuf_free(buf); return encr_req; } -#ifdef EAP_TNC +#ifdef EAP_SERVER_TNC static struct wpabuf * eap_peap_build_phase2_soh(struct eap_sm *sm, struct eap_peap_data *data, u8 id) { - struct wpabuf *buf1, *buf, *encr_req; + struct wpabuf *buf1, *buf, *encr_req, msgbuf; const u8 *req; size_t req_len; @@ -297,13 +298,14 @@ static struct wpabuf * eap_peap_build_phase2_soh(struct eap_sm *sm, req += sizeof(struct eap_hdr); req_len -= sizeof(struct eap_hdr); + wpabuf_set(&msgbuf, req, req_len); - encr_req = eap_server_tls_encrypt(sm, &data->ssl, req, req_len); + encr_req = eap_server_tls_encrypt(sm, &data->ssl, &msgbuf); wpabuf_free(buf); return encr_req; } -#endif /* EAP_TNC */ +#endif /* EAP_SERVER_TNC */ static void eap_peap_get_isk(struct eap_peap_data *data, @@ -370,17 +372,17 @@ static struct wpabuf * eap_peap_build_phase2_tlv(struct eap_sm *sm, u8 id) { struct wpabuf *buf, *encr_req; - size_t len; + size_t mlen; - len = 6; /* Result TLV */ + mlen = 6; /* Result TLV */ if (data->crypto_binding != NO_BINDING) - len += 60; /* Cryptobinding TLV */ -#ifdef EAP_TNC + mlen += 60; /* Cryptobinding TLV */ +#ifdef EAP_SERVER_TNC if (data->soh_response) - len += wpabuf_len(data->soh_response); -#endif /* EAP_TNC */ + mlen += wpabuf_len(data->soh_response); +#endif /* EAP_SERVER_TNC */ - buf = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, len, + buf = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TLV, mlen, EAP_CODE_REQUEST, id); if (buf == NULL) return NULL; @@ -401,7 +403,7 @@ static struct wpabuf * eap_peap_build_phase2_tlv(struct eap_sm *sm, size_t len[2]; u16 tlv_type; -#ifdef EAP_TNC +#ifdef EAP_SERVER_TNC if (data->soh_response) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Adding MS-SOH " "Response TLV"); @@ -409,7 +411,7 @@ static struct wpabuf * eap_peap_build_phase2_tlv(struct eap_sm *sm, wpabuf_free(data->soh_response); data->soh_response = NULL; } -#endif /* EAP_TNC */ +#endif /* EAP_SERVER_TNC */ if (eap_peap_derive_cmk(sm, data) < 0 || os_get_random(data->binding_nonce, 32)) { @@ -450,8 +452,7 @@ static struct wpabuf * eap_peap_build_phase2_tlv(struct eap_sm *sm, wpa_hexdump_buf_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 TLV data", buf); - encr_req = eap_server_tls_encrypt(sm, &data->ssl, wpabuf_head(buf), - wpabuf_len(buf)); + encr_req = eap_server_tls_encrypt(sm, &data->ssl, buf); wpabuf_free(buf); return encr_req; @@ -462,7 +463,7 @@ static struct wpabuf * eap_peap_build_phase2_term(struct eap_sm *sm, struct eap_peap_data *data, u8 id, int success) { - struct wpabuf *encr_req; + struct wpabuf *encr_req, msgbuf; size_t req_len; struct eap_hdr *hdr; @@ -478,7 +479,8 @@ static struct wpabuf * eap_peap_build_phase2_term(struct eap_sm *sm, wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: Encrypting Phase 2 data", (u8 *) hdr, req_len); - encr_req = eap_server_tls_encrypt(sm, &data->ssl, (u8 *) hdr, req_len); + wpabuf_set(&msgbuf, hdr, req_len); + encr_req = eap_server_tls_encrypt(sm, &data->ssl, &msgbuf); os_free(hdr); return encr_req; @@ -513,32 +515,32 @@ static struct wpabuf * eap_peap_buildReq(struct eap_sm *sm, void *priv, u8 id) break; case PHASE2_ID: case PHASE2_METHOD: - wpabuf_free(data->ssl.out_buf); - data->ssl.out_used = 0; - data->ssl.out_buf = eap_peap_build_phase2_req(sm, data, id); + wpabuf_free(data->ssl.tls_out); + data->ssl.tls_out_pos = 0; + data->ssl.tls_out = eap_peap_build_phase2_req(sm, data, id); break; -#ifdef EAP_TNC +#ifdef EAP_SERVER_TNC case PHASE2_SOH: - wpabuf_free(data->ssl.out_buf); - data->ssl.out_used = 0; - data->ssl.out_buf = eap_peap_build_phase2_soh(sm, data, id); + wpabuf_free(data->ssl.tls_out); + data->ssl.tls_out_pos = 0; + data->ssl.tls_out = eap_peap_build_phase2_soh(sm, data, id); break; -#endif /* EAP_TNC */ +#endif /* EAP_SERVER_TNC */ case PHASE2_TLV: - wpabuf_free(data->ssl.out_buf); - data->ssl.out_used = 0; - data->ssl.out_buf = eap_peap_build_phase2_tlv(sm, data, id); + wpabuf_free(data->ssl.tls_out); + data->ssl.tls_out_pos = 0; + data->ssl.tls_out = eap_peap_build_phase2_tlv(sm, data, id); break; case SUCCESS_REQ: - wpabuf_free(data->ssl.out_buf); - data->ssl.out_used = 0; - data->ssl.out_buf = eap_peap_build_phase2_term(sm, data, id, + wpabuf_free(data->ssl.tls_out); + data->ssl.tls_out_pos = 0; + data->ssl.tls_out = eap_peap_build_phase2_term(sm, data, id, 1); break; case FAILURE_REQ: - wpabuf_free(data->ssl.out_buf); - data->ssl.out_used = 0; - data->ssl.out_buf = eap_peap_build_phase2_term(sm, data, id, + wpabuf_free(data->ssl.tls_out); + data->ssl.tls_out_pos = 0; + data->ssl.tls_out = eap_peap_build_phase2_term(sm, data, id, 0); break; default: @@ -757,7 +759,7 @@ static void eap_peap_process_phase2_tlv(struct eap_sm *sm, } -#ifdef EAP_TNC +#ifdef EAP_SERVER_TNC static void eap_peap_process_phase2_soh(struct eap_sm *sm, struct eap_peap_data *data, struct wpabuf *in_data) @@ -885,7 +887,7 @@ auth_method: wpa_printf(MSG_DEBUG, "EAP-PEAP: try EAP type %d", next_type); eap_peap_phase2_init(sm, data, next_type); } -#endif /* EAP_TNC */ +#endif /* EAP_SERVER_TNC */ static void eap_peap_process_phase2_response(struct eap_sm *sm, @@ -902,12 +904,12 @@ static void eap_peap_process_phase2_response(struct eap_sm *sm, return; } -#ifdef EAP_TNC +#ifdef EAP_SERVER_TNC if (data->state == PHASE2_SOH) { eap_peap_process_phase2_soh(sm, data, in_data); return; } -#endif /* EAP_TNC */ +#endif /* EAP_SERVER_TNC */ if (data->phase2_priv == NULL) { wpa_printf(MSG_DEBUG, "EAP-PEAP: %s - Phase2 not " @@ -991,7 +993,7 @@ static void eap_peap_process_phase2_response(struct eap_sm *sm, break; } -#ifdef EAP_TNC +#ifdef EAP_SERVER_TNC if (data->state != PHASE2_SOH && sm->tnc && data->peap_version == 0) { eap_peap_state(data, PHASE2_SOH); @@ -1000,7 +1002,7 @@ static void eap_peap_process_phase2_response(struct eap_sm *sm, next_type = EAP_TYPE_NONE; break; } -#endif /* EAP_TNC */ +#endif /* EAP_SERVER_TNC */ eap_peap_state(data, PHASE2_METHOD); next_type = sm->user->methods[0].method; @@ -1029,17 +1031,11 @@ static void eap_peap_process_phase2(struct eap_sm *sm, struct wpabuf *in_buf) { struct wpabuf *in_decrypted; - int len_decrypted; const struct eap_hdr *hdr; - size_t buf_len, len; - u8 *in_data; - size_t in_len; - - in_data = wpabuf_mhead(in_buf); - in_len = wpabuf_len(in_buf); + size_t len; wpa_printf(MSG_DEBUG, "EAP-PEAP: received %lu bytes encrypted data for" - " Phase 2", (unsigned long) in_len); + " Phase 2", (unsigned long) wpabuf_len(in_buf)); if (data->pending_phase2_resp) { wpa_printf(MSG_DEBUG, "EAP-PEAP: Pending Phase 2 response - " @@ -1051,34 +1047,14 @@ static void eap_peap_process_phase2(struct eap_sm *sm, return; } - buf_len = in_len; - /* - * Even though we try to disable TLS compression, it is possible that - * this cannot be done with all TLS libraries. Add extra buffer space - * to handle the possibility of the decrypted data being longer than - * input data. - */ - buf_len += 500; - buf_len *= 3; - in_decrypted = wpabuf_alloc(buf_len); + in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, + in_buf); if (in_decrypted == NULL) { - wpa_printf(MSG_WARNING, "EAP-PEAP: failed to allocate memory " - "for decryption"); - return; - } - - len_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, - in_data, in_len, - wpabuf_mhead(in_decrypted), - buf_len); - if (len_decrypted < 0) { wpa_printf(MSG_INFO, "EAP-PEAP: Failed to decrypt Phase 2 " "data"); - wpabuf_free(in_decrypted); eap_peap_state(data, FAILURE); return; } - wpabuf_put(in_decrypted, len_decrypted); wpa_hexdump_buf_key(MSG_DEBUG, "EAP-PEAP: Decrypted Phase 2 EAP", in_decrypted); @@ -1191,7 +1167,7 @@ static void eap_peap_process_phase2(struct eap_sm *sm, break; } - os_free(in_decrypted); + wpabuf_free(in_decrypted); } @@ -1199,7 +1175,6 @@ static int eap_peapv2_start_phase2(struct eap_sm *sm, struct eap_peap_data *data) { struct wpabuf *buf, *buf2; - int res; wpa_printf(MSG_DEBUG, "EAP-PEAPv2: Phase1 done, include first Phase2 " "payload in the same message"); @@ -1218,35 +1193,25 @@ static int eap_peapv2_start_phase2(struct eap_sm *sm, wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAPv2: Identity Request", buf2); - buf = wpabuf_alloc(data->ssl.tls_out_limit); - if (buf == NULL) { - wpabuf_free(buf2); - return -1; - } - - res = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn, - wpabuf_head(buf2), wpabuf_len(buf2), - wpabuf_put(buf, 0), - data->ssl.tls_out_limit); + buf = tls_connection_encrypt(sm->ssl_ctx, data->ssl.conn, + buf2); wpabuf_free(buf2); - if (res < 0) { + if (buf == NULL) { wpa_printf(MSG_INFO, "EAP-PEAPv2: Failed to encrypt Phase 2 " "data"); - wpabuf_free(buf); return -1; } - wpabuf_put(buf, res); wpa_hexdump_buf(MSG_DEBUG, "EAP-PEAPv2: Encrypted Identity Request", buf); /* Append TLS data into the pending buffer after the Server Finished */ - if (wpabuf_resize(&data->ssl.out_buf, wpabuf_len(buf)) < 0) { + if (wpabuf_resize(&data->ssl.tls_out, wpabuf_len(buf)) < 0) { wpabuf_free(buf); return -1; } - wpabuf_put_buf(data->ssl.out_buf, buf); + wpabuf_put_buf(data->ssl.tls_out, buf); wpabuf_free(buf); return 0; @@ -1305,7 +1270,7 @@ static void eap_peap_process_msg(struct eap_sm *sm, void *priv, case PHASE2_METHOD: case PHASE2_SOH: case PHASE2_TLV: - eap_peap_process_phase2(sm, data, respData, data->ssl.in_buf); + eap_peap_process_phase2(sm, data, respData, data->ssl.tls_in); break; case SUCCESS_REQ: eap_peap_state(data, SUCCESS); diff --git a/contrib/wpa/src/eap_server/eap_psk.c b/contrib/wpa/src/eap_server/eap_server_psk.c index c68d4c3..4c30346 100644 --- a/contrib/wpa/src/eap_server/eap_psk.c +++ b/contrib/wpa/src/eap_server/eap_server_psk.c @@ -18,9 +18,9 @@ #include "includes.h" #include "common.h" -#include "eap_server/eap_i.h" -#include "aes_wrap.h" +#include "crypto/aes_wrap.h" #include "eap_common/eap_psk_common.h" +#include "eap_server/eap_i.h" struct eap_psk_data { diff --git a/contrib/wpa/src/eap_server/eap_sake.c b/contrib/wpa/src/eap_server/eap_server_sake.c index ce4848f..ce4848f 100644 --- a/contrib/wpa/src/eap_server/eap_sake.c +++ b/contrib/wpa/src/eap_server/eap_server_sake.c diff --git a/contrib/wpa/src/eap_server/eap_sim.c b/contrib/wpa/src/eap_server/eap_server_sim.c index 436c655..436c655 100644 --- a/contrib/wpa/src/eap_server/eap_sim.c +++ b/contrib/wpa/src/eap_server/eap_server_sim.c diff --git a/contrib/wpa/src/eap_server/eap_tls.c b/contrib/wpa/src/eap_server/eap_server_tls.c index 5747940..c98fa18 100644 --- a/contrib/wpa/src/eap_server/eap_tls.c +++ b/contrib/wpa/src/eap_server/eap_server_tls.c @@ -17,7 +17,7 @@ #include "common.h" #include "eap_i.h" #include "eap_tls_common.h" -#include "tls.h" +#include "crypto/tls.h" static void eap_tls_reset(struct eap_sm *sm, void *priv); @@ -169,7 +169,7 @@ static void eap_tls_process_msg(struct eap_sm *sm, void *priv, const struct wpabuf *respData) { struct eap_tls_data *data = priv; - if (data->state == SUCCESS && wpabuf_len(data->ssl.in_buf) == 0) { + if (data->state == SUCCESS && wpabuf_len(data->ssl.tls_in) == 0) { wpa_printf(MSG_DEBUG, "EAP-TLS: Client acknowledged final TLS " "handshake message"); return; diff --git a/contrib/wpa/src/eap_server/eap_tls_common.c b/contrib/wpa/src/eap_server/eap_server_tls_common.c index bda1184..25ae683 100644 --- a/contrib/wpa/src/eap_server/eap_tls_common.c +++ b/contrib/wpa/src/eap_server/eap_server_tls_common.c @@ -1,6 +1,6 @@ /* - * hostapd / EAP-TLS/PEAP/TTLS/FAST common functions - * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * EAP-TLS/PEAP/TTLS/FAST server common functions + * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -15,10 +15,13 @@ #include "includes.h" #include "common.h" +#include "crypto/sha1.h" +#include "crypto/tls.h" #include "eap_i.h" #include "eap_tls_common.h" -#include "sha1.h" -#include "tls.h" + + +static void eap_server_tls_free_in_buf(struct eap_ssl_data *data); int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, @@ -58,8 +61,9 @@ int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data, void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data) { tls_connection_deinit(sm->ssl_ctx, data->conn); - os_free(data->in_buf); - os_free(data->out_buf); + eap_server_tls_free_in_buf(data); + wpabuf_free(data->tls_out); + data->tls_out = NULL; } @@ -114,17 +118,17 @@ struct wpabuf * eap_server_tls_build_msg(struct eap_ssl_data *data, size_t send_len, plen; wpa_printf(MSG_DEBUG, "SSL: Generating Request"); - if (data->out_buf == NULL) { - wpa_printf(MSG_ERROR, "SSL: out_buf NULL in %s", __func__); + if (data->tls_out == NULL) { + wpa_printf(MSG_ERROR, "SSL: tls_out NULL in %s", __func__); return NULL; } flags = version; - send_len = wpabuf_len(data->out_buf) - data->out_used; + send_len = wpabuf_len(data->tls_out) - data->tls_out_pos; if (1 + send_len > data->tls_out_limit) { send_len = data->tls_out_limit - 1; flags |= EAP_TLS_FLAGS_MORE_FRAGMENTS; - if (data->out_used == 0) { + if (data->tls_out_pos == 0) { flags |= EAP_TLS_FLAGS_LENGTH_INCLUDED; send_len -= 4; } @@ -141,25 +145,25 @@ struct wpabuf * eap_server_tls_build_msg(struct eap_ssl_data *data, wpabuf_put_u8(req, flags); /* Flags */ if (flags & EAP_TLS_FLAGS_LENGTH_INCLUDED) - wpabuf_put_be32(req, wpabuf_len(data->out_buf)); + wpabuf_put_be32(req, wpabuf_len(data->tls_out)); - wpabuf_put_data(req, wpabuf_head_u8(data->out_buf) + data->out_used, + wpabuf_put_data(req, wpabuf_head_u8(data->tls_out) + data->tls_out_pos, send_len); - data->out_used += send_len; + data->tls_out_pos += send_len; - if (data->out_used == wpabuf_len(data->out_buf)) { + if (data->tls_out_pos == wpabuf_len(data->tls_out)) { wpa_printf(MSG_DEBUG, "SSL: Sending out %lu bytes " "(message sent completely)", (unsigned long) send_len); - wpabuf_free(data->out_buf); - data->out_buf = NULL; - data->out_used = 0; + wpabuf_free(data->tls_out); + data->tls_out = NULL; + data->tls_out_pos = 0; data->state = MSG; } else { wpa_printf(MSG_DEBUG, "SSL: Sending out %lu bytes " "(%lu more to send)", (unsigned long) send_len, - (unsigned long) wpabuf_len(data->out_buf) - - data->out_used); + (unsigned long) wpabuf_len(data->tls_out) - + data->tls_out_pos); data->state = WAIT_FRAG_ACK; } @@ -185,15 +189,15 @@ static int eap_server_tls_process_cont(struct eap_ssl_data *data, const u8 *buf, size_t len) { /* Process continuation of a pending message */ - if (len > wpabuf_tailroom(data->in_buf)) { + if (len > wpabuf_tailroom(data->tls_in)) { wpa_printf(MSG_DEBUG, "SSL: Fragment overflow"); return -1; } - wpabuf_put_data(data->in_buf, buf, len); + wpabuf_put_data(data->tls_in, buf, len); wpa_printf(MSG_DEBUG, "SSL: Received %lu bytes, waiting for %lu " "bytes more", (unsigned long) len, - (unsigned long) wpabuf_tailroom(data->in_buf)); + (unsigned long) wpabuf_tailroom(data->tls_in)); return 0; } @@ -204,13 +208,13 @@ static int eap_server_tls_process_fragment(struct eap_ssl_data *data, const u8 *buf, size_t len) { /* Process a fragment that is not the last one of the message */ - if (data->in_buf == NULL && !(flags & EAP_TLS_FLAGS_LENGTH_INCLUDED)) { + if (data->tls_in == NULL && !(flags & EAP_TLS_FLAGS_LENGTH_INCLUDED)) { wpa_printf(MSG_DEBUG, "SSL: No Message Length field in a " "fragmented packet"); return -1; } - if (data->in_buf == NULL) { + if (data->tls_in == NULL) { /* First fragment of the message */ /* Limit length to avoid rogue peers from causing large @@ -221,16 +225,16 @@ static int eap_server_tls_process_fragment(struct eap_ssl_data *data, return -1; } - data->in_buf = wpabuf_alloc(message_length); - if (data->in_buf == NULL) { + data->tls_in = wpabuf_alloc(message_length); + if (data->tls_in == NULL) { wpa_printf(MSG_DEBUG, "SSL: No memory for message"); return -1; } - wpabuf_put_data(data->in_buf, buf, len); + wpabuf_put_data(data->tls_in, buf, len); wpa_printf(MSG_DEBUG, "SSL: Received %lu bytes in first " "fragment, waiting for %lu bytes more", (unsigned long) len, - (unsigned long) wpabuf_tailroom(data->in_buf)); + (unsigned long) wpabuf_tailroom(data->tls_in)); } return 0; @@ -239,30 +243,28 @@ static int eap_server_tls_process_fragment(struct eap_ssl_data *data, int eap_server_tls_phase1(struct eap_sm *sm, struct eap_ssl_data *data) { - u8 *next; - size_t next_len; - - next = tls_connection_server_handshake( - sm->ssl_ctx, data->conn, - wpabuf_mhead(data->in_buf), - wpabuf_len(data->in_buf), - &next_len); - if (next == NULL) { - wpa_printf(MSG_INFO, "SSL: TLS processing failed"); - return -1; - } - if (data->out_buf) { + if (data->tls_out) { /* This should not happen.. */ wpa_printf(MSG_INFO, "SSL: pending tls_out data when " "processing new message"); - os_free(data->out_buf); - WPA_ASSERT(data->out_buf == NULL); + wpabuf_free(data->tls_out); + WPA_ASSERT(data->tls_out == NULL); } - data->out_buf = wpabuf_alloc_ext_data(next, next_len); - if (data->out_buf == NULL) { - os_free(next); + + data->tls_out = tls_connection_server_handshake(sm->ssl_ctx, + data->conn, + data->tls_in, NULL); + if (data->tls_out == NULL) { + wpa_printf(MSG_INFO, "SSL: TLS processing failed"); + return -1; + } + if (tls_connection_get_failed(sm->ssl_ctx, data->conn)) { + /* TLS processing has failed - return error */ + wpa_printf(MSG_DEBUG, "SSL: Failed - tls_out available to " + "report error"); return -1; } + return 0; } @@ -299,7 +301,7 @@ static int eap_server_tls_reassemble(struct eap_ssl_data *data, u8 flags, return 1; } - if (data->in_buf && + if (data->tls_in && eap_server_tls_process_cont(data, *pos, end - *pos) < 0) return -1; @@ -317,10 +319,10 @@ static int eap_server_tls_reassemble(struct eap_ssl_data *data, u8 flags, data->state = MSG; } - if (data->in_buf == NULL) { + if (data->tls_in == NULL) { /* Wrap unfragmented messages as wpabuf without extra copy */ wpabuf_set(&data->tmpbuf, *pos, end - *pos); - data->in_buf = &data->tmpbuf; + data->tls_in = &data->tmpbuf; } return 0; @@ -329,36 +331,25 @@ static int eap_server_tls_reassemble(struct eap_ssl_data *data, u8 flags, static void eap_server_tls_free_in_buf(struct eap_ssl_data *data) { - if (data->in_buf != &data->tmpbuf) - wpabuf_free(data->in_buf); - data->in_buf = NULL; + if (data->tls_in != &data->tmpbuf) + wpabuf_free(data->tls_in); + data->tls_in = NULL; } struct wpabuf * eap_server_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data, - const u8 *plain, size_t plain_len) + const struct wpabuf *plain) { - int res; struct wpabuf *buf; - size_t buf_len; - /* reserve some extra room for encryption overhead */ - buf_len = plain_len + 300; - buf = wpabuf_alloc(buf_len); - if (buf == NULL) - return NULL; - res = tls_connection_encrypt(sm->ssl_ctx, data->conn, - plain, plain_len, wpabuf_put(buf, 0), - buf_len); - if (res < 0) { + buf = tls_connection_encrypt(sm->ssl_ctx, data->conn, + plain); + if (buf == NULL) { wpa_printf(MSG_INFO, "SSL: Failed to encrypt Phase 2 data"); - wpabuf_free(buf); return NULL; } - wpabuf_put(buf, res); - return buf; } diff --git a/contrib/wpa/src/eap_server/eap_tnc.c b/contrib/wpa/src/eap_server/eap_server_tnc.c index 4cb3ecf..f3b70ed 100644 --- a/contrib/wpa/src/eap_server/eap_tnc.c +++ b/contrib/wpa/src/eap_server/eap_server_tnc.c @@ -1,6 +1,6 @@ /* * EAP server method: EAP-TNC (Trusted Network Connect) - * Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-2010, Jouni Malinen <j@w1.fi> * * This 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,14 +21,18 @@ struct eap_tnc_data { - enum { START, CONTINUE, RECOMMENDATION, FRAG_ACK, WAIT_FRAG_ACK, DONE, - FAIL } state; + enum eap_tnc_state { + START, CONTINUE, RECOMMENDATION, FRAG_ACK, WAIT_FRAG_ACK, DONE, + FAIL + } state; enum { ALLOW, ISOLATE, NO_ACCESS, NO_RECOMMENDATION } recommendation; struct tncs_data *tncs; struct wpabuf *in_buf; struct wpabuf *out_buf; size_t out_used; size_t fragment_size; + unsigned int was_done:1; + unsigned int was_fail:1; }; @@ -41,6 +45,38 @@ struct eap_tnc_data { #define EAP_TNC_VERSION 1 +static const char * eap_tnc_state_txt(enum eap_tnc_state state) +{ + switch (state) { + case START: + return "START"; + case CONTINUE: + return "CONTINUE"; + case RECOMMENDATION: + return "RECOMMENDATION"; + case FRAG_ACK: + return "FRAG_ACK"; + case WAIT_FRAG_ACK: + return "WAIT_FRAG_ACK"; + case DONE: + return "DONE"; + case FAIL: + return "FAIL"; + } + return "??"; +} + + +static void eap_tnc_set_state(struct eap_tnc_data *data, + enum eap_tnc_state new_state) +{ + wpa_printf(MSG_DEBUG, "EAP-TNC: %s -> %s", + eap_tnc_state_txt(data->state), + eap_tnc_state_txt(new_state)); + data->state = new_state; +} + + static void * eap_tnc_init(struct eap_sm *sm) { struct eap_tnc_data *data; @@ -48,7 +84,7 @@ static void * eap_tnc_init(struct eap_sm *sm) data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; - data->state = START; + eap_tnc_set_state(data, START); data->tncs = tncs_init(); if (data->tncs == NULL) { os_free(data); @@ -81,13 +117,13 @@ static struct wpabuf * eap_tnc_build_start(struct eap_sm *sm, if (req == NULL) { wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory for " "request"); - data->state = FAIL; + eap_tnc_set_state(data, FAIL); return NULL; } wpabuf_put_u8(req, EAP_TNC_FLAGS_START | EAP_TNC_VERSION); - data->state = CONTINUE; + eap_tnc_set_state(data, CONTINUE); return req; } @@ -146,17 +182,17 @@ static struct wpabuf * eap_tnc_build_recommendation(struct eap_sm *sm, { switch (data->recommendation) { case ALLOW: - data->state = DONE; + eap_tnc_set_state(data, DONE); break; case ISOLATE: - data->state = FAIL; + eap_tnc_set_state(data, FAIL); /* TODO: support assignment to a different VLAN */ break; case NO_ACCESS: - data->state = FAIL; + eap_tnc_set_state(data, FAIL); break; case NO_RECOMMENDATION: - data->state = DONE; + eap_tnc_set_state(data, DONE); break; default: wpa_printf(MSG_DEBUG, "EAP-TNC: Unknown recommendation"); @@ -171,12 +207,13 @@ static struct wpabuf * eap_tnc_build_frag_ack(u8 id, u8 code) { struct wpabuf *msg; - msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 0, code, id); + msg = eap_msg_alloc(EAP_VENDOR_IETF, EAP_TYPE_TNC, 1, code, id); if (msg == NULL) { wpa_printf(MSG_ERROR, "EAP-TNC: Failed to allocate memory " "for fragment ack"); return NULL; } + wpabuf_put_u8(msg, EAP_TNC_VERSION); /* Flags */ wpa_printf(MSG_DEBUG, "EAP-TNC: Send fragment ack"); @@ -226,12 +263,20 @@ static struct wpabuf * eap_tnc_build_msg(struct eap_tnc_data *data, u8 id) wpabuf_free(data->out_buf); data->out_buf = NULL; data->out_used = 0; + if (data->was_fail) + eap_tnc_set_state(data, FAIL); + else if (data->was_done) + eap_tnc_set_state(data, DONE); } else { wpa_printf(MSG_DEBUG, "EAP-TNC: Sending out %lu bytes " "(%lu more to send)", (unsigned long) send_len, (unsigned long) wpabuf_len(data->out_buf) - data->out_used); - data->state = WAIT_FRAG_ACK; + if (data->state == FAIL) + data->was_fail = 1; + else if (data->state == DONE) + data->was_done = 1; + eap_tnc_set_state(data, WAIT_FRAG_ACK); } return req; @@ -327,27 +372,27 @@ static void tncs_process(struct eap_tnc_data *data, struct wpabuf *inbuf) switch (res) { case TNCCS_RECOMMENDATION_ALLOW: wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS allowed access"); - data->state = RECOMMENDATION; + eap_tnc_set_state(data, RECOMMENDATION); data->recommendation = ALLOW; break; case TNCCS_RECOMMENDATION_NO_RECOMMENDATION: wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS has no recommendation"); - data->state = RECOMMENDATION; + eap_tnc_set_state(data, RECOMMENDATION); data->recommendation = NO_RECOMMENDATION; break; case TNCCS_RECOMMENDATION_ISOLATE: wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS requested isolation"); - data->state = RECOMMENDATION; + eap_tnc_set_state(data, RECOMMENDATION); data->recommendation = ISOLATE; break; case TNCCS_RECOMMENDATION_NO_ACCESS: wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS rejected access"); - data->state = RECOMMENDATION; + eap_tnc_set_state(data, RECOMMENDATION); data->recommendation = NO_ACCESS; break; case TNCCS_PROCESS_ERROR: wpa_printf(MSG_DEBUG, "EAP-TNC: TNCS processing error"); - data->state = FAIL; + eap_tnc_set_state(data, FAIL); break; default: break; @@ -361,7 +406,7 @@ static int eap_tnc_process_cont(struct eap_tnc_data *data, /* Process continuation of a pending message */ if (len > wpabuf_tailroom(data->in_buf)) { wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment overflow"); - data->state = FAIL; + eap_tnc_set_state(data, FAIL); return -1; } @@ -435,7 +480,7 @@ static void eap_tnc_process(struct eap_sm *sm, void *priv, if (flags & EAP_TNC_FLAGS_LENGTH_INCLUDED) { if (end - pos < 4) { wpa_printf(MSG_DEBUG, "EAP-TNC: Message underflow"); - data->state = FAIL; + eap_tnc_set_state(data, FAIL); return; } message_length = WPA_GET_BE32(pos); @@ -445,7 +490,7 @@ static void eap_tnc_process(struct eap_sm *sm, void *priv, wpa_printf(MSG_DEBUG, "EAP-TNC: Invalid Message " "Length (%d; %ld remaining in this msg)", message_length, (long) (end - pos)); - data->state = FAIL; + eap_tnc_set_state(data, FAIL); return; } } @@ -453,32 +498,32 @@ static void eap_tnc_process(struct eap_sm *sm, void *priv, "Message Length %u", flags, message_length); if (data->state == WAIT_FRAG_ACK) { - if (len != 0) { + if (len > 1) { wpa_printf(MSG_DEBUG, "EAP-TNC: Unexpected payload " "in WAIT_FRAG_ACK state"); - data->state = FAIL; + eap_tnc_set_state(data, FAIL); return; } wpa_printf(MSG_DEBUG, "EAP-TNC: Fragment acknowledged"); - data->state = CONTINUE; + eap_tnc_set_state(data, CONTINUE); return; } if (data->in_buf && eap_tnc_process_cont(data, pos, end - pos) < 0) { - data->state = FAIL; + eap_tnc_set_state(data, FAIL); return; } if (flags & EAP_TNC_FLAGS_MORE_FRAGMENTS) { if (eap_tnc_process_fragment(data, flags, message_length, pos, end - pos) < 0) - data->state = FAIL; + eap_tnc_set_state(data, FAIL); else - data->state = FRAG_ACK; + eap_tnc_set_state(data, FRAG_ACK); return; } else if (data->state == FRAG_ACK) { wpa_printf(MSG_DEBUG, "EAP-TNC: All fragments received"); - data->state = CONTINUE; + eap_tnc_set_state(data, CONTINUE); } if (data->in_buf == NULL) { diff --git a/contrib/wpa/src/eap_server/eap_ttls.c b/contrib/wpa/src/eap_server/eap_server_ttls.c index 21e4b21..702c50c 100644 --- a/contrib/wpa/src/eap_server/eap_ttls.c +++ b/contrib/wpa/src/eap_server/eap_server_ttls.c @@ -15,12 +15,12 @@ #include "includes.h" #include "common.h" +#include "crypto/ms_funcs.h" +#include "crypto/sha1.h" +#include "crypto/tls.h" #include "eap_server/eap_i.h" #include "eap_server/eap_tls_common.h" -#include "ms_funcs.h" -#include "sha1.h" #include "eap_common/chap.h" -#include "tls.h" #include "eap_common/eap_ttls.h" @@ -163,14 +163,14 @@ struct eap_ttls_avp { }; -static int eap_ttls_avp_parse(u8 *buf, size_t len, struct eap_ttls_avp *parse) +static int eap_ttls_avp_parse(struct wpabuf *buf, struct eap_ttls_avp *parse) { struct ttls_avp *avp; u8 *pos; int left; - pos = buf; - left = len; + pos = wpabuf_mhead(buf); + left = wpabuf_len(buf); os_memset(parse, 0, sizeof(*parse)); while (left > 0) { @@ -449,8 +449,6 @@ static struct wpabuf * eap_ttls_build_phase2_eap_req( struct eap_sm *sm, struct eap_ttls_data *data, u8 id) { struct wpabuf *buf, *encr_req; - u8 *req; - size_t req_len; buf = data->phase2_method->buildReq(sm, data->phase2_priv, id); @@ -467,12 +465,10 @@ static struct wpabuf * eap_ttls_build_phase2_eap_req( return NULL; } - req = wpabuf_mhead(buf); - req_len = wpabuf_len(buf); - wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS/EAP: Encrypt encapsulated Phase " - "2 data", req, req_len); + wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS/EAP: Encrypt encapsulated " + "Phase 2 data", buf); - encr_req = eap_server_tls_encrypt(sm, &data->ssl, req, req_len); + encr_req = eap_server_tls_encrypt(sm, &data->ssl, buf); wpabuf_free(buf); return encr_req; @@ -482,10 +478,9 @@ static struct wpabuf * eap_ttls_build_phase2_eap_req( static struct wpabuf * eap_ttls_build_phase2_mschapv2( struct eap_sm *sm, struct eap_ttls_data *data) { - struct wpabuf *encr_req; + struct wpabuf *encr_req, msgbuf; u8 *req, *pos, *end; int ret; - size_t req_len; pos = req = os_malloc(100); if (req == NULL) @@ -510,11 +505,11 @@ static struct wpabuf * eap_ttls_build_phase2_mschapv2( AVP_PAD(req, pos); } - req_len = pos - req; - wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Encrypting Phase 2 " - "data", req, req_len); + wpabuf_set(&msgbuf, req, pos - req); + wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Encrypting Phase 2 " + "data", &msgbuf); - encr_req = eap_server_tls_encrypt(sm, &data->ssl, req, req_len); + encr_req = eap_server_tls_encrypt(sm, &data->ssl, &msgbuf); os_free(req); return encr_req; @@ -524,25 +519,8 @@ static struct wpabuf * eap_ttls_build_phase2_mschapv2( static struct wpabuf * eap_ttls_build_phase_finished( struct eap_sm *sm, struct eap_ttls_data *data, int final) { - int len; - struct wpabuf *req; - const int max_len = 300; - - req = wpabuf_alloc(max_len); - if (req == NULL) - return NULL; - - len = tls_connection_ia_send_phase_finished(sm->ssl_ctx, - data->ssl.conn, final, - wpabuf_mhead(req), - max_len); - if (len < 0) { - wpabuf_free(req); - return NULL; - } - wpabuf_put(req, len); - - return req; + return tls_connection_ia_send_phase_finished(sm->ssl_ctx, + data->ssl.conn, final); } @@ -571,20 +549,20 @@ static struct wpabuf * eap_ttls_buildReq(struct eap_sm *sm, void *priv, u8 id) } break; case PHASE2_METHOD: - wpabuf_free(data->ssl.out_buf); - data->ssl.out_used = 0; - data->ssl.out_buf = eap_ttls_build_phase2_eap_req(sm, data, + wpabuf_free(data->ssl.tls_out); + data->ssl.tls_out_pos = 0; + data->ssl.tls_out = eap_ttls_build_phase2_eap_req(sm, data, id); break; case PHASE2_MSCHAPV2_RESP: - wpabuf_free(data->ssl.out_buf); - data->ssl.out_used = 0; - data->ssl.out_buf = eap_ttls_build_phase2_mschapv2(sm, data); + wpabuf_free(data->ssl.tls_out); + data->ssl.tls_out_pos = 0; + data->ssl.tls_out = eap_ttls_build_phase2_mschapv2(sm, data); break; case PHASE_FINISHED: - wpabuf_free(data->ssl.out_buf); - data->ssl.out_used = 0; - data->ssl.out_buf = eap_ttls_build_phase_finished(sm, data, 1); + wpabuf_free(data->ssl.tls_out); + data->ssl.tls_out_pos = 0; + data->ssl.tls_out = eap_ttls_build_phase_finished(sm, data, 1); break; default: wpa_printf(MSG_DEBUG, "EAP-TTLS: %s - unexpected state %d", @@ -1126,18 +1104,11 @@ static void eap_ttls_process_phase2(struct eap_sm *sm, struct eap_ttls_data *data, struct wpabuf *in_buf) { - u8 *in_decrypted; - int len_decrypted; + struct wpabuf *in_decrypted; struct eap_ttls_avp parse; - size_t buf_len; - u8 *in_data; - size_t in_len; - - in_data = wpabuf_mhead(in_buf); - in_len = wpabuf_len(in_buf); wpa_printf(MSG_DEBUG, "EAP-TTLS: received %lu bytes encrypted data for" - " Phase 2", (unsigned long) in_len); + " Phase 2", (unsigned long) wpabuf_len(in_buf)); if (data->pending_phase2_eap_resp) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Pending Phase 2 EAP response " @@ -1150,35 +1121,17 @@ static void eap_ttls_process_phase2(struct eap_sm *sm, return; } - buf_len = in_len; - /* - * Even though we try to disable TLS compression, it is possible that - * this cannot be done with all TLS libraries. Add extra buffer space - * to handle the possibility of the decrypted data being longer than - * input data. - */ - buf_len += 500; - buf_len *= 3; - in_decrypted = os_malloc(buf_len); + in_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, + in_buf); if (in_decrypted == NULL) { - wpa_printf(MSG_WARNING, "EAP-TTLS: failed to allocate memory " - "for decryption"); - return; - } - - len_decrypted = tls_connection_decrypt(sm->ssl_ctx, data->ssl.conn, - in_data, in_len, - in_decrypted, buf_len); - if (len_decrypted < 0) { wpa_printf(MSG_INFO, "EAP-TTLS: Failed to decrypt Phase 2 " "data"); - os_free(in_decrypted); eap_ttls_state(data, FAILURE); return; } if (data->state == PHASE_FINISHED) { - if (len_decrypted == 0 && + if (wpabuf_len(in_decrypted) == 0 && tls_connection_ia_final_phase_finished(sm->ssl_ctx, data->ssl.conn)) { wpa_printf(MSG_DEBUG, "EAP-TTLS: FinalPhaseFinished " @@ -1190,16 +1143,16 @@ static void eap_ttls_process_phase2(struct eap_sm *sm, eap_ttls_state(data, FAILURE); } - os_free(in_decrypted); + wpabuf_free(in_decrypted); return; } - wpa_hexdump_key(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 EAP", - in_decrypted, len_decrypted); + wpa_hexdump_buf_key(MSG_DEBUG, "EAP-TTLS: Decrypted Phase 2 EAP", + in_decrypted); - if (eap_ttls_avp_parse(in_decrypted, len_decrypted, &parse) < 0) { + if (eap_ttls_avp_parse(in_decrypted, &parse) < 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS: Failed to parse AVPs"); - os_free(in_decrypted); + wpabuf_free(in_decrypted); eap_ttls_state(data, FAILURE); return; } @@ -1221,14 +1174,14 @@ static void eap_ttls_process_phase2(struct eap_sm *sm, } } -#ifdef EAP_TNC +#ifdef EAP_SERVER_TNC if (data->tnc_started && parse.eap == NULL) { wpa_printf(MSG_DEBUG, "EAP-TTLS: TNC started but no EAP " "response from peer"); eap_ttls_state(data, FAILURE); goto done; } -#endif /* EAP_TNC */ +#endif /* EAP_SERVER_TNC */ if (parse.eap) { eap_ttls_process_phase2_eap(sm, data, parse.eap, @@ -1257,14 +1210,14 @@ static void eap_ttls_process_phase2(struct eap_sm *sm, } done: - os_free(in_decrypted); + wpabuf_free(in_decrypted); os_free(parse.eap); } static void eap_ttls_start_tnc(struct eap_sm *sm, struct eap_ttls_data *data) { -#ifdef EAP_TNC +#ifdef EAP_SERVER_TNC if (!sm->tnc || data->state != SUCCESS || data->tnc_started) return; @@ -1277,7 +1230,7 @@ static void eap_ttls_start_tnc(struct eap_sm *sm, struct eap_ttls_data *data) data->tnc_started = 1; eap_ttls_state(data, PHASE2_METHOD); -#endif /* EAP_TNC */ +#endif /* EAP_SERVER_TNC */ } @@ -1318,11 +1271,11 @@ static void eap_ttls_process_msg(struct eap_sm *sm, void *priv, case PHASE2_START: case PHASE2_METHOD: case PHASE_FINISHED: - eap_ttls_process_phase2(sm, data, data->ssl.in_buf); + eap_ttls_process_phase2(sm, data, data->ssl.tls_in); eap_ttls_start_tnc(sm, data); break; case PHASE2_MSCHAPV2_RESP: - if (data->mschapv2_resp_ok && wpabuf_len(data->ssl.in_buf) == + if (data->mschapv2_resp_ok && wpabuf_len(data->ssl.tls_in) == 0) { wpa_printf(MSG_DEBUG, "EAP-TTLS/MSCHAPV2: Peer " "acknowledged response"); @@ -1337,7 +1290,7 @@ static void eap_ttls_process_msg(struct eap_sm *sm, void *priv, "frame from peer (payload len %lu, " "expected empty frame)", (unsigned long) - wpabuf_len(data->ssl.in_buf)); + wpabuf_len(data->ssl.tls_in)); eap_ttls_state(data, FAILURE); } eap_ttls_start_tnc(sm, data); diff --git a/contrib/wpa/src/eap_server/eap_vendor_test.c b/contrib/wpa/src/eap_server/eap_server_vendor_test.c index 0dd0aca..0dd0aca 100644 --- a/contrib/wpa/src/eap_server/eap_vendor_test.c +++ b/contrib/wpa/src/eap_server/eap_server_vendor_test.c diff --git a/contrib/wpa/src/eap_server/eap_wsc.c b/contrib/wpa/src/eap_server/eap_server_wsc.c index 3c17577..77cf9e2 100644 --- a/contrib/wpa/src/eap_server/eap_wsc.c +++ b/contrib/wpa/src/eap_server/eap_server_wsc.c @@ -22,7 +22,7 @@ struct eap_wsc_data { - enum { START, MSG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state; + enum { START, MESG, FRAG_ACK, WAIT_FRAG_ACK, DONE, FAIL } state; int registrar; struct wpabuf *in_buf; struct wpabuf *out_buf; @@ -34,13 +34,14 @@ struct eap_wsc_data { }; +#ifndef CONFIG_NO_STDOUT_DEBUG static const char * eap_wsc_state_txt(int state) { switch (state) { case START: return "START"; - case MSG: - return "MSG"; + case MESG: + return "MESG"; case FRAG_ACK: return "FRAG_ACK"; case WAIT_FRAG_ACK: @@ -53,6 +54,7 @@ static const char * eap_wsc_state_txt(int state) return "?"; } } +#endif /* CONFIG_NO_STDOUT_DEBUG */ static void eap_wsc_state(struct eap_wsc_data *data, int state) @@ -102,7 +104,7 @@ static void * eap_wsc_init(struct eap_sm *sm) data = os_zalloc(sizeof(*data)); if (data == NULL) return NULL; - data->state = registrar ? START : MSG; + data->state = registrar ? START : MESG; data->registrar = registrar; os_memset(&cfg, 0, sizeof(cfg)); @@ -117,15 +119,24 @@ static void * eap_wsc_init(struct eap_sm *sm) } } else { if (sm->user == NULL || sm->user->password == NULL) { - wpa_printf(MSG_INFO, "EAP-WSC: No AP PIN (password) " - "configured for Enrollee functionality"); - os_free(data); - return NULL; + /* + * In theory, this should not really be needed, but + * Windows 7 uses Registrar mode to probe AP's WPS + * capabilities before trying to use Enrollee and fails + * if the AP does not allow that probing to happen.. + */ + wpa_printf(MSG_DEBUG, "EAP-WSC: No AP PIN (password) " + "configured for Enrollee functionality - " + "allow for probing capabilities (M1)"); + } else { + cfg.pin = sm->user->password; + cfg.pin_len = sm->user->password_len; } - cfg.pin = sm->user->password; - cfg.pin_len = sm->user->password_len; } cfg.assoc_wps_ie = sm->assoc_wps_ie; + cfg.peer_addr = sm->peer_addr; + if (0 /* TODO: could provide option for forcing PSK format */) + cfg.use_psk_key = 1; data->wps = wps_init(&cfg); if (data->wps == NULL) { os_free(data); @@ -212,7 +223,7 @@ static struct wpabuf * eap_wsc_build_msg(struct eap_wsc_data *data, u8 id) wpabuf_free(data->out_buf); data->out_buf = NULL; data->out_used = 0; - eap_wsc_state(data, MSG); + eap_wsc_state(data, MESG); } else { wpa_printf(MSG_DEBUG, "EAP-WSC: Sending out %lu bytes " "(%lu more to send)", (unsigned long) send_len, @@ -232,7 +243,7 @@ static struct wpabuf * eap_wsc_buildReq(struct eap_sm *sm, void *priv, u8 id) switch (data->state) { case START: return eap_wsc_build_start(sm, data, id); - case MSG: + case MESG: if (data->out_buf == NULL) { data->out_buf = wps_get_msg(data->wps, &data->out_op_code); @@ -385,7 +396,7 @@ static void eap_wsc_process(struct eap_sm *sm, void *priv, return; } wpa_printf(MSG_DEBUG, "EAP-WSC: Fragment acknowledged"); - eap_wsc_state(data, MSG); + eap_wsc_state(data, MESG); return; } @@ -427,14 +438,14 @@ static void eap_wsc_process(struct eap_sm *sm, void *priv, eap_wsc_state(data, FAIL); break; case WPS_CONTINUE: - eap_wsc_state(data, MSG); + eap_wsc_state(data, MESG); break; case WPS_FAILURE: wpa_printf(MSG_DEBUG, "EAP-WSC: WPS processing failed"); eap_wsc_state(data, FAIL); break; case WPS_PENDING: - eap_wsc_state(data, MSG); + eap_wsc_state(data, MESG); sm->method_pending = METHOD_PENDING_WAIT; eloop_cancel_timeout(eap_wsc_ext_reg_timeout, sm, data); eloop_register_timeout(5, 0, eap_wsc_ext_reg_timeout, diff --git a/contrib/wpa/src/eap_server/eap_sim_db.c b/contrib/wpa/src/eap_server/eap_sim_db.c index ed0bd3c..aba919a 100644 --- a/contrib/wpa/src/eap_server/eap_sim_db.c +++ b/contrib/wpa/src/eap_server/eap_sim_db.c @@ -1023,7 +1023,7 @@ int eap_sim_db_add_reauth(void *priv, const u8 *identity, } -#ifdef EAP_AKA_PRIME +#ifdef EAP_SERVER_AKA_PRIME /** * eap_sim_db_add_reauth_prime - EAP-AKA' DB: Add new re-authentication entry * @priv: Private data pointer from eap_sim_db_init() @@ -1062,7 +1062,7 @@ int eap_sim_db_add_reauth_prime(void *priv, const u8 *identity, return 0; } -#endif /* EAP_AKA_PRIME */ +#endif /* EAP_SERVER_AKA_PRIME */ /** diff --git a/contrib/wpa/src/eap_server/eap_sim_db.h b/contrib/wpa/src/eap_server/eap_sim_db.h index 6622181..ab89ae9 100644 --- a/contrib/wpa/src/eap_server/eap_sim_db.h +++ b/contrib/wpa/src/eap_server/eap_sim_db.h @@ -15,8 +15,6 @@ #ifndef EAP_SIM_DB_H #define EAP_SIM_DB_H -#ifdef EAP_SIM - #include "eap_common/eap_sim_common.h" /* Identity prefixes */ @@ -90,18 +88,4 @@ int eap_sim_db_resynchronize(void *priv, const u8 *identity, size_t identity_len, const u8 *auts, const u8 *_rand); -#else /* EAP_SIM */ -static inline void * -eap_sim_db_init(const char *config, - void (*get_complete_cb)(void *ctx, void *session_ctx), - void *ctx) -{ - return (void *) 1; -} - -static inline void eap_sim_db_deinit(void *priv) -{ -} -#endif /* EAP_SIM */ - #endif /* EAP_SIM_DB_H */ diff --git a/contrib/wpa/src/eap_server/eap_tls_common.h b/contrib/wpa/src/eap_server/eap_tls_common.h index ce8dd25..c34c401 100644 --- a/contrib/wpa/src/eap_server/eap_tls_common.h +++ b/contrib/wpa/src/eap_server/eap_tls_common.h @@ -1,6 +1,6 @@ /* - * hostapd / EAP-TLS/PEAP/TTLS/FAST common functions - * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi> + * EAP-TLS/PEAP/TTLS/FAST server common functions + * Copyright (c) 2004-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -15,19 +15,46 @@ #ifndef EAP_TLS_COMMON_H #define EAP_TLS_COMMON_H +/** + * struct eap_ssl_data - TLS data for EAP methods + */ struct eap_ssl_data { + /** + * conn - TLS connection context data from tls_connection_init() + */ struct tls_connection *conn; + /** + * tls_out - TLS message to be sent out in fragments + */ + struct wpabuf *tls_out; + + /** + * tls_out_pos - The current position in the outgoing TLS message + */ + size_t tls_out_pos; + + /** + * tls_out_limit - Maximum fragment size for outgoing TLS messages + */ size_t tls_out_limit; + /** + * tls_in - Received TLS message buffer for re-assembly + */ + struct wpabuf *tls_in; + + /** + * phase2 - Whether this TLS connection is used in EAP phase 2 (tunnel) + */ int phase2; + /** + * eap - EAP state machine allocated with eap_server_sm_init() + */ struct eap_sm *eap; enum { MSG, FRAG_ACK, WAIT_FRAG_ACK } state; - struct wpabuf *in_buf; - struct wpabuf *out_buf; - size_t out_used; struct wpabuf tmpbuf; }; @@ -53,7 +80,7 @@ struct wpabuf * eap_server_tls_build_ack(u8 id, int eap_type, int version); int eap_server_tls_phase1(struct eap_sm *sm, struct eap_ssl_data *data); struct wpabuf * eap_server_tls_encrypt(struct eap_sm *sm, struct eap_ssl_data *data, - const u8 *plain, size_t plain_len); + const struct wpabuf *plain); int eap_server_tls_process(struct eap_sm *sm, struct eap_ssl_data *data, struct wpabuf *respData, void *priv, int eap_type, int (*proc_version)(struct eap_sm *sm, void *priv, diff --git a/contrib/wpa/src/eap_server/ikev2.c b/contrib/wpa/src/eap_server/ikev2.c index 46767c5..435ba26 100644 --- a/contrib/wpa/src/eap_server/ikev2.c +++ b/contrib/wpa/src/eap_server/ikev2.c @@ -15,7 +15,7 @@ #include "includes.h" #include "common.h" -#include "dh_groups.h" +#include "crypto/dh_groups.h" #include "ikev2.h" diff --git a/contrib/wpa/src/eap_server/tncs.c b/contrib/wpa/src/eap_server/tncs.c index 21d83b3..497b51a 100644 --- a/contrib/wpa/src/eap_server/tncs.c +++ b/contrib/wpa/src/eap_server/tncs.c @@ -1231,6 +1231,7 @@ void tncs_global_deinit(void) } os_free(tncs_global_data); + tncs_global_data = NULL; } diff --git a/contrib/wpa/src/eapol_auth/Makefile b/contrib/wpa/src/eapol_auth/Makefile new file mode 100644 index 0000000..9c41962 --- /dev/null +++ b/contrib/wpa/src/eapol_auth/Makefile @@ -0,0 +1,8 @@ +all: + @echo Nothing to be made. + +clean: + rm -f *~ *.o *.d + +install: + @echo Nothing to be made. diff --git a/contrib/wpa/src/eapol_auth/eapol_auth_dump.c b/contrib/wpa/src/eapol_auth/eapol_auth_dump.c new file mode 100644 index 0000000..a0f0e8d --- /dev/null +++ b/contrib/wpa/src/eapol_auth/eapol_auth_dump.c @@ -0,0 +1,231 @@ +/* + * IEEE 802.1X-2004 Authenticator - State dump + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eap_server/eap.h" +#include "eapol_auth_sm.h" +#include "eapol_auth_sm_i.h" + +static inline const char * port_type_txt(PortTypes pt) +{ + switch (pt) { + case ForceUnauthorized: return "ForceUnauthorized"; + case ForceAuthorized: return "ForceAuthorized"; + case Auto: return "Auto"; + default: return "Unknown"; + } +} + + +static inline const char * port_state_txt(PortState ps) +{ + switch (ps) { + case Unauthorized: return "Unauthorized"; + case Authorized: return "Authorized"; + default: return "Unknown"; + } +} + + +static inline const char * ctrl_dir_txt(ControlledDirection dir) +{ + switch (dir) { + case Both: return "Both"; + case In: return "In"; + default: return "Unknown"; + } +} + + +static inline const char * auth_pae_state_txt(int s) +{ + switch (s) { + case AUTH_PAE_INITIALIZE: return "INITIALIZE"; + case AUTH_PAE_DISCONNECTED: return "DISCONNECTED"; + case AUTH_PAE_CONNECTING: return "CONNECTING"; + case AUTH_PAE_AUTHENTICATING: return "AUTHENTICATING"; + case AUTH_PAE_AUTHENTICATED: return "AUTHENTICATED"; + case AUTH_PAE_ABORTING: return "ABORTING"; + case AUTH_PAE_HELD: return "HELD"; + case AUTH_PAE_FORCE_AUTH: return "FORCE_AUTH"; + case AUTH_PAE_FORCE_UNAUTH: return "FORCE_UNAUTH"; + case AUTH_PAE_RESTART: return "RESTART"; + default: return "Unknown"; + } +} + + +static inline const char * be_auth_state_txt(int s) +{ + switch (s) { + case BE_AUTH_REQUEST: return "REQUEST"; + case BE_AUTH_RESPONSE: return "RESPONSE"; + case BE_AUTH_SUCCESS: return "SUCCESS"; + case BE_AUTH_FAIL: return "FAIL"; + case BE_AUTH_TIMEOUT: return "TIMEOUT"; + case BE_AUTH_IDLE: return "IDLE"; + case BE_AUTH_INITIALIZE: return "INITIALIZE"; + case BE_AUTH_IGNORE: return "IGNORE"; + default: return "Unknown"; + } +} + + +static inline const char * reauth_timer_state_txt(int s) +{ + switch (s) { + case REAUTH_TIMER_INITIALIZE: return "INITIALIZE"; + case REAUTH_TIMER_REAUTHENTICATE: return "REAUTHENTICATE"; + default: return "Unknown"; + } +} + + +static inline const char * auth_key_tx_state_txt(int s) +{ + switch (s) { + case AUTH_KEY_TX_NO_KEY_TRANSMIT: return "NO_KEY_TRANSMIT"; + case AUTH_KEY_TX_KEY_TRANSMIT: return "KEY_TRANSMIT"; + default: return "Unknown"; + } +} + + +static inline const char * key_rx_state_txt(int s) +{ + switch (s) { + case KEY_RX_NO_KEY_RECEIVE: return "NO_KEY_RECEIVE"; + case KEY_RX_KEY_RECEIVE: return "KEY_RECEIVE"; + default: return "Unknown"; + } +} + + +static inline const char * ctrl_dir_state_txt(int s) +{ + switch (s) { + case CTRL_DIR_FORCE_BOTH: return "FORCE_BOTH"; + case CTRL_DIR_IN_OR_BOTH: return "IN_OR_BOTH"; + default: return "Unknown"; + } +} + + +void eapol_auth_dump_state(FILE *f, const char *prefix, + struct eapol_state_machine *sm) +{ + fprintf(f, "%sEAPOL state machine:\n", prefix); + fprintf(f, "%s aWhile=%d quietWhile=%d reAuthWhen=%d\n", prefix, + sm->aWhile, sm->quietWhile, sm->reAuthWhen); +#define _SB(b) ((b) ? "TRUE" : "FALSE") + fprintf(f, + "%s authAbort=%s authFail=%s authPortStatus=%s authStart=%s\n" + "%s authTimeout=%s authSuccess=%s eapFail=%s eapolEap=%s\n" + "%s eapSuccess=%s eapTimeout=%s initialize=%s " + "keyAvailable=%s\n" + "%s keyDone=%s keyRun=%s keyTxEnabled=%s portControl=%s\n" + "%s portEnabled=%s portValid=%s reAuthenticate=%s\n", + prefix, _SB(sm->authAbort), _SB(sm->authFail), + port_state_txt(sm->authPortStatus), _SB(sm->authStart), + prefix, _SB(sm->authTimeout), _SB(sm->authSuccess), + _SB(sm->eap_if->eapFail), _SB(sm->eapolEap), + prefix, _SB(sm->eap_if->eapSuccess), + _SB(sm->eap_if->eapTimeout), + _SB(sm->initialize), _SB(sm->eap_if->eapKeyAvailable), + prefix, _SB(sm->keyDone), _SB(sm->keyRun), + _SB(sm->keyTxEnabled), port_type_txt(sm->portControl), + prefix, _SB(sm->eap_if->portEnabled), _SB(sm->portValid), + _SB(sm->reAuthenticate)); + + fprintf(f, "%s Authenticator PAE:\n" + "%s state=%s\n" + "%s eapolLogoff=%s eapolStart=%s eapRestart=%s\n" + "%s portMode=%s reAuthCount=%d\n" + "%s quietPeriod=%d reAuthMax=%d\n" + "%s authEntersConnecting=%d\n" + "%s authEapLogoffsWhileConnecting=%d\n" + "%s authEntersAuthenticating=%d\n" + "%s authAuthSuccessesWhileAuthenticating=%d\n" + "%s authAuthTimeoutsWhileAuthenticating=%d\n" + "%s authAuthFailWhileAuthenticating=%d\n" + "%s authAuthEapStartsWhileAuthenticating=%d\n" + "%s authAuthEapLogoffWhileAuthenticating=%d\n" + "%s authAuthReauthsWhileAuthenticated=%d\n" + "%s authAuthEapStartsWhileAuthenticated=%d\n" + "%s authAuthEapLogoffWhileAuthenticated=%d\n", + prefix, prefix, auth_pae_state_txt(sm->auth_pae_state), prefix, + _SB(sm->eapolLogoff), _SB(sm->eapolStart), + _SB(sm->eap_if->eapRestart), + prefix, port_type_txt(sm->portMode), sm->reAuthCount, + prefix, sm->quietPeriod, sm->reAuthMax, + prefix, sm->authEntersConnecting, + prefix, sm->authEapLogoffsWhileConnecting, + prefix, sm->authEntersAuthenticating, + prefix, sm->authAuthSuccessesWhileAuthenticating, + prefix, sm->authAuthTimeoutsWhileAuthenticating, + prefix, sm->authAuthFailWhileAuthenticating, + prefix, sm->authAuthEapStartsWhileAuthenticating, + prefix, sm->authAuthEapLogoffWhileAuthenticating, + prefix, sm->authAuthReauthsWhileAuthenticated, + prefix, sm->authAuthEapStartsWhileAuthenticated, + prefix, sm->authAuthEapLogoffWhileAuthenticated); + + fprintf(f, "%s Backend Authentication:\n" + "%s state=%s\n" + "%s eapNoReq=%s eapReq=%s eapResp=%s\n" + "%s serverTimeout=%d\n" + "%s backendResponses=%d\n" + "%s backendAccessChallenges=%d\n" + "%s backendOtherRequestsToSupplicant=%d\n" + "%s backendAuthSuccesses=%d\n" + "%s backendAuthFails=%d\n", + prefix, prefix, + be_auth_state_txt(sm->be_auth_state), + prefix, _SB(sm->eap_if->eapNoReq), _SB(sm->eap_if->eapReq), + _SB(sm->eap_if->eapResp), + prefix, sm->serverTimeout, + prefix, sm->backendResponses, + prefix, sm->backendAccessChallenges, + prefix, sm->backendOtherRequestsToSupplicant, + prefix, sm->backendAuthSuccesses, + prefix, sm->backendAuthFails); + + fprintf(f, "%s Reauthentication Timer:\n" + "%s state=%s\n" + "%s reAuthPeriod=%d reAuthEnabled=%s\n", prefix, prefix, + reauth_timer_state_txt(sm->reauth_timer_state), prefix, + sm->reAuthPeriod, _SB(sm->reAuthEnabled)); + + fprintf(f, "%s Authenticator Key Transmit:\n" + "%s state=%s\n", prefix, prefix, + auth_key_tx_state_txt(sm->auth_key_tx_state)); + + fprintf(f, "%s Key Receive:\n" + "%s state=%s\n" + "%s rxKey=%s\n", prefix, prefix, + key_rx_state_txt(sm->key_rx_state), prefix, _SB(sm->rxKey)); + + fprintf(f, "%s Controlled Directions:\n" + "%s state=%s\n" + "%s adminControlledDirections=%s " + "operControlledDirections=%s\n" + "%s operEdge=%s\n", prefix, prefix, + ctrl_dir_state_txt(sm->ctrl_dir_state), + prefix, ctrl_dir_txt(sm->adminControlledDirections), + ctrl_dir_txt(sm->operControlledDirections), + prefix, _SB(sm->operEdge)); +#undef _SB +} diff --git a/contrib/wpa/src/eapol_auth/eapol_auth_sm.c b/contrib/wpa/src/eapol_auth/eapol_auth_sm.c new file mode 100644 index 0000000..a1976e8 --- /dev/null +++ b/contrib/wpa/src/eapol_auth/eapol_auth_sm.c @@ -0,0 +1,1139 @@ +/* + * IEEE 802.1X-2004 Authenticator - EAPOL state machine + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "state_machine.h" +#include "common/eapol_common.h" +#include "eap_common/eap_defs.h" +#include "eap_common/eap_common.h" +#include "eap_server/eap.h" +#include "eapol_auth_sm.h" +#include "eapol_auth_sm_i.h" + +#define STATE_MACHINE_DATA struct eapol_state_machine +#define STATE_MACHINE_DEBUG_PREFIX "IEEE 802.1X" +#define STATE_MACHINE_ADDR sm->addr + +static struct eapol_callbacks eapol_cb; + +/* EAPOL state machines are described in IEEE Std 802.1X-2004, Chap. 8.2 */ + +#define setPortAuthorized() \ +sm->eapol->cb.set_port_authorized(sm->eapol->conf.ctx, sm->sta, 1) +#define setPortUnauthorized() \ +sm->eapol->cb.set_port_authorized(sm->eapol->conf.ctx, sm->sta, 0) + +/* procedures */ +#define txCannedFail() eapol_auth_tx_canned_eap(sm, 0) +#define txCannedSuccess() eapol_auth_tx_canned_eap(sm, 1) +#define txReq() eapol_auth_tx_req(sm) +#define abortAuth() sm->eapol->cb.abort_auth(sm->eapol->conf.ctx, sm->sta) +#define txKey() sm->eapol->cb.tx_key(sm->eapol->conf.ctx, sm->sta) +#define processKey() do { } while (0) + + +static void eapol_sm_step_run(struct eapol_state_machine *sm); +static void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx); +static void eapol_auth_initialize(struct eapol_state_machine *sm); + + +static void eapol_auth_logger(struct eapol_authenticator *eapol, + const u8 *addr, eapol_logger_level level, + const char *txt) +{ + if (eapol->cb.logger == NULL) + return; + eapol->cb.logger(eapol->conf.ctx, addr, level, txt); +} + + +static void eapol_auth_vlogger(struct eapol_authenticator *eapol, + const u8 *addr, eapol_logger_level level, + const char *fmt, ...) +{ + char *format; + int maxlen; + va_list ap; + + if (eapol->cb.logger == NULL) + return; + + maxlen = os_strlen(fmt) + 100; + format = os_malloc(maxlen); + if (!format) + return; + + va_start(ap, fmt); + vsnprintf(format, maxlen, fmt, ap); + va_end(ap); + + eapol_auth_logger(eapol, addr, level, format); + + os_free(format); +} + + +static void eapol_auth_tx_canned_eap(struct eapol_state_machine *sm, + int success) +{ + struct eap_hdr eap; + + os_memset(&eap, 0, sizeof(eap)); + + eap.code = success ? EAP_CODE_SUCCESS : EAP_CODE_FAILURE; + eap.identifier = ++sm->last_eap_id; + eap.length = host_to_be16(sizeof(eap)); + + eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_DEBUG, + "Sending canned EAP packet %s (identifier %d)", + success ? "SUCCESS" : "FAILURE", eap.identifier); + sm->eapol->cb.eapol_send(sm->eapol->conf.ctx, sm->sta, + IEEE802_1X_TYPE_EAP_PACKET, + (u8 *) &eap, sizeof(eap)); + sm->dot1xAuthEapolFramesTx++; +} + + +static void eapol_auth_tx_req(struct eapol_state_machine *sm) +{ + if (sm->eap_if->eapReqData == NULL || + wpabuf_len(sm->eap_if->eapReqData) < sizeof(struct eap_hdr)) { + eapol_auth_logger(sm->eapol, sm->addr, + EAPOL_LOGGER_DEBUG, + "TxReq called, but there is no EAP request " + "from authentication server"); + return; + } + + if (sm->flags & EAPOL_SM_WAIT_START) { + wpa_printf(MSG_DEBUG, "EAPOL: Drop EAPOL TX to " MACSTR + " while waiting for EAPOL-Start", + MAC2STR(sm->addr)); + return; + } + + sm->last_eap_id = eap_get_id(sm->eap_if->eapReqData); + eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_DEBUG, + "Sending EAP Packet (identifier %d)", + sm->last_eap_id); + sm->eapol->cb.eapol_send(sm->eapol->conf.ctx, sm->sta, + IEEE802_1X_TYPE_EAP_PACKET, + wpabuf_head(sm->eap_if->eapReqData), + wpabuf_len(sm->eap_if->eapReqData)); + sm->dot1xAuthEapolFramesTx++; + if (eap_get_type(sm->eap_if->eapReqData) == EAP_TYPE_IDENTITY) + sm->dot1xAuthEapolReqIdFramesTx++; + else + sm->dot1xAuthEapolReqFramesTx++; +} + + +/** + * eapol_port_timers_tick - Port Timers state machine + * @eloop_ctx: struct eapol_state_machine * + * @timeout_ctx: Not used + * + * This statemachine is implemented as a function that will be called + * once a second as a registered event loop timeout. + */ +static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx) +{ + struct eapol_state_machine *state = timeout_ctx; + + if (state->aWhile > 0) { + state->aWhile--; + 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->quietWhile == 0) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR + " - quietWhile --> 0", + MAC2STR(state->addr)); + } + } + + if (state->reAuthWhen > 0) { + state->reAuthWhen--; + if (state->reAuthWhen == 0) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR + " - reAuthWhen --> 0", + MAC2STR(state->addr)); + } + } + + if (state->eap_if->retransWhile > 0) { + state->eap_if->retransWhile--; + if (state->eap_if->retransWhile == 0) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: " MACSTR + " - (EAP) retransWhile --> 0", + MAC2STR(state->addr)); + } + } + + eapol_sm_step_run(state); + + eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx, state); +} + + + +/* Authenticator PAE state machine */ + +SM_STATE(AUTH_PAE, INITIALIZE) +{ + SM_ENTRY_MA(AUTH_PAE, INITIALIZE, auth_pae); + sm->portMode = Auto; +} + + +SM_STATE(AUTH_PAE, DISCONNECTED) +{ + int from_initialize = sm->auth_pae_state == AUTH_PAE_INITIALIZE; + + if (sm->eapolLogoff) { + if (sm->auth_pae_state == AUTH_PAE_CONNECTING) + sm->authEapLogoffsWhileConnecting++; + else if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATED) + sm->authAuthEapLogoffWhileAuthenticated++; + } + + SM_ENTRY_MA(AUTH_PAE, DISCONNECTED, auth_pae); + + sm->authPortStatus = Unauthorized; + setPortUnauthorized(); + sm->reAuthCount = 0; + sm->eapolLogoff = FALSE; + if (!from_initialize) { + sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0, + sm->flags & EAPOL_SM_PREAUTH); + } +} + + +SM_STATE(AUTH_PAE, RESTART) +{ + if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATED) { + if (sm->reAuthenticate) + sm->authAuthReauthsWhileAuthenticated++; + if (sm->eapolStart) + sm->authAuthEapStartsWhileAuthenticated++; + if (sm->eapolLogoff) + sm->authAuthEapLogoffWhileAuthenticated++; + } + + SM_ENTRY_MA(AUTH_PAE, RESTART, auth_pae); + + sm->eap_if->eapRestart = TRUE; +} + + +SM_STATE(AUTH_PAE, CONNECTING) +{ + if (sm->auth_pae_state != AUTH_PAE_CONNECTING) + sm->authEntersConnecting++; + + SM_ENTRY_MA(AUTH_PAE, CONNECTING, auth_pae); + + sm->reAuthenticate = FALSE; + sm->reAuthCount++; +} + + +SM_STATE(AUTH_PAE, HELD) +{ + if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING && sm->authFail) + sm->authAuthFailWhileAuthenticating++; + + SM_ENTRY_MA(AUTH_PAE, HELD, auth_pae); + + sm->authPortStatus = Unauthorized; + setPortUnauthorized(); + sm->quietWhile = sm->quietPeriod; + sm->eapolLogoff = FALSE; + + eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_WARNING, + "authentication failed - EAP type: %d (%s)", + sm->eap_type_authsrv, + eap_server_get_name(0, sm->eap_type_authsrv)); + if (sm->eap_type_authsrv != sm->eap_type_supp) { + eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_INFO, + "Supplicant used different EAP type: " + "%d (%s)", sm->eap_type_supp, + eap_server_get_name(0, sm->eap_type_supp)); + } + sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 0, + sm->flags & EAPOL_SM_PREAUTH); +} + + +SM_STATE(AUTH_PAE, AUTHENTICATED) +{ + char *extra = ""; + + if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING && sm->authSuccess) + sm->authAuthSuccessesWhileAuthenticating++; + + SM_ENTRY_MA(AUTH_PAE, AUTHENTICATED, auth_pae); + + sm->authPortStatus = Authorized; + setPortAuthorized(); + sm->reAuthCount = 0; + if (sm->flags & EAPOL_SM_PREAUTH) + extra = " (pre-authentication)"; + else if (sm->flags & EAPOL_SM_FROM_PMKSA_CACHE) + extra = " (PMKSA cache)"; + eapol_auth_vlogger(sm->eapol, sm->addr, EAPOL_LOGGER_INFO, + "authenticated - EAP type: %d (%s)%s", + sm->eap_type_authsrv, + eap_server_get_name(0, sm->eap_type_authsrv), + extra); + sm->eapol->cb.finished(sm->eapol->conf.ctx, sm->sta, 1, + sm->flags & EAPOL_SM_PREAUTH); +} + + +SM_STATE(AUTH_PAE, AUTHENTICATING) +{ + SM_ENTRY_MA(AUTH_PAE, AUTHENTICATING, auth_pae); + + sm->eapolStart = FALSE; + sm->authSuccess = FALSE; + sm->authFail = FALSE; + sm->authTimeout = FALSE; + sm->authStart = TRUE; + sm->keyRun = FALSE; + sm->keyDone = FALSE; +} + + +SM_STATE(AUTH_PAE, ABORTING) +{ + if (sm->auth_pae_state == AUTH_PAE_AUTHENTICATING) { + if (sm->authTimeout) + sm->authAuthTimeoutsWhileAuthenticating++; + if (sm->eapolStart) + sm->authAuthEapStartsWhileAuthenticating++; + if (sm->eapolLogoff) + sm->authAuthEapLogoffWhileAuthenticating++; + } + + SM_ENTRY_MA(AUTH_PAE, ABORTING, auth_pae); + + sm->authAbort = TRUE; + sm->keyRun = FALSE; + sm->keyDone = FALSE; +} + + +SM_STATE(AUTH_PAE, FORCE_AUTH) +{ + SM_ENTRY_MA(AUTH_PAE, FORCE_AUTH, auth_pae); + + sm->authPortStatus = Authorized; + setPortAuthorized(); + sm->portMode = ForceAuthorized; + sm->eapolStart = FALSE; + txCannedSuccess(); +} + + +SM_STATE(AUTH_PAE, FORCE_UNAUTH) +{ + SM_ENTRY_MA(AUTH_PAE, FORCE_UNAUTH, auth_pae); + + sm->authPortStatus = Unauthorized; + setPortUnauthorized(); + sm->portMode = ForceUnauthorized; + sm->eapolStart = FALSE; + txCannedFail(); +} + + +SM_STEP(AUTH_PAE) +{ + if ((sm->portControl == Auto && sm->portMode != sm->portControl) || + sm->initialize || !sm->eap_if->portEnabled) + SM_ENTER_GLOBAL(AUTH_PAE, INITIALIZE); + else if (sm->portControl == ForceAuthorized && + sm->portMode != sm->portControl && + !(sm->initialize || !sm->eap_if->portEnabled)) + SM_ENTER_GLOBAL(AUTH_PAE, FORCE_AUTH); + else if (sm->portControl == ForceUnauthorized && + sm->portMode != sm->portControl && + !(sm->initialize || !sm->eap_if->portEnabled)) + SM_ENTER_GLOBAL(AUTH_PAE, FORCE_UNAUTH); + else { + switch (sm->auth_pae_state) { + case AUTH_PAE_INITIALIZE: + SM_ENTER(AUTH_PAE, DISCONNECTED); + break; + case AUTH_PAE_DISCONNECTED: + SM_ENTER(AUTH_PAE, RESTART); + break; + case AUTH_PAE_RESTART: + if (!sm->eap_if->eapRestart) + SM_ENTER(AUTH_PAE, CONNECTING); + break; + case AUTH_PAE_HELD: + if (sm->quietWhile == 0) + SM_ENTER(AUTH_PAE, RESTART); + break; + case AUTH_PAE_CONNECTING: + if (sm->eapolLogoff || sm->reAuthCount > sm->reAuthMax) + SM_ENTER(AUTH_PAE, DISCONNECTED); + else if ((sm->eap_if->eapReq && + sm->reAuthCount <= sm->reAuthMax) || + sm->eap_if->eapSuccess || sm->eap_if->eapFail) + SM_ENTER(AUTH_PAE, AUTHENTICATING); + break; + case AUTH_PAE_AUTHENTICATED: + if (sm->eapolStart || sm->reAuthenticate) + SM_ENTER(AUTH_PAE, RESTART); + else if (sm->eapolLogoff || !sm->portValid) + SM_ENTER(AUTH_PAE, DISCONNECTED); + break; + case AUTH_PAE_AUTHENTICATING: + if (sm->authSuccess && sm->portValid) + SM_ENTER(AUTH_PAE, AUTHENTICATED); + else if (sm->authFail || + (sm->keyDone && !sm->portValid)) + SM_ENTER(AUTH_PAE, HELD); + else if (sm->eapolStart || sm->eapolLogoff || + sm->authTimeout) + SM_ENTER(AUTH_PAE, ABORTING); + break; + case AUTH_PAE_ABORTING: + if (sm->eapolLogoff && !sm->authAbort) + SM_ENTER(AUTH_PAE, DISCONNECTED); + else if (!sm->eapolLogoff && !sm->authAbort) + SM_ENTER(AUTH_PAE, RESTART); + break; + case AUTH_PAE_FORCE_AUTH: + if (sm->eapolStart) + SM_ENTER(AUTH_PAE, FORCE_AUTH); + break; + case AUTH_PAE_FORCE_UNAUTH: + if (sm->eapolStart) + SM_ENTER(AUTH_PAE, FORCE_UNAUTH); + break; + } + } +} + + + +/* Backend Authentication state machine */ + +SM_STATE(BE_AUTH, INITIALIZE) +{ + SM_ENTRY_MA(BE_AUTH, INITIALIZE, be_auth); + + abortAuth(); + sm->eap_if->eapNoReq = FALSE; + sm->authAbort = FALSE; +} + + +SM_STATE(BE_AUTH, REQUEST) +{ + SM_ENTRY_MA(BE_AUTH, REQUEST, be_auth); + + txReq(); + sm->eap_if->eapReq = FALSE; + sm->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; +} + + +SM_STATE(BE_AUTH, RESPONSE) +{ + SM_ENTRY_MA(BE_AUTH, RESPONSE, be_auth); + + sm->authTimeout = FALSE; + sm->eapolEap = FALSE; + sm->eap_if->eapNoReq = FALSE; + sm->aWhile = sm->serverTimeout; + sm->eap_if->eapResp = TRUE; + /* sendRespToServer(); */ + sm->backendResponses++; +} + + +SM_STATE(BE_AUTH, SUCCESS) +{ + SM_ENTRY_MA(BE_AUTH, SUCCESS, be_auth); + + txReq(); + sm->authSuccess = TRUE; + sm->keyRun = TRUE; +} + + +SM_STATE(BE_AUTH, FAIL) +{ + SM_ENTRY_MA(BE_AUTH, FAIL, be_auth); + + txReq(); + sm->authFail = TRUE; +} + + +SM_STATE(BE_AUTH, TIMEOUT) +{ + SM_ENTRY_MA(BE_AUTH, TIMEOUT, be_auth); + + sm->authTimeout = TRUE; +} + + +SM_STATE(BE_AUTH, IDLE) +{ + SM_ENTRY_MA(BE_AUTH, IDLE, be_auth); + + sm->authStart = FALSE; +} + + +SM_STATE(BE_AUTH, IGNORE) +{ + SM_ENTRY_MA(BE_AUTH, IGNORE, be_auth); + + sm->eap_if->eapNoReq = FALSE; +} + + +SM_STEP(BE_AUTH) +{ + if (sm->portControl != Auto || sm->initialize || sm->authAbort) { + SM_ENTER_GLOBAL(BE_AUTH, INITIALIZE); + return; + } + + switch (sm->be_auth_state) { + case BE_AUTH_INITIALIZE: + SM_ENTER(BE_AUTH, IDLE); + break; + case BE_AUTH_REQUEST: + if (sm->eapolEap) + SM_ENTER(BE_AUTH, RESPONSE); + else if (sm->eap_if->eapReq) + SM_ENTER(BE_AUTH, REQUEST); + else if (sm->eap_if->eapTimeout) + SM_ENTER(BE_AUTH, TIMEOUT); + break; + case BE_AUTH_RESPONSE: + if (sm->eap_if->eapNoReq) + SM_ENTER(BE_AUTH, IGNORE); + if (sm->eap_if->eapReq) { + sm->backendAccessChallenges++; + SM_ENTER(BE_AUTH, REQUEST); + } else if (sm->aWhile == 0) + SM_ENTER(BE_AUTH, TIMEOUT); + else if (sm->eap_if->eapFail) { + sm->backendAuthFails++; + SM_ENTER(BE_AUTH, FAIL); + } else if (sm->eap_if->eapSuccess) { + sm->backendAuthSuccesses++; + SM_ENTER(BE_AUTH, SUCCESS); + } + break; + case BE_AUTH_SUCCESS: + SM_ENTER(BE_AUTH, IDLE); + break; + case BE_AUTH_FAIL: + SM_ENTER(BE_AUTH, IDLE); + break; + case BE_AUTH_TIMEOUT: + SM_ENTER(BE_AUTH, IDLE); + break; + case BE_AUTH_IDLE: + if (sm->eap_if->eapFail && sm->authStart) + SM_ENTER(BE_AUTH, FAIL); + else if (sm->eap_if->eapReq && sm->authStart) + SM_ENTER(BE_AUTH, REQUEST); + else if (sm->eap_if->eapSuccess && sm->authStart) + SM_ENTER(BE_AUTH, SUCCESS); + break; + case BE_AUTH_IGNORE: + if (sm->eapolEap) + SM_ENTER(BE_AUTH, RESPONSE); + else if (sm->eap_if->eapReq) + SM_ENTER(BE_AUTH, REQUEST); + else if (sm->eap_if->eapTimeout) + SM_ENTER(BE_AUTH, TIMEOUT); + break; + } +} + + + +/* Reauthentication Timer state machine */ + +SM_STATE(REAUTH_TIMER, INITIALIZE) +{ + SM_ENTRY_MA(REAUTH_TIMER, INITIALIZE, reauth_timer); + + sm->reAuthWhen = sm->reAuthPeriod; +} + + +SM_STATE(REAUTH_TIMER, REAUTHENTICATE) +{ + SM_ENTRY_MA(REAUTH_TIMER, REAUTHENTICATE, reauth_timer); + + sm->reAuthenticate = TRUE; + sm->eapol->cb.eapol_event(sm->eapol->conf.ctx, sm->sta, + EAPOL_AUTH_REAUTHENTICATE); +} + + +SM_STEP(REAUTH_TIMER) +{ + if (sm->portControl != Auto || sm->initialize || + sm->authPortStatus == Unauthorized || !sm->reAuthEnabled) { + SM_ENTER_GLOBAL(REAUTH_TIMER, INITIALIZE); + return; + } + + switch (sm->reauth_timer_state) { + case REAUTH_TIMER_INITIALIZE: + if (sm->reAuthWhen == 0) + SM_ENTER(REAUTH_TIMER, REAUTHENTICATE); + break; + case REAUTH_TIMER_REAUTHENTICATE: + SM_ENTER(REAUTH_TIMER, INITIALIZE); + break; + } +} + + + +/* Authenticator Key Transmit state machine */ + +SM_STATE(AUTH_KEY_TX, NO_KEY_TRANSMIT) +{ + SM_ENTRY_MA(AUTH_KEY_TX, NO_KEY_TRANSMIT, auth_key_tx); +} + + +SM_STATE(AUTH_KEY_TX, KEY_TRANSMIT) +{ + SM_ENTRY_MA(AUTH_KEY_TX, KEY_TRANSMIT, auth_key_tx); + + txKey(); + sm->eap_if->eapKeyAvailable = FALSE; + sm->keyDone = TRUE; +} + + +SM_STEP(AUTH_KEY_TX) +{ + if (sm->initialize || sm->portControl != Auto) { + SM_ENTER_GLOBAL(AUTH_KEY_TX, NO_KEY_TRANSMIT); + return; + } + + switch (sm->auth_key_tx_state) { + case AUTH_KEY_TX_NO_KEY_TRANSMIT: + if (sm->keyTxEnabled && sm->eap_if->eapKeyAvailable && + sm->keyRun && !(sm->flags & EAPOL_SM_USES_WPA)) + SM_ENTER(AUTH_KEY_TX, KEY_TRANSMIT); + break; + case AUTH_KEY_TX_KEY_TRANSMIT: + if (!sm->keyTxEnabled || !sm->keyRun) + SM_ENTER(AUTH_KEY_TX, NO_KEY_TRANSMIT); + else if (sm->eap_if->eapKeyAvailable) + SM_ENTER(AUTH_KEY_TX, KEY_TRANSMIT); + break; + } +} + + + +/* Key Receive state machine */ + +SM_STATE(KEY_RX, NO_KEY_RECEIVE) +{ + SM_ENTRY_MA(KEY_RX, NO_KEY_RECEIVE, key_rx); +} + + +SM_STATE(KEY_RX, KEY_RECEIVE) +{ + SM_ENTRY_MA(KEY_RX, KEY_RECEIVE, key_rx); + + processKey(); + sm->rxKey = FALSE; +} + + +SM_STEP(KEY_RX) +{ + if (sm->initialize || !sm->eap_if->portEnabled) { + SM_ENTER_GLOBAL(KEY_RX, NO_KEY_RECEIVE); + return; + } + + switch (sm->key_rx_state) { + case KEY_RX_NO_KEY_RECEIVE: + if (sm->rxKey) + SM_ENTER(KEY_RX, KEY_RECEIVE); + break; + case KEY_RX_KEY_RECEIVE: + if (sm->rxKey) + SM_ENTER(KEY_RX, KEY_RECEIVE); + break; + } +} + + + +/* Controlled Directions state machine */ + +SM_STATE(CTRL_DIR, FORCE_BOTH) +{ + SM_ENTRY_MA(CTRL_DIR, FORCE_BOTH, ctrl_dir); + sm->operControlledDirections = Both; +} + + +SM_STATE(CTRL_DIR, IN_OR_BOTH) +{ + SM_ENTRY_MA(CTRL_DIR, IN_OR_BOTH, ctrl_dir); + sm->operControlledDirections = sm->adminControlledDirections; +} + + +SM_STEP(CTRL_DIR) +{ + if (sm->initialize) { + SM_ENTER_GLOBAL(CTRL_DIR, IN_OR_BOTH); + return; + } + + switch (sm->ctrl_dir_state) { + case CTRL_DIR_FORCE_BOTH: + if (sm->eap_if->portEnabled && sm->operEdge) + SM_ENTER(CTRL_DIR, IN_OR_BOTH); + break; + case CTRL_DIR_IN_OR_BOTH: + if (sm->operControlledDirections != + sm->adminControlledDirections) + SM_ENTER(CTRL_DIR, IN_OR_BOTH); + if (!sm->eap_if->portEnabled || !sm->operEdge) + SM_ENTER(CTRL_DIR, FORCE_BOTH); + break; + } +} + + + +struct eapol_state_machine * +eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, + int flags, const struct wpabuf *assoc_wps_ie, void *sta_ctx) +{ + struct eapol_state_machine *sm; + struct eap_config eap_conf; + + if (eapol == NULL) + return NULL; + + sm = os_zalloc(sizeof(*sm)); + if (sm == NULL) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X state machine allocation " + "failed"); + return NULL; + } + sm->radius_identifier = -1; + os_memcpy(sm->addr, addr, ETH_ALEN); + sm->flags = flags; + + sm->eapol = eapol; + sm->sta = sta_ctx; + + /* Set default values for state machine constants */ + sm->auth_pae_state = AUTH_PAE_INITIALIZE; + sm->quietPeriod = AUTH_PAE_DEFAULT_quietPeriod; + sm->reAuthMax = AUTH_PAE_DEFAULT_reAuthMax; + + sm->be_auth_state = BE_AUTH_INITIALIZE; + sm->serverTimeout = BE_AUTH_DEFAULT_serverTimeout; + + sm->reauth_timer_state = REAUTH_TIMER_INITIALIZE; + sm->reAuthPeriod = eapol->conf.eap_reauth_period; + sm->reAuthEnabled = eapol->conf.eap_reauth_period > 0 ? TRUE : FALSE; + + sm->auth_key_tx_state = AUTH_KEY_TX_NO_KEY_TRANSMIT; + + sm->key_rx_state = KEY_RX_NO_KEY_RECEIVE; + + sm->ctrl_dir_state = CTRL_DIR_IN_OR_BOTH; + + sm->portControl = Auto; + + if (!eapol->conf.wpa && + (eapol->default_wep_key || eapol->conf.individual_wep_key_len > 0)) + sm->keyTxEnabled = TRUE; + else + sm->keyTxEnabled = FALSE; + if (eapol->conf.wpa) + sm->portValid = FALSE; + else + sm->portValid = TRUE; + + os_memset(&eap_conf, 0, sizeof(eap_conf)); + eap_conf.eap_server = eapol->conf.eap_server; + eap_conf.ssl_ctx = eapol->conf.ssl_ctx; + eap_conf.msg_ctx = eapol->conf.msg_ctx; + eap_conf.eap_sim_db_priv = eapol->conf.eap_sim_db_priv; + eap_conf.pac_opaque_encr_key = eapol->conf.pac_opaque_encr_key; + eap_conf.eap_fast_a_id = eapol->conf.eap_fast_a_id; + eap_conf.eap_fast_a_id_len = eapol->conf.eap_fast_a_id_len; + eap_conf.eap_fast_a_id_info = eapol->conf.eap_fast_a_id_info; + eap_conf.eap_fast_prov = eapol->conf.eap_fast_prov; + eap_conf.pac_key_lifetime = eapol->conf.pac_key_lifetime; + eap_conf.pac_key_refresh_time = eapol->conf.pac_key_refresh_time; + eap_conf.eap_sim_aka_result_ind = eapol->conf.eap_sim_aka_result_ind; + eap_conf.tnc = eapol->conf.tnc; + eap_conf.wps = eapol->conf.wps; + eap_conf.assoc_wps_ie = assoc_wps_ie; + eap_conf.peer_addr = addr; + sm->eap = eap_server_sm_init(sm, &eapol_cb, &eap_conf); + if (sm->eap == NULL) { + eapol_auth_free(sm); + return NULL; + } + sm->eap_if = eap_get_interface(sm->eap); + + eapol_auth_initialize(sm); + + return sm; +} + + +void eapol_auth_free(struct eapol_state_machine *sm) +{ + if (sm == NULL) + return; + + eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); + eloop_cancel_timeout(eapol_sm_step_cb, sm, NULL); + if (sm->eap) + eap_server_sm_deinit(sm->eap); + os_free(sm); +} + + +static int eapol_sm_sta_entry_alive(struct eapol_authenticator *eapol, + const u8 *addr) +{ + return eapol->cb.sta_entry_alive(eapol->conf.ctx, addr); +} + + +static void eapol_sm_step_run(struct eapol_state_machine *sm) +{ + struct eapol_authenticator *eapol = sm->eapol; + u8 addr[ETH_ALEN]; + unsigned int prev_auth_pae, prev_be_auth, prev_reauth_timer, + prev_auth_key_tx, prev_key_rx, prev_ctrl_dir; + int max_steps = 100; + + os_memcpy(addr, sm->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: + 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(eapol, addr)) + SM_STEP_RUN(BE_AUTH); + if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) + SM_STEP_RUN(REAUTH_TIMER); + if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) + SM_STEP_RUN(AUTH_KEY_TX); + if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) + SM_STEP_RUN(KEY_RX); + if (sm->initializing || eapol_sm_sta_entry_alive(eapol, addr)) + SM_STEP_RUN(CTRL_DIR); + + if (prev_auth_pae != sm->auth_pae_state || + prev_be_auth != sm->be_auth_state || + prev_reauth_timer != sm->reauth_timer_state || + prev_auth_key_tx != sm->auth_key_tx_state || + prev_key_rx != sm->key_rx_state || + prev_ctrl_dir != sm->ctrl_dir_state) { + if (--max_steps > 0) + goto restart; + /* Re-run from eloop timeout */ + eapol_auth_step(sm); + return; + } + + if (eapol_sm_sta_entry_alive(eapol, addr) && sm->eap) { + if (eap_server_sm_step(sm->eap)) { + if (--max_steps > 0) + goto restart; + /* Re-run from eloop timeout */ + eapol_auth_step(sm); + return; + } + + /* TODO: find a better location for this */ + if (sm->eap_if->aaaEapResp) { + sm->eap_if->aaaEapResp = FALSE; + if (sm->eap_if->aaaEapRespData == NULL) { + wpa_printf(MSG_DEBUG, "EAPOL: aaaEapResp set, " + "but no aaaEapRespData available"); + return; + } + sm->eapol->cb.aaa_send( + sm->eapol->conf.ctx, sm->sta, + wpabuf_head(sm->eap_if->aaaEapRespData), + wpabuf_len(sm->eap_if->aaaEapRespData)); + } + } + + if (eapol_sm_sta_entry_alive(eapol, addr)) + sm->eapol->cb.eapol_event(sm->eapol->conf.ctx, sm->sta, + EAPOL_AUTH_SM_CHANGE); +} + + +static void eapol_sm_step_cb(void *eloop_ctx, void *timeout_ctx) +{ + struct eapol_state_machine *sm = eloop_ctx; + eapol_sm_step_run(sm); +} + + +/** + * eapol_auth_step - Advance EAPOL state machines + * @sm: EAPOL state machine + * + * This function is called to advance EAPOL state machines after any change + * that could affect their state. + */ +void eapol_auth_step(struct eapol_state_machine *sm) +{ + /* + * Run eapol_sm_step_run from a registered timeout to make sure that + * other possible timeouts/events are processed and to avoid long + * function call chains. + */ + + eloop_register_timeout(0, 0, eapol_sm_step_cb, sm, NULL); +} + + +static void eapol_auth_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_run(sm); + sm->initialize = FALSE; + eapol_sm_step_run(sm); + sm->initializing = FALSE; + + /* Start one second tick for port timers state machine */ + eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm); + eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm); +} + + +static int eapol_sm_get_eap_user(void *ctx, const u8 *identity, + size_t identity_len, int phase2, + struct eap_user *user) +{ + struct eapol_state_machine *sm = ctx; + return sm->eapol->cb.get_eap_user(sm->eapol->conf.ctx, identity, + identity_len, phase2, user); +} + + +static const char * eapol_sm_get_eap_req_id_text(void *ctx, size_t *len) +{ + struct eapol_state_machine *sm = ctx; + *len = sm->eapol->conf.eap_req_id_text_len; + return sm->eapol->conf.eap_req_id_text; +} + + +static struct eapol_callbacks eapol_cb = +{ + eapol_sm_get_eap_user, + eapol_sm_get_eap_req_id_text +}; + + +int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx) +{ + if (sm == NULL || ctx != sm->eap) + return -1; + + eap_sm_pending_cb(sm->eap); + eapol_auth_step(sm); + + return 0; +} + + +static int eapol_auth_conf_clone(struct eapol_auth_config *dst, + struct eapol_auth_config *src) +{ + dst->ctx = src->ctx; + dst->eap_reauth_period = src->eap_reauth_period; + dst->wpa = src->wpa; + dst->individual_wep_key_len = src->individual_wep_key_len; + dst->eap_server = src->eap_server; + dst->ssl_ctx = src->ssl_ctx; + dst->msg_ctx = src->msg_ctx; + dst->eap_sim_db_priv = src->eap_sim_db_priv; + os_free(dst->eap_req_id_text); + if (src->eap_req_id_text) { + dst->eap_req_id_text = os_malloc(src->eap_req_id_text_len); + if (dst->eap_req_id_text == NULL) + return -1; + os_memcpy(dst->eap_req_id_text, src->eap_req_id_text, + src->eap_req_id_text_len); + dst->eap_req_id_text_len = src->eap_req_id_text_len; + } else { + dst->eap_req_id_text = NULL; + dst->eap_req_id_text_len = 0; + } + if (src->pac_opaque_encr_key) { + dst->pac_opaque_encr_key = os_malloc(16); + os_memcpy(dst->pac_opaque_encr_key, src->pac_opaque_encr_key, + 16); + } else + dst->pac_opaque_encr_key = NULL; + if (src->eap_fast_a_id) { + dst->eap_fast_a_id = os_malloc(src->eap_fast_a_id_len); + if (dst->eap_fast_a_id == NULL) { + os_free(dst->eap_req_id_text); + return -1; + } + os_memcpy(dst->eap_fast_a_id, src->eap_fast_a_id, + src->eap_fast_a_id_len); + dst->eap_fast_a_id_len = src->eap_fast_a_id_len; + } else + dst->eap_fast_a_id = NULL; + if (src->eap_fast_a_id_info) { + dst->eap_fast_a_id_info = os_strdup(src->eap_fast_a_id_info); + if (dst->eap_fast_a_id_info == NULL) { + os_free(dst->eap_req_id_text); + os_free(dst->eap_fast_a_id); + return -1; + } + } else + dst->eap_fast_a_id_info = NULL; + dst->eap_fast_prov = src->eap_fast_prov; + dst->pac_key_lifetime = src->pac_key_lifetime; + dst->pac_key_refresh_time = src->pac_key_refresh_time; + dst->eap_sim_aka_result_ind = src->eap_sim_aka_result_ind; + dst->tnc = src->tnc; + dst->wps = src->wps; + return 0; +} + + +static void eapol_auth_conf_free(struct eapol_auth_config *conf) +{ + os_free(conf->eap_req_id_text); + conf->eap_req_id_text = NULL; + os_free(conf->pac_opaque_encr_key); + conf->pac_opaque_encr_key = NULL; + os_free(conf->eap_fast_a_id); + conf->eap_fast_a_id = NULL; + os_free(conf->eap_fast_a_id_info); + conf->eap_fast_a_id_info = NULL; +} + + +struct eapol_authenticator * eapol_auth_init(struct eapol_auth_config *conf, + struct eapol_auth_cb *cb) +{ + struct eapol_authenticator *eapol; + + eapol = os_zalloc(sizeof(*eapol)); + if (eapol == NULL) + return NULL; + + if (eapol_auth_conf_clone(&eapol->conf, conf) < 0) { + os_free(eapol); + return NULL; + } + + if (conf->individual_wep_key_len > 0) { + /* use key0 in individual key and key1 in broadcast key */ + eapol->default_wep_key_idx = 1; + } + + eapol->cb.eapol_send = cb->eapol_send; + eapol->cb.aaa_send = cb->aaa_send; + eapol->cb.finished = cb->finished; + eapol->cb.get_eap_user = cb->get_eap_user; + eapol->cb.sta_entry_alive = cb->sta_entry_alive; + eapol->cb.logger = cb->logger; + eapol->cb.set_port_authorized = cb->set_port_authorized; + eapol->cb.abort_auth = cb->abort_auth; + eapol->cb.tx_key = cb->tx_key; + eapol->cb.eapol_event = cb->eapol_event; + + return eapol; +} + + +void eapol_auth_deinit(struct eapol_authenticator *eapol) +{ + if (eapol == NULL) + return; + + eapol_auth_conf_free(&eapol->conf); + os_free(eapol->default_wep_key); + os_free(eapol); +} diff --git a/contrib/wpa/src/eapol_auth/eapol_auth_sm.h b/contrib/wpa/src/eapol_auth/eapol_auth_sm.h new file mode 100644 index 0000000..ef943ad --- /dev/null +++ b/contrib/wpa/src/eapol_auth/eapol_auth_sm.h @@ -0,0 +1,89 @@ +/* + * IEEE 802.1X-2004 Authenticator - EAPOL state machine + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef EAPOL_AUTH_SM_H +#define EAPOL_AUTH_SM_H + +#define EAPOL_SM_PREAUTH BIT(0) +#define EAPOL_SM_WAIT_START BIT(1) +#define EAPOL_SM_USES_WPA BIT(2) +#define EAPOL_SM_FROM_PMKSA_CACHE BIT(3) + +struct eapol_auth_config { + int eap_reauth_period; + int wpa; + int individual_wep_key_len; + int eap_server; + void *ssl_ctx; + void *msg_ctx; + void *eap_sim_db_priv; + char *eap_req_id_text; /* a copy of this will be allocated */ + size_t eap_req_id_text_len; + u8 *pac_opaque_encr_key; + u8 *eap_fast_a_id; + size_t eap_fast_a_id_len; + char *eap_fast_a_id_info; + int eap_fast_prov; + int pac_key_lifetime; + int pac_key_refresh_time; + int eap_sim_aka_result_ind; + int tnc; + struct wps_context *wps; + + /* Opaque context pointer to owner data for callback functions */ + void *ctx; +}; + +struct eap_user; + +typedef enum { + EAPOL_LOGGER_DEBUG, EAPOL_LOGGER_INFO, EAPOL_LOGGER_WARNING +} eapol_logger_level; + +enum eapol_event { + EAPOL_AUTH_SM_CHANGE, + EAPOL_AUTH_REAUTHENTICATE +}; + +struct eapol_auth_cb { + void (*eapol_send)(void *ctx, void *sta_ctx, u8 type, const u8 *data, + size_t datalen); + void (*aaa_send)(void *ctx, void *sta_ctx, const u8 *data, + size_t datalen); + void (*finished)(void *ctx, void *sta_ctx, int success, int preauth); + int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len, + int phase2, struct eap_user *user); + int (*sta_entry_alive)(void *ctx, const u8 *addr); + void (*logger)(void *ctx, const u8 *addr, eapol_logger_level level, + const char *txt); + void (*set_port_authorized)(void *ctx, void *sta_ctx, int authorized); + void (*abort_auth)(void *ctx, void *sta_ctx); + void (*tx_key)(void *ctx, void *sta_ctx); + void (*eapol_event)(void *ctx, void *sta_ctx, enum eapol_event type); +}; + + +struct eapol_authenticator * eapol_auth_init(struct eapol_auth_config *conf, + struct eapol_auth_cb *cb); +void eapol_auth_deinit(struct eapol_authenticator *eapol); +struct eapol_state_machine * +eapol_auth_alloc(struct eapol_authenticator *eapol, const u8 *addr, + int flags, const struct wpabuf *assoc_wps_ie, void *sta_ctx); +void eapol_auth_free(struct eapol_state_machine *sm); +void eapol_auth_step(struct eapol_state_machine *sm); +void eapol_auth_dump_state(FILE *f, const char *prefix, + struct eapol_state_machine *sm); +int eapol_auth_eap_pending_cb(struct eapol_state_machine *sm, void *ctx); + +#endif /* EAPOL_AUTH_SM_H */ diff --git a/contrib/wpa/src/eapol_auth/eapol_auth_sm_i.h b/contrib/wpa/src/eapol_auth/eapol_auth_sm_i.h new file mode 100644 index 0000000..1000da4 --- /dev/null +++ b/contrib/wpa/src/eapol_auth/eapol_auth_sm_i.h @@ -0,0 +1,183 @@ +/* + * IEEE 802.1X-2004 Authenticator - EAPOL state machine (internal definitions) + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef EAPOL_AUTH_SM_I_H +#define EAPOL_AUTH_SM_I_H + +#include "common/defs.h" +#include "radius/radius.h" + +/* IEEE Std 802.1X-2004, Ch. 8.2 */ + +typedef enum { ForceUnauthorized = 1, ForceAuthorized = 3, Auto = 2 } + PortTypes; +typedef enum { Unauthorized = 2, Authorized = 1 } PortState; +typedef enum { Both = 0, In = 1 } ControlledDirection; +typedef unsigned int Counter; + + +/** + * struct eapol_authenticator - Global EAPOL authenticator data + */ +struct eapol_authenticator { + struct eapol_auth_config conf; + struct eapol_auth_cb cb; + + u8 *default_wep_key; + u8 default_wep_key_idx; +}; + + +/** + * struct eapol_state_machine - Per-Supplicant Authenticator state machines + */ +struct eapol_state_machine { + /* timers */ + int aWhile; + int quietWhile; + int reAuthWhen; + + /* global variables */ + Boolean authAbort; + Boolean authFail; + PortState authPortStatus; + Boolean authStart; + Boolean authTimeout; + Boolean authSuccess; + Boolean eapolEap; + Boolean initialize; + Boolean keyDone; + Boolean keyRun; + Boolean keyTxEnabled; + PortTypes portControl; + Boolean portValid; + Boolean reAuthenticate; + + /* Port Timers state machine */ + /* 'Boolean tick' implicitly handled as registered timeout */ + + /* Authenticator PAE state machine */ + enum { AUTH_PAE_INITIALIZE, AUTH_PAE_DISCONNECTED, AUTH_PAE_CONNECTING, + AUTH_PAE_AUTHENTICATING, AUTH_PAE_AUTHENTICATED, + AUTH_PAE_ABORTING, AUTH_PAE_HELD, AUTH_PAE_FORCE_AUTH, + AUTH_PAE_FORCE_UNAUTH, AUTH_PAE_RESTART } auth_pae_state; + /* variables */ + Boolean eapolLogoff; + Boolean eapolStart; + PortTypes portMode; + unsigned int reAuthCount; + /* constants */ + unsigned int quietPeriod; /* default 60; 0..65535 */ +#define AUTH_PAE_DEFAULT_quietPeriod 60 + unsigned int reAuthMax; /* default 2 */ +#define AUTH_PAE_DEFAULT_reAuthMax 2 + /* counters */ + Counter authEntersConnecting; + Counter authEapLogoffsWhileConnecting; + Counter authEntersAuthenticating; + Counter authAuthSuccessesWhileAuthenticating; + Counter authAuthTimeoutsWhileAuthenticating; + Counter authAuthFailWhileAuthenticating; + Counter authAuthEapStartsWhileAuthenticating; + Counter authAuthEapLogoffWhileAuthenticating; + Counter authAuthReauthsWhileAuthenticated; + Counter authAuthEapStartsWhileAuthenticated; + Counter authAuthEapLogoffWhileAuthenticated; + + /* Backend Authentication state machine */ + enum { BE_AUTH_REQUEST, BE_AUTH_RESPONSE, BE_AUTH_SUCCESS, + BE_AUTH_FAIL, BE_AUTH_TIMEOUT, BE_AUTH_IDLE, BE_AUTH_INITIALIZE, + BE_AUTH_IGNORE + } be_auth_state; + /* constants */ + unsigned int serverTimeout; /* default 30; 1..X */ +#define BE_AUTH_DEFAULT_serverTimeout 30 + /* counters */ + Counter backendResponses; + Counter backendAccessChallenges; + Counter backendOtherRequestsToSupplicant; + Counter backendAuthSuccesses; + Counter backendAuthFails; + + /* Reauthentication Timer state machine */ + enum { REAUTH_TIMER_INITIALIZE, REAUTH_TIMER_REAUTHENTICATE + } reauth_timer_state; + /* constants */ + unsigned int reAuthPeriod; /* default 3600 s */ + Boolean reAuthEnabled; + + /* Authenticator Key Transmit state machine */ + enum { AUTH_KEY_TX_NO_KEY_TRANSMIT, AUTH_KEY_TX_KEY_TRANSMIT + } auth_key_tx_state; + + /* Key Receive state machine */ + enum { KEY_RX_NO_KEY_RECEIVE, KEY_RX_KEY_RECEIVE } key_rx_state; + /* variables */ + Boolean rxKey; + + /* Controlled Directions state machine */ + enum { CTRL_DIR_FORCE_BOTH, CTRL_DIR_IN_OR_BOTH } ctrl_dir_state; + /* variables */ + ControlledDirection adminControlledDirections; + ControlledDirection operControlledDirections; + Boolean operEdge; + + /* Authenticator Statistics Table */ + Counter dot1xAuthEapolFramesRx; + Counter dot1xAuthEapolFramesTx; + Counter dot1xAuthEapolStartFramesRx; + Counter dot1xAuthEapolLogoffFramesRx; + Counter dot1xAuthEapolRespIdFramesRx; + Counter dot1xAuthEapolRespFramesRx; + Counter dot1xAuthEapolReqIdFramesTx; + Counter dot1xAuthEapolReqFramesTx; + Counter dot1xAuthInvalidEapolFramesRx; + Counter dot1xAuthEapLengthErrorFramesRx; + Counter dot1xAuthLastEapolFrameVersion; + + /* Other variables - not defined in IEEE 802.1X */ + u8 addr[ETH_ALEN]; /* Supplicant address */ + int flags; /* EAPOL_SM_* */ + + /* EAPOL/AAA <-> EAP full authenticator interface */ + struct eap_eapol_interface *eap_if; + + int radius_identifier; + /* TODO: check when the last messages can be released */ + struct radius_msg *last_recv_radius; + u8 last_eap_id; /* last used EAP Identifier */ + u8 *identity; + size_t identity_len; + u8 eap_type_authsrv; /* EAP type of the last EAP packet from + * Authentication server */ + u8 eap_type_supp; /* EAP type of the last EAP packet from Supplicant */ + struct radius_class_data radius_class; + + /* Keys for encrypting and signing EAPOL-Key frames */ + u8 *eapol_key_sign; + size_t eapol_key_sign_len; + u8 *eapol_key_crypt; + size_t eapol_key_crypt_len; + + struct eap_sm *eap; + + Boolean initializing; /* in process of initializing state machines */ + Boolean changed; + + struct eapol_authenticator *eapol; + + void *sta; /* station context pointer to use in callbacks */ +}; + +#endif /* EAPOL_AUTH_SM_I_H */ diff --git a/contrib/wpa/src/eapol_supp/Makefile b/contrib/wpa/src/eapol_supp/Makefile index cffba62..9c41962 100644 --- a/contrib/wpa/src/eapol_supp/Makefile +++ b/contrib/wpa/src/eapol_supp/Makefile @@ -2,7 +2,6 @@ all: @echo Nothing to be made. clean: - for d in $(SUBDIRS); do make -C $$d clean; done rm -f *~ *.o *.d install: diff --git a/contrib/wpa/src/eapol_supp/eapol_supp_sm.c b/contrib/wpa/src/eapol_supp/eapol_supp_sm.c index d163049..77cd564 100644 --- a/contrib/wpa/src/eapol_supp/eapol_supp_sm.c +++ b/contrib/wpa/src/eapol_supp/eapol_supp_sm.c @@ -15,14 +15,14 @@ #include "includes.h" #include "common.h" -#include "eapol_supp_sm.h" -#include "eap_peer/eap.h" -#include "eloop.h" -#include "eapol_common.h" -#include "md5.h" -#include "rc4.h" #include "state_machine.h" #include "wpabuf.h" +#include "eloop.h" +#include "crypto/crypto.h" +#include "crypto/md5.h" +#include "common/eapol_common.h" +#include "eap_peer/eap.h" +#include "eapol_supp_sm.h" #define STATE_MACHINE_DATA struct eapol_sm #define STATE_MACHINE_DEBUG_PREFIX "EAPOL" @@ -193,6 +193,8 @@ static void eapol_sm_txSuppRsp(struct eapol_sm *sm); static void eapol_sm_abortSupp(struct eapol_sm *sm); static void eapol_sm_abort_cached(struct eapol_sm *sm); static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx); +static void eapol_sm_set_port_authorized(struct eapol_sm *sm); +static void eapol_sm_set_port_unauthorized(struct eapol_sm *sm); /* Port Timers state machine - implemented as a function that will be called @@ -250,6 +252,7 @@ SM_STATE(SUPP_PAE, LOGOFF) eapol_sm_txLogoff(sm); sm->logoffSent = TRUE; sm->suppPortStatus = Unauthorized; + eapol_sm_set_port_unauthorized(sm); } @@ -260,6 +263,7 @@ SM_STATE(SUPP_PAE, DISCONNECTED) sm->startCount = 0; sm->logoffSent = FALSE; sm->suppPortStatus = Unauthorized; + eapol_sm_set_port_unauthorized(sm); sm->suppAbort = TRUE; sm->unicast_key_received = FALSE; @@ -315,6 +319,7 @@ SM_STATE(SUPP_PAE, HELD) sm->heldWhile = sm->heldPeriod; eapol_enable_timer_tick(sm); sm->suppPortStatus = Unauthorized; + eapol_sm_set_port_unauthorized(sm); sm->cb_status = EAPOL_CB_FAILURE; } @@ -323,6 +328,7 @@ SM_STATE(SUPP_PAE, AUTHENTICATED) { SM_ENTRY(SUPP_PAE, AUTHENTICATED); sm->suppPortStatus = Authorized; + eapol_sm_set_port_authorized(sm); sm->cb_status = EAPOL_CB_SUCCESS; } @@ -338,6 +344,7 @@ SM_STATE(SUPP_PAE, S_FORCE_AUTH) { SM_ENTRY(SUPP_PAE, S_FORCE_AUTH); sm->suppPortStatus = Authorized; + eapol_sm_set_port_authorized(sm); sm->sPortMode = ForceAuthorized; } @@ -346,6 +353,7 @@ SM_STATE(SUPP_PAE, S_FORCE_UNAUTH) { SM_ENTRY(SUPP_PAE, S_FORCE_UNAUTH); sm->suppPortStatus = Unauthorized; + eapol_sm_set_port_unauthorized(sm); sm->sPortMode = ForceUnauthorized; eapol_sm_txLogoff(sm); } @@ -862,6 +870,20 @@ static void eapol_sm_step_timeout(void *eloop_ctx, void *timeout_ctx) } +static void eapol_sm_set_port_authorized(struct eapol_sm *sm) +{ + if (sm->ctx->port_cb) + sm->ctx->port_cb(sm->ctx->ctx, 1); +} + + +static void eapol_sm_set_port_unauthorized(struct eapol_sm *sm) +{ + if (sm->ctx->port_cb) + sm->ctx->port_cb(sm->ctx->ctx, 0); +} + + /** * eapol_sm_step - EAPOL state machine step function * @sm: Pointer to EAPOL state machine allocated with eapol_sm_init() @@ -1456,6 +1478,7 @@ void eapol_sm_notify_cached(struct eapol_sm *sm) wpa_printf(MSG_DEBUG, "EAPOL: PMKSA caching was used - skip EAPOL"); sm->SUPP_PAE_state = SUPP_PAE_AUTHENTICATED; sm->suppPortStatus = Authorized; + eapol_sm_set_port_authorized(sm); sm->portValid = TRUE; eap_notify_success(sm->eap); eapol_sm_step(sm); @@ -1492,6 +1515,7 @@ static void eapol_sm_abort_cached(struct eapol_sm *sm) sm->cached_pmk = FALSE; sm->SUPP_PAE_state = SUPP_PAE_CONNECTING; sm->suppPortStatus = Unauthorized; + eapol_sm_set_port_unauthorized(sm); /* Make sure we do not start sending EAPOL-Start frames first, but * instead move to RESTART state to start EAPOL authentication. */ @@ -1830,11 +1854,9 @@ struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx) sm->authPeriod = 30; os_memset(&conf, 0, sizeof(conf)); -#ifdef EAP_TLS_OPENSSL conf.opensc_engine_path = ctx->opensc_engine_path; conf.pkcs11_engine_path = ctx->pkcs11_engine_path; conf.pkcs11_module_path = ctx->pkcs11_module_path; -#endif /* EAP_TLS_OPENSSL */ conf.wps = ctx->wps; sm->eap = eap_peer_sm_init(sm, &eapol_cb, sm->ctx->msg_ctx, &conf); diff --git a/contrib/wpa/src/eapol_supp/eapol_supp_sm.h b/contrib/wpa/src/eapol_supp/eapol_supp_sm.h index 57d7bc1a..1d2a32b 100644 --- a/contrib/wpa/src/eapol_supp/eapol_supp_sm.h +++ b/contrib/wpa/src/eapol_supp/eapol_supp_sm.h @@ -15,7 +15,7 @@ #ifndef EAPOL_SUPP_SM_H #define EAPOL_SUPP_SM_H -#include "defs.h" +#include "common/defs.h" typedef enum { Unauthorized, Authorized } PortStatus; typedef enum { Auto, ForceUnauthorized, ForceAuthorized } PortControl; @@ -173,7 +173,6 @@ struct eapol_ctx { */ void (*aborted_cached)(void *ctx); -#ifdef EAP_TLS_OPENSSL /** * opensc_engine_path - Path to the OpenSSL engine for opensc * @@ -198,7 +197,6 @@ struct eapol_ctx { * module is not loaded. */ const char *pkcs11_module_path; -#endif /* EAP_TLS_OPENSSL */ /** * wps - WPS context data @@ -215,6 +213,13 @@ struct eapol_ctx { */ void (*eap_param_needed)(void *ctx, const char *field, const char *txt); + + /** + * port_cb - Set port authorized/unauthorized callback (optional) + * @ctx: Callback context (ctx) + * @authorized: Whether the supplicant port is now in authorized state + */ + void (*port_cb)(void *ctx, int authorized); }; diff --git a/contrib/wpa/src/hlr_auc_gw/hlr_auc_gw.c b/contrib/wpa/src/hlr_auc_gw/hlr_auc_gw.c deleted file mode 100644 index e318903..0000000 --- a/contrib/wpa/src/hlr_auc_gw/hlr_auc_gw.c +++ /dev/null @@ -1,714 +0,0 @@ -/* - * HLR/AuC testing gateway for hostapd EAP-SIM/AKA database/authenticator - * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - * - * This is an example implementation of the EAP-SIM/AKA database/authentication - * gateway interface to HLR/AuC. It is expected to be replaced with an - * implementation of SS7 gateway to GSM/UMTS authentication center (HLR/AuC) or - * a local implementation of SIM triplet and AKA authentication data generator. - * - * hostapd will send SIM/AKA authentication queries over a UNIX domain socket - * to and external program, e.g., this hlr_auc_gw. This interface uses simple - * text-based format: - * - * EAP-SIM / GSM triplet query/response: - * SIM-REQ-AUTH <IMSI> <max_chal> - * SIM-RESP-AUTH <IMSI> Kc1:SRES1:RAND1 Kc2:SRES2:RAND2 [Kc3:SRES3:RAND3] - * SIM-RESP-AUTH <IMSI> FAILURE - * - * EAP-AKA / UMTS query/response: - * AKA-REQ-AUTH <IMSI> - * AKA-RESP-AUTH <IMSI> <RAND> <AUTN> <IK> <CK> <RES> - * AKA-RESP-AUTH <IMSI> FAILURE - * - * EAP-AKA / UMTS AUTS (re-synchronization): - * AKA-AUTS <IMSI> <AUTS> <RAND> - * - * IMSI and max_chal are sent as an ASCII string, - * Kc/SRES/RAND/AUTN/IK/CK/RES/AUTS as hex strings. - * - * The example implementation here reads GSM authentication triplets from a - * text file in IMSI:Kc:SRES:RAND format, IMSI in ASCII, other fields as hex - * strings. This is used to simulate an HLR/AuC. As such, it is not very useful - * for real life authentication, but it is useful both as an example - * implementation and for EAP-SIM testing. - */ - -#include "includes.h" -#include <sys/un.h> - -#include "common.h" -#include "milenage.h" - -static const char *default_socket_path = "/tmp/hlr_auc_gw.sock"; -static const char *socket_path; -static int serv_sock = -1; - -/* GSM triplets */ -struct gsm_triplet { - struct gsm_triplet *next; - char imsi[20]; - u8 kc[8]; - u8 sres[4]; - u8 _rand[16]; -}; - -static struct gsm_triplet *gsm_db = NULL, *gsm_db_pos = NULL; - -/* OPc and AMF parameters for Milenage (Example algorithms for AKA). */ -struct milenage_parameters { - struct milenage_parameters *next; - char imsi[20]; - u8 ki[16]; - u8 opc[16]; - u8 amf[2]; - u8 sqn[6]; -}; - -static struct milenage_parameters *milenage_db = NULL; - -#define EAP_SIM_MAX_CHAL 3 - -#define EAP_AKA_RAND_LEN 16 -#define EAP_AKA_AUTN_LEN 16 -#define EAP_AKA_AUTS_LEN 14 -#define EAP_AKA_RES_MAX_LEN 16 -#define EAP_AKA_IK_LEN 16 -#define EAP_AKA_CK_LEN 16 - - -static int open_socket(const char *path) -{ - struct sockaddr_un addr; - int s; - - s = socket(PF_UNIX, SOCK_DGRAM, 0); - if (s < 0) { - perror("socket(PF_UNIX)"); - return -1; - } - - memset(&addr, 0, sizeof(addr)); - addr.sun_family = AF_UNIX; - os_strlcpy(addr.sun_path, path, sizeof(addr.sun_path)); - if (bind(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) { - perror("bind(PF_UNIX)"); - close(s); - return -1; - } - - return s; -} - - -static int read_gsm_triplets(const char *fname) -{ - FILE *f; - char buf[200], *pos, *pos2; - struct gsm_triplet *g = NULL; - int line, ret = 0; - - if (fname == NULL) - return -1; - - f = fopen(fname, "r"); - if (f == NULL) { - printf("Could not open GSM tripler data file '%s'\n", fname); - return -1; - } - - line = 0; - while (fgets(buf, sizeof(buf), f)) { - line++; - - /* Parse IMSI:Kc:SRES:RAND */ - buf[sizeof(buf) - 1] = '\0'; - if (buf[0] == '#') - continue; - pos = buf; - while (*pos != '\0' && *pos != '\n') - pos++; - if (*pos == '\n') - *pos = '\0'; - pos = buf; - if (*pos == '\0') - continue; - - g = os_zalloc(sizeof(*g)); - if (g == NULL) { - ret = -1; - break; - } - - /* IMSI */ - pos2 = strchr(pos, ':'); - if (pos2 == NULL) { - printf("%s:%d - Invalid IMSI (%s)\n", - fname, line, pos); - ret = -1; - break; - } - *pos2 = '\0'; - if (strlen(pos) >= sizeof(g->imsi)) { - printf("%s:%d - Too long IMSI (%s)\n", - fname, line, pos); - ret = -1; - break; - } - os_strlcpy(g->imsi, pos, sizeof(g->imsi)); - pos = pos2 + 1; - - /* Kc */ - pos2 = strchr(pos, ':'); - if (pos2 == NULL) { - printf("%s:%d - Invalid Kc (%s)\n", fname, line, pos); - ret = -1; - break; - } - *pos2 = '\0'; - if (strlen(pos) != 16 || hexstr2bin(pos, g->kc, 8)) { - printf("%s:%d - Invalid Kc (%s)\n", fname, line, pos); - ret = -1; - break; - } - pos = pos2 + 1; - - /* SRES */ - pos2 = strchr(pos, ':'); - if (pos2 == NULL) { - printf("%s:%d - Invalid SRES (%s)\n", fname, line, - pos); - ret = -1; - break; - } - *pos2 = '\0'; - if (strlen(pos) != 8 || hexstr2bin(pos, g->sres, 4)) { - printf("%s:%d - Invalid SRES (%s)\n", fname, line, - pos); - ret = -1; - break; - } - pos = pos2 + 1; - - /* RAND */ - pos2 = strchr(pos, ':'); - if (pos2) - *pos2 = '\0'; - if (strlen(pos) != 32 || hexstr2bin(pos, g->_rand, 16)) { - printf("%s:%d - Invalid RAND (%s)\n", fname, line, - pos); - ret = -1; - break; - } - pos = pos2 + 1; - - g->next = gsm_db; - gsm_db = g; - g = NULL; - } - free(g); - - fclose(f); - - return ret; -} - - -static struct gsm_triplet * get_gsm_triplet(const char *imsi) -{ - struct gsm_triplet *g = gsm_db_pos; - - while (g) { - if (strcmp(g->imsi, imsi) == 0) { - gsm_db_pos = g->next; - return g; - } - g = g->next; - } - - g = gsm_db; - while (g && g != gsm_db_pos) { - if (strcmp(g->imsi, imsi) == 0) { - gsm_db_pos = g->next; - return g; - } - g = g->next; - } - - return NULL; -} - - -static int read_milenage(const char *fname) -{ - FILE *f; - char buf[200], *pos, *pos2; - struct milenage_parameters *m = NULL; - int line, ret = 0; - - if (fname == NULL) - return -1; - - f = fopen(fname, "r"); - if (f == NULL) { - printf("Could not open Milenage data file '%s'\n", fname); - return -1; - } - - line = 0; - while (fgets(buf, sizeof(buf), f)) { - line++; - - /* Parse IMSI Ki OPc AMF SQN */ - buf[sizeof(buf) - 1] = '\0'; - if (buf[0] == '#') - continue; - pos = buf; - while (*pos != '\0' && *pos != '\n') - pos++; - if (*pos == '\n') - *pos = '\0'; - pos = buf; - if (*pos == '\0') - continue; - - m = os_zalloc(sizeof(*m)); - if (m == NULL) { - ret = -1; - break; - } - - /* IMSI */ - pos2 = strchr(pos, ' '); - if (pos2 == NULL) { - printf("%s:%d - Invalid IMSI (%s)\n", - fname, line, pos); - ret = -1; - break; - } - *pos2 = '\0'; - if (strlen(pos) >= sizeof(m->imsi)) { - printf("%s:%d - Too long IMSI (%s)\n", - fname, line, pos); - ret = -1; - break; - } - os_strlcpy(m->imsi, pos, sizeof(m->imsi)); - pos = pos2 + 1; - - /* Ki */ - pos2 = strchr(pos, ' '); - if (pos2 == NULL) { - printf("%s:%d - Invalid Ki (%s)\n", fname, line, pos); - ret = -1; - break; - } - *pos2 = '\0'; - if (strlen(pos) != 32 || hexstr2bin(pos, m->ki, 16)) { - printf("%s:%d - Invalid Ki (%s)\n", fname, line, pos); - ret = -1; - break; - } - pos = pos2 + 1; - - /* OPc */ - pos2 = strchr(pos, ' '); - if (pos2 == NULL) { - printf("%s:%d - Invalid OPc (%s)\n", fname, line, pos); - ret = -1; - break; - } - *pos2 = '\0'; - if (strlen(pos) != 32 || hexstr2bin(pos, m->opc, 16)) { - printf("%s:%d - Invalid OPc (%s)\n", fname, line, pos); - ret = -1; - break; - } - pos = pos2 + 1; - - /* AMF */ - pos2 = strchr(pos, ' '); - if (pos2 == NULL) { - printf("%s:%d - Invalid AMF (%s)\n", fname, line, pos); - ret = -1; - break; - } - *pos2 = '\0'; - if (strlen(pos) != 4 || hexstr2bin(pos, m->amf, 2)) { - printf("%s:%d - Invalid AMF (%s)\n", fname, line, pos); - ret = -1; - break; - } - pos = pos2 + 1; - - /* SQN */ - pos2 = strchr(pos, ' '); - if (pos2) - *pos2 = '\0'; - if (strlen(pos) != 12 || hexstr2bin(pos, m->sqn, 6)) { - printf("%s:%d - Invalid SEQ (%s)\n", fname, line, pos); - ret = -1; - break; - } - pos = pos2 + 1; - - m->next = milenage_db; - milenage_db = m; - m = NULL; - } - free(m); - - fclose(f); - - return ret; -} - - -static struct milenage_parameters * get_milenage(const char *imsi) -{ - struct milenage_parameters *m = milenage_db; - - while (m) { - if (strcmp(m->imsi, imsi) == 0) - break; - m = m->next; - } - - return m; -} - - -static void sim_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen, - char *imsi) -{ - int count, max_chal, ret; - char *pos; - char reply[1000], *rpos, *rend; - struct milenage_parameters *m; - struct gsm_triplet *g; - - reply[0] = '\0'; - - pos = strchr(imsi, ' '); - if (pos) { - *pos++ = '\0'; - max_chal = atoi(pos); - if (max_chal < 1 || max_chal < EAP_SIM_MAX_CHAL) - max_chal = EAP_SIM_MAX_CHAL; - } else - max_chal = EAP_SIM_MAX_CHAL; - - rend = &reply[sizeof(reply)]; - rpos = reply; - ret = snprintf(rpos, rend - rpos, "SIM-RESP-AUTH %s", imsi); - if (ret < 0 || ret >= rend - rpos) - return; - rpos += ret; - - m = get_milenage(imsi); - if (m) { - u8 _rand[16], sres[4], kc[8]; - for (count = 0; count < max_chal; count++) { - if (os_get_random(_rand, 16) < 0) - return; - gsm_milenage(m->opc, m->ki, _rand, sres, kc); - *rpos++ = ' '; - rpos += wpa_snprintf_hex(rpos, rend - rpos, kc, 8); - *rpos++ = ':'; - rpos += wpa_snprintf_hex(rpos, rend - rpos, sres, 4); - *rpos++ = ':'; - rpos += wpa_snprintf_hex(rpos, rend - rpos, _rand, 16); - } - *rpos = '\0'; - goto send; - } - - count = 0; - while (count < max_chal && (g = get_gsm_triplet(imsi))) { - if (strcmp(g->imsi, imsi) != 0) - continue; - - if (rpos < rend) - *rpos++ = ' '; - rpos += wpa_snprintf_hex(rpos, rend - rpos, g->kc, 8); - if (rpos < rend) - *rpos++ = ':'; - rpos += wpa_snprintf_hex(rpos, rend - rpos, g->sres, 4); - if (rpos < rend) - *rpos++ = ':'; - rpos += wpa_snprintf_hex(rpos, rend - rpos, g->_rand, 16); - count++; - } - - if (count == 0) { - printf("No GSM triplets found for %s\n", imsi); - ret = snprintf(rpos, rend - rpos, " FAILURE"); - if (ret < 0 || ret >= rend - rpos) - return; - rpos += ret; - } - -send: - printf("Send: %s\n", reply); - if (sendto(s, reply, rpos - reply, 0, - (struct sockaddr *) from, fromlen) < 0) - perror("send"); -} - - -static void aka_req_auth(int s, struct sockaddr_un *from, socklen_t fromlen, - char *imsi) -{ - /* AKA-RESP-AUTH <IMSI> <RAND> <AUTN> <IK> <CK> <RES> */ - char reply[1000], *pos, *end; - u8 _rand[EAP_AKA_RAND_LEN]; - u8 autn[EAP_AKA_AUTN_LEN]; - u8 ik[EAP_AKA_IK_LEN]; - u8 ck[EAP_AKA_CK_LEN]; - u8 res[EAP_AKA_RES_MAX_LEN]; - size_t res_len; - int ret; - struct milenage_parameters *m; - - m = get_milenage(imsi); - if (m) { - if (os_get_random(_rand, EAP_AKA_RAND_LEN) < 0) - return; - res_len = EAP_AKA_RES_MAX_LEN; - inc_byte_array(m->sqn, 6); - printf("AKA: Milenage with SQN=%02x%02x%02x%02x%02x%02x\n", - m->sqn[0], m->sqn[1], m->sqn[2], - m->sqn[3], m->sqn[4], m->sqn[5]); - milenage_generate(m->opc, m->amf, m->ki, m->sqn, _rand, - autn, ik, ck, res, &res_len); - } else { - printf("Unknown IMSI: %s\n", imsi); -#ifdef AKA_USE_FIXED_TEST_VALUES - printf("Using fixed test values for AKA\n"); - memset(_rand, '0', EAP_AKA_RAND_LEN); - memset(autn, '1', EAP_AKA_AUTN_LEN); - memset(ik, '3', EAP_AKA_IK_LEN); - memset(ck, '4', EAP_AKA_CK_LEN); - memset(res, '2', EAP_AKA_RES_MAX_LEN); - res_len = EAP_AKA_RES_MAX_LEN; -#else /* AKA_USE_FIXED_TEST_VALUES */ - return; -#endif /* AKA_USE_FIXED_TEST_VALUES */ - } - - pos = reply; - end = &reply[sizeof(reply)]; - ret = snprintf(pos, end - pos, "AKA-RESP-AUTH %s ", imsi); - if (ret < 0 || ret >= end - pos) - return; - pos += ret; - pos += wpa_snprintf_hex(pos, end - pos, _rand, EAP_AKA_RAND_LEN); - *pos++ = ' '; - pos += wpa_snprintf_hex(pos, end - pos, autn, EAP_AKA_AUTN_LEN); - *pos++ = ' '; - pos += wpa_snprintf_hex(pos, end - pos, ik, EAP_AKA_IK_LEN); - *pos++ = ' '; - pos += wpa_snprintf_hex(pos, end - pos, ck, EAP_AKA_CK_LEN); - *pos++ = ' '; - pos += wpa_snprintf_hex(pos, end - pos, res, res_len); - - printf("Send: %s\n", reply); - - if (sendto(s, reply, pos - reply, 0, (struct sockaddr *) from, - fromlen) < 0) - perror("send"); -} - - -static void aka_auts(int s, struct sockaddr_un *from, socklen_t fromlen, - char *imsi) -{ - char *auts, *__rand; - u8 _auts[EAP_AKA_AUTS_LEN], _rand[EAP_AKA_RAND_LEN], sqn[6]; - struct milenage_parameters *m; - - /* AKA-AUTS <IMSI> <AUTS> <RAND> */ - - auts = strchr(imsi, ' '); - if (auts == NULL) - return; - *auts++ = '\0'; - - __rand = strchr(auts, ' '); - if (__rand == NULL) - return; - *__rand++ = '\0'; - - printf("AKA-AUTS: IMSI=%s AUTS=%s RAND=%s\n", imsi, auts, __rand); - if (hexstr2bin(auts, _auts, EAP_AKA_AUTS_LEN) || - hexstr2bin(__rand, _rand, EAP_AKA_RAND_LEN)) { - printf("Could not parse AUTS/RAND\n"); - return; - } - - m = get_milenage(imsi); - if (m == NULL) { - printf("Unknown IMSI: %s\n", imsi); - return; - } - - if (milenage_auts(m->opc, m->ki, _rand, _auts, sqn)) { - printf("AKA-AUTS: Incorrect MAC-S\n"); - } else { - memcpy(m->sqn, sqn, 6); - printf("AKA-AUTS: Re-synchronized: " - "SQN=%02x%02x%02x%02x%02x%02x\n", - sqn[0], sqn[1], sqn[2], sqn[3], sqn[4], sqn[5]); - } -} - - -static int process(int s) -{ - char buf[1000]; - struct sockaddr_un from; - socklen_t fromlen; - ssize_t res; - - fromlen = sizeof(from); - res = recvfrom(s, buf, sizeof(buf), 0, (struct sockaddr *) &from, - &fromlen); - if (res < 0) { - perror("recvfrom"); - return -1; - } - - if (res == 0) - return 0; - - if ((size_t) res >= sizeof(buf)) - res = sizeof(buf) - 1; - buf[res] = '\0'; - - printf("Received: %s\n", buf); - - if (strncmp(buf, "SIM-REQ-AUTH ", 13) == 0) - sim_req_auth(s, &from, fromlen, buf + 13); - else if (strncmp(buf, "AKA-REQ-AUTH ", 13) == 0) - aka_req_auth(s, &from, fromlen, buf + 13); - else if (strncmp(buf, "AKA-AUTS ", 9) == 0) - aka_auts(s, &from, fromlen, buf + 9); - else - printf("Unknown request: %s\n", buf); - - return 0; -} - - -static void cleanup(void) -{ - struct gsm_triplet *g, *gprev; - struct milenage_parameters *m, *prev; - - g = gsm_db; - while (g) { - gprev = g; - g = g->next; - free(gprev); - } - - m = milenage_db; - while (m) { - prev = m; - m = m->next; - free(prev); - } - - close(serv_sock); - unlink(socket_path); -} - - -static void handle_term(int sig) -{ - printf("Signal %d - terminate\n", sig); - exit(0); -} - - -static void usage(void) -{ - printf("HLR/AuC testing gateway for hostapd EAP-SIM/AKA " - "database/authenticator\n" - "Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi>\n" - "\n" - "usage:\n" - "hlr_auc_gw [-h] [-s<socket path>] [-g<triplet file>] " - "[-m<milenage file>]\n" - "\n" - "options:\n" - " -h = show this usage help\n" - " -s<socket path> = path for UNIX domain socket\n" - " (default: %s)\n" - " -g<triplet file> = path for GSM authentication triplets\n" - " -m<milenage file> = path for Milenage keys\n", - default_socket_path); -} - - -int main(int argc, char *argv[]) -{ - int c; - char *milenage_file = NULL; - char *gsm_triplet_file = NULL; - - socket_path = default_socket_path; - - for (;;) { - c = getopt(argc, argv, "g:hm:s:"); - if (c < 0) - break; - switch (c) { - case 'g': - gsm_triplet_file = optarg; - break; - case 'h': - usage(); - return 0; - case 'm': - milenage_file = optarg; - break; - case 's': - socket_path = optarg; - break; - default: - usage(); - return -1; - } - } - - if (gsm_triplet_file && read_gsm_triplets(gsm_triplet_file) < 0) - return -1; - - if (milenage_file && read_milenage(milenage_file) < 0) - return -1; - - serv_sock = open_socket(socket_path); - if (serv_sock < 0) - return -1; - - printf("Listening for requests on %s\n", socket_path); - - atexit(cleanup); - signal(SIGTERM, handle_term); - signal(SIGINT, handle_term); - - for (;;) - process(serv_sock); - - return 0; -} diff --git a/contrib/wpa/src/hlr_auc_gw/hlr_auc_gw.milenage_db b/contrib/wpa/src/hlr_auc_gw/hlr_auc_gw.milenage_db deleted file mode 100644 index ecd06d7..0000000 --- a/contrib/wpa/src/hlr_auc_gw/hlr_auc_gw.milenage_db +++ /dev/null @@ -1,13 +0,0 @@ -# Parameters for Milenage (Example algorithms for AKA). -# The example Ki, OPc, and AMF values here are from 3GPP TS 35.208 v6.0.0 -# 4.3.20 Test Set 20. SQN is the last used SQN value. -# These values can be used for both UMTS (EAP-AKA) and GSM (EAP-SIM) -# authentication. In case of GSM/EAP-SIM, AMF and SQN values are not used, but -# dummy values will need to be included in this file. - -# IMSI Ki OPc AMF SQN -232010000000000 90dca4eda45b53cf0f12d7c9c3bc6a89 cb9cccc4b9258e6dca4760379fb82581 61df 000000000000 - -# These values are from Test Set 19 which has the AMF separation bit set to 1 -# and as such, is suitable for EAP-AKA' test. -555444333222111 5122250214c33e723a5dd523fc145fc0 981d464c7c52eb6e5036234984ad0bcf c3ab 16f3b3f70fc1 diff --git a/contrib/wpa/src/hlr_auc_gw/milenage.c b/contrib/wpa/src/hlr_auc_gw/milenage.c deleted file mode 100644 index 0ce5ef3..0000000 --- a/contrib/wpa/src/hlr_auc_gw/milenage.c +++ /dev/null @@ -1,1142 +0,0 @@ -/* - * 3GPP AKA - Milenage algorithm (3GPP TS 35.205, .206, .207, .208) - * Copyright (c) 2006-2007 <j@w1.fi> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - * - * This file implements an example authentication algorithm defined for 3GPP - * AKA. This can be used to implement a simple HLR/AuC into hlr_auc_gw to allow - * EAP-AKA to be tested properly with real USIM cards. - * - * This implementations assumes that the r1..r5 and c1..c5 constants defined in - * TS 35.206 are used, i.e., r1=64, r2=0, r3=32, r4=64, r5=96, c1=00..00, - * c2=00..01, c3=00..02, c4=00..04, c5=00..08. The block cipher is assumed to - * be AES (Rijndael). - */ - -#include "includes.h" - -#include "common.h" -#include "milenage.h" -#include "aes_wrap.h" - - -/** - * milenage_f1 - Milenage f1 and f1* algorithms - * @opc: OPc = 128-bit value derived from OP and K - * @k: K = 128-bit subscriber key - * @_rand: RAND = 128-bit random challenge - * @sqn: SQN = 48-bit sequence number - * @amf: AMF = 16-bit authentication management field - * @mac_a: Buffer for MAC-A = 64-bit network authentication code, or %NULL - * @mac_s: Buffer for MAC-S = 64-bit resync authentication code, or %NULL - * Returns: 0 on success, -1 on failure - */ -static int milenage_f1(const u8 *opc, const u8 *k, const u8 *_rand, - const u8 *sqn, const u8 *amf, u8 *mac_a, u8 *mac_s) -{ - u8 tmp1[16], tmp2[16], tmp3[16]; - int i; - - /* tmp1 = TEMP = E_K(RAND XOR OP_C) */ - for (i = 0; i < 16; i++) - tmp1[i] = _rand[i] ^ opc[i]; - if (aes_128_encrypt_block(k, tmp1, tmp1)) - return -1; - - /* tmp2 = IN1 = SQN || AMF || SQN || AMF */ - os_memcpy(tmp2, sqn, 6); - os_memcpy(tmp2 + 6, amf, 2); - os_memcpy(tmp2 + 8, tmp2, 8); - - /* OUT1 = E_K(TEMP XOR rot(IN1 XOR OP_C, r1) XOR c1) XOR OP_C */ - - /* rotate (tmp2 XOR OP_C) by r1 (= 0x40 = 8 bytes) */ - for (i = 0; i < 16; i++) - tmp3[(i + 8) % 16] = tmp2[i] ^ opc[i]; - /* XOR with TEMP = E_K(RAND XOR OP_C) */ - for (i = 0; i < 16; i++) - tmp3[i] ^= tmp1[i]; - /* XOR with c1 (= ..00, i.e., NOP) */ - - /* f1 || f1* = E_K(tmp3) XOR OP_c */ - if (aes_128_encrypt_block(k, tmp3, tmp1)) - return -1; - for (i = 0; i < 16; i++) - tmp1[i] ^= opc[i]; - if (mac_a) - os_memcpy(mac_a, tmp1, 8); /* f1 */ - if (mac_s) - os_memcpy(mac_s, tmp1 + 8, 8); /* f1* */ - return 0; -} - - -/** - * milenage_f2345 - Milenage f2, f3, f4, f5, f5* algorithms - * @opc: OPc = 128-bit value derived from OP and K - * @k: K = 128-bit subscriber key - * @_rand: RAND = 128-bit random challenge - * @res: Buffer for RES = 64-bit signed response (f2), or %NULL - * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL - * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL - * @ak: Buffer for AK = 48-bit anonymity key (f5), or %NULL - * @akstar: Buffer for AK = 48-bit anonymity key (f5*), or %NULL - * Returns: 0 on success, -1 on failure - */ -static int milenage_f2345(const u8 *opc, const u8 *k, const u8 *_rand, - u8 *res, u8 *ck, u8 *ik, u8 *ak, u8 *akstar) -{ - u8 tmp1[16], tmp2[16], tmp3[16]; - int i; - - /* tmp2 = TEMP = E_K(RAND XOR OP_C) */ - for (i = 0; i < 16; i++) - tmp1[i] = _rand[i] ^ opc[i]; - if (aes_128_encrypt_block(k, tmp1, tmp2)) - return -1; - - /* OUT2 = E_K(rot(TEMP XOR OP_C, r2) XOR c2) XOR OP_C */ - /* OUT3 = E_K(rot(TEMP XOR OP_C, r3) XOR c3) XOR OP_C */ - /* OUT4 = E_K(rot(TEMP XOR OP_C, r4) XOR c4) XOR OP_C */ - /* OUT5 = E_K(rot(TEMP XOR OP_C, r5) XOR c5) XOR OP_C */ - - /* f2 and f5 */ - /* rotate by r2 (= 0, i.e., NOP) */ - for (i = 0; i < 16; i++) - tmp1[i] = tmp2[i] ^ opc[i]; - tmp1[15] ^= 1; /* XOR c2 (= ..01) */ - /* f5 || f2 = E_K(tmp1) XOR OP_c */ - if (aes_128_encrypt_block(k, tmp1, tmp3)) - return -1; - for (i = 0; i < 16; i++) - tmp3[i] ^= opc[i]; - if (res) - os_memcpy(res, tmp3 + 8, 8); /* f2 */ - if (ak) - os_memcpy(ak, tmp3, 6); /* f5 */ - - /* f3 */ - if (ck) { - /* rotate by r3 = 0x20 = 4 bytes */ - for (i = 0; i < 16; i++) - tmp1[(i + 12) % 16] = tmp2[i] ^ opc[i]; - tmp1[15] ^= 2; /* XOR c3 (= ..02) */ - if (aes_128_encrypt_block(k, tmp1, ck)) - return -1; - for (i = 0; i < 16; i++) - ck[i] ^= opc[i]; - } - - /* f4 */ - if (ik) { - /* rotate by r4 = 0x40 = 8 bytes */ - for (i = 0; i < 16; i++) - tmp1[(i + 8) % 16] = tmp2[i] ^ opc[i]; - tmp1[15] ^= 4; /* XOR c4 (= ..04) */ - if (aes_128_encrypt_block(k, tmp1, ik)) - return -1; - for (i = 0; i < 16; i++) - ik[i] ^= opc[i]; - } - - /* f5* */ - if (akstar) { - /* rotate by r5 = 0x60 = 12 bytes */ - for (i = 0; i < 16; i++) - tmp1[(i + 4) % 16] = tmp2[i] ^ opc[i]; - tmp1[15] ^= 8; /* XOR c5 (= ..08) */ - if (aes_128_encrypt_block(k, tmp1, tmp1)) - return -1; - for (i = 0; i < 6; i++) - akstar[i] = tmp1[i] ^ opc[i]; - } - - return 0; -} - - -/** - * milenage_generate - Generate AKA AUTN,IK,CK,RES - * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.) - * @amf: AMF = 16-bit authentication management field - * @k: K = 128-bit subscriber key - * @sqn: SQN = 48-bit sequence number - * @_rand: RAND = 128-bit random challenge - * @autn: Buffer for AUTN = 128-bit authentication token - * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL - * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL - * @res: Buffer for RES = 64-bit signed response (f2), or %NULL - * @res_len: Max length for res; set to used length or 0 on failure - */ -void milenage_generate(const u8 *opc, const u8 *amf, const u8 *k, - const u8 *sqn, const u8 *_rand, u8 *autn, u8 *ik, - u8 *ck, u8 *res, size_t *res_len) -{ - int i; - u8 mac_a[8], ak[6]; - - if (*res_len < 8) { - *res_len = 0; - return; - } - if (milenage_f1(opc, k, _rand, sqn, amf, mac_a, NULL) || - milenage_f2345(opc, k, _rand, res, ck, ik, ak, NULL)) { - *res_len = 0; - return; - } - *res_len = 8; - - /* AUTN = (SQN ^ AK) || AMF || MAC */ - for (i = 0; i < 6; i++) - autn[i] = sqn[i] ^ ak[i]; - os_memcpy(autn + 6, amf, 2); - os_memcpy(autn + 8, mac_a, 8); -} - - -/** - * milenage_auts - Milenage AUTS validation - * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.) - * @k: K = 128-bit subscriber key - * @_rand: RAND = 128-bit random challenge - * @auts: AUTS = 112-bit authentication token from client - * @sqn: Buffer for SQN = 48-bit sequence number - * Returns: 0 = success (sqn filled), -1 on failure - */ -int milenage_auts(const u8 *opc, const u8 *k, const u8 *_rand, const u8 *auts, - u8 *sqn) -{ - u8 amf[2] = { 0x00, 0x00 }; /* TS 33.102 v7.0.0, 6.3.3 */ - u8 ak[6], mac_s[8]; - int i; - - if (milenage_f2345(opc, k, _rand, NULL, NULL, NULL, NULL, ak)) - return -1; - for (i = 0; i < 6; i++) - sqn[i] = auts[i] ^ ak[i]; - if (milenage_f1(opc, k, _rand, sqn, amf, NULL, mac_s) || - memcmp(mac_s, auts + 6, 8) != 0) - return -1; - return 0; -} - - -/** - * gsm_milenage - Generate GSM-Milenage (3GPP TS 55.205) authentication triplet - * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.) - * @k: K = 128-bit subscriber key - * @_rand: RAND = 128-bit random challenge - * @sres: Buffer for SRES = 32-bit SRES - * @kc: Buffer for Kc = 64-bit Kc - * Returns: 0 on success, -1 on failure - */ -int gsm_milenage(const u8 *opc, const u8 *k, const u8 *_rand, u8 *sres, u8 *kc) -{ - u8 res[8], ck[16], ik[16]; - int i; - - if (milenage_f2345(opc, k, _rand, res, ck, ik, NULL, NULL)) - return -1; - - for (i = 0; i < 8; i++) - kc[i] = ck[i] ^ ck[i + 8] ^ ik[i] ^ ik[i + 8]; - -#ifdef GSM_MILENAGE_ALT_SRES - os_memcpy(sres, res, 4); -#else /* GSM_MILENAGE_ALT_SRES */ - for (i = 0; i < 4; i++) - sres[i] = res[i] ^ res[i + 4]; -#endif /* GSM_MILENAGE_ALT_SRES */ - return 0; -} - - -/** - * milenage_generate - Generate AKA AUTN,IK,CK,RES - * @opc: OPc = 128-bit operator variant algorithm configuration field (encr.) - * @k: K = 128-bit subscriber key - * @sqn: SQN = 48-bit sequence number - * @_rand: RAND = 128-bit random challenge - * @autn: AUTN = 128-bit authentication token - * @ik: Buffer for IK = 128-bit integrity key (f4), or %NULL - * @ck: Buffer for CK = 128-bit confidentiality key (f3), or %NULL - * @res: Buffer for RES = 64-bit signed response (f2), or %NULL - * @res_len: Variable that will be set to RES length - * @auts: 112-bit buffer for AUTS - * Returns: 0 on success, -1 on failure, or -2 on synchronization failure - */ -int milenage_check(const u8 *opc, const u8 *k, const u8 *sqn, const u8 *_rand, - const u8 *autn, u8 *ik, u8 *ck, u8 *res, size_t *res_len, - u8 *auts) -{ - int i; - u8 mac_a[8], ak[6], rx_sqn[6]; - const u8 *amf; - - wpa_hexdump(MSG_DEBUG, "Milenage: AUTN", autn, 16); - wpa_hexdump(MSG_DEBUG, "Milenage: RAND", _rand, 16); - - if (milenage_f2345(opc, k, _rand, res, ck, ik, ak, NULL)) - return -1; - - *res_len = 8; - wpa_hexdump_key(MSG_DEBUG, "Milenage: RES", res, *res_len); - wpa_hexdump_key(MSG_DEBUG, "Milenage: CK", ck, 16); - wpa_hexdump_key(MSG_DEBUG, "Milenage: IK", ik, 16); - wpa_hexdump_key(MSG_DEBUG, "Milenage: AK", ak, 6); - - /* AUTN = (SQN ^ AK) || AMF || MAC */ - for (i = 0; i < 6; i++) - rx_sqn[i] = autn[i] ^ ak[i]; - wpa_hexdump(MSG_DEBUG, "Milenage: SQN", rx_sqn, 6); - - if (os_memcmp(rx_sqn, sqn, 6) <= 0) { - u8 auts_amf[2] = { 0x00, 0x00 }; /* TS 33.102 v7.0.0, 6.3.3 */ - if (milenage_f2345(opc, k, _rand, NULL, NULL, NULL, NULL, ak)) - return -1; - wpa_hexdump_key(MSG_DEBUG, "Milenage: AK*", ak, 6); - for (i = 0; i < 6; i++) - auts[i] = sqn[i] ^ ak[i]; - if (milenage_f1(opc, k, _rand, sqn, auts_amf, NULL, auts + 6)) - return -1; - wpa_hexdump(MSG_DEBUG, "Milenage: AUTS", auts, 14); - return -2; - } - - amf = autn + 6; - wpa_hexdump(MSG_DEBUG, "Milenage: AMF", amf, 2); - if (milenage_f1(opc, k, _rand, rx_sqn, amf, mac_a, NULL)) - return -1; - - wpa_hexdump(MSG_DEBUG, "Milenage: MAC_A", mac_a, 8); - - if (os_memcmp(mac_a, autn + 8, 8) != 0) { - wpa_printf(MSG_DEBUG, "Milenage: MAC mismatch"); - wpa_hexdump(MSG_DEBUG, "Milenage: Received MAC_A", - autn + 8, 8); - return -1; - } - - return 0; -} - - -#ifdef TEST_MAIN_MILENAGE - -extern int wpa_debug_level; - - -/** - * milenage_opc - Determine OPc from OP and K - * @op: OP = 128-bit operator variant algorithm configuration field - * @k: K = 128-bit subscriber key - * @opc: Buffer for OPc = 128-bit value derived from OP and K - */ -static void milenage_opc(const u8 *op, const u8 *k, u8 *opc) -{ - int i; - /* OP_C = OP XOR E_K(OP) */ - aes_128_encrypt_block(k, op, opc); - for (i = 0; i < 16; i++) - opc[i] ^= op[i]; -} - - -struct gsm_milenage_test_set { - u8 ki[16]; - u8 rand[16]; - u8 opc[16]; - u8 sres1[4]; - u8 sres2[4]; - u8 kc[8]; -}; - -static const struct gsm_milenage_test_set gsm_test_sets[] = -{ - { - /* 3GPP TS 55.205 v6.0.0 - Test Set 1 */ - { 0x46, 0x5b, 0x5c, 0xe8, 0xb1, 0x99, 0xb4, 0x9f, - 0xaa, 0x5f, 0x0a, 0x2e, 0xe2, 0x38, 0xa6, 0xbc }, - { 0x23, 0x55, 0x3c, 0xbe, 0x96, 0x37, 0xa8, 0x9d, - 0x21, 0x8a, 0xe6, 0x4d, 0xae, 0x47, 0xbf, 0x35 }, - { 0xcd, 0x63, 0xcb, 0x71, 0x95, 0x4a, 0x9f, 0x4e, - 0x48, 0xa5, 0x99, 0x4e, 0x37, 0xa0, 0x2b, 0xaf }, - { 0x46, 0xf8, 0x41, 0x6a }, - { 0xa5, 0x42, 0x11, 0xd5 }, - { 0xea, 0xe4, 0xbe, 0x82, 0x3a, 0xf9, 0xa0, 0x8b } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 2 */ - { 0xfe, 0xc8, 0x6b, 0xa6, 0xeb, 0x70, 0x7e, 0xd0, - 0x89, 0x05, 0x75, 0x7b, 0x1b, 0xb4, 0x4b, 0x8f }, - { 0x9f, 0x7c, 0x8d, 0x02, 0x1a, 0xcc, 0xf4, 0xdb, - 0x21, 0x3c, 0xcf, 0xf0, 0xc7, 0xf7, 0x1a, 0x6a }, - { 0x10, 0x06, 0x02, 0x0f, 0x0a, 0x47, 0x8b, 0xf6, - 0xb6, 0x99, 0xf1, 0x5c, 0x06, 0x2e, 0x42, 0xb3 }, - { 0x8c, 0x30, 0x8a, 0x5e }, - { 0x80, 0x11, 0xc4, 0x8c }, - { 0xaa, 0x01, 0x73, 0x9b, 0x8c, 0xaa, 0x97, 0x6d } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 3 */ - { 0x9e, 0x59, 0x44, 0xae, 0xa9, 0x4b, 0x81, 0x16, - 0x5c, 0x82, 0xfb, 0xf9, 0xf3, 0x2d, 0xb7, 0x51 }, - { 0xce, 0x83, 0xdb, 0xc5, 0x4a, 0xc0, 0x27, 0x4a, - 0x15, 0x7c, 0x17, 0xf8, 0x0d, 0x01, 0x7b, 0xd6 }, - { 0xa6, 0x4a, 0x50, 0x7a, 0xe1, 0xa2, 0xa9, 0x8b, - 0xb8, 0x8e, 0xb4, 0x21, 0x01, 0x35, 0xdc, 0x87 }, - { 0xcf, 0xbc, 0xe3, 0xfe }, - { 0xf3, 0x65, 0xcd, 0x68 }, - { 0x9a, 0x8e, 0xc9, 0x5f, 0x40, 0x8c, 0xc5, 0x07 } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 4 */ - { 0x4a, 0xb1, 0xde, 0xb0, 0x5c, 0xa6, 0xce, 0xb0, - 0x51, 0xfc, 0x98, 0xe7, 0x7d, 0x02, 0x6a, 0x84 }, - { 0x74, 0xb0, 0xcd, 0x60, 0x31, 0xa1, 0xc8, 0x33, - 0x9b, 0x2b, 0x6c, 0xe2, 0xb8, 0xc4, 0xa1, 0x86 }, - { 0xdc, 0xf0, 0x7c, 0xbd, 0x51, 0x85, 0x52, 0x90, - 0xb9, 0x2a, 0x07, 0xa9, 0x89, 0x1e, 0x52, 0x3e }, - { 0x96, 0x55, 0xe2, 0x65 }, - { 0x58, 0x60, 0xfc, 0x1b }, - { 0xcd, 0xc1, 0xdc, 0x08, 0x41, 0xb8, 0x1a, 0x22 } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 5 */ - { 0x6c, 0x38, 0xa1, 0x16, 0xac, 0x28, 0x0c, 0x45, - 0x4f, 0x59, 0x33, 0x2e, 0xe3, 0x5c, 0x8c, 0x4f }, - { 0xee, 0x64, 0x66, 0xbc, 0x96, 0x20, 0x2c, 0x5a, - 0x55, 0x7a, 0xbb, 0xef, 0xf8, 0xba, 0xbf, 0x63 }, - { 0x38, 0x03, 0xef, 0x53, 0x63, 0xb9, 0x47, 0xc6, - 0xaa, 0xa2, 0x25, 0xe5, 0x8f, 0xae, 0x39, 0x34 }, - { 0x13, 0x68, 0x8f, 0x17 }, - { 0x16, 0xc8, 0x23, 0x3f }, - { 0xdf, 0x75, 0xbc, 0x5e, 0xa8, 0x99, 0x87, 0x9f } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 6 */ - { 0x2d, 0x60, 0x9d, 0x4d, 0xb0, 0xac, 0x5b, 0xf0, - 0xd2, 0xc0, 0xde, 0x26, 0x70, 0x14, 0xde, 0x0d }, - { 0x19, 0x4a, 0xa7, 0x56, 0x01, 0x38, 0x96, 0xb7, - 0x4b, 0x4a, 0x2a, 0x3b, 0x0a, 0xf4, 0x53, 0x9e }, - { 0xc3, 0x5a, 0x0a, 0xb0, 0xbc, 0xbf, 0xc9, 0x25, - 0x2c, 0xaf, 0xf1, 0x5f, 0x24, 0xef, 0xbd, 0xe0 }, - { 0x55, 0x3d, 0x00, 0xb3 }, - { 0x8c, 0x25, 0xa1, 0x6c }, - { 0x84, 0xb4, 0x17, 0xae, 0x3a, 0xea, 0xb4, 0xf3 } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 7 */ - { 0xa5, 0x30, 0xa7, 0xfe, 0x42, 0x8f, 0xad, 0x10, - 0x82, 0xc4, 0x5e, 0xdd, 0xfc, 0xe1, 0x38, 0x84 }, - { 0x3a, 0x4c, 0x2b, 0x32, 0x45, 0xc5, 0x0e, 0xb5, - 0xc7, 0x1d, 0x08, 0x63, 0x93, 0x95, 0x76, 0x4d }, - { 0x27, 0x95, 0x3e, 0x49, 0xbc, 0x8a, 0xf6, 0xdc, - 0xc6, 0xe7, 0x30, 0xeb, 0x80, 0x28, 0x6b, 0xe3 }, - { 0x59, 0xf1, 0xa4, 0x4a }, - { 0xa6, 0x32, 0x41, 0xe1 }, - { 0x3b, 0x4e, 0x24, 0x4c, 0xdc, 0x60, 0xce, 0x03 } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 8 */ - { 0xd9, 0x15, 0x1c, 0xf0, 0x48, 0x96, 0xe2, 0x58, - 0x30, 0xbf, 0x2e, 0x08, 0x26, 0x7b, 0x83, 0x60 }, - { 0xf7, 0x61, 0xe5, 0xe9, 0x3d, 0x60, 0x3f, 0xeb, - 0x73, 0x0e, 0x27, 0x55, 0x6c, 0xb8, 0xa2, 0xca }, - { 0xc4, 0xc9, 0x3e, 0xff, 0xe8, 0xa0, 0x81, 0x38, - 0xc2, 0x03, 0xd4, 0xc2, 0x7c, 0xe4, 0xe3, 0xd9 }, - { 0x50, 0x58, 0x88, 0x61 }, - { 0x4a, 0x90, 0xb2, 0x17 }, - { 0x8d, 0x4e, 0xc0, 0x1d, 0xe5, 0x97, 0xac, 0xfe } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 9 */ - { 0xa0, 0xe2, 0x97, 0x1b, 0x68, 0x22, 0xe8, 0xd3, - 0x54, 0xa1, 0x8c, 0xc2, 0x35, 0x62, 0x4e, 0xcb }, - { 0x08, 0xef, 0xf8, 0x28, 0xb1, 0x3f, 0xdb, 0x56, - 0x27, 0x22, 0xc6, 0x5c, 0x7f, 0x30, 0xa9, 0xb2 }, - { 0x82, 0xa2, 0x6f, 0x22, 0xbb, 0xa9, 0xe9, 0x48, - 0x8f, 0x94, 0x9a, 0x10, 0xd9, 0x8e, 0x9c, 0xc4 }, - { 0xcd, 0xe6, 0xb0, 0x27 }, - { 0x4b, 0xc2, 0x21, 0x2d }, - { 0xd8, 0xde, 0xbc, 0x4f, 0xfb, 0xcd, 0x60, 0xaa } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 10 */ - { 0x0d, 0xa6, 0xf7, 0xba, 0x86, 0xd5, 0xea, 0xc8, - 0xa1, 0x9c, 0xf5, 0x63, 0xac, 0x58, 0x64, 0x2d }, - { 0x67, 0x9a, 0xc4, 0xdb, 0xac, 0xd7, 0xd2, 0x33, - 0xff, 0x9d, 0x68, 0x06, 0xf4, 0x14, 0x9c, 0xe3 }, - { 0x0d, 0xb1, 0x07, 0x1f, 0x87, 0x67, 0x56, 0x2c, - 0xa4, 0x3a, 0x0a, 0x64, 0xc4, 0x1e, 0x8d, 0x08 }, - { 0x02, 0xd1, 0x3a, 0xcd }, - { 0x6f, 0xc3, 0x0f, 0xee }, - { 0xf0, 0xea, 0xa5, 0x0a, 0x1e, 0xdc, 0xeb, 0xb7 } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 11 */ - { 0x77, 0xb4, 0x58, 0x43, 0xc8, 0x8e, 0x58, 0xc1, - 0x0d, 0x20, 0x26, 0x84, 0x51, 0x5e, 0xd4, 0x30 }, - { 0x4c, 0x47, 0xeb, 0x30, 0x76, 0xdc, 0x55, 0xfe, - 0x51, 0x06, 0xcb, 0x20, 0x34, 0xb8, 0xcd, 0x78 }, - { 0xd4, 0x83, 0xaf, 0xae, 0x56, 0x24, 0x09, 0xa3, - 0x26, 0xb5, 0xbb, 0x0b, 0x20, 0xc4, 0xd7, 0x62 }, - { 0x44, 0x38, 0x9d, 0x01 }, - { 0xae, 0xfa, 0x35, 0x7b }, - { 0x82, 0xdb, 0xab, 0x7f, 0x83, 0xf0, 0x63, 0xda } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 12 */ - { 0x72, 0x9b, 0x17, 0x72, 0x92, 0x70, 0xdd, 0x87, - 0xcc, 0xdf, 0x1b, 0xfe, 0x29, 0xb4, 0xe9, 0xbb }, - { 0x31, 0x1c, 0x4c, 0x92, 0x97, 0x44, 0xd6, 0x75, - 0xb7, 0x20, 0xf3, 0xb7, 0xe9, 0xb1, 0xcb, 0xd0 }, - { 0x22, 0x8c, 0x2f, 0x2f, 0x06, 0xac, 0x32, 0x68, - 0xa9, 0xe6, 0x16, 0xee, 0x16, 0xdb, 0x4b, 0xa1 }, - { 0x03, 0xe0, 0xfd, 0x84 }, - { 0x98, 0xdb, 0xbd, 0x09 }, - { 0x3c, 0x66, 0xcb, 0x98, 0xca, 0xb2, 0xd3, 0x3d } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 13 */ - { 0xd3, 0x2d, 0xd2, 0x3e, 0x89, 0xdc, 0x66, 0x23, - 0x54, 0xca, 0x12, 0xeb, 0x79, 0xdd, 0x32, 0xfa }, - { 0xcf, 0x7d, 0x0a, 0xb1, 0xd9, 0x43, 0x06, 0x95, - 0x0b, 0xf1, 0x20, 0x18, 0xfb, 0xd4, 0x68, 0x87 }, - { 0xd2, 0x2a, 0x4b, 0x41, 0x80, 0xa5, 0x32, 0x57, - 0x08, 0xa5, 0xff, 0x70, 0xd9, 0xf6, 0x7e, 0xc7 }, - { 0xbe, 0x73, 0xb3, 0xdc }, - { 0xaf, 0x4a, 0x41, 0x1e }, - { 0x96, 0x12, 0xb5, 0xd8, 0x8a, 0x41, 0x30, 0xbb } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 14 */ - { 0xaf, 0x7c, 0x65, 0xe1, 0x92, 0x72, 0x21, 0xde, - 0x59, 0x11, 0x87, 0xa2, 0xc5, 0x98, 0x7a, 0x53 }, - { 0x1f, 0x0f, 0x85, 0x78, 0x46, 0x4f, 0xd5, 0x9b, - 0x64, 0xbe, 0xd2, 0xd0, 0x94, 0x36, 0xb5, 0x7a }, - { 0xa4, 0xcf, 0x5c, 0x81, 0x55, 0xc0, 0x8a, 0x7e, - 0xff, 0x41, 0x8e, 0x54, 0x43, 0xb9, 0x8e, 0x55 }, - { 0x8f, 0xe0, 0x19, 0xc7 }, - { 0x7b, 0xff, 0xa5, 0xc2 }, - { 0x75, 0xa1, 0x50, 0xdf, 0x3c, 0x6a, 0xed, 0x08 } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 15 */ - { 0x5b, 0xd7, 0xec, 0xd3, 0xd3, 0x12, 0x7a, 0x41, - 0xd1, 0x25, 0x39, 0xbe, 0xd4, 0xe7, 0xcf, 0x71 }, - { 0x59, 0xb7, 0x5f, 0x14, 0x25, 0x1c, 0x75, 0x03, - 0x1d, 0x0b, 0xcb, 0xac, 0x1c, 0x2c, 0x04, 0xc7 }, - { 0x76, 0x08, 0x9d, 0x3c, 0x0f, 0xf3, 0xef, 0xdc, - 0x6e, 0x36, 0x72, 0x1d, 0x4f, 0xce, 0xb7, 0x47 }, - { 0x27, 0x20, 0x2b, 0x82 }, - { 0x7e, 0x3f, 0x44, 0xc7 }, - { 0xb7, 0xf9, 0x2e, 0x42, 0x6a, 0x36, 0xfe, 0xc5 } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 16 */ - { 0x6c, 0xd1, 0xc6, 0xce, 0xb1, 0xe0, 0x1e, 0x14, - 0xf1, 0xb8, 0x23, 0x16, 0xa9, 0x0b, 0x7f, 0x3d }, - { 0xf6, 0x9b, 0x78, 0xf3, 0x00, 0xa0, 0x56, 0x8b, - 0xce, 0x9f, 0x0c, 0xb9, 0x3c, 0x4b, 0xe4, 0xc9 }, - { 0xa2, 0x19, 0xdc, 0x37, 0xf1, 0xdc, 0x7d, 0x66, - 0x73, 0x8b, 0x58, 0x43, 0xc7, 0x99, 0xf2, 0x06 }, - { 0xdd, 0xd7, 0xef, 0xe6 }, - { 0x70, 0xf6, 0xbd, 0xb9 }, - { 0x88, 0xd9, 0xde, 0x10, 0xa2, 0x20, 0x04, 0xc5 } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 17 */ - { 0xb7, 0x3a, 0x90, 0xcb, 0xcf, 0x3a, 0xfb, 0x62, - 0x2d, 0xba, 0x83, 0xc5, 0x8a, 0x84, 0x15, 0xdf }, - { 0xb1, 0x20, 0xf1, 0xc1, 0xa0, 0x10, 0x2a, 0x2f, - 0x50, 0x7d, 0xd5, 0x43, 0xde, 0x68, 0x28, 0x1f }, - { 0xdf, 0x0c, 0x67, 0x86, 0x8f, 0xa2, 0x5f, 0x74, - 0x8b, 0x70, 0x44, 0xc6, 0xe7, 0xc2, 0x45, 0xb8 }, - { 0x67, 0xe4, 0xff, 0x3f }, - { 0x47, 0x9d, 0xd2, 0x5c }, - { 0xa8, 0x19, 0xe5, 0x77, 0xa8, 0xd6, 0x17, 0x5b } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 18 */ - { 0x51, 0x22, 0x25, 0x02, 0x14, 0xc3, 0x3e, 0x72, - 0x3a, 0x5d, 0xd5, 0x23, 0xfc, 0x14, 0x5f, 0xc0 }, - { 0x81, 0xe9, 0x2b, 0x6c, 0x0e, 0xe0, 0xe1, 0x2e, - 0xbc, 0xeb, 0xa8, 0xd9, 0x2a, 0x99, 0xdf, 0xa5 }, - { 0x98, 0x1d, 0x46, 0x4c, 0x7c, 0x52, 0xeb, 0x6e, - 0x50, 0x36, 0x23, 0x49, 0x84, 0xad, 0x0b, 0xcf }, - { 0x8a, 0x3b, 0x8d, 0x17 }, - { 0x28, 0xd7, 0xb0, 0xf2 }, - { 0x9a, 0x8d, 0x0e, 0x88, 0x3f, 0xf0, 0x88, 0x7a } - }, { - /* 3GPP TS 55.205 v6.0.0 - Test Set 19 */ - { 0x90, 0xdc, 0xa4, 0xed, 0xa4, 0x5b, 0x53, 0xcf, - 0x0f, 0x12, 0xd7, 0xc9, 0xc3, 0xbc, 0x6a, 0x89 }, - { 0x9f, 0xdd, 0xc7, 0x20, 0x92, 0xc6, 0xad, 0x03, - 0x6b, 0x6e, 0x46, 0x47, 0x89, 0x31, 0x5b, 0x78 }, - { 0xcb, 0x9c, 0xcc, 0xc4, 0xb9, 0x25, 0x8e, 0x6d, - 0xca, 0x47, 0x60, 0x37, 0x9f, 0xb8, 0x25, 0x81 }, - { 0xdf, 0x58, 0x52, 0x2f }, - { 0xa9, 0x51, 0x00, 0xe2 }, - { 0xed, 0x29, 0xb2, 0xf1, 0xc2, 0x7f, 0x9f, 0x34 } - } -}; - -#define NUM_GSM_TESTS (sizeof(gsm_test_sets) / sizeof(gsm_test_sets[0])) - - -struct milenage_test_set { - u8 k[16]; - u8 rand[16]; - u8 sqn[6]; - u8 amf[2]; - u8 op[16]; - u8 opc[16]; - u8 f1[8]; - u8 f1star[8]; - u8 f2[8]; - u8 f3[16]; - u8 f4[16]; - u8 f5[6]; - u8 f5star[6]; -}; - -static const struct milenage_test_set test_sets[] = -{ - { - /* 3GPP TS 35.208 v6.0.0 - 4.3.1 Test Set 1 */ - { 0x46, 0x5b, 0x5c, 0xe8, 0xb1, 0x99, 0xb4, 0x9f, - 0xaa, 0x5f, 0x0a, 0x2e, 0xe2, 0x38, 0xa6, 0xbc }, - { 0x23, 0x55, 0x3c, 0xbe, 0x96, 0x37, 0xa8, 0x9d, - 0x21, 0x8a, 0xe6, 0x4d, 0xae, 0x47, 0xbf, 0x35 }, - { 0xff, 0x9b, 0xb4, 0xd0, 0xb6, 0x07 }, - { 0xb9, 0xb9 }, - { 0xcd, 0xc2, 0x02, 0xd5, 0x12, 0x3e, 0x20, 0xf6, - 0x2b, 0x6d, 0x67, 0x6a, 0xc7, 0x2c, 0xb3, 0x18 }, - { 0xcd, 0x63, 0xcb, 0x71, 0x95, 0x4a, 0x9f, 0x4e, - 0x48, 0xa5, 0x99, 0x4e, 0x37, 0xa0, 0x2b, 0xaf }, - { 0x4a, 0x9f, 0xfa, 0xc3, 0x54, 0xdf, 0xaf, 0xb3 }, - { 0x01, 0xcf, 0xaf, 0x9e, 0xc4, 0xe8, 0x71, 0xe9 }, - { 0xa5, 0x42, 0x11, 0xd5, 0xe3, 0xba, 0x50, 0xbf }, - { 0xb4, 0x0b, 0xa9, 0xa3, 0xc5, 0x8b, 0x2a, 0x05, - 0xbb, 0xf0, 0xd9, 0x87, 0xb2, 0x1b, 0xf8, 0xcb }, - { 0xf7, 0x69, 0xbc, 0xd7, 0x51, 0x04, 0x46, 0x04, - 0x12, 0x76, 0x72, 0x71, 0x1c, 0x6d, 0x34, 0x41 }, - { 0xaa, 0x68, 0x9c, 0x64, 0x83, 0x70 }, - { 0x45, 0x1e, 0x8b, 0xec, 0xa4, 0x3b } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.2 Test Set 2 */ - { 0x46, 0x5b, 0x5c, 0xe8, 0xb1, 0x99, 0xb4, 0x9f, - 0xaa, 0x5f, 0x0a, 0x2e, 0xe2, 0x38, 0xa6, 0xbc }, - { 0x23, 0x55, 0x3c, 0xbe, 0x96, 0x37, 0xa8, 0x9d, - 0x21, 0x8a, 0xe6, 0x4d, 0xae, 0x47, 0xbf, 0x35 }, - { 0xff, 0x9b, 0xb4, 0xd0, 0xb6, 0x07 }, - { 0xb9, 0xb9 }, - { 0xcd, 0xc2, 0x02, 0xd5, 0x12, 0x3e, 0x20, 0xf6, - 0x2b, 0x6d, 0x67, 0x6a, 0xc7, 0x2c, 0xb3, 0x18 }, - { 0xcd, 0x63, 0xcb, 0x71, 0x95, 0x4a, 0x9f, 0x4e, - 0x48, 0xa5, 0x99, 0x4e, 0x37, 0xa0, 0x2b, 0xaf }, - { 0x4a, 0x9f, 0xfa, 0xc3, 0x54, 0xdf, 0xaf, 0xb3 }, - { 0x01, 0xcf, 0xaf, 0x9e, 0xc4, 0xe8, 0x71, 0xe9 }, - { 0xa5, 0x42, 0x11, 0xd5, 0xe3, 0xba, 0x50, 0xbf }, - { 0xb4, 0x0b, 0xa9, 0xa3, 0xc5, 0x8b, 0x2a, 0x05, - 0xbb, 0xf0, 0xd9, 0x87, 0xb2, 0x1b, 0xf8, 0xcb }, - { 0xf7, 0x69, 0xbc, 0xd7, 0x51, 0x04, 0x46, 0x04, - 0x12, 0x76, 0x72, 0x71, 0x1c, 0x6d, 0x34, 0x41 }, - { 0xaa, 0x68, 0x9c, 0x64, 0x83, 0x70 }, - { 0x45, 0x1e, 0x8b, 0xec, 0xa4, 0x3b } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.3 Test Set 3 */ - { 0xfe, 0xc8, 0x6b, 0xa6, 0xeb, 0x70, 0x7e, 0xd0, - 0x89, 0x05, 0x75, 0x7b, 0x1b, 0xb4, 0x4b, 0x8f }, - { 0x9f, 0x7c, 0x8d, 0x02, 0x1a, 0xcc, 0xf4, 0xdb, - 0x21, 0x3c, 0xcf, 0xf0, 0xc7, 0xf7, 0x1a, 0x6a }, - { 0x9d, 0x02, 0x77, 0x59, 0x5f, 0xfc }, - { 0x72, 0x5c }, - { 0xdb, 0xc5, 0x9a, 0xdc, 0xb6, 0xf9, 0xa0, 0xef, - 0x73, 0x54, 0x77, 0xb7, 0xfa, 0xdf, 0x83, 0x74 }, - { 0x10, 0x06, 0x02, 0x0f, 0x0a, 0x47, 0x8b, 0xf6, - 0xb6, 0x99, 0xf1, 0x5c, 0x06, 0x2e, 0x42, 0xb3 }, - { 0x9c, 0xab, 0xc3, 0xe9, 0x9b, 0xaf, 0x72, 0x81 }, - { 0x95, 0x81, 0x4b, 0xa2, 0xb3, 0x04, 0x43, 0x24 }, - { 0x80, 0x11, 0xc4, 0x8c, 0x0c, 0x21, 0x4e, 0xd2 }, - { 0x5d, 0xbd, 0xbb, 0x29, 0x54, 0xe8, 0xf3, 0xcd, - 0xe6, 0x65, 0xb0, 0x46, 0x17, 0x9a, 0x50, 0x98 }, - { 0x59, 0xa9, 0x2d, 0x3b, 0x47, 0x6a, 0x04, 0x43, - 0x48, 0x70, 0x55, 0xcf, 0x88, 0xb2, 0x30, 0x7b }, - { 0x33, 0x48, 0x4d, 0xc2, 0x13, 0x6b }, - { 0xde, 0xac, 0xdd, 0x84, 0x8c, 0xc6 } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.4 Test Set 4 */ - { 0x9e, 0x59, 0x44, 0xae, 0xa9, 0x4b, 0x81, 0x16, - 0x5c, 0x82, 0xfb, 0xf9, 0xf3, 0x2d, 0xb7, 0x51 }, - { 0xce, 0x83, 0xdb, 0xc5, 0x4a, 0xc0, 0x27, 0x4a, - 0x15, 0x7c, 0x17, 0xf8, 0x0d, 0x01, 0x7b, 0xd6 }, - { 0x0b, 0x60, 0x4a, 0x81, 0xec, 0xa8 }, - { 0x9e, 0x09 }, - { 0x22, 0x30, 0x14, 0xc5, 0x80, 0x66, 0x94, 0xc0, - 0x07, 0xca, 0x1e, 0xee, 0xf5, 0x7f, 0x00, 0x4f }, - { 0xa6, 0x4a, 0x50, 0x7a, 0xe1, 0xa2, 0xa9, 0x8b, - 0xb8, 0x8e, 0xb4, 0x21, 0x01, 0x35, 0xdc, 0x87 }, - { 0x74, 0xa5, 0x82, 0x20, 0xcb, 0xa8, 0x4c, 0x49 }, - { 0xac, 0x2c, 0xc7, 0x4a, 0x96, 0x87, 0x18, 0x37 }, - { 0xf3, 0x65, 0xcd, 0x68, 0x3c, 0xd9, 0x2e, 0x96 }, - { 0xe2, 0x03, 0xed, 0xb3, 0x97, 0x15, 0x74, 0xf5, - 0xa9, 0x4b, 0x0d, 0x61, 0xb8, 0x16, 0x34, 0x5d }, - { 0x0c, 0x45, 0x24, 0xad, 0xea, 0xc0, 0x41, 0xc4, - 0xdd, 0x83, 0x0d, 0x20, 0x85, 0x4f, 0xc4, 0x6b }, - { 0xf0, 0xb9, 0xc0, 0x8a, 0xd0, 0x2e }, - { 0x60, 0x85, 0xa8, 0x6c, 0x6f, 0x63 } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.5 Test Set 5 */ - { 0x4a, 0xb1, 0xde, 0xb0, 0x5c, 0xa6, 0xce, 0xb0, - 0x51, 0xfc, 0x98, 0xe7, 0x7d, 0x02, 0x6a, 0x84 }, - { 0x74, 0xb0, 0xcd, 0x60, 0x31, 0xa1, 0xc8, 0x33, - 0x9b, 0x2b, 0x6c, 0xe2, 0xb8, 0xc4, 0xa1, 0x86 }, - { 0xe8, 0x80, 0xa1, 0xb5, 0x80, 0xb6 }, - { 0x9f, 0x07 }, - { 0x2d, 0x16, 0xc5, 0xcd, 0x1f, 0xdf, 0x6b, 0x22, - 0x38, 0x35, 0x84, 0xe3, 0xbe, 0xf2, 0xa8, 0xd8 }, - { 0xdc, 0xf0, 0x7c, 0xbd, 0x51, 0x85, 0x52, 0x90, - 0xb9, 0x2a, 0x07, 0xa9, 0x89, 0x1e, 0x52, 0x3e }, - { 0x49, 0xe7, 0x85, 0xdd, 0x12, 0x62, 0x6e, 0xf2 }, - { 0x9e, 0x85, 0x79, 0x03, 0x36, 0xbb, 0x3f, 0xa2 }, - { 0x58, 0x60, 0xfc, 0x1b, 0xce, 0x35, 0x1e, 0x7e }, - { 0x76, 0x57, 0x76, 0x6b, 0x37, 0x3d, 0x1c, 0x21, - 0x38, 0xf3, 0x07, 0xe3, 0xde, 0x92, 0x42, 0xf9 }, - { 0x1c, 0x42, 0xe9, 0x60, 0xd8, 0x9b, 0x8f, 0xa9, - 0x9f, 0x27, 0x44, 0xe0, 0x70, 0x8c, 0xcb, 0x53 }, - { 0x31, 0xe1, 0x1a, 0x60, 0x91, 0x18 }, - { 0xfe, 0x25, 0x55, 0xe5, 0x4a, 0xa9 } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.6 Test Set 6 */ - { 0x6c, 0x38, 0xa1, 0x16, 0xac, 0x28, 0x0c, 0x45, - 0x4f, 0x59, 0x33, 0x2e, 0xe3, 0x5c, 0x8c, 0x4f }, - { 0xee, 0x64, 0x66, 0xbc, 0x96, 0x20, 0x2c, 0x5a, - 0x55, 0x7a, 0xbb, 0xef, 0xf8, 0xba, 0xbf, 0x63 }, - { 0x41, 0x4b, 0x98, 0x22, 0x21, 0x81 }, - { 0x44, 0x64 }, - { 0x1b, 0xa0, 0x0a, 0x1a, 0x7c, 0x67, 0x00, 0xac, - 0x8c, 0x3f, 0xf3, 0xe9, 0x6a, 0xd0, 0x87, 0x25 }, - { 0x38, 0x03, 0xef, 0x53, 0x63, 0xb9, 0x47, 0xc6, - 0xaa, 0xa2, 0x25, 0xe5, 0x8f, 0xae, 0x39, 0x34 }, - { 0x07, 0x8a, 0xdf, 0xb4, 0x88, 0x24, 0x1a, 0x57 }, - { 0x80, 0x24, 0x6b, 0x8d, 0x01, 0x86, 0xbc, 0xf1 }, - { 0x16, 0xc8, 0x23, 0x3f, 0x05, 0xa0, 0xac, 0x28 }, - { 0x3f, 0x8c, 0x75, 0x87, 0xfe, 0x8e, 0x4b, 0x23, - 0x3a, 0xf6, 0x76, 0xae, 0xde, 0x30, 0xba, 0x3b }, - { 0xa7, 0x46, 0x6c, 0xc1, 0xe6, 0xb2, 0xa1, 0x33, - 0x7d, 0x49, 0xd3, 0xb6, 0x6e, 0x95, 0xd7, 0xb4 }, - { 0x45, 0xb0, 0xf6, 0x9a, 0xb0, 0x6c }, - { 0x1f, 0x53, 0xcd, 0x2b, 0x11, 0x13 } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.7 Test Set 7 */ - { 0x2d, 0x60, 0x9d, 0x4d, 0xb0, 0xac, 0x5b, 0xf0, - 0xd2, 0xc0, 0xde, 0x26, 0x70, 0x14, 0xde, 0x0d }, - { 0x19, 0x4a, 0xa7, 0x56, 0x01, 0x38, 0x96, 0xb7, - 0x4b, 0x4a, 0x2a, 0x3b, 0x0a, 0xf4, 0x53, 0x9e }, - { 0x6b, 0xf6, 0x94, 0x38, 0xc2, 0xe4 }, - { 0x5f, 0x67 }, - { 0x46, 0x0a, 0x48, 0x38, 0x54, 0x27, 0xaa, 0x39, - 0x26, 0x4a, 0xac, 0x8e, 0xfc, 0x9e, 0x73, 0xe8 }, - { 0xc3, 0x5a, 0x0a, 0xb0, 0xbc, 0xbf, 0xc9, 0x25, - 0x2c, 0xaf, 0xf1, 0x5f, 0x24, 0xef, 0xbd, 0xe0 }, - { 0xbd, 0x07, 0xd3, 0x00, 0x3b, 0x9e, 0x5c, 0xc3 }, - { 0xbc, 0xb6, 0xc2, 0xfc, 0xad, 0x15, 0x22, 0x50 }, - { 0x8c, 0x25, 0xa1, 0x6c, 0xd9, 0x18, 0xa1, 0xdf }, - { 0x4c, 0xd0, 0x84, 0x60, 0x20, 0xf8, 0xfa, 0x07, - 0x31, 0xdd, 0x47, 0xcb, 0xdc, 0x6b, 0xe4, 0x11 }, - { 0x88, 0xab, 0x80, 0xa4, 0x15, 0xf1, 0x5c, 0x73, - 0x71, 0x12, 0x54, 0xa1, 0xd3, 0x88, 0xf6, 0x96 }, - { 0x7e, 0x64, 0x55, 0xf3, 0x4c, 0xf3 }, - { 0xdc, 0x6d, 0xd0, 0x1e, 0x8f, 0x15 } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.8 Test Set 8 */ - { 0xa5, 0x30, 0xa7, 0xfe, 0x42, 0x8f, 0xad, 0x10, - 0x82, 0xc4, 0x5e, 0xdd, 0xfc, 0xe1, 0x38, 0x84 }, - { 0x3a, 0x4c, 0x2b, 0x32, 0x45, 0xc5, 0x0e, 0xb5, - 0xc7, 0x1d, 0x08, 0x63, 0x93, 0x95, 0x76, 0x4d }, - { 0xf6, 0x3f, 0x5d, 0x76, 0x87, 0x84 }, - { 0xb9, 0x0e }, - { 0x51, 0x1c, 0x6c, 0x4e, 0x83, 0xe3, 0x8c, 0x89, - 0xb1, 0xc5, 0xd8, 0xdd, 0xe6, 0x24, 0x26, 0xfa }, - { 0x27, 0x95, 0x3e, 0x49, 0xbc, 0x8a, 0xf6, 0xdc, - 0xc6, 0xe7, 0x30, 0xeb, 0x80, 0x28, 0x6b, 0xe3 }, - { 0x53, 0x76, 0x1f, 0xbd, 0x67, 0x9b, 0x0b, 0xad }, - { 0x21, 0xad, 0xfd, 0x33, 0x4a, 0x10, 0xe7, 0xce }, - { 0xa6, 0x32, 0x41, 0xe1, 0xff, 0xc3, 0xe5, 0xab }, - { 0x10, 0xf0, 0x5b, 0xab, 0x75, 0xa9, 0x9a, 0x5f, - 0xbb, 0x98, 0xa9, 0xc2, 0x87, 0x67, 0x9c, 0x3b }, - { 0xf9, 0xec, 0x08, 0x65, 0xeb, 0x32, 0xf2, 0x23, - 0x69, 0xca, 0xde, 0x40, 0xc5, 0x9c, 0x3a, 0x44 }, - { 0x88, 0x19, 0x6c, 0x47, 0x98, 0x6f }, - { 0xc9, 0x87, 0xa3, 0xd2, 0x31, 0x15 } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.9 Test Set 9 */ - { 0xd9, 0x15, 0x1c, 0xf0, 0x48, 0x96, 0xe2, 0x58, - 0x30, 0xbf, 0x2e, 0x08, 0x26, 0x7b, 0x83, 0x60 }, - { 0xf7, 0x61, 0xe5, 0xe9, 0x3d, 0x60, 0x3f, 0xeb, - 0x73, 0x0e, 0x27, 0x55, 0x6c, 0xb8, 0xa2, 0xca }, - { 0x47, 0xee, 0x01, 0x99, 0x82, 0x0a }, - { 0x91, 0x13 }, - { 0x75, 0xfc, 0x22, 0x33, 0xa4, 0x42, 0x94, 0xee, - 0x8e, 0x6d, 0xe2, 0x5c, 0x43, 0x53, 0xd2, 0x6b }, - { 0xc4, 0xc9, 0x3e, 0xff, 0xe8, 0xa0, 0x81, 0x38, - 0xc2, 0x03, 0xd4, 0xc2, 0x7c, 0xe4, 0xe3, 0xd9 }, - { 0x66, 0xcc, 0x4b, 0xe4, 0x48, 0x62, 0xaf, 0x1f }, - { 0x7a, 0x4b, 0x8d, 0x7a, 0x87, 0x53, 0xf2, 0x46 }, - { 0x4a, 0x90, 0xb2, 0x17, 0x1a, 0xc8, 0x3a, 0x76 }, - { 0x71, 0x23, 0x6b, 0x71, 0x29, 0xf9, 0xb2, 0x2a, - 0xb7, 0x7e, 0xa7, 0xa5, 0x4c, 0x96, 0xda, 0x22 }, - { 0x90, 0x52, 0x7e, 0xba, 0xa5, 0x58, 0x89, 0x68, - 0xdb, 0x41, 0x72, 0x73, 0x25, 0xa0, 0x4d, 0x9e }, - { 0x82, 0xa0, 0xf5, 0x28, 0x7a, 0x71 }, - { 0x52, 0x7d, 0xbf, 0x41, 0xf3, 0x5f } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.10 Test Set 10 */ - { 0xa0, 0xe2, 0x97, 0x1b, 0x68, 0x22, 0xe8, 0xd3, - 0x54, 0xa1, 0x8c, 0xc2, 0x35, 0x62, 0x4e, 0xcb }, - { 0x08, 0xef, 0xf8, 0x28, 0xb1, 0x3f, 0xdb, 0x56, - 0x27, 0x22, 0xc6, 0x5c, 0x7f, 0x30, 0xa9, 0xb2 }, - { 0xdb, 0x5c, 0x06, 0x64, 0x81, 0xe0 }, - { 0x71, 0x6b }, - { 0x32, 0x37, 0x92, 0xfa, 0xca, 0x21, 0xfb, 0x4d, - 0x5d, 0x6f, 0x13, 0xc1, 0x45, 0xa9, 0xd2, 0xc1 }, - { 0x82, 0xa2, 0x6f, 0x22, 0xbb, 0xa9, 0xe9, 0x48, - 0x8f, 0x94, 0x9a, 0x10, 0xd9, 0x8e, 0x9c, 0xc4 }, - { 0x94, 0x85, 0xfe, 0x24, 0x62, 0x1c, 0xb9, 0xf6 }, - { 0xbc, 0xe3, 0x25, 0xce, 0x03, 0xe2, 0xe9, 0xb9 }, - { 0x4b, 0xc2, 0x21, 0x2d, 0x86, 0x24, 0x91, 0x0a }, - { 0x08, 0xce, 0xf6, 0xd0, 0x04, 0xec, 0x61, 0x47, - 0x1a, 0x3c, 0x3c, 0xda, 0x04, 0x81, 0x37, 0xfa }, - { 0xed, 0x03, 0x18, 0xca, 0x5d, 0xeb, 0x92, 0x06, - 0x27, 0x2f, 0x6e, 0x8f, 0xa6, 0x4b, 0xa4, 0x11 }, - { 0xa2, 0xf8, 0x58, 0xaa, 0x9e, 0x5d }, - { 0x74, 0xe7, 0x6f, 0xbb, 0xec, 0x38 } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.11 Test Set 11 */ - { 0x0d, 0xa6, 0xf7, 0xba, 0x86, 0xd5, 0xea, 0xc8, - 0xa1, 0x9c, 0xf5, 0x63, 0xac, 0x58, 0x64, 0x2d }, - { 0x67, 0x9a, 0xc4, 0xdb, 0xac, 0xd7, 0xd2, 0x33, - 0xff, 0x9d, 0x68, 0x06, 0xf4, 0x14, 0x9c, 0xe3 }, - { 0x6e, 0x23, 0x31, 0xd6, 0x92, 0xad }, - { 0x22, 0x4a }, - { 0x4b, 0x9a, 0x26, 0xfa, 0x45, 0x9e, 0x3a, 0xcb, - 0xff, 0x36, 0xf4, 0x01, 0x5d, 0xe3, 0xbd, 0xc1 }, - { 0x0d, 0xb1, 0x07, 0x1f, 0x87, 0x67, 0x56, 0x2c, - 0xa4, 0x3a, 0x0a, 0x64, 0xc4, 0x1e, 0x8d, 0x08 }, - { 0x28, 0x31, 0xd7, 0xae, 0x90, 0x88, 0xe4, 0x92 }, - { 0x9b, 0x2e, 0x16, 0x95, 0x11, 0x35, 0xd5, 0x23 }, - { 0x6f, 0xc3, 0x0f, 0xee, 0x6d, 0x12, 0x35, 0x23 }, - { 0x69, 0xb1, 0xca, 0xe7, 0xc7, 0x42, 0x9d, 0x97, - 0x5e, 0x24, 0x5c, 0xac, 0xb0, 0x5a, 0x51, 0x7c }, - { 0x74, 0xf2, 0x4e, 0x8c, 0x26, 0xdf, 0x58, 0xe1, - 0xb3, 0x8d, 0x7d, 0xcd, 0x4f, 0x1b, 0x7f, 0xbd }, - { 0x4c, 0x53, 0x9a, 0x26, 0xe1, 0xfa }, - { 0x07, 0x86, 0x1e, 0x12, 0x69, 0x28 } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.12 Test Set 12 */ - { 0x77, 0xb4, 0x58, 0x43, 0xc8, 0x8e, 0x58, 0xc1, - 0x0d, 0x20, 0x26, 0x84, 0x51, 0x5e, 0xd4, 0x30 }, - { 0x4c, 0x47, 0xeb, 0x30, 0x76, 0xdc, 0x55, 0xfe, - 0x51, 0x06, 0xcb, 0x20, 0x34, 0xb8, 0xcd, 0x78 }, - { 0xfe, 0x1a, 0x87, 0x31, 0x00, 0x5d }, - { 0xad, 0x25 }, - { 0xbf, 0x32, 0x86, 0xc7, 0xa5, 0x14, 0x09, 0xce, - 0x95, 0x72, 0x4d, 0x50, 0x3b, 0xfe, 0x6e, 0x70 }, - { 0xd4, 0x83, 0xaf, 0xae, 0x56, 0x24, 0x09, 0xa3, - 0x26, 0xb5, 0xbb, 0x0b, 0x20, 0xc4, 0xd7, 0x62 }, - { 0x08, 0x33, 0x2d, 0x7e, 0x9f, 0x48, 0x45, 0x70 }, - { 0xed, 0x41, 0xb7, 0x34, 0x48, 0x9d, 0x52, 0x07 }, - { 0xae, 0xfa, 0x35, 0x7b, 0xea, 0xc2, 0xa8, 0x7a }, - { 0x90, 0x8c, 0x43, 0xf0, 0x56, 0x9c, 0xb8, 0xf7, - 0x4b, 0xc9, 0x71, 0xe7, 0x06, 0xc3, 0x6c, 0x5f }, - { 0xc2, 0x51, 0xdf, 0x0d, 0x88, 0x8d, 0xd9, 0x32, - 0x9b, 0xcf, 0x46, 0x65, 0x5b, 0x22, 0x6e, 0x40 }, - { 0x30, 0xff, 0x25, 0xcd, 0xad, 0xf6 }, - { 0xe8, 0x4e, 0xd0, 0xd4, 0x67, 0x7e } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.13 Test Set 13 */ - { 0x72, 0x9b, 0x17, 0x72, 0x92, 0x70, 0xdd, 0x87, - 0xcc, 0xdf, 0x1b, 0xfe, 0x29, 0xb4, 0xe9, 0xbb }, - { 0x31, 0x1c, 0x4c, 0x92, 0x97, 0x44, 0xd6, 0x75, - 0xb7, 0x20, 0xf3, 0xb7, 0xe9, 0xb1, 0xcb, 0xd0 }, - { 0xc8, 0x5c, 0x4c, 0xf6, 0x59, 0x16 }, - { 0x5b, 0xb2 }, - { 0xd0, 0x4c, 0x9c, 0x35, 0xbd, 0x22, 0x62, 0xfa, - 0x81, 0x0d, 0x29, 0x24, 0xd0, 0x36, 0xfd, 0x13 }, - { 0x22, 0x8c, 0x2f, 0x2f, 0x06, 0xac, 0x32, 0x68, - 0xa9, 0xe6, 0x16, 0xee, 0x16, 0xdb, 0x4b, 0xa1 }, - { 0xff, 0x79, 0x4f, 0xe2, 0xf8, 0x27, 0xeb, 0xf8 }, - { 0x24, 0xfe, 0x4d, 0xc6, 0x1e, 0x87, 0x4b, 0x52 }, - { 0x98, 0xdb, 0xbd, 0x09, 0x9b, 0x3b, 0x40, 0x8d }, - { 0x44, 0xc0, 0xf2, 0x3c, 0x54, 0x93, 0xcf, 0xd2, - 0x41, 0xe4, 0x8f, 0x19, 0x7e, 0x1d, 0x10, 0x12 }, - { 0x0c, 0x9f, 0xb8, 0x16, 0x13, 0x88, 0x4c, 0x25, - 0x35, 0xdd, 0x0e, 0xab, 0xf3, 0xb4, 0x40, 0xd8 }, - { 0x53, 0x80, 0xd1, 0x58, 0xcf, 0xe3 }, - { 0x87, 0xac, 0x3b, 0x55, 0x9f, 0xb6 } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.14 Test Set 14 */ - { 0xd3, 0x2d, 0xd2, 0x3e, 0x89, 0xdc, 0x66, 0x23, - 0x54, 0xca, 0x12, 0xeb, 0x79, 0xdd, 0x32, 0xfa }, - { 0xcf, 0x7d, 0x0a, 0xb1, 0xd9, 0x43, 0x06, 0x95, - 0x0b, 0xf1, 0x20, 0x18, 0xfb, 0xd4, 0x68, 0x87 }, - { 0x48, 0x41, 0x07, 0xe5, 0x6a, 0x43 }, - { 0xb5, 0xe6 }, - { 0xfe, 0x75, 0x90, 0x5b, 0x9d, 0xa4, 0x7d, 0x35, - 0x62, 0x36, 0xd0, 0x31, 0x4e, 0x09, 0xc3, 0x2e }, - { 0xd2, 0x2a, 0x4b, 0x41, 0x80, 0xa5, 0x32, 0x57, - 0x08, 0xa5, 0xff, 0x70, 0xd9, 0xf6, 0x7e, 0xc7 }, - { 0xcf, 0x19, 0xd6, 0x2b, 0x6a, 0x80, 0x98, 0x66 }, - { 0x5d, 0x26, 0x95, 0x37, 0xe4, 0x5e, 0x2c, 0xe6 }, - { 0xaf, 0x4a, 0x41, 0x1e, 0x11, 0x39, 0xf2, 0xc2 }, - { 0x5a, 0xf8, 0x6b, 0x80, 0xed, 0xb7, 0x0d, 0xf5, - 0x29, 0x2c, 0xc1, 0x12, 0x1c, 0xba, 0xd5, 0x0c }, - { 0x7f, 0x4d, 0x6a, 0xe7, 0x44, 0x0e, 0x18, 0x78, - 0x9a, 0x8b, 0x75, 0xad, 0x3f, 0x42, 0xf0, 0x3a }, - { 0x21, 0x7a, 0xf4, 0x92, 0x72, 0xad }, - { 0x90, 0x0e, 0x10, 0x1c, 0x67, 0x7e } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.15 Test Set 15 */ - { 0xaf, 0x7c, 0x65, 0xe1, 0x92, 0x72, 0x21, 0xde, - 0x59, 0x11, 0x87, 0xa2, 0xc5, 0x98, 0x7a, 0x53 }, - { 0x1f, 0x0f, 0x85, 0x78, 0x46, 0x4f, 0xd5, 0x9b, - 0x64, 0xbe, 0xd2, 0xd0, 0x94, 0x36, 0xb5, 0x7a }, - { 0x3d, 0x62, 0x7b, 0x01, 0x41, 0x8d }, - { 0x84, 0xf6 }, - { 0x0c, 0x7a, 0xcb, 0x8d, 0x95, 0xb7, 0xd4, 0xa3, - 0x1c, 0x5a, 0xca, 0x6d, 0x26, 0x34, 0x5a, 0x88 }, - { 0xa4, 0xcf, 0x5c, 0x81, 0x55, 0xc0, 0x8a, 0x7e, - 0xff, 0x41, 0x8e, 0x54, 0x43, 0xb9, 0x8e, 0x55 }, - { 0xc3, 0x7c, 0xae, 0x78, 0x05, 0x64, 0x20, 0x32 }, - { 0x68, 0xcd, 0x09, 0xa4, 0x52, 0xd8, 0xdb, 0x7c }, - { 0x7b, 0xff, 0xa5, 0xc2, 0xf4, 0x1f, 0xbc, 0x05 }, - { 0x3f, 0x8c, 0x3f, 0x3c, 0xcf, 0x76, 0x25, 0xbf, - 0x77, 0xfc, 0x94, 0xbc, 0xfd, 0x22, 0xfd, 0x26 }, - { 0xab, 0xcb, 0xae, 0x8f, 0xd4, 0x61, 0x15, 0xe9, - 0x96, 0x1a, 0x55, 0xd0, 0xda, 0x5f, 0x20, 0x78 }, - { 0x83, 0x7f, 0xd7, 0xb7, 0x44, 0x19 }, - { 0x56, 0xe9, 0x7a, 0x60, 0x90, 0xb1 } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.16 Test Set 16 */ - { 0x5b, 0xd7, 0xec, 0xd3, 0xd3, 0x12, 0x7a, 0x41, - 0xd1, 0x25, 0x39, 0xbe, 0xd4, 0xe7, 0xcf, 0x71 }, - { 0x59, 0xb7, 0x5f, 0x14, 0x25, 0x1c, 0x75, 0x03, - 0x1d, 0x0b, 0xcb, 0xac, 0x1c, 0x2c, 0x04, 0xc7 }, - { 0xa2, 0x98, 0xae, 0x89, 0x29, 0xdc }, - { 0xd0, 0x56 }, - { 0xf9, 0x67, 0xf7, 0x60, 0x38, 0xb9, 0x20, 0xa9, - 0xcd, 0x25, 0xe1, 0x0c, 0x08, 0xb4, 0x99, 0x24 }, - { 0x76, 0x08, 0x9d, 0x3c, 0x0f, 0xf3, 0xef, 0xdc, - 0x6e, 0x36, 0x72, 0x1d, 0x4f, 0xce, 0xb7, 0x47 }, - { 0xc3, 0xf2, 0x5c, 0xd9, 0x43, 0x09, 0x10, 0x7e }, - { 0xb0, 0xc8, 0xba, 0x34, 0x36, 0x65, 0xaf, 0xcc }, - { 0x7e, 0x3f, 0x44, 0xc7, 0x59, 0x1f, 0x6f, 0x45 }, - { 0xd4, 0x2b, 0x2d, 0x61, 0x5e, 0x49, 0xa0, 0x3a, - 0xc2, 0x75, 0xa5, 0xae, 0xf9, 0x7a, 0xf8, 0x92 }, - { 0x0b, 0x3f, 0x8d, 0x02, 0x4f, 0xe6, 0xbf, 0xaf, - 0xaa, 0x98, 0x2b, 0x8f, 0x82, 0xe3, 0x19, 0xc2 }, - { 0x5b, 0xe1, 0x14, 0x95, 0x52, 0x5d }, - { 0x4d, 0x6a, 0x34, 0xa1, 0xe4, 0xeb } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.17 Test Set 17 */ - { 0x6c, 0xd1, 0xc6, 0xce, 0xb1, 0xe0, 0x1e, 0x14, - 0xf1, 0xb8, 0x23, 0x16, 0xa9, 0x0b, 0x7f, 0x3d }, - { 0xf6, 0x9b, 0x78, 0xf3, 0x00, 0xa0, 0x56, 0x8b, - 0xce, 0x9f, 0x0c, 0xb9, 0x3c, 0x4b, 0xe4, 0xc9 }, - { 0xb4, 0xfc, 0xe5, 0xfe, 0xb0, 0x59 }, - { 0xe4, 0xbb }, - { 0x07, 0x8b, 0xfc, 0xa9, 0x56, 0x46, 0x59, 0xec, - 0xd8, 0x85, 0x1e, 0x84, 0xe6, 0xc5, 0x9b, 0x48 }, - { 0xa2, 0x19, 0xdc, 0x37, 0xf1, 0xdc, 0x7d, 0x66, - 0x73, 0x8b, 0x58, 0x43, 0xc7, 0x99, 0xf2, 0x06 }, - { 0x69, 0xa9, 0x08, 0x69, 0xc2, 0x68, 0xcb, 0x7b }, - { 0x2e, 0x0f, 0xdc, 0xf9, 0xfd, 0x1c, 0xfa, 0x6a }, - { 0x70, 0xf6, 0xbd, 0xb9, 0xad, 0x21, 0x52, 0x5f }, - { 0x6e, 0xda, 0xf9, 0x9e, 0x5b, 0xd9, 0xf8, 0x5d, - 0x5f, 0x36, 0xd9, 0x1c, 0x12, 0x72, 0xfb, 0x4b }, - { 0xd6, 0x1c, 0x85, 0x3c, 0x28, 0x0d, 0xd9, 0xc4, - 0x6f, 0x29, 0x7b, 0xae, 0xc3, 0x86, 0xde, 0x17 }, - { 0x1c, 0x40, 0x8a, 0x85, 0x8b, 0x3e }, - { 0xaa, 0x4a, 0xe5, 0x2d, 0xaa, 0x30 } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.18 Test Set 18 */ - { 0xb7, 0x3a, 0x90, 0xcb, 0xcf, 0x3a, 0xfb, 0x62, - 0x2d, 0xba, 0x83, 0xc5, 0x8a, 0x84, 0x15, 0xdf }, - { 0xb1, 0x20, 0xf1, 0xc1, 0xa0, 0x10, 0x2a, 0x2f, - 0x50, 0x7d, 0xd5, 0x43, 0xde, 0x68, 0x28, 0x1f }, - { 0xf1, 0xe8, 0xa5, 0x23, 0xa3, 0x6d }, - { 0x47, 0x1b }, - { 0xb6, 0x72, 0x04, 0x7e, 0x00, 0x3b, 0xb9, 0x52, - 0xdc, 0xa6, 0xcb, 0x8a, 0xf0, 0xe5, 0xb7, 0x79 }, - { 0xdf, 0x0c, 0x67, 0x86, 0x8f, 0xa2, 0x5f, 0x74, - 0x8b, 0x70, 0x44, 0xc6, 0xe7, 0xc2, 0x45, 0xb8 }, - { 0xeb, 0xd7, 0x03, 0x41, 0xbc, 0xd4, 0x15, 0xb0 }, - { 0x12, 0x35, 0x9f, 0x5d, 0x82, 0x22, 0x0c, 0x14 }, - { 0x47, 0x9d, 0xd2, 0x5c, 0x20, 0x79, 0x2d, 0x63 }, - { 0x66, 0x19, 0x5d, 0xbe, 0xd0, 0x31, 0x32, 0x74, - 0xc5, 0xca, 0x77, 0x66, 0x61, 0x5f, 0xa2, 0x5e }, - { 0x66, 0xbe, 0xc7, 0x07, 0xeb, 0x2a, 0xfc, 0x47, - 0x6d, 0x74, 0x08, 0xa8, 0xf2, 0x92, 0x7b, 0x36 }, - { 0xae, 0xfd, 0xaa, 0x5d, 0xdd, 0x99 }, - { 0x12, 0xec, 0x2b, 0x87, 0xfb, 0xb1 } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.19 Test Set 19 */ - { 0x51, 0x22, 0x25, 0x02, 0x14, 0xc3, 0x3e, 0x72, - 0x3a, 0x5d, 0xd5, 0x23, 0xfc, 0x14, 0x5f, 0xc0 }, - { 0x81, 0xe9, 0x2b, 0x6c, 0x0e, 0xe0, 0xe1, 0x2e, - 0xbc, 0xeb, 0xa8, 0xd9, 0x2a, 0x99, 0xdf, 0xa5 }, - { 0x16, 0xf3, 0xb3, 0xf7, 0x0f, 0xc2 }, - { 0xc3, 0xab }, - { 0xc9, 0xe8, 0x76, 0x32, 0x86, 0xb5, 0xb9, 0xff, - 0xbd, 0xf5, 0x6e, 0x12, 0x97, 0xd0, 0x88, 0x7b }, - { 0x98, 0x1d, 0x46, 0x4c, 0x7c, 0x52, 0xeb, 0x6e, - 0x50, 0x36, 0x23, 0x49, 0x84, 0xad, 0x0b, 0xcf }, - { 0x2a, 0x5c, 0x23, 0xd1, 0x5e, 0xe3, 0x51, 0xd5 }, - { 0x62, 0xda, 0xe3, 0x85, 0x3f, 0x3a, 0xf9, 0xd2 }, - { 0x28, 0xd7, 0xb0, 0xf2, 0xa2, 0xec, 0x3d, 0xe5 }, - { 0x53, 0x49, 0xfb, 0xe0, 0x98, 0x64, 0x9f, 0x94, - 0x8f, 0x5d, 0x2e, 0x97, 0x3a, 0x81, 0xc0, 0x0f }, - { 0x97, 0x44, 0x87, 0x1a, 0xd3, 0x2b, 0xf9, 0xbb, - 0xd1, 0xdd, 0x5c, 0xe5, 0x4e, 0x3e, 0x2e, 0x5a }, - { 0xad, 0xa1, 0x5a, 0xeb, 0x7b, 0xb8 }, - { 0xd4, 0x61, 0xbc, 0x15, 0x47, 0x5d } - }, { - /* 3GPP TS 35.208 v6.0.0 - 4.3.20 Test Set 20 */ - { 0x90, 0xdc, 0xa4, 0xed, 0xa4, 0x5b, 0x53, 0xcf, - 0x0f, 0x12, 0xd7, 0xc9, 0xc3, 0xbc, 0x6a, 0x89 }, - { 0x9f, 0xdd, 0xc7, 0x20, 0x92, 0xc6, 0xad, 0x03, - 0x6b, 0x6e, 0x46, 0x47, 0x89, 0x31, 0x5b, 0x78 }, - { 0x20, 0xf8, 0x13, 0xbd, 0x41, 0x41 }, - { 0x61, 0xdf }, - { 0x3f, 0xfc, 0xfe, 0x5b, 0x7b, 0x11, 0x11, 0x58, - 0x99, 0x20, 0xd3, 0x52, 0x8e, 0x84, 0xe6, 0x55 }, - { 0xcb, 0x9c, 0xcc, 0xc4, 0xb9, 0x25, 0x8e, 0x6d, - 0xca, 0x47, 0x60, 0x37, 0x9f, 0xb8, 0x25, 0x81 }, - { 0x09, 0xdb, 0x94, 0xea, 0xb4, 0xf8, 0x14, 0x9e }, - { 0xa2, 0x94, 0x68, 0xaa, 0x97, 0x75, 0xb5, 0x27 }, - { 0xa9, 0x51, 0x00, 0xe2, 0x76, 0x09, 0x52, 0xcd }, - { 0xb5, 0xf2, 0xda, 0x03, 0x88, 0x3b, 0x69, 0xf9, - 0x6b, 0xf5, 0x2e, 0x02, 0x9e, 0xd9, 0xac, 0x45 }, - { 0xb4, 0x72, 0x13, 0x68, 0xbc, 0x16, 0xea, 0x67, - 0x87, 0x5c, 0x55, 0x98, 0x68, 0x8b, 0xb0, 0xef }, - { 0x83, 0xcf, 0xd5, 0x4d, 0xb9, 0x13 }, - { 0x4f, 0x20, 0x39, 0x39, 0x2d, 0xdc } - } -}; - -#define NUM_TESTS (sizeof(test_sets) / sizeof(test_sets[0])) - - -int main(int argc, char *argv[]) -{ - u8 buf[16], buf2[16], buf3[16], buf4[16], buf5[16], opc[16]; - u8 auts[14], sqn[6], _rand[16]; - int ret = 0, res, i; - const struct milenage_test_set *t; - size_t res_len; - - wpa_debug_level = 0; - - printf("Milenage test sets\n"); - for (i = 0; i < NUM_TESTS; i++) { - t = &test_sets[i]; - printf("Test Set %d\n", i + 1); - - milenage_opc(t->op, t->k, opc); - if (memcmp(opc, t->opc, 16) != 0) { - printf("- milenage_opc failed\n"); - ret++; - } - - if (milenage_f1(opc, t->k, t->rand, t->sqn, t->amf, buf, buf2) - || memcmp(buf, t->f1, 8) != 0) { - printf("- milenage_f1 failed\n"); - ret++; - } - if (memcmp(buf2, t->f1star, 8) != 0) { - printf("- milenage_f1* failed\n"); - ret++; - } - - if (milenage_f2345(opc, t->k, t->rand, buf, buf2, buf3, buf4, - buf5) || - memcmp(buf, t->f2, 8) != 0) { - printf("- milenage_f2 failed\n"); - ret++; - } - if (memcmp(buf2, t->f3, 16) != 0) { - printf("- milenage_f3 failed\n"); - ret++; - } - if (memcmp(buf3, t->f4, 16) != 0) { - printf("- milenage_f4 failed\n"); - ret++; - } - if (memcmp(buf4, t->f5, 6) != 0) { - printf("- milenage_f5 failed\n"); - ret++; - } - if (memcmp(buf5, t->f5star, 6) != 0) { - printf("- milenage_f5* failed\n"); - ret++; - } - } - - printf("milenage_auts test:\n"); - os_memcpy(auts, "\x4f\x20\x39\x39\x2d\xdd", 6); - os_memcpy(auts + 6, "\x4b\xb4\x31\x6e\xd4\xa1\x46\x88", 8); - res = milenage_auts(t->opc, t->k, t->rand, auts, buf); - printf("AUTS for test set %d: %d / SQN=%02x%02x%02x%02x%02x%02x\n", - i, res, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); - if (res) - ret++; - - os_memset(_rand, 0xaa, sizeof(_rand)); - os_memcpy(auts, - "\x43\x68\x1a\xd3\xda\xf0\x06\xbc\xde\x40\x5a\x20\x72\x67", - 14); - res = milenage_auts(t->opc, t->k, _rand, auts, buf); - printf("AUTS from a test USIM: %d / SQN=%02x%02x%02x%02x%02x%02x\n", - res, buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); - if (res) - ret++; - - printf("milenage_generate test:\n"); - os_memcpy(sqn, "\x00\x00\x00\x00\x40\x44", 6); - os_memcpy(_rand, "\x12\x69\xb8\x23\x41\x39\x35\x66\xfb\x99\x41\xe9\x84" - "\x4f\xe6\x2f", 16); - res_len = 8; - milenage_generate(t->opc, t->amf, t->k, sqn, _rand, buf, buf2, buf3, - buf4, &res_len); - wpa_hexdump(MSG_DEBUG, "SQN", sqn, 6); - wpa_hexdump(MSG_DEBUG, "RAND", _rand, 16); - wpa_hexdump(MSG_DEBUG, "AUTN", buf, 16); - wpa_hexdump(MSG_DEBUG, "IK", buf2, 16); - wpa_hexdump(MSG_DEBUG, "CK", buf3, 16); - wpa_hexdump(MSG_DEBUG, "RES", buf4, res_len); - - printf("GSM-Milenage test sets\n"); - for (i = 0; i < NUM_GSM_TESTS; i++) { - const struct gsm_milenage_test_set *g; - u8 sres[4], kc[8]; - g = &gsm_test_sets[i]; - printf("Test Set %d\n", i + 1); - gsm_milenage(g->opc, g->ki, g->rand, sres, kc); - if (memcmp(g->kc, kc, 8) != 0) { - printf("- gsm_milenage Kc failed\n"); - ret++; - } -#ifdef GSM_MILENAGE_ALT_SRES - if (memcmp(g->sres2, sres, 4) != 0) { - printf("- gsm_milenage SRES#2 failed\n"); - ret++; - } -#else /* GSM_MILENAGE_ALT_SRES */ - if (memcmp(g->sres1, sres, 4) != 0) { - printf("- gsm_milenage SRES#1 failed\n"); - ret++; - } -#endif /* GSM_MILENAGE_ALT_SRES */ - } - - if (ret) - printf("Something failed\n"); - else - printf("OK\n"); - - return ret; -} -#endif /* TEST_MAIN_MILENAGE */ diff --git a/contrib/wpa/src/l2_packet/Makefile b/contrib/wpa/src/l2_packet/Makefile index cffba62..9c41962 100644 --- a/contrib/wpa/src/l2_packet/Makefile +++ b/contrib/wpa/src/l2_packet/Makefile @@ -2,7 +2,6 @@ all: @echo Nothing to be made. clean: - for d in $(SUBDIRS); do make -C $$d clean; done rm -f *~ *.o *.d install: diff --git a/contrib/wpa/src/l2_packet/l2_packet_freebsd.c b/contrib/wpa/src/l2_packet/l2_packet_freebsd.c index d1034aa..009e02c 100644 --- a/contrib/wpa/src/l2_packet/l2_packet_freebsd.c +++ b/contrib/wpa/src/l2_packet/l2_packet_freebsd.c @@ -14,7 +14,7 @@ */ #include "includes.h" -#ifdef __APPLE__ +#if defined(__APPLE__) || defined(__GLIBC__) #include <net/bpf.h> #endif /* __APPLE__ */ #include <pcap.h> diff --git a/contrib/wpa/src/l2_packet/l2_packet_ndis.c b/contrib/wpa/src/l2_packet/l2_packet_ndis.c index 7de5880..6ce29aa 100644 --- a/contrib/wpa/src/l2_packet/l2_packet_ndis.c +++ b/contrib/wpa/src/l2_packet/l2_packet_ndis.c @@ -137,11 +137,17 @@ int l2_packet_send(struct l2_packet_data *l2, const u8 *dst_addr, u16 proto, DWORD err = GetLastError(); #ifndef _WIN32_WCE if (err == ERROR_IO_PENDING) { - /* For now, just assume that the packet will be sent in - * time before the next write happens. This could be - * cleaned up at some point to actually wait for - * completion before starting new writes. - */ + wpa_printf(MSG_DEBUG, "L2(NDISUIO): Wait for pending " + "write to complete"); + res = GetOverlappedResult( + driver_ndis_get_ndisuio_handle(), &overlapped, + &written, TRUE); + if (!res) { + wpa_printf(MSG_DEBUG, "L2(NDISUIO): " + "GetOverlappedResult failed: %d", + (int) GetLastError()); + return -1; + } return 0; } #endif /* _WIN32_WCE */ diff --git a/contrib/wpa/src/lib.rules b/contrib/wpa/src/lib.rules new file mode 100644 index 0000000..b260d25 --- /dev/null +++ b/contrib/wpa/src/lib.rules @@ -0,0 +1,21 @@ +ifndef CC +CC=gcc +endif + +ifndef CFLAGS +CFLAGS = -MMD -O2 -Wall -g +endif + +CFLAGS += -I.. -I../utils + + +Q=@ +E=echo +ifeq ($(V), 1) +Q= +E=true +endif + +%.o: %.c + $(Q)$(CC) -c -o $@ $(CFLAGS) $< + @$(E) " CC " $< diff --git a/contrib/wpa/src/radius/.gitignore b/contrib/wpa/src/radius/.gitignore new file mode 100644 index 0000000..a89a1f9 --- /dev/null +++ b/contrib/wpa/src/radius/.gitignore @@ -0,0 +1 @@ +libradius.a diff --git a/contrib/wpa/src/radius/Makefile b/contrib/wpa/src/radius/Makefile index cffba62..b199be8 100644 --- a/contrib/wpa/src/radius/Makefile +++ b/contrib/wpa/src/radius/Makefile @@ -1,9 +1,22 @@ -all: - @echo Nothing to be made. +all: libradius.a clean: - for d in $(SUBDIRS); do make -C $$d clean; done - rm -f *~ *.o *.d + rm -f *~ *.o *.d libradius.a install: @echo Nothing to be made. + + +include ../lib.rules + +CFLAGS += -DCONFIG_IPV6 + +LIB_OBJS= \ + radius.o \ + radius_client.o \ + radius_server.o + +libradius.a: $(LIB_OBJS) + $(AR) crT $@ $? + +-include $(OBJS:%.o=%.d) diff --git a/contrib/wpa/src/radius/radius.c b/contrib/wpa/src/radius/radius.c index 71bbfb5..70754ef 100644 --- a/contrib/wpa/src/radius/radius.c +++ b/contrib/wpa/src/radius/radius.c @@ -1,6 +1,6 @@ /* - * hostapd / RADIUS message processing - * Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi> + * RADIUS message processing + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -12,62 +12,82 @@ * See README and COPYING for more details. */ -#include "includes.h" +#include "utils/includes.h" -#include "common.h" +#include "utils/common.h" +#include "utils/wpabuf.h" +#include "crypto/md5.h" +#include "crypto/crypto.h" #include "radius.h" -#include "md5.h" -#include "crypto.h" -static struct radius_attr_hdr * -radius_get_attr_hdr(struct radius_msg *msg, int idx) -{ - return (struct radius_attr_hdr *) (msg->buf + msg->attr_pos[idx]); -} +/** + * struct radius_msg - RADIUS message structure for new and parsed messages + */ +struct radius_msg { + /** + * buf - Allocated buffer for RADIUS message + */ + struct wpabuf *buf; + + /** + * hdr - Pointer to the RADIUS header in buf + */ + struct radius_hdr *hdr; + /** + * attr_pos - Array of indexes to attributes + * + * The values are number of bytes from buf to the beginning of + * struct radius_attr_hdr. + */ + size_t *attr_pos; + + /** + * attr_size - Total size of the attribute pointer array + */ + size_t attr_size; + + /** + * attr_used - Total number of attributes in the array + */ + size_t attr_used; +}; -struct radius_msg *radius_msg_new(u8 code, u8 identifier) + +struct radius_hdr * radius_msg_get_hdr(struct radius_msg *msg) { - struct radius_msg *msg; + return msg->hdr; +} - msg = os_malloc(sizeof(*msg)); - if (msg == NULL) - return NULL; - if (radius_msg_initialize(msg, RADIUS_DEFAULT_MSG_SIZE)) { - os_free(msg); - return NULL; - } +struct wpabuf * radius_msg_get_buf(struct radius_msg *msg) +{ + return msg->buf; +} - radius_msg_set_hdr(msg, code, identifier); - return msg; +static struct radius_attr_hdr * +radius_get_attr_hdr(struct radius_msg *msg, int idx) +{ + return (struct radius_attr_hdr *) + (wpabuf_mhead_u8(msg->buf) + msg->attr_pos[idx]); } -int radius_msg_initialize(struct radius_msg *msg, size_t init_len) +static void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier) { - if (msg == NULL || init_len < sizeof(struct radius_hdr)) - return -1; - - os_memset(msg, 0, sizeof(*msg)); - msg->buf = os_zalloc(init_len); - if (msg->buf == NULL) - return -1; + msg->hdr->code = code; + msg->hdr->identifier = identifier; +} - msg->buf_size = init_len; - msg->hdr = (struct radius_hdr *) msg->buf; - msg->buf_used = sizeof(*msg->hdr); +static int radius_msg_initialize(struct radius_msg *msg) +{ msg->attr_pos = os_zalloc(RADIUS_DEFAULT_ATTR_COUNT * sizeof(*msg->attr_pos)); - if (msg->attr_pos == NULL) { - os_free(msg->buf); - msg->buf = NULL; - msg->hdr = NULL; + if (msg->attr_pos == NULL) return -1; - } msg->attr_size = RADIUS_DEFAULT_ATTR_COUNT; msg->attr_used = 0; @@ -76,23 +96,48 @@ int radius_msg_initialize(struct radius_msg *msg, size_t init_len) } -void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier) +/** + * radius_msg_new - Create a new RADIUS message + * @code: Code for RADIUS header + * @identifier: Identifier for RADIUS header + * Returns: Context for RADIUS message or %NULL on failure + * + * The caller is responsible for freeing the returned data with + * radius_msg_free(). + */ +struct radius_msg * radius_msg_new(u8 code, u8 identifier) { - msg->hdr->code = code; - msg->hdr->identifier = identifier; + struct radius_msg *msg; + + msg = os_zalloc(sizeof(*msg)); + if (msg == NULL) + return NULL; + + msg->buf = wpabuf_alloc(RADIUS_DEFAULT_MSG_SIZE); + if (msg->buf == NULL || radius_msg_initialize(msg)) { + radius_msg_free(msg); + return NULL; + } + msg->hdr = wpabuf_put(msg->buf, sizeof(struct radius_hdr)); + + radius_msg_set_hdr(msg, code, identifier); + + return msg; } +/** + * radius_msg_free - Free a RADIUS message + * @msg: RADIUS message from radius_msg_new() or radius_msg_parse() + */ void radius_msg_free(struct radius_msg *msg) { - os_free(msg->buf); - msg->buf = NULL; - msg->hdr = NULL; - msg->buf_size = msg->buf_used = 0; + if (msg == NULL) + return; + wpabuf_free(msg->buf); os_free(msg->attr_pos); - msg->attr_pos = NULL; - msg->attr_size = msg->attr_used = 0; + os_free(msg); } @@ -305,19 +350,19 @@ int radius_msg_finish(struct radius_msg *msg, const u8 *secret, RADIUS_ATTR_MESSAGE_AUTHENTICATOR, auth, MD5_MAC_LEN); if (attr == NULL) { - printf("WARNING: Could not add " - "Message-Authenticator\n"); + wpa_printf(MSG_WARNING, "RADIUS: Could not add " + "Message-Authenticator"); return -1; } - msg->hdr->length = htons(msg->buf_used); - hmac_md5(secret, secret_len, msg->buf, msg->buf_used, - (u8 *) (attr + 1)); + msg->hdr->length = htons(wpabuf_len(msg->buf)); + hmac_md5(secret, secret_len, wpabuf_head(msg->buf), + wpabuf_len(msg->buf), (u8 *) (attr + 1)); } else - msg->hdr->length = htons(msg->buf_used); + msg->hdr->length = htons(wpabuf_len(msg->buf)); - if (msg->buf_used > 0xffff) { - printf("WARNING: too long RADIUS message (%lu)\n", - (unsigned long) msg->buf_used); + if (wpabuf_len(msg->buf) > 0xffff) { + wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)", + (unsigned long) wpabuf_len(msg->buf)); return -1; } return 0; @@ -339,26 +384,26 @@ int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, printf("WARNING: Could not add Message-Authenticator\n"); return -1; } - msg->hdr->length = htons(msg->buf_used); + msg->hdr->length = htons(wpabuf_len(msg->buf)); os_memcpy(msg->hdr->authenticator, req_authenticator, sizeof(msg->hdr->authenticator)); - hmac_md5(secret, secret_len, msg->buf, msg->buf_used, - (u8 *) (attr + 1)); + hmac_md5(secret, secret_len, wpabuf_head(msg->buf), + wpabuf_len(msg->buf), (u8 *) (attr + 1)); /* ResponseAuth = MD5(Code+ID+Length+RequestAuth+Attributes+Secret) */ 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[2] = wpabuf_head_u8(msg->buf) + sizeof(struct radius_hdr); + len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_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", - (unsigned long) msg->buf_used); + if (wpabuf_len(msg->buf) > 0xffff) { + wpa_printf(MSG_WARNING, "RADIUS: Too long message (%lu)", + (unsigned long) wpabuf_len(msg->buf)); return -1; } return 0; @@ -371,17 +416,17 @@ void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret, const u8 *addr[2]; size_t len[2]; - msg->hdr->length = htons(msg->buf_used); + msg->hdr->length = htons(wpabuf_len(msg->buf)); os_memset(msg->hdr->authenticator, 0, MD5_MAC_LEN); - addr[0] = msg->buf; - len[0] = msg->buf_used; + addr[0] = wpabuf_head(msg->buf); + len[0] = wpabuf_len(msg->buf); 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", - (unsigned long) msg->buf_used); + if (wpabuf_len(msg->buf) > 0xffff) { + wpa_printf(MSG_WARNING, "RADIUS: Too long messages (%lu)", + (unsigned long) wpabuf_len(msg->buf)); } } @@ -402,7 +447,8 @@ static int radius_msg_add_attr_to_array(struct radius_msg *msg, msg->attr_size = nlen; } - msg->attr_pos[msg->attr_used++] = (unsigned char *) attr - msg->buf; + msg->attr_pos[msg->attr_used++] = + (unsigned char *) attr - wpabuf_head_u8(msg->buf); return 0; } @@ -420,31 +466,19 @@ struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type, return NULL; } - buf_needed = msg->buf_used + sizeof(*attr) + data_len; + buf_needed = sizeof(*attr) + data_len; - if (msg->buf_size < buf_needed) { + if (wpabuf_tailroom(msg->buf) < buf_needed) { /* allocate more space for message buffer */ - unsigned char *nbuf; - size_t nlen = msg->buf_size; - - while (nlen < buf_needed) - nlen *= 2; - nbuf = os_realloc(msg->buf, nlen); - if (nbuf == NULL) + if (wpabuf_resize(&msg->buf, buf_needed) < 0) return NULL; - msg->buf = nbuf; - msg->hdr = (struct radius_hdr *) msg->buf; - os_memset(msg->buf + msg->buf_size, 0, nlen - msg->buf_size); - msg->buf_size = nlen; + msg->hdr = wpabuf_mhead(msg->buf); } - attr = (struct radius_attr_hdr *) (msg->buf + msg->buf_used); + attr = wpabuf_put(msg->buf, sizeof(struct radius_attr_hdr)); attr->type = type; attr->length = sizeof(*attr) + data_len; - if (data_len > 0) - os_memcpy(attr + 1, data, data_len); - - msg->buf_used += sizeof(*attr) + data_len; + wpabuf_put_data(msg->buf, data, data_len); if (radius_msg_add_attr_to_array(msg, attr)) return NULL; @@ -453,7 +487,16 @@ struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type, } -struct radius_msg *radius_msg_parse(const u8 *data, size_t len) +/** + * radius_msg_parse - Parse a RADIUS message + * @data: RADIUS message to be parsed + * @len: Length of data buffer in octets + * Returns: Parsed RADIUS message or %NULL on failure + * + * This parses a RADIUS message and makes a copy of its data. The caller is + * responsible for freeing the returned data with radius_msg_free(). + */ +struct radius_msg * radius_msg_parse(const u8 *data, size_t len) { struct radius_msg *msg; struct radius_hdr *hdr; @@ -468,30 +511,29 @@ struct radius_msg *radius_msg_parse(const u8 *data, size_t len) msg_len = ntohs(hdr->length); if (msg_len < sizeof(*hdr) || msg_len > len) { - printf("Invalid RADIUS message length\n"); + wpa_printf(MSG_INFO, "RADIUS: Invalid message length"); return NULL; } if (msg_len < len) { - printf("Ignored %lu extra bytes after RADIUS message\n", - (unsigned long) len - msg_len); + wpa_printf(MSG_DEBUG, "RADIUS: Ignored %lu extra bytes after " + "RADIUS message", (unsigned long) len - msg_len); } - msg = os_malloc(sizeof(*msg)); + msg = os_zalloc(sizeof(*msg)); if (msg == NULL) return NULL; - if (radius_msg_initialize(msg, msg_len)) { - os_free(msg); + msg->buf = wpabuf_alloc_copy(data, msg_len); + if (msg->buf == NULL || radius_msg_initialize(msg)) { + radius_msg_free(msg); return NULL; } - - os_memcpy(msg->buf, data, msg_len); - msg->buf_size = msg->buf_used = msg_len; + msg->hdr = wpabuf_mhead(msg->buf); /* parse attributes */ - pos = (unsigned char *) (msg->hdr + 1); - end = msg->buf + msg->buf_used; + pos = wpabuf_mhead_u8(msg->buf) + sizeof(struct radius_hdr); + end = wpabuf_mhead_u8(msg->buf) + wpabuf_len(msg->buf); while (pos < end) { if ((size_t) (end - pos) < sizeof(*attr)) goto fail; @@ -513,7 +555,6 @@ struct radius_msg *radius_msg_parse(const u8 *data, size_t len) fail: radius_msg_free(msg); - os_free(msg); return NULL; } @@ -615,7 +656,8 @@ int radius_msg_verify_msg_auth(struct radius_msg *msg, const u8 *secret, os_memcpy(msg->hdr->authenticator, req_auth, sizeof(msg->hdr->authenticator)); } - hmac_md5(secret, secret_len, msg->buf, msg->buf_used, auth); + hmac_md5(secret, secret_len, wpabuf_head(msg->buf), + wpabuf_len(msg->buf), auth); os_memcpy(attr + 1, orig, MD5_MAC_LEN); if (req_auth) { os_memcpy(msg->hdr->authenticator, orig_authenticator, @@ -654,8 +696,8 @@ int radius_msg_verify(struct radius_msg *msg, const u8 *secret, 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[2] = wpabuf_head_u8(msg->buf) + sizeof(struct radius_hdr); + len[2] = wpabuf_len(msg->buf) - sizeof(struct radius_hdr); addr[3] = secret; len[3] = secret_len; md5_vector(4, addr, len, hash); @@ -1232,3 +1274,44 @@ int radius_msg_get_vlanid(struct radius_msg *msg) return -1; } + + +void radius_free_class(struct radius_class_data *c) +{ + size_t i; + if (c == NULL) + return; + for (i = 0; i < c->count; i++) + os_free(c->attr[i].data); + os_free(c->attr); + c->attr = NULL; + c->count = 0; +} + + +int radius_copy_class(struct radius_class_data *dst, + const struct radius_class_data *src) +{ + size_t i; + + if (src->attr == NULL) + return 0; + + dst->attr = os_zalloc(src->count * sizeof(struct radius_attr_data)); + if (dst->attr == NULL) + return -1; + + dst->count = 0; + + for (i = 0; i < src->count; i++) { + dst->attr[i].data = os_malloc(src->attr[i].len); + if (dst->attr[i].data == NULL) + break; + dst->count++; + os_memcpy(dst->attr[i].data, src->attr[i].data, + src->attr[i].len); + dst->attr[i].len = src->attr[i].len; + } + + return 0; +} diff --git a/contrib/wpa/src/radius/radius.h b/contrib/wpa/src/radius/radius.h index c30621d..a3cdac0 100644 --- a/contrib/wpa/src/radius/radius.h +++ b/contrib/wpa/src/radius/radius.h @@ -1,6 +1,6 @@ /* - * hostapd / RADIUS message processing - * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi> + * RADIUS message processing + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -173,21 +173,7 @@ struct radius_ms_mppe_keys { }; -/* RADIUS message structure for new and parsed messages */ -struct radius_msg { - unsigned char *buf; - size_t buf_size; /* total size allocated for buf */ - size_t buf_used; /* bytes used in buf */ - - struct radius_hdr *hdr; - - size_t *attr_pos; /* array of indexes to attributes (number of bytes - * from buf to the beginning of - * struct radius_attr_hdr). */ - size_t attr_size; /* total size of the attribute pointer array */ - size_t attr_used; /* total number of attributes in the array */ -}; - +struct radius_msg; /* Default size to be allocated for new RADIUS messages */ #define RADIUS_DEFAULT_MSG_SIZE 1024 @@ -202,9 +188,9 @@ struct radius_msg { /* MAC address ASCII format for non-802.1X use */ #define RADIUS_ADDR_FORMAT "%02x%02x%02x%02x%02x%02x" -struct radius_msg *radius_msg_new(u8 code, u8 identifier); -int radius_msg_initialize(struct radius_msg *msg, size_t init_len); -void radius_msg_set_hdr(struct radius_msg *msg, u8 code, u8 identifier); +struct radius_hdr * radius_msg_get_hdr(struct radius_msg *msg); +struct wpabuf * radius_msg_get_buf(struct radius_msg *msg); +struct radius_msg * radius_msg_new(u8 code, u8 identifier); void radius_msg_free(struct radius_msg *msg); void radius_msg_dump(struct radius_msg *msg); int radius_msg_finish(struct radius_msg *msg, const u8 *secret, @@ -213,9 +199,9 @@ int radius_msg_finish_srv(struct radius_msg *msg, const u8 *secret, size_t secret_len, const u8 *req_authenticator); void radius_msg_finish_acct(struct radius_msg *msg, const u8 *secret, size_t secret_len); -struct radius_attr_hdr *radius_msg_add_attr(struct radius_msg *msg, u8 type, - const u8 *data, size_t data_len); -struct radius_msg *radius_msg_parse(const u8 *data, size_t len); +struct radius_attr_hdr * radius_msg_add_attr(struct radius_msg *msg, u8 type, + 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, const u8 *data, size_t data_len); u8 *radius_msg_get_eap(struct radius_msg *msg, size_t *len); @@ -269,4 +255,19 @@ int radius_msg_get_attr_ptr(struct radius_msg *msg, u8 type, u8 **buf, size_t *len, const u8 *start); int radius_msg_count_attr(struct radius_msg *msg, u8 type, int min_len); + +struct radius_attr_data { + u8 *data; + size_t len; +}; + +struct radius_class_data { + struct radius_attr_data *attr; + size_t count; +}; + +void radius_free_class(struct radius_class_data *c); +int radius_copy_class(struct radius_class_data *dst, + const struct radius_class_data *src); + #endif /* RADIUS_H */ diff --git a/contrib/wpa/src/radius/radius_client.c b/contrib/wpa/src/radius/radius_client.c index 826acad..171af29 100644 --- a/contrib/wpa/src/radius/radius_client.c +++ b/contrib/wpa/src/radius/radius_client.c @@ -1,6 +1,6 @@ /* - * hostapd / RADIUS client - * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi> + * RADIUS client + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -20,68 +20,217 @@ #include "eloop.h" /* Defaults for RADIUS retransmit values (exponential backoff) */ -#define RADIUS_CLIENT_FIRST_WAIT 3 /* seconds */ -#define RADIUS_CLIENT_MAX_WAIT 120 /* seconds */ -#define RADIUS_CLIENT_MAX_RETRIES 10 /* maximum number of retransmit attempts - * before entry is removed from retransmit - * list */ -#define RADIUS_CLIENT_MAX_ENTRIES 30 /* maximum number of entries in retransmit - * list (oldest will be removed, if this - * limit is exceeded) */ -#define RADIUS_CLIENT_NUM_FAILOVER 4 /* try to change RADIUS server after this - * many failed retry attempts */ +/** + * RADIUS_CLIENT_FIRST_WAIT - RADIUS client timeout for first retry in seconds + */ +#define RADIUS_CLIENT_FIRST_WAIT 3 + +/** + * RADIUS_CLIENT_MAX_WAIT - RADIUS client maximum retry timeout in seconds + */ +#define RADIUS_CLIENT_MAX_WAIT 120 + +/** + * RADIUS_CLIENT_MAX_RETRIES - RADIUS client maximum retries + * + * Maximum number of retransmit attempts before the entry is removed from + * retransmit list. + */ +#define RADIUS_CLIENT_MAX_RETRIES 10 + +/** + * RADIUS_CLIENT_MAX_ENTRIES - RADIUS client maximum pending messages + * + * Maximum number of entries in retransmit list (oldest entries will be + * removed, if this limit is exceeded). + */ +#define RADIUS_CLIENT_MAX_ENTRIES 30 +/** + * RADIUS_CLIENT_NUM_FAILOVER - RADIUS client failover point + * + * The number of failed retry attempts after which the RADIUS server will be + * changed (if one of more backup servers are configured). + */ +#define RADIUS_CLIENT_NUM_FAILOVER 4 + + +/** + * struct radius_rx_handler - RADIUS client RX handler + * + * This data structure is used internally inside the RADIUS client module to + * store registered RX handlers. These handlers are registered by calls to + * radius_client_register() and unregistered when the RADIUS client is + * deinitialized with a call to radius_client_deinit(). + */ struct radius_rx_handler { + /** + * handler - Received RADIUS message handler + */ RadiusRxResult (*handler)(struct radius_msg *msg, struct radius_msg *req, const u8 *shared_secret, size_t shared_secret_len, void *data); + + /** + * data - Context data for the handler + */ void *data; }; -/* RADIUS message retransmit list */ +/** + * struct radius_msg_list - RADIUS client message retransmit list + * + * This data structure is used internally inside the RADIUS client module to + * store pending RADIUS requests that may still need to be retransmitted. + */ struct radius_msg_list { - u8 addr[ETH_ALEN]; /* STA/client address; used to find RADIUS messages - * for the same STA. */ + /** + * addr - STA/client address + * + * This is used to find RADIUS messages for the same STA. + */ + u8 addr[ETH_ALEN]; + + /** + * msg - RADIUS message + */ struct radius_msg *msg; + + /** + * msg_type - Message type + */ RadiusType msg_type; + + /** + * first_try - Time of the first transmission attempt + */ os_time_t first_try; + + /** + * next_try - Time for the next transmission attempt + */ os_time_t next_try; + + /** + * attempts - Number of transmission attempts + */ int attempts; + + /** + * next_wait - Next retransmission wait time in seconds + */ int next_wait; + + /** + * last_attempt - Time of the last transmission attempt + */ struct os_time last_attempt; - u8 *shared_secret; + /** + * shared_secret - Shared secret with the target RADIUS server + */ + const u8 *shared_secret; + + /** + * shared_secret_len - shared_secret length in octets + */ size_t shared_secret_len; /* TODO: server config with failover to backup server(s) */ + /** + * next - Next message in the list + */ struct radius_msg_list *next; }; +/** + * struct radius_client_data - Internal RADIUS client data + * + * This data structure is used internally inside the RADIUS client module. + * External users allocate this by calling radius_client_init() and free it by + * calling radius_client_deinit(). The pointer to this opaque data is used in + * calls to other functions as an identifier for the RADIUS client instance. + */ struct radius_client_data { + /** + * ctx - Context pointer for hostapd_logger() callbacks + */ void *ctx; + + /** + * conf - RADIUS client configuration (list of RADIUS servers to use) + */ struct hostapd_radius_servers *conf; - int auth_serv_sock; /* socket for authentication RADIUS messages */ - int acct_serv_sock; /* socket for accounting RADIUS messages */ + /** + * auth_serv_sock - IPv4 socket for RADIUS authentication messages + */ + int auth_serv_sock; + + /** + * acct_serv_sock - IPv4 socket for RADIUS accounting messages + */ + int acct_serv_sock; + + /** + * auth_serv_sock6 - IPv6 socket for RADIUS authentication messages + */ int auth_serv_sock6; + + /** + * acct_serv_sock6 - IPv6 socket for RADIUS accounting messages + */ int acct_serv_sock6; - int auth_sock; /* currently used socket */ - int acct_sock; /* currently used socket */ + /** + * auth_sock - Currently used socket for RADIUS authentication server + */ + int auth_sock; + + /** + * acct_sock - Currently used socket for RADIUS accounting server + */ + int acct_sock; + + /** + * auth_handlers - Authentication message handlers + */ struct radius_rx_handler *auth_handlers; + + /** + * num_auth_handlers - Number of handlers in auth_handlers + */ size_t num_auth_handlers; + + /** + * acct_handlers - Accounting message handlers + */ struct radius_rx_handler *acct_handlers; + + /** + * num_acct_handlers - Number of handlers in acct_handlers + */ size_t num_acct_handlers; + /** + * msgs - Pending outgoing RADIUS messages + */ struct radius_msg_list *msgs; + + /** + * num_msgs - Number of pending messages in the msgs list + */ size_t num_msgs; + /** + * next_radius_identifier - Next RADIUS message identifier to use + */ u8 next_radius_identifier; }; @@ -98,11 +247,26 @@ static int radius_client_init_auth(struct radius_client_data *radius); static void radius_client_msg_free(struct radius_msg_list *req) { radius_msg_free(req->msg); - os_free(req->msg); os_free(req); } +/** + * radius_client_register - Register a RADIUS client RX handler + * @radius: RADIUS client context from radius_client_init() + * @msg_type: RADIUS client type (RADIUS_AUTH or RADIUS_ACCT) + * @handler: Handler for received RADIUS messages + * @data: Context pointer for handler callbacks + * Returns: 0 on success, -1 on failure + * + * This function is used to register a handler for processing received RADIUS + * authentication and accounting messages. The handler() callback function will + * be called whenever a RADIUS message is received from the active server. + * + * There can be multiple registered RADIUS message handlers. The handlers will + * be called in order until one of them indicates that it has processed or + * queued the message. + */ int radius_client_register(struct radius_client_data *radius, RadiusType msg_type, RadiusRxResult (*handler)(struct radius_msg *msg, @@ -166,6 +330,7 @@ static int radius_client_retransmit(struct radius_client_data *radius, { struct hostapd_radius_servers *conf = radius->conf; int s; + struct wpabuf *buf; if (entry->msg_type == RADIUS_ACCT || entry->msg_type == RADIUS_ACCT_INTERIM) { @@ -190,10 +355,11 @@ static int radius_client_retransmit(struct radius_client_data *radius, entry->attempts++; hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)", - entry->msg->hdr->identifier); + radius_msg_get_hdr(entry->msg)->identifier); os_get_time(&entry->last_attempt); - if (send(s, entry->msg->buf, entry->msg->buf_used, 0) < 0) + buf = radius_msg_get_buf(entry->msg); + if (send(s, wpabuf_head(buf), wpabuf_len(buf), 0) < 0) radius_client_handle_send_error(radius, s, entry->msg_type); entry->next_try = now + entry->next_wait; @@ -351,7 +517,8 @@ static void radius_client_update_timeout(struct radius_client_data *radius) static void radius_client_list_add(struct radius_client_data *radius, struct radius_msg *msg, - RadiusType msg_type, u8 *shared_secret, + RadiusType msg_type, + const u8 *shared_secret, size_t shared_secret_len, const u8 *addr) { struct radius_msg_list *entry, *prev; @@ -360,7 +527,6 @@ static void radius_client_list_add(struct radius_client_data *radius, /* No point in adding entries to retransmit queue since event * loop has already been terminated. */ radius_msg_free(msg); - os_free(msg); return; } @@ -368,7 +534,6 @@ static void radius_client_list_add(struct radius_client_data *radius, if (entry == NULL) { printf("Failed to add RADIUS packet into retransmit list\n"); radius_msg_free(msg); - os_free(msg); return; } @@ -437,15 +602,38 @@ static void radius_client_list_del(struct radius_client_data *radius, } +/** + * radius_client_send - Send a RADIUS request + * @radius: RADIUS client context from radius_client_init() + * @msg: RADIUS message to be sent + * @msg_type: Message type (RADIUS_AUTH, RADIUS_ACCT, RADIUS_ACCT_INTERIM) + * @addr: MAC address of the device related to this message or %NULL + * Returns: 0 on success, -1 on failure + * + * This function is used to transmit a RADIUS authentication (RADIUS_AUTH) or + * accounting request (RADIUS_ACCT or RADIUS_ACCT_INTERIM). The only difference + * between accounting and interim accounting messages is that the interim + * message will override any pending interim accounting updates while a new + * accounting message does not remove any pending messages. + * + * The message is added on the retransmission queue and will be retransmitted + * automatically until a response is received or maximum number of retries + * (RADIUS_CLIENT_MAX_RETRIES) is reached. + * + * The related device MAC address can be used to identify pending messages that + * can be removed with radius_client_flush_auth() or with interim accounting + * updates. + */ int radius_client_send(struct radius_client_data *radius, struct radius_msg *msg, RadiusType msg_type, const u8 *addr) { struct hostapd_radius_servers *conf = radius->conf; - u8 *shared_secret; + const u8 *shared_secret; size_t shared_secret_len; char *name; int s, res; + struct wpabuf *buf; if (msg_type == RADIUS_ACCT_INTERIM) { /* Remove any pending interim acct update for the same STA. */ @@ -488,7 +676,8 @@ int radius_client_send(struct radius_client_data *radius, if (conf->msg_dumps) radius_msg_dump(msg); - res = send(s, msg->buf, msg->buf_used, 0); + buf = radius_msg_get_buf(msg); + res = send(s, wpabuf_head(buf), wpabuf_len(buf), 0); if (res < 0) radius_client_handle_send_error(radius, s, msg_type); @@ -507,6 +696,7 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) int len, roundtrip; unsigned char buf[3000]; struct radius_msg *msg; + struct radius_hdr *hdr; struct radius_rx_handler *handlers; size_t num_handlers, i; struct radius_msg_list *req, *prev_req; @@ -544,13 +734,14 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) rconf->malformed_responses++; return; } + hdr = radius_msg_get_hdr(msg); 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) { + switch (hdr->code) { case RADIUS_CODE_ACCESS_ACCEPT: rconf->access_accepts++; break; @@ -573,7 +764,8 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) if ((req->msg_type == msg_type || (req->msg_type == RADIUS_ACCT_INTERIM && msg_type == RADIUS_ACCT)) && - req->msg->hdr->identifier == msg->hdr->identifier) + radius_msg_get_hdr(req->msg)->identifier == + hdr->identifier) break; prev_req = req; @@ -585,7 +777,7 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) HOSTAPD_LEVEL_DEBUG, "No matching RADIUS request found (type=%d " "id=%d) - dropping packet", - msg_type, msg->hdr->identifier); + msg_type, hdr->identifier); goto fail; } @@ -614,7 +806,6 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) switch (res) { case RADIUS_RX_PROCESSED: radius_msg_free(msg); - os_free(msg); /* continue */ case RADIUS_RX_QUEUED: radius_client_msg_free(req); @@ -635,17 +826,24 @@ static void radius_client_receive(int sock, void *eloop_ctx, void *sock_ctx) 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, + msg_type, hdr->code, hdr->identifier, invalid_authenticator ? " [INVALID AUTHENTICATOR]" : ""); radius_client_msg_free(req); fail: radius_msg_free(msg); - os_free(msg); } +/** + * radius_client_get_id - Get an identifier for a new RADIUS message + * @radius: RADIUS client context from radius_client_init() + * Returns: Allocated identifier + * + * This function is used to fetch a unique (among pending requests) identifier + * for a new RADIUS message. + */ u8 radius_client_get_id(struct radius_client_data *radius) { struct radius_msg_list *entry, *prev, *_remove; @@ -656,7 +854,7 @@ u8 radius_client_get_id(struct radius_client_data *radius) entry = radius->msgs; prev = NULL; while (entry) { - if (entry->msg->hdr->identifier == id) { + if (radius_msg_get_hdr(entry->msg)->identifier == id) { hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS, HOSTAPD_LEVEL_DEBUG, @@ -681,6 +879,11 @@ u8 radius_client_get_id(struct radius_client_data *radius) } +/** + * radius_client_flush - Flush all pending RADIUS client messages + * @radius: RADIUS client context from radius_client_init() + * @only_auth: Whether only authentication messages are removed + */ void radius_client_flush(struct radius_client_data *radius, int only_auth) { struct radius_msg_list *entry, *prev, *tmp; @@ -714,7 +917,7 @@ void radius_client_flush(struct radius_client_data *radius, int only_auth) static void radius_client_update_acct_msgs(struct radius_client_data *radius, - u8 *shared_secret, + const u8 *shared_secret, size_t shared_secret_len) { struct radius_msg_list *entry; @@ -1037,6 +1240,16 @@ static int radius_client_init_acct(struct radius_client_data *radius) } +/** + * radius_client_init - Initialize RADIUS client + * @ctx: Callback context to be used in hostapd_logger() calls + * @conf: RADIUS client configuration (RADIUS servers) + * Returns: Pointer to private RADIUS client context or %NULL on failure + * + * The caller is responsible for keeping the configuration data available for + * the lifetime of the RADIUS client, i.e., until radius_client_deinit() is + * called for the returned context pointer. + */ struct radius_client_data * radius_client_init(void *ctx, struct hostapd_radius_servers *conf) { @@ -1071,6 +1284,10 @@ radius_client_init(void *ctx, struct hostapd_radius_servers *conf) } +/** + * radius_client_deinit - Deinitialize RADIUS client + * @radius: RADIUS client context from radius_client_init() + */ void radius_client_deinit(struct radius_client_data *radius) { if (!radius) @@ -1096,7 +1313,18 @@ void radius_client_deinit(struct radius_client_data *radius) } -void radius_client_flush_auth(struct radius_client_data *radius, u8 *addr) +/** + * radius_client_flush_auth - Flush pending RADIUS messages for an address + * @radius: RADIUS client context from radius_client_init() + * @addr: MAC address of the related device + * + * This function can be used to remove pending RADIUS authentication messages + * that are related to a specific device. The addr parameter is matched with + * the one used in radius_client_send() call that was used to transmit the + * authentication request. + */ +void radius_client_flush_auth(struct radius_client_data *radius, + const u8 *addr) { struct radius_msg_list *entry, *prev, *tmp; @@ -1224,6 +1452,13 @@ static int radius_client_dump_acct_server(char *buf, size_t buflen, } +/** + * radius_client_get_mib - Get RADIUS client MIB information + * @radius: RADIUS client context from radius_client_init() + * @buf: Buffer for returning MIB data in text format + * @buflen: Maximum buf length in octets + * Returns: Number of octets written into the buffer + */ int radius_client_get_mib(struct radius_client_data *radius, char *buf, size_t buflen) { @@ -1254,48 +1489,3 @@ int radius_client_get_mib(struct radius_client_data *radius, char *buf, return count; } - - -static int radius_servers_diff(struct hostapd_radius_server *nserv, - struct hostapd_radius_server *oserv, - int num) -{ - int i; - - for (i = 0; i < num; i++) { - if (hostapd_ip_diff(&nserv[i].addr, &oserv[i].addr) || - nserv[i].port != oserv[i].port || - nserv[i].shared_secret_len != oserv[i].shared_secret_len || - os_memcmp(nserv[i].shared_secret, oserv[i].shared_secret, - nserv[i].shared_secret_len) != 0) - return 1; - } - - return 0; -} - - -struct radius_client_data * -radius_client_reconfig(struct radius_client_data *old, void *ctx, - struct hostapd_radius_servers *oldconf, - struct hostapd_radius_servers *newconf) -{ - radius_client_flush(old, 0); - - if (newconf->retry_primary_interval != - oldconf->retry_primary_interval || - newconf->num_auth_servers != oldconf->num_auth_servers || - newconf->num_acct_servers != oldconf->num_acct_servers || - radius_servers_diff(newconf->auth_servers, oldconf->auth_servers, - newconf->num_auth_servers) || - radius_servers_diff(newconf->acct_servers, oldconf->acct_servers, - newconf->num_acct_servers)) { - hostapd_logger(ctx, NULL, HOSTAPD_MODULE_RADIUS, - HOSTAPD_LEVEL_DEBUG, - "Reconfiguring RADIUS client"); - radius_client_deinit(old); - return radius_client_init(ctx, newconf); - } - - return old; -} diff --git a/contrib/wpa/src/radius/radius_client.h b/contrib/wpa/src/radius/radius_client.h index 4fe9ba9..644ea23 100644 --- a/contrib/wpa/src/radius/radius_client.h +++ b/contrib/wpa/src/radius/radius_client.h @@ -1,6 +1,6 @@ /* - * hostapd / RADIUS client - * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi> + * RADIUS client + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -19,63 +19,222 @@ struct radius_msg; +/** + * struct hostapd_radius_server - RADIUS server information for RADIUS client + * + * This structure contains information about a RADIUS server. The values are + * mainly for MIB information. The MIB variable prefix (radiusAuth or + * radiusAcc) depends on whether this is an authentication or accounting + * server. + * + * radiusAuthClientPendingRequests (or radiusAccClientPendingRequests) is the + * number struct radius_client_data::msgs for matching msg_type. + */ 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 */ + /** + * addr - radiusAuthServerAddress or radiusAccServerAddress + */ + struct hostapd_ip_addr addr; + + /** + * port - radiusAuthClientServerPortNumber or radiusAccClientServerPortNumber + */ + int port; + + /** + * shared_secret - Shared secret for authenticating RADIUS messages + */ u8 *shared_secret; + + /** + * shared_secret_len - Length of shared_secret in octets + */ 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 */ + + /** + * index - radiusAuthServerIndex or radiusAccServerIndex + */ + int index; + + /** + * round_trip_time - radiusAuthClientRoundTripTime or radiusAccClientRoundTripTime + * Round-trip time in hundredths of a second. + */ + int round_trip_time; + + /** + * requests - radiusAuthClientAccessRequests or radiusAccClientRequests + */ + u32 requests; + + /** + * retransmissions - radiusAuthClientAccessRetransmissions or radiusAccClientRetransmissions + */ + u32 retransmissions; + + /** + * access_accepts - radiusAuthClientAccessAccepts + */ + u32 access_accepts; + + /** + * access_rejects - radiusAuthClientAccessRejects + */ + u32 access_rejects; + + /** + * access_challenges - radiusAuthClientAccessChallenges + */ + u32 access_challenges; + + /** + * responses - radiusAccClientResponses + */ + u32 responses; + + /** + * malformed_responses - radiusAuthClientMalformedAccessResponses or radiusAccClientMalformedResponses + */ + u32 malformed_responses; + + /** + * bad_authenticators - radiusAuthClientBadAuthenticators or radiusAccClientBadAuthenticators + */ + u32 bad_authenticators; + + /** + * timeouts - radiusAuthClientTimeouts or radiusAccClientTimeouts + */ + u32 timeouts; + + /** + * unknown_types - radiusAuthClientUnknownTypes or radiusAccClientUnknownTypes + */ + u32 unknown_types; + + /** + * packets_dropped - radiusAuthClientPacketsDropped or radiusAccClientPacketsDropped + */ + u32 packets_dropped; }; +/** + * struct hostapd_radius_servers - RADIUS servers for RADIUS client + */ struct hostapd_radius_servers { - /* RADIUS Authentication and Accounting servers in priority order */ - struct hostapd_radius_server *auth_servers, *auth_server; + /** + * auth_servers - RADIUS Authentication servers in priority order + */ + struct hostapd_radius_server *auth_servers; + + /** + * num_auth_servers - Number of auth_servers entries + */ int num_auth_servers; - struct hostapd_radius_server *acct_servers, *acct_server; + + /** + * auth_server - The current Authentication server + */ + struct hostapd_radius_server *auth_server; + + /** + * acct_servers - RADIUS Accounting servers in priority order + */ + struct hostapd_radius_server *acct_servers; + + /** + * num_acct_servers - Number of acct_servers entries + */ int num_acct_servers; + /** + * acct_server - The current Accounting server + */ + struct hostapd_radius_server *acct_server; + + /** + * retry_primary_interval - Retry interval for trying primary server + * + * This specifies a retry interval in sexconds for trying to return to + * the primary RADIUS server. RADIUS client code will automatically try + * to use the next server when the current server is not replying to + * requests. If this interval is set (non-zero), the primary server + * will be retried after the specified number of seconds has passed + * even if the current used secondary server is still working. + */ int retry_primary_interval; - int acct_interim_interval; + /** + * msg_dumps - Whether RADIUS message details are shown in stdout + */ int msg_dumps; + /** + * client_addr - Client (local) address to use if force_client_addr + */ struct hostapd_ip_addr client_addr; + + /** + * force_client_addr - Whether to force client (local) address + */ int force_client_addr; }; +/** + * RadiusType - RADIUS server type for RADIUS client + */ typedef enum { + /** + * RADIUS authentication + */ RADIUS_AUTH, + + /** + * RADIUS_ACCT - RADIUS accounting + */ RADIUS_ACCT, - RADIUS_ACCT_INTERIM /* used only with radius_client_send(); just like - * RADIUS_ACCT, but removes any pending interim - * RADIUS Accounting packages for the same STA - * before sending the new interim update */ + + /** + * RADIUS_ACCT_INTERIM - RADIUS interim accounting message + * + * Used only with radius_client_send(). This behaves just like + * RADIUS_ACCT, but removes any pending interim RADIUS Accounting + * messages for the same STA before sending the new interim update. + */ + RADIUS_ACCT_INTERIM } RadiusType; +/** + * RadiusRxResult - RADIUS client RX handler result + */ typedef enum { + /** + * RADIUS_RX_PROCESSED - Message processed + * + * This stops handler calls and frees the message. + */ RADIUS_RX_PROCESSED, + + /** + * RADIUS_RX_QUEUED - Message has been queued + * + * This stops handler calls, but does not free the message; the handler + * that returned this is responsible for eventually freeing the + * message. + */ RADIUS_RX_QUEUED, + + /** + * RADIUS_RX_UNKNOWN - Message is not for this handler + */ RADIUS_RX_UNKNOWN, + + /** + * RADIUS_RX_INVALID_AUTHENTICATOR - Message has invalid Authenticator + */ RADIUS_RX_INVALID_AUTHENTICATOR } RadiusRxResult; @@ -92,17 +251,13 @@ int radius_client_send(struct radius_client_data *radius, struct radius_msg *msg, RadiusType msg_type, const u8 *addr); u8 radius_client_get_id(struct radius_client_data *radius); - void radius_client_flush(struct radius_client_data *radius, int only_auth); struct radius_client_data * radius_client_init(void *ctx, struct hostapd_radius_servers *conf); void radius_client_deinit(struct radius_client_data *radius); -void radius_client_flush_auth(struct radius_client_data *radius, u8 *addr); +void radius_client_flush_auth(struct radius_client_data *radius, + const u8 *addr); int radius_client_get_mib(struct radius_client_data *radius, char *buf, size_t buflen); -struct radius_client_data * -radius_client_reconfig(struct radius_client_data *old, void *ctx, - struct hostapd_radius_servers *oldconf, - struct hostapd_radius_servers *newconf); #endif /* RADIUS_CLIENT_H */ diff --git a/contrib/wpa/src/radius/radius_server.c b/contrib/wpa/src/radius/radius_server.c index 4f399bc..f8780a6 100644 --- a/contrib/wpa/src/radius/radius_server.c +++ b/contrib/wpa/src/radius/radius_server.c @@ -1,6 +1,6 @@ /* - * hostapd / RADIUS authentication server - * Copyright (c) 2005-2008, Jouni Malinen <j@w1.fi> + * RADIUS authentication server + * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -18,12 +18,22 @@ #include "common.h" #include "radius.h" #include "eloop.h" -#include "defs.h" #include "eap_server/eap.h" #include "radius_server.h" +/** + * RADIUS_SESSION_TIMEOUT - Session timeout in seconds + */ #define RADIUS_SESSION_TIMEOUT 60 + +/** + * RADIUS_MAX_SESSION - Maximum number of active sessions + */ #define RADIUS_MAX_SESSION 100 + +/** + * RADIUS_MAX_MSG_LEN - Maximum message length for incoming RADIUS messages + */ #define RADIUS_MAX_MSG_LEN 3000 static struct eapol_callbacks radius_server_eapol_cb; @@ -31,6 +41,9 @@ static struct eapol_callbacks radius_server_eapol_cb; struct radius_client; struct radius_server_data; +/** + * struct radius_server_counters - RADIUS server statistics counters + */ struct radius_server_counters { u32 access_requests; u32 invalid_requests; @@ -44,6 +57,9 @@ struct radius_server_counters { u32 unknown_types; }; +/** + * struct radius_session - Internal RADIUS server data for a session + */ struct radius_session { struct radius_session *next; struct radius_client *client; @@ -62,6 +78,9 @@ struct radius_session { u8 last_authenticator[16]; }; +/** + * struct radius_client - Internal RADIUS server data for a client + */ struct radius_client { struct radius_client *next; struct in_addr addr; @@ -76,31 +95,196 @@ struct radius_client { struct radius_server_counters counters; }; +/** + * struct radius_server_data - Internal RADIUS server data + */ struct radius_server_data { + /** + * auth_sock - Socket for RADIUS authentication messages + */ int auth_sock; + + /** + * clients - List of authorized RADIUS clients + */ struct radius_client *clients; + + /** + * next_sess_id - Next session identifier + */ unsigned int next_sess_id; + + /** + * conf_ctx - Context pointer for callbacks + * + * This is used as the ctx argument in get_eap_user() calls. + */ void *conf_ctx; + + /** + * num_sess - Number of active sessions + */ int num_sess; + + /** + * eap_sim_db_priv - EAP-SIM/AKA database context + * + * This is passed to the EAP-SIM/AKA server implementation as a + * callback context. + */ void *eap_sim_db_priv; + + /** + * ssl_ctx - TLS context + * + * This is passed to the EAP server implementation as a callback + * context for TLS operations. + */ void *ssl_ctx; + + /** + * pac_opaque_encr_key - PAC-Opaque encryption key for EAP-FAST + * + * This parameter is used to set a key for EAP-FAST to encrypt the + * PAC-Opaque data. It can be set to %NULL if EAP-FAST is not used. If + * set, must point to a 16-octet key. + */ u8 *pac_opaque_encr_key; + + /** + * eap_fast_a_id - EAP-FAST authority identity (A-ID) + * + * If EAP-FAST is not used, this can be set to %NULL. In theory, this + * is a variable length field, but due to some existing implementations + * requiring A-ID to be 16 octets in length, it is recommended to use + * that length for the field to provide interoperability with deployed + * peer implementations. + */ u8 *eap_fast_a_id; + + /** + * eap_fast_a_id_len - Length of eap_fast_a_id buffer in octets + */ size_t eap_fast_a_id_len; + + /** + * eap_fast_a_id_info - EAP-FAST authority identifier information + * + * This A-ID-Info contains a user-friendly name for the A-ID. For + * example, this could be the enterprise and server names in + * human-readable format. This field is encoded as UTF-8. If EAP-FAST + * is not used, this can be set to %NULL. + */ char *eap_fast_a_id_info; + + /** + * eap_fast_prov - EAP-FAST provisioning modes + * + * 0 = provisioning disabled, 1 = only anonymous provisioning allowed, + * 2 = only authenticated provisioning allowed, 3 = both provisioning + * modes allowed. + */ int eap_fast_prov; + + /** + * pac_key_lifetime - EAP-FAST PAC-Key lifetime in seconds + * + * This is the hard limit on how long a provisioned PAC-Key can be + * used. + */ int pac_key_lifetime; + + /** + * pac_key_refresh_time - EAP-FAST PAC-Key refresh time in seconds + * + * This is a soft limit on the PAC-Key. The server will automatically + * generate a new PAC-Key when this number of seconds (or fewer) of the + * lifetime remains. + */ int pac_key_refresh_time; + + /** + * eap_sim_aka_result_ind - EAP-SIM/AKA protected success indication + * + * This controls whether the protected success/failure indication + * (AT_RESULT_IND) is used with EAP-SIM and EAP-AKA. + */ int eap_sim_aka_result_ind; + + /** + * tnc - Trusted Network Connect (TNC) + * + * This controls whether TNC is enabled and will be required before the + * peer is allowed to connect. Note: This is only used with EAP-TTLS + * and EAP-FAST. If any other EAP method is enabled, the peer will be + * allowed to connect without TNC. + */ int tnc; + + /** + * wps - Wi-Fi Protected Setup context + * + * If WPS is used with an external RADIUS server (which is quite + * unlikely configuration), this is used to provide a pointer to WPS + * context data. Normally, this can be set to %NULL. + */ struct wps_context *wps; + + /** + * ipv6 - Whether to enable IPv6 support in the RADIUS server + */ int ipv6; + + /** + * start_time - Timestamp of server start + */ struct os_time start_time; + + /** + * counters - Statistics counters for server operations + * + * These counters are the sum over all clients. + */ struct radius_server_counters counters; + + /** + * get_eap_user - Callback for fetching EAP user information + * @ctx: Context data from conf_ctx + * @identity: User identity + * @identity_len: identity buffer length in octets + * @phase2: Whether this is for Phase 2 identity + * @user: Data structure for filling in the user information + * Returns: 0 on success, -1 on failure + * + * This is used to fetch information from user database. The callback + * will fill in information about allowed EAP methods and the user + * password. The password field will be an allocated copy of the + * password data and RADIUS server will free it after use. + */ int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len, int phase2, struct eap_user *user); + + /** + * eap_req_id_text - Optional data for EAP-Request/Identity + * + * This can be used to configure an optional, displayable message that + * will be sent in EAP-Request/Identity. This string can contain an + * ASCII-0 character (nul) to separate network infromation per RFC + * 4284. The actual string length is explicit provided in + * eap_req_id_text_len since nul character will not be used as a string + * terminator. + */ char *eap_req_id_text; + + /** + * eap_req_id_text_len - Length of eap_req_id_text buffer in octets + */ size_t eap_req_id_text_len; + + /* + * msg_ctx - Context data for wpa_msg() calls + */ + void *msg_ctx; }; @@ -182,15 +366,9 @@ static void radius_server_session_free(struct radius_server_data *data, eloop_cancel_timeout(radius_server_session_timeout, data, sess); eloop_cancel_timeout(radius_server_session_remove_timeout, data, sess); eap_server_sm_deinit(sess->eap); - if (sess->last_msg) { - radius_msg_free(sess->last_msg); - os_free(sess->last_msg); - } + radius_msg_free(sess->last_msg); os_free(sess->last_from_addr); - if (sess->last_reply) { - radius_msg_free(sess->last_reply); - os_free(sess->last_reply); - } + radius_msg_free(sess->last_reply); os_free(sess); data->num_sess--; } @@ -313,6 +491,7 @@ radius_server_get_new_session(struct radius_server_data *data, os_memset(&eap_conf, 0, sizeof(eap_conf)); eap_conf.ssl_ctx = data->ssl_ctx; + eap_conf.msg_ctx = data->msg_ctx; eap_conf.eap_sim_db_priv = data->eap_sim_db_priv; eap_conf.backend_auth = TRUE; eap_conf.eap_server = 1; @@ -353,6 +532,7 @@ radius_server_encapsulate_eap(struct radius_server_data *data, struct radius_msg *msg; int code; unsigned int sess_id; + struct radius_hdr *hdr = radius_msg_get_hdr(request); if (sess->eap_if->eapFail) { sess->eap_if->eapFail = FALSE; @@ -365,7 +545,7 @@ radius_server_encapsulate_eap(struct radius_server_data *data, code = RADIUS_CODE_ACCESS_CHALLENGE; } - msg = radius_msg_new(code, request->hdr->identifier); + msg = radius_msg_new(code, hdr->identifier); if (msg == NULL) { RADIUS_DEBUG("Failed to allocate reply message"); return NULL; @@ -391,7 +571,7 @@ radius_server_encapsulate_eap(struct radius_server_data *data, } else { len = sess->eap_if->eapKeyDataLen / 2; } - if (!radius_msg_add_mppe_keys(msg, request->hdr->authenticator, + if (!radius_msg_add_mppe_keys(msg, hdr->authenticator, (u8 *) client->shared_secret, client->shared_secret_len, sess->eap_if->eapKeyData + len, @@ -404,13 +584,12 @@ radius_server_encapsulate_eap(struct radius_server_data *data, if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) { RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)"); radius_msg_free(msg); - os_free(msg); return NULL; } if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret, client->shared_secret_len, - request->hdr->authenticator) < 0) { + hdr->authenticator) < 0) { RADIUS_DEBUG("Failed to add Message-Authenticator attribute"); } @@ -427,12 +606,13 @@ static int radius_server_reject(struct radius_server_data *data, struct radius_msg *msg; int ret = 0; struct eap_hdr eapfail; + struct wpabuf *buf; + struct radius_hdr *hdr = radius_msg_get_hdr(request); RADIUS_DEBUG("Reject invalid request from %s:%d", from_addr, from_port); - msg = radius_msg_new(RADIUS_CODE_ACCESS_REJECT, - request->hdr->identifier); + msg = radius_msg_new(RADIUS_CODE_ACCESS_REJECT, hdr->identifier); if (msg == NULL) { return -1; } @@ -449,13 +629,13 @@ static int radius_server_reject(struct radius_server_data *data, if (radius_msg_copy_attr(msg, request, RADIUS_ATTR_PROXY_STATE) < 0) { RADIUS_DEBUG("Failed to copy Proxy-State attribute(s)"); radius_msg_free(msg); - os_free(msg); return -1; } if (radius_msg_finish_srv(msg, (u8 *) client->shared_secret, client->shared_secret_len, - request->hdr->authenticator) < 0) { + hdr->authenticator) < + 0) { RADIUS_DEBUG("Failed to add Message-Authenticator attribute"); } @@ -465,14 +645,14 @@ static int radius_server_reject(struct radius_server_data *data, data->counters.access_rejects++; client->counters.access_rejects++; - if (sendto(data->auth_sock, msg->buf, msg->buf_used, 0, + buf = radius_msg_get_buf(msg); + if (sendto(data->auth_sock, wpabuf_head(buf), wpabuf_len(buf), 0, (struct sockaddr *) from, sizeof(*from)) < 0) { perror("sendto[RADIUS SRV]"); ret = -1; } radius_msg_free(msg); - os_free(msg); return ret; } @@ -526,16 +706,18 @@ static int radius_server_request(struct radius_server_data *data, } if (sess->last_from_port == from_port && - sess->last_identifier == msg->hdr->identifier && - os_memcmp(sess->last_authenticator, msg->hdr->authenticator, 16) == - 0) { + sess->last_identifier == radius_msg_get_hdr(msg)->identifier && + os_memcmp(sess->last_authenticator, + radius_msg_get_hdr(msg)->authenticator, 16) == 0) { RADIUS_DEBUG("Duplicate message from %s", from_addr); data->counters.dup_access_requests++; client->counters.dup_access_requests++; if (sess->last_reply) { - res = sendto(data->auth_sock, sess->last_reply->buf, - sess->last_reply->buf_used, 0, + struct wpabuf *buf; + buf = radius_msg_get_buf(sess->last_reply); + res = sendto(data->auth_sock, wpabuf_head(buf), + wpabuf_len(buf), 0, (struct sockaddr *) from, fromlen); if (res < 0) { perror("sendto[RADIUS SRV]"); @@ -583,10 +765,7 @@ static int radius_server_request(struct radius_server_data *data, RADIUS_DEBUG("No EAP data from the state machine, but eapFail " "set"); } else if (eap_sm_method_pending(sess->eap)) { - if (sess->last_msg) { - radius_msg_free(sess->last_msg); - os_free(sess->last_msg); - } + radius_msg_free(sess->last_msg); sess->last_msg = msg; sess->last_from_port = from_port; os_free(sess->last_from_addr); @@ -609,12 +788,15 @@ static int radius_server_request(struct radius_server_data *data, reply = radius_server_encapsulate_eap(data, client, sess, msg); if (reply) { + struct wpabuf *buf; + struct radius_hdr *hdr; + RADIUS_DEBUG("Reply to %s:%d", from_addr, from_port); if (wpa_debug_level <= MSG_MSGDUMP) { radius_msg_dump(reply); } - switch (reply->hdr->code) { + switch (radius_msg_get_hdr(reply)->code) { case RADIUS_CODE_ACCESS_ACCEPT: data->counters.access_accepts++; client->counters.access_accepts++; @@ -628,20 +810,19 @@ static int radius_server_request(struct radius_server_data *data, client->counters.access_challenges++; break; } - res = sendto(data->auth_sock, reply->buf, reply->buf_used, 0, + buf = radius_msg_get_buf(reply); + res = sendto(data->auth_sock, wpabuf_head(buf), + wpabuf_len(buf), 0, (struct sockaddr *) from, fromlen); if (res < 0) { perror("sendto[RADIUS SRV]"); } - if (sess->last_reply) { - radius_msg_free(sess->last_reply); - os_free(sess->last_reply); - } + radius_msg_free(sess->last_reply); sess->last_reply = reply; sess->last_from_port = from_port; - sess->last_identifier = msg->hdr->identifier; - os_memcpy(sess->last_authenticator, msg->hdr->authenticator, - 16); + hdr = radius_msg_get_hdr(msg); + sess->last_identifier = hdr->identifier; + os_memcpy(sess->last_authenticator, hdr->authenticator, 16); } else { data->counters.packets_dropped++; client->counters.packets_dropped++; @@ -740,8 +921,9 @@ static void radius_server_receive_auth(int sock, void *eloop_ctx, radius_msg_dump(msg); } - if (msg->hdr->code != RADIUS_CODE_ACCESS_REQUEST) { - RADIUS_DEBUG("Unexpected RADIUS code %d", msg->hdr->code); + if (radius_msg_get_hdr(msg)->code != RADIUS_CODE_ACCESS_REQUEST) { + RADIUS_DEBUG("Unexpected RADIUS code %d", + radius_msg_get_hdr(msg)->code); data->counters.unknown_types++; client->counters.unknown_types++; goto fail; @@ -764,10 +946,7 @@ static void radius_server_receive_auth(int sock, void *eloop_ctx, return; /* msg was stored with the session */ fail: - if (msg) { - radius_msg_free(msg); - os_free(msg); - } + radius_msg_free(msg); os_free(buf); } @@ -1026,6 +1205,15 @@ radius_server_read_clients(const char *client_file, int ipv6) } +/** + * radius_server_init - Initialize RADIUS server + * @conf: Configuration for the RADIUS server + * Returns: Pointer to private RADIUS server context or %NULL on failure + * + * This initializes a RADIUS server instance and returns a context pointer that + * will be used in other calls to the RADIUS server module. The server can be + * deinitialize by calling radius_server_deinit(). + */ struct radius_server_data * radius_server_init(struct radius_server_conf *conf) { @@ -1047,6 +1235,7 @@ radius_server_init(struct radius_server_conf *conf) data->conf_ctx = conf->conf_ctx; data->eap_sim_db_priv = conf->eap_sim_db_priv; data->ssl_ctx = conf->ssl_ctx; + data->msg_ctx = conf->msg_ctx; data->ipv6 = conf->ipv6; if (conf->pac_opaque_encr_key) { data->pac_opaque_encr_key = os_malloc(16); @@ -1110,6 +1299,10 @@ radius_server_init(struct radius_server_conf *conf) } +/** + * radius_server_deinit - Deinitialize RADIUS server + * @data: RADIUS server context from radius_server_init() + */ void radius_server_deinit(struct radius_server_data *data) { if (data == NULL) @@ -1130,6 +1323,13 @@ void radius_server_deinit(struct radius_server_data *data) } +/** + * radius_server_get_mib - Get RADIUS server MIB information + * @data: RADIUS server context from radius_server_init() + * @buf: Buffer for returning the MIB data in text format + * @buflen: buf length in octets + * Returns: Number of octets written into buf + */ int radius_server_get_mib(struct radius_server_data *data, char *buf, size_t buflen) { @@ -1269,6 +1469,14 @@ static struct eapol_callbacks radius_server_eapol_cb = }; +/** + * radius_server_eap_pending_cb - Pending EAP data notification + * @data: RADIUS server context from radius_server_init() + * @ctx: Pending EAP context pointer + * + * This function is used to notify EAP server module that a pending operation + * has been completed and processing of the EAP session can proceed. + */ void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx) { struct radius_client *cli; @@ -1307,5 +1515,4 @@ void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx) return; /* msg was stored with the session */ radius_msg_free(msg); - os_free(msg); } diff --git a/contrib/wpa/src/radius/radius_server.h b/contrib/wpa/src/radius/radius_server.h index d5fb6a1..f9c951d 100644 --- a/contrib/wpa/src/radius/radius_server.h +++ b/contrib/wpa/src/radius/radius_server.h @@ -1,6 +1,6 @@ /* - * hostapd / RADIUS authentication server - * Copyright (c) 2005-2007, Jouni Malinen <j@w1.fi> + * RADIUS authentication server + * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -18,31 +18,184 @@ struct radius_server_data; struct eap_user; +/** + * struct radius_server_conf - RADIUS server configuration + */ struct radius_server_conf { + /** + * auth_port - UDP port to listen to as an authentication server + */ int auth_port; + + /** + * client_file - RADIUS client configuration file + * + * This file contains the RADIUS clients and the shared secret to be + * used with them in a format where each client is on its own line. The + * first item on the line is the IPv4 or IPv6 address of the client + * with an optional address mask to allow full network to be specified + * (e.g., 192.168.1.2 or 192.168.1.0/24). This is followed by white + * space (space or tabulator) and the shared secret. Lines starting + * with '#' are skipped and can be used as comments. + */ char *client_file; + + /** + * conf_ctx - Context pointer for callbacks + * + * This is used as the ctx argument in get_eap_user() calls. + */ void *conf_ctx; + + /** + * eap_sim_db_priv - EAP-SIM/AKA database context + * + * This is passed to the EAP-SIM/AKA server implementation as a + * callback context. + */ void *eap_sim_db_priv; + + /** + * ssl_ctx - TLS context + * + * This is passed to the EAP server implementation as a callback + * context for TLS operations. + */ void *ssl_ctx; + + /** + * pac_opaque_encr_key - PAC-Opaque encryption key for EAP-FAST + * + * This parameter is used to set a key for EAP-FAST to encrypt the + * PAC-Opaque data. It can be set to %NULL if EAP-FAST is not used. If + * set, must point to a 16-octet key. + */ u8 *pac_opaque_encr_key; + + /** + * eap_fast_a_id - EAP-FAST authority identity (A-ID) + * + * If EAP-FAST is not used, this can be set to %NULL. In theory, this + * is a variable length field, but due to some existing implementations + * requiring A-ID to be 16 octets in length, it is recommended to use + * that length for the field to provide interoperability with deployed + * peer implementations. + */ u8 *eap_fast_a_id; + + /** + * eap_fast_a_id_len - Length of eap_fast_a_id buffer in octets + */ size_t eap_fast_a_id_len; + + /** + * eap_fast_a_id_info - EAP-FAST authority identifier information + * + * This A-ID-Info contains a user-friendly name for the A-ID. For + * example, this could be the enterprise and server names in + * human-readable format. This field is encoded as UTF-8. If EAP-FAST + * is not used, this can be set to %NULL. + */ char *eap_fast_a_id_info; + + /** + * eap_fast_prov - EAP-FAST provisioning modes + * + * 0 = provisioning disabled, 1 = only anonymous provisioning allowed, + * 2 = only authenticated provisioning allowed, 3 = both provisioning + * modes allowed. + */ int eap_fast_prov; + + /** + * pac_key_lifetime - EAP-FAST PAC-Key lifetime in seconds + * + * This is the hard limit on how long a provisioned PAC-Key can be + * used. + */ int pac_key_lifetime; + + /** + * pac_key_refresh_time - EAP-FAST PAC-Key refresh time in seconds + * + * This is a soft limit on the PAC-Key. The server will automatically + * generate a new PAC-Key when this number of seconds (or fewer) of the + * lifetime remains. + */ int pac_key_refresh_time; + + /** + * eap_sim_aka_result_ind - EAP-SIM/AKA protected success indication + * + * This controls whether the protected success/failure indication + * (AT_RESULT_IND) is used with EAP-SIM and EAP-AKA. + */ int eap_sim_aka_result_ind; + + /** + * tnc - Trusted Network Connect (TNC) + * + * This controls whether TNC is enabled and will be required before the + * peer is allowed to connect. Note: This is only used with EAP-TTLS + * and EAP-FAST. If any other EAP method is enabled, the peer will be + * allowed to connect without TNC. + */ int tnc; + + /** + * wps - Wi-Fi Protected Setup context + * + * If WPS is used with an external RADIUS server (which is quite + * unlikely configuration), this is used to provide a pointer to WPS + * context data. Normally, this can be set to %NULL. + */ struct wps_context *wps; + + /** + * ipv6 - Whether to enable IPv6 support in the RADIUS server + */ int ipv6; + + /** + * get_eap_user - Callback for fetching EAP user information + * @ctx: Context data from conf_ctx + * @identity: User identity + * @identity_len: identity buffer length in octets + * @phase2: Whether this is for Phase 2 identity + * @user: Data structure for filling in the user information + * Returns: 0 on success, -1 on failure + * + * This is used to fetch information from user database. The callback + * will fill in information about allowed EAP methods and the user + * password. The password field will be an allocated copy of the + * password data and RADIUS server will free it after use. + */ int (*get_eap_user)(void *ctx, const u8 *identity, size_t identity_len, int phase2, struct eap_user *user); + + /** + * eap_req_id_text - Optional data for EAP-Request/Identity + * + * This can be used to configure an optional, displayable message that + * will be sent in EAP-Request/Identity. This string can contain an + * ASCII-0 character (nul) to separate network infromation per RFC + * 4284. The actual string length is explicit provided in + * eap_req_id_text_len since nul character will not be used as a string + * terminator. + */ const char *eap_req_id_text; + + /** + * eap_req_id_text_len - Length of eap_req_id_text buffer in octets + */ size_t eap_req_id_text_len; -}; + /* + * msg_ctx - Context data for wpa_msg() calls + */ + void *msg_ctx; +}; -#ifdef RADIUS_SERVER struct radius_server_data * radius_server_init(struct radius_server_conf *conf); @@ -54,29 +207,4 @@ int radius_server_get_mib(struct radius_server_data *data, char *buf, void radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx); -#else /* RADIUS_SERVER */ - -static inline struct radius_server_data * -radius_server_init(struct radius_server_conf *conf) -{ - return NULL; -} - -static inline void radius_server_deinit(struct radius_server_data *data) -{ -} - -static inline int radius_server_get_mib(struct radius_server_data *data, - char *buf, size_t buflen) -{ - return 0; -} - -static inline void -radius_server_eap_pending_cb(struct radius_server_data *data, void *ctx) -{ -} - -#endif /* RADIUS_SERVER */ - #endif /* RADIUS_SERVER_H */ diff --git a/contrib/wpa/src/rsn_supp/Makefile b/contrib/wpa/src/rsn_supp/Makefile index cffba62..9c41962 100644 --- a/contrib/wpa/src/rsn_supp/Makefile +++ b/contrib/wpa/src/rsn_supp/Makefile @@ -2,7 +2,6 @@ all: @echo Nothing to be made. clean: - for d in $(SUBDIRS); do make -C $$d clean; done rm -f *~ *.o *.d install: diff --git a/contrib/wpa/src/rsn_supp/peerkey.c b/contrib/wpa/src/rsn_supp/peerkey.c index 45c256a..9d60d4a 100644 --- a/contrib/wpa/src/rsn_supp/peerkey.c +++ b/contrib/wpa/src/rsn_supp/peerkey.c @@ -17,13 +17,13 @@ #ifdef CONFIG_PEERKEY #include "common.h" -#include "sha1.h" -#include "sha256.h" #include "eloop.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" +#include "common/ieee802_11_defs.h" #include "wpa.h" #include "wpa_i.h" #include "wpa_ie.h" -#include "ieee802_11_defs.h" #include "peerkey.h" @@ -255,7 +255,7 @@ static int wpa_supplicant_process_smk_m2( #endif /* CONFIG_IEEE80211W */ if (os_get_random(peerkey->pnonce, WPA_NONCE_LEN)) { - wpa_msg(sm->ctx->ctx, MSG_WARNING, + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to get random data for PNonce"); wpa_supplicant_peerkey_free(sm, peerkey); return -1; @@ -371,7 +371,7 @@ static void wpa_supplicant_send_stk_1_of_4(struct wpa_sm *sm, peerkey->smkid, PMKID_LEN); if (os_get_random(peerkey->inonce, WPA_NONCE_LEN)) { - wpa_msg(sm->ctx->ctx, MSG_WARNING, + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "RSN: Failed to get random data for INonce (STK)"); os_free(mbuf); return; @@ -634,9 +634,11 @@ static int wpa_supplicant_process_smk_error( if (kde.mac_addr && kde.mac_addr_len >= ETH_ALEN) os_memcpy(peer, kde.mac_addr, ETH_ALEN); + else + os_memset(peer, 0, ETH_ALEN); os_memcpy(&error, kde.error, sizeof(error)); error_type = be_to_host16(error.error_type); - wpa_msg(sm->ctx->ctx, MSG_INFO, + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: SMK Error KDE received: MUI %d error_type %d peer " MACSTR, be_to_host16(error.mui), error_type, @@ -696,7 +698,7 @@ static void wpa_supplicant_process_stk_1_of_4(struct wpa_sm *sm, } if (os_get_random(peerkey->pnonce, WPA_NONCE_LEN)) { - wpa_msg(sm->ctx->ctx, MSG_WARNING, + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "RSN: Failed to get random data for PNonce"); return; } @@ -1096,7 +1098,7 @@ int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer) inc_byte_array(sm->request_counter, WPA_REPLAY_COUNTER_LEN); if (os_get_random(peerkey->inonce, WPA_NONCE_LEN)) { - wpa_msg(sm->ctx->ctx, MSG_WARNING, + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to get random data for INonce"); os_free(rbuf); wpa_supplicant_peerkey_free(sm, peerkey); diff --git a/contrib/wpa/src/rsn_supp/pmksa_cache.c b/contrib/wpa/src/rsn_supp/pmksa_cache.c index f8373de..cac8c83 100644 --- a/contrib/wpa/src/rsn_supp/pmksa_cache.c +++ b/contrib/wpa/src/rsn_supp/pmksa_cache.c @@ -15,12 +15,10 @@ #include "includes.h" #include "common.h" -#include "wpa.h" #include "eloop.h" -#include "sha1.h" -#include "sha256.h" -#include "wpa_i.h" #include "eapol_supp/eapol_supp_sm.h" +#include "wpa.h" +#include "wpa_i.h" #include "pmksa_cache.h" #if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2) @@ -38,39 +36,6 @@ struct rsn_pmksa_cache { }; -/** - * rsn_pmkid - Calculate PMK identifier - * @pmk: Pairwise master key - * @pmk_len: Length of pmk in bytes - * @aa: Authenticator address - * @spa: Supplicant address - * @use_sha256: Whether to use SHA256-based KDF - * - * IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy - * PMKID = HMAC-SHA1-128(PMK, "PMK Name" || AA || SPA) - */ -static void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, - const u8 *spa, u8 *pmkid, int use_sha256) -{ - char *title = "PMK Name"; - const u8 *addr[3]; - const size_t len[3] = { 8, ETH_ALEN, ETH_ALEN }; - unsigned char hash[SHA256_MAC_LEN]; - - addr[0] = (u8 *) title; - addr[1] = aa; - addr[2] = spa; - -#ifdef CONFIG_IEEE80211W - if (use_sha256) - hmac_sha256_vector(pmk, pmk_len, 3, addr, len, hash); - else -#endif /* CONFIG_IEEE80211W */ - hmac_sha1_vector(pmk, pmk_len, 3, addr, len, hash); - os_memcpy(pmkid, hash, PMKID_LEN); -} - - static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa); @@ -167,7 +132,7 @@ pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, struct rsn_pmksa_cache_entry *entry, *pos, *prev; struct os_time now; - if (pmksa->sm->proto != WPA_PROTO_RSN || pmk_len > PMK_LEN) + if (pmk_len > PMK_LEN) return NULL; entry = os_zalloc(sizeof(*entry)); @@ -439,7 +404,7 @@ int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, /** * pmksa_cache_list - Dump text list of entries in PMKSA cache - * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @pmksa: Pointer to PMKSA cache data from pmksa_cache_init() * @buf: Buffer for the list * @len: Length of the buffer * Returns: number of bytes written to buffer @@ -447,7 +412,7 @@ int pmksa_cache_set_current(struct wpa_sm *sm, const u8 *pmkid, * This function is used to generate a text format representation of the * current PMKSA cache contents for the ctrl_iface PMKSA command. */ -int pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len) +int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len) { int i, ret; char *pos = buf; @@ -462,7 +427,7 @@ int pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len) return pos - buf; pos += ret; i = 0; - entry = sm->pmksa->pmksa; + entry = pmksa->pmksa; while (entry) { i++; ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ", diff --git a/contrib/wpa/src/rsn_supp/pmksa_cache.h b/contrib/wpa/src/rsn_supp/pmksa_cache.h index a329b25..a1447e5 100644 --- a/contrib/wpa/src/rsn_supp/pmksa_cache.h +++ b/contrib/wpa/src/rsn_supp/pmksa_cache.h @@ -53,7 +53,7 @@ pmksa_cache_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void pmksa_cache_deinit(struct rsn_pmksa_cache *pmksa); struct rsn_pmksa_cache_entry * pmksa_cache_get(struct rsn_pmksa_cache *pmksa, const u8 *aa, const u8 *pmkid); -int pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len); +int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len); struct rsn_pmksa_cache_entry * pmksa_cache_add(struct rsn_pmksa_cache *pmksa, const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa, void *network_ctx, int akmp); @@ -93,7 +93,8 @@ pmksa_cache_get_current(struct wpa_sm *sm) return NULL; } -static inline int pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len) +static inline int pmksa_cache_list(struct rsn_pmksa_cache *pmksa, char *buf, + size_t len) { return -1; } diff --git a/contrib/wpa/src/rsn_supp/preauth.c b/contrib/wpa/src/rsn_supp/preauth.c index b00c004..6109f5e 100644 --- a/contrib/wpa/src/rsn_supp/preauth.c +++ b/contrib/wpa/src/rsn_supp/preauth.c @@ -1,6 +1,6 @@ /* - * WPA Supplicant - RSN pre-authentication - * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> + * RSN pre-authentication (supplicant) + * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -16,14 +16,13 @@ #include "common.h" #include "wpa.h" -#include "drivers/driver.h" #include "eloop.h" #include "l2_packet/l2_packet.h" #include "eapol_supp/eapol_supp_sm.h" #include "preauth.h" #include "pmksa_cache.h" #include "wpa_i.h" -#include "ieee802_11_defs.h" +#include "common/ieee802_11_defs.h" #if defined(IEEE8021X_EAPOL) && !defined(CONFIG_NO_WPA2) @@ -32,7 +31,7 @@ struct rsn_pmksa_candidate { - struct rsn_pmksa_candidate *next; + struct dl_list list; u8 bssid[ETH_ALEN]; int priority; }; @@ -44,17 +43,15 @@ struct rsn_pmksa_candidate { */ void pmksa_candidate_free(struct wpa_sm *sm) { - struct rsn_pmksa_candidate *entry, *prev; + struct rsn_pmksa_candidate *entry, *n; if (sm == NULL) return; - entry = sm->pmksa_candidates; - sm->pmksa_candidates = NULL; - while (entry) { - prev = entry; - entry = entry->next; - os_free(prev); + dl_list_for_each_safe(entry, n, &sm->pmksa_candidates, + struct rsn_pmksa_candidate, list) { + dl_list_del(&entry->list); + os_free(entry); } } @@ -107,15 +104,15 @@ static void rsn_preauth_eapol_cb(struct eapol_sm *eapol, int success, sm->network_ctx, WPA_KEY_MGMT_IEEE8021X); } else { - wpa_msg(sm->ctx->ctx, MSG_INFO, "RSN: failed to get " - "master session key from pre-auth EAPOL state " - "machines"); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "RSN: failed to get master session key from " + "pre-auth EAPOL state machines"); success = 0; } } - wpa_msg(sm->ctx->ctx, MSG_INFO, "RSN: pre-authentication with " MACSTR - " %s", MAC2STR(sm->preauth_bssid), + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: pre-authentication with " + MACSTR " %s", MAC2STR(sm->preauth_bssid), success ? "completed successfully" : "failed"); rsn_preauth_deinit(sm); @@ -127,8 +124,8 @@ static void rsn_preauth_timeout(void *eloop_ctx, void *timeout_ctx) { struct wpa_sm *sm = eloop_ctx; - wpa_msg(sm->ctx->ctx, MSG_INFO, "RSN: pre-authentication with " MACSTR - " timed out", MAC2STR(sm->preauth_bssid)); + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "RSN: pre-authentication with " + MACSTR " timed out", MAC2STR(sm->preauth_bssid)); rsn_preauth_deinit(sm); rsn_preauth_candidate_process(sm); } @@ -183,8 +180,8 @@ int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, if (sm->preauth_eapol) return -1; - wpa_msg(sm->ctx->ctx, MSG_DEBUG, "RSN: starting pre-authentication " - "with " MACSTR, MAC2STR(dst)); + wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, + "RSN: starting pre-authentication with " MACSTR, MAC2STR(dst)); sm->l2_preauth = l2_packet_init(sm->ifname, sm->own_addr, ETH_P_RSN_PREAUTH, @@ -293,42 +290,42 @@ void rsn_preauth_deinit(struct wpa_sm *sm) */ void rsn_preauth_candidate_process(struct wpa_sm *sm) { - struct rsn_pmksa_candidate *candidate; + struct rsn_pmksa_candidate *candidate, *n; - if (sm->pmksa_candidates == NULL) + if (dl_list_empty(&sm->pmksa_candidates)) return; /* TODO: drop priority for old candidate entries */ - wpa_msg(sm->ctx->ctx, MSG_DEBUG, "RSN: processing PMKSA candidate " + wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: processing PMKSA candidate " "list"); if (sm->preauth_eapol || sm->proto != WPA_PROTO_RSN || wpa_sm_get_state(sm) != WPA_COMPLETED || (sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X && sm->key_mgmt != WPA_KEY_MGMT_IEEE8021X_SHA256)) { - wpa_msg(sm->ctx->ctx, MSG_DEBUG, "RSN: not in suitable state " - "for new pre-authentication"); + wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: not in suitable " + "state for new pre-authentication"); return; /* invalid state for new pre-auth */ } - while (sm->pmksa_candidates) { + dl_list_for_each_safe(candidate, n, &sm->pmksa_candidates, + struct rsn_pmksa_candidate, list) { struct rsn_pmksa_cache_entry *p = NULL; - candidate = sm->pmksa_candidates; p = pmksa_cache_get(sm->pmksa, candidate->bssid, NULL); if (os_memcmp(sm->bssid, candidate->bssid, ETH_ALEN) != 0 && (p == NULL || p->opportunistic)) { - wpa_msg(sm->ctx->ctx, MSG_DEBUG, "RSN: PMKSA " + wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA " "candidate " MACSTR " selected for pre-authentication", MAC2STR(candidate->bssid)); - sm->pmksa_candidates = candidate->next; + dl_list_del(&candidate->list); rsn_preauth_init(sm, candidate->bssid, sm->eap_conf_ctx); os_free(candidate); return; } - wpa_msg(sm->ctx->ctx, MSG_DEBUG, "RSN: PMKSA candidate " + wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: PMKSA candidate " MACSTR " does not need pre-authentication anymore", MAC2STR(candidate->bssid)); /* Some drivers (e.g., NDIS) expect to get notified about the @@ -337,10 +334,10 @@ void rsn_preauth_candidate_process(struct wpa_sm *sm) wpa_sm_add_pmkid(sm, candidate->bssid, p->pmkid); } - sm->pmksa_candidates = candidate->next; + dl_list_del(&candidate->list); os_free(candidate); } - wpa_msg(sm->ctx->ctx, MSG_DEBUG, "RSN: no more pending PMKSA " + wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: no more pending PMKSA " "candidates"); } @@ -359,7 +356,7 @@ void rsn_preauth_candidate_process(struct wpa_sm *sm) void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid, int prio, int preauth) { - struct rsn_pmksa_candidate *cand, *prev, *pos; + struct rsn_pmksa_candidate *cand, *pos; if (sm->network_ctx && sm->proactive_key_caching) pmksa_cache_get_opportunistic(sm->pmksa, sm->network_ctx, @@ -373,21 +370,17 @@ void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid, /* If BSSID already on candidate list, update the priority of the old * entry. Do not override priority based on normal scan results. */ - prev = NULL; - cand = sm->pmksa_candidates; - while (cand) { - if (os_memcmp(cand->bssid, bssid, ETH_ALEN) == 0) { - if (prev) - prev->next = cand->next; - else - sm->pmksa_candidates = cand->next; + cand = NULL; + dl_list_for_each(pos, &sm->pmksa_candidates, + struct rsn_pmksa_candidate, list) { + if (os_memcmp(pos->bssid, bssid, ETH_ALEN) == 0) { + cand = pos; break; } - prev = cand; - cand = cand->next; } if (cand) { + dl_list_del(&cand->list); if (prio < PMKID_CANDIDATE_PRIO_SCAN) cand->priority = prio; } else { @@ -400,21 +393,18 @@ void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid, /* Add candidate to the list; order by increasing priority value. i.e., * highest priority (smallest value) first. */ - prev = NULL; - pos = sm->pmksa_candidates; - while (pos) { - if (cand->priority <= pos->priority) + dl_list_for_each(pos, &sm->pmksa_candidates, + struct rsn_pmksa_candidate, list) { + if (cand->priority <= pos->priority) { + dl_list_add(pos->list.prev, &cand->list); + cand = NULL; break; - prev = pos; - pos = pos->next; + } } - cand->next = pos; - if (prev) - prev->next = cand; - else - sm->pmksa_candidates = cand; + if (cand) + dl_list_add_tail(&sm->pmksa_candidates, &cand->list); - wpa_msg(sm->ctx->ctx, MSG_DEBUG, "RSN: added PMKSA cache " + wpa_msg(sm->ctx->msg_ctx, MSG_DEBUG, "RSN: added PMKSA cache " "candidate " MACSTR " prio %d", MAC2STR(bssid), prio); rsn_preauth_candidate_process(sm); } @@ -423,23 +413,18 @@ void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid, /* TODO: schedule periodic scans if current AP supports preauth */ /** - * rsn_preauth_scan_results - Process scan results to find PMKSA candidates + * rsn_preauth_scan_results - Start processing scan results for canditates * @sm: Pointer to WPA state machine data from wpa_sm_init() - * @results: Scan results + * Returns: 0 if ready to process results or -1 to skip processing * - * This functions goes through the scan results and adds all suitable APs - * (Authenticators) into PMKSA candidate list. + * This functions is used to notify RSN code about start of new scan results + * processing. The actual scan results will be provided by calling + * rsn_preauth_scan_result() for each BSS if this function returned 0. */ -void rsn_preauth_scan_results(struct wpa_sm *sm, - struct wpa_scan_results *results) +int rsn_preauth_scan_results(struct wpa_sm *sm) { - struct wpa_scan_res *r; - struct wpa_ie_data ie; - int i; - struct rsn_pmksa_cache_entry *pmksa; - if (sm->ssid_len == 0) - return; + return -1; /* * TODO: is it ok to free all candidates? What about the entries @@ -447,37 +432,41 @@ void rsn_preauth_scan_results(struct wpa_sm *sm, */ pmksa_candidate_free(sm); - for (i = results->num - 1; i >= 0; i--) { - const u8 *ssid, *rsn; + return 0; +} - r = results->res[i]; - ssid = wpa_scan_get_ie(r, WLAN_EID_SSID); - if (ssid == NULL || ssid[1] != sm->ssid_len || - os_memcmp(ssid + 2, sm->ssid, ssid[1]) != 0) - continue; +/** + * rsn_preauth_scan_result - Processing scan result for PMKSA canditates + * @sm: Pointer to WPA state machine data from wpa_sm_init() + * + * Add all suitable APs (Authenticators) from scan results into PMKSA + * candidate list. + */ +void rsn_preauth_scan_result(struct wpa_sm *sm, const u8 *bssid, + const u8 *ssid, const u8 *rsn) +{ + struct wpa_ie_data ie; + struct rsn_pmksa_cache_entry *pmksa; - if (os_memcmp(r->bssid, sm->bssid, ETH_ALEN) == 0) - continue; + if (ssid[1] != sm->ssid_len || + os_memcmp(ssid + 2, sm->ssid, sm->ssid_len) != 0) + return; /* Not for the current SSID */ - rsn = wpa_scan_get_ie(r, WLAN_EID_RSN); - if (rsn == NULL || wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie)) - continue; + if (os_memcmp(bssid, sm->bssid, ETH_ALEN) == 0) + return; /* Ignore current AP */ - pmksa = pmksa_cache_get(sm->pmksa, r->bssid, NULL); - if (pmksa && - (!pmksa->opportunistic || - !(ie.capabilities & WPA_CAPABILITY_PREAUTH))) - continue; + if (wpa_parse_wpa_ie(rsn, 2 + rsn[1], &ie)) + return; - /* - * Give less priority to candidates found from normal - * scan results. - */ - pmksa_candidate_add(sm, r->bssid, - PMKID_CANDIDATE_PRIO_SCAN, - ie.capabilities & WPA_CAPABILITY_PREAUTH); - } + pmksa = pmksa_cache_get(sm->pmksa, bssid, NULL); + if (pmksa && (!pmksa->opportunistic || + !(ie.capabilities & WPA_CAPABILITY_PREAUTH))) + return; + + /* Give less priority to candidates found from normal scan results. */ + pmksa_candidate_add(sm, bssid, PMKID_CANDIDATE_PRIO_SCAN, + ie.capabilities & WPA_CAPABILITY_PREAUTH); } diff --git a/contrib/wpa/src/rsn_supp/preauth.h b/contrib/wpa/src/rsn_supp/preauth.h index b9ac57b..f8240ab 100644 --- a/contrib/wpa/src/rsn_supp/preauth.h +++ b/contrib/wpa/src/rsn_supp/preauth.h @@ -1,6 +1,6 @@ /* * wpa_supplicant - WPA2/RSN pre-authentication functions - * Copyright (c) 2003-2005, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -23,8 +23,9 @@ void pmksa_candidate_free(struct wpa_sm *sm); int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, struct eap_peer_config *eap_conf); void rsn_preauth_deinit(struct wpa_sm *sm); -void rsn_preauth_scan_results(struct wpa_sm *sm, - struct wpa_scan_results *results); +int rsn_preauth_scan_results(struct wpa_sm *sm); +void rsn_preauth_scan_result(struct wpa_sm *sm, const u8 *bssid, + const u8 *ssid, const u8 *rsn); void pmksa_candidate_add(struct wpa_sm *sm, const u8 *bssid, int prio, int preauth); void rsn_preauth_candidate_process(struct wpa_sm *sm); @@ -51,8 +52,14 @@ static inline int rsn_preauth_init(struct wpa_sm *sm, const u8 *dst, static inline void rsn_preauth_deinit(struct wpa_sm *sm) { } -static inline void rsn_preauth_scan_results(struct wpa_sm *sm, - struct wpa_scan_results *results) + +static inline int rsn_preauth_scan_results(struct wpa_sm *sm) +{ + return -1; +} + +static inline void rsn_preauth_scan_result(struct wpa_sm *sm, const u8 *bssid, + const u8 *ssid, const u8 *rsn) { } diff --git a/contrib/wpa/src/rsn_supp/wpa.c b/contrib/wpa/src/rsn_supp/wpa.c index e611fc5..9439f97 100644 --- a/contrib/wpa/src/rsn_supp/wpa.c +++ b/contrib/wpa/src/rsn_supp/wpa.c @@ -1,6 +1,6 @@ /* * WPA Supplicant - WPA state machine and EAPOL-Key processing - * Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2003-2010, Jouni Malinen <j@w1.fi> * * This 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,78 +15,17 @@ #include "includes.h" #include "common.h" -#include "rc4.h" -#include "aes_wrap.h" +#include "crypto/aes_wrap.h" +#include "crypto/crypto.h" +#include "common/ieee802_11_defs.h" +#include "eapol_supp/eapol_supp_sm.h" #include "wpa.h" #include "eloop.h" -#include "eapol_supp/eapol_supp_sm.h" #include "preauth.h" #include "pmksa_cache.h" #include "wpa_i.h" #include "wpa_ie.h" #include "peerkey.h" -#include "ieee802_11_defs.h" - - -/** - * wpa_cipher_txt - Convert cipher suite to a text string - * @cipher: Cipher suite (WPA_CIPHER_* enum) - * Returns: Pointer to a text string of the cipher suite name - */ -static const char * wpa_cipher_txt(int cipher) -{ - switch (cipher) { - case WPA_CIPHER_NONE: - return "NONE"; - case WPA_CIPHER_WEP40: - return "WEP-40"; - case WPA_CIPHER_WEP104: - return "WEP-104"; - case WPA_CIPHER_TKIP: - return "TKIP"; - case WPA_CIPHER_CCMP: - return "CCMP"; - default: - return "UNKNOWN"; - } -} - - -/** - * wpa_key_mgmt_txt - Convert key management suite to a text string - * @key_mgmt: Key management suite (WPA_KEY_MGMT_* enum) - * @proto: WPA/WPA2 version (WPA_PROTO_*) - * Returns: Pointer to a text string of the key management suite name - */ -static const char * wpa_key_mgmt_txt(int key_mgmt, int proto) -{ - switch (key_mgmt) { - case WPA_KEY_MGMT_IEEE8021X: - return proto == WPA_PROTO_RSN ? - "WPA2/IEEE 802.1X/EAP" : "WPA/IEEE 802.1X/EAP"; - case WPA_KEY_MGMT_PSK: - return proto == WPA_PROTO_RSN ? - "WPA2-PSK" : "WPA-PSK"; - case WPA_KEY_MGMT_NONE: - return "NONE"; - case WPA_KEY_MGMT_IEEE8021X_NO_WPA: - return "IEEE 802.1X (no WPA)"; -#ifdef CONFIG_IEEE80211R - case WPA_KEY_MGMT_FT_IEEE8021X: - return "FT-EAP"; - case WPA_KEY_MGMT_FT_PSK: - return "FT-PSK"; -#endif /* CONFIG_IEEE80211R */ -#ifdef CONFIG_IEEE80211W - case WPA_KEY_MGMT_IEEE8021X_SHA256: - return "WPA2-EAP-SHA256"; - case WPA_KEY_MGMT_PSK_SHA256: - return "WPA2-PSK-SHA256"; -#endif /* CONFIG_IEEE80211W */ - default: - return "UNKNOWN"; - } -} /** @@ -119,11 +58,16 @@ void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, MAC2STR(dest)); } } - if (key_mic) - wpa_eapol_key_mic(kck, ver, msg, msg_len, key_mic); + if (key_mic && + wpa_eapol_key_mic(kck, ver, msg, msg_len, key_mic)) { + wpa_printf(MSG_ERROR, "WPA: Failed to generate EAPOL-Key " + "version %d MIC", ver); + goto out; + } wpa_hexdump(MSG_MSGDUMP, "WPA: TX EAPOL-Key", msg, msg_len); wpa_sm_ether_send(sm, dest, proto, msg, msg_len); eapol_sm_notify_tx_eapol_key(sm->eapol); +out: os_free(msg); } @@ -246,9 +190,11 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, wpa_hexdump_key(MSG_DEBUG, "WPA: PMK from EAPOL state " "machines", sm->pmk, pmk_len); sm->pmk_len = pmk_len; - pmksa_cache_add(sm->pmksa, sm->pmk, pmk_len, src_addr, - sm->own_addr, sm->network_ctx, - sm->key_mgmt); + if (sm->proto == WPA_PROTO_RSN) { + pmksa_cache_add(sm->pmksa, sm->pmk, pmk_len, + src_addr, sm->own_addr, + sm->network_ctx, sm->key_mgmt); + } if (!sm->cur_pmksa && pmkid && pmksa_cache_get(sm->pmksa, src_addr, pmkid)) { wpa_printf(MSG_DEBUG, "RSN: the new PMK " @@ -256,10 +202,10 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, abort_cached = 0; } } else { - wpa_msg(sm->ctx->ctx, MSG_WARNING, + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to get master session key from " "EAPOL state machines"); - wpa_msg(sm->ctx->ctx, MSG_WARNING, + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Key handshake aborted"); if (sm->cur_pmksa) { wpa_printf(MSG_DEBUG, "RSN: Cancelled PMKSA " @@ -285,6 +231,7 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm, wpa_sm_ether_send(sm, sm->bssid, ETH_P_EAPOL, buf, buflen); os_free(buf); + return -2; } return -1; @@ -315,6 +262,7 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, size_t rlen; struct wpa_eapol_key *reply; u8 *rbuf; + u8 *rsn_ie_buf = NULL; if (wpa_ie == NULL) { wpa_printf(MSG_WARNING, "WPA: No wpa_ie set - cannot " @@ -322,13 +270,46 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, return -1; } +#ifdef CONFIG_IEEE80211R + if (wpa_key_mgmt_ft(sm->key_mgmt)) { + int res; + + /* + * Add PMKR1Name into RSN IE (PMKID-List) and add MDIE and + * FTIE from (Re)Association Response. + */ + rsn_ie_buf = os_malloc(wpa_ie_len + 2 + 2 + PMKID_LEN + + sm->assoc_resp_ies_len); + if (rsn_ie_buf == NULL) + return -1; + os_memcpy(rsn_ie_buf, wpa_ie, wpa_ie_len); + res = wpa_insert_pmkid(rsn_ie_buf, wpa_ie_len, + sm->pmk_r1_name); + if (res < 0) { + os_free(rsn_ie_buf); + return -1; + } + wpa_ie_len += res; + + if (sm->assoc_resp_ies) { + os_memcpy(rsn_ie_buf + wpa_ie_len, sm->assoc_resp_ies, + sm->assoc_resp_ies_len); + wpa_ie_len += sm->assoc_resp_ies_len; + } + + wpa_ie = rsn_ie_buf; + } +#endif /* CONFIG_IEEE80211R */ + wpa_hexdump(MSG_DEBUG, "WPA: WPA IE for msg 2/4", wpa_ie, wpa_ie_len); rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL, sizeof(*reply) + wpa_ie_len, &rlen, (void *) &reply); - if (rbuf == NULL) + if (rbuf == NULL) { + os_free(rsn_ie_buf); return -1; + } reply->type = sm->proto == WPA_PROTO_RSN ? EAPOL_KEY_TYPE_RSN : EAPOL_KEY_TYPE_WPA; @@ -343,6 +324,7 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst, WPA_PUT_BE16(reply->key_data_length, wpa_ie_len); os_memcpy(reply + 1, wpa_ie, wpa_ie_len); + os_free(rsn_ie_buf); os_memcpy(reply->key_nonce, nonce, WPA_NONCE_LEN); @@ -380,6 +362,7 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, struct wpa_eapol_ie_parse ie; struct wpa_ptk *ptk; u8 buf[8]; + int res; if (wpa_sm_get_network_ctx(sm) == NULL) { wpa_printf(MSG_WARNING, "WPA: No SSID info found (msg 1 of " @@ -407,12 +390,18 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm, } #endif /* CONFIG_NO_WPA2 */ - if (wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid)) + res = wpa_supplicant_get_pmk(sm, src_addr, ie.pmkid); + if (res == -2) { + wpa_printf(MSG_DEBUG, "RSN: Do not reply to msg 1/4 - " + "requesting full EAP authentication"); + return; + } + if (res) goto failed; if (sm->renew_snonce) { if (os_get_random(sm->snonce, WPA_NONCE_LEN)) { - wpa_msg(sm->ctx->ctx, MSG_WARNING, + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: Failed to get random data for SNonce"); goto failed; } @@ -454,7 +443,8 @@ static void wpa_sm_start_preauth(void *eloop_ctx, void *timeout_ctx) static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm, const u8 *addr, int secure) { - wpa_msg(sm->ctx->ctx, MSG_INFO, "WPA: Key negotiation completed with " + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, + "WPA: Key negotiation completed with " MACSTR " [PTK=%s GTK=%s]", MAC2STR(addr), wpa_cipher_txt(sm->pairwise_cipher), wpa_cipher_txt(sm->group_cipher)); @@ -487,7 +477,7 @@ static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm, #ifdef CONFIG_IEEE80211R if (wpa_key_mgmt_ft(sm->key_mgmt)) { /* Prepare for the next transition */ - wpa_ft_prepare_auth_request(sm); + wpa_ft_prepare_auth_request(sm, NULL); } #endif /* CONFIG_IEEE80211R */ } @@ -505,7 +495,7 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, const struct wpa_eapol_key *key) { int keylen, rsclen; - wpa_alg alg; + enum wpa_alg alg; const u8 *key_rsc; u8 null_rsc[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; @@ -559,7 +549,8 @@ static int wpa_supplicant_install_ptk(struct wpa_sm *sm, static int wpa_supplicant_check_group_cipher(int group_cipher, int keylen, int maxkeylen, - int *key_rsc_len, wpa_alg *alg) + int *key_rsc_len, + enum wpa_alg *alg) { int ret = 0; @@ -613,7 +604,7 @@ static int wpa_supplicant_check_group_cipher(int group_cipher, struct wpa_gtk_data { - wpa_alg alg; + enum wpa_alg alg; int tx, key_rsc_len, keyidx; u8 gtk[32]; int gtk_len; @@ -774,7 +765,7 @@ static void wpa_report_ie_mismatch(struct wpa_sm *sm, const u8 *wpa_ie, size_t wpa_ie_len, const u8 *rsn_ie, size_t rsn_ie_len) { - wpa_msg(sm->ctx->ctx, MSG_WARNING, "WPA: %s (src=" MACSTR ")", + wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "WPA: %s (src=" MACSTR ")", reason, MAC2STR(src_addr)); if (sm->ap_wpa_ie) { @@ -807,6 +798,135 @@ static void wpa_report_ie_mismatch(struct wpa_sm *sm, } +#ifdef CONFIG_IEEE80211R + +static int ft_validate_mdie(struct wpa_sm *sm, + const unsigned char *src_addr, + struct wpa_eapol_ie_parse *ie, + const u8 *assoc_resp_mdie) +{ + struct rsn_mdie *mdie; + + mdie = (struct rsn_mdie *) (ie->mdie + 2); + if (ie->mdie == NULL || ie->mdie_len < 2 + sizeof(*mdie) || + os_memcmp(mdie->mobility_domain, sm->mobility_domain, + MOBILITY_DOMAIN_ID_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: MDIE in msg 3/4 did not " + "match with the current mobility domain"); + return -1; + } + + if (assoc_resp_mdie && + (assoc_resp_mdie[1] != ie->mdie[1] || + os_memcmp(assoc_resp_mdie, ie->mdie, 2 + ie->mdie[1]) != 0)) { + wpa_printf(MSG_DEBUG, "FT: MDIE mismatch"); + wpa_hexdump(MSG_DEBUG, "FT: MDIE in EAPOL-Key msg 3/4", + ie->mdie, 2 + ie->mdie[1]); + wpa_hexdump(MSG_DEBUG, "FT: MDIE in (Re)Association Response", + assoc_resp_mdie, 2 + assoc_resp_mdie[1]); + return -1; + } + + return 0; +} + + +static int ft_validate_ftie(struct wpa_sm *sm, + const unsigned char *src_addr, + struct wpa_eapol_ie_parse *ie, + const u8 *assoc_resp_ftie) +{ + if (ie->ftie == NULL) { + wpa_printf(MSG_DEBUG, "FT: No FTIE in EAPOL-Key msg 3/4"); + return -1; + } + + if (assoc_resp_ftie == NULL) + return 0; + + if (assoc_resp_ftie[1] != ie->ftie[1] || + os_memcmp(assoc_resp_ftie, ie->ftie, 2 + ie->ftie[1]) != 0) { + wpa_printf(MSG_DEBUG, "FT: FTIE mismatch"); + wpa_hexdump(MSG_DEBUG, "FT: FTIE in EAPOL-Key msg 3/4", + ie->ftie, 2 + ie->ftie[1]); + wpa_hexdump(MSG_DEBUG, "FT: FTIE in (Re)Association Response", + assoc_resp_ftie, 2 + assoc_resp_ftie[1]); + return -1; + } + + return 0; +} + + +static int ft_validate_rsnie(struct wpa_sm *sm, + const unsigned char *src_addr, + struct wpa_eapol_ie_parse *ie) +{ + struct wpa_ie_data rsn; + + if (!ie->rsn_ie) + return 0; + + /* + * Verify that PMKR1Name from EAPOL-Key message 3/4 + * matches with the value we derived. + */ + if (wpa_parse_wpa_ie_rsn(ie->rsn_ie, ie->rsn_ie_len, &rsn) < 0 || + rsn.num_pmkid != 1 || rsn.pmkid == NULL) { + wpa_printf(MSG_DEBUG, "FT: No PMKR1Name in " + "FT 4-way handshake message 3/4"); + return -1; + } + + if (os_memcmp(rsn.pmkid, sm->pmk_r1_name, WPA_PMK_NAME_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: PMKR1Name mismatch in " + "FT 4-way handshake message 3/4"); + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name from Authenticator", + rsn.pmkid, WPA_PMK_NAME_LEN); + wpa_hexdump(MSG_DEBUG, "FT: Derived PMKR1Name", + sm->pmk_r1_name, WPA_PMK_NAME_LEN); + return -1; + } + + return 0; +} + + +static int wpa_supplicant_validate_ie_ft(struct wpa_sm *sm, + const unsigned char *src_addr, + struct wpa_eapol_ie_parse *ie) +{ + const u8 *pos, *end, *mdie = NULL, *ftie = NULL; + + if (sm->assoc_resp_ies) { + pos = sm->assoc_resp_ies; + end = pos + sm->assoc_resp_ies_len; + while (pos + 2 < end) { + if (pos + 2 + pos[1] > end) + break; + switch (*pos) { + case WLAN_EID_MOBILITY_DOMAIN: + mdie = pos; + break; + case WLAN_EID_FAST_BSS_TRANSITION: + ftie = pos; + break; + } + pos += 2 + pos[1]; + } + } + + if (ft_validate_mdie(sm, src_addr, ie, mdie) < 0 || + ft_validate_ftie(sm, src_addr, ie, ftie) < 0 || + ft_validate_rsnie(sm, src_addr, ie) < 0) + return -1; + + return 0; +} + +#endif /* CONFIG_IEEE80211R */ + + static int wpa_supplicant_validate_ie(struct wpa_sm *sm, const unsigned char *src_addr, struct wpa_eapol_ie_parse *ie) @@ -836,8 +956,9 @@ static int wpa_supplicant_validate_ie(struct wpa_sm *sm, (ie->wpa_ie_len != sm->ap_wpa_ie_len || os_memcmp(ie->wpa_ie, sm->ap_wpa_ie, ie->wpa_ie_len) != 0)) || (ie->rsn_ie && sm->ap_rsn_ie && - (ie->rsn_ie_len != sm->ap_rsn_ie_len || - os_memcmp(ie->rsn_ie, sm->ap_rsn_ie, ie->rsn_ie_len) != 0))) { + wpa_compare_rsn_ie(wpa_key_mgmt_ft(sm->key_mgmt), + sm->ap_rsn_ie, sm->ap_rsn_ie_len, + ie->rsn_ie, ie->rsn_ie_len))) { wpa_report_ie_mismatch(sm, "IE in 3/4 msg does not match " "with IE in Beacon/ProbeResp", src_addr, ie->wpa_ie, ie->wpa_ie_len, @@ -857,19 +978,9 @@ static int wpa_supplicant_validate_ie(struct wpa_sm *sm, } #ifdef CONFIG_IEEE80211R - if (wpa_key_mgmt_ft(sm->key_mgmt)) { - struct rsn_mdie *mdie; - /* TODO: verify that full MDIE matches with the one from scan - * results, not only mobility domain */ - mdie = (struct rsn_mdie *) (ie->mdie + 2); - if (ie->mdie == NULL || ie->mdie_len < 2 + sizeof(*mdie) || - os_memcmp(mdie->mobility_domain, sm->mobility_domain, - MOBILITY_DOMAIN_ID_LEN) != 0) { - wpa_printf(MSG_DEBUG, "FT: MDIE in msg 3/4 did not " - "match with the current mobility domain"); - return -1; - } - } + if (wpa_key_mgmt_ft(sm->key_mgmt) && + wpa_supplicant_validate_ie_ft(sm, src_addr, ie) < 0) + return -1; #endif /* CONFIG_IEEE80211R */ return 0; @@ -1128,7 +1239,10 @@ static int wpa_supplicant_process_1_of_2_wpa(struct wpa_sm *sm, return -1; } os_memcpy(gd->gtk, key + 1, keydatalen); - rc4_skip(ek, 32, 256, gd->gtk, keydatalen); + if (rc4_skip(ek, 32, 256, gd->gtk, keydatalen)) { + wpa_printf(MSG_ERROR, "WPA: RC4 failed"); + return -1; + } } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) { if (keydatalen % 8) { wpa_printf(MSG_WARNING, "WPA: Unsupported AES-WRAP " @@ -1233,7 +1347,7 @@ static void wpa_supplicant_process_1_of_2(struct wpa_sm *sm, goto failed; if (rekey) { - wpa_msg(sm->ctx->ctx, MSG_INFO, "WPA: Group rekeying " + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Group rekeying " "completed with " MACSTR " [GTK=%s]", MAC2STR(sm->bssid), wpa_cipher_txt(sm->group_cipher)); wpa_sm_cancel_auth_timeout(sm); @@ -1319,7 +1433,10 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm, u8 ek[32]; os_memcpy(ek, key->key_iv, 16); os_memcpy(ek + 16, sm->ptk.kek, 16); - rc4_skip(ek, 32, 256, (u8 *) (key + 1), keydatalen); + if (rc4_skip(ek, 32, 256, (u8 *) (key + 1), keydatalen)) { + wpa_printf(MSG_ERROR, "WPA: RC4 failed"); + return -1; + } } else if (ver == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES || ver == WPA_KEY_INFO_TYPE_AES_128_CMAC) { u8 *buf; @@ -1605,7 +1722,7 @@ int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, extra_len = data_len - sizeof(*hdr) - sizeof(*key); if (WPA_GET_BE16(key->key_data_length) > extra_len) { - wpa_msg(sm->ctx->ctx, MSG_INFO, "WPA: Invalid EAPOL-Key " + wpa_msg(sm->ctx->msg_ctx, MSG_INFO, "WPA: Invalid EAPOL-Key " "frame - key_data overflow (%d > %lu)", WPA_GET_BE16(key->key_data_length), (unsigned long) extra_len); @@ -1855,6 +1972,7 @@ struct wpa_sm * wpa_sm_init(struct wpa_sm_ctx *ctx) sm = os_zalloc(sizeof(*sm)); if (sm == NULL) return NULL; + dl_list_init(&sm->pmksa_candidates); sm->renew_snonce = 1; sm->ctx = ctx; @@ -1890,6 +2008,9 @@ void wpa_sm_deinit(struct wpa_sm *sm) os_free(sm->ap_rsn_ie); os_free(sm->ctx); peerkey_deinit(sm); +#ifdef CONFIG_IEEE80211R + os_free(sm->assoc_resp_ies); +#endif /* CONFIG_IEEE80211R */ os_free(sm); } @@ -1919,10 +2040,15 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid) #ifdef CONFIG_IEEE80211R if (wpa_ft_is_completed(sm)) { + /* + * Clear portValid to kick EAPOL state machine to re-enter + * AUTHENTICATED state to get the EAPOL port Authorized. + */ + eapol_sm_notify_portValid(sm->eapol, FALSE); wpa_supplicant_key_neg_complete(sm, sm->bssid, 1); /* Prepare for the next transition */ - wpa_ft_prepare_auth_request(sm); + wpa_ft_prepare_auth_request(sm, NULL); clear_ptk = 0; } @@ -2164,6 +2290,9 @@ int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param, case WPA_PARAM_RSN_ENABLED: sm->rsn_enabled = value; break; + case WPA_PARAM_MFP: + sm->mfp = value; + break; default: break; } @@ -2406,3 +2535,32 @@ int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data) return -2; return 0; } + + +int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len) +{ +#ifndef CONFIG_NO_WPA2 + return pmksa_cache_list(sm->pmksa, buf, len); +#else /* CONFIG_NO_WPA2 */ + return -1; +#endif /* CONFIG_NO_WPA2 */ +} + + +void wpa_sm_drop_sa(struct wpa_sm *sm) +{ + wpa_printf(MSG_DEBUG, "WPA: Clear old PMK and PTK"); + sm->ptk_set = 0; + sm->tptk_set = 0; + os_memset(sm->pmk, 0, sizeof(sm->pmk)); + os_memset(&sm->ptk, 0, sizeof(sm->ptk)); + os_memset(&sm->tptk, 0, sizeof(sm->tptk)); +} + + +int wpa_sm_has_ptk(struct wpa_sm *sm) +{ + if (sm == NULL) + return 0; + return sm->ptk_set; +} diff --git a/contrib/wpa/src/rsn_supp/wpa.h b/contrib/wpa/src/rsn_supp/wpa.h index bdf7785..f1a5554 100644 --- a/contrib/wpa/src/rsn_supp/wpa.h +++ b/contrib/wpa/src/rsn_supp/wpa.h @@ -15,17 +15,9 @@ #ifndef WPA_H #define WPA_H -#include "defs.h" -#include "eapol_common.h" -#include "wpa_common.h" - -#ifndef ETH_P_EAPOL -#define ETH_P_EAPOL 0x888e -#endif - -#ifndef ETH_P_RSN_PREAUTH -#define ETH_P_RSN_PREAUTH 0x88c7 -#endif +#include "common/defs.h" +#include "common/eapol_common.h" +#include "common/wpa_common.h" struct wpa_sm; struct eapol_sm; @@ -33,12 +25,13 @@ struct wpa_config_blob; struct wpa_sm_ctx { void *ctx; /* pointer to arbitrary upper level context */ + void *msg_ctx; /* upper level context for wpa_msg() calls */ - void (*set_state)(void *ctx, wpa_states state); - wpa_states (*get_state)(void *ctx); + void (*set_state)(void *ctx, enum wpa_states state); + enum wpa_states (*get_state)(void *ctx); void (*deauthenticate)(void * ctx, int reason_code); void (*disassociate)(void *ctx, int reason_code); - int (*set_key)(void *ctx, wpa_alg alg, + int (*set_key)(void *ctx, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len); @@ -61,6 +54,7 @@ struct wpa_sm_ctx { size_t ies_len); int (*send_ft_action)(void *ctx, u8 action, const u8 *target_ap, const u8 *ies, size_t ies_len); + int (*mark_authenticated)(void *ctx, const u8 *target_ap); }; @@ -73,7 +67,8 @@ enum wpa_sm_conf_params { WPA_PARAM_GROUP, WPA_PARAM_KEY_MGMT, WPA_PARAM_MGMT_GROUP, - WPA_PARAM_RSN_ENABLED + WPA_PARAM_RSN_ENABLED, + WPA_PARAM_MFP }; struct rsn_supp_config { @@ -127,6 +122,9 @@ void wpa_sm_aborted_cached(struct wpa_sm *sm); int wpa_sm_rx_eapol(struct wpa_sm *sm, const u8 *src_addr, const u8 *buf, size_t len); int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, struct wpa_ie_data *data); +int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, size_t len); +void wpa_sm_drop_sa(struct wpa_sm *sm); +int wpa_sm_has_ptk(struct wpa_sm *sm); #else /* CONFIG_NO_WPA */ @@ -258,6 +256,21 @@ static inline int wpa_sm_parse_own_wpa_ie(struct wpa_sm *sm, return -1; } +static inline int wpa_sm_pmksa_cache_list(struct wpa_sm *sm, char *buf, + size_t len) +{ + return -1; +} + +static inline void wpa_sm_drop_sa(struct wpa_sm *sm) +{ +} + +static inline int wpa_sm_has_ptk(struct wpa_sm *sm) +{ + return 0; +} + #endif /* CONFIG_NO_WPA */ #ifdef CONFIG_PEERKEY @@ -271,27 +284,27 @@ static inline int wpa_sm_stkstart(struct wpa_sm *sm, const u8 *peer) #ifdef CONFIG_IEEE80211R -int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *mobility_domain, - const u8 *r0kh_id, size_t r0kh_id_len, - const u8 *r1kh_id); -int wpa_ft_prepare_auth_request(struct wpa_sm *sm); +int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len); +int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie); int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, - int ft_action, const u8 *target_ap); + int ft_action, const u8 *target_ap, + const u8 *ric_ies, size_t ric_ies_len); int wpa_ft_is_completed(struct wpa_sm *sm); int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, size_t ies_len, const u8 *src_addr); -int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap); +int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap, + const u8 *mdie); #else /* CONFIG_IEEE80211R */ static inline int -wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *mobility_domain, - const u8 *r0kh_id, const u8 *r1kh_id) +wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len) { return 0; } -static inline int wpa_ft_prepare_auth_request(struct wpa_sm *sm) +static inline int wpa_ft_prepare_auth_request(struct wpa_sm *sm, + const u8 *mdie) { return 0; } diff --git a/contrib/wpa/src/rsn_supp/wpa_ft.c b/contrib/wpa/src/rsn_supp/wpa_ft.c index 557b311..23063bc 100644 --- a/contrib/wpa/src/rsn_supp/wpa_ft.c +++ b/contrib/wpa/src/rsn_supp/wpa_ft.c @@ -15,19 +15,44 @@ #include "includes.h" #include "common.h" +#include "crypto/aes_wrap.h" +#include "common/ieee802_11_defs.h" +#include "common/ieee802_11_common.h" #include "wpa.h" #include "wpa_i.h" #include "wpa_ie.h" -#include "aes_wrap.h" -#include "ieee802_11_defs.h" #ifdef CONFIG_IEEE80211R +struct wpa_ft_ies { + const u8 *mdie; + size_t mdie_len; + const u8 *ftie; + size_t ftie_len; + const u8 *r1kh_id; + const u8 *gtk; + size_t gtk_len; + const u8 *r0kh_id; + size_t r0kh_id_len; + const u8 *rsn; + size_t rsn_len; + const u8 *rsn_pmkid; + const u8 *tie; + size_t tie_len; + const u8 *igtk; + size_t igtk_len; + const u8 *ric; + size_t ric_len; +}; + +static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, + struct wpa_ft_ies *parse); + + int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, const struct wpa_eapol_key *key, struct wpa_ptk *ptk, size_t ptk_len) { - u8 pmk_r1_name[WPA_PMK_NAME_LEN]; u8 ptk_name[WPA_PMK_NAME_LEN]; const u8 *anonce = key->key_nonce; @@ -45,11 +70,12 @@ int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, wpa_hexdump(MSG_DEBUG, "FT: PMKR0Name", sm->pmk_r0_name, WPA_PMK_NAME_LEN); wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_name, sm->r1kh_id, - sm->own_addr, sm->pmk_r1, pmk_r1_name); + sm->own_addr, sm->pmk_r1, sm->pmk_r1_name); wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, PMK_LEN); - wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", pmk_r1_name, WPA_PMK_NAME_LEN); + wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name", sm->pmk_r1_name, + WPA_PMK_NAME_LEN); wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->snonce, anonce, sm->own_addr, - sm->bssid, pmk_r1_name, + sm->bssid, sm->pmk_r1_name, (u8 *) ptk, ptk_len, ptk_name); wpa_hexdump_key(MSG_DEBUG, "FT: PTK", (u8 *) ptk, ptk_len); wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); @@ -61,31 +87,40 @@ int wpa_derive_ptk_ft(struct wpa_sm *sm, const unsigned char *src_addr, /** * wpa_sm_set_ft_params - Set FT (IEEE 802.11r) parameters * @sm: Pointer to WPA state machine data from wpa_sm_init() - * @mobility_domain: Mobility domain identifier (2 octets) - * @r0kh_id: PMK-R0 key holder identity (1-48 octets) - * @r0kh_id_len: R0KH-ID length (1-48) - * @r1kh_id: PMK-R1 key holder identity (16 octets) + * @ies: Association Response IEs or %NULL to clear FT parameters + * @ies_len: Length of ies buffer in octets * Returns: 0 on success, -1 on failure */ -int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *mobility_domain, - const u8 *r0kh_id, size_t r0kh_id_len, - const u8 *r1kh_id) +int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *ies, size_t ies_len) { - if (sm && mobility_domain) { + struct wpa_ft_ies ft; + + if (sm == NULL) + return 0; + + if (wpa_ft_parse_ies(ies, ies_len, &ft) < 0) + return -1; + + if (ft.mdie && ft.mdie_len < MOBILITY_DOMAIN_ID_LEN + 1) + return -1; + + if (ft.mdie) { wpa_hexdump(MSG_DEBUG, "FT: Mobility domain", - mobility_domain, MOBILITY_DOMAIN_ID_LEN); - os_memcpy(sm->mobility_domain, mobility_domain, + ft.mdie, MOBILITY_DOMAIN_ID_LEN); + os_memcpy(sm->mobility_domain, ft.mdie, MOBILITY_DOMAIN_ID_LEN); - } else if (sm) + sm->mdie_ft_capab = ft.mdie[MOBILITY_DOMAIN_ID_LEN]; + wpa_printf(MSG_DEBUG, "FT: Capability and Policy: 0x%02x", + sm->mdie_ft_capab); + } else os_memset(sm->mobility_domain, 0, MOBILITY_DOMAIN_ID_LEN); - if (sm && r0kh_id) { - if (r0kh_id_len > FT_R0KH_ID_MAX_LEN) - return -1; - wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", r0kh_id, r0kh_id_len); - os_memcpy(sm->r0kh_id, r0kh_id, r0kh_id_len); - sm->r0kh_id_len = r0kh_id_len; - } else if (sm) { + if (ft.r0kh_id) { + wpa_hexdump(MSG_DEBUG, "FT: R0KH-ID", + ft.r0kh_id, ft.r0kh_id_len); + os_memcpy(sm->r0kh_id, ft.r0kh_id, ft.r0kh_id_len); + sm->r0kh_id_len = ft.r0kh_id_len; + } else { /* FIX: When should R0KH-ID be cleared? We need to keep the * old R0KH-ID in order to be able to use this during FT. */ /* @@ -94,31 +129,55 @@ int wpa_sm_set_ft_params(struct wpa_sm *sm, const u8 *mobility_domain, */ } - if (sm && r1kh_id) { - wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", r1kh_id, FT_R1KH_ID_LEN); - os_memcpy(sm->r1kh_id, r1kh_id, FT_R1KH_ID_LEN); - } else if (sm) + if (ft.r1kh_id) { + wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", + ft.r1kh_id, FT_R1KH_ID_LEN); + os_memcpy(sm->r1kh_id, ft.r1kh_id, FT_R1KH_ID_LEN); + } else os_memset(sm->r1kh_id, 0, FT_R1KH_ID_LEN); + os_free(sm->assoc_resp_ies); + sm->assoc_resp_ies = os_malloc(ft.mdie_len + 2 + ft.ftie_len + 2); + if (sm->assoc_resp_ies) { + u8 *pos = sm->assoc_resp_ies; + if (ft.mdie) { + os_memcpy(pos, ft.mdie - 2, ft.mdie_len + 2); + pos += ft.mdie_len + 2; + } + if (ft.ftie) { + os_memcpy(pos, ft.ftie - 2, ft.ftie_len + 2); + pos += ft.ftie_len + 2; + } + sm->assoc_resp_ies_len = pos - sm->assoc_resp_ies; + wpa_hexdump(MSG_DEBUG, "FT: Stored MDIE and FTIE from " + "(Re)Association Response", + sm->assoc_resp_ies, sm->assoc_resp_ies_len); + } + return 0; } /** - * wpa_ft_gen_req_ies - Generate FT (IEEE 802.11r) IEs for Auth Request + * wpa_ft_gen_req_ies - Generate FT (IEEE 802.11r) IEs for Auth/ReAssoc Request * @sm: Pointer to WPA state machine data from wpa_sm_init() * @len: Buffer for returning the length of the IEs * @anonce: ANonce or %NULL if not yet available * @pmk_name: PMKR0Name or PMKR1Name to be added into the RSN IE PMKID List * @kck: 128-bit KCK for MIC or %NULL if no MIC is used * @target_ap: Target AP address + * @ric_ies: Optional IE(s), e.g., WMM TSPEC(s), for RIC-Request or %NULL + * @ric_ies_len: Length of ric_ies buffer in octets + * @ap_mdie: Mobility Domain IE from the target AP * Returns: Pointer to buffer with IEs or %NULL on failure * * Caller is responsible for freeing the returned buffer with os_free(); */ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, const u8 *anonce, const u8 *pmk_name, - const u8 *kck, const u8 *target_ap) + const u8 *kck, const u8 *target_ap, + const u8 *ric_ies, size_t ric_ies_len, + const u8 *ap_mdie) { size_t buf_len; u8 *buf, *pos, *ftie_len, *ftie_pos; @@ -130,13 +189,13 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, sm->ft_completed = 0; buf_len = 2 + sizeof(struct rsn_mdie) + 2 + sizeof(struct rsn_ftie) + - 2 + sm->r0kh_id_len + 100; + 2 + sm->r0kh_id_len + ric_ies_len + 100; buf = os_zalloc(buf_len); if (buf == NULL) return NULL; pos = buf; - /* RSNIE[PMKR0Name] */ + /* RSNIE[PMKR0Name/PMKR1Name] */ rsnie = (struct rsn_ie_hdr *) pos; rsnie->elem_id = WLAN_EID_RSN; WPA_PUT_LE16(rsnie->version, RSN_VERSION); @@ -223,9 +282,10 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, pos += sizeof(*mdie); os_memcpy(mdie->mobility_domain, sm->mobility_domain, MOBILITY_DOMAIN_ID_LEN); - mdie->ft_capab = 0; /* FIX: copy from the target AP's MDIE */ + mdie->ft_capab = ap_mdie && ap_mdie[1] >= 3 ? ap_mdie[4] : + sm->mdie_ft_capab; - /* FTIE[SNonce, R0KH-ID] */ + /* FTIE[SNonce, [R1KH-ID,] R0KH-ID ] */ ftie_pos = pos; *pos++ = WLAN_EID_FAST_BSS_TRANSITION; ftie_len = pos++; @@ -234,6 +294,13 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, os_memcpy(ftie->snonce, sm->snonce, WPA_NONCE_LEN); if (anonce) os_memcpy(ftie->anonce, anonce, WPA_NONCE_LEN); + if (kck) { + /* R1KH-ID sub-element in third FT message */ + *pos++ = FTIE_SUBELEM_R1KH_ID; + *pos++ = FT_R1KH_ID_LEN; + os_memcpy(pos, sm->r1kh_id, FT_R1KH_ID_LEN); + pos += FT_R1KH_ID_LEN; + } /* R0KH-ID sub-element */ *pos++ = FTIE_SUBELEM_R0KH_ID; *pos++ = sm->r0kh_id_len; @@ -241,6 +308,12 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, pos += sm->r0kh_id_len; *ftie_len = pos - ftie_len - 1; + if (ric_ies) { + /* RIC Request */ + os_memcpy(pos, ric_ies, ric_ies_len); + pos += ric_ies_len; + } + if (kck) { /* * IEEE Std 802.11r-2008, 11A.8.4 @@ -253,12 +326,14 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, * FTIE (with MIC field set to 0) * RIC-Request (if present) */ - ftie->mic_control[1] = 3; /* Information element count */ + /* Information element count */ + ftie->mic_control[1] = 3 + ieee802_11_ie_count(ric_ies, + ric_ies_len); if (wpa_ft_mic(kck, sm->own_addr, target_ap, 5, ((u8 *) mdie) - 2, 2 + sizeof(*mdie), ftie_pos, 2 + *ftie_len, - (u8 *) rsnie, 2 + rsnie->len, NULL, 0, - ftie->mic) < 0) { + (u8 *) rsnie, 2 + rsnie->len, ric_ies, + ric_ies_len, ftie->mic) < 0) { wpa_printf(MSG_INFO, "FT: Failed to calculate MIC"); os_free(buf); return NULL; @@ -271,26 +346,6 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len, } -struct wpa_ft_ies { - const u8 *mdie; - size_t mdie_len; - const u8 *ftie; - size_t ftie_len; - const u8 *r1kh_id; - const u8 *gtk; - size_t gtk_len; - const u8 *r0kh_id; - size_t r0kh_id_len; - const u8 *rsn; - size_t rsn_len; - const u8 *rsn_pmkid; - const u8 *tie; - size_t tie_len; - const u8 *igtk; - size_t igtk_len; -}; - - static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len, struct wpa_ft_ies *parse) { @@ -346,6 +401,8 @@ static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, const u8 *end, *pos; struct wpa_ie_data data; int ret; + const struct rsn_ftie *ftie; + int prot_ie_count = 0; os_memset(parse, 0, sizeof(*parse)); if (ies == NULL) @@ -374,6 +431,10 @@ static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, parse->mdie_len = pos[1]; break; case WLAN_EID_FAST_BSS_TRANSITION: + if (pos[1] < sizeof(*ftie)) + return -1; + ftie = (const struct rsn_ftie *) (pos + 2); + prot_ie_count = ftie->mic_control[1]; if (wpa_ft_parse_ftie(pos + 2, pos[1], parse) < 0) return -1; break; @@ -381,11 +442,55 @@ static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, parse->tie = pos + 2; parse->tie_len = pos[1]; break; + case WLAN_EID_RIC_DATA: + if (parse->ric == NULL) + parse->ric = pos; } pos += 2 + pos[1]; } + if (prot_ie_count == 0) + return 0; /* no MIC */ + + /* + * Check that the protected IE count matches with IEs included in the + * frame. + */ + if (parse->rsn) + prot_ie_count--; + if (parse->mdie) + prot_ie_count--; + if (parse->ftie) + prot_ie_count--; + if (parse->tie) + prot_ie_count--; + if (prot_ie_count < 0) { + wpa_printf(MSG_DEBUG, "FT: Some required IEs not included in " + "the protected IE count"); + return -1; + } + + if (prot_ie_count == 0 && parse->ric) { + wpa_printf(MSG_DEBUG, "FT: RIC IE(s) in the frame, but not " + "included in protected IE count"); + return -1; + } + + /* Determine the end of the RIC IE(s) */ + pos = parse->ric; + while (pos && pos + 2 <= end && pos + 2 + pos[1] <= end && + prot_ie_count) { + prot_ie_count--; + pos += 2 + pos[1]; + } + parse->ric_len = pos - parse->ric; + if (prot_ie_count) { + wpa_printf(MSG_DEBUG, "FT: %d protected IEs missing from " + "frame", (int) prot_ie_count); + return -1; + } + return 0; } @@ -393,7 +498,7 @@ static int wpa_ft_parse_ies(const u8 *ies, size_t ies_len, static int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid) { int keylen; - wpa_alg alg; + enum wpa_alg alg; u8 null_rsc[6] = { 0, 0, 0, 0, 0, 0 }; wpa_printf(MSG_DEBUG, "FT: Installing PTK to the driver."); @@ -426,9 +531,10 @@ static int wpa_ft_install_ptk(struct wpa_sm *sm, const u8 *bssid) /** * wpa_ft_prepare_auth_request - Generate over-the-air auth request * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @mdie: Target AP MDIE * Returns: 0 on success, -1 on failure */ -int wpa_ft_prepare_auth_request(struct wpa_sm *sm) +int wpa_ft_prepare_auth_request(struct wpa_sm *sm, const u8 *mdie) { u8 *ft_ies; size_t ft_ies_len; @@ -440,7 +546,7 @@ int wpa_ft_prepare_auth_request(struct wpa_sm *sm) } ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name, - NULL, sm->bssid); + NULL, sm->bssid, NULL, 0, mdie); if (ft_ies) { wpa_sm_update_ft_ies(sm, sm->mobility_domain, ft_ies, ft_ies_len); @@ -452,7 +558,8 @@ int wpa_ft_prepare_auth_request(struct wpa_sm *sm) int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, - int ft_action, const u8 *target_ap) + int ft_action, const u8 *target_ap, + const u8 *ric_ies, size_t ric_ies_len) { u8 *ft_ies; size_t ft_ies_len, ptk_len; @@ -464,6 +571,7 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, const u8 *bssid; wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len); + wpa_hexdump(MSG_DEBUG, "FT: RIC IEs", ric_ies, ric_ies_len); if (ft_action) { if (!sm->over_the_ds_in_progress) { @@ -506,6 +614,15 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, return -1; } + if (os_memcmp(ftie->snonce, sm->snonce, WPA_NONCE_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE"); + wpa_hexdump(MSG_DEBUG, "FT: Received SNonce", + ftie->snonce, WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce", + sm->snonce, WPA_NONCE_LEN); + return -1; + } + if (parse.r0kh_id == NULL) { wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE"); return -1; @@ -538,6 +655,7 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, wpa_hexdump(MSG_DEBUG, "FT: R1KH-ID", sm->r1kh_id, FT_R1KH_ID_LEN); wpa_hexdump(MSG_DEBUG, "FT: SNonce", sm->snonce, WPA_NONCE_LEN); wpa_hexdump(MSG_DEBUG, "FT: ANonce", ftie->anonce, WPA_NONCE_LEN); + os_memcpy(sm->anonce, ftie->anonce, WPA_NONCE_LEN); wpa_derive_pmk_r1(sm->pmk_r0, sm->pmk_r0_name, sm->r1kh_id, sm->own_addr, sm->pmk_r1, sm->pmk_r1_name); wpa_hexdump_key(MSG_DEBUG, "FT: PMK-R1", sm->pmk_r1, PMK_LEN); @@ -554,26 +672,40 @@ int wpa_ft_process_response(struct wpa_sm *sm, const u8 *ies, size_t ies_len, wpa_hexdump(MSG_DEBUG, "FT: PTKName", ptk_name, WPA_PMK_NAME_LEN); ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, ftie->anonce, - sm->pmk_r1_name, sm->ptk.kck, bssid); + sm->pmk_r1_name, sm->ptk.kck, bssid, + ric_ies, ric_ies_len, + parse.mdie ? parse.mdie - 2 : NULL); if (ft_ies) { wpa_sm_update_ft_ies(sm, sm->mobility_domain, ft_ies, ft_ies_len); os_free(ft_ies); } + wpa_sm_mark_authenticated(sm, bssid); ret = wpa_ft_install_ptk(sm, bssid); + if (ret) { + /* + * Some drivers do not support key configuration when we are + * not associated with the target AP. Work around this by + * trying again after the following reassociation gets + * completed. + */ + wpa_printf(MSG_DEBUG, "FT: Failed to set PTK prior to " + "association - try again after reassociation"); + sm->set_ptk_after_assoc = 1; + } else + sm->set_ptk_after_assoc = 0; - if (ret == 0) { - sm->ft_completed = 1; - if (ft_action) { - /* TODO: trigger re-association to the Target AP; - * MLME is now doing this automatically, but it should - * really be done only if we get here successfully. */ - os_memcpy(sm->bssid, target_ap, ETH_ALEN); - } + sm->ft_completed = 1; + if (ft_action) { + /* + * The caller is expected trigger re-association with the + * Target AP. + */ + os_memcpy(sm->bssid, target_ap, ETH_ALEN); } - return ret; + return 0; } @@ -595,7 +727,7 @@ static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem, { u8 gtk[32]; int keyidx; - wpa_alg alg; + enum wpa_alg alg; size_t gtk_len, keylen, rsc_len; if (gtk_elem == NULL) { @@ -606,14 +738,14 @@ static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem, wpa_hexdump_key(MSG_DEBUG, "FT: Received GTK in Reassoc Resp", gtk_elem, gtk_elem_len); - if (gtk_elem_len < 10 + 24 || (gtk_elem_len - 10) % 8 || - gtk_elem_len - 18 > sizeof(gtk)) { + if (gtk_elem_len < 11 + 24 || (gtk_elem_len - 11) % 8 || + gtk_elem_len - 19 > sizeof(gtk)) { wpa_printf(MSG_DEBUG, "FT: Invalid GTK sub-elem " "length %lu", (unsigned long) gtk_elem_len); return -1; } - gtk_len = gtk_elem_len - 18; - if (aes_unwrap(sm->ptk.kek, gtk_len / 8, gtk_elem + 10, gtk)) { + gtk_len = gtk_elem_len - 19; + if (aes_unwrap(sm->ptk.kek, gtk_len / 8, gtk_elem + 11, gtk)) { wpa_printf(MSG_WARNING, "FT: AES unwrap failed - could not " "decrypt GTK"); return -1; @@ -651,20 +783,20 @@ static int wpa_ft_process_gtk_subelem(struct wpa_sm *sm, const u8 *gtk_elem, return -1; } - /* Key Info[1] | Key Length[1] | RSC[8] | Key[5..32]. */ + /* Key Info[2] | Key Length[1] | RSC[8] | Key[5..32]. */ - keyidx = gtk_elem[0] & 0x03; + keyidx = WPA_GET_LE16(gtk_elem) & 0x03; - if (gtk_elem[1] != keylen) { + if (gtk_elem[2] != keylen) { wpa_printf(MSG_DEBUG, "FT: GTK length mismatch: received %d " "negotiated %lu", - gtk_elem[1], (unsigned long) keylen); + gtk_elem[2], (unsigned long) keylen); return -1; } wpa_hexdump_key(MSG_DEBUG, "FT: GTK from Reassoc Resp", gtk, keylen); if (wpa_sm_set_key(sm, alg, (u8 *) "\xff\xff\xff\xff\xff\xff", - keyidx, 0, gtk_elem + 2, rsc_len, gtk, keylen) < + keyidx, 0, gtk_elem + 3, rsc_len, gtk, keylen) < 0) { wpa_printf(MSG_WARNING, "WPA: Failed to set GTK to the " "driver."); @@ -735,7 +867,7 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, struct wpa_ft_ies parse; struct rsn_mdie *mdie; struct rsn_ftie *ftie; - size_t count; + unsigned int count; u8 mic[16]; wpa_hexdump(MSG_DEBUG, "FT: Response IEs", ies, ies_len); @@ -766,6 +898,24 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, return -1; } + if (os_memcmp(ftie->snonce, sm->snonce, WPA_NONCE_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: SNonce mismatch in FTIE"); + wpa_hexdump(MSG_DEBUG, "FT: Received SNonce", + ftie->snonce, WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: Expected SNonce", + sm->snonce, WPA_NONCE_LEN); + return -1; + } + + if (os_memcmp(ftie->anonce, sm->anonce, WPA_NONCE_LEN) != 0) { + wpa_printf(MSG_DEBUG, "FT: ANonce mismatch in FTIE"); + wpa_hexdump(MSG_DEBUG, "FT: Received ANonce", + ftie->anonce, WPA_NONCE_LEN); + wpa_hexdump(MSG_DEBUG, "FT: Expected ANonce", + sm->anonce, WPA_NONCE_LEN); + return -1; + } + if (parse.r0kh_id == NULL) { wpa_printf(MSG_DEBUG, "FT: No R0KH-ID subelem in FTIE"); return -1; @@ -803,17 +953,18 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, count = 3; if (parse.tie) count++; - if (ftie->mic_control[1] != count) { - wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in FTIE (%d)", - ftie->mic_control[1]); + wpa_printf(MSG_DEBUG, "FT: Unexpected IE count in MIC " + "Control: received %u expected %u", + ftie->mic_control[1], count); return -1; } if (wpa_ft_mic(sm->ptk.kck, sm->own_addr, src_addr, 6, parse.mdie - 2, parse.mdie_len + 2, parse.ftie - 2, parse.ftie_len + 2, - parse.rsn - 2, parse.rsn_len + 2, NULL, 0, + parse.rsn - 2, parse.rsn_len + 2, + parse.ric, parse.ric_len, mic) < 0) { wpa_printf(MSG_DEBUG, "FT: Failed to calculate MIC"); return -1; @@ -834,6 +985,20 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, return -1; #endif /* CONFIG_IEEE80211W */ + if (sm->set_ptk_after_assoc) { + wpa_printf(MSG_DEBUG, "FT: Try to set PTK again now that we " + "are associated"); + if (wpa_ft_install_ptk(sm, src_addr) < 0) + return -1; + sm->set_ptk_after_assoc = 0; + } + + if (parse.ric) { + wpa_hexdump(MSG_MSGDUMP, "FT: RIC Response", + parse.ric, parse.ric_len); + /* TODO: parse response and inform driver about results */ + } + return 0; } @@ -841,9 +1006,12 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies, /** * wpa_ft_start_over_ds - Generate over-the-DS auth request * @sm: Pointer to WPA state machine data from wpa_sm_init() + * @target_ap: Target AP Address + * @mdie: Mobility Domain IE from the target AP * Returns: 0 on success, -1 on failure */ -int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap) +int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap, + const u8 *mdie) { u8 *ft_ies; size_t ft_ies_len; @@ -858,7 +1026,7 @@ int wpa_ft_start_over_ds(struct wpa_sm *sm, const u8 *target_ap) } ft_ies = wpa_ft_gen_req_ies(sm, &ft_ies_len, NULL, sm->pmk_r0_name, - NULL, target_ap); + NULL, target_ap, NULL, 0, mdie); if (ft_ies) { sm->over_the_ds_in_progress = 1; os_memcpy(sm->target_ap, target_ap, ETH_ALEN); diff --git a/contrib/wpa/src/rsn_supp/wpa_i.h b/contrib/wpa/src/rsn_supp/wpa_i.h index e0dc6bd..618c090 100644 --- a/contrib/wpa/src/rsn_supp/wpa_i.h +++ b/contrib/wpa/src/rsn_supp/wpa_i.h @@ -1,6 +1,6 @@ /* - * wpa_supplicant - Internal WPA state machine definitions - * Copyright (c) 2004-2007, Jouni Malinen <j@w1.fi> + * Internal WPA/RSN supplicant state machine definitions + * Copyright (c) 2004-2010, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -15,7 +15,8 @@ #ifndef WPA_I_H #define WPA_I_H -struct rsn_pmksa_candidate; +#include "utils/list.h" + struct wpa_peerkey; struct wpa_eapol_key; @@ -38,7 +39,7 @@ struct wpa_sm { struct rsn_pmksa_cache *pmksa; /* PMKSA cache */ struct rsn_pmksa_cache_entry *cur_pmksa; /* current PMKSA entry */ - struct rsn_pmksa_candidate *pmksa_candidates; + struct dl_list pmksa_candidates; struct l2_packet_data *l2_preauth; struct l2_packet_data *l2_preauth_br; @@ -81,6 +82,7 @@ struct wpa_sm { unsigned int mgmt_group_cipher; int rsn_enabled; /* Whether RSN is enabled in configuration */ + int mfp; /* 0 = disabled, 1 = optional, 2 = mandatory */ u8 *assoc_wpa_ie; /* Own WPA/RSN IE from (Re)AssocReq */ size_t assoc_wpa_ie_len; @@ -105,17 +107,21 @@ struct wpa_sm { int ft_completed; int over_the_ds_in_progress; u8 target_ap[ETH_ALEN]; /* over-the-DS target AP */ + int set_ptk_after_assoc; + u8 mdie_ft_capab; /* FT Capability and Policy from target AP MDIE */ + u8 *assoc_resp_ies; /* MDIE and FTIE from (Re)Association Response */ + size_t assoc_resp_ies_len; #endif /* CONFIG_IEEE80211R */ }; -static inline void wpa_sm_set_state(struct wpa_sm *sm, wpa_states state) +static inline void wpa_sm_set_state(struct wpa_sm *sm, enum wpa_states state) { WPA_ASSERT(sm->ctx->set_state); sm->ctx->set_state(sm->ctx->ctx, state); } -static inline wpa_states wpa_sm_get_state(struct wpa_sm *sm) +static inline enum wpa_states wpa_sm_get_state(struct wpa_sm *sm) { WPA_ASSERT(sm->ctx->get_state); return sm->ctx->get_state(sm->ctx->ctx); @@ -133,7 +139,7 @@ static inline void wpa_sm_disassociate(struct wpa_sm *sm, int reason_code) sm->ctx->disassociate(sm->ctx->ctx, reason_code); } -static inline int wpa_sm_set_key(struct wpa_sm *sm, wpa_alg alg, +static inline int wpa_sm_set_key(struct wpa_sm *sm, enum wpa_alg alg, const u8 *addr, int key_idx, int set_tx, const u8 *seq, size_t seq_len, const u8 *key, size_t key_len) @@ -223,6 +229,14 @@ static inline int wpa_sm_send_ft_action(struct wpa_sm *sm, u8 action, return -1; } +static inline int wpa_sm_mark_authenticated(struct wpa_sm *sm, + const u8 *target_ap) +{ + if (sm->ctx->mark_authenticated) + return sm->ctx->mark_authenticated(sm->ctx->ctx, target_ap); + return -1; +} + void wpa_eapol_key_send(struct wpa_sm *sm, const u8 *kck, int ver, const u8 *dest, u16 proto, diff --git a/contrib/wpa/src/rsn_supp/wpa_ie.c b/contrib/wpa/src/rsn_supp/wpa_ie.c index 84f2811..f447223 100644 --- a/contrib/wpa/src/rsn_supp/wpa_ie.c +++ b/contrib/wpa/src/rsn_supp/wpa_ie.c @@ -17,7 +17,7 @@ #include "common.h" #include "wpa.h" #include "pmksa_cache.h" -#include "ieee802_11_defs.h" +#include "common/ieee802_11_defs.h" #include "wpa_i.h" #include "wpa_ie.h" @@ -332,8 +332,10 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len, /* RSN Capabilities */ capab = 0; #ifdef CONFIG_IEEE80211W - if (mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) + if (sm->mfp) capab |= WPA_CAPABILITY_MFPC; + if (sm->mfp == 2) + capab |= WPA_CAPABILITY_MFPR; #endif /* CONFIG_IEEE80211W */ WPA_PUT_LE16(pos, capab); pos += 2; @@ -414,6 +416,8 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end, pos[2 + WPA_SELECTOR_LEN + 1] == 0) { ie->wpa_ie = pos; ie->wpa_ie_len = pos[1] + 2; + wpa_hexdump(MSG_DEBUG, "WPA: WPA IE in EAPOL-Key", + ie->wpa_ie, ie->wpa_ie_len); return 0; } @@ -421,6 +425,8 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end, pos[1] >= RSN_SELECTOR_LEN + PMKID_LEN && RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_PMKID) { ie->pmkid = pos + 2 + RSN_SELECTOR_LEN; + wpa_hexdump(MSG_DEBUG, "WPA: PMKID in EAPOL-Key", + pos, pos[1] + 2); return 0; } @@ -428,6 +434,8 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end, RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_GROUPKEY) { ie->gtk = pos + 2 + RSN_SELECTOR_LEN; ie->gtk_len = pos[1] - RSN_SELECTOR_LEN; + wpa_hexdump_key(MSG_DEBUG, "WPA: GTK in EAPOL-Key", + pos, pos[1] + 2); return 0; } @@ -435,6 +443,8 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end, RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_MAC_ADDR) { ie->mac_addr = pos + 2 + RSN_SELECTOR_LEN; ie->mac_addr_len = pos[1] - RSN_SELECTOR_LEN; + wpa_hexdump(MSG_DEBUG, "WPA: MAC Address in EAPOL-Key", + pos, pos[1] + 2); return 0; } @@ -443,6 +453,8 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end, RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_SMK) { ie->smk = pos + 2 + RSN_SELECTOR_LEN; ie->smk_len = pos[1] - RSN_SELECTOR_LEN; + wpa_hexdump_key(MSG_DEBUG, "WPA: SMK in EAPOL-Key", + pos, pos[1] + 2); return 0; } @@ -450,6 +462,8 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end, RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_NONCE) { ie->nonce = pos + 2 + RSN_SELECTOR_LEN; ie->nonce_len = pos[1] - RSN_SELECTOR_LEN; + wpa_hexdump(MSG_DEBUG, "WPA: Nonce in EAPOL-Key", + pos, pos[1] + 2); return 0; } @@ -457,6 +471,8 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end, RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_LIFETIME) { ie->lifetime = pos + 2 + RSN_SELECTOR_LEN; ie->lifetime_len = pos[1] - RSN_SELECTOR_LEN; + wpa_hexdump(MSG_DEBUG, "WPA: Lifetime in EAPOL-Key", + pos, pos[1] + 2); return 0; } @@ -464,6 +480,8 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end, RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_ERROR) { ie->error = pos + 2 + RSN_SELECTOR_LEN; ie->error_len = pos[1] - RSN_SELECTOR_LEN; + wpa_hexdump(MSG_DEBUG, "WPA: Error in EAPOL-Key", + pos, pos[1] + 2); return 0; } #endif /* CONFIG_PEERKEY */ @@ -473,6 +491,8 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end, RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_IGTK) { ie->igtk = pos + 2 + RSN_SELECTOR_LEN; ie->igtk_len = pos[1] - RSN_SELECTOR_LEN; + wpa_hexdump_key(MSG_DEBUG, "WPA: IGTK in EAPOL-Key", + pos, pos[1] + 2); return 0; } #endif /* CONFIG_IEEE80211W */ @@ -513,10 +533,35 @@ int wpa_supplicant_parse_ies(const u8 *buf, size_t len, if (*pos == WLAN_EID_RSN) { ie->rsn_ie = pos; ie->rsn_ie_len = pos[1] + 2; + wpa_hexdump(MSG_DEBUG, "WPA: RSN IE in EAPOL-Key", + ie->rsn_ie, ie->rsn_ie_len); #ifdef CONFIG_IEEE80211R } else if (*pos == WLAN_EID_MOBILITY_DOMAIN) { ie->mdie = pos; ie->mdie_len = pos[1] + 2; + wpa_hexdump(MSG_DEBUG, "WPA: MDIE in EAPOL-Key", + ie->mdie, ie->mdie_len); + } else if (*pos == WLAN_EID_FAST_BSS_TRANSITION) { + ie->ftie = pos; + ie->ftie_len = pos[1] + 2; + wpa_hexdump(MSG_DEBUG, "WPA: FTIE in EAPOL-Key", + ie->ftie, ie->ftie_len); + } else if (*pos == WLAN_EID_TIMEOUT_INTERVAL && pos[1] >= 5) { + if (pos[2] == WLAN_TIMEOUT_REASSOC_DEADLINE) { + ie->reassoc_deadline = pos; + wpa_hexdump(MSG_DEBUG, "WPA: Reassoc Deadline " + "in EAPOL-Key", + ie->reassoc_deadline, pos[1] + 2); + } else if (pos[2] == WLAN_TIMEOUT_KEY_LIFETIME) { + ie->key_lifetime = pos; + wpa_hexdump(MSG_DEBUG, "WPA: KeyLifetime " + "in EAPOL-Key", + ie->key_lifetime, pos[1] + 2); + } else { + wpa_hexdump(MSG_DEBUG, "WPA: Unrecognized " + "EAPOL-Key Key Data IE", + pos, 2 + pos[1]); + } #endif /* CONFIG_IEEE80211R */ } else if (*pos == WLAN_EID_VENDOR_SPECIFIC) { ret = wpa_parse_generic(pos, end, ie); diff --git a/contrib/wpa/src/rsn_supp/wpa_ie.h b/contrib/wpa/src/rsn_supp/wpa_ie.h index 17e375a..94518d8 100644 --- a/contrib/wpa/src/rsn_supp/wpa_ie.h +++ b/contrib/wpa/src/rsn_supp/wpa_ie.h @@ -42,6 +42,10 @@ struct wpa_eapol_ie_parse { #ifdef CONFIG_IEEE80211R const u8 *mdie; size_t mdie_len; + const u8 *ftie; + size_t ftie_len; + const u8 *reassoc_deadline; + const u8 *key_lifetime; #endif /* CONFIG_IEEE80211R */ }; diff --git a/contrib/wpa/src/tls/.gitignore b/contrib/wpa/src/tls/.gitignore new file mode 100644 index 0000000..d43242d --- /dev/null +++ b/contrib/wpa/src/tls/.gitignore @@ -0,0 +1 @@ +libtls.a diff --git a/contrib/wpa/src/tls/Makefile b/contrib/wpa/src/tls/Makefile index cffba62..a2da096 100644 --- a/contrib/wpa/src/tls/Makefile +++ b/contrib/wpa/src/tls/Makefile @@ -1,9 +1,37 @@ -all: - @echo Nothing to be made. +all: libtls.a clean: - for d in $(SUBDIRS); do make -C $$d clean; done - rm -f *~ *.o *.d + rm -f *~ *.o *.d libtls.a install: @echo Nothing to be made. + + +include ../lib.rules + +CFLAGS += -DCONFIG_INTERNAL_LIBTOMMATH +CFLAGS += -DCONFIG_CRYPTO_INTERNAL + +LIB_OBJS= \ + asn1.o \ + bignum.o \ + pkcs1.o \ + pkcs5.o \ + pkcs8.o \ + rsa.o \ + tlsv1_client.o \ + tlsv1_client_read.o \ + tlsv1_client_write.o \ + tlsv1_common.o \ + tlsv1_cred.o \ + tlsv1_record.o \ + tlsv1_server.o \ + tlsv1_server_read.o \ + tlsv1_server_write.o \ + x509v3.o + + +libtls.a: $(LIB_OBJS) + $(AR) crT $@ $? + +-include $(OBJS:%.o=%.d) diff --git a/contrib/wpa/src/tls/asn1.c b/contrib/wpa/src/tls/asn1.c index 96bc1ac..3391245 100644 --- a/contrib/wpa/src/tls/asn1.c +++ b/contrib/wpa/src/tls/asn1.c @@ -15,9 +15,6 @@ #include "includes.h" #include "common.h" - -#ifdef CONFIG_INTERNAL_X509 - #include "asn1.h" int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr) @@ -85,28 +82,16 @@ int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr) } -int asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid, - const u8 **next) +int asn1_parse_oid(const u8 *buf, size_t len, struct asn1_oid *oid) { - struct asn1_hdr hdr; const u8 *pos, *end; unsigned long val; u8 tmp; os_memset(oid, 0, sizeof(*oid)); - if (asn1_get_next(buf, len, &hdr) < 0 || hdr.length == 0) - return -1; - - if (hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_OID) { - wpa_printf(MSG_DEBUG, "ASN.1: Expected OID - found class %d " - "tag 0x%x", hdr.class, hdr.tag); - return -1; - } - - pos = hdr.payload; - end = hdr.payload + hdr.length; - *next = end; + pos = buf; + end = buf + len; while (pos < end) { val = 0; @@ -141,6 +126,26 @@ int asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid, } +int asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid, + const u8 **next) +{ + struct asn1_hdr hdr; + + if (asn1_get_next(buf, len, &hdr) < 0 || hdr.length == 0) + return -1; + + if (hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_OID) { + wpa_printf(MSG_DEBUG, "ASN.1: Expected OID - found class %d " + "tag 0x%x", hdr.class, hdr.tag); + return -1; + } + + *next = hdr.payload + hdr.length; + + return asn1_parse_oid(hdr.payload, hdr.length, oid); +} + + void asn1_oid_to_str(struct asn1_oid *oid, char *buf, size_t len) { char *pos = buf; @@ -205,5 +210,3 @@ unsigned long asn1_bit_string_to_long(const u8 *buf, size_t len) return val; } - -#endif /* CONFIG_INTERNAL_X509 */ diff --git a/contrib/wpa/src/tls/asn1.h b/contrib/wpa/src/tls/asn1.h index c02ada8..2ff571e 100644 --- a/contrib/wpa/src/tls/asn1.h +++ b/contrib/wpa/src/tls/asn1.h @@ -63,6 +63,7 @@ struct asn1_oid { int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr); +int asn1_parse_oid(const u8 *buf, size_t len, struct asn1_oid *oid); int asn1_get_oid(const u8 *buf, size_t len, struct asn1_oid *oid, const u8 **next); void asn1_oid_to_str(struct asn1_oid *oid, char *buf, size_t len); diff --git a/contrib/wpa/src/tls/asn1_test.c b/contrib/wpa/src/tls/asn1_test.c deleted file mode 100644 index a5c7753..0000000 --- a/contrib/wpa/src/tls/asn1_test.c +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Testing tool for ASN.1/X.509v3 routines - * Copyright (c) 2006, Jouni Malinen <j@w1.fi> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Alternatively, this software may be distributed under the terms of BSD - * license. - * - * See README and COPYING for more details. - */ - -#include "includes.h" - -#include "common.h" -#include "asn1.h" -#include "x509v3.h" - -extern int wpa_debug_level; - - -static const char * asn1_class_str(int class) -{ - switch (class) { - case ASN1_CLASS_UNIVERSAL: - return "Universal"; - case ASN1_CLASS_APPLICATION: - return "Application"; - case ASN1_CLASS_CONTEXT_SPECIFIC: - return "Context-specific"; - case ASN1_CLASS_PRIVATE: - return "Private"; - default: - return "?"; - } -} - - -int asn1_parse(const u8 *buf, size_t len, int level) -{ - const u8 *pos, *prev, *end; - char prefix[10], str[100]; - int _level; - struct asn1_hdr hdr; - struct asn1_oid oid; - u8 tmp; - - _level = level; - if ((size_t) _level > sizeof(prefix) - 1) - _level = sizeof(prefix) - 1; - memset(prefix, ' ', _level); - prefix[_level] = '\0'; - - pos = buf; - end = buf + len; - - while (pos < end) { - if (asn1_get_next(pos, end - pos, &hdr) < 0) - return -1; - - prev = pos; - pos = hdr.payload; - - wpa_printf(MSG_MSGDUMP, "ASN.1:%s Class %d(%s) P/C %d(%s) " - "Tag %u Length %u", - prefix, hdr.class, asn1_class_str(hdr.class), - hdr.constructed, - hdr.constructed ? "Constructed" : "Primitive", - hdr.tag, hdr.length); - - if (hdr.class == ASN1_CLASS_CONTEXT_SPECIFIC && - hdr.constructed) { - if (asn1_parse(pos, hdr.length, level + 1) < 0) - return -1; - pos += hdr.length; - } - - if (hdr.class != ASN1_CLASS_UNIVERSAL) - continue; - - switch (hdr.tag) { - case ASN1_TAG_EOC: - if (hdr.length) { - wpa_printf(MSG_DEBUG, "ASN.1: Non-zero " - "end-of-contents length (%u)", - hdr.length); - return -1; - } - wpa_printf(MSG_MSGDUMP, "ASN.1:%s EOC", prefix); - break; - case ASN1_TAG_BOOLEAN: - if (hdr.length != 1) { - wpa_printf(MSG_DEBUG, "ASN.1: Unexpected " - "Boolean length (%u)", hdr.length); - return -1; - } - tmp = *pos++; - wpa_printf(MSG_MSGDUMP, "ASN.1:%s Boolean %s", - prefix, tmp ? "TRUE" : "FALSE"); - break; - case ASN1_TAG_INTEGER: - wpa_hexdump(MSG_MSGDUMP, "ASN.1: INTEGER", - pos, hdr.length); - pos += hdr.length; - break; - case ASN1_TAG_BITSTRING: - wpa_hexdump(MSG_MSGDUMP, "ASN.1: BitString", - pos, hdr.length); - pos += hdr.length; - break; - case ASN1_TAG_OCTETSTRING: - wpa_hexdump(MSG_MSGDUMP, "ASN.1: OctetString", - pos, hdr.length); - pos += hdr.length; - break; - case ASN1_TAG_NULL: - if (hdr.length) { - wpa_printf(MSG_DEBUG, "ASN.1: Non-zero Null " - "length (%u)", hdr.length); - return -1; - } - wpa_printf(MSG_MSGDUMP, "ASN.1:%s Null", prefix); - break; - case ASN1_TAG_OID: - if (asn1_get_oid(prev, end - prev, &oid, &prev) < 0) { - wpa_printf(MSG_DEBUG, "ASN.1: Invalid OID"); - return -1; - } - asn1_oid_to_str(&oid, str, sizeof(str)); - wpa_printf(MSG_DEBUG, "ASN.1:%s OID %s", prefix, str); - pos += hdr.length; - break; - case ANS1_TAG_RELATIVE_OID: - wpa_hexdump(MSG_MSGDUMP, "ASN.1: Relative OID", - pos, hdr.length); - pos += hdr.length; - break; - case ASN1_TAG_SEQUENCE: - wpa_printf(MSG_MSGDUMP, "ASN.1:%s SEQUENCE", prefix); - if (asn1_parse(pos, hdr.length, level + 1) < 0) - return -1; - pos += hdr.length; - break; - case ASN1_TAG_SET: - wpa_printf(MSG_MSGDUMP, "ASN.1:%s SET", prefix); - if (asn1_parse(pos, hdr.length, level + 1) < 0) - return -1; - pos += hdr.length; - break; - case ASN1_TAG_PRINTABLESTRING: - wpa_hexdump_ascii(MSG_MSGDUMP, - "ASN.1: PrintableString", - pos, hdr.length); - pos += hdr.length; - break; - case ASN1_TAG_IA5STRING: - wpa_hexdump_ascii(MSG_MSGDUMP, "ASN.1: IA5String", - pos, hdr.length); - pos += hdr.length; - break; - case ASN1_TAG_UTCTIME: - wpa_hexdump_ascii(MSG_MSGDUMP, "ASN.1: UTCTIME", - pos, hdr.length); - pos += hdr.length; - break; - case ASN1_TAG_VISIBLESTRING: - wpa_hexdump_ascii(MSG_MSGDUMP, "ASN.1: VisibleString", - pos, hdr.length); - pos += hdr.length; - break; - default: - wpa_printf(MSG_DEBUG, "ASN.1: Unknown tag %d", - hdr.tag); - return -1; - } - } - - return 0; -} - - -int main(int argc, char *argv[]) -{ - FILE *f; - u8 buf[3000]; - size_t len; - struct x509_certificate *cert; - - wpa_debug_level = 0; - - f = fopen(argv[1], "rb"); - if (f == NULL) - return -1; - len = fread(buf, 1, sizeof(buf), f); - fclose(f); - - if (asn1_parse(buf, len, 0) < 0) - printf("Failed to parse DER ASN.1\n"); - - printf("\n\n"); - - cert = x509_certificate_parse(buf, len); - if (cert == NULL) - printf("Failed to parse X.509 certificate\n"); - x509_certificate_free(cert); - - return 0; -} diff --git a/contrib/wpa/src/tls/pkcs1.c b/contrib/wpa/src/tls/pkcs1.c new file mode 100644 index 0000000..72ebd87 --- /dev/null +++ b/contrib/wpa/src/tls/pkcs1.c @@ -0,0 +1,201 @@ +/* + * PKCS #1 (RSA Encryption) + * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "rsa.h" +#include "pkcs1.h" + + +static int pkcs1_generate_encryption_block(u8 block_type, size_t modlen, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen) +{ + size_t ps_len; + u8 *pos; + + /* + * PKCS #1 v1.5, 8.1: + * + * EB = 00 || BT || PS || 00 || D + * BT = 00 or 01 for private-key operation; 02 for public-key operation + * PS = k-3-||D||; at least eight octets + * (BT=0: PS=0x00, BT=1: PS=0xff, BT=2: PS=pseudorandom non-zero) + * k = length of modulus in octets (modlen) + */ + + if (modlen < 12 || modlen > *outlen || inlen > modlen - 11) { + wpa_printf(MSG_DEBUG, "PKCS #1: %s - Invalid buffer " + "lengths (modlen=%lu outlen=%lu inlen=%lu)", + __func__, (unsigned long) modlen, + (unsigned long) *outlen, + (unsigned long) inlen); + return -1; + } + + pos = out; + *pos++ = 0x00; + *pos++ = block_type; /* BT */ + ps_len = modlen - inlen - 3; + switch (block_type) { + case 0: + os_memset(pos, 0x00, ps_len); + pos += ps_len; + break; + case 1: + os_memset(pos, 0xff, ps_len); + pos += ps_len; + break; + case 2: + if (os_get_random(pos, ps_len) < 0) { + wpa_printf(MSG_DEBUG, "PKCS #1: %s - Failed to get " + "random data for PS", __func__); + return -1; + } + while (ps_len--) { + if (*pos == 0x00) + *pos = 0x01; + pos++; + } + break; + default: + wpa_printf(MSG_DEBUG, "PKCS #1: %s - Unsupported block type " + "%d", __func__, block_type); + return -1; + } + *pos++ = 0x00; + os_memcpy(pos, in, inlen); /* D */ + + return 0; +} + + +int pkcs1_encrypt(int block_type, struct crypto_rsa_key *key, + int use_private, const u8 *in, size_t inlen, + u8 *out, size_t *outlen) +{ + size_t modlen; + + modlen = crypto_rsa_get_modulus_len(key); + + if (pkcs1_generate_encryption_block(block_type, modlen, in, inlen, + out, outlen) < 0) + return -1; + + return crypto_rsa_exptmod(out, modlen, out, outlen, key, use_private); +} + + +int pkcs1_v15_private_key_decrypt(struct crypto_rsa_key *key, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen) +{ + int res; + u8 *pos, *end; + + res = crypto_rsa_exptmod(in, inlen, out, outlen, key, 1); + if (res) + return res; + + if (*outlen < 2 || out[0] != 0 || out[1] != 2) + return -1; + + /* Skip PS (pseudorandom non-zero octets) */ + pos = out + 2; + end = out + *outlen; + while (*pos && pos < end) + pos++; + if (pos == end) + return -1; + pos++; + + *outlen -= pos - out; + + /* Strip PKCS #1 header */ + os_memmove(out, pos, *outlen); + + return 0; +} + + +int pkcs1_decrypt_public_key(struct crypto_rsa_key *key, + const u8 *crypt, size_t crypt_len, + u8 *plain, size_t *plain_len) +{ + size_t len; + u8 *pos; + + len = *plain_len; + if (crypto_rsa_exptmod(crypt, crypt_len, plain, &len, key, 0) < 0) + return -1; + + /* + * PKCS #1 v1.5, 8.1: + * + * EB = 00 || BT || PS || 00 || D + * BT = 00 or 01 + * PS = k-3-||D|| times (00 if BT=00) or (FF if BT=01) + * k = length of modulus in octets + */ + + if (len < 3 + 8 + 16 /* min hash len */ || + plain[0] != 0x00 || (plain[1] != 0x00 && plain[1] != 0x01)) { + wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB " + "structure"); + return -1; + } + + pos = plain + 3; + if (plain[1] == 0x00) { + /* BT = 00 */ + if (plain[2] != 0x00) { + wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature " + "PS (BT=00)"); + return -1; + } + while (pos + 1 < plain + len && *pos == 0x00 && pos[1] == 0x00) + pos++; + } else { + /* BT = 01 */ + if (plain[2] != 0xff) { + wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature " + "PS (BT=01)"); + return -1; + } + while (pos < plain + len && *pos == 0xff) + pos++; + } + + if (pos - plain - 2 < 8) { + /* PKCS #1 v1.5, 8.1: At least eight octets long PS */ + wpa_printf(MSG_INFO, "LibTomCrypt: Too short signature " + "padding"); + return -1; + } + + if (pos + 16 /* min hash len */ >= plain + len || *pos != 0x00) { + wpa_printf(MSG_INFO, "LibTomCrypt: Invalid signature EB " + "structure (2)"); + return -1; + } + pos++; + len -= pos - plain; + + /* Strip PKCS #1 header */ + os_memmove(plain, pos, len); + *plain_len = len; + + return 0; +} diff --git a/contrib/wpa/src/tls/pkcs1.h b/contrib/wpa/src/tls/pkcs1.h new file mode 100644 index 0000000..68872b1 --- /dev/null +++ b/contrib/wpa/src/tls/pkcs1.h @@ -0,0 +1,28 @@ +/* + * PKCS #1 (RSA Encryption) + * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef PKCS1_H +#define PKCS1_H + +int pkcs1_encrypt(int block_type, struct crypto_rsa_key *key, + int use_private, const u8 *in, size_t inlen, + u8 *out, size_t *outlen); +int pkcs1_v15_private_key_decrypt(struct crypto_rsa_key *key, + const u8 *in, size_t inlen, + u8 *out, size_t *outlen); +int pkcs1_decrypt_public_key(struct crypto_rsa_key *key, + const u8 *crypt, size_t crypt_len, + u8 *plain, size_t *plain_len); + +#endif /* PKCS1_H */ diff --git a/contrib/wpa/src/tls/pkcs5.c b/contrib/wpa/src/tls/pkcs5.c new file mode 100644 index 0000000..4291b84 --- /dev/null +++ b/contrib/wpa/src/tls/pkcs5.c @@ -0,0 +1,238 @@ +/* + * PKCS #5 (Password-based Encryption) + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "crypto/crypto.h" +#include "crypto/md5.h" +#include "asn1.h" +#include "pkcs5.h" + + +struct pkcs5_params { + enum pkcs5_alg { + PKCS5_ALG_UNKNOWN, + PKCS5_ALG_MD5_DES_CBC + } alg; + u8 salt[8]; + size_t salt_len; + unsigned int iter_count; +}; + + +enum pkcs5_alg pkcs5_get_alg(struct asn1_oid *oid) +{ + if (oid->len == 7 && + oid->oid[0] == 1 /* iso */ && + oid->oid[1] == 2 /* member-body */ && + oid->oid[2] == 840 /* us */ && + oid->oid[3] == 113549 /* rsadsi */ && + oid->oid[4] == 1 /* pkcs */ && + oid->oid[5] == 5 /* pkcs-5 */ && + oid->oid[6] == 3 /* pbeWithMD5AndDES-CBC */) + return PKCS5_ALG_MD5_DES_CBC; + + return PKCS5_ALG_UNKNOWN; +} + + +static int pkcs5_get_params(const u8 *enc_alg, size_t enc_alg_len, + struct pkcs5_params *params) +{ + struct asn1_hdr hdr; + const u8 *enc_alg_end, *pos, *end; + struct asn1_oid oid; + char obuf[80]; + + /* AlgorithmIdentifier */ + + enc_alg_end = enc_alg + enc_alg_len; + + os_memset(params, 0, sizeof(*params)); + + if (asn1_get_oid(enc_alg, enc_alg_end - enc_alg, &oid, &pos)) { + wpa_printf(MSG_DEBUG, "PKCS #5: Failed to parse OID " + "(algorithm)"); + return -1; + } + + asn1_oid_to_str(&oid, obuf, sizeof(obuf)); + wpa_printf(MSG_DEBUG, "PKCS #5: encryption algorithm %s", obuf); + params->alg = pkcs5_get_alg(&oid); + if (params->alg == PKCS5_ALG_UNKNOWN) { + wpa_printf(MSG_INFO, "PKCS #5: unsupported encryption " + "algorithm %s", obuf); + return -1; + } + + /* + * PKCS#5, Section 8 + * PBEParameter ::= SEQUENCE { + * salt OCTET STRING SIZE(8), + * iterationCount INTEGER } + */ + + if (asn1_get_next(pos, enc_alg_end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "PKCS #5: Expected SEQUENCE " + "(PBEParameter) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + pos = hdr.payload; + end = hdr.payload + hdr.length; + + /* salt OCTET STRING SIZE(8) */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING || + hdr.length != 8) { + wpa_printf(MSG_DEBUG, "PKCS #5: Expected OCTETSTRING SIZE(8) " + "(salt) - found class %d tag 0x%x size %d", + hdr.class, hdr.tag, hdr.length); + return -1; + } + pos = hdr.payload + hdr.length; + os_memcpy(params->salt, hdr.payload, hdr.length); + params->salt_len = hdr.length; + wpa_hexdump(MSG_DEBUG, "PKCS #5: salt", + params->salt, params->salt_len); + + /* iterationCount INTEGER */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) { + wpa_printf(MSG_DEBUG, "PKCS #5: Expected INTEGER - found " + "class %d tag 0x%x", hdr.class, hdr.tag); + return -1; + } + if (hdr.length == 1) + params->iter_count = *hdr.payload; + else if (hdr.length == 2) + params->iter_count = WPA_GET_BE16(hdr.payload); + else if (hdr.length == 4) + params->iter_count = WPA_GET_BE32(hdr.payload); + else { + wpa_hexdump(MSG_DEBUG, "PKCS #5: Unsupported INTEGER value " + " (iterationCount)", + hdr.payload, hdr.length); + return -1; + } + wpa_printf(MSG_DEBUG, "PKCS #5: iterationCount=0x%x", + params->iter_count); + if (params->iter_count == 0 || params->iter_count > 0xffff) { + wpa_printf(MSG_INFO, "PKCS #5: Unsupported " + "iterationCount=0x%x", params->iter_count); + return -1; + } + + return 0; +} + + +static struct crypto_cipher * pkcs5_crypto_init(struct pkcs5_params *params, + const char *passwd) +{ + unsigned int i; + u8 hash[MD5_MAC_LEN]; + const u8 *addr[2]; + size_t len[2]; + + if (params->alg != PKCS5_ALG_MD5_DES_CBC) + return NULL; + + addr[0] = (const u8 *) passwd; + len[0] = os_strlen(passwd); + addr[1] = params->salt; + len[1] = params->salt_len; + if (md5_vector(2, addr, len, hash) < 0) + return NULL; + addr[0] = hash; + len[0] = MD5_MAC_LEN; + for (i = 1; i < params->iter_count; i++) { + if (md5_vector(1, addr, len, hash) < 0) + return NULL; + } + /* TODO: DES key parity bits(?) */ + wpa_hexdump_key(MSG_DEBUG, "PKCS #5: DES key", hash, 8); + wpa_hexdump_key(MSG_DEBUG, "PKCS #5: DES IV", hash + 8, 8); + + return crypto_cipher_init(CRYPTO_CIPHER_ALG_DES, hash + 8, hash, 8); +} + + +u8 * pkcs5_decrypt(const u8 *enc_alg, size_t enc_alg_len, + const u8 *enc_data, size_t enc_data_len, + const char *passwd, size_t *data_len) +{ + struct crypto_cipher *ctx; + u8 *eb, pad; + struct pkcs5_params params; + unsigned int i; + + if (pkcs5_get_params(enc_alg, enc_alg_len, ¶ms) < 0) { + wpa_printf(MSG_DEBUG, "PKCS #5: Unsupported parameters"); + return NULL; + } + + ctx = pkcs5_crypto_init(¶ms, passwd); + if (ctx == NULL) { + wpa_printf(MSG_DEBUG, "PKCS #5: Failed to initialize crypto"); + return NULL; + } + + /* PKCS #5, Section 7 - Decryption process */ + if (enc_data_len < 16 || enc_data_len % 8) { + wpa_printf(MSG_INFO, "PKCS #5: invalid length of ciphertext " + "%d", (int) enc_data_len); + crypto_cipher_deinit(ctx); + return NULL; + } + + eb = os_malloc(enc_data_len); + if (eb == NULL) { + crypto_cipher_deinit(ctx); + return NULL; + } + + if (crypto_cipher_decrypt(ctx, enc_data, eb, enc_data_len) < 0) { + wpa_printf(MSG_DEBUG, "PKCS #5: Failed to decrypt EB"); + crypto_cipher_deinit(ctx); + os_free(eb); + return NULL; + } + crypto_cipher_deinit(ctx); + + pad = eb[enc_data_len - 1]; + if (pad > 8) { + wpa_printf(MSG_INFO, "PKCS #5: Invalid PS octet 0x%x", pad); + os_free(eb); + return NULL; + } + for (i = enc_data_len - pad; i < enc_data_len; i++) { + if (eb[i] != pad) { + wpa_hexdump(MSG_INFO, "PKCS #5: Invalid PS", + eb + enc_data_len - pad, pad); + os_free(eb); + return NULL; + } + } + + wpa_hexdump_key(MSG_MSGDUMP, "PKCS #5: message M (encrypted key)", + eb, enc_data_len - pad); + + *data_len = enc_data_len - pad; + return eb; +} diff --git a/contrib/wpa/src/tls/pkcs5.h b/contrib/wpa/src/tls/pkcs5.h new file mode 100644 index 0000000..6ed3923 --- /dev/null +++ b/contrib/wpa/src/tls/pkcs5.h @@ -0,0 +1,22 @@ +/* + * PKCS #5 (Password-based Encryption) + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef PKCS5_H +#define PKCS5_H + +u8 * pkcs5_decrypt(const u8 *enc_alg, size_t enc_alg_len, + const u8 *enc_data, size_t enc_data_len, + const char *passwd, size_t *data_len); + +#endif /* PKCS5_H */ diff --git a/contrib/wpa/src/tls/pkcs8.c b/contrib/wpa/src/tls/pkcs8.c new file mode 100644 index 0000000..69ab262 --- /dev/null +++ b/contrib/wpa/src/tls/pkcs8.c @@ -0,0 +1,193 @@ +/* + * PKCS #8 (Private-key information syntax) + * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "asn1.h" +#include "bignum.h" +#include "rsa.h" +#include "pkcs5.h" +#include "pkcs8.h" + + +struct crypto_private_key * pkcs8_key_import(const u8 *buf, size_t len) +{ + struct asn1_hdr hdr; + const u8 *pos, *end; + struct bignum *zero; + struct asn1_oid oid; + char obuf[80]; + + /* PKCS #8, Chapter 6 */ + + /* PrivateKeyInfo ::= SEQUENCE */ + if (asn1_get_next(buf, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "PKCS #8: Does not start with PKCS #8 " + "header (SEQUENCE); assume PKCS #8 not used"); + return NULL; + } + pos = hdr.payload; + end = pos + hdr.length; + + /* version Version (Version ::= INTEGER) */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_INTEGER) { + wpa_printf(MSG_DEBUG, "PKCS #8: Expected INTEGER - found " + "class %d tag 0x%x; assume PKCS #8 not used", + hdr.class, hdr.tag); + return NULL; + } + + zero = bignum_init(); + if (zero == NULL) + return NULL; + + if (bignum_set_unsigned_bin(zero, hdr.payload, hdr.length) < 0) { + wpa_printf(MSG_DEBUG, "PKCS #8: Failed to parse INTEGER"); + bignum_deinit(zero); + return NULL; + } + pos = hdr.payload + hdr.length; + + if (bignum_cmp_d(zero, 0) != 0) { + wpa_printf(MSG_DEBUG, "PKCS #8: Expected zero INTEGER in the " + "beginning of private key; not found; assume " + "PKCS #8 not used"); + bignum_deinit(zero); + return NULL; + } + bignum_deinit(zero); + + /* privateKeyAlgorithm PrivateKeyAlgorithmIdentifier + * (PrivateKeyAlgorithmIdentifier ::= AlgorithmIdentifier) */ + if (asn1_get_next(pos, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "PKCS #8: Expected SEQUENCE " + "(AlgorithmIdentifier) - found class %d tag 0x%x; " + "assume PKCS #8 not used", + hdr.class, hdr.tag); + return NULL; + } + + if (asn1_get_oid(hdr.payload, hdr.length, &oid, &pos)) { + wpa_printf(MSG_DEBUG, "PKCS #8: Failed to parse OID " + "(algorithm); assume PKCS #8 not used"); + return NULL; + } + + asn1_oid_to_str(&oid, obuf, sizeof(obuf)); + wpa_printf(MSG_DEBUG, "PKCS #8: algorithm=%s", obuf); + + if (oid.len != 7 || + oid.oid[0] != 1 /* iso */ || + oid.oid[1] != 2 /* member-body */ || + oid.oid[2] != 840 /* us */ || + oid.oid[3] != 113549 /* rsadsi */ || + oid.oid[4] != 1 /* pkcs */ || + oid.oid[5] != 1 /* pkcs-1 */ || + oid.oid[6] != 1 /* rsaEncryption */) { + wpa_printf(MSG_DEBUG, "PKCS #8: Unsupported private key " + "algorithm %s", obuf); + return NULL; + } + + pos = hdr.payload + hdr.length; + + /* privateKey PrivateKey (PrivateKey ::= OCTET STRING) */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, "PKCS #8: Expected OCTETSTRING " + "(privateKey) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return NULL; + } + wpa_printf(MSG_DEBUG, "PKCS #8: Try to parse RSAPrivateKey"); + + return (struct crypto_private_key *) + crypto_rsa_import_private_key(hdr.payload, hdr.length); +} + + +struct crypto_private_key * +pkcs8_enc_key_import(const u8 *buf, size_t len, const char *passwd) +{ + struct asn1_hdr hdr; + const u8 *pos, *end, *enc_alg; + size_t enc_alg_len; + u8 *data; + size_t data_len; + + if (passwd == NULL) + return NULL; + + /* + * PKCS #8, Chapter 7 + * EncryptedPrivateKeyInfo ::= SEQUENCE { + * encryptionAlgorithm EncryptionAlgorithmIdentifier, + * encryptedData EncryptedData } + * EncryptionAlgorithmIdentifier ::= AlgorithmIdentifier + * EncryptedData ::= OCTET STRING + */ + + if (asn1_get_next(buf, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "PKCS #8: Does not start with PKCS #8 " + "header (SEQUENCE); assume encrypted PKCS #8 not " + "used"); + return NULL; + } + pos = hdr.payload; + end = pos + hdr.length; + + /* encryptionAlgorithm EncryptionAlgorithmIdentifier */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "PKCS #8: Expected SEQUENCE " + "(AlgorithmIdentifier) - found class %d tag 0x%x; " + "assume encrypted PKCS #8 not used", + hdr.class, hdr.tag); + return NULL; + } + enc_alg = hdr.payload; + enc_alg_len = hdr.length; + pos = hdr.payload + hdr.length; + + /* encryptedData EncryptedData */ + if (asn1_get_next(pos, end - pos, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_OCTETSTRING) { + wpa_printf(MSG_DEBUG, "PKCS #8: Expected OCTETSTRING " + "(encryptedData) - found class %d tag 0x%x", + hdr.class, hdr.tag); + return NULL; + } + + data = pkcs5_decrypt(enc_alg, enc_alg_len, hdr.payload, hdr.length, + passwd, &data_len); + if (data) { + struct crypto_private_key *key; + key = pkcs8_key_import(data, data_len); + os_free(data); + return key; + } + + return NULL; +} diff --git a/contrib/wpa/src/tls/pkcs8.h b/contrib/wpa/src/tls/pkcs8.h new file mode 100644 index 0000000..dac517c --- /dev/null +++ b/contrib/wpa/src/tls/pkcs8.h @@ -0,0 +1,22 @@ +/* + * PKCS #8 (Private-key information syntax) + * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef PKCS8_H +#define PKCS8_H + +struct crypto_private_key * pkcs8_key_import(const u8 *buf, size_t len); +struct crypto_private_key * +pkcs8_enc_key_import(const u8 *buf, size_t len, const char *passwd); + +#endif /* PKCS8_H */ diff --git a/contrib/wpa/src/tls/rsa.c b/contrib/wpa/src/tls/rsa.c index 4965a2a..3084adc 100644 --- a/contrib/wpa/src/tls/rsa.c +++ b/contrib/wpa/src/tls/rsa.c @@ -15,7 +15,6 @@ #include "includes.h" #include "common.h" -#include "crypto.h" #include "asn1.h" #include "bignum.h" #include "rsa.h" @@ -35,7 +34,6 @@ struct crypto_rsa_key { }; -#ifdef EAP_TLS_FUNCS static const u8 * crypto_rsa_parse_integer(const u8 *pos, const u8 *end, struct bignum *num) { @@ -224,7 +222,6 @@ error: crypto_rsa_free(key); return NULL; } -#endif /* EAP_TLS_FUNCS */ /** diff --git a/contrib/wpa/src/tls/tlsv1_client.c b/contrib/wpa/src/tls/tlsv1_client.c index 0bf1174..afb6031 100644 --- a/contrib/wpa/src/tls/tlsv1_client.c +++ b/contrib/wpa/src/tls/tlsv1_client.c @@ -15,8 +15,8 @@ #include "includes.h" #include "common.h" -#include "sha1.h" -#include "tls.h" +#include "crypto/sha1.h" +#include "crypto/tls.h" #include "tlsv1_common.h" #include "tlsv1_record.h" #include "tlsv1_client.h" @@ -605,7 +605,6 @@ int tlsv1_client_get_keyblock_size(struct tlsv1_client *conn) */ int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers) { -#ifdef EAP_FAST size_t count; u16 *suites; @@ -635,9 +634,6 @@ int tlsv1_client_set_cipher_list(struct tlsv1_client *conn, u8 *ciphers) } return 0; -#else /* EAP_FAST */ - return -1; -#endif /* EAP_FAST */ } diff --git a/contrib/wpa/src/tls/tlsv1_client_read.c b/contrib/wpa/src/tls/tlsv1_client_read.c index ee20330..ed3f260 100644 --- a/contrib/wpa/src/tls/tlsv1_client_read.c +++ b/contrib/wpa/src/tls/tlsv1_client_read.c @@ -15,10 +15,10 @@ #include "includes.h" #include "common.h" -#include "md5.h" -#include "sha1.h" +#include "crypto/md5.h" +#include "crypto/sha1.h" +#include "crypto/tls.h" #include "x509v3.h" -#include "tls.h" #include "tlsv1_common.h" #include "tlsv1_record.h" #include "tlsv1_client.h" diff --git a/contrib/wpa/src/tls/tlsv1_client_write.c b/contrib/wpa/src/tls/tlsv1_client_write.c index e0c95cb..b47425f 100644 --- a/contrib/wpa/src/tls/tlsv1_client_write.c +++ b/contrib/wpa/src/tls/tlsv1_client_write.c @@ -15,10 +15,10 @@ #include "includes.h" #include "common.h" -#include "md5.h" -#include "sha1.h" +#include "crypto/md5.h" +#include "crypto/sha1.h" +#include "crypto/tls.h" #include "x509v3.h" -#include "tls.h" #include "tlsv1_common.h" #include "tlsv1_record.h" #include "tlsv1_client.h" @@ -209,7 +209,6 @@ static int tls_write_client_certificate(struct tlsv1_client *conn, static int tlsv1_key_x_anon_dh(struct tlsv1_client *conn, u8 **pos, u8 *end) { -#ifdef EAP_FAST /* ClientDiffieHellmanPublic */ u8 *csecret, *csecret_start, *dh_yc, *shared; size_t csecret_len, dh_yc_len, shared_len; @@ -321,10 +320,6 @@ static int tlsv1_key_x_anon_dh(struct tlsv1_client *conn, u8 **pos, u8 *end) os_free(shared); tlsv1_client_free_dh(conn); return 0; -#else /* EAP_FAST */ - tls_alert(conn, TLS_ALERT_LEVEL_FATAL, TLS_ALERT_INTERNAL_ERROR); - return -1; -#endif /* EAP_FAST */ } diff --git a/contrib/wpa/src/tls/tlsv1_common.h b/contrib/wpa/src/tls/tlsv1_common.h index 7750564..763a4af 100644 --- a/contrib/wpa/src/tls/tlsv1_common.h +++ b/contrib/wpa/src/tls/tlsv1_common.h @@ -15,7 +15,7 @@ #ifndef TLSV1_COMMON_H #define TLSV1_COMMON_H -#include "crypto.h" +#include "crypto/crypto.h" #define TLS_VERSION 0x0301 /* TLSv1 */ #define TLS_RANDOM_LEN 32 diff --git a/contrib/wpa/src/tls/tlsv1_cred.c b/contrib/wpa/src/tls/tlsv1_cred.c index d5564672..aa467ef 100644 --- a/contrib/wpa/src/tls/tlsv1_cred.c +++ b/contrib/wpa/src/tls/tlsv1_cred.c @@ -1,6 +1,6 @@ /* * TLSv1 credentials - * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi> + * Copyright (c) 2006-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -16,7 +16,7 @@ #include "common.h" #include "base64.h" -#include "crypto.h" +#include "crypto/crypto.h" #include "x509v3.h" #include "tlsv1_cred.h" @@ -68,6 +68,12 @@ static int tlsv1_add_cert_der(struct x509_certificate **chain, static const char *pem_cert_begin = "-----BEGIN CERTIFICATE-----"; static const char *pem_cert_end = "-----END CERTIFICATE-----"; +static const char *pem_key_begin = "-----BEGIN RSA PRIVATE KEY-----"; +static const char *pem_key_end = "-----END RSA PRIVATE KEY-----"; +static const char *pem_key2_begin = "-----BEGIN PRIVATE KEY-----"; +static const char *pem_key2_end = "-----END PRIVATE KEY-----"; +static const char *pem_key_enc_begin = "-----BEGIN ENCRYPTED PRIVATE KEY-----"; +static const char *pem_key_enc_end = "-----END ENCRYPTED PRIVATE KEY-----"; static const u8 * search_tag(const char *tag, const u8 *buf, size_t len) @@ -209,10 +215,74 @@ int tlsv1_set_cert(struct tlsv1_credentials *cred, const char *cert, } +static struct crypto_private_key * tlsv1_set_key_pem(const u8 *key, size_t len) +{ + const u8 *pos, *end; + unsigned char *der; + size_t der_len; + struct crypto_private_key *pkey; + + pos = search_tag(pem_key_begin, key, len); + if (!pos) { + pos = search_tag(pem_key2_begin, key, len); + if (!pos) + return NULL; + pos += os_strlen(pem_key2_begin); + end = search_tag(pem_key2_end, pos, key + len - pos); + if (!end) + return NULL; + } else { + pos += os_strlen(pem_key_begin); + end = search_tag(pem_key_end, pos, key + len - pos); + if (!end) + return NULL; + } + + der = base64_decode(pos, end - pos, &der_len); + if (!der) + return NULL; + pkey = crypto_private_key_import(der, der_len, NULL); + os_free(der); + return pkey; +} + + +static struct crypto_private_key * tlsv1_set_key_enc_pem(const u8 *key, + size_t len, + const char *passwd) +{ + const u8 *pos, *end; + unsigned char *der; + size_t der_len; + struct crypto_private_key *pkey; + + if (passwd == NULL) + return NULL; + pos = search_tag(pem_key_enc_begin, key, len); + if (!pos) + return NULL; + pos += os_strlen(pem_key_enc_begin); + end = search_tag(pem_key_enc_end, pos, key + len - pos); + if (!end) + return NULL; + + der = base64_decode(pos, end - pos, &der_len); + if (!der) + return NULL; + pkey = crypto_private_key_import(der, der_len, passwd); + os_free(der); + return pkey; +} + + static int tlsv1_set_key(struct tlsv1_credentials *cred, - const u8 *key, size_t len) + const u8 *key, size_t len, const char *passwd) { - cred->key = crypto_private_key_import(key, len); + cred->key = crypto_private_key_import(key, len, passwd); + if (cred->key == NULL) + cred->key = tlsv1_set_key_pem(key, len); + if (cred->key == NULL) + cred->key = tlsv1_set_key_enc_pem(key, len, passwd); if (cred->key == NULL) { wpa_printf(MSG_INFO, "TLSv1: Failed to parse private key"); return -1; @@ -242,7 +312,8 @@ int tlsv1_set_private_key(struct tlsv1_credentials *cred, if (private_key_blob) return tlsv1_set_key(cred, private_key_blob, - private_key_blob_len); + private_key_blob_len, + private_key_passwd); if (private_key) { u8 *buf; @@ -256,7 +327,7 @@ int tlsv1_set_private_key(struct tlsv1_credentials *cred, return -1; } - ret = tlsv1_set_key(cred, buf, len); + ret = tlsv1_set_key(cred, buf, len, private_key_passwd); os_free(buf); return ret; } diff --git a/contrib/wpa/src/tls/tlsv1_record.c b/contrib/wpa/src/tls/tlsv1_record.c index f226ac3..e811f0e 100644 --- a/contrib/wpa/src/tls/tlsv1_record.c +++ b/contrib/wpa/src/tls/tlsv1_record.c @@ -15,8 +15,8 @@ #include "includes.h" #include "common.h" -#include "md5.h" -#include "sha1.h" +#include "crypto/md5.h" +#include "crypto/sha1.h" #include "tlsv1_common.h" #include "tlsv1_record.h" diff --git a/contrib/wpa/src/tls/tlsv1_record.h b/contrib/wpa/src/tls/tlsv1_record.h index 9170fb1..9c7c0a4 100644 --- a/contrib/wpa/src/tls/tlsv1_record.h +++ b/contrib/wpa/src/tls/tlsv1_record.h @@ -15,7 +15,7 @@ #ifndef TLSV1_RECORD_H #define TLSV1_RECORD_H -#include "crypto.h" +#include "crypto/crypto.h" #define TLS_MAX_WRITE_MAC_SECRET_LEN 20 #define TLS_MAX_WRITE_KEY_LEN 32 diff --git a/contrib/wpa/src/tls/tlsv1_server.c b/contrib/wpa/src/tls/tlsv1_server.c index c204a47..6a61235 100644 --- a/contrib/wpa/src/tls/tlsv1_server.c +++ b/contrib/wpa/src/tls/tlsv1_server.c @@ -15,8 +15,8 @@ #include "includes.h" #include "common.h" -#include "sha1.h" -#include "tls.h" +#include "crypto/sha1.h" +#include "crypto/tls.h" #include "tlsv1_common.h" #include "tlsv1_record.h" #include "tlsv1_server.h" @@ -546,7 +546,6 @@ int tlsv1_server_get_keyblock_size(struct tlsv1_server *conn) */ int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers) { -#ifdef EAP_FAST size_t count; u16 *suites; @@ -572,9 +571,6 @@ int tlsv1_server_set_cipher_list(struct tlsv1_server *conn, u8 *ciphers) } return 0; -#else /* EAP_FAST */ - return -1; -#endif /* EAP_FAST */ } diff --git a/contrib/wpa/src/tls/tlsv1_server_read.c b/contrib/wpa/src/tls/tlsv1_server_read.c index 397d74a..49e811f 100644 --- a/contrib/wpa/src/tls/tlsv1_server_read.c +++ b/contrib/wpa/src/tls/tlsv1_server_read.c @@ -15,10 +15,10 @@ #include "includes.h" #include "common.h" -#include "md5.h" -#include "sha1.h" +#include "crypto/md5.h" +#include "crypto/sha1.h" +#include "crypto/tls.h" #include "x509v3.h" -#include "tls.h" #include "tlsv1_common.h" #include "tlsv1_record.h" #include "tlsv1_server.h" @@ -567,7 +567,6 @@ static int tls_process_client_key_exchange_rsa( static int tls_process_client_key_exchange_dh_anon( struct tlsv1_server *conn, const u8 *pos, const u8 *end) { -#ifdef EAP_FAST const u8 *dh_yc; u16 dh_yc_len; u8 *shared; @@ -665,9 +664,6 @@ static int tls_process_client_key_exchange_dh_anon( } return 0; -#else /* EAP_FAST */ - return -1; -#endif /* EAP_FAST */ } diff --git a/contrib/wpa/src/tls/tlsv1_server_write.c b/contrib/wpa/src/tls/tlsv1_server_write.c index cf54f42..6d1df7f 100644 --- a/contrib/wpa/src/tls/tlsv1_server_write.c +++ b/contrib/wpa/src/tls/tlsv1_server_write.c @@ -15,10 +15,10 @@ #include "includes.h" #include "common.h" -#include "md5.h" -#include "sha1.h" +#include "crypto/md5.h" +#include "crypto/sha1.h" +#include "crypto/tls.h" #include "x509v3.h" -#include "tls.h" #include "tlsv1_common.h" #include "tlsv1_record.h" #include "tlsv1_server.h" @@ -247,12 +247,10 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, { tls_key_exchange keyx; const struct tls_cipher_suite *suite; -#ifdef EAP_FAST u8 *pos, *rhdr, *hs_start, *hs_length; size_t rlen; u8 *dh_ys; size_t dh_ys_len; -#endif /* EAP_FAST */ suite = tls_get_cipher_suite(conn->rl.cipher_suite); if (suite == NULL) @@ -272,7 +270,6 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, return -1; } -#ifdef EAP_FAST if (conn->cred == NULL || conn->cred->dh_p == NULL || conn->cred->dh_g == NULL) { wpa_printf(MSG_DEBUG, "TLSv1: No DH parameters available for " @@ -433,9 +430,6 @@ static int tls_write_server_key_exchange(struct tlsv1_server *conn, *msgpos = pos; return 0; -#else /* EAP_FAST */ - return -1; -#endif /* EAP_FAST */ } diff --git a/contrib/wpa/src/tls/x509v3.c b/contrib/wpa/src/tls/x509v3.c index 59bf4ff..bc93df6 100644 --- a/contrib/wpa/src/tls/x509v3.c +++ b/contrib/wpa/src/tls/x509v3.c @@ -15,25 +15,32 @@ #include "includes.h" #include "common.h" - -#ifdef CONFIG_INTERNAL_X509 - +#include "crypto/crypto.h" #include "asn1.h" -#include "crypto.h" #include "x509v3.h" static void x509_free_name(struct x509_name *name) { - os_free(name->cn); - os_free(name->c); - os_free(name->l); - os_free(name->st); - os_free(name->o); - os_free(name->ou); + size_t i; + + for (i = 0; i < name->num_attr; i++) { + os_free(name->attr[i].value); + name->attr[i].value = NULL; + name->attr[i].type = X509_NAME_ATTR_NOT_USED; + } + name->num_attr = 0; os_free(name->email); - name->cn = name->c = name->l = name->st = name->o = name->ou = NULL; name->email = NULL; + + os_free(name->alt_email); + os_free(name->dns); + os_free(name->uri); + os_free(name->ip); + name->alt_email = name->dns = name->uri = NULL; + name->ip = NULL; + name->ip_len = 0; + os_memset(&name->rid, 0, sizeof(name->rid)); } @@ -146,6 +153,7 @@ static int x509_str_compare(const char *a, const char *b) int x509_name_compare(struct x509_name *a, struct x509_name *b) { int res; + size_t i; if (!a && b) return -1; @@ -153,25 +161,20 @@ int x509_name_compare(struct x509_name *a, struct x509_name *b) return 1; if (!a && !b) return 0; + if (a->num_attr < b->num_attr) + return -1; + if (a->num_attr > b->num_attr) + return 1; - res = x509_str_compare(a->cn, b->cn); - if (res) - return res; - res = x509_str_compare(a->c, b->c); - if (res) - return res; - res = x509_str_compare(a->l, b->l); - if (res) - return res; - res = x509_str_compare(a->st, b->st); - if (res) - return res; - res = x509_str_compare(a->o, b->o); - if (res) - return res; - res = x509_str_compare(a->ou, b->ou); - if (res) - return res; + for (i = 0; i < a->num_attr; i++) { + if (a->attr[i].type < b->attr[i].type) + return -1; + if (a->attr[i].type > b->attr[i].type) + return -1; + res = x509_str_compare(a->attr[i].value, b->attr[i].value); + if (res) + return res; + } res = x509_str_compare(a->email, b->email); if (res) return res; @@ -298,7 +301,7 @@ static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, struct asn1_hdr hdr; const u8 *pos, *end, *set_pos, *set_end, *seq_pos, *seq_end; struct asn1_oid oid; - char **fieldp; + char *val; /* * Name ::= CHOICE { RDNSequence } @@ -328,6 +331,8 @@ static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, end = *next = pos + hdr.length; while (pos < end) { + enum x509_name_attr_type type; + if (asn1_get_next(pos, end - pos, &hdr) < 0 || hdr.class != ASN1_CLASS_UNIVERSAL || hdr.tag != ASN1_TAG_SET) { @@ -375,34 +380,34 @@ static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, * pseudonym, generation qualifier. * MUST: domainComponent (RFC 2247). */ - fieldp = NULL; + type = X509_NAME_ATTR_NOT_USED; if (oid.len == 4 && oid.oid[0] == 2 && oid.oid[1] == 5 && oid.oid[2] == 4) { /* id-at ::= 2.5.4 */ switch (oid.oid[3]) { case 3: /* commonName */ - fieldp = &name->cn; + type = X509_NAME_ATTR_CN; break; case 6: /* countryName */ - fieldp = &name->c; + type = X509_NAME_ATTR_C; break; case 7: /* localityName */ - fieldp = &name->l; + type = X509_NAME_ATTR_L; break; case 8: /* stateOrProvinceName */ - fieldp = &name->st; + type = X509_NAME_ATTR_ST; break; case 10: /* organizationName */ - fieldp = &name->o; + type = X509_NAME_ATTR_O; break; case 11: /* organizationalUnitName */ - fieldp = &name->ou; + type = X509_NAME_ATTR_OU; break; } } else if (oid.len == 7 && @@ -411,10 +416,25 @@ static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, oid.oid[4] == 1 && oid.oid[5] == 9 && oid.oid[6] == 1) { /* 1.2.840.113549.1.9.1 - e-mailAddress */ - fieldp = &name->email; + os_free(name->email); + name->email = os_malloc(hdr.length + 1); + if (name->email == NULL) { + x509_free_name(name); + return -1; + } + os_memcpy(name->email, hdr.payload, hdr.length); + name->email[hdr.length] = '\0'; + continue; + } else if (oid.len == 7 && + oid.oid[0] == 0 && oid.oid[1] == 9 && + oid.oid[2] == 2342 && oid.oid[3] == 19200300 && + oid.oid[4] == 100 && oid.oid[5] == 1 && + oid.oid[6] == 25) { + /* 0.9.2342.19200300.100.1.25 - domainComponent */ + type = X509_NAME_ATTR_DC; } - if (fieldp == NULL) { + if (type == X509_NAME_ATTR_NOT_USED) { wpa_hexdump(MSG_DEBUG, "X509: Unrecognized OID", (u8 *) oid.oid, oid.len * sizeof(oid.oid[0])); @@ -423,20 +443,60 @@ static int x509_parse_name(const u8 *buf, size_t len, struct x509_name *name, continue; } - os_free(*fieldp); - *fieldp = os_malloc(hdr.length + 1); - if (*fieldp == NULL) { + if (name->num_attr == X509_MAX_NAME_ATTRIBUTES) { + wpa_printf(MSG_INFO, "X509: Too many Name attributes"); + x509_free_name(name); + return -1; + } + + val = os_malloc(hdr.length + 1); + if (val == NULL) { x509_free_name(name); return -1; } - os_memcpy(*fieldp, hdr.payload, hdr.length); - (*fieldp)[hdr.length] = '\0'; + os_memcpy(val, hdr.payload, hdr.length); + val[hdr.length] = '\0'; + if (os_strlen(val) != hdr.length) { + wpa_printf(MSG_INFO, "X509: Reject certificate with " + "embedded NUL byte in a string (%s[NUL])", + val); + x509_free_name(name); + return -1; + } + + name->attr[name->num_attr].type = type; + name->attr[name->num_attr].value = val; + name->num_attr++; } return 0; } +static char * x509_name_attr_str(enum x509_name_attr_type type) +{ + switch (type) { + case X509_NAME_ATTR_NOT_USED: + return "[N/A]"; + case X509_NAME_ATTR_DC: + return "DC"; + case X509_NAME_ATTR_CN: + return "CN"; + case X509_NAME_ATTR_C: + return "C"; + case X509_NAME_ATTR_L: + return "L"; + case X509_NAME_ATTR_ST: + return "ST"; + case X509_NAME_ATTR_O: + return "O"; + case X509_NAME_ATTR_OU: + return "OU"; + } + return "?"; +} + + /** * x509_name_string - Convert an X.509 certificate name into a string * @name: Name to convert @@ -447,6 +507,7 @@ void x509_name_string(struct x509_name *name, char *buf, size_t len) { char *pos, *end; int ret; + size_t i; if (len == 0) return; @@ -454,46 +515,20 @@ void x509_name_string(struct x509_name *name, char *buf, size_t len) pos = buf; end = buf + len; - if (name->c) { - ret = os_snprintf(pos, end - pos, "C=%s, ", name->c); - if (ret < 0 || ret >= end - pos) - goto done; - pos += ret; - } - if (name->st) { - ret = os_snprintf(pos, end - pos, "ST=%s, ", name->st); - if (ret < 0 || ret >= end - pos) - goto done; - pos += ret; - } - if (name->l) { - ret = os_snprintf(pos, end - pos, "L=%s, ", name->l); - if (ret < 0 || ret >= end - pos) - goto done; - pos += ret; - } - if (name->o) { - ret = os_snprintf(pos, end - pos, "O=%s, ", name->o); - if (ret < 0 || ret >= end - pos) - goto done; - pos += ret; - } - if (name->ou) { - ret = os_snprintf(pos, end - pos, "OU=%s, ", name->ou); - if (ret < 0 || ret >= end - pos) - goto done; - pos += ret; - } - if (name->cn) { - ret = os_snprintf(pos, end - pos, "CN=%s, ", name->cn); + for (i = 0; i < name->num_attr; i++) { + ret = os_snprintf(pos, end - pos, "%s=%s, ", + x509_name_attr_str(name->attr[i].type), + name->attr[i].value); if (ret < 0 || ret >= end - pos) goto done; pos += ret; } if (pos > buf + 1 && pos[-1] == ' ' && pos[-2] == ',') { - *pos-- = '\0'; - *pos-- = '\0'; + pos--; + *pos = '\0'; + pos--; + *pos = '\0'; } if (name->email) { @@ -815,6 +850,237 @@ static int x509_parse_ext_basic_constraints(struct x509_certificate *cert, } +static int x509_parse_alt_name_rfc8222(struct x509_name *name, + const u8 *pos, size_t len) +{ + /* rfc822Name IA5String */ + wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - rfc822Name", pos, len); + os_free(name->alt_email); + name->alt_email = os_zalloc(len + 1); + if (name->alt_email == NULL) + return -1; + os_memcpy(name->alt_email, pos, len); + if (os_strlen(name->alt_email) != len) { + wpa_printf(MSG_INFO, "X509: Reject certificate with " + "embedded NUL byte in rfc822Name (%s[NUL])", + name->alt_email); + os_free(name->alt_email); + name->alt_email = NULL; + return -1; + } + return 0; +} + + +static int x509_parse_alt_name_dns(struct x509_name *name, + const u8 *pos, size_t len) +{ + /* dNSName IA5String */ + wpa_hexdump_ascii(MSG_MSGDUMP, "X509: altName - dNSName", pos, len); + os_free(name->dns); + name->dns = os_zalloc(len + 1); + if (name->dns == NULL) + return -1; + os_memcpy(name->dns, pos, len); + if (os_strlen(name->dns) != len) { + wpa_printf(MSG_INFO, "X509: Reject certificate with " + "embedded NUL byte in dNSName (%s[NUL])", + name->dns); + os_free(name->dns); + name->dns = NULL; + return -1; + } + return 0; +} + + +static int x509_parse_alt_name_uri(struct x509_name *name, + const u8 *pos, size_t len) +{ + /* uniformResourceIdentifier IA5String */ + wpa_hexdump_ascii(MSG_MSGDUMP, + "X509: altName - uniformResourceIdentifier", + pos, len); + os_free(name->uri); + name->uri = os_zalloc(len + 1); + if (name->uri == NULL) + return -1; + os_memcpy(name->uri, pos, len); + if (os_strlen(name->uri) != len) { + wpa_printf(MSG_INFO, "X509: Reject certificate with " + "embedded NUL byte in uniformResourceIdentifier " + "(%s[NUL])", name->uri); + os_free(name->uri); + name->uri = NULL; + return -1; + } + return 0; +} + + +static int x509_parse_alt_name_ip(struct x509_name *name, + const u8 *pos, size_t len) +{ + /* iPAddress OCTET STRING */ + wpa_hexdump(MSG_MSGDUMP, "X509: altName - iPAddress", pos, len); + os_free(name->ip); + name->ip = os_malloc(len); + if (name->ip == NULL) + return -1; + os_memcpy(name->ip, pos, len); + name->ip_len = len; + return 0; +} + + +static int x509_parse_alt_name_rid(struct x509_name *name, + const u8 *pos, size_t len) +{ + char buf[80]; + + /* registeredID OBJECT IDENTIFIER */ + if (asn1_parse_oid(pos, len, &name->rid) < 0) + return -1; + + asn1_oid_to_str(&name->rid, buf, sizeof(buf)); + wpa_printf(MSG_MSGDUMP, "X509: altName - registeredID: %s", buf); + + return 0; +} + + +static int x509_parse_ext_alt_name(struct x509_name *name, + const u8 *pos, size_t len) +{ + struct asn1_hdr hdr; + const u8 *p, *end; + + /* + * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName + * + * GeneralName ::= CHOICE { + * otherName [0] OtherName, + * rfc822Name [1] IA5String, + * dNSName [2] IA5String, + * x400Address [3] ORAddress, + * directoryName [4] Name, + * ediPartyName [5] EDIPartyName, + * uniformResourceIdentifier [6] IA5String, + * iPAddress [7] OCTET STRING, + * registeredID [8] OBJECT IDENTIFIER } + * + * OtherName ::= SEQUENCE { + * type-id OBJECT IDENTIFIER, + * value [0] EXPLICIT ANY DEFINED BY type-id } + * + * EDIPartyName ::= SEQUENCE { + * nameAssigner [0] DirectoryString OPTIONAL, + * partyName [1] DirectoryString } + */ + + for (p = pos, end = pos + len; p < end; p = hdr.payload + hdr.length) { + int res; + + if (asn1_get_next(p, end - p, &hdr) < 0) { + wpa_printf(MSG_DEBUG, "X509: Failed to parse " + "SubjectAltName item"); + return -1; + } + + if (hdr.class != ASN1_CLASS_CONTEXT_SPECIFIC) + continue; + + switch (hdr.tag) { + case 1: + res = x509_parse_alt_name_rfc8222(name, hdr.payload, + hdr.length); + break; + case 2: + res = x509_parse_alt_name_dns(name, hdr.payload, + hdr.length); + break; + case 6: + res = x509_parse_alt_name_uri(name, hdr.payload, + hdr.length); + break; + case 7: + res = x509_parse_alt_name_ip(name, hdr.payload, + hdr.length); + break; + case 8: + res = x509_parse_alt_name_rid(name, hdr.payload, + hdr.length); + break; + case 0: /* TODO: otherName */ + case 3: /* TODO: x500Address */ + case 4: /* TODO: directoryName */ + case 5: /* TODO: ediPartyName */ + default: + res = 0; + break; + } + if (res < 0) + return res; + } + + return 0; +} + + +static int x509_parse_ext_subject_alt_name(struct x509_certificate *cert, + const u8 *pos, size_t len) +{ + struct asn1_hdr hdr; + + /* SubjectAltName ::= GeneralNames */ + + if (asn1_get_next(pos, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in " + "SubjectAltName; found %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + wpa_printf(MSG_DEBUG, "X509: SubjectAltName"); + cert->extensions_present |= X509_EXT_SUBJECT_ALT_NAME; + + if (hdr.length == 0) + return 0; + + return x509_parse_ext_alt_name(&cert->subject, hdr.payload, + hdr.length); +} + + +static int x509_parse_ext_issuer_alt_name(struct x509_certificate *cert, + const u8 *pos, size_t len) +{ + struct asn1_hdr hdr; + + /* IssuerAltName ::= GeneralNames */ + + if (asn1_get_next(pos, len, &hdr) < 0 || + hdr.class != ASN1_CLASS_UNIVERSAL || + hdr.tag != ASN1_TAG_SEQUENCE) { + wpa_printf(MSG_DEBUG, "X509: Expected SEQUENCE in " + "IssuerAltName; found %d tag 0x%x", + hdr.class, hdr.tag); + return -1; + } + + wpa_printf(MSG_DEBUG, "X509: IssuerAltName"); + cert->extensions_present |= X509_EXT_ISSUER_ALT_NAME; + + if (hdr.length == 0) + return 0; + + return x509_parse_ext_alt_name(&cert->issuer, hdr.payload, + hdr.length); +} + + static int x509_parse_extension_data(struct x509_certificate *cert, struct asn1_oid *oid, const u8 *pos, size_t len) @@ -824,7 +1090,6 @@ static int x509_parse_extension_data(struct x509_certificate *cert, /* TODO: add other extensions required by RFC 3280, Ch 4.2: * certificate policies (section 4.2.1.5) - * the subject alternative name (section 4.2.1.7) * name constraints (section 4.2.1.11) * policy constraints (section 4.2.1.12) * extended key usage (section 4.2.1.13) @@ -833,6 +1098,10 @@ static int x509_parse_extension_data(struct x509_certificate *cert, switch (oid->oid[3]) { case 15: /* id-ce-keyUsage */ return x509_parse_ext_key_usage(cert, pos, len); + case 17: /* id-ce-subjectAltName */ + return x509_parse_ext_subject_alt_name(cert, pos, len); + case 18: /* id-ce-issuerAltName */ + return x509_parse_ext_issuer_alt_name(cert, pos, len); case 19: /* id-ce-basicConstraints */ return x509_parse_ext_basic_constraints(cert, pos, len); default: @@ -1495,18 +1764,12 @@ skip_digest_oid: hash, hash_len); break; case 11: /* sha256WithRSAEncryption */ -#ifdef NEED_SHA256 sha256_vector(1, &cert->tbs_cert_start, &cert->tbs_cert_len, hash); hash_len = 32; wpa_hexdump(MSG_MSGDUMP, "X509: Certificate hash (SHA256)", hash, hash_len); break; -#else /* NEED_SHA256 */ - wpa_printf(MSG_INFO, "X509: SHA256 support disabled"); - os_free(data); - return -1; -#endif /* NEED_SHA256 */ case 2: /* md2WithRSAEncryption */ case 12: /* sha384WithRSAEncryption */ case 13: /* sha512WithRSAEncryption */ @@ -1720,5 +1983,3 @@ int x509_certificate_self_signed(struct x509_certificate *cert) { return x509_name_compare(&cert->issuer, &cert->subject) == 0; } - -#endif /* CONFIG_INTERNAL_X509 */ diff --git a/contrib/wpa/src/tls/x509v3.h b/contrib/wpa/src/tls/x509v3.h index a52bcf8..37292d7 100644 --- a/contrib/wpa/src/tls/x509v3.h +++ b/contrib/wpa/src/tls/x509v3.h @@ -21,14 +21,34 @@ struct x509_algorithm_identifier { struct asn1_oid oid; }; +struct x509_name_attr { + enum x509_name_attr_type { + X509_NAME_ATTR_NOT_USED, + X509_NAME_ATTR_DC, + X509_NAME_ATTR_CN, + X509_NAME_ATTR_C, + X509_NAME_ATTR_L, + X509_NAME_ATTR_ST, + X509_NAME_ATTR_O, + X509_NAME_ATTR_OU + } type; + char *value; +}; + +#define X509_MAX_NAME_ATTRIBUTES 20 + struct x509_name { - char *cn; /* commonName */ - char *c; /* countryName */ - char *l; /* localityName */ - char *st; /* stateOrProvinceName */ - char *o; /* organizationName */ - char *ou; /* organizationalUnitName */ + struct x509_name_attr attr[X509_MAX_NAME_ATTRIBUTES]; + size_t num_attr; char *email; /* emailAddress */ + + /* from alternative name extension */ + char *alt_email; /* rfc822Name */ + char *dns; /* dNSName */ + char *uri; /* uniformResourceIdentifier */ + u8 *ip; /* iPAddress */ + size_t ip_len; /* IPv4: 4, IPv6: 16 */ + struct asn1_oid rid; /* registeredID */ }; struct x509_certificate { @@ -52,6 +72,8 @@ struct x509_certificate { #define X509_EXT_BASIC_CONSTRAINTS (1 << 0) #define X509_EXT_PATH_LEN_CONSTRAINT (1 << 1) #define X509_EXT_KEY_USAGE (1 << 2) +#define X509_EXT_SUBJECT_ALT_NAME (1 << 3) +#define X509_EXT_ISSUER_ALT_NAME (1 << 4) /* BasicConstraints */ int ca; /* cA */ @@ -89,8 +111,6 @@ enum { X509_VALIDATE_UNKNOWN_CA }; -#ifdef CONFIG_INTERNAL_X509 - void x509_certificate_free(struct x509_certificate *cert); struct x509_certificate * x509_certificate_parse(const u8 *buf, size_t len); void x509_name_string(struct x509_name *name, char *buf, size_t len); @@ -106,49 +126,4 @@ x509_certificate_get_subject(struct x509_certificate *chain, struct x509_name *name); int x509_certificate_self_signed(struct x509_certificate *cert); -#else /* CONFIG_INTERNAL_X509 */ - -static inline void x509_certificate_free(struct x509_certificate *cert) -{ -} - -static inline struct x509_certificate * -x509_certificate_parse(const u8 *buf, size_t len) -{ - return NULL; -} - -static inline void x509_name_string(struct x509_name *name, char *buf, - size_t len) -{ - if (len) - buf[0] = '\0'; -} - -static inline void x509_certificate_chain_free(struct x509_certificate *cert) -{ -} - -static inline int -x509_certificate_chain_validate(struct x509_certificate *trusted, - struct x509_certificate *chain, - int *reason) -{ - return -1; -} - -static inline struct x509_certificate * -x509_certificate_get_subject(struct x509_certificate *chain, - struct x509_name *name) -{ - return NULL; -} - -static inline int x509_certificate_self_signed(struct x509_certificate *cert) -{ - return -1; -} - -#endif /* CONFIG_INTERNAL_X509 */ - #endif /* X509V3_H */ diff --git a/contrib/wpa/src/utils/.gitignore b/contrib/wpa/src/utils/.gitignore new file mode 100644 index 0000000..833734f --- /dev/null +++ b/contrib/wpa/src/utils/.gitignore @@ -0,0 +1 @@ +libutils.a diff --git a/contrib/wpa/src/utils/Makefile b/contrib/wpa/src/utils/Makefile index cffba62..527cf3e 100644 --- a/contrib/wpa/src/utils/Makefile +++ b/contrib/wpa/src/utils/Makefile @@ -1,9 +1,36 @@ -all: - @echo Nothing to be made. +all: libutils.a clean: - for d in $(SUBDIRS); do make -C $$d clean; done - rm -f *~ *.o *.d + rm -f *~ *.o *.d libutils.a install: @echo Nothing to be made. + + +include ../lib.rules + +#CFLAGS += -DWPA_TRACE +CFLAGS += -DCONFIG_IPV6 + +LIB_OBJS= \ + base64.o \ + common.o \ + ip_addr.o \ + radiotap.o \ + trace.o \ + uuid.o \ + wpa_debug.o \ + wpabuf.o + +# Pick correct OS wrapper implementation +LIB_OBJS += os_unix.o + +# Pick correct event loop implementation +LIB_OBJS += eloop.o + +#LIB_OBJS += pcsc_funcs.o + +libutils.a: $(LIB_OBJS) + $(AR) crT $@ $? + +-include $(OBJS:%.o=%.d) diff --git a/contrib/wpa/src/utils/base64.c b/contrib/wpa/src/utils/base64.c index 13fc511..155bfce 100644 --- a/contrib/wpa/src/utils/base64.c +++ b/contrib/wpa/src/utils/base64.c @@ -152,38 +152,3 @@ unsigned char * base64_decode(const unsigned char *src, size_t len, *out_len = pos - out; return out; } - - -#ifdef TEST_MAIN - -int main(int argc, char *argv[]) -{ - FILE *f; - size_t len, elen; - unsigned char *buf, *e; - - if (argc != 4) { - printf("Usage: base64 <encode|decode> <in file> <out file>\n"); - return -1; - } - - buf = os_readfile(argv[2], &len); - if (buf == NULL) - return -1; - - if (strcmp(argv[1], "encode") == 0) - e = base64_encode(buf, len, &elen); - else - e = base64_decode(buf, len, &elen); - if (e == NULL) - return -2; - f = fopen(argv[3], "w"); - if (f == NULL) - return -3; - fwrite(e, 1, elen, f); - fclose(f); - free(e); - - return 0; -} -#endif /* TEST_MAIN */ diff --git a/contrib/wpa/src/utils/build_config.h b/contrib/wpa/src/utils/build_config.h index 1e147fe..3666778 100644 --- a/contrib/wpa/src/utils/build_config.h +++ b/contrib/wpa/src/utils/build_config.h @@ -28,7 +28,6 @@ #define CONFIG_ANSI_C_EXTRA #define CONFIG_WINPCAP #define IEEE8021X_EAPOL -#define EAP_TLS_FUNCS #define PKCS12_FUNCS #define PCSC_FUNCS #define CONFIG_CTRL_IFACE @@ -48,16 +47,8 @@ #define _CRT_SECURE_NO_DEPRECATE #ifdef USE_INTERNAL_CRYPTO -#define CONFIG_TLS_INTERNAL #define CONFIG_TLS_INTERNAL_CLIENT #define CONFIG_INTERNAL_LIBTOMMATH -#define INTERNAL_AES -#define INTERNAL_SHA1 -#define INTERNAL_SHA256 -#define INTERNAL_MD5 -#define INTERNAL_MD4 -#define INTERNAL_DES -#define CONFIG_INTERNAL_X509 #define CONFIG_CRYPTO_INTERNAL #endif /* USE_INTERNAL_CRYPTO */ #endif /* CONFIG_WIN32_DEFAULTS */ @@ -69,15 +60,7 @@ #define CONFIG_NO_HOSTAPD_LOGGER #define CONFIG_NO_STDOUT_DEBUG #define CONFIG_BACKEND_FILE -#define INTERNAL_AES -#define INTERNAL_SHA1 -#define INTERNAL_MD5 -#define INTERNAL_MD4 -#define INTERNAL_DES #define CONFIG_INTERNAL_LIBTOMMATH -#define CONFIG_INTERNAL_X509 -#define EAP_TLS_FUNCS -#define CONFIG_TLS_INTERNAL #define CONFIG_CRYPTO_INTERNAL #define IEEE8021X_EAPOL #define PKCS12_FUNCS @@ -92,4 +75,31 @@ #define EAP_FAST #endif /* __SYMBIAN32__ */ +#ifdef CONFIG_XCODE_DEFAULTS +#define CONFIG_DRIVER_OSX +#define CONFIG_BACKEND_FILE +#define IEEE8021X_EAPOL +#define PKCS12_FUNCS +#define CONFIG_CTRL_IFACE +#define CONFIG_CTRL_IFACE_UNIX +#define CONFIG_DEBUG_FILE +#define EAP_MD5 +#define EAP_TLS +#define EAP_MSCHAPv2 +#define EAP_PEAP +#define EAP_TTLS +#define EAP_GTC +#define EAP_OTP +#define EAP_LEAP +#define EAP_TNC +#define CONFIG_WPS +#define EAP_WSC + +#ifdef USE_INTERNAL_CRYPTO +#define CONFIG_TLS_INTERNAL_CLIENT +#define CONFIG_INTERNAL_LIBTOMMATH +#define CONFIG_CRYPTO_INTERNAL +#endif /* USE_INTERNAL_CRYPTO */ +#endif /* CONFIG_XCODE_DEFAULTS */ + #endif /* BUILD_CONFIG_H */ diff --git a/contrib/wpa/src/utils/common.c b/contrib/wpa/src/utils/common.c index 9a46ebe..1b8ea80 100644 --- a/contrib/wpa/src/utils/common.c +++ b/contrib/wpa/src/utils/common.c @@ -43,7 +43,7 @@ static int hex2byte(const char *hex) /** - * hwaddr_aton - Convert ASCII string to MAC address + * hwaddr_aton - Convert ASCII string to MAC address (colon-delimited format) * @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) @@ -71,6 +71,36 @@ int hwaddr_aton(const char *txt, u8 *addr) /** + * hwaddr_aton2 - Convert ASCII string to MAC address (in any known format) + * @txt: MAC address as a string (e.g., 00:11:22:33:44:55 or 0011.2233.4455) + * @addr: Buffer for the MAC address (ETH_ALEN = 6 bytes) + * Returns: Characters used (> 0) on success, -1 on failure + */ +int hwaddr_aton2(const char *txt, u8 *addr) +{ + int i; + const char *pos = txt; + + for (i = 0; i < 6; i++) { + int a, b; + + while (*pos == ':' || *pos == '.' || *pos == '-') + pos++; + + a = hex2num(*pos++); + if (a < 0) + return -1; + b = hex2num(*pos++); + if (b < 0) + return -1; + *addr++ = (a << 4) | b; + } + + return pos - txt; +} + + +/** * hexstr2bin - Convert ASCII hex string into binary data * @hex: ASCII hex string (e.g., "01ab") * @buf: Buffer for the binary data diff --git a/contrib/wpa/src/utils/common.h b/contrib/wpa/src/utils/common.h index d649391..f17bf69 100644 --- a/contrib/wpa/src/utils/common.h +++ b/contrib/wpa/src/utils/common.h @@ -17,7 +17,7 @@ #include "os.h" -#ifdef __linux__ +#if defined(__linux__) || defined(__GLIBC__) #include <endian.h> #include <byteswap.h> #endif /* __linux__ */ @@ -314,6 +314,24 @@ static inline unsigned int wpa_swap_32(unsigned int v) #ifndef ETH_ALEN #define ETH_ALEN 6 #endif +#ifndef IFNAMSIZ +#define IFNAMSIZ 16 +#endif +#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 ETH_P_EAPOL +#define ETH_P_EAPOL ETH_P_PAE +#endif /* ETH_P_EAPOL */ +#ifndef ETH_P_RSN_PREAUTH +#define ETH_P_RSN_PREAUTH 0x88c7 +#endif /* ETH_P_RSN_PREAUTH */ +#ifndef ETH_P_RRB +#define ETH_P_RRB 0x890D +#endif /* ETH_P_RRB */ #ifdef __GNUC__ @@ -418,6 +436,7 @@ typedef u64 __bitwise le64; #endif /* __must_check */ int hwaddr_aton(const char *txt, u8 *addr); +int hwaddr_aton2(const char *txt, u8 *addr); int hexstr2bin(const char *hex, u8 *buf, size_t len); void inc_byte_array(u8 *counter, size_t len); void wpa_get_ntp_timestamp(u8 *buf); diff --git a/contrib/wpa/src/utils/eloop.c b/contrib/wpa/src/utils/eloop.c index 4edb2a7..4b61598 100644 --- a/contrib/wpa/src/utils/eloop.c +++ b/contrib/wpa/src/utils/eloop.c @@ -1,6 +1,6 @@ /* * Event loop based on select() loop - * Copyright (c) 2002-2005, Jouni Malinen <j@w1.fi> + * Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -15,6 +15,8 @@ #include "includes.h" #include "common.h" +#include "trace.h" +#include "list.h" #include "eloop.h" @@ -23,14 +25,20 @@ struct eloop_sock { void *eloop_data; void *user_data; eloop_sock_handler handler; + WPA_TRACE_REF(eloop); + WPA_TRACE_REF(user); + WPA_TRACE_INFO }; struct eloop_timeout { + struct dl_list list; struct os_time time; void *eloop_data; void *user_data; eloop_timeout_handler handler; - struct eloop_timeout *next; + WPA_TRACE_REF(eloop); + WPA_TRACE_REF(user); + WPA_TRACE_INFO }; struct eloop_signal { @@ -47,15 +55,13 @@ struct eloop_sock_table { }; struct eloop_data { - void *user_data; - int max_sock; struct eloop_sock_table readers; struct eloop_sock_table writers; struct eloop_sock_table exceptions; - struct eloop_timeout *timeout; + struct dl_list timeout; int signal_count; struct eloop_signal *signals; @@ -69,10 +75,56 @@ struct eloop_data { static struct eloop_data eloop; -int eloop_init(void *user_data) +#ifdef WPA_TRACE + +static void eloop_sigsegv_handler(int sig) +{ + wpa_trace_show("eloop SIGSEGV"); + abort(); +} + +static void eloop_trace_sock_add_ref(struct eloop_sock_table *table) +{ + int i; + if (table == NULL || table->table == NULL) + return; + for (i = 0; i < table->count; i++) { + wpa_trace_add_ref(&table->table[i], eloop, + table->table[i].eloop_data); + wpa_trace_add_ref(&table->table[i], user, + table->table[i].user_data); + } +} + + +static void eloop_trace_sock_remove_ref(struct eloop_sock_table *table) +{ + int i; + if (table == NULL || table->table == NULL) + return; + for (i = 0; i < table->count; i++) { + wpa_trace_remove_ref(&table->table[i], eloop, + table->table[i].eloop_data); + wpa_trace_remove_ref(&table->table[i], user, + table->table[i].user_data); + } +} + +#else /* WPA_TRACE */ + +#define eloop_trace_sock_add_ref(table) do { } while (0) +#define eloop_trace_sock_remove_ref(table) do { } while (0) + +#endif /* WPA_TRACE */ + + +int eloop_init(void) { os_memset(&eloop, 0, sizeof(eloop)); - eloop.user_data = user_data; + dl_list_init(&eloop.timeout); +#ifdef WPA_TRACE + signal(SIGSEGV, eloop_sigsegv_handler); +#endif /* WPA_TRACE */ return 0; } @@ -86,6 +138,7 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table, if (table == NULL) return -1; + eloop_trace_sock_remove_ref(table); tmp = (struct eloop_sock *) os_realloc(table->table, (table->count + 1) * sizeof(struct eloop_sock)); @@ -96,11 +149,13 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table, tmp[table->count].eloop_data = eloop_data; tmp[table->count].user_data = user_data; tmp[table->count].handler = handler; + wpa_trace_record(&tmp[table->count]); table->count++; table->table = tmp; if (sock > eloop.max_sock) eloop.max_sock = sock; table->changed = 1; + eloop_trace_sock_add_ref(table); return 0; } @@ -120,6 +175,7 @@ static void eloop_sock_table_remove_sock(struct eloop_sock_table *table, } if (i == table->count) return; + eloop_trace_sock_remove_ref(table); if (i != table->count - 1) { os_memmove(&table->table[i], &table->table[i + 1], (table->count - i - 1) * @@ -127,6 +183,7 @@ static void eloop_sock_table_remove_sock(struct eloop_sock_table *table, } table->count--; table->changed = 1; + eloop_trace_sock_add_ref(table); } @@ -171,12 +228,17 @@ static void eloop_sock_table_destroy(struct eloop_sock_table *table) if (table) { int i; for (i = 0; i < table->count && table->table; i++) { - printf("ELOOP: remaining socket: sock=%d " - "eloop_data=%p user_data=%p handler=%p\n", - table->table[i].sock, - table->table[i].eloop_data, - table->table[i].user_data, - table->table[i].handler); + wpa_printf(MSG_INFO, "ELOOP: remaining socket: " + "sock=%d eloop_data=%p user_data=%p " + "handler=%p", + table->table[i].sock, + table->table[i].eloop_data, + table->table[i].user_data, + table->table[i].handler); + wpa_trace_dump_funcname("eloop unregistered socket " + "handler", + table->table[i].handler); + wpa_trace_dump("eloop sock", &table->table[i]); } os_free(table->table); } @@ -237,9 +299,9 @@ int eloop_register_timeout(unsigned int secs, unsigned int usecs, eloop_timeout_handler handler, void *eloop_data, void *user_data) { - struct eloop_timeout *timeout, *tmp, *prev; + struct eloop_timeout *timeout, *tmp; - timeout = os_malloc(sizeof(*timeout)); + timeout = os_zalloc(sizeof(*timeout)); if (timeout == NULL) return -1; if (os_get_time(&timeout->time) < 0) { @@ -255,60 +317,48 @@ int eloop_register_timeout(unsigned int secs, unsigned int usecs, timeout->eloop_data = eloop_data; timeout->user_data = user_data; timeout->handler = handler; - timeout->next = NULL; - - if (eloop.timeout == NULL) { - eloop.timeout = timeout; - return 0; + wpa_trace_add_ref(timeout, eloop, eloop_data); + wpa_trace_add_ref(timeout, user, user_data); + wpa_trace_record(timeout); + + /* Maintain timeouts in order of increasing time */ + dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) { + if (os_time_before(&timeout->time, &tmp->time)) { + dl_list_add(tmp->list.prev, &timeout->list); + return 0; + } } + dl_list_add_tail(&eloop.timeout, &timeout->list); - prev = NULL; - tmp = eloop.timeout; - while (tmp != NULL) { - if (os_time_before(&timeout->time, &tmp->time)) - break; - prev = tmp; - tmp = tmp->next; - } + return 0; +} - if (prev == NULL) { - timeout->next = eloop.timeout; - eloop.timeout = timeout; - } else { - timeout->next = prev->next; - prev->next = timeout; - } - return 0; +static void eloop_remove_timeout(struct eloop_timeout *timeout) +{ + dl_list_del(&timeout->list); + wpa_trace_remove_ref(timeout, eloop, timeout->eloop_data); + wpa_trace_remove_ref(timeout, user, timeout->user_data); + os_free(timeout); } int eloop_cancel_timeout(eloop_timeout_handler handler, void *eloop_data, void *user_data) { - struct eloop_timeout *timeout, *prev, *next; + struct eloop_timeout *timeout, *prev; int removed = 0; - prev = NULL; - timeout = eloop.timeout; - while (timeout != NULL) { - next = timeout->next; - + dl_list_for_each_safe(timeout, prev, &eloop.timeout, + struct eloop_timeout, list) { if (timeout->handler == handler && (timeout->eloop_data == eloop_data || eloop_data == ELOOP_ALL_CTX) && (timeout->user_data == user_data || user_data == ELOOP_ALL_CTX)) { - if (prev == NULL) - eloop.timeout = next; - else - prev->next = next; - os_free(timeout); + eloop_remove_timeout(timeout); removed++; - } else - prev = timeout; - - timeout = next; + } } return removed; @@ -320,14 +370,11 @@ int eloop_is_timeout_registered(eloop_timeout_handler handler, { struct eloop_timeout *tmp; - tmp = eloop.timeout; - while (tmp != NULL) { + dl_list_for_each(tmp, &eloop.timeout, struct eloop_timeout, list) { if (tmp->handler == handler && tmp->eloop_data == eloop_data && tmp->user_data == user_data) return 1; - - tmp = tmp->next; } return 0; @@ -337,11 +384,11 @@ int eloop_is_timeout_registered(eloop_timeout_handler handler, #ifndef CONFIG_NATIVE_WINDOWS static void eloop_handle_alarm(int sig) { - fprintf(stderr, "eloop: could not process SIGINT or SIGTERM in two " - "seconds. Looks like there\n" - "is a bug that ends up in a busy loop that " - "prevents clean shutdown.\n" - "Killing program forcefully.\n"); + wpa_printf(MSG_ERROR, "eloop: could not process SIGINT or SIGTERM in " + "two seconds. Looks like there\n" + "is a bug that ends up in a busy loop that " + "prevents clean shutdown.\n" + "Killing program forcefully.\n"); exit(1); } #endif /* CONFIG_NATIVE_WINDOWS */ @@ -390,7 +437,6 @@ static void eloop_process_pending_signals(void) if (eloop.signals[i].signaled) { eloop.signals[i].signaled = 0; eloop.signals[i].handler(eloop.signals[i].sig, - eloop.user_data, eloop.signals[i].user_data); } } @@ -452,24 +498,21 @@ void eloop_run(void) rfds = os_malloc(sizeof(*rfds)); wfds = os_malloc(sizeof(*wfds)); efds = os_malloc(sizeof(*efds)); - if (rfds == NULL || wfds == NULL || efds == NULL) { - printf("eloop_run - malloc failed\n"); + if (rfds == NULL || wfds == NULL || efds == NULL) goto out; - } while (!eloop.terminate && - (eloop.timeout || eloop.readers.count > 0 || + (!dl_list_empty(&eloop.timeout) || eloop.readers.count > 0 || eloop.writers.count > 0 || eloop.exceptions.count > 0)) { - if (eloop.timeout) { + struct eloop_timeout *timeout; + timeout = dl_list_first(&eloop.timeout, struct eloop_timeout, + list); + if (timeout) { os_get_time(&now); - if (os_time_before(&now, &eloop.timeout->time)) - os_time_sub(&eloop.timeout->time, &now, &tv); + if (os_time_before(&now, &timeout->time)) + os_time_sub(&timeout->time, &now, &tv); else tv.sec = tv.usec = 0; -#if 0 - printf("next timeout in %lu.%06lu sec\n", - tv.sec, tv.usec); -#endif _tv.tv_sec = tv.sec; _tv.tv_usec = tv.usec; } @@ -478,7 +521,7 @@ void eloop_run(void) eloop_sock_table_set_fds(&eloop.writers, wfds); eloop_sock_table_set_fds(&eloop.exceptions, efds); res = select(eloop.max_sock + 1, rfds, wfds, efds, - eloop.timeout ? &_tv : NULL); + timeout ? &_tv : NULL); if (res < 0 && errno != EINTR && errno != 0) { perror("select"); goto out; @@ -486,16 +529,17 @@ void eloop_run(void) eloop_process_pending_signals(); /* check if some registered timeouts have occurred */ - if (eloop.timeout) { - struct eloop_timeout *tmp; - + timeout = dl_list_first(&eloop.timeout, struct eloop_timeout, + list); + if (timeout) { os_get_time(&now); - if (!os_time_before(&now, &eloop.timeout->time)) { - tmp = eloop.timeout; - eloop.timeout = eloop.timeout->next; - tmp->handler(tmp->eloop_data, - tmp->user_data); - os_free(tmp); + if (!os_time_before(&now, &timeout->time)) { + void *eloop_data = timeout->eloop_data; + void *user_data = timeout->user_data; + eloop_timeout_handler handler = + timeout->handler; + eloop_remove_timeout(timeout); + handler(eloop_data, user_data); } } @@ -526,24 +570,24 @@ void eloop_destroy(void) struct eloop_timeout *timeout, *prev; struct os_time now; - timeout = eloop.timeout; - if (timeout) - os_get_time(&now); - while (timeout != NULL) { + os_get_time(&now); + dl_list_for_each_safe(timeout, prev, &eloop.timeout, + struct eloop_timeout, list) { int sec, usec; - prev = timeout; - timeout = timeout->next; - sec = prev->time.sec - now.sec; - usec = prev->time.usec - now.usec; - if (prev->time.usec < now.usec) { + sec = timeout->time.sec - now.sec; + usec = timeout->time.usec - now.usec; + if (timeout->time.usec < now.usec) { sec--; usec += 1000000; } - printf("ELOOP: remaining timeout: %d.%06d eloop_data=%p " - "user_data=%p handler=%p\n", - sec, usec, prev->eloop_data, prev->user_data, - prev->handler); - os_free(prev); + wpa_printf(MSG_INFO, "ELOOP: remaining timeout: %d.%06d " + "eloop_data=%p user_data=%p handler=%p", + sec, usec, timeout->eloop_data, timeout->user_data, + timeout->handler); + wpa_trace_dump_funcname("eloop unregistered timeout handler", + timeout->handler); + wpa_trace_dump("eloop timeout", timeout); + eloop_remove_timeout(timeout); } eloop_sock_table_destroy(&eloop.readers); eloop_sock_table_destroy(&eloop.writers); @@ -569,9 +613,3 @@ void eloop_wait_for_read_sock(int sock) FD_SET(sock, &rfds); select(sock + 1, &rfds, NULL, NULL, NULL); } - - -void * eloop_get_user_data(void) -{ - return eloop.user_data; -} diff --git a/contrib/wpa/src/utils/eloop.h b/contrib/wpa/src/utils/eloop.h index cf83f38..1228f24 100644 --- a/contrib/wpa/src/utils/eloop.h +++ b/contrib/wpa/src/utils/eloop.h @@ -65,25 +65,19 @@ typedef void (*eloop_timeout_handler)(void *eloop_data, void *user_ctx); /** * eloop_signal_handler - eloop signal event callback type * @sig: Signal number - * @eloop_ctx: Registered callback context data (global user_data from - * eloop_init() call) * @signal_ctx: Registered callback context data (user_data from * eloop_register_signal(), eloop_register_signal_terminate(), or * eloop_register_signal_reconfig() call) */ -typedef void (*eloop_signal_handler)(int sig, void *eloop_ctx, - void *signal_ctx); +typedef void (*eloop_signal_handler)(int sig, void *signal_ctx); /** * eloop_init() - Initialize global event loop data - * @user_data: Pointer to global data passed as eloop_ctx to signal handlers * Returns: 0 on success, -1 on failure * - * This function must be called before any other eloop_* function. user_data - * can be used to configure a global (to the process) pointer that will be - * passed as eloop_ctx parameter to signal handlers. + * This function must be called before any other eloop_* function. */ -int eloop_init(void *user_data); +int eloop_init(void); /** * eloop_register_read_sock - Register handler for read events @@ -231,10 +225,6 @@ int eloop_is_timeout_registered(eloop_timeout_handler handler, * 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, eloop_signal_handler handler, void *user_data); @@ -251,10 +241,6 @@ int eloop_register_signal(int sig, eloop_signal_handler handler, * sighandlers (i.e., only "safe functions" allowed) do not apply for the * registered callback. * - * Signals are 'global' events and there is no local eloop_data pointer like - * with other handlers. The global user_data pointer registered with - * eloop_init() will be used as eloop_ctx for signal handlers. - * * This function is a more portable version of eloop_register_signal() since * the knowledge of exact details of the signals is hidden in eloop * implementation. In case of operating systems using signal(), this function @@ -275,10 +261,6 @@ int eloop_register_signal_terminate(eloop_signal_handler handler, * limits for sighandlers (i.e., only "safe functions" allowed) do not apply * for the registered callback. * - * Signals are 'global' events and there is no local eloop_data pointer like - * with other handlers. The global user_data pointer registered with - * eloop_init() will be used as eloop_ctx for signal handlers. - * * This function is a more portable version of eloop_register_signal() since * the knowledge of exact details of the signals is hidden in eloop * implementation. In case of operating systems using signal(), this function @@ -331,10 +313,4 @@ int eloop_terminated(void); */ void eloop_wait_for_read_sock(int sock); -/** - * eloop_get_user_data - Get global user data - * Returns: user_data pointer that was registered with eloop_init() - */ -void * eloop_get_user_data(void); - #endif /* ELOOP_H */ diff --git a/contrib/wpa/src/utils/eloop_none.c b/contrib/wpa/src/utils/eloop_none.c index 215030b..18eae4e 100644 --- a/contrib/wpa/src/utils/eloop_none.c +++ b/contrib/wpa/src/utils/eloop_none.c @@ -41,8 +41,6 @@ struct eloop_signal { }; struct eloop_data { - void *user_data; - int max_sock, reader_count; struct eloop_sock *readers; @@ -60,10 +58,9 @@ struct eloop_data { static struct eloop_data eloop; -int eloop_init(void *user_data) +int eloop_init(void) { memset(&eloop, 0, sizeof(eloop)); - eloop.user_data = user_data; return 0; } @@ -402,9 +399,3 @@ void eloop_wait_for_read_sock(int sock) * reading */ } - - -void * eloop_get_user_data(void) -{ - return eloop.user_data; -} diff --git a/contrib/wpa/src/utils/eloop_win.c b/contrib/wpa/src/utils/eloop_win.c index c95aa76..94cc72d 100644 --- a/contrib/wpa/src/utils/eloop_win.c +++ b/contrib/wpa/src/utils/eloop_win.c @@ -50,8 +50,6 @@ struct eloop_signal { }; struct eloop_data { - void *user_data; - int max_sock; size_t reader_count; struct eloop_sock *readers; @@ -79,10 +77,9 @@ struct eloop_data { static struct eloop_data eloop; -int eloop_init(void *user_data) +int eloop_init(void) { os_memset(&eloop, 0, sizeof(eloop)); - eloop.user_data = user_data; eloop.num_handles = 1; eloop.handles = os_malloc(eloop.num_handles * sizeof(eloop.handles[0])); @@ -372,7 +369,6 @@ static void eloop_process_pending_signals(void) if (eloop.signals[i].signaled) { eloop.signals[i].signaled = 0; eloop.signals[i].handler(eloop.signals[i].sig, - eloop.user_data, eloop.signals[i].user_data); } } @@ -380,7 +376,6 @@ static void eloop_process_pending_signals(void) if (eloop.term_signal.signaled) { eloop.term_signal.signaled = 0; eloop.term_signal.handler(eloop.term_signal.sig, - eloop.user_data, eloop.term_signal.user_data); } } @@ -614,9 +609,3 @@ void eloop_wait_for_read_sock(int sock) WSAEventSelect(sock, event, 0); WSACloseEvent(event); } - - -void * eloop_get_user_data(void) -{ - return eloop.user_data; -} diff --git a/contrib/wpa/src/utils/ip_addr.h b/contrib/wpa/src/utils/ip_addr.h index 192049a..28ccaef 100644 --- a/contrib/wpa/src/utils/ip_addr.h +++ b/contrib/wpa/src/utils/ip_addr.h @@ -16,13 +16,14 @@ #define IP_ADDR_H struct hostapd_ip_addr { + int af; /* AF_INET / AF_INET6 */ union { struct in_addr v4; #ifdef CONFIG_IPV6 struct in6_addr v6; #endif /* CONFIG_IPV6 */ + u8 max_len[16]; } u; - int af; /* AF_INET / AF_INET6 */ }; const char * hostapd_ip_txt(const struct hostapd_ip_addr *addr, char *buf, diff --git a/contrib/wpa/src/utils/list.h b/contrib/wpa/src/utils/list.h new file mode 100644 index 0000000..ed7c022 --- /dev/null +++ b/contrib/wpa/src/utils/list.h @@ -0,0 +1,89 @@ +/* + * Doubly-linked list + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef LIST_H +#define LIST_H + +/** + * struct dl_list - Doubly-linked list + */ +struct dl_list { + struct dl_list *next; + struct dl_list *prev; +}; + +static inline void dl_list_init(struct dl_list *list) +{ + list->next = list; + list->prev = list; +} + +static inline void dl_list_add(struct dl_list *list, struct dl_list *item) +{ + item->next = list->next; + item->prev = list; + list->next->prev = item; + list->next = item; +} + +static inline void dl_list_add_tail(struct dl_list *list, struct dl_list *item) +{ + dl_list_add(list->prev, item); +} + +static inline void dl_list_del(struct dl_list *item) +{ + item->next->prev = item->prev; + item->prev->next = item->next; + item->next = NULL; + item->prev = NULL; +} + +static inline int dl_list_empty(struct dl_list *list) +{ + return list->next == list; +} + +static inline unsigned int dl_list_len(struct dl_list *list) +{ + struct dl_list *item; + int count = 0; + for (item = list->next; item != list; item = item->next) + count++; + return count; +} + +#ifndef offsetof +#define offsetof(type, member) ((long) &((type *) 0)->member) +#endif + +#define dl_list_entry(item, type, member) \ + ((type *) ((char *) item - offsetof(type, member))) + +#define dl_list_first(list, type, member) \ + (dl_list_empty((list)) ? NULL : \ + dl_list_entry((list)->next, type, member)) + +#define dl_list_for_each(item, list, type, member) \ + for (item = dl_list_entry((list)->next, type, member); \ + &item->member != (list); \ + item = dl_list_entry(item->member.next, type, member)) + +#define dl_list_for_each_safe(item, n, list, type, member) \ + for (item = dl_list_entry((list)->next, type, member), \ + n = dl_list_entry(item->member.next, type, member); \ + &item->member != (list); \ + item = n, n = dl_list_entry(n->member.next, type, member)) + +#endif /* LIST_H */ diff --git a/contrib/wpa/src/utils/os.h b/contrib/wpa/src/utils/os.h index d6dfea6..f4723d8 100644 --- a/contrib/wpa/src/utils/os.h +++ b/contrib/wpa/src/utils/os.h @@ -1,6 +1,6 @@ /* - * wpa_supplicant/hostapd / OS specific functions - * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * OS specific functions + * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -379,6 +379,12 @@ int os_snprintf(char *str, size_t size, const char *format, ...); #else /* OS_NO_C_LIB_DEFINES */ +#ifdef WPA_TRACE +void * os_malloc(size_t size); +void * os_realloc(void *ptr, size_t size); +void os_free(void *ptr); +char * os_strdup(const char *s); +#else /* WPA_TRACE */ #ifndef os_malloc #define os_malloc(s) malloc((s)) #endif @@ -388,6 +394,14 @@ int os_snprintf(char *str, size_t size, const char *format, ...); #ifndef os_free #define os_free(p) free((p)) #endif +#ifndef os_strdup +#ifdef _MSC_VER +#define os_strdup(s) _strdup(s) +#else +#define os_strdup(s) strdup(s) +#endif +#endif +#endif /* WPA_TRACE */ #ifndef os_memcpy #define os_memcpy(d, s, n) memcpy((d), (s), (n)) @@ -402,13 +416,6 @@ int os_snprintf(char *str, size_t size, const char *format, ...); #define os_memcmp(s1, s2, n) memcmp((s1), (s2), (n)) #endif -#ifndef os_strdup -#ifdef _MSC_VER -#define os_strdup(s) _strdup(s) -#else -#define os_strdup(s) strdup(s) -#endif -#endif #ifndef os_strlen #define os_strlen(s) strlen(s) #endif diff --git a/contrib/wpa/src/utils/os_internal.c b/contrib/wpa/src/utils/os_internal.c index 7b74bbf..5260e23 100644 --- a/contrib/wpa/src/utils/os_internal.c +++ b/contrib/wpa/src/utils/os_internal.c @@ -206,7 +206,12 @@ char * os_readfile(const char *name, size_t *len) return NULL; } - fread(buf, 1, *len, f); + if (fread(buf, 1, *len, f) != *len) { + fclose(f); + os_free(buf); + return NULL; + } + fclose(f); return buf; diff --git a/contrib/wpa/src/utils/os_unix.c b/contrib/wpa/src/utils/os_unix.c index bc2fc40..6f58fa4 100644 --- a/contrib/wpa/src/utils/os_unix.c +++ b/contrib/wpa/src/utils/os_unix.c @@ -1,6 +1,6 @@ /* - * wpa_supplicant/hostapd / OS specific functions for UNIX/POSIX systems - * Copyright (c) 2005-2006, Jouni Malinen <j@w1.fi> + * OS specific functions for UNIX/POSIX systems + * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -16,6 +16,28 @@ #include "os.h" +#ifdef WPA_TRACE + +#include "common.h" +#include "list.h" +#include "wpa_debug.h" +#include "trace.h" + +static struct dl_list alloc_list; + +#define ALLOC_MAGIC 0xa84ef1b2 +#define FREED_MAGIC 0x67fd487a + +struct os_alloc_trace { + unsigned int magic; + struct dl_list list; + size_t len; + WPA_TRACE_INFO +}; + +#endif /* WPA_TRACE */ + + void os_sleep(os_time_t sec, os_time_t usec) { if (sec) @@ -172,16 +194,16 @@ char * os_rel2abs_path(const char *rel_path) int last_errno; if (rel_path[0] == '/') - return strdup(rel_path); + return os_strdup(rel_path); for (;;) { - buf = malloc(len); + buf = os_malloc(len); if (buf == NULL) return NULL; cwd = getcwd(buf, len); if (cwd == NULL) { last_errno = errno; - free(buf); + os_free(buf); if (last_errno != ERANGE) return NULL; len *= 2; @@ -193,29 +215,51 @@ char * os_rel2abs_path(const char *rel_path) } } - cwd_len = strlen(cwd); - rel_len = strlen(rel_path); + cwd_len = os_strlen(cwd); + rel_len = os_strlen(rel_path); ret_len = cwd_len + 1 + rel_len + 1; - ret = malloc(ret_len); + ret = os_malloc(ret_len); if (ret) { - memcpy(ret, cwd, cwd_len); + os_memcpy(ret, cwd, cwd_len); ret[cwd_len] = '/'; - memcpy(ret + cwd_len + 1, rel_path, rel_len); + os_memcpy(ret + cwd_len + 1, rel_path, rel_len); ret[ret_len - 1] = '\0'; } - free(buf); + os_free(buf); return ret; } int os_program_init(void) { +#ifdef WPA_TRACE + dl_list_init(&alloc_list); +#endif /* WPA_TRACE */ return 0; } void os_program_deinit(void) { +#ifdef WPA_TRACE + struct os_alloc_trace *a; + unsigned long total = 0; + dl_list_for_each(a, &alloc_list, struct os_alloc_trace, list) { + total += a->len; + if (a->magic != ALLOC_MAGIC) { + wpa_printf(MSG_INFO, "MEMLEAK[%p]: invalid magic 0x%x " + "len %lu", + a, a->magic, (unsigned long) a->len); + continue; + } + wpa_printf(MSG_INFO, "MEMLEAK[%p]: len %lu", + a, (unsigned long) a->len); + wpa_trace_dump("memleak", a); + } + if (total) + wpa_printf(MSG_INFO, "MEMLEAK: total %lu bytes", + (unsigned long) total); +#endif /* WPA_TRACE */ } @@ -250,7 +294,7 @@ char * os_readfile(const char *name, size_t *len) *len = ftell(f); fseek(f, 0, SEEK_SET); - buf = malloc(*len); + buf = os_malloc(*len); if (buf == NULL) { fclose(f); return NULL; @@ -258,7 +302,7 @@ char * os_readfile(const char *name, size_t *len) if (fread(buf, 1, *len, f) != *len) { fclose(f); - free(buf); + os_free(buf); return NULL; } @@ -268,10 +312,12 @@ char * os_readfile(const char *name, size_t *len) } +#ifndef WPA_TRACE void * os_zalloc(size_t size) { return calloc(1, size); } +#endif /* WPA_TRACE */ size_t os_strlcpy(char *dest, const char *src, size_t siz) @@ -297,3 +343,95 @@ size_t os_strlcpy(char *dest, const char *src, size_t siz) return s - src - 1; } + + +#ifdef WPA_TRACE + +void * os_malloc(size_t size) +{ + struct os_alloc_trace *a; + a = malloc(sizeof(*a) + size); + if (a == NULL) + return NULL; + a->magic = ALLOC_MAGIC; + dl_list_add(&alloc_list, &a->list); + a->len = size; + wpa_trace_record(a); + return a + 1; +} + + +void * os_realloc(void *ptr, size_t size) +{ + struct os_alloc_trace *a; + size_t copy_len; + void *n; + + if (ptr == NULL) + return os_malloc(size); + + a = (struct os_alloc_trace *) ptr - 1; + if (a->magic != ALLOC_MAGIC) { + wpa_printf(MSG_INFO, "REALLOC[%p]: invalid magic 0x%x%s", + a, a->magic, + a->magic == FREED_MAGIC ? " (already freed)" : ""); + wpa_trace_show("Invalid os_realloc() call"); + abort(); + } + n = os_malloc(size); + if (n == NULL) + return NULL; + copy_len = a->len; + if (copy_len > size) + copy_len = size; + os_memcpy(n, a + 1, copy_len); + os_free(ptr); + return n; +} + + +void os_free(void *ptr) +{ + struct os_alloc_trace *a; + + if (ptr == NULL) + return; + a = (struct os_alloc_trace *) ptr - 1; + if (a->magic != ALLOC_MAGIC) { + wpa_printf(MSG_INFO, "FREE[%p]: invalid magic 0x%x%s", + a, a->magic, + a->magic == FREED_MAGIC ? " (already freed)" : ""); + wpa_trace_show("Invalid os_free() call"); + abort(); + } + dl_list_del(&a->list); + a->magic = FREED_MAGIC; + + wpa_trace_check_ref(ptr); + free(a); +} + + +void * os_zalloc(size_t size) +{ + void *ptr = os_malloc(size); + if (ptr) + os_memset(ptr, 0, size); + return ptr; +} + + +char * os_strdup(const char *s) +{ + size_t len; + char *d; + len = os_strlen(s); + d = os_malloc(len + 1); + if (d == NULL) + return NULL; + os_memcpy(d, s, len); + d[len] = '\0'; + return d; +} + +#endif /* WPA_TRACE */ diff --git a/contrib/wpa/src/drivers/radiotap.c b/contrib/wpa/src/utils/radiotap.c index 804473f..804473f 100644 --- a/contrib/wpa/src/drivers/radiotap.c +++ b/contrib/wpa/src/utils/radiotap.c diff --git a/contrib/wpa/src/drivers/radiotap.h b/contrib/wpa/src/utils/radiotap.h index 508264c..ba23ed3 100644 --- a/contrib/wpa/src/drivers/radiotap.h +++ b/contrib/wpa/src/utils/radiotap.h @@ -1,4 +1,4 @@ -/* $FreeBSD: src/sys/net80211/ieee80211_radiotap.h,v 1.5 2005/01/22 20:12:05 sam Exp $ */ +/* $FreeBSD$ */ /* $NetBSD: ieee80211_radiotap.h,v 1.11 2005/06/22 06:16:02 dyoung Exp $ */ /*- diff --git a/contrib/wpa/src/drivers/radiotap_iter.h b/contrib/wpa/src/utils/radiotap_iter.h index 92a798a..92a798a 100644 --- a/contrib/wpa/src/drivers/radiotap_iter.h +++ b/contrib/wpa/src/utils/radiotap_iter.h diff --git a/contrib/wpa/src/utils/trace.c b/contrib/wpa/src/utils/trace.c new file mode 100644 index 0000000..bb3eb24 --- /dev/null +++ b/contrib/wpa/src/utils/trace.c @@ -0,0 +1,329 @@ +/* + * Backtrace debugging + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "trace.h" + +#ifdef WPA_TRACE + +static struct dl_list active_references = +{ &active_references, &active_references }; + +#ifdef WPA_TRACE_BFD +#include <bfd.h> +#ifdef __linux__ +#include <demangle.h> +#else /* __linux__ */ +#include <libiberty/demangle.h> +#endif /* __linux__ */ + +static char *prg_fname = NULL; +static bfd *cached_abfd = NULL; +static asymbol **syms = NULL; + +static void get_prg_fname(void) +{ + char exe[50], fname[512]; + int len; + os_snprintf(exe, sizeof(exe) - 1, "/proc/%u/exe", getpid()); + len = readlink(exe, fname, sizeof(fname) - 1); + if (len < 0 || len >= (int) sizeof(fname)) { + perror("readlink"); + return; + } + fname[len] = '\0'; + prg_fname = strdup(fname); +} + + +static bfd * open_bfd(const char *fname) +{ + bfd *abfd; + char **matching; + + abfd = bfd_openr(prg_fname, NULL); + if (abfd == NULL) { + wpa_printf(MSG_INFO, "bfd_openr failed"); + return NULL; + } + + if (bfd_check_format(abfd, bfd_archive)) { + wpa_printf(MSG_INFO, "bfd_check_format failed"); + bfd_close(abfd); + return NULL; + } + + if (!bfd_check_format_matches(abfd, bfd_object, &matching)) { + wpa_printf(MSG_INFO, "bfd_check_format_matches failed"); + free(matching); + bfd_close(abfd); + return NULL; + } + + return abfd; +} + + +static void read_syms(bfd *abfd) +{ + long storage, symcount; + bfd_boolean dynamic = FALSE; + + if (syms) + return; + + if (!(bfd_get_file_flags(abfd) & HAS_SYMS)) { + wpa_printf(MSG_INFO, "No symbols"); + return; + } + + storage = bfd_get_symtab_upper_bound(abfd); + if (storage == 0) { + storage = bfd_get_dynamic_symtab_upper_bound(abfd); + dynamic = TRUE; + } + if (storage < 0) { + wpa_printf(MSG_INFO, "Unknown symtab upper bound"); + return; + } + + syms = malloc(storage); + if (syms == NULL) { + wpa_printf(MSG_INFO, "Failed to allocate memory for symtab " + "(%ld bytes)", storage); + return; + } + if (dynamic) + symcount = bfd_canonicalize_dynamic_symtab(abfd, syms); + else + symcount = bfd_canonicalize_symtab(abfd, syms); + if (symcount < 0) { + wpa_printf(MSG_INFO, "Failed to canonicalize %ssymtab", + dynamic ? "dynamic " : ""); + free(syms); + syms = NULL; + return; + } +} + + +struct bfd_data { + bfd_vma pc; + bfd_boolean found; + const char *filename; + const char *function; + unsigned int line; +}; + + +static void find_addr_sect(bfd *abfd, asection *section, void *obj) +{ + struct bfd_data *data = obj; + bfd_vma vma; + bfd_size_type size; + + if (data->found) + return; + + if (!(bfd_get_section_vma(abfd, section))) + return; + + vma = bfd_get_section_vma(abfd, section); + if (data->pc < vma) + return; + + size = bfd_get_section_size(section); + if (data->pc >= vma + size) + return; + + data->found = bfd_find_nearest_line(abfd, section, syms, + data->pc - vma, + &data->filename, + &data->function, + &data->line); +} + + +static void wpa_trace_bfd_addr(void *pc) +{ + bfd *abfd = cached_abfd; + struct bfd_data data; + const char *name; + char *aname = NULL; + const char *filename; + + if (abfd == NULL) + return; + + data.pc = (bfd_vma) pc; + data.found = FALSE; + bfd_map_over_sections(abfd, find_addr_sect, &data); + + if (!data.found) + return; + + do { + if (data.function) + aname = bfd_demangle(abfd, data.function, + DMGL_ANSI | DMGL_PARAMS); + name = aname ? aname : data.function; + filename = data.filename; + if (filename) { + char *end = os_strrchr(filename, '/'); + int i = 0; + while (*filename && *filename == prg_fname[i] && + filename <= end) { + filename++; + i++; + } + } + wpa_printf(MSG_INFO, " %s() %s:%u", + name, filename, data.line); + free(aname); + + data.found = bfd_find_inliner_info(abfd, &data.filename, + &data.function, &data.line); + } while (data.found); +} + + +static const char * wpa_trace_bfd_addr2func(void *pc) +{ + bfd *abfd = cached_abfd; + struct bfd_data data; + + if (abfd == NULL) + return NULL; + + data.pc = (bfd_vma) pc; + data.found = FALSE; + bfd_map_over_sections(abfd, find_addr_sect, &data); + + if (!data.found) + return NULL; + + return data.function; +} + + +static void wpa_trace_bfd_init(void) +{ + if (!prg_fname) { + get_prg_fname(); + if (!prg_fname) + return; + } + + if (!cached_abfd) { + cached_abfd = open_bfd(prg_fname); + if (!cached_abfd) { + wpa_printf(MSG_INFO, "Failed to open bfd"); + return; + } + } + + read_syms(cached_abfd); + if (!syms) { + wpa_printf(MSG_INFO, "Failed to read symbols"); + return; + } +} + + +void wpa_trace_dump_funcname(const char *title, void *pc) +{ + wpa_printf(MSG_INFO, "WPA_TRACE: %s: %p", title, pc); + wpa_trace_bfd_init(); + wpa_trace_bfd_addr(pc); +} + +#else /* WPA_TRACE_BFD */ + +#define wpa_trace_bfd_init() do { } while (0) +#define wpa_trace_bfd_addr(pc) do { } while (0) +#define wpa_trace_bfd_addr2func(pc) NULL + +#endif /* WPA_TRACE_BFD */ + +void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num) +{ + char **sym; + int i; + enum { TRACE_HEAD, TRACE_RELEVANT, TRACE_TAIL } state; + + wpa_trace_bfd_init(); + wpa_printf(MSG_INFO, "WPA_TRACE: %s - START", title); + sym = backtrace_symbols(btrace, btrace_num); + state = TRACE_HEAD; + for (i = 0; i < btrace_num; i++) { + const char *func = wpa_trace_bfd_addr2func(btrace[i]); + if (state == TRACE_HEAD && func && + (os_strcmp(func, "wpa_trace_add_ref_func") == 0 || + os_strcmp(func, "wpa_trace_check_ref") == 0 || + os_strcmp(func, "wpa_trace_show") == 0)) + continue; + if (state == TRACE_TAIL && sym && sym[i] && + os_strstr(sym[i], "__libc_start_main")) + break; + if (state == TRACE_HEAD) + state = TRACE_RELEVANT; + if (sym) + wpa_printf(MSG_INFO, "[%d]: %s", i, sym[i]); + else + wpa_printf(MSG_INFO, "[%d]: ?? [%p]", i, btrace[i]); + wpa_trace_bfd_addr(btrace[i]); + if (state == TRACE_RELEVANT && func && + os_strcmp(func, "main") == 0) + state = TRACE_TAIL; + } + free(sym); + wpa_printf(MSG_INFO, "WPA_TRACE: %s - END", title); +} + + +void wpa_trace_show(const char *title) +{ + struct info { + WPA_TRACE_INFO + } info; + wpa_trace_record(&info); + wpa_trace_dump(title, &info); +} + + +void wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *addr) +{ + if (addr == NULL) + return; + ref->addr = addr; + wpa_trace_record(ref); + dl_list_add(&active_references, &ref->list); +} + + +void wpa_trace_check_ref(const void *addr) +{ + struct wpa_trace_ref *ref; + dl_list_for_each(ref, &active_references, struct wpa_trace_ref, list) { + if (addr != ref->addr) + continue; + wpa_trace_show("Freeing referenced memory"); + wpa_trace_dump("Reference registration", ref); + abort(); + } +} + +#endif /* WPA_TRACE */ diff --git a/contrib/wpa/src/utils/trace.h b/contrib/wpa/src/utils/trace.h new file mode 100644 index 0000000..22d3de0 --- /dev/null +++ b/contrib/wpa/src/utils/trace.h @@ -0,0 +1,74 @@ +/* + * Backtrace debugging + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef TRACE_H +#define TRACE_H + +#define WPA_TRACE_LEN 16 + +#ifdef WPA_TRACE +#include <execinfo.h> + +#include "list.h" + +#define WPA_TRACE_INFO void *btrace[WPA_TRACE_LEN]; int btrace_num; + +struct wpa_trace_ref { + struct dl_list list; + const void *addr; + WPA_TRACE_INFO +}; +#define WPA_TRACE_REF(name) struct wpa_trace_ref wpa_trace_ref_##name + +#define wpa_trace_dump(title, ptr) \ + wpa_trace_dump_func((title), (ptr)->btrace, (ptr)->btrace_num) +void wpa_trace_dump_func(const char *title, void **btrace, int btrace_num); +#define wpa_trace_record(ptr) \ + (ptr)->btrace_num = backtrace((ptr)->btrace, WPA_TRACE_LEN) +void wpa_trace_show(const char *title); +#define wpa_trace_add_ref(ptr, name, addr) \ + wpa_trace_add_ref_func(&(ptr)->wpa_trace_ref_##name, (addr)) +void wpa_trace_add_ref_func(struct wpa_trace_ref *ref, const void *addr); +#define wpa_trace_remove_ref(ptr, name, addr) \ + do { \ + if ((addr)) \ + dl_list_del(&(ptr)->wpa_trace_ref_##name.list); \ + } while (0) +void wpa_trace_check_ref(const void *addr); + +#else /* WPA_TRACE */ + +#define WPA_TRACE_INFO +#define WPA_TRACE_REF(n) +#define wpa_trace_dump(title, ptr) do { } while (0) +#define wpa_trace_record(ptr) do { } while (0) +#define wpa_trace_show(title) do { } while (0) +#define wpa_trace_add_ref(ptr, name, addr) do { } while (0) +#define wpa_trace_remove_ref(ptr, name, addr) do { } while (0) +#define wpa_trace_check_ref(addr) do { } while (0) + +#endif /* WPA_TRACE */ + + +#ifdef WPA_TRACE_BFD + +void wpa_trace_dump_funcname(const char *title, void *pc); + +#else /* WPA_TRACE_BFD */ + +#define wpa_trace_dump_funcname(title, pc) do { } while (0) + +#endif /* WPA_TRACE_BFD */ + +#endif /* TRACE_H */ diff --git a/contrib/wpa/src/utils/uuid.c b/contrib/wpa/src/utils/uuid.c index 620d3d6..d8cc267 100644 --- a/contrib/wpa/src/utils/uuid.c +++ b/contrib/wpa/src/utils/uuid.c @@ -15,8 +15,6 @@ #include "includes.h" #include "common.h" -#include "crypto.h" -#include "sha1.h" #include "uuid.h" int uuid_str2bin(const char *str, u8 *bin) @@ -77,31 +75,3 @@ int is_nil_uuid(const u8 *uuid) return 0; return 1; } - - -void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid) -{ - const u8 *addr[2]; - size_t len[2]; - u8 hash[SHA1_MAC_LEN]; - u8 nsid[16] = { - 0x52, 0x64, 0x80, 0xf8, - 0xc9, 0x9b, - 0x4b, 0xe5, - 0xa6, 0x55, - 0x58, 0xed, 0x5f, 0x5d, 0x60, 0x84 - }; - - addr[0] = nsid; - len[0] = sizeof(nsid); - addr[1] = mac_addr; - len[1] = 6; - sha1_vector(2, addr, len, hash); - os_memcpy(uuid, hash, 16); - - /* Version: 5 = named-based version using SHA-1 */ - uuid[6] = (5 << 4) | (uuid[6] & 0x0f); - - /* Variant specified in RFC 4122 */ - uuid[8] = 0x80 | (uuid[8] & 0x3f); -} diff --git a/contrib/wpa/src/utils/uuid.h b/contrib/wpa/src/utils/uuid.h index 9fc2ba0..0759165 100644 --- a/contrib/wpa/src/utils/uuid.h +++ b/contrib/wpa/src/utils/uuid.h @@ -20,6 +20,5 @@ int uuid_str2bin(const char *str, u8 *bin); int uuid_bin2str(const u8 *bin, char *str, size_t max_len); int is_nil_uuid(const u8 *uuid); -void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid); #endif /* UUID_H */ diff --git a/contrib/wpa/src/utils/wpa_debug.c b/contrib/wpa/src/utils/wpa_debug.c index 0f46aee..6f6fc69 100644 --- a/contrib/wpa/src/utils/wpa_debug.c +++ b/contrib/wpa/src/utils/wpa_debug.c @@ -18,6 +18,8 @@ #ifdef CONFIG_DEBUG_SYSLOG #include <syslog.h> + +static int wpa_debug_syslog = 0; #endif /* CONFIG_DEBUG_SYSLOG */ @@ -27,7 +29,6 @@ static FILE *out_file = NULL; int wpa_debug_level = MSG_INFO; int wpa_debug_show_keys = 0; int wpa_debug_timestamp = 0; -int wpa_debug_syslog = 0; #ifndef CONFIG_NO_STDOUT_DEBUG @@ -49,23 +50,22 @@ void wpa_debug_print_timestamp(void) printf("%ld.%06u: ", (long) tv.sec, (unsigned int) tv.usec); } + +#ifdef CONFIG_DEBUG_SYSLOG void wpa_debug_open_syslog(void) { -#ifdef CONFIG_DEBUG_SYSLOG openlog("wpa_supplicant", LOG_PID | LOG_NDELAY, LOG_DAEMON); wpa_debug_syslog++; -#endif } + void wpa_debug_close_syslog(void) { -#ifdef CONFIG_DEBUG_SYSLOG if (wpa_debug_syslog) closelog(); -#endif } -#ifdef CONFIG_DEBUG_SYSLOG + static int syslog_priority(int level) { switch (level) { @@ -390,6 +390,9 @@ void hostapd_logger(void *ctx, const u8 *addr, unsigned int module, int level, va_end(ap); if (hostapd_logger_cb) hostapd_logger_cb(ctx, addr, module, level, buf, len); + else if (addr) + wpa_printf(MSG_DEBUG, "hostapd_logger: STA " MACSTR " - %s", + MAC2STR(addr), buf); else wpa_printf(MSG_DEBUG, "hostapd_logger: %s", buf); os_free(buf); diff --git a/contrib/wpa/src/utils/wpa_debug.h b/contrib/wpa/src/utils/wpa_debug.h index b4010d5..6e5e79e 100644 --- a/contrib/wpa/src/utils/wpa_debug.h +++ b/contrib/wpa/src/utils/wpa_debug.h @@ -221,6 +221,23 @@ enum hostapd_logger_level { }; +#ifdef CONFIG_DEBUG_SYSLOG + +void wpa_debug_open_syslog(void); +void wpa_debug_close_syslog(void); + +#else /* CONFIG_DEBUG_SYSLOG */ + +static inline void wpa_debug_open_syslog(void) +{ +} + +static inline void wpa_debug_close_syslog(void) +{ +} + +#endif /* CONFIG_DEBUG_SYSLOG */ + #ifdef EAPOL_TEST #define WPA_ASSERT(a) \ diff --git a/contrib/wpa/src/utils/wpabuf.c b/contrib/wpa/src/utils/wpabuf.c index 8181912..eda779e 100644 --- a/contrib/wpa/src/utils/wpabuf.c +++ b/contrib/wpa/src/utils/wpabuf.c @@ -15,13 +15,37 @@ #include "includes.h" #include "common.h" +#include "trace.h" #include "wpabuf.h" +#ifdef WPA_TRACE +#define WPABUF_MAGIC 0x51a974e3 + +struct wpabuf_trace { + unsigned int magic; +}; + +static struct wpabuf_trace * wpabuf_get_trace(const struct wpabuf *buf) +{ + return (struct wpabuf_trace *) + ((const u8 *) buf - sizeof(struct wpabuf_trace)); +} +#endif /* WPA_TRACE */ + + static void wpabuf_overflow(const struct wpabuf *buf, size_t len) { +#ifdef WPA_TRACE + struct wpabuf_trace *trace = wpabuf_get_trace(buf); + if (trace->magic != WPABUF_MAGIC) { + wpa_printf(MSG_ERROR, "wpabuf: invalid magic %x", + trace->magic); + } +#endif /* WPA_TRACE */ wpa_printf(MSG_ERROR, "wpabuf %p (size=%lu used=%lu) overflow len=%lu", buf, (unsigned long) buf->size, (unsigned long) buf->used, (unsigned long) len); + wpa_trace_show("wpabuf overflow"); abort(); } @@ -29,10 +53,25 @@ static void wpabuf_overflow(const struct wpabuf *buf, size_t len) int wpabuf_resize(struct wpabuf **_buf, size_t add_len) { struct wpabuf *buf = *_buf; +#ifdef WPA_TRACE + struct wpabuf_trace *trace; +#endif /* WPA_TRACE */ + if (buf == NULL) { *_buf = wpabuf_alloc(add_len); return *_buf == NULL ? -1 : 0; } + +#ifdef WPA_TRACE + trace = wpabuf_get_trace(buf); + if (trace->magic != WPABUF_MAGIC) { + wpa_printf(MSG_ERROR, "wpabuf: invalid magic %x", + trace->magic); + wpa_trace_show("wpabuf_resize invalid magic"); + abort(); + } +#endif /* WPA_TRACE */ + if (buf->used + add_len > buf->size) { unsigned char *nbuf; if (buf->ext_data) { @@ -42,6 +81,18 @@ int wpabuf_resize(struct wpabuf **_buf, size_t add_len) os_memset(nbuf + buf->used, 0, add_len); buf->ext_data = nbuf; } else { +#ifdef WPA_TRACE + nbuf = os_realloc(trace, sizeof(struct wpabuf_trace) + + sizeof(struct wpabuf) + + buf->used + add_len); + if (nbuf == NULL) + return -1; + trace = (struct wpabuf_trace *) nbuf; + buf = (struct wpabuf *) (trace + 1); + os_memset(nbuf + sizeof(struct wpabuf_trace) + + sizeof(struct wpabuf) + buf->used, 0, + add_len); +#else /* WPA_TRACE */ nbuf = os_realloc(buf, sizeof(struct wpabuf) + buf->used + add_len); if (nbuf == NULL) @@ -49,6 +100,7 @@ int wpabuf_resize(struct wpabuf **_buf, size_t add_len) buf = (struct wpabuf *) nbuf; os_memset(nbuf + sizeof(struct wpabuf) + buf->used, 0, add_len); +#endif /* WPA_TRACE */ *_buf = buf; } buf->size = buf->used + add_len; @@ -65,9 +117,20 @@ int wpabuf_resize(struct wpabuf **_buf, size_t add_len) */ struct wpabuf * wpabuf_alloc(size_t len) { +#ifdef WPA_TRACE + struct wpabuf_trace *trace = os_zalloc(sizeof(struct wpabuf_trace) + + sizeof(struct wpabuf) + len); + struct wpabuf *buf; + if (trace == NULL) + return NULL; + trace->magic = WPABUF_MAGIC; + buf = (struct wpabuf *) (trace + 1); +#else /* WPA_TRACE */ struct wpabuf *buf = os_zalloc(sizeof(struct wpabuf) + len); if (buf == NULL) return NULL; +#endif /* WPA_TRACE */ + buf->size = len; return buf; } @@ -75,9 +138,19 @@ struct wpabuf * wpabuf_alloc(size_t len) struct wpabuf * wpabuf_alloc_ext_data(u8 *data, size_t len) { +#ifdef WPA_TRACE + struct wpabuf_trace *trace = os_zalloc(sizeof(struct wpabuf_trace) + + sizeof(struct wpabuf)); + struct wpabuf *buf; + if (trace == NULL) + return NULL; + trace->magic = WPABUF_MAGIC; + buf = (struct wpabuf *) (trace + 1); +#else /* WPA_TRACE */ struct wpabuf *buf = os_zalloc(sizeof(struct wpabuf)); if (buf == NULL) return NULL; +#endif /* WPA_TRACE */ buf->size = len; buf->used = len; @@ -111,10 +184,25 @@ struct wpabuf * wpabuf_dup(const struct wpabuf *src) */ void wpabuf_free(struct wpabuf *buf) { +#ifdef WPA_TRACE + struct wpabuf_trace *trace; + if (buf == NULL) + return; + trace = wpabuf_get_trace(buf); + if (trace->magic != WPABUF_MAGIC) { + wpa_printf(MSG_ERROR, "wpabuf_free: invalid magic %x", + trace->magic); + wpa_trace_show("wpabuf_free magic mismatch"); + abort(); + } + os_free(buf->ext_data); + os_free(trace); +#else /* WPA_TRACE */ if (buf == NULL) return; os_free(buf->ext_data); os_free(buf); +#endif /* WPA_TRACE */ } diff --git a/contrib/wpa/src/utils/wpabuf.h b/contrib/wpa/src/utils/wpabuf.h index bd8f09e..a150455 100644 --- a/contrib/wpa/src/utils/wpabuf.h +++ b/contrib/wpa/src/utils/wpabuf.h @@ -111,6 +111,12 @@ static inline void wpabuf_put_u8(struct wpabuf *buf, u8 data) *pos = data; } +static inline void wpabuf_put_le16(struct wpabuf *buf, u16 data) +{ + u8 *pos = wpabuf_put(buf, 2); + WPA_PUT_LE16(pos, data); +} + static inline void wpabuf_put_be16(struct wpabuf *buf, u16 data) { u8 *pos = wpabuf_put(buf, 2); diff --git a/contrib/wpa/src/wps/Makefile b/contrib/wpa/src/wps/Makefile index cffba62..9c41962 100644 --- a/contrib/wpa/src/wps/Makefile +++ b/contrib/wpa/src/wps/Makefile @@ -2,7 +2,6 @@ all: @echo Nothing to be made. clean: - for d in $(SUBDIRS); do make -C $$d clean; done rm -f *~ *.o *.d install: diff --git a/contrib/wpa/src/wps/http.h b/contrib/wpa/src/wps/http.h new file mode 100644 index 0000000..2fee3a8 --- /dev/null +++ b/contrib/wpa/src/wps/http.h @@ -0,0 +1,29 @@ +/* + * HTTP for WPS + * Copyright (c) 2000-2003 Intel Corporation + * Copyright (c) 2006-2007 Sony Corporation + * Copyright (c) 2008-2009 Atheros Communications + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * See wps_upnp.c for more details on licensing and code history. + */ + +#ifndef HTTP_H +#define HTTP_H + +enum http_reply_code { + HTTP_OK = 200, + HTTP_BAD_REQUEST = 400, + UPNP_INVALID_ACTION = 401, + UPNP_INVALID_ARGS = 402, + HTTP_NOT_FOUND = 404, + HTTP_PRECONDITION_FAILED = 412, + HTTP_INTERNAL_SERVER_ERROR = 500, + HTTP_UNIMPLEMENTED = 501, + UPNP_ACTION_FAILED = 501, + UPNP_ARG_VALUE_INVALID = 600, + UPNP_ARG_VALUE_OUT_OF_RANGE = 601, + UPNP_OUT_OF_MEMORY = 603 +}; + +#endif /* HTTP_H */ diff --git a/contrib/wpa/src/wps/http_client.c b/contrib/wpa/src/wps/http_client.c new file mode 100644 index 0000000..fea2a04 --- /dev/null +++ b/contrib/wpa/src/wps/http_client.c @@ -0,0 +1,371 @@ +/* + * http_client - HTTP client + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <fcntl.h> + +#include "common.h" +#include "eloop.h" +#include "httpread.h" +#include "http_client.h" + + +#define HTTP_CLIENT_TIMEOUT 30 + + +struct http_client { + struct sockaddr_in dst; + int sd; + struct wpabuf *req; + size_t req_pos; + size_t max_response; + + void (*cb)(void *ctx, struct http_client *c, + enum http_client_event event); + void *cb_ctx; + struct httpread *hread; + struct wpabuf body; +}; + + +static void http_client_timeout(void *eloop_data, void *user_ctx) +{ + struct http_client *c = eloop_data; + wpa_printf(MSG_DEBUG, "HTTP: Timeout"); + c->cb(c->cb_ctx, c, HTTP_CLIENT_TIMEOUT); +} + + +static void http_client_got_response(struct httpread *handle, void *cookie, + enum httpread_event e) +{ + struct http_client *c = cookie; + + eloop_cancel_timeout(http_client_timeout, c, NULL); + switch (e) { + case HTTPREAD_EVENT_FILE_READY: + if (httpread_hdr_type_get(c->hread) == HTTPREAD_HDR_TYPE_REPLY) + { + int reply_code = httpread_reply_code_get(c->hread); + if (reply_code == 200 /* OK */) { + wpa_printf(MSG_DEBUG, "HTTP: Response OK from " + "%s:%d", + inet_ntoa(c->dst.sin_addr), + ntohs(c->dst.sin_port)); + c->cb(c->cb_ctx, c, HTTP_CLIENT_OK); + } else { + wpa_printf(MSG_DEBUG, "HTTP: Error %d from " + "%s:%d", reply_code, + inet_ntoa(c->dst.sin_addr), + ntohs(c->dst.sin_port)); + c->cb(c->cb_ctx, c, HTTP_CLIENT_INVALID_REPLY); + } + } else + c->cb(c->cb_ctx, c, HTTP_CLIENT_INVALID_REPLY); + break; + case HTTPREAD_EVENT_TIMEOUT: + c->cb(c->cb_ctx, c, HTTP_CLIENT_TIMEOUT); + break; + case HTTPREAD_EVENT_ERROR: + c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED); + break; + } +} + + +static void http_client_tx_ready(int sock, void *eloop_ctx, void *sock_ctx) +{ + struct http_client *c = eloop_ctx; + int res; + + wpa_printf(MSG_DEBUG, "HTTP: Send client request to %s:%d (%lu of %lu " + "bytes remaining)", + inet_ntoa(c->dst.sin_addr), ntohs(c->dst.sin_port), + (unsigned long) wpabuf_len(c->req), + (unsigned long) wpabuf_len(c->req) - c->req_pos); + + res = send(c->sd, wpabuf_head(c->req) + c->req_pos, + wpabuf_len(c->req) - c->req_pos, 0); + if (res < 0) { + wpa_printf(MSG_DEBUG, "HTTP: Failed to send buffer: %s", + strerror(errno)); + eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE); + c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED); + return; + } + + if ((size_t) res < wpabuf_len(c->req) - c->req_pos) { + wpa_printf(MSG_DEBUG, "HTTP: Sent %d of %lu bytes; %lu bytes " + "remaining", + res, (unsigned long) wpabuf_len(c->req), + (unsigned long) wpabuf_len(c->req) - c->req_pos - + res); + c->req_pos += res; + return; + } + + wpa_printf(MSG_DEBUG, "HTTP: Full client request sent to %s:%d", + inet_ntoa(c->dst.sin_addr), ntohs(c->dst.sin_port)); + eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE); + wpabuf_free(c->req); + c->req = NULL; + + c->hread = httpread_create(c->sd, http_client_got_response, c, + c->max_response, HTTP_CLIENT_TIMEOUT); + if (c->hread == NULL) { + c->cb(c->cb_ctx, c, HTTP_CLIENT_FAILED); + return; + } +} + + +struct http_client * http_client_addr(struct sockaddr_in *dst, + struct wpabuf *req, size_t max_response, + void (*cb)(void *ctx, + struct http_client *c, + enum http_client_event event), + void *cb_ctx) +{ + struct http_client *c; + + c = os_zalloc(sizeof(*c)); + if (c == NULL) + return NULL; + c->sd = -1; + c->dst = *dst; + c->max_response = max_response; + c->cb = cb; + c->cb_ctx = cb_ctx; + + c->sd = socket(AF_INET, SOCK_STREAM, 0); + if (c->sd < 0) { + http_client_free(c); + return NULL; + } + + if (fcntl(c->sd, F_SETFL, O_NONBLOCK) != 0) { + wpa_printf(MSG_DEBUG, "HTTP: fnctl(O_NONBLOCK) failed: %s", + strerror(errno)); + http_client_free(c); + return NULL; + } + + if (connect(c->sd, (struct sockaddr *) dst, sizeof(*dst))) { + if (errno != EINPROGRESS) { + wpa_printf(MSG_DEBUG, "HTTP: Failed to connect: %s", + strerror(errno)); + http_client_free(c); + return NULL; + } + + /* + * Continue connecting in the background; eloop will call us + * once the connection is ready (or failed). + */ + } + + if (eloop_register_sock(c->sd, EVENT_TYPE_WRITE, http_client_tx_ready, + c, NULL)) { + http_client_free(c); + return NULL; + } + + if (eloop_register_timeout(HTTP_CLIENT_TIMEOUT, 0, http_client_timeout, + c, NULL)) { + http_client_free(c); + return NULL; + } + + c->req = req; + + return c; +} + + +char * http_client_url_parse(const char *url, struct sockaddr_in *dst, + char **ret_path) +{ + char *u, *addr, *port, *path; + + u = os_strdup(url); + if (u == NULL) + return NULL; + + os_memset(dst, 0, sizeof(*dst)); + dst->sin_family = AF_INET; + addr = u + 7; + path = os_strchr(addr, '/'); + port = os_strchr(addr, ':'); + if (path == NULL) { + path = "/"; + } else { + *path = '\0'; /* temporary nul termination for address */ + if (port > path) + port = NULL; + } + if (port) + *port++ = '\0'; + + if (inet_aton(addr, &dst->sin_addr) == 0) { + /* TODO: name lookup */ + wpa_printf(MSG_DEBUG, "HTTP: Unsupported address in URL '%s' " + "(addr='%s' port='%s')", + url, addr, port); + os_free(u); + return NULL; + } + + if (port) + dst->sin_port = htons(atoi(port)); + else + dst->sin_port = htons(80); + + if (*path == '\0') { + /* remove temporary nul termination for address */ + *path = '/'; + } + + *ret_path = path; + + return u; +} + + +struct http_client * http_client_url(const char *url, + struct wpabuf *req, size_t max_response, + void (*cb)(void *ctx, + struct http_client *c, + enum http_client_event event), + void *cb_ctx) +{ + struct sockaddr_in dst; + struct http_client *c; + char *u, *path; + struct wpabuf *req_buf = NULL; + + if (os_strncmp(url, "http://", 7) != 0) + return NULL; + u = http_client_url_parse(url, &dst, &path); + if (u == NULL) + return NULL; + + if (req == NULL) { + req_buf = wpabuf_alloc(os_strlen(url) + 1000); + if (req_buf == NULL) { + os_free(u); + return NULL; + } + req = req_buf; + wpabuf_printf(req, + "GET %s HTTP/1.1\r\n" + "Cache-Control: no-cache\r\n" + "Pragma: no-cache\r\n" + "Accept: text/xml, application/xml\r\n" + "User-Agent: wpa_supplicant\r\n" + "Host: %s:%d\r\n" + "\r\n", + path, inet_ntoa(dst.sin_addr), + ntohs(dst.sin_port)); + } + os_free(u); + + c = http_client_addr(&dst, req, max_response, cb, cb_ctx); + if (c == NULL) { + wpabuf_free(req_buf); + return NULL; + } + + return c; +} + + +void http_client_free(struct http_client *c) +{ + if (c == NULL) + return; + httpread_destroy(c->hread); + wpabuf_free(c->req); + if (c->sd >= 0) { + eloop_unregister_sock(c->sd, EVENT_TYPE_WRITE); + close(c->sd); + } + eloop_cancel_timeout(http_client_timeout, c, NULL); + os_free(c); +} + + +struct wpabuf * http_client_get_body(struct http_client *c) +{ + if (c->hread == NULL) + return NULL; + wpabuf_set(&c->body, httpread_data_get(c->hread), + httpread_length_get(c->hread)); + return &c->body; +} + + +char * http_client_get_hdr_line(struct http_client *c, const char *tag) +{ + if (c->hread == NULL) + return NULL; + return httpread_hdr_line_get(c->hread, tag); +} + + +char * http_link_update(char *url, const char *base) +{ + char *n; + size_t len; + const char *pos; + + /* RFC 2396, Chapter 5.2 */ + /* TODO: consider adding all cases described in RFC 2396 */ + + if (url == NULL) + return NULL; + + if (os_strncmp(url, "http://", 7) == 0) + return url; /* absolute link */ + + if (os_strncmp(base, "http://", 7) != 0) + return url; /* unable to handle base URL */ + + len = os_strlen(url) + 1 + os_strlen(base) + 1; + n = os_malloc(len); + if (n == NULL) + return url; /* failed */ + + if (url[0] == '/') { + pos = os_strchr(base + 7, '/'); + if (pos == NULL) { + os_snprintf(n, len, "%s%s", base, url); + } else { + os_memcpy(n, base, pos - base); + os_memcpy(n + (pos - base), url, os_strlen(url) + 1); + } + } else { + pos = os_strrchr(base + 7, '/'); + if (pos == NULL) { + os_snprintf(n, len, "%s/%s", base, url); + } else { + os_memcpy(n, base, pos - base + 1); + os_memcpy(n + (pos - base) + 1, url, os_strlen(url) + + 1); + } + } + + os_free(url); + + return n; +} diff --git a/contrib/wpa/src/wps/http_client.h b/contrib/wpa/src/wps/http_client.h new file mode 100644 index 0000000..924d6ab --- /dev/null +++ b/contrib/wpa/src/wps/http_client.h @@ -0,0 +1,46 @@ +/* + * http_client - HTTP client + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef HTTP_CLIENT_H +#define HTTP_CLIENT_H + +struct http_client; + +enum http_client_event { + HTTP_CLIENT_FAILED, + HTTP_CLIENT_TIMEOUT, + HTTP_CLIENT_OK, + HTTP_CLIENT_INVALID_REPLY, +}; + +char * http_client_url_parse(const char *url, struct sockaddr_in *dst, + char **path); +struct http_client * http_client_addr(struct sockaddr_in *dst, + struct wpabuf *req, size_t max_response, + void (*cb)(void *ctx, + struct http_client *c, + enum http_client_event event), + void *cb_ctx); +struct http_client * http_client_url(const char *url, + struct wpabuf *req, size_t max_response, + void (*cb)(void *ctx, + struct http_client *c, + enum http_client_event event), + void *cb_ctx); +void http_client_free(struct http_client *c); +struct wpabuf * http_client_get_body(struct http_client *c); +char * http_client_get_hdr_line(struct http_client *c, const char *tag); +char * http_link_update(char *url, const char *base); + +#endif /* HTTP_CLIENT_H */ diff --git a/contrib/wpa/src/wps/http_server.c b/contrib/wpa/src/wps/http_server.c new file mode 100644 index 0000000..356f599 --- /dev/null +++ b/contrib/wpa/src/wps/http_server.c @@ -0,0 +1,312 @@ +/* + * http_server - HTTP server + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include <fcntl.h> + +#include "common.h" +#include "eloop.h" +#include "httpread.h" +#include "http_server.h" + +#define HTTP_SERVER_TIMEOUT 30 +#define HTTP_SERVER_MAX_REQ_LEN 8000 +#define HTTP_SERVER_MAX_CONNECTIONS 10 + +struct http_request { + struct http_request *next; + struct http_server *srv; + int fd; + struct sockaddr_in cli; + struct httpread *hread; +}; + +struct http_server { + void (*cb)(void *ctx, struct http_request *req); + void *cb_ctx; + + int fd; + int port; + + struct http_request *requests; + unsigned int request_count; +}; + + +static void http_request_cb(struct httpread *handle, void *cookie, + enum httpread_event en) +{ + struct http_request *req = cookie; + struct http_server *srv = req->srv; + + if (en == HTTPREAD_EVENT_FILE_READY) { + wpa_printf(MSG_DEBUG, "HTTP: Request from %s:%d received", + inet_ntoa(req->cli.sin_addr), + ntohs(req->cli.sin_port)); + srv->cb(srv->cb_ctx, req); + return; + } + wpa_printf(MSG_DEBUG, "HTTP: Request from %s:%d could not be received " + "completely", inet_ntoa(req->cli.sin_addr), + ntohs(req->cli.sin_port)); + http_request_deinit(req); +} + + +static struct http_request * http_request_init(struct http_server *srv, int fd, + struct sockaddr_in *cli) +{ + struct http_request *req; + + if (srv->request_count >= HTTP_SERVER_MAX_CONNECTIONS) { + wpa_printf(MSG_DEBUG, "HTTP: Too many concurrent requests"); + return NULL; + } + + req = os_zalloc(sizeof(*req)); + if (req == NULL) + return NULL; + + req->srv = srv; + req->fd = fd; + req->cli = *cli; + + req->hread = httpread_create(req->fd, http_request_cb, req, + HTTP_SERVER_MAX_REQ_LEN, + HTTP_SERVER_TIMEOUT); + if (req->hread == NULL) { + http_request_deinit(req); + return NULL; + } + + return req; +} + + +void http_request_deinit(struct http_request *req) +{ + struct http_request *r, *p; + struct http_server *srv; + + if (req == NULL) + return; + + srv = req->srv; + p = NULL; + r = srv->requests; + while (r) { + if (r == req) { + if (p) + p->next = r->next; + else + srv->requests = r->next; + srv->request_count--; + break; + } + p = r; + r = r->next; + } + + httpread_destroy(req->hread); + close(req->fd); + os_free(req); +} + + +static void http_request_free_all(struct http_request *req) +{ + struct http_request *prev; + while (req) { + prev = req; + req = req->next; + http_request_deinit(prev); + } +} + + +void http_request_send(struct http_request *req, struct wpabuf *resp) +{ + int res; + + wpa_printf(MSG_DEBUG, "HTTP: Send %lu byte response to %s:%d", + (unsigned long) wpabuf_len(resp), + inet_ntoa(req->cli.sin_addr), + ntohs(req->cli.sin_port)); + + res = send(req->fd, wpabuf_head(resp), wpabuf_len(resp), 0); + if (res < 0) { + wpa_printf(MSG_DEBUG, "HTTP: Send failed: %s", + strerror(errno)); + } else if ((size_t) res < wpabuf_len(resp)) { + wpa_printf(MSG_DEBUG, "HTTP: Sent only %d of %lu bytes", + res, (unsigned long) wpabuf_len(resp)); + /* TODO: add eloop handler for sending rest of the data */ + } + + wpabuf_free(resp); +} + + +void http_request_send_and_deinit(struct http_request *req, + struct wpabuf *resp) +{ + http_request_send(req, resp); + http_request_deinit(req); +} + + +enum httpread_hdr_type http_request_get_type(struct http_request *req) +{ + return httpread_hdr_type_get(req->hread); +} + + +char * http_request_get_uri(struct http_request *req) +{ + return httpread_uri_get(req->hread); +} + + +char * http_request_get_hdr(struct http_request *req) +{ + return httpread_hdr_get(req->hread); +} + + +char * http_request_get_data(struct http_request *req) +{ + return httpread_data_get(req->hread); +} + + +char * http_request_get_hdr_line(struct http_request *req, const char *tag) +{ + return httpread_hdr_line_get(req->hread, tag); +} + + +struct sockaddr_in * http_request_get_cli_addr(struct http_request *req) +{ + return &req->cli; +} + + +static void http_server_cb(int sd, void *eloop_ctx, void *sock_ctx) +{ + struct sockaddr_in addr; + socklen_t addr_len = sizeof(addr); + struct http_server *srv = eloop_ctx; + int conn; + struct http_request *req; + + conn = accept(srv->fd, (struct sockaddr *) &addr, &addr_len); + if (conn < 0) { + wpa_printf(MSG_DEBUG, "HTTP: Failed to accept new connection: " + "%s", strerror(errno)); + return; + } + wpa_printf(MSG_DEBUG, "HTTP: Connection from %s:%d", + inet_ntoa(addr.sin_addr), ntohs(addr.sin_port)); + + req = http_request_init(srv, conn, &addr); + if (req == NULL) { + close(conn); + return; + } + + req->next = srv->requests; + srv->requests = req; + srv->request_count++; +} + + +struct http_server * http_server_init(struct in_addr *addr, int port, + void (*cb)(void *ctx, + struct http_request *req), + void *cb_ctx) +{ + struct sockaddr_in sin; + struct http_server *srv; + + srv = os_zalloc(sizeof(*srv)); + if (srv == NULL) + return NULL; + srv->cb = cb; + srv->cb_ctx = cb_ctx; + + srv->fd = socket(AF_INET, SOCK_STREAM, 0); + if (srv->fd < 0) + goto fail; + if (fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0) + goto fail; + if (port < 0) + srv->port = 49152; + else + srv->port = port; + + os_memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = addr->s_addr; + + for (;;) { + sin.sin_port = htons(srv->port); + if (bind(srv->fd, (struct sockaddr *) &sin, sizeof(sin)) == 0) + break; + if (errno == EADDRINUSE) { + /* search for unused port */ + if (++srv->port == 65535 || port >= 0) + goto fail; + continue; + } + wpa_printf(MSG_DEBUG, "HTTP: Failed to bind server port %d: " + "%s", srv->port, strerror(errno)); + goto fail; + } + if (listen(srv->fd, 10 /* max backlog */) < 0) + goto fail; + if (fcntl(srv->fd, F_SETFL, O_NONBLOCK) < 0) + goto fail; + if (eloop_register_sock(srv->fd, EVENT_TYPE_READ, http_server_cb, + srv, NULL)) + goto fail; + + wpa_printf(MSG_DEBUG, "HTTP: Started server on %s:%d", + inet_ntoa(*addr), srv->port); + + return srv; + +fail: + http_server_deinit(srv); + return NULL; +} + + +void http_server_deinit(struct http_server *srv) +{ + if (srv == NULL) + return; + if (srv->fd >= 0) { + eloop_unregister_sock(srv->fd, EVENT_TYPE_READ); + close(srv->fd); + } + http_request_free_all(srv->requests); + + os_free(srv); +} + + +int http_server_get_port(struct http_server *srv) +{ + return srv->port; +} diff --git a/contrib/wpa/src/wps/http_server.h b/contrib/wpa/src/wps/http_server.h new file mode 100644 index 0000000..219941c --- /dev/null +++ b/contrib/wpa/src/wps/http_server.h @@ -0,0 +1,39 @@ +/* + * http_server - HTTP server + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef HTTP_SERVER_H +#define HTTP_SERVER_H + +struct http_server; +struct http_request; + +void http_request_deinit(struct http_request *req); +void http_request_send(struct http_request *req, struct wpabuf *resp); +void http_request_send_and_deinit(struct http_request *req, + struct wpabuf *resp); +enum httpread_hdr_type http_request_get_type(struct http_request *req); +char * http_request_get_uri(struct http_request *req); +char * http_request_get_hdr(struct http_request *req); +char * http_request_get_data(struct http_request *req); +char * http_request_get_hdr_line(struct http_request *req, const char *tag); +struct sockaddr_in * http_request_get_cli_addr(struct http_request *req); + +struct http_server * http_server_init(struct in_addr *addr, int port, + void (*cb)(void *ctx, + struct http_request *req), + void *cb_ctx); +void http_server_deinit(struct http_server *srv); +int http_server_get_port(struct http_server *srv); + +#endif /* HTTP_SERVER_H */ diff --git a/contrib/wpa/src/wps/httpread.c b/contrib/wpa/src/wps/httpread.c index 0d7165e..40422e4 100644 --- a/contrib/wpa/src/wps/httpread.c +++ b/contrib/wpa/src/wps/httpread.c @@ -1,4 +1,4 @@ -/** +/* * httpread - Manage reading file(s) from HTTP/TCP socket * Author: Ted Merrill * Copyright 2008 Atheros Communications diff --git a/contrib/wpa/src/wps/httpread.h b/contrib/wpa/src/wps/httpread.h index fb1ecb7..51aa214 100644 --- a/contrib/wpa/src/wps/httpread.h +++ b/contrib/wpa/src/wps/httpread.h @@ -1,4 +1,4 @@ -/** +/* * httpread - Manage reading file(s) from HTTP/TCP socket * Author: Ted Merrill * Copyright 2008 Atheros Communications diff --git a/contrib/wpa/src/wps/ndef.c b/contrib/wpa/src/wps/ndef.c new file mode 100644 index 0000000..9baec7f --- /dev/null +++ b/contrib/wpa/src/wps/ndef.c @@ -0,0 +1,175 @@ +/* + * NDEF(NFC Data Exchange Format) routines for Wi-Fi Protected Setup + * Reference is "NFCForum-TS-NDEF_1.0 2006-07-24". + * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include "common.h" +#include "wps/wps.h" +#include "wps/wps_i.h" + +#define FLAG_MESSAGE_BEGIN (1 << 7) +#define FLAG_MESSAGE_END (1 << 6) +#define FLAG_CHUNK (1 << 5) +#define FLAG_SHORT_RECORD (1 << 4) +#define FLAG_ID_LENGTH_PRESENT (1 << 3) +#define FLAG_TNF_RFC2046 (0x02) + +struct ndef_record { + u8 *type; + u8 *id; + u8 *payload; + u8 type_length; + u8 id_length; + u32 payload_length; + u32 total_length; +}; + +static char wifi_handover_type[] = "application/vnd.wfa.wsc"; + +static int ndef_parse_record(u8 *data, u32 size, struct ndef_record *record) +{ + u8 *pos = data + 1; + + if (size < 2) + return -1; + record->type_length = *pos++; + if (data[0] & FLAG_SHORT_RECORD) { + if (size < 3) + return -1; + record->payload_length = *pos++; + } else { + if (size < 6) + return -1; + record->payload_length = ntohl(*(u32 *)pos); + pos += sizeof(u32); + } + + if (data[0] & FLAG_ID_LENGTH_PRESENT) { + if ((int) size < pos - data + 1) + return -1; + record->id_length = *pos++; + } else + record->id_length = 0; + + record->type = record->type_length == 0 ? NULL : pos; + pos += record->type_length; + + record->id = record->id_length == 0 ? NULL : pos; + pos += record->id_length; + + record->payload = record->payload_length == 0 ? NULL : pos; + pos += record->payload_length; + + record->total_length = pos - data; + if (record->total_length > size) + return -1; + return 0; +} + + +static struct wpabuf * ndef_parse_records(struct wpabuf *buf, + int (*filter)(struct ndef_record *)) +{ + struct ndef_record record; + int len = wpabuf_len(buf); + u8 *data = wpabuf_mhead(buf); + + while (len > 0) { + if (ndef_parse_record(data, len, &record) < 0) { + wpa_printf(MSG_ERROR, "NDEF : Failed to parse"); + return NULL; + } + if (filter == NULL || filter(&record)) + return wpabuf_alloc_copy(record.payload, + record.payload_length); + data += record.total_length; + len -= record.total_length; + } + wpa_printf(MSG_ERROR, "NDEF : Record not found"); + return NULL; +} + + +static struct wpabuf * ndef_build_record(u8 flags, void *type, + u8 type_length, void *id, + u8 id_length, void *payload, + u32 payload_length) +{ + struct wpabuf *record; + size_t total_len; + int short_record; + u8 local_flag; + + short_record = payload_length < 256 ? 1 : 0; + + total_len = 2; /* flag + type length */ + /* payload length */ + total_len += short_record ? sizeof(u8) : sizeof(u32); + if (id_length > 0) + total_len += 1; + total_len += type_length + id_length + payload_length; + record = wpabuf_alloc(total_len); + if (record == NULL) { + wpa_printf(MSG_ERROR, "NDEF : Failed to allocate " + "record for build"); + return NULL; + } + + local_flag = flags; + if (id_length > 0) + local_flag |= FLAG_ID_LENGTH_PRESENT; + if (short_record) + local_flag |= FLAG_SHORT_RECORD; + wpabuf_put_u8(record, local_flag); + + wpabuf_put_u8(record, type_length); + + if (short_record) + wpabuf_put_u8(record, payload_length); + else + wpabuf_put_be32(record, payload_length); + + if (id_length > 0) + wpabuf_put_u8(record, id_length); + wpabuf_put_data(record, type, type_length); + wpabuf_put_data(record, id, id_length); + wpabuf_put_data(record, payload, payload_length); + return record; +} + + +static int wifi_filter(struct ndef_record *record) +{ + if (record->type_length != os_strlen(wifi_handover_type)) + return 0; + if (os_memcmp(record->type, wifi_handover_type, + os_strlen(wifi_handover_type)) != 0) + return 0; + return 1; +} + + +struct wpabuf * ndef_parse_wifi(struct wpabuf *buf) +{ + return ndef_parse_records(buf, wifi_filter); +} + + +struct wpabuf * ndef_build_wifi(struct wpabuf *buf) +{ + return ndef_build_record(FLAG_MESSAGE_BEGIN | FLAG_MESSAGE_END | + FLAG_TNF_RFC2046, wifi_handover_type, + os_strlen(wifi_handover_type), NULL, 0, + wpabuf_mhead(buf), wpabuf_len(buf)); +} diff --git a/contrib/wpa/src/wps/upnp_xml.c b/contrib/wpa/src/wps/upnp_xml.c new file mode 100644 index 0000000..b1b1e2b --- /dev/null +++ b/contrib/wpa/src/wps/upnp_xml.c @@ -0,0 +1,252 @@ +/* + * UPnP XML helper routines + * Copyright (c) 2000-2003 Intel Corporation + * Copyright (c) 2006-2007 Sony Corporation + * Copyright (c) 2008-2009 Atheros Communications + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * See wps_upnp.c for more details on licensing and code history. + */ + +#include "includes.h" + +#include "common.h" +#include "base64.h" +#include "http.h" +#include "upnp_xml.h" + + +/* + * XML parsing and formatting + * + * XML is a markup language based on unicode; usually (and in our case, + * always!) based on utf-8. utf-8 uses a variable number of bytes per + * character. utf-8 has the advantage that all non-ASCII unicode characters are + * represented by sequences of non-ascii (high bit set) bytes, whereas ASCII + * characters are single ascii bytes, thus we can use typical text processing. + * + * (One other interesting thing about utf-8 is that it is possible to look at + * any random byte and determine if it is the first byte of a character as + * versus a continuation byte). + * + * The base syntax of XML uses a few ASCII punctionation characters; any + * characters that would appear in the payload data are rewritten using + * sequences, e.g., & for ampersand(&) and < for left angle bracket (<). + * Five such escapes total (more can be defined but that does not apply to our + * case). Thus we can safely parse for angle brackets etc. + * + * XML describes tree structures of tagged data, with each element beginning + * with an opening tag <label> and ending with a closing tag </label> with + * matching label. (There is also a self-closing tag <label/> which is supposed + * to be equivalent to <label></label>, i.e., no payload, but we are unlikely + * to see it for our purpose). + * + * Actually the opening tags are a little more complicated because they can + * contain "attributes" after the label (delimited by ascii space or tab chars) + * of the form attribute_label="value" or attribute_label='value'; as it turns + * out we do not have to read any of these attributes, just ignore them. + * + * Labels are any sequence of chars other than space, tab, right angle bracket + * (and ?), but may have an inner structure of <namespace><colon><plain_label>. + * As it turns out, we can ignore the namespaces, in fact we can ignore the + * entire tree hierarchy, because the plain labels we are looking for will be + * unique (not in general, but for this application). We do however have to be + * careful to skip over the namespaces. + * + * In generating XML we have to be more careful, but that is easy because + * everything we do is pretty canned. The only real care to take is to escape + * any special chars in our payload. + */ + +/** + * xml_next_tag - Advance to next tag + * @in: Input + * @out: OUT: start of tag just after '<' + * @out_tagname: OUT: start of name of tag, skipping namespace + * @end: OUT: one after tag + * Returns: 0 on success, 1 on failure + * + * A tag has form: + * <left angle bracket><...><right angle bracket> + * Within the angle brackets, there is an optional leading forward slash (which + * makes the tag an ending tag), then an optional leading label (followed by + * colon) and then the tag name itself. + * + * Note that angle brackets present in the original data must have been encoded + * as < and > so they will not trouble us. + */ +static int xml_next_tag(const char *in, const char **out, + const char **out_tagname, const char **end) +{ + while (*in && *in != '<') + in++; + if (*in != '<') + return 1; + *out = ++in; + if (*in == '/') + in++; + *out_tagname = in; /* maybe */ + while (isalnum(*in) || *in == '-') + in++; + if (*in == ':') + *out_tagname = ++in; + while (*in && *in != '>') + in++; + if (*in != '>') + return 1; + *end = ++in; + return 0; +} + + +/* xml_data_encode -- format data for xml file, escaping special characters. + * + * Note that we assume we are using utf8 both as input and as output! + * In utf8, characters may be classed as follows: + * 0xxxxxxx(2) -- 1 byte ascii char + * 11xxxxxx(2) -- 1st byte of multi-byte char w/ unicode value >= 0x80 + * 110xxxxx(2) -- 1st byte of 2 byte sequence (5 payload bits here) + * 1110xxxx(2) -- 1st byte of 3 byte sequence (4 payload bits here) + * 11110xxx(2) -- 1st byte of 4 byte sequence (3 payload bits here) + * 10xxxxxx(2) -- extension byte (6 payload bits per byte) + * Some values implied by the above are however illegal because they + * do not represent unicode chars or are not the shortest encoding. + * Actually, we can almost entirely ignore the above and just do + * text processing same as for ascii text. + * + * XML is written with arbitrary unicode characters, except that five + * characters have special meaning and so must be escaped where they + * appear in payload data... which we do here. + */ +void xml_data_encode(struct wpabuf *buf, const char *data, int len) +{ + int i; + for (i = 0; i < len; i++) { + u8 c = ((u8 *) data)[i]; + if (c == '<') { + wpabuf_put_str(buf, "<"); + continue; + } + if (c == '>') { + wpabuf_put_str(buf, ">"); + continue; + } + if (c == '&') { + wpabuf_put_str(buf, "&"); + continue; + } + if (c == '\'') { + wpabuf_put_str(buf, "'"); + continue; + } + if (c == '"') { + wpabuf_put_str(buf, """); + continue; + } + /* + * We could try to represent control characters using the + * sequence: &#x; where x is replaced by a hex numeral, but not + * clear why we would do this. + */ + wpabuf_put_u8(buf, c); + } +} + + +/* xml_add_tagged_data -- format tagged data as a new xml line. + * + * tag must not have any special chars. + * data may have special chars, which are escaped. + */ +void xml_add_tagged_data(struct wpabuf *buf, const char *tag, const char *data) +{ + wpabuf_printf(buf, "<%s>", tag); + xml_data_encode(buf, data, os_strlen(data)); + wpabuf_printf(buf, "</%s>\n", tag); +} + + +/* A POST body looks something like (per upnp spec): + * <?xml version="1.0"?> + * <s:Envelope + * xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" + * s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> + * <s:Body> + * <u:actionName xmlns:u="urn:schemas-upnp-org:service:serviceType:v"> + * <argumentName>in arg value</argumentName> + * other in args and their values go here, if any + * </u:actionName> + * </s:Body> + * </s:Envelope> + * + * where : + * s: might be some other namespace name followed by colon + * u: might be some other namespace name followed by colon + * actionName will be replaced according to action requested + * schema following actionName will be WFA scheme instead + * argumentName will be actual argument name + * (in arg value) will be actual argument value + */ +char * xml_get_first_item(const char *doc, const char *item) +{ + const char *match = item; + int match_len = os_strlen(item); + const char *tag, *tagname, *end; + char *value; + + /* + * This is crude: ignore any possible tag name conflicts and go right + * to the first tag of this name. This should be ok for the limited + * domain of UPnP messages. + */ + for (;;) { + if (xml_next_tag(doc, &tag, &tagname, &end)) + return NULL; + doc = end; + if (!os_strncasecmp(tagname, match, match_len) && + *tag != '/' && + (tagname[match_len] == '>' || + !isgraph(tagname[match_len]))) { + break; + } + } + end = doc; + while (*end && *end != '<') + end++; + value = os_zalloc(1 + (end - doc)); + if (value == NULL) + return NULL; + os_memcpy(value, doc, end - doc); + return value; +} + + +struct wpabuf * xml_get_base64_item(const char *data, const char *name, + enum http_reply_code *ret) +{ + char *msg; + struct wpabuf *buf; + unsigned char *decoded; + size_t len; + + msg = xml_get_first_item(data, name); + if (msg == NULL) { + *ret = UPNP_ARG_VALUE_INVALID; + return NULL; + } + + decoded = base64_decode((unsigned char *) msg, os_strlen(msg), &len); + os_free(msg); + if (decoded == NULL) { + *ret = UPNP_OUT_OF_MEMORY; + return NULL; + } + + buf = wpabuf_alloc_ext_data(decoded, len); + if (buf == NULL) { + os_free(decoded); + *ret = UPNP_OUT_OF_MEMORY; + return NULL; + } + return buf; +} diff --git a/contrib/wpa/src/wps/upnp_xml.h b/contrib/wpa/src/wps/upnp_xml.h new file mode 100644 index 0000000..62dbe60 --- /dev/null +++ b/contrib/wpa/src/wps/upnp_xml.h @@ -0,0 +1,23 @@ +/* + * UPnP XML helper routines + * Copyright (c) 2000-2003 Intel Corporation + * Copyright (c) 2006-2007 Sony Corporation + * Copyright (c) 2008-2009 Atheros Communications + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * See wps_upnp.c for more details on licensing and code history. + */ + +#ifndef UPNP_XML_H +#define UPNP_XML_H + +#include "http.h" + +void xml_data_encode(struct wpabuf *buf, const char *data, int len); +void xml_add_tagged_data(struct wpabuf *buf, const char *tag, + const char *data); +char * xml_get_first_item(const char *doc, const char *item); +struct wpabuf * xml_get_base64_item(const char *data, const char *name, + enum http_reply_code *ret); + +#endif /* UPNP_XML_H */ diff --git a/contrib/wpa/src/wps/wps.c b/contrib/wpa/src/wps/wps.c index 395eba6..619af15 100644 --- a/contrib/wpa/src/wps/wps.c +++ b/contrib/wpa/src/wps/wps.c @@ -1,6 +1,6 @@ /* * Wi-Fi Protected Setup - * Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -15,9 +15,10 @@ #include "includes.h" #include "common.h" +#include "crypto/dh_group5.h" +#include "common/ieee802_11_defs.h" #include "wps_i.h" #include "wps_dev_attr.h" -#include "ieee802_11_defs.h" /** @@ -44,7 +45,8 @@ struct wps_data * wps_init(const struct wps_config *cfg) os_memcpy(data->uuid_e, cfg->wps->uuid, WPS_UUID_LEN); } if (cfg->pin) { - data->dev_pw_id = DEV_PW_DEFAULT; + data->dev_pw_id = data->wps->oob_dev_pw_id == 0 ? + DEV_PW_DEFAULT : data->wps->oob_dev_pw_id; data->dev_password = os_malloc(cfg->pin_len); if (data->dev_password == NULL) { os_free(data); @@ -88,6 +90,22 @@ struct wps_data * wps_init(const struct wps_config *cfg) } } + if (cfg->new_ap_settings) { + data->new_ap_settings = + os_malloc(sizeof(*data->new_ap_settings)); + if (data->new_ap_settings == NULL) { + os_free(data); + return NULL; + } + os_memcpy(data->new_ap_settings, cfg->new_ap_settings, + sizeof(*data->new_ap_settings)); + } + + if (cfg->peer_addr) + os_memcpy(data->peer_dev.mac_addr, cfg->peer_addr, ETH_ALEN); + + data->use_psk_key = cfg->use_psk_key; + return data; } @@ -114,6 +132,8 @@ void wps_deinit(struct wps_data *data) os_free(data->dev_password); os_free(data->new_psk); wps_device_data_free(&data->peer_dev); + os_free(data->new_ap_settings); + dh5_free(data->dh_ctx); os_free(data); } @@ -269,6 +289,39 @@ struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type) /** + * wps_build_assoc_resp_ie - Build WPS IE for (Re)Association Response + * Returns: WPS IE or %NULL on failure + * + * The caller is responsible for freeing the buffer. + */ +struct wpabuf * wps_build_assoc_resp_ie(void) +{ + struct wpabuf *ie; + u8 *len; + + wpa_printf(MSG_DEBUG, "WPS: Building WPS IE for (Re)Association " + "Response"); + ie = wpabuf_alloc(100); + if (ie == NULL) + return NULL; + + wpabuf_put_u8(ie, WLAN_EID_VENDOR_SPECIFIC); + len = wpabuf_put(ie, 1); + wpabuf_put_be32(ie, WPS_DEV_OUI_WFA); + + if (wps_build_version(ie) || + wps_build_resp_type(ie, WPS_RESP_AP)) { + wpabuf_free(ie); + return NULL; + } + + *len = wpabuf_len(ie) - 2; + + return ie; +} + + +/** * wps_build_probe_req_ie - Build WPS IE for Probe Request * @pbc: Whether searching for PBC mode APs * @dev: Device attributes @@ -298,9 +351,16 @@ struct wpabuf * wps_build_probe_req_ie(int pbc, struct wps_device_data *dev, if (pbc) methods = WPS_CONFIG_PUSHBUTTON; - else + else { methods = WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; +#ifdef CONFIG_WPS_UFD + methods |= WPS_CONFIG_USBA; +#endif /* CONFIG_WPS_UFD */ +#ifdef CONFIG_WPS_NFC + methods |= WPS_CONFIG_NFC_INTERFACE; +#endif /* CONFIG_WPS_NFC */ + } if (wps_build_version(ie) || wps_build_req_type(ie, req_type) || @@ -333,3 +393,105 @@ void wps_free_pending_msgs(struct upnp_pending_message *msgs) os_free(prev); } } + + +int wps_attr_text(struct wpabuf *data, char *buf, char *end) +{ + struct wps_parse_attr attr; + char *pos = buf; + int ret; + + if (wps_parse_msg(data, &attr) < 0) + return -1; + + if (attr.wps_state) { + if (*attr.wps_state == WPS_STATE_NOT_CONFIGURED) + ret = os_snprintf(pos, end - pos, + "wps_state=unconfigured\n"); + else if (*attr.wps_state == WPS_STATE_CONFIGURED) + ret = os_snprintf(pos, end - pos, + "wps_state=configured\n"); + else + ret = 0; + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + if (attr.ap_setup_locked && *attr.ap_setup_locked) { + ret = os_snprintf(pos, end - pos, + "wps_ap_setup_locked=1\n"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + if (attr.selected_registrar && *attr.selected_registrar) { + ret = os_snprintf(pos, end - pos, + "wps_selected_registrar=1\n"); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + if (attr.dev_password_id) { + ret = os_snprintf(pos, end - pos, + "wps_device_password_id=%u\n", + WPA_GET_BE16(attr.dev_password_id)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + if (attr.sel_reg_config_methods) { + ret = os_snprintf(pos, end - pos, + "wps_selected_registrar_config_methods=" + "0x%04x\n", + WPA_GET_BE16(attr.sel_reg_config_methods)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + if (attr.primary_dev_type) { + char devtype[WPS_DEV_TYPE_BUFSIZE]; + ret = os_snprintf(pos, end - pos, + "wps_primary_device_type=%s\n", + wps_dev_type_bin2str(attr.primary_dev_type, + devtype, + sizeof(devtype))); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + if (attr.dev_name) { + char *str = os_malloc(attr.dev_name_len + 1); + size_t i; + if (str == NULL) + return pos - buf; + for (i = 0; i < attr.dev_name_len; i++) { + if (attr.dev_name[i] < 32) + str[i] = '_'; + else + str[i] = attr.dev_name[i]; + } + str[i] = '\0'; + ret = os_snprintf(pos, end - pos, "wps_device_name=%s\n", str); + os_free(str); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + if (attr.config_methods) { + ret = os_snprintf(pos, end - pos, + "wps_config_methods=0x%04x\n", + WPA_GET_BE16(attr.config_methods)); + if (ret < 0 || ret >= end - pos) + return pos - buf; + pos += ret; + } + + return pos - buf; +} diff --git a/contrib/wpa/src/wps/wps.h b/contrib/wpa/src/wps/wps.h index faf32c4..1fd1e52 100644 --- a/contrib/wpa/src/wps/wps.h +++ b/contrib/wpa/src/wps/wps.h @@ -1,6 +1,6 @@ /* * Wi-Fi Protected Setup - * Copyright (c) 2007-2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2007-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -32,6 +32,7 @@ enum wsc_op_code { struct wps_registrar; struct upnp_wps_device_sm; +struct wps_er; /** * struct wps_credential - WPS Credential @@ -60,6 +61,9 @@ struct wps_credential { size_t cred_attr_len; }; +#define WPS_DEV_TYPE_LEN 8 +#define WPS_DEV_TYPE_BUFSIZE 21 + /** * struct wps_device_data - WPS Device Data * @mac_addr: Device MAC address @@ -68,9 +72,7 @@ struct wps_credential { * @model_name: Model Name (0..32 octets encoded in UTF-8) * @model_number: Model Number (0..32 octets encoded in UTF-8) * @serial_number: Serial Number (0..32 octets encoded in UTF-8) - * @categ: Primary Device Category - * @oui: Primary Device OUI - * @sub_categ: Primary Device Sub-Category + * @pri_dev_type: Primary Device Type * @os_version: OS Version * @rf_bands: RF bands (WPS_RF_24GHZ, WPS_RF_50GHZ flags) */ @@ -81,13 +83,22 @@ struct wps_device_data { char *model_name; char *model_number; char *serial_number; - u16 categ; - u32 oui; - u16 sub_categ; + u8 pri_dev_type[WPS_DEV_TYPE_LEN]; u32 os_version; u8 rf_bands; }; +struct oob_conf_data { + enum { + OOB_METHOD_UNKNOWN = 0, + OOB_METHOD_DEV_PWD_E, + OOB_METHOD_DEV_PWD_R, + OOB_METHOD_CRED, + } oob_method; + struct wpabuf *dev_password; + struct wpabuf *pubkey_hash; +}; + /** * struct wps_config - WPS configuration for a single registration protocol run */ @@ -121,6 +132,30 @@ struct wps_config { * assoc_wps_ie: (Re)AssocReq WPS IE (in AP; %NULL if not AP) */ const struct wpabuf *assoc_wps_ie; + + /** + * new_ap_settings - New AP settings (%NULL if not used) + * + * This parameter provides new AP settings when using a wireless + * stations as a Registrar to configure the AP. %NULL means that AP + * will not be reconfigured, i.e., the station will only learn the + * current AP settings by using AP PIN. + */ + const struct wps_credential *new_ap_settings; + + /** + * peer_addr: MAC address of the peer in AP; %NULL if not AP + */ + const u8 *peer_addr; + + /** + * use_psk_key - Use PSK format key in Credential + * + * Force PSK format to be used instead of ASCII passphrase when + * building Credential for an Enrollee. The PSK value is set in + * struct wpa_context::psk. + */ + int use_psk_key; }; struct wps_data * wps_init(const struct wps_config *cfg); @@ -163,6 +198,7 @@ int wps_is_selected_pin_registrar(const struct wpabuf *msg); const u8 * wps_get_uuid_e(const struct wpabuf *msg); struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type); +struct wpabuf * wps_build_assoc_resp_ie(void); struct wpabuf * wps_build_probe_req_ie(int pbc, struct wps_device_data *dev, const u8 *uuid, enum wps_request_type req_type); @@ -189,16 +225,15 @@ struct wps_registrar_config { * set_ie_cb - Callback for WPS IE changes * @ctx: Higher layer context data (cb_ctx) * @beacon_ie: WPS IE for Beacon - * @beacon_ie_len: WPS IE length for Beacon * @probe_resp_ie: WPS IE for Probe Response - * @probe_resp_ie_len: WPS IE length for Probe Response * Returns: 0 on success, -1 on failure * * This callback is called whenever the WPS IE in Beacon or Probe - * Response frames needs to be changed (AP only). + * Response frames needs to be changed (AP only). Callee is responsible + * for freeing the buffers. */ - int (*set_ie_cb)(void *ctx, const u8 *beacon_ie, size_t beacon_ie_len, - const u8 *probe_resp_ie, size_t probe_resp_ie_len); + int (*set_ie_cb)(void *ctx, struct wpabuf *beacon_ie, + struct wpabuf *probe_resp_ie); /** * pin_needed_cb - Callback for requesting a PIN @@ -226,6 +261,40 @@ struct wps_registrar_config { const u8 *uuid_e); /** + * set_sel_reg_cb - Callback for reporting selected registrar changes + * @ctx: Higher layer context data (cb_ctx) + * @sel_reg: Whether the Registrar is selected + * @dev_passwd_id: Device Password ID to indicate with method or + * specific password the Registrar intends to use + * @sel_reg_config_methods: Bit field of active config methods + * + * This callback is called whenever the Selected Registrar state + * changes (e.g., a new PIN becomes available or PBC is invoked). This + * callback is only used by External Registrar implementation; + * set_ie_cb() is used by AP implementation in similar caes, but it + * provides the full WPS IE data instead of just the minimal Registrar + * state information. + */ + void (*set_sel_reg_cb)(void *ctx, int sel_reg, u16 dev_passwd_id, + u16 sel_reg_config_methods); + + /** + * enrollee_seen_cb - Callback for reporting Enrollee based on ProbeReq + * @ctx: Higher layer context data (cb_ctx) + * @addr: MAC address of the Enrollee + * @uuid_e: UUID of the Enrollee + * @pri_dev_type: Primary device type + * @config_methods: Config Methods + * @dev_password_id: Device Password ID + * @request_type: Request Type + * @dev_name: Device Name (if available) + */ + void (*enrollee_seen_cb)(void *ctx, const u8 *addr, const u8 *uuid_e, + const u8 *pri_dev_type, u16 config_methods, + u16 dev_password_id, u8 request_type, + const char *dev_name); + + /** * cb_ctx: Higher layer context data for Registrar callbacks */ void *cb_ctx; @@ -306,7 +375,27 @@ enum wps_event { /** * WPS_EV_PBC_TIMEOUT - PBC walktime expired before protocol run start */ - WPS_EV_PBC_TIMEOUT + WPS_EV_PBC_TIMEOUT, + + /** + * WPS_EV_ER_AP_ADD - ER: AP added + */ + WPS_EV_ER_AP_ADD, + + /** + * WPS_EV_ER_AP_REMOVE - ER: AP removed + */ + WPS_EV_ER_AP_REMOVE, + + /** + * WPS_EV_ER_ENROLLEE_ADD - ER: Enrollee added + */ + WPS_EV_ER_ENROLLEE_ADD, + + /** + * WPS_EV_ER_ENROLLEE_REMOVE - ER: Enrollee removed + */ + WPS_EV_ER_ENROLLEE_REMOVE }; /** @@ -345,6 +434,36 @@ union wps_event_data { int enrollee; int part; } pwd_auth_fail; + + struct wps_event_er_ap { + const u8 *uuid; + const u8 *mac_addr; + const char *friendly_name; + const char *manufacturer; + const char *manufacturer_url; + const char *model_description; + const char *model_name; + const char *model_number; + const char *model_url; + const char *serial_number; + const char *upc; + const u8 *pri_dev_type; + u8 wps_state; + } ap; + + struct wps_event_er_enrollee { + const u8 *uuid; + const u8 *mac_addr; + int m1_received; + u16 config_methods; + u16 dev_passwd_id; + const u8 *pri_dev_type; + const char *dev_name; + const char *manufacturer; + const char *model_name; + const char *model_number; + const char *serial_number; + } enrollee; }; /** @@ -413,6 +532,31 @@ struct wps_context { struct wps_device_data dev; /** + * oob_conf - OOB Config data + */ + struct oob_conf_data oob_conf; + + /** + * oob_dev_pw_id - OOB Device password id + */ + u16 oob_dev_pw_id; + + /** + * dh_ctx - Context data for Diffie-Hellman operation + */ + void *dh_ctx; + + /** + * dh_privkey - Diffie-Hellman private key + */ + struct wpabuf *dh_privkey; + + /** + * dh_pubkey_oob - Diffie-Hellman public key + */ + struct wpabuf *dh_pubkey; + + /** * config_methods - Enabled configuration methods * * Bit field of WPS_CONFIG_* @@ -435,6 +579,14 @@ struct wps_context { * If %NULL, Registrar will generate per-device PSK. In addition, AP * uses this when acting as an Enrollee to notify Registrar of the * current configuration. + * + * When using WPA/WPA2-Person, this key can be either the ASCII + * passphrase (8..63 characters) or the 32-octet PSK (64 hex + * characters). When this is set to the ASCII passphrase, the PSK can + * be provided in the psk buffer and used per-Enrollee to control which + * key type is included in the Credential (e.g., to reduce calculation + * need on low-powered devices by provisioning PSK while still allowing + * other devices to get the passphrase). */ u8 *network_key; @@ -444,6 +596,19 @@ struct wps_context { size_t network_key_len; /** + * psk - The current network PSK + * + * This optional value can be used to provide the current PSK if + * network_key is set to the ASCII passphrase. + */ + u8 psk[32]; + + /** + * psk_set - Whether psk value is set + */ + int psk_set; + + /** * ap_settings - AP Settings override for M7 (only used at AP) * * If %NULL, AP Settings attributes will be generated based on the @@ -509,6 +674,22 @@ struct wps_context { struct upnp_pending_message *upnp_msgs; }; +struct oob_device_data { + char *device_name; + char *device_path; + void * (*init_func)(struct wps_context *, struct oob_device_data *, + int); + struct wpabuf * (*read_func)(void *); + int (*write_func)(void *, struct wpabuf *); + void (*deinit_func)(void *); +}; + +struct oob_nfc_device_data { + int (*init_func)(char *); + void * (*read_func)(size_t *); + int (*write_func)(void *, size_t); + void (*deinit_func)(void); +}; struct wps_registrar * wps_registrar_init(struct wps_context *wps, @@ -522,12 +703,34 @@ int wps_registrar_button_pushed(struct wps_registrar *reg); void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr, const struct wpabuf *wps_data); int wps_registrar_update_ie(struct wps_registrar *reg); -int wps_registrar_set_selected_registrar(struct wps_registrar *reg, - const struct wpabuf *msg); +int wps_registrar_get_info(struct wps_registrar *reg, const u8 *addr, + char *buf, size_t buflen); unsigned int wps_pin_checksum(unsigned int pin); unsigned int wps_pin_valid(unsigned int pin); unsigned int wps_generate_pin(void); void wps_free_pending_msgs(struct upnp_pending_message *msgs); +struct oob_device_data * wps_get_oob_device(char *device_type); +struct oob_nfc_device_data * wps_get_oob_nfc_device(char *device_name); +int wps_get_oob_method(char *method); +int wps_process_oob(struct wps_context *wps, struct oob_device_data *oob_dev, + int registrar); +int wps_attr_text(struct wpabuf *data, char *buf, char *end); + +struct wps_er * wps_er_init(struct wps_context *wps, const char *ifname); +void wps_er_refresh(struct wps_er *er); +void wps_er_deinit(struct wps_er *er, void (*cb)(void *ctx), void *ctx); +void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id, + u16 sel_reg_config_methods); +int wps_er_pbc(struct wps_er *er, const u8 *uuid); +int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *pin, + size_t pin_len); + +int wps_dev_type_str2bin(const char *str, u8 dev_type[WPS_DEV_TYPE_LEN]); +char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf, + size_t buf_len); +void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid); +u16 wps_config_methods_str2bin(const char *str); + #endif /* WPS_H */ diff --git a/contrib/wpa/src/wps/wps_attr_build.c b/contrib/wpa/src/wps/wps_attr_build.c index edeff5c..9da556a 100644 --- a/contrib/wpa/src/wps/wps_attr_build.c +++ b/contrib/wpa/src/wps/wps_attr_build.c @@ -15,9 +15,10 @@ #include "includes.h" #include "common.h" -#include "dh_groups.h" -#include "sha256.h" -#include "aes_wrap.h" +#include "crypto/aes_wrap.h" +#include "crypto/crypto.h" +#include "crypto/dh_group5.h" +#include "crypto/sha256.h" #include "wps_i.h" @@ -26,11 +27,24 @@ int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg) struct wpabuf *pubkey; wpa_printf(MSG_DEBUG, "WPS: * Public Key"); - pubkey = dh_init(dh_groups_get(WPS_DH_GROUP), &wps->dh_privkey); - pubkey = wpabuf_zeropad(pubkey, 192); - if (pubkey == NULL) { + wpabuf_free(wps->dh_privkey); + if (wps->dev_pw_id != DEV_PW_DEFAULT && wps->wps->dh_privkey) { + wpa_printf(MSG_DEBUG, "WPS: Using pre-configured DH keys"); + wps->dh_privkey = wpabuf_dup(wps->wps->dh_privkey); + wps->dh_ctx = wps->wps->dh_ctx; + wps->wps->dh_ctx = NULL; + pubkey = wpabuf_dup(wps->wps->dh_pubkey); + } else { + wpa_printf(MSG_DEBUG, "WPS: Generate new DH keys"); + wps->dh_privkey = NULL; + dh5_free(wps->dh_ctx); + wps->dh_ctx = dh5_init(&wps->dh_privkey, &pubkey); + pubkey = wpabuf_zeropad(pubkey, 192); + } + if (wps->dh_ctx == NULL || wps->dh_privkey == NULL || pubkey == NULL) { wpa_printf(MSG_DEBUG, "WPS: Failed to initialize " "Diffie-Hellman handshake"); + wpabuf_free(pubkey); return -1; } @@ -60,6 +74,16 @@ int wps_build_req_type(struct wpabuf *msg, enum wps_request_type type) } +int wps_build_resp_type(struct wpabuf *msg, enum wps_response_type type) +{ + wpa_printf(MSG_DEBUG, "WPS: * Response Type (%d)", type); + wpabuf_put_be16(msg, ATTR_RESPONSE_TYPE); + wpabuf_put_be16(msg, 1); + wpabuf_put_u8(msg, type); + return 0; +} + + int wps_build_config_methods(struct wpabuf *msg, u16 methods) { wpa_printf(MSG_DEBUG, "WPS: * Config Methods (%x)", methods); @@ -252,3 +276,47 @@ int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg, return 0; } + + +#ifdef CONFIG_WPS_OOB +int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps) +{ + size_t hash_len; + const u8 *addr[1]; + u8 pubkey_hash[WPS_HASH_LEN]; + u8 dev_password_bin[WPS_OOB_DEVICE_PASSWORD_LEN]; + + wpa_printf(MSG_DEBUG, "WPS: * OOB Device Password"); + + addr[0] = wpabuf_head(wps->dh_pubkey); + hash_len = wpabuf_len(wps->dh_pubkey); + sha256_vector(1, addr, &hash_len, pubkey_hash); + + if (os_get_random((u8 *) &wps->oob_dev_pw_id, sizeof(u16)) < 0) { + wpa_printf(MSG_ERROR, "WPS: device password id " + "generation error"); + return -1; + } + wps->oob_dev_pw_id |= 0x0010; + + if (os_get_random(dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN) < 0) { + wpa_printf(MSG_ERROR, "WPS: OOB device password " + "generation error"); + return -1; + } + + wpabuf_put_be16(msg, ATTR_OOB_DEVICE_PASSWORD); + wpabuf_put_be16(msg, WPS_OOB_DEVICE_PASSWORD_ATTR_LEN); + wpabuf_put_data(msg, pubkey_hash, WPS_OOB_PUBKEY_HASH_LEN); + wpabuf_put_be16(msg, wps->oob_dev_pw_id); + wpabuf_put_data(msg, dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN); + + wpa_snprintf_hex_uppercase( + wpabuf_put(wps->oob_conf.dev_password, + wpabuf_size(wps->oob_conf.dev_password)), + wpabuf_size(wps->oob_conf.dev_password), + dev_password_bin, WPS_OOB_DEVICE_PASSWORD_LEN); + + return 0; +} +#endif /* CONFIG_WPS_OOB */ diff --git a/contrib/wpa/src/wps/wps_attr_parse.c b/contrib/wpa/src/wps/wps_attr_parse.c index f50ae39..30b0e79 100644 --- a/contrib/wpa/src/wps/wps_attr_parse.c +++ b/contrib/wpa/src/wps/wps_attr_parse.c @@ -17,6 +17,8 @@ #include "common.h" #include "wps_i.h" +#define WPS_WORKAROUNDS + static int wps_set_attr(struct wps_parse_attr *attr, u16 type, const u8 *pos, u16 len) @@ -111,7 +113,7 @@ static int wps_set_attr(struct wps_parse_attr *attr, u16 type, attr->sel_reg_config_methods = pos; break; case ATTR_PRIMARY_DEV_TYPE: - if (len != sizeof(struct wps_dev_type)) { + if (len != WPS_DEV_TYPE_LEN) { wpa_printf(MSG_DEBUG, "WPS: Invalid Primary Device " "Type length %u", len); return -1; @@ -150,6 +152,14 @@ static int wps_set_attr(struct wps_parse_attr *attr, u16 type, } attr->dev_password_id = pos; break; + case ATTR_OOB_DEVICE_PASSWORD: + if (len != WPS_OOB_DEVICE_PASSWORD_ATTR_LEN) { + wpa_printf(MSG_DEBUG, "WPS: Invalid OOB Device " + "Password length %u", len); + return -1; + } + attr->oob_dev_password = pos; + break; case ATTR_OS_VERSION: if (len != 4) { wpa_printf(MSG_DEBUG, "WPS: Invalid OS Version length " @@ -324,7 +334,7 @@ static int wps_set_attr(struct wps_parse_attr *attr, u16 type, "length %u", len); return -1; } - attr->request_type = pos; + attr->response_type = pos; break; case ATTR_MANUFACTURER: attr->manufacturer = pos; @@ -427,6 +437,25 @@ int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr) return -1; } +#ifdef WPS_WORKAROUNDS + if (type == 0 && len == 0) { + /* + * Mac OS X 10.6 seems to be adding 0x00 padding to the + * end of M1. Skip those to avoid interop issues. + */ + int i; + for (i = 0; i < end - pos; i++) { + if (pos[i]) + break; + } + if (i == end - pos) { + wpa_printf(MSG_DEBUG, "WPS: Workaround - skip " + "unexpected message padding"); + break; + } + } +#endif /* WPS_WORKAROUNDS */ + if (wps_set_attr(attr, type, pos, len) < 0) return -1; diff --git a/contrib/wpa/src/wps/wps_attr_process.c b/contrib/wpa/src/wps/wps_attr_process.c index ae6e906..4751bbc 100644 --- a/contrib/wpa/src/wps/wps_attr_process.c +++ b/contrib/wpa/src/wps/wps_attr_process.c @@ -15,7 +15,7 @@ #include "includes.h" #include "common.h" -#include "sha256.h" +#include "crypto/sha256.h" #include "wps_i.h" @@ -177,6 +177,13 @@ static int wps_process_cred_network_key(struct wps_credential *cred, if (key == NULL) { wpa_printf(MSG_DEBUG, "WPS: Credential did not include " "Network Key"); + if (cred->auth_type == WPS_AUTH_OPEN && + cred->encr_type == WPS_ENCR_NONE) { + wpa_printf(MSG_DEBUG, "WPS: Workaround - Allow " + "missing mandatory Network Key attribute " + "for open network"); + return 0; + } return -1; } diff --git a/contrib/wpa/src/wps/wps_common.c b/contrib/wpa/src/wps/wps_common.c index 4b45f00..6ef14db 100644 --- a/contrib/wpa/src/wps/wps_common.c +++ b/contrib/wpa/src/wps/wps_common.c @@ -1,6 +1,6 @@ /* * Wi-Fi Protected Setup - common functionality - * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -15,10 +15,11 @@ #include "includes.h" #include "common.h" -#include "dh_groups.h" -#include "sha256.h" -#include "aes_wrap.h" -#include "crypto.h" +#include "crypto/aes_wrap.h" +#include "crypto/crypto.h" +#include "crypto/dh_group5.h" +#include "crypto/sha1.h" +#include "crypto/sha256.h" #include "wps_i.h" #include "wps_dev_attr.h" @@ -80,8 +81,9 @@ int wps_derive_keys(struct wps_data *wps) return -1; } - dh_shared = dh_derive_shared(pubkey, wps->dh_privkey, - dh_groups_get(WPS_DH_GROUP)); + dh_shared = dh5_derive_shared(wps->dh_ctx, pubkey, wps->dh_privkey); + dh5_free(wps->dh_ctx); + wps->dh_ctx = NULL; dh_shared = wpabuf_zeropad(dh_shared, 192); if (dh_shared == NULL) { wpa_printf(MSG_DEBUG, "WPS: Failed to derive DH shared key"); @@ -303,3 +305,331 @@ void wps_pbc_timeout_event(struct wps_context *wps) wps->event_cb(wps->cb_ctx, WPS_EV_PBC_TIMEOUT, NULL); } + + +#ifdef CONFIG_WPS_OOB + +static struct wpabuf * wps_get_oob_cred(struct wps_context *wps) +{ + struct wps_data data; + struct wpabuf *plain; + + plain = wpabuf_alloc(500); + if (plain == NULL) { + wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB " + "credential"); + return NULL; + } + + os_memset(&data, 0, sizeof(data)); + data.wps = wps; + data.auth_type = wps->auth_types; + data.encr_type = wps->encr_types; + if (wps_build_version(plain) || wps_build_cred(&data, plain)) { + wpabuf_free(plain); + return NULL; + } + + return plain; +} + + +static struct wpabuf * wps_get_oob_dev_pwd(struct wps_context *wps) +{ + struct wpabuf *data; + + data = wpabuf_alloc(9 + WPS_OOB_DEVICE_PASSWORD_ATTR_LEN); + if (data == NULL) { + wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB " + "device password attribute"); + return NULL; + } + + wpabuf_free(wps->oob_conf.dev_password); + wps->oob_conf.dev_password = + wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1); + if (wps->oob_conf.dev_password == NULL) { + wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB " + "device password"); + wpabuf_free(data); + return NULL; + } + + if (wps_build_version(data) || + wps_build_oob_dev_password(data, wps)) { + wpa_printf(MSG_ERROR, "WPS: Build OOB device password " + "attribute error"); + wpabuf_free(data); + return NULL; + } + + return data; +} + + +static int wps_parse_oob_dev_pwd(struct wps_context *wps, + struct wpabuf *data) +{ + struct oob_conf_data *oob_conf = &wps->oob_conf; + struct wps_parse_attr attr; + const u8 *pos; + + if (wps_parse_msg(data, &attr) < 0 || + attr.oob_dev_password == NULL) { + wpa_printf(MSG_ERROR, "WPS: OOB device password not found"); + return -1; + } + + pos = attr.oob_dev_password; + + oob_conf->pubkey_hash = + wpabuf_alloc_copy(pos, WPS_OOB_PUBKEY_HASH_LEN); + if (oob_conf->pubkey_hash == NULL) { + wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB " + "public key hash"); + return -1; + } + pos += WPS_OOB_PUBKEY_HASH_LEN; + + wps->oob_dev_pw_id = WPA_GET_BE16(pos); + pos += sizeof(wps->oob_dev_pw_id); + + oob_conf->dev_password = + wpabuf_alloc(WPS_OOB_DEVICE_PASSWORD_LEN * 2 + 1); + if (oob_conf->dev_password == NULL) { + wpa_printf(MSG_ERROR, "WPS: Failed to allocate memory for OOB " + "device password"); + return -1; + } + wpa_snprintf_hex_uppercase(wpabuf_put(oob_conf->dev_password, + wpabuf_size(oob_conf->dev_password)), + wpabuf_size(oob_conf->dev_password), pos, + WPS_OOB_DEVICE_PASSWORD_LEN); + + return 0; +} + + +static int wps_parse_oob_cred(struct wps_context *wps, struct wpabuf *data) +{ + struct wpabuf msg; + struct wps_parse_attr attr; + size_t i; + + if (wps_parse_msg(data, &attr) < 0 || attr.num_cred <= 0) { + wpa_printf(MSG_ERROR, "WPS: OOB credential not found"); + return -1; + } + + for (i = 0; i < attr.num_cred; i++) { + struct wps_credential local_cred; + struct wps_parse_attr cattr; + + os_memset(&local_cred, 0, sizeof(local_cred)); + wpabuf_set(&msg, attr.cred[i], attr.cred_len[i]); + if (wps_parse_msg(&msg, &cattr) < 0 || + wps_process_cred(&cattr, &local_cred)) { + wpa_printf(MSG_ERROR, "WPS: Failed to parse OOB " + "credential"); + return -1; + } + wps->cred_cb(wps->cb_ctx, &local_cred); + } + + return 0; +} + + +int wps_process_oob(struct wps_context *wps, struct oob_device_data *oob_dev, + int registrar) +{ + struct wpabuf *data; + int ret, write_f, oob_method = wps->oob_conf.oob_method; + void *oob_priv; + + write_f = oob_method == OOB_METHOD_DEV_PWD_E ? !registrar : registrar; + + oob_priv = oob_dev->init_func(wps, oob_dev, registrar); + if (oob_priv == NULL) { + wpa_printf(MSG_ERROR, "WPS: Failed to initialize OOB device"); + return -1; + } + + if (write_f) { + if (oob_method == OOB_METHOD_CRED) + data = wps_get_oob_cred(wps); + else + data = wps_get_oob_dev_pwd(wps); + + ret = 0; + if (data == NULL || oob_dev->write_func(oob_priv, data) < 0) + ret = -1; + } else { + data = oob_dev->read_func(oob_priv); + if (data == NULL) + ret = -1; + else { + if (oob_method == OOB_METHOD_CRED) + ret = wps_parse_oob_cred(wps, data); + else + ret = wps_parse_oob_dev_pwd(wps, data); + } + } + wpabuf_free(data); + oob_dev->deinit_func(oob_priv); + + if (ret < 0) { + wpa_printf(MSG_ERROR, "WPS: Failed to process OOB data"); + return -1; + } + + return 0; +} + + +struct oob_device_data * wps_get_oob_device(char *device_type) +{ +#ifdef CONFIG_WPS_UFD + if (os_strstr(device_type, "ufd") != NULL) + return &oob_ufd_device_data; +#endif /* CONFIG_WPS_UFD */ +#ifdef CONFIG_WPS_NFC + if (os_strstr(device_type, "nfc") != NULL) + return &oob_nfc_device_data; +#endif /* CONFIG_WPS_NFC */ + + return NULL; +} + + +#ifdef CONFIG_WPS_NFC +struct oob_nfc_device_data * wps_get_oob_nfc_device(char *device_name) +{ + if (device_name == NULL) + return NULL; +#ifdef CONFIG_WPS_NFC_PN531 + if (os_strstr(device_name, "pn531") != NULL) + return &oob_nfc_pn531_device_data; +#endif /* CONFIG_WPS_NFC_PN531 */ + + return NULL; +} +#endif /* CONFIG_WPS_NFC */ + + +int wps_get_oob_method(char *method) +{ + if (os_strstr(method, "pin-e") != NULL) + return OOB_METHOD_DEV_PWD_E; + if (os_strstr(method, "pin-r") != NULL) + return OOB_METHOD_DEV_PWD_R; + if (os_strstr(method, "cred") != NULL) + return OOB_METHOD_CRED; + return OOB_METHOD_UNKNOWN; +} + +#endif /* CONFIG_WPS_OOB */ + + +int wps_dev_type_str2bin(const char *str, u8 dev_type[WPS_DEV_TYPE_LEN]) +{ + const char *pos; + + /* <categ>-<OUI>-<subcateg> */ + WPA_PUT_BE16(dev_type, atoi(str)); + pos = os_strchr(str, '-'); + if (pos == NULL) + return -1; + pos++; + if (hexstr2bin(pos, &dev_type[2], 4)) + return -1; + pos = os_strchr(pos, '-'); + if (pos == NULL) + return -1; + pos++; + WPA_PUT_BE16(&dev_type[6], atoi(pos)); + + + return 0; +} + + +char * wps_dev_type_bin2str(const u8 dev_type[WPS_DEV_TYPE_LEN], char *buf, + size_t buf_len) +{ + int ret; + + ret = os_snprintf(buf, buf_len, "%u-%08X-%u", + WPA_GET_BE16(dev_type), WPA_GET_BE32(&dev_type[2]), + WPA_GET_BE16(&dev_type[6])); + if (ret < 0 || (unsigned int) ret >= buf_len) + return NULL; + + return buf; +} + + +void uuid_gen_mac_addr(const u8 *mac_addr, u8 *uuid) +{ + const u8 *addr[2]; + size_t len[2]; + u8 hash[SHA1_MAC_LEN]; + u8 nsid[16] = { + 0x52, 0x64, 0x80, 0xf8, + 0xc9, 0x9b, + 0x4b, 0xe5, + 0xa6, 0x55, + 0x58, 0xed, 0x5f, 0x5d, 0x60, 0x84 + }; + + addr[0] = nsid; + len[0] = sizeof(nsid); + addr[1] = mac_addr; + len[1] = 6; + sha1_vector(2, addr, len, hash); + os_memcpy(uuid, hash, 16); + + /* Version: 5 = named-based version using SHA-1 */ + uuid[6] = (5 << 4) | (uuid[6] & 0x0f); + + /* Variant specified in RFC 4122 */ + uuid[8] = 0x80 | (uuid[8] & 0x3f); +} + + +u16 wps_config_methods_str2bin(const char *str) +{ + u16 methods = 0; + + if (str == NULL) { + /* Default to enabling methods based on build configuration */ + methods |= WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; +#ifdef CONFIG_WPS_UFD + methods |= WPS_CONFIG_USBA; +#endif /* CONFIG_WPS_UFD */ +#ifdef CONFIG_WPS_NFC + methods |= WPS_CONFIG_NFC_INTERFACE; +#endif /* CONFIG_WPS_NFC */ + } else { + if (os_strstr(str, "usba")) + methods |= WPS_CONFIG_USBA; + if (os_strstr(str, "ethernet")) + methods |= WPS_CONFIG_ETHERNET; + if (os_strstr(str, "label")) + methods |= WPS_CONFIG_LABEL; + if (os_strstr(str, "display")) + methods |= WPS_CONFIG_DISPLAY; + if (os_strstr(str, "ext_nfc_token")) + methods |= WPS_CONFIG_EXT_NFC_TOKEN; + if (os_strstr(str, "int_nfc_token")) + methods |= WPS_CONFIG_INT_NFC_TOKEN; + if (os_strstr(str, "nfc_interface")) + methods |= WPS_CONFIG_NFC_INTERFACE; + if (os_strstr(str, "push_button")) + methods |= WPS_CONFIG_PUSHBUTTON; + if (os_strstr(str, "keypad")) + methods |= WPS_CONFIG_KEYPAD; + } + + return methods; +} diff --git a/contrib/wpa/src/wps/wps_defs.h b/contrib/wpa/src/wps/wps_defs.h index bf6ccc5..750ca41 100644 --- a/contrib/wpa/src/wps/wps_defs.h +++ b/contrib/wpa/src/wps/wps_defs.h @@ -33,6 +33,9 @@ #define WPS_MGMTAUTHKEY_LEN 32 #define WPS_MGMTENCKEY_LEN 16 #define WPS_MGMT_KEY_ID_LEN 16 +#define WPS_OOB_DEVICE_PASSWORD_ATTR_LEN 54 +#define WPS_OOB_DEVICE_PASSWORD_LEN 32 +#define WPS_OOB_PUBKEY_HASH_LEN 20 /* Attribute Types */ enum wps_attribute { @@ -112,7 +115,7 @@ enum wps_attribute { ATTR_PERMITTED_CFG_METHODS = 0x1052, ATTR_SELECTED_REGISTRAR_CONFIG_METHODS = 0x1053, ATTR_PRIMARY_DEV_TYPE = 0x1054, - ATTR_SECONDARY_DEV_TYP_ELIST = 0x1055, + ATTR_SECONDARY_DEV_TYPE_LIST = 0x1055, ATTR_PORTABLE_DEV = 0x1056, ATTR_AP_SETUP_LOCKED = 0x1057, ATTR_APPLICATION_EXT = 0x1058, @@ -229,13 +232,6 @@ enum wps_assoc_state { }; -/* Primary Device Type */ -struct wps_dev_type { - u8 categ_id[2]; - u8 oui[4]; - u8 sub_categ_id[2]; -}; - #define WPS_DEV_OUI_WFA 0x0050f204 enum wps_dev_categ { diff --git a/contrib/wpa/src/wps/wps_dev_attr.c b/contrib/wpa/src/wps/wps_dev_attr.c index 35f58d1..090bfa2 100644 --- a/contrib/wpa/src/wps/wps_dev_attr.c +++ b/contrib/wpa/src/wps/wps_dev_attr.c @@ -113,14 +113,10 @@ static int wps_build_serial_number(struct wps_device_data *dev, int wps_build_primary_dev_type(struct wps_device_data *dev, struct wpabuf *msg) { - struct wps_dev_type *d; wpa_printf(MSG_DEBUG, "WPS: * Primary Device Type"); wpabuf_put_be16(msg, ATTR_PRIMARY_DEV_TYPE); - wpabuf_put_be16(msg, sizeof(*d)); - d = wpabuf_put(msg, sizeof(*d)); - WPA_PUT_BE16(d->categ_id, dev->categ); - WPA_PUT_BE32(d->oui, dev->oui); - WPA_PUT_BE16(d->sub_categ_id, dev->sub_categ); + wpabuf_put_be16(msg, WPS_DEV_TYPE_LEN); + wpabuf_put_data(msg, dev->pri_dev_type, WPS_DEV_TYPE_LEN); return 0; } @@ -288,21 +284,19 @@ static int wps_process_dev_name(struct wps_device_data *dev, const u8 *str, static int wps_process_primary_dev_type(struct wps_device_data *dev, const u8 *dev_type) { - struct wps_dev_type *d; +#ifndef CONFIG_NO_STDOUT_DEBUG + char devtype[WPS_DEV_TYPE_BUFSIZE]; +#endif /* CONFIG_NO_STDOUT_DEBUG */ if (dev_type == NULL) { wpa_printf(MSG_DEBUG, "WPS: No Primary Device Type received"); return -1; } - d = (struct wps_dev_type *) dev_type; - dev->categ = WPA_GET_BE16(d->categ_id); - dev->oui = WPA_GET_BE32(d->oui); - dev->sub_categ = WPA_GET_BE16(d->sub_categ_id); - - wpa_printf(MSG_DEBUG, "WPS: Primary Device Type: category %d " - "OUI %08x sub-category %d", - dev->categ, dev->oui, dev->sub_categ); + os_memcpy(dev->pri_dev_type, dev_type, WPS_DEV_TYPE_LEN); + wpa_printf(MSG_DEBUG, "WPS: Primary Device Type: %s", + wps_dev_type_bin2str(dev->pri_dev_type, devtype, + sizeof(devtype))); return 0; } @@ -367,9 +361,7 @@ void wps_device_data_dup(struct wps_device_data *dst, dst->model_number = os_strdup(src->model_number); if (src->serial_number) dst->serial_number = os_strdup(src->serial_number); - dst->categ = src->categ; - dst->oui = src->oui; - dst->sub_categ = src->sub_categ; + os_memcpy(dst->pri_dev_type, src->pri_dev_type, WPS_DEV_TYPE_LEN); dst->os_version = src->os_version; dst->rf_bands = src->rf_bands; } diff --git a/contrib/wpa/src/wps/wps_enrollee.c b/contrib/wpa/src/wps/wps_enrollee.c index 5cb3e1e..dff24d4 100644 --- a/contrib/wpa/src/wps/wps_enrollee.c +++ b/contrib/wpa/src/wps/wps_enrollee.c @@ -15,7 +15,8 @@ #include "includes.h" #include "common.h" -#include "sha256.h" +#include "crypto/crypto.h" +#include "crypto/sha256.h" #include "wps_i.h" #include "wps_dev_attr.h" @@ -118,7 +119,6 @@ static int wps_build_e_snonce2(struct wps_data *wps, struct wpabuf *msg) static struct wpabuf * wps_build_m1(struct wps_data *wps) { struct wpabuf *msg; - u16 methods; if (os_get_random(wps->nonce_e, WPS_NONCE_LEN) < 0) return NULL; @@ -130,10 +130,6 @@ static struct wpabuf * wps_build_m1(struct wps_data *wps) if (msg == NULL) return NULL; - methods = WPS_CONFIG_LABEL | WPS_CONFIG_DISPLAY | WPS_CONFIG_KEYPAD; - if (wps->pbc) - methods |= WPS_CONFIG_PUSHBUTTON; - if (wps_build_version(msg) || wps_build_msg_type(msg, WPS_M1) || wps_build_uuid_e(msg, wps->uuid_e) || @@ -143,7 +139,7 @@ static struct wpabuf * wps_build_m1(struct wps_data *wps) wps_build_auth_type_flags(wps, msg) || wps_build_encr_type_flags(wps, msg) || wps_build_conn_type_flags(wps, msg) || - wps_build_config_methods(msg, methods) || + wps_build_config_methods(msg, wps->wps->config_methods) || wps_build_wps_state(wps, msg) || wps_build_device_attrs(&wps->wps->dev, msg) || wps_build_rf_bands(&wps->wps->dev, msg) || @@ -321,6 +317,16 @@ static struct wpabuf * wps_build_m7(struct wps_data *wps) } wpabuf_free(plain); + if (wps->wps->ap && wps->wps->registrar) { + /* + * If the Registrar is only learning our current configuration, + * it may not continue protocol run to successful completion. + * Store information here to make sure it remains available. + */ + wps_device_store(wps->wps->registrar, &wps->peer_dev, + wps->uuid_r); + } + wps->state = RECV_M8; return msg; } @@ -513,6 +519,23 @@ static int wps_process_pubkey(struct wps_data *wps, const u8 *pk, return -1; } +#ifdef CONFIG_WPS_OOB + if (wps->dev_pw_id != DEV_PW_DEFAULT && + wps->wps->oob_conf.pubkey_hash) { + const u8 *addr[1]; + u8 hash[WPS_HASH_LEN]; + + addr[0] = pk; + sha256_vector(1, addr, &pk_len, hash); + if (os_memcmp(hash, + wpabuf_head(wps->wps->oob_conf.pubkey_hash), + WPS_OOB_PUBKEY_HASH_LEN) != 0) { + wpa_printf(MSG_ERROR, "WPS: Public Key hash error"); + return -1; + } + } +#endif /* CONFIG_WPS_OOB */ + wpabuf_free(wps->dh_pubkey_r); wps->dh_pubkey_r = wpabuf_alloc_copy(pk, pk_len); if (wps->dh_pubkey_r == NULL) @@ -751,14 +774,13 @@ static enum wps_process_res wps_process_m2(struct wps_data *wps, if (wps_process_registrar_nonce(wps, attr->registrar_nonce) || wps_process_enrollee_nonce(wps, attr->enrollee_nonce) || - wps_process_uuid_r(wps, attr->uuid_r) || - wps_process_pubkey(wps, attr->public_key, attr->public_key_len) || - wps_process_authenticator(wps, attr->authenticator, msg)) { + wps_process_uuid_r(wps, attr->uuid_r)) { wps->state = SEND_WSC_NACK; return WPS_CONTINUE; } - if (wps->wps->ap && wps->wps->ap_setup_locked) { + if (wps->wps->ap && + (wps->wps->ap_setup_locked || wps->dev_password == NULL)) { wpa_printf(MSG_DEBUG, "WPS: AP Setup is locked - refuse " "registration of a new Registrar"); wps->config_error = WPS_CFG_SETUP_LOCKED; @@ -766,6 +788,13 @@ static enum wps_process_res wps_process_m2(struct wps_data *wps, return WPS_CONTINUE; } + if (wps_process_pubkey(wps, attr->public_key, attr->public_key_len) || + wps_process_authenticator(wps, attr->authenticator, msg) || + wps_process_device_attrs(&wps->peer_dev, attr)) { + wps->state = SEND_WSC_NACK; + return WPS_CONTINUE; + } + wps->state = SEND_M3; return WPS_CONTINUE; } diff --git a/contrib/wpa/src/wps/wps_er.c b/contrib/wpa/src/wps/wps_er.c new file mode 100644 index 0000000..e0cdd1d --- /dev/null +++ b/contrib/wpa/src/wps/wps_er.c @@ -0,0 +1,1663 @@ +/* + * Wi-Fi Protected Setup - External Registrar + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "base64.h" +#include "uuid.h" +#include "eloop.h" +#include "httpread.h" +#include "http_client.h" +#include "http_server.h" +#include "upnp_xml.h" +#include "wps_i.h" +#include "wps_upnp.h" +#include "wps_upnp_i.h" +#include "wps_er.h" + + +static void wps_er_deinit_finish(void *eloop_data, void *user_ctx); +static void wps_er_ap_timeout(void *eloop_data, void *user_ctx); +static void wps_er_sta_timeout(void *eloop_data, void *user_ctx); +static void wps_er_ap_process(struct wps_er_ap *ap, struct wpabuf *msg); +static int wps_er_send_get_device_info(struct wps_er_ap *ap, + void (*m1_handler)(struct wps_er_ap *ap, + struct wpabuf *m1)); + + +static void wps_er_sta_event(struct wps_context *wps, struct wps_er_sta *sta, + enum wps_event event) +{ + union wps_event_data data; + struct wps_event_er_enrollee *ev = &data.enrollee; + + if (wps->event_cb == NULL) + return; + + os_memset(&data, 0, sizeof(data)); + ev->uuid = sta->uuid; + ev->mac_addr = sta->addr; + ev->m1_received = sta->m1_received; + ev->config_methods = sta->config_methods; + ev->dev_passwd_id = sta->dev_passwd_id; + ev->pri_dev_type = sta->pri_dev_type; + ev->dev_name = sta->dev_name; + ev->manufacturer = sta->manufacturer; + ev->model_name = sta->model_name; + ev->model_number = sta->model_number; + ev->serial_number = sta->serial_number; + wps->event_cb(wps->cb_ctx, event, &data); +} + + +static struct wps_er_sta * wps_er_sta_get(struct wps_er_ap *ap, const u8 *addr) +{ + struct wps_er_sta *sta; + dl_list_for_each(sta, &ap->sta, struct wps_er_sta, list) { + if (os_memcmp(sta->addr, addr, ETH_ALEN) == 0) + return sta; + } + return NULL; +} + + +static void wps_er_sta_free(struct wps_er_sta *sta) +{ + wps_er_sta_event(sta->ap->er->wps, sta, WPS_EV_ER_ENROLLEE_REMOVE); + if (sta->wps) + wps_deinit(sta->wps); + os_free(sta->manufacturer); + os_free(sta->model_name); + os_free(sta->model_number); + os_free(sta->serial_number); + os_free(sta->dev_name); + http_client_free(sta->http); + eloop_cancel_timeout(wps_er_sta_timeout, sta, NULL); + os_free(sta->cred); + os_free(sta); +} + + +static void wps_er_sta_remove_all(struct wps_er_ap *ap) +{ + struct wps_er_sta *prev, *sta; + dl_list_for_each_safe(sta, prev, &ap->sta, struct wps_er_sta, list) + wps_er_sta_free(sta); +} + + +static struct wps_er_ap * wps_er_ap_get(struct wps_er *er, + struct in_addr *addr, const u8 *uuid) +{ + struct wps_er_ap *ap; + dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { + if ((addr == NULL || ap->addr.s_addr == addr->s_addr) && + (uuid == NULL || + os_memcmp(uuid, ap->uuid, WPS_UUID_LEN) == 0)) + return ap; + } + return NULL; +} + + +static struct wps_er_ap * wps_er_ap_get_id(struct wps_er *er, unsigned int id) +{ + struct wps_er_ap *ap; + dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { + if (ap->id == id) + return ap; + } + return NULL; +} + + +static void wps_er_ap_event(struct wps_context *wps, struct wps_er_ap *ap, + enum wps_event event) +{ + union wps_event_data data; + struct wps_event_er_ap *evap = &data.ap; + + if (wps->event_cb == NULL) + return; + + os_memset(&data, 0, sizeof(data)); + evap->uuid = ap->uuid; + evap->friendly_name = ap->friendly_name; + evap->manufacturer = ap->manufacturer; + evap->manufacturer_url = ap->manufacturer_url; + evap->model_description = ap->model_description; + evap->model_name = ap->model_name; + evap->model_number = ap->model_number; + evap->model_url = ap->model_url; + evap->serial_number = ap->serial_number; + evap->upc = ap->upc; + evap->pri_dev_type = ap->pri_dev_type; + evap->wps_state = ap->wps_state; + evap->mac_addr = ap->mac_addr; + wps->event_cb(wps->cb_ctx, event, &data); +} + + +static void wps_er_ap_free(struct wps_er_ap *ap) +{ + http_client_free(ap->http); + ap->http = NULL; + + os_free(ap->location); + os_free(ap->friendly_name); + os_free(ap->manufacturer); + os_free(ap->manufacturer_url); + os_free(ap->model_description); + os_free(ap->model_name); + os_free(ap->model_number); + os_free(ap->model_url); + os_free(ap->serial_number); + os_free(ap->udn); + os_free(ap->upc); + + os_free(ap->scpd_url); + os_free(ap->control_url); + os_free(ap->event_sub_url); + + os_free(ap->ap_settings); + + os_free(ap); +} + + +static void wps_er_ap_unsubscribed(struct wps_er *er, struct wps_er_ap *ap) +{ + wpa_printf(MSG_DEBUG, "WPS ER: Unsubscribed from AP %s (%s)", + inet_ntoa(ap->addr), ap->location); + dl_list_del(&ap->list); + wps_er_ap_free(ap); + + if (er->deinitializing && dl_list_empty(&er->ap_unsubscribing)) { + eloop_cancel_timeout(wps_er_deinit_finish, er, NULL); + wps_er_deinit_finish(er, NULL); + } +} + + +static void wps_er_http_unsubscribe_cb(void *ctx, struct http_client *c, + enum http_client_event event) +{ + struct wps_er_ap *ap = ctx; + + switch (event) { + case HTTP_CLIENT_OK: + wpa_printf(MSG_DEBUG, "WPS ER: Unsubscribed from events"); + ap->subscribed = 0; + break; + case HTTP_CLIENT_FAILED: + case HTTP_CLIENT_INVALID_REPLY: + case HTTP_CLIENT_TIMEOUT: + wpa_printf(MSG_DEBUG, "WPS ER: Failed to unsubscribe from " + "events"); + break; + } + http_client_free(ap->http); + ap->http = NULL; + + /* + * Need to get rid of the AP entry regardless of whether we managed to + * unsubscribe cleanly or not. + */ + wps_er_ap_unsubscribed(ap->er, ap); +} + + +static void wps_er_ap_unsubscribe(struct wps_er *er, struct wps_er_ap *ap) +{ + struct wpabuf *req; + struct sockaddr_in dst; + char *url, *path; + char sid[100]; + + if (ap->event_sub_url == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: No eventSubURL - cannot " + "subscribe"); + goto fail; + } + if (ap->http) { + wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request - cannot " + "send subscribe request"); + goto fail; + } + + url = http_client_url_parse(ap->event_sub_url, &dst, &path); + if (url == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse eventSubURL"); + goto fail; + } + + req = wpabuf_alloc(os_strlen(ap->event_sub_url) + 1000); + if (req == NULL) { + os_free(url); + goto fail; + } + uuid_bin2str(ap->sid, sid, sizeof(sid)); + wpabuf_printf(req, + "UNSUBSCRIBE %s HTTP/1.1\r\n" + "HOST: %s:%d\r\n" + "SID: uuid:%s\r\n" + "\r\n", + path, inet_ntoa(dst.sin_addr), ntohs(dst.sin_port), sid); + os_free(url); + wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Unsubscription request", + wpabuf_head(req), wpabuf_len(req)); + + ap->http = http_client_addr(&dst, req, 1000, + wps_er_http_unsubscribe_cb, ap); + if (ap->http == NULL) { + wpabuf_free(req); + goto fail; + } + return; + +fail: + /* + * Need to get rid of the AP entry even when we fail to unsubscribe + * cleanly. + */ + wps_er_ap_unsubscribed(ap->er, ap); +} + +static void wps_er_ap_remove_entry(struct wps_er *er, struct wps_er_ap *ap) +{ + wpa_printf(MSG_DEBUG, "WPS ER: Removing AP entry for %s (%s)", + inet_ntoa(ap->addr), ap->location); + eloop_cancel_timeout(wps_er_ap_timeout, er, ap); + wps_er_sta_remove_all(ap); + wps_er_ap_event(er->wps, ap, WPS_EV_ER_AP_REMOVE); + http_client_free(ap->http); + ap->http = NULL; + if (ap->wps) { + wps_deinit(ap->wps); + ap->wps = NULL; + } + + dl_list_del(&ap->list); + if (ap->subscribed) { + dl_list_add(&er->ap_unsubscribing, &ap->list); + wps_er_ap_unsubscribe(er, ap); + } else + wps_er_ap_free(ap); +} + + +static void wps_er_ap_timeout(void *eloop_data, void *user_ctx) +{ + struct wps_er *er = eloop_data; + struct wps_er_ap *ap = user_ctx; + wpa_printf(MSG_DEBUG, "WPS ER: AP advertisement timed out"); + wps_er_ap_remove_entry(er, ap); +} + + +static int wps_er_get_sid(struct wps_er_ap *ap, char *sid) +{ + char *pos; + char txt[100]; + + if (!sid) { + wpa_printf(MSG_DEBUG, "WPS ER: No SID received from %s (%s)", + inet_ntoa(ap->addr), ap->location); + return -1; + } + + pos = os_strstr(sid, "uuid:"); + if (!pos) { + wpa_printf(MSG_DEBUG, "WPS ER: Invalid SID received from " + "%s (%s): '%s'", inet_ntoa(ap->addr), ap->location, + sid); + return -1; + } + + pos += 5; + if (uuid_str2bin(pos, ap->sid) < 0) { + wpa_printf(MSG_DEBUG, "WPS ER: Invalid SID received from " + "%s (%s): '%s'", inet_ntoa(ap->addr), ap->location, + sid); + return -1; + } + + uuid_bin2str(ap->sid, txt, sizeof(txt)); + wpa_printf(MSG_DEBUG, "WPS ER: SID for subscription with %s (%s): %s", + inet_ntoa(ap->addr), ap->location, txt); + + return 0; +} + + +static void wps_er_http_subscribe_cb(void *ctx, struct http_client *c, + enum http_client_event event) +{ + struct wps_er_ap *ap = ctx; + + switch (event) { + case HTTP_CLIENT_OK: + wpa_printf(MSG_DEBUG, "WPS ER: Subscribed to events"); + ap->subscribed = 1; + wps_er_get_sid(ap, http_client_get_hdr_line(c, "SID")); + wps_er_ap_event(ap->er->wps, ap, WPS_EV_ER_AP_ADD); + break; + case HTTP_CLIENT_FAILED: + case HTTP_CLIENT_INVALID_REPLY: + case HTTP_CLIENT_TIMEOUT: + wpa_printf(MSG_DEBUG, "WPS ER: Failed to subscribe to events"); + break; + } + http_client_free(ap->http); + ap->http = NULL; +} + + +static void wps_er_subscribe(struct wps_er_ap *ap) +{ + struct wpabuf *req; + struct sockaddr_in dst; + char *url, *path; + + if (ap->event_sub_url == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: No eventSubURL - cannot " + "subscribe"); + return; + } + if (ap->http) { + wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request - cannot " + "send subscribe request"); + return; + } + + url = http_client_url_parse(ap->event_sub_url, &dst, &path); + if (url == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse eventSubURL"); + return; + } + + req = wpabuf_alloc(os_strlen(ap->event_sub_url) + 1000); + if (req == NULL) { + os_free(url); + return; + } + wpabuf_printf(req, + "SUBSCRIBE %s HTTP/1.1\r\n" + "HOST: %s:%d\r\n" + "CALLBACK: <http://%s:%d/event/%u/%u>\r\n" + "NT: upnp:event\r\n" + "TIMEOUT: Second-%d\r\n" + "\r\n", + path, inet_ntoa(dst.sin_addr), ntohs(dst.sin_port), + ap->er->ip_addr_text, ap->er->http_port, + ap->er->event_id, ap->id, 1800); + os_free(url); + wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Subscription request", + wpabuf_head(req), wpabuf_len(req)); + + ap->http = http_client_addr(&dst, req, 1000, wps_er_http_subscribe_cb, + ap); + if (ap->http == NULL) + wpabuf_free(req); +} + + +static void wps_er_ap_get_m1(struct wps_er_ap *ap, struct wpabuf *m1) +{ + struct wps_parse_attr attr; + + if (wps_parse_msg(m1, &attr) < 0) { + wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse M1"); + return; + } + if (attr.primary_dev_type) + os_memcpy(ap->pri_dev_type, attr.primary_dev_type, 8); + if (attr.wps_state) + ap->wps_state = *attr.wps_state; + if (attr.mac_addr) + os_memcpy(ap->mac_addr, attr.mac_addr, ETH_ALEN); + + wps_er_subscribe(ap); +} + + +static void wps_er_get_device_info(struct wps_er_ap *ap) +{ + wps_er_send_get_device_info(ap, wps_er_ap_get_m1); +} + + +static void wps_er_parse_device_description(struct wps_er_ap *ap, + struct wpabuf *reply) +{ + /* Note: reply includes null termination after the buffer data */ + const char *data = wpabuf_head(reply); + char *pos; + + wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Device info", + wpabuf_head(reply), wpabuf_len(reply)); + + ap->friendly_name = xml_get_first_item(data, "friendlyName"); + wpa_printf(MSG_DEBUG, "WPS ER: friendlyName='%s'", ap->friendly_name); + + ap->manufacturer = xml_get_first_item(data, "manufacturer"); + wpa_printf(MSG_DEBUG, "WPS ER: manufacturer='%s'", ap->manufacturer); + + ap->manufacturer_url = xml_get_first_item(data, "manufacturerURL"); + wpa_printf(MSG_DEBUG, "WPS ER: manufacturerURL='%s'", + ap->manufacturer_url); + + ap->model_description = xml_get_first_item(data, "modelDescription"); + wpa_printf(MSG_DEBUG, "WPS ER: modelDescription='%s'", + ap->model_description); + + ap->model_name = xml_get_first_item(data, "modelName"); + wpa_printf(MSG_DEBUG, "WPS ER: modelName='%s'", ap->model_name); + + ap->model_number = xml_get_first_item(data, "modelNumber"); + wpa_printf(MSG_DEBUG, "WPS ER: modelNumber='%s'", ap->model_number); + + ap->model_url = xml_get_first_item(data, "modelURL"); + wpa_printf(MSG_DEBUG, "WPS ER: modelURL='%s'", ap->model_url); + + ap->serial_number = xml_get_first_item(data, "serialNumber"); + wpa_printf(MSG_DEBUG, "WPS ER: serialNumber='%s'", ap->serial_number); + + ap->udn = xml_get_first_item(data, "UDN"); + wpa_printf(MSG_DEBUG, "WPS ER: UDN='%s'", ap->udn); + pos = os_strstr(ap->udn, "uuid:"); + if (pos) { + pos += 5; + if (uuid_str2bin(pos, ap->uuid) < 0) + wpa_printf(MSG_DEBUG, "WPS ER: Invalid UUID in UDN"); + } + + ap->upc = xml_get_first_item(data, "UPC"); + wpa_printf(MSG_DEBUG, "WPS ER: UPC='%s'", ap->upc); + + ap->scpd_url = http_link_update( + xml_get_first_item(data, "SCPDURL"), ap->location); + wpa_printf(MSG_DEBUG, "WPS ER: SCPDURL='%s'", ap->scpd_url); + + ap->control_url = http_link_update( + xml_get_first_item(data, "controlURL"), ap->location); + wpa_printf(MSG_DEBUG, "WPS ER: controlURL='%s'", ap->control_url); + + ap->event_sub_url = http_link_update( + xml_get_first_item(data, "eventSubURL"), ap->location); + wpa_printf(MSG_DEBUG, "WPS ER: eventSubURL='%s'", ap->event_sub_url); +} + + +static void wps_er_http_dev_desc_cb(void *ctx, struct http_client *c, + enum http_client_event event) +{ + struct wps_er_ap *ap = ctx; + struct wpabuf *reply; + int ok = 0; + + switch (event) { + case HTTP_CLIENT_OK: + reply = http_client_get_body(c); + if (reply == NULL) + break; + wps_er_parse_device_description(ap, reply); + ok = 1; + break; + case HTTP_CLIENT_FAILED: + case HTTP_CLIENT_INVALID_REPLY: + case HTTP_CLIENT_TIMEOUT: + wpa_printf(MSG_DEBUG, "WPS ER: Failed to fetch device info"); + break; + } + http_client_free(ap->http); + ap->http = NULL; + if (ok) + wps_er_get_device_info(ap); +} + + +void wps_er_ap_add(struct wps_er *er, const u8 *uuid, struct in_addr *addr, + const char *location, int max_age) +{ + struct wps_er_ap *ap; + + ap = wps_er_ap_get(er, addr, uuid); + if (ap) { + /* Update advertisement timeout */ + eloop_cancel_timeout(wps_er_ap_timeout, er, ap); + eloop_register_timeout(max_age, 0, wps_er_ap_timeout, er, ap); + return; + } + + ap = os_zalloc(sizeof(*ap)); + if (ap == NULL) + return; + dl_list_init(&ap->sta); + ap->er = er; + ap->id = ++er->next_ap_id; + ap->location = os_strdup(location); + if (ap->location == NULL) { + os_free(ap); + return; + } + dl_list_add(&er->ap, &ap->list); + + ap->addr.s_addr = addr->s_addr; + os_memcpy(ap->uuid, uuid, WPS_UUID_LEN); + eloop_register_timeout(max_age, 0, wps_er_ap_timeout, er, ap); + + wpa_printf(MSG_DEBUG, "WPS ER: Added AP entry for %s (%s)", + inet_ntoa(ap->addr), ap->location); + + /* Fetch device description */ + ap->http = http_client_url(ap->location, NULL, 10000, + wps_er_http_dev_desc_cb, ap); +} + + +void wps_er_ap_remove(struct wps_er *er, struct in_addr *addr) +{ + struct wps_er_ap *ap; + dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { + if (ap->addr.s_addr == addr->s_addr) { + wps_er_ap_remove_entry(er, ap); + return; + } + } +} + + +static void wps_er_ap_remove_all(struct wps_er *er) +{ + struct wps_er_ap *prev, *ap; + dl_list_for_each_safe(ap, prev, &er->ap, struct wps_er_ap, list) + wps_er_ap_remove_entry(er, ap); +} + + +static void http_put_date(struct wpabuf *buf) +{ + wpabuf_put_str(buf, "Date: "); + format_date(buf); + wpabuf_put_str(buf, "\r\n"); +} + + +static void wps_er_http_resp_not_found(struct http_request *req) +{ + struct wpabuf *buf; + buf = wpabuf_alloc(200); + if (buf == NULL) { + http_request_deinit(req); + return; + } + + wpabuf_put_str(buf, + "HTTP/1.1 404 Not Found\r\n" + "Server: unspecified, UPnP/1.0, unspecified\r\n" + "Connection: close\r\n"); + http_put_date(buf); + wpabuf_put_str(buf, "\r\n"); + http_request_send_and_deinit(req, buf); +} + + +static void wps_er_http_resp_ok(struct http_request *req) +{ + struct wpabuf *buf; + buf = wpabuf_alloc(200); + if (buf == NULL) { + http_request_deinit(req); + return; + } + + wpabuf_put_str(buf, + "HTTP/1.1 200 OK\r\n" + "Server: unspecified, UPnP/1.0, unspecified\r\n" + "Connection: close\r\n" + "Content-Length: 0\r\n"); + http_put_date(buf); + wpabuf_put_str(buf, "\r\n"); + http_request_send_and_deinit(req, buf); +} + + +static void wps_er_sta_timeout(void *eloop_data, void *user_ctx) +{ + struct wps_er_sta *sta = eloop_data; + wpa_printf(MSG_DEBUG, "WPS ER: STA entry timed out"); + dl_list_del(&sta->list); + wps_er_sta_free(sta); +} + + +static struct wps_er_sta * wps_er_add_sta_data(struct wps_er_ap *ap, + const u8 *addr, + struct wps_parse_attr *attr, + int probe_req) +{ + struct wps_er_sta *sta = wps_er_sta_get(ap, addr); + int new_sta = 0; + int m1; + + m1 = !probe_req && attr->msg_type && *attr->msg_type == WPS_M1; + + if (sta == NULL) { + /* + * Only allow new STA entry to be added based on Probe Request + * or M1. This will filter out bogus events and anything that + * may have been ongoing at the time ER subscribed for events. + */ + if (!probe_req && !m1) + return NULL; + + sta = os_zalloc(sizeof(*sta)); + if (sta == NULL) + return NULL; + os_memcpy(sta->addr, addr, ETH_ALEN); + sta->ap = ap; + dl_list_add(&ap->sta, &sta->list); + new_sta = 1; + } + + if (m1) + sta->m1_received = 1; + + if (attr->config_methods && (!probe_req || !sta->m1_received)) + sta->config_methods = WPA_GET_BE16(attr->config_methods); + if (attr->uuid_e && (!probe_req || !sta->m1_received)) + os_memcpy(sta->uuid, attr->uuid_e, WPS_UUID_LEN); + if (attr->primary_dev_type && (!probe_req || !sta->m1_received)) + os_memcpy(sta->pri_dev_type, attr->primary_dev_type, 8); + if (attr->dev_password_id && (!probe_req || !sta->m1_received)) + sta->dev_passwd_id = WPA_GET_BE16(attr->dev_password_id); + + if (attr->manufacturer) { + os_free(sta->manufacturer); + sta->manufacturer = os_malloc(attr->manufacturer_len + 1); + if (sta->manufacturer) { + os_memcpy(sta->manufacturer, attr->manufacturer, + attr->manufacturer_len); + sta->manufacturer[attr->manufacturer_len] = '\0'; + } + } + + if (attr->model_name) { + os_free(sta->model_name); + sta->model_name = os_malloc(attr->model_name_len + 1); + if (sta->model_name) { + os_memcpy(sta->model_name, attr->model_name, + attr->model_name_len); + sta->model_name[attr->model_name_len] = '\0'; + } + } + + if (attr->model_number) { + os_free(sta->model_number); + sta->model_number = os_malloc(attr->model_number_len + 1); + if (sta->model_number) { + os_memcpy(sta->model_number, attr->model_number, + attr->model_number_len); + sta->model_number[attr->model_number_len] = '\0'; + } + } + + if (attr->serial_number) { + os_free(sta->serial_number); + sta->serial_number = os_malloc(attr->serial_number_len + 1); + if (sta->serial_number) { + os_memcpy(sta->serial_number, attr->serial_number, + attr->serial_number_len); + sta->serial_number[attr->serial_number_len] = '\0'; + } + } + + if (attr->dev_name) { + os_free(sta->dev_name); + sta->dev_name = os_malloc(attr->dev_name_len + 1); + if (sta->dev_name) { + os_memcpy(sta->dev_name, attr->dev_name, + attr->dev_name_len); + sta->dev_name[attr->dev_name_len] = '\0'; + } + } + + eloop_cancel_timeout(wps_er_sta_timeout, sta, NULL); + eloop_register_timeout(300, 0, wps_er_sta_timeout, sta, NULL); + + if (m1 || new_sta) + wps_er_sta_event(ap->er->wps, sta, WPS_EV_ER_ENROLLEE_ADD); + + return sta; +} + + +static void wps_er_process_wlanevent_probe_req(struct wps_er_ap *ap, + const u8 *addr, + struct wpabuf *msg) +{ + struct wps_parse_attr attr; + + wpa_printf(MSG_DEBUG, "WPS ER: WLANEvent - Probe Request - from " + MACSTR, MAC2STR(addr)); + wpa_hexdump_buf(MSG_MSGDUMP, "WPS ER: WLANEvent - Enrollee's message " + "(TLVs from Probe Request)", msg); + + if (wps_parse_msg(msg, &attr) < 0) { + wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse TLVs in " + "WLANEvent message"); + return; + } + + wps_er_add_sta_data(ap, addr, &attr, 1); +} + + +static void wps_er_http_put_wlan_response_cb(void *ctx, struct http_client *c, + enum http_client_event event) +{ + struct wps_er_sta *sta = ctx; + + switch (event) { + case HTTP_CLIENT_OK: + wpa_printf(MSG_DEBUG, "WPS ER: PutWLANResponse OK"); + break; + case HTTP_CLIENT_FAILED: + case HTTP_CLIENT_INVALID_REPLY: + case HTTP_CLIENT_TIMEOUT: + wpa_printf(MSG_DEBUG, "WPS ER: PutWLANResponse failed"); + break; + } + http_client_free(sta->http); + sta->http = NULL; +} + + +static const char *soap_prefix = + "<?xml version=\"1.0\"?>\n" + "<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" " + "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\n" + "<s:Body>\n"; +static const char *soap_postfix = + "</s:Body>\n</s:Envelope>\n"; +static const char *urn_wfawlanconfig = + "urn:schemas-wifialliance-org:service:WFAWLANConfig:1"; + +static struct wpabuf * wps_er_soap_hdr(const struct wpabuf *msg, + const char *name, const char *arg_name, + const char *path, + const struct sockaddr_in *dst, + char **len_ptr, char **body_ptr) +{ + unsigned char *encoded; + size_t encoded_len; + struct wpabuf *buf; + + if (msg) { + encoded = base64_encode(wpabuf_head(msg), wpabuf_len(msg), + &encoded_len); + if (encoded == NULL) + return NULL; + } else { + encoded = NULL; + encoded_len = 0; + } + + buf = wpabuf_alloc(1000 + encoded_len); + if (buf == NULL) { + os_free(encoded); + return NULL; + } + + wpabuf_printf(buf, + "POST %s HTTP/1.1\r\n" + "Host: %s:%d\r\n" + "Content-Type: text/xml; charset=\"utf-8\"\r\n" + "Content-Length: ", + path, inet_ntoa(dst->sin_addr), ntohs(dst->sin_port)); + + *len_ptr = wpabuf_put(buf, 0); + wpabuf_printf(buf, + " \r\n" + "SOAPACTION: \"%s#%s\"\r\n" + "\r\n", + urn_wfawlanconfig, name); + + *body_ptr = wpabuf_put(buf, 0); + + wpabuf_put_str(buf, soap_prefix); + wpabuf_printf(buf, "<u:%s xmlns:u=\"", name); + wpabuf_put_str(buf, urn_wfawlanconfig); + wpabuf_put_str(buf, "\">\n"); + if (encoded) { + wpabuf_printf(buf, "<%s>%s</%s>\n", + arg_name, (char *) encoded, arg_name); + os_free(encoded); + } + + return buf; +} + + +static void wps_er_soap_end(struct wpabuf *buf, const char *name, + char *len_ptr, char *body_ptr) +{ + char len_buf[10]; + wpabuf_printf(buf, "</u:%s>\n", name); + wpabuf_put_str(buf, soap_postfix); + os_snprintf(len_buf, sizeof(len_buf), "%d", + (int) ((char *) wpabuf_put(buf, 0) - body_ptr)); + os_memcpy(len_ptr, len_buf, os_strlen(len_buf)); +} + + +static void wps_er_sta_send_msg(struct wps_er_sta *sta, struct wpabuf *msg) +{ + struct wpabuf *buf; + char *len_ptr, *body_ptr; + struct sockaddr_in dst; + char *url, *path; + + if (sta->http) { + wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request for STA - " + "ignore new request"); + wpabuf_free(msg); + return; + } + + if (sta->ap->control_url == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP"); + wpabuf_free(msg); + return; + } + + url = http_client_url_parse(sta->ap->control_url, &dst, &path); + if (url == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL"); + wpabuf_free(msg); + return; + } + + buf = wps_er_soap_hdr(msg, "PutWLANResponse", "NewMessage", path, &dst, + &len_ptr, &body_ptr); + wpabuf_free(msg); + os_free(url); + if (buf == NULL) + return; + wpabuf_printf(buf, "<NewWLANEventType>%d</NewWLANEventType>\n", + UPNP_WPS_WLANEVENT_TYPE_EAP); + wpabuf_printf(buf, "<NewWLANEventMAC>" MACSTR "</NewWLANEventMAC>\n", + MAC2STR(sta->addr)); + + wps_er_soap_end(buf, "PutWLANResponse", len_ptr, body_ptr); + + sta->http = http_client_addr(&dst, buf, 1000, + wps_er_http_put_wlan_response_cb, sta); + if (sta->http == NULL) + wpabuf_free(buf); +} + + +static void wps_er_sta_process(struct wps_er_sta *sta, struct wpabuf *msg, + enum wsc_op_code op_code) +{ + enum wps_process_res res; + + res = wps_process_msg(sta->wps, op_code, msg); + if (res == WPS_CONTINUE) { + struct wpabuf *next = wps_get_msg(sta->wps, &op_code); + if (next) + wps_er_sta_send_msg(sta, next); + } else { + wpa_printf(MSG_DEBUG, "WPS ER: Protocol run %s with the " + "enrollee (res=%d)", + res == WPS_DONE ? "succeeded" : "failed", res); + wps_deinit(sta->wps); + sta->wps = NULL; + if (res == WPS_DONE) { + /* Remove the STA entry after short timeout */ + eloop_cancel_timeout(wps_er_sta_timeout, sta, NULL); + eloop_register_timeout(10, 0, wps_er_sta_timeout, sta, + NULL); + } + } +} + + +static void wps_er_sta_start(struct wps_er_sta *sta, struct wpabuf *msg) +{ + struct wps_config cfg; + + if (sta->wps) + wps_deinit(sta->wps); + + os_memset(&cfg, 0, sizeof(cfg)); + cfg.wps = sta->ap->er->wps; + cfg.registrar = 1; + cfg.peer_addr = sta->addr; + + sta->wps = wps_init(&cfg); + if (sta->wps == NULL) + return; + sta->wps->er = 1; + sta->wps->use_cred = sta->ap->ap_settings; + if (sta->ap->ap_settings) { + os_free(sta->cred); + sta->cred = os_malloc(sizeof(*sta->cred)); + if (sta->cred) { + os_memcpy(sta->cred, sta->ap->ap_settings, + sizeof(*sta->cred)); + sta->cred->cred_attr = NULL; + os_memcpy(sta->cred->mac_addr, sta->addr, ETH_ALEN); + sta->wps->use_cred = sta->cred; + } + } + + wps_er_sta_process(sta, msg, WSC_MSG); +} + + +static void wps_er_process_wlanevent_eap(struct wps_er_ap *ap, const u8 *addr, + struct wpabuf *msg) +{ + struct wps_parse_attr attr; + struct wps_er_sta *sta; + + wpa_printf(MSG_DEBUG, "WPS ER: WLANEvent - EAP - from " MACSTR, + MAC2STR(addr)); + wpa_hexdump_buf(MSG_MSGDUMP, "WPS ER: WLANEvent - Enrollee's message " + "(TLVs from EAP-WSC)", msg); + + if (wps_parse_msg(msg, &attr) < 0) { + wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse TLVs in " + "WLANEvent message"); + return; + } + + sta = wps_er_add_sta_data(ap, addr, &attr, 0); + if (sta == NULL) + return; + + if (attr.msg_type && *attr.msg_type == WPS_M1) + wps_er_sta_start(sta, msg); + else if (sta->wps) { + enum wsc_op_code op_code = WSC_MSG; + if (attr.msg_type) { + switch (*attr.msg_type) { + case WPS_WSC_ACK: + op_code = WSC_ACK; + break; + case WPS_WSC_NACK: + op_code = WSC_NACK; + break; + case WPS_WSC_DONE: + op_code = WSC_Done; + break; + } + } + wps_er_sta_process(sta, msg, op_code); + } +} + + +static void wps_er_process_wlanevent(struct wps_er_ap *ap, + struct wpabuf *event) +{ + u8 *data; + u8 wlan_event_type; + u8 wlan_event_mac[ETH_ALEN]; + struct wpabuf msg; + + wpa_hexdump(MSG_MSGDUMP, "WPS ER: Received WLANEvent", + wpabuf_head(event), wpabuf_len(event)); + if (wpabuf_len(event) < 1 + 17) { + wpa_printf(MSG_DEBUG, "WPS ER: Too short WLANEvent"); + return; + } + + data = wpabuf_mhead(event); + wlan_event_type = data[0]; + if (hwaddr_aton((char *) data + 1, wlan_event_mac) < 0) { + wpa_printf(MSG_DEBUG, "WPS ER: Invalid WLANEventMAC in " + "WLANEvent"); + return; + } + + wpabuf_set(&msg, data + 1 + 17, wpabuf_len(event) - (1 + 17)); + + switch (wlan_event_type) { + case 1: + wps_er_process_wlanevent_probe_req(ap, wlan_event_mac, &msg); + break; + case 2: + wps_er_process_wlanevent_eap(ap, wlan_event_mac, &msg); + break; + default: + wpa_printf(MSG_DEBUG, "WPS ER: Unknown WLANEventType %d", + wlan_event_type); + break; + } +} + + +static void wps_er_http_event(struct wps_er *er, struct http_request *req, + unsigned int ap_id) +{ + struct wps_er_ap *ap = wps_er_ap_get_id(er, ap_id); + struct wpabuf *event; + enum http_reply_code ret; + + if (ap == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: HTTP event from unknown AP id " + "%u", ap_id); + wps_er_http_resp_not_found(req); + return; + } + wpa_printf(MSG_MSGDUMP, "WPS ER: HTTP event from AP id %u: %s", + ap_id, http_request_get_data(req)); + + event = xml_get_base64_item(http_request_get_data(req), "WLANEvent", + &ret); + if (event == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: Could not extract WLANEvent " + "from the event notification"); + /* + * Reply with OK anyway to avoid getting unregistered from + * events. + */ + wps_er_http_resp_ok(req); + return; + } + + wps_er_process_wlanevent(ap, event); + + wpabuf_free(event); + wps_er_http_resp_ok(req); +} + + +static void wps_er_http_notify(struct wps_er *er, struct http_request *req) +{ + char *uri = http_request_get_uri(req); + + if (os_strncmp(uri, "/event/", 7) == 0) { + unsigned int event_id; + char *pos; + event_id = atoi(uri + 7); + if (event_id != er->event_id) { + wpa_printf(MSG_DEBUG, "WPS ER: HTTP event for an " + "unknown event id %u", event_id); + return; + } + pos = os_strchr(uri + 7, '/'); + if (pos == NULL) + return; + pos++; + wps_er_http_event(er, req, atoi(pos)); + } else { + wpa_printf(MSG_DEBUG, "WPS ER: Unknown HTTP NOTIFY for '%s'", + uri); + wps_er_http_resp_not_found(req); + } +} + + +static void wps_er_http_req(void *ctx, struct http_request *req) +{ + struct wps_er *er = ctx; + struct sockaddr_in *cli = http_request_get_cli_addr(req); + enum httpread_hdr_type type = http_request_get_type(req); + struct wpabuf *buf; + + wpa_printf(MSG_DEBUG, "WPS ER: HTTP request: '%s' (type %d) from " + "%s:%d", + http_request_get_uri(req), type, + inet_ntoa(cli->sin_addr), ntohs(cli->sin_port)); + + switch (type) { + case HTTPREAD_HDR_TYPE_NOTIFY: + wps_er_http_notify(er, req); + break; + default: + wpa_printf(MSG_DEBUG, "WPS ER: Unsupported HTTP request type " + "%d", type); + buf = wpabuf_alloc(200); + if (buf == NULL) { + http_request_deinit(req); + return; + } + wpabuf_put_str(buf, + "HTTP/1.1 501 Unimplemented\r\n" + "Connection: close\r\n"); + http_put_date(buf); + wpabuf_put_str(buf, "\r\n"); + http_request_send_and_deinit(req, buf); + break; + } +} + + +struct wps_er * +wps_er_init(struct wps_context *wps, const char *ifname) +{ + struct wps_er *er; + struct in_addr addr; + + er = os_zalloc(sizeof(*er)); + if (er == NULL) + return NULL; + dl_list_init(&er->ap); + dl_list_init(&er->ap_unsubscribing); + + er->multicast_sd = -1; + er->ssdp_sd = -1; + + os_strlcpy(er->ifname, ifname, sizeof(er->ifname)); + er->wps = wps; + if (os_get_random((unsigned char *) &er->event_id, + sizeof(er->event_id)) < 0) { + wps_er_deinit(er, NULL, NULL); + return NULL; + } + /* Limit event_id to < 32 bits to avoid issues with atoi() */ + er->event_id &= 0x0fffffff; + + if (get_netif_info(ifname, &er->ip_addr, &er->ip_addr_text, + er->mac_addr)) { + wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address " + "for %s. Does it have IP address?", ifname); + wps_er_deinit(er, NULL, NULL); + return NULL; + } + + if (wps_er_ssdp_init(er) < 0) { + wps_er_deinit(er, NULL, NULL); + return NULL; + } + + addr.s_addr = er->ip_addr; + er->http_srv = http_server_init(&addr, -1, wps_er_http_req, er); + if (er->http_srv == NULL) { + wps_er_deinit(er, NULL, NULL); + return NULL; + } + er->http_port = http_server_get_port(er->http_srv); + + wpa_printf(MSG_DEBUG, "WPS ER: Start (ifname=%s ip_addr=%s)", + er->ifname, er->ip_addr_text); + + return er; +} + + +void wps_er_refresh(struct wps_er *er) +{ + struct wps_er_ap *ap; + struct wps_er_sta *sta; + + dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) { + wps_er_ap_event(er->wps, ap, WPS_EV_ER_AP_ADD); + dl_list_for_each(sta, &ap->sta, struct wps_er_sta, list) + wps_er_sta_event(er->wps, sta, WPS_EV_ER_ENROLLEE_ADD); + } + + wps_er_send_ssdp_msearch(er); +} + + +static void wps_er_deinit_finish(void *eloop_data, void *user_ctx) +{ + struct wps_er *er = eloop_data; + void (*deinit_done_cb)(void *ctx); + void *deinit_done_ctx; + + wpa_printf(MSG_DEBUG, "WPS ER: Finishing deinit"); + + deinit_done_cb = er->deinit_done_cb; + deinit_done_ctx = er->deinit_done_ctx; + os_free(er->ip_addr_text); + os_free(er); + + if (deinit_done_cb) + deinit_done_cb(deinit_done_ctx); +} + + +void wps_er_deinit(struct wps_er *er, void (*cb)(void *ctx), void *ctx) +{ + if (er == NULL) + return; + http_server_deinit(er->http_srv); + wps_er_ap_remove_all(er); + wps_er_ssdp_deinit(er); + eloop_register_timeout(dl_list_empty(&er->ap_unsubscribing) ? 0 : 5, 0, + wps_er_deinit_finish, er, NULL); + wpa_printf(MSG_DEBUG, "WPS ER: Finish deinit from timeout"); + er->deinitializing = 1; + er->deinit_done_cb = cb; + er->deinit_done_ctx = ctx; +} + + +static void wps_er_http_set_sel_reg_cb(void *ctx, struct http_client *c, + enum http_client_event event) +{ + struct wps_er_ap *ap = ctx; + + switch (event) { + case HTTP_CLIENT_OK: + wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar OK"); + break; + case HTTP_CLIENT_FAILED: + case HTTP_CLIENT_INVALID_REPLY: + case HTTP_CLIENT_TIMEOUT: + wpa_printf(MSG_DEBUG, "WPS ER: SetSelectedRegistrar failed"); + break; + } + http_client_free(ap->http); + ap->http = NULL; +} + + +static void wps_er_send_set_sel_reg(struct wps_er_ap *ap, struct wpabuf *msg) +{ + struct wpabuf *buf; + char *len_ptr, *body_ptr; + struct sockaddr_in dst; + char *url, *path; + + if (ap->control_url == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP"); + return; + } + + if (ap->http) { + wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP request for AP - " + "ignore new request"); + return; + } + + url = http_client_url_parse(ap->control_url, &dst, &path); + if (url == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL"); + return; + } + + buf = wps_er_soap_hdr(msg, "SetSelectedRegistrar", "NewMessage", path, + &dst, &len_ptr, &body_ptr); + os_free(url); + if (buf == NULL) + return; + + wps_er_soap_end(buf, "SetSelectedRegistrar", len_ptr, body_ptr); + + ap->http = http_client_addr(&dst, buf, 1000, + wps_er_http_set_sel_reg_cb, ap); + if (ap->http == NULL) + wpabuf_free(buf); +} + + +static int wps_er_build_selected_registrar(struct wpabuf *msg, int sel_reg) +{ + wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR); + wpabuf_put_be16(msg, 1); + wpabuf_put_u8(msg, !!sel_reg); + return 0; +} + + +static int wps_er_build_dev_password_id(struct wpabuf *msg, u16 dev_passwd_id) +{ + wpabuf_put_be16(msg, ATTR_DEV_PASSWORD_ID); + wpabuf_put_be16(msg, 2); + wpabuf_put_be16(msg, dev_passwd_id); + return 0; +} + + +static int wps_er_build_sel_reg_config_methods(struct wpabuf *msg, + u16 sel_reg_config_methods) +{ + wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR_CONFIG_METHODS); + wpabuf_put_be16(msg, 2); + wpabuf_put_be16(msg, sel_reg_config_methods); + return 0; +} + + +void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id, + u16 sel_reg_config_methods) +{ + struct wpabuf *msg; + struct wps_er_ap *ap; + + msg = wpabuf_alloc(500); + if (msg == NULL) + return; + + if (wps_build_version(msg) || + wps_er_build_selected_registrar(msg, sel_reg) || + wps_er_build_dev_password_id(msg, dev_passwd_id) || + wps_er_build_sel_reg_config_methods(msg, sel_reg_config_methods)) { + wpabuf_free(msg); + return; + } + + dl_list_for_each(ap, &er->ap, struct wps_er_ap, list) + wps_er_send_set_sel_reg(ap, msg); + + wpabuf_free(msg); +} + + +int wps_er_pbc(struct wps_er *er, const u8 *uuid) +{ + if (er == NULL || er->wps == NULL) + return -1; + + /* + * TODO: Should enable PBC mode only in a single AP based on which AP + * the Enrollee (uuid) is using. Now, we may end up enabling multiple + * APs in PBC mode which could result in session overlap at the + * Enrollee. + */ + if (wps_registrar_button_pushed(er->wps->registrar)) + return -1; + + return 0; +} + + +static void wps_er_ap_settings_cb(void *ctx, const struct wps_credential *cred) +{ + struct wps_er_ap *ap = ctx; + wpa_printf(MSG_DEBUG, "WPS ER: AP Settings received"); + os_free(ap->ap_settings); + ap->ap_settings = os_malloc(sizeof(*cred)); + if (ap->ap_settings) { + os_memcpy(ap->ap_settings, cred, sizeof(*cred)); + ap->ap_settings->cred_attr = NULL; + } + + /* TODO: send info through ctrl_iface */ +} + + +static void wps_er_http_put_message_cb(void *ctx, struct http_client *c, + enum http_client_event event) +{ + struct wps_er_ap *ap = ctx; + struct wpabuf *reply; + char *msg = NULL; + + switch (event) { + case HTTP_CLIENT_OK: + wpa_printf(MSG_DEBUG, "WPS ER: PutMessage OK"); + reply = http_client_get_body(c); + if (reply == NULL) + break; + msg = os_zalloc(wpabuf_len(reply) + 1); + if (msg == NULL) + break; + os_memcpy(msg, wpabuf_head(reply), wpabuf_len(reply)); + break; + case HTTP_CLIENT_FAILED: + case HTTP_CLIENT_INVALID_REPLY: + case HTTP_CLIENT_TIMEOUT: + wpa_printf(MSG_DEBUG, "WPS ER: PutMessage failed"); + if (ap->wps) { + wps_deinit(ap->wps); + ap->wps = NULL; + } + break; + } + http_client_free(ap->http); + ap->http = NULL; + + if (msg) { + struct wpabuf *buf; + enum http_reply_code ret; + buf = xml_get_base64_item(msg, "NewOutMessage", &ret); + os_free(msg); + if (buf == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: Could not extract " + "NewOutMessage from PutMessage response"); + return; + } + wps_er_ap_process(ap, buf); + wpabuf_free(buf); + } +} + + +static void wps_er_ap_put_message(struct wps_er_ap *ap, + const struct wpabuf *msg) +{ + struct wpabuf *buf; + char *len_ptr, *body_ptr; + struct sockaddr_in dst; + char *url, *path; + + if (ap->http) { + wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP operation ongoing " + "with the AP - cannot continue learn"); + return; + } + + if (ap->control_url == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP"); + return; + } + + url = http_client_url_parse(ap->control_url, &dst, &path); + if (url == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL"); + return; + } + + buf = wps_er_soap_hdr(msg, "PutMessage", "NewInMessage", path, &dst, + &len_ptr, &body_ptr); + os_free(url); + if (buf == NULL) + return; + + wps_er_soap_end(buf, "PutMessage", len_ptr, body_ptr); + + ap->http = http_client_addr(&dst, buf, 10000, + wps_er_http_put_message_cb, ap); + if (ap->http == NULL) + wpabuf_free(buf); +} + + +static void wps_er_ap_process(struct wps_er_ap *ap, struct wpabuf *msg) +{ + enum wps_process_res res; + + res = wps_process_msg(ap->wps, WSC_MSG, msg); + if (res == WPS_CONTINUE) { + enum wsc_op_code op_code; + struct wpabuf *next = wps_get_msg(ap->wps, &op_code); + if (next) { + wps_er_ap_put_message(ap, next); + wpabuf_free(next); + } else { + wpa_printf(MSG_DEBUG, "WPS ER: Failed to build " + "message"); + wps_deinit(ap->wps); + ap->wps = NULL; + } + } else { + wpa_printf(MSG_DEBUG, "WPS ER: Failed to process message from " + "AP (res=%d)", res); + wps_deinit(ap->wps); + ap->wps = NULL; + } +} + + +static void wps_er_ap_learn_m1(struct wps_er_ap *ap, struct wpabuf *m1) +{ + struct wps_config cfg; + + if (ap->wps) { + wpa_printf(MSG_DEBUG, "WPS ER: Protocol run already in " + "progress with this AP"); + return; + } + + os_memset(&cfg, 0, sizeof(cfg)); + cfg.wps = ap->er->wps; + cfg.registrar = 1; + ap->wps = wps_init(&cfg); + if (ap->wps == NULL) + return; + ap->wps->ap_settings_cb = wps_er_ap_settings_cb; + ap->wps->ap_settings_cb_ctx = ap; + + wps_er_ap_process(ap, m1); +} + + +static void wps_er_ap_learn(struct wps_er_ap *ap, const char *dev_info) +{ + struct wpabuf *info; + enum http_reply_code ret; + + wpa_printf(MSG_DEBUG, "WPS ER: Received GetDeviceInfo response (M1) " + "from the AP"); + info = xml_get_base64_item(dev_info, "NewDeviceInfo", &ret); + if (info == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: Could not extract " + "NewDeviceInfo from GetDeviceInfo response"); + return; + } + + ap->m1_handler(ap, info); + wpabuf_free(info); +} + + +static void wps_er_http_get_dev_info_cb(void *ctx, struct http_client *c, + enum http_client_event event) +{ + struct wps_er_ap *ap = ctx; + struct wpabuf *reply; + char *dev_info = NULL; + + switch (event) { + case HTTP_CLIENT_OK: + wpa_printf(MSG_DEBUG, "WPS ER: GetDeviceInfo OK"); + reply = http_client_get_body(c); + if (reply == NULL) + break; + dev_info = os_zalloc(wpabuf_len(reply) + 1); + if (dev_info == NULL) + break; + os_memcpy(dev_info, wpabuf_head(reply), wpabuf_len(reply)); + break; + case HTTP_CLIENT_FAILED: + case HTTP_CLIENT_INVALID_REPLY: + case HTTP_CLIENT_TIMEOUT: + wpa_printf(MSG_DEBUG, "WPS ER: GetDeviceInfo failed"); + break; + } + http_client_free(ap->http); + ap->http = NULL; + + if (dev_info) { + wps_er_ap_learn(ap, dev_info); + os_free(dev_info); + } +} + + +static int wps_er_send_get_device_info(struct wps_er_ap *ap, + void (*m1_handler)(struct wps_er_ap *ap, + struct wpabuf *m1)) +{ + struct wpabuf *buf; + char *len_ptr, *body_ptr; + struct sockaddr_in dst; + char *url, *path; + + if (ap->http) { + wpa_printf(MSG_DEBUG, "WPS ER: Pending HTTP operation ongoing " + "with the AP - cannot get device info"); + return -1; + } + + if (ap->control_url == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: No controlURL for AP"); + return -1; + } + + url = http_client_url_parse(ap->control_url, &dst, &path); + if (url == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: Failed to parse controlURL"); + return -1; + } + + buf = wps_er_soap_hdr(NULL, "GetDeviceInfo", NULL, path, &dst, + &len_ptr, &body_ptr); + os_free(url); + if (buf == NULL) + return -1; + + wps_er_soap_end(buf, "GetDeviceInfo", len_ptr, body_ptr); + + ap->http = http_client_addr(&dst, buf, 10000, + wps_er_http_get_dev_info_cb, ap); + if (ap->http == NULL) { + wpabuf_free(buf); + return -1; + } + + ap->m1_handler = m1_handler; + + return 0; +} + + +int wps_er_learn(struct wps_er *er, const u8 *uuid, const u8 *pin, + size_t pin_len) +{ + struct wps_er_ap *ap; + + if (er == NULL) + return -1; + + ap = wps_er_ap_get(er, NULL, uuid); + if (ap == NULL) { + wpa_printf(MSG_DEBUG, "WPS ER: AP not found for learn " + "request"); + return -1; + } + if (ap->wps) { + wpa_printf(MSG_DEBUG, "WPS ER: Pending operation ongoing " + "with the AP - cannot start learn"); + return -1; + } + + if (wps_er_send_get_device_info(ap, wps_er_ap_learn_m1) < 0) + return -1; + + /* TODO: add PIN without SetSelectedRegistrar trigger to all APs */ + wps_registrar_add_pin(er->wps->registrar, uuid, pin, pin_len, 0); + + return 0; +} diff --git a/contrib/wpa/src/wps/wps_er.h b/contrib/wpa/src/wps/wps_er.h new file mode 100644 index 0000000..b13b950 --- /dev/null +++ b/contrib/wpa/src/wps/wps_er.h @@ -0,0 +1,106 @@ +/* + * Wi-Fi Protected Setup - External Registrar + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#ifndef WPS_ER_H +#define WPS_ER_H + +#include "utils/list.h" + +struct wps_er_sta { + struct dl_list list; + struct wps_er_ap *ap; + u8 addr[ETH_ALEN]; + u16 config_methods; + u8 uuid[WPS_UUID_LEN]; + u8 pri_dev_type[8]; + u16 dev_passwd_id; + int m1_received; + char *manufacturer; + char *model_name; + char *model_number; + char *serial_number; + char *dev_name; + struct wps_data *wps; + struct http_client *http; + struct wps_credential *cred; +}; + +struct wps_er_ap { + struct dl_list list; + struct wps_er *er; + struct dl_list sta; /* list of STAs/Enrollees using this AP */ + struct in_addr addr; + char *location; + struct http_client *http; + struct wps_data *wps; + + u8 uuid[WPS_UUID_LEN]; + u8 pri_dev_type[8]; + u8 wps_state; + u8 mac_addr[ETH_ALEN]; + char *friendly_name; + char *manufacturer; + char *manufacturer_url; + char *model_description; + char *model_name; + char *model_number; + char *model_url; + char *serial_number; + char *udn; + char *upc; + + char *scpd_url; + char *control_url; + char *event_sub_url; + + int subscribed; + u8 sid[WPS_UUID_LEN]; + unsigned int id; + + struct wps_credential *ap_settings; + + void (*m1_handler)(struct wps_er_ap *ap, struct wpabuf *m1); +}; + +struct wps_er { + struct wps_context *wps; + char ifname[17]; + u8 mac_addr[ETH_ALEN]; /* mac addr of network i.f. we use */ + char *ip_addr_text; /* IP address of network i.f. we use */ + unsigned ip_addr; /* IP address of network i.f. we use (host order) */ + int multicast_sd; + int ssdp_sd; + struct dl_list ap; + struct dl_list ap_unsubscribing; + struct http_server *http_srv; + int http_port; + unsigned int next_ap_id; + unsigned int event_id; + int deinitializing; + void (*deinit_done_cb)(void *ctx); + void *deinit_done_ctx; +}; + + +/* wps_er.c */ +void wps_er_ap_add(struct wps_er *er, const u8 *uuid, struct in_addr *addr, + const char *location, int max_age); +void wps_er_ap_remove(struct wps_er *er, struct in_addr *addr); + +/* wps_er_ssdp.c */ +int wps_er_ssdp_init(struct wps_er *er); +void wps_er_ssdp_deinit(struct wps_er *er); +void wps_er_send_ssdp_msearch(struct wps_er *er); + +#endif /* WPS_ER_H */ diff --git a/contrib/wpa/src/wps/wps_er_ssdp.c b/contrib/wpa/src/wps/wps_er_ssdp.c new file mode 100644 index 0000000..f108435 --- /dev/null +++ b/contrib/wpa/src/wps/wps_er_ssdp.c @@ -0,0 +1,198 @@ +/* + * Wi-Fi Protected Setup - External Registrar (SSDP) + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "uuid.h" +#include "eloop.h" +#include "wps_i.h" +#include "wps_upnp.h" +#include "wps_upnp_i.h" +#include "wps_er.h" + + +static void wps_er_ssdp_rx(int sd, void *eloop_ctx, void *sock_ctx) +{ + struct wps_er *er = eloop_ctx; + struct sockaddr_in addr; /* client address */ + socklen_t addr_len; + int nread; + char buf[MULTICAST_MAX_READ], *pos, *pos2, *start; + int wfa = 0, byebye = 0; + int max_age = -1; + char *location = NULL; + u8 uuid[WPS_UUID_LEN]; + + addr_len = sizeof(addr); + nread = recvfrom(sd, buf, sizeof(buf) - 1, 0, + (struct sockaddr *) &addr, &addr_len); + if (nread <= 0) + return; + buf[nread] = '\0'; + + wpa_printf(MSG_DEBUG, "WPS ER: Received SSDP from %s", + inet_ntoa(addr.sin_addr)); + wpa_hexdump_ascii(MSG_MSGDUMP, "WPS ER: Received SSDP contents", + (u8 *) buf, nread); + + if (sd == er->multicast_sd) { + /* Reply to M-SEARCH */ + if (os_strncmp(buf, "HTTP/1.1 200 OK", 15) != 0) + return; /* unexpected response header */ + } else { + /* Unsolicited message (likely NOTIFY or M-SEARCH) */ + if (os_strncmp(buf, "NOTIFY ", 7) != 0) + return; /* only process notifications */ + } + + os_memset(uuid, 0, sizeof(uuid)); + + for (start = buf; start && *start; start = pos) { + pos = os_strchr(start, '\n'); + if (pos) { + if (pos[-1] == '\r') + pos[-1] = '\0'; + *pos++ = '\0'; + } + if (os_strstr(start, "schemas-wifialliance-org:device:" + "WFADevice:1")) + wfa = 1; + if (os_strstr(start, "schemas-wifialliance-org:service:" + "WFAWLANConfig:1")) + wfa = 1; + if (os_strncasecmp(start, "LOCATION:", 9) == 0) { + start += 9; + while (*start == ' ') + start++; + location = start; + } else if (os_strncasecmp(start, "NTS:", 4) == 0) { + if (os_strstr(start, "ssdp:byebye")) + byebye = 1; + } else if (os_strncasecmp(start, "CACHE-CONTROL:", 14) == 0) { + start += 9; + while (*start == ' ') + start++; + pos2 = os_strstr(start, "max-age="); + if (pos2 == NULL) + continue; + pos2 += 8; + max_age = atoi(pos2); + } else if (os_strncasecmp(start, "USN:", 4) == 0) { + start += 4; + pos2 = os_strstr(start, "uuid:"); + if (pos2) { + pos2 += 5; + while (*pos2 == ' ') + pos2++; + if (uuid_str2bin(pos2, uuid) < 0) { + wpa_printf(MSG_DEBUG, "WPS ER: " + "Invalid UUID in USN: %s", + pos2); + return; + } + } + } + } + + if (!wfa) + return; /* Not WPS advertisement/reply */ + + if (byebye) { + wps_er_ap_remove(er, &addr.sin_addr); + return; + } + + if (!location) + return; /* Unknown location */ + + if (max_age < 1) + return; /* No max-age reported */ + + wpa_printf(MSG_DEBUG, "WPS ER: AP discovered: %s " + "(packet source: %s max-age: %d)", + location, inet_ntoa(addr.sin_addr), max_age); + + wps_er_ap_add(er, uuid, &addr.sin_addr, location, max_age); +} + + +void wps_er_send_ssdp_msearch(struct wps_er *er) +{ + struct wpabuf *msg; + struct sockaddr_in dest; + + msg = wpabuf_alloc(500); + if (msg == NULL) + return; + + wpabuf_put_str(msg, + "M-SEARCH * HTTP/1.1\r\n" + "HOST: 239.255.255.250:1900\r\n" + "MAN: \"ssdp:discover\"\r\n" + "MX: 3\r\n" + "ST: urn:schemas-wifialliance-org:device:WFADevice:1" + "\r\n" + "\r\n"); + + os_memset(&dest, 0, sizeof(dest)); + dest.sin_family = AF_INET; + dest.sin_addr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS); + dest.sin_port = htons(UPNP_MULTICAST_PORT); + + if (sendto(er->multicast_sd, wpabuf_head(msg), wpabuf_len(msg), 0, + (struct sockaddr *) &dest, sizeof(dest)) < 0) + wpa_printf(MSG_DEBUG, "WPS ER: M-SEARCH sendto failed: " + "%d (%s)", errno, strerror(errno)); + + wpabuf_free(msg); +} + + +int wps_er_ssdp_init(struct wps_er *er) +{ + if (add_ssdp_network(er->ifname)) + return -1; + + er->multicast_sd = ssdp_open_multicast_sock(er->ip_addr); + if (er->multicast_sd < 0) + return -1; + + er->ssdp_sd = ssdp_listener_open(); + if (er->ssdp_sd < 0) + return -1; + + if (eloop_register_sock(er->multicast_sd, EVENT_TYPE_READ, + wps_er_ssdp_rx, er, NULL) || + eloop_register_sock(er->ssdp_sd, EVENT_TYPE_READ, + wps_er_ssdp_rx, er, NULL)) + return -1; + + wps_er_send_ssdp_msearch(er); + + return 0; +} + + +void wps_er_ssdp_deinit(struct wps_er *er) +{ + if (er->multicast_sd >= 0) { + eloop_unregister_sock(er->multicast_sd, EVENT_TYPE_READ); + close(er->multicast_sd); + } + if (er->ssdp_sd >= 0) { + eloop_unregister_sock(er->ssdp_sd, EVENT_TYPE_READ); + close(er->ssdp_sd); + } +} diff --git a/contrib/wpa/src/wps/wps_i.h b/contrib/wpa/src/wps/wps_i.h index 3317a2c..50e66f6 100644 --- a/contrib/wpa/src/wps/wps_i.h +++ b/contrib/wpa/src/wps/wps_i.h @@ -1,6 +1,6 @@ /* * Wi-Fi Protected Setup - internal definitions - * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -34,6 +34,11 @@ struct wps_data { */ int registrar; + /** + * er - Whether the local end is an external registrar + */ + int er; + enum { /* Enrollee states */ SEND_M1, RECV_M2, SEND_M3, RECV_M4, SEND_M5, RECV_M6, SEND_M7, @@ -99,6 +104,18 @@ struct wps_data { u16 config_error; int ext_reg; + int int_reg; + + struct wps_credential *new_ap_settings; + + void *dh_ctx; + + void (*ap_settings_cb)(void *ctx, const struct wps_credential *cred); + void *ap_settings_cb_ctx; + + struct wps_credential *use_cred; + + int use_psk_key; }; @@ -120,6 +137,8 @@ struct wps_parse_attr { const u8 *assoc_state; /* 2 octets */ const u8 *config_error; /* 2 octets */ const u8 *dev_password_id; /* 2 octets */ + const u8 *oob_dev_password; /* WPS_OOB_DEVICE_PASSWORD_ATTR_LEN (54) + * octets */ const u8 *os_version; /* 4 octets */ const u8 *wps_state; /* 1 octet */ const u8 *authenticator; /* WPS_AUTHENTICATOR_LEN (8) octets */ @@ -189,12 +208,17 @@ void wps_pwd_auth_fail_event(struct wps_context *wps, int enrollee, int part); void wps_pbc_overlap_event(struct wps_context *wps); void wps_pbc_timeout_event(struct wps_context *wps); +extern struct oob_device_data oob_ufd_device_data; +extern struct oob_device_data oob_nfc_device_data; +extern struct oob_nfc_device_data oob_nfc_pn531_device_data; + /* wps_attr_parse.c */ int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr); /* wps_attr_build.c */ int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg); int wps_build_req_type(struct wpabuf *msg, enum wps_request_type type); +int wps_build_resp_type(struct wpabuf *msg, enum wps_response_type type); int wps_build_config_methods(struct wpabuf *msg, u16 methods); int wps_build_uuid_e(struct wpabuf *msg, const u8 *uuid); int wps_build_dev_password_id(struct wpabuf *msg, u16 id); @@ -211,6 +235,7 @@ int wps_build_auth_type_flags(struct wps_data *wps, struct wpabuf *msg); int wps_build_encr_type_flags(struct wps_data *wps, struct wpabuf *msg); int wps_build_conn_type_flags(struct wps_data *wps, struct wpabuf *msg); int wps_build_assoc_state(struct wps_data *wps, struct wpabuf *msg); +int wps_build_oob_dev_password(struct wpabuf *msg, struct wps_context *wps); /* wps_attr_process.c */ int wps_process_authenticator(struct wps_data *wps, const u8 *authenticator, @@ -235,7 +260,14 @@ struct wpabuf * wps_registrar_get_msg(struct wps_data *wps, enum wps_process_res wps_registrar_process_msg(struct wps_data *wps, enum wsc_op_code op_code, const struct wpabuf *msg); +int wps_build_cred(struct wps_data *wps, struct wpabuf *msg); +int wps_device_store(struct wps_registrar *reg, + struct wps_device_data *dev, const u8 *uuid); +void wps_registrar_selected_registrar_changed(struct wps_registrar *reg); +/* ndef.c */ +struct wpabuf * ndef_parse_wifi(struct wpabuf *buf); +struct wpabuf * ndef_build_wifi(struct wpabuf *buf); static inline int wps_version_supported(const u8 *version) { diff --git a/contrib/wpa/src/wps/wps_nfc.c b/contrib/wpa/src/wps/wps_nfc.c new file mode 100644 index 0000000..ff12000 --- /dev/null +++ b/contrib/wpa/src/wps/wps_nfc.c @@ -0,0 +1,117 @@ +/* + * NFC routines for Wi-Fi Protected Setup + * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include "common.h" + +#include "wps/wps.h" +#include "wps_i.h" + + +struct wps_nfc_data { + struct oob_nfc_device_data *oob_nfc_dev; +}; + + +static void * init_nfc(struct wps_context *wps, + struct oob_device_data *oob_dev, int registrar) +{ + struct oob_nfc_device_data *oob_nfc_dev; + struct wps_nfc_data *data; + + oob_nfc_dev = wps_get_oob_nfc_device(oob_dev->device_name); + if (oob_nfc_dev == NULL) { + wpa_printf(MSG_ERROR, "WPS (NFC): Unknown NFC device (%s)", + oob_dev->device_name); + return NULL; + } + + if (oob_nfc_dev->init_func(oob_dev->device_path) < 0) + return NULL; + + data = os_zalloc(sizeof(*data)); + if (data == NULL) { + wpa_printf(MSG_ERROR, "WPS (NFC): Failed to allocate " + "nfc data area"); + return NULL; + } + data->oob_nfc_dev = oob_nfc_dev; + return data; +} + + +static struct wpabuf * read_nfc(void *priv) +{ + struct wps_nfc_data *data = priv; + struct wpabuf *wifi, *buf; + char *raw_data; + size_t len; + + raw_data = data->oob_nfc_dev->read_func(&len); + if (raw_data == NULL) + return NULL; + + wifi = wpabuf_alloc_copy(raw_data, len); + os_free(raw_data); + if (wifi == NULL) { + wpa_printf(MSG_ERROR, "WPS (NFC): Failed to allocate " + "nfc read area"); + return NULL; + } + + buf = ndef_parse_wifi(wifi); + wpabuf_free(wifi); + if (buf == NULL) + wpa_printf(MSG_ERROR, "WPS (NFC): Failed to unwrap"); + return buf; +} + + +static int write_nfc(void *priv, struct wpabuf *buf) +{ + struct wps_nfc_data *data = priv; + struct wpabuf *wifi; + int ret; + + wifi = ndef_build_wifi(buf); + if (wifi == NULL) { + wpa_printf(MSG_ERROR, "WPS (NFC): Failed to wrap"); + return -1; + } + + ret = data->oob_nfc_dev->write_func(wpabuf_mhead(wifi), + wpabuf_len(wifi)); + wpabuf_free(wifi); + return ret; +} + + +static void deinit_nfc(void *priv) +{ + struct wps_nfc_data *data = priv; + + data->oob_nfc_dev->deinit_func(); + + os_free(data); +} + + +struct oob_device_data oob_nfc_device_data = { + .device_name = NULL, + .device_path = NULL, + .init_func = init_nfc, + .read_func = read_nfc, + .write_func = write_nfc, + .deinit_func = deinit_nfc, +}; diff --git a/contrib/wpa/src/wps/wps_nfc_pn531.c b/contrib/wpa/src/wps/wps_nfc_pn531.c new file mode 100644 index 0000000..7e05e4d --- /dev/null +++ b/contrib/wpa/src/wps/wps_nfc_pn531.c @@ -0,0 +1,113 @@ +/* + * NFC PN531 routines for Wi-Fi Protected Setup + * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include "common.h" + +#include "wps/wps.h" +#include "wps_i.h" + +#include "WpsNfcType.h" +#include "WpsNfc.h" + + +static int init_nfc_pn531(char *path) +{ + u32 ret; + + ret = WpsNfcInit(); + if (ret != WPS_NFCLIB_ERR_SUCCESS) { + wpa_printf(MSG_ERROR, "WPS (PN531): Failed to initialize " + "NFC Library: 0x%08x", ret); + return -1; + } + + ret = WpsNfcOpenDevice((int8 *) path); + if (ret != WPS_NFCLIB_ERR_SUCCESS) { + wpa_printf(MSG_ERROR, "WPS (PN531): Failed to open " + "NFC Device(%s): 0x%08x", path, ret); + goto fail; + } + + ret = WpsNfcTokenDiscovery(); + if (ret != WPS_NFCLIB_ERR_SUCCESS) { + wpa_printf(MSG_ERROR, "WPS (PN531): Failed to discover " + "token: 0x%08x", ret); + WpsNfcCloseDevice(); + goto fail; + } + + return 0; + +fail: + WpsNfcDeinit(); + return -1; +} + + +static void * read_nfc_pn531(size_t *size) +{ + uint32 len; + u32 ret; + int8 *data; + + ret = WpsNfcRawReadToken(&data, &len); + if (ret != WPS_NFCLIB_ERR_SUCCESS) { + wpa_printf(MSG_ERROR, "WPS (PN531): Failed to read: 0x%08x", + ret); + return NULL; + } + + *size = len; + return data; +} + + +static int write_nfc_pn531(void *data, size_t len) +{ + u32 ret; + + ret = WpsNfcRawWriteToken(data, len); + if (ret != WPS_NFCLIB_ERR_SUCCESS) { + wpa_printf(MSG_ERROR, "WPS (PN531): Failed to write: 0x%08x", + ret); + return -1; + } + + return 0; +} + + +static void deinit_nfc_pn531(void) +{ + u32 ret; + + ret = WpsNfcCloseDevice(); + if (ret != WPS_NFCLIB_ERR_SUCCESS) + wpa_printf(MSG_ERROR, "WPS (PN531): Failed to close " + "NFC Device: 0x%08x", ret); + + ret = WpsNfcDeinit(); + if (ret != WPS_NFCLIB_ERR_SUCCESS) + wpa_printf(MSG_ERROR, "WPS (PN531): Failed to deinitialize " + "NFC Library: 0x%08x", ret); +} + + +struct oob_nfc_device_data oob_nfc_pn531_device_data = { + .init_func = init_nfc_pn531, + .read_func = read_nfc_pn531, + .write_func = write_nfc_pn531, + .deinit_func = deinit_nfc_pn531, +}; diff --git a/contrib/wpa/src/wps/wps_registrar.c b/contrib/wpa/src/wps/wps_registrar.c index f34c9e9..81ddf3a 100644 --- a/contrib/wpa/src/wps/wps_registrar.c +++ b/contrib/wpa/src/wps/wps_registrar.c @@ -1,6 +1,6 @@ /* * Wi-Fi Protected Setup - Registrar - * Copyright (c) 2008, Jouni Malinen <j@w1.fi> + * Copyright (c) 2008-2009, Jouni Malinen <j@w1.fi> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -12,21 +12,25 @@ * See README and COPYING for more details. */ -#include "includes.h" +#include "utils/includes.h" -#include "common.h" -#include "sha256.h" -#include "base64.h" -#include "ieee802_11_defs.h" -#include "eloop.h" +#include "utils/common.h" +#include "utils/base64.h" +#include "utils/eloop.h" +#include "utils/uuid.h" +#include "utils/list.h" +#include "crypto/crypto.h" +#include "crypto/sha256.h" +#include "common/ieee802_11_defs.h" #include "wps_i.h" #include "wps_dev_attr.h" #include "wps_upnp.h" +#include "wps_upnp_i.h" #define WPS_WORKAROUNDS struct wps_uuid_pin { - struct wps_uuid_pin *next; + struct dl_list list; u8 uuid[WPS_UUID_LEN]; int wildcard_uuid; u8 *pin; @@ -45,16 +49,18 @@ static void wps_free_pin(struct wps_uuid_pin *pin) } -static void wps_free_pins(struct wps_uuid_pin *pins) +static void wps_remove_pin(struct wps_uuid_pin *pin) { - struct wps_uuid_pin *pin, *prev; + dl_list_del(&pin->list); + wps_free_pin(pin); +} - pin = pins; - while (pin) { - prev = pin; - pin = pin->next; - wps_free_pin(prev); - } + +static void wps_free_pins(struct dl_list *pins) +{ + struct wps_uuid_pin *pin, *prev; + dl_list_for_each_safe(pin, prev, pins, struct wps_uuid_pin, list) + wps_remove_pin(pin); } @@ -78,6 +84,13 @@ static void wps_free_pbc_sessions(struct wps_pbc_session *pbc) } +struct wps_registrar_device { + struct wps_registrar_device *next; + struct wps_device_data dev; + u8 uuid[WPS_UUID_LEN]; +}; + + struct wps_registrar { struct wps_context *wps; @@ -86,24 +99,33 @@ struct wps_registrar { int (*new_psk_cb)(void *ctx, const u8 *mac_addr, const u8 *psk, size_t psk_len); - int (*set_ie_cb)(void *ctx, const u8 *beacon_ie, size_t beacon_ie_len, - const u8 *probe_resp_ie, size_t probe_resp_ie_len); + int (*set_ie_cb)(void *ctx, struct wpabuf *beacon_ie, + struct wpabuf *probe_resp_ie); void (*pin_needed_cb)(void *ctx, const u8 *uuid_e, const struct wps_device_data *dev); void (*reg_success_cb)(void *ctx, const u8 *mac_addr, const u8 *uuid_e); + void (*set_sel_reg_cb)(void *ctx, int sel_reg, u16 dev_passwd_id, + u16 sel_reg_config_methods); + void (*enrollee_seen_cb)(void *ctx, const u8 *addr, const u8 *uuid_e, + const u8 *pri_dev_type, u16 config_methods, + u16 dev_password_id, u8 request_type, + const char *dev_name); void *cb_ctx; - struct wps_uuid_pin *pins; + struct dl_list pins; struct wps_pbc_session *pbc_sessions; int skip_cred_build; struct wpabuf *extra_cred; int disable_auto_conf; + int sel_reg_union; int sel_reg_dev_password_id_override; int sel_reg_config_methods_override; int static_wep_only; + struct wps_registrar_device *devices; + int force_pbc_overlap; }; @@ -114,6 +136,72 @@ static void wps_registrar_set_selected_timeout(void *eloop_ctx, void *timeout_ctx); +static void wps_free_devices(struct wps_registrar_device *dev) +{ + struct wps_registrar_device *prev; + + while (dev) { + prev = dev; + dev = dev->next; + wps_device_data_free(&prev->dev); + os_free(prev); + } +} + + +static struct wps_registrar_device * wps_device_get(struct wps_registrar *reg, + const u8 *addr) +{ + struct wps_registrar_device *dev; + + for (dev = reg->devices; dev; dev = dev->next) { + if (os_memcmp(dev->dev.mac_addr, addr, ETH_ALEN) == 0) + return dev; + } + return NULL; +} + + +static void wps_device_clone_data(struct wps_device_data *dst, + struct wps_device_data *src) +{ + os_memcpy(dst->mac_addr, src->mac_addr, ETH_ALEN); + os_memcpy(dst->pri_dev_type, src->pri_dev_type, WPS_DEV_TYPE_LEN); + +#define WPS_STRDUP(n) \ + os_free(dst->n); \ + dst->n = src->n ? os_strdup(src->n) : NULL + + WPS_STRDUP(device_name); + WPS_STRDUP(manufacturer); + WPS_STRDUP(model_name); + WPS_STRDUP(model_number); + WPS_STRDUP(serial_number); +#undef WPS_STRDUP +} + + +int wps_device_store(struct wps_registrar *reg, + struct wps_device_data *dev, const u8 *uuid) +{ + struct wps_registrar_device *d; + + d = wps_device_get(reg, dev->mac_addr); + if (d == NULL) { + d = os_zalloc(sizeof(*d)); + if (d == NULL) + return -1; + d->next = reg->devices; + reg->devices = d; + } + + wps_device_clone_data(&d->dev, dev); + os_memcpy(d->uuid, uuid, WPS_UUID_LEN); + + return 0; +} + + static void wps_registrar_add_pbc_session(struct wps_registrar *reg, const u8 *addr, const u8 *uuid_e) { @@ -264,7 +352,7 @@ static int wps_build_ap_setup_locked(struct wps_context *wps, static int wps_build_selected_registrar(struct wps_registrar *reg, struct wpabuf *msg) { - if (!reg->selected_registrar) + if (!reg->sel_reg_union) return 0; wpa_printf(MSG_DEBUG, "WPS: * Selected Registrar"); wpabuf_put_be16(msg, ATTR_SELECTED_REGISTRAR); @@ -278,7 +366,7 @@ static int wps_build_sel_reg_dev_password_id(struct wps_registrar *reg, struct wpabuf *msg) { u16 id = reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT; - if (!reg->selected_registrar) + if (!reg->sel_reg_union) return 0; if (reg->sel_reg_dev_password_id_override >= 0) id = reg->sel_reg_dev_password_id_override; @@ -294,7 +382,7 @@ static int wps_build_sel_reg_config_methods(struct wps_registrar *reg, struct wpabuf *msg) { u16 methods; - if (!reg->selected_registrar) + if (!reg->sel_reg_union) return 0; methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON; if (reg->pbc) @@ -314,7 +402,11 @@ static int wps_build_probe_config_methods(struct wps_registrar *reg, struct wpabuf *msg) { u16 methods; - methods = 0; + /* + * These are the methods that the AP supports as an Enrollee for adding + * external Registrars. + */ + methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON; wpa_printf(MSG_DEBUG, "WPS: * Config Methods (%x)", methods); wpabuf_put_be16(msg, ATTR_CONFIG_METHODS); wpabuf_put_be16(msg, 2); @@ -334,17 +426,6 @@ static int wps_build_config_methods_r(struct wps_registrar *reg, } -static int wps_build_resp_type(struct wps_registrar *reg, struct wpabuf *msg) -{ - u8 resp = reg->wps->ap ? WPS_RESP_AP : WPS_RESP_REGISTRAR; - wpa_printf(MSG_DEBUG, "WPS: * Response Type (%d)", resp); - wpabuf_put_be16(msg, ATTR_RESPONSE_TYPE); - wpabuf_put_be16(msg, 1); - wpabuf_put_u8(msg, resp); - return 0; -} - - /** * wps_registrar_init - Initialize WPS Registrar data * @wps: Pointer to longterm WPS context @@ -365,11 +446,14 @@ wps_registrar_init(struct wps_context *wps, if (reg == NULL) return NULL; + dl_list_init(®->pins); reg->wps = wps; reg->new_psk_cb = cfg->new_psk_cb; reg->set_ie_cb = cfg->set_ie_cb; reg->pin_needed_cb = cfg->pin_needed_cb; reg->reg_success_cb = cfg->reg_success_cb; + reg->set_sel_reg_cb = cfg->set_sel_reg_cb; + reg->enrollee_seen_cb = cfg->enrollee_seen_cb; reg->cb_ctx = cfg->cb_ctx; reg->skip_cred_build = cfg->skip_cred_build; if (cfg->extra_cred) { @@ -404,9 +488,10 @@ void wps_registrar_deinit(struct wps_registrar *reg) return; eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL); eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); - wps_free_pins(reg->pins); + wps_free_pins(®->pins); wps_free_pbc_sessions(reg->pbc_sessions); wpabuf_free(reg->extra_cred); + wps_free_devices(reg->devices); os_free(reg); } @@ -446,8 +531,7 @@ int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid, p->expiration.sec += timeout; } - p->next = reg->pins; - reg->pins = p; + dl_list_add(®->pins, &p->list); wpa_printf(MSG_DEBUG, "WPS: A new PIN configured (timeout=%d)", timeout); @@ -455,7 +539,7 @@ int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid, wpa_hexdump_ascii_key(MSG_DEBUG, "WPS: PIN", pin, pin_len); reg->selected_registrar = 1; reg->pbc = 0; - wps_set_ie(reg); + wps_registrar_selected_registrar_changed(reg); eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wps_registrar_set_selected_timeout, @@ -467,28 +551,18 @@ int wps_registrar_add_pin(struct wps_registrar *reg, const u8 *uuid, static void wps_registrar_expire_pins(struct wps_registrar *reg) { - struct wps_uuid_pin *pin, *prev, *del; + struct wps_uuid_pin *pin, *prev; struct os_time now; os_get_time(&now); - prev = NULL; - pin = reg->pins; - while (pin) { + dl_list_for_each_safe(pin, prev, ®->pins, struct wps_uuid_pin, list) + { if ((pin->flags & PIN_EXPIRES) && os_time_before(&pin->expiration, &now)) { - if (prev == NULL) - reg->pins = pin->next; - else - prev->next = pin->next; - del = pin; - pin = pin->next; wpa_hexdump(MSG_DEBUG, "WPS: Expired PIN for UUID", - del->uuid, WPS_UUID_LEN); - wps_free_pin(del); - continue; + pin->uuid, WPS_UUID_LEN); + wps_remove_pin(pin); } - prev = pin; - pin = pin->next; } } @@ -503,21 +577,14 @@ int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid) { struct wps_uuid_pin *pin, *prev; - prev = NULL; - pin = reg->pins; - while (pin) { + dl_list_for_each_safe(pin, prev, ®->pins, struct wps_uuid_pin, list) + { if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) { - if (prev == NULL) - reg->pins = pin->next; - else - prev->next = pin->next; wpa_hexdump(MSG_DEBUG, "WPS: Invalidated PIN for UUID", pin->uuid, WPS_UUID_LEN); - wps_free_pin(pin); + wps_remove_pin(pin); return 0; } - prev = pin; - pin = pin->next; } return -1; @@ -527,49 +594,48 @@ int wps_registrar_invalidate_pin(struct wps_registrar *reg, const u8 *uuid) static const u8 * wps_registrar_get_pin(struct wps_registrar *reg, const u8 *uuid, size_t *pin_len) { - struct wps_uuid_pin *pin; + struct wps_uuid_pin *pin, *found = NULL; wps_registrar_expire_pins(reg); - pin = reg->pins; - while (pin) { + dl_list_for_each(pin, ®->pins, struct wps_uuid_pin, list) { if (!pin->wildcard_uuid && - os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) + os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) { + found = pin; break; - pin = pin->next; + } } - if (!pin) { + if (!found) { /* Check for wildcard UUIDs since none of the UUID-specific * PINs matched */ - pin = reg->pins; - while (pin) { + dl_list_for_each(pin, ®->pins, struct wps_uuid_pin, list) { if (pin->wildcard_uuid == 1) { wpa_printf(MSG_DEBUG, "WPS: Found a wildcard " "PIN. Assigned it for this UUID-E"); pin->wildcard_uuid = 2; os_memcpy(pin->uuid, uuid, WPS_UUID_LEN); + found = pin; break; } - pin = pin->next; } } - if (!pin) + if (!found) return NULL; /* * Lock the PIN to avoid attacks based on concurrent re-use of the PIN * that could otherwise avoid PIN invalidations. */ - if (pin->flags & PIN_LOCKED) { + if (found->flags & PIN_LOCKED) { wpa_printf(MSG_DEBUG, "WPS: Selected PIN locked - do not " "allow concurrent re-use"); return NULL; } - *pin_len = pin->pin_len; - pin->flags |= PIN_LOCKED; - return pin->pin; + *pin_len = found->pin_len; + found->flags |= PIN_LOCKED; + return found->pin; } @@ -587,8 +653,7 @@ int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid) { struct wps_uuid_pin *pin; - pin = reg->pins; - while (pin) { + dl_list_for_each(pin, ®->pins, struct wps_uuid_pin, list) { if (os_memcmp(pin->uuid, uuid, WPS_UUID_LEN) == 0) { if (pin->wildcard_uuid == 2) { wpa_printf(MSG_DEBUG, "WPS: Invalidating used " @@ -598,7 +663,6 @@ int wps_registrar_unlock_pin(struct wps_registrar *reg, const u8 *uuid) pin->flags &= ~PIN_LOCKED; return 0; } - pin = pin->next; } return -1; @@ -609,7 +673,7 @@ static void wps_registrar_stop_pbc(struct wps_registrar *reg) { reg->selected_registrar = 0; reg->pbc = 0; - wps_set_ie(reg); + wps_registrar_selected_registrar_changed(reg); } @@ -644,7 +708,7 @@ int wps_registrar_button_pushed(struct wps_registrar *reg) reg->force_pbc_overlap = 0; reg->selected_registrar = 1; reg->pbc = 1; - wps_set_ie(reg); + wps_registrar_selected_registrar_changed(reg); eloop_cancel_timeout(wps_registrar_pbc_timeout, reg, NULL); eloop_register_timeout(WPS_PBC_WALK_TIME, 0, wps_registrar_pbc_timeout, @@ -660,12 +724,13 @@ static void wps_registrar_pbc_completed(struct wps_registrar *reg) wps_registrar_stop_pbc(reg); } + static void wps_registrar_pin_completed(struct wps_registrar *reg) { wpa_printf(MSG_DEBUG, "WPS: PIN completed using internal Registrar"); eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); reg->selected_registrar = 0; - wps_set_ie(reg); + wps_registrar_selected_registrar_changed(reg); } @@ -683,7 +748,6 @@ void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr, const struct wpabuf *wps_data) { struct wps_parse_attr attr; - u16 methods; wpa_hexdump_buf(MSG_MSGDUMP, "WPS: Probe Request with WPS data received", @@ -703,8 +767,31 @@ void wps_registrar_probe_req_rx(struct wps_registrar *reg, const u8 *addr, return; } - methods = WPA_GET_BE16(attr.config_methods); - if (!(methods & WPS_CONFIG_PUSHBUTTON)) + if (attr.dev_password_id == NULL) { + wpa_printf(MSG_DEBUG, "WPS: No Device Password Id attribute " + "in Probe Request"); + return; + } + + if (reg->enrollee_seen_cb && attr.uuid_e && + attr.primary_dev_type && attr.request_type) { + char *dev_name = NULL; + if (attr.dev_name) { + dev_name = os_zalloc(attr.dev_name_len + 1); + if (dev_name) { + os_memcpy(dev_name, attr.dev_name, + attr.dev_name_len); + } + } + reg->enrollee_seen_cb(reg->cb_ctx, addr, attr.uuid_e, + attr.primary_dev_type, + WPA_GET_BE16(attr.config_methods), + WPA_GET_BE16(attr.dev_password_id), + *attr.request_type, dev_name); + os_free(dev_name); + } + + if (WPA_GET_BE16(attr.dev_password_id) != DEV_PW_PUSHBUTTON) return; /* Not PBC */ wpa_printf(MSG_DEBUG, "WPS: Probe Request for PBC received from " @@ -754,17 +841,28 @@ static void wps_cb_reg_success(struct wps_registrar *reg, const u8 *mac_addr, } -static int wps_cb_set_ie(struct wps_registrar *reg, - const struct wpabuf *beacon_ie, - const struct wpabuf *probe_resp_ie) +static int wps_cb_set_ie(struct wps_registrar *reg, struct wpabuf *beacon_ie, + struct wpabuf *probe_resp_ie) { - if (reg->set_ie_cb == NULL) - return 0; + return reg->set_ie_cb(reg->cb_ctx, beacon_ie, probe_resp_ie); +} - return reg->set_ie_cb(reg->cb_ctx, wpabuf_head(beacon_ie), - wpabuf_len(beacon_ie), - wpabuf_head(probe_resp_ie), - wpabuf_len(probe_resp_ie)); + +static void wps_cb_set_sel_reg(struct wps_registrar *reg) +{ + u16 methods = 0; + if (reg->set_sel_reg_cb == NULL) + return; + + if (reg->selected_registrar) { + methods = reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON; + if (reg->pbc) + methods |= WPS_CONFIG_PUSHBUTTON; + } + + reg->set_sel_reg_cb(reg->cb_ctx, reg->selected_registrar, + reg->pbc ? DEV_PW_PUSHBUTTON : DEV_PW_DEFAULT, + methods); } @@ -804,7 +902,9 @@ static int wps_set_ie(struct wps_registrar *reg) { struct wpabuf *beacon; struct wpabuf *probe; - int ret; + + if (reg->set_ie_cb == NULL) + return 0; wpa_printf(MSG_DEBUG, "WPS: Build Beacon and Probe Response IEs"); @@ -829,7 +929,8 @@ static int wps_set_ie(struct wps_registrar *reg) wps_build_selected_registrar(reg, probe) || wps_build_sel_reg_dev_password_id(reg, probe) || wps_build_sel_reg_config_methods(reg, probe) || - wps_build_resp_type(reg, probe) || + wps_build_resp_type(probe, reg->wps->ap ? WPS_RESP_AP : + WPS_RESP_REGISTRAR) || wps_build_uuid_e(probe, reg->wps->uuid) || wps_build_device_attrs(®->wps->dev, probe) || wps_build_probe_config_methods(reg, probe) || @@ -870,11 +971,7 @@ static int wps_set_ie(struct wps_registrar *reg) wpabuf_put_data(probe, ms_wps, sizeof(ms_wps)); } - ret = wps_cb_set_ie(reg, beacon, probe); - wpabuf_free(beacon); - wpabuf_free(probe); - - return ret; + return wps_cb_set_ie(reg, beacon, probe); } @@ -992,7 +1089,7 @@ static int wps_build_r_snonce2(struct wps_data *wps, struct wpabuf *msg) static int wps_build_cred_network_idx(struct wpabuf *msg, - struct wps_credential *cred) + const struct wps_credential *cred) { wpa_printf(MSG_DEBUG, "WPS: * Network Index"); wpabuf_put_be16(msg, ATTR_NETWORK_INDEX); @@ -1003,7 +1100,7 @@ static int wps_build_cred_network_idx(struct wpabuf *msg, static int wps_build_cred_ssid(struct wpabuf *msg, - struct wps_credential *cred) + const struct wps_credential *cred) { wpa_printf(MSG_DEBUG, "WPS: * SSID"); wpabuf_put_be16(msg, ATTR_SSID); @@ -1014,7 +1111,7 @@ static int wps_build_cred_ssid(struct wpabuf *msg, static int wps_build_cred_auth_type(struct wpabuf *msg, - struct wps_credential *cred) + const struct wps_credential *cred) { wpa_printf(MSG_DEBUG, "WPS: * Authentication Type (0x%x)", cred->auth_type); @@ -1026,7 +1123,7 @@ static int wps_build_cred_auth_type(struct wpabuf *msg, static int wps_build_cred_encr_type(struct wpabuf *msg, - struct wps_credential *cred) + const struct wps_credential *cred) { wpa_printf(MSG_DEBUG, "WPS: * Encryption Type (0x%x)", cred->encr_type); @@ -1038,9 +1135,10 @@ static int wps_build_cred_encr_type(struct wpabuf *msg, static int wps_build_cred_network_key(struct wpabuf *msg, - struct wps_credential *cred) + const struct wps_credential *cred) { - wpa_printf(MSG_DEBUG, "WPS: * Network Key"); + wpa_printf(MSG_DEBUG, "WPS: * Network Key (len=%d)", + (int) cred->key_len); wpabuf_put_be16(msg, ATTR_NETWORK_KEY); wpabuf_put_be16(msg, cred->key_len); wpabuf_put_data(msg, cred->key, cred->key_len); @@ -1049,7 +1147,7 @@ static int wps_build_cred_network_key(struct wpabuf *msg, static int wps_build_cred_mac_addr(struct wpabuf *msg, - struct wps_credential *cred) + const struct wps_credential *cred) { wpa_printf(MSG_DEBUG, "WPS: * MAC Address (" MACSTR ")", MAC2STR(cred->mac_addr)); @@ -1061,7 +1159,7 @@ static int wps_build_cred_mac_addr(struct wpabuf *msg, static int wps_build_credential(struct wpabuf *msg, - struct wps_credential *cred) + const struct wps_credential *cred) { if (wps_build_cred_network_idx(msg, cred) || wps_build_cred_ssid(msg, cred) || @@ -1074,7 +1172,7 @@ static int wps_build_credential(struct wpabuf *msg, } -static int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) +int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) { struct wpabuf *cred; @@ -1082,6 +1180,10 @@ static int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) goto skip_cred_build; wpa_printf(MSG_DEBUG, "WPS: * Credential"); + if (wps->use_cred) { + os_memcpy(&wps->cred, wps->use_cred, sizeof(wps->cred)); + goto use_provided; + } os_memset(&wps->cred, 0, sizeof(wps->cred)); os_memcpy(wps->cred.ssid, wps->wps->ssid, wps->wps->ssid_len); @@ -1149,6 +1251,12 @@ static int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) wps->new_psk, wps->new_psk_len); os_memcpy(wps->cred.key, wps->new_psk, wps->new_psk_len); wps->cred.key_len = wps->new_psk_len; + } else if (wps->use_psk_key && wps->wps->psk_set) { + char hex[65]; + wpa_printf(MSG_DEBUG, "WPS: Use PSK format for Network Key"); + wpa_snprintf_hex(hex, sizeof(hex), wps->wps->psk, 32); + os_memcpy(wps->cred.key, hex, 32 * 2); + wps->cred.key_len = 32 * 2; } else if (wps->wps->network_key) { os_memcpy(wps->cred.key, wps->wps->network_key, wps->wps->network_key_len); @@ -1174,6 +1282,7 @@ static int wps_build_cred(struct wps_data *wps, struct wpabuf *msg) wps->cred.key_len = wps->new_psk_len * 2; } +use_provided: cred = wpabuf_alloc(200); if (cred == NULL) return -1; @@ -1246,6 +1355,7 @@ static struct wpabuf * wps_build_m2(struct wps_data *wps) return NULL; } + wps->int_reg = 1; wps->state = RECV_M3; return msg; } @@ -1379,8 +1489,8 @@ static struct wpabuf * wps_build_m8(struct wps_data *wps) if (wps_build_version(msg) || wps_build_msg_type(msg, WPS_M8) || wps_build_enrollee_nonce(wps, msg) || - (wps->wps->ap && wps_build_cred(wps, plain)) || - (!wps->wps->ap && wps_build_ap_settings(wps, plain)) || + ((wps->wps->ap || wps->er) && wps_build_cred(wps, plain)) || + (!wps->wps->ap && !wps->er && wps_build_ap_settings(wps, plain)) || wps_build_key_wrap_auth(wps, plain) || wps_build_encr_settings(wps, msg, plain) || wps_build_authenticator(wps, msg)) { @@ -1446,7 +1556,7 @@ struct wpabuf * wps_registrar_get_msg(struct wps_data *wps, struct wpabuf *msg; #ifdef CONFIG_WPS_UPNP - if (wps->wps->wps_upnp) { + if (!wps->int_reg && wps->wps->wps_upnp) { struct upnp_pending_message *p, *prev = NULL; if (wps->ext_reg > 1) wps_registrar_free_pending_m2(wps->wps); @@ -1732,6 +1842,22 @@ static int wps_process_pubkey(struct wps_data *wps, const u8 *pk, return -1; } +#ifdef CONFIG_WPS_OOB + if (wps->wps->oob_conf.pubkey_hash != NULL) { + const u8 *addr[1]; + u8 hash[WPS_HASH_LEN]; + + addr[0] = pk; + sha256_vector(1, addr, &pk_len, hash); + if (os_memcmp(hash, + wpabuf_head(wps->wps->oob_conf.pubkey_hash), + WPS_OOB_PUBKEY_HASH_LEN) != 0) { + wpa_printf(MSG_ERROR, "WPS: Public Key hash error"); + return -1; + } + } +#endif /* CONFIG_WPS_OOB */ + wpabuf_free(wps->dh_pubkey_e); wps->dh_pubkey_e = wpabuf_alloc_copy(pk, pk_len); if (wps->dh_pubkey_e == NULL) @@ -1847,7 +1973,28 @@ static int wps_process_config_methods(struct wps_data *wps, const u8 *methods) m = WPA_GET_BE16(methods); - wpa_printf(MSG_DEBUG, "WPS: Enrollee Config Methods 0x%x", m); + wpa_printf(MSG_DEBUG, "WPS: Enrollee Config Methods 0x%x" + "%s%s%s%s%s%s%s%s%s", m, + m & WPS_CONFIG_USBA ? " [USBA]" : "", + m & WPS_CONFIG_ETHERNET ? " [Ethernet]" : "", + m & WPS_CONFIG_LABEL ? " [Label]" : "", + m & WPS_CONFIG_DISPLAY ? " [Display]" : "", + m & WPS_CONFIG_EXT_NFC_TOKEN ? " [Ext NFC Token]" : "", + m & WPS_CONFIG_INT_NFC_TOKEN ? " [Int NFC Token]" : "", + m & WPS_CONFIG_NFC_INTERFACE ? " [NFC]" : "", + m & WPS_CONFIG_PUSHBUTTON ? " [PBC]" : "", + m & WPS_CONFIG_KEYPAD ? " [Keypad]" : ""); + + if (!(m & WPS_CONFIG_DISPLAY) && !wps->use_psk_key) { + /* + * The Enrollee does not have a display so it is unlikely to be + * able to show the passphrase to a user and as such, could + * benefit from receiving PSK to reduce key derivation time. + */ + wpa_printf(MSG_DEBUG, "WPS: Prefer PSK format key due to " + "Enrollee not supporting display"); + wps->use_psk_key = 1; + } return 0; } @@ -1928,7 +2075,8 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps, wps_process_os_version(&wps->peer_dev, attr->os_version)) return WPS_FAILURE; - if (wps->dev_pw_id != DEV_PW_DEFAULT && + if (wps->dev_pw_id < 0x10 && + wps->dev_pw_id != DEV_PW_DEFAULT && wps->dev_pw_id != DEV_PW_USER_SPECIFIED && wps->dev_pw_id != DEV_PW_MACHINE_SPECIFIED && wps->dev_pw_id != DEV_PW_REGISTRAR_SPECIFIED && @@ -1940,6 +2088,16 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps, return WPS_CONTINUE; } +#ifdef CONFIG_WPS_OOB + if (wps->dev_pw_id >= 0x10 && + wps->dev_pw_id != wps->wps->oob_dev_pw_id) { + wpa_printf(MSG_DEBUG, "WPS: OOB Device Password ID " + "%d mismatch", wps->dev_pw_id); + wps->state = SEND_M2D; + return WPS_CONTINUE; + } +#endif /* CONFIG_WPS_OOB */ + if (wps->dev_pw_id == DEV_PW_PUSHBUTTON) { if (wps->wps->registrar->force_pbc_overlap || wps_registrar_pbc_overlap(wps->wps->registrar, @@ -1957,6 +2115,23 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps, wps->pbc = 1; } +#ifdef WPS_WORKAROUNDS + /* + * It looks like Mac OS X 10.6.3 and 10.6.4 do not like Network Key in + * passphrase format. To avoid interop issues, force PSK format to be + * used. + */ + if (!wps->use_psk_key && + wps->peer_dev.manufacturer && + os_strncmp(wps->peer_dev.manufacturer, "Apple ", 6) == 0 && + wps->peer_dev.model_name && + os_strcmp(wps->peer_dev.model_name, "AirPort") == 0) { + wpa_printf(MSG_DEBUG, "WPS: Workaround - Force Network Key in " + "PSK format"); + wps->use_psk_key = 1; + } +#endif /* WPS_WORKAROUNDS */ + wps->state = SEND_M2; return WPS_CONTINUE; } @@ -2073,10 +2248,23 @@ static void wps_sta_cred_cb(struct wps_data *wps) } +static void wps_cred_update(struct wps_credential *dst, + struct wps_credential *src) +{ + os_memcpy(dst->ssid, src->ssid, sizeof(dst->ssid)); + dst->ssid_len = src->ssid_len; + dst->auth_type = src->auth_type; + dst->encr_type = src->encr_type; + dst->key_idx = src->key_idx; + os_memcpy(dst->key, src->key, sizeof(dst->key)); + dst->key_len = src->key_len; +} + + static int wps_process_ap_settings_r(struct wps_data *wps, struct wps_parse_attr *attr) { - if (wps->wps->ap) + if (wps->wps->ap || wps->er) return 0; /* AP Settings Attributes in M7 when Enrollee is an AP */ @@ -2085,21 +2273,24 @@ static int wps_process_ap_settings_r(struct wps_data *wps, wpa_printf(MSG_INFO, "WPS: Received old AP configuration from AP"); -#if 0 - /* - * TODO: Provide access to AP settings and allow changes before sending - * out M8. For now, just copy the settings unchanged into M8. - */ - - return 0; -#else - /* - * For now, use the AP PIN only to receive the current AP settings, - * not to reconfigure the AP. - */ - wps_sta_cred_cb(wps); - return 1; -#endif + if (wps->new_ap_settings) { + wpa_printf(MSG_INFO, "WPS: Update AP configuration based on " + "new settings"); + wps_cred_update(&wps->cred, wps->new_ap_settings); + return 0; + } else { + /* + * Use the AP PIN only to receive the current AP settings, not + * to reconfigure the AP. + */ + if (wps->ap_settings_cb) { + wps->ap_settings_cb(wps->ap_settings_cb_ctx, + &wps->cred); + return 1; + } + wps_sta_cred_cb(wps); + return 1; + } } @@ -2136,7 +2327,7 @@ static enum wps_process_res wps_process_m7(struct wps_data *wps, decrypted = wps_decrypt_encr_settings(wps, attr->encr_settings, attr->encr_settings_len); if (decrypted == NULL) { - wpa_printf(MSG_DEBUG, "WPS: Failed to decrypted Encrypted " + wpa_printf(MSG_DEBUG, "WPS: Failed to decrypt Encrypted " "Settings attribute"); wps->state = SEND_WSC_NACK; return WPS_CONTINUE; @@ -2430,6 +2621,8 @@ static enum wps_process_res wps_process_wsc_done(struct wps_data *wps, if (wps->wps->wps_upnp && wps->ext_reg) { wpa_printf(MSG_DEBUG, "WPS: Negotiation using external " "Registrar completed successfully"); + wps_device_store(wps->wps->registrar, &wps->peer_dev, + wps->uuid_e); return WPS_DONE; } #endif /* CONFIG_WPS_UPNP */ @@ -2448,6 +2641,8 @@ static enum wps_process_res wps_process_wsc_done(struct wps_data *wps, } wpa_printf(MSG_DEBUG, "WPS: Negotiation completed successfully"); + wps_device_store(wps->wps->registrar, &wps->peer_dev, + wps->uuid_e); if (wps->wps->wps_state == WPS_STATE_NOT_CONFIGURED && wps->new_psk && wps->wps->ap && !wps->wps->registrar->disable_auto_conf) { @@ -2475,7 +2670,7 @@ static enum wps_process_res wps_process_wsc_done(struct wps_data *wps, wps->new_psk = NULL; } - if (!wps->wps->ap) + if (!wps->wps->ap && !wps->er) wps_sta_cred_cb(wps); if (wps->new_psk) { @@ -2580,61 +2775,126 @@ static void wps_registrar_set_selected_timeout(void *eloop_ctx, { struct wps_registrar *reg = eloop_ctx; - wpa_printf(MSG_DEBUG, "WPS: SetSelectedRegistrar timed out - " - "unselect Registrar"); + wpa_printf(MSG_DEBUG, "WPS: Selected Registrar timeout - " + "unselect internal Registrar"); reg->selected_registrar = 0; reg->pbc = 0; - reg->sel_reg_dev_password_id_override = -1; - reg->sel_reg_config_methods_override = -1; - wps_set_ie(reg); + wps_registrar_selected_registrar_changed(reg); +} + + +#ifdef CONFIG_WPS_UPNP +static void wps_registrar_sel_reg_add(struct wps_registrar *reg, + struct subscription *s) +{ + wpa_printf(MSG_DEBUG, "WPS: External Registrar selected (dev_pw_id=%d " + "config_methods=0x%x)", + s->dev_password_id, s->config_methods); + reg->sel_reg_union = 1; + if (reg->sel_reg_dev_password_id_override != DEV_PW_PUSHBUTTON) + reg->sel_reg_dev_password_id_override = s->dev_password_id; + if (reg->sel_reg_config_methods_override == -1) + reg->sel_reg_config_methods_override = 0; + reg->sel_reg_config_methods_override |= s->config_methods; +} +#endif /* CONFIG_WPS_UPNP */ + + +static void wps_registrar_sel_reg_union(struct wps_registrar *reg) +{ +#ifdef CONFIG_WPS_UPNP + struct subscription *s; + + if (reg->wps->wps_upnp == NULL) + return; + + dl_list_for_each(s, ®->wps->wps_upnp->subscriptions, + struct subscription, list) { + struct subscr_addr *sa; + sa = dl_list_first(&s->addr_list, struct subscr_addr, list); + if (sa) { + wpa_printf(MSG_DEBUG, "WPS: External Registrar %s:%d", + inet_ntoa(sa->saddr.sin_addr), + ntohs(sa->saddr.sin_port)); + } + if (s->selected_registrar) + wps_registrar_sel_reg_add(reg, s); + else + wpa_printf(MSG_DEBUG, "WPS: External Registrar not " + "selected"); + } +#endif /* CONFIG_WPS_UPNP */ } /** - * wps_registrar_set_selected_registrar - Notification of SetSelectedRegistrar + * wps_registrar_selected_registrar_changed - SetSelectedRegistrar change * @reg: Registrar data from wps_registrar_init() - * @msg: Received message from SetSelectedRegistrar - * Returns: 0 on success, -1 on failure * - * This function is called when an AP receives a SetSelectedRegistrar UPnP - * message. + * This function is called when selected registrar state changes, e.g., when an + * AP receives a SetSelectedRegistrar UPnP message. */ -int wps_registrar_set_selected_registrar(struct wps_registrar *reg, - const struct wpabuf *msg) +void wps_registrar_selected_registrar_changed(struct wps_registrar *reg) { - struct wps_parse_attr attr; - - wpa_hexdump_buf(MSG_MSGDUMP, "WPS: SetSelectedRegistrar attributes", - msg); + wpa_printf(MSG_DEBUG, "WPS: Selected registrar information changed"); - if (wps_parse_msg(msg, &attr) < 0) - return -1; - if (!wps_version_supported(attr.version)) { - wpa_printf(MSG_DEBUG, "WPS: Unsupported SetSelectedRegistrar " - "version 0x%x", attr.version ? *attr.version : 0); - return -1; - } + reg->sel_reg_union = reg->selected_registrar; + reg->sel_reg_dev_password_id_override = -1; + reg->sel_reg_config_methods_override = -1; + if (reg->selected_registrar) { + reg->sel_reg_config_methods_override = + reg->wps->config_methods & ~WPS_CONFIG_PUSHBUTTON; + if (reg->pbc) { + reg->sel_reg_dev_password_id_override = + DEV_PW_PUSHBUTTON; + reg->sel_reg_config_methods_override |= + WPS_CONFIG_PUSHBUTTON; + } + wpa_printf(MSG_DEBUG, "WPS: Internal Registrar selected " + "(pbc=%d)", reg->pbc); + } else + wpa_printf(MSG_DEBUG, "WPS: Internal Registrar not selected"); - if (attr.selected_registrar == NULL || - *attr.selected_registrar == 0) { - wpa_printf(MSG_DEBUG, "WPS: SetSelectedRegistrar: Disable " - "Selected Registrar"); - eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, - NULL); - wps_registrar_set_selected_timeout(reg, NULL); - return 0; - } + wps_registrar_sel_reg_union(reg); - reg->selected_registrar = 1; - reg->sel_reg_dev_password_id_override = attr.dev_password_id ? - WPA_GET_BE16(attr.dev_password_id) : DEV_PW_DEFAULT; - reg->sel_reg_config_methods_override = attr.sel_reg_config_methods ? - WPA_GET_BE16(attr.sel_reg_config_methods) : -1; wps_set_ie(reg); + wps_cb_set_sel_reg(reg); +} - eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL); - eloop_register_timeout(WPS_PBC_WALK_TIME, 0, - wps_registrar_set_selected_timeout, - reg, NULL); - return 0; + +int wps_registrar_get_info(struct wps_registrar *reg, const u8 *addr, + char *buf, size_t buflen) +{ + struct wps_registrar_device *d; + int len = 0, ret; + char uuid[40]; + char devtype[WPS_DEV_TYPE_BUFSIZE]; + + d = wps_device_get(reg, addr); + if (d == NULL) + return 0; + if (uuid_bin2str(d->uuid, uuid, sizeof(uuid))) + return 0; + + ret = os_snprintf(buf + len, buflen - len, + "wpsUuid=%s\n" + "wpsPrimaryDeviceType=%s\n" + "wpsDeviceName=%s\n" + "wpsManufacturer=%s\n" + "wpsModelName=%s\n" + "wpsModelNumber=%s\n" + "wpsSerialNumber=%s\n", + uuid, + wps_dev_type_bin2str(d->dev.pri_dev_type, devtype, + sizeof(devtype)), + d->dev.device_name ? d->dev.device_name : "", + d->dev.manufacturer ? d->dev.manufacturer : "", + d->dev.model_name ? d->dev.model_name : "", + d->dev.model_number ? d->dev.model_number : "", + d->dev.serial_number ? d->dev.serial_number : ""); + if (ret < 0 || (size_t) ret >= buflen - len) + return len; + len += ret; + + return len; } diff --git a/contrib/wpa/src/wps/wps_ufd.c b/contrib/wpa/src/wps/wps_ufd.c new file mode 100644 index 0000000..1a911e1 --- /dev/null +++ b/contrib/wpa/src/wps/wps_ufd.c @@ -0,0 +1,235 @@ +/* + * UFD routines for Wi-Fi Protected Setup + * Copyright (c) 2009, Masashi Honma <honma@ictec.co.jp> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" +#include "common.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <fcntl.h> +#include <dirent.h> + +#include "wps/wps.h" +#include "wps/wps_i.h" + +#ifdef CONFIG_NATIVE_WINDOWS +#define UFD_DIR1 "%s\\SMRTNTKY" +#define UFD_DIR2 UFD_DIR1 "\\WFAWSC" +#define UFD_FILE UFD_DIR2 "\\%s" +#else /* CONFIG_NATIVE_WINDOWS */ +#define UFD_DIR1 "%s/SMRTNTKY" +#define UFD_DIR2 UFD_DIR1 "/WFAWSC" +#define UFD_FILE UFD_DIR2 "/%s" +#endif /* CONFIG_NATIVE_WINDOWS */ + + +struct wps_ufd_data { + int ufd_fd; +}; + + +static int dev_pwd_e_file_filter(const struct dirent *entry) +{ + unsigned int prefix; + char ext[5]; + + if (sscanf(entry->d_name, "%8x.%4s", &prefix, ext) != 2) + return 0; + if (prefix == 0) + return 0; + if (os_strcasecmp(ext, "WFA") != 0) + return 0; + + return 1; +} + + +static int wps_get_dev_pwd_e_file_name(char *path, char *file_name) +{ + struct dirent **namelist; + int i, file_num; + + file_num = scandir(path, &namelist, &dev_pwd_e_file_filter, + alphasort); + if (file_num < 0) { + wpa_printf(MSG_ERROR, "WPS: OOB file not found: %d (%s)", + errno, strerror(errno)); + return -1; + } + if (file_num == 0) { + wpa_printf(MSG_ERROR, "WPS: OOB file not found"); + os_free(namelist); + return -1; + } + os_strlcpy(file_name, namelist[0]->d_name, 13); + for (i = 0; i < file_num; i++) + os_free(namelist[i]); + os_free(namelist); + return 0; +} + + +static int get_file_name(struct wps_context *wps, int registrar, + const char *path, char *file_name) +{ + switch (wps->oob_conf.oob_method) { + case OOB_METHOD_CRED: + os_snprintf(file_name, 13, "00000000.WSC"); + break; + case OOB_METHOD_DEV_PWD_E: + if (registrar) { + char temp[128]; + os_snprintf(temp, sizeof(temp), UFD_DIR2, path); + if (wps_get_dev_pwd_e_file_name(temp, file_name) < 0) + return -1; + } else { + u8 *mac_addr = wps->dev.mac_addr; + + os_snprintf(file_name, 13, "%02X%02X%02X%02X.WFA", + mac_addr[2], mac_addr[3], mac_addr[4], + mac_addr[5]); + } + break; + case OOB_METHOD_DEV_PWD_R: + os_snprintf(file_name, 13, "00000000.WFA"); + break; + default: + wpa_printf(MSG_ERROR, "WPS: Invalid USBA OOB method"); + return -1; + } + return 0; +} + + +static int ufd_mkdir(const char *path) +{ + if (mkdir(path, S_IRWXU) < 0 && errno != EEXIST) { + wpa_printf(MSG_ERROR, "WPS (UFD): Failed to create directory " + "'%s': %d (%s)", path, errno, strerror(errno)); + return -1; + } + return 0; +} + + +static void * init_ufd(struct wps_context *wps, + struct oob_device_data *oob_dev, int registrar) +{ + int write_f; + char temp[128]; + char *path = oob_dev->device_path; + char filename[13]; + struct wps_ufd_data *data; + int ufd_fd; + + if (path == NULL) + return NULL; + + write_f = wps->oob_conf.oob_method == OOB_METHOD_DEV_PWD_E ? + !registrar : registrar; + + if (get_file_name(wps, registrar, path, filename) < 0) { + wpa_printf(MSG_ERROR, "WPS (UFD): Failed to get file name"); + return NULL; + } + + if (write_f) { + os_snprintf(temp, sizeof(temp), UFD_DIR1, path); + if (ufd_mkdir(temp)) + return NULL; + os_snprintf(temp, sizeof(temp), UFD_DIR2, path); + if (ufd_mkdir(temp)) + return NULL; + } + + os_snprintf(temp, sizeof(temp), UFD_FILE, path, filename); + if (write_f) + ufd_fd = open(temp, O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR); + else + ufd_fd = open(temp, O_RDONLY); + if (ufd_fd < 0) { + wpa_printf(MSG_ERROR, "WPS (UFD): Failed to open %s: %s", + temp, strerror(errno)); + return NULL; + } + + data = os_zalloc(sizeof(*data)); + if (data == NULL) + return NULL; + data->ufd_fd = ufd_fd; + return data; +} + + +static struct wpabuf * read_ufd(void *priv) +{ + struct wps_ufd_data *data = priv; + struct wpabuf *buf; + struct stat s; + size_t file_size; + + if (fstat(data->ufd_fd, &s) < 0) { + wpa_printf(MSG_ERROR, "WPS (UFD): Failed to get file size"); + return NULL; + } + + file_size = s.st_size; + buf = wpabuf_alloc(file_size); + if (buf == NULL) { + wpa_printf(MSG_ERROR, "WPS (UFD): Failed to alloc read " + "buffer"); + return NULL; + } + + if (read(data->ufd_fd, wpabuf_mhead(buf), file_size) != + (int) file_size) { + wpabuf_free(buf); + wpa_printf(MSG_ERROR, "WPS (UFD): Failed to read"); + return NULL; + } + wpabuf_put(buf, file_size); + return buf; +} + + +static int write_ufd(void *priv, struct wpabuf *buf) +{ + struct wps_ufd_data *data = priv; + + if (write(data->ufd_fd, wpabuf_mhead(buf), wpabuf_len(buf)) != + (int) wpabuf_len(buf)) { + wpa_printf(MSG_ERROR, "WPS (UFD): Failed to write"); + return -1; + } + return 0; +} + + +static void deinit_ufd(void *priv) +{ + struct wps_ufd_data *data = priv; + close(data->ufd_fd); + os_free(data); +} + + +struct oob_device_data oob_ufd_device_data = { + .device_name = NULL, + .device_path = NULL, + .init_func = init_ufd, + .read_func = read_ufd, + .write_func = write_ufd, + .deinit_func = deinit_ufd, +}; diff --git a/contrib/wpa/src/wps/wps_upnp.c b/contrib/wpa/src/wps/wps_upnp.c index 4c6aac2..f99b859 100644 --- a/contrib/wpa/src/wps/wps_upnp.c +++ b/contrib/wpa/src/wps/wps_upnp.c @@ -280,53 +280,18 @@ static void subscr_addr_delete(struct subscr_addr *a) } -/* subscr_addr_unlink -- unlink subscriber address from linked list */ -static void subscr_addr_unlink(struct subscription *s, struct subscr_addr *a) -{ - struct subscr_addr **listp = &s->addr_list; - s->n_addr--; - a->next->prev = a->prev; - a->prev->next = a->next; - if (*listp == a) { - if (a == a->next) { - /* last in queue */ - *listp = NULL; - assert(s->n_addr == 0); - } else { - *listp = a->next; - } - } -} - - /* subscr_addr_free_all -- unlink and delete list of subscriber addresses. */ static void subscr_addr_free_all(struct subscription *s) { - struct subscr_addr **listp = &s->addr_list; - struct subscr_addr *a; - while ((a = *listp) != NULL) { - subscr_addr_unlink(s, a); + struct subscr_addr *a, *tmp; + dl_list_for_each_safe(a, tmp, &s->addr_list, struct subscr_addr, list) + { + dl_list_del(&a->list); subscr_addr_delete(a); } } -/* subscr_addr_link -- add subscriber address to list of addresses */ -static void subscr_addr_link(struct subscription *s, struct subscr_addr *a) -{ - struct subscr_addr **listp = &s->addr_list; - s->n_addr++; - if (*listp == NULL) { - *listp = a->next = a->prev = a; - } else { - a->next = *listp; - a->prev = (*listp)->prev; - a->prev->next = a; - a->next->prev = a; - } -} - - /* subscr_addr_add_url -- add address(es) for one url to subscription */ static void subscr_addr_add_url(struct subscription *s, const char *url) { @@ -403,7 +368,7 @@ static void subscr_addr_add_url(struct subscription *s, const char *url) } for (rp = result; rp; rp = rp->ai_next) { /* Limit no. of address to avoid denial of service attack */ - if (s->n_addr >= MAX_ADDR_PER_SUBSCRIPTION) { + if (dl_list_len(&s->addr_list) >= MAX_ADDR_PER_SUBSCRIPTION) { wpa_printf(MSG_INFO, "WPS UPnP: subscr_addr_add_url: " "Ignoring excessive addresses"); break; @@ -412,7 +377,6 @@ static void subscr_addr_add_url(struct subscription *s, const char *url) a = os_zalloc(sizeof(*a) + alloc_len); if (a == NULL) continue; - a->s = s; mem = (void *) (a + 1); a->domain_and_port = mem; strcpy(mem, domain_and_port); @@ -425,7 +389,7 @@ static void subscr_addr_add_url(struct subscription *s, const char *url) os_memcpy(&a->saddr, rp->ai_addr, sizeof(a->saddr)); a->saddr.sin_port = htons(port); - subscr_addr_link(s, a); + dl_list_add(&s->addr_list, &a->list); a = NULL; /* don't free it below */ } @@ -502,14 +466,14 @@ static void upnp_wps_device_send_event(struct upnp_wps_device_sm *sm) /* Enqueue event message for all subscribers */ struct wpabuf *buf; /* holds event message */ int buf_size = 0; - struct subscription *s; + struct subscription *s, *tmp; /* Actually, utf-8 is the default, but it doesn't hurt to specify it */ const char *format_head = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" "<e:propertyset xmlns:e=\"urn:schemas-upnp-org:event-1-0\">\n"; const char *format_tail = "</e:propertyset>\n"; - if (sm->subscriptions == NULL) { + if (dl_list_empty(&sm->subscriptions)) { /* optimize */ return; } @@ -531,19 +495,15 @@ static void upnp_wps_device_send_event(struct upnp_wps_device_sm *sm) wpa_printf(MSG_MSGDUMP, "WPS UPnP: WLANEvent message:\n%s", (char *) wpabuf_head(buf)); - s = sm->subscriptions; - do { + dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription, + list) { if (event_add(s, buf)) { - struct subscription *s_old = s; wpa_printf(MSG_INFO, "WPS UPnP: Dropping " "subscriber due to event backlog"); - s = s_old->next; - subscription_unlink(s_old); - subscription_destroy(s_old); - } else { - s = s->next; + dl_list_del(&s->list); + subscription_destroy(s); } - } while (s != sm->subscriptions); + } wpabuf_free(buf); } @@ -555,52 +515,15 @@ static void upnp_wps_device_send_event(struct upnp_wps_device_sm *sm) * This is the result of an incoming HTTP over TCP SUBSCRIBE request. */ -/* subscription_unlink -- remove from the active list */ -void subscription_unlink(struct subscription *s) -{ - struct upnp_wps_device_sm *sm = s->sm; - - if (s->next == s) { - /* only one? */ - sm->subscriptions = NULL; - } else { - if (sm->subscriptions == s) - sm->subscriptions = s->next; - s->next->prev = s->prev; - s->prev->next = s->next; - } - sm->n_subscriptions--; -} - - -/* subscription_link_to_end -- link to end of active list - * (should have high expiry time!) - */ -static void subscription_link_to_end(struct subscription *s) -{ - struct upnp_wps_device_sm *sm = s->sm; - - if (sm->subscriptions) { - s->next = sm->subscriptions; - s->prev = s->next->prev; - s->prev->next = s; - s->next->prev = s; - } else { - sm->subscriptions = s->next = s->prev = s; - } - sm->n_subscriptions++; -} - - /* subscription_destroy -- destroy an unlinked subscription * Be sure to unlink first if necessary. */ void subscription_destroy(struct subscription *s) { wpa_printf(MSG_DEBUG, "WPS UPnP: Destroy subscription %p", s); - if (s->addr_list) - subscr_addr_free_all(s); + subscr_addr_free_all(s); event_delete_all(s); + upnp_er_remove_notification(s); os_free(s); } @@ -608,10 +531,13 @@ void subscription_destroy(struct subscription *s) /* subscription_list_age -- remove expired subscriptions */ static void subscription_list_age(struct upnp_wps_device_sm *sm, time_t now) { - struct subscription *s; - while ((s = sm->subscriptions) != NULL && s->timeout_time < now) { + struct subscription *s, *tmp; + dl_list_for_each_safe(s, tmp, &sm->subscriptions, + struct subscription, list) { + if (s->timeout_time > now) + break; wpa_printf(MSG_DEBUG, "WPS UPnP: Removing aged subscription"); - subscription_unlink(s); + dl_list_del(&s->list); subscription_destroy(s); } } @@ -623,17 +549,11 @@ static void subscription_list_age(struct upnp_wps_device_sm *sm, time_t now) struct subscription * subscription_find(struct upnp_wps_device_sm *sm, const u8 uuid[UUID_LEN]) { - struct subscription *s0 = sm->subscriptions; - struct subscription *s = s0; - - if (s0 == NULL) - return NULL; - do { + struct subscription *s; + dl_list_for_each(s, &sm->subscriptions, struct subscription, list) { if (os_memcmp(s->uuid, uuid, UUID_LEN) == 0) return s; /* Found match */ - s = s->next; - } while (s != s0); - + } return NULL; } @@ -645,8 +565,11 @@ static struct wpabuf * build_fake_wsc_ack(void) return NULL; wpabuf_put_u8(msg, UPNP_WPS_WLANEVENT_TYPE_EAP); wpabuf_put_str(msg, "00:00:00:00:00:00"); - wps_build_version(msg); - wps_build_msg_type(msg, WPS_WSC_ACK); + if (wps_build_version(msg) || + wps_build_msg_type(msg, WPS_WSC_ACK)) { + wpabuf_free(msg); + return NULL; + } /* Enrollee Nonce */ wpabuf_put_be16(msg, ATTR_ENROLLEE_NONCE); wpabuf_put_be16(msg, WPS_NONCE_LEN); @@ -750,31 +673,34 @@ struct subscription * subscription_start(struct upnp_wps_device_sm *sm, subscription_list_age(sm, now); /* If too many subscriptions, remove oldest */ - if (sm->n_subscriptions >= MAX_SUBSCRIPTIONS) { - s = sm->subscriptions; + if (dl_list_len(&sm->subscriptions) >= MAX_SUBSCRIPTIONS) { + s = dl_list_first(&sm->subscriptions, struct subscription, + list); wpa_printf(MSG_INFO, "WPS UPnP: Too many subscriptions, " "trashing oldest"); - subscription_unlink(s); + dl_list_del(&s->list); subscription_destroy(s); } s = os_zalloc(sizeof(*s)); if (s == NULL) return NULL; + dl_list_init(&s->addr_list); + dl_list_init(&s->event_queue); s->sm = sm; s->timeout_time = expire; uuid_make(s->uuid); subscr_addr_list_create(s, callback_urls); /* Add to end of list, since it has the highest expiration time */ - subscription_link_to_end(s); + dl_list_add_tail(&sm->subscriptions, &s->list); /* Queue up immediate event message (our last event) * as required by UPnP spec. */ if (subscription_first_event(s)) { wpa_printf(MSG_INFO, "WPS UPnP: Dropping subscriber due to " "event backlog"); - subscription_unlink(s); + dl_list_del(&s->list); subscription_destroy(s); return NULL; } @@ -796,10 +722,10 @@ struct subscription * subscription_renew(struct upnp_wps_device_sm *sm, if (s == NULL) return NULL; wpa_printf(MSG_DEBUG, "WPS UPnP: Subscription renewed"); - subscription_unlink(s); + dl_list_del(&s->list); s->timeout_time = expire; /* add back to end of list, since it now has highest expiry */ - subscription_link_to_end(s); + dl_list_add_tail(&sm->subscriptions, &s->list); return s; } @@ -871,7 +797,7 @@ fail: } -#ifdef __FreeBSD__ +#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) #include <sys/sysctl.h> #include <net/route.h> #include <net/if_dl.h> @@ -921,12 +847,10 @@ static int eth_get(const char *device, u8 ea[ETH_ALEN]) * @ip_addr: Buffer for returning IP address in network byte order * @ip_addr_text: Buffer for returning a pointer to allocated IP address text * @mac: Buffer for returning MAC address - * @mac_addr_text: Buffer for returning allocated MAC address text * Returns: 0 on success, -1 on failure */ -static int get_netif_info(const char *net_if, unsigned *ip_addr, - char **ip_addr_text, u8 mac[ETH_ALEN], - char **mac_addr_text) +int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text, + u8 mac[ETH_ALEN]) { struct ifreq req; int sock = -1; @@ -934,8 +858,7 @@ static int get_netif_info(const char *net_if, unsigned *ip_addr, struct in_addr in_addr; *ip_addr_text = os_zalloc(16); - *mac_addr_text = os_zalloc(18); - if (*ip_addr_text == NULL || *mac_addr_text == NULL) + if (*ip_addr_text == NULL) goto fail; sock = socket(AF_INET, SOCK_DGRAM, 0); @@ -961,7 +884,7 @@ static int get_netif_info(const char *net_if, unsigned *ip_addr, goto fail; } os_memcpy(mac, req.ifr_addr.sa_data, 6); -#elif defined(__FreeBSD__) +#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) if (eth_get(net_if, mac) < 0) { wpa_printf(MSG_ERROR, "WPS UPnP: Failed to get MAC address"); goto fail; @@ -969,7 +892,6 @@ static int get_netif_info(const char *net_if, unsigned *ip_addr, #else #error MAC address fetch not implemented #endif - os_snprintf(*mac_addr_text, 18, MACSTR, MAC2STR(req.ifr_addr.sa_data)); close(sock); return 0; @@ -979,12 +901,29 @@ fail: close(sock); os_free(*ip_addr_text); *ip_addr_text = NULL; - os_free(*mac_addr_text); - *mac_addr_text = NULL; return -1; } +static void upnp_wps_free_msearchreply(struct dl_list *head) +{ + struct advertisement_state_machine *a, *tmp; + dl_list_for_each_safe(a, tmp, head, struct advertisement_state_machine, + list) + msearchreply_state_machine_stop(a); +} + + +static void upnp_wps_free_subscriptions(struct dl_list *head) +{ + struct subscription *s, *tmp; + dl_list_for_each_safe(s, tmp, head, struct subscription, list) { + dl_list_del(&s->list); + subscription_destroy(s); + } +} + + /** * upnp_wps_device_stop - Stop WPS UPnP operations on an interface * @sm: WPS UPnP state machine from upnp_wps_device_init() @@ -996,25 +935,14 @@ void upnp_wps_device_stop(struct upnp_wps_device_sm *sm) wpa_printf(MSG_DEBUG, "WPS UPnP: Stop device"); web_listener_stop(sm); - while (sm->web_connections) - web_connection_stop(sm->web_connections); - while (sm->msearch_replies) - msearchreply_state_machine_stop(sm->msearch_replies); - while (sm->subscriptions) { - struct subscription *s = sm->subscriptions; - subscription_unlink(s); - subscription_destroy(s); - } + upnp_wps_free_msearchreply(&sm->msearch_replies); + upnp_wps_free_subscriptions(&sm->subscriptions); advertisement_state_machine_stop(sm, 1); event_send_stop_all(sm); os_free(sm->wlanevent); sm->wlanevent = NULL; - os_free(sm->net_if); - sm->net_if = NULL; - os_free(sm->mac_addr_text); - sm->mac_addr_text = NULL; os_free(sm->ip_addr_text); sm->ip_addr_text = NULL; if (sm->multicast_sd >= 0) @@ -1040,7 +968,6 @@ int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if) if (sm->started) upnp_wps_device_stop(sm); - sm->net_if = strdup(net_if); sm->multicast_sd = -1; sm->ssdp_sd = -1; sm->started = 1; @@ -1051,9 +978,8 @@ int upnp_wps_device_start(struct upnp_wps_device_sm *sm, char *net_if) goto fail; /* Determine which IP and mac address we're using */ - if (get_netif_info(net_if, - &sm->ip_addr, &sm->ip_addr_text, - sm->mac_addr, &sm->mac_addr_text)) { + if (get_netif_info(net_if, &sm->ip_addr, &sm->ip_addr_text, + sm->mac_addr)) { wpa_printf(MSG_INFO, "WPS UPnP: Could not get IP/MAC address " "for %s. Does it have IP address?", net_if); goto fail; @@ -1104,6 +1030,7 @@ void upnp_wps_device_deinit(struct upnp_wps_device_sm *sm) wps_deinit(sm->peer.wps); os_free(sm->root_dir); os_free(sm->desc_url); + os_free(sm->ctx->ap_pin); os_free(sm->ctx); os_free(sm); } @@ -1131,6 +1058,8 @@ upnp_wps_device_init(struct upnp_wps_device_ctx *ctx, struct wps_context *wps, sm->ctx = ctx; sm->wps = wps; sm->priv = priv; + dl_list_init(&sm->msearch_replies); + dl_list_init(&sm->subscriptions); return sm; } @@ -1143,5 +1072,22 @@ upnp_wps_device_init(struct upnp_wps_device_ctx *ctx, struct wps_context *wps, */ int upnp_wps_subscribers(struct upnp_wps_device_sm *sm) { - return sm->subscriptions != NULL; + return !dl_list_empty(&sm->subscriptions); +} + + +int upnp_wps_set_ap_pin(struct upnp_wps_device_sm *sm, const char *ap_pin) +{ + if (sm == NULL) + return 0; + + os_free(sm->ctx->ap_pin); + if (ap_pin) { + sm->ctx->ap_pin = os_strdup(ap_pin); + if (sm->ctx->ap_pin == NULL) + return -1; + } else + sm->ctx->ap_pin = NULL; + + return 0; } diff --git a/contrib/wpa/src/wps/wps_upnp.h b/contrib/wpa/src/wps/wps_upnp.h index 31b0556..06bc31f 100644 --- a/contrib/wpa/src/wps/wps_upnp.h +++ b/contrib/wpa/src/wps/wps_upnp.h @@ -25,29 +25,12 @@ enum upnp_wps_wlanevent_type { }; struct upnp_wps_device_ctx { - struct wpabuf * (*rx_req_get_device_info)( - void *priv, struct upnp_wps_peer *peer); - struct wpabuf * (*rx_req_put_message)( - void *priv, struct upnp_wps_peer *peer, - const struct wpabuf *msg); - struct wpabuf * (*rx_req_get_ap_settings)(void *priv, - const struct wpabuf *msg); - int (*rx_req_set_ap_settings)(void *priv, const struct wpabuf *msg); - int (*rx_req_del_ap_settings)(void *priv, const struct wpabuf *msg); - struct wpabuf * (*rx_req_get_sta_settings)(void *priv, - const struct wpabuf *msg); - int (*rx_req_set_sta_settings)(void *priv, const struct wpabuf *msg); - int (*rx_req_del_sta_settings)(void *priv, const struct wpabuf *msg); int (*rx_req_put_wlan_response)( void *priv, enum upnp_wps_wlanevent_type ev_type, const u8 *mac_addr, const struct wpabuf *msg, enum wps_msg_type msg_type); - int (*rx_req_set_selected_registrar)(void *priv, - const struct wpabuf *msg); - int (*rx_req_reboot_ap)(void *priv, const struct wpabuf *msg); - int (*rx_req_reset_ap)(void *priv, const struct wpabuf *msg); - int (*rx_req_reboot_sta)(void *priv, const struct wpabuf *msg); - int (*rx_req_reset_sta)(void *priv, const struct wpabuf *msg); + + char *ap_pin; }; struct upnp_wps_device_sm * @@ -63,5 +46,6 @@ int upnp_wps_device_send_wlan_event(struct upnp_wps_device_sm *sm, enum upnp_wps_wlanevent_type ev_type, const struct wpabuf *msg); int upnp_wps_subscribers(struct upnp_wps_device_sm *sm); +int upnp_wps_set_ap_pin(struct upnp_wps_device_sm *sm, const char *ap_pin); #endif /* WPS_UPNP_H */ diff --git a/contrib/wpa/src/wps/wps_upnp_ap.c b/contrib/wpa/src/wps/wps_upnp_ap.c new file mode 100644 index 0000000..93746da --- /dev/null +++ b/contrib/wpa/src/wps/wps_upnp_ap.c @@ -0,0 +1,80 @@ +/* + * Wi-Fi Protected Setup - UPnP AP functionality + * Copyright (c) 2009, Jouni Malinen <j@w1.fi> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Alternatively, this software may be distributed under the terms of BSD + * license. + * + * See README and COPYING for more details. + */ + +#include "includes.h" + +#include "common.h" +#include "eloop.h" +#include "uuid.h" +#include "wps_i.h" +#include "wps_upnp.h" +#include "wps_upnp_i.h" + + +static void upnp_er_set_selected_timeout(void *eloop_ctx, void *timeout_ctx) +{ + struct subscription *s = eloop_ctx; + wpa_printf(MSG_DEBUG, "WPS: SetSelectedRegistrar from ER timed out"); + s->selected_registrar = 0; + wps_registrar_selected_registrar_changed(s->reg); +} + + +int upnp_er_set_selected_registrar(struct wps_registrar *reg, + struct subscription *s, + const struct wpabuf *msg) +{ + struct wps_parse_attr attr; + + wpa_hexdump_buf(MSG_MSGDUMP, "WPS: SetSelectedRegistrar attributes", + msg); + + if (wps_parse_msg(msg, &attr) < 0) + return -1; + if (!wps_version_supported(attr.version)) { + wpa_printf(MSG_DEBUG, "WPS: Unsupported SetSelectedRegistrar " + "version 0x%x", attr.version ? *attr.version : 0); + return -1; + } + + s->reg = reg; + eloop_cancel_timeout(upnp_er_set_selected_timeout, s, NULL); + + if (attr.selected_registrar == NULL || *attr.selected_registrar == 0) { + wpa_printf(MSG_DEBUG, "WPS: SetSelectedRegistrar: Disable " + "Selected Registrar"); + s->selected_registrar = 0; + } else { + s->selected_registrar = 1; + s->dev_password_id = attr.dev_password_id ? + WPA_GET_BE16(attr.dev_password_id) : DEV_PW_DEFAULT; + s->config_methods = attr.sel_reg_config_methods ? + WPA_GET_BE16(attr.sel_reg_config_methods) : -1; + eloop_register_timeout(WPS_PBC_WALK_TIME, 0, + upnp_er_set_selected_timeout, s, NULL); + } + + wps_registrar_selected_registrar_changed(reg); + + return 0; +} + + +void upnp_er_remove_notification(struct subscription *s) +{ + s->selected_registrar = 0; + eloop_cancel_timeout(upnp_er_set_selected_timeout, s, NULL); + if (s->reg) + wps_registrar_selected_registrar_changed(s->reg); +} diff --git a/contrib/wpa/src/wps/wps_upnp_event.c b/contrib/wpa/src/wps/wps_upnp_event.c index 4122a87..ae5efdb 100644 --- a/contrib/wpa/src/wps/wps_upnp_event.c +++ b/contrib/wpa/src/wps/wps_upnp_event.c @@ -10,12 +10,11 @@ #include "includes.h" #include <assert.h> -#include <fcntl.h> #include "common.h" #include "eloop.h" #include "uuid.h" -#include "httpread.h" +#include "http_client.h" #include "wps_defs.h" #include "wps_upnp.h" #include "wps_upnp_i.h" @@ -47,42 +46,25 @@ * TODO: As an optimization we could share data between subscribers. */ struct wps_event_ { - struct wps_event_ *next; - struct wps_event_ *prev; /* double linked list */ + struct dl_list list; struct subscription *s; /* parent */ unsigned subscriber_sequence; /* which event for this subscription*/ - int retry; /* which retry */ + unsigned int retry; /* which retry */ struct subscr_addr *addr; /* address to connect to */ struct wpabuf *data; /* event data to send */ - /* The following apply while we are sending an event message. */ - int sd; /* -1 or socket descriptor for open connection */ - int sd_registered; /* nonzero if we must cancel registration */ - struct httpread *hread; /* NULL or open connection for event msg */ + struct http_client *http_event; }; -static void event_timeout_handler(void *eloop_data, void *user_ctx); - /* event_clean -- clean sockets etc. of event * Leaves data, retry count etc. alone. */ static void event_clean(struct wps_event_ *e) { - if (e->s->current_event == e) { - eloop_cancel_timeout(event_timeout_handler, NULL, e); + if (e->s->current_event == e) e->s->current_event = NULL; - } - if (e->sd_registered) { - eloop_unregister_sock(e->sd, EVENT_TYPE_WRITE); - e->sd_registered = 0; - } - if (e->sd != -1) { - close(e->sd); - e->sd = -1; - } - if (e->hread) - httpread_destroy(e->hread); - e->hread = NULL; + http_client_free(e->http_event); + e->http_event = NULL; } @@ -102,63 +84,14 @@ static void event_delete(struct wps_event_ *e) */ static struct wps_event_ *event_dequeue(struct subscription *s) { - struct wps_event_ **event_head = &s->event_queue; - struct wps_event_ *e = *event_head; - if (e == NULL) - return NULL; - e->next->prev = e->prev; - e->prev->next = e->next; - if (*event_head == e) { - if (e == e->next) { - /* last in queue */ - *event_head = NULL; - } else { - *event_head = e->next; - } - } - s->n_queue--; - e->next = e->prev = NULL; - /* but parent "s" is still valid */ + struct wps_event_ *e; + e = dl_list_first(&s->event_queue, struct wps_event_, list); + if (e) + dl_list_del(&e->list); return e; } -/* event_enqueue_at_end -- add event to end of queue */ -static void event_enqueue_at_end(struct subscription *s, struct wps_event_ *e) -{ - struct wps_event_ **event_head = &s->event_queue; - if (*event_head == NULL) { - *event_head = e->next = e->prev = e; - } else { - e->next = *event_head; - e->prev = e->next->prev; - e->prev->next = e; - e->next->prev = e; - } - s->n_queue++; -} - - -/* event_enqueue_at_begin -- add event to begin of queue - * (appropriate for retrying event only) - */ -static void event_enqueue_at_begin(struct subscription *s, - struct wps_event_ *e) -{ - struct wps_event_ **event_head = &s->event_queue; - if (*event_head == NULL) { - *event_head = e->next = e->prev = e; - } else { - e->prev = *event_head; - e->next = e->prev->next; - e->prev->next = e; - e->next->prev = e; - *event_head = e; - } - s->n_queue++; -} - - /* event_delete_all -- delete entire event queue and current event */ void event_delete_all(struct subscription *s) { @@ -187,114 +120,24 @@ static void event_retry(struct wps_event_ *e, int do_next_address) if (do_next_address) e->retry++; - if (e->retry >= s->n_addr) { + if (e->retry >= dl_list_len(&s->addr_list)) { wpa_printf(MSG_DEBUG, "WPS UPnP: Giving up on sending event " "for %s", e->addr->domain_and_port); return; } - event_enqueue_at_begin(s, e); + dl_list_add(&s->event_queue, &e->list); event_send_all_later(sm); } -/* called if the overall event-sending process takes too long */ -static void event_timeout_handler(void *eloop_data, void *user_ctx) +static struct wpabuf * event_build_message(struct wps_event_ *e) { - struct wps_event_ *e = user_ctx; - struct subscription *s = e->s; - - assert(e == s->current_event); - - wpa_printf(MSG_DEBUG, "WPS UPnP: Event send timeout"); - event_retry(e, 1); -} - - -/* event_got_response_handler -- called back when http response is received. */ -static void event_got_response_handler(struct httpread *handle, void *cookie, - enum httpread_event en) -{ - struct wps_event_ *e = cookie; - struct subscription *s = e->s; - struct upnp_wps_device_sm *sm = s->sm; - struct httpread *hread = e->hread; - int reply_code = 0; - - assert(e == s->current_event); - eloop_cancel_timeout(event_timeout_handler, NULL, e); - - if (en == HTTPREAD_EVENT_FILE_READY) { - if (httpread_hdr_type_get(hread) == HTTPREAD_HDR_TYPE_REPLY) { - reply_code = httpread_reply_code_get(hread); - if (reply_code == HTTP_OK) { - wpa_printf(MSG_DEBUG, - "WPS UPnP: Got event reply OK from " - "%s", e->addr->domain_and_port); - event_delete(e); - goto send_more; - } else { - wpa_printf(MSG_DEBUG, "WPS UPnP: Got event " - "error reply code %d from %s", - reply_code, - e->addr->domain_and_port); - goto bad; - } - } else { - wpa_printf(MSG_DEBUG, "WPS UPnP: Got bogus event " - "response %d from %s", en, - e->addr->domain_and_port); - } - } else { - wpa_printf(MSG_DEBUG, "WPS UPnP: Event response timeout/fail " - "for %s", e->addr->domain_and_port); - goto bad; - } - event_retry(e, 1); - goto send_more; - -send_more: - /* Schedule sending more if there is more to send */ - if (s->event_queue) - event_send_all_later(sm); - return; - -bad: - /* - * If other side doesn't like what we say, forget about them. - * (There is no way to tell other side that we are dropping - * them...). - * Alternately, we could just do event_delete(e) - */ - wpa_printf(MSG_DEBUG, "WPS UPnP: Deleting subscription due to errors"); - subscription_unlink(s); - subscription_destroy(s); -} - - -/* event_send_tx_ready -- actually write event message - * - * Prequisite: subscription socket descriptor has become ready to - * write (because connection to subscriber has been made). - * - * It is also possible that we are called because the connect has failed; - * it is possible to test for this, or we can just go ahead and then - * the write will fail. - */ -static void event_send_tx_ready(int sock, void *eloop_ctx, void *sock_ctx) -{ - struct wps_event_ *e = sock_ctx; - struct subscription *s = e->s; struct wpabuf *buf; char *b; - assert(e == s->current_event); - assert(e->sd == sock); - buf = wpabuf_alloc(1000 + wpabuf_len(e->data)); - if (buf == NULL) { - event_retry(e, 0); - goto bad; - } + if (buf == NULL) + return NULL; wpabuf_printf(buf, "NOTIFY %s HTTP/1.1\r\n", e->addr->path); wpabuf_put_str(buf, "SERVER: Unspecified, UPnP/1.0, Unspecified\r\n"); wpabuf_printf(buf, "HOST: %s\r\n", e->addr->domain_and_port); @@ -303,7 +146,7 @@ static void event_send_tx_ready(int sock, void *eloop_ctx, void *sock_ctx) "NTS: upnp:propchange\r\n"); wpabuf_put_str(buf, "SID: uuid:"); b = wpabuf_put(buf, 0); - uuid_bin2str(s->uuid, b, 80); + uuid_bin2str(e->s->uuid, b, 80); wpabuf_put(buf, os_strlen(b)); wpabuf_put_str(buf, "\r\n"); wpabuf_printf(buf, "SEQ: %u\r\n", e->subscriber_sequence); @@ -311,45 +154,47 @@ static void event_send_tx_ready(int sock, void *eloop_ctx, void *sock_ctx) (int) wpabuf_len(e->data)); wpabuf_put_str(buf, "\r\n"); /* terminating empty line */ wpabuf_put_buf(buf, e->data); + return buf; +} - /* Since the message size is pretty small, we should be - * able to get the operating system to buffer what we give it - * and not have to come back again later to write more... - */ -#if 0 - /* we could: Turn blocking back on? */ - fcntl(e->sd, F_SETFL, 0); -#endif - wpa_printf(MSG_DEBUG, "WPS UPnP: Sending event to %s", - e->addr->domain_and_port); - if (send_wpabuf(e->sd, buf) < 0) { - event_retry(e, 1); - goto bad; - } - wpabuf_free(buf); - buf = NULL; - if (e->sd_registered) { - e->sd_registered = 0; - eloop_unregister_sock(e->sd, EVENT_TYPE_WRITE); - } - /* Set up to read the reply */ - e->hread = httpread_create(e->sd, event_got_response_handler, - e /* cookie */, - 0 /* no data expected */, - EVENT_TIMEOUT_SEC); - if (e->hread == NULL) { - wpa_printf(MSG_ERROR, "WPS UPnP: httpread_create failed"); - event_retry(e, 0); - goto bad; - } - return; +static void event_http_cb(void *ctx, struct http_client *c, + enum http_client_event event) +{ + struct wps_event_ *e = ctx; + struct subscription *s = e->s; -bad: - /* Schedule sending more if there is more to send */ - if (s->event_queue) - event_send_all_later(s->sm); - wpabuf_free(buf); + switch (event) { + case HTTP_CLIENT_OK: + wpa_printf(MSG_DEBUG, + "WPS UPnP: Got event reply OK from " + "%s", e->addr->domain_and_port); + event_delete(e); + + /* Schedule sending more if there is more to send */ + if (!dl_list_empty(&s->event_queue)) + event_send_all_later(s->sm); + break; + case HTTP_CLIENT_FAILED: + case HTTP_CLIENT_INVALID_REPLY: + wpa_printf(MSG_DEBUG, "WPS UPnP: Failed to send event to %s", + e->addr->domain_and_port); + + /* + * If other side doesn't like what we say, forget about them. + * (There is no way to tell other side that we are dropping + * them...). + * Alternately, we could just do event_delete(e) + */ + wpa_printf(MSG_DEBUG, "WPS UPnP: Deleting subscription due to " + "errors"); + dl_list_del(&s->list); + subscription_destroy(s); + break; + case HTTP_CLIENT_TIMEOUT: + wpa_printf(MSG_DEBUG, "WPS UPnP: Event send timeout"); + event_retry(e, 1); + } } @@ -376,57 +221,41 @@ bad: static int event_send_start(struct subscription *s) { struct wps_event_ *e; - int itry; + unsigned int itry; + struct wpabuf *buf; /* * Assume we are called ONLY with no current event and ONLY with * nonempty event queue and ONLY with at least one address to send to. */ - assert(s->addr_list != NULL); + assert(!dl_list_empty(&s->addr_list)); assert(s->current_event == NULL); - assert(s->event_queue != NULL); + assert(!dl_list_empty(&s->event_queue)); s->current_event = e = event_dequeue(s); - /* Use address acc. to no. of retries */ - e->addr = s->addr_list; - for (itry = 0; itry < e->retry; itry++) - e->addr = e->addr->next; - - e->sd = socket(AF_INET, SOCK_STREAM, 0); - if (e->sd < 0) { - event_retry(e, 0); + /* Use address according to number of retries */ + itry = 0; + dl_list_for_each(e->addr, &s->addr_list, struct subscr_addr, list) + if (itry++ == e->retry) + break; + if (itry < e->retry) return -1; - } - /* set non-blocking so we don't sleep waiting for connection */ - if (fcntl(e->sd, F_SETFL, O_NONBLOCK) != 0) { - event_retry(e, 0); - return -1; - } - /* - * Start the connect. It might succeed immediately but more likely will - * return errno EINPROGRESS. - */ - if (connect(e->sd, (struct sockaddr *) &e->addr->saddr, - sizeof(e->addr->saddr))) { - if (errno != EINPROGRESS) { - event_retry(e, 1); - return -1; - } - } - /* Call back when ready for writing (or on failure...). */ - if (eloop_register_sock(e->sd, EVENT_TYPE_WRITE, event_send_tx_ready, - NULL, e)) { + + buf = event_build_message(e); + if (buf == NULL) { event_retry(e, 0); return -1; } - e->sd_registered = 1; - /* Don't wait forever! */ - if (eloop_register_timeout(EVENT_TIMEOUT_SEC, 0, event_timeout_handler, - NULL, e)) { + + e->http_event = http_client_addr(&e->addr->saddr, buf, 0, + event_http_cb, e); + if (e->http_event == NULL) { + wpabuf_free(buf); event_retry(e, 0); return -1; } + return 0; } @@ -435,32 +264,26 @@ static int event_send_start(struct subscription *s) static void event_send_all_later_handler(void *eloop_data, void *user_ctx) { struct upnp_wps_device_sm *sm = user_ctx; - struct subscription *s; - struct subscription *s_old; + struct subscription *s, *tmp; int nerrors = 0; sm->event_send_all_queued = 0; - s = sm->subscriptions; - if (s == NULL) - return; - do { - if (s->addr_list == NULL) { + dl_list_for_each_safe(s, tmp, &sm->subscriptions, struct subscription, + list) { + if (dl_list_empty(&s->addr_list)) { /* if we've given up on all addresses */ wpa_printf(MSG_DEBUG, "WPS UPnP: Removing " "subscription with no addresses"); - s_old = s; - s = s_old->next; - subscription_unlink(s_old); - subscription_destroy(s_old); + dl_list_del(&s->list); + subscription_destroy(s); } else { if (s->current_event == NULL /* not busy */ && - s->event_queue != NULL /* more to do */) { + !dl_list_empty(&s->event_queue) /* more to do */) { if (event_send_start(s)) nerrors++; } - s = s->next; } - } while (sm->subscriptions != NULL && s != sm->subscriptions); + } if (nerrors) { /* Try again later */ @@ -509,7 +332,7 @@ int event_add(struct subscription *s, const struct wpabuf *data) { struct wps_event_ *e; - if (s->n_queue >= MAX_EVENTS_QUEUED) { + if (dl_list_len(&s->event_queue) >= MAX_EVENTS_QUEUED) { wpa_printf(MSG_DEBUG, "WPS UPnP: Too many events queued for " "subscriber"); return 1; @@ -518,8 +341,8 @@ int event_add(struct subscription *s, const struct wpabuf *data) e = os_zalloc(sizeof(*e)); if (e == NULL) return 1; + dl_list_init(&e->list); e->s = s; - e->sd = -1; e->data = wpabuf_dup(data); if (e->data == NULL) { os_free(e); @@ -528,7 +351,7 @@ int event_add(struct subscription *s, const struct wpabuf *data) e->subscriber_sequence = s->next_subscriber_sequence++; if (s->next_subscriber_sequence == 0) s->next_subscriber_sequence++; - event_enqueue_at_end(s, e); + dl_list_add_tail(&s->event_queue, &e->list); event_send_all_later(s->sm); return 0; } diff --git a/contrib/wpa/src/wps/wps_upnp_i.h b/contrib/wpa/src/wps/wps_upnp_i.h index ba4ec20..b31875a 100644 --- a/contrib/wpa/src/wps/wps_upnp_i.h +++ b/contrib/wpa/src/wps/wps_upnp_i.h @@ -11,6 +11,9 @@ #ifndef WPS_UPNP_I_H #define WPS_UPNP_I_H +#include "utils/list.h" +#include "http.h" + #define UPNP_MULTICAST_ADDRESS "239.255.255.250" /* for UPnP multicasting */ #define UPNP_MULTICAST_PORT 1900 /* UDP port to monitor for UPnP */ @@ -25,25 +28,11 @@ #define UPNP_WPS_DEVICE_CONTROL_FILE "wps_control" #define UPNP_WPS_DEVICE_EVENT_FILE "wps_event" - -struct web_connection; -struct subscription; -struct upnp_wps_device_sm; +#define MULTICAST_MAX_READ 1600 /* max bytes we'll read for UPD request */ -enum http_reply_code { - HTTP_OK = 200, - HTTP_BAD_REQUEST = 400, - UPNP_INVALID_ACTION = 401, - UPNP_INVALID_ARGS = 402, - HTTP_PRECONDITION_FAILED = 412, - HTTP_INTERNAL_SERVER_ERROR = 500, - HTTP_UNIMPLEMENTED = 501, - UPNP_ACTION_FAILED = 501, - UPNP_ARG_VALUE_INVALID = 600, - UPNP_ARG_VALUE_OUT_OF_RANGE = 601, - UPNP_OUT_OF_MEMORY = 603 -}; +struct upnp_wps_device_sm; +struct wps_registrar; enum advertisement_type_enum { @@ -60,10 +49,7 @@ enum advertisement_type_enum { * separate packets and spread out in time to avoid congestion. */ struct advertisement_state_machine { - /* double-linked list */ - struct advertisement_state_machine *next; - struct advertisement_state_machine *prev; - struct upnp_wps_device_sm *sm; /* parent */ + struct dl_list list; enum advertisement_type_enum type; int state; int nerrors; @@ -77,10 +63,7 @@ struct advertisement_state_machine { * for a subscriber until we find one that seems to work. */ struct subscr_addr { - /* double linked list */ - struct subscr_addr *next; - struct subscr_addr *prev; - struct subscription *s; /* parent */ + struct dl_list list; char *domain_and_port; /* domain and port part of url */ char *path; /* "filepath" part of url (from "mem") */ struct sockaddr_in saddr; /* address for doing connect */ @@ -93,9 +76,7 @@ struct subscr_addr { * also have to age out subscribers unless they renew. */ struct subscription { - /* double linked list */ - struct subscription *next; - struct subscription *prev; + struct dl_list list; struct upnp_wps_device_sm *sm; /* parent */ time_t timeout_time; /* when to age out the subscription */ unsigned next_subscriber_sequence; /* number our messages */ @@ -106,12 +87,16 @@ struct subscription { */ u8 uuid[UUID_LEN]; /* Linked list of address alternatives (rotate through on failure) */ - struct subscr_addr *addr_list; - int n_addr; /* Number of addresses in list */ - struct wps_event_ *event_queue; /* Queued event messages. */ - int n_queue; /* How many events are queued */ + struct dl_list addr_list; + struct dl_list event_queue; /* Queued event messages. */ struct wps_event_ *current_event; /* non-NULL if being sent (not in q) */ + + /* Information from SetSelectedRegistrar action */ + u8 selected_registrar; + u16 dev_password_id; + u16 config_methods; + struct wps_registrar *reg; }; @@ -128,8 +113,6 @@ struct upnp_wps_device_sm { char *root_dir; char *desc_url; int started; /* nonzero if we are active */ - char *net_if; /* network interface we use */ - char *mac_addr_text; /* mac addr of network i.f. we use */ u8 mac_addr[ETH_ALEN]; /* mac addr of network i.f. we use */ char *ip_addr_text; /* IP address of network i.f. we use */ unsigned ip_addr; /* IP address of network i.f. we use (host order) */ @@ -138,16 +121,11 @@ struct upnp_wps_device_sm { int ssdp_sd_registered; /* nonzero if we must unregister */ unsigned advertise_count; /* how many advertisements done */ struct advertisement_state_machine advertisement; - struct advertisement_state_machine *msearch_replies; - int n_msearch_replies; /* no. of pending M-SEARCH replies */ + struct dl_list msearch_replies; int web_port; /* our port that others get xml files from */ - int web_sd; /* socket to listen for web requests */ - int web_sd_registered; /* nonzero if we must cancel registration */ - struct web_connection *web_connections; /* linked list */ - int n_web_connections; /* no. of pending web connections */ + struct http_server *web_srv; /* Note: subscriptions are kept in expiry order */ - struct subscription *subscriptions; /* linked list */ - int n_subscriptions; /* no of current subscriptions */ + struct dl_list subscriptions; int event_send_all_queued; /* if we are scheduled to send events soon */ @@ -163,11 +141,12 @@ struct subscription * subscription_start(struct upnp_wps_device_sm *sm, const char *callback_urls); struct subscription * subscription_renew(struct upnp_wps_device_sm *sm, const u8 uuid[UUID_LEN]); -void subscription_unlink(struct subscription *s); void subscription_destroy(struct subscription *s); struct subscription * subscription_find(struct upnp_wps_device_sm *sm, const u8 uuid[UUID_LEN]); int send_wpabuf(int fd, struct wpabuf *buf); +int get_netif_info(const char *net_if, unsigned *ip_addr, char **ip_addr_text, + u8 mac[ETH_ALEN]); /* wps_upnp_ssdp.c */ void msearchreply_state_machine_stop(struct advertisement_state_machine *a); @@ -176,11 +155,12 @@ void advertisement_state_machine_stop(struct upnp_wps_device_sm *sm, int send_byebye); void ssdp_listener_stop(struct upnp_wps_device_sm *sm); int ssdp_listener_start(struct upnp_wps_device_sm *sm); -int add_ssdp_network(char *net_if); +int ssdp_listener_open(void); +int add_ssdp_network(const char *net_if); +int ssdp_open_multicast_sock(u32 ip_addr); int ssdp_open_multicast(struct upnp_wps_device_sm *sm); /* wps_upnp_web.c */ -void web_connection_stop(struct web_connection *c); int web_listener_start(struct upnp_wps_device_sm *sm); void web_listener_stop(struct upnp_wps_device_sm *sm); @@ -190,4 +170,10 @@ void event_delete_all(struct subscription *s); void event_send_all_later(struct upnp_wps_device_sm *sm); void event_send_stop_all(struct upnp_wps_device_sm *sm); +/* wps_upnp_ap.c */ +int upnp_er_set_selected_registrar(struct wps_registrar *reg, + struct subscription *s, + const struct wpabuf *msg); +void upnp_er_remove_notification(struct subscription *s); + #endif /* WPS_UPNP_I_H */ diff --git a/contrib/wpa/src/wps/wps_upnp_ssdp.c b/contrib/wpa/src/wps/wps_upnp_ssdp.c index c1dc99d..8505d05 100644 --- a/contrib/wpa/src/wps/wps_upnp_ssdp.c +++ b/contrib/wpa/src/wps/wps_upnp_ssdp.c @@ -24,7 +24,6 @@ #define UPNP_CACHE_SEC (UPNP_CACHE_SEC_MIN + 1) /* cache time we use */ #define UPNP_CACHE_SEC_MIN 1800 /* min cachable time per UPnP standard */ #define UPNP_ADVERTISE_REPEAT 2 /* no more than 3 */ -#define MULTICAST_MAX_READ 1600 /* max bytes we'll read for UPD request */ #define MAX_MSEARCH 20 /* max simultaneous M-SEARCH replies ongoing */ #define SSDP_TARGET "239.0.0.0" #define SSDP_NETMASK "255.0.0.0" @@ -131,14 +130,15 @@ static int str_starts(const char *str, const char *start) * Note: next_advertisement is shared code with msearchreply_* functions */ static struct wpabuf * -next_advertisement(struct advertisement_state_machine *a, int *islast) +next_advertisement(struct upnp_wps_device_sm *sm, + struct advertisement_state_machine *a, int *islast) { struct wpabuf *msg; char *NTString = ""; char uuid_string[80]; *islast = 0; - uuid_bin2str(a->sm->wps->uuid, uuid_string, sizeof(uuid_string)); + uuid_bin2str(sm->wps->uuid, uuid_string, sizeof(uuid_string)); msg = wpabuf_alloc(800); /* more than big enough */ if (msg == NULL) goto fail; @@ -172,7 +172,7 @@ next_advertisement(struct advertisement_state_machine *a, int *islast) if (a->type != ADVERTISE_DOWN) { /* Where others may get our XML files from */ wpabuf_printf(msg, "LOCATION: http://%s:%d/%s\r\n", - a->sm->ip_addr_text, a->sm->web_port, + sm->ip_addr_text, sm->web_port, UPNP_WPS_DEVICE_XML_FILE); } @@ -245,7 +245,6 @@ void advertisement_state_machine_stop(struct upnp_wps_device_sm *sm, a->type = ADVERTISE_DOWN; a->state = 0; - a->sm = sm; os_memset(&dest, 0, sizeof(dest)); dest.sin_family = AF_INET; @@ -253,7 +252,7 @@ void advertisement_state_machine_stop(struct upnp_wps_device_sm *sm, dest.sin_port = htons(UPNP_MULTICAST_PORT); while (!islast) { - msg = next_advertisement(a, &islast); + msg = next_advertisement(sm, a, &islast); if (msg == NULL) break; if (sendto(sm->multicast_sd, wpabuf_head(msg), wpabuf_len(msg), @@ -292,7 +291,7 @@ static void advertisement_state_machine_handler(void *eloop_data, */ wpa_printf(MSG_MSGDUMP, "WPS UPnP: Advertisement state=%d", a->state); - msg = next_advertisement(a, &islast); + msg = next_advertisement(sm, a, &islast); if (msg == NULL) return; @@ -357,7 +356,6 @@ int advertisement_state_machine_start(struct upnp_wps_device_sm *sm) */ a->type = ADVERTISE_DOWN; a->state = 0; - a->sm = sm; /* (other fields not used here) */ /* First timeout should be random interval < 100 msec */ @@ -376,28 +374,15 @@ int advertisement_state_machine_start(struct upnp_wps_device_sm *sm) * They are sent in response to a UDP M-SEARCH packet. **************************************************************************/ -static void msearchreply_state_machine_handler(void *eloop_data, - void *user_ctx); - - /** * msearchreply_state_machine_stop - Stop M-SEARCH reply state machine * @a: Selected advertisement/reply state */ void msearchreply_state_machine_stop(struct advertisement_state_machine *a) { - struct upnp_wps_device_sm *sm = a->sm; wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH stop"); - if (a->next == a) { - sm->msearch_replies = NULL; - } else { - if (sm->msearch_replies == a) - sm->msearch_replies = a->next; - a->next->prev = a->prev; - a->prev->next = a->next; - } + dl_list_del(&a->list); os_free(a); - sm->n_msearch_replies--; } @@ -405,7 +390,7 @@ static void msearchreply_state_machine_handler(void *eloop_data, void *user_ctx) { struct advertisement_state_machine *a = user_ctx; - struct upnp_wps_device_sm *sm = a->sm; + struct upnp_wps_device_sm *sm = eloop_data; struct wpabuf *msg; int next_timeout_msec = 100; int next_timeout_sec = 0; @@ -423,7 +408,7 @@ static void msearchreply_state_machine_handler(void *eloop_data, wpa_printf(MSG_MSGDUMP, "WPS UPnP: M-SEARCH reply state=%d (%s:%d)", a->state, inet_ntoa(a->client.sin_addr), ntohs(a->client.sin_port)); - msg = next_advertisement(a, &islast); + msg = next_advertisement(sm, a, &islast); if (msg == NULL) return; @@ -477,10 +462,12 @@ static void msearchreply_state_machine_start(struct upnp_wps_device_sm *sm, struct advertisement_state_machine *a; int next_timeout_sec; int next_timeout_msec; + int replies; + replies = dl_list_len(&sm->msearch_replies); wpa_printf(MSG_DEBUG, "WPS UPnP: M-SEARCH reply start (%d " - "outstanding)", sm->n_msearch_replies); - if (sm->n_msearch_replies >= MAX_MSEARCH) { + "outstanding)", replies); + if (replies >= MAX_MSEARCH) { wpa_printf(MSG_INFO, "WPS UPnP: Too many outstanding " "M-SEARCH replies"); return; @@ -491,7 +478,6 @@ static void msearchreply_state_machine_start(struct upnp_wps_device_sm *sm, return; a->type = MSEARCH_REPLY; a->state = 0; - a->sm = sm; os_memcpy(&a->client, client, sizeof(*client)); /* Wait time depending on MX value */ next_timeout_msec = (1000 * mx * (os_random() & 0xFF)) >> 8; @@ -504,15 +490,7 @@ static void msearchreply_state_machine_start(struct upnp_wps_device_sm *sm, goto fail; } /* Remember for future cleanup */ - if (sm->msearch_replies) { - a->next = sm->msearch_replies; - a->prev = a->next->prev; - a->prev->next = a; - a->next->prev = a; - } else { - sm->msearch_replies = a->next = a->prev = a; - } - sm->n_msearch_replies++; + dl_list_add(&sm->msearch_replies, &a->list); return; fail: @@ -546,7 +524,9 @@ fail: static void ssdp_parse_msearch(struct upnp_wps_device_sm *sm, struct sockaddr_in *client, const char *data) { +#ifndef CONFIG_NO_STDOUT_DEBUG const char *start = data; +#endif /* CONFIG_NO_STDOUT_DEBUG */ const char *end; int got_host = 0; int got_st = 0, st_match = 0; @@ -688,7 +668,7 @@ bad: * ssdp_listener_stop - Stop SSDP listered * @sm: WPS UPnP state machine from upnp_wps_device_init() * - * This function stops the SSDP listerner that was started by calling + * This function stops the SSDP listener that was started by calling * ssdp_listener_start(). */ void ssdp_listener_stop(struct upnp_wps_device_sm *sm) @@ -750,23 +730,16 @@ static void ssdp_listener_handler(int sd, void *eloop_ctx, void *sock_ctx) } -/** - * ssdp_listener_start - Set up for receiving discovery (UDP) packets - * @sm: WPS UPnP state machine from upnp_wps_device_init() - * Returns: 0 on success, -1 on failure - * - * The SSDP listerner is stopped by calling ssdp_listener_stop(). - */ -int ssdp_listener_start(struct upnp_wps_device_sm *sm) +int ssdp_listener_open(void) { - int sd = -1; struct sockaddr_in addr; struct ip_mreq mcast_addr; int on = 1; /* per UPnP spec, keep IP packet time to live (TTL) small */ unsigned char ttl = 4; + int sd; - sm->ssdp_sd = sd = socket(AF_INET, SOCK_DGRAM, 0); + sd = socket(AF_INET, SOCK_DGRAM, 0); if (sd < 0) goto fail; if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0) @@ -788,8 +761,29 @@ int ssdp_listener_start(struct upnp_wps_device_sm *sm) if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl))) goto fail; - if (eloop_register_sock(sd, EVENT_TYPE_READ, ssdp_listener_handler, - NULL, sm)) + + return sd; + +fail: + if (sd >= 0) + close(sd); + return -1; +} + + +/** + * ssdp_listener_start - Set up for receiving discovery (UDP) packets + * @sm: WPS UPnP state machine from upnp_wps_device_init() + * Returns: 0 on success, -1 on failure + * + * The SSDP listener is stopped by calling ssdp_listener_stop(). + */ +int ssdp_listener_start(struct upnp_wps_device_sm *sm) +{ + sm->ssdp_sd = ssdp_listener_open(); + + if (eloop_register_sock(sm->ssdp_sd, EVENT_TYPE_READ, + ssdp_listener_handler, NULL, sm)) goto fail; sm->ssdp_sd_registered = 1; return 0; @@ -813,7 +807,7 @@ fail: * once after booting up, but it does not hurt to call this more frequently * "to be safe". */ -int add_ssdp_network(char *net_if) +int add_ssdp_network(const char *net_if) { #ifdef __linux__ int ret = -1; @@ -829,7 +823,7 @@ int add_ssdp_network(char *net_if) if (sock < 0) goto fail; - rt.rt_dev = net_if; + rt.rt_dev = (char *) net_if; sin = aliasing_hide_typecast(&rt.rt_dst, struct sockaddr_in); sin->sin_family = AF_INET; sin->sin_port = 0; @@ -864,19 +858,14 @@ fail: } -/** - * ssdp_open_multicast - Open socket for sending multicast SSDP messages - * @sm: WPS UPnP state machine from upnp_wps_device_init() - * Returns: 0 on success, -1 on failure - */ -int ssdp_open_multicast(struct upnp_wps_device_sm *sm) +int ssdp_open_multicast_sock(u32 ip_addr) { - int sd = -1; + int sd; /* per UPnP-arch-DeviceArchitecture, 1. Discovery, keep IP packet * time to live (TTL) small */ unsigned char ttl = 4; - sm->multicast_sd = sd = socket(AF_INET, SOCK_DGRAM, 0); + sd = socket(AF_INET, SOCK_DGRAM, 0); if (sd < 0) return -1; @@ -886,7 +875,7 @@ int ssdp_open_multicast(struct upnp_wps_device_sm *sm) #endif if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_IF, - &sm->ip_addr, sizeof(sm->ip_addr))) + &ip_addr, sizeof(ip_addr))) return -1; if (setsockopt(sd, IPPROTO_IP, IP_MULTICAST_TTL, &ttl, sizeof(ttl))) @@ -896,7 +885,7 @@ int ssdp_open_multicast(struct upnp_wps_device_sm *sm) { struct ip_mreq mreq; mreq.imr_multiaddr.s_addr = inet_addr(UPNP_MULTICAST_ADDRESS); - mreq.imr_interface.s_addr = sm->ip_addr; + mreq.imr_interface.s_addr = ip_addr; wpa_printf(MSG_DEBUG, "WPS UPnP: Multicast addr 0x%x if addr " "0x%x", mreq.imr_multiaddr.s_addr, @@ -917,5 +906,19 @@ int ssdp_open_multicast(struct upnp_wps_device_sm *sm) * which aids debugging I suppose but isn't really necessary? */ + return sd; +} + + +/** + * ssdp_open_multicast - Open socket for sending multicast SSDP messages + * @sm: WPS UPnP state machine from upnp_wps_device_init() + * Returns: 0 on success, -1 on failure + */ +int ssdp_open_multicast(struct upnp_wps_device_sm *sm) +{ + sm->multicast_sd = ssdp_open_multicast_sock(sm->ip_addr); + if (sm->multicast_sd < 0) + return -1; return 0; } diff --git a/contrib/wpa/src/wps/wps_upnp_web.c b/contrib/wpa/src/wps/wps_upnp_web.c index b637454..9a6b36e 100644 --- a/contrib/wpa/src/wps/wps_upnp_web.c +++ b/contrib/wpa/src/wps/wps_upnp_web.c @@ -9,16 +9,16 @@ */ #include "includes.h" -#include <fcntl.h> #include "common.h" #include "base64.h" -#include "eloop.h" #include "uuid.h" #include "httpread.h" +#include "http_server.h" #include "wps_i.h" #include "wps_upnp.h" #include "wps_upnp_i.h" +#include "upnp_xml.h" /*************************************************************************** * Web connections (we serve pages of info about ourselves, handle @@ -38,238 +38,6 @@ static const char *http_connection_close = "Connection: close\r\n"; /* - * Incoming web connections are recorded in this struct. - * A web connection is a TCP connection to us, the server; - * it is called a "web connection" because we use http and serve - * data that looks like web pages. - * State information is need to track the connection until we figure - * out what they want and what we want to do about it. - */ -struct web_connection { - /* double linked list */ - struct web_connection *next; - struct web_connection *prev; - struct upnp_wps_device_sm *sm; /* parent */ - int sd; /* socket to read from */ - struct sockaddr_in cli_addr; - int sd_registered; /* nonzero if we must cancel registration */ - struct httpread *hread; /* state machine for reading socket */ - int n_rcvd_data; /* how much data read so far */ - int done; /* internal flag, set when we've finished */ -}; - - -/* - * XML parsing and formatting - * - * XML is a markup language based on unicode; usually (and in our case, - * always!) based on utf-8. utf-8 uses a variable number of bytes per - * character. utf-8 has the advantage that all non-ASCII unicode characters are - * represented by sequences of non-ascii (high bit set) bytes, whereas ASCII - * characters are single ascii bytes, thus we can use typical text processing. - * - * (One other interesting thing about utf-8 is that it is possible to look at - * any random byte and determine if it is the first byte of a character as - * versus a continuation byte). - * - * The base syntax of XML uses a few ASCII punctionation characters; any - * characters that would appear in the payload data are rewritten using - * sequences, e.g., & for ampersand(&) and < for left angle bracket (<). - * Five such escapes total (more can be defined but that does not apply to our - * case). Thus we can safely parse for angle brackets etc. - * - * XML describes tree structures of tagged data, with each element beginning - * with an opening tag <label> and ending with a closing tag </label> with - * matching label. (There is also a self-closing tag <label/> which is supposed - * to be equivalent to <label></label>, i.e., no payload, but we are unlikely - * to see it for our purpose). - * - * Actually the opening tags are a little more complicated because they can - * contain "attributes" after the label (delimited by ascii space or tab chars) - * of the form attribute_label="value" or attribute_label='value'; as it turns - * out we do not have to read any of these attributes, just ignore them. - * - * Labels are any sequence of chars other than space, tab, right angle bracket - * (and ?), but may have an inner structure of <namespace><colon><plain_label>. - * As it turns out, we can ignore the namespaces, in fact we can ignore the - * entire tree hierarchy, because the plain labels we are looking for will be - * unique (not in general, but for this application). We do however have to be - * careful to skip over the namespaces. - * - * In generating XML we have to be more careful, but that is easy because - * everything we do is pretty canned. The only real care to take is to escape - * any special chars in our payload. - */ - -/** - * xml_next_tag - Advance to next tag - * @in: Input - * @out: OUT: start of tag just after '<' - * @out_tagname: OUT: start of name of tag, skipping namespace - * @end: OUT: one after tag - * Returns: 0 on success, 1 on failure - * - * A tag has form: - * <left angle bracket><...><right angle bracket> - * Within the angle brackets, there is an optional leading forward slash (which - * makes the tag an ending tag), then an optional leading label (followed by - * colon) and then the tag name itself. - * - * Note that angle brackets present in the original data must have been encoded - * as < and > so they will not trouble us. - */ -static int xml_next_tag(char *in, char **out, char **out_tagname, - char **end) -{ - while (*in && *in != '<') - in++; - if (*in != '<') - return 1; - *out = ++in; - if (*in == '/') - in++; - *out_tagname = in; /* maybe */ - while (isalnum(*in) || *in == '-') - in++; - if (*in == ':') - *out_tagname = ++in; - while (*in && *in != '>') - in++; - if (*in != '>') - return 1; - *end = ++in; - return 0; -} - - -/* xml_data_encode -- format data for xml file, escaping special characters. - * - * Note that we assume we are using utf8 both as input and as output! - * In utf8, characters may be classed as follows: - * 0xxxxxxx(2) -- 1 byte ascii char - * 11xxxxxx(2) -- 1st byte of multi-byte char w/ unicode value >= 0x80 - * 110xxxxx(2) -- 1st byte of 2 byte sequence (5 payload bits here) - * 1110xxxx(2) -- 1st byte of 3 byte sequence (4 payload bits here) - * 11110xxx(2) -- 1st byte of 4 byte sequence (3 payload bits here) - * 10xxxxxx(2) -- extension byte (6 payload bits per byte) - * Some values implied by the above are however illegal because they - * do not represent unicode chars or are not the shortest encoding. - * Actually, we can almost entirely ignore the above and just do - * text processing same as for ascii text. - * - * XML is written with arbitrary unicode characters, except that five - * characters have special meaning and so must be escaped where they - * appear in payload data... which we do here. - */ -static void xml_data_encode(struct wpabuf *buf, const char *data, int len) -{ - int i; - for (i = 0; i < len; i++) { - u8 c = ((u8 *) data)[i]; - if (c == '<') { - wpabuf_put_str(buf, "<"); - continue; - } - if (c == '>') { - wpabuf_put_str(buf, ">"); - continue; - } - if (c == '&') { - wpabuf_put_str(buf, "&"); - continue; - } - if (c == '\'') { - wpabuf_put_str(buf, "'"); - continue; - } - if (c == '"') { - wpabuf_put_str(buf, """); - continue; - } - /* - * We could try to represent control characters using the - * sequence: &#x; where x is replaced by a hex numeral, but not - * clear why we would do this. - */ - wpabuf_put_u8(buf, c); - } -} - - -/* xml_add_tagged_data -- format tagged data as a new xml line. - * - * tag must not have any special chars. - * data may have special chars, which are escaped. - */ -static void xml_add_tagged_data(struct wpabuf *buf, const char *tag, - const char *data) -{ - wpabuf_printf(buf, "<%s>", tag); - xml_data_encode(buf, data, os_strlen(data)); - wpabuf_printf(buf, "</%s>\n", tag); -} - - -/* A POST body looks something like (per upnp spec): - * <?xml version="1.0"?> - * <s:Envelope - * xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" - * s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> - * <s:Body> - * <u:actionName xmlns:u="urn:schemas-upnp-org:service:serviceType:v"> - * <argumentName>in arg value</argumentName> - * other in args and their values go here, if any - * </u:actionName> - * </s:Body> - * </s:Envelope> - * - * where : - * s: might be some other namespace name followed by colon - * u: might be some other namespace name followed by colon - * actionName will be replaced according to action requested - * schema following actionName will be WFA scheme instead - * argumentName will be actual argument name - * (in arg value) will be actual argument value - */ -static int -upnp_get_first_document_item(char *doc, const char *item, char **value) -{ - const char *match = item; - int match_len = os_strlen(item); - char *tag; - char *tagname; - char *end; - - *value = NULL; /* default, bad */ - - /* - * This is crude: ignore any possible tag name conflicts and go right - * to the first tag of this name. This should be ok for the limited - * domain of UPnP messages. - */ - for (;;) { - if (xml_next_tag(doc, &tag, &tagname, &end)) - return 1; - doc = end; - if (!os_strncasecmp(tagname, match, match_len) && - *tag != '/' && - (tagname[match_len] == '>' || - !isgraph(tagname[match_len]))) { - break; - } - } - end = doc; - while (*end && *end != '<') - end++; - *value = os_zalloc(1 + (end - doc)); - if (*value == NULL) - return 1; - os_memcpy(*value, doc, end - doc); - return 0; -} - - -/* * "Files" that we serve via HTTP. The format of these files is given by * WFA WPS specifications. Extra white space has been removed to save space. */ @@ -305,76 +73,6 @@ static const char wps_scpd_xml[] = "</argumentList>\n" "</action>\n" "<action>\n" -"<name>GetAPSettings</name>\n" -"<argumentList>\n" -"<argument>\n" -"<name>NewMessage</name>\n" -"<direction>in</direction>\n" -"<relatedStateVariable>Message</relatedStateVariable>\n" -"</argument>\n" -"<argument>\n" -"<name>NewAPSettings</name>\n" -"<direction>out</direction>\n" -"<relatedStateVariable>APSettings</relatedStateVariable>\n" -"</argument>\n" -"</argumentList>\n" -"</action>\n" -"<action>\n" -"<name>SetAPSettings</name>\n" -"<argumentList>\n" -"<argument>\n" -"<name>APSettings</name>\n" -"<direction>in</direction>\n" -"<relatedStateVariable>APSettings</relatedStateVariable>\n" -"</argument>\n" -"</argumentList>\n" -"</action>\n" -"<action>\n" -"<name>DelAPSettings</name>\n" -"<argumentList>\n" -"<argument>\n" -"<name>NewAPSettings</name>\n" -"<direction>in</direction>\n" -"<relatedStateVariable>APSettings</relatedStateVariable>\n" -"</argument>\n" -"</argumentList>\n" -"</action>\n" -"<action>\n" -"<name>GetSTASettings</name>\n" -"<argumentList>\n" -"<argument>\n" -"<name>NewMessage</name>\n" -"<direction>in</direction>\n" -"<relatedStateVariable>Message</relatedStateVariable>\n" -"</argument>\n" -"<argument>\n" -"<name>NewSTASettings</name>\n" -"<direction>out</direction>\n" -"<relatedStateVariable>STASettings</relatedStateVariable>\n" -"</argument>\n" -"</argumentList>\n" -"</action>\n" -"<action>\n" -"<name>SetSTASettings</name>\n" -"<argumentList>\n" -"<argument>\n" -"<name>NewSTASettings</name>\n" -"<direction>out</direction>\n" -"<relatedStateVariable>STASettings</relatedStateVariable>\n" -"</argument>\n" -"</argumentList>\n" -"</action>\n" -"<action>\n" -"<name>DelSTASettings</name>\n" -"<argumentList>\n" -"<argument>\n" -"<name>NewSTASettings</name>\n" -"<direction>in</direction>\n" -"<relatedStateVariable>STASettings</relatedStateVariable>\n" -"</argument>\n" -"</argumentList>\n" -"</action>\n" -"<action>\n" "<name>PutWLANResponse</name>\n" "<argumentList>\n" "<argument>\n" @@ -404,46 +102,6 @@ static const char wps_scpd_xml[] = "</argument>\n" "</argumentList>\n" "</action>\n" -"<action>\n" -"<name>RebootAP</name>\n" -"<argumentList>\n" -"<argument>\n" -"<name>NewAPSettings</name>\n" -"<direction>in</direction>\n" -"<relatedStateVariable>APSettings</relatedStateVariable>\n" -"</argument>\n" -"</argumentList>\n" -"</action>\n" -"<action>\n" -"<name>ResetAP</name>\n" -"<argumentList>\n" -"<argument>\n" -"<name>NewMessage</name>\n" -"<direction>in</direction>\n" -"<relatedStateVariable>Message</relatedStateVariable>\n" -"</argument>\n" -"</argumentList>\n" -"</action>\n" -"<action>\n" -"<name>RebootSTA</name>\n" -"<argumentList>\n" -"<argument>\n" -"<name>NewSTASettings</name>\n" -"<direction>in</direction>\n" -"<relatedStateVariable>APSettings</relatedStateVariable>\n" -"</argument>\n" -"</argumentList>\n" -"</action>\n" -"<action>\n" -"<name>ResetSTA</name>\n" -"<argumentList>\n" -"<argument>\n" -"<name>NewMessage</name>\n" -"<direction>in</direction>\n" -"<relatedStateVariable>Message</relatedStateVariable>\n" -"</argument>\n" -"</argumentList>\n" -"</action>\n" "</actionList>\n" "<serviceStateTable>\n" "<stateVariable sendEvents=\"no\">\n" @@ -462,18 +120,10 @@ static const char wps_scpd_xml[] = "<name>DeviceInfo</name>\n" "<dataType>bin.base64</dataType>\n" "</stateVariable>\n" -"<stateVariable sendEvents=\"no\">\n" -"<name>APSettings</name>\n" -"<dataType>bin.base64</dataType>\n" -"</stateVariable>\n" "<stateVariable sendEvents=\"yes\">\n" "<name>APStatus</name>\n" "<dataType>ui1</dataType>\n" "</stateVariable>\n" -"<stateVariable sendEvents=\"no\">\n" -"<name>STASettings</name>\n" -"<dataType>bin.base64</dataType>\n" -"</stateVariable>\n" "<stateVariable sendEvents=\"yes\">\n" "<name>STAStatus</name>\n" "<dataType>ui1</dataType>\n" @@ -588,27 +238,6 @@ static void format_wps_device_xml(struct upnp_wps_device_sm *sm, } -void web_connection_stop(struct web_connection *c) -{ - struct upnp_wps_device_sm *sm = c->sm; - - httpread_destroy(c->hread); - c->hread = NULL; - close(c->sd); - c->sd = -1; - if (c->next == c) { - sm->web_connections = NULL; - } else { - if (sm->web_connections == c) - sm->web_connections = c->next; - c->next->prev = c->prev; - c->prev->next = c->next; - } - os_free(c); - sm->n_web_connections--; -} - - static void http_put_reply_code(struct wpabuf *buf, enum http_reply_code code) { wpabuf_put_str(buf, "HTTP/1.1 "); @@ -669,9 +298,9 @@ static void http_put_empty(struct wpabuf *buf, enum http_reply_code code) * Per RFC 2616, content-length: is not required but connection:close * would appear to be required (given that we will be closing it!). */ -static void web_connection_parse_get(struct web_connection *c, char *filename) +static void web_connection_parse_get(struct upnp_wps_device_sm *sm, + struct http_request *hreq, char *filename) { - struct upnp_wps_device_sm *sm = c->sm; struct wpabuf *buf; /* output buffer, allocated */ char *put_length_here; char *body_start; @@ -712,8 +341,10 @@ static void web_connection_parse_get(struct web_connection *c, char *filename) wpa_printf(MSG_DEBUG, "WPS UPnP: HTTP GET file not found: %s", filename); buf = wpabuf_alloc(200); - if (buf == NULL) + if (buf == NULL) { + http_request_deinit(hreq); return; + } wpabuf_put_str(buf, "HTTP/1.1 404 Not Found\r\n" "Connection: close\r\n"); @@ -727,8 +358,10 @@ static void web_connection_parse_get(struct web_connection *c, char *filename) } buf = wpabuf_alloc(1000 + extra_len); - if (buf == NULL) + if (buf == NULL) { + http_request_deinit(hreq); return; + } wpabuf_put_str(buf, "HTTP/1.1 200 OK\r\n" @@ -765,38 +398,7 @@ static void web_connection_parse_get(struct web_connection *c, char *filename) os_memcpy(put_length_here, len_buf, os_strlen(len_buf)); send_buf: - send_wpabuf(c->sd, buf); - wpabuf_free(buf); -} - - -static struct wpabuf * web_get_item(char *data, const char *name, - enum http_reply_code *ret) -{ - char *msg; - struct wpabuf *buf; - unsigned char *decoded; - size_t len; - - if (upnp_get_first_document_item(data, name, &msg)) { - *ret = UPNP_ARG_VALUE_INVALID; - return NULL; - } - - decoded = base64_decode((unsigned char *) msg, os_strlen(msg), &len); - os_free(msg); - if (decoded == NULL) { - *ret = UPNP_OUT_OF_MEMORY; - return NULL; - } - - buf = wpabuf_alloc_ext_data(decoded, len); - if (buf == NULL) { - os_free(decoded); - *ret = UPNP_OUT_OF_MEMORY; - return NULL; - } - return buf; + http_request_send_and_deinit(hreq, buf); } @@ -805,11 +407,39 @@ web_process_get_device_info(struct upnp_wps_device_sm *sm, struct wpabuf **reply, const char **replyname) { static const char *name = "NewDeviceInfo"; + struct wps_config cfg; + struct upnp_wps_peer *peer = &sm->peer; wpa_printf(MSG_DEBUG, "WPS UPnP: GetDeviceInfo"); - if (sm->ctx->rx_req_get_device_info == NULL) + + if (sm->ctx->ap_pin == NULL) return HTTP_INTERNAL_SERVER_ERROR; - *reply = sm->ctx->rx_req_get_device_info(sm->priv, &sm->peer); + + /* + * Request for DeviceInfo, i.e., M1 TLVs. This is a start of WPS + * registration over UPnP with the AP acting as an Enrollee. It should + * be noted that this is frequently used just to get the device data, + * i.e., there may not be any intent to actually complete the + * registration. + */ + + if (peer->wps) + wps_deinit(peer->wps); + + os_memset(&cfg, 0, sizeof(cfg)); + cfg.wps = sm->wps; + cfg.pin = (u8 *) sm->ctx->ap_pin; + cfg.pin_len = os_strlen(sm->ctx->ap_pin); + peer->wps = wps_init(&cfg); + if (peer->wps) { + enum wsc_op_code op_code; + *reply = wps_get_msg(peer->wps, &op_code); + if (*reply == NULL) { + wps_deinit(peer->wps); + peer->wps = NULL; + } + } else + *reply = NULL; if (*reply == NULL) { wpa_printf(MSG_INFO, "WPS UPnP: Failed to get DeviceInfo"); return HTTP_INTERNAL_SERVER_ERROR; @@ -826,6 +456,8 @@ web_process_put_message(struct upnp_wps_device_sm *sm, char *data, struct wpabuf *msg; static const char *name = "NewOutMessage"; enum http_reply_code ret; + enum wps_process_res res; + enum wsc_op_code op_code; /* * PutMessage is used by external UPnP-based Registrar to perform WPS @@ -833,104 +465,14 @@ web_process_put_message(struct upnp_wps_device_sm *sm, char *data, * PutWLANResponse which is for proxying. */ wpa_printf(MSG_DEBUG, "WPS UPnP: PutMessage"); - if (sm->ctx->rx_req_put_message == NULL) - return HTTP_INTERNAL_SERVER_ERROR; - msg = web_get_item(data, "NewInMessage", &ret); - if (msg == NULL) - return ret; - *reply = sm->ctx->rx_req_put_message(sm->priv, &sm->peer, msg); - wpabuf_free(msg); - if (*reply == NULL) - return HTTP_INTERNAL_SERVER_ERROR; - *replyname = name; - return HTTP_OK; -} - - -static enum http_reply_code -web_process_get_ap_settings(struct upnp_wps_device_sm *sm, char *data, - struct wpabuf **reply, const char **replyname) -{ - struct wpabuf *msg; - static const char *name = "NewAPSettings"; - enum http_reply_code ret; - - wpa_printf(MSG_DEBUG, "WPS UPnP: GetAPSettings"); - if (sm->ctx->rx_req_get_ap_settings == NULL) - return HTTP_INTERNAL_SERVER_ERROR; - msg = web_get_item(data, "NewMessage", &ret); - if (msg == NULL) - return ret; - *reply = sm->ctx->rx_req_get_ap_settings(sm->priv, msg); - wpabuf_free(msg); - if (*reply == NULL) - return HTTP_INTERNAL_SERVER_ERROR; - *replyname = name; - return HTTP_OK; -} - - -static enum http_reply_code -web_process_set_ap_settings(struct upnp_wps_device_sm *sm, char *data, - struct wpabuf **reply, const char **replyname) -{ - struct wpabuf *msg; - enum http_reply_code ret; - - wpa_printf(MSG_DEBUG, "WPS UPnP: SetAPSettings"); - msg = web_get_item(data, "NewAPSettings", &ret); - if (msg == NULL) - return ret; - if (!sm->ctx->rx_req_set_ap_settings || - sm->ctx->rx_req_set_ap_settings(sm->priv, msg)) { - wpabuf_free(msg); - return HTTP_INTERNAL_SERVER_ERROR; - } - wpabuf_free(msg); - *replyname = NULL; - *reply = NULL; - return HTTP_OK; -} - - -static enum http_reply_code -web_process_del_ap_settings(struct upnp_wps_device_sm *sm, char *data, - struct wpabuf **reply, const char **replyname) -{ - struct wpabuf *msg; - enum http_reply_code ret; - - wpa_printf(MSG_DEBUG, "WPS UPnP: DelAPSettings"); - msg = web_get_item(data, "NewAPSettings", &ret); - if (msg == NULL) - return ret; - if (!sm->ctx->rx_req_del_ap_settings || - sm->ctx->rx_req_del_ap_settings(sm->priv, msg)) { - wpabuf_free(msg); - return HTTP_INTERNAL_SERVER_ERROR; - } - wpabuf_free(msg); - *replyname = NULL; - *reply = NULL; - return HTTP_OK; -} - - -static enum http_reply_code -web_process_get_sta_settings(struct upnp_wps_device_sm *sm, char *data, - struct wpabuf **reply, const char **replyname) -{ - struct wpabuf *msg; - static const char *name = "NewSTASettings"; - enum http_reply_code ret; - - wpa_printf(MSG_DEBUG, "WPS UPnP: GetSTASettings"); - if (sm->ctx->rx_req_get_sta_settings == NULL) - return HTTP_INTERNAL_SERVER_ERROR; - msg = web_get_item(data, "NewMessage", &ret); + msg = xml_get_base64_item(data, "NewInMessage", &ret); if (msg == NULL) return ret; - *reply = sm->ctx->rx_req_get_sta_settings(sm->priv, msg); + res = wps_process_msg(sm->peer.wps, WSC_UPnP, msg); + if (res == WPS_FAILURE) + *reply = NULL; + else + *reply = wps_get_msg(sm->peer.wps, &op_code); wpabuf_free(msg); if (*reply == NULL) return HTTP_INTERNAL_SERVER_ERROR; @@ -940,52 +482,6 @@ web_process_get_sta_settings(struct upnp_wps_device_sm *sm, char *data, static enum http_reply_code -web_process_set_sta_settings(struct upnp_wps_device_sm *sm, char *data, - struct wpabuf **reply, const char **replyname) -{ - struct wpabuf *msg; - enum http_reply_code ret; - - wpa_printf(MSG_DEBUG, "WPS UPnP: SetSTASettings"); - msg = web_get_item(data, "NewSTASettings", &ret); - if (msg == NULL) - return ret; - if (!sm->ctx->rx_req_set_sta_settings || - sm->ctx->rx_req_set_sta_settings(sm->priv, msg)) { - wpabuf_free(msg); - return HTTP_INTERNAL_SERVER_ERROR; - } - wpabuf_free(msg); - *replyname = NULL; - *reply = NULL; - return HTTP_OK; -} - - -static enum http_reply_code -web_process_del_sta_settings(struct upnp_wps_device_sm *sm, char *data, - struct wpabuf **reply, const char **replyname) -{ - struct wpabuf *msg; - enum http_reply_code ret; - - wpa_printf(MSG_DEBUG, "WPS UPnP: DelSTASettings"); - msg = web_get_item(data, "NewSTASettings", &ret); - if (msg == NULL) - return ret; - if (!sm->ctx->rx_req_del_sta_settings || - sm->ctx->rx_req_del_sta_settings(sm->priv, msg)) { - wpabuf_free(msg); - return HTTP_INTERNAL_SERVER_ERROR; - } - wpabuf_free(msg); - *replyname = NULL; - *reply = NULL; - return HTTP_OK; -} - - -static enum http_reply_code web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data, struct wpabuf **reply, const char **replyname) { @@ -1002,22 +498,45 @@ web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data, */ wpa_printf(MSG_DEBUG, "WPS UPnP: PutWLANResponse"); - msg = web_get_item(data, "NewMessage", &ret); - if (msg == NULL) + msg = xml_get_base64_item(data, "NewMessage", &ret); + if (msg == NULL) { + wpa_printf(MSG_DEBUG, "WPS UPnP: Could not extract NewMessage " + "from PutWLANResponse"); return ret; - if (upnp_get_first_document_item(data, "NewWLANEventType", &val)) { + } + val = xml_get_first_item(data, "NewWLANEventType"); + if (val == NULL) { + wpa_printf(MSG_DEBUG, "WPS UPnP: No NewWLANEventType in " + "PutWLANResponse"); wpabuf_free(msg); return UPNP_ARG_VALUE_INVALID; } ev_type = atol(val); os_free(val); - val = NULL; - if (upnp_get_first_document_item(data, "NewWLANEventMAC", &val) || - hwaddr_aton(val, macaddr)) { + val = xml_get_first_item(data, "NewWLANEventMAC"); + if (val == NULL) { + wpa_printf(MSG_DEBUG, "WPS UPnP: No NewWLANEventMAC in " + "PutWLANResponse"); wpabuf_free(msg); - os_free(val); return UPNP_ARG_VALUE_INVALID; } + if (hwaddr_aton(val, macaddr)) { + wpa_printf(MSG_DEBUG, "WPS UPnP: Invalid NewWLANEventMAC in " + "PutWLANResponse: '%s'", val); + if (hwaddr_aton2(val, macaddr) > 0) { + /* + * At least some versions of Intel PROset seem to be + * using dot-deliminated MAC address format here. + */ + wpa_printf(MSG_DEBUG, "WPS UPnP: Workaround - allow " + "incorrect MAC address format in " + "NewWLANEventMAC"); + } else { + wpabuf_free(msg); + os_free(val); + return UPNP_ARG_VALUE_INVALID; + } + } os_free(val); if (ev_type == UPNP_WPS_WLANEVENT_TYPE_EAP) { struct wps_parse_attr attr; @@ -1044,112 +563,50 @@ web_process_put_wlan_response(struct upnp_wps_device_sm *sm, char *data, } -static enum http_reply_code -web_process_set_selected_registrar(struct upnp_wps_device_sm *sm, char *data, - struct wpabuf **reply, - const char **replyname) +static int find_er_addr(struct subscription *s, struct sockaddr_in *cli) { - struct wpabuf *msg; - enum http_reply_code ret; - - wpa_printf(MSG_DEBUG, "WPS UPnP: SetSelectedRegistrar"); - msg = web_get_item(data, "NewMessage", &ret); - if (msg == NULL) - return ret; - if (!sm->ctx->rx_req_set_selected_registrar || - sm->ctx->rx_req_set_selected_registrar(sm->priv, msg)) { - wpabuf_free(msg); - return HTTP_INTERNAL_SERVER_ERROR; - } - wpabuf_free(msg); - *replyname = NULL; - *reply = NULL; - return HTTP_OK; -} - + struct subscr_addr *a; -static enum http_reply_code -web_process_reboot_ap(struct upnp_wps_device_sm *sm, char *data, - struct wpabuf **reply, const char **replyname) -{ - struct wpabuf *msg; - enum http_reply_code ret; - - wpa_printf(MSG_DEBUG, "WPS UPnP: RebootAP"); - msg = web_get_item(data, "NewAPSettings", &ret); - if (msg == NULL) - return ret; - if (!sm->ctx->rx_req_reboot_ap || - sm->ctx->rx_req_reboot_ap(sm->priv, msg)) { - wpabuf_free(msg); - return HTTP_INTERNAL_SERVER_ERROR; + dl_list_for_each(a, &s->addr_list, struct subscr_addr, list) { + if (cli->sin_addr.s_addr == a->saddr.sin_addr.s_addr) + return 1; } - wpabuf_free(msg); - *replyname = NULL; - *reply = NULL; - return HTTP_OK; + return 0; } -static enum http_reply_code -web_process_reset_ap(struct upnp_wps_device_sm *sm, char *data, - struct wpabuf **reply, const char **replyname) +static struct subscription * find_er(struct upnp_wps_device_sm *sm, + struct sockaddr_in *cli) { - struct wpabuf *msg; - enum http_reply_code ret; - - wpa_printf(MSG_DEBUG, "WPS UPnP: ResetAP"); - msg = web_get_item(data, "NewMessage", &ret); - if (msg == NULL) - return ret; - if (!sm->ctx->rx_req_reset_ap || - sm->ctx->rx_req_reset_ap(sm->priv, msg)) { - wpabuf_free(msg); - return HTTP_INTERNAL_SERVER_ERROR; - } - wpabuf_free(msg); - *replyname = NULL; - *reply = NULL; - return HTTP_OK; + struct subscription *s; + dl_list_for_each(s, &sm->subscriptions, struct subscription, list) + if (find_er_addr(s, cli)) + return s; + return NULL; } static enum http_reply_code -web_process_reboot_sta(struct upnp_wps_device_sm *sm, char *data, - struct wpabuf **reply, const char **replyname) +web_process_set_selected_registrar(struct upnp_wps_device_sm *sm, + struct sockaddr_in *cli, char *data, + struct wpabuf **reply, + const char **replyname) { struct wpabuf *msg; enum http_reply_code ret; + struct subscription *s; - wpa_printf(MSG_DEBUG, "WPS UPnP: RebootSTA"); - msg = web_get_item(data, "NewSTASettings", &ret); - if (msg == NULL) - return ret; - if (!sm->ctx->rx_req_reboot_sta || - sm->ctx->rx_req_reboot_sta(sm->priv, msg)) { - wpabuf_free(msg); - return HTTP_INTERNAL_SERVER_ERROR; + wpa_printf(MSG_DEBUG, "WPS UPnP: SetSelectedRegistrar"); + s = find_er(sm, cli); + if (s == NULL) { + wpa_printf(MSG_DEBUG, "WPS UPnP: Ignore SetSelectedRegistrar " + "from unknown ER"); + return UPNP_ACTION_FAILED; } - wpabuf_free(msg); - *replyname = NULL; - *reply = NULL; - return HTTP_OK; -} - - -static enum http_reply_code -web_process_reset_sta(struct upnp_wps_device_sm *sm, char *data, - struct wpabuf **reply, const char **replyname) -{ - struct wpabuf *msg; - enum http_reply_code ret; - - wpa_printf(MSG_DEBUG, "WPS UPnP: ResetSTA"); - msg = web_get_item(data, "NewMessage", &ret); + msg = xml_get_base64_item(data, "NewMessage", &ret); if (msg == NULL) return ret; - if (!sm->ctx->rx_req_reset_sta || - sm->ctx->rx_req_reset_sta(sm->priv, msg)) { + if (upnp_er_set_selected_registrar(sm->wps->registrar, s, msg)) { wpabuf_free(msg); return HTTP_INTERNAL_SERVER_ERROR; } @@ -1180,7 +637,7 @@ static const char *soap_error_postfix = "</detail>\n" "</s:Fault>\n"; -static void web_connection_send_reply(struct web_connection *c, +static void web_connection_send_reply(struct http_request *req, enum http_reply_code ret, const char *action, int action_len, const struct wpabuf *reply, @@ -1208,8 +665,8 @@ static void web_connection_send_reply(struct web_connection *c, if (buf == NULL) { wpa_printf(MSG_INFO, "WPS UPnP: Cannot allocate reply to " "POST"); - wpabuf_free(buf); os_free(replydata); + http_request_deinit(req); return; } @@ -1282,13 +739,12 @@ static void web_connection_send_reply(struct web_connection *c, os_memcpy(put_length_here, len_buf, os_strlen(len_buf)); } - send_wpabuf(c->sd, buf); - wpabuf_free(buf); + http_request_send_and_deinit(req, buf); } -static const char * web_get_action(struct web_connection *c, - const char *filename, size_t *action_len) +static const char * web_get_action(struct http_request *req, + size_t *action_len) { const char *match; int match_len; @@ -1296,13 +752,8 @@ static const char * web_get_action(struct web_connection *c, char *action; *action_len = 0; - if (os_strcasecmp(filename, UPNP_WPS_DEVICE_CONTROL_FILE)) { - wpa_printf(MSG_INFO, "WPS UPnP: Invalid POST filename %s", - filename); - return NULL; - } /* The SOAPAction line of the header tells us what we want to do */ - b = httpread_hdr_line_get(c->hread, "SOAPAction:"); + b = http_request_get_hdr_line(req, "SOAPAction:"); if (b == NULL) return NULL; if (*b == '"') @@ -1349,70 +800,47 @@ static const char * web_get_action(struct web_connection *c, * Per RFC 2616, content-length: is not required but connection:close * would appear to be required (given that we will be closing it!). */ -static void web_connection_parse_post(struct web_connection *c, +static void web_connection_parse_post(struct upnp_wps_device_sm *sm, + struct sockaddr_in *cli, + struct http_request *req, const char *filename) { enum http_reply_code ret; - struct upnp_wps_device_sm *sm = c->sm; - char *data = httpread_data_get(c->hread); /* body of http msg */ - const char *action; - size_t action_len; + char *data = http_request_get_data(req); /* body of http msg */ + const char *action = NULL; + size_t action_len = 0; const char *replyname = NULL; /* argument name for the reply */ struct wpabuf *reply = NULL; /* data for the reply */ + if (os_strcasecmp(filename, UPNP_WPS_DEVICE_CONTROL_FILE)) { + wpa_printf(MSG_INFO, "WPS UPnP: Invalid POST filename %s", + filename); + ret = HTTP_NOT_FOUND; + goto bad; + } + ret = UPNP_INVALID_ACTION; - action = web_get_action(c, filename, &action_len); + action = web_get_action(req, &action_len); if (action == NULL) goto bad; - /* - * There are quite a few possible actions. Although we appear to - * support them all here, not all of them are necessarily supported by - * callbacks at higher levels. - */ if (!os_strncasecmp("GetDeviceInfo", action, action_len)) ret = web_process_get_device_info(sm, &reply, &replyname); else if (!os_strncasecmp("PutMessage", action, action_len)) ret = web_process_put_message(sm, data, &reply, &replyname); - else if (!os_strncasecmp("GetAPSettings", action, action_len)) - ret = web_process_get_ap_settings(sm, data, &reply, - &replyname); - else if (!os_strncasecmp("SetAPSettings", action, action_len)) - ret = web_process_set_ap_settings(sm, data, &reply, - &replyname); - else if (!os_strncasecmp("DelAPSettings", action, action_len)) - ret = web_process_del_ap_settings(sm, data, &reply, - &replyname); - else if (!os_strncasecmp("GetSTASettings", action, action_len)) - ret = web_process_get_sta_settings(sm, data, &reply, - &replyname); - else if (!os_strncasecmp("SetSTASettings", action, action_len)) - ret = web_process_set_sta_settings(sm, data, &reply, - &replyname); - else if (!os_strncasecmp("DelSTASettings", action, action_len)) - ret = web_process_del_sta_settings(sm, data, &reply, - &replyname); else if (!os_strncasecmp("PutWLANResponse", action, action_len)) ret = web_process_put_wlan_response(sm, data, &reply, &replyname); else if (!os_strncasecmp("SetSelectedRegistrar", action, action_len)) - ret = web_process_set_selected_registrar(sm, data, &reply, + ret = web_process_set_selected_registrar(sm, cli, data, &reply, &replyname); - else if (!os_strncasecmp("RebootAP", action, action_len)) - ret = web_process_reboot_ap(sm, data, &reply, &replyname); - else if (!os_strncasecmp("ResetAP", action, action_len)) - ret = web_process_reset_ap(sm, data, &reply, &replyname); - else if (!os_strncasecmp("RebootSTA", action, action_len)) - ret = web_process_reboot_sta(sm, data, &reply, &replyname); - else if (!os_strncasecmp("ResetSTA", action, action_len)) - ret = web_process_reset_sta(sm, data, &reply, &replyname); else wpa_printf(MSG_INFO, "WPS UPnP: Unknown POST type"); bad: if (ret != HTTP_OK) wpa_printf(MSG_INFO, "WPS UPnP: POST failure ret=%d", ret); - web_connection_send_reply(c, ret, action, action_len, reply, + web_connection_send_reply(req, ret, action, action_len, reply, replyname); wpabuf_free(reply); } @@ -1437,13 +865,13 @@ bad: * Per RFC 2616, content-length: is not required but connection:close * would appear to be required (given that we will be closing it!). */ -static void web_connection_parse_subscribe(struct web_connection *c, +static void web_connection_parse_subscribe(struct upnp_wps_device_sm *sm, + struct http_request *req, const char *filename) { - struct upnp_wps_device_sm *sm = c->sm; struct wpabuf *buf; char *b; - char *hdr = httpread_hdr_get(c->hread); + char *hdr = http_request_get_hdr(req); char *h; char *match; int match_len; @@ -1457,8 +885,10 @@ static void web_connection_parse_subscribe(struct web_connection *c, enum http_reply_code ret = HTTP_INTERNAL_SERVER_ERROR; buf = wpabuf_alloc(1000); - if (buf == NULL) + if (buf == NULL) { + http_request_deinit(req); return; + } /* Parse/validate headers */ h = hdr; @@ -1599,9 +1029,8 @@ static void web_connection_parse_subscribe(struct web_connection *c, /* And empty line to terminate header: */ wpabuf_put_str(buf, "\r\n"); - send_wpabuf(c->sd, buf); - wpabuf_free(buf); os_free(callback_urls); + http_request_send_and_deinit(req, buf); return; error: @@ -1627,8 +1056,7 @@ error: * 599 Too many subscriptions (not a standard HTTP error) */ http_put_empty(buf, ret); - send_wpabuf(c->sd, buf); - wpabuf_free(buf); + http_request_send_and_deinit(req, buf); os_free(callback_urls); } @@ -1648,12 +1076,12 @@ error: * Per RFC 2616, content-length: is not required but connection:close * would appear to be required (given that we will be closing it!). */ -static void web_connection_parse_unsubscribe(struct web_connection *c, +static void web_connection_parse_unsubscribe(struct upnp_wps_device_sm *sm, + struct http_request *req, const char *filename) { - struct upnp_wps_device_sm *sm = c->sm; struct wpabuf *buf; - char *hdr = httpread_hdr_get(c->hread); + char *hdr = http_request_get_hdr(req); char *h; char *match; int match_len; @@ -1721,12 +1149,13 @@ static void web_connection_parse_unsubscribe(struct web_connection *c, if (got_uuid) { s = subscription_find(sm, uuid); if (s) { + struct subscr_addr *sa; + sa = dl_list_first(&s->addr_list, struct subscr_addr, + list); wpa_printf(MSG_DEBUG, "WPS UPnP: Unsubscribing %p %s", - s, - (s && s->addr_list && - s->addr_list->domain_and_port) ? - s->addr_list->domain_and_port : "-null-"); - subscription_unlink(s); + s, (sa && sa->domain_and_port) ? + sa->domain_and_port : "-null-"); + dl_list_del(&s->list); subscription_destroy(s); } } else { @@ -1740,40 +1169,42 @@ static void web_connection_parse_unsubscribe(struct web_connection *c, send_msg: buf = wpabuf_alloc(200); - if (buf == NULL) + if (buf == NULL) { + http_request_deinit(req); return; + } http_put_empty(buf, ret); - send_wpabuf(c->sd, buf); - wpabuf_free(buf); + http_request_send_and_deinit(req, buf); } /* Send error in response to unknown requests */ -static void web_connection_unimplemented(struct web_connection *c) +static void web_connection_unimplemented(struct http_request *req) { struct wpabuf *buf; buf = wpabuf_alloc(200); - if (buf == NULL) + if (buf == NULL) { + http_request_deinit(req); return; + } http_put_empty(buf, HTTP_UNIMPLEMENTED); - send_wpabuf(c->sd, buf); - wpabuf_free(buf); + http_request_send_and_deinit(req, buf); } /* Called when we have gotten an apparently valid http request. */ -static void web_connection_check_data(struct web_connection *c) +static void web_connection_check_data(void *ctx, struct http_request *req) { - struct httpread *hread = c->hread; - enum httpread_hdr_type htype = httpread_hdr_type_get(hread); - /* char *data = httpread_data_get(hread); */ - char *filename = httpread_uri_get(hread); + struct upnp_wps_device_sm *sm = ctx; + enum httpread_hdr_type htype = http_request_get_type(req); + char *filename = http_request_get_uri(req); + struct sockaddr_in *cli = http_request_get_cli_addr(req); - c->done = 1; if (!filename) { wpa_printf(MSG_INFO, "WPS UPnP: Could not get HTTP URI"); + http_request_deinit(req); return; } /* Trim leading slashes from filename */ @@ -1781,22 +1212,22 @@ static void web_connection_check_data(struct web_connection *c) filename++; wpa_printf(MSG_DEBUG, "WPS UPnP: Got HTTP request type %d from %s:%d", - htype, inet_ntoa(c->cli_addr.sin_addr), - htons(c->cli_addr.sin_port)); + htype, inet_ntoa(cli->sin_addr), htons(cli->sin_port)); switch (htype) { case HTTPREAD_HDR_TYPE_GET: - web_connection_parse_get(c, filename); + web_connection_parse_get(sm, req, filename); break; case HTTPREAD_HDR_TYPE_POST: - web_connection_parse_post(c, filename); + web_connection_parse_post(sm, cli, req, filename); break; case HTTPREAD_HDR_TYPE_SUBSCRIBE: - web_connection_parse_subscribe(c, filename); + web_connection_parse_subscribe(sm, req, filename); break; case HTTPREAD_HDR_TYPE_UNSUBSCRIBE: - web_connection_parse_unsubscribe(c, filename); + web_connection_parse_unsubscribe(sm, req, filename); break; + /* We are not required to support M-POST; just plain * POST is supposed to work, so we only support that. * If for some reason we need to support M-POST, it is @@ -1804,82 +1235,12 @@ static void web_connection_check_data(struct web_connection *c) */ default: /* Send 501 for anything else */ - web_connection_unimplemented(c); + web_connection_unimplemented(req); break; } } - -/* called back when we have gotten request */ -static void web_connection_got_file_handler(struct httpread *handle, - void *cookie, - enum httpread_event en) -{ - struct web_connection *c = cookie; - - if (en == HTTPREAD_EVENT_FILE_READY) - web_connection_check_data(c); - web_connection_stop(c); -} - - -/* web_connection_start - Start web connection - * @sm: WPS UPnP state machine from upnp_wps_device_init() - * @sd: Socket descriptor - * @addr: Client address - * - * The socket descriptor sd is handed over for ownership by the WPS UPnP - * state machine. - */ -static void web_connection_start(struct upnp_wps_device_sm *sm, - int sd, struct sockaddr_in *addr) -{ - struct web_connection *c = NULL; - - /* if too many connections, bail */ - if (sm->n_web_connections >= MAX_WEB_CONNECTIONS) { - close(sd); - return; - } - - c = os_zalloc(sizeof(*c)); - if (c == NULL) - return; - os_memcpy(&c->cli_addr, addr, sizeof(c->cli_addr)); - c->sm = sm; - c->sd = sd; -#if 0 - /* - * Setting non-blocking should not be necessary for read, and can mess - * up sending where blocking might be better. - */ - if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0) - break; -#endif - c->hread = httpread_create(c->sd, web_connection_got_file_handler, - c /* cookie */, - WEB_CONNECTION_MAX_READ, - WEB_CONNECTION_TIMEOUT_SEC); - if (c->hread == NULL) - goto fail; - if (sm->web_connections) { - c->next = sm->web_connections; - c->prev = c->next->prev; - c->prev->next = c; - c->next->prev = c; - } else { - sm->web_connections = c->next = c->prev = c; - } - sm->n_web_connections++; - return; - -fail: - if (c) - web_connection_stop(c); -} - - /* * Listening for web connections * We have a single TCP listening port, and hand off connections as we get @@ -1888,77 +1249,22 @@ fail: void web_listener_stop(struct upnp_wps_device_sm *sm) { - if (sm->web_sd_registered) { - sm->web_sd_registered = 0; - eloop_unregister_sock(sm->web_sd, EVENT_TYPE_READ); - } - if (sm->web_sd >= 0) - close(sm->web_sd); - sm->web_sd = -1; -} - - -static void web_listener_handler(int sd, void *eloop_ctx, void *sock_ctx) -{ - struct sockaddr_in addr; - socklen_t addr_len = sizeof(addr); - struct upnp_wps_device_sm *sm = sock_ctx; - int new_sd; - - /* Create state for new connection */ - /* Remember so we can cancel if need be */ - new_sd = accept(sm->web_sd, (struct sockaddr *) &addr, &addr_len); - if (new_sd < 0) { - wpa_printf(MSG_ERROR, "WPS UPnP: web listener accept " - "errno=%d (%s) web_sd=%d", - errno, strerror(errno), sm->web_sd); - return; - } - web_connection_start(sm, new_sd, &addr); + http_server_deinit(sm->web_srv); + sm->web_srv = NULL; } int web_listener_start(struct upnp_wps_device_sm *sm) { - struct sockaddr_in addr; - int port; - - sm->web_sd = socket(AF_INET, SOCK_STREAM, 0); - if (sm->web_sd < 0) - goto fail; - if (fcntl(sm->web_sd, F_SETFL, O_NONBLOCK) != 0) - goto fail; - port = 49152; /* first non-reserved port */ - for (;;) { - os_memset(&addr, 0, sizeof(addr)); - addr.sin_family = AF_INET; - addr.sin_addr.s_addr = sm->ip_addr; - addr.sin_port = htons(port); - if (bind(sm->web_sd, (struct sockaddr *) &addr, - sizeof(addr)) == 0) - break; - if (errno == EADDRINUSE) { - /* search for unused port */ - if (++port == 65535) - goto fail; - continue; - } - goto fail; + struct in_addr addr; + addr.s_addr = sm->ip_addr; + sm->web_srv = http_server_init(&addr, -1, web_connection_check_data, + sm); + if (sm->web_srv == NULL) { + web_listener_stop(sm); + return -1; } - if (listen(sm->web_sd, 10 /* max backlog */) != 0) - goto fail; - if (fcntl(sm->web_sd, F_SETFL, O_NONBLOCK) != 0) - goto fail; - if (eloop_register_sock(sm->web_sd, EVENT_TYPE_READ, - web_listener_handler, NULL, sm)) - goto fail; - sm->web_sd_registered = 1; - sm->web_port = port; + sm->web_port = http_server_get_port(sm->web_srv); return 0; - -fail: - /* Error */ - web_listener_stop(sm); - return -1; } |