summaryrefslogtreecommitdiff
path: root/src/libstrongswan/plugins/constraints/constraints_validator.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libstrongswan/plugins/constraints/constraints_validator.c')
-rw-r--r--src/libstrongswan/plugins/constraints/constraints_validator.c217
1 files changed, 170 insertions, 47 deletions
diff --git a/src/libstrongswan/plugins/constraints/constraints_validator.c b/src/libstrongswan/plugins/constraints/constraints_validator.c
index 62ccc7108..a0f4a7465 100644
--- a/src/libstrongswan/plugins/constraints/constraints_validator.c
+++ b/src/libstrongswan/plugins/constraints/constraints_validator.c
@@ -52,16 +52,67 @@ static bool check_pathlen(x509_t *issuer, int pathlen)
}
/**
- * Check if a FQDN/RFC822 constraint matches (suffix match)
+ * Check if a FQDN constraint matches
*/
-static bool suffix_matches(identification_t *constraint, identification_t *id)
+static bool fqdn_matches(identification_t *constraint, identification_t *id)
{
- chunk_t c, i;
+ chunk_t c, i, diff;
c = constraint->get_encoding(constraint);
i = id->get_encoding(id);
- return i.len >= c.len && chunk_equals(c, chunk_skip(i, i.len - c.len));
+ if (!c.len || i.len < c.len)
+ {
+ return FALSE;
+ }
+ diff = chunk_create(i.ptr, i.len - c.len);
+ if (!chunk_equals(c, chunk_skip(i, diff.len)))
+ {
+ return FALSE;
+ }
+ if (!diff.len)
+ {
+ return TRUE;
+ }
+ if (c.ptr[0] == '.' || diff.ptr[diff.len - 1] == '.')
+ {
+ return TRUE;
+ }
+ return FALSE;
+}
+
+/**
+ * Check if a RFC822 constraint matches
+ */
+static bool email_matches(identification_t *constraint, identification_t *id)
+{
+ chunk_t c, i, diff;
+
+ c = constraint->get_encoding(constraint);
+ i = id->get_encoding(id);
+
+ if (!c.len || i.len < c.len)
+ {
+ return FALSE;
+ }
+ if (memchr(c.ptr, '@', c.len))
+ { /* constraint is a full email address */
+ return chunk_equals(c, i);
+ }
+ diff = chunk_create(i.ptr, i.len - c.len);
+ if (!diff.len || !chunk_equals(c, chunk_skip(i, diff.len)))
+ {
+ return FALSE;
+ }
+ if (c.ptr[0] == '.')
+ { /* constraint is domain, suffix match */
+ return TRUE;
+ }
+ if (diff.ptr[diff.len - 1] == '@')
+ { /* constraint is host specific, only username can be appended */
+ return TRUE;
+ }
+ return FALSE;
}
/**
@@ -121,8 +172,10 @@ static bool name_constraint_matches(identification_t *constraint,
switch (type)
{
case ID_FQDN:
+ matches = fqdn_matches(constraint, id);
+ break;
case ID_RFC822_ADDR:
- matches = suffix_matches(constraint, id);
+ matches = email_matches(constraint, id);
break;
case ID_DER_ASN1_DN:
matches = dn_matches(constraint, id);
@@ -151,7 +204,7 @@ static bool name_constraint_inherited(identification_t *constraint,
x509_t *x509, bool permitted)
{
enumerator_t *enumerator;
- identification_t *id;
+ identification_t *id, *a, *b;
bool inherited = FALSE;
id_type_t type;
@@ -166,28 +219,26 @@ static bool name_constraint_inherited(identification_t *constraint,
{
if (id->get_type(id) == type)
{
+ if (permitted)
+ { /* permitted constraint can be narrowed */
+ a = constraint;
+ b = id;
+ }
+ else
+ { /* excluded constraint can be widened */
+ a = id;
+ b = constraint;
+ }
switch (type)
{
case ID_FQDN:
+ inherited = fqdn_matches(a, b);
+ break;
case ID_RFC822_ADDR:
- if (permitted)
- { /* permitted constraint can be narrowed */
- inherited = suffix_matches(constraint, id);
- }
- else
- { /* excluded constraint can be widened */
- inherited = suffix_matches(id, constraint);
- }
+ inherited = email_matches(a, b);
break;
case ID_DER_ASN1_DN:
- if (permitted)
- {
- inherited = dn_matches(constraint, id);
- }
- else
- {
- inherited = dn_matches(id, constraint);
- }
+ inherited = dn_matches(a, b);
break;
default:
DBG1(DBG_CFG, "%N NameConstraint matching not implemented",
@@ -298,8 +349,7 @@ static bool has_policy(x509_t *issuer, chunk_t oid)
/**
* Check certificatePolicies.
*/
-static bool check_policy(x509_t *subject, x509_t *issuer, bool check,
- auth_cfg_t *auth)
+static bool check_policy(x509_t *subject, x509_t *issuer)
{
certificate_t *cert = (certificate_t*)subject;
x509_policy_mapping_t *mapping;
@@ -323,33 +373,85 @@ static bool check_policy(x509_t *subject, x509_t *issuer, bool check,
}
enumerator->destroy(enumerator);
- if (check)
+ enumerator = subject->create_cert_policy_enumerator(subject);
+ while (enumerator->enumerate(enumerator, &policy))
+ {
+ if (!has_policy(issuer, policy->oid))
+ {
+ oid = asn1_oid_to_string(policy->oid);
+ DBG1(DBG_CFG, "policy %s missing in issuing certificate '%Y'",
+ oid, cert->get_issuer(cert));
+ free(oid);
+ enumerator->destroy(enumerator);
+ return FALSE;
+ }
+ }
+ enumerator->destroy(enumerator);
+
+ return TRUE;
+}
+
+/**
+ * Check if a given policy is valid under a trustchain
+ */
+static bool is_policy_valid(linked_list_t *chain, chunk_t oid)
+{
+ x509_policy_mapping_t *mapping;
+ x509_cert_policy_t *policy;
+ x509_t *issuer;
+ enumerator_t *issuers, *policies, *mappings;
+ bool found = TRUE;
+
+ issuers = chain->create_enumerator(chain);
+ while (issuers->enumerate(issuers, &issuer))
{
- enumerator = subject->create_cert_policy_enumerator(subject);
- while (enumerator->enumerate(enumerator, &policy))
+ int maxmap = 8;
+
+ while (found)
{
- if (!has_policy(issuer, policy->oid))
+ found = FALSE;
+
+ policies = issuer->create_cert_policy_enumerator(issuer);
+ while (policies->enumerate(policies, &policy))
{
- oid = asn1_oid_to_string(policy->oid);
- DBG1(DBG_CFG, "policy %s missing in issuing certificate '%Y'",
- oid, cert->get_issuer(cert));
- free(oid);
- enumerator->destroy(enumerator);
- return FALSE;
+ if (chunk_equals(oid, policy->oid) ||
+ chunk_equals(any_policy, policy->oid))
+ {
+ found = TRUE;
+ break;
+ }
}
- if (auth)
+ policies->destroy(policies);
+ if (found)
{
- oid = asn1_oid_to_string(policy->oid);
- if (oid)
+ break;
+ }
+ /* fall back to a mapped policy */
+ mappings = issuer->create_policy_mapping_enumerator(issuer);
+ while (mappings->enumerate(mappings, &mapping))
+ {
+ if (chunk_equals(mapping->subject, oid))
{
- auth->add(auth, AUTH_RULE_CERT_POLICY, oid);
+ oid = mapping->issuer;
+ found = TRUE;
+ break;
}
}
+ mappings->destroy(mappings);
+ if (--maxmap == 0)
+ {
+ found = FALSE;
+ break;
+ }
+ }
+ if (!found)
+ {
+ break;
}
- enumerator->destroy(enumerator);
}
+ issuers->destroy(issuers);
- return TRUE;
+ return found;
}
/**
@@ -364,7 +466,7 @@ static bool has_policy_chain(linked_list_t *chain, x509_t *subject, int len)
enumerator = chain->create_enumerator(chain);
while (len-- > 0 && enumerator->enumerate(enumerator, &issuer))
{
- if (!check_policy(subject, issuer, TRUE, NULL))
+ if (!check_policy(subject, issuer))
{
valid = FALSE;
break;
@@ -450,6 +552,7 @@ static bool check_policy_constraints(x509_t *issuer, u_int pathlen,
{
if (subject->get_type(subject) == CERT_X509)
{
+ x509_cert_policy_t *policy;
enumerator_t *enumerator;
linked_list_t *chain;
certificate_t *cert;
@@ -457,6 +560,7 @@ static bool check_policy_constraints(x509_t *issuer, u_int pathlen,
x509_t *x509;
int len = 0;
u_int expl, inh;
+ char *oid;
/* prepare trustchain to validate */
chain = linked_list_create();
@@ -517,6 +621,31 @@ static bool check_policy_constraints(x509_t *issuer, u_int pathlen,
}
enumerator->destroy(enumerator);
+ if (valid)
+ {
+ x509 = (x509_t*)subject;
+
+ enumerator = x509->create_cert_policy_enumerator(x509);
+ while (enumerator->enumerate(enumerator, &policy))
+ {
+ oid = asn1_oid_to_string(policy->oid);
+ if (oid)
+ {
+ if (is_policy_valid(chain, policy->oid))
+ {
+ auth->add(auth, AUTH_RULE_CERT_POLICY, oid);
+ }
+ else
+ {
+ DBG1(DBG_CFG, "certificate policy %s for '%Y' "
+ "not allowed by trustchain, ignored",
+ oid, subject->get_subject(subject));
+ free(oid);
+ }
+ }
+ }
+ enumerator->destroy(enumerator);
+ }
chain->destroy(chain);
}
}
@@ -543,12 +672,6 @@ METHOD(cert_validator_t, validate, bool,
subject);
return FALSE;
}
- if (!check_policy((x509_t*)subject, (x509_t*)issuer, !pathlen, auth))
- {
- lib->credmgr->call_hook(lib->credmgr, CRED_HOOK_POLICY_VIOLATION,
- subject);
- return FALSE;
- }
if (anchor)
{
if (!check_policy_constraints((x509_t*)issuer, pathlen, auth))