]> granicus.if.org Git - p11-kit/commitdiff
extract: Combine trust policy when extracting
authorStef Walter <stefw@gnome.org>
Fri, 15 Mar 2013 08:22:57 +0000 (09:22 +0100)
committerStef Walter <stefw@gnome.org>
Fri, 15 Mar 2013 16:44:32 +0000 (17:44 +0100)
 * Collapse multiple identical certificates coming from different
   tokens. Note that if a certificate should not be placed multiple
   times on a token. We cannot know which one to respect.
 * Add a new extract filter: --trust-policy
   This extracts all anchor and blacklist information

https://bugs.freedesktop.org/show_bug.cgi?id=61497

doc/manual/p11-kit.xml
tools/extract-info.c
tools/extract.c
tools/extract.h
tools/tests/test-extract.c

index 9791c2983e62c5997f74d12d6669995b4f6c113f..83fd47d446d90a6c2e31949cfbb60b4710cf3e1f 100644 (file)
@@ -98,13 +98,17 @@ $ p11-kit extract --format=x509-directory --filter=ca-certificates /path/to/dire
                </varlistentry>
                <varlistentry>
                        <term><option>--filter=&lt;what&gt;</option></term>
-                       <listitem><para>Specifies what certificates to export.
-                       You can specify the following values:
+                       <listitem>
+                       <para>Specifies what certificates to extract. You can specify the following values:
                        <variablelist>
                                <varlistentry>
                                        <term><option>ca-anchors</option></term>
                                        <listitem><para>Certificate anchors (default)</para></listitem>
                                </varlistentry>
+                               <varlistentry>
+                                       <term><option>trust-policy</option></term>
+                                       <listitem><para>Anchors and blacklist</para></listitem>
+                               </varlistentry>
                                <varlistentry>
                                        <term><option>blacklist</option></term>
                                        <listitem><para>Blacklisted certificates</para></listitem>
@@ -118,7 +122,16 @@ $ p11-kit extract --format=x509-directory --filter=ca-certificates /path/to/dire
                                        <listitem><para>A PKCS#11 URI</para></listitem>
                                </varlistentry>
                        </variablelist>
-                       </para></listitem>
+                       </para>
+
+                       <para>If an output format is chosen that cannot support type what has been
+                       specified by the filter, a message will be printed.</para>
+
+                       <para>None of the available formats support storage of blacklist entries
+                       that do not contain a full certificate. Thus any certificates blacklisted by
+                       their issuer and serial number alone, are not included in the extracted
+                       blacklist.</para>
+                       </listitem>
                </varlistentry>
                <varlistentry>
                        <term><option>--format=&lt;type&gt;</option></term>
index 536d36a2ebb343eb297e6f85d506aa63371d8966..da84bbe653fe4887200405dae1689df73ba02f15 100644 (file)
@@ -34,6 +34,8 @@
 
 #include "config.h"
 
+#define P11_DEBUG_FLAG P11_DEBUG_TOOL
+
 #include "attrs.h"
 #include "debug.h"
 #include "oid.h"
@@ -146,6 +148,55 @@ extract_purposes (p11_extract_info *ex)
        return ex->purposes != NULL;
 }
 
+static bool
+should_collapse_certificate (p11_extract_info *ex,
+                             CK_ATTRIBUTE *value)
+{
+       CK_ATTRIBUTE *attrs;
+
+       if (!(ex->flags & P11_EXTRACT_COLLAPSE))
+               return false;
+
+       if (!ex->already_seen) {
+               ex->already_seen = p11_dict_new (p11_attr_hash, p11_attr_equal,
+                                                NULL, p11_attrs_free);
+               return_val_if_fail (ex->already_seen != NULL, true);
+       }
+
+       if (p11_dict_get (ex->already_seen, value))
+               return true;
+
+       attrs = p11_attrs_build (NULL, value, NULL);
+       return_val_if_fail (attrs != NULL, true);
+
+       if (!p11_dict_set (ex->already_seen, attrs, attrs))
+               return_val_if_reached (true);
+
+       return false;
+}
+
+static bool
+check_trust_flags_match (p11_extract_info *ex)
+{
+       CK_BBOOL boolv;
+       int flags = 0;
+
+       /* If no extract trust flags, then just continue */
+       if (!(ex->flags & (P11_EXTRACT_ANCHORS | P11_EXTRACT_BLACKLIST)))
+               return true;
+
+       if (p11_attrs_find_bool (ex->attrs, CKA_TRUSTED, &boolv) && boolv)
+               flags |= P11_EXTRACT_ANCHORS;
+       if (p11_attrs_find_bool (ex->attrs, CKA_X_DISTRUSTED, &boolv) && boolv)
+               flags |= P11_EXTRACT_BLACKLIST;
+
+       /* Any of the flags can match */
+       if (flags & ex->flags)
+               return true;
+
+       return false;
+}
+
 static bool
 extract_certificate (P11KitIter *iter,
                      p11_extract_info *ex)
@@ -157,12 +208,34 @@ extract_certificate (P11KitIter *iter,
        /* Don't even bother with not X.509 certificates */
        if (!p11_attrs_find_ulong (ex->attrs, CKA_CERTIFICATE_TYPE, &type))
                type = (CK_ULONG)-1;
-       if (type != CKC_X_509)
+       if (type != CKC_X_509) {
+               p11_debug ("skipping non X.509 certificate");
                return false;
+       }
 
        attr = p11_attrs_find_valid (ex->attrs, CKA_VALUE);
-       if (!attr || !attr->pValue)
+       if (!attr || !attr->pValue) {
+               p11_debug ("skipping certificate without a value");
                return false;
+       }
+
+       /*
+        * If collapsing and have already seen this certificate, and shouldn't
+        * process it even again during this extract procedure.
+        */
+       if (should_collapse_certificate (ex, attr)) {
+               p11_debug ("skipping certificate that has already been seen");
+               return false;
+       }
+
+       /*
+        * We do these checks after collapsing, so that blacklisted certificates
+        * mask out anchors even if we're not exporting blacklisted stuff.
+        */
+       if (!check_trust_flags_match (ex)) {
+               p11_debug ("skipping certificate that doesn't match trust flags");
+               return false;
+       }
 
        ex->cert_der = attr->pValue;
        ex->cert_len = attr->ulValueLen;
@@ -308,6 +381,9 @@ p11_extract_info_cleanup (p11_extract_info *ex)
        p11_dict_free (ex->limit_to_purposes);
        ex->limit_to_purposes = NULL;
 
+       p11_dict_free (ex->already_seen);
+       ex->already_seen = NULL;
+
        p11_dict_free (ex->asn1_defs);
        ex->asn1_defs = NULL;
 }
index 6bdedfe0ffb394bd7563eba5427c2968ab40c974..cf08ae978bb7b53b6f7f0fde87cbddba2b4a0aab 100644 (file)
 static bool
 filter_argument (const char *optarg,
                  P11KitUri **uri,
-                 CK_ATTRIBUTE **match)
+                 CK_ATTRIBUTE **match,
+                 int *flags)
 {
        CK_ATTRIBUTE *attrs;
        int ret;
 
-       CK_BBOOL vtrue = CK_TRUE;
        CK_OBJECT_CLASS vcertificate = CKO_CERTIFICATE;
        CK_ULONG vauthority = 2;
        CK_CERTIFICATE_TYPE vx509 = CKC_X_509;
 
-       CK_ATTRIBUTE trusted = { CKA_TRUSTED, &vtrue, sizeof (vtrue) };
-       CK_ATTRIBUTE distrusted = { CKA_X_DISTRUSTED, &vtrue, sizeof (vtrue) };
        CK_ATTRIBUTE certificate = { CKA_CLASS, &vcertificate, sizeof (vcertificate) };
        CK_ATTRIBUTE authority = { CKA_CERTIFICATE_CATEGORY, &vauthority, sizeof (vauthority) };
        CK_ATTRIBUTE x509 = { CKA_CERTIFICATE_TYPE, &vx509, sizeof (vx509) };
@@ -88,13 +86,20 @@ filter_argument (const char *optarg,
        }
 
        if (strcmp (optarg, "ca-anchors") == 0) {
-               attrs = p11_attrs_build (NULL, &trusted, &certificate, &authority, &x509, NULL);
+               attrs = p11_attrs_build (NULL, &certificate, &authority, &x509, NULL);
+               *flags |= P11_EXTRACT_ANCHORS | P11_EXTRACT_COLLAPSE;
+
+       } else if (strcmp (optarg, "trust-policy") == 0) {
+               attrs = p11_attrs_build (NULL, &certificate, &x509, NULL);
+               *flags |= P11_EXTRACT_ANCHORS | P11_EXTRACT_BLACKLIST | P11_EXTRACT_COLLAPSE;
 
        } else if (strcmp (optarg, "blacklist") == 0) {
-               attrs = p11_attrs_build (NULL, &distrusted, &certificate, &x509, NULL);
+               attrs = p11_attrs_build (NULL, &certificate, &x509, NULL);
+               *flags |= P11_EXTRACT_BLACKLIST | P11_EXTRACT_COLLAPSE;
 
        } else if (strcmp (optarg, "certificates") == 0) {
                attrs = p11_attrs_build (NULL, &certificate, &x509, NULL);
+               *flags |= P11_EXTRACT_COLLAPSE;
 
        } else {
                p11_message ("unsupported or unrecognized filter: %s", optarg);
@@ -205,7 +210,7 @@ format_argument (const char *optarg,
 
 static void
 limit_modules_if_necessary (CK_FUNCTION_LIST_PTR *modules,
-                            CK_ATTRIBUTE *match)
+                            int flags)
 {
        char *string;
        int i, out;
@@ -215,8 +220,7 @@ limit_modules_if_necessary (CK_FUNCTION_LIST_PTR *modules,
         * we get from modules explicitly marked as containing trust-policy.
         */
 
-       if (!p11_attrs_find (match, CKA_TRUSTED) &&
-           !p11_attrs_find (match, CKA_X_DISTRUSTED))
+       if ((flags & (P11_EXTRACT_ANCHORS | P11_EXTRACT_BLACKLIST)) == 0)
                return;
 
        /* Count the number of modules */
@@ -239,10 +243,10 @@ limit_modules_if_necessary (CK_FUNCTION_LIST_PTR *modules,
                p11_message ("no modules containing trust policy are registered");
 }
 
-static void
-limit_purposes_if_necessary (p11_extract_info *ex,
-                             p11_extract_func func,
-                             CK_ATTRIBUTE *match)
+static bool
+validate_filter_and_format (p11_extract_info *ex,
+                            p11_extract_func func,
+                            CK_ATTRIBUTE *match)
 {
        int i;
 
@@ -253,27 +257,41 @@ limit_purposes_if_necessary (p11_extract_info *ex,
         * default purpose to limit to.
         */
 
-       static p11_extract_func format_supports_purposes[] = {
+       static p11_extract_func supports_trust_policy[] = {
                p11_extract_openssl_bundle,
                p11_extract_openssl_directory,
                NULL
        };
 
-       /* Check if looking for anchors */
-       if (!p11_attrs_find (match, CKA_TRUSTED))
-               return;
+       for (i = 0; supports_trust_policy[i] != NULL; i++) {
+               if (func == supports_trust_policy[i])
+                       return true;
+       }
 
-       /* Already limiting to one or more purposes */
-       if (ex->limit_to_purposes)
-               return;
+       if ((ex->flags & P11_EXTRACT_ANCHORS) &&
+           (ex->flags & P11_EXTRACT_BLACKLIST)) {
+               /*
+                * If we're extracting *both* anchors and blacklist, then we must have
+                * a format that can represent the different types of information.
+                */
 
-       for (i = 0; format_supports_purposes[i] != NULL; i++) {
-               if (func == format_supports_purposes[i])
-                       return;
+               p11_message ("format does not support trust policy");
+               return false;
+
+       } else if (ex->flags & P11_EXTRACT_ANCHORS) {
+
+               /*
+                * If we're extracting anchors, then we must have either limited the
+                * purposes, or have a format that can represent multiple purposes.
+                */
+
+               if (!ex->limit_to_purposes) {
+                       p11_message ("format does not support multiple purposes, defaulting to 'server-auth'");
+                       p11_extract_info_limit_purpose (ex, P11_OID_SERVER_AUTH_STR);
+               }
        }
 
-       p11_message ("format does not support trust policy, limiting to purpose server-auth");
-       p11_extract_info_limit_purpose (ex, P11_OID_SERVER_AUTH_STR);
+       return true;
 }
 
 int
@@ -319,6 +337,7 @@ p11_tool_extract (int argc,
                  "filter of what to export\n"
                  "  ca-anchors        certificate anchors (default)\n"
                  "  blacklist         blacklisted certificates\n"
+                 "  trust-policy      anchors and blacklist\n"
                  "  certificates      all certificates\n"
                  "  pkcs11:object=xx  a PKCS#11 URI",
                  "what",
@@ -368,7 +387,7 @@ p11_tool_extract (int argc,
                        ex.flags |= P11_EXTRACT_COMMENT;
                        break;
                case opt_filter:
-                       if (!filter_argument (optarg, &uri, &match))
+                       if (!filter_argument (optarg, &uri, &match, &ex.flags))
                                return 2;
                        break;
                case opt_purpose:
@@ -406,10 +425,13 @@ p11_tool_extract (int argc,
 
        /* If nothing that was useful to enumerate was specified, then bail */
        if (uri == NULL && match == NULL) {
-               p11_message ("no filter specified defaulting to 'ca-anchors'");
-               filter_argument ("ca-anchors", &uri, &match);
+               p11_message ("no filter specified, defaulting to 'ca-anchors'");
+               filter_argument ("ca-anchors", &uri, &match, &ex.flags);
        }
 
+       if (!validate_filter_and_format (&ex, format, match))
+               return 1;
+
        if (uri && p11_kit_uri_any_unrecognized (uri))
                p11_message ("uri contained unrecognized components, nothing will be extracted");
 
@@ -420,9 +442,7 @@ p11_tool_extract (int argc,
        }
 
        modules = p11_kit_registered_modules ();
-
-       limit_purposes_if_necessary (&ex, format, match);
-       limit_modules_if_necessary (modules, match);
+       limit_modules_if_necessary (modules, ex.flags);
 
        iter = p11_kit_iter_new (uri);
 
index dfd3a3341a324cd7e547fa0a7e580db980d81338..85405e5fc2abc420a4caf02741b1eca115f3dd52 100644 (file)
 enum {
        /* These overlap with the flags in save.h, so start higher */
        P11_EXTRACT_COMMENT = 1 << 10,
+       P11_EXTRACT_ANCHORS = 1 << 11,
+       P11_EXTRACT_BLACKLIST = 1 << 12,
+       P11_EXTRACT_COLLAPSE = 1 << 13,
 };
 
 typedef struct {
        p11_dict *asn1_defs;
        p11_dict *limit_to_purposes;
+       p11_dict *already_seen;
        char *destination;
        int flags;
 
index 69ba76403418d54b5a747dc7a869ef270efdd1cb..c7382cd5cebc47002de1b4553cd128e67d52eba5 100644 (file)
@@ -171,17 +171,29 @@ teardown (CuTest *tc)
 static CK_OBJECT_CLASS certificate_class = CKO_CERTIFICATE;
 static CK_OBJECT_CLASS extension_class = CKO_X_CERTIFICATE_EXTENSION;
 static CK_CERTIFICATE_TYPE x509_type = CKC_X_509;
+static CK_BBOOL truev = CK_TRUE;
 
-static CK_ATTRIBUTE cacert3_authority_attrs[] = {
+static CK_ATTRIBUTE cacert3_trusted[] = {
        { CKA_VALUE, (void *)test_cacert3_ca_der, sizeof (test_cacert3_ca_der) },
        { CKA_CLASS, &certificate_class, sizeof (certificate_class) },
        { CKA_CERTIFICATE_TYPE, &x509_type, sizeof (x509_type) },
        { CKA_LABEL, "Cacert3 Here", 11 },
        { CKA_SUBJECT, (void *)test_cacert3_ca_subject, sizeof (test_cacert3_ca_subject) },
+       { CKA_TRUSTED, &truev, sizeof (truev) },
        { CKA_ID, "ID1", 3 },
        { CKA_INVALID },
 };
 
+static CK_ATTRIBUTE cacert3_distrusted[] = {
+       { CKA_VALUE, (void *)test_cacert3_ca_der, sizeof (test_cacert3_ca_der) },
+       { CKA_CLASS, &certificate_class, sizeof (certificate_class) },
+       { CKA_CERTIFICATE_TYPE, &x509_type, sizeof (x509_type) },
+       { CKA_LABEL, "Another CaCert", 11 },
+       { CKA_SUBJECT, (void *)test_cacert3_ca_subject, sizeof (test_cacert3_ca_subject) },
+       { CKA_X_DISTRUSTED, &truev, sizeof (truev) },
+       { CKA_INVALID },
+};
+
 static CK_ATTRIBUTE certificate_filter[] = {
        { CKA_CLASS, &certificate_class, sizeof (certificate_class) },
        { CKA_INVALID },
@@ -213,7 +225,7 @@ test_info_simple_certificate (CuTest *tc)
 
        CuAssertPtrNotNull (tc, test.ex.asn1_defs);
 
-       mock_module_add_object (MOCK_SLOT_ONE_ID, cacert3_authority_attrs);
+       mock_module_add_object (MOCK_SLOT_ONE_ID, cacert3_trusted);
        mock_module_add_object (MOCK_SLOT_ONE_ID, extension_eku_server_client);
 
        p11_kit_iter_add_callback (test.iter, p11_extract_info_load_filter, &test.ex, NULL);
@@ -245,7 +257,7 @@ test_info_limit_purposes (CuTest *tc)
 
        setup (tc);
 
-       mock_module_add_object (MOCK_SLOT_ONE_ID, cacert3_authority_attrs);
+       mock_module_add_object (MOCK_SLOT_ONE_ID, cacert3_trusted);
        mock_module_add_object (MOCK_SLOT_ONE_ID, extension_eku_server_client);
 
        /* This should not match the above, with the stapled certificat ext */
@@ -270,7 +282,7 @@ test_info_invalid_purposes (CuTest *tc)
 
        setup (tc);
 
-       mock_module_add_object (MOCK_SLOT_ONE_ID, cacert3_authority_attrs);
+       mock_module_add_object (MOCK_SLOT_ONE_ID, cacert3_trusted);
        mock_module_add_object (MOCK_SLOT_ONE_ID, extension_eku_invalid);
 
        p11_kit_iter_add_callback (test.iter, p11_extract_info_load_filter, &test.ex, NULL);
@@ -295,7 +307,7 @@ test_info_skip_non_certificate (CuTest *tc)
 
        setup (tc);
 
-       mock_module_add_object (MOCK_SLOT_ONE_ID, cacert3_authority_attrs);
+       mock_module_add_object (MOCK_SLOT_ONE_ID, cacert3_trusted);
 
        p11_kit_iter_add_callback (test.iter, p11_extract_info_load_filter, &test.ex, NULL);
        p11_kit_iter_begin_with (test.iter, &test.module, 0, 0);
@@ -322,7 +334,7 @@ test_limit_to_purpose_match (CuTest *tc)
 
        setup (tc);
 
-       mock_module_add_object (MOCK_SLOT_ONE_ID, cacert3_authority_attrs);
+       mock_module_add_object (MOCK_SLOT_ONE_ID, cacert3_trusted);
        mock_module_add_object (MOCK_SLOT_ONE_ID, extension_eku_server_client);
 
        p11_extract_info_limit_purpose (&test.ex, P11_OID_SERVER_AUTH_STR);
@@ -346,7 +358,7 @@ test_limit_to_purpose_no_match (CuTest *tc)
 
        setup (tc);
 
-       mock_module_add_object (MOCK_SLOT_ONE_ID, cacert3_authority_attrs);
+       mock_module_add_object (MOCK_SLOT_ONE_ID, cacert3_trusted);
        mock_module_add_object (MOCK_SLOT_ONE_ID, extension_eku_server_client);
 
        p11_extract_info_limit_purpose (&test.ex, "3.3.3.3");
@@ -363,6 +375,146 @@ test_limit_to_purpose_no_match (CuTest *tc)
        teardown (tc);
 }
 
+static void
+test_duplicate_extract (CuTest *tc)
+{
+       CK_ATTRIBUTE certificate = { CKA_CLASS, &certificate_class, sizeof (certificate_class) };
+       CK_RV rv;
+
+       setup (tc);
+
+       mock_module_add_object (MOCK_SLOT_ONE_ID, cacert3_trusted);
+       mock_module_add_object (MOCK_SLOT_ONE_ID, cacert3_distrusted);
+
+       p11_kit_iter_add_callback (test.iter, p11_extract_info_load_filter, &test.ex, NULL);
+       p11_kit_iter_add_filter (test.iter, &certificate, 1);
+       p11_kit_iter_begin_with (test.iter, &test.module, 0, 0);
+
+       rv = p11_kit_iter_next (test.iter);
+       CuAssertIntEquals (tc, CKR_OK, rv);
+
+       rv = p11_kit_iter_next (test.iter);
+       CuAssertIntEquals (tc, CKR_OK, rv);
+
+       rv = p11_kit_iter_next (test.iter);
+       CuAssertIntEquals (tc, CKR_CANCEL, rv);
+
+       teardown (tc);
+}
+
+static void
+test_duplicate_collapse (CuTest *tc)
+{
+       CK_ATTRIBUTE certificate = { CKA_CLASS, &certificate_class, sizeof (certificate_class) };
+       CK_RV rv;
+
+       setup (tc);
+
+       mock_module_add_object (MOCK_SLOT_ONE_ID, cacert3_trusted);
+       mock_module_add_object (MOCK_SLOT_ONE_ID, cacert3_distrusted);
+
+       test.ex.flags = P11_EXTRACT_COLLAPSE;
+       p11_kit_iter_add_callback (test.iter, p11_extract_info_load_filter, &test.ex, NULL);
+       p11_kit_iter_add_filter (test.iter, &certificate, 1);
+       p11_kit_iter_begin_with (test.iter, &test.module, 0, 0);
+
+       rv = p11_kit_iter_next (test.iter);
+       CuAssertIntEquals (tc, CKR_OK, rv);
+
+       rv = p11_kit_iter_next (test.iter);
+       CuAssertIntEquals (tc, CKR_CANCEL, rv);
+
+       teardown (tc);
+}
+
+static void
+test_trusted_match (CuTest *tc)
+{
+       CK_ATTRIBUTE certificate = { CKA_CLASS, &certificate_class, sizeof (certificate_class) };
+       CK_BBOOL boolv;
+       CK_RV rv;
+
+       setup (tc);
+
+       mock_module_add_object (MOCK_SLOT_ONE_ID, cacert3_trusted);
+       mock_module_add_object (MOCK_SLOT_ONE_ID, cacert3_distrusted);
+
+       test.ex.flags = P11_EXTRACT_ANCHORS;
+       p11_kit_iter_add_callback (test.iter, p11_extract_info_load_filter, &test.ex, NULL);
+       p11_kit_iter_add_filter (test.iter, &certificate, 1);
+       p11_kit_iter_begin_with (test.iter, &test.module, 0, 0);
+
+       rv = p11_kit_iter_next (test.iter);
+       CuAssertIntEquals (tc, CKR_OK, rv);
+
+       if (!p11_attrs_find_bool (test.ex.attrs, CKA_TRUSTED, &boolv))
+               boolv = CK_FALSE;
+       CuAssertIntEquals (tc, CK_TRUE, boolv);
+
+       rv = p11_kit_iter_next (test.iter);
+       CuAssertIntEquals (tc, CKR_CANCEL, rv);
+
+       teardown (tc);
+}
+
+static void
+test_distrust_match (CuTest *tc)
+{
+       CK_ATTRIBUTE certificate = { CKA_CLASS, &certificate_class, sizeof (certificate_class) };
+       CK_BBOOL boolv;
+       CK_RV rv;
+
+       setup (tc);
+
+       mock_module_add_object (MOCK_SLOT_ONE_ID, cacert3_trusted);
+       mock_module_add_object (MOCK_SLOT_ONE_ID, cacert3_distrusted);
+
+       test.ex.flags = P11_EXTRACT_BLACKLIST;
+       p11_kit_iter_add_callback (test.iter, p11_extract_info_load_filter, &test.ex, NULL);
+       p11_kit_iter_add_filter (test.iter, &certificate, 1);
+       p11_kit_iter_begin_with (test.iter, &test.module, 0, 0);
+
+       rv = p11_kit_iter_next (test.iter);
+       CuAssertIntEquals (tc, CKR_OK, rv);
+
+       if (!p11_attrs_find_bool (test.ex.attrs, CKA_X_DISTRUSTED, &boolv))
+               boolv = CK_FALSE;
+       CuAssertIntEquals (tc, CK_TRUE, boolv);
+
+       rv = p11_kit_iter_next (test.iter);
+       CuAssertIntEquals (tc, CKR_CANCEL, rv);
+
+       teardown (tc);
+}
+
+static void
+test_anytrust_match (CuTest *tc)
+{
+       CK_ATTRIBUTE certificate = { CKA_CLASS, &certificate_class, sizeof (certificate_class) };
+       CK_RV rv;
+
+       setup (tc);
+
+       mock_module_add_object (MOCK_SLOT_ONE_ID, cacert3_trusted);
+       mock_module_add_object (MOCK_SLOT_ONE_ID, cacert3_distrusted);
+
+       test.ex.flags =  P11_EXTRACT_ANCHORS | P11_EXTRACT_BLACKLIST;
+       p11_kit_iter_add_callback (test.iter, p11_extract_info_load_filter, &test.ex, NULL);
+       p11_kit_iter_add_filter (test.iter, &certificate, 1);
+       p11_kit_iter_begin_with (test.iter, &test.module, 0, 0);
+
+       rv = p11_kit_iter_next (test.iter);
+       CuAssertIntEquals (tc, CKR_OK, rv);
+
+       rv = p11_kit_iter_next (test.iter);
+       CuAssertIntEquals (tc, CKR_OK, rv);
+
+       rv = p11_kit_iter_next (test.iter);
+       CuAssertIntEquals (tc, CKR_CANCEL, rv);
+
+       teardown (tc);
+}
+
 int
 main (void)
 {
@@ -385,6 +537,11 @@ main (void)
        SUITE_ADD_TEST (suite, test_info_skip_non_certificate);
        SUITE_ADD_TEST (suite, test_limit_to_purpose_match);
        SUITE_ADD_TEST (suite, test_limit_to_purpose_no_match);
+       SUITE_ADD_TEST (suite, test_duplicate_extract);
+       SUITE_ADD_TEST (suite, test_duplicate_collapse);
+       SUITE_ADD_TEST (suite, test_trusted_match);
+       SUITE_ADD_TEST (suite, test_distrust_match);
+       SUITE_ADD_TEST (suite, test_anytrust_match);
 
        CuSuiteRun (suite);
        CuSuiteSummary (suite, output);