$(GTHREAD_CFLAGS) \
$(GLIB_CFLAGS)
+BUILT_SOURCES = \
+ gp11-marshal.c gp11-marshal.h
+
lib_LTLIBRARIES = libgp11.la
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) \
$(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
#include "gp11-private.h"
+#include <string.h>
static GThreadPool *thread_pool = NULL;
static GAsyncQueue *completed_queue = NULL;
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);
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),
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);
}
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));
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;
--- /dev/null
+BOOLEAN:POINTER
+BOOLEAN:OBJECT,POINTER
CK_ATTRIBUTE_PTR _gp11_attributes_raw (GP11Attributes *attrs);
/* ----------------------------------------------------------------------------
+ * SLOT
+ */
+
+gboolean _gp11_slot_token_authentication (GP11Slot *slot,
+ gchar **password);
+
+/* ----------------------------------------------------------------------------
* CALL
*/
} 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))
gpointer object;
GAsyncReadyCallback callback;
gpointer user_data;
+
+ /* For authenticating */
+ gboolean do_login;
+ gchar *password;
};
struct _GP11CallClass {
enum {
PROP_0,
PROP_MODULE,
- PROP_HANDLE
+ PROP_HANDLE,
+ PROP_SLOT
};
G_DEFINE_TYPE (GP11Session, gp11_session, G_TYPE_OBJECT);
case PROP_HANDLE:
g_value_set_uint (value, session->handle);
break;
+ case PROP_SLOT:
+ g_value_set_object(value, session->slot);
+ break;
}
}
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;
}
}
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;
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);
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*
#include "gp11.h"
#include "gp11-private.h"
+#include "gp11-marshal.h"
#include <string.h>
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);
GSList *sessions; /* list of CK_SESSION_HANDLE */
} SessionPool;
+static guint signals[LAST_SIGNAL] = { 0 };
+
/* ----------------------------------------------------------------------------
* HELPERS
*/
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
*/
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;
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) {
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);
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
*/
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)
{
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;
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);
struct _GP11Session {
GObject parent;
+ GP11Slot *slot;
GP11Module *module;
CK_SESSION_HANDLE handle;
};
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");
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) {
"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,
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) {
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");
{
GP11Attributes *attrs;
Session *session;
- gboolean token;
+ gboolean token, priv;
CK_ULONG i;
g_assert (phObject != NULL);
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);
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;
}
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);
+}