Implemented NameConstraint matching in constraints plugin
This commit is contained in:
@@ -49,6 +49,210 @@ static bool check_pathlen(x509_t *issuer, int pathlen)
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a FQDN/RFC822 constraint matches (suffix match)
|
||||
*/
|
||||
static bool suffix_matches(identification_t *constraint, identification_t *id)
|
||||
{
|
||||
chunk_t c, i;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a DN constraint matches (RDN prefix match)
|
||||
*/
|
||||
static bool dn_matches(identification_t *constraint, identification_t *id)
|
||||
{
|
||||
enumerator_t *ec, *ei;
|
||||
id_part_t pc, pi;
|
||||
chunk_t cc, ci;
|
||||
bool match = TRUE;
|
||||
|
||||
ec = constraint->create_part_enumerator(constraint);
|
||||
ei = id->create_part_enumerator(id);
|
||||
while (ec->enumerate(ec, &pc, &cc))
|
||||
{
|
||||
if (!ei->enumerate(ei, &pi, &ci) ||
|
||||
pi != pc || !chunk_equals(cc, ci))
|
||||
{
|
||||
match = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
ec->destroy(ec);
|
||||
ei->destroy(ei);
|
||||
|
||||
return match;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a certificate matches to a NameConstraint
|
||||
*/
|
||||
static bool name_constraint_matches(identification_t *constraint,
|
||||
certificate_t *cert, bool permitted)
|
||||
{
|
||||
x509_t *x509 = (x509_t*)cert;
|
||||
enumerator_t *enumerator;
|
||||
identification_t *id;
|
||||
id_type_t type;
|
||||
bool matches = permitted;
|
||||
|
||||
type = constraint->get_type(constraint);
|
||||
if (type == ID_DER_ASN1_DN)
|
||||
{
|
||||
matches = dn_matches(constraint, cert->get_subject(cert));
|
||||
if (matches != permitted)
|
||||
{
|
||||
return matches;
|
||||
}
|
||||
}
|
||||
|
||||
enumerator = x509->create_subjectAltName_enumerator(x509);
|
||||
while (enumerator->enumerate(enumerator, &id))
|
||||
{
|
||||
if (id->get_type(id) == type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ID_FQDN:
|
||||
case ID_RFC822_ADDR:
|
||||
matches = suffix_matches(constraint, id);
|
||||
break;
|
||||
case ID_DER_ASN1_DN:
|
||||
matches = dn_matches(constraint, id);
|
||||
break;
|
||||
default:
|
||||
DBG1(DBG_CFG, "%N NameConstraint matching not implemented",
|
||||
id_type_names, type);
|
||||
matches = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (matches != permitted)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a permitted or excluded NameConstraint has been inherited to sub-CA
|
||||
*/
|
||||
static bool name_constraint_inherited(identification_t *constraint,
|
||||
x509_t *x509, bool permitted)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
identification_t *id;
|
||||
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 (id->get_type(id) == type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case ID_FQDN:
|
||||
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);
|
||||
}
|
||||
break;
|
||||
case ID_DER_ASN1_DN:
|
||||
if (permitted)
|
||||
{
|
||||
inherited = dn_matches(constraint, id);
|
||||
}
|
||||
else
|
||||
{
|
||||
inherited = dn_matches(id, constraint);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
DBG1(DBG_CFG, "%N NameConstraint matching not implemented",
|
||||
id_type_names, type);
|
||||
inherited = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (inherited)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
return inherited;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check name constraints
|
||||
*/
|
||||
static bool check_name_constraints(certificate_t *subject, x509_t *issuer)
|
||||
{
|
||||
enumerator_t *enumerator;
|
||||
identification_t *constraint;
|
||||
|
||||
enumerator = issuer->create_name_constraint_enumerator(issuer, TRUE);
|
||||
while (enumerator->enumerate(enumerator, &constraint))
|
||||
{
|
||||
if (!name_constraint_matches(constraint, subject, TRUE))
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
|
||||
enumerator = issuer->create_name_constraint_enumerator(issuer, FALSE);
|
||||
while (enumerator->enumerate(enumerator, &constraint))
|
||||
{
|
||||
if (name_constraint_matches(constraint, subject, 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;
|
||||
}
|
||||
}
|
||||
enumerator->destroy(enumerator);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
METHOD(cert_validator_t, validate, bool,
|
||||
private_constraints_validator_t *this, certificate_t *subject,
|
||||
certificate_t *issuer, bool online, int pathlen, auth_cfg_t *auth)
|
||||
@@ -60,6 +264,10 @@ METHOD(cert_validator_t, validate, bool,
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
if (!check_name_constraints(subject, (x509_t*)issuer))
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user