From d96dbdfa9b4d25e3c9b706c2721fb02d92ab9098 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Fri, 6 Feb 2009 16:25:47 -0500 Subject: greeter: Add smartcard plugin This commit adds a plugin to initiate a conversation when smartcards are inserted. --- configure.ac | 11 + gui/simple-greeter/extensions/Makefile.am | 2 +- .../extensions/smartcard/Makefile.am | 79 ++ .../extensions/smartcard/gdm-smartcard | 18 + .../extensions/smartcard/gdm-smartcard-extension.c | 606 ++++++++ .../extensions/smartcard/gdm-smartcard-extension.h | 56 + .../extensions/smartcard/gdm-smartcard-manager.c | 1485 ++++++++++++++++++++ .../extensions/smartcard/gdm-smartcard-manager.h | 86 ++ .../extensions/smartcard/gdm-smartcard-worker.c | 186 +++ .../extensions/smartcard/gdm-smartcard.c | 552 ++++++++ .../extensions/smartcard/gdm-smartcard.h | 94 ++ .../extensions/smartcard/gdm-smartcard.pam | 18 + .../extensions/smartcard/icons/16x16/Makefile.am | 5 + .../smartcard/icons/16x16/gdm-smartcard.png | Bin 0 -> 871 bytes .../extensions/smartcard/icons/48x48/Makefile.am | 5 + .../smartcard/icons/48x48/gdm-smartcard.png | Bin 0 -> 4202 bytes .../extensions/smartcard/icons/Makefile.am | 1 + ...lay-manager.extensions.smartcard.gschema.xml.in | 9 + gui/simple-greeter/extensions/smartcard/page.ui | 57 + po/POTFILES.in | 3 + 20 files changed, 3272 insertions(+), 1 deletion(-) create mode 100644 gui/simple-greeter/extensions/smartcard/Makefile.am create mode 100644 gui/simple-greeter/extensions/smartcard/gdm-smartcard create mode 100644 gui/simple-greeter/extensions/smartcard/gdm-smartcard-extension.c create mode 100644 gui/simple-greeter/extensions/smartcard/gdm-smartcard-extension.h create mode 100644 gui/simple-greeter/extensions/smartcard/gdm-smartcard-manager.c create mode 100644 gui/simple-greeter/extensions/smartcard/gdm-smartcard-manager.h create mode 100644 gui/simple-greeter/extensions/smartcard/gdm-smartcard-worker.c create mode 100644 gui/simple-greeter/extensions/smartcard/gdm-smartcard.c create mode 100644 gui/simple-greeter/extensions/smartcard/gdm-smartcard.h create mode 100644 gui/simple-greeter/extensions/smartcard/gdm-smartcard.pam create mode 100644 gui/simple-greeter/extensions/smartcard/icons/16x16/Makefile.am create mode 100644 gui/simple-greeter/extensions/smartcard/icons/16x16/gdm-smartcard.png create mode 100644 gui/simple-greeter/extensions/smartcard/icons/48x48/Makefile.am create mode 100644 gui/simple-greeter/extensions/smartcard/icons/48x48/gdm-smartcard.png create mode 100644 gui/simple-greeter/extensions/smartcard/icons/Makefile.am create mode 100644 gui/simple-greeter/extensions/smartcard/org.gnome.display-manager.extensions.smartcard.gschema.xml.in create mode 100644 gui/simple-greeter/extensions/smartcard/page.ui diff --git a/configure.ac b/configure.ac index ec3ecd67..efe1e962 100644 --- a/configure.ac +++ b/configure.ac @@ -68,6 +68,7 @@ LIBCANBERRA_GTK_REQUIRED_VERSION=0.4 FONTCONFIG_REQUIRED_VERSION=2.5.0 UPOWER_REQUIRED_VERSION=0.9.0 ACCOUNTS_SERVICE_REQUIRED_VERSION=0.6.12 +NSS_REQUIRED_VERSION=3.11.1 EXTRA_COMPILE_WARNINGS(yes) @@ -94,6 +95,12 @@ AC_SUBST(DAEMON_LIBS) GLIB_GSETTINGS +PKG_CHECK_MODULES(NSS, + nss >= $NSS_REQUIRED_VERSION +) +AC_SUBST(NSS_CFLAGS) +AC_SUBST(NSS_LIBS) + PKG_CHECK_MODULES(XLIB, x11 xau xrandr, , [AC_PATH_XTRA if test "x$no_x" = xyes; then @@ -1424,6 +1431,10 @@ gui/simple-greeter/extensions/fingerprint/Makefile gui/simple-greeter/extensions/fingerprint/icons/Makefile gui/simple-greeter/extensions/fingerprint/icons/16x16/Makefile gui/simple-greeter/extensions/fingerprint/icons/48x48/Makefile +gui/simple-greeter/extensions/smartcard/Makefile +gui/simple-greeter/extensions/smartcard/icons/Makefile +gui/simple-greeter/extensions/smartcard/icons/16x16/Makefile +gui/simple-greeter/extensions/smartcard/icons/48x48/Makefile gui/simple-chooser/Makefile utils/Makefile data/gdm.conf diff --git a/gui/simple-greeter/extensions/Makefile.am b/gui/simple-greeter/extensions/Makefile.am index 9f8b1eab..2cba7ec9 100644 --- a/gui/simple-greeter/extensions/Makefile.am +++ b/gui/simple-greeter/extensions/Makefile.am @@ -1,5 +1,5 @@ SUBDIRS = unified if ENABLE_SPLIT_AUTHENTICATION -SUBDIRS += password fingerprint +SUBDIRS += password fingerprint smartcard endif diff --git a/gui/simple-greeter/extensions/smartcard/Makefile.am b/gui/simple-greeter/extensions/smartcard/Makefile.am new file mode 100644 index 00000000..986bf56c --- /dev/null +++ b/gui/simple-greeter/extensions/smartcard/Makefile.am @@ -0,0 +1,79 @@ +SUBDIRS = icons + +NULL = +PAM_SERVICE_NAME = gdm-smartcard + +gsettings_SCHEMAS =org.gnome.display-manager.extensions.smartcard.gschema.xml +@INTLTOOL_XML_NOMERGE_RULE@ +@GSETTINGS_RULES@ + +extensiondir = $(GDM_SIMPLE_GREETER_EXTENSIONS_DATA_DIR)/smartcard +extension_DATA = page.ui + +AM_CPPFLAGS = \ + -I$(top_srcdir)/common \ + -I$(top_srcdir)/gui/simple-greeter/libgdmsimplegreeter \ + -DDMCONFDIR=\""$(dmconfdir)"\" \ + -DGDMCONFDIR=\"$(gdmconfdir)\" \ + -DPLUGINDATADIR=\""$(extensiondir)"\" \ + -DGDM_SMARTCARD_EXTENSION_SERVICE_NAME=\""$(PAM_SERVICE_NAME)"\" \ + -DSYSCONFDIR=\""$(sysconfdir)"\" \ + -DLIBLOCALEDIR=\""$(prefix)/lib/locale"\" \ + -DGNOMELOCALEDIR=\""$(datadir)/locale"\" \ + -DLIBEXECDIR=\""$(libexecdir)"\" \ + -DLIBDIR=\""$(libdir)"\" \ + -DSBINDIR=\""$(sbindir)"\" \ + $(DISABLE_DEPRECATED_CFLAGS) \ + $(GTK_CFLAGS) \ + $(SIMPLE_GREETER_CFLAGS) \ + $(POLKIT_GNOME_CFLAGS) \ + $(NULL) + +plugindir = $(GDM_SIMPLE_GREETER_PLUGINS_DIR) +plugin_LTLIBRARIES = libsmartcard.la + +libsmartcard_la_CFLAGS = \ + $(SIMPLE_GREETER_CFLAGS) \ + $(NULL) + +libexec_PROGRAMS = \ + gdm-smartcard-worker \ + $(NULL) + +libsmartcard_la_LDFLAGS = -module -avoid-version -export-dynamic +libsmartcard_la_LIBADD = ../../../../common/libgdmcommon.la \ + ../../libgdmsimplegreeter/libgdmsimplegreeter.la +libsmartcard_la_SOURCES = \ + gdm-smartcard-extension.h \ + gdm-smartcard-extension.c + +gdm_smartcard_worker_LDADD = ../../../../common/libgdmcommon.la \ + $(DAEMON_LIBS) \ + $(GTHREAD_LIBS) \ + $(NSS_LIBS) \ + $(NULL) +gdm_smartcard_worker_CFLAGS = $(DAEMON_CFLAGS) \ + $(NSS_CFLAGS) \ + $(NULL) +gdm_smartcard_worker_SOURCES = \ + gdm-smartcard.h \ + gdm-smartcard.c \ + gdm-smartcard-manager.h \ + gdm-smartcard-manager.c \ + gdm-smartcard-worker.c \ + $(NULL) + +$(PAM_SERVICE_NAME): $(PAM_SERVICE_NAME).pam + cp $(PAM_SERVICE_NAME).pam $(PAM_SERVICE_NAME) + +pamdir = $(PAM_PREFIX)/pam.d +pam_DATA = $(PAM_SERVICE_NAME) + +EXTRA_DIST = $(extension_DATA) $(gsettings_SCHEMAS) $(PAM_SERVICE_NAME).pam +CLEANFILES = $(PAM_SERVICE_NAME) $(gsettings_SCHEMAS) + +MAINTAINERCLEANFILES = \ + *~ \ + $(PAM_SERVICE_NAME) \ + $(gsettings_SCHEMAS) \ + Makefile.in diff --git a/gui/simple-greeter/extensions/smartcard/gdm-smartcard b/gui/simple-greeter/extensions/smartcard/gdm-smartcard new file mode 100644 index 00000000..d5ac1fab --- /dev/null +++ b/gui/simple-greeter/extensions/smartcard/gdm-smartcard @@ -0,0 +1,18 @@ +# Sample PAM file for doing smartcard authentication. +# Distros should replace this with what makes sense for them. +auth required pam_env.so +auth [success=done ignore=ignore default=die] pam_pkcs11.so wait_for_card card_only +auth requisite pam_succeed_if.so uid >= 500 quiet +auth required pam_deny.so + +account required pam_unix.so +account sufficient pam_localuser.so +account sufficient pam_succeed_if.so uid < 500 quiet +account required pam_permit.so + +password optional pam_pkcs11.so +password requisite pam_cracklib.so try_first_pass retry=3 type= + +session optional pam_keyinit.so revoke +session required pam_limits.so +session required pam_unix.so diff --git a/gui/simple-greeter/extensions/smartcard/gdm-smartcard-extension.c b/gui/simple-greeter/extensions/smartcard/gdm-smartcard-extension.c new file mode 100644 index 00000000..825683e1 --- /dev/null +++ b/gui/simple-greeter/extensions/smartcard/gdm-smartcard-extension.c @@ -0,0 +1,606 @@ +/* + * Copyright (C) 2009 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * Written By: Ray Strode + * + */ + +#include +#include "gdm-smartcard-extension.h" + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#ifndef GDM_SMARTCARD_WORKER_COMMAND +#define GDM_SMARTCARD_WORKER_COMMAND LIBEXECDIR "/gdm-smartcard-worker" +#endif + +struct _GdmSmartcardExtensionPrivate +{ + GIcon *icon; + GtkWidget *page; + GtkActionGroup *actions; + GtkAction *login_action; + GSettings *settings; + + GtkWidget *message_label; + GtkWidget *prompt_label; + GtkWidget *prompt_entry; + + GPid worker_pid; + int number_of_tokens; + + GQueue *message_queue; + guint message_timeout_id; + + guint answer_pending : 1; + guint select_when_ready : 1; +}; + +typedef struct { + char *text; + GdmServiceMessageType type; +} QueuedMessage; + +static void gdm_smartcard_extension_finalize (GObject *object); + +static void gdm_login_extension_iface_init (GdmLoginExtensionIface *iface); + +G_DEFINE_TYPE_WITH_CODE (GdmSmartcardExtension, + gdm_smartcard_extension, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GDM_TYPE_LOGIN_EXTENSION, + gdm_login_extension_iface_init)); + +static void +set_message (GdmSmartcardExtension *extension, + const char *message) +{ + gtk_widget_show (extension->priv->message_label); + gtk_label_set_text (GTK_LABEL (extension->priv->message_label), message); +} + +static void +free_queued_message (QueuedMessage *message) +{ + g_free (message->text); + g_slice_free (QueuedMessage, message); +} + +static void +purge_message_queue (GdmSmartcardExtension *extension) +{ + if (extension->priv->message_timeout_id) { + g_source_remove (extension->priv->message_timeout_id); + extension->priv->message_timeout_id = 0; + } + g_queue_foreach (extension->priv->message_queue, + (GFunc) free_queued_message, + NULL); + g_queue_clear (extension->priv->message_queue); +} + +static gboolean +dequeue_message (GdmSmartcardExtension *extension) +{ + if (!g_queue_is_empty (extension->priv->message_queue)) { + int duration; + gboolean needs_beep; + + QueuedMessage *message; + message = (QueuedMessage *) g_queue_pop_head (extension->priv->message_queue); + + switch (message->type) { + case GDM_SERVICE_MESSAGE_TYPE_INFO: + needs_beep = FALSE; + break; + case GDM_SERVICE_MESSAGE_TYPE_PROBLEM: + needs_beep = TRUE; + break; + default: + g_assert_not_reached (); + } + + set_message (extension, message->text); + + duration = (int) (g_utf8_strlen (message->text, -1) / 66.0) * 1000; + duration = CLAMP (duration, 400, 3000); + + extension->priv->message_timeout_id = g_timeout_add (duration, + (GSourceFunc) dequeue_message, + extension); + if (needs_beep) { + gdk_window_beep (gtk_widget_get_window (GTK_WIDGET (extension))); + } + + free_queued_message (message); + } else { + extension->priv->message_timeout_id = 0; + + _gdm_login_extension_emit_message_queue_empty (GDM_LOGIN_EXTENSION (extension)); + } + + return FALSE; +} + +static void +gdm_smartcard_extension_queue_message (GdmLoginExtension *login_extension, + GdmServiceMessageType type, + const char *text) +{ + GdmSmartcardExtension *extension = GDM_SMARTCARD_EXTENSION (login_extension); + + QueuedMessage *message = g_slice_new (QueuedMessage); + + message->text = g_strdup (text); + message->type = type; + + g_queue_push_tail (extension->priv->message_queue, message); + + if (extension->priv->message_timeout_id == 0) { + dequeue_message (extension); + } +} + +static gboolean +on_smartcard_event (GIOChannel *io_channel, + GIOCondition condition, + gpointer data) +{ + GdmSmartcardExtension *extension; + + extension = GDM_SMARTCARD_EXTENSION (data); + + if (condition & G_IO_IN) { + char buffer[1024]; + ssize_t num_bytes; + + num_bytes = read (g_io_channel_unix_get_fd (io_channel), + buffer, sizeof (buffer)); + + if (num_bytes < 0 && errno != EINTR) + return FALSE; + + if (num_bytes != 1) { + g_debug ("buffer: %s\n", buffer); + return TRUE; + } + + if (buffer[0] == 'I') { + extension->priv->number_of_tokens++; + } else { + extension->priv->number_of_tokens--; + } + + if (extension->priv->number_of_tokens == 1) { + if (!_gdm_login_extension_emit_choose_user (GDM_LOGIN_EXTENSION (extension), + GDM_SMARTCARD_EXTENSION_SERVICE_NAME)) { + g_debug ("could not choose smart card user, cancelling..."); + _gdm_login_extension_emit_cancel (GDM_LOGIN_EXTENSION (extension)); + extension->priv->select_when_ready = TRUE; + } else { + g_debug ("chose smart card user!"); + } + } else if (extension->priv->number_of_tokens == 0) { + _gdm_login_extension_emit_cancel (GDM_LOGIN_EXTENSION (extension)); + } + + return TRUE; + } + + if (condition & G_IO_HUP) { + return FALSE; + } + + return TRUE; +} + +static void +watch_for_smartcards (GdmSmartcardExtension *extension) +{ + GError *error; + GIOChannel *io_channel; + char *args[] = { GDM_SMARTCARD_WORKER_COMMAND, NULL }; + GPid pid; + int stdout_fd; + + error = NULL; + + if (!g_spawn_async_with_pipes (NULL, args, NULL, 0, + NULL, NULL, &pid, NULL, + &stdout_fd, NULL, &error)) { + g_debug ("could not start smart card manager: %s", error->message); + g_error_free (error); + return; + } + fcntl (stdout_fd, F_SETFD, FD_CLOEXEC); + + io_channel = g_io_channel_unix_new (stdout_fd); + g_io_channel_set_flags (io_channel, G_IO_FLAG_NONBLOCK, NULL); + g_io_channel_set_encoding (io_channel, NULL, NULL); + g_io_channel_set_buffered (io_channel, FALSE); + g_io_add_watch (io_channel, G_IO_IN, on_smartcard_event, extension); + g_io_channel_set_close_on_unref (io_channel, TRUE); + g_io_channel_unref (io_channel); + + extension->priv->worker_pid = pid; +} + +static void +stop_watching_for_smartcards (GdmSmartcardExtension *extension) +{ + kill (extension->priv->worker_pid, SIGTERM); +} + +static void +gdm_smartcard_extension_ask_question (GdmLoginExtension *login_extension, + const char *message) +{ + GdmSmartcardExtension *extension = GDM_SMARTCARD_EXTENSION (login_extension); + gtk_widget_show (extension->priv->prompt_label); + gtk_label_set_text (GTK_LABEL (extension->priv->prompt_label), message); + gtk_entry_set_text (GTK_ENTRY (extension->priv->prompt_entry), ""); + gtk_entry_set_visibility (GTK_ENTRY (extension->priv->prompt_entry), TRUE); + gtk_widget_show (extension->priv->prompt_entry); + gtk_action_set_visible (extension->priv->login_action, TRUE); + gtk_action_set_sensitive (extension->priv->login_action, TRUE); + gtk_widget_grab_focus (extension->priv->prompt_entry); + extension->priv->answer_pending = TRUE; +} + +static void +gdm_smartcard_extension_ask_secret (GdmLoginExtension *login_extension, + const char *message) +{ + GdmSmartcardExtension *extension = GDM_SMARTCARD_EXTENSION (login_extension); + gtk_widget_show (extension->priv->prompt_label); + gtk_label_set_text (GTK_LABEL (extension->priv->prompt_label), message); + gtk_entry_set_visibility (GTK_ENTRY (extension->priv->prompt_entry), FALSE); + gtk_entry_set_text (GTK_ENTRY (extension->priv->prompt_entry), ""); + gtk_widget_show (extension->priv->prompt_entry); + gtk_widget_grab_focus (extension->priv->prompt_entry); + gtk_action_set_visible (extension->priv->login_action, TRUE); + gtk_action_set_sensitive (extension->priv->login_action, TRUE); + extension->priv->answer_pending = TRUE; +} + +static void +gdm_smartcard_extension_reset (GdmLoginExtension *login_extension) +{ + GdmSmartcardExtension *extension = GDM_SMARTCARD_EXTENSION (login_extension); + gtk_widget_hide (extension->priv->prompt_label); + gtk_label_set_text (GTK_LABEL (extension->priv->prompt_label), ""); + + gtk_widget_hide (extension->priv->prompt_entry); + gtk_entry_set_text (GTK_ENTRY (extension->priv->prompt_entry), ""); + gtk_entry_set_visibility (GTK_ENTRY (extension->priv->prompt_entry), TRUE); + gtk_action_set_visible (extension->priv->login_action, FALSE); + extension->priv->answer_pending = FALSE; + + purge_message_queue (extension); + set_message (extension, ""); + + gdm_login_extension_set_enabled (login_extension, FALSE); +} + +static void +gdm_smartcard_extension_set_ready (GdmLoginExtension *login_extension) +{ + GdmSmartcardExtension *extension = GDM_SMARTCARD_EXTENSION (login_extension); + gdm_login_extension_set_enabled (login_extension, TRUE); + + if (extension->priv->worker_pid <= 0) { + watch_for_smartcards (extension); + } + + if (extension->priv->select_when_ready) { + if (_gdm_login_extension_emit_choose_user (login_extension, + GDM_SMARTCARD_EXTENSION_SERVICE_NAME)) { + extension->priv->select_when_ready = FALSE; + } + } +} + +static char * +gdm_smartcard_extension_get_service_name (GdmLoginExtension *login_extension) +{ + return g_strdup (GDM_SMARTCARD_EXTENSION_SERVICE_NAME); +} + +static GtkWidget * +gdm_smartcard_extension_get_page (GdmLoginExtension *login_extension) +{ + GdmSmartcardExtension *extension = GDM_SMARTCARD_EXTENSION (login_extension); + return extension->priv->page; +} + +static GtkActionGroup * +gdm_smartcard_extension_get_actions (GdmLoginExtension *login_extension) +{ + GdmSmartcardExtension *extension = GDM_SMARTCARD_EXTENSION (login_extension); + + return g_object_ref (extension->priv->actions); +} + +static void +request_answer (GdmSmartcardExtension *extension) +{ + const char *text; + + if (!extension->priv->answer_pending) { + _gdm_login_extension_emit_answer (GDM_LOGIN_EXTENSION (extension), NULL); + return; + } + + extension->priv->answer_pending = FALSE; + text = gtk_entry_get_text (GTK_ENTRY (extension->priv->prompt_entry)); + _gdm_login_extension_emit_answer (GDM_LOGIN_EXTENSION (extension), text); + + gtk_widget_hide (extension->priv->prompt_entry); + gtk_label_set_text (GTK_LABEL (extension->priv->prompt_label), ""); + gtk_entry_set_text (GTK_ENTRY (extension->priv->prompt_entry), ""); + gtk_action_set_visible (extension->priv->login_action, FALSE); +} + +static gboolean +gdm_smartcard_extension_focus (GdmLoginExtension *login_extension) +{ + GdmSmartcardExtension *extension = GDM_SMARTCARD_EXTENSION (login_extension); + + if (!extension->priv->answer_pending) { + return FALSE; + } + + gtk_widget_grab_focus (extension->priv->prompt_entry); + return TRUE; +} + +static gboolean +gdm_smartcard_extension_has_queued_messages (GdmLoginExtension *login_extension) +{ + GdmSmartcardExtension *extension = GDM_SMARTCARD_EXTENSION (login_extension); + + if (extension->priv->message_timeout_id != 0) { + return TRUE; + } + + if (!g_queue_is_empty (extension->priv->message_queue)) { + return TRUE; + } + + return FALSE; +} + + +static GIcon * +gdm_smartcard_extension_get_icon (GdmLoginExtension *login_extension) +{ + GdmSmartcardExtension *extension = GDM_SMARTCARD_EXTENSION (login_extension); + return g_object_ref (extension->priv->icon); +} + +static char * +gdm_smartcard_extension_get_name (GdmLoginExtension *login_extension) +{ + return g_strdup (_("Smartcard Authentication")); +} + +static char * +gdm_smartcard_extension_get_description (GdmLoginExtension *login_extension) +{ + return g_strdup (_("Log into session with smartcard")); +} + +static gboolean +gdm_smartcard_extension_is_choosable (GdmLoginExtension *login_extension) +{ + return TRUE; +} + +static gboolean +gdm_smartcard_extension_is_visible (GdmLoginExtension *login_extension) +{ + GdmSmartcardExtension *extension = GDM_SMARTCARD_EXTENSION (login_extension); + + char *contents, **lines, *pid_dir; + gboolean ret; + guint i; + pid_t pid; + + if (!g_settings_get_boolean (extension->priv->settings, "active")) { + return FALSE; + } + + /* FIXME: we should rework things so we find out from the worker that + * there's no daemon running instead of like this. + */ + if (g_file_get_contents ("/var/run/pcscd.pid", + &contents, NULL, NULL) == FALSE) { + return FALSE; + } + + pid = (pid_t) atoi (contents); + g_free (contents); + + if (pid == 0) { + return FALSE; + } + + pid_dir = g_strdup_printf ("/proc/%d", (int) pid); + if (!g_file_test (pid_dir, G_FILE_TEST_EXISTS)) { + g_free (pid_dir); + return FALSE; + } + g_free (pid_dir); + + return TRUE; +} + +static void +gdm_login_extension_iface_init (GdmLoginExtensionIface *iface) +{ + iface->get_icon = gdm_smartcard_extension_get_icon; + iface->get_description = gdm_smartcard_extension_get_description; + iface->get_name = gdm_smartcard_extension_get_name; + iface->is_choosable = gdm_smartcard_extension_is_choosable; + iface->is_visible = gdm_smartcard_extension_is_visible; + iface->queue_message = gdm_smartcard_extension_queue_message; + iface->ask_question = gdm_smartcard_extension_ask_question; + iface->ask_secret = gdm_smartcard_extension_ask_secret; + iface->reset = gdm_smartcard_extension_reset; + iface->set_ready = gdm_smartcard_extension_set_ready; + iface->get_service_name = gdm_smartcard_extension_get_service_name; + iface->get_page = gdm_smartcard_extension_get_page; + iface->get_actions = gdm_smartcard_extension_get_actions; + iface->focus = gdm_smartcard_extension_focus; + iface->has_queued_messages = gdm_smartcard_extension_has_queued_messages; +} + +static void +gdm_smartcard_extension_class_init (GdmSmartcardExtensionClass *extension_class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (extension_class); + + object_class->finalize = gdm_smartcard_extension_finalize; + + g_type_class_add_private (extension_class, + sizeof (GdmSmartcardExtensionPrivate)); +} + +static void +gdm_smartcard_extension_finalize (GObject *object) +{ + GdmSmartcardExtension *extension = GDM_SMARTCARD_EXTENSION (object); + + if (extension->priv->worker_pid > 0) { + stop_watching_for_smartcards (extension); + } + + purge_message_queue (extension); +} + +static void +create_page (GdmSmartcardExtension *extension) +{ + GtkBuilder *builder; + GObject *object; + GError *error; + + builder = gtk_builder_new (); + + error = NULL; + gtk_builder_add_from_file (builder, + PLUGINDATADIR "/page.ui", + &error); + + if (error != NULL) { + g_warning ("Could not load UI file: %s", error->message); + g_error_free (error); + return; + } + + object = gtk_builder_get_object (builder, "page"); + g_object_ref (object); + + extension->priv->page = GTK_WIDGET (object); + + object = gtk_builder_get_object (builder, "auth-prompt-label"); + g_object_ref (object); + extension->priv->prompt_label = GTK_WIDGET (object); + gtk_widget_hide (extension->priv->prompt_label); + + object = gtk_builder_get_object (builder, "auth-prompt-entry"); + g_object_ref (object); + extension->priv->prompt_entry = GTK_WIDGET (object); + gtk_widget_hide (extension->priv->prompt_entry); + + object = gtk_builder_get_object (builder, "auth-message-label"); + g_object_ref (object); + extension->priv->message_label = GTK_WIDGET (object); + gtk_widget_show (extension->priv->message_label); + + g_object_unref (builder); +} + +static void +on_activate_log_in (GdmSmartcardExtension *extension) +{ + request_answer (extension); + gtk_action_set_sensitive (extension->priv->login_action, FALSE); +} + +static void +create_actions (GdmSmartcardExtension *extension) +{ + GtkAction *action; + + extension->priv->actions = gtk_action_group_new (GDM_SMARTCARD_EXTENSION_NAME); + + action = gtk_action_new (GDM_LOGIN_EXTENSION_DEFAULT_ACTION, + _("Log In"), NULL, NULL); + g_signal_connect_swapped (action, "activate", + G_CALLBACK (on_activate_log_in), extension); + g_object_set (G_OBJECT (action), "icon-name", "go-home", NULL); + gtk_action_group_add_action (extension->priv->actions, + action); + + gtk_action_set_visible (action, FALSE); + extension->priv->login_action = action; +} + +static void +gdm_smartcard_extension_init (GdmSmartcardExtension *extension) +{ + extension->priv = G_TYPE_INSTANCE_GET_PRIVATE (extension, + GDM_TYPE_SMARTCARD_EXTENSION, + GdmSmartcardExtensionPrivate); + + extension->priv->icon = g_themed_icon_new ("gdm-smartcard"); + create_page (extension); + create_actions (extension); + + extension->priv->message_queue = g_queue_new (); + + extension->priv->settings = g_settings_new ("org.gnome.display-manager.extensions.smartcard"); + + gdm_smartcard_extension_reset (GDM_LOGIN_EXTENSION (extension)); +} + +void +g_io_module_load (GIOModule *module) +{ + g_io_extension_point_implement (GDM_LOGIN_EXTENSION_POINT_NAME, + GDM_TYPE_SMARTCARD_EXTENSION, + GDM_SMARTCARD_EXTENSION_NAME, + 0); +} + +void +g_io_module_unload (GIOModule *module) +{ +} diff --git a/gui/simple-greeter/extensions/smartcard/gdm-smartcard-extension.h b/gui/simple-greeter/extensions/smartcard/gdm-smartcard-extension.h new file mode 100644 index 00000000..0689bd28 --- /dev/null +++ b/gui/simple-greeter/extensions/smartcard/gdm-smartcard-extension.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2009 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, 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Written By: Ray Strode + */ + +#ifndef __GDM_SMARTCARD_EXTENSION_H +#define __GDM_SMARTCARD_EXTENSION_H + +#include +#include "gdm-login-extension.h" + +G_BEGIN_DECLS + +#define GDM_TYPE_SMARTCARD_EXTENSION (gdm_smartcard_extension_get_type ()) +#define GDM_SMARTCARD_EXTENSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDM_TYPE_SMARTCARD_EXTENSION, GdmSmartcardExtension)) +#define GDM_SMARTCARD_EXTENSION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDM_TYPE_SMARTCARD_EXTENSION, GdmSmartcardExtensionClass)) +#define GDM_IS_SMARTCARD_EXTENSION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDM_TYPE_SMARTCARD_EXTENSION)) +#define GDM_IS_SMARTCARD_EXTENSION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDM_TYPE_SMARTCARD_EXTENSION)) +#define GDM_SMARTCARD_EXTENSION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GDM_TYPE_SMARTCARD_EXTENSION, GdmSmartcardExtensionClass)) + +#define GDM_SMARTCARD_EXTENSION_NAME "gdm-smartcard-extension" + +typedef struct _GdmSmartcardExtensionPrivate GdmSmartcardExtensionPrivate; + +typedef struct +{ + GObject parent; + GdmSmartcardExtensionPrivate *priv; +} GdmSmartcardExtension; + +typedef struct +{ + GObjectClass parent_class; +} GdmSmartcardExtensionClass; + +GType gdm_smartcard_extension_get_type (void); + +G_END_DECLS + +#endif /* GDM_SMARTCARD_EXTENSION_H */ diff --git a/gui/simple-greeter/extensions/smartcard/gdm-smartcard-manager.c b/gui/simple-greeter/extensions/smartcard/gdm-smartcard-manager.c new file mode 100644 index 00000000..dfc6ed09 --- /dev/null +++ b/gui/simple-greeter/extensions/smartcard/gdm-smartcard-manager.c @@ -0,0 +1,1485 @@ +/* gdm-smartcard-manager.c - object for monitoring smartcard insertion and + * removal events + * + * Copyright (C) 2006, 2009 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, 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Written By: Ray Strode + */ +#define _GNU_SOURCE +#include "gdm-smartcard-manager.h" + +#define GDM_SMARTCARD_ENABLE_INTERNAL_API +#include "gdm-smartcard.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#ifndef GDM_SMARTCARD_MANAGER_DRIVER +#define GDM_SMARTCARD_MANAGER_DRIVER LIBDIR"/pkcs11/libcoolkeypk11.so" +#endif + +#ifndef GDM_SMARTCARD_MANAGER_NSS_DB +#define GDM_SMARTCARD_MANAGER_NSS_DB SYSCONFDIR"/pki/nssdb" +#endif + +#ifndef GDM_MAX_OPEN_FILE_DESCRIPTORS +#define GDM_MAX_OPEN_FILE_DESCRIPTORS 1024 +#endif + +#ifndef GDM_OPEN_FILE_DESCRIPTORS_DIR +#define GDM_OPEN_FILE_DESCRIPTORS_DIR "/proc/self/fd" +#endif + +typedef enum _GdmSmartcardManagerState GdmSmartcardManagerState; +typedef struct _GdmSmartcardManagerWorker GdmSmartcardManagerWorker; + +enum _GdmSmartcardManagerState { + GDM_SMARTCARD_MANAGER_STATE_STOPPED = 0, + GDM_SMARTCARD_MANAGER_STATE_STARTING, + GDM_SMARTCARD_MANAGER_STATE_STARTED, + GDM_SMARTCARD_MANAGER_STATE_STOPPING, +}; + +struct _GdmSmartcardManagerPrivate { + GdmSmartcardManagerState state; + GList *modules; + char *module_path; + + GList *workers; + + GPid smartcard_event_watcher_pid; + GHashTable *smartcards; + + guint poll_timeout_id; + + guint32 is_unstoppable : 1; + guint32 nss_is_loaded : 1; +}; + +struct _GdmSmartcardManagerWorker { + GdmSmartcardManager *manager; + gint manager_fd; + + GThread *thread; + SECMODModule *module; + GHashTable *smartcards; + gint fd; + GSource *event_source; + + guint32 nss_is_loaded : 1; +}; + +static void gdm_smartcard_manager_finalize (GObject *object); +static void gdm_smartcard_manager_class_install_signals (GdmSmartcardManagerClass *service_class); +static void gdm_smartcard_manager_class_install_properties (GdmSmartcardManagerClass *service_class); +static void gdm_smartcard_manager_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gdm_smartcard_manager_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void gdm_smartcard_manager_set_module_path (GdmSmartcardManager *manager, + const char *module_path); +static void gdm_smartcard_manager_card_removed_handler (GdmSmartcardManager *manager, + GdmSmartcard *card); +static void gdm_smartcard_manager_card_inserted_handler (GdmSmartcardManager *manager_class, + GdmSmartcard *card); +static gboolean gdm_smartcard_manager_stop_now (GdmSmartcardManager *manager); +static void gdm_smartcard_manager_queue_stop (GdmSmartcardManager *manager); + +static GdmSmartcardManagerWorker *gdm_smartcard_manager_create_worker (GdmSmartcardManager *manager, + SECMODModule *module); + +static GdmSmartcardManagerWorker * gdm_smartcard_manager_worker_new (GdmSmartcardManager *manager, + int worker_fd, + int manager_fd, + SECMODModule *module); +static void gdm_smartcard_manager_worker_free (GdmSmartcardManagerWorker *worker); +static gboolean gdm_open_pipe (gint *write_fd, gint *read_fd); +static gboolean sc_read_bytes (gint fd, gpointer bytes, gsize num_bytes); +static gboolean sc_write_bytes (gint fd, gconstpointer bytes, gsize num_bytes); +static GdmSmartcard *sc_read_smartcard (gint fd, SECMODModule *module); +static gboolean sc_write_smartcard (gint fd, GdmSmartcard *card); + +enum { + PROP_0 = 0, + PROP_MODULE_PATH, + NUMBER_OF_PROPERTIES +}; + +enum { + SMARTCARD_INSERTED = 0, + SMARTCARD_REMOVED, + ERROR, + NUMBER_OF_SIGNALS +}; + +static guint gdm_smartcard_manager_signals[NUMBER_OF_SIGNALS]; + +G_DEFINE_TYPE (GdmSmartcardManager, + gdm_smartcard_manager, + G_TYPE_OBJECT); + +static void +gdm_smartcard_manager_class_init (GdmSmartcardManagerClass *manager_class) +{ + GObjectClass *gobject_class; + + gobject_class = G_OBJECT_CLASS (manager_class); + + gobject_class->finalize = gdm_smartcard_manager_finalize; + + gdm_smartcard_manager_class_install_signals (manager_class); + gdm_smartcard_manager_class_install_properties (manager_class); + + g_type_class_add_private (manager_class, + sizeof (GdmSmartcardManagerPrivate)); +} + +static void +gdm_smartcard_manager_class_install_properties (GdmSmartcardManagerClass *card_class) +{ + GObjectClass *object_class; + GParamSpec *param_spec; + + object_class = G_OBJECT_CLASS (card_class); + object_class->set_property = gdm_smartcard_manager_set_property; + object_class->get_property = gdm_smartcard_manager_get_property; + + param_spec = g_param_spec_string ("module-path", _("Module Path"), + _("path to smartcard PKCS #11 driver"), + NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (object_class, PROP_MODULE_PATH, param_spec); +} + +static void +gdm_smartcard_manager_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GdmSmartcardManager *manager = GDM_SMARTCARD_MANAGER (object); + + switch (prop_id) { + case PROP_MODULE_PATH: + gdm_smartcard_manager_set_module_path (manager, + g_value_get_string (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +static void +gdm_smartcard_manager_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GdmSmartcardManager *manager = GDM_SMARTCARD_MANAGER (object); + char *module_path; + + switch (prop_id) { + case PROP_MODULE_PATH: + module_path = gdm_smartcard_manager_get_module_path (manager); + g_value_set_string (value, module_path); + g_free (module_path); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + +char * +gdm_smartcard_manager_get_module_path (GdmSmartcardManager *manager) +{ + return manager->priv->module_path; +} + +static void +gdm_smartcard_manager_set_module_path (GdmSmartcardManager *manager, + const char *module_path) +{ + if ((manager->priv->module_path == NULL) && (module_path == NULL)) { + return; + } + + if (((manager->priv->module_path == NULL) || + (module_path == NULL) || + (strcmp (manager->priv->module_path, module_path) != 0))) { + g_free (manager->priv->module_path); + manager->priv->module_path = g_strdup (module_path); + g_object_notify (G_OBJECT (manager), "module-path"); + } +} + +static void +gdm_smartcard_manager_card_removed_handler (GdmSmartcardManager *manager, + GdmSmartcard *card) +{ + g_debug ("informing smartcard of its removal"); + _gdm_smartcard_set_state (card, GDM_SMARTCARD_STATE_REMOVED); + g_debug ("done"); +} + +static void +gdm_smartcard_manager_card_inserted_handler (GdmSmartcardManager *manager, + GdmSmartcard *card) +{ + g_debug ("informing smartcard of its insertion"); + + _gdm_smartcard_set_state (card, GDM_SMARTCARD_STATE_INSERTED); + g_debug ("done"); + +} + +static void +gdm_smartcard_manager_class_install_signals (GdmSmartcardManagerClass *manager_class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (manager_class); + + gdm_smartcard_manager_signals[SMARTCARD_INSERTED] = + g_signal_new ("smartcard-inserted", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GdmSmartcardManagerClass, + smartcard_inserted), + NULL, NULL, g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + manager_class->smartcard_inserted = gdm_smartcard_manager_card_inserted_handler; + + gdm_smartcard_manager_signals[SMARTCARD_REMOVED] = + g_signal_new ("smartcard-removed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_FIRST, + G_STRUCT_OFFSET (GdmSmartcardManagerClass, + smartcard_removed), + NULL, NULL, g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + manager_class->smartcard_removed = gdm_smartcard_manager_card_removed_handler; + + gdm_smartcard_manager_signals[ERROR] = + g_signal_new ("error", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GdmSmartcardManagerClass, error), + NULL, NULL, g_cclosure_marshal_VOID__POINTER, + G_TYPE_NONE, 1, G_TYPE_POINTER); + manager_class->error = NULL; +} + +static gboolean +sc_slot_id_equal (CK_SLOT_ID *slot_id_1, + CK_SLOT_ID *slot_id_2) +{ + g_assert (slot_id_1 != NULL); + g_assert (slot_id_2 != NULL); + + return *slot_id_1 == *slot_id_2; +} + +static gboolean +sc_slot_id_hash (CK_SLOT_ID *slot_id) +{ + guint32 upper_bits, lower_bits; + gint temp; + + if (sizeof (CK_SLOT_ID) == sizeof (gint)) { + return g_int_hash (slot_id); + } + + upper_bits = ((*slot_id) >> 31) - 1; + lower_bits = (*slot_id) & 0xffffffff; + + /* The upper bits are almost certainly always zero, + * so let's degenerate to g_int_hash for the + * (very) common case + */ + temp = lower_bits + upper_bits; + return upper_bits + g_int_hash (&temp); +} + +static void +gdm_smartcard_manager_init (GdmSmartcardManager *manager) +{ + g_debug ("initializing smartcard manager"); + + manager->priv = G_TYPE_INSTANCE_GET_PRIVATE (manager, + GDM_TYPE_SMARTCARD_MANAGER, + GdmSmartcardManagerPrivate); + manager->priv->poll_timeout_id = 0; + manager->priv->is_unstoppable = FALSE; + + manager->priv->smartcards = + g_hash_table_new_full (g_str_hash, + g_str_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_object_unref); + + if (!g_thread_supported()) { + g_thread_init (NULL); + } + +} + +static void +gdm_smartcard_manager_finalize (GObject *object) +{ + GdmSmartcardManager *manager; + GObjectClass *gobject_class; + + manager = GDM_SMARTCARD_MANAGER (object); + gobject_class = + G_OBJECT_CLASS (gdm_smartcard_manager_parent_class); + + gdm_smartcard_manager_stop_now (manager); + + g_hash_table_destroy (manager->priv->smartcards); + manager->priv->smartcards = NULL; + + gobject_class->finalize (object); +} + +GQuark +gdm_smartcard_manager_error_quark (void) +{ + static GQuark error_quark = 0; + + if (error_quark == 0) { + error_quark = g_quark_from_static_string ("gdm-smartcard-manager-error-quark"); + } + + return error_quark; +} + +GdmSmartcardManager * +gdm_smartcard_manager_new (const char *module_path) +{ + GdmSmartcardManager *instance; + + instance = GDM_SMARTCARD_MANAGER (g_object_new (GDM_TYPE_SMARTCARD_MANAGER, + "module-path", module_path, + NULL)); + + return instance; +} + +static void +gdm_smartcard_manager_emit_error (GdmSmartcardManager *manager, + GError *error) +{ + manager->priv->is_unstoppable = TRUE; + g_signal_emit (manager, gdm_smartcard_manager_signals[ERROR], 0, + error); + manager->priv->is_unstoppable = FALSE; +} + +static void +gdm_smartcard_manager_emit_smartcard_inserted (GdmSmartcardManager *manager, + GdmSmartcard *card) +{ + manager->priv->is_unstoppable = TRUE; + g_signal_emit (manager, gdm_smartcard_manager_signals[SMARTCARD_INSERTED], 0, + card); + manager->priv->is_unstoppable = FALSE; +} + +static void +gdm_smartcard_manager_emit_smartcard_removed (GdmSmartcardManager *manager, + GdmSmartcard *card) +{ + manager->priv->is_unstoppable = TRUE; + g_signal_emit (manager, gdm_smartcard_manager_signals[SMARTCARD_REMOVED], 0, + card); + manager->priv->is_unstoppable = FALSE; +} + +static gboolean +gdm_smartcard_manager_check_for_and_process_events (GIOChannel *io_channel, + GIOCondition condition, + GdmSmartcardManagerWorker *worker) +{ + GdmSmartcard *card; + GdmSmartcardManager *manager; + gboolean should_stop; + guchar event_type; + char *card_name; + gint fd; + + manager = worker->manager; + + g_debug ("event!"); + card = NULL; + should_stop = (condition & G_IO_HUP) || (condition & G_IO_ERR); + + if (should_stop) { + g_debug ("received %s on event socket, stopping " + "manager...", + (condition & G_IO_HUP) && (condition & G_IO_ERR)? + "error and hangup" : + (condition & G_IO_HUP)? + "hangup" : "error"); + } + + if (!(condition & G_IO_IN)) { + g_debug ("nevermind outta here!"); + goto out; + } + + fd = g_io_channel_unix_get_fd (io_channel); + + event_type = '\0'; + if (!sc_read_bytes (fd, &event_type, 1)) { + g_debug ("could not read event type, stopping"); + should_stop = TRUE; + goto out; + } + + card = sc_read_smartcard (fd, worker->module); + + if (card == NULL) { + g_debug ("could not read card, stopping"); + should_stop = TRUE; + goto out; + } + + card_name = gdm_smartcard_get_name (card); + g_debug ("card '%s' had event %c", card_name, event_type); + + switch (event_type) { + case 'I': + g_hash_table_replace (manager->priv->smartcards, + card_name, card); + card_name = NULL; + + gdm_smartcard_manager_emit_smartcard_inserted (manager, card); + card = NULL; + break; + + case 'R': + gdm_smartcard_manager_emit_smartcard_removed (manager, card); + if (!g_hash_table_remove (manager->priv->smartcards, card_name)) { + g_debug ("got removal event of unknown card!"); + } + g_free (card_name); + card_name = NULL; + card = NULL; + break; + + default: + g_free (card_name); + card_name = NULL; + g_object_unref (card); + + should_stop = TRUE; + break; + } + +out: + if (should_stop) { + GError *error; + + error = g_error_new (GDM_SMARTCARD_MANAGER_ERROR, + GDM_SMARTCARD_MANAGER_ERROR_WATCHING_FOR_EVENTS, + "%s", (condition & G_IO_IN) ? g_strerror (errno) : _("received error or hang up from event source")); + + gdm_smartcard_manager_emit_error (manager, error); + g_error_free (error); + gdm_smartcard_manager_stop_now (manager); + return FALSE; + } + + return TRUE; +} + +static void +stop_manager (GdmSmartcardManager *manager) +{ + manager->priv->state = GDM_SMARTCARD_MANAGER_STATE_STOPPED; + + if (manager->priv->nss_is_loaded) { + NSS_Shutdown (); + manager->priv->nss_is_loaded = FALSE; + } + g_debug ("smartcard manager stopped"); +} + +static void +stop_worker (GdmSmartcardManagerWorker *worker) +{ + GdmSmartcardManager *manager; + + manager = worker->manager; + + if (worker->event_source != NULL) { + g_source_destroy (worker->event_source); + worker->event_source = NULL; + } + + if (worker->thread != NULL) { + SECMOD_CancelWait (worker->module); + worker->thread = NULL; + } + + SECMOD_DestroyModule (worker->module); + manager->priv->workers = g_list_remove (manager->priv->workers, worker); + + if (manager->priv->workers == NULL && manager->priv->state != GDM_SMARTCARD_MANAGER_STATE_STOPPED) { + stop_manager (manager); + } +} + +static void +gdm_smartcard_manager_event_processing_stopped_handler (GdmSmartcardManagerWorker *worker) +{ + worker->event_source = NULL; + + stop_worker (worker); +} + +static gboolean +gdm_open_pipe (gint *write_fd, + gint *read_fd) +{ + gint pipe_fds[2] = { -1, -1 }; + + g_assert (write_fd != NULL); + g_assert (read_fd != NULL); + + if (pipe (pipe_fds) < 0) { + return FALSE; + } + + if (fcntl (pipe_fds[0], F_SETFD, FD_CLOEXEC) < 0) { + close (pipe_fds[0]); + close (pipe_fds[1]); + return FALSE; + } + + if (fcntl (pipe_fds[1], F_SETFD, FD_CLOEXEC) < 0) { + close (pipe_fds[0]); + close (pipe_fds[1]); + return FALSE; + } + + *read_fd = pipe_fds[0]; + *write_fd = pipe_fds[1]; + + return TRUE; +} + +static void +gdm_smartcard_manager_stop_watching_for_events (GdmSmartcardManager *manager) +{ + GList *node; + + node = manager->priv->workers; + while (node != NULL) { + GdmSmartcardManagerWorker *worker; + GList *next_node; + + worker = (GdmSmartcardManagerWorker *) node->data; + next_node = node->next; + + stop_worker (worker); + + node = next_node; + } +} + +static gboolean +sc_load_nss (GError **error) +{ + SECStatus status = SECSuccess; + 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'", + GDM_SMARTCARD_MANAGER_NSS_DB); + + PR_Init (PR_USER_THREAD, PR_PRIORITY_NORMAL, 0); + + status = NSS_Initialize (GDM_SMARTCARD_MANAGER_NSS_DB, + "", "", SECMOD_DB, flags); + + if (status != SECSuccess) { + 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"); + g_set_error (error, + GDM_SMARTCARD_MANAGER_ERROR, + GDM_SMARTCARD_MANAGER_ERROR_WITH_NSS, + _("NSS security system could not be initialized")); + goto out; + } + + error_message = g_slice_alloc0 (error_message_size); + PR_GetErrorText (error_message); + + g_set_error (error, + GDM_SMARTCARD_MANAGER_ERROR, + GDM_SMARTCARD_MANAGER_ERROR_WITH_NSS, + "%s", error_message); + g_debug ("NSS security system could not be initialized - %s", + error_message); + + g_slice_free1 (error_message_size, error_message); + + goto out; + } + + g_debug ("NSS database sucessfully loaded"); + return TRUE; + +out: + g_debug ("NSS database couldn't be sucessfully loaded"); + return FALSE; +} + +static GList * +get_available_modules (GdmSmartcardManager *manager) +{ + SECMODModuleList *module_list, *tmp; + GList *modules; + + g_debug ("Getting list of suitable modules"); + + module_list = SECMOD_GetDefaultModuleList (); + modules = NULL; + for (tmp = module_list; tmp != NULL; tmp = tmp->next) { + if (!SECMOD_HasRemovableSlots (tmp->module) || + !tmp->module->loaded) + continue; + + g_debug ("Using module '%s'", tmp->module->commonName); + + modules = g_list_prepend (modules, + SECMOD_ReferenceModule (tmp->module)); + } + + return modules; +} + +static gboolean +load_driver (GdmSmartcardManager *manager, + char *module_path, + GError **error) +{ + GList *modules; + char *module_spec; + gboolean module_explicitly_specified; + + g_debug ("attempting to load driver..."); + + modules = NULL; + module_explicitly_specified = module_path != NULL; + if (module_explicitly_specified) { + SECMODModule *module; + + module_spec = g_strdup_printf ("library=\"%s\"", module_path); + g_debug ("loading smartcard driver using spec '%s'", + module_spec); + + module = SECMOD_LoadUserModule (module_spec, + NULL /* parent */, + FALSE /* recurse */); + g_free (module_spec); + module_spec = NULL; + + if (!SECMOD_HasRemovableSlots (module) || + !module->loaded) { + modules = g_list_prepend (modules, module); + } else { + g_debug ("fallback module found but not %s", + SECMOD_HasRemovableSlots (module)? + "removable" : "loaded"); + SECMOD_DestroyModule (module); + } + + } else { + SECMODListLock *lock; + + lock = SECMOD_GetDefaultModuleListLock (); + + if (lock != NULL) { + SECMOD_GetReadLock (lock); + modules = get_available_modules (manager); + SECMOD_ReleaseReadLock (lock); + } + + /* fallback to compiled in driver path + */ + if (modules == NULL) { + SECMODModule *module; + module_path = GDM_SMARTCARD_MANAGER_DRIVER; + module_spec = g_strdup_printf ("library=\"%s\"", module_path); + g_debug ("loading smartcard driver using spec '%s'", + module_spec); + + module = SECMOD_LoadUserModule (module_spec, + NULL /* parent */, + FALSE /* recurse */); + g_free (module_spec); + module_spec = NULL; + + if (!SECMOD_HasRemovableSlots (module) || + !module->loaded) { + modules = g_list_prepend (modules, module); + } else { + g_debug ("fallback module found but not loaded"); + SECMOD_DestroyModule (module); + } + } + + } + + if (!module_explicitly_specified && modules == NULL) { + g_set_error (error, + GDM_SMARTCARD_MANAGER_ERROR, + GDM_SMARTCARD_MANAGER_ERROR_LOADING_DRIVER, + _("no suitable smartcard driver could be found")); + } else if (modules == NULL) { + + gsize error_message_size; + char *error_message; + + error_message_size = PR_GetErrorTextLength (); + + if (error_message_size == 0) { + g_debug ("smartcard driver '%s' could not be loaded", + module_path); + g_set_error (error, + GDM_SMARTCARD_MANAGER_ERROR, + GDM_SMARTCARD_MANAGER_ERROR_LOADING_DRIVER, + _("smartcard driver '%s' could not be " + "loaded"), module_path); + goto out; + } + + error_message = g_slice_alloc0 (error_message_size); + PR_GetErrorText (error_message); + + g_set_error (error, + GDM_SMARTCARD_MANAGER_ERROR, + GDM_SMARTCARD_MANAGER_ERROR_LOADING_DRIVER, + "%s", error_message); + + g_debug ("smartcard driver '%s' could not be loaded - %s", + module_path, error_message); + g_slice_free1 (error_message_size, error_message); + } + + manager->priv->modules = modules; +out: + return manager->priv->modules != NULL; +} + +static void +gdm_smartcard_manager_get_all_cards (GdmSmartcardManager *manager) +{ + GList *node; + int i; + + node = manager->priv->workers; + while (node != NULL) { + + GdmSmartcardManagerWorker *worker; + + worker = (GdmSmartcardManagerWorker *) node->data; + + for (i = 0; i < worker->module->slotCount; i++) { + GdmSmartcard *card; + CK_SLOT_ID slot_id; + gint slot_series; + char *card_name; + + slot_id = PK11_GetSlotID (worker->module->slots[i]); + slot_series = PK11_GetSlotSeries (worker->module->slots[i]); + + card = _gdm_smartcard_new (worker->module, + slot_id, slot_series); + + card_name = gdm_smartcard_get_name (card); + + g_hash_table_replace (manager->priv->smartcards, + card_name, card); + } + node = node->next; + } +} + +static GdmSmartcardManagerWorker * +start_worker (GdmSmartcardManager *manager, + SECMODModule *module, + GError **error) +{ + GIOChannel *io_channel; + GSource *source; + GdmSmartcardManagerWorker *worker; + + worker = gdm_smartcard_manager_create_worker (manager, module); + + if (worker == NULL) { + g_set_error (error, + GDM_SMARTCARD_MANAGER_ERROR, + GDM_SMARTCARD_MANAGER_ERROR_WATCHING_FOR_EVENTS, + _("could not watch for incoming card events - %s"), + g_strerror (errno)); + + goto out; + } + + io_channel = g_io_channel_unix_new (worker->manager_fd); + + source = g_io_create_watch (io_channel, G_IO_IN | G_IO_HUP); + g_io_channel_unref (io_channel); + io_channel = NULL; + + worker->event_source = source; + + g_source_set_callback (worker->event_source, + (GSourceFunc) (GIOFunc) + gdm_smartcard_manager_check_for_and_process_events, + worker, + (GDestroyNotify) + gdm_smartcard_manager_event_processing_stopped_handler); + g_source_attach (worker->event_source, NULL); + g_source_unref (worker->event_source); +out: + return worker; +} + +static void +start_workers (GdmSmartcardManager *manager) +{ + GList *node; + + node = manager->priv->modules; + while (node != NULL) { + SECMODModule *module; + GdmSmartcardManagerWorker *worker; + GError *error; + + module = (SECMODModule *) node->data; + + error = NULL; + worker = start_worker (manager, module, &error); + if (worker == NULL) { + g_warning ("%s", error->message); + g_error_free (error); + } else { + manager->priv->workers = g_list_prepend (manager->priv->workers, + worker); + } + node = node->next; + } +} + +gboolean +gdm_smartcard_manager_start (GdmSmartcardManager *manager, + GError **error) +{ + GError *nss_error; + + if (manager->priv->state == GDM_SMARTCARD_MANAGER_STATE_STARTED) { + g_debug ("smartcard manager already started"); + return TRUE; + } + + manager->priv->state = GDM_SMARTCARD_MANAGER_STATE_STARTING; + + nss_error = NULL; + if (!manager->priv->nss_is_loaded && !sc_load_nss (&nss_error)) { + g_propagate_error (error, nss_error); + goto out; + } + manager->priv->nss_is_loaded = TRUE; + + if (manager->priv->modules == NULL) { + if (!load_driver (manager, manager->priv->module_path, &nss_error)) { + g_propagate_error (error, nss_error); + goto out; + } + } + + start_workers (manager); + + /* populate the hash with cards that are already inserted + */ + gdm_smartcard_manager_get_all_cards (manager); + + manager->priv->state = GDM_SMARTCARD_MANAGER_STATE_STARTED; + +out: + /* don't leave it in a half started state + */ + if (manager->priv->state != GDM_SMARTCARD_MANAGER_STATE_STARTED) { + g_debug ("smartcard manager could not be completely started"); + gdm_smartcard_manager_stop (manager); + } else { + g_debug ("smartcard manager started"); + } + + return manager->priv->state == GDM_SMARTCARD_MANAGER_STATE_STARTED; +} + +static gboolean +gdm_smartcard_manager_stop_now (GdmSmartcardManager *manager) +{ + if (manager->priv->state == GDM_SMARTCARD_MANAGER_STATE_STOPPED) { + return FALSE; + } + + gdm_smartcard_manager_stop_watching_for_events (manager); + + return FALSE; +} + +static void +gdm_smartcard_manager_queue_stop (GdmSmartcardManager *manager) +{ + + manager->priv->state = GDM_SMARTCARD_MANAGER_STATE_STOPPING; + + g_idle_add ((GSourceFunc) gdm_smartcard_manager_stop_now, manager); +} + +void +gdm_smartcard_manager_stop (GdmSmartcardManager *manager) +{ + if (manager->priv->state == GDM_SMARTCARD_MANAGER_STATE_STOPPED) { + return; + } + + if (manager->priv->is_unstoppable) { + gdm_smartcard_manager_queue_stop (manager); + return; + } + + gdm_smartcard_manager_stop_now (manager); +} + +static GdmSmartcardManagerWorker * +gdm_smartcard_manager_worker_new (GdmSmartcardManager *manager, + gint worker_fd, + gint manager_fd, + SECMODModule *module) +{ + GdmSmartcardManagerWorker *worker; + + worker = g_slice_new0 (GdmSmartcardManagerWorker); + worker->manager = manager; + worker->fd = worker_fd; + worker->manager_fd = manager_fd; + worker->module = module; + + worker->smartcards = + g_hash_table_new_full ((GHashFunc) sc_slot_id_hash, + (GEqualFunc) sc_slot_id_equal, + (GDestroyNotify) g_free, + (GDestroyNotify) g_object_unref); + + return worker; +} + +static void +gdm_smartcard_manager_worker_free (GdmSmartcardManagerWorker *worker) +{ + if (worker->smartcards != NULL) { + g_hash_table_destroy (worker->smartcards); + worker->smartcards = NULL; + } + + g_slice_free (GdmSmartcardManagerWorker, worker); +} + +static gboolean +sc_read_bytes (gint fd, gpointer bytes, gsize num_bytes) +{ + size_t bytes_left; + size_t total_bytes_read; + ssize_t bytes_read; + + bytes_left = (size_t) num_bytes; + total_bytes_read = 0; + + do { + bytes_read = read (fd, (gchar *) bytes + total_bytes_read, bytes_left); + g_assert (bytes_read <= (ssize_t) bytes_left); + + if (bytes_read <= 0) { + if ((bytes_read < 0) && (errno == EINTR || errno == EAGAIN)) { + continue; + } + + bytes_left = 0; + } else { + bytes_left -= bytes_read; + total_bytes_read += bytes_read; + } + } while (bytes_left > 0); + + if (total_bytes_read < (size_t) num_bytes) { + return FALSE; + } + + return TRUE; +} + +static gboolean +sc_write_bytes (gint fd, gconstpointer bytes, gsize num_bytes) +{ + size_t bytes_left; + size_t total_bytes_written; + ssize_t bytes_written; + + bytes_left = (size_t) num_bytes; + total_bytes_written = 0; + + do { + bytes_written = write (fd, (gchar *) bytes + total_bytes_written, bytes_left); + g_assert (bytes_written <= (ssize_t) bytes_left); + + if (bytes_written <= 0) { + if ((bytes_written < 0) && (errno == EINTR || errno == EAGAIN)) { + continue; + } + + bytes_left = 0; + } else { + bytes_left -= bytes_written; + total_bytes_written += bytes_written; + } + } while (bytes_left > 0); + + if (total_bytes_written < (size_t) num_bytes) { + return FALSE; + } + + return TRUE; +} + +static GdmSmartcard * +sc_read_smartcard (gint fd, + SECMODModule *module) +{ + GdmSmartcard *card; + char *card_name; + gsize card_name_size; + + card_name_size = 0; + if (!sc_read_bytes (fd, &card_name_size, sizeof (card_name_size))) { + return NULL; + } + + card_name = g_slice_alloc0 (card_name_size); + if (!sc_read_bytes (fd, card_name, card_name_size)) { + g_slice_free1 (card_name_size, card_name); + return NULL; + } + card = _gdm_smartcard_new_from_name (module, card_name); + g_slice_free1 (card_name_size, card_name); + + return card; +} + +static gboolean +sc_write_smartcard (gint fd, + GdmSmartcard *card) +{ + gsize card_name_size; + char *card_name; + + card_name = gdm_smartcard_get_name (card); + card_name_size = strlen (card_name) + 1; + + if (!sc_write_bytes (fd, &card_name_size, sizeof (card_name_size))) { + g_free (card_name); + return FALSE; + } + + if (!sc_write_bytes (fd, card_name, card_name_size)) { + g_free (card_name); + return FALSE; + } + g_free (card_name); + + return TRUE; +} + +static gboolean +gdm_smartcard_manager_worker_emit_smartcard_removed (GdmSmartcardManagerWorker *worker, + GdmSmartcard *card, + GError **error) +{ + g_debug ("card '%s' removed!", gdm_smartcard_get_name (card)); + + if (!sc_write_bytes (worker->fd, "R", 1)) { + goto error_out; + } + + if (!sc_write_smartcard (worker->fd, card)) { + goto error_out; + } + + return TRUE; + +error_out: + g_set_error (error, GDM_SMARTCARD_MANAGER_ERROR, + GDM_SMARTCARD_MANAGER_ERROR_REPORTING_EVENTS, + "%s", g_strerror (errno)); + return FALSE; +} + +static gboolean +gdm_smartcard_manager_worker_emit_smartcard_inserted (GdmSmartcardManagerWorker *worker, + GdmSmartcard *card, + GError **error) +{ + + g_debug ("card '%s' inserted!", gdm_smartcard_get_name (card)); + if (!sc_write_bytes (worker->fd, "I", 1)) { + goto error_out; + } + + if (!sc_write_smartcard (worker->fd, card)) { + goto error_out; + } + + return TRUE; + +error_out: + g_set_error (error, GDM_SMARTCARD_MANAGER_ERROR, + GDM_SMARTCARD_MANAGER_ERROR_REPORTING_EVENTS, + "%s", g_strerror (errno)); + return FALSE; +} + +static gboolean +gdm_smartcard_manager_worker_watch_for_and_process_event (GdmSmartcardManagerWorker *worker, + GError **error) +{ + PK11SlotInfo *slot; + CK_SLOT_ID slot_id, *key; + gint slot_series, card_slot_series; + GdmSmartcard *card; + GError *processing_error; + + g_debug ("waiting for card event"); + + /* FIXME: we return FALSE quite a bit in this function without cleaning up + * resources. By returning FALSE we're going to ultimately exit anyway, but + * we should still be tidier about things. + */ + + slot = SECMOD_WaitForAnyTokenEvent (worker->module, 0, PR_SecondsToInterval (1)); + + processing_error = NULL; + + if (slot == NULL) { + int error_code; + + error_code = PORT_GetError (); + if ((error_code == 0) || (error_code == SEC_ERROR_NO_EVENT)) { + g_debug ("spurrious event occurred"); + return TRUE; + } + + /* FIXME: is there a function to convert from a PORT error + * code to a translated string? + */ + g_set_error (error, GDM_SMARTCARD_MANAGER_ERROR, + GDM_SMARTCARD_MANAGER_ERROR_WITH_NSS, + _("encountered unexpected error while " + "waiting for smartcard events")); + return FALSE; + } + + /* the slot id and series together uniquely identify a card. + * You can never have two cards with the same slot id at the + * same time, however (I think), so we can key off of it. + */ + slot_id = PK11_GetSlotID (slot); + slot_series = PK11_GetSlotSeries (slot); + + /* First check to see if there is a card that we're currently + * tracking in the slot. + */ + key = g_new (CK_SLOT_ID, 1); + *key = slot_id; + card = g_hash_table_lookup (worker->smartcards, key); + + if (card != NULL) { + card_slot_series = gdm_smartcard_get_slot_series (card); + } else { + card_slot_series = -1; + } + + if (PK11_IsPresent (slot)) { + /* Now, check to see if their is a new card in the slot. + * If there was a different card in the slot now than + * there was before, then we need to emit a removed signal + * for the old card (we don't want unpaired insertion events). + */ + if ((card != NULL) && + card_slot_series != slot_series) { + if (!gdm_smartcard_manager_worker_emit_smartcard_removed (worker, card, &processing_error)) { + g_propagate_error (error, processing_error); + return FALSE; + } + } + + card = _gdm_smartcard_new (worker->module, + slot_id, slot_series); + + g_hash_table_replace (worker->smartcards, + key, card); + key = NULL; + + if (!gdm_smartcard_manager_worker_emit_smartcard_inserted (worker, card, &processing_error)) { + g_propagate_error (error, processing_error); + return FALSE; + } + } else { + /* if we aren't tracking the card, just discard the event. + * We don't want unpaired remove events. Note on startup + * NSS will generate an "insertion" event if a card is + * already inserted in the slot. + */ + if ((card != NULL)) { + /* FIXME: i'm not sure about this code. Maybe we + * shouldn't do this at all, or maybe we should do it + * n times (where n = slot_series - card_slot_series + 1) + * + * Right now, i'm just doing it once. + */ + if ((slot_series - card_slot_series) > 1) { + + if (!gdm_smartcard_manager_worker_emit_smartcard_removed (worker, card, &processing_error)) { + g_propagate_error (error, processing_error); + return FALSE; + } + g_hash_table_remove (worker->smartcards, key); + + card = _gdm_smartcard_new (worker->module, + slot_id, slot_series); + g_hash_table_replace (worker->smartcards, + key, card); + key = NULL; + if (!gdm_smartcard_manager_worker_emit_smartcard_inserted (worker, card, &processing_error)) { + g_propagate_error (error, processing_error); + return FALSE; + } + } + + if (!gdm_smartcard_manager_worker_emit_smartcard_removed (worker, card, &processing_error)) { + g_propagate_error (error, processing_error); + return FALSE; + } + + g_hash_table_remove (worker->smartcards, key); + card = NULL; + } else { + g_debug ("got spurious remove event"); + } + } + + g_free (key); + PK11_FreeSlot (slot); + + return TRUE; +} + +static void +gdm_smartcard_manager_worker_run (GdmSmartcardManagerWorker *worker) +{ + GError *error; + gboolean should_continue; + + do + { + error = NULL; + should_continue = gdm_smartcard_manager_worker_watch_for_and_process_event (worker, &error); + } + while (should_continue); + + if (error != NULL) { + g_debug ("could not process card event - %s", error->message); + g_error_free (error); + } + + gdm_smartcard_manager_worker_free (worker); +} + +static GdmSmartcardManagerWorker * +gdm_smartcard_manager_create_worker (GdmSmartcardManager *manager, + SECMODModule *module) +{ + GdmSmartcardManagerWorker *worker; + gint write_fd, read_fd; + + write_fd = -1; + read_fd = -1; + if (!gdm_open_pipe (&write_fd, &read_fd)) { + return FALSE; + } + + worker = gdm_smartcard_manager_worker_new (manager, + write_fd, + read_fd, + module); + + worker->thread = g_thread_create ((GThreadFunc) + gdm_smartcard_manager_worker_run, + worker, FALSE, NULL); + + if (worker->thread == NULL) { + gdm_smartcard_manager_worker_free (worker); + return NULL; + } + + return worker; +} + +#ifdef GDM_SMARTCARD_MANAGER_ENABLE_TEST +#include + +static GMainLoop *event_loop; +static gboolean should_exit_on_next_remove = FALSE; + +static gboolean +on_timeout (GdmSmartcardManager *manager) +{ + GError *error; + g_print ("Re-enabling manager.\n"); + + if (!gdm_smartcard_manager_start (manager, &error)) { + g_warning ("could not start smartcard manager - %s", + error->message); + g_error_free (error); + return 1; + } + g_print ("Please re-insert smartcard\n"); + + should_exit_on_next_remove = TRUE; + + return FALSE; +} + +static void +on_device_inserted (GdmSmartcardManager *manager, + GdmSmartcard *card) +{ + g_print ("smartcard inserted!\n"); + g_print ("Please remove it.\n"); +} + +static void +on_device_removed (GdmSmartcardManager *manager, + GdmSmartcard *card) +{ + g_print ("smartcard removed!\n"); + + if (should_exit_on_next_remove) { + g_main_loop_quit (event_loop); + } else { + g_print ("disabling manager for 2 seconds\n"); + gdm_smartcard_manager_stop (manager); + g_timeout_add (2000, (GSourceFunc) on_timeout, manager); + } +} + +int +main (int argc, + char *argv[]) +{ + GdmSmartcardManager *manager; + GError *error; + + g_log_set_always_fatal (G_LOG_LEVEL_ERROR + | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING); + + g_type_init (); + + g_message ("creating instance of 'smartcard manager' object..."); + manager = gdm_smartcard_manager_new (NULL); + g_message ("'smartcard manager' object created successfully"); + + g_signal_connect (manager, "smartcard-inserted", + G_CALLBACK (on_device_inserted), NULL); + + g_signal_connect (manager, "smartcard-removed", + G_CALLBACK (on_device_removed), NULL); + + g_message ("starting listener..."); + + error = NULL; + if (!gdm_smartcard_manager_start (manager, &error)) { + g_warning ("could not start smartcard manager - %s", + error->message); + g_error_free (error); + return 1; + } + + event_loop = g_main_loop_new (NULL, FALSE); + g_main_loop_run (event_loop); + g_main_loop_unref (event_loop); + event_loop = NULL; + + g_message ("destroying previously created 'smartcard manager' object..."); + g_object_unref (manager); + manager = NULL; + g_message ("'smartcard manager' object destroyed successfully"); + + return 0; +} +#endif diff --git a/gui/simple-greeter/extensions/smartcard/gdm-smartcard-manager.h b/gui/simple-greeter/extensions/smartcard/gdm-smartcard-manager.h new file mode 100644 index 00000000..932a7037 --- /dev/null +++ b/gui/simple-greeter/extensions/smartcard/gdm-smartcard-manager.h @@ -0,0 +1,86 @@ +/* gdm-smartcard-manager.h - object for monitoring smartcard insertion and + * removal events + * + * Copyright (C) 2006, 2009 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, 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + * + * Written by: Ray Strode + */ +#ifndef GDM_SMARTCARD_MANAGER_H +#define GDM_SMARTCARD_MANAGER_H + +#define GDM_SMARTCARD_ENABLE_INTERNAL_API +#include "gdm-smartcard.h" + +#include +#include + +G_BEGIN_DECLS +#define GDM_TYPE_SMARTCARD_MANAGER (gdm_smartcard_manager_get_type ()) +#define GDM_SMARTCARD_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDM_TYPE_SMARTCARD_MANAGER, GdmSmartcardManager)) +#define GDM_SMARTCARD_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDM_TYPE_SMARTCARD_MANAGER, GdmSmartcardManagerClass)) +#define GDM_IS_SMARTCARD_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SC_TYPE_SMARTCARD_MANAGER)) +#define GDM_IS_SMARTCARD_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SC_TYPE_SMARTCARD_MANAGER)) +#define GDM_SMARTCARD_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GDM_TYPE_SMARTCARD_MANAGER, GdmSmartcardManagerClass)) +#define GDM_SMARTCARD_MANAGER_ERROR (gdm_smartcard_manager_error_quark ()) +typedef struct _GdmSmartcardManager GdmSmartcardManager; +typedef struct _GdmSmartcardManagerClass GdmSmartcardManagerClass; +typedef struct _GdmSmartcardManagerPrivate GdmSmartcardManagerPrivate; +typedef enum _GdmSmartcardManagerError GdmSmartcardManagerError; + +struct _GdmSmartcardManager { + GObject parent; + + /*< private > */ + GdmSmartcardManagerPrivate *priv; +}; + +struct _GdmSmartcardManagerClass { + GObjectClass parent_class; + + /* Signals */ + void (*smartcard_inserted) (GdmSmartcardManager *manager, + GdmSmartcard *token); + void (*smartcard_removed) (GdmSmartcardManager *manager, + GdmSmartcard *token); + void (*error) (GdmSmartcardManager *manager, + GError *error); +}; + +enum _GdmSmartcardManagerError { + GDM_SMARTCARD_MANAGER_ERROR_GENERIC = 0, + GDM_SMARTCARD_MANAGER_ERROR_WITH_NSS, + GDM_SMARTCARD_MANAGER_ERROR_LOADING_DRIVER, + GDM_SMARTCARD_MANAGER_ERROR_WATCHING_FOR_EVENTS, + GDM_SMARTCARD_MANAGER_ERROR_REPORTING_EVENTS +}; + +GType gdm_smartcard_manager_get_type (void) G_GNUC_CONST; +GQuark gdm_smartcard_manager_error_quark (void) G_GNUC_CONST; + +GdmSmartcardManager *gdm_smartcard_manager_new (const char *module); + +gboolean gdm_smartcard_manager_start (GdmSmartcardManager *manager, + GError **error); + +void gdm_smartcard_manager_stop (GdmSmartcardManager *manager); + +char *gdm_smartcard_manager_get_module_path (GdmSmartcardManager *manager); +gboolean gdm_smartcard_manager_login_token_is_inserted (GdmSmartcardManager *manager); + +G_END_DECLS +#endif /* GDM_SMARTCARD_MANAGER_H */ diff --git a/gui/simple-greeter/extensions/smartcard/gdm-smartcard-worker.c b/gui/simple-greeter/extensions/smartcard/gdm-smartcard-worker.c new file mode 100644 index 00000000..578ef37d --- /dev/null +++ b/gui/simple-greeter/extensions/smartcard/gdm-smartcard-worker.c @@ -0,0 +1,186 @@ +#include "config.h" + +#include +#include +#include +#include +#include + +#include + +#include "gdm-smartcard-manager.h" +#include "gdm-smartcard.h" + +#ifndef GDM_SMARTCARDS_CONF +#define GDM_SMARTCARDS_CONF GDMCONFDIR "/smartcards.conf" +#endif + +#ifndef GDM_SMARTCARDS_GROUP +#define GDM_SMARTCARDS_GROUP "Smartcards" +#endif + +#ifndef GDM_SMARTCARDS_KEY_ENABLED +#define GDM_SMARTCARDS_KEY_ENABLED "Enabled" +#endif + +#ifndef GDM_SMARTCARDS_KEY_DRIVER +#define GDM_SMARTCARDS_KEY_DRIVER "Driver" +#endif + +static GMainLoop *event_loop; +static GdmSmartcardManager *manager; +static int signal_pipe_fds[2] = { -1, -1 }; + +static void +on_smartcard_event (const char *event_string) +{ + g_debug ("smartcard event '%s' happened", event_string); + g_print ("%s", event_string); + fflush (stdout); +} + +static void +watch_for_smartcards (void) +{ + GError *error; + char *driver; + GKeyFile *cfg; + + cfg = g_key_file_new (); + + error = NULL; + driver = NULL; + if (g_key_file_load_from_file (cfg, GDM_SMARTCARDS_CONF, G_KEY_FILE_NONE, &error)) { + if (!g_key_file_get_boolean (cfg, GDM_SMARTCARDS_GROUP, GDM_SMARTCARDS_KEY_ENABLED, &error)) { + g_debug ("smartcard support is not enabled"); + goto out; + } + + driver = g_key_file_get_string (cfg, GDM_SMARTCARDS_GROUP, GDM_SMARTCARDS_KEY_DRIVER, NULL); + g_debug ("smartcards driver is set to '%s'", + driver == NULL || driver[0] == '\0'? "" : driver); + } + + g_debug ("watching for smartcard insertion and removal events"); + manager = gdm_smartcard_manager_new (driver); + g_free (driver); + + g_signal_connect_swapped (manager, + "smartcard-inserted", + G_CALLBACK (on_smartcard_event), + "I"); + + g_signal_connect_swapped (manager, + "smartcard-removed", + G_CALLBACK (on_smartcard_event), + "R"); + + error = NULL; + if (!gdm_smartcard_manager_start (manager, &error)) { + g_object_unref (manager); + manager = NULL; + + if (error != NULL) { + g_debug ("%s", error->message); + g_error_free (error); + } else { + g_debug ("could not start smartcard manager"); + + } + goto out; + } +out: + g_key_file_free (cfg); +} + +static void +stop_watching_for_smartcards (void) +{ + if (manager != NULL) { + gdm_smartcard_manager_stop (manager); + g_object_unref (manager); + manager = NULL; + } +} + +static void +on_alrm_signal (int signal_number) +{ + raise (SIGKILL); +} + +static void +on_term_signal (int signal_number) +{ + close (signal_pipe_fds[1]); + signal_pipe_fds[1] = -1; + + /* Give us 10 seconds to clean up orderly. + * If that fails, then the smartcard stack + * is hung up and we need to die hard + */ + alarm (10); + signal (SIGALRM, on_alrm_signal); +} + +static gboolean +after_term_signal (GIOChannel *io_channel, + GIOCondition condition, + gpointer data) +{ + g_main_loop_quit (event_loop); + return FALSE; +} + +static void +on_debug_message (const char *log_domain, + GLogLevelFlags log_level, + const char *message, + gpointer user_data) +{ + g_printerr ("*** DEBUG: %s\n", message); +} + +int +main (int argc, + char **argv) +{ + GIOChannel *io_channel; + + setlocale (LC_ALL, ""); + + g_type_init (); + + g_log_set_handler (NULL, G_LOG_LEVEL_DEBUG, on_debug_message, NULL); + + event_loop = g_main_loop_new (NULL, FALSE); + + watch_for_smartcards (); + + if (pipe (signal_pipe_fds) != 0) { + return 1; + } + fcntl (signal_pipe_fds[0], F_SETFD, FD_CLOEXEC); + fcntl (signal_pipe_fds[1], F_SETFD, FD_CLOEXEC); + + io_channel = g_io_channel_unix_new (signal_pipe_fds[0]); + g_io_channel_set_flags (io_channel, G_IO_FLAG_NONBLOCK, NULL); + g_io_channel_set_encoding (io_channel, NULL, NULL); + g_io_channel_set_buffered (io_channel, FALSE); + g_io_add_watch (io_channel, G_IO_HUP, after_term_signal, NULL); + g_io_channel_set_close_on_unref (io_channel, TRUE); + g_io_channel_unref (io_channel); + + signal (SIGTERM, on_term_signal); + signal (SIGPIPE, on_term_signal); + +#ifdef HAVE_SYS_PRCTL_H + prctl (PR_SET_PDEATHSIG, SIGKILL); +#endif + + g_main_loop_run (event_loop); + + stop_watching_for_smartcards (); + + return 0; +} diff --git a/gui/simple-greeter/extensions/smartcard/gdm-smartcard.c b/gui/simple-greeter/extensions/smartcard/gdm-smartcard.c new file mode 100644 index 00000000..2203a168 --- /dev/null +++ b/gui/simple-greeter/extensions/smartcard/gdm-smartcard.c @@ -0,0 +1,552 @@ +/* gdm-smartcard.c - smartcard object + * + * Copyright (C) 2006 Ray Strode + * + * 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, 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#define GDM_SMARTCARD_ENABLE_INTERNAL_API +#include "gdm-smartcard.h" + +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +struct _GdmSmartcardPrivate { + SECMODModule *module; + GdmSmartcardState state; + + CK_SLOT_ID slot_id; + int slot_series; + + PK11SlotInfo *slot; + char *name; + + CERTCertificate *signing_certificate; + CERTCertificate *encryption_certificate; +}; + +static void gdm_smartcard_finalize (GObject *object); +static void gdm_smartcard_class_install_signals (GdmSmartcardClass *card_class); +static void gdm_smartcard_class_install_properties (GdmSmartcardClass *card_class); +static void gdm_smartcard_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gdm_smartcard_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static void gdm_smartcard_set_name (GdmSmartcard *card, const char *name); +static void gdm_smartcard_set_slot_id (GdmSmartcard *card, + int slot_id); +static void gdm_smartcard_set_slot_series (GdmSmartcard *card, + int slot_series); +static void gdm_smartcard_set_module (GdmSmartcard *card, + SECMODModule *module); + +static PK11SlotInfo *gdm_smartcard_find_slot_from_id (GdmSmartcard *card, + int slot_id); + +static PK11SlotInfo *gdm_smartcard_find_slot_from_card_name (GdmSmartcard *card, + const char *card_name); + +#ifndef GDM_SMARTCARD_DEFAULT_SLOT_ID +#define GDM_SMARTCARD_DEFAULT_SLOT_ID ((gulong) -1) +#endif + +#ifndef GDM_SMARTCARD_DEFAULT_SLOT_SERIES +#define GDM_SMARTCARD_DEFAULT_SLOT_SERIES -1 +#endif + +enum { + PROP_0 = 0, + PROP_NAME, + PROP_SLOT_ID, + PROP_SLOT_SERIES, + PROP_MODULE, + NUMBER_OF_PROPERTIES +}; + +enum { + INSERTED, + REMOVED, + NUMBER_OF_SIGNALS +}; + +static guint gdm_smartcard_signals[NUMBER_OF_SIGNALS]; + +G_DEFINE_TYPE (GdmSmartcard, gdm_smartcard, G_TYPE_OBJECT); + +static void +gdm_smartcard_class_init (GdmSmartcardClass *card_class) +{ + GObjectClass *gobject_class; + + gobject_class = G_OBJECT_CLASS (card_class); + + gobject_class->finalize = gdm_smartcard_finalize; + + gdm_smartcard_class_install_signals (card_class); + gdm_smartcard_class_install_properties (card_class); + + g_type_class_add_private (card_class, + sizeof (GdmSmartcardPrivate)); +} + +static void +gdm_smartcard_class_install_signals (GdmSmartcardClass *card_class) +{ + GObjectClass *object_class; + + object_class = G_OBJECT_CLASS (card_class); + + gdm_smartcard_signals[INSERTED] = + g_signal_new ("inserted", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GdmSmartcardClass, + inserted), + NULL, NULL, g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + + gdm_smartcard_signals[REMOVED] = + g_signal_new ("removed", + G_OBJECT_CLASS_TYPE (object_class), + G_SIGNAL_RUN_LAST, + G_STRUCT_OFFSET (GdmSmartcardClass, + removed), + NULL, NULL, g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); +} + +static void +gdm_smartcard_class_install_properties (GdmSmartcardClass *card_class) +{ + GObjectClass *object_class; + GParamSpec *param_spec; + + object_class = G_OBJECT_CLASS (card_class); + object_class->set_property = gdm_smartcard_set_property; + object_class->get_property = gdm_smartcard_get_property; + + param_spec = g_param_spec_ulong ("slot-id", _("Slot ID"), + _("The slot the card is in"), + 1, G_MAXULONG, + GDM_SMARTCARD_DEFAULT_SLOT_ID, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (object_class, PROP_SLOT_ID, param_spec); + + param_spec = g_param_spec_int ("slot-series", _("Slot Series"), + _("per-slot card identifier"), + -1, G_MAXINT, + GDM_SMARTCARD_DEFAULT_SLOT_SERIES, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (object_class, PROP_SLOT_SERIES, param_spec); + + param_spec = g_param_spec_string ("name", _("name"), + _("name"), NULL, + G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (object_class, PROP_NAME, param_spec); + + param_spec = g_param_spec_pointer ("module", _("Module"), + _("smartcard driver"), + G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY); + g_object_class_install_property (object_class, PROP_MODULE, param_spec); +} + +static void +gdm_smartcard_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GdmSmartcard *card = GDM_SMARTCARD (object); + + switch (prop_id) { + case PROP_NAME: + gdm_smartcard_set_name (card, g_value_get_string (value)); + break; + + case PROP_SLOT_ID: + gdm_smartcard_set_slot_id (card, + g_value_get_ulong (value)); + break; + + case PROP_SLOT_SERIES: + gdm_smartcard_set_slot_series (card, + g_value_get_int (value)); + break; + + case PROP_MODULE: + gdm_smartcard_set_module (card, + (SECMODModule *) + g_value_get_pointer (value)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +CK_SLOT_ID +gdm_smartcard_get_slot_id (GdmSmartcard *card) +{ + return card->priv->slot_id; +} + +GdmSmartcardState +gdm_smartcard_get_state (GdmSmartcard *card) +{ + return card->priv->state; +} + +char * +gdm_smartcard_get_name (GdmSmartcard *card) +{ + return g_strdup (card->priv->name); +} + +gboolean +gdm_smartcard_is_login_card (GdmSmartcard *card) +{ + const char *login_card_name; + login_card_name = g_getenv ("PKCS11_LOGIN_TOKEN_NAME"); + + if ((login_card_name == NULL) || (card->priv->name == NULL)) { + return FALSE; + } + + if (strcmp (card->priv->name, login_card_name) == 0) { + return TRUE; + } + + return FALSE; +} + +static void +gdm_smartcard_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GdmSmartcard *card = GDM_SMARTCARD (object); + + switch (prop_id) { + case PROP_NAME: + g_value_take_string (value, + gdm_smartcard_get_name (card)); + break; + + case PROP_SLOT_ID: + g_value_set_ulong (value, + (gulong) gdm_smartcard_get_slot_id (card)); + break; + + case PROP_SLOT_SERIES: + g_value_set_int (value, + gdm_smartcard_get_slot_series (card)); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +gdm_smartcard_set_name (GdmSmartcard *card, + const char *name) +{ + if (name == NULL) { + return; + } + + if ((card->priv->name == NULL) || + (strcmp (card->priv->name, name) != 0)) { + g_free (card->priv->name); + card->priv->name = g_strdup (name); + + if (card->priv->slot == NULL) { + card->priv->slot = gdm_smartcard_find_slot_from_card_name (card, + card->priv->name); + + if (card->priv->slot != NULL) { + int slot_id, slot_series; + + slot_id = PK11_GetSlotID (card->priv->slot); + if (slot_id != card->priv->slot_id) { + gdm_smartcard_set_slot_id (card, slot_id); + } + + slot_series = PK11_GetSlotSeries (card->priv->slot); + if (slot_series != card->priv->slot_series) { + gdm_smartcard_set_slot_series (card, slot_series); + } + + _gdm_smartcard_set_state (card, GDM_SMARTCARD_STATE_INSERTED); + } else { + _gdm_smartcard_set_state (card, GDM_SMARTCARD_STATE_REMOVED); + } + } + + g_object_notify (G_OBJECT (card), "name"); + } +} + +static void +gdm_smartcard_set_slot_id (GdmSmartcard *card, + int slot_id) +{ + if (card->priv->slot_id != slot_id) { + card->priv->slot_id = slot_id; + + if (card->priv->slot == NULL) { + card->priv->slot = gdm_smartcard_find_slot_from_id (card, + card->priv->slot_id); + + if (card->priv->slot != NULL) { + const char *card_name; + + card_name = PK11_GetTokenName (card->priv->slot); + if ((card->priv->name == NULL) || + ((card_name != NULL) && + (strcmp (card_name, card->priv->name) != 0))) { + gdm_smartcard_set_name (card, card_name); + } + + _gdm_smartcard_set_state (card, GDM_SMARTCARD_STATE_INSERTED); + } else { + _gdm_smartcard_set_state (card, GDM_SMARTCARD_STATE_REMOVED); + } + } + + g_object_notify (G_OBJECT (card), "slot-id"); + } +} + +static void +gdm_smartcard_set_slot_series (GdmSmartcard *card, + int slot_series) +{ + if (card->priv->slot_series != slot_series) { + card->priv->slot_series = slot_series; + g_object_notify (G_OBJECT (card), "slot-series"); + } +} + +static void +gdm_smartcard_set_module (GdmSmartcard *card, + SECMODModule *module) +{ + gboolean should_notify; + + if (card->priv->module != module) { + should_notify = TRUE; + } else { + should_notify = FALSE; + } + + if (card->priv->module != NULL) { + SECMOD_DestroyModule (card->priv->module); + card->priv->module = NULL; + } + + if (module != NULL) { + card->priv->module = SECMOD_ReferenceModule (module); + } + + if (should_notify) { + g_object_notify (G_OBJECT (card), "module"); + } +} + +int +gdm_smartcard_get_slot_series (GdmSmartcard *card) +{ + return card->priv->slot_series; +} + +static void +gdm_smartcard_init (GdmSmartcard *card) +{ + + g_debug ("initializing smartcard "); + + card->priv = G_TYPE_INSTANCE_GET_PRIVATE (card, + GDM_TYPE_SMARTCARD, + GdmSmartcardPrivate); +} + +static void gdm_smartcard_finalize (GObject *object) +{ + GdmSmartcard *card; + GObjectClass *gobject_class; + + card = GDM_SMARTCARD (object); + + g_free (card->priv->name); + + gdm_smartcard_set_module (card, NULL); + + gobject_class = G_OBJECT_CLASS (gdm_smartcard_parent_class); + + gobject_class->finalize (object); +} + +GQuark gdm_smartcard_error_quark (void) +{ + static GQuark error_quark = 0; + + if (error_quark == 0) { + error_quark = g_quark_from_static_string ("gdm-smartcard-error-quark"); + } + + return error_quark; +} + +GdmSmartcard * +_gdm_smartcard_new (SECMODModule *module, + CK_SLOT_ID slot_id, + int slot_series) +{ + GdmSmartcard *card; + + g_return_val_if_fail (module != NULL, NULL); + g_return_val_if_fail (slot_id >= 1, NULL); + g_return_val_if_fail (slot_series > 0, NULL); + g_return_val_if_fail (sizeof (gulong) == sizeof (slot_id), NULL); + + card = GDM_SMARTCARD (g_object_new (GDM_TYPE_SMARTCARD, + "module", module, + "slot-id", (gulong) slot_id, + "slot-series", slot_series, + NULL)); + return card; +} + +GdmSmartcard * +_gdm_smartcard_new_from_name (SECMODModule *module, + const char *name) +{ + GdmSmartcard *card; + + g_return_val_if_fail (module != NULL, NULL); + g_return_val_if_fail (name != NULL, NULL); + + card = GDM_SMARTCARD (g_object_new (GDM_TYPE_SMARTCARD, + "module", module, + "name", name, + NULL)); + return card; +} + +void +_gdm_smartcard_set_state (GdmSmartcard *card, + GdmSmartcardState state) +{ + if (card->priv->state != state) { + card->priv->state = state; + + if (state == GDM_SMARTCARD_STATE_INSERTED) { + g_signal_emit (card, gdm_smartcard_signals[INSERTED], 0); + } else if (state == GDM_SMARTCARD_STATE_REMOVED) { + g_signal_emit (card, gdm_smartcard_signals[REMOVED], 0); + } else { + g_assert_not_reached (); + } + } +} + +/* So we could conceivably make the closure data a pointer to the card + * or something similiar and then emit signals when we want passwords, + * but it's probably easier to just get the password up front and use + * it. So we just take the passed in g_malloc'd (well probably, who knows) + * and strdup it using NSPR's memory allocation routines. + */ +static char * +gdm_smartcard_password_handler (PK11SlotInfo *slot, + PRBool is_retrying, + const char *password) +{ + if (is_retrying) { + return NULL; + } + + return password != NULL? PL_strdup (password): NULL; +} + +gboolean +gdm_smartcard_unlock (GdmSmartcard *card, + const char *password) +{ + SECStatus status; + + PK11_SetPasswordFunc ((PK11PasswordFunc) gdm_smartcard_password_handler); + + /* we pass PR_TRUE to load certificates + */ + status = PK11_Authenticate (card->priv->slot, PR_TRUE, (gpointer) password); + + if (status != SECSuccess) { + g_debug ("could not unlock card - %d", status); + return FALSE; + } + return TRUE; +} + +static PK11SlotInfo * +gdm_smartcard_find_slot_from_card_name (GdmSmartcard *card, + const char *card_name) +{ + int i; + + for (i = 0; i < card->priv->module->slotCount; i++) { + const char *slot_card_name; + + slot_card_name = PK11_GetTokenName (card->priv->module->slots[i]); + + if ((slot_card_name != NULL) && + (strcmp (slot_card_name, card_name) == 0)) { + return card->priv->module->slots[i]; + } + } + + return NULL; +} + +static PK11SlotInfo * +gdm_smartcard_find_slot_from_id (GdmSmartcard *card, + int slot_id) +{ + int i; + + for (i = 0; i < card->priv->module->slotCount; i++) { + if (PK11_GetSlotID (card->priv->module->slots[i]) == slot_id) { + return card->priv->module->slots[i]; + } + } + + return NULL; +} diff --git a/gui/simple-greeter/extensions/smartcard/gdm-smartcard.h b/gui/simple-greeter/extensions/smartcard/gdm-smartcard.h new file mode 100644 index 00000000..20303bdc --- /dev/null +++ b/gui/simple-greeter/extensions/smartcard/gdm-smartcard.h @@ -0,0 +1,94 @@ +/* securitycard.h - api for reading and writing data to a security card + * + * Copyright (C) 2006 Ray Strode + * + * 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, 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, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA + * 02111-1307, USA. + */ +#ifndef GDM_SMARTCARD_H +#define GDM_SMARTCARD_H + +#include +#include + +#include + +G_BEGIN_DECLS +#define GDM_TYPE_SMARTCARD (gdm_smartcard_get_type ()) +#define GDM_SMARTCARD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDM_TYPE_SMARTCARD, GdmSmartcard)) +#define GDM_SMARTCARD_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDM_TYPE_SMARTCARD, GdmSmartcardClass)) +#define GDM_IS_SMARTCARD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDM_TYPE_SMARTCARD)) +#define GDM_IS_SMARTCARD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDM_TYPE_SMARTCARD)) +#define GDM_SMARTCARD_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), GDM_TYPE_SMARTCARD, GdmSmartcardClass)) +#define GDM_SMARTCARD_ERROR (gdm_smartcard_error_quark ()) +typedef struct _GdmSmartcardClass GdmSmartcardClass; +typedef struct _GdmSmartcard GdmSmartcard; +typedef struct _GdmSmartcardPrivate GdmSmartcardPrivate; +typedef enum _GdmSmartcardError GdmSmartcardError; +typedef enum _GdmSmartcardState GdmSmartcardState; + +typedef struct _GdmSmartcardRequest GdmSmartcardRequest; + +struct _GdmSmartcard { + GObject parent; + + /*< private > */ + GdmSmartcardPrivate *priv; +}; + +struct _GdmSmartcardClass { + GObjectClass parent_class; + + void (* inserted) (GdmSmartcard *card); + void (* removed) (GdmSmartcard *card); +}; + +enum _GdmSmartcardError { + GDM_SMARTCARD_ERROR_GENERIC = 0, +}; + +enum _GdmSmartcardState { + GDM_SMARTCARD_STATE_INSERTED = 0, + GDM_SMARTCARD_STATE_REMOVED, +}; + +GType gdm_smartcard_get_type (void) G_GNUC_CONST; +GQuark gdm_smartcard_error_quark (void) G_GNUC_CONST; + +CK_SLOT_ID gdm_smartcard_get_slot_id (GdmSmartcard *card); +gint gdm_smartcard_get_slot_series (GdmSmartcard *card); +GdmSmartcardState gdm_smartcard_get_state (GdmSmartcard *card); + +char *gdm_smartcard_get_name (GdmSmartcard *card); +gboolean gdm_smartcard_is_login_card (GdmSmartcard *card); + +gboolean gdm_smartcard_unlock (GdmSmartcard *card, + const char *password); + +/* don't under any circumstances call these functions */ +#ifdef GDM_SMARTCARD_ENABLE_INTERNAL_API + +GdmSmartcard *_gdm_smartcard_new (SECMODModule *module, + CK_SLOT_ID slot_id, + gint slot_series); +GdmSmartcard *_gdm_smartcard_new_from_name (SECMODModule *module, + const char *name); + +void _gdm_smartcard_set_state (GdmSmartcard *card, + GdmSmartcardState state); +#endif + +G_END_DECLS +#endif /* GDM_SMARTCARD_H */ diff --git a/gui/simple-greeter/extensions/smartcard/gdm-smartcard.pam b/gui/simple-greeter/extensions/smartcard/gdm-smartcard.pam new file mode 100644 index 00000000..d5ac1fab --- /dev/null +++ b/gui/simple-greeter/extensions/smartcard/gdm-smartcard.pam @@ -0,0 +1,18 @@ +# Sample PAM file for doing smartcard authentication. +# Distros should replace this with what makes sense for them. +auth required pam_env.so +auth [success=done ignore=ignore default=die] pam_pkcs11.so wait_for_card card_only +auth requisite pam_succeed_if.so uid >= 500 quiet +auth required pam_deny.so + +account required pam_unix.so +account sufficient pam_localuser.so +account sufficient pam_succeed_if.so uid < 500 quiet +account required pam_permit.so + +password optional pam_pkcs11.so +password requisite pam_cracklib.so try_first_pass retry=3 type= + +session optional pam_keyinit.so revoke +session required pam_limits.so +session required pam_unix.so diff --git a/gui/simple-greeter/extensions/smartcard/icons/16x16/Makefile.am b/gui/simple-greeter/extensions/smartcard/icons/16x16/Makefile.am new file mode 100644 index 00000000..661d6879 --- /dev/null +++ b/gui/simple-greeter/extensions/smartcard/icons/16x16/Makefile.am @@ -0,0 +1,5 @@ +iconsdir = $(datadir)/icons/hicolor/16x16/apps + +icons_DATA = gdm-smartcard.png + +EXTRA_DIST = $(icons_DATA) diff --git a/gui/simple-greeter/extensions/smartcard/icons/16x16/gdm-smartcard.png b/gui/simple-greeter/extensions/smartcard/icons/16x16/gdm-smartcard.png new file mode 100644 index 00000000..0112af1b Binary files /dev/null and b/gui/simple-greeter/extensions/smartcard/icons/16x16/gdm-smartcard.png differ diff --git a/gui/simple-greeter/extensions/smartcard/icons/48x48/Makefile.am b/gui/simple-greeter/extensions/smartcard/icons/48x48/Makefile.am new file mode 100644 index 00000000..e79d85bf --- /dev/null +++ b/gui/simple-greeter/extensions/smartcard/icons/48x48/Makefile.am @@ -0,0 +1,5 @@ +iconsdir = $(datadir)/icons/hicolor/48x48/apps + +icons_DATA = gdm-smartcard.png + +EXTRA_DIST = $(icons_DATA) diff --git a/gui/simple-greeter/extensions/smartcard/icons/48x48/gdm-smartcard.png b/gui/simple-greeter/extensions/smartcard/icons/48x48/gdm-smartcard.png new file mode 100644 index 00000000..35d5578d Binary files /dev/null and b/gui/simple-greeter/extensions/smartcard/icons/48x48/gdm-smartcard.png differ diff --git a/gui/simple-greeter/extensions/smartcard/icons/Makefile.am b/gui/simple-greeter/extensions/smartcard/icons/Makefile.am new file mode 100644 index 00000000..c20f10d0 --- /dev/null +++ b/gui/simple-greeter/extensions/smartcard/icons/Makefile.am @@ -0,0 +1 @@ +SUBDIRS = 16x16 48x48 diff --git a/gui/simple-greeter/extensions/smartcard/org.gnome.display-manager.extensions.smartcard.gschema.xml.in b/gui/simple-greeter/extensions/smartcard/org.gnome.display-manager.extensions.smartcard.gschema.xml.in new file mode 100644 index 00000000..3c90bb40 --- /dev/null +++ b/gui/simple-greeter/extensions/smartcard/org.gnome.display-manager.extensions.smartcard.gschema.xml.in @@ -0,0 +1,9 @@ + + + + true + <_summary>Activation of this plugin + <_description>Whether this plugin would be activated or not + + + diff --git a/gui/simple-greeter/extensions/smartcard/page.ui b/gui/simple-greeter/extensions/smartcard/page.ui new file mode 100644 index 00000000..8fa5c7be --- /dev/null +++ b/gui/simple-greeter/extensions/smartcard/page.ui @@ -0,0 +1,57 @@ + + + + + True + vertical + + + True + 6 + + + True + + + False + False + 0 + + + + + True + True + True + + + 1 + + + + + True + True + 0 + + + + + True + + + True + + + 0 + + + + + True + True + 1 + + + + diff --git a/po/POTFILES.in b/po/POTFILES.in index daa9bb85..7784dd3f 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -83,6 +83,9 @@ gui/simple-greeter/gdm-user-chooser-widget.c gui/simple-greeter/greeter-main.c gui/simple-greeter/extensions/password/gdm-password-extension.c gui/simple-greeter/extensions/fingerprint/gdm-fingerprint-extension.c +gui/simple-greeter/extensions/smartcard/gdm-smartcard-extension.c +gui/simple-greeter/extensions/smartcard/gdm-smartcard-manager.c +gui/simple-greeter/extensions/smartcard/gdm-smartcard.c utils/gdmflexiserver.c utils/gdm-screenshot.c -- cgit v1.2.1