480 lines
12 KiB
C

/*
* hostapd / IEEE 802.1X-2004 Authenticator
* Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "utils/includes.h"
#include "utils/common.h"
#include "utils/eloop.h"
#include "crypto/crypto.h"
#include "common/ieee802_11_defs.h"
#include "hostapd.h"
#include "ap/sta_info.h"
#include "ap/wpa_auth.h"
#include "eap_server/eap.h"
#include "ap/ap_config.h"
#include "eap_common/eap_wsc_common.h"
#include "ap/ieee802_1x.h"
#include "utils/wpa_debug.h"
#include "eapol_auth/eapol_auth_sm.h"
#include "eapol_auth/eapol_auth_sm_i.h"
#include "eap_server/eap.h"
#include "sta_info.h"
#include "ieee802_1x.h"
int hostapd_send_eapol(const u8 *source, const u8 *sta_addr,
const u8 *data, size_t data_len);
static void ieee802_1x_finished(struct hostapd_data *hapd,
struct sta_info *sta, int success,
int remediation);
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;
len = sizeof(*xhdr) + datalen;
buf = os_zalloc(len);
if (!buf) {
wpa_printf(MSG_ERROR, "malloc() failed for %s(len=%lu)",
__func__, (unsigned long) len);
return;
}
xhdr = (struct ieee802_1x_hdr *) buf;
xhdr->version = EAPOL_VERSION;
xhdr->type = type;
xhdr->length = host_to_be16(datalen);
if (datalen > 0 && data != NULL)
os_memcpy(xhdr + 1, data, datalen);
hostapd_send_eapol(hapd->own_addr, sta->addr, buf, len);
os_free(buf);
}
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)
return;
data = (u8 *) (eap + 1);
if (len < sizeof(*eap) + 1) {
wpa_printf(MSG_INFO, "%s: too short response data", __func__);
return;
}
sm->eap_type_supp = type = data[0];
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)) {
wpa_printf(MSG_INFO, " too short EAP packet");
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_RESPONSE:
handle_eap_response(hapd, sta, eap, eap_len);
break;
case EAP_CODE_INITIATE:
break;
}
}
struct eapol_state_machine *
ieee802_1x_alloc_eapol_sm(struct hostapd_data *hapd, struct sta_info *sta)
{
int flags = 0;
if (sta->wpa_sm) {
flags |= EAPOL_SM_USES_WPA;
}
return eapol_auth_alloc(hapd->eapol_auth, sta->addr, flags,
sta->wps_ie, NULL, sta,
sta->identity, NULL);
}
/**
* 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;
wpa_printf(MSG_DEBUG, "IEEE 802.1X: %lu bytes from " MACSTR,
(unsigned long) len, MAC2STR(sa));
sta = ap_get_sta(hapd, sa);
if (!sta) {
wpa_printf(MSG_DEBUG,
"IEEE 802.1X data frame from not associated/Pre-authenticating STA");
return;
}
if (len < sizeof(*hdr)) {
wpa_printf(MSG_INFO, " too short IEEE 802.1X packet");
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) {
wpa_printf(MSG_INFO,
" frame too short for this IEEE 802.1X packet");
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 (!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 && 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 */
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:
sta->eapol_sm->flags &= ~EAPOL_SM_WAIT_START;
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;
case IEEE802_1X_TYPE_EAPOL_LOGOFF:
break;
case IEEE802_1X_TYPE_EAPOL_KEY:
wpa_printf(MSG_DEBUG, " EAPOL-Key");
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);
}
void ieee802_1x_free_station(struct hostapd_data *hapd, struct sta_info *sta)
{
struct eapol_state_machine *sm = sta->eapol_sm;
if (!sm)
return;
sta->eapol_sm = NULL;
eapol_auth_free(sm);
}
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, int remediation)
{
struct hostapd_data *hapd = ctx;
struct sta_info *sta = sta_ctx;
ieee802_1x_finished(hapd, sta, success, remediation);
}
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;
int rv = -1;
eap_user = hostapd_get_eap_user(hapd, identity, identity_len, phase2);
if (!eap_user)
goto out;
os_memset(user, 0, sizeof(*user));
user->phase2 = phase2;
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;
}
if (eap_user->password) {
user->password = os_memdup(eap_user->password,
eap_user->password_len);
if (!user->password)
goto out;
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;
rv = 0;
out:
if (rv)
wpa_printf(MSG_DEBUG, "%s: Failed to find user", __func__);
return rv;
}
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 || !sta->eapol_sm)
return 0;
return 1;
}
static void ieee802_1x_set_port_authorized(void *ctx, void *sta_ctx,
int authorized)
{
}
static void _ieee802_1x_abort_auth(void *ctx, void *sta_ctx)
{
}
static void ieee802_1x_eapol_event(void *ctx, void *sta_ctx,
enum eapol_event type)
{
#if 0
/* 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;
}
#endif
}
int ieee802_1x_init(struct hostapd_data *hapd)
{
struct eapol_auth_config conf;
struct eapol_auth_cb cb;
struct eap_config *eap_cfg;
os_memset(&conf, 0, sizeof(conf));
eap_cfg = os_zalloc(sizeof(struct eap_config));
eap_cfg->max_auth_rounds = 100;
eap_cfg->max_auth_rounds_short = 50;
//eap_cfg->backend_auth = 1;
eap_cfg->eap_server = 1;
conf.eap_cfg = eap_cfg;
conf.ctx = hapd;
conf.wpa = hapd->conf->wpa;
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.set_port_authorized = ieee802_1x_set_port_authorized;
cb.abort_auth = _ieee802_1x_abort_auth;
cb.eapol_event = ieee802_1x_eapol_event;
hapd->eapol_auth = eapol_auth_init(&conf, &cb);
if (!hapd->eapol_auth)
return -1;
return 0;
}
static void ieee802_1x_finished(struct hostapd_data *hapd,
struct sta_info *sta, int success,
int remediation)
{
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. 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.
*/
ap_sta_delayed_1x_auth_fail_disconnect(hapd, sta);
}
}