* [PATCH 00/15] PMKSA support (SAE only)
@ 2024-11-22 15:15 James Prestwood
2024-11-22 15:15 ` [PATCH 01/15] handshake: add ref counting to handshake_state James Prestwood
` (14 more replies)
0 siblings, 15 replies; 21+ messages in thread
From: James Prestwood @ 2024-11-22 15:15 UTC (permalink / raw)
To: iwd; +Cc: James Prestwood
This adds support for PMKSA-based connections. For now only SAE and
SAE-FT are supported. This was done for a few reasons:
- PMKSA with WPA2 doesn't make a whole lot of sense. There is no
real benefit to using PMKSA over full association.
- PMKSA is required by the WiFi Alliance for WPA3 certificaion
Missing features include:
- 802.1x support, this may be added at a later date.
- Filesystem storage of PMKSA caches. For now this just hasn't
been implemented.
Denis Kenzior (4):
pmksa: Add skeleton
unit: Add basic pmksa test
pmksa: Add debugging
handshake: Add pmksa setter & stealer
James Prestwood (11):
handshake: add ref counting to handshake_state
unit: update use of handshake_state with ref/unref
auto-t: always initialize StationDebug in Device class
auto-t: add pmksa_flush() to hostapd module
auto-t: update testSAE to disable PMKSA
handshake: add handshake_state_remove_pmksa
netdev: add support to use PMKSA over SAE if available
station: hold reference to handshake object
station: support PMKSA connections
auto-t: add PMKSA tests
doc: document DisablePMKSA option
.gitignore | 1 +
Makefile.am | 18 +-
autotests/testPMKSA-SAE/connection_test.py | 114 ++++++++++
autotests/testPMKSA-SAE/hw.conf | 7 +
autotests/testPMKSA-SAE/ssidSAE.conf | 12 ++
autotests/testSAE-roam/connection_test.py | 60 +++++-
autotests/testSAE/main.conf | 3 +
autotests/util/hostapd.py | 4 +
autotests/util/iwd.py | 4 +-
src/adhoc.c | 4 +-
src/ap.c | 2 +-
src/handshake.c | 105 ++++++++-
src/handshake.h | 21 +-
src/iwd.config.rst | 5 +
src/netdev.c | 31 ++-
src/p2p.c | 2 +-
src/pmksa.c | 235 +++++++++++++++++++++
src/pmksa.h | 46 ++++
src/station.c | 90 +++++++-
src/wsc.c | 2 +-
unit/test-eapol.c | 38 ++--
unit/test-pmksa.c | 164 ++++++++++++++
unit/test-sae.c | 15 +-
unit/test-wsc.c | 10 +-
24 files changed, 942 insertions(+), 51 deletions(-)
create mode 100644 autotests/testPMKSA-SAE/connection_test.py
create mode 100644 autotests/testPMKSA-SAE/hw.conf
create mode 100644 autotests/testPMKSA-SAE/ssidSAE.conf
create mode 100644 src/pmksa.c
create mode 100644 src/pmksa.h
create mode 100644 unit/test-pmksa.c
--
2.34.1
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH 01/15] handshake: add ref counting to handshake_state
2024-11-22 15:15 [PATCH 00/15] PMKSA support (SAE only) James Prestwood
@ 2024-11-22 15:15 ` James Prestwood
2024-11-22 15:15 ` [PATCH 02/15] unit: update use of handshake_state with ref/unref James Prestwood
` (13 subsequent siblings)
14 siblings, 0 replies; 21+ messages in thread
From: James Prestwood @ 2024-11-22 15:15 UTC (permalink / raw)
To: iwd; +Cc: James Prestwood
This adds a ref count to the handshake state object (as well as
ref/unref APIs). Currently IWD is careful to ensure that netdev
holds the root reference to the handshake state. Other modules do
track it themselves, but ensure that it doesn't get referenced
after netdev frees it.
Future work related to PMKSA will require that station holds a
references to the handshake state, specifically for retry logic,
after netdev is done with it so we need a way to delay the free
until station is also done.
---
src/adhoc.c | 4 ++--
src/ap.c | 2 +-
src/handshake.c | 12 +++++++++++-
src/handshake.h | 9 ++++++---
src/netdev.c | 5 +++--
src/p2p.c | 2 +-
src/station.c | 8 ++++----
src/wsc.c | 2 +-
8 files changed, 29 insertions(+), 15 deletions(-)
diff --git a/src/adhoc.c b/src/adhoc.c
index e787dab1..930240ae 100644
--- a/src/adhoc.c
+++ b/src/adhoc.c
@@ -94,13 +94,13 @@ static void adhoc_sta_free(void *data)
eapol_sm_free(sta->sm);
if (sta->hs_sta)
- handshake_state_free(sta->hs_sta);
+ handshake_state_unref(sta->hs_sta);
if (sta->sm_a)
eapol_sm_free(sta->sm_a);
if (sta->hs_auth)
- handshake_state_free(sta->hs_auth);
+ handshake_state_unref(sta->hs_auth);
end:
l_free(sta);
diff --git a/src/ap.c b/src/ap.c
index 562e00c8..d52b7e55 100644
--- a/src/ap.c
+++ b/src/ap.c
@@ -230,7 +230,7 @@ static void ap_stop_handshake(struct sta_state *sta)
}
if (sta->hs) {
- handshake_state_free(sta->hs);
+ handshake_state_unref(sta->hs);
sta->hs = NULL;
}
diff --git a/src/handshake.c b/src/handshake.c
index fc1978df..7fb75dc4 100644
--- a/src/handshake.c
+++ b/src/handshake.c
@@ -103,7 +103,14 @@ void __handshake_set_install_ext_tk_func(handshake_install_ext_tk_func_t func)
install_ext_tk = func;
}
-void handshake_state_free(struct handshake_state *s)
+struct handshake_state *handshake_state_ref(struct handshake_state *s)
+{
+ __sync_fetch_and_add(&s->refcount, 1);
+
+ return s;
+}
+
+void handshake_state_unref(struct handshake_state *s)
{
__typeof__(s->free) destroy;
@@ -117,6 +124,9 @@ void handshake_state_free(struct handshake_state *s)
return;
}
+ if (__sync_sub_and_fetch(&s->refcount, 1))
+ return;
+
l_free(s->authenticator_ie);
l_free(s->supplicant_ie);
l_free(s->authenticator_rsnxe);
diff --git a/src/handshake.h b/src/handshake.h
index d1116472..6c0946d4 100644
--- a/src/handshake.h
+++ b/src/handshake.h
@@ -170,6 +170,8 @@ struct handshake_state {
bool in_event;
handshake_event_func_t event_func;
+
+ int refcount;
};
#define HSID(x) UNIQUE_ID(handshake_, x)
@@ -186,7 +188,7 @@ struct handshake_state {
##__VA_ARGS__); \
\
if (!HSID(hs)->in_event) { \
- handshake_state_free(HSID(hs)); \
+ handshake_state_unref(HSID(hs)); \
HSID(freed) = true; \
} else \
HSID(hs)->in_event = false; \
@@ -194,7 +196,8 @@ struct handshake_state {
HSID(freed); \
})
-void handshake_state_free(struct handshake_state *s);
+struct handshake_state *handshake_state_ref(struct handshake_state *s);
+void handshake_state_unref(struct handshake_state *s);
void handshake_state_set_supplicant_address(struct handshake_state *s,
const uint8_t *spa);
@@ -316,4 +319,4 @@ void handshake_util_build_gtk_kde(enum crypto_cipher cipher, const uint8_t *key,
void handshake_util_build_igtk_kde(enum crypto_cipher cipher, const uint8_t *key,
unsigned int key_index, uint8_t *to);
-DEFINE_CLEANUP_FUNC(handshake_state_free);
+DEFINE_CLEANUP_FUNC(handshake_state_unref);
diff --git a/src/netdev.c b/src/netdev.c
index e86ef1bd..4dccb78a 100644
--- a/src/netdev.c
+++ b/src/netdev.c
@@ -376,6 +376,7 @@ struct handshake_state *netdev_handshake_state_new(struct netdev *netdev)
nhs->super.ifindex = netdev->index;
nhs->super.free = netdev_handshake_state_free;
+ nhs->super.refcount = 1;
nhs->netdev = netdev;
/*
@@ -828,7 +829,7 @@ static void netdev_connect_free(struct netdev *netdev)
eapol_preauth_cancel(netdev->index);
if (netdev->handshake) {
- handshake_state_free(netdev->handshake);
+ handshake_state_unref(netdev->handshake);
netdev->handshake = NULL;
}
@@ -4239,7 +4240,7 @@ int netdev_reassociate(struct netdev *netdev, const struct scan_bss *target_bss,
eapol_sm_free(old_sm);
if (old_hs)
- handshake_state_free(old_hs);
+ handshake_state_unref(old_hs);
return 0;
}
diff --git a/src/p2p.c b/src/p2p.c
index 676ef146..7d89da21 100644
--- a/src/p2p.c
+++ b/src/p2p.c
@@ -1497,7 +1497,7 @@ static void p2p_handshake_event(struct handshake_state *hs,
static void p2p_try_connect_group(struct p2p_device *dev)
{
struct scan_bss *bss = dev->conn_wsc_bss;
- _auto_(handshake_state_free) struct handshake_state *hs = NULL;
+ _auto_(handshake_state_unref) struct handshake_state *hs = NULL;
struct iovec ie_iov[16];
int ie_num = 0;
int r;
diff --git a/src/station.c b/src/station.c
index 1238734f..c1c7ba9d 100644
--- a/src/station.c
+++ b/src/station.c
@@ -1394,7 +1394,7 @@ static struct handshake_state *station_handshake_setup(struct station *station,
return hs;
not_supported:
- handshake_state_free(hs);
+ handshake_state_unref(hs);
return NULL;
}
@@ -2484,7 +2484,7 @@ static void station_preauthenticate_cb(struct netdev *netdev,
}
if (station_transition_reassociate(station, bss, new_hs) < 0) {
- handshake_state_free(new_hs);
+ handshake_state_unref(new_hs);
station_roam_failed(station);
}
}
@@ -2687,7 +2687,7 @@ static bool station_try_next_transition(struct station *station,
}
if (station_transition_reassociate(station, bss, new_hs) < 0) {
- handshake_state_free(new_hs);
+ handshake_state_unref(new_hs);
return false;
}
@@ -3734,7 +3734,7 @@ int __station_connect_network(struct station *station, struct network *network,
station_netdev_event,
station_connect_cb, station);
if (r < 0) {
- handshake_state_free(hs);
+ handshake_state_unref(hs);
return r;
}
diff --git a/src/wsc.c b/src/wsc.c
index f88f5deb..44b8d3de 100644
--- a/src/wsc.c
+++ b/src/wsc.c
@@ -393,7 +393,7 @@ static int wsc_enrollee_connect(struct wsc_enrollee *wsce, struct scan_bss *bss,
return 0;
error:
- handshake_state_free(hs);
+ handshake_state_unref(hs);
return r;
}
--
2.34.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 02/15] unit: update use of handshake_state with ref/unref
2024-11-22 15:15 [PATCH 00/15] PMKSA support (SAE only) James Prestwood
2024-11-22 15:15 ` [PATCH 01/15] handshake: add ref counting to handshake_state James Prestwood
@ 2024-11-22 15:15 ` James Prestwood
2024-11-22 15:15 ` [PATCH 03/15] auto-t: always initialize StationDebug in Device class James Prestwood
` (12 subsequent siblings)
14 siblings, 0 replies; 21+ messages in thread
From: James Prestwood @ 2024-11-22 15:15 UTC (permalink / raw)
To: iwd; +Cc: James Prestwood
---
unit/test-eapol.c | 38 +++++++++++++++++++++-----------------
unit/test-sae.c | 15 ++++++++-------
unit/test-wsc.c | 10 ++++++----
3 files changed, 35 insertions(+), 28 deletions(-)
diff --git a/unit/test-eapol.c b/unit/test-eapol.c
index 5317f9ad..0e01bfa5 100644
--- a/unit/test-eapol.c
+++ b/unit/test-eapol.c
@@ -94,6 +94,7 @@ static struct handshake_state *test_handshake_state_new(uint32_t ifindex)
ths->super.ifindex = ifindex;
ths->super.free = test_handshake_state_free;
+ ths->super.refcount = 1;
return &ths->super;
}
@@ -2199,7 +2200,7 @@ static void eapol_sm_test_ptk(const void *data)
assert(verify_step4_called);
eapol_sm_free(sm);
- handshake_state_free(hs);
+ handshake_state_unref(hs);
eapol_exit();
}
@@ -2263,7 +2264,7 @@ static void eapol_sm_test_igtk(const void *data)
assert(verify_step4_called);
eapol_sm_free(sm);
- handshake_state_free(hs);
+ handshake_state_unref(hs);
eapol_exit();
}
@@ -2332,7 +2333,7 @@ static void eapol_sm_test_wpa2_ptk_gtk(const void *data)
assert(verify_gtk_step2_called);
eapol_sm_free(sm);
- handshake_state_free(hs);
+ handshake_state_unref(hs);
eapol_exit();
}
@@ -2399,7 +2400,7 @@ static void eapol_sm_test_wpa_ptk_gtk(const void *data)
assert(verify_gtk_step2_called);
eapol_sm_free(sm);
- handshake_state_free(hs);
+ handshake_state_unref(hs);
eapol_exit();
}
@@ -2467,7 +2468,7 @@ static void eapol_sm_test_wpa_ptk_gtk_2(const void *data)
assert(verify_gtk_step2_called);
eapol_sm_free(sm);
- handshake_state_free(hs);
+ handshake_state_unref(hs);
eapol_exit();
}
@@ -2721,7 +2722,7 @@ static void eapol_sm_wpa2_retransmit_test(const void *data)
l_free(ptk);
eapol_sm_free(sm);
- handshake_state_free(hs);
+ handshake_state_unref(hs);
eapol_exit();
}
@@ -3141,7 +3142,7 @@ done:
if (sm)
eapol_sm_free(sm);
- handshake_state_free(hs);
+ handshake_state_unref(hs);
eapol_exit();
eap_exit();
}
@@ -3424,7 +3425,7 @@ static void eapol_sm_test_eap_nak(const void *data)
sizeof(eap_failure), false);
assert(ths->handshake_failed);
- handshake_state_free(hs);
+ handshake_state_unref(hs);
eapol_exit();
eap_exit();
}
@@ -3510,7 +3511,7 @@ static void eapol_ft_handshake_test(const void *data)
assert(verify_step4_called);
eapol_sm_free(sm);
- handshake_state_free(hs);
+ handshake_state_unref(hs);
eapol_exit();
}
@@ -3609,6 +3610,7 @@ static struct handshake_state *test_ap_sta_hs_new(struct test_ap_sta_data *s,
ths->super.ifindex = ifindex;
ths->super.free = (void (*)(struct handshake_state *s)) l_free;
+ ths->super.refcount = 1;
ths->s = s;
return &ths->super;
@@ -3692,8 +3694,10 @@ static void eapol_ap_sta_handshake_test(const void *data)
test_ap_sta_run(&s);
- handshake_state_free(s.ap_hs);
- handshake_state_free(s.sta_hs);
+ l_info("freeing in eapol_ap_sta_handshake_test()");
+
+ handshake_state_unref(s.ap_hs);
+ handshake_state_unref(s.sta_hs);
__handshake_set_install_tk_func(NULL);
assert(s.ap_success && s.sta_success);
@@ -3753,8 +3757,8 @@ static void eapol_ap_sta_handshake_bad_psk_test(const void *data)
test_ap_sta_run(&s);
- handshake_state_free(s.ap_hs);
- handshake_state_free(s.sta_hs);
+ handshake_state_unref(s.ap_hs);
+ handshake_state_unref(s.sta_hs);
__handshake_set_install_tk_func(NULL);
/*
@@ -3825,8 +3829,8 @@ static void eapol_ap_sta_handshake_ip_alloc_ok_test(const void *data)
assert(s.sta_hs->subnet_mask == s.ap_hs->subnet_mask);
assert(s.sta_hs->go_ip_addr == s.ap_hs->go_ip_addr);
- handshake_state_free(s.ap_hs);
- handshake_state_free(s.sta_hs);
+ handshake_state_unref(s.ap_hs);
+ handshake_state_unref(s.sta_hs);
__handshake_set_install_tk_func(NULL);
assert(s.ap_success && s.sta_success);
@@ -3889,8 +3893,8 @@ static void eapol_ap_sta_handshake_ip_alloc_no_req_test(const void *data)
assert(!s.ap_hs->support_ip_allocation);
assert(!s.sta_hs->support_ip_allocation);
- handshake_state_free(s.ap_hs);
- handshake_state_free(s.sta_hs);
+ handshake_state_unref(s.ap_hs);
+ handshake_state_unref(s.sta_hs);
__handshake_set_install_tk_func(NULL);
assert(s.ap_success && s.sta_success);
diff --git a/unit/test-sae.c b/unit/test-sae.c
index 04783d18..2e564133 100644
--- a/unit/test-sae.c
+++ b/unit/test-sae.c
@@ -101,6 +101,7 @@ static struct handshake_state *test_handshake_state_new(uint32_t ifindex)
ths->super.ifindex = ifindex;
ths->super.free = test_handshake_state_free;
+ ths->super.refcount = 1;
return &ths->super;
}
@@ -183,7 +184,7 @@ static struct auth_proto *test_initialize(struct test_data *td)
static void test_destruct(struct test_data *td)
{
- handshake_state_free(td->handshake);
+ handshake_state_unref(td->handshake);
l_free(td);
}
@@ -459,8 +460,8 @@ static void test_bad_confirm(const void *arg)
assert(td1->tx_assoc_called);
assert(td2->status != 0);
- handshake_state_free(hs1);
- handshake_state_free(hs2);
+ handshake_state_unref(hs1);
+ handshake_state_unref(hs2);
ap1->free(ap1);
ap2->free(ap2);
@@ -544,8 +545,8 @@ static void test_confirm_after_accept(const void *arg)
assert(auth_proto_rx_associate(ap1, (uint8_t *)assoc, frame_len) == 0);
assert(auth_proto_rx_associate(ap2, (uint8_t *)assoc, frame_len) == 0);
- handshake_state_free(hs1);
- handshake_state_free(hs2);
+ handshake_state_unref(hs1);
+ handshake_state_unref(hs2);
auth_proto_free(ap1);
auth_proto_free(ap2);
@@ -621,8 +622,8 @@ static void test_end_to_end(const void *arg)
assert(auth_proto_rx_associate(ap1, (uint8_t *)assoc, frame_len) == 0);
assert(auth_proto_rx_associate(ap2, (uint8_t *)assoc, frame_len) == 0);
- handshake_state_free(hs1);
- handshake_state_free(hs2);
+ handshake_state_unref(hs1);
+ handshake_state_unref(hs2);
auth_proto_free(ap1);
auth_proto_free(ap2);
diff --git a/unit/test-wsc.c b/unit/test-wsc.c
index 8b130f45..8022ebf6 100644
--- a/unit/test-wsc.c
+++ b/unit/test-wsc.c
@@ -61,6 +61,7 @@ static struct handshake_state *test_handshake_state_new(uint32_t ifindex)
ths->super.ifindex = ifindex;
ths->super.free = test_handshake_state_free;
+ ths->super.refcount = 1;
return &ths->super;
}
@@ -2093,7 +2094,7 @@ static void wsc_test_pbc_handshake(const void *data)
eap_fail, sizeof(eap_fail), false);
assert(verify.eapol_failed);
- handshake_state_free(hs);
+ handshake_state_unref(hs);
eapol_exit();
eap_exit();
}
@@ -2216,7 +2217,7 @@ static void wsc_test_retransmission_no_fragmentation(const void *data)
sizeof(eap_fail), false);
assert(verify.eapol_failed);
- handshake_state_free(hs);
+ handshake_state_unref(hs);
eapol_exit();
eap_exit();
}
@@ -2335,6 +2336,7 @@ static struct handshake_state *test_ap_sta_hs_new(struct test_ap_sta_data *s,
ths->super.ifindex = ifindex;
ths->super.free = (void (*)(struct handshake_state *s)) l_free;
+ ths->super.refcount = 1;
ths->s = s;
return &ths->super;
@@ -2535,8 +2537,8 @@ static void wsc_r_test_pbc_handshake(const void *data)
test_ap_sta_run(&s);
- handshake_state_free(s.ap_hs);
- handshake_state_free(s.sta_hs);
+ handshake_state_unref(s.ap_hs);
+ handshake_state_unref(s.sta_hs);
__handshake_set_install_tk_func(NULL);
l_settings_free(ap_8021x_settings);
l_settings_free(sta_8021x_settings);
--
2.34.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 03/15] auto-t: always initialize StationDebug in Device class
2024-11-22 15:15 [PATCH 00/15] PMKSA support (SAE only) James Prestwood
2024-11-22 15:15 ` [PATCH 01/15] handshake: add ref counting to handshake_state James Prestwood
2024-11-22 15:15 ` [PATCH 02/15] unit: update use of handshake_state with ref/unref James Prestwood
@ 2024-11-22 15:15 ` James Prestwood
2024-11-22 15:15 ` [PATCH 04/15] auto-t: add pmksa_flush() to hostapd module James Prestwood
` (11 subsequent siblings)
14 siblings, 0 replies; 21+ messages in thread
From: James Prestwood @ 2024-11-22 15:15 UTC (permalink / raw)
To: iwd; +Cc: James Prestwood
Since IWD doesn't utilize DBus signals in "normal" operations its
fine to lazy initialize any of the DBus interfaces since properties
can be obtained as needed with Get/GetAll.
For test-runner though StationDebug uses signals for debug events
and until the StationDebug class is initialized (via a method call
or property access) all signals will be lost. Fix this by always
initializing the StationDebug interface when a Device class is
initialized.
---
autotests/util/iwd.py | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/autotests/util/iwd.py b/autotests/util/iwd.py
index e9101d75..9091807a 100755
--- a/autotests/util/iwd.py
+++ b/autotests/util/iwd.py
@@ -450,13 +450,15 @@ class Device(IWDDBusAbstract):
self._wps_manager_if = None
self._station_if = None
self._station_props = None
- self._station_debug_obj = None
self._dpp_obj = None
self._sc_dpp_obj = None
self._ap_obj = None
IWDDBusAbstract.__init__(self, *args, **kwargs)
+ self._station_debug_obj = StationDebug(object_path=self._object_path,
+ namespace=self._namespace)
+
@property
def _wps_manager(self):
if self._wps_manager_if is None:
--
2.34.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 04/15] auto-t: add pmksa_flush() to hostapd module
2024-11-22 15:15 [PATCH 00/15] PMKSA support (SAE only) James Prestwood
` (2 preceding siblings ...)
2024-11-22 15:15 ` [PATCH 03/15] auto-t: always initialize StationDebug in Device class James Prestwood
@ 2024-11-22 15:15 ` James Prestwood
2024-11-22 15:15 ` [PATCH 05/15] auto-t: update testSAE to disable PMKSA James Prestwood
` (10 subsequent siblings)
14 siblings, 0 replies; 21+ messages in thread
From: James Prestwood @ 2024-11-22 15:15 UTC (permalink / raw)
To: iwd; +Cc: James Prestwood
---
autotests/util/hostapd.py | 4 ++++
1 file changed, 4 insertions(+)
diff --git a/autotests/util/hostapd.py b/autotests/util/hostapd.py
index e5e35a96..611d8a63 100644
--- a/autotests/util/hostapd.py
+++ b/autotests/util/hostapd.py
@@ -368,3 +368,7 @@ class HostapdCLI(object):
others = [h for h in args if h != hapd]
hapd._add_neighbors(*others)
+
+ def pmksa_flush(self):
+ cmd = self.cmdline + ['pmksa_flush']
+ ctx.start_process(cmd).wait()
--
2.34.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 05/15] auto-t: update testSAE to disable PMKSA
2024-11-22 15:15 [PATCH 00/15] PMKSA support (SAE only) James Prestwood
` (3 preceding siblings ...)
2024-11-22 15:15 ` [PATCH 04/15] auto-t: add pmksa_flush() to hostapd module James Prestwood
@ 2024-11-22 15:15 ` James Prestwood
2024-11-22 15:15 ` [PATCH 06/15] pmksa: Add skeleton James Prestwood
` (9 subsequent siblings)
14 siblings, 0 replies; 21+ messages in thread
From: James Prestwood @ 2024-11-22 15:15 UTC (permalink / raw)
To: iwd; +Cc: James Prestwood
There are quite a few tests here for various scenarios and PMKSA
throws a wrench into that. Rather than potentially breaking the
tests in attempt to get them working with PMKSA, just disable PMKSA.
---
autotests/testSAE/main.conf | 3 +++
1 file changed, 3 insertions(+)
diff --git a/autotests/testSAE/main.conf b/autotests/testSAE/main.conf
index 932eb38b..ee279b4f 100644
--- a/autotests/testSAE/main.conf
+++ b/autotests/testSAE/main.conf
@@ -4,3 +4,6 @@
# hardware, but fails when used in simulated environment with mac80211_hwsim.
# Disable MAC randomization for the tests with hidden networks.
DisableMacAddressRandomization=true
+
+[General]
+DisablePMKSA=true
--
2.34.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 06/15] pmksa: Add skeleton
2024-11-22 15:15 [PATCH 00/15] PMKSA support (SAE only) James Prestwood
` (4 preceding siblings ...)
2024-11-22 15:15 ` [PATCH 05/15] auto-t: update testSAE to disable PMKSA James Prestwood
@ 2024-11-22 15:15 ` James Prestwood
2024-11-22 15:15 ` [PATCH 07/15] unit: Add basic pmksa test James Prestwood
` (8 subsequent siblings)
14 siblings, 0 replies; 21+ messages in thread
From: James Prestwood @ 2024-11-22 15:15 UTC (permalink / raw)
To: iwd; +Cc: Denis Kenzior
From: Denis Kenzior <denkenz@gmail.com>
---
Makefile.am | 7 +-
src/pmksa.c | 224 ++++++++++++++++++++++++++++++++++++++++++++++++++++
src/pmksa.h | 46 +++++++++++
3 files changed, 275 insertions(+), 2 deletions(-)
create mode 100644 src/pmksa.c
create mode 100644 src/pmksa.h
diff --git a/Makefile.am b/Makefile.am
index 61d46d7d..7805ed26 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -65,7 +65,8 @@ ell_headers = ell/util.h \
ell/cleanup.h \
ell/netconfig.h \
ell/sysctl.h \
- ell/notifylist.h
+ ell/notifylist.h \
+ ell/minheap.h
ell_sources = ell/private.h \
ell/missing.h \
@@ -147,7 +148,8 @@ ell_sources = ell/private.h \
ell/acd.c \
ell/netconfig.c \
ell/sysctl.c \
- ell/notifylist.c
+ ell/notifylist.c \
+ ell/minheap.c
ell_shared = ell/useful.h ell/asn1-private.h
@@ -269,6 +271,7 @@ src_iwd_SOURCES = src/main.c linux/nl80211.h src/iwd.h \
src/json.h src/json.c \
src/dpp.c \
src/udev.c \
+ src/pmksa.h src/pmksa.c \
$(eap_sources) \
$(builtin_sources)
diff --git a/src/pmksa.c b/src/pmksa.c
new file mode 100644
index 00000000..b2e65d17
--- /dev/null
+++ b/src/pmksa.c
@@ -0,0 +1,224 @@
+/*
+ *
+ * Wireless daemon for Linux
+ *
+ * Copyright (C) 2023 Cruise LLC. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <stdint.h>
+#include <errno.h>
+
+#include <ell/ell.h>
+#include "ell/useful.h"
+
+#include "src/module.h"
+#include "src/pmksa.h"
+
+static uint64_t dot11RSNAConfigPMKLifetime = 43200ULL * L_USEC_PER_SEC;
+static uint32_t pmksa_cache_capacity = 255;
+
+struct min_heap {
+ struct pmksa **data;
+ uint32_t capacity;
+ uint32_t used;
+};
+
+static struct min_heap cache;
+
+static __always_inline void swap_ptr(void *l, void *r)
+{
+ struct pmksa **lp = l;
+ struct pmksa **rp = r;
+
+ SWAP(*lp, *rp);
+}
+
+static __always_inline
+bool pmksa_compare_expiration(const void *l, const void *r)
+{
+ const struct pmksa * const *lp = l;
+ const struct pmksa * const *rp = r;
+
+ return (*lp)->expiration < (*rp)->expiration;
+}
+
+static struct l_minheap_ops ops = {
+ .elem_size = sizeof(struct pmksa *),
+ .swap = swap_ptr,
+ .less = pmksa_compare_expiration,
+};
+
+static int pmksa_cache_find(const uint8_t spa[static 6],
+ const uint8_t aa[static 6],
+ const uint8_t *ssid, size_t ssid_len,
+ uint32_t akm)
+{
+ unsigned int i;
+
+ for (i = 0; i < cache.used; i++) {
+ struct pmksa *pmksa = cache.data[i];
+
+ if (memcmp(pmksa->spa, spa, sizeof(pmksa->spa)))
+ continue;
+
+ if (memcmp(pmksa->aa, aa, sizeof(pmksa->aa)))
+ continue;
+
+ if (ssid_len != pmksa->ssid_len)
+ continue;
+
+ if (memcmp(pmksa->ssid, ssid, ssid_len))
+ continue;
+
+ if (akm & pmksa->akm)
+ return i;
+ }
+
+ return -ENOENT;
+}
+
+/*
+ * Try to obtain a PMKSA entry from the cache. If the the entry matching
+ * the parameters is found, it is removed from the cache and returned to the
+ * caller. The caller is responsible for managing the returned pmksa
+ * structure
+ */
+struct pmksa *pmksa_cache_get(const uint8_t spa[static 6],
+ const uint8_t aa[static 6],
+ const uint8_t *ssid, size_t ssid_len,
+ uint32_t akm)
+{
+ int r = pmksa_cache_find(spa, aa, ssid, ssid_len, akm);
+
+ if (r < 0)
+ return NULL;
+
+ cache.used -= 1;
+ if ((uint32_t) r == cache.used)
+ goto done;
+
+ SWAP(cache.data[r], cache.data[cache.used]);
+ __minheap_sift_down(cache.data, cache.used, r, &ops);
+
+done:
+ return cache.data[cache.used];
+}
+
+/*
+ * Put a PMKSA into the cache. It will be sorted in soonest-to-expire order.
+ * If the cache is full, then soonest-to-expire entry is removed first.
+ */
+int pmksa_cache_put(struct pmksa *pmksa)
+{
+ if (cache.used == cache.capacity) {
+ l_free(cache.data[0]);
+ cache.data[0] = pmksa;
+ __minheap_sift_down(cache.data, cache.used, 0, &ops);
+ return 0;
+ }
+
+ cache.data[cache.used] = pmksa;
+ __minheap_sift_up(cache.data, cache.used, &ops);
+ cache.used += 1;
+
+ return 0;
+}
+
+/*
+ * Expire all PMKSA entries with expiration time smaller or equal to the cutoff
+ * time.
+ */
+int pmksa_cache_expire(uint64_t cutoff)
+{
+ int i;
+ int used = cache.used;
+ int remaining = 0;
+
+ for (i = 0; i < used; i++) {
+ if (cache.data[i]->expiration <= cutoff) {
+ l_free(cache.data[i]);
+ continue;
+ }
+
+ cache.data[remaining] = cache.data[i];
+ remaining += 1;
+ }
+
+ cache.used = remaining;
+
+ for (i = cache.used >> 1; i >= 0; i--)
+ __minheap_sift_down(cache.data, cache.used, i, &ops);
+
+ return used - remaining;
+}
+
+/*
+ * Flush all PMKSA entries from the cache, regardless of expiration time.
+ */
+int pmksa_cache_flush(void)
+{
+ uint32_t i;
+
+ for (i = 0; i < cache.used; i++)
+ l_free(cache.data[i]);
+
+ memset(cache.data, 0, cache.capacity * sizeof(struct pmksa *));
+ cache.used = 0;
+ return 0;
+}
+
+struct pmksa **__pmksa_cache_get_all(uint32_t *out_n_entries)
+{
+ if (out_n_entries)
+ *out_n_entries = cache.used;
+
+ return cache.data;
+}
+
+uint64_t pmksa_lifetime(void)
+{
+ return dot11RSNAConfigPMKLifetime;
+}
+
+void __pmksa_set_config(const struct l_settings *config)
+{
+ l_settings_get_uint(config, "PMKSA", "Capacity",
+ &pmksa_cache_capacity);
+}
+
+static int pmksa_init(void)
+{
+ cache.capacity = pmksa_cache_capacity;
+ cache.used = 0;
+ cache.data = l_new(struct pmksa *, cache.capacity);
+
+ return 0;
+}
+
+static void pmksa_exit(void)
+{
+ pmksa_cache_flush();
+ l_free(cache.data);
+}
+
+IWD_MODULE(pmksa, pmksa_init, pmksa_exit);
diff --git a/src/pmksa.h b/src/pmksa.h
new file mode 100644
index 00000000..67879309
--- /dev/null
+++ b/src/pmksa.h
@@ -0,0 +1,46 @@
+/*
+ *
+ * Wireless daemon for Linux
+ *
+ * Copyright (C) 2023 Cruise, LLC. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+struct pmksa {
+ uint64_t expiration;
+ uint8_t spa[6];
+ uint8_t aa[6];
+ uint8_t ssid[32];
+ size_t ssid_len;
+ uint32_t akm;
+ uint8_t pmkid[16];
+ uint8_t pmk[64];
+ size_t pmk_len;
+};
+
+struct pmksa **__pmksa_cache_get_all(uint32_t *out_n_entries);
+
+struct pmksa *pmksa_cache_get(const uint8_t spa[static 6],
+ const uint8_t aa[static 6],
+ const uint8_t *ssid, size_t ssid_len,
+ uint32_t akm);
+int pmksa_cache_put(struct pmksa *pmksa);
+int pmksa_cache_expire(uint64_t cutoff);
+int pmksa_cache_flush(void);
+
+uint64_t pmksa_lifetime(void);
+void __pmksa_set_config(const struct l_settings *config);
--
2.34.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 07/15] unit: Add basic pmksa test
2024-11-22 15:15 [PATCH 00/15] PMKSA support (SAE only) James Prestwood
` (5 preceding siblings ...)
2024-11-22 15:15 ` [PATCH 06/15] pmksa: Add skeleton James Prestwood
@ 2024-11-22 15:15 ` James Prestwood
2024-11-22 15:15 ` [PATCH 08/15] pmksa: Add debugging James Prestwood
` (7 subsequent siblings)
14 siblings, 0 replies; 21+ messages in thread
From: James Prestwood @ 2024-11-22 15:15 UTC (permalink / raw)
To: iwd; +Cc: Denis Kenzior
From: Denis Kenzior <denkenz@gmail.com>
---
.gitignore | 1 +
Makefile.am | 7 +-
unit/test-pmksa.c | 164 ++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 171 insertions(+), 1 deletion(-)
create mode 100644 unit/test-pmksa.c
diff --git a/.gitignore b/.gitignore
index 8af48c16..5fb9145e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -67,6 +67,7 @@ unit/test-band
unit/test-dpp
unit/test-json
unit/test-nl80211util
+unit/test-pmksa
unit/cert-*.pem
unit/cert-*.csr
unit/cert-*.srl
diff --git a/Makefile.am b/Makefile.am
index 7805ed26..598b8f90 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -438,7 +438,8 @@ unit_tests += unit/test-cmac-aes \
unit/test-ie unit/test-util unit/test-ssid-security \
unit/test-arc4 unit/test-wsc unit/test-eap-mschapv2 \
unit/test-eap-sim unit/test-sae unit/test-p2p unit/test-band \
- unit/test-dpp unit/test-json unit/test-nl80211util
+ unit/test-dpp unit/test-json unit/test-nl80211util \
+ unit/test-pmksa
endif
if CLIENT
@@ -594,6 +595,10 @@ unit_test_nl80211util_SOURCES = unit/test-nl80211util.c \
src/ie.h src/ie.c \
src/util.h src/util.c
unit_test_nl80211util_LDADD = $(ell_ldadd)
+
+unit_test_pmksa_SOURCES = unit/test-pmksa.c src/pmksa.c src/pmksa.h \
+ src/module.h src/util.h
+unit_test_pmksa_LDADD = $(ell_ldadd)
endif
if CLIENT
diff --git a/unit/test-pmksa.c b/unit/test-pmksa.c
new file mode 100644
index 00000000..4c7111aa
--- /dev/null
+++ b/unit/test-pmksa.c
@@ -0,0 +1,164 @@
+/*
+ *
+ * Wireless daemon for Linux
+ *
+ * Copyright (C) 2023 Cruise LLC. All rights reserved.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <assert.h>
+#include <ell/ell.h>
+
+#include "src/module.h"
+#include "src/util.h"
+#include "src/pmksa.h"
+
+static bool verbose = false;
+static struct l_settings *config;
+extern struct iwd_module_desc __start___iwd_module[];
+extern struct iwd_module_desc __stop___iwd_module[];
+/* There's a single module compiled in, so it will be the pmksa one */
+static struct iwd_module_desc *pmksa = __start___iwd_module;
+
+static void print_cache()
+{
+ uint32_t used;
+ struct pmksa **entries = __pmksa_cache_get_all(&used);
+ uint32_t i;
+
+ for (i = 0; i < used; i++) {
+ struct pmksa *pmksa = entries[i];
+
+ fprintf(stderr, "%02u aa "MAC" spa "MAC" expiration: %"
+ PRIu64"\n", i,
+ MAC_STR(pmksa->aa), MAC_STR(pmksa->spa),
+ pmksa->expiration);
+ }
+}
+
+static struct pmksa *make_pmksa()
+{
+ static uint32_t counter = 0xabcdef00;
+ struct pmksa *pmksa = l_new(struct pmksa, 1);
+
+ memcpy(pmksa->aa, &counter, sizeof(counter));
+ counter += 1;
+ memcpy(pmksa->spa, &counter, sizeof(counter));
+ counter += 1;
+
+ pmksa->ssid_len = 6;
+ pmksa->ssid[0] = 'F';
+ pmksa->ssid[1] = 'o';
+ pmksa->ssid[2] = 'o';
+ pmksa->ssid[3] = 'b';
+ pmksa->ssid[4] = 'a';
+ pmksa->ssid[5] = 'r';
+
+ pmksa->akm = 0x4;
+
+ return pmksa;
+}
+
+static void test_pmksa(const void *data)
+{
+ struct pmksa *p;
+ struct pmksa **entries;
+ uint32_t used;
+
+ config = l_settings_new();
+ l_settings_set_uint(config, "PMKSA", "Capacity", 7);
+ __pmksa_set_config(config);
+ pmksa->init();
+
+ p = make_pmksa();
+ p->expiration = 20;
+ assert(!pmksa_cache_put(p));
+
+ p = make_pmksa();
+ p->expiration = 15;
+ assert(!pmksa_cache_put(p));
+
+ p = make_pmksa();
+ p->expiration = 32;
+ assert(!pmksa_cache_put(p));
+
+ p = make_pmksa();
+ p->expiration = 48;
+ assert(!pmksa_cache_put(p));
+
+ p = make_pmksa();
+ p->expiration = 102;
+ assert(!pmksa_cache_put(p));
+
+ p = make_pmksa();
+ p->expiration = 55;
+ assert(!pmksa_cache_put(p));
+
+ p = make_pmksa();
+ p->expiration = 41;
+ assert(!pmksa_cache_put(p));
+
+ p = make_pmksa();
+ p->expiration = 66;
+ assert(!pmksa_cache_put(p));
+
+ if (verbose)
+ print_cache();
+
+ entries = __pmksa_cache_get_all(&used);
+ assert(used == 7);
+ assert(entries[0]->expiration == 20);
+
+ /* Reverse spa and aa */
+ p = pmksa_cache_get(entries[0]->aa, entries[0]->spa,
+ entries[0]->ssid, entries[0]->ssid_len,
+ 0xff);
+ assert(!p);
+
+ p = pmksa_cache_get(entries[0]->spa, entries[0]->aa,
+ entries[0]->ssid, entries[0]->ssid_len,
+ 0xff);
+ assert(p);
+ l_free(p);
+
+ entries = __pmksa_cache_get_all(&used);
+ assert(used == 6);
+ assert(entries[0]->expiration == 32);
+
+ assert(pmksa_cache_expire(48) == 3);
+ entries = __pmksa_cache_get_all(&used);
+ assert(used == 3);
+ assert(entries[0]->expiration == 55);
+
+ pmksa->exit();
+ l_settings_free(config);
+}
+
+int main(int argc, char *argv[])
+{
+ l_test_init(&argc, &argv);
+
+ l_test_add("PMKSA/basics", test_pmksa, NULL);
+
+ return l_test_run();
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 08/15] pmksa: Add debugging
2024-11-22 15:15 [PATCH 00/15] PMKSA support (SAE only) James Prestwood
` (6 preceding siblings ...)
2024-11-22 15:15 ` [PATCH 07/15] unit: Add basic pmksa test James Prestwood
@ 2024-11-22 15:15 ` James Prestwood
2024-11-22 15:15 ` [PATCH 09/15] handshake: Add pmksa setter & stealer James Prestwood
` (6 subsequent siblings)
14 siblings, 0 replies; 21+ messages in thread
From: James Prestwood @ 2024-11-22 15:15 UTC (permalink / raw)
To: iwd; +Cc: Denis Kenzior
From: Denis Kenzior <denkenz@gmail.com>
---
src/pmksa.c | 13 ++++++++++++-
1 file changed, 12 insertions(+), 1 deletion(-)
diff --git a/src/pmksa.c b/src/pmksa.c
index b2e65d17..bb539b85 100644
--- a/src/pmksa.c
+++ b/src/pmksa.c
@@ -34,6 +34,10 @@
#include "src/module.h"
#include "src/pmksa.h"
+#define PMKID "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
+#define PMKID_STR(x) x[0], x[1], x[2], x[3], x[4], x[5], x[6], x[7], \
+ x[8], x[9], x[10], x[11], x[12], x[13], x[14], x[15]
+
static uint64_t dot11RSNAConfigPMKLifetime = 43200ULL * L_USEC_PER_SEC;
static uint32_t pmksa_cache_capacity = 255;
@@ -108,6 +112,7 @@ struct pmksa *pmksa_cache_get(const uint8_t spa[static 6],
const uint8_t *ssid, size_t ssid_len,
uint32_t akm)
{
+ struct pmksa *pmksa;
int r = pmksa_cache_find(spa, aa, ssid, ssid_len, akm);
if (r < 0)
@@ -121,7 +126,11 @@ struct pmksa *pmksa_cache_get(const uint8_t spa[static 6],
__minheap_sift_down(cache.data, cache.used, r, &ops);
done:
- return cache.data[cache.used];
+ pmksa = cache.data[cache.used];
+
+ l_debug("Returning entry with PMKID: "PMKID, PMKID_STR(pmksa->pmkid));
+
+ return pmksa;
}
/*
@@ -130,6 +139,8 @@ done:
*/
int pmksa_cache_put(struct pmksa *pmksa)
{
+ l_debug("Adding entry with PMKID: "PMKID, PMKID_STR(pmksa->pmkid));
+
if (cache.used == cache.capacity) {
l_free(cache.data[0]);
cache.data[0] = pmksa;
--
2.34.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 09/15] handshake: Add pmksa setter & stealer
2024-11-22 15:15 [PATCH 00/15] PMKSA support (SAE only) James Prestwood
` (7 preceding siblings ...)
2024-11-22 15:15 ` [PATCH 08/15] pmksa: Add debugging James Prestwood
@ 2024-11-22 15:15 ` James Prestwood
2024-11-25 14:56 ` Denis Kenzior
2024-11-22 15:15 ` [PATCH 10/15] handshake: add handshake_state_remove_pmksa James Prestwood
` (5 subsequent siblings)
14 siblings, 1 reply; 21+ messages in thread
From: James Prestwood @ 2024-11-22 15:15 UTC (permalink / raw)
To: iwd; +Cc: Denis Kenzior, James Prestwood
From: Denis Kenzior <denkenz@gmail.com>
The majority of this patch was authored by Denis Kenzior, but
I have appended setting the PMK inside handshake_state_set_pmksa
as well as checking if the pmkid exists in
handshake_state_steal_pmkid.
Authored-by: Denis Kenzior <denkenz@gmail.com>
Authored-by: James Prestwood <prestwoj@gmail.com>
---
Makefile.am | 4 +++
src/handshake.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++
src/handshake.h | 11 ++++++-
3 files changed, 91 insertions(+), 1 deletion(-)
diff --git a/Makefile.am b/Makefile.am
index 598b8f90..89198289 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -458,6 +458,7 @@ unit_test_eap_sim_SOURCES = unit/test-eap-sim.c \
src/eapol.h src/eapol.c \
src/eapolutil.h src/eapolutil.c \
src/handshake.h src/handshake.c \
+ src/pmksa.h src/pmksa.c \
src/eap.h src/eap.c src/eap-private.h \
src/util.h src/util.c \
src/simauth.h src/simauth.c \
@@ -517,6 +518,7 @@ unit_test_eapol_SOURCES = unit/test-eapol.c \
src/eapol.h src/eapol.c \
src/eapolutil.h src/eapolutil.c \
src/handshake.h src/handshake.c \
+ src/pmksa.h src/pmksa.c \
src/eap.h src/eap.c src/eap-private.h \
src/eap-tls.c src/eap-ttls.c \
src/eap-md5.c src/util.c \
@@ -547,6 +549,7 @@ unit_test_wsc_SOURCES = unit/test-wsc.c src/wscutil.h src/wscutil.c \
src/eapol.h src/eapol.c \
src/eapolutil.h src/eapolutil.c \
src/handshake.h src/handshake.c \
+ src/pmksa.h src/pmksa.c \
src/eap.h src/eap.c src/eap-private.h \
src/util.h src/util.c \
src/erp.h src/erp.c \
@@ -565,6 +568,7 @@ unit_test_sae_SOURCES = unit/test-sae.c \
src/crypto.h src/crypto.c \
src/ie.h src/ie.c \
src/handshake.h src/handshake.c \
+ src/pmksa.h src/pmksa.c \
src/erp.h src/erp.c \
src/band.h src/band.c \
src/util.h src/util.c \
diff --git a/src/handshake.c b/src/handshake.c
index 7fb75dc4..a93143d1 100644
--- a/src/handshake.c
+++ b/src/handshake.c
@@ -43,6 +43,7 @@
#include "src/handshake.h"
#include "src/erp.h"
#include "src/band.h"
+#include "src/pmksa.h"
static inline unsigned int n_ecc_groups(void)
{
@@ -138,6 +139,9 @@ void handshake_state_unref(struct handshake_state *s)
l_free(s->fils_ip_resp_ie);
l_free(s->vendor_ies);
+ if (s->have_pmksa)
+ l_free(s->pmksa);
+
if (s->erp_cache)
erp_cache_put(s->erp_cache);
@@ -701,6 +705,11 @@ void handshake_state_install_ptk(struct handshake_state *s)
{
s->ptk_complete = true;
+ if (!s->have_pmksa && IE_AKM_IS_SAE(s->akm_suite)) {
+ l_debug("Adding PMKSA expiration");
+ s->expiration = l_time_now() + pmksa_lifetime();
+ }
+
if (install_tk) {
uint32_t cipher = ie_rsn_cipher_suite_to_cipher(
s->pairwise_cipher);
@@ -1203,3 +1212,71 @@ done:
return r;
}
+
+bool handshake_state_set_pmksa(struct handshake_state *s,
+ struct pmksa *pmksa)
+{
+ /* checks for both expiration || pmksa being set */
+ if (s->expiration)
+ return false;
+
+ s->pmksa = pmksa;
+ s->have_pmksa = true;
+
+ handshake_state_set_pmkid(s, pmksa->pmkid);
+ handshake_state_set_pmk(s, pmksa->pmk, pmksa->pmk_len);
+
+ return true;
+}
+
+static struct pmksa *handshake_state_steal_pmksa(struct handshake_state *s)
+{
+ struct pmksa *pmksa;
+ uint64_t now = l_time_now();
+
+ if (s->have_pmksa) {
+ pmksa = l_steal_ptr(s->pmksa);
+ s->have_pmksa = false;
+
+ if (l_time_after(now, pmksa->expiration)) {
+ l_free(pmksa);
+ pmksa = NULL;
+ }
+
+ return pmksa;
+ }
+
+ if (s->expiration && l_time_after(now, s->expiration)) {
+ s->expiration = 0;
+ return NULL;
+ }
+
+ if (!s->have_pmkid)
+ return NULL;
+
+ pmksa = l_new(struct pmksa, 1);
+ pmksa->expiration = s->expiration;
+ memcpy(pmksa->spa, s->spa, sizeof(s->spa));
+ memcpy(pmksa->aa, s->aa, sizeof(s->aa));
+ memcpy(pmksa->ssid, s->ssid, s->ssid_len);
+ pmksa->ssid_len = s->ssid_len;
+ pmksa->akm = s->akm_suite;
+ memcpy(pmksa->pmkid, s->pmkid, sizeof(s->pmkid));
+ pmksa->pmk_len = s->pmk_len;
+ memcpy(pmksa->pmk, s->pmk, s->pmk_len);
+
+ return pmksa;
+}
+
+void handshake_state_cache_pmksa(struct handshake_state *s)
+{
+ struct pmksa *pmksa = handshake_state_steal_pmksa(s);
+
+ l_debug("%p", pmksa);
+
+ if (!pmksa)
+ return;
+
+ if (L_WARN_ON(pmksa_cache_put(pmksa) < 0))
+ l_free(pmksa);
+}
diff --git a/src/handshake.h b/src/handshake.h
index 6c0946d4..cf7dc48c 100644
--- a/src/handshake.h
+++ b/src/handshake.h
@@ -29,6 +29,7 @@
struct handshake_state;
enum crypto_cipher;
struct eapol_frame;
+struct pmksa;
enum handshake_kde {
/* 802.11-2020 Table 12-9 in section 12.7.2 */
@@ -141,7 +142,12 @@ struct handshake_state {
bool supplicant_ocvc : 1;
bool ext_key_id_capable : 1;
bool force_default_ecc_group : 1;
- uint8_t ssid[SSID_MAX_SIZE];
+ bool have_pmksa : 1;
+ union {
+ struct pmksa *pmksa;
+ uint64_t expiration;
+ };
+ uint8_t ssid[32];
size_t ssid_len;
char *passphrase;
char *password_identifier;
@@ -302,6 +308,9 @@ void handshake_state_set_chandef(struct handshake_state *s,
int handshake_state_verify_oci(struct handshake_state *s, const uint8_t *oci,
size_t oci_len);
+bool handshake_state_set_pmksa(struct handshake_state *s, struct pmksa *pmksa);
+void handshake_state_cache_pmksa(struct handshake_state *s);
+
bool handshake_util_ap_ie_matches(const struct ie_rsn_info *msg_info,
const uint8_t *scan_ie, bool is_wpa);
--
2.34.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 10/15] handshake: add handshake_state_remove_pmksa
2024-11-22 15:15 [PATCH 00/15] PMKSA support (SAE only) James Prestwood
` (8 preceding siblings ...)
2024-11-22 15:15 ` [PATCH 09/15] handshake: Add pmksa setter & stealer James Prestwood
@ 2024-11-22 15:15 ` James Prestwood
2024-11-22 15:15 ` [PATCH 11/15] netdev: add support to use PMKSA over SAE if available James Prestwood
` (4 subsequent siblings)
14 siblings, 0 replies; 21+ messages in thread
From: James Prestwood @ 2024-11-22 15:15 UTC (permalink / raw)
To: iwd; +Cc: James Prestwood
This is needed in order to clear the PMKSA from the handshake state
without actually putting it back into the cache. This is something
that will be needed in case the AP rejects the association due to
an expired (or forgotten) PMKSA.
---
src/handshake.c | 16 ++++++++++++++++
src/handshake.h | 1 +
2 files changed, 17 insertions(+)
diff --git a/src/handshake.c b/src/handshake.c
index a93143d1..f73f91d1 100644
--- a/src/handshake.c
+++ b/src/handshake.c
@@ -1280,3 +1280,19 @@ void handshake_state_cache_pmksa(struct handshake_state *s)
if (L_WARN_ON(pmksa_cache_put(pmksa) < 0))
l_free(pmksa);
}
+
+bool handshake_state_remove_pmksa(struct handshake_state *s)
+{
+ struct pmksa *pmksa;
+
+ if (!s->have_pmksa)
+ return false;
+
+ pmksa = handshake_state_steal_pmksa(s);
+ if (!pmksa)
+ return false;
+
+ l_free(pmksa);
+
+ return true;
+}
diff --git a/src/handshake.h b/src/handshake.h
index cf7dc48c..e4a00531 100644
--- a/src/handshake.h
+++ b/src/handshake.h
@@ -310,6 +310,7 @@ int handshake_state_verify_oci(struct handshake_state *s, const uint8_t *oci,
bool handshake_state_set_pmksa(struct handshake_state *s, struct pmksa *pmksa);
void handshake_state_cache_pmksa(struct handshake_state *s);
+bool handshake_state_remove_pmksa(struct handshake_state *s);
bool handshake_util_ap_ie_matches(const struct ie_rsn_info *msg_info,
const uint8_t *scan_ie, bool is_wpa);
--
2.34.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 11/15] netdev: add support to use PMKSA over SAE if available
2024-11-22 15:15 [PATCH 00/15] PMKSA support (SAE only) James Prestwood
` (9 preceding siblings ...)
2024-11-22 15:15 ` [PATCH 10/15] handshake: add handshake_state_remove_pmksa James Prestwood
@ 2024-11-22 15:15 ` James Prestwood
2024-11-22 15:15 ` [PATCH 12/15] station: hold reference to handshake object James Prestwood
` (3 subsequent siblings)
14 siblings, 0 replies; 21+ messages in thread
From: James Prestwood @ 2024-11-22 15:15 UTC (permalink / raw)
To: iwd; +Cc: James Prestwood
This was quite simple and only requiring caching the PMKSA after a
successful handshake, and using the correct authentication type
for connections if we have a prior PMKSA cached.
This is only being added for initial SAE associations for now since
this is where we gain the biggest improvement, in addition to the
requirement by the WiFi alliance to label products as "WPA3 capable"
---
src/netdev.c | 26 +++++++++++++++++++++++++-
1 file changed, 25 insertions(+), 1 deletion(-)
diff --git a/src/netdev.c b/src/netdev.c
index 4dccb78a..02496c92 100644
--- a/src/netdev.c
+++ b/src/netdev.c
@@ -65,6 +65,7 @@
#include "src/frame-xchg.h"
#include "src/diagnostic.h"
#include "src/band.h"
+#include "src/pmksa.h"
#ifndef ENOTSUPP
#define ENOTSUPP 524
@@ -1517,6 +1518,8 @@ static void try_handshake_complete(struct netdev_handshake_state *nhs)
l_debug("Invoking handshake_event()");
+ handshake_state_cache_pmksa(&nhs->super);
+
if (handshake_event(&nhs->super, HANDSHAKE_EVENT_COMPLETE))
return;
@@ -2458,7 +2461,19 @@ static struct l_genl_msg *netdev_build_cmd_connect(struct netdev *netdev,
{
struct netdev_handshake_state *nhs =
l_container_of(hs, struct netdev_handshake_state, super);
- uint32_t auth_type = IE_AKM_IS_SAE(hs->akm_suite) ?
+ /*
+ * Choose Open system auth type if PMKSA caching is used for an SAE AKM:
+ *
+ * IEEE 802.11-2020 Table 9-151
+ * - SAE authentication:
+ * 3 (SAE) for SAE Authentication
+ * 0 (open) for PMKSA caching
+ * - FT authentication over SAE:
+ * 3 (SAE) for FT Initial Mobility Domain Association
+ * 0 (open) for FT Initial Mobility Domain Association over
+ * PMKSA caching
+ */
+ uint32_t auth_type = IE_AKM_IS_SAE(hs->akm_suite) && !hs->have_pmksa ?
NL80211_AUTHTYPE_SAE :
NL80211_AUTHTYPE_OPEN_SYSTEM;
enum mpdu_management_subtype subtype = prev_bssid ?
@@ -4027,6 +4042,15 @@ static void netdev_connect_common(struct netdev *netdev,
goto done;
}
+ /*
+ * If SAE, and we have a valid PMKSA cache we can skip the entire SAE
+ * protocol and authenticate using the cached keys.
+ */
+ if (IE_AKM_IS_SAE(hs->akm_suite) && hs->have_pmksa) {
+ l_debug("Skipping SAE by using PMKSA cache");
+ goto build_cmd_connect;
+ }
+
if (!IE_AKM_IS_SAE(hs->akm_suite) ||
nhs->type == CONNECTION_TYPE_SAE_OFFLOAD)
goto build_cmd_connect;
--
2.34.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 12/15] station: hold reference to handshake object
2024-11-22 15:15 [PATCH 00/15] PMKSA support (SAE only) James Prestwood
` (10 preceding siblings ...)
2024-11-22 15:15 ` [PATCH 11/15] netdev: add support to use PMKSA over SAE if available James Prestwood
@ 2024-11-22 15:15 ` James Prestwood
2024-11-22 15:15 ` [PATCH 13/15] station: support PMKSA connections James Prestwood
` (2 subsequent siblings)
14 siblings, 0 replies; 21+ messages in thread
From: James Prestwood @ 2024-11-22 15:15 UTC (permalink / raw)
To: iwd; +Cc: James Prestwood
To prepare for PMKSA support station needs access to the handshake
object. This is because if PMKSA fails due to an expired/missing
PMKSA on the AP station should retry using the standard association.
This poses a problem currently because netdev frees the handshake
prior to calling the connect callback.
---
src/station.c | 28 ++++++++++++++++++++++++++++
1 file changed, 28 insertions(+)
diff --git a/src/station.c b/src/station.c
index c1c7ba9d..09193eed 100644
--- a/src/station.c
+++ b/src/station.c
@@ -135,6 +135,8 @@ struct station {
unsigned int affinity_watch;
char *affinity_client;
+ struct handshake_state *hs;
+
bool preparing_roam : 1;
bool roam_scan_full : 1;
bool signal_low : 1;
@@ -1771,6 +1773,11 @@ static void station_enter_state(struct station *station,
station->affinity_watch = 0;
}
+ if (station->hs) {
+ handshake_state_unref(station->hs);
+ station->hs = NULL;
+ }
+
break;
case STATION_STATE_DISCONNECTING:
case STATION_STATE_NETCONFIG:
@@ -2487,6 +2494,9 @@ static void station_preauthenticate_cb(struct netdev *netdev,
handshake_state_unref(new_hs);
station_roam_failed(station);
}
+
+ handshake_state_unref(station->hs);
+ station->hs = handshake_state_ref(new_hs);
}
static void station_transition_start(struct station *station);
@@ -2691,6 +2701,9 @@ static bool station_try_next_transition(struct station *station,
return false;
}
+ handshake_state_unref(station->hs);
+ station->hs = handshake_state_ref(new_hs);
+
return true;
}
@@ -3721,6 +3734,15 @@ int __station_connect_network(struct station *station, struct network *network,
struct handshake_state *hs;
int r;
+ /*
+ * If we already have a handshake_state ref this is due to a retry,
+ * unref that now
+ */
+ if (station->hs) {
+ handshake_state_unref(station->hs);
+ station->hs = NULL;
+ }
+
if (station->netconfig && !netconfig_load_settings(
station->netconfig,
network_get_settings(network)))
@@ -3747,6 +3769,7 @@ int __station_connect_network(struct station *station, struct network *network,
station->connected_bss = bss;
station->connected_network = network;
+ station->hs = handshake_state_ref(hs);
if (station->state != state)
station_enter_state(station, state);
@@ -5039,6 +5062,11 @@ static void station_free(struct station *station)
l_queue_destroy(station->owe_hidden_scan_ids, NULL);
}
+ if (station->hs) {
+ handshake_state_unref(station->hs);
+ station->hs = NULL;
+ }
+
station_roam_state_clear(station);
l_queue_destroy(station->networks_sorted, NULL);
--
2.34.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 13/15] station: support PMKSA connections
2024-11-22 15:15 [PATCH 00/15] PMKSA support (SAE only) James Prestwood
` (11 preceding siblings ...)
2024-11-22 15:15 ` [PATCH 12/15] station: hold reference to handshake object James Prestwood
@ 2024-11-22 15:15 ` James Prestwood
2024-11-22 15:15 ` [PATCH 14/15] auto-t: add PMKSA tests James Prestwood
2024-11-22 15:15 ` [PATCH 15/15] doc: document DisablePMKSA option James Prestwood
14 siblings, 0 replies; 21+ messages in thread
From: James Prestwood @ 2024-11-22 15:15 UTC (permalink / raw)
To: iwd; +Cc: James Prestwood
The actual connection piece of this is very minimal, and only
requires station to check if there is a PMKSA cached, and if so
include the PMKID in the RSNE. Netdev then takes care of the rest.
The remainder of this patch is the error handling if a PMKSA
connection fails with INVALID_PMKID. In this case IWD should retry
the same BSS without PMKSA.
An option was also added to disable PMKSA if a user wants to do
that. In theory PMKSA is actually less secure compared to SAE so
it could be something a user wants to disable. Going forward though
it will be enabled by default as its a requirement from the WiFi
alliance for WPA3 certification.
---
src/station.c | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 53 insertions(+), 1 deletion(-)
diff --git a/src/station.c b/src/station.c
index 09193eed..5403c332 100644
--- a/src/station.c
+++ b/src/station.c
@@ -63,6 +63,7 @@
#include "src/eap.h"
#include "src/eap-tls-common.h"
#include "src/storage.h"
+#include "src/pmksa.h"
#define STATION_RECENT_NETWORK_LIMIT 5
#define STATION_RECENT_FREQS_LIMIT 5
@@ -78,6 +79,7 @@ static bool supports_drop_gratuitous_arp;
static bool supports_drop_unsolicited_na;
static bool supports_ipv4_drop_unicast_in_l2_multicast;
static bool supports_ipv6_drop_unicast_in_l2_multicast;
+static bool pmksa_disabled;
static struct watchlist event_watches;
static uint32_t known_networks_watch;
static uint32_t allowed_bands;
@@ -1157,6 +1159,7 @@ static int station_build_handshake_rsn(struct handshake_state *hs,
struct network *network,
struct scan_bss *bss)
{
+ struct netdev *netdev = netdev_find(hs->ifindex);
const struct l_settings *settings = iwd_get_config();
enum security security = network_get_security(network);
bool add_mde = false;
@@ -1167,6 +1170,7 @@ static int station_build_handshake_rsn(struct handshake_state *hs,
uint8_t *ap_ie;
bool disable_ocv;
enum band_freq band;
+ struct pmksa *pmksa;
memset(&info, 0, sizeof(info));
@@ -1300,6 +1304,17 @@ build_ie:
IE_CIPHER_IS_GCMP_CCMP(info.pairwise_ciphers))
info.extended_key_id = true;
+ if (IE_AKM_IS_SAE(info.akm_suites) && !pmksa_disabled) {
+ pmksa = pmksa_cache_get(netdev_get_address(netdev), bss->addr,
+ bss->ssid, bss->ssid_len,
+ info.akm_suites);
+ if (pmksa) {
+ handshake_state_set_pmksa(hs, pmksa);
+ info.num_pmkids = 1;
+ info.pmkids = hs->pmksa->pmkid;
+ }
+ }
+
/* RSN takes priority */
if (bss->rsne) {
ap_ie = bss->rsne;
@@ -3391,6 +3406,39 @@ try_next:
return station_try_next_bss(station);
}
+static bool station_pmksa_fallback(struct station *station, uint16_t status)
+{
+ /*
+ * IEEE 802.11-2020 12.6.10.3 Cached PMKSAs and RSNA key management
+ *
+ * "If the Authenticator does not have a PMKSA for the PMKIDs in the
+ * (re)association request or the AKM does not match, its behavior
+ * depends on how the PMKSA was established. If SAE authentication was
+ * used to establish the PMKSA, then the AP shall reject (re)association
+ * by sending a (Re)Association Response frame with status code
+ * STATUS_INVALID_PMKID. Note that this allows the non-AP STA to fall
+ * back to full SAE authentication to establish another PMKSA"
+ */
+ if (status != MMPDU_STATUS_CODE_INVALID_PMKID)
+ return false;
+
+ if (L_WARN_ON(!station->hs))
+ return false;
+
+ if (!IE_AKM_IS_SAE(station->hs->akm_suite) || !station->hs->have_pmksa)
+ return false;
+
+ /*
+ * Remove the PMKSA from the handshake and return true to re-try the
+ * same BSS without PMKSA.
+ */
+ handshake_state_remove_pmksa(station->hs);
+
+ station_debug_event(station, "pmksa-invalid-pmkid");
+
+ return true;
+}
+
/* A bit more concise for trying to fit these into 80 characters */
#define IS_TEMPORARY_STATUS(code) \
((code) == MMPDU_STATUS_CODE_DENIED_UNSUFFICIENT_BANDWIDTH || \
@@ -3414,7 +3462,7 @@ static bool station_retry_with_status(struct station *station,
if (IS_TEMPORARY_STATUS(status_code))
network_blacklist_add(station->connected_network,
station->connected_bss);
- else
+ else if (!station_pmksa_fallback(station, status_code))
blacklist_add_bss(station->connected_bss->addr);
iwd_notice(IWD_NOTICE_CONNECT_FAILED, "status: %u", status_code);
@@ -5830,6 +5878,10 @@ static int station_init(void)
&anqp_disabled))
anqp_disabled = true;
+ if (!l_settings_get_bool(iwd_get_config(), "General", "DisablePMKSA",
+ &pmksa_disabled))
+ pmksa_disabled = false;
+
if (!netconfig_enabled())
l_info("station: Network configuration is disabled.");
--
2.34.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 14/15] auto-t: add PMKSA tests
2024-11-22 15:15 [PATCH 00/15] PMKSA support (SAE only) James Prestwood
` (12 preceding siblings ...)
2024-11-22 15:15 ` [PATCH 13/15] station: support PMKSA connections James Prestwood
@ 2024-11-22 15:15 ` James Prestwood
2024-11-22 15:15 ` [PATCH 15/15] doc: document DisablePMKSA option James Prestwood
14 siblings, 0 replies; 21+ messages in thread
From: James Prestwood @ 2024-11-22 15:15 UTC (permalink / raw)
To: iwd; +Cc: James Prestwood
Adds a test for just PMKSA and testing expiration as well as includes
some PMKSA tests in the SAE roaming test to ensure FT/reassociation
works.
---
autotests/testPMKSA-SAE/connection_test.py | 114 +++++++++++++++++++++
autotests/testPMKSA-SAE/hw.conf | 7 ++
autotests/testPMKSA-SAE/ssidSAE.conf | 12 +++
autotests/testSAE-roam/connection_test.py | 60 ++++++++++-
4 files changed, 192 insertions(+), 1 deletion(-)
create mode 100644 autotests/testPMKSA-SAE/connection_test.py
create mode 100644 autotests/testPMKSA-SAE/hw.conf
create mode 100644 autotests/testPMKSA-SAE/ssidSAE.conf
diff --git a/autotests/testPMKSA-SAE/connection_test.py b/autotests/testPMKSA-SAE/connection_test.py
new file mode 100644
index 00000000..5bab3ff8
--- /dev/null
+++ b/autotests/testPMKSA-SAE/connection_test.py
@@ -0,0 +1,114 @@
+#!/usr/bin/python3
+
+import unittest
+import sys
+
+sys.path.append('../util')
+from iwd import IWD
+from iwd import PSKAgent
+from iwd import NetworkType
+from hostapd import HostapdCLI
+import testutil
+
+class Test(unittest.TestCase):
+
+ def validate_connection(self, wd, ssid, hostapd, expected_group):
+ psk_agent = PSKAgent("secret123")
+ wd.register_psk_agent(psk_agent)
+
+ devices = wd.list_devices(1)
+ self.assertIsNotNone(devices)
+ device = devices[0]
+
+ device.disconnect()
+
+ network = device.get_ordered_network(ssid, full_scan=True)
+
+ self.assertEqual(network.type, NetworkType.psk)
+
+ network.network_object.connect()
+
+ condition = 'obj.state == DeviceState.connected'
+ wd.wait_for_object_condition(device, condition)
+
+ wd.wait(2)
+
+ testutil.test_iface_operstate(intf=device.name)
+ testutil.test_ifaces_connected(if0=device.name, if1=hostapd.ifname)
+
+ # Initial connection PMKSA should not be used. So we should see the
+ # SAE group set.
+ sta_status = hostapd.sta_status(device.address)
+ self.assertEqual(int(sta_status["sae_group"]), expected_group)
+
+ device.disconnect()
+
+ condition = 'not obj.connected'
+ wd.wait_for_object_condition(network.network_object, condition)
+
+ wd.unregister_psk_agent(psk_agent)
+
+ network.network_object.connect(wait=False)
+
+ condition = 'obj.state == DeviceState.connected'
+ wd.wait_for_object_condition(device, condition)
+
+ wd.wait(2)
+
+ testutil.test_iface_operstate(intf=device.name)
+ testutil.test_ifaces_connected(if0=device.name, if1=hostapd.ifname)
+
+ # Having connected once prior we should have a PMKSA and SAE should not
+ # have been used.
+ sta_status = hostapd.sta_status(device.address)
+ self.assertNotIn("sae_group", sta_status.keys())
+
+ device.disconnect()
+
+ condition = 'not obj.connected'
+ wd.wait_for_object_condition(network.network_object, condition)
+
+ hostapd.pmksa_flush()
+
+ wd.wait(5)
+
+ network.network_object.connect()
+
+ device.wait_for_event("pmksa-invalid-pmkid")
+
+ condition = 'obj.state == DeviceState.connected'
+ wd.wait_for_object_condition(device, condition)
+
+ wd.wait(2)
+
+ testutil.test_iface_operstate(intf=device.name)
+ testutil.test_ifaces_connected(if0=device.name, if1=hostapd.ifname)
+
+ # Manually flushing the PMKSA from the AP then reconnecting we should
+ # have failed (INVALID_PMKID) then retried the same BSS with SAE, not
+ # PMKSA.
+ sta_status = hostapd.sta_status(device.address)
+ self.assertEqual(int(sta_status["sae_group"]), expected_group)
+
+ def test_pmksa_sae(self):
+ self.hostapd.wait_for_event("AP-ENABLED")
+ self.validate_connection(self.wd, "ssidSAE", self.hostapd, 19)
+
+ def setUp(self):
+ self.hostapd.default()
+ self.wd = IWD(True)
+
+ def tearDown(self):
+ self.wd.clear_storage()
+ self.wd = None
+
+ @classmethod
+ def setUpClass(cls):
+ cls.hostapd = HostapdCLI(config='ssidSAE.conf')
+
+ @classmethod
+ def tearDownClass(cls):
+ pass
+
+if __name__ == '__main__':
+ unittest.main(exit=True)
diff --git a/autotests/testPMKSA-SAE/hw.conf b/autotests/testPMKSA-SAE/hw.conf
new file mode 100644
index 00000000..72b161b8
--- /dev/null
+++ b/autotests/testPMKSA-SAE/hw.conf
@@ -0,0 +1,7 @@
+[SETUP]
+num_radios=2
+start_iwd=0
+hwsim_medium=yes
+
+[HOSTAPD]
+rad0=ssidSAE.conf
diff --git a/autotests/testPMKSA-SAE/ssidSAE.conf b/autotests/testPMKSA-SAE/ssidSAE.conf
new file mode 100644
index 00000000..377646b2
--- /dev/null
+++ b/autotests/testPMKSA-SAE/ssidSAE.conf
@@ -0,0 +1,12 @@
+hw_mode=g
+channel=1
+ssid=ssidSAE
+
+wpa=2
+wpa_key_mgmt=SAE
+wpa_pairwise=CCMP
+sae_password=secret123
+sae_groups=19
+ieee80211w=2
+sae_pwe=0
+rsn_preauth=1
diff --git a/autotests/testSAE-roam/connection_test.py b/autotests/testSAE-roam/connection_test.py
index ca7234a6..718bfd77 100644
--- a/autotests/testSAE-roam/connection_test.py
+++ b/autotests/testSAE-roam/connection_test.py
@@ -13,7 +13,7 @@ import testutil
from config import ctx
class Test(unittest.TestCase):
- def validate_connection(self, wd, ft=True):
+ def validate_connection(self, wd, ft=True, check_used_pmksa=False):
device = wd.list_devices(1)[0]
# This won't guarantee all BSS's are found, but at least ensures that
@@ -37,6 +37,14 @@ class Test(unittest.TestCase):
self.assertRaises(Exception, testutil.test_ifaces_connected,
(self.bss_hostapd[1].ifname, device.name, True, True))
+ # If PMKSA was used, hostapd should not include the sae_group key in
+ # its status for the station.
+ sta_status = self.bss_hostapd[0].sta_status(device.address)
+ if check_used_pmksa:
+ self.assertNotIn("sae_group", sta_status.keys())
+ else:
+ self.assertIn("sae_group", sta_status.keys())
+
device.roam(self.bss_hostapd[1].bssid)
# Check that iwd is on BSS 1 once out of roaming state and doesn't
@@ -88,6 +96,31 @@ class Test(unittest.TestCase):
self.validate_connection(wd, True)
+ def test_ft_roam_pmksa(self):
+ wd = IWD(True)
+
+ self.bss_hostapd[0].set_value('wpa_key_mgmt', 'FT-SAE SAE')
+ self.bss_hostapd[0].reload()
+ self.bss_hostapd[0].wait_for_event("AP-ENABLED")
+ self.bss_hostapd[1].set_value('wpa_key_mgmt', 'FT-SAE SAE')
+ self.bss_hostapd[1].reload()
+ self.bss_hostapd[1].wait_for_event("AP-ENABLED")
+ self.bss_hostapd[2].set_value('wpa_key_mgmt', 'FT-PSK')
+ self.bss_hostapd[2].reload()
+ self.bss_hostapd[2].wait_for_event("AP-ENABLED")
+
+ self.validate_connection(wd, True)
+
+ device = wd.list_devices(1)[0]
+ device.disconnect()
+
+ for hapd in self.bss_hostapd:
+ hapd.deauthenticate(device.address)
+
+ wd.wait(5)
+
+ self.validate_connection(wd, True, check_used_pmksa=True)
+
def test_reassociate_roam_success(self):
wd = IWD(True)
@@ -103,6 +136,31 @@ class Test(unittest.TestCase):
self.validate_connection(wd, False)
+ def test_reassociate_roam_pmksa(self):
+ wd = IWD(True)
+
+ self.bss_hostapd[0].set_value('wpa_key_mgmt', 'SAE')
+ self.bss_hostapd[0].reload()
+ self.bss_hostapd[0].wait_for_event("AP-ENABLED")
+ self.bss_hostapd[1].set_value('wpa_key_mgmt', 'SAE')
+ self.bss_hostapd[1].reload()
+ self.bss_hostapd[1].wait_for_event("AP-ENABLED")
+ self.bss_hostapd[2].set_value('wpa_key_mgmt', 'WPA-PSK')
+ self.bss_hostapd[2].reload()
+ self.bss_hostapd[2].wait_for_event("AP-ENABLED")
+
+ self.validate_connection(wd, False)
+
+ device = wd.list_devices(1)[0]
+ device.disconnect()
+
+ for hapd in self.bss_hostapd:
+ hapd.deauthenticate(device.address)
+
+ wd.wait(5)
+
+ self.validate_connection(wd, False, check_used_pmksa=True)
+
def tearDown(self):
os.system('ip link set "' + self.bss_hostapd[0].ifname + '" down')
os.system('ip link set "' + self.bss_hostapd[1].ifname + '" down')
--
2.34.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* [PATCH 15/15] doc: document DisablePMKSA option
2024-11-22 15:15 [PATCH 00/15] PMKSA support (SAE only) James Prestwood
` (13 preceding siblings ...)
2024-11-22 15:15 ` [PATCH 14/15] auto-t: add PMKSA tests James Prestwood
@ 2024-11-22 15:15 ` James Prestwood
14 siblings, 0 replies; 21+ messages in thread
From: James Prestwood @ 2024-11-22 15:15 UTC (permalink / raw)
To: iwd; +Cc: James Prestwood
---
src/iwd.config.rst | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/src/iwd.config.rst b/src/iwd.config.rst
index 1cb4b05d..7530aae0 100644
--- a/src/iwd.config.rst
+++ b/src/iwd.config.rst
@@ -225,6 +225,11 @@ The group ``[General]`` contains general settings.
request is just a 'hint' and ultimately left up to the kernel to set the
country.
+ * - DisablePMKSA
+ - Value: **false**, true
+
+ Disable PMKSA support in IWD
+
Network
-------
--
2.34.1
^ permalink raw reply related [flat|nested] 21+ messages in thread
* Re: [PATCH 09/15] handshake: Add pmksa setter & stealer
2024-11-22 15:15 ` [PATCH 09/15] handshake: Add pmksa setter & stealer James Prestwood
@ 2024-11-25 14:56 ` Denis Kenzior
2024-11-25 15:01 ` James Prestwood
0 siblings, 1 reply; 21+ messages in thread
From: Denis Kenzior @ 2024-11-25 14:56 UTC (permalink / raw)
To: James Prestwood, iwd
Hi James,
On 11/22/24 9:15 AM, James Prestwood wrote:
> From: Denis Kenzior <denkenz@gmail.com>
>
> The majority of this patch was authored by Denis Kenzior, but
> I have appended setting the PMK inside handshake_state_set_pmksa
> as well as checking if the pmkid exists in
> handshake_state_steal_pmkid.
>
> Authored-by: Denis Kenzior <denkenz@gmail.com>
> Authored-by: James Prestwood <prestwoj@gmail.com>
> ---
> Makefile.am | 4 +++
> src/handshake.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++
> src/handshake.h | 11 ++++++-
> 3 files changed, 91 insertions(+), 1 deletion(-)
>
<snip>
> @@ -141,7 +142,12 @@ struct handshake_state {
> bool supplicant_ocvc : 1;
> bool ext_key_id_capable : 1;
> bool force_default_ecc_group : 1;
> - uint8_t ssid[SSID_MAX_SIZE];
> + bool have_pmksa : 1;
> + union {
> + struct pmksa *pmksa;
> + uint64_t expiration;
> + };
> + uint8_t ssid[32];
I changed this back to using SSID_MAX_SIZE instead of the magic number.
> size_t ssid_len;
> char *passphrase;
> char *password_identifier;
All 15 patches applied, thanks.
Regards,
-Denis
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 09/15] handshake: Add pmksa setter & stealer
2024-11-25 14:56 ` Denis Kenzior
@ 2024-11-25 15:01 ` James Prestwood
2024-11-25 19:25 ` Bryce Johnson
0 siblings, 1 reply; 21+ messages in thread
From: James Prestwood @ 2024-11-25 15:01 UTC (permalink / raw)
To: Denis Kenzior, iwd; +Cc: Bryce Johnson
Hi Denis,
On 11/25/24 6:56 AM, Denis Kenzior wrote:
> Hi James,
>
> On 11/22/24 9:15 AM, James Prestwood wrote:
>> From: Denis Kenzior <denkenz@gmail.com>
>>
>> The majority of this patch was authored by Denis Kenzior, but
>> I have appended setting the PMK inside handshake_state_set_pmksa
>> as well as checking if the pmkid exists in
>> handshake_state_steal_pmkid.
>>
>> Authored-by: Denis Kenzior <denkenz@gmail.com>
>> Authored-by: James Prestwood <prestwoj@gmail.com>
>> ---
>> Makefile.am | 4 +++
>> src/handshake.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++
>> src/handshake.h | 11 ++++++-
>> 3 files changed, 91 insertions(+), 1 deletion(-)
>>
>
> <snip>
>
>> @@ -141,7 +142,12 @@ struct handshake_state {
>> bool supplicant_ocvc : 1;
>> bool ext_key_id_capable : 1;
>> bool force_default_ecc_group : 1;
>> - uint8_t ssid[SSID_MAX_SIZE];
>> + bool have_pmksa : 1;
>> + union {
>> + struct pmksa *pmksa;
>> + uint64_t expiration;
>> + };
>> + uint8_t ssid[32];
>
> I changed this back to using SSID_MAX_SIZE instead of the magic number.
>
>> size_t ssid_len;
>> char *passphrase;
>> char *password_identifier;
>
> All 15 patches applied, thanks.
Thanks!
Bryce, I know you were interested in this. It would be good to see if
this allows IWD to pass certification with the wifi alliance. Let us
know if you run into any more problems on that front.
Thanks,
James
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 09/15] handshake: Add pmksa setter & stealer
2024-11-25 15:01 ` James Prestwood
@ 2024-11-25 19:25 ` Bryce Johnson
2024-11-25 19:49 ` James Prestwood
0 siblings, 1 reply; 21+ messages in thread
From: Bryce Johnson @ 2024-11-25 19:25 UTC (permalink / raw)
To: James Prestwood; +Cc: Denis Kenzior, iwd
Hi James,
Thanks! We will running another product through wifi certification in
the next 3-6 months with a new chipset from TI (cc3301). I'll check
with TI to see if there is a pretest so we aren't running into issues
at the lab. We got our last product certified reverting back to
wpa_supplicant just before the wifi alliance pushed out the latest
changes/requirements.
Also annoying the cheaper automatic testing uses wpa_suppliant pretty
integrated, hopefully wifi alliance will create a way to do that with
IWD in the same way if enough people are using it.
If anyone got a product through certification I would be interested in
any experiences with IWD!
Bryce
On Mon, Nov 25, 2024 at 8:01 AM James Prestwood <prestwoj@gmail.com> wrote:
>
> Hi Denis,
>
> On 11/25/24 6:56 AM, Denis Kenzior wrote:
> > Hi James,
> >
> > On 11/22/24 9:15 AM, James Prestwood wrote:
> >> From: Denis Kenzior <denkenz@gmail.com>
> >>
> >> The majority of this patch was authored by Denis Kenzior, but
> >> I have appended setting the PMK inside handshake_state_set_pmksa
> >> as well as checking if the pmkid exists in
> >> handshake_state_steal_pmkid.
> >>
> >> Authored-by: Denis Kenzior <denkenz@gmail.com>
> >> Authored-by: James Prestwood <prestwoj@gmail.com>
> >> ---
> >> Makefile.am | 4 +++
> >> src/handshake.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++
> >> src/handshake.h | 11 ++++++-
> >> 3 files changed, 91 insertions(+), 1 deletion(-)
> >>
> >
> > <snip>
> >
> >> @@ -141,7 +142,12 @@ struct handshake_state {
> >> bool supplicant_ocvc : 1;
> >> bool ext_key_id_capable : 1;
> >> bool force_default_ecc_group : 1;
> >> - uint8_t ssid[SSID_MAX_SIZE];
> >> + bool have_pmksa : 1;
> >> + union {
> >> + struct pmksa *pmksa;
> >> + uint64_t expiration;
> >> + };
> >> + uint8_t ssid[32];
> >
> > I changed this back to using SSID_MAX_SIZE instead of the magic number.
> >
> >> size_t ssid_len;
> >> char *passphrase;
> >> char *password_identifier;
> >
> > All 15 patches applied, thanks.
>
> Thanks!
>
> Bryce, I know you were interested in this. It would be good to see if
> this allows IWD to pass certification with the wifi alliance. Let us
> know if you run into any more problems on that front.
>
> Thanks,
>
> James
>
>
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 09/15] handshake: Add pmksa setter & stealer
2024-11-25 19:25 ` Bryce Johnson
@ 2024-11-25 19:49 ` James Prestwood
2024-11-25 20:18 ` Bryce Johnson
0 siblings, 1 reply; 21+ messages in thread
From: James Prestwood @ 2024-11-25 19:49 UTC (permalink / raw)
To: Bryce Johnson; +Cc: Denis Kenzior, iwd
Hi Bryce,
On 11/25/24 11:25 AM, Bryce Johnson wrote:
> Hi James,
> Thanks! We will running another product through wifi certification in
> the next 3-6 months with a new chipset from TI (cc3301). I'll check
> with TI to see if there is a pretest so we aren't running into issues
> at the lab. We got our last product certified reverting back to
> wpa_supplicant just before the wifi alliance pushed out the latest
> changes/requirements.
Great, eager to hear how it goes.
>
> Also annoying the cheaper automatic testing uses wpa_suppliant pretty
> integrated, hopefully wifi alliance will create a way to do that with
> IWD in the same way if enough people are using it.
I'd be interested to see what they are doing with this. If they are
letting wpa_supplicant act autonomously and triggering things on the AP
side, or if they are using the CLI to interact with wpa_supplicant
directly for "force" it to exercise some of the features they're testing.
>
> If anyone got a product through certification I would be interested in
> any experiences with IWD!
>
>
> Bryce
>
> On Mon, Nov 25, 2024 at 8:01 AM James Prestwood <prestwoj@gmail.com> wrote:
>> Hi Denis,
>>
>> On 11/25/24 6:56 AM, Denis Kenzior wrote:
>>> Hi James,
>>>
>>> On 11/22/24 9:15 AM, James Prestwood wrote:
>>>> From: Denis Kenzior <denkenz@gmail.com>
>>>>
>>>> The majority of this patch was authored by Denis Kenzior, but
>>>> I have appended setting the PMK inside handshake_state_set_pmksa
>>>> as well as checking if the pmkid exists in
>>>> handshake_state_steal_pmkid.
>>>>
>>>> Authored-by: Denis Kenzior <denkenz@gmail.com>
>>>> Authored-by: James Prestwood <prestwoj@gmail.com>
>>>> ---
>>>> Makefile.am | 4 +++
>>>> src/handshake.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++
>>>> src/handshake.h | 11 ++++++-
>>>> 3 files changed, 91 insertions(+), 1 deletion(-)
>>>>
>>> <snip>
>>>
>>>> @@ -141,7 +142,12 @@ struct handshake_state {
>>>> bool supplicant_ocvc : 1;
>>>> bool ext_key_id_capable : 1;
>>>> bool force_default_ecc_group : 1;
>>>> - uint8_t ssid[SSID_MAX_SIZE];
>>>> + bool have_pmksa : 1;
>>>> + union {
>>>> + struct pmksa *pmksa;
>>>> + uint64_t expiration;
>>>> + };
>>>> + uint8_t ssid[32];
>>> I changed this back to using SSID_MAX_SIZE instead of the magic number.
>>>
>>>> size_t ssid_len;
>>>> char *passphrase;
>>>> char *password_identifier;
>>> All 15 patches applied, thanks.
>> Thanks!
>>
>> Bryce, I know you were interested in this. It would be good to see if
>> this allows IWD to pass certification with the wifi alliance. Let us
>> know if you run into any more problems on that front.
>>
>> Thanks,
>>
>> James
>>
>>
^ permalink raw reply [flat|nested] 21+ messages in thread
* Re: [PATCH 09/15] handshake: Add pmksa setter & stealer
2024-11-25 19:49 ` James Prestwood
@ 2024-11-25 20:18 ` Bryce Johnson
0 siblings, 0 replies; 21+ messages in thread
From: Bryce Johnson @ 2024-11-25 20:18 UTC (permalink / raw)
To: James Prestwood; +Cc: Denis Kenzior, iwd
Hi James,
> >
> > Also annoying the cheaper automatic testing uses wpa_suppliant pretty
> > integrated, hopefully wifi alliance will create a way to do that with
> > IWD in the same way if enough people are using it.
> I'd be interested to see what they are doing with this. If they are
> letting wpa_supplicant act autonomously and triggering things on the AP
> side, or if they are using the CLI to interact with wpa_supplicant
> directly for "force" it to exercise some of the features they're testing.
I looked into it a year or so ago - a bunch of it is hidden behind the
wifi alliance paywall... but they have some examples,
https://github.com/Wi-FiTestSuite/Wi-FiTestSuite-Linux-DUT - looks
like using wpa_cli in some cases.
Also: Mentioned in the doc/INSTALL for the DUT agent
3. Running the software
3.1 DUT -- dut/wfa_dut
a. The current implementation is based on the open source wpa_supplicant
(hostap.epitest.fi/wpa_supplicant). The DUT must have this installed.
The test lab had to run the tests "manually" when I tried to use IWD
to get it certified before.
Bryce
^ permalink raw reply [flat|nested] 21+ messages in thread
end of thread, other threads:[~2024-11-25 20:18 UTC | newest]
Thread overview: 21+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-11-22 15:15 [PATCH 00/15] PMKSA support (SAE only) James Prestwood
2024-11-22 15:15 ` [PATCH 01/15] handshake: add ref counting to handshake_state James Prestwood
2024-11-22 15:15 ` [PATCH 02/15] unit: update use of handshake_state with ref/unref James Prestwood
2024-11-22 15:15 ` [PATCH 03/15] auto-t: always initialize StationDebug in Device class James Prestwood
2024-11-22 15:15 ` [PATCH 04/15] auto-t: add pmksa_flush() to hostapd module James Prestwood
2024-11-22 15:15 ` [PATCH 05/15] auto-t: update testSAE to disable PMKSA James Prestwood
2024-11-22 15:15 ` [PATCH 06/15] pmksa: Add skeleton James Prestwood
2024-11-22 15:15 ` [PATCH 07/15] unit: Add basic pmksa test James Prestwood
2024-11-22 15:15 ` [PATCH 08/15] pmksa: Add debugging James Prestwood
2024-11-22 15:15 ` [PATCH 09/15] handshake: Add pmksa setter & stealer James Prestwood
2024-11-25 14:56 ` Denis Kenzior
2024-11-25 15:01 ` James Prestwood
2024-11-25 19:25 ` Bryce Johnson
2024-11-25 19:49 ` James Prestwood
2024-11-25 20:18 ` Bryce Johnson
2024-11-22 15:15 ` [PATCH 10/15] handshake: add handshake_state_remove_pmksa James Prestwood
2024-11-22 15:15 ` [PATCH 11/15] netdev: add support to use PMKSA over SAE if available James Prestwood
2024-11-22 15:15 ` [PATCH 12/15] station: hold reference to handshake object James Prestwood
2024-11-22 15:15 ` [PATCH 13/15] station: support PMKSA connections James Prestwood
2024-11-22 15:15 ` [PATCH 14/15] auto-t: add PMKSA tests James Prestwood
2024-11-22 15:15 ` [PATCH 15/15] doc: document DisablePMKSA option James Prestwood
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox