mirror of
https://github.com/espressif/esp-idf.git
synced 2025-08-31 14:22:14 +00:00
esp_wifi: WPA3-SAE support for softAP
This commit is contained in:

committed by
Jiang Jiang Jian

parent
ad19981af8
commit
2b8e40e760
@@ -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);
|
||||
}
|
||||
|
@@ -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 */
|
||||
|
142
components/wpa_supplicant/src/ap/comeback_token.c
Normal file
142
components/wpa_supplicant/src/ap/comeback_token.c
Normal 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 */
|
21
components/wpa_supplicant/src/ap/comeback_token.h
Normal file
21
components/wpa_supplicant/src/ap/comeback_token.h
Normal 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 */
|
@@ -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 */
|
||||
|
702
components/wpa_supplicant/src/ap/ieee802_11.c
Normal file
702
components/wpa_supplicant/src/ap/ieee802_11.c
Normal 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;
|
||||
}
|
20
components/wpa_supplicant/src/ap/ieee802_11.h
Normal file
20
components/wpa_supplicant/src/ap/ieee802_11.h
Normal 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 */
|
441
components/wpa_supplicant/src/ap/pmksa_cache_auth.c
Normal file
441
components/wpa_supplicant/src/ap/pmksa_cache_auth.c
Normal 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;
|
||||
}
|
77
components/wpa_supplicant/src/ap/pmksa_cache_auth.h
Normal file
77
components/wpa_supplicant/src/ap/pmksa_cache_auth.h
Normal 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 */
|
@@ -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;
|
||||
}
|
||||
|
@@ -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 */
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
@@ -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);
|
||||
}
|
||||
|
@@ -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 */
|
||||
|
@@ -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,
|
||||
|
@@ -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)
|
||||
|
@@ -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,
|
||||
|
Reference in New Issue
Block a user