mirror of
https://gitlab.freedesktop.org/NetworkManager/NetworkManager
synced 2024-10-04 23:31:19 +00:00
996 lines
32 KiB
C
996 lines
32 KiB
C
/* -*- 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 - 2011 Red Hat, Inc.
|
|
*/
|
|
|
|
#include <config.h>
|
|
#include <ctype.h>
|
|
#include <string.h>
|
|
#include <dbus/dbus-glib-lowlevel.h>
|
|
|
|
#include "nm-glib-compat.h"
|
|
|
|
#include <NetworkManager.h>
|
|
|
|
#include "nm-secret-agent.h"
|
|
#include "nm-glib-enum-types.h"
|
|
#include "nm-glib-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,
|
|
guint32 flags,
|
|
DBusGMethodInvocation *context);
|
|
|
|
static void impl_secret_agent_cancel_get_secrets (NMSecretAgent *self,
|
|
const char *connection_path,
|
|
const char *setting_name,
|
|
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_ABSTRACT_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))
|
|
|
|
static gboolean auto_register_cb (gpointer user_data);
|
|
|
|
typedef struct {
|
|
gboolean registered;
|
|
|
|
DBusGConnection *bus;
|
|
DBusGProxy *dbus_proxy;
|
|
DBusGProxy *manager_proxy;
|
|
DBusGProxyCall *reg_call;
|
|
|
|
/* GetSecretsInfo structs of in-flight GetSecrets requests */
|
|
GSList *pending_gets;
|
|
|
|
char *nm_owner;
|
|
|
|
char *identifier;
|
|
gboolean auto_register;
|
|
gboolean suppress_auto;
|
|
gboolean auto_register_id;
|
|
} NMSecretAgentPrivate;
|
|
|
|
enum {
|
|
PROP_0,
|
|
PROP_IDENTIFIER,
|
|
PROP_AUTO_REGISTER,
|
|
PROP_REGISTERED,
|
|
|
|
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;
|
|
}
|
|
|
|
/*************************************************************/
|
|
|
|
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
|
|
_internal_unregister (NMSecretAgent *self)
|
|
{
|
|
NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
|
|
|
|
if (priv->registered) {
|
|
dbus_g_connection_unregister_g_object (priv->bus, G_OBJECT (self));
|
|
priv->registered = FALSE;
|
|
g_object_notify (G_OBJECT (self), NM_SECRET_AGENT_REGISTERED);
|
|
}
|
|
}
|
|
|
|
typedef struct {
|
|
char *path;
|
|
char *setting_name;
|
|
DBusGMethodInvocation *context;
|
|
} GetSecretsInfo;
|
|
|
|
static void
|
|
get_secrets_info_finalize (NMSecretAgent *self, GetSecretsInfo *info)
|
|
{
|
|
NMSecretAgentPrivate *priv = NM_SECRET_AGENT_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 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);
|
|
gboolean old_owner_good = (old_owner && strlen (old_owner));
|
|
gboolean new_owner_good = (new_owner && strlen (new_owner));
|
|
GSList *iter;
|
|
|
|
if (strcmp (name, NM_DBUS_SERVICE) == 0) {
|
|
g_free (priv->nm_owner);
|
|
priv->nm_owner = g_strdup (new_owner);
|
|
|
|
if (!old_owner_good && new_owner_good) {
|
|
/* NM appeared */
|
|
auto_register_cb (self);
|
|
} else if (old_owner_good && !new_owner_good) {
|
|
/* Cancel any pending secrets requests */
|
|
for (iter = priv->pending_gets; iter; iter = g_slist_next (iter)) {
|
|
GetSecretsInfo *info = iter->data;
|
|
|
|
NM_SECRET_AGENT_GET_CLASS (self)->cancel_get_secrets (self,
|
|
info->path,
|
|
info->setting_name);
|
|
}
|
|
g_slist_free (priv->pending_gets);
|
|
priv->pending_gets = NULL;
|
|
|
|
/* NM disappeared */
|
|
_internal_unregister (self);
|
|
} else if (old_owner_good && new_owner_good && strcmp (old_owner, new_owner)) {
|
|
/* Hmm, NM magically restarted */
|
|
_internal_unregister (self);
|
|
auto_register_cb (self);
|
|
}
|
|
}
|
|
}
|
|
|
|
static gboolean
|
|
verify_request (NMSecretAgent *self,
|
|
DBusGMethodInvocation *context,
|
|
GHashTable *connection_hash,
|
|
const char *connection_path,
|
|
NMConnection **out_connection,
|
|
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, FALSE);
|
|
|
|
/* 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 */
|
|
if (connection_hash) {
|
|
connection = nm_connection_new_from_hash (connection_hash, &local);
|
|
if (connection && connection_path) {
|
|
nm_connection_set_path (connection, connection_path);
|
|
} else {
|
|
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:
|
|
if (out_connection)
|
|
*out_connection = connection;
|
|
g_free (sender);
|
|
|
|
return !!connection;
|
|
}
|
|
|
|
static void
|
|
get_secrets_cb (NMSecretAgent *self,
|
|
NMConnection *connection,
|
|
GHashTable *secrets,
|
|
GError *error,
|
|
gpointer user_data)
|
|
{
|
|
GetSecretsInfo *info = user_data;
|
|
|
|
if (error)
|
|
dbus_g_method_return_error (info->context, error);
|
|
else
|
|
dbus_g_method_return (info->context, secrets);
|
|
|
|
/* Remove the request from internal tracking */
|
|
get_secrets_info_finalize (self, info);
|
|
}
|
|
|
|
static void
|
|
impl_secret_agent_get_secrets (NMSecretAgent *self,
|
|
GHashTable *connection_hash,
|
|
const char *connection_path,
|
|
const char *setting_name,
|
|
const char **hints,
|
|
guint32 flags,
|
|
DBusGMethodInvocation *context)
|
|
{
|
|
NMSecretAgentPrivate *priv = NM_SECRET_AGENT_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_hash, connection_path, &connection, &error)) {
|
|
dbus_g_method_return_error (context, error);
|
|
g_clear_error (&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_GET_CLASS (self)->get_secrets (self,
|
|
connection,
|
|
connection_path,
|
|
setting_name,
|
|
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_cancel_get_secrets (NMSecretAgent *self,
|
|
const char *connection_path,
|
|
const char *setting_name,
|
|
DBusGMethodInvocation *context)
|
|
{
|
|
NMSecretAgentPrivate *priv = NM_SECRET_AGENT_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)) {
|
|
dbus_g_method_return_error (context, error);
|
|
g_clear_error (&error);
|
|
return;
|
|
}
|
|
|
|
info = find_get_secrets_info (priv->pending_gets, connection_path, setting_name);
|
|
if (!info) {
|
|
g_set_error_literal (&error,
|
|
NM_SECRET_AGENT_ERROR,
|
|
NM_SECRET_AGENT_ERROR_INTERNAL_ERROR,
|
|
"No secrets request in progress for this connection.");
|
|
dbus_g_method_return_error (context, error);
|
|
g_clear_error (&error);
|
|
return;
|
|
}
|
|
|
|
/* Send the cancel request up to the subclass and finalize it */
|
|
NM_SECRET_AGENT_GET_CLASS (self)->cancel_get_secrets (self,
|
|
info->path,
|
|
info->setting_name);
|
|
dbus_g_method_return (context);
|
|
}
|
|
|
|
static void
|
|
save_secrets_cb (NMSecretAgent *self,
|
|
NMConnection *connection,
|
|
GError *error,
|
|
gpointer user_data)
|
|
{
|
|
DBusGMethodInvocation *context = user_data;
|
|
|
|
if (error)
|
|
dbus_g_method_return_error (context, error);
|
|
else
|
|
dbus_g_method_return (context);
|
|
}
|
|
|
|
static void
|
|
impl_secret_agent_save_secrets (NMSecretAgent *self,
|
|
GHashTable *connection_hash,
|
|
const char *connection_path,
|
|
DBusGMethodInvocation *context)
|
|
{
|
|
GError *error = NULL;
|
|
NMConnection *connection = NULL;
|
|
|
|
/* Make sure the request comes from NetworkManager and is valid */
|
|
if (!verify_request (self, context, connection_hash, connection_path, &connection, &error)) {
|
|
dbus_g_method_return_error (context, error);
|
|
g_clear_error (&error);
|
|
return;
|
|
}
|
|
|
|
NM_SECRET_AGENT_GET_CLASS (self)->save_secrets (self,
|
|
connection,
|
|
connection_path,
|
|
save_secrets_cb,
|
|
context);
|
|
g_object_unref (connection);
|
|
}
|
|
|
|
static void
|
|
delete_secrets_cb (NMSecretAgent *self,
|
|
NMConnection *connection,
|
|
GError *error,
|
|
gpointer user_data)
|
|
{
|
|
DBusGMethodInvocation *context = user_data;
|
|
|
|
if (error)
|
|
dbus_g_method_return_error (context, error);
|
|
else
|
|
dbus_g_method_return (context);
|
|
}
|
|
|
|
static void
|
|
impl_secret_agent_delete_secrets (NMSecretAgent *self,
|
|
GHashTable *connection_hash,
|
|
const char *connection_path,
|
|
DBusGMethodInvocation *context)
|
|
{
|
|
GError *error = NULL;
|
|
NMConnection *connection = NULL;
|
|
|
|
/* Make sure the request comes from NetworkManager and is valid */
|
|
if (!verify_request (self, context, connection_hash, connection_path, &connection, &error)) {
|
|
dbus_g_method_return_error (context, error);
|
|
g_clear_error (&error);
|
|
return;
|
|
}
|
|
|
|
NM_SECRET_AGENT_GET_CLASS (self)->delete_secrets (self,
|
|
connection,
|
|
connection_path,
|
|
delete_secrets_cb,
|
|
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;
|
|
g_object_notify (G_OBJECT (self), NM_SECRET_AGENT_REGISTERED);
|
|
} else {
|
|
/* If registration failed we shouldn't expose ourselves on the bus */
|
|
_internal_unregister (self);
|
|
}
|
|
|
|
g_signal_emit (self, signals[REGISTRATION_RESULT], 0, error);
|
|
g_clear_error (&error);
|
|
}
|
|
|
|
/**
|
|
* nm_secret_agent_register:
|
|
* @self: a #NMSecretAgent
|
|
*
|
|
* 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);
|
|
|
|
priv->suppress_auto = 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,
|
|
5000,
|
|
G_TYPE_STRING, priv->identifier,
|
|
G_TYPE_INVALID);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* nm_secret_agent_unregister:
|
|
* @self: a #NMSecretAgent
|
|
*
|
|
* 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);
|
|
|
|
_internal_unregister (self);
|
|
priv->suppress_auto = TRUE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* nm_secret_agent_get_registered:
|
|
* @self: a #NMSecretAgent
|
|
*
|
|
* Returns: a %TRUE if the agent is registered, %FALSE if it is not.
|
|
**/
|
|
gboolean
|
|
nm_secret_agent_get_registered (NMSecretAgent *self)
|
|
{
|
|
g_return_val_if_fail (self != NULL, FALSE);
|
|
g_return_val_if_fail (NM_IS_SECRET_AGENT (self), FALSE);
|
|
|
|
return NM_SECRET_AGENT_GET_PRIVATE (self)->registered;
|
|
}
|
|
|
|
static gboolean
|
|
auto_register_cb (gpointer user_data)
|
|
{
|
|
NMSecretAgent *self = NM_SECRET_AGENT (user_data);
|
|
NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE (self);
|
|
|
|
priv->auto_register_id = 0;
|
|
if (priv->auto_register && !priv->suppress_auto && (priv->reg_call == NULL))
|
|
nm_secret_agent_register (self);
|
|
return FALSE;
|
|
}
|
|
|
|
/**************************************************************/
|
|
|
|
/**
|
|
* nm_secret_agent_get_secrets:
|
|
* @self: a #NMSecretAgent
|
|
* @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
|
|
*
|
|
* Asyncronously retrieve 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.
|
|
*
|
|
* VFunc: get_secrets
|
|
*/
|
|
void
|
|
nm_secret_agent_get_secrets (NMSecretAgent *self,
|
|
NMConnection *connection,
|
|
const char *setting_name,
|
|
const char **hints,
|
|
NMSecretAgentGetSecretsFlags flags,
|
|
NMSecretAgentGetSecretsFunc callback,
|
|
gpointer user_data)
|
|
{
|
|
g_return_if_fail (self != NULL);
|
|
g_return_if_fail (NM_IS_SECRET_AGENT (self));
|
|
g_return_if_fail (connection != NULL);
|
|
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 (callback != NULL);
|
|
|
|
NM_SECRET_AGENT_GET_CLASS (self)->get_secrets (self,
|
|
connection,
|
|
nm_connection_get_path (connection),
|
|
setting_name,
|
|
hints,
|
|
flags,
|
|
callback,
|
|
user_data);
|
|
}
|
|
|
|
/**
|
|
* nm_secret_agent_save_secrets:
|
|
* @self: a #NMSecretAgent
|
|
* @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
|
|
*
|
|
* Asyncronously ensure that all secrets inside @connection
|
|
* are stored to disk.
|
|
*
|
|
* VFunc: save_secrets
|
|
*/
|
|
void
|
|
nm_secret_agent_save_secrets (NMSecretAgent *self,
|
|
NMConnection *connection,
|
|
NMSecretAgentSaveSecretsFunc callback,
|
|
gpointer user_data)
|
|
{
|
|
g_return_if_fail (self != NULL);
|
|
g_return_if_fail (NM_IS_SECRET_AGENT (self));
|
|
g_return_if_fail (connection != NULL);
|
|
g_return_if_fail (NM_IS_CONNECTION (connection));
|
|
g_return_if_fail (nm_connection_get_path (connection));
|
|
|
|
NM_SECRET_AGENT_GET_CLASS (self)->save_secrets (self,
|
|
connection,
|
|
nm_connection_get_path (connection),
|
|
callback,
|
|
user_data);
|
|
}
|
|
|
|
/**
|
|
* nm_secret_agent_delete_secrets:
|
|
* @self: a #NMSecretAgent
|
|
* @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 ask the agent to delete all saved secrets belonging to
|
|
* @connection.
|
|
*
|
|
* VFunc: delete_secrets
|
|
*/
|
|
void
|
|
nm_secret_agent_delete_secrets (NMSecretAgent *self,
|
|
NMConnection *connection,
|
|
NMSecretAgentDeleteSecretsFunc callback,
|
|
gpointer user_data)
|
|
{
|
|
g_return_if_fail (self != NULL);
|
|
g_return_if_fail (NM_IS_SECRET_AGENT (self));
|
|
g_return_if_fail (connection != NULL);
|
|
g_return_if_fail (NM_IS_CONNECTION (connection));
|
|
g_return_if_fail (nm_connection_get_path (connection));
|
|
|
|
NM_SECRET_AGENT_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 (!isalnum (*p) && (*p != '_') && (*p != '-') && (*p != '.'))
|
|
return FALSE;
|
|
if ((*p == '.') && (*(p + 1) == '.'))
|
|
return FALSE;
|
|
p++;
|
|
}
|
|
|
|
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_glib_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;
|
|
}
|
|
|
|
priv->auto_register_id = g_idle_add (auto_register_cb, self);
|
|
}
|
|
|
|
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;
|
|
case PROP_AUTO_REGISTER:
|
|
g_value_set_boolean (value, priv->auto_register);
|
|
break;
|
|
case PROP_REGISTERED:
|
|
g_value_set_boolean (value, priv->registered);
|
|
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;
|
|
case PROP_AUTO_REGISTER:
|
|
priv->auto_register = g_value_get_boolean (value);
|
|
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->registered)
|
|
nm_secret_agent_unregister (self);
|
|
|
|
if (priv->auto_register_id) {
|
|
g_source_remove (priv->auto_register_id);
|
|
priv->auto_register_id = 0;
|
|
}
|
|
|
|
g_free (priv->identifier);
|
|
priv->identifier = NULL;
|
|
g_free (priv->nm_owner);
|
|
priv->nm_owner = NULL;
|
|
|
|
while (priv->pending_gets)
|
|
get_secrets_info_finalize (self, priv->pending_gets->data);
|
|
|
|
g_clear_object (&priv->dbus_proxy);
|
|
g_clear_object (&priv->manager_proxy);
|
|
|
|
if (priv->bus) {
|
|
dbus_g_connection_unref (priv->bus);
|
|
priv->bus = NULL;
|
|
}
|
|
|
|
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_READWRITE | G_PARAM_CONSTRUCT_ONLY));
|
|
|
|
/**
|
|
* NMSecretAgent:auto-register:
|
|
*
|
|
* If TRUE, the agent will attempt to automatically register itself after
|
|
* it is created (via an idle handler) and to re-register itself if
|
|
* NetworkManager restarts. If FALSE, the agent does not automatically
|
|
* register with NetworkManager, and nm_secret_agent_register() must be
|
|
* called. If 'auto-register' is TRUE, calling nm_secret_agent_unregister()
|
|
* will suppress auto-registration until nm_secret_agent_register() is
|
|
* called, which re-enables auto-registration.
|
|
**/
|
|
g_object_class_install_property
|
|
(object_class, PROP_AUTO_REGISTER,
|
|
g_param_spec_boolean (NM_SECRET_AGENT_AUTO_REGISTER,
|
|
"Auto Register",
|
|
"Auto Register",
|
|
TRUE,
|
|
G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
|
|
|
|
/**
|
|
* NMSecretAgent: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_REGISTERED,
|
|
"Registered",
|
|
"Registered",
|
|
FALSE,
|
|
G_PARAM_READABLE));
|
|
|
|
/**
|
|
* 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 (NM_SECRET_AGENT_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);
|
|
}
|
|
|