summaryrefslogtreecommitdiffstats
path: root/plugins/smartcard
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/smartcard')
-rw-r--r--plugins/smartcard/gsd-smartcard-enum-types.c.in42
-rw-r--r--plugins/smartcard/gsd-smartcard-enum-types.h.in24
-rw-r--r--plugins/smartcard/gsd-smartcard-manager.c993
-rw-r--r--plugins/smartcard/gsd-smartcard-manager.h66
-rw-r--r--plugins/smartcard/gsd-smartcard-service.c849
-rw-r--r--plugins/smartcard/gsd-smartcard-service.h60
-rw-r--r--plugins/smartcard/gsd-smartcard-utils.c174
-rw-r--r--plugins/smartcard/gsd-smartcard-utils.h33
-rw-r--r--plugins/smartcard/main.c7
-rw-r--r--plugins/smartcard/meson.build49
-rw-r--r--plugins/smartcard/org.gnome.SettingsDaemon.Smartcard.xml89
11 files changed, 2386 insertions, 0 deletions
diff --git a/plugins/smartcard/gsd-smartcard-enum-types.c.in b/plugins/smartcard/gsd-smartcard-enum-types.c.in
new file mode 100644
index 0000000..f281cf4
--- /dev/null
+++ b/plugins/smartcard/gsd-smartcard-enum-types.c.in
@@ -0,0 +1,42 @@
+/*** BEGIN file-header ***/
+
+#include <glib-object.h>
+
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+#include "@filename@"
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType @enum_name@_get_type (void) G_GNUC_CONST;
+
+GType
+@enum_name@_get_type (void)
+{
+ static GType etype = 0;
+
+ if (G_UNLIKELY(etype == 0)) {
+ static const G@Type@Value values[] = {
+/*** END value-header ***/
+
+/*** BEGIN value-production ***/
+ { @VALUENAME@, "@VALUENAME@", "@valuenick@" },
+/*** END value-production ***/
+
+/*** BEGIN value-tail ***/
+ { 0, NULL, NULL }
+ };
+
+ etype = g_@type@_register_static (g_intern_static_string ("@EnumName@"), values);
+ }
+
+ return etype;
+}
+
+/*** END value-tail ***/
+
+/*** BEGIN file-tail ***/
+ /**/
+/*** END file-tail ***/
diff --git a/plugins/smartcard/gsd-smartcard-enum-types.h.in b/plugins/smartcard/gsd-smartcard-enum-types.h.in
new file mode 100644
index 0000000..79dcc3d
--- /dev/null
+++ b/plugins/smartcard/gsd-smartcard-enum-types.h.in
@@ -0,0 +1,24 @@
+/*** BEGIN file-header ***/
+#ifndef GSD_IDENTITY_ENUM_TYPES_H
+#define GSD_IDENTITY_ENUM_TYPES_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType @enum_name@_get_type (void) G_GNUC_CONST;
+#define @ENUMPREFIX@_TYPE_@ENUMSHORT@ (@enum_name@_get_type ())
+/*** END value-header ***/
+
+/*** BEGIN file-tail ***/
+G_END_DECLS
+
+#endif /* GSD_IDENTITY_ENUM_TYPES_H */
+/*** END file-tail ***/
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);
+}
diff --git a/plugins/smartcard/gsd-smartcard-manager.h b/plugins/smartcard/gsd-smartcard-manager.h
new file mode 100644
index 0000000..c2a9eb3
--- /dev/null
+++ b/plugins/smartcard/gsd-smartcard-manager.h
@@ -0,0 +1,66 @@
+/* -*- 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 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/>.
+ *
+ */
+
+#ifndef __GSD_SMARTCARD_MANAGER_H
+#define __GSD_SMARTCARD_MANAGER_H
+
+#include <glib-object.h>
+
+#include <prerror.h>
+#include <prinit.h>
+#include <nss.h>
+#include <pk11func.h>
+#include <secmod.h>
+#include <secerr.h>
+
+G_BEGIN_DECLS
+
+#define GSD_TYPE_SMARTCARD_MANAGER (gsd_smartcard_manager_get_type ())
+#define GSD_SMARTCARD_MANAGER_ERROR (gsd_smartcard_manager_error_quark ())
+
+G_DECLARE_FINAL_TYPE (GsdSmartcardManager, gsd_smartcard_manager, GSD, SMARTCARD_MANAGER, GObject)
+
+typedef enum
+{
+ GSD_SMARTCARD_MANAGER_ERROR_GENERIC = 0,
+ GSD_SMARTCARD_MANAGER_ERROR_WITH_NSS,
+ GSD_SMARTCARD_MANAGER_ERROR_LOADING_DRIVER,
+ GSD_SMARTCARD_MANAGER_ERROR_WATCHING_FOR_EVENTS,
+ GSD_SMARTCARD_MANAGER_ERROR_REPORTING_EVENTS,
+ GSD_SMARTCARD_MANAGER_ERROR_FINDING_SMARTCARD,
+ GSD_SMARTCARD_MANAGER_ERROR_NO_DRIVERS,
+} GsdSmartcardManagerError;
+
+GQuark gsd_smartcard_manager_error_quark (void);
+
+
+GsdSmartcardManager * gsd_smartcard_manager_new (void);
+gboolean gsd_smartcard_manager_start (GsdSmartcardManager *manager,
+ GError **error);
+void gsd_smartcard_manager_stop (GsdSmartcardManager *manager);
+
+PK11SlotInfo * gsd_smartcard_manager_get_login_token (GsdSmartcardManager *manager);
+GList * gsd_smartcard_manager_get_inserted_tokens (GsdSmartcardManager *manager,
+ gsize *num_tokens);
+void gsd_smartcard_manager_do_remove_action (GsdSmartcardManager *manager);
+
+G_END_DECLS
+
+#endif /* __GSD_SMARTCARD_MANAGER_H */
diff --git a/plugins/smartcard/gsd-smartcard-service.c b/plugins/smartcard/gsd-smartcard-service.c
new file mode 100644
index 0000000..4d529c3
--- /dev/null
+++ b/plugins/smartcard/gsd-smartcard-service.c
@@ -0,0 +1,849 @@
+/* -*- 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 <http://www.gnu.org/licenses/>.
+ *
+ * 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 <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <gio/gio.h>
+
+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);
+}
diff --git a/plugins/smartcard/gsd-smartcard-service.h b/plugins/smartcard/gsd-smartcard-service.h
new file mode 100644
index 0000000..11b3e22
--- /dev/null
+++ b/plugins/smartcard/gsd-smartcard-service.h
@@ -0,0 +1,60 @@
+/* -*- 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 <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Ray Strode
+ */
+
+#ifndef __GSD_SMARTCARD_SERVICE_H__
+#define __GSD_SMARTCARD_SERVICE_H__
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+#include "gsd-smartcard-manager.h"
+
+#include "org.gnome.SettingsDaemon.Smartcard.h"
+
+#include <prerror.h>
+#include <prinit.h>
+#include <nss.h>
+#include <pk11func.h>
+#include <secmod.h>
+#include <secerr.h>
+
+G_BEGIN_DECLS
+
+#define GSD_TYPE_SMARTCARD_SERVICE (gsd_smartcard_service_get_type ())
+
+G_DECLARE_FINAL_TYPE (GsdSmartcardService, gsd_smartcard_service, GSD, SMARTCARD_SERVICE, GsdSmartcardServiceManagerSkeleton)
+
+void gsd_smartcard_service_new_async (GsdSmartcardManager *manager,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+GsdSmartcardService *gsd_smartcard_service_new_finish (GAsyncResult *result,
+ GError **error);
+
+void gsd_smartcard_service_register_driver (GsdSmartcardService *service,
+ SECMODModule *driver);
+void gsd_smartcard_service_sync_token (GsdSmartcardService *service,
+ PK11SlotInfo *slot_info,
+ GCancellable *cancellable);
+
+
+G_END_DECLS
+
+#endif /* __GSD_SMARTCARD_SERVICE_H__ */
diff --git a/plugins/smartcard/gsd-smartcard-utils.c b/plugins/smartcard/gsd-smartcard-utils.c
new file mode 100644
index 0000000..6b9461b
--- /dev/null
+++ b/plugins/smartcard/gsd-smartcard-utils.c
@@ -0,0 +1,174 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 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 "gsd-smartcard-utils.h"
+
+#include <string.h>
+
+#include <glib.h>
+#include <gio/gio.h>
+
+static char *
+dashed_string_to_studly_caps (const char *dashed_string)
+{
+ char *studly_string;
+ size_t studly_string_length;
+ size_t i;
+
+ i = 0;
+
+ studly_string = g_strdup (dashed_string);
+ studly_string_length = strlen (studly_string);
+
+ studly_string[i] = g_ascii_toupper (studly_string[i]);
+ i++;
+
+ while (i < studly_string_length) {
+ if (studly_string[i] == '-' || studly_string[i] == '_') {
+ memmove (studly_string + i,
+ studly_string + i + 1,
+ studly_string_length - i - 1);
+ studly_string_length--;
+ if (g_ascii_isalpha (studly_string[i])) {
+ studly_string[i] = g_ascii_toupper (studly_string[i]);
+ }
+ }
+ i++;
+ }
+ studly_string[studly_string_length] = '\0';
+
+ return studly_string;
+}
+
+static char *
+dashed_string_to_dbus_error_string (const char *dashed_string,
+ const char *old_prefix,
+ const char *new_prefix,
+ const char *suffix)
+{
+ char *studly_suffix;
+ char *dbus_error_string;
+ size_t dbus_error_string_length;
+ size_t i;
+
+ i = 0;
+
+ if (g_str_has_prefix (dashed_string, old_prefix) &&
+ (dashed_string[strlen(old_prefix)] == '-' ||
+ dashed_string[strlen(old_prefix)] == '_')) {
+ dashed_string += strlen (old_prefix) + 1;
+ }
+
+ studly_suffix = dashed_string_to_studly_caps (suffix);
+ dbus_error_string = g_strdup_printf ("%s.%s.%s", new_prefix, dashed_string, studly_suffix);
+ g_free (studly_suffix);
+ i += strlen (new_prefix) + 1;
+
+ dbus_error_string_length = strlen (dbus_error_string);
+
+ dbus_error_string[i] = g_ascii_toupper (dbus_error_string[i]);
+ i++;
+
+ while (i < dbus_error_string_length) {
+ if (dbus_error_string[i] == '_' || dbus_error_string[i] == '-') {
+ dbus_error_string[i] = '.';
+
+ if (g_ascii_isalpha (dbus_error_string[i + 1])) {
+ dbus_error_string[i + 1] = g_ascii_toupper (dbus_error_string[i + 1]);
+ }
+ }
+
+ i++;
+ }
+
+ return dbus_error_string;
+}
+
+void
+gsd_smartcard_utils_register_error_domain (GQuark error_domain,
+ GType error_enum)
+{
+ const char *error_domain_string;
+ char *type_name;
+ GType type;
+ GTypeClass *type_class;
+ GEnumClass *enum_class;
+ guint i;
+
+ error_domain_string = g_quark_to_string (error_domain);
+ type_name = dashed_string_to_studly_caps (error_domain_string);
+ type = g_type_from_name (type_name);
+ type_class = g_type_class_ref (type);
+ enum_class = G_ENUM_CLASS (type_class);
+
+ for (i = 0; i < enum_class->n_values; i++) {
+ char *dbus_error_string;
+
+ dbus_error_string = dashed_string_to_dbus_error_string (error_domain_string,
+ "gsd",
+ "org.gnome.SettingsDaemon",
+ enum_class->values[i].value_nick);
+
+ g_debug ("%s: Registering dbus error %s", type_name, dbus_error_string);
+ g_dbus_error_register_error (error_domain,
+ enum_class->values[i].value,
+ dbus_error_string);
+ g_free (dbus_error_string);
+ }
+
+ g_type_class_unref (type_class);
+}
+
+char *
+gsd_smartcard_utils_escape_object_path (const char *unescaped_string)
+{
+ const char *p;
+ char *object_path;
+ GString *string;
+
+ g_return_val_if_fail (unescaped_string != NULL, NULL);
+
+ string = g_string_new ("");
+
+ for (p = unescaped_string; *p != '\0'; p++)
+ {
+ guchar character;
+
+ character = (guchar) * p;
+
+ if (((character >= ((guchar) 'a')) &&
+ (character <= ((guchar) 'z'))) ||
+ ((character >= ((guchar) 'A')) &&
+ (character <= ((guchar) 'Z'))) ||
+ ((character >= ((guchar) '0')) && (character <= ((guchar) '9'))))
+ {
+ g_string_append_c (string, (char) character);
+ continue;
+ }
+
+ g_string_append_printf (string, "_%x_", character);
+ }
+
+ object_path = string->str;
+
+ g_string_free (string, FALSE);
+
+ return object_path;
+}
diff --git a/plugins/smartcard/gsd-smartcard-utils.h b/plugins/smartcard/gsd-smartcard-utils.h
new file mode 100644
index 0000000..c7822bf
--- /dev/null
+++ b/plugins/smartcard/gsd-smartcard-utils.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2013 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/>.
+ *
+ */
+
+#ifndef __GSD_SMARTCARD_UTILS_H
+#define __GSD_SMARTCARD_UTILS_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+void gsd_smartcard_utils_register_error_domain (GQuark error_domain,
+ GType error_enum);
+char * gsd_smartcard_utils_escape_object_path (const char *unescaped_string);
+
+G_END_DECLS
+
+#endif /* __GSD_SMARTCARD_MANAGER_H */
diff --git a/plugins/smartcard/main.c b/plugins/smartcard/main.c
new file mode 100644
index 0000000..3552e5e
--- /dev/null
+++ b/plugins/smartcard/main.c
@@ -0,0 +1,7 @@
+#define NEW gsd_smartcard_manager_new
+#define START gsd_smartcard_manager_start
+#define STOP gsd_smartcard_manager_stop
+#define MANAGER GsdSmartcardManager
+#include "gsd-smartcard-manager.h"
+
+#include "daemon-skeleton.h"
diff --git a/plugins/smartcard/meson.build b/plugins/smartcard/meson.build
new file mode 100644
index 0000000..916a0fc
--- /dev/null
+++ b/plugins/smartcard/meson.build
@@ -0,0 +1,49 @@
+sources = files(
+ 'gsd-smartcard-manager.c',
+ 'gsd-smartcard-service.c',
+ 'gsd-smartcard-utils.c',
+ 'main.c'
+)
+
+enum_headers = files(
+ 'gsd-smartcard-manager.h',
+ 'gsd-smartcard-utils.h'
+)
+
+enum_types = 'gsd-smartcard-enum-types'
+
+sources += gnome.mkenums(
+ enum_types,
+ sources: enum_headers,
+ c_template: enum_types + '.c.in',
+ h_template: enum_types + '.h.in'
+)
+
+gdbus = 'org.gnome.SettingsDaemon.Smartcard'
+
+sources += gnome.gdbus_codegen(
+ gdbus,
+ gdbus + '.xml',
+ interface_prefix: gdbus + '.',
+ namespace: 'GsdSmartcardService',
+ object_manager: true
+)
+
+deps = plugins_deps + [
+ gio_unix_dep,
+ libnotify_dep,
+ nss_dep
+]
+
+cflags += ['-DGSD_SMARTCARD_MANAGER_NSS_DB="@0@"'.format(system_nssdb_dir)]
+
+executable(
+ 'gsd-' + plugin_name,
+ sources,
+ include_directories: [top_inc, common_inc],
+ dependencies: deps,
+ c_args: cflags,
+ install: true,
+ install_rpath: gsd_pkglibdir,
+ install_dir: gsd_libexecdir
+)
diff --git a/plugins/smartcard/org.gnome.SettingsDaemon.Smartcard.xml b/plugins/smartcard/org.gnome.SettingsDaemon.Smartcard.xml
new file mode 100644
index 0000000..53d56a0
--- /dev/null
+++ b/plugins/smartcard/org.gnome.SettingsDaemon.Smartcard.xml
@@ -0,0 +1,89 @@
+<!DOCTYPE node PUBLIC
+ "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
+
+<!--
+ Copyright (C) 2013 Red Hat, Inc.
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Lesser General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Lesser General Public License for more details.
+
+ You should have received a copy of the GNU Lesser General
+ Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+
+ Author: Ray Strode <rstrode@redhat.com>
+-->
+
+<node name="/" xmlns:doc="http://www.freedesktop.org/dbus/1.0/doc.dtd">
+ <!--
+ org.gnome.SettingsDaemon.Smartcard.Manager:
+
+ An interface used for managing smartcard functionality.
+ -->
+ <interface name="org.gnome.SettingsDaemon.Smartcard.Manager">
+ <method name="GetLoginToken">
+ <arg name="token" type="o" direction="out"/>
+ </method>
+
+ <method name="GetInsertedTokens">
+ <arg name="tokens" type="ao" direction="out"/>
+ </method>
+ </interface>
+
+ <!--
+ org.gnome.SettingsDaemon.Smartcard.Driver:
+
+ The smartcard driver interface.
+ -->
+ <interface name="org.gnome.SettingsDaemon.Smartcard.Driver">
+ <!--
+ Library:
+ Path to PKCS11 module
+ -->
+ <property name="Library" type="s" access="read"/>
+
+ <!--
+ Description:
+ String describing the PKCS11 module
+ -->
+ <property name="Description" type="s" access="read"/>
+ </interface>
+
+ <!--
+ org.gnome.SettingsDaemon.Smartcard.Token:
+
+ The smartcard interface.
+ -->
+ <interface name="org.gnome.SettingsDaemon.Smartcard.Token">
+ <!--
+ Name:
+ Name of the token
+ -->
+ <property name="Name" type="s" access="read"/>
+
+ <!--
+ Driver:
+ Driver handling token
+ -->
+ <property name="Driver" type="o" access="read"/>
+
+ <!--
+ IsInserted:
+ Whether or not the card is inserted
+ -->
+ <property name="IsInserted" type="b" access="read"/>
+
+ <!--
+ UsedToLogin:
+ Whether or not the card was used to log in
+ -->
+ <property name="UsedToLogin" type="b" access="read"/>
+ </interface>
+</node>