From: Christoph von Wittich Date: Sat, 5 Dec 2009 20:37:08 +0000 (+0000) Subject: sync crypt32 to wine 1.1.34 X-Git-Tag: backups/aicom-network-stable@46924~399 X-Git-Url: https://git.reactos.org/?p=reactos.git;a=commitdiff_plain;h=13c03eb48b5073193b9daf5a513b6fc1435722bf sync crypt32 to wine 1.1.34 svn path=/trunk/; revision=44422 --- diff --git a/reactos/dll/win32/crypt32/cert.c b/reactos/dll/win32/crypt32/cert.c index 4f3df326943..a24757019cb 100644 --- a/reactos/dll/win32/crypt32/cert.c +++ b/reactos/dll/win32/crypt32/cert.c @@ -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) { diff --git a/reactos/dll/win32/crypt32/chain.c b/reactos/dll/win32/crypt32/chain.c index eb6d757ce7f..5cc46b763bc 100644 --- a/reactos/dll/win32/crypt32/chain.c +++ b/reactos/dll/win32/crypt32/chain.c @@ -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++) { diff --git a/reactos/dll/win32/crypt32/crl.c b/reactos/dll/win32/crypt32/crl.c index 522aadf616e..a24e6adf887 100644 --- a/reactos/dll/win32/crypt32/crl.c +++ b/reactos/dll/win32/crypt32/crl.c @@ -19,10 +19,12 @@ #include #include +#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, + ¶->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) diff --git a/reactos/dll/win32/crypt32/crypt32.spec b/reactos/dll/win32/crypt32/crypt32.spec index b096c090c11..61e4d3d5db7 100644 --- a/reactos/dll/win32/crypt32/crypt32.spec +++ b/reactos/dll/win32/crypt32/crypt32.spec @@ -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) diff --git a/reactos/dll/win32/crypt32/decode.c b/reactos/dll/win32/crypt32/decode.c index a16d89f8802..1fd23837395 100644 --- a/reactos/dll/win32/crypt32/decode.c +++ b/reactos/dll/win32/crypt32/decode.c @@ -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), diff --git a/reactos/dll/win32/crypt32/encode.c b/reactos/dll/win32/crypt32/encode.c index 4d137cebc2f..b7bbc83600c 100644 --- a/reactos/dll/win32/crypt32/encode.c +++ b/reactos/dll/win32/crypt32/encode.c @@ -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++; diff --git a/reactos/dll/win32/crypt32/oid.c b/reactos/dll/win32/crypt32/oid.c index 0a3889d183c..68a48520dac 100644 --- a/reactos/dll/win32/crypt32/oid.c +++ b/reactos/dll/win32/crypt32/oid.c @@ -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; } diff --git a/reactos/dll/win32/crypt32/rootstore.c b/reactos/dll/win32/crypt32/rootstore.c index a55b281601c..70b5ef80a3a 100644 --- a/reactos/dll/win32/crypt32/rootstore.c +++ b/reactos/dll/win32/crypt32/rootstore.c @@ -40,6 +40,9 @@ #include "winternl.h" #include "wine/debug.h" #include "crypt32_private.h" +#ifdef __APPLE__ +#include +#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++)