From 760810cd751c8a511b4eb58c0159fef1163f7355 Mon Sep 17 00:00:00 2001
From: Ray Strode <rstrode@redhat.com>
Date: Fri, 25 Sep 2020 15:27:47 -0400
Subject: [PATCH 15/15] libgdm: Don't leak user verifier extensions on unlock
GdmClient fails to free the hash table associated with user
verifier extensions when the client is done with the user verifier.
This commit ties the user verifier extensions to the user verifier
instance associated with it, instead of storing the extensions
directly in the client struct.
---
libgdm/gdm-client.c | 70 +++++++++++++++++++++++++++++++--------------
1 file changed, 48 insertions(+), 22 deletions(-)
diff --git a/libgdm/gdm-client.c b/libgdm/gdm-client.c
index afe78087f..752961227 100644
--- a/libgdm/gdm-client.c
+++ b/libgdm/gdm-client.c
@@ -147,193 +147,204 @@ user_verifier_data_new (GTask *task, GdmUserVerifier *user_verifier)
data = g_slice_new (UserVerifierData);
data->task = g_object_ref (task);
data->user_verifier = g_object_ref (user_verifier);
return data;
}
static void
user_verifier_data_free (UserVerifierData *data)
{
g_object_unref (data->task);
g_object_unref (data->user_verifier);
g_slice_free (UserVerifierData, data);
}
static void
complete_user_verifier_proxy_operation (GdmClient *client,
UserVerifierData *data)
{
g_task_return_pointer (data->task,
g_object_ref (data->user_verifier),
(GDestroyNotify) g_object_unref);
user_verifier_data_free (data);
}
static void
maybe_complete_user_verifier_proxy_operation (GdmClient *client,
UserVerifierData *data)
{
+ GHashTable *user_verifier_extensions;
GHashTableIter iter;
gpointer key, value;
- if (client->priv->user_verifier_extensions != NULL) {
- g_hash_table_iter_init (&iter, client->priv->user_verifier_extensions);
+ user_verifier_extensions = g_object_get_data (G_OBJECT (client->priv->user_verifier), "gdm-client-user-verifier-extensions");
+ if (user_verifier_extensions != NULL) {
+ g_hash_table_iter_init (&iter, user_verifier_extensions);
while (g_hash_table_iter_next (&iter, &key, &value)) {
if (value == NULL)
return;
}
}
complete_user_verifier_proxy_operation (client, data);
}
static void
on_user_verifier_choice_list_proxy_created (GObject *source,
GAsyncResult *result,
UserVerifierData *data)
{
+ GHashTable *user_verifier_extensions;
g_autoptr(GdmClient) client = NULL;
GdmUserVerifierChoiceList *choice_list;
g_autoptr(GError) error = NULL;
client = GDM_CLIENT (g_async_result_get_source_object (G_ASYNC_RESULT (data->task)));
+ user_verifier_extensions = g_object_get_data (G_OBJECT (data->user_verifier), "gdm-client-user-verifier-extensions");
choice_list = gdm_user_verifier_choice_list_proxy_new_finish (result, &error);
if (choice_list == NULL) {
g_debug ("Couldn't create UserVerifier ChoiceList proxy: %s", error->message);
- g_hash_table_remove (client->priv->user_verifier_extensions, gdm_user_verifier_choice_list_interface_info ()->name);
+ g_hash_table_remove (user_verifier_extensions, gdm_user_verifier_choice_list_interface_info ()->name);
} else {
- g_hash_table_replace (client->priv->user_verifier_extensions, gdm_user_verifier_choice_list_interface_info ()->name, choice_list);
+ g_hash_table_replace (user_verifier_extensions, gdm_user_verifier_choice_list_interface_info ()->name, choice_list);
}
maybe_complete_user_verifier_proxy_operation (client, data);
}
static void
on_user_verifier_extensions_enabled (GdmUserVerifier *user_verifier,
GAsyncResult *result,
UserVerifierData *data)
{
g_autoptr(GdmClient) client = NULL;
+ GHashTable *user_verifier_extensions;
GCancellable *cancellable;
GDBusConnection *connection;
g_autoptr(GError) error = NULL;
size_t i;
client = GDM_CLIENT (g_async_result_get_source_object (G_ASYNC_RESULT (data->task)));
cancellable = g_task_get_cancellable (data->task);
+ user_verifier_extensions = g_object_get_data (G_OBJECT (user_verifier), "gdm-client-user-verifier-extensions");
gdm_user_verifier_call_enable_extensions_finish (user_verifier, result, &error);
if (error != NULL) {
g_debug ("Couldn't enable user verifier extensions: %s",
error->message);
complete_user_verifier_proxy_operation (client, data);
return;
}
connection = g_dbus_proxy_get_connection (G_DBUS_PROXY (user_verifier));
for (i = 0; client->priv->enabled_extensions[i] != NULL; i++) {
g_debug ("Enabled extensions[%lu] = %s", i, client->priv->enabled_extensions[i]);
- g_hash_table_insert (client->priv->user_verifier_extensions, client->priv->enabled_extensions[i], NULL);
+ g_hash_table_insert (user_verifier_extensions, client->priv->enabled_extensions[i], NULL);
if (strcmp (client->priv->enabled_extensions[i],
gdm_user_verifier_choice_list_interface_info ()->name) == 0) {
- g_hash_table_insert (client->priv->user_verifier_extensions, client->priv->enabled_extensions[i], NULL);
+ g_hash_table_insert (user_verifier_extensions, client->priv->enabled_extensions[i], NULL);
gdm_user_verifier_choice_list_proxy_new (connection,
G_DBUS_PROXY_FLAGS_NONE,
NULL,
SESSION_DBUS_PATH,
cancellable,
(GAsyncReadyCallback)
on_user_verifier_choice_list_proxy_created,
data);
} else {
g_debug ("User verifier extension %s is unsupported", client->priv->enabled_extensions[i]);
- g_hash_table_remove (client->priv->user_verifier_extensions,
+ g_hash_table_remove (user_verifier_extensions,
client->priv->enabled_extensions[i]);
}
}
- if (g_hash_table_size (client->priv->user_verifier_extensions) == 0) {
+ if (g_hash_table_size (user_verifier_extensions) == 0) {
g_debug ("No supported user verifier extensions");
complete_user_verifier_proxy_operation (client, data);
}
}
static void
free_interface_skeleton (GDBusInterfaceSkeleton *interface)
{
if (interface == NULL)
return;
g_object_unref (interface);
}
static void
on_user_verifier_proxy_created (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
g_autoptr(GdmClient) self = NULL;
+ GHashTable *user_verifier_extensions;
GCancellable *cancellable = NULL;
g_autoptr(GdmUserVerifier) user_verifier = NULL;
g_autoptr(GTask) task = user_data;
g_autoptr(GError) error = NULL;
user_verifier = gdm_user_verifier_proxy_new_finish (result, &error);
if (user_verifier == NULL) {
g_task_return_error (task, g_steal_pointer (&error));
return;
}
g_debug ("UserVerifier %p created", user_verifier);
self = GDM_CLIENT (g_async_result_get_source_object (G_ASYNC_RESULT (task)));
if (self->priv->enabled_extensions == NULL) {
g_debug ("no enabled extensions");
g_task_return_pointer (task,
g_steal_pointer (&user_verifier),
(GDestroyNotify) g_object_unref);
return;
}
- self->priv->user_verifier_extensions = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- NULL,
- (GDestroyNotify)
- free_interface_skeleton);
+ user_verifier_extensions = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ NULL,
+ (GDestroyNotify)
+ free_interface_skeleton);
+ g_object_set_data_full (G_OBJECT (user_verifier),
+ "gdm-client-user-verifier-extensions",
+ user_verifier_extensions,
+ (GDestroyNotify) g_hash_table_unref);
cancellable = g_task_get_cancellable (task);
gdm_user_verifier_call_enable_extensions (user_verifier,
(const char * const *)
self->priv->enabled_extensions,
cancellable,
(GAsyncReadyCallback)
on_user_verifier_extensions_enabled,
user_verifier_data_new (task, user_verifier));
}
static void
on_reauthentication_channel_connected (GObject *source_object,
GAsyncResult *result,
gpointer user_data)
{
GCancellable *cancellable;
g_autoptr(GTask) task = user_data;
g_autoptr(GDBusConnection) connection = NULL;
g_autoptr(GError) error = NULL;
connection = g_dbus_connection_new_for_address_finish (result, &error);
if (!connection) {
g_task_return_error (task, g_steal_pointer (&error));
return;
}
cancellable = g_task_get_cancellable (task);
gdm_user_verifier_proxy_new (connection,
G_DBUS_PROXY_FLAGS_NONE,
NULL,
@@ -637,87 +648,93 @@ gdm_client_open_reauthentication_channel_finish (GdmClient *client,
*/
GdmUserVerifier *
gdm_client_get_user_verifier_sync (GdmClient *client,
GCancellable *cancellable,
GError **error)
{
g_autoptr(GDBusConnection) connection = NULL;
if (client->priv->user_verifier != NULL) {
return g_object_ref (client->priv->user_verifier);
}
connection = gdm_client_get_connection_sync (client, cancellable, error);
if (connection == NULL) {
return NULL;
}
client->priv->user_verifier = gdm_user_verifier_proxy_new_sync (connection,
G_DBUS_PROXY_FLAGS_NONE,
NULL,
SESSION_DBUS_PATH,
cancellable,
error);
if (client->priv->user_verifier != NULL) {
g_object_add_weak_pointer (G_OBJECT (client->priv->user_verifier),
(gpointer *)
&client->priv->user_verifier);
if (client->priv->enabled_extensions != NULL) {
+ GHashTable *user_verifier_extensions;
gboolean res;
- client->priv->user_verifier_extensions = g_hash_table_new_full (g_str_hash,
- g_str_equal,
- NULL,
- (GDestroyNotify)
- free_interface_skeleton);
+ user_verifier_extensions = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ NULL,
+ (GDestroyNotify)
+ free_interface_skeleton);
+ g_object_set_data_full (G_OBJECT (client->priv->user_verifier),
+ "gdm-client-user-verifier-extensions",
+ user_verifier_extensions,
+ (GDestroyNotify) g_hash_table_unref);
+
res = gdm_user_verifier_call_enable_extensions_sync (client->priv->user_verifier,
(const char * const *)
client->priv->enabled_extensions,
cancellable,
NULL);
if (res) {
size_t i;
for (i = 0; client->priv->enabled_extensions[i] != NULL; i++) {
if (strcmp (client->priv->enabled_extensions[i],
gdm_user_verifier_choice_list_interface_info ()->name) == 0) {
GdmUserVerifierChoiceList *choice_list_interface;
choice_list_interface = gdm_user_verifier_choice_list_proxy_new_sync (connection,
G_DBUS_PROXY_FLAGS_NONE,
NULL,
SESSION_DBUS_PATH,
cancellable,
NULL);
if (choice_list_interface != NULL)
- g_hash_table_insert (client->priv->user_verifier_extensions, client->priv->enabled_extensions[i], choice_list_interface);
+ g_hash_table_insert (user_verifier_extensions, client->priv->enabled_extensions[i], choice_list_interface);
}
}
}
}
}
return client->priv->user_verifier;
}
static void
on_connection_for_user_verifier (GdmClient *client,
GAsyncResult *result,
gpointer user_data)
{
GCancellable *cancellable;
g_autoptr(GTask) task = user_data;
g_autoptr(GDBusConnection) connection = NULL;
g_autoptr(GError) error = NULL;
connection = gdm_client_get_connection_finish (client, result, &error);
if (connection == NULL) {
g_task_return_error (task, g_steal_pointer (&error));
return;
}
cancellable = g_task_get_cancellable (task);
gdm_user_verifier_proxy_new (connection,
G_DBUS_PROXY_FLAGS_NONE,
NULL,
SESSION_DBUS_PATH,
@@ -765,86 +782,95 @@ gdm_client_get_user_verifier (GdmClient *client,
g_steal_pointer (&task));
}
/**
* gdm_client_get_user_verifier_finish:
* @client: a #GdmClient
* @result: The #GAsyncResult from the callback
* @error: a #GError
*
* Finishes an operation started with
* gdm_client_get_user_verifier().
*
* Returns: (transfer full): a #GdmUserVerifier
*/
GdmUserVerifier *
gdm_client_get_user_verifier_finish (GdmClient *client,
GAsyncResult *result,
GError **error)
{
GdmUserVerifier *user_verifier;
g_return_val_if_fail (GDM_IS_CLIENT (client), NULL);
if (client->priv->user_verifier != NULL)
return g_object_ref (client->priv->user_verifier);
user_verifier = g_task_propagate_pointer (G_TASK (result), error);
if (user_verifier == NULL)
return NULL;
- client->priv->user_verifier = user_verifier;
+ if (client->priv->user_verifier != NULL) {
+ g_object_remove_weak_pointer (G_OBJECT (client->priv->user_verifier),
+ (gpointer *)
+ &client->priv->user_verifier);
+ }
g_object_add_weak_pointer (G_OBJECT (client->priv->user_verifier),
(gpointer *)
&client->priv->user_verifier);
+ client->priv->user_verifier = user_verifier;
+
return user_verifier;
}
/**
* gdm_client_get_user_verifier_choice_list:
* @client: a #GdmClient
*
* Gets a #GdmUserVerifierChoiceList object that can be used to
* verify a user's local account.
*
* Returns: (transfer none): #GdmUserVerifierChoiceList or %NULL if user
* verifier isn't yet fetched, or daemon doesn't support choice lists
*/
GdmUserVerifierChoiceList *
gdm_client_get_user_verifier_choice_list (GdmClient *client)
{
- if (client->priv->user_verifier_extensions == NULL)
+ GHashTable *user_verifier_extensions;
+
+ user_verifier_extensions = g_object_get_data (G_OBJECT (client->priv->user_verifier), "gdm-client-user-verifier-extensions");
+ if (user_verifier_extensions == NULL)
return NULL;
- return g_hash_table_lookup (client->priv->user_verifier_extensions,
+ return g_hash_table_lookup (user_verifier_extensions,
gdm_user_verifier_choice_list_interface_info ()->name);
}
static void
on_timed_login_details_got (GdmGreeter *greeter,
GAsyncResult *result)
{
gdm_greeter_call_get_timed_login_details_finish (greeter, NULL, NULL, NULL, result, NULL);
}
static void
query_for_timed_login_requested_signal (GdmGreeter *greeter)
{
/* This just makes sure a timed-login-requested signal gets fired
* off if appropriate.
*/
gdm_greeter_call_get_timed_login_details (greeter,
NULL,
(GAsyncReadyCallback)
on_timed_login_details_got,
NULL);
}
static void
on_greeter_proxy_created (GObject *source,
GAsyncResult *result,
gpointer user_data)
{
g_autoptr(GTask) task = user_data;
g_autoptr(GError) error = NULL;
--
2.26.2