diff options
Diffstat (limited to 'plugins/smartcard')
-rw-r--r-- | plugins/smartcard/gsd-smartcard-enum-types.c.in | 42 | ||||
-rw-r--r-- | plugins/smartcard/gsd-smartcard-enum-types.h.in | 24 | ||||
-rw-r--r-- | plugins/smartcard/gsd-smartcard-manager.c | 993 | ||||
-rw-r--r-- | plugins/smartcard/gsd-smartcard-manager.h | 66 | ||||
-rw-r--r-- | plugins/smartcard/gsd-smartcard-service.c | 849 | ||||
-rw-r--r-- | plugins/smartcard/gsd-smartcard-service.h | 60 | ||||
-rw-r--r-- | plugins/smartcard/gsd-smartcard-utils.c | 174 | ||||
-rw-r--r-- | plugins/smartcard/gsd-smartcard-utils.h | 33 | ||||
-rw-r--r-- | plugins/smartcard/main.c | 7 | ||||
-rw-r--r-- | plugins/smartcard/meson.build | 49 | ||||
-rw-r--r-- | plugins/smartcard/org.gnome.SettingsDaemon.Smartcard.xml | 89 |
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, ¶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); +} 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> |