Blob Blame History Raw
--- gdm-2.19.1/configure.ac.security-tokens	2007-05-21 16:04:51.000000000 -0400
+++ gdm-2.19.1/configure.ac	2007-05-21 16:04:51.000000000 -0400
@@ -20,6 +20,7 @@ LIBRSVG_REQUIRED=1.1.1
 LIBXML_REQUIRED=2.4.12
 LIBART_REQUIRED=2.3.11
 SCROLLKEEPER_REQUIRED=0.1.4
+NSS_REQUIRED=3.11.1
 
 dnl
 dnl Let the user configure where to look for the configuration files.
@@ -176,7 +177,7 @@ PKG_CHECK_MODULES(COMMON, gtk+-2.0 >= $G
 AC_SUBST(COMMON_CFLAGS)
 AC_SUBST(COMMON_LIBS)
 
-PKG_CHECK_MODULES(DAEMON, gtk+-2.0 >= $GTK_REQUIRED)
+PKG_CHECK_MODULES(DAEMON, gtk+-2.0 >= $GTK_REQUIRED nss >= $NSS_REQUIRED)
 AC_SUBST(DAEMON_CFLAGS)
 AC_SUBST(DAEMON_LIBS)
 
--- /dev/null	2007-05-21 09:34:56.803421964 -0400
+++ gdm-2.19.1/config/securitytokens.conf.in	2007-05-21 16:04:51.000000000 -0400
@@ -0,0 +1,3 @@
+[SecurityTokens]
+Enabled=true
+#Driver=@libdir@/pkcs11/libcoolkeypk11.so
--- gdm-2.19.1/config/Makefile.am.security-tokens	2007-05-13 22:08:25.000000000 -0400
+++ gdm-2.19.1/config/Makefile.am	2007-05-21 16:04:51.000000000 -0400
@@ -34,9 +34,11 @@ EXTRA_DIST = \
 	XKeepsCrashing \
 	gettextfoo.h \
 	gdmprefetchlist.in \
+ 	securitytokens.conf.in \
 	extract-shell.sh
 
-CLEANFILES = Xsession gdm.conf gdm.conf-custom default.desktop gnome.desktop CDE.desktop ssh.desktop Init PreSession PostSession gdmprefetchlist
+CLEANFILES = Xsession gdm.conf gdm.conf-custom default.desktop gnome.desktop CDE.desktop ssh.desktop Init PreSession PostSession gdmprefetchlist securitytokens.conf
+
 
 Xsession: $(srcdir)/Xsession.in
 	sed	-e 's,[@]XSESSION_SHELL[@],$(XSESSION_SHELL),g' \
@@ -75,6 +77,31 @@ gdm.conf-custom: $(srcdir)/gdm.conf-cust
 	sed	-e 's,[@]GDM_DEFAULTS_CONF[@],$(GDM_DEFAULTS_CONF),g' \
 		<$(srcdir)/gdm.conf-custom.in >gdm.conf-custom
 
+securitytokens.conf: $(srcdir)/securitytokens.conf.in
+	sed	-e 's,[@]GDMPREFETCHCMD[@],$(GDMPREFETCHCMD),g' \
+		-e 's,[@]GDM_USER_PATH[@],$(GDM_USER_PATH),g' \
+		-e 's,[@]HALT_COMMAND[@],$(HALT_COMMAND),g' \
+		-e 's,[@]REBOOT_COMMAND[@],$(REBOOT_COMMAND),g' \
+		-e 's,[@]SOUND_PROGRAM[@],$(SOUND_PROGRAM),g' \
+		-e 's,[@]SUSPEND_COMMAND[@],$(SUSPEND_COMMAND),g' \
+		-e 's,[@]XEVIE_OPTION[@],$(XEVIE_OPTION),g' \
+		-e 's,[@]X_CONFIG_OPTIONS[@],$(X_CONFIG_OPTIONS),g' \
+		-e 's,[@]X_SERVER[@],$(X_SERVER),g' \
+		-e 's,[@]X_XNEST_CONFIG_OPTIONS[@],$(X_XNEST_CONFIG_OPTIONS),g' \
+		-e 's,[@]X_XNEST_PATH[@],$(X_XNEST_PATH),g' \
+		-e 's,[@]authdir[@],$(authdir),g' \
+		-e 's,[@]datadir[@],$(datadir),g' \
+		-e 's,[@]dmconfdir[@],$(dmconfdir),g' \
+		-e 's,[@]gdmconfdir[@],$(gdmconfdir),g' \
+		-e 's,[@]libdir[@],$(libdir),g' \
+		-e 's,[@]libexecdir[@],$(libexecdir),g' \
+		-e 's,[@]localedir[@],$(libexecdir),g' \
+		-e 's,[@]logdir[@],$(logdir),g' \
+		-e 's,[@]pixmapdir[@],$(pixmapdir),g' \
+		-e 's,[@]sbindir[@],$(sbindir),g' \
+		<$(srcdir)/securitytokens.conf.in >securitytokens.conf
+
+
 gettextfoo.h: XKeepsCrashing Xsession.in
 	cat $^ | $(srcdir)/extract-shell.sh > gettextfoo.h
 
@@ -103,7 +130,7 @@ uninstall-hook:
 	$(DESTDIR)$(predir)/Default \
 	$(DESTDIR)$(postdir)/Default
 
-install-data-hook: gdm.conf gdm.conf-custom Xsession Init PostSession PreSession $(DESKTOP_FILES) $(GDMPREFETCHLIST)
+install-data-hook: gdm.conf gdm.conf-custom Xsession Init PostSession PreSession $(DESKTOP_FILES) $(GDMPREFETCHLIST) securitytokens.conf
 	if test '!' -d $(DESTDIR)$(confdir); then \
 		$(mkinstalldirs) $(DESTDIR)$(confdir); \
 		chmod 755 $(DESTDIR)$(confdir); \
@@ -136,6 +163,7 @@ install-data-hook: gdm.conf gdm.conf-cus
 		chmod 644 $(DESTDIR)$(GDM_CUSTOM_CONF); \
 	fi
 	$(INSTALL_DATA) gdm.conf `dirname $(DESTDIR)$(GDM_DEFAULTS_CONF)`/factory-`basename $(DESTDIR)$(GDM_DEFAULTS_CONF)`
+	$(INSTALL_DATA) securitytokens.conf $(DESTDIR)$(confdir)/securitytokens.conf
 
 	$(INSTALL_SCRIPT) $(srcdir)/XKeepsCrashing $(DESTDIR)$(confdir)/XKeepsCrashing
 	$(INSTALL_SCRIPT) Xsession $(DESTDIR)$(confdir)/Xsession
--- gdm-2.19.1/config/gdm.conf.in.security-tokens	2007-05-21 16:04:51.000000000 -0400
+++ gdm-2.19.1/config/gdm.conf.in	2007-05-21 16:04:51.000000000 -0400
@@ -239,6 +239,10 @@ AlwaysLoginCurrentSession=true
 # kills it.  10 seconds should be long enough for X, but Xgl may need 20 or 25. 
 GdmXserverTimeout=10
 
+# Whether or not to listen for smart card insertion/removal events
+SecurityTokensEnable=true
+SecurityTokensDriver=
+
 [security]
 # Allow root to login.  It makes sense to turn this off for kiosk use, when
 # you want to minimize the possibility of break in.
--- gdm-2.19.1/daemon/gdm.c.security-tokens	2007-05-21 16:04:51.000000000 -0400
+++ gdm-2.19.1/daemon/gdm.c	2007-05-21 16:07:29.000000000 -0400
@@ -42,6 +42,7 @@
 #include <errno.h>
 #include <locale.h>
 #include <dirent.h>
+#include <syslog.h>
 
 #ifdef HAVE_CHKAUTHATTR
 #include <auth_attr.h>
@@ -71,6 +72,8 @@
 #include "cookie.h"
 #include "filecheck.h"
 #include "errorgui.h"
+#include "securitytokenmonitor.h"
+#include "securitytoken.h"
 
 #include "gdm-socket-protocol.h"
 #include "gdm-daemon-config.h"
@@ -93,6 +96,10 @@ static void gdm_handle_message (GdmConne
 static void gdm_handle_user_message (GdmConnection *conn,
 				     const gchar *msg,
 				     gpointer data);
+
+static void gdm_reset_local_displays (void);
+static void gdm_watch_for_security_tokens (void);
+
 static void gdm_daemonify (void);
 static void gdm_safe_restart (void);
 static void gdm_try_logout_action (GdmDisplay *disp);
@@ -1787,6 +1794,8 @@ main (int argc, char *argv[])
 		gdm_xdmcp_run ();
 	}
 
+	gdm_watch_for_security_tokens ();
+
 	/* We always exit via exit (), and sadly we need to g_main_quit ()
 	 * at times not knowing if it's this main or a recursive one we're
 	 * quitting.
@@ -4342,3 +4351,91 @@ gdm_handle_user_message (GdmConnection *
 		gdm_connection_close (conn);
 	}
 }
+
+static void
+gdm_reset_local_displays (void)
+{
+    GSList *li;
+    GSList *displays;
+
+    displays = gdm_daemon_config_get_display_list ();
+
+    for (li = displays; li != NULL; li = li->next) {
+          GdmDisplay *d = li->data;
+
+          if (d->attached)
+                  send_slave_command (d, GDM_NOTIFY_RESET);
+    }
+}
+
+#ifndef GDM_SECURITY_TOKENS_CONF
+#define GDM_SECURITY_TOKENS_CONF GDMCONFDIR "/securitytokens.conf"
+#endif
+
+#ifndef GDM_SECURITY_TOKENS_GROUP 
+#define GDM_SECURITY_TOKENS_GROUP "SecurityTokens"
+#endif
+
+#ifndef GDM_SECURITY_TOKENS_KEY_ENABLED
+#define GDM_SECURITY_TOKENS_KEY_ENABLED "Enabled"
+#endif
+
+#ifndef GDM_SECURITY_TOKENS_KEY_DRIVER
+#define GDM_SECURITY_TOKENS_KEY_DRIVER "Driver"
+#endif
+
+static void
+gdm_watch_for_security_tokens (void)
+{
+    GError *error;
+    ScSecurityTokenMonitor *monitor;
+    gchar *driver;
+    GKeyFile *cfg;
+
+    cfg = g_key_file_new ();
+    if (!g_key_file_load_from_file (cfg, GDM_SECURITY_TOKENS_CONF, 0, NULL)) {
+        goto out;
+    }
+
+    if (g_key_file_has_key (cfg, GDM_SECURITY_TOKENS_GROUP, GDM_SECURITY_TOKENS_KEY_ENABLED, NULL) && !g_key_file_get_boolean (cfg, GDM_SECURITY_TOKENS_GROUP, GDM_SECURITY_TOKENS_KEY_ENABLED, NULL)) {
+
+          gdm_debug ("security token support is not enabled");
+          goto out;
+    }
+
+    gdm_debug ("watching for security token insertion and removal events");
+
+    driver = g_key_file_get_string (cfg, GDM_SECURITY_TOKENS_GROUP, GDM_SECURITY_TOKENS_KEY_DRIVER, NULL);
+    gdm_debug ("security tokens driver is set to '%s'", 
+             ve_string_empty (driver)? "<automatic>" : driver);
+    monitor = sc_security_token_monitor_new (driver);
+    g_free (driver);
+    
+    g_signal_connect (monitor,
+                    "security-token-inserted",
+                    G_CALLBACK (gdm_reset_local_displays), 
+                    NULL);
+
+    g_signal_connect (monitor,
+                    "security-token-removed",
+                    G_CALLBACK (gdm_reset_local_displays), 
+                    NULL);
+
+    error = NULL;
+    if (!sc_security_token_monitor_start (monitor, &error)) {
+          g_object_unref (monitor);
+          monitor = NULL;
+
+          if (error != NULL) {
+                  syslog (LOG_ERR, "%s", error->message);
+                  g_error_free (error);
+          } else {
+                  syslog (LOG_ERR, "could not start security token monitor");
+
+          }
+          goto out;
+    }
+out:
+    g_key_file_free (cfg);
+}
+
--- /dev/null	2007-05-21 09:34:56.803421964 -0400
+++ gdm-2.19.1/daemon/securitytokenmonitor.h	2007-05-21 16:04:51.000000000 -0400
@@ -0,0 +1,84 @@
+/* securitytokenmonitor.h - monitor for security token insertion and
+ *                          removal events
+ *
+ * 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 SC_SECURITY_TOKEN_MONITOR_H
+#define SC_SECURITY_TOKEN_MONITOR_H
+
+#define SC_SECURITY_TOKEN_ENABLE_INTERNAL_API
+#include "securitytoken.h"
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+#define SC_TYPE_SECURITY_TOKEN_MONITOR            (sc_security_token_monitor_get_type ())
+#define SC_SECURITY_TOKEN_MONITOR(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SC_TYPE_SECURITY_TOKEN_MONITOR, ScSecurityTokenMonitor))
+#define SC_SECURITY_TOKEN_MONITOR_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SC_TYPE_SECURITY_TOKEN_MONITOR, ScSecurityTokenMonitorClass))
+#define SC_IS_SECURITY_TOKEN_MONITOR(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SC_TYPE_SECURITY_TOKEN_MONITOR))
+#define SC_IS_SECURITY_TOKEN_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SC_TYPE_SECURITY_TOKEN_MONITOR))
+#define SC_SECURITY_TOKEN_MONITOR_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), SC_TYPE_SECURITY_TOKEN_MONITOR, ScSecurityTokenMonitorClass))
+#define SC_SECURITY_TOKEN_MONITOR_ERROR           (sc_security_token_monitor_error_quark ())
+typedef struct _ScSecurityTokenMonitor ScSecurityTokenMonitor;
+typedef struct _ScSecurityTokenMonitorClass ScSecurityTokenMonitorClass;
+typedef struct _ScSecurityTokenMonitorPrivate ScSecurityTokenMonitorPrivate;
+typedef enum _ScSecurityTokenMonitorError ScSecurityTokenMonitorError;
+
+struct _ScSecurityTokenMonitor {
+    GObject parent;
+
+    /*< private > */
+    ScSecurityTokenMonitorPrivate *priv;
+};
+
+struct _ScSecurityTokenMonitorClass {
+    GObjectClass parent_class;
+
+    /* Signals */
+    void (*security_token_inserted) (ScSecurityTokenMonitor *monitor,
+				     ScSecurityToken *token);
+    void (*security_token_removed) (ScSecurityTokenMonitor *monitor,
+				    ScSecurityToken *token);
+    void (*error) (ScSecurityTokenMonitor *monitor, 
+		   GError                 *error);
+};
+
+enum _ScSecurityTokenMonitorError {
+    SC_SECURITY_TOKEN_MONITOR_ERROR_GENERIC = 0,
+    SC_SECURITY_TOKEN_MONITOR_ERROR_WITH_NSS,
+    SC_SECURITY_TOKEN_MONITOR_ERROR_LOADING_DRIVER,
+    SC_SECURITY_TOKEN_MONITOR_ERROR_WATCHING_FOR_EVENTS,
+    SC_SECURITY_TOKEN_MONITOR_ERROR_REPORTING_EVENTS
+};
+
+GType sc_security_token_monitor_get_type (void) G_GNUC_CONST;
+GQuark sc_security_token_monitor_error_quark (void) G_GNUC_CONST;
+
+ScSecurityTokenMonitor *sc_security_token_monitor_new (const gchar *module);
+
+gboolean sc_security_token_monitor_start (ScSecurityTokenMonitor  *monitor, 
+				 	  GError                 **error);
+
+void sc_security_token_monitor_stop (ScSecurityTokenMonitor *monitor);
+
+gchar *sc_security_token_monitor_get_module_path (ScSecurityTokenMonitor *monitor);
+gboolean sc_security_token_monitor_login_token_is_inserted (ScSecurityTokenMonitor *monitor);
+
+G_END_DECLS
+#endif				/* SC_SECURITY_TOKEN_MONITOR_H */
--- gdm-2.19.1/daemon/Makefile.am.security-tokens	2007-05-13 22:08:25.000000000 -0400
+++ gdm-2.19.1/daemon/Makefile.am	2007-05-21 16:04:51.000000000 -0400
@@ -9,6 +9,7 @@ INCLUDES = \
 	-DAUTHDIR=\"$(authdir)\"			\
 	-DBINDIR=\"$(bindir)\"				\
 	-DDATADIR=\"$(datadir)\"			\
+	-DSYSCONFDIR=\"$(sysconfdir)\"			\
 	-DDMCONFDIR=\"$(dmconfdir)\"			\
 	-DGDMCONFDIR=\"$(gdmconfdir)\"			\
 	-DGDMLOCALEDIR=\"$(gdmlocaledir)\"		\
@@ -68,6 +69,10 @@ gdm_binary_SOURCES = \
 	gdm-net.h \
 	getvt.c \
 	getvt.h	\
+	securitytoken.c \
+	securitytoken.h \
+	securitytokenmonitor.c \
+	securitytokenmonitor.h \
 	$(NULL)
 
 XDMCP_SOURCES =			\
--- /dev/null	2007-05-21 09:34:56.803421964 -0400
+++ gdm-2.19.1/daemon/securitytoken.h	2007-05-21 16:04:51.000000000 -0400
@@ -0,0 +1,94 @@
+/* securitytoken.h - api for reading and writing data to a security token 
+ *
+ * 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 SC_SECURITY_TOKEN_H
+#define SC_SECURITY_TOKEN_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <secmod.h>
+
+G_BEGIN_DECLS
+#define SC_TYPE_SECURITY_TOKEN            (sc_security_token_get_type ())
+#define SC_SECURITY_TOKEN(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), SC_TYPE_SECURITY_TOKEN, ScSecurityToken))
+#define SC_SECURITY_TOKEN_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), SC_TYPE_SECURITY_TOKEN, ScSecurityTokenClass))
+#define SC_IS_SECURITY_TOKEN(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SC_TYPE_SECURITY_TOKEN))
+#define SC_IS_SECURITY_TOKEN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SC_TYPE_SECURITY_TOKEN))
+#define SC_SECURITY_TOKEN_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), SC_TYPE_SECURITY_TOKEN, ScSecurityTokenClass))
+#define SC_SECURITY_TOKEN_ERROR           (sc_security_token_error_quark ())
+typedef struct _ScSecurityTokenClass ScSecurityTokenClass;
+typedef struct _ScSecurityToken ScSecurityToken;
+typedef struct _ScSecurityTokenPrivate ScSecurityTokenPrivate;
+typedef enum _ScSecurityTokenError ScSecurityTokenError;
+typedef enum _ScSecurityTokenState ScSecurityTokenState;
+
+typedef struct _ScSecurityTokenRequest ScSecurityTokenRequest;
+
+struct _ScSecurityToken {
+    GObject parent;
+
+    /*< private > */
+    ScSecurityTokenPrivate *priv;
+};
+
+struct _ScSecurityTokenClass {
+    GObjectClass parent_class;
+
+    void (* inserted) (ScSecurityToken *token);
+    void (* removed) (ScSecurityToken *token);
+};
+
+enum _ScSecurityTokenError {
+    SC_SECURITY_TOKEN_ERROR_GENERIC = 0,
+};
+
+enum _ScSecurityTokenState {
+    SC_SECURITY_TOKEN_STATE_INSERTED = 0,
+    SC_SECURITY_TOKEN_STATE_REMOVED,
+};
+
+GType sc_security_token_get_type (void) G_GNUC_CONST;
+GQuark sc_security_token_error_quark (void) G_GNUC_CONST;
+
+CK_SLOT_ID sc_security_token_get_slot_id (ScSecurityToken *token);
+gint sc_security_token_get_slot_series (ScSecurityToken *token);
+ScSecurityTokenState sc_security_token_get_state (ScSecurityToken *token);
+
+gchar *sc_security_token_get_name (ScSecurityToken *token);
+gboolean sc_security_token_is_login_token (ScSecurityToken *token);
+
+gboolean sc_security_token_unlock (ScSecurityToken *token,
+				   const gchar     *password);
+
+/* don't under any circumstances call these functions */
+#ifdef SC_SECURITY_TOKEN_ENABLE_INTERNAL_API
+
+ScSecurityToken *_sc_security_token_new (SECMODModule *module,
+					 CK_SLOT_ID slot_id, 
+					 gint slot_series);
+ScSecurityToken *_sc_security_token_new_from_name (SECMODModule *module,
+						   const gchar *name);
+
+void _sc_security_token_set_state (ScSecurityToken      *token,
+				   ScSecurityTokenState  state);
+#endif 
+
+G_END_DECLS
+#endif				/* SC_SECURITY_TOKEN_H */
--- /dev/null	2007-05-21 09:34:56.803421964 -0400
+++ gdm-2.19.1/daemon/securitytokenmonitor.c	2007-05-21 16:04:51.000000000 -0400
@@ -0,0 +1,1743 @@
+/* securitytokenmonitor.c - monitor for security token insertion and
+ *                          removal events
+ * 
+ * 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.  
+ *
+ * TODO:     - doing this per project is a bad idea i think.
+ *             We should probably make this a system service 
+ *             and use dbus.
+ */
+#define _GNU_SOURCE
+#include "securitytokenmonitor.h"
+
+#define SC_SECURITY_TOKEN_ENABLE_INTERNAL_API
+#include "securitytoken.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 <nss.h>
+#include <pk11func.h>
+#include <secmod.h>
+#include <secerr.h>
+
+#ifndef SC_SECURITY_TOKEN_MONITOR_DRIVER
+#define SC_SECURITY_TOKEN_MONITOR_DRIVER LIBDIR"/pkcs11/libcoolkeypk11.so"
+#endif
+
+#ifndef SC_SECURITY_TOKEN_MONITOR_NSS_DB
+#define SC_SECURITY_TOKEN_MONITOR_NSS_DB SYSCONFDIR"/pki/nssdb"
+#endif 
+
+#ifndef SC_MAX_OPEN_FILE_DESCRIPTORS
+#define SC_MAX_OPEN_FILE_DESCRIPTORS 1024
+#endif
+
+#ifndef SC_OPEN_FILE_DESCRIPTORS_DIR
+#define SC_OPEN_FILE_DESCRIPTORS_DIR "/proc/self/fd"
+#endif
+
+#ifndef sc_debug
+#if defined (SC_SECURITY_TOKEN_MONITOR_ENABLE_TEST)
+#define sc_debug(fmt, args...) g_printerr("[%u] " fmt " \n", getpid(), ##args)
+#else
+#define sc_debug(fmt, args...) 
+#endif
+#endif
+
+typedef enum _ScSecurityTokenMonitorState ScSecurityTokenMonitorState;
+typedef struct _ScSecurityTokenMonitorWorker ScSecurityTokenMonitorWorker;
+
+enum _ScSecurityTokenMonitorState {
+	SC_SECURITY_TOKEN_MONITOR_STATE_STOPPED = 0,
+	SC_SECURITY_TOKEN_MONITOR_STATE_STARTING,
+	SC_SECURITY_TOKEN_MONITOR_STATE_STARTED,
+	SC_SECURITY_TOKEN_MONITOR_STATE_STOPPING,
+};
+
+struct _ScSecurityTokenMonitorPrivate {
+	ScSecurityTokenMonitorState state;
+	SECMODModule *module;
+	gchar        *module_path;
+
+	GSource *security_token_event_source;
+	GPid security_token_event_watcher_pid;
+	GHashTable *security_tokens;
+
+	guint poll_timeout_id;
+
+	guint32 is_unstoppable : 1;
+	guint32 nss_is_loaded : 1;
+
+#ifndef SC_SECURITY_TOKEN_MONITOR_DRIVER_CAN_BE_RELOADED_AFTER_BEING_DESTROYED
+	GArray *fds_to_close_on_fork;
+#endif 
+};
+
+struct _ScSecurityTokenMonitorWorker {
+	SECMODModule *module;
+	GHashTable *security_tokens;
+	gint write_fd;
+
+	guint32 nss_is_loaded : 1;
+};
+
+static void sc_security_token_monitor_finalize (GObject *object);
+static void sc_security_token_monitor_class_install_signals (ScSecurityTokenMonitorClass *service_class);
+static void sc_security_token_monitor_class_install_properties (ScSecurityTokenMonitorClass *service_class);
+static void sc_security_token_monitor_set_property (GObject       *object,
+						    guint          prop_id,
+						    const GValue  *value,
+						    GParamSpec    *pspec);
+static void sc_security_token_monitor_get_property (GObject    *object,
+						    guint       prop_id,
+						    GValue     *value,
+						    GParamSpec *pspec);
+static void sc_security_token_monitor_set_module_path (ScSecurityTokenMonitor *monitor,
+						       const gchar            *module_path);
+static void sc_security_token_monitor_token_removed_handler (ScSecurityTokenMonitor *monitor,
+							     ScSecurityToken        *token);
+static void sc_security_token_monitor_token_inserted_handler (ScSecurityTokenMonitor *monitor_class,
+							      ScSecurityToken        *token);
+static gboolean sc_security_token_monitor_stop_now (ScSecurityTokenMonitor *monitor);
+static void sc_security_token_monitor_queue_stop (ScSecurityTokenMonitor *monitor);
+
+static gboolean sc_security_token_monitor_create_worker (ScSecurityTokenMonitor *monitor,
+							 gint *worker_fd, GPid *worker_pid);
+
+static ScSecurityTokenMonitorWorker * sc_security_token_monitor_worker_new (gint write_fd);
+static void sc_security_token_monitor_worker_free (ScSecurityTokenMonitorWorker *worker);
+static void sc_security_token_monitor_worker_die_with_parent (ScSecurityTokenMonitorWorker *worker);
+static gboolean sc_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 ScSecurityToken *sc_read_security_token (gint fd, SECMODModule *module);
+static gboolean sc_write_security_token (gint fd, ScSecurityToken *token);
+
+enum {
+	PROP_0 = 0,
+	PROP_MODULE_PATH,
+	NUMBER_OF_PROPERTIES
+};
+
+enum {
+	SECURITY_TOKEN_INSERTED = 0,
+	SECURITY_TOKEN_REMOVED,
+	ERROR,
+	NUMBER_OF_SIGNALS
+};
+
+static guint sc_security_token_monitor_signals[NUMBER_OF_SIGNALS];
+
+G_DEFINE_TYPE (ScSecurityTokenMonitor, 
+	       sc_security_token_monitor, 
+	       G_TYPE_OBJECT);
+
+static void
+sc_security_token_monitor_class_init (ScSecurityTokenMonitorClass *monitor_class)
+{
+    GObjectClass *gobject_class;
+
+    gobject_class = G_OBJECT_CLASS (monitor_class);
+
+    gobject_class->finalize = sc_security_token_monitor_finalize;
+
+    sc_security_token_monitor_class_install_signals (monitor_class);
+    sc_security_token_monitor_class_install_properties (monitor_class);
+
+    g_type_class_add_private (monitor_class,
+			      sizeof (ScSecurityTokenMonitorPrivate));
+}
+
+static void
+sc_security_token_monitor_class_install_properties (ScSecurityTokenMonitorClass *token_class)
+{
+    GObjectClass *object_class;
+    GParamSpec *param_spec;
+
+    object_class = G_OBJECT_CLASS (token_class);
+    object_class->set_property = sc_security_token_monitor_set_property;
+    object_class->get_property = sc_security_token_monitor_get_property;
+
+    param_spec = g_param_spec_string ("module-path", _("Module Path"),
+				      _("path to security token 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 
+sc_security_token_monitor_set_property (GObject       *object,
+					guint          prop_id,
+					const GValue  *value,
+					GParamSpec    *pspec)
+{
+    ScSecurityTokenMonitor *monitor = SC_SECURITY_TOKEN_MONITOR (object);
+
+    switch (prop_id)
+    {
+	    case PROP_MODULE_PATH:
+		    sc_security_token_monitor_set_module_path (monitor, 
+							       g_value_get_string (value));
+		    break;
+
+	    default:
+		    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		    break;
+    }
+}
+
+static void 
+sc_security_token_monitor_get_property (GObject    *object,
+					guint       prop_id,
+					GValue     *value,
+					GParamSpec *pspec)
+{
+    ScSecurityTokenMonitor *monitor = SC_SECURITY_TOKEN_MONITOR (object);
+    gchar *module_path;
+
+    switch (prop_id)
+    {
+	    case PROP_MODULE_PATH:
+		    module_path = sc_security_token_monitor_get_module_path (monitor);
+		    g_value_set_string (value, module_path);
+		    g_free (module_path);
+		    break;
+
+	    default:
+		    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+		    break;
+    }
+}
+
+gchar *
+sc_security_token_monitor_get_module_path (ScSecurityTokenMonitor *monitor)
+{
+    return monitor->priv->module_path;
+}
+
+static void
+sc_security_token_monitor_set_module_path (ScSecurityTokenMonitor *monitor,
+					   const gchar            *module_path)
+{
+    if ((monitor->priv->module_path == NULL) && (module_path == NULL))
+	    return;
+
+    if (((monitor->priv->module_path == NULL) ||
+	 (module_path == NULL) ||
+	 (strcmp (monitor->priv->module_path, module_path) != 0))) {
+	    g_free (monitor->priv->module_path);
+	    monitor->priv->module_path = g_strdup (module_path);
+	    g_object_notify (G_OBJECT (monitor), "module-path");
+    }
+}
+
+static void
+sc_security_token_monitor_token_removed_handler (ScSecurityTokenMonitor *monitor,
+						 ScSecurityToken        *token)
+{
+    sc_debug ("informing security token of its removal");
+    _sc_security_token_set_state (token, SC_SECURITY_TOKEN_STATE_REMOVED);
+    sc_debug ("done");
+}
+
+static void
+sc_security_token_monitor_token_inserted_handler (ScSecurityTokenMonitor *monitor,
+						  ScSecurityToken        *token)
+{
+    sc_debug ("informing security token of its insertion");
+
+    _sc_security_token_set_state (token, SC_SECURITY_TOKEN_STATE_INSERTED);
+    sc_debug ("done");
+
+}
+
+static void
+sc_security_token_monitor_class_install_signals (ScSecurityTokenMonitorClass *monitor_class)
+{
+    GObjectClass *object_class;
+
+    object_class = G_OBJECT_CLASS (monitor_class);
+
+    sc_security_token_monitor_signals[SECURITY_TOKEN_INSERTED] =
+	    g_signal_new ("security-token-inserted",
+			  G_OBJECT_CLASS_TYPE (object_class),
+			  G_SIGNAL_RUN_FIRST,
+			  G_STRUCT_OFFSET (ScSecurityTokenMonitorClass,
+					   security_token_inserted), 
+			  NULL, NULL, g_cclosure_marshal_VOID__POINTER, 
+			  G_TYPE_NONE, 1, G_TYPE_POINTER);
+    monitor_class->security_token_inserted = sc_security_token_monitor_token_inserted_handler;
+
+    sc_security_token_monitor_signals[SECURITY_TOKEN_REMOVED] =
+	    g_signal_new ("security-token-removed",
+			  G_OBJECT_CLASS_TYPE (object_class),
+			  G_SIGNAL_RUN_FIRST,
+			  G_STRUCT_OFFSET (ScSecurityTokenMonitorClass,
+					   security_token_removed), 
+			  NULL, NULL, g_cclosure_marshal_VOID__POINTER, 
+			  G_TYPE_NONE, 1, G_TYPE_POINTER);
+    monitor_class->security_token_removed = sc_security_token_monitor_token_removed_handler;
+
+    sc_security_token_monitor_signals[ERROR] =
+	    g_signal_new ("error",
+			  G_OBJECT_CLASS_TYPE (object_class),
+			  G_SIGNAL_RUN_LAST,
+			  G_STRUCT_OFFSET (ScSecurityTokenMonitorClass, error),
+			  NULL, NULL, g_cclosure_marshal_VOID__POINTER,
+			  G_TYPE_NONE, 1, G_TYPE_POINTER);
+    monitor_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
+sc_security_token_monitor_init (ScSecurityTokenMonitor *monitor)
+{
+    sc_debug ("initializing security token monitor");
+
+    monitor->priv = G_TYPE_INSTANCE_GET_PRIVATE (monitor,
+						 SC_TYPE_SECURITY_TOKEN_MONITOR,
+						 ScSecurityTokenMonitorPrivate);
+    monitor->priv->poll_timeout_id = 0;
+    monitor->priv->is_unstoppable = FALSE;
+    monitor->priv->module = NULL;
+
+    monitor->priv->security_tokens = 
+	    g_hash_table_new_full (g_str_hash, 
+				   g_str_equal,
+				   (GDestroyNotify) g_free, 
+				   (GDestroyNotify) g_object_unref);
+
+#ifndef SC_SECURITY_TOKEN_MONITOR_DRIVER_CAN_BE_RELOADED_AFTER_BEING_DESTROYED
+    monitor->priv->fds_to_close_on_fork = g_array_new (FALSE, FALSE, sizeof (gint));
+#endif
+
+}
+
+static void 
+sc_security_token_monitor_finalize (GObject *object)
+{
+    ScSecurityTokenMonitor *monitor;
+    GObjectClass *gobject_class;
+
+    monitor = SC_SECURITY_TOKEN_MONITOR (object);
+    gobject_class =
+	    G_OBJECT_CLASS (sc_security_token_monitor_parent_class);
+
+    sc_security_token_monitor_stop_now (monitor);
+
+    g_hash_table_destroy (monitor->priv->security_tokens);
+    monitor->priv->security_tokens = NULL;
+
+#ifndef SC_SECURITY_TOKEN_MONITOR_DRIVER_CAN_BE_RELOADED_AFTER_BEING_DESTROYED
+    g_array_free (monitor->priv->fds_to_close_on_fork, TRUE);
+#endif 
+
+    gobject_class->finalize (object);
+}
+
+GQuark 
+sc_security_token_monitor_error_quark (void)
+{
+    static GQuark error_quark = 0;
+
+    if (error_quark == 0)
+	    error_quark = g_quark_from_static_string ("sc-security-token-monitor-error-quark");
+
+    return error_quark;
+}
+
+ScSecurityTokenMonitor *
+sc_security_token_monitor_new (const gchar *module_path)
+{
+    ScSecurityTokenMonitor *instance;
+
+    instance = SC_SECURITY_TOKEN_MONITOR (g_object_new (SC_TYPE_SECURITY_TOKEN_MONITOR, 
+							"module-path", module_path,
+							NULL));
+
+    return instance;
+}
+
+static void 
+sc_security_token_monitor_emit_error (ScSecurityTokenMonitor *monitor,
+				      GError                 *error)
+{
+    monitor->priv->is_unstoppable = TRUE;
+    g_signal_emit (monitor, sc_security_token_monitor_signals[ERROR], 0,
+		   error);
+    monitor->priv->is_unstoppable = FALSE;
+}
+
+static void 
+sc_security_token_monitor_emit_security_token_inserted (ScSecurityTokenMonitor *monitor,
+							ScSecurityToken        *token)
+{
+    monitor->priv->is_unstoppable = TRUE;
+    g_signal_emit (monitor, sc_security_token_monitor_signals[SECURITY_TOKEN_INSERTED], 0,
+		   token);
+    monitor->priv->is_unstoppable = FALSE;
+}
+
+static void 
+sc_security_token_monitor_emit_security_token_removed (ScSecurityTokenMonitor *monitor,
+							ScSecurityToken        *token)
+{
+    ScSecurityTokenMonitorState old_state;
+
+    old_state = monitor->priv->state;
+    monitor->priv->is_unstoppable = TRUE;
+    g_signal_emit (monitor, sc_security_token_monitor_signals[SECURITY_TOKEN_REMOVED], 0,
+		   token);
+    monitor->priv->is_unstoppable = FALSE;
+}
+
+static gboolean
+sc_security_token_monitor_check_for_and_process_events (GIOChannel *io_channel,
+							GIOCondition condition,
+							ScSecurityTokenMonitor *monitor)
+{
+    ScSecurityToken *token;
+    gboolean should_stop;
+    guchar event_type;
+    gchar *token_name;
+    gint fd;
+
+    token = NULL;
+    should_stop = (condition & G_IO_HUP) || (condition & G_IO_ERR);
+
+    if (should_stop)
+	    sc_debug ("received %s on event socket, stopping "
+		      "monitor...", 
+		      (condition & G_IO_HUP) && (condition & G_IO_ERR)? 
+		      "error and hangup" : 
+		      (condition & G_IO_HUP)? 
+		      "hangup" : "error");
+
+    if (!(condition & G_IO_IN))
+	    goto out;
+
+    fd = g_io_channel_unix_get_fd (io_channel);
+
+    event_type = '\0';
+    if (!sc_read_bytes (fd, &event_type, 1)) {
+	    should_stop = TRUE;
+	    goto out;
+    }
+
+    token = sc_read_security_token (fd, monitor->priv->module);
+
+    if (token == NULL) {
+	    should_stop = TRUE;
+	    goto out;
+    }
+
+    token_name = sc_security_token_get_name (token);
+
+    switch (event_type) {
+	    case 'I':
+		    g_hash_table_replace (monitor->priv->security_tokens,
+					  token_name, token);
+		    token_name = NULL;
+
+		    sc_security_token_monitor_emit_security_token_inserted (monitor, token);
+		    token = NULL;
+		    break;
+
+	    case 'R':
+		    sc_security_token_monitor_emit_security_token_removed (monitor, token);
+		    if (!g_hash_table_remove (monitor->priv->security_tokens, token_name))
+			    sc_debug ("got removal event of unknown token!");
+		    g_free (token_name);
+		    token_name = NULL;
+		    token = NULL;
+		    break;
+
+	    default: 
+		    g_free (token_name);
+		    token_name = NULL;
+		    g_object_unref (token);
+
+		    should_stop = TRUE;
+		    break;
+    }
+
+out:
+    if (should_stop) {
+	    GError *error;
+
+	    error = g_error_new (SC_SECURITY_TOKEN_MONITOR_ERROR,
+				 SC_SECURITY_TOKEN_MONITOR_ERROR_WATCHING_FOR_EVENTS,
+				 "%s", (condition & G_IO_IN) ? g_strerror (errno) : _("received error or hang up from event source"));
+
+	    sc_security_token_monitor_emit_error (monitor, error);
+	    g_error_free (error);
+	    sc_security_token_monitor_stop_now (monitor);
+	    return FALSE;
+    }
+
+    return TRUE;
+}
+
+static void
+sc_security_token_monitor_event_processing_stopped_handler (ScSecurityTokenMonitor *monitor)
+{
+    monitor->priv->security_token_event_source = NULL;
+    sc_security_token_monitor_stop_now (monitor);
+}
+
+/* sorta complex function that is nothing more than fork() without having
+ * to worry about reaping the child later with waitpid
+ */
+static GPid
+sc_fork_and_disown (void)
+{
+    pid_t child_pid;
+    GPid grandchild_pid;
+    gint write_fd, read_fd;
+    gint saved_errno;
+
+    write_fd = -1;
+    read_fd = -1;
+    if (!sc_open_pipe (&write_fd, &read_fd))
+	    return (GPid) -1;
+
+    child_pid = fork ();
+
+    if (child_pid < 0) {
+	    close (write_fd);
+	    close (read_fd);
+	    return (GPid) child_pid;
+    }
+
+    if (child_pid == 0) {
+
+	    /* close the end of the pipe we're not going to use
+	     */
+	    close (read_fd);
+
+	    /* fork again 
+	     */
+	    child_pid = fork ();
+
+	    /* in the event of error, write out negative errno
+	     */
+	    if (child_pid < 0) {
+		    child_pid = -1 * errno;
+
+		    sc_write_bytes (write_fd, &child_pid, sizeof (child_pid));
+		    close (write_fd);
+		    _exit (1);
+	    }
+
+	    /* otherwise write out the pid of the child and exit
+	     */
+	    if (child_pid != 0) {
+
+		    signal (SIGPIPE, SIG_IGN);
+
+		    if (!sc_write_bytes (write_fd, &child_pid, sizeof (child_pid))) {
+			    kill (SIGKILL, child_pid);
+			    _exit (2);
+		    }
+		    close (write_fd);
+		    _exit (0);
+	    }
+	    close (write_fd);
+
+	    /* we're done, we've forked without having to worry about
+	     * reaping the child later
+	     */
+	    g_assert (child_pid == 0);
+	    return (GPid) 0;
+    }
+
+    /* close the end of the pipe we're not going to use
+     */
+    close (write_fd);
+
+    grandchild_pid = -1;
+    if (!sc_read_bytes (read_fd, &grandchild_pid, sizeof (grandchild_pid))) {
+	    grandchild_pid = -1;
+    }
+
+    saved_errno = errno;
+
+    /* close the other end of the pipe since we're done with it
+     */
+    close (read_fd);
+
+    /* wait for child to die (and emancipate the grandchild)
+     */
+    waitpid (child_pid, NULL, 0);
+    
+    errno = saved_errno;
+    return (GPid) grandchild_pid;
+}
+
+static gboolean
+sc_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
+sc_security_token_monitor_stop_watching_for_events (ScSecurityTokenMonitor  *monitor)
+{
+    if (monitor->priv->security_token_event_source != NULL) {
+	    g_source_destroy (monitor->priv->security_token_event_source);
+	    monitor->priv->security_token_event_source = NULL;
+    }
+
+    if (monitor->priv->security_token_event_watcher_pid > 0) {
+	    kill (monitor->priv->security_token_event_watcher_pid, SIGKILL);
+	    monitor->priv->security_token_event_watcher_pid = 0;
+    }
+}
+
+static gboolean
+sc_load_nss (GError **error)
+{
+    SECStatus status = SECSuccess;
+    static const guint32 flags = 
+	NSS_INIT_READONLY| NSS_INIT_NOCERTDB | NSS_INIT_NOMODDB | 
+	NSS_INIT_FORCEOPEN | NSS_INIT_NOROOTINIT | 
+	NSS_INIT_OPTIMIZESPACE | NSS_INIT_PK11RELOAD;
+
+    sc_debug ("attempting to load NSS database '%s'",
+	      SC_SECURITY_TOKEN_MONITOR_NSS_DB);
+
+    status = NSS_Initialize (SC_SECURITY_TOKEN_MONITOR_NSS_DB,
+			     "", "", SECMOD_DB, flags);
+
+    if (status != SECSuccess) {
+	    gsize error_message_size;
+	    gchar *error_message;
+
+	    error_message_size = PR_GetErrorTextLength ();
+
+	    if (error_message_size == 0) {
+		    sc_debug ("NSS security system could not be initialized");
+		    g_set_error (error,
+				 SC_SECURITY_TOKEN_MONITOR_ERROR,
+				 SC_SECURITY_TOKEN_MONITOR_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,
+			 SC_SECURITY_TOKEN_MONITOR_ERROR,
+			 SC_SECURITY_TOKEN_MONITOR_ERROR_WITH_NSS,
+			 "%s", error_message);
+	    sc_debug ("NSS security system could not be initialized - %s",
+		      error_message);
+
+	    g_slice_free1 (error_message_size, error_message);
+
+	    goto out;
+    }
+
+    sc_debug ("NSS database sucessfully loaded");
+    return TRUE;
+
+out:
+    sc_debug ("NSS database couldn't be sucessfully loaded");
+    return FALSE;
+}
+
+static SECMODModule *
+sc_load_driver (gchar   *module_path,
+		GError **error)
+{
+    SECMODModule *module;
+    gchar *module_spec;
+    gboolean module_explicitly_specified;
+
+    sc_debug ("attempting to load driver...");
+
+    module = NULL;
+    module_explicitly_specified = module_path != NULL;
+    if (module_explicitly_specified) {
+	    module_spec = g_strdup_printf ("library=\"%s\"", module_path);
+	    sc_debug ("loading security token driver using spec '%s'",
+		      module_spec);
+
+	    module = SECMOD_LoadUserModule (module_spec, 
+					    NULL /* parent */, 
+					    FALSE /* recurse */);
+	    g_free (module_spec);
+	    module_spec = NULL;
+
+    } else {
+	    SECMODModuleList *modules, *tmp;
+
+	    modules = SECMOD_GetDefaultModuleList ();
+
+	    for (tmp = modules; tmp != NULL; tmp = tmp->next) {
+		    if (!SECMOD_HasRemovableSlots (tmp->module) ||
+			!tmp->module->loaded)
+			    continue;
+
+		    module = SECMOD_ReferenceModule (tmp->module);
+		    break;
+	    }
+
+	    /* fallback to compiled in driver path
+	     */
+	    if (module == NULL) {
+		    if (g_file_test (SC_SECURITY_TOKEN_MONITOR_DRIVER,
+				     G_FILE_TEST_IS_REGULAR)) {
+
+			    module_spec = g_strdup_printf ("library=\"%s\"", module_path);
+			    sc_debug ("loading security token driver using spec '%s'",
+				      module_spec);
+
+			    module = SECMOD_LoadUserModule (module_spec, 
+							    NULL /* parent */, 
+							    FALSE /* recurse */);
+			    g_free (module_spec);
+			    module_spec = NULL;
+
+		    }
+	    }
+
+    }
+
+    if (!module_explicitly_specified && module == NULL) {
+	    g_set_error (error,
+			 SC_SECURITY_TOKEN_MONITOR_ERROR,
+			 SC_SECURITY_TOKEN_MONITOR_ERROR_LOADING_DRIVER,
+			 _("no suitable security token driver could be found"));
+    } else if (module == NULL || !module->loaded) {
+
+	    gsize error_message_size;
+	    gchar *error_message;
+            
+	    if (module != NULL && !module->loaded) {
+		    sc_debug ("module found but not loaded?!");
+		    SECMOD_DestroyModule (module);
+		    module = NULL;
+	    }
+
+	    error_message_size = PR_GetErrorTextLength ();
+
+	    if (error_message_size == 0) {
+		    sc_debug ("security token driver '%s' could not be loaded",
+			      module_path);
+		    g_set_error (error,
+				 SC_SECURITY_TOKEN_MONITOR_ERROR,
+				 SC_SECURITY_TOKEN_MONITOR_ERROR_LOADING_DRIVER,
+				 _("security token 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,
+			 SC_SECURITY_TOKEN_MONITOR_ERROR,
+			 SC_SECURITY_TOKEN_MONITOR_ERROR_LOADING_DRIVER,
+			 "%s", error_message);
+
+	    sc_debug ("security token driver '%s' could not be loaded - %s",
+		      module_path, error_message);
+	    g_slice_free1 (error_message_size, error_message);
+    }
+
+out:
+    return module;
+}
+
+static void
+sc_security_token_monitor_get_all_tokens (ScSecurityTokenMonitor *monitor)
+{
+    int i;
+
+    for (i = 0; i < monitor->priv->module->slotCount; i++) {
+	    ScSecurityToken *token;
+	    CK_SLOT_ID    slot_id;
+	    gint          slot_series;
+	    gchar *token_name;
+
+	    slot_id = PK11_GetSlotID (monitor->priv->module->slots[i]);
+	    slot_series = PK11_GetSlotSeries (monitor->priv->module->slots[i]);
+
+	    token = _sc_security_token_new (monitor->priv->module, 
+					    slot_id, slot_series);
+
+	    token_name = sc_security_token_get_name (token);
+
+	    g_hash_table_replace (monitor->priv->security_tokens,
+				  token_name, token);
+    }
+}
+
+gboolean
+sc_security_token_monitor_start (ScSecurityTokenMonitor  *monitor,
+				 GError                 **error)
+{
+    GError *watching_error;
+    gint worker_fd;
+    GPid worker_pid;
+    GIOChannel *io_channel;
+    GSource *source;
+    GIOFlags channel_flags;
+    GError *nss_error;
+
+    if (monitor->priv->state == SC_SECURITY_TOKEN_MONITOR_STATE_STARTED) {
+	    sc_debug ("security token monitor already started");
+	    return TRUE;
+    }
+
+    monitor->priv->state = SC_SECURITY_TOKEN_MONITOR_STATE_STARTING;
+
+    worker_fd = -1;
+    worker_pid = 0;
+
+    nss_error = NULL;
+    if (!monitor->priv->nss_is_loaded && !sc_load_nss (&nss_error)) {
+	    g_propagate_error (error, nss_error);
+	    goto out;
+    }
+    monitor->priv->nss_is_loaded = TRUE;
+
+    if (monitor->priv->module == NULL)
+	    monitor->priv->module = sc_load_driver (monitor->priv->module_path, &nss_error);
+
+    if (monitor->priv->module == NULL) {
+	    g_propagate_error (error, nss_error);
+	    goto out;
+    }
+
+    if (!sc_security_token_monitor_create_worker (monitor, &worker_fd, &worker_pid)) {
+
+	    g_set_error (error,
+			 SC_SECURITY_TOKEN_MONITOR_ERROR,
+			 SC_SECURITY_TOKEN_MONITOR_ERROR_WATCHING_FOR_EVENTS,
+			 _("could not watch for incoming token events - %s"),
+			 g_strerror (errno));
+
+	    goto out;
+    }
+
+    monitor->priv->security_token_event_watcher_pid = worker_pid;
+
+    io_channel = g_io_channel_unix_new (worker_fd);
+
+    channel_flags = g_io_channel_get_flags (io_channel);
+    watching_error = NULL;
+
+    source = g_io_create_watch (io_channel, G_IO_IN | G_IO_HUP);
+    g_io_channel_unref (io_channel);
+    io_channel = NULL;
+
+    monitor->priv->security_token_event_source = source;
+
+    g_source_set_callback (monitor->priv->security_token_event_source,
+			   (GSourceFunc) (GIOFunc)
+			   sc_security_token_monitor_check_for_and_process_events,
+			   monitor,
+			   (GDestroyNotify)
+			   sc_security_token_monitor_event_processing_stopped_handler);
+    g_source_attach (monitor->priv->security_token_event_source, NULL);
+    g_source_unref (monitor->priv->security_token_event_source);
+
+    /* populate the hash with tokens that are already inserted
+     */
+    sc_security_token_monitor_get_all_tokens (monitor);
+
+    monitor->priv->state = SC_SECURITY_TOKEN_MONITOR_STATE_STARTED;
+
+out:
+    /* don't leave it in a half started state
+     */
+    if (monitor->priv->state != SC_SECURITY_TOKEN_MONITOR_STATE_STARTED) {
+	    sc_debug ("security token monitor could not be completely started");
+	    sc_security_token_monitor_stop (monitor);
+    } else
+	    sc_debug ("security token monitor started");
+
+    return monitor->priv->state == SC_SECURITY_TOKEN_MONITOR_STATE_STARTED;
+}
+
+static gboolean
+sc_security_token_monitor_stop_now (ScSecurityTokenMonitor *monitor)
+{
+    if (monitor->priv->state == SC_SECURITY_TOKEN_MONITOR_STATE_STOPPED)
+	    return FALSE;
+
+    monitor->priv->state = SC_SECURITY_TOKEN_MONITOR_STATE_STOPPED;
+    sc_security_token_monitor_stop_watching_for_events (monitor);
+#ifdef SC_SECURITY_TOKEN_MONITOR_DRIVER_CAN_BE_RELOADED_AFTER_BEING_DESTROYED
+    if (monitor->priv->module != NULL) {
+	    SECMOD_DestroyModule (monitor->priv->module);
+	    monitor->priv->module = NULL;
+    }
+
+    if (monitor->priv->nss_is_loaded) {
+	    NSS_Shutdown ();
+	    monitor->priv->nss_is_loaded = FALSE;
+    }
+#endif
+    sc_debug ("security token monitor stopped");
+    
+    return FALSE;
+}
+
+static void
+sc_security_token_monitor_queue_stop (ScSecurityTokenMonitor *monitor)
+{
+
+    monitor->priv->state = SC_SECURITY_TOKEN_MONITOR_STATE_STOPPING;
+
+    g_idle_add ((GSourceFunc) sc_security_token_monitor_stop_now, monitor);
+}
+
+void 
+sc_security_token_monitor_stop (ScSecurityTokenMonitor *monitor)
+{
+    if (monitor->priv->state == SC_SECURITY_TOKEN_MONITOR_STATE_STOPPED)
+	    return;
+
+    if (monitor->priv->is_unstoppable) {
+	    sc_security_token_monitor_queue_stop (monitor);
+	    return;
+    } 
+
+    sc_security_token_monitor_stop_now (monitor);
+}
+
+static void
+sc_security_token_monitor_check_for_login_token (CK_SLOT_ID       slot_id,
+						 ScSecurityToken *token,
+						 gboolean        *is_inserted)
+{
+    g_assert (is_inserted != NULL);
+
+    if (sc_security_token_is_login_token (token))
+	    *is_inserted = TRUE;
+
+}
+
+gboolean 
+sc_security_token_monitor_login_token_is_inserted (ScSecurityTokenMonitor *monitor) 
+
+{
+    gboolean is_inserted;
+
+    is_inserted = FALSE;
+    g_hash_table_foreach (monitor->priv->security_tokens, 
+			  (GHFunc)
+			  sc_security_token_monitor_check_for_login_token,
+			  &is_inserted);
+    return is_inserted;
+}
+
+static gint
+sc_get_max_open_fds (void)
+{
+  struct rlimit open_fd_limit;
+  const gint fallback_limit = SC_MAX_OPEN_FILE_DESCRIPTORS;
+
+  if (getrlimit (RLIMIT_NOFILE, &open_fd_limit) < 0) {
+	  sc_debug ("could not get file descriptor limit: %s",
+		    g_strerror (errno));
+	  sc_debug ("returning fallback file descriptor limit of %d",
+		    fallback_limit);
+	  return fallback_limit;
+  }
+
+  if (open_fd_limit.rlim_cur == RLIM_INFINITY) {
+	  sc_debug ("currently no file descriptor limit, returning fallback limit of %d",
+		    fallback_limit);
+	  return fallback_limit;
+  }
+
+  return (gint) open_fd_limit.rlim_cur;
+}
+
+static void
+sc_close_all_fds (int *fds_to_keep_open)
+{
+    int max_open_fds, fd;
+
+    sc_debug ("closing all file descriptors");
+    max_open_fds = sc_get_max_open_fds ();
+
+    for (fd = 0; fd < max_open_fds; fd++) {
+	    int i;
+	    gboolean should_close_fd;
+
+	    should_close_fd = TRUE;
+
+	    if (fds_to_keep_open != NULL) {
+		    for (i = 0; fds_to_keep_open[i] >= 0; i++) {
+			    if (fd == fds_to_keep_open[i]) {
+				    should_close_fd = FALSE;
+				    break;
+			    }
+		    }
+	    } 
+
+	    if (should_close_fd) {
+		    sc_debug ("closing file descriptor '%d'", fd);
+		    close (fd);
+	    }
+    }
+}
+
+static void
+sc_close_open_fds (int *fds_to_keep_open)
+{
+    /* using DIR instead of GDir because we need access to dirfd so
+     * that we can iterate through the fds and close them in one sweep.
+     * (if we just closed all of them then we would close the one we're using
+     * for reading the directory!)
+     */
+    DIR *dir;
+    struct dirent *entry;
+    gint fd, opendir_fd;
+    gboolean should_use_fallback;
+
+    should_use_fallback = FALSE;
+    opendir_fd = -1;
+
+    dir = opendir (SC_OPEN_FILE_DESCRIPTORS_DIR);
+
+    if (dir != NULL)
+	    opendir_fd = dirfd (dir);
+
+    if ((dir == NULL) || (opendir_fd < 0)) {
+	    sc_debug ("could not open "SC_OPEN_FILE_DESCRIPTORS_DIR": %s", g_strerror (errno));
+	    should_use_fallback = TRUE;
+    } else {
+	    sc_debug ("reading files in '"SC_OPEN_FILE_DESCRIPTORS_DIR"'");
+	    while ((entry = readdir (dir)) != NULL) {
+		    gint i;
+		    glong filename_as_number;
+		    gchar *byte_after_number;
+		    gboolean should_close_fd;
+
+		    errno = 0;
+		    if (entry->d_name[0] == '.')
+			    continue;
+
+		    sc_debug ("scanning filename '%s' for file descriptor number",
+			       entry->d_name);
+		    fd = -1;
+		    filename_as_number = strtol (entry->d_name, &byte_after_number, 10);
+
+		    g_assert (byte_after_number != NULL);
+
+		    if ((*byte_after_number != '\0') ||
+			(filename_as_number < 0) ||
+			(filename_as_number >= G_MAXINT)) {
+			    sc_debug ("filename '%s' does not appear to represent a "
+				       "file descriptor: %s",
+				       entry->d_name, strerror (errno));
+			    should_use_fallback = TRUE;
+		    } else {
+			    fd = (gint) filename_as_number;
+			    sc_debug ("filename '%s' represents file descriptor '%d'",
+				       entry->d_name, fd);
+			    should_use_fallback = FALSE;
+		    }
+
+		    if (fd == opendir_fd) {
+			    should_close_fd = FALSE;
+		    } else {
+			    should_close_fd = TRUE;
+			    if (fds_to_keep_open != NULL)
+				    for (i = 0; fds_to_keep_open[i] >= 0; i++) {
+					    if (fd == fds_to_keep_open[i]) {
+						    should_close_fd = FALSE;
+						    break;
+					    }
+				    }
+		    }
+
+		    if (should_close_fd) {
+			    sc_debug ("closing file descriptor '%d'", fd);
+			    close (fd);
+		    } else {
+			    sc_debug ("will not close file descriptor '%d' because it "
+				      "is still needed", fd);
+		    }
+	    }
+	    
+	    if (entry != NULL)
+		    should_use_fallback = TRUE;
+
+	    sc_debug ("closing directory '"SC_OPEN_FILE_DESCRIPTORS_DIR"'");
+	    closedir (dir);
+    }
+
+    /* if /proc isn't mounted or something else is screwy,
+     * fall back to closing everything
+     */
+    if (should_use_fallback)
+	    sc_close_all_fds (fds_to_keep_open);
+}
+
+static void
+sc_close_fds (gint *fds, 
+	      gint  num_fds)
+{
+    gint i;
+
+    for (i = 0; i < num_fds; i++)
+	    close (fds[i]);
+}
+
+static ScSecurityTokenMonitorWorker *
+sc_security_token_monitor_worker_new (gint write_fd)
+{
+    ScSecurityTokenMonitorWorker *worker;
+
+    worker = g_slice_new0 (ScSecurityTokenMonitorWorker);
+    worker->write_fd = write_fd;
+    worker->module = NULL;
+
+    worker->security_tokens =
+	    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 
+sc_security_token_monitor_worker_free (ScSecurityTokenMonitorWorker *worker)
+{
+    if (worker->security_tokens != NULL) {
+	    g_hash_table_destroy (worker->security_tokens);
+	    worker->security_tokens = NULL;
+    }
+
+    g_slice_free (ScSecurityTokenMonitorWorker, worker);
+}
+
+/* This function checks to see if the helper's connection to the
+ * parent process has been closed.  If it has, we assume the
+ * parent has died (or is otherwise done with the connection)
+ * and so we die, too.  We do this from a signal handler (yuck!)
+ * because there isn't a nice way to cancel the 
+ * SECMOD_WaitForAnyTokenEvent call, which just sits and blocks
+ * indefinitely.  There is a SECMOD_CancelWait wait function
+ * that we could call if we would have gone multithreaded like
+ * NSS really wants us to do, but that call isn't signal handler
+ * safe, so we just _exit() instead (eww).
+ */
+static void
+worker_io_signal_handler (int        signal_number, 
+			  siginfo_t *signal_info,
+			  void      *data)
+{
+    int number_of_events;
+    int old_errno;
+    struct pollfd poll_fds[1] = { { 0 } };
+    int parent_fd;
+
+    old_errno = errno;
+
+    /* pipe fd set up to talk to the parent */
+    parent_fd = signal_info->si_fd;
+
+    /* We only care about disconnection events
+     * (which get unmasked implicitly), so we just
+     * pass 0 for the event mask
+     */
+    poll_fds[0].events = 0;
+    poll_fds[0].fd = parent_fd;
+    
+    do {
+	    number_of_events = poll (poll_fds, G_N_ELEMENTS (poll_fds), 0);
+    } while ((number_of_events < 0) && (errno == EINTR));
+
+    g_assert (number_of_events <= G_N_ELEMENTS (poll_fds));
+
+    if (number_of_events < 0)
+	    _exit (errno);
+
+    /* pipe disconnected; parent died
+     */
+    if (number_of_events > 0) {
+	    g_assert (!(poll_fds[0].revents & POLLNVAL));
+
+	    if ((poll_fds[0].revents & POLLHUP) ||
+		(poll_fds[0].revents & POLLERR)) {
+		    _exit (poll_fds[0].revents);
+	    }
+    } 
+
+    errno = old_errno;
+}
+
+static void
+sc_security_token_monitor_worker_die_with_parent (ScSecurityTokenMonitorWorker *worker)
+{
+    struct sigaction action = { { 0 } };
+    gint flags;
+
+    /* dirty hack to clean up worker if parent goes away
+     */
+    sigemptyset (&action.sa_mask);
+    action.sa_sigaction = worker_io_signal_handler;
+    action.sa_flags = SA_SIGINFO;
+    sigaction (SIGIO, &action, NULL);
+
+    flags = fcntl (worker->write_fd, F_GETFL, 0);
+
+    fcntl (worker->write_fd, F_SETOWN, getpid ());
+    fcntl (worker->write_fd, F_SETFL, flags | O_ASYNC);
+    fcntl (worker->write_fd, F_SETSIG, SIGIO);
+}
+
+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, 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, 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 ScSecurityToken *
+sc_read_security_token (gint fd, SECMODModule *module)
+{
+    ScSecurityToken *token;
+    gchar *token_name;
+    gsize token_name_size;
+
+    token_name_size = 0;
+    if (!sc_read_bytes (fd, &token_name_size, sizeof (token_name_size)))
+	return NULL;
+
+    token_name = g_slice_alloc0 (token_name_size);
+    if (!sc_read_bytes (fd, token_name, token_name_size)) {
+	    g_slice_free1 (token_name_size, token_name);
+	    return NULL;
+    }
+    token = _sc_security_token_new_from_name (module, token_name);
+    g_slice_free1 (token_name_size, token_name);
+
+    return token;
+}
+
+static gboolean
+sc_write_security_token (gint             fd, 
+			 ScSecurityToken *token)
+{
+    gsize token_name_size;
+    gchar *token_name;
+
+    token_name = sc_security_token_get_name (token);
+    token_name_size = strlen (token_name) + 1;
+
+    if (!sc_write_bytes (fd, &token_name_size, sizeof (token_name_size))) {
+	g_free (token_name);
+	return FALSE;
+    }
+
+    if (!sc_write_bytes (fd, token_name, token_name_size)) {
+	g_free (token_name);
+	return FALSE;
+    }
+    g_free (token_name);
+
+    return TRUE;
+}
+
+static gboolean
+sc_security_token_monitor_worker_emit_security_token_removed (ScSecurityTokenMonitorWorker  *worker, 
+							      ScSecurityToken               *token, 
+							      GError                       **error)
+{
+    sc_debug ("token '%s' removed!", sc_security_token_get_name (token));
+
+    if (!sc_write_bytes (worker->write_fd, "R", 1)) 
+	    goto error_out;
+
+    if (!sc_write_security_token (worker->write_fd, token))
+	    goto error_out;
+
+    return TRUE;
+
+error_out:
+    g_set_error (error, SC_SECURITY_TOKEN_MONITOR_ERROR,
+		 SC_SECURITY_TOKEN_MONITOR_ERROR_REPORTING_EVENTS, 
+		 "%s", g_strerror (errno));
+    return FALSE;
+}
+
+static gboolean
+sc_security_token_monitor_worker_emit_security_token_inserted (ScSecurityTokenMonitorWorker  *worker, 
+							       ScSecurityToken               *token,
+							       GError                       **error)
+{
+    GError *write_error;
+
+    write_error = NULL;
+    sc_debug ("token '%s' inserted!", sc_security_token_get_name (token));
+    if (!sc_write_bytes (worker->write_fd, "I", 1)) 
+	    goto error_out;
+
+    if (!sc_write_security_token (worker->write_fd, token))
+	    goto error_out;
+
+    return TRUE;
+
+error_out:
+    g_set_error (error, SC_SECURITY_TOKEN_MONITOR_ERROR,
+		 SC_SECURITY_TOKEN_MONITOR_ERROR_REPORTING_EVENTS, 
+		 "%s", g_strerror (errno));
+    return FALSE;
+}
+
+static gboolean
+sc_security_token_monitor_worker_watch_for_and_process_event (ScSecurityTokenMonitorWorker *worker,
+							      GError                      **error)
+{
+    PK11SlotInfo *slot;
+    CK_SLOT_ID slot_id, *key;
+    gint slot_series, token_slot_series;
+    ScSecurityToken *token;
+    GError *processing_error;
+
+    sc_debug ("waiting for token 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)) {
+		    sc_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, SC_SECURITY_TOKEN_MONITOR_ERROR,
+			 SC_SECURITY_TOKEN_MONITOR_ERROR_WITH_NSS,
+			 _("encountered unexpected error while "
+			   "waiting for security token events"));
+	    return FALSE;
+    }
+
+    /* the slot id and series together uniquely identify a token.
+     * You can never have two tokens 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 token that we're currently
+     * tracking in the slot.
+     */
+    key = g_new (CK_SLOT_ID, 1);
+    *key = slot_id;
+    token = g_hash_table_lookup (worker->security_tokens, key);
+
+    if (token != NULL)
+	    token_slot_series = sc_security_token_get_slot_series (token);
+
+    if (PK11_IsPresent (slot)) {
+	    /* Now, check to see if their is a new token in the slot.
+	     * If there was a different token in the slot now than
+	     * there was before, then we need to emit a removed signal
+	     * for the old token (we don't want unpaired insertion events).
+	     */
+	    if ((token != NULL) && 
+		token_slot_series != slot_series) {
+		    if (!sc_security_token_monitor_worker_emit_security_token_removed (worker, token, &processing_error)) {
+			    g_propagate_error (error, processing_error);
+			    return FALSE;
+		    }
+	    }
+
+	    token = _sc_security_token_new (worker->module, 
+					    slot_id, slot_series);
+
+	    g_hash_table_replace (worker->security_tokens,
+				  key, token);
+	    key = NULL;
+
+	    if (!sc_security_token_monitor_worker_emit_security_token_inserted (worker, token, &processing_error)) {
+		    g_propagate_error (error, processing_error);
+		    return FALSE;
+	    }
+    } else {
+	    /* if we aren't tracking the token, just discard the event.
+	     * We don't want unpaired remove events.  Note on startup
+	     * NSS will generate an "insertion" event if a token is
+	     * already inserted in the slot.
+	     */
+	    if ((token != 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 - token_slot_series + 1)
+		     * 
+		     * Right now, i'm just doing it once.  
+		     */
+		    if ((slot_series - token_slot_series) > 1) {
+
+			    if (!sc_security_token_monitor_worker_emit_security_token_removed (worker, token, &processing_error)) {
+				    g_propagate_error (error, processing_error);
+				    return FALSE;
+			    }
+			    g_hash_table_remove (worker->security_tokens, key);
+
+			    token = _sc_security_token_new (worker->module, 
+							    slot_id, slot_series);
+			    g_hash_table_replace (worker->security_tokens,
+						  key, token);
+			    key = NULL;
+			    if (!sc_security_token_monitor_worker_emit_security_token_inserted (worker, token, &processing_error)) {
+				    g_propagate_error (error, processing_error);
+				    return FALSE;
+			    }
+		    }
+
+		    if (!sc_security_token_monitor_worker_emit_security_token_removed (worker, token, &processing_error)) {
+			    g_propagate_error (error, processing_error);
+			    return FALSE;
+		    }
+
+		    g_hash_table_remove (worker->security_tokens, key);
+		    token = NULL;
+	    } else {
+		    sc_debug ("got spurious remove event");
+	    }
+    }
+
+    g_free (key);
+    PK11_FreeSlot (slot);
+
+    return TRUE;
+}
+
+static gboolean
+sc_security_token_monitor_create_worker (ScSecurityTokenMonitor *monitor,
+					 gint *worker_fd, GPid *worker_pid)
+{
+    GPid child_pid;
+    gint write_fd, read_fd;
+
+    write_fd = -1;
+    read_fd = -1;
+    if (!sc_open_pipe (&write_fd, &read_fd))
+	    return FALSE;
+
+    child_pid = sc_fork_and_disown ();
+
+    if (child_pid < 0)
+	    return FALSE;
+
+    if (child_pid == 0) {
+	    GError *error;
+	    ScSecurityTokenMonitorWorker *worker;
+
+/* FIXME: Gotta figure out why this isn't working
+*/
+#ifdef SC_SECURITY_TOKEN_MONITOR_DRIVER_CAN_BE_RELOADED_AFTER_BEING_DESTROYED
+	    gint fds_to_keep_open[] = { -1, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, -1 };
+
+	    SECMOD_DestroyModule (monitor->priv->module);
+	    monitor->priv->module = NULL;
+
+	    NSS_Shutdown ();
+
+	    fds_to_keep_open[0] = write_fd;
+	    sc_close_open_fds (fds_to_keep_open);
+	    read_fd = -1;
+
+	    if (!sc_load_nss (&error)) {
+		    sc_debug ("could not load nss - %s", error->message);
+		    g_error_free (error);
+		    _exit (1);
+	    }
+#else
+	    g_array_append_val (monitor->priv->fds_to_close_on_fork, read_fd);
+	    /* Junky workaround to keep from leaking fds
+	     */
+	    sc_close_fds ((gint *) monitor->priv->fds_to_close_on_fork->data,
+			  monitor->priv->fds_to_close_on_fork->len);
+#endif
+	    error = NULL;
+
+	    worker = sc_security_token_monitor_worker_new (write_fd);
+
+	    sc_security_token_monitor_worker_die_with_parent (worker);
+
+	    worker->module = sc_load_driver (monitor->priv->module_path, &error);
+
+	    if (worker->module == NULL) {
+		    sc_debug ("could not load nss driver - %s", error->message);
+		    g_error_free (error);
+		    _exit (2);
+	    }
+
+	    while (sc_security_token_monitor_worker_watch_for_and_process_event (worker, &error));
+
+	    sc_debug ("could not process token event - %s", error->message);
+	    sc_security_token_monitor_worker_free (worker);
+
+	    _exit (0);
+    }
+
+    close (write_fd);
+
+#ifndef SC_SECURITY_TOKEN_MONITOR_DRIVER_CAN_BE_RELOADED_AFTER_BEING_DESTROYED
+    g_array_append_val (monitor->priv->fds_to_close_on_fork, read_fd);
+#endif
+
+    if (worker_pid)
+	    *worker_pid = child_pid;
+
+    if (worker_fd)
+	    *worker_fd = read_fd;
+
+    return TRUE;
+}
+
+#ifdef SC_SECURITY_TOKEN_MONITOR_ENABLE_TEST
+#include <glib.h>
+
+static GMainLoop *event_loop;
+static gboolean should_exit_on_next_remove = FALSE;
+
+static gboolean 
+on_timeout (ScSecurityTokenMonitor *monitor)
+{
+    GError *error;
+    g_print ("Re-enabling monitor.\n");
+
+    if (!sc_security_token_monitor_start (monitor, &error)) {
+	    g_warning ("could not start security token monitor - %s",
+		       error->message);
+	    g_error_free (error);
+	    return 1;
+    }
+    g_print ("Please re-insert security token\n");
+
+    should_exit_on_next_remove = TRUE;
+
+    return FALSE;
+}
+
+static void
+on_device_inserted (ScSecurityTokenMonitor * monitor,
+		    ScSecurityToken *token)
+{
+    g_print ("security token inserted!\n");
+    g_print ("Please remove it.\n");
+}
+
+static void
+on_device_removed (ScSecurityTokenMonitor * monitor,
+		   ScSecurityToken *token)
+{
+    g_print ("security token removed!\n");
+
+    if (should_exit_on_next_remove)
+	    g_main_loop_quit (event_loop);
+    else {
+	    g_print ("disabling monitor for 2 seconds\n");
+	    sc_security_token_monitor_stop (monitor);
+	    g_timeout_add (2000, (GSourceFunc) on_timeout, monitor);
+    }
+}
+
+int 
+main (int   argc, 
+      char *argv[])
+{
+    ScSecurityTokenMonitor *monitor;
+    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 'security token monitor' object...");
+    monitor = sc_security_token_monitor_new (NULL);
+    g_message ("'security token monitor' object created successfully");
+
+    g_signal_connect (monitor, "security-token-inserted",
+		      G_CALLBACK (on_device_inserted), NULL);
+
+    g_signal_connect (monitor, "security-token-removed",
+		      G_CALLBACK (on_device_removed), NULL);
+
+    g_message ("starting listener...");
+
+    error = NULL;
+    if (!sc_security_token_monitor_start (monitor, &error)) {
+	    g_warning ("could not start security token monitor - %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 'security token monitor' object...");
+    g_object_unref (monitor);
+    monitor = NULL;
+    g_message ("'security token monitor' object destroyed successfully");
+
+    return 0;
+}
+#endif
--- /dev/null	2007-05-21 09:34:56.803421964 -0400
+++ gdm-2.19.1/daemon/securitytoken.c	2007-05-21 16:04:51.000000000 -0400
@@ -0,0 +1,680 @@
+/* securitytoken.c - security token
+ * 
+ * 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.  
+ *
+ * TODO:     - doing this per project is a bad idea i think.
+ *             We should probably make this a system service 
+ *             and use dbus.
+ *
+ *           - We hardcode a driver right now.  We should probably
+ *             look up the default list and go from there. 
+ */
+#define SC_SECURITY_TOKEN_ENABLE_INTERNAL_API
+#include "securitytoken.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>
+
+#if defined (SC_SECURITY_TOKEN_ENABLE_TEST) || defined (SC_SECURITY_TOKEN_MONITOR_ENABLE_TEST)
+#define sc_debug(format, args...) g_printerr (format "\n", ##args)
+#define sc_warning(format, args...) g_printerr (format "\n", ##args)
+#else
+#define sc_debug(format, args...) 
+#define sc_warning(format, args...) 
+#endif
+
+struct _ScSecurityTokenPrivate {
+	SECMODModule *module;
+	ScSecurityTokenState state;
+
+	CK_SLOT_ID slot_id;
+	gint slot_series;
+
+	PK11SlotInfo *slot;
+	gchar *name;
+
+	CERTCertificate *signing_certificate;
+	CERTCertificate *encryption_certificate;
+};
+
+static void sc_security_token_finalize (GObject *object);
+static void sc_security_token_class_install_signals (ScSecurityTokenClass *token_class);
+static void sc_security_token_class_install_properties (ScSecurityTokenClass *token_class);
+static void sc_security_token_set_property (GObject       *object,
+					    guint          prop_id,
+					    const GValue  *value,
+					    GParamSpec    *pspec);
+static void sc_security_token_get_property (GObject     *object,
+					    guint        prop_id,
+					    GValue      *value,
+					    GParamSpec  *pspec);
+static void sc_security_token_set_name (ScSecurityToken *token, const gchar *name);
+static void sc_security_token_set_slot_id (ScSecurityToken *token, 
+					   gint             slot_id);
+static void sc_security_token_set_slot_series (ScSecurityToken *token, 
+					       gint             slot_series);
+static void sc_security_token_set_module (ScSecurityToken *token,
+					  SECMODModule    *module);
+
+static PK11SlotInfo *sc_security_token_find_slot_from_id (ScSecurityToken *token,
+							  gint slot_id);
+
+static PK11SlotInfo *sc_security_token_find_slot_from_token_name (ScSecurityToken *token,
+								  const gchar     *token_name);
+static gboolean sc_security_token_fetch_certificates (ScSecurityToken *token);
+
+
+#ifndef SC_SECURITY_TOKEN_DEFAULT_SLOT_ID 
+#define SC_SECURITY_TOKEN_DEFAULT_SLOT_ID ((gulong) -1)
+#endif
+
+#ifndef SC_SECURITY_TOKEN_DEFAULT_SLOT_SERIES
+#define SC_SECURITY_TOKEN_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 sc_security_token_signals[NUMBER_OF_SIGNALS];
+
+G_DEFINE_TYPE (ScSecurityToken, sc_security_token, G_TYPE_OBJECT);
+
+static void
+sc_security_token_class_init (ScSecurityTokenClass *token_class)
+{
+    GObjectClass *gobject_class;
+
+    gobject_class = G_OBJECT_CLASS (token_class);
+
+    gobject_class->finalize = sc_security_token_finalize;
+
+    sc_security_token_class_install_signals (token_class);
+    sc_security_token_class_install_properties (token_class);
+
+    g_type_class_add_private (token_class,
+			      sizeof (ScSecurityTokenPrivate));
+}
+
+static void
+sc_security_token_class_install_signals (ScSecurityTokenClass *token_class)
+{
+    GObjectClass *object_class;
+
+    object_class = G_OBJECT_CLASS (token_class);
+
+    sc_security_token_signals[INSERTED] =
+	    g_signal_new ("inserted",
+			  G_OBJECT_CLASS_TYPE (object_class),
+			  G_SIGNAL_RUN_LAST,
+			  G_STRUCT_OFFSET (ScSecurityTokenClass,
+					   inserted), 
+			  NULL, NULL, g_cclosure_marshal_VOID__VOID, 
+			  G_TYPE_NONE, 0);
+    token_class->inserted = NULL;
+
+    sc_security_token_signals[REMOVED] =
+	    g_signal_new ("removed",
+			  G_OBJECT_CLASS_TYPE (object_class),
+			  G_SIGNAL_RUN_LAST,
+			  G_STRUCT_OFFSET (ScSecurityTokenClass,
+					   removed), 
+			  NULL, NULL, g_cclosure_marshal_VOID__VOID, 
+			  G_TYPE_NONE, 0);
+    token_class->removed = NULL;
+}
+
+static void
+sc_security_token_class_install_properties (ScSecurityTokenClass *token_class)
+{
+    GObjectClass *object_class;
+    GParamSpec *param_spec;
+
+    object_class = G_OBJECT_CLASS (token_class);
+    object_class->set_property = sc_security_token_set_property;
+    object_class->get_property = sc_security_token_get_property;
+
+    param_spec = g_param_spec_ulong ("slot-id", _("Slot ID"),
+				   _("The slot the token is in"),
+				   1, G_MAXULONG,
+				   SC_SECURITY_TOKEN_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 token identifier"),
+				   -1, G_MAXINT,
+				   SC_SECURITY_TOKEN_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"),
+				       _("security token driver"),
+				       G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
+    g_object_class_install_property (object_class, PROP_MODULE, param_spec);
+}
+
+static void 
+sc_security_token_set_property (GObject       *object,
+				guint          prop_id,
+				const GValue  *value,
+				GParamSpec    *pspec)
+{
+    ScSecurityToken *token = SC_SECURITY_TOKEN (object);
+
+    switch (prop_id)
+    {
+	    case PROP_NAME:
+		    sc_security_token_set_name (token, g_value_get_string (value));
+		    break;
+
+	    case PROP_SLOT_ID:
+		    sc_security_token_set_slot_id (token, 
+						   g_value_get_ulong (value));
+		    break;
+
+	    case PROP_SLOT_SERIES:
+		    sc_security_token_set_slot_series (token, 
+						       g_value_get_int (value));
+		    break;
+
+	    case PROP_MODULE:
+		    sc_security_token_set_module (token, 
+						  (SECMODModule *)
+						  g_value_get_pointer (value));
+		    break;
+
+	    default:
+		    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+CK_SLOT_ID
+sc_security_token_get_slot_id (ScSecurityToken *token)
+{
+    return token->priv->slot_id;
+}
+
+ScSecurityTokenState 
+sc_security_token_get_state (ScSecurityToken *token)
+{
+    return token->priv->state;
+}
+
+gchar *
+sc_security_token_get_name (ScSecurityToken *token)
+{
+    return g_strdup (token->priv->name);
+}
+
+gboolean
+sc_security_token_is_login_token (ScSecurityToken *token)
+{
+    const gchar *login_token_name;
+    login_token_name = g_getenv ("PKCS11_LOGIN_TOKEN_NAME");
+
+    if ((login_token_name == NULL) || (token->priv->name == NULL))
+	    return FALSE;
+
+    if (strcmp (token->priv->name, login_token_name) == 0)
+	    return TRUE;
+
+    return FALSE;
+}
+
+static void 
+sc_security_token_get_property (GObject    *object,
+				guint        prop_id,
+				GValue      *value,
+				GParamSpec  *pspec)
+{
+    ScSecurityToken *token = SC_SECURITY_TOKEN (object);
+
+    switch (prop_id)
+    {
+	    case PROP_NAME:
+		    g_value_take_string (value, 
+					 sc_security_token_get_name (token));
+		    break;
+
+	    case PROP_SLOT_ID:
+		    g_value_set_ulong (value, 
+				       (gulong) sc_security_token_get_slot_id (token));
+		    break;
+
+	    case PROP_SLOT_SERIES:
+		    g_value_set_int (value, 
+				     sc_security_token_get_slot_series (token));
+		    break;
+
+	    default:
+		    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+sc_security_token_set_name (ScSecurityToken *token,
+			    const gchar     *name)
+{
+    if (name == NULL)
+	    return;
+
+    if ((token->priv->name == NULL) ||
+	(strcmp (token->priv->name, name) != 0)) {
+	    g_free (token->priv->name);
+	    token->priv->name = g_strdup (name);
+
+	    if (token->priv->slot == NULL) {
+		    token->priv->slot = sc_security_token_find_slot_from_token_name (token,
+										     token->priv->name);
+
+		    if (token->priv->slot != NULL) {
+			    gint slot_id, slot_series;
+
+			    slot_id = PK11_GetSlotID (token->priv->slot);
+			    if (slot_id != token->priv->slot_id)
+				    sc_security_token_set_slot_id (token, slot_id);
+
+			    slot_series = PK11_GetSlotSeries (token->priv->slot);
+			    if (slot_series != token->priv->slot_series)
+				    sc_security_token_set_slot_series (token, slot_series);
+
+			    _sc_security_token_set_state (token, SC_SECURITY_TOKEN_STATE_INSERTED);
+		    } else {
+			    _sc_security_token_set_state (token, SC_SECURITY_TOKEN_STATE_REMOVED);
+		    }
+	    }
+
+
+	    g_object_notify (G_OBJECT (token), "name");
+    }
+}
+
+static void
+sc_security_token_set_slot_id (ScSecurityToken *token,
+			       gint             slot_id)
+{
+    if (token->priv->slot_id != slot_id)
+    {
+	    token->priv->slot_id = slot_id;
+
+	    if (token->priv->slot == NULL) {
+		    token->priv->slot = sc_security_token_find_slot_from_id (token,
+									     token->priv->slot_id);
+
+		    if (token->priv->slot != NULL) {
+			    const gchar *token_name;
+
+			    token_name = PK11_GetTokenName (token->priv->slot);
+			    if ((token->priv->name == NULL) || 
+				((token_name != NULL) &&
+				(strcmp (token_name, token->priv->name) != 0)))
+				    sc_security_token_set_name (token, token_name);
+
+			    _sc_security_token_set_state (token, SC_SECURITY_TOKEN_STATE_INSERTED);
+		    } else {
+			    _sc_security_token_set_state (token, SC_SECURITY_TOKEN_STATE_REMOVED);
+		    }
+	    }
+
+	    g_object_notify (G_OBJECT (token), "slot-id");
+    }
+}
+
+static void
+sc_security_token_set_slot_series (ScSecurityToken *token,
+				   gint             slot_series)
+{
+    if (token->priv->slot_series != slot_series)
+    {
+	    token->priv->slot_series = slot_series;
+	    g_object_notify (G_OBJECT (token), "slot-series");
+    }
+}
+
+static void
+sc_security_token_set_module (ScSecurityToken *token,
+			      SECMODModule    *module)
+{
+    gboolean should_notify;
+
+    if (token->priv->module != module)
+	    should_notify = TRUE;
+    else
+	    should_notify = FALSE;
+
+    if (token->priv->module != NULL) {
+	    SECMOD_DestroyModule (token->priv->module);
+	    token->priv->module = NULL;
+    }
+
+    if (module != NULL)
+	    token->priv->module = SECMOD_ReferenceModule (module);
+
+    if (should_notify)
+	    g_object_notify (G_OBJECT (token), "module");
+}
+
+gint 
+sc_security_token_get_slot_series (ScSecurityToken *token)
+{
+    return token->priv->slot_series;
+}
+
+static void
+sc_security_token_init (ScSecurityToken *token)
+{
+
+    sc_debug ("initializing security token ");
+
+    token->priv = G_TYPE_INSTANCE_GET_PRIVATE (token,
+					       SC_TYPE_SECURITY_TOKEN,
+					       ScSecurityTokenPrivate);
+
+    if (token->priv->slot != NULL)
+	    token->priv->name = g_strdup (PK11_GetTokenName (token->priv->slot));
+}
+
+static void sc_security_token_finalize (GObject *object)
+{
+    ScSecurityToken *token;
+    GObjectClass *gobject_class;
+
+    token = SC_SECURITY_TOKEN (object);
+
+    g_free (token->priv->name);
+
+    sc_security_token_set_module (token, NULL);
+
+    gobject_class =
+	    G_OBJECT_CLASS (sc_security_token_parent_class);
+
+    gobject_class->finalize (object);
+}
+
+GQuark sc_security_token_error_quark (void)
+{
+    static GQuark error_quark = 0;
+
+    if (error_quark == 0)
+	    error_quark = g_quark_from_static_string ("sc-security-token-error-quark");
+
+    return error_quark;
+}
+
+ScSecurityToken *
+_sc_security_token_new (SECMODModule *module,
+			CK_SLOT_ID    slot_id,
+			gint          slot_series)
+{
+    ScSecurityToken *token;
+
+    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);
+
+    token = SC_SECURITY_TOKEN (g_object_new (SC_TYPE_SECURITY_TOKEN,
+					     "module", module,
+					     "slot-id", (gulong) slot_id,
+					     "slot-series", slot_series,
+					     NULL));
+    return token;
+}
+
+ScSecurityToken *
+_sc_security_token_new_from_name (SECMODModule *module,
+				  const gchar  *name)
+{
+    ScSecurityToken *token;
+
+    g_return_val_if_fail (module != NULL, NULL);
+    g_return_val_if_fail (name != NULL, NULL);
+
+    token = SC_SECURITY_TOKEN (g_object_new (SC_TYPE_SECURITY_TOKEN,
+					     "module", module,
+					     "name", name,
+					     NULL));
+    return token;
+}
+
+void 
+_sc_security_token_set_state (ScSecurityToken      *token,
+			      ScSecurityTokenState  state)
+{
+    /* sc_security_token_fetch_certificates (token); */
+    if (token->priv->state != state)
+    {
+	    token->priv->state = state;
+
+	    if (state == SC_SECURITY_TOKEN_STATE_INSERTED) {
+		    g_signal_emit (token, sc_security_token_signals[INSERTED], 0);
+	    } else if (state == SC_SECURITY_TOKEN_STATE_REMOVED)
+		    g_signal_emit (token, sc_security_token_signals[REMOVED], 0);
+	    else
+		    g_assert_not_reached ();
+    }
+}
+
+/* So we could conceivably make the closure data a pointer to the token
+ * 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 *
+sc_security_token_password_handler (PK11SlotInfo *slot, 
+				    PRBool        is_retrying, 
+				    const gchar  *password)
+{
+    if (is_retrying)
+	    return NULL;
+
+    return password != NULL? PL_strdup (password): NULL;
+}
+
+gboolean
+sc_security_token_unlock (ScSecurityToken *token,
+			  const gchar     *password)
+{   
+    SECStatus status;
+
+    PK11_SetPasswordFunc ((PK11PasswordFunc) sc_security_token_password_handler);
+
+    /* we pass PR_TRUE to load certificates
+     */
+    status = PK11_Authenticate (token->priv->slot, PR_TRUE, (gpointer) password);
+
+    if (status != SECSuccess) {
+	    sc_debug ("could not unlock token - %d", status);
+	    return FALSE;
+    }
+    return TRUE;
+}
+
+static PK11SlotInfo *
+sc_security_token_find_slot_from_token_name (ScSecurityToken *token,
+					     const gchar     *token_name)
+{
+    int i;
+
+    for (i = 0; i < token->priv->module->slotCount; i++) {
+	    const gchar *slot_token_name;
+
+	    slot_token_name = PK11_GetTokenName (token->priv->module->slots[i]);
+
+	    if ((slot_token_name != NULL) &&
+		(strcmp (slot_token_name, token_name) == 0))
+		    return token->priv->module->slots[i];
+    }
+
+    return NULL;
+}
+
+static PK11SlotInfo *
+sc_security_token_find_slot_from_id (ScSecurityToken *token,
+                                     gint slot_id)
+{
+    int i;
+
+    for (i = 0; i < token->priv->module->slotCount; i++)
+	    if (PK11_GetSlotID (token->priv->module->slots[i]) == slot_id)
+		    return token->priv->module->slots[i];
+
+    return NULL;
+}
+
+static gboolean
+sc_security_token_fetch_certificates (ScSecurityToken *token)
+{
+    PK11SlotInfo *slot;
+    CERTCertList *certificates;
+    CERTCertListNode *node;
+    SECStatus status;
+    int i;
+
+    sc_security_token_unlock (token, "0000");
+
+    sc_debug ("fetching certificates for token in slot %lu",
+	      token->priv->slot_id);
+
+    slot = sc_security_token_find_slot_from_id (token,
+						token->priv->slot_id);
+
+    g_assert (PK11_GetSlotID (slot) == token->priv->slot_id);
+
+    if (i == token->priv->module->slotCount) {
+	    sc_debug ("could not find slot %lu", token->priv->slot_id);
+	    return FALSE;
+    }
+
+    certificates = PK11_ListCertsInSlot (slot);
+
+    sc_debug ("filtering out non-user certificates");
+    if (CERT_FilterCertListForUserCerts (certificates) != SECSuccess) {
+	    CERT_DestroyCertList (certificates);
+	    sc_debug ("could not filter out non-user certificates");
+	    return FALSE;
+    }
+
+    for (node = CERT_LIST_HEAD (certificates); 
+	 !CERT_LIST_END (node, certificates);
+	 node = CERT_LIST_NEXT(node)) {
+
+	    SECCertificateUsage cert_usages;
+
+	    sc_debug ("verifying certificate for use");
+	    status = CERT_VerifyCertificateNow (NULL, node->cert, TRUE, 
+						0, NULL, &cert_usages);
+
+	    if (status != SECSuccess) {
+		    sc_debug ("could not be verified, skipping...");
+		    continue;
+	    }
+
+	    sc_debug ("got cert with usages 0x%lx", (gulong) cert_usages);
+
+	    if (token->priv->encryption_certificate == NULL) {
+
+		    sc_debug ("checking if certificate can be used for data "
+			      "encryption");
+		    status = CERT_CheckCertUsage (node->cert, 
+						  KU_DATA_ENCIPHERMENT);
+
+		    if (status == SECSuccess) {
+			    token->priv->encryption_certificate = 
+				    CERT_DupCertificate (node->cert);
+		    } else {
+			    sc_debug ("certificate can not be used for encryption");
+		    }
+	    }
+
+	    if (token->priv->signing_certificate == NULL) {
+
+		    sc_debug ("checking if certificate can be used for data "
+			      "signing");
+		    status = CERT_CheckCertUsage (node->cert, 
+						  KU_DIGITAL_SIGNATURE);
+
+		    if (status == SECSuccess) {
+			    token->priv->signing_certificate = 
+				    CERT_DupCertificate (node->cert);
+		    } else {
+			    sc_debug ("certificate can not be used for signing things");
+		    }
+	    }
+    }
+    return TRUE;
+}
+
+#ifdef SC_SECURITY_TOKEN_ENABLE_TEST
+#include <glib.h>
+
+static GMainLoop *event_loop;
+
+int 
+main (int   argc, 
+      char *argv[])
+{
+    ScSecurityToken *token;
+    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 'security token' object...");
+    token = _sc_security_token_new (NULL, 1, 1);
+    g_message ("'security token' object created successfully");
+
+    g_message ("destroying previously created 'security token' object...");
+    g_object_unref (token);
+    token = NULL;
+    g_message ("'security token' object destroyed successfully");
+
+    return 0;
+}
+#endif