Auto login of sessions when and where necessary.
authorStefan Walter <stefw@src.gnome.org>
Sun, 20 Jul 2008 02:56:06 +0000 (02:56 +0000)
committerStefan Walter <stefw@src.gnome.org>
Sun, 20 Jul 2008 02:56:06 +0000 (02:56 +0000)
* gp11/gp11.h:
* gp11/gp11-call.c:
* gp11/gp11-marshal.list:
* gp11/gp11-private.h:
* gp11/gp11-session.c:
* gp11/gp11-slot.c:
* gp11/Makefile.am:
* gp11/tests/gp11-test-module.c:
* gp11/tests/unit-test-gp11-session.c: Auto login of sessions when
and where necessary.

svn path=/trunk/; revision=1185

gp11/Makefile.am
gp11/gp11-call.c
gp11/gp11-marshal.list [new file with mode: 0644]
gp11/gp11-private.h
gp11/gp11-session.c
gp11/gp11-slot.c
gp11/gp11.h
gp11/tests/gp11-test-module.c
gp11/tests/unit-test-gp11-session.c

index ee16547..25f5b6b 100644 (file)
@@ -11,6 +11,9 @@ INCLUDES = \
        $(GTHREAD_CFLAGS) \
        $(GLIB_CFLAGS)
 
+BUILT_SOURCES = \
+       gp11-marshal.c gp11-marshal.h
+
 lib_LTLIBRARIES = libgp11.la
 
 libgp11_la_SOURCES = \
@@ -21,7 +24,8 @@ libgp11_la_SOURCES = \
        gp11-module.c \
        gp11-object.c \
        gp11-session.c \
-       gp11-slot.c
+       gp11-slot.c \
+       $(BUILT_SOURCES)
 
 libgp11_la_LDFLAGS = \
        -version-info $(GP11_LT_RELEASE) \
@@ -33,6 +37,13 @@ libgp11_la_LIBADD = \
        $(GIO_LIBS) \
        $(GLIB_LIBS)
 
+gp11-marshal.h: gp11-marshal.list $(GLIB_GENMARSHAL)
+       $(GLIB_GENMARSHAL) $< --header --prefix=_gp11_marshal > $@
+
+gp11-marshal.c: gp11-marshal.list $(GLIB_GENMARSHAL)
+       echo "#include \"gp11-marshal.h\"" > $@ && \
+       $(GLIB_GENMARSHAL) $< --body --prefix=_gp11_marshal >> $@
+
 pkgconfigdir = $(libdir)/pkgconfig
 pkgconfig_DATA = gp11-$(GP11_MAJOR).pc
 
index 824d6f6..937ec24 100644 (file)
@@ -1,6 +1,7 @@
 
 #include "gp11-private.h"
 
+#include <string.h>
 
 static GThreadPool *thread_pool = NULL;
 static GAsyncQueue *completed_queue = NULL;
@@ -48,12 +49,27 @@ static void
 process_async_call (gpointer data, gpointer unused)
 {
        GP11Call *call = GP11_CALL (data);
+       CK_ULONG pin_len;
+
        g_assert (GP11_IS_CALL (call));
 
-       g_assert (call->rv == CKR_OK);
+       /* Try to login to the token, with the provided password */
+       if (call->do_login) {
+               call->do_login = FALSE;
+
+               pin_len = call->password ? strlen (call->password) : 0;
+               call->rv = (call->args->pkcs11->C_Login) (call->args->handle, CKU_USER,
+                                                         (CK_UTF8CHAR_PTR)call->password,
+                                                         pin_len);
 
-       call->rv = perform_call (call->func, call->cancellable,
-                                call->args);
+               /* Fix the result so that we'll try the login again */
+               if (call->rv == CKR_PIN_INCORRECT)
+                       call->rv = CKR_USER_NOT_LOGGED_IN;
+
+       /* An actual call */
+       } else {
+               call->rv = perform_call (call->func, call->cancellable, call->args);
+       }
 
        g_async_queue_push (completed_queue, call);
 
@@ -69,9 +85,28 @@ process_result (GP11Call *call, gpointer unused)
        if (call->cancellable) {
                /* Don't call the callback when cancelled */
                if (g_cancellable_is_cancelled (call->cancellable))
-                       return;
+                       call->rv = CKR_FUNCTION_CANCELED;
+       }
+
+       /*
+        * Now if this is a session call, and the slot wants does
+        * auto-login, then we try to get a password and do auto login.
+        */
+       if (call->rv == CKR_USER_NOT_LOGGED_IN && GP11_IS_SESSION (call->object)) {
+               g_free (call->password);
+               call->password = NULL;
+               call->do_login = _gp11_slot_token_authentication (GP11_SESSION (call->object)->slot,
+                                                                 &call->password);
+       }
+
+       /* If we're supposed to do a login, then queue this call again */
+       if (call->do_login) {
+               g_object_ref (call);
+               g_thread_pool_push (thread_pool, call, NULL);
+               return;
        }
 
+       /* All done, finish processing */
        if (call->callback) {
                g_assert (G_IS_OBJECT (call->object));
                (call->callback) (G_OBJECT (call->object), G_ASYNC_RESULT (call),
@@ -154,6 +189,10 @@ _gp11_call_finalize (GObject *obj)
        call->destroy = NULL;
        call->args = NULL;
 
+       if (call->password)
+               g_free (call->password);
+       call->password = NULL;
+
        G_OBJECT_CLASS (_gp11_call_parent_class)->finalize (obj);
 }
 
@@ -247,7 +286,9 @@ _gp11_call_sync (gpointer object, gpointer func, gpointer data,
                  GCancellable *cancellable, GError **err)
 {
        GP11Arguments *args = (GP11Arguments*)data;
-       GP11Module *module;
+       gchar *password = NULL;
+       GP11Module *module = NULL;
+       CK_ULONG pin_len;
        CK_RV rv;
 
        g_assert (G_IS_OBJECT (object));
@@ -261,6 +302,29 @@ _gp11_call_sync (gpointer object, gpointer func, gpointer data,
        g_object_unref (module);
 
        rv = perform_call ((GP11CallFunc)func, cancellable, args);
+
+       /*
+        * Now if this is a session call, and the slot wants does
+        * auto-login, then we try to get a password and do auto login.
+        */
+       if (rv == CKR_USER_NOT_LOGGED_IN && GP11_IS_SESSION (object)) {
+
+               do {
+                       if (!_gp11_slot_token_authentication (GP11_SESSION (object)->slot,
+                                                             &password)) {
+                               rv = CKR_USER_NOT_LOGGED_IN;
+                       } else {
+                               pin_len = password ? strlen (password) : 0;
+                               rv = (args->pkcs11->C_Login) (args->handle, CKU_USER,
+                                                             (CK_UTF8CHAR_PTR)password, pin_len);
+                       }
+               } while (rv == CKR_PIN_INCORRECT);
+
+               /* If we logged in successfully then try again */
+               if (rv == CKR_OK)
+                       rv = perform_call ((GP11CallFunc)func, cancellable, args);
+       }
+
        if (rv == CKR_OK)
                return TRUE;
 
diff --git a/gp11/gp11-marshal.list b/gp11/gp11-marshal.list
new file mode 100644 (file)
index 0000000..7e7cf5d
--- /dev/null
@@ -0,0 +1,2 @@
+BOOLEAN:POINTER
+BOOLEAN:OBJECT,POINTER
index 24ac16c..d822524 100644 (file)
@@ -26,6 +26,13 @@ void                _gp11_attributes_add_take               (GP11Attributes *att
 CK_ATTRIBUTE_PTR    _gp11_attributes_raw                    (GP11Attributes *attrs);
 
 /* ----------------------------------------------------------------------------
+ * SLOT
+ */
+
+gboolean            _gp11_slot_token_authentication       (GP11Slot *slot,
+                                                           gchar **password);
+
+/* ----------------------------------------------------------------------------
  * CALL
  */
 
@@ -42,7 +49,7 @@ typedef struct _GP11Arguments {
 
 } GP11Arguments;
 
-#define GP11_ARGUMENTS_INIT    { NULL, NULL, 0 }
+#define GP11_ARGUMENTS_INIT       { NULL, NULL, 0 }
 
 #define GP11_TYPE_CALL             (_gp11_call_get_type())
 #define GP11_CALL(obj)             (G_TYPE_CHECK_INSTANCE_CAST((obj), GP11_TYPE_CALL, GP11Call))
@@ -67,6 +74,10 @@ struct _GP11Call {
        gpointer object;
        GAsyncReadyCallback callback;
        gpointer user_data;
+
+       /* For authenticating */
+       gboolean do_login;
+       gchar *password;
 };
 
 struct _GP11CallClass {
index 3845edd..f8a2962 100644 (file)
@@ -14,7 +14,8 @@ enum {
 enum {
        PROP_0,
        PROP_MODULE,
-       PROP_HANDLE
+       PROP_HANDLE,
+       PROP_SLOT
 };
 
 G_DEFINE_TYPE (GP11Session, gp11_session, G_TYPE_OBJECT);
@@ -44,6 +45,9 @@ gp11_session_get_property (GObject *obj, guint prop_id, GValue *value,
        case PROP_HANDLE:
                g_value_set_uint (value, session->handle);
                break;
+       case PROP_SLOT:
+               g_value_set_object(value, session->slot);
+               break;
        }
 }
 
@@ -63,6 +67,11 @@ gp11_session_set_property (GObject *obj, guint prop_id, const GValue *value,
                g_return_if_fail (!session->handle);
                session->handle = g_value_get_uint (value);
                break;
+       case PROP_SLOT:
+               g_return_if_fail (!session->slot);
+               session->slot = g_value_dup_object (value);
+               g_return_if_fail (session->slot);
+               break;
        }
 }
 
@@ -91,6 +100,10 @@ gp11_session_dispose (GObject *obj)
                session->handle = 0;
        }
 
+       if (session->slot)
+               g_object_unref (session->slot);
+       session->slot = NULL;
+
        if (session->module)
                g_object_unref (session->module);
        session->module = NULL;
@@ -129,6 +142,10 @@ gp11_session_class_init (GP11SessionClass *klass)
                g_param_spec_uint ("handle", "Session Handle", "PKCS11 Session Handle",
                                   0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
+       g_object_class_install_property (gobject_class, PROP_SLOT,
+               g_param_spec_object ("slot", "Slot that this session uses", "PKCS11 Slot",
+                                    GP11_TYPE_SLOT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
        signals[DISCARD_HANDLE] = g_signal_new ("discard-handle", GP11_TYPE_SESSION,
                        G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (GP11SessionClass, discard_handle),
                        NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0);
@@ -151,7 +168,8 @@ GP11Session*
 gp11_session_from_handle (GP11Slot *slot, CK_SESSION_HANDLE handle)
 {
        g_return_val_if_fail (GP11_IS_SLOT (slot), NULL);
-       return g_object_new (GP11_TYPE_SESSION, "module", slot->module, "handle", handle, NULL);
+       return g_object_new (GP11_TYPE_SESSION, "module", slot->module,
+                            "handle", handle, "slot", slot, NULL);
 }
 
 GP11SessionInfo*
index a3ada5d..cf4212a 100644 (file)
@@ -3,6 +3,7 @@
 
 #include "gp11.h"
 #include "gp11-private.h"
+#include "gp11-marshal.h"
 
 #include <string.h>
 
@@ -10,12 +11,23 @@ enum {
        PROP_0,
        PROP_MODULE,
        PROP_HANDLE,
-       PROP_REUSE_SESSIONS
+       PROP_REUSE_SESSIONS,
+       PROP_AUTO_LOGIN
+};
+
+enum {
+       AUTHENTICATE_TOKEN,
+#ifdef UNIMPLEMENTED
+       AUTHENTICATE_KEY,
+       SLOT_EVENT
+#endif
+       LAST_SIGNAL
 };
 
 typedef struct _GP11SlotPrivate {
-       gboolean reuse_sessions;
+       gboolean auto_login;
        GHashTable *open_sessions;
+       GP11TokenInfo *token_info;
 } GP11SlotPrivate;
 
 G_DEFINE_TYPE (GP11Slot, gp11_slot, G_TYPE_OBJECT);
@@ -29,6 +41,8 @@ typedef struct _SessionPool {
        GSList *sessions; /* list of CK_SESSION_HANDLE */
 } SessionPool;
 
+static guint signals[LAST_SIGNAL] = { 0 };
+
 /* ----------------------------------------------------------------------------
  * HELPERS
  */
@@ -206,6 +220,14 @@ make_session_object (GP11Slot *slot, guint flags, CK_SESSION_HANDLE handle)
        return session;
 }
 
+static void
+ensure_token_info (GP11Slot *slot)
+{
+       GP11SlotPrivate *pv = GP11_SLOT_GET_PRIVATE (slot);
+       if (!pv->token_info)
+               pv->token_info = gp11_slot_get_token_info (slot);
+}
+
 /* ----------------------------------------------------------------------------
  * OBJECT
  */
@@ -230,6 +252,9 @@ gp11_slot_get_property (GObject *obj, guint prop_id, GValue *value,
        case PROP_HANDLE:
                g_value_set_uint (value, slot->handle);
                break;
+       case PROP_AUTO_LOGIN:
+               g_value_set_boolean (value, pv->auto_login);
+               break;
        case PROP_REUSE_SESSIONS:
                g_value_set_boolean (value, pv->open_sessions != NULL);
                break;
@@ -240,6 +265,7 @@ static void
 gp11_slot_set_property (GObject *obj, guint prop_id, const GValue *value,
                         GParamSpec *pspec)
 {
+       GP11SlotPrivate *pv = GP11_SLOT_GET_PRIVATE (obj);
        GP11Slot *slot = GP11_SLOT (obj);
 
        switch (prop_id) {
@@ -253,6 +279,9 @@ gp11_slot_set_property (GObject *obj, guint prop_id, const GValue *value,
                g_return_if_fail (!slot->handle);
                slot->handle = g_value_get_uint (value);
                break;
+       case PROP_AUTO_LOGIN:
+               pv->auto_login = g_value_get_boolean (value);
+               break;
        case PROP_REUSE_SESSIONS:
                if (g_value_get_boolean (value))
                        create_session_table (slot);
@@ -308,14 +337,54 @@ gp11_slot_class_init (GP11SlotClass *klass)
                g_param_spec_uint ("handle", "Handle", "PKCS11 Slot ID",
                                   0, G_MAXUINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
 
+       g_object_class_install_property (gobject_class, PROP_AUTO_LOGIN,
+               g_param_spec_boolean ("auto-login", "Auto Login", "Auto Login to Token when necessary",
+                                     FALSE, G_PARAM_READWRITE));
+
        g_object_class_install_property (gobject_class, PROP_REUSE_SESSIONS,
                g_param_spec_boolean ("reuse-sessions", "Reuse Sessions", "Reuse sessions?",
                                      FALSE, G_PARAM_READWRITE));
 
+       signals[AUTHENTICATE_TOKEN] = g_signal_new ("authenticate-token", GP11_TYPE_SLOT,
+                       G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GP11SlotClass, authenticate_token),
+                       g_signal_accumulator_true_handled, NULL, _gp11_marshal_BOOLEAN__POINTER,
+                       G_TYPE_BOOLEAN, 1, G_TYPE_POINTER);
+
        g_type_class_add_private (gobject_class, sizeof (GP11SlotPrivate));
 }
 
 /* ----------------------------------------------------------------------------
+ * INTERNAL
+ */
+
+gboolean
+_gp11_slot_token_authentication (GP11Slot *slot, gchar **password)
+{
+       GP11SlotPrivate *pv = GP11_SLOT_GET_PRIVATE (slot);
+       gboolean ret = FALSE;
+
+       g_return_val_if_fail (GP11_IS_SLOT (slot), FALSE);
+       g_return_val_if_fail (password, FALSE);
+
+       if (!pv->auto_login)
+               return FALSE;
+
+       /*
+        * If it's a protected authentication path style token, then
+        * we don't prompt here, the hardware/software is expected
+        * to prompt the user in some other way.
+        */
+       ensure_token_info (slot);
+       if (pv->token_info && (pv->token_info->flags & CKF_PROTECTED_AUTHENTICATION_PATH)) {
+               *password = NULL;
+               return TRUE;
+       }
+
+       g_signal_emit (slot, signals[AUTHENTICATE_TOKEN], 0, password, &ret);
+       return ret;
+}
+
+/* ----------------------------------------------------------------------------
  * PUBLIC
  */
 
@@ -363,6 +432,20 @@ gp11_slot_set_reuse_sessions (GP11Slot *slot, gboolean reuse)
        g_object_set (slot, "reuse-sessions", reuse, NULL);
 }
 
+gboolean
+gp11_slot_get_auto_login (GP11Slot *slot)
+{
+       gboolean auto_login = FALSE;
+       g_object_get (slot, "auto-login", &auto_login, NULL);
+       return auto_login;
+}
+
+void
+gp11_slot_set_auto_login (GP11Slot *slot, gboolean auto_login)
+{
+       g_object_set (slot, "auto-login", auto_login, NULL);
+}
+
 GP11SlotInfo*
 gp11_slot_get_info (GP11Slot *slot)
 {
index 1a1fa3c..fa11648 100644 (file)
@@ -297,7 +297,15 @@ struct _GP11Slot {
 struct _GP11SlotClass {
        GObjectClass parent;
 
+       gboolean (*authenticate_token) (GP11Slot *slot, gchar **password);
+
+#ifdef UNIMPLEMENTED
+       gboolean (*authenticate_key) (GP11Slot *slot, GP11Object *object,
+                                     gchar **password);
+
        void (*slot_event) (GP11Slot *slot);
+#endif
+
 };
 
 GType               gp11_slot_get_type                      (void) G_GNUC_CONST;
@@ -307,6 +315,13 @@ gboolean            gp11_slot_get_reuse_sessions            (GP11Slot *slot);
 void                gp11_slot_set_reuse_sessions            (GP11Slot *slot,
                                                              gboolean reuse);
 
+gboolean            gp11_slot_get_auto_login                (GP11Slot *slot);
+
+void                gp11_slot_set_auto_login                (GP11Slot *slot,
+                                                             gboolean auto_login);
+
+gint                gp11_slot_get_max_pin_length            (GP11Slot *slot);
+
 GP11SlotInfo*       gp11_slot_get_info                      (GP11Slot *slot);
 
 GP11TokenInfo*      gp11_slot_get_token_info                (GP11Slot *slot);
@@ -382,6 +397,7 @@ typedef struct _GP11SessionClass GP11SessionClass;
 struct _GP11Session {
        GObject parent;
 
+       GP11Slot *slot;
        GP11Module *module;
        CK_SESSION_HANDLE handle;
 };
index d800241..533a8ad 100644 (file)
@@ -104,8 +104,6 @@ test_C_Initialize (CK_VOID_PTR pInitArgs)
 static CK_RV
 test_C_Finalize (CK_VOID_PTR pReserved)
 {
-
-
        g_assert (pReserved == NULL && "Invalid reserved pointer");
        g_assert (initialized == TRUE && "Finalize without being initialized");
 
@@ -199,8 +197,6 @@ const static CK_SLOT_INFO TEST_INFO_TWO = {
 static CK_RV
 test_C_GetSlotInfo (CK_SLOT_ID slotID, CK_SLOT_INFO_PTR pInfo)
 {
-
-
        g_assert (pInfo != NULL && "Invalid pInfo");
 
        if (slotID == TEST_SLOT_ONE) {
@@ -220,7 +216,7 @@ const static CK_TOKEN_INFO TEST_TOKEN_ONE = {
        "TEST MANUFACTURER               ",
        "TEST MODEL      ",
        "TEST SERIAL     ",
-       CKF_LOGIN_REQUIRED | CKF_USER_PIN_INITIALIZED | CKF_CLOCK_ON_TOKEN | CKF_PROTECTED_AUTHENTICATION_PATH | CKF_TOKEN_INITIALIZED,
+       CKF_LOGIN_REQUIRED | CKF_USER_PIN_INITIALIZED | CKF_CLOCK_ON_TOKEN | CKF_TOKEN_INITIALIZED,
        1,
        2,
        3,
@@ -239,8 +235,6 @@ const static CK_TOKEN_INFO TEST_TOKEN_ONE = {
 static CK_RV
 test_C_GetTokenInfo (CK_SLOT_ID slotID, CK_TOKEN_INFO_PTR pInfo)
 {
-
-
        g_assert (pInfo != NULL && "Invalid pInfo");
 
        if (slotID == TEST_SLOT_ONE) {
@@ -297,8 +291,6 @@ static CK_RV
 test_C_GetMechanismInfo (CK_SLOT_ID slotID, CK_MECHANISM_TYPE type,
                        CK_MECHANISM_INFO_PTR pInfo)
 {
-
-
        g_assert (slotID == TEST_SLOT_ONE && "Invalid slotID");
        g_assert (pInfo != NULL && "Invalid pInfo");
 
@@ -507,7 +499,7 @@ test_C_CreateObject (CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate,
 {
        GP11Attributes *attrs;
        Session *session;
-       gboolean token;
+       gboolean token, priv;
        CK_ULONG i;
 
        g_assert (phObject != NULL);
@@ -521,6 +513,13 @@ test_C_CreateObject (CK_SESSION_HANDLE hSession, CK_ATTRIBUTE_PTR pTemplate,
        for (i = 0; i < ulCount; ++i)
                gp11_attributes_add_data (attrs, pTemplate[i].type, pTemplate[i].pValue, pTemplate[i].ulValueLen);
 
+       if (gp11_attributes_find_boolean (attrs, CKA_PRIVATE, &priv) && priv) {
+               if (!session->logged_in) {
+                       gp11_attributes_unref (attrs);
+                       return CKR_USER_NOT_LOGGED_IN;
+               }
+       }
+
        *phObject = ++unique_identifier;
        if (gp11_attributes_find_boolean (attrs, CKA_TOKEN, &token) && token)
                g_hash_table_insert (the_objects, GUINT_TO_POINTER (*phObject), attrs);
@@ -543,19 +542,28 @@ test_C_CopyObject (CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject,
 static CK_RV
 test_C_DestroyObject (CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject)
 {
+       GP11Attributes *attrs;
        Session *session;
+       gboolean priv;
 
        session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
        g_assert (session != NULL && "No such session found");
        if (!session)
                return CKR_SESSION_HANDLE_INVALID;
 
-       if (!g_hash_table_remove (the_objects, GUINT_TO_POINTER (hObject)) &&
-           !g_hash_table_remove (session->objects, GUINT_TO_POINTER (hObject))) {
+       attrs = g_hash_table_lookup (the_objects, GUINT_TO_POINTER (hObject));
+       if (!attrs)
+               attrs = g_hash_table_lookup (session->objects, GUINT_TO_POINTER (hObject));
+       if (!attrs) {
                g_assert_not_reached (); /* "no such object found" */
                return CKR_OBJECT_HANDLE_INVALID;
        }
 
+       if (gp11_attributes_find_boolean (attrs, CKA_PRIVATE, &priv) && priv) {
+               if (!session->logged_in)
+                       return CKR_USER_NOT_LOGGED_IN;
+       }
+
        return CKR_OK;
 }
 
index 69ac1a9..a5f4099 100644 (file)
@@ -194,3 +194,64 @@ DEFINE_TEST(login_logout)
        result = NULL;
 
 }
+
+static gboolean
+authenticate_token (GP11Slot *slot, gchar **password, gpointer unused)
+{
+       g_assert (unused == GUINT_TO_POINTER (35));
+       g_assert (password != NULL);
+       g_assert (*password == NULL);
+       g_assert (GP11_IS_SLOT (slot));
+
+       *password = g_strdup ("booo");
+       return TRUE;
+}
+
+DEFINE_TEST(auto_login)
+{
+       GP11Object *object;
+       GAsyncResult *result = NULL;
+       GError *err = NULL;
+       GP11Attributes *attrs;
+       gboolean ret;
+
+       attrs = gp11_attributes_newv (CKA_CLASS, GP11_ULONG, CKO_DATA,
+                                     CKA_LABEL, GP11_STRING, "TEST OBJECT",
+                                     CKA_PRIVATE, GP11_BOOLEAN, CK_TRUE,
+                                     -1);
+
+       /* Try to do something that requires a login */
+       object = gp11_session_create_object_full (session, attrs, NULL, &err);
+       g_assert (!object);
+       g_assert (err && err->code == CKR_USER_NOT_LOGGED_IN);
+       g_clear_error (&err);
+
+       /* Setup for auto login */
+       g_assert (gp11_slot_get_auto_login (slot) == FALSE);
+       gp11_slot_set_auto_login (slot, TRUE);
+       g_assert (gp11_slot_get_auto_login (slot) == TRUE);
+
+       g_signal_connect (slot, "authenticate-token", G_CALLBACK (authenticate_token), GUINT_TO_POINTER (35));
+
+       /* Try again to do something that requires a login */
+       object = gp11_session_create_object_full (session, attrs, NULL, &err);
+       SUCCESS_RES (object, err);
+       g_object_unref (object);
+
+       /* We should now be logged in, try to log out */
+       ret = gp11_session_logout (session, &err);
+       SUCCESS_RES (ret, err);
+
+       /* Now try the same thing, but asyncronously */
+       gp11_session_create_object_async (session, attrs, NULL, fetch_async_result, &result);
+       WAIT_UNTIL (result);
+       g_assert (result != NULL);
+       object = gp11_session_create_object_finish (session, result, &err);
+       SUCCESS_RES (object, err);
+       g_object_unref (result);
+       g_object_unref (object);
+
+       /* We should now be logged in, try to log out */
+       ret = gp11_session_logout (session, &err);
+       SUCCESS_RES (ret, err);
+}