diff options
-rw-r--r-- | src/libstrongswan/plugins/constraints/constraints_validator.c | 217 | ||||
-rw-r--r-- | src/libstrongswan/tests/Makefile.am | 2 | ||||
-rw-r--r-- | src/libstrongswan/tests/suites/test_certnames.c | 398 | ||||
-rw-r--r-- | src/libstrongswan/tests/suites/test_certpolicy.c | 637 | ||||
-rw-r--r-- | src/libstrongswan/tests/tests.h | 2 | ||||
-rw-r--r-- | src/pki/commands/print.c | 20 | ||||
-rw-r--r-- | src/pki/man/pki---issue.1.in | 16 | ||||
-rw-r--r-- | src/pki/man/pki---self.1.in | 16 |
8 files changed, 1255 insertions, 53 deletions
diff --git a/src/libstrongswan/plugins/constraints/constraints_validator.c b/src/libstrongswan/plugins/constraints/constraints_validator.c index 62ccc7108..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", @@ -298,8 +349,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,33 +373,85 @@ 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)) + { + 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; + } + } + 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)) { - enumerator = subject->create_cert_policy_enumerator(subject); - while (enumerator->enumerate(enumerator, &policy)) + int maxmap = 8; + + while (found) { - if (!has_policy(issuer, policy->oid)) + found = FALSE; + + policies = issuer->create_cert_policy_enumerator(issuer); + while (policies->enumerate(policies, &policy)) { - 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 (chunk_equals(oid, policy->oid) || + chunk_equals(any_policy, policy->oid)) + { + found = TRUE; + break; + } } - if (auth) + policies->destroy(policies); + if (found) { - oid = asn1_oid_to_string(policy->oid); - if (oid) + 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)) { - auth->add(auth, AUTH_RULE_CERT_POLICY, oid); + oid = mapping->issuer; + found = TRUE; + break; } } + mappings->destroy(mappings); + if (--maxmap == 0) + { + found = FALSE; + break; + } + } + if (!found) + { + break; } - enumerator->destroy(enumerator); } + issuers->destroy(issuers); - return TRUE; + return found; } /** @@ -364,7 +466,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 +552,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 +560,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 +621,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 +672,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)) diff --git a/src/libstrongswan/tests/Makefile.am b/src/libstrongswan/tests/Makefile.am index 7ecba19da..2a05dc916 100644 --- a/src/libstrongswan/tests/Makefile.am +++ b/src/libstrongswan/tests/Makefile.am @@ -40,6 +40,8 @@ tests_SOURCES = tests.h tests.c \ suites/test_array.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 <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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 <asn1/asn1.h> +#include <credentials/sets/mem_cred.h> +#include <credentials/certificates/x509.h> + +/** + * 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/suites/test_certpolicy.c b/src/libstrongswan/tests/suites/test_certpolicy.c new file mode 100644 index 000000000..7501e1a8b --- /dev/null +++ b/src/libstrongswan/tests/suites/test_certpolicy.c @@ -0,0 +1,637 @@ +/* + * 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 <http://www.fsf.org/copyleft/gpl.txt>. + * + * 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 <asn1/asn1.h> +#include <credentials/sets/mem_cred.h> +#include <credentials/certificates/x509.h> + +/** + * 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 fr given policy, including extended flags + */ +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; + 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_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); + 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; +} + +/** + * 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 + */ +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; +} + +/** + * 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"; +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 + +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 + +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 + +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; + 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); + + 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); + + 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); + + 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; +} diff --git a/src/libstrongswan/tests/tests.h b/src/libstrongswan/tests/tests.h index 586227800..7fe8dbfa4 100644 --- a/src/libstrongswan/tests/tests.h +++ b/src/libstrongswan/tests/tests.h @@ -32,6 +32,8 @@ 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_DEPEND(certnames_suite_create, CERT_ENCODE, CERT_X509) TEST_SUITE(host_suite_create) TEST_SUITE(printf_suite_create) TEST_SUITE(hasher_suite_create) 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 @@ -66,6 +66,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 */ static void print_x509(x509_t *x509) @@ -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. |