diff options
| author | Michael Biebl <biebl@debian.org> | 2015-01-22 00:29:39 +0100 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2015-01-22 00:29:39 +0100 |
| commit | 2c032d8f1c6292c1338a615e6ec40252889ba85c (patch) | |
| tree | 1f77182220b2b0264288ba4a476ab47e5bc48716 /libnm/nm-secret-agent-old.c | |
| parent | 33491bc4279481db8ae47213e34a6d695a0e8830 (diff) | |
Imported Upstream version 1.0.0 upstream/1.0.0
Diffstat (limited to 'libnm/nm-secret-agent-old.c')
| -rw-r--r-- | libnm/nm-secret-agent-old.c | 1366 |
1 files changed, 1366 insertions, 0 deletions
diff --git a/libnm/nm-secret-agent-old.c b/libnm/nm-secret-agent-old.c new file mode 100644 index 00000000..606c4099 --- /dev/null +++ b/libnm/nm-secret-agent-old.c @@ -0,0 +1,1366 @@ +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ +/* + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301 USA. + * + * Copyright 2010 - 2011 Red Hat, Inc. + */ + +#include "config.h" + +#include <string.h> + +#include "nm-glib-compat.h" +#include "nm-dbus-interface.h" +#include "nm-secret-agent-old.h" +#include "nm-enum-types.h" +#include "nm-dbus-helpers.h" +#include "nm-simple-connection.h" + +#include "nmdbus-secret-agent.h" +#include "nmdbus-agent-manager.h" + +static void nm_secret_agent_old_initable_iface_init (GInitableIface *iface); +static void nm_secret_agent_old_async_initable_iface_init (GAsyncInitableIface *iface); +G_DEFINE_ABSTRACT_TYPE_WITH_CODE (NMSecretAgentOld, nm_secret_agent_old, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, nm_secret_agent_old_initable_iface_init); + G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, nm_secret_agent_old_async_initable_iface_init); + ) + +#define NM_SECRET_AGENT_OLD_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_SECRET_AGENT_OLD, NMSecretAgentOldPrivate)) + +typedef struct { + gboolean registered; + gboolean registering; + NMSecretAgentCapabilities capabilities; + + GDBusConnection *bus; + gboolean private_bus; + gboolean session_bus; + NMDBusAgentManager *manager_proxy; + NMDBusSecretAgent *dbus_secret_agent; + + /* GetSecretsInfo structs of in-flight GetSecrets requests */ + GSList *pending_gets; + + char *identifier; + gboolean auto_register; + gboolean suppress_auto; +} NMSecretAgentOldPrivate; + +enum { + PROP_0, + PROP_IDENTIFIER, + PROP_AUTO_REGISTER, + PROP_REGISTERED, + PROP_CAPABILITIES, + + LAST_PROP +}; + +/*************************************************************/ + +static void +_internal_unregister (NMSecretAgentOld *self) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self); + + if (priv->registered) { + g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (priv->dbus_secret_agent)); + priv->registered = FALSE; + priv->registering = FALSE; + g_object_notify (G_OBJECT (self), NM_SECRET_AGENT_OLD_REGISTERED); + } +} + +typedef struct { + char *path; + char *setting_name; + GDBusMethodInvocation *context; +} GetSecretsInfo; + +static void +get_secrets_info_finalize (NMSecretAgentOld *self, GetSecretsInfo *info) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self); + + g_return_if_fail (info != NULL); + + priv->pending_gets = g_slist_remove (priv->pending_gets, info); + + g_free (info->path); + g_free (info->setting_name); + memset (info, 0, sizeof (*info)); + g_free (info); +} + +static inline gboolean +should_auto_register (NMSecretAgentOld *self) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self); + + return ( priv->auto_register + && !priv->suppress_auto + && !priv->registered + && !priv->registering); +} + +static void +name_owner_changed (GObject *proxy, + GParamSpec *pspec, + gpointer user_data) +{ + NMSecretAgentOld *self = NM_SECRET_AGENT_OLD (user_data); + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self); + GSList *iter; + char *owner; + + owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (proxy)); + if (owner != NULL) { + if (should_auto_register (self)) + nm_secret_agent_old_register_async (self, NULL, NULL, NULL); + g_free (owner); + } else { + /* Cancel any pending secrets requests */ + for (iter = priv->pending_gets; iter; iter = g_slist_next (iter)) { + GetSecretsInfo *info = iter->data; + + NM_SECRET_AGENT_OLD_GET_CLASS (self)->cancel_get_secrets (self, + info->path, + info->setting_name); + } + g_slist_free (priv->pending_gets); + priv->pending_gets = NULL; + + _internal_unregister (self); + } +} + +static gboolean +verify_sender (NMSecretAgentOld *self, + GDBusMethodInvocation *context, + GError **error) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self); + char *nm_owner; + const char *sender; + guint32 sender_uid; + GVariant *ret; + GError *local = NULL; + + g_return_val_if_fail (context != NULL, FALSE); + + /* Private bus connection is always to NetworkManager, which is always + * UID 0. + */ + if (priv->private_bus) + return TRUE; + + /* Verify that the sender is the same as NetworkManager's bus name owner. */ + + nm_owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (priv->manager_proxy)); + if (!nm_owner) { + g_set_error_literal (error, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_PERMISSION_DENIED, + "NetworkManager bus name owner unknown."); + return FALSE; + } + + sender = g_dbus_method_invocation_get_sender (context); + if (!sender) { + g_set_error_literal (error, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_PERMISSION_DENIED, + "Failed to get request sender."); + g_free (nm_owner); + return FALSE; + } + + /* Check that the sender matches the current NM bus name owner */ + if (strcmp (sender, nm_owner) != 0) { + g_set_error_literal (error, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_PERMISSION_DENIED, + "Request sender does not match NetworkManager bus name owner."); + g_free (nm_owner); + return FALSE; + } + g_free (nm_owner); + + /* If we're connected to the session bus, then this must be a test program, + * so skip the UID check. + */ + if (priv->session_bus) + return TRUE; + + /* Check the UID of the sender */ + ret = g_dbus_connection_call_sync (priv->bus, + DBUS_SERVICE_DBUS, + DBUS_PATH_DBUS, + DBUS_INTERFACE_DBUS, + "GetConnectionUnixUser", + g_variant_new ("(s)", sender), + G_VARIANT_TYPE ("(u)"), + G_DBUS_CALL_FLAGS_NONE, -1, + NULL, &local); + if (!ret) { + char *remote_error = g_dbus_error_get_remote_error (local); + + g_dbus_error_strip_remote_error (local); + g_set_error (error, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_PERMISSION_DENIED, + "Failed to request unix user: (%s) %s.", + remote_error ? remote_error : "", + local->message); + g_free (remote_error); + g_error_free (local); + return FALSE; + } + g_variant_get (ret, "(u)", &sender_uid); + g_variant_unref (ret); + + /* We only accept requests from NM, which always runs as root */ + if (0 != sender_uid) { + g_set_error_literal (error, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_PERMISSION_DENIED, + "Request sender is not root."); + return FALSE; + } + + return TRUE; +} + +static gboolean +verify_request (NMSecretAgentOld *self, + GDBusMethodInvocation *context, + GVariant *connection_dict, + const char *connection_path, + NMConnection **out_connection, + GError **error) +{ + NMConnection *connection = NULL; + GError *local = NULL; + + if (!verify_sender (self, context, error)) + return FALSE; + + /* No connection? If the sender verified, then we allow the request */ + if (connection_dict == NULL) + return TRUE; + + /* If we have a connection dictionary, we require a path too */ + if (connection_path == NULL) { + g_set_error_literal (error, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_INVALID_CONNECTION, + "Invalid connection: no connection path given."); + return FALSE; + } + + /* Make sure the given connection is valid */ + g_assert (out_connection); + connection = nm_simple_connection_new_from_dbus (connection_dict, &local); + if (connection) { + nm_connection_set_path (connection, connection_path); + *out_connection = connection; + } else { + g_set_error (error, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_INVALID_CONNECTION, + "Invalid connection: %s", local->message); + g_clear_error (&local); + } + + return !!connection; +} + +static void +get_secrets_cb (NMSecretAgentOld *self, + NMConnection *connection, + GVariant *secrets, + GError *error, + gpointer user_data) +{ + GetSecretsInfo *info = user_data; + + if (error) + g_dbus_method_invocation_return_gerror (info->context, error); + else { + g_dbus_method_invocation_return_value (info->context, + g_variant_new ("(@a{sa{sv}})", secrets)); + } + + /* Remove the request from internal tracking */ + get_secrets_info_finalize (self, info); +} + +static void +impl_secret_agent_old_get_secrets (NMSecretAgentOld *self, + GDBusMethodInvocation *context, + GVariant *connection_dict, + const char *connection_path, + const char *setting_name, + const char * const *hints, + guint flags, + gpointer user_data) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self); + GError *error = NULL; + NMConnection *connection = NULL; + GetSecretsInfo *info; + + /* Make sure the request comes from NetworkManager and is valid */ + if (!verify_request (self, context, connection_dict, connection_path, &connection, &error)) { + g_dbus_method_invocation_take_error (context, error); + return; + } + + info = g_malloc0 (sizeof (GetSecretsInfo)); + info->path = g_strdup (connection_path); + info->setting_name = g_strdup (setting_name); + info->context = context; + priv->pending_gets = g_slist_append (priv->pending_gets, info); + + NM_SECRET_AGENT_OLD_GET_CLASS (self)->get_secrets (self, + connection, + connection_path, + setting_name, + (const char **) hints, + flags, + get_secrets_cb, + info); + g_object_unref (connection); +} + +static GetSecretsInfo * +find_get_secrets_info (GSList *list, const char *path, const char *setting_name) +{ + GSList *iter; + + for (iter = list; iter; iter = g_slist_next (iter)) { + GetSecretsInfo *candidate = iter->data; + + if ( g_strcmp0 (path, candidate->path) == 0 + && g_strcmp0 (setting_name, candidate->setting_name) == 0) + return candidate; + } + return NULL; +} + +static void +impl_secret_agent_old_cancel_get_secrets (NMSecretAgentOld *self, + GDBusMethodInvocation *context, + const char *connection_path, + const char *setting_name, + gpointer user_data) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self); + GError *error = NULL; + GetSecretsInfo *info; + + /* Make sure the request comes from NetworkManager and is valid */ + if (!verify_request (self, context, NULL, NULL, NULL, &error)) { + g_dbus_method_invocation_take_error (context, error); + return; + } + + info = find_get_secrets_info (priv->pending_gets, connection_path, setting_name); + if (!info) { + g_dbus_method_invocation_return_error (context, + NM_SECRET_AGENT_ERROR, + NM_SECRET_AGENT_ERROR_FAILED, + "No secrets request in progress for this connection."); + return; + } + + /* Send the cancel request up to the subclass and finalize it */ + NM_SECRET_AGENT_OLD_GET_CLASS (self)->cancel_get_secrets (self, + info->path, + info->setting_name); + g_dbus_method_invocation_return_value (context, NULL); +} + +static void +save_secrets_cb (NMSecretAgentOld *self, + NMConnection *connection, + GError *error, + gpointer user_data) +{ + GDBusMethodInvocation *context = user_data; + + if (error) + g_dbus_method_invocation_return_gerror (context, error); + else + g_dbus_method_invocation_return_value (context, NULL); +} + +static void +impl_secret_agent_old_save_secrets (NMSecretAgentOld *self, + GDBusMethodInvocation *context, + GVariant *connection_dict, + const char *connection_path, + gpointer user_data) +{ + GError *error = NULL; + NMConnection *connection = NULL; + + /* Make sure the request comes from NetworkManager and is valid */ + if (!verify_request (self, context, connection_dict, connection_path, &connection, &error)) { + g_dbus_method_invocation_take_error (context, error); + return; + } + + NM_SECRET_AGENT_OLD_GET_CLASS (self)->save_secrets (self, + connection, + connection_path, + save_secrets_cb, + context); + g_object_unref (connection); +} + +static void +delete_secrets_cb (NMSecretAgentOld *self, + NMConnection *connection, + GError *error, + gpointer user_data) +{ + GDBusMethodInvocation *context = user_data; + + if (error) + g_dbus_method_invocation_return_gerror (context, error); + else + g_dbus_method_invocation_return_value (context, NULL); +} + +static void +impl_secret_agent_old_delete_secrets (NMSecretAgentOld *self, + GDBusMethodInvocation *context, + GVariant *connection_dict, + const char *connection_path, + gpointer user_data) +{ + GError *error = NULL; + NMConnection *connection = NULL; + + /* Make sure the request comes from NetworkManager and is valid */ + if (!verify_request (self, context, connection_dict, connection_path, &connection, &error)) { + g_dbus_method_invocation_take_error (context, error); + return; + } + + NM_SECRET_AGENT_OLD_GET_CLASS (self)->delete_secrets (self, + connection, + connection_path, + delete_secrets_cb, + context); + g_object_unref (connection); +} + +/**************************************************************/ + +static gboolean +check_nm_running (NMSecretAgentOld *self, GError **error) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self); + char *owner; + + if (priv->private_bus) + return TRUE; + owner = g_dbus_proxy_get_name_owner (G_DBUS_PROXY (priv->manager_proxy)); + if (owner) { + g_free (owner); + return TRUE; + } + + g_set_error (error, NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_FAILED, + "NetworkManager is not running"); + return FALSE; +} + +/**************************************************************/ + +/** + * nm_secret_agent_old_register: + * @self: a #NMSecretAgentOld + * @cancellable: a #GCancellable, or %NULL + * @error: return location for a #GError, or %NULL + * + * Registers the #NMSecretAgentOld with the NetworkManager secret manager, + * indicating to NetworkManager that the agent is able to provide and save + * secrets for connections on behalf of its user. + * + * It is a programmer error to attempt to register an agent that is already + * registered, or in the process of registering. + * + * Returns: %TRUE if registration was successful, %FALSE on error. + **/ +gboolean +nm_secret_agent_old_register (NMSecretAgentOld *self, + GCancellable *cancellable, + GError **error) +{ + NMSecretAgentOldPrivate *priv; + NMSecretAgentOldClass *class; + + g_return_val_if_fail (NM_IS_SECRET_AGENT_OLD (self), FALSE); + + priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self); + + g_return_val_if_fail (priv->registered == FALSE, FALSE); + g_return_val_if_fail (priv->registering == FALSE, FALSE); + g_return_val_if_fail (priv->bus != NULL, FALSE); + g_return_val_if_fail (priv->manager_proxy != NULL, FALSE); + + /* Also make sure the subclass can actually respond to secrets requests */ + class = NM_SECRET_AGENT_OLD_GET_CLASS (self); + g_return_val_if_fail (class->get_secrets != NULL, FALSE); + g_return_val_if_fail (class->save_secrets != NULL, FALSE); + g_return_val_if_fail (class->delete_secrets != NULL, FALSE); + + if (!check_nm_running (self, error)) + return FALSE; + + priv->suppress_auto = FALSE; + + /* Export our secret agent interface before registering with the manager */ + if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (priv->dbus_secret_agent), + priv->bus, + NM_DBUS_PATH_SECRET_AGENT, + error)) + return FALSE; + + priv->registering = TRUE; + if (nmdbus_agent_manager_call_register_with_capabilities_sync (priv->manager_proxy, + priv->identifier, + priv->capabilities, + cancellable, NULL)) + goto success; + + /* Might be an old NetworkManager that doesn't support capabilities; + * fall back to old Register() method instead. + */ + if (nmdbus_agent_manager_call_register_sync (priv->manager_proxy, + priv->identifier, + cancellable, NULL)) + goto success; + + /* Failure */ + priv->registering = FALSE; + _internal_unregister (self); + return FALSE; + +success: + priv->registering = FALSE; + priv->registered = TRUE; + g_object_notify (G_OBJECT (self), NM_SECRET_AGENT_OLD_REGISTERED); + return TRUE; +} + +static void +reg_result (NMSecretAgentOld *self, GSimpleAsyncResult *simple, GError *error) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self); + + priv->registering = FALSE; + + if (error) { + g_simple_async_result_take_error (simple, error); + g_simple_async_result_complete (simple); + + /* If registration failed we shouldn't expose ourselves on the bus */ + _internal_unregister (self); + } else { + priv->registered = TRUE; + g_object_notify (G_OBJECT (self), NM_SECRET_AGENT_OLD_REGISTERED); + + g_simple_async_result_set_op_res_gboolean (simple, TRUE); + g_simple_async_result_complete (simple); + } + + g_object_unref (simple); +} + +static void +reg_request_cb (GObject *proxy, + GAsyncResult *result, + gpointer user_data) +{ + GSimpleAsyncResult *simple = user_data; + NMSecretAgentOld *self; + NMSecretAgentOldPrivate *priv; + GError *error = NULL; + + self = NM_SECRET_AGENT_OLD (g_async_result_get_source_object (G_ASYNC_RESULT (simple))); + g_object_unref (self); /* drop extra ref added by get_source_object() */ + priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self); + + if (!nmdbus_agent_manager_call_register_finish (NMDBUS_AGENT_MANAGER (proxy), result, &error)) + g_dbus_error_strip_remote_error (error); + reg_result (self, simple, error); + g_clear_error (&error); +} + +static void +reg_with_caps_cb (GObject *proxy, + GAsyncResult *result, + gpointer user_data) +{ + GSimpleAsyncResult *simple = user_data; + NMSecretAgentOld *self; + NMSecretAgentOldPrivate *priv; + + self = NM_SECRET_AGENT_OLD (g_async_result_get_source_object (G_ASYNC_RESULT (simple))); + g_object_unref (self); /* drop extra ref added by get_source_object() */ + priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self); + + if (nmdbus_agent_manager_call_register_with_capabilities_finish (NMDBUS_AGENT_MANAGER (proxy), result, NULL)) { + reg_result (self, simple, NULL); + return; + } + + /* Might be an old NetworkManager that doesn't support capabilities; + * fall back to old Register() method instead. + */ + nmdbus_agent_manager_call_register (priv->manager_proxy, + priv->identifier, + NULL, reg_request_cb, simple); +} + +/** + * nm_secret_agent_old_register_async: + * @self: a #NMSecretAgentOld + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to call when the agent is registered + * @user_data: data for @callback + * + * Asynchronously registers the #NMSecretAgentOld with the NetworkManager secret + * manager, indicating to NetworkManager that the agent is able to provide and + * save secrets for connections on behalf of its user. + * + * It is a programmer error to attempt to register an agent that is already + * registered, or in the process of registering. + **/ +void +nm_secret_agent_old_register_async (NMSecretAgentOld *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + NMSecretAgentOldPrivate *priv; + NMSecretAgentOldClass *class; + GSimpleAsyncResult *simple; + GError *error = NULL; + + g_return_if_fail (NM_IS_SECRET_AGENT_OLD (self)); + + priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self); + + g_return_if_fail (priv->registered == FALSE); + g_return_if_fail (priv->registering == FALSE); + g_return_if_fail (priv->bus != NULL); + g_return_if_fail (priv->manager_proxy != NULL); + + /* Also make sure the subclass can actually respond to secrets requests */ + class = NM_SECRET_AGENT_OLD_GET_CLASS (self); + g_return_if_fail (class->get_secrets != NULL); + g_return_if_fail (class->save_secrets != NULL); + g_return_if_fail (class->delete_secrets != NULL); + + simple = g_simple_async_result_new (G_OBJECT (self), callback, user_data, + nm_secret_agent_old_register_async); + + if (!check_nm_running (self, &error)) { + g_simple_async_result_take_error (simple, error); + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); + return; + } + + /* Export our secret agent interface before registering with the manager */ + if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (priv->dbus_secret_agent), + priv->bus, + NM_DBUS_PATH_SECRET_AGENT, + &error)) { + g_simple_async_result_take_error (simple, error); + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); + return; + } + + priv->suppress_auto = FALSE; + priv->registering = TRUE; + + nmdbus_agent_manager_call_register_with_capabilities (priv->manager_proxy, + priv->identifier, + priv->capabilities, + NULL, + reg_with_caps_cb, simple); +} + +/** + * nm_secret_agent_old_register_finish: + * @self: a #NMSecretAgentOld + * @result: the result passed to the #GAsyncReadyCallback + * @error: return location for a #GError, or %NULL + * + * Gets the result of a call to nm_secret_agent_old_register_async(). + * + * Returns: %TRUE if registration was successful, %FALSE on error. + **/ +gboolean +nm_secret_agent_old_register_finish (NMSecretAgentOld *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), nm_secret_agent_old_register_async), FALSE); + + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) + return FALSE; + else + return TRUE; +} + +/** + * nm_secret_agent_old_unregister: + * @self: a #NMSecretAgentOld + * @cancellable: a #GCancellable, or %NULL + * @error: return location for a #GError, or %NULL + * + * Unregisters the #NMSecretAgentOld with the NetworkManager secret manager, + * indicating to NetworkManager that the agent will no longer provide or + * store secrets on behalf of this user. + * + * It is a programmer error to attempt to unregister an agent that is not + * registered. + * + * Returns: %TRUE if unregistration was successful, %FALSE on error + **/ +gboolean +nm_secret_agent_old_unregister (NMSecretAgentOld *self, + GCancellable *cancellable, + GError **error) +{ + NMSecretAgentOldPrivate *priv; + gboolean success; + + g_return_val_if_fail (NM_IS_SECRET_AGENT_OLD (self), FALSE); + + priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self); + + g_return_val_if_fail (priv->registered == TRUE, FALSE); + g_return_val_if_fail (priv->bus != NULL, FALSE); + g_return_val_if_fail (priv->manager_proxy != NULL, FALSE); + + priv->suppress_auto = TRUE; + + success = nmdbus_agent_manager_call_unregister_sync (priv->manager_proxy, cancellable, error); + if (error && *error) + g_dbus_error_strip_remote_error (*error); + _internal_unregister (self); + + return success; +} + +static void +unregister_cb (GObject *proxy, GAsyncResult *result, gpointer user_data) +{ + GSimpleAsyncResult *simple = user_data; + NMSecretAgentOld *self; + GError *error = NULL; + + self = NM_SECRET_AGENT_OLD (g_async_result_get_source_object (G_ASYNC_RESULT (simple))); + g_object_unref (self); /* drop extra ref added by get_source_object() */ + + _internal_unregister (self); + + if (nmdbus_agent_manager_call_unregister_finish (NMDBUS_AGENT_MANAGER (proxy), + result, &error)) + g_simple_async_result_set_op_res_gboolean (simple, TRUE); + else { + g_dbus_error_strip_remote_error (error); + g_simple_async_result_take_error (simple, error); + } + + g_simple_async_result_complete (simple); + g_object_unref (simple); +} + +/** + * nm_secret_agent_old_unregister_async: + * @self: a #NMSecretAgentOld + * @cancellable: a #GCancellable, or %NULL + * @callback: callback to call when the agent is unregistered + * @user_data: data for @callback + * + * Asynchronously unregisters the #NMSecretAgentOld with the NetworkManager secret + * manager, indicating to NetworkManager that the agent will no longer provide + * or store secrets on behalf of this user. + * + * It is a programmer error to attempt to unregister an agent that is not + * registered. + **/ +void +nm_secret_agent_old_unregister_async (NMSecretAgentOld *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + NMSecretAgentOldPrivate *priv; + GSimpleAsyncResult *simple; + GError *error = NULL; + + g_return_if_fail (NM_IS_SECRET_AGENT_OLD (self)); + + priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self); + + g_return_if_fail (priv->registered == TRUE); + g_return_if_fail (priv->bus != NULL); + g_return_if_fail (priv->manager_proxy != NULL); + + simple = g_simple_async_result_new (G_OBJECT (self), callback, user_data, + nm_secret_agent_old_unregister_async); + + if (!check_nm_running (self, &error)) { + g_simple_async_result_take_error (simple, error); + g_simple_async_result_complete_in_idle (simple); + g_object_unref (simple); + return; + } + + priv->suppress_auto = TRUE; + + nmdbus_agent_manager_call_unregister (priv->manager_proxy, cancellable, + unregister_cb, simple); +} + +/** + * nm_secret_agent_old_unregister_finish: + * @self: a #NMSecretAgentOld + * @result: the result passed to the #GAsyncReadyCallback + * @error: return location for a #GError, or %NULL + * + * Gets the result of a call to nm_secret_agent_old_unregister_async(). + * + * Returns: %TRUE if unregistration was successful, %FALSE on error. + **/ +gboolean +nm_secret_agent_old_unregister_finish (NMSecretAgentOld *self, + GAsyncResult *result, + GError **error) +{ + g_return_val_if_fail (g_simple_async_result_is_valid (result, G_OBJECT (self), nm_secret_agent_old_unregister_async), FALSE); + + if (g_simple_async_result_propagate_error (G_SIMPLE_ASYNC_RESULT (result), error)) + return FALSE; + else + return TRUE; +} + +/** + * nm_secret_agent_old_get_registered: + * @self: a #NMSecretAgentOld + * + * Returns: a %TRUE if the agent is registered, %FALSE if it is not. + **/ +gboolean +nm_secret_agent_old_get_registered (NMSecretAgentOld *self) +{ + g_return_val_if_fail (NM_IS_SECRET_AGENT_OLD (self), FALSE); + + return NM_SECRET_AGENT_OLD_GET_PRIVATE (self)->registered; +} + +/**************************************************************/ + +/** + * nm_secret_agent_old_get_secrets: + * @self: a #NMSecretAgentOld + * @connection: the #NMConnection for which we're asked secrets + * @setting_name: the name of the secret setting + * @hints: (array zero-terminated=1): hints to the agent + * @flags: flags that modify the behavior of the request + * @callback: (scope async): a callback, to be invoked when the operation is done + * @user_data: (closure): caller-specific data to be passed to @callback + * + * Asynchronously retrieves secrets belonging to @connection for the + * setting @setting_name. @flags indicate specific behavior that the secret + * agent should use when performing the request, for example returning only + * existing secrets without user interaction, or requesting entirely new + * secrets from the user. + * + * Virtual: get_secrets + */ +void +nm_secret_agent_old_get_secrets (NMSecretAgentOld *self, + NMConnection *connection, + const char *setting_name, + const char **hints, + NMSecretAgentGetSecretsFlags flags, + NMSecretAgentOldGetSecretsFunc callback, + gpointer user_data) +{ + g_return_if_fail (NM_IS_SECRET_AGENT_OLD (self)); + g_return_if_fail (NM_IS_CONNECTION (connection)); + g_return_if_fail (nm_connection_get_path (connection)); + g_return_if_fail (setting_name != NULL); + g_return_if_fail (strlen (setting_name) > 0); + g_return_if_fail (!(flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ONLY_SYSTEM)); + g_return_if_fail (!(flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_NO_ERRORS)); + g_return_if_fail (callback != NULL); + + NM_SECRET_AGENT_OLD_GET_CLASS (self)->get_secrets (self, + connection, + nm_connection_get_path (connection), + setting_name, + hints, + flags, + callback, + user_data); +} + +/** + * nm_secret_agent_old_save_secrets: + * @self: a #NMSecretAgentOld + * @connection: a #NMConnection + * @callback: (scope async): a callback, to be invoked when the operation is done + * @user_data: (closure): caller-specific data to be passed to @callback + * + * Asynchronously ensures that all secrets inside @connection are stored to + * disk. + * + * Virtual: save_secrets + */ +void +nm_secret_agent_old_save_secrets (NMSecretAgentOld *self, + NMConnection *connection, + NMSecretAgentOldSaveSecretsFunc callback, + gpointer user_data) +{ + g_return_if_fail (NM_IS_SECRET_AGENT_OLD (self)); + g_return_if_fail (NM_IS_CONNECTION (connection)); + g_return_if_fail (nm_connection_get_path (connection)); + + NM_SECRET_AGENT_OLD_GET_CLASS (self)->save_secrets (self, + connection, + nm_connection_get_path (connection), + callback, + user_data); +} + +/** + * nm_secret_agent_old_delete_secrets: + * @self: a #NMSecretAgentOld + * @connection: a #NMConnection + * @callback: (scope async): a callback, to be invoked when the operation is done + * @user_data: (closure): caller-specific data to be passed to @callback + * + * Asynchronously asks the agent to delete all saved secrets belonging to + * @connection. + * + * Virtual: delete_secrets + */ +void +nm_secret_agent_old_delete_secrets (NMSecretAgentOld *self, + NMConnection *connection, + NMSecretAgentOldDeleteSecretsFunc callback, + gpointer user_data) +{ + g_return_if_fail (NM_IS_SECRET_AGENT_OLD (self)); + g_return_if_fail (NM_IS_CONNECTION (connection)); + g_return_if_fail (nm_connection_get_path (connection)); + + NM_SECRET_AGENT_OLD_GET_CLASS (self)->delete_secrets (self, + connection, + nm_connection_get_path (connection), + callback, + user_data); +} + +/**************************************************************/ + +static gboolean +validate_identifier (const char *identifier) +{ + const char *p = identifier; + size_t id_len; + + /* Length between 3 and 255 characters inclusive */ + id_len = strlen (identifier); + if (id_len < 3 || id_len > 255) + return FALSE; + + if ((identifier[0] == '.') || (identifier[id_len - 1] == '.')) + return FALSE; + + /* FIXME: do complete validation here */ + while (p && *p) { + if (!g_ascii_isalnum (*p) && (*p != '_') && (*p != '-') && (*p != '.')) + return FALSE; + if ((*p == '.') && (*(p + 1) == '.')) + return FALSE; + p++; + } + + return TRUE; +} + +static void +nm_secret_agent_old_init (NMSecretAgentOld *self) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self); + + priv->dbus_secret_agent = nmdbus_secret_agent_skeleton_new (); + _nm_dbus_bind_properties (self, priv->dbus_secret_agent); + _nm_dbus_bind_methods (self, priv->dbus_secret_agent, + "GetSecrets", impl_secret_agent_old_get_secrets, + "CancelGetSecrets", impl_secret_agent_old_cancel_get_secrets, + "DeleteSecrets", impl_secret_agent_old_delete_secrets, + "SaveSecrets", impl_secret_agent_old_save_secrets, + NULL); +} + +static void +init_common (NMSecretAgentOld *self) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self); + + priv->private_bus = _nm_dbus_is_connection_private (priv->bus); + + if (priv->private_bus == FALSE) { + priv->session_bus = _nm_dbus_bus_type () == G_BUS_TYPE_SESSION; + + g_signal_connect (priv->manager_proxy, "notify::g-name-owner", + G_CALLBACK (name_owner_changed), self); + } +} + +static gboolean +init_sync (GInitable *initable, GCancellable *cancellable, GError **error) +{ + NMSecretAgentOld *self = NM_SECRET_AGENT_OLD (initable); + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self); + GDBusProxy *proxy; + + priv->bus = _nm_dbus_new_connection (cancellable, error); + if (!priv->bus) + return FALSE; + + proxy = _nm_dbus_new_proxy_for_connection (priv->bus, + NM_DBUS_PATH_AGENT_MANAGER, + NM_DBUS_INTERFACE_AGENT_MANAGER, + cancellable, error); + if (!proxy) + return FALSE; + priv->manager_proxy = NMDBUS_AGENT_MANAGER (proxy); + + init_common (self); + + if (priv->auto_register) + return nm_secret_agent_old_register (self, cancellable, error); + else + return TRUE; +} + +typedef struct { + NMSecretAgentOld *self; + GCancellable *cancellable; + GSimpleAsyncResult *simple; +} NMSecretAgentOldInitData; + +static void +init_async_complete (NMSecretAgentOldInitData *init_data, GError *error) +{ + if (!error) + g_simple_async_result_set_op_res_gboolean (init_data->simple, TRUE); + else + g_simple_async_result_take_error (init_data->simple, error); + + g_simple_async_result_complete_in_idle (init_data->simple); + + g_object_unref (init_data->simple); + g_clear_object (&init_data->cancellable); + g_slice_free (NMSecretAgentOldInitData, init_data); +} + +static void +init_async_registered (GObject *object, GAsyncResult *result, gpointer user_data) +{ + NMSecretAgentOld *self = NM_SECRET_AGENT_OLD (object); + NMSecretAgentOldInitData *init_data = user_data; + GError *error = NULL; + + nm_secret_agent_old_register_finish (self, result, &error); + init_async_complete (init_data, error); +} + +static void +init_async_got_proxy (GObject *object, GAsyncResult *result, gpointer user_data) +{ + NMSecretAgentOldInitData *init_data = user_data; + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (init_data->self); + GDBusProxy *proxy; + GError *error = NULL; + + proxy = _nm_dbus_new_proxy_for_connection_finish (result, &error); + if (!proxy) { + init_async_complete (init_data, error); + return; + } + priv->manager_proxy = NMDBUS_AGENT_MANAGER (proxy); + + init_common (init_data->self); + + if (priv->auto_register) { + nm_secret_agent_old_register_async (init_data->self, init_data->cancellable, + init_async_registered, init_data); + } else + init_async_complete (init_data, NULL); +} + +static void +init_async_got_bus (GObject *initable, GAsyncResult *result, gpointer user_data) +{ + NMSecretAgentOldInitData *init_data = user_data; + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (init_data->self); + GError *error = NULL; + + priv->bus = _nm_dbus_new_connection_finish (result, &error); + if (!priv->bus) { + init_async_complete (init_data, error); + return; + } + + _nm_dbus_new_proxy_for_connection_async (priv->bus, + NM_DBUS_PATH_AGENT_MANAGER, + NM_DBUS_INTERFACE_AGENT_MANAGER, + init_data->cancellable, + init_async_got_proxy, init_data); +} + +static void +init_async (GAsyncInitable *initable, int io_priority, + GCancellable *cancellable, GAsyncReadyCallback callback, + gpointer user_data) +{ + NMSecretAgentOld *self = NM_SECRET_AGENT_OLD (initable); + NMSecretAgentOldInitData *init_data; + + init_data = g_slice_new (NMSecretAgentOldInitData); + init_data->self = self; + init_data->cancellable = cancellable ? g_object_ref (cancellable) : NULL; + + init_data->simple = g_simple_async_result_new (G_OBJECT (initable), callback, + user_data, init_async); + + _nm_dbus_new_connection_async (cancellable, init_async_got_bus, init_data); +} + +static gboolean +init_finish (GAsyncInitable *initable, GAsyncResult *result, GError **error) +{ + GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result); + + if (g_simple_async_result_propagate_error (simple, error)) + return FALSE; + else + return TRUE; +} + +static void +get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (object); + + switch (prop_id) { + case PROP_IDENTIFIER: + g_value_set_string (value, priv->identifier); + break; + case PROP_AUTO_REGISTER: + g_value_set_boolean (value, priv->auto_register); + break; + case PROP_REGISTERED: + g_value_set_boolean (value, priv->registered); + break; + case PROP_CAPABILITIES: + g_value_set_flags (value, priv->capabilities); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (object); + const char *identifier; + + switch (prop_id) { + case PROP_IDENTIFIER: + identifier = g_value_get_string (value); + + g_return_if_fail (validate_identifier (identifier)); + + g_free (priv->identifier); + priv->identifier = g_strdup (identifier); + break; + case PROP_AUTO_REGISTER: + priv->auto_register = g_value_get_boolean (value); + break; + case PROP_CAPABILITIES: + priv->capabilities = g_value_get_flags (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +dispose (GObject *object) +{ + NMSecretAgentOld *self = NM_SECRET_AGENT_OLD (object); + NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE (self); + + if (priv->registered) + nm_secret_agent_old_unregister_async (self, NULL, NULL, NULL); + + g_clear_pointer (&priv->identifier, g_free); + + while (priv->pending_gets) + get_secrets_info_finalize (self, priv->pending_gets->data); + + g_signal_handlers_disconnect_matched (priv->dbus_secret_agent, G_SIGNAL_MATCH_DATA, + 0, 0, NULL, NULL, self); + g_object_unref (priv->dbus_secret_agent); + + g_clear_object (&priv->manager_proxy); + g_clear_object (&priv->bus); + + G_OBJECT_CLASS (nm_secret_agent_old_parent_class)->dispose (object); +} + +static void +nm_secret_agent_old_class_init (NMSecretAgentOldClass *class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (class); + + g_type_class_add_private (class, sizeof (NMSecretAgentOldPrivate)); + + /* Virtual methods */ + object_class->dispose = dispose; + object_class->get_property = get_property; + object_class->set_property = set_property; + + /** + * NMSecretAgentOld:identifier: + * + * Identifies this agent; only one agent in each user session may use the + * same identifier. Identifier formatting follows the same rules as + * D-Bus bus names with the exception that the ':' character is not + * allowed. The valid set of characters is "[A-Z][a-z][0-9]_-." and the + * identifier is limited in length to 255 characters with a minimum + * of 3 characters. An example valid identifier is 'org.gnome.nm-applet' + * (without quotes). + **/ + g_object_class_install_property + (object_class, PROP_IDENTIFIER, + g_param_spec_string (NM_SECRET_AGENT_OLD_IDENTIFIER, "", "", + NULL, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSecretAgentOld:auto-register: + * + * If %TRUE (the default), the agent will always be registered when + * NetworkManager is running; if NetworkManager exits and restarts, the + * agent will re-register itself automatically. + * + * In particular, if this property is %TRUE at construct time, then the + * agent will register itself with NetworkManager during + * construction/initialization, and initialization will fail with an error + * if the agent is unable to register itself. + * + * If the property is %FALSE, the agent will not automatically register with + * NetworkManager, and nm_secret_agent_old_register() or + * nm_secret_agent_old_register_async() must be called to register it. + * + * Calling nm_secret_agent_old_unregister() will suppress auto-registration + * until nm_secret_agent_old_register() is called, which re-enables + * auto-registration. This ensures that the agent remains un-registered when + * you expect it to be unregistered. + **/ + g_object_class_install_property + (object_class, PROP_AUTO_REGISTER, + g_param_spec_boolean (NM_SECRET_AGENT_OLD_AUTO_REGISTER, "", "", + TRUE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSecretAgentOld:registered: + * + * %TRUE if the agent is registered with NetworkManager, %FALSE if not. + **/ + g_object_class_install_property + (object_class, PROP_REGISTERED, + g_param_spec_boolean (NM_SECRET_AGENT_OLD_REGISTERED, "", "", + FALSE, + G_PARAM_READABLE | + G_PARAM_STATIC_STRINGS)); + + /** + * NMSecretAgentOld:capabilities: + * + * A bitfield of %NMSecretAgentCapabilities. + **/ + g_object_class_install_property + (object_class, PROP_CAPABILITIES, + g_param_spec_flags (NM_SECRET_AGENT_OLD_CAPABILITIES, "", "", + NM_TYPE_SECRET_AGENT_CAPABILITIES, + NM_SECRET_AGENT_CAPABILITY_NONE, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT | + G_PARAM_STATIC_STRINGS)); + + _nm_dbus_register_proxy_type (NM_DBUS_INTERFACE_AGENT_MANAGER, + NMDBUS_TYPE_AGENT_MANAGER_PROXY); +} + +static void +nm_secret_agent_old_initable_iface_init (GInitableIface *iface) +{ + iface->init = init_sync; +} + +static void +nm_secret_agent_old_async_initable_iface_init (GAsyncInitableIface *iface) +{ + iface->init_async = init_async; + iface->init_finish = init_finish; +} |