summaryrefslogtreecommitdiffstats
path: root/plugins/smartcard/gsd-smartcard-manager.c
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/smartcard/gsd-smartcard-manager.c')
-rw-r--r--plugins/smartcard/gsd-smartcard-manager.c993
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, &parameters, 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);
+}