]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
PASN: Support PASN with SAE key derivation
authorIlan Peer <ilan.peer@intel.com>
Wed, 16 Dec 2020 11:00:52 +0000 (13:00 +0200)
committerJouni Malinen <j@w1.fi>
Mon, 25 Jan 2021 18:27:14 +0000 (20:27 +0200)
Signed-off-by: Ilan Peer <ilan.peer@intel.com>
src/rsn_supp/wpa.c
src/rsn_supp/wpa.h
wpa_supplicant/ctrl_iface.c
wpa_supplicant/notify.c
wpa_supplicant/pasn_supplicant.c
wpa_supplicant/wpa_cli.c
wpa_supplicant/wpa_supplicant_i.h

index 0d79f3aceb767a6fedceb15353f464935ef95216..d8beecc239b813a5dca25c90e9e87a4ade473f30 100644 (file)
@@ -5194,3 +5194,14 @@ void wpa_sm_set_dpp_z(struct wpa_sm *sm, const struct wpabuf *z)
        }
 }
 #endif /* CONFIG_DPP2 */
+
+
+#ifdef CONFIG_PASN
+void wpa_pasn_pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
+                             const u8 *pmkid, const u8 *bssid, int key_mgmt)
+{
+       sm->cur_pmksa = pmksa_cache_add(sm->pmksa, pmk, pmk_len, pmkid, NULL, 0,
+                                       bssid, sm->own_addr, NULL,
+                                       key_mgmt, 0);
+}
+#endif /* CONFIG_PASN */
index 9f5164df0cba3a258af2c80d5f7c1b1c0ecc1013..af4a6f90d24859dccae4f2f8373caccf2ab0a599 100644 (file)
@@ -521,5 +521,7 @@ int owe_process_assoc_resp(struct wpa_sm *sm, const u8 *bssid,
 void wpa_sm_set_reset_fils_completed(struct wpa_sm *sm, int set);
 void wpa_sm_set_fils_cache_id(struct wpa_sm *sm, const u8 *fils_cache_id);
 void wpa_sm_set_dpp_z(struct wpa_sm *sm, const struct wpabuf *z);
+void wpa_pasn_pmksa_cache_add(struct wpa_sm *sm, const u8 *pmk, size_t pmk_len,
+                             const u8 *pmkid, const u8 *bssid, int key_mgmt);
 
 #endif /* WPA_H */
index 63bcba494a1d295f6ead2e739e7b63053015281a..da198b938ba386c543875e53a3a31bbdc4cb7865 100644 (file)
@@ -10464,6 +10464,7 @@ static int wpas_ctrl_iface_pasn_start(struct wpa_supplicant *wpa_s, char *cmd)
        u8 bssid[ETH_ALEN];
        int akmp = -1, cipher = -1, got_bssid = 0;
        u16 group = 0xFFFF;
+       int id = 0;
 
        /*
         * Entry format: bssid=<BSSID> akmp=<AKMP> cipher=<CIPHER> group=<group>
@@ -10503,6 +10504,8 @@ static int wpas_ctrl_iface_pasn_start(struct wpa_supplicant *wpa_s, char *cmd)
                        cipher = WPA_CIPHER_GCMP;
                } else if (os_strncmp(token, "group=", 6) == 0) {
                        group = atoi(token + 6);
+               } else if (os_strncmp(token, "nid=", 4) == 0) {
+                       id = atoi(token + 4);
                } else {
                        wpa_printf(MSG_DEBUG,
                                   "CTRL: PASN Invalid parameter: '%s'",
@@ -10516,7 +10519,7 @@ static int wpas_ctrl_iface_pasn_start(struct wpa_supplicant *wpa_s, char *cmd)
                return -1;
        }
 
-       return wpas_pasn_auth_start(wpa_s, bssid, akmp, cipher, group);
+       return wpas_pasn_auth_start(wpa_s, bssid, akmp, cipher, group, id);
 }
 #endif /* CONFIG_PASN */
 
index 0ba1e144c1d24685d48ab6175a6bca15f1caa409..e0e7e5433d77336d87f226e6e45cbaa205c8d2c1 100644 (file)
@@ -387,6 +387,11 @@ void wpas_notify_network_removed(struct wpa_supplicant *wpa_s,
                wpas_notify_persistent_group_removed(wpa_s, ssid);
 
        wpas_p2p_network_removed(wpa_s, ssid);
+
+#ifdef CONFIG_PASN
+       if (wpa_s->pasn.ssid == ssid)
+               wpa_s->pasn.ssid = NULL;
+#endif /* CONFIG_PASN */
 }
 
 
index d4ff650a28a1efc2d006d3fd7b142b40d934b4a9..0382f6b5a0d816f83f0efe8c15e0300908f166f7 100644 (file)
@@ -21,6 +21,7 @@
 #include "wpa_supplicant_i.h"
 #include "driver_i.h"
 #include "bss.h"
+#include "config.h"
 
 static const int dot11RSNAConfigPMKLifetime = 43200;
 
@@ -29,6 +30,7 @@ struct wpa_pasn_auth_work {
        int akmp;
        int cipher;
        u16 group;
+       int network_id;
 };
 
 
@@ -61,13 +63,203 @@ static void wpas_pasn_auth_status(struct wpa_supplicant *wpa_s, const u8 *bssid,
 }
 
 
-static struct wpabuf * wpas_pasn_get_wrapped_data(struct wpas_pasn *pasn)
+#ifdef CONFIG_SAE
+
+static struct wpabuf * wpas_pasn_wd_sae_commit(struct wpa_supplicant *wpa_s)
 {
+       struct wpas_pasn *pasn = &wpa_s->pasn;
+       struct wpabuf *buf = NULL;
+       const char *password = NULL;
+       int ret;
+
+       if (pasn->ssid) {
+               password = pasn->ssid->sae_password;
+               if (!password)
+                       password = pasn->ssid->passphrase;
+       }
+
+       if (!password) {
+               wpa_printf(MSG_DEBUG, "PASN: SAE without a password");
+               return NULL;
+       }
+
+       ret = sae_set_group(&pasn->sae, pasn->group);
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "PASN: Failed to set SAE group");
+               return NULL;
+       }
+
+       /* TODO: SAE H2E */
+       ret = sae_prepare_commit(wpa_s->own_addr, pasn->bssid,
+                                (const u8 *) password, os_strlen(password), 0,
+                                &pasn->sae);
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "PASN: Failed to prepare SAE commit");
+               return NULL;
+       }
+
+       /* Need to add the entire Authentication frame body */
+       buf = wpabuf_alloc(6 + SAE_COMMIT_MAX_LEN);
+       if (!buf) {
+               wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SAE buffer");
+               return NULL;
+       }
+
+       wpabuf_put_le16(buf, WLAN_AUTH_SAE);
+       wpabuf_put_le16(buf, 1);
+       wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+
+       sae_write_commit(&pasn->sae, buf, NULL, 0);
+       pasn->sae.state = SAE_COMMITTED;
+
+       return buf;
+}
+
+
+static int wpas_pasn_wd_sae_rx(struct wpa_supplicant *wpa_s, struct wpabuf *wd)
+{
+       struct wpas_pasn *pasn = &wpa_s->pasn;
+       const u8 *data;
+       size_t buf_len;
+       u16 len, res, alg, seq, status;
+       int groups[] = { pasn->group, 0 };
+       int ret;
+
+       if (!wd)
+               return -1;
+
+       data = wpabuf_head_u8(wd);
+       buf_len = wpabuf_len(wd);
+
+       /* first handle the commit message */
+       if (buf_len < 2) {
+               wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short (commit)");
+               return -1;
+       }
+
+       len = WPA_GET_LE16(data);
+       if (len < 6 || buf_len - 2 < len) {
+               wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short for commit");
+               return -1;
+       }
+
+       buf_len -= 2;
+       data += 2;
+
+       alg = WPA_GET_LE16(data);
+       seq = WPA_GET_LE16(data + 2);
+       status = WPA_GET_LE16(data + 4);
+
+       wpa_printf(MSG_DEBUG, "PASN: SAE: commit: alg=%u, seq=%u, status=%u",
+                  alg, seq, status);
+
+       /* TODO: SAE H2E */
+       if (alg != WLAN_AUTH_SAE || seq != 1 || status != WLAN_STATUS_SUCCESS) {
+               wpa_printf(MSG_DEBUG, "PASN: SAE: dropping peer commit");
+               return -1;
+       }
+
+       res = sae_parse_commit(&pasn->sae, data + 6, len - 6, NULL, 0, groups,
+                              0);
+       if (res != WLAN_STATUS_SUCCESS) {
+               wpa_printf(MSG_DEBUG, "PASN: SAE failed parsing commit");
+               return -1;
+       }
+
+       /* Process the commit message and derive the PMK */
+       ret = sae_process_commit(&pasn->sae);
+       if (ret) {
+               wpa_printf(MSG_DEBUG, "SAE: Failed to process peer commit");
+               return -1;
+       }
+
+       buf_len -= len;
+       data += len;
+
+       /* Handle the confirm message */
+       if (buf_len < 2) {
+               wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short (confirm)");
+               return -1;
+       }
+
+       len = WPA_GET_LE16(data);
+       if (len < 6 || buf_len - 2 < len) {
+               wpa_printf(MSG_DEBUG, "PASN: SAE buffer too short for confirm");
+               return -1;
+       }
+
+       buf_len -= 2;
+       data += 2;
+
+       alg = WPA_GET_LE16(data);
+       seq = WPA_GET_LE16(data + 2);
+       status = WPA_GET_LE16(data + 4);
+
+       wpa_printf(MSG_DEBUG, "PASN: SAE confirm: alg=%u, seq=%u, status=%u",
+                  alg, seq, status);
+
+       if (alg != WLAN_AUTH_SAE || seq != 2 || status != WLAN_STATUS_SUCCESS) {
+               wpa_printf(MSG_DEBUG, "PASN: Dropping peer SAE confirm");
+               return -1;
+       }
+
+       res = sae_check_confirm(&pasn->sae, data + 6, len - 6);
+       if (res != WLAN_STATUS_SUCCESS) {
+               wpa_printf(MSG_DEBUG, "PASN: SAE failed checking confirm");
+               return -1;
+       }
+
+       wpa_printf(MSG_DEBUG, "PASN: SAE completed successfully");
+       pasn->sae.state = SAE_ACCEPTED;
+
+       return 0;
+}
+
+
+static struct wpabuf * wpas_pasn_wd_sae_confirm(struct wpa_supplicant *wpa_s)
+{
+       struct wpas_pasn *pasn = &wpa_s->pasn;
+       struct wpabuf *buf = NULL;
+
+       /* Need to add the entire authentication frame body */
+       buf = wpabuf_alloc(6 + SAE_CONFIRM_MAX_LEN);
+       if (!buf) {
+               wpa_printf(MSG_DEBUG, "PASN: Failed to allocate SAE buffer");
+               return NULL;
+       }
+
+       wpabuf_put_le16(buf, WLAN_AUTH_SAE);
+       wpabuf_put_le16(buf, 2);
+       wpabuf_put_le16(buf, WLAN_STATUS_SUCCESS);
+
+       sae_write_confirm(&pasn->sae, buf);
+       pasn->sae.state = SAE_CONFIRMED;
+
+       return buf;
+}
+
+#endif /* CONFIG_SAE */
+
+
+static struct wpabuf * wpas_pasn_get_wrapped_data(struct wpa_supplicant *wpa_s)
+{
+       struct wpas_pasn *pasn = &wpa_s->pasn;
+
+       if (pasn->using_pmksa)
+               return NULL;
+
        switch (pasn->akmp) {
        case WPA_KEY_MGMT_PASN:
                /* no wrapped data */
                return NULL;
        case WPA_KEY_MGMT_SAE:
+#ifdef CONFIG_SAE
+               if (pasn->trans_seq == 0)
+                       return wpas_pasn_wd_sae_commit(wpa_s);
+               if (pasn->trans_seq == 2)
+                       return wpas_pasn_wd_sae_confirm(wpa_s);
+#endif /* CONFIG_SAE */
+               /* fall through */
        case WPA_KEY_MGMT_FILS_SHA256:
        case WPA_KEY_MGMT_FILS_SHA384:
        case WPA_KEY_MGMT_FT_PSK:
@@ -84,6 +276,9 @@ static struct wpabuf * wpas_pasn_get_wrapped_data(struct wpas_pasn *pasn)
 
 static u8 wpas_pasn_get_wrapped_data_format(struct wpas_pasn *pasn)
 {
+       if (pasn->using_pmksa)
+               return WPA_PASN_WRAPPED_DATA_NO;
+
        /* Note: Valid AKMP is expected to already be validated */
        switch (pasn->akmp) {
        case WPA_KEY_MGMT_SAE:
@@ -141,7 +336,7 @@ static struct wpabuf * wpas_pasn_build_auth_1(struct wpa_supplicant *wpa_s)
                 * Note: Even when PMKSA is available, also add wrapped data as
                 * it is possible that the PMKID is no longer valid at the AP.
                 */
-               wrapped_data_buf = wpas_pasn_get_wrapped_data(pasn);
+               wrapped_data_buf = wpas_pasn_get_wrapped_data(wpa_s);
        } else {
                pmksa = NULL;
        }
@@ -210,7 +405,7 @@ static struct wpabuf * wpas_pasn_build_auth_3(struct wpa_supplicant *wpa_s)
                                   wpa_s->own_addr, pasn->bssid,
                                   pasn->trans_seq + 1, WLAN_STATUS_SUCCESS);
 
-       wrapped_data_buf = wpas_pasn_get_wrapped_data(pasn);
+       wrapped_data_buf = wpas_pasn_get_wrapped_data(wpa_s);
 
        if (!wrapped_data_buf)
                wrapped_data = WPA_PASN_WRAPPED_DATA_NO;
@@ -275,6 +470,7 @@ static void wpas_pasn_reset(struct wpa_supplicant *wpa_s)
        pasn->group = 0;
        pasn->trans_seq = 0;
        pasn->pmk_len = 0;
+       pasn->using_pmksa = false;
 
        forced_memzero(pasn->pmk, sizeof(pasn->pmk));
        forced_memzero(&pasn->ptk, sizeof(pasn->ptk));
@@ -283,6 +479,9 @@ static void wpas_pasn_reset(struct wpa_supplicant *wpa_s)
        wpabuf_free(pasn->beacon_rsne);
        pasn->beacon_rsne = NULL;
 
+#ifdef CONFIG_SAE
+       sae_clear_data(&pasn->sae);
+#endif /* CONFIG_SAE */
        pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
 }
 
@@ -318,11 +517,35 @@ static int wpas_pasn_set_pmk(struct wpa_supplicant *wpa_s,
 
                        pasn->pmk_len = pmksa->pmk_len;
                        os_memcpy(pasn->pmk, pmksa->pmk, pmksa->pmk_len);
+                       pasn->using_pmksa = true;
 
                        return 0;
                }
        }
 
+#ifdef CONFIG_SAE
+       if (pasn->akmp == WPA_KEY_MGMT_SAE) {
+               int ret;
+
+               ret = wpas_pasn_wd_sae_rx(wpa_s, wrapped_data);
+               if (ret) {
+                       wpa_printf(MSG_DEBUG,
+                                  "PASN: Failed processing SAE wrapped data");
+                       pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
+                       return -1;
+               }
+
+               wpa_printf(MSG_DEBUG, "PASN: Success deriving PMK with SAE");
+               pasn->pmk_len = PMK_LEN;
+               os_memcpy(pasn->pmk, pasn->sae.pmk, PMK_LEN);
+
+               wpa_pasn_pmksa_cache_add(wpa_s->wpa, pasn->pmk,
+                                        pasn->pmk_len, pasn->sae.pmkid,
+                                        pasn->bssid, pasn->akmp);
+               return 0;
+       }
+#endif /* CONFIG_SAE */
+
        /* TODO: Derive PMK based on wrapped data */
        wpa_printf(MSG_DEBUG, "PASN: Missing implementation to derive PMK");
        pasn->status = WLAN_STATUS_UNSPECIFIED_FAILURE;
@@ -332,9 +555,11 @@ static int wpas_pasn_set_pmk(struct wpa_supplicant *wpa_s,
 
 static int wpas_pasn_start(struct wpa_supplicant *wpa_s, const u8 *bssid,
                           int akmp, int cipher, u16 group, int freq,
-                          const u8 *beacon_rsne, u8 beacon_rsne_len)
+                          const u8 *beacon_rsne, u8 beacon_rsne_len,
+                          int network_id)
 {
        struct wpas_pasn *pasn = &wpa_s->pasn;
+       struct wpa_ssid *ssid = NULL;
        struct wpabuf *frame;
        int ret;
 
@@ -345,11 +570,21 @@ static int wpas_pasn_start(struct wpa_supplicant *wpa_s, const u8 *bssid,
                return -1;
        }
 
+       ssid = wpa_config_get_network(wpa_s->conf, network_id);
+
        switch (akmp) {
        case WPA_KEY_MGMT_PASN:
                break;
 #ifdef CONFIG_SAE
        case WPA_KEY_MGMT_SAE:
+               if (!ssid) {
+                       wpa_printf(MSG_DEBUG,
+                                  "PASN: No network profile found for SAE");
+                       return -1;
+               }
+               pasn->sae.state = SAE_NOTHING;
+               pasn->sae.send_confirm = 0;
+               pasn->ssid = ssid;
                break;
 #endif /* CONFIG_SAE */
 #ifdef CONFIG_FILS
@@ -497,7 +732,8 @@ static void wpas_pasn_auth_start_cb(struct wpa_radio_work *work, int deinit)
        }
 
        ret = wpas_pasn_start(wpa_s, awork->bssid, awork->akmp, awork->cipher,
-                             awork->group, bss->freq, rsne, *(rsne + 1) + 2);
+                             awork->group, bss->freq, rsne, *(rsne + 1) + 2,
+                             awork->network_id);
        if (ret) {
                wpa_printf(MSG_DEBUG,
                           "PASN: Failed to start PASN authentication");
@@ -514,7 +750,7 @@ fail:
 
 
 int wpas_pasn_auth_start(struct wpa_supplicant *wpa_s, const u8 *bssid,
-                        int akmp, int cipher, u16 group)
+                        int akmp, int cipher, u16 group, int network_id)
 {
        struct wpa_pasn_auth_work *awork;
        struct wpa_bss *bss;
@@ -558,6 +794,7 @@ int wpas_pasn_auth_start(struct wpa_supplicant *wpa_s, const u8 *bssid,
        awork->akmp = akmp;
        awork->cipher = cipher;
        awork->group = group;
+       awork->network_id = network_id;
 
        if (radio_add_work(wpa_s, bss->freq, "pasn-start-auth", 1,
                           wpas_pasn_auth_start_cb, awork) < 0) {
index 5df76aec62e7a80f7d6e4ef74dcdb2701fecbf4b..ec2eeb1cc457467531999d22cd5dea9943c5c262 100644 (file)
@@ -3877,7 +3877,7 @@ static const struct wpa_cli_cmd wpa_cli_commands[] = {
 #ifdef CONFIG_PASN
        { "pasn_auth_start", wpa_cli_cmd_pasn_auth_start, NULL,
          cli_cmd_flag_none,
-         "bssid=<BSSID> akmp=<WPA key mgmt> cipher=<WPA cipher> group=<group> = Start PASN authentication" },
+         "bssid=<BSSID> akmp=<WPA key mgmt> cipher=<WPA cipher> group=<group> nid=<network id> = Start PASN authentication" },
        { "pasn_auth_stop", wpa_cli_cmd_pasn_auth_stop, NULL,
          cli_cmd_flag_none,
          "= Stop PASN authentication" },
index 506de6f0305af64e5db1e5094f4cab3ecbdcd59e..e32b3a0e45e1d22a561bc063e7839e1ab0540cfc 100644 (file)
@@ -528,12 +528,19 @@ struct wpas_pasn {
        u8 bssid[ETH_ALEN];
        size_t pmk_len;
        u8 pmk[PMK_LEN_MAX];
+       bool using_pmksa;
 
        u8 hash[SHA384_MAC_LEN];
 
        struct wpabuf *beacon_rsne;
        struct wpa_ptk ptk;
        struct crypto_ecdh *ecdh;
+
+#ifdef CONFIG_SAE
+       struct sae_data sae;
+#endif /* CONFIG_SAE */
+
+       const struct wpa_ssid *ssid;
 };
 #endif /* CONFIG_PASN */
 
@@ -1685,7 +1692,7 @@ void wpas_handle_assoc_resp_mscs(struct wpa_supplicant *wpa_s, const u8 *bssid,
 
 int wpas_pasn_auth_start(struct wpa_supplicant *wpa_s,
                         const u8 *bssid, int akmp, int cipher,
-                        u16 group);
+                        u16 group, int network_id);
 void wpas_pasn_auth_stop(struct wpa_supplicant *wpa_s);
 int wpas_pasn_auth_tx_status(struct wpa_supplicant *wpa_s,
                             const u8 *data, size_t data_len, u8 acked);