diff --git a/include/NetworkManager.h b/include/NetworkManager.h index eef2bb1d1d..ea05c1f498 100644 --- a/include/NetworkManager.h +++ b/include/NetworkManager.h @@ -52,6 +52,8 @@ #define NM_DBUS_PATH_SETTINGS_CONNECTION "/org/freedesktop/NetworkManager/Settings/Connection" #define NM_DBUS_IFACE_SETTINGS_CONNECTION_SECRETS "org.freedesktop.NetworkManager.Settings.Connection.Secrets" +#define NM_DBUS_INTERFACE_AGENT_MANAGER NM_DBUS_INTERFACE ".AgentManager" +#define NM_DBUS_PATH_AGENT_MANAGER "/org/freedesktop/NetworkManager/AgentManager" /* * Types of NetworkManager states diff --git a/introspection/Makefile.am b/introspection/Makefile.am index 0092504266..54877c7545 100644 --- a/introspection/Makefile.am +++ b/introspection/Makefile.am @@ -23,5 +23,6 @@ EXTRA_DIST = \ nm-ppp-manager.xml \ nm-active-connection.xml \ nm-dhcp4-config.xml \ - nm-dhcp6-config.xml + nm-dhcp6-config.xml \ + nm-agent-manager.xml diff --git a/introspection/all.xml b/introspection/all.xml index 1f33708e53..73084e1cec 100644 --- a/introspection/all.xml +++ b/introspection/all.xml @@ -42,6 +42,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + diff --git a/introspection/nm-agent-manager.xml b/introspection/nm-agent-manager.xml new file mode 100644 index 0000000000..0b1e3e6e86 --- /dev/null +++ b/introspection/nm-agent-manager.xml @@ -0,0 +1,38 @@ + + + + + + + + Called by secret Agents to register their ability to provide and save + network secrets. + + + + + + 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). + + + + + + + Called by secret Agents to notify NetworkManager that they will no + longer handle requests for network secrets. Agents are automatically + unregistered when they disconnect from D-Bus. + + + + + + + + diff --git a/src/Makefile.am b/src/Makefile.am index 217026aef9..9e27f7e938 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -149,7 +149,11 @@ NetworkManager_SOURCES = \ nm-dhcp6-config.h \ nm-rfkill.h \ nm-session-monitor.c \ - nm-session-monitor.h + nm-session-monitor.h \ + nm-agent-manager.c \ + nm-agent-manager.h \ + nm-secret-agent.c \ + nm-secret-agent.h nm-access-point-glue.h: $(top_srcdir)/introspection/nm-access-point.xml $(AM_V_GEN) dbus-binding-tool --prefix=nm_access_point --mode=glib-server --output=$@ $< @@ -193,6 +197,9 @@ nm-device-cdma-glue.h: $(top_srcdir)/introspection/nm-device-cdma.xml nm-device-gsm-glue.h: $(top_srcdir)/introspection/nm-device-gsm.xml $(AM_V_GEN) dbus-binding-tool --prefix=nm_device_gsm --mode=glib-server --output=$@ $< +nm-agent-manager-glue.h: $(top_srcdir)/introspection/nm-agent-manager.xml + $(AM_V_GEN) dbus-binding-tool --prefix=nm_agent_manager --mode=glib-server --output=$@ $< + BUILT_SOURCES = \ nm-access-point-glue.h \ nm-manager-glue.h \ @@ -207,7 +214,8 @@ BUILT_SOURCES = \ nm-ip6-config-glue.h \ nm-active-connection-glue.h \ nm-dhcp4-config-glue.h \ - nm-dhcp6-config-glue.h + nm-dhcp6-config-glue.h \ + nm-agent-manager-glue.h NetworkManager_CPPFLAGS = \ $(DBUS_CFLAGS) \ diff --git a/src/nm-agent-manager.c b/src/nm-agent-manager.c new file mode 100644 index 0000000000..6b1225e1f6 --- /dev/null +++ b/src/nm-agent-manager.c @@ -0,0 +1,366 @@ +/* -*- 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 Red Hat, Inc. + */ + +#include +#include +#include + +#include +#include +#include + +#include "NetworkManager.h" +#include "nm-agent-manager.h" +#include "nm-secret-agent.h" +#include "nm-manager-auth.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; + + /* Hashed by owner name, not identifier, since two agents in different + * sessions can use the same identifier. + */ + GHashTable *agents; +} NMAgentManagerPrivate; + + +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 +} 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"), + { 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; + + g_return_val_if_fail (owner != NULL, FALSE); + + agent = g_hash_table_lookup (priv->agents, owner); + if (!agent) + return FALSE; + + /* FIXME: signal agent removal */ + + 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 != '-')) { + 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; + } + } + + 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; + + 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 (sender, identifier); + 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); + dbus_g_method_return (context); + +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); +} + +/*************************************************************/ + +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_new (NMDBusManager *dbus_mgr, NMSessionMonitor *session_monitor) +{ + NMAgentManager *self; + NMAgentManagerPrivate *priv; + DBusGConnection *connection; + + g_return_val_if_fail (dbus_mgr != NULL, NULL); + + self = (NMAgentManager *) g_object_new (NM_TYPE_AGENT_MANAGER, NULL); + if (self) { + priv = NM_AGENT_MANAGER_GET_PRIVATE (self); + + priv->session_monitor = g_object_ref (session_monitor); + priv->dbus_mgr = g_object_ref (dbus_mgr); + connection = nm_dbus_manager_get_connection (dbus_mgr); + dbus_g_connection_register_g_object (connection, NM_DBUS_PATH_AGENT_MANAGER, G_OBJECT (self)); + + g_signal_connect (priv->dbus_mgr, + NM_DBUS_MANAGER_NAME_OWNER_CHANGED, + G_CALLBACK (name_owner_changed_cb), + self); + } + + return self; +} + +static void +nm_agent_manager_init (NMAgentManager *self) +{ +} + +static void +dispose (GObject *object) +{ + NMAgentManagerPrivate *priv = NM_AGENT_MANAGER_GET_PRIVATE (object); + + if (priv->disposed) + return; + priv->disposed = TRUE; + + g_object_unref (priv->session_monitor); + g_object_unref (priv->dbus_mgr); + + g_hash_table_destroy (priv->agents); + + G_OBJECT_CLASS (nm_agent_manager_parent_class)->dispose (object); +} + +static void +nm_agent_manager_class_init (NMAgentManagerClass *config_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (config_class); + + g_type_class_add_private (config_class, sizeof (NMAgentManagerPrivate)); + + /* virtual methods */ + object_class->dispose = dispose; + + dbus_g_error_domain_register (NM_AGENT_MANAGER_ERROR, + NM_DBUS_INTERFACE_AGENT_MANAGER, + NM_TYPE_AGENT_MANAGER_ERROR); +} diff --git a/src/nm-agent-manager.h b/src/nm-agent-manager.h new file mode 100644 index 0000000000..59a31bdd9f --- /dev/null +++ b/src/nm-agent-manager.h @@ -0,0 +1,50 @@ +/* -*- 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 Red Hat, Inc. + */ + +#ifndef NM_AGENT_MANAGER_H +#define NM_AGENT_MANAGER_H + +#include +#include + +#include "nm-dbus-manager.h" +#include "nm-session-monitor.h" + +#define NM_TYPE_AGENT_MANAGER (nm_agent_manager_get_type ()) +#define NM_AGENT_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_AGENT_MANAGER, NMAgentManager)) +#define NM_AGENT_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_AGENT_MANAGER, NMAgentManagerClass)) +#define NM_IS_AGENT_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_AGENT_MANAGER)) +#define NM_IS_AGENT_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_AGENT_MANAGER)) +#define NM_AGENT_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_AGENT_MANAGER, NMAgentManagerClass)) + +typedef struct { + GObject parent; +} NMAgentManager; + +typedef struct { + GObjectClass parent; +} NMAgentManagerClass; + +GType nm_agent_manager_get_type (void); + +NMAgentManager *nm_agent_manager_new (NMDBusManager *dbus_mgr, + NMSessionMonitor *session_monitor); + +#endif /* NM_AGENT_MANAGER_H */ diff --git a/src/nm-secret-agent.c b/src/nm-secret-agent.c new file mode 100644 index 0000000000..85fd234759 --- /dev/null +++ b/src/nm-secret-agent.c @@ -0,0 +1,94 @@ +/* -*- 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 Red Hat, Inc. + */ + +#include + +#include +#include +#include + +#include "nm-secret-agent.h" + +G_DEFINE_TYPE (NMSecretAgent, nm_secret_agent, G_TYPE_OBJECT) + +#define NM_SECRET_AGENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \ + NM_TYPE_SECRET_AGENT, \ + NMSecretAgentPrivate)) + +typedef struct { + gboolean disposed; + + char *owner; + char *identifier; +} NMSecretAgentPrivate; + +/*************************************************************/ + +NMSecretAgent * +nm_secret_agent_new (const char *owner, const char *identifier) +{ + NMSecretAgent *self; + NMSecretAgentPrivate *priv; + + g_return_val_if_fail (owner != NULL, NULL); + g_return_val_if_fail (identifier != NULL, NULL); + + self = (NMSecretAgent *) g_object_new (NM_TYPE_SECRET_AGENT, NULL); + if (self) { + priv = NM_SECRET_AGENT_GET_PRIVATE (self); + + priv->owner = g_strdup (owner); + priv->identifier = g_strdup (identifier); + } + + return self; +} + +static void +nm_secret_agent_init (NMSecretAgent *self) +{ +} + +static void +dispose (GObject *object) +{ + NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (object); + + if (priv->disposed) + return; + priv->disposed = TRUE; + + g_free (priv->owner); + g_free (priv->identifier); + + G_OBJECT_CLASS (nm_secret_agent_parent_class)->dispose (object); +} + +static void +nm_secret_agent_class_init (NMSecretAgentClass *config_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS (config_class); + + g_type_class_add_private (config_class, sizeof (NMSecretAgentPrivate)); + + /* virtual methods */ + object_class->dispose = dispose; +} + diff --git a/src/nm-secret-agent.h b/src/nm-secret-agent.h new file mode 100644 index 0000000000..ecd7792cc8 --- /dev/null +++ b/src/nm-secret-agent.h @@ -0,0 +1,46 @@ +/* -*- 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 Red Hat, Inc. + */ + +#ifndef NM_SECRET_AGENT_H +#define NM_SECRET_AGENT_H + +#include +#include + +#define NM_TYPE_SECRET_AGENT (nm_secret_agent_get_type ()) +#define NM_SECRET_AGENT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SECRET_AGENT, NMSecretAgent)) +#define NM_SECRET_AGENT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SECRET_AGENT, NMSecretAgentClass)) +#define NM_IS_SECRET_AGENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_SECRET_AGENT)) +#define NM_IS_SECRET_AGENT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_SECRET_AGENT)) +#define NM_SECRET_AGENT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SECRET_AGENT, NMSecretAgentClass)) + +typedef struct { + GObject parent; +} NMSecretAgent; + +typedef struct { + GObjectClass parent; +} NMSecretAgentClass; + +GType nm_secret_agent_get_type (void); + +NMSecretAgent *nm_secret_agent_new (const char *owner, const char *identifier); + +#endif /* NM_SECRET_AGENT_H */