64f45d070b
The output file includes all the capture (or read from wireless PCAP file) frames in their original contents and another copy of each frame that is decrypted in wlantest (including EAPOL-Key Key Data field).
799 lines
20 KiB
C
799 lines
20 KiB
C
/*
|
|
* Received Management frame processing
|
|
* Copyright (c) 2010, Jouni Malinen <j@w1.fi>
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License version 2 as
|
|
* published by the Free Software Foundation.
|
|
*
|
|
* Alternatively, this software may be distributed under the terms of BSD
|
|
* license.
|
|
*
|
|
* See README and COPYING for more details.
|
|
*/
|
|
|
|
#include "utils/includes.h"
|
|
|
|
#include "utils/common.h"
|
|
#include "common/ieee802_11_defs.h"
|
|
#include "common/ieee802_11_common.h"
|
|
#include "crypto/aes_wrap.h"
|
|
#include "wlantest.h"
|
|
|
|
|
|
static const char * mgmt_stype(u16 stype)
|
|
{
|
|
switch (stype) {
|
|
case WLAN_FC_STYPE_ASSOC_REQ:
|
|
return "ASSOC-REQ";
|
|
case WLAN_FC_STYPE_ASSOC_RESP:
|
|
return "ASSOC-RESP";
|
|
case WLAN_FC_STYPE_REASSOC_REQ:
|
|
return "REASSOC-REQ";
|
|
case WLAN_FC_STYPE_REASSOC_RESP:
|
|
return "REASSOC-RESP";
|
|
case WLAN_FC_STYPE_PROBE_REQ:
|
|
return "PROBE-REQ";
|
|
case WLAN_FC_STYPE_PROBE_RESP:
|
|
return "PROBE-RESP";
|
|
case WLAN_FC_STYPE_BEACON:
|
|
return "BEACON";
|
|
case WLAN_FC_STYPE_ATIM:
|
|
return "ATIM";
|
|
case WLAN_FC_STYPE_DISASSOC:
|
|
return "DISASSOC";
|
|
case WLAN_FC_STYPE_AUTH:
|
|
return "AUTH";
|
|
case WLAN_FC_STYPE_DEAUTH:
|
|
return "DEAUTH";
|
|
case WLAN_FC_STYPE_ACTION:
|
|
return "ACTION";
|
|
}
|
|
return "??";
|
|
}
|
|
|
|
|
|
static void rx_mgmt_beacon(struct wlantest *wt, const u8 *data, size_t len)
|
|
{
|
|
const struct ieee80211_mgmt *mgmt;
|
|
struct wlantest_bss *bss;
|
|
struct ieee802_11_elems elems;
|
|
|
|
mgmt = (const struct ieee80211_mgmt *) data;
|
|
bss = bss_get(wt, mgmt->bssid);
|
|
if (bss == NULL)
|
|
return;
|
|
if (bss->proberesp_seen)
|
|
return; /* do not override with Beacon data */
|
|
bss->capab_info = le_to_host16(mgmt->u.beacon.capab_info);
|
|
if (ieee802_11_parse_elems(mgmt->u.beacon.variable,
|
|
len - (mgmt->u.beacon.variable - data),
|
|
&elems, 0) == ParseFailed) {
|
|
if (bss->parse_error_reported)
|
|
return;
|
|
wpa_printf(MSG_INFO, "Invalid IEs in a Beacon frame from "
|
|
MACSTR, MAC2STR(mgmt->sa));
|
|
bss->parse_error_reported = 1;
|
|
return;
|
|
}
|
|
|
|
bss_update(wt, bss, &elems);
|
|
}
|
|
|
|
|
|
static void rx_mgmt_probe_resp(struct wlantest *wt, const u8 *data, size_t len)
|
|
{
|
|
const struct ieee80211_mgmt *mgmt;
|
|
struct wlantest_bss *bss;
|
|
struct ieee802_11_elems elems;
|
|
|
|
mgmt = (const struct ieee80211_mgmt *) data;
|
|
bss = bss_get(wt, mgmt->bssid);
|
|
if (bss == NULL)
|
|
return;
|
|
|
|
bss->capab_info = le_to_host16(mgmt->u.probe_resp.capab_info);
|
|
if (ieee802_11_parse_elems(mgmt->u.probe_resp.variable,
|
|
len - (mgmt->u.probe_resp.variable - data),
|
|
&elems, 0) == ParseFailed) {
|
|
if (bss->parse_error_reported)
|
|
return;
|
|
wpa_printf(MSG_INFO, "Invalid IEs in a Probe Response frame "
|
|
"from " MACSTR, MAC2STR(mgmt->sa));
|
|
bss->parse_error_reported = 1;
|
|
return;
|
|
}
|
|
|
|
bss_update(wt, bss, &elems);
|
|
}
|
|
|
|
|
|
static void rx_mgmt_auth(struct wlantest *wt, const u8 *data, size_t len)
|
|
{
|
|
const struct ieee80211_mgmt *mgmt;
|
|
struct wlantest_bss *bss;
|
|
struct wlantest_sta *sta;
|
|
u16 alg, trans, status;
|
|
|
|
mgmt = (const struct ieee80211_mgmt *) data;
|
|
bss = bss_get(wt, mgmt->bssid);
|
|
if (bss == NULL)
|
|
return;
|
|
if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
|
|
sta = sta_get(bss, mgmt->da);
|
|
else
|
|
sta = sta_get(bss, mgmt->sa);
|
|
if (sta == NULL)
|
|
return;
|
|
|
|
if (len < 24 + 6) {
|
|
wpa_printf(MSG_INFO, "Too short Authentication frame from "
|
|
MACSTR, MAC2STR(mgmt->sa));
|
|
return;
|
|
}
|
|
|
|
alg = le_to_host16(mgmt->u.auth.auth_alg);
|
|
trans = le_to_host16(mgmt->u.auth.auth_transaction);
|
|
status = le_to_host16(mgmt->u.auth.status_code);
|
|
|
|
wpa_printf(MSG_DEBUG, "AUTH " MACSTR " -> " MACSTR
|
|
" (alg=%u trans=%u status=%u)",
|
|
MAC2STR(mgmt->sa), MAC2STR(mgmt->da), alg, trans, status);
|
|
|
|
if (alg == 0 && trans == 2 && status == 0) {
|
|
if (sta->state == STATE1) {
|
|
wpa_printf(MSG_DEBUG, "STA " MACSTR
|
|
" moved to State 2 with " MACSTR,
|
|
MAC2STR(sta->addr), MAC2STR(bss->bssid));
|
|
sta->state = STATE2;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
static void rx_mgmt_deauth(struct wlantest *wt, const u8 *data, size_t len,
|
|
int valid)
|
|
{
|
|
const struct ieee80211_mgmt *mgmt;
|
|
struct wlantest_bss *bss;
|
|
struct wlantest_sta *sta;
|
|
|
|
mgmt = (const struct ieee80211_mgmt *) data;
|
|
bss = bss_get(wt, mgmt->bssid);
|
|
if (bss == NULL)
|
|
return;
|
|
if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
|
|
sta = sta_get(bss, mgmt->da);
|
|
else
|
|
sta = sta_get(bss, mgmt->sa);
|
|
if (sta == NULL)
|
|
return;
|
|
|
|
if (len < 24 + 2) {
|
|
wpa_printf(MSG_INFO, "Too short Deauthentication frame from "
|
|
MACSTR, MAC2STR(mgmt->sa));
|
|
return;
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG, "DEAUTH " MACSTR " -> " MACSTR
|
|
" (reason=%u)",
|
|
MAC2STR(mgmt->sa), MAC2STR(mgmt->da),
|
|
le_to_host16(mgmt->u.deauth.reason_code));
|
|
wpa_hexdump(MSG_MSGDUMP, "DEAUTH payload", data + 24, len - 24);
|
|
|
|
if (!valid) {
|
|
wpa_printf(MSG_INFO, "Do not change STA " MACSTR " State "
|
|
"since Disassociation frame was not protected "
|
|
"correctly", MAC2STR(sta->addr));
|
|
return;
|
|
}
|
|
|
|
if (sta->state != STATE1) {
|
|
wpa_printf(MSG_DEBUG, "STA " MACSTR
|
|
" moved to State 1 with " MACSTR,
|
|
MAC2STR(sta->addr), MAC2STR(bss->bssid));
|
|
sta->state = STATE1;
|
|
}
|
|
}
|
|
|
|
|
|
static void rx_mgmt_assoc_req(struct wlantest *wt, const u8 *data, size_t len)
|
|
{
|
|
const struct ieee80211_mgmt *mgmt;
|
|
struct wlantest_bss *bss;
|
|
struct wlantest_sta *sta;
|
|
struct ieee802_11_elems elems;
|
|
|
|
mgmt = (const struct ieee80211_mgmt *) data;
|
|
bss = bss_get(wt, mgmt->bssid);
|
|
if (bss == NULL)
|
|
return;
|
|
sta = sta_get(bss, mgmt->sa);
|
|
if (sta == NULL)
|
|
return;
|
|
|
|
if (len < 24 + 4) {
|
|
wpa_printf(MSG_INFO, "Too short Association Request frame "
|
|
"from " MACSTR, MAC2STR(mgmt->sa));
|
|
return;
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG, "ASSOCREQ " MACSTR " -> " MACSTR
|
|
" (capab=0x%x listen_int=%u)",
|
|
MAC2STR(mgmt->sa), MAC2STR(mgmt->da),
|
|
le_to_host16(mgmt->u.assoc_req.capab_info),
|
|
le_to_host16(mgmt->u.assoc_req.listen_interval));
|
|
|
|
if (ieee802_11_parse_elems(mgmt->u.assoc_req.variable,
|
|
len - (mgmt->u.assoc_req.variable - data),
|
|
&elems, 0) == ParseFailed) {
|
|
wpa_printf(MSG_INFO, "Invalid IEs in Association Request "
|
|
"frame from " MACSTR, MAC2STR(mgmt->sa));
|
|
return;
|
|
}
|
|
|
|
sta_update_assoc(sta, &elems);
|
|
}
|
|
|
|
|
|
static void rx_mgmt_assoc_resp(struct wlantest *wt, const u8 *data, size_t len)
|
|
{
|
|
const struct ieee80211_mgmt *mgmt;
|
|
struct wlantest_bss *bss;
|
|
struct wlantest_sta *sta;
|
|
u16 capab, status, aid;
|
|
|
|
mgmt = (const struct ieee80211_mgmt *) data;
|
|
bss = bss_get(wt, mgmt->bssid);
|
|
if (bss == NULL)
|
|
return;
|
|
sta = sta_get(bss, mgmt->da);
|
|
if (sta == NULL)
|
|
return;
|
|
|
|
if (len < 24 + 6) {
|
|
wpa_printf(MSG_INFO, "Too short Association Response frame "
|
|
"from " MACSTR, MAC2STR(mgmt->sa));
|
|
return;
|
|
}
|
|
|
|
capab = le_to_host16(mgmt->u.assoc_resp.capab_info);
|
|
status = le_to_host16(mgmt->u.assoc_resp.status_code);
|
|
aid = le_to_host16(mgmt->u.assoc_resp.aid);
|
|
|
|
wpa_printf(MSG_DEBUG, "ASSOCRESP " MACSTR " -> " MACSTR
|
|
" (capab=0x%x status=%u aid=%u)",
|
|
MAC2STR(mgmt->sa), MAC2STR(mgmt->da), capab, status,
|
|
aid & 0x3fff);
|
|
|
|
if (status)
|
|
return;
|
|
|
|
if ((aid & 0xc000) != 0xc000) {
|
|
wpa_printf(MSG_DEBUG, "Two MSBs of the AID were not set to 1 "
|
|
"in Association Response from " MACSTR,
|
|
MAC2STR(mgmt->sa));
|
|
}
|
|
sta->aid = aid & 0xc000;
|
|
|
|
if (sta->state < STATE2) {
|
|
wpa_printf(MSG_DEBUG, "STA " MACSTR " was not in State 2 when "
|
|
"getting associated", MAC2STR(sta->addr));
|
|
}
|
|
|
|
if (sta->state < STATE3) {
|
|
wpa_printf(MSG_DEBUG, "STA " MACSTR
|
|
" moved to State 3 with " MACSTR,
|
|
MAC2STR(sta->addr), MAC2STR(bss->bssid));
|
|
sta->state = STATE3;
|
|
}
|
|
}
|
|
|
|
|
|
static void rx_mgmt_reassoc_req(struct wlantest *wt, const u8 *data,
|
|
size_t len)
|
|
{
|
|
const struct ieee80211_mgmt *mgmt;
|
|
struct wlantest_bss *bss;
|
|
struct wlantest_sta *sta;
|
|
struct ieee802_11_elems elems;
|
|
|
|
mgmt = (const struct ieee80211_mgmt *) data;
|
|
bss = bss_get(wt, mgmt->bssid);
|
|
if (bss == NULL)
|
|
return;
|
|
sta = sta_get(bss, mgmt->sa);
|
|
if (sta == NULL)
|
|
return;
|
|
|
|
if (len < 24 + 4 + ETH_ALEN) {
|
|
wpa_printf(MSG_INFO, "Too short Reassociation Request frame "
|
|
"from " MACSTR, MAC2STR(mgmt->sa));
|
|
return;
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG, "REASSOCREQ " MACSTR " -> " MACSTR
|
|
" (capab=0x%x listen_int=%u current_ap=" MACSTR ")",
|
|
MAC2STR(mgmt->sa), MAC2STR(mgmt->da),
|
|
le_to_host16(mgmt->u.reassoc_req.capab_info),
|
|
le_to_host16(mgmt->u.reassoc_req.listen_interval),
|
|
MAC2STR(mgmt->u.reassoc_req.current_ap));
|
|
|
|
if (ieee802_11_parse_elems(mgmt->u.reassoc_req.variable,
|
|
len - (mgmt->u.reassoc_req.variable - data),
|
|
&elems, 0) == ParseFailed) {
|
|
wpa_printf(MSG_INFO, "Invalid IEs in Reassociation Request "
|
|
"frame from " MACSTR, MAC2STR(mgmt->sa));
|
|
return;
|
|
}
|
|
|
|
sta_update_assoc(sta, &elems);
|
|
}
|
|
|
|
|
|
static void rx_mgmt_reassoc_resp(struct wlantest *wt, const u8 *data,
|
|
size_t len)
|
|
{
|
|
const struct ieee80211_mgmt *mgmt;
|
|
struct wlantest_bss *bss;
|
|
struct wlantest_sta *sta;
|
|
u16 capab, status, aid;
|
|
|
|
mgmt = (const struct ieee80211_mgmt *) data;
|
|
bss = bss_get(wt, mgmt->bssid);
|
|
if (bss == NULL)
|
|
return;
|
|
sta = sta_get(bss, mgmt->da);
|
|
if (sta == NULL)
|
|
return;
|
|
|
|
if (len < 24 + 6) {
|
|
wpa_printf(MSG_INFO, "Too short Reassociation Response frame "
|
|
"from " MACSTR, MAC2STR(mgmt->sa));
|
|
return;
|
|
}
|
|
|
|
capab = le_to_host16(mgmt->u.reassoc_resp.capab_info);
|
|
status = le_to_host16(mgmt->u.reassoc_resp.status_code);
|
|
aid = le_to_host16(mgmt->u.reassoc_resp.aid);
|
|
|
|
wpa_printf(MSG_DEBUG, "REASSOCRESP " MACSTR " -> " MACSTR
|
|
" (capab=0x%x status=%u aid=%u)",
|
|
MAC2STR(mgmt->sa), MAC2STR(mgmt->da), capab, status,
|
|
aid & 0x3fff);
|
|
|
|
if (status)
|
|
return;
|
|
|
|
if ((aid & 0xc000) != 0xc000) {
|
|
wpa_printf(MSG_DEBUG, "Two MSBs of the AID were not set to 1 "
|
|
"in Reassociation Response from " MACSTR,
|
|
MAC2STR(mgmt->sa));
|
|
}
|
|
sta->aid = aid & 0xc000;
|
|
|
|
if (sta->state < STATE2) {
|
|
wpa_printf(MSG_DEBUG, "STA " MACSTR " was not in State 2 when "
|
|
"getting associated", MAC2STR(sta->addr));
|
|
}
|
|
|
|
if (sta->state < STATE3) {
|
|
wpa_printf(MSG_DEBUG, "STA " MACSTR
|
|
" moved to State 3 with " MACSTR,
|
|
MAC2STR(sta->addr), MAC2STR(bss->bssid));
|
|
sta->state = STATE3;
|
|
}
|
|
}
|
|
|
|
|
|
static void rx_mgmt_disassoc(struct wlantest *wt, const u8 *data, size_t len,
|
|
int valid)
|
|
{
|
|
const struct ieee80211_mgmt *mgmt;
|
|
struct wlantest_bss *bss;
|
|
struct wlantest_sta *sta;
|
|
|
|
mgmt = (const struct ieee80211_mgmt *) data;
|
|
bss = bss_get(wt, mgmt->bssid);
|
|
if (bss == NULL)
|
|
return;
|
|
if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
|
|
sta = sta_get(bss, mgmt->da);
|
|
else
|
|
sta = sta_get(bss, mgmt->sa);
|
|
if (sta == NULL)
|
|
return;
|
|
|
|
if (len < 24 + 2) {
|
|
wpa_printf(MSG_INFO, "Too short Disassociation frame from "
|
|
MACSTR, MAC2STR(mgmt->sa));
|
|
return;
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG, "DISASSOC " MACSTR " -> " MACSTR
|
|
" (reason=%u)",
|
|
MAC2STR(mgmt->sa), MAC2STR(mgmt->da),
|
|
le_to_host16(mgmt->u.disassoc.reason_code));
|
|
wpa_hexdump(MSG_MSGDUMP, "DISASSOC payload", data + 24, len - 24);
|
|
|
|
if (!valid) {
|
|
wpa_printf(MSG_INFO, "Do not change STA " MACSTR " State "
|
|
"since Disassociation frame was not protected "
|
|
"correctly", MAC2STR(sta->addr));
|
|
return;
|
|
}
|
|
|
|
if (sta->state < STATE2) {
|
|
wpa_printf(MSG_DEBUG, "STA " MACSTR " was not in State 2 or 3 "
|
|
"when getting disassociated", MAC2STR(sta->addr));
|
|
}
|
|
|
|
if (sta->state > STATE2) {
|
|
wpa_printf(MSG_DEBUG, "STA " MACSTR
|
|
" moved to State 2 with " MACSTR,
|
|
MAC2STR(sta->addr), MAC2STR(bss->bssid));
|
|
sta->state = STATE2;
|
|
}
|
|
}
|
|
|
|
|
|
static void rx_mgmt_action_sa_query(struct wlantest *wt,
|
|
struct wlantest_sta *sta,
|
|
const struct ieee80211_mgmt *mgmt,
|
|
size_t len)
|
|
{
|
|
const u8 *rx_id;
|
|
u8 *id;
|
|
|
|
if (len < 24 + 2 + WLAN_SA_QUERY_TR_ID_LEN) {
|
|
wpa_printf(MSG_INFO, "Too short SA Query frame from " MACSTR,
|
|
MAC2STR(mgmt->sa));
|
|
return;
|
|
}
|
|
|
|
if (len > 24 + 2 + WLAN_SA_QUERY_TR_ID_LEN) {
|
|
size_t elen = len - (24 + 2 + WLAN_SA_QUERY_TR_ID_LEN);
|
|
wpa_printf(MSG_INFO, "Unexpected %u octets of extra data at "
|
|
"the end of SA Query frame from " MACSTR,
|
|
(unsigned) elen, MAC2STR(mgmt->sa));
|
|
wpa_hexdump(MSG_INFO, "SA Query extra data",
|
|
((const u8 *) mgmt) + len - elen, elen);
|
|
}
|
|
|
|
switch (mgmt->u.action.u.sa_query_req.action) {
|
|
case WLAN_SA_QUERY_REQUEST:
|
|
rx_id = (const u8 *) mgmt->u.action.u.sa_query_req.trans_id;
|
|
if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
|
|
id = sta->ap_sa_query_tr;
|
|
else
|
|
id = sta->sta_sa_query_tr;
|
|
wpa_printf(MSG_INFO, "SA Query Request " MACSTR " -> " MACSTR
|
|
" (trans_id=%02x%02x)",
|
|
MAC2STR(mgmt->sa), MAC2STR(mgmt->da),
|
|
rx_id[0], rx_id[1]);
|
|
os_memcpy(id, mgmt->u.action.u.sa_query_req.trans_id, 2);
|
|
break;
|
|
case WLAN_SA_QUERY_RESPONSE:
|
|
rx_id = (const u8 *) mgmt->u.action.u.sa_query_resp.trans_id;
|
|
if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
|
|
id = sta->sta_sa_query_tr;
|
|
else
|
|
id = sta->ap_sa_query_tr;
|
|
wpa_printf(MSG_INFO, "SA Query Response " MACSTR " -> " MACSTR
|
|
" (trans_id=%02x%02x; %s)",
|
|
MAC2STR(mgmt->sa), MAC2STR(mgmt->da),
|
|
rx_id[0], rx_id[1],
|
|
os_memcmp(rx_id, id, 2) == 0 ?
|
|
"match" : "mismatch");
|
|
break;
|
|
default:
|
|
wpa_printf(MSG_INFO, "Unexpected SA Query action value %u "
|
|
"from " MACSTR,
|
|
mgmt->u.action.u.sa_query_req.action,
|
|
MAC2STR(mgmt->sa));
|
|
}
|
|
}
|
|
|
|
|
|
static void rx_mgmt_action(struct wlantest *wt, const u8 *data, size_t len)
|
|
{
|
|
const struct ieee80211_mgmt *mgmt;
|
|
struct wlantest_bss *bss;
|
|
struct wlantest_sta *sta;
|
|
|
|
mgmt = (const struct ieee80211_mgmt *) data;
|
|
if (mgmt->da[0] & 0x01)
|
|
return; /* Ignore group addressed Action frames for now */
|
|
bss = bss_get(wt, mgmt->bssid);
|
|
if (bss == NULL)
|
|
return;
|
|
if (os_memcmp(mgmt->sa, mgmt->bssid, ETH_ALEN) == 0)
|
|
sta = sta_get(bss, mgmt->da);
|
|
else
|
|
sta = sta_get(bss, mgmt->sa);
|
|
if (sta == NULL)
|
|
return;
|
|
|
|
if (len < 24 + 1) {
|
|
wpa_printf(MSG_INFO, "Too short Action frame from "
|
|
MACSTR, MAC2STR(mgmt->sa));
|
|
return;
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG, "ACTION " MACSTR " -> " MACSTR
|
|
" (category=%u)",
|
|
MAC2STR(mgmt->sa), MAC2STR(mgmt->da),
|
|
mgmt->u.action.category);
|
|
wpa_hexdump(MSG_MSGDUMP, "ACTION payload", data + 24, len - 24);
|
|
|
|
if (mgmt->u.action.category != WLAN_ACTION_PUBLIC &&
|
|
sta->state < STATE3) {
|
|
wpa_printf(MSG_INFO, "Action frame sent when STA is not in "
|
|
"State 3 (SA=" MACSTR " DATA=" MACSTR ")",
|
|
MAC2STR(mgmt->sa), MAC2STR(mgmt->da));
|
|
}
|
|
|
|
switch (mgmt->u.action.category) {
|
|
case WLAN_ACTION_SA_QUERY:
|
|
rx_mgmt_action_sa_query(wt, sta, mgmt, len);
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
static int check_mmie_mic(const u8 *igtk, const u8 *data, size_t len)
|
|
{
|
|
u8 *buf;
|
|
u8 mic[16];
|
|
u16 fc;
|
|
const struct ieee80211_hdr *hdr;
|
|
|
|
buf = os_malloc(len + 20 - 24);
|
|
if (buf == NULL)
|
|
return -1;
|
|
|
|
/* BIP AAD: FC(masked) A1 A2 A3 */
|
|
hdr = (const struct ieee80211_hdr *) data;
|
|
fc = le_to_host16(hdr->frame_control);
|
|
fc &= ~(WLAN_FC_RETRY | WLAN_FC_PWRMGT | WLAN_FC_MOREDATA);
|
|
WPA_PUT_LE16(buf, fc);
|
|
os_memcpy(buf + 2, hdr->addr1, 3 * ETH_ALEN);
|
|
|
|
/* Frame body with MMIE MIC masked to zero */
|
|
os_memcpy(buf + 20, data + 24, len - 24 - 8);
|
|
os_memset(buf + 20 + len - 24 - 8, 0, 8);
|
|
|
|
wpa_hexdump(MSG_MSGDUMP, "BIP: AAD|Body(masked)", buf, len + 20 - 24);
|
|
/* MIC = L(AES-128-CMAC(AAD || Frame Body(masked)), 0, 64) */
|
|
if (omac1_aes_128(igtk, buf, len + 20 - 24, mic) < 0) {
|
|
os_free(buf);
|
|
return -1;
|
|
}
|
|
|
|
os_free(buf);
|
|
|
|
if (os_memcmp(data + len - 8, mic, 8) != 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static int check_bip(struct wlantest *wt, const u8 *data, size_t len)
|
|
{
|
|
const struct ieee80211_mgmt *mgmt;
|
|
u16 fc, stype;
|
|
const u8 *mmie;
|
|
int keyid;
|
|
struct wlantest_bss *bss;
|
|
|
|
mgmt = (const struct ieee80211_mgmt *) data;
|
|
fc = le_to_host16(mgmt->frame_control);
|
|
stype = WLAN_FC_GET_STYPE(fc);
|
|
|
|
if (stype == WLAN_FC_STYPE_ACTION) {
|
|
if (len < 24 + 1)
|
|
return 0;
|
|
if (mgmt->u.action.category == WLAN_ACTION_PUBLIC)
|
|
return 0; /* Not a robust management frame */
|
|
}
|
|
|
|
bss = bss_get(wt, mgmt->bssid);
|
|
if (bss == NULL)
|
|
return 0; /* No key known yet */
|
|
|
|
if (len < 24 + 18 || data[len - 18] != WLAN_EID_MMIE ||
|
|
data[len - 17] != 16) {
|
|
/* No MMIE */
|
|
/* TODO: use MFPC flag in RSN IE instead of IGTK flags */
|
|
if (bss->igtk_set[4] || bss->igtk_set[5]) {
|
|
wpa_printf(MSG_INFO, "Robust group-addressed "
|
|
"management frame sent without BIP by "
|
|
MACSTR, MAC2STR(mgmt->sa));
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
mmie = data + len - 16;
|
|
keyid = WPA_GET_LE16(mmie);
|
|
if (keyid < 4 || keyid > 5) {
|
|
wpa_printf(MSG_INFO, "Unexpected MMIE KeyID %u from " MACSTR,
|
|
keyid, MAC2STR(mgmt->sa));
|
|
return 0;
|
|
}
|
|
wpa_printf(MSG_DEBUG, "MMIE KeyID %u", keyid);
|
|
wpa_hexdump(MSG_MSGDUMP, "MMIE IPN", mmie + 2, 6);
|
|
wpa_hexdump(MSG_MSGDUMP, "MMIE MIC", mmie + 8, 8);
|
|
|
|
if (!bss->igtk_set[keyid]) {
|
|
wpa_printf(MSG_DEBUG, "No IGTK known to validate BIP frame");
|
|
return 0;
|
|
}
|
|
|
|
if (os_memcmp(mmie + 2, bss->ipn[keyid], 6) <= 0) {
|
|
wpa_printf(MSG_INFO, "BIP replay detected: SA=" MACSTR,
|
|
MAC2STR(mgmt->sa));
|
|
wpa_hexdump(MSG_INFO, "RX IPN", mmie + 2, 6);
|
|
wpa_hexdump(MSG_INFO, "Last RX IPN", bss->ipn[keyid], 6);
|
|
}
|
|
|
|
if (check_mmie_mic(bss->igtk[keyid], data, len) < 0) {
|
|
wpa_printf(MSG_INFO, "Invalid MMIE MIC in a frame from "
|
|
MACSTR, MAC2STR(mgmt->sa));
|
|
return -1;
|
|
}
|
|
|
|
wpa_printf(MSG_DEBUG, "Valid MMIE MIC");
|
|
os_memcpy(bss->ipn[keyid], mmie + 2, 6);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
static u8 * mgmt_ccmp_decrypt(struct wlantest *wt, const u8 *data, size_t len,
|
|
size_t *dlen)
|
|
{
|
|
struct wlantest_bss *bss;
|
|
struct wlantest_sta *sta;
|
|
const struct ieee80211_hdr *hdr;
|
|
int keyid;
|
|
u8 *decrypted, *frame;
|
|
u8 pn[6], *rsc;
|
|
|
|
hdr = (const struct ieee80211_hdr *) data;
|
|
bss = bss_get(wt, hdr->addr3);
|
|
if (bss == NULL)
|
|
return NULL;
|
|
if (os_memcmp(hdr->addr1, hdr->addr3, ETH_ALEN) == 0)
|
|
sta = sta_get(bss, hdr->addr2);
|
|
else
|
|
sta = sta_get(bss, hdr->addr1);
|
|
if (sta == NULL || !sta->ptk_set) {
|
|
wpa_printf(MSG_MSGDUMP, "No PTK known to decrypt the frame");
|
|
return NULL;
|
|
}
|
|
|
|
keyid = data[3] >> 6;
|
|
if (keyid != 0) {
|
|
wpa_printf(MSG_INFO, "Unexpected non-zero KeyID %d in "
|
|
"individually addressed Management frame from "
|
|
MACSTR, keyid, MAC2STR(hdr->addr2));
|
|
}
|
|
|
|
if (os_memcmp(hdr->addr1, hdr->addr3, ETH_ALEN) == 0)
|
|
rsc = sta->rsc_tods[16];
|
|
else
|
|
rsc = sta->rsc_fromds[16];
|
|
|
|
ccmp_get_pn(pn, data + 24);
|
|
if (os_memcmp(pn, rsc, 6) <= 0) {
|
|
wpa_printf(MSG_INFO, "CCMP/TKIP replay detected: SA=" MACSTR,
|
|
MAC2STR(hdr->addr2));
|
|
wpa_hexdump(MSG_INFO, "RX PN", pn, 6);
|
|
wpa_hexdump(MSG_INFO, "RSC", rsc, 6);
|
|
}
|
|
|
|
decrypted = ccmp_decrypt(sta->ptk.tk1, hdr, data + 24, len - 24, dlen);
|
|
if (decrypted)
|
|
os_memcpy(rsc, pn, 6);
|
|
|
|
frame = os_malloc(24 + *dlen);
|
|
if (frame) {
|
|
os_memcpy(frame, data, 24);
|
|
os_memcpy(frame + 24, decrypted, *dlen);
|
|
*dlen += 24;
|
|
}
|
|
|
|
os_free(decrypted);
|
|
|
|
return frame;
|
|
}
|
|
|
|
|
|
void rx_mgmt(struct wlantest *wt, const u8 *data, size_t len)
|
|
{
|
|
const struct ieee80211_hdr *hdr;
|
|
u16 fc, stype;
|
|
int valid = 1;
|
|
u8 *decrypted = NULL;
|
|
size_t dlen;
|
|
|
|
if (len < 24)
|
|
return;
|
|
|
|
hdr = (const struct ieee80211_hdr *) data;
|
|
fc = le_to_host16(hdr->frame_control);
|
|
wt->rx_mgmt++;
|
|
stype = WLAN_FC_GET_STYPE(fc);
|
|
|
|
if ((hdr->addr1[0] & 0x01) &&
|
|
(stype == WLAN_FC_STYPE_DEAUTH ||
|
|
stype == WLAN_FC_STYPE_DISASSOC ||
|
|
stype == WLAN_FC_STYPE_ACTION))
|
|
check_bip(wt, data, len);
|
|
|
|
wpa_printf((stype == WLAN_FC_STYPE_BEACON ||
|
|
stype == WLAN_FC_STYPE_PROBE_RESP ||
|
|
stype == WLAN_FC_STYPE_PROBE_REQ) ?
|
|
MSG_EXCESSIVE : MSG_MSGDUMP,
|
|
"MGMT %s%s%s DA=" MACSTR " SA=" MACSTR " BSSID=" MACSTR,
|
|
mgmt_stype(stype),
|
|
fc & WLAN_FC_PWRMGT ? " PwrMgt" : "",
|
|
fc & WLAN_FC_ISWEP ? " Prot" : "",
|
|
MAC2STR(hdr->addr1), MAC2STR(hdr->addr2),
|
|
MAC2STR(hdr->addr3));
|
|
|
|
if ((fc & WLAN_FC_ISWEP) &&
|
|
!(hdr->addr1[0] & 0x01) &&
|
|
(stype == WLAN_FC_STYPE_DEAUTH ||
|
|
stype == WLAN_FC_STYPE_DISASSOC ||
|
|
stype == WLAN_FC_STYPE_ACTION)) {
|
|
decrypted = mgmt_ccmp_decrypt(wt, data, len, &dlen);
|
|
if (decrypted) {
|
|
write_pcap_decrypted(wt, decrypted, dlen, NULL, 0);
|
|
data = decrypted;
|
|
len = dlen;
|
|
} else
|
|
valid = 0;
|
|
}
|
|
|
|
/* TODO: verify that robust management frames are protected if MFP was
|
|
* negotiated */
|
|
|
|
switch (stype) {
|
|
case WLAN_FC_STYPE_BEACON:
|
|
rx_mgmt_beacon(wt, data, len);
|
|
break;
|
|
case WLAN_FC_STYPE_PROBE_RESP:
|
|
rx_mgmt_probe_resp(wt, data, len);
|
|
break;
|
|
case WLAN_FC_STYPE_AUTH:
|
|
rx_mgmt_auth(wt, data, len);
|
|
break;
|
|
case WLAN_FC_STYPE_DEAUTH:
|
|
rx_mgmt_deauth(wt, data, len, valid);
|
|
break;
|
|
case WLAN_FC_STYPE_ASSOC_REQ:
|
|
rx_mgmt_assoc_req(wt, data, len);
|
|
break;
|
|
case WLAN_FC_STYPE_ASSOC_RESP:
|
|
rx_mgmt_assoc_resp(wt, data, len);
|
|
break;
|
|
case WLAN_FC_STYPE_REASSOC_REQ:
|
|
rx_mgmt_reassoc_req(wt, data, len);
|
|
break;
|
|
case WLAN_FC_STYPE_REASSOC_RESP:
|
|
rx_mgmt_reassoc_resp(wt, data, len);
|
|
break;
|
|
case WLAN_FC_STYPE_DISASSOC:
|
|
rx_mgmt_disassoc(wt, data, len, valid);
|
|
break;
|
|
case WLAN_FC_STYPE_ACTION:
|
|
if (valid)
|
|
rx_mgmt_action(wt, data, len);
|
|
break;
|
|
}
|
|
|
|
os_free(decrypted);
|
|
}
|