From 1335c73f2ff02d52242a6b3d94152d88542d7e40 Mon Sep 17 00:00:00 2001 From: Ray Strode Date: Mar 20 2007 05:46:34 +0000 Subject: - update and reenable security token patch --- diff --git a/gdm-2.18.0-security-tokens.patch b/gdm-2.18.0-security-tokens.patch new file mode 100644 index 0000000..276bb88 --- /dev/null +++ b/gdm-2.18.0-security-tokens.patch @@ -0,0 +1,2880 @@ +--- gdm-2.18.0/config/gdm.conf.in.security-tokens 2007-03-20 01:21:20.000000000 -0400 ++++ gdm-2.18.0/config/gdm.conf.in 2007-03-20 01:21:24.000000000 -0400 +@@ -200,6 +200,10 @@ + # 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. +--- /dev/null 2007-03-19 18:45:10.978423391 -0400 ++++ gdm-2.18.0/config/securitytokens.conf.in 2007-03-20 01:21:24.000000000 -0400 +@@ -0,0 +1,3 @@ ++[SecurityTokens] ++Enable=true ++#Driver=@libdir@/pkcs11/libcoolkeypk11.so +--- gdm-2.18.0/config/Makefile.am.security-tokens 2007-03-11 17:46:16.000000000 -0400 ++++ gdm-2.18.0/config/Makefile.am 2007-03-20 01:22:40.000000000 -0400 +@@ -34,9 +34,11 @@ + 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' \ +@@ -72,6 +74,31 @@ + 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 + +@@ -100,7 +127,7 @@ + $(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); \ +@@ -133,6 +160,7 @@ + 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.18.0/configure.ac.security-tokens 2007-03-20 01:21:24.000000000 -0400 ++++ gdm-2.18.0/configure.ac 2007-03-20 01:21:24.000000000 -0400 +@@ -20,6 +20,7 @@ + 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 @@ + AC_SUBST(VICIOUS_CFLAGS) + AC_SUBST(VICIOUS_LIBS) + +-PKG_CHECK_MODULES(DAEMON, gtk+-2.0 >= $GTK_REQUIRED dbus-glib-1 >= $DBUS_REQUIRED) ++PKG_CHECK_MODULES(DAEMON, gtk+-2.0 >= $GTK_REQUIRED dbus-glib-1 >= $DBUS_REQUIRED nss >= $NSS_REQUIRED) + AC_SUBST(DAEMON_CFLAGS) + AC_SUBST(DAEMON_LIBS) + +--- /dev/null 2007-03-19 18:45:10.978423391 -0400 ++++ gdm-2.18.0/daemon/securitytokenmonitor.h 2007-03-20 01:21:24.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 ++#include ++ ++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.18.0/daemon/gdm.c.security-tokens 2007-03-20 01:21:24.000000000 -0400 ++++ gdm-2.18.0/daemon/gdm.c 2007-03-20 01:21:24.000000000 -0400 +@@ -68,6 +68,8 @@ + #include "filecheck.h" + #include "gdmconfig.h" + #include "errorgui.h" ++#include "securitytokenmonitor.h" ++#include "securitytoken.h" + + #define DYNAMIC_ADD 0 + #define DYNAMIC_RELEASE 1 +@@ -77,6 +79,7 @@ + #include + #endif /* HAVE_LOGINDEVPERM */ + ++ + extern GSList *displays; + + /* Local functions */ +@@ -86,6 +89,10 @@ + 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); +@@ -159,7 +166,6 @@ + + static gboolean monte_carlo_sqrt2 = FALSE; + +- + /* + * lookup display number if the display number is + * exists then clear the remove flag and return TRUE +@@ -1546,6 +1552,8 @@ + + g_type_init (); + ++ g_type_init (); ++ + ctx = g_option_context_new (_("- The GNOME login manager")); + g_option_context_add_main_entries (ctx, options, _("main options")); + +@@ -1788,6 +1796,8 @@ + 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. +@@ -3996,4 +4006,85 @@ + } + } + ++static void ++gdm_reset_local_displays (void) ++{ ++ GSList *li; ++ ++ 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_KEY_ENABLED ++#define GDM_SECURITY_TOKENS_KEY_ENABLED "SecurityTokens/Enabled=true" ++#endif ++ ++#ifndef GDM_SECURITY_TOKENS_KEY_DRIVER ++#define GDM_SECURITY_TOKENS_KEY_DRIVER "SecurityTokens/Driver" ++#endif ++ ++static void ++gdm_watch_for_security_tokens (void) ++{ ++ GError *error; ++ ScSecurityTokenMonitor *monitor; ++ gchar *driver; ++ VeConfig *cfg; ++ ++ cfg = ve_config_new (GDM_SECURITY_TOKENS_CONF); ++ ++ if (!ve_config_get_bool (cfg, GDM_SECURITY_TOKENS_KEY_ENABLED)) { ++ gdm_debug ("security token support is not enabled"); ++ goto out; ++ } ++ ++ gdm_debug ("watching for security token insertion and removal events"); ++ ++ driver = ve_config_get_string (cfg, GDM_SECURITY_TOKENS_KEY_DRIVER); ++ gdm_debug ("security tokens driver is set to '%s'", ++ ve_string_empty (driver)? "" : 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: ++ ve_config_destroy (cfg); ++} + /* EOF */ +--- /dev/null 2007-03-19 18:45:10.978423391 -0400 ++++ gdm-2.18.0/daemon/securitytoken.h 2007-03-20 01:21:24.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 ++#include ++ ++#include ++ ++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-03-19 18:45:10.978423391 -0400 ++++ gdm-2.18.0/daemon/securitytoken.c 2007-03-20 01:21:24.000000000 -0400 +@@ -0,0 +1,680 @@ ++/* securitytoken.c - 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. ++ * ++ * 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 ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++ ++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 +--- gdm-2.18.0/daemon/Makefile.am.security-tokens 2007-03-11 17:46:16.000000000 -0400 ++++ gdm-2.18.0/daemon/Makefile.am 2007-03-20 01:21:24.000000000 -0400 +@@ -9,6 +9,7 @@ + -DAUTHDIR=\"$(authdir)\" \ + -DBINDIR=\"$(bindir)\" \ + -DDATADIR=\"$(datadir)\" \ ++ -DSYSCONFDIR=\"$(sysconfdir)\" \ + -DDMCONFDIR=\"$(dmconfdir)\" \ + -DGDMCONFDIR=\"$(gdmconfdir)\" \ + -DGDMLOCALEDIR=\"$(gdmlocaledir)\" \ +@@ -72,7 +73,11 @@ + gdm-net.c \ + gdm-net.h \ + getvt.c \ +- getvt.h ++ getvt.h \ ++ securitytoken.c \ ++ securitytoken.h \ ++ securitytokenmonitor.c \ ++ securitytokenmonitor.h + + EXTRA_gdm_binary_SOURCES = verify-pam.c verify-crypt.c verify-shadow.c + +--- /dev/null 2007-03-19 18:45:10.978423391 -0400 ++++ gdm-2.18.0/daemon/securitytokenmonitor.c 2007-03-20 01:21:24.000000000 -0400 +@@ -0,0 +1,1743 @@ ++/* securitytokenmonitor.c - 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. ++ * ++ * 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 ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#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 ++ ++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 diff --git a/gdm.spec b/gdm.spec index 4c972ee..714b540 100644 --- a/gdm.spec +++ b/gdm.spec @@ -16,7 +16,7 @@ Summary: The GNOME Display Manager Name: gdm Version: 2.18.0 -Release: 3%{?dist} +Release: 4%{?dist} Epoch: 1 License: LGPL/GPL Group: User Interface/X @@ -43,7 +43,7 @@ Patch12: gdm-2.17.6-audit-login.patch # http://bugzilla.gnome.org/show_bug.cgi?id=347798 Patch19: gdm-2.17.7-move-default-message.patch Patch20: gdm-2.17.7-reset-pam.patch -#Patch21: gdm-2.17.3-security-tokens.patch +Patch21: gdm-2.18.0-security-tokens.patch # http://bugzilla.gnome.org/show_bug.cgi?id=347871 Patch24: gdm-2.16.0-wtmp.patch @@ -140,7 +140,7 @@ Extra icons / faces for the GNOME Display Manager. %patch12 -p1 -b .audit-login %patch19 -p1 -b .move-default-message %patch20 -p1 -b .reset-pam -#%patch21 -p1 -b .security-tokens +%patch21 -p1 -b .security-tokens %patch24 -p1 -b .wtmp %patch25 -p1 -b .indic-langs %patch28 -p1 -b .desensitize-entry @@ -365,6 +365,9 @@ fi %{_datadir}/pixmaps/faces/extras/*.jpg %changelog +* Mon Mar 19 2007 Ray Strode - 1:2.18.0-4 +- update and reenable security token patch + * Mon Mar 19 2007 David Zeuthen - 1:2.18.0-3 - Also pass AT's to the session from the plain greeter (#232518) - New faces including new subpackage gdm-extra-faces