diff --git a/pkcs11/gck/Makefile.am b/pkcs11/gck/Makefile.am
index f264a53..730f416 100644
--- a/pkcs11/gck/Makefile.am
+++ b/pkcs11/gck/Makefile.am
@@ -40,6 +40,7 @@ libgck_la_SOURCES = \
 	gck-session.c gck-session.h \
 	gck-sexp.c gck-sexp.h \
 	gck-store.c gck-store.h \
+	gck-timer.c gck-timer.h \
 	gck-transaction.c gck-transaction.h \
 	gck-types.h \
 	gck-util.c gck-util.h \
diff --git a/pkcs11/gck/gck-attributes.c b/pkcs11/gck/gck-attributes.c
index ad1b9de..f5c0102 100644
--- a/pkcs11/gck/gck-attributes.c
+++ b/pkcs11/gck/gck-attributes.c
@@ -28,6 +28,71 @@
 #include <stdio.h>
 #include <string.h>
 
+#ifndef HAVE_TIMEGM
+static time_t
+timegm (struct tm *t)
+{
+	time_t tl, tb;
+	struct tm *tg;
+
+	tl = mktime (t);
+	if (tl == -1)
+	{
+		t->tm_hour--;
+		tl = mktime (t);
+		if (tl == -1)
+			return -1; /* can't deal with output from strptime */
+		tl += 3600;
+	}
+	tg = gmtime (&tl);
+	tg->tm_isdst = 0;
+	tb = mktime (tg);
+	if (tb == -1)
+	{
+		tg->tm_hour--;
+		tb = mktime (tg);
+		if (tb == -1)
+			return -1; /* can't deal with output from gmtime */
+		tb += 3600;
+	}
+	return (tl - (tb - tl));
+}
+#endif // NOT_HAVE_TIMEGM
+
+CK_RV
+gck_attribute_get_time (CK_ATTRIBUTE_PTR attr, glong *when)
+{
+	struct tm tm;
+	gchar buf[15];
+	time_t time;
+
+	g_return_val_if_fail (attr, CKR_GENERAL_ERROR);
+	g_return_val_if_fail (when, CKR_GENERAL_ERROR);
+
+	if (attr->ulValueLen == 0) {
+		*when = (glong)-1;
+		return CKR_OK;
+	}
+
+	if (!attr->pValue || attr->ulValueLen != 16)
+		return CKR_ATTRIBUTE_VALUE_INVALID;
+
+	memset (&tm, 0, sizeof (tm));
+	memcpy (buf, attr->pValue, 14);
+	buf[14] = 0;
+
+	if (!strptime(buf, "%Y%m%d%H%M%S", &tm))
+		return CKR_ATTRIBUTE_VALUE_INVALID;
+
+	/* Convert to seconds since epoch */
+	time = timegm (&tm);
+	if (time < 0)
+		return CKR_ATTRIBUTE_VALUE_INVALID;
+
+	*when = time;
+	return CKR_OK;
+}
+
 CK_RV
 gck_attribute_set_bool (CK_ATTRIBUTE_PTR attr, CK_BBOOL value)
 {
@@ -80,6 +145,32 @@ gck_attribute_set_date (CK_ATTRIBUTE_PTR attr, time_t time)
 		
 	return gck_attribute_set_data (attr, &date, sizeof (date));
 }
+
+CK_RV
+gck_attribute_set_time (CK_ATTRIBUTE_PTR attr, glong when)
+{
+	struct tm tm;
+	gchar buf[20];
+
+	/* 'Empty' time as defined in PKCS#11 */
+	if (when == (glong)-1)
+		return gck_attribute_set_data (attr, NULL, 0);
+
+	if (!attr->pValue) {
+		attr->ulValueLen = 16;
+		return CKR_OK;
+	}
+
+	time_t time = when;
+	if (!gmtime_r (&time, &tm))
+		g_return_val_if_reached (CKR_GENERAL_ERROR);
+
+	if (!strftime(buf, sizeof (buf), "%Y%m%d%H%M%S00", &tm))
+		g_return_val_if_reached (CKR_GENERAL_ERROR);
+
+	return gck_attribute_set_data (attr, buf, 16);
+}
+
 CK_RV
 gck_attribute_set_data (CK_ATTRIBUTE_PTR attr, gconstpointer value, gsize n_value)
 {
@@ -173,6 +264,12 @@ gck_attribute_consumed (CK_ATTRIBUTE_PTR attr)
 }
 
 void
+gck_attribute_consume (CK_ATTRIBUTE_PTR attr)
+{
+	attr->type = (CK_ULONG)-1;
+}
+
+void
 gck_attributes_consume (CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs, ...)
 {
 	CK_ATTRIBUTE_TYPE type;
diff --git a/pkcs11/gck/gck-attributes.h b/pkcs11/gck/gck-attributes.h
index 81b84a7..cfbb77d 100644
--- a/pkcs11/gck/gck-attributes.h
+++ b/pkcs11/gck/gck-attributes.h
@@ -28,6 +28,9 @@
 
 #include "pkcs11/pkcs11.h"
 
+CK_RV                 gck_attribute_get_time                           (CK_ATTRIBUTE_PTR attr,
+                                                                        glong *when);
+
 CK_RV                 gck_attribute_set_bool                           (CK_ATTRIBUTE_PTR attr,
                                                                         CK_BBOOL value);
 
@@ -40,6 +43,9 @@ CK_RV                 gck_attribute_set_string                         (CK_ATTRI
 CK_RV                 gck_attribute_set_date                           (CK_ATTRIBUTE_PTR attr,
                                                                         time_t when);
 
+CK_RV                 gck_attribute_set_time                           (CK_ATTRIBUTE_PTR attr,
+                                                                        glong when);
+
 CK_RV                 gck_attribute_set_data                           (CK_ATTRIBUTE_PTR attr,
                                                                         gconstpointer value,
                                                                         gsize n_value);
@@ -57,6 +63,8 @@ guint                 gck_attribute_hash                               (gconstpo
 gboolean              gck_attribute_equal                              (gconstpointer a,
                                                                         gconstpointer b);
 
+void                  gck_attribute_consume                            (CK_ATTRIBUTE_PTR attr);
+
 gboolean              gck_attribute_consumed                           (CK_ATTRIBUTE_PTR attr);
 
 
diff --git a/pkcs11/gck/gck-module-ep.h b/pkcs11/gck/gck-module-ep.h
index 09b5564..7ef2216 100644
--- a/pkcs11/gck/gck-module-ep.h
+++ b/pkcs11/gck/gck-module-ep.h
@@ -28,7 +28,7 @@
 #include <unistd.h>
 
 /* Forward declaration, this must be defined manually or using GCK_DEFINE_MODULE */
-static GckModule* gck_module_instantiate (CK_C_INITIALIZE_ARGS_PTR args);
+static GckModule* gck_module_instantiate (CK_C_INITIALIZE_ARGS_PTR args, GMutex *mutex);
 
 static GckModule *pkcs11_module = NULL;
 static pid_t pkcs11_module_pid = 0;
@@ -73,7 +73,7 @@ gck_C_Initialize (CK_VOID_PTR init_args)
 			else 
 				pkcs11_module_pid = pid;
 		} else {
-			pkcs11_module = gck_module_instantiate (args);
+			pkcs11_module = gck_module_instantiate (args, g_static_mutex_get_mutex (&pkcs11_module_mutex));
 			if (!pkcs11_module) {
 				g_warning ("module could not be instantiated");
 				rv = CKR_GENERAL_ERROR;
diff --git a/pkcs11/gck/gck-module.c b/pkcs11/gck/gck-module.c
index b6a06cb..32cdcf3 100644
--- a/pkcs11/gck/gck-module.c
+++ b/pkcs11/gck/gck-module.c
@@ -33,6 +33,7 @@
 #include "gck-private-key.h"
 #include "gck-public-key.h"
 #include "gck-session.h"
+#include "gck-timer.h"
 #include "gck-transaction.h"
 #include "gck-util.h"
 
@@ -40,7 +41,8 @@ enum {
 	PROP_0,
 	PROP_MANAGER,
 	PROP_WRITE_PROTECTED,
-	PROP_INITIALIZE_ARGS
+	PROP_INITIALIZE_ARGS,
+	PROP_MUTEX
 };
 
 struct _GckModulePrivate {
@@ -50,6 +52,7 @@ struct _GckModulePrivate {
 	gint handle_counter;                    /* Constantly incrementing counter for handles and the like */
 	GArray *factories;                      /* Various registered object factories */
 	gboolean factories_sorted;              /* Whether we need to sort the object factories */
+	GMutex *mutex;                          /* The mutex controlling entry to this module */
 };
 
 typedef struct _VirtualSlot {
@@ -128,6 +131,9 @@ static const MechanismAndInfo mechanism_list[] = {
 	{ CKM_DSA, { 512, 1024, CKF_SIGN | CKF_VERIFY } }
 };
 
+/* Hidden function that you should not use */
+GMutex* _gck_module_get_scary_mutex_that_you_should_not_touch (GckModule *self);
+
 /* -----------------------------------------------------------------------------
  * INTERNAL 
  */
@@ -435,6 +441,8 @@ gck_module_constructor (GType type, guint n_props, GObjectConstructParam *props)
 static void
 gck_module_init (GckModule *self)
 {
+	gck_timer_initialize ();
+
 	self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCK_TYPE_MODULE, GckModulePrivate);
 	self->pv->token_manager = g_object_new (GCK_TYPE_MANAGER, "for-token", TRUE, NULL);
 	self->pv->sessions_by_handle = g_hash_table_new_full (gck_util_ulong_hash, gck_util_ulong_equal, 
@@ -486,6 +494,8 @@ gck_module_finalize (GObject *obj)
 	g_array_free (self->pv->factories, TRUE);
 	self->pv->factories = NULL;
 
+	gck_timer_shutdown ();
+
 	G_OBJECT_CLASS (gck_module_parent_class)->finalize (obj);
 }
 
@@ -502,6 +512,10 @@ gck_module_set_property (GObject *obj, guint prop_id, const GValue *value,
 		if (args != NULL && args->pReserved != NULL) 
 			parse_arguments (self, args->pReserved);
 		break;
+	case PROP_MUTEX:
+		self->pv->mutex = g_value_get_pointer (value);
+		g_return_if_fail (self->pv->mutex);
+		break;
 	default:
 		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
 		break;
@@ -564,6 +578,10 @@ gck_module_class_init (GckModuleClass *klass)
 	g_object_class_install_property (gobject_class, PROP_INITIALIZE_ARGS,
 	           g_param_spec_pointer ("initialize-args", "Initialize Args", "Arguments passed to C_Initialize", 
 	                                 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property (gobject_class, PROP_MUTEX,
+	           g_param_spec_pointer ("mutex", "Mutex", "Module mutex",
+	                                 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY));
 }
 
 /* -----------------------------------------------------------------------------
@@ -731,6 +749,18 @@ gck_module_find_factory (GckModule *self, CK_ATTRIBUTE_PTR attrs, CK_ULONG n_att
 	return NULL;
 }
 
+/*
+ * Hidden method to get the mutex for a module. This is for timers to be
+ * able to reenter the module. Don't use this method.
+ */
+
+GMutex*
+_gck_module_get_scary_mutex_that_you_should_not_touch (GckModule *self)
+{
+	g_return_val_if_fail (GCK_IS_MODULE (self), NULL);
+	return self->pv->mutex;
+}
+
 /* -----------------------------------------------------------------------------
  * PKCS#11
  */
diff --git a/pkcs11/gck/gck-module.h b/pkcs11/gck/gck-module.h
index cf877d5..c374330 100644
--- a/pkcs11/gck/gck-module.h
+++ b/pkcs11/gck/gck-module.h
@@ -94,10 +94,10 @@ struct _GckModuleClass {
 	extern const CK_FUNCTION_LIST_PTR prefix ## _function_list
 
 #define GCK_DEFINE_MODULE(prefix, type) \
-	static GckModule* gck_module_instantiate (CK_C_INITIALIZE_ARGS_PTR args) \
-		{ return g_object_new ((type), "initialize-args", args, NULL); } \
+	static GckModule* gck_module_instantiate (CK_C_INITIALIZE_ARGS_PTR args, GMutex* mutex) \
+		{ return g_object_new ((type), "initialize-args", args, "mutex", mutex, NULL); } \
 	const CK_FUNCTION_LIST_PTR prefix ## _function_list = &gck_module_function_list;
-	
+
 GType                  gck_module_get_type                        (void);
 
 GckManager*            gck_module_get_manager                     (GckModule *self);
diff --git a/pkcs11/gck/gck-object.c b/pkcs11/gck/gck-object.c
index 51754a6..928638e 100644
--- a/pkcs11/gck/gck-object.c
+++ b/pkcs11/gck/gck-object.c
@@ -28,7 +28,9 @@
 #include "gck-manager.h"
 #include "gck-object.h"
 #include "gck-transaction.h"
+#include "gck-session.h"
 #include "gck-store.h"
+#include "gck-timer.h"
 #include "gck-util.h"
 
 enum {
@@ -47,12 +49,18 @@ enum {
 
 static guint signals[LAST_SIGNAL] = { 0 };
 
+typedef struct _GckObjectLifetime {
+	GckTimer *timed_timer;
+	glong timed_when;
+} GckObjectLifetime;
+
 struct _GckObjectPrivate {
 	CK_OBJECT_HANDLE handle;
 	GckManager *manager;
 	GckStore *store;
 	gchar *unique;
 	gboolean permanent;
+	GckObjectLifetime *lifetime;
 };
 
 G_DEFINE_TYPE (GckObject, gck_object, G_TYPE_OBJECT);
@@ -61,6 +69,60 @@ G_DEFINE_TYPE (GckObject, gck_object, G_TYPE_OBJECT);
  * INTERNAL 
  */
 
+static void
+kaboom_callback (GckTimer *timer, gpointer user_data)
+{
+	GckObject *self = user_data;
+	GckTransaction *transaction;
+	GckObjectLifetime *lifetime;
+	GckSession *session;
+	CK_RV rv;
+
+	g_return_if_fail (GCK_IS_OBJECT (self));
+	g_return_if_fail (self->pv->lifetime);
+	lifetime = self->pv->lifetime;
+
+	g_return_if_fail (timer == lifetime->timed_timer);
+	lifetime->timed_timer = NULL;
+
+	g_object_ref (self);
+
+	transaction = gck_transaction_new ();
+
+	session = gck_session_for_session_object (self);
+	g_return_if_fail (session);
+	gck_session_destroy_session_object (session, transaction, GCK_OBJECT (self));
+
+	gck_transaction_complete (transaction);
+	rv = gck_transaction_get_result (transaction);
+	g_object_unref (transaction);
+
+	if (rv != CKR_OK)
+		g_warning ("Unexpected failure to auto destruct object (code: %lu)", (gulong)rv);
+
+	g_object_run_dispose (G_OBJECT (self));
+	g_object_unref (self);
+}
+
+static gboolean
+start_callback (GckTransaction *transaction, GObject *obj, gpointer user_data)
+{
+	GckObject *self = GCK_OBJECT (obj);
+	GckObjectLifetime *lifetime;
+	GckSession *session = user_data;
+
+	g_return_val_if_fail (GCK_IS_OBJECT (self), FALSE);
+	g_return_val_if_fail (GCK_IS_SESSION (session), FALSE);
+	g_return_val_if_fail (self->pv->lifetime, FALSE);
+	lifetime = self->pv->lifetime;
+
+	g_return_val_if_fail (!lifetime->timed_timer, FALSE);
+	lifetime->timed_timer = gck_timer_start (gck_session_get_module (session), 
+	                                         lifetime->timed_when, kaboom_callback, self);
+
+	return TRUE;
+}
+
 /* -----------------------------------------------------------------------------
  * OBJECT 
  */
@@ -85,6 +147,9 @@ gck_object_real_get_attribute (GckObject *self, CK_ATTRIBUTE* attr)
 		if (self->pv->unique)
 			return gck_attribute_set_string (attr, self->pv->unique);
 		return CKR_ATTRIBUTE_TYPE_INVALID;
+	case CKA_GNOME_AUTO_DESTRUCT:
+		return gck_attribute_set_time (attr, self->pv->lifetime ?
+		                                     self->pv->lifetime->timed_when : -1);
 	};
 
 	/* Give store a shot */
@@ -136,6 +201,31 @@ gck_object_real_set_attribute (GckObject *self, GckTransaction* transaction, CK_
 	gck_transaction_fail (transaction, CKR_ATTRIBUTE_TYPE_INVALID);
 }
 
+static void
+gck_object_real_create_attribute (GckObject *self, GckTransaction *transaction, 
+	                          CK_ATTRIBUTE *attr, GckSession *session)
+{
+	CK_RV rv;
+
+	switch (attr->type) {
+	case CKA_GNOME_AUTO_DESTRUCT:
+		if (!self->pv->lifetime)
+			self->pv->lifetime = g_slice_new0 (GckObjectLifetime);
+		rv = gck_attribute_get_time (attr, &self->pv->lifetime->timed_when);
+		gck_attribute_consume (attr);
+		if (rv == CKR_OK) {
+			/* Must be a session object for an auto destruct */
+			if (self->pv->lifetime->timed_when >= 0 && self->pv->permanent)
+				rv = CKR_TEMPLATE_INCONSISTENT;
+			else
+				gck_transaction_add (transaction, self, start_callback, session);
+		}
+		if (rv != CKR_OK)
+			gck_transaction_fail (transaction, rv);
+		return;
+	};
+}
+
 static CK_RV
 gck_object_real_unlock (GckObject *self, CK_UTF8CHAR_PTR pin, CK_ULONG n_pin)
 {
@@ -174,6 +264,7 @@ static void
 gck_object_dispose (GObject *obj)
 {
 	GckObject *self = GCK_OBJECT (obj);
+	GckObjectLifetime *lifetime;
 	
 	if (self->pv->manager)
 		gck_manager_unregister_object (self->pv->manager, self);
@@ -181,6 +272,16 @@ gck_object_dispose (GObject *obj)
 	
 	g_object_set (self, "store", NULL, NULL);
 	g_assert (self->pv->store == NULL);
+
+	if (self->pv->lifetime) {
+		lifetime = self->pv->lifetime;
+		if (lifetime->timed_timer)
+			gck_timer_cancel (lifetime->timed_timer);
+		lifetime->timed_timer = NULL;
+
+		g_slice_free (GckObjectLifetime, lifetime);
+		self->pv->lifetime = NULL;
+	}
     
 	G_OBJECT_CLASS (gck_object_parent_class)->dispose (obj);
 }
@@ -193,6 +294,8 @@ gck_object_finalize (GObject *obj)
 	g_assert (self->pv->manager == NULL);
 	g_free (self->pv->unique);
 
+	g_assert (self->pv->lifetime == NULL);
+
 	G_OBJECT_CLASS (gck_object_parent_class)->finalize (obj);
 }
 
@@ -295,6 +398,7 @@ gck_object_class_init (GckObjectClass *klass)
 	klass->unlock = gck_object_real_unlock;
 	klass->get_attribute = gck_object_real_get_attribute;
 	klass->set_attribute = gck_object_real_set_attribute;
+	klass->create_attribute = gck_object_real_create_attribute;
 	
 	g_object_class_install_property (gobject_class, PROP_HANDLE,
 	           g_param_spec_ulong ("handle", "Handle", "Object handle",
@@ -365,6 +469,22 @@ gck_object_set_attribute (GckObject *self, GckTransaction *transaction,
 }
 
 void
+gck_object_create_attribute (GckObject *self, GckTransaction *transaction,
+                             CK_ATTRIBUTE_PTR attr, GckSession *session)
+{
+	g_return_if_fail (GCK_IS_OBJECT (self));
+	g_return_if_fail (GCK_IS_TRANSACTION (transaction));
+	g_return_if_fail (!gck_transaction_get_failed (transaction));
+	g_return_if_fail (GCK_IS_SESSION (session));
+	g_return_if_fail (attr);
+
+	g_assert (GCK_OBJECT_GET_CLASS (self)->create_attribute);
+
+	/* Check that the value will actually change */
+	GCK_OBJECT_GET_CLASS (self)->create_attribute (self, transaction, attr, session);
+}
+
+void
 gck_object_notify_attribute  (GckObject *self, CK_ATTRIBUTE_TYPE attr_type)
 {
 	g_return_if_fail (GCK_IS_OBJECT (self));
diff --git a/pkcs11/gck/gck-object.h b/pkcs11/gck/gck-object.h
index cb5b12d..a1129c8 100644
--- a/pkcs11/gck/gck-object.h
+++ b/pkcs11/gck/gck-object.h
@@ -56,6 +56,9 @@ struct _GckObjectClass {
 	
 	void (*set_attribute) (GckObject *object, GckTransaction *transaction, CK_ATTRIBUTE *attr);
 	
+	void (*create_attribute) (GckObject *object, GckTransaction *transaction, 
+	                          CK_ATTRIBUTE *attr, GckSession *session);
+
 	CK_RV (*unlock) (GckObject *self, CK_UTF8CHAR_PTR pin, CK_ULONG n_pin);
 };
 
@@ -88,6 +91,11 @@ void                   gck_object_set_attribute          (GckObject *self,
                                                           GckTransaction *transaction,
                                                           CK_ATTRIBUTE_PTR attr);
 
+void                   gck_object_create_attribute       (GckObject *self,
+                                                          GckTransaction *transaction,
+                                                          CK_ATTRIBUTE_PTR attr,
+                                                          GckSession *session);
+
 void                   gck_object_notify_attribute       (GckObject *self,
                                                           CK_ATTRIBUTE_TYPE attr_type);
 
diff --git a/pkcs11/gck/gck-session.c b/pkcs11/gck/gck-session.c
index 3b0744e..a8055d4 100644
--- a/pkcs11/gck/gck-session.c
+++ b/pkcs11/gck/gck-session.c
@@ -558,6 +558,13 @@ gck_session_class_init (GckSessionClass *klass)
  * PUBLIC 
  */
 
+GckSession*
+gck_session_for_session_object (GckObject *obj)
+{
+	g_return_val_if_fail (GCK_IS_OBJECT (obj), NULL);
+	return GCK_SESSION (g_object_get_data (G_OBJECT (obj), "owned-by-session"));
+}
+
 CK_SESSION_HANDLE
 gck_session_get_handle (GckSession *self)
 {
@@ -654,6 +661,18 @@ gck_session_login_context_specific (GckSession *self, CK_UTF8CHAR_PTR pin, CK_UL
 	return gck_object_unlock (object, pin, n_pin);
 }
 
+void
+gck_session_destroy_session_object (GckSession *self, GckTransaction *transaction,
+                                    GckObject *obj)
+{
+	g_return_if_fail (GCK_IS_SESSION (self));
+	g_return_if_fail (gck_session_for_session_object (obj) == self);
+	g_return_if_fail (GCK_IS_TRANSACTION (transaction));
+	g_return_if_fail (!gck_transaction_get_failed (transaction));
+
+	remove_object (self, transaction, obj);
+}
+
 /* -----------------------------------------------------------------------------
  * PKCS#11
  */
@@ -772,6 +791,17 @@ gck_session_C_CreateObject (GckSession* self, CK_ATTRIBUTE_PTR template,
 		}
 	}
 	
+	if (!gck_transaction_get_failed (transaction)) {
+		g_object_set (object, "permanent", is_token, NULL);
+		gck_attributes_consume (attrs, n_attrs, CKA_TOKEN, G_MAXULONG);
+	}
+
+	/* Give the object a chance to create additional attributes */
+	for (i = 0; i < n_attrs && !gck_transaction_get_failed (transaction); ++i) {
+		if (!gck_attribute_consumed (&attrs[i]))
+			gck_object_create_attribute (object, transaction, &attrs[i], self);
+	}
+
 	/* Find somewhere to store the object */
 	if (!gck_transaction_get_failed (transaction)) {
 		if (is_token) 
@@ -885,7 +915,7 @@ gck_session_C_DestroyObject (GckSession* self, CK_OBJECT_HANDLE handle)
 	transaction = gck_transaction_new ();
 
 	/* Lookup the actual session that owns this object, if no session, then a token object */
-	session = g_object_get_data (G_OBJECT (object), "owned-by-session");
+	session = gck_session_for_session_object (object);
 	if (session != NULL)
 		remove_object (session, transaction, object);
 	else
diff --git a/pkcs11/gck/gck-session.h b/pkcs11/gck/gck-session.h
index 6a1b327..815e62c 100644
--- a/pkcs11/gck/gck-session.h
+++ b/pkcs11/gck/gck-session.h
@@ -59,6 +59,8 @@ GckSession*              gck_session_new                                (GckModu
                                                                          CK_SLOT_ID slot_id,
                                                                          CK_FLAGS flags);
 
+GckSession*              gck_session_for_session_object                 (GckObject *obj);
+
 CK_SESSION_HANDLE        gck_session_get_handle                         (GckSession *self);
 
 CK_SLOT_ID               gck_session_get_slot_id                        (GckSession *self);
@@ -86,6 +88,9 @@ CK_RV                    gck_session_login_context_specific             (GckSess
                                                                          CK_UTF8CHAR_PTR pin,
                                                                          CK_ULONG n_pin);
 
+void                     gck_session_destroy_session_object             (GckSession *self,
+                                                                         GckTransaction *transaction,
+                                                                         GckObject *obj);
 
 
 
diff --git a/pkcs11/gck/gck-timer.c b/pkcs11/gck/gck-timer.c
new file mode 100644
index 0000000..4776c3b
--- /dev/null
+++ b/pkcs11/gck/gck-timer.c
@@ -0,0 +1,225 @@
+/*
+ * gnome-keyring
+ *
+ * Copyright (C) 2009 Stefan Walter
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General  License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General  License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * License along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+
+#include "config.h"
+
+#include "gck-timer.h"
+
+#include <glib.h>
+
+struct _GckTimer {
+	glong when;
+	GMutex *mutex;
+	gpointer identifier;
+	GckTimerFunc callback;
+	gpointer user_data;
+};
+
+static GStaticMutex timer_mutex = G_STATIC_MUTEX_INIT;
+static GQueue *timer_queue = NULL;
+static GThread *timer_thread = NULL;
+static GCond *timer_cond = NULL;
+static gboolean timer_run = FALSE;
+static gint timer_refs = 0;
+
+static gint
+compare_timers (gconstpointer a, gconstpointer b, gpointer user_data)
+{
+	const GckTimer *ta = a;
+	const GckTimer *tb = b;
+	if (ta->when < tb->when)
+		return -1;
+	return ta->when > tb->when;
+}
+
+static gpointer
+timer_thread_func (gpointer unused)
+{
+	GMutex *mutex = g_static_mutex_get_mutex (&timer_mutex);
+	GckTimer *timer;
+	GTimeVal tv;
+
+	g_mutex_lock (mutex);
+
+	while (timer_run) {
+		timer = g_queue_peek_head (timer_queue);
+
+		/* Nothing in the queue, wait until we have action */
+		if (!timer) {
+			g_cond_wait (timer_cond, mutex);
+			continue;
+		}
+
+		g_get_current_time (&tv);
+
+		/* We have to wait until the next timer? */
+		if (tv.tv_sec < timer->when) { 
+			tv.tv_sec = timer->when;
+			tv.tv_usec = 0;
+			g_cond_timed_wait (timer_cond, mutex, &tv);
+			continue;
+		}
+
+		/* Leave our thread mutex, and enter the module */
+		g_mutex_unlock (mutex);
+		g_mutex_lock (timer->mutex);
+
+			if (timer->callback) 
+				(timer->callback) (timer, timer->user_data);
+
+		/* Leave the module, and go back into our thread mutex */
+		g_mutex_unlock (timer->mutex);
+		g_mutex_lock (mutex);
+
+		/* There's a chance that the timer may no longer be at head of queue */
+		g_queue_remove (timer_queue, timer);
+		g_slice_free (GckTimer, timer);
+	}
+
+	g_mutex_unlock (mutex);
+	return NULL;
+}
+
+void
+gck_timer_initialize (void)
+{
+	GError *error = NULL;
+	g_static_mutex_lock (&timer_mutex);
+
+		g_atomic_int_inc (&timer_refs);
+		if (!timer_thread) {
+			timer_run = TRUE;
+			timer_thread = g_thread_create (timer_thread_func, NULL, TRUE, &error);
+			if (timer_thread) {
+				g_assert (timer_queue == NULL);
+				timer_queue = g_queue_new ();
+			
+				g_assert (timer_cond == NULL);
+				timer_cond = g_cond_new ();
+			} else {
+				g_warning ("could not create timer thread: %s", 
+				           error && error->message ? error->message : "");
+			}
+		}
+
+	g_static_mutex_unlock (&timer_mutex);
+}
+
+void
+gck_timer_shutdown (void)
+{
+	GckTimer *timer;
+
+	if (g_atomic_int_dec_and_test (&timer_refs)) {
+		timer_run = FALSE;
+		g_assert (timer_cond);
+		g_cond_broadcast (timer_cond);
+
+		g_assert (timer_thread);
+		g_thread_join (timer_thread);
+		timer_thread = NULL;
+
+		g_assert (timer_queue);
+
+		/* Cleanup any outstanding timers */
+		while (!g_queue_is_empty (timer_queue)) {
+			timer = g_queue_pop_head (timer_queue);
+			g_slice_free (GckTimer, timer);
+		}
+
+		g_queue_free (timer_queue);
+		timer_queue = NULL;
+
+		g_cond_free (timer_cond);
+		timer_cond = NULL;
+	}
+}
+
+/* We're the only caller of this function */
+GMutex* _gck_module_get_scary_mutex_that_you_should_not_touch (GckModule *self);
+
+GckTimer*
+gck_timer_start (GckModule *module, glong when, GckTimerFunc callback, gpointer user_data)
+{
+	GckTimer *timer;
+
+	g_return_val_if_fail (callback, NULL);
+	g_return_val_if_fail (timer_queue, NULL);
+
+	timer = g_slice_new (GckTimer);
+	timer->when = when;
+	timer->callback = callback;
+	timer->user_data = user_data;
+
+	timer->mutex = _gck_module_get_scary_mutex_that_you_should_not_touch (module);
+	g_return_val_if_fail (timer->mutex, NULL);
+
+	g_static_mutex_lock (&timer_mutex);
+
+		g_assert (timer_queue);
+		g_queue_insert_sorted (timer_queue, timer, compare_timers, NULL);
+		g_assert (timer_cond);
+		g_cond_broadcast (timer_cond);
+
+	g_static_mutex_unlock (&timer_mutex);
+
+	/* 
+	 * Note that the timer thread could not already completed this timer. 
+	 * This is because we're in the module, and in order to complete a timer
+	 * the timer thread must enter the module mutex.
+	 */
+
+	return timer;
+}
+
+void
+gck_timer_cancel (GckTimer *timer)
+{
+	GList *link;
+
+	g_return_if_fail (timer_queue);
+
+	g_static_mutex_lock (&timer_mutex);
+
+		g_assert (timer_queue);
+
+		link = g_queue_find (timer_queue, timer);
+		if (link) {
+
+			/* 
+			 * For thread safety the timer struct must be freed 
+			 * from the timer thread. So to cancel, what we do 
+			 * is move the timer to the front of the queue, 
+			 * and reset the callback and when.
+			 */
+
+			timer->when = 0;
+			timer->callback = NULL;
+
+			g_queue_delete_link (timer_queue, link);
+			g_queue_push_head (timer_queue, timer);
+
+			g_assert (timer_cond);
+			g_cond_broadcast (timer_cond);
+		}
+
+	g_static_mutex_unlock (&timer_mutex);
+}
diff --git a/pkcs11/gck/gck-timer.h b/pkcs11/gck/gck-timer.h
new file mode 100644
index 0000000..14fdf93
--- /dev/null
+++ b/pkcs11/gck/gck-timer.h
@@ -0,0 +1,43 @@
+/* 
+ * gnome-keyring
+ * 
+ * Copyright (C) 2009 Stefan Walter
+ * 
+ * This program is free software; you can redistribute it and/or modify 
+ * it under the terms of the GNU Lesser General  License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *  
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General  License for more details.
+ *  
+ * You should have received a copy of the GNU Lesser General 
+ * 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 GCKTIMER_H_
+#define GCKTIMER_H_
+
+#include <glib.h>
+
+#include "gck-types.h"
+
+typedef void    (*GckTimerFunc)                (GckTimer *timer,
+                                                gpointer user_data); 
+
+GckTimer*       gck_timer_start                (GckModule *module, 
+                                                glong when,
+                                                GckTimerFunc func, 
+                                                gpointer user_data);
+
+void            gck_timer_cancel               (GckTimer *timer);
+
+void            gck_timer_initialize           (void);
+
+void            gck_timer_shutdown             (void);
+
+#endif /* GCKTIMER_H_ */
diff --git a/pkcs11/gck/gck-types.h b/pkcs11/gck/gck-types.h
index eebcce4..67ae9de 100644
--- a/pkcs11/gck/gck-types.h
+++ b/pkcs11/gck/gck-types.h
@@ -38,6 +38,7 @@ typedef struct _GckSessionPrivateKey GckSessionPrivateKey;
 typedef struct _GckSessionPublicKey GckSessionPublicKey;
 typedef struct _GckSexp GckSexp;
 typedef struct _GckStore GckStore;
+typedef struct _GckTimer GckTimer;
 typedef struct _GckTransaction GckTransaction;
 
 #endif /* __GCK_TYPES_H__ */
diff --git a/pkcs11/gck/tests/Makefile.am b/pkcs11/gck/tests/Makefile.am
index 9bfbe88..7596afa 100644
--- a/pkcs11/gck/tests/Makefile.am
+++ b/pkcs11/gck/tests/Makefile.am
@@ -10,12 +10,14 @@ UNIT_AUTO = \
 	unit-test-crypto.c \
 	unit-test-data-asn1.c \
 	unit-test-data-der.c \
+	unit-test-timer.c \
 	unit-test-transaction.c \
 	unit-test-store.c \
 	unit-test-memory-store.c \
 	unit-test-login.c \
 	unit-test-data-file.c \
 	unit-test-file-tracker.c \
+	test-module.c test-module.h \
 	$(BUILT_SOURCES)
 
 UNIT_PROMPT = 
diff --git a/pkcs11/gck/tests/test-module.c b/pkcs11/gck/tests/test-module.c
new file mode 100644
index 0000000..cb2f395
--- /dev/null
+++ b/pkcs11/gck/tests/test-module.c
@@ -0,0 +1,70 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* test-module.c: A test PKCS#11 module implementation
+
+   Copyright (C) 2009 Stefan Walter
+
+   The Gnome Keyring Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Keyring Library 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Stef Walter <stef@memberwebs.com>
+*/
+
+#include "config.h"
+#include "test-module.h"
+
+/* Include all the module entry points */
+#include "gck/gck-module-ep.h"
+GCK_DEFINE_MODULE (test_module, GCK_TYPE_MODULE);
+
+CK_FUNCTION_LIST_PTR  
+test_module_get_functions  (void)
+{
+	gck_crypto_initialize ();
+	return test_module_function_list;
+}
+
+GckModule*
+test_module_initialize (void)
+{
+	CK_RV rv;
+	
+	gck_crypto_initialize ();
+	rv = test_module_function_list->C_Initialize (NULL);
+	g_return_val_if_fail (rv == CKR_OK, NULL);
+	
+	g_return_val_if_fail (pkcs11_module, NULL);
+	return pkcs11_module;
+}
+
+void
+test_module_finalize (void)
+{
+	CK_RV rv;
+	
+	rv = test_module_function_list->C_Finalize (NULL);
+	g_return_if_fail (rv == CKR_OK);
+}
+
+void
+test_module_leave (void)
+{
+	g_static_mutex_unlock (&pkcs11_module_mutex);	
+}
+
+void
+test_module_enter (void)
+{
+	g_static_mutex_lock (&pkcs11_module_mutex);
+}
diff --git a/pkcs11/gck/tests/test-module.h b/pkcs11/gck/tests/test-module.h
new file mode 100644
index 0000000..8a8ba3f
--- /dev/null
+++ b/pkcs11/gck/tests/test-module.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* test-module.h: A test PKCS#11 module implementation
+
+   Copyright (C) 2009 Stefan Walter
+
+   The Gnome Keyring Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Keyring Library 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Stef Walter <stef@memberwebs.com>
+*/
+
+#ifndef TESTMODULE_H_
+#define TESTMODULE_H_
+
+#include "gck-types.h"
+
+#include "pkcs11.h"
+
+CK_FUNCTION_LIST_PTR   test_module_get_functions            (void);
+
+void                   test_module_leave                    (void);
+
+void                   test_module_enter                    (void);
+
+GckModule*             test_module_initialize               (void);
+
+void                   test_module_finalize                 (void);
+
+#endif /* TESTMODULE_H_ */
diff --git a/pkcs11/gck/tests/unit-test-timer.c b/pkcs11/gck/tests/unit-test-timer.c
new file mode 100644
index 0000000..e826799
--- /dev/null
+++ b/pkcs11/gck/tests/unit-test-timer.c
@@ -0,0 +1,159 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* unit-test-timer.c: Test thread timer functionality
+
+   Copyright (C) 2009 Stefan Walter
+
+   The Gnome Keyring Library is free software; you can redistribute it and/or
+   modify it under the terms of the GNU Library General Public License as
+   published by the Free Software Foundation; either version 2 of the
+   License, or (at your option) any later version.
+
+   The Gnome Keyring Library 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
+   Library General Public License for more details.
+
+   You should have received a copy of the GNU Library General Public
+   License along with the Gnome Library; see the file COPYING.LIB.  If not,
+   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.
+
+   Author: Stef Walter <stef@memberwebs.com>
+*/
+
+#include "run-auto-test.h"
+#include "test-module.h"
+
+#include "gck/gck-timer.h"
+
+GckModule *module = NULL;
+
+DEFINE_SETUP(timer_setup)
+{
+	module = test_module_initialize ();
+	test_module_enter ();
+}
+
+DEFINE_TEARDOWN(timer_teardown)
+{
+	test_module_leave ();
+	test_module_finalize ();
+}
+
+DEFINE_TEST(timer_extra_initialize)
+{
+	gck_timer_initialize ();
+	gck_timer_shutdown ();
+}
+
+static void
+timer_callback (GckTimer *timer, gpointer user_data)
+{
+	GckTimer **value = user_data;
+	g_assert (timer);
+	g_assert (timer == *value);
+	*value = NULL;
+}
+
+DEFINE_TEST(timer_simple)
+{
+	GTimeVal tv;
+	GckTimer *timer;
+	
+	g_get_current_time (&tv);
+	timer = gck_timer_start (module, tv.tv_sec + 2, timer_callback, &timer);
+	
+	test_module_leave ();
+	test_mainloop_run (2200);
+	test_module_enter ();
+	
+	g_assert (timer == NULL);
+}
+
+DEFINE_TEST(timer_cancel)
+{
+	GTimeVal tv;
+	GckTimer *timer;
+	
+	g_get_current_time (&tv);
+	timer = gck_timer_start (module, tv.tv_sec + 2, timer_callback, &timer);
+	
+	test_module_leave ();
+	test_mainloop_run (500);
+	test_module_enter ();
+	
+	gck_timer_cancel (timer);
+
+	test_module_leave ();
+	test_mainloop_run (2000);
+	test_module_enter ();
+
+	/* The callback should not have been called */
+	g_assert (timer != NULL);
+}
+
+DEFINE_TEST(timer_immediate)
+{
+	GTimeVal tv;
+	GckTimer *timer;
+	
+	/* Setup timer in the past, should execute as soon as possible */
+	g_get_current_time (&tv);
+	timer = gck_timer_start (module, tv.tv_sec - 5, timer_callback, &timer);
+	
+	/* Should not be called immediately */
+	g_assert (timer != NULL);
+	
+	test_module_leave ();
+	test_mainloop_run (50);
+	test_module_enter ();
+
+	/* Should have been called now */
+	g_assert (timer == NULL);
+}
+
+static GckTimer *timer_last = NULL;
+static gint timer_check = 0;
+
+static void
+multiple_callback (GckTimer *timer, gpointer user_data)
+{
+	gint value = GPOINTER_TO_INT (user_data);
+	g_assert (timer);
+	g_assert (timer != timer_last);
+	g_assert (value == timer_check);
+	timer_last = timer;
+	timer_check += 1;
+}
+
+DEFINE_TEST(timer_multiple)
+{
+	GTimeVal tv;
+	
+	timer_check = 0;
+	g_get_current_time (&tv);
+	
+	/* Multiple timers, add out of order, should be called in order */
+	gck_timer_start (module, tv.tv_sec + 1, multiple_callback, GINT_TO_POINTER (1));
+	gck_timer_start (module, tv.tv_sec + 3, multiple_callback, GINT_TO_POINTER (3));
+	gck_timer_start (module, tv.tv_sec + 2, multiple_callback, GINT_TO_POINTER (2));
+	gck_timer_start (module, tv.tv_sec + 0, multiple_callback, GINT_TO_POINTER (0));
+	
+	test_module_leave ();
+	test_mainloop_run (3500);
+	test_module_enter ();
+	
+	g_assert (timer_check == 4);
+}
+
+DEFINE_TEST(timer_outstanding)
+{
+	GTimeVal tv;
+	
+	g_get_current_time (&tv);
+
+	/* A timer that can't be called */
+	gck_timer_start (module, tv.tv_sec + 5, timer_callback, NULL);
+	gck_timer_start (module, tv.tv_sec + 10, timer_callback, NULL);
+	gck_timer_start (module, tv.tv_sec + 1, timer_callback, NULL);
+}
diff --git a/pkcs11/pkcs11g.h b/pkcs11/pkcs11g.h
index 2651ef5..d2ac6d9 100644
--- a/pkcs11/pkcs11g.h
+++ b/pkcs11/pkcs11g.h
@@ -97,4 +97,10 @@
 #define CKA_GNOME_PURPOSE_IPSEC_USER             (CKA_GNOME + 108)
 #define CKA_GNOME_PURPOSE_TIME_STAMPING          (CKA_GNOME + 109)
 
+/* -------------------------------------------------------------------
+ * AUTO-DESTRUCT
+ */
+
+#define CKA_GNOME_AUTO_DESTRUCT                  (CKO_GNOME + 200)
+
 #endif /* PKCS11G_H */
diff --git a/pkcs11/ssh-agent/gck-ssh-agent-ops.c b/pkcs11/ssh-agent/gck-ssh-agent-ops.c
index 4fbc216..662935b 100644
--- a/pkcs11/ssh-agent/gck-ssh-agent-ops.c
+++ b/pkcs11/ssh-agent/gck-ssh-agent-ops.c
@@ -531,6 +531,59 @@ replace_key_pair (GP11Session *session, GP11Attributes *priv, GP11Attributes *pu
 	return TRUE;
 }
 
+static gboolean
+load_contraints (EggBuffer *buffer, gsize offset, gsize *next_offset,
+                 GP11Attributes *priv, GP11Attributes *pub)
+{
+	GTimeVal tv;
+	guchar constraint;
+	guint32 lifetime;
+	time_t time;
+	struct tm tm;
+	gchar buf[20];
+
+	/*
+	 * Constraints are a byte flag, and optional data depending
+	 * on the constraint.
+	 */
+
+	while (offset < egg_buffer_length (buffer)) {
+		if (!egg_buffer_get_byte (buffer, offset, &offset, &constraint))
+			return FALSE;
+
+		switch (constraint) {
+		case GCK_SSH_FLAG_CONSTRAIN_LIFETIME:
+			if (!egg_buffer_get_uint32 (buffer, offset, &offset, &lifetime))
+				return FALSE;
+
+			g_get_current_time (&tv);
+			time = tv.tv_sec + lifetime;
+
+			if (!gmtime_r (&time, &tm))
+				g_return_val_if_reached (FALSE);
+
+			if (!strftime(buf, sizeof (buf), "%Y%m%d%H%M%S00", &tm))
+				g_return_val_if_reached (FALSE);
+
+			gp11_attributes_add_data (pub, CKA_GNOME_AUTO_DESTRUCT, buf, 16);
+			gp11_attributes_add_data (priv, CKA_GNOME_AUTO_DESTRUCT, buf, 16);
+			break;
+
+		case GCK_SSH_FLAG_CONSTRAIN_CONFIRM:
+			/* We can't use prompting as access control on an insecure X desktop */
+			g_message ("prompt constraints are not supported.");
+			return FALSE;
+
+		default:
+			g_message ("unsupported constraint or other unsupported data");
+			return FALSE;
+		}
+	}
+
+	*next_offset = offset;
+	return TRUE;
+}
+
 /* -----------------------------------------------------------------------------
  * OPERATIONS
  */
@@ -580,9 +633,6 @@ op_add_identity (GckSshAgentCall *call)
 		return FALSE;
 	}
 		
-		
-	/* TODO: Blinding? See ssh-agent.c */
-
 	/* Get the comment */
 	if (!egg_buffer_get_string (call->req, offset, &offset, &comment, (EggBufferAllocator)g_realloc)) {
 		gp11_attributes_unref (pub);
@@ -593,7 +643,14 @@ op_add_identity (GckSshAgentCall *call)
 	gp11_attributes_add_string (pub, CKA_LABEL, comment);
 	gp11_attributes_add_string (priv, CKA_LABEL, comment);
 	g_free (comment);
-		
+
+	/* Any constraints on loading the key */
+	if (!load_contraints (call->req, offset, &offset, priv, pub)) {
+		gp11_attributes_unref (pub);
+		gp11_attributes_unref (priv);
+		return FALSE;
+	}
+
 	/* 
 	 * This is the session that owns these objects. Only 
 	 * one thread can use it at a time. 
@@ -638,6 +695,13 @@ op_v1_add_identity (GckSshAgentCall *call)
 	gp11_attributes_add_string (priv, CKA_LABEL, V1_LABEL);
 	gp11_attributes_add_string (pub, CKA_LABEL, V1_LABEL);
 	
+	/* Any constraints on loading the key */
+	if (!load_contraints (call->req, offset, &offset, priv, pub)) {
+		gp11_attributes_unref (pub);
+		gp11_attributes_unref (priv);
+		return FALSE;
+	}
+
 	/* 
 	 * This is the session that owns these objects. Only 
 	 * one thread can use it at a time. 
@@ -1228,6 +1292,6 @@ const GckSshAgentOperation gck_ssh_agent_operations[GCK_SSH_OP_MAX] = {
      op_not_implemented_success,                 /* GKR_SSH_OP_LOCK */
      op_not_implemented_success,                 /* GKR_SSH_OP_UNLOCK */
      op_v1_add_identity,                         /* GKR_SSH_OP_ADD_RSA_ID_CONSTRAINED */
-     op_not_implemented_failure,                 /* GKR_SSH_OP_ADD_ID_CONSTRAINED */
+     op_add_identity,                            /* GKR_SSH_OP_ADD_ID_CONSTRAINED */
      op_not_implemented_failure,                 /* GKR_SSH_OP_ADD_SMARTCARD_KEY_CONSTRAINED */
 };
diff --git a/pkcs11/ssh-agent/gck-ssh-agent-standalone.c b/pkcs11/ssh-agent/gck-ssh-agent-standalone.c
index 3dd619c..0a2782f 100644
--- a/pkcs11/ssh-agent/gck-ssh-agent-standalone.c
+++ b/pkcs11/ssh-agent/gck-ssh-agent-standalone.c
@@ -107,7 +107,7 @@ main(int argc, char *argv[])
 	g_signal_connect (module, "authenticate-object", G_CALLBACK (authenticate_object), NULL);
 	gp11_module_set_auto_authenticate (module, GP11_AUTHENTICATE_OBJECTS);
 
-	sock = gck_ssh_agent_initialize_with_module ("/tmp/test-gck-ssh-agent", module);
+	sock = gck_ssh_agent_initialize_with_module ("/tmp", module);
 	g_object_unref (module);
 	
 	if (sock == -1)
