[gnome-keyring: 5/10] [gck] Add authenticator objects for storing authenticated state.



commit 6a6d07aa2265ff842b37900faaf13b096c0fdd5a
Author: Stef Walter <stef memberwebs com>
Date:   Sun Jul 19 03:44:53 2009 +0000

    [gck] Add authenticator objects for storing authenticated state.
    
    Authenticator objects are now used for PKCS#11 context specific
    logins. In the future we'll use them for unlocking stuff on a
    token, session, or timeout basis.

 pkcs11/gck/Makefile.am                         |    1 +
 pkcs11/gck/gck-authenticator.c                 |  402 ++++++++++++++++++++++++
 pkcs11/gck/gck-authenticator.h                 |   77 +++++
 pkcs11/gck/gck-key.c                           |    4 +-
 pkcs11/gck/gck-key.h                           |    5 +-
 pkcs11/gck/gck-login.c                         |    1 +
 pkcs11/gck/gck-module.c                        |   12 +-
 pkcs11/gck/gck-object.c                        |   50 +++-
 pkcs11/gck/gck-object.h                        |    8 +-
 pkcs11/gck/gck-private-key.c                   |   82 ++++--
 pkcs11/gck/gck-private-key.h                   |    8 +-
 pkcs11/gck/gck-public-key.c                    |    2 +-
 pkcs11/gck/gck-session.c                       |  107 ++++++-
 pkcs11/gck/gck-session.h                       |    8 +
 pkcs11/gck/gck-sexp.c                          |    3 +-
 pkcs11/gck/gck-sexp.h                          |    2 +-
 pkcs11/gck/gck-types.h                         |    1 +
 pkcs11/gck/gck-util.c                          |   11 +
 pkcs11/gck/gck-util.h                          |    2 +
 pkcs11/gck/tests/Makefile.am                   |    2 +
 pkcs11/gck/tests/mock-locked-object.c          |   88 +++++
 pkcs11/gck/tests/mock-locked-object.h          |   52 +++
 pkcs11/gck/tests/test-module.c                 |   34 ++-
 pkcs11/gck/tests/test-module.h                 |   10 +-
 pkcs11/gck/tests/unit-test-authenticator.c     |  254 +++++++++++++++
 pkcs11/gck/tests/unit-test-memory-store.c      |    6 +-
 pkcs11/gck/tests/unit-test-object.c            |   23 +--
 pkcs11/gck/tests/unit-test-store.c             |    7 +-
 pkcs11/gck/tests/unit-test-timer.c             |    6 +-
 pkcs11/pkcs11g.h                               |   14 +-
 pkcs11/ssh-store/gck-ssh-module.c              |    8 +
 pkcs11/ssh-store/gck-ssh-private-key.c         |   40 ++-
 pkcs11/ssh-store/tests/Makefile.am             |    4 +-
 pkcs11/ssh-store/tests/test-ssh-module.c       |  105 ++++++
 pkcs11/ssh-store/tests/test-ssh-module.h       |   43 +++
 pkcs11/ssh-store/tests/unit-test-private-key.c |   97 ++++++
 pkcs11/user-store/gck-user-private-key.c       |    4 +-
 pkcs11/user-store/gck-user-storage.c           |   10 +-
 38 files changed, 1451 insertions(+), 142 deletions(-)
---
diff --git a/pkcs11/gck/Makefile.am b/pkcs11/gck/Makefile.am
index 730f416..37f532e 100644
--- a/pkcs11/gck/Makefile.am
+++ b/pkcs11/gck/Makefile.am
@@ -18,6 +18,7 @@ BUILT_SOURCES = \
 
 libgck_la_SOURCES = \
 	gck-attributes.c gck-attributes.h \
+	gck-authenticator.c gck-authenticator.h \
 	gck-certificate.c gck-certificate.h \
 	gck-certificate-key.c gck-certificate-key.h \
 	gck-certificate-trust.c gck-certificate-trust.h \
diff --git a/pkcs11/gck/gck-authenticator.c b/pkcs11/gck/gck-authenticator.c
new file mode 100644
index 0000000..2f71c2a
--- /dev/null
+++ b/pkcs11/gck/gck-authenticator.c
@@ -0,0 +1,402 @@
+/* 
+ * 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-attributes.h"
+#include "gck-authenticator.h"
+#include "gck-login.h"
+#include "gck-session.h"
+#include "gck-transaction.h"
+
+#include "pkcs11/pkcs11.h"
+#include "pkcs11/pkcs11g.h"
+
+enum {
+	PROP_0,
+	PROP_OBJECT,
+	PROP_LOGIN,
+	PROP_USES_REMAINING
+};
+
+struct _GckAuthenticatorPrivate {
+
+	/* The object we authenticated */
+	GckObject *object;
+	
+	/* Optional login */
+	GckLogin *login;
+	
+	/* Can limit by number of uses remaining */
+	gint uses_remaining;
+};
+
+G_DEFINE_TYPE (GckAuthenticator, gck_authenticator, GCK_TYPE_OBJECT);
+
+/* -----------------------------------------------------------------------------
+ * INTERNAL 
+ */
+
+static void
+factory_create_authenticator (GckSession *session, GckTransaction *transaction, 
+                              CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs, GckObject **result)
+{
+	CK_OBJECT_HANDLE handle;
+	GckAuthenticator *auth;
+	CK_ATTRIBUTE *attr;
+	GckObject *object;
+	CK_RV rv;
+	
+	g_return_if_fail (GCK_IS_TRANSACTION (transaction));
+	g_return_if_fail (attrs || !n_attrs);
+	g_return_if_fail (result);
+
+	/* The handle is required */
+	if (!gck_attributes_find_ulong (attrs, n_attrs, CKA_GNOME_OBJECT, &handle)) {
+		gck_transaction_fail (transaction, CKR_TEMPLATE_INCOMPLETE);
+		return;
+	}
+
+	/* Must be a valid object */
+	rv = gck_session_lookup_readable_object (session, handle, &object);
+	if (rv != CKR_OK) {
+		gck_transaction_fail (transaction, rv);
+		return;
+	}
+	
+	/* The value is optional */
+	attr = gck_attributes_find (attrs, n_attrs, CKA_VALUE);
+
+ 	gck_attributes_consume (attrs, n_attrs, CKA_VALUE, CKA_GNOME_OBJECT, G_MAXULONG);
+	
+	rv = gck_authenticator_create (object, attr ? attr->pValue : NULL, 
+	                               attr ? attr->ulValueLen : 0, &auth);
+	if (rv == CKR_OK)
+		*result = GCK_OBJECT (auth);
+	else
+		gck_transaction_fail (transaction, rv);
+}
+
+static void
+self_destruct (GckAuthenticator *self)
+{
+	GckTransaction *transaction;
+	CK_RV rv;
+	
+	g_assert (GCK_IS_AUTHENTICATOR (self));
+	
+	transaction = gck_transaction_new ();
+
+	/* Destroy ourselves */
+	gck_object_destroy (GCK_OBJECT (self), transaction);
+	
+	gck_transaction_complete (transaction);
+	rv = gck_transaction_get_result (transaction);
+	g_object_unref (transaction);
+
+	if (rv != CKR_OK)
+		g_warning ("Couldn't destroy authenticator object: (code %lu)", (gulong)rv);
+}
+
+static void
+object_went_away (gpointer data, GObject *old_object)
+{
+	GckAuthenticator *self = data;
+	g_return_if_fail (GCK_IS_AUTHENTICATOR (self));
+	self->pv->object = NULL;
+	self_destruct (self);
+}
+
+/* -----------------------------------------------------------------------------
+ * OBJECT 
+ */
+
+static CK_RV 
+gck_authenticator_real_get_attribute (GckObject *base, CK_ATTRIBUTE *attr)
+{
+	GckAuthenticator *self = GCK_AUTHENTICATOR (base);
+
+	switch (attr->type) {
+
+	case CKA_CLASS:
+		return gck_attribute_set_ulong (attr, CKO_GNOME_AUTHENTICATOR);
+
+	case CKA_PRIVATE:
+		return gck_attribute_set_bool (attr, TRUE);
+
+	case CKA_GNOME_OBJECT:
+		g_return_val_if_fail (self->pv->object, CKR_GENERAL_ERROR);
+		return gck_attribute_set_ulong (attr, gck_object_get_handle (self->pv->object));
+
+	case CKA_GNOME_USES_REMAINING:
+		if (self->pv->uses_remaining < 0)
+			return gck_attribute_set_ulong (attr, (CK_ULONG)-1);
+		else
+			return gck_attribute_set_ulong (attr, self->pv->uses_remaining);
+		
+	case CKA_VALUE:
+		return CKR_ATTRIBUTE_SENSITIVE;
+	};
+
+	return GCK_OBJECT_CLASS (gck_authenticator_parent_class)->get_attribute (base, attr);
+}
+
+static GObject* 
+gck_authenticator_constructor (GType type, guint n_props, GObjectConstructParam *props) 
+{
+	GckAuthenticator *self = GCK_AUTHENTICATOR (G_OBJECT_CLASS (gck_authenticator_parent_class)->constructor(type, n_props, props));
+	g_return_val_if_fail (self, NULL);	
+
+	g_return_val_if_fail (self->pv->object, NULL);
+
+	return G_OBJECT (self);
+}
+
+static void
+gck_authenticator_init (GckAuthenticator *self)
+{
+	self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCK_TYPE_AUTHENTICATOR, GckAuthenticatorPrivate);
+	self->pv->uses_remaining = -1;
+}
+
+static void
+gck_authenticator_dispose (GObject *obj)
+{
+	GckAuthenticator *self = GCK_AUTHENTICATOR (obj);
+
+	if (self->pv->object)
+		g_object_weak_unref (G_OBJECT (self->pv->object), object_went_away, self);
+	self->pv->object = NULL;
+	
+	if (self->pv->login)
+		g_object_unref (self->pv->login);
+	self->pv->login = NULL;
+	
+	G_OBJECT_CLASS (gck_authenticator_parent_class)->dispose (obj);
+}
+
+static void
+gck_authenticator_finalize (GObject *obj)
+{
+	GckAuthenticator *self = GCK_AUTHENTICATOR (obj);
+	
+	g_assert (!self->pv->object);
+	g_assert (!self->pv->login);
+
+	G_OBJECT_CLASS (gck_authenticator_parent_class)->finalize (obj);
+}
+
+static void
+gck_authenticator_set_property (GObject *obj, guint prop_id, const GValue *value, 
+                                GParamSpec *pspec)
+{
+	GckAuthenticator *self = GCK_AUTHENTICATOR (obj);
+	
+	switch (prop_id) {
+	case PROP_OBJECT:
+		g_return_if_fail (!self->pv->object);
+		self->pv->object = g_value_get_object (value);
+		g_return_if_fail (GCK_IS_OBJECT (self->pv->object));
+		g_object_weak_ref (G_OBJECT (self->pv->object), object_went_away, self);
+		break;
+	case PROP_LOGIN:
+		gck_authenticator_set_login (self, g_value_get_object (value));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gck_authenticator_get_property (GObject *obj, guint prop_id, GValue *value, 
+                              GParamSpec *pspec)
+{
+	GckAuthenticator *self = GCK_AUTHENTICATOR (obj);
+	
+	switch (prop_id) {
+	case PROP_OBJECT:
+		g_value_set_object (value, gck_authenticator_get_object (self));
+		break;
+	case PROP_LOGIN:
+		g_value_set_object (value, gck_authenticator_get_login (self));
+		break;
+	case PROP_USES_REMAINING:
+		g_value_set_int (value, gck_authenticator_get_uses_remaining (self));
+		break;
+	default:
+		G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec);
+		break;
+	}
+}
+
+static void
+gck_authenticator_class_init (GckAuthenticatorClass *klass)
+{
+	GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+	GckObjectClass *gck_class = GCK_OBJECT_CLASS (klass);
+	
+	gck_authenticator_parent_class = g_type_class_peek_parent (klass);
+	g_type_class_add_private (klass, sizeof (GckAuthenticatorPrivate));
+
+	gobject_class->constructor = gck_authenticator_constructor;
+	gobject_class->dispose = gck_authenticator_dispose;
+	gobject_class->finalize = gck_authenticator_finalize;
+	gobject_class->set_property = gck_authenticator_set_property;
+	gobject_class->get_property = gck_authenticator_get_property;
+	
+	gck_class->get_attribute = gck_authenticator_real_get_attribute;
+    
+	g_object_class_install_property (gobject_class, PROP_OBJECT,
+	           g_param_spec_object ("object", "Object", "Object authenticated", 
+	                                GCK_TYPE_OBJECT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+	g_object_class_install_property (gobject_class, PROP_LOGIN,
+	           g_param_spec_object ("login", "Login", "Optiontal login", 
+	                                GCK_TYPE_LOGIN, G_PARAM_READWRITE));
+	
+	g_object_class_install_property (gobject_class, PROP_USES_REMAINING,
+	           g_param_spec_int ("uses-remaining", "Uses Remaining", "Uses remaining",
+	                             -1, G_MAXINT, -1, G_PARAM_READWRITE));
+}
+
+/* -----------------------------------------------------------------------------
+ * PUBLIC 
+ */
+
+GckFactoryInfo*
+gck_authenticator_get_factory (void)
+{
+	static CK_OBJECT_CLASS klass = CKO_GNOME_AUTHENTICATOR;
+
+	static CK_ATTRIBUTE attributes[] = {
+		{ CKA_CLASS, &klass, sizeof (klass) },
+	};
+
+	static GckFactoryInfo factory = {
+		attributes,
+		G_N_ELEMENTS (attributes),
+		factory_create_authenticator
+	};
+	
+	return &factory;
+}
+
+CK_RV
+gck_authenticator_create (GckObject *object, CK_UTF8CHAR_PTR pin,
+                          CK_ULONG n_pin, GckAuthenticator **result)
+{
+	GckAuthenticator *auth;
+	GckLogin *login = NULL;
+	CK_RV rv;
+	
+	g_return_val_if_fail (GCK_IS_OBJECT (object), CKR_GENERAL_ERROR);
+	g_return_val_if_fail (result, CKR_GENERAL_ERROR);
+	
+	if (pin != NULL)
+		login = gck_login_new (pin, n_pin);
+	
+	auth = g_object_new (GCK_TYPE_AUTHENTICATOR, 
+	                     "module", gck_object_get_module (object), 
+	                     "login", login, "object", object, NULL);
+	
+	/* Now the unlock must work */
+	rv = gck_object_unlock (object, auth);
+	if (rv == CKR_OK)
+		*result = auth;
+	else
+		g_object_unref (auth);
+	
+	return rv;
+}
+
+GckLogin*
+gck_authenticator_get_login (GckAuthenticator *self)
+{
+	g_return_val_if_fail (GCK_IS_AUTHENTICATOR (self), NULL);
+	return self->pv->login;
+}
+
+void
+gck_authenticator_set_login (GckAuthenticator *self, GckLogin *login)
+{
+	g_return_if_fail (GCK_IS_AUTHENTICATOR (self));
+	
+	if (login) {
+		g_return_if_fail (GCK_IS_LOGIN (login));
+		g_object_ref (login);
+	}
+	if (self->pv->login)
+		g_object_unref (self->pv->login);
+	self->pv->login = login;
+	
+	g_object_notify (G_OBJECT (self), "login");
+}
+
+const gchar*
+gck_authenticator_get_password (GckAuthenticator *self, gsize *n_password)
+{
+	g_return_val_if_fail (GCK_IS_AUTHENTICATOR (self), NULL);
+	g_return_val_if_fail (n_password, NULL);
+	
+	if (!self->pv->login) {
+		*n_password = 0;
+		return NULL;
+	}
+	
+	return gck_login_get_password (self->pv->login, n_password);	
+}
+
+GckObject*
+gck_authenticator_get_object (GckAuthenticator *self)
+{
+	g_return_val_if_fail (GCK_IS_AUTHENTICATOR (self), NULL);
+	g_return_val_if_fail (GCK_IS_OBJECT (self->pv->object), NULL);
+	return self->pv->object;
+}
+
+gint
+gck_authenticator_get_uses_remaining (GckAuthenticator *self)
+{
+	g_return_val_if_fail (GCK_IS_AUTHENTICATOR (self), 0);
+	return self->pv->uses_remaining;
+}
+
+void
+gck_authenticator_set_uses_remaining (GckAuthenticator *self,
+                                      gint use_count)
+{
+	g_return_if_fail (GCK_IS_AUTHENTICATOR (self));
+	g_return_if_fail (use_count != 0);
+	
+	self->pv->uses_remaining = use_count;
+	g_object_notify (G_OBJECT (self), "uses-remaining");
+}
+
+void
+gck_authenticator_throw_away_one_use (GckAuthenticator *self)
+{
+	g_return_if_fail (GCK_IS_AUTHENTICATOR (self));
+	if (self->pv->uses_remaining > 0)
+		--(self->pv->uses_remaining);
+	if (self->pv->uses_remaining == 0)
+		self_destruct (self);
+}
diff --git a/pkcs11/gck/gck-authenticator.h b/pkcs11/gck/gck-authenticator.h
new file mode 100644
index 0000000..e2d75a8
--- /dev/null
+++ b/pkcs11/gck/gck-authenticator.h
@@ -0,0 +1,77 @@
+/* 
+ * 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 __GCK_AUTHENTICATOR_H__
+#define __GCK_AUTHENTICATOR_H__
+
+#include <glib-object.h>
+
+#include "gck-object.h"
+#include "gck-types.h"
+
+#define GCK_FACTORY_AUTHENTICATOR            (gck_authenticator_get_factory ())
+
+#define GCK_TYPE_AUTHENTICATOR               (gck_authenticator_get_type ())
+#define GCK_AUTHENTICATOR(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), GCK_TYPE_AUTHENTICATOR, GckAuthenticator))
+#define GCK_AUTHENTICATOR_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), GCK_TYPE_AUTHENTICATOR, GckAuthenticatorClass))
+#define GCK_IS_AUTHENTICATOR(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GCK_TYPE_AUTHENTICATOR))
+#define GCK_IS_AUTHENTICATOR_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), GCK_TYPE_AUTHENTICATOR))
+#define GCK_AUTHENTICATOR_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), GCK_TYPE_AUTHENTICATOR, GckAuthenticatorClass))
+
+typedef struct _GckAuthenticatorClass GckAuthenticatorClass;
+typedef struct _GckAuthenticatorPrivate GckAuthenticatorPrivate;
+    
+struct _GckAuthenticator {
+	GckObject parent;
+	GckAuthenticatorPrivate *pv;
+};
+
+struct _GckAuthenticatorClass {
+	GckObjectClass parent_class;
+};
+
+GType                      gck_authenticator_get_type               (void);
+
+GckFactoryInfo*            gck_authenticator_get_factory            (void);
+
+CK_RV                      gck_authenticator_create                 (GckObject *object,
+                                                                     CK_UTF8CHAR_PTR pin,
+                                                                     CK_ULONG n_pin,
+                                                                     GckAuthenticator **result);
+
+GckLogin*                  gck_authenticator_get_login              (GckAuthenticator *self);
+
+void                       gck_authenticator_set_login              (GckAuthenticator *self,
+                                                                     GckLogin *login);
+
+const gchar*               gck_authenticator_get_password           (GckAuthenticator *self,
+                                                                     gsize *n_password);
+
+GckObject*                 gck_authenticator_get_object             (GckAuthenticator *self);
+
+gint                       gck_authenticator_get_uses_remaining     (GckAuthenticator *self);
+
+void                       gck_authenticator_set_uses_remaining     (GckAuthenticator *self,
+                                                                     gint use_count);
+
+void                       gck_authenticator_throw_away_one_use     (GckAuthenticator *self);
+
+#endif /* __GCK_AUTHENTICATOR_H__ */
diff --git a/pkcs11/gck/gck-key.c b/pkcs11/gck/gck-key.c
index 289fdbf..bc27704 100644
--- a/pkcs11/gck/gck-key.c
+++ b/pkcs11/gck/gck-key.c
@@ -283,10 +283,10 @@ gck_key_set_key_part (GckKey *self, int algo, const char *part,
 }
 
 GckSexp*
-gck_key_acquire_crypto_sexp (GckKey *self)
+gck_key_acquire_crypto_sexp (GckKey *self, GckSession *session)
 {
 	g_return_val_if_fail (GCK_IS_KEY (self), NULL);
 	g_return_val_if_fail (GCK_KEY_GET_CLASS (self)->acquire_crypto_sexp, NULL);
-	return GCK_KEY_GET_CLASS (self)->acquire_crypto_sexp (self);
+	return GCK_KEY_GET_CLASS (self)->acquire_crypto_sexp (self, session);
 }
 
diff --git a/pkcs11/gck/gck-key.h b/pkcs11/gck/gck-key.h
index de73347..b2ab7f1 100644
--- a/pkcs11/gck/gck-key.h
+++ b/pkcs11/gck/gck-key.h
@@ -48,7 +48,7 @@ struct _GckKeyClass {
 	
 	/* virtual methods */
 
-	GckSexp* (*acquire_crypto_sexp) (GckKey *self);
+	GckSexp* (*acquire_crypto_sexp) (GckKey *self, GckSession *session);
 };
 
 GType                      gck_key_get_type               (void);
@@ -65,6 +65,7 @@ CK_RV                      gck_key_set_key_part           (GckKey *self,
                                                            const char *part,
                                                            CK_ATTRIBUTE_PTR attr);
 
-GckSexp*                   gck_key_acquire_crypto_sexp    (GckKey *self);
+GckSexp*                   gck_key_acquire_crypto_sexp    (GckKey *self,
+                                                           GckSession *session);
 
 #endif /* __GCK_KEY_H__ */
diff --git a/pkcs11/gck/gck-login.c b/pkcs11/gck/gck-login.c
index e197b49..43492b7 100644
--- a/pkcs11/gck/gck-login.c
+++ b/pkcs11/gck/gck-login.c
@@ -131,6 +131,7 @@ const gchar*
 gck_login_get_password (GckLogin *self, gsize *n_password)
 {
 	g_return_val_if_fail (GCK_IS_LOGIN (self), NULL);
+	g_return_val_if_fail (n_password, NULL);
 	*n_password = self->n_password;
 	return self->password;
 }
diff --git a/pkcs11/gck/gck-module.c b/pkcs11/gck/gck-module.c
index f49fdac..5b2d1e9 100644
--- a/pkcs11/gck/gck-module.c
+++ b/pkcs11/gck/gck-module.c
@@ -26,6 +26,7 @@
 #include "pkcs11/pkcs11i.h"
 
 #include "gck-attributes.h"
+#include "gck-authenticator.h"
 #include "gck-certificate.h"
 #include "gck-factory.h"
 #include "gck-manager.h"
@@ -412,14 +413,6 @@ add_transient_object (GckModule *self, GckTransaction *transaction, GckObject *o
 	}
 }
 
-static void
-dispose_unref_object (gpointer obj)
-{
-	g_assert (G_IS_OBJECT (obj));
-	g_object_run_dispose (obj);
-	g_object_unref (obj);
-}
-
 /* -----------------------------------------------------------------------------
  * OBJECT 
  */
@@ -541,12 +534,13 @@ gck_module_init (GckModule *self)
 	
 	/* Create the store for transient objects */
 	self->pv->transient_store = GCK_STORE (gck_memory_store_new ());
-	self->pv->transient_objects = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, dispose_unref_object);
+	self->pv->transient_objects = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, gck_util_dispose_unref);
 
 	/* Register session object factories */
 	gck_module_register_factory (self, GCK_FACTORY_PRIVATE_KEY);
 	gck_module_register_factory (self, GCK_FACTORY_CERTIFICATE);
 	gck_module_register_factory (self, GCK_FACTORY_PUBLIC_KEY);
+	gck_module_register_factory (self, GCK_FACTORY_AUTHENTICATOR);
 }
 
 static void
diff --git a/pkcs11/gck/gck-object.c b/pkcs11/gck/gck-object.c
index a210b53..e1188a9 100644
--- a/pkcs11/gck/gck-object.c
+++ b/pkcs11/gck/gck-object.c
@@ -75,7 +75,6 @@ kaboom_callback (GckTimer *timer, gpointer user_data)
 	GckObject *self = user_data;
 	GckTransaction *transaction;
 	GckObjectTransient *transient;
-	GckSession *session;
 	CK_RV rv;
 
 	g_return_if_fail (GCK_IS_OBJECT (self));
@@ -89,9 +88,8 @@ kaboom_callback (GckTimer *timer, gpointer user_data)
 
 	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));
+	/* Off we go */
+	gck_object_destroy (self, transaction);
 
 	gck_transaction_complete (transaction);
 	rv = gck_transaction_get_result (transaction);
@@ -100,7 +98,6 @@ kaboom_callback (GckTimer *timer, gpointer user_data)
 	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);
 }
 
@@ -130,6 +127,13 @@ module_went_away (gpointer data, GObject *old_module)
 	self->pv->module = NULL;
 }
 
+static gboolean
+complete_destroy (GckTransaction *transaction, GObject *unused, gpointer user_data)
+{
+	gck_util_dispose_unref (user_data);
+	return TRUE;
+}
+
 /* -----------------------------------------------------------------------------
  * OBJECT 
  */
@@ -264,7 +268,7 @@ gck_object_real_create_attributes (GckObject *self, GckTransaction *transaction,
 }
 
 static CK_RV
-gck_object_real_unlock (GckObject *self, CK_UTF8CHAR_PTR pin, CK_ULONG n_pin)
+gck_object_real_unlock (GckObject *self, GckAuthenticator *auth)
 {
 	gboolean always_auth;
 	
@@ -626,11 +630,11 @@ gck_object_get_transient (GckObject *self)
 
 
 CK_RV
-gck_object_unlock (GckObject *self, CK_UTF8CHAR_PTR pin, CK_ULONG n_pin)
+gck_object_unlock (GckObject *self, GckAuthenticator *auth)
 {
 	g_return_val_if_fail (GCK_IS_OBJECT (self), CKR_GENERAL_ERROR);
 	g_return_val_if_fail (GCK_OBJECT_GET_CLASS (self)->unlock, CKR_GENERAL_ERROR);
-	return GCK_OBJECT_GET_CLASS (self)->unlock (self, pin, n_pin);
+	return GCK_OBJECT_GET_CLASS (self)->unlock (self, auth);
 }
 
 
@@ -702,3 +706,33 @@ gck_object_get_attribute_data (GckObject *self, CK_ATTRIBUTE_TYPE type, gsize *n
 	*n_data = attr.ulValueLen;
 	return attr.pValue;
 }
+
+void
+gck_object_destroy (GckObject *self, GckTransaction *transaction)
+{
+	GckSession *session;
+	GckManager *manager;
+	GckModule *module;
+
+	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 (self->pv->module);
+
+	g_object_ref (self);
+
+	session = gck_session_for_session_object (self);
+	if (session != NULL) {
+		gck_session_destroy_session_object (session, transaction, self);
+	} else {
+		manager = gck_object_get_manager (self);
+		module = gck_object_get_module (self);
+		if (manager == gck_module_get_manager (module))
+			gck_module_remove_token_object (module, transaction, self);
+	}
+
+	/* Forcefully dispose of the object once the transaction completes */
+	gck_transaction_add (transaction, NULL, complete_destroy, g_object_ref (self));
+
+	g_object_unref (self);
+}
diff --git a/pkcs11/gck/gck-object.h b/pkcs11/gck/gck-object.h
index 44d79b8..10508e4 100644
--- a/pkcs11/gck/gck-object.h
+++ b/pkcs11/gck/gck-object.h
@@ -59,7 +59,7 @@ struct _GckObjectClass {
 	void (*create_attributes) (GckObject *object, GckTransaction *transaction, GckSession *session,
 	                           CK_ATTRIBUTE *attrs, CK_ULONG n_attrs);
 
-	CK_RV (*unlock) (GckObject *self, CK_UTF8CHAR_PTR pin, CK_ULONG n_pin);
+	CK_RV (*unlock) (GckObject *self, GckAuthenticator *auth);
 };
 
 GType                  gck_object_get_type               (void);
@@ -78,8 +78,10 @@ const gchar*           gck_object_get_unique             (GckObject *self);
 gboolean               gck_object_get_transient          (GckObject *self);
 
 CK_RV                  gck_object_unlock                 (GckObject *self, 
-                                                          CK_UTF8CHAR_PTR pin, 
-                                                          CK_ULONG n_pin);
+                                                          GckAuthenticator *auth);
+
+void                   gck_object_destroy                (GckObject *self,
+                                                          GckTransaction *transaction);
 
 gboolean               gck_object_match                  (GckObject *self,
                                                           CK_ATTRIBUTE_PTR attr);
diff --git a/pkcs11/gck/gck-private-key.c b/pkcs11/gck/gck-private-key.c
index cd86093..ec19bcf 100644
--- a/pkcs11/gck/gck-private-key.c
+++ b/pkcs11/gck/gck-private-key.c
@@ -24,6 +24,7 @@
 #include "pkcs11/pkcs11.h"
 
 #include "gck-attributes.h"
+#include "gck-authenticator.h"
 #include "gck-factory.h"
 #include "gck-private-key.h"
 #include "gck-session.h"
@@ -31,7 +32,6 @@
 #include "gck-util.h"
 
 struct _GckPrivateKeyPrivate {
-	guint sexp_uses;
 	GckSexp *sexp;
 };
 
@@ -147,6 +147,7 @@ static void
 factory_create_private_key (GckSession *session, GckTransaction *transaction, 
                             CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs, GckObject **object)
 {
+	GckPrivateKey *key;
 	GckSexp *sexp;
 	
 	g_return_if_fail (GCK_IS_TRANSACTION (transaction));
@@ -157,10 +158,29 @@ factory_create_private_key (GckSession *session, GckTransaction *transaction,
 	if (sexp == NULL)
 		return;
 	
-	*object = g_object_new (GCK_TYPE_PRIVATE_KEY, "base-sexp", sexp, 
-	                        "module", gck_session_get_module (session), NULL);
-	gck_private_key_store_private (GCK_PRIVATE_KEY (*object), sexp, G_MAXUINT);
-	gck_sexp_unref (sexp);
+	key = g_object_new (GCK_TYPE_PRIVATE_KEY, "base-sexp", sexp,
+	                    "module", gck_session_get_module (session), NULL);
+	key->pv->sexp = sexp;
+	*object = GCK_OBJECT (key);
+}
+
+static gboolean
+acquire_from_authenticator (GckAuthenticator *auth, GckObject *object, gpointer user_data)
+{
+	GckSexp **result = user_data;
+
+	g_assert (result);
+	g_assert (!*result);
+
+	/* The sexp we stored on the authenticator */
+	*result = g_object_get_data (G_OBJECT (auth), "private-key-sexp");
+	if (*result != NULL) {
+		*result = gck_sexp_ref (*result);
+		gck_authenticator_throw_away_one_use (auth);
+		return TRUE;
+	}
+
+	return FALSE;
 }
 
 /* -----------------------------------------------------------------------------
@@ -210,7 +230,7 @@ gck_private_key_real_get_attribute (GckObject *base, CK_ATTRIBUTE* attr)
 		return CKR_ATTRIBUTE_TYPE_INVALID;
 		
 	case CKA_ALWAYS_AUTHENTICATE:
-		return gck_attribute_set_bool (attr, self->pv->sexp_uses <= 1);
+		return gck_attribute_set_bool (attr, self->pv->sexp == NULL);
 		
 	case CKA_MODULUS:
 		return gck_key_set_key_part (GCK_KEY (self), GCRY_PK_RSA, "n", attr);
@@ -245,26 +265,20 @@ gck_private_key_real_get_attribute (GckObject *base, CK_ATTRIBUTE* attr)
 }
 
 static GckSexp*
-gck_private_key_real_acquire_crypto_sexp (GckKey *base)
+gck_private_key_real_acquire_crypto_sexp (GckKey *base, GckSession *session)
 {
 	GckPrivateKey *self = GCK_PRIVATE_KEY (base);
-	GckSexp *sexp;
+	GckSexp *sexp = NULL;
 	
-	if (self->pv->sexp_uses == 0) {
-		g_return_val_if_fail (!self->pv->sexp, NULL);
-		return NULL;
-	}
+	/* We have an unlocked private key here */
+	if (self->pv->sexp)
+		sexp = gck_sexp_ref (self->pv->sexp);
+
+	/* Find an authenticator, with an unlocked copy */
+	else
+		gck_session_for_each_authenticator (session, GCK_OBJECT (self),
+		                                    acquire_from_authenticator, &sexp);
 	
-	g_return_val_if_fail (self->pv->sexp, NULL);
-		
-	sexp = gck_sexp_ref (self->pv->sexp);
-	--(self->pv->sexp_uses);
-		
-	if (self->pv->sexp_uses == 0) {
-		gck_sexp_unref (self->pv->sexp);
-		self->pv->sexp = NULL;
-	}
-		
 	return sexp;
 }
 
@@ -356,19 +370,33 @@ gck_private_key_class_init (GckPrivateKeyClass *klass)
  */
 
 void
-gck_private_key_store_private (GckPrivateKey *self, GckSexp *sexp, guint num_uses)
+gck_private_key_set_unlocked_private (GckPrivateKey *self, GckSexp *sexp)
 {
 	g_return_if_fail (GCK_IS_PRIVATE_KEY (self));
-	g_return_if_fail (!sexp || num_uses);
-	
+	g_return_if_fail (sexp);
+
 	if (sexp)
 		gck_sexp_ref (sexp);
-	if (self->pv->sexp) 
+	if (self->pv->sexp)
 		gck_sexp_unref (self->pv->sexp);
 	self->pv->sexp = sexp;
-	self->pv->sexp_uses = num_uses;
 }
 
+void
+gck_private_key_set_locked_private (GckPrivateKey *self, GckAuthenticator *auth, 
+                                    GckSexp *sexp, gint num_uses)
+{
+	g_return_if_fail (GCK_IS_PRIVATE_KEY (self));
+	g_return_if_fail (GCK_IS_AUTHENTICATOR (auth));
+	g_return_if_fail (!sexp || num_uses);
+
+	if (sexp == NULL)
+		g_object_set_data (G_OBJECT (auth), "private-key-sexp", NULL);
+	else
+		g_object_set_data_full (G_OBJECT (auth), "private-key-sexp",
+		                        gck_sexp_ref (sexp), gck_sexp_unref);
+	gck_authenticator_set_uses_remaining (auth, num_uses);
+}
 
 GckSexp*
 gck_private_key_create_sexp (GckSession *session, GckTransaction *transaction, 
diff --git a/pkcs11/gck/gck-private-key.h b/pkcs11/gck/gck-private-key.h
index f68ec59..4a7a450 100644
--- a/pkcs11/gck/gck-private-key.h
+++ b/pkcs11/gck/gck-private-key.h
@@ -50,9 +50,13 @@ struct _GckPrivateKeyClass {
 
 GType                      gck_private_key_get_type               (void);
 
-void                       gck_private_key_store_private          (GckPrivateKey *self, 
+void                       gck_private_key_set_unlocked_private   (GckPrivateKey *self,
+                                                                   GckSexp *sexp);
+
+void                       gck_private_key_set_locked_private     (GckPrivateKey *self,
+                                                                   GckAuthenticator *auth,
                                                                    GckSexp *sexp, 
-                                                                   guint num_uses);               
+                                                                   gint num_uses);               
 
 GckFactoryInfo*            gck_private_key_get_factory            (void);
 
diff --git a/pkcs11/gck/gck-public-key.c b/pkcs11/gck/gck-public-key.c
index 404474c..ce2fe48 100644
--- a/pkcs11/gck/gck-public-key.c
+++ b/pkcs11/gck/gck-public-key.c
@@ -216,7 +216,7 @@ gck_public_key_real_get_attribute (GckObject *base, CK_ATTRIBUTE* attr)
 }
 
 static GckSexp*
-gck_public_key_acquire_crypto_sexp (GckKey *self)
+gck_public_key_acquire_crypto_sexp (GckKey *self, GckSession *session)
 {
 	GckSexp* sexp;
 	
diff --git a/pkcs11/gck/gck-session.c b/pkcs11/gck/gck-session.c
index dd4e70a..6c95535 100644
--- a/pkcs11/gck/gck-session.c
+++ b/pkcs11/gck/gck-session.c
@@ -25,6 +25,7 @@
 #include "pkcs11/pkcs11i.h"
 
 #include "gck-attributes.h"
+#include "gck-authenticator.h"
 #include "gck-crypto.h"
 #include "gck-key.h"
 #include "gck-factory.h"
@@ -33,6 +34,7 @@
 #include "gck-session.h"
 #include "gck-sexp.h"
 #include "gck-transaction.h"
+#include "gck-util.h"
 
 enum {
 	PROP_0,
@@ -62,9 +64,10 @@ struct _GckSessionPrivate {
 	/* Objects owned by this session */
 	GHashTable *objects;
 
-	/* Used for context specific logins */
+	/* Used for operations */
 	void (*current_operation) (GckSession *self);
 	GckObject *current_object;
+	GckAuthenticator *authenticator;
 
 	/* Used for find operations */
 	GArray *found_objects;
@@ -100,8 +103,14 @@ cleanup_crypto (GckSession *self)
 	g_assert (GCK_IS_KEY (self->pv->current_object));
 	if (self->pv->current_object)
 		g_object_unref (self->pv->current_object);
-	
 	self->pv->current_object = NULL;
+
+	if (self->pv->authenticator) {
+		g_object_set_data (G_OBJECT (self->pv->authenticator), "owned-by-session", NULL);
+		g_object_unref (self->pv->authenticator);
+		self->pv->authenticator = NULL;
+	}
+
 	self->pv->current_operation = NULL;
 }
 
@@ -186,7 +195,7 @@ process_crypto (GckSession *self, CK_ATTRIBUTE_TYPE method, CK_BYTE_PTR bufone,
 		/* Load up the actual sexp we're going to use */
 		if (!self->pv->crypto_sexp) {
 			g_return_val_if_fail (GCK_IS_KEY (self->pv->current_object), CKR_GENERAL_ERROR);
-			self->pv->crypto_sexp = gck_key_acquire_crypto_sexp (GCK_KEY (self->pv->current_object));
+			self->pv->crypto_sexp = gck_key_acquire_crypto_sexp (GCK_KEY (self->pv->current_object), self);
 			if (!self->pv->crypto_sexp)
 				rv = CKR_USER_NOT_LOGGED_IN;
 		}
@@ -345,14 +354,6 @@ add_object (GckSession *self, GckTransaction *transaction, GckObject *object)
 		                     g_object_ref (object));
 }
 
-static void
-dispose_unref_object (gpointer obj)
-{
-	g_assert (G_IS_OBJECT (obj));
-	g_object_run_dispose (obj);
-	g_object_unref (obj);
-}
-
 static gboolean
 attributes_find_boolean (CK_ATTRIBUTE_PTR attrs, CK_ULONG n_attrs, 
                          CK_ATTRIBUTE_TYPE type, CK_BBOOL *value)
@@ -400,7 +401,7 @@ static void
 gck_session_init (GckSession *self)
 {
 	self->pv = G_TYPE_INSTANCE_GET_PRIVATE (self, GCK_TYPE_SESSION, GckSessionPrivate);
-	self->pv->objects = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, dispose_unref_object);
+	self->pv->objects = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, gck_util_dispose_unref);
 	self->pv->read_only = TRUE;
 	
 	/* Create the store and register attributes */
@@ -421,6 +422,12 @@ gck_session_dispose (GObject *obj)
 		g_object_unref (self->pv->module);
 	self->pv->module = NULL;
 
+	if (self->pv->authenticator) {
+		g_object_set_data (G_OBJECT (self->pv->authenticator), "owned-by-session", NULL);
+		g_object_unref (self->pv->authenticator);
+		self->pv->authenticator = NULL;
+	}
+
 	if (self->pv->manager)
 		g_object_unref (self->pv->manager);
 	self->pv->manager = NULL;
@@ -635,9 +642,11 @@ gck_session_lookup_writable_object (GckSession *self, CK_OBJECT_HANDLE handle,
 CK_RV
 gck_session_login_context_specific (GckSession *self, CK_UTF8CHAR_PTR pin, CK_ULONG n_pin)
 {
+	GckAuthenticator *authenticator;
 	gboolean always_auth;
 	gboolean is_private;
 	GckObject *object;
+	CK_RV rv;
 	
 	g_return_val_if_fail (GCK_IS_SESSION (self), CKR_GENERAL_ERROR);
 
@@ -658,8 +667,18 @@ gck_session_login_context_specific (GckSession *self, CK_UTF8CHAR_PTR pin, CK_UL
 	
 	/* Double check that the object has what it takes */
 	g_return_val_if_fail (is_private == TRUE, CKR_GENERAL_ERROR);
-	
-	return gck_object_unlock (object, pin, n_pin);
+
+	/* Now create the strange object */
+	rv = gck_authenticator_create (self->pv->current_object, pin, n_pin, &authenticator);
+	if (rv != CKR_OK)
+		return rv;
+
+	if (self->pv->authenticator)
+		g_object_unref (self->pv->authenticator);
+	g_object_set_data (G_OBJECT (authenticator), "owned-by-session", self);
+	self->pv->authenticator = authenticator;
+
+	return CKR_OK;
 }
 
 void
@@ -671,9 +690,69 @@ gck_session_destroy_session_object (GckSession *self, GckTransaction *transactio
 	g_return_if_fail (GCK_IS_TRANSACTION (transaction));
 	g_return_if_fail (!gck_transaction_get_failed (transaction));
 
+	/* Don't actually destroy the authenticator */
+	if (self->pv->authenticator && GCK_OBJECT (self->pv->authenticator) == obj)
+		return;
+
 	remove_object (self, transaction, obj);
 }
 
+void
+gck_session_for_each_authenticator (GckSession *self, GckObject *object,
+                                    GckAuthenticatorFunc func, gpointer user_data)
+{
+	CK_OBJECT_HANDLE handle;
+	CK_OBJECT_CLASS klass;
+	CK_ATTRIBUTE attrs[2];
+	GList *results, *l;
+
+	g_return_if_fail (GCK_IS_SESSION (self));
+	g_return_if_fail (GCK_IS_OBJECT (object));
+	g_return_if_fail (func);
+
+	/* Do we have one right on the session */
+	if (self->pv->authenticator != NULL &&
+	    gck_authenticator_get_object (self->pv->authenticator) == object) {
+		if ((func) (self->pv->authenticator, object, user_data))
+			return;
+	}
+
+	klass = CKO_GNOME_AUTHENTICATOR;
+	attrs[0].type = CKA_CLASS;
+	attrs[0].pValue = &klass;
+	attrs[0].ulValueLen = sizeof (klass);
+
+	handle = gck_object_get_handle (object);
+	attrs[1].type = CKA_GNOME_OBJECT;
+	attrs[1].pValue = &handle;
+	attrs[1].ulValueLen = sizeof (handle);
+
+	/* Find any on the session */
+	results = gck_manager_find_by_attributes (self->pv->manager,
+	                                          attrs, G_N_ELEMENTS (attrs));
+
+	for (l = results; l; l = g_list_next (l)) {
+		if ((func) (l->data, object, user_data))
+			break;
+	}
+
+	g_list_free (results);
+
+	if (l != NULL)
+		return;
+
+	/* Find any in the token */
+	results = gck_manager_find_by_attributes (gck_module_get_manager (self->pv->module), 
+	                                          attrs, G_N_ELEMENTS (attrs));
+
+	for (l = results; l; l = g_list_next (l)) {
+		if ((func) (l->data, object, user_data))
+			break;
+	}
+
+	g_list_free (results);
+}
+
 /* -----------------------------------------------------------------------------
  * PKCS#11
  */
diff --git a/pkcs11/gck/gck-session.h b/pkcs11/gck/gck-session.h
index 815e62c..697e925 100644
--- a/pkcs11/gck/gck-session.h
+++ b/pkcs11/gck/gck-session.h
@@ -52,6 +52,10 @@ struct _GckSessionClass {
 #endif
 };
 
+typedef gboolean         (*GckAuthenticatorFunc)                        (GckAuthenticator *auth,
+                                                                         GckObject *object,
+                                                                         gpointer user_data);
+
 GType                    gck_session_get_type                           (void);
 
 GckSession*              gck_session_new                                (GckModule *module, 
@@ -92,6 +96,10 @@ void                     gck_session_destroy_session_object             (GckSess
                                                                          GckTransaction *transaction,
                                                                          GckObject *obj);
 
+void                     gck_session_for_each_authenticator             (GckSession *self,
+                                                                         GckObject *object,
+                                                                         GckAuthenticatorFunc func,
+                                                                         gpointer user_data);
 
 
 
diff --git a/pkcs11/gck/gck-sexp.c b/pkcs11/gck/gck-sexp.c
index 70f275f..a15549d 100644
--- a/pkcs11/gck/gck-sexp.c
+++ b/pkcs11/gck/gck-sexp.c
@@ -48,8 +48,9 @@ gck_sexp_ref (GckSexp *sexp)
 }
 
 void
-gck_sexp_unref (GckSexp *sexp)
+gck_sexp_unref (gpointer data)
 {
+	GckSexp *sexp = data;
 	g_return_if_fail (sexp);
 	if (--(sexp->refs) == 0) {
 		g_assert (sexp->real);
diff --git a/pkcs11/gck/gck-sexp.h b/pkcs11/gck/gck-sexp.h
index e338829..e3f9089 100644
--- a/pkcs11/gck/gck-sexp.h
+++ b/pkcs11/gck/gck-sexp.h
@@ -32,7 +32,7 @@ GckSexp*       gck_sexp_new           (gcry_sexp_t sexp);
 
 GckSexp*       gck_sexp_ref           (GckSexp *sexp);
 
-void           gck_sexp_unref         (GckSexp *sexp);
+void           gck_sexp_unref         (gpointer sexp);
 
 gcry_sexp_t    gck_sexp_get           (GckSexp *sexp);
 
diff --git a/pkcs11/gck/gck-types.h b/pkcs11/gck/gck-types.h
index 67ae9de..ea27e6d 100644
--- a/pkcs11/gck/gck-types.h
+++ b/pkcs11/gck/gck-types.h
@@ -22,6 +22,7 @@
 #ifndef __GCK_TYPES_H__
 #define __GCK_TYPES_H__
 
+typedef struct _GckAuthenticator GckAuthenticator;
 typedef struct _GckCertificate GckCertificate;
 typedef struct _GckCertificateKey GckCertificateKey;
 typedef struct _GckCertificateTrust GckCertificateTrust;
diff --git a/pkcs11/gck/gck-util.c b/pkcs11/gck/gck-util.c
index 6e807fd..c6439b9 100644
--- a/pkcs11/gck/gck-util.c
+++ b/pkcs11/gck/gck-util.c
@@ -23,6 +23,9 @@
 
 #include "gck-util.h"
 
+#include <glib.h>
+#include <glib-object.h>
+
 #include <stdio.h>
 #include <string.h>
 
@@ -87,3 +90,11 @@ gck_util_next_handle (void)
 {
 	return (CK_ULONG)g_atomic_int_exchange_and_add (&next_handle, 1);
 }
+
+void
+gck_util_dispose_unref (gpointer object)
+{
+	g_return_if_fail (G_IS_OBJECT (object));
+	g_object_run_dispose (G_OBJECT (object));
+	g_object_unref (object);
+}
diff --git a/pkcs11/gck/gck-util.h b/pkcs11/gck/gck-util.h
index 97e3556..18be87e 100644
--- a/pkcs11/gck/gck-util.h
+++ b/pkcs11/gck/gck-util.h
@@ -47,4 +47,6 @@ CK_RV                 gck_attribute_set_mpi                       (CK_ATTRIBUTE_
 
 CK_ULONG              gck_util_next_handle                        (void);
 
+void                  gck_util_dispose_unref                      (gpointer object); 
+
 #endif /* GCKUTIL_H_ */
diff --git a/pkcs11/gck/tests/Makefile.am b/pkcs11/gck/tests/Makefile.am
index 4bd2b10..96fe68e 100644
--- a/pkcs11/gck/tests/Makefile.am
+++ b/pkcs11/gck/tests/Makefile.am
@@ -12,6 +12,7 @@ UNIT_AUTO = \
 	unit-test-data-asn1.c \
 	unit-test-data-der.c \
 	unit-test-object.c \
+	unit-test-authenticator.c \
 	unit-test-timer.c \
 	unit-test-transaction.c \
 	unit-test-store.c \
@@ -19,6 +20,7 @@ UNIT_AUTO = \
 	unit-test-login.c \
 	unit-test-data-file.c \
 	unit-test-file-tracker.c \
+	mock-locked-object.c mock-locked-object.h \
 	test-module.c test-module.h \
 	$(BUILT_SOURCES)
 
diff --git a/pkcs11/gck/tests/mock-locked-object.c b/pkcs11/gck/tests/mock-locked-object.c
new file mode 100644
index 0000000..882d100
--- /dev/null
+++ b/pkcs11/gck/tests/mock-locked-object.c
@@ -0,0 +1,88 @@
+/* 
+ * 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 "mock-locked-object.h"
+
+#include "gck/gck-attributes.h"
+#include "gck/gck-authenticator.h"
+
+G_DEFINE_TYPE (MockLockedObject, mock_locked_object, GCK_TYPE_OBJECT);
+
+/* -----------------------------------------------------------------------------
+ * INTERNAL 
+ */
+
+/* -----------------------------------------------------------------------------
+ * KEY 
+ */
+
+static CK_RV 
+mock_locked_object_real_get_attribute (GckObject *base, CK_ATTRIBUTE* attr)
+{
+	switch (attr->type) {
+	case CKA_CLASS:
+		return gck_attribute_set_ulong (attr, CKO_DATA);
+	case CKA_ALWAYS_AUTHENTICATE:
+		return gck_attribute_set_bool (attr, TRUE);
+	};
+
+	return GCK_OBJECT_CLASS (mock_locked_object_parent_class)->get_attribute (base, attr);
+}
+
+static CK_RV
+mock_locked_object_real_unlock (GckObject *base, GckAuthenticator *auth)
+{
+	const gchar *password;
+	gsize n_password;
+	
+	password = gck_authenticator_get_password (auth, &n_password);
+	if (n_password == 4 && memcmp (password, "mock", 4) == 0)
+		return CKR_OK;
+	
+	return CKR_USER_NOT_LOGGED_IN;
+}
+
+static void
+mock_locked_object_init (MockLockedObject *self)
+{
+
+}
+
+static void
+mock_locked_object_class_init (MockLockedObjectClass *klass)
+{
+	GckObjectClass *gck_class = GCK_OBJECT_CLASS (klass);
+	mock_locked_object_parent_class = g_type_class_peek_parent (klass);
+	gck_class->get_attribute = mock_locked_object_real_get_attribute;
+	gck_class->unlock = mock_locked_object_real_unlock;
+}
+
+/* -----------------------------------------------------------------------------
+ * PUBLIC 
+ */
+
+GckObject*
+mock_locked_object_new (GckModule *module)
+{
+	return g_object_new (MOCK_TYPE_LOCKED_OBJECT, "module", module, NULL);
+}
diff --git a/pkcs11/gck/tests/mock-locked-object.h b/pkcs11/gck/tests/mock-locked-object.h
new file mode 100644
index 0000000..0ed6080
--- /dev/null
+++ b/pkcs11/gck/tests/mock-locked-object.h
@@ -0,0 +1,52 @@
+/* 
+ * 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 __MOCK_LOCKED_OBJECT_H__
+#define __MOCK_LOCKED_OBJECT_H__
+
+#include <glib-object.h>
+
+#include "gck-object.h"
+#include "gck-types.h"
+
+#define MOCK_TYPE_LOCKED_OBJECT               (mock_locked_object_get_type ())
+#define MOCK_LOCKED_OBJECT(obj)               (G_TYPE_CHECK_INSTANCE_CAST ((obj), MOCK_TYPE_LOCKED_OBJECT, MockLockedObject))
+#define MOCK_LOCKED_OBJECT_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST ((klass), MOCK_TYPE_LOCKED_OBJECT, MockLockedObjectClass))
+#define MOCK_IS_LOCKED_OBJECT(obj)            (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MOCK_TYPE_LOCKED_OBJECT))
+#define MOCK_IS_LOCKED_OBJECT_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE ((klass), MOCK_TYPE_LOCKED_OBJECT))
+#define MOCK_LOCKED_OBJECT_GET_CLASS(obj)     (G_TYPE_INSTANCE_GET_CLASS ((obj), MOCK_TYPE_LOCKED_OBJECT, MockLockedObjectClass))
+
+typedef struct _MockLockedObject MockLockedObject;
+typedef struct _MockLockedObjectClass MockLockedObjectClass;
+    
+struct _MockLockedObject {
+	GckObject parent;
+};
+
+struct _MockLockedObjectClass {
+	GckObjectClass parent_class;
+};
+
+GType                      mock_locked_object_get_type               (void);
+
+GckObject*                 mock_locked_object_new                    (GckModule *module);
+
+#endif /* __MOCK_LOCKED_OBJECT_H__ */
diff --git a/pkcs11/gck/tests/test-module.c b/pkcs11/gck/tests/test-module.c
index cb2f395..e36071d 100644
--- a/pkcs11/gck/tests/test-module.c
+++ b/pkcs11/gck/tests/test-module.c
@@ -28,15 +28,8 @@
 #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)
+test_module_initialize_and_enter (void)
 {
 	CK_RV rv;
 	
@@ -45,14 +38,17 @@ test_module_initialize (void)
 	g_return_val_if_fail (rv == CKR_OK, NULL);
 	
 	g_return_val_if_fail (pkcs11_module, NULL);
+
+	test_module_enter ();
 	return pkcs11_module;
 }
 
 void
-test_module_finalize (void)
+test_module_leave_and_finalize (void)
 {
 	CK_RV rv;
 	
+	test_module_leave ();
 	rv = test_module_function_list->C_Finalize (NULL);
 	g_return_if_fail (rv == CKR_OK);
 }
@@ -68,3 +64,23 @@ test_module_enter (void)
 {
 	g_static_mutex_lock (&pkcs11_module_mutex);
 }
+
+GckSession*
+test_module_open_session (gboolean writable)
+{
+	CK_ULONG flags = CKF_SERIAL_SESSION;
+	CK_SESSION_HANDLE handle;
+	GckSession *session;
+	CK_RV rv;
+
+	if (writable)
+		flags |= CKF_RW_SESSION;
+
+	rv = gck_module_C_OpenSession (pkcs11_module, 1, flags, NULL, NULL, &handle);
+	g_assert (rv == CKR_OK);
+
+	session = gck_module_lookup_session (pkcs11_module, handle);
+	g_assert (session);
+
+	return session;
+}
diff --git a/pkcs11/gck/tests/test-module.h b/pkcs11/gck/tests/test-module.h
index 8a8ba3f..631dd5b 100644
--- a/pkcs11/gck/tests/test-module.h
+++ b/pkcs11/gck/tests/test-module.h
@@ -24,18 +24,20 @@
 #ifndef TESTMODULE_H_
 #define TESTMODULE_H_
 
+#include <glib.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);
+GckModule*             test_module_initialize_and_enter     (void);
+
+void                   test_module_leave_and_finalize       (void);
 
-void                   test_module_finalize                 (void);
+GckSession*            test_module_open_session             (gboolean writable);
 
 #endif /* TESTMODULE_H_ */
diff --git a/pkcs11/gck/tests/unit-test-authenticator.c b/pkcs11/gck/tests/unit-test-authenticator.c
new file mode 100644
index 0000000..ff2fe14
--- /dev/null
+++ b/pkcs11/gck/tests/unit-test-authenticator.c
@@ -0,0 +1,254 @@
+/* -*- 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 "mock-locked-object.h"
+
+#include "gck/gck-attributes.h"
+#include "gck/gck-authenticator.h"
+#include "gck/gck-login.h"
+#include "gck/gck-object.h"
+#include "gck/gck-session.h"
+#include "gck/gck-module.h"
+
+#include "pkcs11g.h"
+
+static GckModule *module = NULL;
+static GckSession *session = NULL;
+static GckObject *object = NULL;
+
+DEFINE_SETUP(authenticator_setup)
+{
+	CK_RV rv;
+	module = test_module_initialize_and_enter ();
+	session = test_module_open_session (TRUE);
+	
+	rv = gck_module_C_Login (module, gck_session_get_handle (session), CKU_USER, NULL, 0);
+	g_assert (rv == CKR_OK);
+	
+	object = mock_locked_object_new (module);
+	gck_manager_register_object (gck_module_get_manager (module), object);
+}
+
+DEFINE_TEARDOWN(authenticator_teardown)
+{
+	g_object_unref (object);
+	object = NULL;
+	
+	test_module_leave_and_finalize ();
+	module = NULL;
+	session = NULL;
+}
+
+DEFINE_TEST(authenticator_create)
+{
+	CK_OBJECT_CLASS klass = CKO_GNOME_AUTHENTICATOR;
+	CK_OBJECT_HANDLE locked = gck_object_get_handle (object);
+	
+	CK_ATTRIBUTE attrs[] = {
+		{ CKA_CLASS, &klass, sizeof (klass) },
+		{ CKA_GNOME_OBJECT, &locked, sizeof (locked) },
+		{ CKA_VALUE, "mock", 4 },
+	};
+	
+	CK_OBJECT_HANDLE handle;
+	CK_RV rv;
+	
+	rv = gck_session_C_CreateObject (session, attrs, G_N_ELEMENTS (attrs), &handle);
+	g_assert (rv == CKR_OK);
+	g_assert (handle != 0);
+	
+	rv = gck_session_C_DestroyObject (session, handle);
+	g_assert (rv == CKR_OK);
+}
+
+DEFINE_TEST(authenticator_create_missing_pin)
+{
+	CK_OBJECT_CLASS klass = CKO_GNOME_AUTHENTICATOR;
+	CK_OBJECT_HANDLE locked = gck_object_get_handle (object);
+	
+	CK_ATTRIBUTE attrs[] = {
+		{ CKA_CLASS, &klass, sizeof (klass) },
+		{ CKA_GNOME_OBJECT, &locked, sizeof (locked) },
+	};
+	
+	CK_OBJECT_HANDLE handle;
+	CK_RV rv;
+	
+	rv = gck_session_C_CreateObject (session, attrs, G_N_ELEMENTS (attrs), &handle);
+	g_assert (rv == CKR_USER_NOT_LOGGED_IN);
+}
+
+DEFINE_TEST(authenticator_create_no_object)
+{
+	CK_OBJECT_CLASS klass = CKO_GNOME_AUTHENTICATOR;
+	CK_BBOOL token = CK_FALSE;
+	
+	CK_ATTRIBUTE attrs[] = {
+	        { CKA_TOKEN, &token, sizeof (token) },
+		{ CKA_CLASS, &klass, sizeof (klass) },
+	};
+	
+	CK_OBJECT_HANDLE handle;
+	CK_RV rv;
+	
+	rv = gck_session_C_CreateObject (session, attrs, G_N_ELEMENTS (attrs), &handle);
+	g_assert (rv == CKR_TEMPLATE_INCOMPLETE);
+}
+
+DEFINE_TEST(authenticator_create_invalid_object)
+{
+	CK_OBJECT_CLASS klass = CKO_GNOME_AUTHENTICATOR;
+	CK_OBJECT_HANDLE locked = 0;
+	CK_BBOOL token = CK_FALSE;
+	
+	CK_ATTRIBUTE attrs[] = {
+	        { CKA_TOKEN, &token, sizeof (token) },
+		{ CKA_CLASS, &klass, sizeof (klass) },
+		{ CKA_GNOME_OBJECT, &locked, sizeof (locked) },
+	};
+	
+	CK_OBJECT_HANDLE handle;
+	CK_RV rv;
+	
+	rv = gck_session_C_CreateObject (session, attrs, G_N_ELEMENTS (attrs), &handle);
+	g_assert (rv == CKR_OBJECT_HANDLE_INVALID);
+}
+
+DEFINE_TEST(authenticator_get_attributes)
+{
+	CK_OBJECT_CLASS klass = CKO_GNOME_AUTHENTICATOR;
+	CK_OBJECT_HANDLE locked = gck_object_get_handle (object);
+	
+	CK_ATTRIBUTE attrs[] = {
+		{ CKA_CLASS, &klass, sizeof (klass) },
+		{ CKA_GNOME_OBJECT, &locked, sizeof (locked) },
+		{ CKA_VALUE, "mock", 4 },
+	};
+	
+	CK_OBJECT_HANDLE handle;
+	CK_ATTRIBUTE check;
+	CK_ULONG value;
+	CK_RV rv;
+	
+	rv = gck_session_C_CreateObject (session, attrs, G_N_ELEMENTS (attrs), &handle);
+	g_assert (rv == CKR_OK);
+	g_assert (handle != 0);
+
+	check.type = CKA_GNOME_OBJECT;
+	check.pValue = &value;
+	check.ulValueLen = sizeof (value);
+	
+	rv = gck_session_C_GetAttributeValue (session, handle, &check, 1);
+	g_assert (rv == CKR_OK);
+	g_assert (check.ulValueLen == sizeof (value));
+	g_assert (value == locked);
+
+	check.type = CKA_GNOME_USES_REMAINING;
+	check.pValue = &value;
+	check.ulValueLen = sizeof (value);
+	
+	rv = gck_session_C_GetAttributeValue (session, handle, &check, 1);
+	g_assert (rv == CKR_OK);
+	g_assert (check.ulValueLen == sizeof (value));
+	g_assert (value == (CK_ULONG)-1);
+}
+
+DEFINE_TEST(authenticator_uses_property)
+{
+	GckAuthenticator *auth;
+	gint uses;
+	CK_RV rv;
+	
+	rv = gck_authenticator_create (object, (guchar*)"mock", 4, &auth);
+	g_assert (rv == CKR_OK);
+	g_assert (auth);
+	
+	g_object_get (auth, "uses-remaining", &uses, NULL);
+	g_assert (uses == -1);
+
+	gck_authenticator_set_uses_remaining (auth, 5);
+
+	uses = gck_authenticator_get_uses_remaining (auth);
+	g_assert (uses == 5);
+	
+	gck_authenticator_throw_away_one_use (auth);
+	uses = gck_authenticator_get_uses_remaining (auth);
+	g_assert (uses == 4);
+	
+	g_object_unref (auth);
+}
+
+DEFINE_TEST(authenticator_object_property)
+{
+	GckAuthenticator *auth;
+	GckObject *check;
+	CK_RV rv;
+	
+	rv = gck_authenticator_create (object, (guchar*)"mock", 4, &auth);
+	g_assert (rv == CKR_OK);
+	g_assert (auth);
+	
+	g_object_get (auth, "object", &check, NULL);
+	g_assert (check == object);
+	g_object_unref (check);
+
+	check = gck_authenticator_get_object (auth);
+	g_assert (check == object);
+	
+	g_object_unref (auth);
+}
+
+DEFINE_TEST(authenticator_login_property)
+{
+	GckAuthenticator *auth;
+	GckLogin *check, *login;
+	const gchar *password;
+	gsize n_password;
+	CK_RV rv;
+	
+	rv = gck_authenticator_create (object, (guchar*)"mock", 4, &auth);
+	g_assert (rv == CKR_OK);
+	g_assert (auth);
+	
+	g_object_get (auth, "login", &check, NULL);
+	g_assert (check);
+	password = gck_login_get_password (check, &n_password);
+	g_assert (n_password == 4);
+	g_assert (memcmp (password, "mock", 4) == 0);
+	g_object_unref (check);
+
+	check = gck_authenticator_get_login (auth);
+	g_assert (n_password == 4);
+	g_assert (memcmp (password, "mock", 4) == 0);
+	
+	login = gck_login_new ((guchar*)"xxx", -1);
+	gck_authenticator_set_login (auth, login);
+	check = gck_authenticator_get_login (auth);
+	g_assert (n_password == 4);
+	g_assert (memcmp (password, "mock", 4) == 0);
+	g_object_unref (login);
+	
+	g_object_unref (auth);
+}
diff --git a/pkcs11/gck/tests/unit-test-memory-store.c b/pkcs11/gck/tests/unit-test-memory-store.c
index 880e867..162a15f 100644
--- a/pkcs11/gck/tests/unit-test-memory-store.c
+++ b/pkcs11/gck/tests/unit-test-memory-store.c
@@ -64,8 +64,7 @@ DEFINE_SETUP(memory_store)
 	CK_ATTRIBUTE attr;
 	CK_ULONG twentyfour = 24;
 
-	module = test_module_initialize ();
-	test_module_enter ();
+	module = test_module_initialize_and_enter ();
 	
 	attr.type = CKA_LABEL;
 	attr.pValue = "label";
@@ -105,8 +104,7 @@ DEFINE_TEARDOWN(memory_store)
 		g_object_unref (object);
 	object = NULL;
 	
-	test_module_leave ();
-	test_module_finalize ();
+	test_module_leave_and_finalize ();
 	module = NULL;
 }
 
diff --git a/pkcs11/gck/tests/unit-test-object.c b/pkcs11/gck/tests/unit-test-object.c
index d55f36d..3cb323a 100644
--- a/pkcs11/gck/tests/unit-test-object.c
+++ b/pkcs11/gck/tests/unit-test-object.c
@@ -38,35 +38,18 @@ static gsize certificate_n_data = 0;
 
 DEFINE_SETUP(object_setup)
 {
-	CK_SESSION_HANDLE handle;
-	CK_RV rv;
-	
-	module = test_module_initialize ();
-	test_module_enter ();
-	
-	rv = gck_module_C_OpenSession (module, 1, CKF_SERIAL_SESSION | CKF_RW_SESSION, 
-	                               NULL, NULL, &handle);
-	g_assert (rv == CKR_OK);
-	
-	session = gck_module_lookup_session (module, handle);
-	g_assert (session);
-	
+	module = test_module_initialize_and_enter ();
+	session = test_module_open_session (TRUE);
 	certificate_data = test_read_testdata ("test-certificate-1.der", &certificate_n_data);
 }
 
 DEFINE_TEARDOWN(object_teardown)
 {
-	CK_RV rv;
-	
 	g_free (certificate_data);
 	certificate_data = NULL;
 	certificate_n_data = 0;
 	
-	rv = gck_module_C_CloseAllSessions (module, 1);
-	g_assert (rv == CKR_OK);
-	
-	test_module_leave ();
-	test_module_finalize ();
+	test_module_leave_and_finalize ();
 	module = NULL;
 	session = NULL;
 }
diff --git a/pkcs11/gck/tests/unit-test-store.c b/pkcs11/gck/tests/unit-test-store.c
index a53fd14..5096677 100644
--- a/pkcs11/gck/tests/unit-test-store.c
+++ b/pkcs11/gck/tests/unit-test-store.c
@@ -36,9 +36,7 @@ static GckStore *store = NULL;
 
 DEFINE_SETUP(store)
 {
-	module = test_module_initialize ();
-	test_module_enter ();
-	
+	module = test_module_initialize_and_enter ();
 	store = g_object_new (GCK_TYPE_STORE, NULL);
 }
 
@@ -47,8 +45,7 @@ DEFINE_TEARDOWN(store)
 	g_object_unref (store);
 	store = NULL;
 	
-	test_module_leave ();
-	test_module_finalize ();
+	test_module_leave_and_finalize ();
 	module = NULL;
 }
 
diff --git a/pkcs11/gck/tests/unit-test-timer.c b/pkcs11/gck/tests/unit-test-timer.c
index d463041..f9b42cf 100644
--- a/pkcs11/gck/tests/unit-test-timer.c
+++ b/pkcs11/gck/tests/unit-test-timer.c
@@ -30,14 +30,12 @@ static GckModule *module = NULL;
 
 DEFINE_SETUP(timer_setup)
 {
-	module = test_module_initialize ();
-	test_module_enter ();
+	module = test_module_initialize_and_enter ();
 }
 
 DEFINE_TEARDOWN(timer_teardown)
 {
-	test_module_leave ();
-	test_module_finalize ();
+	test_module_leave_and_finalize ();
 }
 
 DEFINE_TEST(timer_extra_initialize)
diff --git a/pkcs11/pkcs11g.h b/pkcs11/pkcs11g.h
index da84c40..4707a34 100644
--- a/pkcs11/pkcs11g.h
+++ b/pkcs11/pkcs11g.h
@@ -101,8 +101,18 @@
  * AUTO-DESTRUCT
  */
 
-#define CKA_GNOME_AUTO_DESTRUCT                  (CKO_GNOME + 200)
+#define CKA_GNOME_AUTO_DESTRUCT                  (CKA_GNOME + 200)
 
-#define CKA_GNOME_TRANSIENT                      (CKO_GNOME + 201)
+#define CKA_GNOME_TRANSIENT                      (CKA_GNOME + 201)
+
+/* -------------------------------------------------------------------
+ * AUTHENTICATOR
+ */
+
+#define CKO_GNOME_AUTHENTICATOR                  (CKO_GNOME + 100)
+
+#define CKA_GNOME_OBJECT                         (CKA_GNOME + 202)
+
+#define CKA_GNOME_USES_REMAINING                 (CKA_GNOME + 203)
 
 #endif /* PKCS11G_H */
diff --git a/pkcs11/ssh-store/gck-ssh-module.c b/pkcs11/ssh-store/gck-ssh-module.c
index f2fa2e6..2dbe909 100644
--- a/pkcs11/ssh-store/gck-ssh-module.c
+++ b/pkcs11/ssh-store/gck-ssh-module.c
@@ -68,6 +68,8 @@ static const CK_TOKEN_INFO gck_ssh_module_token_info = {
 
 G_DEFINE_TYPE (GckSshModule, gck_ssh_module, GCK_TYPE_MODULE);
 
+GckModule*  _gck_ssh_store_get_module_for_testing (void);
+
 /* -----------------------------------------------------------------------------
  * ACTUAL PKCS#11 Module Implementation 
  */
@@ -265,3 +267,9 @@ gck_ssh_store_get_functions (void)
 	gck_crypto_initialize ();
 	return gck_ssh_module_function_list;
 }
+
+GckModule*
+_gck_ssh_store_get_module_for_testing (void)
+{
+	return pkcs11_module;
+}
diff --git a/pkcs11/ssh-store/gck-ssh-private-key.c b/pkcs11/ssh-store/gck-ssh-private-key.c
index 60f1af3..b0b3d1b 100644
--- a/pkcs11/ssh-store/gck-ssh-private-key.c
+++ b/pkcs11/ssh-store/gck-ssh-private-key.c
@@ -25,6 +25,7 @@
 #include "gck-ssh-private-key.h"
 
 #include "gck/gck-attributes.h"
+#include "gck/gck-authenticator.h"
 #include "gck/gck-manager.h"
 #include "gck/gck-object.h"
 #include "gck/gck-sexp.h"
@@ -48,7 +49,6 @@ struct _GckSshPrivateKey {
 	guchar *private_data;
 	gsize n_private_data;
 	
-	GckSexp *private_sexp;
 	gboolean is_encrypted;
 };
 
@@ -59,7 +59,8 @@ G_DEFINE_TYPE (GckSshPrivateKey, gck_ssh_private_key, GCK_TYPE_PRIVATE_KEY);
  */
 
 static CK_RV
-unlock_private_key (GckSshPrivateKey *self, const gchar *password, gssize n_password)
+unlock_private_key (GckSshPrivateKey *self, const gchar *password,
+                    gssize n_password, GckSexp **result)
 {
 	GckDataResult res;
 	gcry_sexp_t sexp;
@@ -89,11 +90,10 @@ unlock_private_key (GckSshPrivateKey *self, const gchar *password, gssize n_pass
 
 	if (!password || !password[0])
 		self->is_encrypted = FALSE;
-
-	wrapper = gck_sexp_new (sexp);
-	gck_private_key_store_private (GCK_PRIVATE_KEY (self), wrapper, self->is_encrypted ? 1 : G_MAXUINT);
-	gck_sexp_unref (wrapper);
 	
+	wrapper = gck_sexp_new (sexp);
+	*result = wrapper;
+
 	return CKR_OK;
 }
 
@@ -121,13 +121,13 @@ realize_and_take_data (GckSshPrivateKey *self, gcry_sexp_t sexp, gchar *comment,
 	self->private_data = private_data;
 	self->n_private_data = n_private_data;
 
-	/* Force parsing next time required */
-	gck_private_key_store_private (GCK_PRIVATE_KEY (self), NULL, 0);
-	
 	/* Try to parse the private data, and note if it's not actually encrypted */
 	self->is_encrypted = TRUE;
-	if (unlock_private_key (self, "", 0) == CKR_OK) 
+	if (unlock_private_key (self, "", 0, &wrapper) == CKR_OK) {
 		self->is_encrypted = FALSE;
+		gck_private_key_set_unlocked_private (GCK_PRIVATE_KEY (self), wrapper);
+		gck_sexp_unref (wrapper);
+	}
 }
 
 /* -----------------------------------------------------------------------------
@@ -159,10 +159,26 @@ gck_ssh_private_key_get_attribute (GckObject *base, CK_ATTRIBUTE_PTR attr)
 }
 
 static CK_RV
-gck_ssh_private_key_unlock (GckObject *base, CK_UTF8CHAR_PTR pin, CK_ULONG n_pin)
+gck_ssh_private_key_unlock (GckObject *base, GckAuthenticator *auth)
 {
 	GckSshPrivateKey *self = GCK_SSH_PRIVATE_KEY (base);
-	return unlock_private_key (self, (const gchar*)pin, n_pin);
+	const gchar *password;
+	GckSexp *wrapper;
+	gsize n_password;
+	CK_RV rv;
+
+	if (!self->is_encrypted)
+		return CKR_OK;
+
+	password = gck_authenticator_get_password (auth, &n_password);
+	rv = unlock_private_key (self, password, n_password, &wrapper);
+
+	if (rv == CKR_OK) {
+		gck_private_key_set_locked_private (GCK_PRIVATE_KEY (self), auth, wrapper, 1);
+		gck_sexp_unref (wrapper);
+	}
+
+	return rv;
 }
 
 static GObject* 
diff --git a/pkcs11/ssh-store/tests/Makefile.am b/pkcs11/ssh-store/tests/Makefile.am
index aae4b68..2a133bc 100644
--- a/pkcs11/ssh-store/tests/Makefile.am
+++ b/pkcs11/ssh-store/tests/Makefile.am
@@ -1,5 +1,7 @@
 UNIT_AUTO = \
-	unit-test-ssh-openssh.c 
+	unit-test-ssh-openssh.c \
+	unit-test-private-key.c \
+	test-ssh-module.c test-ssh-module.h
 
 UNIT_PROMPT = 
 
diff --git a/pkcs11/ssh-store/tests/test-ssh-module.c b/pkcs11/ssh-store/tests/test-ssh-module.c
new file mode 100644
index 0000000..29cbb73
--- /dev/null
+++ b/pkcs11/ssh-store/tests/test-ssh-module.c
@@ -0,0 +1,105 @@
+/* -*- 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-ssh-module.h"
+
+#include "gck/gck-module.h"
+
+#include "ssh-store/gck-ssh-store.h"
+
+static GMutex *mutex = NULL;
+
+GckModule*  _gck_ssh_store_get_module_for_testing (void);
+GMutex* _gck_module_get_scary_mutex_that_you_should_not_touch (GckModule *module);
+
+GckModule*
+test_ssh_module_initialize_and_enter (void)
+{
+	CK_FUNCTION_LIST_PTR funcs;
+	GckModule *module;
+	CK_RV rv;
+	
+	funcs = gck_ssh_store_get_functions ();
+	rv = (funcs->C_Initialize) (NULL);
+	g_return_val_if_fail (rv == CKR_OK, NULL);
+	
+	module = _gck_ssh_store_get_module_for_testing ();
+	g_return_val_if_fail (module, NULL);
+	
+	mutex = _gck_module_get_scary_mutex_that_you_should_not_touch (module);
+	test_ssh_module_enter ();
+	
+	return module;
+}
+
+void
+test_ssh_module_leave_and_finalize (void)
+{
+	CK_FUNCTION_LIST_PTR funcs;
+	CK_RV rv;
+	
+	test_ssh_module_leave ();
+	
+	funcs = gck_ssh_store_get_functions ();
+	rv = (funcs->C_Finalize) (NULL);
+	g_return_if_fail (rv == CKR_OK);
+}
+
+void
+test_ssh_module_leave (void)
+{
+	g_assert (mutex);
+	g_mutex_unlock (mutex);	
+}
+
+void
+test_ssh_module_enter (void)
+{
+	g_assert (mutex);
+	g_mutex_lock (mutex);
+}
+
+GckSession*
+test_ssh_module_open_session (gboolean writable)
+{
+	CK_ULONG flags = CKF_SERIAL_SESSION;
+	CK_SESSION_HANDLE handle;
+	GckModule *module;
+	GckSession *session;
+	CK_RV rv;
+	
+	module = _gck_ssh_store_get_module_for_testing ();
+	g_return_val_if_fail (module, NULL);
+
+	if (writable)
+		flags |= CKF_RW_SESSION;
+	
+	rv = gck_module_C_OpenSession (module, 1, flags, NULL, NULL, &handle);
+	g_assert (rv == CKR_OK);
+
+	session = gck_module_lookup_session (module, handle);
+	g_assert (session);
+
+	return session;
+}
diff --git a/pkcs11/ssh-store/tests/test-ssh-module.h b/pkcs11/ssh-store/tests/test-ssh-module.h
new file mode 100644
index 0000000..f0565dd
--- /dev/null
+++ b/pkcs11/ssh-store/tests/test-ssh-module.h
@@ -0,0 +1,43 @@
+/* -*- 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 TEST_SSH_MODULE_H_
+#define TEST_SSH_MODULE_H_
+
+#include <glib.h>
+
+#include "gck/gck-types.h"
+
+#include "pkcs11.h"
+
+void                   test_ssh_module_leave                    (void);
+
+void                   test_ssh_module_enter                    (void);
+
+GckModule*             test_ssh_module_initialize_and_enter     (void);
+
+void                   test_ssh_module_leave_and_finalize       (void);
+
+GckSession*            test_ssh_module_open_session             (gboolean writable);
+
+#endif /* TEST_SSH_MODULE_H_ */
diff --git a/pkcs11/ssh-store/tests/unit-test-private-key.c b/pkcs11/ssh-store/tests/unit-test-private-key.c
new file mode 100644
index 0000000..fc26ea1
--- /dev/null
+++ b/pkcs11/ssh-store/tests/unit-test-private-key.c
@@ -0,0 +1,97 @@
+/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
+/* unit-test-private-key.c: Test SSH Key Private key 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-ssh-module.h"
+
+#include "gck/gck-authenticator.h"
+#include "gck/gck-session.h"
+#include "gck/gck-module.h"
+
+#include "ssh-store/gck-ssh-private-key.h"
+
+#include "pkcs11g.h"
+
+static GckModule *module = NULL;
+static GckSession *session = NULL;
+
+DEFINE_SETUP(private_key_setup)
+{
+	module = test_ssh_module_initialize_and_enter ();
+	session = test_ssh_module_open_session (TRUE);
+}
+
+DEFINE_TEARDOWN(private_key_teardown)
+{
+	test_ssh_module_leave_and_finalize ();
+	module = NULL;
+	session = NULL;
+}
+
+DEFINE_TEST(private_key_parse_plain)
+{
+	GckSshPrivateKey *key;
+	gchar *pub_path, *priv_path;
+	gboolean ret;
+	
+	key = gck_ssh_private_key_new (module, "my-unique");
+	g_assert (GCK_IS_SSH_PRIVATE_KEY (key));
+
+	pub_path = g_build_filename (test_dir_testdata (), "id_dsa_plain.pub", NULL);
+	priv_path = g_build_filename (test_dir_testdata (), "id_dsa_plain", NULL);
+	
+	ret = gck_ssh_private_key_parse (key, pub_path, priv_path, NULL);
+	g_assert (ret == TRUE);
+	
+	g_object_unref (key);
+	g_free (pub_path);
+	g_free (priv_path);
+}
+
+
+DEFINE_TEST(private_key_parse_and_unlock)
+{
+	GckSshPrivateKey *key;
+	GckAuthenticator *auth;
+	gchar *pub_path, *priv_path;
+	gboolean ret;
+	CK_RV rv;
+	
+	key = gck_ssh_private_key_new (module, "my-unique");
+	g_assert (GCK_IS_SSH_PRIVATE_KEY (key));
+
+	pub_path = g_build_filename (test_dir_testdata (), "id_dsa_encrypted.pub", NULL);
+	priv_path = g_build_filename (test_dir_testdata (), "id_dsa_encrypted", NULL);
+	
+	ret = gck_ssh_private_key_parse (key, pub_path, priv_path, NULL);
+	g_assert (ret == TRUE);
+
+	g_free (pub_path);
+	g_free (priv_path);
+
+	rv = gck_authenticator_create (GCK_OBJECT (key), (guchar*)"password", 8, &auth);
+	g_assert (rv == CKR_OK);
+	
+	g_object_unref (auth);
+	g_object_unref (key);
+}
diff --git a/pkcs11/user-store/gck-user-private-key.c b/pkcs11/user-store/gck-user-private-key.c
index 60e7740..a699e65 100644
--- a/pkcs11/user-store/gck-user-private-key.c
+++ b/pkcs11/user-store/gck-user-private-key.c
@@ -100,7 +100,7 @@ gck_user_private_key_real_get_attribute (GckObject *base, CK_ATTRIBUTE_PTR attr)
 }
 
 static GckSexp* 
-gck_user_private_key_real_acquire_crypto_sexp (GckKey *base)
+gck_user_private_key_real_acquire_crypto_sexp (GckKey *base, GckSession *unused)
 {
 	GckUserPrivateKey *self = GCK_USER_PRIVATE_KEY (base);
 	gcry_sexp_t sexp;
@@ -295,7 +295,7 @@ gck_user_private_key_real_save (GckSerializable *base, GckLogin *login, guchar *
 	g_return_val_if_fail (data, FALSE);
 	g_return_val_if_fail (n_data, FALSE);
 	
-	sexp = gck_user_private_key_real_acquire_crypto_sexp (GCK_KEY (self));
+	sexp = gck_user_private_key_real_acquire_crypto_sexp (GCK_KEY (self), NULL);
 	g_return_val_if_fail (sexp, FALSE);
 	
 	password = gck_login_get_password (login, &n_password);
diff --git a/pkcs11/user-store/gck-user-storage.c b/pkcs11/user-store/gck-user-storage.c
index 12c2263..687c1b3 100644
--- a/pkcs11/user-store/gck-user-storage.c
+++ b/pkcs11/user-store/gck-user-storage.c
@@ -90,14 +90,6 @@ G_DEFINE_TYPE (GckUserStorage, gck_user_storage, GCK_TYPE_STORE);
  * HELPERS 
  */
 
-static void
-dispose_unref_object (gpointer obj)
-{
-	g_assert (G_IS_OBJECT (obj));
-	g_object_run_dispose (obj);
-	g_object_unref (obj);
-}
-
 #ifndef HAVE_FLOCK
 #define LOCK_SH 1
 #define LOCK_EX 2
@@ -882,7 +874,7 @@ gck_user_storage_init (GckUserStorage *self)
 	g_signal_connect (self->file, "entry-removed", G_CALLBACK (data_file_entry_removed), self);
 	
 	/* Each one owns the key and contains weak ref to other's key as its value */
-	self->object_to_identifier = g_hash_table_new_full (g_direct_hash, g_direct_equal, dispose_unref_object, NULL);
+	self->object_to_identifier = g_hash_table_new_full (g_direct_hash, g_direct_equal, gck_util_dispose_unref, NULL);
 	self->identifier_to_object = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
 	
 	self->read_fd = -1;



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]