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