/** * nm_secret_agent_simple_enable: * @self: the #NMSecretAgentSimple * @path: (allow-none): the path of the connection (if any) to handle secrets * for. If %NULL, secrets for any connection will be handled. * * Enables servicing the requests including the already queued ones. If @path * is given, the agent will only handle requests for connections that match * @path. */ void nm_secret_agent_simple_enable (NMSecretAgentSimple *self, const char *path) { NMSecretAgentSimplePrivate *priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE (self); GList *requests, *iter; GError *error; if (g_strcmp0 (path, priv->path) != 0) { g_free (priv->path); priv->path = g_strdup (path); } if (priv->enabled) return; priv->enabled = TRUE; /* Service pending secret requests. */ requests = g_hash_table_get_values (priv->requests); for (iter = requests; iter; iter = g_list_next (iter)) { NMSecretAgentSimpleRequest *request = iter->data; if (g_str_has_prefix (request->request_id, priv->path)) { request_secrets_from_ui (request); } else { /* We only handle requests for connection with @path if set. */ error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_FAILED, "Request for %s secrets doesn't match path %s", request->request_id, priv->path); request->callback (NM_SECRET_AGENT_OLD (self), request->connection, NULL, error, request->callback_data); g_hash_table_remove (priv->requests, request->request_id); g_error_free (error); } } g_list_free (requests); }
static void nm_secret_agent_simple_finalize (GObject *object) { NMSecretAgentSimplePrivate *priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE (object); GError *error; GHashTableIter iter; gpointer key; gpointer value; error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_AGENT_CANCELED, "The secret agent is going away"); g_hash_table_iter_init (&iter, priv->requests); while (g_hash_table_iter_next (&iter, &key, &value)) { NMSecretAgentSimpleRequest *request = value; request->callback (NM_SECRET_AGENT_OLD (object), request->connection, NULL, error, request->callback_data); } g_hash_table_destroy (priv->requests); g_error_free (error); g_free (priv->path); G_OBJECT_CLASS (nm_secret_agent_simple_parent_class)->finalize (object); }
static void cancel_get_secrets (NMSecretAgentOld *agent, const char *connection_path, const char *setting_name) { AppletAgentPrivate *priv = APPLET_AGENT_GET_PRIVATE (agent); GHashTableIter iter; Request *r; GError *error; error = g_error_new_literal (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_AGENT_CANCELED, "Canceled by NetworkManager"); g_hash_table_iter_init (&iter, priv->requests); while (g_hash_table_iter_next (&iter, NULL, (gpointer) &r)) { /* Only care about GetSecrets requests here */ if (r->get_callback == NULL) continue; /* Cancel any matching GetSecrets call */ if ( g_strcmp0 (r->path, connection_path) == 0 && g_strcmp0 (r->setting_name, setting_name) == 0) { /* cancel outstanding keyring operations */ g_cancellable_cancel (r->cancellable); r->get_callback (NM_SECRET_AGENT_OLD (r->agent), r->connection, NULL, error, r->callback_data); g_hash_table_iter_remove (&iter); g_signal_emit (r->agent, signals[CANCEL_SECRETS], 0, GUINT_TO_POINTER (r->id)); } } g_error_free (error); }
/** * nm_secret_agent_simple_response: * @self: the #NMSecretAgentSimple * @request_id: the request ID being responded to * @secrets: (allow-none): the array of secrets, or %NULL * * Response to a #NMSecretAgentSimple::get-secrets signal. * * If the user provided secrets, the caller should set the * corresponding <literal>value</literal> fields in the * #NMSecretAgentSimpleSecrets (freeing any initial values they had), and * pass the array to nm_secret_agent_simple_response(). If the user * cancelled the request, @secrets should be NULL. */ void nm_secret_agent_simple_response (NMSecretAgentSimple *self, const char *request_id, GPtrArray *secrets) { NMSecretAgentSimplePrivate *priv; NMSecretAgentSimpleRequest *request; GVariant *dict = NULL; GError *error = NULL; int i; g_return_if_fail (NM_IS_SECRET_AGENT_SIMPLE (self)); priv = NM_SECRET_AGENT_SIMPLE_GET_PRIVATE (self); request = g_hash_table_lookup (priv->requests, request_id); g_return_if_fail (request != NULL); if (secrets) { GVariantBuilder conn_builder, *setting_builder; GHashTable *settings; GHashTableIter iter; const char *name; settings = g_hash_table_new (g_str_hash, g_str_equal); for (i = 0; i < secrets->len; i++) { NMSecretAgentSimpleSecretReal *secret = secrets->pdata[i]; setting_builder = g_hash_table_lookup (settings, nm_setting_get_name (secret->setting)); if (!setting_builder) { setting_builder = g_variant_builder_new (NM_VARIANT_TYPE_SETTING); g_hash_table_insert (settings, (char *) nm_setting_get_name (secret->setting), setting_builder); } g_variant_builder_add (setting_builder, "{sv}", secret->property, g_variant_new_string (secret->base.value)); } g_variant_builder_init (&conn_builder, NM_VARIANT_TYPE_CONNECTION); g_hash_table_iter_init (&iter, settings); while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &setting_builder)) g_variant_builder_add (&conn_builder, "{sa{sv}}", name, setting_builder); dict = g_variant_builder_end (&conn_builder); g_hash_table_destroy (settings); } else { error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_USER_CANCELED, "User cancelled"); } request->callback (NM_SECRET_AGENT_OLD (self), request->connection, dict, error, request->callback_data); g_clear_error (&error); g_hash_table_remove (priv->requests, request_id); }
static void save_request_try_complete (Request *r) { /* Only call the SaveSecrets callback and free the request when all the * secrets have been saved to the keyring. */ if (r->keyring_calls == 0) { if (!g_cancellable_is_cancelled (r->cancellable)) r->save_callback (NM_SECRET_AGENT_OLD (r->agent), r->connection, NULL, r->callback_data); request_free (r); } }
static void get_secrets_cb (AppletAgent *self, GVariant *secrets, GError *error, gpointer user_data) { Request *r = user_data; /* 'secrets' shouldn't be valid if there was an error */ if (error) { g_warn_if_fail (secrets == NULL); secrets = NULL; } if (!g_cancellable_is_cancelled (r->cancellable)) { /* Save updated secrets as long as user-interaction was allowed; otherwise * we'd be saving secrets we just pulled out of the keyring which is somewhat * redundant. */ if (secrets && (r->flags != NM_SECRET_AGENT_GET_SECRETS_FLAG_NONE)) { NMConnection *dupl; GVariantIter iter; const char *setting_name; /* Copy the existing connection and update its secrets */ dupl = nm_simple_connection_new_clone (r->connection); g_variant_iter_init (&iter, secrets); while (g_variant_iter_next (&iter, "{&s@a{sv}}", (gpointer) &setting_name, NULL)) nm_connection_update_secrets (dupl, setting_name, secrets, NULL); /* And save updated secrets to the keyring */ nm_secret_agent_old_save_secrets (NM_SECRET_AGENT_OLD (self), dupl, get_save_cb, NULL); g_object_unref (dupl); } r->get_callback (NM_SECRET_AGENT_OLD (r->agent), r->connection, secrets, error, r->callback_data); } request_free (r); }
static void keyring_find_secrets_cb (GObject *source, GAsyncResult *result, gpointer user_data) { Request *r = user_data; GError *error = NULL; GError *search_error = NULL; const char *connection_id = NULL; GVariantBuilder builder_setting, builder_connection; GVariant *settings = NULL; GList *list = NULL; GList *iter; gboolean hint_found = FALSE, ask = FALSE; r->keyring_calls--; if (g_cancellable_is_cancelled (r->cancellable)) { /* Callback already called by NM or dispose */ request_free (r); return; } list = secret_service_search_finish (NULL, result, &search_error); connection_id = nm_connection_get_id (r->connection); if (g_error_matches (search_error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { error = g_error_new_literal (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_USER_CANCELED, "The secrets request was canceled by the user"); g_error_free (search_error); goto done; } else if (search_error) { error = g_error_new (NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_FAILED, "%s.%d - failed to read secrets from keyring (%s)", __FILE__, __LINE__, search_error->message); g_error_free (search_error); goto done; } /* Only ask if we're allowed to, so that eg a connection editor which * requests secrets for its UI, for a connection which doesn't have any * secrets yet, doesn't trigger the applet secrets dialog. */ if ( (r->flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION) && g_list_length (list) == 0) { g_message ("No keyring secrets found for %s/%s; asking user.", connection_id, r->setting_name); ask_for_secrets (r); return; } g_variant_builder_init (&builder_setting, NM_VARIANT_TYPE_SETTING); /* Extract the secrets from the list of matching keyring items */ for (iter = list; iter != NULL; iter = g_list_next (iter)) { SecretItem *item = iter->data; SecretValue *secret; const char *key_name; GHashTable *attributes; secret = secret_item_get_secret (item); if (secret) { attributes = secret_item_get_attributes (item); key_name = g_hash_table_lookup (attributes, KEYRING_SK_TAG); if (!key_name) { g_hash_table_unref (attributes); secret_value_unref (secret); continue; } g_variant_builder_add (&builder_setting, "{sv}", key_name, g_variant_new_string (secret_value_get (secret, NULL))); /* See if this property matches a given hint */ if (r->hints && r->hints[0]) { if (!g_strcmp0 (r->hints[0], key_name) || !g_strcmp0 (r->hints[1], key_name)) hint_found = TRUE; } g_hash_table_unref (attributes); secret_value_unref (secret); break; } } /* If there were hints, and none of the hints were returned by the keyring, * get some new secrets. */ if (r->flags) { if (r->hints && r->hints[0] && !hint_found) ask = TRUE; else if (r->flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_REQUEST_NEW) { g_message ("New secrets for %s/%s requested; ask the user", connection_id, r->setting_name); ask = TRUE; } else if ( (r->flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ALLOW_INTERACTION) && is_connection_always_ask (r->connection)) ask = TRUE; } /* Returned secrets are a{sa{sv}}; this is the outer a{s...} hash that * will contain all the individual settings hashes. */ g_variant_builder_init (&builder_connection, NM_VARIANT_TYPE_CONNECTION); g_variant_builder_add (&builder_connection, "{sa{sv}}", r->setting_name, &builder_setting); settings = g_variant_builder_end (&builder_connection); done: g_list_free_full (list, g_object_unref); if (ask) { GVariantIter dict_iter; const char *setting_name; GVariant *setting_dict; /* Stuff all the found secrets into the connection for the UI to use */ g_variant_iter_init (&dict_iter, settings); while (g_variant_iter_next (&dict_iter, "{s@a{sv}}", &setting_name, &setting_dict)) { nm_connection_update_secrets (r->connection, setting_name, setting_dict, NULL); g_variant_unref (setting_dict); } ask_for_secrets (r); } else { /* Otherwise send the secrets back to NetworkManager */ r->get_callback (NM_SECRET_AGENT_OLD (r->agent), r->connection, error ? NULL : settings, error, r->callback_data); request_free (r); } if (settings) g_variant_unref (settings); g_clear_error (&error); }