/**
 * 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);
}
Ejemplo n.º 3
0
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);
}
Ejemplo n.º 5
0
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);
	}
}
Ejemplo n.º 6
0
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);
}
Ejemplo n.º 7
0
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);
}