/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2012 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * Authors: Ray Strode */ #include "config.h" #include "gsd-smartcard-service.h" #include "org.gnome.SettingsDaemon.Smartcard.h" #include "gsd-smartcard-manager.h" #include "gsd-smartcard-enum-types.h" #include "gsd-smartcard-utils.h" #include #include #include struct _GsdSmartcardService { GsdSmartcardServiceManagerSkeleton parent; GDBusConnection *bus_connection; GDBusObjectManagerServer *object_manager_server; GsdSmartcardManager *smartcard_manager; GCancellable *cancellable; GHashTable *tokens; gboolean login_token_bound; guint name_id; }; #define GSD_DBUS_NAME "org.gnome.SettingsDaemon" #define GSD_DBUS_PATH "/org/gnome/SettingsDaemon" #define GSD_DBUS_BASE_INTERFACE "org.gnome.SettingsDaemon" #define GSD_SMARTCARD_DBUS_NAME GSD_DBUS_NAME ".Smartcard" #define GSD_SMARTCARD_DBUS_PATH GSD_DBUS_PATH "/Smartcard" #define GSD_SMARTCARD_MANAGER_DBUS_PATH GSD_SMARTCARD_DBUS_PATH "/Manager" #define GSD_SMARTCARD_MANAGER_DRIVERS_DBUS_PATH GSD_SMARTCARD_MANAGER_DBUS_PATH "/Drivers" #define GSD_SMARTCARD_MANAGER_TOKENS_DBUS_PATH GSD_SMARTCARD_MANAGER_DBUS_PATH "/Tokens" enum { PROP_0, PROP_MANAGER, PROP_BUS_CONNECTION }; static void gsd_smartcard_service_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *param_spec); static void gsd_smartcard_service_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *param_spec); static void async_initable_interface_init (GAsyncInitableIface *interface); static void smartcard_service_manager_interface_init (GsdSmartcardServiceManagerIface *interface); G_LOCK_DEFINE_STATIC (gsd_smartcard_tokens); G_DEFINE_TYPE_WITH_CODE (GsdSmartcardService, gsd_smartcard_service, GSD_SMARTCARD_SERVICE_TYPE_MANAGER_SKELETON, G_IMPLEMENT_INTERFACE (G_TYPE_ASYNC_INITABLE, async_initable_interface_init) G_IMPLEMENT_INTERFACE (GSD_SMARTCARD_SERVICE_TYPE_MANAGER, smartcard_service_manager_interface_init)); static void set_bus_connection (GsdSmartcardService *self, GDBusConnection *connection) { if (self->bus_connection != connection) { g_clear_object (&self->bus_connection); self->bus_connection = g_object_ref (connection); g_object_notify (G_OBJECT (self), "bus-connection"); } } static void register_object_manager (GsdSmartcardService *self) { GsdSmartcardServiceObjectSkeleton *object; self->object_manager_server = g_dbus_object_manager_server_new (GSD_SMARTCARD_DBUS_PATH); object = gsd_smartcard_service_object_skeleton_new (GSD_SMARTCARD_MANAGER_DBUS_PATH); gsd_smartcard_service_object_skeleton_set_manager (object, GSD_SMARTCARD_SERVICE_MANAGER (self)); g_dbus_object_manager_server_export (self->object_manager_server, G_DBUS_OBJECT_SKELETON (object)); g_object_unref (object); g_dbus_object_manager_server_set_connection (self->object_manager_server, self->bus_connection); } static const char * get_login_token_object_path (GsdSmartcardService *self) { return GSD_SMARTCARD_MANAGER_TOKENS_DBUS_PATH "/login_token"; } static void register_login_token_alias (GsdSmartcardService *self) { GDBusObjectSkeleton *object; GDBusInterfaceSkeleton *interface; const char *object_path; const char *token_name; token_name = g_getenv ("PKCS11_LOGIN_TOKEN_NAME"); if (token_name == NULL) return; object_path = get_login_token_object_path (self); object = G_DBUS_OBJECT_SKELETON (gsd_smartcard_service_object_skeleton_new (object_path)); interface = G_DBUS_INTERFACE_SKELETON (gsd_smartcard_service_token_skeleton_new ()); g_dbus_object_skeleton_add_interface (object, interface); g_object_unref (interface); g_object_set (G_OBJECT (interface), "name", token_name, "used-to-login", TRUE, "is-inserted", FALSE, NULL); g_dbus_object_manager_server_export (self->object_manager_server, object); G_LOCK (gsd_smartcard_tokens); g_hash_table_insert (self->tokens, g_strdup (object_path), interface); G_UNLOCK (gsd_smartcard_tokens); } static void on_bus_gotten (GObject *source_object, GAsyncResult *result, GTask *task) { GsdSmartcardService *self; GDBusConnection *connection; GError *error = NULL; connection = g_bus_get_finish (result, &error); if (connection == NULL) { g_task_return_error (task, error); goto out; } g_debug ("taking name %s on session bus", GSD_SMARTCARD_DBUS_NAME); self = g_task_get_source_object (task); set_bus_connection (self, connection); register_object_manager (self); self->name_id = g_bus_own_name_on_connection (connection, GSD_SMARTCARD_DBUS_NAME, G_BUS_NAME_OWNER_FLAGS_NONE, NULL, NULL, NULL, NULL); /* In case the login token is removed at start up, register an * an alias interface that's always around */ register_login_token_alias (self); g_task_return_boolean (task, TRUE); out: g_object_unref (task); return; } static gboolean gsd_smartcard_service_initable_init_finish (GAsyncInitable *initable, GAsyncResult *result, GError **error) { GTask *task; task = G_TASK (result); return g_task_propagate_boolean (task, error); } static void gsd_smartcard_service_initable_init_async (GAsyncInitable *initable, int io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GsdSmartcardService *self = GSD_SMARTCARD_SERVICE (initable); GTask *task; task = g_task_new (G_OBJECT (self), cancellable, callback, user_data); g_task_set_priority (task, io_priority); g_bus_get (G_BUS_TYPE_SESSION, cancellable, (GAsyncReadyCallback) on_bus_gotten, task); } static void async_initable_interface_init (GAsyncInitableIface *interface) { interface->init_async = gsd_smartcard_service_initable_init_async; interface->init_finish = gsd_smartcard_service_initable_init_finish; } static char * get_object_path_for_token (GsdSmartcardService *self, PK11SlotInfo *card_slot) { char *object_path; char *escaped_library_path; SECMODModule *driver; CK_SLOT_ID slot_id; driver = PK11_GetModule (card_slot); slot_id = PK11_GetSlotID (card_slot); escaped_library_path = gsd_smartcard_utils_escape_object_path (driver->dllName); object_path = g_strdup_printf ("%s/token_from_%s_slot_%lu", GSD_SMARTCARD_MANAGER_TOKENS_DBUS_PATH, escaped_library_path, (gulong) slot_id); g_free (escaped_library_path); return object_path; } static gboolean gsd_smartcard_service_handle_get_login_token (GsdSmartcardServiceManager *manager, GDBusMethodInvocation *invocation) { GsdSmartcardService *self = GSD_SMARTCARD_SERVICE (manager); PK11SlotInfo *card_slot; char *object_path; card_slot = gsd_smartcard_manager_get_login_token (self->smartcard_manager); if (card_slot == NULL) { const char *login_token_object_path; /* If we know there's a login token but it was removed before the * smartcard manager could examine it, just return the generic login * token object path */ login_token_object_path = get_login_token_object_path (self); if (g_hash_table_contains (self->tokens, login_token_object_path)) { gsd_smartcard_service_manager_complete_get_login_token (manager, invocation, login_token_object_path); return TRUE; } g_dbus_method_invocation_return_error (invocation, GSD_SMARTCARD_MANAGER_ERROR, GSD_SMARTCARD_MANAGER_ERROR_FINDING_SMARTCARD, _("User was not logged in with smartcard.")); return TRUE; } object_path = get_object_path_for_token (self, card_slot); gsd_smartcard_service_manager_complete_get_login_token (manager, invocation, object_path); g_free (object_path); return TRUE; } static gboolean gsd_smartcard_service_handle_get_inserted_tokens (GsdSmartcardServiceManager *manager, GDBusMethodInvocation *invocation) { GsdSmartcardService *self = GSD_SMARTCARD_SERVICE (manager); GList *inserted_tokens, *node; GPtrArray *object_paths; inserted_tokens = gsd_smartcard_manager_get_inserted_tokens (self->smartcard_manager, NULL); object_paths = g_ptr_array_new (); for (node = inserted_tokens; node != NULL; node = node->next) { PK11SlotInfo *card_slot = node->data; char *object_path; object_path = get_object_path_for_token (self, card_slot); g_ptr_array_add (object_paths, object_path); } g_ptr_array_add (object_paths, NULL); g_list_free (inserted_tokens); gsd_smartcard_service_manager_complete_get_inserted_tokens (manager, invocation, (const char * const *) object_paths->pdata); g_ptr_array_free (object_paths, TRUE); return TRUE; } static void smartcard_service_manager_interface_init (GsdSmartcardServiceManagerIface *interface) { interface->handle_get_login_token = gsd_smartcard_service_handle_get_login_token; interface->handle_get_inserted_tokens = gsd_smartcard_service_handle_get_inserted_tokens; } static void gsd_smartcard_service_init (GsdSmartcardService *self) { self->tokens = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, NULL); } static void gsd_smartcard_service_dispose (GObject *object) { GsdSmartcardService *self = GSD_SMARTCARD_SERVICE (object); g_clear_object (&self->bus_connection); g_clear_object (&self->object_manager_server); g_clear_object (&self->smartcard_manager); g_cancellable_cancel (self->cancellable); g_clear_object (&self->cancellable); g_clear_pointer (&self->tokens, g_hash_table_unref); G_OBJECT_CLASS (gsd_smartcard_service_parent_class)->dispose (object); } static void gsd_smartcard_service_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *param_spec) { GsdSmartcardService *self = GSD_SMARTCARD_SERVICE (object); switch (property_id) { case PROP_MANAGER: self->smartcard_manager = g_value_dup_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, param_spec); break; } } static void gsd_smartcard_service_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *param_spec) { GsdSmartcardService *self = GSD_SMARTCARD_SERVICE (object); switch (property_id) { case PROP_MANAGER: g_value_set_object (value, self->smartcard_manager); break; case PROP_BUS_CONNECTION: g_value_set_object (value, self->bus_connection); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, param_spec); break; } } static void gsd_smartcard_service_class_init (GsdSmartcardServiceClass *service_class) { GObjectClass *object_class = G_OBJECT_CLASS (service_class); GParamSpec *param_spec; object_class->dispose = gsd_smartcard_service_dispose; object_class->set_property = gsd_smartcard_service_set_property; object_class->get_property = gsd_smartcard_service_get_property; param_spec = g_param_spec_object ("manager", "Smartcard Manager", "Smartcard Manager", GSD_TYPE_SMARTCARD_MANAGER, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_MANAGER, param_spec); param_spec = g_param_spec_object ("bus-connection", "Bus Connection", "bus connection", G_TYPE_DBUS_CONNECTION, G_PARAM_READABLE); g_object_class_install_property (object_class, PROP_BUS_CONNECTION, param_spec); } static void on_new_async_finished (GObject *source_object, GAsyncResult *result, GTask *task) { GError *error = NULL; GObject *object; object = g_async_initable_new_finish (G_ASYNC_INITABLE (source_object), result, &error); if (object == NULL) { g_task_return_error (task, error); goto out; } g_assert (GSD_IS_SMARTCARD_SERVICE (object)); g_task_return_pointer (task, object, g_object_unref); out: g_object_unref (task); return; } void gsd_smartcard_service_new_async (GsdSmartcardManager *manager, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (NULL, cancellable, callback, user_data); g_async_initable_new_async (GSD_TYPE_SMARTCARD_SERVICE, G_PRIORITY_DEFAULT, cancellable, (GAsyncReadyCallback) on_new_async_finished, task, "manager", manager, NULL); } GsdSmartcardService * gsd_smartcard_service_new_finish (GAsyncResult *result, GError **error) { GTask *task; GsdSmartcardService *self = NULL; task = G_TASK (result); self = g_task_propagate_pointer (task, error); if (self == NULL) return self; return g_object_ref (self); } static char * get_object_path_for_driver (GsdSmartcardService *self, SECMODModule *driver) { char *object_path; char *escaped_library_path; escaped_library_path = gsd_smartcard_utils_escape_object_path (driver->dllName); object_path = g_build_path ("/", GSD_SMARTCARD_MANAGER_DRIVERS_DBUS_PATH, escaped_library_path, NULL); g_free (escaped_library_path); return object_path; } void gsd_smartcard_service_register_driver (GsdSmartcardService *self, SECMODModule *driver) { char *object_path; GDBusObjectSkeleton *object; GDBusInterfaceSkeleton *interface; object_path = get_object_path_for_driver (self, driver); object = G_DBUS_OBJECT_SKELETON (gsd_smartcard_service_object_skeleton_new (object_path)); g_free (object_path); interface = G_DBUS_INTERFACE_SKELETON (gsd_smartcard_service_driver_skeleton_new ()); g_dbus_object_skeleton_add_interface (object, interface); g_object_unref (interface); g_object_set (G_OBJECT (interface), "library", driver->dllName, "description", driver->commonName, NULL); g_dbus_object_manager_server_export (self->object_manager_server, object); g_object_unref (object); } static void synchronize_token_now (GsdSmartcardService *self, PK11SlotInfo *card_slot) { GDBusInterfaceSkeleton *interface; char *object_path; const char *token_name; gboolean is_present, is_login_card; object_path = get_object_path_for_token (self, card_slot); G_LOCK (gsd_smartcard_tokens); interface = g_hash_table_lookup (self->tokens, object_path); g_free (object_path); if (interface == NULL) goto out; token_name = PK11_GetTokenName (card_slot); is_present = PK11_IsPresent (card_slot); if (g_strcmp0 (g_getenv ("PKCS11_LOGIN_TOKEN_NAME"), token_name) == 0) is_login_card = TRUE; else is_login_card = FALSE; g_debug ("==============================="); g_debug (" Token '%s'", token_name); g_debug (" Inserted: %s", is_present? "yes" : "no"); g_debug (" Previously used to login: %s", is_login_card? "yes" : "no"); g_debug ("===============================\n"); if (!is_present && is_login_card) { gboolean was_present; g_object_get (G_OBJECT (interface), "is-inserted", &was_present, NULL); if (was_present) gsd_smartcard_manager_do_remove_action (self->smartcard_manager); } g_object_set (G_OBJECT (interface), "used-to-login", is_login_card, "is-inserted", is_present, NULL); g_object_get (G_OBJECT (interface), "used-to-login", &is_login_card, "is-inserted", &is_present, NULL); if (is_login_card && !self->login_token_bound) { const char *login_token_path; GDBusInterfaceSkeleton *login_token_interface; login_token_path = get_login_token_object_path (self); login_token_interface = g_hash_table_lookup (self->tokens, login_token_path); if (login_token_interface != NULL) { g_object_bind_property (interface, "driver", login_token_interface, "driver", G_BINDING_SYNC_CREATE); g_object_bind_property (interface, "is-inserted", login_token_interface, "is-inserted", G_BINDING_SYNC_CREATE); self->login_token_bound = TRUE; } } out: G_UNLOCK (gsd_smartcard_tokens); } typedef struct { PK11SlotInfo *card_slot; char *object_path; GSource *main_thread_source; } RegisterNewTokenOperation; static void destroy_register_new_token_operation (RegisterNewTokenOperation *operation) { g_clear_pointer (&operation->main_thread_source, g_source_destroy); PK11_FreeSlot (operation->card_slot); g_free (operation->object_path); g_free (operation); } static gboolean on_main_thread_to_register_new_token (GTask *task) { GsdSmartcardService *self; GDBusObjectSkeleton *object; GDBusInterfaceSkeleton *interface; RegisterNewTokenOperation *operation; SECMODModule *driver; char *driver_object_path; const char *token_name; self = g_task_get_source_object (task); operation = g_task_get_task_data (task); operation->main_thread_source = NULL; object = G_DBUS_OBJECT_SKELETON (gsd_smartcard_service_object_skeleton_new (operation->object_path)); interface = G_DBUS_INTERFACE_SKELETON (gsd_smartcard_service_token_skeleton_new ()); g_dbus_object_skeleton_add_interface (object, interface); g_object_unref (interface); driver = PK11_GetModule (operation->card_slot); driver_object_path = get_object_path_for_driver (self, driver); token_name = PK11_GetTokenName (operation->card_slot); g_object_set (G_OBJECT (interface), "driver", driver_object_path, "name", token_name, NULL); g_free (driver_object_path); g_dbus_object_manager_server_export (self->object_manager_server, object); G_LOCK (gsd_smartcard_tokens); g_hash_table_insert (self->tokens, g_strdup (operation->object_path), interface); G_UNLOCK (gsd_smartcard_tokens); g_task_return_boolean (task, TRUE); g_object_unref (task); return G_SOURCE_REMOVE; } static void create_main_thread_source (GSourceFunc callback, gpointer user_data, GSource **source_out) { GSource *source; source = g_idle_source_new (); g_source_set_callback (source, callback, user_data, NULL); *source_out = source; g_source_attach (source, NULL); g_source_unref (source); } static void register_new_token_in_main_thread (GsdSmartcardService *self, PK11SlotInfo *card_slot, char *object_path, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { RegisterNewTokenOperation *operation; GTask *task; operation = g_new0 (RegisterNewTokenOperation, 1); operation->card_slot = PK11_ReferenceSlot (card_slot); operation->object_path = g_strdup (object_path); task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, operation, (GDestroyNotify) destroy_register_new_token_operation); create_main_thread_source ((GSourceFunc) on_main_thread_to_register_new_token, task, &operation->main_thread_source); } static gboolean register_new_token_in_main_thread_finish (GsdSmartcardService *self, GAsyncResult *result, GError **error) { return g_task_propagate_boolean (G_TASK (result), error); } static void on_token_registered (GsdSmartcardService *self, GAsyncResult *result, PK11SlotInfo *card_slot) { gboolean registered; GError *error = NULL; registered = register_new_token_in_main_thread_finish (self, result, &error); if (!registered) { g_debug ("Couldn't register token: %s", error->message); goto out; } synchronize_token_now (self, card_slot); out: PK11_FreeSlot (card_slot); } typedef struct { PK11SlotInfo *card_slot; GSource *main_thread_source; } SynchronizeTokenOperation; static void destroy_synchronize_token_operation (SynchronizeTokenOperation *operation) { g_clear_pointer (&operation->main_thread_source, g_source_destroy); PK11_FreeSlot (operation->card_slot); g_free (operation); } static gboolean on_main_thread_to_synchronize_token (GTask *task) { GsdSmartcardService *self; SynchronizeTokenOperation *operation; self = g_task_get_source_object (task); operation = g_task_get_task_data (task); operation->main_thread_source = NULL; synchronize_token_now (self, operation->card_slot); g_task_return_boolean (task, TRUE); g_object_unref (task); return G_SOURCE_REMOVE; } static gboolean synchronize_token_in_main_thread_finish (GsdSmartcardService *self, GAsyncResult *result, GError **error) { return g_task_propagate_boolean (G_TASK (result), error); } static void synchronize_token_in_main_thread (GsdSmartcardService *self, PK11SlotInfo *card_slot, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { SynchronizeTokenOperation *operation; GTask *task; operation = g_new0 (SynchronizeTokenOperation, 1); operation->card_slot = PK11_ReferenceSlot (card_slot); task = g_task_new (self, cancellable, callback, user_data); g_task_set_task_data (task, operation, (GDestroyNotify) destroy_synchronize_token_operation); create_main_thread_source ((GSourceFunc) on_main_thread_to_synchronize_token, task, &operation->main_thread_source); } static void on_token_synchronized (GsdSmartcardService *self, GAsyncResult *result, PK11SlotInfo *card_slot) { gboolean synchronized; GError *error = NULL; synchronized = synchronize_token_in_main_thread_finish (self, result, &error); if (!synchronized) g_debug ("Couldn't synchronize token: %s", error->message); PK11_FreeSlot (card_slot); } void gsd_smartcard_service_sync_token (GsdSmartcardService *self, PK11SlotInfo *card_slot, GCancellable *cancellable) { char *object_path; GDBusInterfaceSkeleton *interface; object_path = get_object_path_for_token (self, card_slot); G_LOCK (gsd_smartcard_tokens); interface = g_hash_table_lookup (self->tokens, object_path); G_UNLOCK (gsd_smartcard_tokens); if (interface == NULL) register_new_token_in_main_thread (self, card_slot, object_path, cancellable, (GAsyncReadyCallback) on_token_registered, PK11_ReferenceSlot (card_slot)); else synchronize_token_in_main_thread (self, card_slot, cancellable, (GAsyncReadyCallback) on_token_synchronized, PK11_ReferenceSlot (card_slot)); g_free (object_path); }