]> git.ipfire.org Git - thirdparty/hostap.git/blobdiff - wpa_supplicant/config.c
wpa_supplicant configuration for Beacon protection
[thirdparty/hostap.git] / wpa_supplicant / config.c
index d6bdb33c905c051767471fb371b7baf2abebfea9..9dad4ff7231d9667313de1de2d3e5bc80851aa29 100644 (file)
@@ -1,6 +1,6 @@
 /*
  * WPA Supplicant / Configuration parser and common functions
- * Copyright (c) 2003-2015, Jouni Malinen <j@w1.fi>
+ * Copyright (c) 2003-2019, Jouni Malinen <j@w1.fi>
  *
  * This software may be distributed under the terms of the BSD license.
  * See README for more details.
@@ -11,6 +11,8 @@
 #include "common.h"
 #include "utils/uuid.h"
 #include "utils/ip_addr.h"
+#include "common/ieee802_1x_defs.h"
+#include "common/sae.h"
 #include "crypto/sha1.h"
 #include "rsn_supp/wpa.h"
 #include "eap_peer/eap.h"
@@ -32,7 +34,11 @@ struct parse_data {
        /* Configuration variable name */
        char *name;
 
-       /* Parser function for this variable */
+       /* Parser function for this variable. The parser functions return 0 or 1
+        * to indicate success. Value 0 indicates that the parameter value may
+        * have changed while value 1 means that the value did not change.
+        * Error cases (failure to parse the string) are indicated by returning
+        * -1. */
        int (*parser)(const struct parse_data *data, struct wpa_ssid *ssid,
                      int line, const char *value);
 
@@ -59,7 +65,7 @@ static int wpa_config_parse_str(const struct parse_data *data,
                                struct wpa_ssid *ssid,
                                int line, const char *value)
 {
-       size_t res_len, *dst_len;
+       size_t res_len, *dst_len, prev_len;
        char **dst, *tmp;
 
        if (os_strcmp(value, "NULL") == 0) {
@@ -105,6 +111,21 @@ static int wpa_config_parse_str(const struct parse_data *data,
 set:
        dst = (char **) (((u8 *) ssid) + (long) data->param1);
        dst_len = (size_t *) (((u8 *) ssid) + (long) data->param2);
+
+       if (data->param2)
+               prev_len = *dst_len;
+       else if (*dst)
+               prev_len = os_strlen(*dst);
+       else
+               prev_len = 0;
+       if ((*dst == NULL && tmp == NULL) ||
+           (*dst && tmp && prev_len == res_len &&
+            os_memcmp(*dst, tmp, res_len) == 0)) {
+               /* No change to the previously configured value */
+               os_free(tmp);
+               return 1;
+       }
+
        os_free(*dst);
        *dst = tmp;
        if (data->param2)
@@ -190,6 +211,9 @@ static int wpa_config_parse_int(const struct parse_data *data,
                           line, value);
                return -1;
        }
+
+       if (*dst == val)
+               return 1;
        *dst = val;
        wpa_printf(MSG_MSGDUMP, "%s=%d (0x%x)", data->name, *dst, *dst);
 
@@ -374,6 +398,50 @@ static char * wpa_config_write_bssid(const struct parse_data *data,
 #endif /* NO_CONFIG_WRITE */
 
 
+static int wpa_config_parse_bssid_hint(const struct parse_data *data,
+                                      struct wpa_ssid *ssid, int line,
+                                      const char *value)
+{
+       if (value[0] == '\0' || os_strcmp(value, "\"\"") == 0 ||
+           os_strcmp(value, "any") == 0) {
+               ssid->bssid_hint_set = 0;
+               wpa_printf(MSG_MSGDUMP, "BSSID hint any");
+               return 0;
+       }
+       if (hwaddr_aton(value, ssid->bssid_hint)) {
+               wpa_printf(MSG_ERROR, "Line %d: Invalid BSSID hint '%s'.",
+                          line, value);
+               return -1;
+       }
+       ssid->bssid_hint_set = 1;
+       wpa_hexdump(MSG_MSGDUMP, "BSSID hint", ssid->bssid_hint, ETH_ALEN);
+       return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_bssid_hint(const struct parse_data *data,
+                                         struct wpa_ssid *ssid)
+{
+       char *value;
+       int res;
+
+       if (!ssid->bssid_hint_set)
+               return NULL;
+
+       value = os_malloc(20);
+       if (!value)
+               return NULL;
+       res = os_snprintf(value, 20, MACSTR, MAC2STR(ssid->bssid_hint));
+       if (os_snprintf_error(20, res)) {
+               os_free(value);
+               return NULL;
+       }
+       return value;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
 static int wpa_config_parse_bssid_blacklist(const struct parse_data *data,
                                            struct wpa_ssid *ssid, int line,
                                            const char *value)
@@ -456,9 +524,17 @@ static int wpa_config_parse_psk(const struct parse_data *data,
                }
                wpa_hexdump_ascii_key(MSG_MSGDUMP, "PSK (ASCII passphrase)",
                                      (u8 *) value, len);
+               if (has_ctrl_char((u8 *) value, len)) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: Invalid passphrase character",
+                                  line);
+                       return -1;
+               }
                if (ssid->passphrase && os_strlen(ssid->passphrase) == len &&
-                   os_memcmp(ssid->passphrase, value, len) == 0)
-                       return 0;
+                   os_memcmp(ssid->passphrase, value, len) == 0) {
+                       /* No change to the previously configured value */
+                       return 1;
+               }
                ssid->psk_set = 0;
                str_clear_free(ssid->passphrase);
                ssid->passphrase = dup_binstr(value, len);
@@ -569,6 +645,8 @@ static int wpa_config_parse_proto(const struct parse_data *data,
                errors++;
        }
 
+       if (!errors && ssid->proto == val)
+               return 1;
        wpa_printf(MSG_MSGDUMP, "proto: 0x%x", val);
        ssid->proto = val;
        return errors ? -1 : 0;
@@ -658,13 +736,15 @@ static int wpa_config_parse_key_mgmt(const struct parse_data *data,
                        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 */
-#ifdef CONFIG_IEEE80211W
                else if (os_strcmp(start, "WPA-PSK-SHA256") == 0)
                        val |= WPA_KEY_MGMT_PSK_SHA256;
                else if (os_strcmp(start, "WPA-EAP-SHA256") == 0)
                        val |= WPA_KEY_MGMT_IEEE8021X_SHA256;
-#endif /* CONFIG_IEEE80211W */
 #ifdef CONFIG_WPS
                else if (os_strcmp(start, "WPS") == 0)
                        val |= WPA_KEY_MGMT_WPS;
@@ -687,6 +767,26 @@ static int wpa_config_parse_key_mgmt(const struct parse_data *data,
                else if (os_strcmp(start, "WPA-EAP-SUITE-B-192") == 0)
                        val |= WPA_KEY_MGMT_IEEE8021X_SUITE_B_192;
 #endif /* CONFIG_SUITEB192 */
+#ifdef CONFIG_FILS
+               else if (os_strcmp(start, "FILS-SHA256") == 0)
+                       val |= WPA_KEY_MGMT_FILS_SHA256;
+               else if (os_strcmp(start, "FILS-SHA384") == 0)
+                       val |= WPA_KEY_MGMT_FILS_SHA384;
+#ifdef CONFIG_IEEE80211R
+               else if (os_strcmp(start, "FT-FILS-SHA256") == 0)
+                       val |= WPA_KEY_MGMT_FT_FILS_SHA256;
+               else if (os_strcmp(start, "FT-FILS-SHA384") == 0)
+                       val |= WPA_KEY_MGMT_FT_FILS_SHA384;
+#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_FILS */
+#ifdef CONFIG_OWE
+               else if (os_strcmp(start, "OWE") == 0)
+                       val |= WPA_KEY_MGMT_OWE;
+#endif /* CONFIG_OWE */
+#ifdef CONFIG_DPP
+               else if (os_strcmp(start, "DPP") == 0)
+                       val |= WPA_KEY_MGMT_DPP;
+#endif /* CONFIG_DPP */
                else {
                        wpa_printf(MSG_ERROR, "Line %d: invalid key_mgmt '%s'",
                                   line, start);
@@ -705,6 +805,8 @@ static int wpa_config_parse_key_mgmt(const struct parse_data *data,
                errors++;
        }
 
+       if (!errors && ssid->key_mgmt == val)
+               return 1;
        wpa_printf(MSG_MSGDUMP, "key_mgmt: 0x%x", val);
        ssid->key_mgmt = val;
        return errors ? -1 : 0;
@@ -793,9 +895,20 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data,
                }
                pos += ret;
        }
+
+#ifdef CONFIG_SHA384
+       if (ssid->key_mgmt & WPA_KEY_MGMT_FT_IEEE8021X_SHA384) {
+               ret = os_snprintf(pos, end - pos, "%sFT-EAP-SHA384",
+                                 pos == buf ? "" : " ");
+               if (os_snprintf_error(end - pos, ret)) {
+                       end[-1] = '\0';
+                       return buf;
+               }
+               pos += ret;
+       }
+#endif /* CONFIG_SHA384 */
 #endif /* CONFIG_IEEE80211R */
 
-#ifdef CONFIG_IEEE80211W
        if (ssid->key_mgmt & WPA_KEY_MGMT_PSK_SHA256) {
                ret = os_snprintf(pos, end - pos, "%sWPA-PSK-SHA256",
                                  pos == buf ? "" : " ");
@@ -815,7 +928,6 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data,
                }
                pos += ret;
        }
-#endif /* CONFIG_IEEE80211W */
 
 #ifdef CONFIG_WPS
        if (ssid->key_mgmt & WPA_KEY_MGMT_WPS) {
@@ -887,6 +999,71 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data,
        }
 #endif /* CONFIG_SUITEB192 */
 
+#ifdef CONFIG_FILS
+       if (ssid->key_mgmt & WPA_KEY_MGMT_FILS_SHA256) {
+               ret = os_snprintf(pos, end - pos, "%sFILS-SHA256",
+                                 pos == buf ? "" : " ");
+               if (os_snprintf_error(end - pos, ret)) {
+                       end[-1] = '\0';
+                       return buf;
+               }
+               pos += ret;
+       }
+       if (ssid->key_mgmt & WPA_KEY_MGMT_FILS_SHA384) {
+               ret = os_snprintf(pos, end - pos, "%sFILS-SHA384",
+                                 pos == buf ? "" : " ");
+               if (os_snprintf_error(end - pos, ret)) {
+                       end[-1] = '\0';
+                       return buf;
+               }
+               pos += ret;
+       }
+#ifdef CONFIG_IEEE80211R
+       if (ssid->key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA256) {
+               ret = os_snprintf(pos, end - pos, "%sFT-FILS-SHA256",
+                                 pos == buf ? "" : " ");
+               if (os_snprintf_error(end - pos, ret)) {
+                       end[-1] = '\0';
+                       return buf;
+               }
+               pos += ret;
+       }
+       if (ssid->key_mgmt & WPA_KEY_MGMT_FT_FILS_SHA384) {
+               ret = os_snprintf(pos, end - pos, "%sFT-FILS-SHA384",
+                                 pos == buf ? "" : " ");
+               if (os_snprintf_error(end - pos, ret)) {
+                       end[-1] = '\0';
+                       return buf;
+               }
+               pos += ret;
+       }
+#endif /* CONFIG_IEEE80211R */
+#endif /* CONFIG_FILS */
+
+#ifdef CONFIG_DPP
+       if (ssid->key_mgmt & WPA_KEY_MGMT_DPP) {
+               ret = os_snprintf(pos, end - pos, "%sDPP",
+                                 pos == buf ? "" : " ");
+               if (os_snprintf_error(end - pos, ret)) {
+                       end[-1] = '\0';
+                       return buf;
+               }
+               pos += ret;
+       }
+#endif /* CONFIG_DPP */
+
+#ifdef CONFIG_OWE
+       if (ssid->key_mgmt & WPA_KEY_MGMT_OWE) {
+               ret = os_snprintf(pos, end - pos, "%sOWE",
+                                 pos == buf ? "" : " ");
+               if (os_snprintf_error(end - pos, ret)) {
+                       end[-1] = '\0';
+                       return buf;
+               }
+               pos += ret;
+       }
+#endif /* CONFIG_OWE */
+
        if (pos == buf) {
                os_free(buf);
                buf = NULL;
@@ -899,6 +1076,9 @@ static char * wpa_config_write_key_mgmt(const struct parse_data *data,
 
 static int wpa_config_parse_cipher(int line, const char *value)
 {
+#ifdef CONFIG_NO_WPA
+       return -1;
+#else /* CONFIG_NO_WPA */
        int val = wpa_parse_cipher(value);
        if (val < 0) {
                wpa_printf(MSG_ERROR, "Line %d: invalid cipher '%s'.",
@@ -911,12 +1091,16 @@ static int wpa_config_parse_cipher(int line, const char *value)
                return -1;
        }
        return val;
+#endif /* CONFIG_NO_WPA */
 }
 
 
 #ifndef NO_CONFIG_WRITE
 static char * wpa_config_write_cipher(int cipher)
 {
+#ifdef CONFIG_NO_WPA
+       return NULL;
+#else /* CONFIG_NO_WPA */
        char *buf = os_zalloc(50);
        if (buf == NULL)
                return NULL;
@@ -927,6 +1111,7 @@ static char * wpa_config_write_cipher(int cipher)
        }
 
        return buf;
+#endif /* CONFIG_NO_WPA */
 }
 #endif /* NO_CONFIG_WRITE */
 
@@ -945,6 +1130,8 @@ static int wpa_config_parse_pairwise(const struct parse_data *data,
                return -1;
        }
 
+       if (ssid->pairwise_cipher == val)
+               return 1;
        wpa_printf(MSG_MSGDUMP, "pairwise: 0x%x", val);
        ssid->pairwise_cipher = val;
        return 0;
@@ -981,6 +1168,8 @@ static int wpa_config_parse_group(const struct parse_data *data,
                return -1;
        }
 
+       if (ssid->group_cipher == val)
+               return 1;
        wpa_printf(MSG_MSGDUMP, "group: 0x%x", val);
        ssid->group_cipher = val;
        return 0;
@@ -996,6 +1185,40 @@ static char * wpa_config_write_group(const struct parse_data *data,
 #endif /* NO_CONFIG_WRITE */
 
 
+static int wpa_config_parse_group_mgmt(const struct parse_data *data,
+                                      struct wpa_ssid *ssid, int line,
+                                      const char *value)
+{
+       int val;
+
+       val = wpa_config_parse_cipher(line, value);
+       if (val == -1)
+               return -1;
+
+       if (val & ~WPA_ALLOWED_GROUP_MGMT_CIPHERS) {
+               wpa_printf(MSG_ERROR,
+                          "Line %d: not allowed group management cipher (0x%x).",
+                          line, val);
+               return -1;
+       }
+
+       if (ssid->group_mgmt_cipher == val)
+               return 1;
+       wpa_printf(MSG_MSGDUMP, "group_mgmt: 0x%x", val);
+       ssid->group_mgmt_cipher = val;
+       return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_group_mgmt(const struct parse_data *data,
+                                         struct wpa_ssid *ssid)
+{
+       return wpa_config_write_cipher(ssid->group_mgmt_cipher);
+}
+#endif /* NO_CONFIG_WRITE */
+
+
 static int wpa_config_parse_auth_alg(const struct parse_data *data,
                                     struct wpa_ssid *ssid, int line,
                                     const char *value)
@@ -1042,6 +1265,8 @@ static int wpa_config_parse_auth_alg(const struct parse_data *data,
                errors++;
        }
 
+       if (!errors && ssid->auth_alg == val)
+               return 1;
        wpa_printf(MSG_MSGDUMP, "auth_alg: 0x%x", val);
        ssid->auth_alg = val;
        return errors ? -1 : 0;
@@ -1296,6 +1521,32 @@ static int wpa_config_parse_eap(const struct parse_data *data,
        methods[num_methods].method = EAP_TYPE_NONE;
        num_methods++;
 
+       if (!errors && ssid->eap.eap_methods) {
+               struct eap_method_type *prev_m;
+               size_t i, j, prev_methods, match = 0;
+
+               prev_m = ssid->eap.eap_methods;
+               for (i = 0; prev_m[i].vendor != EAP_VENDOR_IETF ||
+                            prev_m[i].method != EAP_TYPE_NONE; i++) {
+                       /* Count the methods */
+               }
+               prev_methods = i + 1;
+
+               for (i = 0; prev_methods == num_methods && i < prev_methods;
+                    i++) {
+                       for (j = 0; j < num_methods; j++) {
+                               if (prev_m[i].vendor == methods[j].vendor &&
+                                   prev_m[i].method == methods[j].method) {
+                                       match++;
+                                       break;
+                               }
+                       }
+               }
+               if (match == num_methods) {
+                       os_free(methods);
+                       return 1;
+               }
+       }
        wpa_hexdump(MSG_MSGDUMP, "eap methods",
                    (u8 *) methods, num_methods * sizeof(*methods));
        os_free(ssid->eap.eap_methods);
@@ -1348,6 +1599,8 @@ static int wpa_config_parse_password(const struct parse_data *data,
        u8 *hash;
 
        if (os_strcmp(value, "NULL") == 0) {
+               if (!ssid->eap.password)
+                       return 1; /* Already unset */
                wpa_printf(MSG_DEBUG, "Unset configuration string 'password'");
                bin_clear_free(ssid->eap.password, ssid->eap.password_len);
                ssid->eap.password = NULL;
@@ -1358,7 +1611,7 @@ static int wpa_config_parse_password(const struct parse_data *data,
 #ifdef CONFIG_EXT_PASSWORD
        if (os_strncmp(value, "ext:", 4) == 0) {
                char *name = os_strdup(value + 4);
-               if (name == NULL)
+               if (!name)
                        return -1;
                bin_clear_free(ssid->eap.password, ssid->eap.password_len);
                ssid->eap.password = (u8 *) name;
@@ -1374,9 +1627,9 @@ static int wpa_config_parse_password(const struct parse_data *data,
                size_t res_len;
 
                tmp = wpa_config_parse_string(value, &res_len);
-               if (tmp == NULL) {
-                       wpa_printf(MSG_ERROR, "Line %d: failed to parse "
-                                  "password.", line);
+               if (!tmp) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: failed to parse password.", line);
                        return -1;
                }
                wpa_hexdump_ascii_key(MSG_MSGDUMP, data->name,
@@ -1394,13 +1647,14 @@ static int wpa_config_parse_password(const struct parse_data *data,
 
        /* NtPasswordHash: hash:<32 hex digits> */
        if (os_strlen(value + 5) != 2 * 16) {
-               wpa_printf(MSG_ERROR, "Line %d: Invalid password hash length "
-                          "(expected 32 hex digits)", line);
+               wpa_printf(MSG_ERROR,
+                          "Line %d: Invalid password hash length (expected 32 hex digits)",
+                          line);
                return -1;
        }
 
        hash = os_malloc(16);
-       if (hash == NULL)
+       if (!hash)
                return -1;
 
        if (hexstr2bin(value + 5, hash, 16)) {
@@ -1411,6 +1665,12 @@ static int wpa_config_parse_password(const struct parse_data *data,
 
        wpa_hexdump_key(MSG_MSGDUMP, data->name, hash, 16);
 
+       if (ssid->eap.password && ssid->eap.password_len == 16 &&
+           os_memcmp(ssid->eap.password, hash, 16) == 0 &&
+           (ssid->eap.flags & EAP_CONFIG_FLAGS_PASSWORD_NTHASH)) {
+               bin_clear_free(hash, 16);
+               return 1;
+       }
        bin_clear_free(ssid->eap.password, ssid->eap.password_len);
        ssid->eap.password = hash;
        ssid->eap.password_len = 16;
@@ -1421,19 +1681,118 @@ static int wpa_config_parse_password(const struct parse_data *data,
 }
 
 
+static int wpa_config_parse_machine_password(const struct parse_data *data,
+                                            struct wpa_ssid *ssid, int line,
+                                            const char *value)
+{
+       u8 *hash;
+
+       if (os_strcmp(value, "NULL") == 0) {
+               if (!ssid->eap.machine_password)
+                       return 1; /* Already unset */
+               wpa_printf(MSG_DEBUG,
+                          "Unset configuration string 'machine_password'");
+               bin_clear_free(ssid->eap.machine_password,
+                              ssid->eap.machine_password_len);
+               ssid->eap.machine_password = NULL;
+               ssid->eap.machine_password_len = 0;
+               return 0;
+       }
+
+#ifdef CONFIG_EXT_PASSWORD
+       if (os_strncmp(value, "ext:", 4) == 0) {
+               char *name = os_strdup(value + 4);
+
+               if (!name)
+                       return -1;
+               bin_clear_free(ssid->eap.machine_password,
+                              ssid->eap.machine_password_len);
+               ssid->eap.machine_password = (u8 *) name;
+               ssid->eap.machine_password_len = os_strlen(name);
+               ssid->eap.flags &= ~EAP_CONFIG_FLAGS_MACHINE_PASSWORD_NTHASH;
+               ssid->eap.flags |= EAP_CONFIG_FLAGS_EXT_MACHINE_PASSWORD;
+               return 0;
+       }
+#endif /* CONFIG_EXT_PASSWORD */
+
+       if (os_strncmp(value, "hash:", 5) != 0) {
+               char *tmp;
+               size_t res_len;
+
+               tmp = wpa_config_parse_string(value, &res_len);
+               if (!tmp) {
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: failed to parse machine_password.",
+                                  line);
+                       return -1;
+               }
+               wpa_hexdump_ascii_key(MSG_MSGDUMP, data->name,
+                                     (u8 *) tmp, res_len);
+
+               bin_clear_free(ssid->eap.machine_password,
+                              ssid->eap.machine_password_len);
+               ssid->eap.machine_password = (u8 *) tmp;
+               ssid->eap.machine_password_len = res_len;
+               ssid->eap.flags &= ~EAP_CONFIG_FLAGS_MACHINE_PASSWORD_NTHASH;
+               ssid->eap.flags &= ~EAP_CONFIG_FLAGS_EXT_MACHINE_PASSWORD;
+
+               return 0;
+       }
+
+
+       /* NtPasswordHash: hash:<32 hex digits> */
+       if (os_strlen(value + 5) != 2 * 16) {
+               wpa_printf(MSG_ERROR,
+                          "Line %d: Invalid machine_password hash length (expected 32 hex digits)",
+                          line);
+               return -1;
+       }
+
+       hash = os_malloc(16);
+       if (!hash)
+               return -1;
+
+       if (hexstr2bin(value + 5, hash, 16)) {
+               os_free(hash);
+               wpa_printf(MSG_ERROR, "Line %d: Invalid machine_password hash",
+                          line);
+               return -1;
+       }
+
+       wpa_hexdump_key(MSG_MSGDUMP, data->name, hash, 16);
+
+       if (ssid->eap.machine_password &&
+           ssid->eap.machine_password_len == 16 &&
+           os_memcmp(ssid->eap.machine_password, hash, 16) == 0 &&
+           (ssid->eap.flags & EAP_CONFIG_FLAGS_MACHINE_PASSWORD_NTHASH)) {
+               bin_clear_free(hash, 16);
+               return 1;
+       }
+       bin_clear_free(ssid->eap.machine_password,
+                      ssid->eap.machine_password_len);
+       ssid->eap.machine_password = hash;
+       ssid->eap.machine_password_len = 16;
+       ssid->eap.flags |= EAP_CONFIG_FLAGS_MACHINE_PASSWORD_NTHASH;
+       ssid->eap.flags &= ~EAP_CONFIG_FLAGS_EXT_MACHINE_PASSWORD;
+
+       return 0;
+}
+
+
 #ifndef NO_CONFIG_WRITE
+
 static char * wpa_config_write_password(const struct parse_data *data,
                                        struct wpa_ssid *ssid)
 {
        char *buf;
 
-       if (ssid->eap.password == NULL)
+       if (!ssid->eap.password)
                return NULL;
 
 #ifdef CONFIG_EXT_PASSWORD
        if (ssid->eap.flags & EAP_CONFIG_FLAGS_EXT_PASSWORD) {
                buf = os_zalloc(4 + ssid->eap.password_len + 1);
-               if (buf == NULL)
+               if (!buf)
                        return NULL;
                os_memcpy(buf, "ext:", 4);
                os_memcpy(buf + 4, ssid->eap.password, ssid->eap.password_len);
@@ -1447,7 +1806,7 @@ static char * wpa_config_write_password(const struct parse_data *data,
        }
 
        buf = os_malloc(5 + 32 + 1);
-       if (buf == NULL)
+       if (!buf)
                return NULL;
 
        os_memcpy(buf, "hash:", 5);
@@ -1455,6 +1814,44 @@ static char * wpa_config_write_password(const struct parse_data *data,
 
        return buf;
 }
+
+
+static char * wpa_config_write_machine_password(const struct parse_data *data,
+                                               struct wpa_ssid *ssid)
+{
+       char *buf;
+
+       if (!ssid->eap.machine_password)
+               return NULL;
+
+#ifdef CONFIG_EXT_PASSWORD
+       if (ssid->eap.flags & EAP_CONFIG_FLAGS_EXT_MACHINE_PASSWORD) {
+               buf = os_zalloc(4 + ssid->eap.machine_password_len + 1);
+               if (!buf)
+                       return NULL;
+               os_memcpy(buf, "ext:", 4);
+               os_memcpy(buf + 4, ssid->eap.machine_password,
+                         ssid->eap.machine_password_len);
+               return buf;
+       }
+#endif /* CONFIG_EXT_PASSWORD */
+
+       if (!(ssid->eap.flags & EAP_CONFIG_FLAGS_MACHINE_PASSWORD_NTHASH)) {
+               return wpa_config_write_string(
+                       ssid->eap.machine_password,
+                       ssid->eap.machine_password_len);
+       }
+
+       buf = os_malloc(5 + 32 + 1);
+       if (!buf)
+               return NULL;
+
+       os_memcpy(buf, "hash:", 5);
+       wpa_snprintf_hex(buf + 5, 32 + 1, ssid->eap.machine_password, 16);
+
+       return buf;
+}
+
 #endif /* NO_CONFIG_WRITE */
 #endif /* IEEE8021X_EAPOL */
 
@@ -1734,6 +2131,140 @@ static char * wpa_config_write_mesh_basic_rates(const struct parse_data *data,
 #endif /* CONFIG_MESH */
 
 
+#ifdef CONFIG_MACSEC
+
+static int wpa_config_parse_mka_cak(const struct parse_data *data,
+                                   struct wpa_ssid *ssid, int line,
+                                   const char *value)
+{
+       size_t len;
+
+       len = os_strlen(value);
+       if (len > 2 * MACSEC_CAK_MAX_LEN ||
+           (len != 2 * 16 && len != 2 * 32) ||
+           hexstr2bin(value, ssid->mka_cak, len / 2)) {
+               wpa_printf(MSG_ERROR, "Line %d: Invalid MKA-CAK '%s'.",
+                          line, value);
+               return -1;
+       }
+       ssid->mka_cak_len = len / 2;
+       ssid->mka_psk_set |= MKA_PSK_SET_CAK;
+
+       wpa_hexdump_key(MSG_MSGDUMP, "MKA-CAK", ssid->mka_cak,
+                       ssid->mka_cak_len);
+       return 0;
+}
+
+
+static int wpa_config_parse_mka_ckn(const struct parse_data *data,
+                                   struct wpa_ssid *ssid, int line,
+                                   const char *value)
+{
+       size_t len;
+
+       len = os_strlen(value);
+       if (len > 2 * MACSEC_CKN_MAX_LEN || /* too long */
+           len < 2 || /* too short */
+           len % 2 != 0 /* not an integral number of bytes */) {
+               wpa_printf(MSG_ERROR, "Line %d: Invalid MKA-CKN '%s'.",
+                          line, value);
+               return -1;
+       }
+       ssid->mka_ckn_len = len / 2;
+       if (hexstr2bin(value, ssid->mka_ckn, ssid->mka_ckn_len)) {
+               wpa_printf(MSG_ERROR, "Line %d: Invalid MKA-CKN '%s'.",
+                          line, value);
+               return -1;
+       }
+
+       ssid->mka_psk_set |= MKA_PSK_SET_CKN;
+
+       wpa_hexdump_key(MSG_MSGDUMP, "MKA-CKN", ssid->mka_ckn,
+                       ssid->mka_ckn_len);
+       return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+
+static char * wpa_config_write_mka_cak(const struct parse_data *data,
+                                      struct wpa_ssid *ssid)
+{
+       if (!(ssid->mka_psk_set & MKA_PSK_SET_CAK))
+               return NULL;
+
+       return wpa_config_write_string_hex(ssid->mka_cak, ssid->mka_cak_len);
+}
+
+
+static char * wpa_config_write_mka_ckn(const struct parse_data *data,
+                                      struct wpa_ssid *ssid)
+{
+       if (!(ssid->mka_psk_set & MKA_PSK_SET_CKN))
+               return NULL;
+       return wpa_config_write_string_hex(ssid->mka_ckn, ssid->mka_ckn_len);
+}
+
+#endif /* NO_CONFIG_WRITE */
+
+#endif /* CONFIG_MACSEC */
+
+
+#ifdef CONFIG_OCV
+
+static int wpa_config_parse_ocv(const struct parse_data *data,
+                               struct wpa_ssid *ssid, int line,
+                               const char *value)
+{
+       char *end;
+
+       ssid->ocv = strtol(value, &end, 0);
+       if (*end || ssid->ocv < 0 || ssid->ocv > 1) {
+               wpa_printf(MSG_ERROR, "Line %d: Invalid ocv value '%s'.",
+                          line, value);
+               return -1;
+       }
+       if (ssid->ocv && ssid->ieee80211w == NO_MGMT_FRAME_PROTECTION)
+               ssid->ieee80211w = MGMT_FRAME_PROTECTION_OPTIONAL;
+       return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_ocv(const struct parse_data *data,
+                                  struct wpa_ssid *ssid)
+{
+       char *value = os_malloc(20);
+
+       if (!value)
+               return NULL;
+       os_snprintf(value, 20, "%d", ssid->ocv);
+       value[20 - 1] = '\0';
+       return value;
+}
+#endif /* NO_CONFIG_WRITE */
+
+#endif /* CONFIG_OCV */
+
+
+static int wpa_config_parse_peerkey(const struct parse_data *data,
+                                   struct wpa_ssid *ssid, int line,
+                                   const char *value)
+{
+       wpa_printf(MSG_INFO, "NOTE: Obsolete peerkey parameter ignored");
+       return 0;
+}
+
+
+#ifndef NO_CONFIG_WRITE
+static char * wpa_config_write_peerkey(const struct parse_data *data,
+                                      struct wpa_ssid *ssid)
+{
+       return NULL;
+}
+#endif /* NO_CONFIG_WRITE */
+
+
 /* Helper macros for network block parser */
 
 #ifdef OFFSET
@@ -1745,23 +2276,24 @@ static char * wpa_config_write_mesh_basic_rates(const struct parse_data *data,
 /* STR: Define a string variable for an ASCII string; f = field name */
 #ifdef NO_CONFIG_WRITE
 #define _STR(f) #f, wpa_config_parse_str, OFFSET(f)
-#define _STRe(f) #f, wpa_config_parse_str, OFFSET(eap.f)
+#define _STRe(f, m) #f, wpa_config_parse_str, OFFSET(eap.m)
 #else /* NO_CONFIG_WRITE */
 #define _STR(f) #f, wpa_config_parse_str, wpa_config_write_str, OFFSET(f)
-#define _STRe(f) #f, wpa_config_parse_str, wpa_config_write_str, OFFSET(eap.f)
+#define _STRe(f, m) #f, wpa_config_parse_str, wpa_config_write_str, \
+               OFFSET(eap.m)
 #endif /* NO_CONFIG_WRITE */
 #define STR(f) _STR(f), NULL, NULL, NULL, 0
-#define STRe(f) _STRe(f), NULL, NULL, NULL, 0
+#define STRe(f, m) _STRe(f, m), NULL, NULL, NULL, 0
 #define STR_KEY(f) _STR(f), NULL, NULL, NULL, 1
-#define STR_KEYe(f) _STRe(f), NULL, NULL, NULL, 1
+#define STR_KEYe(f, m) _STRe(f, m), NULL, NULL, NULL, 1
 
 /* STR_LEN: Define a string variable with a separate variable for storing the
  * data length. Unlike STR(), this can be used to store arbitrary binary data
  * (i.e., even nul termination character). */
 #define _STR_LEN(f) _STR(f), OFFSET(f ## _len)
-#define _STR_LENe(f) _STRe(f), OFFSET(eap.f ## _len)
+#define _STR_LENe(f, m) _STRe(f, m), OFFSET(eap.m ## _len)
 #define STR_LEN(f) _STR_LEN(f), NULL, NULL, 0
-#define STR_LENe(f) _STR_LENe(f), NULL, NULL, 0
+#define STR_LENe(f, m) _STR_LENe(f, m), NULL, NULL, 0
 #define STR_LEN_KEY(f) _STR_LEN(f), NULL, NULL, 1
 
 /* STR_RANGE: Like STR_LEN(), but with minimum and maximum allowed length
@@ -1772,17 +2304,17 @@ static char * wpa_config_write_mesh_basic_rates(const struct parse_data *data,
 
 #ifdef NO_CONFIG_WRITE
 #define _INT(f) #f, wpa_config_parse_int, OFFSET(f), (void *) 0
-#define _INTe(f) #f, wpa_config_parse_int, OFFSET(eap.f), (void *) 0
+#define _INTe(f, m) #f, wpa_config_parse_int, OFFSET(eap.m), (void *) 0
 #else /* NO_CONFIG_WRITE */
 #define _INT(f) #f, wpa_config_parse_int, wpa_config_write_int, \
        OFFSET(f), (void *) 0
-#define _INTe(f) #f, wpa_config_parse_int, wpa_config_write_int, \
-       OFFSET(eap.f), (void *) 0
+#define _INTe(f, m) #f, wpa_config_parse_int, wpa_config_write_int,    \
+       OFFSET(eap.m), (void *) 0
 #endif /* NO_CONFIG_WRITE */
 
 /* INT: Define an integer variable */
 #define INT(f) _INT(f), NULL, NULL, 0
-#define INTe(f) _INTe(f), NULL, NULL, 0
+#define INTe(f, m) _INTe(f, m), NULL, NULL, 0
 
 /* INT_RANGE: Define an integer variable with allowed value range */
 #define INT_RANGE(f, min, max) _INT(f), (void *) (min), (void *) (max), 0
@@ -1825,62 +2357,99 @@ static const struct parse_data ssid_fields[] = {
        { STR_RANGE(ssid, 0, SSID_MAX_LEN) },
        { INT_RANGE(scan_ssid, 0, 1) },
        { FUNC(bssid) },
+       { FUNC(bssid_hint) },
        { FUNC(bssid_blacklist) },
        { FUNC(bssid_whitelist) },
        { FUNC_KEY(psk) },
        { INT(mem_only_psk) },
+       { STR_KEY(sae_password) },
+       { STR(sae_password_id) },
        { FUNC(proto) },
        { FUNC(key_mgmt) },
        { INT(bg_scan_period) },
        { FUNC(pairwise) },
        { FUNC(group) },
+       { FUNC(group_mgmt) },
        { FUNC(auth_alg) },
        { FUNC(scan_freq) },
        { FUNC(freq_list) },
+       { INT_RANGE(ht, 0, 1) },
+       { INT_RANGE(vht, 0, 1) },
+       { INT_RANGE(ht40, -1, 1) },
+       { INT_RANGE(max_oper_chwidth, CHANWIDTH_USE_HT,
+                   CHANWIDTH_80P80MHZ) },
+       { INT(vht_center_freq1) },
+       { INT(vht_center_freq2) },
 #ifdef IEEE8021X_EAPOL
        { FUNC(eap) },
-       { STR_LENe(identity) },
-       { STR_LENe(anonymous_identity) },
+       { STR_LENe(identity, identity) },
+       { STR_LENe(anonymous_identity, anonymous_identity) },
+       { STR_LENe(imsi_identity, imsi_identity) },
+       { STR_LENe(machine_identity, machine_identity) },
        { FUNC_KEY(password) },
-       { STRe(ca_cert) },
-       { STRe(ca_path) },
-       { STRe(client_cert) },
-       { STRe(private_key) },
-       { STR_KEYe(private_key_passwd) },
-       { STRe(dh_file) },
-       { STRe(subject_match) },
-       { STRe(altsubject_match) },
-       { STRe(domain_suffix_match) },
-       { STRe(domain_match) },
-       { STRe(ca_cert2) },
-       { STRe(ca_path2) },
-       { STRe(client_cert2) },
-       { STRe(private_key2) },
-       { STR_KEYe(private_key2_passwd) },
-       { STRe(dh_file2) },
-       { STRe(subject_match2) },
-       { STRe(altsubject_match2) },
-       { STRe(domain_suffix_match2) },
-       { STRe(domain_match2) },
-       { STRe(phase1) },
-       { STRe(phase2) },
-       { STRe(pcsc) },
-       { STR_KEYe(pin) },
-       { STRe(engine_id) },
-       { STRe(key_id) },
-       { STRe(cert_id) },
-       { STRe(ca_cert_id) },
-       { STR_KEYe(pin2) },
-       { STRe(engine2_id) },
-       { STRe(key2_id) },
-       { STRe(cert2_id) },
-       { STRe(ca_cert2_id) },
-       { INTe(engine) },
-       { INTe(engine2) },
+       { FUNC_KEY(machine_password) },
+       { STRe(ca_cert, cert.ca_cert) },
+       { STRe(ca_path, cert.ca_path) },
+       { STRe(client_cert, cert.client_cert) },
+       { STRe(private_key, cert.private_key) },
+       { STR_KEYe(private_key_passwd, cert.private_key_passwd) },
+       { STRe(dh_file, cert.dh_file) },
+       { STRe(subject_match, cert.subject_match) },
+       { STRe(check_cert_subject, cert.check_cert_subject) },
+       { STRe(altsubject_match, cert.altsubject_match) },
+       { STRe(domain_suffix_match, cert.domain_suffix_match) },
+       { STRe(domain_match, cert.domain_match) },
+       { STRe(ca_cert2, phase2_cert.ca_cert) },
+       { STRe(ca_path2, phase2_cert.ca_path) },
+       { STRe(client_cert2, phase2_cert.client_cert) },
+       { STRe(private_key2, phase2_cert.private_key) },
+       { STR_KEYe(private_key2_passwd, phase2_cert.private_key_passwd) },
+       { STRe(dh_file2, phase2_cert.dh_file) },
+       { STRe(subject_match2, phase2_cert.subject_match) },
+       { STRe(check_cert_subject2, phase2_cert.check_cert_subject) },
+       { STRe(altsubject_match2, phase2_cert.altsubject_match) },
+       { STRe(domain_suffix_match2, phase2_cert.domain_suffix_match) },
+       { STRe(domain_match2, phase2_cert.domain_match) },
+       { STRe(phase1, phase1) },
+       { STRe(phase2, phase2) },
+       { STRe(machine_phase2, machine_phase2) },
+       { STRe(pcsc, pcsc) },
+       { STR_KEYe(pin, cert.pin) },
+       { STRe(engine_id, cert.engine_id) },
+       { STRe(key_id, cert.key_id) },
+       { STRe(cert_id, cert.cert_id) },
+       { STRe(ca_cert_id, cert.ca_cert_id) },
+       { STR_KEYe(pin2, phase2_cert.pin) },
+       { STRe(engine_id2, phase2_cert.engine_id) },
+       { STRe(key_id2, phase2_cert.key_id) },
+       { STRe(cert_id2, phase2_cert.cert_id) },
+       { STRe(ca_cert_id2, phase2_cert.ca_cert_id) },
+       { INTe(engine, cert.engine) },
+       { INTe(engine2, phase2_cert.engine) },
+       { STRe(machine_ca_cert, machine_cert.ca_cert) },
+       { STRe(machine_ca_path, machine_cert.ca_path) },
+       { STRe(machine_client_cert, machine_cert.client_cert) },
+       { STRe(machine_private_key, machine_cert.private_key) },
+       { STR_KEYe(machine_private_key_passwd,
+                  machine_cert.private_key_passwd) },
+       { STRe(machine_dh_file, machine_cert.dh_file) },
+       { STRe(machine_subject_match, machine_cert.subject_match) },
+       { STRe(machine_check_cert_subject, machine_cert.check_cert_subject) },
+       { STRe(machine_altsubject_match, machine_cert.altsubject_match) },
+       { STRe(machine_domain_suffix_match,
+              machine_cert.domain_suffix_match) },
+       { STRe(machine_domain_match, machine_cert.domain_match) },
+       { STR_KEYe(machine_pin, machine_cert.pin) },
+       { STRe(machine_engine_id, machine_cert.engine_id) },
+       { STRe(machine_key_id, machine_cert.key_id) },
+       { STRe(machine_cert_id, machine_cert.cert_id) },
+       { STRe(machine_ca_cert_id, machine_cert.ca_cert_id) },
+       { INTe(machine_engine, machine_cert.engine) },
+       { INTe(machine_ocsp, machine_cert.ocsp) },
        { INT(eapol_flags) },
-       { INTe(sim_num) },
-       { STRe(openssl_ciphers) },
-       { INTe(erp) },
+       { INTe(sim_num, sim_num) },
+       { STRe(openssl_ciphers, openssl_ciphers) },
+       { INTe(erp, erp) },
 #endif /* IEEE8021X_EAPOL */
        { FUNC_KEY(wep_key0) },
        { FUNC_KEY(wep_key1) },
@@ -1890,26 +2459,34 @@ static const struct parse_data ssid_fields[] = {
        { INT(priority) },
 #ifdef IEEE8021X_EAPOL
        { INT(eap_workaround) },
-       { STRe(pac_file) },
-       { INTe(fragment_size) },
-       { INTe(ocsp) },
+       { STRe(pac_file, pac_file) },
+       { INTe(fragment_size, fragment_size) },
+       { INTe(ocsp, cert.ocsp) },
+       { INTe(ocsp2, phase2_cert.ocsp) },
 #endif /* IEEE8021X_EAPOL */
 #ifdef CONFIG_MESH
        { INT_RANGE(mode, 0, 5) },
        { INT_RANGE(no_auto_peer, 0, 1) },
+       { INT_RANGE(mesh_rssi_threshold, -255, 1) },
 #else /* CONFIG_MESH */
        { INT_RANGE(mode, 0, 4) },
 #endif /* CONFIG_MESH */
        { INT_RANGE(proactive_key_caching, 0, 1) },
        { INT_RANGE(disabled, 0, 2) },
        { STR(id_str) },
-#ifdef CONFIG_IEEE80211W
        { INT_RANGE(ieee80211w, 0, 2) },
-#endif /* CONFIG_IEEE80211W */
-       { INT_RANGE(peerkey, 0, 1) },
+#ifdef CONFIG_OCV
+       { FUNC(ocv) },
+#endif /* CONFIG_OCV */
+       { FUNC(peerkey) /* obsolete - removed */ },
        { INT_RANGE(mixed_cell, 0, 1) },
-       { INT_RANGE(frequency, 0, 65000) },
+       { INT_RANGE(frequency, 0, 70200) },
        { INT_RANGE(fixed_freq, 0, 1) },
+       { INT_RANGE(enable_edmg, 0, 1) },
+       { INT_RANGE(edmg_channel, 9, 13) },
+#ifdef CONFIG_ACS
+       { INT_RANGE(acs, 0, 1) },
+#endif /* CONFIG_ACS */
 #ifdef CONFIG_MESH
        { FUNC(mesh_basic_rates) },
        { INT(dot11MeshMaxRetries) },
@@ -1918,6 +2495,7 @@ static const struct parse_data ssid_fields[] = {
        { INT(dot11MeshHoldingTimeout) },
 #endif /* CONFIG_MESH */
        { INT(wpa_ptk_rekey) },
+       { INT(group_rekey) },
        { STR(bgscan) },
        { INT_RANGE(ignore_broadcast_ssid, 0, 2) },
 #ifdef CONFIG_P2P
@@ -1931,6 +2509,8 @@ static const struct parse_data ssid_fields[] = {
        { INT_RANGE(disable_sgi, 0, 1) },
        { INT_RANGE(disable_ldpc, 0, 1) },
        { INT_RANGE(ht40_intolerant, 0, 1) },
+       { INT_RANGE(tx_stbc, -1, 1) },
+       { INT_RANGE(rx_stbc, -1, 3) },
        { INT_RANGE(disable_max_amsdu, -1, 1) },
        { INT_RANGE(ampdu_factor, -1, 3) },
        { INT_RANGE(ampdu_density, -1, 7) },
@@ -1962,11 +2542,34 @@ static const struct parse_data ssid_fields[] = {
        { INT(beacon_int) },
 #ifdef CONFIG_MACSEC
        { INT_RANGE(macsec_policy, 0, 1) },
+       { INT_RANGE(macsec_integ_only, 0, 1) },
+       { INT_RANGE(macsec_replay_protect, 0, 1) },
+       { INT(macsec_replay_window) },
+       { INT_RANGE(macsec_port, 1, 65534) },
+       { INT_RANGE(mka_priority, 0, 255) },
+       { FUNC_KEY(mka_cak) },
+       { FUNC_KEY(mka_ckn) },
 #endif /* CONFIG_MACSEC */
 #ifdef CONFIG_HS20
        { INT(update_identifier) },
+       { STR_RANGE(roaming_consortium_selection, 0, MAX_ROAMING_CONS_OI_LEN) },
 #endif /* CONFIG_HS20 */
        { INT_RANGE(mac_addr, 0, 2) },
+       { INT_RANGE(pbss, 0, 2) },
+       { INT_RANGE(wps_disabled, 0, 1) },
+       { INT_RANGE(fils_dh_group, 0, 65535) },
+#ifdef CONFIG_DPP
+       { STR(dpp_connector) },
+       { STR_LEN(dpp_netaccesskey) },
+       { INT(dpp_netaccesskey_expiry) },
+       { STR_LEN(dpp_csign) },
+#endif /* CONFIG_DPP */
+       { INT_RANGE(owe_group, 0, 65535) },
+       { INT_RANGE(owe_only, 0, 1) },
+       { INT_RANGE(owe_ptk_workaround, 0, 1) },
+       { INT_RANGE(multi_ap_backhaul_sta, 0, 1) },
+       { INT_RANGE(ft_eap_pmksa_caching, 0, 1) },
+       { INT_RANGE(beacon_prot, 0, 1) },
 };
 
 #undef OFFSET
@@ -2073,45 +2676,44 @@ int wpa_config_update_prio_list(struct wpa_config *config)
 
 
 #ifdef IEEE8021X_EAPOL
+
+static void eap_peer_config_free_cert(struct eap_peer_cert_config *cert)
+{
+       os_free(cert->ca_cert);
+       os_free(cert->ca_path);
+       os_free(cert->client_cert);
+       os_free(cert->private_key);
+       str_clear_free(cert->private_key_passwd);
+       os_free(cert->dh_file);
+       os_free(cert->subject_match);
+       os_free(cert->check_cert_subject);
+       os_free(cert->altsubject_match);
+       os_free(cert->domain_suffix_match);
+       os_free(cert->domain_match);
+       str_clear_free(cert->pin);
+       os_free(cert->engine_id);
+       os_free(cert->key_id);
+       os_free(cert->cert_id);
+       os_free(cert->ca_cert_id);
+}
+
+
 static void eap_peer_config_free(struct eap_peer_config *eap)
 {
        os_free(eap->eap_methods);
        bin_clear_free(eap->identity, eap->identity_len);
        os_free(eap->anonymous_identity);
+       os_free(eap->imsi_identity);
+       os_free(eap->machine_identity);
        bin_clear_free(eap->password, eap->password_len);
-       os_free(eap->ca_cert);
-       os_free(eap->ca_path);
-       os_free(eap->client_cert);
-       os_free(eap->private_key);
-       str_clear_free(eap->private_key_passwd);
-       os_free(eap->dh_file);
-       os_free(eap->subject_match);
-       os_free(eap->altsubject_match);
-       os_free(eap->domain_suffix_match);
-       os_free(eap->domain_match);
-       os_free(eap->ca_cert2);
-       os_free(eap->ca_path2);
-       os_free(eap->client_cert2);
-       os_free(eap->private_key2);
-       str_clear_free(eap->private_key2_passwd);
-       os_free(eap->dh_file2);
-       os_free(eap->subject_match2);
-       os_free(eap->altsubject_match2);
-       os_free(eap->domain_suffix_match2);
-       os_free(eap->domain_match2);
+       bin_clear_free(eap->machine_password, eap->machine_password_len);
+       eap_peer_config_free_cert(&eap->cert);
+       eap_peer_config_free_cert(&eap->phase2_cert);
+       eap_peer_config_free_cert(&eap->machine_cert);
        os_free(eap->phase1);
        os_free(eap->phase2);
+       os_free(eap->machine_phase2);
        os_free(eap->pcsc);
-       str_clear_free(eap->pin);
-       os_free(eap->engine_id);
-       os_free(eap->key_id);
-       os_free(eap->cert_id);
-       os_free(eap->ca_cert_id);
-       os_free(eap->key2_id);
-       os_free(eap->cert2_id);
-       os_free(eap->ca_cert2_id);
-       str_clear_free(eap->pin2);
-       os_free(eap->engine2_id);
        os_free(eap->otp);
        os_free(eap->pending_req_otp);
        os_free(eap->pac_file);
@@ -2119,6 +2721,7 @@ static void eap_peer_config_free(struct eap_peer_config *eap)
        str_clear_free(eap->external_sim_resp);
        os_free(eap->openssl_ciphers);
 }
+
 #endif /* IEEE8021X_EAPOL */
 
 
@@ -2136,6 +2739,8 @@ void wpa_config_free_ssid(struct wpa_ssid *ssid)
        os_free(ssid->ssid);
        str_clear_free(ssid->passphrase);
        os_free(ssid->ext_psk);
+       str_clear_free(ssid->sae_password);
+       os_free(ssid->sae_password_id);
 #ifdef IEEE8021X_EAPOL
        eap_peer_config_free(&ssid->eap);
 #endif /* IEEE8021X_EAPOL */
@@ -2152,11 +2757,20 @@ void wpa_config_free_ssid(struct wpa_ssid *ssid)
 #ifdef CONFIG_MESH
        os_free(ssid->mesh_basic_rates);
 #endif /* CONFIG_MESH */
+#ifdef CONFIG_HS20
+       os_free(ssid->roaming_consortium_selection);
+#endif /* CONFIG_HS20 */
+       os_free(ssid->dpp_connector);
+       bin_clear_free(ssid->dpp_netaccesskey, ssid->dpp_netaccesskey_len);
+       os_free(ssid->dpp_csign);
        while ((psk = dl_list_first(&ssid->psk_list, struct psk_list_entry,
                                    list))) {
                dl_list_del(&psk->list);
                bin_clear_free(psk, sizeof(*psk));
        }
+#ifdef CONFIG_SAE
+       sae_deinit_pt(ssid->pt);
+#endif /* CONFIG_SAE */
        bin_clear_free(ssid, sizeof(*ssid));
 }
 
@@ -2271,6 +2885,13 @@ void wpa_config_free(struct wpa_config *config)
        os_free(config->bgscan);
        os_free(config->wowlan_triggers);
        os_free(config->fst_group_id);
+       os_free(config->sched_scan_plans);
+#ifdef CONFIG_MBO
+       os_free(config->non_pref_chan);
+#endif /* CONFIG_MBO */
+       os_free(config->dpp_name);
+       os_free(config->dpp_mud_url);
+
        os_free(config);
 }
 
@@ -2400,6 +3021,7 @@ void wpa_config_set_network_defaults(struct wpa_ssid *ssid)
        ssid->group_cipher = DEFAULT_GROUP;
        ssid->key_mgmt = DEFAULT_KEY_MGMT;
        ssid->bg_scan_period = DEFAULT_BG_SCAN_PERIOD;
+       ssid->ht = 1;
 #ifdef IEEE8021X_EAPOL
        ssid->eapol_flags = DEFAULT_EAPOL_FLAGS;
        ssid->eap_workaround = DEFAULT_EAP_WORKAROUND;
@@ -2411,12 +3033,15 @@ void wpa_config_set_network_defaults(struct wpa_ssid *ssid)
        ssid->dot11MeshRetryTimeout = DEFAULT_MESH_RETRY_TIMEOUT;
        ssid->dot11MeshConfirmTimeout = DEFAULT_MESH_CONFIRM_TIMEOUT;
        ssid->dot11MeshHoldingTimeout = DEFAULT_MESH_HOLDING_TIMEOUT;
+       ssid->mesh_rssi_threshold = DEFAULT_MESH_RSSI_THRESHOLD;
 #endif /* CONFIG_MESH */
 #ifdef CONFIG_HT_OVERRIDES
        ssid->disable_ht = DEFAULT_DISABLE_HT;
        ssid->disable_ht40 = DEFAULT_DISABLE_HT40;
        ssid->disable_sgi = DEFAULT_DISABLE_SGI;
        ssid->disable_ldpc = DEFAULT_DISABLE_LDPC;
+       ssid->tx_stbc = DEFAULT_TX_STBC;
+       ssid->rx_stbc = DEFAULT_RX_STBC;
        ssid->disable_max_amsdu = DEFAULT_DISABLE_MAX_AMSDU;
        ssid->ampdu_factor = DEFAULT_AMPDU_FACTOR;
        ssid->ampdu_density = DEFAULT_AMPDU_DENSITY;
@@ -2440,10 +3065,12 @@ void wpa_config_set_network_defaults(struct wpa_ssid *ssid)
        ssid->vht_tx_mcs_nss_8 = -1;
 #endif /* CONFIG_VHT_OVERRIDES */
        ssid->proactive_key_caching = -1;
-#ifdef CONFIG_IEEE80211W
        ssid->ieee80211w = MGMT_FRAME_PROTECTION_DEFAULT;
-#endif /* CONFIG_IEEE80211W */
+#ifdef CONFIG_MACSEC
+       ssid->mka_priority = DEFAULT_PRIO_NOT_KEY_SERVER;
+#endif /* CONFIG_MACSEC */
        ssid->mac_addr = -1;
+       ssid->max_oper_chwidth = DEFAULT_MAX_OPER_CHWIDTH;
 }
 
 
@@ -2453,7 +3080,8 @@ void wpa_config_set_network_defaults(struct wpa_ssid *ssid)
  * @var: Variable name, e.g., "ssid"
  * @value: Variable value
  * @line: Line number in configuration file or 0 if not used
- * Returns: 0 on success, -1 on failure
+ * Returns: 0 on success with possible change in the value, 1 on success with
+ * no change to previously configured value, or -1 on failure
  *
  * This function can be used to set network configuration variables based on
  * both the configuration file and management interface input. The value
@@ -2474,13 +3102,23 @@ int wpa_config_set(struct wpa_ssid *ssid, const char *var, const char *value,
                if (os_strcmp(var, field->name) != 0)
                        continue;
 
-               if (field->parser(field, ssid, line, value)) {
+               ret = field->parser(field, ssid, line, value);
+               if (ret < 0) {
                        if (line) {
                                wpa_printf(MSG_ERROR, "Line %d: failed to "
                                           "parse %s '%s'.", line, var, value);
                        }
                        ret = -1;
                }
+#ifdef CONFIG_SAE
+               if (os_strcmp(var, "ssid") == 0 ||
+                   os_strcmp(var, "psk") == 0 ||
+                   os_strcmp(var, "sae_password") == 0 ||
+                   os_strcmp(var, "sae_password_id") == 0) {
+                       sae_deinit_pt(ssid->pt);
+                       ssid->pt = NULL;
+               }
+#endif /* CONFIG_SAE */
                break;
        }
        if (i == NUM_SSID_FIELDS) {
@@ -2573,9 +3211,8 @@ char ** wpa_config_get_all(struct wpa_ssid *ssid, int get_keys)
        return props;
 
 err:
-       value = *props;
-       while (value)
-               os_free(value++);
+       for (i = 0; props[i]; i++)
+               os_free(props[i]);
        os_free(props);
        return NULL;
 #endif /* NO_CONFIG_WRITE */
@@ -2604,8 +3241,19 @@ char * wpa_config_get(struct wpa_ssid *ssid, const char *var)
 
        for (i = 0; i < NUM_SSID_FIELDS; i++) {
                const struct parse_data *field = &ssid_fields[i];
-               if (os_strcmp(var, field->name) == 0)
-                       return field->writer(field, ssid);
+               if (os_strcmp(var, field->name) == 0) {
+                       char *ret = field->writer(field, ssid);
+
+                       if (ret && has_newline(ret)) {
+                               wpa_printf(MSG_ERROR,
+                                          "Found newline in value for %s; not returning it",
+                                          var);
+                               os_free(ret);
+                               ret = NULL;
+                       }
+
+                       return ret;
+               }
        }
 
        return NULL;
@@ -2742,11 +3390,64 @@ static int wpa_config_set_cred_req_conn_capab(struct wpa_cred *cred,
 }
 
 
+static int wpa_config_set_cred_roaming_consortiums(struct wpa_cred *cred,
+                                                  const char *value)
+{
+       u8 roaming_consortiums[MAX_ROAMING_CONS][MAX_ROAMING_CONS_OI_LEN];
+       size_t roaming_consortiums_len[MAX_ROAMING_CONS];
+       unsigned int num_roaming_consortiums = 0;
+       const char *pos, *end;
+       size_t len;
+
+       os_memset(roaming_consortiums, 0, sizeof(roaming_consortiums));
+       os_memset(roaming_consortiums_len, 0, sizeof(roaming_consortiums_len));
+
+       for (pos = value;;) {
+               end = os_strchr(pos, ',');
+               len = end ? (size_t) (end - pos) : os_strlen(pos);
+               if (!end && len == 0)
+                       break;
+               if (len == 0 || (len & 1) != 0 ||
+                   len / 2 > MAX_ROAMING_CONS_OI_LEN ||
+                   hexstr2bin(pos,
+                              roaming_consortiums[num_roaming_consortiums],
+                              len / 2) < 0) {
+                       wpa_printf(MSG_INFO,
+                                  "Invalid roaming_consortiums entry: %s",
+                                  pos);
+                       return -1;
+               }
+               roaming_consortiums_len[num_roaming_consortiums] = len / 2;
+               num_roaming_consortiums++;
+
+               if (!end)
+                       break;
+
+               if (num_roaming_consortiums >= MAX_ROAMING_CONS) {
+                       wpa_printf(MSG_INFO,
+                                  "Too many roaming_consortiums OIs");
+                       return -1;
+               }
+
+               pos = end + 1;
+       }
+
+       os_memcpy(cred->roaming_consortiums, roaming_consortiums,
+                 sizeof(roaming_consortiums));
+       os_memcpy(cred->roaming_consortiums_len, roaming_consortiums_len,
+                 sizeof(roaming_consortiums_len));
+       cred->num_roaming_consortiums = num_roaming_consortiums;
+
+       return 0;
+}
+
+
 int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
                        const char *value, int line)
 {
        char *val;
        size_t len;
+       int res;
 
        if (os_strcmp(var, "temporary") == 0) {
                cred->temporary = atoi(value);
@@ -2790,6 +3491,8 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
 
        if (os_strcmp(var, "password") == 0 &&
            os_strncmp(value, "ext:", 4) == 0) {
+               if (has_newline(value))
+                       return -1;
                str_clear_free(cred->password);
                cred->password = os_strdup(value);
                cred->ext_password = 1;
@@ -2840,9 +3543,14 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
        }
 
        val = wpa_config_parse_string(value, &len);
-       if (val == NULL) {
+       if (val == NULL ||
+           (os_strcmp(var, "excluded_ssid") != 0 &&
+            os_strcmp(var, "roaming_consortium") != 0 &&
+            os_strcmp(var, "required_roaming_consortium") != 0 &&
+            has_newline(val))) {
                wpa_printf(MSG_ERROR, "Line %d: invalid field '%s' string "
                           "value '%s'.", line, var, value);
+               os_free(val);
                return -1;
        }
 
@@ -2962,6 +3670,16 @@ int wpa_config_set_cred(struct wpa_cred *cred, const char *var,
                return 0;
        }
 
+       if (os_strcmp(var, "roaming_consortiums") == 0) {
+               res = wpa_config_set_cred_roaming_consortiums(cred, val);
+               if (res < 0)
+                       wpa_printf(MSG_ERROR,
+                                  "Line %d: invalid roaming_consortiums",
+                                  line);
+               os_free(val);
+               return res;
+       }
+
        if (os_strcmp(var, "excluded_ssid") == 0) {
                struct excluded_ssid *e;
 
@@ -3273,6 +3991,31 @@ char * wpa_config_get_cred_no_key(struct wpa_cred *cred, const char *var)
                return buf;
        }
 
+       if (os_strcmp(var, "roaming_consortiums") == 0) {
+               size_t buflen;
+               char *buf, *pos;
+               size_t i;
+
+               if (!cred->num_roaming_consortiums)
+                       return NULL;
+               buflen = cred->num_roaming_consortiums *
+                       MAX_ROAMING_CONS_OI_LEN * 2 + 1;
+               buf = os_malloc(buflen);
+               if (!buf)
+                       return NULL;
+               pos = buf;
+               for (i = 0; i < cred->num_roaming_consortiums; i++) {
+                       if (i > 0)
+                               *pos++ = ',';
+                       pos += wpa_snprintf_hex(
+                               pos, buf + buflen - pos,
+                               cred->roaming_consortiums[i],
+                               cred->roaming_consortiums_len[i]);
+               }
+               *pos = '\0';
+               return buf;
+       }
+
        if (os_strcmp(var, "excluded_ssid") == 0) {
                unsigned int i;
                char *buf, *end, *pos;
@@ -3517,9 +4260,12 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
        config->user_mpm = DEFAULT_USER_MPM;
        config->max_peer_links = DEFAULT_MAX_PEER_LINKS;
        config->mesh_max_inactivity = DEFAULT_MESH_MAX_INACTIVITY;
+       config->dot11RSNASAERetransPeriod =
+               DEFAULT_DOT11_RSNA_SAE_RETRANS_PERIOD;
        config->fast_reauth = DEFAULT_FAST_REAUTH;
        config->p2p_go_intent = DEFAULT_P2P_GO_INTENT;
        config->p2p_intra_bss = DEFAULT_P2P_INTRA_BSS;
+       config->p2p_go_freq_change_policy = DEFAULT_P2P_GO_FREQ_MOVE;
        config->p2p_go_max_inactivity = DEFAULT_P2P_GO_MAX_INACTIVITY;
        config->p2p_optimize_listen_chan = DEFAULT_P2P_OPTIMIZE_LISTEN_CHAN;
        config->p2p_go_ctwindow = DEFAULT_P2P_GO_CTWINDOW;
@@ -3527,6 +4273,7 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
        config->bss_expiration_age = DEFAULT_BSS_EXPIRATION_AGE;
        config->bss_expiration_scan_count = DEFAULT_BSS_EXPIRATION_SCAN_COUNT;
        config->max_num_sta = DEFAULT_MAX_NUM_STA;
+       config->ap_isolate = DEFAULT_AP_ISOLATE;
        config->access_network_type = DEFAULT_ACCESS_NETWORK_TYPE;
        config->scan_cur_freq = DEFAULT_SCAN_CUR_FREQ;
        config->wmm_ac_params[0] = ac_be;
@@ -3537,11 +4284,20 @@ struct wpa_config * wpa_config_alloc_empty(const char *ctrl_interface,
        config->rand_addr_lifetime = DEFAULT_RAND_ADDR_LIFETIME;
        config->key_mgmt_offload = DEFAULT_KEY_MGMT_OFFLOAD;
        config->cert_in_cb = DEFAULT_CERT_IN_CB;
+       config->wpa_rsc_relaxation = DEFAULT_WPA_RSC_RELAXATION;
+
+#ifdef CONFIG_MBO
+       config->mbo_cell_capa = DEFAULT_MBO_CELL_CAPA;
+       config->disassoc_imminent_rssi_threshold =
+               DEFAULT_DISASSOC_IMMINENT_RSSI_THRESHOLD;
+       config->oce = DEFAULT_OCE_SUPPORT;
+#endif /* CONFIG_MBO */
 
        if (ctrl_interface)
                config->ctrl_interface = os_strdup(ctrl_interface);
        if (driver_param)
                config->driver_param = os_strdup(driver_param);
+       config->gas_rand_addr_lifetime = DEFAULT_RAND_ADDR_LIFETIME;
 
        return config;
 }
@@ -3643,6 +4399,12 @@ static int wpa_global_config_parse_str(const struct global_parse_data *data,
                return -1;
        }
 
+       if (has_newline(pos)) {
+               wpa_printf(MSG_ERROR, "Line %d: invalid %s value with newline",
+                          line, data->name);
+               return -1;
+       }
+
        tmp = os_strdup(pos);
        if (tmp == NULL)
                return -1;
@@ -3681,22 +4443,12 @@ static int wpa_global_config_parse_bin(const struct global_parse_data *data,
                                       struct wpa_config *config, int line,
                                       const char *pos)
 {
-       size_t len;
        struct wpabuf **dst, *tmp;
 
-       len = os_strlen(pos);
-       if (len & 0x01)
-               return -1;
-
-       tmp = wpabuf_alloc(len / 2);
-       if (tmp == NULL)
+       tmp = wpabuf_parse_bin(pos);
+       if (!tmp)
                return -1;
 
-       if (hexstr2bin(pos, wpabuf_put(tmp, len / 2), len / 2)) {
-               wpabuf_free(tmp);
-               return -1;
-       }
-
        dst = (struct wpabuf **) (((u8 *) config) + (long) data->param1);
        wpabuf_free(*dst);
        *dst = tmp;
@@ -3952,6 +4704,21 @@ static int wpa_config_process_p2p_no_go_freq(
        return 0;
 }
 
+
+static int wpa_config_process_p2p_device_persistent_mac_addr(
+       const struct global_parse_data *data,
+       struct wpa_config *config, int line, const char *pos)
+{
+       if (hwaddr_aton2(pos, config->p2p_device_persistent_mac_addr) < 0) {
+               wpa_printf(MSG_ERROR,
+                          "Line %d: Invalid p2p_device_persistent_mac_addr '%s'",
+                          line, pos);
+               return -1;
+       }
+
+       return 0;
+}
+
 #endif /* CONFIG_P2P */
 
 
@@ -4130,6 +4897,7 @@ static const struct global_parse_data global_fields[] = {
        { INT(user_mpm), 0 },
        { INT_RANGE(max_peer_links, 0, 255), 0 },
        { INT(mesh_max_inactivity), 0 },
+       { INT(dot11RSNASAERetransPeriod), 0 },
 #endif /* CONFIG_MESH */
        { INT(disable_scan_offload), 0 },
        { INT(fast_reauth), 0 },
@@ -4150,6 +4918,7 @@ static const struct global_parse_data global_fields[] = {
        { FUNC_NO_VAR(load_dynamic_eap), 0 },
 #ifdef CONFIG_WPS
        { FUNC(uuid), CFG_CHANGED_UUID },
+       { INT_RANGE(auto_uuid, 0, 1), 0 },
        { STR_RANGE(device_name, 0, WPS_DEV_NAME_MAX_LEN),
          CFG_CHANGED_DEVICE_NAME },
        { STR_RANGE(manufacturer, 0, 64), CFG_CHANGED_WPS_STRING },
@@ -4160,6 +4929,7 @@ static const struct global_parse_data global_fields[] = {
        { FUNC(os_version), CFG_CHANGED_OS_VERSION },
        { STR(config_methods), CFG_CHANGED_CONFIG_METHODS },
        { INT_RANGE(wps_cred_processing, 0, 2), 0 },
+       { INT_RANGE(wps_cred_add_sae, 0, 1), 0 },
        { FUNC(wps_vendor_ext_m1), CFG_CHANGED_VENDOR_EXTENSION },
 #endif /* CONFIG_WPS */
 #ifdef CONFIG_P2P
@@ -4173,6 +4943,7 @@ static const struct global_parse_data global_fields[] = {
        { INT_RANGE(persistent_reconnect, 0, 1), 0 },
        { INT_RANGE(p2p_intra_bss, 0, 1), CFG_CHANGED_P2P_INTRA_BSS },
        { INT(p2p_group_idle), 0 },
+       { INT_RANGE(p2p_go_freq_change_policy, 0, P2P_GO_FREQ_MOVE_MAX), 0 },
        { INT_RANGE(p2p_passphrase_len, 8, 63),
          CFG_CHANGED_P2P_PASSPHRASE_LEN },
        { FUNC(p2p_pref_chan), CFG_CHANGED_P2P_PREF_CHAN },
@@ -4181,6 +4952,8 @@ static const struct global_parse_data global_fields[] = {
        { INT_RANGE(p2p_optimize_listen_chan, 0, 1), 0 },
        { INT(p2p_go_ht40), 0 },
        { INT(p2p_go_vht), 0 },
+       { INT(p2p_go_he), 0 },
+       { INT(p2p_go_edmg), 0 },
        { INT(p2p_disabled), 0 },
        { INT_RANGE(p2p_go_ctwindow, 0, 127), 0 },
        { INT(p2p_no_group_iface), 0 },
@@ -4190,6 +4963,9 @@ static const struct global_parse_data global_fields[] = {
        { IPV4(ip_addr_start), 0 },
        { IPV4(ip_addr_end), 0 },
        { INT_RANGE(p2p_cli_probe, 0, 1), 0 },
+       { INT(p2p_device_random_mac_addr), 0 },
+       { FUNC(p2p_device_persistent_mac_addr), 0 },
+       { INT(p2p_interface_random_mac_addr), 0 },
 #endif /* CONFIG_P2P */
        { FUNC(country), CFG_CHANGED_COUNTRY },
        { INT(bss_max_count), 0 },
@@ -4198,6 +4974,7 @@ static const struct global_parse_data global_fields[] = {
        { INT_RANGE(filter_ssids, 0, 1), 0 },
        { INT_RANGE(filter_rssi, -100, 0), 0 },
        { INT(max_num_sta), 0 },
+       { INT_RANGE(ap_isolate, 0, 1), 0 },
        { INT_RANGE(disassoc_low_ack, 0, 1), 0 },
 #ifdef CONFIG_HS20
        { INT_RANGE(hs20, 0, 1), 0 },
@@ -4205,6 +4982,11 @@ static const struct global_parse_data global_fields[] = {
        { INT_RANGE(interworking, 0, 1), 0 },
        { FUNC(hessid), 0 },
        { INT_RANGE(access_network_type, 0, 15), 0 },
+       { INT_RANGE(go_interworking, 0, 1), 0 },
+       { INT_RANGE(go_access_network_type, 0, 15), 0 },
+       { INT_RANGE(go_internet, 0, 1), 0 },
+       { INT_RANGE(go_venue_group, 0, 255), 0 },
+       { INT_RANGE(go_venue_type, 0, 255), 0 },
        { INT_RANGE(pbc_in_m1, 0, 1), 0 },
        { STR(autoscan), 0 },
        { INT_RANGE(wps_nfc_dev_pw_id, 0x10, 0xffff),
@@ -4218,6 +5000,8 @@ static const struct global_parse_data global_fields[] = {
        { INT(okc), 0 },
        { INT(pmf), 0 },
        { FUNC(sae_groups), 0 },
+       { INT_RANGE(sae_pwe, 0, 3), 0 },
+       { INT_RANGE(sae_pmkid_in_assoc, 0, 1), 0 },
        { INT(dtim_period), 0 },
        { INT(beacon_int), 0 },
        { FUNC(ap_vendor_elements), 0 },
@@ -4225,9 +5009,10 @@ static const struct global_parse_data global_fields[] = {
        { FUNC(freq_list), 0 },
        { INT(scan_cur_freq), 0 },
        { INT(sched_scan_interval), 0 },
+       { INT(sched_scan_start_delay), 0 },
        { INT(tdls_external_control), 0},
        { STR(osu_dir), 0 },
-       { STR(wowlan_triggers), 0 },
+       { STR(wowlan_triggers), CFG_CHANGED_WOWLAN_TRIGGERS },
        { INT(p2p_search_delay), 0},
        { INT(mac_addr), 0 },
        { INT(rand_addr_lifetime), 0 },
@@ -4241,6 +5026,30 @@ static const struct global_parse_data global_fields[] = {
        { INT_RANGE(fst_priority, 1, FST_MAX_PRIO_VALUE), 0 },
        { INT_RANGE(fst_llt, 1, FST_MAX_LLT_MS), 0 },
 #endif /* CONFIG_FST */
+       { INT_RANGE(cert_in_cb, 0, 1), 0 },
+       { INT_RANGE(wpa_rsc_relaxation, 0, 1), 0 },
+       { STR(sched_scan_plans), CFG_CHANGED_SCHED_SCAN_PLANS },
+#ifdef CONFIG_MBO
+       { STR(non_pref_chan), 0 },
+       { INT_RANGE(mbo_cell_capa, MBO_CELL_CAPA_AVAILABLE,
+                   MBO_CELL_CAPA_NOT_SUPPORTED), 0 },
+       { INT_RANGE(disassoc_imminent_rssi_threshold, -120, 0), 0 },
+       { INT_RANGE(oce, 0, 3), 0 },
+#endif /* CONFIG_MBO */
+       { INT(gas_address3), 0 },
+       { INT_RANGE(ftm_responder, 0, 1), 0 },
+       { INT_RANGE(ftm_initiator, 0, 1), 0 },
+       { INT(gas_rand_addr_lifetime), 0 },
+       { INT_RANGE(gas_rand_mac_addr, 0, 2), 0 },
+#ifdef CONFIG_DPP
+       { INT_RANGE(dpp_config_processing, 0, 2), 0 },
+       { STR(dpp_name), 0 },
+       { STR(dpp_mud_url), 0 },
+#endif /* CONFIG_DPP */
+       { INT_RANGE(coloc_intf_reporting, 0, 1), 0 },
+#ifdef CONFIG_WNM
+       { INT_RANGE(disable_btm, 0, 1), CFG_CHANGED_DISABLE_BTM },
+#endif /* CONFIG_WNM */
 };
 
 #undef FUNC
@@ -4299,6 +5108,23 @@ int wpa_config_get_value(const char *name, struct wpa_config *config,
 }
 
 
+int wpa_config_get_num_global_field_names(void)
+{
+       return NUM_GLOBAL_FIELDS;
+}
+
+
+const char * wpa_config_get_global_field_name(unsigned int i, int *no_var)
+{
+       if (i >= NUM_GLOBAL_FIELDS)
+               return NULL;
+
+       if (no_var)
+               *no_var = !global_fields[i].param1;
+       return global_fields[i].name;
+}
+
+
 int wpa_config_process_global(struct wpa_config *config, char *pos, int line)
 {
        size_t i;
@@ -4339,6 +5165,7 @@ int wpa_config_process_global(struct wpa_config *config, char *pos, int line)
                                           "AC item", line);
                                return -1;
                        }
+                       return ret;
                }
 #endif /* CONFIG_AP */
                if (line < 0)