constraints: Properly validate name constraints according to RFC 5280

The previous code was in a way too simple which resulted in it being too
strict.  For instance, it enforced that intermediate CA certificates
inherited the name constraints of their parents.  That's not required by
RFC 5280 and prevented e.g. adding constraints in an intermediate CA
certificate that's followed by another that doesn't contain any
name constraints.  That's perfectly fine as the set of constraints
specified by the parent continue to apply to that CA certificate and
the children it issues.

Name constraints were previously also applied to all identities of a
matching type, which is way too strict except for some very simple
cases.  It basically prevented multiple constraints of the same type
as e.g. an intermediate CA certificate that has permitted name constraints
for example.org and example.com couldn't issue acceptable certificates
because any SAN with one domain would get rejected by the other
constraint.  According to RFC 5280 matching one constraint is enough.

Also fixed is an issue with name constraints for IP addresses which were
previously only supported for a single level.
This commit is contained in:
Tobias Brunner 2024-02-23 17:44:44 +01:00
parent ea6a6344d3
commit b29be6029e
2 changed files with 816 additions and 203 deletions

View File

@ -1,4 +1,5 @@
/*
* Copyright (C) 2023-2024 Tobias Brunner
* Copyright (C) 2010 Martin Willi
*
* Copyright (C) secunet Security Networks AG
@ -18,6 +19,8 @@
#include <utils/debug.h>
#include <asn1/asn1.h>
#include <collections/array.h>
#include <collections/hashtable.h>
#include <collections/linked_list.h>
#include <credentials/certificates/x509.h>
@ -101,10 +104,14 @@ static bool email_matches(identification_t *constraint, identification_t *id)
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)))
if (!chunk_equals(c, chunk_skip(i, diff.len)))
{
return FALSE;
}
if (!diff.len)
{
return TRUE;
}
if (c.ptr[0] == '.')
{ /* constraint is domain, suffix match */
return TRUE;
@ -144,41 +151,155 @@ static bool dn_matches(identification_t *constraint, identification_t *id)
}
/**
* Check if the given identity type matches the type of NameConstraint
* Check if a new permitted or excluded NameConstraint is matching an
* existing one
*/
static bool type_matches(id_type_t constraint, id_type_t id)
static bool name_constraint_matches(identification_t *existing,
identification_t *new, bool permitted)
{
switch (constraint)
identification_t *a, *b;
bool matching = FALSE;
if (permitted)
{ /* permitted constraint can be narrowed */
a = existing;
b = new;
}
else
{ /* excluded constraint can be widened */
a = new;
b = existing;
}
switch (existing->get_type(existing))
{
case ID_FQDN:
matching = fqdn_matches(a, b);
break;
case ID_RFC822_ADDR:
matching = email_matches(a, b);
break;
case ID_DER_ASN1_DN:
return constraint == id;
matching = dn_matches(a, b);
break;
case ID_IPV4_ADDR_SUBNET:
return id == ID_IPV4_ADDR;
case ID_IPV6_ADDR_SUBNET:
return id == ID_IPV6_ADDR;
matching = b->matches(b, a);
break;
default:
return FALSE;
/* shouldn't happen */
matching = FALSE;
break;
}
return matching;
}
/**
* Get the name constraint type from an identity type
*/
static id_type_t constraint_type_from_id(id_type_t id)
{
switch (id)
{
case ID_IPV4_ADDR:
return ID_IPV4_ADDR_SUBNET;
case ID_IPV6_ADDR:
return ID_IPV6_ADDR_SUBNET;
default:
return id;
}
}
/**
* Check if a certificate matches to a NameConstraint
* Check if the given identity matches any of the given name constraints
*/
static bool name_constraint_matches(identification_t *constraint,
certificate_t *cert, bool permitted)
static bool id_matches_constraints(certificate_t *cert, identification_t *id,
array_t *constraints, bool permitted)
{
x509_t *x509 = (x509_t*)cert;
enumerator_t *enumerator;
identification_t *subject, *constraint;
id_type_t type;
bool matches = FALSE;
subject = cert->get_subject(cert);
type = id->get_type(id);
enumerator = array_create_enumerator(constraints);
while (enumerator->enumerate(enumerator, &constraint))
{
switch (type)
{
case ID_FQDN:
matches = fqdn_matches(constraint, id);
break;
case ID_RFC822_ADDR:
matches = email_matches(constraint, id);
break;
case ID_DER_ASN1_DN:
matches = dn_matches(constraint, id);
break;
case ID_IPV4_ADDR:
case ID_IPV6_ADDR:
matches = id->matches(id, constraint);
break;
default:
/* shouldn't happen */
break;
}
if (matches)
{
if (!permitted)
{
if (id->equals(id, subject))
{
DBG1(DBG_CFG, "subject of certificate '%Y' matches excluded "
"name constraint '%Y'", subject, constraint);
}
else
{
DBG1(DBG_CFG, "subject alternative name '%Y' of certificate "
"'%Y' matches excluded name constraint '%Y'",
id, subject, constraint);
}
}
break;
}
}
enumerator->destroy(enumerator);
if (!matches && permitted)
{
if (id->equals(id, subject))
{
DBG1(DBG_CFG, "subject of certificate '%Y' does not match any "
"permitted name constraints", subject);
}
else
{
DBG1(DBG_CFG, "subject alternative name '%Y' of certificate '%Y' "
"does not match any permitted name constraints", id, subject);
}
}
return matches;
}
/**
* Check if a certificate matches the given permitted/excluded name constraints
*/
static bool cert_matches_constraints(x509_t *x509, hashtable_t *types,
bool permitted)
{
certificate_t *cert = (certificate_t*)x509;
array_t *constraints;
enumerator_t *enumerator;
identification_t *id;
id_type_t type;
bool matches = permitted;
type = constraint->get_type(constraint);
if (type == ID_DER_ASN1_DN)
constraints = types->get(types, (void*)(uintptr_t)ID_DER_ASN1_DN);
if (constraints)
{
matches = dn_matches(constraint, cert->get_subject(cert));
matches = id_matches_constraints(cert, cert->get_subject(cert),
constraints, permitted);
if (matches != permitted)
{
return matches;
@ -188,34 +309,16 @@ static bool name_constraint_matches(identification_t *constraint,
enumerator = x509->create_subjectAltName_enumerator(x509);
while (enumerator->enumerate(enumerator, &id))
{
if (type_matches(type, id->get_type(id)))
type = constraint_type_from_id(id->get_type(id));
constraints = types->get(types, (void*)(uintptr_t)type);
if (constraints)
{
switch (type)
matches = id_matches_constraints(cert, id, constraints, permitted);
if (matches != permitted)
{
case ID_FQDN:
matches = fqdn_matches(constraint, id);
break;
case ID_RFC822_ADDR:
matches = email_matches(constraint, id);
break;
case ID_DER_ASN1_DN:
matches = dn_matches(constraint, id);
break;
case ID_IPV4_ADDR_SUBNET:
case ID_IPV6_ADDR_SUBNET:
matches = id->matches(id, constraint);
break;
default:
DBG1(DBG_CFG, "%N NameConstraint matching not implemented",
id_type_names, type);
matches = FALSE;
break;
break;
}
}
if (matches != permitted)
{
break;
}
}
enumerator->destroy(enumerator);
@ -223,112 +326,297 @@ static bool name_constraint_matches(identification_t *constraint,
}
/**
* Check if a permitted or excluded NameConstraint has been inherited to sub-CA
* Validate the names in the given certificate against the current constraints
*/
static bool name_constraint_inherited(identification_t *constraint,
x509_t *x509, bool permitted)
static bool name_constraints_match(x509_t *x509, hashtable_t *permitted,
hashtable_t *excluded)
{
enumerator_t *enumerator;
identification_t *id, *a, *b;
bool inherited = FALSE;
id_type_t type;
if (!(x509->get_flags(x509) & X509_CA))
{ /* not a sub-CA, not required */
return TRUE;
}
type = constraint->get_type(constraint);
enumerator = x509->create_name_constraint_enumerator(x509, permitted);
while (enumerator->enumerate(enumerator, &id))
if (permitted && !cert_matches_constraints(x509, permitted, TRUE))
{
if (id->get_type(id) == type)
return FALSE;
}
if (excluded && cert_matches_constraints(x509, excluded, FALSE))
{
return FALSE;
}
return TRUE;
}
/**
* Destroy name constraints (callback for hashtable_t::destroy_function())
*/
CALLBACK(destroy_constraints, void,
array_t *this, const void *key)
{
array_destroy(this);
}
/**
* Hashtable hash function
*/
static u_int id_type_hash(const void *key)
{
uintptr_t id = (uintptr_t)key;
return chunk_hash(chunk_from_thing(id));
}
/**
* Hashtable equals function
*/
static bool id_type_equals(const void *a, const void *b)
{
return (uintptr_t)a == (uintptr_t)b;
}
/**
* Collect name constraints (permitted or excluded) of each supported type
* from the given certificate
*/
static bool collect_constraints(x509_t *x509, bool permitted, hashtable_t **out)
{
hashtable_t *collected;
enumerator_t *enumerator;
identification_t *constraint;
array_t *constraints;
id_type_t type;
bool success = TRUE;
collected = hashtable_create(id_type_hash, id_type_equals, 8);
enumerator = x509->create_name_constraint_enumerator(x509, permitted);
while (enumerator->enumerate(enumerator, &constraint))
{
type = constraint->get_type(constraint);
switch (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:
inherited = email_matches(a, b);
break;
case ID_DER_ASN1_DN:
inherited = dn_matches(a, b);
break;
default:
DBG1(DBG_CFG, "%N NameConstraint matching not implemented",
id_type_names, type);
inherited = FALSE;
break;
}
case ID_FQDN:
case ID_RFC822_ADDR:
case ID_DER_ASN1_DN:
case ID_IPV4_ADDR_SUBNET:
case ID_IPV6_ADDR_SUBNET:
break;
default:
DBG1(DBG_CFG, "%N NameConstraint not supported",
id_type_names, type);
success = FALSE;
break;
}
if (inherited)
if (!success)
{
break;
}
constraints = collected->get(collected, (void*)(uintptr_t)type);
if (!constraints)
{
constraints = array_create(0, 8);
collected->put(collected, (void*)(uintptr_t)type, constraints);
}
array_insert(constraints, ARRAY_TAIL, constraint);
}
enumerator->destroy(enumerator);
return inherited;
if (success)
{
*out = collected;
}
else
{
collected->destroy_function(collected, destroy_constraints);
}
return success;
}
/**
* Merge existing and new permitted/excluded name constraints
*/
static void merge_constraints(certificate_t *cert, array_t *existing_constraints,
array_t *new_constraints, bool permitted)
{
enumerator_t *enumerator, *new;
identification_t *constraint, *new_constraint;
if (permitted)
{
array_t *to_move = NULL;
enumerator = array_create_enumerator(existing_constraints);
while (enumerator->enumerate(enumerator, &constraint))
{
new = array_create_enumerator(new_constraints);
while (new->enumerate(new, &new_constraint))
{
if (name_constraint_matches(constraint, new_constraint, TRUE))
{
array_insert_create(&to_move, ARRAY_TAIL, new_constraint);
array_remove_at(new_constraints, new);
}
}
new->destroy(new);
/* remove the existing constraint. if it was matched, it gets
* replaced by the moved equal/narrower constraints, if not, it's
* not permitted anymore */
array_remove_at(existing_constraints, enumerator);
}
enumerator->destroy(enumerator);
if (to_move)
{
while (array_remove(to_move, ARRAY_HEAD, &new_constraint))
{
array_insert(existing_constraints, ARRAY_TAIL, new_constraint);
}
array_destroy(to_move);
}
/* report ignored constraints that would widen the permitted set */
while (array_remove(new_constraints, ARRAY_HEAD, &new_constraint))
{
DBG1(DBG_CFG, "ignoring name constraint '%Y' in certificate "
"'%Y' that's not permitted by parent CAs",
new_constraint, cert->get_subject(cert));
}
}
else
{
/* this is simpler as we basically adopt all new constraints, we just
* check if we can remove a constraint that gets widened */
enumerator = array_create_enumerator(existing_constraints);
while (enumerator->enumerate(enumerator, &constraint))
{
new = array_create_enumerator(new_constraints);
while (new->enumerate(new, &new_constraint))
{
if (name_constraint_matches(constraint, new_constraint, FALSE))
{
/* remove the existing constraint if it is matched, it
* gets replaced by an equal/wider constraint */
array_remove_at(existing_constraints, enumerator);
break;
}
}
new->destroy(new);
}
enumerator->destroy(enumerator);
/* add all new constraints to the list */
while (array_remove(new_constraints, ARRAY_HEAD, &new_constraint))
{
array_insert(existing_constraints, ARRAY_TAIL, new_constraint);
}
}
}
/**
* Update the set of permitted/excluded name constraints
*/
static bool update_name_constraints(x509_t *x509, hashtable_t **existing,
bool permitted)
{
enumerator_t *enumerator;
hashtable_t *collected;
array_t *existing_constraints, *new_constraints;
void *type;
if (!(x509->get_flags(x509) & X509_CA))
{
/* ignore end-entity certificates */
return TRUE;
}
if (!collect_constraints(x509, permitted, &collected))
{
return FALSE;
}
if (collected->get_count(collected))
{
if (!*existing)
{
/* adopt all constraints if we haven't any yet */
*existing = collected;
collected = NULL;
}
else
{
/* merge sets of constraints for each type */
enumerator = collected->create_enumerator(collected);
while (enumerator->enumerate(enumerator, &type, &new_constraints))
{
existing_constraints = (*existing)->get(*existing, type);
if (existing_constraints)
{
/* merge constraints of known types, either allowing them to
* get narrowed or widened */
merge_constraints((certificate_t*)x509, existing_constraints,
new_constraints, permitted);
}
else
{
/* adopt constraints for new types */
collected->remove_at(collected, enumerator);
(*existing)->put(*existing, type, new_constraints);
}
}
enumerator->destroy(enumerator);
}
}
DESTROY_FUNCTION_IF(collected, destroy_constraints);
return TRUE;
}
/**
* Check name constraints
*/
static bool check_name_constraints(certificate_t *subject, x509_t *issuer)
static bool check_name_constraints(x509_t *issuer, u_int pathlen,
auth_cfg_t *auth, certificate_t **violator)
{
enumerator_t *enumerator;
identification_t *constraint;
linked_list_t *chain;
hashtable_t *permitted = NULL, *excluded = NULL;
certificate_t *subject, *cert;
auth_rule_t rule;
x509_t *x509;
int len = 0;
bool valid = TRUE;
enumerator = issuer->create_name_constraint_enumerator(issuer, TRUE);
while (enumerator->enumerate(enumerator, &constraint))
subject = auth->get(auth, AUTH_RULE_SUBJECT_CERT);
if (!subject || subject->get_type(subject) != CERT_X509)
{
if (!name_constraint_matches(constraint, subject, TRUE))
return TRUE;
}
/* prepare trustchain to validate name constraints top-down */
chain = linked_list_create_with_items(subject, NULL);
enumerator = auth->create_enumerator(auth);
while (enumerator->enumerate(enumerator, &rule, &cert))
{
if (rule == AUTH_RULE_IM_CERT &&
cert->get_type(cert) == CERT_X509)
{
DBG1(DBG_CFG, "certificate '%Y' does not match permitted name "
"constraint '%Y'", subject->get_subject(subject), constraint);
enumerator->destroy(enumerator);
return FALSE;
}
if (!name_constraint_inherited(constraint, (x509_t*)subject, TRUE))
{
DBG1(DBG_CFG, "intermediate CA '%Y' does not inherit permitted name "
"constraint '%Y'", subject->get_subject(subject), constraint);
enumerator->destroy(enumerator);
return FALSE;
chain->insert_first(chain, cert);
}
}
enumerator->destroy(enumerator);
chain->insert_first(chain, issuer);
enumerator = issuer->create_name_constraint_enumerator(issuer, FALSE);
while (enumerator->enumerate(enumerator, &constraint))
enumerator = chain->create_enumerator(chain);
while (enumerator->enumerate(enumerator, &x509))
{
if (name_constraint_matches(constraint, subject, FALSE))
if ((len > 0 && !name_constraints_match(x509, permitted, excluded)) ||
!update_name_constraints(x509, &permitted, TRUE) ||
!update_name_constraints(x509, &excluded, FALSE))
{
DBG1(DBG_CFG, "certificate '%Y' matches excluded name "
"constraint '%Y'", subject->get_subject(subject), constraint);
enumerator->destroy(enumerator);
return FALSE;
}
if (!name_constraint_inherited(constraint, (x509_t*)subject, FALSE))
{
DBG1(DBG_CFG, "intermediate CA '%Y' does not inherit excluded name "
"constraint '%Y'", subject->get_subject(subject), constraint);
enumerator->destroy(enumerator);
return FALSE;
valid = FALSE;
*violator = (certificate_t*)x509;
break;
}
len++;
}
enumerator->destroy(enumerator);
return TRUE;
DESTROY_FUNCTION_IF(permitted, destroy_constraints);
DESTROY_FUNCTION_IF(excluded, destroy_constraints);
chain->destroy(chain);
return valid;
}
/**
@ -690,14 +978,16 @@ METHOD(cert_validator_t, validate, bool,
subject);
return FALSE;
}
if (!check_name_constraints(subject, (x509_t*)issuer))
{
lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_POLICY_VIOLATION,
subject);
return FALSE;
}
if (anchor)
{
certificate_t *violator;
if (!check_name_constraints((x509_t*)issuer, pathlen, auth, &violator))
{
lib->credmgr->call_hook(lib->credmgr,
CRED_HOOK_POLICY_VIOLATION, violator);
return FALSE;
}
if (!check_policy_constraints((x509_t*)issuer, pathlen, auth))
{
lib->credmgr->call_hook(lib->credmgr,

View File

@ -68,15 +68,15 @@ static char keydata[] = {
/**
* 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)
static certificate_t* create_cert_lists(certificate_t *ca, char *subject,
linked_list_t *sans, x509_flag_t flags,
linked_list_t *permitted,
linked_list_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),
@ -84,6 +84,39 @@ static certificate_t* create_cert(certificate_t *ca, char *subject, char *san,
ck_assert(privkey);
pubkey = privkey->get_public_key(privkey);
ck_assert(pubkey);
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, permitted,
BUILD_EXCLUDED_NAME_CONSTRAINTS, excluded,
BUILD_END);
ck_assert(cert);
id->destroy(id);
sans->destroy_offset(sans, offsetof(identification_t, destroy));
permitted->destroy_offset(permitted, offsetof(identification_t, destroy));
excluded->destroy_offset(excluded, offsetof(identification_t, destroy));
privkey->destroy(privkey);
pubkey->destroy(pubkey);
return cert;
}
/**
* Issue a certificate with single values
*/
static certificate_t* create_cert(certificate_t *ca, char *subject, char *san,
x509_flag_t flags, identification_t *permitted,
identification_t *excluded)
{
linked_list_t *plist, *elist, *sans;
identification_t *id;
plist = linked_list_create();
if (permitted)
{
@ -100,26 +133,7 @@ static certificate_t* create_cert(certificate_t *ca, char *subject, char *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;
return create_cert_lists(ca, subject, sans, flags, plist, elist);
}
/**
@ -188,26 +202,29 @@ START_TEST(test_permitted_dn)
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 },
{ ".strongswan.org", "test.strongswan.org", TRUE },
{ "strongswan.org", "test.strongswan.org", TRUE },
{ "a.b.c.strongswan.org", "d.a.b.c.strongswan.org", TRUE },
{ "a.b.c.strongswan.org", "a.b.c.d.strongswan.org", FALSE },
{ "strongswan.org", "strongswan.org.com", FALSE },
{ ".strongswan.org", "strongswan.org", FALSE },
{ "strongswan.org", "nostrongswan.org", FALSE },
{ "strongswan.org", "swan.org", FALSE },
{ "strongswan.org", "swan.org", FALSE },
{ "tester@strongswan.org", "tester@strongswan.org", TRUE },
{ "tester@strongswan.org", "atester@strongswan.org", FALSE },
{ "email:strongswan.org", "tester@strongswan.org", TRUE },
{ "email:strongswan.org", "tester@test.strongswan.org", FALSE },
{ "email:.strongswan.org", "tester@test.strongswan.org", TRUE },
{ "email:.strongswan.org", "tester@strongswan.org", FALSE },
{ "192.168.1.0/24", "192.168.1.10", TRUE },
{ "192.168.1.0/24", "192.168.2.10", FALSE },
{ "fec0::/64", "fec0::10", TRUE },
{ "fec0::/64", "fec1::10", FALSE },
};
START_TEST(test_permitted_san)
@ -215,8 +232,7 @@ 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));
id = identification_create_from_string(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);
@ -259,26 +275,29 @@ START_TEST(test_excluded_dn)
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 },
{ ".strongswan.org", "test.strongswan.org", FALSE },
{ "strongswan.org", "test.strongswan.org", FALSE },
{ "a.b.c.strongswan.org", "d.a.b.c.strongswan.org", FALSE },
{ "a.b.c.strongswan.org", "a.b.c.d.strongswan.org", TRUE },
{ "strongswan.org", "strongswan.org.com", TRUE },
{ ".strongswan.org", "strongswan.org", TRUE },
{ "strongswan.org", "nostrongswan.org", TRUE },
{ "strongswan.org", "swan.org", TRUE },
{ "strongswan.org", "swan.org", TRUE },
{ "tester@strongswan.org", "tester@strongswan.org", FALSE },
{ "tester@strongswan.org", "atester@strongswan.org", TRUE },
{ "email:strongswan.org", "tester@strongswan.org", FALSE },
{ "email:strongswan.org", "tester@test.strongswan.org", TRUE },
{ "email:.strongswan.org", "tester@test.strongswan.org", FALSE },
{ "email:.strongswan.org", "tester@strongswan.org", TRUE },
{ "192.168.1.0/24", "192.168.1.10", FALSE },
{ "192.168.1.0/24", "192.168.2.10", TRUE },
{ "fec0::/64", "fec0::10", FALSE },
{ "fec0::/64", "fec1::10", TRUE },
};
START_TEST(test_excluded_san)
@ -286,8 +305,7 @@ 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));
id = identification_create_from_string(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);
@ -298,33 +316,45 @@ START_TEST(test_excluded_san)
}
END_TEST
/**
* Create an identity if the given string is not NULL
*/
static identification_t *create_test_id(char *id)
{
return id ? identification_create_from_string(id) : NULL;
}
static struct {
char *caconst;
char *imconst;
char *subject;
bool good;
} permitted_dninh[] = {
} permitted_dn_levels[] = {
{ "C=CH", "C=CH, O=strongSwan", "C=CH, O=strongSwan, CN=tester", TRUE },
{ "C=CH", NULL, "C=CH, O=strongSwan, CN=tester", TRUE },
{ NULL, "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", "C=DE", "C=DE, O=strongSwan, CN=tester", FALSE },
{ "C=CH, O=strongSwan", "C=CH", "C=CH", FALSE },
{ "C=CH, O=strongSwan, CN=Intermediate", NULL, "C=CH", FALSE },
};
START_TEST(test_permitted_dninh)
START_TEST(test_permitted_dn_levels)
{
certificate_t *ca, *im, *sj;
identification_t *id;
id = identification_create_from_string(permitted_dninh[_i].caconst);
id = create_test_id(permitted_dn_levels[_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);
id = create_test_id(permitted_dn_levels[_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);
sj = create_cert(im, permitted_dn_levels[_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);
ck_assert(check_trust(sj->get_subject(sj)) == permitted_dn_levels[_i].good);
}
END_TEST
@ -333,28 +363,301 @@ static struct {
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 },
} permitted_san_levels[] = {
{ "strongswan.org", NULL, "strongswan.org", TRUE },
{ "strongswan.org", NULL, "vpn.strongswan.org", TRUE },
{ "strongswan.org", NULL, "strongswan.com", FALSE },
{ NULL, "strongswan.org", "strongswan.org", TRUE },
{ NULL, "strongswan.org", "strongswan.com", FALSE },
{ "strongswan.org", "strongswan.org", "strongswan.org", TRUE },
{ "strongswan.org", "strongswan.com", "strongswan.com", FALSE },
{ "strongswan.org", "vpn.strongswan.org", "strongswan.org", FALSE },
{ "strongswan.org", "vpn.strongswan.org", "vpn.strongswan.org", TRUE },
{ "strongswan.org", "vpn.strongswan.org", "a.vpn.strongswan.org", TRUE },
{ "strongswan.org", NULL, "tester@strongswan.org", TRUE },
{ "tester@strongswan.org", NULL, "tester@strongswan.org", TRUE },
{ "email:strongswan.org", NULL, "tester@strongswan.org", TRUE },
{ "email:strongswan.org", NULL, "tester@strongswan.com", FALSE },
{ "email:strongswan.org", "tester@strongswan.org", "tester@strongswan.org", TRUE },
{ "email:strongswan.org", "tester@strongswan.org", "alice@strongswan.org", FALSE },
{ "email:strongswan.org", "strongswan.org", "vpn.strongswan.org", TRUE },
{ "192.168.1.0/24", NULL, "192.168.1.10", TRUE },
{ "192.168.1.0/24", NULL, "192.168.2.10", FALSE },
{ "192.168.1.0/24", "192.168.2.0/24", "192.168.1.10", FALSE },
{ "192.168.1.0/24", "192.168.1.0/28", "192.168.1.10", TRUE },
{ "192.168.1.0/24", "192.168.1.16/28", "192.168.1.10", FALSE },
{ "fec0::/64", NULL, "fec0::10", TRUE },
{ "fec0::/64", NULL, "fec1::10", FALSE },
{ "fec0::/64", "fec1::/64", "fec1::10", FALSE },
{ "fec0::/64", "fec0::/123", "fec0::10", TRUE },
{ "fec0::/64", "fec0::20/123", "fec0::10", FALSE },
};
START_TEST(test_excluded_dninh)
START_TEST(test_permitted_san_levels)
{
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);
id = create_test_id(permitted_san_levels[_i].caconst);
ca = create_cert(NULL, "CN=CA", NULL, X509_CA, id, NULL);
id = create_test_id(permitted_san_levels[_i].imconst);
im = create_cert(ca, "CN=IM", NULL, X509_CA, id, NULL);
sj = create_cert(im, "CN=EE", permitted_san_levels[_i].subject, 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);
ck_assert(check_trust(sj->get_subject(sj)) == permitted_san_levels[_i].good);
}
END_TEST
static struct {
char *caconst;
char *imconst;
char *subject;
bool good;
} excluded_dn_levels[] = {
{ "C=CH, O=strongSwan", "C=CH", "C=DE", TRUE },
{ "C=CH, O=strongSwan", "C=CH", "C=CH", FALSE },
{ "C=CH, O=strongSwan", "C=DE", "C=CH", TRUE },
{ "C=CH, O=strongSwan", "C=DE", "C=DE", FALSE },
{ "C=CH, O=strongSwan", "C=DE", "C=CH, O=strongSwan", FALSE },
{ NULL, "C=CH", "C=CH, O=strongSwan", FALSE },
{ "C=CH", NULL, "C=CH, O=strongSwan", FALSE },
{ "C=CH", "C=CH, O=strongSwan", "C=CH, O=strongSwan, CN=tester", FALSE },
{ "C=DE", NULL, "C=CH, O=strongSwan, CN=tester", FALSE },
};
START_TEST(test_excluded_dn_levels)
{
certificate_t *ca, *im, *sj;
identification_t *id;
id = create_test_id(excluded_dn_levels[_i].caconst);
ca = create_cert(NULL, "C=CH, O=strongSwan, CN=CA", NULL, X509_CA, NULL, id);
id = create_test_id(excluded_dn_levels[_i].imconst);
im = create_cert(ca, "C=DE, CN=IM", NULL, X509_CA, NULL, id);
sj = create_cert(im, excluded_dn_levels[_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_levels[_i].good);
}
END_TEST
static struct {
char *caconst;
char *imconst;
char *subject;
bool good;
} excluded_san_levels[] = {
{ "strongswan.org", NULL, "strongswan.org", FALSE },
{ "strongswan.org", NULL, "strongswan.com", TRUE },
{ NULL, "strongswan.org", "strongswan.org", FALSE },
{ NULL, "strongswan.org", "strongswan.com", TRUE },
{ "strongswan.org", NULL, "test.strongswan.org", FALSE },
{ "test.strongswan.org", NULL, "test.strongswan.org", FALSE },
{ "test.strongswan.org", NULL, "strongswan.org", TRUE },
{ "test.strongswan.org", "strongswan.org", "strongswan.org", FALSE },
{ "test.strongswan.org", "strongswan.org", "test.strongswan.org", FALSE },
{ "test.strongswan.org", "test.strongswan.org", "test.strongswan.org", FALSE },
{ "strongswan.org", NULL, "tester@strongswan.org", TRUE },
{ "tester@strongswan.org", NULL, "tester@strongswan.org", FALSE },
{ "tester@strongswan.org", NULL, "alice@strongswan.org", TRUE },
{ "email:strongswan.org", NULL, "tester@strongswan.org", FALSE },
{ "email:strongswan.org", NULL, "tester@strongswan.com", TRUE },
{ "email:strongswan.org", "email:strongswan.com", "tester@strongswan.org", FALSE },
{ "email:strongswan.org", "email:strongswan.com", "tester@strongswan.com", FALSE },
{ "strongswan.org", "email:strongswan.com", "tester@strongswan.com", FALSE },
{ "192.168.1.0/24", NULL, "192.168.1.10", FALSE },
{ "192.168.1.0/24", NULL, "192.168.2.10", TRUE },
{ "192.168.1.0/24", "192.168.0.0/16", "192.168.2.10", FALSE },
{ "fec0::/64", NULL, "fec0::10", FALSE },
{ "fec0::/64", NULL, "fec1::10", TRUE },
{ "fec0::/64", "fec1::/12", "fec1::10", FALSE },
};
START_TEST(test_excluded_san_levels)
{
certificate_t *ca, *im, *sj;
identification_t *id;
id = create_test_id(excluded_san_levels[_i].caconst);
ca = create_cert(NULL, "CN=CA", NULL, X509_CA, NULL, id);
id = create_test_id(excluded_san_levels[_i].imconst);
im = create_cert(ca, "CN=IM", NULL, X509_CA, NULL, id);
sj = create_cert(im, "CN=EE", excluded_san_levels[_i].subject, 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_san_levels[_i].good);
}
END_TEST
/**
* Add an identity to the given list if not NULL
*/
static void add_identity_to_list(linked_list_t *list, char *idstr)
{
identification_t *id;
if (idstr)
{
id = identification_create_from_string(idstr);
list->insert_last(list, id);
}
}
/**
* Create a certificate with potentially multiple constraints/SANs
*/
static certificate_t *create_cert_multi(certificate_t *ca, char *subject,
x509_flag_t flags,
char *san1, char *san2,
char *pconst1, char *pconst2,
char *econst1, char *econst2)
{
linked_list_t *sans, *permitted, *excluded;
sans = linked_list_create();
add_identity_to_list(sans, san1);
add_identity_to_list(sans, san2);
permitted = linked_list_create();
add_identity_to_list(permitted, pconst1);
add_identity_to_list(permitted, pconst2);
excluded = linked_list_create();
add_identity_to_list(excluded, econst1);
add_identity_to_list(excluded, econst2);
return create_cert_lists(ca, subject, sans, flags, permitted, excluded);
}
static struct {
char *caconst1;
char *caconst2;
char *imconst1;
char *imconst2;
char *san1;
char *san2;
bool good;
} permitted_san_multi[] = {
{ "strongswan.org", "strongswan.com", NULL, NULL, "vpn.strongswan.org", NULL, TRUE },
{ "strongswan.org", "strongswan.com", NULL, NULL, "vpn.strongswan.com", NULL, TRUE },
{ "strongswan.org", "strongswan.com", NULL, NULL, "vpn.strongswan.org", "vpn.strongswan.com", TRUE },
{ NULL, NULL, "strongswan.org", "strongswan.com", "vpn.strongswan.org", NULL, TRUE },
{ NULL, NULL, "strongswan.org", "strongswan.com", "vpn.strongswan.com", NULL, TRUE },
{ NULL, NULL, "strongswan.org", "strongswan.com", "vpn.strongswan.org", "vpn.strongswan.com", TRUE },
{ "strongswan.org", "strongswan.com", "strongswan.org", NULL, "vpn.strongswan.org", NULL, TRUE },
{ "strongswan.org", "strongswan.com", "vpn.strongswan.org", NULL, "vpn.strongswan.org", NULL, TRUE },
{ "strongswan.org", "strongswan.com", "vpn.strongswan.org", NULL, "vpn.strongswan.org", NULL, TRUE },
{ "strongswan.org", "strongswan.com", "vpn.strongswan.org", NULL, "vpn.strongswan.org", "vpn.strongswan.com", FALSE },
{ "strongswan.org", "strongswan.com", "strongswan.com", NULL, "vpn.strongswan.org", "vpn.strongswan.com", FALSE },
{ "strongswan.org", "strongswan.com", "strongswan.org", NULL, "vpn.strongswan.com", NULL, FALSE },
{ "strongswan.org", "strongswan.com", "strongswan.com", NULL, "vpn.strongswan.org", NULL, FALSE },
{ "strongswan.org", "strongswan.com", "strongswan.com", NULL, "vpn.strongswan.com", NULL, TRUE },
{ "strongswan.org", "strongswan.com", "strongswan.net", NULL, "vpn.strongswan.com", NULL, FALSE },
{ "strongswan.org", "strongswan.com", "strongswan.net", NULL, "vpn.strongswan.org", NULL, FALSE },
{ "strongswan.org", "strongswan.com", "strongswan.net", NULL, "vpn.strongswan.net", NULL, FALSE },
{ "strongswan.org", "email:strongswan.org", NULL, NULL, "vpn.strongswan.org", NULL, TRUE },
{ "strongswan.org", "email:strongswan.org", NULL, NULL, "tester@strongswan.org", NULL, TRUE },
{ "strongswan.org", "email:strongswan.org", NULL, NULL, "vpn.strongswan.org", "tester@strongswan.org", TRUE },
{ "strongswan.org", "email:strongswan.org", "strongswan.org", NULL, "vpn.strongswan.org", NULL, TRUE },
{ "strongswan.org", "email:strongswan.org", "strongswan.org", NULL, "tester@strongswan.org", NULL, TRUE },
{ "strongswan.org", "email:strongswan.org", "strongswan.org", NULL, "vpn.strongswan.org", "tester@strongswan.org", TRUE },
{ "strongswan.org", "email:strongswan.org", "strongswan.org", "email:strongswan.com", "vpn.strongswan.org", NULL, TRUE },
{ "strongswan.org", "email:strongswan.org", "strongswan.org", "email:strongswan.com", "tester@strongswan.org", NULL, FALSE },
{ "strongswan.org", "email:strongswan.org", "strongswan.org", "email:strongswan.com", "vpn.strongswan.org", "tester@strongswan.org", FALSE },
{ "strongswan.org", "email:strongswan.org", "email:strongswan.org", NULL, "vpn.strongswan.org", NULL, TRUE },
{ "strongswan.org", "email:strongswan.org", "email:strongswan.org", NULL, "tester@strongswan.org", NULL, TRUE },
{ "strongswan.org", "email:strongswan.org", "email:strongswan.org", NULL, "vpn.strongswan.org", "tester@strongswan.org", TRUE },
{ "strongswan.org", "email:strongswan.org", "email:strongswan.org", "strongswan.com", "vpn.strongswan.org", NULL, FALSE },
{ "strongswan.org", "email:strongswan.org", "email:strongswan.org", "strongswan.com", "tester@strongswan.org", NULL, TRUE },
{ "strongswan.org", "email:strongswan.org", "email:strongswan.org", "strongswan.com", "vpn.strongswan.org", "tester@strongswan.org", FALSE },
};
START_TEST(test_permitted_san_multi)
{
certificate_t *ca, *im, *sj;
ca = create_cert_multi(NULL, "CN=CA", X509_CA, NULL, NULL,
permitted_san_multi[_i].caconst1,
permitted_san_multi[_i].caconst2, NULL, NULL);
im = create_cert_multi(ca, "CN=IM", X509_CA, NULL, NULL,
permitted_san_multi[_i].imconst1,
permitted_san_multi[_i].imconst2, NULL, NULL);
sj = create_cert_multi(im, "CN=EE", 0,
permitted_san_multi[_i].san1,
permitted_san_multi[_i].san2, NULL, NULL, 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_san_multi[_i].good);
}
END_TEST
static struct {
char *caconst1;
char *caconst2;
char *imconst1;
char *imconst2;
char *san1;
char *san2;
bool good;
} excluded_san_multi[] = {
{ "strongswan.org", "strongswan.com", NULL, NULL, "vpn.strongswan.org", NULL, FALSE },
{ "strongswan.org", "strongswan.com", NULL, NULL, "tester@strongswan.org", NULL, TRUE },
{ "strongswan.org", "strongswan.com", NULL, NULL, "vpn.strongswan.com", NULL, FALSE },
{ "strongswan.org", "strongswan.com", NULL, NULL, "vpn.strongswan.net", NULL, TRUE },
{ "strongswan.org", "strongswan.com", NULL, NULL, "vpn.strongswan.org", "vpn.strongswan.com", FALSE },
{ "strongswan.org", "strongswan.com", NULL, NULL, "vpn.strongswan.org", "vpn.strongswan.net", FALSE },
{ "strongswan.org", NULL, NULL, NULL, "vpn.strongswan.org", "vpn.strongswan.com", FALSE },
{ "strongswan.org", NULL, NULL, NULL, "vpn.strongswan.com", "vpn.strongswan.org", FALSE },
{ NULL, NULL, "strongswan.org", "strongswan.com", "vpn.strongswan.org", NULL, FALSE },
{ NULL, NULL, "strongswan.org", "strongswan.com", "vpn.strongswan.com", NULL, FALSE },
{ NULL, NULL, "strongswan.org", "strongswan.com", "vpn.strongswan.net", NULL, TRUE },
{ NULL, NULL, "strongswan.org", "strongswan.com", "vpn.strongswan.org", "vpn.strongswan.com", FALSE },
{ "strongswan.org", "strongswan.com", "strongswan.net", NULL, "vpn.strongswan.net", NULL, FALSE },
{ "strongswan.net", NULL, "strongswan.org", "strongswan.com", "vpn.strongswan.net", NULL, FALSE },
{ "strongswan.net", NULL, "strongswan.org", "strongswan.com", "vpn.strongswan.org", NULL, FALSE },
{ "strongswan.net", NULL, "strongswan.org", "strongswan.com", "vpn.strongswan.com", NULL, FALSE },
{ "vpn.strongswan.org", "vpn.strongswan.com", "strongswan.org", NULL, "a.strongswan.org", NULL, FALSE },
{ "vpn.strongswan.org", "vpn.strongswan.com", "strongswan.org", NULL, "vpn.strongswan.com", NULL, FALSE },
{ "vpn.strongswan.org", "vpn.strongswan.com", "strongswan.org", NULL, "a.strongswan.com", NULL, TRUE },
{ "vpn.strongswan.org", "vpn.strongswan.com", "strongswan.org", "strongswan.com", "a.strongswan.com", NULL, FALSE },
{ "strongswan.org", "email:strongswan.org", NULL, NULL, "vpn.strongswan.org", NULL, FALSE },
{ "strongswan.org", "email:strongswan.org", NULL, NULL, "tester@strongswan.org", NULL, FALSE },
};
START_TEST(test_excluded_san_multi)
{
certificate_t *ca, *im, *sj;
ca = create_cert_multi(NULL, "CN=CA", X509_CA, NULL, NULL, NULL, NULL,
excluded_san_multi[_i].caconst1,
excluded_san_multi[_i].caconst2);
im = create_cert_multi(ca, "CN=IM", X509_CA, NULL, NULL, NULL, NULL,
excluded_san_multi[_i].imconst1,
excluded_san_multi[_i].imconst2);
sj = create_cert_multi(im, "CN=EE", 0,
excluded_san_multi[_i].san1,
excluded_san_multi[_i].san2, NULL, NULL, 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_san_multi[_i].good);
}
END_TEST
@ -385,14 +688,34 @@ Suite *certnames_suite_create()
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");
tc = tcase_create("permitted DN name constraints multilevel");
tcase_add_checked_fixture(tc, setup, teardown);
tcase_add_loop_test(tc, test_permitted_dninh, 0, countof(permitted_dninh));
tcase_add_loop_test(tc, test_permitted_dn_levels, 0, countof(permitted_dn_levels));
suite_add_tcase(s, tc);
tc = tcase_create("excluded DN name constraint inherit");
tc = tcase_create("permitted subjectAltName constraints multilevel");
tcase_add_checked_fixture(tc, setup, teardown);
tcase_add_loop_test(tc, test_excluded_dninh, 0, countof(excluded_dninh));
tcase_add_loop_test(tc, test_permitted_san_levels, 0, countof(permitted_san_levels));
suite_add_tcase(s, tc);
tc = tcase_create("excluded DN name constraints multilevel");
tcase_add_checked_fixture(tc, setup, teardown);
tcase_add_loop_test(tc, test_excluded_dn_levels, 0, countof(excluded_dn_levels));
suite_add_tcase(s, tc);
tc = tcase_create("excluded subjectAltName constraints multilevel");
tcase_add_checked_fixture(tc, setup, teardown);
tcase_add_loop_test(tc, test_excluded_san_levels, 0, countof(excluded_san_levels));
suite_add_tcase(s, tc);
tc = tcase_create("permitted subjectAltName constraints multivalue");
tcase_add_checked_fixture(tc, setup, teardown);
tcase_add_loop_test(tc, test_permitted_san_multi, 0, countof(permitted_san_multi));
suite_add_tcase(s, tc);
tc = tcase_create("excluded subjectAltName constraints multivalue");
tcase_add_checked_fixture(tc, setup, teardown);
tcase_add_loop_test(tc, test_excluded_san_multi, 0, countof(excluded_san_multi));
suite_add_tcase(s, tc);
return s;