]> git.ipfire.org Git - thirdparty/hostap.git/blobdiff - hostapd/config_file.c
WPS: Allow AP SAE configuration to be added automatically for PSK
[thirdparty/hostap.git] / hostapd / config_file.c
index 41612cb9a3954000f0e53b72d00f8b03ab44bcf7..5ddfbfb196b5d9f34b469a5dfb63ab9b114a7a0c 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * hostapd / Configuration file parser
- * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2018, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -37,7 +37,7 @@ static int hostapd_config_read_vlan_file(struct hostapd_bss_config *bss,
                                         const char *fname)
 {
        FILE *f;
-       char buf[128], *pos, *pos2;
+       char buf[128], *pos, *pos2, *pos3;
        int line = 0, vlan_id;
        struct hostapd_vlan *vlan;
 
@@ -82,7 +82,10 @@ static int hostapd_config_read_vlan_file(struct hostapd_bss_config *bss,
                pos2 = pos;
                while (*pos2 != ' ' && *pos2 != '\t' && *pos2 != '\0')
                        pos2++;
-               *pos2 = '\0';
+
+               if (*pos2 != '\0')
+                       *(pos2++) = '\0';
+
                if (*pos == '\0' || os_strlen(pos) > IFNAMSIZ) {
                        wpa_printf(MSG_ERROR, "Invalid VLAN ifname at line %d "
                                   "in '%s'", line, fname);
@@ -90,6 +93,13 @@ static int hostapd_config_read_vlan_file(struct hostapd_bss_config *bss,
                        return -1;
                }
 
+               while (*pos2 == ' ' || *pos2 == '\t')
+                       pos2++;
+               pos3 = pos2;
+               while (*pos3 != ' ' && *pos3 != '\t' && *pos3 != '\0')
+                       pos3++;
+               *pos3 = '\0';
+
                vlan = os_zalloc(sizeof(*vlan));
                if (vlan == NULL) {
                        wpa_printf(MSG_ERROR, "Out of memory while reading "
@@ -102,6 +112,7 @@ static int hostapd_config_read_vlan_file(struct hostapd_bss_config *bss,
                vlan->vlan_desc.untagged = vlan_id;
                vlan->vlan_desc.notempty = !!vlan_id;
                os_strlcpy(vlan->ifname, pos, sizeof(vlan->ifname));
+               os_strlcpy(vlan->bridge, pos2, sizeof(vlan->bridge));
                vlan->next = bss->vlan;
                bss->vlan = vlan;
        }
@@ -113,7 +124,7 @@ static int hostapd_config_read_vlan_file(struct hostapd_bss_config *bss,
 #endif /* CONFIG_NO_VLAN */
 
 
-static int hostapd_acl_comp(const void *a, const void *b)
+int hostapd_acl_comp(const void *a, const void *b)
 {
        const struct mac_acl_entry *aa = a;
        const struct mac_acl_entry *bb = b;
@@ -121,6 +132,44 @@ static int hostapd_acl_comp(const void *a, const void *b)
 }
 
 
+int hostapd_add_acl_maclist(struct mac_acl_entry **acl, int *num,
+                           int vlan_id, const u8 *addr)
+{
+       struct mac_acl_entry *newacl;
+
+       newacl = os_realloc_array(*acl, *num + 1, sizeof(**acl));
+       if (!newacl) {
+               wpa_printf(MSG_ERROR, "MAC list reallocation failed");
+               return -1;
+       }
+
+       *acl = newacl;
+       os_memcpy((*acl)[*num].addr, addr, ETH_ALEN);
+       os_memset(&(*acl)[*num].vlan_id, 0, sizeof((*acl)[*num].vlan_id));
+       (*acl)[*num].vlan_id.untagged = vlan_id;
+       (*acl)[*num].vlan_id.notempty = !!vlan_id;
+       (*num)++;
+
+       return 0;
+}
+
+
+void hostapd_remove_acl_mac(struct mac_acl_entry **acl, int *num,
+                           const u8 *addr)
+{
+       int i = 0;
+
+       while (i < *num) {
+               if (os_memcmp((*acl)[i].addr, addr, ETH_ALEN) == 0) {
+                       os_remove_in_array(*acl, *num, sizeof(**acl), i);
+                       (*num)--;
+               } else {
+                       i++;
+               }
+       }
+}
+
+
 static int hostapd_config_read_maclist(const char *fname,
                                       struct mac_acl_entry **acl, int *num)
 {
@@ -128,7 +177,6 @@ static int hostapd_config_read_maclist(const char *fname,
        char buf[128], *pos;
        int line = 0;
        u8 addr[ETH_ALEN];
-       struct mac_acl_entry *newacl;
        int vlan_id;
 
        f = fopen(fname, "r");
@@ -138,7 +186,7 @@ static int hostapd_config_read_maclist(const char *fname,
        }
 
        while (fgets(buf, sizeof(buf), f)) {
-               int i, rem = 0;
+               int rem = 0;
 
                line++;
 
@@ -168,16 +216,7 @@ static int hostapd_config_read_maclist(const char *fname,
                }
 
                if (rem) {
-                       i = 0;
-                       while (i < *num) {
-                               if (os_memcmp((*acl)[i].addr, addr, ETH_ALEN) ==
-                                   0) {
-                                       os_remove_in_array(*acl, *num,
-                                                          sizeof(**acl), i);
-                                       (*num)--;
-                               } else
-                                       i++;
-                       }
+                       hostapd_remove_acl_mac(acl, num, addr);
                        continue;
                }
                vlan_id = 0;
@@ -189,20 +228,10 @@ static int hostapd_config_read_maclist(const char *fname,
                if (*pos != '\0')
                        vlan_id = atoi(pos);
 
-               newacl = os_realloc_array(*acl, *num + 1, sizeof(**acl));
-               if (newacl == NULL) {
-                       wpa_printf(MSG_ERROR, "MAC list reallocation failed");
+               if (hostapd_add_acl_maclist(acl, num, vlan_id, addr) < 0) {
                        fclose(f);
                        return -1;
                }
-
-               *acl = newacl;
-               os_memcpy((*acl)[*num].addr, addr, ETH_ALEN);
-               os_memset(&(*acl)[*num].vlan_id, 0,
-                         sizeof((*acl)[*num].vlan_id));
-               (*acl)[*num].vlan_id.untagged = vlan_id;
-               (*acl)[*num].vlan_id.notempty = !!vlan_id;
-               (*num)++;
        }
 
        fclose(f);
@@ -215,6 +244,62 @@ static int hostapd_config_read_maclist(const char *fname,
 
 
 #ifdef EAP_SERVER
+
+static int hostapd_config_eap_user_salted(struct hostapd_eap_user *user,
+                                         const char *hash, size_t len,
+                                         char **pos, int line,
+                                         const char *fname)
+{
+       char *pos2 = *pos;
+
+       while (*pos2 != '\0' && *pos2 != ' ' && *pos2 != '\t' && *pos2 != '#')
+               pos2++;
+
+       if (pos2 - *pos < (int) (2 * (len + 1))) { /* at least 1 byte of salt */
+               wpa_printf(MSG_ERROR,
+                          "Invalid salted %s hash on line %d in '%s'",
+                          hash, line, fname);
+               return -1;
+       }
+
+       user->password = os_malloc(len);
+       if (!user->password) {
+               wpa_printf(MSG_ERROR,
+                          "Failed to allocate memory for salted %s hash",
+                          hash);
+               return -1;
+       }
+
+       if (hexstr2bin(*pos, user->password, len) < 0) {
+               wpa_printf(MSG_ERROR,
+                          "Invalid salted password on line %d in '%s'",
+                          line, fname);
+               return -1;
+       }
+       user->password_len = len;
+       *pos += 2 * len;
+
+       user->salt_len = (pos2 - *pos) / 2;
+       user->salt = os_malloc(user->salt_len);
+       if (!user->salt) {
+               wpa_printf(MSG_ERROR,
+                          "Failed to allocate memory for salted %s hash",
+                          hash);
+               return -1;
+       }
+
+       if (hexstr2bin(*pos, user->salt, user->salt_len) < 0) {
+               wpa_printf(MSG_ERROR,
+                          "Invalid salt for password on line %d in '%s'",
+                          line, fname);
+               return -1;
+       }
+
+       *pos = pos2;
+       return 0;
+}
+
+
 static int hostapd_config_read_eap_user(const char *fname,
                                        struct hostapd_bss_config *conf)
 {
@@ -466,6 +551,24 @@ static int hostapd_config_read_eap_user(const char *fname,
                        user->password_len = 16;
                        user->password_hash = 1;
                        pos = pos2;
+               } else if (os_strncmp(pos, "ssha1:", 6) == 0) {
+                       pos += 6;
+                       if (hostapd_config_eap_user_salted(user, "sha1", 20,
+                                                          &pos,
+                                                          line, fname) < 0)
+                               goto failed;
+               } else if (os_strncmp(pos, "ssha256:", 8) == 0) {
+                       pos += 8;
+                       if (hostapd_config_eap_user_salted(user, "sha256", 32,
+                                                          &pos,
+                                                          line, fname) < 0)
+                               goto failed;
+               } else if (os_strncmp(pos, "ssha512:", 8) == 0) {
+                       pos += 8;
+                       if (hostapd_config_eap_user_salted(user, "sha512", 64,
+                                                          &pos,
+                                                          line, fname) < 0)
+                               goto failed;
                } else {
                        pos2 = pos;
                        while (*pos2 != '\0' && *pos2 != ' ' &&
@@ -525,6 +628,7 @@ static int hostapd_config_read_eap_user(const char *fname,
 
        return ret;
 }
+
 #endif /* EAP_SERVER */
 
 
@@ -679,6 +783,10 @@ static int hostapd_config_parse_key_mgmt(int line, const char *value)
                        val |= WPA_KEY_MGMT_FT_PSK;
                else if (os_strcmp(start, "FT-EAP") == 0)
                        val |= WPA_KEY_MGMT_FT_IEEE8021X;
+#ifdef CONFIG_SHA384
+               else if (os_strcmp(start, "FT-EAP-SHA384") == 0)
+                       val |= WPA_KEY_MGMT_FT_IEEE8021X_SHA384;
+#endif /* CONFIG_SHA384 */
 #endif /* CONFIG_IEEE80211R_AP */
 #ifdef CONFIG_IEEE80211W
                else if (os_strcmp(start, "WPA-PSK-SHA256") == 0)
@@ -720,6 +828,10 @@ static int hostapd_config_parse_key_mgmt(int line, const char *value)
                else if (os_strcmp(start, "DPP") == 0)
                        val |= WPA_KEY_MGMT_DPP;
 #endif /* CONFIG_DPP */
+#ifdef CONFIG_HS20
+               else if (os_strcmp(start, "OSEN") == 0)
+                       val |= WPA_KEY_MGMT_OSEN;
+#endif /* CONFIG_HS20 */
                else {
                        wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'",
                                   line, start);
@@ -1136,6 +1248,8 @@ static int hostapd_config_ht_capab(struct hostapd_config *conf,
                conf->ht_capab |= HT_CAP_INFO_SUPP_CHANNEL_WIDTH_SET;
                conf->ht40_plus_minus_allowed = 1;
        }
+       if (!os_strstr(capab, "[HT40+]") && !os_strstr(capab, "[HT40-]"))
+               conf->secondary_channel = 0;
        if (os_strstr(capab, "[SMPS-STATIC]")) {
                conf->ht_capab &= ~HT_CAP_INFO_SMPS_MASK;
                conf->ht_capab |= HT_CAP_INFO_SMPS_STATIC;
@@ -1265,6 +1379,30 @@ static int hostapd_config_vht_capab(struct hostapd_config *conf,
 #endif /* CONFIG_IEEE80211AC */
 
 
+#ifdef CONFIG_IEEE80211AX
+
+static u8 find_bit_offset(u8 val)
+{
+       u8 res = 0;
+
+       for (; val; val >>= 1) {
+               if (val & 1)
+                       break;
+               res++;
+       }
+
+       return res;
+}
+
+
+static u8 set_he_cap(int val, u8 mask)
+{
+       return (u8) (mask & (val << find_bit_offset(mask)));
+}
+
+#endif /* CONFIG_IEEE80211AX */
+
+
 #ifdef CONFIG_INTERWORKING
 static int parse_roaming_consortium(struct hostapd_bss_config *bss, char *pos,
                                    int line)
@@ -1358,6 +1496,44 @@ static int parse_venue_name(struct hostapd_bss_config *bss, char *pos,
 }
 
 
+static int parse_venue_url(struct hostapd_bss_config *bss, char *pos,
+                           int line)
+{
+       char *sep;
+       size_t nlen;
+       struct hostapd_venue_url *url;
+       int ret = -1;
+
+       sep = os_strchr(pos, ':');
+       if (!sep)
+               goto fail;
+       *sep++ = '\0';
+
+       nlen = os_strlen(sep);
+       if (nlen > 254)
+               goto fail;
+
+       url = os_realloc_array(bss->venue_url, bss->venue_url_count + 1,
+                              sizeof(struct hostapd_venue_url));
+       if (!url)
+               goto fail;
+
+       bss->venue_url = url;
+       url = &bss->venue_url[bss->venue_url_count++];
+
+       url->venue_number = atoi(pos);
+       url->url_len = nlen;
+       os_memcpy(url->url, sep, nlen);
+
+       ret = 0;
+fail:
+       if (ret)
+               wpa_printf(MSG_ERROR, "Line %d: Invalid venue_url '%s'",
+                          line, pos);
+       return ret;
+}
+
+
 static int parse_3gpp_cell_net(struct hostapd_bss_config *bss, char *buf,
                               int line)
 {
@@ -1908,6 +2084,24 @@ static int hs20_parse_osu_nai(struct hostapd_bss_config *bss,
 }
 
 
+static int hs20_parse_osu_nai2(struct hostapd_bss_config *bss,
+                              char *pos, int line)
+{
+       if (bss->last_osu == NULL) {
+               wpa_printf(MSG_ERROR, "Line %d: Unexpected OSU field", line);
+               return -1;
+       }
+
+       os_free(bss->last_osu->osu_nai2);
+       bss->last_osu->osu_nai2 = os_strdup(pos);
+       if (bss->last_osu->osu_nai2 == NULL)
+               return -1;
+       bss->hs20_osu_providers_nai_count++;
+
+       return 0;
+}
+
+
 static int hs20_parse_osu_method_list(struct hostapd_bss_config *bss, char *pos,
                                      int line)
 {
@@ -1967,6 +2161,25 @@ static int hs20_parse_osu_service_desc(struct hostapd_bss_config *bss,
        return 0;
 }
 
+
+static int hs20_parse_operator_icon(struct hostapd_bss_config *bss, char *pos,
+                                   int line)
+{
+       char **n;
+
+       n = os_realloc_array(bss->hs20_operator_icon,
+                            bss->hs20_operator_icon_count + 1, sizeof(char *));
+       if (!n)
+               return -1;
+       bss->hs20_operator_icon = n;
+       bss->hs20_operator_icon[bss->hs20_operator_icon_count] = os_strdup(pos);
+       if (!bss->hs20_operator_icon[bss->hs20_operator_icon_count])
+               return -1;
+       bss->hs20_operator_icon_count++;
+
+       return 0;
+}
+
 #endif /* CONFIG_HS20 */
 
 
@@ -2065,24 +2278,104 @@ static unsigned int parse_tls_flags(const char *val)
 {
        unsigned int flags = 0;
 
+       /* Disable TLS v1.3 by default for now to avoid interoperability issue.
+        * This can be enabled by default once the implementation has been fully
+        * completed and tested with other implementations. */
+       flags |= TLS_CONN_DISABLE_TLSv1_3;
+
        if (os_strstr(val, "[ALLOW-SIGN-RSA-MD5]"))
                flags |= TLS_CONN_ALLOW_SIGN_RSA_MD5;
        if (os_strstr(val, "[DISABLE-TIME-CHECKS]"))
                flags |= TLS_CONN_DISABLE_TIME_CHECKS;
        if (os_strstr(val, "[DISABLE-TLSv1.0]"))
                flags |= TLS_CONN_DISABLE_TLSv1_0;
+       if (os_strstr(val, "[ENABLE-TLSv1.0]"))
+               flags |= TLS_CONN_ENABLE_TLSv1_0;
        if (os_strstr(val, "[DISABLE-TLSv1.1]"))
                flags |= TLS_CONN_DISABLE_TLSv1_1;
+       if (os_strstr(val, "[ENABLE-TLSv1.1]"))
+               flags |= TLS_CONN_ENABLE_TLSv1_1;
        if (os_strstr(val, "[DISABLE-TLSv1.2]"))
                flags |= TLS_CONN_DISABLE_TLSv1_2;
+       if (os_strstr(val, "[ENABLE-TLSv1.2]"))
+               flags |= TLS_CONN_ENABLE_TLSv1_2;
+       if (os_strstr(val, "[DISABLE-TLSv1.3]"))
+               flags |= TLS_CONN_DISABLE_TLSv1_3;
+       if (os_strstr(val, "[ENABLE-TLSv1.3]"))
+               flags &= ~TLS_CONN_DISABLE_TLSv1_3;
        if (os_strstr(val, "[SUITEB]"))
                flags |= TLS_CONN_SUITEB;
+       if (os_strstr(val, "[SUITEB-NO-ECDH]"))
+               flags |= TLS_CONN_SUITEB_NO_ECDH | TLS_CONN_SUITEB;
 
        return flags;
 }
 #endif /* EAP_SERVER */
 
 
+#ifdef CONFIG_SAE
+static int parse_sae_password(struct hostapd_bss_config *bss, const char *val)
+{
+       struct sae_password_entry *pw;
+       const char *pos = val, *pos2, *end = NULL;
+
+       pw = os_zalloc(sizeof(*pw));
+       if (!pw)
+               return -1;
+       os_memset(pw->peer_addr, 0xff, ETH_ALEN); /* default to wildcard */
+
+       pos2 = os_strstr(pos, "|mac=");
+       if (pos2) {
+               end = pos2;
+               pos2 += 5;
+               if (hwaddr_aton(pos2, pw->peer_addr) < 0)
+                       goto fail;
+               pos = pos2 + ETH_ALEN * 3 - 1;
+       }
+
+       pos2 = os_strstr(pos, "|vlanid=");
+       if (pos2) {
+               if (!end)
+                       end = pos2;
+               pos2 += 8;
+               pw->vlan_id = atoi(pos2);
+       }
+
+       pos2 = os_strstr(pos, "|id=");
+       if (pos2) {
+               if (!end)
+                       end = pos2;
+               pos2 += 4;
+               pw->identifier = os_strdup(pos2);
+               if (!pw->identifier)
+                       goto fail;
+       }
+
+       if (!end) {
+               pw->password = os_strdup(val);
+               if (!pw->password)
+                       goto fail;
+       } else {
+               pw->password = os_malloc(end - val + 1);
+               if (!pw->password)
+                       goto fail;
+               os_memcpy(pw->password, val, end - val);
+               pw->password[end - val] = '\0';
+       }
+
+       pw->next = bss->sae_passwords;
+       bss->sae_passwords = pw;
+
+       return 0;
+fail:
+       str_clear_free(pw->password);
+       os_free(pw->identifier);
+       os_free(pw);
+       return -1;
+}
+#endif /* CONFIG_SAE */
+
+
 static int hostapd_config_fill(struct hostapd_config *conf,
                               struct hostapd_bss_config *bss,
                               const char *buf, char *pos, int line)
@@ -2234,6 +2527,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
                bss->private_key_passwd = os_strdup(pos);
        } else if (os_strcmp(buf, "check_crl") == 0) {
                bss->check_crl = atoi(pos);
+       } else if (os_strcmp(buf, "check_crl_strict") == 0) {
+               bss->check_crl_strict = atoi(pos);
+       } else if (os_strcmp(buf, "crl_reload_interval") == 0) {
+               bss->crl_reload_interval = atoi(pos);
        } else if (os_strcmp(buf, "tls_session_lifetime") == 0) {
                bss->tls_session_lifetime = atoi(pos);
        } else if (os_strcmp(buf, "tls_flags") == 0) {
@@ -2250,6 +2547,9 @@ static int hostapd_config_fill(struct hostapd_config *conf,
        } else if (os_strcmp(buf, "openssl_ciphers") == 0) {
                os_free(bss->openssl_ciphers);
                bss->openssl_ciphers = os_strdup(pos);
+       } else if (os_strcmp(buf, "openssl_ecdh_curves") == 0) {
+               os_free(bss->openssl_ecdh_curves);
+               bss->openssl_ecdh_curves = os_strdup(pos);
        } else if (os_strcmp(buf, "fragment_size") == 0) {
                bss->fragment_size = atoi(pos);
 #ifdef EAP_SERVER_FAST
@@ -2312,8 +2612,10 @@ static int hostapd_config_fill(struct hostapd_config *conf,
        } else if (os_strcmp(buf, "pwd_group") == 0) {
                bss->pwd_group = atoi(pos);
 #endif /* EAP_SERVER_PWD */
+#ifdef CONFIG_ERP
        } else if (os_strcmp(buf, "eap_server_erp") == 0) {
                bss->eap_server_erp = atoi(pos);
+#endif /* CONFIG_ERP */
 #endif /* EAP_SERVER */
        } else if (os_strcmp(buf, "eap_message") == 0) {
                char *term;
@@ -2568,6 +2870,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
                        return 1;
                }
                bss->wpa_pairwise_update_count = (u32) val;
+       } else if (os_strcmp(buf, "wpa_disable_eapol_key_retries") == 0) {
+               bss->wpa_disable_eapol_key_retries = atoi(pos);
        } else if (os_strcmp(buf, "wpa_passphrase") == 0) {
                int len = os_strlen(pos);
                if (len < 8 || len > 63) {
@@ -2626,7 +2930,7 @@ static int hostapd_config_fill(struct hostapd_config *conf,
                if (bss->wpa_pairwise &
                    (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) {
                        wpa_printf(MSG_ERROR, "Line %d: unsupported pairwise cipher suite '%s'",
-                                  bss->wpa_pairwise, pos);
+                                  line, pos);
                        return 1;
                }
        } else if (os_strcmp(buf, "rsn_pairwise") == 0) {
@@ -2636,7 +2940,21 @@ static int hostapd_config_fill(struct hostapd_config *conf,
                if (bss->rsn_pairwise &
                    (WPA_CIPHER_NONE | WPA_CIPHER_WEP40 | WPA_CIPHER_WEP104)) {
                        wpa_printf(MSG_ERROR, "Line %d: unsupported pairwise cipher suite '%s'",
-                                  bss->rsn_pairwise, pos);
+                                  line, pos);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "group_cipher") == 0) {
+               bss->group_cipher = hostapd_config_parse_cipher(line, pos);
+               if (bss->group_cipher == -1 || bss->group_cipher == 0)
+                       return 1;
+               if (bss->group_cipher != WPA_CIPHER_TKIP &&
+                   bss->group_cipher != WPA_CIPHER_CCMP &&
+                   bss->group_cipher != WPA_CIPHER_GCMP &&
+                   bss->group_cipher != WPA_CIPHER_GCMP_256 &&
+                   bss->group_cipher != WPA_CIPHER_CCMP_256) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: unsupported group cipher suite '%s'",
+                                  line, pos);
                        return 1;
                }
 #ifdef CONFIG_RSN_PREAUTH
@@ -2646,10 +2964,9 @@ static int hostapd_config_fill(struct hostapd_config *conf,
                os_free(bss->rsn_preauth_interfaces);
                bss->rsn_preauth_interfaces = os_strdup(pos);
 #endif /* CONFIG_RSN_PREAUTH */
-#ifdef CONFIG_PEERKEY
        } else if (os_strcmp(buf, "peerkey") == 0) {
-               bss->peerkey = atoi(pos);
-#endif /* CONFIG_PEERKEY */
+               wpa_printf(MSG_INFO,
+                          "Line %d: Obsolete peerkey parameter ignored", line);
 #ifdef CONFIG_IEEE80211R_AP
        } else if (os_strcmp(buf, "mobility_domain") == 0) {
                if (os_strlen(pos) != 2 * MOBILITY_DOMAIN_ID_LEN ||
@@ -2669,7 +2986,12 @@ static int hostapd_config_fill(struct hostapd_config *conf,
                        return 1;
                }
        } else if (os_strcmp(buf, "r0_key_lifetime") == 0) {
+               /* DEPRECATED: Use ft_r0_key_lifetime instead. */
+               bss->r0_key_lifetime = atoi(pos) * 60;
+       } else if (os_strcmp(buf, "ft_r0_key_lifetime") == 0) {
                bss->r0_key_lifetime = atoi(pos);
+       } else if (os_strcmp(buf, "r1_max_key_lifetime") == 0) {
+               bss->r1_max_key_lifetime = atoi(pos);
        } else if (os_strcmp(buf, "reassociation_deadline") == 0) {
                bss->reassociation_deadline = atoi(pos);
        } else if (os_strcmp(buf, "rkh_pos_timeout") == 0) {
@@ -2837,14 +3159,25 @@ static int hostapd_config_fill(struct hostapd_config *conf,
                }
                bss->dtim_period = val;
        } else if (os_strcmp(buf, "bss_load_update_period") == 0) {
-               bss->bss_load_update_period = atoi(pos);
-               if (bss->bss_load_update_period < 0 ||
-                   bss->bss_load_update_period > 100) {
+               int val = atoi(pos);
+
+               if (val < 0 || val > 100) {
                        wpa_printf(MSG_ERROR,
                                   "Line %d: invalid bss_load_update_period %d",
-                                  line, bss->bss_load_update_period);
+                                  line, val);
+                       return 1;
+               }
+               bss->bss_load_update_period = val;
+       } else if (os_strcmp(buf, "chan_util_avg_period") == 0) {
+               int val = atoi(pos);
+
+               if (val < 0) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: invalid chan_util_avg_period",
+                                  line);
                        return 1;
                }
+               bss->chan_util_avg_period = val;
        } else if (os_strcmp(buf, "rts_threshold") == 0) {
                conf->rts_threshold = atoi(pos);
                if (conf->rts_threshold < -1 || conf->rts_threshold > 65535) {
@@ -2871,7 +3204,7 @@ static int hostapd_config_fill(struct hostapd_config *conf,
                                   line, val);
                        return 1;
                }
-               conf->send_probe_response = val;
+               bss->send_probe_response = val;
        } else if (os_strcmp(buf, "supported_rates") == 0) {
                if (hostapd_parse_intlist(&conf->supported_rates, pos)) {
                        wpa_printf(MSG_ERROR, "Line %d: invalid rate list",
@@ -3039,6 +3372,12 @@ static int hostapd_config_fill(struct hostapd_config *conf,
                        return 1;
                }
 #endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_OCV
+       } else if (os_strcmp(buf, "ocv") == 0) {
+               bss->ocv = atoi(pos);
+               if (bss->ocv && !bss->ieee80211w)
+                       bss->ieee80211w = 1;
+#endif /* CONFIG_OCV */
 #ifdef CONFIG_IEEE80211N
        } else if (os_strcmp(buf, "ieee80211n") == 0) {
                conf->ieee80211n = atoi(pos);
@@ -3092,6 +3431,90 @@ static int hostapd_config_fill(struct hostapd_config *conf,
                conf->he_op.he_twt_required = atoi(pos);
        } else if (os_strcmp(buf, "he_rts_threshold") == 0) {
                conf->he_op.he_rts_threshold = atoi(pos);
+       } else if (os_strcmp(buf, "he_mu_edca_qos_info_param_count") == 0) {
+               conf->he_mu_edca.he_qos_info |=
+                       set_he_cap(atoi(pos), HE_QOS_INFO_EDCA_PARAM_SET_COUNT);
+       } else if (os_strcmp(buf, "he_mu_edca_qos_info_q_ack") == 0) {
+               conf->he_mu_edca.he_qos_info |=
+                       set_he_cap(atoi(pos), HE_QOS_INFO_Q_ACK);
+       } else if (os_strcmp(buf, "he_mu_edca_qos_info_queue_request") == 0) {
+               conf->he_mu_edca.he_qos_info |=
+                       set_he_cap(atoi(pos), HE_QOS_INFO_QUEUE_REQUEST);
+       } else if (os_strcmp(buf, "he_mu_edca_qos_info_txop_request") == 0) {
+               conf->he_mu_edca.he_qos_info |=
+                       set_he_cap(atoi(pos), HE_QOS_INFO_TXOP_REQUEST);
+       } else if (os_strcmp(buf, "he_mu_edca_ac_be_aifsn") == 0) {
+               conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_ACI_IDX] |=
+                       set_he_cap(atoi(pos), HE_MU_AC_PARAM_AIFSN);
+       } else if (os_strcmp(buf, "he_mu_edca_ac_be_acm") == 0) {
+               conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_ACI_IDX] |=
+                       set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACM);
+       } else if (os_strcmp(buf, "he_mu_edca_ac_be_aci") == 0) {
+               conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_ACI_IDX] |=
+                       set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACI);
+       } else if (os_strcmp(buf, "he_mu_edca_ac_be_ecwmin") == 0) {
+               conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_ECW_IDX] |=
+                       set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMIN);
+       } else if (os_strcmp(buf, "he_mu_edca_ac_be_ecwmax") == 0) {
+               conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_ECW_IDX] |=
+                       set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMAX);
+       } else if (os_strcmp(buf, "he_mu_edca_ac_be_timer") == 0) {
+               conf->he_mu_edca.he_mu_ac_be_param[HE_MU_AC_PARAM_TIMER_IDX] =
+                       atoi(pos) & 0xff;
+       } else if (os_strcmp(buf, "he_mu_edca_ac_bk_aifsn") == 0) {
+               conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_ACI_IDX] |=
+                       set_he_cap(atoi(pos), HE_MU_AC_PARAM_AIFSN);
+       } else if (os_strcmp(buf, "he_mu_edca_ac_bk_acm") == 0) {
+               conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_ACI_IDX] |=
+                       set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACM);
+       } else if (os_strcmp(buf, "he_mu_edca_ac_bk_aci") == 0) {
+               conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_ACI_IDX] |=
+                       set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACI);
+       } else if (os_strcmp(buf, "he_mu_edca_ac_bk_ecwmin") == 0) {
+               conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_ECW_IDX] |=
+                       set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMIN);
+       } else if (os_strcmp(buf, "he_mu_edca_ac_bk_ecwmax") == 0) {
+               conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_ECW_IDX] |=
+                       set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMAX);
+       } else if (os_strcmp(buf, "he_mu_edca_ac_bk_timer") == 0) {
+               conf->he_mu_edca.he_mu_ac_bk_param[HE_MU_AC_PARAM_TIMER_IDX] =
+                       atoi(pos) & 0xff;
+       } else if (os_strcmp(buf, "he_mu_edca_ac_vi_aifsn") == 0) {
+               conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_ACI_IDX] |=
+                       set_he_cap(atoi(pos), HE_MU_AC_PARAM_AIFSN);
+       } else if (os_strcmp(buf, "he_mu_edca_ac_vi_acm") == 0) {
+               conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_ACI_IDX] |=
+                       set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACM);
+       } else if (os_strcmp(buf, "he_mu_edca_ac_vi_aci") == 0) {
+               conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_ACI_IDX] |=
+                       set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACI);
+       } else if (os_strcmp(buf, "he_mu_edca_ac_vi_ecwmin") == 0) {
+               conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_ECW_IDX] |=
+                       set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMIN);
+       } else if (os_strcmp(buf, "he_mu_edca_ac_vi_ecwmax") == 0) {
+               conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_ECW_IDX] |=
+                       set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMAX);
+       } else if (os_strcmp(buf, "he_mu_edca_ac_vi_timer") == 0) {
+               conf->he_mu_edca.he_mu_ac_vi_param[HE_MU_AC_PARAM_TIMER_IDX] =
+                       atoi(pos) & 0xff;
+       } else if (os_strcmp(buf, "he_mu_edca_ac_vo_aifsn") == 0) {
+               conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_ACI_IDX] |=
+                       set_he_cap(atoi(pos), HE_MU_AC_PARAM_AIFSN);
+       } else if (os_strcmp(buf, "he_mu_edca_ac_vo_acm") == 0) {
+               conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_ACI_IDX] |=
+                       set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACM);
+       } else if (os_strcmp(buf, "he_mu_edca_ac_vo_aci") == 0) {
+               conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_ACI_IDX] |=
+                       set_he_cap(atoi(pos), HE_MU_AC_PARAM_ACI);
+       } else if (os_strcmp(buf, "he_mu_edca_ac_vo_ecwmin") == 0) {
+               conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_ECW_IDX] |=
+                       set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMIN);
+       } else if (os_strcmp(buf, "he_mu_edca_ac_vo_ecwmax") == 0) {
+               conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_ECW_IDX] |=
+                       set_he_cap(atoi(pos), HE_MU_AC_PARAM_ECWMAX);
+       } else if (os_strcmp(buf, "he_mu_edca_ac_vo_timer") == 0) {
+               conf->he_mu_edca.he_mu_ac_vo_param[HE_MU_AC_PARAM_TIMER_IDX] =
+                       atoi(pos) & 0xff;
 #endif /* CONFIG_IEEE80211AX */
        } else if (os_strcmp(buf, "max_listen_interval") == 0) {
                bss->max_listen_interval = atoi(pos);
@@ -3189,6 +3612,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
                }
        } else if (os_strcmp(buf, "wps_cred_processing") == 0) {
                bss->wps_cred_processing = atoi(pos);
+       } else if (os_strcmp(buf, "wps_cred_add_sae") == 0) {
+               bss->wps_cred_add_sae = atoi(pos);
        } else if (os_strcmp(buf, "ap_settings") == 0) {
                os_free(bss->ap_settings);
                bss->ap_settings =
@@ -3198,6 +3623,56 @@ static int hostapd_config_fill(struct hostapd_config *conf,
                                   line, pos);
                        return 1;
                }
+       } else if (os_strcmp(buf, "multi_ap_backhaul_ssid") == 0) {
+               size_t slen;
+               char *str = wpa_config_parse_string(pos, &slen);
+
+               if (!str || slen < 1 || slen > SSID_MAX_LEN) {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'",
+                                  line, pos);
+                       os_free(str);
+                       return 1;
+               }
+               os_memcpy(bss->multi_ap_backhaul_ssid.ssid, str, slen);
+               bss->multi_ap_backhaul_ssid.ssid_len = slen;
+               bss->multi_ap_backhaul_ssid.ssid_set = 1;
+               os_free(str);
+       } else if (os_strcmp(buf, "multi_ap_backhaul_wpa_passphrase") == 0) {
+               int len = os_strlen(pos);
+
+               if (len < 8 || len > 63) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: invalid WPA passphrase length %d (expected 8..63)",
+                                  line, len);
+                       return 1;
+               }
+               os_free(bss->multi_ap_backhaul_ssid.wpa_passphrase);
+               bss->multi_ap_backhaul_ssid.wpa_passphrase = os_strdup(pos);
+               if (bss->multi_ap_backhaul_ssid.wpa_passphrase) {
+                       hostapd_config_clear_wpa_psk(
+                               &bss->multi_ap_backhaul_ssid.wpa_psk);
+                       bss->multi_ap_backhaul_ssid.wpa_passphrase_set = 1;
+               }
+       } else if (os_strcmp(buf, "multi_ap_backhaul_wpa_psk") == 0) {
+               hostapd_config_clear_wpa_psk(
+                       &bss->multi_ap_backhaul_ssid.wpa_psk);
+               bss->multi_ap_backhaul_ssid.wpa_psk =
+                       os_zalloc(sizeof(struct hostapd_wpa_psk));
+               if (!bss->multi_ap_backhaul_ssid.wpa_psk)
+                       return 1;
+               if (hexstr2bin(pos, bss->multi_ap_backhaul_ssid.wpa_psk->psk,
+                              PMK_LEN) ||
+                   pos[PMK_LEN * 2] != '\0') {
+                       wpa_printf(MSG_ERROR, "Line %d: Invalid PSK '%s'.",
+                                  line, pos);
+                       hostapd_config_clear_wpa_psk(
+                               &bss->multi_ap_backhaul_ssid.wpa_psk);
+                       return 1;
+               }
+               bss->multi_ap_backhaul_ssid.wpa_psk->group = 1;
+               os_free(bss->multi_ap_backhaul_ssid.wpa_passphrase);
+               bss->multi_ap_backhaul_ssid.wpa_passphrase = NULL;
+               bss->multi_ap_backhaul_ssid.wpa_psk_set = 1;
        } else if (os_strcmp(buf, "upnp_iface") == 0) {
                os_free(bss->upnp_iface);
                bss->upnp_iface = os_strdup(pos);
@@ -3290,6 +3765,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
 #ifdef CONFIG_WNM_AP
        } else if (os_strcmp(buf, "wnm_sleep_mode") == 0) {
                bss->wnm_sleep_mode = atoi(pos);
+       } else if (os_strcmp(buf, "wnm_sleep_mode_no_keys") == 0) {
+               bss->wnm_sleep_mode_no_keys = atoi(pos);
        } else if (os_strcmp(buf, "bss_transition") == 0) {
                bss->bss_transition = atoi(pos);
 #endif /* CONFIG_WNM_AP */
@@ -3330,6 +3807,9 @@ static int hostapd_config_fill(struct hostapd_config *conf,
        } else if (os_strcmp(buf, "venue_name") == 0) {
                if (parse_venue_name(bss, pos, line) < 0)
                        return 1;
+       } else if (os_strcmp(buf, "venue_url") == 0) {
+               if (parse_venue_url(bss, pos, line) < 0)
+                       return 1;
        } else if (os_strcmp(buf, "network_auth_type") == 0) {
                u8 auth_type;
                u16 redirect_url_len;
@@ -3435,6 +3915,16 @@ static int hostapd_config_fill(struct hostapd_config *conf,
 #ifdef CONFIG_HS20
        } else if (os_strcmp(buf, "hs20") == 0) {
                bss->hs20 = atoi(pos);
+       } else if (os_strcmp(buf, "hs20_release") == 0) {
+               int val = atoi(pos);
+
+               if (val < 1 || val > (HS20_VERSION >> 4) + 1) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: Unsupported hs20_release: %s",
+                                  line, pos);
+                       return 1;
+               }
+               bss->hs20_release = val;
        } else if (os_strcmp(buf, "disable_dgaf") == 0) {
                bss->disable_dgaf = atoi(pos);
        } else if (os_strcmp(buf, "na_mcast_to_ucast") == 0) {
@@ -3497,6 +3987,9 @@ static int hostapd_config_fill(struct hostapd_config *conf,
        } else if (os_strcmp(buf, "osu_nai") == 0) {
                if (hs20_parse_osu_nai(bss, pos, line) < 0)
                        return 1;
+       } else if (os_strcmp(buf, "osu_nai2") == 0) {
+               if (hs20_parse_osu_nai2(bss, pos, line) < 0)
+                       return 1;
        } else if (os_strcmp(buf, "osu_method_list") == 0) {
                if (hs20_parse_osu_method_list(bss, pos, line) < 0)
                        return 1;
@@ -3506,11 +3999,25 @@ static int hostapd_config_fill(struct hostapd_config *conf,
        } else if (os_strcmp(buf, "osu_service_desc") == 0) {
                if (hs20_parse_osu_service_desc(bss, pos, line) < 0)
                        return 1;
+       } else if (os_strcmp(buf, "operator_icon") == 0) {
+               if (hs20_parse_operator_icon(bss, pos, line) < 0)
+                       return 1;
        } else if (os_strcmp(buf, "subscr_remediation_url") == 0) {
                os_free(bss->subscr_remediation_url);
                bss->subscr_remediation_url = os_strdup(pos);
        } else if (os_strcmp(buf, "subscr_remediation_method") == 0) {
                bss->subscr_remediation_method = atoi(pos);
+       } else if (os_strcmp(buf, "hs20_t_c_filename") == 0) {
+               os_free(bss->t_c_filename);
+               bss->t_c_filename = os_strdup(pos);
+       } else if (os_strcmp(buf, "hs20_t_c_timestamp") == 0) {
+               bss->t_c_timestamp = strtol(pos, NULL, 0);
+       } else if (os_strcmp(buf, "hs20_t_c_server_url") == 0) {
+               os_free(bss->t_c_server_url);
+               bss->t_c_server_url = os_strdup(pos);
+       } else if (os_strcmp(buf, "hs20_sim_provisioning_url") == 0) {
+               os_free(bss->hs20_sim_provisioning_url);
+               bss->hs20_sim_provisioning_url = os_strdup(pos);
 #endif /* CONFIG_HS20 */
 #ifdef CONFIG_MBO
        } else if (os_strcmp(buf, "mbo") == 0) {
@@ -3593,6 +4100,14 @@ static int hostapd_config_fill(struct hostapd_config *conf,
                wpabuf_free(bss->sae_commit_override);
                bss->sae_commit_override = wpabuf_parse_bin(pos);
 #endif /* CONFIG_TESTING_OPTIONS */
+#ifdef CONFIG_SAE
+       } else if (os_strcmp(buf, "sae_password") == 0) {
+               if (parse_sae_password(bss, pos) < 0) {
+                       wpa_printf(MSG_ERROR, "Line %d: Invalid sae_password",
+                                  line);
+                       return 1;
+               }
+#endif /* CONFIG_SAE */
        } else if (os_strcmp(buf, "vendor_elements") == 0) {
                if (parse_wpabuf_hex(line, buf, &bss->vendor_elements, pos))
                        return 1;
@@ -3601,6 +4116,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
                        return 1;
        } else if (os_strcmp(buf, "sae_anti_clogging_threshold") == 0) {
                bss->sae_anti_clogging_threshold = atoi(pos);
+       } else if (os_strcmp(buf, "sae_sync") == 0) {
+               bss->sae_sync = atoi(pos);
        } else if (os_strcmp(buf, "sae_groups") == 0) {
                if (hostapd_parse_intlist(&bss->sae_groups, pos)) {
                        wpa_printf(MSG_ERROR,
@@ -3608,6 +4125,8 @@ static int hostapd_config_fill(struct hostapd_config *conf,
                                   line, pos);
                        return 1;
                }
+       } else if (os_strcmp(buf, "sae_require_mfp") == 0) {
+               bss->sae_require_mfp = atoi(pos);
        } else if (os_strcmp(buf, "local_pwr_constraint") == 0) {
                int val = atoi(pos);
                if (val < 0 || val > 255) {
@@ -3768,9 +4287,57 @@ static int hostapd_config_fill(struct hostapd_config *conf,
        } else if (os_strcmp(buf, "dpp_csign") == 0) {
                if (parse_wpabuf_hex(line, buf, &bss->dpp_csign, pos))
                        return 1;
-       } else if (os_strcmp(buf, "dpp_csign_expiry") == 0) {
-               bss->dpp_csign_expiry = strtol(pos, NULL, 0);
 #endif /* CONFIG_DPP */
+#ifdef CONFIG_OWE
+       } else if (os_strcmp(buf, "owe_transition_bssid") == 0) {
+               if (hwaddr_aton(pos, bss->owe_transition_bssid)) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: invalid owe_transition_bssid",
+                                  line);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "owe_transition_ssid") == 0) {
+               size_t slen;
+               char *str = wpa_config_parse_string(pos, &slen);
+
+               if (!str || slen < 1 || slen > SSID_MAX_LEN) {
+                       wpa_printf(MSG_ERROR, "Line %d: invalid SSID '%s'",
+                                  line, pos);
+                       os_free(str);
+                       return 1;
+               }
+               os_memcpy(bss->owe_transition_ssid, str, slen);
+               bss->owe_transition_ssid_len = slen;
+               os_free(str);
+       } else if (os_strcmp(buf, "owe_transition_ifname") == 0) {
+               os_strlcpy(bss->owe_transition_ifname, pos,
+                          sizeof(bss->owe_transition_ifname));
+       } else if (os_strcmp(buf, "owe_groups") == 0) {
+               if (hostapd_parse_intlist(&bss->owe_groups, pos)) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: Invalid owe_groups value '%s'",
+                                  line, pos);
+                       return 1;
+               }
+       } else if (os_strcmp(buf, "coloc_intf_reporting") == 0) {
+               bss->coloc_intf_reporting = atoi(pos);
+#endif /* CONFIG_OWE */
+       } else if (os_strcmp(buf, "multi_ap") == 0) {
+               int val = atoi(pos);
+
+               if (val < 0 || val > 3) {
+                       wpa_printf(MSG_ERROR, "Line %d: Invalid multi_ap '%s'",
+                                  line, buf);
+                       return -1;
+               }
+
+               bss->multi_ap = val;
+       } else if (os_strcmp(buf, "rssi_reject_assoc_rssi") == 0) {
+               conf->rssi_reject_assoc_rssi = atoi(pos);
+       } else if (os_strcmp(buf, "rssi_reject_assoc_timeout") == 0) {
+               conf->rssi_reject_assoc_timeout = atoi(pos);
+       } else if (os_strcmp(buf, "pbss") == 0) {
+               bss->pbss = atoi(pos);
        } else {
                wpa_printf(MSG_ERROR,
                           "Line %d: unknown configuration item '%s'",