esp_wifi: WPA3-SAE support for softAP

This commit is contained in:
Shreyas Sheth
2023-02-28 12:25:05 +08:00
committed by Jiang Jiang Jian
parent ad19981af8
commit 2b8e40e760
44 changed files with 2773 additions and 249 deletions

View File

@@ -12,6 +12,7 @@
#include "crypto/sha1.h"
#include "common/ieee802_11_defs.h"
#include "common/eapol_common.h"
#include "common/sae.h"
#include "ap/wpa_auth.h"
#include "ap/ap_config.h"
#include "utils/wpa_debug.h"
@@ -144,10 +145,39 @@ static int hostapd_derive_psk(struct hostapd_ssid *ssid)
}
int hostapd_setup_sae_pt(struct hostapd_bss_config *conf)
{
#ifdef CONFIG_SAE
struct hostapd_ssid *ssid = &conf->ssid;
if ((conf->sae_pwe == SAE_PWE_HUNT_AND_PECK ||
!wpa_key_mgmt_sae(conf->wpa_key_mgmt)))
return 0; /* PT not needed */
sae_deinit_pt(ssid->pt);
ssid->pt = NULL;
if (ssid->wpa_passphrase) {
ssid->pt = sae_derive_pt(conf->sae_groups, ssid->ssid,
ssid->ssid_len,
(const u8 *) ssid->wpa_passphrase,
os_strlen(ssid->wpa_passphrase),
NULL);
if (!ssid->pt)
return -1;
}
#endif /* CONFIG_SAE */
return 0;
}
int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf)
{
struct hostapd_ssid *ssid = &conf->ssid;
if (hostapd_setup_sae_pt(conf) < 0)
return -1;
if (ssid->wpa_passphrase != NULL) {
if (ssid->wpa_psk != NULL) {
wpa_printf(MSG_DEBUG, "Using pre-configured WPA PSK "
@@ -245,3 +275,25 @@ const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
return NULL;
}
void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **l)
{
struct hostapd_wpa_psk *psk, *tmp;
for (psk = *l; psk;) {
tmp = psk;
psk = psk->next;
bin_clear_free(tmp, sizeof(*tmp));
}
*l = NULL;
}
void hostapd_config_free_bss(struct hostapd_bss_config *conf)
{
hostapd_config_clear_wpa_psk(&conf->ssid.wpa_psk);
str_clear_free(conf->ssid.wpa_passphrase);
#ifdef CONFIG_SAE
sae_deinit_pt(conf->ssid.pt);
#endif /* CONFIG_SAE */
os_free(conf);
}

View File

@@ -53,6 +53,7 @@ struct hostapd_ssid {
struct hostapd_wpa_psk *wpa_psk;
char *wpa_passphrase;
struct sae_pt *pt;
struct hostapd_wep_keys wep;
@@ -299,6 +300,12 @@ struct hostapd_bss_config {
char *dump_msk_file;
#endif /* CONFIG_RADIUS_TEST */
unsigned int sae_anti_clogging_threshold;
enum sae_pwe sae_pwe;
unsigned int sae_sync;
int *sae_groups;
#define SAE_ANTI_CLOGGING_THRESHOLD 2 /* max number of commit msg allowed to queue without anti-clogging token request */
};
@@ -367,13 +374,17 @@ void hostapd_config_free(struct hostapd_config *conf);
int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries,
const u8 *addr, int *vlan_id);
int hostapd_rate_found(int *list, int rate);
void hostapd_config_clear_wpa_psk(struct hostapd_wpa_psk **p);
void hostapd_config_free_bss(struct hostapd_bss_config *conf);
int hostapd_wep_key_cmp(struct hostapd_wep_keys *a,
struct hostapd_wep_keys *b);
const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
const u8 *addr, const u8 *prev_psk);
int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf);
struct sta_info;
bool wpa_ap_join(struct sta_info *sta, uint8_t *bssid, uint8_t *wpa_ie, uint8_t wpa_ie_len, bool *pmf_enable);
bool wpa_ap_join(struct sta_info *sta, uint8_t *bssid, uint8_t *wpa_ie,
uint8_t wpa_ie_len,uint8_t *rsnxe, uint8_t rsnxe_len,
bool *pmf_enable, int subtype);
bool wpa_ap_remove(void* sta_info);
#endif /* HOSTAPD_CONFIG_H */

View File

@@ -0,0 +1,142 @@
/*
* hostapd / Comeback token mechanism for SAE
* Copyright (c) 2002-2017, 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 "hostapd.h"
#include "crypto/sha256.h"
#include "crypto/random.h"
#include "common/ieee802_11_defs.h"
#include "comeback_token.h"
#ifdef CONFIG_SAE
int comeback_token_hash(const u8 *comeback_key, const u8 *addr, u8 *idx)
{
u8 hash[SHA256_MAC_LEN];
if (hmac_sha256(comeback_key, COMEBACK_KEY_SIZE,
addr, ETH_ALEN, hash) < 0)
return -1;
*idx = hash[0];
return 0;
}
int check_comeback_token(const u8 *comeback_key,
u16 *comeback_pending_idx, const u8 *addr,
const u8 *token, size_t token_len)
{
u8 mac[SHA256_MAC_LEN];
const u8 *addrs[2];
size_t len[2];
u16 token_idx;
u8 idx;
if (token_len != SHA256_MAC_LEN ||
comeback_token_hash(comeback_key, addr, &idx) < 0) {
return -1;
}
token_idx = comeback_pending_idx[idx];
if (token_idx == 0 || token_idx != WPA_GET_BE16(token)) {
wpa_printf(MSG_DEBUG,
"Comeback: Invalid anti-clogging token from "
MACSTR " - token_idx 0x%04x, expected 0x%04x",
MAC2STR(addr), WPA_GET_BE16(token), token_idx);
return -1;
}
addrs[0] = addr;
len[0] = ETH_ALEN;
addrs[1] = token;
len[1] = 2;
if (hmac_sha256_vector(comeback_key, COMEBACK_KEY_SIZE,
2, addrs, len, mac) < 0 ||
os_memcmp_const(token + 2, &mac[2], SHA256_MAC_LEN - 2) != 0) {
return -1;
}
comeback_pending_idx[idx] = 0; /* invalidate used token */
return 0;
}
struct wpabuf *
auth_build_token_req(struct os_reltime *last_comeback_key_update,
u8 *comeback_key, u16 comeback_idx,
u16 *comeback_pending_idx, size_t idx_len,
int group, const u8 *addr, int h2e)
{
struct wpabuf *buf;
u8 *token;
struct os_reltime now;
u8 idx[2];
const u8 *addrs[2];
size_t len[2];
u8 p_idx;
u16 token_idx;
os_get_time(&now);
if (!os_reltime_initialized(last_comeback_key_update) ||
os_reltime_expired(&now, last_comeback_key_update, 60) ||
comeback_idx == 0xffff) {
if (random_get_bytes(comeback_key, COMEBACK_KEY_SIZE) < 0) {
return NULL;
}
wpa_hexdump(MSG_DEBUG, "Comeback: Updated token key",
comeback_key, COMEBACK_KEY_SIZE);
*last_comeback_key_update = now;
comeback_idx = 0;
os_memset(comeback_pending_idx, 0, idx_len);
}
buf = wpabuf_alloc(sizeof(le16) + 3 + SHA256_MAC_LEN);
if (buf == NULL) {
return NULL;
}
if (group)
wpabuf_put_le16(buf, group); /* Finite Cyclic Group */
if (h2e) {
/* Encapsulate Anti-clogging Token field in a container IE */
wpabuf_put_u8(buf, WLAN_EID_EXTENSION);
wpabuf_put_u8(buf, 1 + SHA256_MAC_LEN);
wpabuf_put_u8(buf, WLAN_EID_EXT_ANTI_CLOGGING_TOKEN);
}
if (comeback_token_hash(comeback_key, addr, &p_idx) < 0) {
wpabuf_free(buf);
return NULL;
}
token_idx = comeback_pending_idx[p_idx];
if (!token_idx) {
comeback_idx++;
token_idx = comeback_idx;
comeback_pending_idx[p_idx] = token_idx;
}
WPA_PUT_BE16(idx, token_idx);
token = wpabuf_put(buf, SHA256_MAC_LEN);
addrs[0] = addr;
len[0] = ETH_ALEN;
addrs[1] = idx;
len[1] = sizeof(idx);
if (hmac_sha256_vector(comeback_key, COMEBACK_KEY_SIZE,
2, addrs, len, token) < 0) {
wpabuf_free(buf);
return NULL;
}
WPA_PUT_BE16(token, token_idx);
return buf;
}
#endif /* CONFIG_SAE */

View File

@@ -0,0 +1,21 @@
/*
* hostapd / Comeback token mechanism for SAE
* Copyright (c) 2002-2017, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef COMEBACK_TOKEN_H
#define COMEBACK_TOKEN_H
int check_comeback_token(const u8 *comeback_key,
u16 *comeback_pending_idx, const u8 *addr,
const u8 *token, size_t token_len);
struct wpabuf *
auth_build_token_req(struct os_reltime *last_comeback_key_update,
u8 *comeback_key, u16 comeback_idx,
u16 *comeback_pending_idx, size_t idx_len,
int group, const u8 *addr, int h2e);
#endif /* COMEBACK_TOKEN_H */

View File

@@ -63,6 +63,15 @@ struct hostapd_frame_info {
int ssi_signal; /* dBm */
};
struct hostapd_sae_commit_queue {
struct dl_list list;
size_t len;
u8 bssid[ETH_ALEN];
u32 auth_transaction;
u16 status;
u8 msg[];
};
#ifdef CONFIG_WPS
enum hapd_wps_status {
WPS_SUCCESS_STATUS = 1,
@@ -131,6 +140,20 @@ struct hostapd_data {
int noa_start;
int noa_duration;
#endif /* CONFIG_P2P */
#ifdef CONFIG_SAE
#define COMEBACK_KEY_SIZE 8
#define COMEBACK_PENDING_IDX_SIZE 256
/** Key used for generating SAE anti-clogging tokens */
u8 comeback_key[COMEBACK_KEY_SIZE];
struct os_reltime last_comeback_key_update;
u16 comeback_idx;
u16 comeback_pending_idx[COMEBACK_PENDING_IDX_SIZE];
int dot11RSNASAERetransPeriod;
struct dl_list sae_commit_queue; /* struct hostapd_sae_commit_queue */
#endif /* CONFIG_SAE */
#ifdef CONFIG_INTERWORKING
size_t gas_frag_limit;
#endif /* CONFIG_INTERWORKING */

View File

@@ -0,0 +1,702 @@
/*
* hostapd / IEEE 802.11 Management
* Copyright (c) 2002-2017, 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 "common/sae.h"
#include "common/ieee802_11_defs.h"
#include "esp_wifi_driver.h"
#include "ap/wpa_auth.h"
#include "ap/hostapd.h"
#include "ap/sta_info.h"
#include "crypto/sha256.h"
#include "ap/pmksa_cache_auth.h"
#include "ap/comeback_token.h"
#include "crypto/random.h"
#include "esp_wpa3_i.h"
#ifdef CONFIG_SAE
static void sae_set_state(struct sta_info *sta, enum sae_state state,
const char *reason)
{
wpa_printf(MSG_DEBUG, "SAE: State %s -> %s for peer " MACSTR " (%s)",
sae_state_txt(sta->sae->state), sae_state_txt(state),
MAC2STR(sta->addr), reason);
sta->sae->state = state;
}
static const char * sae_get_password(struct hostapd_data *hapd,
struct sta_info *sta,
const char *rx_id,
struct sae_pt **s_pt)
{
const char *password = NULL;
struct sae_pt *pt = NULL;
if (!password) {
password = hapd->conf->ssid.wpa_passphrase;
pt = hapd->conf->ssid.pt;
}
if (s_pt) {
*s_pt = pt;
}
return password;
}
static struct wpabuf *auth_build_sae_commit(struct hostapd_data *hapd,
struct sta_info *sta, int update, int status_code)
{
struct wpabuf *buf;
const char *password = NULL;
const char *rx_id = NULL;
int use_pt = 0;
struct sae_pt *pt = NULL;
if (sta->sae->tmp) {
rx_id = sta->sae->tmp->pw_id;
use_pt = sta->sae->h2e;
}
if (rx_id && hapd->conf->sae_pwe != SAE_PWE_FORCE_HUNT_AND_PECK)
use_pt = 1;
else if (status_code == WLAN_STATUS_SUCCESS)
use_pt = 0;
else if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT)
use_pt = 1;
password = sae_get_password(hapd, sta, rx_id, &pt);
if (!password || (use_pt && !pt)) {
wpa_printf(MSG_DEBUG, "SAE: No password available");
return NULL;
}
if (update && use_pt &&
sae_prepare_commit_pt(sta->sae, pt, hapd->own_addr, sta->addr,
NULL, NULL) < 0) {
return NULL;
}
if (update && !use_pt &&
sae_prepare_commit(hapd->own_addr, sta->addr,
(u8 *) password, os_strlen((const char *)password),
sta->sae) < 0) {
wpa_printf(MSG_DEBUG, "SAE: Could not pick PWE");
return NULL;
}
buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN +
(rx_id ? 3 + os_strlen(rx_id) : 0));
if (buf &&
sae_write_commit(sta->sae, buf, sta->sae->tmp ?
sta->sae->tmp->anti_clogging_token : NULL,
rx_id) < 0) {
wpabuf_free(buf);
buf = NULL;
}
return buf;
}
static struct wpabuf *auth_build_sae_confirm(struct hostapd_data *hapd,
struct sta_info *sta)
{
struct wpabuf *buf;
buf = wpabuf_alloc(SAE_CONFIRM_MAX_LEN);
if (buf == NULL) {
return NULL;
}
if (sae_write_confirm(sta->sae, buf) < 0) {
wpabuf_free(buf);
return NULL;
}
return buf;
}
static int auth_sae_send_commit(struct hostapd_data *hapd,
struct sta_info *sta,
const u8 *bssid, int update, int status_code)
{
struct wpabuf *data;
int reply_res;
u16 status;
data = auth_build_sae_commit(hapd, sta, update, status_code);
if (!data && sta->sae->tmp && sta->sae->tmp->pw_id) {
return WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER;
}
if (data == NULL) {
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
if (sta->sae->tmp && sta->sae->h2e) {
status = WLAN_STATUS_SAE_HASH_TO_ELEMENT;
} else {
status = WLAN_STATUS_SUCCESS;
}
#ifdef ESP_SUPPLICANT
if (sta->remove_pending) {
reply_res = -1;
} else {
reply_res = esp_send_sae_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 1,
status, wpabuf_head(data),
wpabuf_len(data));
}
#endif /* ESP_SUPPLICANT */
wpabuf_free(data);
return reply_res;
}
static int auth_sae_send_confirm(struct hostapd_data *hapd,
struct sta_info *sta,
const u8 *bssid)
{
struct wpabuf *data;
int reply_res;
data = auth_build_sae_confirm(hapd, sta);
if (data == NULL) {
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
#ifdef ESP_SUPPLICANT
if (sta->remove_pending) {
reply_res = -1;
} else {
reply_res = esp_send_sae_auth_reply(hapd, sta->addr, bssid, WLAN_AUTH_SAE, 2,
WLAN_STATUS_SUCCESS, wpabuf_head(data),
wpabuf_len(data));
}
#endif /* ESP_SUPPLICANT */
wpabuf_free(data);
return reply_res;
}
static int use_sae_anti_clogging(struct hostapd_data *hapd)
{
struct sta_info *sta;
unsigned int open = 0;
if (hapd->conf->sae_anti_clogging_threshold == 0) {
return 1;
}
for (sta = hapd->sta_list; sta; sta = sta->next) {
if (sta->sae &&
(sta->sae->state == SAE_COMMITTED ||
sta->sae->state != SAE_CONFIRMED)) {
open++;
}
if (open >= hapd->conf->sae_anti_clogging_threshold) {
return 1;
}
}
/* In addition to already existing open SAE sessions, check whether
* there are enough pending commit messages in the processing queue to
* potentially result in too many open sessions. */
if (open + dl_list_len(&hapd->sae_commit_queue) >=
hapd->conf->sae_anti_clogging_threshold) {
return 1;
}
return 0;
}
static int sae_check_big_sync(struct hostapd_data *hapd, struct sta_info *sta)
{
if (sta->sae->sync > hapd->conf->sae_sync) {
sae_set_state(sta, SAE_NOTHING, "Sync > dot11RSNASAESync");
sta->sae->sync = 0;
return -1;
}
return 0;
}
void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta)
{
sta->flags |= WLAN_STA_AUTH;
#ifdef ESP_SUPPLICANT
sta->sae_commit_processing = false;
#endif /* ESP_SUPPLICANT */
sta->auth_alg = WLAN_AUTH_SAE;
sae_set_state(sta, SAE_ACCEPTED, "Accept Confirm");
wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
sta->sae->pmk, sta->sae->pmkid, false);
}
static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *bssid, u8 auth_transaction, u16 status_code,
int allow_reuse, int *sta_removed)
{
int ret = WLAN_STATUS_SUCCESS;
*sta_removed = 0;
if (auth_transaction != 1 && auth_transaction != 2) {
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
wpa_printf(MSG_DEBUG, "SAE: Peer " MACSTR " state=%s auth_trans=%u",
MAC2STR(sta->addr), sae_state_txt(sta->sae->state),
auth_transaction);
switch (sta->sae->state) {
case SAE_NOTHING:
if (auth_transaction == 1) {
if (sta->sae->tmp) {
sta->sae->h2e =
(status_code ==
WLAN_STATUS_SAE_HASH_TO_ELEMENT);
}
ret = auth_sae_send_commit(hapd, sta, bssid,
!allow_reuse, status_code);
if (ret) {
return ret;
}
sae_set_state(sta, SAE_COMMITTED, "Sent Commit");
if (sae_process_commit(sta->sae) < 0) {
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
sta->sae->sync = 0;
} else {
wpa_printf(MSG_DEBUG, "SAE confirm before commit");
}
break;
case SAE_COMMITTED:
if (auth_transaction == 1) {
if (sae_process_commit(sta->sae) < 0) {
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
ret = auth_sae_send_confirm(hapd, sta, bssid);
if (ret) {
return ret;
}
sae_set_state(sta, SAE_CONFIRMED, "Sent Confirm");
sta->sae->sync = 0;
} else {
/*
* For instructure BSS, send the postponed Confirm from
* Nothing -> Confirmed transition that was reduced to
* Nothing -> Committed above.
*/
ret = auth_sae_send_confirm(hapd, sta, bssid);
if (ret) {
return ret;
}
sae_set_state(sta, SAE_CONFIRMED, "Sent Confirm");
/*
* Since this was triggered on Confirm RX, run another
* step to get to Accepted without waiting for
* additional events.
*/
return sae_sm_step(hapd, sta, bssid, auth_transaction,
WLAN_STATUS_SUCCESS, 0, sta_removed);
}
break;
case SAE_CONFIRMED:
if (auth_transaction == 1) {
if (sae_check_big_sync(hapd, sta)) {
return WLAN_STATUS_SUCCESS;
}
sta->sae->sync++;
ret = auth_sae_send_commit(hapd, sta, bssid, 1,
status_code);
if (ret) {
return ret;
}
if (sae_process_commit(sta->sae) < 0) {
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
ret = auth_sae_send_confirm(hapd, sta, bssid);
if (ret) {
return ret;
}
} else {
sta->sae->send_confirm = 0xffff;
sae_accept_sta(hapd, sta);
}
break;
case SAE_ACCEPTED:
if (auth_transaction == 1) {
wpa_printf(MSG_DEBUG, "SAE: Start reauthentication");
ret = auth_sae_send_commit(hapd, sta, bssid, 1,
status_code);
if (ret) {
return ret;
}
if (sae_process_commit(sta->sae) < 0) {
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
sae_set_state(sta, SAE_COMMITTED, "Sent Commit");
sta->sae->sync = 0;
} else {
if (sae_check_big_sync(hapd, sta)) {
return WLAN_STATUS_SUCCESS;
}
sta->sae->sync++;
ret = auth_sae_send_confirm(hapd, sta, bssid);
sae_clear_temp_data(sta->sae);
if (ret) {
return ret;
}
}
break;
default:
wpa_printf(MSG_ERROR, "SAE: invalid state %d",
sta->sae->state);
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
return WLAN_STATUS_SUCCESS;
}
static int sae_status_success(struct hostapd_data *hapd, u16 status_code)
{
enum sae_pwe sae_pwe = hapd->conf->sae_pwe;
return ((sae_pwe == SAE_PWE_HUNT_AND_PECK ||
sae_pwe == SAE_PWE_FORCE_HUNT_AND_PECK) &&
status_code == WLAN_STATUS_SUCCESS) ||
(sae_pwe == SAE_PWE_HASH_TO_ELEMENT &&
(status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT)) ||
(sae_pwe == SAE_PWE_BOTH &&
(status_code == WLAN_STATUS_SUCCESS ||
status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT));
}
int handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
u8 *buf, size_t len, u8 *bssid,
u16 auth_transaction, u16 status)
{
int resp = WLAN_STATUS_SUCCESS;
struct wpabuf *data = NULL;
int *groups = hapd->conf->sae_groups;
int default_group[] = { IANA_SECP256R1, 0};
const u8 *pos, *end;
int sta_removed = 0;
if (!groups) {
groups = default_group;
}
if (!sta->sae) {
if (auth_transaction != 1 ||
!sae_status_success(hapd, status)) {
wpa_printf(MSG_DEBUG, "SAE: Unexpected Status Code %u",
status);
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto reply;
}
sta->sae = os_zalloc(sizeof(*sta->sae));
if (!sta->sae) {
resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
goto remove_sta;
}
sae_set_state(sta, SAE_NOTHING, "Init");
sta->sae->sync = 0;
}
if (auth_transaction == 1) {
const u8 *token = NULL;
size_t token_len = 0;
int allow_reuse = 0;
if (!sae_status_success(hapd, status)) {
goto remove_sta;
}
if (sta->sae->state == SAE_COMMITTED) {
/* This is needed in the infrastructure BSS case to
* address a sequence where a STA entry may remain in
* hostapd across two attempts to do SAE authentication
* by the same STA. The second attempt may end up trying
* to use a different group and that would not be
* allowed if we remain in Committed state with the
* previously set parameters. */
pos = buf;
end = buf + len;
if (end - pos >= (int) sizeof(le16) &&
sae_group_allowed(sta->sae, groups,
WPA_GET_LE16(pos)) ==
WLAN_STATUS_SUCCESS) {
/* Do not waste resources deriving the same PWE
* again since the same group is reused. */
sae_set_state(sta, SAE_NOTHING,
"Allow previous PWE to be reused");
allow_reuse = 1;
} else {
sae_set_state(sta, SAE_NOTHING,
"Clear existing state to allow restart");
sae_clear_data(sta->sae);
}
}
resp = sae_parse_commit(sta->sae, buf, len, &token, &token_len, default_group,
status == WLAN_STATUS_SAE_HASH_TO_ELEMENT);
if (resp == SAE_SILENTLY_DISCARD) {
wpa_printf(MSG_DEBUG,
"SAE: Drop commit message from " MACSTR " due to reflection attack",
MAC2STR(sta->addr));
resp = WLAN_STATUS_SUCCESS;
goto remove_sta;
}
if (resp == WLAN_STATUS_UNKNOWN_PASSWORD_IDENTIFIER) {
sae_set_state(sta, SAE_NOTHING,
"Unknown Password Identifier");
goto remove_sta;
}
if (token &&
check_comeback_token(hapd->comeback_key,
hapd->comeback_pending_idx, sta->addr,
token, token_len) < 0) {
wpa_printf(MSG_DEBUG, "SAE: Drop commit message with "
"incorrect token from " MACSTR,
MAC2STR(sta->addr));
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto remove_sta;
}
if (resp != WLAN_STATUS_SUCCESS) {
goto reply;
}
if (!token && use_sae_anti_clogging(hapd) && !allow_reuse) {
int h2e = 0;
wpa_printf(MSG_DEBUG,
"SAE: Request anti-clogging token from "
MACSTR, MAC2STR(sta->addr));
if (sta->sae->tmp)
h2e = sta->sae->h2e;
if (status == WLAN_STATUS_SAE_HASH_TO_ELEMENT)
h2e = 1;
data = auth_build_token_req(
&hapd->last_comeback_key_update,
hapd->comeback_key,
hapd->comeback_idx,
hapd->comeback_pending_idx,
sizeof(hapd->comeback_pending_idx),
sta->sae->group,
sta->addr, h2e);
resp = WLAN_STATUS_ANTI_CLOGGING_TOKEN_REQ;
#ifdef ESP_SUPPLICANT
sta->sae_commit_processing = false;
#endif /* ESP_SUPPLICANT */
goto reply;
}
resp = sae_sm_step(hapd, sta, bssid, auth_transaction,
status, allow_reuse, &sta_removed);
} else if (auth_transaction == 2) {
if (status != WLAN_STATUS_SUCCESS) {
goto remove_sta;
}
if (sta->sae->state >= SAE_CONFIRMED) {
const u8 *var;
size_t var_len;
u16 peer_send_confirm;
var = buf;
var_len = len;
if (var_len < 2) {
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto reply;
}
peer_send_confirm = WPA_GET_LE16(var);
if (sta->sae->state == SAE_ACCEPTED &&
(peer_send_confirm <= sta->sae->rc ||
peer_send_confirm == 0xffff)) {
wpa_printf(MSG_DEBUG,
"SAE: Silently ignore unexpected Confirm from peer "
MACSTR
" (peer-send-confirm=%u Rc=%u)",
MAC2STR(sta->addr),
peer_send_confirm, sta->sae->rc);
return 0;
}
if (sae_check_confirm(sta->sae, buf, len) < 0) {
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto reply;
}
sta->sae->rc = peer_send_confirm;
}
resp = sae_sm_step(hapd, sta, bssid, auth_transaction,
status, 0, &sta_removed);
} else {
wpa_printf(MSG_ERROR, "unexpected SAE authentication transaction %u (status=%u )", auth_transaction, status);
if (status != WLAN_STATUS_SUCCESS) {
resp = -1;
goto remove_sta;
}
resp = WLAN_STATUS_UNKNOWN_AUTH_TRANSACTION;
}
reply:
if (!sta_removed && resp != WLAN_STATUS_SUCCESS) {
pos = buf;
end = buf + len;
/* Copy the Finite Cyclic Group field from the request if we
* rejected it as unsupported group. */
if (resp == WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED &&
!data && end - pos >= 2) {
data = wpabuf_alloc_copy(pos, 2);
}
#ifdef ESP_SUPPLICANT
if (!sta->remove_pending) {
esp_send_sae_auth_reply(hapd, bssid, bssid, WLAN_AUTH_SAE,
auth_transaction, resp,
data ? wpabuf_head(data) : (u8 *) "",
data ? wpabuf_len(data) : 0);
}
#endif /* ESP_SUPPLICANT */
}
remove_sta:
wpabuf_free(data);
return resp;
}
int auth_sae_queue(struct hostapd_data *hapd,
u8 *buf, size_t len, u8 *bssid, u16 status, u32 auth_transaction)
{
struct hostapd_sae_commit_queue *q, *q2;
unsigned int queue_len;
queue_len = dl_list_len(&hapd->sae_commit_queue);
if (queue_len >= 5) {
wpa_printf(MSG_DEBUG,
"SAE: No more room in message queue - drop the new frame from "
MACSTR, MAC2STR(bssid));
return 0;
}
wpa_printf(MSG_DEBUG, "SAE: Queue Authentication message from "
MACSTR " for processing (queue_len %u)", MAC2STR(bssid),
queue_len);
q = os_zalloc(sizeof(*q) + len);
if (!q) {
return -1;
}
q->len = len;
os_memcpy(q->msg, buf, len);
os_memcpy(q->bssid, bssid, ETH_ALEN);
q->auth_transaction = auth_transaction;
q->status = status;
/* Check whether there is already a queued Authentication frame from the
* same station with the same transaction number and if so, replace that
* queue entry with the new one. This avoids issues with a peer that
* sends multiple times (e.g., due to frequent SAE retries). There is no
* point in us trying to process the old attempts after a new one has
* obsoleted them. */
dl_list_for_each(q2, &hapd->sae_commit_queue,
struct hostapd_sae_commit_queue, list) {
if (os_memcmp(bssid, q2->bssid, ETH_ALEN) == 0 &&
auth_transaction == q2->auth_transaction) {
wpa_printf(MSG_DEBUG,
"SAE: Replace queued message from same STA with same transaction number");
dl_list_add(&q2->list, &q->list);
dl_list_del(&q2->list);
os_free(q2);
goto queued;
}
}
/* No pending identical entry, so add to the end of the queue */
dl_list_add_tail(&hapd->sae_commit_queue, &q->list);
queued:
#ifdef ESP_SUPPLICANT
/* posting event to the task to handle commit */
if (wpa3_hostap_post_evt(SIG_WPA3_RX_COMMIT, 0) != 0) {
wpa_printf(MSG_ERROR, "failed to queue commit build event");
return -1;
}
return 0;
#endif /* ESP_SUPPLICANT */
}
#endif /* CONFIG_SAE */
u16 wpa_res_to_status_code(enum wpa_validate_result res)
{
switch (res) {
case WPA_IE_OK:
return WLAN_STATUS_SUCCESS;
case WPA_INVALID_IE:
return WLAN_STATUS_INVALID_IE;
case WPA_INVALID_GROUP:
return WLAN_STATUS_GROUP_CIPHER_NOT_VALID;
case WPA_INVALID_PAIRWISE:
return WLAN_STATUS_PAIRWISE_CIPHER_NOT_VALID;
case WPA_INVALID_AKMP:
return WLAN_STATUS_AKMP_NOT_VALID;
case WPA_NOT_ENABLED:
return WLAN_STATUS_INVALID_IE;
case WPA_ALLOC_FAIL:
return WLAN_STATUS_UNSPECIFIED_FAILURE;
case WPA_MGMT_FRAME_PROTECTION_VIOLATION:
return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
case WPA_INVALID_MGMT_GROUP_CIPHER:
return WLAN_STATUS_CIPHER_REJECTED_PER_POLICY;
case WPA_INVALID_MDIE:
return WLAN_STATUS_INVALID_MDIE;
case WPA_INVALID_PROTO:
return WLAN_STATUS_INVALID_IE;
case WPA_INVALID_PMKID:
return WLAN_STATUS_INVALID_PMKID;
case WPA_DENIED_OTHER_REASON:
return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
}
return WLAN_STATUS_INVALID_IE;
}

View File

@@ -0,0 +1,20 @@
/*
* hostapd / IEEE 802.11 Management
* Copyright (c) 2002-2009, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef IEEE802_11_H
#define IEEE802_11_H
enum wpa_validate_result;
int auth_sae_queued_addr(struct hostapd_data *hapd, const u8 *addr);
int auth_sae_queue(struct hostapd_data *hapd, u8 *buf, size_t len, u8 *bssid, u16 status, u32 auth_transaction);
int handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
u8 *buf, size_t len, u8 *bssid,
u16 auth_transaction, u16 status);
u16 wpa_res_to_status_code(enum wpa_validate_result res);
#endif /* IEEE802_11_H */

View File

@@ -0,0 +1,441 @@
/*
* hostapd - PMKSA cache for IEEE 802.11i RSN
* Copyright (c) 2004-2008, 2012-2015, 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 "sta_info.h"
#include "ap_config.h"
#include "pmksa_cache_auth.h"
#include "common/eapol_common.h"
#include "common/ieee802_11_defs.h"
#include "ap/pmksa_cache_auth.h"
#include "ap/ieee802_1x.h"
static const int pmksa_cache_max_entries = 10;
static const int dot11RSNAConfigPMKLifetime = 8640000;
struct rsn_pmksa_cache {
#define PMKID_HASH_SIZE 128
#define PMKID_HASH(pmkid) (unsigned int) ((pmkid)[0] & 0x7f)
struct rsn_pmksa_cache_entry *pmkid[PMKID_HASH_SIZE];
struct rsn_pmksa_cache_entry *pmksa;
int pmksa_count;
void (*free_cb)(struct rsn_pmksa_cache_entry *entry, void *ctx);
void *ctx;
};
static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa);
static void _pmksa_cache_free_entry(struct rsn_pmksa_cache_entry *entry)
{
os_free(entry->vlan_desc);
os_free(entry->identity);
wpabuf_free(entry->cui);
bin_clear_free(entry, sizeof(*entry));
}
void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
struct rsn_pmksa_cache_entry *entry)
{
struct rsn_pmksa_cache_entry *pos, *prev;
unsigned int hash;
pmksa->pmksa_count--;
pmksa->free_cb(entry, pmksa->ctx);
/* unlink from hash list */
hash = PMKID_HASH(entry->pmkid);
pos = pmksa->pmkid[hash];
prev = NULL;
while (pos) {
if (pos == entry) {
if (prev != NULL)
prev->hnext = entry->hnext;
else
pmksa->pmkid[hash] = entry->hnext;
break;
}
prev = pos;
pos = pos->hnext;
}
/* unlink from entry list */
pos = pmksa->pmksa;
prev = NULL;
while (pos) {
if (pos == entry) {
if (prev != NULL)
prev->next = entry->next;
else
pmksa->pmksa = entry->next;
break;
}
prev = pos;
pos = pos->next;
}
_pmksa_cache_free_entry(entry);
}
/**
* pmksa_cache_auth_flush - Flush all PMKSA cache entries
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
*/
void pmksa_cache_auth_flush(struct rsn_pmksa_cache *pmksa)
{
while (pmksa->pmksa) {
wpa_printf(MSG_DEBUG, "RSN: Flush PMKSA cache entry for "
MACSTR, MAC2STR(pmksa->pmksa->spa));
pmksa_cache_free_entry(pmksa, pmksa->pmksa);
}
}
static void pmksa_cache_expire(void *eloop_ctx, void *timeout_ctx)
{
struct rsn_pmksa_cache *pmksa = eloop_ctx;
struct os_reltime now;
os_get_reltime(&now);
while (pmksa->pmksa && pmksa->pmksa->expiration <= now.sec) {
wpa_printf(MSG_DEBUG, "RSN: expired PMKSA cache entry for "
MACSTR, MAC2STR(pmksa->pmksa->spa));
pmksa_cache_free_entry(pmksa, pmksa->pmksa);
}
pmksa_cache_set_expiration(pmksa);
}
static void pmksa_cache_set_expiration(struct rsn_pmksa_cache *pmksa)
{
int sec;
struct os_reltime now;
eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
if (pmksa->pmksa == NULL)
return;
os_get_reltime(&now);
sec = pmksa->pmksa->expiration - now.sec;
if (sec < 0)
sec = 0;
eloop_register_timeout(sec + 1, 0, pmksa_cache_expire, pmksa, NULL);
}
static void pmksa_cache_from_eapol_data(struct rsn_pmksa_cache_entry *entry,
struct eapol_state_machine *eapol)
{
if (eapol == NULL)
return;
}
static void pmksa_cache_link_entry(struct rsn_pmksa_cache *pmksa,
struct rsn_pmksa_cache_entry *entry)
{
struct rsn_pmksa_cache_entry *pos, *prev;
int hash;
/* Add the new entry; order by expiration time */
pos = pmksa->pmksa;
prev = NULL;
while (pos) {
if (pos->expiration > entry->expiration)
break;
prev = pos;
pos = pos->next;
}
if (prev == NULL) {
entry->next = pmksa->pmksa;
pmksa->pmksa = entry;
} else {
entry->next = prev->next;
prev->next = entry;
}
hash = PMKID_HASH(entry->pmkid);
entry->hnext = pmksa->pmkid[hash];
pmksa->pmkid[hash] = entry;
pmksa->pmksa_count++;
if (prev == NULL)
pmksa_cache_set_expiration(pmksa);
wpa_printf(MSG_DEBUG, "RSN: added PMKSA cache entry for " MACSTR,
MAC2STR(entry->spa));
wpa_hexdump(MSG_DEBUG, "RSN: added PMKID", entry->pmkid, PMKID_LEN);
}
/**
* pmksa_cache_auth_add - Add a PMKSA cache entry
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
* @pmk: The new pairwise master key
* @pmk_len: PMK length in bytes, usually PMK_LEN (32)
* @pmkid: Calculated PMKID
* @kck: Key confirmation key or %NULL if not yet derived
* @kck_len: KCK length in bytes
* @aa: Authenticator address
* @spa: Supplicant address
* @session_timeout: Session timeout
* @eapol: Pointer to EAPOL state machine data
* @akmp: WPA_KEY_MGMT_* used in key derivation
* Returns: Pointer to the added PMKSA cache entry or %NULL on error
*
* This function create a PMKSA entry for a new PMK and adds it to the PMKSA
* cache. If an old entry is already in the cache for the same Supplicant,
* this entry will be replaced with the new entry. PMKID will be calculated
* based on the PMK.
*/
struct rsn_pmksa_cache_entry *
pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
const u8 *pmk, size_t pmk_len, const u8 *pmkid,
const u8 *kck, size_t kck_len,
const u8 *aa, const u8 *spa, int session_timeout,
struct eapol_state_machine *eapol, int akmp)
{
struct rsn_pmksa_cache_entry *entry;
entry = pmksa_cache_auth_create_entry(pmk, pmk_len, pmkid, kck, kck_len,
aa, spa, session_timeout, eapol,
akmp);
if (pmksa_cache_auth_add_entry(pmksa, entry) < 0)
return NULL;
return entry;
}
/**
* pmksa_cache_auth_create_entry - Create a PMKSA cache entry
* @pmk: The new pairwise master key
* @pmk_len: PMK length in bytes, usually PMK_LEN (32)
* @pmkid: Calculated PMKID
* @kck: Key confirmation key or %NULL if not yet derived
* @kck_len: KCK length in bytes
* @aa: Authenticator address
* @spa: Supplicant address
* @session_timeout: Session timeout
* @eapol: Pointer to EAPOL state machine data
* @akmp: WPA_KEY_MGMT_* used in key derivation
* Returns: Pointer to the added PMKSA cache entry or %NULL on error
*
* This function creates a PMKSA entry.
*/
struct rsn_pmksa_cache_entry *
pmksa_cache_auth_create_entry(const u8 *pmk, size_t pmk_len, const u8 *pmkid,
const u8 *kck, size_t kck_len, const u8 *aa,
const u8 *spa, int session_timeout,
struct eapol_state_machine *eapol, int akmp)
{
struct rsn_pmksa_cache_entry *entry;
struct os_reltime now;
if (pmk_len > PMK_LEN_MAX)
return NULL;
if (wpa_key_mgmt_suite_b(akmp) && !kck)
return NULL;
entry = os_zalloc(sizeof(*entry));
if (entry == NULL)
return NULL;
os_memcpy(entry->pmk, pmk, pmk_len);
entry->pmk_len = pmk_len;
if (pmkid)
os_memcpy(entry->pmkid, pmkid, PMKID_LEN);
else if (akmp == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192)
rsn_pmkid_suite_b_192(kck, kck_len, aa, spa, entry->pmkid);
else if (wpa_key_mgmt_suite_b(akmp))
rsn_pmkid_suite_b(kck, kck_len, aa, spa, entry->pmkid);
else
rsn_pmkid(pmk, pmk_len, aa, spa, entry->pmkid, akmp);
os_get_reltime(&now);
entry->expiration = now.sec;
if (session_timeout > 0)
entry->expiration += session_timeout;
else
entry->expiration += dot11RSNAConfigPMKLifetime;
entry->akmp = akmp;
os_memcpy(entry->spa, spa, ETH_ALEN);
pmksa_cache_from_eapol_data(entry, eapol);
return entry;
}
/**
* pmksa_cache_auth_add_entry - Add a PMKSA cache entry
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
* @entry: Pointer to PMKSA cache entry
*
* This function adds PMKSA cache entry to the PMKSA cache. If an old entry is
* already in the cache for the same Supplicant, this entry will be replaced
* with the new entry. PMKID will be calculated based on the PMK.
*/
int pmksa_cache_auth_add_entry(struct rsn_pmksa_cache *pmksa,
struct rsn_pmksa_cache_entry *entry)
{
struct rsn_pmksa_cache_entry *pos;
if (entry == NULL)
return -1;
/* Replace an old entry for the same STA (if found) with the new entry
*/
pos = pmksa_cache_auth_get(pmksa, entry->spa, NULL);
if (pos)
pmksa_cache_free_entry(pmksa, pos);
if (pmksa->pmksa_count >= pmksa_cache_max_entries && pmksa->pmksa) {
/* Remove the oldest entry to make room for the new entry */
wpa_printf(MSG_DEBUG, "RSN: removed the oldest PMKSA cache "
"entry (for " MACSTR ") to make room for new one",
MAC2STR(pmksa->pmksa->spa));
pmksa_cache_free_entry(pmksa, pmksa->pmksa);
}
pmksa_cache_link_entry(pmksa, entry);
return 0;
}
/**
* pmksa_cache_auth_deinit - Free all entries in PMKSA cache
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
*/
void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa)
{
struct rsn_pmksa_cache_entry *entry, *prev;
int i;
if (pmksa == NULL)
return;
entry = pmksa->pmksa;
while (entry) {
prev = entry;
entry = entry->next;
_pmksa_cache_free_entry(prev);
}
eloop_cancel_timeout(pmksa_cache_expire, pmksa, NULL);
pmksa->pmksa_count = 0;
pmksa->pmksa = NULL;
for (i = 0; i < PMKID_HASH_SIZE; i++)
pmksa->pmkid[i] = NULL;
os_free(pmksa);
}
/**
* pmksa_cache_auth_get - Fetch a PMKSA cache entry
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
* @spa: Supplicant address or %NULL to match any
* @pmkid: PMKID or %NULL to match any
* Returns: Pointer to PMKSA cache entry or %NULL if no match was found
*/
struct rsn_pmksa_cache_entry *
pmksa_cache_auth_get(struct rsn_pmksa_cache *pmksa,
const u8 *spa, const u8 *pmkid)
{
struct rsn_pmksa_cache_entry *entry;
if (pmkid) {
for (entry = pmksa->pmkid[PMKID_HASH(pmkid)]; entry;
entry = entry->hnext) {
if ((spa == NULL ||
os_memcmp(entry->spa, spa, ETH_ALEN) == 0) &&
os_memcmp(entry->pmkid, pmkid, PMKID_LEN) == 0)
return entry;
}
} else {
for (entry = pmksa->pmksa; entry; entry = entry->next) {
if (spa == NULL ||
os_memcmp(entry->spa, spa, ETH_ALEN) == 0)
return entry;
}
}
return NULL;
}
/**
* pmksa_cache_auth_init - Initialize PMKSA cache
* @free_cb: Callback function to be called when a PMKSA cache entry is freed
* @ctx: Context pointer for free_cb function
* Returns: Pointer to PMKSA cache data or %NULL on failure
*/
struct rsn_pmksa_cache *
pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
void *ctx), void *ctx)
{
struct rsn_pmksa_cache *pmksa;
pmksa = os_zalloc(sizeof(*pmksa));
if (pmksa) {
pmksa->free_cb = free_cb;
pmksa->ctx = ctx;
}
return pmksa;
}
/**
* pmksa_cache_auth_list - Dump text list of entries in PMKSA cache
* @pmksa: Pointer to PMKSA cache data from pmksa_cache_auth_init()
* @buf: Buffer for the list
* @len: Length of the buffer
* Returns: Number of bytes written to buffer
*
* This function is used to generate a text format representation of the
* current PMKSA cache contents for the ctrl_iface PMKSA command.
*/
int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len)
{
int i, ret;
char *pos = buf;
struct rsn_pmksa_cache_entry *entry;
struct os_reltime now;
os_get_reltime(&now);
ret = os_snprintf(pos, buf + len - pos,
"Index / SPA / PMKID / expiration (in seconds) / opportunistic\n");
if (os_snprintf_error(buf + len - pos, ret))
return pos - buf;
pos += ret;
i = 0;
entry = pmksa->pmksa;
while (entry) {
ret = os_snprintf(pos, buf + len - pos, "%d " MACSTR " ",
i, MAC2STR(entry->spa));
if (os_snprintf_error(buf + len - pos, ret))
return pos - buf;
pos += ret;
pos += wpa_snprintf_hex(pos, buf + len - pos, entry->pmkid,
PMKID_LEN);
ret = os_snprintf(pos, buf + len - pos, " %d %d\n",
(int) (entry->expiration - now.sec),
entry->opportunistic);
if (os_snprintf_error(buf + len - pos, ret))
return pos - buf;
pos += ret;
entry = entry->next;
}
return pos - buf;
}

View File

@@ -0,0 +1,77 @@
/*
* hostapd - PMKSA cache for IEEE 802.11i RSN
* Copyright (c) 2004-2008, 2012, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#ifndef PMKSA_CACHE_H
#define PMKSA_CACHE_H
#include "ap/ieee802_1x.h"
/**
* struct rsn_pmksa_cache_entry - PMKSA cache entry
*/
struct rsn_pmksa_cache_entry {
struct rsn_pmksa_cache_entry *next, *hnext;
u8 pmkid[PMKID_LEN];
u8 pmk[PMK_LEN_MAX];
size_t pmk_len;
os_time_t expiration;
int akmp; /* WPA_KEY_MGMT_* */
u8 spa[ETH_ALEN];
u8 *identity;
size_t identity_len;
struct wpabuf *cui;
u8 eap_type_authsrv;
struct vlan_description *vlan_desc;
int opportunistic;
u64 acct_multi_session_id;
};
struct rsn_pmksa_cache;
struct radius_das_attrs;
struct rsn_pmksa_cache *
pmksa_cache_auth_init(void (*free_cb)(struct rsn_pmksa_cache_entry *entry,
void *ctx), void *ctx);
void pmksa_cache_auth_deinit(struct rsn_pmksa_cache *pmksa);
struct rsn_pmksa_cache_entry *
pmksa_cache_auth_get(struct rsn_pmksa_cache *pmksa,
const u8 *spa, const u8 *pmkid);
struct rsn_pmksa_cache_entry * pmksa_cache_get_okc(
struct rsn_pmksa_cache *pmksa, const u8 *spa, const u8 *aa,
const u8 *pmkid);
struct rsn_pmksa_cache_entry *
pmksa_cache_auth_add(struct rsn_pmksa_cache *pmksa,
const u8 *pmk, size_t pmk_len, const u8 *pmkid,
const u8 *kck, size_t kck_len,
const u8 *aa, const u8 *spa, int session_timeout,
struct eapol_state_machine *eapol, int akmp);
struct rsn_pmksa_cache_entry *
pmksa_cache_auth_create_entry(const u8 *pmk, size_t pmk_len, const u8 *pmkid,
const u8 *kck, size_t kck_len, const u8 *aa,
const u8 *spa, int session_timeout,
struct eapol_state_machine *eapol, int akmp);
int pmksa_cache_auth_add_entry(struct rsn_pmksa_cache *pmksa,
struct rsn_pmksa_cache_entry *entry);
struct rsn_pmksa_cache_entry *
pmksa_cache_add_okc(struct rsn_pmksa_cache *pmksa,
const struct rsn_pmksa_cache_entry *old_entry,
const u8 *aa, const u8 *pmkid);
void pmksa_cache_to_eapol_data(struct hostapd_data *hapd,
struct rsn_pmksa_cache_entry *entry,
struct eapol_state_machine *eapol);
void pmksa_cache_free_entry(struct rsn_pmksa_cache *pmksa,
struct rsn_pmksa_cache_entry *entry);
int pmksa_cache_auth_radius_das_disconnect(struct rsn_pmksa_cache *pmksa,
struct radius_das_attrs *attr);
int pmksa_cache_auth_list(struct rsn_pmksa_cache *pmksa, char *buf, size_t len);
void pmksa_cache_auth_flush(struct rsn_pmksa_cache *pmksa);
int pmksa_cache_auth_list_mesh(struct rsn_pmksa_cache *pmksa, const u8 *addr,
char *buf, size_t len);
#endif /* PMKSA_CACHE_H */

View File

@@ -11,6 +11,7 @@
#include "utils/common.h"
#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
#include "common/sae.h"
#include "crypto/crypto.h"
#include "hostapd.h"
#include "ieee802_1x.h"
@@ -104,6 +105,15 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
hapd->num_sta--;
#ifdef CONFIG_SAE
sae_clear_data(sta->sae);
os_free(sta->sae);
if (sta->lock) {
os_mutex_unlock(sta->lock);
os_mutex_delete(sta->lock);
sta->lock = NULL;
}
#endif /* CONFIG_SAE */
wpa_auth_sta_deinit(sta->wpa_sm);
#ifdef CONFIG_WPS_REGISTRAR
if (ap_sta_pending_delayed_1x_auth_fail_disconnect(hapd, sta))
@@ -162,6 +172,11 @@ struct sta_info * ap_sta_add(struct hostapd_data *hapd, const u8 *addr)
hapd->sta_list = sta;
hapd->num_sta++;
ap_sta_hash_add(hapd, sta);
#ifdef CONFIG_SAE
sta->sae_commit_processing = false;
sta->remove_pending = false;
sta->lock = os_mutex_create();
#endif /* CONFIG_SAE */
return sta;
}

View File

@@ -50,6 +50,7 @@ struct sta_info {
char *identity; /* User-Name from RADIUS */
u16 auth_alg;
#ifdef CONFIG_INTERWORKING
#define GAS_DIALOG_MAX 8 /* Max concurrent dialog number */
struct gas_dialog_info *gas_dialog;
@@ -57,10 +58,17 @@ struct sta_info {
#endif /* CONFIG_INTERWORKING */
struct wpabuf *wps_ie; /* WPS IE from (Re)Association Request */
#ifdef ESP_SUPPLICANT
#ifdef CONFIG_SAE
enum { SAE_INIT, SAE_COMMIT, SAE_CONFIRM } sae_state;
u16 sae_send_confirm;
void *lock;
struct sae_data *sae;
bool sae_commit_processing; /* halt queuing commit while we are
* processing commit for that station */
bool remove_pending; /* Flag to indicate to free station when
* whose mutex is taken by task */
#endif /* CONFIG_SAE */
#endif /* ESP_SUPPLICANT */
};

View File

@@ -11,6 +11,9 @@
#include "utils/eloop.h"
#include "utils/state_machine.h"
#include "common/ieee802_11_defs.h"
#include "common/sae.h"
#include "ap/sta_info.h"
#include "ap/ieee802_11.h"
#include "ap/wpa_auth.h"
#include "ap/wpa_auth_i.h"
#include "ap/wpa_auth_ie.h"
@@ -20,7 +23,9 @@
#include "ap/ap_config.h"
#include "ap/sta_info.h"
#include "common/wpa_common.h"
#include "ap/pmksa_cache_auth.h"
#include "crypto/aes.h"
#include "crypto/aes_wrap.h"
#include "crypto/crypto.h"
#include "crypto/sha1.h"
@@ -32,6 +37,7 @@
#include "esp_private/wifi.h"
#include "esp_wpas_glue.h"
#include "esp_wps_i.h"
#include "esp_hostap.h"
#define STATE_MACHINE_DATA struct wpa_state_machine
#define STATE_MACHINE_DEBUG_PREFIX "WPA"
@@ -127,6 +133,20 @@ static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth,
return NULL;
}
#ifdef CONFIG_SAE
struct sta_info *sta = ap_get_sta(hapd, addr);
if (sta && sta->auth_alg == WLAN_AUTH_SAE) {
if (!sta->sae || prev_psk)
return NULL;
return sta->sae->pmk;
}
if (sta && wpa_auth_uses_sae(sta->wpa_sm)) {
wpa_printf(MSG_DEBUG,
"No PSK for STA trying to use SAE with PMKSA caching");
return NULL;
}
#endif /*CONFIG_SAE*/
return (u8*)hostapd_get_psk(hapd->conf, addr, prev_psk);
}
@@ -246,6 +266,23 @@ static void wpa_rekey_ptk(void *eloop_ctx, void *timeout_ctx)
wpa_sm_step(sm);
}
static int wpa_auth_pmksa_clear_cb(struct wpa_state_machine *sm, void *ctx)
{
if (sm->pmksa == ctx)
sm->pmksa = NULL;
return 0;
}
static void wpa_auth_pmksa_free_cb(struct rsn_pmksa_cache_entry *entry,
void *ctx)
{
struct wpa_authenticator *wpa_auth = ctx;
wpa_auth_for_each_sta(wpa_auth, wpa_auth_pmksa_clear_cb, entry);
}
static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth,
struct wpa_group *group)
{
@@ -351,6 +388,17 @@ struct wpa_authenticator * wpa_init(const u8 *addr,
return NULL;
}
wpa_auth->pmksa = pmksa_cache_auth_init(wpa_auth_pmksa_free_cb,
wpa_auth);
if (wpa_auth->pmksa == NULL) {
wpa_printf(MSG_ERROR, "PMKSA cache initialization failed.");
os_free(wpa_auth->group);
os_free(wpa_auth->wpa_ie);
os_free(wpa_auth);
return NULL;
}
#ifdef CONFIG_IEEE80211R_AP
wpa_auth->ft_pmk_cache = wpa_ft_pmk_cache_init();
if (wpa_auth->ft_pmk_cache == NULL) {
@@ -446,7 +494,13 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm)
void wpa_auth_sta_deinit(struct wpa_state_machine *sm)
{
#ifdef ESP_SUPPLICANT
if (esp_wifi_ap_is_sta_sae_reauth_node(sm->addr)) {
wpa_printf( MSG_DEBUG, "deinit old sm=%p\n", sm);
}
#else /* ESP_SUPPLICANT */
wpa_printf( MSG_DEBUG, "deinit sm=%p\n", sm);
#endif /* ESP_SUPPLICANT */
if (sm == NULL)
return;
@@ -652,15 +706,21 @@ void wpa_receive(struct wpa_authenticator *wpa_auth, struct wpa_state_machine *s
sm->pairwise == WPA_CIPHER_GCMP) {
if (wpa_use_aes_cmac(sm) &&
!wpa_key_mgmt_suite_b(sm->wpa_key_mgmt) &&
!wpa_use_akm_defined(sm->wpa_key_mgmt) &&
ver != WPA_KEY_INFO_TYPE_AES_128_CMAC) {
return;
}
if (!wpa_use_aes_cmac(sm) &&
!wpa_use_akm_defined(sm->wpa_key_mgmt) &&
ver != WPA_KEY_INFO_TYPE_HMAC_SHA1_AES) {
return;
}
}
if (wpa_use_akm_defined(sm->wpa_key_mgmt) &&
ver != WPA_KEY_INFO_TYPE_AKM_DEFINED){
return;
}
}
if (key_info & WPA_KEY_INFO_REQUEST) {
@@ -882,6 +942,21 @@ continue_processing:
wpa_sm_step(sm);
}
int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
const u8 *pmk, const u8 *pmkid, bool cache_pmksa)
{
if (cache_pmksa)
return -1;
wpa_hexdump_key(MSG_DEBUG, "RSN: Cache PMK from SAE", pmk, PMK_LEN);
if (pmksa_cache_auth_add(wpa_auth->pmksa, pmk, PMK_LEN, pmkid,
NULL, 0,
wpa_auth->addr, addr, 0, NULL,
WPA_KEY_MGMT_SAE))
return 0;
return -1;
}
static int wpa_gmk_to_gtk(const u8 *gmk, const char *label, const u8 *addr,
const u8 *gnonce, u8 *gtk, size_t gtk_len)
@@ -937,6 +1012,8 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
if (force_version)
version = force_version;
else if (wpa_use_akm_defined(sm->wpa_key_mgmt))
version = WPA_KEY_INFO_TYPE_AKM_DEFINED;
else if (wpa_use_aes_cmac(sm))
version = WPA_KEY_INFO_TYPE_AES_128_CMAC;
else if (sm->pairwise != WPA_CIPHER_TKIP)
@@ -944,7 +1021,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
else
version = WPA_KEY_INFO_TYPE_HMAC_MD5_RC4;
pairwise = key_info & WPA_KEY_INFO_KEY_TYPE;
pairwise = !!(key_info & WPA_KEY_INFO_KEY_TYPE);
wpa_printf( MSG_DEBUG, "WPA: Send EAPOL(version=%d secure=%d mic=%d "
"ack=%d install=%d pairwise=%d kde_len=%lu keyidx=%d "
@@ -959,6 +1036,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
key_data_len = kde_len;
if ((version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
wpa_use_aes_key_wrap(sm->wpa_key_mgmt) ||
version == WPA_KEY_INFO_TYPE_AES_128_CMAC) && encr) {
pad_len = key_data_len % 8;
if (pad_len)
@@ -1027,6 +1105,7 @@ void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
wpa_hexdump_key(MSG_DEBUG, "Plaintext EAPOL-Key Key Data",
buf, key_data_len);
if (version == WPA_KEY_INFO_TYPE_HMAC_SHA1_AES ||
wpa_use_aes_key_wrap(sm->wpa_key_mgmt) ||
version == WPA_KEY_INFO_TYPE_AES_128_CMAC) {
if (aes_wrap(sm->PTK.kek, sm->PTK.kek_len, (key_data_len - 8) / 8, buf,
(u8 *) (key + 1))) {
@@ -1423,6 +1502,13 @@ SM_STATE(WPA_PTK, INITPSK)
sm->xxkey_len = PMK_LEN;
#endif /* CONFIG_IEEE80211R_AP */
}
#ifdef CONFIG_SAE
if (wpa_auth_uses_sae(sm) && sm->pmksa) {
wpa_printf(MSG_DEBUG, "SAE: PMK from PMKSA cache");
os_memcpy(sm->PMK, sm->pmksa->pmk, sm->pmksa->pmk_len);
sm->pmk_len = sm->pmksa->pmk_len;
}
#endif
sm->req_replay_counter_used = 0;
}
@@ -1448,7 +1534,8 @@ SM_STATE(WPA_PTK, PTKSTART)
* one possible PSK for this STA.
*/
if (sm->wpa == WPA_VERSION_WPA2 &&
wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt)) {
(wpa_key_mgmt_wpa_ieee8021x(sm->wpa_key_mgmt) ||
wpa_key_mgmt_sae(sm->wpa_key_mgmt))) {
pmkid = buf;
pmkid_len = 2 + RSN_SELECTOR_LEN + PMKID_LEN;
pmkid[0] = WLAN_EID_VENDOR_SPECIFIC;
@@ -1493,6 +1580,10 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
struct wpa_ptk PTK;
int ok = 0;
const u8 *pmk = NULL;
u16 key_data_length;
struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *key;
struct wpa_eapol_ie_parse kde;
SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
sm->EAPOLKeyReceived = FALSE;
@@ -1502,7 +1593,8 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
* WPA-PSK: iterate through possible PSKs and select the one matching
* the packet */
for (;;) {
if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt)) {
if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
!wpa_key_mgmt_sae(sm->wpa_key_mgmt)) {
wpa_printf( MSG_DEBUG, "wpa psk");
pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, pmk);
if (pmk == NULL){
@@ -1513,6 +1605,11 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
pmk = sm->PMK;
}
if (!pmk && sm->pmksa) {
wpa_printf(MSG_DEBUG, "WPA: Use PMK from PMKSA cache");
pmk = sm->pmksa->pmk;
}
wpa_derive_ptk(sm, sm->SNonce, pmk, &PTK);
if (wpa_verify_key_mic(sm->wpa_key_mgmt, &PTK,
@@ -1535,6 +1632,19 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
return;
}
hdr = (struct ieee802_1x_hdr *) sm->last_rx_eapol_key;
key = (struct wpa_eapol_key *) (hdr + 1);
key_data_length = WPA_GET_BE16(key->key_data_length);
if (key_data_length > sm->last_rx_eapol_key_len - sizeof(*hdr) -
sizeof(*key))
return;
if (wpa_parse_kde_ies((u8 *) (key + 1), key_data_length, &kde) < 0) {
wpa_printf(MSG_DEBUG,
"received EAPOL-Key msg 2/4 with invalid Key Data contents");
return;
}
#ifdef CONFIG_IEEE80211R_AP
if (sm->wpa == WPA_VERSION_WPA2 && wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
/*
@@ -1553,6 +1663,21 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
}
#endif /* CONFIG_IEEE80211R_AP */
if ((!sm->rsnxe && kde.rsnxe) ||
(sm->rsnxe && !kde.rsnxe) ||
(sm->rsnxe && kde.rsnxe &&
(sm->rsnxe_len != kde.rsnxe_len ||
os_memcmp(sm->rsnxe, kde.rsnxe, sm->rsnxe_len) != 0))) {
wpa_printf(MSG_DEBUG,
"RSNXE from (Re)AssocReq did not match the one in EAPOL-Key msg 2/4");
wpa_hexdump(MSG_DEBUG, "RSNXE in AssocReq",
sm->rsnxe, sm->rsnxe_len);
wpa_hexdump(MSG_DEBUG, "RSNXE in EAPOL-Key msg 2/4",
kde.rsnxe, kde.rsnxe_len);
wpa_sta_disconnect(sm->wpa_auth, sm->addr);
return;
}
sm->pending_1_of_4_timeout = 0;
eloop_cancel_timeout(resend_eapol_handle, (void*)(sm->index), NULL);
@@ -1664,6 +1789,8 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
wpa_ie_len > wpa_ie[1] + 2 && wpa_ie[0] == WLAN_EID_RSN) {
/* WPA-only STA, remove RSN IE */
wpa_ie = wpa_ie + wpa_ie[1] + 2;
if (wpa_ie[0] == WLAN_EID_RSNX)
wpa_ie = wpa_ie + wpa_ie[1] + 2;
wpa_ie_len = wpa_ie[1] + 2;
}
if (sm->wpa == WPA_VERSION_WPA2) {
@@ -1888,9 +2015,13 @@ SM_STEP(WPA_PTK)
}
break;
case WPA_PTK_INITPSK:
if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, NULL))
if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, NULL)) {
SM_ENTER(WPA_PTK, PTKSTART);
else {
#ifdef CONFIG_SAE
} else if (wpa_auth_uses_sae(sm) && sm->pmksa) {
SM_ENTER(WPA_PTK, PTKSTART);
#endif /* CONFIG_SAE */
} else {
SM_ENTER(WPA_PTK, DISCONNECT);
}
break;
@@ -2359,11 +2490,35 @@ static int wpa_sm_step(struct wpa_state_machine *sm)
return 0;
}
bool wpa_ap_join(struct sta_info *sta, uint8_t *bssid, uint8_t *wpa_ie, uint8_t wpa_ie_len, bool *pmf_enable)
void wpa_deinit(struct wpa_authenticator *wpa_auth)
{
struct wpa_group *group, *prev;
pmksa_cache_auth_deinit(wpa_auth->pmksa);
if (wpa_auth->wpa_ie != NULL) {
os_free(wpa_auth->wpa_ie);
}
if (wpa_auth->group != NULL) {
group = wpa_auth->group;
while (group) {
prev = group;
group = group->next;
bin_clear_free(prev, sizeof(*prev));
}
}
os_free(wpa_auth);
}
bool wpa_ap_join(struct sta_info *sta, uint8_t *bssid, uint8_t *wpa_ie,
uint8_t wpa_ie_len, uint8_t *rsnxe, uint8_t rsnxe_len,
bool *pmf_enable, int subtype)
{
struct hostapd_data *hapd = (struct hostapd_data*)esp_wifi_get_hostap_private_internal();
enum wpa_validate_result status_code = WPA_IE_OK;
int resp = WLAN_STATUS_SUCCESS;
bool omit_rsnxe = false;
if (!sta || !bssid || !wpa_ie){
if (!sta || !bssid || !wpa_ie) {
return false;
}
@@ -2377,15 +2532,28 @@ bool wpa_ap_join(struct sta_info *sta, uint8_t *bssid, uint8_t *wpa_ie, uint8_t
wpa_printf( MSG_DEBUG, "init wpa sm=%p", sta->wpa_sm);
if (sta->wpa_sm == NULL) {
resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
goto send_resp;
}
status_code = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, wpa_ie, wpa_ie_len, rsnxe, rsnxe_len);
resp = wpa_res_to_status_code(status_code);
send_resp:
if (!rsnxe) {
omit_rsnxe = true;
}
if (esp_send_assoc_resp(hapd, sta, bssid, resp, omit_rsnxe, subtype) != WLAN_STATUS_SUCCESS) {
resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
}
if (resp != WLAN_STATUS_SUCCESS) {
return false;
}
if (wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm, wpa_ie, wpa_ie_len)) {
return false;
}
//Check whether AP uses Management Frame Protection for this connection
*pmf_enable = wpa_auth_uses_mfp(sta->wpa_sm);
//Check whether AP uses Management Frame Protection for this connection
*pmf_enable = wpa_auth_uses_mfp(sta->wpa_sm);
}
wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm);
@@ -2416,11 +2584,23 @@ bool wpa_ap_remove(void* sta_info)
if (!sta_info || !hapd) {
return false;
}
struct sta_info *sta = NULL;
sta = (struct sta_info*)sta_info;
#ifdef CONFIG_SAE
if (sta->lock) {
if (os_mutex_lock(sta->lock)) {
ap_free_sta(hapd, sta);
} else {
sta->remove_pending = true;
}
return true;
}
#endif /* CONFIG_SAE */
#ifdef CONFIG_WPS_REGISTRAR
wpa_printf(MSG_DEBUG, "wps_status=%d", wps_get_status());
if (wps_get_status() == WPS_STATUS_PENDING) {
struct sta_info *sta = (struct sta_info *)sta_info;
u8 *addr = os_malloc(ETH_ALEN);
if (!addr) {
@@ -2430,7 +2610,29 @@ bool wpa_ap_remove(void* sta_info)
eloop_register_timeout(0, 10000, ap_free_sta_timeout, hapd, addr);
} else
#endif
ap_free_sta(hapd, sta_info);
ap_free_sta(hapd, sta);
return true;
}
void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
const u8 *sta_addr)
{
struct rsn_pmksa_cache_entry *pmksa;
if (wpa_auth == NULL || wpa_auth->pmksa == NULL)
return;
pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sta_addr, NULL);
if (pmksa) {
wpa_printf(MSG_DEBUG, "WPA: Remove PMKSA cache entry for "
MACSTR " based on request", MAC2STR(sta_addr));
pmksa_cache_free_entry(wpa_auth->pmksa, pmksa);
}
}
int wpa_auth_uses_sae(struct wpa_state_machine *sm)
{
if (sm == NULL)
return 0;
return wpa_key_mgmt_sae(sm->wpa_key_mgmt);
}

View File

@@ -161,6 +161,7 @@ struct wpa_auth_config {
#endif /* CONFIG_IEEE80211R */
int disable_gtk;
int ap_mlme;
enum sae_pwe sae_pwe;
struct rsn_sppamsdu_sup spp_sup;
};
@@ -213,16 +214,19 @@ void wpa_deinit(struct wpa_authenticator *wpa_auth);
int wpa_reconfig(struct wpa_authenticator *wpa_auth,
struct wpa_auth_config *conf);
enum {
enum wpa_validate_result{
WPA_IE_OK, WPA_INVALID_IE, WPA_INVALID_GROUP, WPA_INVALID_PAIRWISE,
WPA_INVALID_AKMP, WPA_NOT_ENABLED, WPA_ALLOC_FAIL,
WPA_MGMT_FRAME_PROTECTION_VIOLATION, WPA_INVALID_MGMT_GROUP_CIPHER,
WPA_INVALID_MDIE, WPA_INVALID_PROTO
WPA_INVALID_MDIE, WPA_INVALID_PROTO, WPA_INVALID_PMKID,
WPA_DENIED_OTHER_REASON
};
int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
enum wpa_validate_result
wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm,
const u8 *wpa_ie, size_t wpa_ie_len/*,
const u8 *wpa_ie, size_t wpa_ie_len,
const u8 *rsnxe, size_t rsnxe_len/*,
const u8 *mdie, size_t mdie_len*/);
int wpa_auth_uses_mfp(struct wpa_state_machine *sm);
struct wpa_state_machine *
@@ -290,5 +294,10 @@ int wpa_wnmsleep_gtk_subelem(struct wpa_state_machine *sm, u8 *pos);
int wpa_wnmsleep_igtk_subelem(struct wpa_state_machine *sm, u8 *pos);
int wpa_auth_uses_sae(struct wpa_state_machine *sm);
int wpa_auth_pmksa_add_sae(struct wpa_authenticator *wpa_auth, const u8 *addr,
const u8 *pmk, const u8 *pmkid,bool cache_pmksa);
void wpa_auth_add_sae_pmkid(struct wpa_state_machine *sm, const u8 *pmkid);
void wpa_auth_pmksa_remove(struct wpa_authenticator *wpa_auth,
const u8 *sta_addr);
#endif /* WPA_AUTH_H */

View File

@@ -58,6 +58,8 @@ struct wpa_state_machine {
u8 ANonce[WPA_NONCE_LEN];
u8 SNonce[WPA_NONCE_LEN];
u8 PMK[PMK_LEN];
unsigned int pmk_len;
u8 pmkid[PMKID_LEN];
struct wpa_ptk PTK;
Boolean PTK_valid;
Boolean pairwise_set;
@@ -94,6 +96,8 @@ struct wpa_state_machine {
u8 *wpa_ie;
size_t wpa_ie_len;
u8 *rsnxe;
size_t rsnxe_len;
enum {
WPA_VERSION_NO_WPA = 0 /* WPA not used */,
@@ -102,6 +106,7 @@ struct wpa_state_machine {
} wpa;
int pairwise; /* Pairwise cipher suite, WPA_CIPHER_* */
int wpa_key_mgmt; /* the selected WPA_KEY_MGMT_* */
struct rsn_pmksa_cache_entry *pmksa;
#ifdef CONFIG_IEEE80211R_AP
u8 xxkey[PMK_LEN_MAX]; /* PSK or the second 256 bits of MSK, or the
@@ -171,14 +176,16 @@ struct wpa_ft_pmk_cache;
struct wpa_authenticator {
struct wpa_group *group;
u8 dot11RSNAPMKIDUsed[PMKID_LEN];
struct wpa_auth_config conf;
u8 *wpa_ie;
size_t wpa_ie_len;
struct rsn_pmksa_cache *pmksa;
u8 addr[ETH_ALEN];
#ifdef CONFIG_IEEE80211R
struct rsn_pmksa_cache *pmksa;
struct wpa_ft_pmk_cache *ft_pmk_cache;
#endif
@@ -187,6 +194,7 @@ struct wpa_authenticator {
int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
const u8 *pmkid);
int wpa_write_rsnxe(struct wpa_auth_config *conf, u8 *buf, size_t len);
void __wpa_send_eapol(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm, int key_info,
const u8 *key_rsc, const u8 *nonce,

View File

@@ -14,6 +14,7 @@
#include "ap/wpa_auth_i.h"
#include "common/wpa_common.h"
#include "utils/wpa_debug.h"
#include "ap/pmksa_cache_auth.h"
#ifdef CONFIG_RSN_TESTING
int rsn_testing = 0;
@@ -298,6 +299,36 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
}
int wpa_write_rsnxe(struct wpa_auth_config *conf, u8 *buf, size_t len)
{
u8 *pos = buf;
u16 capab = 0;
size_t flen;
if (wpa_key_mgmt_sae(conf->wpa_key_mgmt) &&
(conf->sae_pwe == SAE_PWE_HASH_TO_ELEMENT ||
conf->sae_pwe == SAE_PWE_BOTH)) {
capab |= BIT(WLAN_RSNX_CAPAB_SAE_H2E);
}
flen = (capab & 0xff00) ? 2 : 1;
if (!capab)
return 0; /* no supported extended RSN capabilities */
if (len < 2 + flen)
return -1;
capab |= flen - 1; /* bit 0-3 = Field length (n - 1) */
*pos++ = WLAN_EID_RSNX;
*pos++ = flen;
*pos++ = capab & 0x00ff;
capab >>= 8;
if (capab)
*pos++ = capab;
return pos - buf;
}
int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth)
{
u8 *pos, buf[128];
@@ -311,6 +342,11 @@ int wpa_auth_gen_wpa_ie(struct wpa_authenticator *wpa_auth)
if (res < 0)
return res;
pos += res;
res = wpa_write_rsnxe(&wpa_auth->conf, pos,
buf + sizeof(buf) - pos);
if (res < 0)
return res;
pos += res;
}
#ifdef CONFIG_IEEE80211R_AP
if (wpa_key_mgmt_ft(wpa_auth->conf.wpa_key_mgmt)) {
@@ -355,14 +391,18 @@ u8 * wpa_add_kde(u8 *pos, u32 kde, const u8 *data, size_t data_len,
return pos;
}
int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
enum wpa_validate_result
wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm,
const u8 *wpa_ie, size_t wpa_ie_len/*,
const u8 *wpa_ie, size_t wpa_ie_len,
const u8 *rsnxe, size_t rsnxe_len/*,
const u8 *mdie, size_t mdie_len*/)
{
struct wpa_ie_data data = {0};
int ciphers, key_mgmt, res, version;
u32 selector;
size_t i;
const u8 *pmkid = NULL;
if (wpa_auth == NULL || sm == NULL)
return WPA_NOT_ENABLED;
@@ -569,6 +609,29 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
else
sm->wpa = WPA_VERSION_WPA;
sm->pmksa = NULL;
for (i = 0; i < data.num_pmkid; i++) {
wpa_hexdump(MSG_DEBUG, "RSN IE: STA PMKID",
&data.pmkid[i * PMKID_LEN], PMKID_LEN);
sm->pmksa = pmksa_cache_auth_get(wpa_auth->pmksa, sm->addr,
&data.pmkid[i * PMKID_LEN]);
if (sm->pmksa) {
pmkid = sm->pmksa->pmkid;
break;
}
}
if (sm->pmksa && pmkid) {
wpa_printf(MSG_DEBUG, "PMKID found from PMKSA cache");
os_memcpy(wpa_auth->dot11RSNAPMKIDUsed, pmkid, PMKID_LEN);
}
#ifdef CONFIG_SAE
if (sm->wpa_key_mgmt == WPA_KEY_MGMT_SAE && data.num_pmkid &&
!sm->pmksa) {
wpa_printf(MSG_DEBUG, "No PMKSA cache entry found for SAE");
return WPA_INVALID_PMKID;
}
#endif /* CONFIG_SAE */
if (sm->wpa_ie == NULL || sm->wpa_ie_len < wpa_ie_len) {
os_free(sm->wpa_ie);
sm->wpa_ie = os_malloc(wpa_ie_len);
@@ -577,6 +640,20 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
}
memcpy(sm->wpa_ie, wpa_ie, wpa_ie_len);
sm->wpa_ie_len = wpa_ie_len;
if (rsnxe && rsnxe_len) {
if (!sm->rsnxe || sm->rsnxe_len < rsnxe_len) {
os_free(sm->rsnxe);
sm->rsnxe = os_malloc(rsnxe_len);
if (!sm->rsnxe)
return WPA_ALLOC_FAIL;
}
os_memcpy(sm->rsnxe, rsnxe, rsnxe_len);
sm->rsnxe_len = rsnxe_len;
} else {
os_free(sm->rsnxe);
sm->rsnxe = NULL;
sm->rsnxe_len = 0;
}
return WPA_IE_OK;
}
@@ -676,6 +753,11 @@ int wpa_parse_kde_ies(const u8 *buf, size_t len, struct wpa_eapol_ie_parse *ie)
ie->ftie = pos;
ie->ftie_len = pos[1] + 2;
#endif /* CONFIG_IEEE80211R_AP */
} else if (*pos == WLAN_EID_RSNX) {
ie->rsnxe = pos;
ie->rsnxe_len = pos[1] + 2;
wpa_hexdump(MSG_DEBUG, "WPA: RSNXE in EAPOL-Key",
ie->rsnxe, ie->rsnxe_len);
} else if (*pos == WLAN_EID_VENDOR_SPECIFIC) {
ret = wpa_parse_generic(pos, end, ie);
if (ret < 0)

View File

@@ -29,6 +29,8 @@ struct wpa_eapol_ie_parse {
const u8 *ftie;
size_t ftie_len;
#endif /* CONFIG_IEEE80211R */
const u8 *rsnxe;
size_t rsnxe_len;
};
int wpa_parse_kde_ies(const u8 *buf, size_t len,