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;
|
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,
|
METHOD(cert_validator_t, validate, bool,
|
||||||
private_constraints_validator_t *this, certificate_t *subject,
|
private_constraints_validator_t *this, certificate_t *subject,
|
||||||
certificate_t *issuer, bool online, int pathlen, auth_cfg_t *auth)
|
certificate_t *issuer, bool online, int pathlen, auth_cfg_t *auth)
|
||||||
@@ -60,6 +264,10 @@ METHOD(cert_validator_t, validate, bool,
|
|||||||
{
|
{
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
if (!check_name_constraints(subject, (x509_t*)issuer))
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user