diff options
Diffstat (limited to 'src/core/settings/nm-secret-agent.c')
| -rw-r--r-- | src/core/settings/nm-secret-agent.c | 808 |
1 files changed, 808 insertions, 0 deletions
diff --git a/src/core/settings/nm-secret-agent.c b/src/core/settings/nm-secret-agent.c new file mode 100644 index 00000000..7e7c5d7d --- /dev/null +++ b/src/core/settings/nm-secret-agent.c @@ -0,0 +1,808 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2010 - 2011 Red Hat, Inc. + */ + +#include "src/core/nm-default-daemon.h" + +#include "nm-secret-agent.h" + +#include <sys/types.h> +#include <pwd.h> + +#include "nm-glib-aux/nm-c-list.h" +#include "nm-glib-aux/nm-dbus-aux.h" +#include "nm-dbus-interface.h" +#include "nm-core-internal.h" +#include "nm-libnm-core-intern/nm-auth-subject.h" +#include "nm-simple-connection.h" +#include "NetworkManagerUtils.h" +#include "c-list/src/c-list.h" + +/*****************************************************************************/ + +#define METHOD_GET_SECRETS "GetSecrets" +#define METHOD_CANCEL_GET_SECRETS "CancelGetSecrets" +#define METHOD_SAVE_SECRETS "SaveSecrets" +#define METHOD_DELETE_SECRETS "DeleteSecrets" + +enum { + DISCONNECTED, + + LAST_SIGNAL +}; + +static guint signals[LAST_SIGNAL] = {0}; + +typedef struct _NMSecretAgentPrivate { + CList permissions; + CList requests; + GDBusConnection * dbus_connection; + char * description; + NMAuthSubject * subject; + char * identifier; + char * owner_username; + char * dbus_owner; + GCancellable * name_owner_cancellable; + guint name_owner_changed_id; + NMSecretAgentCapabilities capabilities; + bool shutdown_wait_obj_registered : 1; +} NMSecretAgentPrivate; + +struct _NMSecretAgentClass { + GObjectClass parent; +}; + +G_DEFINE_TYPE(NMSecretAgent, nm_secret_agent, G_TYPE_OBJECT) + +#define NM_SECRET_AGENT_GET_PRIVATE(self) \ + _NM_GET_PRIVATE_PTR(self, NMSecretAgent, NM_IS_SECRET_AGENT) + +/*****************************************************************************/ + +#define _NMLOG_PREFIX_NAME "secret-agent" +#define _NMLOG_DOMAIN LOGD_AGENTS +#define _NMLOG(level, ...) \ + G_STMT_START \ + { \ + if (nm_logging_enabled((level), (_NMLOG_DOMAIN))) { \ + char _prefix[64]; \ + \ + if ((self)) { \ + g_snprintf(_prefix, \ + sizeof(_prefix), \ + _NMLOG_PREFIX_NAME "[" NM_HASH_OBFUSCATE_PTR_FMT "]", \ + NM_HASH_OBFUSCATE_PTR(self)); \ + } else \ + g_strlcpy(_prefix, _NMLOG_PREFIX_NAME, sizeof(_prefix)); \ + \ + _nm_log((level), \ + (_NMLOG_DOMAIN), \ + 0, \ + NULL, \ + NULL, \ + "%s: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + _prefix _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ + } \ + G_STMT_END + +#define _NMLOG2(level, call_id, ...) \ + G_STMT_START \ + { \ + NMSecretAgentCallId *const _call_id = (call_id); \ + \ + nm_assert(_call_id); \ + \ + nm_log((level), \ + (_NMLOG_DOMAIN), \ + NULL, \ + NULL, \ + "%s[" NM_HASH_OBFUSCATE_PTR_FMT "] request [" NM_HASH_OBFUSCATE_PTR_FMT \ + ",%s,%s%s%s%s]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ + _NMLOG_PREFIX_NAME, \ + NM_HASH_OBFUSCATE_PTR(_call_id->self), \ + NM_HASH_OBFUSCATE_PTR(_call_id), \ + _call_id->method_name, \ + NM_PRINT_FMT_QUOTE_STRING(_call_id->path), \ + (_call_id->cancellable ? "" : " (cancelled)") _NM_UTILS_MACRO_REST(__VA_ARGS__)); \ + } \ + G_STMT_END + +/*****************************************************************************/ + +static NM_UTILS_FLAGS2STR_DEFINE(_capabilities_to_string, + NMSecretAgentCapabilities, + NM_UTILS_FLAGS2STR(NM_SECRET_AGENT_CAPABILITY_NONE, "none"), + NM_UTILS_FLAGS2STR(NM_SECRET_AGENT_CAPABILITY_VPN_HINTS, + "vpn-hints"), ); + +/*****************************************************************************/ + +struct _NMSecretAgentCallId { + CList lst; + NMSecretAgent * self; + GCancellable * cancellable; + char * path; + const char * method_name; + char * setting_name; + NMSecretAgentCallback callback; + gpointer callback_data; +}; + +static NMSecretAgentCallId * +_call_id_new(NMSecretAgent * self, + const char * method_name, /* this must be a static string. */ + const char * path, + const char * setting_name, + NMSecretAgentCallback callback, + gpointer callback_data) +{ + NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE(self); + NMSecretAgentCallId * call_id; + + call_id = g_slice_new(NMSecretAgentCallId); + *call_id = (NMSecretAgentCallId){ + .self = g_object_ref(self), + .path = g_strdup(path), + .setting_name = g_strdup(setting_name), + .method_name = method_name, + .callback = callback, + .callback_data = callback_data, + .cancellable = g_cancellable_new(), + }; + c_list_link_tail(&priv->requests, &call_id->lst); + + _LOG2T(call_id, "new request..."); + + if (!priv->shutdown_wait_obj_registered) { + /* self has async requests (that keep self alive). As long as + * we have pending requests, shutdown is blocked. */ + priv->shutdown_wait_obj_registered = TRUE; + nm_shutdown_wait_obj_register_object(G_OBJECT(self), "secret-agent"); + } + + return call_id; +} + +#define _call_id_new(self, method_name, path, setting_name, callback, callback_data) \ + _call_id_new(self, "" method_name "", path, setting_name, callback, callback_data) + +static void +_call_id_free(NMSecretAgentCallId *call_id) +{ + c_list_unlink_stale(&call_id->lst); + g_free(call_id->path); + g_free(call_id->setting_name); + nm_g_object_unref(call_id->cancellable); + g_object_unref(call_id->self); + nm_g_slice_free(call_id); +} + +static void +_call_id_invoke_callback(NMSecretAgentCallId *call_id, + GVariant * secrets, + GError * error, + gboolean cancelled, + gboolean free_call_id) +{ + gs_free_error GError *error_cancelled = NULL; + + nm_assert(call_id); + nm_assert(!c_list_is_empty(&call_id->lst)); + + c_list_unlink(&call_id->lst); + + if (cancelled) { + nm_assert(!secrets); + nm_assert(!error); + if (call_id->callback) { + nm_utils_error_set_cancelled(&error_cancelled, FALSE, "NMSecretAgent"); + error = error_cancelled; + } + _LOG2T(call_id, "cancelled"); + } else if (error) { + nm_assert(!secrets); + _LOG2T(call_id, "completed with failure: %s", error->message); + } else { + nm_assert(!secrets || g_variant_is_of_type(secrets, G_VARIANT_TYPE("a{sa{sv}}"))); + nm_assert((!!secrets) == nm_streq0(call_id->method_name, METHOD_GET_SECRETS)); + _LOG2T(call_id, "completed successfully"); + } + + if (call_id->callback) + call_id->callback(call_id->self, call_id, secrets, error, call_id->callback_data); + + if (free_call_id) + _call_id_free(call_id); +} + +/*****************************************************************************/ + +static char * +_create_description(const char *dbus_owner, const char *identifier, gulong uid) +{ + return g_strdup_printf("%s/%s/%lu", dbus_owner, identifier, uid); +} + +const char * +nm_secret_agent_get_description(NMSecretAgent *agent) +{ + NMSecretAgentPrivate *priv; + + g_return_val_if_fail(NM_IS_SECRET_AGENT(agent), NULL); + + priv = NM_SECRET_AGENT_GET_PRIVATE(agent); + if (!priv->description) { + priv->description = + _create_description(priv->dbus_owner, + priv->identifier, + nm_auth_subject_get_unix_process_uid(priv->subject)); + } + + return priv->description; +} + +/*****************************************************************************/ + +const char * +nm_secret_agent_get_dbus_owner(NMSecretAgent *agent) +{ + g_return_val_if_fail(NM_IS_SECRET_AGENT(agent), NULL); + + return NM_SECRET_AGENT_GET_PRIVATE(agent)->dbus_owner; +} + +const char * +nm_secret_agent_get_identifier(NMSecretAgent *agent) +{ + g_return_val_if_fail(NM_IS_SECRET_AGENT(agent), NULL); + + return NM_SECRET_AGENT_GET_PRIVATE(agent)->identifier; +} + +gulong +nm_secret_agent_get_owner_uid(NMSecretAgent *agent) +{ + g_return_val_if_fail(NM_IS_SECRET_AGENT(agent), G_MAXULONG); + + return nm_auth_subject_get_unix_process_uid(NM_SECRET_AGENT_GET_PRIVATE(agent)->subject); +} + +const char * +nm_secret_agent_get_owner_username(NMSecretAgent *agent) +{ + g_return_val_if_fail(NM_IS_SECRET_AGENT(agent), NULL); + + return NM_SECRET_AGENT_GET_PRIVATE(agent)->owner_username; +} + +gulong +nm_secret_agent_get_pid(NMSecretAgent *agent) +{ + g_return_val_if_fail(NM_IS_SECRET_AGENT(agent), G_MAXULONG); + + return nm_auth_subject_get_unix_process_pid(NM_SECRET_AGENT_GET_PRIVATE(agent)->subject); +} + +NMSecretAgentCapabilities +nm_secret_agent_get_capabilities(NMSecretAgent *agent) +{ + g_return_val_if_fail(NM_IS_SECRET_AGENT(agent), NM_SECRET_AGENT_CAPABILITY_NONE); + + return NM_SECRET_AGENT_GET_PRIVATE(agent)->capabilities; +} + +NMAuthSubject * +nm_secret_agent_get_subject(NMSecretAgent *agent) +{ + g_return_val_if_fail(NM_IS_SECRET_AGENT(agent), NULL); + + return NM_SECRET_AGENT_GET_PRIVATE(agent)->subject; +} + +/*****************************************************************************/ + +/** + * nm_secret_agent_add_permission: + * @agent: A #NMSecretAgent. + * @permission: The name of the permission + * + * Records whether or not the agent has a given permission. + */ +void +nm_secret_agent_add_permission(NMSecretAgent *agent, const char *permission, gboolean allowed) +{ + NMSecretAgentPrivate *priv; + NMCListElem * elem; + + g_return_if_fail(agent != NULL); + g_return_if_fail(permission != NULL); + + priv = NM_SECRET_AGENT_GET_PRIVATE(agent); + + elem = nm_c_list_elem_find_first(&priv->permissions, p, nm_streq(p, permission)); + + if (elem) { + if (!allowed) + nm_c_list_elem_free_full(elem, g_free); + return; + } + + if (allowed) { + c_list_link_tail(&priv->permissions, &nm_c_list_elem_new_stale(g_strdup(permission))->lst); + } +} + +/** + * nm_secret_agent_has_permission: + * @agent: A #NMSecretAgent. + * @permission: The name of the permission to check for + * + * Returns whether or not the agent has the given permission. + * + * Returns: %TRUE if the agent has the given permission, %FALSE if it does not + * or if the permission was not previous recorded with + * nm_secret_agent_add_permission(). + */ +gboolean +nm_secret_agent_has_permission(NMSecretAgent *agent, const char *permission) +{ + g_return_val_if_fail(agent != NULL, FALSE); + g_return_val_if_fail(permission != NULL, FALSE); + + return !!nm_c_list_elem_find_first(&NM_SECRET_AGENT_GET_PRIVATE(agent)->permissions, + p, + nm_streq(p, permission)); +} + +/*****************************************************************************/ + +static void +_dbus_call_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + NMSecretAgentCallId *call_id; + gs_unref_variant GVariant *ret = NULL; + gs_unref_variant GVariant *secrets = NULL; + gs_free_error GError *error = NULL; + + ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error); + + if (!ret && nm_utils_error_is_cancelled(error)) + return; + + call_id = user_data; + + if (!ret) + g_dbus_error_strip_remote_error(error); + else { + if (nm_streq(call_id->method_name, METHOD_GET_SECRETS)) { + g_variant_get(ret, "(@a{sa{sv}})", &secrets); + } + } + + _call_id_invoke_callback(call_id, secrets, error, FALSE, TRUE); +} + +/*****************************************************************************/ + +NMSecretAgentCallId * +nm_secret_agent_get_secrets(NMSecretAgent * self, + const char * path, + NMConnection * connection, + const char * setting_name, + const char ** hints, + NMSecretAgentGetSecretsFlags flags, + NMSecretAgentCallback callback, + gpointer callback_data) +{ + NMSecretAgentPrivate *priv; + GVariant * dict; + NMSecretAgentCallId * call_id; + + g_return_val_if_fail(NM_IS_SECRET_AGENT(self), NULL); + g_return_val_if_fail(NM_IS_CONNECTION(connection), NULL); + g_return_val_if_fail(path && *path, NULL); + g_return_val_if_fail(setting_name, NULL); + g_return_val_if_fail(callback, NULL); + + priv = NM_SECRET_AGENT_GET_PRIVATE(self); + + dict = nm_connection_to_dbus(connection, NM_CONNECTION_SERIALIZE_ALL); + + /* Mask off the private flags if present */ + flags &= ~(NM_SECRET_AGENT_GET_SECRETS_FLAG_ONLY_SYSTEM + | NM_SECRET_AGENT_GET_SECRETS_FLAG_NO_ERRORS); + + call_id = _call_id_new(self, METHOD_GET_SECRETS, path, setting_name, callback, callback_data); + + g_dbus_connection_call(priv->dbus_connection, + priv->dbus_owner, + NM_DBUS_PATH_SECRET_AGENT, + NM_DBUS_INTERFACE_SECRET_AGENT, + call_id->method_name, + g_variant_new("(@a{sa{sv}}os^asu)", + dict, + path, + setting_name, + hints ?: NM_PTRARRAY_EMPTY(const char *), + (guint32) flags), + G_VARIANT_TYPE("(a{sa{sv}})"), + G_DBUS_CALL_FLAGS_NO_AUTO_START, + 120000, + call_id->cancellable, + _dbus_call_cb, + call_id); + + return call_id; +} + +/*****************************************************************************/ + +static void +_call_cancel_cb(GObject *source, GAsyncResult *result, gpointer user_data) +{ + NMSecretAgentCallId *call_id = user_data; + gs_free_error GError *error = NULL; + gs_unref_variant GVariant *ret = NULL; + + ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error); + + if (ret) + _LOG2T(call_id, "success cancelling GetSecrets"); + else if (g_error_matches(error, G_DBUS_ERROR, G_DBUS_ERROR_SERVICE_UNKNOWN)) + _LOG2T(call_id, "cancelling GetSecrets no longer works as service disconnected"); + else { + _LOG2T(call_id, "failed to cancel GetSecrets: %s", error->message); + } + + _call_id_free(call_id); +} + +/** + * nm_secret_agent_cancel_call: + * @self: the #NMSecretAgent instance for the @call_id. + * Maybe be %NULL if @call_id is %NULL. + * @call_id: (allow-none): the call id to cancel. May be %NULL for convenience, + * in which case it does nothing. + * + * It is an error to pass an invalid @call_id or a @call_id for an operation + * that already completed. It is also an error to cancel the call from inside + * the callback, at that point the call is already completed. + * In case of nm_secret_agent_cancel_call() this will synchronously invoke the + * callback before nm_secret_agent_cancel_call() returns. + */ +void +nm_secret_agent_cancel_call(NMSecretAgent *self, NMSecretAgentCallId *call_id) +{ + NMSecretAgentPrivate *priv; + gboolean free_call_id = TRUE; + + if (!call_id) { + /* for convenience, %NULL is accepted fine. */ + nm_assert(!self || NM_IS_SECRET_AGENT(self)); + return; + } + + g_return_if_fail(NM_IS_SECRET_AGENT(call_id->self)); + g_return_if_fail(!c_list_is_empty(&call_id->lst)); + + /* Theoretically, call-id already has a self pointer. But nm_secret_agent_cancel_call() has only + * one user: NMAgentManager. And that one has the self-pointer at hand, so the only purpose of + * the @self argument is to assert that we are cancelling the expected call. + * + * We could drop the @self argument, but that just remove an additional assert-check from + * our code, without making a simplification for the only caller of this function. */ + g_return_if_fail(self == call_id->self); + + priv = NM_SECRET_AGENT_GET_PRIVATE(self); + + nm_assert(c_list_contains(&priv->requests, &call_id->lst)); + + nm_clear_g_cancellable(&call_id->cancellable); + + if (nm_streq(call_id->method_name, METHOD_GET_SECRETS)) { + g_dbus_connection_call( + priv->dbus_connection, + priv->dbus_owner, + NM_DBUS_PATH_SECRET_AGENT, + NM_DBUS_INTERFACE_SECRET_AGENT, + METHOD_CANCEL_GET_SECRETS, + g_variant_new("(os)", call_id->path, call_id->setting_name), + G_VARIANT_TYPE("()"), + G_DBUS_CALL_FLAGS_NO_AUTO_START, + NM_SHUTDOWN_TIMEOUT_MS, + NULL, /* this operation is not cancellable. We rely on the timeout. */ + _call_cancel_cb, + call_id); + /* we keep call-id alive, but it will be unlinked from priv->requests. + * _call_cancel_cb() will finally free it later. */ + free_call_id = FALSE; + } + + _call_id_invoke_callback(call_id, NULL, NULL, TRUE, free_call_id); +} + +/*****************************************************************************/ + +NMSecretAgentCallId * +nm_secret_agent_save_secrets(NMSecretAgent * self, + const char * path, + NMConnection * connection, + NMSecretAgentCallback callback, + gpointer callback_data) +{ + NMSecretAgentPrivate *priv; + GVariant * dict; + NMSecretAgentCallId * call_id; + + g_return_val_if_fail(NM_IS_SECRET_AGENT(self), NULL); + g_return_val_if_fail(NM_IS_CONNECTION(connection), NULL); + g_return_val_if_fail(path && *path, NULL); + + priv = NM_SECRET_AGENT_GET_PRIVATE(self); + + /* Caller should have ensured that only agent-owned secrets exist in 'connection' */ + dict = nm_connection_to_dbus(connection, NM_CONNECTION_SERIALIZE_ALL); + + call_id = _call_id_new(self, METHOD_SAVE_SECRETS, path, NULL, callback, callback_data); + + g_dbus_connection_call(priv->dbus_connection, + priv->dbus_owner, + NM_DBUS_PATH_SECRET_AGENT, + NM_DBUS_INTERFACE_SECRET_AGENT, + call_id->method_name, + g_variant_new("(@a{sa{sv}}o)", dict, path), + G_VARIANT_TYPE("()"), + G_DBUS_CALL_FLAGS_NO_AUTO_START, + 60000, + call_id->cancellable, + _dbus_call_cb, + call_id); + + return call_id; +} + +/*****************************************************************************/ + +NMSecretAgentCallId * +nm_secret_agent_delete_secrets(NMSecretAgent * self, + const char * path, + NMConnection * connection, + NMSecretAgentCallback callback, + gpointer callback_data) +{ + NMSecretAgentPrivate *priv; + GVariant * dict; + NMSecretAgentCallId * call_id; + + g_return_val_if_fail(NM_IS_SECRET_AGENT(self), NULL); + g_return_val_if_fail(NM_IS_CONNECTION(connection), NULL); + g_return_val_if_fail(path && *path, NULL); + + priv = NM_SECRET_AGENT_GET_PRIVATE(self); + + /* No secrets sent; agents must be smart enough to track secrets using the UUID or something */ + dict = nm_connection_to_dbus(connection, NM_CONNECTION_SERIALIZE_NO_SECRETS); + + call_id = _call_id_new(self, METHOD_DELETE_SECRETS, path, NULL, callback, callback_data); + + g_dbus_connection_call(priv->dbus_connection, + priv->dbus_owner, + NM_DBUS_PATH_SECRET_AGENT, + NM_DBUS_INTERFACE_SECRET_AGENT, + call_id->method_name, + g_variant_new("(@a{sa{sv}}o)", dict, path), + G_VARIANT_TYPE("()"), + G_DBUS_CALL_FLAGS_NO_AUTO_START, + 60000, + call_id->cancellable, + _dbus_call_cb, + call_id); + return call_id; +} + +/*****************************************************************************/ + +static void +name_owner_changed(NMSecretAgent *self, const char *owner) +{ + NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE(self); + + nm_assert(!priv->name_owner_cancellable); + + owner = nm_str_not_empty(owner); + + _LOGT("name-owner-changed: %s%s%s", + NM_PRINT_FMT_QUOTED(owner, "has ", owner, "", "disconnected")); + + if (owner) + return; + + nm_clear_g_dbus_connection_signal(priv->dbus_connection, &priv->name_owner_changed_id); + + g_signal_emit(self, signals[DISCONNECTED], 0); +} + +static void +name_owner_changed_cb(GDBusConnection *dbus_connection, + const char * sender_name, + const char * object_path, + const char * interface_name, + const char * signal_name, + GVariant * parameters, + gpointer user_data) +{ + NMSecretAgent *self = NM_SECRET_AGENT(user_data); + const char * new_owner = NULL; + + if (g_variant_is_of_type(parameters, G_VARIANT_TYPE("(sss)"))) { + g_variant_get(parameters, "(&s&s&s)", NULL, NULL, &new_owner); + } + + nm_clear_g_cancellable(&NM_SECRET_AGENT_GET_PRIVATE(self)->name_owner_cancellable); + + name_owner_changed(self, new_owner); +} + +static void +get_name_owner_cb(const char *name_owner, GError *error, gpointer user_data) +{ + NMSecretAgent *self; + + if (!name_owner && g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) + return; + + self = user_data; + + g_clear_object(&NM_SECRET_AGENT_GET_PRIVATE(self)->name_owner_cancellable); + + name_owner_changed(self, name_owner); +} + +/*****************************************************************************/ + +NMSecretAgent * +nm_secret_agent_new(GDBusMethodInvocation * context, + NMAuthSubject * subject, + const char * identifier, + NMSecretAgentCapabilities capabilities) +{ + NMSecretAgent * self; + NMSecretAgentPrivate *priv; + const char * dbus_owner; + gs_free char * owner_username = NULL; + char * description = NULL; + char buf_subject[64]; + char buf_caps[150]; + gulong uid; + GDBusConnection * dbus_connection; + + g_return_val_if_fail(context != NULL, NULL); + g_return_val_if_fail(NM_IS_AUTH_SUBJECT(subject), NULL); + g_return_val_if_fail(nm_auth_subject_get_subject_type(subject) + == NM_AUTH_SUBJECT_TYPE_UNIX_PROCESS, + NULL); + g_return_val_if_fail(identifier != NULL, NULL); + + dbus_connection = g_dbus_method_invocation_get_connection(context); + + g_return_val_if_fail(G_IS_DBUS_CONNECTION(dbus_connection), NULL); + + uid = nm_auth_subject_get_unix_process_uid(subject); + + owner_username = nm_utils_uid_to_name(uid); + + dbus_owner = nm_auth_subject_get_unix_process_dbus_sender(subject); + + self = g_object_new(NM_TYPE_SECRET_AGENT, NULL); + + priv = NM_SECRET_AGENT_GET_PRIVATE(self); + + priv->dbus_connection = g_object_ref(dbus_connection); + + _LOGT("constructed: %s, owner=%s%s%s (%s), unique-name=%s%s%s, capabilities=%s", + (description = _create_description(dbus_owner, identifier, uid)), + NM_PRINT_FMT_QUOTE_STRING(owner_username), + nm_auth_subject_to_string(subject, buf_subject, sizeof(buf_subject)), + NM_PRINT_FMT_QUOTE_STRING(g_dbus_connection_get_unique_name(priv->dbus_connection)), + _capabilities_to_string(capabilities, buf_caps, sizeof(buf_caps))); + + priv->identifier = g_strdup(identifier); + priv->owner_username = g_steal_pointer(&owner_username); + priv->dbus_owner = g_strdup(dbus_owner); + priv->description = description; + priv->capabilities = capabilities; + priv->subject = g_object_ref(subject); + + priv->name_owner_changed_id = + nm_dbus_connection_signal_subscribe_name_owner_changed(priv->dbus_connection, + priv->dbus_owner, + name_owner_changed_cb, + self, + NULL); + + priv->name_owner_cancellable = g_cancellable_new(); + nm_dbus_connection_call_get_name_owner(priv->dbus_connection, + priv->dbus_owner, + -1, + priv->name_owner_cancellable, + get_name_owner_cb, + self); + + return self; +} + +static void +nm_secret_agent_init(NMSecretAgent *self) +{ + NMSecretAgentPrivate *priv; + + priv = G_TYPE_INSTANCE_GET_PRIVATE(self, NM_TYPE_SECRET_AGENT, NMSecretAgentPrivate); + + self->_priv = priv; + + c_list_init(&self->agent_lst); + c_list_init(&priv->permissions); + c_list_init(&priv->requests); +} + +static void +dispose(GObject *object) +{ + NMSecretAgent * self = NM_SECRET_AGENT(object); + NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE(self); + + nm_assert(c_list_is_empty(&self->agent_lst)); + nm_assert(!self->auth_chain); + nm_assert(c_list_is_empty(&priv->requests)); + + nm_clear_g_dbus_connection_signal(priv->dbus_connection, &priv->name_owner_changed_id); + + nm_clear_g_cancellable(&priv->name_owner_cancellable); + + G_OBJECT_CLASS(nm_secret_agent_parent_class)->dispose(object); +} + +static void +finalize(GObject *object) +{ + NMSecretAgent * self = NM_SECRET_AGENT(object); + NMSecretAgentPrivate *priv = NM_SECRET_AGENT_GET_PRIVATE(self); + + g_free(priv->description); + g_free(priv->identifier); + g_free(priv->owner_username); + g_free(priv->dbus_owner); + + nm_c_list_elem_free_all(&priv->permissions, g_free); + + g_clear_object(&priv->subject); + + g_clear_object(&priv->dbus_connection); + + G_OBJECT_CLASS(nm_secret_agent_parent_class)->finalize(object); + + _LOGT("finalized"); +} + +static void +nm_secret_agent_class_init(NMSecretAgentClass *config_class) +{ + GObjectClass *object_class = G_OBJECT_CLASS(config_class); + + g_type_class_add_private(object_class, sizeof(NMSecretAgentPrivate)); + + object_class->dispose = dispose; + object_class->finalize = finalize; + + signals[DISCONNECTED] = g_signal_new(NM_SECRET_AGENT_DISCONNECTED, + G_OBJECT_CLASS_TYPE(object_class), + G_SIGNAL_RUN_FIRST, + 0, + NULL, + NULL, + g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, + 0); +} |