SAE-PK: AP functionality

This adds AP side functionality for SAE-PK. The new sae_password
configuration parameters can now be used to enable SAE-PK mode whenever
SAE is enabled.

Signed-off-by: Jouni Malinen <jouni@codeaurora.org>
This commit is contained in:
Jouni Malinen 2020-05-30 23:30:42 +03:00 committed by Jouni Malinen
parent 00e4fbdcc5
commit 20ccf97b3d
9 changed files with 228 additions and 25 deletions

View file

@ -14,6 +14,7 @@
#include "utils/common.h"
#include "utils/uuid.h"
#include "common/ieee802_11_defs.h"
#include "common/sae.h"
#include "crypto/sha256.h"
#include "crypto/tls.h"
#include "drivers/driver.h"
@ -2290,6 +2291,35 @@ static int parse_sae_password(struct hostapd_bss_config *bss, const char *val)
pw->vlan_id = atoi(pos2);
}
#ifdef CONFIG_SAE_PK
pos2 = os_strstr(pos, "|pk=");
if (pos2) {
const char *epos;
char *tmp;
if (!end)
end = pos2;
pos2 += 4;
epos = os_strchr(pos2, '|');
if (epos) {
tmp = os_malloc(epos - pos2 + 1);
if (!tmp)
goto fail;
os_memcpy(tmp, pos2, epos - pos2);
tmp[epos - pos2] = '\0';
} else {
tmp = os_strdup(pos2);
if (!tmp)
goto fail;
}
pw->pk = sae_parse_pk(tmp);
str_clear_free(tmp);
if (!pw->pk)
goto fail;
}
#endif /* CONFIG_SAE_PK */
pos2 = os_strstr(pos, "|id=");
if (pos2) {
if (!end)
@ -2312,6 +2342,14 @@ static int parse_sae_password(struct hostapd_bss_config *bss, const char *val)
pw->password[end - val] = '\0';
}
#ifdef CONFIG_SAE_PK
if (pw->pk && !sae_pk_valid_password(pw->password)) {
wpa_printf(MSG_INFO,
"Invalid SAE password for a SAE-PK sae_password entry");
goto fail;
}
#endif /* CONFIG_SAE_PK */
pw->next = bss->sae_passwords;
bss->sae_passwords = pw;
@ -2319,6 +2357,9 @@ static int parse_sae_password(struct hostapd_bss_config *bss, const char *val)
fail:
str_clear_free(pw->password);
os_free(pw->identifier);
#ifdef CONFIG_SAE_PK
sae_deinit_pk(pw->pk);
#endif /* CONFIG_SAE_PK */
os_free(pw);
return -1;
}

View file

@ -1776,7 +1776,8 @@ own_ip_addr=127.0.0.1
# special meaning of removing all previously added entries.
#
# sae_password uses the following encoding:
#<password/credential>[|mac=<peer mac>][|vlanid=<VLAN ID>][|id=<identifier>]
#<password/credential>[|mac=<peer mac>][|vlanid=<VLAN ID>]
#[|pk=<m:ECPrivateKey-base64>][|id=<identifier>]
# Examples:
#sae_password=secret
#sae_password=really secret|mac=ff:ff:ff:ff:ff:ff

View file

@ -461,7 +461,8 @@ int hostapd_setup_sae_pt(struct hostapd_bss_config *conf)
struct hostapd_ssid *ssid = &conf->ssid;
struct sae_password_entry *pw;
if ((conf->sae_pwe == 0 && !hostapd_sae_pw_id_in_use(conf)) ||
if ((conf->sae_pwe == 0 && !hostapd_sae_pw_id_in_use(conf) &&
!hostapd_sae_pk_in_use(conf)) ||
conf->sae_pwe == 3 ||
!wpa_key_mgmt_sae(conf->wpa_key_mgmt))
return 0; /* PT not needed */
@ -711,6 +712,9 @@ static void hostapd_config_free_sae_passwords(struct hostapd_bss_config *conf)
#ifdef CONFIG_SAE
sae_deinit_pt(tmp->pt);
#endif /* CONFIG_SAE */
#ifdef CONFIG_SAE_PK
sae_deinit_pk(tmp->pk);
#endif /* CONFIG_SAE_PK */
os_free(tmp);
}
}
@ -1111,6 +1115,25 @@ const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
}
#ifdef CONFIG_SAE_PK
static bool hostapd_sae_pk_password_without_pk(struct hostapd_bss_config *bss)
{
struct sae_password_entry *pw;
if (bss->ssid.wpa_passphrase &&
sae_pk_valid_password(bss->ssid.wpa_passphrase))
return true;
for (pw = bss->sae_passwords; pw; pw = pw->next) {
if (!pw->pk && sae_pk_valid_password(pw->password))
return true;
}
return false;
}
#endif /* CONFIG_SAE_PK */
static int hostapd_config_check_bss(struct hostapd_bss_config *bss,
struct hostapd_config *conf,
int full_config)
@ -1294,6 +1317,15 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss,
}
#endif /* CONFIG_OCV */
#ifdef CONFIG_SAE_PK
if (full_config && hostapd_sae_pk_in_use(bss) &&
hostapd_sae_pk_password_without_pk(bss)) {
wpa_printf(MSG_ERROR,
"SAE-PK: SAE password uses SAE-PK style, but does not have PK configured");
return -1;
}
#endif /* CONFIG_SAE_PK */
return 0;
}
@ -1473,3 +1505,38 @@ int hostapd_sae_pw_id_in_use(struct hostapd_bss_config *conf)
return 2;
return with_id;
}
bool hostapd_sae_pk_in_use(struct hostapd_bss_config *conf)
{
#ifdef CONFIG_SAE_PK
struct sae_password_entry *pw;
for (pw = conf->sae_passwords; pw; pw = pw->next) {
if (pw->pk)
return true;
}
#endif /* CONFIG_SAE_PK */
return false;
}
#ifdef CONFIG_SAE_PK
bool hostapd_sae_pk_exclusively(struct hostapd_bss_config *conf)
{
bool with_pk = false;
struct sae_password_entry *pw;
if (conf->ssid.wpa_passphrase)
return false;
for (pw = conf->sae_passwords; pw; pw = pw->next) {
if (!pw->pk)
return false;
with_pk = true;
}
return with_pk;
}
#endif /* CONFIG_SAE_PK */

View file

@ -261,6 +261,7 @@ struct sae_password_entry {
u8 peer_addr[ETH_ALEN];
int vlan_id;
struct sae_pt *pt;
struct sae_pk *pk;
};
struct dpp_controller_conf {
@ -1147,6 +1148,8 @@ int hostapd_config_check(struct hostapd_config *conf, int full_config);
void hostapd_set_security_params(struct hostapd_bss_config *bss,
int full_config);
int hostapd_sae_pw_id_in_use(struct hostapd_bss_config *conf);
bool hostapd_sae_pk_in_use(struct hostapd_bss_config *conf);
bool hostapd_sae_pk_exclusively(struct hostapd_bss_config *conf);
int hostapd_setup_sae_pt(struct hostapd_bss_config *conf);
#endif /* HOSTAPD_CONFIG_H */

View file

@ -386,7 +386,8 @@ static int send_auth_reply(struct hostapd_data *hapd, struct sta_info *sta,
auth_alg == WLAN_AUTH_SAE) {
if (auth_transaction == 1 && sta &&
(resp == WLAN_STATUS_SUCCESS ||
resp == WLAN_STATUS_SAE_HASH_TO_ELEMENT)) {
resp == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
resp == WLAN_STATUS_SAE_PK)) {
wpa_printf(MSG_DEBUG,
"TESTING: Postpone SAE Commit transmission until Confirm is ready");
os_free(sta->sae_postponed_commit);
@ -478,17 +479,23 @@ static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
const char *rx_id = NULL;
int use_pt = 0;
struct sae_pt *pt = NULL;
const struct sae_pk *pk = NULL;
if (sta->sae->tmp) {
rx_id = sta->sae->tmp->pw_id;
use_pt = sta->sae->tmp->h2e;
#ifdef CONFIG_SAE_PK
os_memcpy(sta->sae->tmp->own_addr, hapd->own_addr, ETH_ALEN);
os_memcpy(sta->sae->tmp->peer_addr, sta->addr, ETH_ALEN);
#endif /* CONFIG_SAE_PK */
}
if (rx_id && hapd->conf->sae_pwe != 3)
use_pt = 1;
else if (status_code == WLAN_STATUS_SUCCESS)
use_pt = 0;
else if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT)
else if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
status_code == WLAN_STATUS_SAE_PK)
use_pt = 1;
for (pw = hapd->conf->sae_passwords; pw; pw = pw->next) {
@ -502,6 +509,8 @@ static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
continue;
password = pw->password;
pt = pw->pt;
if (!(hapd->conf->mesh & MESH_ENABLED))
pk = pw->pk;
break;
}
if (!password) {
@ -515,7 +524,7 @@ static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
if (update && use_pt &&
sae_prepare_commit_pt(sta->sae, pt, hapd->own_addr, sta->addr,
NULL, NULL) < 0)
NULL, pk) < 0)
return NULL;
if (update && !use_pt &&
@ -558,7 +567,10 @@ static struct wpabuf * auth_build_sae_confirm(struct hostapd_data *hapd,
if (buf == NULL)
return NULL;
sae_write_confirm(sta->sae, buf);
if (sae_write_confirm(sta->sae, buf) < 0) {
wpabuf_free(buf);
return NULL;
}
return buf;
}
@ -575,11 +587,19 @@ static int auth_sae_send_commit(struct hostapd_data *hapd,
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;
#ifdef CONFIG_SAE_PK
if (!data && sta->sae->tmp && sta->sae->tmp->reject_group)
return WLAN_STATUS_FINITE_CYCLIC_GROUP_NOT_SUPPORTED;
#endif /* CONFIG_SAE_PK */
if (data == NULL)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
status = (sta->sae->tmp && sta->sae->tmp->h2e) ?
WLAN_STATUS_SAE_HASH_TO_ELEMENT : WLAN_STATUS_SUCCESS;
if (sta->sae->tmp && sta->sae->tmp->pk)
status = WLAN_STATUS_SAE_PK;
else if (sta->sae->tmp && sta->sae->tmp->h2e)
status = WLAN_STATUS_SAE_HASH_TO_ELEMENT;
else
status = WLAN_STATUS_SUCCESS;
reply_res = send_auth_reply(hapd, sta, sta->addr, bssid,
WLAN_AUTH_SAE, 1,
status, wpabuf_head(data),
@ -900,9 +920,14 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
switch (sta->sae->state) {
case SAE_NOTHING:
if (auth_transaction == 1) {
if (sta->sae->tmp)
sta->sae->tmp->h2e = status_code ==
WLAN_STATUS_SAE_HASH_TO_ELEMENT;
if (sta->sae->tmp) {
sta->sae->tmp->h2e =
(status_code ==
WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
status_code == WLAN_STATUS_SAE_PK);
sta->sae->tmp->pk =
status_code == WLAN_STATUS_SAE_PK;
}
ret = auth_sae_send_commit(hapd, sta, bssid,
!allow_reuse, status_code);
if (ret)
@ -1118,14 +1143,20 @@ static int sae_status_success(struct hostapd_data *hapd, u16 status_code)
sae_pwe = 1;
else if (id_in_use == 1 && sae_pwe == 0)
sae_pwe = 2;
#ifdef CONFIG_SAE_PK
if (sae_pwe == 0 && hostapd_sae_pk_in_use(hapd->conf))
sae_pwe = 2;
#endif /* CONFIG_SAE_PK */
return ((sae_pwe == 0 || sae_pwe == 3) &&
status_code == WLAN_STATUS_SUCCESS) ||
(sae_pwe == 1 &&
status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT) ||
(status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
status_code == WLAN_STATUS_SAE_PK)) ||
(sae_pwe == 2 &&
(status_code == WLAN_STATUS_SUCCESS ||
status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT));
status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
status_code == WLAN_STATUS_SAE_PK));
}
@ -1148,11 +1179,15 @@ static int sae_is_group_enabled(struct hostapd_data *hapd, int group)
static int check_sae_rejected_groups(struct hostapd_data *hapd,
const struct wpabuf *groups)
struct sae_data *sae, bool pk)
{
const struct wpabuf *groups;
size_t i, count;
const u8 *pos;
if (!sae->tmp)
return 0;
groups = sae->tmp->peer_rejected_groups;
if (!groups)
return 0;
@ -1165,8 +1200,29 @@ static int check_sae_rejected_groups(struct hostapd_data *hapd,
group = WPA_GET_LE16(pos);
pos += 2;
enabled = sae_is_group_enabled(hapd, group);
wpa_printf(MSG_DEBUG, "SAE: Rejected group %u is %s",
group, enabled ? "enabled" : "disabled");
#ifdef CONFIG_SAE_PK
/* TODO: Could check more explicitly against the matching
* sae_password entry only for the somewhat theoretical case of
* different passwords using different groups for SAE-PK K_AP
* values. */
if (pk) {
struct sae_password_entry *pw;
enabled = false;
for (pw = hapd->conf->sae_passwords; pw;
pw = pw->next) {
if (pw->pk && pw->pk->group == group) {
enabled = true;
break;
}
}
}
#endif /* CONFIG_SAE_PK */
wpa_printf(MSG_DEBUG, "SAE: Rejected group %u is %s%s",
group, enabled ? "enabled" : "disabled",
pk ? " (PK)" : "");
if (enabled)
return 1;
}
@ -1339,7 +1395,8 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
((const u8 *) mgmt) + len -
mgmt->u.auth.variable, &token,
&token_len, groups, status_code ==
WLAN_STATUS_SAE_HASH_TO_ELEMENT);
WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
status_code == WLAN_STATUS_SAE_PK);
if (resp == SAE_SILENTLY_DISCARD) {
wpa_printf(MSG_DEBUG,
"SAE: Drop commit message from " MACSTR " due to reflection attack",
@ -1369,9 +1426,9 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
if (resp != WLAN_STATUS_SUCCESS)
goto reply;
if (sta->sae->tmp &&
check_sae_rejected_groups(
hapd, sta->sae->tmp->peer_rejected_groups)) {
if (check_sae_rejected_groups(hapd, sta->sae,
status_code ==
WLAN_STATUS_SAE_PK)) {
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto reply;
}
@ -1384,7 +1441,8 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
MACSTR, MAC2STR(sta->addr));
if (sta->sae->tmp)
h2e = sta->sae->tmp->h2e;
if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT)
if (status_code == WLAN_STATUS_SAE_HASH_TO_ELEMENT ||
status_code == WLAN_STATUS_SAE_PK)
h2e = 1;
data = auth_build_token_req(hapd, sta->sae->group,
sta->addr, h2e);

View file

@ -427,6 +427,14 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx)
if (hapd->conf->beacon_prot)
*pos |= 0x10; /* Bit 84 - Beacon Protection Enabled */
break;
case 11: /* Bits 88-95 */
#ifdef CONFIG_SAE_PK
if (hapd->conf->wpa &&
wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
hostapd_sae_pk_exclusively(hapd->conf))
*pos |= 0x01; /* Bit 88 - SAE PK Exclusively */
#endif /* CONFIG_SAE_PK */
break;
}
}
@ -487,6 +495,12 @@ u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid)
#endif /* CONFIG_SAE */
if (len < 11 && hapd->conf->beacon_prot)
len = 11;
#ifdef CONFIG_SAE_PK
if (len < 12 && hapd->conf->wpa &&
wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
hostapd_sae_pk_exclusively(hapd->conf))
len = 12;
#endif /* CONFIG_SAE_PK */
if (len < hapd->iface->extended_capa_len)
len = hapd->iface->extended_capa_len;
if (len == 0)
@ -1081,11 +1095,16 @@ int get_tx_parameters(struct sta_info *sta, int ap_max_chanwidth,
u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len)
{
u8 *pos = eid;
bool sae_pk = false;
#ifdef CONFIG_SAE_PK
sae_pk = hostapd_sae_pk_in_use(hapd->conf);
#endif /* CONFIG_SAE_PK */
if (!(hapd->conf->wpa & WPA_PROTO_RSN) ||
!wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) ||
(hapd->conf->sae_pwe != 1 && hapd->conf->sae_pwe != 2 &&
!hostapd_sae_pw_id_in_use(hapd->conf)) ||
!hostapd_sae_pw_id_in_use(hapd->conf) && !sae_pk) ||
hapd->conf->sae_pwe == 3 ||
len < 3)
return pos;
@ -1094,7 +1113,12 @@ u8 * hostapd_eid_rsnxe(struct hostapd_data *hapd, u8 *eid, size_t len)
*pos++ = 1;
/* bits 0-3 = 0 since only one octet of Extended RSN Capabilities is
* used for now */
*pos++ = BIT(WLAN_RSNX_CAPAB_SAE_H2E);
*pos = BIT(WLAN_RSNX_CAPAB_SAE_H2E);
#ifdef CONFIG_SAE_PK
if (sae_pk)
*pos |= BIT(WLAN_RSNX_CAPAB_SAE_PK);
#endif /* CONFIG_SAE_PK */
pos++;
return pos;
}

View file

@ -256,6 +256,7 @@ struct wpa_auth_config {
u8 fils_cache_id[FILS_CACHE_ID_LEN];
#endif /* CONFIG_FILS */
int sae_pwe;
bool sae_pk;
int owe_ptk_workaround;
u8 transition_disable;
#ifdef CONFIG_DPP2

View file

@ -198,6 +198,9 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
wconf->sae_pwe = 1;
else if (sae_pw_id == 1 && wconf->sae_pwe == 0)
wconf->sae_pwe = 2;
#ifdef CONFIG_SAE_PK
wconf->sae_pk = hostapd_sae_pk_in_use(conf);
#endif /* CONFIG_SAE_PK */
#ifdef CONFIG_OWE
wconf->owe_ptk_workaround = conf->owe_ptk_workaround;
#endif /* CONFIG_OWE */

View file

@ -378,7 +378,7 @@ int wpa_write_rsnxe(struct wpa_auth_config *conf, u8 *buf, size_t len)
{
u8 *pos = buf;
if (conf->sae_pwe != 1 && conf->sae_pwe != 2)
if (conf->sae_pwe != 1 && conf->sae_pwe != 2 && !conf->sae_pk)
return 0; /* no supported extended RSN capabilities */
if (len < 3)
@ -388,7 +388,12 @@ int wpa_write_rsnxe(struct wpa_auth_config *conf, u8 *buf, size_t len)
*pos++ = 1;
/* bits 0-3 = 0 since only one octet of Extended RSN Capabilities is
* used for now */
*pos++ = BIT(WLAN_RSNX_CAPAB_SAE_H2E);
*pos = BIT(WLAN_RSNX_CAPAB_SAE_H2E);
#ifdef CONFIG_SAE_PK
if (conf->sae_pk)
*pos |= BIT(WLAN_RSNX_CAPAB_SAE_PK);
#endif /* CONFIG_SAE_PK */
pos++;
return pos - buf;
}