From 885646acd317f4c7e4be13756c7167b8494f8aef Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 10 Oct 2014 15:23:21 +0200 Subject: [PATCH 1/8] constraints: Add certificate policy and policy mapping unit tests --- src/libstrongswan/tests/Makefile.am | 1 + .../tests/suites/test_certpolicy.c | 470 ++++++++++++++++++ src/libstrongswan/tests/tests.h | 1 + 3 files changed, 472 insertions(+) create mode 100644 src/libstrongswan/tests/suites/test_certpolicy.c diff --git a/src/libstrongswan/tests/Makefile.am b/src/libstrongswan/tests/Makefile.am index 7ecba19da..1f118cc5b 100644 --- a/src/libstrongswan/tests/Makefile.am +++ b/src/libstrongswan/tests/Makefile.am @@ -40,6 +40,7 @@ tests_SOURCES = tests.h tests.c \ suites/test_array.c \ suites/test_ecdsa.c \ suites/test_rsa.c \ + suites/test_certpolicy.c \ suites/test_host.c \ suites/test_hasher.c \ suites/test_crypter.c \ diff --git a/src/libstrongswan/tests/suites/test_certpolicy.c b/src/libstrongswan/tests/suites/test_certpolicy.c new file mode 100644 index 000000000..88267222e --- /dev/null +++ b/src/libstrongswan/tests/suites/test_certpolicy.c @@ -0,0 +1,470 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "test_suite.h" + +#include +#include +#include + +/** + * RSA private key, so we don't have to generate one + */ +static char keydata[] = { + 0x30,0x82,0x02,0x5e,0x02,0x01,0x00,0x02,0x81,0x81,0x00,0xb1,0x9b,0xd4,0x51,0x24, + 0xfc,0x56,0x1d,0x3d,0xfb,0xa2,0xea,0x37,0x02,0x70,0x72,0x87,0x84,0x2f,0x3b,0x2d, + 0x6e,0x22,0xef,0x3f,0x37,0x04,0xb2,0x6f,0xb7,0xe7,0xd8,0x58,0x05,0xde,0x34,0xbf, + 0x99,0xe6,0x40,0x7a,0x56,0xa7,0x73,0xf5,0x98,0xcb,0xb0,0x37,0x90,0x5e,0xd1,0x3f, + 0xf4,0x73,0x50,0x7f,0x53,0x8e,0xf1,0x04,0x25,0xb4,0x77,0x22,0x4e,0x8a,0x9d,0x27, + 0x8f,0x6f,0xaf,0x59,0xbd,0xb0,0x0f,0xf0,0xaa,0x11,0x94,0x66,0x16,0x10,0x58,0xad, + 0x77,0xa1,0xac,0x58,0xb4,0xd0,0x0d,0xbc,0x11,0xe0,0xc0,0xe9,0x29,0xdc,0x42,0x63, + 0x01,0x23,0x4f,0x28,0x41,0x6d,0x34,0x9e,0x0c,0x4a,0xc8,0x62,0x83,0xb5,0x71,0x71, + 0x0b,0x51,0xc0,0x4c,0x37,0xd4,0x68,0x19,0x52,0x9a,0x8b,0x02,0x03,0x01,0x00,0x01, + 0x02,0x81,0x81,0x00,0x82,0xca,0x33,0x16,0xb2,0x3a,0xd4,0x1b,0x62,0x9a,0x9c,0xc5, + 0x07,0x4f,0x57,0x89,0x2f,0x7c,0x4a,0xdf,0xb4,0x3b,0xc7,0xa4,0x11,0x14,0x2d,0xf4, + 0x4c,0xca,0xcc,0x03,0x88,0x06,0x82,0x34,0xab,0xe7,0xe4,0x24,0x15,0x33,0x1c,0xcb, + 0x0a,0xcf,0xc3,0x27,0x78,0x33,0x6b,0x6f,0x82,0x3e,0x3c,0x70,0xc9,0xe2,0xb9,0x7f, + 0x88,0xc3,0x4f,0x59,0xb5,0x8e,0xa3,0x81,0xd9,0x88,0x1f,0xc0,0x38,0xbc,0xc8,0x93, + 0x40,0x0f,0x43,0xd8,0x72,0x12,0xb4,0xcc,0x6d,0x76,0x0a,0x6f,0x01,0x05,0xa8,0x88, + 0xf4,0x57,0x44,0xd2,0x05,0xc4,0x77,0xf5,0xfb,0x1b,0xf3,0xb2,0x0d,0x90,0xb8,0xb4, + 0x63,0x62,0x70,0x2c,0xe4,0x28,0xd8,0x20,0x10,0x85,0x4a,0x5e,0x63,0xa9,0xb0,0xdd, + 0xba,0xd0,0x32,0x49,0x02,0x41,0x00,0xdb,0x77,0xf1,0xdd,0x1a,0x12,0xc5,0xfb,0x2b, + 0x5b,0xb2,0xcd,0xb6,0xd0,0x4c,0xc4,0xe5,0x93,0xd6,0xf8,0x88,0xfc,0x18,0x40,0x21, + 0x9c,0xf7,0x2d,0x60,0x6f,0x91,0xf5,0x73,0x3c,0xf7,0x7f,0x67,0x1d,0x5b,0xb5,0xee, + 0x29,0xc1,0xd4,0xc6,0xdb,0x44,0x4c,0x40,0x05,0x63,0xaa,0x71,0x95,0x18,0x14,0xa7, + 0x23,0x9f,0x7a,0xee,0x7f,0xb5,0xc7,0x02,0x41,0x00,0xcf,0x2c,0x24,0x50,0x65,0xf4, + 0x94,0x7b,0xe9,0xf3,0x13,0x77,0xea,0x27,0x3c,0x6f,0x03,0x84,0xa7,0x7d,0xa2,0x54, + 0x40,0x97,0x82,0x0e,0xd9,0x09,0x9f,0x4a,0xa6,0x75,0xe5,0x66,0xe4,0x9c,0x59,0xd9, + 0x3a,0xe6,0xf7,0xd8,0x8b,0x68,0xb0,0x21,0x52,0x31,0xb3,0x4a,0xa0,0x2c,0x41,0xd7, + 0x1f,0x7b,0xe2,0x0f,0x15,0xc9,0x6e,0xc0,0xe5,0x1d,0x02,0x41,0x00,0x9c,0x1a,0x61, + 0x9f,0x89,0xc7,0x26,0xa9,0x33,0xba,0xe2,0xa0,0x6d,0xd3,0x15,0x77,0xcb,0x6f,0xef, + 0xad,0x12,0x0a,0x75,0xd9,0x4f,0xcf,0x4d,0x05,0x2a,0x9d,0xd1,0x2c,0xcb,0xcd,0xe6, + 0xa0,0xe9,0x20,0x39,0xb6,0x5a,0xf3,0xba,0x99,0xf4,0xe3,0xcb,0x5d,0x8d,0x00,0x08, + 0x57,0x18,0xb9,0x1a,0xca,0xbd,0xe3,0x99,0xb1,0x1f,0xe9,0x18,0xcb,0x02,0x40,0x65, + 0x35,0x1b,0x48,0x6b,0x86,0x60,0x43,0x68,0xb6,0xe6,0xfb,0xdd,0xd7,0xed,0x1e,0x0e, + 0x89,0xef,0x88,0xe0,0x94,0x68,0x39,0x9b,0xbf,0xc5,0x27,0x7e,0x39,0xe9,0xb8,0x0e, + 0xa9,0x85,0x65,0x1c,0x3f,0x93,0x16,0xe2,0x5d,0x57,0x3d,0x7d,0x4d,0xc9,0xe9,0x9d, + 0xbd,0x07,0x22,0x97,0xc7,0x90,0x09,0xe5,0x15,0x99,0x7f,0x1e,0x2b,0xfd,0xc1,0x02, + 0x41,0x00,0x92,0x78,0xfe,0x04,0xa0,0x53,0xed,0x36,0x97,0xbd,0x16,0xce,0x91,0x9b, + 0xbe,0x1f,0x8e,0x40,0x00,0x99,0x0c,0x49,0x15,0xca,0x59,0xd3,0xe3,0xd4,0xeb,0x71, + 0xcf,0xda,0xd7,0xc8,0x99,0x74,0xfc,0x6b,0xe8,0xfd,0xe5,0xe0,0x49,0x61,0xcb,0xda, + 0xe3,0xe7,0x8b,0x72,0xb5,0x69,0x73,0x2b,0x8b,0x54,0xcb,0xd9,0x48,0x6d,0x61,0x02, + 0x49,0xe8, +}; + +/** + * Issue a certificate with given certificate policy and flags + */ +static certificate_t* create_cert(certificate_t *ca, char *subject, + char *oid, x509_flag_t flags, + char *map_s, char *map_i) +{ + private_key_t *privkey; + public_key_t *pubkey; + certificate_t *cert; + identification_t *id; + linked_list_t *policies, *maps; + x509_cert_policy_t policy = {}; + x509_policy_mapping_t map = {}; + + privkey = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_RSA, + BUILD_BLOB_ASN1_DER, chunk_from_thing(keydata), + BUILD_END); + ck_assert(privkey); + pubkey = privkey->get_public_key(privkey); + ck_assert(pubkey); + policies = linked_list_create(); + if (oid) + { + policy.oid = asn1_oid_from_string(oid); + ck_assert(policy.oid.ptr); + policies->insert_last(policies, &policy); + } + maps = linked_list_create(); + if (map_s && map_i) + { + map.subject = asn1_oid_from_string(map_s); + ck_assert(map.subject.ptr); + map.issuer = asn1_oid_from_string(map_i); + ck_assert(map.issuer.ptr); + maps->insert_last(maps, &map); + } + id = identification_create_from_string(subject); + cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, + BUILD_SIGNING_KEY, privkey, + BUILD_PUBLIC_KEY, pubkey, + BUILD_SUBJECT, id, + BUILD_X509_FLAG, flags, + BUILD_CERTIFICATE_POLICIES, policies, + BUILD_POLICY_MAPPINGS, maps, + BUILD_SIGNING_CERT, ca, + BUILD_END); + ck_assert(cert); + id->destroy(id); + policies->destroy(policies); + maps->destroy(maps); + privkey->destroy(privkey); + pubkey->destroy(pubkey); + free(policy.oid.ptr); + free(map.subject.ptr); + free(map.issuer.ptr); + + return cert; +} + +/** + * Check if a certificate with given subject has an oid + */ +static bool check_oid(identification_t *subject, char *oid) +{ + enumerator_t *certs, *auths; + certificate_t *cert; + auth_cfg_t *auth; + bool found = FALSE; + auth_rule_t type; + char *current; + + certs = lib->credmgr->create_trusted_enumerator(lib->credmgr, KEY_ANY, + subject, FALSE); + if (!certs->enumerate(certs, &cert, &auth)) + { + certs->destroy(certs); + ck_assert_msg(FALSE, "no trusted certificate found for %Y", subject); + } + auths = auth->create_enumerator(auth); + while (auths->enumerate(auths, &type, ¤t)) + { + if (type == AUTH_RULE_CERT_POLICY) + { + if (streq(current, oid)) + { + found = TRUE; + break; + } + } + } + auths->destroy(auths); + certs->destroy(certs); + + return found; +} + +static mem_cred_t *creds; + +static char *anyPolicy = "2.5.29.32.0"; +static char *extended = "2.23.140.1.1"; +static char *baseline = "2.23.140.1.2"; + +START_SETUP(setup) +{ + creds = mem_cred_create(); + lib->credmgr->add_set(lib->credmgr, &creds->set); +} +END_SETUP + +START_TEARDOWN(teardown) +{ + lib->credmgr->remove_set(lib->credmgr, &creds->set); + creds->destroy(creds); + lib->credmgr->flush_cache(lib->credmgr, CERT_ANY); +} +END_TEARDOWN + +START_TEST(test_valid_fixed) +{ + certificate_t *ca, *im, *sj; + + ca = create_cert(NULL, "CN=CA", baseline, X509_CA, NULL, NULL); + im = create_cert(ca, "CN=IM", baseline, X509_CA, NULL, NULL); + sj = create_cert(im, "CN=SJ", baseline, 0, NULL, NULL); + + creds->add_cert(creds, TRUE, ca); + creds->add_cert(creds, FALSE, im); + creds->add_cert(creds, FALSE, sj); + + ck_assert(check_oid(sj->get_subject(sj), baseline)); +} +END_TEST + +START_TEST(test_valid_any1) +{ + certificate_t *ca, *im, *sj; + + ca = create_cert(NULL, "CN=CA", anyPolicy, X509_CA, NULL, NULL); + im = create_cert(ca, "CN=IM", baseline, X509_CA, NULL, NULL); + sj = create_cert(im, "CN=SJ", baseline, 0, NULL, NULL); + + creds->add_cert(creds, TRUE, ca); + creds->add_cert(creds, FALSE, im); + creds->add_cert(creds, FALSE, sj); + + ck_assert(check_oid(sj->get_subject(sj), baseline)); +} +END_TEST + +START_TEST(test_valid_any2) +{ + certificate_t *ca, *im, *sj; + + ca = create_cert(NULL, "CN=CA", anyPolicy, X509_CA, NULL, NULL); + im = create_cert(ca, "CN=IM", anyPolicy, X509_CA, NULL, NULL); + sj = create_cert(im, "CN=SJ", baseline, 0, NULL, NULL); + + creds->add_cert(creds, TRUE, ca); + creds->add_cert(creds, FALSE, im); + creds->add_cert(creds, FALSE, sj); + + ck_assert(check_oid(sj->get_subject(sj), baseline)); +} +END_TEST + +START_TEST(test_invalid_missing) +{ + certificate_t *ca, *im, *sj; + + ca = create_cert(NULL, "CN=CA", baseline, X509_CA, NULL, NULL); + im = create_cert(ca, "CN=IM", baseline, X509_CA, NULL, NULL); + sj = create_cert(im, "CN=SJ", NULL, 0, NULL, NULL); + + creds->add_cert(creds, TRUE, ca); + creds->add_cert(creds, FALSE, im); + creds->add_cert(creds, FALSE, sj); + + ck_assert(!check_oid(sj->get_subject(sj), baseline)); +} +END_TEST + +START_TEST(test_invalid_wrong) +{ + certificate_t *ca, *im, *sj; + + ca = create_cert(NULL, "CN=CA", baseline, X509_CA, NULL, NULL); + im = create_cert(ca, "CN=IM", baseline, X509_CA, NULL, NULL); + sj = create_cert(im, "CN=SJ", baseline, 0, NULL, NULL); + + creds->add_cert(creds, TRUE, ca); + creds->add_cert(creds, FALSE, im); + creds->add_cert(creds, FALSE, sj); + + ck_assert(!check_oid(sj->get_subject(sj), extended)); +} +END_TEST + +START_TEST(test_invalid_any1) +{ + certificate_t *ca, *im, *sj; + + ca = create_cert(NULL, "CN=CA", anyPolicy, X509_CA, NULL, NULL); + im = create_cert(ca, "CN=IM", anyPolicy, X509_CA, NULL, NULL); + sj = create_cert(im, "CN=SJ", NULL, 0, NULL, NULL); + + creds->add_cert(creds, TRUE, ca); + creds->add_cert(creds, FALSE, im); + creds->add_cert(creds, FALSE, sj); + + ck_assert(!check_oid(sj->get_subject(sj), baseline)); +} +END_TEST + +START_TEST(test_invalid_any2) +{ + certificate_t *ca, *im, *sj; + + ca = create_cert(NULL, "CN=CA", anyPolicy, X509_CA, NULL, NULL); + im = create_cert(ca, "CN=IM", anyPolicy, X509_CA, NULL, NULL); + sj = create_cert(im, "CN=SJ", anyPolicy, 0, NULL, NULL); + + creds->add_cert(creds, TRUE, ca); + creds->add_cert(creds, FALSE, im); + creds->add_cert(creds, FALSE, sj); + + ck_assert(!check_oid(sj->get_subject(sj), baseline)); +} +END_TEST + +START_TEST(test_badchain_wrong) +{ + certificate_t *ca, *im, *sj; + + ca = create_cert(NULL, "CN=CA", baseline, X509_CA, NULL, NULL); + im = create_cert(ca, "CN=IM", extended, X509_CA, NULL, NULL); + sj = create_cert(im, "CN=SJ", extended, 0, NULL, NULL); + + creds->add_cert(creds, TRUE, ca); + creds->add_cert(creds, FALSE, im); + creds->add_cert(creds, FALSE, sj); + + ck_assert(!check_oid(sj->get_subject(sj), baseline)); + ck_assert(!check_oid(sj->get_subject(sj), extended)); +} +END_TEST + +START_TEST(test_badchain_gap) +{ + certificate_t *ca, *im, *sj; + + ca = create_cert(NULL, "CN=CA", baseline, X509_CA, NULL, NULL); + im = create_cert(ca, "CN=IM", NULL, X509_CA, NULL, NULL); + sj = create_cert(im, "CN=SJ", baseline, 0, NULL, NULL); + + creds->add_cert(creds, TRUE, ca); + creds->add_cert(creds, FALSE, im); + creds->add_cert(creds, FALSE, sj); + + ck_assert(!check_oid(sj->get_subject(sj), baseline)); +} +END_TEST + +START_TEST(test_badchain_any) +{ + certificate_t *ca, *im, *sj; + + ca = create_cert(NULL, "CN=CA", baseline, X509_CA, NULL, NULL); + im = create_cert(ca, "CN=IM", anyPolicy, X509_CA, NULL, NULL); + sj = create_cert(im, "CN=SJ", extended, 0, NULL, NULL); + + creds->add_cert(creds, TRUE, ca); + creds->add_cert(creds, FALSE, im); + creds->add_cert(creds, FALSE, sj); + + ck_assert(!check_oid(sj->get_subject(sj), extended)); +} +END_TEST + +START_TEST(test_valid_mapping) +{ + certificate_t *ca, *im, *sj; + + ca = create_cert(NULL, "CN=CA", anyPolicy, X509_CA, NULL, NULL); + im = create_cert(ca, "CN=IM", extended, X509_CA, baseline, extended); + sj = create_cert(im, "CN=SJ", baseline, 0, NULL, NULL); + + creds->add_cert(creds, TRUE, ca); + creds->add_cert(creds, FALSE, im); + creds->add_cert(creds, FALSE, sj); + + ck_assert(check_oid(sj->get_subject(sj), baseline)); +} +END_TEST + +START_TEST(test_valid_mapping_twice) +{ + certificate_t *ca, *im, *sj; + + ca = create_cert(NULL, "CN=CA", "2.23.140.1.3", X509_CA, + extended, "2.23.140.1.3"); + im = create_cert(ca, "CN=IM", extended, X509_CA, baseline, extended); + sj = create_cert(im, "CN=SJ", baseline, 0, NULL, NULL); + + creds->add_cert(creds, TRUE, ca); + creds->add_cert(creds, FALSE, im); + creds->add_cert(creds, FALSE, sj); + + ck_assert(check_oid(sj->get_subject(sj), baseline)); +} +END_TEST + +START_TEST(test_invalid_mapping_loop) +{ + certificate_t *ca, *im, *sj; + + ca = create_cert(NULL, "CN=CA", anyPolicy, X509_CA, NULL, NULL); + im = create_cert(ca, "CN=IM", extended, X509_CA, baseline, baseline); + sj = create_cert(im, "CN=SJ", baseline, 0, NULL, NULL); + + creds->add_cert(creds, TRUE, ca); + creds->add_cert(creds, FALSE, im); + creds->add_cert(creds, FALSE, sj); + + ck_assert(!check_oid(sj->get_subject(sj), baseline)); +} +END_TEST + +START_TEST(test_invalid_mapping_notallowed) +{ + certificate_t *ca, *im, *sj; + + ca = create_cert(NULL, "CN=CA", baseline, X509_CA, NULL, NULL); + im = create_cert(ca, "CN=IM", extended, X509_CA, baseline, extended); + sj = create_cert(im, "CN=SJ", baseline, 0, NULL, NULL); + + creds->add_cert(creds, TRUE, ca); + creds->add_cert(creds, FALSE, im); + creds->add_cert(creds, FALSE, sj); + + ck_assert(!check_oid(sj->get_subject(sj), baseline)); +} +END_TEST + +START_TEST(test_invalid_mapping_nopolicy) +{ + certificate_t *ca, *im, *sj; + + ca = create_cert(NULL, "CN=CA", baseline, X509_CA, NULL, NULL); + im = create_cert(ca, "CN=IM", "2.23.140.1.3", X509_CA, baseline, extended); + sj = create_cert(im, "CN=SJ", baseline, 0, NULL, NULL); + + creds->add_cert(creds, TRUE, ca); + creds->add_cert(creds, FALSE, im); + creds->add_cert(creds, FALSE, sj); + + ck_assert(!check_oid(sj->get_subject(sj), baseline)); +} +END_TEST + +Suite *certpolicy_suite_create() +{ + Suite *s; + TCase *tc; + + s = suite_create("certpolicy"); + + tc = tcase_create("policy valid"); + tcase_add_checked_fixture(tc, setup, teardown); + tcase_add_test(tc, test_valid_fixed); + tcase_add_test(tc, test_valid_any1); + tcase_add_test(tc, test_valid_any2); + suite_add_tcase(s, tc); + + tc = tcase_create("policy invalid"); + tcase_add_checked_fixture(tc, setup, teardown); + tcase_add_test(tc, test_invalid_missing); + tcase_add_test(tc, test_invalid_wrong); + tcase_add_test(tc, test_invalid_any1); + tcase_add_test(tc, test_invalid_any2); + suite_add_tcase(s, tc); + + tc = tcase_create("policy badchain"); + tcase_add_checked_fixture(tc, setup, teardown); + tcase_add_test(tc, test_badchain_wrong); + tcase_add_test(tc, test_badchain_gap); + tcase_add_test(tc, test_badchain_any); + suite_add_tcase(s, tc); + + tc = tcase_create("policy valid mapping"); + tcase_add_checked_fixture(tc, setup, teardown); + tcase_add_test(tc, test_valid_mapping); + tcase_add_test(tc, test_valid_mapping_twice); + suite_add_tcase(s, tc); + + tc = tcase_create("policy invalid mapping"); + tcase_add_checked_fixture(tc, setup, teardown); + tcase_add_test(tc, test_invalid_mapping_loop); + tcase_add_test(tc, test_invalid_mapping_notallowed); + tcase_add_test(tc, test_invalid_mapping_nopolicy); + suite_add_tcase(s, tc); + + return s; +} diff --git a/src/libstrongswan/tests/tests.h b/src/libstrongswan/tests/tests.h index 586227800..eff848f61 100644 --- a/src/libstrongswan/tests/tests.h +++ b/src/libstrongswan/tests/tests.h @@ -32,6 +32,7 @@ TEST_SUITE(settings_suite_create) TEST_SUITE(vectors_suite_create) TEST_SUITE_DEPEND(ecdsa_suite_create, PRIVKEY_GEN, KEY_ECDSA) TEST_SUITE_DEPEND(rsa_suite_create, PRIVKEY_GEN, KEY_RSA) +TEST_SUITE_DEPEND(certpolicy_suite_create, CERT_ENCODE, CERT_X509) TEST_SUITE(host_suite_create) TEST_SUITE(printf_suite_create) TEST_SUITE(hasher_suite_create) From 69232e2d3dd1a1bdae2dfc2f433de9b8a4ddd052 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Fri, 10 Oct 2014 16:33:56 +0200 Subject: [PATCH 2/8] constraints: Don't reject certificates with invalid certificate policies Instead of rejecting the certificate completely if a certificate has a policy OID that is actually not allowed by the issuer CA, we accept it. However, the certificate policy itself is still considered invalid, and is not returned in the auth config resulting from trust chain operations. A user must make sure to rely on the returned auth config certificate policies instead of the policies contained in the certificate; even if the certificate is valid, the policy OID itself in the certificate are not to be trusted anymore. --- .../constraints/constraints_validator.c | 132 ++++++++++++++---- 1 file changed, 102 insertions(+), 30 deletions(-) diff --git a/src/libstrongswan/plugins/constraints/constraints_validator.c b/src/libstrongswan/plugins/constraints/constraints_validator.c index 62ccc7108..b5762b54c 100644 --- a/src/libstrongswan/plugins/constraints/constraints_validator.c +++ b/src/libstrongswan/plugins/constraints/constraints_validator.c @@ -298,8 +298,7 @@ static bool has_policy(x509_t *issuer, chunk_t oid) /** * Check certificatePolicies. */ -static bool check_policy(x509_t *subject, x509_t *issuer, bool check, - auth_cfg_t *auth) +static bool check_policy(x509_t *subject, x509_t *issuer) { certificate_t *cert = (certificate_t*)subject; x509_policy_mapping_t *mapping; @@ -323,35 +322,87 @@ static bool check_policy(x509_t *subject, x509_t *issuer, bool check, } enumerator->destroy(enumerator); - if (check) + enumerator = subject->create_cert_policy_enumerator(subject); + while (enumerator->enumerate(enumerator, &policy)) { - enumerator = subject->create_cert_policy_enumerator(subject); - while (enumerator->enumerate(enumerator, &policy)) + if (!has_policy(issuer, policy->oid)) { - if (!has_policy(issuer, policy->oid)) - { - oid = asn1_oid_to_string(policy->oid); - DBG1(DBG_CFG, "policy %s missing in issuing certificate '%Y'", - oid, cert->get_issuer(cert)); - free(oid); - enumerator->destroy(enumerator); - return FALSE; - } - if (auth) - { - oid = asn1_oid_to_string(policy->oid); - if (oid) - { - auth->add(auth, AUTH_RULE_CERT_POLICY, oid); - } - } + oid = asn1_oid_to_string(policy->oid); + DBG1(DBG_CFG, "policy %s missing in issuing certificate '%Y'", + oid, cert->get_issuer(cert)); + free(oid); + enumerator->destroy(enumerator); + return FALSE; } - enumerator->destroy(enumerator); } + enumerator->destroy(enumerator); return TRUE; } +/** + * Check if a given policy is valid under a trustchain + */ +static bool is_policy_valid(linked_list_t *chain, chunk_t oid) +{ + x509_policy_mapping_t *mapping; + x509_cert_policy_t *policy; + x509_t *issuer; + enumerator_t *issuers, *policies, *mappings; + bool found = TRUE; + + issuers = chain->create_enumerator(chain); + while (issuers->enumerate(issuers, &issuer)) + { + int maxmap = 8; + + while (found) + { + found = FALSE; + + policies = issuer->create_cert_policy_enumerator(issuer); + while (policies->enumerate(policies, &policy)) + { + if (chunk_equals(oid, policy->oid) || + chunk_equals(any_policy, policy->oid)) + { + found = TRUE; + break; + } + } + policies->destroy(policies); + if (found) + { + break; + } + /* fall back to a mapped policy */ + mappings = issuer->create_policy_mapping_enumerator(issuer); + while (mappings->enumerate(mappings, &mapping)) + { + if (chunk_equals(mapping->subject, oid)) + { + oid = mapping->issuer; + found = TRUE; + break; + } + } + mappings->destroy(mappings); + if (--maxmap == 0) + { + found = FALSE; + break; + } + } + if (!found) + { + break; + } + } + issuers->destroy(issuers); + + return found; +} + /** * Check len certificates in trustchain for inherited policies */ @@ -364,7 +415,7 @@ static bool has_policy_chain(linked_list_t *chain, x509_t *subject, int len) enumerator = chain->create_enumerator(chain); while (len-- > 0 && enumerator->enumerate(enumerator, &issuer)) { - if (!check_policy(subject, issuer, TRUE, NULL)) + if (!check_policy(subject, issuer)) { valid = FALSE; break; @@ -450,6 +501,7 @@ static bool check_policy_constraints(x509_t *issuer, u_int pathlen, { if (subject->get_type(subject) == CERT_X509) { + x509_cert_policy_t *policy; enumerator_t *enumerator; linked_list_t *chain; certificate_t *cert; @@ -457,6 +509,7 @@ static bool check_policy_constraints(x509_t *issuer, u_int pathlen, x509_t *x509; int len = 0; u_int expl, inh; + char *oid; /* prepare trustchain to validate */ chain = linked_list_create(); @@ -517,6 +570,31 @@ static bool check_policy_constraints(x509_t *issuer, u_int pathlen, } enumerator->destroy(enumerator); + if (valid) + { + x509 = (x509_t*)subject; + + enumerator = x509->create_cert_policy_enumerator(x509); + while (enumerator->enumerate(enumerator, &policy)) + { + oid = asn1_oid_to_string(policy->oid); + if (oid) + { + if (is_policy_valid(chain, policy->oid)) + { + auth->add(auth, AUTH_RULE_CERT_POLICY, oid); + } + else + { + DBG1(DBG_CFG, "certificate policy %s for '%Y' " + "not allowed by trustchain, ignored", + oid, subject->get_subject(subject)); + free(oid); + } + } + } + enumerator->destroy(enumerator); + } chain->destroy(chain); } } @@ -543,12 +621,6 @@ METHOD(cert_validator_t, validate, bool, subject); return FALSE; } - if (!check_policy((x509_t*)subject, (x509_t*)issuer, !pathlen, auth)) - { - lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_POLICY_VIOLATION, - subject); - return FALSE; - } if (anchor) { if (!check_policy_constraints((x509_t*)issuer, pathlen, auth)) From af37fac9a8aade2bd39c0c8c8d3feabf14bd79de Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 14 Oct 2014 14:56:46 +0200 Subject: [PATCH 3/8] constraints: Add inhibitPolicyMapping tests --- .../tests/suites/test_certpolicy.c | 87 ++++++++++++++++++- 1 file changed, 83 insertions(+), 4 deletions(-) diff --git a/src/libstrongswan/tests/suites/test_certpolicy.c b/src/libstrongswan/tests/suites/test_certpolicy.c index 88267222e..d240db391 100644 --- a/src/libstrongswan/tests/suites/test_certpolicy.c +++ b/src/libstrongswan/tests/suites/test_certpolicy.c @@ -65,11 +65,14 @@ static char keydata[] = { }; /** - * Issue a certificate with given certificate policy and flags + * Issue a certificate fr given policy, including extended flags */ -static certificate_t* create_cert(certificate_t *ca, char *subject, - char *oid, x509_flag_t flags, - char *map_s, char *map_i) +static certificate_t* create_cert_ext(certificate_t *ca, char *subject, + char *oid, x509_flag_t flags, + char *map_s, char *map_i, + u_int require_explicit, + u_int inhibit_mapping, + u_int inhibit_any) { private_key_t *privkey; public_key_t *pubkey; @@ -110,6 +113,9 @@ static certificate_t* create_cert(certificate_t *ca, char *subject, BUILD_CERTIFICATE_POLICIES, policies, BUILD_POLICY_MAPPINGS, maps, BUILD_SIGNING_CERT, ca, + BUILD_POLICY_REQUIRE_EXPLICIT, require_explicit, + BUILD_POLICY_INHIBIT_MAPPING, inhibit_mapping, + BUILD_POLICY_INHIBIT_ANY, inhibit_any, BUILD_END); ck_assert(cert); id->destroy(id); @@ -124,6 +130,18 @@ static certificate_t* create_cert(certificate_t *ca, char *subject, return cert; } +/** + * Issue a certificate with given certificate policy and flags + */ +static certificate_t* create_cert(certificate_t *ca, char *subject, + char *oid, x509_flag_t flags, + char *map_s, char *map_i) +{ + return create_cert_ext(ca, subject, oid, flags, map_s, map_i, + X509_NO_CONSTRAINT, X509_NO_CONSTRAINT, + X509_NO_CONSTRAINT); +} + /** * Check if a certificate with given subject has an oid */ @@ -161,6 +179,23 @@ static bool check_oid(identification_t *subject, char *oid) return found; } +/** + * Check if a certificate with given subject has a valid trustchain + */ +static bool check_trust(identification_t *subject) +{ + enumerator_t *certs; + certificate_t *cert; + bool trusted; + + certs = lib->credmgr->create_trusted_enumerator(lib->credmgr, KEY_ANY, + subject, FALSE); + trusted = certs->enumerate(certs, &cert, NULL); + certs->destroy(certs); + + return trusted; +} + static mem_cred_t *creds; static char *anyPolicy = "2.5.29.32.0"; @@ -424,6 +459,44 @@ START_TEST(test_invalid_mapping_nopolicy) } END_TEST +START_TEST(test_inhibit_mapping_good) +{ + certificate_t *ca, *im, *sj; + + ca = create_cert_ext(NULL, "CN=CA", extended, X509_CA, NULL, NULL, + X509_NO_CONSTRAINT, 1, X509_NO_CONSTRAINT); + im = create_cert(ca, "CN=IM", extended, X509_CA, baseline, extended); + sj = create_cert(im, "CN=SJ", baseline, 0, NULL, NULL); + + creds->add_cert(creds, TRUE, ca); + creds->add_cert(creds, FALSE, im); + creds->add_cert(creds, FALSE, sj); + + ck_assert(check_oid(sj->get_subject(sj), baseline)); +} +END_TEST + +START_TEST(test_inhibit_mapping_bad) +{ + certificate_t *ca, *i1, *i2, *sj; + + ca = create_cert_ext(NULL, "CN=CA", extended, X509_CA, NULL, NULL, + X509_NO_CONSTRAINT, 1, X509_NO_CONSTRAINT); + i1 = create_cert(ca, "CN=IM1", extended, X509_CA, NULL, NULL); + i2 = create_cert(i1, "CN=IM2", extended, X509_CA, baseline, extended); + sj = create_cert(i2, "CN=SJ", baseline, 0, NULL, NULL); + + creds->add_cert(creds, TRUE, ca); + creds->add_cert(creds, FALSE, i1); + creds->add_cert(creds, FALSE, i2); + creds->add_cert(creds, FALSE, sj); + + /* TODO: we currently reject the certificate completely, but should + * actually just invalidate the policy not mapped properly */ + ck_assert(!check_trust(sj->get_subject(sj))); +} +END_TEST + Suite *certpolicy_suite_create() { Suite *s; @@ -466,5 +539,11 @@ Suite *certpolicy_suite_create() tcase_add_test(tc, test_invalid_mapping_nopolicy); suite_add_tcase(s, tc); + tc = tcase_create("inhibit policy mapping"); + tcase_add_checked_fixture(tc, setup, teardown); + tcase_add_test(tc, test_inhibit_mapping_good); + tcase_add_test(tc, test_inhibit_mapping_bad); + suite_add_tcase(s, tc); + return s; } From 3b25be0e5773f0f523ae2f1695d8df15cb37b9ec Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 14 Oct 2014 15:00:22 +0200 Subject: [PATCH 4/8] constraints: Add inhibitAnyPolicy tests --- .../tests/suites/test_certpolicy.c | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/libstrongswan/tests/suites/test_certpolicy.c b/src/libstrongswan/tests/suites/test_certpolicy.c index d240db391..dd6baf829 100644 --- a/src/libstrongswan/tests/suites/test_certpolicy.c +++ b/src/libstrongswan/tests/suites/test_certpolicy.c @@ -497,6 +497,44 @@ START_TEST(test_inhibit_mapping_bad) } END_TEST +START_TEST(test_inhibit_any_good) +{ + certificate_t *ca, *im, *sj; + + ca = create_cert_ext(NULL, "CN=CA", anyPolicy, X509_CA, NULL, NULL, + X509_NO_CONSTRAINT, X509_NO_CONSTRAINT, 1); + im = create_cert(ca, "CN=IM", anyPolicy, X509_CA, NULL, NULL); + sj = create_cert(im, "CN=SJ", baseline, 0, NULL, NULL); + + creds->add_cert(creds, TRUE, ca); + creds->add_cert(creds, FALSE, im); + creds->add_cert(creds, FALSE, sj); + + ck_assert(check_oid(sj->get_subject(sj), baseline)); +} +END_TEST + +START_TEST(test_inhibit_any_bad) +{ + certificate_t *ca, *i1, *i2, *sj; + + ca = create_cert_ext(NULL, "CN=CA", anyPolicy, X509_CA, NULL, NULL, + X509_NO_CONSTRAINT, X509_NO_CONSTRAINT, 1); + i1 = create_cert(ca, "CN=IM1", anyPolicy, X509_CA, NULL, NULL); + i2 = create_cert(i1, "CN=IM2", anyPolicy, X509_CA, NULL, NULL); + sj = create_cert(i2, "CN=SJ", baseline, 0, NULL, NULL); + + creds->add_cert(creds, TRUE, ca); + creds->add_cert(creds, FALSE, i1); + creds->add_cert(creds, FALSE, i2); + creds->add_cert(creds, FALSE, sj); + + /* TODO: we currently reject the certificate completely, but should + * actually just invalidate the policy relying on inhibited anyPolicy */ + ck_assert(!check_trust(sj->get_subject(sj))); +} +END_TEST + Suite *certpolicy_suite_create() { Suite *s; @@ -545,5 +583,11 @@ Suite *certpolicy_suite_create() tcase_add_test(tc, test_inhibit_mapping_bad); suite_add_tcase(s, tc); + tc = tcase_create("inhibit any policy"); + tcase_add_checked_fixture(tc, setup, teardown); + tcase_add_test(tc, test_inhibit_any_good); + tcase_add_test(tc, test_inhibit_any_bad); + suite_add_tcase(s, tc); + return s; } From 6733109d0ed0a6e6ecce4b07fe24a2b0c220d3bf Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 14 Oct 2014 15:25:24 +0200 Subject: [PATCH 5/8] constraints: Add requireExplicitPolicy tests --- .../tests/suites/test_certpolicy.c | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/src/libstrongswan/tests/suites/test_certpolicy.c b/src/libstrongswan/tests/suites/test_certpolicy.c index dd6baf829..7501e1a8b 100644 --- a/src/libstrongswan/tests/suites/test_certpolicy.c +++ b/src/libstrongswan/tests/suites/test_certpolicy.c @@ -535,6 +535,44 @@ START_TEST(test_inhibit_any_bad) } END_TEST +START_TEST(test_require_explicit_good) +{ + certificate_t *ca, *im, *sj; + + ca = create_cert_ext(NULL, "CN=CA", anyPolicy, X509_CA, NULL, NULL, + 1, X509_NO_CONSTRAINT, X509_NO_CONSTRAINT); + im = create_cert(ca, "CN=IM", baseline, X509_CA, NULL, NULL); + sj = create_cert(im, "CN=SJ", baseline, 0, NULL, NULL); + + creds->add_cert(creds, TRUE, ca); + creds->add_cert(creds, FALSE, im); + creds->add_cert(creds, FALSE, sj); + + ck_assert(check_oid(sj->get_subject(sj), baseline)); +} +END_TEST + +START_TEST(test_require_explicit_bad) +{ + certificate_t *ca, *i1, *i2, *sj; + + ca = create_cert_ext(NULL, "CN=CA", anyPolicy, X509_CA, NULL, NULL, + 1, X509_NO_CONSTRAINT, X509_NO_CONSTRAINT); + i1 = create_cert(ca, "CN=IM1", extended, X509_CA, NULL, NULL); + i2 = create_cert(i1, "CN=IM2", extended, X509_CA, NULL, NULL); + sj = create_cert(i2, "CN=SJ", baseline, 0, NULL, NULL); + + creds->add_cert(creds, TRUE, ca); + creds->add_cert(creds, FALSE, i1); + creds->add_cert(creds, FALSE, i2); + creds->add_cert(creds, FALSE, sj); + + /* TODO: we currently reject the certificate completely, but should + * actually just invalidate the policy violating requireExplicit */ + ck_assert(!check_trust(sj->get_subject(sj))); +} +END_TEST + Suite *certpolicy_suite_create() { Suite *s; @@ -589,5 +627,11 @@ Suite *certpolicy_suite_create() tcase_add_test(tc, test_inhibit_any_bad); suite_add_tcase(s, tc); + tc = tcase_create("require explicit policy"); + tcase_add_checked_fixture(tc, setup, teardown); + tcase_add_test(tc, test_require_explicit_good); + tcase_add_test(tc, test_require_explicit_bad); + suite_add_tcase(s, tc); + return s; } From 7e80995c5928b9d22def845b198fcdf10ca5a784 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 15 Oct 2014 12:10:54 +0200 Subject: [PATCH 6/8] constraints: Use a more specific FQDN/email name constraint matching While RFC 5280 is not very specific about the matching rules of subjectAltNames, it has some examples how to match email and FQDN constraints. We try to follow these examples, and restrict DNS names to subdomain matching and email to full email, host or domain matching. --- .../constraints/constraints_validator.c | 95 ++++++++++++++----- 1 file changed, 73 insertions(+), 22 deletions(-) diff --git a/src/libstrongswan/plugins/constraints/constraints_validator.c b/src/libstrongswan/plugins/constraints/constraints_validator.c index b5762b54c..a0f4a7465 100644 --- a/src/libstrongswan/plugins/constraints/constraints_validator.c +++ b/src/libstrongswan/plugins/constraints/constraints_validator.c @@ -52,16 +52,67 @@ static bool check_pathlen(x509_t *issuer, int pathlen) } /** - * Check if a FQDN/RFC822 constraint matches (suffix match) + * Check if a FQDN constraint matches */ -static bool suffix_matches(identification_t *constraint, identification_t *id) +static bool fqdn_matches(identification_t *constraint, identification_t *id) { - chunk_t c, i; + chunk_t c, i, diff; c = constraint->get_encoding(constraint); i = id->get_encoding(id); - return i.len >= c.len && chunk_equals(c, chunk_skip(i, i.len - c.len)); + if (!c.len || i.len < c.len) + { + return FALSE; + } + diff = chunk_create(i.ptr, i.len - c.len); + if (!chunk_equals(c, chunk_skip(i, diff.len))) + { + return FALSE; + } + if (!diff.len) + { + return TRUE; + } + if (c.ptr[0] == '.' || diff.ptr[diff.len - 1] == '.') + { + return TRUE; + } + return FALSE; +} + +/** + * Check if a RFC822 constraint matches + */ +static bool email_matches(identification_t *constraint, identification_t *id) +{ + chunk_t c, i, diff; + + c = constraint->get_encoding(constraint); + i = id->get_encoding(id); + + if (!c.len || i.len < c.len) + { + return FALSE; + } + if (memchr(c.ptr, '@', c.len)) + { /* constraint is a full email address */ + return chunk_equals(c, i); + } + diff = chunk_create(i.ptr, i.len - c.len); + if (!diff.len || !chunk_equals(c, chunk_skip(i, diff.len))) + { + return FALSE; + } + if (c.ptr[0] == '.') + { /* constraint is domain, suffix match */ + return TRUE; + } + if (diff.ptr[diff.len - 1] == '@') + { /* constraint is host specific, only username can be appended */ + return TRUE; + } + return FALSE; } /** @@ -121,8 +172,10 @@ static bool name_constraint_matches(identification_t *constraint, switch (type) { case ID_FQDN: + matches = fqdn_matches(constraint, id); + break; case ID_RFC822_ADDR: - matches = suffix_matches(constraint, id); + matches = email_matches(constraint, id); break; case ID_DER_ASN1_DN: matches = dn_matches(constraint, id); @@ -151,7 +204,7 @@ static bool name_constraint_inherited(identification_t *constraint, x509_t *x509, bool permitted) { enumerator_t *enumerator; - identification_t *id; + identification_t *id, *a, *b; bool inherited = FALSE; id_type_t type; @@ -166,28 +219,26 @@ static bool name_constraint_inherited(identification_t *constraint, { if (id->get_type(id) == type) { + if (permitted) + { /* permitted constraint can be narrowed */ + a = constraint; + b = id; + } + else + { /* excluded constraint can be widened */ + a = id; + b = constraint; + } switch (type) { case ID_FQDN: + inherited = fqdn_matches(a, b); + break; case ID_RFC822_ADDR: - if (permitted) - { /* permitted constraint can be narrowed */ - inherited = suffix_matches(constraint, id); - } - else - { /* excluded constraint can be widened */ - inherited = suffix_matches(id, constraint); - } + inherited = email_matches(a, b); break; case ID_DER_ASN1_DN: - if (permitted) - { - inherited = dn_matches(constraint, id); - } - else - { - inherited = dn_matches(id, constraint); - } + inherited = dn_matches(a, b); break; default: DBG1(DBG_CFG, "%N NameConstraint matching not implemented", From a6c8647eca8014ba2fab9f8f3e8f3f173a256173 Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Tue, 14 Oct 2014 16:29:28 +0200 Subject: [PATCH 7/8] constraints: Add permitted/excludedNameConstraints check --- src/libstrongswan/tests/Makefile.am | 1 + .../tests/suites/test_certnames.c | 398 ++++++++++++++++++ src/libstrongswan/tests/tests.h | 1 + 3 files changed, 400 insertions(+) create mode 100644 src/libstrongswan/tests/suites/test_certnames.c diff --git a/src/libstrongswan/tests/Makefile.am b/src/libstrongswan/tests/Makefile.am index 1f118cc5b..2a05dc916 100644 --- a/src/libstrongswan/tests/Makefile.am +++ b/src/libstrongswan/tests/Makefile.am @@ -41,6 +41,7 @@ tests_SOURCES = tests.h tests.c \ suites/test_ecdsa.c \ suites/test_rsa.c \ suites/test_certpolicy.c \ + suites/test_certnames.c \ suites/test_host.c \ suites/test_hasher.c \ suites/test_crypter.c \ diff --git a/src/libstrongswan/tests/suites/test_certnames.c b/src/libstrongswan/tests/suites/test_certnames.c new file mode 100644 index 000000000..e30702864 --- /dev/null +++ b/src/libstrongswan/tests/suites/test_certnames.c @@ -0,0 +1,398 @@ +/* + * Copyright (C) 2014 Martin Willi + * Copyright (C) 2014 revosec AG + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. See . + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * for more details. + */ + +#include "test_suite.h" + +#include +#include +#include + +/** + * RSA private key, so we don't have to generate one + */ +static char keydata[] = { + 0x30,0x82,0x02,0x5e,0x02,0x01,0x00,0x02,0x81,0x81,0x00,0xb1,0x9b,0xd4,0x51,0x24, + 0xfc,0x56,0x1d,0x3d,0xfb,0xa2,0xea,0x37,0x02,0x70,0x72,0x87,0x84,0x2f,0x3b,0x2d, + 0x6e,0x22,0xef,0x3f,0x37,0x04,0xb2,0x6f,0xb7,0xe7,0xd8,0x58,0x05,0xde,0x34,0xbf, + 0x99,0xe6,0x40,0x7a,0x56,0xa7,0x73,0xf5,0x98,0xcb,0xb0,0x37,0x90,0x5e,0xd1,0x3f, + 0xf4,0x73,0x50,0x7f,0x53,0x8e,0xf1,0x04,0x25,0xb4,0x77,0x22,0x4e,0x8a,0x9d,0x27, + 0x8f,0x6f,0xaf,0x59,0xbd,0xb0,0x0f,0xf0,0xaa,0x11,0x94,0x66,0x16,0x10,0x58,0xad, + 0x77,0xa1,0xac,0x58,0xb4,0xd0,0x0d,0xbc,0x11,0xe0,0xc0,0xe9,0x29,0xdc,0x42,0x63, + 0x01,0x23,0x4f,0x28,0x41,0x6d,0x34,0x9e,0x0c,0x4a,0xc8,0x62,0x83,0xb5,0x71,0x71, + 0x0b,0x51,0xc0,0x4c,0x37,0xd4,0x68,0x19,0x52,0x9a,0x8b,0x02,0x03,0x01,0x00,0x01, + 0x02,0x81,0x81,0x00,0x82,0xca,0x33,0x16,0xb2,0x3a,0xd4,0x1b,0x62,0x9a,0x9c,0xc5, + 0x07,0x4f,0x57,0x89,0x2f,0x7c,0x4a,0xdf,0xb4,0x3b,0xc7,0xa4,0x11,0x14,0x2d,0xf4, + 0x4c,0xca,0xcc,0x03,0x88,0x06,0x82,0x34,0xab,0xe7,0xe4,0x24,0x15,0x33,0x1c,0xcb, + 0x0a,0xcf,0xc3,0x27,0x78,0x33,0x6b,0x6f,0x82,0x3e,0x3c,0x70,0xc9,0xe2,0xb9,0x7f, + 0x88,0xc3,0x4f,0x59,0xb5,0x8e,0xa3,0x81,0xd9,0x88,0x1f,0xc0,0x38,0xbc,0xc8,0x93, + 0x40,0x0f,0x43,0xd8,0x72,0x12,0xb4,0xcc,0x6d,0x76,0x0a,0x6f,0x01,0x05,0xa8,0x88, + 0xf4,0x57,0x44,0xd2,0x05,0xc4,0x77,0xf5,0xfb,0x1b,0xf3,0xb2,0x0d,0x90,0xb8,0xb4, + 0x63,0x62,0x70,0x2c,0xe4,0x28,0xd8,0x20,0x10,0x85,0x4a,0x5e,0x63,0xa9,0xb0,0xdd, + 0xba,0xd0,0x32,0x49,0x02,0x41,0x00,0xdb,0x77,0xf1,0xdd,0x1a,0x12,0xc5,0xfb,0x2b, + 0x5b,0xb2,0xcd,0xb6,0xd0,0x4c,0xc4,0xe5,0x93,0xd6,0xf8,0x88,0xfc,0x18,0x40,0x21, + 0x9c,0xf7,0x2d,0x60,0x6f,0x91,0xf5,0x73,0x3c,0xf7,0x7f,0x67,0x1d,0x5b,0xb5,0xee, + 0x29,0xc1,0xd4,0xc6,0xdb,0x44,0x4c,0x40,0x05,0x63,0xaa,0x71,0x95,0x18,0x14,0xa7, + 0x23,0x9f,0x7a,0xee,0x7f,0xb5,0xc7,0x02,0x41,0x00,0xcf,0x2c,0x24,0x50,0x65,0xf4, + 0x94,0x7b,0xe9,0xf3,0x13,0x77,0xea,0x27,0x3c,0x6f,0x03,0x84,0xa7,0x7d,0xa2,0x54, + 0x40,0x97,0x82,0x0e,0xd9,0x09,0x9f,0x4a,0xa6,0x75,0xe5,0x66,0xe4,0x9c,0x59,0xd9, + 0x3a,0xe6,0xf7,0xd8,0x8b,0x68,0xb0,0x21,0x52,0x31,0xb3,0x4a,0xa0,0x2c,0x41,0xd7, + 0x1f,0x7b,0xe2,0x0f,0x15,0xc9,0x6e,0xc0,0xe5,0x1d,0x02,0x41,0x00,0x9c,0x1a,0x61, + 0x9f,0x89,0xc7,0x26,0xa9,0x33,0xba,0xe2,0xa0,0x6d,0xd3,0x15,0x77,0xcb,0x6f,0xef, + 0xad,0x12,0x0a,0x75,0xd9,0x4f,0xcf,0x4d,0x05,0x2a,0x9d,0xd1,0x2c,0xcb,0xcd,0xe6, + 0xa0,0xe9,0x20,0x39,0xb6,0x5a,0xf3,0xba,0x99,0xf4,0xe3,0xcb,0x5d,0x8d,0x00,0x08, + 0x57,0x18,0xb9,0x1a,0xca,0xbd,0xe3,0x99,0xb1,0x1f,0xe9,0x18,0xcb,0x02,0x40,0x65, + 0x35,0x1b,0x48,0x6b,0x86,0x60,0x43,0x68,0xb6,0xe6,0xfb,0xdd,0xd7,0xed,0x1e,0x0e, + 0x89,0xef,0x88,0xe0,0x94,0x68,0x39,0x9b,0xbf,0xc5,0x27,0x7e,0x39,0xe9,0xb8,0x0e, + 0xa9,0x85,0x65,0x1c,0x3f,0x93,0x16,0xe2,0x5d,0x57,0x3d,0x7d,0x4d,0xc9,0xe9,0x9d, + 0xbd,0x07,0x22,0x97,0xc7,0x90,0x09,0xe5,0x15,0x99,0x7f,0x1e,0x2b,0xfd,0xc1,0x02, + 0x41,0x00,0x92,0x78,0xfe,0x04,0xa0,0x53,0xed,0x36,0x97,0xbd,0x16,0xce,0x91,0x9b, + 0xbe,0x1f,0x8e,0x40,0x00,0x99,0x0c,0x49,0x15,0xca,0x59,0xd3,0xe3,0xd4,0xeb,0x71, + 0xcf,0xda,0xd7,0xc8,0x99,0x74,0xfc,0x6b,0xe8,0xfd,0xe5,0xe0,0x49,0x61,0xcb,0xda, + 0xe3,0xe7,0x8b,0x72,0xb5,0x69,0x73,0x2b,0x8b,0x54,0xcb,0xd9,0x48,0x6d,0x61,0x02, + 0x49,0xe8, +}; + +/** + * Issue a certificate with permitted/excluded name constraints + */ +static certificate_t* create_cert(certificate_t *ca, char *subject, char *san, + x509_flag_t flags, identification_t *permitted, + identification_t *excluded) +{ + private_key_t *privkey; + public_key_t *pubkey; + certificate_t *cert; + identification_t *id; + linked_list_t *plist, *elist, *sans; + + privkey = lib->creds->create(lib->creds, CRED_PRIVATE_KEY, KEY_RSA, + BUILD_BLOB_ASN1_DER, chunk_from_thing(keydata), + BUILD_END); + ck_assert(privkey); + pubkey = privkey->get_public_key(privkey); + ck_assert(pubkey); + plist = linked_list_create(); + if (permitted) + { + plist->insert_last(plist, permitted); + } + elist = linked_list_create(); + if (excluded) + { + elist->insert_last(elist, excluded); + } + sans = linked_list_create(); + if (san) + { + id = identification_create_from_string(san); + sans->insert_last(sans, id); + } + id = identification_create_from_string(subject); + cert = lib->creds->create(lib->creds, CRED_CERTIFICATE, CERT_X509, + BUILD_SIGNING_KEY, privkey, + BUILD_PUBLIC_KEY, pubkey, + BUILD_SUBJECT, id, + BUILD_X509_FLAG, flags, + BUILD_SIGNING_CERT, ca, + BUILD_SUBJECT_ALTNAMES, sans, + BUILD_PERMITTED_NAME_CONSTRAINTS, plist, + BUILD_EXCLUDED_NAME_CONSTRAINTS, elist, + BUILD_END); + ck_assert(cert); + id->destroy(id); + sans->destroy_offset(sans, offsetof(identification_t, destroy)); + plist->destroy_offset(plist, offsetof(identification_t, destroy)); + elist->destroy_offset(elist, offsetof(identification_t, destroy)); + privkey->destroy(privkey); + pubkey->destroy(pubkey); + + return cert; +} + +/** + * Check if a certificate with given subject has a valid trustchain + */ +static bool check_trust(identification_t *subject) +{ + enumerator_t *certs; + certificate_t *cert; + bool trusted; + + certs = lib->credmgr->create_trusted_enumerator(lib->credmgr, KEY_ANY, + subject, FALSE); + trusted = certs->enumerate(certs, &cert, NULL); + certs->destroy(certs); + + return trusted; +} + +static mem_cred_t *creds; + +START_SETUP(setup) +{ + creds = mem_cred_create(); + lib->credmgr->add_set(lib->credmgr, &creds->set); +} +END_SETUP + +START_TEARDOWN(teardown) +{ + lib->credmgr->remove_set(lib->credmgr, &creds->set); + creds->destroy(creds); + lib->credmgr->flush_cache(lib->credmgr, CERT_ANY); +} +END_TEARDOWN + +static struct { + char *constraint; + char *subject; + bool good; +} permitted_dn[] = { + { "C=CH, O=strongSwan", "C=CH, O=strongSwan, CN=tester", TRUE }, + { "C=CH, O=strongSwan", "C=CH, O=strong", FALSE }, + { "C=CH, O=strongSwan", "C=CH, O=strong, CN=tester", FALSE }, + { "C=CH, O=strongSwan", "C=CH, O=another, CN=tester", FALSE }, + { "C=CH, O=strongSwan", "C=CH, CN=tester, O=strongSwan", FALSE }, +}; + +START_TEST(test_permitted_dn) +{ + certificate_t *ca, *im, *sj; + identification_t *id; + + id = identification_create_from_string(permitted_dn[_i].constraint); + ca = create_cert(NULL, "C=CH, O=strongSwan, CN=CA", NULL, X509_CA, id, NULL); + id = identification_create_from_string(permitted_dn[_i].constraint); + im = create_cert(ca, "C=CH, O=strongSwan, CN=IM", NULL, X509_CA, id, NULL); + sj = create_cert(im, permitted_dn[_i].subject, NULL, 0, NULL, NULL); + + creds->add_cert(creds, TRUE, ca); + creds->add_cert(creds, FALSE, im); + creds->add_cert(creds, FALSE, sj); + + ck_assert(check_trust(sj->get_subject(sj)) == permitted_dn[_i].good); +} +END_TEST + +static struct { + id_type_t ctype; + char *cdata; + char *subject; + bool good; +} permitted_san[] = { + { ID_FQDN, ".strongswan.org", "test.strongswan.org", TRUE }, + { ID_FQDN, "strongswan.org", "test.strongswan.org", TRUE }, + { ID_FQDN, "a.b.c.strongswan.org", "d.a.b.c.strongswan.org", TRUE }, + { ID_FQDN, "a.b.c.strongswan.org", "a.b.c.d.strongswan.org", FALSE }, + { ID_FQDN, "strongswan.org", "strongswan.org.com", FALSE }, + { ID_FQDN, ".strongswan.org", "strongswan.org", FALSE }, + { ID_FQDN, "strongswan.org", "nostrongswan.org", FALSE }, + { ID_FQDN, "strongswan.org", "swan.org", FALSE }, + { ID_FQDN, "strongswan.org", "swan.org", FALSE }, + { ID_RFC822_ADDR, "tester@strongswan.org", "tester@strongswan.org", TRUE }, + { ID_RFC822_ADDR, "tester@strongswan.org", "atester@strongswan.org", FALSE }, + { ID_RFC822_ADDR, "strongswan.org", "tester@strongswan.org", TRUE }, + { ID_RFC822_ADDR, "strongswan.org", "tester@test.strongswan.org", FALSE }, + { ID_RFC822_ADDR, ".strongswan.org", "tester@test.strongswan.org", TRUE }, + { ID_RFC822_ADDR, ".strongswan.org", "tester@strongswan.org", FALSE }, +}; + +START_TEST(test_permitted_san) +{ + certificate_t *ca, *sj; + identification_t *id; + + id = identification_create_from_encoding(permitted_san[_i].ctype, + chunk_from_str(permitted_san[_i].cdata)); + ca = create_cert(NULL, "CN=CA", NULL, X509_CA, id, NULL); + sj = create_cert(ca, "CN=SJ", permitted_san[_i].subject, 0, NULL, NULL); + + creds->add_cert(creds, TRUE, ca); + creds->add_cert(creds, FALSE, sj); + + ck_assert(check_trust(sj->get_subject(sj)) == permitted_san[_i].good); +} +END_TEST + +static struct { + char *constraint; + char *subject; + bool good; +} excluded_dn[] = { + { "C=CH, O=another", "C=CH, O=strongSwan, CN=tester", TRUE }, + { "C=CH, O=another", "C=CH, O=anot", TRUE }, + { "C=CH, O=another", "C=CH, O=anot, CN=tester", TRUE }, + { "C=CH, O=another", "C=CH, O=another, CN=tester", FALSE }, + { "C=CH, O=another", "C=CH, CN=tester, O=another", TRUE }, +}; + +START_TEST(test_excluded_dn) +{ + certificate_t *ca, *im, *sj; + identification_t *id; + + id = identification_create_from_string(excluded_dn[_i].constraint); + ca = create_cert(NULL, "C=CH, O=strongSwan, CN=CA", NULL, X509_CA, NULL, id); + id = identification_create_from_string(excluded_dn[_i].constraint); + im = create_cert(ca, "C=CH, O=strongSwan, CN=IM", NULL, X509_CA, NULL, id); + sj = create_cert(im, excluded_dn[_i].subject, NULL, 0, NULL, NULL); + + creds->add_cert(creds, TRUE, ca); + creds->add_cert(creds, FALSE, im); + creds->add_cert(creds, FALSE, sj); + + ck_assert(check_trust(sj->get_subject(sj)) == excluded_dn[_i].good); +} +END_TEST + +static struct { + id_type_t ctype; + char *cdata; + char *subject; + bool good; +} excluded_san[] = { + { ID_FQDN, ".strongswan.org", "test.strongswan.org", FALSE }, + { ID_FQDN, "strongswan.org", "test.strongswan.org", FALSE }, + { ID_FQDN, "a.b.c.strongswan.org", "d.a.b.c.strongswan.org", FALSE }, + { ID_FQDN, "a.b.c.strongswan.org", "a.b.c.d.strongswan.org", TRUE }, + { ID_FQDN, "strongswan.org", "strongswan.org.com", TRUE }, + { ID_FQDN, ".strongswan.org", "strongswan.org", TRUE }, + { ID_FQDN, "strongswan.org", "nostrongswan.org", TRUE }, + { ID_FQDN, "strongswan.org", "swan.org", TRUE }, + { ID_FQDN, "strongswan.org", "swan.org", TRUE }, + { ID_RFC822_ADDR, "tester@strongswan.org", "tester@strongswan.org", FALSE }, + { ID_RFC822_ADDR, "tester@strongswan.org", "atester@strongswan.org", TRUE }, + { ID_RFC822_ADDR, "strongswan.org", "tester@strongswan.org", FALSE }, + { ID_RFC822_ADDR, "strongswan.org", "tester@test.strongswan.org", TRUE }, + { ID_RFC822_ADDR, ".strongswan.org", "tester@test.strongswan.org", FALSE }, + { ID_RFC822_ADDR, ".strongswan.org", "tester@strongswan.org", TRUE }, +}; + +START_TEST(test_excluded_san) +{ + certificate_t *ca, *sj; + identification_t *id; + + id = identification_create_from_encoding(excluded_san[_i].ctype, + chunk_from_str(excluded_san[_i].cdata)); + ca = create_cert(NULL, "CN=CA", NULL, X509_CA, NULL, id); + sj = create_cert(ca, "CN=SJ", excluded_san[_i].subject, 0, NULL, NULL); + + creds->add_cert(creds, TRUE, ca); + creds->add_cert(creds, FALSE, sj); + + ck_assert(check_trust(sj->get_subject(sj)) == excluded_san[_i].good); +} +END_TEST + +static struct { + char *caconst; + char *imconst; + char *subject; + bool good; +} permitted_dninh[] = { + { "C=CH", "C=CH, O=strongSwan", "C=CH, O=strongSwan, CN=tester", TRUE }, + { "C=CH", "C=DE, O=strongSwan", "C=CH, O=strongSwan, CN=tester", FALSE }, + { "C=CH, O=strongSwan", "C=CH", "C=CH", FALSE }, +}; + +START_TEST(test_permitted_dninh) +{ + certificate_t *ca, *im, *sj; + identification_t *id; + + id = identification_create_from_string(permitted_dninh[_i].caconst); + ca = create_cert(NULL, "C=CH, O=strongSwan, CN=CA", NULL, X509_CA, id, NULL); + id = identification_create_from_string(permitted_dninh[_i].imconst); + im = create_cert(ca, "C=CH, O=strongSwan, CN=IM", NULL, X509_CA, id, NULL); + sj = create_cert(im, permitted_dninh[_i].subject, NULL, 0, NULL, NULL); + + creds->add_cert(creds, TRUE, ca); + creds->add_cert(creds, FALSE, im); + creds->add_cert(creds, FALSE, sj); + + ck_assert(check_trust(sj->get_subject(sj)) == permitted_dninh[_i].good); +} +END_TEST + +static struct { + char *caconst; + char *imconst; + char *subject; + bool good; +} excluded_dninh[] = { + { "C=CH, O=strongSwan", "C=CH", "C=DE", TRUE }, + { "C=CH, O=strongSwan", "C=DE", "C=CH", FALSE }, + { "C=CH", "C=CH, O=strongSwan", "C=CH, O=strongSwan, CN=tester", FALSE }, +}; + +START_TEST(test_excluded_dninh) +{ + certificate_t *ca, *im, *sj; + identification_t *id; + + id = identification_create_from_string(excluded_dninh[_i].caconst); + ca = create_cert(NULL, "C=CH, O=strongSwan, CN=CA", NULL, X509_CA, NULL, id); + id = identification_create_from_string(excluded_dninh[_i].imconst); + im = create_cert(ca, "C=DE, CN=IM", NULL, X509_CA, NULL, id); + sj = create_cert(im, excluded_dninh[_i].subject, NULL, 0, NULL, NULL); + + creds->add_cert(creds, TRUE, ca); + creds->add_cert(creds, FALSE, im); + creds->add_cert(creds, FALSE, sj); + + ck_assert(check_trust(sj->get_subject(sj)) == excluded_dninh[_i].good); +} +END_TEST + +Suite *certnames_suite_create() +{ + Suite *s; + TCase *tc; + + s = suite_create("certnames"); + + tc = tcase_create("permitted DN name constraints"); + tcase_add_checked_fixture(tc, setup, teardown); + tcase_add_loop_test(tc, test_permitted_dn, 0, countof(permitted_dn)); + suite_add_tcase(s, tc); + + tc = tcase_create("permitted subjectAltName constraints"); + tcase_add_checked_fixture(tc, setup, teardown); + tcase_add_loop_test(tc, test_permitted_san, 0, countof(permitted_san)); + suite_add_tcase(s, tc); + + tc = tcase_create("excluded DN constraints"); + tcase_add_checked_fixture(tc, setup, teardown); + tcase_add_loop_test(tc, test_excluded_dn, 0, countof(excluded_dn)); + suite_add_tcase(s, tc); + + tc = tcase_create("excluded subjectAltName constraints"); + tcase_add_checked_fixture(tc, setup, teardown); + tcase_add_loop_test(tc, test_excluded_san, 0, countof(excluded_san)); + suite_add_tcase(s, tc); + + tc = tcase_create("permitted DN name constraint inherit"); + tcase_add_checked_fixture(tc, setup, teardown); + tcase_add_loop_test(tc, test_permitted_dninh, 0, countof(permitted_dninh)); + suite_add_tcase(s, tc); + + tc = tcase_create("excluded DN name constraint inherit"); + tcase_add_checked_fixture(tc, setup, teardown); + tcase_add_loop_test(tc, test_excluded_dninh, 0, countof(excluded_dninh)); + suite_add_tcase(s, tc); + + return s; +} diff --git a/src/libstrongswan/tests/tests.h b/src/libstrongswan/tests/tests.h index eff848f61..7fe8dbfa4 100644 --- a/src/libstrongswan/tests/tests.h +++ b/src/libstrongswan/tests/tests.h @@ -33,6 +33,7 @@ TEST_SUITE(vectors_suite_create) TEST_SUITE_DEPEND(ecdsa_suite_create, PRIVKEY_GEN, KEY_ECDSA) TEST_SUITE_DEPEND(rsa_suite_create, PRIVKEY_GEN, KEY_RSA) TEST_SUITE_DEPEND(certpolicy_suite_create, CERT_ENCODE, CERT_X509) +TEST_SUITE_DEPEND(certnames_suite_create, CERT_ENCODE, CERT_X509) TEST_SUITE(host_suite_create) TEST_SUITE(printf_suite_create) TEST_SUITE(hasher_suite_create) From b9d38c9fa2397fffb7cfdc56d91292c93f84b2ba Mon Sep 17 00:00:00 2001 From: Martin Willi Date: Wed, 15 Oct 2014 12:33:17 +0200 Subject: [PATCH 8/8] pki: Print and document the name constraint type for DNS or email constraints As email constraints may be for a specific host, it is not clear from the name itself if it is a DNS or email constraint. --- src/pki/commands/print.c | 20 ++++++++++++++++++-- src/pki/man/pki---issue.1.in | 16 ++++++++++++++-- src/pki/man/pki---self.1.in | 16 ++++++++++++++-- 3 files changed, 46 insertions(+), 6 deletions(-) diff --git a/src/pki/commands/print.c b/src/pki/commands/print.c index fb07169bf..a8a4e8375 100644 --- a/src/pki/commands/print.c +++ b/src/pki/commands/print.c @@ -65,6 +65,22 @@ static void print_key(private_key_t *key) } } +/** + * Get a prefix for a named constraint identity type + */ +static char* get_type_pfx(identification_t *id) +{ + switch (id->get_type(id)) + { + case ID_RFC822_ADDR: + return "email:"; + case ID_FQDN: + return "dns:"; + default: + return ""; + } +} + /** * Print X509 specific certificate information */ @@ -202,7 +218,7 @@ static void print_x509(x509_t *x509) printf("Permitted NameConstraints:\n"); first = FALSE; } - printf(" %Y\n", id); + printf(" %s%Y\n", get_type_pfx(id), id); } enumerator->destroy(enumerator); first = TRUE; @@ -214,7 +230,7 @@ static void print_x509(x509_t *x509) printf("Excluded NameConstraints:\n"); first = FALSE; } - printf(" %Y\n", id); + printf(" %s%Y\n", get_type_pfx(id), id); } enumerator->destroy(enumerator); diff --git a/src/pki/man/pki---issue.1.in b/src/pki/man/pki---issue.1.in index 375cb2fe4..d017bfe1d 100644 --- a/src/pki/man/pki---issue.1.in +++ b/src/pki/man/pki---issue.1.in @@ -147,10 +147,22 @@ times. Set path length constraint. .TP .BI "\-n, \-\-nc-permitted " name -Add permitted NameConstraint extension to certificate. +Add permitted NameConstraint extension to certificate. For DNS or email +constraints, the identity type is not always detectable by the given name. Use +the +.B dns: +or +.B email: +prefix to force a constraint type. .TP .BI "\-N, \-\-nc-excluded " name -Add excluded NameConstraint extension to certificate. +Add excluded NameConstraint extension to certificate. For DNS or email +constraints, the identity type is not always detectable by the given name. Use +the +.B dns: +or +.B email: +prefix to force a constraint type. .TP .BI "\-M, \-\-policy-mapping " issuer-oid:subject-oid Add policyMapping from issuer to subject OID. diff --git a/src/pki/man/pki---self.1.in b/src/pki/man/pki---self.1.in index 5e6e78bd0..03ce03934 100644 --- a/src/pki/man/pki---self.1.in +++ b/src/pki/man/pki---self.1.in @@ -127,10 +127,22 @@ times. Set path length constraint. .TP .BI "\-n, \-\-nc-permitted " name -Add permitted NameConstraint extension to certificate. +Add permitted NameConstraint extension to certificate. For DNS or email +constraints, the identity type is not always detectable by the given name. Use +the +.B dns: +or +.B email: +prefix to force a constraint type. .TP .BI "\-N, \-\-nc-excluded " name -Add excluded NameConstraint extension to certificate. +Add excluded NameConstraint extension to certificate. For DNS or email +constraints, the identity type is not always detectable by the given name. Use +the +.B dns: +or +.B email: +prefix to force a constraint type. .TP .BI "\-M, \-\-policy-mapping " issuer-oid:subject-oid Add policyMapping from issuer to subject OID.