summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRay Strode <rstrode@redhat.com>2009-02-06 16:25:47 -0500
committerRay Strode <rstrode@redhat.com>2011-06-13 20:33:47 -0400
commitd96dbdfa9b4d25e3c9b706c2721fb02d92ab9098 (patch)
treee5ffb98a745b77d1b6bf519ebd09d977ea0439e5
parent123ea3394c6566843a64df54fe6215b37a20271d (diff)
downloadgdm-d96dbdfa9b4d25e3c9b706c2721fb02d92ab9098.tar.gz
greeter: Add smartcard plugin
This commit adds a plugin to initiate a conversation when smartcards are inserted.
-rw-r--r--configure.ac11
-rw-r--r--gui/simple-greeter/extensions/Makefile.am2
-rw-r--r--gui/simple-greeter/extensions/smartcard/Makefile.am79
-rw-r--r--gui/simple-greeter/extensions/smartcard/gdm-smartcard18
-rw-r--r--gui/simple-greeter/extensions/smartcard/gdm-smartcard-extension.c606
-rw-r--r--gui/simple-greeter/extensions/smartcard/gdm-smartcard-extension.h56
-rw-r--r--gui/simple-greeter/extensions/smartcard/gdm-smartcard-manager.c1485
-rw-r--r--gui/simple-greeter/extensions/smartcard/gdm-smartcard-manager.h86
-rw-r--r--gui/simple-greeter/extensions/smartcard/gdm-smartcard-worker.c186
-rw-r--r--gui/simple-greeter/extensions/smartcard/gdm-smartcard.c552
-rw-r--r--gui/simple-greeter/extensions/smartcard/gdm-smartcard.h94
-rw-r--r--gui/simple-greeter/extensions/smartcard/gdm-smartcard.pam18
-rw-r--r--gui/simple-greeter/extensions/smartcard/icons/16x16/Makefile.am5
-rw-r--r--gui/simple-greeter/extensions/smartcard/icons/16x16/gdm-smartcard.pngbin0 -> 871 bytes
-rw-r--r--gui/simple-greeter/extensions/smartcard/icons/48x48/Makefile.am5
-rw-r--r--gui/simple-greeter/extensions/smartcard/icons/48x48/gdm-smartcard.pngbin0 -> 4202 bytes
-rw-r--r--gui/simple-greeter/extensions/smartcard/icons/Makefile.am1
-rw-r--r--gui/simple-greeter/extensions/smartcard/org.gnome.display-manager.extensions.smartcard.gschema.xml.in9
-rw-r--r--gui/simple-greeter/extensions/smartcard/page.ui57
-rw-r--r--po/POTFILES.in3
20 files changed, 3272 insertions, 1 deletions
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 <rstrode@redhat.com>
+ *
+ */
+
+#include <config.h>
+#include "gdm-smartcard-extension.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <glib/gi18n-lib.h>
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+#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 <rstrode@redhat.com>
+ */
+
+#ifndef __GDM_SMARTCARD_EXTENSION_H
+#define __GDM_SMARTCARD_EXTENSION_H
+
+#include <glib-object.h>
+#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 <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <prerror.h>
+#include <prinit.h>
+#include <nss.h>
+#include <pk11func.h>
+#include <secmod.h>
+#include <secerr.h>
+
+#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 <glib.h>
+
+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 <glib.h>
+#include <glib-object.h>
+
+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 <fcntl.h>
+#include <locale.h>
+#include <sys/prctl.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#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'? "<automatic>" : 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 <rstrode@redhat.com>
+ *
+ * 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 <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <cert.h>
+#include <nss.h>
+#include <pk11func.h>
+#include <prerror.h>
+#include <secmod.h>
+#include <secerr.h>
+
+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 <glib.h>
+#include <glib-object.h>
+
+#include <secmod.h>
+
+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
--- /dev/null
+++ b/gui/simple-greeter/extensions/smartcard/icons/16x16/gdm-smartcard.png
Binary files 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
--- /dev/null
+++ b/gui/simple-greeter/extensions/smartcard/icons/48x48/gdm-smartcard.png
Binary files 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 @@
+<schemalist>
+ <schema id="org.gnome.display-manager.extensions.smartcard" path="/org/gnome/display-manager/extensions/smartcard/">
+ <key name="active" type="b">
+ <default>true</default>
+ <_summary>Activation of this plugin</_summary>
+ <_description>Whether this plugin would be activated or not</_description>
+ </key>
+ </schema>
+</schemalist>
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 @@
+<?xml version="1.0"?>
+<interface>
+ <requires lib="gtk+" version="2.14"/>
+ <object class="GtkVBox" id="page">
+ <property name="visible">True</property>
+ <property name="orientation">vertical</property>
+ <child>
+ <object class="GtkHBox" id="auth-input-box">
+ <property name="visible">True</property>
+ <property name="spacing">6</property>
+ <child>
+ <object class="GtkLabel" id="auth-prompt-label">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">False</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkEntry" id="auth-prompt-entry">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="activates_default">True</property>
+ </object>
+ <packing>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkHBox" id="auth-message-box">
+ <property name="visible">True</property>
+ <child>
+ <object class="GtkLabel" id="auth-message-label">
+ <property name="visible">True</property>
+ </object>
+ <packing>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">True</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+</interface>
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