sync crypt32 to wine 1.1.34
authorChristoph von Wittich <christoph_vw@reactos.org>
Sat, 5 Dec 2009 20:37:08 +0000 (20:37 +0000)
committerChristoph von Wittich <christoph_vw@reactos.org>
Sat, 5 Dec 2009 20:37:08 +0000 (20:37 +0000)
svn path=/trunk/; revision=44422

reactos/dll/win32/crypt32/cert.c
reactos/dll/win32/crypt32/chain.c
reactos/dll/win32/crypt32/crl.c
reactos/dll/win32/crypt32/crypt32.spec
reactos/dll/win32/crypt32/decode.c
reactos/dll/win32/crypt32/encode.c
reactos/dll/win32/crypt32/oid.c
reactos/dll/win32/crypt32/rootstore.c

index 4f3df32..a247570 100644 (file)
@@ -1832,6 +1832,92 @@ PCERT_RDN_ATTR WINAPI CertFindRDNAttr(LPCSTR pszObjId, PCERT_NAME_INFO pName)
     return ret;
 }
 
+static BOOL find_matching_rdn_attr(DWORD dwFlags, const CERT_NAME_INFO *name,
+ const CERT_RDN_ATTR *attr)
+{
+    DWORD i, j;
+    BOOL match = FALSE;
+
+    for (i = 0; !match && i < name->cRDN; i++)
+    {
+        for (j = 0; j < name->rgRDN[i].cRDNAttr; j++)
+        {
+            if (!strcmp(name->rgRDN[i].rgRDNAttr[j].pszObjId,
+             attr->pszObjId) &&
+             name->rgRDN[i].rgRDNAttr[j].dwValueType ==
+             attr->dwValueType)
+            {
+                if (dwFlags & CERT_UNICODE_IS_RDN_ATTRS_FLAG)
+                {
+                    LPCWSTR nameStr =
+                     (LPCWSTR)name->rgRDN[i].rgRDNAttr[j].Value.pbData;
+                    LPCWSTR attrStr = (LPCWSTR)attr->Value.pbData;
+
+                    if (attr->Value.cbData !=
+                     name->rgRDN[i].rgRDNAttr[j].Value.cbData)
+                        match = FALSE;
+                    else if (dwFlags & CERT_CASE_INSENSITIVE_IS_RDN_ATTRS_FLAG)
+                        match = !strncmpiW(nameStr, attrStr,
+                         attr->Value.cbData / sizeof(WCHAR));
+                    else
+                        match = !strncmpW(nameStr, attrStr,
+                         attr->Value.cbData / sizeof(WCHAR));
+                    TRACE("%s : %s => %d\n",
+                     debugstr_wn(nameStr, attr->Value.cbData / sizeof(WCHAR)),
+                     debugstr_wn(attrStr, attr->Value.cbData / sizeof(WCHAR)),
+                     match);
+                }
+                else
+                {
+                    LPCSTR nameStr =
+                     (LPCSTR)name->rgRDN[i].rgRDNAttr[j].Value.pbData;
+                    LPCSTR attrStr = (LPCSTR)attr->Value.pbData;
+
+                    if (attr->Value.cbData !=
+                     name->rgRDN[i].rgRDNAttr[j].Value.cbData)
+                        match = FALSE;
+                    else if (dwFlags & CERT_CASE_INSENSITIVE_IS_RDN_ATTRS_FLAG)
+                        match = !strncasecmp(nameStr, attrStr,
+                         attr->Value.cbData);
+                    else
+                        match = !strncmp(nameStr, attrStr, attr->Value.cbData);
+                    TRACE("%s : %s => %d\n",
+                     debugstr_an(nameStr, attr->Value.cbData),
+                     debugstr_an(attrStr, attr->Value.cbData), match);
+                }
+            }
+        }
+    }
+    return match;
+}
+
+BOOL WINAPI CertIsRDNAttrsInCertificateName(DWORD dwCertEncodingType,
+ DWORD dwFlags, PCERT_NAME_BLOB pCertName, PCERT_RDN pRDN)
+{
+    CERT_NAME_INFO *name;
+    LPCSTR type;
+    DWORD size;
+    BOOL ret;
+
+    TRACE("(%08x, %08x, %p, %p)\n", dwCertEncodingType, dwFlags, pCertName,
+     pRDN);
+
+    type = dwFlags & CERT_UNICODE_IS_RDN_ATTRS_FLAG ? X509_UNICODE_NAME :
+     X509_NAME;
+    if ((ret = CryptDecodeObjectEx(dwCertEncodingType, type, pCertName->pbData,
+     pCertName->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &name, &size)))
+    {
+        DWORD i;
+
+        for (i = 0; ret && i < pRDN->cRDNAttr; i++)
+            ret = find_matching_rdn_attr(dwFlags, name, &pRDN->rgRDNAttr[i]);
+        if (!ret)
+            SetLastError(CRYPT_E_NO_MATCH);
+        LocalFree(name);
+    }
+    return ret;
+}
+
 LONG WINAPI CertVerifyTimeValidity(LPFILETIME pTimeToVerify,
  PCERT_INFO pCertInfo)
 {
index eb6d757..5cc46b7 100644 (file)
@@ -506,6 +506,41 @@ static BOOL CRYPT_CheckBasicConstraintsForCA(PCertificateChainEngine engine,
     return validBasicConstraints;
 }
 
+static BOOL domain_name_matches(LPCWSTR constraint, LPCWSTR name)
+{
+    BOOL match;
+
+    /* RFC 5280, section 4.2.1.10:
+     * "For URIs, the constraint applies to the host part of the name...
+     *  When the constraint begins with a period, it MAY be expanded with one
+     *  or more labels.  That is, the constraint ".example.com" is satisfied by
+     *  both host.example.com and my.host.example.com.  However, the constraint
+     *  ".example.com" is not satisfied by "example.com".  When the constraint
+     *  does not begin with a period, it specifies a host."
+     * and for email addresses,
+     * "To indicate all Internet mail addresses on a particular host, the
+     *  constraint is specified as the host name.  For example, the constraint
+     *  "example.com" is satisfied by any mail address at the host
+     *  "example.com".  To specify any address within a domain, the constraint
+     *  is specified with a leading period (as with URIs)."
+     */
+    if (constraint[0] == '.')
+    {
+        /* Must be strictly greater than, a name can't begin with '.' */
+        if (lstrlenW(name) > lstrlenW(constraint))
+            match = !lstrcmpiW(name + lstrlenW(name) - lstrlenW(constraint),
+             constraint);
+        else
+        {
+            /* name is too short, no match */
+            match = FALSE;
+        }
+    }
+    else
+        match = !lstrcmpiW(name, constraint);
+     return match;
+}
+
 static BOOL url_matches(LPCWSTR constraint, LPCWSTR name,
  DWORD *trustErrorStatus)
 {
@@ -517,14 +552,58 @@ static BOOL url_matches(LPCWSTR constraint, LPCWSTR name,
         *trustErrorStatus |= CERT_TRUST_INVALID_NAME_CONSTRAINTS;
     else if (!name)
         ; /* no match */
-    else if (constraint[0] == '.')
+    else
     {
-        if (lstrlenW(name) > lstrlenW(constraint))
-            match = !lstrcmpiW(name + lstrlenW(name) - lstrlenW(constraint),
-             constraint);
+        LPCWSTR colon, authority_end, at, hostname = NULL;
+        /* The maximum length for a hostname is 254 in the DNS, see RFC 1034 */
+        WCHAR hostname_buf[255];
+
+        /* RFC 5280: only the hostname portion of the URL is compared.  From
+         * section 4.2.1.10:
+         * "For URIs, the constraint applies to the host part of the name.
+         *  The constraint MUST be specified as a fully qualified domain name
+         *  and MAY specify a host or a domain."
+         * The format for URIs is in RFC 2396.
+         *
+         * First, remove any scheme that's present. */
+        colon = strchrW(name, ':');
+        if (colon && *(colon + 1) == '/' && *(colon + 2) == '/')
+            name = colon + 3;
+        /* Next, find the end of the authority component.  (The authority is
+         * generally just the hostname, but it may contain a username or a port.
+         * Those are removed next.)
+         */
+        authority_end = strchrW(name, '/');
+        if (!authority_end)
+            authority_end = strchrW(name, '?');
+        if (!authority_end)
+            authority_end = name + strlenW(name);
+        /* Remove any port number from the authority */
+        for (colon = authority_end; colon >= name && *colon != ':'; colon--)
+            ;
+        if (*colon == ':')
+            authority_end = colon;
+        /* Remove any username from the authority */
+        if ((at = strchrW(name, '@')))
+            name = at;
+        /* Ignore any path or query portion of the URL. */
+        if (*authority_end)
+        {
+            if (authority_end - name < sizeof(hostname_buf) /
+             sizeof(hostname_buf[0]))
+            {
+                memcpy(hostname_buf, name,
+                 (authority_end - name) * sizeof(WCHAR));
+                hostname_buf[authority_end - name] = 0;
+                hostname = hostname_buf;
+            }
+            /* else: Hostname is too long, not a match */
+        }
+        else
+            hostname = name;
+        if (hostname)
+            match = domain_name_matches(constraint, hostname);
     }
-    else
-        match = !lstrcmpiW(constraint, name);
     return match;
 }
 
@@ -545,7 +624,7 @@ static BOOL rfc822_name_matches(LPCWSTR constraint, LPCWSTR name,
     else
     {
         if ((at = strchrW(name, '@')))
-            match = url_matches(constraint, at + 1, trustErrorStatus);
+            match = domain_name_matches(constraint, at + 1);
         else
             match = !lstrcmpiW(constraint, name);
     }
@@ -563,9 +642,35 @@ static BOOL dns_name_matches(LPCWSTR constraint, LPCWSTR name,
         *trustErrorStatus |= CERT_TRUST_INVALID_NAME_CONSTRAINTS;
     else if (!name)
         ; /* no match */
-    else if (lstrlenW(name) >= lstrlenW(constraint))
+    /* RFC 5280, section 4.2.1.10:
+     * "DNS name restrictions are expressed as host.example.com.  Any DNS name
+     *  that can be constructed by simply adding zero or more labels to the
+     *  left-hand side of the name satisfies the name constraint.  For example,
+     *  www.host.example.com would satisfy the constraint but host1.example.com
+     *  would not."
+     */
+    else if (lstrlenW(name) == lstrlenW(constraint))
+        match = !lstrcmpiW(name, constraint);
+    else if (lstrlenW(name) > lstrlenW(constraint))
+    {
         match = !lstrcmpiW(name + lstrlenW(name) - lstrlenW(constraint),
          constraint);
+        if (match)
+        {
+            BOOL dot = FALSE;
+            LPCWSTR ptr;
+
+            /* This only matches if name is a subdomain of constraint, i.e.
+             * there's a '.' between the beginning of the name and the
+             * matching portion of the name.
+             */
+            for (ptr = name + lstrlenW(name) - lstrlenW(constraint);
+             !dot && ptr >= name; ptr--)
+                if (*ptr == '.')
+                    dot = TRUE;
+            match = dot;
+        }
+    }
     /* else:  name is too short, no match */
 
     return match;
@@ -615,46 +720,95 @@ static BOOL ip_address_matches(const CRYPT_DATA_BLOB *constraint,
     return match;
 }
 
-static void CRYPT_FindMatchingNameEntry(const CERT_ALT_NAME_ENTRY *constraint,
- const CERT_ALT_NAME_INFO *subjectName, DWORD *trustErrorStatus,
- DWORD errorIfFound, DWORD errorIfNotFound)
+static BOOL directory_name_matches(const CERT_NAME_BLOB *constraint,
+ const CERT_NAME_BLOB *name)
+{
+    CERT_NAME_INFO *constraintName;
+    DWORD size;
+    BOOL match = FALSE;
+
+    if (CryptDecodeObjectEx(X509_ASN_ENCODING, X509_NAME, constraint->pbData,
+     constraint->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &constraintName, &size))
+    {
+        DWORD i;
+
+        match = TRUE;
+        for (i = 0; match && i < constraintName->cRDN; i++)
+            match = CertIsRDNAttrsInCertificateName(X509_ASN_ENCODING,
+             CERT_CASE_INSENSITIVE_IS_RDN_ATTRS_FLAG,
+             (CERT_NAME_BLOB *)name, &constraintName->rgRDN[i]);
+        LocalFree(constraintName);
+    }
+    return match;
+}
+
+static BOOL alt_name_matches(const CERT_ALT_NAME_ENTRY *name,
+ const CERT_ALT_NAME_ENTRY *constraint, DWORD *trustErrorStatus, BOOL *present)
 {
-    DWORD i;
     BOOL match = FALSE;
 
-    for (i = 0; i < subjectName->cAltEntry; i++)
+    if (name->dwAltNameChoice == constraint->dwAltNameChoice)
     {
-        if (subjectName->rgAltEntry[i].dwAltNameChoice ==
-         constraint->dwAltNameChoice)
+        if (present)
+            *present = TRUE;
+        switch (constraint->dwAltNameChoice)
         {
-            switch (constraint->dwAltNameChoice)
-            {
-            case CERT_ALT_NAME_RFC822_NAME:
-                match = rfc822_name_matches(constraint->u.pwszURL,
-                 subjectName->rgAltEntry[i].u.pwszURL, trustErrorStatus);
-                break;
-            case CERT_ALT_NAME_DNS_NAME:
-                match = dns_name_matches(constraint->u.pwszURL,
-                 subjectName->rgAltEntry[i].u.pwszURL, trustErrorStatus);
-                break;
-            case CERT_ALT_NAME_URL:
-                match = url_matches(constraint->u.pwszURL,
-                 subjectName->rgAltEntry[i].u.pwszURL, trustErrorStatus);
-                break;
-            case CERT_ALT_NAME_IP_ADDRESS:
-                match = ip_address_matches(&constraint->u.IPAddress,
-                 &subjectName->rgAltEntry[i].u.IPAddress, trustErrorStatus);
-                break;
-            case CERT_ALT_NAME_DIRECTORY_NAME:
-            default:
-                ERR("name choice %d unsupported in this context\n",
-                 constraint->dwAltNameChoice);
-                *trustErrorStatus |=
-                 CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT;
-            }
+        case CERT_ALT_NAME_RFC822_NAME:
+            match = rfc822_name_matches(constraint->u.pwszURL,
+             name->u.pwszURL, trustErrorStatus);
+            break;
+        case CERT_ALT_NAME_DNS_NAME:
+            match = dns_name_matches(constraint->u.pwszURL,
+             name->u.pwszURL, trustErrorStatus);
+            break;
+        case CERT_ALT_NAME_URL:
+            match = url_matches(constraint->u.pwszURL,
+             name->u.pwszURL, trustErrorStatus);
+            break;
+        case CERT_ALT_NAME_IP_ADDRESS:
+            match = ip_address_matches(&constraint->u.IPAddress,
+             &name->u.IPAddress, trustErrorStatus);
+            break;
+        case CERT_ALT_NAME_DIRECTORY_NAME:
+            match = directory_name_matches(&constraint->u.DirectoryName,
+             &name->u.DirectoryName);
+            break;
+        default:
+            ERR("name choice %d unsupported in this context\n",
+             constraint->dwAltNameChoice);
+            *trustErrorStatus |=
+             CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT;
         }
     }
-    *trustErrorStatus |= match ? errorIfFound : errorIfNotFound;
+    else if (present)
+        *present = FALSE;
+    return match;
+}
+
+static BOOL alt_name_matches_excluded_name(const CERT_ALT_NAME_ENTRY *name,
+ const CERT_NAME_CONSTRAINTS_INFO *nameConstraints, DWORD *trustErrorStatus)
+{
+    DWORD i;
+    BOOL match = FALSE;
+
+    for (i = 0; !match && i < nameConstraints->cExcludedSubtree; i++)
+        match = alt_name_matches(name,
+         &nameConstraints->rgExcludedSubtree[i].Base, trustErrorStatus, NULL);
+    return match;
+}
+
+static BOOL alt_name_matches_permitted_name(const CERT_ALT_NAME_ENTRY *name,
+ const CERT_NAME_CONSTRAINTS_INFO *nameConstraints, DWORD *trustErrorStatus,
+ BOOL *present)
+{
+    DWORD i;
+    BOOL match = FALSE;
+
+    for (i = 0; !match && i < nameConstraints->cPermittedSubtree; i++)
+        match = alt_name_matches(name,
+         &nameConstraints->rgPermittedSubtree[i].Base, trustErrorStatus,
+         present);
+    return match;
 }
 
 static inline PCERT_EXTENSION get_subject_alt_name_ext(const CERT_INFO *cert)
@@ -669,57 +823,253 @@ static inline PCERT_EXTENSION get_subject_alt_name_ext(const CERT_INFO *cert)
     return ext;
 }
 
-static void CRYPT_CheckNameConstraints(
- const CERT_NAME_CONSTRAINTS_INFO *nameConstraints, const CERT_INFO *cert,
- DWORD *trustErrorStatus)
+static void compare_alt_name_with_constraints(const CERT_EXTENSION *altNameExt,
+ const CERT_NAME_CONSTRAINTS_INFO *nameConstraints, DWORD *trustErrorStatus)
 {
-    /* If there aren't any existing constraints, don't bother checking */
-    if (nameConstraints->cPermittedSubtree || nameConstraints->cExcludedSubtree)
+    CERT_ALT_NAME_INFO *subjectAltName;
+    DWORD size;
+
+    if (CryptDecodeObjectEx(X509_ASN_ENCODING, X509_ALTERNATE_NAME,
+     altNameExt->Value.pbData, altNameExt->Value.cbData,
+     CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL,
+     &subjectAltName, &size))
     {
-        CERT_EXTENSION *ext = get_subject_alt_name_ext(cert);
+        DWORD i;
 
-        if (ext)
+        for (i = 0; i < subjectAltName->cAltEntry; i++)
         {
-            CERT_ALT_NAME_INFO *subjectName;
-            DWORD size;
-
-            if (CryptDecodeObjectEx(X509_ASN_ENCODING, X509_ALTERNATE_NAME,
-             ext->Value.pbData, ext->Value.cbData,
-             CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL,
-             &subjectName, &size))
+             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 (alt_name_matches_excluded_name(
+             &subjectAltName->rgAltEntry[i], nameConstraints,
+             trustErrorStatus))
             {
-                DWORD i;
-
-                for (i = 0; i < nameConstraints->cExcludedSubtree; i++)
-                    CRYPT_FindMatchingNameEntry(
-                     &nameConstraints->rgExcludedSubtree[i].Base, subjectName,
-                     trustErrorStatus,
-                     CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT, 0);
-                for (i = 0; i < nameConstraints->cPermittedSubtree; i++)
-                    CRYPT_FindMatchingNameEntry(
-                     &nameConstraints->rgPermittedSubtree[i].Base, subjectName,
-                     trustErrorStatus, 0,
-                     CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT);
-                LocalFree(subjectName);
+                TRACE_(chain)("subject alternate name form %d excluded\n",
+                 subjectAltName->rgAltEntry[i].dwAltNameChoice);
+                *trustErrorStatus |=
+                 CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT;
             }
-            else
+            nameFormPresent = FALSE;
+            if (!alt_name_matches_permitted_name(
+             &subjectAltName->rgAltEntry[i], nameConstraints,
+             trustErrorStatus, &nameFormPresent) && nameFormPresent)
+            {
+                TRACE_(chain)("subject alternate name form %d not permitted\n",
+                 subjectAltName->rgAltEntry[i].dwAltNameChoice);
                 *trustErrorStatus |=
-                 CERT_TRUST_INVALID_EXTENSION |
-                 CERT_TRUST_INVALID_NAME_CONSTRAINTS;
+                 CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT;
+            }
         }
-        else
+        LocalFree(subjectAltName);
+    }
+    else
+        *trustErrorStatus |=
+         CERT_TRUST_INVALID_EXTENSION | CERT_TRUST_INVALID_NAME_CONSTRAINTS;
+}
+
+static BOOL rfc822_attr_matches_excluded_name(const CERT_RDN_ATTR *attr,
+ const CERT_NAME_CONSTRAINTS_INFO *nameConstraints, DWORD *trustErrorStatus)
+{
+    DWORD i;
+    BOOL match = FALSE;
+
+    for (i = 0; !match && i < nameConstraints->cExcludedSubtree; i++)
+    {
+        const CERT_ALT_NAME_ENTRY *constraint =
+         &nameConstraints->rgExcludedSubtree[i].Base;
+
+        if (constraint->dwAltNameChoice == CERT_ALT_NAME_RFC822_NAME)
+            match = rfc822_name_matches(constraint->u.pwszRfc822Name,
+             (LPCWSTR)attr->Value.pbData, trustErrorStatus);
+    }
+    return match;
+}
+
+static BOOL rfc822_attr_matches_permitted_name(const CERT_RDN_ATTR *attr,
+ const CERT_NAME_CONSTRAINTS_INFO *nameConstraints, DWORD *trustErrorStatus,
+ BOOL *present)
+{
+    DWORD i;
+    BOOL match = FALSE;
+
+    for (i = 0; !match && i < nameConstraints->cPermittedSubtree; i++)
+    {
+        const CERT_ALT_NAME_ENTRY *constraint =
+         &nameConstraints->rgPermittedSubtree[i].Base;
+
+        if (constraint->dwAltNameChoice == CERT_ALT_NAME_RFC822_NAME)
         {
-            if (nameConstraints->cPermittedSubtree)
-                *trustErrorStatus |=
-                 CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT |
-                 CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT;
-            if (nameConstraints->cExcludedSubtree)
-                *trustErrorStatus |=
-                 CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT;
+            *present = TRUE;
+            match = rfc822_name_matches(constraint->u.pwszRfc822Name,
+             (LPCWSTR)attr->Value.pbData, trustErrorStatus);
+        }
+    }
+    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);
+}
+
 /* Gets cert's name constraints, if any.  Free with LocalFree. */
 static CERT_NAME_CONSTRAINTS_INFO *CRYPT_GetNameConstraints(CERT_INFO *cert)
 {
@@ -745,6 +1095,17 @@ static BOOL CRYPT_IsValidNameConstraint(const CERT_NAME_CONSTRAINTS_INFO *info)
     DWORD i;
     BOOL ret = TRUE;
 
+    /* Make sure at least one permitted or excluded subtree is present.  From
+     * RFC 5280, section 4.2.1.10:
+     * "Conforming CAs MUST NOT issue certificates where name constraints is an
+     *  empty sequence.  That is, either the permittedSubtrees field or the
+     *  excludedSubtrees MUST be present."
+     */
+    if (!info->cPermittedSubtree && !info->cExcludedSubtree)
+    {
+        WARN_(chain)("constraints contain no permitted nor excluded subtree\n");
+        ret = FALSE;
+    }
     /* Check that none of the constraints specifies a minimum or a maximum.
      * See RFC 5280, section 4.2.1.10:
      * "Within this profile, the minimum and maximum fields are not used with
@@ -815,8 +1176,16 @@ static void CRYPT_CheckChainNameConstraints(PCERT_SIMPLE_CHAIN chain)
                         CRYPT_CheckNameConstraints(nameConstraints,
                          chain->rgpElement[j]->pCertContext->pCertInfo,
                          &errorStatus);
-                        chain->rgpElement[i]->TrustStatus.dwErrorStatus |=
-                         errorStatus;
+                        if (errorStatus)
+                        {
+                            chain->rgpElement[i]->TrustStatus.dwErrorStatus |=
+                             errorStatus;
+                            CRYPT_CombineTrustStatus(&chain->TrustStatus,
+                             &chain->rgpElement[i]->TrustStatus);
+                        }
+                        else
+                            chain->rgpElement[i]->TrustStatus.dwInfoStatus |=
+                             CERT_TRUST_HAS_VALID_NAME_CONSTRAINTS;
                     }
                 }
             }
@@ -1235,58 +1604,6 @@ static BOOL CRYPT_KeyUsageValid(PCertificateChainEngine engine,
     return ret;
 }
 
-static BOOL CRYPT_ExtendedKeyUsageValidForCA(PCCERT_CONTEXT cert)
-{
-    PCERT_EXTENSION ext;
-    BOOL ret;
-
-    /* RFC 5280, section 4.2.1.12:  "In general, this extension will only
-     * appear in end entity certificates."  And, "If a certificate contains
-     * both a key usage extension and an extended key usage extension, then
-     * both extensions MUST be processed independently and the certificate MUST
-     * only be used for a purpose consistent with both extensions."  This seems
-     * to imply that it should be checked if present, and ignored if not.
-     * Unfortunately some CAs, e.g. the Thawte SGC CA, don't include the code
-     * signing extended key usage, whereas they do include the keyCertSign
-     * key usage.  Thus, when checking for a CA, we only require the
-     * code signing extended key usage if the extended key usage is critical.
-     */
-    ext = CertFindExtension(szOID_ENHANCED_KEY_USAGE,
-     cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension);
-    if (ext && ext->fCritical)
-    {
-        CERT_ENHKEY_USAGE *usage;
-        DWORD size;
-
-        ret = CryptDecodeObjectEx(cert->dwCertEncodingType,
-         X509_ENHANCED_KEY_USAGE, ext->Value.pbData, ext->Value.cbData,
-         CRYPT_DECODE_ALLOC_FLAG, NULL, &usage, &size);
-        if (ret)
-        {
-            DWORD i;
-
-            /* Explicitly require the code signing extended key usage for a CA
-             * with an extended key usage extension.  That is, don't assume
-             * a cert is allowed to be a CA if it specifies the
-             * anyExtendedKeyUsage usage oid.  See again RFC 5280, section
-             * 4.2.1.12: "Applications that require the presence of a
-             * particular purpose MAY reject certificates that include the
-             * anyExtendedKeyUsage OID but not the particular OID expected for
-             * the application."
-             */
-            ret = FALSE;
-            for (i = 0; !ret && i < usage->cUsageIdentifier; i++)
-                if (!strcmp(usage->rgpszUsageIdentifier[i],
-                 szOID_PKIX_KP_CODE_SIGNING))
-                    ret = TRUE;
-            LocalFree(usage);
-        }
-    }
-    else
-        ret = TRUE;
-    return ret;
-}
-
 static BOOL CRYPT_CriticalExtensionsSupported(PCCERT_CONTEXT cert)
 {
     BOOL ret = TRUE;
@@ -1435,11 +1752,6 @@ static void CRYPT_CheckSimpleChain(PCertificateChainEngine engine,
          isRoot, constraints.fCA, i))
             chain->rgpElement[i]->TrustStatus.dwErrorStatus |=
              CERT_TRUST_IS_NOT_VALID_FOR_USAGE;
-        if (i != 0)
-            if (!CRYPT_ExtendedKeyUsageValidForCA(
-             chain->rgpElement[i]->pCertContext))
-                chain->rgpElement[i]->TrustStatus.dwErrorStatus |=
-                 CERT_TRUST_IS_NOT_VALID_FOR_USAGE;
         if (CRYPT_IsSimpleChainCyclic(chain))
         {
             /* If the chain is cyclic, then the path length constraints
@@ -1501,7 +1813,10 @@ static PCCERT_CONTEXT CRYPT_GetIssuer(HCERTSTORE store, PCCERT_CONTEXT subject,
                  subject->dwCertEncodingType, 0, CERT_FIND_CERT_ID, &id,
                  prevIssuer);
                 if (issuer)
+                {
+                    TRACE_(chain)("issuer found by issuer/serial number\n");
                     *infoStatus = CERT_TRUST_HAS_EXACT_MATCH_ISSUER;
+                }
             }
             else if (info->KeyId.cbData)
             {
@@ -1511,7 +1826,10 @@ static PCCERT_CONTEXT CRYPT_GetIssuer(HCERTSTORE store, PCCERT_CONTEXT subject,
                  subject->dwCertEncodingType, 0, CERT_FIND_CERT_ID, &id,
                  prevIssuer);
                 if (issuer)
+                {
+                    TRACE_(chain)("issuer found by key id\n");
                     *infoStatus = CERT_TRUST_HAS_KEY_MATCH_ISSUER;
+                }
             }
             LocalFree(info);
         }
@@ -1554,7 +1872,10 @@ static PCCERT_CONTEXT CRYPT_GetIssuer(HCERTSTORE store, PCCERT_CONTEXT subject,
                      subject->dwCertEncodingType, 0, CERT_FIND_CERT_ID, &id,
                      prevIssuer);
                     if (issuer)
+                    {
+                        TRACE_(chain)("issuer found by directory name\n");
                         *infoStatus = CERT_TRUST_HAS_EXACT_MATCH_ISSUER;
+                    }
                 }
                 else
                     FIXME("no supported name type in authority key id2\n");
@@ -1567,7 +1888,10 @@ static PCCERT_CONTEXT CRYPT_GetIssuer(HCERTSTORE store, PCCERT_CONTEXT subject,
                  subject->dwCertEncodingType, 0, CERT_FIND_CERT_ID, &id,
                  prevIssuer);
                 if (issuer)
+                {
+                    TRACE_(chain)("issuer found by key id\n");
                     *infoStatus = CERT_TRUST_HAS_KEY_MATCH_ISSUER;
+                }
             }
             LocalFree(info);
         }
@@ -1577,6 +1901,7 @@ static PCCERT_CONTEXT CRYPT_GetIssuer(HCERTSTORE store, PCCERT_CONTEXT subject,
         issuer = CertFindCertificateInStore(store,
          subject->dwCertEncodingType, 0, CERT_FIND_SUBJECT_NAME,
          &subject->pCertInfo->Issuer, prevIssuer);
+        TRACE_(chain)("issuer found by name\n");
         *infoStatus = CERT_TRUST_HAS_NAME_MATCH_ISSUER;
     }
     return issuer;
@@ -2054,7 +2379,7 @@ static void CRYPT_VerifyChainRevocation(PCERT_CHAIN_CONTEXT chain,
     if (cContext)
     {
         PCCERT_CONTEXT *contexts =
-         CryptMemAlloc(cContext * sizeof(PCCERT_CONTEXT *));
+         CryptMemAlloc(cContext * sizeof(PCCERT_CONTEXT));
 
         if (contexts)
         {
@@ -2101,7 +2426,11 @@ static void CRYPT_VerifyChainRevocation(PCERT_CHAIN_CONTEXT chain,
                 case CRYPT_E_NO_REVOCATION_CHECK:
                 case CRYPT_E_NO_REVOCATION_DLL:
                 case CRYPT_E_NOT_IN_REVOCATION_DATABASE:
-                    error = CERT_TRUST_REVOCATION_STATUS_UNKNOWN;
+                    /* If the revocation status is unknown, it's assumed to be
+                     * offline too.
+                     */
+                    error = CERT_TRUST_REVOCATION_STATUS_UNKNOWN |
+                     CERT_TRUST_IS_OFFLINE_REVOCATION;
                     break;
                 case CRYPT_E_REVOCATION_OFFLINE:
                     error = CERT_TRUST_IS_OFFLINE_REVOCATION;
@@ -2125,14 +2454,122 @@ static void CRYPT_VerifyChainRevocation(PCERT_CHAIN_CONTEXT chain,
     }
 }
 
+static void CRYPT_CheckUsages(PCERT_CHAIN_CONTEXT chain,
+ const CERT_CHAIN_PARA *pChainPara)
+{
+    if (pChainPara->cbSize >= sizeof(CERT_CHAIN_PARA_NO_EXTRA_FIELDS) &&
+     pChainPara->RequestedUsage.Usage.cUsageIdentifier)
+    {
+        PCCERT_CONTEXT endCert;
+        PCERT_EXTENSION ext;
+        BOOL validForUsage;
+
+        /* A chain, if created, always includes the end certificate */
+        endCert = chain->rgpChain[0]->rgpElement[0]->pCertContext;
+        /* The extended key usage extension specifies how a certificate's
+         * public key may be used.  From RFC 5280, section 4.2.1.12:
+         * "This extension indicates one or more purposes for which the
+         *  certified public key may be used, in addition to or in place of the
+         *  basic purposes indicated in the key usage extension."
+         * If the extension is present, it only satisfies the requested usage
+         * if that usage is included in the extension:
+         * "If the extension is present, then the certificate MUST only be used
+         *  for one of the purposes indicated."
+         * There is also the special anyExtendedKeyUsage OID, but it doesn't
+         * have to be respected:
+         * "Applications that require the presence of a particular purpose
+         *  MAY reject certificates that include the anyExtendedKeyUsage OID
+         *  but not the particular OID expected for the application."
+         * For now, I'm being more conservative and ignoring the presence of
+         * the anyExtendedKeyUsage OID.
+         */
+        if ((ext = CertFindExtension(szOID_ENHANCED_KEY_USAGE,
+         endCert->pCertInfo->cExtension, endCert->pCertInfo->rgExtension)))
+        {
+            const CERT_ENHKEY_USAGE *requestedUsage =
+             &pChainPara->RequestedUsage.Usage;
+            CERT_ENHKEY_USAGE *usage;
+            DWORD size;
+
+            if (CryptDecodeObjectEx(X509_ASN_ENCODING,
+             X509_ENHANCED_KEY_USAGE, ext->Value.pbData, ext->Value.cbData,
+             CRYPT_DECODE_ALLOC_FLAG, NULL, &usage, &size))
+            {
+                if (pChainPara->RequestedUsage.dwType == USAGE_MATCH_TYPE_AND)
+                {
+                    DWORD i, j;
+
+                    /* For AND matches, all usages must be present */
+                    validForUsage = TRUE;
+                    for (i = 0; validForUsage &&
+                     i < requestedUsage->cUsageIdentifier; i++)
+                    {
+                        BOOL match = FALSE;
+
+                        for (j = 0; !match && j < usage->cUsageIdentifier; j++)
+                            match = !strcmp(usage->rgpszUsageIdentifier[j],
+                             requestedUsage->rgpszUsageIdentifier[i]);
+                        if (!match)
+                            validForUsage = FALSE;
+                    }
+                }
+                else
+                {
+                    DWORD i, j;
+
+                    /* For OR matches, any matching usage suffices */
+                    validForUsage = FALSE;
+                    for (i = 0; !validForUsage &&
+                     i < requestedUsage->cUsageIdentifier; i++)
+                    {
+                        for (j = 0; !validForUsage &&
+                         j < usage->cUsageIdentifier; j++)
+                            validForUsage =
+                             !strcmp(usage->rgpszUsageIdentifier[j],
+                             requestedUsage->rgpszUsageIdentifier[i]);
+                    }
+                }
+                LocalFree(usage);
+            }
+            else
+                validForUsage = FALSE;
+        }
+        else
+        {
+            /* If the extension isn't present, any interpretation is valid:
+             * "Certificate using applications MAY require that the extended
+             *  key usage extension be present and that a particular purpose
+             *  be indicated in order for the certificate to be acceptable to
+             *  that application."
+             * For now I'm being more conservative and disallowing it.
+             */
+            WARN_(chain)("requested usage from a certificate with no usages\n");
+            validForUsage = FALSE;
+        }
+        if (!validForUsage)
+        {
+            chain->TrustStatus.dwErrorStatus |=
+             CERT_TRUST_IS_NOT_VALID_FOR_USAGE;
+            chain->rgpChain[0]->rgpElement[0]->TrustStatus.dwErrorStatus |=
+             CERT_TRUST_IS_NOT_VALID_FOR_USAGE;
+        }
+    }
+    if (pChainPara->cbSize >= sizeof(CERT_CHAIN_PARA) &&
+     pChainPara->RequestedIssuancePolicy.Usage.cUsageIdentifier)
+        FIXME("unimplemented for RequestedIssuancePolicy\n");
+}
+
 static void dump_usage_match(LPCSTR name, const CERT_USAGE_MATCH *usageMatch)
 {
-    DWORD i;
+    if (usageMatch->Usage.cUsageIdentifier)
+    {
+        DWORD i;
 
-    TRACE_(chain)("%s: %s\n", name,
-     usageMatch->dwType == USAGE_MATCH_TYPE_AND ? "AND" : "OR");
-    for (i = 0; i < usageMatch->Usage.cUsageIdentifier; i++)
-        TRACE_(chain)("%s\n", usageMatch->Usage.rgpszUsageIdentifier[i]);
+        TRACE_(chain)("%s: %s\n", name,
+         usageMatch->dwType == USAGE_MATCH_TYPE_AND ? "AND" : "OR");
+        for (i = 0; i < usageMatch->Usage.cUsageIdentifier; i++)
+            TRACE_(chain)("%s\n", usageMatch->Usage.rgpszUsageIdentifier[i]);
+    }
 }
 
 static void dump_chain_para(const CERT_CHAIN_PARA *pChainPara)
@@ -2201,7 +2638,9 @@ BOOL WINAPI CertGetCertificateChain(HCERTCHAINENGINE hChainEngine,
         if (!(dwFlags & CERT_CHAIN_RETURN_LOWER_QUALITY_CONTEXTS))
             CRYPT_FreeLowerQualityChains(chain);
         pChain = (PCERT_CHAIN_CONTEXT)chain;
-        CRYPT_VerifyChainRevocation(pChain, pTime, pChainPara, dwFlags);
+        if (!pChain->TrustStatus.dwErrorStatus)
+            CRYPT_VerifyChainRevocation(pChain, pTime, pChainPara, dwFlags);
+        CRYPT_CheckUsages(pChain, pChainPara);
         if (ppChainContext)
             *ppChainContext = pChain;
         else
@@ -2378,8 +2817,8 @@ static BOOL match_dns_to_subject_alt_name(PCERT_EXTENSION ext,
          * in section 4.2.1.6:
          * "Multiple name forms, and multiple instances of each name form,
          *  MAY be included."
-         * It doesn't specify the behavior in such cases, but common usage is
-         * to accept a certificate if any name matches.
+         * It doesn't specify the behavior in such cases, but both RFC 2818
+         * and RFC 2595 explicitly accept a certificate if any name matches.
          */
         for (i = 0; !matches && i < subjectName->cAltEntry; i++)
         {
index 522aadf..a24e6ad 100644 (file)
 
 #include <assert.h>
 #include <stdarg.h>
+#define NONAMELESSUNION
 #include "windef.h"
 #include "winbase.h"
 #include "wincrypt.h"
 #include "wine/debug.h"
+#include "wine/unicode.h"
 #include "crypt32_private.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
@@ -113,7 +115,82 @@ static BOOL compare_crl_issued_by(PCCRL_CONTEXT pCrlContext, DWORD dwType,
         PCCERT_CONTEXT issuer = pvPara;
 
         ret = CertCompareCertificateName(issuer->dwCertEncodingType,
-         &issuer->pCertInfo->Issuer, &pCrlContext->pCrlInfo->Issuer);
+         &issuer->pCertInfo->Subject, &pCrlContext->pCrlInfo->Issuer);
+        if (ret && (dwFlags & CRL_FIND_ISSUED_BY_SIGNATURE_FLAG))
+            ret = CryptVerifyCertificateSignatureEx(0,
+             issuer->dwCertEncodingType,
+             CRYPT_VERIFY_CERT_SIGN_SUBJECT_CRL, (void *)pCrlContext,
+             CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT, (void *)issuer, 0, NULL);
+        if (ret && (dwFlags & CRL_FIND_ISSUED_BY_AKI_FLAG))
+        {
+            PCERT_EXTENSION ext = CertFindExtension(
+             szOID_AUTHORITY_KEY_IDENTIFIER2, pCrlContext->pCrlInfo->cExtension,
+             pCrlContext->pCrlInfo->rgExtension);
+
+            if (ext)
+            {
+                CERT_AUTHORITY_KEY_ID2_INFO *info;
+                DWORD size;
+
+                if ((ret = CryptDecodeObjectEx(X509_ASN_ENCODING,
+                 X509_AUTHORITY_KEY_ID2, ext->Value.pbData, ext->Value.cbData,
+                 CRYPT_DECODE_ALLOC_FLAG, NULL, &info, &size)))
+                {
+                    if (info->AuthorityCertIssuer.cAltEntry &&
+                     info->AuthorityCertSerialNumber.cbData)
+                    {
+                        PCERT_ALT_NAME_ENTRY directoryName = NULL;
+                        DWORD i;
+
+                        for (i = 0; !directoryName &&
+                         i < info->AuthorityCertIssuer.cAltEntry; i++)
+                            if (info->AuthorityCertIssuer.rgAltEntry[i].
+                             dwAltNameChoice == CERT_ALT_NAME_DIRECTORY_NAME)
+                                directoryName =
+                                 &info->AuthorityCertIssuer.rgAltEntry[i];
+                        if (directoryName)
+                        {
+                            ret = CertCompareCertificateName(
+                             issuer->dwCertEncodingType,
+                             &issuer->pCertInfo->Subject,
+                             &directoryName->u.DirectoryName);
+                            if (ret)
+                                ret = CertCompareIntegerBlob(
+                                 &issuer->pCertInfo->SerialNumber,
+                                 &info->AuthorityCertSerialNumber);
+                        }
+                        else
+                        {
+                            FIXME("no supported name type in authority key id2\n");
+                            ret = FALSE;
+                        }
+                    }
+                    else if (info->KeyId.cbData)
+                    {
+                        if ((ext = CertFindExtension(
+                         szOID_SUBJECT_KEY_IDENTIFIER,
+                         issuer->pCertInfo->cExtension,
+                         issuer->pCertInfo->rgExtension)))
+                        {
+                            if (info->KeyId.cbData == ext->Value.cbData)
+                                ret = !memcmp(info->KeyId.pbData,
+                                 ext->Value.pbData, info->KeyId.cbData);
+                            else
+                                ret = FALSE;
+                        }
+                        else
+                            ret = FALSE;
+                    }
+                    else
+                    {
+                        FIXME("unsupported value for AKI extension\n");
+                        ret = FALSE;
+                    }
+                    LocalFree(info);
+                }
+            }
+            /* else: a CRL without an AKI matches any cert */
+        }
     }
     else
         ret = TRUE;
@@ -137,6 +214,17 @@ static BOOL compare_crl_existing(PCCRL_CONTEXT pCrlContext, DWORD dwType,
     return ret;
 }
 
+static BOOL compare_crl_issued_for(PCCRL_CONTEXT pCrlContext, DWORD dwType,
+ DWORD dwFlags, const void *pvPara)
+{
+    const CRL_FIND_ISSUED_FOR_PARA *para = pvPara;
+    BOOL ret;
+
+    ret = CertCompareCertificateName(para->pIssuerCert->dwCertEncodingType,
+     &para->pIssuerCert->pCertInfo->Issuer, &pCrlContext->pCrlInfo->Issuer);
+    return ret;
+}
+
 PCCRL_CONTEXT WINAPI CertFindCRLInStore(HCERTSTORE hCertStore,
  DWORD dwCertEncodingType, DWORD dwFindFlags, DWORD dwFindType,
  const void *pvFindPara, PCCRL_CONTEXT pPrevCrlContext)
@@ -158,6 +246,9 @@ PCCRL_CONTEXT WINAPI CertFindCRLInStore(HCERTSTORE hCertStore,
     case CRL_FIND_EXISTING:
         compare = compare_crl_existing;
         break;
+    case CRL_FIND_ISSUED_FOR:
+        compare = compare_crl_issued_for;
+        break;
     default:
         FIXME("find type %08x unimplemented\n", dwFindType);
         compare = NULL;
@@ -467,11 +558,151 @@ BOOL WINAPI CertSetCRLContextProperty(PCCRL_CONTEXT pCRLContext,
     return ret;
 }
 
+static BOOL compare_dist_point_name(const CRL_DIST_POINT_NAME *name1,
+ const CRL_DIST_POINT_NAME *name2)
+{
+    BOOL match;
+
+    if (name1->dwDistPointNameChoice == name2->dwDistPointNameChoice)
+    {
+        match = TRUE;
+        if (name1->dwDistPointNameChoice == CRL_DIST_POINT_FULL_NAME)
+        {
+            if (name1->u.FullName.cAltEntry == name2->u.FullName.cAltEntry)
+            {
+                DWORD i;
+
+                for (i = 0; match && i < name1->u.FullName.cAltEntry; i++)
+                {
+                    const CERT_ALT_NAME_ENTRY *entry1 =
+                     &name1->u.FullName.rgAltEntry[i];
+                    const CERT_ALT_NAME_ENTRY *entry2 =
+                     &name2->u.FullName.rgAltEntry[i];
+
+                    if (entry1->dwAltNameChoice == entry2->dwAltNameChoice)
+                    {
+                        switch (entry1->dwAltNameChoice)
+                        {
+                        case CERT_ALT_NAME_URL:
+                            match = !strcmpiW(entry1->u.pwszURL,
+                             entry2->u.pwszURL);
+                            break;
+                        case CERT_ALT_NAME_DIRECTORY_NAME:
+                            match = (entry1->u.DirectoryName.cbData ==
+                             entry2->u.DirectoryName.cbData) &&
+                             !memcmp(entry1->u.DirectoryName.pbData,
+                             entry2->u.DirectoryName.pbData,
+                             entry1->u.DirectoryName.cbData);
+                            break;
+                        default:
+                            FIXME("unimplemented for type %d\n",
+                             entry1->dwAltNameChoice);
+                            match = FALSE;
+                        }
+                    }
+                    else
+                        match = FALSE;
+                }
+            }
+            else
+                match = FALSE;
+        }
+    }
+    else
+        match = FALSE;
+    return match;
+}
+
+static BOOL match_dist_point_with_issuing_dist_point(
+ const CRL_DIST_POINT *distPoint, const CRL_ISSUING_DIST_POINT *idp)
+{
+    BOOL match;
+
+    /* While RFC 5280, section 4.2.1.13 recommends against segmenting
+     * CRL distribution points by reasons, it doesn't preclude doing so.
+     * "This profile RECOMMENDS against segmenting CRLs by reason code."
+     * If the issuing distribution point for this CRL is only valid for
+     * some reasons, only match if the reasons covered also match the
+     * reasons in the CRL distribution point.
+     */
+    if (idp->OnlySomeReasonFlags.cbData)
+    {
+        if (idp->OnlySomeReasonFlags.cbData == distPoint->ReasonFlags.cbData)
+        {
+            DWORD i;
+
+            match = TRUE;
+            for (i = 0; match && i < distPoint->ReasonFlags.cbData; i++)
+                if (idp->OnlySomeReasonFlags.pbData[i] !=
+                 distPoint->ReasonFlags.pbData[i])
+                    match = FALSE;
+        }
+        else
+            match = FALSE;
+    }
+    else
+        match = TRUE;
+    if (match)
+        match = compare_dist_point_name(&idp->DistPointName,
+         &distPoint->DistPointName);
+    return match;
+}
+
 BOOL WINAPI CertIsValidCRLForCertificate(PCCERT_CONTEXT pCert,
  PCCRL_CONTEXT pCrl, DWORD dwFlags, void *pvReserved)
 {
+    PCERT_EXTENSION ext;
+    BOOL ret;
+
     TRACE("(%p, %p, %08x, %p)\n", pCert, pCrl, dwFlags, pvReserved);
-    return TRUE;
+
+    if (!pCert)
+        return TRUE;
+
+    if ((ext = CertFindExtension(szOID_ISSUING_DIST_POINT,
+     pCrl->pCrlInfo->cExtension, pCrl->pCrlInfo->rgExtension)))
+    {
+        CRL_ISSUING_DIST_POINT *idp;
+        DWORD size;
+
+        if ((ret = CryptDecodeObjectEx(pCrl->dwCertEncodingType,
+         X509_ISSUING_DIST_POINT, ext->Value.pbData, ext->Value.cbData,
+         CRYPT_DECODE_ALLOC_FLAG, NULL, &idp, &size)))
+        {
+            if ((ext = CertFindExtension(szOID_CRL_DIST_POINTS,
+             pCert->pCertInfo->cExtension, pCert->pCertInfo->rgExtension)))
+            {
+                CRL_DIST_POINTS_INFO *distPoints;
+
+                if ((ret = CryptDecodeObjectEx(pCert->dwCertEncodingType,
+                 X509_CRL_DIST_POINTS, ext->Value.pbData, ext->Value.cbData,
+                 CRYPT_DECODE_ALLOC_FLAG, NULL, &distPoints, &size)))
+                {
+                    DWORD i;
+
+                    ret = FALSE;
+                    for (i = 0; !ret && i < distPoints->cDistPoint; i++)
+                        ret = match_dist_point_with_issuing_dist_point(
+                         &distPoints->rgDistPoint[i], idp);
+                    if (!ret)
+                        SetLastError(CRYPT_E_NO_MATCH);
+                    LocalFree(distPoints);
+                }
+            }
+            else
+            {
+                /* no CRL dist points extension in cert, can't match the CRL
+                 * (which has an issuing dist point extension)
+                 */
+                ret = FALSE;
+                SetLastError(CRYPT_E_NO_MATCH);
+            }
+            LocalFree(idp);
+        }
+    }
+    else
+        ret = TRUE;
+    return ret;
 }
 
 static PCRL_ENTRY CRYPT_FindCertificateInCRL(PCERT_INFO cert, const CRL_INFO *crl)
index b096c09..61e4d3d 100644 (file)
@@ -66,7 +66,7 @@
 @ stdcall CertGetStoreProperty(ptr long ptr ptr)
 @ stdcall CertGetSubjectCertificateFromStore(ptr long ptr)
 @ stdcall CertGetValidUsages(long ptr ptr ptr ptr)
-@ stub CertIsRDNAttrsInCertificateName
+@ stdcall CertIsRDNAttrsInCertificateName(long long ptr ptr)
 @ stdcall CertIsValidCRLForCertificate(ptr ptr long ptr)
 @ stdcall CertNameToStrA(long ptr long ptr long)
 @ stdcall CertNameToStrW(long ptr long ptr long)
index a16d89f..1fd2383 100644 (file)
@@ -3106,43 +3106,6 @@ static BOOL CRYPT_AsnDecodeAltNameInternal(const BYTE *pbEncoded,
     return ret;
 }
 
-/* Like CRYPT_AsnDecodeIntegerInternal, but swaps the bytes */
-static BOOL CRYPT_AsnDecodeIntegerSwapBytes(const BYTE *pbEncoded,
- DWORD cbEncoded, DWORD dwFlags, void *pvStructInfo, DWORD *pcbStructInfo,
- DWORD *pcbDecoded)
-{
-    BOOL ret;
-
-    TRACE("(%p, %d, 0x%08x, %p, %d, %p)\n", pbEncoded, cbEncoded, dwFlags,
-     pvStructInfo, *pcbStructInfo, pcbDecoded);
-
-    /* Can't use the CRYPT_DECODE_NOCOPY_FLAG, because we modify the bytes in-
-     * place.
-     */
-    ret = CRYPT_AsnDecodeIntegerInternal(pbEncoded, cbEncoded,
-     dwFlags & ~CRYPT_DECODE_NOCOPY_FLAG, pvStructInfo, pcbStructInfo,
-     pcbDecoded);
-    if (ret && pvStructInfo)
-    {
-        CRYPT_DATA_BLOB *blob = pvStructInfo;
-
-        if (blob->cbData)
-        {
-            DWORD i;
-            BYTE temp;
-
-            for (i = 0; i < blob->cbData / 2; i++)
-            {
-                temp = blob->pbData[i];
-                blob->pbData[i] = blob->pbData[blob->cbData - i - 1];
-                blob->pbData[blob->cbData - i - 1] = temp;
-            }
-        }
-    }
-    TRACE("returning %d (%08x)\n", ret, GetLastError());
-    return ret;
-}
-
 static BOOL WINAPI CRYPT_AsnDecodeAuthorityKeyId(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const BYTE *pbEncoded, DWORD cbEncoded, DWORD dwFlags,
  PCRYPT_DECODE_PARA pDecodePara, void *pvStructInfo, DWORD *pcbStructInfo)
@@ -3153,7 +3116,7 @@ static BOOL WINAPI CRYPT_AsnDecodeAuthorityKeyId(DWORD dwCertEncodingType,
     {
         struct AsnDecodeSequenceItem items[] = {
          { ASN_CONTEXT | 0, offsetof(CERT_AUTHORITY_KEY_ID_INFO, KeyId),
-           CRYPT_AsnDecodeIntegerSwapBytes, sizeof(CRYPT_DATA_BLOB),
+           CRYPT_AsnDecodeOctetsInternal, sizeof(CRYPT_DATA_BLOB),
            TRUE, TRUE, offsetof(CERT_AUTHORITY_KEY_ID_INFO, KeyId.pbData), 0 },
          { ASN_CONTEXT | ASN_CONSTRUCTOR| 1,
            offsetof(CERT_AUTHORITY_KEY_ID_INFO, CertIssuer),
@@ -3188,7 +3151,7 @@ static BOOL WINAPI CRYPT_AsnDecodeAuthorityKeyId2(DWORD dwCertEncodingType,
     {
         struct AsnDecodeSequenceItem items[] = {
          { ASN_CONTEXT | 0, offsetof(CERT_AUTHORITY_KEY_ID2_INFO, KeyId),
-           CRYPT_AsnDecodeIntegerSwapBytes, sizeof(CRYPT_DATA_BLOB),
+           CRYPT_AsnDecodeOctetsInternal, sizeof(CRYPT_DATA_BLOB),
            TRUE, TRUE, offsetof(CERT_AUTHORITY_KEY_ID2_INFO, KeyId.pbData), 0 },
          { ASN_CONTEXT | ASN_CONSTRUCTOR| 1,
            offsetof(CERT_AUTHORITY_KEY_ID2_INFO, AuthorityCertIssuer),
index 4d137ce..b7bbc83 100644 (file)
@@ -2438,45 +2438,6 @@ static BOOL WINAPI CRYPT_AsnEncodeAltNameEntry(DWORD dwCertEncodingType,
     return ret;
 }
 
-static BOOL WINAPI CRYPT_AsnEncodeIntegerSwapBytes(DWORD dwCertEncodingType,
- LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
- PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded)
-{
-    BOOL ret;
-
-    __TRY
-    {
-        const CRYPT_DATA_BLOB *blob = pvStructInfo;
-        CRYPT_DATA_BLOB newBlob = { blob->cbData, NULL };
-
-        ret = TRUE;
-        if (newBlob.cbData)
-        {
-            newBlob.pbData = CryptMemAlloc(newBlob.cbData);
-            if (newBlob.pbData)
-            {
-                DWORD i;
-
-                for (i = 0; i < newBlob.cbData; i++)
-                    newBlob.pbData[newBlob.cbData - i - 1] = blob->pbData[i];
-            }
-            else
-                ret = FALSE;
-        }
-        if (ret)
-            ret = CRYPT_AsnEncodeInteger(dwCertEncodingType, lpszStructType,
-             &newBlob, dwFlags, pEncodePara, pbEncoded, pcbEncoded);
-        CryptMemFree(newBlob.pbData);
-    }
-    __EXCEPT_PAGE_FAULT
-    {
-        SetLastError(STATUS_ACCESS_VIOLATION);
-        ret = FALSE;
-    }
-    __ENDTRY
-    return ret;
-}
-
 static BOOL WINAPI CRYPT_AsnEncodeAuthorityKeyId(DWORD dwCertEncodingType,
  LPCSTR lpszStructType, const void *pvStructInfo, DWORD dwFlags,
  PCRYPT_ENCODE_PARA pEncodePara, BYTE *pbEncoded, DWORD *pcbEncoded)
@@ -2495,7 +2456,7 @@ static BOOL WINAPI CRYPT_AsnEncodeAuthorityKeyId(DWORD dwCertEncodingType,
         {
             swapped[cSwapped].tag = ASN_CONTEXT | 0;
             swapped[cSwapped].pvStructInfo = &info->KeyId;
-            swapped[cSwapped].encodeFunc = CRYPT_AsnEncodeIntegerSwapBytes;
+            swapped[cSwapped].encodeFunc = CRYPT_AsnEncodeOctets;
             items[cItem].pvStructInfo = &swapped[cSwapped];
             items[cItem].encodeFunc = CRYPT_AsnEncodeSwapTag;
             cSwapped++;
@@ -2626,7 +2587,7 @@ static BOOL WINAPI CRYPT_AsnEncodeAuthorityKeyId2(DWORD dwCertEncodingType,
         {
             swapped[cSwapped].tag = ASN_CONTEXT | 0;
             swapped[cSwapped].pvStructInfo = &info->KeyId;
-            swapped[cSwapped].encodeFunc = CRYPT_AsnEncodeIntegerSwapBytes;
+            swapped[cSwapped].encodeFunc = CRYPT_AsnEncodeOctets;
             items[cItem].pvStructInfo = &swapped[cSwapped];
             items[cItem].encodeFunc = CRYPT_AsnEncodeSwapTag;
             cSwapped++;
index 0a3889d..68a4852 100644 (file)
@@ -881,9 +881,11 @@ static BOOL CRYPT_RemoveStringFromMultiString(LPWSTR multi, LPCWSTR toRemove)
         }
         else
         {
+            LPCWSTR nextStr = spotToRemove + lstrlenW(toRemove) + 1;
+
             /* Copy remainder of string "left" */
-            memmove(spotToRemove, spotToRemove + lstrlenW(toRemove) + 1,
-             (len - (spotToRemove - multi)) * sizeof(WCHAR));
+            memmove(spotToRemove, nextStr,
+             (len - (nextStr - multi)) * sizeof(WCHAR));
         }
         ret = TRUE;
     }
index a55b281..70b5ef8 100644 (file)
@@ -40,6 +40,9 @@
 #include "winternl.h"
 #include "wine/debug.h"
 #include "crypt32_private.h"
+#ifdef __APPLE__
+#include <Security/Security.h>
+#endif
 
 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
 
@@ -713,6 +716,35 @@ static void read_trusted_roots_from_known_locations(HCERTSTORE store)
         DWORD i;
         BOOL ret = FALSE;
 
+#ifdef __APPLE__
+        OSStatus status;
+        CFArrayRef rootCerts;
+
+        status = SecTrustCopyAnchorCertificates(&rootCerts);
+        if (status == noErr)
+        {
+            int i;
+            for (i = 0; i < CFArrayGetCount(rootCerts); i++)
+            {
+                SecCertificateRef cert = (SecCertificateRef)CFArrayGetValueAtIndex(rootCerts, i);
+                CFDataRef certData;
+                if ((status = SecKeychainItemExport(cert, kSecFormatX509Cert, 0, NULL, &certData)) == noErr)
+                {
+                    if (CertAddEncodedCertificateToStore(store, X509_ASN_ENCODING,
+                            CFDataGetBytePtr(certData), CFDataGetLength(certData),
+                            CERT_STORE_ADD_NEW, NULL))
+                        ret = TRUE;
+                    else
+                        WARN("adding root cert %d failed: %08x\n", i, GetLastError());
+                    CFRelease(certData);
+                }
+                else
+                    WARN("could not export certificate %d to X509 format: 0x%08x\n", i, (unsigned int)status);
+            }
+            CFRelease(rootCerts);
+        }
+#endif
+
         for (i = 0; !ret &&
          i < sizeof(CRYPT_knownLocations) / sizeof(CRYPT_knownLocations[0]);
          i++)