diff mbox series

[04/10] network: add support for SAE password identifiers

Message ID 20231205154647.1778389-4-prestwoj@gmail.com (mailing list archive)
State New
Headers show
Series [01/10] scan: parse password identifier/exclusive bits | expand

Checks

Context Check Description
tedd_an/pre-ci_am success Success
prestwoj/iwd-ci-gitlint success GitLint

Commit Message

James Prestwood Dec. 5, 2023, 3:46 p.m. UTC
Adds a new network profile setting [Security].PasswordIdentifier.
When set (and the BSS enables SAE password identifiers) the network
and handshake object will read this and use it for the SAE
exchange.

Loading the PSK will fail if there is no password identifier set
and the BSS sets the "exclusive" bit. If a password identifier is
set and the BSS doesn't indicate support the setting will be ignored
(with a debug print).
---
 src/network.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 49 insertions(+), 1 deletion(-)

Comments

Denis Kenzior Dec. 6, 2023, 5:08 p.m. UTC | #1
Hi James,

On 12/5/23 09:46, James Prestwood wrote:
> Adds a new network profile setting [Security].PasswordIdentifier.
> When set (and the BSS enables SAE password identifiers) the network
> and handshake object will read this and use it for the SAE
> exchange.
> 
> Loading the PSK will fail if there is no password identifier set
> and the BSS sets the "exclusive" bit. If a password identifier is

I'm not so sure about this.  The trouble is that this logic is sufficient for 
the initial connection, but isn't sufficient when you consider re-association.

> set and the BSS doesn't indicate support the setting will be ignored
> (with a debug print).
> ---
>   src/network.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++++-
>   1 file changed, 49 insertions(+), 1 deletion(-)
> 

<snip>

> @@ -641,6 +657,32 @@ static int network_load_psk(struct network *network, struct scan_bss *bss)
>   		psk_len = 0;
>   	}
>   
> +	/*
> +	 * Sort out if the password identifier is required, should be used, "
> +	 * or should be ignored.
> +	 */
> +	if (is_sae) {
> +		if (bss->sae_pw_id_exclusive && !password_id) {

This likely needs to be taken into consideration much later, when building the 
actual handshake state.

> +			l_error("BSS requires SAE password identifiers, check "
> +				"[Security].PasswordIdentifier");
> +			return -ENOKEY;
> +		}
> +
> +		/*
> +		 * If the profile contains a password identifier but the network
> +		 * does not support it IWD will still attempt to connect. The

Password identifier is only used by SAE H2E.  One can easily imagine a weird 
network of mixed APs with some being SAE H2E, some not.  This is why I didn't 
bother implementing this, it is a half-baked feature.

> +		 * caveat here is if the connection is successful the sync will
> +		 * remove the password identifier entry. Though this might be
> +		 * unexpected to the user, retaining this (invalid) setting
> +		 * isn't worth special casing.

And this doesn't sound nice at all...  I think the setting should be preserved.

> +		 */
> +		if (!bss->sae_pw_id_used && password_id) {
> +			l_debug("[Security].PasswordIdentifier set but BSS "
> +				"does not not use password identifiers");
> +			l_free(l_steal_ptr(password_id));
> +		}
> +	}
> +
>   	/* PSK can be generated from the passphrase but not the other way */
>   	if (!psk || is_sae) {
>   		if (!passphrase)

Regards,
-Denis
James Prestwood Dec. 6, 2023, 6:44 p.m. UTC | #2
Hi Denis,

On 12/6/23 09:08, Denis Kenzior wrote:
> Hi James,
>
> On 12/5/23 09:46, James Prestwood wrote:
>> Adds a new network profile setting [Security].PasswordIdentifier.
>> When set (and the BSS enables SAE password identifiers) the network
>> and handshake object will read this and use it for the SAE
>> exchange.
>>
>> Loading the PSK will fail if there is no password identifier set
>> and the BSS sets the "exclusive" bit. If a password identifier is
>
> I'm not so sure about this.  The trouble is that this logic is 
> sufficient for the initial connection, but isn't sufficient when you 
> consider re-association.
Your right, roaming would be entirely broken between BSS's that mismatch 
using password identifiers. Maybe even hunt-and-peck and H2E? not 
entirely sure. We would need to re-derive the point for each roam, like 
in network_set_handshake_secrets_psk().
>
>> set and the BSS doesn't indicate support the setting will be ignored
>> (with a debug print).
>> ---
>>   src/network.c | 50 +++++++++++++++++++++++++++++++++++++++++++++++++-
>>   1 file changed, 49 insertions(+), 1 deletion(-)
>>
>
> <snip>
>
>> @@ -641,6 +657,32 @@ static int network_load_psk(struct network 
>> *network, struct scan_bss *bss)
>>           psk_len = 0;
>>       }
>>   +    /*
>> +     * Sort out if the password identifier is required, should be 
>> used, "
>> +     * or should be ignored.
>> +     */
>> +    if (is_sae) {
>> +        if (bss->sae_pw_id_exclusive && !password_id) {
>
> This likely needs to be taken into consideration much later, when 
> building the actual handshake state.

Yeah, we'd need to move this into network_set_handshake_secrets_psk and 
rederive the points. And actually if we do this storing the points in 
the network profile doesn't make a whole lot of sense anymore since its 
being rederived every time.

Alternatively we just keep it how I have it and tell they user they're 
network isn't configured properly :)

>
>> +            l_error("BSS requires SAE password identifiers, check "
>> +                "[Security].PasswordIdentifier");
>> +            return -ENOKEY;
>> +        }
>> +
>> +        /*
>> +         * If the profile contains a password identifier but the 
>> network
>> +         * does not support it IWD will still attempt to connect. The
>
> Password identifier is only used by SAE H2E.  One can easily imagine a 
> weird network of mixed APs with some being SAE H2E, some not.  This is 
> why I didn't bother implementing this, it is a half-baked feature.

Ugh, ok. I guess we have to keep the identifier around then.

>
>> +         * caveat here is if the connection is successful the sync will
>> +         * remove the password identifier entry. Though this might be
>> +         * unexpected to the user, retaining this (invalid) setting
>> +         * isn't worth special casing.
>
> And this doesn't sound nice at all...  I think the setting should be 
> preserved.
>
>> +         */
>> +        if (!bss->sae_pw_id_used && password_id) {
>> +            l_debug("[Security].PasswordIdentifier set but BSS "
>> +                "does not not use password identifiers");
>> +            l_free(l_steal_ptr(password_id));
>> +        }
>> +    }
>> +
>>       /* PSK can be generated from the passphrase but not the other 
>> way */
>>       if (!psk || is_sae) {
>>           if (!passphrase)
>
> Regards,
> -Denis
>
Denis Kenzior Dec. 6, 2023, 7:44 p.m. UTC | #3
Hi James,

>>> Loading the PSK will fail if there is no password identifier set
>>> and the BSS sets the "exclusive" bit. If a password identifier is
>>
>> I'm not so sure about this.  The trouble is that this logic is sufficient for 
>> the initial connection, but isn't sufficient when you consider re-association.
> Your right, roaming would be entirely broken between BSS's that mismatch using 
> password identifiers. Maybe even hunt-and-peck and H2E? not entirely sure. We 

Well, ReAssociate would just use SAE passphrase directly, so it would work in 
theory...  But it is a bit of a strange case.

> would need to re-derive the point for each roam, like in 
> network_set_handshake_secrets_psk().

??  You mean SAE-H2E with password identifier for BSSes that report 
exclusive/in-use bit and SAE-H2E for BSSes without?  Or something else?

>>
>> This likely needs to be taken into consideration much later, when building the 
>> actual handshake state.
> 
> Yeah, we'd need to move this into network_set_handshake_secrets_psk and rederive 
> the points. And actually if we do this storing the points in the network profile 
> doesn't make a whole lot of sense anymore since its being rederived every time.

I would hate for this to be the outcome.  Re-deriving the PT is pretty expensive.

> 
> Alternatively we just keep it how I have it and tell they user they're network 
> isn't configured properly :)

I think it could be argued that if PasswordIdentifier is set, then any BSSes 
that are not H2E/do not set the in-use bit are not connectable.

Regards,
-Denis
James Prestwood Dec. 6, 2023, 7:53 p.m. UTC | #4
On 12/6/23 11:44, Denis Kenzior wrote:
> Hi James,
>
>>>> Loading the PSK will fail if there is no password identifier set
>>>> and the BSS sets the "exclusive" bit. If a password identifier is
>>>
>>> I'm not so sure about this.  The trouble is that this logic is 
>>> sufficient for the initial connection, but isn't sufficient when you 
>>> consider re-association.
>> Your right, roaming would be entirely broken between BSS's that 
>> mismatch using password identifiers. Maybe even hunt-and-peck and 
>> H2E? not entirely sure. We 
>
> Well, ReAssociate would just use SAE passphrase directly, so it would 
> work in theory...  But it is a bit of a strange case.
>
>> would need to re-derive the point for each roam, like in 
>> network_set_handshake_secrets_psk().
>
> ??  You mean SAE-H2E with password identifier for BSSes that report 
> exclusive/in-use bit and SAE-H2E for BSSes without?  Or something else?

Yeah, I'm talking about multiple H2E BSS's that set or don't set the 
exclusive/in-use bits. Maybe it would be ok actually. I got concerned 
when I saw the points being set into the handshake in 
network_set_handshake_secrets_psk() (which happens on roaming) but it 
looks like this is only used for initial SAE association. Maybe 
roaming/FT would be ok? But this is all moot if we just bail early if 
the password identifier setting does not match the BSS's capabilities.

>>>
>>> This likely needs to be taken into consideration much later, when 
>>> building the actual handshake state.
>>
>> Yeah, we'd need to move this into network_set_handshake_secrets_psk 
>> and rederive the points. And actually if we do this storing the 
>> points in the network profile doesn't make a whole lot of sense 
>> anymore since its being rederived every time.
>
> I would hate for this to be the outcome.  Re-deriving the PT is pretty 
> expensive.
>
>>
>> Alternatively we just keep it how I have it and tell they user 
>> they're network isn't configured properly :)
>
> I think it could be argued that if PasswordIdentifier is set, then any 
> BSSes that are not H2E/do not set the in-use bit are not connectable.
This works for me. Much easier and will enforce a properly configured 
network.
>
> Regards,
> -Denis
diff mbox series

Patch

diff --git a/src/network.c b/src/network.c
index 79f964b2..d422b282 100644
--- a/src/network.c
+++ b/src/network.c
@@ -70,6 +70,7 @@  struct network {
 	struct network_info *info;
 	unsigned char *psk;
 	char *passphrase;
+	char *password_identifier;
 	struct l_ecc_point *sae_pt_19; /* SAE PT for Group 19 */
 	struct l_ecc_point *sae_pt_20; /* SAE PT for Group 20 */
 	unsigned int agent_request;
@@ -124,6 +125,13 @@  static void network_reset_passphrase(struct network *network)
 		network->passphrase = NULL;
 	}
 
+	if (network->password_identifier) {
+		explicit_bzero(network->password_identifier,
+				strlen(network->password_identifier));
+		l_free(network->password_identifier);
+		network->password_identifier = NULL;
+	}
+
 	if (network->sae_pt_19) {
 		l_ecc_point_free(network->sae_pt_19);
 		network->sae_pt_19 = NULL;
@@ -317,7 +325,8 @@  static struct l_ecc_point *network_generate_sae_pt(struct network *network,
 	l_debug("Generating PT for Group %u", group);
 
 	pt = crypto_derive_sae_pt_ecc(group, network->ssid,
-						network->passphrase, NULL);
+						network->passphrase,
+						network->password_identifier);
 	if (!pt)
 		l_warn("SAE PT generation for Group %u failed", group);
 
@@ -462,6 +471,10 @@  static int network_set_handshake_secrets_psk(struct network *network,
 
 		handshake_state_set_passphrase(hs, network->passphrase);
 
+		if (network->password_identifier)
+			handshake_state_set_password_identifier(hs,
+						network->password_identifier);
+
 		if (ie_rsnxe_capable(hs->authenticator_rsnxe,
 							IE_RSNX_SAE_H2E)) {
 			l_debug("Authenticator is SAE H2E capable");
@@ -631,6 +644,9 @@  static int network_load_psk(struct network *network, struct scan_bss *bss)
 	_auto_(l_free) char *passphrase =
 			l_settings_get_string(network->settings,
 						"Security", "Passphrase");
+	_auto_(l_free) char *password_id =
+			l_settings_get_string(network->settings, "Security",
+						"PasswordIdentifier");
 	_auto_(l_free) char *path =
 		storage_get_network_file_path(security, ssid);
 
@@ -641,6 +657,32 @@  static int network_load_psk(struct network *network, struct scan_bss *bss)
 		psk_len = 0;
 	}
 
+	/*
+	 * Sort out if the password identifier is required, should be used, "
+	 * or should be ignored.
+	 */
+	if (is_sae) {
+		if (bss->sae_pw_id_exclusive && !password_id) {
+			l_error("BSS requires SAE password identifiers, check "
+				"[Security].PasswordIdentifier");
+			return -ENOKEY;
+		}
+
+		/*
+		 * If the profile contains a password identifier but the network
+		 * does not support it IWD will still attempt to connect. The
+		 * caveat here is if the connection is successful the sync will
+		 * remove the password identifier entry. Though this might be
+		 * unexpected to the user, retaining this (invalid) setting
+		 * isn't worth special casing.
+		 */
+		if (!bss->sae_pw_id_used && password_id) {
+			l_debug("[Security].PasswordIdentifier set but BSS "
+				"does not not use password identifiers");
+			l_free(l_steal_ptr(password_id));
+		}
+	}
+
 	/* PSK can be generated from the passphrase but not the other way */
 	if (!psk || is_sae) {
 		if (!passphrase)
@@ -655,6 +697,7 @@  static int network_load_psk(struct network *network, struct scan_bss *bss)
 	network_reset_passphrase(network);
 	network_reset_psk(network);
 	network->passphrase = l_steal_ptr(passphrase);
+	network->password_identifier = l_steal_ptr(password_id);
 
 	if (network_settings_load_pt_ecc(network, path,
 						19, &network->sae_pt_19) > 0)
@@ -726,6 +769,11 @@  static void network_settings_save(struct network *network,
 		l_settings_set_string(settings, "Security", "Passphrase",
 					network->passphrase);
 
+	if (network->password_identifier)
+		l_settings_set_string(settings, "Security",
+					"PasswordIdentifier",
+					network->password_identifier);
+
 	if (network->sae_pt_19)
 		network_settings_save_sae_pt_ecc(settings, network->sae_pt_19);