+ return match;
+}
+
+static void compare_subject_with_email_constraints(
+ const CERT_NAME_BLOB *subjectName,
+ const CERT_NAME_CONSTRAINTS_INFO *nameConstraints, DWORD *trustErrorStatus)
+{
+ CERT_NAME_INFO *name;
+ DWORD size;
+
+ if (CryptDecodeObjectEx(X509_ASN_ENCODING, X509_UNICODE_NAME,
+ subjectName->pbData, subjectName->cbData,
+ CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL, &name, &size))
+ {
+ DWORD i, j;
+
+ for (i = 0; i < name->cRDN; i++)
+ for (j = 0; j < name->rgRDN[i].cRDNAttr; j++)
+ if (!strcmp(name->rgRDN[i].rgRDNAttr[j].pszObjId,
+ szOID_RSA_emailAddr))
+ {
+ BOOL nameFormPresent;
+
+ /* A name constraint only applies if the name form is
+ * present. From RFC 5280, section 4.2.1.10:
+ * "Restrictions apply only when the specified name form is
+ * present. If no name of the type is in the certificate,
+ * the certificate is acceptable."
+ */
+ if (rfc822_attr_matches_excluded_name(
+ &name->rgRDN[i].rgRDNAttr[j], nameConstraints,
+ trustErrorStatus))
+ {
+ TRACE_(chain)(
+ "email address in subject name is excluded\n");
+ *trustErrorStatus |=
+ CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT;
+ }
+ nameFormPresent = FALSE;
+ if (!rfc822_attr_matches_permitted_name(
+ &name->rgRDN[i].rgRDNAttr[j], nameConstraints,
+ trustErrorStatus, &nameFormPresent) && nameFormPresent)
+ {
+ TRACE_(chain)(
+ "email address in subject name is not permitted\n");
+ *trustErrorStatus |=
+ CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT;
+ }
+ }
+ LocalFree(name);
+ }
+ else
+ *trustErrorStatus |=
+ CERT_TRUST_INVALID_EXTENSION | CERT_TRUST_INVALID_NAME_CONSTRAINTS;
+}
+
+static BOOL CRYPT_IsEmptyName(const CERT_NAME_BLOB *name)
+{
+ BOOL empty;
+
+ if (!name->cbData)
+ empty = TRUE;
+ else if (name->cbData == 2 && name->pbData[1] == 0)
+ {
+ /* An empty sequence is also empty */
+ empty = TRUE;
+ }
+ else
+ empty = FALSE;
+ return empty;
+}
+
+static void compare_subject_with_constraints(const CERT_NAME_BLOB *subjectName,
+ const CERT_NAME_CONSTRAINTS_INFO *nameConstraints, DWORD *trustErrorStatus)
+{
+ BOOL hasEmailConstraint = FALSE;
+ DWORD i;
+
+ /* In general, a subject distinguished name only matches a directory name
+ * constraint. However, an exception exists for email addresses.
+ * From RFC 5280, section 4.2.1.6:
+ * "Legacy implementations exist where an electronic mail address is
+ * embedded in the subject distinguished name as an emailAddress
+ * attribute [RFC2985]."
+ * If an email address constraint exists, check that constraint separately.
+ */
+ for (i = 0; !hasEmailConstraint && i < nameConstraints->cExcludedSubtree;
+ i++)
+ if (nameConstraints->rgExcludedSubtree[i].Base.dwAltNameChoice ==
+ CERT_ALT_NAME_RFC822_NAME)
+ hasEmailConstraint = TRUE;
+ for (i = 0; !hasEmailConstraint && i < nameConstraints->cPermittedSubtree;
+ i++)
+ if (nameConstraints->rgPermittedSubtree[i].Base.dwAltNameChoice ==
+ CERT_ALT_NAME_RFC822_NAME)
+ hasEmailConstraint = TRUE;
+ if (hasEmailConstraint)
+ compare_subject_with_email_constraints(subjectName, nameConstraints,
+ trustErrorStatus);
+ for (i = 0; i < nameConstraints->cExcludedSubtree; i++)
+ {
+ CERT_ALT_NAME_ENTRY *constraint =
+ &nameConstraints->rgExcludedSubtree[i].Base;
+
+ if (constraint->dwAltNameChoice == CERT_ALT_NAME_DIRECTORY_NAME &&
+ directory_name_matches(&constraint->u.DirectoryName, subjectName))
+ {
+ TRACE_(chain)("subject name is excluded\n");
+ *trustErrorStatus |=
+ CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT;
+ }
+ }
+ /* RFC 5280, section 4.2.1.10:
+ * "Restrictions apply only when the specified name form is present.
+ * If no name of the type is in the certificate, the certificate is
+ * acceptable."
+ * An empty name can't have the name form present, so don't check it.
+ */
+ if (nameConstraints->cPermittedSubtree && !CRYPT_IsEmptyName(subjectName))
+ {
+ BOOL match = FALSE, hasDirectoryConstraint = FALSE;
+
+ for (i = 0; !match && i < nameConstraints->cPermittedSubtree; i++)
+ {
+ CERT_ALT_NAME_ENTRY *constraint =
+ &nameConstraints->rgPermittedSubtree[i].Base;
+
+ if (constraint->dwAltNameChoice == CERT_ALT_NAME_DIRECTORY_NAME)
+ {
+ hasDirectoryConstraint = TRUE;
+ match = directory_name_matches(&constraint->u.DirectoryName,
+ subjectName);
+ }
+ }
+ if (hasDirectoryConstraint && !match)
+ {
+ TRACE_(chain)("subject name is not permitted\n");
+ *trustErrorStatus |= CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT;
+ }
+ }
+}
+
+static void CRYPT_CheckNameConstraints(
+ const CERT_NAME_CONSTRAINTS_INFO *nameConstraints, const CERT_INFO *cert,
+ DWORD *trustErrorStatus)
+{
+ CERT_EXTENSION *ext = get_subject_alt_name_ext(cert);
+
+ if (ext)
+ compare_alt_name_with_constraints(ext, nameConstraints,
+ trustErrorStatus);
+ /* Name constraints apply to the subject alternative name as well as the
+ * subject name. From RFC 5280, section 4.2.1.10:
+ * "Restrictions apply to the subject distinguished name and apply to
+ * subject alternative names."
+ */
+ compare_subject_with_constraints(&cert->Subject, nameConstraints,
+ trustErrorStatus);