📄 ieee802_1x.c
字号:
/* * hostapd / IEEE 802.1X-2004 Authenticator * Copyright (c) 2002-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 "hostapd.h"#include "ieee802_1x.h"#include "accounting.h"#include "radius/radius.h"#include "radius/radius_client.h"#include "eapol_sm.h"#include "md5.h"#include "rc4.h"#include "eloop.h"#include "sta_info.h"#include "wpa.h"#include "preauth.h"#include "pmksa_cache.h"#include "driver.h"#include "hw_features.h"#include "eap_server/eap.h"#include "ieee802_11_defs.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 { hostapd_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) { sta->flags |= WLAN_STA_AUTHORIZED; res = hostapd_sta_set_flags(hapd, sta->addr, sta->flags, WLAN_STA_AUTHORIZED, ~0); hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X, HOSTAPD_LEVEL_DEBUG, "authorizing port"); } else { sta->flags &= ~WLAN_STA_AUTHORIZED; res = hostapd_sta_set_flags(hapd, sta->addr, sta->flags, 0, ~WLAN_STA_AUTHORIZED); 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((u8 *) (key + 1), key_len, ekey, ekey_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);}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 (hostapd_set_encryption(ifname, hapd, "WEP", NULL, key->idx, key->key[key->idx], key->len[key->idx], 1)) printf("Could not set dynamic VLAN WEP encryption key.\n"); hostapd_set_ieee8021x(ifname, hapd, 1); return key;}static struct hostapd_wep_keys *ieee802_1x_get_group(struct hostapd_data *hapd, struct hostapd_ssid *ssid, size_t vlan_id){ const char *ifname; if (vlan_id == 0) return &ssid->wep; if (vlan_id <= ssid->max_dyn_vlan_keys && ssid->dyn_vlan_keys && ssid->dyn_vlan_keys[vlan_id]) return ssid->dyn_vlan_keys[vlan_id]; 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];}void ieee802_1x_tx_key(struct hostapd_data *hapd, struct sta_info *sta){ struct hostapd_wep_keys *key = NULL; struct eapol_state_machine *sm = sta->eapol_sm; int vlan_id; if (sm == NULL || !sm->eap_if->eapKeyData) return; wpa_printf(MSG_DEBUG, "IEEE 802.1X: Sending EAPOL-Key(s) to " MACSTR, MAC2STR(sta->addr)); vlan_id = sta->vlan_id; if (vlan_id < 0 || vlan_id > MAX_VLAN_ID) vlan_id = 0; if (vlan_id) { key = ieee802_1x_get_group(hapd, sta->ssid, vlan_id); if (key && key->key[key->idx]) ieee802_1x_tx_key_one(hapd, sta, key->idx, 1, key->key[key->idx], key->len[key->idx]); } else if (hapd->default_wep_key) { ieee802_1x_tx_key_one(hapd, sta, hapd->default_wep_key_idx, 1, hapd->default_wep_key, hapd->conf->default_wep_key_len); } if (hapd->conf->individual_wep_key_len > 0) { u8 *ikey; 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 (hostapd_set_encryption(hapd->conf->iface, hapd, "WEP", sta->addr, 0, ikey, hapd->conf->individual_wep_key_len, 1)) { wpa_printf(MSG_ERROR, "Could not set individual WEP " "encryption."); } os_free(ikey); }}const char *radius_mode_txt(struct hostapd_data *hapd){ if (hapd->iface->current_mode == NULL) return "802.11"; switch (hapd->iface->current_mode->mode) { case HOSTAPD_MODE_IEEE80211A: return "802.11a"; case HOSTAPD_MODE_IEEE80211G: return "802.11g"; case HOSTAPD_MODE_IEEE80211B: default: return "802.11b"; }}int radius_sta_rate(struct hostapd_data *hapd, struct sta_info *sta){ int i; u8 rate = 0; for (i = 0; i < sta->supported_rates_len; i++) if ((sta->supported_rates[i] & 0x7f) > rate) rate = sta->supported_rates[i] & 0x7f; return rate;}static void ieee802_1x_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))) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -