diff mbox series

[1/1] hostapd: Add support for SAE offload for STA/AP interface

Message ID bafa3477120554b091aa6449ca7fc9b459af8203.1697095686.git.vinayak.yadawad@broadcom.com
State Changes Requested
Headers show
Series [1/1] hostapd: Add support for SAE offload for STA/AP interface | expand

Commit Message

Vinayak Yadawad Oct. 12, 2023, 9:29 a.m. UTC
In the current change we enable support for SAE offload by
1. Adding support of 4-way HS offload for AP interface
2. Adding support for SAE authentication offload
The changes basically involve handling of necessary parameter
plumbing to the driver in the connect path for STA interface.
Also the changes involve AP parameter plumbing in AP bringup
path and Port authorized event handling.

Signed-off-by: Vinayak Yadawad <vinayak.yadawad@broadcom.com>
---
 src/ap/beacon.c                    | 31 ++++++++++++++++++++++++++++++
 src/ap/drv_callbacks.c             |  6 ++++++
 src/ap/hostapd.c                   |  7 +++++--
 src/ap/wpa_auth_glue.c             |  6 ++++++
 src/ap/wpa_auth_glue.h             |  1 +
 src/ap/wpa_auth_ie.c               |  6 ++++--
 src/drivers/driver.h               | 24 +++++++++++++++++++++++
 src/drivers/driver_nl80211.c       | 22 +++++++++++++++++++++
 src/drivers/driver_nl80211_capa.c  | 12 ++++++++++++
 src/drivers/driver_nl80211_event.c |  9 ++++++++-
 wpa_supplicant/wpa_supplicant.c    | 15 +++++++++++++++
 11 files changed, 134 insertions(+), 5 deletions(-)

Comments

Jouni Malinen Nov. 7, 2023, 9:25 a.m. UTC | #1
On Thu, Oct 12, 2023 at 02:59:29PM +0530, Vinayak Yadawad wrote:
> In the current change we enable support for SAE offload by
> 1. Adding support of 4-way HS offload for AP interface
> 2. Adding support for SAE authentication offload
> The changes basically involve handling of necessary parameter
> plumbing to the driver in the connect path for STA interface.
> Also the changes involve AP parameter plumbing in AP bringup
> path and Port authorized event handling.

SAE offload for STA mode was added in an earlier patch:
https://w1.fi/cgit/hostap/commit/?id=c3b8452e0e04038349fbfe757639ff3b377e98da
https://w1.fi/cgit/hostap/commit/?id=236c0cfbcdf7459be1727afb7eb540c4ee081cbc

So that part would need some small changes here.

As far as the AP mode case is concerned, does this work only if both SAE
and the following 4-way handshake are offloaded? Or would there be
possibility for offload SAE and performing 4-way handshake from hostapd
(with the PMK from the driver provided to user space)? It would be good
for the commit message to be clear on this detail.

> diff --git a/src/ap/beacon.c b/src/ap/beacon.c
> @@ -2011,6 +2012,36 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
> +	/* If SAE offload is enabled, provide passphrase to lower layer for
> +	 * PMK generation
> +	 */
> +	if ((wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt)) &&
> +	    (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SAE_OFFLOAD_AP)) {
> +		if (hapd->conf->ssid.wpa_passphrase) {
> +			params->sae_passphrase_len =
> +				os_strlen(hapd->conf->ssid.wpa_passphrase);
> +			if (params->sae_passphrase_len) {
> +				os_memcpy(params->sae_passphrase,
> +					hapd->conf->ssid.wpa_passphrase,
> +					params->sae_passphrase_len);
> +			}
> +		}
> +		if (hapd->conf->sae_pwe) {
> +			params->sae_pwe = hapd->conf->sae_pwe;
> +		}
> +	}

There is no "SAE passphrase". It is "SAE password". Please note that
hostapd supports multiple SAE passwords and selection of which one to
use based on either STA MAC address or SAE password identifier. Even if
the SAE-in-driver case would not support those options, the
implementation hostapd would need to check for these being enabled in
the configuration and if so, refuse to try to configure SAE offload that
won't work in a way that was configured.

By default, this should use hapd->conf->sae_passwords and fall back to
hapd->conf->ssid.wpa_passphrase only if no SAE-specific password is
configured.

> +	/* If key mgmt offload is enabled, configure PSK  */
> +	if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) &&
> +	    (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_4WAY_HANDSHAKE_AP_PSK)) {
> +		if (hapd->conf->ssid.wpa_psk && hapd->conf->ssid.wpa_psk_set) {
> +			os_memcpy(params->psk, hapd->conf->ssid.wpa_psk->psk, PMK_LEN);
> +		} else if (hapd->conf->ssid.wpa_passphrase) {
> +			pbkdf2_sha1(hapd->conf->ssid.wpa_passphrase, hapd->conf->ssid.ssid,
> +				hapd->conf->ssid.ssid_len, 4096, params->psk, PMK_LEN);
> +		}
> +	}

The commit title identified this as adding SAE offload, but this seems
to be adding WPA2-Personal (PSK) offload.. It would be better to split
these into two separate patches to make the exact applicable changes
easier to understand.

> diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
> +#include "wpa_auth_glue.h"

That is not supposed to done in wpa_auth_ie.c, i.e., the WPA
authenticator functionality is supposed to be generic that does not
depend on hostapd-specific implementation.

>  #ifdef CONFIG_SAE
> -	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_SAE && data.num_pmkid &&
> -	    !sm->pmksa) {
> +	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_SAE &&
> +	    !(hostapd_wpa_auth_is_sae_offload_enabled(wpa_auth->cb_ctx)) &&
> +	    data.num_pmkid && !sm->pmksa) {

In other words, this direct hostapd_wpa_auth_is_sae_offload_enabled()
call is expected to be done using struct wpa_auth_callbacks instead to
avoid direct dependency on hostapd-specific wpa_auth_glue.c.

> diff --git a/src/drivers/driver.h b/src/drivers/driver.h
> @@ -69,6 +69,8 @@ enum hostapd_chan_width_attr {
> +#define MAX_PASSPHRASE_LEN 63

> @@ -1785,6 +1787,21 @@ struct wpa_driver_ap_params {
> +	/**
> +	 * sae_passphrase - sae passphrase for SAE offload
> +	 */
> +	u8 sae_passphrase[MAX_PASSPHRASE_LEN];

That should be called sae_password. 63 is not the correct length limit
for this. Why would this even need to be a fixed length array instead of
being const pointer to the password in some upper layer struct owned by
the caller?

> @@ -2253,6 +2270,12 @@ struct wpa_driver_capa {
> +/** Driver support AP_PSK authentication offload */
> +#define WPA_DRIVER_FLAGS2_4WAY_HANDSHAKE_AP_PSK	0x0000000000008000ULL

This should be in a separate patch.

> +/** Driver support SAE STA authentication offload */
> +#define WPA_DRIVER_FLAGS2_SAE_OFFLOAD		0x0000000000010000ULL

This is already included in hostap.git.

> +/** Driver support SAE AP authentication offload */
> +#define WPA_DRIVER_FLAGS2_SAE_OFFLOAD_AP	0x0000000000020000ULL
>  	u64 flags2;

And this would be in the AP mode SAE offload patch.

> @@ -6645,6 +6668,7 @@ union wpa_event_data {
>  	struct port_authorized {
>  		const u8 *td_bitmap;
>  		size_t td_bitmap_len;
> +		u8 sta_addr[ETH_ALEN];
>  	} port_authorized;

This should now really be documented since the previous uses of struct
port_authorized were for STA mode while this last one is for AP mode and
such a mix of parameters is quite confusing without clear documentation.

> diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
> @@ -6926,6 +6937,17 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
> +	/* Add SAE passphrase in case of SAE offload */
> +	if (wpa_key_mgmt_sae(params->key_mgmt_suite) &&
> +	    (drv->capa.flags2 & WPA_DRIVER_FLAGS2_SAE_OFFLOAD) &&
> +	    params->passphrase) {
> +		wpa_hexdump_key(MSG_DEBUG, "  * SAE passphrase",
> +			params->passphrase, os_strlen(params->passphrase));
> +		if (nla_put(msg, NL80211_ATTR_SAE_PASSWORD,
> +		    os_strlen(params->passphrase), params->passphrase))
> +			return -1;
> +	}

This is in incorrect function (i.e., this should be only for CONNECT,
not for ASSOCIATE). Anyway, the STA case is already covered, so no need
for this anymore.

> diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
> @@ -3505,7 +3505,14 @@ static void nl80211_port_authorized(struct wpa_driver_nl80211_data *drv,
>  	}
>  
>  	addr = nla_data(tb[NL80211_ATTR_MAC]);
> -	if (os_memcmp(addr, drv->bssid, ETH_ALEN) != 0) {
> +	if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) {
> +		/* Update STA assoc address */
> +		os_memcpy(event.port_authorized.sta_addr, addr, ETH_ALEN);
> +		wpa_printf(MSG_DEBUG,
> +			   "nl80211: Port authorized for STA BSSID "  MACSTR,
> +			   MAC2STR(addr));
> +	} else if (is_sta_interface(drv->nlmode) &&
> +	    os_memcmp(addr, drv->bssid, ETH_ALEN) != 0) {
>  		wpa_printf(MSG_DEBUG,
>  			   "nl80211: Ignore port authorized event for " MACSTR
>  			   " (not the currently connected BSSID " MACSTR ")",

Is this use for NL80211_CMD_PORT_AUTHORIZED for AP mode documented
somewhere? nl80211.h does not seem to describe such use case at all. The
documentation implies this even to be for STA mode only even if
interpreted more widely to cover AP mode, it would be only for 802.1X
FT.

> diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
> @@ -4269,6 +4269,21 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
>  	     params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192))
>  		params.req_handshake_offload = 1;
>  
> +	params.sae_pwe = wpa_s->conf->sae_pwe;
> +	if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE) &&
> +	    (wpa_key_mgmt_sae(params.key_mgmt_suite))) {
> +		if (ssid->sae_password) {
> +			wpa_printf(MSG_DEBUG, "sae enabled join..Using sae_password");
> +			params.passphrase = ssid->sae_password;
> +		} else if (ssid->passphrase) {
> +			wpa_printf(MSG_DEBUG, "sae enabled join..Using passphrase");
> +			params.passphrase = ssid->passphrase;
> +		} else {
> +			wpa_printf(MSG_ERROR, "sae enabled join.."
> +				"But neither sae_password nor passphrase set");
> +		}
> +	}

No need for this change anymore.
Vinayak Yadawad Nov. 8, 2023, 10:30 a.m. UTC | #2
Hi Jouni,

Thanks for the review suggestions.

>SAE offload for STA mode was added in an earlier patch:
>https://w1.fi/cgit/hostap/commit/?id=c3b8452e0e04038349fbfe757639ff3b377e98da
>https://w1.fi/cgit/hostap/commit/?id=236c0cfbcdf7459be1727afb7eb540c4ee081cbc
>So that part would need some small changes here.
We will sync the SAE offload changes based on top of the branch.

>As far as the AP mode case is concerned, does this work only if both SAE
>and the following 4-way handshake are offloaded? Or would there be
>possibility for offload SAE and performing 4-way handshake from hostapd
>(with the PMK from the driver provided to user space)? It would be good
f>or the commit message to be clear on this detail.
This is applicable only for drivers supporting 4way HS offload. We
will update the description accordingly.
As you suggested we will split and have separate patch for 4way HS
offload and SAE offload.

>There is no "SAE passphrase". It is "SAE password". Please note that
>hostapd supports multiple SAE passwords and selection of which one to
>use based on either STA MAC address or SAE password identifier. Even if
>the SAE-in-driver case would not support those options, the
>implementation hostapd would need to check for these being enabled in
>the configuration and if so, refuse to try to configure SAE offload that
>won't work in a way that was configured.
>By default, this should use hapd->conf->sae_passwords and fall back to
>hapd->conf->ssid.wpa_passphrase only if no SAE-specific password is
>configured.
We will address as per your suggestion.

>That is not supposed to done in wpa_auth_ie.c, i.e., the WPA
>authenticator functionality is supposed to be generic that does not
>depend on hostapd-specific implementation.

>In other words, this direct hostapd_wpa_auth_is_sae_offload_enabled()
>call is expected to be done using struct wpa_auth_callbacks instead to
>avoid direct dependency on hostapd-specific wpa_auth_glue.c.
Ack. We will add a callback to get the driver capability.

>That should be called sae_password. 63 is not the correct length limit
>for this. Why would this even need to be a fixed length array instead of
>being const pointer to the password in some upper layer struct owned by
>the caller?
Ack we will address this.

>This should be in a separate patch.
Ack we will be updating separate patch for PSK offload.

>This is already included in hostap.git.
Ack, thanks.

>And this would be in the AP mode SAE offload patch.
Ack.

>This should now really be documented since the previous uses of struct
>port_authorized were for STA mode while this last one is for AP mode and
>such a mix of parameters is quite confusing without clear documentation.
We will update the documentation.

>This is in incorrect function (i.e., this should be only for CONNECT,
>not for ASSOCIATE). Anyway, the STA case is already covered, so no need
>for this anymore.
Ack.

>Is this use for NL80211_CMD_PORT_AUTHORIZED for AP mode documented
>somewhere? nl80211.h does not seem to describe such use case at all. The
>documentation implies this even to be for STA mode only even if
>interpreted more widely to cover AP mode, it would be only for 802.1X
>FT.
The commit below adds support for nl80211_send_port_authorized() to be
used for both STA/GC and AP/GO.
https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next.git/commit/?h=for-next&id=e4e7e3af73694380f0d9a742d13b80598a3393e9

Regards,
Vinayak

On Tue, Nov 7, 2023 at 2:56 PM Jouni Malinen <j@w1.fi> wrote:
>
> On Thu, Oct 12, 2023 at 02:59:29PM +0530, Vinayak Yadawad wrote:
> > In the current change we enable support for SAE offload by
> > 1. Adding support of 4-way HS offload for AP interface
> > 2. Adding support for SAE authentication offload
> > The changes basically involve handling of necessary parameter
> > plumbing to the driver in the connect path for STA interface.
> > Also the changes involve AP parameter plumbing in AP bringup
> > path and Port authorized event handling.
>
> SAE offload for STA mode was added in an earlier patch:
> https://w1.fi/cgit/hostap/commit/?id=c3b8452e0e04038349fbfe757639ff3b377e98da
> https://w1.fi/cgit/hostap/commit/?id=236c0cfbcdf7459be1727afb7eb540c4ee081cbc
>
> So that part would need some small changes here.
>
> As far as the AP mode case is concerned, does this work only if both SAE
> and the following 4-way handshake are offloaded? Or would there be
> possibility for offload SAE and performing 4-way handshake from hostapd
> (with the PMK from the driver provided to user space)? It would be good
> for the commit message to be clear on this detail.
>
> > diff --git a/src/ap/beacon.c b/src/ap/beacon.c
> > @@ -2011,6 +2012,36 @@ int ieee802_11_build_ap_params(struct hostapd_data *hapd,
> > +     /* If SAE offload is enabled, provide passphrase to lower layer for
> > +      * PMK generation
> > +      */
> > +     if ((wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt)) &&
> > +         (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SAE_OFFLOAD_AP)) {
> > +             if (hapd->conf->ssid.wpa_passphrase) {
> > +                     params->sae_passphrase_len =
> > +                             os_strlen(hapd->conf->ssid.wpa_passphrase);
> > +                     if (params->sae_passphrase_len) {
> > +                             os_memcpy(params->sae_passphrase,
> > +                                     hapd->conf->ssid.wpa_passphrase,
> > +                                     params->sae_passphrase_len);
> > +                     }
> > +             }
> > +             if (hapd->conf->sae_pwe) {
> > +                     params->sae_pwe = hapd->conf->sae_pwe;
> > +             }
> > +     }
>
> There is no "SAE passphrase". It is "SAE password". Please note that
> hostapd supports multiple SAE passwords and selection of which one to
> use based on either STA MAC address or SAE password identifier. Even if
> the SAE-in-driver case would not support those options, the
> implementation hostapd would need to check for these being enabled in
> the configuration and if so, refuse to try to configure SAE offload that
> won't work in a way that was configured.
>
> By default, this should use hapd->conf->sae_passwords and fall back to
> hapd->conf->ssid.wpa_passphrase only if no SAE-specific password is
> configured.
>
> > +     /* If key mgmt offload is enabled, configure PSK  */
> > +     if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) &&
> > +         (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_4WAY_HANDSHAKE_AP_PSK)) {
> > +             if (hapd->conf->ssid.wpa_psk && hapd->conf->ssid.wpa_psk_set) {
> > +                     os_memcpy(params->psk, hapd->conf->ssid.wpa_psk->psk, PMK_LEN);
> > +             } else if (hapd->conf->ssid.wpa_passphrase) {
> > +                     pbkdf2_sha1(hapd->conf->ssid.wpa_passphrase, hapd->conf->ssid.ssid,
> > +                             hapd->conf->ssid.ssid_len, 4096, params->psk, PMK_LEN);
> > +             }
> > +     }
>
> The commit title identified this as adding SAE offload, but this seems
> to be adding WPA2-Personal (PSK) offload.. It would be better to split
> these into two separate patches to make the exact applicable changes
> easier to understand.
>
> > diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
> > +#include "wpa_auth_glue.h"
>
> That is not supposed to done in wpa_auth_ie.c, i.e., the WPA
> authenticator functionality is supposed to be generic that does not
> depend on hostapd-specific implementation.
>
> >  #ifdef CONFIG_SAE
> > -     if (sm->wpa_key_mgmt == WPA_KEY_MGMT_SAE && data.num_pmkid &&
> > -         !sm->pmksa) {
> > +     if (sm->wpa_key_mgmt == WPA_KEY_MGMT_SAE &&
> > +         !(hostapd_wpa_auth_is_sae_offload_enabled(wpa_auth->cb_ctx)) &&
> > +         data.num_pmkid && !sm->pmksa) {
>
> In other words, this direct hostapd_wpa_auth_is_sae_offload_enabled()
> call is expected to be done using struct wpa_auth_callbacks instead to
> avoid direct dependency on hostapd-specific wpa_auth_glue.c.
>
> > diff --git a/src/drivers/driver.h b/src/drivers/driver.h
> > @@ -69,6 +69,8 @@ enum hostapd_chan_width_attr {
> > +#define MAX_PASSPHRASE_LEN 63
>
> > @@ -1785,6 +1787,21 @@ struct wpa_driver_ap_params {
> > +     /**
> > +      * sae_passphrase - sae passphrase for SAE offload
> > +      */
> > +     u8 sae_passphrase[MAX_PASSPHRASE_LEN];
>
> That should be called sae_password. 63 is not the correct length limit
> for this. Why would this even need to be a fixed length array instead of
> being const pointer to the password in some upper layer struct owned by
> the caller?
>
> > @@ -2253,6 +2270,12 @@ struct wpa_driver_capa {
> > +/** Driver support AP_PSK authentication offload */
> > +#define WPA_DRIVER_FLAGS2_4WAY_HANDSHAKE_AP_PSK      0x0000000000008000ULL
>
> This should be in a separate patch.
>
> > +/** Driver support SAE STA authentication offload */
> > +#define WPA_DRIVER_FLAGS2_SAE_OFFLOAD                0x0000000000010000ULL
>
> This is already included in hostap.git.
>
> > +/** Driver support SAE AP authentication offload */
> > +#define WPA_DRIVER_FLAGS2_SAE_OFFLOAD_AP     0x0000000000020000ULL
> >       u64 flags2;
>
> And this would be in the AP mode SAE offload patch.
>
> > @@ -6645,6 +6668,7 @@ union wpa_event_data {
> >       struct port_authorized {
> >               const u8 *td_bitmap;
> >               size_t td_bitmap_len;
> > +             u8 sta_addr[ETH_ALEN];
> >       } port_authorized;
>
> This should now really be documented since the previous uses of struct
> port_authorized were for STA mode while this last one is for AP mode and
> such a mix of parameters is quite confusing without clear documentation.
>
> > diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
> > @@ -6926,6 +6937,17 @@ static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
> > +     /* Add SAE passphrase in case of SAE offload */
> > +     if (wpa_key_mgmt_sae(params->key_mgmt_suite) &&
> > +         (drv->capa.flags2 & WPA_DRIVER_FLAGS2_SAE_OFFLOAD) &&
> > +         params->passphrase) {
> > +             wpa_hexdump_key(MSG_DEBUG, "  * SAE passphrase",
> > +                     params->passphrase, os_strlen(params->passphrase));
> > +             if (nla_put(msg, NL80211_ATTR_SAE_PASSWORD,
> > +                 os_strlen(params->passphrase), params->passphrase))
> > +                     return -1;
> > +     }
>
> This is in incorrect function (i.e., this should be only for CONNECT,
> not for ASSOCIATE). Anyway, the STA case is already covered, so no need
> for this anymore.
>
> > diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
> > @@ -3505,7 +3505,14 @@ static void nl80211_port_authorized(struct wpa_driver_nl80211_data *drv,
> >       }
> >
> >       addr = nla_data(tb[NL80211_ATTR_MAC]);
> > -     if (os_memcmp(addr, drv->bssid, ETH_ALEN) != 0) {
> > +     if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) {
> > +             /* Update STA assoc address */
> > +             os_memcpy(event.port_authorized.sta_addr, addr, ETH_ALEN);
> > +             wpa_printf(MSG_DEBUG,
> > +                        "nl80211: Port authorized for STA BSSID "  MACSTR,
> > +                        MAC2STR(addr));
> > +     } else if (is_sta_interface(drv->nlmode) &&
> > +         os_memcmp(addr, drv->bssid, ETH_ALEN) != 0) {
> >               wpa_printf(MSG_DEBUG,
> >                          "nl80211: Ignore port authorized event for " MACSTR
> >                          " (not the currently connected BSSID " MACSTR ")",
>
> Is this use for NL80211_CMD_PORT_AUTHORIZED for AP mode documented
> somewhere? nl80211.h does not seem to describe such use case at all. The
> documentation implies this even to be for STA mode only even if
> interpreted more widely to cover AP mode, it would be only for 802.1X
> FT.
>
> > diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
> > @@ -4269,6 +4269,21 @@ static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
> >            params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192))
> >               params.req_handshake_offload = 1;
> >
> > +     params.sae_pwe = wpa_s->conf->sae_pwe;
> > +     if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE) &&
> > +         (wpa_key_mgmt_sae(params.key_mgmt_suite))) {
> > +             if (ssid->sae_password) {
> > +                     wpa_printf(MSG_DEBUG, "sae enabled join..Using sae_password");
> > +                     params.passphrase = ssid->sae_password;
> > +             } else if (ssid->passphrase) {
> > +                     wpa_printf(MSG_DEBUG, "sae enabled join..Using passphrase");
> > +                     params.passphrase = ssid->passphrase;
> > +             } else {
> > +                     wpa_printf(MSG_ERROR, "sae enabled join.."
> > +                             "But neither sae_password nor passphrase set");
> > +             }
> > +     }
>
> No need for this change anymore.
>
> --
> Jouni Malinen                                            PGP id EFC895FA
Jouni Malinen Nov. 8, 2023, 1:29 p.m. UTC | #3
On Wed, Nov 08, 2023 at 04:00:10PM +0530, Vinayak Yadawad wrote:
> The commit below adds support for nl80211_send_port_authorized() to be
> used for both STA/GC and AP/GO.
> https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next.git/commit/?h=for-next&id=e4e7e3af73694380f0d9a742d13b80598a3393e9

That commit does not modify include/uapi/linux/nl80211.h which is the
place where such things should be documented for the kernel interface.
It would be good to get an additional kernel change to update the
documentation of NL80211_CMD_PORT_AUTHORIZED there to match the extended
implementation. The current description is clearly out-of-date and it
would seem to imply that this extended use case is not really expected.
Vinayak Yadawad Nov. 9, 2023, 10:04 a.m. UTC | #4
Hi Jouni,

>That commit does not modify include/uapi/linux/nl80211.h which is the
>place where such things should be documented for the kernel interface.
>It would be good to get an additional kernel change to update the
>documentation of NL80211_CMD_PORT_AUTHORIZED there to match the extended
>implementation. The current description is clearly out-of-date and it
>would seem to imply that this extended use case is not really expected.
We have raised a request for updating documentation for the
NL80211_CMD_PORT_AUTHORIZED event.
https://patchwork.kernel.org/project/linux-wireless/patch/f746b59f41436e9df29c24688035fbc6eb91ab06.1699510229.git.vinayak.yadawad@broadcom.com/

Regards,
Vinayak

On Wed, Nov 8, 2023 at 7:00 PM Jouni Malinen <j@w1.fi> wrote:
>
> On Wed, Nov 08, 2023 at 04:00:10PM +0530, Vinayak Yadawad wrote:
> > The commit below adds support for nl80211_send_port_authorized() to be
> > used for both STA/GC and AP/GO.
> > https://git.kernel.org/pub/scm/linux/kernel/git/wireless/wireless-next.git/commit/?h=for-next&id=e4e7e3af73694380f0d9a742d13b80598a3393e9
>
> That commit does not modify include/uapi/linux/nl80211.h which is the
> place where such things should be documented for the kernel interface.
> It would be good to get an additional kernel change to update the
> documentation of NL80211_CMD_PORT_AUTHORIZED there to match the extended
> implementation. The current description is clearly out-of-date and it
> would seem to imply that this extended use case is not really expected.
>
> --
> Jouni Malinen                                            PGP id EFC895FA
diff mbox series

Patch

diff --git a/src/ap/beacon.c b/src/ap/beacon.c
index 1d3b96ac7..a844f4169 100644
--- a/src/ap/beacon.c
+++ b/src/ap/beacon.c
@@ -32,6 +32,7 @@ 
 #include "dfs.h"
 #include "taxonomy.h"
 #include "ieee802_11_auth.h"
+#include "crypto/sha1.h"
 
 
 #ifdef NEED_AP_MLME
@@ -2011,6 +2012,36 @@  int ieee802_11_build_ap_params(struct hostapd_data *hapd,
 	resp = hostapd_probe_resp_offloads(hapd, &resp_len);
 #endif /* NEED_AP_MLME */
 
+	/* If SAE offload is enabled, provide passphrase to lower layer for
+	 * PMK generation
+	 */
+	if ((wpa_key_mgmt_sae(hapd->conf->wpa_key_mgmt)) &&
+	    (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SAE_OFFLOAD_AP)) {
+		if (hapd->conf->ssid.wpa_passphrase) {
+			params->sae_passphrase_len =
+				os_strlen(hapd->conf->ssid.wpa_passphrase);
+			if (params->sae_passphrase_len) {
+				os_memcpy(params->sae_passphrase,
+					hapd->conf->ssid.wpa_passphrase,
+					params->sae_passphrase_len);
+			}
+		}
+		if (hapd->conf->sae_pwe) {
+			params->sae_pwe = hapd->conf->sae_pwe;
+		}
+	}
+
+	/* If key mgmt offload is enabled, configure PSK  */
+	if ((hapd->conf->wpa_key_mgmt & WPA_KEY_MGMT_PSK) &&
+	    (hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_4WAY_HANDSHAKE_AP_PSK)) {
+		if (hapd->conf->ssid.wpa_psk && hapd->conf->ssid.wpa_psk_set) {
+			os_memcpy(params->psk, hapd->conf->ssid.wpa_psk->psk, PMK_LEN);
+		} else if (hapd->conf->ssid.wpa_passphrase) {
+			pbkdf2_sha1(hapd->conf->ssid.wpa_passphrase, hapd->conf->ssid.ssid,
+				hapd->conf->ssid.ssid_len, 4096, params->psk, PMK_LEN);
+		}
+	}
+
 	params->head = (u8 *) head;
 	params->head_len = head_len;
 	params->tail = tail;
diff --git a/src/ap/drv_callbacks.c b/src/ap/drv_callbacks.c
index 0516213f4..42419f523 100644
--- a/src/ap/drv_callbacks.c
+++ b/src/ap/drv_callbacks.c
@@ -2188,6 +2188,8 @@  void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 			  union wpa_event_data *data)
 {
 	struct hostapd_data *hapd = ctx;
+	struct sta_info *sta = NULL;
+
 #ifndef CONFIG_NO_STDOUT_DEBUG
 	int level = MSG_DEBUG;
 
@@ -2482,6 +2484,10 @@  void wpa_supplicant_event(void *ctx, enum wpa_event_type event,
 		hostapd_cleanup_cca_params(hapd);
 		break;
 #endif /* CONFIG_IEEE80211AX */
+	case EVENT_PORT_AUTHORIZED:
+		sta = ap_get_sta(hapd, data->port_authorized.sta_addr);
+		ap_sta_set_authorized(hapd, sta, 1);
+		break;
 	default:
 		wpa_printf(MSG_DEBUG, "Unknown event %d", event);
 		break;
diff --git a/src/ap/hostapd.c b/src/ap/hostapd.c
index 966030d57..961524b31 100644
--- a/src/ap/hostapd.c
+++ b/src/ap/hostapd.c
@@ -3573,8 +3573,11 @@  void hostapd_new_assoc_sta(struct hostapd_data *hapd, struct sta_info *sta,
 		    sta->auth_alg != WLAN_AUTH_FILS_PK &&
 		    !(sta->flags & (WLAN_STA_WPS | WLAN_STA_MAYBE_WPS)))
 			wpa_auth_sm_event(sta->wpa_sm, WPA_REAUTH);
-	} else
-		wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm);
+	} else {
+		if (!(hapd->iface->drv_flags2 &
+		    WPA_DRIVER_FLAGS2_4WAY_HANDSHAKE_AP_PSK))
+			wpa_auth_sta_associated(hapd->wpa_auth, sta->wpa_sm);
+	}
 
 	if (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_WIRED) {
 		if (eloop_cancel_timeout(ap_handle_timer, hapd, sta) > 0) {
diff --git a/src/ap/wpa_auth_glue.c b/src/ap/wpa_auth_glue.c
index 30a72b126..35b56b9df 100644
--- a/src/ap/wpa_auth_glue.c
+++ b/src/ap/wpa_auth_glue.c
@@ -1757,6 +1757,12 @@  int hostapd_setup_wpa(struct hostapd_data *hapd)
 
 }
 
+bool hostapd_wpa_auth_is_sae_offload_enabled(void *ctx)
+{
+	struct hostapd_data *hapd = ctx;
+
+	return	!!(hapd->iface->drv_flags2 & WPA_DRIVER_FLAGS2_SAE_OFFLOAD_AP);
+}
 
 void hostapd_reconfig_wpa(struct hostapd_data *hapd)
 {
diff --git a/src/ap/wpa_auth_glue.h b/src/ap/wpa_auth_glue.h
index 1b13ae7be..7a7cb966b 100644
--- a/src/ap/wpa_auth_glue.h
+++ b/src/ap/wpa_auth_glue.h
@@ -12,5 +12,6 @@ 
 int hostapd_setup_wpa(struct hostapd_data *hapd);
 void hostapd_reconfig_wpa(struct hostapd_data *hapd);
 void hostapd_deinit_wpa(struct hostapd_data *hapd);
+bool hostapd_wpa_auth_is_sae_offload_enabled(void *ctx);
 
 #endif /* WPA_AUTH_GLUE_H */
diff --git a/src/ap/wpa_auth_ie.c b/src/ap/wpa_auth_ie.c
index 43ccec9be..3d33ac934 100644
--- a/src/ap/wpa_auth_ie.c
+++ b/src/ap/wpa_auth_ie.c
@@ -17,6 +17,7 @@ 
 #include "pmksa_cache_auth.h"
 #include "wpa_auth_ie.h"
 #include "wpa_auth_i.h"
+#include "wpa_auth_glue.h"
 
 
 #ifdef CONFIG_RSN_TESTING
@@ -998,8 +999,9 @@  wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
 	}
 
 #ifdef CONFIG_SAE
-	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_SAE && data.num_pmkid &&
-	    !sm->pmksa) {
+	if (sm->wpa_key_mgmt == WPA_KEY_MGMT_SAE &&
+	    !(hostapd_wpa_auth_is_sae_offload_enabled(wpa_auth->cb_ctx)) &&
+	    data.num_pmkid && !sm->pmksa) {
 		wpa_auth_vlogger(wpa_auth, sm->addr, LOGGER_DEBUG,
 				 "No PMKSA cache entry found for SAE");
 		return WPA_INVALID_PMKID;
diff --git a/src/drivers/driver.h b/src/drivers/driver.h
index dbe2ad5e4..96fe56787 100644
--- a/src/drivers/driver.h
+++ b/src/drivers/driver.h
@@ -69,6 +69,8 @@  enum hostapd_chan_width_attr {
 #define HOSTAPD_DFS_REGION_ETSI	2
 #define HOSTAPD_DFS_REGION_JP	3
 
+#define MAX_PASSPHRASE_LEN 63
+
 /**
  * enum reg_change_initiator - Regulatory change initiator
  */
@@ -1785,6 +1787,21 @@  struct wpa_driver_ap_params {
 	 * mld_link_id - Link id for MLD BSS's
 	 */
 	u8 mld_link_id;
+
+	/**
+	 * sae_passphrase - sae passphrase for SAE offload
+	 */
+	u8 sae_passphrase[MAX_PASSPHRASE_LEN];
+
+	/**
+	 * sae_passphrase_len - sae passphrase length for SAE offload
+	 */
+	u8 sae_passphrase_len;
+
+	/**
+	 * psk - PSK passed to driver for 4-way HS offload
+	 */
+	u8 psk[PMK_LEN];
 };
 
 struct wpa_driver_mesh_bss_params {
@@ -2253,6 +2270,12 @@  struct wpa_driver_capa {
 #define WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_STA	0x0000000000002000ULL
 /** Driver supports MLO in station/AP mode */
 #define WPA_DRIVER_FLAGS2_MLO			0x0000000000004000ULL
+/** Driver support AP_PSK authentication offload */
+#define WPA_DRIVER_FLAGS2_4WAY_HANDSHAKE_AP_PSK	0x0000000000008000ULL
+/** Driver support SAE STA authentication offload */
+#define WPA_DRIVER_FLAGS2_SAE_OFFLOAD		0x0000000000010000ULL
+/** Driver support SAE AP authentication offload */
+#define WPA_DRIVER_FLAGS2_SAE_OFFLOAD_AP	0x0000000000020000ULL
 	u64 flags2;
 
 #define FULL_AP_CLIENT_STATE_SUPP(drv_flags) \
@@ -6645,6 +6668,7 @@  union wpa_event_data {
 	struct port_authorized {
 		const u8 *td_bitmap;
 		size_t td_bitmap_len;
+		u8 sta_addr[ETH_ALEN];
 	} port_authorized;
 
 	/**
diff --git a/src/drivers/driver_nl80211.c b/src/drivers/driver_nl80211.c
index 9bd6a58e0..817bc084f 100644
--- a/src/drivers/driver_nl80211.c
+++ b/src/drivers/driver_nl80211.c
@@ -5109,6 +5109,17 @@  static int wpa_driver_nl80211_set_ap(void *priv,
 			 suites))
 		goto fail;
 
+	if ((params->key_mgmt_suites & WPA_KEY_MGMT_PSK) &&
+	    (drv->capa.flags & WPA_DRIVER_FLAGS2_4WAY_HANDSHAKE_AP_PSK) &&
+	    (nla_put(msg, NL80211_ATTR_PMK, 32, params->psk)))
+		goto fail;
+
+	if (wpa_key_mgmt_sae(params->key_mgmt_suites) &&
+	    (drv->capa.flags2 & WPA_DRIVER_FLAGS2_SAE_OFFLOAD_AP) &&
+	    (nla_put(msg, NL80211_ATTR_SAE_PASSWORD, params->sae_passphrase_len,
+	    params->sae_passphrase)))
+		goto fail;
+
 	if (params->key_mgmt_suites & WPA_KEY_MGMT_IEEE8021X_NO_WPA &&
 	    (!params->pairwise_ciphers ||
 	     params->pairwise_ciphers & (WPA_CIPHER_WEP104 | WPA_CIPHER_WEP40)) &&
@@ -6926,6 +6937,17 @@  static int nl80211_connect_common(struct wpa_driver_nl80211_data *drv,
 			return -1;
 	}
 
+	/* Add SAE passphrase in case of SAE offload */
+	if (wpa_key_mgmt_sae(params->key_mgmt_suite) &&
+	    (drv->capa.flags2 & WPA_DRIVER_FLAGS2_SAE_OFFLOAD) &&
+	    params->passphrase) {
+		wpa_hexdump_key(MSG_DEBUG, "  * SAE passphrase",
+			params->passphrase, os_strlen(params->passphrase));
+		if (nla_put(msg, NL80211_ATTR_SAE_PASSWORD,
+		    os_strlen(params->passphrase), params->passphrase))
+			return -1;
+	}
+
 	if (nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT))
 		return -1;
 
diff --git a/src/drivers/driver_nl80211_capa.c b/src/drivers/driver_nl80211_capa.c
index 5e6406885..c17957381 100644
--- a/src/drivers/driver_nl80211_capa.c
+++ b/src/drivers/driver_nl80211_capa.c
@@ -697,6 +697,18 @@  static void wiphy_info_ext_feature_flags(struct wiphy_info_data *info,
 		capa->flags2 |= WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_STA;
 		capa->flags2 |= WPA_DRIVER_FLAGS2_PROT_RANGE_NEG_AP;
 	}
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_4WAY_HANDSHAKE_AP_PSK))
+		capa->flags2 |= WPA_DRIVER_FLAGS2_4WAY_HANDSHAKE_AP_PSK;
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_SAE_OFFLOAD_AP))
+		capa->flags2 |= WPA_DRIVER_FLAGS2_SAE_OFFLOAD_AP;
+
+	if (ext_feature_isset(ext_features, len,
+			      NL80211_EXT_FEATURE_SAE_OFFLOAD))
+		capa->flags2 |= WPA_DRIVER_FLAGS2_SAE_OFFLOAD;
 }
 
 
diff --git a/src/drivers/driver_nl80211_event.c b/src/drivers/driver_nl80211_event.c
index 9d39703e0..d1473e8f9 100644
--- a/src/drivers/driver_nl80211_event.c
+++ b/src/drivers/driver_nl80211_event.c
@@ -3505,7 +3505,14 @@  static void nl80211_port_authorized(struct wpa_driver_nl80211_data *drv,
 	}
 
 	addr = nla_data(tb[NL80211_ATTR_MAC]);
-	if (os_memcmp(addr, drv->bssid, ETH_ALEN) != 0) {
+	if (is_ap_interface(drv->nlmode) && drv->device_ap_sme) {
+		/* Update STA assoc address */
+		os_memcpy(event.port_authorized.sta_addr, addr, ETH_ALEN);
+		wpa_printf(MSG_DEBUG,
+			   "nl80211: Port authorized for STA BSSID "  MACSTR,
+			   MAC2STR(addr));
+	} else if (is_sta_interface(drv->nlmode) &&
+	    os_memcmp(addr, drv->bssid, ETH_ALEN) != 0) {
 		wpa_printf(MSG_DEBUG,
 			   "nl80211: Ignore port authorized event for " MACSTR
 			   " (not the currently connected BSSID " MACSTR ")",
diff --git a/wpa_supplicant/wpa_supplicant.c b/wpa_supplicant/wpa_supplicant.c
index 69f228919..961411596 100644
--- a/wpa_supplicant/wpa_supplicant.c
+++ b/wpa_supplicant/wpa_supplicant.c
@@ -4269,6 +4269,21 @@  static void wpas_start_assoc_cb(struct wpa_radio_work *work, int deinit)
 	     params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SUITE_B_192))
 		params.req_handshake_offload = 1;
 
+	params.sae_pwe = wpa_s->conf->sae_pwe;
+	if ((wpa_s->drv_flags & WPA_DRIVER_FLAGS_SAE) &&
+	    (wpa_key_mgmt_sae(params.key_mgmt_suite))) {
+		if (ssid->sae_password) {
+			wpa_printf(MSG_DEBUG, "sae enabled join..Using sae_password");
+			params.passphrase = ssid->sae_password;
+		} else if (ssid->passphrase) {
+			wpa_printf(MSG_DEBUG, "sae enabled join..Using passphrase");
+			params.passphrase = ssid->passphrase;
+		} else {
+			wpa_printf(MSG_ERROR, "sae enabled join.."
+				"But neither sae_password nor passphrase set");
+		}
+	}
+
 	if (wpa_s->conf->key_mgmt_offload) {
 		if (params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X ||
 		    params.key_mgmt_suite == WPA_KEY_MGMT_IEEE8021X_SHA256 ||