X-Git-Url: http://www.project-moonshot.org/gitweb/?p=mech_eap.git;a=blobdiff_plain;f=libeap%2Fsrc%2Fap%2Fieee802_1x.c;h=0f2d428cf752aa7c826570c06bac82a0aa1677ce;hp=a93581678c2efa3286d80683b9e508df7c4293c4;hb=4f319dde67a76fe0aaf33f6d2788968012584ada;hpb=ed09b5e64dd485851310307979d5eed14678087b diff --git a/libeap/src/ap/ieee802_1x.c b/libeap/src/ap/ieee802_1x.c index a935816..0f2d428 100644 --- a/libeap/src/ap/ieee802_1x.c +++ b/libeap/src/ap/ieee802_1x.c @@ -1,15 +1,9 @@ /* * hostapd / IEEE 802.1X-2004 Authenticator - * Copyright (c) 2002-2009, Jouni Malinen + * Copyright (c) 2002-2012, Jouni Malinen * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published 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 software may be distributed under the terms of the BSD license. + * See README for more details. */ #include "utils/includes.h" @@ -18,14 +12,15 @@ #include "utils/eloop.h" #include "crypto/md5.h" #include "crypto/crypto.h" +#include "crypto/random.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 "p2p/p2p.h" #include "hostapd.h" #include "accounting.h" #include "sta_info.h" @@ -33,11 +28,15 @@ #include "preauth_auth.h" #include "pmksa_cache_auth.h" #include "ap_config.h" +#include "ap_drv_ops.h" +#include "wps_hostapd.h" +#include "hs20.h" #include "ieee802_1x.h" static void ieee802_1x_finished(struct hostapd_data *hapd, - struct sta_info *sta, int success); + struct sta_info *sta, int success, + int remediation); static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta, @@ -67,10 +66,26 @@ static void ieee802_1x_send(struct hostapd_data *hapd, struct sta_info *sta, if (wpa_auth_pairwise_set(sta->wpa_sm)) encrypt = 1; +#ifdef CONFIG_TESTING_OPTIONS + if (hapd->ext_eapol_frame_io) { + size_t hex_len = 2 * len + 1; + char *hex = os_malloc(hex_len); + + if (hex) { + wpa_snprintf_hex(hex, hex_len, buf, len); + wpa_msg(hapd->msg_ctx, MSG_INFO, + "EAPOL-TX " MACSTR " %s", + MAC2STR(sta->addr), hex); + os_free(hex); + } + } else +#endif /* CONFIG_TESTING_OPTIONS */ if (sta->flags & WLAN_STA_PREAUTH) { rsn_preauth_send(hapd, sta, buf, len); } else { - hapd->drv.send_eapol(hapd, sta->addr, buf, len, encrypt); + hostapd_drv_hapd_send_eapol( + hapd, sta->addr, buf, len, + encrypt, hostapd_sta_flags_to_drv(sta->flags)); } os_free(buf); @@ -86,35 +101,33 @@ void ieee802_1x_set_sta_authorized(struct hostapd_data *hapd, 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); + ap_sta_set_authorized(hapd, sta, 1); + res = hostapd_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); + ap_sta_set_authorized(hapd, sta, 0); + res = hostapd_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); + wpa_printf(MSG_DEBUG, "Could not set station " MACSTR + " flags for kernel driver (errno=%d).", + MAC2STR(sta->addr), errno); } - if (authorized) + if (authorized) { + os_get_reltime(&sta->connected_time); accounting_sta_start(hapd, sta); + } } +#ifndef CONFIG_FIPS +#ifndef CONFIG_NO_RC4 + static void ieee802_1x_tx_key_one(struct hostapd_data *hapd, struct sta_info *sta, int idx, int broadcast, @@ -137,10 +150,10 @@ static void ieee802_1x_tx_key_one(struct hostapd_data *hapd, 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_PUT_BE16(key->key_length, key_len); wpa_get_ntp_timestamp(key->replay_counter); - if (os_get_random(key->key_iv, sizeof(key->key_iv))) { + if (random_get_bytes(key->key_iv, sizeof(key->key_iv))) { wpa_printf(MSG_ERROR, "Could not get random numbers"); os_free(buf); return; @@ -194,112 +207,10 @@ static void ieee802_1x_tx_key_one(struct hostapd_data *hapd, } -#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) +static 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; @@ -308,18 +219,12 @@ void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) 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 + if (sta->vlan_id > 0 && sta->vlan_id <= MAX_VLAN_ID) { + wpa_printf(MSG_ERROR, "Using WEP with vlans is not supported."); + return; + } #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, @@ -330,7 +235,8 @@ void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) u8 *ikey; ikey = os_malloc(hapd->conf->individual_wep_key_len); if (ikey == NULL || - os_get_random(ikey, hapd->conf->individual_wep_key_len)) { + random_get_bytes(ikey, hapd->conf->individual_wep_key_len)) + { wpa_printf(MSG_ERROR, "Could not generate random " "individual WEP key."); os_free(ikey); @@ -345,9 +251,9 @@ void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) /* 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)) { + if (hostapd_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."); } @@ -356,10 +262,15 @@ void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta) } } +#endif /* CONFIG_NO_RC4 */ +#endif /* CONFIG_FIPS */ + const char *radius_mode_txt(struct hostapd_data *hapd) { switch (hapd->iface->conf->hw_mode) { + case HOSTAPD_MODE_IEEE80211AD: + return "802.11ad"; case HOSTAPD_MODE_IEEE80211A: return "802.11a"; case HOSTAPD_MODE_IEEE80211G: @@ -391,9 +302,15 @@ static void ieee802_1x_learn_identity(struct hostapd_data *hapd, { const u8 *identity; size_t identity_len; + const struct eap_hdr *hdr = (const struct eap_hdr *) eap; if (len <= sizeof(struct eap_hdr) || - eap[sizeof(struct eap_hdr)] != EAP_TYPE_IDENTITY) + (hdr->code == EAP_CODE_RESPONSE && + eap[sizeof(struct eap_hdr)] != EAP_TYPE_IDENTITY) || + (hdr->code == EAP_CODE_INITIATE && + eap[sizeof(struct eap_hdr)] != EAP_ERP_TYPE_REAUTH) || + (hdr->code != EAP_CODE_RESPONSE && + hdr->code != EAP_CODE_INITIATE)) return; identity = eap_get_identity(sm->eap, &identity_len); @@ -402,133 +319,295 @@ static void ieee802_1x_learn_identity(struct hostapd_data *hapd, /* Save station identity for future RADIUS packets */ os_free(sm->identity); - sm->identity = os_malloc(identity_len + 1); + sm->identity = (u8 *) dup_binstr(identity, identity_len); 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, +static int add_common_radius_sta_attr_rsn(struct hostapd_data *hapd, + struct hostapd_radius_attr *req_attr, struct sta_info *sta, - const u8 *eap, size_t len) + struct radius_msg *msg) { - struct radius_msg *msg; - char buf[128]; - struct eapol_state_machine *sm = sta->eapol_sm; + u32 suite; + int ver, val; + + ver = wpa_auth_sta_wpa_version(sta->wpa_sm); + val = wpa_auth_get_pairwise(sta->wpa_sm); + suite = wpa_cipher_to_suite(ver, val); + if (val != -1 && + !hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_WLAN_PAIRWISE_CIPHER) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_PAIRWISE_CIPHER, + suite)) { + wpa_printf(MSG_ERROR, "Could not add WLAN-Pairwise-Cipher"); + return -1; + } - if (sm == NULL) - return; + suite = wpa_cipher_to_suite(((hapd->conf->wpa & 0x2) || + hapd->conf->osen) ? + WPA_PROTO_RSN : WPA_PROTO_WPA, + hapd->conf->wpa_group); + if (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_WLAN_GROUP_CIPHER) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_GROUP_CIPHER, + suite)) { + wpa_printf(MSG_ERROR, "Could not add WLAN-Group-Cipher"); + return -1; + } - ieee802_1x_learn_identity(hapd, sm, eap, len); + val = wpa_auth_sta_key_mgmt(sta->wpa_sm); + suite = wpa_akm_to_suite(val); + if (val != -1 && + !hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_WLAN_AKM_SUITE) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_WLAN_AKM_SUITE, + suite)) { + wpa_printf(MSG_ERROR, "Could not add WLAN-AKM-Suite"); + return -1; + } - wpa_printf(MSG_DEBUG, "Encapsulating EAP message into a RADIUS " - "packet"); +#ifdef CONFIG_IEEE80211W + if (hapd->conf->ieee80211w != NO_MGMT_FRAME_PROTECTION) { + suite = wpa_cipher_to_suite(WPA_PROTO_RSN, + hapd->conf->group_mgmt_cipher); + if (!hostapd_config_get_radius_attr( + req_attr, RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER) && + !radius_msg_add_attr_int32( + msg, RADIUS_ATTR_WLAN_GROUP_MGMT_CIPHER, suite)) { + wpa_printf(MSG_ERROR, + "Could not add WLAN-Group-Mgmt-Cipher"); + return -1; + } + } +#endif /* CONFIG_IEEE80211W */ - 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; + return 0; +} + + +static int add_common_radius_sta_attr(struct hostapd_data *hapd, + struct hostapd_radius_attr *req_attr, + struct sta_info *sta, + struct radius_msg *msg) +{ + char buf[128]; + + if (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_NAS_PORT) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT, sta->aid)) { + wpa_printf(MSG_ERROR, "Could not add NAS-Port"); + return -1; } - radius_msg_make_authenticator(msg, (u8 *) sta, sizeof(*sta)); + 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))) { + wpa_printf(MSG_ERROR, "Could not add Calling-Station-Id"); + return -1; + } - 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 (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 (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_CONNECT_INFO) && + !radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, + (u8 *) buf, os_strlen(buf))) { + wpa_printf(MSG_ERROR, "Could not add Connect-Info"); + return -1; + } + + if (sta->acct_session_id_hi || sta->acct_session_id_lo) { + os_snprintf(buf, sizeof(buf), "%08X-%08X", + sta->acct_session_id_hi, sta->acct_session_id_lo); + if (!radius_msg_add_attr(msg, RADIUS_ATTR_ACCT_SESSION_ID, + (u8 *) buf, os_strlen(buf))) { + wpa_printf(MSG_ERROR, "Could not add Acct-Session-Id"); + return -1; + } + } + +#ifdef CONFIG_IEEE80211R + if (hapd->conf->wpa && wpa_key_mgmt_ft(hapd->conf->wpa_key_mgmt) && + sta->wpa_sm && + (wpa_key_mgmt_ft(wpa_auth_sta_key_mgmt(sta->wpa_sm)) || + sta->auth_alg == WLAN_AUTH_FT) && + !hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_MOBILITY_DOMAIN_ID) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_MOBILITY_DOMAIN_ID, + WPA_GET_BE16( + hapd->conf->mobility_domain))) { + wpa_printf(MSG_ERROR, "Could not add Mobility-Domain-Id"); + return -1; } +#endif /* CONFIG_IEEE80211R */ - if (hapd->conf->own_ip_addr.af == AF_INET && + if ((hapd->conf->wpa || hapd->conf->osen) && sta->wpa_sm && + add_common_radius_sta_attr_rsn(hapd, req_attr, sta, msg) < 0) + return -1; + + return 0; +} + + +int add_common_radius_attr(struct hostapd_data *hapd, + struct hostapd_radius_attr *req_attr, + struct sta_info *sta, + struct radius_msg *msg) +{ + char buf[128]; + struct hostapd_radius_attr *attr; + + if (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_NAS_IP_ADDRESS) && + 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; + wpa_printf(MSG_ERROR, "Could not add NAS-IP-Address"); + return -1; } #ifdef CONFIG_IPV6 - if (hapd->conf->own_ip_addr.af == AF_INET6 && + if (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_NAS_IPV6_ADDRESS) && + 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; + wpa_printf(MSG_ERROR, "Could not add NAS-IPv6-Address"); + return -1; } #endif /* CONFIG_IPV6 */ - if (hapd->conf->nas_identifier && + if (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_NAS_IDENTIFIER) && + 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; + wpa_printf(MSG_ERROR, "Could not add NAS-Identifier"); + return -1; } os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT ":%s", - MAC2STR(hapd->own_addr), hapd->conf->ssid.ssid); + MAC2STR(hapd->own_addr), + wpa_ssid_txt(hapd->conf->ssid.ssid, + hapd->conf->ssid.ssid_len)); buf[sizeof(buf) - 1] = '\0'; - if (!radius_msg_add_attr(msg, RADIUS_ATTR_CALLED_STATION_ID, + if (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_CALLED_STATION_ID) && + !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; + wpa_printf(MSG_ERROR, "Could not add Called-Station-Id"); + return -1; } - 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; + if (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_NAS_PORT_TYPE) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_NAS_PORT_TYPE, + RADIUS_NAS_PORT_TYPE_IEEE_802_11)) { + wpa_printf(MSG_ERROR, "Could not add NAS-Port-Type"); + return -1; } - /* 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; +#ifdef CONFIG_INTERWORKING + if (hapd->conf->interworking && + !is_zero_ether_addr(hapd->conf->hessid)) { + os_snprintf(buf, sizeof(buf), RADIUS_802_1X_ADDR_FORMAT, + MAC2STR(hapd->conf->hessid)); + buf[sizeof(buf) - 1] = '\0'; + if (!hostapd_config_get_radius_attr(req_attr, + RADIUS_ATTR_WLAN_HESSID) && + !radius_msg_add_attr(msg, RADIUS_ATTR_WLAN_HESSID, + (u8 *) buf, os_strlen(buf))) { + wpa_printf(MSG_ERROR, "Could not add WLAN-HESSID"); + return -1; + } } +#endif /* CONFIG_INTERWORKING */ - 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 && add_common_radius_sta_attr(hapd, req_attr, sta, msg) < 0) + return -1; + + for (attr = req_attr; attr; attr = attr->next) { + if (!radius_msg_add_attr(msg, attr->type, + wpabuf_head(attr->val), + wpabuf_len(attr->val))) { + wpa_printf(MSG_ERROR, "Could not add RADIUS " + "attribute"); + return -1; + } } - 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'; + return 0; +} + + +static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, + struct sta_info *sta, + const u8 *eap, size_t len) +{ + struct radius_msg *msg; + 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) { + wpa_printf(MSG_INFO, "Could not create new RADIUS packet"); + return; } - if (!radius_msg_add_attr(msg, RADIUS_ATTR_CONNECT_INFO, - (u8 *) buf, os_strlen(buf))) { - printf("Could not add Connect-Info\n"); + + 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)) { + wpa_printf(MSG_INFO, "Could not add User-Name"); goto fail; } - if (eap && !radius_msg_add_eap(msg, eap, len)) { - printf("Could not add EAP-Message\n"); + if (add_common_radius_attr(hapd, hapd->conf->radius_auth_req_attr, sta, + msg) < 0) + 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 (!hostapd_config_get_radius_attr(hapd->conf->radius_auth_req_attr, + RADIUS_ATTR_FRAMED_MTU) && + !radius_msg_add_attr_int32(msg, RADIUS_ATTR_FRAMED_MTU, 1400)) { + wpa_printf(MSG_INFO, "Could not add Framed-MTU"); + goto fail; + } + + if (!radius_msg_add_eap(msg, eap, len)) { + wpa_printf(MSG_INFO, "Could not add EAP-Message"); goto fail; } @@ -540,8 +619,7 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, 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"); + wpa_printf(MSG_INFO, "Could not copy State attribute from previous Access-Challenge"); goto fail; } if (res > 0) { @@ -549,7 +627,63 @@ static void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd, } } - radius_client_send(hapd->radius, msg, RADIUS_AUTH, sta->addr); + if (hapd->conf->radius_request_cui) { + const u8 *cui; + size_t cui_len; + /* Add previously learned CUI or nul CUI to request CUI */ + if (sm->radius_cui) { + cui = wpabuf_head(sm->radius_cui); + cui_len = wpabuf_len(sm->radius_cui); + } else { + cui = (const u8 *) "\0"; + cui_len = 1; + } + if (!radius_msg_add_attr(msg, + RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, + cui, cui_len)) { + wpa_printf(MSG_ERROR, "Could not add CUI"); + goto fail; + } + } + +#ifdef CONFIG_HS20 + if (hapd->conf->hs20) { + u8 ver = 1; /* Release 2 */ + if (!radius_msg_add_wfa( + msg, RADIUS_VENDOR_ATTR_WFA_HS20_AP_VERSION, + &ver, 1)) { + wpa_printf(MSG_ERROR, "Could not add HS 2.0 AP " + "version"); + goto fail; + } + + if (sta->hs20_ie && wpabuf_len(sta->hs20_ie) > 0) { + const u8 *pos; + u8 buf[3]; + u16 id; + pos = wpabuf_head_u8(sta->hs20_ie); + buf[0] = (*pos) >> 4; + if (((*pos) & HS20_PPS_MO_ID_PRESENT) && + wpabuf_len(sta->hs20_ie) >= 3) + id = WPA_GET_LE16(pos + 1); + else + id = 0; + WPA_PUT_BE16(buf + 1, id); + if (!radius_msg_add_wfa( + msg, + RADIUS_VENDOR_ATTR_WFA_HS20_STA_VERSION, + buf, sizeof(buf))) { + wpa_printf(MSG_ERROR, "Could not add HS 2.0 " + "STA version"); + goto fail; + } + } + } +#endif /* CONFIG_HS20 */ + + if (radius_client_send(hapd->radius, msg, RADIUS_AUTH, sta->addr) < 0) + goto fail; + return; fail: @@ -570,7 +704,7 @@ static void handle_eap_response(struct hostapd_data *hapd, data = (u8 *) (eap + 1); if (len < sizeof(*eap) + 1) { - printf("handle_eap_response: too short response data\n"); + wpa_printf(MSG_INFO, "handle_eap_response: too short response data"); return; } @@ -590,6 +724,39 @@ static void handle_eap_response(struct hostapd_data *hapd, } +static void handle_eap_initiate(struct hostapd_data *hapd, + struct sta_info *sta, struct eap_hdr *eap, + size_t len) +{ +#ifdef CONFIG_ERP + u8 type, *data; + struct eapol_state_machine *sm = sta->eapol_sm; + + if (sm == NULL) + return; + + if (len < sizeof(*eap) + 1) { + wpa_printf(MSG_INFO, + "handle_eap_initiate: too short response data"); + return; + } + + data = (u8 *) (eap + 1); + 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 Initiate type %u", + eap->code, eap->identifier, be_to_host16(eap->length), + type); + + wpabuf_free(sm->eap_if->eapRespData); + sm->eap_if->eapRespData = wpabuf_alloc_copy(eap, len); + sm->eapolEap = TRUE; +#endif /* CONFIG_ERP */ +} + + /* Process incoming EAP packet from Supplicant */ static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta, u8 *buf, size_t len) @@ -598,7 +765,7 @@ static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta, u16 eap_len; if (len < sizeof(*eap)) { - printf(" too short EAP packet\n"); + wpa_printf(MSG_INFO, " too short EAP packet"); return; } @@ -633,6 +800,13 @@ static void handle_eap(struct hostapd_data *hapd, struct sta_info *sta, case EAP_CODE_FAILURE: wpa_printf(MSG_DEBUG, " (failure)"); return; + case EAP_CODE_INITIATE: + wpa_printf(MSG_DEBUG, " (initiate)"); + handle_eap_initiate(hapd, sta, eap, eap_len); + break; + case EAP_CODE_FINISH: + wpa_printf(MSG_DEBUG, " (finish)"); + break; default: wpa_printf(MSG_DEBUG, " (unknown code)"); return; @@ -652,7 +826,8 @@ ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta) flags |= EAPOL_SM_FROM_PMKSA_CACHE; } return eapol_auth_alloc(hapd->eapol_auth, sta->addr, flags, - sta->wps_ie, sta->p2p_ie, sta); + sta->wps_ie, sta->p2p_ie, sta, + sta->identity, sta->radius_cui); } @@ -673,22 +848,24 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, struct ieee802_1x_eapol_key *key; u16 datalen; struct rsn_pmksa_cache_entry *pmksa; + int key_mgmt; - if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && + if (!hapd->conf->ieee802_1x && !hapd->conf->wpa && !hapd->conf->osen && !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)) { + if (!sta || (!(sta->flags & (WLAN_STA_ASSOC | WLAN_STA_PREAUTH)) && + !(hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED))) { wpa_printf(MSG_DEBUG, "IEEE 802.1X data frame from not " - "associated STA"); + "associated/Pre-authenticating STA"); return; } if (len < sizeof(*hdr)) { - printf(" too short IEEE 802.1X packet\n"); + wpa_printf(MSG_INFO, " too short IEEE 802.1X packet"); return; } @@ -698,7 +875,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, hdr->version, hdr->type, datalen); if (len - sizeof(*hdr) < datalen) { - printf(" frame too short for this IEEE 802.1X packet\n"); + wpa_printf(MSG_INFO, " frame too short for this IEEE 802.1X packet"); if (sta->eapol_sm) sta->eapol_sm->dot1xAuthEapLengthErrorFramesRx++; return; @@ -724,10 +901,19 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, 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))) + if (!hapd->conf->ieee802_1x && !hapd->conf->osen && + !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore EAPOL message - " + "802.1X not enabled and WPS not used"); + return; + } + + key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm); + if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore EAPOL message - " + "STA is using PSK"); return; + } if (!sta->eapol_sm) { sta->eapol_sm = ieee802_1x_alloc_eapol_sm(hapd, sta); @@ -735,14 +921,24 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, 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; + if (!hapd->conf->ieee802_1x && hapd->conf->wps_state) { + u32 wflags = sta->flags & (WLAN_STA_WPS | + WLAN_STA_WPS2 | + WLAN_STA_MAYBE_WPS); + if (wflags == WLAN_STA_MAYBE_WPS || + wflags == (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)) { + /* + * Delay EAPOL frame transmission until a + * possible WPS STA initiates the handshake + * with EAPOL-Start. Only allow the wait to be + * skipped if the STA is known to support WPS + * 2.0. + */ + wpa_printf(MSG_DEBUG, "WPS: Do not start " + "EAPOL until EAPOL-Start is " + "received"); + sta->eapol_sm->flags |= EAPOL_SM_WAIT_START; + } } #endif /* CONFIG_WPS */ @@ -776,6 +972,7 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, } sta->eapol_sm->eapolStart = TRUE; sta->eapol_sm->dot1xAuthEapolStartFramesRx++; + eap_server_clear_identity(sta->eapol_sm->eap); wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH_EAPOL); break; @@ -788,11 +985,12 @@ void ieee802_1x_receive(struct hostapd_data *hapd, const u8 *sa, const u8 *buf, accounting_sta_stop(hapd, sta); sta->eapol_sm->eapolLogoff = TRUE; sta->eapol_sm->dot1xAuthEapolLogoffFramesRx++; + eap_server_clear_identity(sta->eapol_sm->eap); break; case IEEE802_1X_TYPE_EAPOL_KEY: wpa_printf(MSG_DEBUG, " EAPOL-Key"); - if (!(sta->flags & WLAN_STA_AUTHORIZED)) { + if (!ap_sta_is_authorized(sta)) { wpa_printf(MSG_DEBUG, " Dropped key data from " "unauthorized Supplicant"); break; @@ -827,10 +1025,12 @@ 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; + int key_mgmt; #ifdef CONFIG_WPS - if (hapd->conf->wps_state && hapd->conf->wpa && - (sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS))) { + if (hapd->conf->wps_state && + ((hapd->conf->wpa && (sta->flags & WLAN_STA_MAYBE_WPS)) || + (sta->flags & WLAN_STA_WPS))) { /* * Need to enable IEEE 802.1X/EAPOL state machines for possible * WPS handshake even if IEEE 802.1X/EAPOL is not used for @@ -840,9 +1040,27 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) } #endif /* CONFIG_WPS */ - if ((!force_1x && !hapd->conf->ieee802_1x) || - wpa_key_mgmt_wpa_psk(wpa_auth_sta_key_mgmt(sta->wpa_sm))) + if (!force_1x && !hapd->conf->ieee802_1x && !hapd->conf->osen) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - " + "802.1X not enabled or forced for WPS"); + /* + * Clear any possible EAPOL authenticator state to support + * reassociation change from WPS to PSK. + */ + ieee802_1x_free_station(sta); return; + } + + key_mgmt = wpa_auth_sta_key_mgmt(sta->wpa_sm); + if (key_mgmt != -1 && wpa_key_mgmt_wpa_psk(key_mgmt)) { + wpa_printf(MSG_DEBUG, "IEEE 802.1X: Ignore STA - using PSK"); + /* + * Clear any possible EAPOL authenticator state to support + * reassociation change from WPA-EAP to PSK. + */ + ieee802_1x_free_station(sta); + return; + } if (sta->eapol_sm == NULL) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, @@ -860,21 +1078,43 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) #ifdef CONFIG_WPS sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START; - if (!hapd->conf->ieee802_1x && !(sta->flags & WLAN_STA_WPS)) { + if (!hapd->conf->ieee802_1x && hapd->conf->wps_state && + !(sta->flags & WLAN_STA_WPS2)) { /* - * Delay EAPOL frame transmission until a possible WPS - * initiates the handshake with EAPOL-Start. + * Delay EAPOL frame transmission until a possible WPS STA + * initiates the handshake with EAPOL-Start. Only allow the + * wait to be skipped if the STA is known to support WPS 2.0. */ + wpa_printf(MSG_DEBUG, "WPS: Do not start EAPOL until " + "EAPOL-Start is received"); sta->eapol_sm->flags |= EAPOL_SM_WAIT_START; } #endif /* CONFIG_WPS */ sta->eapol_sm->eap_if->portEnabled = TRUE; +#ifdef CONFIG_IEEE80211R + if (sta->auth_alg == WLAN_AUTH_FT) { + hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, + HOSTAPD_LEVEL_DEBUG, + "PMK from FT - skip IEEE 802.1X/EAP"); + /* Setup EAPOL state machines to already authenticated state + * because of existing FT information from R0KH. */ + 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; + sta->eapol_sm->authFail = FALSE; + if (sta->eapol_sm->eap) + eap_sm_notify_cached(sta->eapol_sm->eap); + /* TODO: get vlan_id from R0KH using RRB message */ + return; + } +#endif /* CONFIG_IEEE80211R */ + 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"); @@ -885,13 +1125,11 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta) sta->eapol_sm->auth_pae_state = AUTH_PAE_AUTHENTICATING; sta->eapol_sm->be_auth_state = BE_AUTH_SUCCESS; sta->eapol_sm->authSuccess = TRUE; + sta->eapol_sm->authFail = FALSE; 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); + ap_sta_bind_vlan(hapd, sta); } else { if (reassoc) { /* @@ -918,6 +1156,7 @@ void ieee802_1x_free_station(struct sta_info *sta) #ifndef CONFIG_NO_RADIUS radius_msg_free(sm->last_recv_radius); radius_free_class(&sm->radius_class); + wpabuf_free(sm->radius_cui); #endif /* CONFIG_NO_RADIUS */ os_free(sm->identity); @@ -929,9 +1168,8 @@ void ieee802_1x_free_station(struct sta_info *sta) static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd, struct sta_info *sta) { - u8 *eap; - size_t len; - struct eap_hdr *hdr; + struct wpabuf *eap; + const struct eap_hdr *hdr; int eap_type = -1; char buf[64]; struct radius_msg *msg; @@ -945,7 +1183,7 @@ static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd, msg = sm->last_recv_radius; - eap = radius_msg_get_eap(msg, &len); + eap = radius_msg_get_eap(msg); if (eap == NULL) { /* RFC 3579, Chap. 2.6.3: * RADIUS server SHOULD NOT send Access-Reject/no EAP-Message @@ -957,33 +1195,29 @@ static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd, return; } - if (len < sizeof(*hdr)) { + if (wpabuf_len(eap) < sizeof(*hdr)) { hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_WARNING, "too short EAP packet " "received from authentication server"); - os_free(eap); + wpabuf_free(eap); sm->eap_if->aaaEapNoReq = TRUE; return; } - if (len > sizeof(*hdr)) - eap_type = eap[sizeof(*hdr)]; + if (wpabuf_len(eap) > sizeof(*hdr)) + eap_type = (wpabuf_head_u8(eap))[sizeof(*hdr)]; - hdr = (struct eap_hdr *) eap; + hdr = wpabuf_head(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); + 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); + eap_server_get_name(0, eap_type), eap_type); break; case EAP_CODE_SUCCESS: os_strlcpy(buf, "EAP Success", sizeof(buf)); @@ -1004,7 +1238,7 @@ static void ieee802_1x_decapsulate_radius(struct hostapd_data *hapd, sm->eap_if->aaaEapReq = TRUE; wpabuf_free(sm->eap_if->aaaEapReqData); - sm->eap_if->aaaEapReqData = wpabuf_alloc_ext_data(eap, len); + sm->eap_if->aaaEapReqData = eap; } @@ -1039,6 +1273,11 @@ static void ieee802_1x_get_keys(struct hostapd_data *hapd, sm->eap_if->aaaEapKeyDataLen = len; sm->eap_if->aaaEapKeyAvailable = TRUE; } + } else { + wpa_printf(MSG_DEBUG, + "MS-MPPE: 1x_get_keys, could not get keys: %p send: %p recv: %p", + keys, keys ? keys->send : NULL, + keys ? keys->recv : NULL); } if (keys) { @@ -1053,7 +1292,7 @@ static void ieee802_1x_store_radius_class(struct hostapd_data *hapd, struct sta_info *sta, struct radius_msg *msg) { - u8 *class; + u8 *attr_class; size_t class_len; struct eapol_state_machine *sm = sta->eapol_sm; int count, i; @@ -1069,18 +1308,18 @@ static void ieee802_1x_store_radius_class(struct hostapd_data *hapd, if (count <= 0) return; - nclass = os_zalloc(count * sizeof(struct radius_attr_data)); + nclass = os_calloc(count, sizeof(struct radius_attr_data)); if (nclass == NULL) return; nclass_count = 0; - class = NULL; + attr_class = NULL; for (i = 0; i < count; i++) { do { if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CLASS, - &class, &class_len, - class) < 0) { + &attr_class, &class_len, + attr_class) < 0) { i = count; break; } @@ -1090,7 +1329,7 @@ static void ieee802_1x_store_radius_class(struct hostapd_data *hapd, if (nclass[nclass_count].data == NULL) break; - os_memcpy(nclass[nclass_count].data, class, class_len); + os_memcpy(nclass[nclass_count].data, attr_class, class_len); nclass[nclass_count].len = class_len; nclass_count++; } @@ -1120,13 +1359,10 @@ static void ieee802_1x_update_sta_identity(struct hostapd_data *hapd, NULL) < 0) return; - identity = os_malloc(len + 1); + identity = (u8 *) dup_binstr(buf, len); 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'", @@ -1139,6 +1375,173 @@ static void ieee802_1x_update_sta_identity(struct hostapd_data *hapd, } +/* Update CUI based on Chargeable-User-Identity attribute in Access-Accept */ +static void ieee802_1x_update_sta_cui(struct hostapd_data *hapd, + struct sta_info *sta, + struct radius_msg *msg) +{ + struct eapol_state_machine *sm = sta->eapol_sm; + struct wpabuf *cui; + u8 *buf; + size_t len; + + if (sm == NULL) + return; + + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_CHARGEABLE_USER_IDENTITY, + &buf, &len, NULL) < 0) + return; + + cui = wpabuf_alloc_copy(buf, len); + if (cui == NULL) + return; + + wpabuf_free(sm->radius_cui); + sm->radius_cui = cui; +} + + +#ifdef CONFIG_HS20 + +static void ieee802_1x_hs20_sub_rem(struct sta_info *sta, u8 *pos, size_t len) +{ + sta->remediation = 1; + os_free(sta->remediation_url); + if (len > 2) { + sta->remediation_url = os_malloc(len); + if (!sta->remediation_url) + return; + sta->remediation_method = pos[0]; + os_memcpy(sta->remediation_url, pos + 1, len - 1); + sta->remediation_url[len - 1] = '\0'; + wpa_printf(MSG_DEBUG, "HS 2.0: Subscription remediation needed " + "for " MACSTR " - server method %u URL %s", + MAC2STR(sta->addr), sta->remediation_method, + sta->remediation_url); + } else { + sta->remediation_url = NULL; + wpa_printf(MSG_DEBUG, "HS 2.0: Subscription remediation needed " + "for " MACSTR, MAC2STR(sta->addr)); + } + /* TODO: assign the STA into remediation VLAN or add filtering */ +} + + +static void ieee802_1x_hs20_deauth_req(struct hostapd_data *hapd, + struct sta_info *sta, u8 *pos, + size_t len) +{ + if (len < 3) + return; /* Malformed information */ + sta->hs20_deauth_requested = 1; + wpa_printf(MSG_DEBUG, "HS 2.0: Deauthentication request - Code %u " + "Re-auth Delay %u", + *pos, WPA_GET_LE16(pos + 1)); + wpabuf_free(sta->hs20_deauth_req); + sta->hs20_deauth_req = wpabuf_alloc(len + 1); + if (sta->hs20_deauth_req) { + wpabuf_put_data(sta->hs20_deauth_req, pos, 3); + wpabuf_put_u8(sta->hs20_deauth_req, len - 3); + wpabuf_put_data(sta->hs20_deauth_req, pos + 3, len - 3); + } + ap_sta_session_timeout(hapd, sta, hapd->conf->hs20_deauth_req_timeout); +} + + +static void ieee802_1x_hs20_session_info(struct hostapd_data *hapd, + struct sta_info *sta, u8 *pos, + size_t len, int session_timeout) +{ + unsigned int swt; + int warning_time, beacon_int; + + if (len < 1) + return; /* Malformed information */ + os_free(sta->hs20_session_info_url); + sta->hs20_session_info_url = os_malloc(len); + if (sta->hs20_session_info_url == NULL) + return; + swt = pos[0]; + os_memcpy(sta->hs20_session_info_url, pos + 1, len - 1); + sta->hs20_session_info_url[len - 1] = '\0'; + wpa_printf(MSG_DEBUG, "HS 2.0: Session Information URL='%s' SWT=%u " + "(session_timeout=%d)", + sta->hs20_session_info_url, swt, session_timeout); + if (session_timeout < 0) { + wpa_printf(MSG_DEBUG, "HS 2.0: No Session-Timeout set - ignore session info URL"); + return; + } + if (swt == 255) + swt = 1; /* Use one minute as the AP selected value */ + + if ((unsigned int) session_timeout < swt * 60) + warning_time = 0; + else + warning_time = session_timeout - swt * 60; + + beacon_int = hapd->iconf->beacon_int; + if (beacon_int < 1) + beacon_int = 100; /* best guess */ + sta->hs20_disassoc_timer = swt * 60 * 1000 / beacon_int * 125 / 128; + if (sta->hs20_disassoc_timer > 65535) + sta->hs20_disassoc_timer = 65535; + + ap_sta_session_warning_timeout(hapd, sta, warning_time); +} + +#endif /* CONFIG_HS20 */ + + +static void ieee802_1x_check_hs20(struct hostapd_data *hapd, + struct sta_info *sta, + struct radius_msg *msg, + int session_timeout) +{ +#ifdef CONFIG_HS20 + u8 *buf, *pos, *end, type, sublen; + size_t len; + + buf = NULL; + sta->remediation = 0; + sta->hs20_deauth_requested = 0; + + for (;;) { + if (radius_msg_get_attr_ptr(msg, RADIUS_ATTR_VENDOR_SPECIFIC, + &buf, &len, buf) < 0) + break; + if (len < 6) + continue; + pos = buf; + end = buf + len; + if (WPA_GET_BE32(pos) != RADIUS_VENDOR_ID_WFA) + continue; + pos += 4; + + type = *pos++; + sublen = *pos++; + if (sublen < 2) + continue; /* invalid length */ + sublen -= 2; /* skip header */ + if (pos + sublen > end) + continue; /* invalid WFA VSA */ + + switch (type) { + case RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION: + ieee802_1x_hs20_sub_rem(sta, pos, sublen); + break; + case RADIUS_VENDOR_ATTR_WFA_HS20_DEAUTH_REQ: + ieee802_1x_hs20_deauth_req(hapd, sta, pos, sublen); + break; + case RADIUS_VENDOR_ATTR_WFA_HS20_SESSION_INFO_URL: + ieee802_1x_hs20_session_info(hapd, sta, pos, sublen, + session_timeout); + break; + } + } +#endif /* CONFIG_HS20 */ +} + + struct sta_id_search { u8 identifier; struct eapol_state_machine *sm; @@ -1189,7 +1592,7 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, struct hostapd_data *hapd = data; struct sta_info *sta; u32 session_timeout = 0, termination_action, acct_interim_interval; - int session_timeout_set, old_vlanid = 0; + int session_timeout_set, vlan_id = 0; struct eapol_state_machine *sm; int override_eapReq = 0; struct radius_hdr *hdr = radius_msg_get_hdr(msg); @@ -1213,15 +1616,14 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, "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"); + wpa_printf(MSG_INFO, "Incoming RADIUS packet did not have correct Message-Authenticator - dropped"); 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"); + wpa_printf(MSG_INFO, "Unknown RADIUS message code"); return RADIUS_RX_UNKNOWN; } @@ -1257,21 +1659,27 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, switch (hdr->code) { case RADIUS_CODE_ACCESS_ACCEPT: - if (sta->ssid->dynamic_vlan == DYNAMIC_VLAN_DISABLED) - sta->vlan_id = 0; + if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED) + 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)) { + else + vlan_id = radius_msg_get_vlanid(msg); + if (vlan_id > 0 && + hostapd_vlan_id_valid(hapd->conf->vlan, 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) { + "VLAN ID %d", vlan_id); + } else if (vlan_id > 0) { + sta->eapol_sm->authFail = TRUE; + hostapd_logger(hapd, sta->addr, + HOSTAPD_MODULE_RADIUS, + HOSTAPD_LEVEL_INFO, + "Invalid VLAN ID %d received from RADIUS server", + vlan_id); + break; + } else if (hapd->conf->ssid.dynamic_vlan == + DYNAMIC_VLAN_REQUIRED) { sta->eapol_sm->authFail = TRUE; hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, @@ -1282,9 +1690,14 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, } #endif /* CONFIG_NO_VLAN */ - if (ap_sta_bind_vlan(hapd, sta, old_vlanid) < 0) + sta->vlan_id = vlan_id; + if ((sta->flags & WLAN_STA_ASSOC) && + ap_sta_bind_vlan(hapd, sta) < 0) break; + sta->session_timeout_set = !!session_timeout_set; + sta->session_timeout = session_timeout; + /* RFC 3580, Ch. 3.17 */ if (session_timeout_set && termination_action == RADIUS_TERMINATION_ACTION_RADIUS_REQUEST) { @@ -1298,7 +1711,12 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req, shared_secret_len); ieee802_1x_store_radius_class(hapd, sta, msg); ieee802_1x_update_sta_identity(hapd, sta, msg); - if (sm->eap_if->eapKeyAvailable && + ieee802_1x_update_sta_cui(hapd, sta, msg); + ieee802_1x_check_hs20(hapd, sta, msg, + session_timeout_set ? + (int) session_timeout : -1); + if (sm->eap_if->eapKeyAvailable && !sta->remediation && + !sta->hs20_deauth_requested && wpa_auth_pmksa_add(sta->wpa_sm, sm->eapol_key_crypt, session_timeout_set ? (int) session_timeout : -1, sm) == 0) { @@ -1363,6 +1781,9 @@ void ieee802_1x_abort_auth(struct hostapd_data *hapd, struct sta_info *sta) * request and we cannot continue EAP processing (EAP-Failure * could only be sent if the EAP peer actually replied). */ + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "EAP Timeout, STA " MACSTR, + MAC2STR(sta->addr)); + sm->eap_if->portEnabled = FALSE; ap_sta_disconnect(hapd, sta, sta->addr, WLAN_REASON_PREV_AUTH_NOT_VALID); @@ -1380,9 +1801,9 @@ static int ieee802_1x_rekey_broadcast(struct hostapd_data *hapd) 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"); + random_get_bytes(eapol->default_wep_key, + hapd->conf->default_wep_key_len)) { + wpa_printf(MSG_INFO, "Could not generate random WEP key"); os_free(eapol->default_wep_key); eapol->default_wep_key = NULL; return -1; @@ -1432,10 +1853,11 @@ static void ieee802_1x_rekey(void *eloop_ctx, void *timeout_ctx) /* TODO: Could setup key for RX here, but change default TX keyid only * after new broadcast key has been sent to all stations. */ - if (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)) { + if (hostapd_drv_set_key(hapd->conf->iface, hapd, WPA_ALG_WEP, + broadcast_ether_addr, + 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"); @@ -1497,14 +1919,14 @@ static void ieee802_1x_aaa_send(void *ctx, void *sta_ctx, static void _ieee802_1x_finished(void *ctx, void *sta_ctx, int success, - int preauth) + int preauth, int remediation) { 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); + ieee802_1x_finished(hapd, sta, success, remediation); } @@ -1514,19 +1936,16 @@ static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity, { struct hostapd_data *hapd = ctx; const struct hostapd_eap_user *eap_user; - int i, count; + int i; + int rv = -1; - eap_user = hostapd_get_eap_user(hapd->conf, identity, - identity_len, phase2); + eap_user = hostapd_get_eap_user(hapd, identity, identity_len, phase2); if (eap_user == NULL) - return -1; + goto out; 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++) { + for (i = 0; i < EAP_MAX_METHODS; i++) { user->methods[i].vendor = eap_user->methods[i].vendor; user->methods[i].method = eap_user->methods[i].method; } @@ -1534,15 +1953,23 @@ static int ieee802_1x_get_eap_user(void *ctx, const u8 *identity, if (eap_user->password) { user->password = os_malloc(eap_user->password_len); if (user->password == NULL) - return -1; + goto out; 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->macacl = eap_user->macacl; user->ttls_auth = eap_user->ttls_auth; + user->remediation = eap_user->remediation; + rv = 0; - return 0; +out: + if (rv) + wpa_printf(MSG_DEBUG, "%s: Failed to find user", __func__); + + return rv; } @@ -1602,9 +2029,13 @@ static void _ieee802_1x_abort_auth(void *ctx, void *sta_ctx) static void _ieee802_1x_tx_key(void *ctx, void *sta_ctx) { +#ifndef CONFIG_FIPS +#ifndef CONFIG_NO_RC4 struct hostapd_data *hapd = ctx; struct sta_info *sta = sta_ctx; ieee802_1x_tx_key(hapd, sta); +#endif /* CONFIG_NO_RC4 */ +#endif /* CONFIG_FIPS */ } @@ -1624,12 +2055,43 @@ static void ieee802_1x_eapol_event(void *ctx, void *sta_ctx, } +#ifdef CONFIG_ERP + +static struct eap_server_erp_key * +ieee802_1x_erp_get_key(void *ctx, const char *keyname) +{ + struct hostapd_data *hapd = ctx; + struct eap_server_erp_key *erp; + + dl_list_for_each(erp, &hapd->erp_keys, struct eap_server_erp_key, + list) { + if (os_strcmp(erp->keyname_nai, keyname) == 0) + return erp; + } + + return NULL; +} + + +static int ieee802_1x_erp_add_key(void *ctx, struct eap_server_erp_key *erp) +{ + struct hostapd_data *hapd = ctx; + + dl_list_add(&hapd->erp_keys, &erp->list); + return 0; +} + +#endif /* CONFIG_ERP */ + + int ieee802_1x_init(struct hostapd_data *hapd) { int i; struct eapol_auth_config conf; struct eapol_auth_cb cb; + dl_list_init(&hapd->erp_keys); + os_memset(&conf, 0, sizeof(conf)); conf.ctx = hapd; conf.eap_reauth_period = hapd->conf->eap_reauth_period; @@ -1641,6 +2103,10 @@ int ieee802_1x_init(struct hostapd_data *hapd) 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.erp_send_reauth_start = hapd->conf->erp_send_reauth_start; + conf.erp_domain = hapd->conf->erp_domain; + conf.erp = hapd->conf->eap_server_erp; + conf.tls_session_lifetime = hapd->conf->tls_session_lifetime; 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; @@ -1653,6 +2119,14 @@ int ieee802_1x_init(struct hostapd_data *hapd) conf.wps = hapd->wps; conf.fragment_size = hapd->conf->fragment_size; conf.pwd_group = hapd->conf->pwd_group; + conf.pbc_in_m1 = hapd->conf->pbc_in_m1; + if (hapd->conf->server_id) { + conf.server_id = (const u8 *) hapd->conf->server_id; + conf.server_id_len = os_strlen(hapd->conf->server_id); + } else { + conf.server_id = (const u8 *) "hostapd"; + conf.server_id_len = 7; + } os_memset(&cb, 0, sizeof(cb)); cb.eapol_send = ieee802_1x_eapol_send; @@ -1665,13 +2139,17 @@ int ieee802_1x_init(struct hostapd_data *hapd) cb.abort_auth = _ieee802_1x_abort_auth; cb.tx_key = _ieee802_1x_tx_key; cb.eapol_event = ieee802_1x_eapol_event; +#ifdef CONFIG_ERP + cb.erp_get_key = ieee802_1x_erp_get_key; + cb.erp_add_key = ieee802_1x_erp_add_key; +#endif /* CONFIG_ERP */ 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)) + hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 1)) return -1; #ifndef CONFIG_NO_RADIUS @@ -1682,9 +2160,9 @@ int ieee802_1x_init(struct hostapd_data *hapd) 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); + hostapd_drv_set_key(hapd->conf->iface, hapd, + WPA_ALG_NONE, NULL, i, 0, NULL, 0, + NULL, 0); ieee802_1x_rekey(hapd, NULL); @@ -1696,16 +2174,30 @@ int ieee802_1x_init(struct hostapd_data *hapd) } +void ieee802_1x_erp_flush(struct hostapd_data *hapd) +{ + struct eap_server_erp_key *erp; + + while ((erp = dl_list_first(&hapd->erp_keys, struct eap_server_erp_key, + list)) != NULL) { + dl_list_del(&erp->list); + bin_clear_free(erp, sizeof(*erp)); + } +} + + 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); + hostapd_set_drv_ieee8021x(hapd, hapd->conf->iface, 0); eapol_auth_deinit(hapd->eapol_auth); hapd->eapol_auth = NULL; + + ieee802_1x_erp_flush(hapd); } @@ -1713,15 +2205,13 @@ 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)) + if (len < sizeof(*hdr) + sizeof(rfc1042_hdr) + 2) return 0; hdr = (struct ieee80211_hdr *) buf; @@ -1733,21 +2223,44 @@ int ieee802_1x_tx_status(struct hostapd_data *hapd, struct sta_info *sta, return 0; pos += 2; - xhdr = (struct ieee802_1x_hdr *) pos; - pos += sizeof(*xhdr); + return ieee802_1x_eapol_tx_status(hapd, sta, pos, buf + len - pos, + ack); +} + + +int ieee802_1x_eapol_tx_status(struct hostapd_data *hapd, struct sta_info *sta, + const u8 *buf, int len, int ack) +{ + const struct ieee802_1x_hdr *xhdr = + (const struct ieee802_1x_hdr *) buf; + const u8 *pos = buf + sizeof(*xhdr); + struct ieee802_1x_eapol_key *key; + if (len < (int) sizeof(*xhdr)) + return 0; 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); + if (xhdr->type != IEEE802_1X_TYPE_EAPOL_KEY) + return 0; + + if (pos + sizeof(struct wpa_eapol_key) <= buf + len) { + const struct wpa_eapol_key *wpa; + wpa = (const struct wpa_eapol_key *) pos; + if (wpa->type == EAPOL_KEY_TYPE_RSN || + wpa->type == EAPOL_KEY_TYPE_WPA) + wpa_auth_eapol_key_tx_status(hapd->wpa_auth, + sta->wpa_sm, 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 + * retransmitted in case of failure. Try to re-send 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) { + if (!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 " @@ -1791,8 +2304,17 @@ u8 * ieee802_1x_get_radius_class(struct eapol_state_machine *sm, size_t *len, } +struct wpabuf * ieee802_1x_get_radius_cui(struct eapol_state_machine *sm) +{ + if (sm == NULL) + return NULL; + return sm->radius_cui; +} + + const u8 * ieee802_1x_get_key(struct eapol_state_machine *sm, size_t *len) { + *len = 0; if (sm == NULL) return NULL; @@ -1832,9 +2354,9 @@ void ieee802_1x_notify_pre_auth(struct eapol_state_machine *sm, int pre_auth) } -static const char * bool_txt(Boolean bool) +static const char * bool_txt(Boolean val) { - return bool ? "TRUE" : "FALSE"; + return val ? "TRUE" : "FALSE"; } @@ -1850,6 +2372,9 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, { int len = 0, ret; struct eapol_state_machine *sm = sta->eapol_sm; + struct os_reltime diff; + const char *name1; + const char *name2; if (sm == NULL) return 0; @@ -1863,7 +2388,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, sta->aid, EAPOL_VERSION, sm->initialize); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -1891,7 +2416,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, sm->reAuthPeriod, bool_txt(sm->reAuthEnabled), bool_txt(sm->keyTxEnabled)); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -1921,7 +2446,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, sm->dot1xAuthEapLengthErrorFramesRx, sm->dot1xAuthLastEapolFrameVersion, MAC2STR(sm->addr)); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -1959,11 +2484,12 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, sm->backendOtherRequestsToSupplicant, sm->backendAuthSuccesses, sm->backendAuthFails); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; /* dot1xAuthSessionStatsTable */ + os_reltime_age(&sta->acct_session_start, &diff); ret = os_snprintf(buf + len, buflen - len, /* TODO: dot1xAuthSessionOctetsRx */ /* TODO: dot1xAuthSessionOctetsTx */ @@ -1978,10 +2504,30 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, (wpa_key_mgmt_wpa_ieee8021x( wpa_auth_sta_key_mgmt(sta->wpa_sm))) ? 1 : 2, - (unsigned int) (time(NULL) - - sta->acct_session_start), + (unsigned int) diff.sec, sm->identity); - if (ret < 0 || (size_t) ret >= buflen - len) + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + + if (sm->acct_multi_session_id_hi) { + ret = os_snprintf(buf + len, buflen - len, + "authMultiSessionId=%08X+%08X\n", + sm->acct_multi_session_id_hi, + sm->acct_multi_session_id_lo); + if (os_snprintf_error(buflen - len, ret)) + return len; + len += ret; + } + + name1 = eap_server_get_name(0, sm->eap_type_authsrv); + name2 = eap_server_get_name(0, sm->eap_type_supp); + ret = os_snprintf(buf + len, buflen - len, + "last_eap_type_as=%d (%s)\n" + "last_eap_type_sta=%d (%s)\n", + sm->eap_type_authsrv, name1, + sm->eap_type_supp, name2); + if (os_snprintf_error(buflen - len, ret)) return len; len += ret; @@ -1990,38 +2536,81 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta, static void ieee802_1x_finished(struct hostapd_data *hapd, - struct sta_info *sta, int success) + struct sta_info *sta, int success, + int remediation) { const u8 *key; size_t len; /* TODO: get PMKLifetime from WPA parameters */ static const int dot11RSNAConfigPMKLifetime = 43200; + unsigned int session_timeout; + +#ifdef CONFIG_HS20 + if (remediation && !sta->remediation) { + sta->remediation = 1; + os_free(sta->remediation_url); + sta->remediation_url = + os_strdup(hapd->conf->subscr_remediation_url); + sta->remediation_method = 1; /* SOAP-XML SPP */ + } + + if (success) { + if (sta->remediation) { + wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification " + "to " MACSTR " to indicate Subscription " + "Remediation", + MAC2STR(sta->addr)); + hs20_send_wnm_notification(hapd, sta->addr, + sta->remediation_method, + sta->remediation_url); + os_free(sta->remediation_url); + sta->remediation_url = NULL; + } + + if (sta->hs20_deauth_req) { + wpa_printf(MSG_DEBUG, "HS 2.0: Send WNM-Notification " + "to " MACSTR " to indicate imminent " + "deauthentication", MAC2STR(sta->addr)); + hs20_send_wnm_notification_deauth_req( + hapd, sta->addr, sta->hs20_deauth_req); + } + } +#endif /* CONFIG_HS20 */ key = ieee802_1x_get_key(sta->eapol_sm, &len); - if (success && key && len >= PMK_LEN && - wpa_auth_pmksa_add(sta->wpa_sm, key, dot11RSNAConfigPMKLifetime, + if (sta->session_timeout_set) + session_timeout = sta->session_timeout; + else + session_timeout = dot11RSNAConfigPMKLifetime; + if (success && key && len >= PMK_LEN && !sta->remediation && + !sta->hs20_deauth_requested && + wpa_auth_pmksa_add(sta->wpa_sm, key, session_timeout, 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)) { + if (!success) { /* * Many devices require deauthentication after WPS provisioning * and some may not be be able to do that themselves, so - * disconnect the client here. + * disconnect the client here. In addition, this may also + * benefit IEEE 802.1X/EAPOL authentication cases, too since + * the EAPOL PAE state machine would remain in HELD state for + * considerable amount of time and some EAP methods, like + * EAP-FAST with anonymous provisioning, may require another + * EAPOL authentication to be started to complete connection. */ - wpa_printf(MSG_DEBUG, "WPS: Force disconnection after " - "EAP-Failure"); + wpa_dbg(hapd->msg_ctx, MSG_DEBUG, "IEEE 802.1X: 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); + WLAN_REASON_IEEE_802_1X_AUTH_FAILED); + hostapd_wps_eap_completed(hapd); } -#endif /* CONFIG_WPS */ }