gdm/gdm-2.19.1-security-tokens.patch
Matthias Clasen 81bc226e49 another try
2007-05-21 19:57:47 +00:00

2860 lines
85 KiB
Diff

--- gdm-2.19.1/configure.ac.security-tokens 2007-05-21 15:52:58.000000000 -0400
+++ gdm-2.19.1/configure.ac 2007-05-21 15:52:58.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 15:52:58.000000000 -0400
@@ -0,0 +1,3 @@
+[SecurityTokens]
+Enable=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 15:52:58.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 15:52:58.000000000 -0400
+++ gdm-2.19.1/config/gdm.conf.in 2007-05-21 15:52:58.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 15:52:58.000000000 -0400
+++ gdm-2.19.1/daemon/gdm.c 2007-05-21 15:53:26.000000000 -0400
@@ -71,6 +71,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 +95,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 +1793,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 +4350,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_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 15:52:58.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 15:52:58.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 15:52:58.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 15:52:58.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 15:52:58.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