diff options
Diffstat (limited to 'plugins/smartcard/gsd-smartcard-manager.c')
-rw-r--r-- | plugins/smartcard/gsd-smartcard-manager.c | 993 |
1 files changed, 993 insertions, 0 deletions
diff --git a/plugins/smartcard/gsd-smartcard-manager.c b/plugins/smartcard/gsd-smartcard-manager.c new file mode 100644 index 0000000..fc6ae0b --- /dev/null +++ b/plugins/smartcard/gsd-smartcard-manager.c @@ -0,0 +1,993 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- + * + * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu> + * Copyright (C) 2010,2011 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 <http://www.gnu.org/licenses/>. + * + */ + +#include "config.h" + +#include <glib.h> +#include <gio/gio.h> + +#include "gnome-settings-profile.h" +#include "gnome-settings-bus.h" +#include "gsd-smartcard-manager.h" +#include "gsd-smartcard-service.h" +#include "gsd-smartcard-enum-types.h" +#include "gsd-smartcard-utils.h" + +#include <prerror.h> +#include <prinit.h> +#include <nss.h> +#include <pk11func.h> +#include <secmod.h> +#include <secerr.h> + +#define GSD_SESSION_MANAGER_LOGOUT_MODE_FORCE 2 + +struct _GsdSmartcardManager +{ + GObject parent; + + guint start_idle_id; + GsdSmartcardService *service; + GList *smartcards_watch_tasks; + GCancellable *cancellable; + + GsdSessionManager *session_manager; + GsdScreenSaver *screen_saver; + + GSettings *settings; + + NSSInitContext *nss_context; +}; + +#define CONF_SCHEMA "org.gnome.settings-daemon.peripherals.smartcard" +#define KEY_REMOVE_ACTION "removal-action" + +static void gsd_smartcard_manager_class_init (GsdSmartcardManagerClass *klass); +static void gsd_smartcard_manager_init (GsdSmartcardManager *self); +static void gsd_smartcard_manager_finalize (GObject *object); +static void lock_screen (GsdSmartcardManager *self); +static void log_out (GsdSmartcardManager *self); +static void on_smartcards_from_driver_watched (GsdSmartcardManager *self, + GAsyncResult *result, + GTask *task); +G_DEFINE_TYPE (GsdSmartcardManager, gsd_smartcard_manager, G_TYPE_OBJECT) +G_DEFINE_QUARK (gsd-smartcard-manager-error, gsd_smartcard_manager_error) +G_LOCK_DEFINE_STATIC (gsd_smartcards_watch_tasks); + +typedef struct { + SECMODModule *driver; + guint idle_id; + GError *error; +} DriverRegistrationOperation; + +static gpointer manager_object = NULL; + +static void +gsd_smartcard_manager_class_init (GsdSmartcardManagerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->finalize = gsd_smartcard_manager_finalize; + + gsd_smartcard_utils_register_error_domain (GSD_SMARTCARD_MANAGER_ERROR, + GSD_TYPE_SMARTCARD_MANAGER_ERROR); +} + +static void +gsd_smartcard_manager_init (GsdSmartcardManager *self) +{ +} + +static void +load_nss (GsdSmartcardManager *self) +{ + NSSInitContext *context = NULL; + + /* The first field in the NSSInitParameters structure + * is the size of the structure. NSS requires this, so + * that it can change the size of the structure in future + * versions of NSS in a detectable way + */ + NSSInitParameters parameters = { sizeof (parameters), }; + static const guint32 flags = NSS_INIT_READONLY + | NSS_INIT_FORCEOPEN + | NSS_INIT_NOROOTINIT + | NSS_INIT_OPTIMIZESPACE + | NSS_INIT_PK11RELOAD; + + g_debug ("attempting to load NSS database '%s'", + GSD_SMARTCARD_MANAGER_NSS_DB); + + PR_Init (PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + + context = NSS_InitContext (GSD_SMARTCARD_MANAGER_NSS_DB, + "", "", SECMOD_DB, ¶meters, flags); + + if (context == NULL) { + gsize error_message_size; + char *error_message; + + error_message_size = PR_GetErrorTextLength (); + + if (error_message_size == 0) { + g_debug ("NSS security system could not be initialized"); + } else { + error_message = g_alloca (error_message_size); + PR_GetErrorText (error_message); + + g_debug ("NSS security system could not be initialized - %s", + error_message); + } + + self->nss_context = NULL; + return; + + } + + g_debug ("NSS database '%s' loaded", GSD_SMARTCARD_MANAGER_NSS_DB); + self->nss_context = context; +} + +static void +unload_nss (GsdSmartcardManager *self) +{ + g_debug ("attempting to unload NSS security system with database '%s'", + GSD_SMARTCARD_MANAGER_NSS_DB); + + if (self->nss_context != NULL) { + g_clear_pointer (&self->nss_context, + NSS_ShutdownContext); + g_debug ("NSS database '%s' unloaded", GSD_SMARTCARD_MANAGER_NSS_DB); + } else { + g_debug ("NSS database '%s' already not loaded", GSD_SMARTCARD_MANAGER_NSS_DB); + } +} + +typedef struct +{ + SECMODModule *driver; + GHashTable *smartcards; + int number_of_consecutive_errors; +} WatchSmartcardsOperation; + +static void +on_watch_cancelled (GCancellable *cancellable, + WatchSmartcardsOperation *operation) +{ + SECMOD_CancelWait (operation->driver); +} + +static gboolean +watch_one_event_from_driver (GsdSmartcardManager *self, + WatchSmartcardsOperation *operation, + GCancellable *cancellable, + GError **error) +{ + PK11SlotInfo *card = NULL, *old_card; + CK_SLOT_ID slot_id; + gulong handler_id; + int old_slot_series = -1, slot_series; + + handler_id = g_cancellable_connect (cancellable, + G_CALLBACK (on_watch_cancelled), + operation, + NULL); + + if (handler_id != 0) { + /* Use the non-blocking version of the call as p11-kit, which + * is used on both Fedora and Ubuntu, doesn't support the + * blocking version of the call. + */ + card = SECMOD_WaitForAnyTokenEvent (operation->driver, CKF_DONT_BLOCK, PR_SecondsToInterval (1)); + } + + g_cancellable_disconnect (cancellable, handler_id); + + if (g_cancellable_set_error_if_cancelled (cancellable, error)) { + g_warning ("smartcard event function cancelled"); + return FALSE; + } + + if (card == NULL) { + int error_code; + + error_code = PORT_GetError (); + + if (error_code == SEC_ERROR_NO_EVENT) { + g_usleep (1 * G_USEC_PER_SEC); + + return TRUE; + } + + operation->number_of_consecutive_errors++; + if (operation->number_of_consecutive_errors > 10) { + g_warning ("Got %d consecutive smartcard errors, so giving up.", + operation->number_of_consecutive_errors); + + g_set_error (error, + GSD_SMARTCARD_MANAGER_ERROR, + GSD_SMARTCARD_MANAGER_ERROR_WITH_NSS, + "encountered unexpected error while " + "waiting for smartcard events (error %x)", + error_code); + return FALSE; + } + + g_warning ("Got potentially spurious smartcard event error: %x.", error_code); + + g_usleep (1 * G_USEC_PER_SEC); + return TRUE; + } + operation->number_of_consecutive_errors = 0; + + slot_id = PK11_GetSlotID (card); + slot_series = PK11_GetSlotSeries (card); + + old_card = g_hash_table_lookup (operation->smartcards, GINT_TO_POINTER ((int) slot_id)); + + /* If there is a different card in the slot now than + * there was before, then we need to emit a removed signal + * for the old card + */ + if (old_card != NULL) { + old_slot_series = PK11_GetSlotSeries (old_card); + + if (old_slot_series != slot_series) { + /* Card registered with slot previously is + * different than this card, so update its + * exported state to track the implicit missed + * removal + */ + gsd_smartcard_service_sync_token (self->service, old_card, cancellable); + } + + g_hash_table_remove (operation->smartcards, GINT_TO_POINTER ((int) slot_id)); + } + + if (PK11_IsPresent (card)) { + g_debug ("Detected smartcard insertion event in slot %d", (int) slot_id); + + g_hash_table_replace (operation->smartcards, + GINT_TO_POINTER ((int) slot_id), + PK11_ReferenceSlot (card)); + + gsd_smartcard_service_sync_token (self->service, card, cancellable); + } else if (old_card == NULL) { + /* If the just removed smartcard is not known to us then + * ignore the removal event. NSS sends a synthentic removal + * event for slots that are empty at startup + */ + g_debug ("Detected slot %d is empty in reader", (int) slot_id); + } else { + g_debug ("Detected smartcard removal event in slot %d", (int) slot_id); + + /* If the just removed smartcard is known to us then + * we need to update its exported state to reflect the + * removal + */ + if (old_slot_series == slot_series) + gsd_smartcard_service_sync_token (self->service, card, cancellable); + } + + PK11_FreeSlot (card); + + return TRUE; +} + +static void +watch_smartcards_from_driver (GTask *task, + GsdSmartcardManager *self, + WatchSmartcardsOperation *operation, + GCancellable *cancellable) +{ + g_debug ("watching for smartcard events"); + while (!g_cancellable_is_cancelled (cancellable)) { + gboolean watch_succeeded; + GError *error = NULL; + + watch_succeeded = watch_one_event_from_driver (self, operation, cancellable, &error); + + if (g_task_return_error_if_cancelled (task)) { + break; + } + + if (!watch_succeeded) { + g_task_return_error (task, error); + break; + } + } +} + +static void +destroy_watch_smartcards_operation (WatchSmartcardsOperation *operation) +{ + SECMOD_DestroyModule (operation->driver); + g_hash_table_unref (operation->smartcards); + g_free (operation); +} + +static void +on_smartcards_watch_task_destroyed (GsdSmartcardManager *self, + GTask *freed_task) +{ + G_LOCK (gsd_smartcards_watch_tasks); + self->smartcards_watch_tasks = g_list_remove (self->smartcards_watch_tasks, + freed_task); + G_UNLOCK (gsd_smartcards_watch_tasks); +} + +static void +sync_initial_tokens_from_driver (GsdSmartcardManager *self, + SECMODModule *driver, + GHashTable *smartcards, + GCancellable *cancellable) +{ + int i; + + for (i = 0; i < driver->slotCount; i++) { + PK11SlotInfo *card; + + card = driver->slots[i]; + + if (PK11_IsPresent (card)) { + CK_SLOT_ID slot_id; + slot_id = PK11_GetSlotID (card); + + g_debug ("Detected smartcard in slot %d at start up", (int) slot_id); + + g_hash_table_replace (smartcards, + GINT_TO_POINTER ((int) slot_id), + PK11_ReferenceSlot (card)); + gsd_smartcard_service_sync_token (self->service, card, cancellable); + } + } +} + +static void +watch_smartcards_from_driver_async (GsdSmartcardManager *self, + SECMODModule *driver, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + WatchSmartcardsOperation *operation; + + operation = g_new0 (WatchSmartcardsOperation, 1); + operation->driver = SECMOD_ReferenceModule (driver); + operation->smartcards = g_hash_table_new_full (g_direct_hash, + g_direct_equal, + NULL, + (GDestroyNotify) PK11_FreeSlot); + + task = g_task_new (self, cancellable, callback, user_data); + + g_task_set_task_data (task, + operation, + (GDestroyNotify) destroy_watch_smartcards_operation); + + G_LOCK (gsd_smartcards_watch_tasks); + self->smartcards_watch_tasks = g_list_prepend (self->smartcards_watch_tasks, + task); + g_object_weak_ref (G_OBJECT (task), + (GWeakNotify) on_smartcards_watch_task_destroyed, + self); + G_UNLOCK (gsd_smartcards_watch_tasks); + + sync_initial_tokens_from_driver (self, driver, operation->smartcards, cancellable); + + g_task_run_in_thread (task, (GTaskThreadFunc) watch_smartcards_from_driver); +} + +static gboolean +register_driver_finish (GsdSmartcardManager *self, + GAsyncResult *result, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (result), error); +} + +static void +on_driver_registered (GsdSmartcardManager *self, + GAsyncResult *result, + GTask *task) +{ + GError *error = NULL; + DriverRegistrationOperation *operation; + + operation = g_task_get_task_data (G_TASK (result)); + + if (!register_driver_finish (self, result, &error)) { + g_task_return_error (task, error); + g_object_unref (task); + return; + } + + watch_smartcards_from_driver_async (self, + operation->driver, + self->cancellable, + (GAsyncReadyCallback) on_smartcards_from_driver_watched, + task); + + g_task_return_boolean (task, TRUE); + g_object_unref (task); +} + +static void +on_smartcards_from_driver_watched (GsdSmartcardManager *self, + GAsyncResult *result, + GTask *task) +{ + g_debug ("Done watching smartcards from driver"); +} + +static void +destroy_driver_registration_operation (DriverRegistrationOperation *operation) +{ + SECMOD_DestroyModule (operation->driver); + g_free (operation); +} + +static gboolean +on_task_thread_to_complete_driver_registration (GTask *task) +{ + DriverRegistrationOperation *operation; + operation = g_task_get_task_data (task); + + if (operation->error != NULL) + g_task_return_error (task, operation->error); + else + g_task_return_boolean (task, TRUE); + + return G_SOURCE_REMOVE; +} + +static gboolean +on_main_thread_to_register_driver (GTask *task) +{ + GsdSmartcardManager *self; + DriverRegistrationOperation *operation; + GSource *source; + + self = g_task_get_source_object (task); + operation = g_task_get_task_data (task); + + gsd_smartcard_service_register_driver (self->service, + operation->driver); + + source = g_idle_source_new (); + g_task_attach_source (task, + source, + (GSourceFunc) on_task_thread_to_complete_driver_registration); + g_source_unref (source); + + return G_SOURCE_REMOVE; +} + +static void +register_driver (GsdSmartcardManager *self, + SECMODModule *driver, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + DriverRegistrationOperation *operation; + + task = g_task_new (self, cancellable, callback, user_data); + operation = g_new0 (DriverRegistrationOperation, 1); + operation->driver = SECMOD_ReferenceModule (driver); + g_task_set_task_data (task, + operation, + (GDestroyNotify) destroy_driver_registration_operation); + + operation->idle_id = g_idle_add ((GSourceFunc) on_main_thread_to_register_driver, task); + g_source_set_name_by_id (operation->idle_id, "[gnome-settings-daemon] on_main_thread_to_register_driver"); +} + +static void +activate_driver (GsdSmartcardManager *self, + SECMODModule *driver, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + + g_debug ("Activating driver '%s'", driver->commonName); + + task = g_task_new (self, cancellable, callback, user_data); + + register_driver (self, + driver, + cancellable, + (GAsyncReadyCallback) on_driver_registered, + task); +} + +typedef struct +{ + int pending_drivers_count; + int activated_drivers_count; +} ActivateAllDriversOperation; + +static gboolean +activate_driver_async_finish (GsdSmartcardManager *self, + GAsyncResult *result, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (result), error); +} + +static void +try_to_complete_all_drivers_activation (GTask *task) +{ + ActivateAllDriversOperation *operation; + + operation = g_task_get_task_data (task); + + if (operation->pending_drivers_count > 0) + return; + + if (operation->activated_drivers_count > 0) + g_task_return_boolean (task, TRUE); + else + g_task_return_new_error (task, GSD_SMARTCARD_MANAGER_ERROR, + GSD_SMARTCARD_MANAGER_ERROR_NO_DRIVERS, + "No smartcards exist to be activated."); + + g_object_unref (task); +} + +static void +on_driver_activated (GsdSmartcardManager *self, + GAsyncResult *result, + GTask *task) +{ + GError *error = NULL; + gboolean driver_activated; + ActivateAllDriversOperation *operation; + + driver_activated = activate_driver_async_finish (self, result, &error); + + operation = g_task_get_task_data (task); + + if (driver_activated) + operation->activated_drivers_count++; + + operation->pending_drivers_count--; + + try_to_complete_all_drivers_activation (task); +} + +static void +activate_all_drivers_async (GsdSmartcardManager *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + SECMODListLock *lock; + SECMODModuleList *driver_list, *node; + ActivateAllDriversOperation *operation; + + task = g_task_new (self, cancellable, callback, user_data); + operation = g_new0 (ActivateAllDriversOperation, 1); + g_task_set_task_data (task, operation, (GDestroyNotify) g_free); + + lock = SECMOD_GetDefaultModuleListLock (); + + g_assert (lock != NULL); + + SECMOD_GetReadLock (lock); + driver_list = SECMOD_GetDefaultModuleList (); + for (node = driver_list; node != NULL; node = node->next) { + if (!node->module->loaded) + continue; + + if (!SECMOD_HasRemovableSlots (node->module)) + continue; + + if (node->module->dllName == NULL) + continue; + + operation->pending_drivers_count++; + + activate_driver (self, node->module, + cancellable, + (GAsyncReadyCallback) on_driver_activated, + task); + + } + SECMOD_ReleaseReadLock (lock); + + try_to_complete_all_drivers_activation (task); +} + +/* Will error with %GSD_SMARTCARD_MANAGER_ERROR_NO_DRIVERS if there were no + * drivers to activate.. */ +static gboolean +activate_all_drivers_async_finish (GsdSmartcardManager *self, + GAsyncResult *result, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (result), error); +} + +static void +on_all_drivers_activated (GsdSmartcardManager *self, + GAsyncResult *result, + GTask *task) +{ + GError *error = NULL; + gboolean driver_activated; + PK11SlotInfo *login_token; + + driver_activated = activate_all_drivers_async_finish (self, result, &error); + + if (!driver_activated) { + g_task_return_error (task, error); + return; + } + + login_token = gsd_smartcard_manager_get_login_token (self); + + if (login_token || g_getenv ("PKCS11_LOGIN_TOKEN_NAME") != NULL) { + /* The card used to log in was removed before login completed. + * Do removal action immediately + */ + if (!login_token || !PK11_IsPresent (login_token)) + gsd_smartcard_manager_do_remove_action (self); + } + + g_task_return_boolean (task, TRUE); + g_object_unref (task); +} + +static void +watch_smartcards (GTask *task, + GsdSmartcardManager *self, + gpointer data, + GCancellable *cancellable) +{ + GMainContext *context; + GMainLoop *loop; + + g_debug ("Getting list of suitable drivers"); + context = g_main_context_new (); + g_main_context_push_thread_default (context); + + activate_all_drivers_async (self, + cancellable, + (GAsyncReadyCallback) on_all_drivers_activated, + task); + + loop = g_main_loop_new (context, FALSE); + g_main_loop_run (loop); + g_main_loop_unref (loop); + + g_main_context_pop_thread_default (context); + g_main_context_unref (context); +} + +static void +watch_smartcards_async (GsdSmartcardManager *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + GTask *task; + + task = g_task_new (self, cancellable, callback, user_data); + + g_task_run_in_thread (task, (GTaskThreadFunc) watch_smartcards); +} + +static gboolean +watch_smartcards_async_finish (GsdSmartcardManager *self, + GAsyncResult *result, + GError **error) +{ + return g_task_propagate_boolean (G_TASK (result), error); +} + +static void +on_smartcards_watched (GsdSmartcardManager *self, + GAsyncResult *result) +{ + GError *error = NULL; + + if (!watch_smartcards_async_finish (self, result, &error)) { + g_debug ("Error watching smartcards: %s", error->message); + g_error_free (error); + } +} + +static void +on_service_created (GObject *source_object, + GAsyncResult *result, + GsdSmartcardManager *self) +{ + GsdSmartcardService *service; + GError *error = NULL; + + service = gsd_smartcard_service_new_finish (result, &error); + + if (service == NULL) { + g_warning("Couldn't create session bus service: %s", error->message); + g_error_free (error); + return; + } + + self->service = service; + + watch_smartcards_async (self, + self->cancellable, + (GAsyncReadyCallback) on_smartcards_watched, + NULL); + +} + +static gboolean +gsd_smartcard_manager_idle_cb (GsdSmartcardManager *self) +{ + gnome_settings_profile_start (NULL); + + self->cancellable = g_cancellable_new(); + self->settings = g_settings_new (CONF_SCHEMA); + + load_nss (self); + + gsd_smartcard_service_new_async (self, + self->cancellable, + (GAsyncReadyCallback) on_service_created, + self); + + gnome_settings_profile_end (NULL); + + self->start_idle_id = 0; + return FALSE; +} + +gboolean +gsd_smartcard_manager_start (GsdSmartcardManager *self, + GError **error) +{ + gnome_settings_profile_start (NULL); + + self->start_idle_id = g_idle_add ((GSourceFunc) gsd_smartcard_manager_idle_cb, self); + g_source_set_name_by_id (self->start_idle_id, "[gnome-settings-daemon] gsd_smartcard_manager_idle_cb"); + + gnome_settings_profile_end (NULL); + + return TRUE; +} + +void +gsd_smartcard_manager_stop (GsdSmartcardManager *self) +{ + g_debug ("Stopping smartcard manager"); + + g_cancellable_cancel (self->cancellable); + + unload_nss (self); + + g_clear_object (&self->settings); + g_clear_object (&self->cancellable); + g_clear_object (&self->session_manager); + g_clear_object (&self->screen_saver); +} + +static void +on_screen_locked (GsdScreenSaver *screen_saver, + GAsyncResult *result, + GsdSmartcardManager *self) +{ + gboolean is_locked; + GError *error = NULL; + + is_locked = gsd_screen_saver_call_lock_finish (screen_saver, result, &error); + + if (!is_locked) { + g_warning ("Couldn't lock screen: %s", error->message); + g_error_free (error); + return; + } +} + +static void +lock_screen (GsdSmartcardManager *self) +{ + if (self->screen_saver == NULL) + self->screen_saver = gnome_settings_bus_get_screen_saver_proxy (); + + gsd_screen_saver_call_lock (self->screen_saver, + self->cancellable, + (GAsyncReadyCallback) on_screen_locked, + self); +} + +static void +on_logged_out (GsdSessionManager *session_manager, + GAsyncResult *result, + GsdSmartcardManager *self) +{ + gboolean is_logged_out; + GError *error = NULL; + + is_logged_out = gsd_session_manager_call_logout_finish (session_manager, result, &error); + + if (!is_logged_out) { + g_warning ("Couldn't log out: %s", error->message); + g_error_free (error); + return; + } +} + +static void +log_out (GsdSmartcardManager *self) +{ + if (self->session_manager == NULL) + self->session_manager = gnome_settings_bus_get_session_proxy (); + + gsd_session_manager_call_logout (self->session_manager, + GSD_SESSION_MANAGER_LOGOUT_MODE_FORCE, + self->cancellable, + (GAsyncReadyCallback) on_logged_out, + self); +} + +void +gsd_smartcard_manager_do_remove_action (GsdSmartcardManager *self) +{ + char *remove_action; + + remove_action = g_settings_get_string (self->settings, KEY_REMOVE_ACTION); + + if (strcmp (remove_action, "lock-screen") == 0) + lock_screen (self); + else if (strcmp (remove_action, "force-logout") == 0) + log_out (self); +} + +static PK11SlotInfo * +get_login_token_for_operation (GsdSmartcardManager *self, + WatchSmartcardsOperation *operation) +{ + GHashTableIter iter; + gpointer key, value; + + g_hash_table_iter_init (&iter, operation->smartcards); + while (g_hash_table_iter_next (&iter, &key, &value)) { + PK11SlotInfo *card_slot; + const char *token_name; + + card_slot = (PK11SlotInfo *) value; + token_name = PK11_GetTokenName (card_slot); + + if (g_strcmp0 (g_getenv ("PKCS11_LOGIN_TOKEN_NAME"), token_name) == 0) + return card_slot; + } + + return NULL; +} + +PK11SlotInfo * +gsd_smartcard_manager_get_login_token (GsdSmartcardManager *self) +{ + PK11SlotInfo *card_slot = NULL; + GList *node; + + G_LOCK (gsd_smartcards_watch_tasks); + node = self->smartcards_watch_tasks; + while (node != NULL) { + GTask *task = node->data; + WatchSmartcardsOperation *operation = g_task_get_task_data (task); + + card_slot = get_login_token_for_operation (self, operation); + + if (card_slot != NULL) + break; + + node = node->next; + } + G_UNLOCK (gsd_smartcards_watch_tasks); + + return card_slot; +} + +static GList * +get_inserted_tokens_for_operation (GsdSmartcardManager *self, + WatchSmartcardsOperation *operation) +{ + GList *inserted_tokens = NULL; + GHashTableIter iter; + gpointer key, value; + + g_hash_table_iter_init (&iter, operation->smartcards); + while (g_hash_table_iter_next (&iter, &key, &value)) { + PK11SlotInfo *card_slot; + + card_slot = (PK11SlotInfo *) value; + + if (PK11_IsPresent (card_slot)) + inserted_tokens = g_list_prepend (inserted_tokens, card_slot); + } + + return inserted_tokens; +} + +GList * +gsd_smartcard_manager_get_inserted_tokens (GsdSmartcardManager *self, + gsize *num_tokens) +{ + GList *inserted_tokens = NULL, *node; + + G_LOCK (gsd_smartcards_watch_tasks); + for (node = self->smartcards_watch_tasks; node != NULL; node = node->next) { + GTask *task = node->data; + WatchSmartcardsOperation *operation = g_task_get_task_data (task); + GList *operation_inserted_tokens; + + operation_inserted_tokens = get_inserted_tokens_for_operation (self, operation); + + inserted_tokens = g_list_concat (inserted_tokens, operation_inserted_tokens); + } + G_UNLOCK (gsd_smartcards_watch_tasks); + + if (num_tokens != NULL) + *num_tokens = g_list_length (inserted_tokens); + + return inserted_tokens; +} + +static void +gsd_smartcard_manager_finalize (GObject *object) +{ + GsdSmartcardManager *self; + + g_return_if_fail (object != NULL); + g_return_if_fail (GSD_IS_SMARTCARD_MANAGER (object)); + + self = GSD_SMARTCARD_MANAGER (object); + + g_return_if_fail (self != NULL); + + if (self->start_idle_id != 0) + g_source_remove (self->start_idle_id); + + gsd_smartcard_manager_stop (self); + + G_OBJECT_CLASS (gsd_smartcard_manager_parent_class)->finalize (object); +} + +GsdSmartcardManager * +gsd_smartcard_manager_new (void) +{ + if (manager_object != NULL) { + g_object_ref (manager_object); + } else { + manager_object = g_object_new (GSD_TYPE_SMARTCARD_MANAGER, NULL); + g_object_add_weak_pointer (manager_object, + (gpointer *) &manager_object); + } + + return GSD_SMARTCARD_MANAGER (manager_object); +} |