IEEE 802.11w: Added association ping

This updates management frame protection to use the assocition ping process
from the latest draft (D6.0) to protect against unauthenticated
authenticate or (re)associate frames dropping association.
This commit is contained in:
Jouni Malinen 2008-08-31 11:04:47 +03:00
parent 1e858f69d9
commit 5d22a1d5aa
13 changed files with 353 additions and 15 deletions

View file

@ -10,6 +10,9 @@ ChangeLog for hostapd
* added support for setting VLAN ID for STAs based on local MAC ACL
(accept_mac_file) as an alternative for RADIUS server-based
configuration
* updated management frame protection to use IEEE 802.11w/D6.0
(adds a new association ping to protect against unauthenticated
authenticate or (re)associate request frames dropping association)
2008-08-10 - v0.6.4
* added peer identity into EAP-FAST PAC-Opaque and skip Phase 2

View file

@ -93,6 +93,14 @@ struct sta_info {
#ifdef CONFIG_IEEE80211N
struct ht_cap_ie ht_capabilities; /* IEEE 802.11n capabilities */
#endif /* CONFIG_IEEE80211N */
#ifdef CONFIG_IEEE80211W
int ping_count; /* number of pending ping requests;
* 0 = no ping in progress */
int ping_timed_out;
u8 *ping_trans_id; /* buffer of WLAN_PING_TRANS_ID_LEN * ping_count
* octets of pending ping transaction identifiers */
#endif /* CONFIG_IEEE80211W */
};

View file

@ -1,6 +1,6 @@
/*
* hostapd / Configuration file
* Copyright (c) 2003-2007, Jouni Malinen <j@w1.fi>
* Copyright (c) 2003-2008, Jouni Malinen <j@w1.fi>
* Copyright (c) 2007-2008, Intel Corporation
*
* This program is free software; you can redistribute it and/or modify
@ -181,6 +181,11 @@ static void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
bss->eapol_version = EAPOL_VERSION;
bss->max_listen_interval = 65535;
#ifdef CONFIG_IEEE80211W
bss->assoc_ping_timeout = 1000;
bss->assoc_ping_attempts = 3;
#endif /* CONFIG_IEEE80211W */
}
@ -1965,6 +1970,21 @@ struct hostapd_config * hostapd_config_read(const char *fname)
#ifdef CONFIG_IEEE80211W
} else if (os_strcmp(buf, "ieee80211w") == 0) {
bss->ieee80211w = atoi(pos);
} else if (os_strcmp(buf, "assoc_ping_timeout") == 0) {
bss->assoc_ping_timeout = atoi(pos);
if (bss->assoc_ping_timeout == 0) {
printf("Line %d: invalid assoc_ping_timeout\n",
line);
errors++;
}
} else if (os_strcmp(buf, "assoc_ping_attempts") == 0) {
bss->assoc_ping_timeout = atoi(pos);
if (bss->assoc_ping_timeout == 0) {
printf("Line %d: invalid assoc_ping_attempts "
"(valid range: 1..255)\n",
line);
errors++;
}
#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_IEEE80211N
} else if (os_strcmp(buf, "ieee80211n") == 0) {

View file

@ -213,6 +213,10 @@ struct hostapd_bss_config {
IEEE80211W_OPTIONAL = 1,
IEEE80211W_REQUIRED = 2
} ieee80211w;
/* dot11AssociationPingResponseTimeout (in TU) */
unsigned int assoc_ping_timeout;
/* dot11AssociationMaximumPingAttempts */
int assoc_ping_attempts;
#endif /* CONFIG_IEEE80211W */
int wpa_pairwise;
int wpa_group;

View file

@ -704,12 +704,21 @@ own_ip_addr=127.0.0.1
# 1 = enabled
#peerkey=1
# ieee80211w: Whether management frame protection is enabled
# ieee80211w: Whether management frame protection (MFP) is enabled
# 0 = disabled (default)
# 1 = optional
# 2 = required
#ieee80211w=0
# Association ping timeout (in TU = 1.024 ms; for MFP)
# dot11AssociationPingResponseTimeout, 1...4294967295
#assoc_ping_timeout=1000
# Maximum number of association pings
# dot11AssociationMaximumPingAttempts , 1...255
#assoc_ping_attempts=3
# okc: Opportunistic Key Caching (aka Proactive Key Caching)
# Allow PMK cache to be shared opportunistically among configured interfaces
# and BSSes (i.e., all configurations within a single hostapd process).

View file

@ -371,6 +371,25 @@ static int ieee802_11_parse_vendor_specific(struct hostapd_data *hapd,
}
#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;
*pos++ = WLAN_EID_ASSOC_COMEBACK_TIME;
*pos++ = 4;
timeout = (hapd->conf->assoc_ping_attempts - sta->ping_count + 1) *
hapd->conf->assoc_ping_timeout;
WPA_PUT_LE32(pos, timeout);
pos += 4;
return pos;
}
#endif /* CONFIG_IEEE80211W */
ParseRes ieee802_11_parse_elems(struct hostapd_data *hapd, u8 *start,
size_t len,
struct ieee802_11_elems *elems,
@ -514,12 +533,10 @@ void ieee802_11_print_ssid(char *buf, const u8 *ssid, u8 len)
void ieee802_11_send_deauth(struct hostapd_data *hapd, u8 *addr, u16 reason)
{
struct ieee80211_mgmt mgmt;
char buf[30];
hostapd_logger(hapd, addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"deauthenticate - reason %d", reason);
os_snprintf(buf, sizeof(buf), "SEND-DEAUTHENTICATE %d", reason);
os_memset(&mgmt, 0, sizeof(mgmt));
mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_DEAUTH);
@ -1160,6 +1177,21 @@ static void handle_assoc(struct hostapd_data *hapd,
if (resp != WLAN_STATUS_SUCCESS)
goto fail;
#ifdef CONFIG_IEEE80211W
if ((sta->flags & WLAN_STA_MFP) && !sta->ping_timed_out) {
/*
* STA has already been associated with MFP and ping
* timeout has not been reached. Reject the
* association attempt temporarily and start ping, if
* one is not pending.
*/
if (sta->ping_count == 0)
ap_sta_start_ping(hapd, sta);
resp = WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY;
goto fail;
}
if (wpa_auth_uses_mfp(sta->wpa_sm))
sta->flags |= WLAN_STA_MFP;
else
@ -1352,6 +1384,11 @@ static void handle_assoc(struct hostapd_data *hapd,
}
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_IEEE80211W
if (resp == WLAN_STATUS_ASSOC_REJECTED_TEMPORARILY)
p = hostapd_eid_assoc_comeback_time(hapd, sta, p);
#endif /* CONFIG_IEEE80211W */
send_len += p - reply->u.assoc_resp.variable;
/* Request TX callback */
@ -1553,6 +1590,56 @@ static void handle_beacon(struct hostapd_data *hapd,
}
#ifdef CONFIG_IEEE80211W
static void hostapd_ping_action(struct hostapd_data *hapd,
struct ieee80211_mgmt *mgmt, size_t len)
{
struct sta_info *sta;
u8 *end;
int i;
end = mgmt->u.action.u.ping_resp.trans_id + WLAN_PING_TRANS_ID_LEN;
if (((u8 *) mgmt) + len < end) {
wpa_printf(MSG_DEBUG, "IEEE 802.11: Too short Ping Action "
"frame (len=%lu)", (unsigned long) len);
return;
}
if (mgmt->u.action.u.ping_resp.action != WLAN_PING_RESPONSE) {
wpa_printf(MSG_DEBUG, "IEEE 802.11: Unexpected Ping Action %d",
mgmt->u.action.u.ping_resp.action);
return;
}
/* MLME-PING.confirm */
sta = ap_get_sta(hapd, mgmt->sa);
if (sta == NULL || sta->ping_trans_id == NULL) {
wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with "
"pending ping request found");
return;
}
for (i = 0; i < sta->ping_count; i++) {
if (os_memcmp(sta->ping_trans_id + i * WLAN_PING_TRANS_ID_LEN,
mgmt->u.action.u.ping_resp.trans_id,
WLAN_PING_TRANS_ID_LEN) == 0)
break;
}
if (i >= sta->ping_count) {
wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching ping "
"transaction identifier found");
return;
}
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG, "Reply to pending ping received");
ap_sta_stop_ping(hapd, sta);
}
#endif /* CONFIG_IEEE80211W */
static void handle_action(struct hostapd_data *hapd,
struct ieee80211_mgmt *mgmt, size_t len)
{
@ -1588,6 +1675,11 @@ static void handle_action(struct hostapd_data *hapd,
case WME_ACTION_CATEGORY:
hostapd_wme_action(hapd, mgmt, len);
return;
#ifdef CONFIG_IEEE80211W
case WLAN_ACTION_PING:
hostapd_ping_action(hapd, mgmt, len);
return;
#endif /* CONFIG_IEEE80211W */
}
hostapd_logger(hapd, mgmt->sa, HOSTAPD_MODULE_IEEE80211,
@ -1811,6 +1903,10 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
ht_cap = &sta->ht_capabilities;
#endif /* CONFIG_IEEE80211N */
#ifdef CONFIG_IEEE80211W
sta->ping_timed_out = 0;
#endif /* CONFIG_IEEE80211W */
if (hostapd_sta_add(hapd->conf->iface, hapd, sta->addr, sta->aid,
sta->capability, sta->supported_rates,
sta->supported_rates_len, 0, sta->listen_interval,
@ -1881,6 +1977,9 @@ void ieee802_11_mgmt_cb(struct hostapd_data *hapd, u8 *buf, size_t len,
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;

View file

@ -58,7 +58,7 @@ void mlme_authenticate_indication(struct hostapd_data *hapd,
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)
if (sta->auth_alg != WLAN_AUTH_FT && !(sta->flags & WLAN_STA_MFP))
mlme_deletekeys_request(hapd, sta);
}

View file

@ -1,6 +1,6 @@
/*
* hostapd / Station table
* Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
* Copyright (c) 2002-2008, Jouni Malinen <j@w1.fi>
* Copyright (c) 2007-2008, Intel Corporation
*
* This program is free software; you can redistribute it and/or modify
@ -34,6 +34,9 @@
static int ap_sta_in_other_bss(struct hostapd_data *hapd,
struct sta_info *sta, u32 flags);
static void ap_handle_session_timer(void *eloop_ctx, void *timeout_ctx);
#ifdef CONFIG_IEEE80211W
static void ap_ping_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,
@ -179,6 +182,12 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
os_free(sta->last_assoc_req);
os_free(sta->challenge);
#ifdef CONFIG_IEEE80211W
os_free(sta->ping_trans_id);
eloop_cancel_timeout(ap_ping_timer, hapd, sta);
#endif /* CONFIG_IEEE80211W */
os_free(sta);
}
@ -594,3 +603,87 @@ int ap_sta_bind_vlan(struct hostapd_data *hapd, struct sta_info *sta,
return hostapd_set_sta_vlan(iface, hapd, sta->addr, sta->vlan_id);
}
#ifdef CONFIG_IEEE80211W
/* MLME-PING.request */
static void ieee802_11_send_ping_req(struct hostapd_data *hapd, const u8 *addr,
const u8 *trans_id)
{
struct ieee80211_mgmt mgmt;
u8 *end;
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_PING;
mgmt.u.action.u.ping_req.action = WLAN_PING_REQUEST;
os_memcpy(mgmt.u.action.u.ping_req.trans_id, trans_id,
WLAN_PING_TRANS_ID_LEN);
end = mgmt.u.action.u.ping_req.trans_id + WLAN_PING_TRANS_ID_LEN;
if (hostapd_send_mgmt_frame(hapd, &mgmt, IEEE80211_HDRLEN +
end - (u8 *) &mgmt, 0) < 0)
perror("ieee802_11_send_ping_req: send");
}
static void ap_ping_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->ping_count >= hapd->conf->assoc_ping_attempts) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"association ping timed out");
sta->ping_timed_out = 1;
os_free(sta->ping_trans_id);
sta->ping_trans_id = NULL;
sta->ping_count = 0;
return;
}
nbuf = os_realloc(sta->ping_trans_id,
(sta->ping_count + 1) * WLAN_PING_TRANS_ID_LEN);
if (nbuf == NULL)
return;
trans_id = nbuf + sta->ping_count * WLAN_PING_TRANS_ID_LEN;
sta->ping_trans_id = nbuf;
sta->ping_count++;
os_get_random(trans_id, WLAN_PING_TRANS_ID_LEN);
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"association ping attempt %d", sta->ping_count);
ieee802_11_send_ping_req(hapd, sta->addr, trans_id);
timeout = hapd->conf->assoc_ping_timeout;
sec = ((timeout / 1000) * 1024) / 1000;
usec = (timeout % 1000) * 1024;
eloop_register_timeout(sec, usec, ap_ping_timer, hapd, sta);
}
void ap_sta_start_ping(struct hostapd_data *hapd, struct sta_info *sta)
{
ap_ping_timer(hapd, sta);
}
void ap_sta_stop_ping(struct hostapd_data *hapd, struct sta_info *sta)
{
eloop_cancel_timeout(ap_ping_timer, hapd, sta);
os_free(sta->ping_trans_id);
sta->ping_trans_id = NULL;
sta->ping_count = 0;
}
#endif /* CONFIG_IEEE80211W */

View file

@ -36,5 +36,7 @@ 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_ping(struct hostapd_data *hapd, struct sta_info *sta);
void ap_sta_stop_ping(struct hostapd_data *hapd, struct sta_info *sta);
#endif /* STA_INFO_H */

View file

@ -1081,6 +1081,8 @@ void wpa_remove_ptk(struct wpa_state_machine *sm)
void wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event)
{
int remove_ptk = 1;
if (sm == NULL)
return;
@ -1113,11 +1115,18 @@ void wpa_auth_sm_event(struct wpa_state_machine *sm, wpa_event event)
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);
}
wpa_sm_step(sm);
}

View file

@ -218,6 +218,12 @@
#define WLAN_ACTION_FT 6
#define WLAN_ACTION_PING 8
/* Ping Action frame (IEEE 802.11w/D6.0, 7.4.9) */
#define WLAN_PING_REQUEST 0
#define WLAN_PING_RESPONSE 1
#define WLAN_PING_TRANS_ID_LEN 16
#ifdef _MSC_VER
#pragma pack(push, 1)
@ -316,11 +322,11 @@ struct ieee80211_mgmt {
} STRUCT_PACKED ft_action_resp;
struct {
u8 action;
u8 transaction_id[16];
u8 trans_id[WLAN_PING_TRANS_ID_LEN];
} STRUCT_PACKED ping_req;
struct {
u8 action;
u8 transaction_id[16];
u8 action; /* */
u8 trans_id[WLAN_PING_TRANS_ID_LEN];
} STRUCT_PACKED ping_resp;
} u;
} STRUCT_PACKED action;

View file

@ -3,6 +3,7 @@ ChangeLog for wpa_supplicant
????-??-?? - v0.6.5
* added support for SHA-256 as X.509 certificate digest when using the
internal X.509/TLSv1 implementation
* updated management frame protection to use IEEE 802.11w/D6.0
2008-08-10 - v0.6.4
* added support for EAP Sequences in EAP-FAST Phase 2

View file

@ -1814,6 +1814,7 @@ static void ieee80211_rx_mgmt_probe_req(struct wpa_supplicant *wpa_s,
}
#ifdef CONFIG_IEEE80211R
static void ieee80211_rx_mgmt_ft_action(struct wpa_supplicant *wpa_s,
struct ieee80211_mgmt *mgmt,
size_t len,
@ -1873,6 +1874,78 @@ static void ieee80211_rx_mgmt_ft_action(struct wpa_supplicant *wpa_s,
os_memcpy(wpa_s->bssid, target_ap_addr, ETH_ALEN);
ieee80211_associate(wpa_s);
}
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_IEEE80211W
/* MLME-PING.response */
static int ieee80211_sta_send_ping_resp(struct wpa_supplicant *wpa_s,
const u8 *addr, const u8 *trans_id)
{
struct ieee80211_mgmt *mgmt;
int res;
size_t len;
mgmt = os_zalloc(sizeof(*mgmt));
if (mgmt == NULL) {
wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
"ping action frame");
return -1;
}
len = 24;
os_memcpy(mgmt->da, addr, ETH_ALEN);
os_memcpy(mgmt->sa, wpa_s->own_addr, ETH_ALEN);
os_memcpy(mgmt->bssid, wpa_s->bssid, ETH_ALEN);
mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
WLAN_FC_STYPE_ACTION);
mgmt->u.action.category = WLAN_ACTION_PING;
mgmt->u.action.u.ping_resp.action = WLAN_PING_RESPONSE;
os_memcpy(mgmt->u.action.u.ping_resp.trans_id, trans_id,
WLAN_PING_TRANS_ID_LEN);
len += 1 + sizeof(mgmt->u.action.u.ping_resp);
res = ieee80211_sta_tx(wpa_s, (u8 *) mgmt, len);
os_free(mgmt);
return res;
}
static void ieee80211_rx_mgmt_ping_action(
struct wpa_supplicant *wpa_s, struct ieee80211_mgmt *mgmt, size_t len,
struct ieee80211_rx_status *rx_status)
{
if (len < 24 + 1 + sizeof(mgmt->u.action.u.ping_req)) {
wpa_printf(MSG_DEBUG, "MLME: Too short Ping Action frame");
return;
}
if (mgmt->u.action.u.ping_req.action != WLAN_PING_REQUEST) {
wpa_printf(MSG_DEBUG, "MLME: Unexpected Ping Action %d",
mgmt->u.action.u.ping_req.action);
return;
}
if (os_memcmp(mgmt->sa, wpa_s->bssid, ETH_ALEN) != 0) {
wpa_printf(MSG_DEBUG, "MLME: Ignore ping from unknown source "
MACSTR, MAC2STR(mgmt->sa));
return;
}
if (wpa_s->mlme.state == IEEE80211_ASSOCIATE) {
wpa_printf(MSG_DEBUG, "MLME: Ignore ping request during "
"association process");
return;
}
wpa_printf(MSG_DEBUG, "MLME: Replying to ping request");
ieee80211_sta_send_ping_resp(wpa_s, mgmt->sa,
mgmt->u.action.u.ping_req.trans_id);
}
#endif /* CONFIG_IEEE80211W */
static void ieee80211_rx_mgmt_action(struct wpa_supplicant *wpa_s,
@ -1885,11 +1958,22 @@ static void ieee80211_rx_mgmt_action(struct wpa_supplicant *wpa_s,
if (len < 25)
return;
if (mgmt->u.action.category == WLAN_ACTION_FT)
switch (mgmt->u.action.category) {
#ifdef CONFIG_IEEE80211R
case WLAN_ACTION_FT:
ieee80211_rx_mgmt_ft_action(wpa_s, mgmt, len, rx_status);
else
break;
#endif /* CONFIG_IEEE80211R */
case WLAN_ACTION_PING:
ieee80211_rx_mgmt_ping_action(wpa_s, mgmt, len, rx_status);
break;
#ifdef CONFIG_IEEE80211W
#endif /* CONFIG_IEEE80211W */
default:
wpa_printf(MSG_DEBUG, "MLME: unknown Action Category %d",
mgmt->u.action.category);
break;
}
}