]> git.ipfire.org Git - thirdparty/strongswan.git/commitdiff
Implemented NameConstraint matching in constraints plugin
authorMartin Willi <martin@revosec.ch>
Thu, 9 Dec 2010 15:39:07 +0000 (16:39 +0100)
committerMartin Willi <martin@revosec.ch>
Wed, 5 Jan 2011 15:46:00 +0000 (16:46 +0100)
src/libstrongswan/plugins/constraints/constraints_validator.c

index 7598f2896dcbb3c89aa96a7e4b6f6ac7579a557c..bab2535c174fb797b7895ec9a5e5ac86b7dd9696 100644 (file)
@@ -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;
 }