Merge branch 'policy-constraints'
authorMartin Willi <martin@revosec.ch>
Thu, 30 Oct 2014 10:42:04 +0000 (11:42 +0100)
committerMartin Willi <martin@revosec.ch>
Thu, 30 Oct 2014 10:42:04 +0000 (11:42 +0100)
Fixes handling of invalid policies in end entity certificates by not rejecting
the full certificate, but just invalidating the affected policy. Additionally
adds a bunch of unit tests for the constraints plugin, and some minor fixes
to the nameConstraints handling.

Currently we still reject CAs that use invalid policy mapping; we should accept
such certificates and just invalid affected policies in a next iteration.

Fixes #453.

src/libstrongswan/plugins/constraints/constraints_validator.c
src/libstrongswan/tests/Makefile.am
src/libstrongswan/tests/suites/test_certnames.c [new file with mode: 0644]
src/libstrongswan/tests/suites/test_certpolicy.c [new file with mode: 0644]
src/libstrongswan/tests/tests.h
src/pki/commands/print.c
src/pki/man/pki---issue.1.in
src/pki/man/pki---self.1.in

index 62ccc71..a0f4a74 100644 (file)
@@ -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))
index 7ecba19..2a05dc9 100644 (file)
@@ -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 (file)
index 0000000..e307028
--- /dev/null
@@ -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 (file)
index 0000000..7501e1a
--- /dev/null
@@ -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, &current))
+       {
+               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;
+}
index 5862278..7fe8dbf 100644 (file)
@@ -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)
index fb07169..a8a4e83 100644 (file)
@@ -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);
 
index 375cb2f..d017bfe 100644 (file)
@@ -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.
index 5e6e78b..03ce039 100644 (file)
@@ -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.