diff options
-rw-r--r-- | doc/manual/p11-kit.xml | 19 | ||||
-rw-r--r-- | tools/extract-info.c | 80 | ||||
-rw-r--r-- | tools/extract.c | 82 | ||||
-rw-r--r-- | tools/extract.h | 4 | ||||
-rw-r--r-- | tools/tests/test-extract.c | 171 |
5 files changed, 313 insertions, 43 deletions
diff --git a/doc/manual/p11-kit.xml b/doc/manual/p11-kit.xml index 9791c29..83fd47d 100644 --- a/doc/manual/p11-kit.xml +++ b/doc/manual/p11-kit.xml @@ -98,14 +98,18 @@ $ p11-kit extract --format=x509-directory --filter=ca-certificates /path/to/dire </varlistentry> <varlistentry> <term><option>--filter=<what></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> </varlistentry> @@ -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=<type></option></term> diff --git a/tools/extract-info.c b/tools/extract-info.c index 536d36a..da84bbe 100644 --- a/tools/extract-info.c +++ b/tools/extract-info.c @@ -34,6 +34,8 @@ #include "config.h" +#define P11_DEBUG_FLAG P11_DEBUG_TOOL + #include "attrs.h" #include "debug.h" #include "oid.h" @@ -147,6 +149,55 @@ extract_purposes (p11_extract_info *ex) } 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; } diff --git a/tools/extract.c b/tools/extract.c index 6bdedfe..cf08ae9 100644 --- a/tools/extract.c +++ b/tools/extract.c @@ -57,18 +57,16 @@ 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); diff --git a/tools/extract.h b/tools/extract.h index dfd3a33..85405e5 100644 --- a/tools/extract.h +++ b/tools/extract.h @@ -46,11 +46,15 @@ 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; diff --git a/tools/tests/test-extract.c b/tools/tests/test-extract.c index 69ba764..c7382cd 100644 --- a/tools/tests/test-extract.c +++ b/tools/tests/test-extract.c @@ -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); |