Merge branch 'name-constraints'

This refactors the name constraints validation in the revocation plugin
so it aligns with what's specified in RFC 5820.

It also expands the subnet/range matching for identities.

Closes strongswan/strongswan#2114
This commit is contained in:
Tobias Brunner 2024-03-13 15:05:14 +01:00
commit ddd926b698
5 changed files with 1392 additions and 286 deletions

View File

@ -1553,7 +1553,7 @@ ADD_PLUGIN([random], [s charon pki scripts manager medsrv attest n
ADD_PLUGIN([nonce], [s charon nm cmd aikgen])
ADD_PLUGIN([x509], [s charon pki scripts attest nm cmd aikgen fuzz])
ADD_PLUGIN([revocation], [s charon pki nm cmd])
ADD_PLUGIN([constraints], [s charon nm cmd])
ADD_PLUGIN([constraints], [s charon pki nm cmd])
ADD_PLUGIN([acert], [s charon])
ADD_PLUGIN([pubkey], [s charon pki cmd aikgen])
ADD_PLUGIN([pkcs1], [s charon pki scripts manager medsrv attest nm cmd aikgen fuzz])

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;

View File

@ -786,7 +786,7 @@ START_TEST(test_matches_binary)
}
END_TEST
START_TEST(test_matches_range)
START_TEST(test_matches_range_addr)
{
identification_t *a, *b;
@ -869,6 +869,242 @@ START_TEST(test_matches_range)
}
END_TEST
START_TEST(test_matches_range_subnet)
{
identification_t *a, *b;
/* IPv4 subnets */
a = identification_create_from_string("192.168.1.0/24");
ck_assert(a->get_type(a) == ID_IPV4_ADDR_SUBNET);
ck_assert(id_matches(a, "%any", ID_MATCH_ANY));
ck_assert(id_matches(a, "0.0.0.0/0", ID_MATCH_MAX_WILDCARDS));
ck_assert(id_matches(a, "192.168.1.1", ID_MATCH_NONE));
ck_assert(id_matches(a, "192.168.1.2", ID_MATCH_NONE));
ck_assert(id_matches(a, "192.168.1.1/32", ID_MATCH_NONE));
ck_assert(id_matches(a, "192.168.1.0/32", ID_MATCH_NONE));
ck_assert(id_matches(a, "192.168.1.0/24", ID_MATCH_PERFECT));
ck_assert(id_matches(a, "192.168.0.0/24", ID_MATCH_NONE));
ck_assert(id_matches(a, "192.168.0.0/16", ID_MATCH_ONE_WILDCARD));
ck_assert(id_matches(a, "192.168.1.1-192.168.1.1", ID_MATCH_NONE));
ck_assert(id_matches(a, "192.168.1.0-192.168.1.255", ID_MATCH_PERFECT));
ck_assert(id_matches(a, "192.168.0.0-192.168.255.255", ID_MATCH_ONE_WILDCARD));
ck_assert(id_matches(a, "foo@bar", ID_MATCH_NONE));
/* Malformed IPv4 subnet and range encoding */
b = identification_create_from_encoding(ID_IPV4_ADDR_SUBNET, chunk_empty);
ck_assert(a->matches(a, b) == ID_MATCH_NONE);
b->destroy(b);
b = identification_create_from_encoding(ID_IPV4_ADDR_RANGE, chunk_empty);
ck_assert(a->matches(a, b) == ID_MATCH_NONE);
b->destroy(b);
b = identification_create_from_encoding(ID_IPV4_ADDR_RANGE,
chunk_from_chars(0xc0,0xa8,0x01,0x28,0xc0,0xa8,0x01,0x00));
ck_assert(a->matches(a, b) == ID_MATCH_NONE);
b->destroy(b);
a->destroy(a);
a = identification_create_from_string("192.168.1.1/32");
ck_assert(id_matches(a, "%any", ID_MATCH_ANY));
ck_assert(id_matches(a, "0.0.0.0/0", ID_MATCH_MAX_WILDCARDS));
ck_assert(id_matches(a, "192.168.1.1/32", ID_MATCH_PERFECT));
ck_assert(id_matches(a, "192.168.1.1/31", ID_MATCH_ONE_WILDCARD));
ck_assert(id_matches(a, "192.168.1.1", ID_MATCH_PERFECT));
ck_assert(id_matches(a, "192.168.1.2", ID_MATCH_NONE));
ck_assert(id_matches(a, "192.168.1.0/24", ID_MATCH_ONE_WILDCARD));
ck_assert(id_matches(a, "192.168.1.0-192.168.1.0", ID_MATCH_NONE));
ck_assert(id_matches(a, "192.168.1.1-192.168.1.1", ID_MATCH_PERFECT));
ck_assert(id_matches(a, "192.168.1.0-192.168.1.1", ID_MATCH_ONE_WILDCARD));
ck_assert(id_matches(a, "192.168.1.1-192.168.1.2", ID_MATCH_ONE_WILDCARD));
ck_assert(id_matches(a, "192.168.0.0-192.168.1.255", ID_MATCH_ONE_WILDCARD));
a->destroy(a);
/* IPv6 subnets */
a = identification_create_from_string("fec0::0/64");
ck_assert(a->get_type(a) == ID_IPV6_ADDR_SUBNET);
ck_assert(id_matches(a, "%any", ID_MATCH_ANY));
ck_assert(id_matches(a, "::/0", ID_MATCH_MAX_WILDCARDS));
ck_assert(id_matches(a, "fec0::1", ID_MATCH_NONE));
ck_assert(id_matches(a, "fec0::2", ID_MATCH_NONE));
ck_assert(id_matches(a, "fec0::1/128", ID_MATCH_NONE));
ck_assert(id_matches(a, "fec0::/128", ID_MATCH_NONE));
ck_assert(id_matches(a, "fec0::/64", ID_MATCH_PERFECT));
ck_assert(id_matches(a, "fec0::/48", ID_MATCH_ONE_WILDCARD));
ck_assert(id_matches(a, "fec1::/48", ID_MATCH_NONE));
ck_assert(id_matches(a, "fec0::-fec0::1", ID_MATCH_NONE));
ck_assert(id_matches(a, "fec0::-fec0::ffff:ffff:ffff:ffff", ID_MATCH_PERFECT));
ck_assert(id_matches(a, "fec0::-fec0::f:ffff:ffff:ffff:ffff", ID_MATCH_ONE_WILDCARD));
ck_assert(id_matches(a, "fec0::4001-fec0::4ffe", ID_MATCH_NONE));
ck_assert(id_matches(a, "feb0::1-fec0::0", ID_MATCH_NONE));
ck_assert(id_matches(a, "foo@bar", ID_MATCH_NONE));
/* Malformed IPv6 subnet and range encoding */
b = identification_create_from_encoding(ID_IPV6_ADDR_SUBNET, chunk_empty);
ck_assert(a->matches(a, b) == ID_MATCH_NONE);
b->destroy(b);
b = identification_create_from_encoding(ID_IPV6_ADDR_RANGE, chunk_empty);
ck_assert(a->matches(a, b) == ID_MATCH_NONE);
b->destroy(b);
b = identification_create_from_encoding(ID_IPV6_ADDR_RANGE,
chunk_from_chars(0xfe,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x4f,0xff,
0xfe,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01 ));
ck_assert(a->matches(a, b) == ID_MATCH_NONE);
b->destroy(b);
a->destroy(a);
a = identification_create_from_string("fec0::1/128");
ck_assert(a->get_type(a) == ID_IPV6_ADDR_SUBNET);
ck_assert(id_matches(a, "%any", ID_MATCH_ANY));
ck_assert(id_matches(a, "::/0", ID_MATCH_MAX_WILDCARDS));
ck_assert(id_matches(a, "fec0::1", ID_MATCH_PERFECT));
ck_assert(id_matches(a, "fec0::2", ID_MATCH_NONE));
ck_assert(id_matches(a, "fec0::1/128", ID_MATCH_PERFECT));
ck_assert(id_matches(a, "fec0::/128", ID_MATCH_NONE));
ck_assert(id_matches(a, "fec0::/64", ID_MATCH_ONE_WILDCARD));
ck_assert(id_matches(a, "fec1::/48", ID_MATCH_NONE));
ck_assert(id_matches(a, "fec0::0-fec0::0", ID_MATCH_NONE));
ck_assert(id_matches(a, "fec0::1-fec0::1", ID_MATCH_PERFECT));
ck_assert(id_matches(a, "fec0::0-fec0::1", ID_MATCH_ONE_WILDCARD));
ck_assert(id_matches(a, "fec0::1-fec0::2", ID_MATCH_ONE_WILDCARD));
ck_assert(id_matches(a, "fec0::-fec0::ffff:ffff:ffff:ffff", ID_MATCH_ONE_WILDCARD));
a->destroy(a);
/* Malformed IPv4 subnet encoding */
a = identification_create_from_encoding(ID_IPV4_ADDR_SUBNET, chunk_empty);
ck_assert(id_matches(a, "192.168.1.1", ID_MATCH_NONE));
ck_assert(id_matches(a, "0.0.0.0/0", ID_MATCH_NONE));
ck_assert(id_matches(a, "0.0.0.0-255.255.255.255", ID_MATCH_NONE));
a->destroy(a);
/* Malformed IPv6 subnet encoding */
a = identification_create_from_encoding(ID_IPV6_ADDR_SUBNET, chunk_empty);
ck_assert(id_matches(a, "fec0::1", ID_MATCH_NONE));
ck_assert(id_matches(a, "::/0", ID_MATCH_NONE));
ck_assert(id_matches(a, "::-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", ID_MATCH_NONE));
a->destroy(a);
}
END_TEST
START_TEST(test_matches_range_range)
{
identification_t *a, *b;
/* IPv4 ranges */
a = identification_create_from_string("192.168.1.0-192.168.1.255");
ck_assert(a->get_type(a) == ID_IPV4_ADDR_RANGE);
ck_assert(id_matches(a, "%any", ID_MATCH_ANY));
ck_assert(id_matches(a, "0.0.0.0/0", ID_MATCH_MAX_WILDCARDS));
ck_assert(id_matches(a, "192.168.1.1", ID_MATCH_NONE));
ck_assert(id_matches(a, "192.168.1.1/32", ID_MATCH_NONE));
ck_assert(id_matches(a, "192.168.1.0/24", ID_MATCH_PERFECT));
ck_assert(id_matches(a, "192.168.0.0/24", ID_MATCH_NONE));
ck_assert(id_matches(a, "192.168.0.0/16", ID_MATCH_ONE_WILDCARD));
ck_assert(id_matches(a, "192.168.1.1-192.168.1.1", ID_MATCH_NONE));
ck_assert(id_matches(a, "192.168.1.0-192.168.1.255", ID_MATCH_PERFECT));
ck_assert(id_matches(a, "192.168.0.0-192.168.255.255", ID_MATCH_ONE_WILDCARD));
ck_assert(id_matches(a, "192.168.0.240-192.168.1.0", ID_MATCH_NONE));
ck_assert(id_matches(a, "foo@bar", ID_MATCH_NONE));
/* Malformed IPv4 subnet and range encoding */
b = identification_create_from_encoding(ID_IPV4_ADDR_SUBNET, chunk_empty);
ck_assert(a->matches(a, b) == ID_MATCH_NONE);
b->destroy(b);
b = identification_create_from_encoding(ID_IPV4_ADDR_RANGE, chunk_empty);
ck_assert(a->matches(a, b) == ID_MATCH_NONE);
b->destroy(b);
b = identification_create_from_encoding(ID_IPV4_ADDR_RANGE,
chunk_from_chars(0xc0,0xa8,0x01,0x28,0xc0,0xa8,0x01,0x00));
ck_assert(a->matches(a, b) == ID_MATCH_NONE);
b->destroy(b);
a->destroy(a);
a = identification_create_from_string("192.168.1.1-192.168.1.1");
ck_assert(a->get_type(a) == ID_IPV4_ADDR_RANGE);
ck_assert(id_matches(a, "%any", ID_MATCH_ANY));
ck_assert(id_matches(a, "0.0.0.0/0", ID_MATCH_MAX_WILDCARDS));
ck_assert(id_matches(a, "192.168.1.1", ID_MATCH_PERFECT));
ck_assert(id_matches(a, "192.168.1.2", ID_MATCH_NONE));
ck_assert(id_matches(a, "192.168.1.1/32", ID_MATCH_PERFECT));
ck_assert(id_matches(a, "192.168.1.0/32", ID_MATCH_NONE));
ck_assert(id_matches(a, "192.168.1.0/24", ID_MATCH_ONE_WILDCARD));
ck_assert(id_matches(a, "192.168.0.0/24", ID_MATCH_NONE));
ck_assert(id_matches(a, "192.168.1.1-192.168.1.1", ID_MATCH_PERFECT));
ck_assert(id_matches(a, "192.168.1.0-192.168.1.0", ID_MATCH_NONE));
ck_assert(id_matches(a, "192.168.1.1-192.168.1.1", ID_MATCH_PERFECT));
ck_assert(id_matches(a, "192.168.1.0-192.168.1.1", ID_MATCH_ONE_WILDCARD));
ck_assert(id_matches(a, "192.168.1.1-192.168.1.2", ID_MATCH_ONE_WILDCARD));
ck_assert(id_matches(a, "192.168.0.0-192.168.1.255", ID_MATCH_ONE_WILDCARD));
ck_assert(id_matches(a, "foo@bar", ID_MATCH_NONE));
a->destroy(a);
/* IPv6 ranges */
a = identification_create_from_string("fec0::-fec0::ffff:ffff:ffff:ffff");
ck_assert(a->get_type(a) == ID_IPV6_ADDR_RANGE);
ck_assert(id_matches(a, "%any", ID_MATCH_ANY));
ck_assert(id_matches(a, "::/0", ID_MATCH_MAX_WILDCARDS));
ck_assert(id_matches(a, "fec0::1", ID_MATCH_NONE));
ck_assert(id_matches(a, "fec0::1/128", ID_MATCH_NONE));
ck_assert(id_matches(a, "fec0::/128", ID_MATCH_NONE));
ck_assert(id_matches(a, "fec0::/64", ID_MATCH_PERFECT));
ck_assert(id_matches(a, "fec0::/48", ID_MATCH_ONE_WILDCARD));
ck_assert(id_matches(a, "fec1::/48", ID_MATCH_NONE));
ck_assert(id_matches(a, "fec0::-fec0::1", ID_MATCH_NONE));
ck_assert(id_matches(a, "fec0::-fec0::ffff:ffff:ffff:ffff", ID_MATCH_PERFECT));
ck_assert(id_matches(a, "fec0::-fec0::f:ffff:ffff:ffff:ffff", ID_MATCH_ONE_WILDCARD));
ck_assert(id_matches(a, "fec0::4001-fec0::4ffe", ID_MATCH_NONE));
ck_assert(id_matches(a, "feb0::1-fec0::0", ID_MATCH_NONE));
ck_assert(id_matches(a, "foo@bar", ID_MATCH_NONE));
/* Malformed IPv6 subnet and range encoding */
b = identification_create_from_encoding(ID_IPV6_ADDR_SUBNET, chunk_empty);
ck_assert(a->matches(a, b) == ID_MATCH_NONE);
b->destroy(b);
b = identification_create_from_encoding(ID_IPV6_ADDR_RANGE, chunk_empty);
ck_assert(a->matches(a, b) == ID_MATCH_NONE);
b->destroy(b);
b = identification_create_from_encoding(ID_IPV6_ADDR_RANGE,
chunk_from_chars(0xfe,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x4f,0xff,
0xfe,0xc0,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x01 ));
ck_assert(a->matches(a, b) == ID_MATCH_NONE);
b->destroy(b);
a->destroy(a);
a = identification_create_from_string("fec0::1-fec0::1");
ck_assert(a->get_type(a) == ID_IPV6_ADDR_RANGE);
ck_assert(id_matches(a, "%any", ID_MATCH_ANY));
ck_assert(id_matches(a, "::/0", ID_MATCH_MAX_WILDCARDS));
ck_assert(id_matches(a, "fec0::1", ID_MATCH_PERFECT));
ck_assert(id_matches(a, "fec0::2", ID_MATCH_NONE));
ck_assert(id_matches(a, "fec0::1/128", ID_MATCH_PERFECT));
ck_assert(id_matches(a, "fec0::/128", ID_MATCH_NONE));
ck_assert(id_matches(a, "fec0::/64", ID_MATCH_ONE_WILDCARD));
ck_assert(id_matches(a, "fec1::/48", ID_MATCH_NONE));
ck_assert(id_matches(a, "fec0::0-fec0::0", ID_MATCH_NONE));
ck_assert(id_matches(a, "fec0::1-fec0::1", ID_MATCH_PERFECT));
ck_assert(id_matches(a, "fec0::0-fec0::1", ID_MATCH_ONE_WILDCARD));
ck_assert(id_matches(a, "fec0::1-fec0::2", ID_MATCH_ONE_WILDCARD));
ck_assert(id_matches(a, "fec0::-fec0::ffff:ffff:ffff:ffff", ID_MATCH_ONE_WILDCARD));
a->destroy(a);
/* Malformed IPv4 range encoding */
a = identification_create_from_encoding(ID_IPV4_ADDR_RANGE, chunk_empty);
ck_assert(id_matches(a, "192.168.1.1", ID_MATCH_NONE));
ck_assert(id_matches(a, "0.0.0.0/0", ID_MATCH_NONE));
ck_assert(id_matches(a, "0.0.0.0-255.255.255.255", ID_MATCH_NONE));
a->destroy(a);
/* Malformed IPv6 range encoding */
a = identification_create_from_encoding(ID_IPV6_ADDR_RANGE, chunk_empty);
ck_assert(id_matches(a, "fec0::1", ID_MATCH_NONE));
ck_assert(id_matches(a, "::/0", ID_MATCH_NONE));
ck_assert(id_matches(a, "::-ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", ID_MATCH_NONE));
a->destroy(a);
}
END_TEST
START_TEST(test_matches_string)
{
identification_t *a;
@ -1204,7 +1440,9 @@ Suite *identification_suite_create()
tcase_add_loop_test(tc, test_matches_two_ou, 0, countof(rdn_matching));
tcase_add_test(tc, test_matches_any);
tcase_add_test(tc, test_matches_binary);
tcase_add_test(tc, test_matches_range);
tcase_add_test(tc, test_matches_range_addr);
tcase_add_test(tc, test_matches_range_subnet);
tcase_add_test(tc, test_matches_range_range);
tcase_add_test(tc, test_matches_string);
tcase_add_loop_test(tc, test_matches_empty, ID_ANY, ID_KEY_ID + 1);
tcase_add_loop_test(tc, test_matches_empty_reverse, ID_ANY, ID_KEY_ID + 1);

View File

@ -1046,10 +1046,9 @@ METHOD(identification_t, matches_dn_relaxed, id_match_t,
/**
* Transform netmask to CIDR bits
*/
static int netmask_to_cidr(char *netmask, size_t address_size)
static uint8_t netmask_to_cidr(char *netmask, uint8_t address_size)
{
uint8_t byte;
int i, netbits = 0;
uint8_t i, byte, netbits = 0;
for (i = 0; i < address_size; i++)
{
@ -1075,118 +1074,369 @@ static int netmask_to_cidr(char *netmask, size_t address_size)
return netbits;
}
/**
* Converts the given network/netmask to an address range
*/
static void subnet_to_range(chunk_t encoding, uint8_t address_size,
uint8_t *from, uint8_t *to)
{
uint8_t *network, *netmask, netbits, mask, i;
network = encoding.ptr;
netmask = encoding.ptr + address_size;
netbits = netmask_to_cidr(netmask, address_size);
memcpy(from, network, address_size);
memcpy(to, network, address_size);
i = netbits / 8;
if (i < address_size)
{
mask = 0xff << (8 - netbits % 8);
from[i] = from[i] & mask;
to[i] = to[i] | ~mask;
memset(&from[i+1], 0, address_size - i - 1);
memset(&to[i+1], 0xff, address_size - i - 1);
}
}
/**
* Matches one subnet (or address if netmask is NULL) against another
*/
static id_match_t match_subnets(uint8_t *network, uint8_t *netmask,
uint8_t *other_network, uint8_t *other_netmask,
uint8_t address_size)
{
uint8_t netbits, other_netbits, i;
other_netbits = other_netmask ? netmask_to_cidr(other_netmask, address_size)
: 8 * address_size;
if (!other_netbits)
{
return ID_MATCH_MAX_WILDCARDS;
}
netbits = netmask ? netmask_to_cidr(netmask, address_size) : 8 * address_size;
if (netbits == other_netbits)
{
return memeq(network, other_network, address_size) ? ID_MATCH_PERFECT
: ID_MATCH_NONE;
}
else if (netbits < other_netbits)
{
return ID_MATCH_NONE;
}
for (i = 0; i < (other_netbits + 7)/8; i++)
{
if ((network[i] ^ other_network[i]) & other_netmask[i])
{
return ID_MATCH_NONE;
}
}
return ID_MATCH_ONE_WILDCARD;
}
/**
* Matches two address ranges against each other
*/
static id_match_t match_ranges(uint8_t *from, uint8_t *to,
uint8_t *other_from, uint8_t *other_to,
uint8_t address_size)
{
const uint8_t zeroes[16] = { 0 };
const uint8_t ones[16] = { 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff };
int match_from, match_to;
if (memcmp(to, from, address_size) < 0 ||
memcmp(other_to, other_from, address_size) < 0)
{
/* to is smaller than from in one of the ranges */
return ID_MATCH_NONE;
}
else if (memeq(other_from, zeroes, address_size) &&
memeq(other_to, ones, address_size))
{
return ID_MATCH_MAX_WILDCARDS;
}
match_from = memcmp(from, other_from, address_size);
match_to = memcmp(to, other_to, address_size);
if (!match_from && !match_to)
{
return ID_MATCH_PERFECT;
}
else if (match_from >= 0 && match_to <= 0)
{
return ID_MATCH_ONE_WILDCARD;
}
return ID_MATCH_NONE;
}
/**
* Match a subnet to an address
*/
static id_match_t matches_subnet_to_addr(private_identification_t *this,
identification_t *other,
uint8_t address_size)
{
chunk_t other_encoding;
uint8_t *network, *netmask, *address;
other_encoding = other->get_encoding(other);
if (this->encoded.len != 2 * address_size ||
other_encoding.len != address_size)
{
return ID_MATCH_NONE;
}
network = this->encoded.ptr;
netmask = this->encoded.ptr + address_size;
address = other_encoding.ptr;
return match_subnets(network, netmask, address, NULL, address_size);
}
/**
* Match a subnet to an address
*/
static id_match_t matches_range_to_addr(private_identification_t *this,
identification_t *other,
uint8_t address_size)
{
chunk_t other_encoding;
uint8_t *from, *to, *address;
other_encoding = other->get_encoding(other);
if (this->encoded.len != 2 * address_size ||
other_encoding.len != address_size)
{
return ID_MATCH_NONE;
}
from = this->encoded.ptr;
to = this->encoded.ptr + address_size;
address = other_encoding.ptr;
return match_ranges(from, to, address, address, address_size);
}
/**
* Matches an address to a subnet
*/
static id_match_t matches_addr_to_subnet(private_identification_t *this,
identification_t *other,
uint8_t address_size)
{
chunk_t other_encoding;
uint8_t *address, *network, *netmask;
other_encoding = other->get_encoding(other);
if (this->encoded.len != address_size ||
other_encoding.len != 2 * address_size)
{
return ID_MATCH_NONE;
}
address = this->encoded.ptr;
network = other_encoding.ptr;
netmask = other_encoding.ptr + address_size;
return match_subnets(address, NULL, network, netmask, address_size);
}
/**
* Matches a subnet to a subnet
*/
static id_match_t matches_subnet_to_subnet(private_identification_t *this,
identification_t *other,
uint8_t address_size)
{
chunk_t other_encoding;
uint8_t *network, *netmask, *other_network, *other_netmask;
other_encoding = other->get_encoding(other);
if (this->encoded.len != 2 * address_size ||
other_encoding.len != 2 * address_size)
{
return ID_MATCH_NONE;
}
network = this->encoded.ptr;
netmask = this->encoded.ptr + address_size;
other_network = other_encoding.ptr;
other_netmask = other_encoding.ptr + address_size;
return match_subnets(network, netmask, other_network, other_netmask,
address_size);
}
/**
* Matches a range to a subnet
*/
static id_match_t matches_range_to_subnet(private_identification_t *this,
identification_t *other,
uint8_t address_size)
{
chunk_t other_encoding;
uint8_t *from, *to, other_from[address_size], other_to[address_size];
other_encoding = other->get_encoding(other);
if (this->encoded.len != 2 * address_size ||
other_encoding.len != 2 * address_size)
{
return ID_MATCH_NONE;
}
from = this->encoded.ptr;
to = this->encoded.ptr + address_size;
subnet_to_range(other_encoding, address_size, other_from, other_to);
return match_ranges(from, to, other_from, other_to, address_size);
}
/**
* Match an address to a range
*/
static id_match_t matches_addr_to_range(private_identification_t *this,
identification_t *other,
uint8_t address_size)
{
chunk_t other_encoding;
uint8_t *address, *from, *to;
other_encoding = other->get_encoding(other);
if (this->encoded.len != address_size ||
other_encoding.len != 2 * address_size)
{
return ID_MATCH_NONE;
}
address = this->encoded.ptr;
from = other_encoding.ptr;
to = other_encoding.ptr + address_size;
return match_ranges(address, address, from, to, address_size);
}
/**
* Match a subnet to a range
*/
static id_match_t matches_subnet_to_range(private_identification_t *this,
identification_t *other,
uint8_t address_size)
{
chunk_t other_encoding;
uint8_t from[address_size], to[address_size], *other_from, *other_to;
other_encoding = other->get_encoding(other);
if (this->encoded.len != 2 * address_size ||
other_encoding.len != 2 * address_size)
{
return ID_MATCH_NONE;
}
subnet_to_range(this->encoded, address_size, from, to);
other_from = other_encoding.ptr;
other_to = other_encoding.ptr + address_size;
return match_ranges(from, to, other_from, other_to, address_size);
}
/**
* Match a range to a range
*/
static id_match_t matches_range_to_range(private_identification_t *this,
identification_t *other,
uint8_t address_size)
{
chunk_t other_encoding;
uint8_t *from, *to, *other_from, *other_to;
other_encoding = other->get_encoding(other);
if (this->encoded.len != 2 * address_size ||
other_encoding.len != 2 * address_size)
{
return ID_MATCH_NONE;
}
from = this->encoded.ptr;
to = this->encoded.ptr + address_size;
other_from = other_encoding.ptr;
other_to = other_encoding.ptr + address_size;
return match_ranges(from, to, other_from, other_to, address_size);
}
METHOD(identification_t, matches_range, id_match_t,
private_identification_t *this, identification_t *other)
{
chunk_t other_encoding;
uint8_t *address, *from, *to, *network, *netmask;
size_t address_size = 0;
int netbits, range_sign, i;
id_type_t other_type;
uint8_t address_size = 0;
if (other->get_type(other) == ID_ANY)
other_type = other->get_type(other);
if (other_type == ID_ANY)
{
return ID_MATCH_ANY;
}
if (this->type == other->get_type(other) &&
if (this->type == other_type &&
chunk_equals(this->encoded, other->get_encoding(other)))
{
return ID_MATCH_PERFECT;
}
if ((this->type == ID_IPV4_ADDR &&
other->get_type(other) == ID_IPV4_ADDR_SUBNET))
if (other_type == ID_IPV4_ADDR &&
(this->type == ID_IPV4_ADDR_SUBNET || this->type == ID_IPV4_ADDR_RANGE))
{
address_size = sizeof(struct in_addr);
}
else if ((this->type == ID_IPV6_ADDR &&
other->get_type(other) == ID_IPV6_ADDR_SUBNET))
else if (other_type == ID_IPV6_ADDR &&
(this->type == ID_IPV6_ADDR_SUBNET || this->type == ID_IPV6_ADDR_RANGE))
{
address_size = sizeof(struct in6_addr);
}
if (address_size)
{
other_encoding = other->get_encoding(other);
if (this->encoded.len != address_size ||
other_encoding.len != 2 * address_size)
if (this->type == ID_IPV4_ADDR_SUBNET || this->type == ID_IPV6_ADDR_SUBNET)
{
return ID_MATCH_NONE;
return matches_subnet_to_addr(this, other, address_size);
}
address = this->encoded.ptr;
network = other_encoding.ptr;
netmask = other_encoding.ptr + address_size;
netbits = netmask_to_cidr(netmask, address_size);
if (netbits == 0)
{
return ID_MATCH_MAX_WILDCARDS;
}
if (netbits == 8 * address_size)
{
return memeq(address, network, address_size) ?
ID_MATCH_PERFECT : ID_MATCH_NONE;
}
for (i = 0; i < (netbits + 7)/8; i++)
{
if ((address[i] ^ network[i]) & netmask[i])
{
return ID_MATCH_NONE;
}
}
return ID_MATCH_ONE_WILDCARD;
return matches_range_to_addr(this, other, address_size);
}
if ((this->type == ID_IPV4_ADDR &&
other->get_type(other) == ID_IPV4_ADDR_RANGE))
if (other_type == ID_IPV4_ADDR_SUBNET &&
(this->type == ID_IPV4_ADDR || this->type == ID_IPV4_ADDR_SUBNET ||
this->type == ID_IPV4_ADDR_RANGE))
{
address_size = sizeof(struct in_addr);
}
else if ((this->type == ID_IPV6_ADDR &&
other->get_type(other) == ID_IPV6_ADDR_RANGE))
else if (other_type == ID_IPV6_ADDR_SUBNET &&
(this->type == ID_IPV6_ADDR || this->type == ID_IPV6_ADDR_SUBNET ||
this->type == ID_IPV6_ADDR_RANGE))
{
address_size = sizeof(struct in6_addr);
}
if (address_size)
{
other_encoding = other->get_encoding(other);
if (this->encoded.len != address_size ||
other_encoding.len != 2 * address_size)
if (this->type == ID_IPV4_ADDR || this->type == ID_IPV6_ADDR)
{
return ID_MATCH_NONE;
return matches_addr_to_subnet(this, other, address_size);
}
address = this->encoded.ptr;
from = other_encoding.ptr;
to = other_encoding.ptr + address_size;
range_sign = memcmp(to, from, address_size);
if (range_sign < 0)
{ /* to is smaller than from */
return ID_MATCH_NONE;
}
/* check lower bound */
for (i = 0; i < address_size; i++)
else if (this->type == ID_IPV4_ADDR_SUBNET || this->type == ID_IPV6_ADDR_SUBNET)
{
if (address[i] != from[i])
{
if (address[i] < from[i])
{
return ID_MATCH_NONE;
}
break;
}
return matches_subnet_to_subnet(this, other, address_size);
}
/* check upper bound */
for (i = 0; i < address_size; i++)
return matches_range_to_subnet(this, other, address_size);
}
if (other_type == ID_IPV4_ADDR_RANGE &&
(this->type == ID_IPV4_ADDR || this->type == ID_IPV4_ADDR_SUBNET ||
this->type == ID_IPV4_ADDR_RANGE))
{
address_size = sizeof(struct in_addr);
}
else if (other_type == ID_IPV6_ADDR_RANGE &&
(this->type == ID_IPV6_ADDR || this->type == ID_IPV6_ADDR_SUBNET ||
this->type == ID_IPV6_ADDR_RANGE))
{
address_size = sizeof(struct in6_addr);
}
if (address_size)
{
if (this->type == ID_IPV4_ADDR || this->type == ID_IPV6_ADDR)
{
if (address[i] != to[i])
{
if (address[i] > to[i])
{
return ID_MATCH_NONE;
}
break;
}
return matches_addr_to_range(this, other, address_size);
}
return range_sign ? ID_MATCH_ONE_WILDCARD : ID_MATCH_PERFECT;
else if (this->type == ID_IPV4_ADDR_SUBNET || this->type == ID_IPV6_ADDR_SUBNET)
{
return matches_subnet_to_range(this, other, address_size);
}
return matches_range_to_range(this, other, address_size);
}
return ID_MATCH_NONE;
}
@ -1200,7 +1450,8 @@ int identification_printf_hook(printf_hook_data_t *data,
private_identification_t *this = *((private_identification_t**)(args[0]));
chunk_t proper;
char buf[BUF_LEN], *pos;
size_t len, address_size;
uint8_t address_size;
size_t len;
int written;
if (this == NULL)
@ -1402,6 +1653,10 @@ static private_identification_t *identification_create(id_type_t type)
break;
case ID_IPV4_ADDR:
case ID_IPV6_ADDR:
case ID_IPV4_ADDR_SUBNET:
case ID_IPV6_ADDR_SUBNET:
case ID_IPV4_ADDR_RANGE:
case ID_IPV6_ADDR_RANGE:
this->public.hash = _hash_binary;
this->public.equals = _equals_binary;
this->public.matches = _matches_range;