summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorCy Schubert <cy@FreeBSD.org>2019-04-22 15:42:53 +0000
committerCy Schubert <cy@FreeBSD.org>2019-04-22 15:42:53 +0000
commit6e6d0eb51ef7b7487340bae7f20097ee5a57dbf4 (patch)
tree406310b03a08c8e00c863a82934ba6a8e33c6b2f /src
parent8a36c5c2ca4d1f8a900ca3d9ffde40b96463def7 (diff)
Notes
Diffstat (limited to 'src')
-rw-r--r--src/ap/acs.c92
-rw-r--r--src/ap/ap_config.c124
-rw-r--r--src/ap/ap_config.h31
-rw-r--r--src/ap/ap_drv_ops.h18
-rw-r--r--src/ap/authsrv.c16
-rw-r--r--src/ap/beacon.c22
-rw-r--r--src/ap/ctrl_iface_ap.c14
-rw-r--r--src/ap/dfs.c15
-rw-r--r--src/ap/dhcp_snoop.c18
-rw-r--r--src/ap/dpp_hostapd.c756
-rw-r--r--src/ap/dpp_hostapd.h6
-rw-r--r--src/ap/drv_callbacks.c72
-rw-r--r--src/ap/eap_user_db.c12
-rw-r--r--src/ap/fils_hlp.c13
-rw-r--r--src/ap/hostapd.c174
-rw-r--r--src/ap/hostapd.h14
-rw-r--r--src/ap/hs20.c17
-rw-r--r--src/ap/hw_features.c44
-rw-r--r--src/ap/ieee802_11.c663
-rw-r--r--src/ap/ieee802_11.h15
-rw-r--r--src/ap/ieee802_11_auth.c14
-rw-r--r--src/ap/ieee802_11_he.c31
-rw-r--r--src/ap/ieee802_11_shared.c302
-rw-r--r--src/ap/ieee802_11_vht.c23
-rw-r--r--src/ap/ieee802_1x.c107
-rw-r--r--src/ap/neighbor_db.c123
-rw-r--r--src/ap/neighbor_db.h3
-rw-r--r--src/ap/rrm.c2
-rw-r--r--src/ap/sta_info.c65
-rw-r--r--src/ap/sta_info.h10
-rw-r--r--src/ap/vlan_full.c87
-rw-r--r--src/ap/vlan_init.c10
-rw-r--r--src/ap/wnm_ap.c90
-rw-r--r--src/ap/wpa_auth.c401
-rw-r--r--src/ap/wpa_auth.h25
-rw-r--r--src/ap/wpa_auth_ft.c155
-rw-r--r--src/ap/wpa_auth_glue.c94
-rw-r--r--src/ap/wpa_auth_i.h13
-rw-r--r--src/ap/wpa_auth_ie.c87
-rw-r--r--src/ap/wpa_auth_ie.h4
-rw-r--r--src/ap/wps_hostapd.c68
-rw-r--r--src/common/common_module_tests.c178
-rw-r--r--src/common/defs.h32
-rw-r--r--src/common/dpp.c1096
-rw-r--r--src/common/dpp.h72
-rw-r--r--src/common/hw_features_common.c91
-rw-r--r--src/common/hw_features_common.h5
-rw-r--r--src/common/ieee802_11_common.c247
-rw-r--r--src/common/ieee802_11_common.h70
-rw-r--r--src/common/ieee802_11_defs.h192
-rw-r--r--src/common/linux_bridge.h15
-rw-r--r--src/common/ocv.c172
-rw-r--r--src/common/ocv.h40
-rw-r--r--src/common/qca-vendor.h471
-rw-r--r--src/common/sae.c347
-rw-r--r--src/common/sae.h2
-rw-r--r--src/common/version.h2
-rw-r--r--src/common/wpa_common.c57
-rw-r--r--src/common/wpa_common.h15
-rw-r--r--src/common/wpa_ctrl.c23
-rw-r--r--src/crypto/Makefile2
-rw-r--r--src/crypto/aes-internal-enc.c4
-rw-r--r--src/crypto/crypto.h9
-rw-r--r--src/crypto/crypto_gnutls.c43
-rw-r--r--src/crypto/crypto_internal-modexp.c41
-rw-r--r--src/crypto/crypto_internal.c3
-rw-r--r--src/crypto/crypto_libtomcrypt.c5
-rw-r--r--src/crypto/crypto_linux.c3
-rw-r--r--src/crypto/crypto_nettle.c36
-rw-r--r--src/crypto/crypto_openssl.c122
-rw-r--r--src/crypto/crypto_wolfssl.c15
-rw-r--r--src/crypto/dh_groups.c1
-rw-r--r--src/crypto/md4-internal.c2
-rw-r--r--src/crypto/random.c72
-rw-r--r--src/crypto/sha1-tlsprf.c3
-rw-r--r--src/crypto/sha512-internal.c8
-rw-r--r--src/crypto/sha512.c104
-rw-r--r--src/crypto/tls.h35
-rw-r--r--src/crypto/tls_gnutls.c75
-rw-r--r--src/crypto/tls_internal.c46
-rw-r--r--src/crypto/tls_none.c5
-rw-r--r--src/crypto/tls_openssl.c524
-rw-r--r--src/crypto/tls_wolfssl.c46
-rw-r--r--src/drivers/driver.h121
-rw-r--r--src/drivers/driver_atheros.c3
-rw-r--r--src/drivers/driver_bsd.c2
-rw-r--r--src/drivers/driver_common.c22
-rw-r--r--src/drivers/driver_hostap.c12
-rw-r--r--src/drivers/driver_macsec_linux.c163
-rw-r--r--src/drivers/driver_nl80211.c282
-rw-r--r--src/drivers/driver_nl80211.h1
-rw-r--r--src/drivers/driver_nl80211_capa.c226
-rw-r--r--src/drivers/driver_nl80211_event.c102
-rw-r--r--src/drivers/driver_nl80211_scan.c20
-rw-r--r--src/drivers/driver_openbsd.c3
-rw-r--r--src/drivers/driver_roboswitch.c36
-rw-r--r--src/drivers/driver_wext.c13
-rw-r--r--src/drivers/drivers.mak72
-rw-r--r--src/drivers/drivers.mk42
-rw-r--r--src/drivers/linux_ioctl.c21
-rw-r--r--src/drivers/nl80211_copy.h602
-rw-r--r--src/eap_common/eap_eke_common.c2
-rw-r--r--src/eap_common/eap_pwd_common.c302
-rw-r--r--src/eap_common/eap_pwd_common.h6
-rw-r--r--src/eap_common/eap_sake_common.c75
-rw-r--r--src/eap_common/eap_sake_common.h8
-rw-r--r--src/eap_peer/eap_config.h99
-rw-r--r--src/eap_peer/eap_fast.c28
-rw-r--r--src/eap_peer/eap_mschapv2.c10
-rw-r--r--src/eap_peer/eap_peap.c91
-rw-r--r--src/eap_peer/eap_pwd.c93
-rw-r--r--src/eap_peer/eap_sake.c12
-rw-r--r--src/eap_peer/eap_tls.c1
-rw-r--r--src/eap_peer/eap_tls_common.c87
-rw-r--r--src/eap_peer/eap_tls_common.h3
-rw-r--r--src/eap_peer/eap_ttls.c48
-rw-r--r--src/eap_peer/eap_wsc.c3
-rw-r--r--src/eap_server/eap.h3
-rw-r--r--src/eap_server/eap_i.h1
-rw-r--r--src/eap_server/eap_server.c31
-rw-r--r--src/eap_server/eap_server_aka.c4
-rw-r--r--src/eap_server/eap_server_gpsk.c4
-rw-r--r--src/eap_server/eap_server_mschapv2.c10
-rw-r--r--src/eap_server/eap_server_pax.c65
-rw-r--r--src/eap_server/eap_server_peap.c58
-rw-r--r--src/eap_server/eap_server_pwd.c119
-rw-r--r--src/eap_server/eap_server_sake.c44
-rw-r--r--src/eap_server/eap_server_sim.c3
-rw-r--r--src/eap_server/eap_server_tls.c21
-rw-r--r--src/eap_server/eap_server_tls_common.c31
-rw-r--r--src/eap_server/eap_server_ttls.c6
-rw-r--r--src/eap_server/eap_tls_common.h3
-rw-r--r--src/eapol_supp/eapol_supp_sm.c13
-rw-r--r--src/fst/fst.h16
-rw-r--r--src/lib.rules5
-rw-r--r--src/p2p/p2p.c34
-rw-r--r--src/p2p/p2p.h15
-rw-r--r--src/p2p/p2p_build.c2
-rw-r--r--src/p2p/p2p_group.c3
-rw-r--r--src/p2p/p2p_i.h4
-rw-r--r--src/p2p/p2p_invitation.c2
-rw-r--r--src/p2p/p2p_utils.c23
-rw-r--r--src/pae/ieee802_1x_cp.c36
-rw-r--r--src/pae/ieee802_1x_cp.h1
-rw-r--r--src/pae/ieee802_1x_kay.c964
-rw-r--r--src/pae/ieee802_1x_kay.h8
-rw-r--r--src/pae/ieee802_1x_kay_i.h52
-rw-r--r--src/pae/ieee802_1x_key.c89
-rw-r--r--src/pae/ieee802_1x_key.h26
-rw-r--r--src/pae/ieee802_1x_secy_ops.c25
-rw-r--r--src/pae/ieee802_1x_secy_ops.h2
-rw-r--r--src/radius/radius_client.c86
-rw-r--r--src/radius/radius_server.c255
-rw-r--r--src/radius/radius_server.h1
-rw-r--r--src/rsn_supp/pmksa_cache.c3
-rw-r--r--src/rsn_supp/tdls.c2
-rw-r--r--src/rsn_supp/wpa.c283
-rw-r--r--src/rsn_supp/wpa.h12
-rw-r--r--src/rsn_supp/wpa_ft.c43
-rw-r--r--src/rsn_supp/wpa_i.h18
-rw-r--r--src/rsn_supp/wpa_ie.c13
-rw-r--r--src/rsn_supp/wpa_ie.h4
-rw-r--r--src/tls/asn1.c8
-rw-r--r--src/tls/bignum.c4
-rw-r--r--src/tls/tlsv1_client.c34
-rw-r--r--src/tls/tlsv1_client.h3
-rw-r--r--src/tls/tlsv1_client_read.c2
-rw-r--r--src/tls/tlsv1_client_write.c3
-rw-r--r--src/tls/tlsv1_server.c57
-rw-r--r--src/tls/tlsv1_server.h7
-rw-r--r--src/tls/tlsv1_server_i.h2
-rw-r--r--src/tls/tlsv1_server_read.c51
-rw-r--r--src/tls/tlsv1_server_write.c5
-rw-r--r--src/tls/x509v3.c2
-rw-r--r--src/utils/Makefile1
-rw-r--r--src/utils/base64.c5
-rw-r--r--src/utils/browser.c3
-rw-r--r--src/utils/common.c36
-rw-r--r--src/utils/common.h1
-rw-r--r--src/utils/const_time.h191
-rw-r--r--src/utils/eloop.c29
-rw-r--r--src/utils/http_curl.c35
-rw-r--r--src/utils/json.c7
-rw-r--r--src/utils/list.h6
-rw-r--r--src/utils/os_internal.c16
-rw-r--r--src/utils/os_none.c6
-rw-r--r--src/utils/os_unix.c12
-rw-r--r--src/utils/utils_module_tests.c290
-rw-r--r--src/utils/wpa_debug.c6
-rw-r--r--src/wps/wps.c8
-rw-r--r--src/wps/wps.h38
-rw-r--r--src/wps/wps_attr_build.c14
-rw-r--r--src/wps/wps_attr_parse.c11
-rw-r--r--src/wps/wps_attr_parse.h1
-rw-r--r--src/wps/wps_common.c16
-rw-r--r--src/wps/wps_defs.h3
-rw-r--r--src/wps/wps_dev_attr.c8
-rw-r--r--src/wps/wps_dev_attr.h1
-rw-r--r--src/wps/wps_enrollee.c14
-rw-r--r--src/wps/wps_er.c4
-rw-r--r--src/wps/wps_i.h5
-rw-r--r--src/wps/wps_registrar.c88
-rw-r--r--src/wps/wps_upnp.c2
-rw-r--r--src/wps/wps_validate.c2
204 files changed, 11489 insertions, 2783 deletions
diff --git a/src/ap/acs.c b/src/ap/acs.c
index 6d4c0416dd425..3b4507575328a 100644
--- a/src/ap/acs.c
+++ b/src/ap/acs.c
@@ -13,6 +13,7 @@
#include "utils/common.h"
#include "utils/list.h"
#include "common/ieee802_11_defs.h"
+#include "common/hw_features_common.h"
#include "common/wpa_ctrl.h"
#include "drivers/driver.h"
#include "hostapd.h"
@@ -362,7 +363,7 @@ acs_survey_chan_interference_factor(struct hostapd_iface *iface,
}
-static int acs_usable_ht40_chan(struct hostapd_channel_data *chan)
+static int acs_usable_ht40_chan(const struct hostapd_channel_data *chan)
{
const int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 149,
157, 184, 192 };
@@ -376,7 +377,7 @@ static int acs_usable_ht40_chan(struct hostapd_channel_data *chan)
}
-static int acs_usable_vht80_chan(struct hostapd_channel_data *chan)
+static int acs_usable_vht80_chan(const struct hostapd_channel_data *chan)
{
const int allowed[] = { 36, 52, 100, 116, 132, 149 };
unsigned int i;
@@ -389,6 +390,19 @@ static int acs_usable_vht80_chan(struct hostapd_channel_data *chan)
}
+static int acs_usable_vht160_chan(const struct hostapd_channel_data *chan)
+{
+ const int allowed[] = { 36, 100 };
+ unsigned int i;
+
+ for (i = 0; i < ARRAY_SIZE(allowed); i++)
+ if (chan->chan == allowed[i])
+ return 1;
+
+ return 0;
+}
+
+
static int acs_survey_is_sufficient(struct freq_survey *survey)
{
if (!(survey->filled & SURVEY_HAS_NF)) {
@@ -565,6 +579,7 @@ acs_find_ideal_chan(struct hostapd_iface *iface)
long double factor, ideal_factor = 0;
int i, j;
int n_chans = 1;
+ u32 bw;
unsigned int k;
/* TODO: HT40- support */
@@ -579,16 +594,23 @@ acs_find_ideal_chan(struct hostapd_iface *iface)
iface->conf->secondary_channel)
n_chans = 2;
- if (iface->conf->ieee80211ac &&
- iface->conf->vht_oper_chwidth == 1)
- n_chans = 4;
+ if (iface->conf->ieee80211ac) {
+ switch (iface->conf->vht_oper_chwidth) {
+ case VHT_CHANWIDTH_80MHZ:
+ n_chans = 4;
+ break;
+ case VHT_CHANWIDTH_160MHZ:
+ n_chans = 8;
+ break;
+ }
+ }
+
+ bw = num_chan_to_bw(n_chans);
- /* TODO: VHT80+80, VHT160. Update acs_adjust_vht_center_freq() too. */
+ /* TODO: VHT80+80. Update acs_adjust_vht_center_freq() too. */
- wpa_printf(MSG_DEBUG, "ACS: Survey analysis for selected bandwidth %d MHz",
- n_chans == 1 ? 20 :
- n_chans == 2 ? 40 :
- 80);
+ wpa_printf(MSG_DEBUG,
+ "ACS: Survey analysis for selected bandwidth %d MHz", bw);
for (i = 0; i < iface->current_mode->num_channels; i++) {
double total_weight;
@@ -596,12 +618,23 @@ acs_find_ideal_chan(struct hostapd_iface *iface)
chan = &iface->current_mode->channels[i];
- if (chan->flag & HOSTAPD_CHAN_DISABLED)
+ /* Since in the current ACS implementation the first channel is
+ * always a primary channel, skip channels not available as
+ * primary until more sophisticated channel selection is
+ * implemented. */
+ if (!chan_pri_allowed(chan))
continue;
if (!is_in_chanlist(iface, chan))
continue;
+ if (!chan_bw_allowed(chan, bw, 1, 1)) {
+ wpa_printf(MSG_DEBUG,
+ "ACS: Channel %d: BW %u is not supported",
+ chan->chan, bw);
+ continue;
+ }
+
/* HT40 on 5 GHz has a limited set of primary channels as per
* 11n Annex J */
if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
@@ -614,12 +647,24 @@ acs_find_ideal_chan(struct hostapd_iface *iface)
}
if (iface->current_mode->mode == HOSTAPD_MODE_IEEE80211A &&
- iface->conf->ieee80211ac &&
- iface->conf->vht_oper_chwidth == 1 &&
- !acs_usable_vht80_chan(chan)) {
- wpa_printf(MSG_DEBUG, "ACS: Channel %d: not allowed as primary channel for VHT80",
- chan->chan);
- continue;
+ iface->conf->ieee80211ac) {
+ if (iface->conf->vht_oper_chwidth ==
+ VHT_CHANWIDTH_80MHZ &&
+ !acs_usable_vht80_chan(chan)) {
+ wpa_printf(MSG_DEBUG,
+ "ACS: Channel %d: not allowed as primary channel for VHT80",
+ chan->chan);
+ continue;
+ }
+
+ if (iface->conf->vht_oper_chwidth ==
+ VHT_CHANWIDTH_160MHZ &&
+ !acs_usable_vht160_chan(chan)) {
+ wpa_printf(MSG_DEBUG,
+ "ACS: Channel %d: not allowed as primary channel for VHT160",
+ chan->chan);
+ continue;
+ }
}
factor = 0;
@@ -632,6 +677,13 @@ acs_find_ideal_chan(struct hostapd_iface *iface)
if (!adj_chan)
break;
+ if (!chan_bw_allowed(adj_chan, bw, 1, 0)) {
+ wpa_printf(MSG_DEBUG,
+ "ACS: PRI Channel %d: secondary channel %d BW %u is not supported",
+ chan->chan, adj_chan->chan, bw);
+ break;
+ }
+
if (acs_usable_chan(adj_chan)) {
factor += adj_chan->interference_factor;
total_weight += 1;
@@ -744,10 +796,14 @@ static void acs_adjust_vht_center_freq(struct hostapd_iface *iface)
case VHT_CHANWIDTH_80MHZ:
offset = 6;
break;
+ case VHT_CHANWIDTH_160MHZ:
+ offset = 14;
+ break;
default:
/* TODO: How can this be calculated? Adjust
* acs_find_ideal_chan() */
- wpa_printf(MSG_INFO, "ACS: Only VHT20/40/80 is supported now");
+ wpa_printf(MSG_INFO,
+ "ACS: Only VHT20/40/80/160 is supported now");
return;
}
diff --git a/src/ap/ap_config.c b/src/ap/ap_config.c
index f9b6f295927f9..e640e9984b70e 100644
--- a/src/ap/ap_config.c
+++ b/src/ap/ap_config.c
@@ -131,6 +131,15 @@ void hostapd_config_defaults_bss(struct hostapd_bss_config *bss)
* This can be enabled by default once the implementation has been fully
* completed and tested with other implementations. */
bss->tls_flags = TLS_CONN_DISABLE_TLSv1_3;
+
+ bss->send_probe_response = 1;
+
+#ifdef CONFIG_HS20
+ bss->hs20_release = (HS20_VERSION >> 4) + 1;
+#endif /* CONFIG_HS20 */
+
+ /* Default to strict CRL checking. */
+ bss->check_crl_strict = 1;
}
@@ -191,9 +200,8 @@ struct hostapd_config * hostapd_config_defaults(void)
conf->num_bss = 1;
conf->beacon_int = 100;
- conf->rts_threshold = -1; /* use driver default: 2347 */
- conf->fragm_threshold = -1; /* user driver default: 2346 */
- conf->send_probe_response = 1;
+ conf->rts_threshold = -2; /* use driver default: 2347 */
+ conf->fragm_threshold = -2; /* user driver default: 2346 */
/* Set to invalid value means do not add Power Constraint IE */
conf->local_pwr_constraint = -1;
@@ -233,6 +241,9 @@ struct hostapd_config * hostapd_config_defaults(void)
* environments for the current frequency band in the country. */
conf->country[2] = ' ';
+ conf->rssi_reject_assoc_rssi = 0;
+ conf->rssi_reject_assoc_timeout = 30;
+
return conf;
}
@@ -248,6 +259,12 @@ static int hostapd_config_read_wpa_psk(const char *fname,
{
FILE *f;
char buf[128], *pos;
+ const char *keyid;
+ char *context;
+ char *context2;
+ char *token;
+ char *name;
+ char *value;
int line = 0, ret = 0, len, ok;
u8 addr[ETH_ALEN];
struct hostapd_wpa_psk *psk;
@@ -262,6 +279,8 @@ static int hostapd_config_read_wpa_psk(const char *fname,
}
while (fgets(buf, sizeof(buf), f)) {
+ int vlan_id = 0;
+
line++;
if (buf[0] == '#')
@@ -277,9 +296,39 @@ static int hostapd_config_read_wpa_psk(const char *fname,
if (buf[0] == '\0')
continue;
- if (hwaddr_aton(buf, addr)) {
+ context = NULL;
+ keyid = NULL;
+ while ((token = str_token(buf, " ", &context))) {
+ if (!os_strchr(token, '='))
+ break;
+ context2 = NULL;
+ name = str_token(token, "=", &context2);
+ if (!name)
+ break;
+ value = str_token(token, "", &context2);
+ if (!value)
+ value = "";
+ if (!os_strcmp(name, "keyid")) {
+ keyid = value;
+ } else if (!os_strcmp(name, "vlanid")) {
+ vlan_id = atoi(value);
+ } else {
+ wpa_printf(MSG_ERROR,
+ "Unrecognized '%s=%s' on line %d in '%s'",
+ name, value, line, fname);
+ ret = -1;
+ break;
+ }
+ }
+
+ if (ret == -1)
+ break;
+
+ if (!token)
+ token = "";
+ if (hwaddr_aton(token, addr)) {
wpa_printf(MSG_ERROR, "Invalid MAC address '%s' on "
- "line %d in '%s'", buf, line, fname);
+ "line %d in '%s'", token, line, fname);
ret = -1;
break;
}
@@ -290,20 +339,20 @@ static int hostapd_config_read_wpa_psk(const char *fname,
ret = -1;
break;
}
+ psk->vlan_id = vlan_id;
if (is_zero_ether_addr(addr))
psk->group = 1;
else
os_memcpy(psk->addr, addr, ETH_ALEN);
- pos = buf + 17;
- if (*pos == '\0') {
+ pos = str_token(buf, "", &context);
+ if (!pos) {
wpa_printf(MSG_ERROR, "No PSK on line %d in '%s'",
line, fname);
os_free(psk);
ret = -1;
break;
}
- pos++;
ok = 0;
len = os_strlen(pos);
@@ -322,6 +371,18 @@ static int hostapd_config_read_wpa_psk(const char *fname,
break;
}
+ if (keyid) {
+ len = os_strlcpy(psk->keyid, keyid, sizeof(psk->keyid));
+ if ((size_t) len >= sizeof(psk->keyid)) {
+ wpa_printf(MSG_ERROR,
+ "PSK keyid too long on line %d in '%s'",
+ line, fname);
+ os_free(psk);
+ ret = -1;
+ break;
+ }
+ }
+
psk->next = ssid->wpa_psk;
ssid->wpa_psk = psk;
}
@@ -534,10 +595,12 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf)
os_free(conf->server_cert);
os_free(conf->private_key);
os_free(conf->private_key_passwd);
+ os_free(conf->check_cert_subject);
os_free(conf->ocsp_stapling_response);
os_free(conf->ocsp_stapling_response_multi);
os_free(conf->dh_file);
os_free(conf->openssl_ciphers);
+ os_free(conf->openssl_ecdh_curves);
os_free(conf->pac_opaque_encr_key);
os_free(conf->eap_fast_a_id);
os_free(conf->eap_fast_a_id_info);
@@ -582,6 +645,8 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf)
os_free(conf->ap_pin);
os_free(conf->extra_cred);
os_free(conf->ap_settings);
+ hostapd_config_clear_wpa_psk(&conf->multi_ap_backhaul_ssid.wpa_psk);
+ str_clear_free(conf->multi_ap_backhaul_ssid.wpa_passphrase);
os_free(conf->upnp_iface);
os_free(conf->friendly_name);
os_free(conf->manufacturer_url);
@@ -644,6 +709,7 @@ void hostapd_config_free_bss(struct hostapd_bss_config *conf)
os_free(conf->hs20_operator_icon);
}
os_free(conf->subscr_remediation_url);
+ os_free(conf->hs20_sim_provisioning_url);
os_free(conf->t_c_filename);
os_free(conf->t_c_server_url);
#endif /* CONFIG_HS20 */
@@ -802,11 +868,14 @@ const char * hostapd_get_vlan_id_ifname(struct hostapd_vlan *vlan, int vlan_id)
const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
const u8 *addr, const u8 *p2p_dev_addr,
- const u8 *prev_psk)
+ const u8 *prev_psk, int *vlan_id)
{
struct hostapd_wpa_psk *psk;
int next_ok = prev_psk == NULL;
+ if (vlan_id)
+ *vlan_id = 0;
+
if (p2p_dev_addr && !is_zero_ether_addr(p2p_dev_addr)) {
wpa_printf(MSG_DEBUG, "Searching a PSK for " MACSTR
" p2p_dev_addr=" MACSTR " prev_psk=%p",
@@ -824,8 +893,11 @@ const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
(addr && os_memcmp(psk->addr, addr, ETH_ALEN) == 0) ||
(!addr && p2p_dev_addr &&
os_memcmp(psk->p2p_dev_addr, p2p_dev_addr, ETH_ALEN) ==
- 0)))
+ 0))) {
+ if (vlan_id)
+ *vlan_id = psk->vlan_id;
return psk->psk;
+ }
if (psk->psk == prev_psk)
next_ok = 1;
@@ -1003,6 +1075,15 @@ static int hostapd_config_check_bss(struct hostapd_bss_config *bss,
}
#endif /* CONFIG_MBO */
+#ifdef CONFIG_OCV
+ if (full_config && bss->ieee80211w == NO_MGMT_FRAME_PROTECTION &&
+ bss->ocv) {
+ wpa_printf(MSG_ERROR,
+ "OCV: PMF needs to be enabled whenever using OCV");
+ return -1;
+ }
+#endif /* CONFIG_OCV */
+
return 0;
}
@@ -1146,3 +1227,26 @@ void hostapd_set_security_params(struct hostapd_bss_config *bss,
}
}
}
+
+
+int hostapd_sae_pw_id_in_use(struct hostapd_bss_config *conf)
+{
+ int with_id = 0, without_id = 0;
+ struct sae_password_entry *pw;
+
+ if (conf->ssid.wpa_passphrase)
+ without_id = 1;
+
+ for (pw = conf->sae_passwords; pw; pw = pw->next) {
+ if (pw->identifier)
+ with_id = 1;
+ else
+ without_id = 1;
+ if (with_id && without_id)
+ break;
+ }
+
+ if (with_id && !without_id)
+ return 2;
+ return with_id;
+}
diff --git a/src/ap/ap_config.h b/src/ap/ap_config.h
index 778366d49afe0..509677a45f056 100644
--- a/src/ap/ap_config.h
+++ b/src/ap/ap_config.h
@@ -42,6 +42,7 @@ struct mesh_conf {
#define MESH_CONF_SEC_AMPE BIT(2)
unsigned int security;
enum mfp_options ieee80211w;
+ int ocv;
unsigned int pairwise_cipher;
unsigned int group_cipher;
unsigned int mgmt_group_cipher;
@@ -122,6 +123,7 @@ struct hostapd_vlan {
int vlan_id; /* VLAN ID or -1 (VLAN_ID_WILDCARD) for wildcard entry */
struct vlan_description vlan_desc;
char ifname[IFNAMSIZ + 1];
+ char bridge[IFNAMSIZ + 1];
int configured;
int dynamic_vlan;
#ifdef CONFIG_FULL_DYNAMIC_VLAN
@@ -132,6 +134,7 @@ struct hostapd_vlan {
};
#define PMK_LEN 32
+#define KEYID_LEN 32
#define MIN_PASSPHRASE_LEN 8
#define MAX_PASSPHRASE_LEN 63
struct hostapd_sta_wpa_psk_short {
@@ -145,9 +148,11 @@ struct hostapd_sta_wpa_psk_short {
struct hostapd_wpa_psk {
struct hostapd_wpa_psk *next;
int group;
+ char keyid[KEYID_LEN];
u8 psk[PMK_LEN];
u8 addr[ETH_ALEN];
u8 p2p_dev_addr[ETH_ALEN];
+ int vlan_id;
};
struct hostapd_eap_user {
@@ -244,6 +249,7 @@ struct sae_password_entry {
char *password;
char *identifier;
u8 peer_addr[ETH_ALEN];
+ int vlan_id;
};
/**
@@ -335,6 +341,9 @@ struct hostapd_bss_config {
/* dot11AssociationSAQueryRetryTimeout (in TUs) */
int assoc_sa_query_retry_timeout;
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_OCV
+ int ocv; /* Operating Channel Validation */
+#endif /* CONFIG_OCV */
enum {
PSK_RADIUS_IGNORED = 0,
PSK_RADIUS_ACCEPTED = 1,
@@ -383,13 +392,17 @@ struct hostapd_bss_config {
char *server_cert;
char *private_key;
char *private_key_passwd;
+ char *check_cert_subject;
int check_crl;
+ int check_crl_strict;
+ unsigned int crl_reload_interval;
unsigned int tls_session_lifetime;
unsigned int tls_flags;
char *ocsp_stapling_response;
char *ocsp_stapling_response_multi;
char *dh_file;
char *openssl_ciphers;
+ char *openssl_ecdh_curves;
u8 *pac_opaque_encr_key;
u8 *eap_fast_a_id;
size_t eap_fast_a_id_len;
@@ -452,9 +465,11 @@ struct hostapd_bss_config {
u8 *extra_cred;
size_t extra_cred_len;
int wps_cred_processing;
+ int wps_cred_add_sae;
int force_per_enrollee_psk;
u8 *ap_settings;
size_t ap_settings_len;
+ struct hostapd_ssid multi_ap_backhaul_ssid;
char *upnp_iface;
char *friendly_name;
char *manufacturer_url;
@@ -557,6 +572,7 @@ struct hostapd_bss_config {
int na_mcast_to_ucast;
#ifdef CONFIG_HS20
int hs20;
+ int hs20_release;
int disable_dgaf;
u16 anqp_domain_id;
unsigned int hs20_oper_friendly_name_count;
@@ -596,6 +612,7 @@ struct hostapd_bss_config {
unsigned int hs20_deauth_req_timeout;
char *subscr_remediation_url;
u8 subscr_remediation_method;
+ char *hs20_sim_provisioning_url;
char *t_c_filename;
u32 t_c_timestamp;
char *t_c_server_url;
@@ -686,6 +703,12 @@ struct hostapd_bss_config {
#endif /* CONFIG_OWE */
int coloc_intf_reporting;
+
+ u8 send_probe_response;
+
+#define BACKHAUL_BSS 1
+#define FRONTHAUL_BSS 2
+ int multi_ap; /* bitmap of BACKHAUL_BSS, FRONTHAUL_BSS */
};
/**
@@ -717,7 +740,6 @@ struct hostapd_config {
u16 beacon_int;
int rts_threshold;
int fragm_threshold;
- u8 send_probe_response;
u8 channel;
u8 acs;
struct wpa_freq_range_list acs_ch_list;
@@ -829,12 +851,16 @@ struct hostapd_config {
#ifdef CONFIG_IEEE80211AX
struct he_phy_capabilities_info he_phy_capab;
struct he_operation he_op;
+ struct ieee80211_he_mu_edca_parameter_set he_mu_edca;
#endif /* CONFIG_IEEE80211AX */
/* VHT enable/disable config from CHAN_SWITCH */
#define CH_SWITCH_VHT_ENABLED BIT(0)
#define CH_SWITCH_VHT_DISABLED BIT(1)
unsigned int ch_switch_vht_config;
+
+ int rssi_reject_assoc_rssi;
+ int rssi_reject_assoc_timeout;
};
@@ -851,7 +877,7 @@ int hostapd_maclist_found(struct mac_acl_entry *list, int num_entries,
int hostapd_rate_found(int *list, int rate);
const u8 * hostapd_get_psk(const struct hostapd_bss_config *conf,
const u8 *addr, const u8 *p2p_dev_addr,
- const u8 *prev_psk);
+ const u8 *prev_psk, int *vlan_id);
int hostapd_setup_wpa_psk(struct hostapd_bss_config *conf);
int hostapd_vlan_valid(struct hostapd_vlan *vlan,
struct vlan_description *vlan_desc);
@@ -862,5 +888,6 @@ hostapd_config_get_radius_attr(struct hostapd_radius_attr *attr, u8 type);
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);
#endif /* HOSTAPD_CONFIG_H */
diff --git a/src/ap/ap_drv_ops.h b/src/ap/ap_drv_ops.h
index db93fde7d6e3a..de40171e18dcb 100644
--- a/src/ap/ap_drv_ops.h
+++ b/src/ap/ap_drv_ops.h
@@ -356,4 +356,22 @@ static inline int hostapd_drv_stop_ap(struct hostapd_data *hapd)
return hapd->driver->stop_ap(hapd->drv_priv);
}
+static inline int hostapd_drv_channel_info(struct hostapd_data *hapd,
+ struct wpa_channel_info *ci)
+{
+ if (!hapd->driver || !hapd->driver->channel_info)
+ return -1;
+ return hapd->driver->channel_info(hapd->drv_priv, ci);
+}
+
+static inline int
+hostapd_drv_send_external_auth_status(struct hostapd_data *hapd,
+ struct external_auth *params)
+{
+ if (!hapd->driver || !hapd->drv_priv ||
+ !hapd->driver->send_external_auth_status)
+ return -1;
+ return hapd->driver->send_external_auth_status(hapd->drv_priv, params);
+}
+
#endif /* AP_DRV_OPS */
diff --git a/src/ap/authsrv.c b/src/ap/authsrv.c
index 95d004ed2b16a..eced6c7c6d941 100644
--- a/src/ap/authsrv.c
+++ b/src/ap/authsrv.c
@@ -136,6 +136,7 @@ static int hostapd_setup_radius_srv(struct hostapd_data *hapd)
#ifdef CONFIG_HS20
srv.subscr_remediation_url = conf->subscr_remediation_url;
srv.subscr_remediation_method = conf->subscr_remediation_method;
+ srv.hs20_sim_provisioning_url = conf->hs20_sim_provisioning_url;
srv.t_c_server_url = conf->t_c_server_url;
#endif /* CONFIG_HS20 */
srv.erp = conf->eap_server_erp;
@@ -200,6 +201,16 @@ int authsrv_init(struct hostapd_data *hapd)
os_memset(&conf, 0, sizeof(conf));
conf.tls_session_lifetime = hapd->conf->tls_session_lifetime;
+ if (hapd->conf->crl_reload_interval > 0 &&
+ hapd->conf->check_crl <= 0) {
+ wpa_printf(MSG_INFO,
+ "Cannot enable CRL reload functionality - it depends on check_crl being set");
+ } else if (hapd->conf->crl_reload_interval > 0) {
+ conf.crl_reload_interval =
+ hapd->conf->crl_reload_interval;
+ wpa_printf(MSG_INFO,
+ "Enabled CRL reload functionality");
+ }
conf.tls_flags = hapd->conf->tls_flags;
conf.event_cb = authsrv_tls_event;
conf.cb_ctx = hapd;
@@ -217,10 +228,12 @@ int authsrv_init(struct hostapd_data *hapd)
params.private_key_passwd = hapd->conf->private_key_passwd;
params.dh_file = hapd->conf->dh_file;
params.openssl_ciphers = hapd->conf->openssl_ciphers;
+ params.openssl_ecdh_curves = hapd->conf->openssl_ecdh_curves;
params.ocsp_stapling_response =
hapd->conf->ocsp_stapling_response;
params.ocsp_stapling_response_multi =
hapd->conf->ocsp_stapling_response_multi;
+ params.check_cert_subject = hapd->conf->check_cert_subject;
if (tls_global_set_params(hapd->ssl_ctx, &params)) {
wpa_printf(MSG_ERROR, "Failed to set TLS parameters");
@@ -229,7 +242,8 @@ int authsrv_init(struct hostapd_data *hapd)
}
if (tls_global_set_verify(hapd->ssl_ctx,
- hapd->conf->check_crl)) {
+ hapd->conf->check_crl,
+ hapd->conf->check_crl_strict)) {
wpa_printf(MSG_ERROR, "Failed to enable check_crl");
authsrv_deinit(hapd);
return -1;
diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 59bd4af395d7a..3e62991d07aff 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -397,7 +397,8 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
#ifdef CONFIG_IEEE80211AX
if (hapd->iconf->ieee80211ax) {
buflen += 3 + sizeof(struct ieee80211_he_capabilities) +
- 3 + sizeof(struct ieee80211_he_operation);
+ 3 + sizeof(struct ieee80211_he_operation) +
+ 3 + sizeof(struct ieee80211_he_mu_edca_parameter_set);
}
#endif /* CONFIG_IEEE80211AX */
@@ -510,6 +511,7 @@ static u8 * hostapd_gen_probe_resp(struct hostapd_data *hapd,
if (hapd->iconf->ieee80211ax) {
pos = hostapd_eid_he_capab(hapd, pos);
pos = hostapd_eid_he_operation(hapd, pos);
+ pos = hostapd_eid_he_mu_edca_parameter_set(hapd, pos);
}
#endif /* CONFIG_IEEE80211AX */
@@ -767,7 +769,7 @@ void handle_probe_req(struct hostapd_data *hapd,
ie, ie_len, ssi_signal) > 0)
return;
- if (!hapd->iconf->send_probe_response)
+ if (!hapd->conf->send_probe_response)
return;
if (ieee802_11_parse_elems(ie, ie_len, &elems, 0) == ParseFailed) {
@@ -1085,7 +1087,8 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
#ifdef CONFIG_IEEE80211AX
if (hapd->iconf->ieee80211ax) {
tail_len += 3 + sizeof(struct ieee80211_he_capabilities) +
- 3 + sizeof(struct ieee80211_he_operation);
+ 3 + sizeof(struct ieee80211_he_operation) +
+ 3 + sizeof(struct ieee80211_he_mu_edca_parameter_set);
}
#endif /* CONFIG_IEEE80211AX */
@@ -1222,6 +1225,7 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
if (hapd->iconf->ieee80211ax) {
tailpos = hostapd_eid_he_capab(hapd, tailpos);
tailpos = hostapd_eid_he_operation(hapd, tailpos);
+ tailpos = hostapd_eid_he_mu_edca_parameter_set(hapd, tailpos);
}
#endif /* CONFIG_IEEE80211AX */
@@ -1357,6 +1361,18 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
#endif /* CONFIG_HS20 */
params->multicast_to_unicast = hapd->conf->multicast_to_unicast;
params->pbss = hapd->conf->pbss;
+
+ if (hapd->conf->ftm_responder) {
+ if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_FTM_RESPONDER) {
+ params->ftm_responder = 1;
+ params->lci = hapd->iface->conf->lci;
+ params->civic = hapd->iface->conf->civic;
+ } else {
+ wpa_printf(MSG_WARNING,
+ "Not configuring FTM responder as the driver doesn't advertise support for it");
+ }
+ }
+
return 0;
}
diff --git a/src/ap/ctrl_iface_ap.c b/src/ap/ctrl_iface_ap.c
index 21b813ee18d77..c693715116344 100644
--- a/src/ap/ctrl_iface_ap.c
+++ b/src/ap/ctrl_iface_ap.c
@@ -1,6 +1,6 @@
/*
* Control interface for shared AP commands
- * Copyright (c) 2004-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -207,6 +207,7 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
char *buf, size_t buflen)
{
int len, res, ret, i;
+ const char *keyid;
if (!sta)
return 0;
@@ -341,6 +342,13 @@ static int hostapd_ctrl_iface_sta_mib(struct hostapd_data *hapd,
len += ret;
}
+ keyid = ap_sta_wpa_get_keyid(hapd, sta);
+ if (keyid) {
+ ret = os_snprintf(buf + len, buflen - len, "keyid=%s\n", keyid);
+ if (!os_snprintf_error(buflen - len, ret))
+ len += ret;
+ }
+
return len;
}
@@ -443,11 +451,11 @@ static int p2p_manager_disconnect(struct hostapd_data *hapd, u16 stype,
if (stype == WLAN_FC_STYPE_DEAUTH) {
mgmt->u.deauth.reason_code =
host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
- pos = (u8 *) (&mgmt->u.deauth.reason_code + 1);
+ pos = mgmt->u.deauth.variable;
} else {
mgmt->u.disassoc.reason_code =
host_to_le16(WLAN_REASON_PREV_AUTH_NOT_VALID);
- pos = (u8 *) (&mgmt->u.disassoc.reason_code + 1);
+ pos = mgmt->u.disassoc.variable;
}
*pos++ = WLAN_EID_VENDOR_SPECIFIC;
diff --git a/src/ap/dfs.c b/src/ap/dfs.c
index 993dd19c2cb07..79cd00f44a780 100644
--- a/src/ap/dfs.c
+++ b/src/ap/dfs.c
@@ -142,18 +142,30 @@ static int dfs_chan_range_available(struct hostapd_hw_modes *mode,
{
struct hostapd_channel_data *first_chan, *chan;
int i;
+ u32 bw = num_chan_to_bw(num_chans);
if (first_chan_idx + num_chans > mode->num_channels)
return 0;
first_chan = &mode->channels[first_chan_idx];
+ /* hostapd DFS implementation assumes the first channel as primary.
+ * If it's not allowed to use the first channel as primary, decline the
+ * whole channel range. */
+ if (!chan_pri_allowed(first_chan))
+ return 0;
+
for (i = 0; i < num_chans; i++) {
chan = dfs_get_chan_data(mode, first_chan->freq + i * 20,
first_chan_idx);
if (!chan)
return 0;
+ /* HT 40 MHz secondary channel availability checked only for
+ * primary channel */
+ if (!chan_bw_allowed(chan, bw, 1, !i))
+ return 0;
+
if (!dfs_channel_available(chan, skip_radar))
return 0;
}
@@ -197,7 +209,8 @@ static int dfs_find_channel(struct hostapd_iface *iface,
/* Skip HT40/VHT incompatible channels */
if (iface->conf->ieee80211n &&
iface->conf->secondary_channel &&
- !dfs_is_chan_allowed(chan, n_chans))
+ (!dfs_is_chan_allowed(chan, n_chans) ||
+ !(chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)))
continue;
/* Skip incompatible chandefs */
diff --git a/src/ap/dhcp_snoop.c b/src/ap/dhcp_snoop.c
index 6d8c2f4be0da9..ed37fc8fe96a0 100644
--- a/src/ap/dhcp_snoop.c
+++ b/src/ap/dhcp_snoop.c
@@ -88,6 +88,15 @@ static void handle_dhcp(void *ctx, const u8 *src_addr, const u8 *buf,
}
}
+ if (hapd->conf->disable_dgaf && is_broadcast_ether_addr(buf)) {
+ for (sta = hapd->sta_list; sta; sta = sta->next) {
+ if (!(sta->flags & WLAN_STA_AUTHORIZED))
+ continue;
+ x_snoop_mcast_to_ucast_convert_send(hapd, sta,
+ (u8 *) buf, len);
+ }
+ }
+
if (msgtype == DHCPACK) {
if (b->your_ip == 0)
return;
@@ -124,15 +133,6 @@ static void handle_dhcp(void *ctx, const u8 *src_addr, const u8 *buf,
}
sta->ipaddr = b->your_ip;
}
-
- if (hapd->conf->disable_dgaf && is_broadcast_ether_addr(buf)) {
- for (sta = hapd->sta_list; sta; sta = sta->next) {
- if (!(sta->flags & WLAN_STA_AUTHORIZED))
- continue;
- x_snoop_mcast_to_ucast_convert_send(hapd, sta,
- (u8 *) buf, len);
- }
- }
}
diff --git a/src/ap/dpp_hostapd.c b/src/ap/dpp_hostapd.c
index 149f389f789f1..75edbc909e7a0 100644
--- a/src/ap/dpp_hostapd.c
+++ b/src/ap/dpp_hostapd.c
@@ -28,34 +28,6 @@ static int hostapd_dpp_auth_init_next(struct hostapd_data *hapd);
static const u8 broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
-static struct dpp_configurator *
-hostapd_dpp_configurator_get_id(struct hostapd_data *hapd, unsigned int id)
-{
- struct dpp_configurator *conf;
-
- dl_list_for_each(conf, &hapd->iface->interfaces->dpp_configurator,
- struct dpp_configurator, list) {
- if (conf->id == id)
- return conf;
- }
- return NULL;
-}
-
-
-static unsigned int hapd_dpp_next_id(struct hostapd_data *hapd)
-{
- struct dpp_bootstrap_info *bi;
- unsigned int max_id = 0;
-
- dl_list_for_each(bi, &hapd->iface->interfaces->dpp_bootstrap,
- struct dpp_bootstrap_info, list) {
- if (bi->id > max_id)
- max_id = bi->id;
- }
- return max_id + 1;
-}
-
-
/**
* hostapd_dpp_qr_code - Parse and add DPP bootstrapping info from a QR Code
* @hapd: Pointer to hostapd_data
@@ -67,13 +39,10 @@ int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd)
struct dpp_bootstrap_info *bi;
struct dpp_authentication *auth = hapd->dpp_auth;
- bi = dpp_parse_qr_code(cmd);
+ bi = dpp_add_qr_code(hapd->iface->interfaces->dpp, cmd);
if (!bi)
return -1;
- bi->id = hapd_dpp_next_id(hapd);
- dl_list_add(&hapd->iface->interfaces->dpp_bootstrap, &bi->list);
-
if (auth && auth->response_pending &&
dpp_notify_new_qr_code(auth, bi) == 1) {
wpa_printf(MSG_DEBUG,
@@ -92,195 +61,6 @@ int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd)
}
-static char * get_param(const char *cmd, const char *param)
-{
- const char *pos, *end;
- char *val;
- size_t len;
-
- pos = os_strstr(cmd, param);
- if (!pos)
- return NULL;
-
- pos += os_strlen(param);
- end = os_strchr(pos, ' ');
- if (end)
- len = end - pos;
- else
- len = os_strlen(pos);
- val = os_malloc(len + 1);
- if (!val)
- return NULL;
- os_memcpy(val, pos, len);
- val[len] = '\0';
- return val;
-}
-
-
-int hostapd_dpp_bootstrap_gen(struct hostapd_data *hapd, const char *cmd)
-{
- char *chan = NULL, *mac = NULL, *info = NULL, *pk = NULL, *curve = NULL;
- char *key = NULL;
- u8 *privkey = NULL;
- size_t privkey_len = 0;
- size_t len;
- int ret = -1;
- struct dpp_bootstrap_info *bi;
-
- bi = os_zalloc(sizeof(*bi));
- if (!bi)
- goto fail;
-
- if (os_strstr(cmd, "type=qrcode"))
- bi->type = DPP_BOOTSTRAP_QR_CODE;
- else if (os_strstr(cmd, "type=pkex"))
- bi->type = DPP_BOOTSTRAP_PKEX;
- else
- goto fail;
-
- chan = get_param(cmd, " chan=");
- mac = get_param(cmd, " mac=");
- info = get_param(cmd, " info=");
- curve = get_param(cmd, " curve=");
- key = get_param(cmd, " key=");
-
- if (key) {
- privkey_len = os_strlen(key) / 2;
- privkey = os_malloc(privkey_len);
- if (!privkey ||
- hexstr2bin(key, privkey, privkey_len) < 0)
- goto fail;
- }
-
- pk = dpp_keygen(bi, curve, privkey, privkey_len);
- if (!pk)
- goto fail;
-
- len = 4; /* "DPP:" */
- if (chan) {
- if (dpp_parse_uri_chan_list(bi, chan) < 0)
- goto fail;
- len += 3 + os_strlen(chan); /* C:...; */
- }
- if (mac) {
- if (dpp_parse_uri_mac(bi, mac) < 0)
- goto fail;
- len += 3 + os_strlen(mac); /* M:...; */
- }
- if (info) {
- if (dpp_parse_uri_info(bi, info) < 0)
- goto fail;
- len += 3 + os_strlen(info); /* I:...; */
- }
- len += 4 + os_strlen(pk);
- bi->uri = os_malloc(len + 1);
- if (!bi->uri)
- goto fail;
- os_snprintf(bi->uri, len + 1, "DPP:%s%s%s%s%s%s%s%s%sK:%s;;",
- chan ? "C:" : "", chan ? chan : "", chan ? ";" : "",
- mac ? "M:" : "", mac ? mac : "", mac ? ";" : "",
- info ? "I:" : "", info ? info : "", info ? ";" : "",
- pk);
- bi->id = hapd_dpp_next_id(hapd);
- dl_list_add(&hapd->iface->interfaces->dpp_bootstrap, &bi->list);
- ret = bi->id;
- bi = NULL;
-fail:
- os_free(curve);
- os_free(pk);
- os_free(chan);
- os_free(mac);
- os_free(info);
- str_clear_free(key);
- bin_clear_free(privkey, privkey_len);
- dpp_bootstrap_info_free(bi);
- return ret;
-}
-
-
-static struct dpp_bootstrap_info *
-dpp_bootstrap_get_id(struct hostapd_data *hapd, unsigned int id)
-{
- struct dpp_bootstrap_info *bi;
-
- dl_list_for_each(bi, &hapd->iface->interfaces->dpp_bootstrap,
- struct dpp_bootstrap_info, list) {
- if (bi->id == id)
- return bi;
- }
- return NULL;
-}
-
-
-static int dpp_bootstrap_del(struct hapd_interfaces *ifaces, unsigned int id)
-{
- struct dpp_bootstrap_info *bi, *tmp;
- int found = 0;
-
- dl_list_for_each_safe(bi, tmp, &ifaces->dpp_bootstrap,
- struct dpp_bootstrap_info, list) {
- if (id && bi->id != id)
- continue;
- found = 1;
- dl_list_del(&bi->list);
- dpp_bootstrap_info_free(bi);
- }
-
- if (id == 0)
- return 0; /* flush succeeds regardless of entries found */
- return found ? 0 : -1;
-}
-
-
-int hostapd_dpp_bootstrap_remove(struct hostapd_data *hapd, const char *id)
-{
- unsigned int id_val;
-
- if (os_strcmp(id, "*") == 0) {
- id_val = 0;
- } else {
- id_val = atoi(id);
- if (id_val == 0)
- return -1;
- }
-
- return dpp_bootstrap_del(hapd->iface->interfaces, id_val);
-}
-
-
-const char * hostapd_dpp_bootstrap_get_uri(struct hostapd_data *hapd,
- unsigned int id)
-{
- struct dpp_bootstrap_info *bi;
-
- bi = dpp_bootstrap_get_id(hapd, id);
- if (!bi)
- return NULL;
- return bi->uri;
-}
-
-
-int hostapd_dpp_bootstrap_info(struct hostapd_data *hapd, int id,
- char *reply, int reply_size)
-{
- struct dpp_bootstrap_info *bi;
-
- bi = dpp_bootstrap_get_id(hapd, id);
- if (!bi)
- return -1;
- return os_snprintf(reply, reply_size, "type=%s\n"
- "mac_addr=" MACSTR "\n"
- "info=%s\n"
- "num_freq=%u\n"
- "curve=%s\n",
- dpp_bootstrap_type_txt(bi->type),
- MAC2STR(bi->mac_addr),
- bi->info ? bi->info : "",
- bi->num_freq,
- bi->curve->name);
-}
-
-
static void hostapd_dpp_auth_resp_retry_timeout(void *eloop_ctx,
void *timeout_ctx)
{
@@ -354,6 +134,16 @@ void hostapd_dpp_tx_status(struct hostapd_data *hapd, const u8 *dst,
return;
}
+#ifdef CONFIG_DPP2
+ if (auth->connect_on_tx_status) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Complete exchange on configuration result");
+ dpp_auth_deinit(hapd->dpp_auth);
+ hapd->dpp_auth = NULL;
+ return;
+ }
+#endif /* CONFIG_DPP2 */
+
if (hapd->dpp_auth->remove_on_tx_status) {
wpa_printf(MSG_DEBUG,
"DPP: Terminate authentication exchange due to an earlier error");
@@ -505,178 +295,6 @@ static void hostapd_dpp_set_testing_options(struct hostapd_data *hapd,
}
-static int hostapd_dpp_set_configurator(struct hostapd_data *hapd,
- struct dpp_authentication *auth,
- const char *cmd)
-{
- const char *pos, *end;
- struct dpp_configuration *conf_sta = NULL, *conf_ap = NULL;
- struct dpp_configurator *conf = NULL;
- u8 ssid[32] = { "test" };
- size_t ssid_len = 4;
- char pass[64] = { };
- size_t pass_len = 0;
- u8 psk[PMK_LEN];
- int psk_set = 0;
- char *group_id = NULL;
-
- if (!cmd)
- return 0;
-
- wpa_printf(MSG_DEBUG, "DPP: Set configurator parameters: %s", cmd);
- pos = os_strstr(cmd, " ssid=");
- if (pos) {
- pos += 6;
- end = os_strchr(pos, ' ');
- ssid_len = end ? (size_t) (end - pos) : os_strlen(pos);
- ssid_len /= 2;
- if (ssid_len > sizeof(ssid) ||
- hexstr2bin(pos, ssid, ssid_len) < 0)
- goto fail;
- }
-
- pos = os_strstr(cmd, " pass=");
- if (pos) {
- pos += 6;
- end = os_strchr(pos, ' ');
- pass_len = end ? (size_t) (end - pos) : os_strlen(pos);
- pass_len /= 2;
- if (pass_len > sizeof(pass) - 1 || pass_len < 8 ||
- hexstr2bin(pos, (u8 *) pass, pass_len) < 0)
- goto fail;
- }
-
- pos = os_strstr(cmd, " psk=");
- if (pos) {
- pos += 5;
- if (hexstr2bin(pos, psk, PMK_LEN) < 0)
- goto fail;
- psk_set = 1;
- }
-
- pos = os_strstr(cmd, " group_id=");
- if (pos) {
- size_t group_id_len;
-
- pos += 10;
- end = os_strchr(pos, ' ');
- group_id_len = end ? (size_t) (end - pos) : os_strlen(pos);
- group_id = os_malloc(group_id_len + 1);
- if (!group_id)
- goto fail;
- os_memcpy(group_id, pos, group_id_len);
- group_id[group_id_len] = '\0';
- }
-
- if (os_strstr(cmd, " conf=sta-")) {
- conf_sta = os_zalloc(sizeof(struct dpp_configuration));
- if (!conf_sta)
- goto fail;
- os_memcpy(conf_sta->ssid, ssid, ssid_len);
- conf_sta->ssid_len = ssid_len;
- if (os_strstr(cmd, " conf=sta-psk") ||
- os_strstr(cmd, " conf=sta-sae") ||
- os_strstr(cmd, " conf=sta-psk-sae")) {
- if (os_strstr(cmd, " conf=sta-psk-sae"))
- conf_sta->akm = DPP_AKM_PSK_SAE;
- else if (os_strstr(cmd, " conf=sta-sae"))
- conf_sta->akm = DPP_AKM_SAE;
- else
- conf_sta->akm = DPP_AKM_PSK;
- if (psk_set) {
- os_memcpy(conf_sta->psk, psk, PMK_LEN);
- } else {
- conf_sta->passphrase = os_strdup(pass);
- if (!conf_sta->passphrase)
- goto fail;
- }
- } else if (os_strstr(cmd, " conf=sta-dpp")) {
- conf_sta->akm = DPP_AKM_DPP;
- } else {
- goto fail;
- }
- if (os_strstr(cmd, " group_id=")) {
- conf_sta->group_id = group_id;
- group_id = NULL;
- }
- }
-
- if (os_strstr(cmd, " conf=ap-")) {
- conf_ap = os_zalloc(sizeof(struct dpp_configuration));
- if (!conf_ap)
- goto fail;
- os_memcpy(conf_ap->ssid, ssid, ssid_len);
- conf_ap->ssid_len = ssid_len;
- if (os_strstr(cmd, " conf=ap-psk") ||
- os_strstr(cmd, " conf=ap-sae") ||
- os_strstr(cmd, " conf=ap-psk-sae")) {
- if (os_strstr(cmd, " conf=ap-psk-sae"))
- conf_ap->akm = DPP_AKM_PSK_SAE;
- else if (os_strstr(cmd, " conf=ap-sae"))
- conf_ap->akm = DPP_AKM_SAE;
- else
- conf_ap->akm = DPP_AKM_PSK;
- if (psk_set) {
- os_memcpy(conf_ap->psk, psk, PMK_LEN);
- } else if (pass_len > 0) {
- conf_ap->passphrase = os_strdup(pass);
- if (!conf_ap->passphrase)
- goto fail;
- } else {
- goto fail;
- }
- } else if (os_strstr(cmd, " conf=ap-dpp")) {
- conf_ap->akm = DPP_AKM_DPP;
- } else {
- goto fail;
- }
- if (os_strstr(cmd, " group_id=")) {
- conf_ap->group_id = group_id;
- group_id = NULL;
- }
- }
-
- pos = os_strstr(cmd, " expiry=");
- if (pos) {
- long int val;
-
- pos += 8;
- val = strtol(pos, NULL, 0);
- if (val <= 0)
- goto fail;
- if (conf_sta)
- conf_sta->netaccesskey_expiry = val;
- if (conf_ap)
- conf_ap->netaccesskey_expiry = val;
- }
-
- pos = os_strstr(cmd, " configurator=");
- if (pos) {
- auth->configurator = 1;
- pos += 14;
- conf = hostapd_dpp_configurator_get_id(hapd, atoi(pos));
- if (!conf) {
- wpa_printf(MSG_INFO,
- "DPP: Could not find the specified configurator");
- goto fail;
- }
- }
- auth->conf_sta = conf_sta;
- auth->conf_ap = conf_ap;
- auth->conf = conf;
- os_free(group_id);
- return 0;
-
-fail:
- wpa_msg(hapd->msg_ctx, MSG_INFO,
- "DPP: Failed to set configurator parameters");
- dpp_configuration_free(conf_sta);
- dpp_configuration_free(conf_ap);
- os_free(group_id);
- return -1;
-}
-
-
static void hostapd_dpp_init_timeout(void *eloop_ctx, void *timeout_ctx)
{
struct hostapd_data *hapd = eloop_ctx;
@@ -786,7 +404,7 @@ int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd)
if (!pos)
return -1;
pos += 6;
- peer_bi = dpp_bootstrap_get_id(hapd, atoi(pos));
+ peer_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, atoi(pos));
if (!peer_bi) {
wpa_printf(MSG_INFO,
"DPP: Could not find bootstrapping info for the identified peer");
@@ -796,7 +414,8 @@ int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd)
pos = os_strstr(cmd, " own=");
if (pos) {
pos += 5;
- own_bi = dpp_bootstrap_get_id(hapd, atoi(pos));
+ own_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp,
+ atoi(pos));
if (!own_bi) {
wpa_printf(MSG_INFO,
"DPP: Could not find bootstrapping info for the identified local entry");
@@ -846,7 +465,8 @@ int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd)
if (!hapd->dpp_auth)
goto fail;
hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth);
- if (hostapd_dpp_set_configurator(hapd, hapd->dpp_auth, cmd) < 0) {
+ if (dpp_set_configurator(hapd->iface->interfaces->dpp, hapd->msg_ctx,
+ hapd->dpp_auth, cmd) < 0) {
dpp_auth_deinit(hapd->dpp_auth);
hapd->dpp_auth = NULL;
goto fail;
@@ -905,7 +525,10 @@ static void hostapd_dpp_rx_auth_req(struct hostapd_data *hapd, const u8 *src,
{
const u8 *r_bootstrap, *i_bootstrap;
u16 r_bootstrap_len, i_bootstrap_len;
- struct dpp_bootstrap_info *bi, *own_bi = NULL, *peer_bi = NULL;
+ struct dpp_bootstrap_info *own_bi = NULL, *peer_bi = NULL;
+
+ if (!hapd->iface->interfaces->dpp)
+ return;
wpa_printf(MSG_DEBUG, "DPP: Authentication Request from " MACSTR,
MAC2STR(src));
@@ -932,28 +555,8 @@ static void hostapd_dpp_rx_auth_req(struct hostapd_data *hapd, const u8 *src,
/* Try to find own and peer bootstrapping key matches based on the
* received hash values */
- dl_list_for_each(bi, &hapd->iface->interfaces->dpp_bootstrap,
- struct dpp_bootstrap_info, list) {
- if (!own_bi && bi->own &&
- os_memcmp(bi->pubkey_hash, r_bootstrap,
- SHA256_MAC_LEN) == 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Found matching own bootstrapping information");
- own_bi = bi;
- }
-
- if (!peer_bi && !bi->own &&
- os_memcmp(bi->pubkey_hash, i_bootstrap,
- SHA256_MAC_LEN) == 0) {
- wpa_printf(MSG_DEBUG,
- "DPP: Found matching peer bootstrapping information");
- peer_bi = bi;
- }
-
- if (own_bi && peer_bi)
- break;
- }
-
+ dpp_bootstrap_find_pair(hapd->iface->interfaces->dpp, i_bootstrap,
+ r_bootstrap, &own_bi, &peer_bi);
if (!own_bi) {
wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_FAIL
"No matching own bootstrapping key found - ignore message");
@@ -975,8 +578,9 @@ static void hostapd_dpp_rx_auth_req(struct hostapd_data *hapd, const u8 *src,
return;
}
hostapd_dpp_set_testing_options(hapd, hapd->dpp_auth);
- if (hostapd_dpp_set_configurator(hapd, hapd->dpp_auth,
- hapd->dpp_configurator_params) < 0) {
+ if (dpp_set_configurator(hapd->iface->interfaces->dpp, hapd->msg_ctx,
+ hapd->dpp_auth,
+ hapd->dpp_configurator_params) < 0) {
dpp_auth_deinit(hapd->dpp_auth);
hapd->dpp_auth = NULL;
return;
@@ -1072,6 +676,7 @@ static void hostapd_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
struct hostapd_data *hapd = ctx;
const u8 *pos;
struct dpp_authentication *auth = hapd->dpp_auth;
+ enum dpp_status_error status = DPP_STATUS_CONFIG_REJECTED;
if (!auth || !auth->auth_success) {
wpa_printf(MSG_DEBUG, "DPP: No matching exchange in progress");
@@ -1107,12 +712,41 @@ static void hostapd_dpp_gas_resp_cb(void *ctx, const u8 *addr, u8 dialog_token,
}
hostapd_dpp_handle_config_obj(hapd, auth);
- dpp_auth_deinit(hapd->dpp_auth);
- hapd->dpp_auth = NULL;
- return;
-
+ status = DPP_STATUS_OK;
+#ifdef CONFIG_TESTING_OPTIONS
+ if (dpp_test == DPP_TEST_REJECT_CONFIG) {
+ wpa_printf(MSG_INFO, "DPP: TESTING - Reject Config Object");
+ status = DPP_STATUS_CONFIG_REJECTED;
+ }
+#endif /* CONFIG_TESTING_OPTIONS */
fail:
- wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+ if (status != DPP_STATUS_OK)
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+#ifdef CONFIG_DPP2
+ if (auth->peer_version >= 2 &&
+ auth->conf_resp_status == DPP_STATUS_OK) {
+ struct wpabuf *msg;
+
+ wpa_printf(MSG_DEBUG, "DPP: Send DPP Configuration Result");
+ msg = dpp_build_conf_result(auth, status);
+ if (!msg)
+ goto fail2;
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO,
+ DPP_EVENT_TX "dst=" MACSTR " freq=%u type=%d",
+ MAC2STR(addr), auth->curr_freq,
+ DPP_PA_CONFIGURATION_RESULT);
+ hostapd_drv_send_action(hapd, auth->curr_freq, 0,
+ addr, wpabuf_head(msg),
+ wpabuf_len(msg));
+ wpabuf_free(msg);
+
+ /* This exchange will be terminated in the TX status handler */
+ auth->connect_on_tx_status = 1;
+ return;
+ }
+fail2:
+#endif /* CONFIG_DPP2 */
dpp_auth_deinit(hapd->dpp_auth);
hapd->dpp_auth = NULL;
}
@@ -1121,7 +755,7 @@ fail:
static void hostapd_dpp_start_gas_client(struct hostapd_data *hapd)
{
struct dpp_authentication *auth = hapd->dpp_auth;
- struct wpabuf *buf, *conf_req;
+ struct wpabuf *buf;
char json[100];
int res;
int netrole_ap = 1;
@@ -1133,34 +767,13 @@ static void hostapd_dpp_start_gas_client(struct hostapd_data *hapd)
netrole_ap ? "ap" : "sta");
wpa_printf(MSG_DEBUG, "DPP: GAS Config Attributes: %s", json);
- conf_req = dpp_build_conf_req(auth, json);
- if (!conf_req) {
+ buf = dpp_build_conf_req(auth, json);
+ if (!buf) {
wpa_printf(MSG_DEBUG,
"DPP: No configuration request data available");
return;
}
- buf = gas_build_initial_req(0, 10 + 2 + wpabuf_len(conf_req));
- if (!buf) {
- wpabuf_free(conf_req);
- return;
- }
-
- /* Advertisement Protocol IE */
- wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
- wpabuf_put_u8(buf, 8); /* Length */
- wpabuf_put_u8(buf, 0x7f);
- wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
- wpabuf_put_u8(buf, 5);
- wpabuf_put_be24(buf, OUI_WFA);
- wpabuf_put_u8(buf, DPP_OUI_TYPE);
- wpabuf_put_u8(buf, 0x01);
-
- /* GAS Query */
- wpabuf_put_le16(buf, wpabuf_len(conf_req));
- wpabuf_put_buf(buf, conf_req);
- wpabuf_free(conf_req);
-
wpa_printf(MSG_DEBUG, "DPP: GAS request to " MACSTR " (freq %u MHz)",
MAC2STR(auth->peer_mac_addr), auth->curr_freq);
@@ -1281,6 +894,63 @@ static void hostapd_dpp_rx_auth_conf(struct hostapd_data *hapd, const u8 *src,
}
+#ifdef CONFIG_DPP2
+
+static void hostapd_dpp_config_result_wait_timeout(void *eloop_ctx,
+ void *timeout_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct dpp_authentication *auth = hapd->dpp_auth;
+
+ if (!auth || !auth->waiting_conf_result)
+ return;
+
+ wpa_printf(MSG_DEBUG,
+ "DPP: Timeout while waiting for Configuration Result");
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+ dpp_auth_deinit(auth);
+ hapd->dpp_auth = NULL;
+}
+
+
+static void hostapd_dpp_rx_conf_result(struct hostapd_data *hapd, const u8 *src,
+ const u8 *hdr, const u8 *buf, size_t len)
+{
+ struct dpp_authentication *auth = hapd->dpp_auth;
+ enum dpp_status_error status;
+
+ wpa_printf(MSG_DEBUG, "DPP: Configuration Result from " MACSTR,
+ MAC2STR(src));
+
+ if (!auth || !auth->waiting_conf_result) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No DPP Configuration waiting for result - drop");
+ return;
+ }
+
+ if (os_memcmp(src, auth->peer_mac_addr, ETH_ALEN) != 0) {
+ wpa_printf(MSG_DEBUG, "DPP: MAC address mismatch (expected "
+ MACSTR ") - drop", MAC2STR(auth->peer_mac_addr));
+ return;
+ }
+
+ status = dpp_conf_result_rx(auth, hdr, buf, len);
+
+ hostapd_drv_send_action_cancel_wait(hapd);
+ hostapd_dpp_listen_stop(hapd);
+ if (status == DPP_STATUS_OK)
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_SENT);
+ else
+ wpa_msg(hapd->msg_ctx, MSG_INFO, DPP_EVENT_CONF_FAILED);
+ dpp_auth_deinit(auth);
+ hapd->dpp_auth = NULL;
+ eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout, hapd,
+ NULL);
+}
+
+#endif /* CONFIG_DPP2 */
+
+
static void hostapd_dpp_send_peer_disc_resp(struct hostapd_data *hapd,
const u8 *src, unsigned int freq,
u8 trans_id,
@@ -1596,24 +1266,10 @@ hostapd_dpp_rx_pkex_commit_reveal_req(struct hostapd_data *hapd, const u8 *src,
wpabuf_head(msg), wpabuf_len(msg));
wpabuf_free(msg);
- bi = os_zalloc(sizeof(*bi));
+ bi = dpp_pkex_finish(hapd->iface->interfaces->dpp, pkex, src, freq);
if (!bi)
return;
- bi->id = hapd_dpp_next_id(hapd);
- bi->type = DPP_BOOTSTRAP_PKEX;
- os_memcpy(bi->mac_addr, src, ETH_ALEN);
- bi->num_freq = 1;
- bi->freq[0] = freq;
- bi->curve = pkex->own_bi->curve;
- bi->pubkey = pkex->peer_bootstrap_key;
- pkex->peer_bootstrap_key = NULL;
- dpp_pkex_free(pkex);
hapd->dpp_pkex = NULL;
- if (dpp_bootstrap_key_hash(bi) < 0) {
- dpp_bootstrap_info_free(bi);
- return;
- }
- dl_list_add(&hapd->iface->interfaces->dpp_bootstrap, &bi->list);
}
@@ -1623,7 +1279,7 @@ hostapd_dpp_rx_pkex_commit_reveal_resp(struct hostapd_data *hapd, const u8 *src,
unsigned int freq)
{
int res;
- struct dpp_bootstrap_info *bi, *own_bi;
+ struct dpp_bootstrap_info *bi;
struct dpp_pkex *pkex = hapd->dpp_pkex;
char cmd[500];
@@ -1641,26 +1297,10 @@ hostapd_dpp_rx_pkex_commit_reveal_resp(struct hostapd_data *hapd, const u8 *src,
return;
}
- own_bi = pkex->own_bi;
-
- bi = os_zalloc(sizeof(*bi));
+ bi = dpp_pkex_finish(hapd->iface->interfaces->dpp, pkex, src, freq);
if (!bi)
return;
- bi->id = hapd_dpp_next_id(hapd);
- bi->type = DPP_BOOTSTRAP_PKEX;
- os_memcpy(bi->mac_addr, src, ETH_ALEN);
- bi->num_freq = 1;
- bi->freq[0] = freq;
- bi->curve = own_bi->curve;
- bi->pubkey = pkex->peer_bootstrap_key;
- pkex->peer_bootstrap_key = NULL;
- dpp_pkex_free(pkex);
hapd->dpp_pkex = NULL;
- if (dpp_bootstrap_key_hash(bi) < 0) {
- dpp_bootstrap_info_free(bi);
- return;
- }
- dl_list_add(&hapd->iface->interfaces->dpp_bootstrap, &bi->list);
os_snprintf(cmd, sizeof(cmd), " peer=%u %s",
bi->id,
@@ -1744,6 +1384,11 @@ void hostapd_dpp_rx_action(struct hostapd_data *hapd, const u8 *src,
hostapd_dpp_rx_pkex_commit_reveal_resp(hapd, src, hdr, buf, len,
freq);
break;
+#ifdef CONFIG_DPP2
+ case DPP_PA_CONFIGURATION_RESULT:
+ hostapd_dpp_rx_conf_result(hapd, src, hdr, buf, len);
+ break;
+#endif /* CONFIG_DPP2 */
default:
wpa_printf(MSG_DEBUG,
"DPP: Ignored unsupported frame subtype %d", type);
@@ -1790,11 +1435,28 @@ hostapd_dpp_gas_req_handler(struct hostapd_data *hapd, const u8 *sa,
void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok)
{
- if (!hapd->dpp_auth)
+ struct dpp_authentication *auth = hapd->dpp_auth;
+
+ if (!auth)
return;
+ wpa_printf(MSG_DEBUG, "DPP: Configuration exchange completed (ok=%d)",
+ ok);
eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
+#ifdef CONFIG_DPP2
+ if (ok && auth->peer_version >= 2 &&
+ auth->conf_resp_status == DPP_STATUS_OK) {
+ wpa_printf(MSG_DEBUG, "DPP: Wait for Configuration Result");
+ auth->waiting_conf_result = 1;
+ eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout,
+ hapd, NULL);
+ eloop_register_timeout(2, 0,
+ hostapd_dpp_config_result_wait_timeout,
+ hapd, NULL);
+ return;
+ }
+#endif /* CONFIG_DPP2 */
hostapd_drv_send_action_cancel_wait(hapd);
if (ok)
@@ -1806,93 +1468,6 @@ void hostapd_dpp_gas_status_handler(struct hostapd_data *hapd, int ok)
}
-static unsigned int hostapd_dpp_next_configurator_id(struct hostapd_data *hapd)
-{
- struct dpp_configurator *conf;
- unsigned int max_id = 0;
-
- dl_list_for_each(conf, &hapd->iface->interfaces->dpp_configurator,
- struct dpp_configurator, list) {
- if (conf->id > max_id)
- max_id = conf->id;
- }
- return max_id + 1;
-}
-
-
-int hostapd_dpp_configurator_add(struct hostapd_data *hapd, const char *cmd)
-{
- char *curve = NULL;
- char *key = NULL;
- u8 *privkey = NULL;
- size_t privkey_len = 0;
- int ret = -1;
- struct dpp_configurator *conf = NULL;
-
- curve = get_param(cmd, " curve=");
- key = get_param(cmd, " key=");
-
- if (key) {
- privkey_len = os_strlen(key) / 2;
- privkey = os_malloc(privkey_len);
- if (!privkey ||
- hexstr2bin(key, privkey, privkey_len) < 0)
- goto fail;
- }
-
- conf = dpp_keygen_configurator(curve, privkey, privkey_len);
- if (!conf)
- goto fail;
-
- conf->id = hostapd_dpp_next_configurator_id(hapd);
- dl_list_add(&hapd->iface->interfaces->dpp_configurator, &conf->list);
- ret = conf->id;
- conf = NULL;
-fail:
- os_free(curve);
- str_clear_free(key);
- bin_clear_free(privkey, privkey_len);
- dpp_configurator_free(conf);
- return ret;
-}
-
-
-static int dpp_configurator_del(struct hapd_interfaces *ifaces, unsigned int id)
-{
- struct dpp_configurator *conf, *tmp;
- int found = 0;
-
- dl_list_for_each_safe(conf, tmp, &ifaces->dpp_configurator,
- struct dpp_configurator, list) {
- if (id && conf->id != id)
- continue;
- found = 1;
- dl_list_del(&conf->list);
- dpp_configurator_free(conf);
- }
-
- if (id == 0)
- return 0; /* flush succeeds regardless of entries found */
- return found ? 0 : -1;
-}
-
-
-int hostapd_dpp_configurator_remove(struct hostapd_data *hapd, const char *id)
-{
- unsigned int id_val;
-
- if (os_strcmp(id, "*") == 0) {
- id_val = 0;
- } else {
- id_val = atoi(id);
- if (id_val == 0)
- return -1;
- }
-
- return dpp_configurator_del(hapd->iface->interfaces, id_val);
-}
-
-
int hostapd_dpp_configurator_sign(struct hostapd_data *hapd, const char *cmd)
{
struct dpp_authentication *auth;
@@ -1905,7 +1480,8 @@ int hostapd_dpp_configurator_sign(struct hostapd_data *hapd, const char *cmd)
curve = get_param(cmd, " curve=");
hostapd_dpp_set_testing_options(hapd, auth);
- if (hostapd_dpp_set_configurator(hapd, auth, cmd) == 0 &&
+ if (dpp_set_configurator(hapd->iface->interfaces->dpp, hapd->msg_ctx,
+ auth, cmd) == 0 &&
dpp_configurator_own_config(auth, curve, 1) == 0) {
hostapd_dpp_handle_config_obj(hapd, auth);
ret = 0;
@@ -1918,19 +1494,6 @@ int hostapd_dpp_configurator_sign(struct hostapd_data *hapd, const char *cmd)
}
-int hostapd_dpp_configurator_get_key(struct hostapd_data *hapd, unsigned int id,
- char *buf, size_t buflen)
-{
- struct dpp_configurator *conf;
-
- conf = hostapd_dpp_configurator_get_id(hapd, id);
- if (!conf)
- return -1;
-
- return dpp_configurator_get_key(conf, buf, buflen);
-}
-
-
int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd)
{
struct dpp_bootstrap_info *own_bi;
@@ -1940,7 +1503,7 @@ int hostapd_dpp_pkex_add(struct hostapd_data *hapd, const char *cmd)
if (!pos)
return -1;
pos += 5;
- own_bi = dpp_bootstrap_get_id(hapd, atoi(pos));
+ own_bi = dpp_bootstrap_get_id(hapd->iface->interfaces->dpp, atoi(pos));
if (!own_bi) {
wpa_printf(MSG_DEBUG,
"DPP: Identified bootstrap info not found");
@@ -2070,6 +1633,10 @@ void hostapd_dpp_deinit(struct hostapd_data *hapd)
eloop_cancel_timeout(hostapd_dpp_reply_wait_timeout, hapd, NULL);
eloop_cancel_timeout(hostapd_dpp_init_timeout, hapd, NULL);
eloop_cancel_timeout(hostapd_dpp_auth_resp_retry_timeout, hapd, NULL);
+#ifdef CONFIG_DPP2
+ eloop_cancel_timeout(hostapd_dpp_config_result_wait_timeout, hapd,
+ NULL);
+#endif /* CONFIG_DPP2 */
dpp_auth_deinit(hapd->dpp_auth);
hapd->dpp_auth = NULL;
hostapd_dpp_pkex_remove(hapd, "*");
@@ -2077,20 +1644,3 @@ void hostapd_dpp_deinit(struct hostapd_data *hapd)
os_free(hapd->dpp_configurator_params);
hapd->dpp_configurator_params = NULL;
}
-
-
-void hostapd_dpp_init_global(struct hapd_interfaces *ifaces)
-{
- dl_list_init(&ifaces->dpp_bootstrap);
- dl_list_init(&ifaces->dpp_configurator);
- ifaces->dpp_init_done = 1;
-}
-
-
-void hostapd_dpp_deinit_global(struct hapd_interfaces *ifaces)
-{
- if (!ifaces->dpp_init_done)
- return;
- dpp_bootstrap_del(ifaces, 0);
- dpp_configurator_del(ifaces, 0);
-}
diff --git a/src/ap/dpp_hostapd.h b/src/ap/dpp_hostapd.h
index 3ef7c14567e88..449ca16d118ff 100644
--- a/src/ap/dpp_hostapd.h
+++ b/src/ap/dpp_hostapd.h
@@ -10,12 +10,6 @@
#define DPP_HOSTAPD_H
int hostapd_dpp_qr_code(struct hostapd_data *hapd, const char *cmd);
-int hostapd_dpp_bootstrap_gen(struct hostapd_data *hapd, const char *cmd);
-int hostapd_dpp_bootstrap_remove(struct hostapd_data *hapd, const char *id);
-const char * hostapd_dpp_bootstrap_get_uri(struct hostapd_data *hapd,
- unsigned int id);
-int hostapd_dpp_bootstrap_info(struct hostapd_data *hapd, int id,
- char *reply, int reply_size);
int hostapd_dpp_auth_init(struct hostapd_data *hapd, const char *cmd);
int hostapd_dpp_listen(struct hostapd_data *hapd, const char *cmd);
void hostapd_dpp_listen_stop(struct hostapd_data *hapd);
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index a726a6ff87e01..8ddf754f60a74 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -15,6 +15,7 @@
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
#include "common/wpa_ctrl.h"
+#include "common/dpp.h"
#include "crypto/random.h"
#include "p2p/p2p.h"
#include "wps/wps.h"
@@ -38,6 +39,7 @@
#include "mbo_ap.h"
#include "dpp_hostapd.h"
#include "fils_hlp.h"
+#include "neighbor_db.h"
#ifdef CONFIG_FILS
@@ -304,6 +306,7 @@ int hostapd_notif_assoc(struct hostapd_data *hapd, const u8 *addr,
return -1;
}
res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
+ hapd->iface->freq,
ie, ielen,
elems.mdie, elems.mdie_len,
elems.owe_dh, elems.owe_dh_len);
@@ -563,6 +566,38 @@ skip_wpa_check:
}
#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP2
+ dpp_pfs_free(sta->dpp_pfs);
+ sta->dpp_pfs = NULL;
+
+ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) &&
+ hapd->conf->dpp_netaccesskey && sta->wpa_sm &&
+ wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP &&
+ elems.owe_dh) {
+ sta->dpp_pfs = dpp_pfs_init(
+ wpabuf_head(hapd->conf->dpp_netaccesskey),
+ wpabuf_len(hapd->conf->dpp_netaccesskey));
+ if (!sta->dpp_pfs) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not initialize PFS");
+ /* Try to continue without PFS */
+ goto pfs_fail;
+ }
+
+ if (dpp_pfs_process(sta->dpp_pfs, elems.owe_dh,
+ elems.owe_dh_len) < 0) {
+ dpp_pfs_free(sta->dpp_pfs);
+ sta->dpp_pfs = NULL;
+ reason = WLAN_REASON_UNSPECIFIED;
+ goto fail;
+ }
+ }
+
+ wpa_auth_set_dpp_z(sta->wpa_sm, sta->dpp_pfs ?
+ sta->dpp_pfs->secret : NULL);
+ pfs_fail:
+#endif /* CONFIG_DPP2 */
+
#if defined(CONFIG_IEEE80211R_AP) || defined(CONFIG_FILS) || defined(CONFIG_OWE)
hostapd_sta_assoc(hapd, addr, reassoc, status, buf, p - buf);
@@ -739,9 +774,12 @@ void hostapd_event_sta_opmode_changed(struct hostapd_data *hapd, const u8 *addr,
void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
int offset, int width, int cf1, int cf2)
{
+ /* TODO: If OCV is enabled deauth STAs that don't perform a SA Query */
+
#ifdef NEED_AP_MLME
int channel, chwidth, is_dfs;
u8 seg0_idx = 0, seg1_idx = 0;
+ size_t i;
hostapd_logger(hapd, NULL, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_INFO,
@@ -824,6 +862,9 @@ void hostapd_event_ch_switch(struct hostapd_data *hapd, int freq, int ht,
wpa_msg(hapd->msg_ctx, MSG_INFO, AP_CSA_FINISHED
"freq=%d dfs=%d", freq, is_dfs);
}
+
+ for (i = 0; i < hapd->iface->num_bss; i++)
+ hostapd_neighbor_set_own_report(hapd->iface->bss[i]);
#endif /* NEED_AP_MLME */
}
@@ -1065,6 +1106,7 @@ fail:
}
+#ifndef NEED_AP_MLME
static void hostapd_action_rx(struct hostapd_data *hapd,
struct rx_mgmt *drv_mgmt)
{
@@ -1077,7 +1119,7 @@ static void hostapd_action_rx(struct hostapd_data *hapd,
if (drv_mgmt->frame_len < IEEE80211_HDRLEN + 2 + 1)
return;
- plen = drv_mgmt->frame_len - IEEE80211_HDRLEN - 1;
+ plen = drv_mgmt->frame_len - IEEE80211_HDRLEN;
mgmt = (struct ieee80211_mgmt *) drv_mgmt->frame;
fc = le_to_host16(mgmt->frame_control);
@@ -1097,22 +1139,20 @@ static void hostapd_action_rx(struct hostapd_data *hapd,
}
#ifdef CONFIG_IEEE80211R_AP
if (mgmt->u.action.category == WLAN_ACTION_FT) {
- const u8 *payload = drv_mgmt->frame + 24 + 1;
-
- wpa_ft_action_rx(sta->wpa_sm, payload, plen);
+ wpa_ft_action_rx(sta->wpa_sm, (u8 *) &mgmt->u.action, plen);
+ return;
}
#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_IEEE80211W
- if (mgmt->u.action.category == WLAN_ACTION_SA_QUERY && plen >= 4) {
- ieee802_11_sa_query_action(
- hapd, mgmt->sa,
- mgmt->u.action.u.sa_query_resp.action,
- mgmt->u.action.u.sa_query_resp.trans_id);
+ if (mgmt->u.action.category == WLAN_ACTION_SA_QUERY) {
+ ieee802_11_sa_query_action(hapd, mgmt, drv_mgmt->frame_len);
+ return;
}
#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_WNM_AP
if (mgmt->u.action.category == WLAN_ACTION_WNM) {
ieee802_11_rx_wnm_action_ap(hapd, mgmt, drv_mgmt->frame_len);
+ return;
}
#endif /* CONFIG_WNM_AP */
#ifdef CONFIG_FST
@@ -1122,7 +1162,7 @@ static void hostapd_action_rx(struct hostapd_data *hapd,
}
#endif /* CONFIG_FST */
#ifdef CONFIG_DPP
- if (plen >= 1 + 4 &&
+ if (plen >= 2 + 4 &&
mgmt->u.action.u.vs_public_action.action ==
WLAN_PA_VENDOR_SPECIFIC &&
WPA_GET_BE24(mgmt->u.action.u.vs_public_action.oui) ==
@@ -1139,6 +1179,7 @@ static void hostapd_action_rx(struct hostapd_data *hapd,
}
#endif /* CONFIG_DPP */
}
+#endif /* NEED_AP_MLME */
#ifdef NEED_AP_MLME
@@ -1600,10 +1641,10 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
if (!data->rx_mgmt.frame)
break;
#ifdef NEED_AP_MLME
- if (hostapd_mgmt_rx(hapd, &data->rx_mgmt) > 0)
- break;
-#endif /* NEED_AP_MLME */
+ hostapd_mgmt_rx(hapd, &data->rx_mgmt);
+#else /* NEED_AP_MLME */
hostapd_action_rx(hapd, &data->rx_mgmt);
+#endif /* NEED_AP_MLME */
break;
case EVENT_RX_PROBE_REQ:
if (data->rx_probe_req.sa == NULL ||
@@ -1725,6 +1766,11 @@ void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
hostapd_reconfig_encryption(hapd);
hapd->reenable_beacon = 1;
ieee802_11_set_beacon(hapd);
+#ifdef NEED_AP_MLME
+ } else if (hapd->disabled && hapd->iface->cac_started) {
+ wpa_printf(MSG_DEBUG, "DFS: restarting pending CAC");
+ hostapd_handle_dfs(hapd->iface);
+#endif /* NEED_AP_MLME */
}
break;
case EVENT_INTERFACE_DISABLED:
diff --git a/src/ap/eap_user_db.c b/src/ap/eap_user_db.c
index 296d5c2ddf311..a510ee3e29fdc 100644
--- a/src/ap/eap_user_db.c
+++ b/src/ap/eap_user_db.c
@@ -139,6 +139,7 @@ eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity,
struct hostapd_eap_user *user = NULL;
char id_str[256], cmd[300];
size_t i;
+ int res;
if (identity_len >= sizeof(id_str)) {
wpa_printf(MSG_DEBUG, "%s: identity len too big: %d >= %d",
@@ -174,6 +175,7 @@ eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity,
if (hapd->tmp_eap_user.identity == NULL)
return NULL;
os_memcpy(hapd->tmp_eap_user.identity, identity, identity_len);
+ hapd->tmp_eap_user.identity_len = identity_len;
if (sqlite3_open(hapd->conf->eap_user_sqlite, &db)) {
wpa_printf(MSG_INFO, "DB: Failed to open database %s: %s",
@@ -182,9 +184,12 @@ eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity,
return NULL;
}
- os_snprintf(cmd, sizeof(cmd),
- "SELECT * FROM users WHERE identity='%s' AND phase2=%d;",
- id_str, phase2);
+ res = os_snprintf(cmd, sizeof(cmd),
+ "SELECT * FROM users WHERE identity='%s' AND phase2=%d;",
+ id_str, phase2);
+ if (os_snprintf_error(sizeof(cmd), res))
+ goto fail;
+
wpa_printf(MSG_DEBUG, "DB: %s", cmd);
if (sqlite3_exec(db, cmd, get_user_cb, &hapd->tmp_eap_user, NULL) !=
SQLITE_OK) {
@@ -214,6 +219,7 @@ eap_user_sqlite_get(struct hostapd_data *hapd, const u8 *identity,
}
}
+fail:
sqlite3_close(db);
return user;
diff --git a/src/ap/fils_hlp.c b/src/ap/fils_hlp.c
index 2a359ab03c818..6da514a4d0fb4 100644
--- a/src/ap/fils_hlp.c
+++ b/src/ap/fils_hlp.c
@@ -580,6 +580,19 @@ int fils_process_hlp(struct hostapd_data *hapd, struct sta_info *sta,
u8 *tmp, *tmp_pos;
int ret = 0;
+ if (sta->fils_pending_assoc_req &&
+ eloop_is_timeout_registered(fils_hlp_timeout, hapd, sta)) {
+ /* Do not process FILS HLP request again if the station
+ * retransmits (Re)Association Request frame before the previous
+ * HLP response has either been received or timed out. */
+ wpa_printf(MSG_DEBUG,
+ "FILS: Do not relay another HLP request from "
+ MACSTR
+ " before processing of the already pending one has been completed",
+ MAC2STR(sta->addr));
+ return 1;
+ }
+
/* Old DHCPDISCOVER is not needed anymore, if it was still pending */
wpabuf_free(sta->hlp_dhcp_discover);
sta->hlp_dhcp_discover = NULL;
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 7501bac6e42a1..20c8e8f5a4f71 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -1,6 +1,6 @@
/*
* hostapd / Initialization and configuration
- * Copyright (c) 2002-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -348,10 +348,11 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd)
if (!hapd->started) {
wpa_printf(MSG_ERROR, "%s: Interface %s wasn't started",
- __func__, hapd->conf->iface);
+ __func__, hapd->conf ? hapd->conf->iface : "N/A");
return;
}
hapd->started = 0;
+ hapd->beacon_set_done = 0;
wpa_printf(MSG_DEBUG, "%s(%s)", __func__, hapd->conf->iface);
iapp_deinit(hapd->iapp);
@@ -417,6 +418,20 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd)
hostapd_clean_rrm(hapd);
fils_hlp_deinit(hapd);
+
+#ifdef CONFIG_SAE
+ {
+ struct hostapd_sae_commit_queue *q;
+
+ while ((q = dl_list_first(&hapd->sae_commit_queue,
+ struct hostapd_sae_commit_queue,
+ list))) {
+ dl_list_del(&q->list);
+ os_free(q);
+ }
+ }
+ eloop_cancel_timeout(auth_sae_process_commit, hapd, NULL);
+#endif /* CONFIG_SAE */
}
@@ -431,7 +446,7 @@ static void hostapd_free_hapd_data(struct hostapd_data *hapd)
static void hostapd_cleanup(struct hostapd_data *hapd)
{
wpa_printf(MSG_DEBUG, "%s(hapd=%p (%s))", __func__, hapd,
- hapd->conf->iface);
+ hapd->conf ? hapd->conf->iface : "N/A");
if (hapd->iface->interfaces &&
hapd->iface->interfaces->ctrl_iface_deinit) {
wpa_msg(hapd->msg_ctx, MSG_INFO, WPA_EVENT_TERMINATING);
@@ -506,7 +521,7 @@ static void hostapd_cleanup_iface(struct hostapd_iface *iface)
static void hostapd_clear_wep(struct hostapd_data *hapd)
{
- if (hapd->drv_priv && !hapd->iface->driver_ap_teardown) {
+ if (hapd->drv_priv && !hapd->iface->driver_ap_teardown && hapd->conf) {
hostapd_set_privacy(hapd, 0);
hostapd_broadcast_wep_clear(hapd);
}
@@ -658,8 +673,10 @@ static int hostapd_validate_bssid_configuration(struct hostapd_iface *iface)
for (i = 5; i > 5 - j; i--)
mask[i] = 0;
j = bits % 8;
- while (j--)
+ while (j) {
+ j--;
mask[i] <<= 1;
+ }
skip_mask_ext:
wpa_printf(MSG_DEBUG, "BSS count %lu, BSSID mask " MACSTR " (%d bits)",
@@ -1668,127 +1685,6 @@ void fst_hostapd_fill_iface_obj(struct hostapd_data *hapd,
#endif /* CONFIG_FST */
-
-#ifdef NEED_AP_MLME
-static enum nr_chan_width hostapd_get_nr_chan_width(struct hostapd_data *hapd,
- int ht, int vht)
-{
- if (!ht && !vht)
- return NR_CHAN_WIDTH_20;
- if (!hapd->iconf->secondary_channel)
- return NR_CHAN_WIDTH_20;
- if (!vht || hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_USE_HT)
- return NR_CHAN_WIDTH_40;
- if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80MHZ)
- return NR_CHAN_WIDTH_80;
- if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_160MHZ)
- return NR_CHAN_WIDTH_160;
- if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80P80MHZ)
- return NR_CHAN_WIDTH_80P80;
- return NR_CHAN_WIDTH_20;
-}
-#endif /* NEED_AP_MLME */
-
-
-static void hostapd_set_own_neighbor_report(struct hostapd_data *hapd)
-{
-#ifdef NEED_AP_MLME
- u16 capab = hostapd_own_capab_info(hapd);
- int ht = hapd->iconf->ieee80211n && !hapd->conf->disable_11n;
- int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac;
- struct wpa_ssid_value ssid;
- u8 channel, op_class;
- u8 center_freq1_idx = 0, center_freq2_idx = 0;
- enum nr_chan_width width;
- u32 bssid_info;
- struct wpabuf *nr;
-
- if (!(hapd->conf->radio_measurements[0] &
- WLAN_RRM_CAPS_NEIGHBOR_REPORT))
- return;
-
- bssid_info = 3; /* AP is reachable */
- bssid_info |= NEI_REP_BSSID_INFO_SECURITY; /* "same as the AP" */
- bssid_info |= NEI_REP_BSSID_INFO_KEY_SCOPE; /* "same as the AP" */
-
- if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT)
- bssid_info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT;
-
- bssid_info |= NEI_REP_BSSID_INFO_RM; /* RRM is supported */
-
- if (hapd->conf->wmm_enabled) {
- bssid_info |= NEI_REP_BSSID_INFO_QOS;
-
- if (hapd->conf->wmm_uapsd &&
- (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD))
- bssid_info |= NEI_REP_BSSID_INFO_APSD;
- }
-
- if (ht) {
- bssid_info |= NEI_REP_BSSID_INFO_HT |
- NEI_REP_BSSID_INFO_DELAYED_BA;
-
- /* VHT bit added in IEEE P802.11-REVmc/D4.3 */
- if (vht)
- bssid_info |= NEI_REP_BSSID_INFO_VHT;
- }
-
- /* TODO: Set NEI_REP_BSSID_INFO_MOBILITY_DOMAIN if MDE is set */
-
- if (ieee80211_freq_to_channel_ext(hapd->iface->freq,
- hapd->iconf->secondary_channel,
- hapd->iconf->vht_oper_chwidth,
- &op_class, &channel) ==
- NUM_HOSTAPD_MODES)
- return;
- width = hostapd_get_nr_chan_width(hapd, ht, vht);
- if (vht) {
- center_freq1_idx = hapd->iconf->vht_oper_centr_freq_seg0_idx;
- if (width == NR_CHAN_WIDTH_80P80)
- center_freq2_idx =
- hapd->iconf->vht_oper_centr_freq_seg1_idx;
- } else if (ht) {
- ieee80211_freq_to_chan(hapd->iface->freq +
- 10 * hapd->iconf->secondary_channel,
- &center_freq1_idx);
- }
-
- ssid.ssid_len = hapd->conf->ssid.ssid_len;
- os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len);
-
- /*
- * Neighbor Report element size = BSSID + BSSID info + op_class + chan +
- * phy type + wide bandwidth channel subelement.
- */
- nr = wpabuf_alloc(ETH_ALEN + 4 + 1 + 1 + 1 + 5);
- if (!nr)
- return;
-
- wpabuf_put_data(nr, hapd->own_addr, ETH_ALEN);
- wpabuf_put_le32(nr, bssid_info);
- wpabuf_put_u8(nr, op_class);
- wpabuf_put_u8(nr, channel);
- wpabuf_put_u8(nr, ieee80211_get_phy_type(hapd->iface->freq, ht, vht));
-
- /*
- * Wide Bandwidth Channel subelement may be needed to allow the
- * receiving STA to send packets to the AP. See IEEE P802.11-REVmc/D5.0
- * Figure 9-301.
- */
- wpabuf_put_u8(nr, WNM_NEIGHBOR_WIDE_BW_CHAN);
- wpabuf_put_u8(nr, 3);
- wpabuf_put_u8(nr, width);
- wpabuf_put_u8(nr, center_freq1_idx);
- wpabuf_put_u8(nr, center_freq2_idx);
-
- hostapd_neighbor_set(hapd, hapd->own_addr, &ssid, nr, hapd->iconf->lci,
- hapd->iconf->civic, hapd->iconf->stationary_ap);
-
- wpabuf_free(nr);
-#endif /* NEED_AP_MLME */
-}
-
-
#ifdef CONFIG_OWE
static int hostapd_owe_iface_iter(struct hostapd_iface *iface, void *ctx)
@@ -1988,15 +1884,17 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
}
}
- if (hapd->iconf->rts_threshold > -1 &&
- hostapd_set_rts(hapd, hapd->iconf->rts_threshold)) {
+ if (hapd->iconf->rts_threshold >= -1 &&
+ hostapd_set_rts(hapd, hapd->iconf->rts_threshold) &&
+ hapd->iconf->rts_threshold >= -1) {
wpa_printf(MSG_ERROR, "Could not set RTS threshold for "
"kernel driver");
goto fail;
}
- if (hapd->iconf->fragm_threshold > -1 &&
- hostapd_set_frag(hapd, hapd->iconf->fragm_threshold)) {
+ if (hapd->iconf->fragm_threshold >= -1 &&
+ hostapd_set_frag(hapd, hapd->iconf->fragm_threshold) &&
+ hapd->iconf->fragm_threshold != -1) {
wpa_printf(MSG_ERROR, "Could not set fragmentation threshold "
"for kernel driver");
goto fail;
@@ -2009,11 +1907,14 @@ static int hostapd_setup_interface_complete_sync(struct hostapd_iface *iface,
if (j)
os_memcpy(hapd->own_addr, prev_addr, ETH_ALEN);
if (hostapd_setup_bss(hapd, j == 0)) {
- do {
+ for (;;) {
hapd = iface->bss[j];
hostapd_bss_deinit_no_free(hapd);
hostapd_free_hapd_data(hapd);
- } while (j-- > 0);
+ if (j == 0)
+ break;
+ j--;
+ }
goto fail;
}
if (is_zero_ether_addr(hapd->conf->bssid))
@@ -2085,7 +1986,7 @@ dfs_offload:
iface->interfaces->terminate_on_error--;
for (j = 0; j < iface->num_bss; j++)
- hostapd_set_own_neighbor_report(iface->bss[j]);
+ hostapd_neighbor_set_own_report(iface->bss[j]);
return 0;
@@ -2266,6 +2167,9 @@ hostapd_alloc_bss_data(struct hostapd_iface *hapd_iface,
dl_list_init(&hapd->l2_queue);
dl_list_init(&hapd->l2_oui_queue);
#endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_SAE
+ dl_list_init(&hapd->sae_commit_queue);
+#endif /* CONFIG_SAE */
return hapd;
}
@@ -2276,7 +2180,7 @@ static void hostapd_bss_deinit(struct hostapd_data *hapd)
if (!hapd)
return;
wpa_printf(MSG_DEBUG, "%s: deinit bss %s", __func__,
- hapd->conf->iface);
+ hapd->conf ? hapd->conf->iface : "N/A");
hostapd_bss_deinit_no_free(hapd);
wpa_msg(hapd->msg_ctx, MSG_INFO, AP_EVENT_DISABLED);
hostapd_cleanup(hapd);
@@ -2303,7 +2207,7 @@ void hostapd_interface_deinit(struct hostapd_iface *iface)
}
#endif /* CONFIG_FST */
- for (j = iface->num_bss - 1; j >= 0; j--) {
+ for (j = (int) iface->num_bss - 1; j >= 0; j--) {
if (!iface->bss)
break;
hostapd_bss_deinit(iface->bss[j]);
diff --git a/src/ap/hostapd.h b/src/ap/hostapd.h
index d304c1171810c..790d377548702 100644
--- a/src/ap/hostapd.h
+++ b/src/ap/hostapd.h
@@ -66,9 +66,7 @@ struct hapd_interfaces {
int eloop_initialized;
#ifdef CONFIG_DPP
- int dpp_init_done;
- struct dl_list dpp_bootstrap; /* struct dpp_bootstrap_info */
- struct dl_list dpp_configurator; /* struct dpp_configurator */
+ struct dpp_global *dpp;
#endif /* CONFIG_DPP */
};
@@ -129,6 +127,13 @@ struct hostapd_neighbor_entry {
int stationary;
};
+struct hostapd_sae_commit_queue {
+ struct dl_list list;
+ int rssi;
+ size_t len;
+ u8 msg[];
+};
+
/**
* struct hostapd_data - hostapd per-BSS data structure
*/
@@ -307,7 +312,10 @@ struct hostapd_data {
/** Key used for generating SAE anti-clogging tokens */
u8 sae_token_key[8];
struct os_reltime last_sae_token_key_update;
+ u16 sae_token_idx;
+ u16 sae_pending_token_idx[256];
int dot11RSNASAERetransPeriod; /* msec */
+ struct dl_list sae_commit_queue; /* struct hostapd_sae_commit_queue */
#endif /* CONFIG_SAE */
#ifdef CONFIG_TESTING_OPTIONS
diff --git a/src/ap/hs20.c b/src/ap/hs20.c
index e265569aef389..532580e7c66c4 100644
--- a/src/ap/hs20.c
+++ b/src/ap/hs20.c
@@ -25,17 +25,20 @@ u8 * hostapd_eid_hs20_indication(struct hostapd_data *hapd, u8 *eid)
if (!hapd->conf->hs20)
return eid;
*eid++ = WLAN_EID_VENDOR_SPECIFIC;
- *eid++ = 7;
+ *eid++ = hapd->conf->hs20_release < 2 ? 5 : 7;
WPA_PUT_BE24(eid, OUI_WFA);
eid += 3;
*eid++ = HS20_INDICATION_OUI_TYPE;
- conf = HS20_VERSION; /* Release Number */
- conf |= HS20_ANQP_DOMAIN_ID_PRESENT;
+ conf = (hapd->conf->hs20_release - 1) << 4; /* Release Number */
+ if (hapd->conf->hs20_release >= 2)
+ conf |= HS20_ANQP_DOMAIN_ID_PRESENT;
if (hapd->conf->disable_dgaf)
conf |= HS20_DGAF_DISABLED;
*eid++ = conf;
- WPA_PUT_LE16(eid, hapd->conf->anqp_domain_id);
- eid += 2;
+ if (hapd->conf->hs20_release >= 2) {
+ WPA_PUT_LE16(eid, hapd->conf->anqp_domain_id);
+ eid += 2;
+ }
return eid;
}
@@ -84,6 +87,10 @@ u8 * hostapd_eid_osen(struct hostapd_data *hapd, u8 *eid)
capab |= WPA_CAPABILITY_MFPR;
}
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_OCV
+ if (hapd->conf->ocv)
+ capab |= WPA_CAPABILITY_OCVC;
+#endif /* CONFIG_OCV */
WPA_PUT_LE16(eid, capab);
eid += 2;
diff --git a/src/ap/hw_features.c b/src/ap/hw_features.c
index 5279abca1f1fa..9d3d990a281fd 100644
--- a/src/ap/hw_features.c
+++ b/src/ap/hw_features.c
@@ -229,9 +229,6 @@ static int ieee80211n_allowed_ht40_channel_pair(struct hostapd_iface *iface)
{
int pri_chan, sec_chan;
- if (!iface->conf->secondary_channel)
- return 1; /* HT40 not used */
-
pri_chan = iface->conf->channel;
sec_chan = pri_chan + iface->conf->secondary_channel * 4;
@@ -697,30 +694,25 @@ int hostapd_check_ht_capab(struct hostapd_iface *iface)
static int hostapd_is_usable_chan(struct hostapd_iface *iface,
int channel, int primary)
{
- int i;
struct hostapd_channel_data *chan;
if (!iface->current_mode)
return 0;
- for (i = 0; i < iface->current_mode->num_channels; i++) {
- chan = &iface->current_mode->channels[i];
- if (chan->chan != channel)
- continue;
-
- if (!(chan->flag & HOSTAPD_CHAN_DISABLED))
- return 1;
+ chan = hw_get_channel_chan(iface->current_mode, channel, NULL);
+ if (!chan)
+ return 0;
- wpa_printf(MSG_DEBUG,
- "%schannel [%i] (%i) is disabled for use in AP mode, flags: 0x%x%s%s",
- primary ? "" : "Configured HT40 secondary ",
- i, chan->chan, chan->flag,
- chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "",
- chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : "");
- }
+ if ((primary && chan_pri_allowed(chan)) ||
+ (!primary && !(chan->flag & HOSTAPD_CHAN_DISABLED)))
+ return 1;
- wpa_printf(MSG_INFO, "Channel %d (%s) not allowed for AP mode",
- channel, primary ? "primary" : "secondary");
+ wpa_printf(MSG_INFO,
+ "Channel %d (%s) not allowed for AP mode, flags: 0x%x%s%s",
+ channel, primary ? "primary" : "secondary",
+ chan->flag,
+ chan->flag & HOSTAPD_CHAN_NO_IR ? " NO-IR" : "",
+ chan->flag & HOSTAPD_CHAN_RADAR ? " RADAR" : "");
return 0;
}
@@ -728,6 +720,12 @@ static int hostapd_is_usable_chan(struct hostapd_iface *iface,
static int hostapd_is_usable_chans(struct hostapd_iface *iface)
{
int secondary_chan;
+ struct hostapd_channel_data *pri_chan;
+
+ pri_chan = hw_get_channel_chan(iface->current_mode,
+ iface->conf->channel, NULL);
+ if (!pri_chan)
+ return 0;
if (!hostapd_is_usable_chan(iface, iface->conf->channel, 1))
return 0;
@@ -742,13 +740,15 @@ static int hostapd_is_usable_chans(struct hostapd_iface *iface)
/* Both HT40+ and HT40- are set, pick a valid secondary channel */
secondary_chan = iface->conf->channel + 4;
- if (hostapd_is_usable_chan(iface, secondary_chan, 0)) {
+ if (hostapd_is_usable_chan(iface, secondary_chan, 0) &&
+ (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) {
iface->conf->secondary_channel = 1;
return 1;
}
secondary_chan = iface->conf->channel - 4;
- if (hostapd_is_usable_chan(iface, secondary_chan, 0)) {
+ if (hostapd_is_usable_chan(iface, secondary_chan, 0) &&
+ (pri_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M)) {
iface->conf->secondary_channel = -1;
return 1;
}
diff --git a/src/ap/ieee802_11.c b/src/ap/ieee802_11.c
index f9bb99d985495..fde19b526f051 100644
--- a/src/ap/ieee802_11.c
+++ b/src/ap/ieee802_11.c
@@ -21,6 +21,8 @@
#include "common/ieee802_11_common.h"
#include "common/wpa_ctrl.h"
#include "common/sae.h"
+#include "common/dpp.h"
+#include "common/ocv.h"
#include "radius/radius.h"
#include "radius/radius_client.h"
#include "p2p/p2p.h"
@@ -61,6 +63,25 @@ prepare_auth_resp_fils(struct hostapd_data *hapd,
const u8 *msk, size_t msk_len,
int *is_pub);
#endif /* CONFIG_FILS */
+static void handle_auth(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len,
+ int rssi, int from_queue);
+
+
+u8 * hostapd_eid_multi_ap(struct hostapd_data *hapd, u8 *eid)
+{
+ u8 multi_ap_val = 0;
+
+ if (!hapd->conf->multi_ap)
+ return eid;
+ if (hapd->conf->multi_ap & BACKHAUL_BSS)
+ multi_ap_val |= MULTI_AP_BACKHAUL_BSS;
+ if (hapd->conf->multi_ap & FRONTHAUL_BSS)
+ multi_ap_val |= MULTI_AP_FRONTHAUL_BSS;
+
+ return eid + add_multi_ap_ie(eid, 9, multi_ap_val);
+}
+
u8 * hostapd_eid_supp_rates(struct hostapd_data *hapd, u8 *eid)
{
@@ -403,6 +424,15 @@ static struct wpabuf * auth_build_sae_commit(struct hostapd_data *hapd,
return NULL;
}
+ if (pw && pw->vlan_id) {
+ if (!sta->sae->tmp) {
+ wpa_printf(MSG_INFO,
+ "SAE: No temporary data allocated - cannot store VLAN ID");
+ return NULL;
+ }
+ sta->sae->tmp->vlan_id = pw->vlan_id;
+ }
+
buf = wpabuf_alloc(SAE_COMMIT_MAX_LEN +
(rx_id ? 3 + os_strlen(rx_id) : 0));
if (buf == NULL)
@@ -492,22 +522,58 @@ static int use_sae_anti_clogging(struct hostapd_data *hapd)
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 u8 sae_token_hash(struct hostapd_data *hapd, const u8 *addr)
+{
+ u8 hash[SHA256_MAC_LEN];
+
+ hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key),
+ addr, ETH_ALEN, hash);
+ return hash[0];
+}
+
+
static int check_sae_token(struct hostapd_data *hapd, 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)
return -1;
- if (hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key),
- addr, ETH_ALEN, mac) < 0 ||
- os_memcmp_const(token, mac, SHA256_MAC_LEN) != 0)
+ idx = sae_token_hash(hapd, addr);
+ token_idx = hapd->sae_pending_token_idx[idx];
+ if (token_idx == 0 || token_idx != WPA_GET_BE16(token)) {
+ wpa_printf(MSG_DEBUG, "SAE: 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(hapd->sae_token_key, sizeof(hapd->sae_token_key),
+ 2, addrs, len, mac) < 0 ||
+ os_memcmp_const(token + 2, &mac[2], SHA256_MAC_LEN - 2) != 0)
return -1;
+ hapd->sae_pending_token_idx[idx] = 0; /* invalidate used token */
+
return 0;
}
@@ -518,16 +584,25 @@ static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd,
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_reltime(&now);
if (!os_reltime_initialized(&hapd->last_sae_token_key_update) ||
- os_reltime_expired(&now, &hapd->last_sae_token_key_update, 60)) {
+ os_reltime_expired(&now, &hapd->last_sae_token_key_update, 60) ||
+ hapd->sae_token_idx == 0xffff) {
if (random_get_bytes(hapd->sae_token_key,
sizeof(hapd->sae_token_key)) < 0)
return NULL;
wpa_hexdump(MSG_DEBUG, "SAE: Updated token key",
hapd->sae_token_key, sizeof(hapd->sae_token_key));
hapd->last_sae_token_key_update = now;
+ hapd->sae_token_idx = 0;
+ os_memset(hapd->sae_pending_token_idx, 0,
+ sizeof(hapd->sae_pending_token_idx));
}
buf = wpabuf_alloc(sizeof(le16) + SHA256_MAC_LEN);
@@ -536,9 +611,25 @@ static struct wpabuf * auth_build_token_req(struct hostapd_data *hapd,
wpabuf_put_le16(buf, group); /* Finite Cyclic Group */
+ p_idx = sae_token_hash(hapd, addr);
+ token_idx = hapd->sae_pending_token_idx[p_idx];
+ if (!token_idx) {
+ hapd->sae_token_idx++;
+ token_idx = hapd->sae_token_idx;
+ hapd->sae_pending_token_idx[p_idx] = token_idx;
+ }
+ WPA_PUT_BE16(idx, token_idx);
token = wpabuf_put(buf, SHA256_MAC_LEN);
- hmac_sha256(hapd->sae_token_key, sizeof(hapd->sae_token_key),
- addr, ETH_ALEN, token);
+ addrs[0] = addr;
+ len[0] = ETH_ALEN;
+ addrs[1] = idx;
+ len[1] = sizeof(idx);
+ if (hmac_sha256_vector(hapd->sae_token_key, sizeof(hapd->sae_token_key),
+ 2, addrs, len, token) < 0) {
+ wpabuf_free(buf);
+ return NULL;
+ }
+ WPA_PUT_BE16(token, token_idx);
return buf;
}
@@ -610,8 +701,52 @@ static void sae_set_retransmit_timer(struct hostapd_data *hapd,
}
+static void sae_sme_send_external_auth_status(struct hostapd_data *hapd,
+ struct sta_info *sta, u16 status)
+{
+ struct external_auth params;
+
+ os_memset(&params, 0, sizeof(params));
+ params.status = status;
+ params.bssid = sta->addr;
+ if (status == WLAN_STATUS_SUCCESS && sta->sae)
+ params.pmkid = sta->sae->pmkid;
+
+ hostapd_drv_send_external_auth_status(hapd, &params);
+}
+
+
void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta)
{
+#ifndef CONFIG_NO_VLAN
+ struct vlan_description vlan_desc;
+
+ if (sta->sae->tmp && sta->sae->tmp->vlan_id > 0) {
+ wpa_printf(MSG_DEBUG, "SAE: Assign STA " MACSTR
+ " to VLAN ID %d",
+ MAC2STR(sta->addr), sta->sae->tmp->vlan_id);
+
+ os_memset(&vlan_desc, 0, sizeof(vlan_desc));
+ vlan_desc.notempty = 1;
+ vlan_desc.untagged = sta->sae->tmp->vlan_id;
+ if (!hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) {
+ wpa_printf(MSG_INFO,
+ "Invalid VLAN ID %d in sae_password",
+ sta->sae->tmp->vlan_id);
+ return;
+ }
+
+ if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0 ||
+ ap_sta_bind_vlan(hapd, sta) < 0) {
+ wpa_printf(MSG_INFO,
+ "Failed to assign VLAN ID %d from sae_password to "
+ MACSTR, sta->sae->tmp->vlan_id,
+ MAC2STR(sta->addr));
+ return;
+ }
+ }
+#endif /* CONFIG_NO_VLAN */
+
sta->flags |= WLAN_STA_AUTH;
sta->auth_alg = WLAN_AUTH_SAE;
mlme_authenticate_indication(hapd, sta);
@@ -619,14 +754,18 @@ void sae_accept_sta(struct hostapd_data *hapd, struct sta_info *sta)
sae_set_state(sta, SAE_ACCEPTED, "Accept Confirm");
wpa_auth_pmksa_add_sae(hapd->wpa_auth, sta->addr,
sta->sae->pmk, sta->sae->pmkid);
+ sae_sme_send_external_auth_status(hapd, sta, WLAN_STATUS_SUCCESS);
}
static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
- const u8 *bssid, u8 auth_transaction)
+ const u8 *bssid, u8 auth_transaction, int allow_reuse,
+ int *sta_removed)
{
int ret;
+ *sta_removed = 0;
+
if (auth_transaction != 1 && auth_transaction != 2)
return WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -636,7 +775,8 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
switch (sta->sae->state) {
case SAE_NOTHING:
if (auth_transaction == 1) {
- ret = auth_sae_send_commit(hapd, sta, bssid, 1);
+ ret = auth_sae_send_commit(hapd, sta, bssid,
+ !allow_reuse);
if (ret)
return ret;
sae_set_state(sta, SAE_COMMITTED, "Sent Commit");
@@ -725,7 +865,8 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
* step to get to Accepted without waiting for
* additional events.
*/
- return sae_sm_step(hapd, sta, bssid, auth_transaction);
+ return sae_sm_step(hapd, sta, bssid, auth_transaction,
+ 0, sta_removed);
}
break;
case SAE_CONFIRMED:
@@ -758,8 +899,9 @@ static int sae_sm_step(struct hostapd_data *hapd, struct sta_info *sta,
wpa_printf(MSG_DEBUG, "SAE: remove the STA (" MACSTR
") doing reauthentication",
MAC2STR(sta->addr));
- ap_free_sta(hapd, sta);
wpa_auth_pmksa_remove(hapd->wpa_auth, sta->addr);
+ ap_free_sta(hapd, sta);
+ *sta_removed = 1;
} else if (auth_transaction == 1) {
wpa_printf(MSG_DEBUG, "SAE: Start reauthentication");
ret = auth_sae_send_commit(hapd, sta, bssid, 1);
@@ -795,18 +937,21 @@ static void sae_pick_next_group(struct hostapd_data *hapd, struct sta_info *sta)
{
struct sae_data *sae = sta->sae;
int i, *groups = hapd->conf->sae_groups;
+ int default_groups[] = { 19, 0 };
if (sae->state != SAE_COMMITTED)
return;
wpa_printf(MSG_DEBUG, "SAE: Previously selected group: %d", sae->group);
- for (i = 0; groups && groups[i] > 0; i++) {
+ if (!groups)
+ groups = default_groups;
+ for (i = 0; groups[i] > 0; i++) {
if (sae->group == groups[i])
break;
}
- if (!groups || groups[i] <= 0) {
+ if (groups[i] <= 0) {
wpa_printf(MSG_DEBUG,
"SAE: Previously selected group not found from the current configuration");
return;
@@ -835,11 +980,16 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
{
int resp = WLAN_STATUS_SUCCESS;
struct wpabuf *data = NULL;
+ int *groups = hapd->conf->sae_groups;
+ int default_groups[] = { 19, 0 };
+ const u8 *pos, *end;
+ int sta_removed = 0;
+
+ if (!groups)
+ groups = default_groups;
#ifdef CONFIG_TESTING_OPTIONS
if (hapd->conf->sae_reflection_attack && auth_transaction == 1) {
- const u8 *pos, *end;
-
wpa_printf(MSG_DEBUG, "SAE: TESTING - reflection attack");
pos = mgmt->u.auth.variable;
end = ((const u8 *) mgmt) + len;
@@ -882,8 +1032,10 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
}
if (auth_transaction == 1) {
- const u8 *token = NULL, *pos, *end;
+ const u8 *token = NULL;
size_t token_len = 0;
+ int allow_reuse = 0;
+
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
"start SAE authentication (RX commit, status=%u)",
@@ -900,8 +1052,7 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
resp = WLAN_STATUS_UNSPECIFIED_FAILURE;
goto reply;
}
- resp = sae_group_allowed(sta->sae,
- hapd->conf->sae_groups,
+ resp = sae_group_allowed(sta->sae, groups,
WPA_GET_LE16(pos));
if (resp != WLAN_STATUS_SUCCESS) {
wpa_printf(MSG_ERROR,
@@ -962,15 +1113,28 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
* to use a different group and that would not be
* allowed if we remain in Committed state with the
* previously set parameters. */
- sae_set_state(sta, SAE_NOTHING,
- "Clear existing state to allow restart");
- sae_clear_data(sta->sae);
+ pos = mgmt->u.auth.variable;
+ end = ((const u8 *) mgmt) + 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, mgmt->u.auth.variable,
((const u8 *) mgmt) + len -
mgmt->u.auth.variable, &token,
- &token_len, hapd->conf->sae_groups);
+ &token_len, groups);
if (resp == SAE_SILENTLY_DISCARD) {
wpa_printf(MSG_DEBUG,
"SAE: Drop commit message from " MACSTR " due to reflection attack",
@@ -1000,7 +1164,7 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
if (resp != WLAN_STATUS_SUCCESS)
goto reply;
- if (!token && use_sae_anti_clogging(hapd)) {
+ if (!token && use_sae_anti_clogging(hapd) && !allow_reuse) {
wpa_printf(MSG_DEBUG,
"SAE: Request anti-clogging token from "
MACSTR, MAC2STR(sta->addr));
@@ -1013,7 +1177,8 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
goto reply;
}
- resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction);
+ resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction,
+ allow_reuse, &sta_removed);
} else if (auth_transaction == 2) {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
@@ -1054,7 +1219,8 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
}
sta->sae->rc = peer_send_confirm;
}
- resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction);
+ resp = sae_sm_step(hapd, sta, mgmt->bssid, auth_transaction, 0,
+ &sta_removed);
} else {
hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
HOSTAPD_LEVEL_DEBUG,
@@ -1066,7 +1232,17 @@ static void handle_auth_sae(struct hostapd_data *hapd, struct sta_info *sta,
}
reply:
- if (resp != WLAN_STATUS_SUCCESS) {
+ if (!sta_removed && resp != WLAN_STATUS_SUCCESS) {
+ pos = mgmt->u.auth.variable;
+ end = ((const u8 *) mgmt) + 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);
+
+ sae_sme_send_external_auth_status(hapd, sta, resp);
send_auth_reply(hapd, mgmt->sa, mgmt->bssid, WLAN_AUTH_SAE,
auth_transaction, resp,
data ? wpabuf_head(data) : (u8 *) "",
@@ -1074,8 +1250,9 @@ reply:
}
remove_sta:
- if (sta->added_unassoc && (resp != WLAN_STATUS_SUCCESS ||
- status_code != WLAN_STATUS_SUCCESS)) {
+ if (!sta_removed && sta->added_unassoc &&
+ (resp != WLAN_STATUS_SUCCESS ||
+ status_code != WLAN_STATUS_SUCCESS)) {
hostapd_drv_sta_remove(hapd, sta->addr);
sta->added_unassoc = 0;
}
@@ -1114,6 +1291,105 @@ int auth_sae_init_committed(struct hostapd_data *hapd, struct sta_info *sta)
return 0;
}
+
+void auth_sae_process_commit(void *eloop_ctx, void *user_ctx)
+{
+ struct hostapd_data *hapd = eloop_ctx;
+ struct hostapd_sae_commit_queue *q;
+ unsigned int queue_len;
+
+ q = dl_list_first(&hapd->sae_commit_queue,
+ struct hostapd_sae_commit_queue, list);
+ if (!q)
+ return;
+ wpa_printf(MSG_DEBUG,
+ "SAE: Process next available message from queue");
+ dl_list_del(&q->list);
+ handle_auth(hapd, (const struct ieee80211_mgmt *) q->msg, q->len,
+ q->rssi, 1);
+ os_free(q);
+
+ if (eloop_is_timeout_registered(auth_sae_process_commit, hapd, NULL))
+ return;
+ queue_len = dl_list_len(&hapd->sae_commit_queue);
+ eloop_register_timeout(0, queue_len * 10000, auth_sae_process_commit,
+ hapd, NULL);
+}
+
+
+static void auth_sae_queue(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt, size_t len,
+ int rssi)
+{
+ struct hostapd_sae_commit_queue *q, *q2;
+ unsigned int queue_len;
+ const struct ieee80211_mgmt *mgmt2;
+
+ queue_len = dl_list_len(&hapd->sae_commit_queue);
+ if (queue_len >= 15) {
+ wpa_printf(MSG_DEBUG,
+ "SAE: No more room in message queue - drop the new frame from "
+ MACSTR, MAC2STR(mgmt->sa));
+ return;
+ }
+
+ wpa_printf(MSG_DEBUG, "SAE: Queue Authentication message from "
+ MACSTR " for processing (queue_len %u)", MAC2STR(mgmt->sa),
+ queue_len);
+ q = os_zalloc(sizeof(*q) + len);
+ if (!q)
+ return;
+ q->rssi = rssi;
+ q->len = len;
+ os_memcpy(q->msg, mgmt, len);
+
+ /* 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) {
+ mgmt2 = (const struct ieee80211_mgmt *) q2->msg;
+ if (os_memcmp(mgmt->sa, mgmt2->sa, ETH_ALEN) == 0 &&
+ mgmt->u.auth.auth_transaction ==
+ mgmt2->u.auth.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:
+ if (eloop_is_timeout_registered(auth_sae_process_commit, hapd, NULL))
+ return;
+ eloop_register_timeout(0, queue_len * 10000, auth_sae_process_commit,
+ hapd, NULL);
+}
+
+
+static int auth_sae_queued_addr(struct hostapd_data *hapd, const u8 *addr)
+{
+ struct hostapd_sae_commit_queue *q;
+ const struct ieee80211_mgmt *mgmt;
+
+ dl_list_for_each(q, &hapd->sae_commit_queue,
+ struct hostapd_sae_commit_queue, list) {
+ mgmt = (const struct ieee80211_mgmt *) q->msg;
+ if (os_memcmp(addr, mgmt->sa, ETH_ALEN) == 0)
+ return 1;
+ }
+
+ return 0;
+}
+
#endif /* CONFIG_SAE */
@@ -1273,6 +1549,7 @@ void handle_auth_fils(struct hostapd_data *hapd, struct sta_info *sta,
}
res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
+ hapd->iface->freq,
elems.rsn_ie - 2, elems.rsn_ie_len + 2,
elems.mdie, elems.mdie_len, NULL, 0);
resp = wpa_res_to_status_code(res);
@@ -1544,7 +1821,10 @@ prepare_auth_resp_fils(struct hostapd_data *hapd,
}
sta->fils_erp_pmkid_set = 0;
- if (wpa_auth_pmksa_add2(
+ wpa_auth_add_fils_pmk_pmkid(sta->wpa_sm, pmk, pmk_len,
+ sta->fils_erp_pmkid);
+ if (!hapd->conf->disable_pmksa_caching &&
+ wpa_auth_pmksa_add2(
hapd->wpa_auth, sta->addr,
pmk, pmk_len,
sta->fils_erp_pmkid,
@@ -1743,7 +2023,8 @@ ieee802_11_set_radius_info(struct hostapd_data *hapd, struct sta_info *sta,
static void handle_auth(struct hostapd_data *hapd,
- const struct ieee80211_mgmt *mgmt, size_t len)
+ const struct ieee80211_mgmt *mgmt, size_t len,
+ int rssi, int from_queue)
{
u16 auth_alg, auth_transaction, status_code;
u16 resp = WLAN_STATUS_SUCCESS;
@@ -1790,11 +2071,12 @@ static void handle_auth(struct hostapd_data *hapd,
wpa_printf(MSG_DEBUG, "authentication: STA=" MACSTR " auth_alg=%d "
"auth_transaction=%d status_code=%d wep=%d%s "
- "seq_ctrl=0x%x%s",
+ "seq_ctrl=0x%x%s%s",
MAC2STR(mgmt->sa), auth_alg, auth_transaction,
status_code, !!(fc & WLAN_FC_ISWEP),
challenge ? " challenge" : "",
- seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "");
+ seq_ctrl, (fc & WLAN_FC_RETRY) ? " retry" : "",
+ from_queue ? " (from queue)" : "");
#ifdef CONFIG_NO_RC4
if (auth_alg == WLAN_AUTH_SHARED_KEY) {
@@ -1922,9 +2204,26 @@ static void handle_auth(struct hostapd_data *hapd,
if (res == HOSTAPD_ACL_PENDING)
return;
+#ifdef CONFIG_SAE
+ if (auth_alg == WLAN_AUTH_SAE && !from_queue &&
+ (auth_transaction == 1 ||
+ (auth_transaction == 2 && auth_sae_queued_addr(hapd, mgmt->sa)))) {
+ /* Handle SAE Authentication commit message through a queue to
+ * provide more control for postponing the needed heavy
+ * processing under a possible DoS attack scenario. In addition,
+ * queue SAE Authentication confirm message if there happens to
+ * be a queued commit message from the same peer. This is needed
+ * to avoid reordering Authentication frames within the same
+ * SAE exchange. */
+ auth_sae_queue(hapd, mgmt, len, rssi);
+ return;
+ }
+#endif /* CONFIG_SAE */
+
sta = ap_get_sta(hapd, mgmt->sa);
if (sta) {
sta->flags &= ~WLAN_STA_PENDING_FILS_ERP;
+ sta->ft_over_ds = 0;
if ((fc & WLAN_FC_RETRY) &&
sta->last_seq_ctrl != WLAN_INVALID_MGMT_SEQ &&
sta->last_seq_ctrl == seq_ctrl &&
@@ -1973,6 +2272,9 @@ static void handle_auth(struct hostapd_data *hapd,
}
sta->last_seq_ctrl = seq_ctrl;
sta->last_subtype = WLAN_FC_STYPE_AUTH;
+#ifdef CONFIG_MBO
+ sta->auth_rssi = rssi;
+#endif /* CONFIG_MBO */
res = ieee802_11_set_radius_info(
hapd, sta, res, session_timeout, acct_interim_interval,
@@ -2210,6 +2512,59 @@ static u16 check_wmm(struct hostapd_data *hapd, struct sta_info *sta,
return WLAN_STATUS_SUCCESS;
}
+static u16 check_multi_ap(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *multi_ap_ie, size_t multi_ap_len)
+{
+ u8 multi_ap_value = 0;
+
+ sta->flags &= ~WLAN_STA_MULTI_AP;
+
+ if (!hapd->conf->multi_ap)
+ return WLAN_STATUS_SUCCESS;
+
+ if (multi_ap_ie) {
+ const u8 *multi_ap_subelem;
+
+ multi_ap_subelem = get_ie(multi_ap_ie + 4,
+ multi_ap_len - 4,
+ MULTI_AP_SUB_ELEM_TYPE);
+ if (multi_ap_subelem && multi_ap_subelem[1] == 1) {
+ multi_ap_value = multi_ap_subelem[2];
+ } else {
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Multi-AP IE has missing or invalid Multi-AP subelement");
+ return WLAN_STATUS_INVALID_IE;
+ }
+ }
+
+ if (multi_ap_value && multi_ap_value != MULTI_AP_BACKHAUL_STA)
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Multi-AP IE with unexpected value 0x%02x",
+ multi_ap_value);
+
+ if (!(multi_ap_value & MULTI_AP_BACKHAUL_STA)) {
+ if (hapd->conf->multi_ap & FRONTHAUL_BSS)
+ return WLAN_STATUS_SUCCESS;
+
+ hostapd_logger(hapd, sta->addr,
+ HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_INFO,
+ "Non-Multi-AP STA tries to associate with backhaul-only BSS");
+ return WLAN_STATUS_ASSOC_DENIED_UNSPEC;
+ }
+
+ if (!(hapd->conf->multi_ap & BACKHAUL_BSS))
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE80211,
+ HOSTAPD_LEVEL_DEBUG,
+ "Backhaul STA tries to associate with fronthaul-only BSS");
+
+ sta->flags |= WLAN_STA_MULTI_AP;
+ return WLAN_STATUS_SUCCESS;
+}
+
static u16 copy_supp_rates(struct hostapd_data *hapd, struct sta_info *sta,
struct ieee802_11_elems *elems)
@@ -2466,6 +2821,11 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
resp = copy_supp_rates(hapd, sta, &elems);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
+
+ resp = check_multi_ap(hapd, sta, elems.multi_ap, elems.multi_ap_len);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+
#ifdef CONFIG_IEEE80211N
resp = copy_sta_ht_capab(hapd, sta, elems.ht_capabilities);
if (resp != WLAN_STATUS_SUCCESS)
@@ -2485,6 +2845,10 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
if (resp != WLAN_STATUS_SUCCESS)
return resp;
+ resp = copy_sta_vht_oper(hapd, sta, elems.vht_operation);
+ if (resp != WLAN_STATUS_SUCCESS)
+ return resp;
+
resp = set_sta_vht_opmode(hapd, sta, elems.vht_opmode_notif);
if (resp != WLAN_STATUS_SUCCESS)
return resp;
@@ -2577,7 +2941,9 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
"state machine");
return WLAN_STATUS_UNSPECIFIED_FAILURE;
}
+ wpa_auth_set_auth_alg(sta->wpa_sm, sta->auth_alg);
res = wpa_validate_wpa_ie(hapd->wpa_auth, sta->wpa_sm,
+ hapd->iface->freq,
wpa_ie, wpa_ie_len,
elems.mdie, elems.mdie_len,
elems.owe_dh, elems.owe_dh_len);
@@ -2669,6 +3035,37 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
}
#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP2
+ dpp_pfs_free(sta->dpp_pfs);
+ sta->dpp_pfs = NULL;
+
+ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) &&
+ hapd->conf->dpp_netaccesskey && sta->wpa_sm &&
+ wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP &&
+ elems.owe_dh) {
+ sta->dpp_pfs = dpp_pfs_init(
+ wpabuf_head(hapd->conf->dpp_netaccesskey),
+ wpabuf_len(hapd->conf->dpp_netaccesskey));
+ if (!sta->dpp_pfs) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Could not initialize PFS");
+ /* Try to continue without PFS */
+ goto pfs_fail;
+ }
+
+ if (dpp_pfs_process(sta->dpp_pfs, elems.owe_dh,
+ elems.owe_dh_len) < 0) {
+ dpp_pfs_free(sta->dpp_pfs);
+ sta->dpp_pfs = NULL;
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ }
+
+ wpa_auth_set_dpp_z(sta->wpa_sm, sta->dpp_pfs ?
+ sta->dpp_pfs->secret : NULL);
+ pfs_fail:
+#endif /* CONFIG_DPP2 */
+
#ifdef CONFIG_IEEE80211N
if ((sta->flags & (WLAN_STA_HT | WLAN_STA_VHT)) &&
wpa_auth_get_pairwise(sta->wpa_sm) == WPA_CIPHER_TKIP) {
@@ -2713,10 +3110,20 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
#ifdef CONFIG_HS20
wpabuf_free(sta->hs20_ie);
if (elems.hs20 && elems.hs20_len > 4) {
+ int release;
+
sta->hs20_ie = wpabuf_alloc_copy(elems.hs20 + 4,
elems.hs20_len - 4);
- } else
+ release = ((elems.hs20[4] >> 4) & 0x0f) + 1;
+ if (release >= 2 && !wpa_auth_uses_mfp(sta->wpa_sm)) {
+ wpa_printf(MSG_DEBUG,
+ "HS 2.0: PMF not negotiated by release %d station "
+ MACSTR, release, MAC2STR(sta->addr));
+ return WLAN_STATUS_ROBUST_MGMT_FRAME_POLICY_VIOLATION;
+ }
+ } else {
sta->hs20_ie = NULL;
+ }
wpabuf_free(sta->roaming_consortium);
if (elems.roaming_cons_sel)
@@ -2747,6 +3154,35 @@ static u16 check_assoc_ies(struct hostapd_data *hapd, struct sta_info *sta,
}
#endif /* CONFIG_MBO */
+#if defined(CONFIG_FILS) && defined(CONFIG_OCV)
+ if (wpa_auth_uses_ocv(sta->wpa_sm) &&
+ (sta->auth_alg == WLAN_AUTH_FILS_SK ||
+ sta->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+ sta->auth_alg == WLAN_AUTH_FILS_PK)) {
+ struct wpa_channel_info ci;
+ int tx_chanwidth;
+ int tx_seg1_idx;
+
+ if (hostapd_drv_channel_info(hapd, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "Failed to get channel info to validate received OCI in FILS (Re)Association Request frame");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ if (get_sta_tx_parameters(sta->wpa_sm,
+ channel_width_to_int(ci.chanwidth),
+ ci.seg1_idx, &tx_chanwidth,
+ &tx_seg1_idx) < 0)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci,
+ tx_chanwidth, tx_seg1_idx) != 0) {
+ wpa_printf(MSG_WARNING, "FILS: %s", ocv_errorstr);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ }
+#endif /* CONFIG_FILS && CONFIG_OCV */
+
ap_copy_sta_supp_op_classes(sta, elems.supp_op_classes,
elems.supp_op_classes_len);
@@ -2791,7 +3227,7 @@ static void send_deauth(struct hostapd_data *hapd, const u8 *addr,
static int add_associated_sta(struct hostapd_data *hapd,
- struct sta_info *sta)
+ struct sta_info *sta, int reassoc)
{
struct ieee80211_ht_capabilities ht_cap;
struct ieee80211_vht_capabilities vht_cap;
@@ -2807,14 +3243,36 @@ static int add_associated_sta(struct hostapd_data *hapd,
* Skip this if the STA has already completed FT reassociation and the
* TK has been configured since the TX/RX PN must not be reset to 0 for
* the same key.
+ *
+ * FT-over-the-DS has a special case where the STA entry (and as such,
+ * the TK) has not yet been configured to the driver depending on which
+ * driver interface is used. For that case, allow add-STA operation to
+ * be used (instead of set-STA). This is needed to allow mac80211-based
+ * drivers to accept the STA parameter configuration. Since this is
+ * after a new FT-over-DS exchange, a new TK has been derived, so key
+ * reinstallation is not a concern for this case.
*/
+ wpa_printf(MSG_DEBUG, "Add associated STA " MACSTR
+ " (added_unassoc=%d auth_alg=%u ft_over_ds=%u reassoc=%d authorized=%d ft_tk=%d fils_tk=%d)",
+ MAC2STR(sta->addr), sta->added_unassoc, sta->auth_alg,
+ sta->ft_over_ds, reassoc,
+ !!(sta->flags & WLAN_STA_AUTHORIZED),
+ wpa_auth_sta_ft_tk_already_set(sta->wpa_sm),
+ wpa_auth_sta_fils_tk_already_set(sta->wpa_sm));
+
if (!sta->added_unassoc &&
(!(sta->flags & WLAN_STA_AUTHORIZED) ||
+ (reassoc && sta->ft_over_ds && sta->auth_alg == WLAN_AUTH_FT) ||
(!wpa_auth_sta_ft_tk_already_set(sta->wpa_sm) &&
!wpa_auth_sta_fils_tk_already_set(sta->wpa_sm)))) {
hostapd_drv_sta_remove(hapd, sta->addr);
wpa_auth_sm_event(sta->wpa_sm, WPA_DRV_STA_REMOVED);
set = 0;
+
+ /* Do not allow the FT-over-DS exception to be used more than
+ * once per authentication exchange to guarantee a new TK is
+ * used here */
+ sta->ft_over_ds = 0;
}
#ifdef CONFIG_IEEE80211N
@@ -2860,7 +3318,7 @@ static int add_associated_sta(struct hostapd_data *hapd,
static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *addr, u16 status_code, int reassoc,
- const u8 *ies, size_t ies_len)
+ const u8 *ies, size_t ies_len, int rssi)
{
int send_len;
u8 *buf;
@@ -2878,6 +3336,10 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
if (sta && (hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE))
buflen += 150;
#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP2
+ if (sta && sta->dpp_pfs)
+ buflen += 5 + sta->dpp_pfs->curve->prime_len;
+#endif /* CONFIG_DPP2 */
buf = os_zalloc(buflen);
if (!buf) {
res = WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -2905,6 +3367,16 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
/* Extended supported rates */
p = hostapd_eid_ext_supp_rates(hapd, p);
+#ifdef CONFIG_MBO
+ if (status_code == WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS &&
+ rssi != 0) {
+ int delta = hapd->iconf->rssi_reject_assoc_rssi - rssi;
+
+ p = hostapd_eid_mbo_rssi_assoc_rej(hapd, p, buf + buflen - p,
+ delta);
+ }
+#endif /* CONFIG_MBO */
+
#ifdef CONFIG_IEEE80211R_AP
if (sta && status_code == WLAN_STATUS_SUCCESS) {
/* IEEE 802.11r: Mobility Domain Information, Fast BSS
@@ -2975,6 +3447,39 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
}
#endif /* CONFIG_FST */
+#ifdef CONFIG_OWE
+ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
+ sta && sta->owe_ecdh && status_code == WLAN_STATUS_SUCCESS &&
+ wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE) {
+ struct wpabuf *pub;
+
+ pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
+ if (!pub) {
+ res = WLAN_STATUS_UNSPECIFIED_FAILURE;
+ goto done;
+ }
+ /* OWE Diffie-Hellman Parameter element */
+ *p++ = WLAN_EID_EXTENSION; /* Element ID */
+ *p++ = 1 + 2 + wpabuf_len(pub); /* Length */
+ *p++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension */
+ WPA_PUT_LE16(p, sta->owe_group);
+ p += 2;
+ os_memcpy(p, wpabuf_head(pub), wpabuf_len(pub));
+ p += wpabuf_len(pub);
+ wpabuf_free(pub);
+ }
+#endif /* CONFIG_OWE */
+
+#ifdef CONFIG_DPP2
+ if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_DPP) &&
+ sta && sta->dpp_pfs && status_code == WLAN_STATUS_SUCCESS &&
+ wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_DPP) {
+ os_memcpy(p, wpabuf_head(sta->dpp_pfs->ie),
+ wpabuf_len(sta->dpp_pfs->ie));
+ p += wpabuf_len(sta->dpp_pfs->ie);
+ }
+#endif /* CONFIG_DPP2 */
+
#ifdef CONFIG_IEEE80211AC
if (sta && hapd->conf->vendor_vht && (sta->flags & WLAN_STA_VENDOR_VHT))
p = hostapd_eid_vendor_vht(hapd, p);
@@ -2996,6 +3501,9 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
}
#endif /* CONFIG_WPS */
+ if (sta && (sta->flags & WLAN_STA_MULTI_AP))
+ p = hostapd_eid_multi_ap(hapd, p);
+
#ifdef CONFIG_P2P
if (sta && sta->p2p_ie && hapd->p2p_group) {
struct wpabuf *p2p_resp_ie;
@@ -3068,30 +3576,6 @@ static u16 send_assoc_resp(struct hostapd_data *hapd, struct sta_info *sta,
}
#endif /* CONFIG_FILS */
-#ifdef CONFIG_OWE
- if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_OWE) &&
- sta && sta->owe_ecdh && status_code == WLAN_STATUS_SUCCESS &&
- wpa_auth_sta_key_mgmt(sta->wpa_sm) == WPA_KEY_MGMT_OWE) {
- struct wpabuf *pub;
-
- pub = crypto_ecdh_get_pubkey(sta->owe_ecdh, 0);
- if (!pub) {
- res = WLAN_STATUS_UNSPECIFIED_FAILURE;
- goto done;
- }
- /* OWE Diffie-Hellman Parameter element */
- *p++ = WLAN_EID_EXTENSION; /* Element ID */
- *p++ = 1 + 2 + wpabuf_len(pub); /* Length */
- *p++ = WLAN_EID_EXT_OWE_DH_PARAM; /* Element ID Extension */
- WPA_PUT_LE16(p, sta->owe_group);
- p += 2;
- os_memcpy(p, wpabuf_head(pub), wpabuf_len(pub));
- p += wpabuf_len(pub);
- send_len += 3 + 2 + wpabuf_len(pub);
- wpabuf_free(pub);
- }
-#endif /* CONFIG_OWE */
-
if (hostapd_drv_send_mlme(hapd, reply, send_len, 0) < 0) {
wpa_printf(MSG_INFO, "Failed to send assoc resp: %s",
strerror(errno));
@@ -3173,7 +3657,7 @@ void fils_hlp_finish_assoc(struct hostapd_data *hapd, struct sta_info *sta)
reply_res = send_assoc_resp(hapd, sta, sta->addr, WLAN_STATUS_SUCCESS,
sta->fils_pending_assoc_is_reassoc,
sta->fils_pending_assoc_req,
- sta->fils_pending_assoc_req_len);
+ sta->fils_pending_assoc_req_len, 0);
os_free(sta->fils_pending_assoc_req);
sta->fils_pending_assoc_req = NULL;
sta->fils_pending_assoc_req_len = 0;
@@ -3210,7 +3694,7 @@ void fils_hlp_timeout(void *eloop_ctx, void *eloop_data)
static void handle_assoc(struct hostapd_data *hapd,
const struct ieee80211_mgmt *mgmt, size_t len,
- int reassoc)
+ int reassoc, int rssi)
{
u16 capab_info, listen_interval, seq_ctrl, fc;
u16 resp = WLAN_STATUS_SUCCESS, reply_res;
@@ -3393,6 +3877,14 @@ static void handle_assoc(struct hostapd_data *hapd,
resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
goto fail;
}
+
+ if (hapd->iconf->rssi_reject_assoc_rssi && rssi &&
+ rssi < hapd->iconf->rssi_reject_assoc_rssi &&
+ (sta->auth_rssi == 0 ||
+ sta->auth_rssi < hapd->iconf->rssi_reject_assoc_rssi)) {
+ resp = WLAN_STATUS_DENIED_POOR_CHANNEL_CONDITIONS;
+ goto fail;
+ }
#endif /* CONFIG_MBO */
/*
@@ -3550,10 +4042,24 @@ static void handle_assoc(struct hostapd_data *hapd,
* issues with processing other non-Data Class 3 frames during this
* window.
*/
- if (resp == WLAN_STATUS_SUCCESS && sta && add_associated_sta(hapd, sta))
+ if (resp == WLAN_STATUS_SUCCESS && sta &&
+ add_associated_sta(hapd, sta, reassoc))
resp = WLAN_STATUS_AP_UNABLE_TO_HANDLE_NEW_STA;
#ifdef CONFIG_FILS
+ if (sta && delay_assoc && resp == WLAN_STATUS_SUCCESS &&
+ eloop_is_timeout_registered(fils_hlp_timeout, hapd, sta) &&
+ sta->fils_pending_assoc_req) {
+ /* Do not reschedule fils_hlp_timeout in case the station
+ * retransmits (Re)Association Request frame while waiting for
+ * the previously started FILS HLP wait, so that the timeout can
+ * be determined from the first pending attempt. */
+ wpa_printf(MSG_DEBUG,
+ "FILS: Continue waiting for HLP processing before sending (Re)Association Response frame to "
+ MACSTR, MAC2STR(sta->addr));
+ os_free(tmp);
+ return;
+ }
if (sta) {
eloop_cancel_timeout(fils_hlp_timeout, hapd, sta);
os_free(sta->fils_pending_assoc_req);
@@ -3578,7 +4084,7 @@ static void handle_assoc(struct hostapd_data *hapd,
#endif /* CONFIG_FILS */
reply_res = send_assoc_resp(hapd, sta, mgmt->sa, resp, reassoc, pos,
- left);
+ left, rssi);
os_free(tmp);
/*
@@ -3718,28 +4224,6 @@ static void handle_beacon(struct hostapd_data *hapd,
#ifdef CONFIG_IEEE80211W
-
-static int hostapd_sa_query_action(struct hostapd_data *hapd,
- const struct ieee80211_mgmt *mgmt,
- size_t len)
-{
- const u8 *end;
-
- end = mgmt->u.action.u.sa_query_resp.trans_id +
- WLAN_SA_QUERY_TR_ID_LEN;
- if (((u8 *) mgmt) + len < end) {
- wpa_printf(MSG_DEBUG, "IEEE 802.11: Too short SA Query Action "
- "frame (len=%lu)", (unsigned long) len);
- return 0;
- }
-
- ieee802_11_sa_query_action(hapd, mgmt->sa,
- mgmt->u.action.u.sa_query_resp.action,
- mgmt->u.action.u.sa_query_resp.trans_id);
- return 1;
-}
-
-
static int robust_action_frame(u8 category)
{
return category != WLAN_ACTION_PUBLIC &&
@@ -3825,7 +4309,8 @@ static int handle_action(struct hostapd_data *hapd,
return 1;
#ifdef CONFIG_IEEE80211W
case WLAN_ACTION_SA_QUERY:
- return hostapd_sa_query_action(hapd, mgmt, len);
+ ieee802_11_sa_query_action(hapd, mgmt, len);
+ return 1;
#endif /* CONFIG_IEEE80211W */
#ifdef CONFIG_WNM_AP
case WLAN_ACTION_WNM:
@@ -4020,17 +4505,17 @@ int ieee802_11_mgmt(struct hostapd_data *hapd, const u8 *buf, size_t len,
switch (stype) {
case WLAN_FC_STYPE_AUTH:
wpa_printf(MSG_DEBUG, "mgmt::auth");
- handle_auth(hapd, mgmt, len);
+ handle_auth(hapd, mgmt, len, ssi_signal, 0);
ret = 1;
break;
case WLAN_FC_STYPE_ASSOC_REQ:
wpa_printf(MSG_DEBUG, "mgmt::assoc_req");
- handle_assoc(hapd, mgmt, len, 0);
+ handle_assoc(hapd, mgmt, len, 0, ssi_signal);
ret = 1;
break;
case WLAN_FC_STYPE_REASSOC_REQ:
wpa_printf(MSG_DEBUG, "mgmt::reassoc_req");
- handle_assoc(hapd, mgmt, len, 1);
+ handle_assoc(hapd, mgmt, len, 1, ssi_signal);
ret = 1;
break;
case WLAN_FC_STYPE_DISASSOC:
@@ -4236,7 +4721,7 @@ static void handle_assoc_cb(struct hostapd_data *hapd,
sta->flags |= WLAN_STA_WDS;
}
- if (sta->flags & WLAN_STA_WDS) {
+ if (sta->flags & (WLAN_STA_WDS | WLAN_STA_MULTI_AP)) {
int ret;
char ifname_wds[IFNAMSIZ + 1];
diff --git a/src/ap/ieee802_11.h b/src/ap/ieee802_11.h
index 2f3b4da8e752a..db7badcfffaf6 100644
--- a/src/ap/ieee802_11.h
+++ b/src/ap/ieee802_11.h
@@ -59,6 +59,7 @@ u8 * hostapd_eid_wb_chsw_wrapper(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_txpower_envelope(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_he_capab(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid);
+u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid);
int hostapd_ht_operation_update(struct hostapd_iface *iface);
void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
@@ -80,6 +81,8 @@ void ht40_intolerant_add(struct hostapd_iface *iface, struct sta_info *sta);
void ht40_intolerant_remove(struct hostapd_iface *iface, struct sta_info *sta);
u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *vht_capab);
+u16 copy_sta_vht_oper(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *vht_oper);
u16 set_sta_vht_opmode(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *vht_opmode);
void hostapd_tx_status(struct hostapd_data *hapd, const u8 *addr,
@@ -91,8 +94,8 @@ void ieee802_11_rx_from_unknown(struct hostapd_data *hapd, const u8 *src,
u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
struct sta_info *sta, u8 *eid);
void ieee802_11_sa_query_action(struct hostapd_data *hapd,
- const u8 *sa, const u8 action_type,
- const u8 *trans_id);
+ const struct ieee80211_mgmt *mgmt,
+ size_t len);
u8 * hostapd_eid_interworking(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_adv_proto(struct hostapd_data *hapd, u8 *eid);
u8 * hostapd_eid_roaming_consortium(struct hostapd_data *hapd, u8 *eid);
@@ -120,6 +123,9 @@ u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len);
u8 hostapd_mbo_ie_len(struct hostapd_data *hapd);
+u8 * hostapd_eid_mbo_rssi_assoc_rej(struct hostapd_data *hapd, u8 *eid,
+ size_t len, int delta);
+
#else /* CONFIG_MBO */
static inline u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid,
@@ -166,4 +172,9 @@ int ieee802_11_allowed_address(struct hostapd_data *hapd, const u8 *addr,
char **identity, char **radius_cui,
int is_probe_req);
+int get_tx_parameters(struct sta_info *sta, int ap_max_chanwidth,
+ int ap_seg1_idx, int *bandwidth, int *seg1_idx);
+
+void auth_sae_process_commit(void *eloop_ctx, void *user_ctx);
+
#endif /* IEEE802_11_H */
diff --git a/src/ap/ieee802_11_auth.c b/src/ap/ieee802_11_auth.c
index 5cb7fb1454f76..931d4d0659c3c 100644
--- a/src/ap/ieee802_11_auth.c
+++ b/src/ap/ieee802_11_auth.c
@@ -289,6 +289,9 @@ int hostapd_allowed_address(struct hostapd_data *hapd, const u8 *addr,
return HOSTAPD_ACL_ACCEPT;
};
+ if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED)
+ vlan_id = NULL;
+
/* Check whether ACL cache has an entry for this station */
res = hostapd_acl_cache_get(hapd, addr, session_timeout,
acct_interim_interval, vlan_id, psk,
@@ -516,7 +519,6 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req,
struct hostapd_acl_query_data *query, *prev;
struct hostapd_cached_radius_acl *cache;
struct radius_hdr *hdr = radius_msg_get_hdr(msg);
- int *untagged, *tagged, *notempty;
query = hapd->acl_queries;
prev = NULL;
@@ -574,12 +576,10 @@ hostapd_acl_recv_radius(struct radius_msg *msg, struct radius_msg *req,
cache->acct_interim_interval = 0;
}
- notempty = &cache->vlan_id.notempty;
- untagged = &cache->vlan_id.untagged;
- tagged = cache->vlan_id.tagged;
- *notempty = !!radius_msg_get_vlanid(msg, untagged,
- MAX_NUM_TAGGED_VLAN,
- tagged);
+ if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED)
+ cache->vlan_id.notempty = !!radius_msg_get_vlanid(
+ msg, &cache->vlan_id.untagged,
+ MAX_NUM_TAGGED_VLAN, cache->vlan_id.tagged);
decode_tunnel_passwords(hapd, shared_secret, shared_secret_len,
msg, req, cache);
diff --git a/src/ap/ieee802_11_he.c b/src/ap/ieee802_11_he.c
index 1a8d469729856..072135863682e 100644
--- a/src/ap/ieee802_11_he.c
+++ b/src/ap/ieee802_11_he.c
@@ -86,3 +86,34 @@ u8 * hostapd_eid_he_operation(struct hostapd_data *hapd, u8 *eid)
return pos;
}
+
+
+u8 * hostapd_eid_he_mu_edca_parameter_set(struct hostapd_data *hapd, u8 *eid)
+{
+ struct ieee80211_he_mu_edca_parameter_set *edca;
+ u8 *pos;
+ size_t i;
+
+ pos = (u8 *) &hapd->iface->conf->he_mu_edca;
+ for (i = 0; i < sizeof(*edca); i++) {
+ if (pos[i])
+ break;
+ }
+ if (i == sizeof(*edca))
+ return eid; /* no MU EDCA Parameters configured */
+
+ pos = eid;
+ *pos++ = WLAN_EID_EXTENSION;
+ *pos++ = 1 + sizeof(*edca);
+ *pos++ = WLAN_EID_EXT_HE_MU_EDCA_PARAMS;
+
+ edca = (struct ieee80211_he_mu_edca_parameter_set *) pos;
+ os_memcpy(edca, &hapd->iface->conf->he_mu_edca, sizeof(*edca));
+
+ wpa_hexdump(MSG_DEBUG, "HE: MU EDCA Parameter Set element",
+ pos, sizeof(*edca));
+
+ pos += sizeof(*edca);
+
+ return pos;
+}
diff --git a/src/ap/ieee802_11_shared.c b/src/ap/ieee802_11_shared.c
index 49e9bf8cc7c9c..707381ffe7099 100644
--- a/src/ap/ieee802_11_shared.c
+++ b/src/ap/ieee802_11_shared.c
@@ -10,10 +10,12 @@
#include "utils/common.h"
#include "common/ieee802_11_defs.h"
+#include "common/ocv.h"
#include "hostapd.h"
#include "sta_info.h"
#include "ap_config.h"
#include "ap_drv_ops.h"
+#include "wpa_auth.h"
#include "ieee802_11.h"
@@ -49,7 +51,12 @@ u8 * hostapd_eid_assoc_comeback_time(struct hostapd_data *hapd,
void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
const u8 *addr, const u8 *trans_id)
{
- struct ieee80211_mgmt mgmt;
+#ifdef CONFIG_OCV
+ struct sta_info *sta;
+#endif /* CONFIG_OCV */
+ struct ieee80211_mgmt *mgmt;
+ u8 *oci_ie = NULL;
+ u8 oci_ie_len = 0;
u8 *end;
wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Request to "
@@ -57,19 +64,61 @@ void ieee802_11_send_sa_query_req(struct hostapd_data *hapd,
wpa_hexdump(MSG_DEBUG, "IEEE 802.11: SA Query Transaction ID",
trans_id, WLAN_SA_QUERY_TR_ID_LEN);
- os_memset(&mgmt, 0, sizeof(mgmt));
- mgmt.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_ACTION);
- os_memcpy(mgmt.da, addr, ETH_ALEN);
- os_memcpy(mgmt.sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(mgmt.bssid, hapd->own_addr, ETH_ALEN);
- mgmt.u.action.category = WLAN_ACTION_SA_QUERY;
- mgmt.u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST;
- os_memcpy(mgmt.u.action.u.sa_query_req.trans_id, trans_id,
+#ifdef CONFIG_OCV
+ sta = ap_get_sta(hapd, addr);
+ if (sta && wpa_auth_uses_ocv(sta->wpa_sm)) {
+ struct wpa_channel_info ci;
+
+ if (hostapd_drv_channel_info(hapd, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "Failed to get channel info for OCI element in SA Query Request");
+ return;
+ }
+
+ oci_ie_len = OCV_OCI_EXTENDED_LEN;
+ oci_ie = os_zalloc(oci_ie_len);
+ if (!oci_ie) {
+ wpa_printf(MSG_WARNING,
+ "Failed to allocate buffer for OCI element in SA Query Request");
+ return;
+ }
+
+ if (ocv_insert_extended_oci(&ci, oci_ie) < 0) {
+ os_free(oci_ie);
+ return;
+ }
+ }
+#endif /* CONFIG_OCV */
+
+ mgmt = os_zalloc(sizeof(*mgmt) + oci_ie_len);
+ if (!mgmt) {
+ wpa_printf(MSG_DEBUG,
+ "Failed to allocate buffer for SA Query Response frame");
+ os_free(oci_ie);
+ return;
+ }
+
+ mgmt->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ os_memcpy(mgmt->da, addr, ETH_ALEN);
+ os_memcpy(mgmt->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(mgmt->bssid, hapd->own_addr, ETH_ALEN);
+ mgmt->u.action.category = WLAN_ACTION_SA_QUERY;
+ mgmt->u.action.u.sa_query_req.action = WLAN_SA_QUERY_REQUEST;
+ os_memcpy(mgmt->u.action.u.sa_query_req.trans_id, trans_id,
WLAN_SA_QUERY_TR_ID_LEN);
- end = mgmt.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN;
- if (hostapd_drv_send_mlme(hapd, &mgmt, end - (u8 *) &mgmt, 0) < 0)
+ end = mgmt->u.action.u.sa_query_req.variable;
+#ifdef CONFIG_OCV
+ if (oci_ie_len > 0) {
+ os_memcpy(end, oci_ie, oci_ie_len);
+ end += oci_ie_len;
+ }
+#endif /* CONFIG_OCV */
+ if (hostapd_drv_send_mlme(hapd, mgmt, end - (u8 *) mgmt, 0) < 0)
wpa_printf(MSG_INFO, "ieee802_11_send_sa_query_req: send failed");
+
+ os_free(mgmt);
+ os_free(oci_ie);
}
@@ -77,7 +126,9 @@ static void ieee802_11_send_sa_query_resp(struct hostapd_data *hapd,
const u8 *sa, const u8 *trans_id)
{
struct sta_info *sta;
- struct ieee80211_mgmt resp;
+ struct ieee80211_mgmt *resp;
+ u8 *oci_ie = NULL;
+ u8 oci_ie_len = 0;
u8 *end;
wpa_printf(MSG_DEBUG, "IEEE 802.11: Received SA Query Request from "
@@ -92,30 +143,123 @@ static void ieee802_11_send_sa_query_resp(struct hostapd_data *hapd,
return;
}
+#ifdef CONFIG_OCV
+ if (wpa_auth_uses_ocv(sta->wpa_sm)) {
+ struct wpa_channel_info ci;
+
+ if (hostapd_drv_channel_info(hapd, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "Failed to get channel info for OCI element in SA Query Response");
+ return;
+ }
+
+ oci_ie_len = OCV_OCI_EXTENDED_LEN;
+ oci_ie = os_zalloc(oci_ie_len);
+ if (!oci_ie) {
+ wpa_printf(MSG_WARNING,
+ "Failed to allocate buffer for for OCI element in SA Query Response");
+ return;
+ }
+
+ if (ocv_insert_extended_oci(&ci, oci_ie) < 0) {
+ os_free(oci_ie);
+ return;
+ }
+ }
+#endif /* CONFIG_OCV */
+
+ resp = os_zalloc(sizeof(*resp) + oci_ie_len);
+ if (!resp) {
+ wpa_printf(MSG_DEBUG,
+ "Failed to allocate buffer for SA Query Response frame");
+ os_free(oci_ie);
+ return;
+ }
+
wpa_printf(MSG_DEBUG, "IEEE 802.11: Sending SA Query Response to "
MACSTR, MAC2STR(sa));
- os_memset(&resp, 0, sizeof(resp));
- resp.frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
- WLAN_FC_STYPE_ACTION);
- os_memcpy(resp.da, sa, ETH_ALEN);
- os_memcpy(resp.sa, hapd->own_addr, ETH_ALEN);
- os_memcpy(resp.bssid, hapd->own_addr, ETH_ALEN);
- resp.u.action.category = WLAN_ACTION_SA_QUERY;
- resp.u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE;
- os_memcpy(resp.u.action.u.sa_query_req.trans_id, trans_id,
+ resp->frame_control = IEEE80211_FC(WLAN_FC_TYPE_MGMT,
+ WLAN_FC_STYPE_ACTION);
+ os_memcpy(resp->da, sa, ETH_ALEN);
+ os_memcpy(resp->sa, hapd->own_addr, ETH_ALEN);
+ os_memcpy(resp->bssid, hapd->own_addr, ETH_ALEN);
+ resp->u.action.category = WLAN_ACTION_SA_QUERY;
+ resp->u.action.u.sa_query_req.action = WLAN_SA_QUERY_RESPONSE;
+ os_memcpy(resp->u.action.u.sa_query_req.trans_id, trans_id,
WLAN_SA_QUERY_TR_ID_LEN);
- end = resp.u.action.u.sa_query_req.trans_id + WLAN_SA_QUERY_TR_ID_LEN;
- if (hostapd_drv_send_mlme(hapd, &resp, end - (u8 *) &resp, 0) < 0)
+ end = resp->u.action.u.sa_query_req.variable;
+#ifdef CONFIG_OCV
+ if (oci_ie_len > 0) {
+ os_memcpy(end, oci_ie, oci_ie_len);
+ end += oci_ie_len;
+ }
+#endif /* CONFIG_OCV */
+ if (hostapd_drv_send_mlme(hapd, resp, end - (u8 *) resp, 0) < 0)
wpa_printf(MSG_INFO, "ieee80211_mgmt_sa_query_request: send failed");
+
+ os_free(resp);
+ os_free(oci_ie);
}
-void ieee802_11_sa_query_action(struct hostapd_data *hapd, const u8 *sa,
- const u8 action_type, const u8 *trans_id)
+void ieee802_11_sa_query_action(struct hostapd_data *hapd,
+ const struct ieee80211_mgmt *mgmt,
+ size_t len)
{
struct sta_info *sta;
int i;
+ const u8 *sa = mgmt->sa;
+ const u8 action_type = mgmt->u.action.u.sa_query_resp.action;
+ const u8 *trans_id = mgmt->u.action.u.sa_query_resp.trans_id;
+
+ if (((const u8 *) mgmt) + len <
+ mgmt->u.action.u.sa_query_resp.variable) {
+ wpa_printf(MSG_DEBUG,
+ "IEEE 802.11: Too short SA Query Action frame (len=%lu)",
+ (unsigned long) len);
+ return;
+ }
+
+ sta = ap_get_sta(hapd, sa);
+
+#ifdef CONFIG_OCV
+ if (sta && wpa_auth_uses_ocv(sta->wpa_sm)) {
+ struct ieee802_11_elems elems;
+ struct wpa_channel_info ci;
+ int tx_chanwidth;
+ int tx_seg1_idx;
+ size_t ies_len;
+ const u8 *ies;
+
+ ies = mgmt->u.action.u.sa_query_resp.variable;
+ ies_len = len - (ies - (u8 *) mgmt);
+ if (ieee802_11_parse_elems(ies, ies_len, &elems, 1) ==
+ ParseFailed) {
+ wpa_printf(MSG_DEBUG,
+ "SA Query: Failed to parse elements");
+ return;
+ }
+
+ if (hostapd_drv_channel_info(hapd, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "Failed to get channel info to validate received OCI in SA Query Action frame");
+ return;
+ }
+
+ if (get_sta_tx_parameters(sta->wpa_sm,
+ channel_width_to_int(ci.chanwidth),
+ ci.seg1_idx, &tx_chanwidth,
+ &tx_seg1_idx) < 0)
+ return;
+
+ if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci,
+ tx_chanwidth, tx_seg1_idx) != 0) {
+ wpa_printf(MSG_WARNING, "%s", ocv_errorstr);
+ return;
+ }
+ }
+#endif /* CONFIG_OCV */
if (action_type == WLAN_SA_QUERY_REQUEST) {
ieee802_11_send_sa_query_resp(hapd, sa, trans_id);
@@ -135,7 +279,6 @@ void ieee802_11_sa_query_action(struct hostapd_data *hapd, const u8 *sa,
/* MLME-SAQuery.confirm */
- sta = ap_get_sta(hapd, sa);
if (sta == NULL || sta->sa_query_trans_id == NULL) {
wpa_printf(MSG_DEBUG, "IEEE 802.11: No matching STA with "
"pending SA Query request found");
@@ -237,6 +380,21 @@ static void hostapd_ext_capab_byte(struct hostapd_data *hapd, u8 *pos, int idx)
*pos |= 0x01;
#endif /* CONFIG_FILS */
break;
+ case 10: /* Bits 80-87 */
+#ifdef CONFIG_SAE
+ if (hapd->conf->wpa &&
+ wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt)) {
+ int in_use = hostapd_sae_pw_id_in_use(hapd->conf);
+
+ if (in_use)
+ *pos |= 0x02; /* Bit 81 - SAE Password
+ * Identifiers In Use */
+ if (in_use == 2)
+ *pos |= 0x04; /* Bit 82 - SAE Password
+ * Identifiers Used Exclusively */
+ }
+#endif /* CONFIG_SAE */
+ break;
}
}
@@ -276,6 +434,12 @@ u8 * hostapd_eid_ext_capab(struct hostapd_data *hapd, u8 *eid)
!wpa_key_mgmt_fils(hapd->conf->wpa_key_mgmt)) && len < 10)
len = 10;
#endif /* CONFIG_FILS */
+#ifdef CONFIG_SAE
+ if (len < 11 && hapd->conf->wpa &&
+ wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt) &&
+ hostapd_sae_pw_id_in_use(hapd->conf))
+ len = 11;
+#endif /* CONFIG_SAE */
if (len < hapd->iface->extended_capa_len)
len = hapd->iface->extended_capa_len;
if (len == 0)
@@ -547,6 +711,22 @@ u8 * hostapd_eid_bss_max_idle_period(struct hostapd_data *hapd, u8 *eid)
#ifdef CONFIG_MBO
+u8 * hostapd_eid_mbo_rssi_assoc_rej(struct hostapd_data *hapd, u8 *eid,
+ size_t len, int delta)
+{
+ u8 mbo[4];
+
+ mbo[0] = OCE_ATTR_ID_RSSI_BASED_ASSOC_REJECT;
+ mbo[1] = 2;
+ /* Delta RSSI */
+ mbo[2] = delta;
+ /* Retry delay */
+ mbo[3] = hapd->iconf->rssi_reject_assoc_timeout;
+
+ return eid + mbo_add_ie(eid, len, mbo, 4);
+}
+
+
u8 * hostapd_eid_mbo(struct hostapd_data *hapd, u8 *eid, size_t len)
{
u8 mbo[9], *mbo_pos = mbo;
@@ -752,3 +932,71 @@ u8 * hostapd_eid_fils_indic(struct hostapd_data *hapd, u8 *eid, int hessid)
return pos;
}
+
+
+#ifdef CONFIG_OCV
+int get_tx_parameters(struct sta_info *sta, int ap_max_chanwidth,
+ int ap_seg1_idx, int *bandwidth, int *seg1_idx)
+{
+ int ht_40mhz = 0;
+ int vht_80p80 = 0;
+ int requested_bw;
+
+ if (sta->ht_capabilities)
+ ht_40mhz = !!(sta->ht_capabilities->ht_capabilities_info &
+ HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET);
+
+ if (sta->vht_operation) {
+ struct ieee80211_vht_operation *oper = sta->vht_operation;
+
+ /*
+ * If a VHT Operation element was present, use it to determine
+ * the supported channel bandwidth.
+ */
+ if (oper->vht_op_info_chwidth == 0) {
+ requested_bw = ht_40mhz ? 40 : 20;
+ } else if (oper->vht_op_info_chan_center_freq_seg1_idx == 0) {
+ requested_bw = 80;
+ } else {
+ int diff;
+
+ requested_bw = 160;
+ diff = abs((int)
+ oper->vht_op_info_chan_center_freq_seg0_idx -
+ (int)
+ oper->vht_op_info_chan_center_freq_seg1_idx);
+ vht_80p80 = oper->vht_op_info_chan_center_freq_seg1_idx
+ != 0 && diff > 16;
+ }
+ } else if (sta->vht_capabilities) {
+ struct ieee80211_vht_capabilities *capab;
+ int vht_chanwidth;
+
+ capab = sta->vht_capabilities;
+
+ /*
+ * If only the VHT Capabilities element is present (e.g., for
+ * normal clients), use it to determine the supported channel
+ * bandwidth.
+ */
+ vht_chanwidth = capab->vht_capabilities_info &
+ VHT_CAP_SUPP_CHAN_WIDTH_MASK;
+ vht_80p80 = capab->vht_capabilities_info &
+ VHT_CAP_SUPP_CHAN_WIDTH_160_80PLUS80MHZ;
+
+ /* TODO: Also take into account Extended NSS BW Support field */
+ requested_bw = vht_chanwidth ? 160 : 80;
+ } else {
+ requested_bw = ht_40mhz ? 40 : 20;
+ }
+
+ *bandwidth = requested_bw < ap_max_chanwidth ?
+ requested_bw : ap_max_chanwidth;
+
+ *seg1_idx = 0;
+ if (ap_seg1_idx && vht_80p80)
+ *seg1_idx = ap_seg1_idx;
+
+ return 0;
+}
+#endif /* CONFIG_OCV */
diff --git a/src/ap/ieee802_11_vht.c b/src/ap/ieee802_11_vht.c
index 8d0662078bcf1..54ee080a43f02 100644
--- a/src/ap/ieee802_11_vht.c
+++ b/src/ap/ieee802_11_vht.c
@@ -357,6 +357,29 @@ u16 copy_sta_vht_capab(struct hostapd_data *hapd, struct sta_info *sta,
}
+u16 copy_sta_vht_oper(struct hostapd_data *hapd, struct sta_info *sta,
+ const u8 *vht_oper)
+{
+ if (!vht_oper) {
+ os_free(sta->vht_operation);
+ sta->vht_operation = NULL;
+ return WLAN_STATUS_SUCCESS;
+ }
+
+ if (!sta->vht_operation) {
+ sta->vht_operation =
+ os_zalloc(sizeof(struct ieee80211_vht_operation));
+ if (!sta->vht_operation)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ os_memcpy(sta->vht_operation, vht_oper,
+ sizeof(struct ieee80211_vht_operation));
+
+ return WLAN_STATUS_SUCCESS;
+}
+
+
u16 copy_sta_vendor_vht(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *ie, size_t len)
{
diff --git a/src/ap/ieee802_1x.c b/src/ap/ieee802_1x.c
index 185279f9e3ef1..97f503f75cc3d 100644
--- a/src/ap/ieee802_1x.c
+++ b/src/ap/ieee802_1x.c
@@ -1,6 +1,6 @@
/*
* hostapd / IEEE 802.1X-2004 Authenticator
- * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -682,9 +682,8 @@ void ieee802_1x_encapsulate_radius(struct hostapd_data *hapd,
#ifdef CONFIG_HS20
if (hapd->conf->hs20) {
- u8 ver = 1; /* Release 2 */
- if (HS20_VERSION > 0x10)
- ver = 2; /* Release 3 */
+ u8 ver = hapd->conf->hs20_release - 1;
+
if (!radius_msg_add_wfa(
msg, RADIUS_VENDOR_ATTR_WFA_HS20_AP_VERSION,
&ver, 1)) {
@@ -1237,6 +1236,7 @@ void ieee802_1x_new_station(struct hostapd_data *hapd, struct sta_info *sta)
sta->eapol_sm->portValid = TRUE;
if (sta->eapol_sm->eap)
eap_sm_notify_cached(sta->eapol_sm->eap);
+ wpa_auth_set_ptk_rekey_timer(sta->wpa_sm);
return;
}
#endif /* CONFIG_FILS */
@@ -1743,6 +1743,45 @@ ieee802_1x_search_radius_identifier(struct hostapd_data *hapd, u8 identifier)
}
+#ifndef CONFIG_NO_VLAN
+static int ieee802_1x_update_vlan(struct radius_msg *msg,
+ struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ struct vlan_description vlan_desc;
+
+ os_memset(&vlan_desc, 0, sizeof(vlan_desc));
+ vlan_desc.notempty = !!radius_msg_get_vlanid(msg, &vlan_desc.untagged,
+ MAX_NUM_TAGGED_VLAN,
+ vlan_desc.tagged);
+
+ if (vlan_desc.notempty &&
+ !hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) {
+ sta->eapol_sm->authFail = TRUE;
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_RADIUS,
+ HOSTAPD_LEVEL_INFO,
+ "Invalid VLAN %d%s received from RADIUS server",
+ vlan_desc.untagged,
+ vlan_desc.tagged[0] ? "+" : "");
+ os_memset(&vlan_desc, 0, sizeof(vlan_desc));
+ ap_sta_set_vlan(hapd, sta, &vlan_desc);
+ return -1;
+ }
+
+ if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED &&
+ !vlan_desc.notempty) {
+ sta->eapol_sm->authFail = TRUE;
+ hostapd_logger(hapd, sta->addr, HOSTAPD_MODULE_IEEE8021X,
+ HOSTAPD_LEVEL_INFO,
+ "authentication server did not include required VLAN ID in Access-Accept");
+ return -1;
+ }
+
+ return ap_sta_set_vlan(hapd, sta, &vlan_desc);
+}
+#endif /* CONFIG_NO_VLAN */
+
+
/**
* ieee802_1x_receive_auth - Process RADIUS frames from Authentication Server
* @msg: RADIUS response message
@@ -1765,12 +1804,6 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
struct eapol_state_machine *sm;
int override_eapReq = 0;
struct radius_hdr *hdr = radius_msg_get_hdr(msg);
- struct vlan_description vlan_desc;
-#ifndef CONFIG_NO_VLAN
- int *untagged, *tagged, *notempty;
-#endif /* CONFIG_NO_VLAN */
-
- os_memset(&vlan_desc, 0, sizeof(vlan_desc));
sm = ieee802_1x_search_radius_identifier(hapd, hdr->identifier);
if (sm == NULL) {
@@ -1835,56 +1868,21 @@ ieee802_1x_receive_auth(struct radius_msg *msg, struct radius_msg *req,
switch (hdr->code) {
case RADIUS_CODE_ACCESS_ACCEPT:
#ifndef CONFIG_NO_VLAN
- if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED) {
- notempty = &vlan_desc.notempty;
- untagged = &vlan_desc.untagged;
- tagged = vlan_desc.tagged;
- *notempty = !!radius_msg_get_vlanid(msg, untagged,
- MAX_NUM_TAGGED_VLAN,
- tagged);
- }
-
- if (vlan_desc.notempty &&
- !hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) {
- sta->eapol_sm->authFail = TRUE;
- hostapd_logger(hapd, sta->addr,
- HOSTAPD_MODULE_RADIUS,
- HOSTAPD_LEVEL_INFO,
- "Invalid VLAN %d%s received from RADIUS server",
- vlan_desc.untagged,
- vlan_desc.tagged[0] ? "+" : "");
- os_memset(&vlan_desc, 0, sizeof(vlan_desc));
- ap_sta_set_vlan(hapd, sta, &vlan_desc);
- break;
- }
-
- if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_REQUIRED &&
- !vlan_desc.notempty) {
- sta->eapol_sm->authFail = TRUE;
- hostapd_logger(hapd, sta->addr,
- HOSTAPD_MODULE_IEEE8021X,
- HOSTAPD_LEVEL_INFO, "authentication "
- "server did not include required VLAN "
- "ID in Access-Accept");
- break;
- }
-#endif /* CONFIG_NO_VLAN */
-
- if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0)
+ if (hapd->conf->ssid.dynamic_vlan != DYNAMIC_VLAN_DISABLED &&
+ ieee802_1x_update_vlan(msg, hapd, sta) < 0)
break;
-#ifndef CONFIG_NO_VLAN
if (sta->vlan_id > 0) {
hostapd_logger(hapd, sta->addr,
HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_INFO,
"VLAN ID %d", sta->vlan_id);
}
-#endif /* CONFIG_NO_VLAN */
if ((sta->flags & WLAN_STA_ASSOC) &&
ap_sta_bind_vlan(hapd, sta) < 0)
break;
+#endif /* CONFIG_NO_VLAN */
sta->session_timeout_set = !!session_timeout_set;
os_get_reltime(&sta->session_timeout);
@@ -2597,6 +2595,7 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
struct os_reltime diff;
const char *name1;
const char *name2;
+ char *identity_buf = NULL;
if (sm == NULL)
return 0;
@@ -2712,6 +2711,14 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
/* dot1xAuthSessionStatsTable */
os_reltime_age(&sta->acct_session_start, &diff);
+ if (sm->eap && !sm->identity) {
+ const u8 *id;
+ size_t id_len;
+
+ id = eap_get_identity(sm->eap, &id_len);
+ if (id)
+ identity_buf = dup_binstr(id, id_len);
+ }
ret = os_snprintf(buf + len, buflen - len,
/* TODO: dot1xAuthSessionOctetsRx */
/* TODO: dot1xAuthSessionOctetsTx */
@@ -2727,7 +2734,9 @@ int ieee802_1x_get_mib_sta(struct hostapd_data *hapd, struct sta_info *sta,
wpa_auth_sta_key_mgmt(sta->wpa_sm))) ?
1 : 2,
(unsigned int) diff.sec,
- sm->identity);
+ sm->identity ? (char *) sm->identity :
+ (identity_buf ? identity_buf : "N/A"));
+ os_free(identity_buf);
if (os_snprintf_error(buflen - len, ret))
return len;
len += ret;
diff --git a/src/ap/neighbor_db.c b/src/ap/neighbor_db.c
index b8fd5924b8893..2b6f72726b64e 100644
--- a/src/ap/neighbor_db.c
+++ b/src/ap/neighbor_db.c
@@ -11,6 +11,7 @@
#include "utils/common.h"
#include "hostapd.h"
+#include "ieee802_11.h"
#include "neighbor_db.h"
@@ -123,7 +124,7 @@ int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
}
-void hostpad_free_neighbor_db(struct hostapd_data *hapd)
+void hostapd_free_neighbor_db(struct hostapd_data *hapd)
{
struct hostapd_neighbor_entry *nr, *prev;
@@ -134,3 +135,123 @@ void hostpad_free_neighbor_db(struct hostapd_data *hapd)
os_free(nr);
}
}
+
+
+#ifdef NEED_AP_MLME
+static enum nr_chan_width hostapd_get_nr_chan_width(struct hostapd_data *hapd,
+ int ht, int vht)
+{
+ if (!ht && !vht)
+ return NR_CHAN_WIDTH_20;
+ if (!hapd->iconf->secondary_channel)
+ return NR_CHAN_WIDTH_20;
+ if (!vht || hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_USE_HT)
+ return NR_CHAN_WIDTH_40;
+ if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80MHZ)
+ return NR_CHAN_WIDTH_80;
+ if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_160MHZ)
+ return NR_CHAN_WIDTH_160;
+ if (hapd->iconf->vht_oper_chwidth == VHT_CHANWIDTH_80P80MHZ)
+ return NR_CHAN_WIDTH_80P80;
+ return NR_CHAN_WIDTH_20;
+}
+#endif /* NEED_AP_MLME */
+
+
+void hostapd_neighbor_set_own_report(struct hostapd_data *hapd)
+{
+#ifdef NEED_AP_MLME
+ u16 capab = hostapd_own_capab_info(hapd);
+ int ht = hapd->iconf->ieee80211n && !hapd->conf->disable_11n;
+ int vht = hapd->iconf->ieee80211ac && !hapd->conf->disable_11ac;
+ struct wpa_ssid_value ssid;
+ u8 channel, op_class;
+ u8 center_freq1_idx = 0, center_freq2_idx = 0;
+ enum nr_chan_width width;
+ u32 bssid_info;
+ struct wpabuf *nr;
+
+ if (!(hapd->conf->radio_measurements[0] &
+ WLAN_RRM_CAPS_NEIGHBOR_REPORT))
+ return;
+
+ bssid_info = 3; /* AP is reachable */
+ bssid_info |= NEI_REP_BSSID_INFO_SECURITY; /* "same as the AP" */
+ bssid_info |= NEI_REP_BSSID_INFO_KEY_SCOPE; /* "same as the AP" */
+
+ if (capab & WLAN_CAPABILITY_SPECTRUM_MGMT)
+ bssid_info |= NEI_REP_BSSID_INFO_SPECTRUM_MGMT;
+
+ bssid_info |= NEI_REP_BSSID_INFO_RM; /* RRM is supported */
+
+ if (hapd->conf->wmm_enabled) {
+ bssid_info |= NEI_REP_BSSID_INFO_QOS;
+
+ if (hapd->conf->wmm_uapsd &&
+ (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_AP_UAPSD))
+ bssid_info |= NEI_REP_BSSID_INFO_APSD;
+ }
+
+ if (ht) {
+ bssid_info |= NEI_REP_BSSID_INFO_HT |
+ NEI_REP_BSSID_INFO_DELAYED_BA;
+
+ /* VHT bit added in IEEE P802.11-REVmc/D4.3 */
+ if (vht)
+ bssid_info |= NEI_REP_BSSID_INFO_VHT;
+ }
+
+ /* TODO: Set NEI_REP_BSSID_INFO_MOBILITY_DOMAIN if MDE is set */
+
+ if (ieee80211_freq_to_channel_ext(hapd->iface->freq,
+ hapd->iconf->secondary_channel,
+ hapd->iconf->vht_oper_chwidth,
+ &op_class, &channel) ==
+ NUM_HOSTAPD_MODES)
+ return;
+ width = hostapd_get_nr_chan_width(hapd, ht, vht);
+ if (vht) {
+ center_freq1_idx = hapd->iconf->vht_oper_centr_freq_seg0_idx;
+ if (width == NR_CHAN_WIDTH_80P80)
+ center_freq2_idx =
+ hapd->iconf->vht_oper_centr_freq_seg1_idx;
+ } else if (ht) {
+ ieee80211_freq_to_chan(hapd->iface->freq +
+ 10 * hapd->iconf->secondary_channel,
+ &center_freq1_idx);
+ }
+
+ ssid.ssid_len = hapd->conf->ssid.ssid_len;
+ os_memcpy(ssid.ssid, hapd->conf->ssid.ssid, ssid.ssid_len);
+
+ /*
+ * Neighbor Report element size = BSSID + BSSID info + op_class + chan +
+ * phy type + wide bandwidth channel subelement.
+ */
+ nr = wpabuf_alloc(ETH_ALEN + 4 + 1 + 1 + 1 + 5);
+ if (!nr)
+ return;
+
+ wpabuf_put_data(nr, hapd->own_addr, ETH_ALEN);
+ wpabuf_put_le32(nr, bssid_info);
+ wpabuf_put_u8(nr, op_class);
+ wpabuf_put_u8(nr, channel);
+ wpabuf_put_u8(nr, ieee80211_get_phy_type(hapd->iface->freq, ht, vht));
+
+ /*
+ * Wide Bandwidth Channel subelement may be needed to allow the
+ * receiving STA to send packets to the AP. See IEEE P802.11-REVmc/D5.0
+ * Figure 9-301.
+ */
+ wpabuf_put_u8(nr, WNM_NEIGHBOR_WIDE_BW_CHAN);
+ wpabuf_put_u8(nr, 3);
+ wpabuf_put_u8(nr, width);
+ wpabuf_put_u8(nr, center_freq1_idx);
+ wpabuf_put_u8(nr, center_freq2_idx);
+
+ hostapd_neighbor_set(hapd, hapd->own_addr, &ssid, nr, hapd->iconf->lci,
+ hapd->iconf->civic, hapd->iconf->stationary_ap);
+
+ wpabuf_free(nr);
+#endif /* NEED_AP_MLME */
+}
diff --git a/src/ap/neighbor_db.h b/src/ap/neighbor_db.h
index ba46d88435e56..9c8f4f2dd69d6 100644
--- a/src/ap/neighbor_db.h
+++ b/src/ap/neighbor_db.h
@@ -17,8 +17,9 @@ int hostapd_neighbor_set(struct hostapd_data *hapd, const u8 *bssid,
const struct wpa_ssid_value *ssid,
const struct wpabuf *nr, const struct wpabuf *lci,
const struct wpabuf *civic, int stationary);
+void hostapd_neighbor_set_own_report(struct hostapd_data *hapd);
int hostapd_neighbor_remove(struct hostapd_data *hapd, const u8 *bssid,
const struct wpa_ssid_value *ssid);
-void hostpad_free_neighbor_db(struct hostapd_data *hapd);
+void hostapd_free_neighbor_db(struct hostapd_data *hapd);
#endif /* NEIGHBOR_DB_H */
diff --git a/src/ap/rrm.c b/src/ap/rrm.c
index 56ed29c1c0687..f2d5cd16e8858 100644
--- a/src/ap/rrm.c
+++ b/src/ap/rrm.c
@@ -558,7 +558,7 @@ int hostapd_send_range_req(struct hostapd_data *hapd, const u8 *addr,
void hostapd_clean_rrm(struct hostapd_data *hapd)
{
- hostpad_free_neighbor_db(hapd);
+ hostapd_free_neighbor_db(hapd);
eloop_cancel_timeout(hostapd_lci_rep_timeout_handler, hapd, NULL);
hapd->lci_req_active = 0;
eloop_cancel_timeout(hostapd_range_rep_timeout_handler, hapd, NULL);
diff --git a/src/ap/sta_info.c b/src/ap/sta_info.c
index 179cf43b60b17..4f9eae8477b63 100644
--- a/src/ap/sta_info.c
+++ b/src/ap/sta_info.c
@@ -13,6 +13,7 @@
#include "common/ieee802_11_defs.h"
#include "common/wpa_ctrl.h"
#include "common/sae.h"
+#include "common/dpp.h"
#include "radius/radius.h"
#include "radius/radius_client.h"
#include "p2p/p2p.h"
@@ -166,7 +167,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
/* just in case */
ap_sta_set_authorized(hapd, sta, 0);
- if (sta->flags & WLAN_STA_WDS)
+ if (sta->flags & (WLAN_STA_WDS | WLAN_STA_MULTI_AP))
hostapd_set_wds_sta(hapd, NULL, sta->addr, sta->aid, 0);
if (sta->ipaddr)
@@ -328,6 +329,7 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
os_free(sta->ht_capabilities);
os_free(sta->vht_capabilities);
+ os_free(sta->vht_operation);
hostapd_free_psk_list(sta->psk);
os_free(sta->identity);
os_free(sta->radius_cui);
@@ -361,6 +363,11 @@ void ap_free_sta(struct hostapd_data *hapd, struct sta_info *sta)
crypto_ecdh_deinit(sta->owe_ecdh);
#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP2
+ dpp_pfs_free(sta->dpp_pfs);
+ sta->dpp_pfs = NULL;
+#endif /* CONFIG_DPP2 */
+
os_free(sta->ext_capability);
#ifdef CONFIG_WNM_AP
@@ -500,6 +507,13 @@ skip_poll:
} else if (sta->timeout_next != STA_REMOVE) {
int deauth = sta->timeout_next == STA_DEAUTH;
+ if (!deauth && !(sta->flags & WLAN_STA_ASSOC)) {
+ /* Cannot disassociate not-associated STA, so move
+ * directly to deauthentication. */
+ sta->timeout_next = STA_DEAUTH;
+ deauth = 1;
+ }
+
wpa_dbg(hapd->msg_ctx, MSG_DEBUG,
"Timeout, sending %s info to STA " MACSTR,
deauth ? "deauthentication" : "disassociation",
@@ -798,6 +812,8 @@ void ap_sta_disassociate(struct hostapd_data *hapd, struct sta_info *sta,
ap_handle_timer, hapd, sta);
accounting_sta_stop(hapd, sta);
ieee802_1x_free_station(hapd, sta);
+ wpa_auth_sta_deinit(sta->wpa_sm);
+ sta->wpa_sm = NULL;
sta->disassoc_reason = reason;
sta->flags |= WLAN_STA_PENDING_DISASSOC_CB;
@@ -896,9 +912,6 @@ int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta,
struct hostapd_vlan *vlan = NULL, *wildcard_vlan = NULL;
int old_vlan_id, vlan_id = 0, ret = 0;
- if (hapd->conf->ssid.dynamic_vlan == DYNAMIC_VLAN_DISABLED)
- vlan_desc = NULL;
-
/* Check if there is something to do */
if (hapd->conf->ssid.per_sta_vif && !sta->vlan_id) {
/* This sta is lacking its own vif */
@@ -1165,6 +1178,32 @@ void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta)
#endif /* CONFIG_IEEE80211W */
+const char * ap_sta_wpa_get_keyid(struct hostapd_data *hapd,
+ struct sta_info *sta)
+{
+ struct hostapd_wpa_psk *psk;
+ struct hostapd_ssid *ssid;
+ const u8 *pmk;
+ int pmk_len;
+
+ ssid = &hapd->conf->ssid;
+
+ pmk = wpa_auth_get_pmk(sta->wpa_sm, &pmk_len);
+ if (!pmk || pmk_len != PMK_LEN)
+ return NULL;
+
+ for (psk = ssid->wpa_psk; psk; psk = psk->next)
+ if (os_memcmp(pmk, psk->psk, PMK_LEN) == 0)
+ break;
+ if (!psk)
+ return NULL;
+ if (!psk || !psk->keyid[0])
+ return NULL;
+
+ return psk->keyid;
+}
+
+
void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
int authorized)
{
@@ -1203,7 +1242,11 @@ void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
sta->addr, authorized, dev_addr);
if (authorized) {
+ const char *keyid;
+ char keyid_buf[100];
char ip_addr[100];
+
+ keyid_buf[0] = '\0';
ip_addr[0] = '\0';
#ifdef CONFIG_P2P
if (wpa_auth_get_ip_addr(sta->wpa_sm, ip_addr_buf) == 0) {
@@ -1214,14 +1257,20 @@ void ap_sta_set_authorized(struct hostapd_data *hapd, struct sta_info *sta,
}
#endif /* CONFIG_P2P */
- wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s",
- buf, ip_addr);
+ keyid = ap_sta_wpa_get_keyid(hapd, sta);
+ if (keyid) {
+ os_snprintf(keyid_buf, sizeof(keyid_buf),
+ " keyid=%s", keyid);
+ }
+
+ wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_CONNECTED "%s%s%s",
+ buf, ip_addr, keyid_buf);
if (hapd->msg_ctx_parent &&
hapd->msg_ctx_parent != hapd->msg_ctx)
wpa_msg_no_global(hapd->msg_ctx_parent, MSG_INFO,
- AP_STA_CONNECTED "%s%s",
- buf, ip_addr);
+ AP_STA_CONNECTED "%s%s%s",
+ buf, ip_addr, keyid_buf);
} else {
wpa_msg(hapd->msg_ctx, MSG_INFO, AP_STA_DISCONNECTED "%s", buf);
diff --git a/src/ap/sta_info.h b/src/ap/sta_info.h
index 9cac6f157f68f..ece0c60abd364 100644
--- a/src/ap/sta_info.h
+++ b/src/ap/sta_info.h
@@ -36,6 +36,7 @@
#define WLAN_STA_VHT_OPMODE_ENABLED BIT(20)
#define WLAN_STA_VENDOR_VHT BIT(21)
#define WLAN_STA_PENDING_FILS_ERP BIT(22)
+#define WLAN_STA_MULTI_AP BIT(23)
#define WLAN_STA_PENDING_DISASSOC_CB BIT(29)
#define WLAN_STA_PENDING_DEAUTH_CB BIT(30)
#define WLAN_STA_NONERP BIT(31)
@@ -117,6 +118,7 @@ struct sta_info {
unsigned int power_capab:1;
unsigned int agreed_to_steer:1;
unsigned int hs20_t_c_filtering:1;
+ unsigned int ft_over_ds:1;
u16 auth_alg;
@@ -162,6 +164,7 @@ struct sta_info {
struct ieee80211_ht_capabilities *ht_capabilities;
struct ieee80211_vht_capabilities *vht_capabilities;
+ struct ieee80211_vht_operation *vht_operation;
u8 vht_opmode;
#ifdef CONFIG_IEEE80211W
@@ -215,6 +218,7 @@ struct sta_info {
u8 cell_capa; /* 0 = unknown (not an MBO STA); otherwise,
* enum mbo_cellular_capa values */
struct mbo_non_pref_chan_info *non_pref_chan;
+ int auth_rssi; /* Last Authentication frame RSSI */
#endif /* CONFIG_MBO */
u8 *supp_op_classes; /* Supported Operating Classes element, if
@@ -261,6 +265,10 @@ struct sta_info {
u8 *ext_capability;
char *ifname_wds; /* WDS ifname, if in use */
+#ifdef CONFIG_DPP2
+ struct dpp_pfs *dpp_pfs;
+#endif /* CONFIG_DPP2 */
+
#ifdef CONFIG_TESTING_OPTIONS
enum wpa_alg last_tk_alg;
int last_tk_key_idx;
@@ -320,6 +328,8 @@ int ap_sta_set_vlan(struct hostapd_data *hapd, struct sta_info *sta,
void ap_sta_start_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
void ap_sta_stop_sa_query(struct hostapd_data *hapd, struct sta_info *sta);
int ap_check_sa_query_timeout(struct hostapd_data *hapd, struct sta_info *sta);
+const char * ap_sta_wpa_get_keyid(struct hostapd_data *hapd,
+ struct sta_info *sta);
void ap_sta_disconnect(struct hostapd_data *hapd, struct sta_info *sta,
const u8 *addr, u16 reason);
diff --git a/src/ap/vlan_full.c b/src/ap/vlan_full.c
index aa42335b96a19..19aa3c649a5db 100644
--- a/src/ap/vlan_full.c
+++ b/src/ap/vlan_full.c
@@ -16,6 +16,7 @@
#include "utils/common.h"
#include "drivers/priv_netlink.h"
+#include "drivers/linux_ioctl.h"
#include "common/linux_bridge.h"
#include "common/linux_vlan.h"
#include "utils/eloop.h"
@@ -143,6 +144,9 @@ static int br_delif(const char *br_name, const char *if_name)
return -1;
}
+ if (linux_br_del_if(fd, br_name, if_name) == 0)
+ goto done;
+
if_index = if_nametoindex(if_name);
if (if_index == 0) {
@@ -168,6 +172,7 @@ static int br_delif(const char *br_name, const char *if_name)
return -1;
}
+done:
close(fd);
return 0;
}
@@ -194,6 +199,14 @@ static int br_addif(const char *br_name, const char *if_name)
return -1;
}
+ if (linux_br_add_if(fd, br_name, if_name) == 0)
+ goto done;
+ if (errno == EBUSY) {
+ /* The interface is already added. */
+ close(fd);
+ return 1;
+ }
+
if_index = if_nametoindex(if_name);
if (if_index == 0) {
@@ -224,6 +237,7 @@ static int br_addif(const char *br_name, const char *if_name)
return -1;
}
+done:
close(fd);
return 0;
}
@@ -241,6 +255,9 @@ static int br_delbr(const char *br_name)
return -1;
}
+ if (linux_br_del(fd, br_name) == 0)
+ goto done;
+
arg[0] = BRCTL_DEL_BRIDGE;
arg[1] = (unsigned long) br_name;
@@ -252,6 +269,7 @@ static int br_delbr(const char *br_name)
return -1;
}
+done:
close(fd);
return 0;
}
@@ -277,11 +295,19 @@ static int br_addbr(const char *br_name)
return -1;
}
+ if (linux_br_add(fd, br_name) == 0)
+ goto done;
+ if (errno == EEXIST) {
+ /* The bridge is already added. */
+ close(fd);
+ return 1;
+ }
+
arg[0] = BRCTL_ADD_BRIDGE;
arg[1] = (unsigned long) br_name;
if (ioctl(fd, SIOCGIFBR, arg) < 0) {
- if (errno == EEXIST) {
+ if (errno == EEXIST) {
/* The bridge is already added. */
close(fd);
return 1;
@@ -294,6 +320,7 @@ static int br_addbr(const char *br_name)
}
}
+done:
/* Decrease forwarding delay to avoid EAPOL timeouts. */
os_memset(&ifr, 0, sizeof(ifr));
os_strlcpy(ifr.ifr_name, br_name, IFNAMSIZ);
@@ -363,12 +390,18 @@ static void vlan_newlink_tagged(int vlan_naming, const char *tagged_interface,
{
char vlan_ifname[IFNAMSIZ];
int clean;
+ int ret;
if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE)
- os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d",
- tagged_interface, vid);
+ ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d",
+ tagged_interface, vid);
else
- os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", vid);
+ ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d",
+ vid);
+ if (ret >= (int) sizeof(vlan_ifname))
+ wpa_printf(MSG_WARNING,
+ "VLAN: Interface name was truncated to %s",
+ vlan_ifname);
clean = 0;
ifconfig_up(tagged_interface);
@@ -384,19 +417,28 @@ static void vlan_newlink_tagged(int vlan_naming, const char *tagged_interface,
}
-static void vlan_bridge_name(char *br_name, struct hostapd_data *hapd, int vid)
+static void vlan_bridge_name(char *br_name, struct hostapd_data *hapd,
+ struct hostapd_vlan *vlan, int vid)
{
char *tagged_interface = hapd->conf->ssid.vlan_tagged_interface;
-
- if (hapd->conf->vlan_bridge[0]) {
- os_snprintf(br_name, IFNAMSIZ, "%s%d",
- hapd->conf->vlan_bridge, vid);
+ int ret;
+
+ if (vlan->bridge[0]) {
+ os_strlcpy(br_name, vlan->bridge, IFNAMSIZ);
+ ret = 0;
+ } else if (hapd->conf->vlan_bridge[0]) {
+ ret = os_snprintf(br_name, IFNAMSIZ, "%s%d",
+ hapd->conf->vlan_bridge, vid);
} else if (tagged_interface) {
- os_snprintf(br_name, IFNAMSIZ, "br%s.%d",
- tagged_interface, vid);
+ ret = os_snprintf(br_name, IFNAMSIZ, "br%s.%d",
+ tagged_interface, vid);
} else {
- os_snprintf(br_name, IFNAMSIZ, "brvlan%d", vid);
+ ret = os_snprintf(br_name, IFNAMSIZ, "brvlan%d", vid);
}
+ if (ret >= IFNAMSIZ)
+ wpa_printf(MSG_WARNING,
+ "VLAN: Interface name was truncated to %s",
+ br_name);
}
@@ -445,7 +487,7 @@ void vlan_newlink(const char *ifname, struct hostapd_data *hapd)
!br_addif(hapd->conf->bridge, ifname))
vlan->clean |= DVLAN_CLEAN_WLAN_PORT;
} else if (untagged > 0 && untagged <= MAX_VLAN_ID) {
- vlan_bridge_name(br_name, hapd, untagged);
+ vlan_bridge_name(br_name, hapd, vlan, untagged);
vlan_get_bridge(br_name, hapd, untagged);
@@ -458,7 +500,7 @@ void vlan_newlink(const char *ifname, struct hostapd_data *hapd)
tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID ||
(i > 0 && tagged[i] == tagged[i - 1]))
continue;
- vlan_bridge_name(br_name, hapd, tagged[i]);
+ vlan_bridge_name(br_name, hapd, vlan, tagged[i]);
vlan_get_bridge(br_name, hapd, tagged[i]);
vlan_newlink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE,
ifname, br_name, tagged[i], hapd);
@@ -474,12 +516,19 @@ static void vlan_dellink_tagged(int vlan_naming, const char *tagged_interface,
{
char vlan_ifname[IFNAMSIZ];
int clean;
+ int ret;
if (vlan_naming == DYNAMIC_VLAN_NAMING_WITH_DEVICE)
- os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d",
- tagged_interface, vid);
+ ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "%s.%d",
+ tagged_interface, vid);
else
- os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d", vid);
+ ret = os_snprintf(vlan_ifname, sizeof(vlan_ifname), "vlan%d",
+ vid);
+ if (ret >= (int) sizeof(vlan_ifname))
+ wpa_printf(MSG_WARNING,
+ "VLAN: Interface name was truncated to %s",
+ vlan_ifname);
+
clean = dyn_iface_put(hapd, vlan_ifname);
@@ -543,7 +592,7 @@ void vlan_dellink(const char *ifname, struct hostapd_data *hapd)
tagged[i] <= 0 || tagged[i] > MAX_VLAN_ID ||
(i > 0 && tagged[i] == tagged[i - 1]))
continue;
- vlan_bridge_name(br_name, hapd, tagged[i]);
+ vlan_bridge_name(br_name, hapd, vlan, tagged[i]);
vlan_dellink_tagged(DYNAMIC_VLAN_NAMING_WITH_DEVICE,
ifname, br_name, tagged[i], hapd);
vlan_put_bridge(br_name, hapd, tagged[i]);
@@ -555,7 +604,7 @@ void vlan_dellink(const char *ifname, struct hostapd_data *hapd)
(vlan->clean & DVLAN_CLEAN_WLAN_PORT))
br_delif(hapd->conf->bridge, ifname);
} else if (untagged > 0 && untagged <= MAX_VLAN_ID) {
- vlan_bridge_name(br_name, hapd, untagged);
+ vlan_bridge_name(br_name, hapd, vlan, untagged);
if (vlan->clean & DVLAN_CLEAN_WLAN_PORT)
br_delif(br_name, vlan->ifname);
diff --git a/src/ap/vlan_init.c b/src/ap/vlan_init.c
index 01fecee026a11..e293a003303f9 100644
--- a/src/ap/vlan_init.c
+++ b/src/ap/vlan_init.c
@@ -187,6 +187,7 @@ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
{
struct hostapd_vlan *n;
char ifname[IFNAMSIZ + 1], *pos;
+ int ret;
if (vlan == NULL || vlan->vlan_id != VLAN_ID_WILDCARD)
return NULL;
@@ -208,8 +209,13 @@ struct hostapd_vlan * vlan_add_dynamic(struct hostapd_data *hapd,
n->vlan_desc = *vlan_desc;
n->dynamic_vlan = 1;
- os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s", ifname, vlan_id,
- pos);
+ ret = os_snprintf(n->ifname, sizeof(n->ifname), "%s%d%s",
+ ifname, vlan_id, pos);
+ if (os_snprintf_error(sizeof(n->ifname), ret)) {
+ os_free(n);
+ return NULL;
+ }
+ os_strlcpy(n->bridge, vlan->bridge, sizeof(n->bridge));
n->next = hapd->conf->vlan;
hapd->conf->vlan = n;
diff --git a/src/ap/wnm_ap.c b/src/ap/wnm_ap.c
index 1e8f58b4d07ee..27c69d34a7caa 100644
--- a/src/ap/wnm_ap.c
+++ b/src/ap/wnm_ap.c
@@ -12,6 +12,7 @@
#include "utils/eloop.h"
#include "common/ieee802_11_defs.h"
#include "common/wpa_ctrl.h"
+#include "common/ocv.h"
#include "ap/hostapd.h"
#include "ap/sta_info.h"
#include "ap/ap_config.h"
@@ -54,8 +55,8 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
size_t gtk_elem_len = 0;
size_t igtk_elem_len = 0;
struct wnm_sleep_element wnmsleep_ie;
- u8 *wnmtfs_ie;
- u8 wnmsleep_ie_len;
+ u8 *wnmtfs_ie, *oci_ie;
+ u8 wnmsleep_ie_len, oci_ie_len;
u16 wnmtfs_ie_len;
u8 *pos;
struct sta_info *sta;
@@ -88,10 +89,42 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
wnmtfs_ie = NULL;
}
+ oci_ie = NULL;
+ oci_ie_len = 0;
+#ifdef CONFIG_OCV
+ if (action_type == WNM_SLEEP_MODE_EXIT &&
+ wpa_auth_uses_ocv(sta->wpa_sm)) {
+ struct wpa_channel_info ci;
+
+ if (hostapd_drv_channel_info(hapd, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "Failed to get channel info for OCI element in WNM-Sleep Mode frame");
+ os_free(wnmtfs_ie);
+ return -1;
+ }
+
+ oci_ie_len = OCV_OCI_EXTENDED_LEN;
+ oci_ie = os_zalloc(oci_ie_len);
+ if (!oci_ie) {
+ wpa_printf(MSG_WARNING,
+ "Failed to allocate buffer for OCI element in WNM-Sleep Mode frame");
+ os_free(wnmtfs_ie);
+ return -1;
+ }
+
+ if (ocv_insert_extended_oci(&ci, oci_ie) < 0) {
+ os_free(wnmtfs_ie);
+ os_free(oci_ie);
+ return -1;
+ }
+ }
+#endif /* CONFIG_OCV */
+
#define MAX_GTK_SUBELEM_LEN 45
#define MAX_IGTK_SUBELEM_LEN 26
mgmt = os_zalloc(sizeof(*mgmt) + wnmsleep_ie_len +
- MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN);
+ MAX_GTK_SUBELEM_LEN + MAX_IGTK_SUBELEM_LEN +
+ oci_ie_len);
if (mgmt == NULL) {
wpa_printf(MSG_DEBUG, "MLME: Failed to allocate buffer for "
"WNM-Sleep Response action frame");
@@ -134,11 +167,18 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
os_memcpy(pos, &wnmsleep_ie, wnmsleep_ie_len);
/* copy TFS IE here */
pos += wnmsleep_ie_len;
- if (wnmtfs_ie)
+ if (wnmtfs_ie) {
os_memcpy(pos, wnmtfs_ie, wnmtfs_ie_len);
+ pos += wnmtfs_ie_len;
+ }
+#ifdef CONFIG_OCV
+ /* copy OCV OCI here */
+ if (oci_ie_len > 0)
+ os_memcpy(pos, oci_ie, oci_ie_len);
+#endif /* CONFIG_OCV */
len = 1 + sizeof(mgmt->u.action.u.wnm_sleep_resp) + gtk_elem_len +
- igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len;
+ igtk_elem_len + wnmsleep_ie_len + wnmtfs_ie_len + oci_ie_len;
/* In driver, response frame should be forced to sent when STA is in
* PS mode */
@@ -185,6 +225,7 @@ static int ieee802_11_send_wnmsleep_resp(struct hostapd_data *hapd,
#undef MAX_IGTK_SUBELEM_LEN
fail:
os_free(wnmtfs_ie);
+ os_free(oci_ie);
os_free(mgmt);
return res;
}
@@ -201,6 +242,11 @@ static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd,
u8 *tfsreq_ie_start = NULL;
u8 *tfsreq_ie_end = NULL;
u16 tfsreq_ie_len = 0;
+#ifdef CONFIG_OCV
+ struct sta_info *sta;
+ const u8 *oci_ie = NULL;
+ u8 oci_ie_len = 0;
+#endif /* CONFIG_OCV */
if (!hapd->conf->wnm_sleep_mode) {
wpa_printf(MSG_DEBUG, "Ignore WNM-Sleep Mode Request from "
@@ -209,6 +255,13 @@ static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd,
return;
}
+ if (len < 1) {
+ wpa_printf(MSG_DEBUG,
+ "WNM: Ignore too short WNM-Sleep Mode Request from "
+ MACSTR, MAC2STR(addr));
+ return;
+ }
+
dialog_token = *pos++;
while (pos + 1 < frm + len) {
u8 ie_len = pos[1];
@@ -221,6 +274,12 @@ static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd,
if (!tfsreq_ie_start)
tfsreq_ie_start = (u8 *) pos;
tfsreq_ie_end = (u8 *) pos;
+#ifdef CONFIG_OCV
+ } else if (*pos == WLAN_EID_EXTENSION && ie_len >= 1 &&
+ pos[2] == WLAN_EID_EXT_OCV_OCI) {
+ oci_ie = pos + 3;
+ oci_ie_len = ie_len - 1;
+#endif /* CONFIG_OCV */
} else
wpa_printf(MSG_DEBUG, "WNM: EID %d not recognized",
*pos);
@@ -232,6 +291,27 @@ static void ieee802_11_rx_wnmsleep_req(struct hostapd_data *hapd,
return;
}
+#ifdef CONFIG_OCV
+ sta = ap_get_sta(hapd, addr);
+ if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_EXIT &&
+ sta && wpa_auth_uses_ocv(sta->wpa_sm)) {
+ struct wpa_channel_info ci;
+
+ if (hostapd_drv_channel_info(hapd, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "Failed to get channel info to validate received OCI in WNM-Sleep Mode frame");
+ return;
+ }
+
+ if (ocv_verify_tx_params(oci_ie, oci_ie_len, &ci,
+ channel_width_to_int(ci.chanwidth),
+ ci.seg1_idx) != 0) {
+ wpa_msg(hapd, MSG_WARNING, "WNM: %s", ocv_errorstr);
+ return;
+ }
+ }
+#endif /* CONFIG_OCV */
+
if (wnmsleep_ie->action_type == WNM_SLEEP_MODE_ENTER &&
tfsreq_ie_start && tfsreq_ie_end &&
tfsreq_ie_end - tfsreq_ie_start >= 0) {
diff --git a/src/ap/wpa_auth.c b/src/ap/wpa_auth.c
index 34969e79e46ff..f2e028c1599e8 100644
--- a/src/ap/wpa_auth.c
+++ b/src/ap/wpa_auth.c
@@ -1,6 +1,6 @@
/*
* IEEE 802.11 RSN / WPA Authenticator
- * Copyright (c) 2004-2018, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -13,6 +13,7 @@
#include "utils/state_machine.h"
#include "utils/bitfield.h"
#include "common/ieee802_11_defs.h"
+#include "common/ocv.h"
#include "crypto/aes.h"
#include "crypto/aes_wrap.h"
#include "crypto/aes_siv.h"
@@ -22,6 +23,7 @@
#include "crypto/sha384.h"
#include "crypto/random.h"
#include "eapol_auth/eapol_auth_sm.h"
+#include "drivers/driver.h"
#include "ap_config.h"
#include "ieee802_11.h"
#include "wpa_auth.h"
@@ -112,12 +114,13 @@ static inline int wpa_auth_get_eapol(struct wpa_authenticator *wpa_auth,
static inline const u8 * wpa_auth_get_psk(struct wpa_authenticator *wpa_auth,
const u8 *addr,
const u8 *p2p_dev_addr,
- const u8 *prev_psk, size_t *psk_len)
+ const u8 *prev_psk, size_t *psk_len,
+ int *vlan_id)
{
if (wpa_auth->cb->get_psk == NULL)
return NULL;
return wpa_auth->cb->get_psk(wpa_auth->cb_ctx, addr, p2p_dev_addr,
- prev_psk, psk_len);
+ prev_psk, psk_len, vlan_id);
}
@@ -238,6 +241,26 @@ static void wpa_sta_disconnect(struct wpa_authenticator *wpa_auth,
}
+#ifdef CONFIG_OCV
+static int wpa_channel_info(struct wpa_authenticator *wpa_auth,
+ struct wpa_channel_info *ci)
+{
+ if (!wpa_auth->cb->channel_info)
+ return -1;
+ return wpa_auth->cb->channel_info(wpa_auth->cb_ctx, ci);
+}
+#endif /* CONFIG_OCV */
+
+
+static int wpa_auth_update_vlan(struct wpa_authenticator *wpa_auth,
+ const u8 *addr, int vlan_id)
+{
+ if (!wpa_auth->cb->update_vlan)
+ return -1;
+ return wpa_auth->cb->update_vlan(wpa_auth->cb_ctx, addr, vlan_id);
+}
+
+
static void wpa_rekey_gmk(void *eloop_ctx, void *timeout_ctx)
{
struct wpa_authenticator *wpa_auth = eloop_ctx;
@@ -297,6 +320,19 @@ static void wpa_rekey_ptk(void *eloop_ctx, void *timeout_ctx)
}
+void wpa_auth_set_ptk_rekey_timer(struct wpa_state_machine *sm)
+{
+ if (sm && sm->wpa_auth->conf.wpa_ptk_rekey) {
+ wpa_printf(MSG_DEBUG, "WPA: Start PTK rekeying timer for "
+ MACSTR " (%d seconds)", MAC2STR(sm->addr),
+ sm->wpa_auth->conf.wpa_ptk_rekey);
+ eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
+ eloop_register_timeout(sm->wpa_auth->conf.wpa_ptk_rekey, 0,
+ wpa_rekey_ptk, sm->wpa_auth, sm);
+ }
+}
+
+
static int wpa_auth_pmksa_clear_cb(struct wpa_state_machine *sm, void *ctx)
{
if (sm->pmksa == ctx)
@@ -332,6 +368,10 @@ static int wpa_group_init_gmk_and_counter(struct wpa_authenticator *wpa_auth,
wpa_get_ntp_timestamp(buf + ETH_ALEN);
ptr = (unsigned long) group;
os_memcpy(buf + ETH_ALEN + 8, &ptr, sizeof(ptr));
+#ifdef TEST_FUZZ
+ os_memset(buf + ETH_ALEN, 0xab, 8);
+ os_memset(buf + ETH_ALEN + 8, 0xcd, sizeof(ptr));
+#endif /* TEST_FUZZ */
if (random_get_bytes(rkey, sizeof(rkey)) < 0)
return -1;
@@ -669,7 +709,10 @@ static void wpa_free_sta_sm(struct wpa_state_machine *sm)
os_free(sm->last_rx_eapol_key);
os_free(sm->wpa_ie);
wpa_group_put(sm->wpa_auth, sm->group);
- os_free(sm);
+#ifdef CONFIG_DPP2
+ wpabuf_clear_free(sm->dpp_z);
+#endif /* CONFIG_DPP2 */
+ bin_clear_free(sm, sizeof(*sm));
}
@@ -835,13 +878,15 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data,
int ok = 0;
const u8 *pmk = NULL;
size_t pmk_len;
+ int vlan_id = 0;
os_memset(&PTK, 0, sizeof(PTK));
for (;;) {
if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
!wpa_key_mgmt_sae(sm->wpa_key_mgmt)) {
pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr,
- sm->p2p_dev_addr, pmk, &pmk_len);
+ sm->p2p_dev_addr, pmk, &pmk_len,
+ &vlan_id);
if (pmk == NULL)
break;
#ifdef CONFIG_IEEE80211R_AP
@@ -860,6 +905,10 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data,
if (wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK,
data, data_len) == 0) {
+ if (sm->PMK != pmk) {
+ os_memcpy(sm->PMK, pmk, pmk_len);
+ sm->pmk_len = pmk_len;
+ }
ok = 1;
break;
}
@@ -878,6 +927,11 @@ static int wpa_try_alt_snonce(struct wpa_state_machine *sm, u8 *data,
wpa_printf(MSG_DEBUG,
"WPA: Earlier SNonce resulted in matching MIC");
sm->alt_snonce_valid = 0;
+
+ if (vlan_id && wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
+ wpa_auth_update_vlan(sm->wpa_auth, sm->addr, vlan_id) < 0)
+ return -1;
+
os_memcpy(sm->SNonce, sm->alt_SNonce, WPA_NONCE_LEN);
os_memcpy(&sm->PTK, &PTK, sizeof(PTK));
sm->PTK_valid = TRUE;
@@ -1202,6 +1256,11 @@ continue_processing:
wpa_try_alt_snonce(sm, data, data_len))) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
"received EAPOL-Key with invalid MIC");
+#ifdef TEST_FUZZ
+ wpa_printf(MSG_INFO,
+ "TEST: Ignore Key MIC failure for fuzz testing");
+ goto continue_fuzz;
+#endif /* TEST_FUZZ */
return;
}
#ifdef CONFIG_FILS
@@ -1210,9 +1269,17 @@ continue_processing:
&key_data_length) < 0) {
wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
"received EAPOL-Key with invalid MIC");
+#ifdef TEST_FUZZ
+ wpa_printf(MSG_INFO,
+ "TEST: Ignore Key MIC failure for fuzz testing");
+ goto continue_fuzz;
+#endif /* TEST_FUZZ */
return;
}
#endif /* CONFIG_FILS */
+#ifdef TEST_FUZZ
+ continue_fuzz:
+#endif /* TEST_FUZZ */
sm->MICVerified = TRUE;
eloop_cancel_timeout(wpa_send_eapol_timeout, wpa_auth, sm);
sm->pending_1_of_4_timeout = 0;
@@ -1317,6 +1384,9 @@ static int wpa_gmk_to_gtk(const u8 *gmk, const char *label, const u8 *addr,
os_memcpy(data + ETH_ALEN, gnonce, WPA_NONCE_LEN);
pos = data + ETH_ALEN + WPA_NONCE_LEN;
wpa_get_ntp_timestamp(pos);
+#ifdef TEST_FUZZ
+ os_memset(pos, 0xef, 8);
+#endif /* TEST_FUZZ */
pos += 8;
if (random_get_bytes(pos, gtk_len) < 0)
ret = -1;
@@ -1596,6 +1666,9 @@ static void wpa_send_eapol(struct wpa_authenticator *wpa_auth,
timeout_ms = eapol_key_timeout_no_retrans;
if (pairwise && ctr == 1 && !(key_info & WPA_KEY_INFO_MIC))
sm->pending_1_of_4_timeout = 1;
+#ifdef TEST_FUZZ
+ timeout_ms = 1;
+#endif /* TEST_FUZZ */
wpa_printf(MSG_DEBUG, "WPA: Use EAPOL-Key timeout of %u ms (retry "
"counter %u)", timeout_ms, ctr);
eloop_register_timeout(timeout_ms / 1000, (timeout_ms % 1000) * 1000,
@@ -1670,6 +1743,14 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event)
case WPA_DEAUTH:
case WPA_DISASSOC:
sm->DeauthenticationRequest = TRUE;
+#ifdef CONFIG_IEEE80211R_AP
+ os_memset(sm->PMK, 0, sizeof(sm->PMK));
+ sm->pmk_len = 0;
+ os_memset(sm->xxkey, 0, sizeof(sm->xxkey));
+ sm->xxkey_len = 0;
+ os_memset(sm->pmk_r1, 0, sizeof(sm->pmk_r1));
+ sm->pmk_r1_len = 0;
+#endif /* CONFIG_IEEE80211R_AP */
break;
case WPA_REAUTH:
case WPA_REAUTH_EAPOL:
@@ -1710,6 +1791,7 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event)
/* Using FT protocol, not WPA auth state machine */
sm->ft_completed = 1;
+ wpa_auth_set_ptk_rekey_timer(sm);
return 0;
#else /* CONFIG_IEEE80211R_AP */
break;
@@ -1986,7 +2068,7 @@ SM_STATE(WPA_PTK, INITPSK)
SM_ENTRY_MA(WPA_PTK, INITPSK, wpa_ptk);
psk = wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr, NULL,
- &psk_len);
+ &psk_len, NULL);
if (psk) {
os_memcpy(sm->PMK, psk, psk_len);
sm->pmk_len = psk_len;
@@ -2000,6 +2082,10 @@ SM_STATE(WPA_PTK, INITPSK)
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;
+#ifdef CONFIG_IEEE80211R_AP
+ os_memcpy(sm->xxkey, sm->pmksa->pmk, sm->pmksa->pmk_len);
+ sm->xxkey_len = sm->pmksa->pmk_len;
+#endif /* CONFIG_IEEE80211R_AP */
}
#endif /* CONFIG_SAE */
sm->req_replay_counter_used = 0;
@@ -2060,6 +2146,29 @@ SM_STATE(WPA_PTK, PTKSTART)
wpa_printf(MSG_DEBUG,
"RSN: No KCK available to derive PMKID for message 1/4");
pmkid = NULL;
+#ifdef CONFIG_FILS
+ } else if (wpa_key_mgmt_fils(sm->wpa_key_mgmt)) {
+ if (sm->pmkid_set) {
+ wpa_hexdump(MSG_DEBUG,
+ "RSN: Message 1/4 PMKID from FILS/ERP",
+ sm->pmkid, PMKID_LEN);
+ os_memcpy(&pmkid[2 + RSN_SELECTOR_LEN],
+ sm->pmkid, PMKID_LEN);
+ } else {
+ /* No PMKID available */
+ wpa_printf(MSG_DEBUG,
+ "RSN: No FILS/ERP PMKID available for message 1/4");
+ pmkid = NULL;
+ }
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_IEEE80211R_AP
+ } else if (wpa_key_mgmt_ft(sm->wpa_key_mgmt) &&
+ sm->ft_completed) {
+ wpa_printf(MSG_DEBUG,
+ "FT: No PMKID in message 1/4 when using FT protocol");
+ pmkid = NULL;
+ pmkid_len = 0;
+#endif /* CONFIG_IEEE80211R_AP */
#ifdef CONFIG_SAE
} else if (wpa_key_mgmt_sae(sm->wpa_key_mgmt)) {
if (sm->pmkid_set) {
@@ -2098,14 +2207,36 @@ static int wpa_derive_ptk(struct wpa_state_machine *sm, const u8 *snonce,
const u8 *pmk, unsigned int pmk_len,
struct wpa_ptk *ptk)
{
+ const u8 *z = NULL;
+ size_t z_len = 0;
+
#ifdef CONFIG_IEEE80211R_AP
- if (wpa_key_mgmt_ft(sm->wpa_key_mgmt))
- return wpa_auth_derive_ptk_ft(sm, pmk, ptk);
+ if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
+ if (sm->ft_completed) {
+ u8 ptk_name[WPA_PMK_NAME_LEN];
+
+ return wpa_pmk_r1_to_ptk(sm->pmk_r1, sm->pmk_r1_len,
+ sm->SNonce, sm->ANonce,
+ sm->addr, sm->wpa_auth->addr,
+ sm->pmk_r1_name,
+ ptk, ptk_name,
+ sm->wpa_key_mgmt,
+ sm->pairwise);
+ }
+ return wpa_auth_derive_ptk_ft(sm, ptk);
+ }
#endif /* CONFIG_IEEE80211R_AP */
+#ifdef CONFIG_DPP2
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && sm->dpp_z) {
+ z = wpabuf_head(sm->dpp_z);
+ z_len = wpabuf_len(sm->dpp_z);
+ }
+#endif /* CONFIG_DPP2 */
+
return wpa_pmk_to_ptk(pmk, pmk_len, "Pairwise key expansion",
sm->wpa_auth->addr, sm->addr, sm->ANonce, snonce,
- ptk, sm->wpa_key_mgmt, sm->pairwise);
+ ptk, sm->wpa_key_mgmt, sm->pairwise, z, z_len);
}
@@ -2155,6 +2286,16 @@ int fils_auth_pmk_to_ptk(struct wpa_state_machine *sm, const u8 *pmk,
pmk_r0_name, WPA_PMK_NAME_LEN);
wpa_ft_store_pmk_fils(sm, pmk_r0, pmk_r0_name);
os_memset(fils_ft, 0, sizeof(fils_ft));
+
+ res = wpa_derive_pmk_r1_name(pmk_r0_name, conf->r1_key_holder,
+ sm->addr, sm->pmk_r1_name,
+ use_sha384);
+ os_memset(pmk_r0, 0, PMK_LEN_MAX);
+ if (res < 0)
+ return -1;
+ wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR1Name", sm->pmk_r1_name,
+ WPA_PMK_NAME_LEN);
+ sm->pmk_r1_name_valid = 1;
}
#endif /* CONFIG_IEEE80211R_AP */
@@ -2559,6 +2700,27 @@ static struct wpabuf * fils_prepare_plainbuf(struct wpa_state_machine *sm,
wpabuf_put(plain, tmp2 - tmp);
*len = (u8 *) wpabuf_put(plain, 0) - len - 1;
+
+#ifdef CONFIG_OCV
+ if (wpa_auth_uses_ocv(sm)) {
+ struct wpa_channel_info ci;
+ u8 *pos;
+
+ if (wpa_channel_info(sm->wpa_auth, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "FILS: Failed to get channel info for OCI element");
+ wpabuf_free(plain);
+ return NULL;
+ }
+
+ pos = wpabuf_put(plain, OCV_OCI_EXTENDED_LEN);
+ if (ocv_insert_extended_oci(&ci, pos) < 0) {
+ wpabuf_free(plain);
+ return NULL;
+ }
+ }
+#endif /* CONFIG_OCV */
+
return plain;
}
@@ -2624,6 +2786,21 @@ u8 * hostapd_eid_assoc_fils_session(struct wpa_state_machine *sm, u8 *buf,
#endif /* CONFIG_FILS */
+#ifdef CONFIG_OCV
+int get_sta_tx_parameters(struct wpa_state_machine *sm, int ap_max_chanwidth,
+ int ap_seg1_idx, int *bandwidth, int *seg1_idx)
+{
+ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+
+ if (!wpa_auth->cb->get_sta_tx_params)
+ return -1;
+ return wpa_auth->cb->get_sta_tx_params(wpa_auth->cb_ctx, sm->addr,
+ ap_max_chanwidth, ap_seg1_idx,
+ bandwidth, seg1_idx);
+}
+#endif /* CONFIG_OCV */
+
+
SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
{
struct wpa_authenticator *wpa_auth = sm->wpa_auth;
@@ -2638,6 +2815,7 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
struct ieee802_1x_hdr *hdr;
struct wpa_eapol_key *key;
struct wpa_eapol_ie_parse kde;
+ int vlan_id = 0;
SM_ENTRY_MA(WPA_PTK, PTKCALCNEGOTIATING, wpa_ptk);
sm->EAPOLKeyReceived = FALSE;
@@ -2653,7 +2831,8 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
!wpa_key_mgmt_sae(sm->wpa_key_mgmt)) {
pmk = wpa_auth_get_psk(sm->wpa_auth, sm->addr,
- sm->p2p_dev_addr, pmk, &pmk_len);
+ sm->p2p_dev_addr, pmk, &pmk_len,
+ &vlan_id);
if (pmk == NULL)
break;
psk_found = 1;
@@ -2668,6 +2847,12 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
pmk_len = sm->pmk_len;
}
+ if ((!pmk || !pmk_len) && sm->pmksa) {
+ wpa_printf(MSG_DEBUG, "WPA: Use PMK from PMKSA cache");
+ pmk = sm->pmksa->pmk;
+ pmk_len = sm->pmksa->pmk_len;
+ }
+
if (wpa_derive_ptk(sm, sm->SNonce, pmk, pmk_len, &PTK) < 0)
break;
@@ -2675,6 +2860,10 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
wpa_verify_key_mic(sm->wpa_key_mgmt, pmk_len, &PTK,
sm->last_rx_eapol_key,
sm->last_rx_eapol_key_len) == 0) {
+ if (sm->PMK != pmk) {
+ os_memcpy(sm->PMK, pmk, pmk_len);
+ sm->pmk_len = pmk_len;
+ }
ok = 1;
break;
}
@@ -2746,6 +2935,32 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
WLAN_REASON_PREV_AUTH_NOT_VALID);
return;
}
+#ifdef CONFIG_OCV
+ if (wpa_auth_uses_ocv(sm)) {
+ struct wpa_channel_info ci;
+ int tx_chanwidth;
+ int tx_seg1_idx;
+
+ if (wpa_channel_info(wpa_auth, &ci) != 0) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+ "Failed to get channel info to validate received OCI in EAPOL-Key 2/4");
+ return;
+ }
+
+ if (get_sta_tx_parameters(sm,
+ channel_width_to_int(ci.chanwidth),
+ ci.seg1_idx, &tx_chanwidth,
+ &tx_seg1_idx) < 0)
+ return;
+
+ if (ocv_verify_tx_params(kde.oci, kde.oci_len, &ci,
+ tx_chanwidth, tx_seg1_idx) != 0) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+ ocv_errorstr);
+ return;
+ }
+ }
+#endif /* CONFIG_OCV */
#ifdef CONFIG_IEEE80211R_AP
if (ft && ft_check_msg_2_of_4(wpa_auth, sm, &kde) < 0) {
wpa_sta_disconnect(wpa_auth, sm->addr,
@@ -2794,6 +3009,13 @@ SM_STATE(WPA_PTK, PTKCALCNEGOTIATING)
}
#endif /* CONFIG_IEEE80211R_AP */
+ if (vlan_id && wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) &&
+ wpa_auth_update_vlan(wpa_auth, sm->addr, vlan_id) < 0) {
+ wpa_sta_disconnect(wpa_auth, sm->addr,
+ WLAN_REASON_PREV_AUTH_NOT_VALID);
+ return;
+ }
+
sm->pending_1_of_4_timeout = 0;
eloop_cancel_timeout(wpa_send_eapol_timeout, sm->wpa_auth, sm);
@@ -2883,6 +3105,36 @@ static u8 * ieee80211w_kde_add(struct wpa_state_machine *sm, u8 *pos)
#endif /* CONFIG_IEEE80211W */
+static int ocv_oci_len(struct wpa_state_machine *sm)
+{
+#ifdef CONFIG_OCV
+ if (wpa_auth_uses_ocv(sm))
+ return OCV_OCI_KDE_LEN;
+#endif /* CONFIG_OCV */
+ return 0;
+}
+
+static int ocv_oci_add(struct wpa_state_machine *sm, u8 **argpos)
+{
+#ifdef CONFIG_OCV
+ struct wpa_channel_info ci;
+
+ if (!wpa_auth_uses_ocv(sm))
+ return 0;
+
+ if (wpa_channel_info(sm->wpa_auth, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "Failed to get channel info for OCI element");
+ return -1;
+ }
+
+ return ocv_insert_oci_kde(&ci, argpos);
+#else /* CONFIG_OCV */
+ return 0;
+#endif /* CONFIG_OCV */
+}
+
+
SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
{
u8 rsc[WPA_KEY_RSC_LEN], *_rsc, *gtk, *kde, *pos, dummy_gtk[32];
@@ -2966,7 +3218,7 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
}
}
- kde_len = wpa_ie_len + ieee80211w_kde_len(sm);
+ kde_len = wpa_ie_len + ieee80211w_kde_len(sm) + ocv_oci_len(sm);
if (gtk)
kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len;
#ifdef CONFIG_IEEE80211R_AP
@@ -3011,6 +3263,10 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
gtk, gtk_len);
}
pos = ieee80211w_kde_add(sm, pos);
+ if (ocv_oci_add(sm, &pos) < 0) {
+ os_free(kde);
+ return;
+ }
#ifdef CONFIG_IEEE80211R_AP
if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
@@ -3094,12 +3350,7 @@ SM_STATE(WPA_PTK, PTKINITDONE)
/* FIX: MLME-SetProtection.Request(TA, Tx_Rx) */
sm->pairwise_set = TRUE;
- if (sm->wpa_auth->conf.wpa_ptk_rekey) {
- eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
- eloop_register_timeout(sm->wpa_auth->conf.
- wpa_ptk_rekey, 0, wpa_rekey_ptk,
- sm->wpa_auth, sm);
- }
+ wpa_auth_set_ptk_rekey_timer(sm);
if (wpa_key_mgmt_wpa_psk(sm->wpa_key_mgmt) ||
sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP ||
@@ -3199,7 +3450,7 @@ SM_STEP(WPA_PTK)
break;
case WPA_PTK_INITPSK:
if (wpa_auth_get_psk(sm->wpa_auth, sm->addr, sm->p2p_dev_addr,
- NULL, NULL)) {
+ NULL, NULL, NULL)) {
SM_ENTER(WPA_PTK, PTKSTART);
#ifdef CONFIG_SAE
} else if (wpa_auth_uses_sae(sm) && sm->pmksa) {
@@ -3322,7 +3573,7 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING)
}
if (sm->wpa == WPA_VERSION_WPA2) {
kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len +
- ieee80211w_kde_len(sm);
+ ieee80211w_kde_len(sm) + ocv_oci_len(sm);
kde_buf = os_malloc(kde_len);
if (kde_buf == NULL)
return;
@@ -3333,6 +3584,10 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING)
pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
gtk, gsm->GTK_len);
pos = ieee80211w_kde_add(sm, pos);
+ if (ocv_oci_add(sm, &pos) < 0) {
+ os_free(kde_buf);
+ return;
+ }
kde_len = pos - kde;
} else {
kde = gtk;
@@ -3353,8 +3608,67 @@ SM_STATE(WPA_PTK_GROUP, REKEYNEGOTIATING)
SM_STATE(WPA_PTK_GROUP, REKEYESTABLISHED)
{
+#ifdef CONFIG_OCV
+ struct wpa_authenticator *wpa_auth = sm->wpa_auth;
+ const u8 *key_data, *mic;
+ struct ieee802_1x_hdr *hdr;
+ struct wpa_eapol_key *key;
+ struct wpa_eapol_ie_parse kde;
+ size_t mic_len;
+ u16 key_data_length;
+#endif /* CONFIG_OCV */
+
SM_ENTRY_MA(WPA_PTK_GROUP, REKEYESTABLISHED, wpa_ptk_group);
sm->EAPOLKeyReceived = FALSE;
+
+#ifdef CONFIG_OCV
+ mic_len = wpa_mic_len(sm->wpa_key_mgmt, sm->pmk_len);
+
+ /*
+ * Note: last_rx_eapol_key length fields have already been validated in
+ * wpa_receive().
+ */
+ hdr = (struct ieee802_1x_hdr *) sm->last_rx_eapol_key;
+ key = (struct wpa_eapol_key *) (hdr + 1);
+ mic = (u8 *) (key + 1);
+ key_data = mic + mic_len + 2;
+ key_data_length = WPA_GET_BE16(mic + mic_len);
+ if (key_data_length > sm->last_rx_eapol_key_len - sizeof(*hdr) -
+ sizeof(*key) - mic_len - 2)
+ return;
+
+ if (wpa_parse_kde_ies(key_data, key_data_length, &kde) < 0) {
+ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_INFO,
+ "received EAPOL-Key group msg 2/2 with invalid Key Data contents");
+ return;
+ }
+
+ if (wpa_auth_uses_ocv(sm)) {
+ struct wpa_channel_info ci;
+ int tx_chanwidth;
+ int tx_seg1_idx;
+
+ if (wpa_channel_info(wpa_auth, &ci) != 0) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+ "Failed to get channel info to validate received OCI in EAPOL-Key group 1/2");
+ return;
+ }
+
+ if (get_sta_tx_parameters(sm,
+ channel_width_to_int(ci.chanwidth),
+ ci.seg1_idx, &tx_chanwidth,
+ &tx_seg1_idx) < 0)
+ return;
+
+ if (ocv_verify_tx_params(kde.oci, kde.oci_len, &ci,
+ tx_chanwidth, tx_seg1_idx) != 0) {
+ wpa_auth_logger(wpa_auth, sm->addr, LOGGER_INFO,
+ ocv_errorstr);
+ return;
+ }
+ }
+#endif /* CONFIG_OCV */
+
if (sm->GUpdateStationKeys)
sm->group->GKeyDoneStations--;
sm->GUpdateStationKeys = FALSE;
@@ -3963,6 +4277,15 @@ int wpa_auth_get_pairwise(struct wpa_state_machine *sm)
}
+const u8 * wpa_auth_get_pmk(struct wpa_state_machine *sm, int *len)
+{
+ if (!sm)
+ return NULL;
+ *len = sm->pmk_len;
+ return sm->PMK;
+}
+
+
int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm)
{
if (sm == NULL)
@@ -4575,9 +4898,37 @@ void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm,
*fils_kek_len = sm->PTK.kek_len;
}
+
+void wpa_auth_add_fils_pmk_pmkid(struct wpa_state_machine *sm, const u8 *pmk,
+ size_t pmk_len, const u8 *pmkid)
+{
+ os_memcpy(sm->PMK, pmk, pmk_len);
+ sm->pmk_len = pmk_len;
+ os_memcpy(sm->pmkid, pmkid, PMKID_LEN);
+ sm->pmkid_set = 1;
+}
+
#endif /* CONFIG_FILS */
+void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg)
+{
+ if (sm)
+ sm->auth_alg = auth_alg;
+}
+
+
+#ifdef CONFIG_DPP2
+void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z)
+{
+ if (sm) {
+ wpabuf_clear_free(sm->dpp_z);
+ sm->dpp_z = z ? wpabuf_dup(z) : NULL;
+ }
+}
+#endif /* CONFIG_DPP2 */
+
+
#ifdef CONFIG_TESTING_OPTIONS
int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce,
@@ -4666,7 +5017,7 @@ int wpa_auth_resend_m3(struct wpa_state_machine *sm,
}
}
- kde_len = wpa_ie_len + ieee80211w_kde_len(sm);
+ kde_len = wpa_ie_len + ieee80211w_kde_len(sm) + ocv_oci_len(sm);
if (gtk)
kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len;
#ifdef CONFIG_IEEE80211R_AP
@@ -4715,6 +5066,10 @@ int wpa_auth_resend_m3(struct wpa_state_machine *sm,
os_memset(opos, 0, 6); /* clear PN */
}
#endif /* CONFIG_IEEE80211W */
+ if (ocv_oci_add(sm, &pos) < 0) {
+ os_free(kde);
+ return -1;
+ }
#ifdef CONFIG_IEEE80211R_AP
if (wpa_key_mgmt_ft(sm->wpa_key_mgmt)) {
@@ -4796,7 +5151,7 @@ int wpa_auth_resend_group_m1(struct wpa_state_machine *sm,
gtk = gsm->GTK[gsm->GN - 1];
if (sm->wpa == WPA_VERSION_WPA2) {
kde_len = 2 + RSN_SELECTOR_LEN + 2 + gsm->GTK_len +
- ieee80211w_kde_len(sm);
+ ieee80211w_kde_len(sm) + ocv_oci_len(sm);
kde_buf = os_malloc(kde_len);
if (kde_buf == NULL)
return -1;
@@ -4816,6 +5171,10 @@ int wpa_auth_resend_group_m1(struct wpa_state_machine *sm,
os_memset(opos, 0, 6); /* clear PN */
}
#endif /* CONFIG_IEEE80211W */
+ if (ocv_oci_add(sm, &pos) < 0) {
+ os_free(kde_buf);
+ return -1;
+ }
kde_len = pos - kde;
} else {
kde = gtk;
diff --git a/src/ap/wpa_auth.h b/src/ap/wpa_auth.h
index fad5536f740e9..df1e17a003f8c 100644
--- a/src/ap/wpa_auth.h
+++ b/src/ap/wpa_auth.h
@@ -145,6 +145,7 @@ struct wpa_state_machine;
struct rsn_pmksa_cache_entry;
struct eapol_state_machine;
struct ft_remote_seq;
+struct wpa_channel_info;
struct ft_remote_r0kh {
@@ -191,6 +192,9 @@ struct wpa_auth_config {
int group_mgmt_cipher;
int sae_require_mfp;
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_OCV
+ int ocv; /* Operating Channel Validation */
+#endif /* CONFIG_OCV */
#ifdef CONFIG_IEEE80211R_AP
u8 ssid[SSID_MAX_LEN];
size_t ssid_len;
@@ -250,7 +254,8 @@ struct wpa_auth_callbacks {
int value);
int (*get_eapol)(void *ctx, const u8 *addr, wpa_eapol_variable var);
const u8 * (*get_psk)(void *ctx, const u8 *addr, const u8 *p2p_dev_addr,
- const u8 *prev_psk, size_t *psk_len);
+ const u8 *prev_psk, size_t *psk_len,
+ int *vlan_id);
int (*get_msk)(void *ctx, const u8 *addr, u8 *msk, size_t *len);
int (*set_key)(void *ctx, int vlan_id, enum wpa_alg alg,
const u8 *addr, int idx, u8 *key, size_t key_len);
@@ -265,6 +270,11 @@ struct wpa_auth_callbacks {
size_t data_len);
int (*send_oui)(void *ctx, const u8 *dst, u8 oui_suffix, const u8 *data,
size_t data_len);
+ int (*channel_info)(void *ctx, struct wpa_channel_info *ci);
+ int (*update_vlan)(void *ctx, const u8 *addr, int vlan_id);
+ int (*get_sta_tx_params)(void *ctx, const u8 *addr,
+ int ap_max_chanwidth, int ap_seg1_idx,
+ int *bandwidth, int *seg1_idx);
#ifdef CONFIG_IEEE80211R_AP
struct wpa_state_machine * (*add_sta)(void *ctx, const u8 *sta_addr);
int (*set_vlan)(void *ctx, const u8 *sta_addr,
@@ -308,7 +318,7 @@ enum {
};
int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
- struct wpa_state_machine *sm,
+ struct wpa_state_machine *sm, int freq,
const u8 *wpa_ie, size_t wpa_ie_len,
const u8 *mdie, size_t mdie_len,
const u8 *owe_dh, size_t owe_dh_len);
@@ -316,6 +326,8 @@ int wpa_validate_osen(struct wpa_authenticator *wpa_auth,
struct wpa_state_machine *sm,
const u8 *osen_ie, size_t osen_ie_len);
int wpa_auth_uses_mfp(struct wpa_state_machine *sm);
+void wpa_auth_set_ocv(struct wpa_state_machine *sm, int ocv);
+int wpa_auth_uses_ocv(struct wpa_state_machine *sm);
struct wpa_state_machine *
wpa_auth_sta_init(struct wpa_authenticator *wpa_auth, const u8 *addr,
const u8 *p2p_dev_addr);
@@ -339,6 +351,7 @@ int wpa_get_mib_sta(struct wpa_state_machine *sm, char *buf, size_t buflen);
void wpa_auth_countermeasures_start(struct wpa_authenticator *wpa_auth);
int wpa_auth_pairwise_set(struct wpa_state_machine *sm);
int wpa_auth_get_pairwise(struct wpa_state_machine *sm);
+const u8 * wpa_auth_get_pmk(struct wpa_state_machine *sm, int *len);
int wpa_auth_sta_key_mgmt(struct wpa_state_machine *sm);
int wpa_auth_sta_wpa_version(struct wpa_state_machine *sm);
int wpa_auth_sta_ft_tk_already_set(struct wpa_state_machine *sm);
@@ -449,14 +462,21 @@ const u8 * wpa_fils_validate_fils_session(struct wpa_state_machine *sm,
int wpa_fils_validate_key_confirm(struct wpa_state_machine *sm, const u8 *ies,
size_t ies_len);
+int get_sta_tx_parameters(struct wpa_state_machine *sm, int ap_max_chanwidth,
+ int ap_seg1_idx, int *bandwidth, int *seg1_idx);
+
int wpa_auth_write_fte(struct wpa_authenticator *wpa_auth, int use_sha384,
u8 *buf, size_t len);
void wpa_auth_get_fils_aead_params(struct wpa_state_machine *sm,
u8 *fils_anonce, u8 *fils_snonce,
u8 *fils_kek, size_t *fils_kek_len);
+void wpa_auth_add_fils_pmk_pmkid(struct wpa_state_machine *sm, const u8 *pmk,
+ size_t pmk_len, const u8 *pmkid);
u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm,
u8 *pos, size_t max_len,
const u8 *req_ies, size_t req_ies_len);
+void wpa_auth_set_auth_alg(struct wpa_state_machine *sm, u16 auth_alg);
+void wpa_auth_set_dpp_z(struct wpa_state_machine *sm, const struct wpabuf *z);
int wpa_auth_resend_m1(struct wpa_state_machine *sm, int change_anonce,
void (*cb)(void *ctx1, void *ctx2),
@@ -468,5 +488,6 @@ int wpa_auth_resend_group_m1(struct wpa_state_machine *sm,
void (*cb)(void *ctx1, void *ctx2),
void *ctx1, void *ctx2);
int wpa_auth_rekey_gtk(struct wpa_authenticator *wpa_auth);
+void wpa_auth_set_ptk_rekey_timer(struct wpa_state_machine *sm);
#endif /* WPA_AUTH_H */
diff --git a/src/ap/wpa_auth_ft.c b/src/ap/wpa_auth_ft.c
index f6792e00f32d8..ac16199a6006f 100644
--- a/src/ap/wpa_auth_ft.c
+++ b/src/ap/wpa_auth_ft.c
@@ -13,6 +13,8 @@
#include "utils/list.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
+#include "common/ocv.h"
+#include "drivers/driver.h"
#include "crypto/aes.h"
#include "crypto/aes_siv.h"
#include "crypto/aes_wrap.h"
@@ -64,7 +66,7 @@ struct tlv_list {
* Returns: 0 on success, -1 on error
*/
static int wpa_ft_rrb_decrypt(const u8 *key, const size_t key_len,
- const u8 *enc, const size_t enc_len,
+ const u8 *enc, size_t enc_len,
const u8 *auth, const size_t auth_len,
const u8 *src_addr, u8 type,
u8 **plain, size_t *plain_size)
@@ -72,7 +74,11 @@ static int wpa_ft_rrb_decrypt(const u8 *key, const size_t key_len,
const u8 *ad[3] = { src_addr, auth, &type };
size_t ad_len[3] = { ETH_ALEN, auth_len, sizeof(type) };
+ wpa_printf(MSG_DEBUG, "FT(RRB): src_addr=" MACSTR " type=%u",
+ MAC2STR(src_addr), type);
wpa_hexdump_key(MSG_DEBUG, "FT(RRB): decrypt using key", key, key_len);
+ wpa_hexdump(MSG_DEBUG, "FT(RRB): encrypted TLVs", enc, enc_len);
+ wpa_hexdump(MSG_DEBUG, "FT(RRB): authenticated TLVs", auth, auth_len);
if (!key) { /* skip decryption */
*plain = os_memdup(enc, enc_len);
@@ -95,8 +101,18 @@ static int wpa_ft_rrb_decrypt(const u8 *key, const size_t key_len,
goto err;
if (aes_siv_decrypt(key, key_len, enc, enc_len, 3, ad, ad_len,
- *plain) < 0)
- goto err;
+ *plain) < 0) {
+ if (enc_len < AES_BLOCK_SIZE + 2)
+ goto err;
+
+ /* Try to work around Ethernet devices that add extra
+ * two octet padding even if the frame is longer than
+ * the minimum Ethernet frame. */
+ enc_len -= 2;
+ if (aes_siv_decrypt(key, key_len, enc, enc_len, 3, ad, ad_len,
+ *plain) < 0)
+ goto err;
+ }
*plain_size = enc_len - AES_BLOCK_SIZE;
wpa_hexdump_key(MSG_DEBUG, "FT(RRB): decrypted TLVs",
@@ -461,9 +477,12 @@ static int wpa_ft_rrb_encrypt(const u8 *key, const size_t key_len,
const u8 *ad[3] = { src_addr, auth, &type };
size_t ad_len[3] = { ETH_ALEN, auth_len, sizeof(type) };
+ wpa_printf(MSG_DEBUG, "FT(RRB): src_addr=" MACSTR " type=%u",
+ MAC2STR(src_addr), type);
wpa_hexdump_key(MSG_DEBUG, "FT(RRB): plaintext message",
plain, plain_len);
wpa_hexdump_key(MSG_DEBUG, "FT(RRB): encrypt using key", key, key_len);
+ wpa_hexdump(MSG_DEBUG, "FT(RRB): authenticated TLVs", auth, auth_len);
if (!key) {
/* encryption not needed, return plaintext as packet */
@@ -473,6 +492,8 @@ static int wpa_ft_rrb_encrypt(const u8 *key, const size_t key_len,
wpa_printf(MSG_ERROR, "FT: Failed to encrypt RRB-OUI message");
return -1;
}
+ wpa_hexdump(MSG_DEBUG, "FT(RRB): encrypted TLVs",
+ enc, plain_len + AES_BLOCK_SIZE);
return 0;
}
@@ -501,9 +522,10 @@ static int wpa_ft_rrb_build(const u8 *key, const size_t key_len,
const u8 *src_addr, u8 type,
u8 **packet, size_t *packet_len)
{
- u8 *plain = NULL, *auth = NULL, *pos;
+ u8 *plain = NULL, *auth = NULL, *pos, *tmp;
size_t plain_len = 0, auth_len = 0;
int ret = -1;
+ size_t pad_len = 0;
*packet = NULL;
if (wpa_ft_rrb_lin(tlvs_enc0, tlvs_enc1, vlan, &plain, &plain_len) < 0)
@@ -515,6 +537,28 @@ static int wpa_ft_rrb_build(const u8 *key, const size_t key_len,
*packet_len = sizeof(u16) + auth_len + plain_len;
if (key)
*packet_len += AES_BLOCK_SIZE;
+#define RRB_MIN_MSG_LEN 64
+ if (*packet_len < RRB_MIN_MSG_LEN) {
+ pad_len = RRB_MIN_MSG_LEN - *packet_len;
+ if (pad_len < sizeof(struct ft_rrb_tlv))
+ pad_len = sizeof(struct ft_rrb_tlv);
+ wpa_printf(MSG_DEBUG,
+ "FT: Pad message to minimum Ethernet frame length (%d --> %d)",
+ (int) *packet_len, (int) (*packet_len + pad_len));
+ *packet_len += pad_len;
+ tmp = os_realloc(auth, auth_len + pad_len);
+ if (!tmp)
+ goto out;
+ auth = tmp;
+ pos = auth + auth_len;
+ WPA_PUT_LE16(pos, FT_RRB_LAST_EMPTY);
+ pos += 2;
+ WPA_PUT_LE16(pos, pad_len - sizeof(struct ft_rrb_tlv));
+ pos += 2;
+ os_memset(pos, 0, pad_len - sizeof(struct ft_rrb_tlv));
+ auth_len += pad_len;
+
+ }
*packet = os_zalloc(*packet_len);
if (!*packet)
goto out;
@@ -527,6 +571,7 @@ static int wpa_ft_rrb_build(const u8 *key, const size_t key_len,
if (wpa_ft_rrb_encrypt(key, key_len, plain, plain_len, auth,
auth_len, src_addr, type, pos) < 0)
goto out;
+ wpa_hexdump(MSG_MSGDUMP, "FT: RRB frame payload", *packet, *packet_len);
ret = 0;
@@ -594,8 +639,8 @@ static int wpa_ft_rrb_oui_send(struct wpa_authenticator *wpa_auth,
{
if (!wpa_auth->cb->send_oui)
return -1;
- wpa_printf(MSG_DEBUG, "FT: RRB-OUI type %u send to " MACSTR,
- oui_suffix, MAC2STR(dst));
+ wpa_printf(MSG_DEBUG, "FT: RRB-OUI type %u send to " MACSTR " (len=%u)",
+ oui_suffix, MAC2STR(dst), (unsigned int) data_len);
return wpa_auth->cb->send_oui(wpa_auth->cb_ctx, dst, oui_suffix, data,
data_len);
}
@@ -618,7 +663,7 @@ static const u8 * wpa_ft_get_psk(struct wpa_authenticator *wpa_auth,
if (wpa_auth->cb->get_psk == NULL)
return NULL;
return wpa_auth->cb->get_psk(wpa_auth->cb_ctx, addr, p2p_dev_addr,
- prev_psk, NULL);
+ prev_psk, NULL, NULL);
}
@@ -727,6 +772,17 @@ static int wpa_ft_add_tspec(struct wpa_authenticator *wpa_auth,
}
+#ifdef CONFIG_OCV
+static int wpa_channel_info(struct wpa_authenticator *wpa_auth,
+ struct wpa_channel_info *ci)
+{
+ if (!wpa_auth->cb->channel_info)
+ return -1;
+ return wpa_auth->cb->channel_info(wpa_auth->cb_ctx, ci);
+}
+#endif /* CONFIG_OCV */
+
+
int wpa_write_mdie(struct wpa_auth_config *conf, u8 *buf, size_t len)
{
u8 *pos = buf;
@@ -894,6 +950,8 @@ wpa_ft_rrb_seq_req(struct wpa_authenticator *wpa_auth,
goto err;
}
+ wpa_printf(MSG_DEBUG, "FT: Send out sequence number request to " MACSTR,
+ MAC2STR(src_addr));
item = os_zalloc(sizeof(*item));
if (!item)
goto err;
@@ -2016,8 +2074,7 @@ int wpa_ft_store_pmk_fils(struct wpa_state_machine *sm,
}
-int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
- struct wpa_ptk *ptk)
+int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk)
{
u8 pmk_r0[PMK_LEN_MAX], pmk_r0_name[WPA_PMK_NAME_LEN];
size_t pmk_r0_len = wpa_key_mgmt_sha384(sm->wpa_key_mgmt) ?
@@ -2373,10 +2430,24 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
end = pos + max_len;
- if (auth_alg == WLAN_AUTH_FT) {
+ if (auth_alg == WLAN_AUTH_FT ||
+ ((auth_alg == WLAN_AUTH_FILS_SK ||
+ auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+ auth_alg == WLAN_AUTH_FILS_PK) &&
+ (sm->wpa_key_mgmt & (WPA_KEY_MGMT_FT_FILS_SHA256 |
+ WPA_KEY_MGMT_FT_FILS_SHA384)))) {
+ if (!sm->pmk_r1_name_valid) {
+ wpa_printf(MSG_ERROR,
+ "FT: PMKR1Name is not valid for Assoc Resp RSNE");
+ return NULL;
+ }
+ wpa_hexdump(MSG_DEBUG, "FT: PMKR1Name for Assoc Resp RSNE",
+ sm->pmk_r1_name, WPA_PMK_NAME_LEN);
/*
* RSN (only present if this is a Reassociation Response and
- * part of a fast BSS transition)
+ * part of a fast BSS transition; or if this is a
+ * (Re)Association Response frame during an FT initial mobility
+ * domain association using FILS)
*/
res = wpa_write_rsn_ie(conf, pos, end - pos, sm->pmk_r1_name);
if (res < 0)
@@ -2430,6 +2501,35 @@ u8 * wpa_sm_write_assoc_resp_ies(struct wpa_state_machine *sm, u8 *pos,
os_free(igtk);
}
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_OCV
+ if (wpa_auth_uses_ocv(sm)) {
+ struct wpa_channel_info ci;
+ u8 *nbuf, *ocipos;
+
+ if (wpa_channel_info(sm->wpa_auth, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "Failed to get channel info for OCI element");
+ os_free(subelem);
+ return NULL;
+ }
+
+ subelem_len += 2 + OCV_OCI_LEN;
+ nbuf = os_realloc(subelem, subelem_len);
+ if (!nbuf) {
+ os_free(subelem);
+ return NULL;
+ }
+ subelem = nbuf;
+
+ ocipos = subelem + subelem_len - 2 - OCV_OCI_LEN;
+ *ocipos++ = FTIE_SUBELEM_OCI;
+ *ocipos++ = OCV_OCI_LEN;
+ if (ocv_insert_oci(&ci, &ocipos) < 0) {
+ os_free(subelem);
+ return NULL;
+ }
+ }
+#endif /* CONFIG_OCV */
} else {
r0kh_id = conf->r0_key_holder;
r0kh_id_len = conf->r0_key_holder_len;
@@ -2596,6 +2696,8 @@ static int wpa_ft_psk_pmk_r1(struct wpa_state_machine *sm,
os_memcpy(out_pmk_r1, pmk_r1, PMK_LEN);
if (out_pairwise)
*out_pairwise = pairwise;
+ os_memcpy(sm->PMK, pmk, PMK_LEN);
+ sm->pmk_len = PMK_LEN;
if (out_vlan &&
wpa_ft_get_vlan(sm->wpa_auth, sm->addr, out_vlan) < 0) {
wpa_printf(MSG_DEBUG, "FT: vlan not available for STA "
@@ -2881,6 +2983,8 @@ pmk_r1_derived:
wpa_hexdump_key(MSG_DEBUG, "FT: Selected PMK-R1", pmk_r1, pmk_r1_len);
sm->pmk_r1_name_valid = 1;
os_memcpy(sm->pmk_r1_name, pmk_r1_name, WPA_PMK_NAME_LEN);
+ os_memcpy(sm->pmk_r1, pmk_r1, pmk_r1_len);
+ sm->pmk_r1_len = pmk_r1_len;
if (random_get_bytes(sm->ANonce, WPA_NONCE_LEN)) {
wpa_printf(MSG_DEBUG, "FT: Failed to get random data for "
@@ -3178,6 +3282,32 @@ u16 wpa_ft_validate_reassoc(struct wpa_state_machine *sm, const u8 *ies,
return WLAN_STATUS_INVALID_FTIE;
}
+#ifdef CONFIG_OCV
+ if (wpa_auth_uses_ocv(sm)) {
+ struct wpa_channel_info ci;
+ int tx_chanwidth;
+ int tx_seg1_idx;
+
+ if (wpa_channel_info(sm->wpa_auth, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "Failed to get channel info to validate received OCI in (Re)Assoc Request");
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+
+ if (get_sta_tx_parameters(sm,
+ channel_width_to_int(ci.chanwidth),
+ ci.seg1_idx, &tx_chanwidth,
+ &tx_seg1_idx) < 0)
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+
+ if (ocv_verify_tx_params(parse.oci, parse.oci_len, &ci,
+ tx_chanwidth, tx_seg1_idx) != 0) {
+ wpa_printf(MSG_WARNING, "%s", ocv_errorstr);
+ return WLAN_STATUS_UNSPECIFIED_FAILURE;
+ }
+ }
+#endif /* CONFIG_OCV */
+
return WLAN_STATUS_SUCCESS;
}
@@ -4303,6 +4433,7 @@ void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
wpa_printf(MSG_DEBUG, "FT: RRB-OUI received frame from remote AP "
MACSTR, MAC2STR(src_addr));
wpa_printf(MSG_DEBUG, "FT: RRB-OUI frame - oui_suffix=%d", oui_suffix);
+ wpa_hexdump(MSG_MSGDUMP, "FT: RRB frame payload", data, data_len);
if (is_multicast_ether_addr(src_addr)) {
wpa_printf(MSG_DEBUG,
@@ -4331,8 +4462,10 @@ void wpa_ft_rrb_oui_rx(struct wpa_authenticator *wpa_auth, const u8 *src_addr,
}
auth = data + sizeof(u16);
+ wpa_hexdump(MSG_MSGDUMP, "FT: Authenticated payload", auth, alen);
enc = data + sizeof(u16) + alen;
elen = data_len - sizeof(u16) - alen;
+ wpa_hexdump(MSG_MSGDUMP, "FT: Encrypted payload", enc, elen);
switch (oui_suffix) {
case FT_PACKET_R0KH_R1KH_PULL:
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 812740301c8b2..45172c69a9fad 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -27,6 +27,7 @@
#include "tkip_countermeasures.h"
#include "ap_drv_ops.h"
#include "ap_config.h"
+#include "ieee802_11.h"
#include "pmksa_cache_auth.h"
#include "wpa_auth.h"
#include "wpa_auth_glue.h"
@@ -55,6 +56,9 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
wconf->wmm_enabled = conf->wmm_enabled;
wconf->wmm_uapsd = conf->wmm_uapsd;
wconf->disable_pmksa_caching = conf->disable_pmksa_caching;
+#ifdef CONFIG_OCV
+ wconf->ocv = conf->ocv;
+#endif /* CONFIG_OCV */
wconf->okc = conf->okc;
#ifdef CONFIG_IEEE80211W
wconf->ieee80211w = conf->ieee80211w;
@@ -242,12 +246,15 @@ static int hostapd_wpa_auth_get_eapol(void *ctx, const u8 *addr,
static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr,
const u8 *p2p_dev_addr,
- const u8 *prev_psk, size_t *psk_len)
+ const u8 *prev_psk, size_t *psk_len,
+ int *vlan_id)
{
struct hostapd_data *hapd = ctx;
struct sta_info *sta = ap_get_sta(hapd, addr);
const u8 *psk;
+ if (vlan_id)
+ *vlan_id = 0;
if (psk_len)
*psk_len = PMK_LEN;
@@ -283,7 +290,8 @@ static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr,
}
#endif /* CONFIG_OWE */
- psk = hostapd_get_psk(hapd->conf, addr, p2p_dev_addr, prev_psk);
+ psk = hostapd_get_psk(hapd->conf, addr, p2p_dev_addr, prev_psk,
+ vlan_id);
/*
* This is about to iterate over all psks, prev_psk gives the last
* returned psk which should not be returned again.
@@ -291,6 +299,9 @@ static const u8 * hostapd_wpa_auth_get_psk(void *ctx, const u8 *addr,
*/
if (sta && sta->psk && !psk) {
struct hostapd_sta_wpa_psk_short *pos;
+
+ if (vlan_id)
+ *vlan_id = 0;
psk = sta->psk->psk;
for (pos = sta->psk; pos; pos = pos->next) {
if (pos->is_passphrase) {
@@ -776,6 +787,74 @@ static int hostapd_wpa_auth_send_oui(void *ctx, const u8 *dst, u8 oui_suffix,
}
+static int hostapd_channel_info(void *ctx, struct wpa_channel_info *ci)
+{
+ struct hostapd_data *hapd = ctx;
+
+ return hostapd_drv_channel_info(hapd, ci);
+}
+
+
+static int hostapd_wpa_auth_update_vlan(void *ctx, const u8 *addr, int vlan_id)
+{
+#ifndef CONFIG_NO_VLAN
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+ struct vlan_description vlan_desc;
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta)
+ return -1;
+
+ os_memset(&vlan_desc, 0, sizeof(vlan_desc));
+ vlan_desc.notempty = 1;
+ vlan_desc.untagged = vlan_id;
+ if (!hostapd_vlan_valid(hapd->conf->vlan, &vlan_desc)) {
+ wpa_printf(MSG_INFO, "Invalid VLAN ID %d in wpa_psk_file",
+ vlan_id);
+ return -1;
+ }
+
+ if (ap_sta_set_vlan(hapd, sta, &vlan_desc) < 0) {
+ wpa_printf(MSG_INFO,
+ "Failed to assign VLAN ID %d from wpa_psk_file to "
+ MACSTR, vlan_id, MAC2STR(sta->addr));
+ return -1;
+ }
+
+ wpa_printf(MSG_INFO,
+ "Assigned VLAN ID %d from wpa_psk_file to " MACSTR,
+ vlan_id, MAC2STR(sta->addr));
+ if ((sta->flags & WLAN_STA_ASSOC) &&
+ ap_sta_bind_vlan(hapd, sta) < 0)
+ return -1;
+#endif /* CONFIG_NO_VLAN */
+
+ return 0;
+}
+
+
+#ifdef CONFIG_OCV
+static int hostapd_get_sta_tx_params(void *ctx, const u8 *addr,
+ int ap_max_chanwidth, int ap_seg1_idx,
+ int *bandwidth, int *seg1_idx)
+{
+ struct hostapd_data *hapd = ctx;
+ struct sta_info *sta;
+
+ sta = ap_get_sta(hapd, addr);
+ if (!sta) {
+ hostapd_wpa_auth_logger(hapd, addr, LOGGER_INFO,
+ "Failed to get STA info to validate received OCI");
+ return -1;
+ }
+
+ return get_tx_parameters(sta, ap_max_chanwidth, ap_seg1_idx, bandwidth,
+ seg1_idx);
+}
+#endif /* CONFIG_OCV */
+
+
#ifdef CONFIG_IEEE80211R_AP
static int hostapd_wpa_auth_send_ft_action(void *ctx, const u8 *dst,
@@ -814,12 +893,18 @@ hostapd_wpa_auth_add_sta(void *ctx, const u8 *sta_addr)
struct hostapd_data *hapd = ctx;
struct sta_info *sta;
+ wpa_printf(MSG_DEBUG, "Add station entry for " MACSTR
+ " based on WPA authenticator callback",
+ MAC2STR(sta_addr));
if (hostapd_add_sta_node(hapd, sta_addr, WLAN_AUTH_FT) < 0)
return NULL;
sta = ap_sta_add(hapd, sta_addr);
if (sta == NULL)
return NULL;
+ if (hapd->driver && hapd->driver->add_sta_node)
+ sta->added_unassoc = 1;
+ sta->ft_over_ds = 1;
if (sta->wpa_sm) {
sta->auth_alg = WLAN_AUTH_FT;
return sta->wpa_sm;
@@ -1189,6 +1274,11 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
.for_each_auth = hostapd_wpa_auth_for_each_auth,
.send_ether = hostapd_wpa_auth_send_ether,
.send_oui = hostapd_wpa_auth_send_oui,
+ .channel_info = hostapd_channel_info,
+ .update_vlan = hostapd_wpa_auth_update_vlan,
+#ifdef CONFIG_OCV
+ .get_sta_tx_params = hostapd_get_sta_tx_params,
+#endif /* CONFIG_OCV */
#ifdef CONFIG_IEEE80211R_AP
.send_ft_action = hostapd_wpa_auth_send_ft_action,
.add_sta = hostapd_wpa_auth_add_sta,
diff --git a/src/ap/wpa_auth_i.h b/src/ap/wpa_auth_i.h
index b1cea1b49b14c..4babd0cbb044c 100644
--- a/src/ap/wpa_auth_i.h
+++ b/src/ap/wpa_auth_i.h
@@ -22,6 +22,7 @@ struct wpa_state_machine {
u8 addr[ETH_ALEN];
u8 p2p_dev_addr[ETH_ALEN];
+ u16 auth_alg;
enum {
WPA_PTK_INITIALIZE, WPA_PTK_DISCONNECT, WPA_PTK_DISCONNECTED,
@@ -92,6 +93,9 @@ struct wpa_state_machine {
#endif /* CONFIG_IEEE80211R_AP */
unsigned int is_wnmsleep:1;
unsigned int pmkid_set:1;
+#ifdef CONFIG_OCV
+ unsigned int ocv_enabled:1;
+#endif /* CONFIG_OCV */
u8 req_replay_counter[WPA_REPLAY_COUNTER_LEN];
int req_replay_counter_used;
@@ -115,6 +119,8 @@ struct wpa_state_machine {
u8 xxkey[PMK_LEN_MAX]; /* PSK or the second 256 bits of MSK, or the
* first 384 bits of MSK */
size_t xxkey_len;
+ u8 pmk_r1[PMK_LEN_MAX];
+ unsigned int pmk_r1_len;
u8 pmk_r1_name[WPA_PMK_NAME_LEN]; /* PMKR1Name derived from FT Auth
* Request */
u8 r0kh_id[FT_R0KH_ID_MAX_LEN]; /* R0KH-ID from FT Auth Request */
@@ -147,6 +153,10 @@ struct wpa_state_machine {
unsigned int fils_completed:1;
#endif /* CONFIG_FILS */
+#ifdef CONFIG_DPP2
+ struct wpabuf *dpp_z;
+#endif /* CONFIG_DPP2 */
+
#ifdef CONFIG_TESTING_OPTIONS
void (*eapol_status_cb)(void *ctx1, void *ctx2);
void *eapol_status_cb_ctx1;
@@ -282,8 +292,7 @@ int wpa_write_ftie(struct wpa_auth_config *conf, int use_sha384,
const u8 *anonce, const u8 *snonce,
u8 *buf, size_t len, const u8 *subelem,
size_t subelem_len);
-int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, const u8 *pmk,
- struct wpa_ptk *ptk);
+int wpa_auth_derive_ptk_ft(struct wpa_state_machine *sm, struct wpa_ptk *ptk);
struct wpa_ft_pmk_cache * wpa_ft_pmk_cache_init(void);
void wpa_ft_pmk_cache_deinit(struct wpa_ft_pmk_cache *cache);
void wpa_ft_install_ptk(struct wpa_state_machine *sm);
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index cdcc5de39d457..8580a5a69be8a 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -293,9 +293,13 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
capab |= WPA_CAPABILITY_MFPR;
}
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_OCV
+ if (conf->ocv)
+ capab |= WPA_CAPABILITY_OCVC;
+#endif /* CONFIG_OCV */
#ifdef CONFIG_RSN_TESTING
if (rsn_testing)
- capab |= BIT(8) | BIT(14) | BIT(15);
+ capab |= BIT(8) | BIT(15);
#endif /* CONFIG_RSN_TESTING */
WPA_PUT_LE16(pos, capab);
pos += 2;
@@ -414,6 +418,10 @@ static u8 * wpa_write_osen(struct wpa_auth_config *conf, u8 *eid)
capab |= WPA_CAPABILITY_MFPR;
}
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_OCV
+ if (conf->ocv)
+ capab |= WPA_CAPABILITY_OCVC;
+#endif /* CONFIG_OCV */
WPA_PUT_LE16(eid, capab);
eid += 2;
@@ -522,7 +530,7 @@ static int wpa_auth_okc_iter(struct wpa_authenticator *a, void *ctx)
int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
- struct wpa_state_machine *sm,
+ struct wpa_state_machine *sm, int freq,
const u8 *wpa_ie, size_t wpa_ie_len,
const u8 *mdie, size_t mdie_len,
const u8 *owe_dh, size_t owe_dh_len)
@@ -552,6 +560,23 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
if (version == WPA_PROTO_RSN) {
res = wpa_parse_wpa_ie_rsn(wpa_ie, wpa_ie_len, &data);
+ if (!data.has_pairwise)
+ data.pairwise_cipher = wpa_default_rsn_cipher(freq);
+ if (!data.has_group)
+ data.group_cipher = wpa_default_rsn_cipher(freq);
+
+ if (wpa_key_mgmt_ft(data.key_mgmt) && !mdie &&
+ !wpa_key_mgmt_only_ft(data.key_mgmt)) {
+ /* Workaround for some HP and Epson printers that seem
+ * to incorrectly copy the FT-PSK + WPA-PSK AKMs from AP
+ * advertised RSNE to Association Request frame. */
+ wpa_printf(MSG_DEBUG,
+ "RSN: FT set in RSNE AKM but MDE is missing from "
+ MACSTR
+ " - ignore FT AKM(s) because there's also a non-FT AKM",
+ MAC2STR(sm->addr));
+ data.key_mgmt &= ~WPA_KEY_MGMT_FT;
+ }
selector = RSN_AUTH_KEY_MGMT_UNSPEC_802_1X;
if (0) {
@@ -760,6 +785,17 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
}
#endif /* CONFIG_SAE */
+#ifdef CONFIG_OCV
+ if ((data.capabilities & WPA_CAPABILITY_OCVC) &&
+ !(data.capabilities & WPA_CAPABILITY_MFPC)) {
+ wpa_printf(MSG_DEBUG,
+ "Management frame protection required with OCV, but client did not enable it");
+ return WPA_MGMT_FRAME_PROTECTION_VIOLATION;
+ }
+ wpa_auth_set_ocv(sm, wpa_auth->conf.ocv &&
+ (data.capabilities & WPA_CAPABILITY_OCVC));
+#endif /* CONFIG_OCV */
+
if (wpa_auth->conf.ieee80211w == NO_MGMT_FRAME_PROTECTION ||
!(data.capabilities & WPA_CAPABILITY_MFPC))
sm->mgmt_frame_prot = 0;
@@ -799,6 +835,12 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
"OWE: No Diffie-Hellman Parameter element");
return WPA_INVALID_AKMP;
}
+#ifdef CONFIG_DPP
+ if (sm->wpa_key_mgmt == WPA_KEY_MGMT_DPP && owe_dh) {
+ /* Diffie-Hellman Parameter element can be used with DPP as
+ * well, so allow this to proceed. */
+ } else
+#endif /* CONFIG_DPP */
if (sm->wpa_key_mgmt != WPA_KEY_MGMT_OWE && owe_dh) {
wpa_printf(MSG_DEBUG,
"OWE: Unexpected Diffie-Hellman Parameter element with non-OWE AKM");
@@ -816,6 +858,21 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
else
sm->wpa = WPA_VERSION_WPA;
+#if defined(CONFIG_IEEE80211R_AP) && defined(CONFIG_FILS)
+ if ((sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA256 ||
+ sm->wpa_key_mgmt == WPA_KEY_MGMT_FT_FILS_SHA384) &&
+ (sm->auth_alg == WLAN_AUTH_FILS_SK ||
+ sm->auth_alg == WLAN_AUTH_FILS_SK_PFS ||
+ sm->auth_alg == WLAN_AUTH_FILS_PK) &&
+ (data.num_pmkid != 1 || !data.pmkid || !sm->pmk_r1_name_valid ||
+ os_memcmp_const(data.pmkid, sm->pmk_r1_name,
+ WPA_PMK_NAME_LEN) != 0)) {
+ wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
+ "No PMKR1Name match for FILS+FT");
+ return WPA_INVALID_PMKID;
+ }
+#endif /* CONFIG_IEEE80211R_AP && CONFIG_FILS */
+
sm->pmksa = NULL;
for (i = 0; i < data.num_pmkid; i++) {
wpa_hexdump(MSG_DEBUG, "RSN IE: STA PMKID",
@@ -995,6 +1052,15 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end,
}
#endif /* CONFIG_P2P */
+#ifdef CONFIG_OCV
+ if (pos[1] > RSN_SELECTOR_LEN + 2 &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_OCI) {
+ ie->oci = pos + 2 + RSN_SELECTOR_LEN;
+ ie->oci_len = pos[1] - RSN_SELECTOR_LEN;
+ return 0;
+ }
+#endif /* CONFIG_OCV */
+
return 0;
}
@@ -1062,6 +1128,23 @@ int wpa_auth_uses_mfp(struct wpa_state_machine *sm)
}
+#ifdef CONFIG_OCV
+
+void wpa_auth_set_ocv(struct wpa_state_machine *sm, int ocv)
+{
+ if (sm)
+ sm->ocv_enabled = ocv;
+}
+
+
+int wpa_auth_uses_ocv(struct wpa_state_machine *sm)
+{
+ return sm ? sm->ocv_enabled : 0;
+}
+
+#endif /* CONFIG_OCV */
+
+
#ifdef CONFIG_OWE
u8 * wpa_auth_write_assoc_resp_owe(struct wpa_state_machine *sm,
u8 *pos, size_t max_len,
diff --git a/src/ap/wpa_auth_ie.h b/src/ap/wpa_auth_ie.h
index 73e433349049b..a38b206fd0f44 100644
--- a/src/ap/wpa_auth_ie.h
+++ b/src/ap/wpa_auth_ie.h
@@ -33,6 +33,10 @@ struct wpa_eapol_ie_parse {
const u8 *ip_addr_req;
const u8 *ip_addr_alloc;
#endif /* CONFIG_P2P */
+#ifdef CONFIG_OCV
+ const u8 *oci;
+ size_t oci_len;
+#endif /* CONFIG_OCV */
const u8 *osen;
size_t osen_len;
diff --git a/src/ap/wps_hostapd.c b/src/ap/wps_hostapd.c
index 5ec019971f372..6161cdbdb922c 100644
--- a/src/ap/wps_hostapd.c
+++ b/src/ap/wps_hostapd.c
@@ -354,6 +354,18 @@ static int hapd_wps_reconfig_in_memory(struct hostapd_data *hapd,
bss->wpa_pairwise,
bss->rsn_pairwise);
+ if (hapd->conf->wps_cred_add_sae &&
+ (cred->auth_type & WPS_AUTH_WPA2PSK) &&
+ cred->key_len != 2 * PMK_LEN) {
+ bss->wpa_key_mgmt |= WPA_KEY_MGMT_SAE;
+#ifdef CONFIG_IEEE80211W
+ if (bss->ieee80211w == NO_MGMT_FRAME_PROTECTION)
+ bss->ieee80211w =
+ MGMT_FRAME_PROTECTION_OPTIONAL;
+ bss->sae_require_mfp = 1;
+#endif /* CONFIG_IEEE80211W */
+ }
+
if (cred->key_len >= 8 && cred->key_len < 64) {
os_free(bss->ssid.wpa_passphrase);
bss->ssid.wpa_passphrase = os_zalloc(cred->key_len + 1);
@@ -401,6 +413,7 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx)
char buf[1024];
int multi_bss;
int wpa;
+ int pmf_changed = 0;
if (hapd->wps == NULL)
return 0;
@@ -520,6 +533,10 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx)
if (wpa) {
char *prefix;
+#ifdef CONFIG_IEEE80211W
+ int sae = 0;
+#endif /* CONFIG_IEEE80211W */
+
fprintf(nconf, "wpa=%d\n", wpa);
fprintf(nconf, "wpa_key_mgmt=");
@@ -528,10 +545,30 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx)
fprintf(nconf, "WPA-EAP");
prefix = " ";
}
- if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK))
+ if (cred->auth_type & (WPS_AUTH_WPA2PSK | WPS_AUTH_WPAPSK)) {
fprintf(nconf, "%sWPA-PSK", prefix);
+ prefix = " ";
+ }
+ if (hapd->conf->wps_cred_add_sae &&
+ (cred->auth_type & WPS_AUTH_WPA2PSK) &&
+ cred->key_len != 2 * PMK_LEN) {
+ fprintf(nconf, "%sSAE", prefix);
+#ifdef CONFIG_IEEE80211W
+ sae = 1;
+#endif /* CONFIG_IEEE80211W */
+ }
fprintf(nconf, "\n");
+#ifdef CONFIG_IEEE80211W
+ if (sae && hapd->conf->ieee80211w == NO_MGMT_FRAME_PROTECTION) {
+ fprintf(nconf, "ieee80211w=%d\n",
+ MGMT_FRAME_PROTECTION_OPTIONAL);
+ pmf_changed = 1;
+ }
+ if (sae)
+ fprintf(nconf, "sae_require_mfp=1\n");
+#endif /* CONFIG_IEEE80211W */
+
fprintf(nconf, "wpa_pairwise=");
prefix = "";
if (cred->encr_type & WPS_ENCR_AES) {
@@ -585,6 +622,7 @@ static int hapd_wps_cred_cb(struct hostapd_data *hapd, void *ctx)
str_starts(buf, "wep_default_key=") ||
str_starts(buf, "wep_key") ||
str_starts(buf, "wps_state=") ||
+ (pmf_changed && str_starts(buf, "ieee80211w=")) ||
str_starts(buf, "wpa=") ||
str_starts(buf, "wpa_psk=") ||
str_starts(buf, "wpa_pairwise=") ||
@@ -975,6 +1013,7 @@ int hostapd_init_wps(struct hostapd_data *hapd,
{
struct wps_context *wps;
struct wps_registrar_config cfg;
+ u8 *multi_ap_netw_key = NULL;
if (conf->wps_state == 0) {
hostapd_wps_clear_ies(hapd, 0);
@@ -1133,6 +1172,31 @@ int hostapd_init_wps(struct hostapd_data *hapd,
wps->encr_types_wpa = WPS_ENCR_AES | WPS_ENCR_TKIP;
}
+ if ((hapd->conf->multi_ap & FRONTHAUL_BSS) &&
+ hapd->conf->multi_ap_backhaul_ssid.ssid_len) {
+ cfg.multi_ap_backhaul_ssid_len =
+ hapd->conf->multi_ap_backhaul_ssid.ssid_len;
+ cfg.multi_ap_backhaul_ssid =
+ hapd->conf->multi_ap_backhaul_ssid.ssid;
+
+ if (conf->multi_ap_backhaul_ssid.wpa_passphrase) {
+ cfg.multi_ap_backhaul_network_key = (const u8 *)
+ conf->multi_ap_backhaul_ssid.wpa_passphrase;
+ cfg.multi_ap_backhaul_network_key_len =
+ os_strlen(conf->multi_ap_backhaul_ssid.wpa_passphrase);
+ } else if (conf->multi_ap_backhaul_ssid.wpa_psk) {
+ multi_ap_netw_key = os_malloc(2 * PMK_LEN + 1);
+ if (!multi_ap_netw_key)
+ goto fail;
+ wpa_snprintf_hex((char *) multi_ap_netw_key,
+ 2 * PMK_LEN + 1,
+ conf->multi_ap_backhaul_ssid.wpa_psk->psk,
+ PMK_LEN);
+ cfg.multi_ap_backhaul_network_key = multi_ap_netw_key;
+ cfg.multi_ap_backhaul_network_key_len = 2 * PMK_LEN;
+ }
+ }
+
wps->ap_settings = conf->ap_settings;
wps->ap_settings_len = conf->ap_settings_len;
@@ -1174,10 +1238,12 @@ int hostapd_init_wps(struct hostapd_data *hapd,
hostapd_register_probereq_cb(hapd, hostapd_wps_probe_req_rx, hapd);
hapd->wps = wps;
+ bin_clear_free(multi_ap_netw_key, 2 * PMK_LEN);
return 0;
fail:
+ bin_clear_free(multi_ap_netw_key, 2 * PMK_LEN);
hostapd_free_wps(wps);
return -1;
}
diff --git a/src/common/common_module_tests.c b/src/common/common_module_tests.c
index 0b596bbcc6313..30c52476bbed8 100644
--- a/src/common/common_module_tests.c
+++ b/src/common/common_module_tests.c
@@ -1,6 +1,6 @@
/*
* common module tests
- * Copyright (c) 2014-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2014-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -10,10 +10,12 @@
#include "utils/common.h"
#include "utils/module_tests.h"
+#include "crypto/crypto.h"
#include "ieee802_11_common.h"
#include "ieee802_11_defs.h"
#include "gas.h"
#include "wpa_common.h"
+#include "sae.h"
struct ieee802_11_parse_test_data {
@@ -248,6 +250,179 @@ static int gas_tests(void)
}
+static int sae_tests(void)
+{
+#ifdef CONFIG_SAE
+ struct sae_data sae;
+ int ret = -1;
+ /* IEEE P802.11-REVmd/D2.1, Annex J.10 */
+ const u8 addr1[ETH_ALEN] = { 0x82, 0x7b, 0x91, 0x9d, 0xd4, 0xb9 };
+ const u8 addr2[ETH_ALEN] = { 0x1e, 0xec, 0x49, 0xea, 0x64, 0x88 };
+ const char *pw = "mekmitasdigoat";
+ const char *pwid = "psk4internet";
+ const u8 local_rand[] = {
+ 0xa9, 0x06, 0xf6, 0x1e, 0x4d, 0x3a, 0x5d, 0x4e,
+ 0xb2, 0x96, 0x5f, 0xf3, 0x4c, 0xf9, 0x17, 0xdd,
+ 0x04, 0x44, 0x45, 0xc8, 0x78, 0xc1, 0x7c, 0xa5,
+ 0xd5, 0xb9, 0x37, 0x86, 0xda, 0x9f, 0x83, 0xcf
+ };
+ const u8 local_mask[] = {
+ 0x42, 0x34, 0xb4, 0xfb, 0x17, 0xaa, 0x43, 0x5c,
+ 0x52, 0xfb, 0xfd, 0xeb, 0xe6, 0x40, 0x39, 0xb4,
+ 0x34, 0x78, 0x20, 0x0e, 0x54, 0xff, 0x7b, 0x6e,
+ 0x07, 0xb6, 0x9c, 0xad, 0x74, 0x15, 0x3c, 0x15
+ };
+ const u8 local_commit[] = {
+ 0x13, 0x00, 0xeb, 0x3b, 0xab, 0x19, 0x64, 0xe4,
+ 0xa0, 0xab, 0x05, 0x92, 0x5d, 0xdf, 0x33, 0x39,
+ 0x51, 0x91, 0x38, 0xbc, 0x65, 0xd6, 0xcd, 0xc0,
+ 0xf8, 0x13, 0xdd, 0x6f, 0xd4, 0x34, 0x4e, 0xb4,
+ 0xbf, 0xe4, 0x4b, 0x5c, 0x21, 0x59, 0x76, 0x58,
+ 0xf4, 0xe3, 0xed, 0xdf, 0xb4, 0xb9, 0x9f, 0x25,
+ 0xb4, 0xd6, 0x54, 0x0f, 0x32, 0xff, 0x1f, 0xd5,
+ 0xc5, 0x30, 0xc6, 0x0a, 0x79, 0x44, 0x48, 0x61,
+ 0x0b, 0xc6, 0xde, 0x3d, 0x92, 0xbd, 0xbb, 0xd4,
+ 0x7d, 0x93, 0x59, 0x80, 0xca, 0x6c, 0xf8, 0x98,
+ 0x8a, 0xb6, 0x63, 0x0b, 0xe6, 0x76, 0x4c, 0x88,
+ 0x5c, 0xeb, 0x97, 0x93, 0x97, 0x0f, 0x69, 0x52,
+ 0x17, 0xee, 0xff, 0x0d, 0x21, 0x70, 0x73, 0x6b,
+ 0x34, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
+ 0x74
+ };
+ const u8 peer_commit[] = {
+ 0x13, 0x00, 0x55, 0x64, 0xf0, 0x45, 0xb2, 0xea,
+ 0x1e, 0x56, 0x6c, 0xf1, 0xdd, 0x74, 0x1f, 0x70,
+ 0xd9, 0xbe, 0x35, 0xd2, 0xdf, 0x5b, 0x9a, 0x55,
+ 0x02, 0x94, 0x6e, 0xe0, 0x3c, 0xf8, 0xda, 0xe2,
+ 0x7e, 0x1e, 0x05, 0xb8, 0x43, 0x0e, 0xb7, 0xa9,
+ 0x9e, 0x24, 0x87, 0x7c, 0xe6, 0x9b, 0xaf, 0x3d,
+ 0xc5, 0x80, 0xe3, 0x09, 0x63, 0x3d, 0x6b, 0x38,
+ 0x5f, 0x83, 0xee, 0x1c, 0x3e, 0xc3, 0x59, 0x1f,
+ 0x1a, 0x53, 0x93, 0xc0, 0x6e, 0x80, 0x5d, 0xdc,
+ 0xeb, 0x2f, 0xde, 0x50, 0x93, 0x0d, 0xd7, 0xcf,
+ 0xeb, 0xb9, 0x87, 0xc6, 0xff, 0x96, 0x66, 0xaf,
+ 0x16, 0x4e, 0xb5, 0x18, 0x4d, 0x8e, 0x66, 0x62,
+ 0xed, 0x6a, 0xff, 0x0d, 0x21, 0x70, 0x73, 0x6b,
+ 0x34, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
+ 0x74
+ };
+ const u8 kck[] = {
+ 0x59, 0x9d, 0x6f, 0x1e, 0x27, 0x54, 0x8b, 0xe8,
+ 0x49, 0x9d, 0xce, 0xed, 0x2f, 0xec, 0xcf, 0x94,
+ 0x81, 0x8c, 0xe1, 0xc7, 0x9f, 0x1b, 0x4e, 0xb3,
+ 0xd6, 0xa5, 0x32, 0x28, 0xa0, 0x9b, 0xf3, 0xed
+ };
+ const u8 pmk[] = {
+ 0x7a, 0xea, 0xd8, 0x6f, 0xba, 0x4c, 0x32, 0x21,
+ 0xfc, 0x43, 0x7f, 0x5f, 0x14, 0xd7, 0x0d, 0x85,
+ 0x4e, 0xa5, 0xd5, 0xaa, 0xc1, 0x69, 0x01, 0x16,
+ 0x79, 0x30, 0x81, 0xed, 0xa4, 0xd5, 0x57, 0xc5
+ };
+ const u8 pmkid[] = {
+ 0x40, 0xa0, 0x9b, 0x60, 0x17, 0xce, 0xbf, 0x00,
+ 0x72, 0x84, 0x3b, 0x53, 0x52, 0xaa, 0x2b, 0x4f
+ };
+ const u8 local_confirm[] = {
+ 0x01, 0x00, 0x12, 0xd9, 0xd5, 0xc7, 0x8c, 0x50,
+ 0x05, 0x26, 0xd3, 0x6c, 0x41, 0xdb, 0xc5, 0x6a,
+ 0xed, 0xf2, 0x91, 0x4c, 0xed, 0xdd, 0xd7, 0xca,
+ 0xd4, 0xa5, 0x8c, 0x48, 0xf8, 0x3d, 0xbd, 0xe9,
+ 0xfc, 0x77
+ };
+ const u8 peer_confirm[] = {
+ 0x01, 0x00, 0x02, 0x87, 0x1c, 0xf9, 0x06, 0x89,
+ 0x8b, 0x80, 0x60, 0xec, 0x18, 0x41, 0x43, 0xbe,
+ 0x77, 0xb8, 0xc0, 0x8a, 0x80, 0x19, 0xb1, 0x3e,
+ 0xb6, 0xd0, 0xae, 0xf0, 0xd8, 0x38, 0x3d, 0xfa,
+ 0xc2, 0xfd
+ };
+ struct wpabuf *buf = NULL;
+ struct crypto_bignum *mask = NULL;
+
+ os_memset(&sae, 0, sizeof(sae));
+ buf = wpabuf_alloc(1000);
+ if (!buf ||
+ sae_set_group(&sae, 19) < 0 ||
+ sae_prepare_commit(addr1, addr2, (const u8 *) pw, os_strlen(pw),
+ pwid, &sae) < 0)
+ goto fail;
+
+ /* Override local values based on SAE test vector */
+ crypto_bignum_deinit(sae.tmp->sae_rand, 1);
+ sae.tmp->sae_rand = crypto_bignum_init_set(local_rand,
+ sizeof(local_rand));
+ mask = crypto_bignum_init_set(local_mask, sizeof(local_mask));
+ if (!sae.tmp->sae_rand || !mask)
+ goto fail;
+
+ if (crypto_bignum_add(sae.tmp->sae_rand, mask,
+ sae.tmp->own_commit_scalar) < 0 ||
+ crypto_bignum_mod(sae.tmp->own_commit_scalar, sae.tmp->order,
+ sae.tmp->own_commit_scalar) < 0 ||
+ crypto_ec_point_mul(sae.tmp->ec, sae.tmp->pwe_ecc, mask,
+ sae.tmp->own_commit_element_ecc) < 0 ||
+ crypto_ec_point_invert(sae.tmp->ec,
+ sae.tmp->own_commit_element_ecc) < 0)
+ goto fail;
+
+ /* Check that output matches the test vector */
+ sae_write_commit(&sae, buf, NULL, pwid);
+ wpa_hexdump_buf(MSG_DEBUG, "SAE: Commit message", buf);
+
+ if (wpabuf_len(buf) != sizeof(local_commit) ||
+ os_memcmp(wpabuf_head(buf), local_commit,
+ sizeof(local_commit)) != 0) {
+ wpa_printf(MSG_ERROR, "SAE: Mismatch in local commit");
+ goto fail;
+ }
+
+ if (sae_parse_commit(&sae, peer_commit, sizeof(peer_commit), NULL, NULL,
+ NULL) != 0 ||
+ sae_process_commit(&sae) < 0)
+ goto fail;
+
+ if (os_memcmp(kck, sae.tmp->kck, SAE_KCK_LEN) != 0) {
+ wpa_printf(MSG_ERROR, "SAE: Mismatch in KCK");
+ goto fail;
+ }
+
+ if (os_memcmp(pmk, sae.pmk, SAE_PMK_LEN) != 0) {
+ wpa_printf(MSG_ERROR, "SAE: Mismatch in PMK");
+ goto fail;
+ }
+
+ if (os_memcmp(pmkid, sae.pmkid, SAE_PMKID_LEN) != 0) {
+ wpa_printf(MSG_ERROR, "SAE: Mismatch in PMKID");
+ goto fail;
+ }
+
+ buf->used = 0;
+ sae.send_confirm = 1;
+ sae_write_confirm(&sae, buf);
+ wpa_hexdump_buf(MSG_DEBUG, "SAE: Confirm message", buf);
+
+ if (wpabuf_len(buf) != sizeof(local_confirm) ||
+ os_memcmp(wpabuf_head(buf), local_confirm,
+ sizeof(local_confirm)) != 0) {
+ wpa_printf(MSG_ERROR, "SAE: Mismatch in local confirm");
+ goto fail;
+ }
+
+ if (sae_check_confirm(&sae, peer_confirm, sizeof(peer_confirm)) < 0)
+ goto fail;
+
+ ret = 0;
+fail:
+ sae_clear_data(&sae);
+ wpabuf_free(buf);
+ crypto_bignum_deinit(mask, 1);
+ return ret;
+#else /* CONFIG_SAE */
+ return 0;
+#endif /* CONFIG_SAE */
+}
+
+
int common_module_tests(void)
{
int ret = 0;
@@ -256,6 +431,7 @@ int common_module_tests(void)
if (ieee802_11_parse_tests() < 0 ||
gas_tests() < 0 ||
+ sae_tests() < 0 ||
rsn_ie_parse_tests() < 0)
ret = -1;
diff --git a/src/common/defs.h b/src/common/defs.h
index c968cd6cb82a5..4faf1c8601d04 100644
--- a/src/common/defs.h
+++ b/src/common/defs.h
@@ -59,6 +59,13 @@ typedef enum { FALSE = 0, TRUE = 1 } Boolean;
#define WPA_KEY_MGMT_DPP BIT(23)
#define WPA_KEY_MGMT_FT_IEEE8021X_SHA384 BIT(24)
+#define WPA_KEY_MGMT_FT (WPA_KEY_MGMT_FT_PSK | \
+ WPA_KEY_MGMT_FT_IEEE8021X | \
+ WPA_KEY_MGMT_FT_IEEE8021X_SHA384 | \
+ WPA_KEY_MGMT_FT_SAE | \
+ WPA_KEY_MGMT_FT_FILS_SHA256 | \
+ WPA_KEY_MGMT_FT_FILS_SHA384)
+
static inline int wpa_key_mgmt_wpa_ieee8021x(int akm)
{
return !!(akm & (WPA_KEY_MGMT_IEEE8021X |
@@ -86,12 +93,14 @@ static inline int wpa_key_mgmt_wpa_psk(int akm)
static inline int wpa_key_mgmt_ft(int akm)
{
- return !!(akm & (WPA_KEY_MGMT_FT_PSK |
- WPA_KEY_MGMT_FT_IEEE8021X |
- WPA_KEY_MGMT_FT_IEEE8021X_SHA384 |
- WPA_KEY_MGMT_FT_SAE |
- WPA_KEY_MGMT_FT_FILS_SHA256 |
- WPA_KEY_MGMT_FT_FILS_SHA384));
+ return !!(akm & WPA_KEY_MGMT_FT);
+}
+
+static inline int wpa_key_mgmt_only_ft(int akm)
+{
+ int ft = wpa_key_mgmt_ft(akm);
+ akm &= ~WPA_KEY_MGMT_FT;
+ return ft && !akm;
}
static inline int wpa_key_mgmt_ft_psk(int akm)
@@ -399,4 +408,15 @@ enum eap_proxy_sim_state {
#define OCE_STA_CFON BIT(1)
#define OCE_AP BIT(2)
+/* enum chan_width - Channel width definitions */
+enum chan_width {
+ CHAN_WIDTH_20_NOHT,
+ CHAN_WIDTH_20,
+ CHAN_WIDTH_40,
+ CHAN_WIDTH_80,
+ CHAN_WIDTH_80P80,
+ CHAN_WIDTH_160,
+ CHAN_WIDTH_UNKNOWN
+};
+
#endif /* DEFS_H */
diff --git a/src/common/dpp.c b/src/common/dpp.c
index e715e0454d725..49de476973846 100644
--- a/src/common/dpp.c
+++ b/src/common/dpp.c
@@ -1,6 +1,7 @@
/*
* DPP functionality shared between hostapd and wpa_supplicant
* Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2018-2019, The Linux Foundation
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -18,6 +19,7 @@
#include "common/ieee802_11_common.h"
#include "common/ieee802_11_defs.h"
#include "common/wpa_ctrl.h"
+#include "common/gas.h"
#include "crypto/crypto.h"
#include "crypto/random.h"
#include "crypto/aes.h"
@@ -68,6 +70,11 @@ static void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr,
#endif
+struct dpp_global {
+ struct dl_list bootstrap; /* struct dpp_bootstrap_info */
+ struct dl_list configurator; /* struct dpp_configurator */
+};
+
static const struct dpp_curve_params dpp_curves[] = {
/* The mandatory to support and the default NIST P-256 curve needs to
* be the first entry on this list. */
@@ -813,7 +820,9 @@ static int dpp_parse_uri_pk(struct dpp_bootstrap_info *bi, const char *info)
const unsigned char *pk;
int ppklen;
X509_ALGOR *pa;
-#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20800000L)
ASN1_OBJECT *pa_oid;
#else
const ASN1_OBJECT *pa_oid;
@@ -1535,6 +1544,9 @@ static struct wpabuf * dpp_auth_build_req(struct dpp_authentication *auth,
4 + sizeof(wrapped_data);
if (neg_freq > 0)
attr_len += 4 + 2;
+#ifdef CONFIG_DPP2
+ attr_len += 5;
+#endif /* CONFIG_DPP2 */
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_REQ)
attr_len += 5;
@@ -1577,6 +1589,13 @@ static struct wpabuf * dpp_auth_build_req(struct dpp_authentication *auth,
wpabuf_put_u8(msg, channel);
}
+#ifdef CONFIG_DPP2
+ /* Protocol Version */
+ wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, 2);
+#endif /* CONFIG_DPP2 */
+
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_NO_WRAPPED_DATA_AUTH_REQ) {
wpa_printf(MSG_INFO, "DPP: TESTING - no Wrapped Data");
@@ -1703,6 +1722,9 @@ static struct wpabuf * dpp_auth_build_resp(struct dpp_authentication *auth,
/* Build DPP Authentication Response frame attributes */
attr_len = 4 + 1 + 2 * (4 + SHA256_MAC_LEN) +
4 + (pr ? wpabuf_len(pr) : 0) + 4 + sizeof(wrapped_data);
+#ifdef CONFIG_DPP2
+ attr_len += 5;
+#endif /* CONFIG_DPP2 */
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_AFTER_WRAPPED_DATA_AUTH_RESP)
attr_len += 5;
@@ -1730,6 +1752,13 @@ static struct wpabuf * dpp_auth_build_resp(struct dpp_authentication *auth,
wpabuf_put_buf(msg, pr);
}
+#ifdef CONFIG_DPP2
+ /* Protocol Version */
+ wpabuf_put_le16(msg, DPP_ATTR_PROTOCOL_VERSION);
+ wpabuf_put_le16(msg, 1);
+ wpabuf_put_u8(msg, 2);
+#endif /* CONFIG_DPP2 */
+
attr_end = wpabuf_put(msg, 0);
#ifdef CONFIG_TESTING_OPTIONS
@@ -2200,8 +2229,8 @@ fail:
}
-struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth,
- const char *json)
+static struct wpabuf * dpp_build_conf_req_attr(struct dpp_authentication *auth,
+ const char *json)
{
size_t nonce_len;
size_t json_len, clear_len;
@@ -2305,6 +2334,55 @@ fail:
}
+static void dpp_write_adv_proto(struct wpabuf *buf)
+{
+ /* Advertisement Protocol IE */
+ wpabuf_put_u8(buf, WLAN_EID_ADV_PROTO);
+ wpabuf_put_u8(buf, 8); /* Length */
+ wpabuf_put_u8(buf, 0x7f);
+ wpabuf_put_u8(buf, WLAN_EID_VENDOR_SPECIFIC);
+ wpabuf_put_u8(buf, 5);
+ wpabuf_put_be24(buf, OUI_WFA);
+ wpabuf_put_u8(buf, DPP_OUI_TYPE);
+ wpabuf_put_u8(buf, 0x01);
+}
+
+
+static void dpp_write_gas_query(struct wpabuf *buf, struct wpabuf *query)
+{
+ /* GAS Query */
+ wpabuf_put_le16(buf, wpabuf_len(query));
+ wpabuf_put_buf(buf, query);
+}
+
+
+struct wpabuf * dpp_build_conf_req(struct dpp_authentication *auth,
+ const char *json)
+{
+ struct wpabuf *buf, *conf_req;
+
+ conf_req = dpp_build_conf_req_attr(auth, json);
+ if (!conf_req) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: No configuration request data available");
+ return NULL;
+ }
+
+ buf = gas_build_initial_req(0, 10 + 2 + wpabuf_len(conf_req));
+ if (!buf) {
+ wpabuf_free(conf_req);
+ return NULL;
+ }
+
+ dpp_write_adv_proto(buf);
+ dpp_write_gas_query(buf, conf_req);
+ wpabuf_free(conf_req);
+ wpa_hexdump_buf(MSG_MSGDUMP, "DPP: GAS Config Request", buf);
+
+ return buf;
+}
+
+
static void dpp_auth_success(struct dpp_authentication *auth)
{
wpa_printf(MSG_DEBUG,
@@ -2891,6 +2969,10 @@ dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
u16 wrapped_data_len, i_proto_len, i_nonce_len, i_capab_len,
i_bootstrap_len, channel_len;
struct dpp_authentication *auth = NULL;
+#ifdef CONFIG_DPP2
+ const u8 *version;
+ u16 version_len;
+#endif /* CONFIG_DPP2 */
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_STOP_AT_AUTH_REQ) {
@@ -2920,6 +3002,22 @@ dpp_auth_req_rx(void *msg_ctx, u8 dpp_allowed_roles, int qr_mutual,
auth->curve = own_bi->curve;
auth->curr_freq = freq;
+ auth->peer_version = 1; /* default to the first version */
+#ifdef CONFIG_DPP2
+ version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION,
+ &version_len);
+ if (version) {
+ if (version_len < 1 || version[0] == 0) {
+ dpp_auth_fail(auth,
+ "Invalid Protocol Version attribute");
+ goto fail;
+ }
+ auth->peer_version = version[0];
+ wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u",
+ auth->peer_version);
+ }
+#endif /* CONFIG_DPP2 */
+
channel = dpp_get_attr(attr_start, attr_len, DPP_ATTR_CHANNEL,
&channel_len);
if (channel) {
@@ -3448,6 +3546,10 @@ dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
wrapped2_len, r_auth_len;
u8 r_auth2[DPP_MAX_HASH_LEN];
u8 role;
+#ifdef CONFIG_DPP2
+ const u8 *version;
+ u16 version_len;
+#endif /* CONFIG_DPP2 */
#ifdef CONFIG_TESTING_OPTIONS
if (dpp_test == DPP_TEST_STOP_AT_AUTH_RESP) {
@@ -3522,6 +3624,22 @@ dpp_auth_resp_rx(struct dpp_authentication *auth, const u8 *hdr,
return NULL;
}
+ auth->peer_version = 1; /* default to the first version */
+#ifdef CONFIG_DPP2
+ version = dpp_get_attr(attr_start, attr_len, DPP_ATTR_PROTOCOL_VERSION,
+ &version_len);
+ if (version) {
+ if (version_len < 1 || version[0] == 0) {
+ dpp_auth_fail(auth,
+ "Invalid Protocol Version attribute");
+ return NULL;
+ }
+ auth->peer_version = version[0];
+ wpa_printf(MSG_DEBUG, "DPP: Peer protocol version %u",
+ auth->peer_version);
+ }
+#endif /* CONFIG_DPP2 */
+
status = dpp_get_attr(attr_start, attr_len, DPP_ATTR_STATUS,
&status_len);
if (!status || status_len < 1) {
@@ -3985,6 +4103,99 @@ fail:
}
+static int bin_str_eq(const char *val, size_t len, const char *cmp)
+{
+ return os_strlen(cmp) == len && os_memcmp(val, cmp, len) == 0;
+}
+
+
+struct dpp_configuration * dpp_configuration_alloc(const char *type)
+{
+ struct dpp_configuration *conf;
+ const char *end;
+ size_t len;
+
+ conf = os_zalloc(sizeof(*conf));
+ if (!conf)
+ goto fail;
+
+ end = os_strchr(type, ' ');
+ if (end)
+ len = end - type;
+ else
+ len = os_strlen(type);
+
+ if (bin_str_eq(type, len, "psk"))
+ conf->akm = DPP_AKM_PSK;
+ else if (bin_str_eq(type, len, "sae"))
+ conf->akm = DPP_AKM_SAE;
+ else if (bin_str_eq(type, len, "psk-sae") ||
+ bin_str_eq(type, len, "psk+sae"))
+ conf->akm = DPP_AKM_PSK_SAE;
+ else if (bin_str_eq(type, len, "sae-dpp") ||
+ bin_str_eq(type, len, "dpp+sae"))
+ conf->akm = DPP_AKM_SAE_DPP;
+ else if (bin_str_eq(type, len, "psk-sae-dpp") ||
+ bin_str_eq(type, len, "dpp+psk+sae"))
+ conf->akm = DPP_AKM_PSK_SAE_DPP;
+ else if (bin_str_eq(type, len, "dpp"))
+ conf->akm = DPP_AKM_DPP;
+ else
+ goto fail;
+
+ return conf;
+fail:
+ dpp_configuration_free(conf);
+ return NULL;
+}
+
+
+int dpp_akm_psk(enum dpp_akm akm)
+{
+ return akm == DPP_AKM_PSK || akm == DPP_AKM_PSK_SAE ||
+ akm == DPP_AKM_PSK_SAE_DPP;
+}
+
+
+int dpp_akm_sae(enum dpp_akm akm)
+{
+ return akm == DPP_AKM_SAE || akm == DPP_AKM_PSK_SAE ||
+ akm == DPP_AKM_SAE_DPP || akm == DPP_AKM_PSK_SAE_DPP;
+}
+
+
+int dpp_akm_legacy(enum dpp_akm akm)
+{
+ return akm == DPP_AKM_PSK || akm == DPP_AKM_PSK_SAE ||
+ akm == DPP_AKM_SAE;
+}
+
+
+int dpp_akm_dpp(enum dpp_akm akm)
+{
+ return akm == DPP_AKM_DPP || akm == DPP_AKM_SAE_DPP ||
+ akm == DPP_AKM_PSK_SAE_DPP;
+}
+
+
+int dpp_akm_ver2(enum dpp_akm akm)
+{
+ return akm == DPP_AKM_SAE_DPP || akm == DPP_AKM_PSK_SAE_DPP;
+}
+
+
+int dpp_configuration_valid(const struct dpp_configuration *conf)
+{
+ if (conf->ssid_len == 0)
+ return 0;
+ if (dpp_akm_psk(conf->akm) && !conf->passphrase && !conf->psk_set)
+ return 0;
+ if (dpp_akm_sae(conf->akm) && !conf->passphrase)
+ return 0;
+ return 1;
+}
+
+
void dpp_configuration_free(struct dpp_configuration *conf)
{
if (!conf)
@@ -3995,6 +4206,162 @@ void dpp_configuration_free(struct dpp_configuration *conf)
}
+static int dpp_configuration_parse(struct dpp_authentication *auth,
+ const char *cmd)
+{
+ const char *pos, *end;
+ struct dpp_configuration *conf_sta = NULL, *conf_ap = NULL;
+ struct dpp_configuration *conf = NULL;
+
+ pos = os_strstr(cmd, " conf=sta-");
+ if (pos) {
+ conf_sta = dpp_configuration_alloc(pos + 10);
+ if (!conf_sta)
+ goto fail;
+ conf = conf_sta;
+ }
+
+ pos = os_strstr(cmd, " conf=ap-");
+ if (pos) {
+ conf_ap = dpp_configuration_alloc(pos + 9);
+ if (!conf_ap)
+ goto fail;
+ conf = conf_ap;
+ }
+
+ if (!conf)
+ return 0;
+
+ pos = os_strstr(cmd, " ssid=");
+ if (pos) {
+ pos += 6;
+ end = os_strchr(pos, ' ');
+ conf->ssid_len = end ? (size_t) (end - pos) : os_strlen(pos);
+ conf->ssid_len /= 2;
+ if (conf->ssid_len > sizeof(conf->ssid) ||
+ hexstr2bin(pos, conf->ssid, conf->ssid_len) < 0)
+ goto fail;
+ } else {
+#ifdef CONFIG_TESTING_OPTIONS
+ /* use a default SSID for legacy testing reasons */
+ os_memcpy(conf->ssid, "test", 4);
+ conf->ssid_len = 4;
+#else /* CONFIG_TESTING_OPTIONS */
+ goto fail;
+#endif /* CONFIG_TESTING_OPTIONS */
+ }
+
+ pos = os_strstr(cmd, " pass=");
+ if (pos) {
+ size_t pass_len;
+
+ pos += 6;
+ end = os_strchr(pos, ' ');
+ pass_len = end ? (size_t) (end - pos) : os_strlen(pos);
+ pass_len /= 2;
+ if (pass_len > 63 || pass_len < 8)
+ goto fail;
+ conf->passphrase = os_zalloc(pass_len + 1);
+ if (!conf->passphrase ||
+ hexstr2bin(pos, (u8 *) conf->passphrase, pass_len) < 0)
+ goto fail;
+ }
+
+ pos = os_strstr(cmd, " psk=");
+ if (pos) {
+ pos += 5;
+ if (hexstr2bin(pos, conf->psk, PMK_LEN) < 0)
+ goto fail;
+ conf->psk_set = 1;
+ }
+
+ pos = os_strstr(cmd, " group_id=");
+ if (pos) {
+ size_t group_id_len;
+
+ pos += 10;
+ end = os_strchr(pos, ' ');
+ group_id_len = end ? (size_t) (end - pos) : os_strlen(pos);
+ conf->group_id = os_malloc(group_id_len + 1);
+ if (!conf->group_id)
+ goto fail;
+ os_memcpy(conf->group_id, pos, group_id_len);
+ conf->group_id[group_id_len] = '\0';
+ }
+
+ pos = os_strstr(cmd, " expiry=");
+ if (pos) {
+ long int val;
+
+ pos += 8;
+ val = strtol(pos, NULL, 0);
+ if (val <= 0)
+ goto fail;
+ conf->netaccesskey_expiry = val;
+ }
+
+ if (!dpp_configuration_valid(conf))
+ goto fail;
+
+ auth->conf_sta = conf_sta;
+ auth->conf_ap = conf_ap;
+ return 0;
+
+fail:
+ dpp_configuration_free(conf_sta);
+ dpp_configuration_free(conf_ap);
+ return -1;
+}
+
+
+static struct dpp_configurator *
+dpp_configurator_get_id(struct dpp_global *dpp, unsigned int id)
+{
+ struct dpp_configurator *conf;
+
+ if (!dpp)
+ return NULL;
+
+ dl_list_for_each(conf, &dpp->configurator,
+ struct dpp_configurator, list) {
+ if (conf->id == id)
+ return conf;
+ }
+ return NULL;
+}
+
+
+int dpp_set_configurator(struct dpp_global *dpp, void *msg_ctx,
+ struct dpp_authentication *auth,
+ const char *cmd)
+{
+ const char *pos;
+
+ if (!cmd)
+ return 0;
+
+ wpa_printf(MSG_DEBUG, "DPP: Set configurator parameters: %s", cmd);
+
+ pos = os_strstr(cmd, " configurator=");
+ if (pos) {
+ pos += 14;
+ auth->conf = dpp_configurator_get_id(dpp, atoi(pos));
+ if (!auth->conf) {
+ wpa_printf(MSG_INFO,
+ "DPP: Could not find the specified configurator");
+ return -1;
+ }
+ }
+
+ if (dpp_configuration_parse(auth, cmd) < 0) {
+ wpa_msg(msg_ctx, MSG_INFO,
+ "DPP: Failed to set configurator parameters");
+ return -1;
+ }
+ return 0;
+}
+
+
void dpp_auth_deinit(struct dpp_authentication *auth)
{
if (!auth)
@@ -4094,6 +4461,31 @@ fail:
}
+static void dpp_build_legacy_cred_params(struct wpabuf *buf,
+ struct dpp_configuration *conf)
+{
+ if (conf->passphrase && os_strlen(conf->passphrase) < 64) {
+ char pass[63 * 6 + 1];
+
+ json_escape_string(pass, sizeof(pass), conf->passphrase,
+ os_strlen(conf->passphrase));
+ wpabuf_put_str(buf, "\"pass\":\"");
+ wpabuf_put_str(buf, pass);
+ wpabuf_put_str(buf, "\"");
+ os_memset(pass, 0, sizeof(pass));
+ } else if (conf->psk_set) {
+ char psk[2 * sizeof(conf->psk) + 1];
+
+ wpa_snprintf_hex(psk, sizeof(psk),
+ conf->psk, sizeof(conf->psk));
+ wpabuf_put_str(buf, "\"psk_hex\":\"");
+ wpabuf_put_str(buf, psk);
+ wpabuf_put_str(buf, "\"");
+ os_memset(psk, 0, sizeof(psk));
+ }
+}
+
+
static struct wpabuf *
dpp_build_conf_obj_dpp(struct dpp_authentication *auth, int ap,
struct dpp_configuration *conf)
@@ -4114,6 +4506,8 @@ dpp_build_conf_obj_dpp(struct dpp_authentication *auth, int ap,
const EVP_MD *sign_md;
const BIGNUM *r, *s;
size_t extra_len = 1000;
+ int incl_legacy;
+ enum dpp_akm akm;
if (!auth->conf) {
wpa_printf(MSG_INFO,
@@ -4132,6 +4526,13 @@ dpp_build_conf_obj_dpp(struct dpp_authentication *auth, int ap,
goto fail;
}
+ akm = conf->akm;
+ if (dpp_akm_ver2(akm) && auth->peer_version < 2) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Convert DPP+legacy credential to DPP-only for peer that does not support version 2");
+ akm = DPP_AKM_DPP;
+ }
+
#ifdef CONFIG_TESTING_OPTIONS
if (auth->groups_override)
extra_len += os_strlen(auth->groups_override);
@@ -4249,14 +4650,22 @@ skip_groups:
if (!signed3)
goto fail;
+ incl_legacy = dpp_akm_psk(akm) || dpp_akm_sae(akm);
tailroom = 1000;
tailroom += 2 * curve->prime_len * 4 / 3 + os_strlen(auth->conf->kid);
tailroom += signed1_len + signed2_len + signed3_len;
+ if (incl_legacy)
+ tailroom += 1000;
buf = dpp_build_conf_start(auth, conf, tailroom);
if (!buf)
goto fail;
- wpabuf_put_str(buf, "\"cred\":{\"akm\":\"dpp\",\"signedConnector\":\"");
+ wpabuf_printf(buf, "\"cred\":{\"akm\":\"%s\",", dpp_akm_str(akm));
+ if (incl_legacy) {
+ dpp_build_legacy_cred_params(buf, conf);
+ wpabuf_put_str(buf, ",");
+ }
+ wpabuf_put_str(buf, "\"signedConnector\":\"");
wpabuf_put_str(buf, signed1);
wpabuf_put_u8(buf, '.');
wpabuf_put_str(buf, signed2);
@@ -4302,28 +4711,7 @@ dpp_build_conf_obj_legacy(struct dpp_authentication *auth, int ap,
return NULL;
wpabuf_printf(buf, "\"cred\":{\"akm\":\"%s\",", dpp_akm_str(conf->akm));
- if (conf->passphrase) {
- char pass[63 * 6 + 1];
-
- if (os_strlen(conf->passphrase) > 63) {
- wpabuf_free(buf);
- return NULL;
- }
-
- json_escape_string(pass, sizeof(pass), conf->passphrase,
- os_strlen(conf->passphrase));
- wpabuf_put_str(buf, "\"pass\":\"");
- wpabuf_put_str(buf, pass);
- wpabuf_put_str(buf, "\"");
- } else {
- char psk[2 * sizeof(conf->psk) + 1];
-
- wpa_snprintf_hex(psk, sizeof(psk),
- conf->psk, sizeof(conf->psk));
- wpabuf_put_str(buf, "\"psk_hex\":\"");
- wpabuf_put_str(buf, psk);
- wpabuf_put_str(buf, "\"");
- }
+ dpp_build_legacy_cred_params(buf, conf);
wpabuf_put_str(buf, "}}");
wpa_hexdump_ascii_key(MSG_DEBUG, "DPP: Configuration Object (legacy)",
@@ -4354,7 +4742,7 @@ dpp_build_conf_obj(struct dpp_authentication *auth, int ap)
return NULL;
}
- if (conf->akm == DPP_AKM_DPP)
+ if (dpp_akm_dpp(conf->akm))
return dpp_build_conf_obj_dpp(auth, ap, conf);
return dpp_build_conf_obj_legacy(auth, ap, conf);
}
@@ -4378,6 +4766,7 @@ dpp_build_conf_resp(struct dpp_authentication *auth, const u8 *e_nonce,
wpabuf_head(conf), wpabuf_len(conf));
}
status = conf ? DPP_STATUS_OK : DPP_STATUS_CONFIGURE_FAILURE;
+ auth->conf_resp_status = status;
/* { E-nonce, configurationObject}ke */
clear_len = 4 + e_nonce_len;
@@ -4550,6 +4939,7 @@ dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start,
goto fail;
}
wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len);
+ os_memcpy(auth->e_nonce, e_nonce, e_nonce_len);
config_attr = dpp_get_attr(unwrapped, unwrapped_len,
DPP_ATTR_CONFIG_ATTR_OBJ,
@@ -4714,7 +5104,7 @@ static int dpp_parse_cred_legacy(struct dpp_authentication *auth,
os_strlcpy(auth->passphrase, pass->string,
sizeof(auth->passphrase));
} else if (psk_hex && psk_hex->type == JSON_STRING) {
- if (auth->akm == DPP_AKM_SAE) {
+ if (dpp_akm_sae(auth->akm) && !dpp_akm_psk(auth->akm)) {
wpa_printf(MSG_DEBUG,
"DPP: Unexpected psk_hex with akm=sae");
return -1;
@@ -4732,8 +5122,7 @@ static int dpp_parse_cred_legacy(struct dpp_authentication *auth,
return -1;
}
- if ((auth->akm == DPP_AKM_SAE || auth->akm == DPP_AKM_PSK_SAE) &&
- !auth->passphrase[0]) {
+ if (dpp_akm_sae(auth->akm) && !auth->passphrase[0]) {
wpa_printf(MSG_DEBUG, "DPP: No pass for sae found");
return -1;
}
@@ -5246,6 +5635,13 @@ static int dpp_parse_cred_dpp(struct dpp_authentication *auth,
os_memset(&info, 0, sizeof(info));
+ if (dpp_akm_psk(auth->akm) || dpp_akm_sae(auth->akm)) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Legacy credential included in Connector credential");
+ if (dpp_parse_cred_legacy(auth, cred) < 0)
+ return -1;
+ }
+
wpa_printf(MSG_DEBUG, "DPP: Connector credential");
csign = json_get_member(cred, "csign");
@@ -5311,6 +5707,10 @@ const char * dpp_akm_str(enum dpp_akm akm)
return "sae";
case DPP_AKM_PSK_SAE:
return "psk+sae";
+ case DPP_AKM_SAE_DPP:
+ return "dpp+sae";
+ case DPP_AKM_PSK_SAE_DPP:
+ return "dpp+psk+sae";
default:
return "??";
}
@@ -5327,6 +5727,10 @@ static enum dpp_akm dpp_akm_from_str(const char *akm)
return DPP_AKM_PSK_SAE;
if (os_strcmp(akm, "dpp") == 0)
return DPP_AKM_DPP;
+ if (os_strcmp(akm, "dpp+sae") == 0)
+ return DPP_AKM_SAE_DPP;
+ if (os_strcmp(akm, "dpp+psk+sae") == 0)
+ return DPP_AKM_PSK_SAE_DPP;
return DPP_AKM_UNKNOWN;
}
@@ -5390,11 +5794,10 @@ static int dpp_parse_conf_obj(struct dpp_authentication *auth,
}
auth->akm = dpp_akm_from_str(token->string);
- if (auth->akm == DPP_AKM_PSK || auth->akm == DPP_AKM_SAE ||
- auth->akm == DPP_AKM_PSK_SAE) {
+ if (dpp_akm_legacy(auth->akm)) {
if (dpp_parse_cred_legacy(auth, cred) < 0)
goto fail;
- } else if (auth->akm == DPP_AKM_DPP) {
+ } else if (dpp_akm_dpp(auth->akm)) {
if (dpp_parse_cred_dpp(auth, cred) < 0)
goto fail;
} else {
@@ -5423,6 +5826,8 @@ int dpp_conf_resp_rx(struct dpp_authentication *auth,
size_t unwrapped_len = 0;
int ret = -1;
+ auth->conf_resp_status = 255;
+
if (dpp_check_attrs(wpabuf_head(resp), wpabuf_len(resp)) < 0) {
dpp_auth_fail(auth, "Invalid attribute in config response");
return -1;
@@ -5483,6 +5888,7 @@ int dpp_conf_resp_rx(struct dpp_authentication *auth,
"Missing or invalid required DPP Status attribute");
goto fail;
}
+ auth->conf_resp_status = status[0];
wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
if (status[0] != DPP_STATUS_OK) {
dpp_auth_fail(auth, "Configurator rejected configuration");
@@ -5509,6 +5915,146 @@ fail:
}
+#ifdef CONFIG_DPP2
+enum dpp_status_error dpp_conf_result_rx(struct dpp_authentication *auth,
+ const u8 *hdr,
+ const u8 *attr_start, size_t attr_len)
+{
+ const u8 *wrapped_data, *status, *e_nonce;
+ u16 wrapped_data_len, status_len, e_nonce_len;
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *unwrapped = NULL;
+ size_t unwrapped_len = 0;
+ enum dpp_status_error ret = 256;
+
+ wrapped_data = dpp_get_attr(attr_start, attr_len, DPP_ATTR_WRAPPED_DATA,
+ &wrapped_data_len);
+ if (!wrapped_data || wrapped_data_len < AES_BLOCK_SIZE) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required Wrapped Data attribute");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Wrapped data",
+ wrapped_data, wrapped_data_len);
+
+ attr_len = wrapped_data - 4 - attr_start;
+
+ addr[0] = hdr;
+ len[0] = DPP_HDR_LEN;
+ addr[1] = attr_start;
+ len[1] = attr_len;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV ciphertext",
+ wrapped_data, wrapped_data_len);
+ unwrapped_len = wrapped_data_len - AES_BLOCK_SIZE;
+ unwrapped = os_malloc(unwrapped_len);
+ if (!unwrapped)
+ goto fail;
+ if (aes_siv_decrypt(auth->ke, auth->curve->hash_len,
+ wrapped_data, wrapped_data_len,
+ 2, addr, len, unwrapped) < 0) {
+ dpp_auth_fail(auth, "AES-SIV decryption failed");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: AES-SIV cleartext",
+ unwrapped, unwrapped_len);
+
+ if (dpp_check_attrs(unwrapped, unwrapped_len) < 0) {
+ dpp_auth_fail(auth, "Invalid attribute in unwrapped data");
+ goto fail;
+ }
+
+ e_nonce = dpp_get_attr(unwrapped, unwrapped_len,
+ DPP_ATTR_ENROLLEE_NONCE,
+ &e_nonce_len);
+ if (!e_nonce || e_nonce_len != auth->curve->nonce_len) {
+ dpp_auth_fail(auth,
+ "Missing or invalid Enrollee Nonce attribute");
+ goto fail;
+ }
+ wpa_hexdump(MSG_DEBUG, "DPP: Enrollee Nonce", e_nonce, e_nonce_len);
+ if (os_memcmp(e_nonce, auth->e_nonce, e_nonce_len) != 0) {
+ dpp_auth_fail(auth, "Enrollee Nonce mismatch");
+ wpa_hexdump(MSG_DEBUG, "DPP: Expected Enrollee Nonce",
+ auth->e_nonce, e_nonce_len);
+ goto fail;
+ }
+
+ status = dpp_get_attr(unwrapped, unwrapped_len, DPP_ATTR_STATUS,
+ &status_len);
+ if (!status || status_len < 1) {
+ dpp_auth_fail(auth,
+ "Missing or invalid required DPP Status attribute");
+ goto fail;
+ }
+ wpa_printf(MSG_DEBUG, "DPP: Status %u", status[0]);
+ ret = status[0];
+
+fail:
+ bin_clear_free(unwrapped, unwrapped_len);
+ return ret;
+}
+#endif /* CONFIG_DPP2 */
+
+
+struct wpabuf * dpp_build_conf_result(struct dpp_authentication *auth,
+ enum dpp_status_error status)
+{
+ struct wpabuf *msg, *clear;
+ size_t nonce_len, clear_len, attr_len;
+ const u8 *addr[2];
+ size_t len[2];
+ u8 *wrapped;
+
+ nonce_len = auth->curve->nonce_len;
+ clear_len = 5 + 4 + nonce_len;
+ attr_len = 4 + clear_len + AES_BLOCK_SIZE;
+ clear = wpabuf_alloc(clear_len);
+ msg = dpp_alloc_msg(DPP_PA_CONFIGURATION_RESULT, attr_len);
+ if (!clear || !msg)
+ return NULL;
+
+ /* DPP Status */
+ dpp_build_attr_status(clear, status);
+
+ /* E-nonce */
+ wpabuf_put_le16(clear, DPP_ATTR_ENROLLEE_NONCE);
+ wpabuf_put_le16(clear, nonce_len);
+ wpabuf_put_data(clear, auth->e_nonce, nonce_len);
+
+ /* OUI, OUI type, Crypto Suite, DPP frame type */
+ addr[0] = wpabuf_head_u8(msg) + 2;
+ len[0] = 3 + 1 + 1 + 1;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[0]", addr[0], len[0]);
+
+ /* Attributes before Wrapped Data (none) */
+ addr[1] = wpabuf_put(msg, 0);
+ len[1] = 0;
+ wpa_hexdump(MSG_DEBUG, "DDP: AES-SIV AD[1]", addr[1], len[1]);
+
+ /* Wrapped Data */
+ wpabuf_put_le16(msg, DPP_ATTR_WRAPPED_DATA);
+ wpabuf_put_le16(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+ wrapped = wpabuf_put(msg, wpabuf_len(clear) + AES_BLOCK_SIZE);
+
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: AES-SIV cleartext", clear);
+ if (aes_siv_encrypt(auth->ke, auth->curve->hash_len,
+ wpabuf_head(clear), wpabuf_len(clear),
+ 2, addr, len, wrapped) < 0)
+ goto fail;
+
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: Configuration Result attributes", msg);
+ wpabuf_free(clear);
+ return msg;
+fail:
+ wpabuf_free(clear);
+ wpabuf_free(msg);
+ return NULL;
+}
+
+
void dpp_configurator_free(struct dpp_configurator *conf)
{
if (!conf)
@@ -7689,3 +8235,487 @@ fail:
goto out;
}
#endif /* CONFIG_TESTING_OPTIONS */
+
+
+#ifdef CONFIG_DPP2
+
+struct dpp_pfs * dpp_pfs_init(const u8 *net_access_key,
+ size_t net_access_key_len)
+{
+ struct wpabuf *pub = NULL;
+ EVP_PKEY *own_key;
+ struct dpp_pfs *pfs;
+
+ pfs = os_zalloc(sizeof(*pfs));
+ if (!pfs)
+ return NULL;
+
+ own_key = dpp_set_keypair(&pfs->curve, net_access_key,
+ net_access_key_len);
+ if (!own_key) {
+ wpa_printf(MSG_ERROR, "DPP: Failed to parse own netAccessKey");
+ goto fail;
+ }
+ EVP_PKEY_free(own_key);
+
+ pfs->ecdh = crypto_ecdh_init(pfs->curve->ike_group);
+ if (!pfs->ecdh)
+ goto fail;
+
+ pub = crypto_ecdh_get_pubkey(pfs->ecdh, 0);
+ pub = wpabuf_zeropad(pub, pfs->curve->prime_len);
+ if (!pub)
+ goto fail;
+
+ pfs->ie = wpabuf_alloc(5 + wpabuf_len(pub));
+ if (!pfs->ie)
+ goto fail;
+ wpabuf_put_u8(pfs->ie, WLAN_EID_EXTENSION);
+ wpabuf_put_u8(pfs->ie, 1 + 2 + wpabuf_len(pub));
+ wpabuf_put_u8(pfs->ie, WLAN_EID_EXT_OWE_DH_PARAM);
+ wpabuf_put_le16(pfs->ie, pfs->curve->ike_group);
+ wpabuf_put_buf(pfs->ie, pub);
+ wpabuf_free(pub);
+ wpa_hexdump_buf(MSG_DEBUG, "DPP: Diffie-Hellman Parameter element",
+ pfs->ie);
+
+ return pfs;
+fail:
+ wpabuf_free(pub);
+ dpp_pfs_free(pfs);
+ return NULL;
+}
+
+
+int dpp_pfs_process(struct dpp_pfs *pfs, const u8 *peer_ie, size_t peer_ie_len)
+{
+ if (peer_ie_len < 2)
+ return -1;
+ if (WPA_GET_LE16(peer_ie) != pfs->curve->ike_group) {
+ wpa_printf(MSG_DEBUG, "DPP: Peer used different group for PFS");
+ return -1;
+ }
+
+ pfs->secret = crypto_ecdh_set_peerkey(pfs->ecdh, 0, peer_ie + 2,
+ peer_ie_len - 2);
+ pfs->secret = wpabuf_zeropad(pfs->secret, pfs->curve->prime_len);
+ if (!pfs->secret) {
+ wpa_printf(MSG_DEBUG, "DPP: Invalid peer DH public key");
+ return -1;
+ }
+ wpa_hexdump_buf_key(MSG_DEBUG, "DPP: DH shared secret", pfs->secret);
+ return 0;
+}
+
+
+void dpp_pfs_free(struct dpp_pfs *pfs)
+{
+ if (!pfs)
+ return;
+ crypto_ecdh_deinit(pfs->ecdh);
+ wpabuf_free(pfs->ie);
+ wpabuf_clear_free(pfs->secret);
+ os_free(pfs);
+}
+
+#endif /* CONFIG_DPP2 */
+
+
+static unsigned int dpp_next_id(struct dpp_global *dpp)
+{
+ struct dpp_bootstrap_info *bi;
+ unsigned int max_id = 0;
+
+ dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) {
+ if (bi->id > max_id)
+ max_id = bi->id;
+ }
+ return max_id + 1;
+}
+
+
+static int dpp_bootstrap_del(struct dpp_global *dpp, unsigned int id)
+{
+ struct dpp_bootstrap_info *bi, *tmp;
+ int found = 0;
+
+ if (!dpp)
+ return -1;
+
+ dl_list_for_each_safe(bi, tmp, &dpp->bootstrap,
+ struct dpp_bootstrap_info, list) {
+ if (id && bi->id != id)
+ continue;
+ found = 1;
+ dl_list_del(&bi->list);
+ dpp_bootstrap_info_free(bi);
+ }
+
+ if (id == 0)
+ return 0; /* flush succeeds regardless of entries found */
+ return found ? 0 : -1;
+}
+
+
+struct dpp_bootstrap_info * dpp_add_qr_code(struct dpp_global *dpp,
+ const char *uri)
+{
+ struct dpp_bootstrap_info *bi;
+
+ if (!dpp)
+ return NULL;
+
+ bi = dpp_parse_qr_code(uri);
+ if (!bi)
+ return NULL;
+
+ bi->id = dpp_next_id(dpp);
+ dl_list_add(&dpp->bootstrap, &bi->list);
+ return bi;
+}
+
+
+int dpp_bootstrap_gen(struct dpp_global *dpp, const char *cmd)
+{
+ char *chan = NULL, *mac = NULL, *info = NULL, *pk = NULL, *curve = NULL;
+ char *key = NULL;
+ u8 *privkey = NULL;
+ size_t privkey_len = 0;
+ size_t len;
+ int ret = -1;
+ struct dpp_bootstrap_info *bi;
+
+ if (!dpp)
+ return -1;
+
+ bi = os_zalloc(sizeof(*bi));
+ if (!bi)
+ goto fail;
+
+ if (os_strstr(cmd, "type=qrcode"))
+ bi->type = DPP_BOOTSTRAP_QR_CODE;
+ else if (os_strstr(cmd, "type=pkex"))
+ bi->type = DPP_BOOTSTRAP_PKEX;
+ else
+ goto fail;
+
+ chan = get_param(cmd, " chan=");
+ mac = get_param(cmd, " mac=");
+ info = get_param(cmd, " info=");
+ curve = get_param(cmd, " curve=");
+ key = get_param(cmd, " key=");
+
+ if (key) {
+ privkey_len = os_strlen(key) / 2;
+ privkey = os_malloc(privkey_len);
+ if (!privkey ||
+ hexstr2bin(key, privkey, privkey_len) < 0)
+ goto fail;
+ }
+
+ pk = dpp_keygen(bi, curve, privkey, privkey_len);
+ if (!pk)
+ goto fail;
+
+ len = 4; /* "DPP:" */
+ if (chan) {
+ if (dpp_parse_uri_chan_list(bi, chan) < 0)
+ goto fail;
+ len += 3 + os_strlen(chan); /* C:...; */
+ }
+ if (mac) {
+ if (dpp_parse_uri_mac(bi, mac) < 0)
+ goto fail;
+ len += 3 + os_strlen(mac); /* M:...; */
+ }
+ if (info) {
+ if (dpp_parse_uri_info(bi, info) < 0)
+ goto fail;
+ len += 3 + os_strlen(info); /* I:...; */
+ }
+ len += 4 + os_strlen(pk);
+ bi->uri = os_malloc(len + 1);
+ if (!bi->uri)
+ goto fail;
+ os_snprintf(bi->uri, len + 1, "DPP:%s%s%s%s%s%s%s%s%sK:%s;;",
+ chan ? "C:" : "", chan ? chan : "", chan ? ";" : "",
+ mac ? "M:" : "", mac ? mac : "", mac ? ";" : "",
+ info ? "I:" : "", info ? info : "", info ? ";" : "",
+ pk);
+ bi->id = dpp_next_id(dpp);
+ dl_list_add(&dpp->bootstrap, &bi->list);
+ ret = bi->id;
+ bi = NULL;
+fail:
+ os_free(curve);
+ os_free(pk);
+ os_free(chan);
+ os_free(mac);
+ os_free(info);
+ str_clear_free(key);
+ bin_clear_free(privkey, privkey_len);
+ dpp_bootstrap_info_free(bi);
+ return ret;
+}
+
+
+struct dpp_bootstrap_info *
+dpp_bootstrap_get_id(struct dpp_global *dpp, unsigned int id)
+{
+ struct dpp_bootstrap_info *bi;
+
+ if (!dpp)
+ return NULL;
+
+ dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) {
+ if (bi->id == id)
+ return bi;
+ }
+ return NULL;
+}
+
+
+int dpp_bootstrap_remove(struct dpp_global *dpp, const char *id)
+{
+ unsigned int id_val;
+
+ if (os_strcmp(id, "*") == 0) {
+ id_val = 0;
+ } else {
+ id_val = atoi(id);
+ if (id_val == 0)
+ return -1;
+ }
+
+ return dpp_bootstrap_del(dpp, id_val);
+}
+
+
+struct dpp_bootstrap_info *
+dpp_pkex_finish(struct dpp_global *dpp, struct dpp_pkex *pkex, const u8 *peer,
+ unsigned int freq)
+{
+ struct dpp_bootstrap_info *bi;
+
+ bi = os_zalloc(sizeof(*bi));
+ if (!bi)
+ return NULL;
+ bi->id = dpp_next_id(dpp);
+ bi->type = DPP_BOOTSTRAP_PKEX;
+ os_memcpy(bi->mac_addr, peer, ETH_ALEN);
+ bi->num_freq = 1;
+ bi->freq[0] = freq;
+ bi->curve = pkex->own_bi->curve;
+ bi->pubkey = pkex->peer_bootstrap_key;
+ pkex->peer_bootstrap_key = NULL;
+ if (dpp_bootstrap_key_hash(bi) < 0) {
+ dpp_bootstrap_info_free(bi);
+ return NULL;
+ }
+ dpp_pkex_free(pkex);
+ dl_list_add(&dpp->bootstrap, &bi->list);
+ return bi;
+}
+
+
+const char * dpp_bootstrap_get_uri(struct dpp_global *dpp, unsigned int id)
+{
+ struct dpp_bootstrap_info *bi;
+
+ bi = dpp_bootstrap_get_id(dpp, id);
+ if (!bi)
+ return NULL;
+ return bi->uri;
+}
+
+
+int dpp_bootstrap_info(struct dpp_global *dpp, int id,
+ char *reply, int reply_size)
+{
+ struct dpp_bootstrap_info *bi;
+
+ bi = dpp_bootstrap_get_id(dpp, id);
+ if (!bi)
+ return -1;
+ return os_snprintf(reply, reply_size, "type=%s\n"
+ "mac_addr=" MACSTR "\n"
+ "info=%s\n"
+ "num_freq=%u\n"
+ "curve=%s\n",
+ dpp_bootstrap_type_txt(bi->type),
+ MAC2STR(bi->mac_addr),
+ bi->info ? bi->info : "",
+ bi->num_freq,
+ bi->curve->name);
+}
+
+
+void dpp_bootstrap_find_pair(struct dpp_global *dpp, const u8 *i_bootstrap,
+ const u8 *r_bootstrap,
+ struct dpp_bootstrap_info **own_bi,
+ struct dpp_bootstrap_info **peer_bi)
+{
+ struct dpp_bootstrap_info *bi;
+
+ *own_bi = NULL;
+ *peer_bi = NULL;
+ if (!dpp)
+ return;
+
+ dl_list_for_each(bi, &dpp->bootstrap, struct dpp_bootstrap_info, list) {
+ if (!*own_bi && bi->own &&
+ os_memcmp(bi->pubkey_hash, r_bootstrap,
+ SHA256_MAC_LEN) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Found matching own bootstrapping information");
+ *own_bi = bi;
+ }
+
+ if (!*peer_bi && !bi->own &&
+ os_memcmp(bi->pubkey_hash, i_bootstrap,
+ SHA256_MAC_LEN) == 0) {
+ wpa_printf(MSG_DEBUG,
+ "DPP: Found matching peer bootstrapping information");
+ *peer_bi = bi;
+ }
+
+ if (*own_bi && *peer_bi)
+ break;
+ }
+
+}
+
+
+static unsigned int dpp_next_configurator_id(struct dpp_global *dpp)
+{
+ struct dpp_configurator *conf;
+ unsigned int max_id = 0;
+
+ dl_list_for_each(conf, &dpp->configurator, struct dpp_configurator,
+ list) {
+ if (conf->id > max_id)
+ max_id = conf->id;
+ }
+ return max_id + 1;
+}
+
+
+int dpp_configurator_add(struct dpp_global *dpp, const char *cmd)
+{
+ char *curve = NULL;
+ char *key = NULL;
+ u8 *privkey = NULL;
+ size_t privkey_len = 0;
+ int ret = -1;
+ struct dpp_configurator *conf = NULL;
+
+ curve = get_param(cmd, " curve=");
+ key = get_param(cmd, " key=");
+
+ if (key) {
+ privkey_len = os_strlen(key) / 2;
+ privkey = os_malloc(privkey_len);
+ if (!privkey ||
+ hexstr2bin(key, privkey, privkey_len) < 0)
+ goto fail;
+ }
+
+ conf = dpp_keygen_configurator(curve, privkey, privkey_len);
+ if (!conf)
+ goto fail;
+
+ conf->id = dpp_next_configurator_id(dpp);
+ dl_list_add(&dpp->configurator, &conf->list);
+ ret = conf->id;
+ conf = NULL;
+fail:
+ os_free(curve);
+ str_clear_free(key);
+ bin_clear_free(privkey, privkey_len);
+ dpp_configurator_free(conf);
+ return ret;
+}
+
+
+static int dpp_configurator_del(struct dpp_global *dpp, unsigned int id)
+{
+ struct dpp_configurator *conf, *tmp;
+ int found = 0;
+
+ if (!dpp)
+ return -1;
+
+ dl_list_for_each_safe(conf, tmp, &dpp->configurator,
+ struct dpp_configurator, list) {
+ if (id && conf->id != id)
+ continue;
+ found = 1;
+ dl_list_del(&conf->list);
+ dpp_configurator_free(conf);
+ }
+
+ if (id == 0)
+ return 0; /* flush succeeds regardless of entries found */
+ return found ? 0 : -1;
+}
+
+
+int dpp_configurator_remove(struct dpp_global *dpp, const char *id)
+{
+ unsigned int id_val;
+
+ if (os_strcmp(id, "*") == 0) {
+ id_val = 0;
+ } else {
+ id_val = atoi(id);
+ if (id_val == 0)
+ return -1;
+ }
+
+ return dpp_configurator_del(dpp, id_val);
+}
+
+
+int dpp_configurator_get_key_id(struct dpp_global *dpp, unsigned int id,
+ char *buf, size_t buflen)
+{
+ struct dpp_configurator *conf;
+
+ conf = dpp_configurator_get_id(dpp, id);
+ if (!conf)
+ return -1;
+
+ return dpp_configurator_get_key(conf, buf, buflen);
+}
+
+
+struct dpp_global * dpp_global_init(void)
+{
+ struct dpp_global *dpp;
+
+ dpp = os_zalloc(sizeof(*dpp));
+ if (!dpp)
+ return NULL;
+
+ dl_list_init(&dpp->bootstrap);
+ dl_list_init(&dpp->configurator);
+
+ return dpp;
+}
+
+
+void dpp_global_clear(struct dpp_global *dpp)
+{
+ if (!dpp)
+ return;
+
+ dpp_bootstrap_del(dpp, 0);
+ dpp_configurator_del(dpp, 0);
+}
+
+
+void dpp_global_deinit(struct dpp_global *dpp)
+{
+ dpp_global_clear(dpp);
+ os_free(dpp);
+}
diff --git a/src/common/dpp.h b/src/common/dpp.h
index 25759088a57ef..5a6d8cc79c2cf 100644
--- a/src/common/dpp.h
+++ b/src/common/dpp.h
@@ -1,6 +1,7 @@
/*
* DPP functionality shared between hostapd and wpa_supplicant
* Copyright (c) 2017, Qualcomm Atheros, Inc.
+ * Copyright (c) 2018-2019, The Linux Foundation
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -9,12 +10,16 @@
#ifndef DPP_H
#define DPP_H
+#ifdef CONFIG_DPP
#include <openssl/x509.h>
#include "utils/list.h"
#include "common/wpa_common.h"
#include "crypto/sha256.h"
+struct crypto_ecdh;
+struct dpp_global;
+
#define DPP_HDR_LEN (4 + 2) /* OUI, OUI Type, Crypto Suite, DPP frame type */
enum dpp_public_action_frame_type {
@@ -27,6 +32,7 @@ enum dpp_public_action_frame_type {
DPP_PA_PKEX_EXCHANGE_RESP = 8,
DPP_PA_PKEX_COMMIT_REVEAL_REQ = 9,
DPP_PA_PKEX_COMMIT_REVEAL_RESP = 10,
+ DPP_PA_CONFIGURATION_RESULT = 11,
};
enum dpp_attribute_id {
@@ -54,6 +60,8 @@ enum dpp_attribute_id {
DPP_ATTR_TRANSACTION_ID = 0x1016,
DPP_ATTR_BOOTSTRAP_INFO = 0x1017,
DPP_ATTR_CHANNEL = 0x1018,
+ DPP_ATTR_PROTOCOL_VERSION = 0x1019,
+ DPP_ATTR_ENVELOPED_DATA = 0x101A,
};
enum dpp_status_error {
@@ -66,6 +74,7 @@ enum dpp_status_error {
DPP_STATUS_RESPONSE_PENDING = 6,
DPP_STATUS_INVALID_CONNECTOR = 7,
DPP_STATUS_NO_MATCH = 8,
+ DPP_STATUS_CONFIG_REJECTED = 9,
};
#define DPP_CAPAB_ENROLLEE BIT(0)
@@ -141,7 +150,9 @@ enum dpp_akm {
DPP_AKM_DPP,
DPP_AKM_PSK,
DPP_AKM_SAE,
- DPP_AKM_PSK_SAE
+ DPP_AKM_PSK_SAE,
+ DPP_AKM_SAE_DPP,
+ DPP_AKM_PSK_SAE_DPP,
};
struct dpp_configuration {
@@ -158,10 +169,12 @@ struct dpp_configuration {
/* For legacy configuration */
char *passphrase;
u8 psk[32];
+ int psk_set;
};
struct dpp_authentication {
void *msg_ctx;
+ u8 peer_version;
const struct dpp_curve_params *curve;
struct dpp_bootstrap_info *peer_bi;
struct dpp_bootstrap_info *own_bi;
@@ -169,6 +182,7 @@ struct dpp_authentication {
u8 waiting_pubkey_hash[SHA256_MAC_LEN];
int response_pending;
enum dpp_status_error auth_resp_status;
+ enum dpp_status_error conf_resp_status;
u8 peer_mac_addr[ETH_ALEN];
u8 i_nonce[DPP_MAX_NONCE_LEN];
u8 r_nonce[DPP_MAX_NONCE_LEN];
@@ -204,6 +218,8 @@ struct dpp_authentication {
u8 allowed_roles;
int configurator;
int remove_on_tx_status;
+ int connect_on_tx_status;
+ int waiting_conf_result;
int auth_success;
struct wpabuf *conf_req;
const struct wpabuf *conf_resp; /* owned by GAS server */
@@ -336,6 +352,7 @@ enum dpp_test_behavior {
DPP_TEST_STOP_AT_AUTH_RESP = 88,
DPP_TEST_STOP_AT_AUTH_CONF = 89,
DPP_TEST_STOP_AT_CONF_REQ = 90,
+ DPP_TEST_REJECT_CONFIG = 91,
};
extern enum dpp_test_behavior dpp_test;
@@ -382,13 +399,28 @@ int dpp_auth_conf_rx(struct dpp_authentication *auth, const u8 *hdr,
const u8 *attr_start, size_t attr_len);
int dpp_notify_new_qr_code(struct dpp_authentication *auth,
struct dpp_bootstrap_info *peer_bi);
+struct dpp_configuration * dpp_configuration_alloc(const char *type);
+int dpp_akm_psk(enum dpp_akm akm);
+int dpp_akm_sae(enum dpp_akm akm);
+int dpp_akm_legacy(enum dpp_akm akm);
+int dpp_akm_dpp(enum dpp_akm akm);
+int dpp_akm_ver2(enum dpp_akm akm);
+int dpp_configuration_valid(const struct dpp_configuration *conf);
void dpp_configuration_free(struct dpp_configuration *conf);
+int dpp_set_configurator(struct dpp_global *dpp, void *msg_ctx,
+ struct dpp_authentication *auth,
+ const char *cmd);
void dpp_auth_deinit(struct dpp_authentication *auth);
struct wpabuf *
dpp_conf_req_rx(struct dpp_authentication *auth, const u8 *attr_start,
size_t attr_len);
int dpp_conf_resp_rx(struct dpp_authentication *auth,
const struct wpabuf *resp);
+enum dpp_status_error dpp_conf_result_rx(struct dpp_authentication *auth,
+ const u8 *hdr,
+ const u8 *attr_start, size_t attr_len);
+struct wpabuf * dpp_build_conf_result(struct dpp_authentication *auth,
+ enum dpp_status_error status);
struct wpabuf * dpp_alloc_msg(enum dpp_public_action_frame_type type,
size_t len);
const u8 * dpp_get_attr(const u8 *buf, size_t len, u16 req_id, u16 *ret_len);
@@ -432,4 +464,42 @@ void dpp_pkex_free(struct dpp_pkex *pkex);
char * dpp_corrupt_connector_signature(const char *connector);
+
+struct dpp_pfs {
+ struct crypto_ecdh *ecdh;
+ const struct dpp_curve_params *curve;
+ struct wpabuf *ie;
+ struct wpabuf *secret;
+};
+
+struct dpp_pfs * dpp_pfs_init(const u8 *net_access_key,
+ size_t net_access_key_len);
+int dpp_pfs_process(struct dpp_pfs *pfs, const u8 *peer_ie, size_t peer_ie_len);
+void dpp_pfs_free(struct dpp_pfs *pfs);
+
+struct dpp_bootstrap_info * dpp_add_qr_code(struct dpp_global *dpp,
+ const char *uri);
+int dpp_bootstrap_gen(struct dpp_global *dpp, const char *cmd);
+struct dpp_bootstrap_info *
+dpp_bootstrap_get_id(struct dpp_global *dpp, unsigned int id);
+int dpp_bootstrap_remove(struct dpp_global *dpp, const char *id);
+struct dpp_bootstrap_info *
+dpp_pkex_finish(struct dpp_global *dpp, struct dpp_pkex *pkex, const u8 *peer,
+ unsigned int freq);
+const char * dpp_bootstrap_get_uri(struct dpp_global *dpp, unsigned int id);
+int dpp_bootstrap_info(struct dpp_global *dpp, int id,
+ char *reply, int reply_size);
+void dpp_bootstrap_find_pair(struct dpp_global *dpp, const u8 *i_bootstrap,
+ const u8 *r_bootstrap,
+ struct dpp_bootstrap_info **own_bi,
+ struct dpp_bootstrap_info **peer_bi);
+int dpp_configurator_add(struct dpp_global *dpp, const char *cmd);
+int dpp_configurator_remove(struct dpp_global *dpp, const char *id);
+int dpp_configurator_get_key_id(struct dpp_global *dpp, unsigned int id,
+ char *buf, size_t buflen);
+struct dpp_global * dpp_global_init(void);
+void dpp_global_clear(struct dpp_global *dpp);
+void dpp_global_deinit(struct dpp_global *dpp);
+
+#endif /* CONFIG_DPP */
#endif /* DPP_H */
diff --git a/src/common/hw_features_common.c b/src/common/hw_features_common.c
index db40379271850..49ed80657521a 100644
--- a/src/common/hw_features_common.c
+++ b/src/common/hw_features_common.c
@@ -87,13 +87,29 @@ int hw_get_chan(struct hostapd_hw_modes *mode, int freq)
int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan,
int sec_chan)
{
- int ok, j, first;
+ int ok, first;
int allowed[] = { 36, 44, 52, 60, 100, 108, 116, 124, 132, 140,
149, 157, 165, 184, 192 };
size_t k;
+ struct hostapd_channel_data *p_chan, *s_chan;
+ const int ht40_plus = pri_chan < sec_chan;
- if (pri_chan == sec_chan || !sec_chan)
- return 1; /* HT40 not used */
+ p_chan = hw_get_channel_chan(mode, pri_chan, NULL);
+ if (!p_chan)
+ return 0;
+
+ if (pri_chan == sec_chan || !sec_chan) {
+ if (chan_pri_allowed(p_chan))
+ return 1; /* HT40 not used */
+
+ wpa_printf(MSG_ERROR, "Channel %d is not allowed as primary",
+ pri_chan);
+ return 0;
+ }
+
+ s_chan = hw_get_channel_chan(mode, sec_chan, NULL);
+ if (!s_chan)
+ return 0;
wpa_printf(MSG_DEBUG,
"HT40: control channel: %d secondary channel: %d",
@@ -101,16 +117,9 @@ int allowed_ht40_channel_pair(struct hostapd_hw_modes *mode, int pri_chan,
/* Verify that HT40 secondary channel is an allowed 20 MHz
* channel */
- ok = 0;
- for (j = 0; j < mode->num_channels; j++) {
- struct hostapd_channel_data *chan = &mode->channels[j];
- if (!(chan->flag & HOSTAPD_CHAN_DISABLED) &&
- chan->chan == sec_chan) {
- ok = 1;
- break;
- }
- }
- if (!ok) {
+ if ((s_chan->flag & HOSTAPD_CHAN_DISABLED) ||
+ (ht40_plus && !(p_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40P)) ||
+ (!ht40_plus && !(p_chan->allowed_bw & HOSTAPD_CHAN_WIDTH_40M))) {
wpa_printf(MSG_ERROR, "HT40 secondary channel %d not allowed",
sec_chan);
return 0;
@@ -553,3 +562,59 @@ int ieee80211ac_cap_check(u32 hw, u32 conf)
}
#endif /* CONFIG_IEEE80211AC */
+
+
+u32 num_chan_to_bw(int num_chans)
+{
+ switch (num_chans) {
+ case 2:
+ case 4:
+ case 8:
+ return num_chans * 20;
+ default:
+ return 20;
+ }
+}
+
+
+/* check if BW is applicable for channel */
+int chan_bw_allowed(const struct hostapd_channel_data *chan, u32 bw,
+ int ht40_plus, int pri)
+{
+ u32 bw_mask;
+
+ switch (bw) {
+ case 20:
+ bw_mask = HOSTAPD_CHAN_WIDTH_20;
+ break;
+ case 40:
+ /* HT 40 MHz support declared only for primary channel,
+ * just skip 40 MHz secondary checking */
+ if (pri && ht40_plus)
+ bw_mask = HOSTAPD_CHAN_WIDTH_40P;
+ else if (pri && !ht40_plus)
+ bw_mask = HOSTAPD_CHAN_WIDTH_40M;
+ else
+ bw_mask = 0;
+ break;
+ case 80:
+ bw_mask = HOSTAPD_CHAN_WIDTH_80;
+ break;
+ case 160:
+ bw_mask = HOSTAPD_CHAN_WIDTH_160;
+ break;
+ default:
+ bw_mask = 0;
+ break;
+ }
+
+ return (chan->allowed_bw & bw_mask) == bw_mask;
+}
+
+
+/* check if channel is allowed to be used as primary */
+int chan_pri_allowed(const struct hostapd_channel_data *chan)
+{
+ return !(chan->flag & HOSTAPD_CHAN_DISABLED) &&
+ (chan->allowed_bw & HOSTAPD_CHAN_WIDTH_20);
+}
diff --git a/src/common/hw_features_common.h b/src/common/hw_features_common.h
index 9cddbd50e56a9..eb1f1c57f10f5 100644
--- a/src/common/hw_features_common.h
+++ b/src/common/hw_features_common.h
@@ -39,4 +39,9 @@ void set_disable_ht40(struct ieee80211_ht_capabilities *htcaps,
int disabled);
int ieee80211ac_cap_check(u32 hw, u32 conf);
+u32 num_chan_to_bw(int num_chans);
+int chan_bw_allowed(const struct hostapd_channel_data *chan, u32 bw,
+ int ht40_plus, int pri);
+int chan_pri_allowed(const struct hostapd_channel_data *chan);
+
#endif /* HW_FEATURES_COMMON_H */
diff --git a/src/common/ieee802_11_common.c b/src/common/ieee802_11_common.c
index e1ef27795b99a..e42a327449eb8 100644
--- a/src/common/ieee802_11_common.c
+++ b/src/common/ieee802_11_common.c
@@ -1,6 +1,6 @@
/*
* IEEE 802.11 Common routines
- * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -126,6 +126,10 @@ static int ieee802_11_parse_vendor_specific(const u8 *pos, size_t elen,
elems->roaming_cons_sel = pos;
elems->roaming_cons_sel_len = elen;
break;
+ case MULTI_AP_OUI_TYPE:
+ elems->multi_ap = pos;
+ elems->multi_ap_len = elen;
+ break;
default:
wpa_printf(MSG_MSGDUMP, "Unknown WFA "
"information element ignored "
@@ -266,6 +270,14 @@ static int ieee802_11_parse_extension(const u8 *pos, size_t elen,
elems->password_id = pos;
elems->password_id_len = elen;
break;
+ case WLAN_EID_EXT_HE_CAPABILITIES:
+ elems->he_capabilities = pos;
+ elems->he_capabilities_len = elen;
+ break;
+ case WLAN_EID_EXT_OCV_OCI:
+ elems->oci = pos;
+ elems->oci_len = elen;
+ break;
default:
if (show_errors) {
wpa_printf(MSG_MSGDUMP,
@@ -291,29 +303,17 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
struct ieee802_11_elems *elems,
int show_errors)
{
- size_t left = len;
- const u8 *pos = start;
+ const struct element *elem;
int unknown = 0;
os_memset(elems, 0, sizeof(*elems));
- while (left >= 2) {
- u8 id, elen;
+ if (!start)
+ return ParseOK;
- id = *pos++;
- elen = *pos++;
- left -= 2;
-
- if (elen > left) {
- if (show_errors) {
- wpa_printf(MSG_DEBUG, "IEEE 802.11 element "
- "parse failed (id=%d elen=%d "
- "left=%lu)",
- id, elen, (unsigned long) left);
- wpa_hexdump(MSG_MSGDUMP, "IEs", start, len);
- }
- return ParseFailed;
- }
+ for_each_element(elem, start, len) {
+ u8 id = elem->id, elen = elem->datalen;
+ const u8 *pos = elem->data;
switch (id) {
case WLAN_EID_SSID:
@@ -461,8 +461,7 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
elems->mic = pos;
elems->mic_len = elen;
/* after mic everything is encrypted, so stop. */
- left = elen;
- break;
+ goto done;
case WLAN_EID_MULTI_BAND:
if (elems->mb_ies.nof_ies >= MAX_NOF_MB_IES_SUPPORTED) {
wpa_printf(MSG_MSGDUMP,
@@ -521,35 +520,33 @@ ParseRes ieee802_11_parse_elems(const u8 *start, size_t len,
id, elen);
break;
}
-
- left -= elen;
- pos += elen;
}
- if (left)
+ if (!for_each_element_completed(elem, start, len)) {
+ if (show_errors) {
+ wpa_printf(MSG_DEBUG,
+ "IEEE 802.11 element parse failed @%d",
+ (int) (start + len - (const u8 *) elem));
+ wpa_hexdump(MSG_MSGDUMP, "IEs", start, len);
+ }
return ParseFailed;
+ }
+done:
return unknown ? ParseUnknown : ParseOK;
}
int ieee802_11_ie_count(const u8 *ies, size_t ies_len)
{
+ const struct element *elem;
int count = 0;
- const u8 *pos, *end;
if (ies == NULL)
return 0;
- pos = ies;
- end = ies + ies_len;
-
- while (end - pos >= 2) {
- if (2 + pos[1] > end - pos)
- break;
+ for_each_element(elem, ies, ies_len)
count++;
- pos += 2 + pos[1];
- }
return count;
}
@@ -559,24 +556,17 @@ struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
u32 oui_type)
{
struct wpabuf *buf;
- const u8 *end, *pos, *ie;
-
- pos = ies;
- end = ies + ies_len;
- ie = NULL;
+ const struct element *elem, *found = NULL;
- while (end - pos > 1) {
- if (2 + pos[1] > end - pos)
- return NULL;
- if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
- WPA_GET_BE32(&pos[2]) == oui_type) {
- ie = pos;
+ for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, ies_len) {
+ if (elem->datalen >= 4 &&
+ WPA_GET_BE32(elem->data) == oui_type) {
+ found = elem;
break;
}
- pos += 2 + pos[1];
}
- if (ie == NULL)
+ if (!found)
return NULL; /* No specified vendor IE found */
buf = wpabuf_alloc(ies_len);
@@ -587,13 +577,9 @@ struct wpabuf * ieee802_11_vendor_ie_concat(const u8 *ies, size_t ies_len,
* There may be multiple vendor IEs in the message, so need to
* concatenate their data fields.
*/
- while (end - pos > 1) {
- if (2 + pos[1] > end - pos)
- break;
- if (pos[0] == WLAN_EID_VENDOR_SPECIFIC && pos[1] >= 4 &&
- WPA_GET_BE32(&pos[2]) == oui_type)
- wpabuf_put_data(buf, pos + 6, pos[1] - 4);
- pos += 2 + pos[1];
+ for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, ies_len) {
+ if (elem->datalen >= 4 && WPA_GET_BE32(elem->data) == oui_type)
+ wpabuf_put_data(buf, elem->data + 4, elem->datalen - 4);
}
return buf;
@@ -896,6 +882,41 @@ enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq,
}
+int ieee80211_chaninfo_to_channel(unsigned int freq, enum chan_width chanwidth,
+ int sec_channel, u8 *op_class, u8 *channel)
+{
+ int vht = CHAN_WIDTH_UNKNOWN;
+
+ switch (chanwidth) {
+ case CHAN_WIDTH_UNKNOWN:
+ case CHAN_WIDTH_20_NOHT:
+ case CHAN_WIDTH_20:
+ case CHAN_WIDTH_40:
+ vht = VHT_CHANWIDTH_USE_HT;
+ break;
+ case CHAN_WIDTH_80:
+ vht = VHT_CHANWIDTH_80MHZ;
+ break;
+ case CHAN_WIDTH_80P80:
+ vht = VHT_CHANWIDTH_80P80MHZ;
+ break;
+ case CHAN_WIDTH_160:
+ vht = VHT_CHANWIDTH_160MHZ;
+ break;
+ }
+
+ if (ieee80211_freq_to_channel_ext(freq, sec_channel, vht, op_class,
+ channel) == NUM_HOSTAPD_MODES) {
+ wpa_printf(MSG_WARNING,
+ "Cannot determine operating class and channel (freq=%u chanwidth=%d sec_channel=%d)",
+ freq, chanwidth, sec_channel);
+ return -1;
+ }
+
+ return 0;
+}
+
+
static const char *const us_op_class_cc[] = {
"US", "CA", NULL
};
@@ -1297,27 +1318,27 @@ const char * fc2str(u16 fc)
int mb_ies_info_by_ies(struct mb_ies_info *info, const u8 *ies_buf,
size_t ies_len)
{
+ const struct element *elem;
+
os_memset(info, 0, sizeof(*info));
- while (ies_buf && ies_len >= 2 &&
- info->nof_ies < MAX_NOF_MB_IES_SUPPORTED) {
- size_t len = 2 + ies_buf[1];
+ if (!ies_buf)
+ return 0;
- if (len > ies_len) {
- wpa_hexdump(MSG_DEBUG, "Truncated IEs",
- ies_buf, ies_len);
- return -1;
- }
+ for_each_element_id(elem, WLAN_EID_MULTI_BAND, ies_buf, ies_len) {
+ if (info->nof_ies >= MAX_NOF_MB_IES_SUPPORTED)
+ return 0;
- if (ies_buf[0] == WLAN_EID_MULTI_BAND) {
- wpa_printf(MSG_DEBUG, "MB IE of %zu bytes found", len);
- info->ies[info->nof_ies].ie = ies_buf + 2;
- info->ies[info->nof_ies].ie_len = ies_buf[1];
- info->nof_ies++;
- }
+ wpa_printf(MSG_DEBUG, "MB IE of %u bytes found",
+ elem->datalen + 2);
+ info->ies[info->nof_ies].ie = elem->data;
+ info->ies[info->nof_ies].ie_len = elem->datalen;
+ info->nof_ies++;
+ }
- ies_len -= len;
- ies_buf += len;
+ if (!for_each_element_completed(elem, ies_buf, ies_len)) {
+ wpa_hexdump(MSG_DEBUG, "Truncated IEs", ies_buf, ies_len);
+ return -1;
}
return 0;
@@ -1440,22 +1461,13 @@ size_t global_op_class_size = ARRAY_SIZE(global_op_class);
*/
const u8 * get_ie(const u8 *ies, size_t len, u8 eid)
{
- const u8 *end;
+ const struct element *elem;
if (!ies)
return NULL;
- end = ies + len;
-
- while (end - ies > 1) {
- if (2 + ies[1] > end - ies)
- break;
-
- if (ies[0] == eid)
- return ies;
-
- ies += 2 + ies[1];
- }
+ for_each_element_id(elem, eid, ies, len)
+ return &elem->id;
return NULL;
}
@@ -1473,22 +1485,26 @@ const u8 * get_ie(const u8 *ies, size_t len, u8 eid)
*/
const u8 * get_ie_ext(const u8 *ies, size_t len, u8 ext)
{
- const u8 *end;
+ const struct element *elem;
if (!ies)
return NULL;
- end = ies + len;
+ for_each_element_extid(elem, ext, ies, len)
+ return &elem->id;
+
+ return NULL;
+}
- while (end - ies > 1) {
- if (2 + ies[1] > end - ies)
- break;
- if (ies[0] == WLAN_EID_EXTENSION && ies[1] >= 1 &&
- ies[2] == ext)
- return ies;
+const u8 * get_vendor_ie(const u8 *ies, size_t len, u32 vendor_type)
+{
+ const struct element *elem;
- ies += 2 + ies[1];
+ for_each_element_id(elem, WLAN_EID_VENDOR_SPECIFIC, ies, len) {
+ if (elem->datalen >= 4 &&
+ vendor_type == WPA_GET_BE32(elem->data))
+ return &elem->id;
}
return NULL;
@@ -1519,6 +1535,26 @@ size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len)
}
+size_t add_multi_ap_ie(u8 *buf, size_t len, u8 value)
+{
+ u8 *pos = buf;
+
+ if (len < 9)
+ return 0;
+
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = 7; /* len */
+ WPA_PUT_BE24(pos, OUI_WFA);
+ pos += 3;
+ *pos++ = MULTI_AP_OUI_TYPE;
+ *pos++ = MULTI_AP_SUB_ELEM_TYPE;
+ *pos++ = 1; /* len */
+ *pos++ = value;
+
+ return pos - buf;
+}
+
+
static const struct country_op_class us_op_class[] = {
{ 1, 115 },
{ 2, 118 },
@@ -1664,6 +1700,27 @@ const struct oper_class_map * get_oper_class(const char *country, u8 op_class)
}
+int oper_class_bw_to_int(const struct oper_class_map *map)
+{
+ switch (map->bw) {
+ case BW20:
+ return 20;
+ case BW40PLUS:
+ case BW40MINUS:
+ return 40;
+ case BW80:
+ return 80;
+ case BW80P80:
+ case BW160:
+ return 160;
+ case BW2160:
+ return 2160;
+ default:
+ return 0;
+ }
+}
+
+
int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep,
size_t nei_rep_len)
{
@@ -1764,3 +1821,11 @@ int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep,
return nei_pos - nei_rep;
}
+
+
+int ieee802_11_ext_capab(const u8 *ie, unsigned int capab)
+{
+ if (!ie || ie[1] <= capab / 8)
+ return 0;
+ return !!(ie[2 + capab / 8] & BIT(capab % 8));
+}
diff --git a/src/common/ieee802_11_common.h b/src/common/ieee802_11_common.h
index ff7e51de3dc9b..d41bd39e7a93c 100644
--- a/src/common/ieee802_11_common.h
+++ b/src/common/ieee802_11_common.h
@@ -1,6 +1,6 @@
/*
* IEEE 802.11 Common routines
- * Copyright (c) 2002-2012, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -10,6 +10,13 @@
#define IEEE802_11_COMMON_H
#include "defs.h"
+#include "ieee802_11_defs.h"
+
+struct element {
+ u8 id;
+ u8 datalen;
+ u8 data[];
+} STRUCT_PACKED;
struct hostapd_hw_modes;
@@ -84,6 +91,9 @@ struct ieee802_11_elems {
const u8 *power_capab;
const u8 *roaming_cons_sel;
const u8 *password_id;
+ const u8 *oci;
+ const u8 *multi_ap;
+ const u8 *he_capabilities;
u8 ssid_len;
u8 supp_rates_len;
@@ -130,6 +140,9 @@ struct ieee802_11_elems {
u8 power_capab_len;
u8 roaming_cons_sel_len;
u8 password_id_len;
+ u8 oci_len;
+ u8 multi_ap_len;
+ u8 he_capabilities_len;
struct mb_ies_info mb_ies;
};
@@ -160,6 +173,8 @@ int ieee80211_chan_to_freq(const char *country, u8 op_class, u8 chan);
enum hostapd_hw_mode ieee80211_freq_to_channel_ext(unsigned int freq,
int sec_channel, int vht,
u8 *op_class, u8 *channel);
+int ieee80211_chaninfo_to_channel(unsigned int freq, enum chan_width chanwidth,
+ int sec_channel, u8 *op_class, u8 *channel);
int ieee80211_is_dfs(int freq, const struct hostapd_hw_modes *modes,
u16 num_modes);
enum phy_type ieee80211_get_phy_type(int freq, int ht, int vht);
@@ -186,9 +201,12 @@ extern size_t global_op_class_size;
const u8 * get_ie(const u8 *ies, size_t len, u8 eid);
const u8 * get_ie_ext(const u8 *ies, size_t len, u8 ext);
+const u8 * get_vendor_ie(const u8 *ies, size_t len, u32 vendor_type);
size_t mbo_add_ie(u8 *buf, size_t len, const u8 *attr, size_t attr_len);
+size_t add_multi_ap_ie(u8 *buf, size_t len, u8 value);
+
struct country_op_class {
u8 country_op_class;
u8 global_op_class;
@@ -197,8 +215,58 @@ struct country_op_class {
u8 country_to_global_op_class(const char *country, u8 op_class);
const struct oper_class_map * get_oper_class(const char *country, u8 op_class);
+int oper_class_bw_to_int(const struct oper_class_map *map);
int ieee802_11_parse_candidate_list(const char *pos, u8 *nei_rep,
size_t nei_rep_len);
+int ieee802_11_ext_capab(const u8 *ie, unsigned int capab);
+
+/* element iteration helpers */
+#define for_each_element(_elem, _data, _datalen) \
+ for (_elem = (const struct element *) (_data); \
+ (const u8 *) (_data) + (_datalen) - (const u8 *) _elem >= \
+ (int) sizeof(*_elem) && \
+ (const u8 *) (_data) + (_datalen) - (const u8 *) _elem >= \
+ (int) sizeof(*_elem) + _elem->datalen; \
+ _elem = (const struct element *) (_elem->data + _elem->datalen))
+
+#define for_each_element_id(element, _id, data, datalen) \
+ for_each_element(element, data, datalen) \
+ if (element->id == (_id))
+
+#define for_each_element_extid(element, extid, _data, _datalen) \
+ for_each_element(element, _data, _datalen) \
+ if (element->id == WLAN_EID_EXTENSION && \
+ element->datalen > 0 && \
+ element->data[0] == (extid))
+
+#define for_each_subelement(sub, element) \
+ for_each_element(sub, (element)->data, (element)->datalen)
+
+#define for_each_subelement_id(sub, id, element) \
+ for_each_element_id(sub, id, (element)->data, (element)->datalen)
+
+#define for_each_subelement_extid(sub, extid, element) \
+ for_each_element_extid(sub, extid, (element)->data, (element)->datalen)
+
+/**
+ * for_each_element_completed - Determine if element parsing consumed all data
+ * @element: Element pointer after for_each_element() or friends
+ * @data: Same data pointer as passed to for_each_element() or friends
+ * @datalen: Same data length as passed to for_each_element() or friends
+ *
+ * This function returns 1 if all the data was parsed or considered
+ * while walking the elements. Only use this if your for_each_element()
+ * loop cannot be broken out of, otherwise it always returns 0.
+ *
+ * If some data was malformed, this returns %false since the last parsed
+ * element will not fill the whole remaining data.
+ */
+static inline int for_each_element_completed(const struct element *element,
+ const void *data, size_t datalen)
+{
+ return (const u8 *) element == (const u8 *) data + datalen;
+}
+
#endif /* IEEE802_11_COMMON_H */
diff --git a/src/common/ieee802_11_defs.h b/src/common/ieee802_11_defs.h
index 762e731ab022c..adaa8931093ee 100644
--- a/src/common/ieee802_11_defs.h
+++ b/src/common/ieee802_11_defs.h
@@ -1,6 +1,6 @@
/*
* IEEE 802.11 Frame type definitions
- * Copyright (c) 2002-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi>
* Copyright (c) 2007-2008 Intel Corporation
*
* This software may be distributed under the terms of the BSD license.
@@ -334,7 +334,7 @@
#define WLAN_EID_LOCATION_PARAMETERS 82
#define WLAN_EID_NONTRANSMITTED_BSSID_CAPA 83
#define WLAN_EID_SSID_LIST 84
-#define WLAN_EID_MLTIPLE_BSSID_INDEX 85
+#define WLAN_EID_MULTIPLE_BSSID_INDEX 85
#define WLAN_EID_FMS_DESCRIPTOR 86
#define WLAN_EID_FMS_REQUEST 87
#define WLAN_EID_FMS_RESPONSE 88
@@ -467,7 +467,89 @@
#define WLAN_EID_EXT_PASSWORD_IDENTIFIER 33
#define WLAN_EID_EXT_HE_CAPABILITIES 35
#define WLAN_EID_EXT_HE_OPERATION 36
-
+#define WLAN_EID_EXT_HE_MU_EDCA_PARAMS 38
+#define WLAN_EID_EXT_OCV_OCI 54
+
+/* Extended Capabilities field */
+#define WLAN_EXT_CAPAB_20_40_COEX 0
+#define WLAN_EXT_CAPAB_GLK 1
+#define WLAN_EXT_CAPAB_EXT_CHAN_SWITCH 2
+#define WLAN_EXT_CAPAB_GLK_GCR 3
+#define WLAN_EXT_CAPAB_PSMP 4
+/* 5 - Reserved */
+#define WLAN_EXT_CAPAB_S_PSMP 6
+#define WLAN_EXT_CAPAB_EVENT 7
+#define WLAN_EXT_CAPAB_DIAGNOSTICS 8
+#define WLAN_EXT_CAPAB_MULTICAST_DIAGNOSTICS 9
+#define WLAN_EXT_CAPAB_LOCATION_TRACKING 10
+#define WLAN_EXT_CAPAB_FMS 11
+#define WLAN_EXT_CAPAB_PROXY_ARP 12
+#define WLAN_EXT_CAPAB_COLL_INTERF_REP 13
+#define WLAN_EXT_CAPAB_CIVIC_LOCATION 14
+#define WLAN_EXT_CAPAB_GEOSPATIAL_LOCATION 15
+#define WLAN_EXT_CAPAB_TFS 16
+#define WLAN_EXT_CAPAB_WNM_SLEEP_MODE 17
+#define WLAN_EXT_CAPAB_TIM_BROADCAST 18
+#define WLAN_EXT_CAPAB_BSS_TRANSITION 19
+#define WLAN_EXT_CAPAB_QOS_TRAFFIC 20
+#define WLAN_EXT_CAPAB_AC_STA_COUNT 21
+#define WLAN_EXT_CAPAB_MULTIPLE_BSSID 22
+#define WLAN_EXT_CAPAB_TIMING_MEASUREMENT 23
+#define WLAN_EXT_CAPAB_CHANNEL_USAGE 24
+#define WLAN_EXT_CAPAB_SSID_LIST 25
+#define WLAN_EXT_CAPAB_DMS 26
+#define WLAN_EXT_CAPAB_UTF_TSF_OFFSET 27
+#define WLAN_EXT_CAPAB_TPU_BUFFER_STA 28
+#define WLAN_EXT_CAPAB_TDLS_PEER_PSM 29
+#define WLAN_EXT_CAPAB_TDLS_CHANNEL_SWITCH 30
+#define WLAN_EXT_CAPAB_INTERWORKING 31
+#define WLAN_EXT_CAPAB_QOS_MAP 32
+#define WLAN_EXT_CAPAB_EBR 33
+#define WLAN_EXT_CAPAB_SSPN_INTERFACE 34
+/* 35 - Reserved */
+#define WLAN_EXT_CAPAB_MSGCF 36
+#define WLAN_EXT_CAPAB_TDLS 37
+#define WLAN_EXT_CAPAB_TDLS_PROHIBITED 38
+#define WLAN_EXT_CAPAB_TDLS_CHANNEL_SWITCH_PROHIBITED 39
+#define WLAN_EXT_CAPAB_REJECT_UNADMITTED_FRAME 40
+#define WLAN_EXT_CAPAB_
+/* 41-43 - Service Interval Granularity */
+#define WLAN_EXT_CAPAB_IDENTIFIER_LOCATION 44
+#define WLAN_EXT_CAPAB_U_APSD_COEX 45
+#define WLAN_EXT_CAPAB_WNM_NOTIFCATION 46
+#define WLAN_EXT_CAPAB_QAB 47
+#define WLAN_EXT_CAPAB_UTF_8_SSID 48
+#define WLAN_EXT_CAPAB_QMF 49
+#define WLAN_EXT_CAPAB_QMF_RECONFIG 50
+#define WLAN_EXT_CAPAB_ROBUST_AV_STREAMING 51
+#define WLAN_EXT_CAPAB_ADVANCED_GCR 52
+#define WLAN_EXT_CAPAB_MESH_GCR 53
+#define WLAN_EXT_CAPAB_SCS 54
+#define WLAN_EXT_CAPAB_QLOAD_REPORT 55
+#define WLAN_EXT_CAPAB_ALT_EDCA 56
+#define WLAN_EXT_CAPAB_UNPROT_TXOP_NEG 57
+#define WLAN_EXT_CAPAB_PROT_TXOP_NEG 58
+/* 59 - Reserved */
+#define WLAN_EXT_CAPAB_PROT_QLOAD_REPORT 60
+#define WLAN_EXT_CAPAB_TDLS_WIDER_BW 61
+#define WLAN_EXT_CAPAB_OPMODE_NOTIF 62
+#define WLAN_EXT_CAPAB_
+/* 63-64 - Max Number of MSDUs In A-MSDU */
+#define WLAN_EXT_CAPAB_CHANNEL_SCHEDULE_MGMT 65
+#define WLAN_EXT_CAPAB_GEODB_INBAND_ENABLING_SIGNAL 66
+#define WLAN_EXT_CAPAB_NETWORK_CHANNEL_CTRL 67
+#define WLAN_EXT_CAPAB_WHITE_SPACE_MAP 68
+#define WLAN_EXT_CAPAB_CHANNEL_AVAIL_QUERY 69
+#define WLAN_EXT_CAPAB_FTM_RESPONDER 70
+#define WLAN_EXT_CAPAB_FTM_INITIATOR 71
+#define WLAN_EXT_CAPAB_FILS 72
+#define WLAN_EXT_CAPAB_EXT_SPECTRUM_MGMT 73
+#define WLAN_EXT_CAPAB_FUTURE_CHANNEL_GUIDANCE 74
+#define WLAN_EXT_CAPAB_PAD 75
+/* 76-79 - Reserved */
+#define WLAN_EXT_CAPAB_COMPLETE_NON_TX_BSSID_PROFILE 80
+#define WLAN_EXT_CAPAB_SAE_PW_ID 81
+#define WLAN_EXT_CAPAB_SAE_PW_ID_EXCLUSIVELY 82
/* Action frame categories (IEEE Std 802.11-2016, 9.4.1.11, Table 9-76) */
#define WLAN_ACTION_SPECTRUM_MGMT 0
@@ -865,10 +947,12 @@ struct ieee80211_mgmt {
struct {
u8 action;
u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
+ u8 variable[]; /* OCI element */
} STRUCT_PACKED sa_query_req;
struct {
u8 action; /* */
u8 trans_id[WLAN_SA_QUERY_TR_ID_LEN];
+ u8 variable[]; /* OCI element */
} STRUCT_PACKED sa_query_resp;
struct {
u8 action;
@@ -1210,6 +1294,13 @@ struct ieee80211_ampe_ie {
#define MBO_OUI_TYPE 22
#define OWE_IE_VENDOR_TYPE 0x506f9a1c
#define OWE_OUI_TYPE 28
+#define MULTI_AP_OUI_TYPE 0x1B
+
+#define MULTI_AP_SUB_ELEM_TYPE 0x06
+#define MULTI_AP_TEAR_DOWN BIT(4)
+#define MULTI_AP_FRONTHAUL_BSS BIT(5)
+#define MULTI_AP_BACKHAUL_BSS BIT(6)
+#define MULTI_AP_BACKHAUL_STA BIT(7)
#define WMM_OUI_TYPE 2
#define WMM_OUI_SUBTYPE_INFORMATION_ELEMENT 0
@@ -1347,13 +1438,15 @@ enum wmm_ac {
#define HS20_PPS_MO_ID_PRESENT 0x02
#define HS20_ANQP_DOMAIN_ID_PRESENT 0x04
#ifndef HS20_VERSION
-#define HS20_VERSION 0x10 /* Release 2 */
+#define HS20_VERSION 0x20 /* Release 3 */
#endif /* HS20_VERSION */
/* WNM-Notification WFA vendors specific subtypes */
#define HS20_WNM_SUB_REM_NEEDED 0
#define HS20_WNM_DEAUTH_IMMINENT_NOTICE 1
-#define HS20_WNM_T_C_ACCEPTANCE 2
+#define WFA_WNM_NOTIF_SUBELEM_NON_PREF_CHAN_REPORT 2
+#define WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA 3
+#define HS20_WNM_T_C_ACCEPTANCE 4
#define HS20_DEAUTH_REASON_CODE_BSS 0
#define HS20_DEAUTH_REASON_CODE_ESS 1
@@ -1442,12 +1535,6 @@ enum mbo_transition_reject_reason {
MBO_TRANSITION_REJECT_REASON_SERVICES = 6,
};
-/* MBO v0.0_r19, 4.4: WNM-Notification vendor subelements */
-enum wfa_wnm_notif_subelem_id {
- WFA_WNM_NOTIF_SUBELEM_NON_PREF_CHAN_REPORT = 2,
- WFA_WNM_NOTIF_SUBELEM_CELL_DATA_CAPA = 3,
-};
-
/* MBO v0.0_r27, 4.3: MBO ANQP-elements */
#define MBO_ANQP_OUI_TYPE 0x12
#define MBO_ANQP_SUBTYPE_QUERY_LIST 1
@@ -1841,11 +1928,14 @@ enum beacon_report_mode {
};
/* IEEE Std 802.11-2016, Table 9-88 - Beacon Request subelement IDs */
+/* IEEE P802.11-REVmd/D2.0, Table 9-106 - Optional subelement IDs for
+ * Beacon request */
#define WLAN_BEACON_REQUEST_SUBELEM_SSID 0
#define WLAN_BEACON_REQUEST_SUBELEM_INFO 1 /* Beacon Reporting */
#define WLAN_BEACON_REQUEST_SUBELEM_DETAIL 2 /* Reporting Detail */
#define WLAN_BEACON_REQUEST_SUBELEM_REQUEST 10
#define WLAN_BEACON_REQUEST_SUBELEM_AP_CHANNEL 51 /* AP Channel Report */
+#define WLAN_BEACON_REQUEST_SUBELEM_LAST_INDICATION 164
#define WLAN_BEACON_REQUEST_SUBELEM_VENDOR 221
/*
@@ -1895,9 +1985,21 @@ struct rrm_measurement_beacon_report {
} STRUCT_PACKED;
/* IEEE Std 802.11-2016, Table 9-112 - Beacon report Subelement IDs */
+/* IEEE P802.11-REVmd/D2.0, Table 9-130 - Optional subelement IDs for
+ * Beacon report */
#define WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY 1
+#define WLAN_BEACON_REPORT_SUBELEM_FRAME_BODY_FRAGMENT_ID 2
+#define WLAN_BEACON_REPORT_SUBELEM_LAST_INDICATION 164
#define WLAN_BEACON_REPORT_SUBELEM_VENDOR 221
+/* IEEE P802.11-REVmd/D2.0, Table 9-232 - Data field format of the
+ * Reported Frame Body Fragment ID subelement */
+#define REPORTED_FRAME_BODY_SUBELEM_LEN 4
+#define REPORTED_FRAME_BODY_MORE_FRAGMENTS BIT(7)
+
+/* IEEE P802.11-REVmd/D2.0, 9.4.2.21.7 - Beacon report */
+#define BEACON_REPORT_LAST_INDICATION_SUBELEM_LEN 3
+
/* IEEE Std 802.11ad-2012 - Multi-band element */
struct multi_band_ie {
u8 eid; /* WLAN_EID_MULTI_BAND */
@@ -2000,14 +2102,15 @@ enum nr_chan_width {
};
struct ieee80211_he_capabilities {
- u8 he_mac_capab_info[5];
- u8 he_phy_capab_info[9];
+ u8 he_mac_capab_info[6];
+ u8 he_phy_capab_info[11];
u8 he_txrx_mcs_support[12]; /* TODO: 4, 8, or 12 octets */
/* PPE Thresholds (optional) */
} STRUCT_PACKED;
struct ieee80211_he_operation {
- u32 he_oper_params;
+ u32 he_oper_params; /* HE Operation Parameters[3] and
+ * BSS Color Information[1] */
u8 he_mcs_nss_set[2];
u8 vht_op_info_chwidth;
u8 vht_op_info_chan_center_freq_seg0_idx;
@@ -2024,28 +2127,55 @@ struct ieee80211_he_operation {
#define HE_PHYCAP_MU_BEAMFORMER_CAPAB ((u8) BIT(1))
/* HE Operation defines */
+/* HE Operation Parameters and BSS Color Information fields */
#define HE_OPERATION_BSS_COLOR_MASK ((u32) (BIT(0) | BIT(1) | \
BIT(2) | BIT(3) | \
BIT(4) | BIT(5)))
-#define HE_OPERATION_DFLT_PE_DURATION_MASK ((u32) (BIT(6) | BIT(7) | \
- BIT(8)))
-#define HE_OPERATION_DFLT_PE_DURATION_OFFSET 6
-#define HE_OPERATION_TWT_REQUIRED ((u32) BIT(9))
-#define HE_OPERATION_RTS_THRESHOLD_MASK ((u32) (BIT(10) | BIT(11) | \
- BIT(12) | BIT(13) | \
+#define HE_OPERATION_PARTIAL_BSS_COLOR ((u32) BIT(6))
+#define HE_OPERATION_BSS_COLOR_DISABLED ((u32) BIT(7))
+#define HE_OPERATION_DFLT_PE_DURATION_MASK ((u32) (BIT(8) | BIT(9) | \
+ BIT(10)))
+#define HE_OPERATION_DFLT_PE_DURATION_OFFSET 8
+#define HE_OPERATION_TWT_REQUIRED ((u32) BIT(11))
+#define HE_OPERATION_RTS_THRESHOLD_MASK ((u32) (BIT(12) | BIT(13) | \
BIT(14) | BIT(15) | \
BIT(16) | BIT(17) | \
- BIT(18) | BIT(19)))
-#define HE_OPERATION_RTS_THRESHOLD_OFFSET 10
-#define HE_OPERATION_PARTIAL_BSS_COLOR ((u32) BIT(20))
-#define HE_OPERATION_MAX_BSSID_INDICATOR_MASK ((u32) (BIT(21) | BIT(22) | \
- BIT(23) | BIT(24) | \
- BIT(25) | BIT(26) | \
- BIT(27) | BIT(28)))
-#define HE_OPERATION_MAX_BSSID_INDICATOR_OFFSET 21
-#define HE_OPERATION_TX_BSSID_INDICATOR ((u32) BIT(29))
-#define HE_OPERATION_BSS_COLOR_DISABLED ((u32) BIT(30))
-#define HE_OPERATION_BSS_DUAL_BEACON ((u32) BIT(31))
+ BIT(18) | BIT(19) | \
+ BIT(20) | BIT(21)))
+#define HE_OPERATION_RTS_THRESHOLD_OFFSET 12
+
+struct ieee80211_he_mu_edca_parameter_set {
+ u8 he_qos_info;
+ u8 he_mu_ac_be_param[3];
+ u8 he_mu_ac_bk_param[3];
+ u8 he_mu_ac_vi_param[3];
+ u8 he_mu_ac_vo_param[3];
+} STRUCT_PACKED;
+
+/* HE MU AC parameter record field format */
+/* ACI/AIFSN */
+#define HE_MU_AC_PARAM_ACI_IDX 0
+#define HE_MU_AC_PARAM_AIFSN ((u8) (BIT(0) | BIT(1) | BIT(2) | BIT(3)))
+#define HE_MU_AC_PARAM_ACM ((u8) BIT(4))
+#define HE_MU_AC_PARAM_ACI ((u8) (BIT(5) | BIT(6)))
+/* B7: Reserved */
+
+/* ECWmin/ECWmax */
+#define HE_MU_AC_PARAM_ECW_IDX 1
+#define HE_MU_AC_PARAM_ECWMIN ((u8) (BIT(0) | BIT(1) | BIT(2) | BIT(3)))
+#define HE_MU_AC_PARAM_ECWMAX ((u8) (BIT(4) | BIT(5) | BIT(6) | BIT(7)))
+
+/* MU EDCA Timer */
+#define HE_MU_AC_PARAM_TIMER_IDX 2
+
+/* HE QoS Info field */
+#define HE_QOS_INFO_EDCA_PARAM_SET_COUNT ((u8) (BIT(0) | BIT(1) | \
+ BIT(2) | BIT(3)))
+#define HE_QOS_INFO_Q_ACK ((u8) (BIT(4)))
+#define HE_QOS_INFO_QUEUE_REQUEST ((u8) (BIT(5)))
+#define HE_QOS_INFO_TXOP_REQUEST ((u8) (BIT(6)))
+/* B7: Reserved if sent by an AP; More Data Ack if sent by a non-AP STA */
+#define HE_QOS_INFO_MORE_DATA_ACK ((u8) (BIT(7)))
/* DPP Public Action frame identifiers - OUI_WFA */
#define DPP_OUI_TYPE 0x1A
diff --git a/src/common/linux_bridge.h b/src/common/linux_bridge.h
index 7b768464fb542..84386e60f7504 100644
--- a/src/common/linux_bridge.h
+++ b/src/common/linux_bridge.h
@@ -9,6 +9,21 @@
#ifndef LINUX_BRIDGE_H
#define LINUX_BRIDGE_H
+/* This ioctl is defined in linux/sockios.h */
+
+#ifndef SIOCBRADDBR
+#define SIOCBRADDBR 0x89a0
+#endif
+#ifndef SIOCBRDELBR
+#define SIOCBRDELBR 0x89a1
+#endif
+#ifndef SIOCBRADDIF
+#define SIOCBRADDIF 0x89a2
+#endif
+#ifndef SIOCBRDELIF
+#define SIOCBRDELIF 0x89a3
+#endif
+
/* This interface is defined in linux/if_bridge.h */
#define BRCTL_GET_VERSION 0
diff --git a/src/common/ocv.c b/src/common/ocv.c
new file mode 100644
index 0000000000000..06badfbfb4549
--- /dev/null
+++ b/src/common/ocv.c
@@ -0,0 +1,172 @@
+/*
+ * Operating Channel Validation (OCV)
+ * Copyright (c) 2018, Mathy Vanhoef
+ *
+ * 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 "drivers/driver.h"
+#include "common/ieee802_11_common.h"
+#include "ocv.h"
+
+/**
+ * Caller of OCV functionality may use various debug output functions, so store
+ * the error here and let the caller use an appropriate debug output function.
+ */
+char ocv_errorstr[256];
+
+
+int ocv_derive_all_parameters(struct oci_info *oci)
+{
+ const struct oper_class_map *op_class_map;
+
+ oci->freq = ieee80211_chan_to_freq(NULL, oci->op_class, oci->channel);
+ if (oci->freq < 0) {
+ wpa_printf(MSG_INFO,
+ "Error interpreting OCI: unrecognized opclass/channel pair (%d/%d)",
+ oci->op_class, oci->channel);
+ return -1;
+ }
+
+ op_class_map = get_oper_class(NULL, oci->op_class);
+ if (!op_class_map) {
+ wpa_printf(MSG_INFO,
+ "Error interpreting OCI: Unrecognized opclass (%d)",
+ oci->op_class);
+ return -1;
+ }
+
+ oci->chanwidth = oper_class_bw_to_int(op_class_map);
+ oci->sec_channel = 0;
+ if (op_class_map->bw == BW40PLUS)
+ oci->sec_channel = 1;
+ else if (op_class_map->bw == BW40MINUS)
+ oci->sec_channel = -1;
+
+ return 0;
+}
+
+
+int ocv_insert_oci(struct wpa_channel_info *ci, u8 **argpos)
+{
+ u8 op_class, channel;
+ u8 *pos = *argpos;
+
+ if (ieee80211_chaninfo_to_channel(ci->frequency, ci->chanwidth,
+ ci->sec_channel,
+ &op_class, &channel) < 0) {
+ wpa_printf(MSG_WARNING,
+ "Cannot determine operating class and channel for OCI element");
+ return -1;
+ }
+
+ *pos++ = op_class;
+ *pos++ = channel;
+ *pos++ = ci->seg1_idx;
+
+ *argpos = pos;
+ return 0;
+}
+
+
+int ocv_insert_oci_kde(struct wpa_channel_info *ci, u8 **argpos)
+{
+ u8 *pos = *argpos;
+
+ *pos++ = WLAN_EID_VENDOR_SPECIFIC;
+ *pos++ = RSN_SELECTOR_LEN + 3;
+ RSN_SELECTOR_PUT(pos, RSN_KEY_DATA_OCI);
+ pos += RSN_SELECTOR_LEN;
+
+ *argpos = pos;
+ return ocv_insert_oci(ci, argpos);
+}
+
+
+int ocv_insert_extended_oci(struct wpa_channel_info *ci, u8 *pos)
+{
+ *pos++ = WLAN_EID_EXTENSION;
+ *pos++ = 1 + OCV_OCI_LEN;
+ *pos++ = WLAN_EID_EXT_OCV_OCI;
+ return ocv_insert_oci(ci, &pos);
+}
+
+
+int ocv_verify_tx_params(const u8 *oci_ie, size_t oci_ie_len,
+ struct wpa_channel_info *ci, int tx_chanwidth,
+ int tx_seg1_idx)
+{
+ struct oci_info oci;
+
+ if (!oci_ie) {
+ os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
+ "OCV failed: did not receive mandatory OCI");
+ return -1;
+ }
+
+ if (oci_ie_len != 3) {
+ os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
+ "OCV failed: received OCI of unexpected length (%d)",
+ (int) oci_ie_len);
+ return -1;
+ }
+
+ os_memset(&oci, 0, sizeof(oci));
+ oci.op_class = oci_ie[0];
+ oci.channel = oci_ie[1];
+ oci.seg1_idx = oci_ie[2];
+ if (ocv_derive_all_parameters(&oci) != 0) {
+ os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
+ "OCV failed: unable to interpret received OCI");
+ return -1;
+ }
+
+ /* Primary frequency used to send frames to STA must match the STA's */
+ if ((int) ci->frequency != oci.freq) {
+ os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
+ "OCV failed: primary channel mismatch in received OCI (we use %d but receiver is using %d)",
+ ci->frequency, oci.freq);
+ return -1;
+ }
+
+ /* We shouldn't transmit with a higher bandwidth than the STA supports
+ */
+ if (tx_chanwidth > oci.chanwidth) {
+ os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
+ "OCV failed: channel bandwidth mismatch in received OCI (we use %d but receiver only supports %d)",
+ tx_chanwidth, oci.chanwidth);
+ return -1;
+ }
+
+ /*
+ * Secondary channel only needs be checked for 40 MHz in the 2.4 GHz
+ * band. In the 5 GHz band it's verified through the primary frequency.
+ * Note that the field ci->sec_channel is only filled in when we use
+ * 40 MHz.
+ */
+ if (tx_chanwidth == 40 && ci->frequency < 2500 &&
+ ci->sec_channel != oci.sec_channel) {
+ os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
+ "OCV failed: secondary channel mismatch in received OCI (we use %d but receiver is using %d)",
+ ci->sec_channel, oci.sec_channel);
+ return -1;
+ }
+
+ /*
+ * When using a 160 or 80+80 MHz channel to transmit, verify that we use
+ * the same segments as the receiver by comparing frequency segment 1.
+ */
+ if ((ci->chanwidth == CHAN_WIDTH_160 ||
+ ci->chanwidth == CHAN_WIDTH_80P80) &&
+ tx_seg1_idx != oci.seg1_idx) {
+ os_snprintf(ocv_errorstr, sizeof(ocv_errorstr),
+ "OCV failed: frequency segment 1 mismatch in received OCI (we use %d but receiver is using %d)",
+ tx_seg1_idx, oci.seg1_idx);
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/src/common/ocv.h b/src/common/ocv.h
new file mode 100644
index 0000000000000..6379d9d06c9ac
--- /dev/null
+++ b/src/common/ocv.h
@@ -0,0 +1,40 @@
+/*
+ * Operating Channel Validation (OCV)
+ * Copyright (c) 2018, Mathy Vanhoef
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#ifndef OCV_H
+#define OCV_H
+
+struct wpa_channel_info;
+
+struct oci_info {
+ /* Values in the OCI element */
+ u8 op_class;
+ u8 channel;
+ u8 seg1_idx;
+
+ /* Derived values for easier verification */
+ int freq;
+ int sec_channel;
+ int chanwidth;
+};
+
+#define OCV_OCI_LEN 3
+#define OCV_OCI_EXTENDED_LEN (3 + OCV_OCI_LEN)
+#define OCV_OCI_KDE_LEN (2 + RSN_SELECTOR_LEN + OCV_OCI_LEN)
+
+extern char ocv_errorstr[256];
+
+int ocv_derive_all_parameters(struct oci_info *oci);
+int ocv_insert_oci(struct wpa_channel_info *ci, u8 **argpos);
+int ocv_insert_oci_kde(struct wpa_channel_info *ci, u8 **argpos);
+int ocv_insert_extended_oci(struct wpa_channel_info *ci, u8 *pos);
+int ocv_verify_tx_params(const u8 *oci_ie, size_t oci_ie_len,
+ struct wpa_channel_info *ci, int tx_chanwidth,
+ int tx_seg1_idx);
+
+#endif /* OCV_H */
diff --git a/src/common/qca-vendor.h b/src/common/qca-vendor.h
index 7c75d0804553d..c34a3bc1f3e41 100644
--- a/src/common/qca-vendor.h
+++ b/src/common/qca-vendor.h
@@ -42,8 +42,12 @@ enum qca_radiotap_vendor_ids {
*
* @QCA_NL80211_VENDOR_SUBCMD_AVOID_FREQUENCY: Recommendation of frequency
* ranges to avoid to reduce issues due to interference or internal
- * co-existence information in the driver. The event data structure is
- * defined in struct qca_avoid_freq_list.
+ * co-existence information in the driver. These frequencies aim to
+ * minimize the traffic but not to totally avoid the traffic. That said
+ * for a P2P use case, these frequencies are allowed for the P2P
+ * discovery/negotiation but avoid the group to get formed on these
+ * frequencies. The event data structure is defined in
+ * struct qca_avoid_freq_list.
*
* @QCA_NL80211_VENDOR_SUBCMD_DFS_CAPABILITY: Command to check driver support
* for DFS offloading.
@@ -499,6 +503,27 @@ enum qca_radiotap_vendor_ids {
*
* Based on the config provided, FW will boost the weight and prioritize
* the traffic for that subsystem (WLAN/BT/Zigbee).
+ *
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_AKMS: This command is used to query
+ * the supported AKM suite selectorss from the driver. It returns the list
+ * of supported AKMs in the attribute NL80211_ATTR_AKM_SUITES.
+ * @QCA_NL80211_VENDOR_SUBCMD_GET_FW_STATE: This command is used to get firmware
+ * state from the driver. It returns the firmware state in the attribute
+ * QCA_WLAN_VENDOR_ATTR_FW_STATE.
+ * @QCA_NL80211_VENDOR_SUBCMD_PEER_STATS_CACHE_FLUSH: This vendor subcommand
+ * is used by the driver to flush per-peer cached statistics to user space
+ * application. This interface is used as an event from the driver to
+ * user space application. Attributes for this event are specified in
+ * enum qca_wlan_vendor_attr_peer_stats_cache_params.
+ * QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_DATA attribute is expected to be
+ * sent in the event.
+ * @QCA_NL80211_VENDOR_SUBCMD_MPTA_HELPER_CONFIG: This sub command is used to
+ * improve the success rate of Zigbee joining network.
+ * Due to PTA master limitation, Zigbee joining network success rate is
+ * low while WLAN is working. The WLAN driver needs to configure some
+ * parameters including Zigbee state and specific WLAN periods to enhance
+ * PTA master. All these parameters are delivered by the attributes
+ * defined in enum qca_mpta_helper_vendor_attr.
*/
enum qca_nl80211_vendor_subcmds {
QCA_NL80211_VENDOR_SUBCMD_UNSPEC = 0,
@@ -663,6 +688,10 @@ enum qca_nl80211_vendor_subcmds {
QCA_NL80211_VENDOR_SUBCMD_PEER_CFR_CAPTURE_CFG = 173,
QCA_NL80211_VENDOR_SUBCMD_THROUGHPUT_CHANGE_EVENT = 174,
QCA_NL80211_VENDOR_SUBCMD_COEX_CONFIG = 175,
+ QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_AKMS = 176,
+ QCA_NL80211_VENDOR_SUBCMD_GET_FW_STATE = 177,
+ QCA_NL80211_VENDOR_SUBCMD_PEER_STATS_CACHE_FLUSH = 178,
+ QCA_NL80211_VENDOR_SUBCMD_MPTA_HELPER_CONFIG = 179,
};
enum qca_wlan_vendor_attr {
@@ -843,6 +872,18 @@ enum qca_wlan_vendor_attr {
* to report the corresponding antenna index to the chain RSSI value
*/
QCA_WLAN_VENDOR_ATTR_ANTENNA_INFO = 40,
+ /* Used in QCA_NL80211_VENDOR_SUBCMD_GET_CHAIN_RSSI command to report
+ * the specific antenna EVM value (unsigned 32 bit value). With a
+ * determinate group of antennas, the driver specifies the EVM value
+ * for each antenna ID, and application extract them in user space.
+ */
+ QCA_WLAN_VENDOR_ATTR_CHAIN_EVM = 41,
+ /*
+ * Used in QCA_NL80211_VENDOR_SUBCMD_GET_FW_STATE command to report
+ * wlan firmware current state. FW state is an unsigned 8 bit value,
+ * one of the values in enum qca_wlan_vendor_attr_fw_state.
+ */
+ QCA_WLAN_VENDOR_ATTR_FW_STATE = 42,
/* keep last */
QCA_WLAN_VENDOR_ATTR_AFTER_LAST,
@@ -854,6 +895,49 @@ enum qca_roaming_policy {
QCA_ROAMING_ALLOWED_WITHIN_ESS,
};
+/**
+ * enum qca_roam_reason - Represents the reason codes for roaming. Used by
+ * QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REASON.
+ *
+ * @QCA_ROAM_REASON_UNKNOWN: Any reason that do not classify under the below
+ * reasons.
+ *
+ * @QCA_ROAM_REASON_PER: Roam triggered when packet error rates (PER) breached
+ * the configured threshold.
+ *
+ * @QCA_ROAM_REASON_BEACON_MISS: Roam triggered due to the continuous configured
+ * beacon misses from the then connected AP.
+ *
+ * @QCA_ROAM_REASON_POOR_RSSI: Roam triggered due to the poor RSSI reported
+ * by the connected AP.
+ *
+ * @QCA_ROAM_REASON_BETTER_RSSI: Roam triggered for finding a BSS with a better
+ * RSSI than the connected BSS. Here the RSSI of the current BSS is not poor.
+ *
+ * @QCA_ROAM_REASON_CONGESTION: Roam triggered considering the connected channel
+ * or environment being very noisy or congested.
+ *
+ * @QCA_ROAM_REASON_EXPLICIT_REQUEST: Roam triggered due to an explicit request
+ * from the user (user space).
+ *
+ * @QCA_ROAM_REASON_BTM: Roam triggered due to BTM Request frame received from
+ * the connected AP.
+ *
+ * @QCA_ROAM_REASON_BSS_LOAD: Roam triggered due to the channel utilization
+ * breaching out the configured threshold.
+ */
+enum qca_roam_reason {
+ QCA_ROAM_REASON_UNKNOWN,
+ QCA_ROAM_REASON_PER,
+ QCA_ROAM_REASON_BEACON_MISS,
+ QCA_ROAM_REASON_POOR_RSSI,
+ QCA_ROAM_REASON_BETTER_RSSI,
+ QCA_ROAM_REASON_CONGESTION,
+ QCA_ROAM_REASON_USER_TRIGGER,
+ QCA_ROAM_REASON_BTM,
+ QCA_ROAM_REASON_BSS_LOAD,
+};
+
enum qca_wlan_vendor_attr_roam_auth {
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_INVALID = 0,
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_BSSID,
@@ -896,6 +980,11 @@ enum qca_wlan_vendor_attr_roam_auth {
* doing subsequent ERP based connections in the same realm.
*/
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_FILS_ERP_NEXT_SEQ_NUM = 13,
+ /* A 16-bit unsigned value representing the reasons for the roaming.
+ * Defined by enum qca_roam_reason.
+ */
+ QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_REASON = 14,
+
/* keep last */
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_ROAM_AUTH_MAX =
@@ -994,6 +1083,7 @@ enum qca_wlan_vendor_acs_hw_mode {
* only OCE STA-CFON functionalities.
* @QCA_WLAN_VENDOR_FEATURE_SELF_MANAGED_REGULATORY: Device supports self
* managed regulatory.
+ * @QCA_WLAN_VENDOR_FEATURE_TWT: Device supports TWT (Target Wake Time).
* @NUM_QCA_WLAN_VENDOR_FEATURES: Number of assigned feature bits
*/
enum qca_wlan_vendor_features {
@@ -1005,6 +1095,7 @@ enum qca_wlan_vendor_features {
QCA_WLAN_VENDOR_FEATURE_OCE_AP = 5,
QCA_WLAN_VENDOR_FEATURE_OCE_STA_CFON = 6,
QCA_WLAN_VENDOR_FEATURE_SELF_MANAGED_REGULATORY = 7,
+ QCA_WLAN_VENDOR_FEATURE_TWT = 8,
NUM_QCA_WLAN_VENDOR_FEATURES /* keep last */
};
@@ -2438,6 +2529,18 @@ enum qca_wlan_vendor_attr_dmg_rf_sector_type {
};
/**
+ * enum qca_wlan_vendor_attr_fw_state - State of firmware
+ *
+ * @QCA_WLAN_VENDOR_ATTR_FW_STATE_ERROR: FW is in bad state
+ * @QCA_WLAN_VENDOR_ATTR_FW_STATE_ACTIVE: FW is active
+ */
+enum qca_wlan_vendor_attr_fw_state {
+ QCA_WLAN_VENDOR_ATTR_FW_STATE_ERROR,
+ QCA_WLAN_VENDOR_ATTR_FW_STATE_ACTIVE,
+ QCA_WLAN_VENDOR_ATTR_FW_STATE_MAX
+};
+
+/**
* BRP antenna limit mode
*
* @QCA_WLAN_VENDOR_ATTR_BRP_ANT_LIMIT_MODE_DISABLE: Disable BRP force
@@ -3181,6 +3284,8 @@ enum qca_wlan_vendor_attr_roaming_config_params {
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS = 18,
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_NUM_BSSID = 19,
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_BSSID = 20,
+ /* Flag attribute indicates this BSSID blacklist as a hint */
+ QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_SET_BSSID_PARAMS_HINT = 21,
/* keep last */
QCA_WLAN_VENDOR_ATTR_ROAMING_PARAM_AFTER_LAST,
@@ -4433,6 +4538,27 @@ enum qca_wlan_vendor_attr_spectral_cap {
* qca_wlan_vendor_spectral_scan_cap_hw_gen.
*/
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_HW_GEN = 5,
+ /* Spectral bin scaling formula ID. u16 attribute.
+ * It uses values defined in enum
+ * qca_wlan_vendor_spectral_scan_cap_formula_id.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_FORMULA_ID = 6,
+ /* Spectral bin scaling param - low level offset.
+ * s16 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_LOW_LEVEL_OFFSET = 7,
+ /* Spectral bin scaling param - high level offset.
+ * s16 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_HIGH_LEVEL_OFFSET = 8,
+ /* Spectral bin scaling param - RSSI threshold.
+ * s16 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_RSSI_THR = 9,
+ /* Spectral bin scaling param - default AGC max gain.
+ * u8 attribute.
+ */
+ QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_DEFAULT_AGC_MAX_GAIN = 10,
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_MAX =
@@ -4578,6 +4704,20 @@ enum qca_wlan_vendor_attr_flush_pending {
};
/**
+ * qca_wlan_vendor_spectral_scan_cap_formula_id: Attribute values for
+ * QCA_WLAN_VENDOR_ATTR_SPECTRAL_SCAN_CAP_FORMULA_ID in the vendor subcmd
+ * QCA_NL80211_VENDOR_SUBCMD_SPECTRAL_SCAN_GET_CAP_INFO. This represents the
+ * Spectral bin scaling formula ID.
+ * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_NO_SCALING: No scaling
+ * @QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_AGC_GAIN_RSSI_CORR_BASED: AGC gain
+ * and RSSI threshold based formula.
+ */
+enum qca_wlan_vendor_spectral_scan_cap_formula_id {
+ QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_NO_SCALING = 0,
+ QCA_WLAN_VENDOR_SPECTRAL_SCAN_CAP_AGC_GAIN_RSSI_CORR_BASED = 1,
+};
+
+/**
* enum qca_wlan_vendor_attr_rropavail_info - Specifies whether Representative
* RF Operating Parameter (RROP) information is available, and if so, at which
* point in the application-driver interaction sequence it can be retrieved by
@@ -4836,6 +4976,10 @@ enum qca_wlan_vendor_attr_offloaded_packets {
QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_DST_MAC_ADDR,
/* Unsigned 32-bit value, in milli seconds */
QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_PERIOD,
+ /* This optional unsigned 16-bit attribute is used for specifying
+ * ethernet protocol type. If not specified ethertype defaults to IPv4.
+ */
+ QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_ETHER_PROTO_TYPE,
/* keep last */
QCA_WLAN_VENDOR_ATTR_OFFLOADED_PACKETS_AFTER_LAST,
@@ -5457,6 +5601,54 @@ enum qca_wlan_he_om_ctrl_ch_bw {
QCA_WLAN_HE_OM_CTRL_BW_160M = 3,
};
+/**
+ * enum qca_wlan_vendor_attr_he_omi_tx: Represents attributes for
+ * HE operating mode control transmit request. These attributes are
+ * sent as part of QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OMI_TX and
+ * QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_HE_OMI_RX_NSS: Mandatory 8-bit unsigned value
+ * indicates the maximum number of spatial streams, NSS, that the STA
+ * supports in reception for PPDU bandwidths less than or equal to 80 MHz
+ * and is set to NSS - 1.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_HE_OMI_CH_BW: Mandatory 8-bit unsigned value
+ * indicates the operating channel width supported by the STA for both
+ * reception and transmission. Uses enum qca_wlan_he_om_ctrl_ch_bw values.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DISABLE: Mandatory 8-bit unsigned value
+ * indicates the all trigger based UL MU operations by the STA.
+ * 0 - UL MU operations are enabled by the STA.
+ * 1 - All triggered UL MU transmissions are suspended by the STA.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_HE_OMI_TX_NSTS: Mandatory 8-bit unsigned value
+ * indicates the maximum number of space-time streams, NSTS, that
+ * the STA supports in transmission and is set to NSTS - 1.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DATA_DISABLE: 8-bit unsigned value
+ * combined with the UL MU Disable subfield and the recipient's setting
+ * of the OM Control UL MU Data Disable RX Support subfield in the HE MAC
+ * capabilities to determine which HE TB PPDUs are possible by the
+ * STA to transmit.
+ * 0 - UL MU data operations are enabled by the STA.
+ * 1 - Determine which HE TB PPDU types are allowed by the STA if UL MU disable
+ * bit is not set, else UL MU Tx is suspended.
+ *
+ */
+enum qca_wlan_vendor_attr_he_omi_tx {
+ QCA_WLAN_VENDOR_ATTR_HE_OMI_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_HE_OMI_RX_NSS = 1,
+ QCA_WLAN_VENDOR_ATTR_HE_OMI_CH_BW = 2,
+ QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DISABLE = 3,
+ QCA_WLAN_VENDOR_ATTR_HE_OMI_TX_NSTS = 4,
+ QCA_WLAN_VENDOR_ATTR_HE_OMI_ULMU_DATA_DISABLE = 5,
+
+ /* keep last */
+ QCA_WLAN_VENDOR_ATTR_HE_OMI_AFTER_LAST,
+ QCA_WLAN_VENDOR_ATTR_HE_OMI_MAX =
+ QCA_WLAN_VENDOR_ATTR_HE_OMI_AFTER_LAST - 1,
+};
+
/* Attributes for data used by
* QCA_NL80211_VENDOR_SUBCMD_WIFI_TEST_CONFIGURATION
*/
@@ -5703,6 +5895,43 @@ enum qca_wlan_vendor_attr_wifi_test_config {
*/
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_ACTION_TX_TB_PPDU = 32,
+ /* Nested attribute to indicate HE operating mode control field
+ * transmission. It contains operating mode control field Nss,
+ * channel bandwidth, Tx Nsts and UL MU disable attributes.
+ * These nested attributes are used to send HE operating mode control
+ * with configured values.
+ * Uses the enum qca_wlan_vendor_attr_he_omi_tx attributes.
+ * This attribute is used to configure the testbed device.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_OMI_TX = 33,
+
+ /* 8-bit unsigned value to configure +HTC_HE support to indicate the
+ * support for the reception of a frame that carries an HE variant
+ * HT Control field.
+ * This attribute is used to configure the testbed device.
+ * 1-enable, 0-disable
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_HTC_HE_SUPP = 34,
+
+ /* 8-bit unsigned value to configure VHT support in 2.4G band.
+ * This attribute is used to configure the testbed device.
+ * 1-enable, 0-disable
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_ENABLE_2G_VHT = 35,
+
+ /* 8-bit unsigned value to configure HE testbed defaults.
+ * This attribute is used to configure the testbed device.
+ * 1-set the device HE capabilities to testbed defaults.
+ * 0-reset the device HE capabilities to supported config.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_SET_HE_TESTBED_DEFAULTS = 36,
+
+ /* 8-bit unsigned value to configure TWT request support.
+ * This attribute is used to configure the testbed device.
+ * 1-enable, 0-disable.
+ */
+ QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_HE_TWT_REQ_SUPPORT = 37,
+
/* keep last */
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_AFTER_LAST,
QCA_WLAN_VENDOR_ATTR_WIFI_TEST_CONFIG_MAX =
@@ -6185,13 +6414,35 @@ enum qca_coex_config_profiles {
QCA_WIFI_SAP_CLASS_3_MGMT = 7,
QCA_WIFI_SAP_DATA = 8,
QCA_WIFI_SAP_ALL = 9,
+ QCA_WIFI_CASE_MAX = 31,
/* 32 - 63 corresponds to BT */
QCA_BT_A2DP = 32,
QCA_BT_BLE = 33,
QCA_BT_SCO = 34,
+ QCA_BT_CASE_MAX = 63,
/* 64 - 95 corresponds to Zigbee */
QCA_ZB_LOW = 64,
- QCA_ZB_HIGH = 65
+ QCA_ZB_HIGH = 65,
+ QCA_ZB_CASE_MAX = 95,
+ /* 0xff is default value if the u8 profile value is not set. */
+ QCA_COEX_CONFIG_PROFILE_DEFAULT_VALUE = 255
+};
+
+/**
+ * enum qca_vendor_attr_coex_config_types - Coex configurations types.
+ * This enum defines the valid set of values of coex configuration types. These
+ * values may used by attribute
+ * %QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_CONFIG_TYPE.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_RESET: Reset all the
+ * weights to default values.
+ * @QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_START: Start to config
+ * weights with configurability value.
+ */
+enum qca_vendor_attr_coex_config_types {
+ QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_INVALID = 0,
+ QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_RESET = 1,
+ QCA_WLAN_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_COEX_START = 2,
};
/**
@@ -6223,6 +6474,80 @@ enum qca_vendor_attr_coex_config {
};
/**
+ * enum qca_vendor_attr_coex_config_three_way - Specifies vendor coex config
+ * attributes
+ * Attributes for data used by QCA_NL80211_VENDOR_SUBCMD_COEX_CONFIG
+ *
+ * QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_CONFIG_TYPE: u32 attribute.
+ * Indicate config type.
+ * The config types are 32-bit values from qca_vendor_attr_coex_config_types
+ *
+ * @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_1: u32 attribute.
+ * Indicate the Priority 1 profiles.
+ * The profiles are 8-bit values from enum qca_coex_config_profiles.
+ * In same priority level, maximum to 4 profiles can be set here.
+ * @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_2: u32 attribute.
+ * Indicate the Priority 2 profiles.
+ * The profiles are 8-bit values from enum qca_coex_config_profiles.
+ * In same priority level, maximum to 4 profiles can be set here.
+ * @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_3: u32 attribute.
+ * Indicate the Priority 3 profiles.
+ * The profiles are 8-bit values from enum qca_coex_config_profiles.
+ * In same priority level, maximum to 4 profiles can be set here.
+ * @QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_4: u32 attribute.
+ * Indicate the Priority 4 profiles.
+ * The profiles are 8-bit values from enum qca_coex_config_profiles.
+ * In same priority level, maximum to 4 profiles can be set here.
+ * NOTE:
+ * Limitations for QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_x priority
+ * arrangement:
+ * 1: In the same u32 attribute (priority x), the profiles enum values own
+ * same priority level.
+ * 2: 0xff is default value if the u8 profile value is not set.
+ * 3: max to 4 rules/profiles in same priority level.
+ * 4: max to 4 priority level (priority 1 - priority 4)
+ * 5: one priority level only supports one scenario from WLAN/BT/ZB,
+ * hybrid rules not support.
+ * 6: if WMI_COEX_CONFIG_THREE_WAY_COEX_RESET called, priority x will
+ * remain blank to reset all parameters.
+ * For example:
+ *
+ * If the attributes as follow:
+ * priority 1:
+ * ------------------------------------
+ * | 0xff | 0 | 1 | 2 |
+ * ------------------------------------
+ * priority 2:
+ * -------------------------------------
+ * | 0xff | 0xff | 0xff | 32 |
+ * -------------------------------------
+ * priority 3:
+ * -------------------------------------
+ * | 0xff | 0xff | 0xff | 65 |
+ * -------------------------------------
+ * then it means:
+ * 1: WIFI_STA_DISCOVERY, WIFI_STA_CLASS_3_MGMT and WIFI_STA_CONNECTION
+ * owns same priority level.
+ * 2: WIFI_STA_DISCOVERY, WIFI_STA_CLASS_3_MGMT and WIFI_STA_CONNECTION
+ * has priority over BT_A2DP and ZB_HIGH.
+ * 3: BT_A2DP has priority over ZB_HIGH.
+ */
+
+enum qca_vendor_attr_coex_config_three_way {
+ QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_INVALID = 0,
+ QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_CONFIG_TYPE = 1,
+ QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_1 = 2,
+ QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_2 = 3,
+ QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_3 = 4,
+ QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_PRIORITY_4 = 5,
+
+ /* Keep last */
+ QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_AFTER_LAST,
+ QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_MAX =
+ QCA_VENDOR_ATTR_COEX_CONFIG_THREE_WAY_AFTER_LAST - 1,
+};
+
+/**
* enum qca_wlan_vendor_attr_link_properties - Represent the link properties.
*
* @QCA_WLAN_VENDOR_ATTR_LINK_PROPERTIES_MAC_ADDR: MAC address of the peer
@@ -6244,4 +6569,144 @@ enum qca_wlan_vendor_attr_link_properties {
QCA_VENDOR_ATTR_LINK_PROPERTIES_AFTER_LAST - 1,
};
+/**
+ * enum qca_vendor_attr_peer_stats_cache_type - Represents peer stats cache type
+ * This enum defines the valid set of values of peer stats cache types. These
+ * values are used by attribute
+ * %QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PEER_TX_RATE_STATS: Represents peer TX rate statistics
+ * @QCA_WLAN_VENDOR_ATTR_PEER_RX_RATE_STATS: Represents peer RX rate statistics
+ * @QCA_WLAN_VENDOR_ATTR_PEER_TX_SOJOURN_STATS: Represents peer TX sojourn
+ * statistics
+ */
+enum qca_vendor_attr_peer_stats_cache_type {
+ QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE_INVALID = 0,
+
+ QCA_WLAN_VENDOR_ATTR_PEER_TX_RATE_STATS,
+ QCA_WLAN_VENDOR_ATTR_PEER_RX_RATE_STATS,
+ QCA_WLAN_VENDOR_ATTR_PEER_TX_SOJOURN_STATS,
+};
+
+/**
+ * enum qca_wlan_vendor_attr_peer_stats_cache_params - This enum defines
+ * attributes required for QCA_NL80211_VENDOR_SUBCMD_PEER_STATS_CACHE_FLUSH
+ * Information in these attributes is used to flush peer rate statistics from
+ * the driver to user application.
+ *
+ * @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE: Unsigned 32-bit attribute
+ * Indicate peer statistics cache type.
+ * The statistics types are 32-bit values from
+ * enum qca_vendor_attr_peer_stats_cache_type.
+ * @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_MAC: Unsigned 8-bit array
+ * of size 6 octets, representing the peer MAC address.
+ * @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_DATA: Opaque data attribute
+ * containing buffer of statistics to send to application layer entity.
+ * @QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_COOKIE: Unsigned 64-bit attribute
+ * representing a cookie for peer unique session.
+ */
+enum qca_wlan_vendor_attr_peer_stats_cache_params {
+ QCA_WLAN_VENDOR_ATTR_PEER_STATS_INVALID = 0,
+
+ QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_TYPE = 1,
+ QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_MAC = 2,
+ QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_DATA = 3,
+ QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_PEER_COOKIE = 4,
+
+ /* Keep last */
+ QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_LAST,
+ QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_MAX =
+ QCA_WLAN_VENDOR_ATTR_PEER_STATS_CACHE_LAST - 1
+};
+
+/**
+ * enum qca_mpta_helper_attr_zigbee_state - Current Zigbee state
+ * This enum defines all the possible states of Zigbee, which can be
+ * delivered in the QCA_MPTA_HELPER_VENDOR_ATTR_ZIGBEE_STATE attribute.
+ *
+ * @ZIGBEE_IDLE: Zigbee in idle state
+ * @ZIGBEE_FORM_NETWORK: Zigbee forming network
+ * @ZIGBEE_WAIT_JOIN: Zigbee waiting for joining network
+ * @ZIGBEE_JOIN: Zigbee joining network
+ * @ZIGBEE_NETWORK_UP: Zigbee network is up
+ * @ZIGBEE_HMI: Zigbee in HMI mode
+ */
+enum qca_mpta_helper_attr_zigbee_state {
+ ZIGBEE_IDLE = 0,
+ ZIGBEE_FORM_NETWORK = 1,
+ ZIGBEE_WAIT_JOIN = 2,
+ ZIGBEE_JOIN = 3,
+ ZIGBEE_NETWORK_UP = 4,
+ ZIGBEE_HMI = 5,
+};
+
+/*
+ * enum qca_mpta_helper_vendor_attr - Attributes used in vendor sub-command
+ * QCA_NL80211_VENDOR_SUBCMD_MPTA_HELPER_CONFIG.
+ */
+enum qca_mpta_helper_vendor_attr {
+ QCA_MPTA_HELPER_VENDOR_ATTR_INVALID = 0,
+ /* Optional attribute used to update Zigbee state.
+ * enum qca_mpta_helper_attr_zigbee_state.
+ * NLA_U32 attribute.
+ */
+ QCA_MPTA_HELPER_VENDOR_ATTR_ZIGBEE_STATE = 1,
+ /* Optional attribute used to configure WLAN duration for Shape-OCS
+ * during interrupt.
+ * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_INT_NON_WLAN_DURATION.
+ * Value range 0 ~ 300 (ms).
+ * NLA_U32 attribute.
+ */
+ QCA_MPTA_HELPER_VENDOR_ATTR_INT_WLAN_DURATION = 2,
+ /* Optional attribute used to configure non-WLAN duration for Shape-OCS
+ * during interrupt.
+ * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_INT_WLAN_DURATION.
+ * Value range 0 ~ 300 (ms).
+ * NLA_U32 attribute.
+ */
+ QCA_MPTA_HELPER_VENDOR_ATTR_INT_NON_WLAN_DURATION = 3,
+ /* Optional attribute used to configure WLAN duration for Shape-OCS
+ * monitor period.
+ * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_MON_NON_WLAN_DURATION.
+ * Value range 0 ~ 300 (ms)
+ * NLA_U32 attribute
+ */
+ QCA_MPTA_HELPER_VENDOR_ATTR_MON_WLAN_DURATION = 4,
+ /* Optional attribute used to configure non-WLAN duration for Shape-OCS
+ * monitor period.
+ * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_MON_WLAN_DURATION.
+ * Value range 0 ~ 300 (ms)
+ * NLA_U32 attribute
+ */
+ QCA_MPTA_HELPER_VENDOR_ATTR_MON_NON_WLAN_DURATION = 5,
+ /* Optional attribute used to configure OCS interrupt duration.
+ * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_MON_OCS_DURATION.
+ * Value range 1000 ~ 20000 (ms)
+ * NLA_U32 attribute
+ */
+ QCA_MPTA_HELPER_VENDOR_ATTR_INT_OCS_DURATION = 6,
+ /* Optional attribute used to configure OCS monitor duration.
+ * Set in pair with QCA_MPTA_HELPER_VENDOR_ATTR_INT_OCS_DURATION.
+ * Value range 1000 ~ 20000 (ms)
+ * NLA_U32 attribute
+ */
+ QCA_MPTA_HELPER_VENDOR_ATTR_MON_OCS_DURATION = 7,
+ /* Optional attribute used to notify WLAN firmware the current Zigbee
+ * channel.
+ * Value range 11 ~ 26
+ * NLA_U32 attribute
+ */
+ QCA_MPTA_HELPER_VENDOR_ATTR_ZIGBEE_CHAN = 8,
+ /* Optional attribute used to configure WLAN mute duration.
+ * Value range 0 ~ 400 (ms)
+ * NLA_U32 attribute
+ */
+ QCA_MPTA_HELPER_VENDOR_ATTR_WLAN_MUTE_DURATION = 9,
+
+ /* keep last */
+ QCA_MPTA_HELPER_VENDOR_ATTR_AFTER_LAST,
+ QCA_MPTA_HELPER_VENDOR_ATTR_MAX =
+ QCA_MPTA_HELPER_VENDOR_ATTR_AFTER_LAST - 1
+};
+
#endif /* QCA_VENDOR_H */
diff --git a/src/common/sae.c b/src/common/sae.c
index 981e788dc7518..5a50294a6dc8f 100644
--- a/src/common/sae.c
+++ b/src/common/sae.c
@@ -9,6 +9,7 @@
#include "includes.h"
#include "common.h"
+#include "utils/const_time.h"
#include "crypto/crypto.h"
#include "crypto/sha256.h"
#include "crypto/random.h"
@@ -17,10 +18,33 @@
#include "sae.h"
+static int sae_suitable_group(int group)
+{
+#ifdef CONFIG_TESTING_OPTIONS
+ /* Allow all groups for testing purposes in non-production builds. */
+ return 1;
+#else /* CONFIG_TESTING_OPTIONS */
+ /* Enforce REVmd rules on which SAE groups are suitable for production
+ * purposes: FFC groups whose prime is >= 3072 bits and ECC groups
+ * defined over a prime field whose prime is >= 256 bits. Furthermore,
+ * ECC groups defined over a characteristic 2 finite field and ECC
+ * groups with a co-factor greater than 1 are not suitable. */
+ return group == 19 || group == 20 || group == 21 ||
+ group == 28 || group == 29 || group == 30 ||
+ group == 15 || group == 16 || group == 17 || group == 18;
+#endif /* CONFIG_TESTING_OPTIONS */
+}
+
+
int sae_set_group(struct sae_data *sae, int group)
{
struct sae_temporary_data *tmp;
+ if (!sae_suitable_group(group)) {
+ wpa_printf(MSG_DEBUG, "SAE: Reject unsuitable group %d", group);
+ return -1;
+ }
+
sae_clear_data(sae);
tmp = sae->tmp = os_zalloc(sizeof(*tmp));
if (tmp == NULL)
@@ -208,12 +232,14 @@ get_rand_1_to_p_1(const u8 *prime, size_t prime_len, size_t prime_bits,
static int is_quadratic_residue_blind(struct sae_data *sae,
const u8 *prime, size_t bits,
- const struct crypto_bignum *qr,
- const struct crypto_bignum *qnr,
+ const u8 *qr, const u8 *qnr,
const struct crypto_bignum *y_sqr)
{
- struct crypto_bignum *r, *num;
+ struct crypto_bignum *r, *num, *qr_or_qnr = NULL;
int r_odd, check, res = -1;
+ u8 qr_or_qnr_bin[SAE_MAX_ECC_PRIME_LEN];
+ size_t prime_len = sae->tmp->prime_len;
+ unsigned int mask;
/*
* Use the blinding technique to mask y_sqr while determining
@@ -224,7 +250,7 @@ static int is_quadratic_residue_blind(struct sae_data *sae,
* r = a random number between 1 and p-1, inclusive
* num = (v * r * r) modulo p
*/
- r = get_rand_1_to_p_1(prime, sae->tmp->prime_len, bits, &r_odd);
+ r = get_rand_1_to_p_1(prime, prime_len, bits, &r_odd);
if (!r)
return -1;
@@ -234,50 +260,51 @@ static int is_quadratic_residue_blind(struct sae_data *sae,
crypto_bignum_mulmod(num, r, sae->tmp->prime, num) < 0)
goto fail;
- if (r_odd) {
- /*
- * num = (num * qr) module p
- * LGR(num, p) = 1 ==> quadratic residue
- */
- if (crypto_bignum_mulmod(num, qr, sae->tmp->prime, num) < 0)
- goto fail;
- check = 1;
- } else {
- /*
- * num = (num * qnr) module p
- * LGR(num, p) = -1 ==> quadratic residue
- */
- if (crypto_bignum_mulmod(num, qnr, sae->tmp->prime, num) < 0)
- goto fail;
- check = -1;
- }
+ /*
+ * Need to minimize differences in handling different cases, so try to
+ * avoid branches and timing differences.
+ *
+ * If r_odd:
+ * num = (num * qr) module p
+ * LGR(num, p) = 1 ==> quadratic residue
+ * else:
+ * num = (num * qnr) module p
+ * LGR(num, p) = -1 ==> quadratic residue
+ */
+ mask = const_time_is_zero(r_odd);
+ const_time_select_bin(mask, qnr, qr, prime_len, qr_or_qnr_bin);
+ qr_or_qnr = crypto_bignum_init_set(qr_or_qnr_bin, prime_len);
+ if (!qr_or_qnr ||
+ crypto_bignum_mulmod(num, qr_or_qnr, sae->tmp->prime, num) < 0)
+ goto fail;
+ /* r_odd is 0 or 1; branchless version of check = r_odd ? 1 : -1, */
+ check = const_time_select_int(mask, -1, 1);
res = crypto_bignum_legendre(num, sae->tmp->prime);
if (res == -2) {
res = -1;
goto fail;
}
- res = res == check;
+ /* branchless version of res = res == check
+ * (res is -1, 0, or 1; check is -1 or 1) */
+ mask = const_time_eq(res, check);
+ res = const_time_select_int(mask, 1, 0);
fail:
crypto_bignum_deinit(num, 1);
crypto_bignum_deinit(r, 1);
+ crypto_bignum_deinit(qr_or_qnr, 1);
return res;
}
static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
- const u8 *prime,
- const struct crypto_bignum *qr,
- const struct crypto_bignum *qnr,
- struct crypto_bignum **ret_x_cand)
+ const u8 *prime, const u8 *qr, const u8 *qnr,
+ u8 *pwd_value)
{
- u8 pwd_value[SAE_MAX_ECC_PRIME_LEN];
struct crypto_bignum *y_sqr, *x_cand;
int res;
size_t bits;
- *ret_x_cand = NULL;
-
wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
/* pwd-value = KDF-z(pwd-seed, "SAE Hunting and Pecking", p) */
@@ -286,7 +313,7 @@ static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
prime, sae->tmp->prime_len, pwd_value, bits) < 0)
return -1;
if (bits % 8)
- buf_shift_right(pwd_value, sizeof(pwd_value), 8 - bits % 8);
+ buf_shift_right(pwd_value, sae->tmp->prime_len, 8 - bits % 8);
wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value",
pwd_value, sae->tmp->prime_len);
@@ -297,31 +324,27 @@ static int sae_test_pwd_seed_ecc(struct sae_data *sae, const u8 *pwd_seed,
if (!x_cand)
return -1;
y_sqr = crypto_ec_point_compute_y_sqr(sae->tmp->ec, x_cand);
- if (!y_sqr) {
- crypto_bignum_deinit(x_cand, 1);
+ crypto_bignum_deinit(x_cand, 1);
+ if (!y_sqr)
return -1;
- }
res = is_quadratic_residue_blind(sae, prime, bits, qr, qnr, y_sqr);
crypto_bignum_deinit(y_sqr, 1);
- if (res <= 0) {
- crypto_bignum_deinit(x_cand, 1);
- return res;
- }
-
- *ret_x_cand = x_cand;
- return 1;
+ return res;
}
+/* Returns -1 on fatal failure, 0 if PWE cannot be derived from the provided
+ * pwd-seed, or 1 if a valid PWE was derived from pwd-seed. */
static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed,
struct crypto_bignum *pwe)
{
u8 pwd_value[SAE_MAX_PRIME_LEN];
size_t bits = sae->tmp->prime_len * 8;
u8 exp[1];
- struct crypto_bignum *a, *b;
- int res;
+ struct crypto_bignum *a, *b = NULL;
+ int res, is_val;
+ u8 pwd_value_valid;
wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-seed", pwd_seed, SHA256_MAC_LEN);
@@ -333,16 +356,29 @@ static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed,
wpa_hexdump_key(MSG_DEBUG, "SAE: pwd-value", pwd_value,
sae->tmp->prime_len);
- if (os_memcmp(pwd_value, sae->tmp->dh->prime, sae->tmp->prime_len) >= 0)
- {
- wpa_printf(MSG_DEBUG, "SAE: pwd-value >= p");
- return 0;
- }
+ /* Check whether pwd-value < p */
+ res = const_time_memcmp(pwd_value, sae->tmp->dh->prime,
+ sae->tmp->prime_len);
+ /* pwd-value >= p is invalid, so res is < 0 for the valid cases and
+ * the negative sign can be used to fill the mask for constant time
+ * selection */
+ pwd_value_valid = const_time_fill_msb(res);
+
+ /* If pwd-value >= p, force pwd-value to be < p and perform the
+ * calculations anyway to hide timing difference. The derived PWE will
+ * be ignored in that case. */
+ pwd_value[0] = const_time_select_u8(pwd_value_valid, pwd_value[0], 0);
/* PWE = pwd-value^((p-1)/r) modulo p */
+ res = -1;
a = crypto_bignum_init_set(pwd_value, sae->tmp->prime_len);
+ if (!a)
+ goto fail;
+ /* This is an optimization based on the used group that does not depend
+ * on the password in any way, so it is fine to use separate branches
+ * for this step without constant time operations. */
if (sae->tmp->dh->safe_prime) {
/*
* r = (p-1)/2 for the group used here, so this becomes:
@@ -356,33 +392,34 @@ static int sae_test_pwd_seed_ffc(struct sae_data *sae, const u8 *pwd_seed,
b = crypto_bignum_init_set(exp, sizeof(exp));
if (b == NULL ||
crypto_bignum_sub(sae->tmp->prime, b, b) < 0 ||
- crypto_bignum_div(b, sae->tmp->order, b) < 0) {
- crypto_bignum_deinit(b, 0);
- b = NULL;
- }
+ crypto_bignum_div(b, sae->tmp->order, b) < 0)
+ goto fail;
}
- if (a == NULL || b == NULL)
- res = -1;
- else
- res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe);
-
- crypto_bignum_deinit(a, 0);
- crypto_bignum_deinit(b, 0);
+ if (!b)
+ goto fail;
- if (res < 0) {
- wpa_printf(MSG_DEBUG, "SAE: Failed to calculate PWE");
- return -1;
- }
+ res = crypto_bignum_exptmod(a, b, sae->tmp->prime, pwe);
+ if (res < 0)
+ goto fail;
- /* if (PWE > 1) --> found */
- if (crypto_bignum_is_zero(pwe) || crypto_bignum_is_one(pwe)) {
- wpa_printf(MSG_DEBUG, "SAE: PWE <= 1");
- return 0;
- }
+ /* There were no fatal errors in calculations, so determine the return
+ * value using constant time operations. We get here for number of
+ * invalid cases which are cleared here after having performed all the
+ * computation. PWE is valid if pwd-value was less than prime and
+ * PWE > 1. Start with pwd-value check first and then use constant time
+ * operations to clear res to 0 if PWE is 0 or 1.
+ */
+ res = const_time_select_u8(pwd_value_valid, 1, 0);
+ is_val = crypto_bignum_is_zero(pwe);
+ res = const_time_select_u8(const_time_is_zero(is_val), res, 0);
+ is_val = crypto_bignum_is_one(pwe);
+ res = const_time_select_u8(const_time_is_zero(is_val), res, 0);
- wpa_printf(MSG_DEBUG, "SAE: PWE found");
- return 1;
+fail:
+ crypto_bignum_deinit(a, 1);
+ crypto_bignum_deinit(b, 1);
+ return res;
}
@@ -431,25 +468,32 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
const u8 *addr[3];
size_t len[3];
size_t num_elem;
- u8 dummy_password[32];
- size_t dummy_password_len;
+ u8 *dummy_password, *tmp_password;
int pwd_seed_odd = 0;
u8 prime[SAE_MAX_ECC_PRIME_LEN];
size_t prime_len;
- struct crypto_bignum *x = NULL, *qr, *qnr;
+ struct crypto_bignum *x = NULL, *qr = NULL, *qnr = NULL;
+ u8 x_bin[SAE_MAX_ECC_PRIME_LEN];
+ u8 x_cand_bin[SAE_MAX_ECC_PRIME_LEN];
+ u8 qr_bin[SAE_MAX_ECC_PRIME_LEN];
+ u8 qnr_bin[SAE_MAX_ECC_PRIME_LEN];
size_t bits;
- int res;
+ int res = -1;
+ u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
+ * mask */
- dummy_password_len = password_len;
- if (dummy_password_len > sizeof(dummy_password))
- dummy_password_len = sizeof(dummy_password);
- if (random_get_bytes(dummy_password, dummy_password_len) < 0)
- return -1;
+ os_memset(x_bin, 0, sizeof(x_bin));
+
+ dummy_password = os_malloc(password_len);
+ tmp_password = os_malloc(password_len);
+ if (!dummy_password || !tmp_password ||
+ random_get_bytes(dummy_password, password_len) < 0)
+ goto fail;
prime_len = sae->tmp->prime_len;
if (crypto_bignum_to_bin(sae->tmp->prime, prime, sizeof(prime),
prime_len) < 0)
- return -1;
+ goto fail;
bits = crypto_ec_prime_len_bits(sae->tmp->ec);
/*
@@ -457,8 +501,10 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
* (qnr) modulo p for blinding purposes during the loop.
*/
if (get_random_qr_qnr(prime, prime_len, sae->tmp->prime, bits,
- &qr, &qnr) < 0)
- return -1;
+ &qr, &qnr) < 0 ||
+ crypto_bignum_to_bin(qr, qr_bin, sizeof(qr_bin), prime_len) < 0 ||
+ crypto_bignum_to_bin(qnr, qnr_bin, sizeof(qnr_bin), prime_len) < 0)
+ goto fail;
wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
password, password_len);
@@ -474,7 +520,7 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
*/
sae_pwd_seed_key(addr1, addr2, addrs);
- addr[0] = password;
+ addr[0] = tmp_password;
len[0] = password_len;
num_elem = 1;
if (identifier) {
@@ -491,9 +537,8 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
* attacks that attempt to determine the number of iterations required
* in the loop.
*/
- for (counter = 1; counter <= k || !x; counter++) {
+ for (counter = 1; counter <= k || !found; counter++) {
u8 pwd_seed[SHA256_MAC_LEN];
- struct crypto_bignum *x_cand;
if (counter > 200) {
/* This should not happen in practice */
@@ -501,36 +546,45 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
break;
}
- wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter);
+ wpa_printf(MSG_DEBUG, "SAE: counter = %03u", counter);
+ const_time_select_bin(found, dummy_password, password,
+ password_len, tmp_password);
if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem,
addr, len, pwd_seed) < 0)
break;
res = sae_test_pwd_seed_ecc(sae, pwd_seed,
- prime, qr, qnr, &x_cand);
+ prime, qr_bin, qnr_bin, x_cand_bin);
+ const_time_select_bin(found, x_bin, x_cand_bin, prime_len,
+ x_bin);
+ pwd_seed_odd = const_time_select_u8(
+ found, pwd_seed_odd,
+ pwd_seed[SHA256_MAC_LEN - 1] & 0x01);
+ os_memset(pwd_seed, 0, sizeof(pwd_seed));
if (res < 0)
goto fail;
- if (res > 0 && !x) {
- wpa_printf(MSG_DEBUG,
- "SAE: Selected pwd-seed with counter %u",
- counter);
- x = x_cand;
- pwd_seed_odd = pwd_seed[SHA256_MAC_LEN - 1] & 0x01;
- os_memset(pwd_seed, 0, sizeof(pwd_seed));
+ /* Need to minimize differences in handling res == 0 and 1 here
+ * to avoid differences in timing and instruction cache access,
+ * so use const_time_select_*() to make local copies of the
+ * values based on whether this loop iteration was the one that
+ * found the pwd-seed/x. */
+
+ /* found is 0 or 0xff here and res is 0 or 1. Bitwise OR of them
+ * (with res converted to 0/0xff) handles this in constant time.
+ */
+ found |= res * 0xff;
+ wpa_printf(MSG_DEBUG, "SAE: pwd-seed result %d found=0x%02x",
+ res, found);
+ }
- /*
- * Use a dummy password for the following rounds, if
- * any.
- */
- addr[0] = dummy_password;
- len[0] = dummy_password_len;
- } else if (res > 0) {
- crypto_bignum_deinit(x_cand, 1);
- }
+ if (!found) {
+ wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE");
+ res = -1;
+ goto fail;
}
+ x = crypto_bignum_init_set(x_bin, prime_len);
if (!x) {
- wpa_printf(MSG_DEBUG, "SAE: Could not generate PWE");
res = -1;
goto fail;
}
@@ -543,7 +597,6 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
res = crypto_ec_point_solve_y_coord(sae->tmp->ec,
sae->tmp->pwe_ecc, x,
pwd_seed_odd);
- crypto_bignum_deinit(x, 1);
if (res < 0) {
/*
* This should not happen since we already checked that there
@@ -555,27 +608,48 @@ static int sae_derive_pwe_ecc(struct sae_data *sae, const u8 *addr1,
fail:
crypto_bignum_deinit(qr, 0);
crypto_bignum_deinit(qnr, 0);
+ os_free(dummy_password);
+ bin_clear_free(tmp_password, password_len);
+ crypto_bignum_deinit(x, 1);
+ os_memset(x_bin, 0, sizeof(x_bin));
+ os_memset(x_cand_bin, 0, sizeof(x_cand_bin));
return res;
}
+static int sae_modp_group_require_masking(int group)
+{
+ /* Groups for which pwd-value is likely to be >= p frequently */
+ return group == 22 || group == 23 || group == 24;
+}
+
+
static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1,
const u8 *addr2, const u8 *password,
size_t password_len, const char *identifier)
{
- u8 counter;
+ u8 counter, k, sel_counter = 0;
u8 addrs[2 * ETH_ALEN];
const u8 *addr[3];
size_t len[3];
size_t num_elem;
- int found = 0;
-
- if (sae->tmp->pwe_ffc == NULL) {
- sae->tmp->pwe_ffc = crypto_bignum_init();
- if (sae->tmp->pwe_ffc == NULL)
- return -1;
- }
+ u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
+ * mask */
+ u8 mask;
+ struct crypto_bignum *pwe;
+ size_t prime_len = sae->tmp->prime_len * 8;
+ u8 *pwe_buf;
+
+ crypto_bignum_deinit(sae->tmp->pwe_ffc, 1);
+ sae->tmp->pwe_ffc = NULL;
+
+ /* Allocate a buffer to maintain selected and candidate PWE for constant
+ * time selection. */
+ pwe_buf = os_zalloc(prime_len * 2);
+ pwe = crypto_bignum_init();
+ if (!pwe_buf || !pwe)
+ goto fail;
wpa_hexdump_ascii_key(MSG_DEBUG, "SAE: password",
password, password_len);
@@ -599,7 +673,9 @@ static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1,
len[num_elem] = sizeof(counter);
num_elem++;
- for (counter = 1; !found; counter++) {
+ k = sae_modp_group_require_masking(sae->group) ? 40 : 1;
+
+ for (counter = 1; counter <= k || !found; counter++) {
u8 pwd_seed[SHA256_MAC_LEN];
int res;
@@ -609,20 +685,37 @@ static int sae_derive_pwe_ffc(struct sae_data *sae, const u8 *addr1,
break;
}
- wpa_printf(MSG_DEBUG, "SAE: counter = %u", counter);
+ wpa_printf(MSG_DEBUG, "SAE: counter = %02u", counter);
if (hmac_sha256_vector(addrs, sizeof(addrs), num_elem,
addr, len, pwd_seed) < 0)
break;
- res = sae_test_pwd_seed_ffc(sae, pwd_seed, sae->tmp->pwe_ffc);
+ res = sae_test_pwd_seed_ffc(sae, pwd_seed, pwe);
+ /* res is -1 for fatal failure, 0 if a valid PWE was not found,
+ * or 1 if a valid PWE was found. */
if (res < 0)
break;
- if (res > 0) {
- wpa_printf(MSG_DEBUG, "SAE: Use this PWE");
- found = 1;
- }
+ /* Store the candidate PWE into the second half of pwe_buf and
+ * the selected PWE in the beginning of pwe_buf using constant
+ * time selection. */
+ if (crypto_bignum_to_bin(pwe, pwe_buf + prime_len, prime_len,
+ prime_len) < 0)
+ break;
+ const_time_select_bin(found, pwe_buf, pwe_buf + prime_len,
+ prime_len, pwe_buf);
+ sel_counter = const_time_select_u8(found, sel_counter, counter);
+ mask = const_time_eq_u8(res, 1);
+ found = const_time_select_u8(found, found, mask);
}
- return found ? 0 : -1;
+ if (!found)
+ goto fail;
+
+ wpa_printf(MSG_DEBUG, "SAE: Use PWE from counter = %02u", sel_counter);
+ sae->tmp->pwe_ffc = crypto_bignum_init_set(pwe_buf, prime_len);
+fail:
+ crypto_bignum_deinit(pwe, 1);
+ bin_clear_free(pwe_buf, prime_len * 2);
+ return sae->tmp->pwe_ffc ? 0 : -1;
}
@@ -1394,23 +1487,31 @@ int sae_check_confirm(struct sae_data *sae, const u8 *data, size_t len)
wpa_printf(MSG_DEBUG, "SAE: peer-send-confirm %u", WPA_GET_LE16(data));
- if (sae->tmp == NULL) {
+ if (!sae->tmp || !sae->peer_commit_scalar ||
+ !sae->tmp->own_commit_scalar) {
wpa_printf(MSG_DEBUG, "SAE: Temporary data not yet available");
return -1;
}
- if (sae->tmp->ec)
+ if (sae->tmp->ec) {
+ if (!sae->tmp->peer_commit_element_ecc ||
+ !sae->tmp->own_commit_element_ecc)
+ return -1;
sae_cn_confirm_ecc(sae, data, sae->peer_commit_scalar,
sae->tmp->peer_commit_element_ecc,
sae->tmp->own_commit_scalar,
sae->tmp->own_commit_element_ecc,
verifier);
- else
+ } else {
+ if (!sae->tmp->peer_commit_element_ffc ||
+ !sae->tmp->own_commit_element_ffc)
+ return -1;
sae_cn_confirm_ffc(sae, data, sae->peer_commit_scalar,
sae->tmp->peer_commit_element_ffc,
sae->tmp->own_commit_scalar,
sae->tmp->own_commit_element_ffc,
verifier);
+ }
if (os_memcmp_const(verifier, data + 2, SHA256_MAC_LEN) != 0) {
wpa_printf(MSG_DEBUG, "SAE: Confirm mismatch");
diff --git a/src/common/sae.h b/src/common/sae.h
index 3fbcb58d155a0..3eb6e323a68ff 100644
--- a/src/common/sae.h
+++ b/src/common/sae.h
@@ -40,6 +40,8 @@ struct sae_temporary_data {
struct crypto_bignum *order_buf;
struct wpabuf *anti_clogging_token;
char *pw_id;
+ int vlan_id;
+ u8 bssid[ETH_ALEN];
};
enum sae_state {
diff --git a/src/common/version.h b/src/common/version.h
index 2f47903d407c2..06fc5e4d25a34 100644
--- a/src/common/version.h
+++ b/src/common/version.h
@@ -9,6 +9,6 @@
#define GIT_VERSION_STR_POSTFIX ""
#endif /* GIT_VERSION_STR_POSTFIX */
-#define VERSION_STR "2.7" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX
+#define VERSION_STR "2.8" VERSION_STR_POSTFIX GIT_VERSION_STR_POSTFIX
#endif /* VERSION_H */
diff --git a/src/common/wpa_common.c b/src/common/wpa_common.c
index 2d989048ac943..ed2d1c2a02363 100644
--- a/src/common/wpa_common.c
+++ b/src/common/wpa_common.c
@@ -340,14 +340,21 @@ int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver,
* IEEE Std 802.11i-2004 - 8.5.1.2 Pairwise key hierarchy
* PTK = PRF-X(PMK, "Pairwise key expansion",
* Min(AA, SA) || Max(AA, SA) ||
- * Min(ANonce, SNonce) || Max(ANonce, SNonce))
+ * Min(ANonce, SNonce) || Max(ANonce, SNonce)
+ * [ || Z.x ])
+ *
+ * The optional Z.x component is used only with DPP and that part is not defined
+ * in IEEE 802.11.
*/
int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
const u8 *addr1, const u8 *addr2,
const u8 *nonce1, const u8 *nonce2,
- struct wpa_ptk *ptk, int akmp, int cipher)
+ struct wpa_ptk *ptk, int akmp, int cipher,
+ const u8 *z, size_t z_len)
{
- u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN];
+#define MAX_Z_LEN 66 /* with NIST P-521 */
+ u8 data[2 * ETH_ALEN + 2 * WPA_NONCE_LEN + MAX_Z_LEN];
+ size_t data_len = 2 * ETH_ALEN + 2 * WPA_NONCE_LEN;
u8 tmp[WPA_KCK_MAX_LEN + WPA_KEK_MAX_LEN + WPA_TK_MAX_LEN];
size_t ptk_len;
@@ -356,6 +363,9 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
return -1;
}
+ if (z_len > MAX_Z_LEN)
+ return -1;
+
if (os_memcmp(addr1, addr2, ETH_ALEN) < 0) {
os_memcpy(data, addr1, ETH_ALEN);
os_memcpy(data + ETH_ALEN, addr2, ETH_ALEN);
@@ -374,6 +384,11 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
WPA_NONCE_LEN);
}
+ if (z && z_len) {
+ os_memcpy(data + 2 * ETH_ALEN + 2 * WPA_NONCE_LEN, z, z_len);
+ data_len += z_len;
+ }
+
ptk->kck_len = wpa_kck_len(akmp, pmk_len);
ptk->kek_len = wpa_kek_len(akmp, pmk_len);
ptk->tk_len = wpa_cipher_key_len(cipher);
@@ -388,7 +403,7 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
if (wpa_key_mgmt_sha384(akmp)) {
#if defined(CONFIG_SUITEB192) || defined(CONFIG_FILS)
wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA384)");
- if (sha384_prf(pmk, pmk_len, label, data, sizeof(data),
+ if (sha384_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
#else /* CONFIG_SUITEB192 || CONFIG_FILS */
@@ -397,7 +412,7 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
} else if (wpa_key_mgmt_sha256(akmp) || akmp == WPA_KEY_MGMT_OWE) {
#if defined(CONFIG_IEEE80211W) || defined(CONFIG_SAE) || defined(CONFIG_FILS)
wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA256)");
- if (sha256_prf(pmk, pmk_len, label, data, sizeof(data),
+ if (sha256_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
#else /* CONFIG_IEEE80211W or CONFIG_SAE or CONFIG_FILS */
@@ -406,17 +421,17 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
#ifdef CONFIG_DPP
} else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 32) {
wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA256)");
- if (sha256_prf(pmk, pmk_len, label, data, sizeof(data),
+ if (sha256_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
} else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 48) {
wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA384)");
- if (sha384_prf(pmk, pmk_len, label, data, sizeof(data),
+ if (sha384_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
} else if (akmp == WPA_KEY_MGMT_DPP && pmk_len == 64) {
wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA512)");
- if (sha512_prf(pmk, pmk_len, label, data, sizeof(data),
+ if (sha512_prf(pmk, pmk_len, label, data, data_len,
tmp, ptk_len) < 0)
return -1;
} else if (akmp == WPA_KEY_MGMT_DPP) {
@@ -426,7 +441,7 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
#endif /* CONFIG_DPP */
} else {
wpa_printf(MSG_DEBUG, "WPA: PTK derivation using PRF(SHA1)");
- if (sha1_prf(pmk, pmk_len, label, data, sizeof(data), tmp,
+ if (sha1_prf(pmk, pmk_len, label, data, data_len, tmp,
ptk_len) < 0)
return -1;
}
@@ -435,6 +450,8 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
MAC2STR(addr1), MAC2STR(addr2));
wpa_hexdump(MSG_DEBUG, "WPA: Nonce1", nonce1, WPA_NONCE_LEN);
wpa_hexdump(MSG_DEBUG, "WPA: Nonce2", nonce2, WPA_NONCE_LEN);
+ if (z && z_len)
+ wpa_hexdump_key(MSG_DEBUG, "WPA: Z.x", z, z_len);
wpa_hexdump_key(MSG_DEBUG, "WPA: PMK", pmk, pmk_len);
wpa_hexdump_key(MSG_DEBUG, "WPA: PTK", tmp, ptk_len);
@@ -451,6 +468,7 @@ int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
ptk->kck2_len = 0;
os_memset(tmp, 0, sizeof(tmp));
+ os_memset(data, 0, data_len);
return 0;
}
@@ -880,6 +898,12 @@ static int wpa_ft_parse_ftie(const u8 *ie, size_t ie_len,
parse->igtk_len = len;
break;
#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_OCV
+ case FTIE_SUBELEM_OCI:
+ parse->oci = pos;
+ parse->oci_len = len;
+ break;
+#endif /* CONFIG_OCV */
default:
wpa_printf(MSG_DEBUG, "FT: Unknown subelem id %u", id);
break;
@@ -1203,6 +1227,7 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len,
left = rsn_ie_len - 6;
data->group_cipher = WPA_CIPHER_GTK_NOT_USED;
+ data->has_group = 1;
data->key_mgmt = WPA_KEY_MGMT_OSEN;
data->proto = WPA_PROTO_OSEN;
} else {
@@ -1224,6 +1249,7 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len,
if (left >= RSN_SELECTOR_LEN) {
data->group_cipher = rsn_selector_to_bitfield(pos);
+ data->has_group = 1;
if (!wpa_cipher_valid_group(data->group_cipher)) {
wpa_printf(MSG_DEBUG,
"%s: invalid group cipher 0x%x (%08x)",
@@ -1249,6 +1275,8 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len,
"count %u left %u", __func__, count, left);
return -4;
}
+ if (count)
+ data->has_pairwise = 1;
for (i = 0; i < count; i++) {
data->pairwise_cipher |= rsn_selector_to_bitfield(pos);
pos += RSN_SELECTOR_LEN;
@@ -1467,6 +1495,15 @@ int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
}
+int wpa_default_rsn_cipher(int freq)
+{
+ if (freq > 56160)
+ return WPA_CIPHER_GCMP; /* DMG */
+
+ return WPA_CIPHER_CCMP;
+}
+
+
#ifdef CONFIG_IEEE80211R
/**
@@ -1754,7 +1791,7 @@ int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, size_t pmk_r1_len,
os_memcpy(ptk->tk, tmp + offset, ptk->tk_len);
offset += ptk->tk_len;
os_memcpy(ptk->kck2, tmp + offset, ptk->kck2_len);
- offset = ptk->kck2_len;
+ offset += ptk->kck2_len;
os_memcpy(ptk->kek2, tmp + offset, ptk->kek2_len);
wpa_hexdump_key(MSG_DEBUG, "FT: KCK", ptk->kck, ptk->kck_len);
diff --git a/src/common/wpa_common.h b/src/common/wpa_common.h
index 62617444058b4..e83d6887a1cd5 100644
--- a/src/common/wpa_common.h
+++ b/src/common/wpa_common.h
@@ -110,6 +110,7 @@ WPA_CIPHER_BIP_CMAC_256)
#define RSN_KEY_DATA_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 10)
#define RSN_KEY_DATA_MULTIBAND_GTK RSN_SELECTOR(0x00, 0x0f, 0xac, 11)
#define RSN_KEY_DATA_MULTIBAND_KEYID RSN_SELECTOR(0x00, 0x0f, 0xac, 12)
+#define RSN_KEY_DATA_OCI RSN_SELECTOR(0x00, 0x0f, 0xac, 13)
#define WFA_KEY_DATA_IP_ADDR_REQ RSN_SELECTOR(0x50, 0x6f, 0x9a, 4)
#define WFA_KEY_DATA_IP_ADDR_ALLOC RSN_SELECTOR(0x50, 0x6f, 0x9a, 5)
@@ -148,7 +149,8 @@ WPA_CIPHER_BIP_CMAC_256)
#define WPA_CAPABILITY_SPP_A_MSDU_REQUIRED BIT(11)
#define WPA_CAPABILITY_PBAC BIT(12)
#define WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST BIT(13)
-/* B14-B15: Reserved */
+#define WPA_CAPABILITY_OCVC BIT(14)
+/* B15: Reserved */
/* IEEE 802.11r */
@@ -326,6 +328,7 @@ struct rsn_ftie_sha384 {
#define FTIE_SUBELEM_GTK 2
#define FTIE_SUBELEM_R0KH_ID 3
#define FTIE_SUBELEM_IGTK 4
+#define FTIE_SUBELEM_OCI 5
struct rsn_rdie {
u8 id;
@@ -344,7 +347,8 @@ int wpa_eapol_key_mic(const u8 *key, size_t key_len, int akmp, int ver,
int wpa_pmk_to_ptk(const u8 *pmk, size_t pmk_len, const char *label,
const u8 *addr1, const u8 *addr2,
const u8 *nonce1, const u8 *nonce2,
- struct wpa_ptk *ptk, int akmp, int cipher);
+ struct wpa_ptk *ptk, int akmp, int cipher,
+ const u8 *z, size_t z_len);
int fils_rmsk_to_pmk(int akmp, const u8 *rmsk, size_t rmsk_len,
const u8 *snonce, const u8 *anonce, const u8 *dh_ss,
size_t dh_ss_len, u8 *pmk, size_t *pmk_len);
@@ -389,7 +393,9 @@ int wpa_pmk_r1_to_ptk(const u8 *pmk_r1, size_t pmk_r1_len, const u8 *snonce,
struct wpa_ie_data {
int proto;
int pairwise_cipher;
+ int has_pairwise;
int group_cipher;
+ int has_group;
int key_mgmt;
int capabilities;
size_t num_pmkid;
@@ -402,6 +408,7 @@ int wpa_parse_wpa_ie_rsn(const u8 *rsn_ie, size_t rsn_ie_len,
struct wpa_ie_data *data);
int wpa_parse_wpa_ie_wpa(const u8 *wpa_ie, size_t wpa_ie_len,
struct wpa_ie_data *data);
+int wpa_default_rsn_cipher(int freq);
void rsn_pmkid(const u8 *pmk, size_t pmk_len, const u8 *aa, const u8 *spa,
u8 *pmkid, int akmp);
@@ -451,6 +458,10 @@ struct wpa_ft_ies {
size_t tie_len;
const u8 *igtk;
size_t igtk_len;
+#ifdef CONFIG_OCV
+ const u8 *oci;
+ size_t oci_len;
+#endif /* CONFIG_OCV */
const u8 *ric;
size_t ric_len;
int key_mgmt;
diff --git a/src/common/wpa_ctrl.c b/src/common/wpa_ctrl.c
index 623c2a768e43d..c9890a0e49050 100644
--- a/src/common/wpa_ctrl.c
+++ b/src/common/wpa_ctrl.c
@@ -11,6 +11,8 @@
#ifdef CONFIG_CTRL_IFACE
#ifdef CONFIG_CTRL_IFACE_UNIX
+#include <sys/stat.h>
+#include <fcntl.h>
#include <sys/un.h>
#include <unistd.h>
#include <fcntl.h>
@@ -133,6 +135,19 @@ try_again:
return NULL;
}
tries++;
+#ifdef ANDROID
+ /* Set client socket file permissions so that bind() creates the client
+ * socket with these permissions and there is no need to try to change
+ * them with chmod() after bind() which would have potential issues with
+ * race conditions. These permissions are needed to make sure the server
+ * side (wpa_supplicant or hostapd) can reply to the control interface
+ * messages.
+ *
+ * The lchown() calls below after bind() are also part of the needed
+ * operations to allow the response to go through. Those are using the
+ * no-deference-symlinks version to avoid races. */
+ fchmod(ctrl->s, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+#endif /* ANDROID */
if (bind(ctrl->s, (struct sockaddr *) &ctrl->local,
sizeof(ctrl->local)) < 0) {
if (errno == EADDRINUSE && tries < 2) {
@@ -151,10 +166,9 @@ try_again:
}
#ifdef ANDROID
- chmod(ctrl->local.sun_path, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
/* Set group even if we do not have privileges to change owner */
- chown(ctrl->local.sun_path, -1, AID_WIFI);
- chown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI);
+ lchown(ctrl->local.sun_path, -1, AID_WIFI);
+ lchown(ctrl->local.sun_path, AID_SYSTEM, AID_WIFI);
if (os_strncmp(ctrl_path, "@android:", 9) == 0) {
if (socket_local_client_connect(
@@ -540,7 +554,8 @@ retry_send:
res = recv(ctrl->s, reply, *reply_len, 0);
if (res < 0)
return res;
- if (res > 0 && reply[0] == '<') {
+ if ((res > 0 && reply[0] == '<') ||
+ (res > 6 && strncmp(reply, "IFNAME=", 7) == 0)) {
/* This is an unsolicited message from
* wpa_supplicant, not the reply to the
* request. Use msg_cb to report this to the
diff --git a/src/crypto/Makefile b/src/crypto/Makefile
index ee93e41fbbb17..ab108daac835e 100644
--- a/src/crypto/Makefile
+++ b/src/crypto/Makefile
@@ -62,7 +62,9 @@ LIB_OBJS += crypto_internal-modexp.o
LIB_OBJS += crypto_internal-rsa.o
LIB_OBJS += tls_internal.o
LIB_OBJS += fips_prf_internal.o
+ifndef TEST_FUZZ
LIB_OBJS += random.o
+endif
libcrypto.a: $(LIB_OBJS)
diff --git a/src/crypto/aes-internal-enc.c b/src/crypto/aes-internal-enc.c
index 9fdb4f35cb9b7..baeffcaf630c3 100644
--- a/src/crypto/aes-internal-enc.c
+++ b/src/crypto/aes-internal-enc.c
@@ -99,6 +99,10 @@ void * aes_encrypt_init(const u8 *key, size_t len)
{
u32 *rk;
int res;
+
+ if (TEST_FAIL())
+ return NULL;
+
rk = os_malloc(AES_PRIV_SIZE);
if (rk == NULL)
return NULL;
diff --git a/src/crypto/crypto.h b/src/crypto/crypto.h
index 507b7cab86fc7..12109ce83a9ad 100644
--- a/src/crypto/crypto.h
+++ b/src/crypto/crypto.h
@@ -420,6 +420,7 @@ int __must_check crypto_public_key_decrypt_pkcs1(
int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
u8 *pubkey);
int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+ const u8 *order, size_t order_len,
const u8 *privkey, size_t privkey_len,
const u8 *pubkey, size_t pubkey_len,
u8 *secret, size_t *len);
@@ -703,14 +704,6 @@ struct crypto_ec * crypto_ec_init(int group);
void crypto_ec_deinit(struct crypto_ec *e);
/**
- * crypto_ec_cofactor - Set the cofactor into the big number
- * @e: EC context from crypto_ec_init()
- * @cofactor: Cofactor of curve.
- * Returns: 0 on success, -1 on failure
- */
-int crypto_ec_cofactor(struct crypto_ec *e, struct crypto_bignum *cofactor);
-
-/**
* crypto_ec_prime_len - Get length of the prime in octets
* @e: EC context from crypto_ec_init()
* Returns: Length of the prime defining the group
diff --git a/src/crypto/crypto_gnutls.c b/src/crypto/crypto_gnutls.c
index 7a797b5c359d1..4ef11462b36e8 100644
--- a/src/crypto/crypto_gnutls.c
+++ b/src/crypto/crypto_gnutls.c
@@ -310,12 +310,51 @@ int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+ const u8 *order, size_t order_len,
const u8 *privkey, size_t privkey_len,
const u8 *pubkey, size_t pubkey_len,
u8 *secret, size_t *len)
{
- return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
- prime, prime_len, secret, len);
+ gcry_mpi_t pub = NULL;
+ int res = -1;
+
+ if (pubkey_len > prime_len ||
+ (pubkey_len == prime_len &&
+ os_memcmp(pubkey, prime, prime_len) >= 0))
+ return -1;
+
+ if (gcry_mpi_scan(&pub, GCRYMPI_FMT_USG, pubkey, pubkey_len, NULL) !=
+ GPG_ERR_NO_ERROR ||
+ gcry_mpi_cmp_ui(pub, 1) <= 0)
+ goto fail;
+
+ if (order) {
+ gcry_mpi_t p = NULL, q = NULL, tmp;
+ int failed;
+
+ /* verify: pubkey^q == 1 mod p */
+ tmp = gcry_mpi_new(prime_len * 8);
+ failed = !tmp ||
+ gcry_mpi_scan(&p, GCRYMPI_FMT_USG, prime, prime_len,
+ NULL) != GPG_ERR_NO_ERROR ||
+ gcry_mpi_scan(&q, GCRYMPI_FMT_USG, order, order_len,
+ NULL) != GPG_ERR_NO_ERROR;
+ if (!failed) {
+ gcry_mpi_powm(tmp, pub, q, p);
+ failed = gcry_mpi_cmp_ui(tmp, 1) != 0;
+ }
+ gcry_mpi_release(p);
+ gcry_mpi_release(q);
+ gcry_mpi_release(tmp);
+ if (failed)
+ goto fail;
+ }
+
+ res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
+ prime, prime_len, secret, len);
+fail:
+ gcry_mpi_release(pub);
+ return res;
}
diff --git a/src/crypto/crypto_internal-modexp.c b/src/crypto/crypto_internal-modexp.c
index 92581ac676d31..6819f1a6ab6a7 100644
--- a/src/crypto/crypto_internal-modexp.c
+++ b/src/crypto/crypto_internal-modexp.c
@@ -40,12 +40,49 @@ int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+ const u8 *order, size_t order_len,
const u8 *privkey, size_t privkey_len,
const u8 *pubkey, size_t pubkey_len,
u8 *secret, size_t *len)
{
- return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
- prime, prime_len, secret, len);
+ struct bignum *pub;
+ int res = -1;
+
+ if (pubkey_len > prime_len ||
+ (pubkey_len == prime_len &&
+ os_memcmp(pubkey, prime, prime_len) >= 0))
+ return -1;
+
+ pub = bignum_init();
+ if (!pub || bignum_set_unsigned_bin(pub, pubkey, pubkey_len) < 0 ||
+ bignum_cmp_d(pub, 1) <= 0)
+ goto fail;
+
+ if (order) {
+ struct bignum *p, *q, *tmp;
+ int failed;
+
+ /* verify: pubkey^q == 1 mod p */
+ p = bignum_init();
+ q = bignum_init();
+ tmp = bignum_init();
+ failed = !p || !q || !tmp ||
+ bignum_set_unsigned_bin(p, prime, prime_len) < 0 ||
+ bignum_set_unsigned_bin(q, order, order_len) < 0 ||
+ bignum_exptmod(pub, q, p, tmp) < 0 ||
+ bignum_cmp_d(tmp, 1) != 0;
+ bignum_deinit(p);
+ bignum_deinit(q);
+ bignum_deinit(tmp);
+ if (failed)
+ goto fail;
+ }
+
+ res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
+ prime, prime_len, secret, len);
+fail:
+ bignum_deinit(pub);
+ return res;
}
diff --git a/src/crypto/crypto_internal.c b/src/crypto/crypto_internal.c
index d391f48ab5b18..aad40af16e06e 100644
--- a/src/crypto/crypto_internal.c
+++ b/src/crypto/crypto_internal.c
@@ -310,6 +310,9 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
os_free(ctx);
+ if (TEST_FAIL())
+ return -1;
+
return 0;
}
diff --git a/src/crypto/crypto_libtomcrypt.c b/src/crypto/crypto_libtomcrypt.c
index 259f99500bcd3..ed30efa021d76 100644
--- a/src/crypto/crypto_libtomcrypt.c
+++ b/src/crypto/crypto_libtomcrypt.c
@@ -278,6 +278,9 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
os_free(ctx);
+ if (TEST_FAIL())
+ return -1;
+
return ret;
}
@@ -721,10 +724,12 @@ int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+ const u8 *order, size_t order_len,
const u8 *privkey, size_t privkey_len,
const u8 *pubkey, size_t pubkey_len,
u8 *secret, size_t *len)
{
+ /* TODO: check pubkey */
return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
prime, prime_len, secret, len);
}
diff --git a/src/crypto/crypto_linux.c b/src/crypto/crypto_linux.c
index 8099193bf0682..17244561b372f 100644
--- a/src/crypto/crypto_linux.c
+++ b/src/crypto/crypto_linux.c
@@ -386,6 +386,9 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
}
crypto_hash_deinit(ctx);
+
+ if (TEST_FAIL())
+ return -1;
return 0;
}
diff --git a/src/crypto/crypto_nettle.c b/src/crypto/crypto_nettle.c
index 4e31bc8011325..f85d36532ea19 100644
--- a/src/crypto/crypto_nettle.c
+++ b/src/crypto/crypto_nettle.c
@@ -331,12 +331,44 @@ int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+ const u8 *order, size_t order_len,
const u8 *privkey, size_t privkey_len,
const u8 *pubkey, size_t pubkey_len,
u8 *secret, size_t *len)
{
- return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
- prime, prime_len, secret, len);
+ mpz_t pub;
+ int res = -1;
+
+ if (pubkey_len > prime_len ||
+ (pubkey_len == prime_len &&
+ os_memcmp(pubkey, prime, prime_len) >= 0))
+ return -1;
+
+ mpz_init(pub);
+ mpz_import(pub, pubkey_len, 1, 1, 1, 0, pubkey);
+ if (mpz_cmp_d(pub, 1) <= 0)
+ goto fail;
+
+ if (order) {
+ mpz_t p, q, tmp;
+ int failed;
+
+ /* verify: pubkey^q == 1 mod p */
+ mpz_inits(p, q, tmp, NULL);
+ mpz_import(p, prime_len, 1, 1, 1, 0, prime);
+ mpz_import(q, order_len, 1, 1, 1, 0, order);
+ mpz_powm(tmp, pub, q, p);
+ failed = mpz_cmp_d(tmp, 1) != 0;
+ mpz_clears(p, q, tmp, NULL);
+ if (failed)
+ goto fail;
+ }
+
+ res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
+ prime, prime_len, secret, len);
+fail:
+ mpz_clear(pub);
+ return res;
}
diff --git a/src/crypto/crypto_openssl.c b/src/crypto/crypto_openssl.c
index f89053a89d67e..1b0c1ec96b36c 100644
--- a/src/crypto/crypto_openssl.c
+++ b/src/crypto/crypto_openssl.c
@@ -24,6 +24,7 @@
#endif /* CONFIG_ECC */
#include "common.h"
+#include "utils/const_time.h"
#include "wpabuf.h"
#include "dh_group5.h"
#include "sha1.h"
@@ -111,6 +112,31 @@ static BIGNUM * get_group5_prime(void)
#endif
}
+
+static BIGNUM * get_group5_order(void)
+{
+ static const unsigned char RFC3526_ORDER_1536[] = {
+ 0x7F,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xE4,0x87,0xED,0x51,
+ 0x10,0xB4,0x61,0x1A,0x62,0x63,0x31,0x45,0xC0,0x6E,0x0E,0x68,
+ 0x94,0x81,0x27,0x04,0x45,0x33,0xE6,0x3A,0x01,0x05,0xDF,0x53,
+ 0x1D,0x89,0xCD,0x91,0x28,0xA5,0x04,0x3C,0xC7,0x1A,0x02,0x6E,
+ 0xF7,0xCA,0x8C,0xD9,0xE6,0x9D,0x21,0x8D,0x98,0x15,0x85,0x36,
+ 0xF9,0x2F,0x8A,0x1B,0xA7,0xF0,0x9A,0xB6,0xB6,0xA8,0xE1,0x22,
+ 0xF2,0x42,0xDA,0xBB,0x31,0x2F,0x3F,0x63,0x7A,0x26,0x21,0x74,
+ 0xD3,0x1B,0xF6,0xB5,0x85,0xFF,0xAE,0x5B,0x7A,0x03,0x5B,0xF6,
+ 0xF7,0x1C,0x35,0xFD,0xAD,0x44,0xCF,0xD2,0xD7,0x4F,0x92,0x08,
+ 0xBE,0x25,0x8F,0xF3,0x24,0x94,0x33,0x28,0xF6,0x72,0x2D,0x9E,
+ 0xE1,0x00,0x3E,0x5C,0x50,0xB1,0xDF,0x82,0xCC,0x6D,0x24,0x1B,
+ 0x0E,0x2A,0xE9,0xCD,0x34,0x8B,0x1F,0xD4,0x7E,0x92,0x67,0xAF,
+ 0xC1,0xB2,0xAE,0x91,0xEE,0x51,0xD6,0xCB,0x0E,0x31,0x79,0xAB,
+ 0x10,0x42,0xA9,0x5D,0xCF,0x6A,0x94,0x83,0xB8,0x4B,0x4B,0x36,
+ 0xB3,0x86,0x1A,0xA7,0x25,0x5E,0x4C,0x02,0x78,0xBA,0x36,0x04,
+ 0x65,0x11,0xB9,0x93,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
+ };
+ return BN_bin2bn(RFC3526_ORDER_1536, sizeof(RFC3526_ORDER_1536), NULL);
+}
+
+
#ifdef OPENSSL_NO_SHA256
#define NO_SHA256_WRAPPER
#endif
@@ -518,12 +544,45 @@ int crypto_dh_init(u8 generator, const u8 *prime, size_t prime_len, u8 *privkey,
int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+ const u8 *order, size_t order_len,
const u8 *privkey, size_t privkey_len,
const u8 *pubkey, size_t pubkey_len,
u8 *secret, size_t *len)
{
- return crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
- prime, prime_len, secret, len);
+ BIGNUM *pub, *p;
+ int res = -1;
+
+ pub = BN_bin2bn(pubkey, pubkey_len, NULL);
+ p = BN_bin2bn(prime, prime_len, NULL);
+ if (!pub || !p || BN_is_zero(pub) || BN_is_one(pub) ||
+ BN_cmp(pub, p) >= 0)
+ goto fail;
+
+ if (order) {
+ BN_CTX *ctx;
+ BIGNUM *q, *tmp;
+ int failed;
+
+ /* verify: pubkey^q == 1 mod p */
+ q = BN_bin2bn(order, order_len, NULL);
+ ctx = BN_CTX_new();
+ tmp = BN_new();
+ failed = !q || !ctx || !tmp ||
+ !BN_mod_exp(tmp, pub, q, p, ctx) ||
+ !BN_is_one(tmp);
+ BN_clear(q);
+ BN_clear(tmp);
+ BN_CTX_free(ctx);
+ if (failed)
+ goto fail;
+ }
+
+ res = crypto_mod_exp(pubkey, pubkey_len, privkey, privkey_len,
+ prime, prime_len, secret, len);
+fail:
+ BN_clear(pub);
+ BN_clear(p);
+ return res;
}
@@ -549,7 +608,8 @@ int crypto_mod_exp(const u8 *base, size_t base_len,
bn_result == NULL)
goto error;
- if (BN_mod_exp(bn_result, bn_base, bn_exp, bn_modulus, ctx) != 1)
+ if (BN_mod_exp_mont_consttime(bn_result, bn_base, bn_exp, bn_modulus,
+ ctx, NULL) != 1)
goto error;
*result_len = BN_bn2bin(bn_result, result);
@@ -709,6 +769,10 @@ void * dh5_init(struct wpabuf **priv, struct wpabuf **publ)
if (dh->p == NULL)
goto err;
+ dh->q = get_group5_order();
+ if (!dh->q)
+ goto err;
+
if (DH_generate_key(dh) != 1)
goto err;
@@ -737,7 +801,7 @@ err:
DH *dh;
struct wpabuf *pubkey = NULL, *privkey = NULL;
size_t publen, privlen;
- BIGNUM *p = NULL, *g;
+ BIGNUM *p, *g, *q;
const BIGNUM *priv_key = NULL, *pub_key = NULL;
*priv = NULL;
@@ -750,10 +814,12 @@ err:
g = BN_new();
p = get_group5_prime();
- if (!g || BN_set_word(g, 2) != 1 || !p ||
- DH_set0_pqg(dh, p, NULL, g) != 1)
+ q = get_group5_order();
+ if (!g || BN_set_word(g, 2) != 1 || !p || !q ||
+ DH_set0_pqg(dh, p, q, g) != 1)
goto err;
p = NULL;
+ q = NULL;
g = NULL;
if (DH_generate_key(dh) != 1)
@@ -778,6 +844,7 @@ err:
err:
BN_free(p);
+ BN_free(q);
BN_free(g);
wpabuf_clear_free(pubkey);
wpabuf_clear_free(privkey);
@@ -987,6 +1054,9 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
HMAC_CTX_free(ctx->ctx);
bin_clear_free(ctx, sizeof(*ctx));
+ if (TEST_FAIL())
+ return -1;
+
if (res == 1) {
*len = mdlen;
return 0;
@@ -1250,6 +1320,8 @@ int crypto_bignum_to_bin(const struct crypto_bignum *a,
int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m)
{
+ if (TEST_FAIL())
+ return -1;
return BN_rand_range((BIGNUM *) r, (const BIGNUM *) m) == 1 ? 0 : -1;
}
@@ -1295,8 +1367,9 @@ int crypto_bignum_exptmod(const struct crypto_bignum *a,
bnctx = BN_CTX_new();
if (bnctx == NULL)
return -1;
- res = BN_mod_exp((BIGNUM *) d, (const BIGNUM *) a, (const BIGNUM *) b,
- (const BIGNUM *) c, bnctx);
+ res = BN_mod_exp_mont_consttime((BIGNUM *) d, (const BIGNUM *) a,
+ (const BIGNUM *) b, (const BIGNUM *) c,
+ bnctx, NULL);
BN_CTX_free(bnctx);
return res ? 0 : -1;
@@ -1315,6 +1388,11 @@ int crypto_bignum_inverse(const struct crypto_bignum *a,
bnctx = BN_CTX_new();
if (bnctx == NULL)
return -1;
+#ifdef OPENSSL_IS_BORINGSSL
+ /* TODO: use BN_mod_inverse_blinded() ? */
+#else /* OPENSSL_IS_BORINGSSL */
+ BN_set_flags((BIGNUM *) a, BN_FLG_CONSTTIME);
+#endif /* OPENSSL_IS_BORINGSSL */
res = BN_mod_inverse((BIGNUM *) c, (const BIGNUM *) a,
(const BIGNUM *) b, bnctx);
BN_CTX_free(bnctx);
@@ -1348,6 +1426,9 @@ int crypto_bignum_div(const struct crypto_bignum *a,
bnctx = BN_CTX_new();
if (bnctx == NULL)
return -1;
+#ifndef OPENSSL_IS_BORINGSSL
+ BN_set_flags((BIGNUM *) a, BN_FLG_CONSTTIME);
+#endif /* OPENSSL_IS_BORINGSSL */
res = BN_div((BIGNUM *) c, NULL, (const BIGNUM *) a,
(const BIGNUM *) b, bnctx);
BN_CTX_free(bnctx);
@@ -1425,6 +1506,7 @@ int crypto_bignum_legendre(const struct crypto_bignum *a,
BN_CTX *bnctx;
BIGNUM *exp = NULL, *tmp = NULL;
int res = -2;
+ unsigned int mask;
if (TEST_FAIL())
return -2;
@@ -1439,16 +1521,17 @@ int crypto_bignum_legendre(const struct crypto_bignum *a,
/* exp = (p-1) / 2 */
!BN_sub(exp, (const BIGNUM *) p, BN_value_one()) ||
!BN_rshift1(exp, exp) ||
- !BN_mod_exp(tmp, (const BIGNUM *) a, exp, (const BIGNUM *) p,
- bnctx))
+ !BN_mod_exp_mont_consttime(tmp, (const BIGNUM *) a, exp,
+ (const BIGNUM *) p, bnctx, NULL))
goto fail;
- if (BN_is_word(tmp, 1))
- res = 1;
- else if (BN_is_zero(tmp))
- res = 0;
- else
- res = -1;
+ /* Return 1 if tmp == 1, 0 if tmp == 0, or -1 otherwise. Need to use
+ * constant time selection to avoid branches here. */
+ res = -1;
+ mask = const_time_eq(BN_is_word(tmp, 1), 1);
+ res = const_time_select_int(mask, 1, res);
+ mask = const_time_eq(BN_is_zero(tmp), 1);
+ res = const_time_select_int(mask, 0, res);
fail:
BN_clear_free(tmp);
@@ -1553,13 +1636,6 @@ void crypto_ec_deinit(struct crypto_ec *e)
}
-int crypto_ec_cofactor(struct crypto_ec *e, struct crypto_bignum *cofactor)
-{
- return EC_GROUP_get_cofactor(e->group, (BIGNUM *) cofactor,
- e->bnctx) == 0 ? -1 : 0;
-}
-
-
struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e)
{
if (TEST_FAIL())
diff --git a/src/crypto/crypto_wolfssl.c b/src/crypto/crypto_wolfssl.c
index b5a1e3fa31bc1..976a008651b75 100644
--- a/src/crypto/crypto_wolfssl.c
+++ b/src/crypto/crypto_wolfssl.c
@@ -826,6 +826,7 @@ done:
int crypto_dh_derive_secret(u8 generator, const u8 *prime, size_t prime_len,
+ const u8 *order, size_t order_len,
const u8 *privkey, size_t privkey_len,
const u8 *pubkey, size_t pubkey_len,
u8 *secret, size_t *len)
@@ -952,6 +953,8 @@ int crypto_hash_finish(struct crypto_hash *ctx, u8 *mac, size_t *len)
ret = 0;
done:
bin_clear_free(ctx, sizeof(*ctx));
+ if (TEST_FAIL())
+ return -1;
return ret;
}
@@ -1082,6 +1085,8 @@ int crypto_bignum_rand(struct crypto_bignum *r, const struct crypto_bignum *m)
int ret = 0;
WC_RNG rng;
+ if (TEST_FAIL())
+ return -1;
if (wc_InitRng(&rng) != 0)
return -1;
if (mp_rand_prime((mp_int *) r,
@@ -1347,16 +1352,6 @@ void crypto_ec_deinit(struct crypto_ec* e)
}
-int crypto_ec_cofactor(struct crypto_ec *e, struct crypto_bignum *cofactor)
-{
- if (!e || !cofactor)
- return -1;
-
- mp_set((mp_int *) cofactor, e->key.dp->cofactor);
- return 0;
-}
-
-
struct crypto_ec_point * crypto_ec_point_init(struct crypto_ec *e)
{
if (TEST_FAIL())
diff --git a/src/crypto/dh_groups.c b/src/crypto/dh_groups.c
index a9b770ec1f160..5e421b24f5a56 100644
--- a/src/crypto/dh_groups.c
+++ b/src/crypto/dh_groups.c
@@ -1249,6 +1249,7 @@ struct wpabuf * dh_derive_shared(const struct wpabuf *peer_public,
if (shared == NULL)
return NULL;
if (crypto_dh_derive_secret(*dh->generator, dh->prime, dh->prime_len,
+ dh->order, dh->order_len,
wpabuf_head(own_private),
wpabuf_len(own_private),
wpabuf_head(peer_public),
diff --git a/src/crypto/md4-internal.c b/src/crypto/md4-internal.c
index d9c737a2970b6..cf408e84fae6a 100644
--- a/src/crypto/md4-internal.c
+++ b/src/crypto/md4-internal.c
@@ -85,7 +85,7 @@ MD4Transform(u32 state[4], const u8 block[MD4_BLOCK_LENGTH]);
(cp)[1] = (value) >> 8; \
(cp)[0] = (value); } while (0)
-static u8 PADDING[MD4_BLOCK_LENGTH] = {
+static const u8 PADDING[MD4_BLOCK_LENGTH] = {
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
diff --git a/src/crypto/random.c b/src/crypto/random.c
index c278d9cb9de94..1cabf3f4b9a42 100644
--- a/src/crypto/random.c
+++ b/src/crypto/random.c
@@ -25,6 +25,9 @@
#include "utils/includes.h"
#ifdef __linux__
#include <fcntl.h>
+#ifdef CONFIG_GETRANDOM
+#include <sys/random.h>
+#endif /* CONFIG_GETRANDOM */
#endif /* __linux__ */
#include "utils/common.h"
@@ -228,30 +231,52 @@ int random_pool_ready(void)
return 1; /* Already initialized - good to continue */
/*
- * Try to fetch some more data from the kernel high quality
- * /dev/random. There may not be enough data available at this point,
+ * Try to fetch some more data from the kernel high quality RNG.
+ * There may not be enough data available at this point,
* so use non-blocking read to avoid blocking the application
* completely.
*/
- fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
- if (fd < 0) {
- wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s",
- strerror(errno));
- return -1;
- }
- res = read(fd, dummy_key + dummy_key_avail,
- sizeof(dummy_key) - dummy_key_avail);
+#ifdef CONFIG_GETRANDOM
+ res = getrandom(dummy_key + dummy_key_avail,
+ sizeof(dummy_key) - dummy_key_avail, GRND_NONBLOCK);
if (res < 0) {
- wpa_printf(MSG_ERROR, "random: Cannot read from /dev/random: "
- "%s", strerror(errno));
- res = 0;
+ if (errno == ENOSYS) {
+ wpa_printf(MSG_DEBUG,
+ "random: getrandom() not supported, falling back to /dev/random");
+ } else {
+ wpa_printf(MSG_INFO,
+ "random: no data from getrandom(): %s",
+ strerror(errno));
+ res = 0;
+ }
+ }
+#else /* CONFIG_GETRANDOM */
+ res = -1;
+#endif /* CONFIG_GETRANDOM */
+ if (res < 0) {
+ fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
+ if (fd < 0) {
+ wpa_printf(MSG_ERROR,
+ "random: Cannot open /dev/random: %s",
+ strerror(errno));
+ return -1;
+ }
+
+ res = read(fd, dummy_key + dummy_key_avail,
+ sizeof(dummy_key) - dummy_key_avail);
+ if (res < 0) {
+ wpa_printf(MSG_ERROR,
+ "random: Cannot read from /dev/random: %s",
+ strerror(errno));
+ res = 0;
+ }
+ close(fd);
}
- wpa_printf(MSG_DEBUG, "random: Got %u/%u bytes from "
- "/dev/random", (unsigned) res,
+
+ wpa_printf(MSG_DEBUG, "random: Got %u/%u random bytes", (unsigned) res,
(unsigned) (sizeof(dummy_key) - dummy_key_avail));
dummy_key_avail += res;
- close(fd);
if (dummy_key_avail == sizeof(dummy_key)) {
if (own_pool_ready < MIN_READY_MARK)
@@ -261,7 +286,7 @@ int random_pool_ready(void)
}
wpa_printf(MSG_INFO, "random: Only %u/%u bytes of strong "
- "random data available from /dev/random",
+ "random data available",
(unsigned) dummy_key_avail, (unsigned) sizeof(dummy_key));
if (own_pool_ready >= MIN_READY_MARK ||
@@ -413,6 +438,19 @@ void random_init(const char *entropy_file)
if (random_fd >= 0)
return;
+#ifdef CONFIG_GETRANDOM
+ {
+ u8 dummy;
+
+ if (getrandom(&dummy, 0, GRND_NONBLOCK) == 0 ||
+ errno != ENOSYS) {
+ wpa_printf(MSG_DEBUG,
+ "random: getrandom() support available");
+ return;
+ }
+ }
+#endif /* CONFIG_GETRANDOM */
+
random_fd = open("/dev/random", O_RDONLY | O_NONBLOCK);
if (random_fd < 0) {
wpa_printf(MSG_ERROR, "random: Cannot open /dev/random: %s",
diff --git a/src/crypto/sha1-tlsprf.c b/src/crypto/sha1-tlsprf.c
index f9bc0ebf6e3d0..a11649a933eb3 100644
--- a/src/crypto/sha1-tlsprf.c
+++ b/src/crypto/sha1-tlsprf.c
@@ -40,9 +40,6 @@ int tls_prf_sha1_md5(const u8 *secret, size_t secret_len, const char *label,
const unsigned char *SHA1_addr[3];
size_t SHA1_len[3];
- if (secret_len & 1)
- return -1;
-
MD5_addr[0] = A_MD5;
MD5_len[0] = MD5_MAC_LEN;
MD5_addr[1] = (unsigned char *) label;
diff --git a/src/crypto/sha512-internal.c b/src/crypto/sha512-internal.c
index 76c4fe750b65f..c0263941c123c 100644
--- a/src/crypto/sha512-internal.c
+++ b/src/crypto/sha512-internal.c
@@ -109,9 +109,14 @@ static const u64 K[80] = {
/* compress 1024-bits */
static int sha512_compress(struct sha512_state *md, unsigned char *buf)
{
- u64 S[8], W[80], t0, t1;
+ u64 S[8], t0, t1;
+ u64 *W;
int i;
+ W = os_malloc(80 * sizeof(u64));
+ if (!W)
+ return -1;
+
/* copy state into S */
for (i = 0; i < 8; i++) {
S[i] = md->state[i];
@@ -146,6 +151,7 @@ static int sha512_compress(struct sha512_state *md, unsigned char *buf)
md->state[i] = md->state[i] + S[i];
}
+ os_free(W);
return 0;
}
diff --git a/src/crypto/sha512.c b/src/crypto/sha512.c
new file mode 100644
index 0000000000000..66311c3739208
--- /dev/null
+++ b/src/crypto/sha512.c
@@ -0,0 +1,104 @@
+/*
+ * SHA-512 hash implementation and interface functions
+ * Copyright (c) 2003-2018, Jouni Malinen <j@w1.fi>
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ */
+
+#include "includes.h"
+
+#include "common.h"
+#include "sha512.h"
+#include "crypto.h"
+
+
+/**
+ * hmac_sha512_vector - HMAC-SHA512 over data vector (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @num_elem: Number of elements in the data vector
+ * @addr: Pointers to the data areas
+ * @len: Lengths of the data blocks
+ * @mac: Buffer for the hash (64 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int hmac_sha512_vector(const u8 *key, size_t key_len, size_t num_elem,
+ const u8 *addr[], const size_t *len, u8 *mac)
+{
+ unsigned char k_pad[128]; /* padding - key XORd with ipad/opad */
+ unsigned char tk[64];
+ const u8 *_addr[6];
+ size_t _len[6], i;
+
+ if (num_elem > 5) {
+ /*
+ * Fixed limit on the number of fragments to avoid having to
+ * allocate memory (which could fail).
+ */
+ return -1;
+ }
+
+ /* if key is longer than 128 bytes reset it to key = SHA512(key) */
+ if (key_len > 128) {
+ if (sha512_vector(1, &key, &key_len, tk) < 0)
+ return -1;
+ key = tk;
+ key_len = 64;
+ }
+
+ /* the HMAC_SHA512 transform looks like:
+ *
+ * SHA512(K XOR opad, SHA512(K XOR ipad, text))
+ *
+ * where K is an n byte key
+ * ipad is the byte 0x36 repeated 128 times
+ * opad is the byte 0x5c repeated 128 times
+ * and text is the data being protected */
+
+ /* start out by storing key in ipad */
+ os_memset(k_pad, 0, sizeof(k_pad));
+ os_memcpy(k_pad, key, key_len);
+ /* XOR key with ipad values */
+ for (i = 0; i < 128; i++)
+ k_pad[i] ^= 0x36;
+
+ /* perform inner SHA512 */
+ _addr[0] = k_pad;
+ _len[0] = 128;
+ for (i = 0; i < num_elem; i++) {
+ _addr[i + 1] = addr[i];
+ _len[i + 1] = len[i];
+ }
+ if (sha512_vector(1 + num_elem, _addr, _len, mac) < 0)
+ return -1;
+
+ os_memset(k_pad, 0, sizeof(k_pad));
+ os_memcpy(k_pad, key, key_len);
+ /* XOR key with opad values */
+ for (i = 0; i < 128; i++)
+ k_pad[i] ^= 0x5c;
+
+ /* perform outer SHA512 */
+ _addr[0] = k_pad;
+ _len[0] = 128;
+ _addr[1] = mac;
+ _len[1] = SHA512_MAC_LEN;
+ return sha512_vector(2, _addr, _len, mac);
+}
+
+
+/**
+ * hmac_sha512 - HMAC-SHA512 over data buffer (RFC 2104)
+ * @key: Key for HMAC operations
+ * @key_len: Length of the key in bytes
+ * @data: Pointers to the data area
+ * @data_len: Length of the data area
+ * @mac: Buffer for the hash (64 bytes)
+ * Returns: 0 on success, -1 on failure
+ */
+int hmac_sha512(const u8 *key, size_t key_len, const u8 *data,
+ size_t data_len, u8 *mac)
+{
+ return hmac_sha512_vector(key, key_len, 1, &data, &data_len, mac);
+}
diff --git a/src/crypto/tls.h b/src/crypto/tls.h
index 481b34681d7ba..8bdb91ff2469c 100644
--- a/src/crypto/tls.h
+++ b/src/crypto/tls.h
@@ -42,6 +42,7 @@ enum tls_fail_reason {
TLS_FAIL_DOMAIN_SUFFIX_MISMATCH = 9,
TLS_FAIL_DOMAIN_MISMATCH = 10,
TLS_FAIL_INSUFFICIENT_KEY_LEN = 11,
+ TLS_FAIL_DN_MISMATCH = 12,
};
@@ -82,6 +83,7 @@ struct tls_config {
int cert_in_cb;
const char *openssl_ciphers;
unsigned int tls_session_lifetime;
+ unsigned int crl_reload_interval;
unsigned int tls_flags;
void (*event_cb)(void *ctx, enum tls_event ev,
@@ -103,6 +105,9 @@ struct tls_config {
#define TLS_CONN_SUITEB BIT(11)
#define TLS_CONN_SUITEB_NO_ECDH BIT(12)
#define TLS_CONN_DISABLE_TLSv1_3 BIT(13)
+#define TLS_CONN_ENABLE_TLSv1_0 BIT(14)
+#define TLS_CONN_ENABLE_TLSv1_1 BIT(15)
+#define TLS_CONN_ENABLE_TLSv1_2 BIT(16)
/**
* struct tls_connection_params - Parameters for TLS connection
@@ -115,12 +120,19 @@ struct tls_config {
* %NULL to allow all subjects
* @altsubject_match: String to match in the alternative subject of the peer
* certificate or %NULL to allow all alternative subjects
- * @suffix_match: String to suffix match in the dNSName or CN of the peer
- * certificate or %NULL to allow all domain names. This may allow subdomains an
- * wildcard certificates. Each domain name label must have a full match.
+ * @suffix_match: Semicolon deliminated string of values to suffix match against
+ * the dNSName or CN of the peer certificate or %NULL to allow all domain names.
+ * This may allow subdomains and wildcard certificates. Each domain name label
+ * must have a full case-insensitive match.
* @domain_match: String to match in the dNSName or CN of the peer
* certificate or %NULL to allow all domain names. This requires a full,
* case-insensitive match.
+ *
+ * More than one match string can be provided by using semicolons to
+ * separate the strings (e.g., example.org;example.com). When multiple
+ * strings are specified, a match with any one of the values is
+ * considered a sufficient match for the certificate, i.e., the
+ * conditions are ORed together.
* @client_cert: File or reference name for client X.509 certificate in PEM or
* DER format
* @client_cert_blob: client_cert as inlined data or %NULL if not used
@@ -144,12 +156,15 @@ struct tls_config {
* @cert_id: the certificate's id when using engine
* @ca_cert_id: the CA certificate's id when using engine
* @openssl_ciphers: OpenSSL cipher configuration
+ * @openssl_ecdh_curves: OpenSSL ECDH curve configuration. %NULL for auto if
+ * supported, empty string to disable, or a colon-separated curve list.
* @flags: Parameter options (TLS_CONN_*)
* @ocsp_stapling_response: DER encoded file with cached OCSP stapling response
* or %NULL if OCSP is not enabled
* @ocsp_stapling_response_multi: DER encoded file with cached OCSP stapling
* response list (OCSPResponseList for ocsp_multi in RFC 6961) or %NULL if
* ocsp_multi is not enabled
+ * @check_cert_subject: Client certificate subject name matching string
*
* TLS connection parameters to be configured with tls_connection_set_params()
* and tls_global_set_params().
@@ -187,10 +202,12 @@ struct tls_connection_params {
const char *cert_id;
const char *ca_cert_id;
const char *openssl_ciphers;
+ const char *openssl_ecdh_curves;
unsigned int flags;
const char *ocsp_stapling_response;
const char *ocsp_stapling_response_multi;
+ const char *check_cert_subject;
};
@@ -321,9 +338,11 @@ int __must_check tls_global_set_params(
* @tls_ctx: TLS context data from tls_init()
* @check_crl: 0 = do not verify CRLs, 1 = verify CRL for the user certificate,
* 2 = verify CRL for all certificates
+ * @strict: 0 = allow CRL time errors, 1 = do not allow CRL time errors
* Returns: 0 on success, -1 on failure
*/
-int __must_check tls_global_set_verify(void *tls_ctx, int check_crl);
+int __must_check tls_global_set_verify(void *tls_ctx, int check_crl,
+ int strict);
/**
* tls_connection_set_verify - Set certificate verification options
@@ -358,15 +377,21 @@ int __must_check tls_connection_get_random(void *tls_ctx,
* @tls_ctx: TLS context data from tls_init()
* @conn: Connection context data from tls_connection_init()
* @label: Label (e.g., description of the key) for PRF
+ * @context: Optional extra upper-layer context (max len 2^16)
+ * @context_len: The length of the context value
* @out: Buffer for output data from TLS-PRF
* @out_len: Length of the output buffer
* Returns: 0 on success, -1 on failure
*
- * Exports keying material using the mechanism described in RFC 5705.
+ * Exports keying material using the mechanism described in RFC 5705. If
+ * context is %NULL, context is not provided; otherwise, context is provided
+ * (including the case of empty context with context_len == 0).
*/
int __must_check tls_connection_export_key(void *tls_ctx,
struct tls_connection *conn,
const char *label,
+ const u8 *context,
+ size_t context_len,
u8 *out, size_t out_len);
/**
diff --git a/src/crypto/tls_gnutls.c b/src/crypto/tls_gnutls.c
index 36dafd2603f0c..daa01d9ed4f64 100644
--- a/src/crypto/tls_gnutls.c
+++ b/src/crypto/tls_gnutls.c
@@ -461,6 +461,12 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
}
}
+ if (params->openssl_ecdh_curves) {
+ wpa_printf(MSG_INFO,
+ "GnuTLS: openssl_ecdh_curves not supported");
+ return -1;
+ }
+
/* TODO: gnutls_certificate_set_verify_flags(xcred, flags);
* to force peer validation(?) */
@@ -733,6 +739,9 @@ int tls_global_set_params(void *tls_ctx,
struct tls_global *global = tls_ctx;
int ret;
+ if (params->check_cert_subject)
+ return -1; /* not yet supported */
+
/* Currently, global parameters are only set when running in server
* mode. */
global->server = 1;
@@ -842,7 +851,7 @@ fail:
}
-int tls_global_set_verify(void *ssl_ctx, int check_crl)
+int tls_global_set_verify(void *ssl_ctx, int check_crl, int strict)
{
/* TODO */
return 0;
@@ -889,14 +898,23 @@ int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn,
int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
- const char *label, u8 *out, size_t out_len)
+ const char *label, const u8 *context,
+ size_t context_len, u8 *out, size_t out_len)
{
if (conn == NULL || conn->session == NULL)
return -1;
+#if GNUTLS_VERSION_NUMBER >= 0x030404
+ return gnutls_prf_rfc5705(conn->session, os_strlen(label), label,
+ context_len, (const char *) context,
+ out_len, (char *) out);
+#else /* 3.4.4 */
+ if (context)
+ return -1;
return gnutls_prf(conn->session, os_strlen(label), label,
0 /* client_random first */, 0, NULL, out_len,
(char *) out);
+#endif /* 3.4.4 */
}
@@ -1068,6 +1086,52 @@ ocsp_error:
}
+static int tls_match_suffix_helper(gnutls_x509_crt_t cert, const char *match,
+ int full)
+{
+ int res = -1;
+
+#if GNUTLS_VERSION_NUMBER >= 0x030300
+ if (full)
+ res = gnutls_x509_crt_check_hostname2(
+ cert, match,
+ GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS);
+#endif /* >= 3.3.0 */
+ if (res == -1)
+ res = gnutls_x509_crt_check_hostname(cert, match);
+
+ wpa_printf(MSG_DEBUG, "TLS: Match domain against %s%s --> res=%d",
+ full ? "": "suffix ", match, res);
+ return res;
+}
+
+
+static int tls_match_suffix(gnutls_x509_crt_t cert, const char *match,
+ int full)
+{
+ char *values, *token, *context = NULL;
+ int ret = 0;
+
+ if (!os_strchr(match, ';'))
+ return tls_match_suffix_helper(cert, match, full);
+
+ values = os_strdup(match);
+ if (!values)
+ return 0;
+
+ /* Process each match alternative separately until a match is found */
+ while ((token = str_token(values, ";", &context))) {
+ if (tls_match_suffix_helper(cert, token, full)) {
+ ret = 1;
+ break;
+ }
+ }
+
+ os_free(values);
+ return ret;
+}
+
+
static int tls_connection_verify_peer(gnutls_session_t session)
{
struct tls_connection *conn;
@@ -1263,8 +1327,7 @@ static int tls_connection_verify_peer(gnutls_session_t session)
if (i == 0) {
if (conn->suffix_match &&
- !gnutls_x509_crt_check_hostname(
- cert, conn->suffix_match)) {
+ !tls_match_suffix(cert, conn->suffix_match, 0)) {
wpa_printf(MSG_WARNING,
"TLS: Domain suffix match '%s' not found",
conn->suffix_match);
@@ -1280,9 +1343,7 @@ static int tls_connection_verify_peer(gnutls_session_t session)
#if GNUTLS_VERSION_NUMBER >= 0x030300
if (conn->domain_match &&
- !gnutls_x509_crt_check_hostname2(
- cert, conn->domain_match,
- GNUTLS_VERIFY_DO_NOT_ALLOW_WILDCARDS)) {
+ !tls_match_suffix(cert, conn->domain_match, 1)) {
wpa_printf(MSG_WARNING,
"TLS: Domain match '%s' not found",
conn->domain_match);
diff --git a/src/crypto/tls_internal.c b/src/crypto/tls_internal.c
index d289c9442ceb3..8095b43bd21bc 100644
--- a/src/crypto/tls_internal.c
+++ b/src/crypto/tls_internal.c
@@ -1,6 +1,6 @@
/*
* TLS interface functions and an internal TLS implementation
- * Copyright (c) 2004-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -248,6 +248,12 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
return -1;
}
+ if (params->openssl_ecdh_curves) {
+ wpa_printf(MSG_INFO, "TLS: openssl_ecdh_curves not supported");
+ tlsv1_cred_free(cred);
+ return -1;
+ }
+
if (tlsv1_set_ca_cert(cred, params->ca_cert,
params->ca_cert_blob, params->ca_cert_blob_len,
params->ca_path)) {
@@ -303,6 +309,9 @@ int tls_global_set_params(void *tls_ctx,
struct tls_global *global = tls_ctx;
struct tlsv1_credentials *cred;
+ if (params->check_cert_subject)
+ return -1; /* not yet supported */
+
/* Currently, global parameters are only set when running in server
* mode. */
global->server = 1;
@@ -353,7 +362,7 @@ int tls_global_set_params(void *tls_ctx,
}
-int tls_global_set_verify(void *tls_ctx, int check_crl)
+int tls_global_set_verify(void *tls_ctx, int check_crl, int strict)
{
struct tls_global *global = tls_ctx;
global->check_crl = check_crl;
@@ -403,7 +412,8 @@ static int tls_get_keyblock_size(struct tls_connection *conn)
static int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
- const char *label, int server_random_first,
+ const char *label, const u8 *context,
+ size_t context_len, int server_random_first,
int skip_keyblock, u8 *out, size_t out_len)
{
int ret = -1, skip = 0;
@@ -422,15 +432,15 @@ static int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
#ifdef CONFIG_TLS_INTERNAL_CLIENT
if (conn->client) {
- ret = tlsv1_client_prf(conn->client, label,
- server_random_first,
+ ret = tlsv1_client_prf(conn->client, label, context,
+ context_len, server_random_first,
_out, skip + out_len);
}
#endif /* CONFIG_TLS_INTERNAL_CLIENT */
#ifdef CONFIG_TLS_INTERNAL_SERVER
if (conn->server) {
- ret = tlsv1_server_prf(conn->server, label,
- server_random_first,
+ ret = tlsv1_server_prf(conn->server, label, context,
+ context_len, server_random_first,
_out, skip + out_len);
}
#endif /* CONFIG_TLS_INTERNAL_SERVER */
@@ -443,17 +453,19 @@ static int tls_connection_prf(void *tls_ctx, struct tls_connection *conn,
int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
- const char *label, u8 *out, size_t out_len)
+ const char *label, const u8 *context,
+ size_t context_len, u8 *out, size_t out_len)
{
- return tls_connection_prf(tls_ctx, conn, label, 0, 0, out, out_len);
+ return tls_connection_prf(tls_ctx, conn, label, context, context_len,
+ 0, 0, out, out_len);
}
int tls_connection_get_eap_fast_key(void *tls_ctx, struct tls_connection *conn,
u8 *out, size_t out_len)
{
- return tls_connection_prf(tls_ctx, conn, "key expansion", 1, 1, out,
- out_len);
+ return tls_connection_prf(tls_ctx, conn, "key expansion", NULL, 0,
+ 1, 1, out, out_len);
}
@@ -720,12 +732,20 @@ int tls_connection_client_hello_ext(void *tls_ctx, struct tls_connection *conn,
int tls_connection_get_failed(void *tls_ctx, struct tls_connection *conn)
{
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (conn->server)
+ return tlsv1_server_get_failed(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
return 0;
}
int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn)
{
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (conn->server)
+ return tlsv1_server_get_read_alerts(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
return 0;
}
@@ -733,6 +753,10 @@ int tls_connection_get_read_alerts(void *tls_ctx, struct tls_connection *conn)
int tls_connection_get_write_alerts(void *tls_ctx,
struct tls_connection *conn)
{
+#ifdef CONFIG_TLS_INTERNAL_SERVER
+ if (conn->server)
+ return tlsv1_server_get_write_alerts(conn->server);
+#endif /* CONFIG_TLS_INTERNAL_SERVER */
return 0;
}
diff --git a/src/crypto/tls_none.c b/src/crypto/tls_none.c
index 5d0c6bda15479..6d6fb0cafd31c 100644
--- a/src/crypto/tls_none.c
+++ b/src/crypto/tls_none.c
@@ -72,7 +72,7 @@ int tls_global_set_params(void *tls_ctx,
}
-int tls_global_set_verify(void *tls_ctx, int check_crl)
+int tls_global_set_verify(void *tls_ctx, int check_crl, int strict)
{
return -1;
}
@@ -94,7 +94,8 @@ int tls_connection_get_random(void *tls_ctx, struct tls_connection *conn,
int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
- const char *label, u8 *out, size_t out_len)
+ const char *label, const u8 *context,
+ size_t context_len, u8 *out, size_t out_len)
{
return -1;
}
diff --git a/src/crypto/tls_openssl.c b/src/crypto/tls_openssl.c
index 0d5ebda699f02..b0c23ae6c9b1f 100644
--- a/src/crypto/tls_openssl.c
+++ b/src/crypto/tls_openssl.c
@@ -104,7 +104,9 @@ static size_t SSL_SESSION_get_master_key(const SSL_SESSION *session,
#endif
-#if OPENSSL_VERSION_NUMBER < 0x10100000L
+#if OPENSSL_VERSION_NUMBER < 0x10100000L || \
+ (defined(LIBRESSL_VERSION_NUMBER) && \
+ LIBRESSL_VERSION_NUMBER < 0x20700000L)
#ifdef CONFIG_SUITEB
static int RSA_bits(const RSA *r)
{
@@ -212,10 +214,17 @@ static struct tls_context *tls_global = NULL;
struct tls_data {
SSL_CTX *ssl;
unsigned int tls_session_lifetime;
+ int check_crl;
+ int check_crl_strict;
+ char *ca_cert;
+ unsigned int crl_reload_interval;
+ struct os_reltime crl_last_reload;
+ char *check_cert_subject;
};
struct tls_connection {
struct tls_context *context;
+ struct tls_data *data;
SSL_CTX *ssl_ctx;
SSL *ssl;
BIO *ssl_in, *ssl_out;
@@ -224,6 +233,7 @@ struct tls_connection {
EVP_PKEY *private_key; /* the private key if using engine */
#endif /* OPENSSL_NO_ENGINE */
char *subject_match, *altsubject_match, *suffix_match, *domain_match;
+ char *check_cert_subject;
int read_alerts, write_alerts, failed;
tls_session_ticket_cb session_ticket_cb;
@@ -301,6 +311,36 @@ static void tls_show_errors(int level, const char *func, const char *txt)
#endif /* CONFIG_NO_STDOUT_DEBUG */
+static X509_STORE * tls_crl_cert_reload(const char *ca_cert, int check_crl)
+{
+ int flags;
+ X509_STORE *store;
+
+ store = X509_STORE_new();
+ if (!store) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: %s - failed to allocate new certificate store",
+ __func__);
+ return NULL;
+ }
+
+ if (ca_cert && X509_STORE_load_locations(store, ca_cert, NULL) != 1) {
+ tls_show_errors(MSG_WARNING, __func__,
+ "Failed to load root certificates");
+ X509_STORE_free(store);
+ return NULL;
+ }
+
+ flags = check_crl ? X509_V_FLAG_CRL_CHECK : 0;
+ if (check_crl == 2)
+ flags |= X509_V_FLAG_CRL_CHECK_ALL;
+
+ X509_STORE_set_flags(store, flags);
+
+ return store;
+}
+
+
#ifdef CONFIG_NATIVE_WINDOWS
/* Windows CryptoAPI and access to certificate stores */
@@ -989,8 +1029,10 @@ void * tls_init(const struct tls_config *conf)
return NULL;
}
data->ssl = ssl;
- if (conf)
+ if (conf) {
data->tls_session_lifetime = conf->tls_session_lifetime;
+ data->crl_reload_interval = conf->crl_reload_interval;
+ }
SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv2);
SSL_CTX_set_options(ssl, SSL_OP_NO_SSLv3);
@@ -1072,6 +1114,7 @@ void tls_deinit(void *ssl_ctx)
os_free(context);
if (data->tls_session_lifetime > 0)
SSL_CTX_flush_sessions(ssl, 0);
+ os_free(data->ca_cert);
SSL_CTX_free(ssl);
tls_openssl_ref_count--;
@@ -1093,6 +1136,7 @@ void tls_deinit(void *ssl_ctx)
tls_global = NULL;
}
+ os_free(data->check_cert_subject);
os_free(data);
}
@@ -1305,8 +1349,16 @@ static const char * openssl_handshake_type(int content_type, const u8 *buf,
return "client hello";
case 2:
return "server hello";
+ case 3:
+ return "hello verify request";
case 4:
return "new session ticket";
+ case 5:
+ return "end of early data";
+ case 6:
+ return "hello retry request";
+ case 8:
+ return "encrypted extensions";
case 11:
return "certificate";
case 12:
@@ -1325,6 +1377,12 @@ static const char * openssl_handshake_type(int content_type, const u8 *buf,
return "certificate url";
case 22:
return "certificate status";
+ case 23:
+ return "supplemental data";
+ case 24:
+ return "key update";
+ case 254:
+ return "message hash";
default:
return "?";
}
@@ -1467,11 +1525,32 @@ struct tls_connection * tls_connection_init(void *ssl_ctx)
SSL_CTX *ssl = data->ssl;
struct tls_connection *conn;
long options;
+ X509_STORE *new_cert_store;
+ struct os_reltime now;
struct tls_context *context = SSL_CTX_get_app_data(ssl);
+ /* Replace X509 store if it is time to update CRL. */
+ if (data->crl_reload_interval > 0 && os_get_reltime(&now) == 0 &&
+ os_reltime_expired(&now, &data->crl_last_reload,
+ data->crl_reload_interval)) {
+ wpa_printf(MSG_INFO,
+ "OpenSSL: Flushing X509 store with ca_cert file");
+ new_cert_store = tls_crl_cert_reload(data->ca_cert,
+ data->check_crl);
+ if (!new_cert_store) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: Error replacing X509 store with ca_cert file");
+ } else {
+ /* Replace old store */
+ SSL_CTX_set_cert_store(ssl, new_cert_store);
+ data->crl_last_reload = now;
+ }
+ }
+
conn = os_zalloc(sizeof(*conn));
if (conn == NULL)
return NULL;
+ conn->data = data;
conn->ssl_ctx = ssl;
conn->ssl = SSL_new(ssl);
if (conn->ssl == NULL) {
@@ -1535,6 +1614,7 @@ void tls_connection_deinit(void *ssl_ctx, struct tls_connection *conn)
os_free(conn->altsubject_match);
os_free(conn->suffix_match);
os_free(conn->domain_match);
+ os_free(conn->check_cert_subject);
os_free(conn->session_ticket);
os_free(conn);
}
@@ -1655,9 +1735,9 @@ static int tls_match_altsubject(X509 *cert, const char *match)
#ifndef CONFIG_NATIVE_WINDOWS
static int domain_suffix_match(const u8 *val, size_t len, const char *match,
- int full)
+ size_t match_len, int full)
{
- size_t i, match_len;
+ size_t i;
/* Check for embedded nuls that could mess up suffix matching */
for (i = 0; i < len; i++) {
@@ -1667,7 +1747,6 @@ static int domain_suffix_match(const u8 *val, size_t len, const char *match,
}
}
- match_len = os_strlen(match);
if (match_len > len || (full && match_len != len))
return 0;
@@ -1687,12 +1766,223 @@ static int domain_suffix_match(const u8 *val, size_t len, const char *match,
#endif /* CONFIG_NATIVE_WINDOWS */
-static int tls_match_suffix(X509 *cert, const char *match, int full)
+struct tls_dn_field_order_cnt {
+ u8 cn;
+ u8 c;
+ u8 l;
+ u8 st;
+ u8 o;
+ u8 ou;
+ u8 email;
+};
+
+
+static int get_dn_field_index(const struct tls_dn_field_order_cnt *dn_cnt,
+ int nid)
+{
+ switch (nid) {
+ case NID_commonName:
+ return dn_cnt->cn;
+ case NID_countryName:
+ return dn_cnt->c;
+ case NID_localityName:
+ return dn_cnt->l;
+ case NID_stateOrProvinceName:
+ return dn_cnt->st;
+ case NID_organizationName:
+ return dn_cnt->o;
+ case NID_organizationalUnitName:
+ return dn_cnt->ou;
+ case NID_pkcs9_emailAddress:
+ return dn_cnt->email;
+ default:
+ wpa_printf(MSG_ERROR,
+ "TLS: Unknown NID '%d' in check_cert_subject",
+ nid);
+ return -1;
+ }
+}
+
+
+/**
+ * match_dn_field - Match configuration DN field against Certificate DN field
+ * @cert: Certificate
+ * @nid: NID of DN field
+ * @field: Field name
+ * @value DN field value which is passed from configuration
+ * e.g., if configuration have C=US and this argument will point to US.
+ * @dn_cnt: DN matching context
+ * Returns: 1 on success and 0 on failure
+ */
+static int match_dn_field(const X509 *cert, int nid, const char *field,
+ const char *value,
+ const struct tls_dn_field_order_cnt *dn_cnt)
+{
+ int i, ret = 0, len, config_dn_field_index, match_index = 0;
+ X509_NAME *name;
+
+ len = os_strlen(value);
+ name = X509_get_subject_name((X509 *) cert);
+
+ /* Assign incremented cnt for every field of DN to check DN field in
+ * right order */
+ config_dn_field_index = get_dn_field_index(dn_cnt, nid);
+ if (config_dn_field_index < 0)
+ return 0;
+
+ /* Fetch value based on NID */
+ for (i = -1; (i = X509_NAME_get_index_by_NID(name, nid, i)) > -1;) {
+ X509_NAME_ENTRY *e;
+ ASN1_STRING *cn;
+
+ e = X509_NAME_get_entry(name, i);
+ if (!e)
+ continue;
+
+ cn = X509_NAME_ENTRY_get_data(e);
+ if (!cn)
+ continue;
+
+ match_index++;
+
+ /* check for more than one DN field with same name */
+ if (match_index != config_dn_field_index)
+ continue;
+
+ /* Check wildcard at the right end side */
+ /* E.g., if OU=develop* mentioned in configuration, allow 'OU'
+ * of the subject in the client certificate to start with
+ * 'develop' */
+ if (len > 0 && value[len - 1] == '*') {
+ /* Compare actual certificate DN field value with
+ * configuration DN field value up to the specified
+ * length. */
+ ret = ASN1_STRING_length(cn) >= len - 1 &&
+ os_memcmp(ASN1_STRING_get0_data(cn), value,
+ len - 1) == 0;
+ } else {
+ /* Compare actual certificate DN field value with
+ * configuration DN field value */
+ ret = ASN1_STRING_length(cn) == len &&
+ os_memcmp(ASN1_STRING_get0_data(cn), value,
+ len) == 0;
+ }
+ if (!ret) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: Failed to match %s '%s' with certificate DN field value '%s'",
+ field, value, ASN1_STRING_get0_data(cn));
+ }
+ break;
+ }
+
+ return ret;
+}
+
+
+/**
+ * get_value_from_field - Get value from DN field
+ * @cert: Certificate
+ * @field_str: DN field string which is passed from configuration file (e.g.,
+ * C=US)
+ * @dn_cnt: DN matching context
+ * Returns: 1 on success and 0 on failure
+ */
+static int get_value_from_field(const X509 *cert, char *field_str,
+ struct tls_dn_field_order_cnt *dn_cnt)
+{
+ int nid;
+ char *context = NULL, *name, *value;
+
+ if (os_strcmp(field_str, "*") == 0)
+ return 1; /* wildcard matches everything */
+
+ name = str_token(field_str, "=", &context);
+ if (!name)
+ return 0;
+
+ /* Compare all configured DN fields and assign nid based on that to
+ * fetch correct value from certificate subject */
+ if (os_strcmp(name, "CN") == 0) {
+ nid = NID_commonName;
+ dn_cnt->cn++;
+ } else if(os_strcmp(name, "C") == 0) {
+ nid = NID_countryName;
+ dn_cnt->c++;
+ } else if (os_strcmp(name, "L") == 0) {
+ nid = NID_localityName;
+ dn_cnt->l++;
+ } else if (os_strcmp(name, "ST") == 0) {
+ nid = NID_stateOrProvinceName;
+ dn_cnt->st++;
+ } else if (os_strcmp(name, "O") == 0) {
+ nid = NID_organizationName;
+ dn_cnt->o++;
+ } else if (os_strcmp(name, "OU") == 0) {
+ nid = NID_organizationalUnitName;
+ dn_cnt->ou++;
+ } else if (os_strcmp(name, "emailAddress") == 0) {
+ nid = NID_pkcs9_emailAddress;
+ dn_cnt->email++;
+ } else {
+ wpa_printf(MSG_ERROR,
+ "TLS: Unknown field '%s' in check_cert_subject", name);
+ return 0;
+ }
+
+ value = str_token(field_str, "=", &context);
+ if (!value) {
+ wpa_printf(MSG_ERROR,
+ "TLS: Distinguished Name field '%s' value is not defined in check_cert_subject",
+ name);
+ return 0;
+ }
+
+ return match_dn_field(cert, nid, name, value, dn_cnt);
+}
+
+
+/**
+ * tls_match_dn_field - Match subject DN field with check_cert_subject
+ * @cert: Certificate
+ * @match: check_cert_subject string
+ * Returns: Return 1 on success and 0 on failure
+*/
+static int tls_match_dn_field(X509 *cert, const char *match)
+{
+ const char *token, *last = NULL;
+ char field[256];
+ struct tls_dn_field_order_cnt dn_cnt;
+
+ os_memset(&dn_cnt, 0, sizeof(dn_cnt));
+
+ /* Maximum length of each DN field is 255 characters */
+
+ /* Process each '/' delimited field */
+ while ((token = cstr_token(match, "/", &last))) {
+ if (last - token >= (int) sizeof(field)) {
+ wpa_printf(MSG_ERROR,
+ "OpenSSL: Too long DN matching field value in '%s'",
+ match);
+ return 0;
+ }
+ os_memcpy(field, token, last - token);
+ field[last - token] = '\0';
+
+ if (!get_value_from_field(cert, field, &dn_cnt)) {
+ wpa_printf(MSG_DEBUG, "OpenSSL: No match for DN '%s'",
+ field);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+
+#ifndef CONFIG_NATIVE_WINDOWS
+static int tls_match_suffix_helper(X509 *cert, const char *match,
+ size_t match_len, int full)
{
-#ifdef CONFIG_NATIVE_WINDOWS
- /* wincrypt.h has conflicting X509_NAME definition */
- return -1;
-#else /* CONFIG_NATIVE_WINDOWS */
GENERAL_NAME *gen;
void *ext;
int i;
@@ -1714,8 +2004,8 @@ static int tls_match_suffix(X509 *cert, const char *match, int full)
gen->d.dNSName->data,
gen->d.dNSName->length);
if (domain_suffix_match(gen->d.dNSName->data,
- gen->d.dNSName->length, match, full) ==
- 1) {
+ gen->d.dNSName->length,
+ match, match_len, full) == 1) {
wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found",
full ? "Match" : "Suffix match");
sk_GENERAL_NAME_pop_free(ext, GENERAL_NAME_free);
@@ -1746,8 +2036,8 @@ static int tls_match_suffix(X509 *cert, const char *match, int full)
continue;
wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName",
cn->data, cn->length);
- if (domain_suffix_match(cn->data, cn->length, match, full) == 1)
- {
+ if (domain_suffix_match(cn->data, cn->length,
+ match, match_len, full) == 1) {
wpa_printf(MSG_DEBUG, "TLS: %s in commonName found",
full ? "Match" : "Suffix match");
return 1;
@@ -1757,6 +2047,25 @@ static int tls_match_suffix(X509 *cert, const char *match, int full)
wpa_printf(MSG_DEBUG, "TLS: No CommonName %smatch found",
full ? "": "suffix ");
return 0;
+}
+#endif /* CONFIG_NATIVE_WINDOWS */
+
+
+static int tls_match_suffix(X509 *cert, const char *match, int full)
+{
+#ifdef CONFIG_NATIVE_WINDOWS
+ /* wincrypt.h has conflicting X509_NAME definition */
+ return -1;
+#else /* CONFIG_NATIVE_WINDOWS */
+ const char *token, *last = NULL;
+
+ /* Process each match alternative separately until a match is found */
+ while ((token = cstr_token(match, ";", &last))) {
+ if (tls_match_suffix_helper(cert, token, last - token, full))
+ return 1;
+ }
+
+ return 0;
#endif /* CONFIG_NATIVE_WINDOWS */
}
@@ -1951,6 +2260,7 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
struct tls_connection *conn;
struct tls_context *context;
char *match, *altmatch, *suffix_match, *domain_match;
+ const char *check_cert_subject;
const char *err_str;
err_cert = X509_STORE_CTX_get_current_cert(x509_ctx);
@@ -1991,6 +2301,13 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
"time mismatch");
preverify_ok = 1;
}
+ if (!preverify_ok && !conn->data->check_crl_strict &&
+ (err == X509_V_ERR_CRL_HAS_EXPIRED ||
+ err == X509_V_ERR_CRL_NOT_YET_VALID)) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Ignore certificate validity CRL time mismatch");
+ preverify_ok = 1;
+ }
err_str = X509_verify_cert_error_string(err);
@@ -2044,6 +2361,18 @@ static int tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
"err=%d (%s) ca_cert_verify=%d depth=%d buf='%s'",
preverify_ok, err, err_str,
conn->ca_cert_verify, depth, buf);
+ check_cert_subject = conn->check_cert_subject;
+ if (!check_cert_subject)
+ check_cert_subject = conn->data->check_cert_subject;
+ if (check_cert_subject) {
+ if (depth == 0 &&
+ !tls_match_dn_field(err_cert, check_cert_subject)) {
+ preverify_ok = 0;
+ openssl_tls_fail_event(conn, err_cert, err, depth, buf,
+ "Distinguished Name",
+ TLS_FAIL_DN_MISMATCH);
+ }
+ }
if (depth == 0 && match && os_strstr(buf, match) == NULL) {
wpa_printf(MSG_WARNING, "TLS: Subject '%s' did not "
"match with '%s'", buf, match);
@@ -2381,13 +2710,16 @@ static int tls_global_ca_cert(struct tls_data *data, const char *ca_cert)
SSL_CTX_set_client_CA_list(ssl_ctx,
SSL_load_client_CA_file(ca_cert));
#endif /* OPENSSL_NO_STDIO */
+
+ os_free(data->ca_cert);
+ data->ca_cert = os_strdup(ca_cert);
}
return 0;
}
-int tls_global_set_verify(void *ssl_ctx, int check_crl)
+int tls_global_set_verify(void *ssl_ctx, int check_crl, int strict)
{
int flags;
@@ -2404,6 +2736,10 @@ int tls_global_set_verify(void *ssl_ctx, int check_crl)
if (check_crl == 2)
flags |= X509_V_FLAG_CRL_CHECK_ALL;
X509_STORE_set_flags(cs, flags);
+
+ data->check_crl = check_crl;
+ data->check_crl_strict = strict;
+ os_get_reltime(&data->crl_last_reload);
}
return 0;
}
@@ -2413,7 +2749,8 @@ static int tls_connection_set_subject_match(struct tls_connection *conn,
const char *subject_match,
const char *altsubject_match,
const char *suffix_match,
- const char *domain_match)
+ const char *domain_match,
+ const char *check_cert_subject)
{
os_free(conn->subject_match);
conn->subject_match = NULL;
@@ -2447,6 +2784,14 @@ static int tls_connection_set_subject_match(struct tls_connection *conn,
return -1;
}
+ os_free(conn->check_cert_subject);
+ conn->check_cert_subject = NULL;
+ if (check_cert_subject) {
+ conn->check_cert_subject = os_strdup(check_cert_subject);
+ if (!conn->check_cert_subject)
+ return -1;
+ }
+
return 0;
}
@@ -2519,6 +2864,38 @@ static int tls_set_conn_flags(struct tls_connection *conn, unsigned int flags,
else
SSL_clear_options(ssl, SSL_OP_NO_TLSv1_3);
#endif /* SSL_OP_NO_TLSv1_3 */
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
+ if (flags & (TLS_CONN_ENABLE_TLSv1_0 |
+ TLS_CONN_ENABLE_TLSv1_1 |
+ TLS_CONN_ENABLE_TLSv1_2)) {
+ int version = 0;
+
+ /* Explicit request to enable TLS versions even if needing to
+ * override systemwide policies. */
+ if (flags & TLS_CONN_ENABLE_TLSv1_0) {
+ version = TLS1_VERSION;
+ } else if (flags & TLS_CONN_ENABLE_TLSv1_1) {
+ if (!(flags & TLS_CONN_DISABLE_TLSv1_0))
+ version = TLS1_1_VERSION;
+ } else if (flags & TLS_CONN_ENABLE_TLSv1_2) {
+ if (!(flags & (TLS_CONN_DISABLE_TLSv1_0 |
+ TLS_CONN_DISABLE_TLSv1_1)))
+ version = TLS1_2_VERSION;
+ }
+ if (!version) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Invalid TLS version configuration");
+ return -1;
+ }
+
+ if (SSL_set_min_proto_version(ssl, version) != 1) {
+ wpa_printf(MSG_DEBUG,
+ "OpenSSL: Failed to set minimum TLS version");
+ return -1;
+ }
+ }
+#endif /* >= 1.1.0 */
+
#ifdef CONFIG_SUITEB
#ifdef OPENSSL_IS_BORINGSSL
/* Start with defaults from BoringSSL */
@@ -2621,7 +2998,22 @@ static int tls_set_conn_flags(struct tls_connection *conn, unsigned int flags,
return -1;
}
}
+#else /* OPENSSL_IS_BORINGSSL */
+ if (!(flags & (TLS_CONN_SUITEB | TLS_CONN_SUITEB_NO_ECDH)) &&
+ openssl_ciphers && SSL_set_cipher_list(ssl, openssl_ciphers) != 1) {
+ wpa_printf(MSG_INFO,
+ "OpenSSL: Failed to set openssl_ciphers '%s'",
+ openssl_ciphers);
+ return -1;
+ }
#endif /* OPENSSL_IS_BORINGSSL */
+#else /* CONFIG_SUITEB */
+ if (openssl_ciphers && SSL_set_cipher_list(ssl, openssl_ciphers) != 1) {
+ wpa_printf(MSG_INFO,
+ "OpenSSL: Failed to set openssl_ciphers '%s'",
+ openssl_ciphers);
+ return -1;
+ }
#endif /* CONFIG_SUITEB */
return 0;
@@ -2743,6 +3135,15 @@ static int tls_connection_client_cert(struct tls_connection *conn,
return 0;
}
+#if OPENSSL_VERSION_NUMBER >= 0x10100000L && \
+ !defined(LIBRESSL_VERSION_NUMBER) && !defined(OPENSSL_IS_BORINGSSL)
+ if (SSL_use_certificate_chain_file(conn->ssl, client_cert) == 1) {
+ ERR_clear_error();
+ wpa_printf(MSG_DEBUG, "OpenSSL: SSL_use_certificate_chain_file"
+ " --> OK");
+ return 0;
+ }
+#else
if (SSL_use_certificate_file(conn->ssl, client_cert,
SSL_FILETYPE_PEM) == 1) {
ERR_clear_error();
@@ -2750,6 +3151,7 @@ static int tls_connection_client_cert(struct tls_connection *conn,
" --> OK");
return 0;
}
+#endif
tls_show_errors(MSG_DEBUG, __func__,
"SSL_use_certificate_file failed");
@@ -3523,11 +3925,13 @@ static int openssl_get_keyblock_size(SSL *ssl)
int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
- const char *label, u8 *out, size_t out_len)
+ const char *label, const u8 *context,
+ size_t context_len, u8 *out, size_t out_len)
{
if (!conn ||
SSL_export_keying_material(conn->ssl, out, out_len, label,
- os_strlen(label), NULL, 0, 0) != 1)
+ os_strlen(label), context, context_len,
+ context != NULL) != 1)
return -1;
return 0;
}
@@ -4445,7 +4849,8 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
params->subject_match,
params->altsubject_match,
params->suffix_match,
- params->domain_match))
+ params->domain_match,
+ params->check_cert_subject))
return -1;
if (engine_id && ca_cert_id) {
@@ -4503,6 +4908,40 @@ int tls_connection_set_params(void *tls_ctx, struct tls_connection *conn,
return -1;
}
+ if (!params->openssl_ecdh_curves) {
+#ifndef OPENSSL_IS_BORINGSSL
+#ifndef OPENSSL_NO_EC
+#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) && \
+ (OPENSSL_VERSION_NUMBER < 0x10100000L)
+ if (SSL_set_ecdh_auto(conn->ssl, 1) != 1) {
+ wpa_printf(MSG_INFO,
+ "OpenSSL: Failed to set ECDH curves to auto");
+ return -1;
+ }
+#endif /* >= 1.0.2 && < 1.1.0 */
+#endif /* OPENSSL_NO_EC */
+#endif /* OPENSSL_IS_BORINGSSL */
+ } else if (params->openssl_ecdh_curves[0]) {
+#if defined(OPENSSL_IS_BORINGSSL) || (OPENSSL_VERSION_NUMBER < 0x10002000L)
+ wpa_printf(MSG_INFO,
+ "OpenSSL: ECDH configuration nnot supported");
+ return -1;
+#else /* OPENSSL_IS_BORINGSSL || < 1.0.2 */
+#ifndef OPENSSL_NO_EC
+ if (SSL_set1_curves_list(conn->ssl,
+ params->openssl_ecdh_curves) != 1) {
+ wpa_printf(MSG_INFO,
+ "OpenSSL: Failed to set ECDH curves '%s'",
+ params->openssl_ecdh_curves);
+ return -1;
+ }
+#else /* OPENSSL_NO_EC */
+ wpa_printf(MSG_INFO, "OpenSSL: ECDH not supported");
+ return -1;
+#endif /* OPENSSL_NO_EC */
+#endif /* OPENSSL_IS_BORINGSSL */
+ }
+
if (tls_set_conn_flags(conn, params->flags,
params->openssl_ciphers) < 0)
return -1;
@@ -4552,6 +4991,15 @@ int tls_global_set_params(void *tls_ctx,
__func__, ERR_error_string(err, NULL));
}
+ os_free(data->check_cert_subject);
+ data->check_cert_subject = NULL;
+ if (params->check_cert_subject) {
+ data->check_cert_subject =
+ os_strdup(params->check_cert_subject);
+ if (!data->check_cert_subject)
+ return -1;
+ }
+
if (tls_global_ca_cert(data, params->ca_cert) ||
tls_global_client_cert(data, params->client_cert) ||
tls_global_private_key(data, params->private_key,
@@ -4569,6 +5017,44 @@ int tls_global_set_params(void *tls_ctx,
return -1;
}
+ if (!params->openssl_ecdh_curves) {
+#ifndef OPENSSL_IS_BORINGSSL
+#ifndef OPENSSL_NO_EC
+#if (OPENSSL_VERSION_NUMBER >= 0x10002000L) && \
+ (OPENSSL_VERSION_NUMBER < 0x10100000L)
+ if (SSL_CTX_set_ecdh_auto(ssl_ctx, 1) != 1) {
+ wpa_printf(MSG_INFO,
+ "OpenSSL: Failed to set ECDH curves to auto");
+ return -1;
+ }
+#endif /* >= 1.0.2 && < 1.1.0 */
+#endif /* OPENSSL_NO_EC */
+#endif /* OPENSSL_IS_BORINGSSL */
+ } else if (params->openssl_ecdh_curves[0]) {
+#if defined(OPENSSL_IS_BORINGSSL) || (OPENSSL_VERSION_NUMBER < 0x10002000L)
+ wpa_printf(MSG_INFO,
+ "OpenSSL: ECDH configuration nnot supported");
+ return -1;
+#else /* OPENSSL_IS_BORINGSSL || < 1.0.2 */
+#ifndef OPENSSL_NO_EC
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+ SSL_CTX_set_ecdh_auto(ssl_ctx, 1);
+#endif
+ if (SSL_CTX_set1_curves_list(ssl_ctx,
+ params->openssl_ecdh_curves) !=
+ 1) {
+ wpa_printf(MSG_INFO,
+ "OpenSSL: Failed to set ECDH curves '%s'",
+ params->openssl_ecdh_curves);
+ return -1;
+ }
+#else /* OPENSSL_NO_EC */
+ wpa_printf(MSG_INFO, "OpenSSL: ECDH not supported");
+ return -1;
+#endif /* OPENSSL_NO_EC */
+#endif /* OPENSSL_IS_BORINGSSL */
+ }
+
#ifdef SSL_OP_NO_TICKET
if (params->flags & TLS_CONN_DISABLE_SESSION_TICKET)
SSL_CTX_set_options(ssl_ctx, SSL_OP_NO_TICKET);
diff --git a/src/crypto/tls_wolfssl.c b/src/crypto/tls_wolfssl.c
index cc8c704466d2b..e9cb425c115a0 100644
--- a/src/crypto/tls_wolfssl.c
+++ b/src/crypto/tls_wolfssl.c
@@ -643,9 +643,9 @@ static int tls_match_alt_subject(WOLFSSL_X509 *cert, const char *match)
static int domain_suffix_match(const char *val, size_t len, const char *match,
- int full)
+ size_t match_len, int full)
{
- size_t i, match_len;
+ size_t i;
/* Check for embedded nuls that could mess up suffix matching */
for (i = 0; i < len; i++) {
@@ -656,7 +656,6 @@ static int domain_suffix_match(const char *val, size_t len, const char *match,
}
}
- match_len = os_strlen(match);
if (match_len > len || (full && match_len != len))
return 0;
@@ -674,7 +673,8 @@ static int domain_suffix_match(const char *val, size_t len, const char *match,
}
-static int tls_match_suffix(WOLFSSL_X509 *cert, const char *match, int full)
+static int tls_match_suffix_helper(WOLFSSL_X509 *cert, const char *match,
+ size_t match_len, int full)
{
WOLFSSL_ASN1_OBJECT *gen;
void *ext;
@@ -690,14 +690,14 @@ static int tls_match_suffix(WOLFSSL_X509 *cert, const char *match, int full)
for (j = 0; ext && j < wolfSSL_sk_num(ext); j++) {
gen = wolfSSL_sk_value(ext, j);
- if (gen->type != ALT_NAMES_OID)
+ if (gen->type != ASN_DNS_TYPE)
continue;
dns_name++;
wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate dNSName",
gen->obj, os_strlen((char *)gen->obj));
if (domain_suffix_match((const char *) gen->obj,
os_strlen((char *) gen->obj), match,
- full) == 1) {
+ match_len, full) == 1) {
wpa_printf(MSG_DEBUG, "TLS: %s in dNSName found",
full ? "Match" : "Suffix match");
wolfSSL_sk_ASN1_OBJECT_free(ext);
@@ -729,8 +729,8 @@ static int tls_match_suffix(WOLFSSL_X509 *cert, const char *match, int full)
continue;
wpa_hexdump_ascii(MSG_DEBUG, "TLS: Certificate commonName",
cn->data, cn->length);
- if (domain_suffix_match(cn->data, cn->length, match, full) == 1)
- {
+ if (domain_suffix_match(cn->data, cn->length,
+ match, match_len, full) == 1) {
wpa_printf(MSG_DEBUG, "TLS: %s in commonName found",
full ? "Match" : "Suffix match");
return 1;
@@ -743,6 +743,20 @@ static int tls_match_suffix(WOLFSSL_X509 *cert, const char *match, int full)
}
+static int tls_match_suffix(WOLFSSL_X509 *cert, const char *match, int full)
+{
+ const char *token, *last = NULL;
+
+ /* Process each match alternative separately until a match is found */
+ while ((token = cstr_token(match, ";", &last))) {
+ if (tls_match_suffix_helper(cert, token, last - token, full))
+ return 1;
+ }
+
+ return 0;
+}
+
+
static enum tls_fail_reason wolfssl_tls_fail_reason(int err)
{
switch (err) {
@@ -1487,6 +1501,9 @@ int tls_global_set_params(void *tls_ctx,
{
wpa_printf(MSG_DEBUG, "SSL: global set params");
+ if (params->check_cert_subject)
+ return -1; /* not yet supported */
+
if (tls_global_ca_cert(tls_ctx, params->ca_cert) < 0) {
wpa_printf(MSG_INFO, "SSL: Failed to load ca cert file '%s'",
params->ca_cert);
@@ -1524,6 +1541,12 @@ int tls_global_set_params(void *tls_ctx,
return -1;
}
+ if (params->openssl_ecdh_curves) {
+ wpa_printf(MSG_INFO,
+ "wolfSSL: openssl_ecdh_curves not supported");
+ return -1;
+ }
+
#ifdef HAVE_SESSION_TICKET
/* Session ticket is off by default - can't disable once on. */
if (!(params->flags & TLS_CONN_DISABLE_SESSION_TICKET))
@@ -1543,7 +1566,7 @@ int tls_global_set_params(void *tls_ctx,
}
-int tls_global_set_verify(void *tls_ctx, int check_crl)
+int tls_global_set_verify(void *tls_ctx, int check_crl, int strict)
{
wpa_printf(MSG_DEBUG, "SSL: global set verify: %d", check_crl);
@@ -1964,8 +1987,11 @@ int tls_connection_get_random(void *ssl_ctx, struct tls_connection *conn,
int tls_connection_export_key(void *tls_ctx, struct tls_connection *conn,
- const char *label, u8 *out, size_t out_len)
+ const char *label, const u8 *context,
+ size_t context_len, u8 *out, size_t out_len)
{
+ if (context)
+ return -1;
if (!conn || wolfSSL_make_eap_keys(conn->ssl, out, out_len, label) != 0)
return -1;
return 0;
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index 4ac9f16a0efcf..e7c8f318f35d7 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -58,6 +58,16 @@
#define HOSTAPD_CHAN_VHT_130_30 0x04000000
#define HOSTAPD_CHAN_VHT_150_10 0x08000000
+/* Allowed bandwidth mask */
+enum hostapd_chan_width_attr {
+ HOSTAPD_CHAN_WIDTH_10 = BIT(0),
+ HOSTAPD_CHAN_WIDTH_20 = BIT(1),
+ HOSTAPD_CHAN_WIDTH_40P = BIT(2),
+ HOSTAPD_CHAN_WIDTH_40M = BIT(3),
+ HOSTAPD_CHAN_WIDTH_80 = BIT(4),
+ HOSTAPD_CHAN_WIDTH_160 = BIT(5),
+};
+
/* Filter gratuitous ARP */
#define WPA_DATA_FRAME_FILTER_FLAG_ARP BIT(0)
/* Filter unsolicited Neighbor Advertisement */
@@ -111,6 +121,13 @@ struct hostapd_channel_data {
int flag;
/**
+ * allowed_bw - Allowed channel width bitmask
+ *
+ * See enum hostapd_chan_width_attr.
+ */
+ u32 allowed_bw;
+
+ /**
* max_tx_power - Regulatory transmit power limit in dBm
*/
u8 max_tx_power;
@@ -914,10 +931,10 @@ struct wpa_driver_associate_params {
* passphrase - RSN passphrase for PSK
*
* This value is made available only for WPA/WPA2-Personal (PSK) and
- * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE. This is
- * the 8..63 character ASCII passphrase, if available. Please note that
- * this can be %NULL if passphrase was not used to generate the PSK. In
- * that case, the psk field must be used to fetch the PSK.
+ * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK. This
+ * is the 8..63 character ASCII passphrase, if available. Please note
+ * that this can be %NULL if passphrase was not used to generate the
+ * PSK. In that case, the psk field must be used to fetch the PSK.
*/
const char *passphrase;
@@ -925,9 +942,9 @@ struct wpa_driver_associate_params {
* psk - RSN PSK (alternative for passphrase for PSK)
*
* This value is made available only for WPA/WPA2-Personal (PSK) and
- * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE. This is
- * the 32-octet (256-bit) PSK, if available. The driver wrapper should
- * be prepared to handle %NULL value as an error.
+ * only for drivers that set WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK. This
+ * is the 32-octet (256-bit) PSK, if available. The driver wrapper
+ * should be prepared to handle %NULL value as an error.
*/
const u8 *psk;
@@ -1367,6 +1384,23 @@ struct wpa_driver_ap_params {
* service).
*/
int multicast_to_unicast;
+
+ /**
+ * ftm_responder - Whether FTM responder is enabled
+ */
+ int ftm_responder;
+
+ /**
+ * lci - Binary data, the content of an LCI report IE with type 8 as
+ * defined in IEEE Std 802.11-2016, 9.4.2.22.10
+ */
+ const struct wpabuf *lci;
+
+ /**
+ * civic - Binary data, the content of a measurement report IE with
+ * type 11 as defined in IEEE Std 802.11-2016, 9.4.2.22.13
+ */
+ const struct wpabuf *civic;
};
struct wpa_driver_mesh_bss_params {
@@ -1424,6 +1458,7 @@ struct wpa_driver_capa {
#define WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384 0x00002000
#define WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 0x00004000
#define WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384 0x00008000
+#define WPA_DRIVER_CAPA_KEY_MGMT_SAE 0x00010000
/** Bitfield of supported key management suites */
unsigned int key_mgmt;
@@ -1457,7 +1492,7 @@ struct wpa_driver_capa {
#define WPA_DRIVER_FLAGS_DFS_OFFLOAD 0x00000004
/** Driver takes care of RSN 4-way handshake internally; PMK is configured with
* struct wpa_driver_ops::set_key using alg = WPA_ALG_PMK */
-#define WPA_DRIVER_FLAGS_4WAY_HANDSHAKE 0x00000008
+#define WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X 0x00000008
/** Driver is for a wired Ethernet interface */
#define WPA_DRIVER_FLAGS_WIRED 0x00000010
/** Driver provides separate commands for authentication and association (SME in
@@ -1579,6 +1614,10 @@ struct wpa_driver_capa {
#define WPA_DRIVER_FLAGS_MFP_OPTIONAL 0x0040000000000000ULL
/** Driver is a self-managed regulatory device */
#define WPA_DRIVER_FLAGS_SELF_MANAGED_REGULATORY 0x0080000000000000ULL
+/** Driver supports FTM responder functionality */
+#define WPA_DRIVER_FLAGS_FTM_RESPONDER 0x0100000000000000ULL
+/** Driver support 4-way handshake offload for WPA-Personal */
+#define WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK 0x0200000000000000ULL
u64 flags;
#define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
@@ -1902,17 +1941,6 @@ enum smps_mode {
SMPS_INVALID,
};
-/* enum chan_width - Channel width definitions */
-enum chan_width {
- CHAN_WIDTH_20_NOHT,
- CHAN_WIDTH_20,
- CHAN_WIDTH_40,
- CHAN_WIDTH_80,
- CHAN_WIDTH_80P80,
- CHAN_WIDTH_160,
- CHAN_WIDTH_UNKNOWN
-};
-
#define WPA_INVALID_NOISE 9999
/**
@@ -1943,6 +1971,26 @@ struct wpa_signal_info {
};
/**
+ * struct wpa_channel_info - Information about the current channel
+ * @frequency: Center frequency of the primary 20 MHz channel
+ * @chanwidth: Width of the current operating channel
+ * @sec_channel: Location of the secondary 20 MHz channel (either +1 or -1).
+ * This field is only filled in when using a 40 MHz channel.
+ * @center_frq1: Center frequency of frequency segment 0
+ * @center_frq2: Center frequency of frequency segment 1 (for 80+80 channels)
+ * @seg1_idx: Frequency segment 1 index when using a 80+80 channel. This is
+ * derived from center_frq2 for convenience.
+ */
+struct wpa_channel_info {
+ u32 frequency;
+ enum chan_width chanwidth;
+ int sec_channel;
+ int center_frq1;
+ int center_frq2;
+ u8 seg1_idx;
+};
+
+/**
* struct beacon_data - Beacon data
* @head: Head portion of Beacon frame (before TIM IE)
* @tail: Tail portion of Beacon frame (after TIM IE)
@@ -2108,17 +2156,19 @@ enum wpa_drv_update_connect_params_mask {
* use %WLAN_STATUS_UNSPECIFIED_FAILURE if wpa_supplicant cannot give
* the real status code for failures. Used only for the request interface
* from user space to the driver.
+ * @pmkid: Generated PMKID as part of external auth exchange (e.g., SAE).
*/
struct external_auth {
enum {
EXT_AUTH_START,
EXT_AUTH_ABORT,
} action;
- u8 bssid[ETH_ALEN];
- u8 ssid[SSID_MAX_LEN];
+ const u8 *bssid;
+ const u8 *ssid;
size_t ssid_len;
unsigned int key_mgmt_suite;
u16 status;
+ const u8 *pmkid;
};
/**
@@ -3378,6 +3428,14 @@ struct wpa_driver_ops {
int (*signal_poll)(void *priv, struct wpa_signal_info *signal_info);
/**
+ * channel_info - Get parameters of the current operating channel
+ * @priv: Private driver interface data
+ * @channel_info: Channel info structure
+ * Returns: 0 on success, negative (<0) on failure
+ */
+ int (*channel_info)(void *priv, struct wpa_channel_info *channel_info);
+
+ /**
* set_authmode - Set authentication algorithm(s) for static WEP
* @priv: Private driver interface data
* @authmode: 1=Open System, 2=Shared Key, 3=both
@@ -3658,7 +3716,7 @@ struct wpa_driver_ops {
/**
* status - Get driver interface status information
* @priv: Private driver interface data
- * @buf: Buffer for printing tou the status information
+ * @buf: Buffer for printing the status information
* @buflen: Maximum length of the buffer
* Returns: Length of written status information or -1 on failure
*/
@@ -3782,6 +3840,14 @@ struct wpa_driver_ops {
int (*set_transmit_next_pn)(void *priv, struct transmit_sa *sa);
/**
+ * set_receive_lowest_pn - Set receive lowest PN
+ * @priv: Private driver interface data
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+ int (*set_receive_lowest_pn)(void *priv, struct receive_sa *sa);
+
+ /**
* create_receive_sc - create secure channel for receiving
* @priv: Private driver interface data
* @sc: secure channel
@@ -4092,6 +4158,15 @@ struct wpa_driver_ops {
*/
int (*send_external_auth_status)(void *priv,
struct external_auth *params);
+
+ /**
+ * set_4addr_mode - Set 4-address mode
+ * @priv: Private driver interface data
+ * @bridge_ifname: Bridge interface name
+ * @val: 0 - disable 4addr mode, 1 - enable 4addr mode
+ * Returns: 0 on success, < 0 on failure
+ */
+ int (*set_4addr_mode)(void *priv, const char *bridge_ifname, int val);
};
/**
@@ -5534,6 +5609,8 @@ const char * event_to_string(enum wpa_event_type event);
/* Convert chan_width to a string for logging and control interfaces */
const char * channel_width_to_string(enum chan_width width);
+int channel_width_to_int(enum chan_width width);
+
int ht_supported(const struct hostapd_hw_modes *mode);
int vht_supported(const struct hostapd_hw_modes *mode);
diff --git a/src/drivers/driver_atheros.c b/src/drivers/driver_atheros.c
index 62f5baad63612..807cd94691d0d 100644
--- a/src/drivers/driver_atheros.c
+++ b/src/drivers/driver_atheros.c
@@ -1218,8 +1218,7 @@ atheros_new_sta(struct atheros_driver_data *drv, u8 addr[IEEE80211_ADDR_LEN])
#ifdef ATH_WPS_IE
/* if WPS IE is present, preference is given to WPS */
- if (ie.wps_ie &&
- (ie.wps_ie[1] > 0 && (ie.wps_ie[0] == WLAN_EID_VENDOR_SPECIFIC))) {
+ if (ie.wps_ie[0] == WLAN_EID_VENDOR_SPECIFIC && ie.wps_ie[1] > 0) {
iebuf = ie.wps_ie;
ielen = ie.wps_ie[1];
}
diff --git a/src/drivers/driver_bsd.c b/src/drivers/driver_bsd.c
index 8621aa0b0099f..46754968f13d7 100644
--- a/src/drivers/driver_bsd.c
+++ b/src/drivers/driver_bsd.c
@@ -143,7 +143,7 @@ bsd_get80211(void *priv, struct ieee80211req *ireq, int op, void *arg,
ireq->i_data = arg;
if (ioctl(drv->global->sock, SIOCG80211, ireq) < 0) {
- wpa_printf(MSG_ERROR, "ioctl[SIOCS80211, op=%u, "
+ wpa_printf(MSG_ERROR, "ioctl[SIOCG80211, op=%u, "
"arg_len=%u]: %s", op, arg_len, strerror(errno));
return -1;
}
diff --git a/src/drivers/driver_common.c b/src/drivers/driver_common.c
index ac0916e4061f9..e55e6cd2b795a 100644
--- a/src/drivers/driver_common.c
+++ b/src/drivers/driver_common.c
@@ -115,6 +115,25 @@ const char * channel_width_to_string(enum chan_width width)
}
+int channel_width_to_int(enum chan_width width)
+{
+ switch (width) {
+ case CHAN_WIDTH_20_NOHT:
+ case CHAN_WIDTH_20:
+ return 20;
+ case CHAN_WIDTH_40:
+ return 40;
+ case CHAN_WIDTH_80:
+ return 80;
+ case CHAN_WIDTH_80P80:
+ case CHAN_WIDTH_160:
+ return 160;
+ default:
+ return 0;
+ }
+}
+
+
int ht_supported(const struct hostapd_hw_modes *mode)
{
if (!(mode->flags & HOSTAPD_MODE_FLAG_HT_INFO_KNOWN)) {
@@ -234,7 +253,8 @@ const char * driver_flag_to_string(u64 flag)
DF2S(DRIVER_IE);
DF2S(SET_KEYS_AFTER_ASSOC);
DF2S(DFS_OFFLOAD);
- DF2S(4WAY_HANDSHAKE);
+ DF2S(4WAY_HANDSHAKE_PSK);
+ DF2S(4WAY_HANDSHAKE_8021X);
DF2S(WIRED);
DF2S(SME);
DF2S(AP);
diff --git a/src/drivers/driver_hostap.c b/src/drivers/driver_hostap.c
index 597da335e4748..61b39b19721c1 100644
--- a/src/drivers/driver_hostap.c
+++ b/src/drivers/driver_hostap.c
@@ -231,7 +231,11 @@ static int hostap_init_sockets(struct hostap_driver_data *drv, u8 *own_addr)
}
memset(&ifr, 0, sizeof(ifr));
- snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%sap", drv->iface);
+ if (os_snprintf(ifr.ifr_name, sizeof(ifr.ifr_name), "%sap",
+ drv->iface) >= (int) sizeof(ifr.ifr_name)) {
+ wpa_printf(MSG_ERROR, "hostap: AP interface name truncated");
+ return -1;
+ }
if (ioctl(drv->sock, SIOCGIFINDEX, &ifr) != 0) {
wpa_printf(MSG_ERROR, "ioctl(SIOCGIFINDEX): %s",
strerror(errno));
@@ -348,7 +352,10 @@ static int hostap_set_iface_flags(void *priv, int dev_up)
struct ifreq ifr;
char ifname[IFNAMSIZ];
- os_snprintf(ifname, IFNAMSIZ, "%sap", drv->iface);
+ if (os_snprintf(ifname, IFNAMSIZ, "%sap", drv->iface) >= IFNAMSIZ) {
+ wpa_printf(MSG_ERROR, "hostap: AP interface name truncated");
+ return -1;
+ }
if (linux_set_iface_flags(drv->ioctl_sock, ifname, dev_up) < 0)
return -1;
@@ -1124,6 +1131,7 @@ static struct hostapd_hw_modes * hostap_get_hw_feature_data(void *priv,
for (i = 0; i < 14; i++) {
mode->channels[i].chan = i + 1;
mode->channels[i].freq = chan2freq[i];
+ mode->channels[i].allowed_bw = HOSTAPD_CHAN_WIDTH_20;
/* TODO: Get allowed channel list from the driver */
if (i >= 11)
mode->channels[i].flag = HOSTAPD_CHAN_DISABLED;
diff --git a/src/drivers/driver_macsec_linux.c b/src/drivers/driver_macsec_linux.c
index 4f6629f5aaa2b..9d981bb70f78c 100644
--- a/src/drivers/driver_macsec_linux.c
+++ b/src/drivers/driver_macsec_linux.c
@@ -177,6 +177,9 @@ static int try_commit(struct macsec_drv_data *drv)
if (drv->controlled_port_enabled_set) {
struct rtnl_link *change = rtnl_link_alloc();
+ wpa_printf(MSG_DEBUG, DRV_PREFIX
+ "%s: try_commit controlled_port_enabled=%d",
+ drv->ifname, drv->controlled_port_enabled);
if (!change)
return -1;
@@ -196,13 +199,24 @@ static int try_commit(struct macsec_drv_data *drv)
drv->controlled_port_enabled_set = FALSE;
}
- if (drv->protect_frames_set)
+ if (drv->protect_frames_set) {
+ wpa_printf(MSG_DEBUG, DRV_PREFIX
+ "%s: try_commit protect_frames=%d",
+ drv->ifname, drv->protect_frames);
rtnl_link_macsec_set_protect(drv->link, drv->protect_frames);
+ }
- if (drv->encrypt_set)
+ if (drv->encrypt_set) {
+ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: try_commit encrypt=%d",
+ drv->ifname, drv->encrypt);
rtnl_link_macsec_set_encrypt(drv->link, drv->encrypt);
+ }
if (drv->replay_protect_set) {
+ wpa_printf(MSG_DEBUG, DRV_PREFIX
+ "%s: try_commit replay_protect=%d replay_window=%d",
+ drv->ifname, drv->replay_protect,
+ drv->replay_window);
rtnl_link_macsec_set_replay_protect(drv->link,
drv->replay_protect);
if (drv->replay_protect)
@@ -210,8 +224,12 @@ static int try_commit(struct macsec_drv_data *drv)
drv->replay_window);
}
- if (drv->encoding_sa_set)
+ if (drv->encoding_sa_set) {
+ wpa_printf(MSG_DEBUG, DRV_PREFIX
+ "%s: try_commit encoding_sa=%d",
+ drv->ifname, drv->encoding_sa);
rtnl_link_macsec_set_encoding_sa(drv->link, drv->encoding_sa);
+ }
err = rtnl_link_add(drv->sk, drv->link, 0);
if (err < 0)
@@ -318,6 +336,8 @@ static int macsec_drv_macsec_init(void *priv, struct macsec_init_params *params)
drv->common.ifname);
goto cache;
}
+ wpa_printf(MSG_DEBUG, DRV_PREFIX "ifname=%s parent_ifi=%d",
+ drv->common.ifname, drv->parent_ifi);
err = init_genl_ctx(drv);
if (err < 0)
@@ -670,6 +690,50 @@ static int macsec_drv_get_receive_lowest_pn(void *priv, struct receive_sa *sa)
/**
+ * macsec_drv_set_receive_lowest_pn - Set receive lowest PN
+ * @priv: Private driver interface data
+ * @sa: secure association
+ * Returns: 0 on success, -1 on failure (or if not supported)
+ */
+static int macsec_drv_set_receive_lowest_pn(void *priv, struct receive_sa *sa)
+{
+ struct macsec_drv_data *drv = priv;
+ struct macsec_genl_ctx *ctx = &drv->ctx;
+ struct nl_msg *msg;
+ struct nlattr *nest;
+ int ret = -1;
+
+ wpa_printf(MSG_DEBUG,
+ DRV_PREFIX "%s: set_receive_lowest_pn -> %d: %d",
+ drv->ifname, sa->an, sa->next_pn);
+
+ msg = msg_prepare(MACSEC_CMD_UPD_RXSA, ctx, drv->ifi);
+ if (!msg)
+ return ret;
+
+ nest = nla_nest_start(msg, MACSEC_ATTR_SA_CONFIG);
+ if (!nest)
+ goto nla_put_failure;
+
+ NLA_PUT_U8(msg, MACSEC_SA_ATTR_AN, sa->an);
+ NLA_PUT_U32(msg, MACSEC_SA_ATTR_PN, sa->next_pn);
+
+ nla_nest_end(msg, nest);
+
+ ret = nl_send_recv(ctx->sk, msg);
+ if (ret < 0) {
+ wpa_printf(MSG_ERROR,
+ DRV_PREFIX "failed to communicate: %d (%s)",
+ ret, nl_geterror(-ret));
+ }
+
+nla_put_failure:
+ nlmsg_free(msg);
+ return ret;
+}
+
+
+/**
* macsec_drv_get_transmit_next_pn - Get transmit next PN
* @priv: Private driver interface data
* @sa: secure association
@@ -754,8 +818,10 @@ static int macsec_drv_create_receive_sc(void *priv, struct receive_sc *sc,
struct nl_msg *msg;
int ret = -1;
- wpa_printf(MSG_DEBUG, "%s -> " SCISTR, __func__,
- SCI2STR(sc->sci.addr, sc->sci.port));
+ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: create_receive_sc -> " SCISTR
+ " (conf_offset=%u validation=%d)",
+ drv->ifname, SCI2STR(sc->sci.addr, sc->sci.port),
+ conf_offset, validation);
msg = msg_prepare(MACSEC_CMD_ADD_RXSC, ctx, drv->ifi);
if (!msg)
@@ -790,8 +856,8 @@ static int macsec_drv_delete_receive_sc(void *priv, struct receive_sc *sc)
struct nl_msg *msg;
int ret = -1;
- wpa_printf(MSG_DEBUG, "%s -> " SCISTR, __func__,
- SCI2STR(sc->sci.addr, sc->sci.port));
+ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: delete_receive_sc -> " SCISTR,
+ drv->ifname, SCI2STR(sc->sci.addr, sc->sci.port));
msg = msg_prepare(MACSEC_CMD_DEL_RXSC, ctx, drv->ifi);
if (!msg)
@@ -827,8 +893,17 @@ static int macsec_drv_create_receive_sa(void *priv, struct receive_sa *sa)
struct nlattr *nest;
int ret = -1;
- wpa_printf(MSG_DEBUG, "%s -> %d on " SCISTR, __func__, sa->an,
- SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
+ wpa_printf(MSG_DEBUG,
+ DRV_PREFIX "%s: create_receive_sa -> %d on " SCISTR
+ " (enable_receive=%d next_pn=%u)",
+ drv->ifname, sa->an,
+ SCI2STR(sa->sc->sci.addr, sa->sc->sci.port),
+ sa->enable_receive, sa->next_pn);
+ wpa_hexdump(MSG_DEBUG, DRV_PREFIX "SA keyid",
+ &sa->pkey->key_identifier,
+ sizeof(sa->pkey->key_identifier));
+ wpa_hexdump_key(MSG_DEBUG, DRV_PREFIX "SA key",
+ sa->pkey->key, sa->pkey->key_len);
msg = msg_prepare(MACSEC_CMD_ADD_RXSA, ctx, drv->ifi);
if (!msg)
@@ -877,7 +952,8 @@ static int macsec_drv_delete_receive_sa(void *priv, struct receive_sa *sa)
struct nlattr *nest;
int ret = -1;
- wpa_printf(MSG_DEBUG, "%s -> %d on " SCISTR, __func__, sa->an,
+ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: delete_receive_sa -> %d on "
+ SCISTR, drv->ifname, sa->an,
SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
msg = msg_prepare(MACSEC_CMD_DEL_RXSA, ctx, drv->ifi);
@@ -954,7 +1030,8 @@ static int macsec_drv_enable_receive_sa(void *priv, struct receive_sa *sa)
struct macsec_drv_data *drv = priv;
struct macsec_genl_ctx *ctx = &drv->ctx;
- wpa_printf(MSG_DEBUG, "%s -> %d on " SCISTR, __func__, sa->an,
+ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: enable_receive_sa -> %d on "
+ SCISTR, drv->ifname, sa->an,
SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
return set_active_rx_sa(ctx, drv->ifi, mka_sci_u64(&sa->sc->sci),
@@ -973,7 +1050,8 @@ static int macsec_drv_disable_receive_sa(void *priv, struct receive_sa *sa)
struct macsec_drv_data *drv = priv;
struct macsec_genl_ctx *ctx = &drv->ctx;
- wpa_printf(MSG_DEBUG, "%s -> %d on " SCISTR, __func__, sa->an,
+ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: disable_receive_sa -> %d on "
+ SCISTR, drv->ifname, sa->an,
SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
return set_active_rx_sa(ctx, drv->ifi, mka_sci_u64(&sa->sc->sci),
@@ -1017,7 +1095,10 @@ static int macsec_drv_create_transmit_sc(
u64 sci;
int err;
- wpa_printf(MSG_DEBUG, "%s", __func__);
+ wpa_printf(MSG_DEBUG, DRV_PREFIX
+ "%s: create_transmit_sc -> " SCISTR " (conf_offset=%d)",
+ drv->common.ifname, SCI2STR(sc->sci.addr, sc->sci.port),
+ conf_offset);
if (!drv->sk) {
wpa_printf(MSG_ERROR, DRV_PREFIX "NULL rtnl socket");
@@ -1060,6 +1141,9 @@ static int macsec_drv_create_transmit_sc(
drv->ifi = rtnl_link_get_ifindex(link);
ifname = rtnl_link_get_name(link);
+ wpa_printf(MSG_DEBUG,
+ DRV_PREFIX "%s: create_transmit_sc: ifi=%d ifname=%s",
+ drv->common.ifname, drv->ifi, ifname);
os_strlcpy(drv->ifname, ifname, sizeof(drv->ifname));
rtnl_link_put(link);
@@ -1088,7 +1172,8 @@ static int macsec_drv_delete_transmit_sc(void *priv, struct transmit_sc *sc)
struct macsec_drv_data *drv = priv;
int err;
- wpa_printf(MSG_DEBUG, "%s", __func__);
+ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: delete_transmit_sc -> " SCISTR,
+ drv->ifname, SCI2STR(sc->sci.addr, sc->sci.port));
if (!drv->sk)
return 0;
@@ -1125,7 +1210,16 @@ static int macsec_drv_create_transmit_sa(void *priv, struct transmit_sa *sa)
struct nlattr *nest;
int ret = -1;
- wpa_printf(MSG_DEBUG, "%s -> %d", __func__, sa->an);
+ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: create_transmit_sa -> %d on "
+ SCISTR " (enable_transmit=%d next_pn=%u)",
+ drv->ifname, sa->an,
+ SCI2STR(sa->sc->sci.addr, sa->sc->sci.port),
+ sa->enable_transmit, sa->next_pn);
+ wpa_hexdump(MSG_DEBUG, DRV_PREFIX "SA keyid",
+ &sa->pkey->key_identifier,
+ sizeof(sa->pkey->key_identifier));
+ wpa_hexdump_key(MSG_DEBUG, DRV_PREFIX "SA key",
+ sa->pkey->key, sa->pkey->key_len);
msg = msg_prepare(MACSEC_CMD_ADD_TXSA, ctx, drv->ifi);
if (!msg)
@@ -1171,7 +1265,9 @@ static int macsec_drv_delete_transmit_sa(void *priv, struct transmit_sa *sa)
struct nlattr *nest;
int ret = -1;
- wpa_printf(MSG_DEBUG, "%s -> %d", __func__, sa->an);
+ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: delete_transmit_sa -> %d on "
+ SCISTR, drv->ifname, sa->an,
+ SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
msg = msg_prepare(MACSEC_CMD_DEL_TXSA, ctx, drv->ifi);
if (!msg)
@@ -1243,7 +1339,9 @@ static int macsec_drv_enable_transmit_sa(void *priv, struct transmit_sa *sa)
struct macsec_genl_ctx *ctx = &drv->ctx;
int ret;
- wpa_printf(MSG_DEBUG, "%s -> %d", __func__, sa->an);
+ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: enable_transmit_sa -> %d on "
+ SCISTR, drv->ifname, sa->an,
+ SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
ret = set_active_tx_sa(ctx, drv->ifi, sa->an, TRUE);
if (ret < 0) {
@@ -1269,12 +1367,38 @@ static int macsec_drv_disable_transmit_sa(void *priv, struct transmit_sa *sa)
struct macsec_drv_data *drv = priv;
struct macsec_genl_ctx *ctx = &drv->ctx;
- wpa_printf(MSG_DEBUG, "%s -> %d", __func__, sa->an);
+ wpa_printf(MSG_DEBUG, DRV_PREFIX "%s: disable_transmit_sa -> %d on "
+ SCISTR, drv->ifname, sa->an,
+ SCI2STR(sa->sc->sci.addr, sa->sc->sci.port));
return set_active_tx_sa(ctx, drv->ifi, sa->an, FALSE);
}
+static int macsec_drv_status(void *priv, char *buf, size_t buflen)
+{
+ struct macsec_drv_data *drv = priv;
+ int res;
+ char *pos, *end;
+
+ pos = buf;
+ end = buf + buflen;
+
+ res = os_snprintf(pos, end - pos,
+ "ifname=%s\n"
+ "ifi=%d\n"
+ "parent_ifname=%s\n"
+ "parent_ifi=%d\n",
+ drv->common.ifname, drv->ifi,
+ drv->ifname, drv->parent_ifi);
+ if (os_snprintf_error(end - pos, res))
+ return pos - buf;
+ pos += res;
+
+ return pos - buf;
+}
+
+
const struct wpa_driver_ops wpa_driver_macsec_linux_ops = {
.name = "macsec_linux",
.desc = "MACsec Ethernet driver for Linux",
@@ -1293,6 +1417,7 @@ const struct wpa_driver_ops wpa_driver_macsec_linux_ops = {
.set_current_cipher_suite = macsec_drv_set_current_cipher_suite,
.enable_controlled_port = macsec_drv_enable_controlled_port,
.get_receive_lowest_pn = macsec_drv_get_receive_lowest_pn,
+ .set_receive_lowest_pn = macsec_drv_set_receive_lowest_pn,
.get_transmit_next_pn = macsec_drv_get_transmit_next_pn,
.set_transmit_next_pn = macsec_drv_set_transmit_next_pn,
.create_receive_sc = macsec_drv_create_receive_sc,
@@ -1307,4 +1432,6 @@ const struct wpa_driver_ops wpa_driver_macsec_linux_ops = {
.delete_transmit_sa = macsec_drv_delete_transmit_sa,
.enable_transmit_sa = macsec_drv_enable_transmit_sa,
.disable_transmit_sa = macsec_drv_disable_transmit_sa,
+
+ .status = macsec_drv_status,
};
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 871a5d0b4a205..54fe3900096a8 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -40,6 +40,9 @@
#include "driver_nl80211.h"
+#ifndef NETLINK_CAP_ACK
+#define NETLINK_CAP_ACK 10
+#endif /* NETLINK_CAP_ACK */
/* support for extack if compilation headers are too old */
#ifndef NETLINK_EXT_ACK
#define NETLINK_EXT_ACK 11
@@ -304,6 +307,7 @@ void nl80211_mark_disconnected(struct wpa_driver_nl80211_data *drv)
os_memcpy(drv->prev_bssid, drv->bssid, ETH_ALEN);
drv->associated = 0;
os_memset(drv->bssid, 0, ETH_ALEN);
+ drv->first_bss->freq = 0;
}
@@ -406,6 +410,11 @@ static int send_and_recv(struct nl80211_global *global,
setsockopt(nl_socket_get_fd(nl_handle), SOL_NETLINK,
NETLINK_EXT_ACK, &opt, sizeof(opt));
+ /* try to set NETLINK_CAP_ACK to 1, ignoring errors */
+ opt = 1;
+ setsockopt(nl_socket_get_fd(nl_handle), SOL_NETLINK,
+ NETLINK_CAP_ACK, &opt, sizeof(opt));
+
err = nl_send_auto_complete(nl_handle, msg);
if (err < 0)
goto out;
@@ -1539,6 +1548,70 @@ int nl80211_get_link_noise(struct wpa_driver_nl80211_data *drv,
}
+static int get_channel_info(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1] = { 0 };
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ struct wpa_channel_info *chan_info = arg;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ os_memset(chan_info, 0, sizeof(struct wpa_channel_info));
+ chan_info->chanwidth = CHAN_WIDTH_UNKNOWN;
+
+ if (tb[NL80211_ATTR_WIPHY_FREQ])
+ chan_info->frequency =
+ nla_get_u32(tb[NL80211_ATTR_WIPHY_FREQ]);
+ if (tb[NL80211_ATTR_CHANNEL_WIDTH])
+ chan_info->chanwidth = convert2width(
+ nla_get_u32(tb[NL80211_ATTR_CHANNEL_WIDTH]));
+ if (tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
+ enum nl80211_channel_type ct =
+ nla_get_u32(tb[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
+
+ switch (ct) {
+ case NL80211_CHAN_HT40MINUS:
+ chan_info->sec_channel = -1;
+ break;
+ case NL80211_CHAN_HT40PLUS:
+ chan_info->sec_channel = 1;
+ break;
+ default:
+ chan_info->sec_channel = 0;
+ break;
+ }
+ }
+ if (tb[NL80211_ATTR_CENTER_FREQ1])
+ chan_info->center_frq1 =
+ nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ1]);
+ if (tb[NL80211_ATTR_CENTER_FREQ2])
+ chan_info->center_frq2 =
+ nla_get_u32(tb[NL80211_ATTR_CENTER_FREQ2]);
+
+ if (chan_info->center_frq2) {
+ u8 seg1_idx = 0;
+
+ if (ieee80211_freq_to_chan(chan_info->center_frq2, &seg1_idx) !=
+ NUM_HOSTAPD_MODES)
+ chan_info->seg1_idx = seg1_idx;
+ }
+
+ return NL_SKIP;
+}
+
+
+static int nl80211_channel_info(void *priv, struct wpa_channel_info *ci)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+
+ msg = nl80211_drv_msg(drv, 0, NL80211_CMD_GET_INTERFACE);
+ return send_and_recv_msgs(drv, msg, get_channel_info, ci);
+}
+
+
static void wpa_driver_nl80211_event_receive(int sock, void *eloop_ctx,
void *handle)
{
@@ -2169,6 +2242,11 @@ static int nl80211_mgmt_subscribe_non_ap(struct i802_bss *bss)
ret = -1;
#endif /* CONFIG_DPP */
#ifdef CONFIG_IEEE80211W
+#ifdef CONFIG_OCV
+ /* SA Query Request */
+ if (nl80211_register_action_frame(bss, (u8 *) "\x08\x00", 2) < 0)
+ ret = -1;
+#endif /* CONFIG_OCV */
/* SA Query Response */
if (nl80211_register_action_frame(bss, (u8 *) "\x08\x01", 2) < 0)
ret = -1;
@@ -2392,6 +2470,16 @@ static int nl80211_mgmt_subscribe_ap_dev_sme(struct i802_bss *bss)
if (nl80211_action_subscribe_ap(bss))
goto out_err;
+ if (bss->drv->device_ap_sme) {
+ u16 type = (WLAN_FC_TYPE_MGMT << 2) | (WLAN_FC_STYPE_AUTH << 4);
+
+ /* Register for all Authentication frames */
+ if (nl80211_register_frame(bss, bss->nl_mgmt, type, NULL, 0)
+ < 0)
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Failed to subscribe to handle Authentication frames - SAE offload may not work");
+ }
+
nl80211_mgmt_handle_register_eloop(bss);
return 0;
@@ -2962,7 +3050,7 @@ static int wpa_driver_nl80211_set_key(const char *ifname, struct i802_bss *bss,
#endif /* CONFIG_DRIVER_NL80211_QCA */
if (alg == WPA_ALG_PMK &&
- (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE))
+ (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X))
return nl80211_set_pmk(drv, key, key_len, addr);
if (alg == WPA_ALG_NONE) {
@@ -3400,8 +3488,8 @@ retry:
goto fail;
}
if (params->ssid) {
- wpa_hexdump_ascii(MSG_DEBUG, " * SSID",
- params->ssid, params->ssid_len);
+ wpa_printf(MSG_DEBUG, " * SSID=%s",
+ wpa_ssid_txt(params->ssid, params->ssid_len));
if (nla_put(msg, NL80211_ATTR_SSID, params->ssid_len,
params->ssid))
goto fail;
@@ -3968,7 +4056,7 @@ static int wpa_driver_nl80211_set_ap(void *priv,
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
u8 cmd = NL80211_CMD_NEW_BEACON;
- int ret;
+ int ret = -ENOBUFS;
int beacon_set;
int num_suites;
int smps_mode;
@@ -3997,8 +4085,8 @@ static int wpa_driver_nl80211_set_ap(void *priv,
wpa_printf(MSG_DEBUG, "nl80211: beacon_rate=%u", params->beacon_rate);
wpa_printf(MSG_DEBUG, "nl80211: rate_type=%d", params->rate_type);
wpa_printf(MSG_DEBUG, "nl80211: dtim_period=%d", params->dtim_period);
- wpa_hexdump_ascii(MSG_DEBUG, "nl80211: ssid",
- params->ssid, params->ssid_len);
+ wpa_printf(MSG_DEBUG, "nl80211: ssid=%s",
+ wpa_ssid_txt(params->ssid, params->ssid_len));
if (!(msg = nl80211_bss_msg(bss, 0, cmd)) ||
nla_put(msg, NL80211_ATTR_BEACON_HEAD, params->head_len,
params->head) ||
@@ -4083,6 +4171,11 @@ static int wpa_driver_nl80211_set_ap(void *priv,
nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT)))
goto fail;
+ if (drv->device_ap_sme &&
+ (params->key_mgmt_suites & WPA_KEY_MGMT_SAE) &&
+ nla_put_flag(msg, NL80211_ATTR_EXTERNAL_AUTH_SUPPORT))
+ goto fail;
+
wpa_printf(MSG_DEBUG, "nl80211: pairwise_ciphers=0x%x",
params->pairwise_ciphers);
num_suites = wpa_cipher_to_cipher_suites(params->pairwise_ciphers,
@@ -4174,6 +4267,29 @@ static int wpa_driver_nl80211_set_ap(void *priv,
goto fail;
}
+ if (params->ftm_responder) {
+ struct nlattr *ftm;
+
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_FTM_RESPONDER)) {
+ ret = -ENOTSUP;
+ goto fail;
+ }
+
+ ftm = nla_nest_start(msg, NL80211_ATTR_FTM_RESPONDER);
+ if (!ftm ||
+ nla_put_flag(msg, NL80211_FTM_RESP_ATTR_ENABLED) ||
+ (params->lci &&
+ nla_put(msg, NL80211_FTM_RESP_ATTR_LCI,
+ wpabuf_len(params->lci),
+ wpabuf_head(params->lci))) ||
+ (params->civic &&
+ nla_put(msg, NL80211_FTM_RESP_ATTR_CIVICLOC,
+ wpabuf_len(params->civic),
+ wpabuf_head(params->civic))))
+ goto fail;
+ nla_nest_end(msg, ftm);
+ }
+
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
if (ret) {
wpa_printf(MSG_DEBUG, "nl80211: Beacon set failed: %d (%s)",
@@ -4225,7 +4341,7 @@ static int wpa_driver_nl80211_set_ap(void *priv,
return ret;
fail:
nlmsg_free(msg);
- return -ENOBUFS;
+ return ret;
}
@@ -4579,7 +4695,8 @@ static int wpa_driver_nl80211_sta_add(void *priv,
goto fail;
#endif /* CONFIG_MESH */
- if (params->flags & WPA_STA_WMM) {
+ if ((!params->set || FULL_AP_CLIENT_STATE_SUPP(drv->capa.flags)) &&
+ (params->flags & WPA_STA_WMM)) {
struct nlattr *wme = nla_nest_start(msg, NL80211_ATTR_STA_WME);
wpa_printf(MSG_DEBUG, " * qosinfo=0x%x", params->qosinfo);
@@ -5189,8 +5306,8 @@ retry:
params->ssid == NULL || params->ssid_len > sizeof(drv->ssid))
goto fail;
- wpa_hexdump_ascii(MSG_DEBUG, " * SSID",
- params->ssid, params->ssid_len);
+ wpa_printf(MSG_DEBUG, " * SSID=%s",
+ wpa_ssid_txt(params->ssid, params->ssid_len));
if (nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid))
goto fail;
os_memcpy(drv->ssid, params->ssid, params->ssid_len);
@@ -5351,8 +5468,8 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
}
if (params->ssid) {
- wpa_hexdump_ascii(MSG_DEBUG, " * SSID",
- params->ssid, params->ssid_len);
+ wpa_printf(MSG_DEBUG, " * SSID=%s",
+ wpa_ssid_txt(params->ssid, params->ssid_len));
if (nla_put(msg, NL80211_ATTR_SSID, params->ssid_len,
params->ssid))
return -1;
@@ -5410,8 +5527,11 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
params->key_mgmt_suite == WPA_KEY_MGMT_OSEN ||
params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||
params->key_mgmt_suite == WPA_KEY_MGMT_PSK_SHA256 ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_SAE ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_FT_SAE ||
params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B ||
params->key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192 ||
+ params->key_mgmt_suite == WPA_KEY_MGMT_FT_IEEE8021X_SHA384 ||
params->key_mgmt_suite == WPA_KEY_MGMT_FILS_SHA256 ||
params->key_mgmt_suite == WPA_KEY_MGMT_FILS_SHA384 ||
params->key_mgmt_suite == WPA_KEY_MGMT_FT_FILS_SHA256 ||
@@ -5442,12 +5562,21 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
case WPA_KEY_MGMT_OSEN:
mgmt = RSN_AUTH_KEY_MGMT_OSEN;
break;
+ case WPA_KEY_MGMT_SAE:
+ mgmt = RSN_AUTH_KEY_MGMT_SAE;
+ break;
+ case WPA_KEY_MGMT_FT_SAE:
+ mgmt = RSN_AUTH_KEY_MGMT_FT_SAE;
+ break;
case WPA_KEY_MGMT_IEEE8021X_SUITE_B:
mgmt = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B;
break;
case WPA_KEY_MGMT_IEEE8021X_SUITE_B_192:
mgmt = RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192;
break;
+ case WPA_KEY_MGMT_FT_IEEE8021X_SHA384:
+ mgmt = RSN_AUTH_KEY_MGMT_FT_802_1X_SHA384;
+ break;
case WPA_KEY_MGMT_FILS_SHA256:
mgmt = RSN_AUTH_KEY_MGMT_FILS_SHA256;
break;
@@ -5476,9 +5605,16 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
return -1;
}
+ if (params->req_key_mgmt_offload &&
+ (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X)) {
+ wpa_printf(MSG_DEBUG, " * WANT_1X_4WAY_HS");
+ if (nla_put_flag(msg, NL80211_ATTR_WANT_1X_4WAY_HS))
+ return -1;
+ }
+
/* Add PSK in case of 4-way handshake offload */
if (params->psk &&
- (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE)) {
+ (drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK)) {
wpa_hexdump_key(MSG_DEBUG, " * PSK", params->psk, 32);
if (nla_put(msg, NL80211_ATTR_PMK, 32, params->psk))
return -1;
@@ -5609,9 +5745,10 @@ skip_auth_type:
goto fail;
if (nl_connect)
- ret = send_and_recv(drv->global, nl_connect, msg, NULL, NULL);
+ ret = send_and_recv(drv->global, nl_connect, msg,
+ NULL, (void *) -1);
else
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, NULL, (void *) -1);
msg = NULL;
if (ret) {
@@ -5623,6 +5760,7 @@ skip_auth_type:
}
fail:
+ nl80211_nlmsg_clear(msg);
nlmsg_free(msg);
return ret;
@@ -6023,6 +6161,7 @@ static int get_key_handler(struct nl_msg *msg, void *arg)
if (tb[NL80211_ATTR_KEY_SEQ])
memcpy(arg, nla_data(tb[NL80211_ATTR_KEY_SEQ]),
min_int(nla_len(tb[NL80211_ATTR_KEY_SEQ]), 6));
+ nl80211_nlmsg_clear(msg);
return NL_SKIP;
}
@@ -6057,7 +6196,7 @@ static int i802_set_rts(void *priv, int rts)
int ret;
u32 val;
- if (rts >= 2347)
+ if (rts >= 2347 || rts == -1)
val = (u32) -1;
else
val = rts;
@@ -6085,7 +6224,7 @@ static int i802_set_frag(void *priv, int frag)
int ret;
u32 val;
- if (frag >= 2346)
+ if (frag >= 2346 || frag == -1)
val = (u32) -1;
else
val = frag;
@@ -6646,9 +6785,12 @@ static int i802_set_wds_sta(void *priv, const u8 *addr, int aid, int val,
}
return i802_set_sta_vlan(priv, addr, name, 0);
} else {
- if (bridge_ifname)
- linux_br_del_if(drv->global->ioctl_sock, bridge_ifname,
- name);
+ if (bridge_ifname &&
+ linux_br_del_if(drv->global->ioctl_sock, bridge_ifname,
+ name) < 0)
+ wpa_printf(MSG_INFO,
+ "nl80211: Failed to remove interface %s from bridge %s: %s",
+ name, bridge_ifname, strerror(errno));
i802_set_sta_vlan(priv, addr, bss->ifname, 0);
nl80211_remove_iface(drv, if_nametoindex(name));
@@ -7815,13 +7957,15 @@ static int nl80211_pmkid(struct i802_bss *bss, int cmd,
(params->fils_cache_id &&
nla_put(msg, NL80211_ATTR_FILS_CACHE_ID, 2,
params->fils_cache_id)) ||
- (params->pmk_len && params->pmk_len <= PMK_MAX_LEN &&
+ (cmd != NL80211_CMD_DEL_PMKSA &&
+ params->pmk_len && params->pmk_len <= PMK_MAX_LEN &&
nla_put(msg, NL80211_ATTR_PMK, params->pmk_len, params->pmk))) {
+ nl80211_nlmsg_clear(msg);
nlmsg_free(msg);
return -ENOBUFS;
}
- return send_and_recv_msgs(bss->drv, msg, NULL, NULL);
+ return send_and_recv_msgs(bss->drv, msg, NULL, (void *) -1);
}
@@ -8144,6 +8288,7 @@ static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr,
struct i802_bss *bss = priv;
struct wpa_driver_nl80211_data *drv = bss->drv;
struct nl_msg *msg;
+ u64 cookie;
int ret;
if (!drv->poll_command_supported) {
@@ -8157,11 +8302,16 @@ static void nl80211_poll_client(void *priv, const u8 *own_addr, const u8 *addr,
return;
}
- ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ ret = send_and_recv_msgs(drv, msg, cookie_handler, &cookie);
if (ret < 0) {
wpa_printf(MSG_DEBUG, "nl80211: Client probe request for "
MACSTR " failed: ret=%d (%s)",
MAC2STR(addr), ret, strerror(-ret));
+ } else {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Client probe request addr=" MACSTR
+ " cookie=%llu", MAC2STR(addr),
+ (long long unsigned int) cookie);
}
}
@@ -8593,6 +8743,8 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen)
struct wpa_driver_nl80211_data *drv = bss->drv;
int res;
char *pos, *end;
+ struct nl_msg *msg;
+ char alpha2[3] = { 0, 0, 0 };
pos = buf;
end = buf + buflen;
@@ -8737,6 +8889,23 @@ static int wpa_driver_nl80211_status(void *priv, char *buf, size_t buflen)
pos += res;
}
+ msg = nlmsg_alloc();
+ if (msg &&
+ nl80211_cmd(drv, msg, 0, NL80211_CMD_GET_REG) &&
+ nla_put_u32(msg, NL80211_ATTR_WIPHY, drv->wiphy_idx) == 0) {
+ if (send_and_recv_msgs(drv, msg, nl80211_get_country,
+ alpha2) == 0 &&
+ alpha2[0]) {
+ res = os_snprintf(pos, end - pos, "country=%s\n",
+ alpha2);
+ if (os_snprintf_error(end - pos, res))
+ return pos - buf;
+ pos += res;
+ }
+ } else {
+ nlmsg_free(msg);
+ }
+
return pos - buf;
}
@@ -9303,8 +9472,8 @@ static int nl80211_put_mesh_id(struct nl_msg *msg, const u8 *mesh_id,
size_t mesh_id_len)
{
if (mesh_id) {
- wpa_hexdump_ascii(MSG_DEBUG, " * Mesh ID (SSID)",
- mesh_id, mesh_id_len);
+ wpa_printf(MSG_DEBUG, " * Mesh ID (SSID)=%s",
+ wpa_ssid_txt(mesh_id, mesh_id_len));
return nla_put(msg, NL80211_ATTR_MESH_ID, mesh_id_len, mesh_id);
}
@@ -10645,15 +10814,27 @@ static int nl80211_send_external_auth_status(void *priv,
struct nl_msg *msg = NULL;
int ret = -1;
+ /* External auth command/status is intended for drivers that implement
+ * intenral SME but want to offload authentication processing (e.g.,
+ * SAE) to hostapd/wpa_supplicant. Do nott send the status to drivers
+ * which do not support AP SME or use wpa_supplicant/hostapd SME.
+ */
+ if (!bss->drv->device_ap_sme ||
+ (drv->capa.flags & WPA_DRIVER_FLAGS_SME))
+ return -1;
+
wpa_dbg(drv->ctx, MSG_DEBUG,
"nl80211: External auth status: %u", params->status);
msg = nl80211_drv_msg(drv, 0, NL80211_CMD_EXTERNAL_AUTH);
if (!msg ||
nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, params->status) ||
- nla_put(msg, NL80211_ATTR_SSID, params->ssid_len,
- params->ssid) ||
- nla_put(msg, NL80211_ATTR_BSSID, ETH_ALEN, params->bssid))
+ (params->ssid && params->ssid_len &&
+ nla_put(msg, NL80211_ATTR_SSID, params->ssid_len, params->ssid)) ||
+ (params->pmkid &&
+ nla_put(msg, NL80211_ATTR_PMKID, PMKID_LEN, params->pmkid)) ||
+ (params->bssid &&
+ nla_put(msg, NL80211_ATTR_BSSID, ETH_ALEN, params->bssid)))
goto fail;
ret = send_and_recv_msgs(drv, msg, NULL, NULL);
msg = NULL;
@@ -10669,6 +10850,49 @@ fail:
}
+static int nl80211_set_4addr_mode(void *priv, const char *bridge_ifname,
+ int val)
+{
+ struct i802_bss *bss = priv;
+ struct wpa_driver_nl80211_data *drv = bss->drv;
+ struct nl_msg *msg;
+ int ret = -ENOBUFS;
+
+ wpa_printf(MSG_DEBUG, "nl80211: %s 4addr mode (bridge_ifname: %s)",
+ val ? "Enable" : "Disable", bridge_ifname);
+
+ msg = nl80211_cmd_msg(drv->first_bss, 0, NL80211_CMD_SET_INTERFACE);
+ if (!msg || nla_put_u8(msg, NL80211_ATTR_4ADDR, val))
+ goto fail;
+
+ if (bridge_ifname[0] && bss->added_if_into_bridge && !val) {
+ if (linux_br_del_if(drv->global->ioctl_sock,
+ bridge_ifname, bss->ifname)) {
+ wpa_printf(MSG_ERROR,
+ "nl80211: Failed to remove interface %s from bridge %s",
+ bss->ifname, bridge_ifname);
+ return -1;
+ }
+ bss->added_if_into_bridge = 0;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, NULL, NULL);
+ msg = NULL;
+ if (!ret) {
+ if (bridge_ifname[0] && val &&
+ i802_check_bridge(drv, bss, bridge_ifname, bss->ifname) < 0)
+ return -1;
+ return 0;
+ }
+
+fail:
+ nlmsg_free(msg);
+ wpa_printf(MSG_ERROR, "nl80211: Failed to enable/disable 4addr");
+
+ return ret;
+}
+
+
const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.name = "nl80211",
.desc = "Linux nl80211/cfg80211",
@@ -10728,6 +10952,7 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.resume = wpa_driver_nl80211_resume,
.signal_monitor = nl80211_signal_monitor,
.signal_poll = nl80211_signal_poll,
+ .channel_info = nl80211_channel_info,
.send_frame = nl80211_send_frame,
.set_param = nl80211_set_param,
.get_radio_name = nl80211_get_radio_name,
@@ -10797,4 +11022,5 @@ const struct wpa_driver_ops wpa_driver_nl80211_ops = {
.get_ext_capab = nl80211_get_ext_capab,
.update_connect_params = nl80211_update_connection_params,
.send_external_auth_status = nl80211_send_external_auth_status,
+ .set_4addr_mode = nl80211_set_4addr_mode,
};
diff --git a/src/drivers/driver_nl80211.h b/src/drivers/driver_nl80211.h
index bc562ba7a8cca..1e7fe7a98fff3 100644
--- a/src/drivers/driver_nl80211.h
+++ b/src/drivers/driver_nl80211.h
@@ -166,6 +166,7 @@ struct wpa_driver_nl80211_data {
unsigned int he_capab_vendor_cmd_avail:1;
unsigned int fetch_bss_trans_status:1;
unsigned int roam_vendor_cmd_avail:1;
+ unsigned int get_supported_akm_suites_avail:1;
u64 vendor_scan_cookie;
u64 remain_on_chan_cookie;
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index 7b360d209aba2..37eeb5e6686d3 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -403,10 +403,11 @@ static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info,
capa->flags |= WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD;
if (ext_feature_isset(ext_features, len,
- NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK) &&
- ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_PSK))
+ capa->flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK;
+ if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_4WAY_HANDSHAKE_STA_1X))
- capa->flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE;
+ capa->flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X;
if (ext_feature_isset(ext_features, len,
NL80211_EXT_FEATURE_MFP_OPTIONAL))
@@ -428,6 +429,10 @@ static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info,
NL80211_EXT_FEATURE_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION))
capa->flags |= WPA_DRIVER_FLAGS_OCE_STA;
#endif /* CONFIG_MBO */
+
+ if (ext_feature_isset(ext_features, len,
+ NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER))
+ capa->flags |= WPA_DRIVER_FLAGS_FTM_RESPONDER;
}
@@ -782,6 +787,9 @@ static int wiphy_info_handler(struct nl_msg *msg, void *arg)
case QCA_NL80211_VENDOR_SUBCMD_ROAM:
drv->roam_vendor_cmd_avail = 1;
break;
+ case QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_AKMS:
+ drv->get_supported_akm_suites_avail = 1;
+ break;
#endif /* CONFIG_DRIVER_NL80211_QCA */
}
}
@@ -954,6 +962,126 @@ static void qca_nl80211_check_dfs_capa(struct wpa_driver_nl80211_data *drv)
}
+static unsigned int get_akm_suites_info(struct nlattr *tb)
+{
+ int i, num;
+ unsigned int key_mgmt = 0;
+ u32 *akms;
+
+ if (!tb)
+ return 0;
+
+ num = nla_len(tb) / sizeof(u32);
+ akms = nla_data(tb);
+ for (i = 0; i < num; i++) {
+ u32 a = akms[i];
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Supported AKM %02x-%02x-%02x:%u",
+ a >> 24, (a >> 16) & 0xff,
+ (a >> 8) & 0xff, a & 0xff);
+ switch (a) {
+ case RSN_AUTH_KEY_MGMT_UNSPEC_802_1X:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2;
+ break;
+ case RSN_AUTH_KEY_MGMT_PSK_OVER_802_1X:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_WPA_PSK |
+ WPA_DRIVER_CAPA_KEY_MGMT_WPA2_PSK;
+ break;
+ case RSN_AUTH_KEY_MGMT_FT_802_1X:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT;
+ break;
+ case RSN_AUTH_KEY_MGMT_FT_PSK:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_PSK;
+ break;
+ case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B;
+ break;
+ case RSN_AUTH_KEY_MGMT_802_1X_SUITE_B_192:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SUITE_B_192;
+ break;
+ case RSN_AUTH_KEY_MGMT_OWE:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_OWE;
+ break;
+ case RSN_AUTH_KEY_MGMT_DPP:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_DPP;
+ break;
+ case RSN_AUTH_KEY_MGMT_FILS_SHA256:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256;
+ break;
+ case RSN_AUTH_KEY_MGMT_FILS_SHA384:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384;
+ break;
+ case RSN_AUTH_KEY_MGMT_FT_FILS_SHA256:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256;
+ break;
+ case RSN_AUTH_KEY_MGMT_FT_FILS_SHA384:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384;
+ break;
+ case RSN_AUTH_KEY_MGMT_SAE:
+ key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_SAE;
+ break;
+ }
+ }
+
+ return key_mgmt;
+}
+
+
+static int get_akm_suites_handler(struct nl_msg *msg, void *arg)
+{
+ struct nlattr *tb[NL80211_ATTR_MAX + 1];
+ struct genlmsghdr *gnlh = nlmsg_data(nlmsg_hdr(msg));
+ unsigned int *key_mgmt = arg;
+
+ nla_parse(tb, NL80211_ATTR_MAX, genlmsg_attrdata(gnlh, 0),
+ genlmsg_attrlen(gnlh, 0), NULL);
+
+ if (tb[NL80211_ATTR_VENDOR_DATA]) {
+ struct nlattr *nl_vend = tb[NL80211_ATTR_VENDOR_DATA];
+ struct nlattr *tb_data[NL80211_ATTR_MAX + 1];
+
+ nla_parse(tb_data, NL80211_ATTR_MAX,
+ nla_data(nl_vend), nla_len(nl_vend), NULL);
+
+ *key_mgmt =
+ get_akm_suites_info(tb_data[NL80211_ATTR_AKM_SUITES]);
+ }
+
+ return NL_SKIP;
+}
+
+
+static int qca_nl80211_get_akm_suites(struct wpa_driver_nl80211_data *drv)
+{
+ struct nl_msg *msg;
+ unsigned int key_mgmt = 0;
+ int ret;
+
+ if (!drv->get_supported_akm_suites_avail)
+ return -1;
+
+ if (!(msg = nl80211_drv_msg(drv, 0, NL80211_CMD_VENDOR)) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_ID, OUI_QCA) ||
+ nla_put_u32(msg, NL80211_ATTR_VENDOR_SUBCMD,
+ QCA_NL80211_VENDOR_SUBCMD_GET_SUPPORTED_AKMS)) {
+ nlmsg_free(msg);
+ return -1;
+ }
+
+ ret = send_and_recv_msgs(drv, msg, get_akm_suites_handler, &key_mgmt);
+ if (!ret) {
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Replace capa.key_mgmt based on driver advertised capabilities: 0x%x",
+ key_mgmt);
+ drv->capa.key_mgmt = key_mgmt;
+ }
+
+ return ret;
+}
+
+
static int qca_nl80211_he_capab_handler(struct nl_msg *msg, void *arg)
{
struct nlattr *tb[NL80211_ATTR_MAX + 1];
@@ -1179,11 +1307,18 @@ int wpa_driver_nl80211_capa(struct wpa_driver_nl80211_data *drv)
drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 |
WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384 |
WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA256 |
- WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384;
+ WPA_DRIVER_CAPA_KEY_MGMT_FT_FILS_SHA384 |
+ WPA_DRIVER_CAPA_KEY_MGMT_SAE;
else if (drv->capa.flags & WPA_DRIVER_FLAGS_FILS_SK_OFFLOAD)
drv->capa.key_mgmt |= WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA256 |
WPA_DRIVER_CAPA_KEY_MGMT_FILS_SHA384;
+#ifdef CONFIG_DRIVER_NL80211_QCA
+ /* Override drv->capa.key_mgmt based on driver advertised capability
+ * constraints, if available. */
+ qca_nl80211_get_akm_suites(drv);
+#endif /* CONFIG_DRIVER_NL80211_QCA */
+
drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
WPA_DRIVER_AUTH_SHARED |
WPA_DRIVER_AUTH_LEAP;
@@ -1307,6 +1442,7 @@ static void phy_info_freq(struct hostapd_hw_modes *mode,
u8 channel;
chan->freq = nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_FREQ]);
chan->flag = 0;
+ chan->allowed_bw = ~0;
chan->dfs_cac_ms = 0;
if (ieee80211_freq_to_chan(chan->freq, &channel) != NUM_HOSTAPD_MODES)
chan->chan = channel;
@@ -1322,6 +1458,19 @@ static void phy_info_freq(struct hostapd_hw_modes *mode,
if (tb_freq[NL80211_FREQUENCY_ATTR_GO_CONCURRENT])
chan->flag |= HOSTAPD_CHAN_GO_CONCURRENT;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_NO_10MHZ])
+ chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_10;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_NO_20MHZ])
+ chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_20;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_NO_HT40_PLUS])
+ chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_40P;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_NO_HT40_MINUS])
+ chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_40M;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_NO_80MHZ])
+ chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_80;
+ if (tb_freq[NL80211_FREQUENCY_ATTR_NO_160MHZ])
+ chan->allowed_bw &= ~HOSTAPD_CHAN_WIDTH_160;
+
if (tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]) {
enum nl80211_dfs_state state =
nla_get_u32(tb_freq[NL80211_FREQUENCY_ATTR_DFS_STATE]);
@@ -1356,6 +1505,12 @@ static int phy_info_freqs(struct phy_info_arg *phy_info,
[NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG },
[NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 },
[NL80211_FREQUENCY_ATTR_DFS_STATE] = { .type = NLA_U32 },
+ [NL80211_FREQUENCY_ATTR_NO_10MHZ] = { .type = NLA_FLAG },
+ [NL80211_FREQUENCY_ATTR_NO_20MHZ] = { .type = NLA_FLAG },
+ [NL80211_FREQUENCY_ATTR_NO_HT40_PLUS] = { .type = NLA_FLAG },
+ [NL80211_FREQUENCY_ATTR_NO_HT40_MINUS] = { .type = NLA_FLAG },
+ [NL80211_FREQUENCY_ATTR_NO_80MHZ] = { .type = NLA_FLAG },
+ [NL80211_FREQUENCY_ATTR_NO_160MHZ] = { .type = NLA_FLAG },
};
int new_channels = 0;
struct hostapd_channel_data *channel;
@@ -1932,6 +2087,61 @@ static int nl80211_set_regulatory_flags(struct wpa_driver_nl80211_data *drv,
}
+static const char * modestr(enum hostapd_hw_mode mode)
+{
+ switch (mode) {
+ case HOSTAPD_MODE_IEEE80211B:
+ return "802.11b";
+ case HOSTAPD_MODE_IEEE80211G:
+ return "802.11g";
+ case HOSTAPD_MODE_IEEE80211A:
+ return "802.11a";
+ case HOSTAPD_MODE_IEEE80211AD:
+ return "802.11ad";
+ default:
+ return "?";
+ }
+}
+
+
+static void nl80211_dump_chan_list(struct hostapd_hw_modes *modes,
+ u16 num_modes)
+{
+ int i;
+
+ if (!modes)
+ return;
+
+ for (i = 0; i < num_modes; i++) {
+ struct hostapd_hw_modes *mode = &modes[i];
+ char str[200];
+ char *pos = str;
+ char *end = pos + sizeof(str);
+ int j, res;
+
+ for (j = 0; j < mode->num_channels; j++) {
+ struct hostapd_channel_data *chan = &mode->channels[j];
+
+ res = os_snprintf(pos, end - pos, " %d%s%s%s",
+ chan->freq,
+ (chan->flag & HOSTAPD_CHAN_DISABLED) ?
+ "[DISABLED]" : "",
+ (chan->flag & HOSTAPD_CHAN_NO_IR) ?
+ "[NO_IR]" : "",
+ (chan->flag & HOSTAPD_CHAN_RADAR) ?
+ "[RADAR]" : "");
+ if (os_snprintf_error(end - pos, res))
+ break;
+ pos += res;
+ }
+
+ *pos = '\0';
+ wpa_printf(MSG_DEBUG, "nl80211: Mode IEEE %s:%s",
+ modestr(mode->mode), str);
+ }
+}
+
+
struct hostapd_hw_modes *
nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags,
u8 *dfs_domain)
@@ -1963,6 +2173,8 @@ nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags,
}
if (send_and_recv_msgs(drv, msg, phy_info_handler, &result) == 0) {
+ struct hostapd_hw_modes *modes;
+
nl80211_set_regulatory_flags(drv, &result);
if (result.failed) {
int i;
@@ -1978,8 +2190,10 @@ nl80211_get_hw_feature_data(void *priv, u16 *num_modes, u16 *flags,
*dfs_domain = result.dfs_domain;
- return wpa_driver_nl80211_postprocess_modes(result.modes,
- num_modes);
+ modes = wpa_driver_nl80211_postprocess_modes(result.modes,
+ num_modes);
+ nl80211_dump_chan_list(modes, *num_modes);
+ return modes;
}
return NULL;
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index 205b4cd4b0829..ee7b4da38d870 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -206,7 +206,8 @@ static void nl80211_parse_wmm_params(struct nlattr *wmm_attr,
static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
- const u8 *frame, size_t len, struct nlattr *wmm)
+ const u8 *frame, size_t len, struct nlattr *wmm,
+ struct nlattr *req_ie)
{
const struct ieee80211_mgmt *mgmt;
union wpa_event_data event;
@@ -261,7 +262,10 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
len - 24 - sizeof(mgmt->u.assoc_resp);
}
- event.assoc_info.freq = drv->assoc_freq;
+ if (req_ie) {
+ event.assoc_info.req_ies = nla_data(req_ie);
+ event.assoc_info.req_ies_len = nla_len(req_ie);
+ }
/* When this association was initiated outside of wpa_supplicant,
* drv->ssid needs to be set here to satisfy later checking. */
@@ -273,6 +277,9 @@ static void mlme_event_assoc(struct wpa_driver_nl80211_data *drv,
wpa_ssid_txt(drv->ssid, drv->ssid_len));
}
+ event.assoc_info.freq = drv->assoc_freq;
+ drv->first_bss->freq = drv->assoc_freq;
+
nl80211_parse_wmm_params(wmm, &event.assoc_info.wmm_params);
wpa_supplicant_event(drv->ctx, EVENT_ASSOC, &event);
@@ -402,6 +409,7 @@ static void mlme_event_connect(struct wpa_driver_nl80211_data *drv,
}
event.assoc_info.freq = nl80211_get_assoc_freq(drv);
+ drv->first_bss->freq = drv->assoc_freq;
if ((!ssid || ssid[1] == 0 || ssid[1] > 32) &&
(ssid_len = nl80211_get_assoc_ssid(drv, drv->ssid)) > 0) {
@@ -868,7 +876,7 @@ static void mlme_event(struct i802_bss *bss,
struct nlattr *addr, struct nlattr *timed_out,
struct nlattr *freq, struct nlattr *ack,
struct nlattr *cookie, struct nlattr *sig,
- struct nlattr *wmm)
+ struct nlattr *wmm, struct nlattr *req_ie)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
const u8 *data;
@@ -917,7 +925,8 @@ static void mlme_event(struct i802_bss *bss,
mlme_event_auth(drv, nla_data(frame), nla_len(frame));
break;
case NL80211_CMD_ASSOCIATE:
- mlme_event_assoc(drv, nla_data(frame), nla_len(frame), wmm);
+ mlme_event_assoc(drv, nla_data(frame), nla_len(frame), wmm,
+ req_ie);
break;
case NL80211_CMD_DEAUTHENTICATE:
mlme_event_deauth_disassoc(drv, EVENT_DEAUTH,
@@ -1429,16 +1438,23 @@ static void nl80211_client_probe_event(struct wpa_driver_nl80211_data *drv,
struct nlattr **tb)
{
union wpa_event_data data;
+ const u8 *addr;
+ u64 cookie = 0;
- wpa_printf(MSG_DEBUG, "nl80211: Probe client event");
-
- if (!tb[NL80211_ATTR_MAC] || !tb[NL80211_ATTR_ACK])
+ addr = nla_data(tb[NL80211_ATTR_MAC]);
+ if (!addr)
+ return;
+ if (tb[NL80211_ATTR_COOKIE])
+ cookie = nla_get_u64(tb[NL80211_ATTR_COOKIE]);
+ wpa_printf(MSG_DEBUG, "nl80211: Probe client event (addr=" MACSTR
+ " ack=%d cookie=%llu)", MAC2STR(addr),
+ tb[NL80211_ATTR_ACK] != NULL,
+ (long long unsigned int) cookie);
+ if (!tb[NL80211_ATTR_ACK])
return;
os_memset(&data, 0, sizeof(data));
- os_memcpy(data.client_poll.addr,
- nla_data(tb[NL80211_ATTR_MAC]), ETH_ALEN);
-
+ os_memcpy(data.client_poll.addr, addr, ETH_ALEN);
wpa_supplicant_event(drv->ctx, EVENT_DRIVER_CLIENT_POLL_OK, &data);
}
@@ -2183,6 +2199,54 @@ static void nl80211_reg_change_event(struct wpa_driver_nl80211_data *drv,
}
+static void nl80211_dump_freq(const char *title, struct nlattr *nl_freq)
+{
+ static struct nla_policy freq_policy[NL80211_FREQUENCY_ATTR_MAX + 1] = {
+ [NL80211_FREQUENCY_ATTR_FREQ] = { .type = NLA_U32 },
+ [NL80211_FREQUENCY_ATTR_DISABLED] = { .type = NLA_FLAG },
+ [NL80211_FREQUENCY_ATTR_NO_IR] = { .type = NLA_FLAG },
+ [NL80211_FREQUENCY_ATTR_RADAR] = { .type = NLA_FLAG },
+ [NL80211_FREQUENCY_ATTR_MAX_TX_POWER] = { .type = NLA_U32 },
+ };
+ struct nlattr *tb[NL80211_FREQUENCY_ATTR_MAX + 1];
+ u32 freq = 0, max_tx_power = 0;
+
+ nla_parse(tb, NL80211_FREQUENCY_ATTR_MAX,
+ nla_data(nl_freq), nla_len(nl_freq), freq_policy);
+
+ if (tb[NL80211_FREQUENCY_ATTR_FREQ])
+ freq = nla_get_u32(tb[NL80211_FREQUENCY_ATTR_FREQ]);
+ if (tb[NL80211_FREQUENCY_ATTR_MAX_TX_POWER])
+ max_tx_power =
+ nla_get_u32(tb[NL80211_FREQUENCY_ATTR_MAX_TX_POWER]);
+
+ wpa_printf(MSG_DEBUG,
+ "nl80211: Channel (%s): freq=%u max_tx_power=%u%s%s%s",
+ title, freq, max_tx_power,
+ tb[NL80211_FREQUENCY_ATTR_DISABLED] ? " disabled" : "",
+ tb[NL80211_FREQUENCY_ATTR_NO_IR] ? " no-IR" : "",
+ tb[NL80211_FREQUENCY_ATTR_RADAR] ? " radar" : "");
+}
+
+
+static void nl80211_reg_beacon_hint_event(struct wpa_driver_nl80211_data *drv,
+ struct nlattr *tb[])
+{
+ union wpa_event_data data;
+
+ wpa_printf(MSG_DEBUG, "nl80211: Regulatory beacon hint");
+ os_memset(&data, 0, sizeof(data));
+ data.channel_list_changed.initiator = REGDOM_BEACON_HINT;
+
+ if (tb[NL80211_ATTR_FREQ_BEFORE])
+ nl80211_dump_freq("before", tb[NL80211_ATTR_FREQ_BEFORE]);
+ if (tb[NL80211_ATTR_FREQ_AFTER])
+ nl80211_dump_freq("after", tb[NL80211_ATTR_FREQ_AFTER]);
+
+ wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED, &data);
+}
+
+
static void nl80211_external_auth(struct wpa_driver_nl80211_data *drv,
struct nlattr **tb)
{
@@ -2214,11 +2278,9 @@ static void nl80211_external_auth(struct wpa_driver_nl80211_data *drv,
event.external_auth.ssid_len = nla_len(tb[NL80211_ATTR_SSID]);
if (event.external_auth.ssid_len > SSID_MAX_LEN)
return;
- os_memcpy(event.external_auth.ssid, nla_data(tb[NL80211_ATTR_SSID]),
- event.external_auth.ssid_len);
+ event.external_auth.ssid = nla_data(tb[NL80211_ATTR_SSID]);
- os_memcpy(event.external_auth.bssid, nla_data(tb[NL80211_ATTR_BSSID]),
- ETH_ALEN);
+ event.external_auth.bssid = nla_data(tb[NL80211_ATTR_BSSID]);
wpa_printf(MSG_DEBUG,
"nl80211: External auth action: %u, AKM: 0x%x",
@@ -2327,7 +2389,6 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
struct nlattr **tb)
{
struct wpa_driver_nl80211_data *drv = bss->drv;
- union wpa_event_data data;
int external_scan_event = 0;
wpa_printf(MSG_DEBUG, "nl80211: Drv Event %d (%s) received for %s",
@@ -2428,7 +2489,8 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK],
tb[NL80211_ATTR_COOKIE],
tb[NL80211_ATTR_RX_SIGNAL_DBM],
- tb[NL80211_ATTR_STA_WME]);
+ tb[NL80211_ATTR_STA_WME],
+ tb[NL80211_ATTR_REQ_IE]);
break;
case NL80211_CMD_CONNECT:
case NL80211_CMD_ROAM:
@@ -2480,11 +2542,7 @@ static void do_process_drv_event(struct i802_bss *bss, int cmd,
nl80211_reg_change_event(drv, tb);
break;
case NL80211_CMD_REG_BEACON_HINT:
- wpa_printf(MSG_DEBUG, "nl80211: Regulatory beacon hint");
- os_memset(&data, 0, sizeof(data));
- data.channel_list_changed.initiator = REGDOM_BEACON_HINT;
- wpa_supplicant_event(drv->ctx, EVENT_CHANNEL_LIST_CHANGED,
- &data);
+ nl80211_reg_beacon_hint_event(drv, tb);
break;
case NL80211_CMD_NEW_STATION:
nl80211_new_station_event(drv, bss, tb);
@@ -2605,7 +2663,7 @@ int process_bss_event(struct nl_msg *msg, void *arg)
tb[NL80211_ATTR_WIPHY_FREQ], tb[NL80211_ATTR_ACK],
tb[NL80211_ATTR_COOKIE],
tb[NL80211_ATTR_RX_SIGNAL_DBM],
- tb[NL80211_ATTR_STA_WME]);
+ tb[NL80211_ATTR_STA_WME], NULL);
break;
case NL80211_CMD_UNEXPECTED_FRAME:
nl80211_spurious_frame(bss, tb, 0);
diff --git a/src/drivers/driver_nl80211_scan.c b/src/drivers/driver_nl80211_scan.c
index 33a8d359848fb..9afa5b304cf87 100644
--- a/src/drivers/driver_nl80211_scan.c
+++ b/src/drivers/driver_nl80211_scan.c
@@ -197,9 +197,9 @@ nl80211_scan_common(struct i802_bss *bss, u8 cmd,
if (ssids == NULL)
goto fail;
for (i = 0; i < params->num_ssids; i++) {
- wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID",
- params->ssids[i].ssid,
- params->ssids[i].ssid_len);
+ wpa_printf(MSG_MSGDUMP, "nl80211: Scan SSID %s",
+ wpa_ssid_txt(params->ssids[i].ssid,
+ params->ssids[i].ssid_len));
if (nla_put(msg, i + 1, params->ssids[i].ssid_len,
params->ssids[i].ssid))
goto fail;
@@ -537,10 +537,10 @@ int wpa_driver_nl80211_sched_scan(void *priv,
for (i = 0; i < drv->num_filter_ssids; i++) {
struct nlattr *match_set_ssid;
- wpa_hexdump_ascii(MSG_MSGDUMP,
- "nl80211: Sched scan filter SSID",
- drv->filter_ssids[i].ssid,
- drv->filter_ssids[i].ssid_len);
+ wpa_printf(MSG_MSGDUMP,
+ "nl80211: Sched scan filter SSID %s",
+ wpa_ssid_txt(drv->filter_ssids[i].ssid,
+ drv->filter_ssids[i].ssid_len));
match_set_ssid = nla_nest_start(msg, i + 1);
if (match_set_ssid == NULL ||
@@ -1098,9 +1098,9 @@ int wpa_driver_nl80211_vendor_scan(struct i802_bss *bss,
if (ssids == NULL)
goto fail;
for (i = 0; i < params->num_ssids; i++) {
- wpa_hexdump_ascii(MSG_MSGDUMP, "nl80211: Scan SSID",
- params->ssids[i].ssid,
- params->ssids[i].ssid_len);
+ wpa_printf(MSG_MSGDUMP, "nl80211: Scan SSID %s",
+ wpa_ssid_txt(params->ssids[i].ssid,
+ params->ssids[i].ssid_len));
if (nla_put(msg, i + 1, params->ssids[i].ssid_len,
params->ssids[i].ssid))
goto fail;
diff --git a/src/drivers/driver_openbsd.c b/src/drivers/driver_openbsd.c
index e94eda08f6210..c06e75c0f2841 100644
--- a/src/drivers/driver_openbsd.c
+++ b/src/drivers/driver_openbsd.c
@@ -62,7 +62,8 @@ static int
wpa_driver_openbsd_get_capa(void *priv, struct wpa_driver_capa *capa)
{
os_memset(capa, 0, sizeof(*capa));
- capa->flags = WPA_DRIVER_FLAGS_4WAY_HANDSHAKE;
+ capa->flags = WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK |
+ WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X;
return 0;
}
diff --git a/src/drivers/driver_roboswitch.c b/src/drivers/driver_roboswitch.c
index e8a51354dd0cc..9beb6c46d367c 100644
--- a/src/drivers/driver_roboswitch.c
+++ b/src/drivers/driver_roboswitch.c
@@ -290,21 +290,26 @@ static int wpa_driver_roboswitch_leave(struct wpa_driver_roboswitch_data *drv,
wpa_driver_roboswitch_addr_be16(addr, addr_be16);
- wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_CONF,
- &_read, 1);
+ if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_CONF, &_read, 1) < 0)
+ return -1;
/* If ARL control is disabled, there is nothing to leave. */
if (!(_read & (1 << 4))) return -1;
- wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
- ROBO_ARLCTRL_ADDR_1, addr_read, 3);
- wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE, ROBO_ARLCTRL_VEC_1,
- &ports_read, 1);
+ if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_ADDR_1, addr_read, 3) < 0 ||
+ wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_VEC_1, &ports_read, 1) < 0)
+ return -1;
/* check if we occupy multiport address 1 */
if (os_memcmp(addr_read, addr_be16, 6) == 0 && ports_read == ports) {
- wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
- ROBO_ARLCTRL_ADDR_2, addr_read, 3);
- wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
- ROBO_ARLCTRL_VEC_2, &ports_read, 1);
+ if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_ADDR_2, addr_read,
+ 3) < 0 ||
+ wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_VEC_2, &ports_read,
+ 1) < 0)
+ return -1;
/* and multiport address 2 */
if (os_memcmp(addr_read, addr_be16, 6) == 0 &&
ports_read == ports) {
@@ -327,10 +332,13 @@ static int wpa_driver_roboswitch_leave(struct wpa_driver_roboswitch_data *drv,
&ports_read, 1);
}
} else {
- wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
- ROBO_ARLCTRL_ADDR_2, addr_read, 3);
- wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
- ROBO_ARLCTRL_VEC_2, &ports_read, 1);
+ if (wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_ADDR_2, addr_read,
+ 3) < 0 ||
+ wpa_driver_roboswitch_read(drv, ROBO_ARLCTRL_PAGE,
+ ROBO_ARLCTRL_VEC_2, &ports_read,
+ 1) < 0)
+ return -1;
/* or multiport address 2 */
if (os_memcmp(addr_read, addr_be16, 6) == 0 &&
ports_read == ports) {
diff --git a/src/drivers/driver_wext.c b/src/drivers/driver_wext.c
index 20abaab4cd84c..f7755cccde27b 100644
--- a/src/drivers/driver_wext.c
+++ b/src/drivers/driver_wext.c
@@ -868,14 +868,16 @@ static int wext_hostap_ifname(struct wpa_driver_wext_data *drv,
const char *ifname)
{
char buf[200], *res;
- int type;
+ int type, ret;
FILE *f;
if (strcmp(ifname, ".") == 0 || strcmp(ifname, "..") == 0)
return -1;
- snprintf(buf, sizeof(buf), "/sys/class/net/%s/device/net/%s/type",
- drv->ifname, ifname);
+ ret = snprintf(buf, sizeof(buf), "/sys/class/net/%s/device/net/%s/type",
+ drv->ifname, ifname);
+ if (os_snprintf_error(sizeof(buf), ret))
+ return -1;
f = fopen(buf, "r");
if (!f)
@@ -1645,7 +1647,8 @@ static int wpa_driver_wext_get_range(void *priv)
if (range->enc_capa & IW_ENC_CAPA_CIPHER_CCMP)
drv->capa.enc |= WPA_DRIVER_CAPA_ENC_CCMP;
if (range->enc_capa & IW_ENC_CAPA_4WAY_HANDSHAKE)
- drv->capa.flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE;
+ drv->capa.flags |= WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_PSK |
+ WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X;
drv->capa.auth = WPA_DRIVER_AUTH_OPEN |
WPA_DRIVER_AUTH_SHARED |
WPA_DRIVER_AUTH_LEAP;
@@ -1676,7 +1679,7 @@ static int wpa_driver_wext_set_psk(struct wpa_driver_wext_data *drv,
wpa_printf(MSG_DEBUG, "%s", __FUNCTION__);
- if (!(drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE))
+ if (!(drv->capa.flags & WPA_DRIVER_FLAGS_4WAY_HANDSHAKE_8021X))
return 0;
if (!psk)
diff --git a/src/drivers/drivers.mak b/src/drivers/drivers.mak
index 1496b47d0ad50..442c59cf4a5f6 100644
--- a/src/drivers/drivers.mak
+++ b/src/drivers/drivers.mak
@@ -22,6 +22,7 @@ ifdef CONFIG_DRIVER_MACSEC_LINUX
DRV_CFLAGS += -DCONFIG_DRIVER_MACSEC_LINUX
DRV_OBJS += ../src/drivers/driver_macsec_linux.o
NEED_DRV_WIRED_COMMON=1
+NEED_LIBNL=y
CONFIG_LIBNL3_ROUTE=y
endif
@@ -51,37 +52,7 @@ NEED_NETLINK=y
NEED_LINUX_IOCTL=y
NEED_RFKILL=y
NEED_RADIOTAP=y
-
-ifdef CONFIG_LIBNL32
- DRV_LIBS += -lnl-3
- DRV_LIBS += -lnl-genl-3
- DRV_CFLAGS += -DCONFIG_LIBNL20
- ifdef LIBNL_INC
- DRV_CFLAGS += -I$(LIBNL_INC)
- else
- PKG_CONFIG ?= pkg-config
- DRV_CFLAGS += $(shell $(PKG_CONFIG) --cflags libnl-3.0)
- endif
-ifdef CONFIG_LIBNL3_ROUTE
- DRV_LIBS += -lnl-route-3
- DRV_CFLAGS += -DCONFIG_LIBNL3_ROUTE
-endif
-else
- ifdef CONFIG_LIBNL_TINY
- DRV_LIBS += -lnl-tiny
- else
- ifndef CONFIG_OSX
- DRV_LIBS += -lnl
- endif
- endif
-
- ifdef CONFIG_LIBNL20
- ifndef CONFIG_LIBNL_TINY
- DRV_LIBS += -lnl-genl
- endif
- DRV_CFLAGS += -DCONFIG_LIBNL20
- endif
-endif
+NEED_LIBNL=y
endif
ifdef CONFIG_DRIVER_BSD
@@ -183,26 +154,55 @@ endif
ifdef CONFIG_VLAN_NETLINK
ifdef CONFIG_FULL_DYNAMIC_VLAN
+NEED_LIBNL=y
+CONFIG_LIBNL3_ROUTE=y
+endif
+endif
+
+ifdef NEED_LIBNL
+ifndef CONFIG_LIBNL32
+ifndef CONFIG_LIBNL20
+ifndef CONFIG_LIBNL_TINY
+PKG_CONFIG ?= pkg-config
+HAVE_LIBNL3 := $(shell $(PKG_CONFIG) --exists libnl-3.0; echo $$?)
+ifeq ($(HAVE_LIBNL3),0)
+CONFIG_LIBNL32=y
+endif
+endif
+endif
+endif
+
ifdef CONFIG_LIBNL32
DRV_LIBS += -lnl-3
DRV_LIBS += -lnl-genl-3
- DRV_LIBS += -lnl-route-3
DRV_CFLAGS += -DCONFIG_LIBNL20
+ ifdef LIBNL_INC
+ DRV_CFLAGS += -I$(LIBNL_INC)
+ else
+ PKG_CONFIG ?= pkg-config
+ DRV_CFLAGS += $(shell $(PKG_CONFIG) --cflags libnl-3.0)
+ endif
+ ifdef CONFIG_LIBNL3_ROUTE
+ DRV_LIBS += -lnl-route-3
+ DRV_CFLAGS += -DCONFIG_LIBNL3_ROUTE
+ endif
else
ifdef CONFIG_LIBNL_TINY
DRV_LIBS += -lnl-tiny
else
- DRV_LIBS += -lnl
+ ifndef CONFIG_OSX
+ DRV_LIBS += -lnl
+ endif
endif
ifdef CONFIG_LIBNL20
- DRV_LIBS += -lnl-genl
- DRV_LIBS += -lnl-route
+ ifndef CONFIG_LIBNL_TINY
+ DRV_LIBS += -lnl-genl
+ endif
DRV_CFLAGS += -DCONFIG_LIBNL20
endif
endif
endif
-endif
##### COMMON VARS
DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS)
diff --git a/src/drivers/drivers.mk b/src/drivers/drivers.mk
index cd25133af8088..599a0b5794fe7 100644
--- a/src/drivers/drivers.mk
+++ b/src/drivers/drivers.mk
@@ -23,6 +23,7 @@ DRV_CFLAGS += -DCONFIG_DRIVER_MACSEC_LINUX
DRV_OBJS += src/drivers/driver_macsec_linux.c
NEED_DRV_WIRED_COMMON=1
CONFIG_LIBNL3_ROUTE=y
+NEED_LIBNL=y
endif
ifdef NEED_DRV_WIRED_COMMON
@@ -46,29 +47,7 @@ NEED_NETLINK=y
NEED_LINUX_IOCTL=y
NEED_RFKILL=y
NEED_RADIOTAP=y
-
-ifdef CONFIG_LIBNL32
- DRV_LIBS += -lnl-3
- DRV_LIBS += -lnl-genl-3
- DRV_CFLAGS += -DCONFIG_LIBNL20 -I/usr/include/libnl3
-ifdef CONFIG_LIBNL3_ROUTE
- DRV_LIBS += -lnl-route-3
- DRV_CFLAGS += -DCONFIG_LIBNL3_ROUTE
-endif
-else
- ifdef CONFIG_LIBNL_TINY
- DRV_LIBS += -lnl-tiny
- else
- DRV_LIBS += -lnl
- endif
-
- ifdef CONFIG_LIBNL20
- ifndef CONFIG_LIBNL_TINY
- DRV_LIBS += -lnl-genl
- endif
- DRV_CFLAGS += -DCONFIG_LIBNL20
- endif
-endif
+NEED_LIBNL=y
endif
ifdef CONFIG_DRIVER_BSD
@@ -171,11 +150,20 @@ endif
ifdef CONFIG_VLAN_NETLINK
ifdef CONFIG_FULL_DYNAMIC_VLAN
+NEED_LIBNL=y
+CONFIG_LIBNL3_ROUTE=y
+endif
+endif
+
+ifdef NEED_LIBNL
ifdef CONFIG_LIBNL32
DRV_LIBS += -lnl-3
DRV_LIBS += -lnl-genl-3
+ DRV_CFLAGS += -DCONFIG_LIBNL20 -I/usr/include/libnl3
+ifdef CONFIG_LIBNL3_ROUTE
DRV_LIBS += -lnl-route-3
- DRV_CFLAGS += -DCONFIG_LIBNL20
+ DRV_CFLAGS += -DCONFIG_LIBNL3_ROUTE
+endif
else
ifdef CONFIG_LIBNL_TINY
DRV_LIBS += -lnl-tiny
@@ -184,13 +172,13 @@ else
endif
ifdef CONFIG_LIBNL20
- DRV_LIBS += -lnl-genl
- DRV_LIBS += -lnl-route
+ ifndef CONFIG_LIBNL_TINY
+ DRV_LIBS += -lnl-genl
+ endif
DRV_CFLAGS += -DCONFIG_LIBNL20
endif
endif
endif
-endif
##### COMMON VARS
DRV_BOTH_CFLAGS := $(DRV_CFLAGS) $(DRV_WPA_CFLAGS) $(DRV_AP_CFLAGS)
diff --git a/src/drivers/linux_ioctl.c b/src/drivers/linux_ioctl.c
index e21147af1bcd6..7edb9df2edd49 100644
--- a/src/drivers/linux_ioctl.c
+++ b/src/drivers/linux_ioctl.c
@@ -12,6 +12,7 @@
#include <net/if_arp.h>
#include "utils/common.h"
+#include "common/linux_bridge.h"
#include "linux_ioctl.h"
@@ -119,25 +120,14 @@ int linux_set_ifhwaddr(int sock, const char *ifname, const u8 *addr)
}
-#ifndef SIOCBRADDBR
-#define SIOCBRADDBR 0x89a0
-#endif
-#ifndef SIOCBRDELBR
-#define SIOCBRDELBR 0x89a1
-#endif
-#ifndef SIOCBRADDIF
-#define SIOCBRADDIF 0x89a2
-#endif
-#ifndef SIOCBRDELIF
-#define SIOCBRDELIF 0x89a3
-#endif
-
-
int linux_br_add(int sock, const char *brname)
{
if (ioctl(sock, SIOCBRADDBR, brname) < 0) {
+ int saved_errno = errno;
+
wpa_printf(MSG_DEBUG, "Could not add bridge %s: %s",
brname, strerror(errno));
+ errno = saved_errno;
return -1;
}
@@ -170,8 +160,11 @@ int linux_br_add_if(int sock, const char *brname, const char *ifname)
os_strlcpy(ifr.ifr_name, brname, IFNAMSIZ);
ifr.ifr_ifindex = ifindex;
if (ioctl(sock, SIOCBRADDIF, &ifr) < 0) {
+ int saved_errno = errno;
+
wpa_printf(MSG_DEBUG, "Could not add interface %s into bridge "
"%s: %s", ifname, brname, strerror(errno));
+ errno = saved_errno;
return -1;
}
diff --git a/src/drivers/nl80211_copy.h b/src/drivers/nl80211_copy.h
index 1766a12b231c7..dd4f86ee286ec 100644
--- a/src/drivers/nl80211_copy.h
+++ b/src/drivers/nl80211_copy.h
@@ -1033,6 +1033,38 @@
* %NL80211_ATTR_CHANNEL_WIDTH,%NL80211_ATTR_NSS attributes with its
* address(specified in %NL80211_ATTR_MAC).
*
+ * @NL80211_CMD_GET_FTM_RESPONDER_STATS: Retrieve FTM responder statistics, in
+ * the %NL80211_ATTR_FTM_RESPONDER_STATS attribute.
+ *
+ * @NL80211_CMD_PEER_MEASUREMENT_START: start a (set of) peer measurement(s)
+ * with the given parameters, which are encapsulated in the nested
+ * %NL80211_ATTR_PEER_MEASUREMENTS attribute. Optionally, MAC address
+ * randomization may be enabled and configured by specifying the
+ * %NL80211_ATTR_MAC and %NL80211_ATTR_MAC_MASK attributes.
+ * If a timeout is requested, use the %NL80211_ATTR_TIMEOUT attribute.
+ * A u64 cookie for further %NL80211_ATTR_COOKIE use is is returned in
+ * the netlink extended ack message.
+ *
+ * To cancel a measurement, close the socket that requested it.
+ *
+ * Measurement results are reported to the socket that requested the
+ * measurement using @NL80211_CMD_PEER_MEASUREMENT_RESULT when they
+ * become available, so applications must ensure a large enough socket
+ * buffer size.
+ *
+ * Depending on driver support it may or may not be possible to start
+ * multiple concurrent measurements.
+ * @NL80211_CMD_PEER_MEASUREMENT_RESULT: This command number is used for the
+ * result notification from the driver to the requesting socket.
+ * @NL80211_CMD_PEER_MEASUREMENT_COMPLETE: Notification only, indicating that
+ * the measurement completed, using the measurement cookie
+ * (%NL80211_ATTR_COOKIE).
+ *
+ * @NL80211_CMD_NOTIFY_RADAR: Notify the kernel that a radar signal was
+ * detected and reported by a neighboring device on the channel
+ * indicated by %NL80211_ATTR_WIPHY_FREQ and other attributes
+ * determining the width and type.
+ *
* @NL80211_CMD_MAX: highest used command number
* @__NL80211_CMD_AFTER_LAST: internal use
*/
@@ -1245,6 +1277,14 @@ enum nl80211_commands {
NL80211_CMD_CONTROL_PORT_FRAME,
+ NL80211_CMD_GET_FTM_RESPONDER_STATS,
+
+ NL80211_CMD_PEER_MEASUREMENT_START,
+ NL80211_CMD_PEER_MEASUREMENT_RESULT,
+ NL80211_CMD_PEER_MEASUREMENT_COMPLETE,
+
+ NL80211_CMD_NOTIFY_RADAR,
+
/* add new commands above here */
/* used to define NL80211_CMD_MAX below */
@@ -1525,6 +1565,12 @@ enum nl80211_commands {
* (a u32 with flags from &enum nl80211_wpa_versions).
* @NL80211_ATTR_AKM_SUITES: Used with CONNECT, ASSOCIATE, and NEW_BEACON to
* indicate which key management algorithm(s) to use (an array of u32).
+ * This attribute is also sent in response to @NL80211_CMD_GET_WIPHY,
+ * indicating the supported AKM suites, intended for specific drivers which
+ * implement SME and have constraints on which AKMs are supported and also
+ * the cases where an AKM support is offloaded to the driver/firmware.
+ * If there is no such notification from the driver, user space should
+ * assume the driver supports all the AKM suites.
*
* @NL80211_ATTR_REQ_IE: (Re)association request information elements as
* sent out by the card, for ROAM and successful CONNECT events.
@@ -1701,7 +1747,7 @@ enum nl80211_commands {
* the values passed in @NL80211_ATTR_SCAN_SSIDS (eg. if an SSID
* is included in the probe request, but the match attributes
* will never let it go through), -EINVAL may be returned.
- * If ommited, no filtering is done.
+ * If omitted, no filtering is done.
*
* @NL80211_ATTR_INTERFACE_COMBINATIONS: Nested attribute listing the supported
* interface combinations. In each nested item, it contains attributes
@@ -1806,7 +1852,7 @@ enum nl80211_commands {
*
* @NL80211_ATTR_INACTIVITY_TIMEOUT: timeout value in seconds, this can be
* used by the drivers which has MLME in firmware and does not have support
- * to report per station tx/rx activity to free up the staion entry from
+ * to report per station tx/rx activity to free up the station entry from
* the list. This needs to be used when the driver advertises the
* capability to timeout the stations.
*
@@ -2167,7 +2213,7 @@ enum nl80211_commands {
*
* @NL80211_ATTR_SCHED_SCAN_RSSI_ADJUST: When present the RSSI level for BSSs in
* the specified band is to be adjusted before doing
- * %NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI based comparision to figure out
+ * %NL80211_ATTR_SCHED_SCAN_RELATIVE_RSSI based comparison to figure out
* better BSSs. The attribute value is a packed structure
* value as specified by &struct nl80211_bss_select_rssi_adjust.
*
@@ -2220,10 +2266,10 @@ enum nl80211_commands {
* &enum nl80211_external_auth_action value). This is used with the
* %NL80211_CMD_EXTERNAL_AUTH request event.
* @NL80211_ATTR_EXTERNAL_AUTH_SUPPORT: Flag attribute indicating that the user
- * space supports external authentication. This attribute shall be used
- * only with %NL80211_CMD_CONNECT request. The driver may offload
- * authentication processing to user space if this capability is indicated
- * in NL80211_CMD_CONNECT requests from the user space.
+ * space supports external authentication. This attribute shall be used
+ * with %NL80211_CMD_CONNECT and %NL80211_CMD_START_AP request. The driver
+ * may offload authentication processing to user space if this capability
+ * is indicated in the respective requests from the user space.
*
* @NL80211_ATTR_NSS: Station's New/updated RX_NSS value notified using this
* u8 attribute. This is used with %NL80211_CMD_STA_OPMODE_CHANGED.
@@ -2241,6 +2287,27 @@ enum nl80211_commands {
* association request when used with NL80211_CMD_NEW_STATION). Can be set
* only if %NL80211_STA_FLAG_WME is set.
*
+ * @NL80211_ATTR_FTM_RESPONDER: nested attribute which user-space can include
+ * in %NL80211_CMD_START_AP or %NL80211_CMD_SET_BEACON for fine timing
+ * measurement (FTM) responder functionality and containing parameters as
+ * possible, see &enum nl80211_ftm_responder_attr
+ *
+ * @NL80211_ATTR_FTM_RESPONDER_STATS: Nested attribute with FTM responder
+ * statistics, see &enum nl80211_ftm_responder_stats.
+ *
+ * @NL80211_ATTR_TIMEOUT: Timeout for the given operation in milliseconds (u32),
+ * if the attribute is not given no timeout is requested. Note that 0 is an
+ * invalid value.
+ *
+ * @NL80211_ATTR_PEER_MEASUREMENTS: peer measurements request (and result)
+ * data, uses nested attributes specified in
+ * &enum nl80211_peer_measurement_attrs.
+ * This is also used for capability advertisement in the wiphy information,
+ * with the appropriate sub-attributes.
+ *
+ * @NL80211_ATTR_AIRTIME_WEIGHT: Station's weight when scheduled by the airtime
+ * scheduler.
+ *
* @NUM_NL80211_ATTR: total number of nl80211_attrs available
* @NL80211_ATTR_MAX: highest attribute number currently defined
* @__NL80211_ATTR_AFTER_LAST: internal use
@@ -2682,6 +2749,16 @@ enum nl80211_attrs {
NL80211_ATTR_HE_CAPABILITY,
+ NL80211_ATTR_FTM_RESPONDER,
+
+ NL80211_ATTR_FTM_RESPONDER_STATS,
+
+ NL80211_ATTR_TIMEOUT,
+
+ NL80211_ATTR_PEER_MEASUREMENTS,
+
+ NL80211_ATTR_AIRTIME_WEIGHT,
+
/* add attributes here, update the policy in nl80211.c */
__NL80211_ATTR_AFTER_LAST,
@@ -3051,6 +3128,17 @@ enum nl80211_sta_bss_param {
* @NL80211_STA_INFO_PAD: attribute used for padding for 64-bit alignment
* @NL80211_STA_INFO_ACK_SIGNAL: signal strength of the last ACK frame(u8, dBm)
* @NL80211_STA_INFO_ACK_SIGNAL_AVG: avg signal strength of ACK frames (s8, dBm)
+ * @NL80211_STA_INFO_RX_MPDUS: total number of received packets (MPDUs)
+ * (u32, from this station)
+ * @NL80211_STA_INFO_FCS_ERROR_COUNT: total number of packets (MPDUs) received
+ * with an FCS error (u32, from this station). This count may not include
+ * some packets with an FCS error due to TA corruption. Hence this counter
+ * might not be fully accurate.
+ * @NL80211_STA_INFO_CONNECTED_TO_GATE: set to true if STA has a path to a
+ * mesh gate (u8, 0 or 1)
+ * @NL80211_STA_INFO_TX_DURATION: aggregate PPDU duration for all frames
+ * sent to the station (u64, usec)
+ * @NL80211_STA_INFO_AIRTIME_WEIGHT: current airtime weight for station (u16)
* @__NL80211_STA_INFO_AFTER_LAST: internal
* @NL80211_STA_INFO_MAX: highest possible station info attribute
*/
@@ -3091,6 +3179,11 @@ enum nl80211_sta_info {
NL80211_STA_INFO_PAD,
NL80211_STA_INFO_ACK_SIGNAL,
NL80211_STA_INFO_ACK_SIGNAL_AVG,
+ NL80211_STA_INFO_RX_MPDUS,
+ NL80211_STA_INFO_FCS_ERROR_COUNT,
+ NL80211_STA_INFO_CONNECTED_TO_GATE,
+ NL80211_STA_INFO_TX_DURATION,
+ NL80211_STA_INFO_AIRTIME_WEIGHT,
/* keep last */
__NL80211_STA_INFO_AFTER_LAST,
@@ -3200,8 +3293,10 @@ enum nl80211_mpath_flags {
* &enum nl80211_mpath_flags;
* @NL80211_MPATH_INFO_DISCOVERY_TIMEOUT: total path discovery timeout, in msec
* @NL80211_MPATH_INFO_DISCOVERY_RETRIES: mesh path discovery retries
+ * @NL80211_MPATH_INFO_HOP_COUNT: hop count to destination
+ * @NL80211_MPATH_INFO_PATH_CHANGE: total number of path changes to destination
* @NL80211_MPATH_INFO_MAX: highest mesh path information attribute number
- * currently defind
+ * currently defined
* @__NL80211_MPATH_INFO_AFTER_LAST: internal use
*/
enum nl80211_mpath_info {
@@ -3213,6 +3308,8 @@ enum nl80211_mpath_info {
NL80211_MPATH_INFO_FLAGS,
NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
NL80211_MPATH_INFO_DISCOVERY_RETRIES,
+ NL80211_MPATH_INFO_HOP_COUNT,
+ NL80211_MPATH_INFO_PATH_CHANGE,
/* keep last */
__NL80211_MPATH_INFO_AFTER_LAST,
@@ -3870,6 +3967,11 @@ enum nl80211_mesh_power_mode {
* remove it from the STA's list of peers. You may set this to 0 to disable
* the removal of the STA. Default is 30 minutes.
*
+ * @NL80211_MESHCONF_CONNECTED_TO_GATE: If set to true then this mesh STA
+ * will advertise that it is connected to a gate in the mesh formation
+ * field. If left unset then the mesh formation field will only
+ * advertise such if there is an active root mesh path.
+ *
* @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use
*/
enum nl80211_meshconf_params {
@@ -3902,6 +4004,7 @@ enum nl80211_meshconf_params {
NL80211_MESHCONF_POWER_MODE,
NL80211_MESHCONF_AWAKE_WINDOW,
NL80211_MESHCONF_PLINK_TIMEOUT,
+ NL80211_MESHCONF_CONNECTED_TO_GATE,
/* keep last */
__NL80211_MESHCONF_ATTR_AFTER_LAST,
@@ -4834,7 +4937,7 @@ enum nl80211_iface_limit_attrs {
* numbers = [ #{STA} <= 1, #{P2P-client,P2P-GO} <= 3 ], max = 4
* => allows a STA plus three P2P interfaces
*
- * The list of these four possiblities could completely be contained
+ * The list of these four possibilities could completely be contained
* within the %NL80211_ATTR_INTERFACE_COMBINATIONS attribute to indicate
* that any of these groups must match.
*
@@ -4864,7 +4967,7 @@ enum nl80211_if_combination_attrs {
* enum nl80211_plink_state - state of a mesh peer link finite state machine
*
* @NL80211_PLINK_LISTEN: initial state, considered the implicit
- * state of non existant mesh peer links
+ * state of non existent mesh peer links
* @NL80211_PLINK_OPN_SNT: mesh plink open frame has been sent to
* this mesh peer
* @NL80211_PLINK_OPN_RCVD: mesh plink open frame has been received
@@ -5225,6 +5328,20 @@ enum nl80211_feature_flags {
* @NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT: Driver/device can omit all data
* except for supported rates from the probe request content if requested
* by the %NL80211_SCAN_FLAG_MIN_PREQ_CONTENT flag.
+ * @NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER: Driver supports enabling fine
+ * timing measurement responder role.
+ *
+ * @NL80211_EXT_FEATURE_CAN_REPLACE_PTK0: Driver/device confirm that they are
+ * able to rekey an in-use key correctly. Userspace must not rekey PTK keys
+ * if this flag is not set. Ignoring this can leak clear text packets and/or
+ * freeze the connection.
+ *
+ * @NL80211_EXT_FEATURE_AIRTIME_FAIRNESS: Driver supports getting airtime
+ * fairness for transmitted packets and has enabled airtime fairness
+ * scheduling.
+ *
+ * @NL80211_EXT_FEATURE_AP_PMKSA_CACHING: Driver/device supports PMKSA caching
+ * (set/del PMKSA operations) in AP mode.
*
* @NUM_NL80211_EXT_FEATURES: number of extended features.
* @MAX_NL80211_EXT_FEATURES: highest extended feature index.
@@ -5263,6 +5380,10 @@ enum nl80211_ext_feature_index {
NL80211_EXT_FEATURE_TXQS,
NL80211_EXT_FEATURE_SCAN_RANDOM_SN,
NL80211_EXT_FEATURE_SCAN_MIN_PREQ_CONTENT,
+ NL80211_EXT_FEATURE_CAN_REPLACE_PTK0,
+ NL80211_EXT_FEATURE_ENABLE_FTM_RESPONDER,
+ NL80211_EXT_FEATURE_AIRTIME_FAIRNESS,
+ NL80211_EXT_FEATURE_AP_PMKSA_CACHING,
/* add new features before the definition below */
NUM_NL80211_EXT_FEATURES,
@@ -5347,7 +5468,7 @@ enum nl80211_timeout_reason {
* request parameters IE in the probe request
* @NL80211_SCAN_FLAG_ACCEPT_BCAST_PROBE_RESP: accept broadcast probe responses
* @NL80211_SCAN_FLAG_OCE_PROBE_REQ_HIGH_TX_RATE: send probe request frames at
- * rate of at least 5.5M. In case non OCE AP is dicovered in the channel,
+ * rate of at least 5.5M. In case non OCE AP is discovered in the channel,
* only the first probe req in the channel will be sent in high rate.
* @NL80211_SCAN_FLAG_OCE_PROBE_REQ_DEFERRAL_SUPPRESSION: allow probe request
* tx deferral (dot11FILSProbeDelay shall be set to 15ms)
@@ -5514,9 +5635,14 @@ enum nl80211_crit_proto_id {
* Used by cfg80211_rx_mgmt()
*
* @NL80211_RXMGMT_FLAG_ANSWERED: frame was answered by device/driver.
+ * @NL80211_RXMGMT_FLAG_EXTERNAL_AUTH: Host driver intends to offload
+ * the authentication. Exclusively defined for host drivers that
+ * advertises the SME functionality but would like the userspace
+ * to handle certain authentication algorithms (e.g. SAE).
*/
enum nl80211_rxmgmt_flags {
NL80211_RXMGMT_FLAG_ANSWERED = 1 << 0,
+ NL80211_RXMGMT_FLAG_EXTERNAL_AUTH = 1 << 1,
};
/*
@@ -5802,4 +5928,458 @@ enum nl80211_external_auth_action {
NL80211_EXTERNAL_AUTH_ABORT,
};
+/**
+ * enum nl80211_ftm_responder_attributes - fine timing measurement
+ * responder attributes
+ * @__NL80211_FTM_RESP_ATTR_INVALID: Invalid
+ * @NL80211_FTM_RESP_ATTR_ENABLED: FTM responder is enabled
+ * @NL80211_FTM_RESP_ATTR_LCI: The content of Measurement Report Element
+ * (9.4.2.22 in 802.11-2016) with type 8 - LCI (9.4.2.22.10),
+ * i.e. starting with the measurement token
+ * @NL80211_FTM_RESP_ATTR_CIVIC: The content of Measurement Report Element
+ * (9.4.2.22 in 802.11-2016) with type 11 - Civic (Section 9.4.2.22.13),
+ * i.e. starting with the measurement token
+ * @__NL80211_FTM_RESP_ATTR_LAST: Internal
+ * @NL80211_FTM_RESP_ATTR_MAX: highest FTM responder attribute.
+ */
+enum nl80211_ftm_responder_attributes {
+ __NL80211_FTM_RESP_ATTR_INVALID,
+
+ NL80211_FTM_RESP_ATTR_ENABLED,
+ NL80211_FTM_RESP_ATTR_LCI,
+ NL80211_FTM_RESP_ATTR_CIVICLOC,
+
+ /* keep last */
+ __NL80211_FTM_RESP_ATTR_LAST,
+ NL80211_FTM_RESP_ATTR_MAX = __NL80211_FTM_RESP_ATTR_LAST - 1,
+};
+
+/*
+ * enum nl80211_ftm_responder_stats - FTM responder statistics
+ *
+ * These attribute types are used with %NL80211_ATTR_FTM_RESPONDER_STATS
+ * when getting FTM responder statistics.
+ *
+ * @__NL80211_FTM_STATS_INVALID: attribute number 0 is reserved
+ * @NL80211_FTM_STATS_SUCCESS_NUM: number of FTM sessions in which all frames
+ * were ssfully answered (u32)
+ * @NL80211_FTM_STATS_PARTIAL_NUM: number of FTM sessions in which part of the
+ * frames were successfully answered (u32)
+ * @NL80211_FTM_STATS_FAILED_NUM: number of failed FTM sessions (u32)
+ * @NL80211_FTM_STATS_ASAP_NUM: number of ASAP sessions (u32)
+ * @NL80211_FTM_STATS_NON_ASAP_NUM: number of non-ASAP sessions (u32)
+ * @NL80211_FTM_STATS_TOTAL_DURATION_MSEC: total sessions durations - gives an
+ * indication of how much time the responder was busy (u64, msec)
+ * @NL80211_FTM_STATS_UNKNOWN_TRIGGERS_NUM: number of unknown FTM triggers -
+ * triggers from initiators that didn't finish successfully the negotiation
+ * phase with the responder (u32)
+ * @NL80211_FTM_STATS_RESCHEDULE_REQUESTS_NUM: number of FTM reschedule requests
+ * - initiator asks for a new scheduling although it already has scheduled
+ * FTM slot (u32)
+ * @NL80211_FTM_STATS_OUT_OF_WINDOW_TRIGGERS_NUM: number of FTM triggers out of
+ * scheduled window (u32)
+ * @NL80211_FTM_STATS_PAD: used for padding, ignore
+ * @__NL80211_TXQ_ATTR_AFTER_LAST: Internal
+ * @NL80211_FTM_STATS_MAX: highest possible FTM responder stats attribute
+ */
+enum nl80211_ftm_responder_stats {
+ __NL80211_FTM_STATS_INVALID,
+ NL80211_FTM_STATS_SUCCESS_NUM,
+ NL80211_FTM_STATS_PARTIAL_NUM,
+ NL80211_FTM_STATS_FAILED_NUM,
+ NL80211_FTM_STATS_ASAP_NUM,
+ NL80211_FTM_STATS_NON_ASAP_NUM,
+ NL80211_FTM_STATS_TOTAL_DURATION_MSEC,
+ NL80211_FTM_STATS_UNKNOWN_TRIGGERS_NUM,
+ NL80211_FTM_STATS_RESCHEDULE_REQUESTS_NUM,
+ NL80211_FTM_STATS_OUT_OF_WINDOW_TRIGGERS_NUM,
+ NL80211_FTM_STATS_PAD,
+
+ /* keep last */
+ __NL80211_FTM_STATS_AFTER_LAST,
+ NL80211_FTM_STATS_MAX = __NL80211_FTM_STATS_AFTER_LAST - 1
+};
+
+/**
+ * enum nl80211_preamble - frame preamble types
+ * @NL80211_PREAMBLE_LEGACY: legacy (HR/DSSS, OFDM, ERP PHY) preamble
+ * @NL80211_PREAMBLE_HT: HT preamble
+ * @NL80211_PREAMBLE_VHT: VHT preamble
+ * @NL80211_PREAMBLE_DMG: DMG preamble
+ */
+enum nl80211_preamble {
+ NL80211_PREAMBLE_LEGACY,
+ NL80211_PREAMBLE_HT,
+ NL80211_PREAMBLE_VHT,
+ NL80211_PREAMBLE_DMG,
+};
+
+/**
+ * enum nl80211_peer_measurement_type - peer measurement types
+ * @NL80211_PMSR_TYPE_INVALID: invalid/unused, needed as we use
+ * these numbers also for attributes
+ *
+ * @NL80211_PMSR_TYPE_FTM: flight time measurement
+ *
+ * @NUM_NL80211_PMSR_TYPES: internal
+ * @NL80211_PMSR_TYPE_MAX: highest type number
+ */
+enum nl80211_peer_measurement_type {
+ NL80211_PMSR_TYPE_INVALID,
+
+ NL80211_PMSR_TYPE_FTM,
+
+ NUM_NL80211_PMSR_TYPES,
+ NL80211_PMSR_TYPE_MAX = NUM_NL80211_PMSR_TYPES - 1
+};
+
+/**
+ * enum nl80211_peer_measurement_status - peer measurement status
+ * @NL80211_PMSR_STATUS_SUCCESS: measurement completed successfully
+ * @NL80211_PMSR_STATUS_REFUSED: measurement was locally refused
+ * @NL80211_PMSR_STATUS_TIMEOUT: measurement timed out
+ * @NL80211_PMSR_STATUS_FAILURE: measurement failed, a type-dependent
+ * reason may be available in the response data
+ */
+enum nl80211_peer_measurement_status {
+ NL80211_PMSR_STATUS_SUCCESS,
+ NL80211_PMSR_STATUS_REFUSED,
+ NL80211_PMSR_STATUS_TIMEOUT,
+ NL80211_PMSR_STATUS_FAILURE,
+};
+
+/**
+ * enum nl80211_peer_measurement_req - peer measurement request attributes
+ * @__NL80211_PMSR_REQ_ATTR_INVALID: invalid
+ *
+ * @NL80211_PMSR_REQ_ATTR_DATA: This is a nested attribute with measurement
+ * type-specific request data inside. The attributes used are from the
+ * enums named nl80211_peer_measurement_<type>_req.
+ * @NL80211_PMSR_REQ_ATTR_GET_AP_TSF: include AP TSF timestamp, if supported
+ * (flag attribute)
+ *
+ * @NUM_NL80211_PMSR_REQ_ATTRS: internal
+ * @NL80211_PMSR_REQ_ATTR_MAX: highest attribute number
+ */
+enum nl80211_peer_measurement_req {
+ __NL80211_PMSR_REQ_ATTR_INVALID,
+
+ NL80211_PMSR_REQ_ATTR_DATA,
+ NL80211_PMSR_REQ_ATTR_GET_AP_TSF,
+
+ /* keep last */
+ NUM_NL80211_PMSR_REQ_ATTRS,
+ NL80211_PMSR_REQ_ATTR_MAX = NUM_NL80211_PMSR_REQ_ATTRS - 1
+};
+
+/**
+ * enum nl80211_peer_measurement_resp - peer measurement response attributes
+ * @__NL80211_PMSR_RESP_ATTR_INVALID: invalid
+ *
+ * @NL80211_PMSR_RESP_ATTR_DATA: This is a nested attribute with measurement
+ * type-specific results inside. The attributes used are from the enums
+ * named nl80211_peer_measurement_<type>_resp.
+ * @NL80211_PMSR_RESP_ATTR_STATUS: u32 value with the measurement status
+ * (using values from &enum nl80211_peer_measurement_status.)
+ * @NL80211_PMSR_RESP_ATTR_HOST_TIME: host time (%CLOCK_BOOTTIME) when the
+ * result was measured; this value is not expected to be accurate to
+ * more than 20ms. (u64, nanoseconds)
+ * @NL80211_PMSR_RESP_ATTR_AP_TSF: TSF of the AP that the interface
+ * doing the measurement is connected to when the result was measured.
+ * This shall be accurately reported if supported and requested
+ * (u64, usec)
+ * @NL80211_PMSR_RESP_ATTR_FINAL: If results are sent to the host partially
+ * (*e.g. with FTM per-burst data) this flag will be cleared on all but
+ * the last result; if all results are combined it's set on the single
+ * result.
+ * @NL80211_PMSR_RESP_ATTR_PAD: padding for 64-bit attributes, ignore
+ *
+ * @NUM_NL80211_PMSR_RESP_ATTRS: internal
+ * @NL80211_PMSR_RESP_ATTR_MAX: highest attribute number
+ */
+enum nl80211_peer_measurement_resp {
+ __NL80211_PMSR_RESP_ATTR_INVALID,
+
+ NL80211_PMSR_RESP_ATTR_DATA,
+ NL80211_PMSR_RESP_ATTR_STATUS,
+ NL80211_PMSR_RESP_ATTR_HOST_TIME,
+ NL80211_PMSR_RESP_ATTR_AP_TSF,
+ NL80211_PMSR_RESP_ATTR_FINAL,
+ NL80211_PMSR_RESP_ATTR_PAD,
+
+ /* keep last */
+ NUM_NL80211_PMSR_RESP_ATTRS,
+ NL80211_PMSR_RESP_ATTR_MAX = NUM_NL80211_PMSR_RESP_ATTRS - 1
+};
+
+/**
+ * enum nl80211_peer_measurement_peer_attrs - peer attributes for measurement
+ * @__NL80211_PMSR_PEER_ATTR_INVALID: invalid
+ *
+ * @NL80211_PMSR_PEER_ATTR_ADDR: peer's MAC address
+ * @NL80211_PMSR_PEER_ATTR_CHAN: channel definition, nested, using top-level
+ * attributes like %NL80211_ATTR_WIPHY_FREQ etc.
+ * @NL80211_PMSR_PEER_ATTR_REQ: This is a nested attribute indexed by
+ * measurement type, with attributes from the
+ * &enum nl80211_peer_measurement_req inside.
+ * @NL80211_PMSR_PEER_ATTR_RESP: This is a nested attribute indexed by
+ * measurement type, with attributes from the
+ * &enum nl80211_peer_measurement_resp inside.
+ *
+ * @NUM_NL80211_PMSR_PEER_ATTRS: internal
+ * @NL80211_PMSR_PEER_ATTR_MAX: highest attribute number
+ */
+enum nl80211_peer_measurement_peer_attrs {
+ __NL80211_PMSR_PEER_ATTR_INVALID,
+
+ NL80211_PMSR_PEER_ATTR_ADDR,
+ NL80211_PMSR_PEER_ATTR_CHAN,
+ NL80211_PMSR_PEER_ATTR_REQ,
+ NL80211_PMSR_PEER_ATTR_RESP,
+
+ /* keep last */
+ NUM_NL80211_PMSR_PEER_ATTRS,
+ NL80211_PMSR_PEER_ATTR_MAX = NUM_NL80211_PMSR_PEER_ATTRS - 1,
+};
+
+/**
+ * enum nl80211_peer_measurement_attrs - peer measurement attributes
+ * @__NL80211_PMSR_ATTR_INVALID: invalid
+ *
+ * @NL80211_PMSR_ATTR_MAX_PEERS: u32 attribute used for capability
+ * advertisement only, indicates the maximum number of peers
+ * measurements can be done with in a single request
+ * @NL80211_PMSR_ATTR_REPORT_AP_TSF: flag attribute in capability
+ * indicating that the connected AP's TSF can be reported in
+ * measurement results
+ * @NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR: flag attribute in capability
+ * indicating that MAC address randomization is supported.
+ * @NL80211_PMSR_ATTR_TYPE_CAPA: capabilities reported by the device,
+ * this contains a nesting indexed by measurement type, and
+ * type-specific capabilities inside, which are from the enums
+ * named nl80211_peer_measurement_<type>_capa.
+ * @NL80211_PMSR_ATTR_PEERS: nested attribute, the nesting index is
+ * meaningless, just a list of peers to measure with, with the
+ * sub-attributes taken from
+ * &enum nl80211_peer_measurement_peer_attrs.
+ *
+ * @NUM_NL80211_PMSR_ATTR: internal
+ * @NL80211_PMSR_ATTR_MAX: highest attribute number
+ */
+enum nl80211_peer_measurement_attrs {
+ __NL80211_PMSR_ATTR_INVALID,
+
+ NL80211_PMSR_ATTR_MAX_PEERS,
+ NL80211_PMSR_ATTR_REPORT_AP_TSF,
+ NL80211_PMSR_ATTR_RANDOMIZE_MAC_ADDR,
+ NL80211_PMSR_ATTR_TYPE_CAPA,
+ NL80211_PMSR_ATTR_PEERS,
+
+ /* keep last */
+ NUM_NL80211_PMSR_ATTR,
+ NL80211_PMSR_ATTR_MAX = NUM_NL80211_PMSR_ATTR - 1
+};
+
+/**
+ * enum nl80211_peer_measurement_ftm_capa - FTM capabilities
+ * @__NL80211_PMSR_FTM_CAPA_ATTR_INVALID: invalid
+ *
+ * @NL80211_PMSR_FTM_CAPA_ATTR_ASAP: flag attribute indicating ASAP mode
+ * is supported
+ * @NL80211_PMSR_FTM_CAPA_ATTR_NON_ASAP: flag attribute indicating non-ASAP
+ * mode is supported
+ * @NL80211_PMSR_FTM_CAPA_ATTR_REQ_LCI: flag attribute indicating if LCI
+ * data can be requested during the measurement
+ * @NL80211_PMSR_FTM_CAPA_ATTR_REQ_CIVICLOC: flag attribute indicating if civic
+ * location data can be requested during the measurement
+ * @NL80211_PMSR_FTM_CAPA_ATTR_PREAMBLES: u32 bitmap attribute of bits
+ * from &enum nl80211_preamble.
+ * @NL80211_PMSR_FTM_CAPA_ATTR_BANDWIDTHS: bitmap of values from
+ * &enum nl80211_chan_width indicating the supported channel
+ * bandwidths for FTM. Note that a higher channel bandwidth may be
+ * configured to allow for other measurements types with different
+ * bandwidth requirement in the same measurement.
+ * @NL80211_PMSR_FTM_CAPA_ATTR_MAX_BURSTS_EXPONENT: u32 attribute indicating
+ * the maximum bursts exponent that can be used (if not present anything
+ * is valid)
+ * @NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST: u32 attribute indicating
+ * the maximum FTMs per burst (if not present anything is valid)
+ *
+ * @NUM_NL80211_PMSR_FTM_CAPA_ATTR: internal
+ * @NL80211_PMSR_FTM_CAPA_ATTR_MAX: highest attribute number
+ */
+enum nl80211_peer_measurement_ftm_capa {
+ __NL80211_PMSR_FTM_CAPA_ATTR_INVALID,
+
+ NL80211_PMSR_FTM_CAPA_ATTR_ASAP,
+ NL80211_PMSR_FTM_CAPA_ATTR_NON_ASAP,
+ NL80211_PMSR_FTM_CAPA_ATTR_REQ_LCI,
+ NL80211_PMSR_FTM_CAPA_ATTR_REQ_CIVICLOC,
+ NL80211_PMSR_FTM_CAPA_ATTR_PREAMBLES,
+ NL80211_PMSR_FTM_CAPA_ATTR_BANDWIDTHS,
+ NL80211_PMSR_FTM_CAPA_ATTR_MAX_BURSTS_EXPONENT,
+ NL80211_PMSR_FTM_CAPA_ATTR_MAX_FTMS_PER_BURST,
+
+ /* keep last */
+ NUM_NL80211_PMSR_FTM_CAPA_ATTR,
+ NL80211_PMSR_FTM_CAPA_ATTR_MAX = NUM_NL80211_PMSR_FTM_CAPA_ATTR - 1
+};
+
+/**
+ * enum nl80211_peer_measurement_ftm_req - FTM request attributes
+ * @__NL80211_PMSR_FTM_REQ_ATTR_INVALID: invalid
+ *
+ * @NL80211_PMSR_FTM_REQ_ATTR_ASAP: ASAP mode requested (flag)
+ * @NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE: preamble type (see
+ * &enum nl80211_preamble), optional for DMG (u32)
+ * @NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP: number of bursts exponent as in
+ * 802.11-2016 9.4.2.168 "Fine Timing Measurement Parameters element"
+ * (u8, 0-15, optional with default 15 i.e. "no preference")
+ * @NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD: interval between bursts in units
+ * of 100ms (u16, optional with default 0)
+ * @NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION: burst duration, as in 802.11-2016
+ * Table 9-257 "Burst Duration field encoding" (u8, 0-15, optional with
+ * default 15 i.e. "no preference")
+ * @NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST: number of successful FTM frames
+ * requested per burst
+ * (u8, 0-31, optional with default 0 i.e. "no preference")
+ * @NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES: number of FTMR frame retries
+ * (u8, default 3)
+ * @NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI: request LCI data (flag)
+ * @NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC: request civic location data
+ * (flag)
+ *
+ * @NUM_NL80211_PMSR_FTM_REQ_ATTR: internal
+ * @NL80211_PMSR_FTM_REQ_ATTR_MAX: highest attribute number
+ */
+enum nl80211_peer_measurement_ftm_req {
+ __NL80211_PMSR_FTM_REQ_ATTR_INVALID,
+
+ NL80211_PMSR_FTM_REQ_ATTR_ASAP,
+ NL80211_PMSR_FTM_REQ_ATTR_PREAMBLE,
+ NL80211_PMSR_FTM_REQ_ATTR_NUM_BURSTS_EXP,
+ NL80211_PMSR_FTM_REQ_ATTR_BURST_PERIOD,
+ NL80211_PMSR_FTM_REQ_ATTR_BURST_DURATION,
+ NL80211_PMSR_FTM_REQ_ATTR_FTMS_PER_BURST,
+ NL80211_PMSR_FTM_REQ_ATTR_NUM_FTMR_RETRIES,
+ NL80211_PMSR_FTM_REQ_ATTR_REQUEST_LCI,
+ NL80211_PMSR_FTM_REQ_ATTR_REQUEST_CIVICLOC,
+
+ /* keep last */
+ NUM_NL80211_PMSR_FTM_REQ_ATTR,
+ NL80211_PMSR_FTM_REQ_ATTR_MAX = NUM_NL80211_PMSR_FTM_REQ_ATTR - 1
+};
+
+/**
+ * enum nl80211_peer_measurement_ftm_failure_reasons - FTM failure reasons
+ * @NL80211_PMSR_FTM_FAILURE_UNSPECIFIED: unspecified failure, not used
+ * @NL80211_PMSR_FTM_FAILURE_NO_RESPONSE: no response from the FTM responder
+ * @NL80211_PMSR_FTM_FAILURE_REJECTED: FTM responder rejected measurement
+ * @NL80211_PMSR_FTM_FAILURE_WRONG_CHANNEL: we already know the peer is
+ * on a different channel, so can't measure (if we didn't know, we'd
+ * try and get no response)
+ * @NL80211_PMSR_FTM_FAILURE_PEER_NOT_CAPABLE: peer can't actually do FTM
+ * @NL80211_PMSR_FTM_FAILURE_INVALID_TIMESTAMP: invalid T1/T4 timestamps
+ * received
+ * @NL80211_PMSR_FTM_FAILURE_PEER_BUSY: peer reports busy, you may retry
+ * later (see %NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME)
+ * @NL80211_PMSR_FTM_FAILURE_BAD_CHANGED_PARAMS: parameters were changed
+ * by the peer and are no longer supported
+ */
+enum nl80211_peer_measurement_ftm_failure_reasons {
+ NL80211_PMSR_FTM_FAILURE_UNSPECIFIED,
+ NL80211_PMSR_FTM_FAILURE_NO_RESPONSE,
+ NL80211_PMSR_FTM_FAILURE_REJECTED,
+ NL80211_PMSR_FTM_FAILURE_WRONG_CHANNEL,
+ NL80211_PMSR_FTM_FAILURE_PEER_NOT_CAPABLE,
+ NL80211_PMSR_FTM_FAILURE_INVALID_TIMESTAMP,
+ NL80211_PMSR_FTM_FAILURE_PEER_BUSY,
+ NL80211_PMSR_FTM_FAILURE_BAD_CHANGED_PARAMS,
+};
+
+/**
+ * enum nl80211_peer_measurement_ftm_resp - FTM response attributes
+ * @__NL80211_PMSR_FTM_RESP_ATTR_INVALID: invalid
+ *
+ * @NL80211_PMSR_FTM_RESP_ATTR_FAIL_REASON: FTM-specific failure reason
+ * (u32, optional)
+ * @NL80211_PMSR_FTM_RESP_ATTR_BURST_INDEX: optional, if bursts are reported
+ * as separate results then it will be the burst index 0...(N-1) and
+ * the top level will indicate partial results (u32)
+ * @NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_ATTEMPTS: number of FTM Request frames
+ * transmitted (u32, optional)
+ * @NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_SUCCESSES: number of FTM Request frames
+ * that were acknowleged (u32, optional)
+ * @NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME: retry time received from the
+ * busy peer (u32, seconds)
+ * @NL80211_PMSR_FTM_RESP_ATTR_NUM_BURSTS_EXP: actual number of bursts exponent
+ * used by the responder (similar to request, u8)
+ * @NL80211_PMSR_FTM_RESP_ATTR_BURST_DURATION: actual burst duration used by
+ * the responder (similar to request, u8)
+ * @NL80211_PMSR_FTM_RESP_ATTR_FTMS_PER_BURST: actual FTMs per burst used
+ * by the responder (similar to request, u8)
+ * @NL80211_PMSR_FTM_RESP_ATTR_RSSI_AVG: average RSSI across all FTM action
+ * frames (optional, s32, 1/2 dBm)
+ * @NL80211_PMSR_FTM_RESP_ATTR_RSSI_SPREAD: RSSI spread across all FTM action
+ * frames (optional, s32, 1/2 dBm)
+ * @NL80211_PMSR_FTM_RESP_ATTR_TX_RATE: bitrate we used for the response to the
+ * FTM action frame (optional, nested, using &enum nl80211_rate_info
+ * attributes)
+ * @NL80211_PMSR_FTM_RESP_ATTR_RX_RATE: bitrate the responder used for the FTM
+ * action frame (optional, nested, using &enum nl80211_rate_info attrs)
+ * @NL80211_PMSR_FTM_RESP_ATTR_RTT_AVG: average RTT (s64, picoseconds, optional
+ * but one of RTT/DIST must be present)
+ * @NL80211_PMSR_FTM_RESP_ATTR_RTT_VARIANCE: RTT variance (u64, ps^2, note that
+ * standard deviation is the square root of variance, optional)
+ * @NL80211_PMSR_FTM_RESP_ATTR_RTT_SPREAD: RTT spread (u64, picoseconds,
+ * optional)
+ * @NL80211_PMSR_FTM_RESP_ATTR_DIST_AVG: average distance (s64, mm, optional
+ * but one of RTT/DIST must be present)
+ * @NL80211_PMSR_FTM_RESP_ATTR_DIST_VARIANCE: distance variance (u64, mm^2, note
+ * that standard deviation is the square root of variance, optional)
+ * @NL80211_PMSR_FTM_RESP_ATTR_DIST_SPREAD: distance spread (u64, mm, optional)
+ * @NL80211_PMSR_FTM_RESP_ATTR_LCI: LCI data from peer (binary, optional);
+ * this is the contents of the Measurement Report Element (802.11-2016
+ * 9.4.2.22.1) starting with the Measurement Token, with Measurement
+ * Type 8.
+ * @NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC: civic location data from peer
+ * (binary, optional);
+ * this is the contents of the Measurement Report Element (802.11-2016
+ * 9.4.2.22.1) starting with the Measurement Token, with Measurement
+ * Type 11.
+ * @NL80211_PMSR_FTM_RESP_ATTR_PAD: ignore, for u64/s64 padding only
+ *
+ * @NUM_NL80211_PMSR_FTM_RESP_ATTR: internal
+ * @NL80211_PMSR_FTM_RESP_ATTR_MAX: highest attribute number
+ */
+enum nl80211_peer_measurement_ftm_resp {
+ __NL80211_PMSR_FTM_RESP_ATTR_INVALID,
+
+ NL80211_PMSR_FTM_RESP_ATTR_FAIL_REASON,
+ NL80211_PMSR_FTM_RESP_ATTR_BURST_INDEX,
+ NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_ATTEMPTS,
+ NL80211_PMSR_FTM_RESP_ATTR_NUM_FTMR_SUCCESSES,
+ NL80211_PMSR_FTM_RESP_ATTR_BUSY_RETRY_TIME,
+ NL80211_PMSR_FTM_RESP_ATTR_NUM_BURSTS_EXP,
+ NL80211_PMSR_FTM_RESP_ATTR_BURST_DURATION,
+ NL80211_PMSR_FTM_RESP_ATTR_FTMS_PER_BURST,
+ NL80211_PMSR_FTM_RESP_ATTR_RSSI_AVG,
+ NL80211_PMSR_FTM_RESP_ATTR_RSSI_SPREAD,
+ NL80211_PMSR_FTM_RESP_ATTR_TX_RATE,
+ NL80211_PMSR_FTM_RESP_ATTR_RX_RATE,
+ NL80211_PMSR_FTM_RESP_ATTR_RTT_AVG,
+ NL80211_PMSR_FTM_RESP_ATTR_RTT_VARIANCE,
+ NL80211_PMSR_FTM_RESP_ATTR_RTT_SPREAD,
+ NL80211_PMSR_FTM_RESP_ATTR_DIST_AVG,
+ NL80211_PMSR_FTM_RESP_ATTR_DIST_VARIANCE,
+ NL80211_PMSR_FTM_RESP_ATTR_DIST_SPREAD,
+ NL80211_PMSR_FTM_RESP_ATTR_LCI,
+ NL80211_PMSR_FTM_RESP_ATTR_CIVICLOC,
+ NL80211_PMSR_FTM_RESP_ATTR_PAD,
+
+ /* keep last */
+ NUM_NL80211_PMSR_FTM_RESP_ATTR,
+ NL80211_PMSR_FTM_RESP_ATTR_MAX = NUM_NL80211_PMSR_FTM_RESP_ATTR - 1
+};
+
#endif /* __LINUX_NL80211_H */
diff --git a/src/eap_common/eap_eke_common.c b/src/eap_common/eap_eke_common.c
index bfe8811e33f27..438baf190be71 100644
--- a/src/eap_common/eap_eke_common.c
+++ b/src/eap_common/eap_eke_common.c
@@ -399,7 +399,7 @@ int eap_eke_shared_secret(struct eap_eke_session *sess, const u8 *key,
/* SharedSecret = prf(0+, g ^ (x_s * x_p) (mod p)) */
len = dh->prime_len;
if (crypto_dh_derive_secret(*dh->generator, dh->prime, dh->prime_len,
- dhpriv, dh->prime_len, peer_pub,
+ NULL, 0, dhpriv, dh->prime_len, peer_pub,
dh->prime_len, modexp, &len) < 0)
return -1;
if (len < dh->prime_len) {
diff --git a/src/eap_common/eap_pwd_common.c b/src/eap_common/eap_pwd_common.c
index 88c6595887ab9..884150e6c1eb5 100644
--- a/src/eap_common/eap_pwd_common.c
+++ b/src/eap_common/eap_pwd_common.c
@@ -8,11 +8,15 @@
#include "includes.h"
#include "common.h"
+#include "utils/const_time.h"
#include "crypto/sha256.h"
#include "crypto/crypto.h"
#include "eap_defs.h"
#include "eap_pwd_common.h"
+#define MAX_ECC_PRIME_LEN 66
+
+
/* The random function H(x) = HMAC-SHA256(0^32, x) */
struct crypto_hash * eap_pwd_h_init(void)
{
@@ -81,10 +85,23 @@ static int eap_pwd_kdf(const u8 *key, size_t keylen, const u8 *label,
}
+static int eap_pwd_suitable_group(u16 num)
+{
+ /* Do not allow ECC groups with prime under 256 bits based on guidance
+ * for the similar design in SAE. */
+ return num == 19 || num == 20 || num == 21 ||
+ num == 28 || num == 29 || num == 30;
+}
+
+
EAP_PWD_group * get_eap_pwd_group(u16 num)
{
EAP_PWD_group *grp;
+ if (!eap_pwd_suitable_group(num)) {
+ wpa_printf(MSG_INFO, "EAP-pwd: unsuitable group %u", num);
+ return NULL;
+ }
grp = os_zalloc(sizeof(EAP_PWD_group));
if (!grp)
return NULL;
@@ -102,6 +119,15 @@ EAP_PWD_group * get_eap_pwd_group(u16 num)
}
+static void buf_shift_right(u8 *buf, size_t len, size_t bits)
+{
+ size_t i;
+ for (i = len - 1; i > 0; i--)
+ buf[i] = (buf[i - 1] << (8 - bits)) | (buf[i] >> bits);
+ buf[0] >>= bits;
+}
+
+
/*
* compute a "random" secret point on an elliptic curve based
* on the password and identities.
@@ -113,33 +139,37 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
const u8 *token)
{
struct crypto_bignum *qr = NULL, *qnr = NULL, *one = NULL;
+ struct crypto_bignum *qr_or_qnr = NULL;
+ u8 qr_bin[MAX_ECC_PRIME_LEN];
+ u8 qnr_bin[MAX_ECC_PRIME_LEN];
+ u8 qr_or_qnr_bin[MAX_ECC_PRIME_LEN];
+ u8 x_bin[MAX_ECC_PRIME_LEN];
struct crypto_bignum *tmp1 = NULL, *tmp2 = NULL, *pm1 = NULL;
struct crypto_hash *hash;
unsigned char pwe_digest[SHA256_MAC_LEN], *prfbuf = NULL, ctr;
- int is_odd, ret = 0, check, found = 0;
- size_t primebytelen, primebitlen;
- struct crypto_bignum *x_candidate = NULL, *rnd = NULL, *cofactor = NULL;
+ int ret = 0, check, res;
+ u8 found = 0; /* 0 (false) or 0xff (true) to be used as const_time_*
+ * mask */
+ size_t primebytelen = 0, primebitlen;
+ struct crypto_bignum *x_candidate = NULL;
const struct crypto_bignum *prime;
+ u8 mask, found_ctr = 0, is_odd = 0;
if (grp->pwe)
return -1;
+ os_memset(x_bin, 0, sizeof(x_bin));
+
prime = crypto_ec_get_prime(grp->group);
- cofactor = crypto_bignum_init();
grp->pwe = crypto_ec_point_init(grp->group);
tmp1 = crypto_bignum_init();
pm1 = crypto_bignum_init();
one = crypto_bignum_init_set((const u8 *) "\x01", 1);
- if (!cofactor || !grp->pwe || !tmp1 || !pm1 || !one) {
+ if (!grp->pwe || !tmp1 || !pm1 || !one) {
wpa_printf(MSG_INFO, "EAP-pwd: unable to create bignums");
goto fail;
}
- if (crypto_ec_cofactor(grp->group, cofactor) < 0) {
- wpa_printf(MSG_INFO, "EAP-pwd: unable to get cofactor for "
- "curve");
- goto fail;
- }
primebitlen = crypto_ec_prime_len_bits(grp->group);
primebytelen = crypto_ec_prime_len(grp->group);
if ((prfbuf = os_malloc(primebytelen)) == NULL) {
@@ -152,8 +182,6 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
/* get a random quadratic residue and nonresidue */
while (!qr || !qnr) {
- int res;
-
if (crypto_bignum_rand(tmp1, prime) < 0)
goto fail;
res = crypto_bignum_legendre(tmp1, prime);
@@ -167,6 +195,11 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
if (!tmp1)
goto fail;
}
+ if (crypto_bignum_to_bin(qr, qr_bin, sizeof(qr_bin),
+ primebytelen) < 0 ||
+ crypto_bignum_to_bin(qnr, qnr_bin, sizeof(qnr_bin),
+ primebytelen) < 0)
+ goto fail;
os_memset(prfbuf, 0, primebytelen);
ctr = 0;
@@ -194,17 +227,16 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
eap_pwd_h_update(hash, &ctr, sizeof(ctr));
eap_pwd_h_final(hash, pwe_digest);
- crypto_bignum_deinit(rnd, 1);
- rnd = crypto_bignum_init_set(pwe_digest, SHA256_MAC_LEN);
- if (!rnd) {
- wpa_printf(MSG_INFO, "EAP-pwd: unable to create rnd");
- goto fail;
- }
+ is_odd = const_time_select_u8(
+ found, is_odd, pwe_digest[SHA256_MAC_LEN - 1] & 0x01);
if (eap_pwd_kdf(pwe_digest, SHA256_MAC_LEN,
(u8 *) "EAP-pwd Hunting And Pecking",
os_strlen("EAP-pwd Hunting And Pecking"),
prfbuf, primebitlen) < 0)
goto fail;
+ if (primebitlen % 8)
+ buf_shift_right(prfbuf, primebytelen,
+ 8 - primebitlen % 8);
crypto_bignum_deinit(x_candidate, 1);
x_candidate = crypto_bignum_init_set(prfbuf, primebytelen);
@@ -214,30 +246,20 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
goto fail;
}
- /*
- * eap_pwd_kdf() returns a string of bits 0..primebitlen but
- * BN_bin2bn will treat that string of bits as a big endian
- * number. If the primebitlen is not an even multiple of 8
- * then excessive bits-- those _after_ primebitlen-- so now
- * we have to shift right the amount we masked off.
- */
- if ((primebitlen % 8) &&
- crypto_bignum_rshift(x_candidate,
- (8 - (primebitlen % 8)),
- x_candidate) < 0)
- goto fail;
-
if (crypto_bignum_cmp(x_candidate, prime) >= 0)
continue;
- wpa_hexdump(MSG_DEBUG, "EAP-pwd: x_candidate",
- prfbuf, primebytelen);
+ wpa_hexdump_key(MSG_DEBUG, "EAP-pwd: x_candidate",
+ prfbuf, primebytelen);
+ const_time_select_bin(found, x_bin, prfbuf, primebytelen,
+ x_bin);
/*
* compute y^2 using the equation of the curve
*
* y^2 = x^3 + ax + b
*/
+ crypto_bignum_deinit(tmp2, 1);
tmp2 = crypto_ec_point_compute_y_sqr(grp->group, x_candidate);
if (!tmp2)
goto fail;
@@ -260,13 +282,15 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
* Flip a coin, multiply by the random quadratic residue or the
* random quadratic nonresidue and record heads or tails.
*/
- if (crypto_bignum_is_odd(tmp1)) {
- crypto_bignum_mulmod(tmp2, qr, prime, tmp2);
- check = 1;
- } else {
- crypto_bignum_mulmod(tmp2, qnr, prime, tmp2);
- check = -1;
- }
+ mask = const_time_eq_u8(crypto_bignum_is_odd(tmp1), 1);
+ check = const_time_select_s8(mask, 1, -1);
+ const_time_select_bin(mask, qr_bin, qnr_bin, primebytelen,
+ qr_or_qnr_bin);
+ crypto_bignum_deinit(qr_or_qnr, 1);
+ qr_or_qnr = crypto_bignum_init_set(qr_or_qnr_bin, primebytelen);
+ if (!qr_or_qnr ||
+ crypto_bignum_mulmod(tmp2, qr_or_qnr, prime, tmp2) < 0)
+ goto fail;
/*
* Now it's safe to do legendre, if check is 1 then it's
@@ -274,59 +298,12 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
* change result), if check is -1 then it's the opposite test
* (multiplying a qr by qnr would make a qnr).
*/
- if (crypto_bignum_legendre(tmp2, prime) == check) {
- if (found == 1)
- continue;
-
- /* need to unambiguously identify the solution */
- is_odd = crypto_bignum_is_odd(rnd);
-
- /*
- * We know x_candidate is a quadratic residue so set
- * it here.
- */
- if (crypto_ec_point_solve_y_coord(grp->group, grp->pwe,
- x_candidate,
- is_odd) != 0) {
- wpa_printf(MSG_INFO,
- "EAP-pwd: Could not solve for y");
- continue;
- }
-
- /*
- * If there's a solution to the equation then the point
- * must be on the curve so why check again explicitly?
- * OpenSSL code says this is required by X9.62. We're
- * not X9.62 but it can't hurt just to be sure.
- */
- if (!crypto_ec_point_is_on_curve(grp->group,
- grp->pwe)) {
- wpa_printf(MSG_INFO,
- "EAP-pwd: point is not on curve");
- continue;
- }
-
- if (!crypto_bignum_is_one(cofactor)) {
- /* make sure the point is not in a small
- * sub-group */
- if (crypto_ec_point_mul(grp->group, grp->pwe,
- cofactor,
- grp->pwe) != 0) {
- wpa_printf(MSG_INFO,
- "EAP-pwd: cannot multiply generator by order");
- continue;
- }
- if (crypto_ec_point_is_at_infinity(grp->group,
- grp->pwe)) {
- wpa_printf(MSG_INFO,
- "EAP-pwd: point is at infinity");
- continue;
- }
- }
- wpa_printf(MSG_DEBUG,
- "EAP-pwd: found a PWE in %d tries", ctr);
- found = 1;
- }
+ res = crypto_bignum_legendre(tmp2, prime);
+ if (res == -2)
+ goto fail;
+ mask = const_time_eq(res, check);
+ found_ctr = const_time_select_u8(found, found_ctr, ctr);
+ found |= mask;
}
if (found == 0) {
wpa_printf(MSG_INFO,
@@ -334,6 +311,31 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
num);
goto fail;
}
+
+ /*
+ * We know x_candidate is a quadratic residue so set it here.
+ */
+ crypto_bignum_deinit(x_candidate, 1);
+ x_candidate = crypto_bignum_init_set(x_bin, primebytelen);
+ if (!x_candidate ||
+ crypto_ec_point_solve_y_coord(grp->group, grp->pwe, x_candidate,
+ is_odd) != 0) {
+ wpa_printf(MSG_INFO, "EAP-pwd: Could not solve for y");
+ goto fail;
+ }
+
+ /*
+ * If there's a solution to the equation then the point must be on the
+ * curve so why check again explicitly? OpenSSL code says this is
+ * required by X9.62. We're not X9.62 but it can't hurt just to be sure.
+ */
+ if (!crypto_ec_point_is_on_curve(grp->group, grp->pwe)) {
+ wpa_printf(MSG_INFO, "EAP-pwd: point is not on curve");
+ goto fail;
+ }
+
+ wpa_printf(MSG_DEBUG, "EAP-pwd: found a PWE in %02d tries", found_ctr);
+
if (0) {
fail:
crypto_ec_point_deinit(grp->pwe, 1);
@@ -341,16 +343,19 @@ int compute_password_element(EAP_PWD_group *grp, u16 num,
ret = 1;
}
/* cleanliness and order.... */
- crypto_bignum_deinit(cofactor, 1);
crypto_bignum_deinit(x_candidate, 1);
- crypto_bignum_deinit(rnd, 1);
crypto_bignum_deinit(pm1, 0);
crypto_bignum_deinit(tmp1, 1);
crypto_bignum_deinit(tmp2, 1);
crypto_bignum_deinit(qr, 1);
crypto_bignum_deinit(qnr, 1);
+ crypto_bignum_deinit(qr_or_qnr, 1);
crypto_bignum_deinit(one, 0);
- os_free(prfbuf);
+ bin_clear_free(prfbuf, primebytelen);
+ os_memset(qr_bin, 0, sizeof(qr_bin));
+ os_memset(qnr_bin, 0, sizeof(qnr_bin));
+ os_memset(qr_or_qnr_bin, 0, sizeof(qr_or_qnr_bin));
+ os_memset(pwe_digest, 0, sizeof(pwe_digest));
return ret;
}
@@ -416,3 +421,108 @@ int compute_keys(EAP_PWD_group *grp, const struct crypto_bignum *k,
return 1;
}
+
+
+static int eap_pwd_element_coord_ok(const struct crypto_bignum *prime,
+ const u8 *buf, size_t len)
+{
+ struct crypto_bignum *val;
+ int ok = 1;
+
+ val = crypto_bignum_init_set(buf, len);
+ if (!val || crypto_bignum_is_zero(val) ||
+ crypto_bignum_cmp(val, prime) >= 0)
+ ok = 0;
+ crypto_bignum_deinit(val, 0);
+ return ok;
+}
+
+
+struct crypto_ec_point * eap_pwd_get_element(EAP_PWD_group *group,
+ const u8 *buf)
+{
+ struct crypto_ec_point *element;
+ const struct crypto_bignum *prime;
+ size_t prime_len;
+
+ prime = crypto_ec_get_prime(group->group);
+ prime_len = crypto_ec_prime_len(group->group);
+
+ /* RFC 5931, 2.8.5.2.2: 0 < x,y < p */
+ if (!eap_pwd_element_coord_ok(prime, buf, prime_len) ||
+ !eap_pwd_element_coord_ok(prime, buf + prime_len, prime_len)) {
+ wpa_printf(MSG_INFO, "EAP-pwd: Invalid coordinate in element");
+ return NULL;
+ }
+
+ element = crypto_ec_point_from_bin(group->group, buf);
+ if (!element) {
+ wpa_printf(MSG_INFO, "EAP-pwd: EC point from element failed");
+ return NULL;
+ }
+
+ /* RFC 5931, 2.8.5.2.2: on curve and not the point at infinity */
+ if (!crypto_ec_point_is_on_curve(group->group, element) ||
+ crypto_ec_point_is_at_infinity(group->group, element)) {
+ wpa_printf(MSG_INFO, "EAP-pwd: Invalid element");
+ goto fail;
+ }
+
+out:
+ return element;
+fail:
+ crypto_ec_point_deinit(element, 0);
+ element = NULL;
+ goto out;
+}
+
+
+struct crypto_bignum * eap_pwd_get_scalar(EAP_PWD_group *group, const u8 *buf)
+{
+ struct crypto_bignum *scalar;
+ const struct crypto_bignum *order;
+ size_t order_len;
+
+ order = crypto_ec_get_order(group->group);
+ order_len = crypto_ec_order_len(group->group);
+
+ /* RFC 5931, 2.8.5.2: 1 < scalar < r */
+ scalar = crypto_bignum_init_set(buf, order_len);
+ if (!scalar || crypto_bignum_is_zero(scalar) ||
+ crypto_bignum_is_one(scalar) ||
+ crypto_bignum_cmp(scalar, order) >= 0) {
+ wpa_printf(MSG_INFO, "EAP-pwd: received scalar is invalid");
+ crypto_bignum_deinit(scalar, 0);
+ scalar = NULL;
+ }
+
+ return scalar;
+}
+
+
+int eap_pwd_get_rand_mask(EAP_PWD_group *group, struct crypto_bignum *_rand,
+ struct crypto_bignum *_mask,
+ struct crypto_bignum *scalar)
+{
+ const struct crypto_bignum *order;
+ int count;
+
+ order = crypto_ec_get_order(group->group);
+
+ /* Select two random values rand,mask such that 1 < rand,mask < r and
+ * rand + mask mod r > 1. */
+ for (count = 0; count < 100; count++) {
+ if (crypto_bignum_rand(_rand, order) == 0 &&
+ !crypto_bignum_is_zero(_rand) &&
+ crypto_bignum_rand(_mask, order) == 0 &&
+ !crypto_bignum_is_zero(_mask) &&
+ crypto_bignum_add(_rand, _mask, scalar) == 0 &&
+ crypto_bignum_mod(scalar, order, scalar) == 0 &&
+ !crypto_bignum_is_zero(scalar) &&
+ !crypto_bignum_is_one(scalar))
+ return 0;
+ }
+
+ wpa_printf(MSG_INFO, "EAP-pwd: unable to get randomness");
+ return -1;
+}
diff --git a/src/eap_common/eap_pwd_common.h b/src/eap_common/eap_pwd_common.h
index 6b07cf8f797c8..c48acee204d3b 100644
--- a/src/eap_common/eap_pwd_common.h
+++ b/src/eap_common/eap_pwd_common.h
@@ -67,5 +67,11 @@ int compute_keys(EAP_PWD_group *grp, const struct crypto_bignum *k,
struct crypto_hash * eap_pwd_h_init(void);
void eap_pwd_h_update(struct crypto_hash *hash, const u8 *data, size_t len);
void eap_pwd_h_final(struct crypto_hash *hash, u8 *digest);
+struct crypto_ec_point * eap_pwd_get_element(EAP_PWD_group *group,
+ const u8 *buf);
+struct crypto_bignum * eap_pwd_get_scalar(EAP_PWD_group *group, const u8 *buf);
+int eap_pwd_get_rand_mask(EAP_PWD_group *group, struct crypto_bignum *_rand,
+ struct crypto_bignum *_mask,
+ struct crypto_bignum *scalar);
#endif /* EAP_PWD_COMMON_H */
diff --git a/src/eap_common/eap_sake_common.c b/src/eap_common/eap_sake_common.c
index 8819541b2264c..8ee9e32e1e488 100644
--- a/src/eap_common/eap_sake_common.c
+++ b/src/eap_common/eap_sake_common.c
@@ -1,6 +1,6 @@
/*
* EAP server/peer: EAP-SAKE shared routines
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -201,14 +201,15 @@ int eap_sake_parse_attributes(const u8 *buf, size_t len,
* @data2_len: Length of the data2
* @buf: Buffer for the generated pseudo-random key
* @buf_len: Number of bytes of key to generate
+ * Returns: 0 on success or -1 on failure
*
* This function is used to derive new, cryptographically separate keys from a
* given key (e.g., SMS). This is identical to the PRF used in IEEE 802.11i.
*/
-static void eap_sake_kdf(const u8 *key, size_t key_len, const char *label,
- const u8 *data, size_t data_len,
- const u8 *data2, size_t data2_len,
- u8 *buf, size_t buf_len)
+static int eap_sake_kdf(const u8 *key, size_t key_len, const char *label,
+ const u8 *data, size_t data_len,
+ const u8 *data2, size_t data2_len,
+ u8 *buf, size_t buf_len)
{
u8 counter = 0;
size_t pos, plen;
@@ -230,17 +231,21 @@ static void eap_sake_kdf(const u8 *key, size_t key_len, const char *label,
while (pos < buf_len) {
plen = buf_len - pos;
if (plen >= SHA1_MAC_LEN) {
- hmac_sha1_vector(key, key_len, 4, addr, len,
- &buf[pos]);
+ if (hmac_sha1_vector(key, key_len, 4, addr, len,
+ &buf[pos]) < 0)
+ return -1;
pos += SHA1_MAC_LEN;
} else {
- hmac_sha1_vector(key, key_len, 4, addr, len,
- hash);
+ if (hmac_sha1_vector(key, key_len, 4, addr, len,
+ hash) < 0)
+ return -1;
os_memcpy(&buf[pos], hash, plen);
break;
}
counter++;
}
+
+ return 0;
}
@@ -253,12 +258,13 @@ static void eap_sake_kdf(const u8 *key, size_t key_len, const char *label,
* @tek: Buffer for Temporary EAK Keys (TEK-Auth[16] | TEK-Cipher[16])
* @msk: Buffer for 64-byte MSK
* @emsk: Buffer for 64-byte EMSK
+ * Returns: 0 on success or -1 on failure
*
* This function derives EAP-SAKE keys as defined in RFC 4763, section 3.2.6.
*/
-void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b,
- const u8 *rand_s, const u8 *rand_p, u8 *tek, u8 *msk,
- u8 *emsk)
+int eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b,
+ const u8 *rand_s, const u8 *rand_p, u8 *tek, u8 *msk,
+ u8 *emsk)
{
u8 sms_a[EAP_SAKE_SMS_LEN];
u8 sms_b[EAP_SAKE_SMS_LEN];
@@ -268,14 +274,16 @@ void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b,
wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: Root-Secret-A",
root_secret_a, EAP_SAKE_ROOT_SECRET_LEN);
- eap_sake_kdf(root_secret_a, EAP_SAKE_ROOT_SECRET_LEN,
- "SAKE Master Secret A",
- rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN,
- sms_a, EAP_SAKE_SMS_LEN);
+ if (eap_sake_kdf(root_secret_a, EAP_SAKE_ROOT_SECRET_LEN,
+ "SAKE Master Secret A",
+ rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN,
+ sms_a, EAP_SAKE_SMS_LEN) < 0)
+ return -1;
wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: SMS-A", sms_a, EAP_SAKE_SMS_LEN);
- eap_sake_kdf(sms_a, EAP_SAKE_SMS_LEN, "Transient EAP Key",
- rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN,
- tek, EAP_SAKE_TEK_LEN);
+ if (eap_sake_kdf(sms_a, EAP_SAKE_SMS_LEN, "Transient EAP Key",
+ rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN,
+ tek, EAP_SAKE_TEK_LEN) < 0)
+ return -1;
wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: TEK-Auth",
tek, EAP_SAKE_TEK_AUTH_LEN);
wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: TEK-Cipher",
@@ -283,18 +291,21 @@ void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b,
wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: Root-Secret-B",
root_secret_b, EAP_SAKE_ROOT_SECRET_LEN);
- eap_sake_kdf(root_secret_b, EAP_SAKE_ROOT_SECRET_LEN,
- "SAKE Master Secret B",
- rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN,
- sms_b, EAP_SAKE_SMS_LEN);
+ if (eap_sake_kdf(root_secret_b, EAP_SAKE_ROOT_SECRET_LEN,
+ "SAKE Master Secret B",
+ rand_p, EAP_SAKE_RAND_LEN, rand_s, EAP_SAKE_RAND_LEN,
+ sms_b, EAP_SAKE_SMS_LEN) < 0)
+ return -1;
wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: SMS-B", sms_b, EAP_SAKE_SMS_LEN);
- eap_sake_kdf(sms_b, EAP_SAKE_SMS_LEN, "Master Session Key",
- rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN,
- key_buf, sizeof(key_buf));
+ if (eap_sake_kdf(sms_b, EAP_SAKE_SMS_LEN, "Master Session Key",
+ rand_s, EAP_SAKE_RAND_LEN, rand_p, EAP_SAKE_RAND_LEN,
+ key_buf, sizeof(key_buf)) < 0)
+ return -1;
os_memcpy(msk, key_buf, EAP_MSK_LEN);
os_memcpy(emsk, key_buf + EAP_MSK_LEN, EAP_EMSK_LEN);
wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: MSK", msk, EAP_MSK_LEN);
wpa_hexdump_key(MSG_DEBUG, "EAP-SAKE: EMSK", emsk, EAP_EMSK_LEN);
+ return 0;
}
@@ -312,6 +323,7 @@ void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b,
* @eap_len: EAP packet length
* @mic_pos: MIC position in the EAP packet (must be [eap .. eap + eap_len])
* @mic: Buffer for the computed 16-byte MIC
+ * Returns: 0 on success or -1 on failure
*/
int eap_sake_compute_mic(const u8 *tek_auth,
const u8 *rand_s, const u8 *rand_p,
@@ -323,6 +335,7 @@ int eap_sake_compute_mic(const u8 *tek_auth,
u8 _rand[2 * EAP_SAKE_RAND_LEN];
u8 *tmp, *pos;
size_t tmplen;
+ int ret;
tmplen = serverid_len + 1 + peerid_len + 1 + eap_len;
tmp = os_malloc(tmplen);
@@ -364,14 +377,14 @@ int eap_sake_compute_mic(const u8 *tek_auth,
os_memcpy(pos, eap, eap_len);
os_memset(pos + (mic_pos - eap), 0, EAP_SAKE_MIC_LEN);
- eap_sake_kdf(tek_auth, EAP_SAKE_TEK_AUTH_LEN,
- peer ? "Peer MIC" : "Server MIC",
- _rand, 2 * EAP_SAKE_RAND_LEN, tmp, tmplen,
- mic, EAP_SAKE_MIC_LEN);
+ ret = eap_sake_kdf(tek_auth, EAP_SAKE_TEK_AUTH_LEN,
+ peer ? "Peer MIC" : "Server MIC",
+ _rand, 2 * EAP_SAKE_RAND_LEN, tmp, tmplen,
+ mic, EAP_SAKE_MIC_LEN);
os_free(tmp);
- return 0;
+ return ret;
}
diff --git a/src/eap_common/eap_sake_common.h b/src/eap_common/eap_sake_common.h
index 9e1e75745a115..a817a35d438cc 100644
--- a/src/eap_common/eap_sake_common.h
+++ b/src/eap_common/eap_sake_common.h
@@ -1,6 +1,6 @@
/*
* EAP server/peer: EAP-SAKE shared routines
- * Copyright (c) 2006-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -81,9 +81,9 @@ struct eap_sake_parse_attr {
int eap_sake_parse_attributes(const u8 *buf, size_t len,
struct eap_sake_parse_attr *attr);
-void eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b,
- const u8 *rand_s, const u8 *rand_p,
- u8 *tek, u8 *msk, u8 *emsk);
+int eap_sake_derive_keys(const u8 *root_secret_a, const u8 *root_secret_b,
+ const u8 *rand_s, const u8 *rand_p,
+ u8 *tek, u8 *msk, u8 *emsk);
int eap_sake_compute_mic(const u8 *tek_auth,
const u8 *rand_s, const u8 *rand_p,
const u8 *serverid, size_t serverid_len,
diff --git a/src/eap_peer/eap_config.h b/src/eap_peer/eap_config.h
index d416afd56d59f..3a88f2abd236b 100644
--- a/src/eap_peer/eap_config.h
+++ b/src/eap_peer/eap_config.h
@@ -101,7 +101,7 @@ struct eap_peer_config {
* certificate store (My user account) is used, whereas computer store
* (Computer account) is used when running wpasvc as a service.
*/
- u8 *ca_cert;
+ char *ca_cert;
/**
* ca_path - Directory path for CA certificate files (PEM)
@@ -112,7 +112,7 @@ struct eap_peer_config {
* these certificates are added to the list of trusted CAs. ca_cert
* may also be included in that case, but it is not required.
*/
- u8 *ca_path;
+ char *ca_path;
/**
* client_cert - File path to client certificate file (PEM/DER)
@@ -126,7 +126,7 @@ struct eap_peer_config {
* Alternatively, a named configuration blob can be used by setting
* this to blob://blob_name.
*/
- u8 *client_cert;
+ char *client_cert;
/**
* private_key - File path to client private key file (PEM/DER/PFX)
@@ -153,7 +153,7 @@ struct eap_peer_config {
* Alternatively, a named configuration blob can be used by setting
* this to blob://blob_name.
*/
- u8 *private_key;
+ char *private_key;
/**
* private_key_passwd - Password for private key file
@@ -178,7 +178,7 @@ struct eap_peer_config {
* Alternatively, a named configuration blob can be used by setting
* this to blob://blob_name.
*/
- u8 *dh_file;
+ char *dh_file;
/**
* subject_match - Constraint for server certificate subject
@@ -194,7 +194,49 @@ struct eap_peer_config {
* to do a suffix match against a possible domain name in the CN entry.
* For such a use case, domain_suffix_match should be used instead.
*/
- u8 *subject_match;
+ char *subject_match;
+
+ /**
+ * check_cert_subject - Constraint for server certificate subject fields
+ *
+ * If check_cert_subject is set, the value of every field will be
+ * checked against the DN of the subject in the authentication server
+ * certificate. If the values do not match, the certificate verification
+ * will fail, rejecting the server. This option allows wpa_supplicant to
+ * match every individual field in the right order against the DN of the
+ * subject in the server certificate.
+ *
+ * For example, check_cert_subject=C=US/O=XX/OU=ABC/OU=XYZ/CN=1234 will
+ * check every individual DN field of the subject in the server
+ * certificate. If OU=XYZ comes first in terms of the order in the
+ * server certificate (DN field of server certificate
+ * C=US/O=XX/OU=XYZ/OU=ABC/CN=1234), wpa_supplicant will reject the
+ * server because the order of 'OU' is not matching the specified string
+ * in check_cert_subject.
+ *
+ * This option also allows '*' as a wildcard. This option has some
+ * limitation.
+ * It can only be used as per the following example.
+ *
+ * For example, check_cert_subject=C=US/O=XX/OU=Production* and we have
+ * two servers and DN of the subject in the first server certificate is
+ * (C=US/O=XX/OU=Production Unit) and DN of the subject in the second
+ * server is (C=US/O=XX/OU=Production Factory). In this case,
+ * wpa_supplicant will allow both servers because the value of 'OU'
+ * field in both server certificates matches 'OU' value in
+ * 'check_cert_subject' up to 'wildcard'.
+ *
+ * (Allow all servers, e.g., check_cert_subject=*)
+ */
+ char *check_cert_subject;
+
+ /**
+ * check_cert_subject2 - Constraint for server certificate subject fields
+ *
+ * This field is like check_cert_subject, but used for phase 2 (inside
+ * EAP-TTLS/PEAP/FAST tunnel) authentication.
+ */
+ char *check_cert_subject2;
/**
* altsubject_match - Constraint for server certificate alt. subject
@@ -212,23 +254,26 @@ struct eap_peer_config {
*
* Following types are supported: EMAIL, DNS, URI
*/
- u8 *altsubject_match;
+ char *altsubject_match;
/**
* domain_suffix_match - Constraint for server domain name
*
- * If set, this FQDN is used as a suffix match requirement for the
- * server certificate in SubjectAltName dNSName element(s). If a
- * matching dNSName is found, this constraint is met. If no dNSName
- * values are present, this constraint is matched against SubjectName CN
- * using same suffix match comparison. Suffix match here means that the
- * host/domain name is compared one label at a time starting from the
- * top-level domain and all the labels in domain_suffix_match shall be
- * included in the certificate. The certificate may include additional
- * sub-level labels in addition to the required labels.
+ * If set, this semicolon deliminated list of FQDNs is used as suffix
+ * match requirements for the server certificate in SubjectAltName
+ * dNSName element(s). If a matching dNSName is found against any of the
+ * specified values, this constraint is met. If no dNSName values are
+ * present, this constraint is matched against SubjectName CN using same
+ * suffix match comparison. Suffix match here means that the host/domain
+ * name is compared case-insentively one label at a time starting from
+ * the top-level domain and all the labels in domain_suffix_match shall
+ * be included in the certificate. The certificate may include
+ * additional sub-level labels in addition to the required labels.
*
* For example, domain_suffix_match=example.com would match
- * test.example.com but would not match test-example.com.
+ * test.example.com but would not match test-example.com. Multiple
+ * match options can be specified in following manner:
+ * example.org;example.com.
*/
char *domain_suffix_match;
@@ -244,6 +289,12 @@ struct eap_peer_config {
* no subdomains or wildcard matches are allowed. Case-insensitive
* comparison is used, so "Example.com" matches "example.com", but would
* not match "test.Example.com".
+ *
+ * More than one match string can be provided by using semicolons to
+ * separate the strings (e.g., example.org;example.com). When multiple
+ * strings are specified, a match with any one of the values is
+ * considered a sufficient match for the certificate, i.e., the
+ * conditions are ORed together.
*/
char *domain_match;
@@ -263,7 +314,7 @@ struct eap_peer_config {
* Alternatively, a named configuration blob can be used by setting
* this to blob://blob_name.
*/
- u8 *ca_cert2;
+ char *ca_cert2;
/**
* ca_path2 - Directory path for CA certificate files (PEM) (Phase 2)
@@ -277,7 +328,7 @@ struct eap_peer_config {
* This field is like ca_path, but used for phase 2 (inside
* EAP-TTLS/PEAP/FAST tunnel) authentication.
*/
- u8 *ca_path2;
+ char *ca_path2;
/**
* client_cert2 - File path to client certificate file
@@ -290,7 +341,7 @@ struct eap_peer_config {
* Alternatively, a named configuration blob can be used by setting
* this to blob://blob_name.
*/
- u8 *client_cert2;
+ char *client_cert2;
/**
* private_key2 - File path to client private key file
@@ -303,7 +354,7 @@ struct eap_peer_config {
* Alternatively, a named configuration blob can be used by setting
* this to blob://blob_name.
*/
- u8 *private_key2;
+ char *private_key2;
/**
* private_key2_passwd - Password for private key file
@@ -324,7 +375,7 @@ struct eap_peer_config {
* Alternatively, a named configuration blob can be used by setting
* this to blob://blob_name.
*/
- u8 *dh_file2;
+ char *dh_file2;
/**
* subject_match2 - Constraint for server certificate subject
@@ -332,7 +383,7 @@ struct eap_peer_config {
* This field is like subject_match, but used for phase 2 (inside
* EAP-TTLS/PEAP/FAST tunnel) authentication.
*/
- u8 *subject_match2;
+ char *subject_match2;
/**
* altsubject_match2 - Constraint for server certificate alt. subject
@@ -340,7 +391,7 @@ struct eap_peer_config {
* This field is like altsubject_match, but used for phase 2 (inside
* EAP-TTLS/PEAP/FAST tunnel) authentication.
*/
- u8 *altsubject_match2;
+ char *altsubject_match2;
/**
* domain_suffix_match2 - Constraint for server domain name
diff --git a/src/eap_peer/eap_fast.c b/src/eap_peer/eap_fast.c
index 74cec7dd583f8..94ce57d6219f2 100644
--- a/src/eap_peer/eap_fast.c
+++ b/src/eap_peer/eap_fast.c
@@ -250,8 +250,8 @@ static void eap_fast_deinit(struct eap_sm *sm, void *priv)
os_memset(data->key_data, 0, EAP_FAST_KEY_LEN);
os_memset(data->emsk, 0, EAP_EMSK_LEN);
os_free(data->session_id);
- wpabuf_free(data->pending_phase2_req);
- wpabuf_free(data->pending_resp);
+ wpabuf_clear_free(data->pending_phase2_req);
+ wpabuf_clear_free(data->pending_resp);
os_free(data);
}
@@ -486,7 +486,7 @@ static int eap_fast_phase2_request(struct eap_sm *sm,
(config->pending_req_identity || config->pending_req_password ||
config->pending_req_otp || config->pending_req_new_password ||
config->pending_req_sim)) {
- wpabuf_free(data->pending_phase2_req);
+ wpabuf_clear_free(data->pending_phase2_req);
data->pending_phase2_req = wpabuf_alloc_copy(hdr, len);
} else if (*resp == NULL)
return -1;
@@ -801,7 +801,7 @@ static struct wpabuf * eap_fast_process_crypto_binding(
ret->methodState = METHOD_DONE;
ret->decision = DECISION_FAIL;
data->phase2_success = 0;
- wpabuf_free(resp);
+ wpabuf_clear_free(resp);
return NULL;
}
@@ -815,7 +815,7 @@ static struct wpabuf * eap_fast_process_crypto_binding(
} else {
wpa_printf(MSG_ERROR, "EAP-FAST: Failed to derive "
"Session-Id");
- wpabuf_free(resp);
+ wpabuf_clear_free(resp);
return NULL;
}
}
@@ -1150,7 +1150,7 @@ static int eap_fast_encrypt_response(struct eap_sm *sm,
wpa_printf(MSG_INFO, "EAP-FAST: Failed to encrypt a Phase 2 "
"frame");
}
- wpabuf_free(resp);
+ wpabuf_clear_free(resp);
return 0;
}
@@ -1328,14 +1328,14 @@ continue_req:
wpa_printf(MSG_INFO, "EAP-FAST: Too short Phase 2 "
"TLV frame (len=%lu)",
(unsigned long) wpabuf_len(in_decrypted));
- wpabuf_free(in_decrypted);
+ wpabuf_clear_free(in_decrypted);
return -1;
}
res = eap_fast_process_decrypted(sm, data, ret, identifier,
in_decrypted, out_data);
- wpabuf_free(in_decrypted);
+ wpabuf_clear_free(in_decrypted);
return res;
}
@@ -1613,7 +1613,7 @@ static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv,
if (sm->waiting_ext_cert_check) {
wpa_printf(MSG_DEBUG,
"EAP-FAST: Waiting external server certificate validation");
- wpabuf_free(data->pending_resp);
+ wpabuf_clear_free(data->pending_resp);
data->pending_resp = resp;
return NULL;
}
@@ -1641,7 +1641,7 @@ static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv,
"EAP-FAST: Could not derive keys");
ret->methodState = METHOD_DONE;
ret->decision = DECISION_FAIL;
- wpabuf_free(resp);
+ wpabuf_clear_free(resp);
return NULL;
}
}
@@ -1650,7 +1650,7 @@ static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv,
/*
* Application data included in the handshake message.
*/
- wpabuf_free(data->pending_phase2_req);
+ wpabuf_clear_free(data->pending_phase2_req);
data->pending_phase2_req = resp;
resp = NULL;
res = eap_fast_decrypt(sm, data, ret, id, &msg, &resp);
@@ -1658,7 +1658,7 @@ static struct wpabuf * eap_fast_process(struct eap_sm *sm, void *priv,
}
if (res == 1) {
- wpabuf_free(resp);
+ wpabuf_clear_free(resp);
return eap_peer_tls_build_ack(id, EAP_TYPE_FAST,
data->fast_version);
}
@@ -1684,9 +1684,9 @@ static void eap_fast_deinit_for_reauth(struct eap_sm *sm, void *priv)
data->phase2_method->deinit_for_reauth(sm, data->phase2_priv);
os_free(data->key_block_p);
data->key_block_p = NULL;
- wpabuf_free(data->pending_phase2_req);
+ wpabuf_clear_free(data->pending_phase2_req);
data->pending_phase2_req = NULL;
- wpabuf_free(data->pending_resp);
+ wpabuf_clear_free(data->pending_resp);
data->pending_resp = NULL;
}
diff --git a/src/eap_peer/eap_mschapv2.c b/src/eap_peer/eap_mschapv2.c
index 877495cf3ac72..249baec88ebb2 100644
--- a/src/eap_peer/eap_mschapv2.c
+++ b/src/eap_peer/eap_mschapv2.c
@@ -856,9 +856,13 @@ static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
/* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key, i.e.,
* peer MS-MPPE-Send-Key | MS-MPPE-Recv-Key */
- get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 1, 0);
- get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
- MSCHAPV2_KEY_LEN, 0, 0);
+ if (get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 1,
+ 0) < 0 ||
+ get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
+ MSCHAPV2_KEY_LEN, 0, 0) < 0) {
+ os_free(key);
+ return NULL;
+ }
wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key",
key, key_len);
diff --git a/src/eap_peer/eap_peap.c b/src/eap_peer/eap_peap.c
index 34075b1d9af65..8dcf7cc2926f8 100644
--- a/src/eap_peer/eap_peap.c
+++ b/src/eap_peer/eap_peap.c
@@ -1,6 +1,6 @@
/*
* EAP peer method: EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt)
- * Copyright (c) 2004-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -169,7 +169,7 @@ static void * eap_peap_init(struct eap_sm *sm)
static void eap_peap_free_key(struct eap_peap_data *data)
{
if (data->key_data) {
- bin_clear_free(data->key_data, EAP_TLS_KEY_LEN);
+ bin_clear_free(data->key_data, EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
data->key_data = NULL;
}
}
@@ -186,9 +186,9 @@ static void eap_peap_deinit(struct eap_sm *sm, void *priv)
eap_peer_tls_ssl_deinit(sm, &data->ssl);
eap_peap_free_key(data);
os_free(data->session_id);
- wpabuf_free(data->pending_phase2_req);
- wpabuf_free(data->pending_resp);
- os_free(data);
+ wpabuf_clear_free(data->pending_phase2_req);
+ wpabuf_clear_free(data->pending_resp);
+ bin_clear_free(data, sizeof(*data));
}
@@ -253,7 +253,7 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data)
{
u8 *tk;
u8 isk[32], imck[60];
- int resumed;
+ int resumed, res;
/*
* Tunnel key (TK) is the first 60 octets of the key generated by
@@ -292,9 +292,11 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data)
* in the end of the label just before ISK; is that just a typo?)
*/
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40);
- if (peap_prfplus(data->peap_version, tk, 40,
- "Inner Methods Compound Keys",
- isk, sizeof(isk), imck, sizeof(imck)) < 0)
+ res = peap_prfplus(data->peap_version, tk, 40,
+ "Inner Methods Compound Keys",
+ isk, sizeof(isk), imck, sizeof(imck));
+ os_memset(isk, 0, sizeof(isk));
+ if (res < 0)
return -1;
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IMCK (IPMKj)",
imck, sizeof(imck));
@@ -303,6 +305,7 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data)
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40);
os_memcpy(data->cmk, imck + 40, 20);
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20);
+ os_memset(imck, 0, sizeof(imck));
return 0;
}
@@ -382,7 +385,7 @@ static struct wpabuf * eap_tlv_build_result(struct eap_sm *sm,
wpabuf_put_be16(msg, status); /* Status */
if (crypto_tlv_used && eap_tlv_add_cryptobinding(sm, data, msg)) {
- wpabuf_free(msg);
+ wpabuf_clear_free(msg);
return NULL;
}
@@ -651,11 +654,11 @@ static int eap_peap_phase2_request(struct eap_sm *sm,
if (*resp == NULL) {
ret->methodState = METHOD_DONE;
ret->decision = DECISION_FAIL;
- wpabuf_free(buf);
+ wpabuf_clear_free(buf);
return -1;
}
wpabuf_put_buf(*resp, buf);
- wpabuf_free(buf);
+ wpabuf_clear_free(buf);
break;
}
}
@@ -728,7 +731,7 @@ static int eap_peap_phase2_request(struct eap_sm *sm,
(config->pending_req_identity || config->pending_req_password ||
config->pending_req_otp || config->pending_req_new_password ||
config->pending_req_sim)) {
- wpabuf_free(data->pending_phase2_req);
+ wpabuf_clear_free(data->pending_phase2_req);
data->pending_phase2_req = wpabuf_alloc_copy(hdr, len);
}
@@ -807,7 +810,7 @@ continue_req:
struct wpabuf *nmsg = wpabuf_alloc(sizeof(struct eap_hdr) +
wpabuf_len(in_decrypted));
if (nmsg == NULL) {
- wpabuf_free(in_decrypted);
+ wpabuf_clear_free(in_decrypted);
return 0;
}
nhdr = wpabuf_put(nmsg, sizeof(*nhdr));
@@ -817,7 +820,7 @@ continue_req:
nhdr->length = host_to_be16(sizeof(struct eap_hdr) +
wpabuf_len(in_decrypted));
- wpabuf_free(in_decrypted);
+ wpabuf_clear_free(in_decrypted);
in_decrypted = nmsg;
}
@@ -826,7 +829,7 @@ continue_req:
wpa_printf(MSG_INFO, "EAP-PEAP: Too short Phase 2 "
"EAP frame (len=%lu)",
(unsigned long) wpabuf_len(in_decrypted));
- wpabuf_free(in_decrypted);
+ wpabuf_clear_free(in_decrypted);
return 0;
}
len = be_to_host16(hdr->length);
@@ -835,7 +838,7 @@ continue_req:
"Phase 2 EAP frame (len=%lu hdr->length=%lu)",
(unsigned long) wpabuf_len(in_decrypted),
(unsigned long) len);
- wpabuf_free(in_decrypted);
+ wpabuf_clear_free(in_decrypted);
return 0;
}
if (len < wpabuf_len(in_decrypted)) {
@@ -852,7 +855,7 @@ continue_req:
case EAP_CODE_REQUEST:
if (eap_peap_phase2_request(sm, data, ret, in_decrypted,
&resp)) {
- wpabuf_free(in_decrypted);
+ wpabuf_clear_free(in_decrypted);
wpa_printf(MSG_INFO, "EAP-PEAP: Phase2 Request "
"processing failed");
return 0;
@@ -872,7 +875,7 @@ continue_req:
"completed successfully");
ret->methodState = METHOD_DONE;
ret->decision = DECISION_FAIL;
- wpabuf_free(in_decrypted);
+ wpabuf_clear_free(in_decrypted);
return 0;
}
wpa_printf(MSG_DEBUG, "EAP-PEAP: Version 1 - "
@@ -882,7 +885,7 @@ continue_req:
ret->methodState = METHOD_DONE;
data->phase2_success = 1;
if (data->peap_outer_success == 2) {
- wpabuf_free(in_decrypted);
+ wpabuf_clear_free(in_decrypted);
wpa_printf(MSG_DEBUG, "EAP-PEAP: Use TLS ACK "
"to finish authentication");
return 1;
@@ -928,7 +931,7 @@ continue_req:
break;
}
- wpabuf_free(in_decrypted);
+ wpabuf_clear_free(in_decrypted);
if (resp) {
int skip_change2 = 0;
@@ -955,7 +958,7 @@ continue_req:
wpa_printf(MSG_INFO, "EAP-PEAP: Failed to encrypt "
"a Phase 2 frame");
}
- wpabuf_free(resp);
+ wpabuf_clear_free(resp);
}
return 0;
@@ -1056,7 +1059,7 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv,
if (sm->waiting_ext_cert_check) {
wpa_printf(MSG_DEBUG,
"EAP-PEAP: Waiting external server certificate validation");
- wpabuf_free(data->pending_resp);
+ wpabuf_clear_free(data->pending_resp);
data->pending_resp = resp;
return NULL;
}
@@ -1081,12 +1084,19 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv,
"key derivation", label);
data->key_data =
eap_peer_tls_derive_key(sm, &data->ssl, label,
- EAP_TLS_KEY_LEN);
+ NULL, 0,
+ EAP_TLS_KEY_LEN +
+ EAP_EMSK_LEN);
if (data->key_data) {
wpa_hexdump_key(MSG_DEBUG,
"EAP-PEAP: Derived key",
data->key_data,
EAP_TLS_KEY_LEN);
+ wpa_hexdump_key(MSG_DEBUG,
+ "EAP-PEAP: Derived EMSK",
+ data->key_data +
+ EAP_TLS_KEY_LEN,
+ EAP_EMSK_LEN);
} else {
wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to "
"derive key");
@@ -1131,7 +1141,7 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv,
/*
* Application data included in the handshake message.
*/
- wpabuf_free(data->pending_phase2_req);
+ wpabuf_clear_free(data->pending_phase2_req);
data->pending_phase2_req = resp;
resp = NULL;
res = eap_peap_decrypt(sm, data, ret, req, &msg,
@@ -1144,7 +1154,7 @@ static struct wpabuf * eap_peap_process(struct eap_sm *sm, void *priv,
}
if (res == 1) {
- wpabuf_free(resp);
+ wpabuf_clear_free(resp);
return eap_peer_tls_build_ack(id, EAP_TYPE_PEAP,
data->peap_version);
}
@@ -1168,9 +1178,9 @@ static void eap_peap_deinit_for_reauth(struct eap_sm *sm, void *priv)
if (data->phase2_priv && data->phase2_method &&
data->phase2_method->deinit_for_reauth)
data->phase2_method->deinit_for_reauth(sm, data->phase2_priv);
- wpabuf_free(data->pending_phase2_req);
+ wpabuf_clear_free(data->pending_phase2_req);
data->pending_phase2_req = NULL;
- wpabuf_free(data->pending_resp);
+ wpabuf_clear_free(data->pending_resp);
data->pending_resp = NULL;
data->crypto_binding_used = 0;
}
@@ -1257,6 +1267,7 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len)
os_memcpy(key, csk, EAP_TLS_KEY_LEN);
wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key",
key, EAP_TLS_KEY_LEN);
+ os_memset(csk, 0, sizeof(csk));
} else
os_memcpy(key, data->key_data, EAP_TLS_KEY_LEN);
@@ -1264,6 +1275,29 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len)
}
+static u8 * eap_peap_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_peap_data *data = priv;
+ u8 *key;
+
+ if (!data->key_data || !data->phase2_success)
+ return NULL;
+
+ if (data->crypto_binding_used) {
+ /* [MS-PEAP] does not define EMSK derivation */
+ return NULL;
+ }
+
+ key = os_memdup(data->key_data + EAP_TLS_KEY_LEN, EAP_EMSK_LEN);
+ if (!key)
+ return NULL;
+
+ *len = EAP_EMSK_LEN;
+
+ return key;
+}
+
+
static u8 * eap_peap_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
{
struct eap_peap_data *data = priv;
@@ -1296,6 +1330,7 @@ int eap_peer_peap_register(void)
eap->process = eap_peap_process;
eap->isKeyAvailable = eap_peap_isKeyAvailable;
eap->getKey = eap_peap_getKey;
+ eap->get_emsk = eap_peap_get_emsk;
eap->get_status = eap_peap_get_status;
eap->has_reauth_data = eap_peap_has_reauth_data;
eap->deinit_for_reauth = eap_peap_deinit_for_reauth;
diff --git a/src/eap_peer/eap_pwd.c b/src/eap_peer/eap_pwd.c
index 761c16af996a3..76fcad4a50e0d 100644
--- a/src/eap_peer/eap_pwd.c
+++ b/src/eap_peer/eap_pwd.c
@@ -308,10 +308,10 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
const struct wpabuf *reqData,
const u8 *payload, size_t payload_len)
{
- struct crypto_ec_point *K = NULL, *point = NULL;
- struct crypto_bignum *mask = NULL, *cofactor = NULL;
+ struct crypto_ec_point *K = NULL;
+ struct crypto_bignum *mask = NULL;
const u8 *ptr = payload;
- u8 *scalar = NULL, *element = NULL;
+ u8 *scalar, *element;
size_t prime_len, order_len;
const u8 *password;
size_t password_len;
@@ -527,34 +527,17 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
data->private_value = crypto_bignum_init();
data->my_element = crypto_ec_point_init(data->grp->group);
- cofactor = crypto_bignum_init();
data->my_scalar = crypto_bignum_init();
mask = crypto_bignum_init();
- if (!data->private_value || !data->my_element || !cofactor ||
+ if (!data->private_value || !data->my_element ||
!data->my_scalar || !mask) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): scalar allocation fail");
goto fin;
}
- if (crypto_ec_cofactor(data->grp->group, cofactor) < 0) {
- wpa_printf(MSG_INFO, "EAP-pwd (peer): unable to get cofactor "
- "for curve");
+ if (eap_pwd_get_rand_mask(data->grp, data->private_value, mask,
+ data->my_scalar) < 0)
goto fin;
- }
-
- if (crypto_bignum_rand(data->private_value,
- crypto_ec_get_order(data->grp->group)) < 0 ||
- crypto_bignum_rand(mask,
- crypto_ec_get_order(data->grp->group)) < 0 ||
- crypto_bignum_add(data->private_value, mask,
- data->my_scalar) < 0 ||
- crypto_bignum_mod(data->my_scalar,
- crypto_ec_get_order(data->grp->group),
- data->my_scalar) < 0) {
- wpa_printf(MSG_INFO,
- "EAP-pwd (peer): unable to get randomness");
- goto fin;
- }
if (crypto_ec_point_mul(data->grp->group, data->grp->pwe, mask,
data->my_element) < 0) {
@@ -572,43 +555,27 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
/* process the request */
data->k = crypto_bignum_init();
K = crypto_ec_point_init(data->grp->group);
- point = crypto_ec_point_init(data->grp->group);
- if (!data->k || !K || !point) {
+ if (!data->k || !K) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): peer data allocation "
"fail");
goto fin;
}
/* element, x then y, followed by scalar */
- data->server_element = crypto_ec_point_from_bin(data->grp->group, ptr);
+ data->server_element = eap_pwd_get_element(data->grp, ptr);
if (!data->server_element) {
wpa_printf(MSG_INFO, "EAP-PWD (peer): setting peer element "
"fail");
goto fin;
}
ptr += prime_len * 2;
- data->server_scalar = crypto_bignum_init_set(ptr, order_len);
+ data->server_scalar = eap_pwd_get_scalar(data->grp, ptr);
if (!data->server_scalar) {
wpa_printf(MSG_INFO,
"EAP-PWD (peer): setting peer scalar fail");
goto fin;
}
- /* check to ensure server's element is not in a small sub-group */
- if (!crypto_bignum_is_one(cofactor)) {
- if (crypto_ec_point_mul(data->grp->group, data->server_element,
- cofactor, point) < 0) {
- wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply "
- "server element by order!\n");
- goto fin;
- }
- if (crypto_ec_point_is_at_infinity(data->grp->group, point)) {
- wpa_printf(MSG_INFO, "EAP-PWD (peer): server element "
- "is at infinity!\n");
- goto fin;
- }
- }
-
/* compute the shared key, k */
if (crypto_ec_point_mul(data->grp->group, data->grp->pwe,
data->server_scalar, K) < 0 ||
@@ -621,17 +588,8 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
goto fin;
}
- /* ensure that the shared key isn't in a small sub-group */
- if (!crypto_bignum_is_one(cofactor)) {
- if (crypto_ec_point_mul(data->grp->group, K, cofactor, K) < 0) {
- wpa_printf(MSG_INFO, "EAP-PWD (peer): cannot multiply "
- "shared key point by order");
- goto fin;
- }
- }
-
/*
- * This check is strictly speaking just for the case above where
+ * This check is strictly speaking just for the case where
* co-factor > 1 but it was suggested that even though this is probably
* never going to happen it is a simple and safe check "just to be
* sure" so let's be safe.
@@ -649,12 +607,12 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
}
/* now do the response */
- scalar = os_zalloc(order_len);
- element = os_zalloc(prime_len * 2);
- if (!scalar || !element) {
- wpa_printf(MSG_INFO, "EAP-PWD (peer): data allocation fail");
+ data->outbuf = wpabuf_alloc(2 * prime_len + order_len);
+ if (data->outbuf == NULL)
goto fin;
- }
+ /* We send the element as (x,y) followed by the scalar */
+ element = wpabuf_put(data->outbuf, 2 * prime_len);
+ scalar = wpabuf_put(data->outbuf, order_len);
/*
* bignums occupy as little memory as possible so one that is
@@ -668,21 +626,9 @@ eap_pwd_perform_commit_exchange(struct eap_sm *sm, struct eap_pwd_data *data,
goto fin;
}
- data->outbuf = wpabuf_alloc(order_len + 2 * prime_len);
- if (data->outbuf == NULL)
- goto fin;
-
- /* we send the element as (x,y) follwed by the scalar */
- wpabuf_put_data(data->outbuf, element, 2 * prime_len);
- wpabuf_put_data(data->outbuf, scalar, order_len);
-
fin:
- os_free(scalar);
- os_free(element);
crypto_bignum_deinit(mask, 1);
- crypto_bignum_deinit(cofactor, 1);
crypto_ec_point_deinit(K, 1);
- crypto_ec_point_deinit(point, 1);
if (data->outbuf == NULL)
eap_pwd_state(data, FAILURE);
else
@@ -986,6 +932,13 @@ eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret,
* buffer and ACK the fragment
*/
if (EAP_PWD_GET_MORE_BIT(lm_exch) || data->in_frag_pos) {
+ if (!data->inbuf) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-pwd: No buffer for reassembly");
+ ret->methodState = METHOD_DONE;
+ ret->decision = DECISION_FAIL;
+ return NULL;
+ }
data->in_frag_pos += len;
if (data->in_frag_pos > wpabuf_size(data->inbuf)) {
wpa_printf(MSG_INFO, "EAP-pwd: Buffer overflow attack "
@@ -1012,7 +965,7 @@ eap_pwd_process(struct eap_sm *sm, void *priv, struct eap_method_ret *ret,
/*
* we're buffering and this is the last fragment
*/
- if (data->in_frag_pos) {
+ if (data->in_frag_pos && data->inbuf) {
wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes",
(int) len);
pos = wpabuf_head_u8(data->inbuf);
diff --git a/src/eap_peer/eap_sake.c b/src/eap_peer/eap_sake.c
index 0a6ce255af4de..255241f6d5aa9 100644
--- a/src/eap_peer/eap_sake.c
+++ b/src/eap_peer/eap_sake.c
@@ -1,6 +1,6 @@
/*
* EAP peer method: EAP-SAKE (RFC 4763)
- * Copyright (c) 2006-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -235,9 +235,13 @@ static struct wpabuf * eap_sake_process_challenge(struct eap_sm *sm,
data->serverid_len = attr.serverid_len;
}
- eap_sake_derive_keys(data->root_secret_a, data->root_secret_b,
- data->rand_s, data->rand_p,
- (u8 *) &data->tek, data->msk, data->emsk);
+ if (eap_sake_derive_keys(data->root_secret_a, data->root_secret_b,
+ data->rand_s, data->rand_p,
+ (u8 *) &data->tek, data->msk,
+ data->emsk) < 0) {
+ wpa_printf(MSG_INFO, "EAP-SAKE: Failed to derive keys");
+ return NULL;
+ }
wpa_printf(MSG_DEBUG, "EAP-SAKE: Sending Response/Challenge");
diff --git a/src/eap_peer/eap_tls.c b/src/eap_peer/eap_tls.c
index cb747026cb8a5..ffea9d213855f 100644
--- a/src/eap_peer/eap_tls.c
+++ b/src/eap_peer/eap_tls.c
@@ -198,6 +198,7 @@ static void eap_tls_success(struct eap_sm *sm, struct eap_tls_data *data,
eap_tls_free_key(data);
data->key_data = eap_peer_tls_derive_key(sm, &data->ssl, label,
+ NULL, 0,
EAP_TLS_KEY_LEN +
EAP_EMSK_LEN);
if (data->key_data) {
diff --git a/src/eap_peer/eap_tls_common.c b/src/eap_peer/eap_tls_common.c
index 0de131526a519..cb94c452efceb 100644
--- a/src/eap_peer/eap_tls_common.c
+++ b/src/eap_peer/eap_tls_common.c
@@ -1,6 +1,6 @@
/*
* EAP peer: EAP-TLS/PEAP/TTLS/FAST common functions
- * Copyright (c) 2004-2013, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -70,16 +70,22 @@ static void eap_tls_params_flags(struct tls_connection_params *params,
params->flags &= ~TLS_CONN_DISABLE_SESSION_TICKET;
if (os_strstr(txt, "tls_disable_tlsv1_0=1"))
params->flags |= TLS_CONN_DISABLE_TLSv1_0;
- if (os_strstr(txt, "tls_disable_tlsv1_0=0"))
+ if (os_strstr(txt, "tls_disable_tlsv1_0=0")) {
params->flags &= ~TLS_CONN_DISABLE_TLSv1_0;
+ params->flags |= TLS_CONN_ENABLE_TLSv1_0;
+ }
if (os_strstr(txt, "tls_disable_tlsv1_1=1"))
params->flags |= TLS_CONN_DISABLE_TLSv1_1;
- if (os_strstr(txt, "tls_disable_tlsv1_1=0"))
+ if (os_strstr(txt, "tls_disable_tlsv1_1=0")) {
params->flags &= ~TLS_CONN_DISABLE_TLSv1_1;
+ params->flags |= TLS_CONN_ENABLE_TLSv1_1;
+ }
if (os_strstr(txt, "tls_disable_tlsv1_2=1"))
params->flags |= TLS_CONN_DISABLE_TLSv1_2;
- if (os_strstr(txt, "tls_disable_tlsv1_2=0"))
+ if (os_strstr(txt, "tls_disable_tlsv1_2=0")) {
params->flags &= ~TLS_CONN_DISABLE_TLSv1_2;
+ params->flags |= TLS_CONN_ENABLE_TLSv1_2;
+ }
if (os_strstr(txt, "tls_disable_tlsv1_3=1"))
params->flags |= TLS_CONN_DISABLE_TLSv1_3;
if (os_strstr(txt, "tls_disable_tlsv1_3=0"))
@@ -102,14 +108,15 @@ static void eap_tls_params_flags(struct tls_connection_params *params,
static void eap_tls_params_from_conf1(struct tls_connection_params *params,
struct eap_peer_config *config)
{
- params->ca_cert = (char *) config->ca_cert;
- params->ca_path = (char *) config->ca_path;
- params->client_cert = (char *) config->client_cert;
- params->private_key = (char *) config->private_key;
- params->private_key_passwd = (char *) config->private_key_passwd;
- params->dh_file = (char *) config->dh_file;
- params->subject_match = (char *) config->subject_match;
- params->altsubject_match = (char *) config->altsubject_match;
+ params->ca_cert = config->ca_cert;
+ params->ca_path = config->ca_path;
+ params->client_cert = config->client_cert;
+ params->private_key = config->private_key;
+ params->private_key_passwd = config->private_key_passwd;
+ params->dh_file = config->dh_file;
+ params->subject_match = config->subject_match;
+ params->altsubject_match = config->altsubject_match;
+ params->check_cert_subject = config->check_cert_subject;
params->suffix_match = config->domain_suffix_match;
params->domain_match = config->domain_match;
params->engine = config->engine;
@@ -125,14 +132,15 @@ static void eap_tls_params_from_conf1(struct tls_connection_params *params,
static void eap_tls_params_from_conf2(struct tls_connection_params *params,
struct eap_peer_config *config)
{
- params->ca_cert = (char *) config->ca_cert2;
- params->ca_path = (char *) config->ca_path2;
- params->client_cert = (char *) config->client_cert2;
- params->private_key = (char *) config->private_key2;
- params->private_key_passwd = (char *) config->private_key2_passwd;
- params->dh_file = (char *) config->dh_file2;
- params->subject_match = (char *) config->subject_match2;
- params->altsubject_match = (char *) config->altsubject_match2;
+ params->ca_cert = config->ca_cert2;
+ params->ca_path = config->ca_path2;
+ params->client_cert = config->client_cert2;
+ params->private_key = config->private_key2;
+ params->private_key_passwd = config->private_key2_passwd;
+ params->dh_file = config->dh_file2;
+ params->subject_match = config->subject_match2;
+ params->altsubject_match = config->altsubject_match2;
+ params->check_cert_subject = config->check_cert_subject2;
params->suffix_match = config->domain_suffix_match2;
params->domain_match = config->domain_match2;
params->engine = config->engine2;
@@ -170,7 +178,9 @@ static int eap_tls_params_from_conf(struct eap_sm *sm,
* TLS v1.3 changes, so disable this by default for now. */
params->flags |= TLS_CONN_DISABLE_TLSv1_3;
}
- if (data->eap_type == EAP_TYPE_TLS) {
+ if (data->eap_type == EAP_TYPE_TLS ||
+ data->eap_type == EAP_UNAUTH_TLS_TYPE ||
+ data->eap_type == EAP_WFA_UNAUTH_TLS_TYPE) {
/* While the current EAP-TLS implementation is more or less
* complete for TLS v1.3, there has been no interoperability
* testing with other implementations, so disable for by default
@@ -339,6 +349,8 @@ void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data)
* @sm: Pointer to EAP state machine allocated with eap_peer_sm_init()
* @data: Data for TLS processing
* @label: Label string for deriving the keys, e.g., "client EAP encryption"
+ * @context: Optional extra upper-layer context (max len 2^16)
+ * @context_len: The length of the context value
* @len: Length of the key material to generate (usually 64 for MSK)
* Returns: Pointer to allocated key on success or %NULL on failure
*
@@ -347,9 +359,12 @@ void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data)
* different label to bind the key usage into the generated material.
*
* The caller is responsible for freeing the returned buffer.
+ *
+ * Note: To provide the RFC 5705 context, the context variable must be non-NULL.
*/
u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
- const char *label, size_t len)
+ const char *label, const u8 *context,
+ size_t context_len, size_t len)
{
u8 *out;
@@ -357,8 +372,8 @@ u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
if (out == NULL)
return NULL;
- if (tls_connection_export_key(data->ssl_ctx, data->conn, label, out,
- len)) {
+ if (tls_connection_export_key(data->ssl_ctx, data->conn, label,
+ context, context_len, out, len)) {
os_free(out);
return NULL;
}
@@ -388,10 +403,26 @@ u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm,
u8 *out;
if (eap_type == EAP_TYPE_TLS && data->tls_v13) {
- *len = 64;
- return eap_peer_tls_derive_key(sm, data,
- "EXPORTER_EAP_TLS_Session-Id",
- 64);
+ u8 *id, *method_id;
+
+ /* Session-Id = <EAP-Type> || Method-Id
+ * Method-Id = TLS-Exporter("EXPORTER_EAP_TLS_Method-Id",
+ * "", 64)
+ */
+ *len = 1 + 64;
+ id = os_malloc(*len);
+ if (!id)
+ return NULL;
+ method_id = eap_peer_tls_derive_key(
+ sm, data, "EXPORTER_EAP_TLS_Method-Id", NULL, 0, 64);
+ if (!method_id) {
+ os_free(id);
+ return NULL;
+ }
+ id[0] = eap_type;
+ os_memcpy(id + 1, method_id, 64);
+ os_free(method_id);
+ return id;
}
if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys) ||
diff --git a/src/eap_peer/eap_tls_common.h b/src/eap_peer/eap_tls_common.h
index 306e6a98bc3f7..5f825294d7871 100644
--- a/src/eap_peer/eap_tls_common.h
+++ b/src/eap_peer/eap_tls_common.h
@@ -99,7 +99,8 @@ int eap_peer_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
struct eap_peer_config *config, u8 eap_type);
void eap_peer_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data);
u8 * eap_peer_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
- const char *label, size_t len);
+ const char *label, const u8 *context,
+ size_t context_len, size_t len);
u8 * eap_peer_tls_derive_session_id(struct eap_sm *sm,
struct eap_ssl_data *data, u8 eap_type,
size_t *len);
diff --git a/src/eap_peer/eap_ttls.c b/src/eap_peer/eap_ttls.c
index f18788ce8cb5c..1c8dbe2b43310 100644
--- a/src/eap_peer/eap_ttls.c
+++ b/src/eap_peer/eap_ttls.c
@@ -196,8 +196,8 @@ static void eap_ttls_deinit(struct eap_sm *sm, void *priv)
eap_peer_tls_ssl_deinit(sm, &data->ssl);
eap_ttls_free_key(data);
os_free(data->session_id);
- wpabuf_free(data->pending_phase2_req);
- wpabuf_free(data->pending_resp);
+ wpabuf_clear_free(data->pending_phase2_req);
+ wpabuf_clear_free(data->pending_resp);
os_free(data);
}
@@ -248,7 +248,7 @@ static int eap_ttls_avp_encapsulate(struct wpabuf **resp, u32 avp_code,
msg = wpabuf_alloc(sizeof(struct ttls_avp) + wpabuf_len(*resp) + 4);
if (msg == NULL) {
- wpabuf_free(*resp);
+ wpabuf_clear_free(*resp);
*resp = NULL;
return -1;
}
@@ -258,7 +258,7 @@ static int eap_ttls_avp_encapsulate(struct wpabuf **resp, u32 avp_code,
os_memcpy(pos, wpabuf_head(*resp), wpabuf_len(*resp));
pos += wpabuf_len(*resp);
AVP_PAD(avp, pos);
- wpabuf_free(*resp);
+ wpabuf_clear_free(*resp);
wpabuf_put(msg, pos - avp);
*resp = msg;
return 0;
@@ -271,6 +271,7 @@ static int eap_ttls_v0_derive_key(struct eap_sm *sm,
eap_ttls_free_key(data);
data->key_data = eap_peer_tls_derive_key(sm, &data->ssl,
"ttls keying material",
+ NULL, 0,
EAP_TLS_KEY_LEN +
EAP_EMSK_LEN);
if (!data->key_data) {
@@ -303,7 +304,8 @@ static int eap_ttls_v0_derive_key(struct eap_sm *sm,
static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm,
struct eap_ttls_data *data, size_t len)
{
- return eap_peer_tls_derive_key(sm, &data->ssl, "ttls challenge", len);
+ return eap_peer_tls_derive_key(sm, &data->ssl, "ttls challenge",
+ NULL, 0, len);
}
#endif /* CONFIG_FIPS */
@@ -510,7 +512,7 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,
challenge = eap_ttls_implicit_challenge(
sm, data, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN + 1);
if (challenge == NULL) {
- wpabuf_free(msg);
+ wpabuf_clear_free(msg);
wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive "
"implicit challenge");
return -1;
@@ -529,7 +531,7 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,
*pos++ = 0; /* Flags */
if (os_get_random(pos, EAP_TTLS_MSCHAPV2_CHALLENGE_LEN) < 0) {
os_free(challenge);
- wpabuf_free(msg);
+ wpabuf_clear_free(msg);
wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to get "
"random data for peer challenge");
return -1;
@@ -543,7 +545,7 @@ static int eap_ttls_phase2_request_mschapv2(struct eap_sm *sm,
peer_challenge, pos, data->auth_response,
data->master_key)) {
os_free(challenge);
- wpabuf_free(msg);
+ wpabuf_clear_free(msg);
wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAPV2: Failed to derive "
"response");
return -1;
@@ -604,7 +606,7 @@ static int eap_ttls_phase2_request_mschap(struct eap_sm *sm,
challenge = eap_ttls_implicit_challenge(
sm, data, EAP_TTLS_MSCHAP_CHALLENGE_LEN + 1);
if (challenge == NULL) {
- wpabuf_free(msg);
+ wpabuf_clear_free(msg);
wpa_printf(MSG_ERROR, "EAP-TTLS/MSCHAP: Failed to derive "
"implicit challenge");
return -1;
@@ -628,7 +630,7 @@ static int eap_ttls_phase2_request_mschap(struct eap_sm *sm,
if (challenge_response(challenge, password, pos)) {
wpa_printf(MSG_ERROR,
"EAP-TTLS/MSCHAP: Failed derive password hash");
- wpabuf_free(msg);
+ wpabuf_clear_free(msg);
os_free(challenge);
return -1;
}
@@ -641,7 +643,7 @@ static int eap_ttls_phase2_request_mschap(struct eap_sm *sm,
pos)) {
wpa_printf(MSG_ERROR,
"EAP-TTLS/MSCHAP: Failed derive password");
- wpabuf_free(msg);
+ wpabuf_clear_free(msg);
os_free(challenge);
return -1;
}
@@ -760,7 +762,7 @@ static int eap_ttls_phase2_request_chap(struct eap_sm *sm,
challenge = eap_ttls_implicit_challenge(
sm, data, EAP_TTLS_CHAP_CHALLENGE_LEN + 1);
if (challenge == NULL) {
- wpabuf_free(msg);
+ wpabuf_clear_free(msg);
wpa_printf(MSG_ERROR, "EAP-TTLS/CHAP: Failed to derive "
"implicit challenge");
return -1;
@@ -1073,10 +1075,10 @@ static int eap_ttls_encrypt_response(struct eap_sm *sm,
resp, out_data)) {
wpa_printf(MSG_INFO, "EAP-TTLS: Failed to encrypt a Phase 2 "
"frame");
- wpabuf_free(resp);
+ wpabuf_clear_free(resp);
return -1;
}
- wpabuf_free(resp);
+ wpabuf_clear_free(resp);
return 0;
}
@@ -1297,7 +1299,7 @@ static int eap_ttls_process_decrypted(struct eap_sm *sm,
config->pending_req_otp ||
config->pending_req_new_password ||
config->pending_req_sim) {
- wpabuf_free(data->pending_phase2_req);
+ wpabuf_clear_free(data->pending_phase2_req);
data->pending_phase2_req = wpabuf_dup(in_decrypted);
}
@@ -1340,7 +1342,7 @@ static int eap_ttls_implicit_identity_request(struct eap_sm *sm,
* processing when EAP request is re-processed after
* user input.
*/
- wpabuf_free(data->pending_phase2_req);
+ wpabuf_clear_free(data->pending_phase2_req);
data->pending_phase2_req = wpabuf_alloc(0);
}
@@ -1413,7 +1415,7 @@ static int eap_ttls_decrypt(struct eap_sm *sm, struct eap_ttls_data *data,
in_decrypted = data->pending_phase2_req;
data->pending_phase2_req = NULL;
if (wpabuf_len(in_decrypted) == 0) {
- wpabuf_free(in_decrypted);
+ wpabuf_clear_free(in_decrypted);
return eap_ttls_implicit_identity_request(
sm, data, ret, identifier, out_data);
}
@@ -1449,7 +1451,7 @@ continue_req:
&parse, in_decrypted, out_data);
done:
- wpabuf_free(in_decrypted);
+ wpabuf_clear_free(in_decrypted);
os_free(parse.eapdata);
if (retval < 0) {
@@ -1509,7 +1511,7 @@ static int eap_ttls_process_handshake(struct eap_sm *sm,
if (sm->waiting_ext_cert_check) {
wpa_printf(MSG_DEBUG,
"EAP-TTLS: Waiting external server certificate validation");
- wpabuf_free(data->pending_resp);
+ wpabuf_clear_free(data->pending_resp);
data->pending_resp = *out_data;
*out_data = NULL;
return 0;
@@ -1543,7 +1545,7 @@ static int eap_ttls_process_handshake(struct eap_sm *sm,
/*
* Application data included in the handshake message.
*/
- wpabuf_free(data->pending_phase2_req);
+ wpabuf_clear_free(data->pending_phase2_req);
data->pending_phase2_req = *out_data;
*out_data = NULL;
res = eap_ttls_decrypt(sm, data, ret, identifier, in_data,
@@ -1646,7 +1648,7 @@ static struct wpabuf * eap_ttls_process(struct eap_sm *sm, void *priv,
/* FIX: what about res == -1? Could just move all error processing into
* the other functions and get rid of this res==1 case here. */
if (res == 1) {
- wpabuf_free(resp);
+ wpabuf_clear_free(resp);
return eap_peer_tls_build_ack(id, EAP_TYPE_TTLS,
data->ttls_version);
}
@@ -1669,9 +1671,9 @@ static void eap_ttls_deinit_for_reauth(struct eap_sm *sm, void *priv)
if (data->phase2_priv && data->phase2_method &&
data->phase2_method->deinit_for_reauth)
data->phase2_method->deinit_for_reauth(sm, data->phase2_priv);
- wpabuf_free(data->pending_phase2_req);
+ wpabuf_clear_free(data->pending_phase2_req);
data->pending_phase2_req = NULL;
- wpabuf_free(data->pending_resp);
+ wpabuf_clear_free(data->pending_resp);
data->pending_resp = NULL;
data->decision_succ = DECISION_FAIL;
#ifdef EAP_TNC
diff --git a/src/eap_peer/eap_wsc.c b/src/eap_peer/eap_wsc.c
index d140c88b8cdd8..92d5a02351304 100644
--- a/src/eap_peer/eap_wsc.c
+++ b/src/eap_peer/eap_wsc.c
@@ -255,6 +255,9 @@ static void * eap_wsc_init(struct eap_sm *sm)
cfg.new_ap_settings = &new_ap_settings;
}
+ if (os_strstr(phase1, "multi_ap=1"))
+ cfg.multi_ap_backhaul_sta = 1;
+
data->wps = wps_init(&cfg);
if (data->wps == NULL) {
os_free(data);
diff --git a/src/eap_server/eap.h b/src/eap_server/eap.h
index 4fbc661c22fe2..b130368b64da2 100644
--- a/src/eap_server/eap.h
+++ b/src/eap_server/eap.h
@@ -153,11 +153,14 @@ void eap_sm_pending_cb(struct eap_sm *sm);
int eap_sm_method_pending(struct eap_sm *sm);
const u8 * eap_get_identity(struct eap_sm *sm, size_t *len);
const char * eap_get_serial_num(struct eap_sm *sm);
+const char * eap_get_method(struct eap_sm *sm);
+const char * eap_get_imsi(struct eap_sm *sm);
struct eap_eapol_interface * eap_get_interface(struct eap_sm *sm);
void eap_server_clear_identity(struct eap_sm *sm);
void eap_server_mschap_rx_callback(struct eap_sm *sm, const char *source,
const u8 *username, size_t username_len,
const u8 *challenge, const u8 *response);
void eap_erp_update_identity(struct eap_sm *sm, const u8 *eap, size_t len);
+void eap_user_free(struct eap_user *user);
#endif /* EAP_H */
diff --git a/src/eap_server/eap_i.h b/src/eap_server/eap_i.h
index cf8a9f0d98e1f..1cade10bee554 100644
--- a/src/eap_server/eap_i.h
+++ b/src/eap_server/eap_i.h
@@ -160,6 +160,7 @@ struct eap_sm {
u8 *identity;
size_t identity_len;
char *serial_num;
+ char imsi[20];
/* Whether Phase 2 method should validate identity match */
int require_identity_match;
int lastId; /* Identifier used in the last EAP-Packet */
diff --git a/src/eap_server/eap_server.c b/src/eap_server/eap_server.c
index 38a1b5c9ee220..e8b36e13380db 100644
--- a/src/eap_server/eap_server.c
+++ b/src/eap_server/eap_server.c
@@ -25,9 +25,6 @@
#define EAP_MAX_AUTH_ROUNDS 50
-static void eap_user_free(struct eap_user *user);
-
-
/* EAP state machines are described in RFC 4137 */
static int eap_sm_calculateTimeout(struct eap_sm *sm, int retransCount,
@@ -1814,7 +1811,7 @@ int eap_server_sm_step(struct eap_sm *sm)
}
-static void eap_user_free(struct eap_user *user)
+void eap_user_free(struct eap_user *user)
{
if (user == NULL)
return;
@@ -2003,6 +2000,32 @@ const char * eap_get_serial_num(struct eap_sm *sm)
}
+/**
+ * eap_get_method - Get the used EAP method
+ * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
+ * Returns: Pointer to the method name or %NULL if not available
+ */
+const char * eap_get_method(struct eap_sm *sm)
+{
+ if (!sm || !sm->m)
+ return NULL;
+ return sm->m->name;
+}
+
+
+/**
+ * eap_get_imsi - Get IMSI of the user
+ * @sm: Pointer to EAP state machine allocated with eap_server_sm_init()
+ * Returns: Pointer to IMSI or %NULL if not available
+ */
+const char * eap_get_imsi(struct eap_sm *sm)
+{
+ if (!sm || sm->imsi[0] == '\0')
+ return NULL;
+ return sm->imsi;
+}
+
+
void eap_erp_update_identity(struct eap_sm *sm, const u8 *eap, size_t len)
{
#ifdef CONFIG_ERP
diff --git a/src/eap_server/eap_server_aka.c b/src/eap_server/eap_server_aka.c
index 175021163c1df..1bea706d4990e 100644
--- a/src/eap_server/eap_server_aka.c
+++ b/src/eap_server/eap_server_aka.c
@@ -796,6 +796,10 @@ static void eap_aka_fullauth(struct eap_sm *sm, struct eap_aka_data *data)
return;
}
+ if (data->permanent[0] == EAP_AKA_PERMANENT_PREFIX ||
+ data->permanent[0] == EAP_AKA_PRIME_PERMANENT_PREFIX)
+ os_strlcpy(sm->imsi, &data->permanent[1], sizeof(sm->imsi));
+
#ifdef EAP_SERVER_AKA_PRIME
if (data->eap_method == EAP_TYPE_AKA_PRIME) {
/* Note: AUTN = (SQN ^ AK) || AMF || MAC which gives us the
diff --git a/src/eap_server/eap_server_gpsk.c b/src/eap_server/eap_server_gpsk.c
index fb3d11748c8c2..bebb17f40aaa5 100644
--- a/src/eap_server/eap_server_gpsk.c
+++ b/src/eap_server/eap_server_gpsk.c
@@ -181,7 +181,7 @@ static struct wpabuf * eap_gpsk_build_gpsk_3(struct eap_sm *sm,
if (eap_gpsk_compute_mic(data->sk, data->sk_len, data->vendor,
data->specifier, start, pos - start, pos) < 0)
{
- os_free(req);
+ wpabuf_free(req);
eap_gpsk_state(data, FAILURE);
return NULL;
}
@@ -379,7 +379,7 @@ static void eap_gpsk_process_gpsk_2(struct eap_sm *sm,
data->specifier = WPA_GET_BE16(csuite->specifier);
wpa_printf(MSG_DEBUG, "EAP-GPSK: CSuite_Sel %d:%d",
data->vendor, data->specifier);
- pos += sizeof(*csuite);
+ pos += sizeof(*csuite);
if (end - pos < 2) {
wpa_printf(MSG_DEBUG, "EAP-GPSK: Too short message for "
diff --git a/src/eap_server/eap_server_mschapv2.c b/src/eap_server/eap_server_mschapv2.c
index 6c47bb636aab9..e9e03b0afb452 100644
--- a/src/eap_server/eap_server_mschapv2.c
+++ b/src/eap_server/eap_server_mschapv2.c
@@ -551,9 +551,13 @@ static u8 * eap_mschapv2_getKey(struct eap_sm *sm, void *priv, size_t *len)
if (key == NULL)
return NULL;
/* MSK = server MS-MPPE-Recv-Key | MS-MPPE-Send-Key */
- get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0, 1);
- get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
- MSCHAPV2_KEY_LEN, 1, 1);
+ if (get_asymetric_start_key(data->master_key, key, MSCHAPV2_KEY_LEN, 0,
+ 1) < 0 ||
+ get_asymetric_start_key(data->master_key, key + MSCHAPV2_KEY_LEN,
+ MSCHAPV2_KEY_LEN, 1, 1) < 0) {
+ os_free(key);
+ return NULL;
+ }
wpa_hexdump_key(MSG_DEBUG, "EAP-MSCHAPV2: Derived key", key, *len);
return key;
diff --git a/src/eap_server/eap_server_pax.c b/src/eap_server/eap_server_pax.c
index 3257789695cde..2e8c1a60c71fe 100644
--- a/src/eap_server/eap_server_pax.c
+++ b/src/eap_server/eap_server_pax.c
@@ -107,9 +107,14 @@ static struct wpabuf * eap_pax_build_std_1(struct eap_sm *sm,
data->rand.r.x, EAP_PAX_RAND_LEN);
pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
- eap_pax_mac(data->mac_id, (u8 *) "", 0,
- wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN,
- NULL, 0, NULL, 0, pos);
+ if (eap_pax_mac(data->mac_id, (u8 *) "", 0,
+ wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN,
+ NULL, 0, NULL, 0, pos) < 0) {
+ wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate ICV");
+ data->state = FAILURE;
+ wpabuf_free(req);
+ return NULL;
+ }
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
return req;
@@ -144,18 +149,28 @@ static struct wpabuf * eap_pax_build_std_3(struct eap_sm *sm,
wpabuf_put_be16(req, EAP_PAX_MAC_LEN);
pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
- eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
- data->rand.r.y, EAP_PAX_RAND_LEN,
- (u8 *) data->cid, data->cid_len, NULL, 0, pos);
+ if (eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
+ data->rand.r.y, EAP_PAX_RAND_LEN,
+ (u8 *) data->cid, data->cid_len, NULL, 0, pos) < 0) {
+ wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate MAC");
+ data->state = FAILURE;
+ wpabuf_free(req);
+ return NULL;
+ }
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: MAC_CK(B, CID)",
pos, EAP_PAX_MAC_LEN);
/* Optional ADE could be added here, if needed */
pos = wpabuf_put(req, EAP_PAX_MAC_LEN);
- eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
- wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN,
- NULL, 0, NULL, 0, pos);
+ if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
+ wpabuf_mhead(req), wpabuf_len(req) - EAP_PAX_ICV_LEN,
+ NULL, 0, NULL, 0, pos) < 0) {
+ wpa_printf(MSG_ERROR, "EAP-PAX: Failed to calculate ICV");
+ data->state = FAILURE;
+ wpabuf_free(req);
+ return NULL;
+ }
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
return req;
@@ -190,7 +205,7 @@ static Boolean eap_pax_check(struct eap_sm *sm, void *priv,
u8 icvbuf[EAP_PAX_ICV_LEN], *icv;
pos = eap_hdr_validate(EAP_VENDOR_IETF, EAP_TYPE_PAX, respData, &len);
- if (pos == NULL || len < sizeof(*resp)) {
+ if (pos == NULL || len < sizeof(*resp) + EAP_PAX_ICV_LEN) {
wpa_printf(MSG_INFO, "EAP-PAX: Invalid frame");
return TRUE;
}
@@ -264,11 +279,11 @@ static Boolean eap_pax_check(struct eap_sm *sm, void *priv,
}
icv = wpabuf_mhead_u8(respData) + mlen - EAP_PAX_ICV_LEN;
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", icv, EAP_PAX_ICV_LEN);
- eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
- wpabuf_mhead(respData),
- wpabuf_len(respData) - EAP_PAX_ICV_LEN,
- NULL, 0, NULL, 0, icvbuf);
- if (os_memcmp_const(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) {
+ if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
+ wpabuf_mhead(respData),
+ wpabuf_len(respData) - EAP_PAX_ICV_LEN,
+ NULL, 0, NULL, 0, icvbuf) < 0 ||
+ os_memcmp_const(icvbuf, icv, EAP_PAX_ICV_LEN) != 0) {
wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV");
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV",
icvbuf, EAP_PAX_ICV_LEN);
@@ -395,11 +410,11 @@ static void eap_pax_process_std_2(struct eap_sm *sm,
}
data->keys_set = 1;
- eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
- data->rand.r.x, EAP_PAX_RAND_LEN,
- data->rand.r.y, EAP_PAX_RAND_LEN,
- (u8 *) data->cid, data->cid_len, mac);
- if (os_memcmp_const(mac, pos, EAP_PAX_MAC_LEN) != 0) {
+ if (eap_pax_mac(data->mac_id, data->ck, EAP_PAX_CK_LEN,
+ data->rand.r.x, EAP_PAX_RAND_LEN,
+ data->rand.r.y, EAP_PAX_RAND_LEN,
+ (u8 *) data->cid, data->cid_len, mac) < 0 ||
+ os_memcmp_const(mac, pos, EAP_PAX_MAC_LEN) != 0) {
wpa_printf(MSG_INFO, "EAP-PAX: Invalid MAC_CK(A, B, CID) in "
"PAX_STD-2");
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected MAC_CK(A, B, CID)",
@@ -417,11 +432,11 @@ static void eap_pax_process_std_2(struct eap_sm *sm,
return;
}
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: ICV", pos, EAP_PAX_ICV_LEN);
- eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
- wpabuf_head(respData),
- wpabuf_len(respData) - EAP_PAX_ICV_LEN, NULL, 0, NULL, 0,
- icvbuf);
- if (os_memcmp_const(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) {
+ if (eap_pax_mac(data->mac_id, data->ick, EAP_PAX_ICK_LEN,
+ wpabuf_head(respData),
+ wpabuf_len(respData) - EAP_PAX_ICV_LEN, NULL, 0,
+ NULL, 0, icvbuf) < 0 ||
+ os_memcmp_const(icvbuf, pos, EAP_PAX_ICV_LEN) != 0) {
wpa_printf(MSG_INFO, "EAP-PAX: Invalid ICV in PAX_STD-2");
wpa_hexdump(MSG_MSGDUMP, "EAP-PAX: Expected ICV",
icvbuf, EAP_PAX_ICV_LEN);
diff --git a/src/eap_server/eap_server_peap.c b/src/eap_server/eap_server_peap.c
index 18d31b527fdd7..92c0e5ec97163 100644
--- a/src/eap_server/eap_server_peap.c
+++ b/src/eap_server/eap_server_peap.c
@@ -1,6 +1,6 @@
/*
* hostapd / EAP-PEAP (draft-josefsson-pppext-eap-tls-eap-10.txt)
- * Copyright (c) 2004-2008, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2004-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -324,13 +324,14 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data)
{
u8 *tk;
u8 isk[32], imck[60];
+ int res;
/*
* Tunnel key (TK) is the first 60 octets of the key generated by
* phase 1 of PEAP (based on TLS).
*/
tk = eap_server_tls_derive_key(sm, &data->ssl, "client EAP encryption",
- EAP_TLS_KEY_LEN);
+ NULL, 0, EAP_TLS_KEY_LEN);
if (tk == NULL)
return -1;
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TK", tk, 60);
@@ -358,9 +359,11 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data)
* in the end of the label just before ISK; is that just a typo?)
*/
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: TempKey", tk, 40);
- if (peap_prfplus(data->peap_version, tk, 40,
- "Inner Methods Compound Keys",
- isk, sizeof(isk), imck, sizeof(imck)) < 0) {
+ res = peap_prfplus(data->peap_version, tk, 40,
+ "Inner Methods Compound Keys",
+ isk, sizeof(isk), imck, sizeof(imck));
+ os_memset(isk, 0, sizeof(isk));
+ if (res < 0) {
os_free(tk);
return -1;
}
@@ -373,6 +376,7 @@ static int eap_peap_derive_cmk(struct eap_sm *sm, struct eap_peap_data *data)
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: IPMK (S-IPMKj)", data->ipmk, 40);
os_memcpy(data->cmk, imck + 40, 20);
wpa_hexdump_key(MSG_DEBUG, "EAP-PEAP: CMK (CMKj)", data->cmk, 20);
+ os_memset(imck, 0, sizeof(imck));
return 0;
}
@@ -756,7 +760,7 @@ static void eap_peap_process_phase2_tlv(struct eap_sm *sm,
} else {
eap_peap_state(data, FAILURE);
}
-
+
} else if (status == EAP_TLV_RESULT_FAILURE) {
wpa_printf(MSG_INFO, "EAP-PEAP: TLV Result - Failure "
"- requested %s", requested);
@@ -1322,14 +1326,17 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len)
"key");
}
+ os_memset(csk, 0, sizeof(csk));
+
return eapKeyData;
}
/* TODO: PEAPv1 - different label in some cases */
eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
- "client EAP encryption",
- EAP_TLS_KEY_LEN);
+ "client EAP encryption", NULL, 0,
+ EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
if (eapKeyData) {
+ os_memset(eapKeyData + EAP_TLS_KEY_LEN, 0, EAP_EMSK_LEN);
*len = EAP_TLS_KEY_LEN;
wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived key",
eapKeyData, EAP_TLS_KEY_LEN);
@@ -1341,6 +1348,40 @@ static u8 * eap_peap_getKey(struct eap_sm *sm, void *priv, size_t *len)
}
+static u8 * eap_peap_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
+{
+ struct eap_peap_data *data = priv;
+ u8 *eapKeyData, *emsk;
+
+ if (data->state != SUCCESS)
+ return NULL;
+
+ if (data->crypto_binding_used) {
+ /* [MS-PEAP] does not define EMSK derivation */
+ return NULL;
+ }
+
+ /* TODO: PEAPv1 - different label in some cases */
+ eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
+ "client EAP encryption", NULL, 0,
+ EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
+ if (eapKeyData) {
+ emsk = os_memdup(eapKeyData + EAP_TLS_KEY_LEN, EAP_EMSK_LEN);
+ bin_clear_free(eapKeyData, EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
+ if (!emsk)
+ return NULL;
+ *len = EAP_EMSK_LEN;
+ wpa_hexdump(MSG_DEBUG, "EAP-PEAP: Derived EMSK",
+ emsk, EAP_EMSK_LEN);
+ } else {
+ wpa_printf(MSG_DEBUG, "EAP-PEAP: Failed to derive EMSK");
+ emsk = NULL;
+ }
+
+ return emsk;
+}
+
+
static Boolean eap_peap_isSuccess(struct eap_sm *sm, void *priv)
{
struct eap_peap_data *data = priv;
@@ -1376,6 +1417,7 @@ int eap_server_peap_register(void)
eap->process = eap_peap_process;
eap->isDone = eap_peap_isDone;
eap->getKey = eap_peap_getKey;
+ eap->get_emsk = eap_peap_get_emsk;
eap->isSuccess = eap_peap_isSuccess;
eap->getSessionId = eap_peap_get_session_id;
diff --git a/src/eap_server/eap_server_pwd.c b/src/eap_server/eap_server_pwd.c
index d0fa54a3aba7d..e720a28c85bac 100644
--- a/src/eap_server/eap_server_pwd.c
+++ b/src/eap_server/eap_server_pwd.c
@@ -236,7 +236,7 @@ static void eap_pwd_build_commit_req(struct eap_sm *sm,
struct eap_pwd_data *data, u8 id)
{
struct crypto_bignum *mask = NULL;
- u8 *scalar = NULL, *element = NULL;
+ u8 *scalar, *element;
size_t prime_len, order_len;
wpa_printf(MSG_DEBUG, "EAP-pwd: Commit/Request");
@@ -261,18 +261,9 @@ static void eap_pwd_build_commit_req(struct eap_sm *sm,
goto fin;
}
- if (crypto_bignum_rand(data->private_value,
- crypto_ec_get_order(data->grp->group)) < 0 ||
- crypto_bignum_rand(mask,
- crypto_ec_get_order(data->grp->group)) < 0 ||
- crypto_bignum_add(data->private_value, mask, data->my_scalar) < 0 ||
- crypto_bignum_mod(data->my_scalar,
- crypto_ec_get_order(data->grp->group),
- data->my_scalar) < 0) {
- wpa_printf(MSG_INFO,
- "EAP-pwd (server): unable to get randomness");
+ if (eap_pwd_get_rand_mask(data->grp, data->private_value, mask,
+ data->my_scalar) < 0)
goto fin;
- }
if (crypto_ec_point_mul(data->grp->group, data->grp->pwe, mask,
data->my_element) < 0) {
@@ -288,22 +279,6 @@ static void eap_pwd_build_commit_req(struct eap_sm *sm,
goto fin;
}
- scalar = os_malloc(order_len);
- element = os_malloc(prime_len * 2);
- if (!scalar || !element) {
- wpa_printf(MSG_INFO, "EAP-PWD (server): data allocation fail");
- goto fin;
- }
-
- if (crypto_ec_point_to_bin(data->grp->group, data->my_element, element,
- element + prime_len) < 0) {
- wpa_printf(MSG_INFO, "EAP-PWD (server): point assignment "
- "fail");
- goto fin;
- }
-
- crypto_bignum_to_bin(data->my_scalar, scalar, order_len, order_len);
-
data->outbuf = wpabuf_alloc(2 * prime_len + order_len +
(data->salt ? 1 + data->salt_len : 0));
if (data->outbuf == NULL)
@@ -316,13 +291,18 @@ static void eap_pwd_build_commit_req(struct eap_sm *sm,
}
/* We send the element as (x,y) followed by the scalar */
- wpabuf_put_data(data->outbuf, element, 2 * prime_len);
- wpabuf_put_data(data->outbuf, scalar, order_len);
+ element = wpabuf_put(data->outbuf, 2 * prime_len);
+ scalar = wpabuf_put(data->outbuf, order_len);
+ crypto_bignum_to_bin(data->my_scalar, scalar, order_len, order_len);
+ if (crypto_ec_point_to_bin(data->grp->group, data->my_element, element,
+ element + prime_len) < 0) {
+ wpa_printf(MSG_INFO, "EAP-PWD (server): point assignment "
+ "fail");
+ goto fin;
+ }
fin:
crypto_bignum_deinit(mask, 1);
- os_free(scalar);
- os_free(element);
if (data->outbuf == NULL)
eap_pwd_state(data, FAILURE);
}
@@ -331,7 +311,7 @@ fin:
static void eap_pwd_build_confirm_req(struct eap_sm *sm,
struct eap_pwd_data *data, u8 id)
{
- struct crypto_hash *hash;
+ struct crypto_hash *hash = NULL;
u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr;
u16 grp;
size_t prime_len, order_len;
@@ -412,6 +392,7 @@ static void eap_pwd_build_confirm_req(struct eap_sm *sm,
/* all done with the random function */
eap_pwd_h_final(hash, conf);
+ hash = NULL;
os_memcpy(data->my_confirm, conf, SHA256_MAC_LEN);
data->outbuf = wpabuf_alloc(SHA256_MAC_LEN);
@@ -424,6 +405,7 @@ fin:
bin_clear_free(cruft, prime_len * 2);
if (data->outbuf == NULL)
eap_pwd_state(data, FAILURE);
+ eap_pwd_h_final(hash, NULL);
}
@@ -668,8 +650,7 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data,
const u8 *payload, size_t payload_len)
{
const u8 *ptr;
- struct crypto_bignum *cofactor = NULL;
- struct crypto_ec_point *K = NULL, *point = NULL;
+ struct crypto_ec_point *K = NULL;
int res = 0;
size_t prime_len, order_len;
@@ -687,50 +668,36 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data,
}
data->k = crypto_bignum_init();
- cofactor = crypto_bignum_init();
- point = crypto_ec_point_init(data->grp->group);
K = crypto_ec_point_init(data->grp->group);
- if (!data->k || !cofactor || !point || !K) {
+ if (!data->k || !K) {
wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation "
"fail");
goto fin;
}
- if (crypto_ec_cofactor(data->grp->group, cofactor) < 0) {
- wpa_printf(MSG_INFO, "EAP-PWD (server): unable to get "
- "cofactor for curve");
- goto fin;
- }
-
/* element, x then y, followed by scalar */
ptr = payload;
- data->peer_element = crypto_ec_point_from_bin(data->grp->group, ptr);
+ data->peer_element = eap_pwd_get_element(data->grp, ptr);
if (!data->peer_element) {
wpa_printf(MSG_INFO, "EAP-PWD (server): setting peer element "
"fail");
goto fin;
}
ptr += prime_len * 2;
- data->peer_scalar = crypto_bignum_init_set(ptr, order_len);
+ data->peer_scalar = eap_pwd_get_scalar(data->grp, ptr);
if (!data->peer_scalar) {
wpa_printf(MSG_INFO, "EAP-PWD (server): peer data allocation "
"fail");
goto fin;
}
- /* check to ensure peer's element is not in a small sub-group */
- if (!crypto_bignum_is_one(cofactor)) {
- if (crypto_ec_point_mul(data->grp->group, data->peer_element,
- cofactor, point) != 0) {
- wpa_printf(MSG_INFO, "EAP-PWD (server): cannot "
- "multiply peer element by order");
- goto fin;
- }
- if (crypto_ec_point_is_at_infinity(data->grp->group, point)) {
- wpa_printf(MSG_INFO, "EAP-PWD (server): peer element "
- "is at infinity!\n");
- goto fin;
- }
+ /* detect reflection attacks */
+ if (crypto_bignum_cmp(data->my_scalar, data->peer_scalar) == 0 ||
+ crypto_ec_point_cmp(data->grp->group, data->my_element,
+ data->peer_element) == 0) {
+ wpa_printf(MSG_INFO,
+ "EAP-PWD (server): detected reflection attack!");
+ goto fin;
}
/* compute the shared key, k */
@@ -745,18 +712,8 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data,
goto fin;
}
- /* ensure that the shared key isn't in a small sub-group */
- if (!crypto_bignum_is_one(cofactor)) {
- if (crypto_ec_point_mul(data->grp->group, K, cofactor,
- K) != 0) {
- wpa_printf(MSG_INFO, "EAP-PWD (server): cannot "
- "multiply shared key point by order!\n");
- goto fin;
- }
- }
-
/*
- * This check is strictly speaking just for the case above where
+ * This check is strictly speaking just for the case where
* co-factor > 1 but it was suggested that even though this is probably
* never going to happen it is a simple and safe check "just to be
* sure" so let's be safe.
@@ -775,8 +732,6 @@ eap_pwd_process_commit_resp(struct eap_sm *sm, struct eap_pwd_data *data,
fin:
crypto_ec_point_deinit(K, 1);
- crypto_ec_point_deinit(point, 1);
- crypto_bignum_deinit(cofactor, 1);
if (res)
eap_pwd_state(data, PWD_Confirm_Req);
@@ -789,7 +744,7 @@ static void
eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data,
const u8 *payload, size_t payload_len)
{
- struct crypto_hash *hash;
+ struct crypto_hash *hash = NULL;
u32 cs;
u16 grp;
u8 conf[SHA256_MAC_LEN], *cruft = NULL, *ptr;
@@ -864,6 +819,7 @@ eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data,
/* all done */
eap_pwd_h_final(hash, conf);
+ hash = NULL;
ptr = (u8 *) payload;
if (os_memcmp_const(conf, ptr, SHA256_MAC_LEN)) {
@@ -883,6 +839,7 @@ eap_pwd_process_confirm_resp(struct eap_sm *sm, struct eap_pwd_data *data,
fin:
bin_clear_free(cruft, prime_len * 2);
+ eap_pwd_h_final(hash, NULL);
}
@@ -955,6 +912,12 @@ static void eap_pwd_process(struct eap_sm *sm, void *priv,
* the first and all intermediate fragments have the M bit set
*/
if (EAP_PWD_GET_MORE_BIT(lm_exch) || data->in_frag_pos) {
+ if (!data->inbuf) {
+ wpa_printf(MSG_DEBUG,
+ "EAP-pwd: No buffer for reassembly");
+ eap_pwd_state(data, FAILURE);
+ return;
+ }
if ((data->in_frag_pos + len) > wpabuf_size(data->inbuf)) {
wpa_printf(MSG_DEBUG, "EAP-pwd: Buffer overflow "
"attack detected! (%d+%d > %d)",
@@ -975,7 +938,7 @@ static void eap_pwd_process(struct eap_sm *sm, void *priv,
* last fragment won't have the M bit set (but we're obviously
* buffering fragments so that's how we know it's the last)
*/
- if (data->in_frag_pos) {
+ if (data->in_frag_pos && data->inbuf) {
pos = wpabuf_head_u8(data->inbuf);
len = data->in_frag_pos;
wpa_printf(MSG_DEBUG, "EAP-pwd: Last fragment, %d bytes",
@@ -1075,14 +1038,6 @@ static u8 * eap_pwd_get_session_id(struct eap_sm *sm, void *priv, size_t *len)
int eap_server_pwd_register(void)
{
struct eap_method *eap;
- struct timeval tp;
- struct timezone tz;
- u32 sr;
-
- sr = 0xdeaddada;
- (void) gettimeofday(&tp, &tz);
- sr ^= (tp.tv_sec ^ tp.tv_usec);
- srandom(sr);
eap = eap_server_method_alloc(EAP_SERVER_METHOD_INTERFACE_VERSION,
EAP_VENDOR_IETF, EAP_TYPE_PWD,
diff --git a/src/eap_server/eap_server_sake.c b/src/eap_server/eap_server_sake.c
index 66183f5f5c042..2fc2c0575a94e 100644
--- a/src/eap_server/eap_server_sake.c
+++ b/src/eap_server/eap_server_sake.c
@@ -204,7 +204,7 @@ static struct wpabuf * eap_sake_build_confirm(struct eap_sm *sm,
{
wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
data->state = FAILURE;
- os_free(msg);
+ wpabuf_free(msg);
return NULL;
}
@@ -340,16 +340,25 @@ static void eap_sake_process_challenge(struct eap_sm *sm,
data->state = FAILURE;
return;
}
- eap_sake_derive_keys(sm->user->password,
- sm->user->password + EAP_SAKE_ROOT_SECRET_LEN,
- data->rand_s, data->rand_p,
- (u8 *) &data->tek, data->msk, data->emsk);
-
- eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
- sm->server_id, sm->server_id_len,
- data->peerid, data->peerid_len, 1,
- wpabuf_head(respData), wpabuf_len(respData),
- attr.mic_p, mic_p);
+ if (eap_sake_derive_keys(sm->user->password,
+ sm->user->password + EAP_SAKE_ROOT_SECRET_LEN,
+ data->rand_s, data->rand_p,
+ (u8 *) &data->tek, data->msk,
+ data->emsk) < 0) {
+ wpa_printf(MSG_INFO, "EAP-SAKE: Failed to derive keys");
+ data->state = FAILURE;
+ return;
+ }
+
+ if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
+ sm->server_id, sm->server_id_len,
+ data->peerid, data->peerid_len, 1,
+ wpabuf_head(respData), wpabuf_len(respData),
+ attr.mic_p, mic_p) < 0) {
+ wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
+ data->state = FAILURE;
+ return;
+ }
if (os_memcmp_const(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) {
wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P");
eap_sake_state(data, FAILURE);
@@ -382,11 +391,14 @@ static void eap_sake_process_confirm(struct eap_sm *sm,
return;
}
- eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
- sm->server_id, sm->server_id_len,
- data->peerid, data->peerid_len, 1,
- wpabuf_head(respData), wpabuf_len(respData),
- attr.mic_p, mic_p);
+ if (eap_sake_compute_mic(data->tek.auth, data->rand_s, data->rand_p,
+ sm->server_id, sm->server_id_len,
+ data->peerid, data->peerid_len, 1,
+ wpabuf_head(respData), wpabuf_len(respData),
+ attr.mic_p, mic_p) < 0) {
+ wpa_printf(MSG_INFO, "EAP-SAKE: Failed to compute MIC");
+ return;
+ }
if (os_memcmp_const(attr.mic_p, mic_p, EAP_SAKE_MIC_LEN) != 0) {
wpa_printf(MSG_INFO, "EAP-SAKE: Incorrect AT_MIC_P");
eap_sake_state(data, FAILURE);
diff --git a/src/eap_server/eap_server_sim.c b/src/eap_server/eap_server_sim.c
index 10637d4c66b90..128782735fb38 100644
--- a/src/eap_server/eap_server_sim.c
+++ b/src/eap_server/eap_server_sim.c
@@ -535,6 +535,9 @@ skip_id_update:
goto failed;
}
+ if (data->permanent[0] == EAP_SIM_PERMANENT_PREFIX)
+ os_strlcpy(sm->imsi, &data->permanent[1], sizeof(sm->imsi));
+
identity_len = sm->identity_len;
while (identity_len > 0 && sm->identity[identity_len - 1] == '\0') {
wpa_printf(MSG_DEBUG, "EAP-SIM: Workaround - drop last null "
diff --git a/src/eap_server/eap_server_tls.c b/src/eap_server/eap_server_tls.c
index 8b9e53c61d799..357e72a825f6d 100644
--- a/src/eap_server/eap_server_tls.c
+++ b/src/eap_server/eap_server_tls.c
@@ -22,6 +22,7 @@ struct eap_tls_data {
enum { START, CONTINUE, SUCCESS, FAILURE } state;
int established;
u8 eap_type;
+ int phase2;
};
@@ -85,6 +86,8 @@ static void * eap_tls_init(struct eap_sm *sm)
data->eap_type = EAP_TYPE_TLS;
+ data->phase2 = sm->init_phase2;
+
return data;
}
@@ -202,6 +205,20 @@ check_established:
wpa_printf(MSG_DEBUG, "EAP-TLS: Done");
eap_tls_state(data, SUCCESS);
eap_tls_valid_session(sm, data);
+ if (sm->serial_num) {
+ char user[128];
+ int user_len;
+
+ user_len = os_snprintf(user, sizeof(user), "cert-%s",
+ sm->serial_num);
+ if (eap_user_get(sm, (const u8 *) user, user_len,
+ data->phase2) < 0)
+ wpa_printf(MSG_DEBUG,
+ "EAP-TLS: No user entry found based on the serial number of the client certificate ");
+ else
+ wpa_printf(MSG_DEBUG,
+ "EAP-TLS: Updated user entry based on the serial number of the client certificate ");
+ }
}
return res;
@@ -288,6 +305,8 @@ static void eap_tls_process(struct eap_sm *sm, void *priv,
"EAP-TLS: Resuming previous session");
eap_tls_state(data, SUCCESS);
tls_connection_set_success_data_resumed(data->ssl.conn);
+ /* TODO: Cache serial number with session and update EAP user
+ * information based on the cached serial number */
}
@@ -312,6 +331,7 @@ static u8 * eap_tls_getKey(struct eap_sm *sm, void *priv, size_t *len)
else
label = "client EAP encryption";
eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, label,
+ NULL, 0,
EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
if (eapKeyData) {
*len = EAP_TLS_KEY_LEN;
@@ -340,6 +360,7 @@ static u8 * eap_tls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
else
label = "client EAP encryption";
eapKeyData = eap_server_tls_derive_key(sm, &data->ssl, label,
+ NULL, 0,
EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
if (eapKeyData) {
emsk = os_malloc(EAP_EMSK_LEN);
diff --git a/src/eap_server/eap_server_tls_common.c b/src/eap_server/eap_server_tls_common.c
index 0ae7867fccf7f..0eca0ff774094 100644
--- a/src/eap_server/eap_server_tls_common.c
+++ b/src/eap_server/eap_server_tls_common.c
@@ -107,7 +107,8 @@ void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data)
u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
- const char *label, size_t len)
+ const char *label, const u8 *context,
+ size_t context_len, size_t len)
{
u8 *out;
@@ -115,8 +116,8 @@ u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
if (out == NULL)
return NULL;
- if (tls_connection_export_key(sm->ssl_ctx, data->conn, label, out,
- len)) {
+ if (tls_connection_export_key(sm->ssl_ctx, data->conn, label,
+ context, context_len, out, len)) {
os_free(out);
return NULL;
}
@@ -146,10 +147,26 @@ u8 * eap_server_tls_derive_session_id(struct eap_sm *sm,
u8 *out;
if (eap_type == EAP_TYPE_TLS && data->tls_v13) {
- *len = 64;
- return eap_server_tls_derive_key(sm, data,
- "EXPORTER_EAP_TLS_Session-Id",
- 64);
+ u8 *id, *method_id;
+
+ /* Session-Id = <EAP-Type> || Method-Id
+ * Method-Id = TLS-Exporter("EXPORTER_EAP_TLS_Method-Id",
+ * "", 64)
+ */
+ *len = 1 + 64;
+ id = os_malloc(*len);
+ if (!id)
+ return NULL;
+ method_id = eap_server_tls_derive_key(
+ sm, data, "EXPORTER_EAP_TLS_Method-Id", NULL, 0, 64);
+ if (!method_id) {
+ os_free(id);
+ return NULL;
+ }
+ id[0] = eap_type;
+ os_memcpy(id + 1, method_id, 64);
+ os_free(method_id);
+ return id;
}
if (tls_connection_get_random(sm->ssl_ctx, data->conn, &keys))
diff --git a/src/eap_server/eap_server_ttls.c b/src/eap_server/eap_server_ttls.c
index b14996b0b9909..52bff8afe42d8 100644
--- a/src/eap_server/eap_server_ttls.c
+++ b/src/eap_server/eap_server_ttls.c
@@ -332,7 +332,7 @@ static u8 * eap_ttls_implicit_challenge(struct eap_sm *sm,
struct eap_ttls_data *data, size_t len)
{
return eap_server_tls_derive_key(sm, &data->ssl, "ttls challenge",
- len);
+ NULL, 0, len);
}
@@ -1268,7 +1268,7 @@ static u8 * eap_ttls_getKey(struct eap_sm *sm, void *priv, size_t *len)
return NULL;
eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
- "ttls keying material",
+ "ttls keying material", NULL, 0,
EAP_TLS_KEY_LEN);
if (eapKeyData) {
*len = EAP_TLS_KEY_LEN;
@@ -1310,7 +1310,7 @@ static u8 * eap_ttls_get_emsk(struct eap_sm *sm, void *priv, size_t *len)
return NULL;
eapKeyData = eap_server_tls_derive_key(sm, &data->ssl,
- "ttls keying material",
+ "ttls keying material", NULL, 0,
EAP_TLS_KEY_LEN + EAP_EMSK_LEN);
if (eapKeyData) {
emsk = os_malloc(EAP_EMSK_LEN);
diff --git a/src/eap_server/eap_tls_common.h b/src/eap_server/eap_tls_common.h
index 31f6e72d779a1..0b04983adb0e9 100644
--- a/src/eap_server/eap_tls_common.h
+++ b/src/eap_server/eap_tls_common.h
@@ -78,7 +78,8 @@ int eap_server_tls_ssl_init(struct eap_sm *sm, struct eap_ssl_data *data,
int verify_peer, int eap_type);
void eap_server_tls_ssl_deinit(struct eap_sm *sm, struct eap_ssl_data *data);
u8 * eap_server_tls_derive_key(struct eap_sm *sm, struct eap_ssl_data *data,
- const char *label, size_t len);
+ const char *label, const u8 *context,
+ size_t context_len, size_t len);
u8 * eap_server_tls_derive_session_id(struct eap_sm *sm,
struct eap_ssl_data *data, u8 eap_type,
size_t *len);
diff --git a/src/eapol_supp/eapol_supp_sm.c b/src/eapol_supp/eapol_supp_sm.c
index 9f029b0d3710f..a0f27fd2bdb29 100644
--- a/src/eapol_supp/eapol_supp_sm.c
+++ b/src/eapol_supp/eapol_supp_sm.c
@@ -189,8 +189,9 @@ static void eapol_port_timers_tick(void *eloop_ctx, void *timeout_ctx)
}
if (sm->authWhile | sm->heldWhile | sm->startWhen | sm->idleWhile) {
- eloop_register_timeout(1, 0, eapol_port_timers_tick, eloop_ctx,
- sm);
+ if (eloop_register_timeout(1, 0, eapol_port_timers_tick,
+ eloop_ctx, sm) < 0)
+ sm->timer_tick_enabled = 0;
} else {
wpa_printf(MSG_DEBUG, "EAPOL: disable timer tick");
sm->timer_tick_enabled = 0;
@@ -204,9 +205,9 @@ static void eapol_enable_timer_tick(struct eapol_sm *sm)
if (sm->timer_tick_enabled)
return;
wpa_printf(MSG_DEBUG, "EAPOL: enable timer tick");
- sm->timer_tick_enabled = 1;
eloop_cancel_timeout(eapol_port_timers_tick, NULL, sm);
- eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm);
+ if (eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm) == 0)
+ sm->timer_tick_enabled = 1;
}
@@ -2141,8 +2142,8 @@ struct eapol_sm *eapol_sm_init(struct eapol_ctx *ctx)
sm->initialize = FALSE;
eapol_sm_step(sm);
- sm->timer_tick_enabled = 1;
- eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm);
+ if (eloop_register_timeout(1, 0, eapol_port_timers_tick, NULL, sm) == 0)
+ sm->timer_tick_enabled = 1;
return sm;
}
diff --git a/src/fst/fst.h b/src/fst/fst.h
index 0c0e435b974b5..296749120b2a1 100644
--- a/src/fst/fst.h
+++ b/src/fst/fst.h
@@ -19,10 +19,18 @@
#define US_IN_MS 1000
#define LLT_UNIT_US 32 /* See 10.32.2.2 Transitioning between states */
-#define FST_LLT_MS_TO_VAL(m) (((u32) (m)) * US_IN_MS / LLT_UNIT_US)
-#define FST_LLT_VAL_TO_MS(v) (((u32) (v)) * LLT_UNIT_US / US_IN_MS)
-
-#define FST_MAX_LLT_MS FST_LLT_VAL_TO_MS(-1)
+/*
+ * These were originally
+ * #define FST_LLT_MS_TO_VAL(m) (((u32) (m)) * US_IN_MS / LLT_UNIT_US)
+ * #define FST_LLT_VAL_TO_MS(v) (((u32) (v)) * LLT_UNIT_US / US_IN_MS)
+ * #define FST_MAX_LLT_MS FST_LLT_VAL_TO_MS(-1)
+ * but those can overflow 32-bit unsigned integer, so use alternative defines
+ * to avoid undefined behavior with such overflow.
+ * LLT_UNIT_US/US_IN_MS = 32/1000 = 4/125
+ */
+#define FST_LLT_MS_TO_VAL(m) (((u32) (m)) * 125 / 4)
+#define FST_LLT_VAL_TO_MS(v) (((u32) (v)) * 4 / 125)
+#define FST_MAX_LLT_MS (((u32) -1) / 4)
#define FST_MAX_PRIO_VALUE ((u8) -1)
#define FST_MAX_GROUP_ID_LEN IFNAMSIZ
diff --git a/src/lib.rules b/src/lib.rules
index 0c79d992a6aa4..4ec4711e36acf 100644
--- a/src/lib.rules
+++ b/src/lib.rules
@@ -6,6 +6,11 @@ ifndef CFLAGS
CFLAGS = -MMD -O2 -Wall -g
endif
+ifdef TEST_FUZZ
+CFLAGS += -DCONFIG_NO_RANDOM_POOL
+CFLAGS += -DTEST_FUZZ
+endif
+
CFLAGS += -I.. -I../utils
diff --git a/src/p2p/p2p.c b/src/p2p/p2p.c
index b4660c4f9616e..157bf891c4ab8 100644
--- a/src/p2p/p2p.c
+++ b/src/p2p/p2p.c
@@ -1076,7 +1076,7 @@ static int p2p_run_after_scan(struct p2p_data *p2p)
p2p->after_scan_tx->bssid,
(u8 *) (p2p->after_scan_tx + 1),
p2p->after_scan_tx->len,
- p2p->after_scan_tx->wait_time);
+ p2p->after_scan_tx->wait_time, NULL);
os_free(p2p->after_scan_tx);
p2p->after_scan_tx = NULL;
return 1;
@@ -1172,9 +1172,9 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout,
u8 seek_count, const char **seek, int freq)
{
int res;
+ struct os_reltime start;
p2p_dbg(p2p, "Starting find (type=%d)", type);
- os_get_reltime(&p2p->find_start);
if (p2p->p2p_scan_running) {
p2p_dbg(p2p, "p2p_scan is already running");
}
@@ -1258,6 +1258,7 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout,
if (timeout)
eloop_register_timeout(timeout, 0, p2p_find_timeout,
p2p, NULL);
+ os_get_reltime(&start);
switch (type) {
case P2P_FIND_START_WITH_FULL:
if (freq > 0) {
@@ -1289,6 +1290,9 @@ int p2p_find(struct p2p_data *p2p, unsigned int timeout,
return -1;
}
+ if (!res)
+ p2p->find_start = start;
+
if (res != 0 && p2p->p2p_scan_running) {
p2p_dbg(p2p, "Failed to start p2p_scan - another p2p_scan was already running");
/* wait for the previous p2p_scan to complete */
@@ -1470,7 +1474,8 @@ static void p2p_prepare_channel_best(struct p2p_data *p2p)
p2p->op_channel = p2p->cfg->op_channel;
} else if (p2p_channel_random_social(&p2p->cfg->channels,
&p2p->op_reg_class,
- &p2p->op_channel) == 0) {
+ &p2p->op_channel,
+ NULL, NULL) == 0) {
p2p_dbg(p2p, "Select random available social channel (op_class %u channel %u) as operating channel preference",
p2p->op_reg_class, p2p->op_channel);
} else {
@@ -2456,7 +2461,7 @@ p2p_reply_probe(struct p2p_data *p2p, const u8 *addr, const u8 *dst,
if (msg.wps_attributes &&
!p2p_match_dev_type(p2p, msg.wps_attributes)) {
/* No match with Requested Device Type */
- p2p_dbg(p2p, "Probe Req requestred Device Type did not match - ignore it");
+ p2p_dbg(p2p, "Probe Req requested Device Type did not match - ignore it");
p2p_parse_free(&msg);
return P2P_PREQ_NOT_PROCESSED;
}
@@ -4764,9 +4769,12 @@ void p2p_set_managed_oper(struct p2p_data *p2p, int enabled)
int p2p_config_get_random_social(struct p2p_config *p2p, u8 *op_class,
- u8 *op_channel)
+ u8 *op_channel,
+ struct wpa_freq_range_list *avoid_list,
+ struct wpa_freq_range_list *disallow_list)
{
- return p2p_channel_random_social(&p2p->channels, op_class, op_channel);
+ return p2p_channel_random_social(&p2p->channels, op_class, op_channel,
+ avoid_list, disallow_list);
}
@@ -4965,6 +4973,8 @@ int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
const u8 *src, const u8 *bssid, const u8 *buf,
size_t len, unsigned int wait_time)
{
+ int res, scheduled;
+
if (p2p->p2p_scan_running) {
p2p_dbg(p2p, "Delay Action frame TX until p2p_scan completes");
if (p2p->after_scan_tx) {
@@ -4985,8 +4995,16 @@ int p2p_send_action(struct p2p_data *p2p, unsigned int freq, const u8 *dst,
return 0;
}
- return p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, dst, src, bssid,
- buf, len, wait_time);
+ res = p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, dst, src, bssid,
+ buf, len, wait_time, &scheduled);
+ if (res == 0 && scheduled && p2p->in_listen && freq > 0 &&
+ (unsigned int) p2p->drv_in_listen != freq) {
+ p2p_dbg(p2p,
+ "Stop listen on %d MHz to allow a frame to be sent immediately on %d MHz",
+ p2p->drv_in_listen, freq);
+ p2p_stop_listen_for_freq(p2p, freq);
+ }
+ return res;
}
diff --git a/src/p2p/p2p.h b/src/p2p/p2p.h
index fac5ce05ae6e1..425b037be5158 100644
--- a/src/p2p/p2p.h
+++ b/src/p2p/p2p.h
@@ -104,6 +104,11 @@ struct p2p_go_neg_results {
unsigned int vht_center_freq2;
/**
+ * he - Indicates if IEEE 802.11ax HE is enabled
+ */
+ int he;
+
+ /**
* ssid - SSID of the group
*/
u8 ssid[SSID_MAX_LEN];
@@ -660,6 +665,8 @@ struct p2p_config {
* @buf: Frame body (starting from Category field)
* @len: Length of buf in octets
* @wait_time: How many msec to wait for a response frame
+ * @scheduled: Return value indicating whether the transmissions was
+ * scheduled to happen once the radio is available
* Returns: 0 on success, -1 on failure
*
* The Action frame may not be transmitted immediately and the status
@@ -670,7 +677,7 @@ struct p2p_config {
*/
int (*send_action)(void *ctx, unsigned int freq, const u8 *dst,
const u8 *src, const u8 *bssid, const u8 *buf,
- size_t len, unsigned int wait_time);
+ size_t len, unsigned int wait_time, int *scheduled);
/**
* send_action_done - Notify that Action frame sequence was completed
@@ -2005,6 +2012,8 @@ void p2p_set_managed_oper(struct p2p_data *p2p, int enabled);
* @p2p: P2P config
* @op_class: Selected operating class
* @op_channel: Selected social channel
+ * @avoid_list: Channel ranges to try to avoid or %NULL
+ * @disallow_list: Channel ranges to discard or %NULL
* Returns: 0 on success, -1 on failure
*
* This function is used before p2p_init is called. A random social channel
@@ -2012,7 +2021,9 @@ void p2p_set_managed_oper(struct p2p_data *p2p, int enabled);
* returned on success.
*/
int p2p_config_get_random_social(struct p2p_config *p2p, u8 *op_class,
- u8 *op_channel);
+ u8 *op_channel,
+ struct wpa_freq_range_list *avoid_list,
+ struct wpa_freq_range_list *disallow_list);
int p2p_set_listen_channel(struct p2p_data *p2p, u8 reg_class, u8 channel,
u8 forced);
diff --git a/src/p2p/p2p_build.c b/src/p2p/p2p_build.c
index 2882c6ad02e7e..63eb2e84c3762 100644
--- a/src/p2p/p2p_build.c
+++ b/src/p2p/p2p_build.c
@@ -802,7 +802,7 @@ int p2p_build_wps_ie(struct p2p_data *p2p, struct wpabuf *buf, int pw_id,
wpabuf_put_be16(buf, p2p->cfg->config_methods);
}
- if (wps_build_wfa_ext(buf, 0, NULL, 0) < 0)
+ if (wps_build_wfa_ext(buf, 0, NULL, 0, 0) < 0)
return -1;
if (all_attr && p2p->cfg->num_sec_dev_types) {
diff --git a/src/p2p/p2p_group.c b/src/p2p/p2p_group.c
index 16c28a0d95ee7..aa18af6c16c6b 100644
--- a/src/p2p/p2p_group.c
+++ b/src/p2p/p2p_group.c
@@ -941,7 +941,8 @@ int p2p_group_go_discover(struct p2p_group *group, const u8 *dev_id,
if (p2p->cfg->send_action(p2p->cfg->cb_ctx, freq, m->addr,
group->cfg->interface_addr,
group->cfg->interface_addr,
- wpabuf_head(req), wpabuf_len(req), 200) < 0)
+ wpabuf_head(req), wpabuf_len(req), 200, NULL)
+ < 0)
{
p2p_dbg(p2p, "Failed to send Action frame");
}
diff --git a/src/p2p/p2p_i.h b/src/p2p/p2p_i.h
index 6a4d751c090ab..d2c55c9976c2e 100644
--- a/src/p2p/p2p_i.h
+++ b/src/p2p/p2p_i.h
@@ -707,7 +707,9 @@ void p2p_channels_dump(struct p2p_data *p2p, const char *title,
int p2p_channel_select(struct p2p_channels *chans, const int *classes,
u8 *op_class, u8 *op_channel);
int p2p_channel_random_social(struct p2p_channels *chans, u8 *op_class,
- u8 *op_channel);
+ u8 *op_channel,
+ struct wpa_freq_range_list *avoid_list,
+ struct wpa_freq_range_list *disallow_list);
/* p2p_parse.c */
void p2p_copy_filter_devname(char *dst, size_t dst_len,
diff --git a/src/p2p/p2p_invitation.c b/src/p2p/p2p_invitation.c
index bbba001a7c930..77d662a47ae24 100644
--- a/src/p2p/p2p_invitation.c
+++ b/src/p2p/p2p_invitation.c
@@ -488,7 +488,7 @@ void p2p_process_invitation_resp(struct p2p_data *p2p, const u8 *sa,
if (*msg.status == P2P_SC_FAIL_NO_COMMON_CHANNELS &&
p2p->retry_invite_req &&
p2p_channel_random_social(&p2p->cfg->channels, &p2p->op_reg_class,
- &p2p->op_channel) == 0) {
+ &p2p->op_channel, NULL, NULL) == 0) {
p2p->retry_invite_req = 0;
p2p->cfg->send_action_done(p2p->cfg->cb_ctx);
p2p->cfg->stop_listen(p2p->cfg->cb_ctx);
diff --git a/src/p2p/p2p_utils.c b/src/p2p/p2p_utils.c
index 2e2aa8ad06f08..1a62a44a2df39 100644
--- a/src/p2p/p2p_utils.c
+++ b/src/p2p/p2p_utils.c
@@ -413,17 +413,30 @@ int p2p_channel_select(struct p2p_channels *chans, const int *classes,
int p2p_channel_random_social(struct p2p_channels *chans, u8 *op_class,
- u8 *op_channel)
+ u8 *op_channel,
+ struct wpa_freq_range_list *avoid_list,
+ struct wpa_freq_range_list *disallow_list)
{
u8 chan[4];
unsigned int num_channels = 0;
- /* Try to find available social channels from 2.4 GHz */
- if (p2p_channels_includes(chans, 81, 1))
+ /* Try to find available social channels from 2.4 GHz.
+ * If the avoid_list includes any of the 2.4 GHz social channels, that
+ * channel is not allowed by p2p_channels_includes() rules. However, it
+ * is assumed to allow minimal traffic for P2P negotiation, so allow it
+ * here for social channel selection unless explicitly disallowed in the
+ * disallow_list. */
+ if (p2p_channels_includes(chans, 81, 1) ||
+ (freq_range_list_includes(avoid_list, 2412) &&
+ !freq_range_list_includes(disallow_list, 2412)))
chan[num_channels++] = 1;
- if (p2p_channels_includes(chans, 81, 6))
+ if (p2p_channels_includes(chans, 81, 6) ||
+ (freq_range_list_includes(avoid_list, 2437) &&
+ !freq_range_list_includes(disallow_list, 2437)))
chan[num_channels++] = 6;
- if (p2p_channels_includes(chans, 81, 11))
+ if (p2p_channels_includes(chans, 81, 11) ||
+ (freq_range_list_includes(avoid_list, 2462) &&
+ !freq_range_list_includes(disallow_list, 2462)))
chan[num_channels++] = 11;
/* Try to find available social channels from 60 GHz */
diff --git a/src/pae/ieee802_1x_cp.c b/src/pae/ieee802_1x_cp.c
index 360fcd3f5fcdf..1c4dc3e63c9ff 100644
--- a/src/pae/ieee802_1x_cp.c
+++ b/src/pae/ieee802_1x_cp.c
@@ -38,12 +38,10 @@ struct ieee802_1x_cp_sm {
/* Logon -> CP */
enum connect_type connect;
- u8 *authorization_data;
/* KaY -> CP */
Boolean chgd_server; /* clear by CP */
Boolean elected_self;
- u8 *authorization_data1;
enum confidentiality_offset cipher_offset;
u64 cipher_suite;
Boolean new_sak; /* clear by CP */
@@ -216,6 +214,10 @@ SM_STATE(CP, RECEIVE)
SM_ENTRY(CP, RECEIVE);
/* RECEIVE state machine not keep with Figure 12-2 in
* IEEE Std 802.1X-2010 */
+ if (sm->oki) {
+ ieee802_1x_kay_delete_sas(sm->kay, sm->oki);
+ os_free(sm->oki);
+ }
sm->oki = sm->lki;
sm->oan = sm->lan;
sm->otx = sm->ltx;
@@ -320,8 +322,11 @@ SM_STATE(CP, RETIRE)
SM_ENTRY(CP, RETIRE);
/* RETIRE state machine not keep with Figure 12-2 in
* IEEE Std 802.1X-2010 */
- os_free(sm->oki);
- sm->oki = NULL;
+ if (sm->oki) {
+ ieee802_1x_kay_delete_sas(sm->kay, sm->oki);
+ os_free(sm->oki);
+ sm->oki = NULL;
+ }
sm->orx = FALSE;
sm->otx = FALSE;
ieee802_1x_kay_set_old_sa_attr(sm->kay, sm->oki, sm->oan,
@@ -383,7 +388,8 @@ SM_STEP(CP)
if (!sm->elected_self)
SM_ENTER(CP, READY);
if (sm->elected_self &&
- (sm->all_receiving || !sm->transmit_when))
+ (sm->all_receiving || !sm->controlled_port_enabled ||
+ !sm->transmit_when))
SM_ENTER(CP, TRANSMIT);
break;
@@ -406,8 +412,8 @@ SM_STEP(CP)
case CP_READY:
if (sm->new_sak || changed_connect(sm))
- SM_ENTER(CP, RECEIVE);
- if (sm->server_transmitting)
+ SM_ENTER(CP, ABANDON);
+ if (sm->server_transmitting || !sm->controlled_port_enabled)
SM_ENTER(CP, TRANSMIT);
break;
case CP_ABANDON:
@@ -464,7 +470,6 @@ struct ieee802_1x_cp_sm * ieee802_1x_cp_sm_init(struct ieee802_1x_kay *kay)
sm->retire_delay = MKA_SAK_RETIRE_TIME;
sm->CP_state = CP_BEGIN;
sm->changed = FALSE;
- sm->authorization_data = NULL;
wpa_printf(MSG_DEBUG, "CP: state machine created");
@@ -476,7 +481,6 @@ struct ieee802_1x_cp_sm * ieee802_1x_cp_sm_init(struct ieee802_1x_kay *kay)
secy_cp_control_confidentiality_offset(sm->kay,
sm->confidentiality_offset);
- SM_ENTER(CP, INIT);
SM_STEP_RUN(CP);
return sm;
@@ -518,7 +522,6 @@ void ieee802_1x_cp_sm_deinit(struct ieee802_1x_cp_sm *sm)
eloop_cancel_timeout(ieee802_1x_cp_step_cb, sm, NULL);
os_free(sm->lki);
os_free(sm->oki);
- os_free(sm->authorization_data);
os_free(sm);
}
@@ -589,19 +592,6 @@ void ieee802_1x_cp_set_electedself(void *cp_ctx, Boolean status)
/**
- * ieee802_1x_cp_set_authorizationdata -
- */
-void ieee802_1x_cp_set_authorizationdata(void *cp_ctx, u8 *pdata, int len)
-{
- struct ieee802_1x_cp_sm *sm = cp_ctx;
- os_free(sm->authorization_data);
- sm->authorization_data = os_zalloc(len);
- if (sm->authorization_data)
- os_memcpy(sm->authorization_data, pdata, len);
-}
-
-
-/**
* ieee802_1x_cp_set_ciphersuite -
*/
void ieee802_1x_cp_set_ciphersuite(void *cp_ctx, u64 cs)
diff --git a/src/pae/ieee802_1x_cp.h b/src/pae/ieee802_1x_cp.h
index 695629e5c0bce..a357b278f40a8 100644
--- a/src/pae/ieee802_1x_cp.h
+++ b/src/pae/ieee802_1x_cp.h
@@ -25,7 +25,6 @@ void ieee802_1x_cp_connect_authenticated(void *cp_ctx);
void ieee802_1x_cp_connect_secure(void *cp_ctx);
void ieee802_1x_cp_signal_chgdserver(void *cp_ctx);
void ieee802_1x_cp_set_electedself(void *cp_ctx, Boolean status);
-void ieee802_1x_cp_set_authorizationdata(void *cp_ctx, u8 *pdata, int len);
void ieee802_1x_cp_set_ciphersuite(void *cp_ctx, u64 cs);
void ieee802_1x_cp_set_offset(void *cp_ctx, enum confidentiality_offset offset);
void ieee802_1x_cp_signal_newsak(void *cp_ctx);
diff --git a/src/pae/ieee802_1x_kay.c b/src/pae/ieee802_1x_kay.c
index cda23fcab41aa..b4455c8f4e081 100644
--- a/src/pae/ieee802_1x_kay.c
+++ b/src/pae/ieee802_1x_kay.c
@@ -1,5 +1,5 @@
/*
- * IEEE 802.1X-2010 Key Agree Protocol of PAE state machine
+ * IEEE 802.1X-2010 Key Agreement Protocol of PAE state machine
* Copyright (c) 2013, Qualcomm Atheros, Inc.
*
* This software may be distributed under the terms of the BSD license.
@@ -27,6 +27,9 @@
#define DEFAULT_ICV_LEN 16
#define MAX_ICV_LEN 32 /* 32 bytes, 256 bits */
+#define MAX_MISSING_SAK_USE 10 /* Accept up to 10 inbound MKPDUs without
+ * SAK-USE before dropping */
+
#define PENDING_PN_EXHAUSTION 0xC0000000
#define MKA_ALIGN_LENGTH(len) (((len) + 0x3) & ~0x3)
@@ -43,7 +46,6 @@ static struct macsec_ciphersuite cipher_suite_tbl[] = {
.name = CS_NAME_GCM_AES_128,
.capable = MACSEC_CAP_INTEG_AND_CONF_0_30_50,
.sak_len = DEFAULT_SA_KEY_LEN,
- .index = 0,
},
/* GCM-AES-256 */
{
@@ -51,7 +53,6 @@ static struct macsec_ciphersuite cipher_suite_tbl[] = {
.name = CS_NAME_GCM_AES_256,
.capable = MACSEC_CAP_INTEG_AND_CONF_0_30_50,
.sak_len = 32,
- .index = 1 /* index */
},
};
#define CS_TABLE_SIZE (ARRAY_SIZE(cipher_suite_tbl))
@@ -61,19 +62,13 @@ static struct mka_alg mka_alg_tbl[] = {
{
.parameter = MKA_ALGO_AGILITY_2009,
- /* 128-bit CAK, KEK, ICK, ICV */
- .cak_len = DEFAULT_ICV_LEN,
- .kek_len = DEFAULT_ICV_LEN,
- .ick_len = DEFAULT_ICV_LEN,
.icv_len = DEFAULT_ICV_LEN,
- .cak_trfm = ieee802_1x_cak_128bits_aes_cmac,
- .ckn_trfm = ieee802_1x_ckn_128bits_aes_cmac,
- .kek_trfm = ieee802_1x_kek_128bits_aes_cmac,
- .ick_trfm = ieee802_1x_ick_128bits_aes_cmac,
- .icv_hash = ieee802_1x_icv_128bits_aes_cmac,
-
- .index = 1,
+ .cak_trfm = ieee802_1x_cak_aes_cmac,
+ .ckn_trfm = ieee802_1x_ckn_aes_cmac,
+ .kek_trfm = ieee802_1x_kek_aes_cmac,
+ .ick_trfm = ieee802_1x_ick_aes_cmac,
+ .icv_hash = ieee802_1x_icv_aes_cmac,
},
};
#define MKA_ALG_TABLE_SIZE (ARRAY_SIZE(mka_alg_tbl))
@@ -109,6 +104,34 @@ static u8 get_mka_param_body_type(const void *body)
}
+static const char * mi_txt(const u8 *mi)
+{
+ static char txt[MI_LEN * 2 + 1];
+
+ wpa_snprintf_hex(txt, sizeof(txt), mi, MI_LEN);
+ return txt;
+}
+
+
+static const char * sci_txt(const struct ieee802_1x_mka_sci *sci)
+{
+ static char txt[ETH_ALEN * 3 + 1 + 5 + 1];
+
+ os_snprintf(txt, sizeof(txt), MACSTR "@%u",
+ MAC2STR(sci->addr), be_to_host16(sci->port));
+ return txt;
+}
+
+
+static const char * algo_agility_txt(const u8 *algo_agility)
+{
+ static char txt[4 * 2 + 1];
+
+ wpa_snprintf_hex(txt, sizeof(txt), algo_agility, 4);
+ return txt;
+}
+
+
/**
* ieee802_1x_mka_dump_basic_body -
*/
@@ -120,26 +143,25 @@ ieee802_1x_mka_dump_basic_body(struct ieee802_1x_mka_basic_body *body)
if (!body)
return;
+ /* IEEE Std 802.1X-2010, Figure 11-8 */
body_len = get_mka_param_body_len(body);
- wpa_printf(MSG_DEBUG, "*** MKA Basic Parameter set ***");
- wpa_printf(MSG_DEBUG, "\tVersion.......: %d", body->version);
- wpa_printf(MSG_DEBUG, "\tPriority......: %d", body->priority);
- wpa_printf(MSG_DEBUG, "\tKeySvr........: %d", body->key_server);
- wpa_printf(MSG_DEBUG, "\tMACSecDesired.: %d", body->macsec_desired);
- wpa_printf(MSG_DEBUG, "\tMACSecCapable.: %d", body->macsec_capability);
- wpa_printf(MSG_DEBUG, "\tBody Length...: %zu", body_len);
- wpa_printf(MSG_DEBUG, "\tSCI MAC.......: " MACSTR,
- MAC2STR(body->actor_sci.addr));
- wpa_printf(MSG_DEBUG, "\tSCI Port .....: %d",
- be_to_host16(body->actor_sci.port));
- wpa_hexdump(MSG_DEBUG, "\tMember Id.....:",
- body->actor_mi, sizeof(body->actor_mi));
- wpa_printf(MSG_DEBUG, "\tMessage Number: %d",
+ wpa_printf(MSG_DEBUG, "MKA Basic Parameter Set");
+ wpa_printf(MSG_DEBUG, "\tMKA Version Identifier: %d", body->version);
+ wpa_printf(MSG_DEBUG, "\tKey Server Priority: %d", body->priority);
+ wpa_printf(MSG_DEBUG, "\tKey Server: %d", body->key_server);
+ wpa_printf(MSG_DEBUG, "\tMACsec Desired: %d", body->macsec_desired);
+ wpa_printf(MSG_DEBUG, "\tMACsec Capability: %d",
+ body->macsec_capability);
+ wpa_printf(MSG_DEBUG, "\tParameter set body length: %zu", body_len);
+ wpa_printf(MSG_DEBUG, "\tSCI: %s", sci_txt(&body->actor_sci));
+ wpa_printf(MSG_DEBUG, "\tActor's Member Identifier: %s",
+ mi_txt(body->actor_mi));
+ wpa_printf(MSG_DEBUG, "\tActor's Message Number: %d",
be_to_host32(body->actor_mn));
- wpa_hexdump(MSG_DEBUG, "\tAlgo Agility..:",
- body->algo_agility, sizeof(body->algo_agility));
- wpa_hexdump_ascii(MSG_DEBUG, "\tCAK Name......:", body->ckn,
- body_len + MKA_HDR_LEN - sizeof(*body));
+ wpa_printf(MSG_DEBUG, "\tAlgorithm Agility: %s",
+ algo_agility_txt(body->algo_agility));
+ wpa_hexdump(MSG_DEBUG, "\tCAK Name", body->ckn,
+ body_len + MKA_HDR_LEN - sizeof(*body));
}
@@ -157,20 +179,21 @@ ieee802_1x_mka_dump_peer_body(struct ieee802_1x_mka_peer_body *body)
if (body == NULL)
return;
+ /* IEEE Std 802.1X-2010, Figure 11-9 */
body_len = get_mka_param_body_len(body);
if (body->type == MKA_LIVE_PEER_LIST) {
- wpa_printf(MSG_DEBUG, "*** Live Peer List ***");
- wpa_printf(MSG_DEBUG, "\tBody Length...: %zu", body_len);
+ wpa_printf(MSG_DEBUG, "Live Peer List parameter set");
+ wpa_printf(MSG_DEBUG, "\tBody Length: %zu", body_len);
} else if (body->type == MKA_POTENTIAL_PEER_LIST) {
- wpa_printf(MSG_DEBUG, "*** Potential Live Peer List ***");
- wpa_printf(MSG_DEBUG, "\tBody Length...: %zu", body_len);
+ wpa_printf(MSG_DEBUG, "Potential Peer List parameter set");
+ wpa_printf(MSG_DEBUG, "\tBody Length: %zu", body_len);
}
for (i = 0; i < body_len; i += MI_LEN + sizeof(mn)) {
mi = body->peer + i;
os_memcpy(&mn, mi + MI_LEN, sizeof(mn));
- wpa_hexdump_ascii(MSG_DEBUG, "\tMember Id.....:", mi, MI_LEN);
- wpa_printf(MSG_DEBUG, "\tMessage Number: %d", be_to_host32(mn));
+ wpa_printf(MSG_DEBUG, "\tMember Id: %s Message Number: %d",
+ mi_txt(mi), be_to_host32(mn));
}
}
@@ -186,18 +209,20 @@ ieee802_1x_mka_dump_dist_sak_body(struct ieee802_1x_mka_dist_sak_body *body)
if (body == NULL)
return;
+ /* IEEE Std 802.1X-2010, Figure 11-11 and 11-12 */
body_len = get_mka_param_body_len(body);
- wpa_printf(MSG_INFO, "*** Distributed SAK ***");
- wpa_printf(MSG_INFO, "\tDistributed AN........: %d", body->dan);
- wpa_printf(MSG_INFO, "\tConfidentiality Offset: %d",
+ wpa_printf(MSG_DEBUG, "Distributed SAK parameter set");
+ wpa_printf(MSG_DEBUG, "\tDistributed AN........: %d", body->dan);
+ wpa_printf(MSG_DEBUG, "\tConfidentiality Offset: %d",
body->confid_offset);
- wpa_printf(MSG_INFO, "\tBody Length...........: %zu", body_len);
+ wpa_printf(MSG_DEBUG, "\tBody Length...........: %zu", body_len);
if (!body_len)
return;
- wpa_printf(MSG_INFO, "\tKey Number............: %d",
+ wpa_printf(MSG_DEBUG, "\tKey Number............: %d",
be_to_host32(body->kn));
- wpa_hexdump(MSG_INFO, "\tAES Key Wrap of SAK...:", body->sak, 24);
+ /* TODO: Other than GCM-AES-128 case: MACsec Cipher Suite */
+ wpa_hexdump(MSG_DEBUG, "\tAES Key Wrap of SAK...:", body->sak, 24);
}
@@ -218,33 +243,32 @@ ieee802_1x_mka_dump_sak_use_body(struct ieee802_1x_mka_sak_use_body *body)
if (body == NULL)
return;
+ /* IEEE Std 802.1X-2010, Figure 11-10 */
body_len = get_mka_param_body_len(body);
- wpa_printf(MSG_DEBUG, "*** MACsec SAK Use ***");
+ wpa_printf(MSG_DEBUG, "MACsec SAK Use parameter set");
wpa_printf(MSG_DEBUG, "\tLatest Key AN....: %d", body->lan);
wpa_printf(MSG_DEBUG, "\tLatest Key Tx....: %s", yes_no(body->ltx));
wpa_printf(MSG_DEBUG, "\tLatest Key Rx....: %s", yes_no(body->lrx));
- wpa_printf(MSG_DEBUG, "\tOld Key AN....: %d", body->oan);
- wpa_printf(MSG_DEBUG, "\tOld Key Tx....: %s", yes_no(body->otx));
- wpa_printf(MSG_DEBUG, "\tOld Key Rx....: %s", yes_no(body->orx));
- wpa_printf(MSG_DEBUG, "\tPlain Key Tx....: %s", yes_no(body->ptx));
- wpa_printf(MSG_DEBUG, "\tPlain Key Rx....: %s", yes_no(body->prx));
+ wpa_printf(MSG_DEBUG, "\tOld Key AN.......: %d", body->oan);
+ wpa_printf(MSG_DEBUG, "\tOld Key Tx.......: %s", yes_no(body->otx));
+ wpa_printf(MSG_DEBUG, "\tOld Key Rx.......: %s", yes_no(body->orx));
+ wpa_printf(MSG_DEBUG, "\tPlain Tx.........: %s", yes_no(body->ptx));
+ wpa_printf(MSG_DEBUG, "\tPlain Rx.........: %s", yes_no(body->prx));
wpa_printf(MSG_DEBUG, "\tDelay Protect....: %s",
yes_no(body->delay_protect));
wpa_printf(MSG_DEBUG, "\tBody Length......: %d", body_len);
if (!body_len)
return;
- wpa_hexdump(MSG_DEBUG, "\tKey Server MI....:",
- body->lsrv_mi, sizeof(body->lsrv_mi));
+ wpa_printf(MSG_DEBUG, "\tKey Server MI....: %s", mi_txt(body->lsrv_mi));
wpa_printf(MSG_DEBUG, "\tKey Number.......: %u",
be_to_host32(body->lkn));
wpa_printf(MSG_DEBUG, "\tLowest PN........: %u",
be_to_host32(body->llpn));
- wpa_hexdump_ascii(MSG_DEBUG, "\tOld Key Server MI....:",
- body->osrv_mi, sizeof(body->osrv_mi));
- wpa_printf(MSG_DEBUG, "\tOld Key Number.......: %u",
+ wpa_printf(MSG_DEBUG, "\tOld Key Server MI: %s", mi_txt(body->osrv_mi));
+ wpa_printf(MSG_DEBUG, "\tOld Key Number...: %u",
be_to_host32(body->okn));
- wpa_printf(MSG_DEBUG, "\tOld Lowest PN........: %u",
+ wpa_printf(MSG_DEBUG, "\tOld Lowest PN....: %u",
be_to_host32(body->olpn));
}
@@ -371,7 +395,7 @@ ieee802_1x_kay_get_peer(struct ieee802_1x_mka_participant *participant,
*/
static struct macsec_ciphersuite *
ieee802_1x_kay_get_cipher_suite(struct ieee802_1x_mka_participant *participant,
- const u8 *cs_id)
+ const u8 *cs_id, unsigned int *idx)
{
unsigned int i;
u64 cs;
@@ -381,8 +405,10 @@ ieee802_1x_kay_get_cipher_suite(struct ieee802_1x_mka_participant *participant,
cs = be_to_host64(_cs);
for (i = 0; i < CS_TABLE_SIZE; i++) {
- if (cipher_suite_tbl[i].id == cs)
+ if (cipher_suite_tbl[i].id == cs) {
+ *idx = i;
return &cipher_suite_tbl[i];
+ }
}
return NULL;
@@ -464,7 +490,7 @@ ieee802_1x_kay_init_receive_sa(struct receive_sc *psc, u8 an, u32 lowest_pn,
dl_list_add(&psc->sa_list, &psa->list);
wpa_printf(MSG_DEBUG,
- "KaY: Create receive SA(AN: %hhu lowest_pn: %u of SC",
+ "KaY: Create receive SA(an: %hhu lowest_pn: %u) of SC",
an, lowest_pn);
return psa;
@@ -511,8 +537,8 @@ ieee802_1x_kay_init_receive_sc(const struct ieee802_1x_mka_sci *psci)
psc->receiving = FALSE;
dl_list_init(&psc->sa_list);
- wpa_printf(MSG_DEBUG, "KaY: Create receive SC");
- wpa_hexdump(MSG_DEBUG, "SCI: ", (u8 *)psci, sizeof(*psci));
+ wpa_printf(MSG_DEBUG, "KaY: Create receive SC: SCI %s",
+ sci_txt(&psc->sci));
return psc;
}
@@ -549,10 +575,8 @@ ieee802_1x_kay_deinit_receive_sc(
static void ieee802_1x_kay_dump_peer(struct ieee802_1x_kay_peer *peer)
{
- wpa_hexdump(MSG_DEBUG, "\tMI: ", peer->mi, sizeof(peer->mi));
- wpa_printf(MSG_DEBUG, "\tMN: %d", peer->mn);
- wpa_hexdump(MSG_DEBUG, "\tSCI Addr: ", peer->sci.addr, ETH_ALEN);
- wpa_printf(MSG_DEBUG, "\tPort: %d", peer->sci.port);
+ wpa_printf(MSG_DEBUG, "\tMI: %s MN: %d SCI: %s",
+ mi_txt(peer->mi), peer->mn, sci_txt(&peer->sci));
}
@@ -571,6 +595,7 @@ ieee802_1x_kay_create_peer(const u8 *mi, u32 mn)
peer->mn = mn;
peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
peer->sak_used = FALSE;
+ peer->missing_sak_use_count = 0;
return peer;
}
@@ -599,9 +624,13 @@ ieee802_1x_kay_create_live_peer(struct ieee802_1x_mka_participant *participant,
return NULL;
}
+ if (secy_create_receive_sc(participant->kay, rxsc)) {
+ os_free(rxsc);
+ os_free(peer);
+ return NULL;
+ }
dl_list_add(&participant->live_peers, &peer->list);
dl_list_add(&participant->rxsc_list, &rxsc->list);
- secy_create_receive_sc(participant->kay, rxsc);
wpa_printf(MSG_DEBUG, "KaY: Live peer created");
ieee802_1x_kay_dump_peer(peer);
@@ -625,7 +654,7 @@ ieee802_1x_kay_create_potential_peer(
dl_list_add(&participant->potential_peers, &peer->list);
- wpa_printf(MSG_DEBUG, "KaY: potential peer created");
+ wpa_printf(MSG_DEBUG, "KaY: Potential peer created");
ieee802_1x_kay_dump_peer(peer);
return peer;
@@ -655,14 +684,19 @@ ieee802_1x_kay_move_live_peer(struct ieee802_1x_mka_participant *participant,
peer->mn = mn;
peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
- wpa_printf(MSG_DEBUG, "KaY: move potential peer to live peer");
+ wpa_printf(MSG_DEBUG, "KaY: Move potential peer to live peer");
ieee802_1x_kay_dump_peer(peer);
dl_list_del(&peer->list);
+ if (secy_create_receive_sc(participant->kay, rxsc)) {
+ wpa_printf(MSG_ERROR, "KaY: Can't create SC, discard peer");
+ os_free(rxsc);
+ os_free(peer);
+ return NULL;
+ }
dl_list_add_tail(&participant->live_peers, &peer->list);
dl_list_add(&participant->rxsc_list, &rxsc->list);
- secy_create_receive_sc(participant->kay, rxsc);
return peer;
}
@@ -704,12 +738,15 @@ ieee802_1x_mka_encode_basic_body(
{
struct ieee802_1x_mka_basic_body *body;
struct ieee802_1x_kay *kay = participant->kay;
- unsigned int length = ieee802_1x_mka_basic_body_length(participant);
+ unsigned int length = sizeof(struct ieee802_1x_mka_basic_body);
- body = wpabuf_put(buf, length);
+ length += participant->ckn.len;
+ body = wpabuf_put(buf, MKA_ALIGN_LENGTH(length));
body->version = kay->mka_version;
body->priority = kay->actor_priority;
+ /* The Key Server flag is set if and only if the participant has not
+ * decided that another participant is or will be the Key Server. */
if (participant->is_elected)
body->key_server = participant->is_key_server;
else
@@ -765,11 +802,11 @@ ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay *kay, const u8 *mka_msg,
if (body->version > MKA_VERSION_ID) {
wpa_printf(MSG_DEBUG,
- "KaY: peer's version(%d) greater than mka current version(%d)",
+ "KaY: Peer's version(%d) greater than MKA current version(%d)",
body->version, MKA_VERSION_ID);
}
if (kay->is_obliged_key_server && body->key_server) {
- wpa_printf(MSG_DEBUG, "I must be as key server");
+ wpa_printf(MSG_DEBUG, "KaY: I must be key server - ignore MKPDU claiming to be from a key server");
return NULL;
}
@@ -783,7 +820,8 @@ ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay *kay, const u8 *mka_msg,
(sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN);
participant = ieee802_1x_kay_get_participant(kay, body->ckn, ckn_len);
if (!participant) {
- wpa_printf(MSG_DEBUG, "Peer is not included in my CA");
+ wpa_printf(MSG_DEBUG,
+ "KaY: Peer is not included in my CA - ignore MKPDU");
return NULL;
}
@@ -791,6 +829,9 @@ ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay *kay, const u8 *mka_msg,
if (os_memcmp(body->actor_mi, participant->mi, MI_LEN) == 0) {
if (!reset_participant_mi(participant))
return NULL;
+ wpa_printf(MSG_DEBUG,
+ "KaY: Peer using my MI - selected a new random MI: %s",
+ mi_txt(participant->mi));
}
os_memcpy(participant->current_peer_id.mi, body->actor_mi, MI_LEN);
@@ -802,24 +843,48 @@ ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay *kay, const u8 *mka_msg,
/* handler peer */
peer = ieee802_1x_kay_get_peer(participant, body->actor_mi);
if (!peer) {
- /* Check duplicated SCI */
- /* TODO: What policy should be applied to detect duplicated SCI
- * is active attacker or a valid peer whose MI is be changed?
+ /* Check duplicated SCI
+ *
+ * A duplicated SCI indicates either an active attacker or
+ * a valid peer whose MI is being changed. The latter scenario
+ * is more likely because to have gotten this far the received
+ * MKPDU must have had a valid ICV, indicating the peer holds
+ * the same CAK as our participant.
+ *
+ * Before creating a new peer object for the new MI we must
+ * clean up the resources (SCs and SAs) associated with the
+ * old peer. An easy way to do this is to ignore MKPDUs with
+ * the new MI's for now and just wait for the old peer to
+ * time out and clean itself up (within MKA_LIFE_TIME).
+ *
+ * This method is preferable to deleting the old peer here
+ * and now and continuing on with processing because if this
+ * MKPDU is from an attacker it's better to ignore the MKPDU
+ * than to process it (and delete a valid peer as well).
*/
peer = ieee802_1x_kay_get_peer_sci(participant,
&body->actor_sci);
if (peer) {
+ time_t new_expire;
+
wpa_printf(MSG_WARNING,
- "KaY: duplicated SCI detected, Maybe active attacker");
- dl_list_del(&peer->list);
- os_free(peer);
+ "KaY: duplicated SCI detected - maybe active attacker or peer selected new MI - ignore MKPDU");
+ /* Reduce timeout to speed up this process but left the
+ * chance for old one to prove aliveness. */
+ new_expire = time(NULL) + MKA_HELLO_TIME * 1.5 / 1000;
+ if (peer->expire > new_expire)
+ peer->expire = new_expire;
+ return NULL;
}
peer = ieee802_1x_kay_create_potential_peer(
participant, body->actor_mi,
be_to_host32(body->actor_mn));
- if (!peer)
+ if (!peer) {
+ wpa_printf(MSG_DEBUG,
+ "KaY: No potential peer entry found - ignore MKPDU");
return NULL;
+ }
peer->macsec_desired = body->macsec_desired;
peer->macsec_capability = body->macsec_capability;
@@ -827,13 +892,13 @@ ieee802_1x_mka_decode_basic_body(struct ieee802_1x_kay *kay, const u8 *mka_msg,
peer->key_server_priority = body->priority;
} else if (peer->mn < be_to_host32(body->actor_mn)) {
peer->mn = be_to_host32(body->actor_mn);
- peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
peer->macsec_desired = body->macsec_desired;
peer->macsec_capability = body->macsec_capability;
peer->is_key_server = (Boolean) body->key_server;
peer->key_server_priority = body->priority;
} else {
- wpa_printf(MSG_WARNING, "KaY: The peer MN have received");
+ wpa_printf(MSG_WARNING,
+ "KaY: The peer MN did not increase - ignore MKPDU");
return NULL;
}
@@ -978,8 +1043,8 @@ ieee802_1x_mka_i_in_peerlist(struct ieee802_1x_mka_participant *participant,
for (pos = mka_msg, left_len = msg_len;
left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN;
- left_len -= body_len + MKA_HDR_LEN,
- pos += body_len + MKA_HDR_LEN) {
+ left_len -= MKA_ALIGN_LENGTH(body_len) + MKA_HDR_LEN,
+ pos += MKA_ALIGN_LENGTH(body_len) + MKA_HDR_LEN) {
hdr = (struct ieee802_1x_mka_hdr *) pos;
body_len = get_mka_param_body_len(hdr);
body_type = get_mka_param_body_type(hdr);
@@ -1014,9 +1079,15 @@ ieee802_1x_mka_i_in_peerlist(struct ieee802_1x_mka_participant *participant,
peer_mi = (const struct ieee802_1x_mka_peer_id *)
(pos + MKA_HDR_LEN + i);
if (os_memcmp(peer_mi->mi, participant->mi,
- MI_LEN) == 0 &&
- be_to_host32(peer_mi->mn) == participant->mn)
- return TRUE;
+ MI_LEN) == 0) {
+ u32 mn = be_to_host32(peer_mi->mn);
+
+ wpa_printf(MSG_DEBUG,
+ "KaY: My MI - received MN %u, most recently transmitted MN %u",
+ mn, participant->mn);
+ if (mn == participant->mn)
+ return TRUE;
+ }
}
}
@@ -1072,7 +1143,6 @@ static int ieee802_1x_mka_decode_live_peer_body(
peer = ieee802_1x_kay_get_peer(participant, peer_mi->mi);
if (peer) {
peer->mn = peer_mn;
- peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
} else if (!ieee802_1x_kay_create_potential_peer(
participant, peer_mi->mi, peer_mn)) {
return -1;
@@ -1154,27 +1224,38 @@ ieee802_1x_mka_get_sak_use_length(
/**
- *
+ * ieee802_1x_mka_get_lpn
*/
static u32
ieee802_1x_mka_get_lpn(struct ieee802_1x_mka_participant *principal,
struct ieee802_1x_mka_ki *ki)
{
- struct receive_sa *rxsa;
- struct receive_sc *rxsc;
+ struct transmit_sa *txsa;
u32 lpn = 0;
- dl_list_for_each(rxsc, &principal->rxsc_list, struct receive_sc, list) {
- dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa, list)
- {
- if (is_ki_equal(&rxsa->pkey->key_identifier, ki)) {
- secy_get_receive_lowest_pn(principal->kay,
- rxsa);
-
- lpn = lpn > rxsa->lowest_pn ?
- lpn : rxsa->lowest_pn;
- break;
- }
+ dl_list_for_each(txsa, &principal->txsc->sa_list,
+ struct transmit_sa, list) {
+ if (is_ki_equal(&txsa->pkey->key_identifier, ki)) {
+ /* Per IEEE Std 802.1X-2010, Clause 9, "Each SecY uses
+ * MKA to communicate the lowest PN used for
+ * transmission with the SAK within the last two
+ * seconds". Achieve this 2 second delay by setting the
+ * lpn using the transmit next PN (i.e., txsa->next_pn)
+ * that was read last time here (i.e., mka_hello_time
+ * 2 seconds ago).
+ *
+ * The lowest acceptable PN is the same as the last
+ * transmitted PN, which is one less than the next
+ * transmit PN.
+ *
+ * NOTE: This method only works if mka_hello_time is 2s.
+ */
+ lpn = (txsa->next_pn > 0) ? (txsa->next_pn - 1) : 0;
+
+ /* Now read the current transmit next PN for use next
+ * time through. */
+ secy_get_transmit_next_pn(principal->kay, txsa);
+ break;
}
}
@@ -1214,8 +1295,9 @@ ieee802_1x_mka_encode_sak_use_body(
return 0;
}
- /* data protect, lowest accept packet number */
- body->delay_protect = kay->macsec_replay_protect;
+ /* data delay protect */
+ body->delay_protect = kay->mka_hello_time <= MKA_BOUNDED_HELLO_TIME;
+ /* lowest accept packet number */
pn = ieee802_1x_mka_get_lpn(participant, &participant->lki);
if (pn > kay->pn_exhaustion) {
wpa_printf(MSG_WARNING, "KaY: My LPN exhaustion");
@@ -1276,7 +1358,8 @@ ieee802_1x_mka_decode_sak_use_body(
struct ieee802_1x_mka_hdr *hdr;
struct ieee802_1x_mka_sak_use_body *body;
struct ieee802_1x_kay_peer *peer;
- struct transmit_sa *txsa;
+ struct receive_sc *rxsc;
+ struct receive_sa *rxsa;
struct data_key *sa_key = NULL;
size_t body_len;
struct ieee802_1x_mka_ki ki;
@@ -1292,7 +1375,9 @@ ieee802_1x_mka_decode_sak_use_body(
peer = ieee802_1x_kay_get_live_peer(participant,
participant->current_peer_id.mi);
if (!peer) {
- wpa_printf(MSG_WARNING, "KaY: the peer is not my live peer");
+ wpa_printf(MSG_WARNING,
+ "KaY: The peer (%s) is not my live peer - ignore MACsec SAK Use parameter set",
+ mi_txt(participant->current_peer_id.mi));
return -1;
}
@@ -1336,7 +1421,7 @@ ieee802_1x_mka_decode_sak_use_body(
}
}
if (!found) {
- wpa_printf(MSG_WARNING, "KaY: Latest key is invalid");
+ wpa_printf(MSG_INFO, "KaY: Latest key is invalid");
return -1;
}
if (os_memcmp(participant->lki.mi, body->lsrv_mi,
@@ -1366,7 +1451,7 @@ ieee802_1x_mka_decode_sak_use_body(
if (body->delay_protect &&
(!be_to_host32(body->llpn) || !be_to_host32(body->olpn))) {
wpa_printf(MSG_WARNING,
- "KaY: Lowest packet number should greater than 0 when delay_protect is TRUE");
+ "KaY: Lowest packet number should be greater than 0 when delay_protect is TRUE");
return -1;
}
@@ -1385,7 +1470,7 @@ ieee802_1x_mka_decode_sak_use_body(
ieee802_1x_cp_sm_step(kay->cp);
}
- /* if i'm key server, and detects peer member pn exhaustion, rekey.*/
+ /* if I'm key server, and detects peer member pn exhaustion, rekey. */
lpn = be_to_host32(body->llpn);
if (lpn > kay->pn_exhaustion) {
if (participant->is_key_server) {
@@ -1394,26 +1479,41 @@ ieee802_1x_mka_decode_sak_use_body(
}
}
+ if (sa_key)
+ sa_key->next_pn = lpn;
found = FALSE;
- dl_list_for_each(txsa, &participant->txsc->sa_list,
- struct transmit_sa, list) {
- if (sa_key != NULL && txsa->pkey == sa_key) {
- found = TRUE;
- break;
+ dl_list_for_each(rxsc, &participant->rxsc_list, struct receive_sc,
+ list) {
+ dl_list_for_each(rxsa, &rxsc->sa_list, struct receive_sa,
+ list) {
+ if (sa_key && rxsa->pkey == sa_key) {
+ found = TRUE;
+ break;
+ }
}
+ if (found)
+ break;
}
if (!found) {
- wpa_printf(MSG_WARNING, "KaY: Can't find txsa");
+ wpa_printf(MSG_WARNING, "KaY: Can't find rxsa");
return -1;
}
- /* FIXME: Secy creates txsa with default npn. If MKA detected Latest Key
- * npn is larger than txsa's npn, set it to txsa.
- */
- secy_get_transmit_next_pn(kay, txsa);
- if (lpn > txsa->next_pn) {
- secy_set_transmit_next_pn(kay, txsa);
- wpa_printf(MSG_INFO, "KaY: update lpn =0x%x", lpn);
+ if (body->delay_protect) {
+ secy_get_receive_lowest_pn(participant->kay, rxsa);
+ if (lpn > rxsa->lowest_pn) {
+ /* Delay protect window (communicated via MKA) is
+ * tighter than SecY's current replay protect window,
+ * so tell SecY the new (and higher) lpn. */
+ rxsa->lowest_pn = lpn;
+ secy_set_receive_lowest_pn(participant->kay, rxsa);
+ wpa_printf(MSG_DEBUG, "KaY: update lpn =0x%x", lpn);
+ }
+ /* FIX: Delay protection for olpn not implemented.
+ * Note that Old Key is only active for MKA_SAK_RETIRE_TIME
+ * (3 seconds) and delay protection does allow PN's within
+ * a 2 seconds window, so olpn would be a lot of work for
+ * just 1 second's worth of protection. */
}
return 0;
@@ -1427,7 +1527,8 @@ static Boolean
ieee802_1x_mka_dist_sak_body_present(
struct ieee802_1x_mka_participant *participant)
{
- return participant->to_dist_sak && participant->new_key;
+ return participant->is_key_server && participant->to_dist_sak &&
+ participant->new_key;
}
@@ -1478,6 +1579,11 @@ ieee802_1x_mka_encode_dist_sak_body(
}
sak = participant->new_key;
+ if (!sak) {
+ wpa_printf(MSG_DEBUG,
+ "KaY: No SAK available to build Distributed SAK parameter set");
+ return -1;
+ }
body->confid_offset = sak->confidentiality_offset;
body->dan = sak->an;
body->kn = host_to_be32(sak->key_identifier.kn);
@@ -1492,7 +1598,7 @@ ieee802_1x_mka_encode_dist_sak_body(
os_memcpy(body->sak, &cs, CS_ID_LEN);
sak_pos = CS_ID_LEN;
}
- if (aes_wrap(participant->kek.key, 16,
+ if (aes_wrap(participant->kek.key, participant->kek.len,
cipher_suite_tbl[cs_index].sak_len / 8,
sak->key, body->sak + sak_pos)) {
wpa_printf(MSG_ERROR, "KaY: AES wrap failed");
@@ -1514,6 +1620,7 @@ static void ieee802_1x_kay_init_data_key(struct data_key *pkey)
pkey->receives = TRUE;
os_get_time(&pkey->created_time);
+ pkey->next_pn = 1;
pkey->user = 1;
}
@@ -1553,7 +1660,7 @@ ieee802_1x_mka_decode_dist_sak_body(
}
if (participant->is_key_server) {
wpa_printf(MSG_ERROR,
- "KaY: I can't accept the distributed SAK as myself is key server ");
+ "KaY: Reject distributed SAK since I'm a key server");
return -1;
}
if (!kay->macsec_desired ||
@@ -1582,7 +1689,7 @@ ieee802_1x_mka_decode_dist_sak_body(
participant->advised_desired = FALSE;
ieee802_1x_cp_connect_authenticated(kay->cp);
ieee802_1x_cp_sm_step(kay->cp);
- wpa_printf(MSG_WARNING, "KaY:The Key server advise no MACsec");
+ wpa_printf(MSG_WARNING, "KaY: The Key server advise no MACsec");
participant->to_use_sak = FALSE;
return 0;
}
@@ -1601,7 +1708,8 @@ ieee802_1x_mka_decode_dist_sak_body(
if (os_memcmp(sa_key->key_identifier.mi,
participant->current_peer_id.mi, MI_LEN) == 0 &&
sa_key->key_identifier.kn == be_to_host32(body->kn)) {
- wpa_printf(MSG_WARNING, "KaY:The Key has installed");
+ wpa_printf(MSG_DEBUG,
+ "KaY: SAK has already been installed - do not set it again");
return 0;
}
}
@@ -1612,7 +1720,10 @@ ieee802_1x_mka_decode_dist_sak_body(
kay->macsec_csindex = DEFAULT_CS_INDEX;
cs = &cipher_suite_tbl[kay->macsec_csindex];
} else {
- cs = ieee802_1x_kay_get_cipher_suite(participant, body->sak);
+ unsigned int idx;
+
+ cs = ieee802_1x_kay_get_cipher_suite(participant, body->sak,
+ &idx);
if (!cs) {
wpa_printf(MSG_ERROR,
"KaY: I can't support the Cipher Suite advised by key server");
@@ -1620,7 +1731,7 @@ ieee802_1x_mka_decode_dist_sak_body(
}
sak_len = cs->sak_len;
wrap_sak = body->sak + CS_ID_LEN;
- kay->macsec_csindex = cs->index;
+ kay->macsec_csindex = idx;
}
unwrap_sak = os_zalloc(sak_len);
@@ -1628,13 +1739,13 @@ ieee802_1x_mka_decode_dist_sak_body(
wpa_printf(MSG_ERROR, "KaY-%s: Out of memory", __func__);
return -1;
}
- if (aes_unwrap(participant->kek.key, 16, sak_len >> 3, wrap_sak,
- unwrap_sak)) {
+ if (aes_unwrap(participant->kek.key, participant->kek.len,
+ sak_len >> 3, wrap_sak, unwrap_sak)) {
wpa_printf(MSG_ERROR, "KaY: AES unwrap failed");
os_free(unwrap_sak);
return -1;
}
- wpa_hexdump_key(MSG_DEBUG, "\tAES Key Unwrap of SAK:",
+ wpa_hexdump_key(MSG_DEBUG, "\tAES Key Unwrap of SAK.:",
unwrap_sak, sak_len);
sa_key = os_zalloc(sizeof(*sa_key));
@@ -1691,7 +1802,12 @@ ieee802_1x_mka_get_icv_length(struct ieee802_1x_mka_participant *participant)
{
int length;
- length = sizeof(struct ieee802_1x_mka_icv_body);
+ /* Determine if we need space for the ICV Indicator */
+ if (mka_alg_tbl[participant->kay->mka_algindex].icv_len !=
+ DEFAULT_ICV_LEN)
+ length = sizeof(struct ieee802_1x_mka_icv_body);
+ else
+ length = 0;
length += mka_alg_tbl[participant->kay->mka_algindex].icv_len;
return MKA_ALIGN_LENGTH(length);
@@ -1710,20 +1826,23 @@ ieee802_1x_mka_encode_icv_body(struct ieee802_1x_mka_participant *participant,
u8 cmac[MAX_ICV_LEN];
length = ieee802_1x_mka_get_icv_length(participant);
- if (length != DEFAULT_ICV_LEN) {
+ if (mka_alg_tbl[participant->kay->mka_algindex].icv_len !=
+ DEFAULT_ICV_LEN) {
+ wpa_printf(MSG_DEBUG, "KaY: ICV Indicator");
body = wpabuf_put(buf, MKA_HDR_LEN);
body->type = MKA_ICV_INDICATOR;
- set_mka_param_body_len(body, length - MKA_HDR_LEN);
+ length -= MKA_HDR_LEN;
+ set_mka_param_body_len(body, length);
}
if (mka_alg_tbl[participant->kay->mka_algindex].icv_hash(
- participant->ick.key, wpabuf_head(buf), buf->used, cmac)) {
- wpa_printf(MSG_ERROR, "KaY, omac1_aes_128 failed");
+ participant->ick.key, participant->ick.len,
+ wpabuf_head(buf), wpabuf_len(buf), cmac)) {
+ wpa_printf(MSG_ERROR, "KaY: failed to calculate ICV");
return -1;
}
+ wpa_hexdump(MSG_DEBUG, "KaY: ICV", cmac, length);
- if (length != DEFAULT_ICV_LEN)
- length -= MKA_HDR_LEN;
os_memcpy(wpabuf_put(buf, length), cmac, length);
return 0;
@@ -1732,12 +1851,12 @@ ieee802_1x_mka_encode_icv_body(struct ieee802_1x_mka_participant *participant,
/**
* ieee802_1x_mka_decode_icv_body -
*/
-static u8 *
+static const u8 *
ieee802_1x_mka_decode_icv_body(struct ieee802_1x_mka_participant *participant,
const u8 *mka_msg, size_t msg_len)
{
- struct ieee802_1x_mka_hdr *hdr;
- struct ieee802_1x_mka_icv_body *body;
+ const struct ieee802_1x_mka_hdr *hdr;
+ const struct ieee802_1x_mka_icv_body *body;
size_t body_len;
size_t left_len;
u8 body_type;
@@ -1745,12 +1864,12 @@ ieee802_1x_mka_decode_icv_body(struct ieee802_1x_mka_participant *participant,
pos = mka_msg;
left_len = msg_len;
- while (left_len > (MKA_HDR_LEN + DEFAULT_ICV_LEN)) {
- hdr = (struct ieee802_1x_mka_hdr *) pos;
- body_len = get_mka_param_body_len(hdr);
+ while (left_len > MKA_HDR_LEN + DEFAULT_ICV_LEN) {
+ hdr = (const struct ieee802_1x_mka_hdr *) pos;
+ body_len = MKA_ALIGN_LENGTH(get_mka_param_body_len(hdr));
body_type = get_mka_param_body_type(hdr);
- if (left_len < (body_len + MKA_HDR_LEN))
+ if (left_len < body_len + MKA_HDR_LEN)
break;
if (body_type != MKA_ICV_INDICATOR) {
@@ -1759,16 +1878,15 @@ ieee802_1x_mka_decode_icv_body(struct ieee802_1x_mka_participant *participant,
continue;
}
- body = (struct ieee802_1x_mka_icv_body *)pos;
+ body = (const struct ieee802_1x_mka_icv_body *) pos;
if (body_len
- < mka_alg_tbl[participant->kay->mka_algindex].icv_len) {
+ < mka_alg_tbl[participant->kay->mka_algindex].icv_len)
return NULL;
- }
return body->icv;
}
- return (u8 *) (mka_msg + msg_len - DEFAULT_ICV_LEN);
+ return mka_msg + msg_len - DEFAULT_ICV_LEN;
}
@@ -1787,7 +1905,7 @@ ieee802_1x_mka_decode_dist_cak_body(
body_len = get_mka_param_body_len(hdr);
if (body_len < 28) {
wpa_printf(MSG_ERROR,
- "KaY: MKA Use SAK Packet Body Length (%zu bytes) should be 28 or more octets",
+ "KaY: MKA Use CAK Packet Body Length (%zu bytes) should be 28 or more octets",
body_len);
return -1;
}
@@ -1811,7 +1929,7 @@ ieee802_1x_mka_decode_kmd_body(
body_len = get_mka_param_body_len(hdr);
if (body_len < 5) {
wpa_printf(MSG_ERROR,
- "KaY: MKA Use SAK Packet Body Length (%zu bytes) should be 5 or more octets",
+ "KaY: MKA Use KMD Packet Body Length (%zu bytes) should be 5 or more octets",
body_len);
return -1;
}
@@ -1842,7 +1960,7 @@ struct mka_param_body_handler {
static struct mka_param_body_handler mka_body_handler[] = {
- /* basic parameter set */
+ /* Basic parameter set */
{
.body_tx = ieee802_1x_mka_encode_basic_body,
.body_rx = NULL,
@@ -1850,7 +1968,7 @@ static struct mka_param_body_handler mka_body_handler[] = {
.body_present = ieee802_1x_mka_basic_body_present
},
- /* live peer list parameter set */
+ /* Live Peer List parameter set */
{
.body_tx = ieee802_1x_mka_encode_live_peer_body,
.body_rx = ieee802_1x_mka_decode_live_peer_body,
@@ -1858,7 +1976,7 @@ static struct mka_param_body_handler mka_body_handler[] = {
.body_present = ieee802_1x_mka_live_peer_body_present
},
- /* potential peer list parameter set */
+ /* Potential Peer List parameter set */
{
.body_tx = ieee802_1x_mka_encode_potential_peer_body,
.body_rx = ieee802_1x_mka_decode_potential_peer_body,
@@ -1866,7 +1984,7 @@ static struct mka_param_body_handler mka_body_handler[] = {
.body_present = ieee802_1x_mka_potential_peer_body_present
},
- /* sak use parameter set */
+ /* MACsec SAK Use parameter set */
{
.body_tx = ieee802_1x_mka_encode_sak_use_body,
.body_rx = ieee802_1x_mka_decode_sak_use_body,
@@ -1874,7 +1992,7 @@ static struct mka_param_body_handler mka_body_handler[] = {
.body_present = ieee802_1x_mka_sak_use_body_present
},
- /* distribute sak parameter set */
+ /* Distributed SAK parameter set */
{
.body_tx = ieee802_1x_mka_encode_dist_sak_body,
.body_rx = ieee802_1x_mka_decode_dist_sak_body,
@@ -1882,7 +2000,7 @@ static struct mka_param_body_handler mka_body_handler[] = {
.body_present = ieee802_1x_mka_dist_sak_body_present
},
- /* distribute cak parameter set */
+ /* Distribute CAK parameter set */
{
.body_tx = NULL,
.body_rx = ieee802_1x_mka_decode_dist_cak_body,
@@ -1890,7 +2008,7 @@ static struct mka_param_body_handler mka_body_handler[] = {
.body_present = NULL
},
- /* kmd parameter set */
+ /* KMD parameter set */
{
.body_tx = NULL,
.body_rx = ieee802_1x_mka_decode_kmd_body,
@@ -1898,7 +2016,7 @@ static struct mka_param_body_handler mka_body_handler[] = {
.body_present = NULL
},
- /* announce parameter set */
+ /* Announcement parameter set */
{
.body_tx = NULL,
.body_rx = ieee802_1x_mka_decode_announce_body,
@@ -1906,7 +2024,7 @@ static struct mka_param_body_handler mka_body_handler[] = {
.body_present = NULL
},
- /* icv parameter set */
+ /* ICV Indicator parameter set */
{
.body_tx = ieee802_1x_mka_encode_icv_body,
.body_rx = NULL,
@@ -1965,7 +2083,7 @@ ieee802_1x_kay_generate_new_sak(struct ieee802_1x_mka_participant *participant)
*/
if (dl_list_empty(&participant->live_peers)) {
wpa_printf(MSG_ERROR,
- "KaY: Live peers list must not empty when generating fresh SAK");
+ "KaY: Live peers list must not be empty when generating fresh SAK");
return -1;
}
@@ -1979,7 +2097,7 @@ ieee802_1x_kay_generate_new_sak(struct ieee802_1x_mka_participant *participant)
*/
if ((time(NULL) - kay->dist_time) < MKA_LIFE_TIME / 1000) {
wpa_printf(MSG_ERROR,
- "KaY: Life time have not elapsed since prior SAK distributed");
+ "KaY: Life time has not elapsed since prior SAK distributed");
return -1;
}
@@ -2016,14 +2134,17 @@ ieee802_1x_kay_generate_new_sak(struct ieee802_1x_mka_participant *participant)
ctx_offset += sizeof(participant->mi);
os_memcpy(context + ctx_offset, &kay->dist_kn, sizeof(kay->dist_kn));
- if (key_len == 16) {
- ieee802_1x_sak_128bits_aes_cmac(participant->cak.key,
- context, ctx_len, key);
- } else if (key_len == 32) {
- ieee802_1x_sak_128bits_aes_cmac(participant->cak.key,
- context, ctx_len, key);
+ if (key_len == 16 || key_len == 32) {
+ if (ieee802_1x_sak_aes_cmac(participant->cak.key,
+ participant->cak.len,
+ context, ctx_len,
+ key, key_len)) {
+ wpa_printf(MSG_ERROR, "KaY: Failed to generate SAK");
+ goto fail;
+ }
} else {
- wpa_printf(MSG_ERROR, "KaY: SAK Length not support");
+ wpa_printf(MSG_ERROR, "KaY: SAK Length(%u) not supported",
+ key_len);
goto fail;
}
wpa_hexdump_key(MSG_DEBUG, "KaY: generated new SAK", key, key_len);
@@ -2155,7 +2276,7 @@ ieee802_1x_kay_elect_key_server(struct ieee802_1x_mka_participant *participant)
participant->is_key_server = TRUE;
participant->principal = TRUE;
participant->new_sak = TRUE;
- wpa_printf(MSG_DEBUG, "KaY: I is elected as key server");
+ wpa_printf(MSG_DEBUG, "KaY: I am elected as key server");
participant->to_dist_sak = FALSE;
participant->is_elected = TRUE;
@@ -2163,6 +2284,9 @@ ieee802_1x_kay_elect_key_server(struct ieee802_1x_mka_participant *participant)
sizeof(kay->key_server_sci));
kay->key_server_priority = kay->actor_priority;
} else if (key_server) {
+ wpa_printf(MSG_DEBUG,
+ "KaY: Peer %s was elected as the key server",
+ mi_txt(key_server->mi));
ieee802_1x_cp_set_electedself(kay->cp, FALSE);
if (!sci_equal(&kay->key_server_sci, &key_server->sci)) {
ieee802_1x_cp_signal_chgdserver(kay->cp);
@@ -2281,11 +2405,19 @@ ieee802_1x_kay_encode_mkpdu(struct ieee802_1x_mka_participant *participant,
os_memcpy(ether_hdr->src, participant->kay->actor_sci.addr,
sizeof(ether_hdr->dest));
ether_hdr->ethertype = host_to_be16(ETH_P_EAPOL);
+ wpa_printf(MSG_DEBUG, "KaY: Ethernet header: DA=" MACSTR " SA=" MACSTR
+ " Ethertype=0x%x",
+ MAC2STR(ether_hdr->dest), MAC2STR(ether_hdr->src),
+ be_to_host16(ether_hdr->ethertype));
eapol_hdr = wpabuf_put(pbuf, sizeof(*eapol_hdr));
eapol_hdr->version = EAPOL_VERSION;
eapol_hdr->type = IEEE802_1X_TYPE_EAPOL_MKA;
- eapol_hdr->length = host_to_be16(pbuf->size - pbuf->used);
+ eapol_hdr->length = host_to_be16(wpabuf_tailroom(pbuf));
+ wpa_printf(MSG_DEBUG,
+ "KaY: Common EAPOL PDU structure: Protocol Version=%u Packet Type=%u Packet Body Length=%u",
+ eapol_hdr->version, eapol_hdr->type,
+ be_to_host16(eapol_hdr->length));
for (i = 0; i < ARRAY_SIZE(mka_body_handler); i++) {
if (mka_body_handler[i].body_present &&
@@ -2298,6 +2430,7 @@ ieee802_1x_kay_encode_mkpdu(struct ieee802_1x_mka_participant *participant,
return 0;
}
+
/**
* ieee802_1x_participant_send_mkpdu -
*/
@@ -2310,7 +2443,8 @@ ieee802_1x_participant_send_mkpdu(
size_t length = 0;
unsigned int i;
- wpa_printf(MSG_DEBUG, "KaY: to enpacket and send the MKPDU");
+ wpa_printf(MSG_DEBUG, "KaY: Encode and send an MKPDU (ifname=%s)",
+ kay->if_name);
length += sizeof(struct ieee802_1x_hdr) + sizeof(struct ieee8023_hdr);
for (i = 0; i < ARRAY_SIZE(mka_body_handler); i++) {
if (mka_body_handler[i].body_present &&
@@ -2325,10 +2459,11 @@ ieee802_1x_participant_send_mkpdu(
}
if (ieee802_1x_kay_encode_mkpdu(participant, buf)) {
- wpa_printf(MSG_ERROR, "KaY: encode mkpdu fail!");
+ wpa_printf(MSG_ERROR, "KaY: encode mkpdu fail");
return -1;
}
+ wpa_hexdump_buf(MSG_MSGDUMP, "KaY: Outgoing MKPDU", buf);
l2_packet_send(kay->l2_mka, NULL, 0, wpabuf_head(buf), wpabuf_len(buf));
wpabuf_free(buf);
@@ -2365,6 +2500,8 @@ static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx)
participant = (struct ieee802_1x_mka_participant *)eloop_ctx;
kay = participant->kay;
+ wpa_printf(MSG_DEBUG, "KaY: Participant timer (ifname=%s)",
+ kay->if_name);
if (participant->cak_life) {
if (now > participant->cak_life)
goto delete_mka;
@@ -2452,7 +2589,7 @@ static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx)
}
}
- if (participant->new_sak) {
+ if (participant->new_sak && participant->is_key_server) {
if (!ieee802_1x_kay_generate_new_sak(participant))
participant->to_dist_sak = TRUE;
@@ -2465,7 +2602,7 @@ static void ieee802_1x_participant_timer(void *eloop_ctx, void *timeout_ctx)
participant->retry_count++;
}
- eloop_register_timeout(MKA_HELLO_TIME / 1000, 0,
+ eloop_register_timeout(kay->mka_hello_time / 1000, 0,
ieee802_1x_participant_timer,
participant, NULL);
@@ -2514,7 +2651,7 @@ ieee802_1x_kay_init_transmit_sa(struct transmit_sc *psc, u8 an, u32 next_PN,
dl_list_add(&psc->sa_list, &psa->list);
wpa_printf(MSG_DEBUG,
- "KaY: Create transmit SA(an: %hhu, next_PN: %u) of SC",
+ "KaY: Create transmit SA(an: %hhu, next_pn: %u) of SC",
an, next_PN);
return psa;
@@ -2557,8 +2694,8 @@ ieee802_1x_kay_init_transmit_sc(const struct ieee802_1x_mka_sci *sci)
psc->enciphering_sa = FALSE;
dl_list_init(&psc->sa_list);
- wpa_printf(MSG_DEBUG, "KaY: Create transmit SC");
- wpa_hexdump(MSG_DEBUG, "SCI: ", (u8 *)sci , sizeof(*sci));
+ wpa_printf(MSG_DEBUG, "KaY: Create transmit SC - SCI: %s",
+ sci_txt(&psc->sci));
return psc;
}
@@ -2709,7 +2846,7 @@ int ieee802_1x_kay_create_sas(struct ieee802_1x_kay *kay,
}
}
if (!latest_sak) {
- wpa_printf(MSG_ERROR, "lki related sak not found");
+ wpa_printf(MSG_ERROR, "KaY: lki related sak not found");
return -1;
}
@@ -2730,7 +2867,9 @@ int ieee802_1x_kay_create_sas(struct ieee802_1x_kay *kay,
ieee802_1x_delete_transmit_sa(kay, txsa);
txsa = ieee802_1x_kay_init_transmit_sa(principal->txsc, latest_sak->an,
- 1, latest_sak);
+ latest_sak->next_pn ?
+ latest_sak->next_pn : 1,
+ latest_sak);
if (!txsa)
return -1;
@@ -2779,12 +2918,12 @@ int ieee802_1x_kay_delete_sas(struct ieee802_1x_kay *kay,
dl_list_for_each_safe(sa_key, pre_key, &principal->sak_list,
struct data_key, list) {
if (is_ki_equal(&sa_key->key_identifier, ki)) {
+ if (principal->new_key == sa_key)
+ principal->new_key = NULL;
dl_list_del(&sa_key->list);
ieee802_1x_kay_deinit_data_key(sa_key);
break;
}
- if (principal->new_key == sa_key)
- principal->new_key = NULL;
}
return 0;
@@ -2872,7 +3011,8 @@ int ieee802_1x_kay_enable_new_info(struct ieee802_1x_kay *kay)
/**
* ieee802_1x_kay_mkpdu_sanity_check -
- * sanity check specified in clause 11.11.2 of IEEE802.1X-2010
+ * Sanity checks specified in IEEE Std 802.1X-2010, 11.11.2 (Validation of
+ * MKPDUs)
*/
static int ieee802_1x_kay_mkpdu_sanity_check(struct ieee802_1x_kay *kay,
const u8 *buf, size_t len)
@@ -2886,34 +3026,49 @@ static int ieee802_1x_kay_mkpdu_sanity_check(struct ieee802_1x_kay *kay,
size_t body_len;
size_t ckn_len;
u8 icv[MAX_ICV_LEN];
- u8 *msg_icv;
+ const u8 *msg_icv;
+ /* len > eth+eapol header already verified in kay_l2_receive();
+ * likewise, eapol_hdr->length validated there */
eth_hdr = (struct ieee8023_hdr *) buf;
eapol_hdr = (struct ieee802_1x_hdr *) (eth_hdr + 1);
mka_hdr = (struct ieee802_1x_mka_hdr *) (eapol_hdr + 1);
- /* destination address should be not individual address */
+ wpa_printf(MSG_DEBUG, "KaY: Ethernet header: DA=" MACSTR " SA=" MACSTR
+ " Ethertype=0x%x",
+ MAC2STR(eth_hdr->dest), MAC2STR(eth_hdr->src),
+ be_to_host16(eth_hdr->ethertype));
+
+ /* the destination address shall not be an individual address */
if (os_memcmp(eth_hdr->dest, pae_group_addr, ETH_ALEN) != 0) {
- wpa_printf(MSG_MSGDUMP,
+ wpa_printf(MSG_DEBUG,
"KaY: ethernet destination address is not PAE group address");
return -1;
}
- /* MKPDU should not be less than 32 octets */
+ wpa_printf(MSG_DEBUG,
+ "KaY: Common EAPOL PDU structure: Protocol Version=%u Packet Type=%u Packet Body Length=%u",
+ eapol_hdr->version, eapol_hdr->type,
+ be_to_host16(eapol_hdr->length));
+
+ /* MKPDU shall not be less than 32 octets */
mka_msg_len = be_to_host16(eapol_hdr->length);
if (mka_msg_len < 32) {
- wpa_printf(MSG_MSGDUMP, "KaY: MKPDU is less than 32 octets");
+ wpa_printf(MSG_DEBUG, "KaY: MKPDU is less than 32 octets");
return -1;
}
- /* MKPDU should be a multiple of 4 octets */
+ /* MKPDU shall be a multiple of 4 octets */
if ((mka_msg_len % 4) != 0) {
- wpa_printf(MSG_MSGDUMP,
+ wpa_printf(MSG_DEBUG,
"KaY: MKPDU is not multiple of 4 octets");
return -1;
}
+ wpa_hexdump(MSG_MSGDUMP, "KaY: EAPOL-MKA Packet Body (MKPDU)",
+ mka_hdr, mka_msg_len);
+
+ /* Room for body_len already verified in kay_l2_receive() */
body = (struct ieee802_1x_mka_basic_body *) mka_hdr;
- ieee802_1x_mka_dump_basic_body(body);
body_len = get_mka_param_body_len(body);
/* EAPOL-MKA body should comprise basic parameter set and ICV */
if (mka_msg_len < MKA_HDR_LEN + body_len + DEFAULT_ICV_LEN) {
@@ -2932,24 +3087,27 @@ static int ieee802_1x_kay_mkpdu_sanity_check(struct ieee802_1x_kay *kay,
ckn_len = body_len -
(sizeof(struct ieee802_1x_mka_basic_body) - MKA_HDR_LEN);
if (ckn_len < 1 || ckn_len > MAX_CKN_LEN) {
- wpa_printf(MSG_ERROR,
+ wpa_printf(MSG_WARNING,
"KaY: Received EAPOL-MKA CKN Length (%zu bytes) is out of range (<= %u bytes)",
ckn_len, MAX_CKN_LEN);
return -1;
}
+ ieee802_1x_mka_dump_basic_body(body);
+
/* CKN should be owned by I */
participant = ieee802_1x_kay_get_participant(kay, body->ckn, ckn_len);
if (!participant) {
- wpa_printf(MSG_DEBUG, "CKN is not included in my CA");
+ wpa_printf(MSG_DEBUG, "KaY: CKN is not included in my CA");
return -1;
}
/* algorithm agility check */
if (os_memcmp(body->algo_agility, mka_algo_agility,
sizeof(body->algo_agility)) != 0) {
- wpa_printf(MSG_ERROR,
- "KaY: peer's algorithm agility not supported for me");
+ wpa_printf(MSG_INFO,
+ "KaY: Peer's algorithm agility (%s) not supported",
+ algo_agility_txt(body->algo_agility));
return -1;
}
@@ -2959,23 +3117,29 @@ static int ieee802_1x_kay_mkpdu_sanity_check(struct ieee802_1x_kay *kay,
* its size, not the fixed length 16 octets, indicated by the EAPOL
* packet body length.
*/
- if (mka_alg_tbl[kay->mka_algindex].icv_hash(
- participant->ick.key,
+ if (len < mka_alg_tbl[kay->mka_algindex].icv_len ||
+ mka_alg_tbl[kay->mka_algindex].icv_hash(
+ participant->ick.key, participant->ick.len,
buf, len - mka_alg_tbl[kay->mka_algindex].icv_len, icv)) {
- wpa_printf(MSG_ERROR, "KaY: omac1_aes_128 failed");
+ wpa_printf(MSG_ERROR, "KaY: Failed to calculate ICV");
return -1;
}
- msg_icv = ieee802_1x_mka_decode_icv_body(participant, (u8 *) mka_hdr,
+ msg_icv = ieee802_1x_mka_decode_icv_body(participant,
+ (const u8 *) mka_hdr,
mka_msg_len);
if (!msg_icv) {
- wpa_printf(MSG_ERROR, "KaY: No ICV");
+ wpa_printf(MSG_WARNING, "KaY: No ICV in MKPDU - ignore it");
return -1;
}
+ wpa_hexdump(MSG_DEBUG, "KaY: Received ICV",
+ msg_icv, mka_alg_tbl[kay->mka_algindex].icv_len);
if (os_memcmp_const(msg_icv, icv,
mka_alg_tbl[kay->mka_algindex].icv_len) != 0) {
- wpa_printf(MSG_ERROR,
+ wpa_printf(MSG_WARNING,
"KaY: Computed ICV is not equal to Received ICV");
+ wpa_hexdump(MSG_DEBUG, "KaY: Calculated ICV",
+ icv, mka_alg_tbl[kay->mka_algindex].icv_len);
return -1;
}
@@ -2991,13 +3155,19 @@ static int ieee802_1x_kay_decode_mkpdu(struct ieee802_1x_kay *kay,
{
struct ieee802_1x_mka_participant *participant;
struct ieee802_1x_mka_hdr *hdr;
+ struct ieee802_1x_kay_peer *peer;
size_t body_len;
size_t left_len;
u8 body_type;
int i;
const u8 *pos;
Boolean handled[256];
+ Boolean bad_sak_use = FALSE; /* Error detected while processing SAK Use
+ * parameter set */
+ Boolean i_in_peerlist, is_in_live_peer, is_in_potential_peer;
+ wpa_printf(MSG_DEBUG, "KaY: Decode received MKPDU (ifname=%s)",
+ kay->if_name);
if (ieee802_1x_kay_mkpdu_sanity_check(kay, buf, len))
return -1;
@@ -3011,17 +3181,24 @@ static int ieee802_1x_kay_decode_mkpdu(struct ieee802_1x_kay *kay,
/* to skip basic parameter set */
hdr = (struct ieee802_1x_mka_hdr *) pos;
- body_len = get_mka_param_body_len(hdr);
+ body_len = MKA_ALIGN_LENGTH(get_mka_param_body_len(hdr));
+ if (left_len < body_len + MKA_HDR_LEN)
+ return -1;
pos += body_len + MKA_HDR_LEN;
left_len -= body_len + MKA_HDR_LEN;
/* check i am in the peer's peer list */
- if (ieee802_1x_mka_i_in_peerlist(participant, pos, left_len) &&
- !ieee802_1x_kay_is_in_live_peer(participant,
- participant->current_peer_id.mi)) {
+ i_in_peerlist = ieee802_1x_mka_i_in_peerlist(participant, pos,
+ left_len);
+ is_in_live_peer = ieee802_1x_kay_is_in_live_peer(
+ participant, participant->current_peer_id.mi);
+ wpa_printf(MSG_DEBUG, "KaY: i_in_peerlist=%s is_in_live_peer=%s",
+ yes_no(i_in_peerlist), yes_no(is_in_live_peer));
+ if (i_in_peerlist && !is_in_live_peer) {
/* accept the peer as live peer */
- if (ieee802_1x_kay_is_in_potential_peer(
- participant, participant->current_peer_id.mi)) {
+ is_in_potential_peer = ieee802_1x_kay_is_in_potential_peer(
+ participant, participant->current_peer_id.mi);
+ if (is_in_potential_peer) {
if (!ieee802_1x_kay_move_live_peer(
participant,
participant->current_peer_id.mi,
@@ -3051,7 +3228,7 @@ static int ieee802_1x_kay_decode_mkpdu(struct ieee802_1x_kay *kay,
pos += body_len + MKA_HDR_LEN,
left_len -= body_len + MKA_HDR_LEN) {
hdr = (struct ieee802_1x_mka_hdr *) pos;
- body_len = get_mka_param_body_len(hdr);
+ body_len = MKA_ALIGN_LENGTH(get_mka_param_body_len(hdr));
body_type = get_mka_param_body_type(hdr);
if (body_type == MKA_ICV_INDICATOR)
@@ -3065,21 +3242,103 @@ static int ieee802_1x_kay_decode_mkpdu(struct ieee802_1x_kay *kay,
return -1;
}
- if (handled[body_type])
+ if (handled[body_type]) {
+ wpa_printf(MSG_DEBUG,
+ "KaY: Ignore duplicated body type %u",
+ body_type);
continue;
+ }
handled[body_type] = TRUE;
if (body_type < ARRAY_SIZE(mka_body_handler) &&
mka_body_handler[body_type].body_rx) {
- mka_body_handler[body_type].body_rx
- (participant, pos, left_len);
+ if (mka_body_handler[body_type].body_rx
+ (participant, pos, left_len) != 0) {
+ /* Handle parameter set failure */
+ if (body_type != MKA_SAK_USE) {
+ wpa_printf(MSG_INFO,
+ "KaY: Discarding Rx MKPDU: decode of parameter set type (%d) failed",
+ body_type);
+ return -1;
+ }
+
+ /* Ideally DIST-SAK should be processed before
+ * SAK-USE. Unfortunately IEEE Std 802.1X-2010,
+ * 11.11.3 (Encoding MKPDUs) states SAK-USE(3)
+ * must always be encoded before DIST-SAK(4).
+ * Rather than redesigning mka_body_handler so
+ * that it somehow processes DIST-SAK before
+ * SAK-USE, just ignore SAK-USE failures if
+ * DIST-SAK is also present in this MKPDU. */
+ bad_sak_use = TRUE;
+ }
} else {
wpa_printf(MSG_ERROR,
- "The type %d is not supported in this MKA version %d",
+ "KaY: The body type %d is not supported in this MKA version %d",
body_type, MKA_VERSION_ID);
}
}
+ if (bad_sak_use && !handled[MKA_DISTRIBUTED_SAK]) {
+ wpa_printf(MSG_INFO,
+ "KaY: Discarding Rx MKPDU: decode of parameter set type (%d) failed",
+ MKA_SAK_USE);
+ if (!reset_participant_mi(participant))
+ wpa_printf(MSG_DEBUG, "KaY: Could not update mi");
+ else
+ wpa_printf(MSG_DEBUG,
+ "KaY: Selected a new random MI: %s",
+ mi_txt(participant->mi));
+ return -1;
+ }
+
+ /* Detect missing parameter sets */
+ peer = ieee802_1x_kay_get_live_peer(participant,
+ participant->current_peer_id.mi);
+ if (peer) {
+ /* MKPDU is from live peer */
+ if (!handled[MKA_SAK_USE]) {
+ /* Once a live peer starts sending SAK-USE, it should be
+ * sent every time. */
+ if (peer->sak_used) {
+ wpa_printf(MSG_INFO,
+ "KaY: Discarding Rx MKPDU: Live Peer stopped sending SAK-USE");
+ return -1;
+ }
+
+ /* Live peer is probably hung if it hasn't sent SAK-USE
+ * after a reasonable number of MKPDUs. Drop the MKPDU,
+ * which will eventually force an timeout. */
+ if (++peer->missing_sak_use_count >
+ MAX_MISSING_SAK_USE) {
+ wpa_printf(MSG_INFO,
+ "KaY: Discarding Rx MKPDU: Live Peer not sending SAK-USE");
+ return -1;
+ }
+ } else {
+ peer->missing_sak_use_count = 0;
+
+ /* Only update live peer watchdog after successful
+ * decode of all parameter sets */
+ peer->expire = time(NULL) + MKA_LIFE_TIME / 1000;
+ }
+ } else {
+ /* MKPDU is from new or potential peer */
+ peer = ieee802_1x_kay_get_peer(participant,
+ participant->current_peer_id.mi);
+ if (!peer) {
+ wpa_printf(MSG_DEBUG, "KaY: No peer entry found");
+ return -1;
+ }
+
+ /* Do not update potential peer watchdog. Per IEEE Std
+ * 802.1X-2010, 9.4.3, potential peers need to show liveness by
+ * including our MI/MN in their transmitted MKPDU (within
+ * potential or live parameter sets). Whena potential peer does
+ * include our MI/MN in an MKPDU, we respond by moving the peer
+ * from 'potential_peers' to 'live_peers'. */
+ }
+
kay->active = TRUE;
participant->retry_count = 0;
participant->active = TRUE;
@@ -3095,6 +3354,9 @@ static void kay_l2_receive(void *ctx, const u8 *src_addr, const u8 *buf,
struct ieee802_1x_kay *kay = ctx;
struct ieee8023_hdr *eth_hdr;
struct ieee802_1x_hdr *eapol_hdr;
+ size_t calc_len;
+
+ /* IEEE Std 802.1X-2010, 11.4 (Validation of received EAPOL PDUs) */
/* must contain at least ieee8023_hdr + ieee802_1x_hdr */
if (len < sizeof(*eth_hdr) + sizeof(*eapol_hdr)) {
@@ -3105,13 +3367,21 @@ static void kay_l2_receive(void *ctx, const u8 *src_addr, const u8 *buf,
eth_hdr = (struct ieee8023_hdr *) buf;
eapol_hdr = (struct ieee802_1x_hdr *) (eth_hdr + 1);
- if (len != sizeof(*eth_hdr) + sizeof(*eapol_hdr) +
- be_to_host16(eapol_hdr->length)) {
- wpa_printf(MSG_MSGDUMP, "KAY: EAPOL MPDU is invalid: (%lu-%lu)",
+ calc_len = sizeof(*eth_hdr) + sizeof(*eapol_hdr) +
+ be_to_host16(eapol_hdr->length);
+ if (len < calc_len) {
+ wpa_printf(MSG_MSGDUMP, "KaY: EAPOL MPDU is invalid: (received len %lu, calculated len %lu, EAPOL length %u)",
(unsigned long) len,
- (unsigned long) be_to_host16(eapol_hdr->length));
+ (unsigned long) calc_len,
+ be_to_host16(eapol_hdr->length));
return;
}
+ if (len > calc_len) {
+ wpa_hexdump(MSG_DEBUG,
+ "KaY: Ignore extra octets following the Packey Body field",
+ &buf[calc_len], len - calc_len);
+ len = calc_len;
+ }
if (eapol_hdr->version < EAPOL_VERSION) {
wpa_printf(MSG_MSGDUMP, "KaY: version %d does not support MKA",
@@ -3120,11 +3390,12 @@ static void kay_l2_receive(void *ctx, const u8 *src_addr, const u8 *buf,
}
if (be_to_host16(eth_hdr->ethertype) != ETH_P_PAE ||
eapol_hdr->type != IEEE802_1X_TYPE_EAPOL_MKA)
- return;
+ return; /* ignore other EAPOL types silently here */
- wpa_hexdump(MSG_DEBUG, "RX EAPOL-MKA: ", buf, len);
+ wpa_hexdump(MSG_DEBUG, "KaY: RX EAPOL-MKA", buf, len);
if (dl_list_empty(&kay->participant_list)) {
- wpa_printf(MSG_ERROR, "KaY: no MKA participant instance");
+ wpa_printf(MSG_ERROR,
+ "KaY: No MKA participant instance - ignore EAPOL-MKA");
return;
}
@@ -3137,10 +3408,14 @@ static void kay_l2_receive(void *ctx, const u8 *src_addr, const u8 *buf,
*/
struct ieee802_1x_kay *
ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy,
+ Boolean macsec_replay_protect, u32 macsec_replay_window,
u16 port, u8 priority, const char *ifname, const u8 *addr)
{
struct ieee802_1x_kay *kay;
+ wpa_printf(MSG_DEBUG, "KaY: Initialize - ifname=%s addr=" MACSTR
+ " port=%u priority=%u",
+ ifname, MAC2STR(addr), port, priority);
kay = os_zalloc(sizeof(*kay));
if (!kay) {
wpa_printf(MSG_ERROR, "KaY-%s: out of memory", __func__);
@@ -3161,6 +3436,8 @@ ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy,
os_strlcpy(kay->if_name, ifname, IFNAMSIZ);
os_memcpy(kay->actor_sci.addr, addr, ETH_ALEN);
kay->actor_sci.port = host_to_be16(port ? port : 0x0001);
+ wpa_printf(MSG_DEBUG, "KaY: Generated SCI: %s",
+ sci_txt(&kay->actor_sci));
kay->actor_priority = priority;
/* While actor acts as a key server, shall distribute sakey */
@@ -3187,21 +3464,27 @@ ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy,
kay->macsec_capable = MACSEC_CAP_NOT_IMPLEMENTED;
kay->macsec_desired = FALSE;
kay->macsec_protect = FALSE;
+ kay->macsec_encrypt = FALSE;
kay->macsec_validate = Disabled;
kay->macsec_replay_protect = FALSE;
kay->macsec_replay_window = 0;
kay->macsec_confidentiality = CONFIDENTIALITY_NONE;
+ kay->mka_hello_time = MKA_HELLO_TIME;
} else {
kay->macsec_desired = TRUE;
kay->macsec_protect = TRUE;
- kay->macsec_encrypt = policy == SHOULD_ENCRYPT;
- kay->macsec_validate = Strict;
- kay->macsec_replay_protect = FALSE;
- kay->macsec_replay_window = 0;
- if (kay->macsec_capable >= MACSEC_CAP_INTEG_AND_CONF)
+ if (kay->macsec_capable >= MACSEC_CAP_INTEG_AND_CONF &&
+ policy == SHOULD_ENCRYPT) {
+ kay->macsec_encrypt = TRUE;
kay->macsec_confidentiality = CONFIDENTIALITY_OFFSET_0;
- else
+ } else { /* SHOULD_SECURE */
+ kay->macsec_encrypt = FALSE;
kay->macsec_confidentiality = CONFIDENTIALITY_NONE;
+ }
+ kay->macsec_validate = Strict;
+ kay->macsec_replay_protect = macsec_replay_protect;
+ kay->macsec_replay_window = macsec_replay_window;
+ kay->mka_hello_time = MKA_HELLO_TIME;
}
wpa_printf(MSG_DEBUG, "KaY: state machine created");
@@ -3273,6 +3556,19 @@ ieee802_1x_kay_deinit(struct ieee802_1x_kay *kay)
}
+static const char * mode_txt(enum mka_created_mode mode)
+{
+ switch (mode) {
+ case PSK:
+ return "PSK";
+ case EAP_EXCHANGE:
+ return "EAP";
+ }
+
+ return "?";
+}
+
+
/**
* ieee802_1x_kay_create_mka -
*/
@@ -3285,17 +3581,22 @@ ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay,
struct ieee802_1x_mka_participant *participant;
unsigned int usecs;
+ wpa_printf(MSG_DEBUG,
+ "KaY: Create MKA (ifname=%s mode=%s authenticator=%s)",
+ kay->if_name, mode_txt(mode), yes_no(is_authenticator));
+
if (!kay || !ckn || !cak) {
wpa_printf(MSG_ERROR, "KaY: ckn or cak is null");
return NULL;
}
- if (cak->len != mka_alg_tbl[kay->mka_algindex].cak_len) {
- wpa_printf(MSG_ERROR, "KaY: CAK length not follow key schema");
+ if (cak->len != 16 && cak->len != 32) {
+ wpa_printf(MSG_ERROR, "KaY: Unexpected CAK length %u",
+ (unsigned int) cak->len);
return NULL;
}
if (ckn->len > MAX_CKN_LEN) {
- wpa_printf(MSG_ERROR, "KaY: CKN is out of range(<=32 bytes)");
+ wpa_printf(MSG_ERROR, "KaY: CKN is out of range (>32 bytes)");
return NULL;
}
if (!kay->enable) {
@@ -3311,8 +3612,12 @@ ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay,
participant->ckn.len = ckn->len;
os_memcpy(participant->ckn.name, ckn->name, ckn->len);
+ wpa_hexdump(MSG_DEBUG, "KaY: CKN", participant->ckn.name,
+ participant->ckn.len);
participant->cak.len = cak->len;
os_memcpy(participant->cak.key, cak->key, cak->len);
+ wpa_hexdump_key(MSG_DEBUG, "KaY: CAK", participant->cak.key,
+ participant->cak.len);
if (life)
participant->cak_life = life + time(NULL);
@@ -3362,6 +3667,8 @@ ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay,
if (!reset_participant_mi(participant))
goto fail;
+ wpa_printf(MSG_DEBUG, "KaY: Selected random MI: %s",
+ mi_txt(participant->mi));
participant->lrx = FALSE;
participant->ltx = FALSE;
@@ -3377,37 +3684,40 @@ ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay,
secy_cp_control_protect_frames(kay, kay->macsec_protect);
secy_cp_control_replay(kay, kay->macsec_replay_protect,
kay->macsec_replay_window);
- secy_create_transmit_sc(kay, participant->txsc);
+ if (secy_create_transmit_sc(kay, participant->txsc))
+ goto fail;
/* to derive KEK from CAK and CKN */
- participant->kek.len = mka_alg_tbl[kay->mka_algindex].kek_len;
+ participant->kek.len = participant->cak.len;
if (mka_alg_tbl[kay->mka_algindex].kek_trfm(participant->cak.key,
+ participant->cak.len,
participant->ckn.name,
participant->ckn.len,
- participant->kek.key)) {
- wpa_printf(MSG_ERROR, "KaY: Derived KEK failed");
+ participant->kek.key,
+ participant->kek.len)) {
+ wpa_printf(MSG_ERROR, "KaY: KEK derivation failed");
goto fail;
}
wpa_hexdump_key(MSG_DEBUG, "KaY: Derived KEK",
participant->kek.key, participant->kek.len);
/* to derive ICK from CAK and CKN */
- participant->ick.len = mka_alg_tbl[kay->mka_algindex].ick_len;
+ participant->ick.len = participant->cak.len;
if (mka_alg_tbl[kay->mka_algindex].ick_trfm(participant->cak.key,
+ participant->cak.len,
participant->ckn.name,
participant->ckn.len,
- participant->ick.key)) {
- wpa_printf(MSG_ERROR, "KaY: Derived ICK failed");
+ participant->ick.key,
+ participant->ick.len)) {
+ wpa_printf(MSG_ERROR, "KaY: ICK derivation failed");
goto fail;
}
wpa_hexdump_key(MSG_DEBUG, "KaY: Derived ICK",
participant->ick.key, participant->ick.len);
dl_list_add(&kay->participant_list, &participant->list);
- wpa_hexdump(MSG_DEBUG, "KaY: Participant created:",
- ckn->name, ckn->len);
- usecs = os_random() % (MKA_HELLO_TIME * 1000);
+ usecs = os_random() % (kay->mka_hello_time * 1000);
eloop_register_timeout(0, usecs, ieee802_1x_participant_timer,
participant, NULL);
@@ -3425,6 +3735,7 @@ ieee802_1x_kay_create_mka(struct ieee802_1x_kay *kay,
return participant;
fail:
+ os_free(participant->txsc);
os_free(participant);
return NULL;
}
@@ -3580,6 +3891,7 @@ ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay,
#ifdef CONFIG_CTRL_IFACE
+
/**
* ieee802_1x_kay_get_status - Get IEEE 802.1X KaY status details
* @sm: Pointer to KaY allocated with ieee802_1x_kay_init()
@@ -3588,19 +3900,24 @@ ieee802_1x_kay_change_cipher_suite(struct ieee802_1x_kay *kay,
* @verbose: Whether to include verbose status information
* Returns: Number of bytes written to buf.
*
- * Query KAY status information. This function fills in a text area with current
+ * Query KaY status information. This function fills in a text area with current
* status information. If the buffer (buf) is not large enough, status
* information will be truncated to fit the buffer.
*/
int ieee802_1x_kay_get_status(struct ieee802_1x_kay *kay, char *buf,
size_t buflen)
{
- int len;
+ char *pos, *end;
+ int res, count;
+ struct ieee802_1x_mka_participant *p;
if (!kay)
return 0;
- len = os_snprintf(buf, buflen,
+ pos = buf;
+ end = buf + buflen;
+
+ res = os_snprintf(pos, end - pos,
"PAE KaY status=%s\n"
"Authenticated=%s\n"
"Secured=%s\n"
@@ -3609,7 +3926,8 @@ int ieee802_1x_kay_get_status(struct ieee802_1x_kay *kay, char *buf,
"Key Server Priority=%u\n"
"Is Key Server=%s\n"
"Number of Keys Distributed=%u\n"
- "Number of Keys Received=%u\n",
+ "Number of Keys Received=%u\n"
+ "MKA Hello Time=%u\n",
kay->active ? "Active" : "Not-Active",
kay->authenticated ? "Yes" : "No",
kay->secured ? "Yes" : "No",
@@ -3618,10 +3936,162 @@ int ieee802_1x_kay_get_status(struct ieee802_1x_kay *kay, char *buf,
kay->key_server_priority,
kay->is_key_server ? "Yes" : "No",
kay->dist_kn - 1,
- kay->rcvd_keys);
- if (os_snprintf_error(buflen, len))
+ kay->rcvd_keys,
+ kay->mka_hello_time);
+ if (os_snprintf_error(buflen, res))
+ return 0;
+ pos += res;
+
+ res = os_snprintf(pos, end - pos,
+ "actor_sci=%s\n", sci_txt(&kay->actor_sci));
+ if (os_snprintf_error(buflen, res))
+ return end - pos;
+ pos += res;
+
+ res = os_snprintf(pos, end - pos,
+ "key_server_sci=%s\n", sci_txt(&kay->key_server_sci));
+ if (os_snprintf_error(buflen, res))
+ return end - pos;
+ pos += res;
+
+ count = 0;
+ dl_list_for_each(p, &kay->participant_list,
+ struct ieee802_1x_mka_participant, list) {
+ char *pos2 = pos;
+
+ res = os_snprintf(pos2, end - pos2, "participant_idx=%d\nckn=",
+ count);
+ if (os_snprintf_error(buflen, res))
+ return end - pos;
+ pos2 += res;
+ count++;
+
+ pos2 += wpa_snprintf_hex(pos2, end - pos2, p->ckn.name,
+ p->ckn.len);
+
+ res = os_snprintf(pos2, end - pos2,
+ "\nmi=%s\n"
+ "mn=%u\n"
+ "active=%s\n"
+ "participant=%s\n"
+ "retain=%s\n"
+ "live_peers=%u\n"
+ "potential_peers=%u\n"
+ "is_key_server=%s\n"
+ "is_elected=%s\n",
+ mi_txt(p->mi), p->mn,
+ yes_no(p->active),
+ yes_no(p->participant),
+ yes_no(p->retain),
+ dl_list_len(&p->live_peers),
+ dl_list_len(&p->potential_peers),
+ yes_no(p->is_key_server),
+ yes_no(p->is_elected));
+ if (os_snprintf_error(buflen, res))
+ return end - pos;
+ pos2 += res;
+ pos = pos2;
+ }
+
+ return pos - buf;
+}
+
+
+static const char * true_false(Boolean val)
+{
+ return val ? "true" : "false";
+}
+
+
+static const char * activate_control_txt(enum activate_ctrl activate)
+{
+ switch (activate) {
+ case DEFAULT:
+ return "default";
+ case DISABLED:
+ return "disabled";
+ case ON_OPER_UP:
+ return "onOperUp";
+ case ALWAYS:
+ return "always";
+ }
+
+ return "?";
+}
+
+
+static char * mka_mib_peer(struct dl_list *peers, Boolean live, char *buf,
+ char *end)
+{
+ char *pos = buf;
+ struct ieee802_1x_kay_peer *p;
+ int res;
+
+ dl_list_for_each(p, peers, struct ieee802_1x_kay_peer, list) {
+ res = os_snprintf(pos, end - pos,
+ "ieee8021XKayMkaPeerListMI=%s\n"
+ "ieee8021XKayMkaPeerListMN=%u\n"
+ "ieee8021XKayMkaPeerListType=%u\n"
+ "ieee8021XKayMkaPeerListSCI=%s\n",
+ mi_txt(p->mi),
+ p->mn,
+ live ? 1 : 2,
+ sci_txt(&p->sci));
+ if (os_snprintf_error(end - pos, res))
+ return pos;
+ pos += res;
+ }
+
+ return pos;
+}
+
+
+int ieee802_1x_kay_get_mib(struct ieee802_1x_kay *kay, char *buf,
+ size_t buflen)
+{
+ char *pos, *end;
+ int res;
+ struct ieee802_1x_mka_participant *p;
+
+ if (!kay)
return 0;
- return len;
+ pos = buf;
+ end = buf + buflen;
+
+ dl_list_for_each(p, &kay->participant_list,
+ struct ieee802_1x_mka_participant, list) {
+ char *pos2 = pos;
+
+ res = os_snprintf(pos2, end - pos2, "ieee8021XKayMkaPartCKN=");
+ if (os_snprintf_error(buflen, res))
+ return end - pos;
+ pos2 += res;
+
+ pos2 += wpa_snprintf_hex(pos2, end - pos2, p->ckn.name,
+ p->ckn.len);
+
+ res = os_snprintf(pos2, end - pos2,
+ "\nieee8021XKayMkaPartCached=%s\n"
+ "ieee8021XKayMkaPartActive=%s\n"
+ "ieee8021XKayMkaPartRetain=%s\n"
+ "ieee8021XKayMkaPartActivateControl=%s\n"
+ "ieee8021XKayMkaPartPrincipal=%s\n",
+ true_false(p->cached),
+ true_false(p->active),
+ true_false(p->retain),
+ activate_control_txt(p->activate),
+ true_false(p->principal));
+ if (os_snprintf_error(buflen, res))
+ return end - pos;
+ pos2 += res;
+ pos = pos2;
+
+ pos = mka_mib_peer(&p->live_peers, TRUE, pos, end);
+ pos = mka_mib_peer(&p->potential_peers, FALSE, pos, end);
+ }
+
+ return pos - buf;
}
+
#endif /* CONFIG_CTRL_IFACE */
diff --git a/src/pae/ieee802_1x_kay.h b/src/pae/ieee802_1x_kay.h
index b2650596cae3c..3367d3aaa8c1a 100644
--- a/src/pae/ieee802_1x_kay.h
+++ b/src/pae/ieee802_1x_kay.h
@@ -21,6 +21,7 @@ struct macsec_init_params;
/* MKA timer, unit: millisecond */
#define MKA_HELLO_TIME 2000
+#define MKA_BOUNDED_HELLO_TIME 500
#define MKA_LIFE_TIME 6000
#define MKA_SAK_RETIRE_TIME 3000
@@ -38,7 +39,7 @@ struct ieee802_1x_mka_ki {
struct ieee802_1x_mka_sci {
u8 addr[ETH_ALEN];
be16 port;
-};
+} STRUCT_PACKED;
struct mka_key {
u8 key[MAX_KEY_LEN];
@@ -149,6 +150,7 @@ struct ieee802_1x_kay_ctx {
int (*get_receive_lowest_pn)(void *ctx, struct receive_sa *sa);
int (*get_transmit_next_pn)(void *ctx, struct transmit_sa *sa);
int (*set_transmit_next_pn)(void *ctx, struct transmit_sa *sa);
+ int (*set_receive_lowest_pn)(void *ctx, struct receive_sa *sa);
int (*create_receive_sc)(void *ctx, struct receive_sc *sc,
enum validate_frames vf,
enum confidentiality_offset co);
@@ -187,6 +189,7 @@ struct ieee802_1x_kay {
u32 macsec_replay_window;
enum validate_frames macsec_validate;
enum confidentiality_offset macsec_confidentiality;
+ u32 mka_hello_time;
u32 ltx_kn;
u8 ltx_an;
@@ -236,6 +239,7 @@ u64 mka_sci_u64(struct ieee802_1x_mka_sci *sci);
struct ieee802_1x_kay *
ieee802_1x_kay_init(struct ieee802_1x_kay_ctx *ctx, enum macsec_policy policy,
+ Boolean macsec_replay_protect, u32 macsec_replay_window,
u16 port, u8 priority, const char *ifname, const u8 *addr);
void ieee802_1x_kay_deinit(struct ieee802_1x_kay *kay);
@@ -271,5 +275,7 @@ int ieee802_1x_kay_enable_rx_sas(struct ieee802_1x_kay *kay,
int ieee802_1x_kay_enable_new_info(struct ieee802_1x_kay *kay);
int ieee802_1x_kay_get_status(struct ieee802_1x_kay *kay, char *buf,
size_t buflen);
+int ieee802_1x_kay_get_mib(struct ieee802_1x_kay *kay, char *buf,
+ size_t buflen);
#endif /* IEEE802_1X_KAY_H */
diff --git a/src/pae/ieee802_1x_kay_i.h b/src/pae/ieee802_1x_kay_i.h
index bc522d89852bf..f9cd3f41b093b 100644
--- a/src/pae/ieee802_1x_kay_i.h
+++ b/src/pae/ieee802_1x_kay_i.h
@@ -15,7 +15,7 @@
#define MKA_VERSION_ID 1
-/* IEEE Std 802.1X-2010, 11.11.1, Table 11-7 */
+/* IEEE Std 802.1X-2010, 11.11.1, Table 11-7 (MKPDU parameter sets) */
enum mka_packet_type {
MKA_BASIC_PARAMETER_SET = MKA_VERSION_ID,
MKA_LIVE_PEER_LIST = 1,
@@ -39,7 +39,7 @@ struct ieee802_1x_kay;
struct ieee802_1x_mka_peer_id {
u8 mi[MI_LEN];
be32 mn;
-};
+} STRUCT_PACKED;
struct ieee802_1x_kay_peer {
struct ieee802_1x_mka_sci sci;
@@ -51,6 +51,7 @@ struct ieee802_1x_kay_peer {
Boolean macsec_desired;
enum macsec_cap macsec_capability;
Boolean sak_used;
+ int missing_sak_use_count;
struct dl_list list;
};
@@ -59,25 +60,24 @@ struct macsec_ciphersuite {
char name[32];
enum macsec_cap capable;
int sak_len; /* unit: byte */
-
- u32 index;
};
struct mka_alg {
u8 parameter[4];
- size_t cak_len;
- size_t kek_len;
- size_t ick_len;
size_t icv_len;
- int (*cak_trfm)(const u8 *msk, const u8 *mac1, const u8 *mac2, u8 *cak);
- int (*ckn_trfm)(const u8 *msk, const u8 *mac1, const u8 *mac2,
- const u8 *sid, size_t sid_len, u8 *ckn);
- int (*kek_trfm)(const u8 *cak, const u8 *ckn, size_t ckn_len, u8 *kek);
- int (*ick_trfm)(const u8 *cak, const u8 *ckn, size_t ckn_len, u8 *ick);
- int (*icv_hash)(const u8 *ick, const u8 *msg, size_t msg_len, u8 *icv);
-
- int index; /* index for configuring */
+ int (*cak_trfm)(const u8 *msk, size_t msk_bytes, const u8 *mac1,
+ const u8 *mac2, u8 *cak, size_t cak_bytes);
+ int (*ckn_trfm)(const u8 *msk, size_t msk_bytes, const u8 *mac1,
+ const u8 *mac2, const u8 *sid, size_t sid_len, u8 *ckn);
+ int (*kek_trfm)(const u8 *cak, size_t cak_bytes,
+ const u8 *ckn, size_t ckn_len,
+ u8 *kek, size_t kek_bytes);
+ int (*ick_trfm)(const u8 *cak, size_t cak_bytes,
+ const u8 *ckn, size_t ckn_len,
+ u8 *ick, size_t ick_bytes);
+ int (*icv_hash)(const u8 *ick, size_t ick_bytes,
+ const u8 *msg, size_t msg_len, u8 *icv);
};
#define DEFAULT_MKA_ALG_INDEX 0
@@ -95,7 +95,7 @@ struct ieee802_1x_mka_participant {
Boolean retain;
enum mka_created_mode mode;
- enum { DEFAULT, DISABLED, ON_OPER_UP, ALWAYS } activate;
+ enum activate_ctrl { DEFAULT, DISABLED, ON_OPER_UP, ALWAYS } activate;
/* used for active participant */
Boolean principal;
@@ -131,8 +131,10 @@ struct ieee802_1x_mka_participant {
u8 mi[MI_LEN];
u32 mn;
+ /* Current peer MI and SCI during MKPDU processing */
struct ieee802_1x_mka_peer_id current_peer_id;
struct ieee802_1x_mka_sci current_peer_sci;
+
time_t cak_life;
time_t mka_life;
Boolean to_dist_sak;
@@ -165,7 +167,7 @@ struct ieee802_1x_mka_hdr {
#endif
/* octet 4 */
u8 length1;
-};
+} STRUCT_PACKED;
#define MKA_HDR_LEN sizeof(struct ieee802_1x_mka_hdr)
@@ -210,9 +212,9 @@ struct ieee802_1x_mka_basic_body {
be32 actor_mn;
u8 algo_agility[4];
- /* followed by CAK Name*/
+ /* followed by CAK Name */
u8 ckn[0];
-};
+} STRUCT_PACKED;
/**
* struct ieee802_1x_mka_peer_body - Live Peer List and Potential Peer List
@@ -238,9 +240,9 @@ struct ieee802_1x_mka_peer_body {
/* octet 4 */
u8 length1;
- u8 peer[0];
/* followed by Peers */
-};
+ u8 peer[0];
+} STRUCT_PACKED;
/**
* struct ieee802_1x_mka_sak_use_body - MACsec SAK Use parameter set (Figure
@@ -315,7 +317,7 @@ struct ieee802_1x_mka_sak_use_body {
be32 okn;
/* octet 41 - 44 */
be32 olpn;
-};
+} STRUCT_PACKED;
/**
* struct ieee802_1x_mka_dist_sak_body - Distributed SAK parameter set
@@ -362,7 +364,7 @@ struct ieee802_1x_mka_dist_sak_body {
* for other cipher suite: octet 9-16: cipher suite id, octet 17-: SAK
*/
u8 sak[0];
-};
+} STRUCT_PACKED;
/**
* struct ieee802_1x_mka_dist_cak_body - Distributed CAK parameter set (Figure
@@ -398,7 +400,7 @@ struct ieee802_1x_mka_dist_cak_body {
/* followed by CAK Name, 29- */
u8 ckn[0];
-};
+} STRUCT_PACKED;
struct ieee802_1x_mka_icv_body {
/* octet 1 */
@@ -418,6 +420,6 @@ struct ieee802_1x_mka_icv_body {
/* octet 5 - */
u8 icv[0];
-};
+} STRUCT_PACKED;
#endif /* IEEE802_1X_KAY_I_H */
diff --git a/src/pae/ieee802_1x_key.c b/src/pae/ieee802_1x_key.c
index 9a8d923d14f18..d63ca7f7038ac 100644
--- a/src/pae/ieee802_1x_key.c
+++ b/src/pae/ieee802_1x_key.c
@@ -31,8 +31,9 @@ static void joint_two_mac(const u8 *mac1, const u8 *mac2, u8 *out)
/* IEEE Std 802.1X-2010, 6.2.1 KDF */
-static int aes_kdf_128(const u8 *kdk, const char *label, const u8 *context,
- int ctx_bits, int ret_bits, u8 *ret)
+static int aes_kdf(const u8 *kdk, size_t kdk_bits,
+ const char *label, const u8 *context,
+ int ctx_bits, int ret_bits, u8 *ret)
{
const int h = 128;
const int r = 8;
@@ -40,6 +41,9 @@ static int aes_kdf_128(const u8 *kdk, const char *label, const u8 *context,
int lab_len, ctx_len, ret_len, buf_len;
u8 *buf;
+ if (kdk_bits != 128 && kdk_bits != 256)
+ return -1;
+
lab_len = os_strlen(label);
ctx_len = (ctx_bits + 7) / 8;
ret_len = ((ret_bits & 0xffff) + 7) / 8;
@@ -60,8 +64,14 @@ static int aes_kdf_128(const u8 *kdk, const char *label, const u8 *context,
WPA_PUT_BE16(&buf[buf_len - 2], ret_bits);
for (i = 0; i < n; i++) {
+ int res;
+
buf[0] = (u8) (i + 1);
- if (omac1_aes_128(kdk, buf, buf_len, ret)) {
+ if (kdk_bits == 128)
+ res = omac1_aes_128(kdk, buf, buf_len, ret);
+ else
+ res = omac1_aes_256(kdk, buf, buf_len, ret);
+ if (res) {
os_free(buf);
return -1;
}
@@ -72,33 +82,32 @@ static int aes_kdf_128(const u8 *kdk, const char *label, const u8 *context,
}
-/********** AES-CMAC-128 **********/
/**
- * ieee802_1x_cak_128bits_aes_cmac
+ * ieee802_1x_cak_aes_cmac
*
* IEEE Std 802.1X-2010, 6.2.2
* CAK = KDF(Key, Label, mac1 | mac2, CAKlength)
*/
-int ieee802_1x_cak_128bits_aes_cmac(const u8 *msk, const u8 *mac1,
- const u8 *mac2, u8 *cak)
+int ieee802_1x_cak_aes_cmac(const u8 *msk, size_t msk_bytes, const u8 *mac1,
+ const u8 *mac2, u8 *cak, size_t cak_bytes)
{
u8 context[2 * ETH_ALEN];
joint_two_mac(mac1, mac2, context);
- return aes_kdf_128(msk, "IEEE8021 EAP CAK",
- context, sizeof(context) * 8, 128, cak);
+ return aes_kdf(msk, 8 * msk_bytes, "IEEE8021 EAP CAK",
+ context, sizeof(context) * 8, 8 * cak_bytes, cak);
}
/**
- * ieee802_1x_ckn_128bits_aes_cmac
+ * ieee802_1x_ckn_aes_cmac
*
* IEEE Std 802.1X-2010, 6.2.2
* CKN = KDF(Key, Label, ID | mac1 | mac2, CKNlength)
*/
-int ieee802_1x_ckn_128bits_aes_cmac(const u8 *msk, const u8 *mac1,
- const u8 *mac2, const u8 *sid,
- size_t sid_bytes, u8 *ckn)
+int ieee802_1x_ckn_aes_cmac(const u8 *msk, size_t msk_bytes, const u8 *mac1,
+ const u8 *mac2, const u8 *sid,
+ size_t sid_bytes, u8 *ckn)
{
int res;
u8 *context;
@@ -112,21 +121,21 @@ int ieee802_1x_ckn_128bits_aes_cmac(const u8 *msk, const u8 *mac1,
os_memcpy(context, sid, sid_bytes);
joint_two_mac(mac1, mac2, context + sid_bytes);
- res = aes_kdf_128(msk, "IEEE8021 EAP CKN", context, ctx_len * 8,
- 128, ckn);
+ res = aes_kdf(msk, 8 * msk_bytes, "IEEE8021 EAP CKN",
+ context, ctx_len * 8, 128, ckn);
os_free(context);
return res;
}
/**
- * ieee802_1x_kek_128bits_aes_cmac
+ * ieee802_1x_kek_aes_cmac
*
* IEEE Std 802.1X-2010, 9.3.3
* KEK = KDF(Key, Label, Keyid, KEKLength)
*/
-int ieee802_1x_kek_128bits_aes_cmac(const u8 *cak, const u8 *ckn,
- size_t ckn_bytes, u8 *kek)
+int ieee802_1x_kek_aes_cmac(const u8 *cak, size_t cak_bytes, const u8 *ckn,
+ size_t ckn_bytes, u8 *kek, size_t kek_bytes)
{
u8 context[16];
@@ -134,19 +143,20 @@ int ieee802_1x_kek_128bits_aes_cmac(const u8 *cak, const u8 *ckn,
os_memset(context, 0, sizeof(context));
os_memcpy(context, ckn, (ckn_bytes < 16) ? ckn_bytes : 16);
- return aes_kdf_128(cak, "IEEE8021 KEK", context, sizeof(context) * 8,
- 128, kek);
+ return aes_kdf(cak, 8 * cak_bytes, "IEEE8021 KEK",
+ context, sizeof(context) * 8,
+ 8 * kek_bytes, kek);
}
/**
- * ieee802_1x_ick_128bits_aes_cmac
+ * ieee802_1x_ick_aes_cmac
*
* IEEE Std 802.1X-2010, 9.3.3
* ICK = KDF(Key, Label, Keyid, ICKLength)
*/
-int ieee802_1x_ick_128bits_aes_cmac(const u8 *cak, const u8 *ckn,
- size_t ckn_bytes, u8 *ick)
+int ieee802_1x_ick_aes_cmac(const u8 *cak, size_t cak_bytes, const u8 *ckn,
+ size_t ckn_bytes, u8 *ick, size_t ick_bytes)
{
u8 context[16];
@@ -154,22 +164,32 @@ int ieee802_1x_ick_128bits_aes_cmac(const u8 *cak, const u8 *ckn,
os_memset(context, 0, sizeof(context));
os_memcpy(context, ckn, (ckn_bytes < 16) ? ckn_bytes : 16);
- return aes_kdf_128(cak, "IEEE8021 ICK", context, sizeof(context) * 8,
- 128, ick);
+ return aes_kdf(cak, 8 *cak_bytes, "IEEE8021 ICK",
+ context, sizeof(context) * 8,
+ 8 * ick_bytes, ick);
}
/**
- * ieee802_1x_icv_128bits_aes_cmac
+ * ieee802_1x_icv_aes_cmac
*
* IEEE Std 802.1X-2010, 9.4.1
* ICV = AES-CMAC(ICK, M, 128)
*/
-int ieee802_1x_icv_128bits_aes_cmac(const u8 *ick, const u8 *msg,
- size_t msg_bytes, u8 *icv)
+int ieee802_1x_icv_aes_cmac(const u8 *ick, size_t ick_bytes, const u8 *msg,
+ size_t msg_bytes, u8 *icv)
{
- if (omac1_aes_128(ick, msg, msg_bytes, icv)) {
- wpa_printf(MSG_ERROR, "MKA: omac1_aes_128 failed");
+ int res;
+
+ if (ick_bytes == 16)
+ res = omac1_aes_128(ick, msg, msg_bytes, icv);
+ else if (ick_bytes == 32)
+ res = omac1_aes_256(ick, msg, msg_bytes, icv);
+ else
+ return -1;
+ if (res) {
+ wpa_printf(MSG_ERROR,
+ "MKA: AES-CMAC failed for ICV calculation");
return -1;
}
return 0;
@@ -177,13 +197,14 @@ int ieee802_1x_icv_128bits_aes_cmac(const u8 *ick, const u8 *msg,
/**
- * ieee802_1x_sak_128bits_aes_cmac
+ * ieee802_1x_sak_aes_cmac
*
* IEEE Std 802.1X-2010, 9.8.1
* SAK = KDF(Key, Label, KS-nonce | MI-value list | KN, SAKLength)
*/
-int ieee802_1x_sak_128bits_aes_cmac(const u8 *cak, const u8 *ctx,
- size_t ctx_bytes, u8 *sak)
+int ieee802_1x_sak_aes_cmac(const u8 *cak, size_t cak_bytes, const u8 *ctx,
+ size_t ctx_bytes, u8 *sak, size_t sak_bytes)
{
- return aes_kdf_128(cak, "IEEE8021 SAK", ctx, ctx_bytes * 8, 128, sak);
+ return aes_kdf(cak, cak_bytes * 8, "IEEE8021 SAK", ctx, ctx_bytes * 8,
+ sak_bytes * 8, sak);
}
diff --git a/src/pae/ieee802_1x_key.h b/src/pae/ieee802_1x_key.h
index ea318ea4dde3f..1f9058de5cccf 100644
--- a/src/pae/ieee802_1x_key.h
+++ b/src/pae/ieee802_1x_key.h
@@ -9,18 +9,18 @@
#ifndef IEEE802_1X_KEY_H
#define IEEE802_1X_KEY_H
-int ieee802_1x_cak_128bits_aes_cmac(const u8 *msk, const u8 *mac1,
- const u8 *mac2, u8 *cak);
-int ieee802_1x_ckn_128bits_aes_cmac(const u8 *msk, const u8 *mac1,
- const u8 *mac2, const u8 *sid,
- size_t sid_bytes, u8 *ckn);
-int ieee802_1x_kek_128bits_aes_cmac(const u8 *cak, const u8 *ckn,
- size_t ckn_bytes, u8 *kek);
-int ieee802_1x_ick_128bits_aes_cmac(const u8 *cak, const u8 *ckn,
- size_t ckn_bytes, u8 *ick);
-int ieee802_1x_icv_128bits_aes_cmac(const u8 *ick, const u8 *msg,
- size_t msg_bytes, u8 *icv);
-int ieee802_1x_sak_128bits_aes_cmac(const u8 *cak, const u8 *ctx,
- size_t ctx_bytes, u8 *sak);
+int ieee802_1x_cak_aes_cmac(const u8 *msk, size_t msk_bytes, const u8 *mac1,
+ const u8 *mac2, u8 *cak, size_t cak_bytes);
+int ieee802_1x_ckn_aes_cmac(const u8 *msk, size_t msk_bytes, const u8 *mac1,
+ const u8 *mac2, const u8 *sid,
+ size_t sid_bytes, u8 *ckn);
+int ieee802_1x_kek_aes_cmac(const u8 *cak, size_t cak_bytes, const u8 *ckn,
+ size_t ckn_bytes, u8 *kek, size_t kek_bytes);
+int ieee802_1x_ick_aes_cmac(const u8 *cak, size_t cak_bytes, const u8 *ckn,
+ size_t ckn_bytes, u8 *ick, size_t ick_bytes);
+int ieee802_1x_icv_aes_cmac(const u8 *ick, size_t ick_bytes, const u8 *msg,
+ size_t msg_bytes, u8 *icv);
+int ieee802_1x_sak_aes_cmac(const u8 *cak, size_t cak_bytes, const u8 *ctx,
+ size_t ctx_bytes, u8 *sak, size_t sak_bytes);
#endif /* IEEE802_1X_KEY_H */
diff --git a/src/pae/ieee802_1x_secy_ops.c b/src/pae/ieee802_1x_secy_ops.c
index ab5339bb20462..84ee42b05896c 100644
--- a/src/pae/ieee802_1x_secy_ops.c
+++ b/src/pae/ieee802_1x_secy_ops.c
@@ -187,7 +187,7 @@ int secy_get_transmit_next_pn(struct ieee802_1x_kay *kay,
ops = kay->ctx;
if (!ops || !ops->get_transmit_next_pn) {
wpa_printf(MSG_ERROR,
- "KaY: secy get_receive_lowest_pn operation not supported");
+ "KaY: secy get_transmit_next_pn operation not supported");
return -1;
}
@@ -208,7 +208,7 @@ int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay,
ops = kay->ctx;
if (!ops || !ops->set_transmit_next_pn) {
wpa_printf(MSG_ERROR,
- "KaY: secy get_receive_lowest_pn operation not supported");
+ "KaY: secy set_transmit_next_pn operation not supported");
return -1;
}
@@ -216,6 +216,27 @@ int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay,
}
+int secy_set_receive_lowest_pn(struct ieee802_1x_kay *kay,
+ struct receive_sa *rxsa)
+{
+ struct ieee802_1x_kay_ctx *ops;
+
+ if (!kay || !rxsa) {
+ wpa_printf(MSG_ERROR, "KaY: %s params invalid", __func__);
+ return -1;
+ }
+
+ ops = kay->ctx;
+ if (!ops || !ops->set_receive_lowest_pn) {
+ wpa_printf(MSG_ERROR,
+ "KaY: secy set_receive_lowest_pn operation not supported");
+ return -1;
+ }
+
+ return ops->set_receive_lowest_pn(ops->ctx, rxsa);
+}
+
+
int secy_create_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc)
{
struct ieee802_1x_kay_ctx *ops;
diff --git a/src/pae/ieee802_1x_secy_ops.h b/src/pae/ieee802_1x_secy_ops.h
index 9fb29c3ddfa01..2d112ba7c5d58 100644
--- a/src/pae/ieee802_1x_secy_ops.h
+++ b/src/pae/ieee802_1x_secy_ops.h
@@ -36,6 +36,8 @@ int secy_get_transmit_next_pn(struct ieee802_1x_kay *kay,
struct transmit_sa *txsa);
int secy_set_transmit_next_pn(struct ieee802_1x_kay *kay,
struct transmit_sa *txsa);
+int secy_set_receive_lowest_pn(struct ieee802_1x_kay *kay,
+ struct receive_sa *txsa);
int secy_create_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc);
int secy_delete_receive_sc(struct ieee802_1x_kay *kay, struct receive_sc *rxsc);
int secy_create_receive_sa(struct ieee802_1x_kay *kay, struct receive_sa *rxsa);
diff --git a/src/radius/radius_client.c b/src/radius/radius_client.c
index a87ee745eb284..a3db4048c4a7a 100644
--- a/src/radius/radius_client.c
+++ b/src/radius/radius_client.c
@@ -26,12 +26,12 @@
#define RADIUS_CLIENT_MAX_WAIT 120
/**
- * RADIUS_CLIENT_MAX_RETRIES - RADIUS client maximum retries
+ * RADIUS_CLIENT_MAX_FAILOVER - RADIUS client maximum retries
*
- * Maximum number of retransmit attempts before the entry is removed from
+ * Maximum number of server failovers before the entry is removed from
* retransmit list.
*/
-#define RADIUS_CLIENT_MAX_RETRIES 10
+#define RADIUS_CLIENT_MAX_FAILOVER 3
/**
* RADIUS_CLIENT_MAX_ENTRIES - RADIUS client maximum pending messages
@@ -110,11 +110,16 @@ struct radius_msg_list {
os_time_t next_try;
/**
- * attempts - Number of transmission attempts
+ * attempts - Number of transmission attempts for one server
*/
int attempts;
/**
+ * accu_attempts - Number of accumulated attempts
+ */
+ int accu_attempts;
+
+ /**
* next_wait - Next retransmission wait time in seconds
*/
int next_wait;
@@ -367,9 +372,11 @@ static int radius_client_retransmit(struct radius_client_data *radius,
size_t prev_num_msgs;
u8 *acct_delay_time;
size_t acct_delay_time_len;
+ int num_servers;
if (entry->msg_type == RADIUS_ACCT ||
entry->msg_type == RADIUS_ACCT_INTERIM) {
+ num_servers = conf->num_acct_servers;
if (radius->acct_sock < 0)
radius_client_init_acct(radius);
if (radius->acct_sock < 0 && conf->num_acct_servers > 1) {
@@ -386,6 +393,7 @@ static int radius_client_retransmit(struct radius_client_data *radius,
conf->acct_server->retransmissions++;
}
} else {
+ num_servers = conf->num_auth_servers;
if (radius->auth_sock < 0)
radius_client_init_auth(radius);
if (radius->auth_sock < 0 && conf->num_auth_servers > 1) {
@@ -449,7 +457,15 @@ static int radius_client_retransmit(struct radius_client_data *radius,
}
/* retransmit; remove entry if too many attempts */
+ if (entry->accu_attempts > RADIUS_CLIENT_MAX_FAILOVER *
+ RADIUS_CLIENT_NUM_FAILOVER * num_servers) {
+ wpa_printf(MSG_INFO,
+ "RADIUS: Removing un-ACKed message due to too many failed retransmit attempts");
+ return 1;
+ }
+
entry->attempts++;
+ entry->accu_attempts++;
hostapd_logger(radius->ctx, entry->addr, HOSTAPD_MODULE_RADIUS,
HOSTAPD_LEVEL_DEBUG, "Resending RADIUS message (id=%d)",
radius_msg_get_hdr(entry->msg)->identifier);
@@ -466,10 +482,6 @@ static int radius_client_retransmit(struct radius_client_data *radius,
entry->next_wait *= 2;
if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT)
entry->next_wait = RADIUS_CLIENT_MAX_WAIT;
- if (entry->attempts >= RADIUS_CLIENT_MAX_RETRIES) {
- wpa_printf(MSG_INFO, "RADIUS: Removing un-ACKed message due to too many failed retransmit attempts");
- return 1;
- }
return 0;
}
@@ -490,6 +502,30 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx)
return;
os_get_reltime(&now);
+
+ while (entry) {
+ if (now.sec >= entry->next_try) {
+ s = entry->msg_type == RADIUS_AUTH ? radius->auth_sock :
+ radius->acct_sock;
+ if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER ||
+ (s < 0 && entry->attempts > 0)) {
+ if (entry->msg_type == RADIUS_ACCT ||
+ entry->msg_type == RADIUS_ACCT_INTERIM)
+ acct_failover++;
+ else
+ auth_failover++;
+ }
+ }
+ entry = entry->next;
+ }
+
+ if (auth_failover)
+ radius_client_auth_failover(radius);
+
+ if (acct_failover)
+ radius_client_acct_failover(radius);
+
+ entry = radius->msgs;
first = 0;
prev = NULL;
@@ -517,17 +553,6 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx)
continue;
}
- s = entry->msg_type == RADIUS_AUTH ? radius->auth_sock :
- radius->acct_sock;
- if (entry->attempts > RADIUS_CLIENT_NUM_FAILOVER ||
- (s < 0 && entry->attempts > 0)) {
- if (entry->msg_type == RADIUS_ACCT ||
- entry->msg_type == RADIUS_ACCT_INTERIM)
- acct_failover++;
- else
- auth_failover++;
- }
-
if (first == 0 || entry->next_try < first)
first = entry->next_try;
@@ -538,6 +563,7 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx)
if (radius->msgs) {
if (first < now.sec)
first = now.sec;
+ eloop_cancel_timeout(radius_client_timer, radius, NULL);
eloop_register_timeout(first - now.sec, 0,
radius_client_timer, radius, NULL);
hostapd_logger(radius->ctx, NULL, HOSTAPD_MODULE_RADIUS,
@@ -545,12 +571,6 @@ static void radius_client_timer(void *eloop_ctx, void *timeout_ctx)
"retransmit in %ld seconds",
(long int) (first - now.sec));
}
-
- if (auth_failover)
- radius_client_auth_failover(radius);
-
- if (acct_failover)
- radius_client_acct_failover(radius);
}
@@ -674,7 +694,10 @@ static void radius_client_list_add(struct radius_client_data *radius,
entry->first_try = entry->last_attempt.sec;
entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
entry->attempts = 1;
+ entry->accu_attempts = 1;
entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
+ if (entry->next_wait > RADIUS_CLIENT_MAX_WAIT)
+ entry->next_wait = RADIUS_CLIENT_MAX_WAIT;
entry->next = radius->msgs;
radius->msgs = entry;
radius_client_update_timeout(radius);
@@ -713,9 +736,9 @@ static void radius_client_list_add(struct radius_client_data *radius,
*
* The message is added on the retransmission queue and will be retransmitted
* automatically until a response is received or maximum number of retries
- * (RADIUS_CLIENT_MAX_RETRIES) is reached. No such retries are used with
- * RADIUS_ACCT_INTERIM, i.e., such a pending message is removed from the queue
- * automatically on transmission failure.
+ * (RADIUS_CLIENT_MAX_FAILOVER * RADIUS_CLIENT_NUM_FAILOVER) is reached. No
+ * such retries are used with RADIUS_ACCT_INTERIM, i.e., such a pending message
+ * is removed from the queue automatically on transmission failure.
*
* The related device MAC address can be used to identify pending messages that
* can be removed with radius_client_flush_auth().
@@ -1087,14 +1110,13 @@ radius_change_server(struct radius_client_data *radius,
}
}
- /* Reset retry counters for the new server */
- for (entry = radius->msgs; oserv && oserv != nserv && entry;
- entry = entry->next) {
+ /* Reset retry counters */
+ for (entry = radius->msgs; oserv && entry; entry = entry->next) {
if ((auth && entry->msg_type != RADIUS_AUTH) ||
(!auth && entry->msg_type != RADIUS_ACCT))
continue;
entry->next_try = entry->first_try + RADIUS_CLIENT_FIRST_WAIT;
- entry->attempts = 0;
+ entry->attempts = 1;
entry->next_wait = RADIUS_CLIENT_FIRST_WAIT * 2;
}
diff --git a/src/radius/radius_server.c b/src/radius/radius_server.c
index e3afc0d532981..b621ada554ee9 100644
--- a/src/radius/radius_server.c
+++ b/src/radius/radius_server.c
@@ -1,6 +1,6 @@
/*
* RADIUS authentication server
- * Copyright (c) 2005-2009, 2011-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2009, 2011-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -357,6 +357,7 @@ struct radius_server_data {
char *subscr_remediation_url;
u8 subscr_remediation_method;
+ char *hs20_sim_provisioning_url;
char *t_c_server_url;
@@ -380,6 +381,44 @@ static void radius_server_session_timeout(void *eloop_ctx, void *timeout_ctx);
static void radius_server_session_remove_timeout(void *eloop_ctx,
void *timeout_ctx);
+#ifdef CONFIG_SQLITE
+#ifdef CONFIG_HS20
+
+static int db_table_exists(sqlite3 *db, const char *name)
+{
+ char cmd[128];
+
+ os_snprintf(cmd, sizeof(cmd), "SELECT 1 FROM %s;", name);
+ return sqlite3_exec(db, cmd, NULL, NULL, NULL) == SQLITE_OK;
+}
+
+
+static int db_table_create_sim_provisioning(sqlite3 *db)
+{
+ char *err = NULL;
+ const char *sql =
+ "CREATE TABLE sim_provisioning("
+ " mobile_identifier_hash TEXT PRIMARY KEY,"
+ " imsi TEXT,"
+ " mac_addr TEXT,"
+ " eap_method TEXT,"
+ " timestamp TEXT"
+ ");";
+
+ RADIUS_DEBUG("Adding database table for SIM provisioning information");
+ if (sqlite3_exec(db, sql, NULL, NULL, &err) != SQLITE_OK) {
+ RADIUS_ERROR("SQLite error: %s", err);
+ sqlite3_free(err);
+ return -1;
+ }
+
+ return 0;
+}
+
+#endif /* CONFIG_HS20 */
+#endif /* CONFIG_SQLITE */
+
+
void srv_log(struct radius_session *sess, const char *fmt, ...)
PRINTF_FORMAT(2, 3);
@@ -637,6 +676,23 @@ static void radius_server_testing_options(struct radius_session *sess,
}
+#ifdef CONFIG_ERP
+static struct eap_server_erp_key *
+radius_server_erp_find_key(struct radius_server_data *data, const char *keyname)
+{
+ struct eap_server_erp_key *erp;
+
+ dl_list_for_each(erp, &data->erp_keys, struct eap_server_erp_key,
+ list) {
+ if (os_strcmp(erp->keyname_nai, keyname) == 0)
+ return erp;
+ }
+
+ return NULL;
+}
+#endif /* CONFIG_ERP */
+
+
static struct radius_session *
radius_server_get_new_session(struct radius_server_data *data,
struct radius_client *client,
@@ -647,7 +703,7 @@ radius_server_get_new_session(struct radius_server_data *data,
int res;
struct radius_session *sess;
struct eap_config eap_conf;
- struct eap_user tmp;
+ struct eap_user *tmp;
RADIUS_DEBUG("Creating a new session");
@@ -658,12 +714,27 @@ radius_server_get_new_session(struct radius_server_data *data,
}
RADIUS_DUMP_ASCII("User-Name", user, user_len);
- os_memset(&tmp, 0, sizeof(tmp));
- res = data->get_eap_user(data->conf_ctx, user, user_len, 0, &tmp);
- bin_clear_free(tmp.password, tmp.password_len);
+ tmp = os_zalloc(sizeof(*tmp));
+ if (!tmp)
+ return NULL;
+ res = data->get_eap_user(data->conf_ctx, user, user_len, 0, tmp);
+#ifdef CONFIG_ERP
+ if (res != 0 && data->erp) {
+ char *username;
+
+ username = os_zalloc(user_len + 1);
+ if (username) {
+ os_memcpy(username, user, user_len);
+ if (radius_server_erp_find_key(data, username))
+ res = 0;
+ os_free(username);
+ }
+ }
+#endif /* CONFIG_ERP */
if (res != 0) {
RADIUS_DEBUG("User-Name not found from user database");
+ eap_user_free(tmp);
return NULL;
}
@@ -671,10 +742,12 @@ radius_server_get_new_session(struct radius_server_data *data,
sess = radius_server_new_session(data, client);
if (sess == NULL) {
RADIUS_DEBUG("Failed to create a new session");
+ eap_user_free(tmp);
return NULL;
}
- sess->accept_attr = tmp.accept_attr;
- sess->macacl = tmp.macacl;
+ sess->accept_attr = tmp->accept_attr;
+ sess->macacl = tmp->macacl;
+ eap_user_free(tmp);
sess->username = os_malloc(user_len * 4 + 1);
if (sess->username == NULL) {
@@ -866,6 +939,117 @@ static void db_update_last_msk(struct radius_session *sess, const char *msk)
}
+#ifdef CONFIG_HS20
+
+static int radius_server_is_sim_method(struct radius_session *sess)
+{
+ const char *name;
+
+ name = eap_get_method(sess->eap);
+ return name &&
+ (os_strcmp(name, "SIM") == 0 ||
+ os_strcmp(name, "AKA") == 0 ||
+ os_strcmp(name, "AKA'") == 0);
+}
+
+
+static int radius_server_hs20_missing_sim_pps(struct radius_msg *request)
+{
+ u8 *buf, *pos, *end, type, sublen;
+ size_t len;
+
+ buf = NULL;
+ for (;;) {
+ if (radius_msg_get_attr_ptr(request,
+ RADIUS_ATTR_VENDOR_SPECIFIC,
+ &buf, &len, buf) < 0)
+ return 0;
+ if (len < 6)
+ continue;
+ pos = buf;
+ end = buf + len;
+ if (WPA_GET_BE32(pos) != RADIUS_VENDOR_ID_WFA)
+ continue;
+ pos += 4;
+
+ type = *pos++;
+ sublen = *pos++;
+ if (sublen < 2)
+ continue; /* invalid length */
+ sublen -= 2; /* skip header */
+ if (pos + sublen > end)
+ continue; /* invalid WFA VSA */
+
+ if (type != RADIUS_VENDOR_ATTR_WFA_HS20_STA_VERSION)
+ continue;
+
+ RADIUS_DUMP("HS2.0 mobile device version", pos, sublen);
+ if (sublen < 1 + 2)
+ continue;
+ if (pos[0] == 0)
+ continue; /* Release 1 STA does not support provisioning
+
+ */
+ /* UpdateIdentifier 0 indicates no PPS MO */
+ return WPA_GET_BE16(pos + 1) == 0;
+ }
+}
+
+
+#define HS20_MOBILE_ID_HASH_LEN 16
+
+static int radius_server_sim_provisioning_session(struct radius_session *sess,
+ const u8 *hash)
+{
+#ifdef CONFIG_SQLITE
+ char *sql;
+ char addr_txt[ETH_ALEN * 3];
+ char hash_txt[2 * HS20_MOBILE_ID_HASH_LEN + 1];
+ struct os_time now;
+ int res;
+ const char *imsi, *eap_method;
+
+ if (!sess->server->db ||
+ (!db_table_exists(sess->server->db, "sim_provisioning") &&
+ db_table_create_sim_provisioning(sess->server->db) < 0))
+ return -1;
+
+ imsi = eap_get_imsi(sess->eap);
+ if (!imsi)
+ return -1;
+
+ eap_method = eap_get_method(sess->eap);
+ if (!eap_method)
+ return -1;
+
+ os_snprintf(addr_txt, sizeof(addr_txt), MACSTR,
+ MAC2STR(sess->mac_addr));
+ wpa_snprintf_hex(hash_txt, sizeof(hash_txt), hash,
+ HS20_MOBILE_ID_HASH_LEN);
+
+ os_get_time(&now);
+ sql = sqlite3_mprintf("INSERT INTO sim_provisioning(mobile_identifier_hash,imsi,mac_addr,eap_method,timestamp) VALUES (%Q,%Q,%Q,%Q,%u)",
+ hash_txt, imsi, addr_txt, eap_method, now.sec);
+ if (!sql)
+ return -1;
+
+ if (sqlite3_exec(sess->server->db, sql, NULL, NULL, NULL) !=
+ SQLITE_OK) {
+ RADIUS_ERROR("Failed to add SIM provisioning entry into sqlite database: %s",
+ sqlite3_errmsg(sess->server->db));
+ res = -1;
+ } else {
+ res = 0;
+ }
+ sqlite3_free(sql);
+ return res;
+#endif /* CONFIG_SQLITE */
+ return -1;
+}
+
+#endif /* CONFIG_HS20 */
+
+
static struct radius_msg *
radius_server_encapsulate_eap(struct radius_server_data *data,
struct radius_client *client,
@@ -979,6 +1163,48 @@ radius_server_encapsulate_eap(struct radius_server_data *data,
buf, 0)) {
RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem");
}
+ } else if (code == RADIUS_CODE_ACCESS_ACCEPT &&
+ data->hs20_sim_provisioning_url &&
+ radius_server_is_sim_method(sess) &&
+ radius_server_hs20_missing_sim_pps(request)) {
+ u8 *buf, *pos, hash[HS20_MOBILE_ID_HASH_LEN];
+ size_t prefix_len, url_len;
+
+ RADIUS_DEBUG("Device needs HS 2.0 SIM provisioning");
+
+ if (os_get_random(hash, HS20_MOBILE_ID_HASH_LEN) < 0) {
+ radius_msg_free(msg);
+ return NULL;
+ }
+ RADIUS_DUMP("hotspot2dot0-mobile-identifier-hash",
+ hash, HS20_MOBILE_ID_HASH_LEN);
+
+ if (radius_server_sim_provisioning_session(sess, hash) < 0) {
+ radius_msg_free(msg);
+ return NULL;
+ }
+
+ prefix_len = os_strlen(data->hs20_sim_provisioning_url);
+ url_len = prefix_len + 2 * HS20_MOBILE_ID_HASH_LEN;
+ buf = os_malloc(1 + url_len + 1);
+ if (!buf) {
+ radius_msg_free(msg);
+ return NULL;
+ }
+ pos = buf;
+ *pos++ = data->subscr_remediation_method;
+ os_memcpy(pos, data->hs20_sim_provisioning_url, prefix_len);
+ pos += prefix_len;
+ wpa_snprintf_hex((char *) pos, 2 * HS20_MOBILE_ID_HASH_LEN + 1,
+ hash, HS20_MOBILE_ID_HASH_LEN);
+ RADIUS_DEBUG("HS 2.0 subscription remediation URL: %s",
+ (char *) &buf[1]);
+ if (!radius_msg_add_wfa(
+ msg, RADIUS_VENDOR_ATTR_WFA_HS20_SUBSCR_REMEDIATION,
+ buf, 1 + url_len)) {
+ RADIUS_DEBUG("Failed to add WFA-HS20-SubscrRem");
+ }
+ os_free(buf);
}
if (code == RADIUS_CODE_ACCESS_ACCEPT && sess->t_c_filtering) {
@@ -2059,7 +2285,7 @@ radius_server_read_clients(const char *client_file, int ipv6)
entry->addr.s_addr = addr.s_addr;
val = 0;
for (i = 0; i < mask; i++)
- val |= 1 << (31 - i);
+ val |= 1U << (31 - i);
entry->mask.s_addr = htonl(val);
}
#ifdef CONFIG_IPV6
@@ -2173,6 +2399,9 @@ radius_server_init(struct radius_server_conf *conf)
os_strdup(conf->subscr_remediation_url);
}
data->subscr_remediation_method = conf->subscr_remediation_method;
+ if (conf->hs20_sim_provisioning_url)
+ data->hs20_sim_provisioning_url =
+ os_strdup(conf->hs20_sim_provisioning_url);
if (conf->t_c_server_url)
data->t_c_server_url = os_strdup(conf->t_c_server_url);
@@ -2293,6 +2522,7 @@ void radius_server_deinit(struct radius_server_data *data)
os_free(data->dump_msk_file);
#endif /* CONFIG_RADIUS_TEST */
os_free(data->subscr_remediation_url);
+ os_free(data->hs20_sim_provisioning_url);
os_free(data->t_c_server_url);
#ifdef CONFIG_SQLITE
@@ -2506,15 +2736,8 @@ radius_server_erp_get_key(void *ctx, const char *keyname)
{
struct radius_session *sess = ctx;
struct radius_server_data *data = sess->server;
- struct eap_server_erp_key *erp;
-
- dl_list_for_each(erp, &data->erp_keys, struct eap_server_erp_key,
- list) {
- if (os_strcmp(erp->keyname_nai, keyname) == 0)
- return erp;
- }
- return NULL;
+ return radius_server_erp_find_key(data, keyname);
}
diff --git a/src/radius/radius_server.h b/src/radius/radius_server.h
index 167bbf5b28813..53728f9d7bf27 100644
--- a/src/radius/radius_server.h
+++ b/src/radius/radius_server.h
@@ -233,6 +233,7 @@ struct radius_server_conf {
char *subscr_remediation_url;
u8 subscr_remediation_method;
+ char *hs20_sim_provisioning_url;
char *t_c_server_url;
};
diff --git a/src/rsn_supp/pmksa_cache.c b/src/rsn_supp/pmksa_cache.c
index fdd522087dbc9..d720f7b811441 100644
--- a/src/rsn_supp/pmksa_cache.c
+++ b/src/rsn_supp/pmksa_cache.c
@@ -263,7 +263,8 @@ pmksa_cache_add_entry(struct rsn_pmksa_cache *pmksa,
}
pmksa->pmksa_count++;
wpa_printf(MSG_DEBUG, "RSN: Added PMKSA cache entry for " MACSTR
- " network_ctx=%p", MAC2STR(entry->aa), entry->network_ctx);
+ " network_ctx=%p akmp=0x%x", MAC2STR(entry->aa),
+ entry->network_ctx, entry->akmp);
wpa_sm_add_pmkid(pmksa->sm, entry->network_ctx, entry->aa, entry->pmkid,
entry->fils_cache_id_set ? entry->fils_cache_id : NULL,
entry->pmk, entry->pmk_len);
diff --git a/src/rsn_supp/tdls.c b/src/rsn_supp/tdls.c
index 345b0c84d871c..704c95e686169 100644
--- a/src/rsn_supp/tdls.c
+++ b/src/rsn_supp/tdls.c
@@ -1594,7 +1594,7 @@ static int copy_supp_rates(const struct wpa_eapol_ie_parse *kde,
peer->supp_rates, sizeof(peer->supp_rates),
kde->supp_rates + 2, kde->supp_rates_len - 2,
kde->ext_supp_rates ? kde->ext_supp_rates + 2 : NULL,
- kde->ext_supp_rates_len - 2);
+ kde->ext_supp_rates ? kde->ext_supp_rates_len - 2 : 0);
return 0;
}
diff --git a/src/rsn_supp/wpa.c b/src/rsn_supp/wpa.c
index e0c913074c631..9163f61fa2f24 100644
--- a/src/rsn_supp/wpa.c
+++ b/src/rsn_supp/wpa.c
@@ -20,8 +20,10 @@
#include "crypto/sha512.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
+#include "common/ocv.h"
#include "eap_common/eap_defs.h"
#include "eapol_supp/eapol_supp_sm.h"
+#include "drivers/driver.h"
#include "wpa.h"
#include "eloop.h"
#include "preauth.h"
@@ -382,6 +384,11 @@ static int wpa_supplicant_get_pmk(struct wpa_sm *sm,
if (!sm->cur_pmksa)
sm->cur_pmksa = sa;
+#ifdef CONFIG_IEEE80211R
+ } else if (wpa_key_mgmt_ft(sm->key_mgmt) && sm->ft_protocol) {
+ wpa_printf(MSG_DEBUG,
+ "FT: Continue 4-way handshake without PMK/PMKID for association using FT protocol");
+#endif /* CONFIG_IEEE80211R */
} else {
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Failed to get master session key from "
@@ -532,15 +539,25 @@ int wpa_supplicant_send_2_of_4(struct wpa_sm *sm, const unsigned char *dst,
static int wpa_derive_ptk(struct wpa_sm *sm, const unsigned char *src_addr,
const struct wpa_eapol_key *key, struct wpa_ptk *ptk)
{
+ const u8 *z = NULL;
+ size_t z_len = 0;
+
#ifdef CONFIG_IEEE80211R
if (wpa_key_mgmt_ft(sm->key_mgmt))
return wpa_derive_ptk_ft(sm, src_addr, key, ptk);
#endif /* CONFIG_IEEE80211R */
+#ifdef CONFIG_DPP2
+ if (sm->key_mgmt == WPA_KEY_MGMT_DPP && sm->dpp_z) {
+ z = wpabuf_head(sm->dpp_z);
+ z_len = wpabuf_len(sm->dpp_z);
+ }
+#endif /* CONFIG_DPP2 */
+
return wpa_pmk_to_ptk(sm->pmk, sm->pmk_len, "Pairwise key expansion",
sm->own_addr, sm->bssid, sm->snonce,
key->key_nonce, ptk, sm->key_mgmt,
- sm->pairwise_cipher);
+ sm->pairwise_cipher, z, z_len);
}
@@ -618,6 +635,33 @@ static void wpa_supplicant_process_1_of_4(struct wpa_sm *sm,
kde = sm->assoc_wpa_ie;
kde_len = sm->assoc_wpa_ie_len;
+#ifdef CONFIG_OCV
+ if (wpa_sm_ocv_enabled(sm)) {
+ struct wpa_channel_info ci;
+ u8 *pos;
+
+ if (wpa_sm_channel_info(sm, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "Failed to get channel info for OCI element in EAPOL-Key 2/4");
+ goto failed;
+ }
+
+ kde_buf = os_malloc(kde_len + 2 + RSN_SELECTOR_LEN + 3);
+ if (!kde_buf) {
+ wpa_printf(MSG_WARNING,
+ "Failed to allocate memory for KDE with OCI in EAPOL-Key 2/4");
+ goto failed;
+ }
+
+ os_memcpy(kde_buf, kde, kde_len);
+ kde = kde_buf;
+ pos = kde + kde_len;
+ if (ocv_insert_oci_kde(&ci, &pos) < 0)
+ goto failed;
+ kde_len = pos - kde;
+ }
+#endif /* CONFIG_OCV */
+
#ifdef CONFIG_P2P
if (sm->p2p) {
kde_buf = os_malloc(kde_len + 2 + RSN_SELECTOR_LEN + 1);
@@ -686,7 +730,9 @@ static void wpa_supplicant_key_neg_complete(struct wpa_sm *sm,
* likelihood of the first preauth EAPOL-Start frame getting to
* the target AP.
*/
- eloop_register_timeout(1, 0, wpa_sm_start_preauth, sm, NULL);
+ if (!dl_list_empty(&sm->pmksa_candidates))
+ eloop_register_timeout(1, 0, wpa_sm_start_preauth,
+ sm, NULL);
}
if (sm->cur_pmksa && sm->cur_pmksa->opportunistic) {
@@ -980,8 +1026,6 @@ static int wpa_supplicant_pairwise_gtk(struct wpa_sm *sm,
}
os_memset(&gd, 0, sizeof(gd));
- wpa_supplicant_key_neg_complete(sm, sm->bssid,
- key_info & WPA_KEY_INFO_SECURE);
return 0;
}
@@ -1019,9 +1063,27 @@ static int wpa_supplicant_install_igtk(struct wpa_sm *sm,
broadcast_ether_addr,
keyidx, 0, igtk->pn, sizeof(igtk->pn),
igtk->igtk, len) < 0) {
- wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
- "WPA: Failed to configure IGTK to the driver");
- return -1;
+ if (keyidx == 0x0400 || keyidx == 0x0500) {
+ /* Assume the AP has broken PMF implementation since it
+ * seems to have swapped the KeyID bytes. The AP cannot
+ * be trusted to implement BIP correctly or provide a
+ * valid IGTK, so do not try to configure this key with
+ * swapped KeyID bytes. Instead, continue without
+ * configuring the IGTK so that the driver can drop any
+ * received group-addressed robust management frames due
+ * to missing keys.
+ *
+ * Normally, this error behavior would result in us
+ * disconnecting, but there are number of deployed APs
+ * with this broken behavior, so as an interoperability
+ * workaround, allow the connection to proceed. */
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "WPA: Ignore IGTK configuration error due to invalid IGTK KeyID byte order");
+ } else {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "WPA: Failed to configure IGTK to the driver");
+ return -1;
+ }
}
if (wnm_sleep) {
@@ -1418,6 +1480,26 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
}
#endif /* CONFIG_P2P */
+#ifdef CONFIG_OCV
+ if (wpa_sm_ocv_enabled(sm)) {
+ struct wpa_channel_info ci;
+
+ if (wpa_sm_channel_info(sm, &ci) != 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "Failed to get channel info to validate received OCI in EAPOL-Key 3/4");
+ return;
+ }
+
+ if (ocv_verify_tx_params(ie.oci, ie.oci_len, &ci,
+ channel_width_to_int(ci.chanwidth),
+ ci.seg1_idx) != 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "%s",
+ ocv_errorstr);
+ return;
+ }
+ }
+#endif /* CONFIG_OCV */
+
if (wpa_supplicant_send_4_of_4(sm, sm->bssid, key, ver, key_info,
&sm->ptk) < 0) {
goto failed;
@@ -1442,8 +1524,11 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
wpa_sm_set_state(sm, WPA_GROUP_HANDSHAKE);
if (sm->group_cipher == WPA_CIPHER_GTK_NOT_USED) {
- wpa_supplicant_key_neg_complete(sm, sm->bssid,
- key_info & WPA_KEY_INFO_SECURE);
+ /* No GTK to be set to the driver */
+ } else if (!ie.gtk && sm->proto == WPA_PROTO_RSN) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_INFO,
+ "RSN: No GTK KDE included in EAPOL-Key msg 3/4");
+ goto failed;
} else if (ie.gtk &&
wpa_supplicant_pairwise_gtk(sm, key,
ie.gtk, ie.gtk_len, key_info) < 0) {
@@ -1458,6 +1543,10 @@ static void wpa_supplicant_process_3_of_4(struct wpa_sm *sm,
goto failed;
}
+ if (sm->group_cipher == WPA_CIPHER_GTK_NOT_USED || ie.gtk)
+ wpa_supplicant_key_neg_complete(sm, sm->bssid,
+ key_info & WPA_KEY_INFO_SECURE);
+
if (ie.gtk)
wpa_sm_set_rekey_offload(sm);
@@ -1511,6 +1600,26 @@ static int wpa_supplicant_process_1_of_2_rsn(struct wpa_sm *sm,
}
maxkeylen = gd->gtk_len = ie.gtk_len - 2;
+#ifdef CONFIG_OCV
+ if (wpa_sm_ocv_enabled(sm)) {
+ struct wpa_channel_info ci;
+
+ if (wpa_sm_channel_info(sm, &ci) != 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
+ "Failed to get channel info to validate received OCI in EAPOL-Key group msg 1/2");
+ return -1;
+ }
+
+ if (ocv_verify_tx_params(ie.oci, ie.oci_len, &ci,
+ channel_width_to_int(ci.chanwidth),
+ ci.seg1_idx) != 0) {
+ wpa_msg(sm->ctx->msg_ctx, MSG_WARNING, "%s",
+ ocv_errorstr);
+ return -1;
+ }
+ }
+#endif /* CONFIG_OCV */
+
if (wpa_supplicant_check_group_cipher(sm, sm->group_cipher,
gd->gtk_len, maxkeylen,
&gd->key_rsc_len, &gd->alg))
@@ -1631,11 +1740,17 @@ static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm,
size_t mic_len, hdrlen, rlen;
struct wpa_eapol_key *reply;
u8 *rbuf, *key_mic;
+ size_t kde_len = 0;
+
+#ifdef CONFIG_OCV
+ if (wpa_sm_ocv_enabled(sm))
+ kde_len = OCV_OCI_KDE_LEN;
+#endif /* CONFIG_OCV */
mic_len = wpa_mic_len(sm->key_mgmt, sm->pmk_len);
hdrlen = sizeof(*reply) + mic_len + 2;
rbuf = wpa_sm_alloc_eapol(sm, IEEE802_1X_TYPE_EAPOL_KEY, NULL,
- hdrlen, &rlen, (void *) &reply);
+ hdrlen + kde_len, &rlen, (void *) &reply);
if (rbuf == NULL)
return -1;
@@ -1657,7 +1772,27 @@ static int wpa_supplicant_send_2_of_2(struct wpa_sm *sm,
WPA_REPLAY_COUNTER_LEN);
key_mic = (u8 *) (reply + 1);
- WPA_PUT_BE16(key_mic + mic_len, 0);
+ WPA_PUT_BE16(key_mic + mic_len, kde_len); /* Key Data Length */
+
+#ifdef CONFIG_OCV
+ if (wpa_sm_ocv_enabled(sm)) {
+ struct wpa_channel_info ci;
+ u8 *pos;
+
+ if (wpa_sm_channel_info(sm, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "Failed to get channel info for OCI element in EAPOL-Key 2/2");
+ os_free(rbuf);
+ return -1;
+ }
+
+ pos = key_mic + mic_len + 2; /* Key Data */
+ if (ocv_insert_oci_kde(&ci, &pos) < 0) {
+ os_free(rbuf);
+ return -1;
+ }
+ }
+#endif /* CONFIG_OCV */
wpa_dbg(sm->ctx->msg_ctx, MSG_DEBUG, "WPA: Sending EAPOL-Key 2/2");
return wpa_eapol_key_send(sm, &sm->ptk, ver, sm->bssid, ETH_P_EAPOL,
@@ -1755,7 +1890,15 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm,
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Invalid EAPOL-Key MIC "
"when using TPTK - ignoring TPTK");
+#ifdef TEST_FUZZ
+ wpa_printf(MSG_INFO,
+ "TEST: Ignore Key MIC failure for fuzz testing");
+ goto continue_fuzz;
+#endif /* TEST_FUZZ */
} else {
+#ifdef TEST_FUZZ
+ continue_fuzz:
+#endif /* TEST_FUZZ */
ok = 1;
sm->tptk_set = 0;
sm->ptk_set = 1;
@@ -1781,8 +1924,16 @@ static int wpa_supplicant_verify_eapol_key_mic(struct wpa_sm *sm,
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: Invalid EAPOL-Key MIC - "
"dropping packet");
+#ifdef TEST_FUZZ
+ wpa_printf(MSG_INFO,
+ "TEST: Ignore Key MIC failure for fuzz testing");
+ goto continue_fuzz2;
+#endif /* TEST_FUZZ */
return -1;
}
+#ifdef TEST_FUZZ
+ continue_fuzz2:
+#endif /* TEST_FUZZ */
ok = 1;
}
@@ -1857,14 +2008,25 @@ static int wpa_supplicant_decrypt_key_data(struct wpa_sm *sm,
"WPA: No memory for AES-UNWRAP buffer");
return -1;
}
+#ifdef TEST_FUZZ
+ os_memset(buf, 0x11, *key_data_len);
+#endif /* TEST_FUZZ */
if (aes_unwrap(sm->ptk.kek, sm->ptk.kek_len, *key_data_len / 8,
key_data, buf)) {
+#ifdef TEST_FUZZ
+ wpa_printf(MSG_INFO,
+ "TEST: Ignore AES unwrap failure for fuzz testing");
+ goto continue_fuzz;
+#endif /* TEST_FUZZ */
bin_clear_free(buf, *key_data_len);
wpa_msg(sm->ctx->msg_ctx, MSG_WARNING,
"WPA: AES unwrap failed - "
"could not decrypt EAPOL-Key key data");
return -1;
}
+#ifdef TEST_FUZZ
+ continue_fuzz:
+#endif /* TEST_FUZZ */
os_memcpy(key_data, buf, *key_data_len);
bin_clear_free(buf, *key_data_len);
WPA_PUT_BE16(((u8 *) (key + 1)) + mic_len, *key_data_len);
@@ -2513,6 +2675,9 @@ void wpa_sm_deinit(struct wpa_sm *sm)
#ifdef CONFIG_OWE
crypto_ecdh_deinit(sm->owe_ecdh);
#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP2
+ wpabuf_clear_free(sm->dpp_z);
+#endif /* CONFIG_DPP2 */
os_free(sm);
}
@@ -2554,6 +2719,9 @@ void wpa_sm_notify_assoc(struct wpa_sm *sm, const u8 *bssid)
wpa_ft_prepare_auth_request(sm, NULL);
clear_keys = 0;
+ sm->ft_protocol = 1;
+ } else {
+ sm->ft_protocol = 0;
}
#endif /* CONFIG_IEEE80211R */
#ifdef CONFIG_FILS
@@ -2618,6 +2786,7 @@ void wpa_sm_notify_disassoc(struct wpa_sm *sm)
#endif /* CONFIG_FILS */
#ifdef CONFIG_IEEE80211R
sm->ft_reassoc_completed = 0;
+ sm->ft_protocol = 0;
#endif /* CONFIG_IEEE80211R */
/* Keys are not needed in the WPA state machine anymore */
@@ -2864,6 +3033,9 @@ int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param,
case WPA_PARAM_MFP:
sm->mfp = value;
break;
+ case WPA_PARAM_OCV:
+ sm->ocv = value;
+ break;
default:
break;
}
@@ -2938,6 +3110,19 @@ int wpa_sm_pmf_enabled(struct wpa_sm *sm)
}
+int wpa_sm_ocv_enabled(struct wpa_sm *sm)
+{
+ struct wpa_ie_data rsn;
+
+ if (!sm->ocv || !sm->ap_rsn_ie)
+ return 0;
+
+ return wpa_parse_wpa_ie_rsn(sm->ap_rsn_ie, sm->ap_rsn_ie_len,
+ &rsn) >= 0 &&
+ (rsn.capabilities & WPA_CAPABILITY_OCVC);
+}
+
+
/**
* wpa_sm_set_assoc_wpa_ie_default - Generate own WPA/RSN IE from configuration
* @sm: Pointer to WPA state machine data from wpa_sm_init()
@@ -3817,6 +4002,8 @@ static int fils_ft_build_assoc_req_rsne(struct wpa_sm *sm, struct wpabuf *buf)
if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC)
capab |= WPA_CAPABILITY_MFPC;
#endif /* CONFIG_IEEE80211W */
+ if (sm->ocv)
+ capab |= WPA_CAPABILITY_OCVC;
wpabuf_put_le16(buf, capab);
/* PMKID Count */
@@ -3846,11 +4033,13 @@ static int fils_ft_build_assoc_req_rsne(struct wpa_sm *sm, struct wpabuf *buf)
MAC2STR(sm->r1kh_id));
pos = wpabuf_put(buf, WPA_PMK_NAME_LEN);
if (wpa_derive_pmk_r1_name(sm->pmk_r0_name, sm->r1kh_id, sm->own_addr,
- pos, use_sha384) < 0) {
+ sm->pmk_r1_name, use_sha384) < 0) {
wpa_printf(MSG_WARNING, "FILS+FT: Could not derive PMKR1Name");
return -1;
}
- wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR1Name", pos, WPA_PMK_NAME_LEN);
+ wpa_hexdump(MSG_DEBUG, "FILS+FT: PMKR1Name", sm->pmk_r1_name,
+ WPA_PMK_NAME_LEN);
+ os_memcpy(pos, sm->pmk_r1_name, WPA_PMK_NAME_LEN);
#ifdef CONFIG_IEEE80211W
if (sm->mgmt_group_cipher == WPA_CIPHER_AES_128_CMAC) {
@@ -3951,6 +4140,26 @@ struct wpabuf * fils_build_assoc_req(struct wpa_sm *sm, const u8 **kek,
/* TODO: FILS IP Address Assignment */
+#ifdef CONFIG_OCV
+ if (wpa_sm_ocv_enabled(sm)) {
+ struct wpa_channel_info ci;
+ u8 *pos;
+
+ if (wpa_sm_channel_info(sm, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "FILS: Failed to get channel info for OCI element");
+ wpabuf_free(buf);
+ return NULL;
+ }
+
+ pos = wpabuf_put(buf, OCV_OCI_EXTENDED_LEN);
+ if (ocv_insert_extended_oci(&ci, pos) < 0) {
+ wpabuf_free(buf);
+ return NULL;
+ }
+ }
+#endif /* CONFIG_OCV */
+
wpa_hexdump_buf(MSG_DEBUG, "FILS: Association Request plaintext", buf);
*kek = sm->ptk.kek;
@@ -4114,6 +4323,43 @@ int fils_process_assoc_resp(struct wpa_sm *sm, const u8 *resp, size_t len)
goto fail;
}
+#ifdef CONFIG_OCV
+ if (wpa_sm_ocv_enabled(sm)) {
+ struct wpa_channel_info ci;
+
+ if (wpa_sm_channel_info(sm, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "Failed to get channel info to validate received OCI in FILS (Re)Association Response frame");
+ goto fail;
+ }
+
+ if (ocv_verify_tx_params(elems.oci, elems.oci_len, &ci,
+ channel_width_to_int(ci.chanwidth),
+ ci.seg1_idx) != 0) {
+ wpa_printf(MSG_WARNING, "FILS: %s", ocv_errorstr);
+ goto fail;
+ }
+ }
+#endif /* CONFIG_OCV */
+
+#ifdef CONFIG_IEEE80211R
+ if (wpa_key_mgmt_ft(sm->key_mgmt) && sm->fils_ft_ies) {
+ struct wpa_ie_data rsn;
+
+ /* Check that PMKR1Name derived by the AP matches */
+ if (!elems.rsn_ie ||
+ wpa_parse_wpa_ie_rsn(elems.rsn_ie - 2, elems.rsn_ie_len + 2,
+ &rsn) < 0 ||
+ !rsn.pmkid || rsn.num_pmkid != 1 ||
+ os_memcmp(rsn.pmkid, sm->pmk_r1_name,
+ WPA_PMK_NAME_LEN) != 0) {
+ wpa_printf(MSG_DEBUG,
+ "FILS+FT: No RSNE[PMKR1Name] match in AssocResp");
+ goto fail;
+ }
+ }
+#endif /* CONFIG_IEEE80211R */
+
/* Key Delivery */
if (!elems.key_delivery) {
wpa_printf(MSG_DEBUG, "FILS: No Key Delivery element");
@@ -4435,3 +4681,14 @@ void wpa_sm_set_fils_cache_id(struct wpa_sm *sm, const u8 *fils_cache_id)
}
#endif /* CONFIG_FILS */
}
+
+
+#ifdef CONFIG_DPP2
+void wpa_sm_set_dpp_z(struct wpa_sm *sm, const struct wpabuf *z)
+{
+ if (sm) {
+ wpabuf_clear_free(sm->dpp_z);
+ sm->dpp_z = z ? wpabuf_dup(z) : NULL;
+ }
+}
+#endif /* CONFIG_DPP2 */
diff --git a/src/rsn_supp/wpa.h b/src/rsn_supp/wpa.h
index 21f4b17815e93..8903f8e14c691 100644
--- a/src/rsn_supp/wpa.h
+++ b/src/rsn_supp/wpa.h
@@ -18,6 +18,7 @@ struct wpa_sm;
struct eapol_sm;
struct wpa_config_blob;
struct hostapd_freq_params;
+struct wpa_channel_info;
struct wpa_sm_ctx {
void *ctx; /* pointer to arbitrary upper level context */
@@ -82,6 +83,7 @@ struct wpa_sm_ctx {
int (*key_mgmt_set_pmk)(void *ctx, const u8 *pmk, size_t pmk_len);
void (*fils_hlp_rx)(void *ctx, const u8 *dst, const u8 *src,
const u8 *pkt, size_t pkt_len);
+ int (*channel_info)(void *ctx, struct wpa_channel_info *ci);
};
@@ -95,7 +97,8 @@ enum wpa_sm_conf_params {
WPA_PARAM_KEY_MGMT,
WPA_PARAM_MGMT_GROUP,
WPA_PARAM_RSN_ENABLED,
- WPA_PARAM_MFP
+ WPA_PARAM_MFP,
+ WPA_PARAM_OCV
};
struct rsn_supp_config {
@@ -141,6 +144,7 @@ int wpa_sm_set_param(struct wpa_sm *sm, enum wpa_sm_conf_params param,
int wpa_sm_get_status(struct wpa_sm *sm, char *buf, size_t buflen,
int verbose);
int wpa_sm_pmf_enabled(struct wpa_sm *sm);
+int wpa_sm_ocv_enabled(struct wpa_sm *sm);
void wpa_sm_key_request(struct wpa_sm *sm, int error, int pairwise);
@@ -279,6 +283,11 @@ static inline int wpa_sm_pmf_enabled(struct wpa_sm *sm)
return 0;
}
+static inline int wpa_sm_ocv_enabled(struct wpa_sm *sm)
+{
+ return 0;
+}
+
static inline void wpa_sm_key_request(struct wpa_sm *sm, int error,
int pairwise)
{
@@ -456,5 +465,6 @@ int owe_process_assoc_resp(struct wpa_sm *sm, const u8 *bssid,
void wpa_sm_set_reset_fils_completed(struct wpa_sm *sm, int set);
void wpa_sm_set_fils_cache_id(struct wpa_sm *sm, const u8 *fils_cache_id);
+void wpa_sm_set_dpp_z(struct wpa_sm *sm, const struct wpabuf *z);
#endif /* WPA_H */
diff --git a/src/rsn_supp/wpa_ft.c b/src/rsn_supp/wpa_ft.c
index b8d60e3208d09..7dcb1043bfcc4 100644
--- a/src/rsn_supp/wpa_ft.c
+++ b/src/rsn_supp/wpa_ft.c
@@ -14,6 +14,8 @@
#include "crypto/random.h"
#include "common/ieee802_11_defs.h"
#include "common/ieee802_11_common.h"
+#include "common/ocv.h"
+#include "drivers/driver.h"
#include "wpa.h"
#include "wpa_i.h"
@@ -242,6 +244,8 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len,
sm->mgmt_group_cipher == WPA_CIPHER_BIP_CMAC_256)
capab |= WPA_CAPABILITY_MFPC;
#endif /* CONFIG_IEEE80211W */
+ if (sm->ocv)
+ capab |= WPA_CAPABILITY_OCVC;
WPA_PUT_LE16(pos, capab);
pos += 2;
@@ -323,6 +327,26 @@ static u8 * wpa_ft_gen_req_ies(struct wpa_sm *sm, size_t *len,
*pos++ = sm->r0kh_id_len;
os_memcpy(pos, sm->r0kh_id, sm->r0kh_id_len);
pos += sm->r0kh_id_len;
+#ifdef CONFIG_OCV
+ if (kck && wpa_sm_ocv_enabled(sm)) {
+ /* OCI sub-element in the third FT message */
+ struct wpa_channel_info ci;
+
+ if (wpa_sm_channel_info(sm, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "Failed to get channel info for OCI element in FTE");
+ os_free(buf);
+ return NULL;
+ }
+
+ *pos++ = FTIE_SUBELEM_OCI;
+ *pos++ = OCV_OCI_LEN;
+ if (ocv_insert_oci(&ci, &pos) < 0) {
+ os_free(buf);
+ return NULL;
+ }
+ }
+#endif /* CONFIG_OCV */
*ftie_len = pos - ftie_len - 1;
if (ric_ies) {
@@ -961,6 +985,25 @@ int wpa_ft_validate_reassoc_resp(struct wpa_sm *sm, const u8 *ies,
return -1;
}
+#ifdef CONFIG_OCV
+ if (wpa_sm_ocv_enabled(sm)) {
+ struct wpa_channel_info ci;
+
+ if (wpa_sm_channel_info(sm, &ci) != 0) {
+ wpa_printf(MSG_WARNING,
+ "Failed to get channel info to validate received OCI in (Re)Assoc Response");
+ return -1;
+ }
+
+ if (ocv_verify_tx_params(parse.oci, parse.oci_len, &ci,
+ channel_width_to_int(ci.chanwidth),
+ ci.seg1_idx) != 0) {
+ wpa_printf(MSG_WARNING, "%s", ocv_errorstr);
+ return -1;
+ }
+ }
+#endif /* CONFIG_OCV */
+
sm->ft_reassoc_completed = 1;
if (wpa_ft_process_gtk_subelem(sm, parse.gtk, parse.gtk_len) < 0)
diff --git a/src/rsn_supp/wpa_i.h b/src/rsn_supp/wpa_i.h
index b94b17a85a3a6..0c5955c66f882 100644
--- a/src/rsn_supp/wpa_i.h
+++ b/src/rsn_supp/wpa_i.h
@@ -86,6 +86,7 @@ struct wpa_sm {
int rsn_enabled; /* Whether RSN is enabled in configuration */
int mfp; /* 0 = disabled, 1 = optional, 2 = mandatory */
+ int ocv; /* Operating Channel Validation */
u8 *assoc_wpa_ie; /* Own WPA/RSN IE from (Re)AssocReq */
size_t assoc_wpa_ie_len;
@@ -125,8 +126,9 @@ struct wpa_sm {
u8 r0kh_id[FT_R0KH_ID_MAX_LEN];
size_t r0kh_id_len;
u8 r1kh_id[FT_R1KH_ID_LEN];
- int ft_completed;
- int ft_reassoc_completed;
+ unsigned int ft_completed:1;
+ unsigned int ft_reassoc_completed:1;
+ unsigned int ft_protocol:1;
int over_the_ds_in_progress;
u8 target_ap[ETH_ALEN]; /* over-the-DS target AP */
int set_ptk_after_assoc;
@@ -167,6 +169,10 @@ struct wpa_sm {
struct crypto_ecdh *owe_ecdh;
u16 owe_group;
#endif /* CONFIG_OWE */
+
+#ifdef CONFIG_DPP2
+ struct wpabuf *dpp_z;
+#endif /* CONFIG_DPP2 */
};
@@ -395,6 +401,14 @@ static inline void wpa_sm_fils_hlp_rx(struct wpa_sm *sm,
sm->ctx->fils_hlp_rx(sm->ctx->ctx, dst, src, pkt, pkt_len);
}
+static inline int wpa_sm_channel_info(struct wpa_sm *sm,
+ struct wpa_channel_info *ci)
+{
+ if (!sm->ctx->channel_info)
+ return -1;
+ return sm->ctx->channel_info(sm->ctx->ctx, ci);
+}
+
int wpa_eapol_key_send(struct wpa_sm *sm, struct wpa_ptk *ptk,
int ver, const u8 *dest, u16 proto,
diff --git a/src/rsn_supp/wpa_ie.c b/src/rsn_supp/wpa_ie.c
index a3410d15447a4..ae9f4ca241d82 100644
--- a/src/rsn_supp/wpa_ie.c
+++ b/src/rsn_supp/wpa_ie.c
@@ -223,6 +223,8 @@ static int wpa_gen_wpa_ie_rsn(u8 *rsn_ie, size_t rsn_ie_len,
if (sm->mfp == 2)
capab |= WPA_CAPABILITY_MFPR;
#endif /* CONFIG_IEEE80211W */
+ if (sm->ocv)
+ capab |= WPA_CAPABILITY_OCVC;
WPA_PUT_LE16(pos, capab);
pos += 2;
@@ -463,6 +465,17 @@ static int wpa_parse_generic(const u8 *pos, const u8 *end,
}
#endif /* CONFIG_P2P */
+#ifdef CONFIG_OCV
+ if (pos[1] >= RSN_SELECTOR_LEN + 1 &&
+ RSN_SELECTOR_GET(pos + 2) == RSN_KEY_DATA_OCI) {
+ ie->oci = pos + 2 + RSN_SELECTOR_LEN;
+ ie->oci_len = pos[1] - RSN_SELECTOR_LEN;
+ wpa_hexdump(MSG_DEBUG, "WPA: OCI KDE in EAPOL-Key",
+ pos, pos[1] + 2);
+ return 0;
+ }
+#endif /* CONFIG_OCV */
+
return 0;
}
diff --git a/src/rsn_supp/wpa_ie.h b/src/rsn_supp/wpa_ie.h
index 0e72af56029eb..9d53973a94314 100644
--- a/src/rsn_supp/wpa_ie.h
+++ b/src/rsn_supp/wpa_ie.h
@@ -53,6 +53,10 @@ struct wpa_eapol_ie_parse {
const u8 *ip_addr_req;
const u8 *ip_addr_alloc;
#endif /* CONFIG_P2P */
+#ifdef CONFIG_OCV
+ const u8 *oci;
+ size_t oci_len;
+#endif /* CONFIG_OCV */
};
int wpa_supplicant_parse_ies(const u8 *buf, size_t len,
diff --git a/src/tls/asn1.c b/src/tls/asn1.c
index cec109292d5ab..822f87c182124 100644
--- a/src/tls/asn1.c
+++ b/src/tls/asn1.c
@@ -31,6 +31,10 @@ int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr)
pos = buf;
end = buf + len;
+ if (pos >= end) {
+ wpa_printf(MSG_DEBUG, "ASN.1: No room for Identifier");
+ return -1;
+ }
hdr->identifier = *pos++;
hdr->class = hdr->identifier >> 6;
hdr->constructed = !!(hdr->identifier & (1 << 5));
@@ -51,6 +55,10 @@ int asn1_get_next(const u8 *buf, size_t len, struct asn1_hdr *hdr)
} else
hdr->tag = hdr->identifier & 0x1f;
+ if (pos >= end) {
+ wpa_printf(MSG_DEBUG, "ASN.1: No room for Length");
+ return -1;
+ }
tmp = *pos++;
if (tmp & 0x80) {
if (tmp == 0xff) {
diff --git a/src/tls/bignum.c b/src/tls/bignum.c
index f3baafe1061df..1a87c82d5a726 100644
--- a/src/tls/bignum.c
+++ b/src/tls/bignum.c
@@ -119,10 +119,10 @@ int bignum_cmp(const struct bignum *a, const struct bignum *b)
/**
- * bignum_cmd_d - Compare bignum to standard integer
+ * bignum_cmp_d - Compare bignum to standard integer
* @a: Bignum from bignum_init()
* @b: Small integer
- * Returns: 0 on success, -1 on failure
+ * Returns: -1 if a < b, 0 if a == b, 1 if a > b
*/
int bignum_cmp_d(const struct bignum *a, unsigned long b)
{
diff --git a/src/tls/tlsv1_client.c b/src/tls/tlsv1_client.c
index 76e19746b0209..a147a54a3d106 100644
--- a/src/tls/tlsv1_client.c
+++ b/src/tls/tlsv1_client.c
@@ -1,6 +1,6 @@
/*
* TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246)
- * Copyright (c) 2006-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -514,6 +514,8 @@ int tlsv1_client_established(struct tlsv1_client *conn)
* tlsv1_client_prf - Use TLS-PRF to derive keying material
* @conn: TLSv1 client connection data from tlsv1_client_init()
* @label: Label (e.g., description of the key) for PRF
+ * @context: Optional extra upper-layer context (max len 2^16)
+ * @context_len: The length of the context value
* @server_random_first: seed is 0 = client_random|server_random,
* 1 = server_random|client_random
* @out: Buffer for output data from TLS-PRF
@@ -521,13 +523,26 @@ int tlsv1_client_established(struct tlsv1_client *conn)
* Returns: 0 on success, -1 on failure
*/
int tlsv1_client_prf(struct tlsv1_client *conn, const char *label,
+ const u8 *context, size_t context_len,
int server_random_first, u8 *out, size_t out_len)
{
- u8 seed[2 * TLS_RANDOM_LEN];
+ u8 *seed, *pos;
+ size_t seed_len = 2 * TLS_RANDOM_LEN;
+ int res;
if (conn->state != ESTABLISHED)
return -1;
+ if (context_len > 65535)
+ return -1;
+
+ if (context)
+ seed_len += 2 + context_len;
+
+ seed = os_malloc(seed_len);
+ if (!seed)
+ return -1;
+
if (server_random_first) {
os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random,
@@ -538,9 +553,18 @@ int tlsv1_client_prf(struct tlsv1_client *conn, const char *label,
TLS_RANDOM_LEN);
}
- return tls_prf(conn->rl.tls_version,
- conn->master_secret, TLS_MASTER_SECRET_LEN,
- label, seed, 2 * TLS_RANDOM_LEN, out, out_len);
+ if (context) {
+ pos = seed + 2 * TLS_RANDOM_LEN;
+ WPA_PUT_BE16(pos, context_len);
+ pos += 2;
+ os_memcpy(pos, context, context_len);
+ }
+
+ res = tls_prf(conn->rl.tls_version,
+ conn->master_secret, TLS_MASTER_SECRET_LEN,
+ label, seed, seed_len, out, out_len);
+ os_free(seed);
+ return res;
}
diff --git a/src/tls/tlsv1_client.h b/src/tls/tlsv1_client.h
index 40fa6c7fbdeeb..7fcc256f14aaa 100644
--- a/src/tls/tlsv1_client.h
+++ b/src/tls/tlsv1_client.h
@@ -1,6 +1,6 @@
/*
* TLS v1.0/v1.1/v1.2 client (RFC 2246, RFC 4346, RFC 5246)
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -19,6 +19,7 @@ struct tlsv1_client * tlsv1_client_init(void);
void tlsv1_client_deinit(struct tlsv1_client *conn);
int tlsv1_client_established(struct tlsv1_client *conn);
int tlsv1_client_prf(struct tlsv1_client *conn, const char *label,
+ const u8 *context, size_t context_len,
int server_random_first, u8 *out, size_t out_len);
u8 * tlsv1_client_handshake(struct tlsv1_client *conn,
const u8 *in_data, size_t in_len,
diff --git a/src/tls/tlsv1_client_read.c b/src/tls/tlsv1_client_read.c
index e66f1a98896de..80874e59d1de5 100644
--- a/src/tls/tlsv1_client_read.c
+++ b/src/tls/tlsv1_client_read.c
@@ -290,7 +290,7 @@ static void tls_peer_cert_event(struct tlsv1_client *conn, int depth,
return;
os_memset(&ev, 0, sizeof(ev));
- if (conn->cred->cert_probe || conn->cert_in_cb) {
+ if ((conn->cred && conn->cred->cert_probe) || conn->cert_in_cb) {
cert_buf = wpabuf_alloc_copy(cert->cert_start,
cert->cert_len);
ev.peer_cert.cert = cert_buf;
diff --git a/src/tls/tlsv1_client_write.c b/src/tls/tlsv1_client_write.c
index 04d895e61926a..4a1147b69e761 100644
--- a/src/tls/tlsv1_client_write.c
+++ b/src/tls/tlsv1_client_write.c
@@ -72,6 +72,9 @@ u8 * tls_send_client_hello(struct tlsv1_client *conn, size_t *out_len)
*out_len = 0;
os_get_time(&now);
+#ifdef TEST_FUZZ
+ now.sec = 0xfffefdfc;
+#endif /* TEST_FUZZ */
WPA_PUT_BE32(conn->client_random, now.sec);
if (random_get_bytes(conn->client_random + 4, TLS_RANDOM_LEN - 4)) {
wpa_printf(MSG_ERROR, "TLSv1: Could not generate "
diff --git a/src/tls/tlsv1_server.c b/src/tls/tlsv1_server.c
index 5406969040959..12dcc859a2803 100644
--- a/src/tls/tlsv1_server.c
+++ b/src/tls/tlsv1_server.c
@@ -1,6 +1,6 @@
/*
* TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246)
- * Copyright (c) 2006-2014, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -164,7 +164,8 @@ u8 * tlsv1_server_handshake(struct tlsv1_server *conn,
/* need more data */
wpa_printf(MSG_DEBUG, "TLSv1: Partial processing not "
"yet supported");
- tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL, alert);
+ tlsv1_server_alert(conn, TLS_ALERT_LEVEL_FATAL,
+ TLS_ALERT_INTERNAL_ERROR);
goto failed;
}
ct = pos[0];
@@ -204,6 +205,7 @@ failed:
msg = tlsv1_server_send_alert(conn, conn->alert_level,
conn->alert_description,
out_len);
+ conn->write_alerts++;
}
return msg;
@@ -296,6 +298,7 @@ int tlsv1_server_decrypt(struct tlsv1_server *conn,
}
tlsv1_server_log(conn, "Received alert %d:%d",
out_pos[0], out_pos[1]);
+ conn->read_alerts++;
if (out_pos[0] == TLS_ALERT_LEVEL_WARNING) {
/* Continue processing */
pos += used;
@@ -459,6 +462,8 @@ int tlsv1_server_established(struct tlsv1_server *conn)
* tlsv1_server_prf - Use TLS-PRF to derive keying material
* @conn: TLSv1 server connection data from tlsv1_server_init()
* @label: Label (e.g., description of the key) for PRF
+ * @context: Optional extra upper-layer context (max len 2^16)
+ * @context_len: The length of the context value
* @server_random_first: seed is 0 = client_random|server_random,
* 1 = server_random|client_random
* @out: Buffer for output data from TLS-PRF
@@ -466,13 +471,26 @@ int tlsv1_server_established(struct tlsv1_server *conn)
* Returns: 0 on success, -1 on failure
*/
int tlsv1_server_prf(struct tlsv1_server *conn, const char *label,
+ const u8 *context, size_t context_len,
int server_random_first, u8 *out, size_t out_len)
{
- u8 seed[2 * TLS_RANDOM_LEN];
+ u8 *seed, *pos;
+ size_t seed_len = 2 * TLS_RANDOM_LEN;
+ int res;
if (conn->state != ESTABLISHED)
return -1;
+ if (context_len > 65535)
+ return -1;
+
+ if (context)
+ seed_len += 2 + context_len;
+
+ seed = os_malloc(seed_len);
+ if (!seed)
+ return -1;
+
if (server_random_first) {
os_memcpy(seed, conn->server_random, TLS_RANDOM_LEN);
os_memcpy(seed + TLS_RANDOM_LEN, conn->client_random,
@@ -483,9 +501,18 @@ int tlsv1_server_prf(struct tlsv1_server *conn, const char *label,
TLS_RANDOM_LEN);
}
- return tls_prf(conn->rl.tls_version,
- conn->master_secret, TLS_MASTER_SECRET_LEN,
- label, seed, 2 * TLS_RANDOM_LEN, out, out_len);
+ if (context) {
+ pos = seed + 2 * TLS_RANDOM_LEN;
+ WPA_PUT_BE16(pos, context_len);
+ pos += 2;
+ os_memcpy(pos, context, context_len);
+ }
+
+ res = tls_prf(conn->rl.tls_version,
+ conn->master_secret, TLS_MASTER_SECRET_LEN,
+ label, seed, seed_len, out, out_len);
+ os_free(seed);
+ return res;
}
@@ -708,6 +735,24 @@ void tlsv1_server_set_log_cb(struct tlsv1_server *conn,
}
+int tlsv1_server_get_failed(struct tlsv1_server *conn)
+{
+ return conn->state == FAILED;
+}
+
+
+int tlsv1_server_get_read_alerts(struct tlsv1_server *conn)
+{
+ return conn->read_alerts;
+}
+
+
+int tlsv1_server_get_write_alerts(struct tlsv1_server *conn)
+{
+ return conn->write_alerts;
+}
+
+
#ifdef CONFIG_TESTING_OPTIONS
void tlsv1_server_set_test_flags(struct tlsv1_server *conn, u32 flags)
{
diff --git a/src/tls/tlsv1_server.h b/src/tls/tlsv1_server.h
index 10e7699312b06..c9c0875ca3308 100644
--- a/src/tls/tlsv1_server.h
+++ b/src/tls/tlsv1_server.h
@@ -1,6 +1,6 @@
/*
* TLS v1.0/v1.1/v1.2 server (RFC 2246, RFC 4346, RFC 5246)
- * Copyright (c) 2006-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2006-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -19,6 +19,7 @@ struct tlsv1_server * tlsv1_server_init(struct tlsv1_credentials *cred);
void tlsv1_server_deinit(struct tlsv1_server *conn);
int tlsv1_server_established(struct tlsv1_server *conn);
int tlsv1_server_prf(struct tlsv1_server *conn, const char *label,
+ const u8 *context, size_t context_len,
int server_random_first, u8 *out, size_t out_len);
u8 * tlsv1_server_handshake(struct tlsv1_server *conn,
const u8 *in_data, size_t in_len, size_t *out_len);
@@ -48,6 +49,10 @@ void tlsv1_server_set_session_ticket_cb(struct tlsv1_server *conn,
void tlsv1_server_set_log_cb(struct tlsv1_server *conn,
void (*cb)(void *ctx, const char *msg), void *ctx);
+int tlsv1_server_get_failed(struct tlsv1_server *conn);
+int tlsv1_server_get_read_alerts(struct tlsv1_server *conn);
+int tlsv1_server_get_write_alerts(struct tlsv1_server *conn);
+
void tlsv1_server_set_test_flags(struct tlsv1_server *conn, u32 flags);
#endif /* TLSV1_SERVER_H */
diff --git a/src/tls/tlsv1_server_i.h b/src/tls/tlsv1_server_i.h
index 29c6678772150..2622585d84d0d 100644
--- a/src/tls/tlsv1_server_i.h
+++ b/src/tls/tlsv1_server_i.h
@@ -30,6 +30,8 @@ struct tlsv1_server {
u8 alert_level;
u8 alert_description;
+ int read_alerts, write_alerts;
+
struct crypto_public_key *client_rsa_key;
struct tls_verify_hash verify;
diff --git a/src/tls/tlsv1_server_read.c b/src/tls/tlsv1_server_read.c
index 4aa8a019f3e6e..e957678fc0d99 100644
--- a/src/tls/tlsv1_server_read.c
+++ b/src/tls/tlsv1_server_read.c
@@ -139,8 +139,11 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct,
pos = in_data;
left = *in_len;
- if (left < 4)
+ if (left < 4) {
+ tlsv1_server_log(conn,
+ "Truncated handshake message (expected ClientHello)");
goto decode_error;
+ }
/* HandshakeType msg_type */
if (*pos != TLS_HANDSHAKE_TYPE_CLIENT_HELLO) {
@@ -157,8 +160,12 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct,
pos += 3;
left -= 4;
- if (len > left)
+ if (len > left) {
+ tlsv1_server_log(conn,
+ "Truncated ClientHello (len=%d left=%d)",
+ (int) len, (int) left);
goto decode_error;
+ }
/* body - ClientHello */
@@ -166,8 +173,10 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct,
end = pos + len;
/* ProtocolVersion client_version */
- if (end - pos < 2)
+ if (end - pos < 2) {
+ tlsv1_server_log(conn, "Truncated ClientHello/client_version");
goto decode_error;
+ }
conn->client_version = WPA_GET_BE16(pos);
tlsv1_server_log(conn, "Client version %d.%d",
conn->client_version >> 8,
@@ -196,8 +205,10 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct,
tls_version_str(conn->rl.tls_version));
/* Random random */
- if (end - pos < TLS_RANDOM_LEN)
+ if (end - pos < TLS_RANDOM_LEN) {
+ tlsv1_server_log(conn, "Truncated ClientHello/client_random");
goto decode_error;
+ }
os_memcpy(conn->client_random, pos, TLS_RANDOM_LEN);
pos += TLS_RANDOM_LEN;
@@ -205,25 +216,36 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct,
conn->client_random, TLS_RANDOM_LEN);
/* SessionID session_id */
- if (end - pos < 1)
+ if (end - pos < 1) {
+ tlsv1_server_log(conn, "Truncated ClientHello/session_id len");
goto decode_error;
- if (end - pos < 1 + *pos || *pos > TLS_SESSION_ID_MAX_LEN)
+ }
+ if (end - pos < 1 + *pos || *pos > TLS_SESSION_ID_MAX_LEN) {
+ tlsv1_server_log(conn, "Truncated ClientHello/session_id");
goto decode_error;
+ }
wpa_hexdump(MSG_MSGDUMP, "TLSv1: client session_id", pos + 1, *pos);
pos += 1 + *pos;
/* TODO: add support for session resumption */
/* CipherSuite cipher_suites<2..2^16-1> */
- if (end - pos < 2)
+ if (end - pos < 2) {
+ tlsv1_server_log(conn,
+ "Truncated ClientHello/cipher_suites len");
goto decode_error;
+ }
num_suites = WPA_GET_BE16(pos);
pos += 2;
- if (end - pos < num_suites)
+ if (end - pos < num_suites) {
+ tlsv1_server_log(conn, "Truncated ClientHello/cipher_suites");
goto decode_error;
+ }
wpa_hexdump(MSG_MSGDUMP, "TLSv1: client cipher suites",
pos, num_suites);
- if (num_suites & 1)
+ if (num_suites & 1) {
+ tlsv1_server_log(conn, "Odd len ClientHello/cipher_suites");
goto decode_error;
+ }
num_suites /= 2;
cipher_suite = 0;
@@ -259,11 +281,17 @@ static int tls_process_client_hello(struct tlsv1_server *conn, u8 ct,
conn->cipher_suite = cipher_suite;
/* CompressionMethod compression_methods<1..2^8-1> */
- if (end - pos < 1)
+ if (end - pos < 1) {
+ tlsv1_server_log(conn,
+ "Truncated ClientHello/compression_methods len");
goto decode_error;
+ }
num_suites = *pos++;
- if (end - pos < num_suites)
+ if (end - pos < num_suites) {
+ tlsv1_server_log(conn,
+ "Truncated ClientHello/compression_methods");
goto decode_error;
+ }
wpa_hexdump(MSG_MSGDUMP, "TLSv1: client compression_methods",
pos, num_suites);
compr_null_found = 0;
@@ -1217,6 +1245,7 @@ static int tls_process_client_finished(struct tlsv1_server *conn, u8 ct,
if (os_memcmp_const(pos, verify_data, TLS_VERIFY_DATA_LEN) != 0) {
tlsv1_server_log(conn, "Mismatch in verify_data");
+ conn->state = FAILED;
return -1;
}
diff --git a/src/tls/tlsv1_server_write.c b/src/tls/tlsv1_server_write.c
index bdc6c11992384..8d36cf1353910 100644
--- a/src/tls/tlsv1_server_write.c
+++ b/src/tls/tlsv1_server_write.c
@@ -26,7 +26,7 @@ static size_t tls_server_cert_chain_der_len(struct tlsv1_server *conn)
size_t len = 0;
struct x509_certificate *cert;
- cert = conn->cred->cert;
+ cert = conn->cred ? conn->cred->cert : NULL;
while (cert) {
len += 3 + cert->cert_len;
if (x509_certificate_self_signed(cert))
@@ -53,6 +53,9 @@ static int tls_write_server_hello(struct tlsv1_server *conn,
pos += TLS_RECORD_HEADER_LEN;
os_get_time(&now);
+#ifdef TEST_FUZZ
+ now.sec = 0xfffefdfc;
+#endif /* TEST_FUZZ */
WPA_PUT_BE32(conn->server_random, now.sec);
if (random_get_bytes(conn->server_random + 4, TLS_RANDOM_LEN - 4)) {
wpa_printf(MSG_ERROR, "TLSv1: Could not generate "
diff --git a/src/tls/x509v3.c b/src/tls/x509v3.c
index f80c9a358bf52..fa4d44229622d 100644
--- a/src/tls/x509v3.c
+++ b/src/tls/x509v3.c
@@ -532,6 +532,8 @@ void x509_name_string(struct x509_name *name, char *buf, size_t len)
}
done:
+ if (pos < end)
+ *pos = '\0';
end[-1] = '\0';
}
diff --git a/src/utils/Makefile b/src/utils/Makefile
index 52efc5321fca1..1ee2bee67f6b3 100644
--- a/src/utils/Makefile
+++ b/src/utils/Makefile
@@ -19,6 +19,7 @@ LIB_OBJS= \
common.o \
crc32.o \
ip_addr.o \
+ json.o \
radiotap.o \
trace.o \
uuid.o \
diff --git a/src/utils/base64.c b/src/utils/base64.c
index 8eb4ba127d480..53a92f49ed83f 100644
--- a/src/utils/base64.c
+++ b/src/utils/base64.c
@@ -1,12 +1,13 @@
/*
* Base64 encoding/decoding (RFC1341)
- * Copyright (c) 2005-2011, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
*/
#include "includes.h"
+#include <stdint.h>
#include "os.h"
#include "base64.h"
@@ -27,6 +28,8 @@ static unsigned char * base64_gen_encode(const unsigned char *src, size_t len,
size_t olen;
int line_len;
+ if (len >= SIZE_MAX / 4)
+ return NULL;
olen = len * 4 / 3 + 4; /* 3-byte blocks to 4-byte */
if (add_pad)
olen += olen / 72; /* line feeds */
diff --git a/src/utils/browser.c b/src/utils/browser.c
index 9cf6152d6cbd8..ad0b382fbc119 100644
--- a/src/utils/browser.c
+++ b/src/utils/browser.c
@@ -166,8 +166,7 @@ int hs20_web_browser(const char *url)
g_object_set(G_OBJECT(s), "ssl-strict", FALSE, NULL);
ctx.win = gtk_window_new(GTK_WINDOW_TOPLEVEL);
- gtk_window_set_wmclass(GTK_WINDOW(ctx.win), "Hotspot 2.0 client",
- "Hotspot 2.0 client");
+ gtk_window_set_role(GTK_WINDOW(ctx.win), "Hotspot 2.0 client");
gtk_window_set_default_size(GTK_WINDOW(ctx.win), 800, 600);
scroll = gtk_scrolled_window_new(NULL, NULL);
diff --git a/src/utils/common.c b/src/utils/common.c
index 1eb33705bef34..b9c8bfdb98e95 100644
--- a/src/utils/common.c
+++ b/src/utils/common.c
@@ -1,6 +1,6 @@
/*
* wpa_supplicant/hostapd / common helper functions, etc.
- * Copyright (c) 2002-2007, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2002-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -1073,7 +1073,8 @@ size_t utf8_unescape(const char *inp, size_t in_size,
in_size--;
}
- while (in_size--) {
+ while (in_size) {
+ in_size--;
if (res_size >= out_size)
return 0;
@@ -1084,8 +1085,9 @@ size_t utf8_unescape(const char *inp, size_t in_size,
return res_size;
case '\\':
- if (!in_size--)
+ if (!in_size)
return 0;
+ in_size--;
inp++;
/* fall through */
@@ -1116,7 +1118,8 @@ size_t utf8_escape(const char *inp, size_t in_size,
if (!in_size)
in_size = os_strlen(inp);
- while (in_size--) {
+ while (in_size) {
+ in_size--;
if (res_size++ >= out_size)
return 0;
@@ -1221,3 +1224,28 @@ u8 rssi_to_rcpi(int rssi)
return 220;
return (rssi + 110) * 2;
}
+
+
+char * get_param(const char *cmd, const char *param)
+{
+ const char *pos, *end;
+ char *val;
+ size_t len;
+
+ pos = os_strstr(cmd, param);
+ if (!pos)
+ return NULL;
+
+ pos += os_strlen(param);
+ end = os_strchr(pos, ' ');
+ if (end)
+ len = end - pos;
+ else
+ len = os_strlen(pos);
+ val = os_malloc(len + 1);
+ if (!val)
+ return NULL;
+ os_memcpy(val, pos, len);
+ val[len] = '\0';
+ return val;
+}
diff --git a/src/utils/common.h b/src/utils/common.h
index f824d001aeab8..792a30ab9bbeb 100644
--- a/src/utils/common.h
+++ b/src/utils/common.h
@@ -567,6 +567,7 @@ int is_ctrl_char(char c);
int str_starts(const char *str, const char *start);
u8 rssi_to_rcpi(int rssi);
+char * get_param(const char *cmd, const char *param);
/*
* gcc 4.4 ends up generating strict-aliasing warnings about some very common
diff --git a/src/utils/const_time.h b/src/utils/const_time.h
new file mode 100644
index 0000000000000..ab8f611ef6931
--- /dev/null
+++ b/src/utils/const_time.h
@@ -0,0 +1,191 @@
+/*
+ * Helper functions for constant time operations
+ * Copyright (c) 2019, The Linux Foundation
+ *
+ * This software may be distributed under the terms of the BSD license.
+ * See README for more details.
+ *
+ * These helper functions can be used to implement logic that needs to minimize
+ * externally visible differences in execution path by avoiding use of branches,
+ * avoiding early termination or other time differences, and forcing same memory
+ * access pattern regardless of values.
+ */
+
+#ifndef CONST_TIME_H
+#define CONST_TIME_H
+
+
+#if defined(__clang__)
+#define NO_UBSAN_UINT_OVERFLOW \
+ __attribute__((no_sanitize("unsigned-integer-overflow")))
+#else
+#define NO_UBSAN_UINT_OVERFLOW
+#endif
+
+
+/**
+ * const_time_fill_msb - Fill all bits with MSB value
+ * @val: Input value
+ * Returns: Value with all the bits set to the MSB of the input val
+ */
+static inline unsigned int const_time_fill_msb(unsigned int val)
+{
+ /* Move the MSB to LSB and multiple by -1 to fill in all bits. */
+ return (val >> (sizeof(val) * 8 - 1)) * ~0U;
+}
+
+
+/* Returns: -1 if val is zero; 0 if val is not zero */
+static inline unsigned int const_time_is_zero(unsigned int val)
+ NO_UBSAN_UINT_OVERFLOW
+{
+ /* Set MSB to 1 for 0 and fill rest of bits with the MSB value */
+ return const_time_fill_msb(~val & (val - 1));
+}
+
+
+/* Returns: -1 if a == b; 0 if a != b */
+static inline unsigned int const_time_eq(unsigned int a, unsigned int b)
+{
+ return const_time_is_zero(a ^ b);
+}
+
+
+/* Returns: -1 if a == b; 0 if a != b */
+static inline u8 const_time_eq_u8(unsigned int a, unsigned int b)
+{
+ return (u8) const_time_eq(a, b);
+}
+
+
+/**
+ * const_time_eq_bin - Constant time memory comparison
+ * @a: First buffer to compare
+ * @b: Second buffer to compare
+ * @len: Number of octets to compare
+ * Returns: -1 if buffers are equal, 0 if not
+ *
+ * This function is meant for comparing passwords or hash values where
+ * difference in execution time or memory access pattern could provide external
+ * observer information about the location of the difference in the memory
+ * buffers. The return value does not behave like memcmp(), i.e.,
+ * const_time_eq_bin() cannot be used to sort items into a defined order. Unlike
+ * memcmp(), the execution time of const_time_eq_bin() does not depend on the
+ * contents of the compared memory buffers, but only on the total compared
+ * length.
+ */
+static inline unsigned int const_time_eq_bin(const void *a, const void *b,
+ size_t len)
+{
+ const u8 *aa = a;
+ const u8 *bb = b;
+ size_t i;
+ u8 res = 0;
+
+ for (i = 0; i < len; i++)
+ res |= aa[i] ^ bb[i];
+
+ return const_time_is_zero(res);
+}
+
+
+/**
+ * const_time_select - Constant time unsigned int selection
+ * @mask: 0 (false) or -1 (true) to identify which value to select
+ * @true_val: Value to select for the true case
+ * @false_val: Value to select for the false case
+ * Returns: true_val if mask == -1, false_val if mask == 0
+ */
+static inline unsigned int const_time_select(unsigned int mask,
+ unsigned int true_val,
+ unsigned int false_val)
+{
+ return (mask & true_val) | (~mask & false_val);
+}
+
+
+/**
+ * const_time_select_int - Constant time int selection
+ * @mask: 0 (false) or -1 (true) to identify which value to select
+ * @true_val: Value to select for the true case
+ * @false_val: Value to select for the false case
+ * Returns: true_val if mask == -1, false_val if mask == 0
+ */
+static inline int const_time_select_int(unsigned int mask, int true_val,
+ int false_val)
+{
+ return (int) const_time_select(mask, (unsigned int) true_val,
+ (unsigned int) false_val);
+}
+
+
+/**
+ * const_time_select_u8 - Constant time u8 selection
+ * @mask: 0 (false) or -1 (true) to identify which value to select
+ * @true_val: Value to select for the true case
+ * @false_val: Value to select for the false case
+ * Returns: true_val if mask == -1, false_val if mask == 0
+ */
+static inline u8 const_time_select_u8(u8 mask, u8 true_val, u8 false_val)
+{
+ return (u8) const_time_select(mask, true_val, false_val);
+}
+
+
+/**
+ * const_time_select_s8 - Constant time s8 selection
+ * @mask: 0 (false) or -1 (true) to identify which value to select
+ * @true_val: Value to select for the true case
+ * @false_val: Value to select for the false case
+ * Returns: true_val if mask == -1, false_val if mask == 0
+ */
+static inline s8 const_time_select_s8(u8 mask, s8 true_val, s8 false_val)
+{
+ return (s8) const_time_select(mask, (unsigned int) true_val,
+ (unsigned int) false_val);
+}
+
+
+/**
+ * const_time_select_bin - Constant time binary buffer selection copy
+ * @mask: 0 (false) or -1 (true) to identify which value to copy
+ * @true_val: Buffer to copy for the true case
+ * @false_val: Buffer to copy for the false case
+ * @len: Number of octets to copy
+ * @dst: Destination buffer for the copy
+ *
+ * This function copies the specified buffer into the destination buffer using
+ * operations with identical memory access pattern regardless of which buffer
+ * is being copied.
+ */
+static inline void const_time_select_bin(u8 mask, const u8 *true_val,
+ const u8 *false_val, size_t len,
+ u8 *dst)
+{
+ size_t i;
+
+ for (i = 0; i < len; i++)
+ dst[i] = const_time_select_u8(mask, true_val[i], false_val[i]);
+}
+
+
+static inline int const_time_memcmp(const void *a, const void *b, size_t len)
+{
+ const u8 *aa = a;
+ const u8 *bb = b;
+ int diff, res = 0;
+ unsigned int mask;
+
+ if (len == 0)
+ return 0;
+ do {
+ len--;
+ diff = (int) aa[len] - (int) bb[len];
+ mask = const_time_is_zero((unsigned int) diff);
+ res = const_time_select_int(mask, res, diff);
+ } while (len);
+
+ return res;
+}
+
+#endif /* CONST_TIME_H */
diff --git a/src/utils/eloop.c b/src/utils/eloop.c
index 436bc8c993389..bb375be1095e6 100644
--- a/src/utils/eloop.c
+++ b/src/utils/eloop.c
@@ -224,22 +224,25 @@ static int eloop_sock_queue(int sock, eloop_event_type type)
#ifdef CONFIG_ELOOP_KQUEUE
-static int eloop_sock_queue(int sock, eloop_event_type type)
-{
- int filter;
- struct kevent ke;
+static short event_type_kevent_filter(eloop_event_type type)
+{
switch (type) {
case EVENT_TYPE_READ:
- filter = EVFILT_READ;
- break;
+ return EVFILT_READ;
case EVENT_TYPE_WRITE:
- filter = EVFILT_WRITE;
- break;
+ return EVFILT_WRITE;
default:
- filter = 0;
+ return 0;
}
- EV_SET(&ke, sock, filter, EV_ADD, 0, 0, 0);
+}
+
+
+static int eloop_sock_queue(int sock, eloop_event_type type)
+{
+ struct kevent ke;
+
+ EV_SET(&ke, sock, event_type_kevent_filter(type), EV_ADD, 0, 0, 0);
if (kevent(eloop.kqueuefd, &ke, 1, NULL, 0, NULL) == -1) {
wpa_printf(MSG_ERROR, "%s: kevent(ADD) for fd=%d failed: %s",
__func__, sock, strerror(errno));
@@ -247,6 +250,7 @@ static int eloop_sock_queue(int sock, eloop_event_type type)
}
return 0;
}
+
#endif /* CONFIG_ELOOP_KQUEUE */
@@ -301,7 +305,7 @@ static int eloop_sock_table_add_sock(struct eloop_sock_table *table,
#endif /* CONFIG_ELOOP_POLL */
#if defined(CONFIG_ELOOP_EPOLL) || defined(CONFIG_ELOOP_KQUEUE)
if (new_max_sock >= eloop.max_fd) {
- next = eloop.max_fd == 0 ? 16 : eloop.max_fd * 2;
+ next = new_max_sock + 16;
temp_table = os_realloc_array(eloop.fd_table, next,
sizeof(struct eloop_sock));
if (temp_table == NULL)
@@ -411,7 +415,8 @@ static void eloop_sock_table_remove_sock(struct eloop_sock_table *table,
os_memset(&eloop.fd_table[sock], 0, sizeof(struct eloop_sock));
#endif /* CONFIG_ELOOP_EPOLL */
#ifdef CONFIG_ELOOP_KQUEUE
- EV_SET(&ke, sock, 0, EV_DELETE, 0, 0, 0);
+ EV_SET(&ke, sock, event_type_kevent_filter(table->type), EV_DELETE, 0,
+ 0, 0);
if (kevent(eloop.kqueuefd, &ke, 1, NULL, 0, NULL) < 0) {
wpa_printf(MSG_ERROR, "%s: kevent(DEL) for fd=%d failed: %s",
__func__, sock, strerror(errno));
diff --git a/src/utils/http_curl.c b/src/utils/http_curl.c
index 58519ea8d2489..e62fbf96bcb32 100644
--- a/src/utils/http_curl.c
+++ b/src/utils/http_curl.c
@@ -31,6 +31,14 @@
#endif /* EAP_TLS_OPENSSL */
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+static const unsigned char * ASN1_STRING_get0_data(const ASN1_STRING *x)
+{
+ return ASN1_STRING_data((ASN1_STRING *) x);
+}
+#endif /* OpenSSL < 1.1.0 */
+
+
struct http_ctx {
void *ctx;
struct xml_node_ctx *xml;
@@ -446,6 +454,7 @@ sk_num(CHECKED_CAST(_STACK *, STACK_OF(ASN1_IA5STRING) *, (st)))
#define sk_ASN1_IA5STRING_value(st, i) (ASN1_IA5STRING *) \
sk_value(CHECKED_CAST(_STACK *, const STACK_OF(ASN1_IA5STRING) *, (st)), (i))
#else /* OPENSSL_IS_BORINGSSL */
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
#define sk_LogotypeInfo_num(st) SKM_sk_num(LogotypeInfo, (st))
#define sk_LogotypeInfo_value(st, i) SKM_sk_value(LogotypeInfo, (st), (i))
#define sk_LogotypeImage_num(st) SKM_sk_num(LogotypeImage, (st))
@@ -456,6 +465,13 @@ sk_value(CHECKED_CAST(_STACK *, const STACK_OF(ASN1_IA5STRING) *, (st)), (i))
#define sk_HashAlgAndValue_value(st, i) SKM_sk_value(HashAlgAndValue, (st), (i))
#define sk_ASN1_IA5STRING_num(st) SKM_sk_num(ASN1_IA5STRING, (st))
#define sk_ASN1_IA5STRING_value(st, i) SKM_sk_value(ASN1_IA5STRING, (st), (i))
+#else
+DEFINE_STACK_OF(LogotypeInfo)
+DEFINE_STACK_OF(LogotypeImage)
+DEFINE_STACK_OF(LogotypeAudio)
+DEFINE_STACK_OF(HashAlgAndValue)
+DEFINE_STACK_OF(ASN1_IA5STRING)
+#endif
#endif /* OPENSSL_IS_BORINGSSL */
@@ -486,7 +502,8 @@ static void add_logo(struct http_ctx *ctx, struct http_cert *hcert,
return;
n->hash_len = ASN1_STRING_length(hash->hashValue);
- n->hash = os_memdup(ASN1_STRING_data(hash->hashValue), n->hash_len);
+ n->hash = os_memdup(ASN1_STRING_get0_data(hash->hashValue),
+ n->hash_len);
if (n->hash == NULL) {
os_free(n->alg_oid);
return;
@@ -499,7 +516,7 @@ static void add_logo(struct http_ctx *ctx, struct http_cert *hcert,
os_free(n->hash);
return;
}
- os_memcpy(n->uri, ASN1_STRING_data(uri), len);
+ os_memcpy(n->uri, ASN1_STRING_get0_data(uri), len);
n->uri[len] = '\0';
hcert->num_logo++;
@@ -814,9 +831,9 @@ static void add_logotype_ext(struct http_ctx *ctx, struct http_cert *hcert,
}
wpa_hexdump(MSG_DEBUG, "logotypeExtn",
- ASN1_STRING_data(os), ASN1_STRING_length(os));
+ ASN1_STRING_get0_data(os), ASN1_STRING_length(os));
- data = ASN1_STRING_data(os);
+ data = ASN1_STRING_get0_data(os);
logo = d2i_LogotypeExtn(NULL, &data, ASN1_STRING_length(os));
if (logo == NULL) {
wpa_printf(MSG_INFO, "Failed to parse logotypeExtn");
@@ -1136,7 +1153,7 @@ static int ocsp_resp_cb(SSL *s, void *arg)
return 0;
}
- store = SSL_CTX_get_cert_store(s->ctx);
+ store = SSL_CTX_get_cert_store(SSL_get_SSL_CTX(s));
if (ctx->peer_issuer) {
wpa_printf(MSG_DEBUG, "OpenSSL: Add issuer");
debug_dump_cert("OpenSSL: Issuer certificate",
@@ -1272,12 +1289,13 @@ static int ocsp_resp_cb(SSL *s, void *arg)
}
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
static SSL_METHOD patch_ssl_method;
static const SSL_METHOD *real_ssl_method;
static int curl_patch_ssl_new(SSL *s)
{
- SSL_CTX *ssl = s->ctx;
+ SSL_CTX *ssl = SSL_get_SSL_CTX(s);
int ret;
ssl->method = real_ssl_method;
@@ -1288,6 +1306,7 @@ static int curl_patch_ssl_new(SSL *s)
return ret;
}
+#endif /* OpenSSL < 1.1.0 */
#endif /* HAVE_OCSP */
@@ -1306,6 +1325,7 @@ static CURLcode curl_cb_ssl(CURL *curl, void *sslctx, void *parm)
SSL_CTX_set_tlsext_status_cb(ssl, ocsp_resp_cb);
SSL_CTX_set_tlsext_status_arg(ssl, ctx);
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
/*
* Use a temporary SSL_METHOD to get a callback on SSL_new()
* from libcurl since there is no proper callback registration
@@ -1315,6 +1335,7 @@ static CURLcode curl_cb_ssl(CURL *curl, void *sslctx, void *parm)
patch_ssl_method.ssl_new = curl_patch_ssl_new;
real_ssl_method = ssl->method;
ssl->method = &patch_ssl_method;
+#endif /* OpenSSL < 1.1.0 */
}
#endif /* HAVE_OCSP */
@@ -1351,7 +1372,7 @@ static CURL * setup_curl_post(struct http_ctx *ctx, const char *address,
#ifdef EAP_TLS_OPENSSL
curl_easy_setopt(curl, CURLOPT_SSL_CTX_FUNCTION, curl_cb_ssl);
curl_easy_setopt(curl, CURLOPT_SSL_CTX_DATA, ctx);
-#ifdef OPENSSL_IS_BORINGSSL
+#if defined(OPENSSL_IS_BORINGSSL) || (OPENSSL_VERSION_NUMBER >= 0x10100000L)
/* For now, using the CURLOPT_SSL_VERIFYSTATUS option only
* with BoringSSL since the OpenSSL specific callback hack to
* enable OCSP is not available with BoringSSL. The OCSP
diff --git a/src/utils/json.c b/src/utils/json.c
index b9130d3a65ab0..b64433959ff7b 100644
--- a/src/utils/json.c
+++ b/src/utils/json.c
@@ -103,6 +103,11 @@ static char * json_parse_string(const char **json_pos, const char *end)
return str;
case '\\':
pos++;
+ if (pos >= end) {
+ wpa_printf(MSG_DEBUG,
+ "JSON: Truncated \\ escape");
+ goto fail;
+ }
switch (*pos) {
case '"':
case '\\':
@@ -165,6 +170,8 @@ static int json_parse_number(const char **json_pos, const char *end,
break;
}
}
+ if (pos == end)
+ pos--;
if (pos < *json_pos)
return -1;
len = pos - *json_pos + 1;
diff --git a/src/utils/list.h b/src/utils/list.h
index ee2f4856950f1..85aa5e39cfe16 100644
--- a/src/utils/list.h
+++ b/src/utils/list.h
@@ -1,6 +1,6 @@
/*
* Doubly-linked list
- * Copyright (c) 2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2009-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -76,8 +76,8 @@ static inline unsigned int dl_list_len(struct dl_list *list)
dl_list_entry((list)->prev, type, member))
#define dl_list_for_each(item, list, type, member) \
- for (item = dl_list_entry((list)->next, type, member); \
- &item->member != (list); \
+ for (item = dl_list_first((list), type, member); \
+ item && item != dl_list_entry((list), type, member); \
item = dl_list_entry(item->member.next, type, member))
#define dl_list_for_each_safe(item, n, list, type, member) \
diff --git a/src/utils/os_internal.c b/src/utils/os_internal.c
index ed6eb3c6b6770..474c8a372205f 100644
--- a/src/utils/os_internal.c
+++ b/src/utils/os_internal.c
@@ -430,22 +430,6 @@ int os_strncmp(const char *s1, const char *s2, size_t n)
}
-char * os_strncpy(char *dest, const char *src, size_t n)
-{
- char *d = dest;
-
- while (n--) {
- *d = *src;
- if (*src == '\0')
- break;
- d++;
- src++;
- }
-
- return dest;
-}
-
-
size_t os_strlcpy(char *dest, const char *src, size_t siz)
{
const char *s = src;
diff --git a/src/utils/os_none.c b/src/utils/os_none.c
index e74f206a2c5a2..5e0a3ada66788 100644
--- a/src/utils/os_none.c
+++ b/src/utils/os_none.c
@@ -218,12 +218,6 @@ int os_strncmp(const char *s1, const char *s2, size_t n)
}
-char * os_strncpy(char *dest, const char *src, size_t n)
-{
- return dest;
-}
-
-
size_t os_strlcpy(char *dest, const char *src, size_t size)
{
return 0;
diff --git a/src/utils/os_unix.c b/src/utils/os_unix.c
index 1894fcdb0cf25..800c50772d893 100644
--- a/src/utils/os_unix.c
+++ b/src/utils/os_unix.c
@@ -1,6 +1,6 @@
/*
* OS specific functions for UNIX/POSIX systems
- * Copyright (c) 2005-2009, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2005-2019, Jouni Malinen <j@w1.fi>
*
* This software may be distributed under the terms of the BSD license.
* See README for more details.
@@ -250,6 +250,13 @@ void os_daemonize_terminate(const char *pid_file)
int os_get_random(unsigned char *buf, size_t len)
{
+#ifdef TEST_FUZZ
+ size_t i;
+
+ for (i = 0; i < len; i++)
+ buf[i] = i & 0xff;
+ return 0;
+#else /* TEST_FUZZ */
FILE *f;
size_t rc;
@@ -266,6 +273,7 @@ int os_get_random(unsigned char *buf, size_t len)
fclose(f);
return rc != len ? -1 : 0;
+#endif /* TEST_FUZZ */
}
@@ -512,7 +520,7 @@ void * os_memdup(const void *src, size_t len)
{
void *r = os_malloc(len);
- if (r)
+ if (r && src)
os_memcpy(r, src, len);
return r;
}
diff --git a/src/utils/utils_module_tests.c b/src/utils/utils_module_tests.c
index 1b8ff82b41731..3af4fcde1daa8 100644
--- a/src/utils/utils_module_tests.c
+++ b/src/utils/utils_module_tests.c
@@ -9,6 +9,7 @@
#include "utils/includes.h"
#include "utils/common.h"
+#include "utils/const_time.h"
#include "common/ieee802_11_defs.h"
#include "utils/bitfield.h"
#include "utils/ext_password.h"
@@ -919,6 +920,294 @@ static int json_tests(void)
}
+static int const_time_tests(void)
+{
+ struct const_time_fill_msb_test {
+ unsigned int val;
+ unsigned int expected;
+ } const_time_fill_msb_tests[] = {
+ { 0, 0 },
+ { 1, 0 },
+ { 2, 0 },
+ { 1 << (sizeof(unsigned int) * 8 - 1), ~0 },
+ { ~0 - 1, ~0 },
+ { ~0, ~0 }
+ };
+ struct const_time_is_zero_test {
+ unsigned int val;
+ unsigned int expected;
+ } const_time_is_zero_tests[] = {
+ { 0, ~0 },
+ { 1, 0 },
+ { 2, 0 },
+ { 1 << (sizeof(unsigned int) * 8 - 1), 0 },
+ { ~0 - 1, 0 },
+ { ~0, 0 }
+ };
+ struct const_time_eq_test {
+ unsigned int a;
+ unsigned int b;
+ unsigned int expected;
+ unsigned int expected_u8;
+ } const_time_eq_tests[] = {
+ { 0, 1, 0, 0 },
+ { 1, 2, 0, 0 },
+ { 1, 1, ~0, 0xff },
+ { ~0, ~0, ~0, 0xff },
+ { ~0, ~0 - 1, 0, 0 },
+ { 0, 0, ~0, 0xff }
+ };
+ struct const_time_eq_bin_test {
+ u8 *a;
+ u8 *b;
+ size_t len;
+ unsigned int expected;
+ } const_time_eq_bin_tests[] = {
+ { (u8 *) "", (u8 *) "", 0, ~0 },
+ { (u8 *) "abcde", (u8 *) "abcde", 5, ~0 },
+ { (u8 *) "abcde", (u8 *) "Abcde", 5, 0 },
+ { (u8 *) "abcde", (u8 *) "aBcde", 5, 0 },
+ { (u8 *) "abcde", (u8 *) "abCde", 5, 0 },
+ { (u8 *) "abcde", (u8 *) "abcDe", 5, 0 },
+ { (u8 *) "abcde", (u8 *) "abcdE", 5, 0 },
+ { (u8 *) "\x00", (u8 *) "\x01", 1, 0 },
+ { (u8 *) "\x00", (u8 *) "\x80", 1, 0 },
+ { (u8 *) "\x00", (u8 *) "\x00", 1, ~0 }
+ };
+ struct const_time_select_test {
+ unsigned int mask;
+ unsigned int true_val;
+ unsigned int false_val;
+ unsigned int expected;
+ } const_time_select_tests[] = {
+ { ~0, ~0, ~0, ~0 },
+ { 0, ~0, ~0, ~0 },
+ { ~0, ~0, 0, ~0 },
+ { 0, ~0, 0, 0 },
+ { ~0, 0xaaaaaaaa, 0x55555555, 0xaaaaaaaa },
+ { 0, 0xaaaaaaaa, 0x55555555, 0x55555555 },
+ { ~0, 3, 3, 3 },
+ { 0, 3, 3, 3 },
+ { ~0, 1, 2, 1 },
+ { 0, 1, 2, 2 }
+ };
+ struct const_time_select_int_test {
+ unsigned int mask;
+ int true_val;
+ int false_val;
+ int expected;
+ } const_time_select_int_tests[] = {
+ { ~0, -128, 127, -128 },
+ { 0, -128, 127, 127 },
+ { ~0, -2147483648, 2147483647, -2147483648 },
+ { 0, -2147483648, 2147483647, 2147483647 },
+ { ~0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { ~0, -1, 1, -1 },
+ { 0, -1, 1, 1 }
+ };
+ struct const_time_select_u8_test {
+ u8 mask;
+ u8 true_val;
+ u8 false_val;
+ u8 expected;
+ } const_time_select_u8_tests[] = {
+ { ~0, ~0, ~0, ~0 },
+ { 0, ~0, ~0, ~0 },
+ { ~0, ~0, 0, ~0 },
+ { 0, ~0, 0, 0 },
+ { ~0, 0xaa, 0x55, 0xaa },
+ { 0, 0xaa, 0x55, 0x55 },
+ { ~0, 1, 2, 1 },
+ { 0, 1, 2, 2 }
+ };
+ struct const_time_select_s8_test {
+ u8 mask;
+ s8 true_val;
+ s8 false_val;
+ s8 expected;
+ } const_time_select_s8_tests[] = {
+ { ~0, -128, 127, -128 },
+ { 0, -128, 127, 127 },
+ { ~0, 0, 0, 0 },
+ { 0, 0, 0, 0 },
+ { ~0, -1, 1, -1 },
+ { 0, -1, 1, 1 }
+ };
+ struct const_time_select_bin_test {
+ u8 mask;
+ u8 *true_val;
+ u8 *false_val;
+ size_t len;
+ u8 *expected;
+ } const_time_select_bin_tests[] = {
+ { ~0, (u8 *) "abcde", (u8 *) "ABCDE", 5, (u8 *) "abcde" },
+ { 0, (u8 *) "abcde", (u8 *) "ABCDE", 5, (u8 *) "ABCDE" },
+ { ~0, (u8 *) "", (u8 *) "", 0, (u8 *) "" },
+ { 0, (u8 *) "", (u8 *) "", 0, (u8 *) "" }
+ };
+ struct const_time_memcmp_test {
+ char *a;
+ char *b;
+ size_t len;
+ int expected;
+ } const_time_memcmp_tests[] = {
+ { "abcde", "abcde", 5, 0 },
+ { "abcde", "bbcde", 5, -1 },
+ { "bbcde", "abcde", 5, 1 },
+ { "accde", "abcde", 5, 1 },
+ { "abcee", "abcde", 5, 1 },
+ { "abcdf", "abcde", 5, 1 },
+ { "cbcde", "aXXXX", 5, 2 },
+ { "a", "d", 1, -3 },
+ { "", "", 0, 0 }
+ };
+ unsigned int i;
+ int ret = 0;
+
+ wpa_printf(MSG_INFO, "constant time tests");
+
+ for (i = 0; i < ARRAY_SIZE(const_time_fill_msb_tests); i++) {
+ struct const_time_fill_msb_test *test;
+
+ test = &const_time_fill_msb_tests[i];
+ if (const_time_fill_msb(test->val) != test->expected) {
+ wpa_printf(MSG_ERROR,
+ "const_time_fill_msb(0x%x) test failed",
+ test->val);
+ ret = -1;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(const_time_is_zero_tests); i++) {
+ struct const_time_is_zero_test *test;
+
+ test = &const_time_is_zero_tests[i];
+ if (const_time_is_zero(test->val) != test->expected) {
+ wpa_printf(MSG_ERROR,
+ "const_time_is_zero(0x%x) test failed",
+ test->val);
+ ret = -1;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(const_time_eq_tests); i++) {
+ struct const_time_eq_test *test;
+
+ test = &const_time_eq_tests[i];
+ if (const_time_eq(test->a, test->b) != test->expected) {
+ wpa_printf(MSG_ERROR,
+ "const_time_eq(0x%x,0x%x) test failed",
+ test->a, test->b);
+ ret = -1;
+ }
+ if (const_time_eq_u8(test->a, test->b) != test->expected_u8) {
+ wpa_printf(MSG_ERROR,
+ "const_time_eq_u8(0x%x,0x%x) test failed",
+ test->a, test->b);
+ ret = -1;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(const_time_eq_bin_tests); i++) {
+ struct const_time_eq_bin_test *test;
+
+ test = &const_time_eq_bin_tests[i];
+ if (const_time_eq_bin(test->a, test->b, test->len) !=
+ test->expected) {
+ wpa_printf(MSG_ERROR,
+ "const_time_eq_bin(len=%u) test failed",
+ (unsigned int) test->len);
+ ret = -1;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(const_time_select_tests); i++) {
+ struct const_time_select_test *test;
+
+ test = &const_time_select_tests[i];
+ if (const_time_select(test->mask, test->true_val,
+ test->false_val) != test->expected) {
+ wpa_printf(MSG_ERROR,
+ "const_time_select(0x%x,0x%x,0x%x) test failed",
+ test->mask, test->true_val, test->false_val);
+ ret = -1;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(const_time_select_int_tests); i++) {
+ struct const_time_select_int_test *test;
+
+ test = &const_time_select_int_tests[i];
+ if (const_time_select_int(test->mask, test->true_val,
+ test->false_val) != test->expected) {
+ wpa_printf(MSG_ERROR,
+ "const_time_select_int(0x%x,%d,%d) test failed",
+ test->mask, test->true_val, test->false_val);
+ ret = -1;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(const_time_select_u8_tests); i++) {
+ struct const_time_select_u8_test *test;
+
+ test = &const_time_select_u8_tests[i];
+ if (const_time_select_u8(test->mask, test->true_val,
+ test->false_val) != test->expected) {
+ wpa_printf(MSG_ERROR,
+ "const_time_select_u8(0x%x,0x%x,0x%x) test failed",
+ test->mask, test->true_val, test->false_val);
+ ret = -1;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(const_time_select_s8_tests); i++) {
+ struct const_time_select_s8_test *test;
+
+ test = &const_time_select_s8_tests[i];
+ if (const_time_select_s8(test->mask, test->true_val,
+ test->false_val) != test->expected) {
+ wpa_printf(MSG_ERROR,
+ "const_time_select_s8(0x%x,0x%x,0x%x) test failed",
+ test->mask, test->true_val, test->false_val);
+ ret = -1;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(const_time_select_bin_tests); i++) {
+ struct const_time_select_bin_test *test;
+ u8 dst[100];
+
+ test = &const_time_select_bin_tests[i];
+ const_time_select_bin(test->mask, test->true_val,
+ test->false_val, test->len, dst);
+ if (os_memcmp(dst, test->expected, test->len) != 0) {
+ wpa_printf(MSG_ERROR,
+ "const_time_select_bin(0x%x,%u) test failed",
+ test->mask, (unsigned int) test->len);
+ ret = -1;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(const_time_memcmp_tests); i++) {
+ struct const_time_memcmp_test *test;
+ int res;
+
+ test = &const_time_memcmp_tests[i];
+ res = const_time_memcmp(test->a, test->b, test->len);
+ if (res != test->expected) {
+ wpa_printf(MSG_ERROR,
+ "const_time_memcmp(%s,%s,%d) test failed (%d != %d)",
+ test->a, test->b, (int) test->len,
+ res, test->expected);
+ ret = -1;
+ }
+ }
+
+ return ret;
+}
+
+
int utils_module_tests(void)
{
int ret = 0;
@@ -936,6 +1225,7 @@ int utils_module_tests(void)
ip_addr_tests() < 0 ||
eloop_tests() < 0 ||
json_tests() < 0 ||
+ const_time_tests() < 0 ||
int_array_tests() < 0)
ret = -1;
diff --git a/src/utils/wpa_debug.c b/src/utils/wpa_debug.c
index a56462b8bbdc7..c437000a7f500 100644
--- a/src/utils/wpa_debug.c
+++ b/src/utils/wpa_debug.c
@@ -422,6 +422,12 @@ static void _wpa_hexdump_ascii(int level, const char *title, const void *buf,
#ifdef CONFIG_ANDROID_LOG
_wpa_hexdump(level, title, buf, len, show);
#else /* CONFIG_ANDROID_LOG */
+#ifdef CONFIG_DEBUG_SYSLOG
+ if (wpa_debug_syslog) {
+ _wpa_hexdump(level, title, buf, len, show);
+ return;
+ }
+#endif /* CONFIG_DEBUG_SYSLOG */
wpa_debug_print_timestamp();
#ifdef CONFIG_DEBUG_FILE
if (out_file) {
diff --git a/src/wps/wps.c b/src/wps/wps.c
index 8d228270ff10c..484df262c3ca2 100644
--- a/src/wps/wps.c
+++ b/src/wps/wps.c
@@ -145,6 +145,8 @@ struct wps_data * wps_init(const struct wps_config *cfg)
data->peer_pubkey_hash_set = 1;
}
+ data->multi_ap_backhaul_sta = cfg->multi_ap_backhaul_sta;
+
return data;
}
@@ -430,7 +432,7 @@ struct wpabuf * wps_build_assoc_req_ie(enum wps_request_type req_type)
if (wps_build_version(ie) ||
wps_build_req_type(ie, req_type) ||
- wps_build_wfa_ext(ie, 0, NULL, 0)) {
+ wps_build_wfa_ext(ie, 0, NULL, 0, 0)) {
wpabuf_free(ie);
return NULL;
}
@@ -464,7 +466,7 @@ struct wpabuf * wps_build_assoc_resp_ie(void)
if (wps_build_version(ie) ||
wps_build_resp_type(ie, WPS_RESP_AP) ||
- wps_build_wfa_ext(ie, 0, NULL, 0)) {
+ wps_build_wfa_ext(ie, 0, NULL, 0, 0)) {
wpabuf_free(ie);
return NULL;
}
@@ -516,7 +518,7 @@ struct wpabuf * wps_build_probe_req_ie(u16 pw_id, struct wps_device_data *dev,
wps_build_model_name(dev, ie) ||
wps_build_model_number(dev, ie) ||
wps_build_dev_name(dev, ie) ||
- wps_build_wfa_ext(ie, req_type == WPS_REQ_ENROLLEE, NULL, 0) ||
+ wps_build_wfa_ext(ie, req_type == WPS_REQ_ENROLLEE, NULL, 0, 0) ||
wps_build_req_dev_type(dev, ie, num_req_dev_types, req_dev_types)
||
wps_build_secondary_dev_type(dev, ie)
diff --git a/src/wps/wps.h b/src/wps/wps.h
index 2505d2d9f246d..14ce863269f6c 100644
--- a/src/wps/wps.h
+++ b/src/wps/wps.h
@@ -100,6 +100,7 @@ struct wps_device_data {
struct wpabuf *vendor_ext[MAX_WPS_VENDOR_EXTENSIONS];
int p2p;
+ u8 multi_ap_ext;
};
/**
@@ -187,6 +188,12 @@ struct wps_config {
* peer_pubkey_hash - Peer public key hash or %NULL if not known
*/
const u8 *peer_pubkey_hash;
+
+ /**
+ * multi_ap_backhaul_sta - Whether this is a Multi-AP backhaul STA
+ * enrollee
+ */
+ int multi_ap_backhaul_sta;
};
struct wps_data * wps_init(const struct wps_config *cfg);
@@ -395,6 +402,37 @@ struct wps_registrar_config {
* PSK is set for a network.
*/
int force_per_enrollee_psk;
+
+ /**
+ * multi_ap_backhaul_ssid - SSID to supply to a Multi-AP backhaul
+ * enrollee
+ *
+ * This SSID is used by the Registrar to fill in information for
+ * Credentials when the enrollee advertises it is a Multi-AP backhaul
+ * STA.
+ */
+ const u8 *multi_ap_backhaul_ssid;
+
+ /**
+ * multi_ap_backhaul_ssid_len - Length of multi_ap_backhaul_ssid in
+ * octets
+ */
+ size_t multi_ap_backhaul_ssid_len;
+
+ /**
+ * multi_ap_backhaul_network_key - The Network Key (PSK) for the
+ * Multi-AP backhaul enrollee.
+ *
+ * This key can be either the ASCII passphrase (8..63 characters) or the
+ * 32-octet PSK (64 hex characters).
+ */
+ const u8 *multi_ap_backhaul_network_key;
+
+ /**
+ * multi_ap_backhaul_network_key_len - Length of
+ * multi_ap_backhaul_network_key in octets
+ */
+ size_t multi_ap_backhaul_network_key_len;
};
diff --git a/src/wps/wps_attr_build.c b/src/wps/wps_attr_build.c
index 770f5e90cbde0..4e872f37295c1 100644
--- a/src/wps/wps_attr_build.c
+++ b/src/wps/wps_attr_build.c
@@ -60,7 +60,8 @@ int wps_build_public_key(struct wps_data *wps, struct wpabuf *msg)
}
wps->dh_privkey = wpabuf_dup(wps->wps->ap_nfc_dh_privkey);
pubkey = wpabuf_dup(wps->wps->ap_nfc_dh_pubkey);
- wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, pubkey);
+ if (wps->dh_privkey && pubkey)
+ wps->dh_ctx = dh5_init_fixed(wps->dh_privkey, pubkey);
#endif /* CONFIG_WPS_NFC */
} else {
wpa_printf(MSG_DEBUG, "WPS: Generate new DH keys");
@@ -203,7 +204,8 @@ int wps_build_version(struct wpabuf *msg)
int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll,
- const u8 *auth_macs, size_t auth_macs_count)
+ const u8 *auth_macs, size_t auth_macs_count,
+ u8 multi_ap_subelem)
{
u8 *len;
@@ -244,6 +246,14 @@ int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll,
MAC2STR(&auth_macs[i * ETH_ALEN]));
}
+ if (multi_ap_subelem) {
+ wpa_printf(MSG_DEBUG, "WPS: * Multi-AP (0x%x)",
+ multi_ap_subelem);
+ wpabuf_put_u8(msg, WFA_ELEM_MULTI_AP);
+ wpabuf_put_u8(msg, 1); /* length */
+ wpabuf_put_u8(msg, multi_ap_subelem);
+ }
+
WPA_PUT_BE16(len, (u8 *) wpabuf_put(msg, 0) - len - 2);
#ifdef CONFIG_WPS_TESTING
diff --git a/src/wps/wps_attr_parse.c b/src/wps/wps_attr_parse.c
index 756d57e876c55..fd51635158ac5 100644
--- a/src/wps/wps_attr_parse.c
+++ b/src/wps/wps_attr_parse.c
@@ -67,6 +67,17 @@ static int wps_set_vendor_ext_wfa_subelem(struct wps_parse_attr *attr,
}
attr->registrar_configuration_methods = pos;
break;
+ case WFA_ELEM_MULTI_AP:
+ if (len != 1) {
+ wpa_printf(MSG_DEBUG,
+ "WPS: Invalid Multi-AP Extension length %u",
+ len);
+ return -1;
+ }
+ attr->multi_ap_ext = *pos;
+ wpa_printf(MSG_DEBUG, "WPS: Multi-AP Extension 0x%02x",
+ attr->multi_ap_ext);
+ break;
default:
wpa_printf(MSG_MSGDUMP, "WPS: Skipped unknown WFA Vendor "
"Extension subelement %u", id);
diff --git a/src/wps/wps_attr_parse.h b/src/wps/wps_attr_parse.h
index 8188fe9173d44..4de27b26d4e26 100644
--- a/src/wps/wps_attr_parse.h
+++ b/src/wps/wps_attr_parse.h
@@ -97,6 +97,7 @@ struct wps_parse_attr {
const u8 *cred[MAX_CRED_COUNT];
const u8 *req_dev_type[MAX_REQ_DEV_TYPE_COUNT];
const u8 *vendor_ext[MAX_WPS_PARSE_VENDOR_EXT];
+ u8 multi_ap_ext;
};
int wps_parse_msg(const struct wpabuf *msg, struct wps_parse_attr *attr);
diff --git a/src/wps/wps_common.c b/src/wps/wps_common.c
index bcae1ba5887b6..747dc4710b20b 100644
--- a/src/wps/wps_common.c
+++ b/src/wps/wps_common.c
@@ -374,7 +374,7 @@ struct wpabuf * wps_get_oob_cred(struct wps_context *wps, int rf_band,
(rf_band && wps_build_rf_bands_attr(plain, rf_band)) ||
(channel && wps_build_ap_channel(plain, channel)) ||
wps_build_mac_addr(plain, wps->dev.mac_addr) ||
- wps_build_wfa_ext(plain, 0, NULL, 0)) {
+ wps_build_wfa_ext(plain, 0, NULL, 0, 0)) {
os_free(data.new_psk);
wpabuf_clear_free(plain);
return NULL;
@@ -421,7 +421,7 @@ struct wpabuf * wps_build_nfc_pw_token(u16 dev_pw_id,
if (wps_build_oob_dev_pw(data, dev_pw_id, pubkey,
wpabuf_head(dev_pw), wpabuf_len(dev_pw)) ||
- wps_build_wfa_ext(data, 0, NULL, 0)) {
+ wps_build_wfa_ext(data, 0, NULL, 0, 0)) {
wpa_printf(MSG_ERROR, "WPS: Failed to build NFC password "
"token");
wpabuf_clear_free(data);
@@ -586,7 +586,7 @@ struct wpabuf * wps_build_wsc_ack(struct wps_data *wps)
wps_build_msg_type(msg, WPS_WSC_ACK) ||
wps_build_enrollee_nonce(wps, msg) ||
wps_build_registrar_nonce(wps, msg) ||
- wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wps_build_wfa_ext(msg, 0, NULL, 0, 0)) {
wpabuf_free(msg);
return NULL;
}
@@ -610,7 +610,7 @@ struct wpabuf * wps_build_wsc_nack(struct wps_data *wps)
wps_build_enrollee_nonce(wps, msg) ||
wps_build_registrar_nonce(wps, msg) ||
wps_build_config_error(msg, wps->config_error) ||
- wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wps_build_wfa_ext(msg, 0, NULL, 0, 0)) {
wpabuf_free(msg);
return NULL;
}
@@ -726,7 +726,7 @@ struct wpabuf * wps_build_nfc_handover_req(struct wps_context *ctx,
if (wps_build_oob_dev_pw(msg, DEV_PW_NFC_CONNECTION_HANDOVER,
nfc_dh_pubkey, NULL, 0) ||
wps_build_uuid_e(msg, ctx->uuid) ||
- wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wps_build_wfa_ext(msg, 0, NULL, 0, 0)) {
wpabuf_free(msg);
return NULL;
}
@@ -809,7 +809,7 @@ struct wpabuf * wps_build_nfc_handover_sel(struct wps_context *ctx,
wps_build_ssid(msg, ctx) ||
wps_build_ap_freq(msg, freq) ||
(bssid && wps_build_mac_addr(msg, bssid)) ||
- wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wps_build_wfa_ext(msg, 0, NULL, 0, 0)) {
wpabuf_free(msg);
return NULL;
}
@@ -848,7 +848,7 @@ struct wpabuf * wps_build_nfc_handover_req_p2p(struct wps_context *ctx,
wps_build_rf_bands(&ctx->dev, msg, 0) ||
wps_build_serial_number(&ctx->dev, msg) ||
wps_build_uuid_e(msg, ctx->uuid) ||
- wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wps_build_wfa_ext(msg, 0, NULL, 0, 0)) {
wpabuf_free(msg);
return NULL;
}
@@ -900,7 +900,7 @@ struct wpabuf * wps_build_nfc_handover_sel_p2p(struct wps_context *ctx,
wps_build_rf_bands(&ctx->dev, msg, 0) ||
wps_build_serial_number(&ctx->dev, msg) ||
wps_build_uuid_e(msg, ctx->uuid) ||
- wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wps_build_wfa_ext(msg, 0, NULL, 0, 0)) {
wpabuf_free(msg);
return NULL;
}
diff --git a/src/wps/wps_defs.h b/src/wps/wps_defs.h
index 301864da433d0..9fccb4eeb5c10 100644
--- a/src/wps/wps_defs.h
+++ b/src/wps/wps_defs.h
@@ -152,7 +152,8 @@ enum {
WFA_ELEM_NETWORK_KEY_SHAREABLE = 0x02,
WFA_ELEM_REQUEST_TO_ENROLL = 0x03,
WFA_ELEM_SETTINGS_DELAY_TIME = 0x04,
- WFA_ELEM_REGISTRAR_CONFIGURATION_METHODS = 0x05
+ WFA_ELEM_REGISTRAR_CONFIGURATION_METHODS = 0x05,
+ WFA_ELEM_MULTI_AP = 0x06
};
/* Device Password ID */
diff --git a/src/wps/wps_dev_attr.c b/src/wps/wps_dev_attr.c
index 0d01211a261cb..b209fea8a4f20 100644
--- a/src/wps/wps_dev_attr.c
+++ b/src/wps/wps_dev_attr.c
@@ -390,6 +390,14 @@ int wps_process_os_version(struct wps_device_data *dev, const u8 *ver)
}
+void wps_process_vendor_ext_m1(struct wps_device_data *dev, const u8 ext)
+{
+ dev->multi_ap_ext = ext;
+ wpa_printf(MSG_DEBUG, "WPS: Multi-AP extension value %02x",
+ dev->multi_ap_ext);
+}
+
+
int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands)
{
if (bands == NULL) {
diff --git a/src/wps/wps_dev_attr.h b/src/wps/wps_dev_attr.h
index c9034addbcc64..a4b4173cdbaf4 100644
--- a/src/wps/wps_dev_attr.h
+++ b/src/wps/wps_dev_attr.h
@@ -29,6 +29,7 @@ int wps_build_dev_name(struct wps_device_data *dev, struct wpabuf *msg);
int wps_process_device_attrs(struct wps_device_data *dev,
struct wps_parse_attr *attr);
int wps_process_os_version(struct wps_device_data *dev, const u8 *ver);
+void wps_process_vendor_ext_m1(struct wps_device_data *dev, const u8 ext);
int wps_process_rf_bands(struct wps_device_data *dev, const u8 *bands);
void wps_device_data_free(struct wps_device_data *dev);
int wps_build_vendor_ext(struct wps_device_data *dev, struct wpabuf *msg);
diff --git a/src/wps/wps_enrollee.c b/src/wps/wps_enrollee.c
index 417507740d7aa..80ed603fc3847 100644
--- a/src/wps/wps_enrollee.c
+++ b/src/wps/wps_enrollee.c
@@ -105,6 +105,7 @@ static struct wpabuf * wps_build_m1(struct wps_data *wps)
{
struct wpabuf *msg;
u16 config_methods;
+ u8 multi_ap_backhaul_sta = 0;
if (random_get_bytes(wps->nonce_e, WPS_NONCE_LEN) < 0)
return NULL;
@@ -134,6 +135,9 @@ static struct wpabuf * wps_build_m1(struct wps_data *wps)
WPS_CONFIG_PHY_PUSHBUTTON);
}
+ if (wps->multi_ap_backhaul_sta)
+ multi_ap_backhaul_sta = MULTI_AP_BACKHAUL_STA;
+
if (wps_build_version(msg) ||
wps_build_msg_type(msg, WPS_M1) ||
wps_build_uuid_e(msg, wps->uuid_e) ||
@@ -152,7 +156,7 @@ static struct wpabuf * wps_build_m1(struct wps_data *wps)
wps_build_dev_password_id(msg, wps->dev_pw_id) ||
wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
wps_build_os_version(&wps->wps->dev, msg) ||
- wps_build_wfa_ext(msg, 0, NULL, 0) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0, multi_ap_backhaul_sta) ||
wps_build_vendor_ext_m1(&wps->wps->dev, msg)) {
wpabuf_free(msg);
return NULL;
@@ -190,7 +194,7 @@ static struct wpabuf * wps_build_m3(struct wps_data *wps)
wps_build_msg_type(msg, WPS_M3) ||
wps_build_registrar_nonce(wps, msg) ||
wps_build_e_hash(wps, msg) ||
- wps_build_wfa_ext(msg, 0, NULL, 0) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0, 0) ||
wps_build_authenticator(wps, msg)) {
wpabuf_free(msg);
return NULL;
@@ -223,7 +227,7 @@ static struct wpabuf * wps_build_m5(struct wps_data *wps)
wps_build_e_snonce1(wps, plain) ||
wps_build_key_wrap_auth(wps, plain) ||
wps_build_encr_settings(wps, msg, plain) ||
- wps_build_wfa_ext(msg, 0, NULL, 0) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0, 0) ||
wps_build_authenticator(wps, msg)) {
wpabuf_clear_free(plain);
wpabuf_free(msg);
@@ -393,7 +397,7 @@ static struct wpabuf * wps_build_m7(struct wps_data *wps)
(wps->wps->ap && wps_build_ap_settings(wps, plain)) ||
wps_build_key_wrap_auth(wps, plain) ||
wps_build_encr_settings(wps, msg, plain) ||
- wps_build_wfa_ext(msg, 0, NULL, 0) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0, 0) ||
wps_build_authenticator(wps, msg)) {
wpabuf_clear_free(plain);
wpabuf_free(msg);
@@ -430,7 +434,7 @@ static struct wpabuf * wps_build_wsc_done(struct wps_data *wps)
wps_build_msg_type(msg, WPS_WSC_DONE) ||
wps_build_enrollee_nonce(wps, msg) ||
wps_build_registrar_nonce(wps, msg) ||
- wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wps_build_wfa_ext(msg, 0, NULL, 0, 0)) {
wpabuf_free(msg);
return NULL;
}
diff --git a/src/wps/wps_er.c b/src/wps/wps_er.c
index affd6a4af38a3..06a8fdaf3459c 100644
--- a/src/wps/wps_er.c
+++ b/src/wps/wps_er.c
@@ -1530,7 +1530,7 @@ void wps_er_set_sel_reg(struct wps_er *er, int sel_reg, u16 dev_passwd_id,
wps_er_build_selected_registrar(msg, sel_reg) ||
wps_er_build_dev_password_id(msg, dev_passwd_id) ||
wps_er_build_sel_reg_config_methods(msg, sel_reg_config_methods) ||
- wps_build_wfa_ext(msg, 0, auth_macs, count) ||
+ wps_build_wfa_ext(msg, 0, auth_macs, count, 0) ||
wps_er_build_uuid_r(msg, er->wps->uuid)) {
wpabuf_free(msg);
return;
@@ -2048,7 +2048,7 @@ struct wpabuf * wps_er_config_token_from_cred(struct wps_context *wps,
data.wps = wps;
data.use_cred = cred;
if (wps_build_cred(&data, ret) ||
- wps_build_wfa_ext(ret, 0, NULL, 0)) {
+ wps_build_wfa_ext(ret, 0, NULL, 0, 0)) {
wpabuf_free(ret);
return NULL;
}
diff --git a/src/wps/wps_i.h b/src/wps/wps_i.h
index fe0c60bd120bf..2cf22d4b7a636 100644
--- a/src/wps/wps_i.h
+++ b/src/wps/wps_i.h
@@ -125,6 +125,8 @@ struct wps_data {
int pbc_in_m1;
struct wps_nfc_pw_token *nfc_pw_token;
+
+ int multi_ap_backhaul_sta;
};
@@ -163,7 +165,8 @@ int wps_build_encr_settings(struct wps_data *wps, struct wpabuf *msg,
struct wpabuf *plain);
int wps_build_version(struct wpabuf *msg);
int wps_build_wfa_ext(struct wpabuf *msg, int req_to_enroll,
- const u8 *auth_macs, size_t auth_macs_count);
+ const u8 *auth_macs, size_t auth_macs_count,
+ u8 multi_ap_subelem);
int wps_build_msg_type(struct wpabuf *msg, enum wps_msg_type msg_type);
int wps_build_enrollee_nonce(struct wps_data *wps, struct wpabuf *msg);
int wps_build_registrar_nonce(struct wps_data *wps, struct wpabuf *msg);
diff --git a/src/wps/wps_registrar.c b/src/wps/wps_registrar.c
index 379925e3f0a9e..0ac5b28313793 100644
--- a/src/wps/wps_registrar.c
+++ b/src/wps/wps_registrar.c
@@ -188,6 +188,37 @@ struct wps_registrar {
#ifdef WPS_WORKAROUNDS
struct os_reltime pbc_ignore_start;
#endif /* WPS_WORKAROUNDS */
+
+ /**
+ * multi_ap_backhaul_ssid - SSID to supply to a Multi-AP backhaul
+ * enrollee
+ *
+ * This SSID is used by the Registrar to fill in information for
+ * Credentials when the enrollee advertises it is a Multi-AP backhaul
+ * STA.
+ */
+ u8 multi_ap_backhaul_ssid[SSID_MAX_LEN];
+
+ /**
+ * multi_ap_backhaul_ssid_len - Length of multi_ap_backhaul_ssid in
+ * octets
+ */
+ size_t multi_ap_backhaul_ssid_len;
+
+ /**
+ * multi_ap_backhaul_network_key - The Network Key (PSK) for the
+ * Multi-AP backhaul enrollee.
+ *
+ * This key can be either the ASCII passphrase (8..63 characters) or the
+ * 32-octet PSK (64 hex characters).
+ */
+ u8 *multi_ap_backhaul_network_key;
+
+ /**
+ * multi_ap_backhaul_network_key_len - Length of
+ * multi_ap_backhaul_network_key in octets
+ */
+ size_t multi_ap_backhaul_network_key_len;
};
@@ -667,6 +698,22 @@ wps_registrar_init(struct wps_context *wps,
reg->dualband = cfg->dualband;
reg->force_per_enrollee_psk = cfg->force_per_enrollee_psk;
+ if (cfg->multi_ap_backhaul_ssid) {
+ os_memcpy(reg->multi_ap_backhaul_ssid,
+ cfg->multi_ap_backhaul_ssid,
+ cfg->multi_ap_backhaul_ssid_len);
+ reg->multi_ap_backhaul_ssid_len =
+ cfg->multi_ap_backhaul_ssid_len;
+ }
+ if (cfg->multi_ap_backhaul_network_key) {
+ reg->multi_ap_backhaul_network_key =
+ os_memdup(cfg->multi_ap_backhaul_network_key,
+ cfg->multi_ap_backhaul_network_key_len);
+ if (reg->multi_ap_backhaul_network_key)
+ reg->multi_ap_backhaul_network_key_len =
+ cfg->multi_ap_backhaul_network_key_len;
+ }
+
if (wps_set_ie(reg)) {
wps_registrar_deinit(reg);
return NULL;
@@ -704,6 +751,8 @@ void wps_registrar_deinit(struct wps_registrar *reg)
eloop_cancel_timeout(wps_registrar_set_selected_timeout, reg, NULL);
wps_registrar_flush(reg);
wpabuf_clear_free(reg->extra_cred);
+ bin_clear_free(reg->multi_ap_backhaul_network_key,
+ reg->multi_ap_backhaul_network_key_len);
os_free(reg);
}
@@ -1281,7 +1330,7 @@ static int wps_set_ie(struct wps_registrar *reg)
wps_build_sel_reg_config_methods(reg, beacon) ||
wps_build_sel_pbc_reg_uuid_e(reg, beacon) ||
(reg->dualband && wps_build_rf_bands(&reg->wps->dev, beacon, 0)) ||
- wps_build_wfa_ext(beacon, 0, auth_macs, count) ||
+ wps_build_wfa_ext(beacon, 0, auth_macs, count, 0) ||
wps_build_vendor_ext(&reg->wps->dev, beacon)) {
wpabuf_free(beacon);
wpabuf_free(probe);
@@ -1311,7 +1360,7 @@ static int wps_set_ie(struct wps_registrar *reg)
wps_build_device_attrs(&reg->wps->dev, probe) ||
wps_build_probe_config_methods(reg, probe) ||
(reg->dualband && wps_build_rf_bands(&reg->wps->dev, probe, 0)) ||
- wps_build_wfa_ext(probe, 0, auth_macs, count) ||
+ wps_build_wfa_ext(probe, 0, auth_macs, count, 0) ||
wps_build_vendor_ext(&reg->wps->dev, probe)) {
wpabuf_free(beacon);
wpabuf_free(probe);
@@ -1592,6 +1641,7 @@ int wps_build_credential_wrap(struct wpabuf *msg,
int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
{
struct wpabuf *cred;
+ struct wps_registrar *reg = wps->wps->registrar;
if (wps->wps->registrar->skip_cred_build)
goto skip_cred_build;
@@ -1603,6 +1653,29 @@ int wps_build_cred(struct wps_data *wps, struct wpabuf *msg)
}
os_memset(&wps->cred, 0, sizeof(wps->cred));
+ if (wps->peer_dev.multi_ap_ext == MULTI_AP_BACKHAUL_STA &&
+ reg->multi_ap_backhaul_ssid_len) {
+ wpa_printf(MSG_DEBUG, "WPS: Use backhaul STA credentials");
+ os_memcpy(wps->cred.ssid, reg->multi_ap_backhaul_ssid,
+ reg->multi_ap_backhaul_ssid_len);
+ wps->cred.ssid_len = reg->multi_ap_backhaul_ssid_len;
+ /* Backhaul is always WPA2PSK */
+ wps->cred.auth_type = WPS_AUTH_WPA2PSK;
+ wps->cred.encr_type = WPS_ENCR_AES;
+ /* Set MAC address in the Credential to be the Enrollee's MAC
+ * address
+ */
+ os_memcpy(wps->cred.mac_addr, wps->mac_addr_e, ETH_ALEN);
+ if (reg->multi_ap_backhaul_network_key) {
+ os_memcpy(wps->cred.key,
+ reg->multi_ap_backhaul_network_key,
+ reg->multi_ap_backhaul_network_key_len);
+ wps->cred.key_len =
+ reg->multi_ap_backhaul_network_key_len;
+ }
+ goto use_provided;
+ }
+
os_memcpy(wps->cred.ssid, wps->wps->ssid, wps->wps->ssid_len);
wps->cred.ssid_len = wps->wps->ssid_len;
@@ -1845,7 +1918,7 @@ static struct wpabuf * wps_build_m2(struct wps_data *wps)
wps_build_config_error(msg, WPS_CFG_NO_ERROR) ||
wps_build_dev_password_id(msg, wps->dev_pw_id) ||
wps_build_os_version(&wps->wps->dev, msg) ||
- wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wps_build_wfa_ext(msg, 0, NULL, 0, 0)) {
wpabuf_free(msg);
return NULL;
}
@@ -1913,7 +1986,7 @@ static struct wpabuf * wps_build_m2d(struct wps_data *wps)
wps_build_assoc_state(wps, msg) ||
wps_build_config_error(msg, err) ||
wps_build_os_version(&wps->wps->dev, msg) ||
- wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ wps_build_wfa_ext(msg, 0, NULL, 0, 0)) {
wpabuf_free(msg);
return NULL;
}
@@ -1949,7 +2022,7 @@ static struct wpabuf * wps_build_m4(struct wps_data *wps)
wps_build_r_snonce1(wps, plain) ||
wps_build_key_wrap_auth(wps, plain) ||
wps_build_encr_settings(wps, msg, plain) ||
- wps_build_wfa_ext(msg, 0, NULL, 0) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0, 0) ||
wps_build_authenticator(wps, msg)) {
wpabuf_clear_free(plain);
wpabuf_free(msg);
@@ -1984,7 +2057,7 @@ static struct wpabuf * wps_build_m6(struct wps_data *wps)
wps_build_r_snonce2(wps, plain) ||
wps_build_key_wrap_auth(wps, plain) ||
wps_build_encr_settings(wps, msg, plain) ||
- wps_build_wfa_ext(msg, 0, NULL, 0) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0, 0) ||
wps_build_authenticator(wps, msg)) {
wpabuf_clear_free(plain);
wpabuf_free(msg);
@@ -2021,7 +2094,7 @@ static struct wpabuf * wps_build_m8(struct wps_data *wps)
(!wps->wps->ap && !wps->er && wps_build_ap_settings(wps, plain)) ||
wps_build_key_wrap_auth(wps, plain) ||
wps_build_encr_settings(wps, msg, plain) ||
- wps_build_wfa_ext(msg, 0, NULL, 0) ||
+ wps_build_wfa_ext(msg, 0, NULL, 0, 0) ||
wps_build_authenticator(wps, msg)) {
wpabuf_clear_free(plain);
wpabuf_clear_free(msg);
@@ -2705,6 +2778,7 @@ static enum wps_process_res wps_process_m1(struct wps_data *wps,
wps->use_psk_key = 1;
}
#endif /* WPS_WORKAROUNDS */
+ wps_process_vendor_ext_m1(&wps->peer_dev, attr->multi_ap_ext);
wps->state = SEND_M2;
return WPS_CONTINUE;
diff --git a/src/wps/wps_upnp.c b/src/wps/wps_upnp.c
index 0c458c6adef9d..ca893a43c64b4 100644
--- a/src/wps/wps_upnp.c
+++ b/src/wps/wps_upnp.c
@@ -599,7 +599,7 @@ static struct wpabuf * build_fake_wsc_ack(void)
wpabuf_put_be16(msg, ATTR_REGISTRAR_NONCE);
wpabuf_put_be16(msg, WPS_NONCE_LEN);
wpabuf_put(msg, WPS_NONCE_LEN);
- if (wps_build_wfa_ext(msg, 0, NULL, 0)) {
+ if (wps_build_wfa_ext(msg, 0, NULL, 0, 0)) {
wpabuf_free(msg);
return NULL;
}
diff --git a/src/wps/wps_validate.c b/src/wps/wps_validate.c
index 267b565e47847..5c12bce252397 100644
--- a/src/wps/wps_validate.c
+++ b/src/wps/wps_validate.c
@@ -1057,7 +1057,7 @@ static int wps_validate_cred(const u8 *cred, size_t len)
}
-static int wps_validate_credential(const u8 *cred[], size_t len[], size_t num,
+static int wps_validate_credential(const u8 *cred[], u16 len[], size_t num,
int mandatory)
{
size_t i;