]> granicus.if.org Git - p11-kit/commitdiff
filter: New virtual wrapper for access control
authorDaiki Ueno <dueno@redhat.com>
Tue, 13 Dec 2016 17:24:16 +0000 (18:24 +0100)
committerDaiki Ueno <ueno@gnu.org>
Wed, 8 Feb 2017 15:34:28 +0000 (16:34 +0100)
doc/manual/Makefile.am
p11-kit/Makefile.am
p11-kit/filter.c [new file with mode: 0644]
p11-kit/filter.h [new file with mode: 0644]
p11-kit/test-filter.c [new file with mode: 0644]

index ae59530f2b0803aba27c49db9220d1446ef5f0a2..7108977f4f673b237aa9f6d9f4599f36c964dd1a 100644 (file)
@@ -52,6 +52,7 @@ IGNORE_HFILES= \
        conf.h \
        debug.h \
        dict.h \
+       filter.h \
        log.h \
        mock.h \
        modules.h \
index c55746a16857087cd080a6c431247166b0039048..d51c052a09b28e0552e8130495e2e4efb29818a3 100644 (file)
@@ -13,6 +13,7 @@ MODULE_SRCS = \
        p11-kit/conf.c p11-kit/conf.h \
        p11-kit/iter.c \
        p11-kit/log.c p11-kit/log.h \
+       p11-kit/filter.c p11-kit/filter.h \
        p11-kit/modules.c p11-kit/modules.h \
        p11-kit/pkcs11.h \
        p11-kit/pin.c \
@@ -217,12 +218,16 @@ CHECK_PROGS += \
        test-virtual \
        test-managed \
        test-log \
+       test-filter \
        test-transport \
        $(NULL)
 
 test_log_SOURCES = p11-kit/test-log.c
 test_log_LDADD = $(p11_kit_LIBS)
 
+test_filter_SOURCES = p11-kit/test-filter.c
+test_filter_LDADD = $(p11_kit_LIBS)
+
 test_managed_SOURCES = p11-kit/test-managed.c
 test_managed_LDADD = $(p11_kit_LIBS)
 
diff --git a/p11-kit/filter.c b/p11-kit/filter.c
new file mode 100644 (file)
index 0000000..09e1a45
--- /dev/null
@@ -0,0 +1,420 @@
+/*
+ * Copyright (c) 2016, Red Hat Inc.
+ *
+ * All rights reserved.
+ *
+ * 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.
+ *
+ *
+ * CONTRIBUTORS
+ *  Daiki Ueno
+ */
+
+#include "config.h"
+
+#include "attrs.h"
+#include "buffer.h"
+#include "constants.h"
+#include "debug.h"
+#include "filter.h"
+#include "iter.h"
+#include "message.h"
+#include "p11-kit.h"
+#include "virtual.h"
+
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+
+typedef struct {
+       p11_virtual virt;
+       CK_X_FUNCTION_LIST *lower;
+       p11_destroyer destroyer;
+       p11_array *entries;
+       bool allowed;
+       bool initialized;
+       CK_SLOT_ID *slots;
+       CK_ULONG n_slots;
+       CK_ULONG max_slots;
+} FilterData;
+
+extern int p11_match_uri_token_info (CK_TOKEN_INFO_PTR one,
+                                    CK_TOKEN_INFO_PTR two);
+
+static bool
+filter_match_token (FilterData *filter, CK_TOKEN_INFO *token)
+{
+       unsigned int i;
+
+       for (i = 0; i < filter->entries->num; i++) {
+               CK_TOKEN_INFO *entry = filter->entries->elem[i];
+               bool matched = p11_match_uri_token_info (entry, token);
+
+               if ((filter->allowed && matched) ||
+                   (!filter->allowed && !matched))
+                       return true;
+       }
+
+       return false;
+}
+
+static bool
+filter_add_slot (FilterData *filter, CK_SLOT_ID slot)
+{
+       if (filter->n_slots >= filter->max_slots) {
+               filter->max_slots = filter->max_slots * 2 + 1;
+               filter->slots = realloc (filter->slots,
+                                        filter->max_slots * sizeof (CK_SLOT_ID));
+               if (filter->slots == NULL)
+                       return false;
+       }
+       filter->slots[filter->n_slots++] = slot;
+       return true;
+}
+
+static CK_RV
+filter_ensure (FilterData *filter)
+{
+       CK_FUNCTION_LIST *lower = NULL;
+       P11KitIter *iter = NULL;
+       CK_RV rv = CKR_OK;
+
+       if (filter->slots != NULL) {
+               free (filter->slots);
+               filter->slots = NULL;
+       }
+       filter->n_slots = 0;
+       filter->max_slots = 0;
+
+       iter = p11_kit_iter_new (NULL,
+                                P11_KIT_ITER_WITH_TOKENS |
+                                P11_KIT_ITER_WITHOUT_OBJECTS);
+       if (iter == NULL) {
+               rv = CKR_HOST_MEMORY;
+               goto out;
+       }
+
+       lower = p11_virtual_wrap (filter->virt.lower_module, NULL);
+       if (lower == NULL) {
+               rv = CKR_HOST_MEMORY;
+               goto out;
+       }
+
+       p11_kit_iter_begin_with (iter, lower, 0, CK_INVALID_HANDLE);
+       while (p11_kit_iter_next (iter) == CKR_OK) {
+               CK_TOKEN_INFO *token;
+
+               token = p11_kit_iter_get_token (iter);
+               if (filter_match_token (filter, token)) {
+                       CK_SLOT_ID slot;
+
+                       slot = p11_kit_iter_get_slot (iter);
+                       if (!filter_add_slot (filter, slot)) {
+                               rv = CKR_HOST_MEMORY;
+                               goto out;
+                       }
+               }
+       }
+
+       rv = CKR_OK;
+ out:
+       p11_kit_iter_free (iter);
+       if (lower)
+               p11_virtual_unwrap (lower);
+       return rv;
+}
+
+static void
+filter_reinit (FilterData *filter)
+{
+       CK_RV rv;
+
+       rv = filter_ensure (filter);
+       if (rv == CKR_OK)
+               filter->initialized = true;
+       else {
+               filter->initialized = false;
+               p11_message ("filter cannot be initialized");
+       }
+}
+
+static CK_RV
+filter_C_Initialize (CK_X_FUNCTION_LIST *self,
+                    CK_VOID_PTR pInitArgs)
+{
+       FilterData *filter = (FilterData *)self;
+       CK_RV rv;
+
+       rv = filter->lower->C_Initialize (filter->lower, pInitArgs);
+       if (rv == CKR_OK)
+               filter_reinit (filter);
+       return rv;
+}
+
+static CK_RV
+filter_C_Finalize (CK_X_FUNCTION_LIST *self,
+                  CK_VOID_PTR pReserved)
+{
+       FilterData *filter = (FilterData *)self;
+
+       free (filter->slots);
+       filter->n_slots = 0;
+       p11_array_clear (filter->entries);
+       filter->initialized = false;
+       filter->allowed = false;
+
+       return filter->lower->C_Finalize (filter->lower, pReserved);
+}
+
+static CK_RV
+filter_C_GetSlotList (CK_X_FUNCTION_LIST *self,
+                     CK_BBOOL tokenPresent,
+                     CK_SLOT_ID_PTR pSlotList,
+                     CK_ULONG_PTR pulCount)
+{
+       FilterData *filter = (FilterData *)self;
+       CK_ULONG count;
+
+       if (pulCount == NULL)
+               return CKR_ARGUMENTS_BAD;
+
+       count = *pulCount;
+       *pulCount = filter->n_slots;
+
+       if (pSlotList == NULL)
+               return CKR_OK;
+
+       if (filter->n_slots > count)
+               return CKR_BUFFER_TOO_SMALL;
+
+       for (count = 0; count < filter->n_slots; count++)
+               pSlotList[count] = count;
+       *pulCount = filter->n_slots;
+       return CKR_OK;
+}
+
+static CK_RV
+filter_C_GetSlotInfo (CK_X_FUNCTION_LIST *self,
+                     CK_SLOT_ID slotID,
+                     CK_SLOT_INFO_PTR pInfo)
+{
+       FilterData *filter = (FilterData *)self;
+
+       if (slotID >= filter->n_slots)
+               return CKR_SLOT_ID_INVALID;
+
+       return filter->lower->C_GetSlotInfo (filter->lower, filter->slots[slotID], pInfo);
+}
+
+static CK_RV
+filter_C_GetTokenInfo (CK_X_FUNCTION_LIST *self,
+                      CK_SLOT_ID slotID,
+                      CK_TOKEN_INFO_PTR pInfo)
+{
+       FilterData *filter = (FilterData *)self;
+
+       if (slotID >= filter->n_slots)
+               return CKR_SLOT_ID_INVALID;
+
+       return filter->lower->C_GetTokenInfo (filter->lower, filter->slots[slotID], pInfo);
+}
+
+static CK_RV
+filter_C_GetMechanismList (CK_X_FUNCTION_LIST *self,
+                          CK_SLOT_ID slotID,
+                          CK_MECHANISM_TYPE_PTR pMechanismList,
+                          CK_ULONG_PTR pulCount)
+{
+       FilterData *filter = (FilterData *)self;
+
+       if (slotID >= filter->n_slots)
+               return CKR_SLOT_ID_INVALID;
+
+       return filter->lower->C_GetMechanismList (filter->lower,
+                                                 filter->slots[slotID],
+                                                 pMechanismList,
+                                                 pulCount);
+}
+
+static CK_RV
+filter_C_GetMechanismInfo (CK_X_FUNCTION_LIST *self,
+                          CK_SLOT_ID slotID,
+                          CK_MECHANISM_TYPE type,
+                          CK_MECHANISM_INFO_PTR pInfo)
+{
+       FilterData *filter = (FilterData *)self;
+
+       if (slotID >= filter->n_slots)
+               return CKR_SLOT_ID_INVALID;
+
+       return filter->lower->C_GetMechanismInfo (filter->lower,
+                                                 filter->slots[slotID],
+                                                 type,
+                                                 pInfo);
+}
+
+static CK_RV
+filter_C_InitToken (CK_X_FUNCTION_LIST *self,
+                   CK_SLOT_ID slotID,
+                   CK_UTF8CHAR_PTR pPin,
+                   CK_ULONG ulPinLen,
+                   CK_UTF8CHAR_PTR pLabel)
+{
+       FilterData *filter = (FilterData *)self;
+
+       if (slotID >= filter->n_slots)
+               return CKR_SLOT_ID_INVALID;
+
+       return filter->lower->C_InitToken (filter->lower, filter->slots[slotID],
+                                          pPin, ulPinLen, pLabel);
+}
+
+static CK_RV
+filter_C_WaitForSlotEvent (CK_X_FUNCTION_LIST *self,
+                          CK_FLAGS flags,
+                          CK_SLOT_ID_PTR pSlot,
+                          CK_VOID_PTR pReserved)
+{
+       return CKR_FUNCTION_NOT_SUPPORTED;
+}
+
+static CK_RV
+filter_C_OpenSession (CK_X_FUNCTION_LIST *self,
+                     CK_SLOT_ID slotID,
+                     CK_FLAGS flags,
+                     CK_VOID_PTR pApplication,
+                     CK_NOTIFY Notify,
+                     CK_SESSION_HANDLE_PTR phSession)
+{
+       FilterData *filter = (FilterData *)self;
+
+       if (slotID >= filter->n_slots)
+               return CKR_SLOT_ID_INVALID;
+
+       return filter->lower->C_OpenSession (filter->lower,
+                                            filter->slots[slotID], flags,
+                                            pApplication, Notify,
+                                            phSession);
+}
+
+static CK_RV
+filter_C_CloseAllSessions (CK_X_FUNCTION_LIST *self,
+                          CK_SLOT_ID slotID)
+{
+       FilterData *filter = (FilterData *)self;
+
+       if (slotID >= filter->n_slots)
+               return CKR_SLOT_ID_INVALID;
+
+       return filter->lower->C_CloseAllSessions (filter->lower,
+                                                 filter->slots[slotID]);
+}
+
+void
+p11_filter_release (void *data)
+{
+       FilterData *filter = (FilterData *)data;
+
+       return_if_fail (data != NULL);
+       p11_virtual_uninit (&filter->virt);
+       p11_array_free (filter->entries);
+       free (filter);
+}
+
+p11_virtual *
+p11_filter_subclass (p11_virtual *lower,
+                    p11_destroyer destroyer)
+{
+       FilterData *filter;
+       CK_X_FUNCTION_LIST functions;
+
+       filter = calloc (1, sizeof (FilterData));
+       return_val_if_fail (filter != NULL, NULL);
+
+       memcpy (&functions, &p11_virtual_stack, sizeof (CK_X_FUNCTION_LIST));
+       functions.C_Initialize = filter_C_Initialize;
+       functions.C_Finalize = filter_C_Finalize;
+       functions.C_GetSlotList = filter_C_GetSlotList;
+       functions.C_GetSlotInfo = filter_C_GetSlotInfo;
+       functions.C_GetTokenInfo = filter_C_GetTokenInfo;
+       functions.C_GetMechanismList = filter_C_GetMechanismList;
+       functions.C_GetMechanismInfo = filter_C_GetMechanismInfo;
+       functions.C_InitToken = filter_C_InitToken;
+       functions.C_WaitForSlotEvent = filter_C_WaitForSlotEvent;
+       functions.C_OpenSession = filter_C_OpenSession;
+       functions.C_CloseAllSessions = filter_C_CloseAllSessions;
+
+       p11_virtual_init (&filter->virt, &functions, lower, destroyer);
+       filter->lower = &lower->funcs;
+       filter->entries = p11_array_new ((p11_destroyer)free);
+       return &filter->virt;
+}
+
+void
+p11_filter_allow_token (p11_virtual *virt,
+                       CK_TOKEN_INFO *token)
+{
+       FilterData *filter = (FilterData *)virt;
+       CK_TOKEN_INFO *token_copy;
+
+       return_if_fail (filter->allowed || filter->entries->num == 0);
+       filter->allowed = true;
+
+       token_copy = memdup (token, sizeof (CK_TOKEN_INFO));
+       return_if_fail (token_copy != NULL);
+
+       if (!p11_array_push (filter->entries, token_copy))
+               return_if_reached ();
+
+       if (filter->initialized)
+               filter_reinit (filter);
+}
+
+void
+p11_filter_deny_token (p11_virtual *virt,
+                      CK_TOKEN_INFO *token)
+{
+       FilterData *filter = (FilterData *)virt;
+       CK_TOKEN_INFO *token_copy;
+
+       return_if_fail (!filter->allowed || filter->entries->num == 0);
+       filter->allowed = false;
+
+       token_copy = memdup (token, sizeof (CK_TOKEN_INFO));
+       return_if_fail (token_copy != NULL);
+
+       if (!p11_array_push (filter->entries, token_copy))
+               return_if_reached ();
+
+       if (filter->initialized)
+               filter_reinit (filter);
+}
diff --git a/p11-kit/filter.h b/p11-kit/filter.h
new file mode 100644 (file)
index 0000000..90cdb12
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2016, Red Hat Inc.
+ *
+ * All rights reserved.
+ *
+ * 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.
+ *
+ *
+ * CONTRIBUTORS
+ *  Daiki Ueno
+ */
+
+#ifndef P11_FILTER_H_
+#define P11_FILTER_H_
+
+#include "virtual.h"
+
+p11_virtual *p11_filter_subclass    (p11_virtual   *lower,
+                                     p11_destroyer  destroyer);
+
+void         p11_filter_release     (void          *filterger);
+
+void         p11_filter_allow_token (p11_virtual   *virt,
+                                     CK_TOKEN_INFO *token);
+
+void         p11_filter_deny_token  (p11_virtual   *virt,
+                                     CK_TOKEN_INFO *token);
+
+#endif /* P11_FILTER_H_ */
diff --git a/p11-kit/test-filter.c b/p11-kit/test-filter.c
new file mode 100644 (file)
index 0000000..72272db
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2016 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: Daiki Ueno
+ */
+
+#include "config.h"
+#include "test.h"
+
+#include "dict.h"
+#include "library.h"
+#include "filter.h"
+#include "mock.h"
+#include "modules.h"
+#include "p11-kit.h"
+#include "virtual.h"
+
+#include <errno.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+static CK_TOKEN_INFO 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' }
+};
+
+static void
+test_allowed (void)
+{
+       CK_FUNCTION_LIST_PTR module;
+       p11_virtual virt;
+       p11_virtual *filter;
+       CK_ULONG count;
+       CK_RV rv;
+
+       p11_virtual_init (&virt, &p11_virtual_base, &mock_module, NULL);
+       filter = p11_filter_subclass (&virt, NULL);
+       module = p11_virtual_wrap (filter, (p11_destroyer)p11_virtual_uninit);
+       assert_ptr_not_null (module);
+
+       p11_filter_allow_token (filter, &TOKEN_ONE);
+
+       rv = (module->C_Initialize) (NULL);
+       assert_num_eq (CKR_OK, rv);
+
+       rv = (module->C_GetSlotList) (CK_TRUE, NULL, &count);
+       assert_num_eq (CKR_OK, rv);
+       assert_num_eq (count, 1);
+
+       rv = (module->C_Finalize) (NULL);
+       assert_num_eq (CKR_OK, rv);
+
+       p11_virtual_unwrap (module);
+       p11_filter_release (filter);
+}
+
+static void
+test_denied (void)
+{
+       CK_FUNCTION_LIST_PTR module;
+       p11_virtual virt;
+       p11_virtual *filter;
+       CK_ULONG count;
+       CK_RV rv;
+
+       p11_virtual_init (&virt, &p11_virtual_base, &mock_module, NULL);
+       filter = p11_filter_subclass (&virt, NULL);
+       module = p11_virtual_wrap (filter, (p11_destroyer)p11_virtual_uninit);
+       assert_ptr_not_null (module);
+
+       p11_filter_deny_token (filter, &TOKEN_ONE);
+
+       rv = (module->C_Initialize) (NULL);
+       assert_num_eq (CKR_OK, rv);
+
+       rv = (module->C_GetSlotList) (CK_TRUE, NULL, &count);
+       assert_num_eq (CKR_OK, rv);
+       assert_num_eq (count, 0);
+
+       rv = (module->C_Finalize) (NULL);
+       assert_num_eq (CKR_OK, rv);
+
+       p11_virtual_unwrap (module);
+       p11_filter_release (filter);
+}
+
+int
+main (int argc,
+      char *argv[])
+{
+       p11_library_init ();
+       mock_module_init ();
+
+       p11_test (test_allowed, "/filter/test_allowed");
+       p11_test (test_denied, "/filter/test_denied");
+
+       return p11_test_run (argc, argv);
+}