]> git.ipfire.org Git - thirdparty/hostap.git/commitdiff
AP: Support Extended Key ID
authorAlexander Wetzel <alexander@wetzel-home.de>
Fri, 20 Mar 2020 19:04:31 +0000 (20:04 +0100)
committerJouni Malinen <j@w1.fi>
Mon, 23 Mar 2020 09:43:10 +0000 (11:43 +0200)
Support Extended Key ID in hostapd according to IEEE Std 802.11-2016.

Extended Key ID allows to rekey pairwise keys without the otherwise
unavoidable MPDU losses on a busy link. The standard is fully backward
compatible, allowing an AP to serve STAs with and without Extended Key
ID support in the same BSS.

Signed-off-by: Alexander Wetzel <alexander@wetzel-home.de>
hostapd/config_file.c
hostapd/ctrl_iface.c
hostapd/hostapd.conf
src/ap/ap_config.h
src/ap/wpa_auth.c
src/ap/wpa_auth.h
src/ap/wpa_auth_ft.c
src/ap/wpa_auth_glue.c
src/ap/wpa_auth_i.h
src/ap/wpa_auth_ie.c

index 3c7bb395f9bdd4f3d75cd76af167c7cc8492f550..2bc0679b5c3d4482a1284cc5d4949a737d903dd2 100644 (file)
@@ -2869,6 +2869,16 @@ static int hostapd_config_fill(struct hostapd_config *conf,
                }
        } else if (os_strcmp(buf, "wpa") == 0) {
                bss->wpa = atoi(pos);
+       } else if (os_strcmp(buf, "extended_key_id") == 0) {
+               int val = atoi(pos);
+
+               if (bss->extended_key_id < 0 || bss->extended_key_id > 2) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: Invalid extended_key_id=%d; allowed range 0..2",
+                                  line, bss->extended_key_id);
+                       return 1;
+               }
+               bss->extended_key_id = val;
        } else if (os_strcmp(buf, "wpa_group_rekey") == 0) {
                bss->wpa_group_rekey = atoi(pos);
                bss->wpa_group_rekey_set = 1;
index 6d2ecbc9c2759236482636e0fe87ed47b40c9e3e..1a369ed2aedabe0c1c99fdb4884550373fdd54dc 100644 (file)
@@ -1295,6 +1295,14 @@ static int hostapd_ctrl_iface_get_config(struct hostapd_data *hapd,
                pos += ret;
        }
 
+       if ((hapd->conf->wpa & WPA_PROTO_RSN) && hapd->conf->extended_key_id) {
+               ret = os_snprintf(pos, end - pos, "extended_key_id=%d\n",
+                                 hapd->conf->extended_key_id);
+               if (os_snprintf_error(end - pos, ret))
+                       return pos - buf;
+               pos += ret;
+       }
+
        return pos - buf;
 }
 
index bc5d1a7f697d4dea5ea79c45ba01006b0cebbdf7..b09f6ed0a701576da36bdc02f79fc207c5a78522 100644 (file)
@@ -1510,6 +1510,17 @@ own_ip_addr=127.0.0.1
 # wpa_key_mgmt=SAE for WPA3-Personal instead of wpa_key_mgmt=WPA-PSK).
 #wpa=2
 
+# Extended Key ID support for Individually Addressed frames
+#
+# Extended Key ID allows to rekey PTK keys without the impacts the "normal"
+# PTK rekeying with only a single Key ID 0 has. It can only be used when the
+# driver supports it and RSN/WPA2 is used with a CCMP/GCMP pairwise cipher.
+#
+# 0 = force off, i.e., use only Key ID 0 (default)
+# 1 = enable and use Extended Key ID support when possible
+# 2 = identical to 1 but start with Key ID 1 when possible
+#extended_key_id=0
+
 # WPA pre-shared keys for WPA-PSK. This can be either entered as a 256-bit
 # secret in hex format (64 hex digits), wpa_psk, or as an ASCII passphrase
 # (8..63 characters) that will be converted to PSK. This conversion uses SSID
index 80be7ed3908746fd78317d5c58b4be34afe7c75b..5f4665c07c8ed8897a33c4b4b67f1080938befe5 100644 (file)
@@ -354,6 +354,7 @@ struct hostapd_bss_config {
                        * algorithms, WPA_AUTH_ALG_{OPEN,SHARED,LEAP} */
 
        int wpa; /* bitfield of WPA_PROTO_WPA, WPA_PROTO_RSN */
+       int extended_key_id;
        int wpa_key_mgmt;
        enum mfp_options ieee80211w;
        int group_mgmt_cipher;
index 64fc09ca66d0a9a81bace772348ca06b49cb6838..6512c0194098ab99787728a30cb7590b3c6db992 100644 (file)
@@ -781,7 +781,7 @@ static void wpa_request_new_ptk(struct wpa_state_machine *sm)
        if (sm == NULL)
                return;
 
-       if (sm->wpa_auth->conf.wpa_deny_ptk0_rekey) {
+       if (!sm->use_ext_key_id && sm->wpa_auth->conf.wpa_deny_ptk0_rekey) {
                wpa_printf(MSG_INFO,
                           "WPA: PTK0 rekey not allowed, disconnect " MACSTR,
                           MAC2STR(sm->addr));
@@ -790,6 +790,8 @@ static void wpa_request_new_ptk(struct wpa_state_machine *sm)
                sm->disconnect_reason =
                        WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA;
        } else {
+               if (sm->use_ext_key_id)
+                       sm->keyidx_active ^= 1; /* flip Key ID */
                sm->PTKRequest = TRUE;
                sm->PTK_valid = 0;
        }
@@ -1754,6 +1756,11 @@ void wpa_remove_ptk(struct wpa_state_machine *sm)
                             0, KEY_FLAG_PAIRWISE))
                wpa_printf(MSG_DEBUG,
                           "RSN: PTK removal from the driver failed");
+       if (sm->wpa_auth->conf.extended_key_id && sm->use_ext_key_id &&
+           wpa_auth_set_key(sm->wpa_auth, 0, WPA_ALG_NONE, sm->addr, 1, NULL,
+                            0, KEY_FLAG_PAIRWISE))
+               wpa_printf(MSG_DEBUG,
+                          "RSN: PTK Key ID 1 removal from the driver failed");
        sm->pairwise_set = FALSE;
        eloop_cancel_timeout(wpa_rekey_ptk, sm->wpa_auth, sm);
 }
@@ -1812,16 +1819,23 @@ int wpa_auth_sm_event(struct wpa_state_machine *sm, enum wpa_event event)
                        sm->Init = FALSE;
                        sm->AuthenticationRequest = TRUE;
                        break;
-               } else if (sm->wpa_auth->conf.wpa_deny_ptk0_rekey) {
+               }
+
+               if (!sm->use_ext_key_id &&
+                   sm->wpa_auth->conf.wpa_deny_ptk0_rekey) {
                        wpa_printf(MSG_INFO,
                                   "WPA: PTK0 rekey not allowed, disconnect "
                                   MACSTR, MAC2STR(sm->addr));
                        sm->Disconnect = TRUE;
-                       /* Try to encourage the STA reconnect */
+                       /* Try to encourage the STA to reconnect */
                        sm->disconnect_reason =
                                WLAN_REASON_CLASS3_FRAME_FROM_NONASSOC_STA;
                        break;
                }
+
+               if (sm->use_ext_key_id)
+                       sm->keyidx_active ^= 1; /* flip Key ID */
+
                if (sm->GUpdateStationKeys) {
                        /*
                         * Reauthentication cancels the pending group key
@@ -3261,6 +3275,7 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
        u8 *wpa_ie;
        int secure, gtkidx, encr = 0;
        u8 *wpa_ie_buf = NULL, *wpa_ie_buf2 = NULL;
+       u8 hdr[2];
 
        SM_ENTRY_MA(WPA_PTK, PTKINITNEGOTIATING, wpa_ptk);
        sm->TimeoutEvt = FALSE;
@@ -3317,6 +3332,18 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
        wpa_auth_logger(sm->wpa_auth, sm->addr, LOGGER_DEBUG,
                        "sending 3/4 msg of 4-Way Handshake");
        if (sm->wpa == WPA_VERSION_WPA2) {
+               if (sm->use_ext_key_id && sm->TimeoutCtr == 1 &&
+                   wpa_auth_set_key(sm->wpa_auth, 0,
+                                    wpa_cipher_to_alg(sm->pairwise),
+                                    sm->addr,
+                                    sm->keyidx_active, sm->PTK.tk,
+                                    wpa_cipher_key_len(sm->pairwise),
+                                    KEY_FLAG_PAIRWISE_RX)) {
+                       wpa_sta_disconnect(sm->wpa_auth, sm->addr,
+                                          WLAN_REASON_PREV_AUTH_NOT_VALID);
+                       return;
+               }
+
                /* WPA2 send GTK in the 4-way handshake */
                secure = 1;
                gtk = gsm->GTK[gsm->GN - 1];
@@ -3357,6 +3384,10 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
        }
 
        kde_len = wpa_ie_len + ieee80211w_kde_len(sm) + ocv_oci_len(sm);
+
+       if (sm->use_ext_key_id)
+               kde_len += 2 + RSN_SELECTOR_LEN + 2;
+
        if (gtk)
                kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len;
 #ifdef CONFIG_IEEE80211R_AP
@@ -3392,10 +3423,15 @@ SM_STATE(WPA_PTK, PTKINITNEGOTIATING)
                pos += elen;
        }
 #endif /* CONFIG_IEEE80211R_AP */
+       hdr[1] = 0;
+
+       if (sm->use_ext_key_id) {
+               hdr[0] = sm->keyidx_active & 0x01;
+               pos = wpa_add_kde(pos, RSN_KEY_DATA_KEYID, hdr, 2, NULL, 0);
+       }
+
        if (gtk) {
-               u8 hdr[2];
                hdr[0] = gtkidx & 0x03;
-               hdr[1] = 0;
                pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
                                  gtk, gtk_len);
        }
@@ -3478,9 +3514,17 @@ SM_STATE(WPA_PTK, PTKINITDONE)
        if (sm->Pair) {
                enum wpa_alg alg = wpa_cipher_to_alg(sm->pairwise);
                int klen = wpa_cipher_key_len(sm->pairwise);
-               if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
-                                    sm->PTK.tk, klen,
-                                    KEY_FLAG_PAIRWISE_RX_TX)) {
+               int res;
+
+               if (sm->use_ext_key_id)
+                       res = wpa_auth_set_key(sm->wpa_auth, 0, 0, sm->addr,
+                                              sm->keyidx_active, NULL, 0,
+                                              KEY_FLAG_PAIRWISE_RX_TX_MODIFY);
+               else
+                       res = wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr,
+                                              0, sm->PTK.tk, klen,
+                                              KEY_FLAG_PAIRWISE_RX_TX);
+               if (res) {
                        wpa_sta_disconnect(sm->wpa_auth, sm->addr,
                                           WLAN_REASON_PREV_AUTH_NOT_VALID);
                        return;
@@ -5167,6 +5211,7 @@ int wpa_auth_resend_m3(struct wpa_state_machine *sm,
        struct wpa_group *gsm = sm->group;
        u8 *wpa_ie;
        int wpa_ie_len, secure, gtkidx, encr = 0;
+       u8 hdr[2];
 
        /* Send EAPOL(1, 1, 1, Pair, P, RSC, ANonce, MIC(PTK), RSNIE, [MDIE],
           GTK[GN], IGTK, [BIGTK], [FTIE], [TIE * 2])
@@ -5219,6 +5264,10 @@ int wpa_auth_resend_m3(struct wpa_state_machine *sm,
        }
 
        kde_len = wpa_ie_len + ieee80211w_kde_len(sm) + ocv_oci_len(sm);
+
+       if (sm->use_ext_key_id)
+               kde_len += 2 + RSN_SELECTOR_LEN + 2;
+
        if (gtk)
                kde_len += 2 + RSN_SELECTOR_LEN + 2 + gtk_len;
 #ifdef CONFIG_IEEE80211R_AP
@@ -5251,10 +5300,15 @@ int wpa_auth_resend_m3(struct wpa_state_machine *sm,
                pos += elen;
        }
 #endif /* CONFIG_IEEE80211R_AP */
+       hdr[1] = 0;
+
+       if (sm->use_ext_key_id) {
+               hdr[0] = sm->keyidx_active & 0x01;
+               pos = wpa_add_kde(pos, RSN_KEY_DATA_KEYID, hdr, 2, NULL, 0);
+       }
+
        if (gtk) {
-               u8 hdr[2];
                hdr[0] = gtkidx & 0x03;
-               hdr[1] = 0;
                pos = wpa_add_kde(pos, RSN_KEY_DATA_GROUPKEY, hdr, 2,
                                  gtk, gtk_len);
        }
index 58aa9ff2142ed9c46fe980519aec4161292ea0a1..528516195c4c00fb438e84dc1a05a2b4542a5a17 100644 (file)
@@ -169,6 +169,7 @@ struct ft_remote_r1kh {
 
 struct wpa_auth_config {
        int wpa;
+       int extended_key_id;
        int wpa_key_mgmt;
        int wpa_pairwise;
        int wpa_group;
index 5ed78e6a6fa49d6dcdf9cdb41eee139138d36804..476a2be698d72ab8c5a17d9080c47e6c9f77104d 100644 (file)
@@ -2775,7 +2775,7 @@ void wpa_ft_install_ptk(struct wpa_state_machine *sm)
         * again after association to get the PTK configured, but that could be
         * optimized by adding the STA entry earlier.
         */
-       if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, 0,
+       if (wpa_auth_set_key(sm->wpa_auth, 0, alg, sm->addr, sm->keyidx_active,
                             sm->PTK.tk, klen, KEY_FLAG_PAIRWISE_RX_TX))
                return;
 
index 926ff455f104c690d278c29b1e87783f249d579e..70d32818a142bfcafdf5b564969ec9bc7b7209ee 100644 (file)
@@ -41,6 +41,7 @@ static void hostapd_wpa_auth_conf(struct hostapd_bss_config *conf,
 
        os_memset(wconf, 0, sizeof(*wconf));
        wconf->wpa = conf->wpa;
+       wconf->extended_key_id = conf->extended_key_id;
        wconf->wpa_key_mgmt = conf->wpa_key_mgmt;
        wconf->wpa_pairwise = conf->wpa_pairwise;
        wconf->wpa_group = conf->wpa_group;
@@ -433,7 +434,11 @@ static int hostapd_wpa_auth_set_key(void *ctx, int vlan_id, enum wpa_alg alg,
        }
 
 #ifdef CONFIG_TESTING_OPTIONS
-       if (addr && !is_broadcast_ether_addr(addr)) {
+       if (key_flag & KEY_FLAG_MODIFY) {
+               /* We are updating an already installed key. Don't overwrite
+                * the already stored key information with zeros.
+                */
+       } else if (addr && !is_broadcast_ether_addr(addr)) {
                struct sta_info *sta;
 
                sta = ap_get_sta(hapd, addr);
@@ -1419,6 +1424,12 @@ int hostapd_setup_wpa(struct hostapd_data *hapd)
                _conf.wpa_deny_ptk0_rekey = 1;
        }
 
+       if (_conf.extended_key_id &&
+           (hapd->iface->drv_flags & WPA_DRIVER_FLAGS_EXTENDED_KEY_ID))
+               wpa_msg(hapd->msg_ctx, MSG_DEBUG, "Extended Key ID supported");
+       else
+               _conf.extended_key_id = 0;
+
        hapd->wpa_auth = wpa_init(hapd->own_addr, &_conf, &cb, hapd);
        if (hapd->wpa_auth == NULL) {
                wpa_printf(MSG_ERROR, "WPA initialization failed.");
index 5d7b96c6f194efbe3f2e1159ab9b836959acd83a..bc59d6a4c0f039263023966eb6b8536ddd57d822 100644 (file)
@@ -61,6 +61,8 @@ struct wpa_state_machine {
        unsigned int pmk_len;
        u8 pmkid[PMKID_LEN]; /* valid if pmkid_set == 1 */
        struct wpa_ptk PTK;
+       u8 keyidx_active;
+       Boolean use_ext_key_id;
        Boolean PTK_valid;
        Boolean pairwise_set;
        Boolean tk_already_set;
index 2e6d0591040926cafcf48e8164d9f4f35cc26e3a..11153e0b8537432fde6780a6bd1aadbed4a126db 100644 (file)
@@ -297,6 +297,8 @@ int wpa_write_rsn_ie(struct wpa_auth_config *conf, u8 *buf, size_t len,
        if (rsn_testing)
                capab |= BIT(8) | BIT(15);
 #endif /* CONFIG_RSN_TESTING */
+       if (conf->extended_key_id)
+               capab |= WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST;
        WPA_PUT_LE16(pos, capab);
        pos += 2;
 
@@ -553,6 +555,7 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
                        const u8 *mdie, size_t mdie_len,
                        const u8 *owe_dh, size_t owe_dh_len)
 {
+       struct wpa_auth_config *conf = &wpa_auth->conf;
        struct wpa_ie_data data;
        int ciphers, key_mgmt, res, version;
        u32 selector;
@@ -944,6 +947,23 @@ int wpa_validate_wpa_ie(struct wpa_authenticator *wpa_auth,
        }
 #endif /* CONFIG_DPP */
 
+       if (conf->extended_key_id && sm->wpa == WPA_VERSION_WPA2 &&
+           sm->pairwise != WPA_CIPHER_TKIP &&
+           (data.capabilities & WPA_CAPABILITY_EXT_KEY_ID_FOR_UNICAST)) {
+               sm->use_ext_key_id = TRUE;
+               if (conf->extended_key_id == 2 &&
+                   !wpa_key_mgmt_ft(sm->wpa_key_mgmt) &&
+                   !wpa_key_mgmt_fils(sm->wpa_key_mgmt))
+                       sm->keyidx_active = 1;
+               else
+                       sm->keyidx_active = 0;
+               wpa_printf(MSG_DEBUG,
+                          "RSN: Extended Key ID supported (start with %d)",
+                          sm->keyidx_active);
+       } else {
+               sm->use_ext_key_id = FALSE;
+       }
+
        if (sm->wpa_ie == NULL || sm->wpa_ie_len < wpa_ie_len) {
                os_free(sm->wpa_ie);
                sm->wpa_ie = os_malloc(wpa_ie_len);