summary refs log tree commit diff
path: root/src/core/supplicant
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/supplicant')
-rw-r--r--src/core/supplicant/nm-supplicant-config.c170
-rw-r--r--src/core/supplicant/nm-supplicant-config.h4
-rw-r--r--src/core/supplicant/nm-supplicant-interface.c191
-rw-r--r--src/core/supplicant/tests/test-supplicant-config.c4
4 files changed, 277 insertions, 92 deletions
diff --git a/src/core/supplicant/nm-supplicant-config.c b/src/core/supplicant/nm-supplicant-config.c
index 9e066395..56cc832b 100644
--- a/src/core/supplicant/nm-supplicant-config.c
+++ b/src/core/supplicant/nm-supplicant-config.c
@@ -30,6 +30,7 @@ typedef struct {
 typedef struct {
     GHashTable    *config;
     GHashTable    *blobs;
+    char          *private_user;
     NMSupplCapMask capabilities;
     guint32        ap_scan;
     bool           fast_required : 1;
@@ -60,7 +61,7 @@ _get_capability(NMSupplicantConfigPrivate *priv, NMSupplCapType type)
 }
 
 NMSupplicantConfig *
-nm_supplicant_config_new(NMSupplCapMask capabilities)
+nm_supplicant_config_new(NMSupplCapMask capabilities, const char *private_user)
 {
     NMSupplicantConfigPrivate *priv;
     NMSupplicantConfig        *self;
@@ -69,6 +70,7 @@ nm_supplicant_config_new(NMSupplCapMask capabilities)
     priv = NM_SUPPLICANT_CONFIG_GET_PRIVATE(self);
 
     priv->capabilities = capabilities;
+    priv->private_user = g_strdup(private_user);
 
     return self;
 }
@@ -258,19 +260,19 @@ static gboolean
 nm_supplicant_config_add_blob_for_connection(NMSupplicantConfig *self,
                                              GBytes             *field,
                                              const char         *name,
-                                             const char         *con_uid,
+                                             const char         *con_uuid,
                                              GError            **error)
 {
     if (field && g_bytes_get_size(field)) {
-        gs_free char *uid = NULL;
+        gs_free char *blob_id = NULL;
         char         *p;
 
-        uid = g_strdup_printf("%s-%s", con_uid, name);
-        for (p = uid; *p; p++) {
+        blob_id = g_strdup_printf("%s-%s", con_uuid, name);
+        for (p = blob_id; *p; p++) {
             if (*p == '/')
                 *p = '-';
         }
-        if (!nm_supplicant_config_add_blob(self, name, field, uid, error))
+        if (!nm_supplicant_config_add_blob(self, name, field, blob_id, error))
             return FALSE;
     }
     return TRUE;
@@ -283,6 +285,7 @@ nm_supplicant_config_finalize(GObject *object)
 
     g_hash_table_destroy(priv->config);
     nm_clear_pointer(&priv->blobs, g_hash_table_destroy);
+    nm_clear_pointer(&priv->private_user, g_free);
 
     G_OBJECT_CLASS(nm_supplicant_config_parent_class)->finalize(object);
 }
@@ -930,6 +933,7 @@ nm_supplicant_config_add_setting_wireless_security(NMSupplicantConfig
                                                    guint32                       mtu,
                                                    NMSettingWirelessSecurityPmf  pmf,
                                                    NMSettingWirelessSecurityFils fils,
+                                                   GHashTable                   *files,
                                                    GError                      **error)
 {
     NMSupplicantConfigPrivate    *priv          = NM_SUPPLICANT_CONFIG_GET_PRIVATE(self);
@@ -1284,6 +1288,7 @@ nm_supplicant_config_add_setting_wireless_security(NMSupplicantConfig
                                                         con_uuid,
                                                         mtu,
                                                         FALSE,
+                                                        files,
                                                         error))
                 return FALSE;
         }
@@ -1365,6 +1370,7 @@ nm_supplicant_config_add_setting_8021x(NMSupplicantConfig *self,
                                        const char         *con_uuid,
                                        guint32             mtu,
                                        gboolean            wired,
+                                       GHashTable         *files,
                                        GError            **error)
 {
     NMSupplicantConfigPrivate    *priv;
@@ -1594,24 +1600,21 @@ nm_supplicant_config_add_setting_8021x(NMSupplicantConfig *self,
     }
 
     /* CA certificate */
+    path  = NULL;
+    bytes = NULL;
     if (ca_cert_override) {
-        if (!add_string_val(self, ca_cert_override, "ca_cert", FALSE, NULL, error))
-            return FALSE;
+        /* This is a build-time-configured system-wide file path, no need to pass
+         * it as a blob */
+        path = ca_cert_override;
     } else {
         switch (nm_setting_802_1x_get_ca_cert_scheme(setting)) {
         case NM_SETTING_802_1X_CK_SCHEME_BLOB:
             bytes = nm_setting_802_1x_get_ca_cert_blob(setting);
-            if (!nm_supplicant_config_add_blob_for_connection(self,
-                                                              bytes,
-                                                              "ca_cert",
-                                                              con_uuid,
-                                                              error))
-                return FALSE;
             break;
         case NM_SETTING_802_1X_CK_SCHEME_PATH:
             path = nm_setting_802_1x_get_ca_cert_path(setting);
-            if (!add_string_val(self, path, "ca_cert", FALSE, NULL, error))
-                return FALSE;
+            if (priv->private_user)
+                bytes = nm_g_hash_table_lookup(files, path);
             break;
         case NM_SETTING_802_1X_CK_SCHEME_PKCS11:
             if (!add_pkcs11_uri_with_pin(self,
@@ -1627,26 +1630,32 @@ nm_supplicant_config_add_setting_8021x(NMSupplicantConfig *self,
             break;
         }
     }
+    if (bytes) {
+        if (!nm_supplicant_config_add_blob_for_connection(self, bytes, "ca_cert", con_uuid, error))
+            return FALSE;
+    } else if (path) {
+        /* Private connections cannot use paths other than the system CA store */
+        g_return_val_if_fail(ca_cert_override || !priv->private_user, FALSE);
+        if (!add_string_val(self, path, "ca_cert", FALSE, NULL, error))
+            return FALSE;
+    }
 
     /* Phase 2 CA certificate */
+    path  = NULL;
+    bytes = NULL;
     if (ca_cert_override) {
-        if (!add_string_val(self, ca_cert_override, "ca_cert2", FALSE, NULL, error))
-            return FALSE;
+        /* This is a build-time-configured system-wide file path, no need to pass
+         * it as a blob */
+        path = ca_cert_override;
     } else {
         switch (nm_setting_802_1x_get_phase2_ca_cert_scheme(setting)) {
         case NM_SETTING_802_1X_CK_SCHEME_BLOB:
             bytes = nm_setting_802_1x_get_phase2_ca_cert_blob(setting);
-            if (!nm_supplicant_config_add_blob_for_connection(self,
-                                                              bytes,
-                                                              "ca_cert2",
-                                                              con_uuid,
-                                                              error))
-                return FALSE;
             break;
         case NM_SETTING_802_1X_CK_SCHEME_PATH:
             path = nm_setting_802_1x_get_phase2_ca_cert_path(setting);
-            if (!add_string_val(self, path, "ca_cert2", FALSE, NULL, error))
-                return FALSE;
+            if (priv->private_user)
+                bytes = nm_g_hash_table_lookup(files, path);
             break;
         case NM_SETTING_802_1X_CK_SCHEME_PKCS11:
             if (!add_pkcs11_uri_with_pin(
@@ -1663,6 +1672,15 @@ nm_supplicant_config_add_setting_8021x(NMSupplicantConfig *self,
             break;
         }
     }
+    if (bytes) {
+        if (!nm_supplicant_config_add_blob_for_connection(self, bytes, "ca_cert2", con_uuid, error))
+            return FALSE;
+    } else if (path) {
+        /* Private connections cannot use paths other than the system CA store */
+        g_return_val_if_fail(ca_cert_override || !priv->private_user, FALSE);
+        if (!add_string_val(self, path, "ca_cert2", FALSE, NULL, error))
+            return FALSE;
+    }
 
     /* Subject match */
     value = nm_setting_802_1x_get_subject_match(setting);
@@ -1714,21 +1732,17 @@ nm_supplicant_config_add_setting_8021x(NMSupplicantConfig *self,
 
     /* Private key */
     added = FALSE;
+    path  = NULL;
+    bytes = NULL;
     switch (nm_setting_802_1x_get_private_key_scheme(setting)) {
     case NM_SETTING_802_1X_CK_SCHEME_BLOB:
         bytes = nm_setting_802_1x_get_private_key_blob(setting);
-        if (!nm_supplicant_config_add_blob_for_connection(self,
-                                                          bytes,
-                                                          "private_key",
-                                                          con_uuid,
-                                                          error))
-            return FALSE;
         added = TRUE;
         break;
     case NM_SETTING_802_1X_CK_SCHEME_PATH:
         path = nm_setting_802_1x_get_private_key_path(setting);
-        if (!add_string_val(self, path, "private_key", FALSE, NULL, error))
-            return FALSE;
+        if (priv->private_user)
+            bytes = nm_g_hash_table_lookup(files, path);
         added = TRUE;
         break;
     case NM_SETTING_802_1X_CK_SCHEME_PKCS11:
@@ -1745,6 +1759,19 @@ nm_supplicant_config_add_setting_8021x(NMSupplicantConfig *self,
     default:
         break;
     }
+    if (bytes) {
+        if (!nm_supplicant_config_add_blob_for_connection(self,
+                                                          bytes,
+                                                          "private_key",
+                                                          con_uuid,
+                                                          error))
+            return FALSE;
+    } else if (path) {
+        /* Private connections cannot use paths */
+        g_return_val_if_fail(!priv->private_user, FALSE);
+        if (!add_string_val(self, path, "private_key", FALSE, NULL, error))
+            return FALSE;
+    }
 
     if (added) {
         NMSetting8021xCKFormat format;
@@ -1768,20 +1795,16 @@ nm_supplicant_config_add_setting_8021x(NMSupplicantConfig *self,
             /* Only add the client cert if the private key is not PKCS#12, as
              * wpa_supplicant configuration directs us to do.
              */
+            path  = NULL;
+            bytes = NULL;
             switch (nm_setting_802_1x_get_client_cert_scheme(setting)) {
             case NM_SETTING_802_1X_CK_SCHEME_BLOB:
                 bytes = nm_setting_802_1x_get_client_cert_blob(setting);
-                if (!nm_supplicant_config_add_blob_for_connection(self,
-                                                                  bytes,
-                                                                  "client_cert",
-                                                                  con_uuid,
-                                                                  error))
-                    return FALSE;
                 break;
             case NM_SETTING_802_1X_CK_SCHEME_PATH:
                 path = nm_setting_802_1x_get_client_cert_path(setting);
-                if (!add_string_val(self, path, "client_cert", FALSE, NULL, error))
-                    return FALSE;
+                if (priv->private_user)
+                    bytes = nm_g_hash_table_lookup(files, path);
                 break;
             case NM_SETTING_802_1X_CK_SCHEME_PKCS11:
                 if (!add_pkcs11_uri_with_pin(
@@ -1797,26 +1820,35 @@ nm_supplicant_config_add_setting_8021x(NMSupplicantConfig *self,
             default:
                 break;
             }
+            if (bytes) {
+                if (!nm_supplicant_config_add_blob_for_connection(self,
+                                                                  bytes,
+                                                                  "client_cert",
+                                                                  con_uuid,
+                                                                  error))
+                    return FALSE;
+            } else if (path) {
+                /* Private connections cannot use paths */
+                g_return_val_if_fail(!priv->private_user, FALSE);
+                if (!add_string_val(self, path, "client_cert", FALSE, NULL, error))
+                    return FALSE;
+            }
         }
     }
 
     /* Phase 2 private key */
     added = FALSE;
+    path  = NULL;
+    bytes = NULL;
     switch (nm_setting_802_1x_get_phase2_private_key_scheme(setting)) {
     case NM_SETTING_802_1X_CK_SCHEME_BLOB:
         bytes = nm_setting_802_1x_get_phase2_private_key_blob(setting);
-        if (!nm_supplicant_config_add_blob_for_connection(self,
-                                                          bytes,
-                                                          "private_key2",
-                                                          con_uuid,
-                                                          error))
-            return FALSE;
         added = TRUE;
         break;
     case NM_SETTING_802_1X_CK_SCHEME_PATH:
         path = nm_setting_802_1x_get_phase2_private_key_path(setting);
-        if (!add_string_val(self, path, "private_key2", FALSE, NULL, error))
-            return FALSE;
+        if (priv->private_user)
+            bytes = nm_g_hash_table_lookup(files, path);
         added = TRUE;
         break;
     case NM_SETTING_802_1X_CK_SCHEME_PKCS11:
@@ -1834,6 +1866,19 @@ nm_supplicant_config_add_setting_8021x(NMSupplicantConfig *self,
     default:
         break;
     }
+    if (bytes) {
+        if (!nm_supplicant_config_add_blob_for_connection(self,
+                                                          bytes,
+                                                          "private_key2",
+                                                          con_uuid,
+                                                          error))
+            return FALSE;
+    } else if (path) {
+        /* Private connections cannot use paths */
+        g_return_val_if_fail(!priv->private_user, FALSE);
+        if (!add_string_val(self, path, "private_key2", FALSE, NULL, error))
+            return FALSE;
+    }
 
     if (added) {
         NMSetting8021xCKFormat format;
@@ -1857,20 +1902,16 @@ nm_supplicant_config_add_setting_8021x(NMSupplicantConfig *self,
             /* Only add the client cert if the private key is not PKCS#12, as
              * wpa_supplicant configuration directs us to do.
              */
+            path  = NULL;
+            bytes = NULL;
             switch (nm_setting_802_1x_get_phase2_client_cert_scheme(setting)) {
             case NM_SETTING_802_1X_CK_SCHEME_BLOB:
                 bytes = nm_setting_802_1x_get_phase2_client_cert_blob(setting);
-                if (!nm_supplicant_config_add_blob_for_connection(self,
-                                                                  bytes,
-                                                                  "client_cert2",
-                                                                  con_uuid,
-                                                                  error))
-                    return FALSE;
                 break;
             case NM_SETTING_802_1X_CK_SCHEME_PATH:
                 path = nm_setting_802_1x_get_phase2_client_cert_path(setting);
-                if (!add_string_val(self, path, "client_cert2", FALSE, NULL, error))
-                    return FALSE;
+                if (priv->private_user)
+                    bytes = nm_g_hash_table_lookup(files, path);
                 break;
             case NM_SETTING_802_1X_CK_SCHEME_PKCS11:
                 if (!add_pkcs11_uri_with_pin(
@@ -1886,6 +1927,19 @@ nm_supplicant_config_add_setting_8021x(NMSupplicantConfig *self,
             default:
                 break;
             }
+            if (bytes) {
+                if (!nm_supplicant_config_add_blob_for_connection(self,
+                                                                  bytes,
+                                                                  "client_cert2",
+                                                                  con_uuid,
+                                                                  error))
+                    return FALSE;
+            } else if (path) {
+                /* Private connections cannot use paths */
+                g_return_val_if_fail(!priv->private_user, FALSE);
+                if (!add_string_val(self, path, "client_cert2", FALSE, NULL, error))
+                    return FALSE;
+            }
         }
     }
 
diff --git a/src/core/supplicant/nm-supplicant-config.h b/src/core/supplicant/nm-supplicant-config.h
index c52b756e..96460b86 100644
--- a/src/core/supplicant/nm-supplicant-config.h
+++ b/src/core/supplicant/nm-supplicant-config.h
@@ -29,7 +29,7 @@ typedef struct _NMSupplicantConfigClass NMSupplicantConfigClass;
 
 GType nm_supplicant_config_get_type(void);
 
-NMSupplicantConfig *nm_supplicant_config_new(NMSupplCapMask capabilities);
+NMSupplicantConfig *nm_supplicant_config_new(NMSupplCapMask capabilities, const char *private_user);
 
 guint32 nm_supplicant_config_get_ap_scan(NMSupplicantConfig *self);
 
@@ -57,6 +57,7 @@ gboolean nm_supplicant_config_add_setting_wireless_security(NMSupplicantConfig
                                                             guint32         mtu,
                                                             NMSettingWirelessSecurityPmf  pmf,
                                                             NMSettingWirelessSecurityFils fils,
+                                                            GHashTable                   *files,
                                                             GError                      **error);
 
 gboolean nm_supplicant_config_add_no_security(NMSupplicantConfig *self, GError **error);
@@ -66,6 +67,7 @@ gboolean nm_supplicant_config_add_setting_8021x(NMSupplicantConfig *self,
                                                 const char         *con_uuid,
                                                 guint32             mtu,
                                                 gboolean            wired,
+                                                GHashTable         *files,
                                                 GError            **error);
 
 gboolean nm_supplicant_config_add_setting_macsec(NMSupplicantConfig    *self,
diff --git a/src/core/supplicant/nm-supplicant-interface.c b/src/core/supplicant/nm-supplicant-interface.c
index 514b7c0d..ef72447a 100644
--- a/src/core/supplicant/nm-supplicant-interface.c
+++ b/src/core/supplicant/nm-supplicant-interface.c
@@ -46,6 +46,7 @@ typedef struct {
     gpointer                     user_data;
     guint                        fail_on_idle_id;
     guint                        blobs_left;
+    guint                        remove_blobs_left;
     guint                        calls_left;
     struct _AddNetworkData      *add_network_data;
 } AssocData;
@@ -2264,6 +2265,7 @@ assoc_add_blob_cb(GObject *source, GAsyncResult *result, gpointer user_data)
         return;
     }
 
+    nm_assert(priv->assoc_data->blobs_left > 0);
     priv->assoc_data->blobs_left--;
     _LOGT("assoc[" NM_HASH_OBFUSCATE_PTR_FMT "]: blob added (%u left)",
           NM_HASH_OBFUSCATE_PTR(priv->assoc_data),
@@ -2273,18 +2275,156 @@ assoc_add_blob_cb(GObject *source, GAsyncResult *result, gpointer user_data)
 }
 
 static void
+assoc_add_blobs(NMSupplicantInterface *self)
+{
+    NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE(self);
+    GHashTable                   *blobs;
+    GHashTableIter                iter;
+    const char                   *blob_name;
+    GBytes                       *blob_data;
+
+    blobs                        = nm_supplicant_config_get_blobs(priv->assoc_data->cfg);
+    priv->assoc_data->blobs_left = nm_g_hash_table_size(blobs);
+
+    _LOGT("assoc[" NM_HASH_OBFUSCATE_PTR_FMT "]: need to add %u blobs",
+          NM_HASH_OBFUSCATE_PTR(priv->assoc_data),
+          priv->assoc_data->blobs_left);
+
+    if (priv->assoc_data->blobs_left == 0) {
+        assoc_call_select_network(self);
+        return;
+    }
+
+    g_hash_table_iter_init(&iter, blobs);
+    while (g_hash_table_iter_next(&iter, (gpointer) &blob_name, (gpointer) &blob_data)) {
+        _LOGT("assoc[" NM_HASH_OBFUSCATE_PTR_FMT "]: adding blob '%s'",
+              NM_HASH_OBFUSCATE_PTR(priv->assoc_data),
+              blob_name);
+        _dbus_connection_call(
+            self,
+            NM_WPAS_DBUS_IFACE_INTERFACE,
+            "AddBlob",
+            g_variant_new("(s@ay)", blob_name, nm_g_bytes_to_variant_ay(blob_data)),
+            G_VARIANT_TYPE("()"),
+            G_DBUS_CALL_FLAGS_NONE,
+            DBUS_TIMEOUT_MSEC,
+            priv->assoc_data->cancellable,
+            assoc_add_blob_cb,
+            self);
+    }
+}
+
+static void
+assoc_remove_blob_cb(GObject *source, GAsyncResult *result, gpointer user_data)
+{
+    NMSupplicantInterface        *self;
+    NMSupplicantInterfacePrivate *priv;
+    gs_free_error GError         *error = NULL;
+    gs_unref_variant GVariant    *res   = NULL;
+
+    res = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error);
+    if (nm_utils_error_is_cancelled(error))
+        return;
+
+    self = NM_SUPPLICANT_INTERFACE(user_data);
+    priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE(self);
+
+    /* We don't consider a failure fatal. The new association might be able
+     * to proceed even with the existing blobs, if they don't conflict with new
+     * ones. */
+
+    nm_assert(priv->assoc_data->remove_blobs_left > 0);
+    priv->assoc_data->remove_blobs_left--;
+
+    if (error) {
+        g_dbus_error_strip_remote_error(error);
+        _LOGD("assoc[" NM_HASH_OBFUSCATE_PTR_FMT "]: failed to delete blob: %s",
+              NM_HASH_OBFUSCATE_PTR(priv->assoc_data),
+              error->message);
+    } else {
+        _LOGT("assoc[" NM_HASH_OBFUSCATE_PTR_FMT "]: blob removed (%u left)",
+              NM_HASH_OBFUSCATE_PTR(priv->assoc_data),
+              priv->assoc_data->remove_blobs_left);
+    }
+
+    if (priv->assoc_data->remove_blobs_left == 0)
+        assoc_add_blobs(self);
+}
+
+static void
+assoc_get_blobs_cb(GObject *source, GAsyncResult *result, gpointer user_data)
+{
+    NMSupplicantInterface        *self;
+    NMSupplicantInterfacePrivate *priv;
+    gs_free_error GError         *error = NULL;
+    gs_unref_variant GVariant    *res   = NULL;
+    gs_unref_variant GVariant    *value = NULL;
+    GVariantIter                  iter;
+    const char                   *blob_name;
+    GVariant                     *blob_data;
+
+    res = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error);
+    if (nm_utils_error_is_cancelled(error))
+        return;
+
+    self = NM_SUPPLICANT_INTERFACE(user_data);
+    priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE(self);
+
+    if (error) {
+        _LOGD("assoc[" NM_HASH_OBFUSCATE_PTR_FMT "]: failed to get blob list: %s",
+              NM_HASH_OBFUSCATE_PTR(priv->assoc_data),
+              error->message);
+        assoc_add_blobs(self);
+        return;
+    }
+
+    g_variant_get(res, "(v)", &value);
+
+    /* While the "Blobs" property is documented as type "as", it is actually "a{say}" */
+    if (!value || !g_variant_is_of_type(value, G_VARIANT_TYPE("a{say}"))) {
+        _LOGD("assoc[" NM_HASH_OBFUSCATE_PTR_FMT "]: failed to get blob list: wrong return type %s",
+              NM_HASH_OBFUSCATE_PTR(priv->assoc_data),
+              value ? g_variant_get_type_string(value) : "NULL");
+        assoc_add_blobs(self);
+        return;
+    }
+
+    g_variant_iter_init(&iter, value);
+    priv->assoc_data->remove_blobs_left = g_variant_iter_n_children(&iter);
+    _LOGT("assoc[" NM_HASH_OBFUSCATE_PTR_FMT "]: need to delete %u blobs",
+          NM_HASH_OBFUSCATE_PTR(priv->assoc_data),
+          priv->assoc_data->remove_blobs_left);
+
+    if (priv->assoc_data->remove_blobs_left == 0) {
+        assoc_add_blobs(self);
+    } else {
+        while (g_variant_iter_loop(&iter, "{&s@ay}", &blob_name, &blob_data)) {
+            _LOGT("assoc[" NM_HASH_OBFUSCATE_PTR_FMT "]: removing blob '%s'",
+                  NM_HASH_OBFUSCATE_PTR(priv->assoc_data),
+                  blob_name);
+            _dbus_connection_call(self,
+                                  NM_WPAS_DBUS_IFACE_INTERFACE,
+                                  "RemoveBlob",
+                                  g_variant_new("(s)", blob_name),
+                                  G_VARIANT_TYPE("()"),
+                                  G_DBUS_CALL_FLAGS_NONE,
+                                  DBUS_TIMEOUT_MSEC,
+                                  priv->assoc_data->cancellable,
+                                  assoc_remove_blob_cb,
+                                  self);
+        }
+    }
+}
+
+static void
 assoc_add_network_cb(GObject *source, GAsyncResult *result, gpointer user_data)
 {
     AddNetworkData                 *add_network_data = user_data;
     AssocData                      *assoc_data;
     NMSupplicantInterface          *self;
     NMSupplicantInterfacePrivate   *priv;
-    gs_unref_variant GVariant      *res   = NULL;
-    gs_free_error GError           *error = NULL;
-    GHashTable                     *blobs;
-    GHashTableIter                  iter;
-    const char                     *blob_name;
-    GBytes                         *blob_data;
+    gs_unref_variant GVariant      *res         = NULL;
+    gs_free_error GError           *error       = NULL;
     nm_auto_ref_string NMRefString *name_owner  = NULL;
     nm_auto_ref_string NMRefString *object_path = NULL;
 
@@ -2336,34 +2476,21 @@ assoc_add_network_cb(GObject *source, GAsyncResult *result, gpointer user_data)
     nm_assert(!priv->net_path);
     g_variant_get(res, "(o)", &priv->net_path);
 
-    /* Send blobs first; otherwise jump to selecting the network */
-    blobs                        = nm_supplicant_config_get_blobs(priv->assoc_data->cfg);
-    priv->assoc_data->blobs_left = blobs ? g_hash_table_size(blobs) : 0u;
-
-    _LOGT("assoc[" NM_HASH_OBFUSCATE_PTR_FMT "]: network added (%s) (%u blobs left)",
+    _LOGT("assoc[" NM_HASH_OBFUSCATE_PTR_FMT "]: network added (%s)",
           NM_HASH_OBFUSCATE_PTR(priv->assoc_data),
-          priv->net_path,
-          priv->assoc_data->blobs_left);
-
-    if (priv->assoc_data->blobs_left == 0) {
-        assoc_call_select_network(self);
-        return;
-    }
+          priv->net_path);
 
-    g_hash_table_iter_init(&iter, blobs);
-    while (g_hash_table_iter_next(&iter, (gpointer) &blob_name, (gpointer) &blob_data)) {
-        _dbus_connection_call(
-            self,
-            NM_WPAS_DBUS_IFACE_INTERFACE,
-            "AddBlob",
-            g_variant_new("(s@ay)", blob_name, nm_g_bytes_to_variant_ay(blob_data)),
-            G_VARIANT_TYPE("()"),
-            G_DBUS_CALL_FLAGS_NONE,
-            DBUS_TIMEOUT_MSEC,
-            priv->assoc_data->cancellable,
-            assoc_add_blob_cb,
-            self);
-    }
+    /* Delete any existing blobs before adding new ones */
+    _dbus_connection_call(self,
+                          DBUS_INTERFACE_PROPERTIES,
+                          "Get",
+                          g_variant_new("(ss)", NM_WPAS_DBUS_IFACE_INTERFACE, "Blobs"),
+                          G_VARIANT_TYPE("(v)"),
+                          G_DBUS_CALL_FLAGS_NONE,
+                          DBUS_TIMEOUT_MSEC,
+                          priv->assoc_data->cancellable,
+                          assoc_get_blobs_cb,
+                          self);
 }
 
 static void
diff --git a/src/core/supplicant/tests/test-supplicant-config.c b/src/core/supplicant/tests/test-supplicant-config.c
index 1ca5b26e..416fe005 100644
--- a/src/core/supplicant/tests/test-supplicant-config.c
+++ b/src/core/supplicant/tests/test-supplicant-config.c
@@ -98,7 +98,8 @@ build_supplicant_config(NMConnection  *connection,
     NMSetting8021x                     *s_8021x;
     gboolean                            success;
 
-    config = nm_supplicant_config_new(capabilities);
+    config = nm_supplicant_config_new(capabilities,
+                                      nm_utils_get_connection_first_permissions_user(connection));
 
     s_wifi = nm_connection_get_setting_wireless(connection);
     g_assert(s_wifi);
@@ -120,6 +121,7 @@ build_supplicant_config(NMConnection  *connection,
                                                                mtu,
                                                                pmf,
                                                                fils,
+                                                               NULL,
                                                                &error);
     } else {
         success = nm_supplicant_config_add_no_security(config, &error);