iwd.lists.linux.dev archive mirror
 help / color / mirror / Atom feed
* [PATCH v2 1/4] eapol: implement rekey support for authenticator
@ 2023-01-12 19:32 James Prestwood
  2023-01-12 19:32 ` [PATCH v2 2/4] eapol: detect message 2/4 retransmits James Prestwood
                   ` (3 more replies)
  0 siblings, 4 replies; 8+ messages in thread
From: James Prestwood @ 2023-01-12 19:32 UTC (permalink / raw)
  To: iwd; +Cc: James Prestwood

The only changes required was to set the secure bit for message 1,
reset the frame retry counter, and change the 2/4 verifier to use
the rekey flag rather than ptk_complete. This is because we must
set ptk_complete false in order to detect retransmissions of the
4/4 frame.

Initiating a rekey can now be done by simply calling eapol_start().
---
 src/eapol.c | 14 +++++++++++---
 1 file changed, 11 insertions(+), 3 deletions(-)

diff --git a/src/eapol.c b/src/eapol.c
index 22b2d5d1..2048a87d 100644
--- a/src/eapol.c
+++ b/src/eapol.c
@@ -1086,8 +1086,6 @@ static void eapol_send_ptk_1_of_4(struct eapol_sm *sm)
 
 	handshake_state_new_anonce(sm->handshake);
 
-	sm->handshake->ptk_complete = false;
-
 	sm->replay_counter++;
 
 	memset(ek, 0, EAPOL_FRAME_LEN(sm->mic_len));
@@ -1111,6 +1109,12 @@ static void eapol_send_ptk_1_of_4(struct eapol_sm *sm)
 
 	eapol_key_data_append(ek, sm->mic_len, HANDSHAKE_KDE_PMKID, pmkid, 16);
 
+	if (sm->handshake->ptk_complete) {
+		ek->secure = true;
+		sm->rekey = true;
+		sm->handshake->ptk_complete = false;
+	}
+
 	ek->header.packet_len = L_CPU_TO_BE16(EAPOL_FRAME_LEN(sm->mic_len) +
 				EAPOL_KEY_DATA_LEN(ek, sm->mic_len) - 4);
 
@@ -1589,7 +1593,7 @@ static void eapol_handle_ptk_2_of_4(struct eapol_sm *sm,
 
 	l_debug("ifindex=%u", sm->handshake->ifindex);
 
-	if (!eapol_verify_ptk_2_of_4(ek, sm->handshake->ptk_complete))
+	if (!eapol_verify_ptk_2_of_4(ek, sm->rekey))
 		return;
 
 	if (L_BE64_TO_CPU(ek->key_replay_counter) != sm->replay_counter)
@@ -2482,6 +2486,8 @@ static void eapol_eap_complete_cb(enum eap_result result, void *user_data)
 
 		/* sm->mic_len will have been set in eapol_eap_results_cb */
 
+		sm->frame_retry = 0;
+
 		/* Kick off 4-Way Handshake */
 		eapol_ptk_1_of_4_retry(NULL, sm);
 	}
@@ -2873,6 +2879,8 @@ bool eapol_start(struct eapol_sm *sm)
 			if (L_WARN_ON(!sm->handshake->have_pmk))
 				return false;
 
+			sm->frame_retry = 0;
+
 			/* Kick off handshake */
 			eapol_ptk_1_of_4_retry(NULL, sm);
 		}
-- 
2.34.3


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH v2 2/4] eapol: detect message 2/4 retransmits
  2023-01-12 19:32 [PATCH v2 1/4] eapol: implement rekey support for authenticator James Prestwood
@ 2023-01-12 19:32 ` James Prestwood
  2023-01-13 15:16   ` Denis Kenzior
  2023-01-12 19:32 ` [PATCH v2 3/4] ap: support PTK rekeys James Prestwood
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 8+ messages in thread
From: James Prestwood @ 2023-01-12 19:32 UTC (permalink / raw)
  To: iwd; +Cc: James Prestwood

If the authenticator has already set an snonce then the packet must
be a retransmit. Handle this by sending 3/4 again but making sure
to not reset the frame counter.
---
 src/eapol.c | 10 ++++++++--
 1 file changed, 8 insertions(+), 2 deletions(-)

diff --git a/src/eapol.c b/src/eapol.c
index 2048a87d..c1ef8f90 100644
--- a/src/eapol.c
+++ b/src/eapol.c
@@ -1645,12 +1645,18 @@ static void eapol_handle_ptk_2_of_4(struct eapol_sm *sm,
 		sm->handshake->support_ip_allocation = ip_req_kde != NULL;
 	}
 
+	/*
+	 * If the snonce is already set don't reset the retry counter as this
+	 * is a rekey. To be safe take the most recent snonce (in this frame)
+	 * in case the station created a new one.
+	 */
+	if (!sm->handshake->have_snonce)
+		sm->frame_retry = 0;
+
 	memcpy(sm->handshake->snonce, ek->key_nonce,
 			sizeof(sm->handshake->snonce));
 	sm->handshake->have_snonce = true;
 
-	sm->frame_retry = 0;
-
 	eapol_ptk_3_of_4_retry(NULL, sm);
 }
 
-- 
2.34.3


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH v2 3/4] ap: support PTK rekeys
  2023-01-12 19:32 [PATCH v2 1/4] eapol: implement rekey support for authenticator James Prestwood
  2023-01-12 19:32 ` [PATCH v2 2/4] eapol: detect message 2/4 retransmits James Prestwood
@ 2023-01-12 19:32 ` James Prestwood
  2023-01-13 15:35   ` Denis Kenzior
  2023-01-12 19:32 ` [PATCH v2 4/4] doc: Document RekeyTimeout for AP profiles James Prestwood
  2023-01-13 15:13 ` [PATCH v2 1/4] eapol: implement rekey support for authenticator Denis Kenzior
  3 siblings, 1 reply; 8+ messages in thread
From: James Prestwood @ 2023-01-12 19:32 UTC (permalink / raw)
  To: iwd; +Cc: James Prestwood

This adds support for rekeys to AP mode. A single timer is used and
reset to the next station needing a rekey. A default rekey timer of
600 seconds is used unless the profile sets a timeout.
---
 src/ap.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 114 insertions(+)

diff --git a/src/ap.c b/src/ap.c
index 1d937103..ef819724 100644
--- a/src/ap.c
+++ b/src/ap.c
@@ -60,6 +60,8 @@
 #include "src/band.h"
 #include "src/common.h"
 
+#define AP_DEFAULT_REKEY_SECONDS 600
+
 struct ap_state {
 	struct netdev *netdev;
 	struct l_genl_family *nl80211;
@@ -106,6 +108,9 @@ struct ap_state {
 	struct l_dbus_message *scan_pending;
 	struct l_queue *networks;
 
+	struct l_timeout *rekey_timeout;
+	unsigned int rekey_time;
+
 	bool started : 1;
 	bool gtk_set : 1;
 	bool netconfig_set_addr4 : 1;
@@ -137,6 +142,7 @@ struct sta_state {
 	bool wsc_v2;
 	struct l_dhcp_lease *ip_alloc_lease;
 	bool ip_alloc_sent;
+	uint64_t rekey_time;
 
 	bool ht_support : 1;
 	bool ht_greenfield : 1;
@@ -345,6 +351,11 @@ static void ap_reset(struct ap_state *ap)
 		l_queue_destroy(ap->networks, l_free);
 		ap->networks = NULL;
 	}
+
+	if (ap->rekey_timeout) {
+		l_timeout_remove(ap->rekey_timeout);
+		ap->rekey_timeout = NULL;
+	}
 }
 
 static bool ap_event_done(struct ap_state *ap, bool prev_in_event)
@@ -377,6 +388,8 @@ static bool ap_event(struct ap_state *ap, enum ap_event_type event,
 	return ap_event_done(ap, prev);
 }
 
+static void ap_reset_rekey_timeout(struct ap_state *ap);
+
 static void ap_del_station(struct sta_state *sta, uint16_t reason,
 				bool disassociate)
 {
@@ -439,6 +452,89 @@ static void ap_del_station(struct sta_state *sta, uint16_t reason,
 
 		ap_event_done(ap, prev);
 	}
+
+	ap_reset_rekey_timeout(ap);
+}
+
+static void ap_start_rekey(struct ap_state *ap, struct sta_state *sta)
+{
+	l_debug("Rekey STA "MAC, MAC_STR(sta->addr));
+
+	eapol_start(sta->sm);
+}
+
+static void ap_rekey_timeout(struct l_timeout *timeout, void *user_data)
+{
+	struct ap_state *ap = user_data;
+
+	l_timeout_remove(timeout);
+
+	ap_reset_rekey_timeout(ap);
+}
+
+/*
+ * Used to initiate any rekeys which are due and reset the rekey timer to the
+ * next soonest station needing a rekey.
+ *
+ * TODO: Could adapt this to also take into account the next GTK rekey and
+ * service that as well. But GTK rekeys are not yet supported in AP mode.
+ */
+static void ap_reset_rekey_timeout(struct ap_state *ap)
+{
+	const struct l_queue_entry *e;
+	uint64_t now = l_time_now();
+	uint64_t next = 0;
+
+	if (!ap->rekey_time)
+		return;
+
+	/* Find the station(s) that need a rekey and start it */
+	for (e = l_queue_get_entries(ap->sta_states); e; e = e->next) {
+		struct sta_state *sta = e->data;
+
+		if (!sta->associated || !sta->rsna)
+			continue;
+
+		if (l_time_before(now, sta->rekey_time)) {
+			uint64_t diff = l_time_diff(now, sta->rekey_time);
+
+			/* Finding the next rekey time */
+			if (next < diff)
+				next = diff;
+
+			continue;
+		}
+
+		ap_start_rekey(ap, sta);
+	}
+
+	/*
+	 * Set the next rekey to the station needing it the soonest, or NULL
+	 * if a single station and wait until the rekey is complete to reset
+	 * the timer.
+	 */
+	if (next)
+		ap->rekey_timeout = l_timeout_create(l_time_to_secs(next),
+						ap_rekey_timeout, ap, NULL);
+	else
+		ap->rekey_timeout = NULL;
+}
+
+static void ap_set_sta_rekey_timer(struct ap_state *ap, struct sta_state *sta)
+{
+	if (!ap->rekey_time)
+		return;
+
+	sta->rekey_time = l_time_now() + ap->rekey_time - 1;
+
+	/*
+	 * First/only station authenticated, set rekey timer. Any more stations
+	 * will just set their rekey time and be serviced by the single callback
+	 */
+	if (!ap->rekey_timeout)
+		ap->rekey_timeout = l_timeout_create(
+						l_time_to_secs(ap->rekey_time),
+						ap_rekey_timeout, ap, NULL);
 }
 
 static bool ap_sta_match_addr(const void *a, const void *b)
@@ -479,6 +575,8 @@ static void ap_new_rsna(struct sta_state *sta)
 
 	sta->rsna = true;
 
+	ap_set_sta_rekey_timer(ap, sta);
+
 	event_data.mac = sta->addr;
 	event_data.assoc_ies = sta->assoc_ies;
 	event_data.assoc_ies_len = sta->assoc_ies_len;
@@ -1372,6 +1470,9 @@ static void ap_handshake_event(struct handshake_state *hs,
 		sta->hs->go_ip_addr = IP4_FROM_STR(own_addr_str);
 		break;
 	}
+	case HANDSHAKE_EVENT_REKEY_COMPLETE:
+		ap_set_sta_rekey_timer(ap, sta);
+		return;
 	default:
 		break;
 	}
@@ -3628,6 +3729,19 @@ static int ap_load_config(struct ap_state *ap, const struct l_settings *config,
 		l_strfreev(strvval);
 	}
 
+	if (l_settings_has_key(config, "General", "RekeyTimeout")) {
+		unsigned int uintval;
+
+		if (!l_settings_get_uint(config, "General",
+						"RekeyTimeout", &uintval)) {
+			l_error("AP [General].RekeyTimeout is not valid");
+			return -EINVAL;
+		}
+
+		ap->rekey_time = uintval * L_USEC_PER_SEC;
+	} else
+		ap->rekey_time = AP_DEFAULT_REKEY_SECONDS * L_USEC_PER_SEC;
+
 	/*
 	 * Since 5GHz won't ever support only CCK rates we can ignore this
 	 * setting on that band.
-- 
2.34.3


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* [PATCH v2 4/4] doc: Document RekeyTimeout for AP profiles
  2023-01-12 19:32 [PATCH v2 1/4] eapol: implement rekey support for authenticator James Prestwood
  2023-01-12 19:32 ` [PATCH v2 2/4] eapol: detect message 2/4 retransmits James Prestwood
  2023-01-12 19:32 ` [PATCH v2 3/4] ap: support PTK rekeys James Prestwood
@ 2023-01-12 19:32 ` James Prestwood
  2023-01-13 15:19   ` Denis Kenzior
  2023-01-13 15:13 ` [PATCH v2 1/4] eapol: implement rekey support for authenticator Denis Kenzior
  3 siblings, 1 reply; 8+ messages in thread
From: James Prestwood @ 2023-01-12 19:32 UTC (permalink / raw)
  To: iwd; +Cc: James Prestwood

---
 src/iwd.ap.rst | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/src/iwd.ap.rst b/src/iwd.ap.rst
index 823aba99..ce402f91 100644
--- a/src/iwd.ap.rst
+++ b/src/iwd.ap.rst
@@ -67,6 +67,13 @@ The group ``[General]`` contains general AP configuration.
        ensure the country is set, and that the desired frequency/channel is
        unrestricted.
 
+   * - RekeyTimeout
+     - Timeout for PTK rekeys (seconds)
+
+       The time interval at which the AP starts a rekey for a given station. If
+       not provided a default value of 600 seconds is used. A value of 0 will
+       disable PTK rekeys completely.
+
 Network Authentication Settings
 -------------------------------
 
-- 
2.34.3


^ permalink raw reply related	[flat|nested] 8+ messages in thread

* Re: [PATCH v2 1/4] eapol: implement rekey support for authenticator
  2023-01-12 19:32 [PATCH v2 1/4] eapol: implement rekey support for authenticator James Prestwood
                   ` (2 preceding siblings ...)
  2023-01-12 19:32 ` [PATCH v2 4/4] doc: Document RekeyTimeout for AP profiles James Prestwood
@ 2023-01-13 15:13 ` Denis Kenzior
  3 siblings, 0 replies; 8+ messages in thread
From: Denis Kenzior @ 2023-01-13 15:13 UTC (permalink / raw)
  To: James Prestwood, iwd

Hi James,

On 1/12/23 13:32, James Prestwood wrote:
> The only changes required was to set the secure bit for message 1,
> reset the frame retry counter, and change the 2/4 verifier to use
> the rekey flag rather than ptk_complete. This is because we must
> set ptk_complete false in order to detect retransmissions of the
> 4/4 frame.
> 
> Initiating a rekey can now be done by simply calling eapol_start().
> ---
>   src/eapol.c | 14 +++++++++++---
>   1 file changed, 11 insertions(+), 3 deletions(-)
> 

<snip>

> @@ -1111,6 +1109,12 @@ static void eapol_send_ptk_1_of_4(struct eapol_sm *sm)
>   
>   	eapol_key_data_append(ek, sm->mic_len, HANDSHAKE_KDE_PMKID, pmkid, 16);
>   
> +	if (sm->handshake->ptk_complete) {
> +		ek->secure = true;
> +		sm->rekey = true;
> +		sm->handshake->ptk_complete = false;
> +	}
> +

Hmm, shouldn't ek->secure always be set to sm->rekey?  I'm thinking of 
retransmissions.  Lets say we start a rekey with eapol_start().  ptk_complete is 
true, so the first transmit of the 1/4 packet will set ek->secure to true.  But 
on subsequent retransmissions, this if() statement won't be hit due to 
ptk_complete being false.  So ek->secure won't be set properly, no?

>   	ek->header.packet_len = L_CPU_TO_BE16(EAPOL_FRAME_LEN(sm->mic_len) +
>   				EAPOL_KEY_DATA_LEN(ek, sm->mic_len) - 4);
>   

Regards,
-Denis

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2 2/4] eapol: detect message 2/4 retransmits
  2023-01-12 19:32 ` [PATCH v2 2/4] eapol: detect message 2/4 retransmits James Prestwood
@ 2023-01-13 15:16   ` Denis Kenzior
  0 siblings, 0 replies; 8+ messages in thread
From: Denis Kenzior @ 2023-01-13 15:16 UTC (permalink / raw)
  To: James Prestwood, iwd

Hi James,

On 1/12/23 13:32, James Prestwood wrote:
> If the authenticator has already set an snonce then the packet must
> be a retransmit. Handle this by sending 3/4 again but making sure
> to not reset the frame counter.
> ---
>   src/eapol.c | 10 ++++++++--
>   1 file changed, 8 insertions(+), 2 deletions(-)
> 

Applied, thanks.

Regards,
-Denis


^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2 4/4] doc: Document RekeyTimeout for AP profiles
  2023-01-12 19:32 ` [PATCH v2 4/4] doc: Document RekeyTimeout for AP profiles James Prestwood
@ 2023-01-13 15:19   ` Denis Kenzior
  0 siblings, 0 replies; 8+ messages in thread
From: Denis Kenzior @ 2023-01-13 15:19 UTC (permalink / raw)
  To: James Prestwood, iwd

Hi James,

On 1/12/23 13:32, James Prestwood wrote:
> ---
>   src/iwd.ap.rst | 7 +++++++
>   1 file changed, 7 insertions(+)
> 
> diff --git a/src/iwd.ap.rst b/src/iwd.ap.rst
> index 823aba99..ce402f91 100644
> --- a/src/iwd.ap.rst
> +++ b/src/iwd.ap.rst
> @@ -67,6 +67,13 @@ The group ``[General]`` contains general AP configuration.
>          ensure the country is set, and that the desired frequency/channel is
>          unrestricted.
>   
> +   * - RekeyTimeout
> +     - Timeout for PTK rekeys (seconds)
> +
> +       The time interval at which the AP starts a rekey for a given station. If
> +       not provided a default value of 600 seconds is used. A value of 0 will
> +       disable PTK rekeys completely.

So the default should be 0 (disabled).  Many older Linux kernels can't support 
pairwise rekeys without (the potential) for leaking cleartext packets during the 
process.  iwd checks for the presence of NL80211_EXT_FEATURE_CAN_REPLACE_PTK0. 
If the driver isn't capable of this, we actually disconnect when the 
Authenticator requests a rekey.

> +
>   Network Authentication Settings
>   -------------------------------
>   

Regards,
-Denis

^ permalink raw reply	[flat|nested] 8+ messages in thread

* Re: [PATCH v2 3/4] ap: support PTK rekeys
  2023-01-12 19:32 ` [PATCH v2 3/4] ap: support PTK rekeys James Prestwood
@ 2023-01-13 15:35   ` Denis Kenzior
  0 siblings, 0 replies; 8+ messages in thread
From: Denis Kenzior @ 2023-01-13 15:35 UTC (permalink / raw)
  To: James Prestwood, iwd

Hi James,

On 1/12/23 13:32, James Prestwood wrote:
> This adds support for rekeys to AP mode. A single timer is used and
> reset to the next station needing a rekey. A default rekey timer of
> 600 seconds is used unless the profile sets a timeout.
> ---
>   src/ap.c | 114 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
>   1 file changed, 114 insertions(+)
> 

<snip>

> @@ -439,6 +452,89 @@ static void ap_del_station(struct sta_state *sta, uint16_t reason,
>   
>   		ap_event_done(ap, prev);
>   	}
> +
> +	ap_reset_rekey_timeout(ap);

Shouldn't you be cleaning up the timeout here?

> +}
> +
> +static void ap_start_rekey(struct ap_state *ap, struct sta_state *sta)
> +{
> +	l_debug("Rekey STA "MAC, MAC_STR(sta->addr));
> +
> +	eapol_start(sta->sm);
> +}
> +
> +static void ap_rekey_timeout(struct l_timeout *timeout, void *user_data)
> +{
> +	struct ap_state *ap = user_data;
> +
> +	l_timeout_remove(timeout);
> +
> +	ap_reset_rekey_timeout(ap);
> +}
> +
> +/*
> + * Used to initiate any rekeys which are due and reset the rekey timer to the
> + * next soonest station needing a rekey.
> + *
> + * TODO: Could adapt this to also take into account the next GTK rekey and
> + * service that as well. But GTK rekeys are not yet supported in AP mode.
> + */
> +static void ap_reset_rekey_timeout(struct ap_state *ap)
> +{
> +	const struct l_queue_entry *e;
> +	uint64_t now = l_time_now();
> +	uint64_t next = 0;
> +
> +	if (!ap->rekey_time)
> +		return;
> +
> +	/* Find the station(s) that need a rekey and start it */
> +	for (e = l_queue_get_entries(ap->sta_states); e; e = e->next) {
> +		struct sta_state *sta = e->data;
> +
> +		if (!sta->associated || !sta->rsna)
> +			continue;

Would checking sta->rekey_time == 0 also be worthwhile?  For stas that haven't 
authenticated yet?

> +
> +		if (l_time_before(now, sta->rekey_time)) {
> +			uint64_t diff = l_time_diff(now, sta->rekey_time);
> +
> +			/* Finding the next rekey time */
> +			if (next < diff)
> +				next = diff;
> +
> +			continue;

Ok, so you try to find the next soonest (absolute) rekey_time to schedule the 
next timeout.  Might be easier to just set next to ~0 and loop over the stations 
using l_time_before(sta->rekey_time, next), setting next as needed.

> +		}
> +
> +		ap_start_rekey(ap, sta);

And looks like this starts a rekey for any stations that we somehow missed?  How 
does this happen?

> +	}
> +
> +	/*
> +	 * Set the next rekey to the station needing it the soonest, or NULL
> +	 * if a single station and wait until the rekey is complete to reset
> +	 * the timer.
> +	 */
> +	if (next)
> +		ap->rekey_timeout = l_timeout_create(l_time_to_secs(next),
> +						ap_rekey_timeout, ap, NULL);
> +	else
> +		ap->rekey_timeout = NULL;

Are you sure the rekey_timeout is destroyed here?

Might be easier to use l_timeout_modify instead of creating/destroying it all 
the time?

> +}
> +
Regards,
-Denis

^ permalink raw reply	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2023-01-13 15:55 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-01-12 19:32 [PATCH v2 1/4] eapol: implement rekey support for authenticator James Prestwood
2023-01-12 19:32 ` [PATCH v2 2/4] eapol: detect message 2/4 retransmits James Prestwood
2023-01-13 15:16   ` Denis Kenzior
2023-01-12 19:32 ` [PATCH v2 3/4] ap: support PTK rekeys James Prestwood
2023-01-13 15:35   ` Denis Kenzior
2023-01-12 19:32 ` [PATCH v2 4/4] doc: Document RekeyTimeout for AP profiles James Prestwood
2023-01-13 15:19   ` Denis Kenzior
2023-01-13 15:13 ` [PATCH v2 1/4] eapol: implement rekey support for authenticator Denis Kenzior

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).