[glib-networking/wip/tingping/pkcs11] WIP: Re-add pkcs11 support
- From: Patrick Griffis <pgriffis src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib-networking/wip/tingping/pkcs11] WIP: Re-add pkcs11 support
- Date: Fri, 26 Apr 2019 19:13:47 +0000 (UTC)
commit d3ba6eba23aa0b2178811ffe986ebf8b7de3bfc7
Author: Patrick Griffis <pgriffis igalia com>
Date: Fri Apr 26 12:13:11 2019 -0700
WIP: Re-add pkcs11 support
meson.build | 12 +
meson_options.txt | 1 +
po/POTFILES.in | 2 +
tls/gnutls/gtlsbackend-gnutls.c | 36 +
tls/gnutls/gtlscertificate-gnutls.c | 107 ++-
tls/gnutls/gtlscertificate-gnutls.h | 12 +
tls/gnutls/gtlsconnection-gnutls.c | 76 ++
tls/gnutls/gtlsdatabase-gnutls-pkcs11.c | 1139 +++++++++++++++++++++++
tls/gnutls/gtlsdatabase-gnutls-pkcs11.h | 44 +
tls/gnutls/meson.build | 10 +
tls/pkcs11/gpkcs11array.c | 282 ++++++
tls/pkcs11/gpkcs11array.h | 107 +++
tls/pkcs11/gpkcs11pin.c | 159 ++++
tls/pkcs11/gpkcs11pin.h | 46 +
tls/pkcs11/gpkcs11slot.c | 618 ++++++++++++
tls/pkcs11/gpkcs11slot.h | 73 ++
tls/pkcs11/gpkcs11util.c | 63 ++
tls/pkcs11/gpkcs11util.h | 51 +
tls/pkcs11/meson.build | 28 +
tls/pkcs11/pkcs11-trust-assertions.h | 59 ++
tls/tests/meson.build | 14 +
tls/tests/mock-pkcs11.c | 1547 +++++++++++++++++++++++++++++++
tls/tests/mock-pkcs11.h | 396 ++++++++
tls/tests/pkcs11-array.c | 288 ++++++
tls/tests/pkcs11-pin.c | 152 +++
tls/tests/pkcs11-slot.c | 526 +++++++++++
tls/tests/pkcs11-util.c | 63 ++
27 files changed, 5910 insertions(+), 1 deletion(-)
---
diff --git a/meson.build b/meson.build
index 8d01f52..5c03f6e 100644
--- a/meson.build
+++ b/meson.build
@@ -75,6 +75,14 @@ gnutls_dep = dependency('gnutls', version: '>= 3.4.6', required: get_option('gnu
if gnutls_dep.found()
backends += ['gnutls']
+
+ # *** Checks for p11-kit ***
+ pkcs11_dep = dependency('p11-kit-1', version: '>= 0.20', required: get_option('pkcs11'))
+
+ if pkcs11_dep.found()
+ config_h.set('HAVE_PKCS11', 1,
+ description: 'Building with PKCS#11 support')
+ endif
endif
# *** Checks for OpenSSL ***
@@ -172,6 +180,9 @@ if libproxy_dep.found() or gsettings_desktop_schemas_dep.found()
endif
subdir('tls/base')
+if pkcs11_dep.found()
+ subdir('tls/pkcs11')
+endif
if gnutls_dep.found()
subdir('tls/gnutls')
@@ -194,4 +205,5 @@ output += ' gnutls support: ' + backends.contains('gnutls').to_string() +
output += ' openssl support: ' + backends.contains('openssl').to_string() + '\n'
output += ' libproxy support: ' + libproxy_dep.found().to_string() + '\n'
output += ' GNOME proxy support: ' + gsettings_desktop_schemas_dep.found().to_string() + '\n'
+output += ' PKCS#11 support: ' + pkcs11_dep.found().to_string() + '\n'
message(output)
diff --git a/meson_options.txt b/meson_options.txt
index 3a525dd..04706be 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -2,5 +2,6 @@ option('gnutls', type: 'feature', value: 'auto', description: 'support for GnuTL
option('openssl', type: 'feature', value: 'disabled', description: 'support for OpenSSL networking
configration')
option('libproxy', type: 'feature', value: 'auto', description: 'support for libproxy proxy configration')
option('gnome_proxy', type: 'feature', value: 'auto', description: 'support for GNOME desktop proxy
configuration')
+option('pkcs11', type: 'feature', value: 'disabled', description: 'support for PKCS#11 using p11-kit')
option('installed_tests', type: 'boolean', value: false, description: 'enable installed tests')
option('static_modules', type: 'boolean', value: false, description: 'build static modules')
diff --git a/po/POTFILES.in b/po/POTFILES.in
index dffd37e..0308cc4 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -13,3 +13,5 @@ tls/openssl/gtlscertificate-openssl.c
tls/openssl/gtlsclientconnection-openssl.c
tls/openssl/gtlsconnection-openssl.c
tls/openssl/gtlsserverconnection-openssl.c
+tls/pkcs11/gpkcs11pin.c
+tls/pkcs11/gpkcs11slot.c
diff --git a/tls/gnutls/gtlsbackend-gnutls.c b/tls/gnutls/gtlsbackend-gnutls.c
index c955327..23278cd 100644
--- a/tls/gnutls/gtlsbackend-gnutls.c
+++ b/tls/gnutls/gtlsbackend-gnutls.c
@@ -35,6 +35,7 @@
#include "gtlsclientconnection-gnutls.h"
#include "gtlsfiledatabase-gnutls.h"
#include "gtlsserverconnection-gnutls.h"
+#include "gtlsdatabase-gnutls-pkcs11.h"
struct _GTlsBackendGnutls
{
@@ -42,6 +43,7 @@ struct _GTlsBackendGnutls
GMutex mutex;
GTlsDatabase *default_database;
+ GTlsDatabase *pkcs11_database;
};
static void g_tls_backend_gnutls_interface_init (GTlsBackendInterface *iface);
@@ -153,6 +155,39 @@ g_tls_backend_gnutls_get_default_database (GTlsBackend *backend)
return result;
}
+static GTlsDatabase*
+g_tls_backend_gnutls_get_pkcs11_database (GTlsBackend *backend)
+{
+ GTlsBackendGnutls *self = G_TLS_BACKEND_GNUTLS (backend);
+ GTlsDatabase *result;
+ GError *error = NULL;
+
+ g_mutex_lock (&self->mutex);
+
+ if (self->pkcs11_database)
+ {
+ result = g_object_ref (self->pkcs11_database);
+ }
+ else
+ {
+ result = G_TLS_DATABASE (g_tls_database_gnutls_pkcs11_new (&error));
+ if (error)
+ {
+ g_warning ("Failed to load PKCS11 TLS database: %s", error->message);
+ g_clear_error (&error);
+ }
+ else
+ {
+ g_assert (result);
+ self->pkcs11_database = g_object_ref (result);
+ }
+ }
+
+ g_mutex_unlock (&self->mutex);
+
+ return result;
+}
+
static void
g_tls_backend_gnutls_interface_init (GTlsBackendInterface *iface)
{
@@ -161,6 +196,7 @@ g_tls_backend_gnutls_interface_init (GTlsBackendInterface *iface)
iface->get_server_connection_type = g_tls_server_connection_gnutls_get_type;
iface->get_file_database_type = g_tls_file_database_gnutls_get_type;
iface->get_default_database = g_tls_backend_gnutls_get_default_database;
+ iface->get_pkcs11_database = g_tls_backend_gnutls_get_pkcs11_database;
iface->get_dtls_client_connection_type = g_tls_client_connection_gnutls_get_type;
iface->get_dtls_server_connection_type = g_tls_server_connection_gnutls_get_type;
}
diff --git a/tls/gnutls/gtlscertificate-gnutls.c b/tls/gnutls/gtlscertificate-gnutls.c
index ff18c46..8023e91 100644
--- a/tls/gnutls/gtlscertificate-gnutls.c
+++ b/tls/gnutls/gtlscertificate-gnutls.c
@@ -39,7 +39,9 @@ enum
PROP_CERTIFICATE_PEM,
PROP_PRIVATE_KEY,
PROP_PRIVATE_KEY_PEM,
- PROP_ISSUER
+ PROP_ISSUER,
+ PROP_CERTIFICATE_URI,
+ PROP_PRIVATE_KEY_URI,
};
struct _GTlsCertificateGnutls
@@ -53,6 +55,9 @@ struct _GTlsCertificateGnutls
GError *construct_error;
+ gchar *certificate_uri;
+ gchar *private_key_uri;
+
guint have_cert : 1;
guint have_key : 1;
};
@@ -141,6 +146,14 @@ g_tls_certificate_gnutls_get_property (GObject *object,
g_value_set_object (value, gnutls->issuer);
break;
+ case PROP_CERTIFICATE_URI:
+ g_value_set_string (value, gnutls->certificate_uri);
+ break;
+
+ case PROP_PRIVATE_KEY_URI:
+ g_value_set_string (value, gnutls->private_key_uri);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
@@ -267,6 +280,16 @@ g_tls_certificate_gnutls_set_property (GObject *object,
gnutls->issuer = g_value_dup_object (value);
break;
+ case PROP_CERTIFICATE_URI:
+ g_free (gnutls->certificate_uri);
+ gnutls->certificate_uri = g_value_dup_string (value);
+ break;
+
+ case PROP_PRIVATE_KEY_URI:
+ g_free (gnutls->private_key_uri);
+ gnutls->private_key_uri = g_value_dup_string (value);
+ break;
+
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
@@ -376,6 +399,17 @@ g_tls_certificate_gnutls_class_init (GTlsCertificateGnutlsClass *klass)
g_object_class_override_property (gobject_class, PROP_PRIVATE_KEY, "private-key");
g_object_class_override_property (gobject_class, PROP_PRIVATE_KEY_PEM, "private-key-pem");
g_object_class_override_property (gobject_class, PROP_ISSUER, "issuer");
+
+ g_object_class_install_property (gobject_class, PROP_CERTIFICATE_URI,
+ g_param_spec_string ("certificate-uri", "Certificate URI",
+ "PKCS#11 URI of Certificate", NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_PRIVATE_KEY_URI,
+ g_param_spec_string ("private-key-uri", "Private Key URI",
+ "PKCS#11 URI of Private Key", NULL,
+ G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
}
static void
@@ -398,6 +432,33 @@ g_tls_certificate_gnutls_new (const gnutls_datum_t *datum,
return G_TLS_CERTIFICATE (gnutls);
}
+GTlsCertificate *
+g_tls_certificate_gnutls_pkcs11_new (gpointer certificate_data,
+ gsize certificate_data_length,
+ const gchar *certificate_uri,
+ const gchar *private_key_uri,
+ GTlsCertificate *issuer)
+{
+ GTlsCertificate *certificate;
+ gnutls_datum_t datum;
+
+ g_return_val_if_fail (certificate_data, NULL);
+ g_return_val_if_fail (certificate_uri, NULL);
+
+ datum.data = certificate_data;
+ datum.size = certificate_data_length;
+
+ certificate = g_object_new (G_TYPE_TLS_CERTIFICATE_GNUTLS,
+ "issuer", issuer,
+ "certificate-uri", certificate_uri,
+ "private-key-uri", private_key_uri,
+ NULL);
+
+ g_tls_certificate_gnutls_set_data (G_TLS_CERTIFICATE_GNUTLS (certificate), &datum);
+
+ return certificate;
+}
+
void
g_tls_certificate_gnutls_set_data (GTlsCertificateGnutls *gnutls,
const gnutls_datum_t *datum)
@@ -489,6 +550,26 @@ g_tls_certificate_gnutls_copy (GTlsCertificateGnutls *gnutls,
{
*pkey = NULL;
}
+
+ // if (*pkey == NULL)
+ // {
+ // gchar *uri = g_tls_certificate_gnutls_pkcs11_build_private_key_uri (chain, interaction_id);
+ // g_message("%s", uri);
+ // if (uri != NULL)
+ // {
+ // gnutls_pkcs11_privkey_t pkcs11_privkey;
+ // gnutls_privkey_t privkey;
+
+ // gnutls_pkcs11_privkey_init (&pkcs11_privkey);
+ // gnutls_pkcs11_privkey_import_url (pkcs11_privkey, uri, GNUTLS_PKCS11_URL_GENERIC);
+ // g_free (uri);
+
+ // gnutls_privkey_init (&privkey);
+ // gnutls_privkey_import_pkcs11 (privkey, pkcs11_privkey, GNUTLS_PRIVKEY_IMPORT_COPY);
+ // *pkey = privkey;
+ // gnutls_pkcs11_privkey_deinit (pkcs11_privkey);
+ // }
+ // }
}
void
@@ -761,3 +842,27 @@ g_tls_certificate_gnutls_build_chain (const gnutls_datum_t *certs,
return result;
}
+
+gchar *
+g_tls_certificate_gnutls_pkcs11_build_certificate_uri (GTlsCertificateGnutls *self,
+ const gchar *interaction_id)
+{
+ if (self->certificate_uri == NULL)
+ return NULL;
+ else if (interaction_id)
+ return g_strdup_printf ("%s;pinfile=%s", self->certificate_uri, interaction_id);
+ else
+ return g_strdup (self->certificate_uri);
+}
+
+gchar *
+g_tls_certificate_gnutls_pkcs11_build_private_key_uri (GTlsCertificateGnutls *self,
+ const gchar *interaction_id)
+{
+ if (self->private_key_uri == NULL)
+ return NULL;
+ else if (interaction_id)
+ return g_strdup_printf ("%s;pinfile=%s", self->private_key_uri, interaction_id);
+ else
+ return g_strdup (self->private_key_uri);
+}
\ No newline at end of file
diff --git a/tls/gnutls/gtlscertificate-gnutls.h b/tls/gnutls/gtlscertificate-gnutls.h
index c5aff43..3c7a4e9 100644
--- a/tls/gnutls/gtlscertificate-gnutls.h
+++ b/tls/gnutls/gtlscertificate-gnutls.h
@@ -70,6 +70,18 @@ GTlsCertificateGnutls* g_tls_certificate_gnutls_build_chain (const gnu
guint num_certs,
gnutls_x509_crt_fmt_t format);
+GTlsCertificate * g_tls_certificate_gnutls_pkcs11_new (gpointer certificate_der,
+ gsize
certificate_der_length,
+ const gchar *certificate_uri,
+ const gchar *private_key_uri,
+ GTlsCertificate *issuer);
+
+gchar * g_tls_certificate_gnutls_pkcs11_build_certificate_uri (GTlsCertificateGnutls *self,
+ const gchar *interaction_id);
+
+gchar * g_tls_certificate_gnutls_pkcs11_build_private_key_uri (GTlsCertificateGnutls *self,
+ const gchar *interaction_id);
+
G_END_DECLS
#endif /* __G_TLS_CERTIFICATE_GNUTLS_H___ */
diff --git a/tls/gnutls/gtlsconnection-gnutls.c b/tls/gnutls/gtlsconnection-gnutls.c
index 774d668..4eb1e36 100644
--- a/tls/gnutls/gtlsconnection-gnutls.c
+++ b/tls/gnutls/gtlsconnection-gnutls.c
@@ -40,6 +40,11 @@
#include "gtlsoutputstream-gnutls.h"
#include "gtlsserverconnection-gnutls.h"
+#ifdef HAVE_PKCS11
+#include <p11-kit/pin.h>
+#include "pkcs11/gpkcs11pin.h"
+#endif
+
#ifdef G_OS_WIN32
#include <winsock2.h>
#include <winerror.h>
@@ -103,6 +108,14 @@ static gboolean g_tls_connection_gnutls_initable_init (GInitable *in
static void g_tls_connection_gnutls_dtls_connection_iface_init (GDtlsConnectionInterface *iface);
static void g_tls_connection_gnutls_datagram_based_iface_init (GDatagramBasedInterface *iface);
+#ifdef HAVE_PKCS11
+static P11KitPin* on_pin_prompt_callback (const char *pinfile,
+ P11KitUri *pin_uri,
+ const char *pin_description,
+ P11KitPinFlags pin_flags,
+ void *callback_data);
+#endif
+
static void g_tls_connection_gnutls_init_priorities (void);
static int verify_certificate_cb (gnutls_session_t session);
@@ -269,6 +282,11 @@ g_tls_connection_gnutls_init (GTlsConnectionGnutls *gnutls)
unique_id = g_atomic_int_add (&unique_interaction_id, 1);
priv->interaction_id = g_strdup_printf ("gtls:%d", unique_id);
+#ifdef HAVE_PKCS11
+ p11_kit_pin_register_callback (priv->interaction_id,
+ on_pin_prompt_callback, gnutls, NULL);
+#endif
+
priv->waiting_for_op = g_cancellable_new ();
g_cancellable_cancel (priv->waiting_for_op);
g_mutex_init (&priv->op_mutex);
@@ -462,6 +480,10 @@ g_tls_connection_gnutls_finalize (GObject *object)
g_clear_pointer (&priv->app_data_buf, g_byte_array_unref);
+#ifdef HAVE_PKCS11
+ p11_kit_pin_unregister_callback (priv->interaction_id,
+ on_pin_prompt_callback, gnutls);
+#endif
g_free (priv->interaction_id);
g_clear_object (&priv->interaction);
@@ -3117,6 +3139,60 @@ g_tls_connection_gnutls_dtls_get_negotiated_protocol (GDtlsConnection *conn)
}
#endif
+#ifdef HAVE_PKCS11
+
+static P11KitPin*
+on_pin_prompt_callback (const char *pinfile,
+ P11KitUri *pin_uri,
+ const char *pin_description,
+ P11KitPinFlags pin_flags,
+ void *callback_data)
+{
+ GTlsConnectionGnutls *gnutls = G_TLS_CONNECTION_GNUTLS (callback_data);
+ GTlsConnectionGnutlsPrivate *priv = g_tls_connection_gnutls_get_instance_private (gnutls);
+ GTlsInteractionResult result;
+ GTlsPasswordFlags flags = 0;
+ GTlsPassword *password;
+ P11KitPin *pin = NULL;
+ GError *error = NULL;
+
+ if (!priv->interaction)
+ return NULL;
+
+ if (pin_flags & P11_KIT_PIN_FLAGS_RETRY)
+ flags |= G_TLS_PASSWORD_RETRY;
+ if (pin_flags & P11_KIT_PIN_FLAGS_MANY_TRIES)
+ flags |= G_TLS_PASSWORD_MANY_TRIES;
+ if (pin_flags & P11_KIT_PIN_FLAGS_FINAL_TRY)
+ flags |= G_TLS_PASSWORD_FINAL_TRY;
+
+ password = g_pkcs11_pin_new (flags, pin_description);
+
+ result = g_tls_interaction_ask_password (priv->interaction, password,
+ g_cancellable_get_current (), &error);
+
+ switch (result)
+ {
+ case G_TLS_INTERACTION_FAILED:
+ if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
+ g_warning ("couldn't ask for password: %s", error->message);
+ pin = NULL;
+ break;
+ case G_TLS_INTERACTION_UNHANDLED:
+ default:
+ pin = NULL;
+ break;
+ case G_TLS_INTERACTION_HANDLED:
+ pin = g_pkcs11_pin_steal_internal (G_PKCS11_PIN (password));
+ break;
+ }
+
+ g_object_unref (password);
+ return pin;
+}
+
+#endif /* HAVE_PKCS11 */
+
static void
g_tls_connection_gnutls_class_init (GTlsConnectionGnutlsClass *klass)
{
diff --git a/tls/gnutls/gtlsdatabase-gnutls-pkcs11.c b/tls/gnutls/gtlsdatabase-gnutls-pkcs11.c
new file mode 100644
index 0000000..6a647d3
--- /dev/null
+++ b/tls/gnutls/gtlsdatabase-gnutls-pkcs11.c
@@ -0,0 +1,1139 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright 2011 Collabora, Ltd
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#include "config.h"
+
+#include "gtlsdatabase-gnutls-pkcs11.h"
+#include "gtlscertificate-gnutls.h"
+
+#include <gio/gio.h>
+#include <glib/gi18n-lib.h>
+#include <gnutls/x509.h>
+
+#include <p11-kit/p11-kit.h>
+#include <stdlib.h>
+
+#include "pkcs11/gpkcs11pin.h"
+#include "pkcs11/gpkcs11slot.h"
+#include "pkcs11/gpkcs11util.h"
+#include "pkcs11/pkcs11-trust-assertions.h"
+
+typedef enum {
+ G_TLS_DATABASE_GNUTLS_PINNED_CERTIFICATE = 1,
+ G_TLS_DATABASE_GNUTLS_ANCHORED_CERTIFICATE = 2,
+} GTlsDatabaseGnutlsAssertion;
+
+static const CK_ATTRIBUTE_TYPE CERTIFICATE_ATTRIBUTE_TYPES[] = {
+ CKA_ID, CKA_LABEL, CKA_CLASS, CKA_VALUE
+};
+
+static const CK_ATTRIBUTE_TYPE KEY_ATTRIBUTE_TYPES[] = {
+ CKA_ID, CKA_LABEL, CKA_CLASS, CKA_KEY_TYPE
+};
+
+static void g_tls_database_gnutls_pkcs11_initable_iface_init (GInitableIface *iface);
+
+struct _GTlsDatabaseGnutlsPkcs11
+{
+ GTlsDatabase parent_instance;
+
+ /* no changes after construction */
+ CK_FUNCTION_LIST **modules;
+ GList *pkcs11_slots;
+ GList *trust_uris;
+};
+
+G_DEFINE_TYPE_WITH_CODE (GTlsDatabaseGnutlsPkcs11, g_tls_database_gnutls_pkcs11,
+ G_TYPE_TLS_DATABASE,
+ G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE,
+ g_tls_database_gnutls_pkcs11_initable_iface_init));
+
+static gboolean
+discover_module_slots_and_options (GTlsDatabaseGnutlsPkcs11 *self,
+ CK_FUNCTION_LIST_PTR module,
+ GError **error)
+{
+ CK_ULONG i, count = 0;
+ CK_SLOT_ID *list;
+ GPkcs11Slot *slot;
+ P11KitUri *uri;
+ char *string;
+ guint uri_type;
+ int ret;
+ CK_RV rv;
+
+ /*
+ * Ask module for the number of slots. We include slots without tokens
+ * since we want to be able to use them if the user inserts a token
+ * later.
+ */
+
+ rv = (module->C_GetSlotList) (CK_FALSE, NULL, &count);
+ if (rv != CKR_OK)
+ {
+ g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
+ "Couldn't load list of slots in PKCS#11 module: %s",
+ p11_kit_strerror (rv));
+ return FALSE;
+ }
+
+ if (count == 0)
+ return TRUE;
+
+ /* Actually retrieve the slot ids */
+ list = g_new0 (CK_SLOT_ID, count);
+ rv = (module->C_GetSlotList) (CK_FALSE, list, &count);
+ if (rv != CKR_OK)
+ {
+ g_set_error (error, G_TLS_ERROR, G_TLS_ERROR_MISC,
+ "Couldn't load list of slots in PKCS#11 module: %s",
+ p11_kit_strerror (rv));
+ g_free (list);
+ return FALSE;
+ }
+
+ for (i = 0; i < count; ++i)
+ {
+ slot = g_object_new (G_TYPE_PKCS11_SLOT,
+ "slot-id", list[i],
+ "module", module,
+ NULL);
+ self->pkcs11_slots = g_list_append (self->pkcs11_slots, slot);
+ }
+
+ /*
+ * Load up relevant options. We use the x-trust-lookup option to determine
+ * which slots we can use for looking up trust assertionts.
+ */
+
+ string = p11_kit_config_option (module, "x-trust-lookup");
+ if (string != NULL)
+ {
+ uri = p11_kit_uri_new ();
+ uri_type = P11_KIT_URI_FOR_TOKEN | P11_KIT_URI_FOR_MODULE_WITH_VERSION;
+ ret = p11_kit_uri_parse (string, uri_type, uri);
+
+ if (ret < 0)
+ {
+ g_message ("couldn't parse configured uri for trust lookups: %s: %s",
+ string, p11_kit_uri_message (ret));
+ p11_kit_uri_free (uri);
+ }
+ else
+ {
+ self->trust_uris = g_list_append (self->trust_uris, uri);
+ }
+
+ free (string);
+ }
+
+ return TRUE;
+}
+
+static GTlsCertificate *
+create_database_pkcs11_certificate (GPkcs11Slot *slot,
+ GPkcs11Array *certificate_attrs,
+ GPkcs11Array *private_key_attrs)
+{
+ GTlsCertificate *certificate;
+ gchar *certificate_uri = NULL;
+ gchar *private_key_uri = NULL;
+ const CK_ATTRIBUTE *value_attr;
+ P11KitUri *uri;
+ int ret;
+
+ value_attr = g_pkcs11_array_find (certificate_attrs, CKA_VALUE);
+ if (value_attr == NULL)
+ return NULL;
+
+ uri = p11_kit_uri_new ();
+
+ /*
+ * The PKCS#11 URIs we create for certificates and keys are not bound to
+ * the module. They are bound to the token.
+ *
+ * For example the user could have keys on a smart card token. He could insert
+ * this smart card into a different slot, or perhaps change the driver
+ * (through an OS upgrade). So the key and certificate should still be
+ * referenceable through the URI.
+ *
+ * We also set a 'pinfile' prompting id, so that users of p11-kit like
+ * gnutls can call our callback.
+ */
+
+ if (!g_pkcs11_slot_get_token_info (slot, p11_kit_uri_get_token_info (uri)))
+ g_return_val_if_reached (NULL);
+
+ ret = p11_kit_uri_set_attributes (uri, certificate_attrs->attrs,
+ certificate_attrs->count);
+ g_return_val_if_fail (ret == P11_KIT_URI_OK, NULL);
+
+ ret = p11_kit_uri_format (uri, P11_KIT_URI_FOR_OBJECT_ON_TOKEN, &certificate_uri);
+ g_return_val_if_fail (ret == P11_KIT_URI_OK, NULL);
+
+ if (private_key_attrs != NULL)
+ {
+
+ /* The URI will keep the token info above, so we just change attributes */
+
+ ret = p11_kit_uri_set_attributes (uri, private_key_attrs->attrs,
+ private_key_attrs->count);
+ g_return_val_if_fail (ret == P11_KIT_URI_OK, NULL);
+
+ ret = p11_kit_uri_format (uri, P11_KIT_URI_FOR_OBJECT_ON_TOKEN, &private_key_uri);
+ g_return_val_if_fail (ret == P11_KIT_URI_OK, NULL);
+ }
+
+ certificate = g_tls_certificate_gnutls_pkcs11_new (value_attr->pValue,
+ value_attr->ulValueLen,
+ certificate_uri,
+ private_key_uri,
+ NULL);
+
+ p11_kit_uri_free (uri);
+ g_free (certificate_uri);
+ g_free (private_key_uri);
+
+ return certificate;
+}
+
+static const gchar *
+calculate_peer_for_identity (GSocketConnectable *identity)
+{
+ const char *peer;
+
+ if (G_IS_NETWORK_ADDRESS (identity))
+ peer = g_network_address_get_hostname (G_NETWORK_ADDRESS (identity));
+ else if (G_IS_NETWORK_SERVICE (identity))
+ peer = g_network_service_get_domain (G_NETWORK_SERVICE (identity));
+ else
+ peer = NULL;
+
+ return peer;
+}
+
+static void
+g_tls_database_gnutls_pkcs11_finalize (GObject *object)
+{
+ GTlsDatabaseGnutlsPkcs11 *self = G_TLS_DATABASE_GNUTLS_PKCS11 (object);
+ GList *l;
+
+ for (l = self->pkcs11_slots; l; l = g_list_next (l))
+ g_object_unref (l->data);
+ g_list_free (self->pkcs11_slots);
+
+ for (l = self->trust_uris; l; l = g_list_next (l))
+ p11_kit_uri_free (l->data);
+ g_list_free (self->trust_uris);
+
+ if (self->modules)
+ p11_kit_modules_release (self->modules);
+
+ G_OBJECT_CLASS (g_tls_database_gnutls_pkcs11_parent_class)->finalize (object);
+}
+
+static void
+g_tls_database_gnutls_pkcs11_init (GTlsDatabaseGnutlsPkcs11 *self)
+{
+}
+
+static gboolean
+accumulate_stop (gpointer result,
+ gpointer user_data)
+{
+ return FALSE; /* stop enumeration */
+}
+
+static gboolean
+accumulate_exists (gpointer result,
+ gpointer user_data)
+{
+ gboolean *exists = (gboolean *)user_data;
+ *exists = TRUE;
+ return FALSE; /* stop enumeration */
+}
+
+static gboolean
+accumulate_first_attributes (gpointer result,
+ gpointer user_data)
+{
+ GPkcs11Array **attributes = (GPkcs11Array **)user_data;
+ g_assert (attributes);
+ *attributes = g_pkcs11_array_ref (result);
+ return FALSE; /* stop enumeration */
+}
+
+static gboolean
+accumulate_list_attributes (gpointer result,
+ gpointer user_data)
+{
+ GList **results = (GList **)user_data;
+ g_assert (results);
+ *results = g_list_append (*results, g_pkcs11_array_ref (result));
+ return TRUE; /* continue enumeration */
+}
+
+static gboolean
+accumulate_first_object (gpointer result,
+ gpointer user_data)
+{
+ GObject **object = (GObject **)user_data;
+ g_assert (object);
+ *object = g_object_ref (result);
+ return FALSE; /* stop enumeration */
+}
+
+static gboolean
+accumulate_list_objects (gpointer result,
+ gpointer user_data)
+{
+ GList **results = (GList **)user_data;
+ g_assert (results);
+ *results = g_list_append (*results, g_object_ref (result));
+ return TRUE; /* continue enumeration */
+}
+
+static GPkcs11EnumerateState
+enumerate_call_accumulator (GPkcs11Accumulator accumulator,
+ gpointer result,
+ gpointer user_data)
+{
+ g_assert (accumulator);
+
+ if (!(accumulator) (result, user_data))
+ return G_PKCS11_ENUMERATE_STOP;
+
+ return G_PKCS11_ENUMERATE_CONTINUE;
+}
+
+static GPkcs11EnumerateState
+enumerate_assertion_exists_in_slot (GPkcs11Slot *slot,
+ GTlsInteraction *interaction,
+ GPkcs11Array *match,
+ GPkcs11Accumulator accumulator,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GPkcs11EnumerateState state;
+
+ state = g_pkcs11_slot_enumerate (slot, interaction, match->attrs, match->count,
+ FALSE, NULL, 0, accumulate_stop, NULL,
+ cancellable, error);
+
+ /* A stop means that something matched */
+ if (state == G_PKCS11_ENUMERATE_STOP)
+ return enumerate_call_accumulator (accumulator, NULL, user_data);
+
+ return state;
+}
+
+static GPkcs11EnumerateState
+enumerate_assertion_exists_in_database (GTlsDatabaseGnutlsPkcs11 *self,
+ GTlsInteraction *interaction,
+ GPkcs11Array *match,
+ GPkcs11Accumulator accumulator,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GPkcs11EnumerateState state = G_PKCS11_ENUMERATE_CONTINUE;
+ gboolean slot_matched;
+ GPkcs11Slot *slot;
+ GList *l, *t;
+
+ for (l = self->pkcs11_slots; l != NULL; l = g_list_next (l))
+ {
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return G_PKCS11_ENUMERATE_FAILED;
+
+ slot = l->data;
+
+ /* We only search for assertions on slots that match the trust-lookup uris */
+ slot_matched = FALSE;
+ for (t = self->trust_uris; !slot_matched && t != NULL; t = g_list_next (t))
+ slot_matched = g_pkcs11_slot_matches_uri (slot, t->data);
+ if (!slot_matched)
+ continue;
+
+ state = enumerate_assertion_exists_in_slot (slot, interaction, match, accumulator,
+ user_data, cancellable, error);
+ if (state != G_PKCS11_ENUMERATE_CONTINUE)
+ break;
+ }
+
+ return state;
+}
+
+static gboolean
+g_tls_database_gnutls_pkcs11_lookup_assertion (GTlsDatabaseGnutlsPkcs11 *self,
+ GTlsCertificateGnutls *certificate,
+ GTlsDatabaseGnutlsAssertion assertion,
+ const gchar *purpose,
+ GSocketConnectable *identity,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GByteArray *der = NULL;
+ gboolean found, ready;
+ GPkcs11Array *match;
+ const gchar *peer;
+
+ ready = FALSE;
+ found = FALSE;
+ match = g_pkcs11_array_new ();
+
+ if (assertion == G_TLS_DATABASE_GNUTLS_ANCHORED_CERTIFICATE ||
+ assertion == G_TLS_DATABASE_GNUTLS_PINNED_CERTIFICATE)
+ {
+ g_object_get (certificate, "certificate", &der, NULL);
+ g_return_val_if_fail (der, FALSE);
+ g_pkcs11_array_add_value (match, CKA_X_CERTIFICATE_VALUE, der->data, der->len);
+ g_byte_array_unref (der);
+
+ g_pkcs11_array_add_value (match, CKA_X_PURPOSE, purpose, -1);
+
+ if (assertion == G_TLS_DATABASE_GNUTLS_ANCHORED_CERTIFICATE)
+ {
+ g_pkcs11_array_add_ulong (match, CKA_X_ASSERTION_TYPE, CKT_X_ANCHORED_CERTIFICATE);
+ ready = TRUE;
+ }
+ else if (assertion == G_TLS_DATABASE_GNUTLS_PINNED_CERTIFICATE)
+ {
+ g_pkcs11_array_add_ulong (match, CKA_X_ASSERTION_TYPE, CKT_X_PINNED_CERTIFICATE);
+ peer = calculate_peer_for_identity (identity);
+ if (peer)
+ {
+ g_pkcs11_array_add_value (match, CKA_X_PEER, peer, -1);
+ ready = TRUE;
+ }
+ }
+ }
+
+ if (ready == TRUE)
+ enumerate_assertion_exists_in_database (self, NULL, match, accumulate_exists,
+ &found, cancellable, error);
+
+ g_pkcs11_array_unref (match);
+ return found;
+}
+
+static GPkcs11EnumerateState
+enumerate_keypair_for_certificate (GPkcs11Slot *slot,
+ GTlsInteraction *interaction,
+ GPkcs11Array *match_certificate,
+ GPkcs11Accumulator accumulator,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **error)
+{
+ static CK_OBJECT_CLASS key_class = CKO_PRIVATE_KEY;
+ GPkcs11Array *private_key_attrs = NULL;
+ const CK_ATTRIBUTE *id_attribute;
+ CK_ATTRIBUTE match[2];
+ GTlsCertificate *certificate;
+ GPkcs11EnumerateState state;
+
+ /*
+ * We need to find a private key that matches the certificate.
+ *
+ * The PKCS#11 standard strongly suggests the norm that matching certificates
+ * and keys have the same CKA_ID. This is how we lookup the key that matches
+ * a certificate.
+ */
+
+ id_attribute = g_pkcs11_array_find (match_certificate, CKA_ID);
+ if (id_attribute == NULL)
+ return TRUE;
+
+ match[0].type = CKA_ID;
+ match[0].pValue = id_attribute->pValue;
+ match[0].ulValueLen = id_attribute->ulValueLen;
+ match[1].type = CKA_CLASS;
+ match[1].pValue = &key_class;
+ match[1].ulValueLen = sizeof (key_class);
+
+ g_assert (private_key_attrs == NULL);
+ state = g_pkcs11_slot_enumerate (slot, interaction, match, G_N_ELEMENTS (match), TRUE,
+ KEY_ATTRIBUTE_TYPES, G_N_ELEMENTS (KEY_ATTRIBUTE_TYPES),
+ accumulate_first_attributes, &private_key_attrs,
+ cancellable, error);
+
+ if (state == G_PKCS11_ENUMERATE_FAILED)
+ return state;
+
+ state = G_PKCS11_ENUMERATE_CONTINUE;
+ if (private_key_attrs)
+ {
+ /* We searched for public key (see above) so change attributes to look like private */
+ g_pkcs11_array_set_ulong (private_key_attrs, CKA_CLASS, CKO_PRIVATE_KEY);
+ certificate = create_database_pkcs11_certificate (slot, match_certificate,
+ private_key_attrs);
+ g_pkcs11_array_unref (private_key_attrs);
+
+ if (certificate)
+ {
+ state = enumerate_call_accumulator (accumulator, certificate, user_data);
+ g_object_unref (certificate);
+ }
+ }
+
+ return state;
+}
+
+static GPkcs11EnumerateState
+enumerate_keypairs_in_slot (GPkcs11Slot *slot,
+ GTlsInteraction *interaction,
+ CK_ATTRIBUTE_PTR match,
+ CK_ULONG match_count,
+ GPkcs11Accumulator accumulator,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GPkcs11EnumerateState state;
+ GList *results = NULL;
+ GList *l;
+
+ /*
+ * Find all the certificates that match for this slot, and then below
+ * we lookup to see if there's a private key for any of them.
+ *
+ * Note that we shouldn't be doing two find operations at once, because
+ * this may use too many sessions on smart cards and fragile drivers. So
+ * that's why we list all certificates, complete that find operation, and
+ * then do more find ops looking for private keys.
+ */
+
+ state = g_pkcs11_slot_enumerate (slot, interaction, match, match_count, FALSE,
+ CERTIFICATE_ATTRIBUTE_TYPES,
+ G_N_ELEMENTS (CERTIFICATE_ATTRIBUTE_TYPES),
+ accumulate_list_attributes, &results,
+ cancellable, error);
+ if (state == G_PKCS11_ENUMERATE_CONTINUE)
+ {
+ for (l = results; l != NULL; l = g_list_next (l))
+ {
+ state = enumerate_keypair_for_certificate (slot, interaction, l->data, accumulator,
+ user_data, cancellable, error);
+ if (state != G_PKCS11_ENUMERATE_CONTINUE)
+ break;
+ }
+ }
+
+ for (l = results; l != NULL; l = g_list_next (l))
+ g_pkcs11_array_unref (l->data);
+ g_list_free (results);
+
+ return state;
+}
+
+typedef struct {
+ GPkcs11Accumulator accumulator;
+ gpointer user_data;
+ GPkcs11Slot *slot;
+} enumerate_certificates_closure;
+
+static gboolean
+accumulate_wrap_into_certificate (gpointer result,
+ gpointer user_data)
+{
+ GPkcs11EnumerateState state = G_PKCS11_ENUMERATE_CONTINUE;
+ enumerate_certificates_closure *closure = user_data;
+ GTlsCertificate *certificate;
+
+ certificate = create_database_pkcs11_certificate (closure->slot,
+ result, NULL);
+ if (certificate)
+ {
+ state = enumerate_call_accumulator (closure->accumulator, certificate,
+ closure->user_data);
+ g_object_unref (certificate);
+ }
+
+ return (state == G_PKCS11_ENUMERATE_CONTINUE);
+}
+
+static GPkcs11EnumerateState
+enumerate_certificates_in_slot (GPkcs11Slot *slot,
+ GTlsInteraction *interaction,
+ CK_ATTRIBUTE_PTR match,
+ CK_ULONG match_count,
+ GPkcs11Accumulator accumulator,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **error)
+{
+ enumerate_certificates_closure closure = { accumulator, user_data, slot };
+
+ /*
+ * We create the certificates inline, so we can stop the enumeration early
+ * if only one certificate is necessary, but a whole bunch match. We provide
+ * our own accumulator here, turning the attributes into certificates and
+ * then calling the original accumulator.
+ */
+
+ return g_pkcs11_slot_enumerate (slot, interaction, match, match_count, FALSE,
+ CERTIFICATE_ATTRIBUTE_TYPES,
+ G_N_ELEMENTS (CERTIFICATE_ATTRIBUTE_TYPES),
+ accumulate_wrap_into_certificate,
+ &closure, cancellable, error);
+}
+
+static GPkcs11EnumerateState
+enumerate_certificates_in_database (GTlsDatabaseGnutlsPkcs11 *self,
+ GTlsInteraction *interaction,
+ GTlsDatabaseLookupFlags flags,
+ CK_ATTRIBUTE_PTR match,
+ CK_ULONG match_count,
+ P11KitUri *match_slot_to_uri,
+ GPkcs11Accumulator accumulator,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GPkcs11EnumerateState state = G_PKCS11_ENUMERATE_CONTINUE;
+ GPkcs11Slot *slot;
+ GList *l;
+
+ /* These are the flags we support */
+ if (flags & ~(G_TLS_DATABASE_LOOKUP_KEYPAIR))
+ return G_PKCS11_ENUMERATE_CONTINUE;
+
+ for (l = self->pkcs11_slots; l; l = g_list_next (l))
+ {
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return G_PKCS11_ENUMERATE_FAILED;
+
+ slot = l->data;
+
+ /* If the slot doesn't match the URI (when one is present) nothing matches */
+ if (match_slot_to_uri && !g_pkcs11_slot_matches_uri (slot, match_slot_to_uri))
+ continue;
+
+ if (flags & G_TLS_DATABASE_LOOKUP_KEYPAIR)
+ {
+ state = enumerate_keypairs_in_slot (slot, interaction, match,
+ match_count, accumulator, user_data,
+ cancellable, error);
+
+ }
+ else
+ {
+ state = enumerate_certificates_in_slot (slot, interaction, match,
+ match_count, accumulator,
+ user_data, cancellable, error);
+ }
+
+ if (state != G_PKCS11_ENUMERATE_CONTINUE)
+ break;
+ }
+
+ return state;
+}
+
+static GTlsCertificate *
+g_tls_database_gnutls_pkcs11_lookup_certificate_issuer (GTlsDatabase *database,
+ GTlsCertificate *certificate,
+ GTlsInteraction *interaction,
+ GTlsDatabaseLookupFlags flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsDatabaseGnutlsPkcs11 *self = G_TLS_DATABASE_GNUTLS_PKCS11 (database);
+ GTlsCertificate *result = NULL;
+ GPkcs11Array *match = NULL;
+ gnutls_x509_crt_t cert;
+ gnutls_datum_t dn;
+ int gerr;
+
+ g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (certificate), NULL);
+
+ /* Dig out the issuer of this certificate */
+ cert = g_tls_certificate_gnutls_get_cert (G_TLS_CERTIFICATE_GNUTLS (certificate));
+ gerr = gnutls_x509_crt_get_raw_issuer_dn (cert, &dn);
+ if (gerr < 0)
+ {
+ g_warning ("failed to get issuer of certificate: %s", gnutls_strerror (gerr));
+ return NULL;
+ }
+
+ match = g_pkcs11_array_new ();
+ g_pkcs11_array_add_ulong (match, CKA_CLASS, CKO_CERTIFICATE);
+ g_pkcs11_array_add_ulong (match, CKA_CERTIFICATE_TYPE, CKC_X_509);
+ g_pkcs11_array_add_value (match, CKA_SUBJECT, dn.data, dn.size);
+ gnutls_free (dn.data);
+
+ enumerate_certificates_in_database (self, interaction, flags, match->attrs,
+ match->count, NULL, accumulate_first_object,
+ &result, cancellable, error);
+ g_pkcs11_array_unref (match);
+ return result;
+}
+
+static GList *
+g_tls_database_gnutls_pkcs11_lookup_certificates_issued_by (GTlsDatabase *database,
+ GByteArray *issuer_subject,
+ GTlsInteraction *interaction,
+ GTlsDatabaseLookupFlags flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsDatabaseGnutlsPkcs11 *self = G_TLS_DATABASE_GNUTLS_PKCS11 (database);
+ GList *l, *results = NULL;
+ GPkcs11Array *match = NULL;
+ GPkcs11EnumerateState state;
+
+ g_return_val_if_fail (issuer_subject, NULL);
+
+ match = g_pkcs11_array_new ();
+ g_pkcs11_array_add_ulong (match, CKA_CLASS, CKO_CERTIFICATE);
+ g_pkcs11_array_add_ulong (match, CKA_CERTIFICATE_TYPE, CKC_X_509);
+ g_pkcs11_array_add_value (match, CKA_ISSUER, issuer_subject->data, issuer_subject->len);
+
+ state = enumerate_certificates_in_database (self, interaction, flags, match->attrs,
+ match->count, NULL, accumulate_list_objects,
+ &results, cancellable, error);
+
+ /* Could have had partial success, don't leak memory */
+ if (state == G_PKCS11_ENUMERATE_FAILED)
+ {
+ for (l = results; l != NULL; l = g_list_next (l))
+ g_object_unref (l->data);
+ g_list_free (results);
+ results = NULL;
+ }
+
+ g_pkcs11_array_unref (match);
+ return results;
+}
+
+static gchar *
+g_tls_database_gnutls_pkcs11_create_certificate_handle (GTlsDatabase *database,
+ GTlsCertificate *certificate)
+{
+ return g_tls_certificate_gnutls_pkcs11_build_certificate_uri (G_TLS_CERTIFICATE_GNUTLS (certificate),
NULL);
+}
+
+static GTlsCertificate *
+g_tls_database_gnutls_pkcs11_lookup_certificate_for_handle (GTlsDatabase *database,
+ const gchar *handle,
+ GTlsInteraction *interaction,
+ GTlsDatabaseLookupFlags flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsDatabaseGnutlsPkcs11 *self = G_TLS_DATABASE_GNUTLS_PKCS11 (database);
+ GTlsCertificate *result = NULL;
+ P11KitUri *uri;
+ CK_ATTRIBUTE_PTR match;
+ CK_ULONG match_count;
+ int ret;
+
+ /* The handle is a PKCS#11 URI */
+
+ /* These are the flags we support */
+ if (flags & ~(G_TLS_DATABASE_LOOKUP_KEYPAIR))
+ return NULL;
+
+ uri = p11_kit_uri_new ();
+ if (uri == NULL)
+ g_error ("out of memory in p11_kit_uri_new()");
+
+ ret = p11_kit_uri_parse (handle, P11_KIT_URI_FOR_OBJECT_ON_TOKEN_AND_MODULE |
+ P11_KIT_URI_FOR_MODULE_WITH_VERSION, uri);
+ if (ret == P11_KIT_URI_NO_MEMORY)
+ {
+ g_error ("out of memory in p11_kit_uri_parse()");
+ }
+ else if (ret != P11_KIT_URI_OK)
+ {
+ p11_kit_uri_free (uri);
+ g_set_error (error, G_PKCS11_ERROR, G_PKCS11_ERROR_BAD_URI,
+ "Invalid PKCS#11 URI: %s", handle);
+ return NULL;
+ }
+
+ match = p11_kit_uri_get_attributes (uri, &match_count);
+ enumerate_certificates_in_database (self, interaction, flags, match, match_count,
+ uri, accumulate_first_object, &result,
+ cancellable, error);
+
+ p11_kit_uri_free (uri);
+ return result;
+}
+
+#define BUILD_CERTIFICATE_CHAIN_RECURSION_LIMIT 10
+
+enum {
+ STATUS_FAILURE,
+ STATUS_INCOMPLETE,
+ STATUS_SELFSIGNED,
+ STATUS_ANCHORED,
+ STATUS_RECURSION_LIMIT_REACHED
+};
+
+static gboolean
+is_self_signed (GTlsCertificateGnutls *certificate)
+{
+ const gnutls_x509_crt_t cert = g_tls_certificate_gnutls_get_cert (certificate);
+ return (gnutls_x509_crt_check_issuer (cert, cert) > 0);
+}
+
+static gint
+build_certificate_chain (GTlsDatabaseGnutlsPkcs11 *self,
+ GTlsCertificateGnutls *certificate,
+ GTlsCertificateGnutls *previous,
+ gboolean certificate_is_from_db,
+ guint recursion_depth,
+ const gchar *purpose,
+ GSocketConnectable *identity,
+ GTlsInteraction *interaction,
+ GCancellable *cancellable,
+ GTlsCertificateGnutls **anchor,
+ GError **error)
+{
+ GTlsCertificate *issuer;
+ gint status;
+
+ if (recursion_depth++ > BUILD_CERTIFICATE_CHAIN_RECURSION_LIMIT)
+ return STATUS_RECURSION_LIMIT_REACHED;
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return STATUS_FAILURE;
+
+ /* Look up whether this certificate is an anchor */
+ if (g_tls_database_gnutls_pkcs11_lookup_assertion (self, certificate,
+ G_TLS_DATABASE_GNUTLS_ANCHORED_CERTIFICATE,
+ purpose, identity, cancellable, error))
+ {
+ g_tls_certificate_gnutls_set_issuer (certificate, NULL);
+ *anchor = certificate;
+ return STATUS_ANCHORED;
+ }
+ else if (*error)
+ {
+ return STATUS_FAILURE;
+ }
+
+ /* Is it self-signed? */
+ if (is_self_signed (certificate))
+ {
+ /*
+ * Since at this point we would fail with 'self-signed', can we replace
+ * this certificate with one from the database and do better?
+ */
+ if (previous && !certificate_is_from_db)
+ {
+ issuer = g_tls_database_lookup_certificate_issuer (G_TLS_DATABASE (self),
+ G_TLS_CERTIFICATE (previous),
+ interaction,
+ G_TLS_DATABASE_LOOKUP_NONE,
+ cancellable, error);
+ if (*error)
+ {
+ return STATUS_FAILURE;
+ }
+ else if (issuer)
+ {
+ /* Replaced with certificate in the db, restart step again with this certificate */
+ g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (issuer), STATUS_FAILURE);
+ certificate = G_TLS_CERTIFICATE_GNUTLS (issuer);
+ g_tls_certificate_gnutls_set_issuer (previous, certificate);
+ g_object_unref (issuer);
+
+ return build_certificate_chain (self, certificate, previous, TRUE, recursion_depth,
+ purpose, identity, interaction, cancellable, anchor, error);
+ }
+ }
+
+ g_tls_certificate_gnutls_set_issuer (certificate, NULL);
+ return STATUS_SELFSIGNED;
+ }
+
+ previous = certificate;
+
+ /* Bring over the next certificate in the chain */
+ issuer = g_tls_certificate_get_issuer (G_TLS_CERTIFICATE (certificate));
+ if (issuer)
+ {
+ g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (issuer), STATUS_FAILURE);
+ certificate = G_TLS_CERTIFICATE_GNUTLS (issuer);
+
+ status = build_certificate_chain (self, certificate, previous, FALSE, recursion_depth,
+ purpose, identity, interaction, cancellable, anchor, error);
+ if (status != STATUS_INCOMPLETE)
+ {
+ return status;
+ }
+ }
+
+ /* Search for the next certificate in chain */
+ issuer = g_tls_database_lookup_certificate_issuer (G_TLS_DATABASE (self),
+ G_TLS_CERTIFICATE (certificate),
+ interaction,
+ G_TLS_DATABASE_LOOKUP_NONE,
+ cancellable, error);
+ if (*error)
+ return STATUS_FAILURE;
+
+ if (!issuer)
+ return STATUS_INCOMPLETE;
+
+ g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (issuer), STATUS_FAILURE);
+ g_tls_certificate_gnutls_set_issuer (certificate, G_TLS_CERTIFICATE_GNUTLS (issuer));
+ certificate = G_TLS_CERTIFICATE_GNUTLS (issuer);
+ g_object_unref (issuer);
+
+ return build_certificate_chain (self, certificate, previous, TRUE, recursion_depth,
+ purpose, identity, interaction, cancellable, anchor, error);
+}
+
+static GTlsCertificateFlags
+double_check_before_after_dates (GTlsCertificateGnutls *chain)
+{
+ GTlsCertificateFlags gtls_flags = 0;
+ gnutls_x509_crt_t cert;
+ time_t t, now;
+
+ now = time (NULL);
+ while (chain)
+ {
+ cert = g_tls_certificate_gnutls_get_cert (chain);
+ t = gnutls_x509_crt_get_activation_time (cert);
+ if (t == (time_t) -1 || t > now)
+ gtls_flags |= G_TLS_CERTIFICATE_NOT_ACTIVATED;
+
+ t = gnutls_x509_crt_get_expiration_time (cert);
+ if (t == (time_t) -1 || t < now)
+ gtls_flags |= G_TLS_CERTIFICATE_EXPIRED;
+
+ chain = G_TLS_CERTIFICATE_GNUTLS (g_tls_certificate_get_issuer
+ (G_TLS_CERTIFICATE (chain)));
+ }
+
+ return gtls_flags;
+}
+
+static void
+convert_certificate_chain_to_gnutls (GTlsCertificateGnutls *chain,
+ gnutls_x509_crt_t **gnutls_chain,
+ guint *gnutls_chain_length)
+{
+ GTlsCertificate *cert;
+ guint i;
+
+ g_assert (gnutls_chain);
+ g_assert (gnutls_chain_length);
+
+ for (*gnutls_chain_length = 0, cert = G_TLS_CERTIFICATE (chain);
+ cert; cert = g_tls_certificate_get_issuer (cert))
+ ++(*gnutls_chain_length);
+
+ *gnutls_chain = g_new0 (gnutls_x509_crt_t, *gnutls_chain_length);
+
+ for (i = 0, cert = G_TLS_CERTIFICATE (chain);
+ cert; cert = g_tls_certificate_get_issuer (cert), ++i)
+ (*gnutls_chain)[i] = g_tls_certificate_gnutls_get_cert (G_TLS_CERTIFICATE_GNUTLS (cert));
+
+ g_assert (i == *gnutls_chain_length);
+}
+
+static GTlsCertificateFlags
+g_tls_database_gnutls_pkcs11_verify_chain (GTlsDatabase *database,
+ GTlsCertificate *chain,
+ const gchar *purpose,
+ GSocketConnectable *identity,
+ GTlsInteraction *interaction,
+ GTlsDatabaseVerifyFlags flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsDatabaseGnutlsPkcs11 *self;
+ GTlsCertificateFlags result;
+ GTlsCertificateGnutls *certificate;
+ GError *err = NULL;
+ GTlsCertificateGnutls *anchor;
+ guint gnutls_result;
+ gnutls_x509_crt_t *certs, *anchors;
+ guint certs_length, anchors_length;
+ gint status, gerr;
+ guint recursion_depth = 0;
+
+ g_return_val_if_fail (G_IS_TLS_CERTIFICATE_GNUTLS (chain),
+ G_TLS_CERTIFICATE_GENERIC_ERROR);
+ g_assert (purpose);
+
+ self = G_TLS_DATABASE_GNUTLS_PKCS11 (database);
+ certificate = G_TLS_CERTIFICATE_GNUTLS (chain);
+
+ /* First check for pinned certificate */
+ if (g_tls_database_gnutls_pkcs11_lookup_assertion (self, certificate,
+ G_TLS_DATABASE_GNUTLS_PINNED_CERTIFICATE,
+ purpose, identity, cancellable, &err))
+ {
+ /*
+ * A pinned certificate is verified on its own, without any further
+ * verification.
+ */
+ g_tls_certificate_gnutls_set_issuer (certificate, NULL);
+ return 0;
+ }
+
+ if (err)
+ {
+ g_propagate_error (error, err);
+ return G_TLS_CERTIFICATE_GENERIC_ERROR;
+ }
+
+ anchor = NULL;
+ status = build_certificate_chain (self, certificate, NULL, FALSE, recursion_depth,
+ purpose, identity, interaction, cancellable, &anchor, &err);
+ if (status == STATUS_FAILURE)
+ {
+ g_propagate_error (error, err);
+ return G_TLS_CERTIFICATE_GENERIC_ERROR;
+ }
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return G_TLS_CERTIFICATE_GENERIC_ERROR;
+
+ convert_certificate_chain_to_gnutls (G_TLS_CERTIFICATE_GNUTLS (chain),
+ &certs, &certs_length);
+
+ if (anchor)
+ {
+ g_assert (g_tls_certificate_get_issuer (G_TLS_CERTIFICATE (anchor)) == NULL);
+ convert_certificate_chain_to_gnutls (G_TLS_CERTIFICATE_GNUTLS (anchor),
+ &anchors, &anchors_length);
+ }
+ else
+ {
+ anchors = NULL;
+ anchors_length = 0;
+ }
+
+ gerr = gnutls_x509_crt_list_verify (certs, certs_length,
+ anchors, anchors_length,
+ NULL, 0, 0,
+ &gnutls_result);
+
+ g_free (certs);
+ g_free (anchors);
+
+ if (gerr != 0)
+ return G_TLS_CERTIFICATE_GENERIC_ERROR;
+ else if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return G_TLS_CERTIFICATE_GENERIC_ERROR;
+
+ result = g_tls_certificate_gnutls_convert_flags (gnutls_result);
+
+ /*
+ * We have to check these ourselves since gnutls_x509_crt_list_verify
+ * won't bother if it gets an UNKNOWN_CA.
+ */
+ result |= double_check_before_after_dates (G_TLS_CERTIFICATE_GNUTLS (chain));
+
+ if (identity)
+ result |= g_tls_certificate_gnutls_verify_identity (G_TLS_CERTIFICATE_GNUTLS (chain),
+ identity);
+
+ return result;
+}
+
+static void
+g_tls_database_gnutls_pkcs11_class_init (GTlsDatabaseGnutlsPkcs11Class *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GTlsDatabaseClass *database_class = G_TLS_DATABASE_CLASS (klass);
+
+ gobject_class->finalize = g_tls_database_gnutls_pkcs11_finalize;
+
+ database_class->create_certificate_handle = g_tls_database_gnutls_pkcs11_create_certificate_handle;
+ database_class->lookup_certificate_issuer = g_tls_database_gnutls_pkcs11_lookup_certificate_issuer;
+ database_class->lookup_certificates_issued_by = g_tls_database_gnutls_pkcs11_lookup_certificates_issued_by;
+ database_class->lookup_certificate_for_handle = g_tls_database_gnutls_pkcs11_lookup_certificate_for_handle;
+ database_class->verify_chain = g_tls_database_gnutls_pkcs11_verify_chain;
+}
+
+static gboolean
+g_tls_database_gnutls_pkcs11_initable_init (GInitable *initable,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsDatabaseGnutlsPkcs11 *self = G_TLS_DATABASE_GNUTLS_PKCS11 (initable);
+ GError *err = NULL;
+ gboolean any_success = FALSE;
+ gboolean any_failure = FALSE;
+ guint i;
+
+ g_return_val_if_fail (!self->modules, FALSE);
+
+ self->modules = p11_kit_modules_load (NULL, 0);
+ if (self->modules == NULL) {
+ g_set_error_literal (error, G_PKCS11_ERROR, CKR_FUNCTION_FAILED, p11_kit_message ());
+ return FALSE;
+ }
+
+ for (i = 0; self->modules[i] != NULL; i++)
+ {
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ {
+ any_failure = TRUE;
+ any_success = FALSE;
+ break;
+ }
+
+ if (discover_module_slots_and_options (self, self->modules[i], &err))
+ {
+ /* A module was setup correctly */
+ any_success = TRUE;
+ g_clear_error (error);
+ }
+ else
+ {
+ /* No module success, first module failure */
+ if (!any_success && !any_failure)
+ g_propagate_error (error, err);
+ any_failure = TRUE;
+ }
+ }
+
+ return (any_failure && !any_success) ? FALSE : TRUE;
+}
+
+static void
+g_tls_database_gnutls_pkcs11_initable_iface_init (GInitableIface *iface)
+{
+ iface->init = g_tls_database_gnutls_pkcs11_initable_init;
+}
+
+GTlsDatabase *
+g_tls_database_gnutls_pkcs11_new (GError **error)
+{
+ g_return_val_if_fail (!error || !*error, NULL);
+ return g_initable_new (G_TYPE_TLS_DATABASE_GNUTLS_PKCS11, NULL, error, NULL);
+}
diff --git a/tls/gnutls/gtlsdatabase-gnutls-pkcs11.h b/tls/gnutls/gtlsdatabase-gnutls-pkcs11.h
new file mode 100644
index 0000000..cd7a52d
--- /dev/null
+++ b/tls/gnutls/gtlsdatabase-gnutls-pkcs11.h
@@ -0,0 +1,44 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO - GLib Certificate, Output and Gnutlsing Library
+ *
+ * Copyright 2011 Collabora, Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#ifndef __G_TLS_DATABASE_GNUTLS_PKCS11_H__
+#define __G_TLS_DATABASE_GNUTLS_PKCS11_H__
+
+#include <gio/gio.h>
+
+#include "gtlsdatabase-gnutls.h"
+
+G_BEGIN_DECLS
+
+#define G_TYPE_TLS_DATABASE_GNUTLS_PKCS11 (g_tls_database_gnutls_pkcs11_get_type ())
+
+G_DECLARE_FINAL_TYPE (GTlsDatabaseGnutlsPkcs11, g_tls_database_gnutls_pkcs11, G, TLS_DATABASE_GNUTLS_PKCS11,
GTlsDatabase)
+
+GTlsDatabase* g_tls_database_gnutls_pkcs11_new (GError **error);
+
+G_END_DECLS
+
+#endif /* __G_TLS_DATABASE_GNUTLS_PKCS11_H___ */
diff --git a/tls/gnutls/meson.build b/tls/gnutls/meson.build
index 4ff127e..4aff069 100644
--- a/tls/gnutls/meson.build
+++ b/tls/gnutls/meson.build
@@ -21,6 +21,16 @@ deps = [
gnutls_dep
]
+if pkcs11_dep.found()
+ sources += files(
+ 'gtlsdatabase-gnutls-pkcs11.c',
+ )
+
+ incs += tls_inc
+
+ deps += libgiopkcs11_dep
+endif
+
module = shared_module(
'giognutls',
sources: sources,
diff --git a/tls/pkcs11/gpkcs11array.c b/tls/pkcs11/gpkcs11array.c
new file mode 100644
index 0000000..c3627f4
--- /dev/null
+++ b/tls/pkcs11/gpkcs11array.c
@@ -0,0 +1,282 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO - Small GLib wrapper of PKCS#11 for use in GTls
+ *
+ * Copyright 2011 Collabora, Ltd
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#include "config.h"
+
+#include "gpkcs11array.h"
+
+#include <string.h>
+
+G_DEFINE_BOXED_TYPE (GPkcs11Array, g_pkcs11_array, g_pkcs11_array_ref, g_pkcs11_array_unref);
+
+typedef struct _GRealPkcs11Array
+{
+ CK_ATTRIBUTE *attrs;
+ CK_ULONG len;
+ volatile gint ref_count;
+} GRealPkcs11Array;
+
+GPkcs11Array*
+g_pkcs11_array_new (void)
+{
+ GRealPkcs11Array *array = g_slice_new (GRealPkcs11Array);
+
+ array->attrs = NULL;
+ array->len = 0;
+ array->ref_count = 1;
+
+ return (GPkcs11Array *)array;
+}
+
+void
+g_pkcs11_array_add (GPkcs11Array *array,
+ CK_ATTRIBUTE *attr)
+{
+ GRealPkcs11Array *rarray = (GRealPkcs11Array *)array;
+
+ g_return_if_fail (array);
+ g_return_if_fail (attr);
+ g_return_if_fail (attr->ulValueLen != (CK_ATTRIBUTE_TYPE)-1 || !attr->pValue);
+ g_return_if_fail (attr->pValue || !attr->ulValueLen);
+
+ rarray->attrs = g_renew (CK_ATTRIBUTE, rarray->attrs, rarray->len + 1);
+ memcpy (rarray->attrs + rarray->len, attr, sizeof (CK_ATTRIBUTE));
+ if (attr->pValue)
+ rarray->attrs[rarray->len].pValue = g_memdup (attr->pValue, attr->ulValueLen);
+ rarray->len++;
+}
+
+void
+g_pkcs11_array_add_value (GPkcs11Array *array,
+ CK_ATTRIBUTE_TYPE type,
+ gconstpointer value,
+ gssize length)
+{
+ CK_ATTRIBUTE attr;
+
+ g_return_if_fail (array);
+
+ if (length < 0)
+ length = strlen (value);
+
+ attr.type = type;
+ attr.pValue = (gpointer)value;
+ attr.ulValueLen = length;
+ g_pkcs11_array_add (array, &attr);
+}
+
+void
+g_pkcs11_array_add_boolean (GPkcs11Array *array,
+ CK_ATTRIBUTE_TYPE attr_type,
+ gboolean value)
+{
+ CK_ATTRIBUTE attr;
+ CK_BBOOL bval;
+
+ g_return_if_fail (array);
+
+ bval = value ? CK_TRUE : CK_FALSE;
+ attr.type = attr_type;
+ attr.pValue = &bval;
+ attr.ulValueLen = sizeof (bval);
+ g_pkcs11_array_add (array, &attr);
+}
+
+void
+g_pkcs11_array_add_ulong (GPkcs11Array *array,
+ CK_ATTRIBUTE_TYPE type,
+ gulong value)
+{
+ CK_ATTRIBUTE attr;
+ CK_ULONG uval;
+
+ g_return_if_fail (array);
+
+ uval = value;
+ attr.type = type;
+ attr.pValue = &uval;
+ attr.ulValueLen = sizeof (uval);
+ g_pkcs11_array_add (array, &attr);
+}
+
+void
+g_pkcs11_array_set (GPkcs11Array *array,
+ CK_ATTRIBUTE *attr)
+{
+ CK_ATTRIBUTE *previous;
+
+ g_return_if_fail (array);
+ g_return_if_fail (attr);
+ g_return_if_fail (attr->ulValueLen != (CK_ATTRIBUTE_TYPE)-1 || !attr->pValue);
+ g_return_if_fail (attr->pValue || !attr->ulValueLen);
+
+ previous = (CK_ATTRIBUTE *)g_pkcs11_array_find (array, attr->type);
+ if (previous == NULL)
+ {
+ g_pkcs11_array_add (array, attr);
+ }
+ else
+ {
+ g_free (previous->pValue);
+ previous->pValue = g_memdup (attr->pValue, attr->ulValueLen);
+ previous->ulValueLen = attr->ulValueLen;
+ }
+}
+
+void
+g_pkcs11_array_set_value (GPkcs11Array *array,
+ CK_ATTRIBUTE_TYPE type,
+ gconstpointer value,
+ gssize length)
+{
+ CK_ATTRIBUTE attr;
+
+ g_return_if_fail (array);
+
+ if (length < 0)
+ length = strlen (value);
+
+ attr.type = type;
+ attr.pValue = (gpointer)value;
+ attr.ulValueLen = length;
+ g_pkcs11_array_set (array, &attr);
+}
+
+void
+g_pkcs11_array_set_boolean (GPkcs11Array *array,
+ CK_ATTRIBUTE_TYPE attr_type,
+ gboolean value)
+{
+ CK_ATTRIBUTE attr;
+ CK_BBOOL bval;
+
+ g_return_if_fail (array);
+
+ bval = value ? CK_TRUE : CK_FALSE;
+ attr.type = attr_type;
+ attr.pValue = &bval;
+ attr.ulValueLen = sizeof (bval);
+ g_pkcs11_array_set (array, &attr);
+}
+
+void
+g_pkcs11_array_set_ulong (GPkcs11Array *array,
+ CK_ATTRIBUTE_TYPE type,
+ gulong value)
+{
+ CK_ATTRIBUTE attr;
+ CK_ULONG uval;
+
+ g_return_if_fail (array);
+
+ uval = value;
+ attr.type = type;
+ attr.pValue = &uval;
+ attr.ulValueLen = sizeof (uval);
+ g_pkcs11_array_set (array, &attr);
+}
+
+
+const CK_ATTRIBUTE*
+g_pkcs11_array_find (GPkcs11Array *array,
+ CK_ATTRIBUTE_TYPE type)
+{
+ const CK_ATTRIBUTE* attr;
+ guint i;
+
+ g_return_val_if_fail (array, NULL);
+
+ for (i = 0; i < array->count; ++i)
+ {
+ attr = &g_pkcs11_array_index (array, i);
+ if (attr->type == type)
+ return attr;
+ }
+
+ return NULL;
+}
+
+gboolean
+g_pkcs11_array_find_boolean (GPkcs11Array *array,
+ CK_ATTRIBUTE_TYPE type,
+ gboolean *value)
+{
+ const CK_ATTRIBUTE* attr;
+
+ g_return_val_if_fail (array, FALSE);
+ g_return_val_if_fail (value, FALSE);
+
+ attr = g_pkcs11_array_find (array, type);
+ if (!attr || !attr->pValue || attr->ulValueLen != sizeof (CK_BBOOL))
+ return FALSE;
+ *value = *((CK_BBOOL *)attr->pValue) ? TRUE : FALSE;
+ return TRUE;
+}
+
+gboolean
+g_pkcs11_array_find_ulong (GPkcs11Array *array,
+ CK_ATTRIBUTE_TYPE type,
+ gulong *value)
+{
+ const CK_ATTRIBUTE* attr;
+
+ g_return_val_if_fail (array, FALSE);
+ g_return_val_if_fail (value, FALSE);
+
+ attr = g_pkcs11_array_find (array, type);
+ if (!attr || !attr->pValue || attr->ulValueLen != sizeof (CK_ULONG))
+ return FALSE;
+ *value = *((CK_ULONG *)attr->pValue);
+ return TRUE;
+}
+
+GPkcs11Array*
+g_pkcs11_array_ref (GPkcs11Array *array)
+{
+ GRealPkcs11Array *rarray = (GRealPkcs11Array *)array;
+
+ g_return_val_if_fail (array, NULL);
+ g_return_val_if_fail (g_atomic_int_get (&rarray->ref_count) > 0, array);
+ g_atomic_int_inc (&rarray->ref_count);
+ return array;
+}
+
+void
+g_pkcs11_array_unref (GPkcs11Array *array)
+{
+ GRealPkcs11Array *rarray = (GRealPkcs11Array *)array;
+ CK_ULONG i;
+
+ g_return_if_fail (array);
+ g_return_if_fail (g_atomic_int_get (&rarray->ref_count) > 0);
+ if (g_atomic_int_dec_and_test (&rarray->ref_count))
+ {
+ for (i = 0; i < rarray->len; ++i)
+ g_free (rarray->attrs[i].pValue);
+ g_free (rarray->attrs);
+ g_slice_free1 (sizeof (GRealPkcs11Array), array);
+ }
+}
diff --git a/tls/pkcs11/gpkcs11array.h b/tls/pkcs11/gpkcs11array.h
new file mode 100644
index 0000000..04d1a66
--- /dev/null
+++ b/tls/pkcs11/gpkcs11array.h
@@ -0,0 +1,107 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO - Small GLib wrapper of PKCS#11 for use in GTls
+ *
+ * Copyright 2011 Collabora, Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#ifndef __G_PKCS11_ARRAY_H__
+#define __G_PKCS11_ARRAY_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <p11-kit/pkcs11.h>
+
+#include <p11-kit/uri.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GPkcs11Array GPkcs11Array;
+
+struct _GPkcs11Array
+{
+ CK_ATTRIBUTE *attrs;
+ CK_ULONG count;
+};
+
+#define G_TYPE_PKCS11_ARRAY (g_pkcs11_array_get_type ())
+
+GType g_pkcs11_array_get_type (void) G_GNUC_CONST;
+
+GPkcs11Array* g_pkcs11_array_new (void);
+
+#define g_pkcs11_array_index(array,index_) ((array)->attrs)[index_]
+
+void g_pkcs11_array_add (GPkcs11Array *array,
+ CK_ATTRIBUTE *attr);
+
+void g_pkcs11_array_add_value (GPkcs11Array *array,
+ CK_ATTRIBUTE_TYPE type,
+ gconstpointer value,
+ gssize length);
+
+void g_pkcs11_array_add_boolean (GPkcs11Array *array,
+ CK_ATTRIBUTE_TYPE type,
+ gboolean value);
+
+void g_pkcs11_array_add_ulong (GPkcs11Array *array,
+ CK_ATTRIBUTE_TYPE type,
+ gulong value);
+
+void g_pkcs11_array_set (GPkcs11Array *array,
+ CK_ATTRIBUTE *attr);
+
+void g_pkcs11_array_set_value (GPkcs11Array *array,
+ CK_ATTRIBUTE_TYPE type,
+ gconstpointer value,
+ gssize length);
+
+void g_pkcs11_array_set_boolean (GPkcs11Array *array,
+ CK_ATTRIBUTE_TYPE type,
+ gboolean value);
+
+void g_pkcs11_array_set_ulong (GPkcs11Array *array,
+ CK_ATTRIBUTE_TYPE type,
+ gulong value);
+
+const CK_ATTRIBUTE* g_pkcs11_array_find (GPkcs11Array *array,
+ CK_ATTRIBUTE_TYPE type);
+
+const CK_ATTRIBUTE* g_pkcs11_array_find_valid (GPkcs11Array *array,
+ CK_ATTRIBUTE_TYPE type);
+
+gboolean g_pkcs11_array_find_boolean (GPkcs11Array *array,
+ CK_ATTRIBUTE_TYPE type,
+ gboolean *value);
+
+gboolean g_pkcs11_array_find_ulong (GPkcs11Array *array,
+ CK_ATTRIBUTE_TYPE type,
+ gulong *value);
+
+GPkcs11Array* g_pkcs11_array_ref (GPkcs11Array *array);
+
+void g_pkcs11_array_unref (GPkcs11Array *array);
+
+G_END_DECLS
+
+#endif /* __G_PKCS11_ARRAY_H___ */
diff --git a/tls/pkcs11/gpkcs11pin.c b/tls/pkcs11/gpkcs11pin.c
new file mode 100644
index 0000000..6578bf4
--- /dev/null
+++ b/tls/pkcs11/gpkcs11pin.c
@@ -0,0 +1,159 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright © 2011 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#include "config.h"
+
+#include <string.h>
+
+#include "gpkcs11pin.h"
+#include <glib/gi18n-lib.h>
+
+enum
+{
+ PROP_0,
+
+ PROP_FLAGS,
+ PROP_DESCRIPTION
+};
+
+struct _GPkcs11Pin
+{
+ GTlsPassword parent_instance;
+
+ P11KitPin *pin;
+};
+
+G_DEFINE_TYPE (GPkcs11Pin, g_pkcs11_pin, G_TYPE_TLS_PASSWORD);
+
+static void
+g_pkcs11_pin_init (GPkcs11Pin *self)
+{
+}
+
+static void
+g_pkcs11_pin_finalize (GObject *object)
+{
+ GPkcs11Pin *self = G_PKCS11_PIN (object);
+
+ if (self->pin)
+ p11_kit_pin_unref (self->pin);
+
+ G_OBJECT_CLASS (g_pkcs11_pin_parent_class)->finalize (object);
+}
+
+static const guchar *
+g_pkcs11_pin_get_value (GTlsPassword *password,
+ gsize *length)
+{
+ GPkcs11Pin *self = G_PKCS11_PIN (password);
+
+ if (!self->pin)
+ {
+ if (length)
+ *length = 0;
+ return NULL;
+ }
+
+ return p11_kit_pin_get_value (self->pin, length);
+}
+
+static void
+g_pkcs11_pin_set_value (GTlsPassword *password,
+ guchar *value,
+ gssize length,
+ GDestroyNotify destroy)
+{
+ GPkcs11Pin *self = G_PKCS11_PIN (password);
+
+ if (self->pin)
+ {
+ p11_kit_pin_unref (self->pin);
+ self->pin = NULL;
+ }
+
+ if (length < 0)
+ length = strlen ((gchar *)value);
+
+ self->pin = p11_kit_pin_new_for_buffer (value, length, destroy);
+}
+
+static const gchar *
+g_pkcs11_pin_get_default_warning (GTlsPassword *password)
+{
+ GTlsPasswordFlags flags;
+
+ flags = g_tls_password_get_flags (password);
+
+ if (flags & G_TLS_PASSWORD_FINAL_TRY)
+ return _("This is the last chance to enter the PIN correctly before the token is locked.");
+ if (flags & G_TLS_PASSWORD_MANY_TRIES)
+ return _("Several PIN attempts have been incorrect, and the token will be locked after further
failures.");
+ if (flags & G_TLS_PASSWORD_RETRY)
+ return _("The PIN entered is incorrect.");
+
+ return NULL;
+}
+
+
+static void
+g_pkcs11_pin_class_init (GPkcs11PinClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+ GTlsPasswordClass *password_class = G_TLS_PASSWORD_CLASS (klass);
+
+ password_class->get_value = g_pkcs11_pin_get_value;
+ password_class->set_value = g_pkcs11_pin_set_value;
+ password_class->get_default_warning = g_pkcs11_pin_get_default_warning;
+
+ gobject_class->finalize = g_pkcs11_pin_finalize;
+}
+
+GTlsPassword *
+g_pkcs11_pin_new (GTlsPasswordFlags flags,
+ const gchar *description)
+{
+ GPkcs11Pin *self;
+
+ self = g_object_new (G_TYPE_PKCS11_PIN,
+ "flags", flags,
+ "description", description,
+ NULL);
+
+ return G_TLS_PASSWORD (self);
+}
+
+
+P11KitPin *
+g_pkcs11_pin_steal_internal (GPkcs11Pin *self)
+{
+ P11KitPin *pin;
+
+ g_return_val_if_fail (G_IS_PKCS11_PIN (self), NULL);
+
+ pin = self->pin;
+ self->pin = NULL;
+ return pin;
+}
diff --git a/tls/pkcs11/gpkcs11pin.h b/tls/pkcs11/gpkcs11pin.h
new file mode 100644
index 0000000..5fbb662
--- /dev/null
+++ b/tls/pkcs11/gpkcs11pin.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO - GLib Pin, Output and Pkcs11ing Library
+ *
+ * Copyright © 2011 Collabora Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#ifndef __G_PKCS11_PIN_H__
+#define __G_PKCS11_PIN_H__
+
+#include <gio/gio.h>
+#include <p11-kit/pin.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_PKCS11_PIN (g_pkcs11_pin_get_type ())
+
+G_DECLARE_FINAL_TYPE (GPkcs11Pin, g_pkcs11_pin, G, PKCS11_PIN, GTlsPassword)
+
+GTlsPassword * g_pkcs11_pin_new (GTlsPasswordFlags flags,
+ const gchar *description);
+
+P11KitPin * g_pkcs11_pin_steal_internal (GPkcs11Pin *self);
+
+G_END_DECLS
+
+#endif /* __G_PKCS11_PIN_H___ */
diff --git a/tls/pkcs11/gpkcs11slot.c b/tls/pkcs11/gpkcs11slot.c
new file mode 100644
index 0000000..9b24dc0
--- /dev/null
+++ b/tls/pkcs11/gpkcs11slot.c
@@ -0,0 +1,618 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO - Small GLib wrapper of PKCS#11 for use in GTls
+ *
+ * Copyright 2011 Collabora, Ltd
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#include "config.h"
+
+#include "gpkcs11slot.h"
+
+#include "gpkcs11array.h"
+#include "gpkcs11pin.h"
+#include "gpkcs11util.h"
+
+#include <glib/gi18n.h>
+
+#include <p11-kit/p11-kit.h>
+#include <p11-kit/pin.h>
+
+#include <stdlib.h>
+
+enum {
+ PROP_0,
+ PROP_MODULE,
+ PROP_SLOT_ID
+};
+
+struct _GPkcs11Slot
+{
+ GObject parent_instance;
+
+ /* read-only after construct */
+ CK_FUNCTION_LIST_PTR module;
+ CK_SLOT_ID slot_id;
+
+ /* protected by mutex */
+ GMutex mutex;
+ CK_SESSION_HANDLE last_session;
+};
+
+G_DEFINE_TYPE (GPkcs11Slot, g_pkcs11_slot, G_TYPE_OBJECT);
+
+static gboolean
+check_if_session_logged_in (GPkcs11Slot *self,
+ CK_SESSION_HANDLE session)
+{
+ CK_SESSION_INFO session_info;
+ CK_RV rv;
+
+ rv = (self->module->C_GetSessionInfo) (session, &session_info);
+ if (rv != CKR_OK)
+ return FALSE;
+
+ /* Already logged in */
+ if (session_info.state == CKS_RO_USER_FUNCTIONS ||
+ session_info.state == CKS_RW_USER_FUNCTIONS)
+ return TRUE;
+
+ return FALSE;
+}
+
+static gboolean
+session_login_protected_auth_path (GPkcs11Slot *self,
+ CK_SESSION_HANDLE session,
+ GError **error)
+{
+ CK_RV rv;
+
+ rv = (self->module->C_Login) (session, CKU_USER, NULL, 0);
+ if (rv == CKR_USER_ALREADY_LOGGED_IN)
+ rv = CKR_OK;
+ if (g_pkcs11_propagate_error (error, rv))
+ return FALSE;
+ return TRUE;
+}
+
+static gboolean
+session_login_with_pin (GPkcs11Slot *self,
+ GTlsInteraction *interaction,
+ CK_SESSION_HANDLE session,
+ CK_TOKEN_INFO *token_info,
+ GTlsPasswordFlags flags,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GTlsInteractionResult result = G_TLS_INTERACTION_UNHANDLED;
+ GTlsPassword *password = NULL;
+ const guchar *value;
+ gsize length;
+ CK_RV rv;
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return FALSE;
+
+ else if (interaction != NULL)
+ {
+ gchar *description = p11_kit_space_strdup (token_info->label,
+ sizeof (token_info->label));
+ password = g_tls_password_new (flags, description);
+ free (description);
+
+ result = g_tls_interaction_ask_password (interaction, password, cancellable, error);
+ }
+
+ switch (result)
+ {
+ case G_TLS_INTERACTION_UNHANDLED:
+ g_clear_object (&password);
+ g_message ("no pin is available to log in, or the user cancelled pin entry");
+ return TRUE;
+ case G_TLS_INTERACTION_FAILED:
+ g_clear_object (&password);
+ return FALSE;
+ case G_TLS_INTERACTION_HANDLED:
+ break;
+ }
+
+ g_assert (interaction != NULL && password != NULL);
+ value = g_tls_password_get_value (password, &length);
+ rv = (self->module->C_Login) (session, CKU_USER, (CK_UTF8CHAR_PTR)value, length);
+ g_object_unref (password);
+
+ if (rv == CKR_USER_ALREADY_LOGGED_IN)
+ rv = CKR_OK;
+ if (g_pkcs11_propagate_error (error, rv))
+ return FALSE;
+ return TRUE;
+}
+
+static gboolean
+session_login_if_necessary (GPkcs11Slot *self,
+ GTlsInteraction *interaction,
+ CK_SESSION_HANDLE session,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CK_TOKEN_INFO token_info;
+ GTlsPasswordFlags flags = 0;
+ GError *err = NULL;
+ CK_RV rv;
+
+ for (;;)
+ {
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return FALSE;
+
+ /* Do we actually need to login? */
+ if (check_if_session_logged_in (self, session))
+ return TRUE;
+
+ /* Get the token information, this can change between login attempts */
+ rv = (self->module->C_GetTokenInfo) (self->slot_id, &token_info);
+ if (g_pkcs11_propagate_error (error, rv))
+ return FALSE;
+
+ if (!(token_info.flags & CKF_LOGIN_REQUIRED))
+ return TRUE;
+
+ /* Login is not initialized on token, don't try to login */
+ if (!(token_info.flags & CKF_USER_PIN_INITIALIZED))
+ return TRUE;
+
+ /* Protected auth path, only call login once, and let token prompt user */
+ if (token_info.flags & CKF_PROTECTED_AUTHENTICATION_PATH)
+ return session_login_protected_auth_path (self, session, error);
+
+ /* Normal authentication path, ask p11-kit to call any callbacks */
+ else
+ {
+
+ if (token_info.flags & CKF_SO_PIN_COUNT_LOW)
+ flags |= G_TLS_PASSWORD_MANY_TRIES;
+ if (token_info.flags & CKF_SO_PIN_FINAL_TRY)
+ flags |= G_TLS_PASSWORD_FINAL_TRY;
+
+ if (session_login_with_pin (self, interaction, session, &token_info,
+ flags, cancellable, &err))
+ return TRUE;
+
+ /* User cancelled, don't try to log in */
+ if (err == NULL)
+ return TRUE;
+
+ if (!g_error_matches (err, G_PKCS11_ERROR, CKR_PIN_INCORRECT))
+ {
+ g_propagate_error (error, err);
+ return FALSE;
+ }
+
+ /* Try again */
+ g_clear_error (&err);
+ flags |= G_TLS_PASSWORD_RETRY;
+ }
+ }
+}
+
+static CK_SESSION_HANDLE
+session_checkout_or_open (GPkcs11Slot *self,
+ GTlsInteraction *interaction,
+ gboolean login,
+ GCancellable *cancellable,
+ GError **error)
+{
+ CK_SESSION_HANDLE session = 0;
+ CK_RV rv;
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ return 0;
+
+ g_mutex_lock (&self->mutex);
+
+ if (self->last_session)
+ {
+ session = self->last_session;
+ self->last_session = 0;
+ }
+
+ g_mutex_unlock (&self->mutex);
+
+ if (!session)
+ {
+ rv = (self->module->C_OpenSession) (self->slot_id, CKF_SERIAL_SESSION,
+ NULL, NULL, &session);
+ if (g_pkcs11_propagate_error (error, rv))
+ return 0;
+ }
+
+ if (login)
+ {
+ if (!session_login_if_necessary (self, interaction, session, cancellable, error))
+ {
+ (self->module->C_CloseSession) (session);
+ return 0;
+ }
+ }
+
+ return session;
+}
+
+static void
+session_close (GPkcs11Slot *self,
+ CK_SESSION_HANDLE session)
+{
+ CK_RV rv;
+
+ g_assert (session != 0);
+
+ rv = (self->module->C_CloseSession) (session);
+ if (rv != CKR_OK)
+ g_warning ("couldn't close pkcs11 session: %s",
+ p11_kit_strerror (rv));
+}
+
+static void
+session_checkin_or_close (GPkcs11Slot *self,
+ CK_SESSION_HANDLE session)
+{
+ g_assert (session != 0);
+
+ g_mutex_lock (&self->mutex);
+
+ if (self->last_session == 0)
+ {
+ self->last_session = session;
+ session = 0;
+ }
+
+ g_mutex_unlock (&self->mutex);
+
+ if (session != 0)
+ session_close (self, session);
+}
+
+static GPkcs11Array*
+retrieve_object_attributes (GPkcs11Slot *self,
+ CK_SESSION_HANDLE session,
+ CK_OBJECT_HANDLE object,
+ const CK_ATTRIBUTE_TYPE *attr_types,
+ guint attr_types_length,
+ GError **error)
+{
+ GPkcs11Array *result;
+ CK_ATTRIBUTE_PTR attr;
+ CK_ATTRIBUTE blank;
+ CK_RV rv;
+ guint i;
+
+ result = g_pkcs11_array_new ();
+ memset (&blank, 0, sizeof (blank));
+ for (i = 0; i < attr_types_length; ++i)
+ {
+ blank.type = attr_types[i];
+ g_pkcs11_array_add (result, &blank);
+ }
+
+ /* Get all the required buffer sizes */
+ rv = (self->module->C_GetAttributeValue) (session, object,
+ result->attrs, result->count);
+ if (rv == CKR_ATTRIBUTE_SENSITIVE ||
+ rv == CKR_ATTRIBUTE_TYPE_INVALID)
+ rv = CKR_OK;
+ if (g_pkcs11_propagate_error (error, rv))
+ {
+ g_pkcs11_array_unref (result);
+ return NULL;
+ }
+
+ /* Now allocate memory for them all */
+ for (i = 0; i < attr_types_length; ++i)
+ {
+ attr = &g_pkcs11_array_index (result, i);
+ if (attr->ulValueLen != (CK_ULONG)-1 && attr->ulValueLen)
+ attr->pValue = g_malloc0 (attr->ulValueLen);
+ }
+
+ /* And finally get all the values */
+ rv = (self->module->C_GetAttributeValue) (session, object,
+ result->attrs, result->count);
+ if (rv == CKR_ATTRIBUTE_SENSITIVE ||
+ rv == CKR_ATTRIBUTE_TYPE_INVALID ||
+ rv == CKR_BUFFER_TOO_SMALL)
+ rv = CKR_OK;
+ if (g_pkcs11_propagate_error (error, rv))
+ {
+ g_pkcs11_array_unref (result);
+ return NULL;
+ }
+
+ return result;
+}
+
+static void
+g_pkcs11_slot_init (GPkcs11Slot *self)
+{
+ g_mutex_init (&self->mutex);
+}
+
+static void
+g_pkcs11_slot_dispose (GObject *object)
+{
+ GPkcs11Slot *self = G_PKCS11_SLOT (object);
+ CK_SESSION_HANDLE session = 0;
+
+ g_mutex_lock (&self->mutex);
+
+ session = self->last_session;
+ self->last_session = 0;
+
+ g_mutex_unlock (&self->mutex);
+
+ if (session)
+ session_close (self, session);
+
+ G_OBJECT_CLASS (g_pkcs11_slot_parent_class)->dispose (object);
+}
+
+static void
+g_pkcs11_slot_finalize (GObject *object)
+{
+ GPkcs11Slot *self = G_PKCS11_SLOT (object);
+
+ g_assert (self->last_session == 0);
+ g_mutex_clear (&self->mutex);
+
+ G_OBJECT_CLASS (g_pkcs11_slot_parent_class)->finalize (object);
+}
+
+static void
+g_pkcs11_slot_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GPkcs11Slot *self = G_PKCS11_SLOT (object);
+
+ switch (prop_id)
+ {
+ case PROP_MODULE:
+ g_value_set_pointer (value, self->module);
+ break;
+
+ case PROP_SLOT_ID:
+ g_value_set_ulong (value, self->slot_id);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+g_pkcs11_slot_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GPkcs11Slot *self = G_PKCS11_SLOT (object);
+
+ switch (prop_id)
+ {
+ case PROP_MODULE:
+ self->module = g_value_get_pointer (value);
+ g_assert (self->module);
+ break;
+
+ case PROP_SLOT_ID:
+ self->slot_id = g_value_get_ulong (value);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ }
+}
+
+static void
+g_pkcs11_slot_class_init (GPkcs11SlotClass *klass)
+{
+ GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
+
+ gobject_class->get_property = g_pkcs11_slot_get_property;
+ gobject_class->set_property = g_pkcs11_slot_set_property;
+ gobject_class->dispose = g_pkcs11_slot_dispose;
+ gobject_class->finalize = g_pkcs11_slot_finalize;
+
+ g_object_class_install_property (gobject_class, PROP_MODULE,
+ g_param_spec_pointer ("module",
+ N_("Module"),
+ N_("PKCS#11 Module Pointer"),
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class, PROP_SLOT_ID,
+ g_param_spec_ulong ("slot-id",
+ N_("Slot ID"),
+ N_("PKCS#11 Slot Identifier"),
+ 0,
+ G_MAXULONG,
+ G_MAXULONG,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT |
+ G_PARAM_STATIC_STRINGS));
+}
+
+GPkcs11EnumerateState
+g_pkcs11_slot_enumerate (GPkcs11Slot *self,
+ GTlsInteraction *interaction,
+ CK_ATTRIBUTE_PTR match,
+ CK_ULONG match_count,
+ gboolean match_private,
+ const CK_ATTRIBUTE_TYPE *attr_types,
+ guint attr_types_length,
+ GPkcs11Accumulator accumulator,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GPkcs11EnumerateState state = G_PKCS11_ENUMERATE_CONTINUE;
+ CK_OBJECT_HANDLE objects[256];
+ CK_SESSION_HANDLE session;
+ GPkcs11Array *attrs;
+ GError *err = NULL;
+ CK_ULONG count, i;
+ CK_RV rv;
+
+ g_return_val_if_fail (G_IS_PKCS11_SLOT (self), FALSE);
+ g_return_val_if_fail (accumulator, FALSE);
+ g_return_val_if_fail (!error || !*error, FALSE);
+
+ session = session_checkout_or_open (self, interaction, match_private,
+ cancellable, &err);
+ if (err != NULL)
+ {
+ /* If the slot isn't present, then nothing to match :) */
+ if (g_error_matches (err, G_PKCS11_ERROR, CKR_TOKEN_NOT_PRESENT))
+ {
+ g_clear_error (&err);
+ return G_PKCS11_ENUMERATE_CONTINUE;
+ }
+
+ g_propagate_error (error, err);
+ return G_PKCS11_ENUMERATE_FAILED;
+ }
+
+ rv = (self->module->C_FindObjectsInit) (session, match, match_count);
+
+ while (state == G_PKCS11_ENUMERATE_CONTINUE && rv == CKR_OK &&
+ !g_cancellable_is_cancelled (cancellable))
+ {
+ count = 0;
+ rv = (self->module->C_FindObjects) (session, objects,
+ G_N_ELEMENTS (objects), &count);
+ if (rv == CKR_OK)
+ {
+ if (count == 0)
+ break;
+
+ for (i = 0; state == G_PKCS11_ENUMERATE_CONTINUE && i < count; ++i)
+ {
+ if (attr_types_length)
+ {
+ attrs = retrieve_object_attributes (self, session, objects[i],
+ attr_types, attr_types_length, error);
+ if (attrs == NULL)
+ state = G_PKCS11_ENUMERATE_FAILED;
+ }
+ else
+ {
+ attrs = NULL;
+ }
+
+ if (state == G_PKCS11_ENUMERATE_CONTINUE)
+ {
+ if (!(accumulator) (attrs, user_data))
+ state = G_PKCS11_ENUMERATE_STOP;
+ }
+
+ if (attrs)
+ g_pkcs11_array_unref (attrs);
+
+ if (g_cancellable_is_cancelled (cancellable))
+ break;
+ }
+ }
+ }
+
+ if (g_cancellable_set_error_if_cancelled (cancellable, error))
+ {
+ state = G_PKCS11_ENUMERATE_FAILED;
+ }
+ else if (rv != CKR_OK && rv != CKR_TOKEN_NOT_PRESENT)
+ {
+ g_pkcs11_propagate_error (error, rv);
+ state = G_PKCS11_ENUMERATE_FAILED;
+ }
+
+ rv = (self->module->C_FindObjectsFinal) (session);
+ if (rv == CKR_OK)
+ session_checkin_or_close (self, session);
+ else
+ session_close (self, session);
+
+ return state;
+}
+
+gboolean
+g_pkcs11_slot_get_token_info (GPkcs11Slot *self,
+ CK_TOKEN_INFO_PTR token_info)
+{
+ CK_RV rv;
+
+ g_return_val_if_fail (G_IS_PKCS11_SLOT (self), FALSE);
+ g_return_val_if_fail (token_info, FALSE);
+
+ memset (token_info, 0, sizeof (CK_TOKEN_INFO));
+ rv = (self->module->C_GetTokenInfo) (self->slot_id, token_info);
+ if (rv == CKR_TOKEN_NOT_PRESENT)
+ return FALSE;
+
+ if (rv != CKR_OK)
+ {
+ g_warning ("call to C_GetTokenInfo on PKCS#11 module failed: %s",
+ p11_kit_strerror (rv));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+g_pkcs11_slot_matches_uri (GPkcs11Slot *self,
+ P11KitUri *uri)
+{
+ CK_INFO library;
+ CK_TOKEN_INFO token;
+ CK_RV rv;
+
+ g_return_val_if_fail (G_IS_PKCS11_SLOT (self), FALSE);
+ g_return_val_if_fail (uri, FALSE);
+
+ memset (&library, 0, sizeof (library));
+ rv = (self->module->C_GetInfo) (&library);
+ if (rv != CKR_OK)
+ {
+ g_warning ("call to C_GetInfo on PKCS#11 module failed: %s",
+ p11_kit_strerror (rv));
+ return FALSE;
+ }
+
+ if (!p11_kit_uri_match_module_info (uri, &library))
+ return FALSE;
+
+ memset (&token, 0, sizeof (token));
+ if (!g_pkcs11_slot_get_token_info (self, &token))
+ return FALSE;
+
+ return p11_kit_uri_match_token_info (uri, &token);
+}
diff --git a/tls/pkcs11/gpkcs11slot.h b/tls/pkcs11/gpkcs11slot.h
new file mode 100644
index 0000000..a57c2a6
--- /dev/null
+++ b/tls/pkcs11/gpkcs11slot.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO - Small GLib wrapper of PKCS#11 for use in GTls
+ *
+ * Copyright 2011 Collabora, Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#ifndef __G_PKCS11_SLOT_H__
+#define __G_PKCS11_SLOT_H__
+
+#include <gio/gio.h>
+
+#include "gpkcs11array.h"
+
+#include <p11-kit/pkcs11.h>
+#include <p11-kit/uri.h>
+
+G_BEGIN_DECLS
+
+typedef enum
+{
+ G_PKCS11_ENUMERATE_FAILED,
+ G_PKCS11_ENUMERATE_STOP,
+ G_PKCS11_ENUMERATE_CONTINUE
+} GPkcs11EnumerateState;
+
+#define G_TYPE_PKCS11_SLOT (g_pkcs11_slot_get_type ())
+
+G_DECLARE_FINAL_TYPE (GPkcs11Slot, g_pkcs11_slot, G, PKCS11_SLOT, GObject)
+
+typedef gboolean (*GPkcs11Accumulator) (gpointer result,
+ gpointer user_data);
+
+GPkcs11EnumerateState g_pkcs11_slot_enumerate (GPkcs11Slot *self,
+ GTlsInteraction *interaction,
+ CK_ATTRIBUTE_PTR match,
+ CK_ULONG match_count,
+ gboolean match_private,
+ const CK_ATTRIBUTE_TYPE *attr_types,
+ guint attr_types_length,
+ GPkcs11Accumulator accumulator,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **error);
+
+gboolean g_pkcs11_slot_get_token_info (GPkcs11Slot *self,
+ CK_TOKEN_INFO_PTR token_info);
+
+gboolean g_pkcs11_slot_matches_uri (GPkcs11Slot *self,
+ P11KitUri *uri);
+
+G_END_DECLS
+
+#endif /* __G_PKCS11_SLOT_H___ */
diff --git a/tls/pkcs11/gpkcs11util.c b/tls/pkcs11/gpkcs11util.c
new file mode 100644
index 0000000..6ffe18f
--- /dev/null
+++ b/tls/pkcs11/gpkcs11util.c
@@ -0,0 +1,63 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO - Small GLib wrapper of PKCS#11 for use in GTls
+ *
+ * Copyright 2011 Collabora, Ltd
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#include "config.h"
+
+#include "gpkcs11util.h"
+
+#include <glib/gi18n-lib.h>
+#include <gio/gio.h>
+
+#include <p11-kit/p11-kit.h>
+
+GQuark
+g_pkcs11_get_error_domain (void)
+{
+ static GQuark domain = 0;
+ static volatile gsize quark_inited = 0;
+
+ if (g_once_init_enter (&quark_inited))
+ {
+ domain = g_quark_from_static_string ("g-pkcs11-error");
+ g_once_init_leave (&quark_inited, 1);
+ }
+
+ return domain;
+}
+
+gboolean
+g_pkcs11_propagate_error (GError **error, CK_RV rv)
+{
+ if (rv == CKR_OK)
+ return FALSE;
+ if (rv == CKR_CANCEL)
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CANCELLED,
+ p11_kit_strerror (rv));
+ else
+ g_set_error_literal (error, G_PKCS11_ERROR, (gint)rv,
+ p11_kit_strerror (rv));
+ return TRUE;
+}
diff --git a/tls/pkcs11/gpkcs11util.h b/tls/pkcs11/gpkcs11util.h
new file mode 100644
index 0000000..9368a78
--- /dev/null
+++ b/tls/pkcs11/gpkcs11util.h
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO - Small GLib wrapper of PKCS#11 for use in GTls
+ *
+ * Copyright 2011 Collabora, Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#ifndef __G_PKCS11_UTIL_H__
+#define __G_PKCS11_UTIL_H__
+
+#include <glib.h>
+
+#include <p11-kit/pkcs11.h>
+
+G_BEGIN_DECLS
+
+#define G_PKCS11_VENDOR_CODE 0x47000000 /* G000 */
+
+enum {
+ G_PKCS11_ERROR_BAD_URI = (CKR_VENDOR_DEFINED | (G_PKCS11_VENDOR_CODE + 1)),
+};
+
+#define G_PKCS11_ERROR (g_pkcs11_get_error_domain ())
+
+GQuark g_pkcs11_get_error_domain (void) G_GNUC_CONST;
+
+gboolean g_pkcs11_propagate_error (GError **error,
+ CK_RV rv);
+
+G_END_DECLS
+
+#endif /* __G_PKCS11_UTIL_H___ */
diff --git a/tls/pkcs11/meson.build b/tls/pkcs11/meson.build
new file mode 100644
index 0000000..94f55d9
--- /dev/null
+++ b/tls/pkcs11/meson.build
@@ -0,0 +1,28 @@
+sources = files(
+ 'gpkcs11array.c',
+ 'gpkcs11pin.c',
+ 'gpkcs11slot.c',
+ 'gpkcs11util.c'
+)
+
+deps = [
+ glib_dep,
+ gio_dep,
+ gmodule_dep,
+ gobject_dep,
+ pkcs11_dep
+]
+
+libgiopkcs11 = static_library(
+ 'giopkcs11',
+ sources: sources,
+ include_directories: top_inc,
+ dependencies: deps,
+ install: get_option('static_modules')
+)
+
+libgiopkcs11_dep = declare_dependency(
+ link_with: libgiopkcs11,
+ include_directories: include_directories('.'),
+ dependencies: deps
+)
diff --git a/tls/pkcs11/pkcs11-trust-assertions.h b/tls/pkcs11/pkcs11-trust-assertions.h
new file mode 100644
index 0000000..cfc916b
--- /dev/null
+++ b/tls/pkcs11/pkcs11-trust-assertions.h
@@ -0,0 +1,59 @@
+/*
+ * pkcs11x.h
+ * Copyright 2010 Collabora, Ltd
+ *
+ * This file is free software; as a special exception the author gives
+ * unlimited permission to copy and/or distribute it, with or without
+ * modifications, as long as this notice is preserved.
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY, to the extent permitted by law; without even
+ * the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
+ * PURPOSE.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ */
+
+/*
+ * The latest version of this file is at:
+ *
+ * git://thewalter.net/git/pkcs11-trust-assertions
+ *
+ * or viewable on the web at:
+ *
+ * http://thewalter.net/git/cgit.cgi/pkcs11-trust-assertions/tree/pkcs11-trust-assertions.h
+ *
+ */
+
+#ifndef PKCS11_TRUST_ASSERTIONS_H
+#define PKCS11_TRUST_ASSERTIONS_H
+
+#include <p11-kit/pkcs11.h>
+
+#define CKA_XDG (CKA_VENDOR_DEFINED | 0x58444700UL /* XDG0 */ )
+#define CKO_XDG (CKA_VENDOR_DEFINED | 0x58444700UL /* XDG0 */ )
+
+/* -------------------------------------------------------------------
+ * TRUST ASSERTIONS
+ */
+
+#define CKO_X_TRUST_ASSERTION (CKO_XDG + 100)
+
+#define CKA_X_ASSERTION_TYPE (CKA_XDG + 1)
+
+#define CKA_X_CERTIFICATE_VALUE (CKA_XDG + 2)
+
+#define CKA_X_PURPOSE (CKA_XDG + 3)
+
+#define CKA_X_PEER (CKA_XDG + 4)
+
+typedef CK_ULONG CK_X_ASSERTION_TYPE;
+
+#define CKT_X_UNTRUSTED_CERTIFICATE 1UL
+
+#define CKT_X_PINNED_CERTIFICATE 2UL
+
+#define CKT_X_ANCHORED_CERTIFICATE 3UL
+
+#endif /* PKCS11_TRUST_ASSERTIONS_H */
diff --git a/tls/tests/meson.build b/tls/tests/meson.build
index fc73a4b..1aee911 100644
--- a/tls/tests/meson.build
+++ b/tls/tests/meson.build
@@ -29,6 +29,20 @@ test_programs = [
# ['dtls-connection', ['mock-interaction.c'], deps, ['openssl']],
]
+if pkcs11_dep.found()
+ pkcs11_deps = deps + [
+ libgiopkcs11_dep,
+ pkcs11_dep
+ ]
+
+ test_programs += [
+ ['pkcs11-util', [], pkcs11_deps, []],
+ ['pkcs11-array', [], pkcs11_deps, []],
+ ['pkcs11-pin', [], pkcs11_deps, []],
+ ['pkcs11-slot', ['mock-interaction.c', 'mock-pkcs11.c'], pkcs11_deps, []]
+ ]
+endif
+
foreach backend: backends
foreach program: test_programs
if not program[3].contains(backend)
diff --git a/tls/tests/mock-pkcs11.c b/tls/tests/mock-pkcs11.c
new file mode 100644
index 0000000..75a6874
--- /dev/null
+++ b/tls/tests/mock-pkcs11.c
@@ -0,0 +1,1547 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright (C) 2010 Stefan Walter
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * 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
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ */
+
+#include "config.h"
+
+#include "mock-pkcs11.h"
+
+#include <p11-kit/pkcs11.h>
+
+#include <glib.h>
+
+#include <string.h>
+
+/*
+ * This is *NOT* how you'd want to implement a PKCS#11 module. This
+ * fake module simply provides enough for gnutls-pkcs11 backend to test against.
+ * It doesn't pass any tests, or behave as expected from a PKCS#11 module.
+ */
+
+static gboolean initialized = FALSE;
+static gchar *the_pin = NULL;
+static gulong n_the_pin = 0;
+
+static gboolean logged_in = FALSE;
+static CK_USER_TYPE user_type = 0;
+static CK_FUNCTION_LIST functionList;
+
+typedef enum
+{
+ OP_FIND = 1,
+ OP_CRYPTO
+} Operation;
+
+typedef struct
+{
+ CK_SESSION_HANDLE handle;
+ CK_SESSION_INFO info;
+ GHashTable *objects;
+
+ Operation operation;
+
+ /* For find operations */
+ GList *matches;
+
+ /* For crypto operations */
+ CK_OBJECT_HANDLE crypto_key;
+ CK_ATTRIBUTE_TYPE crypto_method;
+ CK_MECHANISM_TYPE crypto_mechanism;
+ CK_BBOOL want_context_login;
+} Session;
+
+static guint unique_identifier = 100;
+static GHashTable *the_sessions = NULL;
+static GHashTable *the_objects = NULL;
+
+static void
+free_session (gpointer data)
+{
+ Session *sess = (Session *)data;
+ if (sess)
+ g_hash_table_destroy (sess->objects);
+ g_free (sess);
+}
+
+static GPkcs11Array *
+lookup_object (Session *session,
+ CK_OBJECT_HANDLE hObject)
+{
+ GPkcs11Array *attrs;
+ attrs = g_hash_table_lookup (the_objects, GUINT_TO_POINTER (hObject));
+ if (!attrs)
+ attrs = g_hash_table_lookup (session->objects, GUINT_TO_POINTER (hObject));
+ return attrs;
+}
+
+CK_OBJECT_HANDLE
+mock_module_take_object (GPkcs11Array *attrs)
+{
+ gboolean token;
+ guint handle;
+
+ g_return_val_if_fail (the_objects, 0);
+
+ if (g_pkcs11_array_find_boolean (attrs, CKA_TOKEN, &token))
+ g_return_val_if_fail (token == TRUE, 0);
+
+ handle = ++unique_identifier;
+ g_pkcs11_array_add_boolean (attrs, CKA_TOKEN, TRUE);
+ g_hash_table_insert (the_objects, GUINT_TO_POINTER (handle), attrs);
+ return handle;
+}
+
+void
+mock_module_enumerate_objects (CK_SESSION_HANDLE handle,
+ MockEnumerator func,
+ gpointer user_data)
+{
+ GHashTableIter iter;
+ gpointer key;
+ gpointer value;
+ Session *session;
+ gboolean private;
+
+ g_assert (the_objects);
+ g_assert (func);
+
+ /* Token objects */
+ g_hash_table_iter_init (&iter, the_objects);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ /* Don't include private objects when not logged in */
+ if (!logged_in)
+ {
+ if (g_pkcs11_array_find_boolean (value, CKA_PRIVATE, &private) && private == TRUE)
+ continue;
+ }
+
+ if (!(func) (GPOINTER_TO_UINT (key), value, user_data))
+ return;
+ }
+
+ /* session objects */
+ if (handle)
+ {
+ session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (handle));
+ if (session)
+ {
+ g_hash_table_iter_init (&iter, session->objects);
+ while (g_hash_table_iter_next (&iter, &key, &value))
+ {
+ /* Don't include private objects when not logged in */
+ if (!logged_in)
+ {
+ if (g_pkcs11_array_find_boolean (value, CKA_PRIVATE, &private) && private == TRUE)
+ continue;
+ }
+
+ if (!(func) (GPOINTER_TO_UINT (key), value, user_data))
+ return;
+ }
+ }
+ }
+}
+
+typedef struct {
+ CK_ATTRIBUTE_PTR attrs;
+ CK_ULONG n_attrs;
+ CK_OBJECT_HANDLE object;
+} FindObject;
+
+static gboolean
+enumerate_and_find_object (CK_OBJECT_HANDLE object,
+ GPkcs11Array *attrs,
+ gpointer user_data)
+{
+ FindObject *ctx = user_data;
+ const CK_ATTRIBUTE *match;
+ const CK_ATTRIBUTE *attr;
+ CK_ULONG i;
+
+ for (i = 0; i < ctx->n_attrs; ++i)
+ {
+ match = ctx->attrs + i;
+ attr = g_pkcs11_array_find (attrs, match->type);
+ if (!attr)
+ return TRUE; /* Continue */
+
+ if (attr->ulValueLen != match->ulValueLen ||
+ memcmp (attr->pValue, match->pValue, attr->ulValueLen) != 0)
+ return TRUE; /* Continue */
+ }
+
+ ctx->object = object;
+ return FALSE; /* Stop iteration */
+}
+
+CK_OBJECT_HANDLE
+mock_module_find_object (CK_SESSION_HANDLE session,
+ CK_ATTRIBUTE_PTR attrs,
+ CK_ULONG n_attrs)
+{
+ FindObject ctx;
+
+ ctx.attrs = attrs;
+ ctx.n_attrs = n_attrs;
+ ctx.object = 0;
+
+ mock_module_enumerate_objects (session, enumerate_and_find_object, &ctx);
+ return ctx.object;
+}
+
+static gboolean
+enumerate_and_count_objects (CK_OBJECT_HANDLE object,
+ GPkcs11Array *attrs,
+ gpointer user_data)
+{
+ guint *n_objects = user_data;
+ ++(*n_objects);
+ return TRUE; /* Continue */
+}
+
+guint
+mock_module_count_objects (CK_SESSION_HANDLE session)
+{
+ guint n_objects = 0;
+ mock_module_enumerate_objects (session, enumerate_and_count_objects, &n_objects);
+ return n_objects;
+}
+
+void
+mock_module_set_object (CK_OBJECT_HANDLE object,
+ CK_ATTRIBUTE_PTR attrs,
+ CK_ULONG n_attrs)
+{
+ CK_ULONG i;
+ GPkcs11Array *atts;
+
+ g_return_if_fail (object != 0);
+ g_return_if_fail (the_objects);
+
+ atts = g_hash_table_lookup (the_objects, GUINT_TO_POINTER (object));
+ g_return_if_fail (atts);
+
+ for (i = 0; i < n_attrs; ++i)
+ g_pkcs11_array_set (atts, &attrs[i]);
+}
+
+void
+mock_module_set_pin (const gchar *password)
+{
+ g_free (the_pin);
+ the_pin = g_strdup (password);
+ n_the_pin = strlen (password);
+}
+
+CK_RV
+mock_C_Initialize (CK_VOID_PTR pInitArgs)
+{
+ GPkcs11Array *attrs;
+ CK_C_INITIALIZE_ARGS_PTR args;
+
+ g_return_val_if_fail (initialized == FALSE, CKR_CRYPTOKI_ALREADY_INITIALIZED);
+
+ args = (CK_C_INITIALIZE_ARGS_PTR)pInitArgs;
+ if (args)
+ {
+ g_return_val_if_fail(
+ (args->CreateMutex == NULL && args->DestroyMutex == NULL &&
+ args->LockMutex == NULL && args->UnlockMutex == NULL) ||
+ (args->CreateMutex != NULL && args->DestroyMutex != NULL &&
+ args->LockMutex != NULL && args->UnlockMutex != NULL),
+ CKR_ARGUMENTS_BAD);
+
+ /* Flags should allow OS locking and os threads */
+ g_return_val_if_fail ((args->flags & CKF_OS_LOCKING_OK), CKR_CANT_LOCK);
+ g_return_val_if_fail ((args->flags & CKF_LIBRARY_CANT_CREATE_OS_THREADS) == 0,
CKR_NEED_TO_CREATE_THREADS);
+ }
+
+ the_pin = g_strdup (MOCK_SLOT_ONE_PIN);
+ n_the_pin = strlen (the_pin);
+ the_sessions = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, free_session);
+ the_objects = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
(GDestroyNotify)g_pkcs11_array_unref);
+
+ /* Our first token object */
+ attrs = g_pkcs11_array_new ();
+ g_pkcs11_array_add_ulong (attrs, CKA_CLASS, CKO_DATA);
+ g_pkcs11_array_add_value (attrs, CKA_LABEL, "TEST LABEL", -1);
+ g_pkcs11_array_add_boolean (attrs, CKA_TOKEN, TRUE);
+ g_hash_table_insert (the_objects, GUINT_TO_POINTER (2), attrs);
+
+ /* Our second token object */
+ attrs = g_pkcs11_array_new ();
+ g_pkcs11_array_add_ulong (attrs, CKA_CLASS, CKO_DATA);
+ g_pkcs11_array_add_value (attrs, CKA_LABEL, "LABEL TWO", -1);
+ g_pkcs11_array_add_boolean (attrs, CKA_TOKEN, TRUE);
+ g_hash_table_insert (the_objects, GUINT_TO_POINTER (3), attrs);
+
+ /* A private object */
+ attrs = g_pkcs11_array_new ();
+ g_pkcs11_array_add_ulong (attrs, CKA_CLASS, CKO_DATA);
+ g_pkcs11_array_add_value (attrs, CKA_LABEL, "PRIVATE", -1);
+ g_pkcs11_array_add_boolean (attrs, CKA_PRIVATE, TRUE);
+ g_pkcs11_array_add_boolean (attrs, CKA_TOKEN, TRUE);
+ g_hash_table_insert (the_objects, GUINT_TO_POINTER (4), attrs);
+
+ initialized = TRUE;
+ return CKR_OK;
+}
+
+CK_RV
+mock_validate_and_C_Initialize (CK_VOID_PTR pInitArgs)
+{
+ CK_C_INITIALIZE_ARGS_PTR args;
+ void *mutex;
+ CK_RV rv;
+
+ args = (CK_C_INITIALIZE_ARGS_PTR)pInitArgs;
+ if (args)
+ {
+ g_assert ((args->CreateMutex) (NULL) == CKR_ARGUMENTS_BAD && "CreateMutex succeeded wrong");
+ g_assert ((args->DestroyMutex) (NULL) == CKR_MUTEX_BAD && "DestroyMutex succeeded wrong");
+ g_assert ((args->LockMutex) (NULL) == CKR_MUTEX_BAD && "LockMutex succeeded wrong");
+ g_assert ((args->UnlockMutex) (NULL) == CKR_MUTEX_BAD && "UnlockMutex succeeded wrong");
+
+ /* Try to create an actual mutex */
+ rv = (args->CreateMutex) (&mutex);
+ g_assert (rv == CKR_OK && "CreateMutex g_assert_not_reacheded");
+ g_assert (mutex != NULL && "CreateMutex created null mutex");
+
+ /* Try and lock the mutex */
+ rv = (args->LockMutex) (mutex);
+ g_assert (rv == CKR_OK && "LockMutex g_assert_not_reacheded");
+
+ /* Try and unlock the mutex */
+ rv = (args->UnlockMutex) (mutex);
+ g_assert (rv == CKR_OK && "UnlockMutex g_assert_not_reacheded");
+
+ /* Try and destroy the mutex */
+ rv = (args->DestroyMutex) (mutex);
+ g_assert (rv == CKR_OK && "DestroyMutex g_assert_not_reacheded");
+ }
+
+ return mock_C_Initialize (pInitArgs);
+}
+
+CK_RV
+mock_C_Finalize (CK_VOID_PTR pReserved)
+{
+ g_return_val_if_fail (pReserved == NULL, CKR_ARGUMENTS_BAD);
+ g_return_val_if_fail (initialized == TRUE, CKR_CRYPTOKI_NOT_INITIALIZED);
+
+ initialized = FALSE;
+ logged_in = FALSE;
+ g_hash_table_destroy (the_objects);
+ the_objects = NULL;
+
+ g_hash_table_destroy (the_sessions);
+ the_sessions = NULL;
+
+ g_free (the_pin);
+ return CKR_OK;
+}
+
+static const CK_INFO TEST_INFO = {
+ { CRYPTOKI_VERSION_MAJOR, CRYPTOKI_VERSION_MINOR },
+ "TEST MANUFACTURER ",
+ 0,
+ "TEST LIBRARY ",
+ { 45, 145 }
+};
+
+CK_RV
+mock_C_GetInfo (CK_INFO_PTR pInfo)
+{
+ g_return_val_if_fail (pInfo, CKR_ARGUMENTS_BAD);
+ memcpy (pInfo, &TEST_INFO, sizeof (*pInfo));
+ return CKR_OK;
+}
+
+CK_RV
+mock_C_GetFunctionList (CK_FUNCTION_LIST_PTR_PTR list)
+{
+ g_return_val_if_fail (list, CKR_ARGUMENTS_BAD);
+ *list = &functionList;
+ return CKR_OK;
+}
+
+/*
+ * Two slots
+ * ONE: token present
+ * TWO: token not present
+ */
+
+CK_RV
+mock_C_GetSlotList (CK_BBOOL tokenPresent,
+ CK_SLOT_ID_PTR pSlotList, CK_ULONG_PTR pulCount)
+{
+ CK_ULONG count;
+
+ g_return_val_if_fail (pulCount, CKR_ARGUMENTS_BAD);
+
+ count = tokenPresent ? 1 : 2;
+
+ /* Application only wants to know the number of slots. */
+ if (pSlotList == NULL)
+ {
+ *pulCount = count;
+ return CKR_OK;
+ }
+
+ if (*pulCount < count)
+ g_return_val_if_reached (CKR_BUFFER_TOO_SMALL);
+
+ *pulCount = count;
+ pSlotList[0] = MOCK_SLOT_ONE_ID;
+ if (!tokenPresent)
+ pSlotList[1] = MOCK_SLOT_TWO_ID;
+
+ return CKR_OK;
+}
+
+/* Update mock-pkcs11.h URIs when updating this */
+
+static const CK_SLOT_INFO TEST_INFO_ONE = {
+ "TEST SLOT ",
+ "TEST MANUFACTURER ",
+ CKF_TOKEN_PRESENT | CKF_REMOVABLE_DEVICE,
+ { 55, 155 },
+ { 65, 165 },
+};
+
+/* Update mock-pkcs11.h URIs when updating this */
+
+static const CK_SLOT_INFO TEST_INFO_TWO = {
+ "TEST SLOT ",
+ "TEST MANUFACTURER ",
+ CKF_REMOVABLE_DEVICE,
+ { 55, 155 },
+ { 65, 165 },
+};
+
+CK_RV
+mock_C_GetSlotInfo (CK_SLOT_ID slotID,
+ CK_SLOT_INFO_PTR pInfo)
+{
+ g_return_val_if_fail (pInfo, CKR_ARGUMENTS_BAD);
+
+ if (slotID == MOCK_SLOT_ONE_ID)
+ {
+ memcpy (pInfo, &TEST_INFO_ONE, sizeof (*pInfo));
+ return CKR_OK;
+ }
+ else if (slotID == MOCK_SLOT_TWO_ID)
+ {
+ memcpy (pInfo, &TEST_INFO_TWO, sizeof (*pInfo));
+ return CKR_OK;
+ }
+ else
+ {
+ g_return_val_if_reached (CKR_SLOT_ID_INVALID);
+ }
+}
+
+/* Update mock-pkcs11.h URIs when updating this */
+
+static const CK_TOKEN_INFO TEST_TOKEN_ONE = {
+ "TEST LABEL ",
+ "TEST MANUFACTURER ",
+ "TEST MODEL ",
+ "TEST SERIAL ",
+ CKF_LOGIN_REQUIRED | CKF_USER_PIN_INITIALIZED | CKF_CLOCK_ON_TOKEN | CKF_TOKEN_INITIALIZED,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ { 75, 175 },
+ { 85, 185 },
+ { '1', '9', '9', '9', '0', '5', '2', '5', '0', '9', '1', '9', '5', '9', '0', '0' }
+};
+
+CK_RV
+mock_C_GetTokenInfo (CK_SLOT_ID slotID,
+ CK_TOKEN_INFO_PTR pInfo)
+{
+ g_return_val_if_fail (pInfo != NULL, CKR_ARGUMENTS_BAD);
+
+ if (slotID == MOCK_SLOT_ONE_ID)
+ {
+ memcpy (pInfo, &TEST_TOKEN_ONE, sizeof (*pInfo));
+ return CKR_OK;
+ }
+ else if (slotID == MOCK_SLOT_TWO_ID)
+ {
+ return CKR_TOKEN_NOT_PRESENT;
+ }
+ else
+ {
+ g_return_val_if_reached (CKR_SLOT_ID_INVALID);
+ }
+}
+
+CK_RV
+mock_fail_C_GetTokenInfo (CK_SLOT_ID slotID,
+ CK_TOKEN_INFO_PTR pInfo)
+{
+ return CKR_GENERAL_ERROR;
+}
+
+/*
+ * TWO mechanisms:
+ * CKM_MOCK_CAPITALIZE
+ * CKM_MOCK_PREFIX
+ */
+
+CK_RV
+mock_C_GetMechanismList (CK_SLOT_ID slotID,
+ CK_MECHANISM_TYPE_PTR pMechanismList,
+ CK_ULONG_PTR pulCount)
+{
+ g_return_val_if_fail (slotID == MOCK_SLOT_ONE_ID, CKR_SLOT_ID_INVALID);
+ g_return_val_if_fail (pulCount, CKR_ARGUMENTS_BAD);
+
+ /* Application only wants to know the number of slots. */
+ if (pMechanismList == NULL)
+ {
+ *pulCount = 0;
+ return CKR_OK;
+ }
+
+ return CKR_OK;
+}
+
+CK_RV
+mock_C_GetMechanismInfo (CK_SLOT_ID slotID,
+ CK_MECHANISM_TYPE type,
+ CK_MECHANISM_INFO_PTR pInfo)
+{
+ g_return_val_if_fail (slotID == MOCK_SLOT_ONE_ID, CKR_SLOT_ID_INVALID);
+ g_return_val_if_fail (pInfo, CKR_ARGUMENTS_BAD);
+
+ g_return_val_if_reached (CKR_MECHANISM_INVALID);
+}
+
+CK_RV
+mock_specific_args_C_InitToken (CK_SLOT_ID slotID,
+ CK_UTF8CHAR_PTR pPin,
+ CK_ULONG ulPinLen,
+ CK_UTF8CHAR_PTR pLabel)
+{
+ g_return_val_if_fail (slotID == MOCK_SLOT_ONE_ID, CKR_SLOT_ID_INVALID);
+
+ g_return_val_if_fail (pPin, CKR_PIN_INVALID);
+ g_return_val_if_fail (strlen ("TEST PIN") == ulPinLen, CKR_PIN_INVALID);
+ g_return_val_if_fail (strncmp ((gchar *)pPin, "TEST PIN", ulPinLen) == 0, CKR_PIN_INVALID);
+ g_return_val_if_fail (pLabel != NULL, CKR_PIN_INVALID);
+ g_return_val_if_fail (strcmp ((gchar *)pPin, "TEST LABEL") == 0, CKR_PIN_INVALID);
+
+ g_free (the_pin);
+ the_pin = g_strndup ((gchar *)pPin, ulPinLen);
+ n_the_pin = ulPinLen;
+ return CKR_OK;
+}
+
+CK_RV
+mock_unsupported_C_WaitForSlotEvent (CK_FLAGS flags,
+ CK_SLOT_ID_PTR pSlot,
+ CK_VOID_PTR pReserved)
+{
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_C_OpenSession (CK_SLOT_ID slotID,
+ CK_FLAGS flags,
+ CK_VOID_PTR pApplication,
+ CK_NOTIFY Notify,
+ CK_SESSION_HANDLE_PTR phSession)
+{
+ Session *sess;
+
+ g_return_val_if_fail (slotID == MOCK_SLOT_ONE_ID || slotID == MOCK_SLOT_TWO_ID, CKR_SLOT_ID_INVALID);
+ g_return_val_if_fail (phSession != NULL, CKR_ARGUMENTS_BAD);
+ g_return_val_if_fail ((flags & CKF_SERIAL_SESSION) == CKF_SERIAL_SESSION,
CKR_SESSION_PARALLEL_NOT_SUPPORTED);
+
+ if (slotID == MOCK_SLOT_TWO_ID)
+ return CKR_TOKEN_NOT_PRESENT;
+
+ sess = g_new0 (Session, 1);
+ sess->handle = ++unique_identifier;
+ sess->info.flags = flags;
+ sess->info.slotID = slotID;
+ sess->info.state = 0;
+ sess->info.ulDeviceError = 1414;
+ sess->objects = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL,
(GDestroyNotify)g_pkcs11_array_unref);
+ *phSession = sess->handle;
+
+ g_hash_table_replace (the_sessions, GUINT_TO_POINTER (sess->handle), sess);
+ return CKR_OK;
+}
+
+CK_RV
+mock_fail_C_OpenSession (CK_SLOT_ID slotID,
+ CK_FLAGS flags,
+ CK_VOID_PTR pApplication,
+ CK_NOTIFY Notify,
+ CK_SESSION_HANDLE_PTR phSession)
+{
+ return CKR_GENERAL_ERROR;
+}
+
+CK_RV
+mock_C_CloseSession (CK_SESSION_HANDLE hSession)
+{
+ Session *session;
+
+ session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+ g_return_val_if_fail (session, CKR_SESSION_HANDLE_INVALID);
+
+ g_hash_table_remove (the_sessions, GUINT_TO_POINTER (hSession));
+ return CKR_OK;
+}
+
+CK_RV
+mock_C_CloseAllSessions (CK_SLOT_ID slotID)
+{
+ g_return_val_if_fail (slotID == MOCK_SLOT_ONE_ID, CKR_SLOT_ID_INVALID);
+
+ g_hash_table_remove_all (the_sessions);
+ return CKR_OK;
+}
+
+CK_RV
+mock_C_GetFunctionStatus (CK_SESSION_HANDLE hSession)
+{
+ return CKR_FUNCTION_NOT_PARALLEL;
+}
+
+CK_RV
+mock_C_CancelFunction (CK_SESSION_HANDLE hSession)
+{
+ return CKR_FUNCTION_NOT_PARALLEL;
+}
+
+CK_RV
+mock_C_GetSessionInfo (CK_SESSION_HANDLE hSession,
+ CK_SESSION_INFO_PTR pInfo)
+{
+ Session *session;
+
+ g_return_val_if_fail (pInfo != NULL, CKR_ARGUMENTS_BAD);
+
+ 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 (logged_in)
+ {
+ if (session->info.flags & CKF_RW_SESSION)
+ session->info.state = CKS_RW_USER_FUNCTIONS;
+ else
+ session->info.state = CKS_RO_USER_FUNCTIONS;
+ }
+ else
+ {
+ if (session->info.flags & CKF_RW_SESSION)
+ session->info.state = CKS_RW_PUBLIC_SESSION;
+ else
+ session->info.state = CKS_RO_PUBLIC_SESSION;
+ }
+
+ memcpy (pInfo, &session->info, sizeof (*pInfo));
+ return CKR_OK;
+}
+
+CK_RV
+mock_fail_C_GetSessionInfo (CK_SESSION_HANDLE hSession,
+ CK_SESSION_INFO_PTR pInfo)
+{
+ return CKR_GENERAL_ERROR;
+}
+
+CK_RV
+mock_C_InitPIN (CK_SESSION_HANDLE hSession, CK_UTF8CHAR_PTR pPin,
+ CK_ULONG ulPinLen)
+{
+ Session *session;
+
+ session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+ g_return_val_if_fail (session, CKR_SESSION_HANDLE_INVALID);
+
+ g_free (the_pin);
+ the_pin = g_strndup ((gchar *)pPin, ulPinLen);
+ n_the_pin = ulPinLen;
+ return CKR_OK;
+}
+
+CK_RV
+mock_C_SetPIN (CK_SESSION_HANDLE hSession,
+ CK_UTF8CHAR_PTR pOldPin,
+ CK_ULONG ulOldLen,
+ CK_UTF8CHAR_PTR pNewPin,
+ CK_ULONG ulNewLen)
+{
+ Session *session;
+ gchar *old;
+
+ session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+ g_return_val_if_fail (session, CKR_SESSION_HANDLE_INVALID);
+
+ old = g_strndup ((gchar *)pOldPin, ulOldLen);
+ if (!old || !g_str_equal (old, the_pin))
+ return CKR_PIN_INCORRECT;
+
+ g_free (the_pin);
+ the_pin = g_strndup ((gchar *)pNewPin, ulNewLen);
+ n_the_pin = ulNewLen;
+ return CKR_OK;
+}
+
+CK_RV
+mock_unsupported_C_GetOperationState (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pOperationState,
+ CK_ULONG_PTR pulOperationStateLen)
+{
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_SetOperationState (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pOperationState,
+ CK_ULONG ulOperationStateLen,
+ CK_OBJECT_HANDLE hEncryptionKey,
+ CK_OBJECT_HANDLE hAuthenticationKey)
+{
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_C_Login (CK_SESSION_HANDLE hSession,
+ CK_USER_TYPE userType,
+ CK_UTF8CHAR_PTR pPin,
+ CK_ULONG pPinLen)
+{
+ Session *session;
+
+ g_return_val_if_fail (userType == CKU_SO ||
+ userType == CKU_USER ||
+ userType == CKU_CONTEXT_SPECIFIC,
+ CKR_USER_TYPE_INVALID);
+
+ session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+ g_return_val_if_fail (session != NULL, CKR_SESSION_HANDLE_INVALID);
+ g_return_val_if_fail (logged_in == FALSE, CKR_USER_ALREADY_LOGGED_IN);
+
+ if (!pPin)
+ return CKR_PIN_INCORRECT;
+
+ if (pPinLen != strlen (the_pin))
+ return CKR_PIN_INCORRECT;
+ if (strncmp ((gchar *)pPin, the_pin, pPinLen) != 0)
+ return CKR_PIN_INCORRECT;
+
+ if (userType == CKU_CONTEXT_SPECIFIC)
+ {
+ g_return_val_if_fail (session->want_context_login == TRUE, CKR_OPERATION_NOT_INITIALIZED);
+ session->want_context_login = CK_FALSE;
+ }
+ else
+ {
+ logged_in = TRUE;
+ user_type = userType;
+ }
+
+ return CKR_OK;
+}
+
+CK_RV
+mock_C_Logout (CK_SESSION_HANDLE hSession)
+{
+ Session *session;
+
+ 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;
+
+ g_assert (logged_in && "Not logged in");
+ logged_in = FALSE;
+ user_type = 0;
+ return CKR_OK;
+}
+
+CK_RV
+mock_C_CreateObject (CK_SESSION_HANDLE hSession,
+ CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount,
+ CK_OBJECT_HANDLE_PTR phObject)
+{
+ GPkcs11Array *attrs;
+ Session *session;
+ gboolean token, priv;
+ CK_ULONG i;
+
+ g_return_val_if_fail (phObject, CKR_ARGUMENTS_BAD);
+
+ session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+ g_return_val_if_fail (session, CKR_SESSION_HANDLE_INVALID);
+
+ attrs = g_pkcs11_array_new ();
+ for (i = 0; i < ulCount; ++i)
+ g_pkcs11_array_add_value (attrs, pTemplate[i].type, pTemplate[i].pValue, pTemplate[i].ulValueLen);
+
+ if (g_pkcs11_array_find_boolean (attrs, CKA_PRIVATE, &priv) && priv)
+ {
+ if (!logged_in)
+ {
+ g_pkcs11_array_unref (attrs);
+ return CKR_USER_NOT_LOGGED_IN;
+ }
+ }
+
+ *phObject = ++unique_identifier;
+ if (g_pkcs11_array_find_boolean (attrs, CKA_TOKEN, &token) && token)
+ g_hash_table_insert (the_objects, GUINT_TO_POINTER (*phObject), attrs);
+ else
+ g_hash_table_insert (session->objects, GUINT_TO_POINTER (*phObject), attrs);
+
+ return CKR_OK;
+}
+
+CK_RV
+mock_fail_C_CreateObject (CK_SESSION_HANDLE hSession,
+ CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount,
+ CK_OBJECT_HANDLE_PTR phObject)
+{
+ /* Always fails */
+ return CKR_FUNCTION_FAILED;
+}
+
+CK_RV
+mock_unsupported_C_CopyObject (CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject,
+ CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount,
+ CK_OBJECT_HANDLE_PTR phNewObject)
+{
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_C_DestroyObject (CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject)
+{
+ GPkcs11Array *attrs;
+ Session *session;
+ gboolean priv;
+
+ session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+ g_return_val_if_fail (session, CKR_SESSION_HANDLE_INVALID);
+
+ attrs = lookup_object (session, hObject);
+ g_return_val_if_fail (attrs, CKR_OBJECT_HANDLE_INVALID);
+
+ if (g_pkcs11_array_find_boolean (attrs, CKA_PRIVATE, &priv) && priv)
+ {
+ if (!logged_in)
+ return CKR_USER_NOT_LOGGED_IN;
+ }
+
+ g_hash_table_remove (the_objects, GUINT_TO_POINTER (hObject));
+ g_hash_table_remove (session->objects, GUINT_TO_POINTER (hObject));
+
+ return CKR_OK;
+}
+
+CK_RV
+mock_unsupported_C_GetObjectSize (CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject,
+ CK_ULONG_PTR pulSize)
+{
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_C_GetAttributeValue (CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject,
+ CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount)
+{
+ CK_ATTRIBUTE_PTR result;
+ CK_RV ret = CKR_OK;
+ GPkcs11Array *attrs;
+ const CK_ATTRIBUTE *attr;
+ Session *session;
+ CK_ULONG i;
+
+ session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+ g_return_val_if_fail (session, CKR_SESSION_HANDLE_INVALID);
+
+ attrs = lookup_object (session, hObject);
+ if (!attrs)
+ {
+ g_assert_not_reached (); /* "invalid object handle passed" */
+ return CKR_OBJECT_HANDLE_INVALID;
+ }
+
+ for (i = 0; i < ulCount; ++i)
+ {
+ result = pTemplate + i;
+ attr = g_pkcs11_array_find (attrs, result->type);
+ if (!attr)
+ {
+ result->ulValueLen = (CK_ULONG)-1;
+ ret = CKR_ATTRIBUTE_TYPE_INVALID;
+ continue;
+ }
+
+ if (!result->pValue)
+ {
+ result->ulValueLen = attr->ulValueLen;
+ continue;
+ }
+
+ if (result->ulValueLen >= attr->ulValueLen)
+ {
+ memcpy (result->pValue, attr->pValue, attr->ulValueLen);
+ continue;
+ }
+
+ result->ulValueLen = (CK_ULONG)-1;
+ ret = CKR_BUFFER_TOO_SMALL;
+ }
+
+ return ret;
+}
+
+CK_RV
+mock_fail_C_GetAttributeValue (CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject,
+ CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount)
+{
+ return CKR_FUNCTION_FAILED;
+}
+
+CK_RV
+mock_C_SetAttributeValue (CK_SESSION_HANDLE hSession, CK_OBJECT_HANDLE hObject,
+ CK_ATTRIBUTE_PTR pTemplate, CK_ULONG ulCount)
+{
+ Session *session;
+ GPkcs11Array *attrs;
+ CK_ULONG i;
+
+ session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+ g_return_val_if_fail (session, CKR_SESSION_HANDLE_INVALID);
+
+ attrs = lookup_object (session, hObject);
+ g_return_val_if_fail (attrs, CKR_OBJECT_HANDLE_INVALID);
+
+ for (i = 0; i < ulCount; ++i)
+ g_pkcs11_array_set (attrs, pTemplate + i);
+
+ return CKR_OK;
+}
+
+typedef struct
+{
+ CK_ATTRIBUTE_PTR template;
+ CK_ULONG count;
+ Session *session;
+} FindObjects;
+
+static gboolean
+enumerate_and_find_objects (CK_OBJECT_HANDLE object,
+ GPkcs11Array *attrs,
+ gpointer user_data)
+{
+ FindObjects *ctx = user_data;
+ CK_ATTRIBUTE_PTR match;
+ const CK_ATTRIBUTE *attr;
+ CK_ULONG i;
+
+ for (i = 0; i < ctx->count; ++i)
+ {
+ match = ctx->template + i;
+ attr = g_pkcs11_array_find (attrs, match->type);
+ if (!attr)
+ return TRUE; /* Continue */
+
+ if (attr->ulValueLen != match->ulValueLen ||
+ memcmp (attr->pValue, match->pValue, attr->ulValueLen) != 0)
+ return TRUE; /* Continue */
+ }
+
+ ctx->session->matches = g_list_prepend (ctx->session->matches, GUINT_TO_POINTER (object));
+ return TRUE; /* Continue */
+}
+
+CK_RV
+mock_C_FindObjectsInit (CK_SESSION_HANDLE hSession,
+ CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount)
+{
+ Session *session;
+ FindObjects ctx;
+
+ session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+ g_return_val_if_fail (session != NULL, CKR_SESSION_HANDLE_INVALID);
+
+ /* Starting an operation, cancels any previous one */
+ if (session->operation != 0)
+ session->operation = 0;
+
+ session->operation = OP_FIND;
+
+ ctx.template = pTemplate;
+ ctx.count = ulCount;
+ ctx.session = session;
+
+ mock_module_enumerate_objects (hSession, enumerate_and_find_objects, &ctx);
+ return CKR_OK;
+}
+
+CK_RV
+mock_fail_C_FindObjects (CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE_PTR phObject,
+ CK_ULONG ulMaxObjectCount,
+ CK_ULONG_PTR pulObjectCount)
+{
+ /* Always fails */
+ return CKR_FUNCTION_FAILED;
+}
+
+CK_RV
+mock_C_FindObjects (CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE_PTR phObject,
+ CK_ULONG ulMaxObjectCount,
+ CK_ULONG_PTR pulObjectCount)
+{
+ Session *session;
+
+ g_return_val_if_fail (phObject, CKR_ARGUMENTS_BAD);
+ g_return_val_if_fail (pulObjectCount, CKR_ARGUMENTS_BAD);
+ g_return_val_if_fail (ulMaxObjectCount != 0, CKR_ARGUMENTS_BAD);
+
+ session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+ g_return_val_if_fail (session != NULL, CKR_SESSION_HANDLE_INVALID);
+ g_return_val_if_fail (session->operation == OP_FIND, CKR_OPERATION_NOT_INITIALIZED);
+
+ *pulObjectCount = 0;
+ while (ulMaxObjectCount > 0 && session->matches)
+ {
+ *phObject = GPOINTER_TO_UINT (session->matches->data);
+ ++phObject;
+ --ulMaxObjectCount;
+ ++(*pulObjectCount);
+ session->matches = g_list_remove (session->matches, session->matches->data);
+ }
+
+ return CKR_OK;
+}
+
+CK_RV
+mock_C_FindObjectsFinal (CK_SESSION_HANDLE hSession)
+{
+ Session *session;
+
+ session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+ g_return_val_if_fail (session != NULL, CKR_SESSION_HANDLE_INVALID);
+ g_return_val_if_fail (session->operation == OP_FIND, CKR_OPERATION_NOT_INITIALIZED);
+
+ session->operation = 0;
+ g_list_free (session->matches);
+ session->matches = NULL;
+
+ return CKR_OK;
+}
+
+CK_RV
+mock_no_mechanisms_C_EncryptInit (CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hKey)
+{
+ Session *session;
+
+ session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+ g_return_val_if_fail (session != NULL, CKR_SESSION_HANDLE_INVALID);
+
+ return CKR_MECHANISM_INVALID;
+}
+
+CK_RV
+mock_not_initialized_C_Encrypt (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pData,
+ CK_ULONG ulDataLen,
+ CK_BYTE_PTR pEncryptedData,
+ CK_ULONG_PTR pulEncryptedDataLen)
+{
+ return CKR_OPERATION_NOT_INITIALIZED;
+}
+
+CK_RV
+mock_unsupported_C_EncryptUpdate (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pPart,
+ CK_ULONG ulPartLen,
+ CK_BYTE_PTR pEncryptedPart,
+ CK_ULONG_PTR pulEncryptedPartLen)
+{
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_EncryptFinal (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pLastEncryptedPart,
+ CK_ULONG_PTR pulLastEncryptedPartLen)
+{
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_no_mechanisms_C_DecryptInit (CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hKey)
+{
+ Session *session;
+
+ session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+ g_return_val_if_fail (session != NULL, CKR_SESSION_HANDLE_INVALID);
+
+ return CKR_MECHANISM_INVALID;
+}
+
+CK_RV
+mock_not_initialized_C_Decrypt (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pEncryptedData,
+ CK_ULONG ulEncryptedDataLen,
+ CK_BYTE_PTR pData,
+ CK_ULONG_PTR pulDataLen)
+{
+ return CKR_OPERATION_NOT_INITIALIZED;
+}
+
+CK_RV
+mock_unsupported_C_DecryptUpdate (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pEncryptedPart,
+ CK_ULONG ulEncryptedPartLen,
+ CK_BYTE_PTR pPart,
+ CK_ULONG_PTR pulPartLen)
+{
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_DecryptFinal (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pLastPart,
+ CK_ULONG_PTR pulLastPartLen)
+{
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_DigestInit (CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism)
+{
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_Digest (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pData,
+ CK_ULONG ulDataLen,
+ CK_BYTE_PTR pDigest,
+ CK_ULONG_PTR pulDigestLen)
+{
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_DigestUpdate (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pPart,
+ CK_ULONG ulPartLen)
+{
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_DigestKey (CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hKey)
+{
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_DigestFinal (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pDigest,
+ CK_ULONG_PTR pulDigestLen)
+{
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_no_mechanisms_C_SignInit (CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hKey)
+{
+ Session *session;
+
+ session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+ g_return_val_if_fail (session != NULL, CKR_SESSION_HANDLE_INVALID);
+
+ return CKR_MECHANISM_INVALID;
+}
+
+CK_RV
+mock_not_initialized_C_Sign (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pData,
+ CK_ULONG ulDataLen,
+ CK_BYTE_PTR pSignature,
+ CK_ULONG_PTR pulSignatureLen)
+{
+ return CKR_OPERATION_NOT_INITIALIZED;
+}
+
+CK_RV
+mock_unsupported_C_SignUpdate (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pPart,
+ CK_ULONG ulPartLen)
+{
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_SignFinal (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pSignature,
+ CK_ULONG_PTR pulSignatureLen)
+{
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_SignRecoverInit (CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hKey)
+{
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_SignRecover (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pData,
+ CK_ULONG ulDataLen,
+ CK_BYTE_PTR pSignature,
+ CK_ULONG_PTR pulSignatureLen)
+{
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_no_mechanisms_C_VerifyInit (CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hKey)
+{
+ Session *session;
+
+ session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+ g_return_val_if_fail (session != NULL, CKR_SESSION_HANDLE_INVALID);
+
+ return CKR_MECHANISM_INVALID;
+}
+
+CK_RV
+mock_not_initialized_C_Verify (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pData,
+ CK_ULONG ulDataLen,
+ CK_BYTE_PTR pSignature,
+ CK_ULONG ulSignatureLen)
+{
+ return CKR_OPERATION_NOT_INITIALIZED;
+}
+
+CK_RV
+mock_unsupported_C_VerifyUpdate (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pPart,
+ CK_ULONG ulPartLen)
+{
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_VerifyFinal (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pSignature,
+ CK_ULONG pulSignatureLen)
+{
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_VerifyRecoverInit (CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hKey)
+{
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_VerifyRecover (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pSignature,
+ CK_ULONG pulSignatureLen,
+ CK_BYTE_PTR pData,
+ CK_ULONG_PTR pulDataLen)
+{
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_DigestEncryptUpdate (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pPart,
+ CK_ULONG ulPartLen,
+ CK_BYTE_PTR pEncryptedPart,
+ CK_ULONG_PTR ulEncryptedPartLen)
+{
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_DecryptDigestUpdate (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pEncryptedPart,
+ CK_ULONG ulEncryptedPartLen,
+ CK_BYTE_PTR pPart,
+ CK_ULONG_PTR pulPartLen)
+{
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_SignEncryptUpdate (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pPart,
+ CK_ULONG ulPartLen,
+ CK_BYTE_PTR pEncryptedPart,
+ CK_ULONG_PTR ulEncryptedPartLen)
+{
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_DecryptVerifyUpdate (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pEncryptedPart,
+ CK_ULONG ulEncryptedPartLen,
+ CK_BYTE_PTR pPart,
+ CK_ULONG_PTR pulPartLen)
+{
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_GenerateKey (CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount,
+ CK_OBJECT_HANDLE_PTR phKey)
+{
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_no_mechanisms_C_GenerateKeyPair (CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_ATTRIBUTE_PTR pPublicKeyTemplate,
+ CK_ULONG ulPublicKeyAttributeCount,
+ CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
+ CK_ULONG ulPrivateKeyAttributeCount,
+ CK_OBJECT_HANDLE_PTR phPublicKey,
+ CK_OBJECT_HANDLE_PTR phPrivateKey)
+{
+ Session *session;
+
+ g_return_val_if_fail (pMechanism, CKR_MECHANISM_INVALID);
+ g_return_val_if_fail (pPublicKeyTemplate, CKR_TEMPLATE_INCOMPLETE);
+ g_return_val_if_fail (ulPublicKeyAttributeCount, CKR_TEMPLATE_INCOMPLETE);
+ g_return_val_if_fail (pPrivateKeyTemplate, CKR_TEMPLATE_INCOMPLETE);
+ g_return_val_if_fail (ulPrivateKeyAttributeCount, CKR_TEMPLATE_INCOMPLETE);
+ g_return_val_if_fail (phPublicKey, CKR_ARGUMENTS_BAD);
+ g_return_val_if_fail (phPrivateKey, CKR_ARGUMENTS_BAD);
+
+ session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+ g_return_val_if_fail (session != NULL, CKR_SESSION_HANDLE_INVALID);
+
+ return CKR_MECHANISM_INVALID;
+}
+
+CK_RV
+mock_no_mechanisms_C_WrapKey (CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hWrappingKey,
+ CK_OBJECT_HANDLE hKey,
+ CK_BYTE_PTR pWrappedKey,
+ CK_ULONG_PTR pulWrappedKeyLen)
+{
+ Session *session;
+
+ g_return_val_if_fail (pMechanism, CKR_MECHANISM_INVALID);
+ g_return_val_if_fail (hWrappingKey, CKR_OBJECT_HANDLE_INVALID);
+ g_return_val_if_fail (hKey, CKR_OBJECT_HANDLE_INVALID);
+ g_return_val_if_fail (pulWrappedKeyLen, CKR_WRAPPED_KEY_LEN_RANGE);
+
+ session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+ g_return_val_if_fail (session != NULL, CKR_SESSION_HANDLE_INVALID);
+
+ return CKR_MECHANISM_INVALID;
+}
+
+CK_RV
+mock_no_mechanisms_C_UnwrapKey (CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hUnwrappingKey,
+ CK_BYTE_PTR pWrappedKey,
+ CK_ULONG ulWrappedKeyLen,
+ CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount,
+ CK_OBJECT_HANDLE_PTR phKey)
+{
+ Session *session;
+
+ g_return_val_if_fail (pMechanism, CKR_MECHANISM_INVALID);
+ g_return_val_if_fail (hUnwrappingKey, CKR_WRAPPING_KEY_HANDLE_INVALID);
+ g_return_val_if_fail (pWrappedKey, CKR_WRAPPED_KEY_INVALID);
+ g_return_val_if_fail (ulWrappedKeyLen, CKR_WRAPPED_KEY_LEN_RANGE);
+ g_return_val_if_fail (phKey, CKR_ARGUMENTS_BAD);
+ g_return_val_if_fail (pTemplate, CKR_TEMPLATE_INCOMPLETE);
+ g_return_val_if_fail (ulCount, CKR_TEMPLATE_INCONSISTENT);
+
+ session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+ g_return_val_if_fail (session != NULL, CKR_SESSION_HANDLE_INVALID);
+
+ return CKR_MECHANISM_INVALID;
+}
+
+CK_RV
+mock_no_mechanisms_C_DeriveKey (CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hBaseKey,
+ CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount,
+ CK_OBJECT_HANDLE_PTR phKey)
+{
+ Session *session;
+
+ g_return_val_if_fail (pMechanism, CKR_MECHANISM_INVALID);
+ g_return_val_if_fail (ulCount, CKR_TEMPLATE_INCOMPLETE);
+ g_return_val_if_fail (pTemplate, CKR_TEMPLATE_INCOMPLETE);
+ g_return_val_if_fail (phKey, CKR_ARGUMENTS_BAD);
+
+ session = g_hash_table_lookup (the_sessions, GUINT_TO_POINTER (hSession));
+ g_return_val_if_fail (session, CKR_SESSION_HANDLE_INVALID);
+
+ return CKR_MECHANISM_INVALID;
+}
+
+CK_RV
+mock_unsupported_C_SeedRandom (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pSeed,
+ CK_ULONG ulSeedLen)
+{
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_RV
+mock_unsupported_C_GenerateRandom (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pRandomData,
+ CK_ULONG ulRandomLen)
+{
+ return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+CK_FUNCTION_LIST mock_default_functions = {
+ { 2, 11 }, /* version */
+ mock_validate_and_C_Initialize,
+ mock_C_Finalize,
+ mock_C_GetInfo,
+ mock_C_GetFunctionList,
+ mock_C_GetSlotList,
+ mock_C_GetSlotInfo,
+ mock_C_GetTokenInfo,
+ mock_C_GetMechanismList,
+ mock_C_GetMechanismInfo,
+ mock_specific_args_C_InitToken,
+ mock_C_InitPIN,
+ mock_C_SetPIN,
+ mock_C_OpenSession,
+ mock_C_CloseSession,
+ mock_C_CloseAllSessions,
+ mock_C_GetSessionInfo,
+ mock_unsupported_C_GetOperationState,
+ mock_unsupported_C_SetOperationState,
+ mock_C_Login,
+ mock_C_Logout,
+ mock_C_CreateObject,
+ mock_unsupported_C_CopyObject,
+ mock_C_DestroyObject,
+ mock_unsupported_C_GetObjectSize,
+ mock_C_GetAttributeValue,
+ mock_C_SetAttributeValue,
+ mock_C_FindObjectsInit,
+ mock_C_FindObjects,
+ mock_C_FindObjectsFinal,
+ mock_no_mechanisms_C_EncryptInit,
+ mock_not_initialized_C_Encrypt,
+ mock_unsupported_C_EncryptUpdate,
+ mock_unsupported_C_EncryptFinal,
+ mock_no_mechanisms_C_DecryptInit,
+ mock_not_initialized_C_Decrypt,
+ mock_unsupported_C_DecryptUpdate,
+ mock_unsupported_C_DecryptFinal,
+ mock_unsupported_C_DigestInit,
+ mock_unsupported_C_Digest,
+ mock_unsupported_C_DigestUpdate,
+ mock_unsupported_C_DigestKey,
+ mock_unsupported_C_DigestFinal,
+ mock_no_mechanisms_C_SignInit,
+ mock_not_initialized_C_Sign,
+ mock_unsupported_C_SignUpdate,
+ mock_unsupported_C_SignFinal,
+ mock_unsupported_C_SignRecoverInit,
+ mock_unsupported_C_SignRecover,
+ mock_no_mechanisms_C_VerifyInit,
+ mock_not_initialized_C_Verify,
+ mock_unsupported_C_VerifyUpdate,
+ mock_unsupported_C_VerifyFinal,
+ mock_unsupported_C_VerifyRecoverInit,
+ mock_unsupported_C_VerifyRecover,
+ mock_unsupported_C_DigestEncryptUpdate,
+ mock_unsupported_C_DecryptDigestUpdate,
+ mock_unsupported_C_SignEncryptUpdate,
+ mock_unsupported_C_DecryptVerifyUpdate,
+ mock_unsupported_C_GenerateKey,
+ mock_no_mechanisms_C_GenerateKeyPair,
+ mock_no_mechanisms_C_WrapKey,
+ mock_no_mechanisms_C_UnwrapKey,
+ mock_no_mechanisms_C_DeriveKey,
+ mock_unsupported_C_SeedRandom,
+ mock_unsupported_C_GenerateRandom,
+ mock_C_GetFunctionStatus,
+ mock_C_CancelFunction,
+ mock_unsupported_C_WaitForSlotEvent
+};
diff --git a/tls/tests/mock-pkcs11.h b/tls/tests/mock-pkcs11.h
new file mode 100644
index 0000000..a6bfa27
--- /dev/null
+++ b/tls/tests/mock-pkcs11.h
@@ -0,0 +1,396 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * Copyright (C) 2010 Stefan Walter
+ * Copyright (C) 2011 Collabora Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public 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 Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ */
+
+#include <glib.h>
+
+#include <p11-kit/pkcs11.h>
+
+#include "pkcs11/gpkcs11array.h"
+
+#ifndef MOCK_MODULE_H
+#define MOCK_MODULE_H
+
+extern CK_FUNCTION_LIST mock_default_functions;
+
+CK_RV mock_C_Initialize (CK_VOID_PTR pInitArgs);
+
+CK_RV mock_validate_and_C_Initialize (CK_VOID_PTR pInitArgs);
+
+CK_RV mock_C_Finalize (CK_VOID_PTR pReserved);
+
+CK_RV mock_C_GetInfo (CK_INFO_PTR pInfo);
+
+CK_RV mock_C_GetFunctionList (CK_FUNCTION_LIST_PTR_PTR list);
+
+CK_RV mock_C_GetSlotList (CK_BBOOL tokenPresent,
+ CK_SLOT_ID_PTR pSlotList,
+ CK_ULONG_PTR pulCount);
+
+CK_RV mock_C_GetSlotInfo (CK_SLOT_ID slotID,
+ CK_SLOT_INFO_PTR pInfo);
+
+CK_RV mock_C_GetTokenInfo (CK_SLOT_ID slotID,
+ CK_TOKEN_INFO_PTR pInfo);
+
+CK_RV mock_fail_C_GetTokenInfo (CK_SLOT_ID slotID,
+ CK_TOKEN_INFO_PTR pInfo);
+
+CK_RV mock_C_GetMechanismList (CK_SLOT_ID slotID,
+ CK_MECHANISM_TYPE_PTR pMechanismList,
+ CK_ULONG_PTR pulCount);
+
+CK_RV mock_C_GetMechanismInfo (CK_SLOT_ID slotID,
+ CK_MECHANISM_TYPE type,
+ CK_MECHANISM_INFO_PTR pInfo);
+
+CK_RV mock_specific_args_C_InitToken (CK_SLOT_ID slotID,
+ CK_UTF8CHAR_PTR pPin,
+ CK_ULONG ulPinLen,
+ CK_UTF8CHAR_PTR pLabel);
+
+CK_RV mock_unsupported_C_WaitForSlotEvent (CK_FLAGS flags,
+ CK_SLOT_ID_PTR pSlot,
+ CK_VOID_PTR pReserved);
+
+CK_RV mock_C_OpenSession (CK_SLOT_ID slotID,
+ CK_FLAGS flags,
+ CK_VOID_PTR pApplication,
+ CK_NOTIFY Notify,
+ CK_SESSION_HANDLE_PTR phSession);
+
+CK_RV mock_fail_C_OpenSession (CK_SLOT_ID slotID,
+ CK_FLAGS flags,
+ CK_VOID_PTR pApplication,
+ CK_NOTIFY Notify,
+ CK_SESSION_HANDLE_PTR phSession);
+
+CK_RV mock_C_CloseSession (CK_SESSION_HANDLE hSession);
+
+CK_RV mock_C_CloseAllSessions (CK_SLOT_ID slotID);
+
+CK_RV mock_C_GetFunctionStatus (CK_SESSION_HANDLE hSession);
+
+CK_RV mock_C_CancelFunction (CK_SESSION_HANDLE hSession);
+
+CK_RV mock_C_GetSessionInfo (CK_SESSION_HANDLE hSession,
+ CK_SESSION_INFO_PTR pInfo);
+
+CK_RV mock_fail_C_GetSessionInfo (CK_SESSION_HANDLE hSession,
+ CK_SESSION_INFO_PTR pInfo);
+
+CK_RV mock_C_InitPIN (CK_SESSION_HANDLE hSession,
+ CK_UTF8CHAR_PTR pPin,
+ CK_ULONG ulPinLen);
+
+CK_RV mock_C_SetPIN (CK_SESSION_HANDLE hSession,
+ CK_UTF8CHAR_PTR pOldPin,
+ CK_ULONG ulOldLen,
+ CK_UTF8CHAR_PTR pNewPin,
+ CK_ULONG ulNewLen);
+
+CK_RV mock_unsupported_C_GetOperationState (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pOperationState,
+ CK_ULONG_PTR pulOperationStateLen);
+
+CK_RV mock_unsupported_C_SetOperationState (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pOperationState,
+ CK_ULONG ulOperationStateLen,
+ CK_OBJECT_HANDLE hEncryptionKey,
+ CK_OBJECT_HANDLE hAuthenticationKey);
+
+CK_RV mock_C_Login (CK_SESSION_HANDLE hSession,
+ CK_USER_TYPE userType,
+ CK_UTF8CHAR_PTR pPin,
+ CK_ULONG pPinLen);
+
+CK_RV mock_C_Logout (CK_SESSION_HANDLE hSession);
+
+CK_RV mock_C_CreateObject (CK_SESSION_HANDLE hSession,
+ CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount,
+ CK_OBJECT_HANDLE_PTR phObject);
+
+CK_RV mock_fail_C_CreateObject (CK_SESSION_HANDLE hSession,
+ CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount,
+ CK_OBJECT_HANDLE_PTR phObject);
+
+CK_RV mock_unsupported_C_CopyObject (CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject,
+ CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount,
+ CK_OBJECT_HANDLE_PTR phNewObject);
+
+CK_RV mock_C_DestroyObject (CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject);
+
+CK_RV mock_unsupported_C_GetObjectSize (CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject,
+ CK_ULONG_PTR pulSize);
+
+CK_RV mock_C_GetAttributeValue (CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject,
+ CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount);
+
+CK_RV mock_fail_C_GetAttributeValue (CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject,
+ CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount);
+
+CK_RV mock_C_SetAttributeValue (CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hObject,
+ CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount);
+
+CK_RV mock_C_FindObjectsInit (CK_SESSION_HANDLE hSession,
+ CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount);
+
+CK_RV mock_C_FindObjects (CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE_PTR phObject,
+ CK_ULONG ulMaxObjectCount,
+ CK_ULONG_PTR pulObjectCount);
+
+CK_RV mock_fail_C_FindObjects (CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE_PTR phObject,
+ CK_ULONG ulMaxObjectCount,
+ CK_ULONG_PTR pulObjectCount);
+
+CK_RV mock_C_FindObjectsFinal (CK_SESSION_HANDLE hSession);
+
+CK_RV mock_no_mechanisms_C_EncryptInit (CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hKey);
+
+CK_RV mock_not_initialized_C_Encrypt (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pData,
+ CK_ULONG ulDataLen,
+ CK_BYTE_PTR pEncryptedData,
+ CK_ULONG_PTR pulEncryptedDataLen);
+
+CK_RV mock_unsupported_C_EncryptUpdate (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pPart,
+ CK_ULONG ulPartLen,
+ CK_BYTE_PTR pEncryptedPart,
+ CK_ULONG_PTR pulEncryptedPartLen);
+
+CK_RV mock_unsupported_C_EncryptFinal (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pLastEncryptedPart,
+ CK_ULONG_PTR pulLastEncryptedPartLen);
+
+CK_RV mock_no_mechanisms_C_DecryptInit (CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hKey);
+
+CK_RV mock_not_initialized_C_Decrypt (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pEncryptedData,
+ CK_ULONG ulEncryptedDataLen,
+ CK_BYTE_PTR pData,
+ CK_ULONG_PTR pulDataLen);
+
+CK_RV mock_unsupported_C_DecryptUpdate (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pEncryptedPart,
+ CK_ULONG ulEncryptedPartLen,
+ CK_BYTE_PTR pPart,
+ CK_ULONG_PTR pulPartLen);
+
+CK_RV mock_unsupported_C_DecryptFinal (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pLastPart,
+ CK_ULONG_PTR pulLastPartLen);
+
+CK_RV mock_unsupported_C_DigestInit (CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism);
+
+CK_RV mock_unsupported_C_Digest (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pData,
+ CK_ULONG ulDataLen,
+ CK_BYTE_PTR pDigest,
+ CK_ULONG_PTR pulDigestLen);
+
+CK_RV mock_unsupported_C_DigestUpdate (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pPart,
+ CK_ULONG ulPartLen);
+
+CK_RV mock_unsupported_C_DigestKey (CK_SESSION_HANDLE hSession,
+ CK_OBJECT_HANDLE hKey);
+
+CK_RV mock_unsupported_C_DigestFinal (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pDigest,
+ CK_ULONG_PTR pulDigestLen);
+
+CK_RV mock_no_mechanisms_C_SignInit (CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hKey);
+
+CK_RV mock_not_initialized_C_Sign (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pData,
+ CK_ULONG ulDataLen,
+ CK_BYTE_PTR pSignature,
+ CK_ULONG_PTR pulSignatureLen);
+
+CK_RV mock_unsupported_C_SignUpdate (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pPart,
+ CK_ULONG ulPartLen);
+
+CK_RV mock_unsupported_C_SignFinal (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pSignature,
+ CK_ULONG_PTR pulSignatureLen);
+
+CK_RV mock_unsupported_C_SignRecoverInit (CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hKey);
+
+CK_RV mock_unsupported_C_SignRecover (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pData,
+ CK_ULONG ulDataLen,
+ CK_BYTE_PTR pSignature,
+ CK_ULONG_PTR pulSignatureLen);
+
+CK_RV mock_no_mechanisms_C_VerifyInit (CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hKey);
+
+CK_RV mock_not_initialized_C_Verify (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pData,
+ CK_ULONG ulDataLen,
+ CK_BYTE_PTR pSignature,
+ CK_ULONG ulSignatureLen);
+
+CK_RV mock_unsupported_C_VerifyUpdate (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pPart,
+ CK_ULONG ulPartLen);
+
+CK_RV mock_unsupported_C_VerifyFinal (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pSignature,
+ CK_ULONG pulSignatureLen);
+
+CK_RV mock_unsupported_C_VerifyRecoverInit (CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hKey);
+
+CK_RV mock_unsupported_C_VerifyRecover (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pSignature,
+ CK_ULONG pulSignatureLen,
+ CK_BYTE_PTR pData,
+ CK_ULONG_PTR pulDataLen);
+
+CK_RV mock_unsupported_C_DigestEncryptUpdate (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pPart,
+ CK_ULONG ulPartLen,
+ CK_BYTE_PTR pEncryptedPart,
+ CK_ULONG_PTR ulEncryptedPartLen);
+
+CK_RV mock_unsupported_C_DecryptDigestUpdate (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pEncryptedPart,
+ CK_ULONG ulEncryptedPartLen,
+ CK_BYTE_PTR pPart,
+ CK_ULONG_PTR pulPartLen);
+
+CK_RV mock_unsupported_C_SignEncryptUpdate (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pPart,
+ CK_ULONG ulPartLen,
+ CK_BYTE_PTR pEncryptedPart,
+ CK_ULONG_PTR ulEncryptedPartLen);
+
+CK_RV mock_unsupported_C_DecryptVerifyUpdate (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pEncryptedPart,
+ CK_ULONG ulEncryptedPartLen,
+ CK_BYTE_PTR pPart,
+ CK_ULONG_PTR pulPartLen);
+
+CK_RV mock_unsupported_C_GenerateKey (CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount,
+ CK_OBJECT_HANDLE_PTR phKey);
+
+CK_RV mock_no_mechanisms_C_GenerateKeyPair (CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_ATTRIBUTE_PTR pPublicKeyTemplate,
+ CK_ULONG ulPublicKeyAttributeCount,
+ CK_ATTRIBUTE_PTR pPrivateKeyTemplate,
+ CK_ULONG ulPrivateKeyAttributeCount,
+ CK_OBJECT_HANDLE_PTR phPublicKey,
+ CK_OBJECT_HANDLE_PTR phPrivateKey);
+
+CK_RV mock_no_mechanisms_C_WrapKey (CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hWrappingKey,
+ CK_OBJECT_HANDLE hKey,
+ CK_BYTE_PTR pWrappedKey,
+ CK_ULONG_PTR pulWrappedKeyLen);
+
+CK_RV mock_no_mechanisms_C_UnwrapKey (CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE pUnwrappingKey,
+ CK_BYTE_PTR pWrappedKey,
+ CK_ULONG pulWrappedKeyLen,
+ CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount,
+ CK_OBJECT_HANDLE_PTR phKey);
+
+CK_RV mock_no_mechanisms_C_DeriveKey (CK_SESSION_HANDLE hSession,
+ CK_MECHANISM_PTR pMechanism,
+ CK_OBJECT_HANDLE hBaseKey,
+ CK_ATTRIBUTE_PTR pTemplate,
+ CK_ULONG ulCount,
+ CK_OBJECT_HANDLE_PTR phKey);
+
+CK_RV mock_unsupported_C_SeedRandom (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pSeed,
+ CK_ULONG ulSeedLen);
+
+CK_RV mock_unsupported_C_GenerateRandom (CK_SESSION_HANDLE hSession,
+ CK_BYTE_PTR pRandomData,
+ CK_ULONG ulRandomLen);
+
+CK_OBJECT_HANDLE mock_module_find_object (CK_SESSION_HANDLE session,
+ CK_ATTRIBUTE_PTR attrs,
+ CK_ULONG n_attrs);
+
+guint mock_module_count_objects (CK_SESSION_HANDLE session);
+
+typedef gboolean (*MockEnumerator) (CK_OBJECT_HANDLE handle,
+ GPkcs11Array *attrs,
+ gpointer user_data);
+
+void mock_module_enumerate_objects (CK_SESSION_HANDLE session,
+ MockEnumerator func,
+ gpointer user_data);
+
+CK_OBJECT_HANDLE mock_module_take_object (GPkcs11Array *attrs);
+
+void mock_module_set_object (CK_OBJECT_HANDLE object,
+ CK_ATTRIBUTE_PTR attrs,
+ CK_ULONG n_attrs);
+
+void mock_module_set_pin (const gchar *password);
+
+#define MOCK_SLOT_ONE_ID 52
+#define MOCK_SLOT_TWO_ID 134
+
+#define MOCK_SLOT_ONE_PIN "booo"
+#define MOCK_SLOT_ONE_URI "pkcs11:manufacturer=TEST%20MANUFACTURER;serial=TEST%20SERIAL"
+
+#endif /* MOCK_MODULE_H */
diff --git a/tls/tests/pkcs11-array.c b/tls/tests/pkcs11-array.c
new file mode 100644
index 0000000..c3c512d
--- /dev/null
+++ b/tls/tests/pkcs11-array.c
@@ -0,0 +1,288 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO TLS tests
+ *
+ * Copyright (C) 2011 Collabora, Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#include <gio/gio.h>
+
+#include <sys/types.h>
+#include <string.h>
+
+#include "pkcs11/gpkcs11array.h"
+
+typedef struct {
+ GPkcs11Array *array;
+} TestArray;
+
+static void
+setup_array (TestArray *test,
+ gconstpointer unused)
+{
+ test->array = g_pkcs11_array_new ();
+ g_assert (test->array);
+}
+
+static void
+teardown_array (TestArray *test,
+ gconstpointer unused)
+{
+ g_pkcs11_array_unref (test->array);
+}
+
+static void
+test_add_find (TestArray *test,
+ gconstpointer data)
+{
+ CK_ATTRIBUTE attr;
+ const CK_ATTRIBUTE *check;
+ const gchar *value = "test";
+
+ attr.type = CKA_LABEL;
+ attr.ulValueLen = strlen (value) + 1;
+ attr.pValue = (gpointer)value;
+ g_pkcs11_array_add (test->array, &attr);
+ memset (&attr, 0, sizeof (attr));
+
+ check = g_pkcs11_array_find (test->array, CKA_LABEL);
+ g_assert (check != NULL);
+ g_assert_cmpuint ((guint)check->ulValueLen, ==, strlen (value) + 1);
+ g_assert_cmpstr (check->pValue, ==, value);
+ g_assert (check->pValue != value);
+
+ /* Should be copied properly, and be independent from stack value */
+ g_assert (check != &attr);
+
+ check = g_pkcs11_array_find (test->array, CKA_ID);
+ g_assert (check == NULL);
+ g_assert_cmpuint (test->array->count, ==, 1);
+
+ /* Adding a second value of same type, should add a duplicate */
+ attr.type = CKA_LABEL;
+ attr.ulValueLen = 3;
+ attr.pValue = "bye";
+ g_pkcs11_array_add (test->array, &attr);
+ g_assert_cmpuint (test->array->count, ==, 2);
+}
+
+static void
+test_set_find (TestArray *test,
+ gconstpointer data)
+{
+ CK_ATTRIBUTE attr;
+ const CK_ATTRIBUTE *check;
+ const gchar *value = "test";
+
+ attr.type = CKA_LABEL;
+ attr.ulValueLen = strlen (value) + 1;
+ attr.pValue = (gpointer)value;
+ g_pkcs11_array_set (test->array, &attr);
+ memset (&attr, 0, sizeof (attr));
+
+ check = g_pkcs11_array_find (test->array, CKA_LABEL);
+ g_assert (check != NULL);
+ g_assert_cmpuint ((guint)check->ulValueLen, ==, strlen (value) + 1);
+ g_assert_cmpstr (check->pValue, ==, value);
+ g_assert (check->pValue != value);
+
+ /* Should be copied properly, and be independent from stack value */
+ g_assert (check != &attr);
+
+ /* Adding a second value of same type should override */
+ attr.type = CKA_LABEL;
+ attr.ulValueLen = 3;
+ attr.pValue = "bye";
+ g_pkcs11_array_set (test->array, &attr);
+ g_assert_cmpuint (test->array->count, ==, 1);
+}
+
+static void
+test_value (TestArray *test,
+ gconstpointer data)
+{
+ const CK_ATTRIBUTE *check;
+ const gchar *value = "test";
+
+ /* Add with null termiator */
+ g_pkcs11_array_add_value (test->array, CKA_LABEL, value, -1);
+ check = g_pkcs11_array_find (test->array, CKA_LABEL);
+ g_assert (check != NULL);
+ g_assert_cmpuint ((guint)check->ulValueLen, ==, strlen (value));
+ g_assert (memcmp (check->pValue, value, check->ulValueLen) == 0);
+ g_assert (check->pValue != value);
+
+ /* Add with value length */
+ g_pkcs11_array_add_value (test->array, CKA_ID, value, 3);
+ check = g_pkcs11_array_find (test->array, CKA_ID);
+ g_assert (check != NULL);
+ g_assert_cmpuint ((guint)check->ulValueLen, ==, 3);
+ g_assert (memcmp (check->pValue, value, check->ulValueLen) == 0);
+ g_assert (check->pValue != value);
+ g_assert_cmpuint (test->array->count, ==, 2);
+
+ /* Set should override */
+ g_pkcs11_array_set_value (test->array, CKA_LABEL, "boring", 6);
+ check = g_pkcs11_array_find (test->array, CKA_LABEL);
+ g_assert (check != NULL);
+ g_assert_cmpuint ((guint)check->ulValueLen, ==, 6);
+ g_assert (memcmp (check->pValue, "boring", check->ulValueLen) == 0);
+ g_assert_cmpuint (test->array->count, ==, 2);
+
+ /* Override with calculated length */
+ g_pkcs11_array_set_value (test->array, CKA_LABEL, "boring", -1);
+ check = g_pkcs11_array_find (test->array, CKA_LABEL);
+ g_assert (check != NULL);
+ g_assert_cmpuint ((guint)check->ulValueLen, ==, 6);
+ g_assert (memcmp (check->pValue, "boring", check->ulValueLen) == 0);
+ g_assert_cmpuint (test->array->count, ==, 2);
+
+}
+
+static void
+test_boolean (TestArray *test,
+ gconstpointer data)
+{
+ const CK_ATTRIBUTE *check;
+ gboolean bval = FALSE;
+
+ g_pkcs11_array_add_boolean (test->array, CKA_TOKEN, TRUE);
+ if (!g_pkcs11_array_find_boolean (test->array, CKA_TOKEN, &bval))
+ g_assert_not_reached ();
+ g_assert (bval == TRUE);
+
+ /* Check that it's actually formatted right */
+ check = g_pkcs11_array_find (test->array, CKA_TOKEN);
+ g_assert (check != NULL);
+ g_assert_cmpuint (check->ulValueLen, ==, sizeof (CK_BBOOL));
+ g_assert (check->pValue != NULL);
+ g_assert (*((CK_BBOOL*)check->pValue) == CK_TRUE);
+
+ /* Check FALSE */
+ g_pkcs11_array_add_boolean (test->array, CKA_ENCRYPT, FALSE);
+
+ /* Check that it's actually formatted right */
+ check = g_pkcs11_array_find (test->array, CKA_ENCRYPT);
+ g_assert (check != NULL);
+ g_assert_cmpuint (check->ulValueLen, ==, sizeof (CK_BBOOL));
+ g_assert (check->pValue != NULL);
+ g_assert (*((CK_BBOOL*)check->pValue) == CK_FALSE);
+ g_assert_cmpuint (test->array->count, ==, 2);
+
+ /* Add a non boolean value */
+ g_pkcs11_array_add_value (test->array, CKA_LABEL, "label", -1);
+
+ /* Shouldn't work to find boolean on that */
+ if (g_pkcs11_array_find_boolean (test->array, CKA_LABEL, &bval))
+ g_assert_not_reached ();
+ g_assert_cmpuint (test->array->count, ==, 3);
+
+ /* Set should override */
+ g_pkcs11_array_set_boolean (test->array, CKA_TOKEN, FALSE);
+ if (!g_pkcs11_array_find_boolean (test->array, CKA_TOKEN, &bval))
+ g_assert_not_reached ();
+ g_assert (bval == FALSE);
+ g_assert_cmpuint (test->array->count, ==, 3);
+}
+
+static void
+test_ulong (TestArray *test,
+ gconstpointer data)
+{
+ const CK_ATTRIBUTE *check;
+ gulong uval = FALSE;
+
+ g_pkcs11_array_add_ulong (test->array, CKA_PIXEL_X, 38938);
+ if (!g_pkcs11_array_find_ulong (test->array, CKA_PIXEL_X, &uval))
+ g_assert_not_reached ();
+ g_assert (uval == 38938UL);
+ g_assert_cmpuint (test->array->count, ==, 1);
+
+ /* Check that it's actually formatted right */
+ check = g_pkcs11_array_find (test->array, CKA_PIXEL_X);
+ g_assert (check != NULL);
+ g_assert_cmpuint (check->ulValueLen, ==, sizeof (CK_ULONG));
+ g_assert (check->pValue != NULL);
+ g_assert (*((CK_ULONG*)check->pValue) == 38938UL);
+
+ /* Check -1, since this is used regularly */
+ g_pkcs11_array_add_ulong (test->array, CKA_MODULUS_BITS, (gulong)-1);
+
+ /* Check that it's actually formatted right */
+ check = g_pkcs11_array_find (test->array, CKA_MODULUS_BITS);
+ g_assert (check != NULL);
+ g_assert_cmpuint (check->ulValueLen, ==, sizeof (CK_ULONG));
+ g_assert (check->pValue != NULL);
+ g_assert (*((CK_ULONG*)check->pValue) == (CK_ULONG)-1);
+ g_assert_cmpuint (test->array->count, ==, 2);
+
+ /* Add a non ulong length value */
+ g_pkcs11_array_add_value (test->array, CKA_LABEL, "label", -1);
+ g_assert_cmpuint (test->array->count, ==, 3);
+
+ /* Shouldn't work to find ulong on that */
+ if (g_pkcs11_array_find_ulong (test->array, CKA_LABEL, &uval))
+ g_assert_not_reached ();
+
+ /* Set should override */
+ g_pkcs11_array_set_ulong (test->array, CKA_PIXEL_X, 48);
+ if (!g_pkcs11_array_find_ulong (test->array, CKA_PIXEL_X, &uval))
+ g_assert_not_reached ();
+ g_assert (uval == 48UL);
+ g_assert_cmpuint (test->array->count, ==, 3);
+}
+
+static void
+test_boxed (TestArray *test,
+ gconstpointer data)
+{
+ GPkcs11Array *array;
+
+ /* Should reference */
+ array = g_boxed_copy (G_TYPE_PKCS11_ARRAY, test->array);
+ g_assert (array == test->array);
+
+ /* Should unreference */
+ g_boxed_free (G_TYPE_PKCS11_ARRAY, array);
+}
+
+int
+main (int argc,
+ char *argv[])
+{
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add ("/pkcs11/array/add-find", TestArray, NULL,
+ setup_array, test_add_find, teardown_array);
+ g_test_add ("/pkcs11/array/set-find", TestArray, NULL,
+ setup_array, test_set_find, teardown_array);
+ g_test_add ("/pkcs11/array/value", TestArray, NULL,
+ setup_array, test_value, teardown_array);
+ g_test_add ("/pkcs11/array/boolean", TestArray, NULL,
+ setup_array, test_boolean, teardown_array);
+ g_test_add ("/pkcs11/array/ulong", TestArray, NULL,
+ setup_array, test_ulong, teardown_array);
+ g_test_add ("/pkcs11/array/boxed", TestArray, NULL,
+ setup_array, test_boxed, teardown_array);
+
+ return g_test_run();
+}
diff --git a/tls/tests/pkcs11-pin.c b/tls/tests/pkcs11-pin.c
new file mode 100644
index 0000000..79a34dd
--- /dev/null
+++ b/tls/tests/pkcs11-pin.c
@@ -0,0 +1,152 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO TLS tests
+ *
+ * Copyright (C) 2011 Collabora, Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#include <gio/gio.h>
+
+#include <sys/types.h>
+#include <string.h>
+
+#include "pkcs11/gpkcs11pin.h"
+
+typedef struct {
+ GTlsPassword *pin;
+} TestPin;
+
+static void
+setup_pin (TestPin *test,
+ gconstpointer unused)
+{
+ test->pin = g_pkcs11_pin_new (G_TLS_PASSWORD_RETRY, "Test description");
+ g_assert (G_IS_PKCS11_PIN (test->pin));
+ g_assert (G_IS_TLS_PASSWORD (test->pin));
+}
+
+static void
+teardown_pin (TestPin *test,
+ gconstpointer unused)
+{
+ g_assert_cmpint (G_OBJECT (test->pin)->ref_count, ==, 1);
+ g_object_unref (test->pin);
+}
+
+static void
+test_attributes (TestPin *test,
+ gconstpointer data)
+{
+ GTlsPasswordFlags flags;
+ const gchar *description;
+
+ flags = g_tls_password_get_flags (test->pin);
+ g_assert_cmpuint (flags, ==, G_TLS_PASSWORD_RETRY);
+
+ description = g_tls_password_get_description (test->pin);
+ g_assert_cmpstr (description, ==, "Test description");
+}
+
+static void
+test_warnings (TestPin *test,
+ gconstpointer data)
+{
+ const gchar *warning;
+
+ g_tls_password_set_flags (test->pin, G_TLS_PASSWORD_RETRY);
+ warning = g_tls_password_get_warning (test->pin);
+ g_assert (warning != NULL);
+
+ g_tls_password_set_flags (test->pin, G_TLS_PASSWORD_FINAL_TRY);
+ warning = g_tls_password_get_warning (test->pin);
+ g_assert (warning != NULL);
+
+ g_tls_password_set_flags (test->pin, G_TLS_PASSWORD_MANY_TRIES);
+ warning = g_tls_password_get_warning (test->pin);
+ g_assert (warning != NULL);
+
+ g_tls_password_set_flags (test->pin, (GTlsPasswordFlags)0x10000000);
+ warning = g_tls_password_get_warning (test->pin);
+ g_assert (warning == NULL);
+
+}
+
+static void
+test_set_get_value (TestPin *test,
+ gconstpointer data)
+{
+ const guchar *value;
+ gsize n_value = G_MAXSIZE;
+
+ value = g_tls_password_get_value (test->pin, &n_value);
+ g_assert_cmpuint (n_value, ==, 0);
+ g_assert (value == NULL);
+
+ g_tls_password_set_value (test->pin, (const guchar *)"secret", -1);
+
+ value = g_tls_password_get_value (test->pin, &n_value);
+ g_assert_cmpuint (n_value, ==, 6);
+ g_assert (!strncmp ((const gchar *)value, "secret", n_value));
+
+ g_tls_password_set_value (test->pin, (const guchar *)"other", 5);
+
+ value = g_tls_password_get_value (test->pin, &n_value);
+ g_assert_cmpuint (n_value, ==, 5);
+ g_assert (!strncmp ((const gchar *)value, "other", n_value));
+}
+
+static void
+test_internal_pin (TestPin *test,
+ gconstpointer data)
+{
+ P11KitPin *pin;
+ const unsigned char *value;
+ size_t n_value;
+
+ g_tls_password_set_value (test->pin, (const guchar *)"secret", -1);
+
+ pin = g_pkcs11_pin_steal_internal (G_PKCS11_PIN (test->pin));
+
+ value = p11_kit_pin_get_value (pin, &n_value);
+ g_assert_cmpuint (n_value, ==, 6);
+ g_assert (!strncmp ((const gchar *)value, "secret", n_value));
+
+ p11_kit_pin_unref (pin);
+}
+
+int
+main (int argc,
+ char *argv[])
+{
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add ("/pkcs11/pin/attributes", TestPin, NULL,
+ setup_pin, test_attributes, teardown_pin);
+ g_test_add ("/pkcs11/pin/warnings", TestPin, NULL,
+ setup_pin, test_warnings, teardown_pin);
+ g_test_add ("/pkcs11/pin/set-get-value", TestPin, NULL,
+ setup_pin, test_set_get_value, teardown_pin);
+ g_test_add ("/pkcs11/pin/internal-pin", TestPin, NULL,
+ setup_pin, test_internal_pin, teardown_pin);
+
+ return g_test_run();
+}
diff --git a/tls/tests/pkcs11-slot.c b/tls/tests/pkcs11-slot.c
new file mode 100644
index 0000000..0fcd44b
--- /dev/null
+++ b/tls/tests/pkcs11-slot.c
@@ -0,0 +1,526 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO TLS tests
+ *
+ * Copyright (C) 2011 Collabora, Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#include <gio/gio.h>
+
+#include <sys/types.h>
+#include <string.h>
+
+#include "pkcs11/gpkcs11slot.h"
+#include "pkcs11/gpkcs11util.h"
+
+#include "mock-pkcs11.h"
+#include "mock-interaction.h"
+
+#include <p11-kit/p11-kit.h>
+
+#include <stdlib.h>
+
+typedef struct {
+ CK_FUNCTION_LIST funcs;
+ GPkcs11Slot *slot;
+ GPkcs11Slot *not_present;
+} TestSlot;
+
+static void
+setup_slot (TestSlot *test,
+ gconstpointer unused)
+{
+ CK_RV rv;
+
+ /* Copy this so we can replace certain functions in our tests */
+ memcpy (&test->funcs, &mock_default_functions, sizeof (test->funcs));
+
+ rv = p11_kit_module_initialize (&test->funcs);
+ g_assert (rv == CKR_OK);
+
+ test->slot = g_object_new (G_TYPE_PKCS11_SLOT,
+ "slot-id", MOCK_SLOT_ONE_ID,
+ "module", &test->funcs,
+ NULL);
+ g_assert (G_IS_PKCS11_SLOT (test->slot));
+
+ test->not_present = g_object_new (G_TYPE_PKCS11_SLOT,
+ "slot-id", MOCK_SLOT_TWO_ID,
+ "module", &test->funcs,
+ NULL);
+ g_assert (G_IS_PKCS11_SLOT (test->not_present));
+}
+
+static void
+teardown_slot (TestSlot *test,
+ gconstpointer unused)
+{
+ CK_RV rv;
+
+ g_assert_cmpint (G_OBJECT (test->slot)->ref_count, ==, 1);
+ g_object_unref (test->slot);
+
+ g_assert_cmpint (G_OBJECT (test->not_present)->ref_count, ==, 1);
+ g_object_unref (test->not_present);
+
+ rv = p11_kit_module_finalize (&test->funcs);
+ g_assert (rv == CKR_OK);
+}
+
+static void
+test_properties (TestSlot *test,
+ gconstpointer unused)
+{
+ CK_SLOT_ID id;
+ CK_FUNCTION_LIST_PTR module;
+
+ g_object_get (test->slot, "slot-id", &id, "module", &module, NULL);
+ g_assert_cmpuint (id, ==, MOCK_SLOT_ONE_ID);
+ g_assert (module == &test->funcs);
+}
+
+static void
+test_token_info (TestSlot *test,
+ gconstpointer unused)
+{
+ CK_TOKEN_INFO token_info;
+ char *label;
+
+ if (!g_pkcs11_slot_get_token_info (test->slot, &token_info))
+ g_assert_not_reached ();
+
+ label = p11_kit_space_strdup (token_info.label, sizeof (token_info.label));
+ g_assert_cmpstr (label, ==, "TEST LABEL");
+ free (label);
+}
+
+static void
+test_token_info_not_present (TestSlot *test,
+ gconstpointer unused)
+{
+ CK_TOKEN_INFO token_info;
+ char *label;
+
+ if (!g_pkcs11_slot_get_token_info (test->slot, &token_info))
+ g_assert_not_reached ();
+
+ label = p11_kit_space_strdup (token_info.label, sizeof (token_info.label));
+ g_assert_cmpstr (label, ==, "TEST LABEL");
+ free (label);
+}
+
+static void
+test_matches_uri (TestSlot *test,
+ gconstpointer unused)
+{
+ P11KitUri *uri;
+
+ uri = p11_kit_uri_new ();
+ if (p11_kit_uri_parse (MOCK_SLOT_ONE_URI, P11_KIT_URI_FOR_TOKEN, uri) != 0)
+ g_assert_not_reached ();
+ g_assert (!p11_kit_uri_any_unrecognized (uri));
+
+ if (!g_pkcs11_slot_matches_uri (test->slot, uri))
+ g_assert_not_reached();
+
+ if (g_pkcs11_slot_matches_uri (test->not_present, uri))
+ g_assert_not_reached ();
+
+ p11_kit_uri_free (uri);
+}
+
+
+static gboolean
+accumulate_check_not_called (gpointer result,
+ gpointer user_data)
+{
+ g_assert_not_reached ();
+ return FALSE;
+}
+
+static void
+test_enumerate_no_match (TestSlot *test,
+ gconstpointer unused)
+{
+ GPkcs11EnumerateState state;
+ CK_ATTRIBUTE_TYPE types[] = { CKA_LABEL, CKA_ID };
+ GError *error = NULL;
+ GPkcs11Array *match;
+
+ match = g_pkcs11_array_new ();
+ g_pkcs11_array_add_value (match, CKA_LABEL, "Non existant", -1);
+ g_pkcs11_array_add_value (match, CKA_ID, "Bad ID", -1);
+
+ state = g_pkcs11_slot_enumerate (test->slot, NULL,
+ match->attrs, match->count, FALSE,
+ types, G_N_ELEMENTS (types),
+ accumulate_check_not_called, NULL,
+ NULL, &error);
+
+ g_assert_cmpuint (state, ==, G_PKCS11_ENUMERATE_CONTINUE);
+ g_assert_no_error (error);
+
+ g_pkcs11_array_unref (match);
+}
+
+static void
+test_enumerate_not_present (TestSlot *test,
+ gconstpointer unused)
+{
+ GPkcs11EnumerateState state;
+ CK_ATTRIBUTE_TYPE types[] = { CKA_LABEL, CKA_ID };
+ GError *error = NULL;
+ GPkcs11Array *match;
+
+ /* Empty match should match anything ... */
+ match = g_pkcs11_array_new ();
+
+ /* ... but token is not present, so nothing */
+ state = g_pkcs11_slot_enumerate (test->not_present, NULL,
+ match->attrs, match->count, FALSE,
+ types, G_N_ELEMENTS (types),
+ accumulate_check_not_called, NULL,
+ NULL, &error);
+
+ g_assert_cmpuint (state, ==, G_PKCS11_ENUMERATE_CONTINUE);
+ g_assert_no_error (error);
+
+ g_pkcs11_array_unref (match);
+}
+
+static gboolean
+accumulate_results (gpointer result,
+ gpointer user_data)
+{
+ GPtrArray *results = user_data;
+ GPkcs11Array *attrs = result;
+
+ g_assert (results);
+ g_assert (attrs);
+
+ g_ptr_array_add (results, g_pkcs11_array_ref (attrs));
+ return TRUE;
+}
+
+static void
+test_enumerate_all (TestSlot *test,
+ gconstpointer unused)
+{
+ GPkcs11EnumerateState state;
+ CK_ATTRIBUTE_TYPE types[] = { CKA_LABEL, CKA_ID };
+ GError *error = NULL;
+ GPkcs11Array *match;
+ GPkcs11Array *attrs;
+ GPtrArray *results;
+ const CK_ATTRIBUTE *attr;
+ guint i;
+
+ /* Match anything */
+ match = g_pkcs11_array_new ();
+
+ results = g_ptr_array_new_with_free_func ((GDestroyNotify)g_pkcs11_array_unref);
+
+ state = g_pkcs11_slot_enumerate (test->slot, NULL,
+ match->attrs, match->count, FALSE,
+ types, G_N_ELEMENTS (types),
+ accumulate_results, results,
+ NULL, &error);
+
+ g_pkcs11_array_unref (match);
+
+ g_assert_cmpuint (state, ==, G_PKCS11_ENUMERATE_CONTINUE);
+ g_assert_no_error (error);
+
+ g_assert_cmpuint (results->len, >, 1);
+
+ for (i = 0; i < results->len; i++)
+ {
+ attrs = results->pdata[i];
+ attr = g_pkcs11_array_find (attrs, CKA_LABEL);
+ g_assert (attr != NULL);
+ g_assert (g_utf8_validate (attr->pValue, attr->ulValueLen, NULL));
+ }
+
+ g_ptr_array_free (results, TRUE);
+}
+
+static gboolean
+accumulate_first (gpointer result,
+ gpointer user_data)
+{
+ GPtrArray *results = user_data;
+ GPkcs11Array *attrs = result;
+
+ g_assert (results);
+ g_assert (attrs);
+ g_assert_cmpuint (results->len, ==, 0);
+
+ g_ptr_array_add (results, g_pkcs11_array_ref (attrs));
+ return FALSE; /* Don't call again */
+}
+
+static void
+test_enumerate_first (TestSlot *test,
+ gconstpointer unused)
+{
+ GPkcs11EnumerateState state;
+ CK_ATTRIBUTE_TYPE types[] = { CKA_LABEL, CKA_ID };
+ GError *error = NULL;
+ GPkcs11Array *match;
+ GPkcs11Array *attrs;
+ GPtrArray *results;
+ const CK_ATTRIBUTE *attr;
+
+ /* Match anything */
+ match = g_pkcs11_array_new ();
+
+ results = g_ptr_array_new_with_free_func ((GDestroyNotify)g_pkcs11_array_unref);
+
+ state = g_pkcs11_slot_enumerate (test->slot, NULL,
+ match->attrs, match->count, FALSE,
+ types, G_N_ELEMENTS (types),
+ accumulate_first, results,
+ NULL, &error);
+
+ g_pkcs11_array_unref (match);
+
+ g_assert_cmpuint (state, ==, G_PKCS11_ENUMERATE_STOP);
+ g_assert_no_error (error);
+
+ g_assert_cmpuint (results->len, ==, 1);
+ attrs = results->pdata[0];
+ attr = g_pkcs11_array_find (attrs, CKA_LABEL);
+ g_assert (attr != NULL);
+ g_assert (g_utf8_validate (attr->pValue, attr->ulValueLen, NULL));
+
+ g_ptr_array_free (results, TRUE);
+}
+
+static gboolean
+accumulate_check_null_result (gpointer result,
+ gpointer user_data)
+{
+ GPkcs11Array *attrs = result;
+ g_assert (attrs == NULL);
+ return TRUE; /* call again */
+}
+
+static void
+test_enumerate_no_attrs (TestSlot *test,
+ gconstpointer unused)
+{
+ GPkcs11EnumerateState state;
+ GError *error = NULL;
+ GPkcs11Array *match;
+
+ /* Match anything */
+ match = g_pkcs11_array_new ();
+
+ state = g_pkcs11_slot_enumerate (test->slot, NULL,
+ match->attrs, match->count, FALSE,
+ NULL, 0,
+ accumulate_check_null_result, NULL,
+ NULL, &error);
+
+ g_pkcs11_array_unref (match);
+
+ /* Didn't find anything, so continue */
+ g_assert_cmpuint (state, ==, G_PKCS11_ENUMERATE_CONTINUE);
+ g_assert_no_error (error);
+}
+
+static void
+test_enumerate_fail_session (TestSlot *test,
+ gconstpointer unused)
+{
+ GPkcs11EnumerateState state;
+ GError *error = NULL;
+
+ /* Make opening a session fail */
+ test->funcs.C_OpenSession = mock_fail_C_OpenSession;
+
+ state = g_pkcs11_slot_enumerate (test->slot, NULL,
+ NULL, 0, FALSE,
+ NULL, 0,
+ accumulate_check_not_called, NULL,
+ NULL, &error);
+
+ g_assert_cmpuint (state, ==, G_PKCS11_ENUMERATE_FAILED);
+ g_assert_error (error, G_PKCS11_ERROR, CKR_GENERAL_ERROR);
+ g_error_free (error);
+}
+
+static void
+test_enumerate_fail_attributes (TestSlot *test,
+ gconstpointer unused)
+{
+ GPkcs11EnumerateState state;
+ GError *error = NULL;
+ CK_ATTRIBUTE_TYPE types[] = { CKA_LABEL, CKA_ID };
+
+ /* Make retrieving object attrs fail */
+ test->funcs.C_GetAttributeValue = mock_fail_C_GetAttributeValue;
+
+ state = g_pkcs11_slot_enumerate (test->slot, NULL,
+ NULL, 0, FALSE,
+ types, G_N_ELEMENTS (types),
+ accumulate_check_not_called, NULL,
+ NULL, &error);
+
+ g_assert_cmpuint (state, ==, G_PKCS11_ENUMERATE_FAILED);
+ g_assert_error (error, G_PKCS11_ERROR, CKR_FUNCTION_FAILED);
+ g_error_free (error);
+}
+
+static gboolean
+accumulate_cancel_on_first (gpointer result,
+ gpointer user_data)
+{
+ GCancellable *cancellable = G_CANCELLABLE (user_data);
+ g_assert (!g_cancellable_is_cancelled (cancellable));
+ g_cancellable_cancel (cancellable);
+ return TRUE; /* call again, except that above cancellation should stop */
+}
+
+static void
+test_enumerate_cancel (TestSlot *test,
+ gconstpointer unused)
+{
+ GPkcs11EnumerateState state;
+ GError *error = NULL;
+ GPkcs11Array *match;
+ GCancellable *cancellable;
+
+ cancellable = g_cancellable_new ();
+
+ /* Match anything */
+ match = g_pkcs11_array_new ();
+
+ state = g_pkcs11_slot_enumerate (test->slot, NULL,
+ match->attrs, match->count, FALSE,
+ NULL, 0,
+ accumulate_cancel_on_first, cancellable,
+ cancellable, &error);
+
+ g_pkcs11_array_unref (match);
+ g_object_unref (cancellable);
+
+ g_assert_cmpuint (state, ==, G_PKCS11_ENUMERATE_FAILED);
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
+ g_error_free (error);
+}
+
+static void
+test_enumerate_private (TestSlot *test,
+ gconstpointer unused)
+{
+ CK_ATTRIBUTE_TYPE types[] = { CKA_LABEL, CKA_ID, CKA_PRIVATE };
+ GPkcs11EnumerateState state;
+ GError *error = NULL;
+ GPkcs11Array *match;
+ GPtrArray *results;
+ gboolean bval;
+ GTlsInteraction *interaction;
+
+ /* Match label of private object, see mock*/
+ match = g_pkcs11_array_new ();
+ g_pkcs11_array_add_value (match, CKA_LABEL, "PRIVATE", -1);
+
+ /* Shouldn't match anything, since not logged in */
+ state = g_pkcs11_slot_enumerate (test->slot, NULL,
+ match->attrs, match->count, FALSE,
+ types, G_N_ELEMENTS (types),
+ accumulate_check_not_called, NULL,
+ NULL, &error);
+
+ g_assert_cmpuint (state, ==, G_PKCS11_ENUMERATE_CONTINUE);
+ g_assert_no_error (error);
+
+ /* This time we try to log in but no interaction is set */
+ state = g_pkcs11_slot_enumerate (test->slot, NULL,
+ match->attrs, match->count, TRUE, /* match privates */
+ types, G_N_ELEMENTS (types),
+ accumulate_check_not_called, NULL,
+ NULL, &error);
+
+ g_assert_cmpuint (state, ==, G_PKCS11_ENUMERATE_CONTINUE);
+ g_assert_no_error (error);
+
+ /* This time we log in, and should have a match */
+ results = g_ptr_array_new_with_free_func ((GDestroyNotify)g_pkcs11_array_unref);
+ interaction = mock_interaction_new_static_password (MOCK_SLOT_ONE_PIN);
+
+ state = g_pkcs11_slot_enumerate (test->slot, interaction,
+ match->attrs, match->count, TRUE,
+ types, G_N_ELEMENTS (types),
+ accumulate_results, results,
+ NULL, &error);
+
+ g_assert_cmpuint (state, ==, G_PKCS11_ENUMERATE_CONTINUE);
+ g_assert_no_error (error);
+
+ /* One private object, with following info */
+ g_assert_cmpuint (results->len, ==, 1);
+ if (!g_pkcs11_array_find_boolean (results->pdata[0], CKA_PRIVATE, &bval))
+ g_assert_not_reached ();
+ g_assert (bval == TRUE);
+
+ g_object_unref (interaction);
+ g_pkcs11_array_unref (match);
+ g_ptr_array_free (results, TRUE);
+}
+
+int
+main (int argc,
+ char *argv[])
+{
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add ("/pkcs11/slot/properties", TestSlot, NULL,
+ setup_slot, test_properties, teardown_slot);
+ g_test_add ("/pkcs11/slot/token-info", TestSlot, NULL,
+ setup_slot, test_token_info, teardown_slot);
+ g_test_add ("/pkcs11/slot/token-not-present", TestSlot, NULL,
+ setup_slot, test_token_info_not_present, teardown_slot);
+ g_test_add ("/pkcs11/slot/matches-uri", TestSlot, NULL,
+ setup_slot, test_matches_uri, teardown_slot);
+ g_test_add ("/pkcs11/slot/enumerate-no-match", TestSlot, NULL,
+ setup_slot, test_enumerate_no_match, teardown_slot);
+ g_test_add ("/pkcs11/slot/enumerate-not-present", TestSlot, NULL,
+ setup_slot, test_enumerate_not_present, teardown_slot);
+ g_test_add ("/pkcs11/slot/enumerate-all", TestSlot, NULL,
+ setup_slot, test_enumerate_all, teardown_slot);
+ g_test_add ("/pkcs11/slot/enumerate-first", TestSlot, NULL,
+ setup_slot, test_enumerate_first, teardown_slot);
+ g_test_add ("/pkcs11/slot/enumerate-no-attrs", TestSlot, NULL,
+ setup_slot, test_enumerate_no_attrs, teardown_slot);
+ g_test_add ("/pkcs11/slot/enumerate-fail-session", TestSlot, NULL,
+ setup_slot, test_enumerate_fail_session, teardown_slot);
+ g_test_add ("/pkcs11/slot/enumerate-fail-attributes", TestSlot, NULL,
+ setup_slot, test_enumerate_fail_attributes, teardown_slot);
+ g_test_add ("/pkcs11/slot/enumerate-cancel", TestSlot, NULL,
+ setup_slot, test_enumerate_cancel, teardown_slot);
+ g_test_add ("/pkcs11/slot/enumerate-private", TestSlot, NULL,
+ setup_slot, test_enumerate_private, teardown_slot);
+
+ return g_test_run();
+}
diff --git a/tls/tests/pkcs11-util.c b/tls/tests/pkcs11-util.c
new file mode 100644
index 0000000..b432651
--- /dev/null
+++ b/tls/tests/pkcs11-util.c
@@ -0,0 +1,63 @@
+/* -*- Mode: C; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/*
+ * GIO TLS tests
+ *
+ * Copyright (C) 2011 Collabora, Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * In addition, when the library is used with OpenSSL, a special
+ * exception applies. Refer to the LICENSE_EXCEPTION file for details.
+ *
+ * Author: Stef Walter <stefw collabora co uk>
+ */
+
+#include <gio/gio.h>
+
+#include <sys/types.h>
+#include <string.h>
+
+#include "pkcs11/gpkcs11util.h"
+
+static void
+test_propagate_error (void)
+{
+ GError *error = NULL;
+
+ if (!g_pkcs11_propagate_error (&error, CKR_BUFFER_TOO_SMALL))
+ g_assert_not_reached ();
+ g_assert_error (error, G_PKCS11_ERROR, (gint)CKR_BUFFER_TOO_SMALL);
+ g_clear_error (&error);
+
+ if (g_pkcs11_propagate_error (&error, CKR_OK))
+ g_assert_not_reached ();
+ g_assert_no_error (error);
+
+ if (!g_pkcs11_propagate_error (&error, CKR_CANCEL))
+ g_assert_not_reached ();
+ g_assert_error (error, G_IO_ERROR, G_IO_ERROR_CANCELLED);
+ g_clear_error (&error);
+}
+
+int
+main (int argc,
+ char *argv[])
+{
+ g_test_init (&argc, &argv, NULL);
+
+ g_test_add_func ("/pkcs11/util/propagate-error", test_propagate_error);
+
+ return g_test_run();
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]