diff --git a/include/NetworkManager.h b/include/NetworkManager.h
index ea05c1f49..558eb3e40 100644
--- a/include/NetworkManager.h
+++ b/include/NetworkManager.h
@@ -55,6 +55,9 @@
#define NM_DBUS_INTERFACE_AGENT_MANAGER NM_DBUS_INTERFACE ".AgentManager"
#define NM_DBUS_PATH_AGENT_MANAGER "/org/freedesktop/NetworkManager/AgentManager"
+#define NM_DBUS_INTERFACE_SECRET_AGENT NM_DBUS_INTERFACE ".SecretAgent"
+#define NM_DBUS_PATH_SECRET_AGENT "/org/freedesktop/NetworkManager/SecretAgent"
+
/*
* Types of NetworkManager states
*/
diff --git a/introspection/nm-secret-agent.xml b/introspection/nm-secret-agent.xml
new file mode 100644
index 000000000..bfab5852c
--- /dev/null
+++ b/introspection/nm-secret-agent.xml
@@ -0,0 +1,106 @@
+
+
+
+
+
+
+ Private D-Bus interface used by secret agents that store and provide
+ secrets to NetworkManager.
+
+
+
+
+ Retrieve and return stored secrets, if any, or request new
+ secrets from the agent's user.
+
+
+
+
+
+ Nested settings maps containing the connection for which
+ secrets are being requested.
+
+
+
+
+ Object path of the connection for which secrets are being
+ requested.
+
+
+
+
+ Setting name for which secrets are being requested.
+
+
+
+
+ Array of strings of key names in the requested setting for
+ which NetworkManager thinks a secrets may be required. The
+ Agent should return any secrets it has, or that it thinks
+ are required, regardless of what hints NetworkManager sends
+ in this request.
+
+
+
+
+ If true, new secrets are assumed to be invalid or incorrect,
+ and the agent should ask the user for new secrets. If false,
+ existing secrets should be retrieved from storage and
+ returned without interrupting the user.
+
+
+
+
+
+ Nested settings maps containing secrets. Each setting MUST
+ contain at least the 'name' field, containing the name of
+ the setting, and one or more secrets.
+
+
+
+
+
+
+ Save given secrets to backing storage.
+
+
+
+
+
+ Nested settings maps containing the entire connection
+ (including secrets), for which the agent should save the
+ secrets to backing storage.
+
+
+
+
+ Object path of the connection for which the agent should
+ save secrets to backing storage.
+
+
+
+
+
+
+ Delete secrets from backing storage.
+
+
+
+
+
+ Nested settings maps containing the entire connection
+ (including secrets), for which the agent should delete the
+ secrets from backing storage.
+
+
+
+
+ Object path of the connection for which the agent should
+ delete secrets from backing storage.
+
+
+
+
+
+
+
diff --git a/libnm-glib/Makefile.am b/libnm-glib/Makefile.am
index aa6f95960..5a6045ebc 100644
--- a/libnm-glib/Makefile.am
+++ b/libnm-glib/Makefile.am
@@ -18,7 +18,8 @@ BUILT_SOURCES = \
nm-ip4-config-bindings.h \
nm-dhcp4-config-bindings.h \
nm-ip6-config-bindings.h \
- nm-dhcp6-config-bindings.h
+ nm-dhcp6-config-bindings.h \
+ nm-secret-agent-glue.h
#####################################################
# Deprecated original libnm_glib bits
@@ -76,7 +77,8 @@ libnminclude_HEADERS = \
nm-ip6-config.h \
nm-dhcp6-config.h \
nm-remote-connection.h \
- nm-remote-settings.h
+ nm-remote-settings.h \
+ nm-secret-agent.h
libnm_glib_la_SOURCES = \
nm-object.c \
@@ -105,7 +107,8 @@ libnm_glib_la_SOURCES = \
nm-dhcp6-config.c \
nm-remote-connection.c \
nm-remote-connection-private.h \
- nm-remote-settings.c
+ nm-remote-settings.c \
+ nm-secret-agent.c
libnm_glib_la_LIBADD = \
$(top_builddir)/libnm-util/libnm-util.la \
@@ -178,6 +181,9 @@ nm-ip6-config-bindings.h: $(top_srcdir)/introspection/nm-ip6-config.xml
nm-dhcp6-config-bindings.h: $(top_srcdir)/introspection/nm-dhcp6-config.xml
$(AM_V_GEN) dbus-binding-tool --prefix=nm_dhcp6_config --mode=glib-client --output=$@ $<
+nm-secret-agent-glue.h: $(top_srcdir)/introspection/nm-secret-agent.xml
+ $(AM_V_GEN) dbus-binding-tool --prefix=nm_secret_agent --mode=glib-server --output=$@ $<
+
pkgconfigdir = $(libdir)/pkgconfig
pkgconfig_DATA = libnm-glib.pc libnm-glib-vpn.pc
diff --git a/libnm-glib/nm-secret-agent.c b/libnm-glib/nm-secret-agent.c
new file mode 100644
index 000000000..68d6af9cf
--- /dev/null
+++ b/libnm-glib/nm-secret-agent.c
@@ -0,0 +1,638 @@
+/* -*- 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.
+ *
+ * (C) Copyright 2010 Red Hat, Inc.
+ */
+
+#include
+#include
+#include
+#include
+#include
+
+#include "nm-secret-agent.h"
+#include "nm-marshal.h"
+#include "NetworkManager.h"
+
+static void impl_secret_agent_get_secrets (NMSecretAgent *self,
+ GHashTable *connection_hash,
+ const char *connection_path,
+ const char *setting_name,
+ const char **hints,
+ gboolean request_new,
+ DBusGMethodInvocation *context);
+
+static void impl_secret_agent_save_secrets (NMSecretAgent *self,
+ GHashTable *connection_hash,
+ const char *connection_path,
+ DBusGMethodInvocation *context);
+
+static void impl_secret_agent_delete_secrets (NMSecretAgent *self,
+ GHashTable *connection_hash,
+ const char *connection_path,
+ DBusGMethodInvocation *context);
+
+#include "nm-secret-agent-glue.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 registered;
+
+ DBusGConnection *bus;
+ DBusGProxy *dbus_proxy;
+ DBusGProxy *manager_proxy;
+ DBusGProxyCall *reg_call;
+
+ char *nm_owner;
+
+ char *identifier;
+
+ gboolean disposed;
+} NMSecretAgentPrivate;
+
+enum {
+ PROP_0,
+ PROP_IDENTIFIER,
+
+ LAST_PROP
+};
+
+enum {
+ REGISTRATION_RESULT,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+
+/********************************************************************/
+
+GQuark
+nm_secret_agent_error_quark (void)
+{
+ static GQuark ret = 0;
+
+ if (G_UNLIKELY (ret == 0))
+ ret = g_quark_from_static_string ("nm-secret-agent-error");
+ return ret;
+}
+
+#define ENUM_ENTRY(NAME, DESC) { NAME, "" #NAME "", DESC }
+
+GType
+nm_secret_agent_error_get_type (void)
+{
+ static GType etype = 0;
+
+ if (etype == 0) {
+ static const GEnumValue values[] = {
+ /* Sender is not authorized to make this request */
+ ENUM_ENTRY (NM_SECRET_AGENT_ERROR_NOT_AUTHORIZED, "NotAuthorized"),
+ /* Given connection details do not make a valid connection */
+ ENUM_ENTRY (NM_SECRET_AGENT_ERROR_INVALID_CONNECTION, "InvalidConnection"),
+ /* The request was canceled explicitly by the user */
+ ENUM_ENTRY (NM_SECRET_AGENT_ERROR_USER_CANCELED, "UserCanceled"),
+ /* The request was canceled, but not by the user */
+ ENUM_ENTRY (NM_SECRET_AGENT_ERROR_AGENT_CANCELED, "AgentCanceled"),
+ { 0, 0, 0 }
+ };
+ etype = g_enum_register_static ("NMSecretAgentError", values);
+ }
+ return etype;
+}
+
+/*************************************************************/
+
+static const char *
+get_nm_owner (NMSecretAgent *self)
+{
+ NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
+ GError *error = NULL;
+ char *owner;
+
+ if (!priv->nm_owner) {
+ if (!dbus_g_proxy_call_with_timeout (priv->dbus_proxy,
+ "GetNameOwner", 2000, &error,
+ G_TYPE_STRING, NM_DBUS_SERVICE,
+ G_TYPE_INVALID,
+ G_TYPE_STRING, &owner,
+ G_TYPE_INVALID))
+ return NULL;
+
+ priv->nm_owner = g_strdup (owner);
+ g_free (owner);
+ }
+
+ return priv->nm_owner;
+}
+
+static void
+name_owner_changed (DBusGProxy *proxy,
+ const char *name,
+ const char *old_owner,
+ const char *new_owner,
+ gpointer user_data)
+{
+ NMSecretAgent *self = NM_SECRET_AGENT (user_data);
+ NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
+
+ if (strcmp (name, NM_DBUS_SERVICE) == 0) {
+ g_free (priv->nm_owner);
+ priv->nm_owner = g_strdup (new_owner);
+ }
+}
+
+static NMConnection *
+verify_request (NMSecretAgent *self,
+ DBusGMethodInvocation *context,
+ GHashTable *connection_hash,
+ GError **error)
+{
+ NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
+ NMConnection *connection = NULL;
+ DBusConnection *bus;
+ char *sender;
+ const char *nm_owner;
+ DBusError dbus_error;
+ uid_t sender_uid = G_MAXUINT;
+ GError *local = NULL;
+
+ g_return_val_if_fail (context != NULL, NULL);
+ g_return_val_if_fail (connection_hash != NULL, NULL);
+
+ /* Verify the sender's UID is 0, and that the sender is the same as
+ * NetworkManager's bus name owner.
+ */
+
+ nm_owner = get_nm_owner (self);
+ if (!nm_owner) {
+ g_set_error_literal (error,
+ NM_SECRET_AGENT_ERROR,
+ NM_SECRET_AGENT_ERROR_NOT_AUTHORIZED,
+ "NetworkManager bus name owner unknown.");
+ return FALSE;
+ }
+
+ bus = dbus_g_connection_get_connection (priv->bus);
+ if (!bus) {
+ g_set_error_literal (error,
+ NM_SECRET_AGENT_ERROR,
+ NM_SECRET_AGENT_ERROR_NOT_AUTHORIZED,
+ "Failed to get DBus connection.");
+ return FALSE;
+ }
+
+ sender = dbus_g_method_get_sender (context);
+ if (!sender) {
+ g_set_error_literal (error,
+ NM_SECRET_AGENT_ERROR,
+ NM_SECRET_AGENT_ERROR_NOT_AUTHORIZED,
+ "Failed to get request sender.");
+ 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_NOT_AUTHORIZED,
+ "Request sender does not match NetworkManager bus name owner.");
+ goto out;
+ }
+
+ dbus_error_init (&dbus_error);
+ sender_uid = dbus_bus_get_unix_user (bus, sender, &dbus_error);
+ if (dbus_error_is_set (&dbus_error)) {
+ g_set_error (error,
+ NM_SECRET_AGENT_ERROR,
+ NM_SECRET_AGENT_ERROR_NOT_AUTHORIZED,
+ "Failed to get request unix user: (%s) %s.",
+ dbus_error.name, dbus_error.message);
+ dbus_error_free (&dbus_error);
+ goto out;
+ }
+
+ if (0 != sender_uid) {
+ g_set_error_literal (error,
+ NM_SECRET_AGENT_ERROR,
+ NM_SECRET_AGENT_ERROR_NOT_AUTHORIZED,
+ "Request sender is not root.");
+ goto out;
+ }
+
+ /* And make sure the connection is actually valid */
+ connection = nm_connection_new_from_hash (connection_hash, &local);
+ if (!connection) {
+ g_set_error (error,
+ NM_SECRET_AGENT_ERROR,
+ NM_SECRET_AGENT_ERROR_INVALID_CONNECTION,
+ "Invalid connection: (%d) %s",
+ local ? local->code : -1,
+ (local && local->message) ? local->message : "(unknown)");
+ g_clear_error (&local);
+ }
+
+
+out:
+ g_free (sender);
+ return connection;
+}
+
+static void
+impl_secret_agent_get_secrets (NMSecretAgent *self,
+ GHashTable *connection_hash,
+ const char *connection_path,
+ const char *setting_name,
+ const char **hints,
+ gboolean request_new,
+ DBusGMethodInvocation *context)
+{
+ GError *error = NULL;
+ NMConnection *connection;
+
+ /* Make sure the request comes from NetworkManager and is valid */
+ connection = verify_request (self, context, connection_hash, &error);
+ if (!connection) {
+ dbus_g_method_return_error (context, error);
+ g_clear_error (&error);
+ return;
+ }
+
+ NM_SECRET_AGENT_GET_CLASS (self)->get_secrets (self,
+ connection,
+ connection_path,
+ setting_name,
+ hints,
+ request_new,
+ context);
+ g_object_unref (connection);
+}
+
+static void
+impl_secret_agent_save_secrets (NMSecretAgent *self,
+ GHashTable *connection_hash,
+ const char *connection_path,
+ DBusGMethodInvocation *context)
+{
+ GError *error = NULL;
+ NMConnection *connection;
+
+ /* Make sure the request comes from NetworkManager and is valid */
+ connection = verify_request (self, context, connection_hash, &error);
+ if (!connection) {
+ dbus_g_method_return_error (context, error);
+ g_clear_error (&error);
+ return;
+ }
+
+ NM_SECRET_AGENT_GET_CLASS (self)->save_secrets (self,
+ connection,
+ connection_path,
+ context);
+ g_object_unref (connection);
+}
+
+static void
+impl_secret_agent_delete_secrets (NMSecretAgent *self,
+ GHashTable *connection_hash,
+ const char *connection_path,
+ DBusGMethodInvocation *context)
+{
+ GError *error = NULL;
+ NMConnection *connection;
+
+ /* Make sure the request comes from NetworkManager and is valid */
+ connection = verify_request (self, context, connection_hash, &error);
+ if (!connection) {
+ dbus_g_method_return_error (context, error);
+ g_clear_error (&error);
+ return;
+ }
+
+ NM_SECRET_AGENT_GET_CLASS (self)->delete_secrets (self,
+ connection,
+ connection_path,
+ context);
+ g_object_unref (connection);
+}
+
+/**************************************************************/
+
+static void
+reg_request_cb (DBusGProxy *proxy,
+ DBusGProxyCall *call,
+ gpointer user_data)
+{
+ NMSecretAgent *self = NM_SECRET_AGENT (user_data);
+ NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
+ GError *error = NULL;
+
+ priv->reg_call = NULL;
+
+ if (dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID))
+ priv->registered = TRUE;
+ else {
+ /* If registration failed we shouldn't expose ourselves on the bus */
+ dbus_g_connection_unregister_g_object (priv->bus, G_OBJECT (self));
+ }
+
+ g_signal_emit (self, signals[REGISTRATION_RESULT], 0, error);
+ g_clear_error (&error);
+}
+
+/**
+ * nm_secret_agent_register:
+ *
+ * Registers the #NMSecretAgent 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. Registration is an
+ * asynchronous operation and its success or failure is indicated via the
+ * 'registration-result' signal.
+ *
+ * Returns: a new %TRUE if registration was successfully requested (this does
+ * not mean registration itself was successful), %FALSE if registration was not
+ * successfully requested.
+ **/
+gboolean
+nm_secret_agent_register (NMSecretAgent *self)
+{
+ NMSecretAgentPrivate *priv;
+ NMSecretAgentClass *class;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (NM_IS_SECRET_AGENT (self), FALSE);
+
+ priv = NM_SECRET_AGENT_GET_PRIVATE (self);
+
+ g_return_val_if_fail (priv->registered == FALSE, FALSE);
+ g_return_val_if_fail (priv->reg_call == NULL, 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_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);
+
+ /* Export our secret agent interface before registering with the manager */
+ dbus_g_connection_register_g_object (priv->bus,
+ NM_DBUS_PATH_SECRET_AGENT,
+ G_OBJECT (self));
+
+ priv->reg_call = dbus_g_proxy_begin_call_with_timeout (priv->manager_proxy,
+ "Register",
+ reg_request_cb,
+ self,
+ NULL,
+ G_USEC_PER_SEC * 5,
+ G_TYPE_STRING, priv->identifier,
+ G_TYPE_INVALID);
+
+ return TRUE;
+}
+
+/**
+ * nm_secret_agent_unregister:
+ *
+ * Unregisters the #NMSecretAgent with the NetworkManager secret manager,
+ * indicating to NetworkManager that the agent is will no longer provide or
+ * store secrets on behalf of this user.
+ *
+ * Returns: a new %TRUE if unregistration was successful, %FALSE if it was not.
+ **/
+gboolean
+nm_secret_agent_unregister (NMSecretAgent *self)
+{
+ NMSecretAgentPrivate *priv;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (NM_IS_SECRET_AGENT (self), FALSE);
+
+ priv = NM_SECRET_AGENT_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);
+
+ dbus_g_proxy_call_no_reply (priv->manager_proxy, "Unregister", G_TYPE_INVALID);
+
+ return TRUE;
+}
+
+/**************************************************************/
+
+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 (!isalnum (*p) && (*p != '_') && (*p != '-'))
+ return FALSE;
+ if ((*p == '.') && (*(p + 1) == '.'))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static void
+nm_secret_agent_init (NMSecretAgent *self)
+{
+ NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
+ GError *error = NULL;
+
+ priv->bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
+ if (!priv->bus) {
+ g_warning ("Couldn't connect to system bus: %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ priv->dbus_proxy = dbus_g_proxy_new_for_name (priv->bus,
+ DBUS_SERVICE_DBUS,
+ DBUS_PATH_DBUS,
+ DBUS_INTERFACE_DBUS);
+ if (!priv->dbus_proxy) {
+ g_warning ("Couldn't create messagebus proxy.");
+ return;
+ }
+
+ dbus_g_object_register_marshaller (_nm_marshal_VOID__STRING_STRING_STRING,
+ G_TYPE_NONE,
+ G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_INVALID);
+ dbus_g_proxy_add_signal (priv->dbus_proxy, "NameOwnerChanged",
+ G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal (priv->dbus_proxy,
+ "NameOwnerChanged",
+ G_CALLBACK (name_owner_changed),
+ self, NULL);
+
+ priv->manager_proxy = dbus_g_proxy_new_for_name (priv->bus,
+ NM_DBUS_SERVICE,
+ NM_DBUS_PATH_AGENT_MANAGER,
+ NM_DBUS_INTERFACE_AGENT_MANAGER);
+ if (!priv->manager_proxy) {
+ g_warning ("Couldn't create NM agent manager proxy.");
+ return;
+ }
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_IDENTIFIER:
+ g_value_set_string (value, priv->identifier);
+ 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)
+{
+ NMSecretAgentPrivate *priv = NM_SECRET_AGENT_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;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+dispose (GObject *object)
+{
+ NMSecretAgent *self = NM_SECRET_AGENT (object);
+ NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
+
+ if (!priv->disposed) {
+ priv->disposed = TRUE;
+
+ if (priv->registered)
+ nm_secret_agent_unregister (self);
+
+ g_free (priv->identifier);
+ g_free (priv->nm_owner);
+
+ if (priv->dbus_proxy)
+ g_object_unref (priv->dbus_proxy);
+
+ if (priv->manager_proxy)
+ g_object_unref (priv->manager_proxy);
+
+ if (priv->bus)
+ dbus_g_connection_unref (priv->bus);
+ }
+
+ G_OBJECT_CLASS (nm_secret_agent_parent_class)->dispose (object);
+}
+
+static void
+nm_secret_agent_class_init (NMSecretAgentClass *class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (class);
+
+ g_type_class_add_private (class, sizeof (NMSecretAgentPrivate));
+
+ /* Virtual methods */
+ object_class->dispose = dispose;
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+
+ /**
+ * NMSecretAgent: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_IDENTIFIER,
+ "Identifier",
+ "Identifier",
+ NULL,
+ G_PARAM_READABLE | G_PARAM_CONSTRUCT_ONLY));
+
+ /**
+ * NMSecretAgent::registration-result:
+ * @agent: the agent that received the signal
+ * @error: the error, if any, that occured while registering
+ *
+ * Indicates the result of a registration request; if @error is NULL the
+ * request was successful.
+ **/
+ signals[REGISTRATION_RESULT] =
+ g_signal_new (REGISTRATION_RESULT,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+ dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (class),
+ &dbus_glib_nm_secret_agent_object_info);
+
+ dbus_g_error_domain_register (NM_SECRET_AGENT_ERROR,
+ NM_DBUS_INTERFACE_SECRET_AGENT,
+ NM_TYPE_SECRET_AGENT_ERROR);
+}
+
diff --git a/libnm-glib/nm-secret-agent.h b/libnm-glib/nm-secret-agent.h
new file mode 100644
index 000000000..b9f0fd727
--- /dev/null
+++ b/libnm-glib/nm-secret-agent.h
@@ -0,0 +1,119 @@
+/* -*- 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.
+ *
+ * (C) Copyright 2010 Red Hat, Inc.
+ */
+
+#ifndef NM_SECRET_AGENT_H
+#define NM_SECRET_AGENT_H
+
+#include
+#include
+#include
+
+G_BEGIN_DECLS
+
+#define NM_SECRET_AGENT_ERROR (nm_secret_agent_error_quark ())
+#define NM_TYPE_SECRET_AGENT_ERROR (nm_secret_agent_error_get_type ())
+
+GQuark nm_secret_agent_error_quark (void);
+GType nm_secret_agent_error_get_type (void);
+
+typedef enum {
+ NM_SECRET_AGENT_ERROR_NOT_AUTHORIZED = 0,
+ NM_SECRET_AGENT_ERROR_INVALID_CONNECTION,
+ NM_SECRET_AGENT_ERROR_USER_CANCELED,
+ NM_SECRET_AGENT_ERROR_AGENT_CANCELED
+} NMSecretAgentError;
+
+
+#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))
+
+#define NM_SECRET_AGENT_IDENTIFIER "identifier"
+
+#define NM_SECRET_AGENT_REGISTRATION_RESULT "registration-result"
+
+typedef struct {
+ GObject parent;
+} NMSecretAgent;
+
+typedef struct {
+ GObjectClass parent;
+
+ /* Virtual methods for subclasses */
+
+ /* Called when the subclass should retrieve and return secrets. Subclass
+ * must copy or reference any arguments it may require after returning from
+ * this method, as the arguments will freed (except for 'agent' and
+ * 'context' of course).
+ */
+ void (*get_secrets) (NMSecretAgent *agent,
+ NMConnection *connection,
+ const char *connection_path,
+ const char *setting_name,
+ const char **hints,
+ gboolean request_new,
+ DBusGMethodInvocation *context);
+
+ /* Called when the subclass should save the secrets contained in the
+ * connection to backing storage. Subclass must copy or reference any
+ * arguments it may require after returning from this method, as the
+ * arguments will freed (except for 'agent' and 'context' of course).
+ */
+ void (*save_secrets) (NMSecretAgent *agent,
+ NMConnection *connection,
+ const char *connection_path,
+ DBusGMethodInvocation *context);
+
+ /* Called when the subclass should delete the secrets contained in the
+ * connection from backing storage. Subclass must copy or reference any
+ * arguments it may require after returning from this method, as the
+ * arguments will freed (except for 'agent' and 'context' of course).
+ */
+ void (*delete_secrets) (NMSecretAgent *agent,
+ NMConnection *connection,
+ const char *connection_path,
+ DBusGMethodInvocation *context);
+
+ /* Signals */
+ void (*registration_result) (NMSecretAgent *agent, GError *error);
+
+ /* Padding for future expansion */
+ void (*_reserved1) (void);
+ void (*_reserved2) (void);
+ void (*_reserved3) (void);
+ void (*_reserved4) (void);
+ void (*_reserved5) (void);
+ void (*_reserved6) (void);
+} NMSecretAgentClass;
+
+GType nm_secret_agent_get_type (void);
+
+NMSecretAgent *nm_secret_agent_new (const char *identifier);
+
+gboolean nm_secret_agent_register (NMSecretAgent *self);
+
+gboolean nm_secret_agent_unregister (NMSecretAgent *self);
+
+G_END_DECLS
+
+#endif /* NM_SECRET_AGENT_H */