mirror of
https://github.com/reactos/reactos.git
synced 2025-06-04 08:50:27 +00:00
sync crypt32 to wine 1.1.34
svn path=/trunk/; revision=44422
This commit is contained in:
parent
ef635964be
commit
13c03eb48b
8 changed files with 957 additions and 243 deletions
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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++)
|
||||
{
|
||||
|
|
|
@ -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,
|
||||
¶->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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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++;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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++)
|
||||
|
|
Loading…
Reference in a new issue