/* SPDX-License-Identifier: LGPL-2.1-or-later */ /* * Copyright (C) 2010 - 2011 Red Hat, Inc. */ #include "nm-default.h" #include "nm-secret-agent-old.h" #include "c-list/src/c-list.h" #include "nm-core-internal.h" #include "nm-dbus-helpers.h" #include "nm-dbus-interface.h" #include "nm-enum-types.h" #include "nm-glib-aux/nm-c-list.h" #include "nm-glib-aux/nm-dbus-aux.h" #include "nm-glib-aux/nm-time-utils.h" #include "nm-simple-connection.h" #define REGISTER_RETRY_TIMEOUT_MSEC 3000 #define _CALL_REGISTER_TIMEOUT_MSEC 15000 /*****************************************************************************/ typedef struct { char * connection_path; char * setting_name; GDBusMethodInvocation *context; CList gsi_lst; bool is_cancelling : 1; } GetSecretsInfo; NM_GOBJECT_PROPERTIES_DEFINE(NMSecretAgentOld, PROP_IDENTIFIER, PROP_AUTO_REGISTER, PROP_REGISTERED, PROP_CAPABILITIES, PROP_DBUS_CONNECTION, ); typedef struct { GDBusConnection *dbus_connection; GMainContext * main_context; GMainContext * dbus_context; GObject * context_busy_watcher; GCancellable * name_owner_cancellable; GCancellable * registering_cancellable; GSource * registering_retry_source; NMLInitData *init_data; CList gsi_lst_head; CList pending_tasks_register_lst_head; char *identifier; NMRefString *name_owner_curr; NMRefString *name_owner_next; gint64 registering_timeout_msec; guint name_owner_changed_id; guint exported_id; guint capabilities; guint8 registering_try_count; guint8 register_state_change_reenter : 2; bool session_bus : 1; bool auto_register : 1; bool is_registered : 1; bool is_enabled : 1; bool registration_force_unregister : 1; /* This is true, if we either are in the process of RegisterWithCapabilities() or * are already successfully registered. * * This is only TRUE, if the name owner was authenticated to run as root user. * * It also means, we should follow up with an Unregister() call during shutdown. */ bool registered_against_server : 1; bool is_initialized : 1; bool is_destroyed : 1; } NMSecretAgentOldPrivate; static void nm_secret_agent_old_initable_iface_init(GInitableIface *iface); static void nm_secret_agent_old_async_initable_iface_init(GAsyncInitableIface *iface); G_DEFINE_ABSTRACT_TYPE_WITH_CODE( NMSecretAgentOld, nm_secret_agent_old, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE(G_TYPE_INITABLE, nm_secret_agent_old_initable_iface_init); G_IMPLEMENT_INTERFACE(G_TYPE_ASYNC_INITABLE, nm_secret_agent_old_async_initable_iface_init);) #define NM_SECRET_AGENT_OLD_GET_PRIVATE(self) \ (G_TYPE_INSTANCE_GET_PRIVATE((self), NM_TYPE_SECRET_AGENT_OLD, NMSecretAgentOldPrivate)) /*****************************************************************************/ #define _NMLOG(level, ...) \ NML_DBUS_LOG((level), \ "secret-agent[" NM_HASH_OBFUSCATE_PTR_FMT \ "]: " _NM_UTILS_MACRO_FIRST(__VA_ARGS__), \ NM_HASH_OBFUSCATE_PTR(self) _NM_UTILS_MACRO_REST(__VA_ARGS__)) /*****************************************************************************/ static const GDBusInterfaceInfo interface_info = NM_DEFINE_GDBUS_INTERFACE_INFO_INIT( NM_DBUS_INTERFACE_SECRET_AGENT, .methods = NM_DEFINE_GDBUS_METHOD_INFOS( NM_DEFINE_GDBUS_METHOD_INFO("GetSecrets", .in_args = NM_DEFINE_GDBUS_ARG_INFOS( NM_DEFINE_GDBUS_ARG_INFO("connection", "a{sa{sv}}"), NM_DEFINE_GDBUS_ARG_INFO("connection_path", "o"), NM_DEFINE_GDBUS_ARG_INFO("setting_name", "s"), NM_DEFINE_GDBUS_ARG_INFO("hints", "as"), NM_DEFINE_GDBUS_ARG_INFO("flags", "u"), ), .out_args = NM_DEFINE_GDBUS_ARG_INFOS( NM_DEFINE_GDBUS_ARG_INFO("secrets", "a{sa{sv}}"), ), ), NM_DEFINE_GDBUS_METHOD_INFO("CancelGetSecrets", .in_args = NM_DEFINE_GDBUS_ARG_INFOS( NM_DEFINE_GDBUS_ARG_INFO("connection_path", "o"), NM_DEFINE_GDBUS_ARG_INFO("setting_name", "s"), ), ), NM_DEFINE_GDBUS_METHOD_INFO("SaveSecrets", .in_args = NM_DEFINE_GDBUS_ARG_INFOS( NM_DEFINE_GDBUS_ARG_INFO("connection", "a{sa{sv}}"), NM_DEFINE_GDBUS_ARG_INFO("connection_path", "o"), ), ), NM_DEFINE_GDBUS_METHOD_INFO( "DeleteSecrets", .in_args = NM_DEFINE_GDBUS_ARG_INFOS( NM_DEFINE_GDBUS_ARG_INFO("connection", "a{sa{sv}}"), NM_DEFINE_GDBUS_ARG_INFO("connection_path", "o"), ), ), ), ); /*****************************************************************************/ static void _register_state_change(NMSecretAgentOld *self); static void _register_dbus_call(NMSecretAgentOld *self); static void _init_complete(NMSecretAgentOld *self, GError *error_take); static void _register_state_complete(NMSecretAgentOld *self); /*****************************************************************************/ /** * nm_secret_agent_old_get_dbus_connection: * @self: the #NMSecretAgentOld instance * * Returns: (transfer none): the #GDBusConnection used by the secret agent. * You may either set this as construct property %NM_SECRET_AGENT_OLD_DBUS_CONNECTION, * or it will automatically set during initialization. * * Since: 1.24 */ GDBusConnection * nm_secret_agent_old_get_dbus_connection(NMSecretAgentOld *self) { g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), NULL); return NM_SECRET_AGENT_OLD_GET_PRIVATE(self)->dbus_connection; } /** * nm_secret_agent_old_get_main_context: * @self: the #NMSecretAgentOld instance * * Returns: (transfer none): the #GMainContext instance associate with the * instance. This is the g_main_context_get_thread_default() at the time * when creating the instance. * * Since: 1.24 */ GMainContext * nm_secret_agent_old_get_main_context(NMSecretAgentOld *self) { g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), NULL); return NM_SECRET_AGENT_OLD_GET_PRIVATE(self)->main_context; } /** * nm_secret_agent_old_get_context_busy_watcher: * @self: the #NMSecretAgentOld instance * * Returns a #GObject that stays alive as long as there are pending * requests in the #GDBusConnection. Such requests keep the #GMainContext * alive, and thus you may want to keep iterating the context as long * until a weak reference indicates that this object is gone. This is * useful because even when you destroy the instance right away (and all * the internally pending requests get cancelled), any pending g_dbus_connection_call() * requests will still invoke the result on the #GMainContext. Hence, this * allows you to know how long you must iterate the context to know * that all remains are cleaned up. * * Returns: (transfer none): a #GObject that you may register a weak pointer * to know that the #GMainContext is still kept busy by @self. * * Since: 1.24 */ GObject * nm_secret_agent_old_get_context_busy_watcher(NMSecretAgentOld *self) { g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), NULL); return NM_SECRET_AGENT_OLD_GET_PRIVATE(self)->context_busy_watcher; } /** * nm_secret_agent_old_get_dbus_name_owner: * @self: the #NMSecretAgentOld instance * * Returns: the current D-Bus name owner. While this property * is set while registering, it really only makes sense when * the nm_secret_agent_old_get_registered() indicates that * registration is successful. * * Since: 1.24 */ const char * nm_secret_agent_old_get_dbus_name_owner(NMSecretAgentOld *self) { g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), NULL); return nm_ref_string_get_str(NM_SECRET_AGENT_OLD_GET_PRIVATE(self)->name_owner_curr); } /** * nm_secret_agent_old_get_registered: * @self: a #NMSecretAgentOld * * Note that the secret agent transparently registers and re-registers * as the D-Bus name owner appears. Hence, this property is not really * useful. Also, to be graceful against races during registration, the * instance will already accept requests while being in the process of * registering. * If you need to avoid races and want to wait until @self is registered, * call nm_secret_agent_old_register_async(). If that function completes * with success, you know the instance is registered. * * Returns: a %TRUE if the agent is registered, %FALSE if it is not. **/ gboolean nm_secret_agent_old_get_registered(NMSecretAgentOld *self) { g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), FALSE); return NM_SECRET_AGENT_OLD_GET_PRIVATE(self)->is_registered; } /*****************************************************************************/ static void get_secret_info_free(GetSecretsInfo *info) { nm_assert(info); nm_assert(!info->context); c_list_unlink_stale(&info->gsi_lst); g_free(info->connection_path); g_free(info->setting_name); nm_g_slice_free(info); } static void get_secret_info_complete_and_free(GetSecretsInfo *info, GVariant *secrets, GError *error) { if (error) { if (secrets) nm_g_variant_unref_floating(secrets); g_dbus_method_invocation_return_gerror(g_steal_pointer(&info->context), error); } else { g_dbus_method_invocation_return_value(g_steal_pointer(&info->context), g_variant_new("(@a{sa{sv}})", secrets)); } get_secret_info_free(info); } static void get_secret_info_complete_and_free_error(GetSecretsInfo *info, GQuark error_domain, int error_code, const char * error_message) { g_dbus_method_invocation_return_error_literal(g_steal_pointer(&info->context), error_domain, error_code, error_message); get_secret_info_free(info); } /*****************************************************************************/ static void _dbus_connection_call_cb(GObject *source, GAsyncResult *result, gpointer user_data) { gs_unref_object GObject *context_busy_watcher = NULL; GAsyncReadyCallback callback; gpointer callback_user_data; nm_utils_user_data_unpack(user_data, &context_busy_watcher, &callback, &callback_user_data); callback(source, result, callback_user_data); } static void _dbus_connection_call(NMSecretAgentOld * self, const char * bus_name, const char * object_path, const char * interface_name, const char * method_name, GVariant * parameters, const GVariantType *reply_type, GDBusCallFlags flags, int timeout_msec, GCancellable * cancellable, GAsyncReadyCallback callback, gpointer user_data) { NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); nm_assert(nm_g_main_context_is_thread_default(priv->dbus_context)); g_dbus_connection_call( priv->dbus_connection, bus_name, object_path, interface_name, method_name, parameters, reply_type, flags, timeout_msec, cancellable, callback ? _dbus_connection_call_cb : NULL, callback ? nm_utils_user_data_pack(g_object_ref(priv->context_busy_watcher), callback, user_data) : NULL); } /*****************************************************************************/ static GetSecretsInfo * find_get_secrets_info(NMSecretAgentOldPrivate *priv, const char * connection_path, const char * setting_name) { GetSecretsInfo *info; c_list_for_each_entry (info, &priv->gsi_lst_head, gsi_lst) { if (nm_streq(connection_path, info->connection_path) && nm_streq(setting_name, info->setting_name)) return info; } return NULL; } static void _cancel_get_secret_request(NMSecretAgentOld *self, GetSecretsInfo *info, const char *message) { c_list_unlink(&info->gsi_lst); info->is_cancelling = TRUE; _LOGT("cancel get-secrets request \"%s\", \"%s\": %s", info->connection_path, info->setting_name, message); NM_SECRET_AGENT_OLD_GET_CLASS(self)->cancel_get_secrets(self, info->connection_path, info->setting_name); get_secret_info_complete_and_free_error(info, NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_AGENT_CANCELED, message); } static gboolean verify_request(NMSecretAgentOld * self, GDBusMethodInvocation *context, GVariant * connection_dict, const char * connection_path, NMConnection ** out_connection, GError ** error) { gs_unref_object NMConnection *connection = NULL; gs_free_error GError *local = NULL; if (!nm_dbus_path_not_empty(connection_path)) { g_set_error_literal(error, NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_INVALID_CONNECTION, "Invalid connection: no connection path given."); return FALSE; } connection = _nm_simple_connection_new_from_dbus(connection_dict, NM_SETTING_PARSE_FLAGS_BEST_EFFORT, &local); if (!connection) { g_set_error(error, NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_INVALID_CONNECTION, "Invalid connection: %s", local->message); return FALSE; } nm_connection_set_path(connection, connection_path); NM_SET_OUT(out_connection, g_steal_pointer(&connection)); return TRUE; } static void get_secrets_cb(NMSecretAgentOld *self, NMConnection * connection, GVariant * secrets, GError * error, gpointer user_data) { GetSecretsInfo *info = user_data; if (info->is_cancelling) { if (secrets) nm_g_variant_unref_floating(secrets); return; } _LOGT("request: get-secrets request \"%s\", \"%s\" complete with %s%s%s", info->connection_path, info->setting_name, NM_PRINT_FMT_QUOTED(error, "error: ", error->message, "", "success")); get_secret_info_complete_and_free(info, secrets, error); } static void impl_get_secrets(NMSecretAgentOld *self, GVariant *parameters, GDBusMethodInvocation *context) { NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); GError * error = NULL; gs_unref_object NMConnection *connection = NULL; GetSecretsInfo * info; gs_unref_variant GVariant *arg_connection = NULL; const char * arg_connection_path; const char * arg_setting_name; gs_free const char ** arg_hints = NULL; guint32 arg_flags; g_variant_get(parameters, "(@a{sa{sv}}&o&s^a&su)", &arg_connection, &arg_connection_path, &arg_setting_name, &arg_hints, &arg_flags); if (!verify_request(self, context, arg_connection, arg_connection_path, &connection, &error)) { g_dbus_method_invocation_take_error(context, error); return; } _LOGT("request: get-secrets(\"%s\", \"%s\")", arg_connection_path, arg_setting_name); info = find_get_secrets_info(priv, arg_connection_path, arg_setting_name); if (info) _cancel_get_secret_request(self, info, "Request aborted due to new request"); info = g_slice_new(GetSecretsInfo); *info = (GetSecretsInfo){ .context = context, .connection_path = g_strdup(arg_connection_path), .setting_name = g_strdup(arg_setting_name), }; c_list_link_tail(&priv->gsi_lst_head, &info->gsi_lst); NM_SECRET_AGENT_OLD_GET_CLASS(self)->get_secrets(self, connection, info->connection_path, info->setting_name, arg_hints, arg_flags, get_secrets_cb, info); } static void impl_cancel_get_secrets(NMSecretAgentOld * self, GVariant * parameters, GDBusMethodInvocation *context) { NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); GetSecretsInfo * info; const char * arg_connection_path; const char * arg_setting_name; g_variant_get(parameters, "(&o&s)", &arg_connection_path, &arg_setting_name); info = find_get_secrets_info(priv, arg_connection_path, arg_setting_name); if (!info) { g_dbus_method_invocation_return_error_literal( context, NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_FAILED, "No secrets request in progress for this connection."); return; } _cancel_get_secret_request(self, info, "Request cancelled by NetworkManager"); g_dbus_method_invocation_return_value(context, NULL); } static void save_secrets_cb(NMSecretAgentOld *self, NMConnection *connection, GError *error, gpointer user_data) { GDBusMethodInvocation *context = user_data; if (error) g_dbus_method_invocation_return_gerror(context, error); else g_dbus_method_invocation_return_value(context, NULL); } static void impl_save_secrets(NMSecretAgentOld *self, GVariant *parameters, GDBusMethodInvocation *context) { gs_unref_object NMConnection *connection = NULL; gs_unref_variant GVariant *arg_connection = NULL; const char * arg_connection_path; GError * error = NULL; g_variant_get(parameters, "(@a{sa{sv}}&o)", &arg_connection, &arg_connection_path); if (!verify_request(self, context, arg_connection, arg_connection_path, &connection, &error)) { g_dbus_method_invocation_take_error(context, error); return; } _LOGT("request: save-secrets(\"%s\")", arg_connection_path); NM_SECRET_AGENT_OLD_GET_CLASS(self)->save_secrets(self, connection, arg_connection_path, save_secrets_cb, context); } static void delete_secrets_cb(NMSecretAgentOld *self, NMConnection * connection, GError * error, gpointer user_data) { GDBusMethodInvocation *context = user_data; if (error) g_dbus_method_invocation_return_gerror(context, error); else g_dbus_method_invocation_return_value(context, NULL); } static void impl_delete_secrets(NMSecretAgentOld *self, GVariant *parameters, GDBusMethodInvocation *context) { gs_unref_object NMConnection *connection = NULL; gs_unref_variant GVariant *arg_connection = NULL; const char * arg_connection_path; GError * error = NULL; g_variant_get(parameters, "(@a{sa{sv}}&o)", &arg_connection, &arg_connection_path); if (!verify_request(self, context, arg_connection, arg_connection_path, &connection, &error)) { g_dbus_method_invocation_take_error(context, error); return; } _LOGT("request: delete-secrets(\"%s\")", arg_connection_path); NM_SECRET_AGENT_OLD_GET_CLASS(self)->delete_secrets(self, connection, arg_connection_path, delete_secrets_cb, context); } /*****************************************************************************/ /** * nm_secret_agent_old_enable: * @self: the #NMSecretAgentOld instance * @enable: whether to enable or disable the listener. * * This has the same effect as setting %NM_SECRET_AGENT_OLD_AUTO_REGISTER * property. * * Unlike most other functions, you may already call this function before * initialization completes. * * Since: 1.24 */ void nm_secret_agent_old_enable(NMSecretAgentOld *self, gboolean enable) { NMSecretAgentOldPrivate *priv; g_return_if_fail(NM_IS_SECRET_AGENT_OLD(self)); priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); enable = (!!enable); if (priv->auto_register != enable) { priv->auto_register = enable; priv->is_enabled = enable; _notify(self, PROP_AUTO_REGISTER); } _register_state_change(self); } static void _secret_agent_old_destroy(NMSecretAgentOld *self) { NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); priv->is_destroyed = TRUE; if (priv->exported_id != 0) { g_dbus_connection_unregister_object(priv->dbus_connection, nm_steal_int(&priv->exported_id)); } _register_state_change(self); nm_assert(!priv->name_owner_changed_id); nm_assert(!priv->name_owner_curr); nm_assert(!priv->name_owner_next); nm_assert(!priv->name_owner_cancellable); nm_assert(!priv->registering_retry_source); nm_assert(!priv->registering_cancellable); nm_assert(!priv->init_data); nm_assert(c_list_is_empty(&priv->gsi_lst_head)); nm_assert(c_list_is_empty(&priv->pending_tasks_register_lst_head)); } /** * nm_secret_agent_old_destroy: * @self: the #NMSecretAgentOld instance. * * Since 1.24, the instance will already register a D-Bus object on the * D-Bus connection during initialization. That object will stay registered * until @self gets unrefed (destroyed) or this function is called. This * function performs the necessary cleanup to tear down the instance. Afterwards, * the function can not longer be used. This is optional, but necessary to * ensure unregistering the D-Bus object at a define point, when other users * might still have a reference on @self. * * You may call this function any time and repeatedly. However, after destroying * the instance, it is a bug to still use the instance for other purposes. The * instance becomes defunct and cannot re-register. * * Since: 1.24 */ void nm_secret_agent_old_destroy(NMSecretAgentOld *self) { g_return_if_fail(NM_IS_SECRET_AGENT_OLD(self)); _LOGT("destroying"); _secret_agent_old_destroy(self); } /*****************************************************************************/ /** * nm_secret_agent_old_register: * @self: a #NMSecretAgentOld * @cancellable: a #GCancellable, or %NULL * @error: return location for a #GError, or %NULL * * Registers the #NMSecretAgentOld 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. * * Returns: %TRUE if registration was successful, %FALSE on error. * * Since 1.24, this can no longer fail unless the @cancellable gets * cancelled. Contrary to nm_secret_agent_old_register_async(), this also * does not wait for the registration to succeed. You cannot synchronously * (without iterating the caller's GMainContext) wait for registration. * * Since 1.24, registration is idempotent. It has the same effect as setting * %NM_SECRET_AGENT_OLD_AUTO_REGISTER to %TRUE or nm_secret_agent_old_enable(). * * Deprecated: 1.24: Use nm_secret_agent_old_enable() or nm_secret_agent_old_register_async(). **/ gboolean nm_secret_agent_old_register(NMSecretAgentOld *self, GCancellable *cancellable, GError **error) { NMSecretAgentOldPrivate *priv; g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), FALSE); g_return_val_if_fail(!error || !*error, FALSE); priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); g_return_val_if_fail(priv->is_initialized && !priv->is_destroyed, FALSE); priv->is_enabled = TRUE; _register_state_change(self); if (g_cancellable_set_error_if_cancelled(cancellable, error)) return FALSE; /* This is a synchronous function, meaning: we are not allowed to iterate * the caller's GMainContext. This is a catch 22, because we don't want * to perform synchronous calls that bypasses the ordering of our otherwise * asynchronous mode of operation. Hence, we always signal success. * That's why this function is deprecated. * * So despite claiming success, we might still be in the process of registering * or NetworkManager might not be available. * * This is a change in behavior with respect to libnm before 1.24. */ return TRUE; } static void _register_cancelled_cb(GObject *object, gpointer user_data) { GTask * task0 = user_data; gs_unref_object GTask * task = NULL; NMSecretAgentOld * self = g_task_get_source_object(task0); NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); gulong * p_cancelled_id; NMCListElem * elem; gs_free_error GError *error = NULL; elem = nm_c_list_elem_find_first(&priv->pending_tasks_register_lst_head, x, x == task0); g_return_if_fail(elem); task = nm_c_list_elem_free_steal(elem); p_cancelled_id = g_task_get_task_data(task); if (p_cancelled_id) { g_signal_handler_disconnect(g_task_get_cancellable(task), *p_cancelled_id); g_task_set_task_data(task, NULL, NULL); } nm_utils_error_set_cancelled(&error, FALSE, NULL); g_task_return_error(task, error); } /** * nm_secret_agent_old_register_async: * @self: a #NMSecretAgentOld * @cancellable: a #GCancellable, or %NULL * @callback: callback to call when the agent is registered * @user_data: data for @callback * * Asynchronously registers the #NMSecretAgentOld 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. * * Since 1.24, registration cannot fail and is idempotent. It has * the same effect as setting %NM_SECRET_AGENT_OLD_AUTO_REGISTER to %TRUE * or nm_secret_agent_old_enable(). * * Since 1.24, the asynchronous result indicates whether the instance is successfully * registered. In any case, this call enables the agent and it will automatically * try to register and handle secret requests. A failure of this function only indicates * that currently the instance might not be ready (but since it will automatically * try to recover, it might be ready in a moment afterwards). Use this function if * you want to check and ensure that the agent is registered. **/ void nm_secret_agent_old_register_async(NMSecretAgentOld * self, GCancellable * cancellable, GAsyncReadyCallback callback, gpointer user_data) { NMSecretAgentOldPrivate *priv; g_return_if_fail(NM_IS_SECRET_AGENT_OLD(self)); g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); g_return_if_fail(priv->is_initialized && !priv->is_destroyed); if (callback) { GTask *task; task = nm_g_task_new(self, cancellable, nm_secret_agent_old_register_async, callback, user_data); c_list_link_tail(&priv->pending_tasks_register_lst_head, &nm_c_list_elem_new_stale(task)->lst); if (cancellable) { gulong cancelled_id; cancelled_id = g_cancellable_connect(cancellable, G_CALLBACK(_register_cancelled_cb), task, NULL); if (cancelled_id != 0) { g_task_set_task_data(task, g_memdup(&cancelled_id, sizeof(cancelled_id)), g_free); } } } priv->is_enabled = TRUE; _register_state_change(self); } /** * nm_secret_agent_old_register_finish: * @self: a #NMSecretAgentOld * @result: the result passed to the #GAsyncReadyCallback * @error: return location for a #GError, or %NULL * * Gets the result of a call to nm_secret_agent_old_register_async(). * * Returns: %TRUE if registration was successful, %FALSE on error. * * Since 1.24, registration cannot fail and is idempotent. It has * the same effect as setting %NM_SECRET_AGENT_OLD_AUTO_REGISTER to %TRUE * or nm_secret_agent_old_enable(). **/ gboolean nm_secret_agent_old_register_finish(NMSecretAgentOld *self, GAsyncResult *result, GError **error) { g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), FALSE); g_return_val_if_fail(nm_g_task_is_valid(result, self, nm_secret_agent_old_register_async), FALSE); return g_task_propagate_boolean(G_TASK(result), error); } /** * nm_secret_agent_old_unregister: * @self: a #NMSecretAgentOld * @cancellable: a #GCancellable, or %NULL * @error: return location for a #GError, or %NULL * * Unregisters the #NMSecretAgentOld with the NetworkManager secret manager, * indicating to NetworkManager that the agent will no longer provide or * store secrets on behalf of this user. * * Returns: %TRUE if unregistration was successful, %FALSE on error * * Since 1.24, registration cannot fail and is idempotent. It has * the same effect as setting %NM_SECRET_AGENT_OLD_AUTO_REGISTER to %FALSE * or nm_secret_agent_old_enable(). * * Deprecated: 1.24: Use nm_secret_agent_old_enable(). **/ gboolean nm_secret_agent_old_unregister(NMSecretAgentOld *self, GCancellable *cancellable, GError **error) { NMSecretAgentOldPrivate *priv; g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), FALSE); g_return_val_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable), FALSE); g_return_val_if_fail(!error || !*error, FALSE); priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); g_return_val_if_fail(priv->is_initialized && !priv->is_destroyed, FALSE); priv->is_enabled = FALSE; _register_state_change(self); return !g_cancellable_set_error_if_cancelled(cancellable, error); } /** * nm_secret_agent_old_unregister_async: * @self: a #NMSecretAgentOld * @cancellable: a #GCancellable, or %NULL * @callback: callback to call when the agent is unregistered * @user_data: data for @callback * * Asynchronously unregisters the #NMSecretAgentOld with the NetworkManager secret * manager, indicating to NetworkManager that the agent will no longer provide * or store secrets on behalf of this user. * * Since 1.24, registration cannot fail and is idempotent. It has * the same effect as setting %NM_SECRET_AGENT_OLD_AUTO_REGISTER to %FALSE * or nm_secret_agent_old_enable(). * * Deprecated: 1.24: Use nm_secret_agent_old_enable(). **/ void nm_secret_agent_old_unregister_async(NMSecretAgentOld * self, GCancellable * cancellable, GAsyncReadyCallback callback, gpointer user_data) { NMSecretAgentOldPrivate *priv; g_return_if_fail(NM_IS_SECRET_AGENT_OLD(self)); g_return_if_fail(!cancellable || G_IS_CANCELLABLE(cancellable)); priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); g_return_if_fail(priv->is_initialized && !priv->is_destroyed); if (callback) { gs_unref_object GTask *task = NULL; task = nm_g_task_new(self, cancellable, nm_secret_agent_old_unregister_async, callback, user_data); g_task_return_boolean(task, TRUE); } priv->is_enabled = FALSE; _register_state_change(self); } /** * nm_secret_agent_old_unregister_finish: * @self: a #NMSecretAgentOld * @result: the result passed to the #GAsyncReadyCallback * @error: return location for a #GError, or %NULL * * Gets the result of a call to nm_secret_agent_old_unregister_async(). * * Returns: %TRUE if unregistration was successful, %FALSE on error. * * Since 1.24, registration cannot fail and is idempotent. It has * the same effect as setting %NM_SECRET_AGENT_OLD_AUTO_REGISTER to %FALSE * or nm_secret_agent_old_enable(). * * Deprecated: 1.24: Use nm_secret_agent_old_enable(). **/ gboolean nm_secret_agent_old_unregister_finish(NMSecretAgentOld *self, GAsyncResult *result, GError **error) { g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(self), FALSE); g_return_val_if_fail(nm_g_task_is_valid(result, self, nm_secret_agent_old_unregister_async), FALSE); return g_task_propagate_boolean(G_TASK(result), error); } /*****************************************************************************/ /** * nm_secret_agent_old_get_secrets: (virtual get_secrets): * @self: a #NMSecretAgentOld * @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 * * Asynchronously retrieves 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. */ void nm_secret_agent_old_get_secrets(NMSecretAgentOld * self, NMConnection * connection, const char * setting_name, const char ** hints, NMSecretAgentGetSecretsFlags flags, NMSecretAgentOldGetSecretsFunc callback, gpointer user_data) { g_return_if_fail(NM_IS_SECRET_AGENT_OLD(self)); g_return_if_fail(NM_IS_CONNECTION(connection)); g_return_if_fail(nm_connection_get_path(connection)); g_return_if_fail(setting_name && setting_name[0]); g_return_if_fail(!(flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_ONLY_SYSTEM)); g_return_if_fail(!(flags & NM_SECRET_AGENT_GET_SECRETS_FLAG_NO_ERRORS)); g_return_if_fail(callback != NULL); NM_SECRET_AGENT_OLD_GET_CLASS(self)->get_secrets(self, connection, nm_connection_get_path(connection), setting_name, hints, flags, callback, user_data); } /** * nm_secret_agent_old_save_secrets: (virtual save_secrets): * @self: a #NMSecretAgentOld * @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 ensures that all secrets inside @connection are stored to * disk. */ void nm_secret_agent_old_save_secrets(NMSecretAgentOld * self, NMConnection * connection, NMSecretAgentOldSaveSecretsFunc callback, gpointer user_data) { g_return_if_fail(NM_IS_SECRET_AGENT_OLD(self)); g_return_if_fail(NM_IS_CONNECTION(connection)); g_return_if_fail(nm_connection_get_path(connection)); NM_SECRET_AGENT_OLD_GET_CLASS(self)->save_secrets(self, connection, nm_connection_get_path(connection), callback, user_data); } /** * nm_secret_agent_old_delete_secrets: (virtual delete_secrets): * @self: a #NMSecretAgentOld * @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 asks the agent to delete all saved secrets belonging to * @connection. */ void nm_secret_agent_old_delete_secrets(NMSecretAgentOld * self, NMConnection * connection, NMSecretAgentOldDeleteSecretsFunc callback, gpointer user_data) { g_return_if_fail(NM_IS_SECRET_AGENT_OLD(self)); g_return_if_fail(NM_IS_CONNECTION(connection)); g_return_if_fail(nm_connection_get_path(connection)); NM_SECRET_AGENT_OLD_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 (!g_ascii_isalnum(*p) && (*p != '_') && (*p != '-') && (*p != '.')) return FALSE; if ((*p == '.') && (*(p + 1) == '.')) return FALSE; p++; } return TRUE; } /*****************************************************************************/ static gboolean _register_retry_cb(gpointer user_data) { NMSecretAgentOld * self = user_data; NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); nm_auto_pop_gmaincontext GMainContext *dbus_context = NULL; dbus_context = nm_g_main_context_push_thread_default_if_necessary(priv->dbus_context); nm_clear_g_source_inst(&priv->registering_retry_source); _register_dbus_call(self); return G_SOURCE_CONTINUE; } static void _register_call_cb(GObject *source, GAsyncResult *result, gpointer user_data) { NMSecretAgentOld * self = user_data; NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); gs_unref_variant GVariant *ret = NULL; gs_free_error GError *error = NULL; ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error); if (nm_utils_error_is_cancelled(error)) return; nm_assert(!priv->registering_retry_source); nm_assert(!priv->is_registered); nm_assert(priv->registering_cancellable); if (nm_dbus_error_is(error, NM_DBUS_ERROR_NAME_UNKNOWN_METHOD) && nm_utils_get_monotonic_timestamp_msec() < priv->registering_timeout_msec) { guint timeout_msec; timeout_msec = (2u << NM_MIN(6u, ++priv->registering_try_count)); _LOGT("register: registration failed with error \"%s\". Retry in %u msec...", error->message, timeout_msec); priv->registering_retry_source = nm_g_source_attach(nm_g_timeout_source_new(timeout_msec, G_PRIORITY_DEFAULT, _register_retry_cb, self, NULL), priv->dbus_context); return; } g_clear_object(&priv->registering_cancellable); if (error) { /* registration apparently failed. However we still keep priv->registered_against_server TRUE, because * * - eventually we want to still make an Unregister() call. Even if it probably has no effect, * better be sure. * * - we actually accept secret request (from the right name owner). We register so that * NetworkManager knows that we are here. We don't require the registration to succeed * for our purpose. If NetworkManager makes requests for us, despite the registration * failing, that is fine. */ _LOGT("register: registration failed with error \"%s\"", error->message); goto out; } _LOGT("register: registration succeeded"); priv->is_registered = TRUE; _notify(self, PROP_REGISTERED); out: _register_state_complete(self); } static void _register_dbus_call(NMSecretAgentOld *self) { NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); _dbus_connection_call(self, nm_ref_string_get_str(priv->name_owner_curr), NM_DBUS_PATH_AGENT_MANAGER, NM_DBUS_INTERFACE_AGENT_MANAGER, "RegisterWithCapabilities", g_variant_new("(su)", priv->identifier, (guint32) priv->capabilities), G_VARIANT_TYPE("()"), G_DBUS_CALL_FLAGS_NONE, _CALL_REGISTER_TIMEOUT_MSEC, priv->registering_cancellable, _register_call_cb, self); } static void _get_connection_unix_user_cb(GObject *source, GAsyncResult *result, gpointer user_data) { NMSecretAgentOld * self; NMSecretAgentOldPrivate *priv; gs_unref_variant GVariant *ret = NULL; gs_free_error GError *error = NULL; guint32 sender_uid = 0; ret = g_dbus_connection_call_finish(G_DBUS_CONNECTION(source), result, &error); if (nm_utils_error_is_cancelled(error)) return; self = user_data; priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); nm_assert(priv->registering_cancellable); nm_assert(!priv->registered_against_server); if (ret) g_variant_get(ret, "(u)", &sender_uid); if (ret && sender_uid == 0) _LOGT("register: peer %s is owned by root. Validated to accept requests.", priv->name_owner_curr->str); else if (ret && priv->session_bus) { _LOGT( "register: peer %s is owned by user %d for session bus. Validated to accept requests.", priv->name_owner_curr->str, sender_uid); } else { /* the peer is not validated. We don't actually register. */ if (ret) _LOGT("register: peer %s is owned by user %u. Not validated as NetworkManager service.", priv->name_owner_curr->str, sender_uid); else _LOGT("register: failed to get user id for peer %s: %s. Not validated as " "NetworkManager service.", priv->name_owner_curr->str, error->message); /* we actually don't do anything and keep the agent unregistered. * * We keep priv->registering_cancellable set to not retry this again, until we loose the * name owner. But the state of the agent is lingering and won't accept any requests. */ return; } priv->registering_timeout_msec = nm_utils_get_monotonic_timestamp_msec() + REGISTER_RETRY_TIMEOUT_MSEC; priv->registering_try_count = 0; priv->registered_against_server = TRUE; _register_dbus_call(self); } /*****************************************************************************/ static void _name_owner_changed(NMSecretAgentOld *self, const char *name_owner, gboolean is_event) { NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); if (is_event) { if (priv->name_owner_cancellable) { /* we are still fetching the name-owner. Ignore this event. */ return; } } else g_clear_object(&priv->name_owner_cancellable); nm_ref_string_unref(priv->name_owner_next); priv->name_owner_next = nm_ref_string_new(nm_str_not_empty(name_owner)); _LOGT("name-owner changed: %s%s%s -> %s%s%s", NM_PRINT_FMT_QUOTED(priv->name_owner_curr, "\"", priv->name_owner_curr->str, "\"", "(null)"), NM_PRINT_FMT_QUOTED(priv->name_owner_next, "\"", priv->name_owner_next->str, "\"", "(null)")); _register_state_change(self); } static void _name_owner_changed_cb(GDBusConnection *connection, const char * sender_name, const char * object_path, const char * interface_name, const char * signal_name, GVariant * parameters, gpointer user_data) { NMSecretAgentOld *self = user_data; const char * new_owner; if (!g_variant_is_of_type(parameters, G_VARIANT_TYPE("(sss)"))) return; g_variant_get(parameters, "(&s&s&s)", NULL, NULL, &new_owner); _name_owner_changed(self, new_owner, TRUE); } static void _name_owner_get_cb(const char *name_owner, GError *error, gpointer user_data) { if (name_owner || !nm_utils_error_is_cancelled(error)) _name_owner_changed(user_data, name_owner, FALSE); } /*****************************************************************************/ static void _method_call(GDBusConnection * connection, const char * sender, const char * object_path, const char * interface_name, const char * method_name, GVariant * parameters, GDBusMethodInvocation *context, gpointer user_data) { NMSecretAgentOld * self = user_data; NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); nm_assert(nm_streq0(object_path, NM_DBUS_PATH_SECRET_AGENT)); nm_assert(nm_streq0(interface_name, NM_DBUS_INTERFACE_SECRET_AGENT)); nm_assert(sender); nm_assert(nm_streq0(sender, g_dbus_method_invocation_get_sender(context))); if (!priv->name_owner_curr || !priv->registered_against_server) { /* priv->registered_against_server means that we started to register, but not necessarily * that the registration fully succeeded. However, we already authenticated the request * and so we accept it, even if the registration is not yet complete. */ g_dbus_method_invocation_return_error_literal(context, NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_PERMISSION_DENIED, "Request by non authenticated peer rejected"); return; } if (nm_streq(method_name, "GetSecrets")) impl_get_secrets(self, parameters, context); else if (nm_streq(method_name, "CancelGetSecrets")) impl_cancel_get_secrets(self, parameters, context); else if (nm_streq(method_name, "SaveSecrets")) impl_save_secrets(self, parameters, context); else if (nm_streq(method_name, "DeleteSecrets")) impl_delete_secrets(self, parameters, context); else nm_assert_not_reached(); } /*****************************************************************************/ static void _register_state_complete(NMSecretAgentOld *self) { NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); NMCListElem * elem; gboolean any_tasks_to_complete = FALSE; if (!c_list_is_empty(&priv->pending_tasks_register_lst_head)) { /* add a dummy sentinel. We want to complete all the task we started * so far, but as we invoke user callbacks, the user might register * new tasks. Those we don't complete in this run. */ g_object_ref(self); any_tasks_to_complete = TRUE; c_list_link_tail(&priv->pending_tasks_register_lst_head, &nm_c_list_elem_new_stale(&any_tasks_to_complete)->lst); } _init_complete(self, NULL); if (any_tasks_to_complete) { while ( (elem = c_list_first_entry(&priv->pending_tasks_register_lst_head, NMCListElem, lst))) { gpointer data = nm_c_list_elem_free_steal(elem); gs_unref_object GTask *task = NULL; if (data == &any_tasks_to_complete) { any_tasks_to_complete = FALSE; break; } task = data; if (!priv->is_registered) { g_task_return_error(task, g_error_new_literal(NM_SECRET_AGENT_ERROR, NM_SECRET_AGENT_ERROR_FAILED, _("registration failed"))); continue; } g_task_return_boolean(task, TRUE); } nm_assert(!any_tasks_to_complete); g_object_unref(self); } } static void _register_state_change_do(NMSecretAgentOld *self) { NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); if (priv->is_destroyed) priv->is_enabled = FALSE; if (!priv->is_enabled || priv->registration_force_unregister || priv->name_owner_curr != priv->name_owner_next) { GetSecretsInfo *info; while ((info = c_list_first_entry(&priv->gsi_lst_head, GetSecretsInfo, gsi_lst))) { _cancel_get_secret_request(self, info, "The secret agent is going away"); _register_state_change(self); return; } priv->registration_force_unregister = FALSE; nm_clear_g_cancellable(&priv->registering_cancellable); nm_clear_g_source_inst(&priv->registering_retry_source); if (priv->registered_against_server) { priv->registered_against_server = FALSE; if (priv->name_owner_curr) { _LOGT("register: unregister from %s", priv->name_owner_curr->str); _dbus_connection_call(self, priv->name_owner_curr->str, NM_DBUS_PATH_AGENT_MANAGER, NM_DBUS_INTERFACE_AGENT_MANAGER, "Unregister", g_variant_new("()"), G_VARIANT_TYPE("()"), G_DBUS_CALL_FLAGS_NONE, _CALL_REGISTER_TIMEOUT_MSEC, NULL, NULL, NULL); } } if (!priv->is_enabled) { nm_clear_g_cancellable(&priv->name_owner_cancellable); nm_clear_g_dbus_connection_signal(priv->dbus_connection, &priv->name_owner_changed_id); nm_clear_pointer(&priv->name_owner_curr, nm_ref_string_unref); nm_clear_pointer(&priv->name_owner_next, nm_ref_string_unref); } if (priv->is_registered) { priv->is_registered = FALSE; if (!priv->is_destroyed) { _LOGT("register: now unregistered"); _notify(self, PROP_REGISTERED); _register_state_change(self); return; } } if (!priv->is_enabled) { _register_state_complete(self); return; } if (priv->name_owner_curr != priv->name_owner_next) { nm_ref_string_unref(priv->name_owner_curr); priv->name_owner_curr = nm_ref_string_ref(priv->name_owner_next); } } if (priv->name_owner_changed_id == 0) { nm_assert(!priv->name_owner_cancellable); nm_assert(!priv->name_owner_curr); nm_assert(!priv->name_owner_next); priv->name_owner_cancellable = g_cancellable_new(); priv->name_owner_changed_id = nm_dbus_connection_signal_subscribe_name_owner_changed(priv->dbus_connection, NM_DBUS_SERVICE, _name_owner_changed_cb, self, NULL); nm_dbus_connection_call_get_name_owner(priv->dbus_connection, NM_DBUS_SERVICE, -1, priv->name_owner_cancellable, _name_owner_get_cb, self); return; } if (priv->name_owner_cancellable) { /* we still wait for the name owner. Nothing to do for now. */ return; } if (!priv->name_owner_curr) { /* we don't have a name owner. We are done and wait. */ _register_state_complete(self); return; } if (priv->registering_cancellable) { /* we are already registering... wait longer. */ return; } nm_assert(!priv->registering_retry_source); if (!priv->is_registered) { /* start registering... */ priv->registering_cancellable = g_cancellable_new(); _dbus_connection_call(self, DBUS_SERVICE_DBUS, DBUS_PATH_DBUS, DBUS_INTERFACE_DBUS, "GetConnectionUnixUser", g_variant_new("(s)", priv->name_owner_curr->str), G_VARIANT_TYPE("(u)"), G_DBUS_CALL_FLAGS_NONE, _CALL_REGISTER_TIMEOUT_MSEC, priv->registering_cancellable, _get_connection_unix_user_cb, self); return; } /* we are fully registered and done. */ _register_state_complete(self); } static void _register_state_change(NMSecretAgentOld *self) { _nm_unused gs_unref_object NMSecretAgentOld *self_keep_alive = g_object_ref(self); NMSecretAgentOldPrivate * priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); nm_auto_pop_gmaincontext GMainContext *dbus_context = NULL; if (priv->register_state_change_reenter == 0) { /* We are not yet initialized. Do nothing. */ return; } if (priv->register_state_change_reenter != 1) { /* Recursive calls are prevented. Do nothing for now, but repeat * the state change afterwards. */ priv->register_state_change_reenter = 3; return; } dbus_context = nm_g_main_context_push_thread_default_if_necessary(priv->dbus_context); again: priv->register_state_change_reenter = 2; _register_state_change_do(self); if (priv->register_state_change_reenter != 2) goto again; priv->register_state_change_reenter = 1; } /*****************************************************************************/ static void _init_complete(NMSecretAgentOld *self, GError *error_take) { NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); gs_free_error GError *error = g_steal_pointer(&error_take); GError * error_cancelled = NULL; if (!priv->init_data) return; if (g_cancellable_set_error_if_cancelled(priv->init_data->cancellable, &error_cancelled)) { g_clear_error(&error); g_propagate_error(&error, error_cancelled); } priv->is_initialized = (!error); _LOGT("%s init complete with %s%s%s", priv->init_data->is_sync ? "sync" : "async", NM_PRINT_FMT_QUOTED(error_take, "error: ", error_take->message, "", "success")); nml_init_data_return(g_steal_pointer(&priv->init_data), g_steal_pointer(&error)); } static void _init_register_object(NMSecretAgentOld *self) { NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); gs_free_error GError *error = NULL; GDBusInterfaceVTable interface_vtable = { .method_call = _method_call, }; if (g_cancellable_set_error_if_cancelled(priv->init_data->cancellable, &error)) { _init_complete(self, g_steal_pointer(&error)); return; } priv->exported_id = g_dbus_connection_register_object(priv->dbus_connection, NM_DBUS_PATH_SECRET_AGENT, (GDBusInterfaceInfo *) &interface_info, &interface_vtable, self, NULL, &error); if (priv->exported_id == 0) { _init_complete(self, g_steal_pointer(&error)); return; } priv->register_state_change_reenter = 1; _register_state_change(self); } static void _init_got_bus(GObject *initable, GAsyncResult *result, gpointer user_data) { NMSecretAgentOld * self = user_data; NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); gs_free_error GError *error = NULL; priv->dbus_connection = g_bus_get_finish(result, &error); if (!priv->dbus_connection) { _init_complete(self, g_steal_pointer(&error)); return; } _LOGT("init: got GDBusConnection"); _notify(self, PROP_DBUS_CONNECTION); _init_register_object(self); } static void _init_start(NMSecretAgentOld *self) { NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); if (!priv->dbus_connection) { GBusType bus_type; bus_type = _nm_dbus_bus_type(); priv->session_bus = (bus_type == G_BUS_TYPE_SESSION); g_bus_get(bus_type, priv->init_data->cancellable, _init_got_bus, self); return; } _init_register_object(self); } static void init_async(GAsyncInitable * initable, int io_priority, GCancellable * cancellable, GAsyncReadyCallback callback, gpointer user_data) { NMSecretAgentOld * self; NMSecretAgentOldClass * klass; NMSecretAgentOldPrivate *priv; nm_auto_pop_gmaincontext GMainContext *dbus_context = NULL; gs_unref_object GTask *task = NULL; g_return_if_fail(NM_IS_SECRET_AGENT_OLD(initable)); self = NM_SECRET_AGENT_OLD(initable); priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); g_return_if_fail(!priv->dbus_context); g_return_if_fail(!priv->is_destroyed); klass = NM_SECRET_AGENT_OLD_GET_CLASS(self); g_return_if_fail(klass->get_secrets); g_return_if_fail(klass->cancel_get_secrets); g_return_if_fail(klass->save_secrets); g_return_if_fail(klass->delete_secrets); _LOGT("init-async starting..."); priv->dbus_context = g_main_context_ref(priv->main_context); dbus_context = nm_g_main_context_push_thread_default_if_necessary(priv->dbus_context); task = nm_g_task_new(self, cancellable, init_async, callback, user_data); g_task_set_priority(task, io_priority); priv->init_data = nml_init_data_new_async(cancellable, g_steal_pointer(&task)); _init_start(self); } static gboolean init_finish(GAsyncInitable *initable, GAsyncResult *result, GError **error) { g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(initable), FALSE); g_return_val_if_fail(nm_g_task_is_valid(result, initable, init_async), FALSE); return g_task_propagate_boolean(G_TASK(result), error); } /*****************************************************************************/ static gboolean init_sync(GInitable *initable, GCancellable *cancellable, GError **error) { gs_unref_object NMSecretAgentOld *self = NULL; NMSecretAgentOldPrivate * priv; NMSecretAgentOldClass * klass; GMainLoop * main_loop; GError * local_error = NULL; g_return_val_if_fail(NM_IS_SECRET_AGENT_OLD(initable), FALSE); self = g_object_ref(NM_SECRET_AGENT_OLD(initable)); priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); g_return_val_if_fail(!priv->dbus_context, FALSE); g_return_val_if_fail(!priv->is_destroyed, FALSE); klass = NM_SECRET_AGENT_OLD_GET_CLASS(self); g_return_val_if_fail(klass->get_secrets, FALSE); g_return_val_if_fail(klass->cancel_get_secrets, FALSE); g_return_val_if_fail(klass->save_secrets, FALSE); g_return_val_if_fail(klass->delete_secrets, FALSE); _LOGT("init-sync"); /* See NMClient's sync-init method for explanation about why we create * an internal GMainContext priv->dbus_context. */ priv->dbus_context = g_main_context_new(); g_main_context_push_thread_default(priv->dbus_context); main_loop = g_main_loop_new(priv->dbus_context, FALSE); priv->init_data = nml_init_data_new_sync(cancellable, main_loop, &local_error); _init_start(self); g_main_loop_run(main_loop); g_main_loop_unref(main_loop); g_main_context_pop_thread_default(priv->dbus_context); nm_context_busy_watcher_integrate_source(priv->main_context, priv->dbus_context, priv->context_busy_watcher); if (local_error) { g_propagate_error(error, local_error); return FALSE; } return TRUE; } /*****************************************************************************/ static void get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(object); switch (prop_id) { case PROP_DBUS_CONNECTION: g_value_set_object(value, priv->dbus_connection); break; 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->is_registered); break; case PROP_CAPABILITIES: g_value_set_flags(value, priv->capabilities); 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) { NMSecretAgentOld * self = NM_SECRET_AGENT_OLD(object); NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); guint u; switch (prop_id) { case PROP_DBUS_CONNECTION: /* construct-only */ priv->dbus_connection = g_value_dup_object(value); break; case PROP_IDENTIFIER: /* construct-only */ priv->identifier = g_value_dup_string(value); g_return_if_fail(validate_identifier(priv->identifier)); break; case PROP_AUTO_REGISTER: /* construct */ priv->auto_register = g_value_get_boolean(value); priv->is_enabled = priv->auto_register; _register_state_change(self); break; case PROP_CAPABILITIES: /* construct */ u = g_value_get_flags(value); if (u != priv->capabilities) { priv->capabilities = u; priv->registration_force_unregister = TRUE; _register_state_change(self); } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } /*****************************************************************************/ static void nm_secret_agent_old_init(NMSecretAgentOld *self) { NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); _LOGT("create new instance"); c_list_init(&priv->gsi_lst_head); c_list_init(&priv->pending_tasks_register_lst_head); priv->main_context = g_main_context_ref_thread_default(); priv->context_busy_watcher = g_object_new(G_TYPE_OBJECT, NULL); } static void dispose(GObject *object) { NMSecretAgentOld *self = NM_SECRET_AGENT_OLD(object); _LOGT("disposing"); _secret_agent_old_destroy(self); G_OBJECT_CLASS(nm_secret_agent_old_parent_class)->dispose(object); } static void finalize(GObject *object) { NMSecretAgentOld * self = NM_SECRET_AGENT_OLD(object); NMSecretAgentOldPrivate *priv = NM_SECRET_AGENT_OLD_GET_PRIVATE(self); _LOGT("finalizing"); if (priv->dbus_context) { nml_cleanup_context_busy_watcher_on_idle(g_steal_pointer(&priv->context_busy_watcher), priv->dbus_context); } g_clear_object(&priv->dbus_connection); nm_clear_pointer(&priv->dbus_context, g_main_context_unref); nm_clear_pointer(&priv->main_context, g_main_context_unref); g_clear_object(&priv->context_busy_watcher); g_free(priv->identifier); G_OBJECT_CLASS(nm_secret_agent_old_parent_class)->finalize(object); } static void nm_secret_agent_old_class_init(NMSecretAgentOldClass *class) { GObjectClass *object_class = G_OBJECT_CLASS(class); g_type_class_add_private(class, sizeof(NMSecretAgentOldPrivate)); object_class->get_property = get_property; object_class->set_property = set_property; object_class->dispose = dispose; object_class->finalize = finalize; /** * NMSecretAgentOld:dbus-connection: * * The #GDBusConnection used by the instance. You may either set this * as construct-only property, or otherwise #NMSecretAgentOld will choose * a connection via g_bus_get() during initialization. * * Since: 1.24 **/ obj_properties[PROP_DBUS_CONNECTION] = g_param_spec_object(NM_SECRET_AGENT_OLD_DBUS_CONNECTION, "", "", G_TYPE_DBUS_CONNECTION, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); /** * NMSecretAgentOld: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). **/ obj_properties[PROP_IDENTIFIER] = g_param_spec_string(NM_SECRET_AGENT_OLD_IDENTIFIER, "", "", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); /** * NMSecretAgentOld:auto-register: * * If %TRUE (the default), the agent will always be registered when * NetworkManager is running; if NetworkManager exits and restarts, the * agent will re-register itself automatically. * * In particular, if this property is %TRUE at construct time, then the * agent will register itself with NetworkManager during * construction/initialization and initialization will only complete * after registration is completed (either successfully or unsuccessfully). * Since 1.24, a failure to register will no longer cause initialization * of #NMSecretAgentOld to fail. * * If the property is %FALSE, the agent will not automatically register with * NetworkManager, and nm_secret_agent_old_enable() or * nm_secret_agent_old_register_async() must be called to register it. * * Calling nm_secret_agent_old_enable() has the same effect as setting this * property. **/ obj_properties[PROP_AUTO_REGISTER] = g_param_spec_boolean(NM_SECRET_AGENT_OLD_AUTO_REGISTER, "", "", TRUE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); /** * NMSecretAgentOld:registered: * * %TRUE if the agent is registered with NetworkManager, %FALSE if not. **/ obj_properties[PROP_REGISTERED] = g_param_spec_boolean(NM_SECRET_AGENT_OLD_REGISTERED, "", "", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * NMSecretAgentOld:capabilities: * * A bitfield of %NMSecretAgentCapabilities. * * Changing this property is possible at any time. In case the secret * agent is currently registered, this will cause a re-registration. **/ obj_properties[PROP_CAPABILITIES] = g_param_spec_flags(NM_SECRET_AGENT_OLD_CAPABILITIES, "", "", NM_TYPE_SECRET_AGENT_CAPABILITIES, NM_SECRET_AGENT_CAPABILITY_NONE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); g_object_class_install_properties(object_class, _PROPERTY_ENUMS_LAST, obj_properties); } static void nm_secret_agent_old_initable_iface_init(GInitableIface *iface) { iface->init = init_sync; } static void nm_secret_agent_old_async_initable_iface_init(GAsyncInitableIface *iface) { iface->init_async = init_async; iface->init_finish = init_finish; }