diff options
| author | Michael Biebl <biebl@debian.org> | 2011-04-21 10:42:53 +0200 |
|---|---|---|
| committer | Michael Biebl <biebl@debian.org> | 2011-04-21 10:42:53 +0200 |
| commit | 8baa1aca8dfd35e3190d7d5655eb83b5b901e263 (patch) | |
| tree | a9ac1e67d0c22a48330c76f08fc5291f85307055 /src/settings/nm-agent-manager.c | |
| parent | f75dd6fd1975146623052b843b182dc32c3fbe46 (diff) | |
Imported Upstream version 0.8.4.0 upstream/0.8.4.0
Diffstat (limited to 'src/settings/nm-agent-manager.c')
| -rw-r--r-- | src/settings/nm-agent-manager.c | 1390 |
1 files changed, 0 insertions, 1390 deletions
diff --git a/src/settings/nm-agent-manager.c b/src/settings/nm-agent-manager.c deleted file mode 100644 index dbc29cae..00000000 --- a/src/settings/nm-agent-manager.c +++ /dev/null @@ -1,1390 +0,0 @@ -/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */ -/* NetworkManager -- Network link manager - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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 General Public License for more details. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Copyright (C) 2010 - 2011 Red Hat, Inc. - */ - -#include <config.h> -#include <string.h> -#include <ctype.h> -#include <pwd.h> - -#include <glib.h> -#include <dbus/dbus-glib.h> -#include <dbus/dbus-glib-lowlevel.h> - -#include "NetworkManager.h" -#include "nm-logging.h" -#include "nm-agent-manager.h" -#include "nm-secret-agent.h" -#include "nm-manager-auth.h" -#include "nm-dbus-glib-types.h" -#include "nm-polkit-helpers.h" -#include "nm-manager-auth.h" -#include "nm-setting-vpn.h" -#include "nm-setting-connection.h" - -G_DEFINE_TYPE (NMAgentManager, nm_agent_manager, G_TYPE_OBJECT) - -#define NM_AGENT_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ - NM_TYPE_AGENT_MANAGER, \ - NMAgentManagerPrivate)) - -typedef struct { - gboolean disposed; - - NMDBusManager *dbus_mgr; - NMSessionMonitor *session_monitor; - PolkitAuthority *authority; - - /* Hashed by owner name, not identifier, since two agents in different - * sessions can use the same identifier. - */ - GHashTable *agents; - - GHashTable *requests; -} NMAgentManagerPrivate; - -typedef struct _Request Request; - -static void request_add_agent (Request *req, - NMSecretAgent *agent, - NMSessionMonitor *session_monitor); - -static void request_remove_agent (Request *req, NMSecretAgent *agent); - -static void impl_agent_manager_register (NMAgentManager *self, - const char *identifier, - DBusGMethodInvocation *context); - -static void impl_agent_manager_unregister (NMAgentManager *self, - DBusGMethodInvocation *context); - -#include "nm-agent-manager-glue.h" - -/********************************************************************/ - -#define NM_AGENT_MANAGER_ERROR (nm_agent_manager_error_quark ()) -#define NM_TYPE_AGENT_MANAGER_ERROR (nm_agent_manager_error_get_type ()) - -typedef enum { - NM_AGENT_MANAGER_ERROR_SENDER_UNKNOWN = 0, - NM_AGENT_MANAGER_ERROR_PERMISSION_DENIED, - NM_AGENT_MANAGER_ERROR_SESSION_NOT_FOUND, - NM_AGENT_MANAGER_ERROR_INVALID_IDENTIFIER, - NM_AGENT_MANAGER_ERROR_NOT_REGISTERED, - NM_AGENT_MANAGER_ERROR_INTERNAL_ERROR, - NM_AGENT_MANAGER_ERROR_NO_SECRETS -} NMAgentManagerError; - -static GQuark -nm_agent_manager_error_quark (void) -{ - static GQuark ret = 0; - - if (G_UNLIKELY (ret == 0)) - ret = g_quark_from_static_string ("nm-agent-manager-error"); - return ret; -} - -#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC } - -static GType -nm_agent_manager_error_get_type (void) -{ - static GType etype = 0; - - if (etype == 0) { - static const GEnumValue values[] = { - /* Unable to determine caller's sender or UID */ - ENUM_ENTRY (NM_AGENT_MANAGER_ERROR_SENDER_UNKNOWN, "SenderUnknown"), - /* Permission for some operation was denied */ - ENUM_ENTRY (NM_AGENT_MANAGER_ERROR_PERMISSION_DENIED, "PermissionDenied"), - /* The caller's session could not be determined */ - ENUM_ENTRY (NM_AGENT_MANAGER_ERROR_SESSION_NOT_FOUND, "SessionNotFound"), - /* The identifier was invalid */ - ENUM_ENTRY (NM_AGENT_MANAGER_ERROR_INVALID_IDENTIFIER, "InvalidIdentifier"), - /* Request was not from a registered agent */ - ENUM_ENTRY (NM_AGENT_MANAGER_ERROR_NOT_REGISTERED, "NotRegistered"), - /* Some internal error occurred */ - ENUM_ENTRY (NM_AGENT_MANAGER_ERROR_INTERNAL_ERROR, "InternalError"), - /* No secrets were available */ - ENUM_ENTRY (NM_AGENT_MANAGER_ERROR_NO_SECRETS, "NoSecrets"), - { 0, 0, 0 } - }; - - etype = g_enum_register_static ("NMAgentManagerError", values); - } - return etype; -} - -/*************************************************************/ - -static gboolean -remove_agent (NMAgentManager *self, const char *owner) -{ - NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self); - NMSecretAgent *agent; - GHashTableIter iter; - gpointer data; - - g_return_val_if_fail (owner != NULL, FALSE); - - /* Make sure this agent has already registered */ - agent = g_hash_table_lookup (priv->agents, owner); - if (!agent) - return FALSE; - - nm_log_dbg (LOGD_AGENTS, "(%s) agent unregistered", - nm_secret_agent_get_description (agent)); - - /* Remove this agent to any in-progress secrets requests */ - g_hash_table_iter_init (&iter, priv->requests); - while (g_hash_table_iter_next (&iter, NULL, &data)) - request_remove_agent ((Request *) data, agent); - - /* And dispose of the agent */ - g_hash_table_remove (priv->agents, owner); - return TRUE; -} - -/*************************************************************/ - -static gboolean -validate_identifier (const char *identifier, GError **error) -{ - const char *p = identifier; - size_t id_len; - - if (!identifier) { - g_set_error_literal (error, - NM_AGENT_MANAGER_ERROR, - NM_AGENT_MANAGER_ERROR_INVALID_IDENTIFIER, - "No identifier was given"); - return FALSE; - } - - /* Length between 3 and 255 characters inclusive */ - id_len = strlen (identifier); - if (id_len < 3 || id_len > 255) { - g_set_error_literal (error, - NM_AGENT_MANAGER_ERROR, - NM_AGENT_MANAGER_ERROR_INVALID_IDENTIFIER, - "Identifier length not between 3 and 255 characters (inclusive)"); - return FALSE; - } - - if ((identifier[0] == '.') || (identifier[id_len - 1] == '.')) { - g_set_error_literal (error, - NM_AGENT_MANAGER_ERROR, - NM_AGENT_MANAGER_ERROR_INVALID_IDENTIFIER, - "Identifier must not start or end with '.'"); - return FALSE; - } - - /* FIXME: do complete validation here */ - while (p && *p) { - if (!isalnum (*p) && (*p != '_') && (*p != '-') && (*p != '.')) { - g_set_error (error, - NM_AGENT_MANAGER_ERROR, - NM_AGENT_MANAGER_ERROR_INVALID_IDENTIFIER, - "Identifier contains invalid character '%c'", *p); - return FALSE; - } - - if ((*p == '.') && (*(p + 1) == '.')) { - g_set_error_literal (error, - NM_AGENT_MANAGER_ERROR, - NM_AGENT_MANAGER_ERROR_INVALID_IDENTIFIER, - "Identifier contains two '.' characters in sequence"); - return FALSE; - } - p++; - } - - return TRUE; -} - -static void -impl_agent_manager_register (NMAgentManager *self, - const char *identifier, - DBusGMethodInvocation *context) -{ - NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self); - char *error_desc = NULL, *sender = NULL; - gulong sender_uid = G_MAXULONG; - GError *error = NULL, *local = NULL; - NMSecretAgent *agent; - GHashTableIter iter; - gpointer data; - - if (!nm_auth_get_caller_uid (context, - priv->dbus_mgr, - &sender_uid, - &error_desc)) { - error = g_error_new_literal (NM_AGENT_MANAGER_ERROR, - NM_AGENT_MANAGER_ERROR_SENDER_UNKNOWN, - error_desc); - g_free (error_desc); - goto done; - } - - if (!nm_session_monitor_uid_has_session (priv->session_monitor, - sender_uid, - NULL, - &local)) { - error = g_error_new_literal (NM_AGENT_MANAGER_ERROR, - NM_AGENT_MANAGER_ERROR_SESSION_NOT_FOUND, - local && local->message ? local->message : "Session not found"); - goto done; - } - - sender = dbus_g_method_get_sender (context); - if (!sender) { - error = g_error_new_literal (NM_AGENT_MANAGER_ERROR, - NM_AGENT_MANAGER_ERROR_SENDER_UNKNOWN, - "Failed to get D-Bus request sender"); - goto done; - } - - /* Validate the identifier */ - if (!validate_identifier (identifier, &error)) - goto done; - - /* Success, add the new agent */ - agent = nm_secret_agent_new (priv->dbus_mgr, sender, identifier, sender_uid); - if (!agent) { - error = g_error_new_literal (NM_AGENT_MANAGER_ERROR, - NM_AGENT_MANAGER_ERROR_INTERNAL_ERROR, - "Failed to initialize the agent"); - goto done; - } - - g_hash_table_insert (priv->agents, g_strdup (sender), agent); - nm_log_dbg (LOGD_AGENTS, "(%s) agent registered", - nm_secret_agent_get_description (agent)); - dbus_g_method_return (context); - - /* Add this agent to any in-progress secrets requests */ - g_hash_table_iter_init (&iter, priv->requests); - while (g_hash_table_iter_next (&iter, NULL, &data)) - request_add_agent ((Request *) data, agent, priv->session_monitor); - -done: - if (error) - dbus_g_method_return_error (context, error); - g_clear_error (&error); - g_clear_error (&local); - g_free (sender); -} - -static void -impl_agent_manager_unregister (NMAgentManager *self, - DBusGMethodInvocation *context) -{ - GError *error = NULL; - char *sender = NULL; - - sender = dbus_g_method_get_sender (context); - if (!sender) { - error = g_error_new_literal (NM_AGENT_MANAGER_ERROR, - NM_AGENT_MANAGER_ERROR_SENDER_UNKNOWN, - "Failed to get D-Bus request sender"); - goto done; - } - - /* Found the agent, unregister and remove it */ - if (!remove_agent (self, sender)) { - error = g_error_new_literal (NM_AGENT_MANAGER_ERROR, - NM_AGENT_MANAGER_ERROR_NOT_REGISTERED, - "Caller is not registered as an Agent"); - goto done; - } - - dbus_g_method_return (context); - -done: - if (error) - dbus_g_method_return_error (context, error); - g_clear_error (&error); - g_free (sender); -} - -/*************************************************************/ - -typedef void (*RequestCompleteFunc) (Request *req, - GHashTable *secrets, - const char *agent_dbus_owner, - const char *agent_username, - gboolean agent_has_modify, - GError *error, - gpointer user_data); -typedef void (*RequestNextFunc) (Request *req); -typedef void (*RequestCancelFunc) (Request *req); - -struct _Request { - guint32 reqid; - PolkitAuthority *authority; - NMAuthChain *chain; - - NMConnection *connection; - gboolean filter_by_uid; - gulong uid_filter; - char *setting_name; - NMSettingsGetSecretsFlags flags; - char *hint; - - /* Current agent being asked for secrets */ - NMSecretAgent *current; - gconstpointer current_call_id; - gboolean current_has_modify; - - /* Stores the sorted list of NMSecretAgents which will be asked for secrets */ - GSList *pending; - - /* Stores the list of NMSecretAgent hashes that we've already - * asked for secrets, so that we don't ask the same agent twice - * if it quits and re-registers during this secrets request. - */ - GSList *asked; - - guint32 idle_id; - - GHashTable *existing_secrets; - - NMAgentSecretsResultFunc callback; - gpointer callback_data; - gpointer other_data2; - gpointer other_data3; - - RequestCancelFunc cancel_callback; - RequestNextFunc next_callback; - RequestCompleteFunc complete_callback; - gpointer complete_callback_data; -}; - -static guint32 next_req_id = 1; - -static Request * -request_new_get (NMConnection *connection, - PolkitAuthority *authority, - gboolean filter_by_uid, - gulong uid_filter, - GHashTable *existing_secrets, - const char *setting_name, - NMSettingsGetSecretsFlags flags, - const char *hint, - NMAgentSecretsResultFunc callback, - gpointer callback_data, - gpointer other_data2, - gpointer other_data3, - RequestCompleteFunc complete_callback, - gpointer complete_callback_data, - RequestNextFunc next_callback, - RequestCancelFunc cancel_callback) -{ - Request *req; - - req = g_malloc0 (sizeof (Request)); - req->reqid = next_req_id++; - req->connection = g_object_ref (connection); - req->authority = g_object_ref (authority); - req->filter_by_uid = filter_by_uid; - req->uid_filter = uid_filter; - if (existing_secrets) - req->existing_secrets = g_hash_table_ref (existing_secrets); - req->setting_name = g_strdup (setting_name); - req->flags = flags; - req->hint = g_strdup (hint); - req->callback = callback; - req->callback_data = callback_data; - req->other_data2 = other_data2; - req->other_data3 = other_data3; - req->complete_callback = complete_callback; - req->complete_callback_data = complete_callback_data; - req->next_callback = next_callback; - req->cancel_callback = cancel_callback; - - return req; -} - -static Request * -request_new_other (NMConnection *connection, - gboolean filter_by_uid, - gulong uid_filter, - RequestCompleteFunc complete_callback, - gpointer complete_callback_data, - RequestNextFunc next_callback) -{ - Request *req; - - req = g_malloc0 (sizeof (Request)); - req->reqid = next_req_id++; - req->connection = g_object_ref (connection); - req->filter_by_uid = filter_by_uid; - req->uid_filter = uid_filter; - req->complete_callback = complete_callback; - req->complete_callback_data = complete_callback_data; - req->next_callback = next_callback; - - return req; -} - -static void -request_free (Request *req) -{ - if (req->idle_id) - g_source_remove (req->idle_id); - - if (req->cancel_callback) - req->cancel_callback (req); - - g_slist_free (req->pending); - g_slist_free (req->asked); - g_object_unref (req->connection); - g_free (req->setting_name); - g_free (req->hint); - if (req->existing_secrets) - g_hash_table_unref (req->existing_secrets); - if (req->chain) - nm_auth_chain_unref (req->chain); - if (req->authority) - g_object_unref (req->authority); - memset (req, 0, sizeof (Request)); - g_free (req); -} - -static void -req_complete_success (Request *req, - GHashTable *secrets, - const char *agent_dbus_owner, - const char *agent_uname, - gboolean agent_has_modify) -{ - req->complete_callback (req, - secrets, - agent_dbus_owner, - agent_uname, - agent_has_modify, - NULL, - req->complete_callback_data); -} - -static void -req_complete_error (Request *req, GError *error) -{ - req->complete_callback (req, NULL, NULL, NULL, FALSE, error, req->complete_callback_data); -} - -static gint -agent_compare_func (NMSecretAgent *a, NMSecretAgent *b, gpointer user_data) -{ - NMSessionMonitor *session_monitor = NM_SESSION_MONITOR (user_data); - gboolean a_active, b_active; - - if (a && !b) - return -1; - else if (a == b) - return 0; - else if (!a && b) - return 1; - - /* Prefer agents in active sessions */ - a_active = nm_session_monitor_uid_active (session_monitor, - nm_secret_agent_get_owner_uid (a), - NULL); - b_active = nm_session_monitor_uid_active (session_monitor, - nm_secret_agent_get_owner_uid (b), - NULL); - if (a_active && !b_active) - return -1; - else if (a_active == b_active) - return 0; - else if (!a_active && b_active) - return 1; - - return 0; -} - -static void -request_add_agent (Request *req, - NMSecretAgent *agent, - NMSessionMonitor *session_monitor) -{ - uid_t agent_uid; - - g_return_if_fail (req != NULL); - g_return_if_fail (agent != NULL); - - if (g_slist_find (req->asked, GUINT_TO_POINTER (nm_secret_agent_get_hash (agent)))) - return; - - /* Ensure the caller's username exists in the connection's permissions, - * or that the permissions is empty (ie, visible by everyone). - */ - agent_uid = nm_secret_agent_get_owner_uid (agent); - if (0 != agent_uid) { - if (!nm_auth_uid_in_acl (req->connection, session_monitor, agent_uid, NULL)) { - nm_log_dbg (LOGD_AGENTS, "(%s) agent ignored for secrets request %p/%s (not in ACL)", - nm_secret_agent_get_description (agent), - req, req->setting_name); - /* Connection not visible to this agent's user */ - return; - } - /* Caller is allowed to manipulate this connection */ - } - - /* If the request should filter agents by UID, do that now */ - if (req->filter_by_uid && (agent_uid != req->uid_filter)) { - nm_log_dbg (LOGD_AGENTS, "(%s) agent ignored for secrets request %p/%s " - "(uid %d not required %ld)", - nm_secret_agent_get_description (agent), - req, req->setting_name, agent_uid, req->uid_filter); - return; - } - - nm_log_dbg (LOGD_AGENTS, "(%s) agent allowed for secrets request %p/%s", - nm_secret_agent_get_description (agent), - req, req->setting_name); - - /* Add this agent to the list, preferring active sessions */ - req->pending = g_slist_insert_sorted_with_data (req->pending, - agent, - (GCompareDataFunc) agent_compare_func, - session_monitor); -} - -static void -request_add_agents (NMAgentManager *self, Request *req) -{ - NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self); - GHashTableIter iter; - gpointer data; - - g_hash_table_iter_init (&iter, priv->agents); - while (g_hash_table_iter_next (&iter, NULL, &data)) - request_add_agent (req, NM_SECRET_AGENT (data), priv->session_monitor); -} - -static void -request_remove_agent (Request *req, NMSecretAgent *agent) -{ - gboolean try_next = FALSE; - const char *detail = ""; - - g_return_if_fail (req != NULL); - g_return_if_fail (agent != NULL); - - /* If this agent is being asked right now, cancel the request */ - if (agent == req->current) { - req->cancel_callback (req); - req->current_has_modify = FALSE; - req->current = NULL; - req->current_call_id = NULL; - try_next = TRUE; - detail = " current"; - } - - nm_log_dbg (LOGD_AGENTS, "(%s)%s agent removed from secrets request %p/%s", - nm_secret_agent_get_description (agent), - detail, req, req->setting_name); - - req->pending = g_slist_remove (req->pending, agent); - - if (try_next) { - /* If the agent serving the in-progress secrets request went away then - * we need to send the request to the next agent. - */ - req->next_callback (req); - } -} - -static gboolean -next_generic (Request *req, const char *detail) -{ - GError *error = NULL; - gboolean success = FALSE; - - if (req->pending == NULL) { - /* No more secret agents are available to fulfill this secrets request */ - error = g_error_new_literal (NM_AGENT_MANAGER_ERROR, - NM_AGENT_MANAGER_ERROR_NO_SECRETS, - "No agents were available for this request."); - req_complete_error (req, error); - g_error_free (error); - } else { - /* Send a secrets request to the next agent */ - req->current_has_modify = FALSE; - req->current = req->pending->data; - req->pending = g_slist_remove (req->pending, req->current); - - nm_log_dbg (LOGD_AGENTS, "(%s) agent %s secrets for request %p/%s", - nm_secret_agent_get_description (req->current), - detail, req, req->setting_name); - success = TRUE; - } - - return success; -} - -static gboolean -start_generic (gpointer user_data) -{ - Request *req = user_data; - - req->idle_id = 0; - req->next_callback (req); - return FALSE; -} - - -/*************************************************************/ - -static void -get_done_cb (NMSecretAgent *agent, - gconstpointer call_id, - GHashTable *secrets, - GError *error, - gpointer user_data) -{ - Request *req = user_data; - GHashTable *setting_secrets; - const char *agent_dbus_owner; - gboolean agent_has_modify; - struct passwd *pw; - char *agent_uname = NULL; - - g_return_if_fail (call_id == req->current_call_id); - - agent_has_modify = req->current_has_modify; - req->current_has_modify = FALSE; - req->current = NULL; - req->current_call_id = NULL; - - if (error) { - nm_log_dbg (LOGD_AGENTS, "(%s) agent failed secrets request %p/%s: (%d) %s", - nm_secret_agent_get_description (agent), - req, req->setting_name, - error ? error->code : -1, - (error && error->message) ? error->message : "(unknown)"); - - /* Try the next agent */ - req->next_callback (req); - return; - } - - /* Ensure the setting we wanted secrets for got returned and has something in it */ - setting_secrets = g_hash_table_lookup (secrets, req->setting_name); - if (!setting_secrets || !g_hash_table_size (setting_secrets)) { - nm_log_dbg (LOGD_AGENTS, "(%s) agent returned no secrets for request %p/%s", - nm_secret_agent_get_description (agent), - req, req->setting_name); - - /* Try the next agent */ - req->next_callback (req); - return; - } - - nm_log_dbg (LOGD_AGENTS, "(%s) agent returned secrets for request %p/%s", - nm_secret_agent_get_description (agent), - req, req->setting_name); - - /* Get the agent's username */ - pw = getpwuid (nm_secret_agent_get_owner_uid (agent)); - if (pw && strlen (pw->pw_name)) { - /* Needs to be UTF-8 valid since it may be pushed through D-Bus */ - if (g_utf8_validate (pw->pw_name, -1, NULL)) - agent_uname = g_strdup (pw->pw_name); - } - - agent_dbus_owner = nm_secret_agent_get_dbus_owner (agent); - req_complete_success (req, secrets, agent_dbus_owner, agent_uname, agent_has_modify); - g_free (agent_uname); -} - -static void -set_secrets_not_required (NMConnection *connection, GHashTable *hash) -{ - GHashTableIter iter, setting_iter; - const char *setting_name = NULL; - GHashTable *setting_hash = NULL; - - /* Iterate through the settings hashes */ - g_hash_table_iter_init (&iter, hash); - while (g_hash_table_iter_next (&iter, - (gpointer *) &setting_name, - (gpointer *) &setting_hash)) { - const char *key_name = NULL; - NMSetting *setting; - GValue *val; - - setting = nm_connection_get_setting_by_name (connection, setting_name); - if (setting) { - /* Now through each secret in the setting and mark it as not required */ - g_hash_table_iter_init (&setting_iter, setting_hash); - while (g_hash_table_iter_next (&setting_iter, (gpointer *) &key_name, (gpointer *) &val)) { - /* For each secret, set the flag that it's not required; VPN - * secrets need slightly different treatment here since the - * "secrets" property is actually a hash table of secrets. - */ - if ( strcmp (setting_name, NM_SETTING_VPN_SETTING_NAME) == 0 - && strcmp (key_name, NM_SETTING_VPN_SECRETS) == 0 - && G_VALUE_HOLDS (val, DBUS_TYPE_G_MAP_OF_STRING)) { - GHashTableIter vpn_secret_iter; - const char *secret_name; - - g_hash_table_iter_init (&vpn_secret_iter, g_value_get_boxed (val)); - while (g_hash_table_iter_next (&vpn_secret_iter, (gpointer *) &secret_name, NULL)) - nm_setting_set_secret_flags (setting, secret_name, NM_SETTING_SECRET_FLAG_NOT_REQUIRED, NULL); - } else - nm_setting_set_secret_flags (setting, key_name, NM_SETTING_SECRET_FLAG_NOT_REQUIRED, NULL); - } - } - } -} - -static void -get_agent_request_secrets (Request *req, gboolean include_system_secrets) -{ - NMConnection *tmp; - - tmp = nm_connection_duplicate (req->connection); - nm_connection_clear_secrets (tmp); - if (include_system_secrets) { - if (req->existing_secrets) - nm_connection_update_secrets (tmp, req->setting_name, req->existing_secrets, NULL); - } else { - /* Update secret flags in the temporary connection to indicate that - * the system secrets we're not sending to the agent aren't required, - * so the agent can properly validate UI controls and such. - */ - if (req->existing_secrets) - set_secrets_not_required (tmp, req->existing_secrets); - } - - req->current_call_id = nm_secret_agent_get_secrets (NM_SECRET_AGENT (req->current), - tmp, - req->setting_name, - req->hint, - req->flags, - get_done_cb, - req); - if (req->current_call_id == NULL) { - /* Shouldn't hit this, but handle it anyway */ - g_warn_if_fail (req->current_call_id != NULL); - req->current_has_modify = FALSE; - req->current = NULL; - req->next_callback (req); - } - - g_object_unref (tmp); -} - -static void -get_agent_modify_auth_cb (NMAuthChain *chain, - GError *error, - DBusGMethodInvocation *context, - gpointer user_data) -{ - Request *req = user_data; - NMAuthCallResult result; - const char *perm; - - req->chain = NULL; - - if (error) { - nm_log_dbg (LOGD_AGENTS, "(%p/%s) agent MODIFY check error: (%d) %s", - req, req->setting_name, - error->code, error->message ? error->message : "(unknown)"); - - /* Try the next agent */ - req->next_callback (req); - } else { - /* If the agent obtained the 'modify' permission, we send all system secrets - * to it. If it didn't, we still ask it for secrets, but we don't send - * any system secrets. - */ - perm = nm_auth_chain_get_data (chain, "perm"); - g_assert (perm); - result = nm_auth_chain_get_result (chain, perm); - if (result == NM_AUTH_CALL_RESULT_YES) - req->current_has_modify = TRUE; - - nm_log_dbg (LOGD_AGENTS, "(%p/%s) agent MODIFY check result %d", - req, req->setting_name, result); - - get_agent_request_secrets (req, req->current_has_modify); - } - nm_auth_chain_unref (chain); -} - -static void -check_system_secrets_cb (NMSetting *setting, - const char *key, - const GValue *value, - GParamFlags flags, - gpointer user_data) -{ - NMSettingSecretFlags secret_flags = NM_SETTING_SECRET_FLAG_NONE; - gboolean *has_system = user_data; - - if (!(flags & NM_SETTING_PARAM_SECRET)) - return; - - /* Clear out system-owned or always-ask secrets */ - if (NM_IS_SETTING_VPN (setting) && !strcmp (key, NM_SETTING_VPN_SECRETS)) { - GHashTableIter iter; - const char *secret_name = NULL; - - /* VPNs are special; need to handle each secret separately */ - g_hash_table_iter_init (&iter, (GHashTable *) g_value_get_boxed (value)); - while (g_hash_table_iter_next (&iter, (gpointer *) &secret_name, NULL)) { - if (nm_setting_get_secret_flags (setting, secret_name, &secret_flags, NULL)) { - if (secret_flags == NM_SETTING_SECRET_FLAG_NONE) - *has_system = TRUE; - } - } - } else { - nm_setting_get_secret_flags (setting, key, &secret_flags, NULL); - if (secret_flags == NM_SETTING_SECRET_FLAG_NONE) - *has_system = TRUE; - } -} - -static gboolean -has_system_secrets (NMConnection *connection) -{ - gboolean has_system = FALSE; - - nm_connection_for_each_setting_value (connection, check_system_secrets_cb, &has_system); - return has_system; -} - -static void -get_next_cb (Request *req) -{ - NMSettingConnection *s_con; - const char *agent_dbus_owner, *perm; - - if (!next_generic (req, "getting")) - return; - - agent_dbus_owner = nm_secret_agent_get_dbus_owner (NM_SECRET_AGENT (req->current)); - - /* If the request flags allow user interaction, and there are existing - * system secrets (or blank secrets that are supposed to be system-owned), - * check whether the agent has the 'modify' permission before sending those - * secrets to the agent. We shouldn't leak system-owned secrets to - * unprivileged users. - */ - if ( (req->flags != NM_SETTINGS_GET_SECRETS_FLAG_NONE) - && (req->existing_secrets || has_system_secrets (req->connection))) { - nm_log_dbg (LOGD_AGENTS, "(%p/%s) request has system secrets; checking agent %s for MODIFY", - req, req->setting_name, agent_dbus_owner); - - req->chain = nm_auth_chain_new_dbus_sender (req->authority, - agent_dbus_owner, - get_agent_modify_auth_cb, - req); - g_assert (req->chain); - - /* If the caller is the only user in the connection's permissions, then - * we use the 'modify.own' permission instead of 'modify.system'. If the - * request affects more than just the caller, require 'modify.system'. - */ - s_con = (NMSettingConnection *) nm_connection_get_setting (req->connection, NM_TYPE_SETTING_CONNECTION); - g_assert (s_con); - if (nm_setting_connection_get_num_permissions (s_con) == 1) - perm = NM_AUTH_PERMISSION_SETTINGS_MODIFY_OWN; - else - perm = NM_AUTH_PERMISSION_SETTINGS_MODIFY_SYSTEM; - nm_auth_chain_set_data (req->chain, "perm", (gpointer) perm, NULL); - - nm_auth_chain_add_call (req->chain, perm, TRUE); - } else { - nm_log_dbg (LOGD_AGENTS, "(%p/%s) requesting user-owned secrets from agent %s", - req, req->setting_name, agent_dbus_owner); - - get_agent_request_secrets (req, FALSE); - } -} - -static gboolean -get_start (gpointer user_data) -{ - Request *req = user_data; - GHashTable *setting_secrets = NULL; - - req->idle_id = 0; - - /* Check if there are any existing secrets */ - if (req->existing_secrets) - setting_secrets = g_hash_table_lookup (req->existing_secrets, req->setting_name); - - if (setting_secrets && g_hash_table_size (setting_secrets)) { - NMConnection *tmp; - GError *error = NULL; - gboolean request_new = (req->flags & NM_SETTINGS_GET_SECRETS_FLAG_REQUEST_NEW); - - /* The connection already had secrets; check if any more are required. - * If no more are required, we're done. If secrets are still needed, - * ask a secret agent for more. This allows admins to provide generic - * secrets but allow additional user-specific ones as well. - */ - tmp = nm_connection_duplicate (req->connection); - g_assert (tmp); - - if (!nm_connection_update_secrets (tmp, req->setting_name, req->existing_secrets, &error)) { - req_complete_error (req, error); - g_clear_error (&error); - } else { - /* Do we have everything we need? */ - /* FIXME: handle second check for VPN connections */ - if ((nm_connection_need_secrets (tmp, NULL) == NULL) && (request_new == FALSE)) { - nm_log_dbg (LOGD_AGENTS, "(%p/%s) system settings secrets sufficient", - req, req->setting_name); - - /* Got everything, we're done */ - req_complete_success (req, req->existing_secrets, NULL, NULL, FALSE); - } else { - nm_log_dbg (LOGD_AGENTS, "(%p/%s) system settings secrets insufficient, asking agents", - req, req->setting_name); - - /* We don't, so ask some agents for additional secrets */ - req->next_callback (req); - } - } - g_object_unref (tmp); - } else { - /* Couldn't get secrets from system settings, so now we ask the - * agents for secrets. Let the Agent Manager handle which agents - * we'll ask and in which order. - */ - req->next_callback (req); - } - - return FALSE; -} - -static void -get_complete_cb (Request *req, - GHashTable *secrets, - const char *agent_dbus_owner, - const char *agent_username, - gboolean agent_has_modify, - GError *error, - gpointer user_data) -{ - NMAgentManager *self = NM_AGENT_MANAGER (user_data); - NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self); - - /* Send secrets back to the requesting object */ - req->callback (self, - req->reqid, - agent_dbus_owner, - agent_username, - agent_has_modify, - req->setting_name, - req->flags, - error ? NULL : secrets, - error, - req->callback_data, - req->other_data2, - req->other_data3); - - g_hash_table_remove (priv->requests, GUINT_TO_POINTER (req->reqid)); -} - -static void -get_cancel_cb (Request *req) -{ - if (req->current && req->current_call_id) - nm_secret_agent_cancel_secrets (req->current, req->current_call_id); -} - -guint32 -nm_agent_manager_get_secrets (NMAgentManager *self, - NMConnection *connection, - gboolean filter_by_uid, - gulong uid_filter, - GHashTable *existing_secrets, - const char *setting_name, - NMSettingsGetSecretsFlags flags, - const char *hint, - NMAgentSecretsResultFunc callback, - gpointer callback_data, - gpointer other_data2, - gpointer other_data3) -{ - NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self); - Request *req; - - g_return_val_if_fail (self != NULL, 0); - g_return_val_if_fail (connection != NULL, 0); - g_return_val_if_fail (NM_IS_CONNECTION (connection), 0); - g_return_val_if_fail (callback != NULL, 0); - - nm_log_dbg (LOGD_SETTINGS, - "Secrets requested for connection %s (%s)", - nm_connection_get_path (connection), - setting_name); - - /* NOTE: a few things in the Request handling depend on existing_secrets - * being NULL if there aren't any system-owned secrets for this connection. - * This in turn depends on nm_connection_to_hash() and nm_setting_to_hash() - * both returning NULL if they didn't hash anything. - */ - - req = request_new_get (connection, - priv->authority, - filter_by_uid, - uid_filter, - existing_secrets, - setting_name, - flags, - hint, - callback, - callback_data, - other_data2, - other_data3, - get_complete_cb, - self, - get_next_cb, - get_cancel_cb); - g_hash_table_insert (priv->requests, GUINT_TO_POINTER (req->reqid), req); - - /* Kick off the request */ - request_add_agents (self, req); - req->idle_id = g_idle_add (get_start, req); - - return req->reqid; -} - -void -nm_agent_manager_cancel_secrets (NMAgentManager *self, - guint32 request_id) -{ - g_return_if_fail (self != NULL); - g_return_if_fail (request_id > 0); - - g_hash_table_remove (NM_AGENT_MANAGER_GET_PRIVATE (self)->requests, - GUINT_TO_POINTER (request_id)); -} - -/*************************************************************/ - -static void -save_done_cb (NMSecretAgent *agent, - gconstpointer call_id, - GHashTable *secrets, - GError *error, - gpointer user_data) -{ - Request *req = user_data; - const char *agent_dbus_owner; - - g_return_if_fail (call_id == req->current_call_id); - - req->current = NULL; - req->current_call_id = NULL; - - if (error) { - nm_log_dbg (LOGD_AGENTS, "(%s) agent failed save secrets request %p/%s: (%d) %s", - nm_secret_agent_get_description (agent), - req, req->setting_name, - error ? error->code : -1, - (error && error->message) ? error->message : "(unknown)"); - - /* Try the next agent */ - req->next_callback (req); - return; - } - - nm_log_dbg (LOGD_AGENTS, "(%s) agent saved secrets for request %p/%s", - nm_secret_agent_get_description (agent), - req, req->setting_name); - - agent_dbus_owner = nm_secret_agent_get_dbus_owner (agent); - req_complete_success (req, NULL, NULL, agent_dbus_owner, FALSE); -} - -static void -save_next_cb (Request *req) -{ - if (!next_generic (req, "saving")) - return; - - req->current_call_id = nm_secret_agent_save_secrets (NM_SECRET_AGENT (req->current), - req->connection, - save_done_cb, - req); - if (req->current_call_id == NULL) { - /* Shouldn't hit this, but handle it anyway */ - g_warn_if_fail (req->current_call_id != NULL); - req->current = NULL; - req->next_callback (req); - } -} - -static void -save_complete_cb (Request *req, - GHashTable *secrets, - const char *agent_dbus_owner, - const char *agent_username, - gboolean agent_has_modify, - GError *error, - gpointer user_data) -{ - NMAgentManager *self = NM_AGENT_MANAGER (user_data); - NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self); - - g_hash_table_remove (priv->requests, GUINT_TO_POINTER (req->reqid)); -} - -guint32 -nm_agent_manager_save_secrets (NMAgentManager *self, - NMConnection *connection, - gboolean filter_by_uid, - gulong uid_filter) -{ - NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self); - Request *req; - - g_return_val_if_fail (self != NULL, 0); - g_return_val_if_fail (connection != NULL, 0); - g_return_val_if_fail (NM_IS_CONNECTION (connection), 0); - - nm_log_dbg (LOGD_SETTINGS, - "Saving secrets for connection %s", - nm_connection_get_path (connection)); - - req = request_new_other (connection, - filter_by_uid, - uid_filter, - save_complete_cb, - self, - save_next_cb); - g_hash_table_insert (priv->requests, GUINT_TO_POINTER (req->reqid), req); - - /* Kick off the request */ - request_add_agents (self, req); - req->idle_id = g_idle_add (start_generic, req); - - return req->reqid; -} - -/*************************************************************/ - -static void -delete_done_cb (NMSecretAgent *agent, - gconstpointer call_id, - GHashTable *secrets, - GError *error, - gpointer user_data) -{ - Request *req = user_data; - - g_return_if_fail (call_id == req->current_call_id); - - req->current = NULL; - req->current_call_id = NULL; - - if (error) { - nm_log_dbg (LOGD_AGENTS, "(%s) agent failed delete secrets request %p/%s: (%d) %s", - nm_secret_agent_get_description (agent), - req, req->setting_name, - error ? error->code : -1, - (error && error->message) ? error->message : "(unknown)"); - } else { - nm_log_dbg (LOGD_AGENTS, "(%s) agent deleted secrets for request %p/%s", - nm_secret_agent_get_description (agent), - req, req->setting_name); - } - - /* Tell the next agent to delete secrets */ - req->next_callback (req); -} - -static void -delete_next_cb (Request *req) -{ - if (!next_generic (req, "deleting")) - return; - - req->current_call_id = nm_secret_agent_delete_secrets (NM_SECRET_AGENT (req->current), - req->connection, - delete_done_cb, - req); - if (req->current_call_id == NULL) { - /* Shouldn't hit this, but handle it anyway */ - g_warn_if_fail (req->current_call_id != NULL); - req->current = NULL; - req->next_callback (req); - } -} - -static void -delete_complete_cb (Request *req, - GHashTable *secrets, - const char *agent_dbus_owner, - const char *agent_username, - gboolean agent_has_modify, - GError *error, - gpointer user_data) -{ - NMAgentManager *self = NM_AGENT_MANAGER (user_data); - NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self); - - g_hash_table_remove (priv->requests, GUINT_TO_POINTER (req->reqid)); -} - -guint32 -nm_agent_manager_delete_secrets (NMAgentManager *self, - NMConnection *connection, - gboolean filter_by_uid, - gulong uid_filter) -{ - NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self); - Request *req; - - g_return_val_if_fail (self != NULL, 0); - g_return_val_if_fail (connection != NULL, 0); - g_return_val_if_fail (NM_IS_CONNECTION (connection), 0); - - nm_log_dbg (LOGD_SETTINGS, - "Deleting secrets for connection %s", - nm_connection_get_path (connection)); - - req = request_new_other (connection, - filter_by_uid, - uid_filter, - delete_complete_cb, - self, - delete_next_cb); - g_hash_table_insert (priv->requests, GUINT_TO_POINTER (req->reqid), req); - - /* Kick off the request */ - request_add_agents (self, req); - req->idle_id = g_idle_add (start_generic, req); - - return req->reqid; -} - -/*************************************************************/ - -static void -name_owner_changed_cb (NMDBusManager *dbus_mgr, - const char *name, - const char *old_owner, - const char *new_owner, - gpointer user_data) -{ - if (old_owner) { - /* The agent quit, so remove it and let interested clients know */ - remove_agent (NM_AGENT_MANAGER (user_data), old_owner); - } -} - -/*************************************************************/ - -NMAgentManager * -nm_agent_manager_get (void) -{ - static NMAgentManager *singleton = NULL; - NMAgentManagerPrivate *priv; - DBusGConnection *connection; - - if (singleton) - return g_object_ref (singleton); - - singleton = (NMAgentManager *) g_object_new (NM_TYPE_AGENT_MANAGER, NULL); - g_assert (singleton); - - priv = NM_AGENT_MANAGER_GET_PRIVATE (singleton); - priv->session_monitor = nm_session_monitor_get (); - priv->dbus_mgr = nm_dbus_manager_get (); - - connection = nm_dbus_manager_get_connection (priv->dbus_mgr); - dbus_g_connection_register_g_object (connection, - NM_DBUS_PATH_AGENT_MANAGER, - G_OBJECT (singleton)); - - g_signal_connect (priv->dbus_mgr, - NM_DBUS_MANAGER_NAME_OWNER_CHANGED, - G_CALLBACK (name_owner_changed_cb), - singleton); - - return singleton; -} - -static void -nm_agent_manager_init (NMAgentManager *self) -{ - NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (self); - GError *error = NULL; - - priv->authority = polkit_authority_get_sync (NULL, &error); - if (!priv->authority) { - nm_log_warn (LOGD_SETTINGS, "failed to create PolicyKit authority: (%d) %s", - error ? error->code : -1, - error && error->message ? error->message : "(unknown)"); - g_clear_error (&error); - } - - priv->agents = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); - priv->requests = g_hash_table_new_full (g_direct_hash, - g_direct_equal, - NULL, - (GDestroyNotify) request_free); -} - -static void -dispose (GObject *object) -{ - NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (object); - - if (!priv->disposed) { - priv->disposed = TRUE; - - g_hash_table_destroy (priv->agents); - g_hash_table_destroy (priv->requests); - - g_object_unref (priv->session_monitor); - g_object_unref (priv->dbus_mgr); - g_object_unref (priv->authority); - } - - G_OBJECT_CLASS (nm_agent_manager_parent_class)->dispose (object); -} - -static void -nm_agent_manager_class_init (NMAgentManagerClass *agent_manager_class) -{ - GObjectClass *object_class = G_OBJECT_CLASS (agent_manager_class); - - g_type_class_add_private (agent_manager_class, sizeof (NMAgentManagerPrivate)); - - /* virtual methods */ - object_class->dispose = dispose; - - dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (agent_manager_class), - &dbus_glib_nm_agent_manager_object_info); - - dbus_g_error_domain_register (NM_AGENT_MANAGER_ERROR, - NM_DBUS_INTERFACE_AGENT_MANAGER, - NM_TYPE_AGENT_MANAGER_ERROR); -} |