sync crypt32 to wine 1.1.34

svn path=/trunk/; revision=44422
This commit is contained in:
Christoph von Wittich 2009-12-05 20:37:08 +00:00
parent ef635964be
commit 13c03eb48b
8 changed files with 957 additions and 243 deletions

View 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)
{

View 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] == '.')
{
if (lstrlenW(name) > lstrlenW(constraint))
match = !lstrcmpiW(name + lstrlenW(name) - lstrlenW(constraint),
constraint);
}
else
match = !lstrcmpiW(constraint, name);
{
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);
}
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)
{
BOOL match = FALSE;
if (name->dwAltNameChoice == constraint->dwAltNameChoice)
{
if (present)
*present = TRUE;
switch (constraint->dwAltNameChoice)
{
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;
}
}
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; i < subjectName->cAltEntry; i++)
{
if (subjectName->rgAltEntry[i].dwAltNameChoice ==
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;
}
}
}
*trustErrorStatus |= match ? errorIfFound : errorIfNotFound;
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,55 +823,251 @@ static inline PCERT_EXTENSION get_subject_alt_name_ext(const CERT_INFO *cert)
return ext;
}
static void compare_alt_name_with_constraints(const CERT_EXTENSION *altNameExt,
const CERT_NAME_CONSTRAINTS_INFO *nameConstraints, DWORD *trustErrorStatus)
{
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))
{
DWORD i;
for (i = 0; i < subjectAltName->cAltEntry; i++)
{
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))
{
TRACE_(chain)("subject alternate name form %d excluded\n",
subjectAltName->rgAltEntry[i].dwAltNameChoice);
*trustErrorStatus |=
CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT;
}
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_HAS_NOT_PERMITTED_NAME_CONSTRAINT;
}
}
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)
{
*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)
{
/* If there aren't any existing constraints, don't bother checking */
if (nameConstraints->cPermittedSubtree || nameConstraints->cExcludedSubtree)
{
CERT_EXTENSION *ext = get_subject_alt_name_ext(cert);
CERT_EXTENSION *ext = get_subject_alt_name_ext(cert);
if (ext)
{
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))
{
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);
}
else
*trustErrorStatus |=
CERT_TRUST_INVALID_EXTENSION |
CERT_TRUST_INVALID_NAME_CONSTRAINTS;
}
else
{
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;
}
}
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. */
@ -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++)
{

View file

@ -19,10 +19,12 @@
#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)

View 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)

View 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),

View 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++;

View 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;
}

View 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++)