summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStef Walter <stefw@gnome.org>2013-01-21 11:06:41 +0100
committerStef Walter <stefw@gnome.org>2013-02-05 14:54:53 +0100
commit3e70ecbab850bcc08ee89e1256d82cca70d80ee7 (patch)
treefadca8bd00fb750cbbedf51c09c2bf65e2689a1c
parente5816187231ce27e5f634995e62c1d3ae5c5b2f1 (diff)
downloadp11-kit-3e70ecbab850bcc08ee89e1256d82cca70d80ee7.tar.gz
Add public iterator API to p11-kit
-rw-r--r--common/mock.c113
-rw-r--r--common/mock.h37
-rw-r--r--doc/Makefile.am1
-rw-r--r--doc/annotation-glossary.xml67
-rw-r--r--doc/p11-kit-docs.sgml3
-rw-r--r--doc/p11-kit-sections.txt17
-rw-r--r--p11-kit/Makefile.am2
-rw-r--r--p11-kit/iter.c829
-rw-r--r--p11-kit/iter.h101
-rw-r--r--p11-kit/p11-kit.h2
-rw-r--r--p11-kit/tests/Makefile.am4
-rw-r--r--p11-kit/tests/mock-module-ep.c2
-rw-r--r--p11-kit/tests/test-iter.c1140
13 files changed, 2308 insertions, 10 deletions
diff --git a/common/mock.c b/common/mock.c
index 14fd3ec..411c6eb 100644
--- a/common/mock.c
+++ b/common/mock.c
@@ -34,6 +34,7 @@
#include "config.h"
+#include "debug.h"
#include "library.h"
#define CRYPTOKI_EXPORTS
#include "pkcs11.h"
@@ -220,7 +221,7 @@ mock_module_reset_objects (CK_SLOT_ID slot_id)
{ CKA_LABEL, label, strlen (label) },
{ CKA_INVALID, NULL, 0 },
};
- p11_dict_set (the_objects, handle_to_pointer (2), p11_attrs_dup (attrs));
+ p11_dict_set (the_objects, handle_to_pointer (MOCK_DATA_OBJECT), p11_attrs_dup (attrs));
}
/* Private capitalize key */
@@ -353,8 +354,6 @@ mock_C_Initialize (CK_VOID_PTR init_args)
CK_RV ret = CKR_OK;
pid_t pid;
- p11_debug ("C_Initialize: enter");
-
p11_mutex_lock (&init_mutex);
if (init_args != NULL) {
@@ -369,7 +368,7 @@ mock_C_Initialize (CK_VOID_PTR init_args)
(args->CreateMutex != NULL && args->DestroyMutex != NULL &&
args->LockMutex != NULL && args->UnlockMutex != NULL);
if (!supplied_ok) {
- p11_debug_precond ("p11-kit: invalid set of mutex calls supplied");
+ p11_debug_precond ("invalid set of mutex calls supplied\n");
ret = CKR_ARGUMENTS_BAD;
goto done;
}
@@ -379,7 +378,7 @@ mock_C_Initialize (CK_VOID_PTR init_args)
* We must be able to use our pthread functionality.
*/
if (!(args->flags & CKF_OS_LOCKING_OK)) {
- p11_debug_precond ("p11-kit: can't do without os locking");
+ p11_debug_precond ("can't do without os locking\n");
ret = CKR_CANT_LOCK;
goto done;
}
@@ -390,7 +389,7 @@ mock_C_Initialize (CK_VOID_PTR init_args)
/* This process has called C_Initialize already */
if (pid == pkcs11_initialized_pid) {
- p11_debug_precond ("p11-kit: C_Initialize called twice for same process");
+ p11_debug_precond ("p11-kit: C_Initialize called twice for same process\n");
ret = CKR_CRYPTOKI_ALREADY_INITIALIZED;
goto done;
}
@@ -560,7 +559,25 @@ mock_C_GetSlotInfo (CK_SLOT_ID slot_id,
}
CK_RV
-mock_C_GetSlotInfo__invalid_slotid (CK_SLOT_ID slot_id,
+mock_C_GetSlotList__fail_first (CK_BBOOL token_present,
+ CK_SLOT_ID_PTR slot_list,
+ CK_ULONG_PTR count)
+{
+ return CKR_VENDOR_DEFINED;
+}
+
+CK_RV
+mock_C_GetSlotList__fail_late (CK_BBOOL token_present,
+ CK_SLOT_ID_PTR slot_list,
+ CK_ULONG_PTR count)
+{
+ if (!slot_list)
+ return mock_C_GetSlotList (token_present, slot_list, count);
+ return CKR_VENDOR_DEFINED;
+}
+
+CK_RV
+mock_C_GetSlotInfo__invalid_slotid (CK_SLOT_ID id,
CK_SLOT_INFO_PTR info)
{
return_val_if_fail (info, CKR_ARGUMENTS_BAD);
@@ -650,7 +667,26 @@ mock_C_GetMechanismList (CK_SLOT_ID slot_id,
}
CK_RV
-mock_C_GetMechanismList__invalid_slotid (CK_SLOT_ID slot_id,
+mock_C_GetTokenInfo_not_initialized (CK_SLOT_ID slot_id,
+ CK_TOKEN_INFO_PTR info)
+{
+ CK_RV rv;
+
+ rv = mock_C_GetTokenInfo (slot_id, info);
+ if (rv == CKR_OK)
+ info->flags &= ~ CKF_TOKEN_INITIALIZED;
+
+ return rv;
+}
+
+/*
+ * TWO mechanisms:
+ * CKM_MOCK_CAPITALIZE
+ * CKM_MOCK_PREFIX
+ */
+
+CK_RV
+mock_C_GetMechanismList__invalid_slotid (CK_SLOT_ID id,
CK_MECHANISM_TYPE_PTR mechanism_list,
CK_ULONG_PTR count)
{
@@ -808,6 +844,18 @@ mock_C_OpenSession__invalid_slotid (CK_SLOT_ID slot_id,
}
CK_RV
+mock_C_OpenSession__fails (CK_SLOT_ID slot_id,
+ CK_FLAGS flags,
+ CK_VOID_PTR user_data,
+ CK_NOTIFY callback,
+ CK_SESSION_HANDLE_PTR session)
+{
+ return_val_if_fail (session, CKR_ARGUMENTS_BAD);
+
+ return CKR_DEVICE_ERROR;
+}
+
+CK_RV
mock_C_CloseSession (CK_SESSION_HANDLE session)
{
Session *sess;
@@ -1339,6 +1387,30 @@ mock_C_GetAttributeValue__invalid_handle (CK_SESSION_HANDLE session,
}
CK_RV
+mock_C_GetAttributeValue__fail_first (CK_SESSION_HANDLE session,
+ CK_OBJECT_HANDLE object,
+ CK_ATTRIBUTE_PTR template,
+ CK_ULONG count)
+{
+ return CKR_FUNCTION_REJECTED;
+}
+
+CK_RV
+mock_C_GetAttributeValue__fail_late (CK_SESSION_HANDLE session,
+ CK_OBJECT_HANDLE object,
+ CK_ATTRIBUTE_PTR template,
+ CK_ULONG count)
+{
+ CK_ULONG i;
+
+ for (i = 0; i < count; i++) {
+ if (template[i].pValue)
+ return CKR_FUNCTION_FAILED;
+ }
+ return mock_C_GetAttributeValue (session, object, template, count);
+}
+
+CK_RV
mock_C_SetAttributeValue (CK_SESSION_HANDLE session,
CK_OBJECT_HANDLE object,
CK_ATTRIBUTE_PTR template,
@@ -1386,8 +1458,14 @@ enumerate_and_find_objects (CK_OBJECT_HANDLE object,
FindObjects *ctx = user_data;
CK_ATTRIBUTE *match;
CK_ATTRIBUTE *attr;
+ CK_BBOOL private;
CK_ULONG i;
+ if (!logged_in) {
+ if (p11_attrs_find_bool (attrs, CKA_PRIVATE, &private) && private)
+ return 1; /* Continue */
+ }
+
for (i = 0; i < ctx->count; ++i) {
match = ctx->template + i;
attr = p11_attrs_find (attrs, match->type);
@@ -1450,6 +1528,14 @@ mock_C_FindObjectsInit__invalid_handle (CK_SESSION_HANDLE session,
}
CK_RV
+mock_C_FindObjectsInit__fails (CK_SESSION_HANDLE session,
+ CK_ATTRIBUTE_PTR template,
+ CK_ULONG count)
+{
+ return CKR_DEVICE_MEMORY;
+}
+
+CK_RV
mock_C_FindObjects (CK_SESSION_HANDLE session,
CK_OBJECT_HANDLE_PTR objects,
CK_ULONG max_object_count,
@@ -1493,6 +1579,17 @@ mock_C_FindObjects__invalid_handle (CK_SESSION_HANDLE session,
}
CK_RV
+mock_C_FindObjects__fails (CK_SESSION_HANDLE session,
+ CK_OBJECT_HANDLE_PTR objects,
+ CK_ULONG max_count,
+ CK_ULONG_PTR count)
+{
+ return_val_if_fail (count, CKR_ARGUMENTS_BAD);
+
+ return CKR_DEVICE_REMOVED;
+}
+
+CK_RV
mock_C_FindObjectsFinal (CK_SESSION_HANDLE session)
{
diff --git a/common/mock.h b/common/mock.h
index 5beebe6..daac1c8 100644
--- a/common/mock.h
+++ b/common/mock.h
@@ -67,6 +67,7 @@ enum {
MOCK_SLOT_ONE_ID = 52,
MOCK_SLOT_TWO_ID = 134,
+ MOCK_DATA_OBJECT = 2,
MOCK_PRIVATE_KEY_CAPITALIZE = 3,
MOCK_PUBLIC_KEY_CAPITALIZE = 4,
MOCK_PRIVATE_KEY_PREFIX = 5,
@@ -109,6 +110,14 @@ CK_RV mock_C_GetSlotList__no_tokens (CK_BBOOL token_presen
CK_SLOT_ID_PTR slot_list,
CK_ULONG_PTR count);
+CK_RV mock_C_GetSlotList__fail_first (CK_BBOOL token_present,
+ CK_SLOT_ID_PTR slot_list,
+ CK_ULONG_PTR count);
+
+CK_RV mock_C_GetSlotList__fail_late (CK_BBOOL token_present,
+ CK_SLOT_ID_PTR slot_list,
+ CK_ULONG_PTR count);
+
CK_RV mock_C_GetSlotInfo (CK_SLOT_ID slot_id,
CK_SLOT_INFO_PTR info);
@@ -121,6 +130,9 @@ CK_RV mock_C_GetTokenInfo (CK_SLOT_ID slot_id,
CK_RV mock_C_GetTokenInfo__invalid_slotid (CK_SLOT_ID slot_id,
CK_TOKEN_INFO_PTR info);
+CK_RV mock_C_GetTokenInfo_not_initialized (CK_SLOT_ID slot_id,
+ CK_TOKEN_INFO_PTR info);
+
CK_RV mock_C_GetMechanismList (CK_SLOT_ID slot_id,
CK_MECHANISM_TYPE_PTR mechanism_list,
CK_ULONG_PTR count);
@@ -161,6 +173,12 @@ CK_RV mock_C_OpenSession__invalid_slotid (CK_SLOT_ID slot_id,
CK_NOTIFY callback,
CK_SESSION_HANDLE_PTR session);
+CK_RV mock_C_OpenSession__fails (CK_SLOT_ID slot_id,
+ CK_FLAGS flags,
+ CK_VOID_PTR user_data,
+ CK_NOTIFY callback,
+ CK_SESSION_HANDLE_PTR session);
+
CK_RV mock_C_OpenSession (CK_SLOT_ID slot_id,
CK_FLAGS flags,
CK_VOID_PTR user_data,
@@ -289,6 +307,16 @@ CK_RV mock_C_GetAttributeValue__invalid_handle (CK_SESSION_HANDLE ses
CK_ATTRIBUTE_PTR template,
CK_ULONG count);
+CK_RV mock_C_GetAttributeValue__fail_first (CK_SESSION_HANDLE session,
+ CK_OBJECT_HANDLE object,
+ CK_ATTRIBUTE_PTR template,
+ CK_ULONG count);
+
+CK_RV mock_C_GetAttributeValue__fail_late (CK_SESSION_HANDLE session,
+ CK_OBJECT_HANDLE object,
+ CK_ATTRIBUTE_PTR template,
+ CK_ULONG count);
+
CK_RV mock_C_SetAttributeValue (CK_SESSION_HANDLE session,
CK_OBJECT_HANDLE object,
CK_ATTRIBUTE_PTR template,
@@ -307,6 +335,10 @@ CK_RV mock_C_FindObjectsInit__invalid_handle (CK_SESSION_HANDLE ses
CK_ATTRIBUTE_PTR template,
CK_ULONG count);
+CK_RV mock_C_FindObjectsInit__fails (CK_SESSION_HANDLE session,
+ CK_ATTRIBUTE_PTR template,
+ CK_ULONG count);
+
CK_RV mock_C_FindObjects (CK_SESSION_HANDLE session,
CK_OBJECT_HANDLE_PTR objects,
CK_ULONG max_object_count,
@@ -317,6 +349,11 @@ CK_RV mock_C_FindObjects__invalid_handle (CK_SESSION_HANDLE ses
CK_ULONG max_count,
CK_ULONG_PTR count);
+CK_RV mock_C_FindObjects__fails (CK_SESSION_HANDLE session,
+ CK_OBJECT_HANDLE_PTR objects,
+ CK_ULONG max_count,
+ CK_ULONG_PTR count);
+
CK_RV mock_C_FindObjectsFinal (CK_SESSION_HANDLE session);
CK_RV mock_C_FindObjectsFinal__invalid_handle (CK_SESSION_HANDLE session);
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 3154215..e5befe7 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -69,6 +69,7 @@ content_files=p11-kit-config.xml p11-kit-sharing.xml \
p11-kit-devel.xml \
p11-kit-trust.xml \
p11-kit.xml \
+ annotation-glossary.xml \
$(NULL)
# SGML files where gtk-doc abbrevations (#GtkWidget) are expanded
diff --git a/doc/annotation-glossary.xml b/doc/annotation-glossary.xml
new file mode 100644
index 0000000..4a0f8a6
--- /dev/null
+++ b/doc/annotation-glossary.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0"?>
+<!DOCTYPE glossary PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN"
+ "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd" [
+<!ENTITY version SYSTEM "version.xml">
+]>
+
+<glossary id="annotation-glossary">
+<title>Annotation Glossary</title>
+<glossdiv><title>A</title>
+ <glossentry>
+ <glossterm><anchor id="annotation-glossterm-allow-none"/>allow-none</glossterm>
+ <glossdef>
+ <para>NULL is ok, both for passing and for returning.</para>
+ </glossdef>
+ </glossentry>
+ <glossentry>
+ <glossterm><anchor id="annotation-glossterm-array"/>array</glossterm>
+ <glossdef>
+ <para>Parameter points to an array of items.</para>
+ </glossdef>
+ </glossentry>
+</glossdiv>
+<glossdiv><title>E</title>
+ <glossentry>
+ <glossterm><anchor id="annotation-glossterm-element-type"/>element-type</glossterm>
+ <glossdef>
+ <para>Generics and defining elements of containers and arrays.</para>
+ </glossdef>
+ </glossentry>
+</glossdiv>
+<glossdiv><title>I</title>
+ <glossentry>
+ <glossterm><anchor id="annotation-glossterm-inout"/>inout</glossterm>
+ <glossdef>
+ <para>Parameter for input and for returning results. Default is <acronym>transfer full</acronym>.</para>
+ </glossdef>
+ </glossentry>
+</glossdiv>
+<glossdiv><title>O</title>
+ <glossentry>
+ <glossterm><anchor id="annotation-glossterm-out"/>out</glossterm>
+ <glossdef>
+ <para>Parameter for returning results. Default is <acronym>transfer full</acronym>.</para>
+ </glossdef>
+ </glossentry>
+</glossdiv>
+<glossdiv><title>T</title>
+ <glossentry>
+ <glossterm><anchor id="annotation-glossterm-transfer full"/>transfer full</glossterm>
+ <glossdef>
+ <para>Free data after the code is done.</para>
+ </glossdef>
+ </glossentry>
+ <glossentry>
+ <glossterm><anchor id="annotation-glossterm-type"/>type</glossterm>
+ <glossdef>
+ <para>Override the parsed C type with given type</para>
+ </glossdef>
+ </glossentry>
+ <glossentry>
+ <glossterm><anchor id="annotation-glossterm-transfer none"/>transfer none</glossterm>
+ <glossdef>
+ <para>Don't free data after the code is done.</para>
+ </glossdef>
+ </glossentry>
+</glossdiv>
+</glossary> \ No newline at end of file
diff --git a/doc/p11-kit-docs.sgml b/doc/p11-kit-docs.sgml
index 5627f6f..7138690 100644
--- a/doc/p11-kit-docs.sgml
+++ b/doc/p11-kit-docs.sgml
@@ -27,10 +27,13 @@
<xi:include href="xml/p11-kit-pin.xml"/>
<xi:include href="xml/p11-kit-util.xml"/>
<xi:include href="xml/p11-kit-future.xml"/>
+
<index id="api-index-full">
<title>API Index</title>
<xi:include href="xml/api-index-full.xml"><xi:fallback /></xi:include>
</index>
+
+ <xi:include href="annotation-glossary.xml"/>
</chapter>
<xi:include href="p11-kit-devel.xml"/>
diff --git a/doc/p11-kit-sections.txt b/doc/p11-kit-sections.txt
index c6a4686..dc85f2d 100644
--- a/doc/p11-kit-sections.txt
+++ b/doc/p11-kit-sections.txt
@@ -86,4 +86,21 @@ p11_kit_set_progname
p11_kit_be_quiet
p11_kit_be_loud
p11_kit_message
+p11_kit_destroyer
+P11KitIter
+p11_kit_iter_new
+p11_kit_iter_set_session_flags
+p11_kit_iter_add_callback
+p11_kit_iter_callback
+p11_kit_iter_add_filter
+p11_kit_iter_begin
+p11_kit_iter_begin_with
+p11_kit_iter_next
+p11_kit_iter_get_module
+p11_kit_iter_get_slot
+p11_kit_iter_get_session
+p11_kit_iter_keep_session
+p11_kit_iter_get_object
+p11_kit_iter_load_attributes
+p11_kit_iter_free
</SECTION>
diff --git a/p11-kit/Makefile.am b/p11-kit/Makefile.am
index 650fe44..776d337 100644
--- a/p11-kit/Makefile.am
+++ b/p11-kit/Makefile.am
@@ -13,6 +13,7 @@ INCLUDES = \
incdir = $(includedir)/p11-kit-1/p11-kit
inc_HEADERS = \
+ iter.h \
p11-kit.h \
pin.h \
uri.h \
@@ -21,6 +22,7 @@ inc_HEADERS = \
MODULE_SRCS = \
util.c \
conf.c conf.h \
+ iter.c \
modules.c \
pkcs11.h \
pin.c \
diff --git a/p11-kit/iter.c b/p11-kit/iter.c
new file mode 100644
index 0000000..4a4dbd3
--- /dev/null
+++ b/p11-kit/iter.c
@@ -0,0 +1,829 @@
+/*
+ * Copyright (C) 2013 Red Hat Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ * * The names of contributors to this software may not be
+ * used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Author: Stef Walter <stefw@redhat.com>
+ */
+
+#include "config.h"
+
+#include "array.h"
+#include "attrs.h"
+#include "debug.h"
+#include "iter.h"
+#include "pin.h"
+#include "private.h"
+
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+typedef struct _Callback {
+ p11_kit_iter_callback func;
+ void *callback_data;
+ p11_kit_destroyer destroyer;
+ struct _Callback *next;
+} Callback;
+
+#define MAX_OBJECTS 64
+
+/**
+ * P11KitIter:
+ *
+ * Used to iterate over PKCS\#11 objects.
+ */
+typedef struct p11_kit_iter {
+
+ /* Iterator matching data */
+ CK_INFO match_module;
+ CK_TOKEN_INFO match_token;
+ CK_ATTRIBUTE *match_attrs;
+ Callback *callbacks;
+ CK_FLAGS session_flags;
+
+ /* The input modules */
+ p11_array *modules;
+
+ /* The results of C_GetSlotList */
+ CK_SLOT_ID *slots;
+ CK_ULONG num_slots;
+ CK_ULONG saw_slots;
+
+ /* The results of C_FindObjects */
+ CK_OBJECT_HANDLE objects[MAX_OBJECTS];
+ CK_ULONG num_objects;
+ CK_ULONG saw_objects;
+
+ /* The current iteration */
+ CK_FUNCTION_LIST_PTR module;
+ CK_SLOT_ID slot;
+ CK_SESSION_HANDLE session;
+ CK_OBJECT_HANDLE object;
+
+ /* And various flags */
+ int searching : 1;
+ int searched : 1;
+ int iterating : 1;
+ int match_nothing : 1;
+ int keep_session : 1;
+} P11KitIter;
+
+/**
+ * p11_kit_iter_new:
+ * @uri: (allow-none): a PKCS\#11 URI to filter on, or %NULL
+ *
+ * Create a new PKCS\#11 iterator for iterating over objects. Only
+ * objects that match the @uri will be returned by the iterator.
+ * Relevant information in @uri is copied, and you need not keep
+ * @uri around.
+ *
+ * If no @uri is specified then the iterator will iterate over all
+ * objects, unless otherwise filtered.
+ *
+ * Returns: (transfer full): a new iterator, which should be freed
+ * with p11_kit_iter_free()
+ */
+P11KitIter *
+p11_kit_iter_new (P11KitUri *uri)
+{
+ P11KitIter *iter;
+ CK_ATTRIBUTE *attrs;
+ CK_TOKEN_INFO *tinfo;
+ CK_INFO *minfo;
+ CK_ULONG count;
+
+ iter = calloc (1, sizeof (P11KitIter));
+ return_val_if_fail (iter != NULL, NULL);
+
+ iter->modules = p11_array_new (NULL);
+ return_val_if_fail (iter->modules != NULL, NULL);
+
+ if (uri != NULL) {
+
+ if (p11_kit_uri_any_unrecognized (uri)) {
+ iter->match_nothing = 1;
+
+ } else {
+ attrs = p11_kit_uri_get_attributes (uri, &count);
+ iter->match_attrs = p11_attrs_buildn (NULL, attrs, count);
+
+ minfo = p11_kit_uri_get_module_info (uri);
+ if (minfo != NULL)
+ memcpy (&iter->match_module, minfo, sizeof (CK_INFO));
+
+ tinfo = p11_kit_uri_get_token_info (uri);
+ if (tinfo != NULL)
+ memcpy (&iter->match_token, tinfo, sizeof (CK_TOKEN_INFO));
+ }
+ } else {
+ /* Match any module version number*/
+ iter->match_module.libraryVersion.major = (CK_BYTE)-1;
+ iter->match_module.libraryVersion.minor = (CK_BYTE)-1;
+ }
+
+ iter->session_flags = CKF_SERIAL_SESSION;
+
+ return iter;
+}
+
+/**
+ * p11_kit_iter_set_session_flags:
+ * @iter: the iterator
+ * @flags: set of session flags
+ *
+ * Set the PKCS\#11 session flags to be used when the iterator opens
+ * new sessions.
+ */
+void
+p11_kit_iter_set_session_flags (P11KitIter *iter,
+ CK_FLAGS flags)
+{
+ return_if_fail (iter != NULL);
+ return_if_fail (!iter->iterating);
+ iter->session_flags = flags | CKF_SERIAL_SESSION;
+}
+
+/**
+ * p11_kit_destroyer:
+ * @data: data to destroy
+ *
+ * A callback called to free a resource.
+ */
+
+/**
+ * p11_kit_iter_callback:
+ * @iter: the iterator
+ * @matches: (out): whether to match the current object
+ * @data: callback data
+ *
+ * A callback setup with p11_kit_iter_add_callback(). This callback is
+ * called for each object iterated.
+ *
+ * If the callback sets @matches to CK_FALSE, then this object is
+ * skipped and not matched by p11_kit_iter_next(). If you return
+ * anything but CKR_OK, then the iteration is stopped, and
+ * p11_kit_iter_next() returns the result code.
+ *
+ * Returns: CKR_OK to continue iterating, CKR_CANCEL to stop, or
+ * anything else to fail
+ */
+
+/**
+ * p11_kit_iter_add_callback:
+ * @iter: the iterator
+ * @callback: a function to call for each iteration
+ * @callback_data: (allow-none): data to pass to the function
+ * @callback_destroy: (allow-none): used to cleanup the data
+ *
+ * Adds a callback to the iterator which will be called each time
+ * that an object is iterated.
+ *
+ * These callbacks can also perform filtering. If any callback
+ * indicates through it's <literal>matches</literal> argument that
+ * the object should not match, then that object will not be iterated
+ * as far as p11_kit_iter_next() is concerned.
+ */
+void
+p11_kit_iter_add_callback (P11KitIter *iter,
+ p11_kit_iter_callback callback,
+ void *callback_data,
+ p11_kit_destroyer callback_destroy)
+{
+ Callback *cb;
+
+ return_if_fail (iter != NULL);
+ return_if_fail (callback != NULL);
+
+ cb = calloc (1, sizeof (Callback));
+ return_if_fail (cb != NULL);
+
+ cb->func = callback;
+ cb->destroyer = callback_destroy;
+ cb->callback_data = callback_data;
+ cb->next = iter->callbacks;
+ iter->callbacks = cb;
+}
+
+/**
+ * p11_kit_iter_add_filter:
+ * @iter: the iterator
+ * @matching: (array length=count): the attributes that the objects should match
+ * @count: the number of attributes
+ *
+ * Add a filter to limit the objects that the iterator iterates over.
+ *
+ * Only objects matching the passed in attributes will be iterated.
+ * This function can be called multiple times.
+ *
+ * The @matching attributes are copied.
+ */
+void
+p11_kit_iter_add_filter (P11KitIter *iter,
+ CK_ATTRIBUTE *matching,
+ CK_ULONG count)
+{
+ return_if_fail (iter != NULL);
+ return_if_fail (!iter->iterating);
+
+ iter->match_attrs = p11_attrs_buildn (iter->match_attrs, matching, count);
+ return_if_fail (iter->match_attrs != NULL);
+}
+
+static void
+finish_object (P11KitIter *iter)
+{
+ iter->object = 0;
+}
+
+static void
+finish_slot (P11KitIter *iter)
+{
+ if (iter->session && !iter->keep_session) {
+ assert (iter->module != NULL);
+ (iter->module->C_CloseSession) (iter->session);
+ }
+
+ iter->keep_session = 0;
+ iter->session = 0;
+ iter->searched = 0;
+ iter->searching = 0;
+ iter->slot = 0;
+}
+
+static void
+finish_module (P11KitIter *iter)
+{
+ iter->num_slots = 0;
+ iter->saw_slots = 0;
+ iter->module = NULL;
+}
+
+static CK_RV
+finish_iterating (P11KitIter *iter,
+ CK_RV rv)
+{
+ finish_object (iter);
+ finish_slot (iter);
+ finish_module (iter);
+ p11_array_clear (iter->modules);
+
+ iter->iterating = 0;
+ return rv;
+}
+
+/**
+ * p11_kit_iter_begin:
+ * @iter: the iterator
+ * @modules: (array zero-terminated=1): null-terminated list of
+ * modules to iterate over
+ *
+ * Begin iterating PKCS\#11 objects in the given @modules.
+ *
+ * The @modules arguments should be a null-terminated list of
+ * pointers to the modules' PKCS\#11 function pointers.
+ *
+ * For each module, all initialized slots will be iterated over,
+ * having sessions opened for each of them in turn, and searched
+ * for objects matching the search criteria.
+ */
+void
+p11_kit_iter_begin (P11KitIter *iter,
+ CK_FUNCTION_LIST_PTR *modules)
+{
+ int i;
+
+ return_if_fail (modules != NULL);
+
+ finish_iterating (iter, CKR_OK);
+
+ /* Use this module */
+ for (i = 0; modules[i] != NULL; i++) {
+ if (!p11_array_push (iter->modules, modules[i]))
+ return_if_reached ();
+ }
+
+ iter->iterating = 1;
+ iter->searched = 1;
+}
+
+/**
+ * p11_kit_iter_begin_with:
+ * @iter: the iterator
+ * @module: the module to iterate over
+ * @slot: (allow-none): the slot to iterate objects in, or zero
+ * @session: (allow-none): the session to search for objects on, or zero
+ *
+ * Begin iterating PKCS\#11 objects in the given @module.
+ *
+ * If @slot is non-zero then the iteration will be limited to that
+ * slot.
+ *
+ * If @session is non-zero then the iteration will be limited to
+ * objects visible through that session, which implies that they
+ * are also limited to the slot which the session was opened for.
+ */
+void
+p11_kit_iter_begin_with (P11KitIter *iter,
+ CK_FUNCTION_LIST_PTR module,
+ CK_SLOT_ID slot,
+ CK_SESSION_HANDLE session)
+{
+ CK_SESSION_INFO info;
+ CK_RV rv;
+
+ finish_iterating (iter, CKR_OK);
+
+ return_if_fail (module != NULL);
+
+ if (session != 0) {
+ /*
+ * A currently active session. Initialize as if we're ready
+ * to search using this session.
+ */
+
+ /* If we have a session, but no slot, then look it up */
+ if (slot == 0) {
+ assert (module != NULL);
+ rv = (module->C_GetSessionInfo) (session, &info);
+ if (rv == CKR_OK)
+ slot = info.slotID;
+ }
+
+ /* So initialize as if we're ready to search */
+ iter->session = session;
+ iter->slot = slot;
+ iter->module = module;
+ iter->keep_session = 1;
+
+ } else if (slot != 0) {
+
+ /*
+ * Limit to this slot. Initialize as if we're ready to use the
+ * slot from the slots list.
+ */
+
+ iter->module = module;
+ iter->slots = realloc (iter->slots, sizeof (CK_SLOT_ID));
+ return_if_fail (iter->slots != NULL);
+ iter->slots[0] = slot;
+ iter->num_slots = 1;
+ iter->searched = 1;
+
+ } else {
+
+ /*
+ * Limit to this module. Initialize as if we're ready to use
+ * the module from the modules array.
+ */
+
+ assert (module != NULL);
+ p11_array_push (iter->modules, module);
+ iter->session = 0;
+ iter->slot = 0;
+ iter->searched = 1;
+ }
+
+ iter->iterating = 1;
+}
+
+static CK_RV
+call_all_filters (P11KitIter *iter,
+ CK_BBOOL *matches)
+{
+ Callback *cb;
+ CK_RV rv;
+
+ *matches = CK_TRUE;
+
+ for (cb = iter->callbacks; cb != NULL; cb = cb->next) {
+ rv = (cb->func) (iter, matches, cb->callback_data);
+ if (rv != CKR_OK || !*matches)
+ return rv;
+ }
+
+ return CKR_OK;
+}
+
+static CK_RV
+move_next_session (P11KitIter *iter)
+{
+ CK_TOKEN_INFO tinfo;
+ CK_ULONG num_slots;
+ CK_INFO minfo;
+ CK_RV rv;
+
+ finish_slot (iter);
+
+ /* If we have no more slots, then move to next module */
+ while (iter->saw_slots >= iter->num_slots) {
+ finish_module (iter);
+
+ /* Iter is finished */
+ if (iter->modules->num == 0)
+ return finish_iterating (iter, CKR_CANCEL);
+
+ iter->module = iter->modules->elem[0];
+ p11_array_remove (iter->modules, 0);
+
+ /* Skip module if it doesn't match uri */
+ assert (iter->module != NULL);
+ rv = (iter->module->C_GetInfo) (&minfo);
+ if (rv != CKR_OK || !p11_match_uri_module_info (&iter->match_module, &minfo))
+ continue;
+
+ rv = (iter->module->C_GetSlotList) (CK_TRUE, NULL, &num_slots);
+ if (rv != CKR_OK)
+ return finish_iterating (iter, rv);
+
+ iter->slots = realloc (iter->slots, sizeof (CK_SLOT_ID) * (num_slots + 1));
+ return_val_if_fail (iter->slots != NULL, CKR_HOST_MEMORY);
+
+ rv = (iter->module->C_GetSlotList) (CK_TRUE, iter->slots, &num_slots);
+ if (rv != CKR_OK)
+ return finish_iterating (iter, rv);
+
+ iter->num_slots = num_slots;
+ assert (iter->saw_slots == 0);
+ }
+
+ /* Move to the next slot, and open a session on it */
+ while (iter->saw_slots < iter->num_slots) {
+ iter->slot = iter->slots[iter->saw_slots++];
+
+ assert (iter->module != NULL);
+ rv = (iter->module->C_GetTokenInfo) (iter->slot, &tinfo);
+ if (rv != CKR_OK || !p11_match_uri_token_info (&iter->match_token, &tinfo))
+ continue;
+
+ /* Token is not initialized, we're not going to get further, so skip */
+ if (!(tinfo.flags & CKF_TOKEN_INITIALIZED))
+ continue;
+
+ rv = (iter->module->C_OpenSession) (iter->slot, iter->session_flags,
+ NULL, NULL, &iter->session);
+ if (rv != CKR_OK)
+ return finish_iterating (iter, rv);
+
+ if (iter->session != 0)
+ return CKR_OK;
+ }
+
+ /* Otherwise try again */
+ return move_next_session (iter);
+}
+
+/**
+ * p11_kit_iter_next:
+ * @iter: the iterator
+ *
+ * Iterate to the next matching object.
+ *
+ * To access the object, session and so on, use the p11_kit_iter_get_object(),
+ * p11_kit_iter_get_session(), and p11_kit_iter_get_module() functions.
+ *
+ * This call must only be called after either p11_kit_iter_begin()
+ * or p11_kit_iter_begin_with() have been called.
+ *
+ * Objects which are skipped by callbacks will not be returned here
+ * as matching objects.
+ *
+ * Returns: CKR_OK if an object matched, CKR_CANCEL if no more objects, or another error
+ */
+CK_RV
+p11_kit_iter_next (P11KitIter *iter)
+{
+ CK_ULONG count;
+ CK_BBOOL matches;
+ CK_RV rv;
+
+ return_val_if_fail (iter->iterating, CKR_OPERATION_NOT_INITIALIZED);
+
+ iter->object = 0;
+
+ if (iter->match_nothing)
+ return finish_iterating (iter, CKR_CANCEL);
+
+ /*
+ * If we have outstanding objects, then iterate one through those
+ * Note that we pass each object through the filters, and only
+ * assume it's iterated if it matches
+ */
+ while (iter->saw_objects < iter->num_objects) {
+ iter->object = iter->objects[iter->saw_objects++];
+
+ rv = call_all_filters (iter, &matches);
+ if (rv != CKR_OK)
+ return finish_iterating (iter, rv);
+
+ if (matches)
+ return CKR_OK;
+ }
+
+ /* If we have finished searching then move to next session */
+ if (iter->searched) {
+ rv = move_next_session (iter);
+ if (rv != CKR_OK)
+ return finish_iterating (iter, rv);
+ }
+
+ /* Ready to start searching */
+ if (!iter->searching && !iter->searched) {
+ count = p11_attrs_count (iter->match_attrs);
+ rv = (iter->module->C_FindObjectsInit) (iter->session, iter->match_attrs, count);
+ if (rv != CKR_OK)
+ return finish_iterating (iter, rv);
+ iter->searching = 1;
+ iter->searched = 0;
+ }
+
+ /* If we have searched on this session then try to continue */
+ if (iter->searching) {
+ assert (iter->module != NULL);
+ assert (iter->session != 0);
+ iter->num_objects = 0;
+ iter->saw_objects = 0;
+
+ rv = (iter->module->C_FindObjects) (iter->session, iter->objects,
+ MAX_OBJECTS, &iter->num_objects);
+ if (rv != CKR_OK)
+ return finish_iterating (iter, rv);
+
+ /*
+ * Done searching on this session, although there are still
+ * objects outstanding, which will be returned on next
+ * iterations.
+ */
+ if (iter->num_objects != MAX_OBJECTS) {
+ iter->searching = 0;
+ iter->searched = 1;
+ (iter->module->C_FindObjectsFinal) (iter->session);
+ }
+ }
+
+ /* Try again */
+ return p11_kit_iter_next (iter);
+}
+
+/**
+ * p11_kit_iter_get_module:
+ * @iter: the iterator
+ *
+ * Get the module function pointers for the current matching object.
+ *
+ * This can only be called after p11_kit_iter_next() succeeds.
+ *
+ * Returns: the module which the current matching object is in
+ */
+CK_FUNCTION_LIST_PTR
+p11_kit_iter_get_module (P11KitIter *iter)
+{
+ return_val_if_fail (iter != NULL, NULL);
+ return_val_if_fail (iter->iterating, 0);
+ return iter->module;
+}
+
+/**
+ * p11_kit_iter_get_slot:
+ * @iter: the iterator
+ *
+ * Get the slot which the current matching object is on.
+ *
+ * This can only be called after p11_kit_iter_next() succeeds.
+ *
+ * Returns: the slot of the current matching object
+ */
+CK_SLOT_ID
+p11_kit_iter_get_slot (P11KitIter *iter)
+{
+ return_val_if_fail (iter != NULL, 0);
+ return_val_if_fail (iter->iterating, 0);
+ return iter->slot;
+}
+
+/**
+ * p11_kit_iter_get_session:
+ * @iter: the iterator
+ *
+ * Get the session which the current matching object is acessible
+ * through.
+ *
+ * This can only be called after p11_kit_iter_next() succeeds.
+ *
+ * The session may be closed after the next p11_kit_iter_next() call
+ * unless p11_kit_iter_keep_session() is called.
+ *
+ * Returns: the slot of the current matching object
+ */
+CK_SESSION_HANDLE
+p11_kit_iter_get_session (P11KitIter *iter)
+{
+ return_val_if_fail (iter != NULL, 0);
+ return_val_if_fail (iter->iterating, 0);
+ return iter->session;
+}
+
+/**
+ * p11_kit_iter_get_object:
+ * @iter: the iterator
+ *
+ * Get the current matching object.
+ *
+ * This can only be called after p11_kit_iter_next() succeeds.
+ *
+ * Returns: the current matching object
+ */
+CK_OBJECT_HANDLE
+p11_kit_iter_get_object (P11KitIter *iter)
+{
+ return_val_if_fail (iter != NULL, 0);
+ return iter->object;
+}
+
+/**
+ * p11_kit_iter_load_attributes:
+ * @iter: the iterator
+ * @template: (array length=count) (inout): the attributes to load
+ * @count: the number of attributes
+ *
+ * Retrieve attributes for the current matching object.
+ *
+ * Each attribute in the array will be filled in with the value
+ * of that attribute retrieved from the object. After use the
+ * attribute value memory pointed to by the <literal>pValue</literal>
+ * of each attribute should be freed with the <literal>free<!-- -->()</literal>
+ * function.
+ *
+ * If the <literal>pValue</literal> of an attribute is not %NULL passed
+ * to this function, then it will be passed to
+ * <literal>realloc<!-- -->()</literal> to allocate the correct amount
+ * of space for the attribute value.
+ *
+ * If any attribute is not present on the object, or is sensitive and
+ * cannot be retrieved, then the <literal>pValue</literal> will be NULL.
+ * If <literal>pValue</literal> was not %NULL when passed to this function
+ * then it will be freed with <literal>free<!-- -->()</literal>. In these
+ * cases <literal>CKR_OK</literal> is returned.
+ *
+ * This can only be called after p11_kit_iter_next() succeeds.
+ *
+ * Returns: CKR_OK or a failure code
+ */
+CK_RV
+p11_kit_iter_load_attributes (P11KitIter *iter,
+ CK_ATTRIBUTE *template,
+ CK_ULONG count)
+{
+ CK_ATTRIBUTE *original = NULL;
+ CK_ULONG i;
+ CK_RV rv;
+
+ return_val_if_fail (iter != NULL, CKR_GENERAL_ERROR);
+ return_val_if_fail (iter->iterating, CKR_GENERAL_ERROR);
+ return_val_if_fail (iter->module != NULL, CKR_GENERAL_ERROR);
+ return_val_if_fail (iter->session != 0, CKR_GENERAL_ERROR);
+ return_val_if_fail (iter->object != 0, CKR_GENERAL_ERROR);
+
+ if (count == 0)
+ return CKR_OK;
+
+ original = memdup (template, count * sizeof (CK_ATTRIBUTE));
+ return_val_if_fail (original != NULL, CKR_HOST_MEMORY);
+
+ for (i = 0; i < count; i++)
+ template[i].pValue = NULL;
+
+ rv = (iter->module->C_GetAttributeValue) (iter->session, iter->object, template, count);
+
+ switch (rv) {
+ case CKR_OK:
+ case CKR_ATTRIBUTE_TYPE_INVALID:
+ case CKR_ATTRIBUTE_SENSITIVE:
+ case CKR_BUFFER_TOO_SMALL:
+ break;
+ default:
+ free (original);
+ return rv;
+ }
+
+ for (i = 0; i < count; i++) {
+ if (template[i].ulValueLen == (CK_ULONG)-1 ||
+ template[i].ulValueLen == 0) {
+ free (original[i].pValue);
+
+ } else if (original[i].pValue != NULL &&
+ template[i].ulValueLen == original[i].ulValueLen) {
+ template[i].pValue = original[i].pValue;
+
+ } else {
+ template[i].pValue = realloc (original[i].pValue, template[i].ulValueLen);
+ return_val_if_fail (template[i].pValue != NULL, 0);
+ }
+ }
+
+ free (original);
+
+ rv = (iter->module->C_GetAttributeValue) (iter->session, iter->object, template, count);
+
+ switch (rv) {
+ case CKR_OK:
+ case CKR_ATTRIBUTE_TYPE_INVALID:
+ case CKR_ATTRIBUTE_SENSITIVE:
+ rv = CKR_OK;
+ break;
+ default:
+ return_val_if_fail (rv != CKR_BUFFER_TOO_SMALL, rv);
+ return rv;
+ }
+
+ for (i = 0; i < count; i++) {
+ if (template[i].ulValueLen == (CK_ULONG)-1 ||
+ template[i].ulValueLen == 0) {
+ free (template[i].pValue);
+ template[i].pValue = NULL;
+ }
+ }
+
+ return rv;
+}
+
+/**
+ * p11_kit_iter_keep_session:
+ * @iter: the iterator
+ *
+ * After calling this function the session open for iterating
+ * the current object will not be automatically closed by
+ * the iterator after later calls to p11_kit_iter_next() or
+ * p11_kit_iter_free().
+ *
+ * It is the callers responsibility to close this session,
+ * after the iterator has been freed. The session may still be
+ * used by the iterator if further iterations are performed.
+ *
+ * This can only be called after p11_kit_iter_next() succeeds.
+ *
+ * Returns: the current session
+ */
+CK_SESSION_HANDLE
+p11_kit_iter_keep_session (P11KitIter *iter)
+{
+ return_val_if_fail (iter != NULL, 0);
+ return_val_if_fail (iter->iterating, 0);
+ return_val_if_fail (iter->session != 0, 0);
+
+ iter->keep_session = 1;
+ return iter->session;
+}
+
+/**
+ * p11_kit_iter_free:
+ * @iter: the iterator
+ *
+ * Frees the iterator and all resources, such as sessions
+ * or callbacks held by the iterator.
+ */
+void
+p11_kit_iter_free (P11KitIter *iter)
+{
+ Callback *cb, *next;
+
+ if (iter == NULL)
+ return;
+
+ finish_iterating (iter, CKR_OK);
+ p11_array_free (iter->modules);
+ p11_attrs_free (iter->match_attrs);
+
+ for (cb = iter->callbacks; cb != NULL; cb = next) {
+ next = cb->next;
+ if (cb->destroyer)
+ (cb->destroyer) (cb->callback_data);
+ free (cb);
+ }
+}
diff --git a/p11-kit/iter.h b/p11-kit/iter.h
new file mode 100644
index 0000000..2b87273
--- /dev/null
+++ b/p11-kit/iter.h
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 2013 Red Hat, Inc
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ * * The names of contributors to this software may not be
+ * used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Author: Stef Walter <stefw@redhat.com>
+ */
+
+#ifndef P11_KIT_ITER_H
+#define P11_KIT_ITER_H
+
+#include "p11-kit/p11-kit.h"
+#include "p11-kit/pkcs11.h"
+#include "p11-kit/uri.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifdef P11_KIT_FUTURE_UNSTABLE_API
+
+typedef struct p11_kit_iter P11KitIter;
+
+typedef CK_RV (* p11_kit_iter_callback) (P11KitIter *iter,
+ CK_BBOOL *matches,
+ void *data);
+
+P11KitIter * p11_kit_iter_new (P11KitUri *uri);
+
+void p11_kit_iter_free (P11KitIter *iter);
+
+void p11_kit_iter_set_session_flags (P11KitIter *iter,
+ CK_FLAGS flags);
+
+void p11_kit_iter_add_callback (P11KitIter *iter,
+ p11_kit_iter_callback callback,
+ void *callback_data,
+ p11_kit_destroyer callback_destroy);
+
+void p11_kit_iter_add_filter (P11KitIter *iter,
+ CK_ATTRIBUTE *matching,
+ CK_ULONG count);
+
+void p11_kit_iter_begin (P11KitIter *iter,
+ CK_FUNCTION_LIST_PTR *modules);
+
+void p11_kit_iter_begin_with (P11KitIter *iter,
+ CK_FUNCTION_LIST_PTR module,
+ CK_SLOT_ID slot,
+ CK_SESSION_HANDLE session);
+
+CK_RV p11_kit_iter_next (P11KitIter *iter);
+
+CK_FUNCTION_LIST_PTR p11_kit_iter_get_module (P11KitIter *iter);
+
+CK_SLOT_ID p11_kit_iter_get_slot (P11KitIter *iter);
+
+CK_SESSION_HANDLE p11_kit_iter_get_session (P11KitIter *iter);
+
+CK_OBJECT_HANDLE p11_kit_iter_get_object (P11KitIter *iter);
+
+CK_RV p11_kit_iter_load_attributes (P11KitIter *iter,
+ CK_ATTRIBUTE *template,
+ CK_ULONG count);
+
+CK_SESSION_HANDLE p11_kit_iter_keep_session (P11KitIter *iter);
+
+
+#endif /* P11_KIT_FUTURE_UNSTABLE_API */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* P11_KIT_ITER_H */
diff --git a/p11-kit/p11-kit.h b/p11-kit/p11-kit.h
index 042ed44..f83cbd0 100644
--- a/p11-kit/p11-kit.h
+++ b/p11-kit/p11-kit.h
@@ -88,6 +88,8 @@ void p11_kit_be_loud (void);
const char* p11_kit_message (void);
+typedef void (* p11_kit_destroyer) (void *data);
+
#endif
#ifdef __cplusplus
diff --git a/p11-kit/tests/Makefile.am b/p11-kit/tests/Makefile.am
index e39ac8f..a6c9fac 100644
--- a/p11-kit/tests/Makefile.am
+++ b/p11-kit/tests/Makefile.am
@@ -23,7 +23,9 @@ CHECK_PROGS = \
uri-test \
pin-test \
test-init \
- test-modules
+ test-modules \
+ test-iter \
+ $(NULL)
noinst_PROGRAMS = \
print-messages \
diff --git a/p11-kit/tests/mock-module-ep.c b/p11-kit/tests/mock-module-ep.c
index 89b31f6..9ba739a 100644
--- a/p11-kit/tests/mock-module-ep.c
+++ b/p11-kit/tests/mock-module-ep.c
@@ -49,6 +49,6 @@ C_GetFunctionList (CK_FUNCTION_LIST_PTR_PTR list)
mock_module_no_slots.C_GetFunctionList = C_GetFunctionList;
if (list == NULL)
return CKR_ARGUMENTS_BAD;
- *list = &mock_module_no_slots;
+ *list = &mock_module;
return CKR_OK;
}
diff --git a/p11-kit/tests/test-iter.c b/p11-kit/tests/test-iter.c
new file mode 100644
index 0000000..31f25e4
--- /dev/null
+++ b/p11-kit/tests/test-iter.c
@@ -0,0 +1,1140 @@
+/*
+ * Copyright (c) 2013, Red Hat Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * * Redistributions of source code must retain the above
+ * copyright notice, this list of conditions and the
+ * following disclaimer.
+ * * Redistributions in binary form must reproduce the
+ * above copyright notice, this list of conditions and
+ * the following disclaimer in the documentation and/or
+ * other materials provided with the distribution.
+ * * The names of contributors to this software may not be
+ * used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
+ * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ *
+ * Author: Stef Walter <stefw@collabora.co.uk>
+ */
+
+#include "config.h"
+#include "CuTest.h"
+
+#define P11_KIT_FUTURE_UNSTABLE_API 1
+
+#include "attrs.h"
+#include "iter.h"
+#include "library.h"
+#include "mock.h"
+
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+static CK_FUNCTION_LIST_PTR_PTR
+initialize_and_get_modules (CuTest *tc)
+{
+ CK_FUNCTION_LIST_PTR_PTR modules;
+ CK_RV rv;
+
+ p11_message_quiet ();
+
+ rv = p11_kit_initialize_registered ();
+ CuAssertIntEquals (tc, CKR_OK, rv);
+ modules = p11_kit_registered_modules ();
+ CuAssertTrue (tc, modules != NULL && modules[0] != NULL);
+
+ p11_message_loud ();
+
+ return modules;
+}
+
+static void
+finalize_and_free_modules (CuTest *tc,
+ CK_FUNCTION_LIST_PTR_PTR modules)
+{
+ CK_RV rv;
+
+ free (modules);
+ rv = p11_kit_finalize_registered ();
+ CuAssertIntEquals (tc, CKR_OK, rv);
+}
+
+static int
+has_handle (CK_ULONG *objects,
+ int count,
+ CK_ULONG handle)
+{
+ int i;
+ for (i = 0; i < count; i++) {
+ if (objects[i] == handle)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static void
+test_all (CuTest *tc)
+{
+ CK_OBJECT_HANDLE objects[128];
+ CK_FUNCTION_LIST_PTR *modules;
+ CK_FUNCTION_LIST_PTR module;
+ CK_SESSION_HANDLE session;
+ CK_ULONG size;
+ P11KitIter *iter;
+ CK_RV rv;
+ int at;
+
+ modules = initialize_and_get_modules (tc);
+
+ iter = p11_kit_iter_new (NULL);
+ p11_kit_iter_begin (iter, modules);
+
+ at = 0;
+ while ((rv = p11_kit_iter_next (iter)) == CKR_OK) {
+ CuAssertTrue (tc, at < 128);
+ objects[at] = p11_kit_iter_get_object (iter);
+
+ module = p11_kit_iter_get_module (iter);
+ CuAssertPtrNotNull (tc, module);
+
+ session = p11_kit_iter_get_session (iter);
+ CuAssertTrue (tc, session != 0);
+
+ /* Do something with the object */
+ size = 0;
+ rv = (module->C_GetObjectSize) (session, objects[at], &size);
+ CuAssertTrue (tc, rv == CKR_OK);
+ CuAssertTrue (tc, size > 0);
+
+ at++;
+ }
+
+ CuAssertTrue (tc, rv == CKR_CANCEL);
+
+ /* Three modules, each with 1 slot, and 3 public objects */
+ CuAssertIntEquals (tc, 9, at);
+
+ CuAssertTrue (tc, has_handle (objects, at, MOCK_DATA_OBJECT));
+ CuAssertTrue (tc, !has_handle (objects, at, MOCK_PRIVATE_KEY_CAPITALIZE));
+ CuAssertTrue (tc, has_handle (objects, at, MOCK_PUBLIC_KEY_CAPITALIZE));
+ CuAssertTrue (tc, !has_handle (objects, at, MOCK_PRIVATE_KEY_PREFIX));
+ CuAssertTrue (tc, has_handle (objects, at, MOCK_PUBLIC_KEY_PREFIX));
+
+ p11_kit_iter_free (iter);
+
+ finalize_and_free_modules (tc, modules);
+}
+
+static CK_RV
+on_iter_callback (P11KitIter *iter,
+ CK_BBOOL *matches,
+ void *data)
+{
+ CuTest *tc = data;
+ CK_OBJECT_HANDLE object;
+ CK_FUNCTION_LIST_PTR module;
+ CK_SESSION_HANDLE session;
+ CK_ULONG size;
+ CK_RV rv;
+
+ object = p11_kit_iter_get_object (iter);
+ if (object != MOCK_PUBLIC_KEY_CAPITALIZE && object != MOCK_PUBLIC_KEY_PREFIX) {
+ *matches = CK_FALSE;
+ return CKR_OK;
+ }
+
+ module = p11_kit_iter_get_module (iter);
+ CuAssertPtrNotNull (tc, module);
+
+ session = p11_kit_iter_get_session (iter);
+ CuAssertTrue (tc, session != 0);
+
+ /* Do something with the object */
+ size = 0;
+ rv = (module->C_GetObjectSize) (session, object, &size);
+ CuAssertTrue (tc, rv == CKR_OK);
+ CuAssertTrue (tc, size > 0);
+
+ return CKR_OK;
+}
+
+static void
+test_callback (CuTest *tc)
+{
+ CK_OBJECT_HANDLE objects[128];
+ CK_FUNCTION_LIST_PTR *modules;
+ P11KitIter *iter;
+ CK_RV rv;
+ int at;
+
+ modules = initialize_and_get_modules (tc);
+
+ iter = p11_kit_iter_new (NULL);
+ p11_kit_iter_add_callback (iter, on_iter_callback, tc, NULL);
+ p11_kit_iter_begin (iter, modules);
+
+ at= 0;
+ while ((rv = p11_kit_iter_next (iter)) == CKR_OK) {
+ CuAssertTrue (tc, at < 128);
+ objects[at] = p11_kit_iter_get_object (iter);
+ at++;
+ }
+
+ CuAssertTrue (tc, rv == CKR_CANCEL);
+
+ /* Three modules, each with 1 slot, and 2 public keys */
+ CuAssertIntEquals (tc, 6, at);
+
+ CuAssertTrue (tc, !has_handle (objects, at, MOCK_DATA_OBJECT));
+ CuAssertTrue (tc, !has_handle (objects, at, MOCK_PRIVATE_KEY_CAPITALIZE));
+ CuAssertTrue (tc, has_handle (objects, at, MOCK_PUBLIC_KEY_CAPITALIZE));
+ CuAssertTrue (tc, !has_handle (objects, at, MOCK_PRIVATE_KEY_PREFIX));
+ CuAssertTrue (tc, has_handle (objects, at, MOCK_PUBLIC_KEY_PREFIX));
+
+ p11_kit_iter_free (iter);
+
+ finalize_and_free_modules (tc, modules);
+}
+
+static CK_RV
+on_callback_fail (P11KitIter *iter,
+ CK_BBOOL *matches,
+ void *data)
+{
+ return CKR_DATA_INVALID;
+}
+
+static void
+test_callback_fails (CuTest *tc)
+{
+ CK_FUNCTION_LIST_PTR *modules;
+ P11KitIter *iter;
+ CK_RV rv;
+ int at;
+
+ modules = initialize_and_get_modules (tc);
+
+ iter = p11_kit_iter_new (NULL);
+ p11_kit_iter_add_callback (iter, on_callback_fail, tc, NULL);
+ p11_kit_iter_begin (iter, modules);
+
+ at= 0;
+ while ((rv = p11_kit_iter_next (iter)) == CKR_OK)
+ at++;
+
+ CuAssertTrue (tc, rv == CKR_DATA_INVALID);
+
+ /* Shouldn't have succeeded at all */
+ CuAssertIntEquals (tc, 0, at);
+
+ p11_kit_iter_free (iter);
+ finalize_and_free_modules (tc, modules);
+}
+
+static void
+on_destroy_increment (void *data)
+{
+ int *value = data;
+ (*value)++;
+}
+
+static void
+test_callback_destroyer (CuTest *tc)
+{
+ P11KitIter *iter;
+ int value = 1;
+
+ iter = p11_kit_iter_new (NULL);
+ p11_kit_iter_add_callback (iter, on_callback_fail, &value, on_destroy_increment);
+ p11_kit_iter_free (iter);
+
+ CuAssertIntEquals (tc, 2, value);
+}
+
+static void
+test_with_session (CuTest *tc)
+{
+ CK_OBJECT_HANDLE objects[128];
+ CK_SESSION_HANDLE session;
+ CK_FUNCTION_LIST_PTR module;
+ CK_SLOT_ID slot;
+ P11KitIter *iter;
+ CK_RV rv;
+ int at;
+
+ rv = p11_kit_initialize_module (&mock_module);
+ CuAssertTrue (tc, rv == CKR_OK);
+
+ rv = mock_C_OpenSession (MOCK_SLOT_ONE_ID, CKF_SERIAL_SESSION, NULL, NULL, &session);
+ CuAssertTrue (tc, rv == CKR_OK);
+
+ iter = p11_kit_iter_new (NULL);
+ p11_kit_iter_begin_with (iter, &mock_module, 0, session);
+
+ at= 0;
+ while ((rv = p11_kit_iter_next (iter)) == CKR_OK) {
+ CuAssertTrue (tc, at < 128);
+ objects[at] = p11_kit_iter_get_object (iter);
+
+ slot = p11_kit_iter_get_slot (iter);
+ CuAssertTrue (tc, slot == MOCK_SLOT_ONE_ID);
+
+ module = p11_kit_iter_get_module (iter);
+ CuAssertPtrEquals (tc, module, &mock_module);
+
+ CuAssertTrue (tc, session == p11_kit_iter_get_session (iter));
+ at++;
+ }
+
+ CuAssertTrue (tc, rv == CKR_CANCEL);
+
+ /* 1 modules, each with 1 slot, and 3 public objects */
+ CuAssertIntEquals (tc, 3, at);
+
+ CuAssertTrue (tc, has_handle (objects, at, MOCK_DATA_OBJECT));
+ CuAssertTrue (tc, !has_handle (objects, at, MOCK_PRIVATE_KEY_CAPITALIZE));
+ CuAssertTrue (tc, has_handle (objects, at, MOCK_PUBLIC_KEY_CAPITALIZE));
+ CuAssertTrue (tc, !has_handle (objects, at, MOCK_PRIVATE_KEY_PREFIX));
+ CuAssertTrue (tc, has_handle (objects, at, MOCK_PUBLIC_KEY_PREFIX));
+
+ p11_kit_iter_free (iter);
+
+ /* The session is still valid ... */
+ rv = mock_module.C_CloseSession (session);
+ CuAssertTrue (tc, rv == CKR_OK);
+
+ rv = p11_kit_finalize_module (&mock_module);
+ CuAssertTrue (tc, rv == CKR_OK);
+}
+
+static void
+test_with_slot (CuTest *tc)
+{
+ CK_OBJECT_HANDLE objects[128];
+ CK_FUNCTION_LIST_PTR module;
+ CK_SLOT_ID slot;
+ P11KitIter *iter;
+ CK_RV rv;
+ int at;
+
+ rv = p11_kit_initialize_module (&mock_module);
+ CuAssertTrue (tc, rv == CKR_OK);
+
+ iter = p11_kit_iter_new (NULL);
+ p11_kit_iter_begin_with (iter, &mock_module, MOCK_SLOT_ONE_ID, 0);
+
+ at= 0;
+ while ((rv = p11_kit_iter_next (iter)) == CKR_OK) {
+ CuAssertTrue (tc, at < 128);
+ objects[at] = p11_kit_iter_get_object (iter);
+
+ slot = p11_kit_iter_get_slot (iter);
+ CuAssertTrue (tc, slot == MOCK_SLOT_ONE_ID);
+
+ module = p11_kit_iter_get_module (iter);
+ CuAssertPtrEquals (tc, module, &mock_module);
+ at++;
+ }
+
+ CuAssertTrue (tc, rv == CKR_CANCEL);
+
+ /* 1 modules, each with 1 slot, and 3 public objects */
+ CuAssertIntEquals (tc, 3, at);
+
+ CuAssertTrue (tc, has_handle (objects, at, MOCK_DATA_OBJECT));
+ CuAssertTrue (tc, !has_handle (objects, at, MOCK_PRIVATE_KEY_CAPITALIZE));
+ CuAssertTrue (tc, has_handle (objects, at, MOCK_PUBLIC_KEY_CAPITALIZE));
+ CuAssertTrue (tc, !has_handle (objects, at, MOCK_PRIVATE_KEY_PREFIX));
+ CuAssertTrue (tc, has_handle (objects, at, MOCK_PUBLIC_KEY_PREFIX));
+
+ p11_kit_iter_free (iter);
+
+ rv = p11_kit_finalize_module (&mock_module);
+ CuAssertTrue (tc, rv == CKR_OK);
+}
+
+static void
+test_with_module (CuTest *tc)
+{
+ CK_OBJECT_HANDLE objects[128];
+ CK_FUNCTION_LIST_PTR module;
+ P11KitIter *iter;
+ CK_RV rv;
+ int at;
+
+ rv = p11_kit_initialize_module (&mock_module);
+ CuAssertTrue (tc, rv == CKR_OK);
+
+ iter = p11_kit_iter_new (NULL);
+ p11_kit_iter_begin_with (iter, &mock_module, 0, 0);
+
+ at= 0;
+ while ((rv = p11_kit_iter_next (iter)) == CKR_OK) {
+ CuAssertTrue (tc, at < 128);
+ objects[at] = p11_kit_iter_get_object (iter);
+
+ module = p11_kit_iter_get_module (iter);
+ CuAssertPtrEquals (tc, module, &mock_module);
+ at++;
+ }
+
+ CuAssertTrue (tc, rv == CKR_CANCEL);
+
+ /* 1 modules, each with 1 slot, and 3 public objects */
+ CuAssertIntEquals (tc, 3, at);
+
+ CuAssertTrue (tc, has_handle (objects, at, MOCK_DATA_OBJECT));
+ CuAssertTrue (tc, !has_handle (objects, at, MOCK_PRIVATE_KEY_CAPITALIZE));
+ CuAssertTrue (tc, has_handle (objects, at, MOCK_PUBLIC_KEY_CAPITALIZE));
+ CuAssertTrue (tc, !has_handle (objects, at, MOCK_PRIVATE_KEY_PREFIX));
+ CuAssertTrue (tc, has_handle (objects, at, MOCK_PUBLIC_KEY_PREFIX));
+
+ p11_kit_iter_free (iter);
+
+ rv = p11_kit_finalize_module (&mock_module);
+ CuAssertTrue (tc, rv == CKR_OK);
+}
+
+static void
+test_keep_session (CuTest *tc)
+{
+ CK_SESSION_HANDLE session;
+ P11KitIter *iter;
+ CK_RV rv;
+
+ rv = p11_kit_initialize_module (&mock_module);
+ CuAssertTrue (tc, rv == CKR_OK);
+
+ iter = p11_kit_iter_new (NULL);
+ p11_kit_iter_begin_with (iter, &mock_module, 0, 0);
+
+ rv = p11_kit_iter_next (iter);
+ CuAssertTrue (tc, rv == CKR_OK);
+
+ session = p11_kit_iter_keep_session (iter);
+ p11_kit_iter_free (iter);
+
+ /* The session is still valid ... */
+ rv = mock_module.C_CloseSession (session);
+ CuAssertTrue (tc, rv == CKR_OK);
+
+ rv = p11_kit_finalize_module (&mock_module);
+ CuAssertTrue (tc, rv == CKR_OK);
+}
+
+static void
+test_unrecognized (CuTest *tc)
+{
+ CK_FUNCTION_LIST_PTR *modules;
+ P11KitIter *iter;
+ P11KitUri *uri;
+ CK_RV rv;
+ int count;
+
+ modules = initialize_and_get_modules (tc);
+
+ uri = p11_kit_uri_new ();
+ p11_kit_uri_set_unrecognized (uri, 1);
+ iter = p11_kit_iter_new (uri);
+ p11_kit_uri_free (uri);
+
+ p11_kit_iter_begin (iter, modules);
+
+ count = 0;
+ while ((rv = p11_kit_iter_next (iter)) == CKR_OK)
+ count++;
+
+ CuAssertTrue (tc, rv == CKR_CANCEL);
+
+ /* Nothing should have matched */
+ CuAssertIntEquals (tc, 0, count);
+
+ p11_kit_iter_free (iter);
+
+ finalize_and_free_modules (tc, modules);
+}
+
+static void
+test_uri_with_type (CuTest *tc)
+{
+ CK_OBJECT_HANDLE objects[128];
+ CK_FUNCTION_LIST_PTR *modules;
+ P11KitIter *iter;
+ P11KitUri *uri;
+ CK_RV rv;
+ int at;
+ int ret;
+
+ modules = initialize_and_get_modules (tc);
+
+ uri = p11_kit_uri_new ();
+ ret = p11_kit_uri_parse ("pkcs11:object-type=public", P11_KIT_URI_FOR_OBJECT, uri);
+ CuAssertIntEquals (tc, ret, P11_KIT_URI_OK);
+
+ iter = p11_kit_iter_new (uri);
+ p11_kit_uri_free (uri);
+
+ p11_kit_iter_begin (iter, modules);
+
+ at = 0;
+ while ((rv = p11_kit_iter_next (iter)) == CKR_OK) {
+ CuAssertTrue (tc, at < 128);
+ objects[at] = p11_kit_iter_get_object (iter);
+ at++;
+ }
+
+ CuAssertTrue (tc, rv == CKR_CANCEL);
+
+ /* Three modules, each with 1 slot, and 2 public keys */
+ CuAssertIntEquals (tc, 6, at);
+
+ CuAssertTrue (tc, !has_handle (objects, at, MOCK_DATA_OBJECT));
+ CuAssertTrue (tc, !has_handle (objects, at, MOCK_PRIVATE_KEY_CAPITALIZE));
+ CuAssertTrue (tc, has_handle (objects, at, MOCK_PUBLIC_KEY_CAPITALIZE));
+ CuAssertTrue (tc, !has_handle (objects, at, MOCK_PRIVATE_KEY_PREFIX));
+ CuAssertTrue (tc, has_handle (objects, at, MOCK_PUBLIC_KEY_PREFIX));
+
+ p11_kit_iter_free (iter);
+
+ finalize_and_free_modules (tc, modules);
+}
+
+static void
+test_filter (CuTest *tc)
+{
+ CK_OBJECT_HANDLE objects[128];
+ CK_FUNCTION_LIST_PTR *modules;
+ P11KitIter *iter;
+ CK_RV rv;
+ int at;
+
+ CK_BBOOL vfalse = CK_FALSE;
+ CK_OBJECT_CLASS public_key = CKO_PUBLIC_KEY;
+ CK_ATTRIBUTE attrs[] = {
+ { CKA_PRIVATE, &vfalse, sizeof (vfalse) },
+ { CKA_CLASS, &public_key, sizeof (public_key) },
+ };
+
+ modules = initialize_and_get_modules (tc);
+
+ iter = p11_kit_iter_new (NULL);
+ p11_kit_iter_add_filter (iter, attrs, 2);
+
+ p11_kit_iter_begin (iter, modules);
+
+ at = 0;
+ while ((rv = p11_kit_iter_next (iter)) == CKR_OK) {
+ CuAssertTrue (tc, at < 128);
+ objects[at] = p11_kit_iter_get_object (iter);
+ at++;
+ }
+
+ CuAssertTrue (tc, rv == CKR_CANCEL);
+
+ /* Three modules, each with 1 slot, and 2 public keys */
+ CuAssertIntEquals (tc, 6, at);
+
+ CuAssertTrue (tc, !has_handle (objects, at, MOCK_DATA_OBJECT));
+ CuAssertTrue (tc, !has_handle (objects, at, MOCK_PRIVATE_KEY_CAPITALIZE));
+ CuAssertTrue (tc, has_handle (objects, at, MOCK_PUBLIC_KEY_CAPITALIZE));
+ CuAssertTrue (tc, !has_handle (objects, at, MOCK_PRIVATE_KEY_PREFIX));
+ CuAssertTrue (tc, has_handle (objects, at, MOCK_PUBLIC_KEY_PREFIX));
+
+ p11_kit_iter_free (iter);
+
+ finalize_and_free_modules (tc, modules);
+}
+
+static void
+test_session_flags (CuTest *tc)
+{
+ CK_FUNCTION_LIST_PTR *modules;
+ CK_FUNCTION_LIST_PTR module;
+ CK_SESSION_HANDLE session;
+ CK_SESSION_INFO info;
+ P11KitIter *iter;
+ CK_RV rv;
+
+ modules = initialize_and_get_modules (tc);
+
+ iter = p11_kit_iter_new (NULL);
+ p11_kit_iter_set_session_flags (iter, CKF_RW_SESSION);
+
+ p11_kit_iter_begin (iter, modules);
+
+ while ((rv = p11_kit_iter_next (iter)) == CKR_OK) {
+ module = p11_kit_iter_get_module (iter);
+ CuAssertPtrNotNull (tc, module);
+
+ session = p11_kit_iter_get_session (iter);
+ CuAssertTrue (tc, session != 0);
+
+ rv = (module->C_GetSessionInfo) (session, &info);
+ CuAssertTrue (tc, rv == CKR_OK);
+
+ CuAssertIntEquals (tc, CKS_RW_PUBLIC_SESSION, info.state);
+ }
+
+ CuAssertTrue (tc, rv == CKR_CANCEL);
+
+ p11_kit_iter_free (iter);
+
+ finalize_and_free_modules (tc, modules);
+}
+
+static void
+test_module_match (CuTest *tc)
+{
+ CK_FUNCTION_LIST_PTR *modules;
+ P11KitIter *iter;
+ P11KitUri *uri;
+ CK_RV rv;
+ int count;
+ int ret;
+
+ modules = initialize_and_get_modules (tc);
+
+ uri = p11_kit_uri_new ();
+ ret = p11_kit_uri_parse ("pkcs11:library-description=MOCK%20LIBRARY", P11_KIT_URI_FOR_MODULE, uri);
+ CuAssertIntEquals (tc, P11_KIT_URI_OK, ret);
+
+ iter = p11_kit_iter_new (uri);
+ p11_kit_uri_free (uri);
+
+ p11_kit_iter_begin (iter, modules);
+
+ count = 0;
+ while ((rv = p11_kit_iter_next (iter)) == CKR_OK)
+ count++;
+
+ CuAssertTrue (tc, rv == CKR_CANCEL);
+
+ /* Three modules, each with 1 slot, and 3 public objects */
+ CuAssertIntEquals (tc, 9, count);
+
+ p11_kit_iter_free (iter);
+
+ finalize_and_free_modules (tc, modules);
+}
+
+static void
+test_module_mismatch (CuTest *tc)
+{
+ CK_FUNCTION_LIST_PTR *modules;
+ P11KitIter *iter;
+ P11KitUri *uri;
+ CK_RV rv;
+ int count;
+ int ret;
+
+ modules = initialize_and_get_modules (tc);
+
+ uri = p11_kit_uri_new ();
+ ret = p11_kit_uri_parse ("pkcs11:library-description=blah", P11_KIT_URI_FOR_MODULE, uri);
+ CuAssertIntEquals (tc, P11_KIT_URI_OK, ret);
+
+ iter = p11_kit_iter_new (uri);
+ p11_kit_uri_free (uri);
+
+ p11_kit_iter_begin (iter, modules);
+
+ count = 0;
+ while ((rv = p11_kit_iter_next (iter)) == CKR_OK)
+ count++;
+
+ CuAssertTrue (tc, rv == CKR_CANCEL);
+
+ /* Nothing should have matched */
+ CuAssertIntEquals (tc, 0, count);
+
+ p11_kit_iter_free (iter);
+
+ finalize_and_free_modules (tc, modules);
+}
+
+static void
+test_token_match (CuTest *tc)
+{
+ CK_FUNCTION_LIST_PTR *modules;
+ P11KitIter *iter;
+ P11KitUri *uri;
+ CK_RV rv;
+ int count;
+ int ret;
+
+ modules = initialize_and_get_modules (tc);
+
+ uri = p11_kit_uri_new ();
+ ret = p11_kit_uri_parse ("pkcs11:manufacturer=TEST%20MANUFACTURER", P11_KIT_URI_FOR_TOKEN, uri);
+ CuAssertIntEquals (tc, P11_KIT_URI_OK, ret);
+
+ iter = p11_kit_iter_new (uri);
+ p11_kit_uri_free (uri);
+
+ p11_kit_iter_begin (iter, modules);
+
+ count = 0;
+ while ((rv = p11_kit_iter_next (iter)) == CKR_OK)
+ count++;
+
+ CuAssertTrue (tc, rv == CKR_CANCEL);
+
+ /* Three modules, each with 1 slot, and 3 public objects */
+ CuAssertIntEquals (tc, 9, count);
+
+ p11_kit_iter_free (iter);
+
+ finalize_and_free_modules (tc, modules);
+}
+
+static void
+test_token_mismatch (CuTest *tc)
+{
+ CK_FUNCTION_LIST_PTR *modules;
+ P11KitIter *iter;
+ P11KitUri *uri;
+ CK_RV rv;
+ int count;
+ int ret;
+
+ modules = initialize_and_get_modules (tc);
+
+ uri = p11_kit_uri_new ();
+ ret = p11_kit_uri_parse ("pkcs11:manufacturer=blah", P11_KIT_URI_FOR_TOKEN, uri);
+ CuAssertIntEquals (tc, P11_KIT_URI_OK, ret);
+
+ iter = p11_kit_iter_new (uri);
+ p11_kit_uri_free (uri);
+
+ p11_kit_iter_begin (iter, modules);
+
+ count = 0;
+ while ((rv = p11_kit_iter_next (iter)) == CKR_OK)
+ count++;
+
+ CuAssertTrue (tc, rv == CKR_CANCEL);
+
+ /* Nothing should have matched */
+ CuAssertIntEquals (tc, 0, count);
+
+ p11_kit_iter_free (iter);
+
+ finalize_and_free_modules (tc, modules);
+}
+
+static void
+test_getslotlist_fail_first (CuTest *tc)
+{
+ CK_FUNCTION_LIST module;
+ P11KitIter *iter;
+ CK_RV rv;
+ int at;
+
+ rv = p11_kit_initialize_module (&mock_module);
+ CuAssertTrue (tc, rv == CKR_OK);
+
+ memcpy (&module, &mock_module, sizeof (CK_FUNCTION_LIST));
+ module.C_GetSlotList = mock_C_GetSlotList__fail_first;
+
+ iter = p11_kit_iter_new (NULL);
+ p11_kit_iter_begin_with (iter, &module, 0, 0);
+
+ at= 0;
+ while ((rv = p11_kit_iter_next (iter)) == CKR_OK)
+ at++;
+
+ CuAssertTrue (tc, rv == CKR_VENDOR_DEFINED);
+
+ /* Should fail on the first iteration */
+ CuAssertIntEquals (tc, 0, at);
+
+ p11_kit_iter_free (iter);
+
+ rv = p11_kit_finalize_module (&mock_module);
+ CuAssertTrue (tc, rv == CKR_OK);
+}
+
+static void
+test_getslotlist_fail_late (CuTest *tc)
+{
+ CK_FUNCTION_LIST module;
+ P11KitIter *iter;
+ CK_RV rv;
+ int at;
+
+ rv = p11_kit_initialize_module (&mock_module);
+ CuAssertTrue (tc, rv == CKR_OK);
+
+ memcpy (&module, &mock_module, sizeof (CK_FUNCTION_LIST));
+ module.C_GetSlotList = mock_C_GetSlotList__fail_late;
+
+ iter = p11_kit_iter_new (NULL);
+ p11_kit_iter_begin_with (iter, &module, 0, 0);
+
+ at= 0;
+ while ((rv = p11_kit_iter_next (iter)) == CKR_OK)
+ at++;
+
+ CuAssertTrue (tc, rv == CKR_VENDOR_DEFINED);
+
+ /* Should fail on the first iteration */
+ CuAssertIntEquals (tc, 0, at);
+
+ p11_kit_iter_free (iter);
+
+ rv = p11_kit_finalize_module (&mock_module);
+ CuAssertTrue (tc, rv == CKR_OK);
+}
+
+static void
+test_token_not_initialized (CuTest *tc)
+{
+ CK_FUNCTION_LIST module;
+ P11KitIter *iter;
+ CK_RV rv;
+ int at;
+
+ rv = p11_kit_initialize_module (&mock_module);
+ CuAssertTrue (tc, rv == CKR_OK);
+
+ memcpy (&module, &mock_module, sizeof (CK_FUNCTION_LIST));
+ module.C_GetTokenInfo = mock_C_GetTokenInfo_not_initialized;
+
+ iter = p11_kit_iter_new (NULL);
+ p11_kit_iter_begin_with (iter, &module, 0, 0);
+
+ at= 0;
+ while ((rv = p11_kit_iter_next (iter)) == CKR_OK)
+ at++;
+
+ CuAssertTrue (tc, rv == CKR_CANCEL);
+
+ /* Should fail on the first iteration */
+ CuAssertIntEquals (tc, 0, at);
+
+ p11_kit_iter_free (iter);
+
+ rv = p11_kit_finalize_module (&mock_module);
+ CuAssertTrue (tc, rv == CKR_OK);
+}
+
+static void
+test_open_session_fail (CuTest *tc)
+{
+ CK_FUNCTION_LIST module;
+ P11KitIter *iter;
+ CK_RV rv;
+ int at;
+
+ rv = p11_kit_initialize_module (&mock_module);
+ CuAssertTrue (tc, rv == CKR_OK);
+
+ memcpy (&module, &mock_module, sizeof (CK_FUNCTION_LIST));
+ module.C_OpenSession = mock_C_OpenSession__fails;
+
+ iter = p11_kit_iter_new (NULL);
+ p11_kit_iter_begin_with (iter, &module, 0, 0);
+
+ at= 0;
+ while ((rv = p11_kit_iter_next (iter)) == CKR_OK)
+ at++;
+
+ CuAssertTrue (tc, rv == CKR_DEVICE_ERROR);
+
+ /* Should fail on the first iteration */
+ CuAssertIntEquals (tc, 0, at);
+
+ p11_kit_iter_free (iter);
+
+ rv = p11_kit_finalize_module (&mock_module);
+ CuAssertTrue (tc, rv == CKR_OK);
+}
+
+static void
+test_find_init_fail (CuTest *tc)
+{
+ CK_FUNCTION_LIST module;
+ P11KitIter *iter;
+ CK_RV rv;
+ int at;
+
+ rv = p11_kit_initialize_module (&mock_module);
+ CuAssertTrue (tc, rv == CKR_OK);
+
+ memcpy (&module, &mock_module, sizeof (CK_FUNCTION_LIST));
+ module.C_FindObjectsInit = mock_C_FindObjectsInit__fails;
+
+ iter = p11_kit_iter_new (NULL);
+ p11_kit_iter_begin_with (iter, &module, 0, 0);
+
+ at= 0;
+ while ((rv = p11_kit_iter_next (iter)) == CKR_OK)
+ at++;
+
+ CuAssertTrue (tc, rv == CKR_DEVICE_MEMORY);
+
+ /* Should fail on the first iteration */
+ CuAssertIntEquals (tc, 0, at);
+
+ p11_kit_iter_free (iter);
+
+ rv = p11_kit_finalize_module (&mock_module);
+ CuAssertTrue (tc, rv == CKR_OK);
+}
+
+static void
+test_find_objects_fail (CuTest *tc)
+{
+ CK_FUNCTION_LIST module;
+ P11KitIter *iter;
+ CK_RV rv;
+ int at;
+
+ rv = p11_kit_initialize_module (&mock_module);
+ CuAssertTrue (tc, rv == CKR_OK);
+
+ memcpy (&module, &mock_module, sizeof (CK_FUNCTION_LIST));
+ module.C_FindObjects = mock_C_FindObjects__fails;
+
+ iter = p11_kit_iter_new (NULL);
+ p11_kit_iter_begin_with (iter, &module, 0, 0);
+
+ at= 0;
+ while ((rv = p11_kit_iter_next (iter)) == CKR_OK)
+ at++;
+
+ CuAssertTrue (tc, rv == CKR_DEVICE_REMOVED);
+
+ /* Should fail on the first iteration */
+ CuAssertIntEquals (tc, 0, at);
+
+ p11_kit_iter_free (iter);
+
+ rv = p11_kit_finalize_module (&mock_module);
+ CuAssertTrue (tc, rv == CKR_OK);
+}
+
+static void
+test_load_attributes (CuTest *tc)
+{
+ CK_FUNCTION_LIST_PTR *modules;
+ P11KitIter *iter;
+ CK_ATTRIBUTE *attrs;
+ CK_OBJECT_HANDLE object;
+ CK_ULONG ulong;
+ CK_RV rv;
+ int at;
+
+ CK_ATTRIBUTE types[] = {
+ { CKA_CLASS },
+ { CKA_LABEL },
+ };
+
+ modules = initialize_and_get_modules (tc);
+
+ iter = p11_kit_iter_new (NULL);
+ p11_kit_iter_begin (iter, modules);
+
+ attrs = p11_attrs_buildn (NULL, types, 2);
+
+ at = 0;
+ while ((rv = p11_kit_iter_next (iter)) == CKR_OK) {
+ rv = p11_kit_iter_load_attributes (iter, attrs, 2);
+ CuAssertTrue (tc, rv == CKR_OK);
+
+ object = p11_kit_iter_get_object (iter);
+ switch (object) {
+ case MOCK_DATA_OBJECT:
+ CuAssertTrue (tc, p11_attrs_find_ulong (attrs, CKA_CLASS, &ulong) && ulong == CKO_DATA);
+ CuAssertTrue (tc, p11_attr_match_value (p11_attrs_find (attrs, CKA_LABEL), "TEST LABEL", -1));
+ break;
+ case MOCK_PUBLIC_KEY_CAPITALIZE:
+ CuAssertTrue (tc, p11_attrs_find_ulong (attrs, CKA_CLASS, &ulong) && ulong == CKO_PUBLIC_KEY);
+ CuAssertTrue (tc, p11_attr_match_value (p11_attrs_find (attrs, CKA_LABEL), "Public Capitalize Key", -1));
+ break;
+ case MOCK_PUBLIC_KEY_PREFIX:
+ CuAssertTrue (tc, p11_attrs_find_ulong (attrs, CKA_CLASS, &ulong) && ulong == CKO_PUBLIC_KEY);
+ CuAssertTrue (tc, p11_attr_match_value (p11_attrs_find (attrs, CKA_LABEL), "Public prefix key", -1));
+ break;
+ default:
+ CuFail (tc, "Unknown object matched");
+ break;
+ }
+
+ at++;
+ }
+
+ p11_attrs_free (attrs);
+
+ CuAssertTrue (tc, rv == CKR_CANCEL);
+
+ /* Three modules, each with 1 slot, and 3 public objects */
+ CuAssertIntEquals (tc, 9, at);
+
+ p11_kit_iter_free (iter);
+
+ finalize_and_free_modules (tc, modules);
+}
+
+static void
+test_load_attributes_none (CuTest *tc)
+{
+ CK_FUNCTION_LIST module;
+ P11KitIter *iter;
+ CK_ATTRIBUTE *attrs;
+ CK_RV rv;
+
+ rv = p11_kit_initialize_module (&mock_module);
+ CuAssertTrue (tc, rv == CKR_OK);
+
+ memcpy (&module, &mock_module, sizeof (CK_FUNCTION_LIST));
+
+ iter = p11_kit_iter_new (NULL);
+ p11_kit_iter_begin_with (iter, &module, 0, 0);
+
+ while ((rv = p11_kit_iter_next (iter)) == CKR_OK) {
+ attrs = p11_attrs_buildn (NULL, NULL, 0);
+ rv = p11_kit_iter_load_attributes (iter, attrs, 0);
+ CuAssertTrue (tc, rv == CKR_OK);
+ }
+
+ CuAssertTrue (tc, rv == CKR_CANCEL);
+
+ p11_kit_iter_free (iter);
+
+ rv = p11_kit_finalize_module (&mock_module);
+ CuAssertTrue (tc, rv == CKR_OK);
+}
+
+static void
+test_load_attributes_fail_first (CuTest *tc)
+{
+ CK_ATTRIBUTE label = { CKA_LABEL, };
+ CK_FUNCTION_LIST module;
+ P11KitIter *iter;
+ CK_ATTRIBUTE *attrs;
+ CK_RV rv;
+
+ rv = p11_kit_initialize_module (&mock_module);
+ CuAssertTrue (tc, rv == CKR_OK);
+
+ memcpy (&module, &mock_module, sizeof (CK_FUNCTION_LIST));
+ module.C_GetAttributeValue = mock_C_GetAttributeValue__fail_first;
+
+ iter = p11_kit_iter_new (NULL);
+ p11_kit_iter_begin_with (iter, &module, 0, 0);
+
+ while ((rv = p11_kit_iter_next (iter)) == CKR_OK) {
+ attrs = p11_attrs_build (NULL, &label, NULL);
+ rv = p11_kit_iter_load_attributes (iter, attrs, 1);
+ CuAssertTrue (tc, rv == CKR_FUNCTION_REJECTED);
+ p11_attrs_free (attrs);
+ }
+
+ CuAssertTrue (tc, rv == CKR_CANCEL);
+
+ p11_kit_iter_free (iter);
+
+ rv = p11_kit_finalize_module (&mock_module);
+ CuAssertTrue (tc, rv == CKR_OK);
+}
+
+static void
+test_load_attributes_fail_late (CuTest *tc)
+{
+ CK_ATTRIBUTE label = { CKA_LABEL, };
+ CK_FUNCTION_LIST module;
+ P11KitIter *iter;
+ CK_ATTRIBUTE *attrs;
+ CK_RV rv;
+
+ rv = p11_kit_initialize_module (&mock_module);
+ CuAssertTrue (tc, rv == CKR_OK);
+
+ memcpy (&module, &mock_module, sizeof (CK_FUNCTION_LIST));
+ module.C_GetAttributeValue = mock_C_GetAttributeValue__fail_late;
+
+ iter = p11_kit_iter_new (NULL);
+ p11_kit_iter_begin_with (iter, &module, 0, 0);
+
+ while ((rv = p11_kit_iter_next (iter)) == CKR_OK) {
+ attrs = p11_attrs_build (NULL, &label, NULL);
+ rv = p11_kit_iter_load_attributes (iter, attrs, 1);
+ CuAssertTrue (tc, rv == CKR_FUNCTION_FAILED);
+ p11_attrs_free (attrs);
+ }
+
+ CuAssertTrue (tc, rv == CKR_CANCEL);
+
+ p11_kit_iter_free (iter);
+
+ rv = p11_kit_finalize_module (&mock_module);
+ CuAssertTrue (tc, rv == CKR_OK);
+}
+
+int
+main (void)
+{
+ CuString *output = CuStringNew ();
+ CuSuite* suite = CuSuiteNew ();
+ int ret;
+
+ setenv ("P11_KIT_STRICT", "1", 1);
+ p11_library_init ();
+ mock_module_init ();
+
+ SUITE_ADD_TEST (suite, test_all);
+ SUITE_ADD_TEST (suite, test_unrecognized);
+ SUITE_ADD_TEST (suite, test_uri_with_type);
+ SUITE_ADD_TEST (suite, test_session_flags);
+ SUITE_ADD_TEST (suite, test_callback);
+ SUITE_ADD_TEST (suite, test_callback_fails);
+ SUITE_ADD_TEST (suite, test_callback_destroyer);
+ SUITE_ADD_TEST (suite, test_filter);
+ SUITE_ADD_TEST (suite, test_with_session);
+ SUITE_ADD_TEST (suite, test_with_slot);
+ SUITE_ADD_TEST (suite, test_with_module);
+ SUITE_ADD_TEST (suite, test_keep_session);
+ SUITE_ADD_TEST (suite, test_token_match);
+ SUITE_ADD_TEST (suite, test_token_mismatch);
+ SUITE_ADD_TEST (suite, test_module_match);
+ SUITE_ADD_TEST (suite, test_module_mismatch);
+ SUITE_ADD_TEST (suite, test_getslotlist_fail_first);
+ SUITE_ADD_TEST (suite, test_getslotlist_fail_late);
+ SUITE_ADD_TEST (suite, test_token_not_initialized);
+ SUITE_ADD_TEST (suite, test_open_session_fail);
+ SUITE_ADD_TEST (suite, test_find_init_fail);
+ SUITE_ADD_TEST (suite, test_find_objects_fail);
+ SUITE_ADD_TEST (suite, test_load_attributes);
+ SUITE_ADD_TEST (suite, test_load_attributes_none);
+ SUITE_ADD_TEST (suite, test_load_attributes_fail_first);
+ SUITE_ADD_TEST (suite, test_load_attributes_fail_late);
+
+ CuSuiteRun (suite);
+ CuSuiteSummary (suite, output);
+ CuSuiteDetails (suite, output);
+ printf ("%s\n", output->buffer);
+ ret = suite->failCount;
+ CuSuiteDelete (suite);
+ CuStringDelete (output);
+ return ret;
+}