server: Allow reading directly from console handle.
[wine.git] / dlls / crypt32 / chain.c
blobc7d387ebc5907e83b1f313119f2210fc91b7e87d
1 /*
2 * Copyright 2006 Juan Lang
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 #include <stdarg.h>
21 #include <wchar.h>
22 #define NONAMELESSUNION
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winnls.h"
26 #define CERT_CHAIN_PARA_HAS_EXTRA_FIELDS
27 #define CERT_REVOCATION_PARA_HAS_EXTRA_FIELDS
28 #include "wincrypt.h"
29 #include "wininet.h"
30 #include "wine/debug.h"
31 #include "crypt32_private.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
34 WINE_DECLARE_DEBUG_CHANNEL(chain);
36 #define DEFAULT_CYCLE_MODULUS 7
38 /* This represents a subset of a certificate chain engine: it doesn't include
39 * the "hOther" store described by MSDN, because I'm not sure how that's used.
40 * It also doesn't include the "hTrust" store, because I don't yet implement
41 * CTLs or complex certificate chains.
43 typedef struct _CertificateChainEngine
45 LONG ref;
46 HCERTSTORE hRoot;
47 HCERTSTORE hWorld;
48 DWORD dwFlags;
49 DWORD dwUrlRetrievalTimeout;
50 DWORD MaximumCachedCertificates;
51 DWORD CycleDetectionModulus;
52 } CertificateChainEngine;
54 static inline void CRYPT_AddStoresToCollection(HCERTSTORE collection,
55 DWORD cStores, HCERTSTORE *stores)
57 DWORD i;
59 for (i = 0; i < cStores; i++)
60 CertAddStoreToCollection(collection, stores[i], 0, 0);
63 static inline void CRYPT_CloseStores(DWORD cStores, HCERTSTORE *stores)
65 DWORD i;
67 for (i = 0; i < cStores; i++)
68 CertCloseStore(stores[i], 0);
71 /* Finds cert in store by comparing the cert's hashes. */
72 static PCCERT_CONTEXT CRYPT_FindCertInStore(HCERTSTORE store,
73 PCCERT_CONTEXT cert)
75 PCCERT_CONTEXT matching = NULL;
76 BYTE hash[20];
77 DWORD size = sizeof(hash);
79 if (CertGetCertificateContextProperty(cert, CERT_HASH_PROP_ID, hash, &size))
81 CRYPT_HASH_BLOB blob = { sizeof(hash), hash };
83 matching = CertFindCertificateInStore(store, cert->dwCertEncodingType,
84 0, CERT_FIND_SHA1_HASH, &blob, NULL);
86 return matching;
89 static BOOL CRYPT_CheckRestrictedRoot(HCERTSTORE store)
91 BOOL ret = TRUE;
93 if (store)
95 HCERTSTORE rootStore = CertOpenSystemStoreW(0, L"Root");
96 PCCERT_CONTEXT cert = NULL, check;
98 do {
99 cert = CertEnumCertificatesInStore(store, cert);
100 if (cert)
102 if (!(check = CRYPT_FindCertInStore(rootStore, cert)))
103 ret = FALSE;
104 else
105 CertFreeCertificateContext(check);
107 } while (ret && cert);
108 if (cert)
109 CertFreeCertificateContext(cert);
110 CertCloseStore(rootStore, 0);
112 return ret;
115 HCERTCHAINENGINE CRYPT_CreateChainEngine(HCERTSTORE root, DWORD system_store, const CERT_CHAIN_ENGINE_CONFIG *config)
117 CertificateChainEngine *engine;
118 HCERTSTORE worldStores[4];
120 if(!root) {
121 if(config->cbSize >= sizeof(CERT_CHAIN_ENGINE_CONFIG) && config->hExclusiveRoot)
122 root = CertDuplicateStore(config->hExclusiveRoot);
123 else if (config->hRestrictedRoot)
124 root = CertDuplicateStore(config->hRestrictedRoot);
125 else
126 root = CertOpenStore(CERT_STORE_PROV_SYSTEM_W, 0, 0, system_store, L"Root");
127 if(!root)
128 return NULL;
131 engine = CryptMemAlloc(sizeof(CertificateChainEngine));
132 if(!engine) {
133 CertCloseStore(root, 0);
134 return NULL;
137 engine->ref = 1;
138 engine->hRoot = root;
139 engine->hWorld = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, 0, CERT_STORE_CREATE_NEW_FLAG, NULL);
140 worldStores[0] = CertDuplicateStore(engine->hRoot);
141 worldStores[1] = CertOpenStore(CERT_STORE_PROV_SYSTEM_W, 0, 0, system_store, L"CA");
142 worldStores[2] = CertOpenStore(CERT_STORE_PROV_SYSTEM_W, 0, 0, system_store, L"My");
143 worldStores[3] = CertOpenStore(CERT_STORE_PROV_SYSTEM_W, 0, 0, system_store, L"Trust");
145 CRYPT_AddStoresToCollection(engine->hWorld, ARRAY_SIZE(worldStores), worldStores);
146 CRYPT_AddStoresToCollection(engine->hWorld, config->cAdditionalStore, config->rghAdditionalStore);
147 CRYPT_CloseStores(ARRAY_SIZE(worldStores), worldStores);
149 engine->dwFlags = config->dwFlags;
150 engine->dwUrlRetrievalTimeout = config->dwUrlRetrievalTimeout;
151 engine->MaximumCachedCertificates = config->MaximumCachedCertificates;
152 if(config->CycleDetectionModulus)
153 engine->CycleDetectionModulus = config->CycleDetectionModulus;
154 else
155 engine->CycleDetectionModulus = DEFAULT_CYCLE_MODULUS;
157 return engine;
160 static CertificateChainEngine *default_cu_engine, *default_lm_engine;
162 static CertificateChainEngine *get_chain_engine(HCERTCHAINENGINE handle, BOOL allow_default)
164 const CERT_CHAIN_ENGINE_CONFIG config = { sizeof(config) };
166 if(handle == HCCE_CURRENT_USER) {
167 if(!allow_default)
168 return NULL;
170 if(!default_cu_engine) {
171 handle = CRYPT_CreateChainEngine(NULL, CERT_SYSTEM_STORE_CURRENT_USER, &config);
172 InterlockedCompareExchangePointer((void**)&default_cu_engine, handle, NULL);
173 if(default_cu_engine != handle)
174 CertFreeCertificateChainEngine(handle);
177 return default_cu_engine;
180 if(handle == HCCE_LOCAL_MACHINE) {
181 if(!allow_default)
182 return NULL;
184 if(!default_lm_engine) {
185 handle = CRYPT_CreateChainEngine(NULL, CERT_SYSTEM_STORE_LOCAL_MACHINE, &config);
186 InterlockedCompareExchangePointer((void**)&default_lm_engine, handle, NULL);
187 if(default_lm_engine != handle)
188 CertFreeCertificateChainEngine(handle);
191 return default_lm_engine;
194 return (CertificateChainEngine*)handle;
197 static void free_chain_engine(CertificateChainEngine *engine)
199 if(!engine || InterlockedDecrement(&engine->ref))
200 return;
202 CertCloseStore(engine->hWorld, 0);
203 CertCloseStore(engine->hRoot, 0);
204 CryptMemFree(engine);
207 typedef struct _CERT_CHAIN_ENGINE_CONFIG_NO_EXCLUSIVE_ROOT
209 DWORD cbSize;
210 HCERTSTORE hRestrictedRoot;
211 HCERTSTORE hRestrictedTrust;
212 HCERTSTORE hRestrictedOther;
213 DWORD cAdditionalStore;
214 HCERTSTORE *rghAdditionalStore;
215 DWORD dwFlags;
216 DWORD dwUrlRetrievalTimeout;
217 DWORD MaximumCachedCertificates;
218 DWORD CycleDetectionModulus;
219 } CERT_CHAIN_ENGINE_CONFIG_NO_EXCLUSIVE_ROOT;
221 BOOL WINAPI CertCreateCertificateChainEngine(PCERT_CHAIN_ENGINE_CONFIG pConfig,
222 HCERTCHAINENGINE *phChainEngine)
224 BOOL ret;
226 TRACE("(%p, %p)\n", pConfig, phChainEngine);
228 if (pConfig->cbSize != sizeof(CERT_CHAIN_ENGINE_CONFIG_NO_EXCLUSIVE_ROOT)
229 && pConfig->cbSize != sizeof(CERT_CHAIN_ENGINE_CONFIG))
231 SetLastError(E_INVALIDARG);
232 return FALSE;
234 ret = CRYPT_CheckRestrictedRoot(pConfig->hRestrictedRoot);
235 if (!ret)
237 *phChainEngine = NULL;
238 return FALSE;
241 *phChainEngine = CRYPT_CreateChainEngine(NULL, CERT_SYSTEM_STORE_CURRENT_USER, pConfig);
242 return *phChainEngine != NULL;
245 void WINAPI CertFreeCertificateChainEngine(HCERTCHAINENGINE hChainEngine)
247 TRACE("(%p)\n", hChainEngine);
248 free_chain_engine(get_chain_engine(hChainEngine, FALSE));
251 void default_chain_engine_free(void)
253 free_chain_engine(default_cu_engine);
254 free_chain_engine(default_lm_engine);
257 typedef struct _CertificateChain
259 CERT_CHAIN_CONTEXT context;
260 HCERTSTORE world;
261 LONG ref;
262 } CertificateChain;
264 DWORD CRYPT_IsCertificateSelfSigned(const CERT_CONTEXT *cert)
266 DWORD size, status = 0;
267 PCERT_EXTENSION ext;
268 BOOL ret;
270 if ((ext = CertFindExtension(szOID_AUTHORITY_KEY_IDENTIFIER2,
271 cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension)))
273 CERT_AUTHORITY_KEY_ID2_INFO *info;
275 ret = CryptDecodeObjectEx(cert->dwCertEncodingType,
276 X509_AUTHORITY_KEY_ID2, ext->Value.pbData, ext->Value.cbData,
277 CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL,
278 &info, &size);
279 if (ret)
281 if (info->AuthorityCertIssuer.cAltEntry &&
282 info->AuthorityCertSerialNumber.cbData)
284 PCERT_ALT_NAME_ENTRY directoryName = NULL;
285 DWORD i;
287 for (i = 0; !directoryName &&
288 i < info->AuthorityCertIssuer.cAltEntry; i++)
289 if (info->AuthorityCertIssuer.rgAltEntry[i].dwAltNameChoice
290 == CERT_ALT_NAME_DIRECTORY_NAME)
291 directoryName =
292 &info->AuthorityCertIssuer.rgAltEntry[i];
293 if (directoryName)
295 if (CertCompareCertificateName(cert->dwCertEncodingType, &directoryName->u.DirectoryName, &cert->pCertInfo->Issuer)
296 && CertCompareIntegerBlob(&info->AuthorityCertSerialNumber, &cert->pCertInfo->SerialNumber))
297 status = CERT_TRUST_HAS_NAME_MATCH_ISSUER;
299 else
301 FIXME("no supported name type in authority key id2\n");
302 ret = FALSE;
305 else if (info->KeyId.cbData)
307 ret = CertGetCertificateContextProperty(cert,
308 CERT_KEY_IDENTIFIER_PROP_ID, NULL, &size);
309 if (ret && size == info->KeyId.cbData)
311 LPBYTE buf = CryptMemAlloc(size);
313 if (buf)
315 CertGetCertificateContextProperty(cert, CERT_KEY_IDENTIFIER_PROP_ID, buf, &size);
316 if (!memcmp(buf, info->KeyId.pbData, size))
317 status = CERT_TRUST_HAS_KEY_MATCH_ISSUER;
318 CryptMemFree(buf);
322 LocalFree(info);
325 else if ((ext = CertFindExtension(szOID_AUTHORITY_KEY_IDENTIFIER,
326 cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension)))
328 CERT_AUTHORITY_KEY_ID_INFO *info;
330 ret = CryptDecodeObjectEx(cert->dwCertEncodingType,
331 X509_AUTHORITY_KEY_ID, ext->Value.pbData, ext->Value.cbData,
332 CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL,
333 &info, &size);
334 if (ret)
336 if (info->CertIssuer.cbData && info->CertSerialNumber.cbData)
338 if (CertCompareCertificateName(cert->dwCertEncodingType, &info->CertIssuer, &cert->pCertInfo->Issuer)
339 && CertCompareIntegerBlob(&info->CertSerialNumber, &cert->pCertInfo->SerialNumber))
340 status = CERT_TRUST_HAS_NAME_MATCH_ISSUER;
342 else if (info->KeyId.cbData)
344 ret = CertGetCertificateContextProperty(cert,
345 CERT_KEY_IDENTIFIER_PROP_ID, NULL, &size);
346 if (ret && size == info->KeyId.cbData)
348 LPBYTE buf = CryptMemAlloc(size);
350 if (buf)
352 CertGetCertificateContextProperty(cert,
353 CERT_KEY_IDENTIFIER_PROP_ID, buf, &size);
354 if (!memcmp(buf, info->KeyId.pbData, size))
355 status = CERT_TRUST_HAS_KEY_MATCH_ISSUER;
356 CryptMemFree(buf);
360 LocalFree(info);
363 else
364 if (CertCompareCertificateName(cert->dwCertEncodingType, &cert->pCertInfo->Subject, &cert->pCertInfo->Issuer))
365 status = CERT_TRUST_HAS_NAME_MATCH_ISSUER;
367 if (status)
368 status |= CERT_TRUST_IS_SELF_SIGNED;
370 return status;
373 static void CRYPT_FreeChainElement(PCERT_CHAIN_ELEMENT element)
375 CertFreeCertificateContext(element->pCertContext);
376 CryptMemFree(element);
379 static void CRYPT_CheckSimpleChainForCycles(PCERT_SIMPLE_CHAIN chain)
381 DWORD i, j, cyclicCertIndex = 0;
383 /* O(n^2) - I don't think there's a faster way */
384 for (i = 0; !cyclicCertIndex && i < chain->cElement; i++)
385 for (j = i + 1; !cyclicCertIndex && j < chain->cElement; j++)
386 if (CertCompareCertificate(X509_ASN_ENCODING,
387 chain->rgpElement[i]->pCertContext->pCertInfo,
388 chain->rgpElement[j]->pCertContext->pCertInfo))
389 cyclicCertIndex = j;
390 if (cyclicCertIndex)
392 chain->rgpElement[cyclicCertIndex]->TrustStatus.dwErrorStatus
393 |= CERT_TRUST_IS_CYCLIC | CERT_TRUST_INVALID_BASIC_CONSTRAINTS;
394 /* Release remaining certs */
395 for (i = cyclicCertIndex + 1; i < chain->cElement; i++)
396 CRYPT_FreeChainElement(chain->rgpElement[i]);
397 /* Truncate chain */
398 chain->cElement = cyclicCertIndex + 1;
402 /* Checks whether the chain is cyclic by examining the last element's status */
403 static inline BOOL CRYPT_IsSimpleChainCyclic(const CERT_SIMPLE_CHAIN *chain)
405 if (chain->cElement)
406 return chain->rgpElement[chain->cElement - 1]->TrustStatus.dwErrorStatus
407 & CERT_TRUST_IS_CYCLIC;
408 else
409 return FALSE;
412 static inline void CRYPT_CombineTrustStatus(CERT_TRUST_STATUS *chainStatus,
413 const CERT_TRUST_STATUS *elementStatus)
415 /* Any error that applies to an element also applies to a chain.. */
416 chainStatus->dwErrorStatus |= elementStatus->dwErrorStatus;
417 /* but the bottom nibble of an element's info status doesn't apply to the
418 * chain.
420 chainStatus->dwInfoStatus |= (elementStatus->dwInfoStatus & 0xfffffff0);
423 static BOOL CRYPT_AddCertToSimpleChain(const CertificateChainEngine *engine,
424 PCERT_SIMPLE_CHAIN chain, PCCERT_CONTEXT cert, DWORD subjectInfoStatus)
426 BOOL ret = FALSE;
427 PCERT_CHAIN_ELEMENT element = CryptMemAlloc(sizeof(CERT_CHAIN_ELEMENT));
429 if (element)
431 if (!chain->cElement)
432 chain->rgpElement = CryptMemAlloc(sizeof(PCERT_CHAIN_ELEMENT));
433 else
434 chain->rgpElement = CryptMemRealloc(chain->rgpElement,
435 (chain->cElement + 1) * sizeof(PCERT_CHAIN_ELEMENT));
436 if (chain->rgpElement)
438 chain->rgpElement[chain->cElement++] = element;
439 memset(element, 0, sizeof(CERT_CHAIN_ELEMENT));
440 element->cbSize = sizeof(CERT_CHAIN_ELEMENT);
441 element->pCertContext = CertDuplicateCertificateContext(cert);
442 if (chain->cElement > 1)
443 chain->rgpElement[chain->cElement - 2]->TrustStatus.dwInfoStatus
444 = subjectInfoStatus;
445 /* FIXME: initialize the rest of element */
446 if (!(chain->cElement % engine->CycleDetectionModulus))
448 CRYPT_CheckSimpleChainForCycles(chain);
449 /* Reinitialize the element pointer in case the chain is
450 * cyclic, in which case the chain is truncated.
452 element = chain->rgpElement[chain->cElement - 1];
454 CRYPT_CombineTrustStatus(&chain->TrustStatus,
455 &element->TrustStatus);
456 ret = TRUE;
458 else
459 CryptMemFree(element);
461 return ret;
464 static void CRYPT_FreeSimpleChain(PCERT_SIMPLE_CHAIN chain)
466 DWORD i;
468 for (i = 0; i < chain->cElement; i++)
469 CRYPT_FreeChainElement(chain->rgpElement[i]);
470 CryptMemFree(chain->rgpElement);
471 CryptMemFree(chain);
474 static void CRYPT_CheckTrustedStatus(HCERTSTORE hRoot,
475 PCERT_CHAIN_ELEMENT rootElement)
477 PCCERT_CONTEXT trustedRoot = CRYPT_FindCertInStore(hRoot,
478 rootElement->pCertContext);
480 if (!trustedRoot)
481 rootElement->TrustStatus.dwErrorStatus |=
482 CERT_TRUST_IS_UNTRUSTED_ROOT;
483 else
484 CertFreeCertificateContext(trustedRoot);
487 static void CRYPT_CheckRootCert(HCERTSTORE hRoot,
488 PCERT_CHAIN_ELEMENT rootElement)
490 PCCERT_CONTEXT root = rootElement->pCertContext;
492 if (!CryptVerifyCertificateSignatureEx(0, root->dwCertEncodingType,
493 CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT, (void *)root,
494 CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT, (void *)root, 0, NULL))
496 TRACE_(chain)("Last certificate's signature is invalid\n");
497 rootElement->TrustStatus.dwErrorStatus |=
498 CERT_TRUST_IS_NOT_SIGNATURE_VALID;
500 CRYPT_CheckTrustedStatus(hRoot, rootElement);
503 /* Decodes a cert's basic constraints extension (either szOID_BASIC_CONSTRAINTS
504 * or szOID_BASIC_CONSTRAINTS2, whichever is present) into a
505 * CERT_BASIC_CONSTRAINTS2_INFO. If it neither extension is present, sets
506 * constraints->fCA to defaultIfNotSpecified.
507 * Returns FALSE if the extension is present but couldn't be decoded.
509 static BOOL CRYPT_DecodeBasicConstraints(PCCERT_CONTEXT cert,
510 CERT_BASIC_CONSTRAINTS2_INFO *constraints, BOOL defaultIfNotSpecified)
512 BOOL ret = TRUE;
513 PCERT_EXTENSION ext = CertFindExtension(szOID_BASIC_CONSTRAINTS,
514 cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension);
516 constraints->fPathLenConstraint = FALSE;
517 if (ext)
519 CERT_BASIC_CONSTRAINTS_INFO *info;
520 DWORD size = 0;
522 ret = CryptDecodeObjectEx(X509_ASN_ENCODING, szOID_BASIC_CONSTRAINTS,
523 ext->Value.pbData, ext->Value.cbData, CRYPT_DECODE_ALLOC_FLAG,
524 NULL, &info, &size);
525 if (ret)
527 if (info->SubjectType.cbData == 1)
528 constraints->fCA =
529 info->SubjectType.pbData[0] & CERT_CA_SUBJECT_FLAG;
530 LocalFree(info);
533 else
535 ext = CertFindExtension(szOID_BASIC_CONSTRAINTS2,
536 cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension);
537 if (ext)
539 DWORD size = sizeof(CERT_BASIC_CONSTRAINTS2_INFO);
541 ret = CryptDecodeObjectEx(X509_ASN_ENCODING,
542 szOID_BASIC_CONSTRAINTS2, ext->Value.pbData, ext->Value.cbData,
543 0, NULL, constraints, &size);
545 else
546 constraints->fCA = defaultIfNotSpecified;
548 return ret;
551 /* Checks element's basic constraints to see if it can act as a CA, with
552 * remainingCAs CAs left in this chain. In general, a cert must include the
553 * basic constraints extension, with the CA flag asserted, in order to be
554 * allowed to be a CA. A V1 or V2 cert, which has no extensions, is also
555 * allowed to be a CA if it's installed locally (in the engine's world store.)
556 * This matches the expected usage in RFC 5280, section 4.2.1.9: a conforming
557 * CA MUST include the basic constraints extension in all certificates that are
558 * used to validate digital signatures on certificates. It also matches
559 * section 6.1.4(k): "If a certificate is a v1 or v2 certificate, then the
560 * application MUST either verify that the certificate is a CA certificate
561 * through out-of-band means or reject the certificate." Rejecting the
562 * certificate prohibits a large number of commonly used certificates, so
563 * accepting locally installed ones is a compromise.
564 * Root certificates are also allowed to be CAs even without a basic
565 * constraints extension. This is implied by RFC 5280, section 6.1: the
566 * root of a certificate chain's only requirement is that it was used to issue
567 * the next certificate in the chain.
568 * Updates chainConstraints with the element's constraints, if:
569 * 1. chainConstraints doesn't have a path length constraint, or
570 * 2. element's path length constraint is smaller than chainConstraints's
571 * Sets *pathLengthConstraintViolated to TRUE if a path length violation
572 * occurs.
573 * Returns TRUE if the element can be a CA, and the length of the remaining
574 * chain is valid.
576 static BOOL CRYPT_CheckBasicConstraintsForCA(CertificateChainEngine *engine,
577 PCCERT_CONTEXT cert, CERT_BASIC_CONSTRAINTS2_INFO *chainConstraints,
578 DWORD remainingCAs, BOOL isRoot, BOOL *pathLengthConstraintViolated)
580 BOOL validBasicConstraints, implicitCA = FALSE;
581 CERT_BASIC_CONSTRAINTS2_INFO constraints;
583 if (isRoot)
584 implicitCA = TRUE;
585 else if (cert->pCertInfo->dwVersion == CERT_V1 ||
586 cert->pCertInfo->dwVersion == CERT_V2)
588 BYTE hash[20];
589 DWORD size = sizeof(hash);
591 if (CertGetCertificateContextProperty(cert, CERT_HASH_PROP_ID,
592 hash, &size))
594 CRYPT_HASH_BLOB blob = { sizeof(hash), hash };
595 PCCERT_CONTEXT localCert = CertFindCertificateInStore(
596 engine->hWorld, cert->dwCertEncodingType, 0, CERT_FIND_SHA1_HASH,
597 &blob, NULL);
599 if (localCert)
601 CertFreeCertificateContext(localCert);
602 implicitCA = TRUE;
606 if ((validBasicConstraints = CRYPT_DecodeBasicConstraints(cert,
607 &constraints, implicitCA)))
609 chainConstraints->fCA = constraints.fCA;
610 if (!constraints.fCA)
612 TRACE_(chain)("chain element %d can't be a CA\n", remainingCAs + 1);
613 validBasicConstraints = FALSE;
615 else if (constraints.fPathLenConstraint)
617 /* If the element has path length constraints, they apply to the
618 * entire remaining chain.
620 if (!chainConstraints->fPathLenConstraint ||
621 constraints.dwPathLenConstraint <
622 chainConstraints->dwPathLenConstraint)
624 TRACE_(chain)("setting path length constraint to %d\n",
625 chainConstraints->dwPathLenConstraint);
626 chainConstraints->fPathLenConstraint = TRUE;
627 chainConstraints->dwPathLenConstraint =
628 constraints.dwPathLenConstraint;
632 if (chainConstraints->fPathLenConstraint &&
633 remainingCAs > chainConstraints->dwPathLenConstraint)
635 TRACE_(chain)("remaining CAs %d exceed max path length %d\n",
636 remainingCAs, chainConstraints->dwPathLenConstraint);
637 validBasicConstraints = FALSE;
638 *pathLengthConstraintViolated = TRUE;
640 return validBasicConstraints;
643 static BOOL domain_name_matches(LPCWSTR constraint, LPCWSTR name)
645 BOOL match;
647 /* RFC 5280, section 4.2.1.10:
648 * "For URIs, the constraint applies to the host part of the name...
649 * When the constraint begins with a period, it MAY be expanded with one
650 * or more labels. That is, the constraint ".example.com" is satisfied by
651 * both host.example.com and my.host.example.com. However, the constraint
652 * ".example.com" is not satisfied by "example.com". When the constraint
653 * does not begin with a period, it specifies a host."
654 * and for email addresses,
655 * "To indicate all Internet mail addresses on a particular host, the
656 * constraint is specified as the host name. For example, the constraint
657 * "example.com" is satisfied by any mail address at the host
658 * "example.com". To specify any address within a domain, the constraint
659 * is specified with a leading period (as with URIs)."
661 if (constraint[0] == '.')
663 /* Must be strictly greater than, a name can't begin with '.' */
664 if (lstrlenW(name) > lstrlenW(constraint))
665 match = !lstrcmpiW(name + lstrlenW(name) - lstrlenW(constraint),
666 constraint);
667 else
669 /* name is too short, no match */
670 match = FALSE;
673 else
674 match = !lstrcmpiW(name, constraint);
675 return match;
678 static BOOL url_matches(LPCWSTR constraint, LPCWSTR name,
679 DWORD *trustErrorStatus)
681 BOOL match = FALSE;
683 TRACE("%s, %s\n", debugstr_w(constraint), debugstr_w(name));
685 if (!constraint)
686 *trustErrorStatus |= CERT_TRUST_INVALID_NAME_CONSTRAINTS;
687 else if (!name)
688 ; /* no match */
689 else
691 LPCWSTR colon, authority_end, at, hostname = NULL;
692 /* The maximum length for a hostname is 254 in the DNS, see RFC 1034 */
693 WCHAR hostname_buf[255];
695 /* RFC 5280: only the hostname portion of the URL is compared. From
696 * section 4.2.1.10:
697 * "For URIs, the constraint applies to the host part of the name.
698 * The constraint MUST be specified as a fully qualified domain name
699 * and MAY specify a host or a domain."
700 * The format for URIs is in RFC 2396.
702 * First, remove any scheme that's present. */
703 colon = wcschr(name, ':');
704 if (colon && *(colon + 1) == '/' && *(colon + 2) == '/')
705 name = colon + 3;
706 /* Next, find the end of the authority component. (The authority is
707 * generally just the hostname, but it may contain a username or a port.
708 * Those are removed next.)
710 authority_end = wcschr(name, '/');
711 if (!authority_end)
712 authority_end = wcschr(name, '?');
713 if (!authority_end)
714 authority_end = name + lstrlenW(name);
715 /* Remove any port number from the authority. The userinfo portion
716 * of an authority may contain a colon, so stop if a userinfo portion
717 * is found (indicated by '@').
719 for (colon = authority_end; colon >= name && *colon != ':' &&
720 *colon != '@'; colon--)
722 if (*colon == ':')
723 authority_end = colon;
724 /* Remove any username from the authority */
725 if ((at = wcschr(name, '@')))
726 name = at;
727 /* Ignore any path or query portion of the URL. */
728 if (*authority_end)
730 if (authority_end - name < ARRAY_SIZE(hostname_buf))
732 memcpy(hostname_buf, name,
733 (authority_end - name) * sizeof(WCHAR));
734 hostname_buf[authority_end - name] = 0;
735 hostname = hostname_buf;
737 /* else: Hostname is too long, not a match */
739 else
740 hostname = name;
741 if (hostname)
742 match = domain_name_matches(constraint, hostname);
744 return match;
747 static BOOL rfc822_name_matches(LPCWSTR constraint, LPCWSTR name,
748 DWORD *trustErrorStatus)
750 BOOL match = FALSE;
751 LPCWSTR at;
753 TRACE("%s, %s\n", debugstr_w(constraint), debugstr_w(name));
755 if (!constraint)
756 *trustErrorStatus |= CERT_TRUST_INVALID_NAME_CONSTRAINTS;
757 else if (!name)
758 ; /* no match */
759 else if (wcschr(constraint, '@'))
760 match = !lstrcmpiW(constraint, name);
761 else
763 if ((at = wcschr(name, '@')))
764 match = domain_name_matches(constraint, at + 1);
765 else
766 match = !lstrcmpiW(constraint, name);
768 return match;
771 static BOOL dns_name_matches(LPCWSTR constraint, LPCWSTR name,
772 DWORD *trustErrorStatus)
774 BOOL match = FALSE;
776 TRACE("%s, %s\n", debugstr_w(constraint), debugstr_w(name));
778 if (!constraint)
779 *trustErrorStatus |= CERT_TRUST_INVALID_NAME_CONSTRAINTS;
780 else if (!name)
781 ; /* no match */
782 /* RFC 5280, section 4.2.1.10:
783 * "DNS name restrictions are expressed as host.example.com. Any DNS name
784 * that can be constructed by simply adding zero or more labels to the
785 * left-hand side of the name satisfies the name constraint. For example,
786 * www.host.example.com would satisfy the constraint but host1.example.com
787 * would not."
789 else if (lstrlenW(name) == lstrlenW(constraint))
790 match = !lstrcmpiW(name, constraint);
791 else if (lstrlenW(name) > lstrlenW(constraint))
793 match = !lstrcmpiW(name + lstrlenW(name) - lstrlenW(constraint),
794 constraint);
795 if (match)
797 BOOL dot = FALSE;
798 LPCWSTR ptr;
800 /* This only matches if name is a subdomain of constraint, i.e.
801 * there's a '.' between the beginning of the name and the
802 * matching portion of the name.
804 for (ptr = name + lstrlenW(name) - lstrlenW(constraint);
805 !dot && ptr >= name; ptr--)
806 if (*ptr == '.')
807 dot = TRUE;
808 match = dot;
811 /* else: name is too short, no match */
813 return match;
816 static BOOL ip_address_matches(const CRYPT_DATA_BLOB *constraint,
817 const CRYPT_DATA_BLOB *name, DWORD *trustErrorStatus)
819 BOOL match = FALSE;
821 TRACE("(%d, %p), (%d, %p)\n", constraint->cbData, constraint->pbData,
822 name->cbData, name->pbData);
824 /* RFC5280, section 4.2.1.10, iPAddress syntax: either 8 or 32 bytes, for
825 * IPv4 or IPv6 addresses, respectively.
827 if (constraint->cbData != sizeof(DWORD) * 2 && constraint->cbData != 32)
828 *trustErrorStatus |= CERT_TRUST_INVALID_NAME_CONSTRAINTS;
829 else if (name->cbData == sizeof(DWORD) &&
830 constraint->cbData == sizeof(DWORD) * 2)
832 DWORD subnet, mask, addr;
834 memcpy(&subnet, constraint->pbData, sizeof(subnet));
835 memcpy(&mask, constraint->pbData + sizeof(subnet), sizeof(mask));
836 memcpy(&addr, name->pbData, sizeof(addr));
837 /* These are really in big-endian order, but for equality matching we
838 * don't need to swap to host order
840 match = (subnet & mask) == (addr & mask);
842 else if (name->cbData == 16 && constraint->cbData == 32)
844 const BYTE *subnet, *mask, *addr;
845 DWORD i;
847 subnet = constraint->pbData;
848 mask = constraint->pbData + 16;
849 addr = name->pbData;
850 match = TRUE;
851 for (i = 0; match && i < 16; i++)
852 if ((subnet[i] & mask[i]) != (addr[i] & mask[i]))
853 match = FALSE;
855 /* else: name is wrong size, no match */
857 return match;
860 static BOOL directory_name_matches(const CERT_NAME_BLOB *constraint,
861 const CERT_NAME_BLOB *name)
863 CERT_NAME_INFO *constraintName;
864 DWORD size;
865 BOOL match = FALSE;
867 if (CryptDecodeObjectEx(X509_ASN_ENCODING, X509_NAME, constraint->pbData,
868 constraint->cbData, CRYPT_DECODE_ALLOC_FLAG, NULL, &constraintName, &size))
870 DWORD i;
872 match = TRUE;
873 for (i = 0; match && i < constraintName->cRDN; i++)
874 match = CertIsRDNAttrsInCertificateName(X509_ASN_ENCODING,
875 CERT_CASE_INSENSITIVE_IS_RDN_ATTRS_FLAG,
876 (CERT_NAME_BLOB *)name, &constraintName->rgRDN[i]);
877 LocalFree(constraintName);
879 return match;
882 static BOOL alt_name_matches(const CERT_ALT_NAME_ENTRY *name,
883 const CERT_ALT_NAME_ENTRY *constraint, DWORD *trustErrorStatus, BOOL *present)
885 BOOL match = FALSE;
887 if (name->dwAltNameChoice == constraint->dwAltNameChoice)
889 if (present)
890 *present = TRUE;
891 switch (constraint->dwAltNameChoice)
893 case CERT_ALT_NAME_RFC822_NAME:
894 match = rfc822_name_matches(constraint->u.pwszURL,
895 name->u.pwszURL, trustErrorStatus);
896 break;
897 case CERT_ALT_NAME_DNS_NAME:
898 match = dns_name_matches(constraint->u.pwszURL,
899 name->u.pwszURL, trustErrorStatus);
900 break;
901 case CERT_ALT_NAME_URL:
902 match = url_matches(constraint->u.pwszURL,
903 name->u.pwszURL, trustErrorStatus);
904 break;
905 case CERT_ALT_NAME_IP_ADDRESS:
906 match = ip_address_matches(&constraint->u.IPAddress,
907 &name->u.IPAddress, trustErrorStatus);
908 break;
909 case CERT_ALT_NAME_DIRECTORY_NAME:
910 match = directory_name_matches(&constraint->u.DirectoryName,
911 &name->u.DirectoryName);
912 break;
913 default:
914 ERR("name choice %d unsupported in this context\n",
915 constraint->dwAltNameChoice);
916 *trustErrorStatus |=
917 CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT;
920 else if (present)
921 *present = FALSE;
922 return match;
925 static BOOL alt_name_matches_excluded_name(const CERT_ALT_NAME_ENTRY *name,
926 const CERT_NAME_CONSTRAINTS_INFO *nameConstraints, DWORD *trustErrorStatus)
928 DWORD i;
929 BOOL match = FALSE;
931 for (i = 0; !match && i < nameConstraints->cExcludedSubtree; i++)
932 match = alt_name_matches(name,
933 &nameConstraints->rgExcludedSubtree[i].Base, trustErrorStatus, NULL);
934 return match;
937 static BOOL alt_name_matches_permitted_name(const CERT_ALT_NAME_ENTRY *name,
938 const CERT_NAME_CONSTRAINTS_INFO *nameConstraints, DWORD *trustErrorStatus,
939 BOOL *present)
941 DWORD i;
942 BOOL match = FALSE;
944 for (i = 0; !match && i < nameConstraints->cPermittedSubtree; i++)
945 match = alt_name_matches(name,
946 &nameConstraints->rgPermittedSubtree[i].Base, trustErrorStatus,
947 present);
948 return match;
951 static inline PCERT_EXTENSION get_subject_alt_name_ext(const CERT_INFO *cert)
953 PCERT_EXTENSION ext;
955 ext = CertFindExtension(szOID_SUBJECT_ALT_NAME2,
956 cert->cExtension, cert->rgExtension);
957 if (!ext)
958 ext = CertFindExtension(szOID_SUBJECT_ALT_NAME,
959 cert->cExtension, cert->rgExtension);
960 return ext;
963 static void compare_alt_name_with_constraints(const CERT_EXTENSION *altNameExt,
964 const CERT_NAME_CONSTRAINTS_INFO *nameConstraints, DWORD *trustErrorStatus)
966 CERT_ALT_NAME_INFO *subjectAltName;
967 DWORD size;
969 if (CryptDecodeObjectEx(X509_ASN_ENCODING, X509_ALTERNATE_NAME,
970 altNameExt->Value.pbData, altNameExt->Value.cbData,
971 CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL,
972 &subjectAltName, &size))
974 DWORD i;
976 for (i = 0; i < subjectAltName->cAltEntry; i++)
978 BOOL nameFormPresent;
980 /* A name constraint only applies if the name form is present.
981 * From RFC 5280, section 4.2.1.10:
982 * "Restrictions apply only when the specified name form is
983 * present. If no name of the type is in the certificate,
984 * the certificate is acceptable."
986 if (alt_name_matches_excluded_name(
987 &subjectAltName->rgAltEntry[i], nameConstraints,
988 trustErrorStatus))
990 TRACE_(chain)("subject alternate name form %d excluded\n",
991 subjectAltName->rgAltEntry[i].dwAltNameChoice);
992 *trustErrorStatus |=
993 CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT;
995 nameFormPresent = FALSE;
996 if (!alt_name_matches_permitted_name(
997 &subjectAltName->rgAltEntry[i], nameConstraints,
998 trustErrorStatus, &nameFormPresent) && nameFormPresent)
1000 TRACE_(chain)("subject alternate name form %d not permitted\n",
1001 subjectAltName->rgAltEntry[i].dwAltNameChoice);
1002 *trustErrorStatus |=
1003 CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT;
1006 LocalFree(subjectAltName);
1008 else
1009 *trustErrorStatus |=
1010 CERT_TRUST_INVALID_EXTENSION | CERT_TRUST_INVALID_NAME_CONSTRAINTS;
1013 static BOOL rfc822_attr_matches_excluded_name(const CERT_RDN_ATTR *attr,
1014 const CERT_NAME_CONSTRAINTS_INFO *nameConstraints, DWORD *trustErrorStatus)
1016 DWORD i;
1017 BOOL match = FALSE;
1019 for (i = 0; !match && i < nameConstraints->cExcludedSubtree; i++)
1021 const CERT_ALT_NAME_ENTRY *constraint =
1022 &nameConstraints->rgExcludedSubtree[i].Base;
1024 if (constraint->dwAltNameChoice == CERT_ALT_NAME_RFC822_NAME)
1025 match = rfc822_name_matches(constraint->u.pwszRfc822Name,
1026 (LPCWSTR)attr->Value.pbData, trustErrorStatus);
1028 return match;
1031 static BOOL rfc822_attr_matches_permitted_name(const CERT_RDN_ATTR *attr,
1032 const CERT_NAME_CONSTRAINTS_INFO *nameConstraints, DWORD *trustErrorStatus,
1033 BOOL *present)
1035 DWORD i;
1036 BOOL match = FALSE;
1038 for (i = 0; !match && i < nameConstraints->cPermittedSubtree; i++)
1040 const CERT_ALT_NAME_ENTRY *constraint =
1041 &nameConstraints->rgPermittedSubtree[i].Base;
1043 if (constraint->dwAltNameChoice == CERT_ALT_NAME_RFC822_NAME)
1045 *present = TRUE;
1046 match = rfc822_name_matches(constraint->u.pwszRfc822Name,
1047 (LPCWSTR)attr->Value.pbData, trustErrorStatus);
1050 return match;
1053 static void compare_subject_with_email_constraints(
1054 const CERT_NAME_BLOB *subjectName,
1055 const CERT_NAME_CONSTRAINTS_INFO *nameConstraints, DWORD *trustErrorStatus)
1057 CERT_NAME_INFO *name;
1058 DWORD size;
1060 if (CryptDecodeObjectEx(X509_ASN_ENCODING, X509_UNICODE_NAME,
1061 subjectName->pbData, subjectName->cbData,
1062 CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL, &name, &size))
1064 DWORD i, j;
1066 for (i = 0; i < name->cRDN; i++)
1067 for (j = 0; j < name->rgRDN[i].cRDNAttr; j++)
1068 if (!strcmp(name->rgRDN[i].rgRDNAttr[j].pszObjId,
1069 szOID_RSA_emailAddr))
1071 BOOL nameFormPresent;
1073 /* A name constraint only applies if the name form is
1074 * present. From RFC 5280, section 4.2.1.10:
1075 * "Restrictions apply only when the specified name form is
1076 * present. If no name of the type is in the certificate,
1077 * the certificate is acceptable."
1079 if (rfc822_attr_matches_excluded_name(
1080 &name->rgRDN[i].rgRDNAttr[j], nameConstraints,
1081 trustErrorStatus))
1083 TRACE_(chain)(
1084 "email address in subject name is excluded\n");
1085 *trustErrorStatus |=
1086 CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT;
1088 nameFormPresent = FALSE;
1089 if (!rfc822_attr_matches_permitted_name(
1090 &name->rgRDN[i].rgRDNAttr[j], nameConstraints,
1091 trustErrorStatus, &nameFormPresent) && nameFormPresent)
1093 TRACE_(chain)(
1094 "email address in subject name is not permitted\n");
1095 *trustErrorStatus |=
1096 CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT;
1099 LocalFree(name);
1101 else
1102 *trustErrorStatus |=
1103 CERT_TRUST_INVALID_EXTENSION | CERT_TRUST_INVALID_NAME_CONSTRAINTS;
1106 static BOOL CRYPT_IsEmptyName(const CERT_NAME_BLOB *name)
1108 BOOL empty;
1110 if (!name->cbData)
1111 empty = TRUE;
1112 else if (name->cbData == 2 && name->pbData[1] == 0)
1114 /* An empty sequence is also empty */
1115 empty = TRUE;
1117 else
1118 empty = FALSE;
1119 return empty;
1122 static void compare_subject_with_constraints(const CERT_NAME_BLOB *subjectName,
1123 const CERT_NAME_CONSTRAINTS_INFO *nameConstraints, DWORD *trustErrorStatus)
1125 BOOL hasEmailConstraint = FALSE;
1126 DWORD i;
1128 /* In general, a subject distinguished name only matches a directory name
1129 * constraint. However, an exception exists for email addresses.
1130 * From RFC 5280, section 4.2.1.6:
1131 * "Legacy implementations exist where an electronic mail address is
1132 * embedded in the subject distinguished name as an emailAddress
1133 * attribute [RFC2985]."
1134 * If an email address constraint exists, check that constraint separately.
1136 for (i = 0; !hasEmailConstraint && i < nameConstraints->cExcludedSubtree;
1137 i++)
1138 if (nameConstraints->rgExcludedSubtree[i].Base.dwAltNameChoice ==
1139 CERT_ALT_NAME_RFC822_NAME)
1140 hasEmailConstraint = TRUE;
1141 for (i = 0; !hasEmailConstraint && i < nameConstraints->cPermittedSubtree;
1142 i++)
1143 if (nameConstraints->rgPermittedSubtree[i].Base.dwAltNameChoice ==
1144 CERT_ALT_NAME_RFC822_NAME)
1145 hasEmailConstraint = TRUE;
1146 if (hasEmailConstraint)
1147 compare_subject_with_email_constraints(subjectName, nameConstraints,
1148 trustErrorStatus);
1149 for (i = 0; i < nameConstraints->cExcludedSubtree; i++)
1151 CERT_ALT_NAME_ENTRY *constraint =
1152 &nameConstraints->rgExcludedSubtree[i].Base;
1154 if (constraint->dwAltNameChoice == CERT_ALT_NAME_DIRECTORY_NAME &&
1155 directory_name_matches(&constraint->u.DirectoryName, subjectName))
1157 TRACE_(chain)("subject name is excluded\n");
1158 *trustErrorStatus |=
1159 CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT;
1162 /* RFC 5280, section 4.2.1.10:
1163 * "Restrictions apply only when the specified name form is present.
1164 * If no name of the type is in the certificate, the certificate is
1165 * acceptable."
1166 * An empty name can't have the name form present, so don't check it.
1168 if (nameConstraints->cPermittedSubtree && !CRYPT_IsEmptyName(subjectName))
1170 BOOL match = FALSE, hasDirectoryConstraint = FALSE;
1172 for (i = 0; !match && i < nameConstraints->cPermittedSubtree; i++)
1174 CERT_ALT_NAME_ENTRY *constraint =
1175 &nameConstraints->rgPermittedSubtree[i].Base;
1177 if (constraint->dwAltNameChoice == CERT_ALT_NAME_DIRECTORY_NAME)
1179 hasDirectoryConstraint = TRUE;
1180 match = directory_name_matches(&constraint->u.DirectoryName,
1181 subjectName);
1184 if (hasDirectoryConstraint && !match)
1186 TRACE_(chain)("subject name is not permitted\n");
1187 *trustErrorStatus |= CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT;
1192 static void CRYPT_CheckNameConstraints(
1193 const CERT_NAME_CONSTRAINTS_INFO *nameConstraints, const CERT_INFO *cert,
1194 DWORD *trustErrorStatus)
1196 CERT_EXTENSION *ext = get_subject_alt_name_ext(cert);
1198 if (ext)
1199 compare_alt_name_with_constraints(ext, nameConstraints,
1200 trustErrorStatus);
1201 /* Name constraints apply to the subject alternative name as well as the
1202 * subject name. From RFC 5280, section 4.2.1.10:
1203 * "Restrictions apply to the subject distinguished name and apply to
1204 * subject alternative names."
1206 compare_subject_with_constraints(&cert->Subject, nameConstraints,
1207 trustErrorStatus);
1210 /* Gets cert's name constraints, if any. Free with LocalFree. */
1211 static CERT_NAME_CONSTRAINTS_INFO *CRYPT_GetNameConstraints(CERT_INFO *cert)
1213 CERT_NAME_CONSTRAINTS_INFO *info = NULL;
1215 CERT_EXTENSION *ext;
1217 if ((ext = CertFindExtension(szOID_NAME_CONSTRAINTS, cert->cExtension,
1218 cert->rgExtension)))
1220 DWORD size;
1222 CryptDecodeObjectEx(X509_ASN_ENCODING, X509_NAME_CONSTRAINTS,
1223 ext->Value.pbData, ext->Value.cbData,
1224 CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL, &info,
1225 &size);
1227 return info;
1230 static BOOL CRYPT_IsValidNameConstraint(const CERT_NAME_CONSTRAINTS_INFO *info)
1232 DWORD i;
1233 BOOL ret = TRUE;
1235 /* Make sure at least one permitted or excluded subtree is present. From
1236 * RFC 5280, section 4.2.1.10:
1237 * "Conforming CAs MUST NOT issue certificates where name constraints is an
1238 * empty sequence. That is, either the permittedSubtrees field or the
1239 * excludedSubtrees MUST be present."
1241 if (!info->cPermittedSubtree && !info->cExcludedSubtree)
1243 WARN_(chain)("constraints contain no permitted nor excluded subtree\n");
1244 ret = FALSE;
1246 /* Check that none of the constraints specifies a minimum or a maximum.
1247 * See RFC 5280, section 4.2.1.10:
1248 * "Within this profile, the minimum and maximum fields are not used with
1249 * any name forms, thus, the minimum MUST be zero, and maximum MUST be
1250 * absent. However, if an application encounters a critical name
1251 * constraints extension that specifies other values for minimum or
1252 * maximum for a name form that appears in a subsequent certificate, the
1253 * application MUST either process these fields or reject the
1254 * certificate."
1255 * Since it gives no guidance as to how to process these fields, we
1256 * reject any name constraint that contains them.
1258 for (i = 0; ret && i < info->cPermittedSubtree; i++)
1259 if (info->rgPermittedSubtree[i].dwMinimum ||
1260 info->rgPermittedSubtree[i].fMaximum)
1262 TRACE_(chain)("found a minimum or maximum in permitted subtrees\n");
1263 ret = FALSE;
1265 for (i = 0; ret && i < info->cExcludedSubtree; i++)
1266 if (info->rgExcludedSubtree[i].dwMinimum ||
1267 info->rgExcludedSubtree[i].fMaximum)
1269 TRACE_(chain)("found a minimum or maximum in excluded subtrees\n");
1270 ret = FALSE;
1272 return ret;
1275 static void CRYPT_CheckChainNameConstraints(PCERT_SIMPLE_CHAIN chain)
1277 int i, j;
1279 /* Microsoft's implementation appears to violate RFC 3280: according to
1280 * MSDN, the various CERT_TRUST_*_NAME_CONSTRAINT errors are set if a CA's
1281 * name constraint is violated in the end cert. According to RFC 3280,
1282 * the constraints should be checked against every subsequent certificate
1283 * in the chain, not just the end cert.
1284 * Microsoft's implementation also sets the name constraint errors on the
1285 * certs whose constraints were violated, not on the certs that violated
1286 * them.
1287 * In order to be error-compatible with Microsoft's implementation, while
1288 * still adhering to RFC 3280, I use a O(n ^ 2) algorithm to check name
1289 * constraints.
1291 for (i = chain->cElement - 1; i > 0; i--)
1293 CERT_NAME_CONSTRAINTS_INFO *nameConstraints;
1295 if ((nameConstraints = CRYPT_GetNameConstraints(
1296 chain->rgpElement[i]->pCertContext->pCertInfo)))
1298 if (!CRYPT_IsValidNameConstraint(nameConstraints))
1299 chain->rgpElement[i]->TrustStatus.dwErrorStatus |=
1300 CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT;
1301 else
1303 for (j = i - 1; j >= 0; j--)
1305 DWORD errorStatus = 0;
1307 /* According to RFC 3280, self-signed certs don't have name
1308 * constraints checked unless they're the end cert.
1310 if (j == 0 || !CRYPT_IsCertificateSelfSigned(
1311 chain->rgpElement[j]->pCertContext))
1313 CRYPT_CheckNameConstraints(nameConstraints,
1314 chain->rgpElement[j]->pCertContext->pCertInfo,
1315 &errorStatus);
1316 if (errorStatus)
1318 chain->rgpElement[i]->TrustStatus.dwErrorStatus |=
1319 errorStatus;
1320 CRYPT_CombineTrustStatus(&chain->TrustStatus,
1321 &chain->rgpElement[i]->TrustStatus);
1323 else
1324 chain->rgpElement[i]->TrustStatus.dwInfoStatus |=
1325 CERT_TRUST_HAS_VALID_NAME_CONSTRAINTS;
1329 LocalFree(nameConstraints);
1334 /* Gets cert's policies info, if any. Free with LocalFree. */
1335 static CERT_POLICIES_INFO *CRYPT_GetPolicies(PCCERT_CONTEXT cert)
1337 PCERT_EXTENSION ext;
1338 CERT_POLICIES_INFO *policies = NULL;
1340 ext = CertFindExtension(szOID_KEY_USAGE, cert->pCertInfo->cExtension,
1341 cert->pCertInfo->rgExtension);
1342 if (ext)
1344 DWORD size;
1346 CryptDecodeObjectEx(X509_ASN_ENCODING, X509_CERT_POLICIES,
1347 ext->Value.pbData, ext->Value.cbData, CRYPT_DECODE_ALLOC_FLAG, NULL,
1348 &policies, &size);
1350 return policies;
1353 static void CRYPT_CheckPolicies(const CERT_POLICIES_INFO *policies, CERT_INFO *cert,
1354 DWORD *errorStatus)
1356 DWORD i;
1358 for (i = 0; i < policies->cPolicyInfo; i++)
1360 /* For now, the only accepted policy identifier is the anyPolicy
1361 * identifier.
1362 * FIXME: the policy identifiers should be compared against the
1363 * cert's certificate policies extension, subject to the policy
1364 * mappings extension, and the policy constraints extension.
1365 * See RFC 5280, sections 4.2.1.4, 4.2.1.5, and 4.2.1.11.
1367 if (strcmp(policies->rgPolicyInfo[i].pszPolicyIdentifier,
1368 szOID_ANY_CERT_POLICY))
1370 FIXME("unsupported policy %s\n",
1371 policies->rgPolicyInfo[i].pszPolicyIdentifier);
1372 *errorStatus |= CERT_TRUST_INVALID_POLICY_CONSTRAINTS;
1377 static void CRYPT_CheckChainPolicies(PCERT_SIMPLE_CHAIN chain)
1379 int i, j;
1381 for (i = chain->cElement - 1; i > 0; i--)
1383 CERT_POLICIES_INFO *policies;
1385 if ((policies = CRYPT_GetPolicies(chain->rgpElement[i]->pCertContext)))
1387 for (j = i - 1; j >= 0; j--)
1389 DWORD errorStatus = 0;
1391 CRYPT_CheckPolicies(policies,
1392 chain->rgpElement[j]->pCertContext->pCertInfo, &errorStatus);
1393 if (errorStatus)
1395 chain->rgpElement[i]->TrustStatus.dwErrorStatus |=
1396 errorStatus;
1397 CRYPT_CombineTrustStatus(&chain->TrustStatus,
1398 &chain->rgpElement[i]->TrustStatus);
1401 LocalFree(policies);
1406 static LPWSTR name_value_to_str(const CERT_NAME_BLOB *name)
1408 DWORD len = cert_name_to_str_with_indent(X509_ASN_ENCODING, 0, name,
1409 CERT_SIMPLE_NAME_STR, NULL, 0);
1410 LPWSTR str = NULL;
1412 if (len)
1414 str = CryptMemAlloc(len * sizeof(WCHAR));
1415 if (str)
1416 cert_name_to_str_with_indent(X509_ASN_ENCODING, 0, name,
1417 CERT_SIMPLE_NAME_STR, str, len);
1419 return str;
1422 static void dump_alt_name_entry(const CERT_ALT_NAME_ENTRY *entry)
1424 LPWSTR str;
1426 switch (entry->dwAltNameChoice)
1428 case CERT_ALT_NAME_OTHER_NAME:
1429 TRACE_(chain)("CERT_ALT_NAME_OTHER_NAME, oid = %s\n",
1430 debugstr_a(entry->u.pOtherName->pszObjId));
1431 break;
1432 case CERT_ALT_NAME_RFC822_NAME:
1433 TRACE_(chain)("CERT_ALT_NAME_RFC822_NAME: %s\n",
1434 debugstr_w(entry->u.pwszRfc822Name));
1435 break;
1436 case CERT_ALT_NAME_DNS_NAME:
1437 TRACE_(chain)("CERT_ALT_NAME_DNS_NAME: %s\n",
1438 debugstr_w(entry->u.pwszDNSName));
1439 break;
1440 case CERT_ALT_NAME_DIRECTORY_NAME:
1441 str = name_value_to_str(&entry->u.DirectoryName);
1442 TRACE_(chain)("CERT_ALT_NAME_DIRECTORY_NAME: %s\n", debugstr_w(str));
1443 CryptMemFree(str);
1444 break;
1445 case CERT_ALT_NAME_URL:
1446 TRACE_(chain)("CERT_ALT_NAME_URL: %s\n", debugstr_w(entry->u.pwszURL));
1447 break;
1448 case CERT_ALT_NAME_IP_ADDRESS:
1449 TRACE_(chain)("CERT_ALT_NAME_IP_ADDRESS: %d bytes\n",
1450 entry->u.IPAddress.cbData);
1451 break;
1452 case CERT_ALT_NAME_REGISTERED_ID:
1453 TRACE_(chain)("CERT_ALT_NAME_REGISTERED_ID: %s\n",
1454 debugstr_a(entry->u.pszRegisteredID));
1455 break;
1456 default:
1457 TRACE_(chain)("dwAltNameChoice = %d\n", entry->dwAltNameChoice);
1461 static void dump_alt_name(LPCSTR type, const CERT_EXTENSION *ext)
1463 CERT_ALT_NAME_INFO *name;
1464 DWORD size;
1466 TRACE_(chain)("%s:\n", type);
1467 if (CryptDecodeObjectEx(X509_ASN_ENCODING, X509_ALTERNATE_NAME,
1468 ext->Value.pbData, ext->Value.cbData,
1469 CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL, &name, &size))
1471 DWORD i;
1473 TRACE_(chain)("%d alt name entries:\n", name->cAltEntry);
1474 for (i = 0; i < name->cAltEntry; i++)
1475 dump_alt_name_entry(&name->rgAltEntry[i]);
1476 LocalFree(name);
1480 static void dump_basic_constraints(const CERT_EXTENSION *ext)
1482 CERT_BASIC_CONSTRAINTS_INFO *info;
1483 DWORD size = 0;
1485 if (CryptDecodeObjectEx(X509_ASN_ENCODING, szOID_BASIC_CONSTRAINTS,
1486 ext->Value.pbData, ext->Value.cbData, CRYPT_DECODE_ALLOC_FLAG,
1487 NULL, &info, &size))
1489 TRACE_(chain)("SubjectType: %02x\n", info->SubjectType.pbData[0]);
1490 TRACE_(chain)("%s path length constraint\n",
1491 info->fPathLenConstraint ? "has" : "doesn't have");
1492 TRACE_(chain)("path length=%d\n", info->dwPathLenConstraint);
1493 LocalFree(info);
1497 static void dump_basic_constraints2(const CERT_EXTENSION *ext)
1499 CERT_BASIC_CONSTRAINTS2_INFO constraints;
1500 DWORD size = sizeof(CERT_BASIC_CONSTRAINTS2_INFO);
1502 if (CryptDecodeObjectEx(X509_ASN_ENCODING,
1503 szOID_BASIC_CONSTRAINTS2, ext->Value.pbData, ext->Value.cbData,
1504 0, NULL, &constraints, &size))
1506 TRACE_(chain)("basic constraints:\n");
1507 TRACE_(chain)("can%s be a CA\n", constraints.fCA ? "" : "not");
1508 TRACE_(chain)("%s path length constraint\n",
1509 constraints.fPathLenConstraint ? "has" : "doesn't have");
1510 TRACE_(chain)("path length=%d\n", constraints.dwPathLenConstraint);
1514 static void dump_key_usage(const CERT_EXTENSION *ext)
1516 CRYPT_BIT_BLOB usage;
1517 DWORD size = sizeof(usage);
1519 if (CryptDecodeObjectEx(X509_ASN_ENCODING, X509_BITS, ext->Value.pbData,
1520 ext->Value.cbData, CRYPT_DECODE_NOCOPY_FLAG, NULL, &usage, &size))
1522 #define trace_usage_bit(bits, bit) \
1523 if ((bits) & (bit)) TRACE_(chain)("%s\n", #bit)
1524 if (usage.cbData)
1526 trace_usage_bit(usage.pbData[0], CERT_DIGITAL_SIGNATURE_KEY_USAGE);
1527 trace_usage_bit(usage.pbData[0], CERT_NON_REPUDIATION_KEY_USAGE);
1528 trace_usage_bit(usage.pbData[0], CERT_KEY_ENCIPHERMENT_KEY_USAGE);
1529 trace_usage_bit(usage.pbData[0], CERT_DATA_ENCIPHERMENT_KEY_USAGE);
1530 trace_usage_bit(usage.pbData[0], CERT_KEY_AGREEMENT_KEY_USAGE);
1531 trace_usage_bit(usage.pbData[0], CERT_KEY_CERT_SIGN_KEY_USAGE);
1532 trace_usage_bit(usage.pbData[0], CERT_CRL_SIGN_KEY_USAGE);
1533 trace_usage_bit(usage.pbData[0], CERT_ENCIPHER_ONLY_KEY_USAGE);
1535 #undef trace_usage_bit
1536 if (usage.cbData > 1 && usage.pbData[1] & CERT_DECIPHER_ONLY_KEY_USAGE)
1537 TRACE_(chain)("CERT_DECIPHER_ONLY_KEY_USAGE\n");
1541 static void dump_general_subtree(const CERT_GENERAL_SUBTREE *subtree)
1543 dump_alt_name_entry(&subtree->Base);
1544 TRACE_(chain)("dwMinimum = %d, fMaximum = %d, dwMaximum = %d\n",
1545 subtree->dwMinimum, subtree->fMaximum, subtree->dwMaximum);
1548 static void dump_name_constraints(const CERT_EXTENSION *ext)
1550 CERT_NAME_CONSTRAINTS_INFO *nameConstraints;
1551 DWORD size;
1553 if (CryptDecodeObjectEx(X509_ASN_ENCODING, X509_NAME_CONSTRAINTS,
1554 ext->Value.pbData, ext->Value.cbData,
1555 CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL, &nameConstraints,
1556 &size))
1558 DWORD i;
1560 TRACE_(chain)("%d permitted subtrees:\n",
1561 nameConstraints->cPermittedSubtree);
1562 for (i = 0; i < nameConstraints->cPermittedSubtree; i++)
1563 dump_general_subtree(&nameConstraints->rgPermittedSubtree[i]);
1564 TRACE_(chain)("%d excluded subtrees:\n",
1565 nameConstraints->cExcludedSubtree);
1566 for (i = 0; i < nameConstraints->cExcludedSubtree; i++)
1567 dump_general_subtree(&nameConstraints->rgExcludedSubtree[i]);
1568 LocalFree(nameConstraints);
1572 static void dump_cert_policies(const CERT_EXTENSION *ext)
1574 CERT_POLICIES_INFO *policies;
1575 DWORD size;
1577 if (CryptDecodeObjectEx(X509_ASN_ENCODING, X509_CERT_POLICIES,
1578 ext->Value.pbData, ext->Value.cbData, CRYPT_DECODE_ALLOC_FLAG, NULL,
1579 &policies, &size))
1581 DWORD i, j;
1583 TRACE_(chain)("%d policies:\n", policies->cPolicyInfo);
1584 for (i = 0; i < policies->cPolicyInfo; i++)
1586 TRACE_(chain)("policy identifier: %s\n",
1587 debugstr_a(policies->rgPolicyInfo[i].pszPolicyIdentifier));
1588 TRACE_(chain)("%d policy qualifiers:\n",
1589 policies->rgPolicyInfo[i].cPolicyQualifier);
1590 for (j = 0; j < policies->rgPolicyInfo[i].cPolicyQualifier; j++)
1591 TRACE_(chain)("%s\n", debugstr_a(
1592 policies->rgPolicyInfo[i].rgPolicyQualifier[j].
1593 pszPolicyQualifierId));
1595 LocalFree(policies);
1599 static void dump_enhanced_key_usage(const CERT_EXTENSION *ext)
1601 CERT_ENHKEY_USAGE *usage;
1602 DWORD size;
1604 if (CryptDecodeObjectEx(X509_ASN_ENCODING, X509_ENHANCED_KEY_USAGE,
1605 ext->Value.pbData, ext->Value.cbData, CRYPT_DECODE_ALLOC_FLAG, NULL,
1606 &usage, &size))
1608 DWORD i;
1610 TRACE_(chain)("%d usages:\n", usage->cUsageIdentifier);
1611 for (i = 0; i < usage->cUsageIdentifier; i++)
1612 TRACE_(chain)("%s\n", usage->rgpszUsageIdentifier[i]);
1613 LocalFree(usage);
1617 static void dump_netscape_cert_type(const CERT_EXTENSION *ext)
1619 CRYPT_BIT_BLOB usage;
1620 DWORD size = sizeof(usage);
1622 if (CryptDecodeObjectEx(X509_ASN_ENCODING, X509_BITS, ext->Value.pbData,
1623 ext->Value.cbData, CRYPT_DECODE_NOCOPY_FLAG, NULL, &usage, &size))
1625 #define trace_cert_type_bit(bits, bit) \
1626 if ((bits) & (bit)) TRACE_(chain)("%s\n", #bit)
1627 if (usage.cbData)
1629 trace_cert_type_bit(usage.pbData[0],
1630 NETSCAPE_SSL_CLIENT_AUTH_CERT_TYPE);
1631 trace_cert_type_bit(usage.pbData[0],
1632 NETSCAPE_SSL_SERVER_AUTH_CERT_TYPE);
1633 trace_cert_type_bit(usage.pbData[0], NETSCAPE_SMIME_CERT_TYPE);
1634 trace_cert_type_bit(usage.pbData[0], NETSCAPE_SIGN_CERT_TYPE);
1635 trace_cert_type_bit(usage.pbData[0], NETSCAPE_SSL_CA_CERT_TYPE);
1636 trace_cert_type_bit(usage.pbData[0], NETSCAPE_SMIME_CA_CERT_TYPE);
1637 trace_cert_type_bit(usage.pbData[0], NETSCAPE_SIGN_CA_CERT_TYPE);
1639 #undef trace_cert_type_bit
1643 static void dump_extension(const CERT_EXTENSION *ext)
1645 TRACE_(chain)("%s (%scritical)\n", debugstr_a(ext->pszObjId),
1646 ext->fCritical ? "" : "not ");
1647 if (!strcmp(ext->pszObjId, szOID_SUBJECT_ALT_NAME))
1648 dump_alt_name("subject alt name", ext);
1649 else if (!strcmp(ext->pszObjId, szOID_ISSUER_ALT_NAME))
1650 dump_alt_name("issuer alt name", ext);
1651 else if (!strcmp(ext->pszObjId, szOID_BASIC_CONSTRAINTS))
1652 dump_basic_constraints(ext);
1653 else if (!strcmp(ext->pszObjId, szOID_KEY_USAGE))
1654 dump_key_usage(ext);
1655 else if (!strcmp(ext->pszObjId, szOID_SUBJECT_ALT_NAME2))
1656 dump_alt_name("subject alt name 2", ext);
1657 else if (!strcmp(ext->pszObjId, szOID_ISSUER_ALT_NAME2))
1658 dump_alt_name("issuer alt name 2", ext);
1659 else if (!strcmp(ext->pszObjId, szOID_BASIC_CONSTRAINTS2))
1660 dump_basic_constraints2(ext);
1661 else if (!strcmp(ext->pszObjId, szOID_NAME_CONSTRAINTS))
1662 dump_name_constraints(ext);
1663 else if (!strcmp(ext->pszObjId, szOID_CERT_POLICIES))
1664 dump_cert_policies(ext);
1665 else if (!strcmp(ext->pszObjId, szOID_ENHANCED_KEY_USAGE))
1666 dump_enhanced_key_usage(ext);
1667 else if (!strcmp(ext->pszObjId, szOID_NETSCAPE_CERT_TYPE))
1668 dump_netscape_cert_type(ext);
1671 static LPCSTR filetime_to_str(const FILETIME *time)
1673 char date[80];
1674 char dateFmt[80]; /* sufficient for all versions of LOCALE_SSHORTDATE */
1675 SYSTEMTIME sysTime;
1677 if (!time) return "(null)";
1679 GetLocaleInfoA(LOCALE_SYSTEM_DEFAULT, LOCALE_SSHORTDATE, dateFmt, ARRAY_SIZE(dateFmt));
1680 FileTimeToSystemTime(time, &sysTime);
1681 GetDateFormatA(LOCALE_SYSTEM_DEFAULT, 0, &sysTime, dateFmt, date, ARRAY_SIZE(date));
1682 return wine_dbg_sprintf("%s", date);
1685 static void dump_element(PCCERT_CONTEXT cert)
1687 LPWSTR name = NULL;
1688 DWORD len, i;
1690 TRACE_(chain)("%p: version %d\n", cert, cert->pCertInfo->dwVersion);
1691 len = CertGetNameStringW(cert, CERT_NAME_SIMPLE_DISPLAY_TYPE,
1692 CERT_NAME_ISSUER_FLAG, NULL, NULL, 0);
1693 name = CryptMemAlloc(len * sizeof(WCHAR));
1694 if (name)
1696 CertGetNameStringW(cert, CERT_NAME_SIMPLE_DISPLAY_TYPE,
1697 CERT_NAME_ISSUER_FLAG, NULL, name, len);
1698 TRACE_(chain)("issued by %s\n", debugstr_w(name));
1699 CryptMemFree(name);
1701 len = CertGetNameStringW(cert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL,
1702 NULL, 0);
1703 name = CryptMemAlloc(len * sizeof(WCHAR));
1704 if (name)
1706 CertGetNameStringW(cert, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL,
1707 name, len);
1708 TRACE_(chain)("issued to %s\n", debugstr_w(name));
1709 CryptMemFree(name);
1711 TRACE_(chain)("valid from %s to %s\n",
1712 filetime_to_str(&cert->pCertInfo->NotBefore),
1713 filetime_to_str(&cert->pCertInfo->NotAfter));
1714 TRACE_(chain)("%d extensions\n", cert->pCertInfo->cExtension);
1715 for (i = 0; i < cert->pCertInfo->cExtension; i++)
1716 dump_extension(&cert->pCertInfo->rgExtension[i]);
1719 static BOOL CRYPT_KeyUsageValid(CertificateChainEngine *engine,
1720 PCCERT_CONTEXT cert, BOOL isRoot, BOOL isCA, DWORD index)
1722 PCERT_EXTENSION ext;
1723 BOOL ret;
1724 BYTE usageBits = 0;
1726 ext = CertFindExtension(szOID_KEY_USAGE, cert->pCertInfo->cExtension,
1727 cert->pCertInfo->rgExtension);
1728 if (ext)
1730 CRYPT_BIT_BLOB usage;
1731 DWORD size = sizeof(usage);
1733 ret = CryptDecodeObjectEx(cert->dwCertEncodingType, X509_BITS,
1734 ext->Value.pbData, ext->Value.cbData, CRYPT_DECODE_NOCOPY_FLAG, NULL,
1735 &usage, &size);
1736 if (!ret)
1737 return FALSE;
1738 else if (usage.cbData > 2)
1740 /* The key usage extension only defines 9 bits => no more than 2
1741 * bytes are needed to encode all known usages.
1743 return FALSE;
1745 else
1747 /* The only bit relevant to chain validation is the keyCertSign
1748 * bit, which is always in the least significant byte of the
1749 * key usage bits.
1751 usageBits = usage.pbData[usage.cbData - 1];
1754 if (isCA)
1756 if (!ext)
1758 /* MS appears to violate RFC 5280, section 4.2.1.3 (Key Usage)
1759 * here. Quoting the RFC:
1760 * "This [key usage] extension MUST appear in certificates that
1761 * contain public keys that are used to validate digital signatures
1762 * on other public key certificates or CRLs."
1763 * MS appears to accept certs that do not contain key usage
1764 * extensions as CA certs. V1 and V2 certificates did not have
1765 * extensions, and many root certificates are V1 certificates, so
1766 * perhaps this is prudent. On the other hand, MS also accepts V3
1767 * certs without key usage extensions. Because some CAs, e.g.
1768 * Certum, also do not include key usage extensions in their
1769 * intermediate certificates, we are forced to accept V3
1770 * certificates without key usage extensions as well.
1772 ret = TRUE;
1774 else
1776 if (!(usageBits & CERT_KEY_CERT_SIGN_KEY_USAGE))
1778 WARN_(chain)("keyCertSign not asserted on a CA cert\n");
1779 ret = FALSE;
1781 else
1782 ret = TRUE;
1785 else
1787 if (ext && (usageBits & CERT_KEY_CERT_SIGN_KEY_USAGE))
1789 WARN_(chain)("keyCertSign asserted on a non-CA cert\n");
1790 ret = FALSE;
1792 else
1793 ret = TRUE;
1795 return ret;
1798 static BOOL CRYPT_CriticalExtensionsSupported(PCCERT_CONTEXT cert)
1800 BOOL ret = TRUE;
1801 DWORD i;
1803 for (i = 0; ret && i < cert->pCertInfo->cExtension; i++)
1805 if (cert->pCertInfo->rgExtension[i].fCritical)
1807 LPCSTR oid = cert->pCertInfo->rgExtension[i].pszObjId;
1809 if (!strcmp(oid, szOID_BASIC_CONSTRAINTS))
1810 ret = TRUE;
1811 else if (!strcmp(oid, szOID_BASIC_CONSTRAINTS2))
1812 ret = TRUE;
1813 else if (!strcmp(oid, szOID_NAME_CONSTRAINTS))
1814 ret = TRUE;
1815 else if (!strcmp(oid, szOID_KEY_USAGE))
1816 ret = TRUE;
1817 else if (!strcmp(oid, szOID_SUBJECT_ALT_NAME))
1818 ret = TRUE;
1819 else if (!strcmp(oid, szOID_SUBJECT_ALT_NAME2))
1820 ret = TRUE;
1821 else if (!strcmp(oid, szOID_CERT_POLICIES))
1822 ret = TRUE;
1823 else if (!strcmp(oid, szOID_ENHANCED_KEY_USAGE))
1824 ret = TRUE;
1825 else
1827 FIXME("unsupported critical extension %s\n",
1828 debugstr_a(oid));
1829 ret = FALSE;
1833 return ret;
1836 static BOOL CRYPT_IsCertVersionValid(PCCERT_CONTEXT cert)
1838 BOOL ret = TRUE;
1840 /* Checks whether the contents of the cert match the cert's version. */
1841 switch (cert->pCertInfo->dwVersion)
1843 case CERT_V1:
1844 /* A V1 cert may not contain unique identifiers. See RFC 5280,
1845 * section 4.1.2.8:
1846 * "These fields MUST only appear if the version is 2 or 3 (Section
1847 * 4.1.2.1). These fields MUST NOT appear if the version is 1."
1849 if (cert->pCertInfo->IssuerUniqueId.cbData ||
1850 cert->pCertInfo->SubjectUniqueId.cbData)
1851 ret = FALSE;
1852 /* A V1 cert may not contain extensions. See RFC 5280, section 4.1.2.9:
1853 * "This field MUST only appear if the version is 3 (Section 4.1.2.1)."
1855 if (cert->pCertInfo->cExtension)
1856 ret = FALSE;
1857 break;
1858 case CERT_V2:
1859 /* A V2 cert may not contain extensions. See RFC 5280, section 4.1.2.9:
1860 * "This field MUST only appear if the version is 3 (Section 4.1.2.1)."
1862 if (cert->pCertInfo->cExtension)
1863 ret = FALSE;
1864 break;
1865 case CERT_V3:
1866 /* Do nothing, all fields are allowed for V3 certs */
1867 break;
1868 default:
1869 WARN_(chain)("invalid cert version %d\n", cert->pCertInfo->dwVersion);
1870 ret = FALSE;
1872 return ret;
1875 static void CRYPT_CheckSimpleChain(CertificateChainEngine *engine,
1876 PCERT_SIMPLE_CHAIN chain, LPFILETIME time)
1878 PCERT_CHAIN_ELEMENT rootElement = chain->rgpElement[chain->cElement - 1];
1879 int i;
1880 BOOL pathLengthConstraintViolated = FALSE;
1881 CERT_BASIC_CONSTRAINTS2_INFO constraints = { FALSE, FALSE, 0 };
1882 DWORD status;
1884 TRACE_(chain)("checking chain with %d elements for time %s\n",
1885 chain->cElement, filetime_to_str(time));
1886 for (i = chain->cElement - 1; i >= 0; i--)
1888 BOOL isRoot;
1890 if (TRACE_ON(chain))
1891 dump_element(chain->rgpElement[i]->pCertContext);
1892 if (i == chain->cElement - 1)
1893 isRoot = CRYPT_IsCertificateSelfSigned(
1894 chain->rgpElement[i]->pCertContext);
1895 else
1896 isRoot = FALSE;
1897 if (!CRYPT_IsCertVersionValid(chain->rgpElement[i]->pCertContext))
1899 /* MS appears to accept certs whose versions don't match their
1900 * contents, so there isn't an appropriate error code.
1902 chain->rgpElement[i]->TrustStatus.dwErrorStatus |=
1903 CERT_TRUST_INVALID_EXTENSION;
1905 if (CertVerifyTimeValidity(time,
1906 chain->rgpElement[i]->pCertContext->pCertInfo) != 0)
1907 chain->rgpElement[i]->TrustStatus.dwErrorStatus |=
1908 CERT_TRUST_IS_NOT_TIME_VALID;
1909 if (i != 0)
1911 /* Check the signature of the cert this issued */
1912 if (!CryptVerifyCertificateSignatureEx(0, X509_ASN_ENCODING,
1913 CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT,
1914 (void *)chain->rgpElement[i - 1]->pCertContext,
1915 CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT,
1916 (void *)chain->rgpElement[i]->pCertContext, 0, NULL))
1917 chain->rgpElement[i - 1]->TrustStatus.dwErrorStatus |=
1918 CERT_TRUST_IS_NOT_SIGNATURE_VALID;
1919 /* Once a path length constraint has been violated, every remaining
1920 * CA cert's basic constraints is considered invalid.
1922 if (pathLengthConstraintViolated)
1923 chain->rgpElement[i]->TrustStatus.dwErrorStatus |=
1924 CERT_TRUST_INVALID_BASIC_CONSTRAINTS;
1925 else if (!CRYPT_CheckBasicConstraintsForCA(engine,
1926 chain->rgpElement[i]->pCertContext, &constraints, i - 1, isRoot,
1927 &pathLengthConstraintViolated))
1928 chain->rgpElement[i]->TrustStatus.dwErrorStatus |=
1929 CERT_TRUST_INVALID_BASIC_CONSTRAINTS;
1930 else if (constraints.fPathLenConstraint &&
1931 constraints.dwPathLenConstraint)
1933 /* This one's valid - decrement max length */
1934 constraints.dwPathLenConstraint--;
1937 else
1939 /* Check whether end cert has a basic constraints extension */
1940 if (!CRYPT_DecodeBasicConstraints(
1941 chain->rgpElement[i]->pCertContext, &constraints, FALSE))
1942 chain->rgpElement[i]->TrustStatus.dwErrorStatus |=
1943 CERT_TRUST_INVALID_BASIC_CONSTRAINTS;
1945 if (!CRYPT_KeyUsageValid(engine, chain->rgpElement[i]->pCertContext,
1946 isRoot, constraints.fCA, i))
1947 chain->rgpElement[i]->TrustStatus.dwErrorStatus |=
1948 CERT_TRUST_IS_NOT_VALID_FOR_USAGE;
1949 if (CRYPT_IsSimpleChainCyclic(chain))
1951 /* If the chain is cyclic, then the path length constraints
1952 * are violated, because the chain is infinitely long.
1954 pathLengthConstraintViolated = TRUE;
1955 chain->TrustStatus.dwErrorStatus |=
1956 CERT_TRUST_IS_PARTIAL_CHAIN |
1957 CERT_TRUST_INVALID_BASIC_CONSTRAINTS;
1959 /* Check whether every critical extension is supported */
1960 if (!CRYPT_CriticalExtensionsSupported(
1961 chain->rgpElement[i]->pCertContext))
1962 chain->rgpElement[i]->TrustStatus.dwErrorStatus |=
1963 CERT_TRUST_INVALID_EXTENSION |
1964 CERT_TRUST_HAS_NOT_SUPPORTED_CRITICAL_EXT;
1965 CRYPT_CombineTrustStatus(&chain->TrustStatus,
1966 &chain->rgpElement[i]->TrustStatus);
1968 CRYPT_CheckChainNameConstraints(chain);
1969 CRYPT_CheckChainPolicies(chain);
1970 if ((status = CRYPT_IsCertificateSelfSigned(rootElement->pCertContext)))
1972 rootElement->TrustStatus.dwInfoStatus |= status;
1973 CRYPT_CheckRootCert(engine->hRoot, rootElement);
1975 CRYPT_CombineTrustStatus(&chain->TrustStatus, &rootElement->TrustStatus);
1978 static PCCERT_CONTEXT CRYPT_FindIssuer(const CertificateChainEngine *engine, const CERT_CONTEXT *cert,
1979 HCERTSTORE store, DWORD type, void *para, DWORD flags, PCCERT_CONTEXT prev_issuer)
1981 CRYPT_URL_ARRAY *urls;
1982 PCCERT_CONTEXT issuer;
1983 DWORD size;
1984 BOOL res;
1986 issuer = CertFindCertificateInStore(store, cert->dwCertEncodingType, 0, type, para, prev_issuer);
1987 if(issuer) {
1988 TRACE("Found in store %p\n", issuer);
1989 return issuer;
1992 /* FIXME: For alternate issuers, we don't search world store nor try to retrieve issuer from URL.
1993 * This needs more tests.
1995 if(prev_issuer)
1996 return NULL;
1998 if(engine->hWorld) {
1999 issuer = CertFindCertificateInStore(engine->hWorld, cert->dwCertEncodingType, 0, type, para, NULL);
2000 if(issuer) {
2001 TRACE("Found in world %p\n", issuer);
2002 return issuer;
2006 res = CryptGetObjectUrl(URL_OID_CERTIFICATE_ISSUER, (void*)cert, 0, NULL, &size, NULL, NULL, NULL);
2007 if(!res)
2008 return NULL;
2010 urls = HeapAlloc(GetProcessHeap(), 0, size);
2011 if(!urls)
2012 return NULL;
2014 res = CryptGetObjectUrl(URL_OID_CERTIFICATE_ISSUER, (void*)cert, 0, urls, &size, NULL, NULL, NULL);
2015 if(res)
2017 CERT_CONTEXT *new_cert;
2018 HCERTSTORE new_store;
2019 unsigned i;
2021 for(i=0; i < urls->cUrl; i++)
2023 TRACE("Trying URL %s\n", debugstr_w(urls->rgwszUrl[i]));
2025 res = CryptRetrieveObjectByUrlW(urls->rgwszUrl[i], CONTEXT_OID_CERTIFICATE,
2026 (flags & CERT_CHAIN_CACHE_ONLY_URL_RETRIEVAL) ? CRYPT_CACHE_ONLY_RETRIEVAL : CRYPT_AIA_RETRIEVAL,
2027 0, (void**)&new_cert, NULL, NULL, NULL, NULL);
2028 if(!res)
2030 TRACE("CryptRetrieveObjectByUrlW failed: %u\n", GetLastError());
2031 continue;
2034 /* FIXME: Use new_cert->hCertStore once cert ref count bug is fixed. */
2035 new_store = CertOpenStore(CERT_STORE_PROV_MEMORY, 0, 0, CERT_STORE_CREATE_NEW_FLAG, NULL);
2036 CertAddCertificateContextToStore(new_store, new_cert, CERT_STORE_ADD_NEW, NULL);
2037 issuer = CertFindCertificateInStore(new_store, cert->dwCertEncodingType, 0, type, para, NULL);
2038 CertFreeCertificateContext(new_cert);
2039 CertCloseStore(new_store, 0);
2040 if(issuer)
2042 TRACE("Found downloaded issuer %p\n", issuer);
2043 break;
2048 HeapFree(GetProcessHeap(), 0, urls);
2049 return issuer;
2052 static PCCERT_CONTEXT CRYPT_GetIssuer(const CertificateChainEngine *engine,
2053 HCERTSTORE store, PCCERT_CONTEXT subject, PCCERT_CONTEXT prevIssuer,
2054 DWORD flags, DWORD *infoStatus)
2056 PCCERT_CONTEXT issuer = NULL;
2057 PCERT_EXTENSION ext;
2058 DWORD size;
2060 *infoStatus = 0;
2061 if ((ext = CertFindExtension(szOID_AUTHORITY_KEY_IDENTIFIER,
2062 subject->pCertInfo->cExtension, subject->pCertInfo->rgExtension)))
2064 CERT_AUTHORITY_KEY_ID_INFO *info;
2065 BOOL ret;
2067 ret = CryptDecodeObjectEx(subject->dwCertEncodingType,
2068 X509_AUTHORITY_KEY_ID, ext->Value.pbData, ext->Value.cbData,
2069 CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL,
2070 &info, &size);
2071 if (ret)
2073 CERT_ID id;
2075 if (info->CertIssuer.cbData && info->CertSerialNumber.cbData)
2077 id.dwIdChoice = CERT_ID_ISSUER_SERIAL_NUMBER;
2078 memcpy(&id.u.IssuerSerialNumber.Issuer, &info->CertIssuer,
2079 sizeof(CERT_NAME_BLOB));
2080 memcpy(&id.u.IssuerSerialNumber.SerialNumber,
2081 &info->CertSerialNumber, sizeof(CRYPT_INTEGER_BLOB));
2083 issuer = CRYPT_FindIssuer(engine, subject, store, CERT_FIND_CERT_ID, &id, flags, prevIssuer);
2084 if (issuer)
2086 TRACE_(chain)("issuer found by issuer/serial number\n");
2087 *infoStatus = CERT_TRUST_HAS_EXACT_MATCH_ISSUER;
2090 else if (info->KeyId.cbData)
2092 id.dwIdChoice = CERT_ID_KEY_IDENTIFIER;
2094 memcpy(&id.u.KeyId, &info->KeyId, sizeof(CRYPT_HASH_BLOB));
2095 issuer = CRYPT_FindIssuer(engine, subject, store, CERT_FIND_CERT_ID, &id, flags, prevIssuer);
2096 if (issuer)
2098 TRACE_(chain)("issuer found by key id\n");
2099 *infoStatus = CERT_TRUST_HAS_KEY_MATCH_ISSUER;
2102 LocalFree(info);
2105 else if ((ext = CertFindExtension(szOID_AUTHORITY_KEY_IDENTIFIER2,
2106 subject->pCertInfo->cExtension, subject->pCertInfo->rgExtension)))
2108 CERT_AUTHORITY_KEY_ID2_INFO *info;
2109 BOOL ret;
2111 ret = CryptDecodeObjectEx(subject->dwCertEncodingType,
2112 X509_AUTHORITY_KEY_ID2, ext->Value.pbData, ext->Value.cbData,
2113 CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL,
2114 &info, &size);
2115 if (ret)
2117 CERT_ID id;
2119 if (info->AuthorityCertIssuer.cAltEntry &&
2120 info->AuthorityCertSerialNumber.cbData)
2122 PCERT_ALT_NAME_ENTRY directoryName = NULL;
2123 DWORD i;
2125 for (i = 0; !directoryName &&
2126 i < info->AuthorityCertIssuer.cAltEntry; i++)
2127 if (info->AuthorityCertIssuer.rgAltEntry[i].dwAltNameChoice
2128 == CERT_ALT_NAME_DIRECTORY_NAME)
2129 directoryName =
2130 &info->AuthorityCertIssuer.rgAltEntry[i];
2131 if (directoryName)
2133 id.dwIdChoice = CERT_ID_ISSUER_SERIAL_NUMBER;
2134 memcpy(&id.u.IssuerSerialNumber.Issuer,
2135 &directoryName->u.DirectoryName, sizeof(CERT_NAME_BLOB));
2136 memcpy(&id.u.IssuerSerialNumber.SerialNumber,
2137 &info->AuthorityCertSerialNumber,
2138 sizeof(CRYPT_INTEGER_BLOB));
2140 issuer = CRYPT_FindIssuer(engine, subject, store, CERT_FIND_CERT_ID, &id, flags, prevIssuer);
2141 if (issuer)
2143 TRACE_(chain)("issuer found by directory name\n");
2144 *infoStatus = CERT_TRUST_HAS_EXACT_MATCH_ISSUER;
2147 else
2148 FIXME("no supported name type in authority key id2\n");
2150 else if (info->KeyId.cbData)
2152 id.dwIdChoice = CERT_ID_KEY_IDENTIFIER;
2153 memcpy(&id.u.KeyId, &info->KeyId, sizeof(CRYPT_HASH_BLOB));
2154 issuer = CRYPT_FindIssuer(engine, subject, store, CERT_FIND_CERT_ID, &id, flags, prevIssuer);
2155 if (issuer)
2157 TRACE_(chain)("issuer found by key id\n");
2158 *infoStatus = CERT_TRUST_HAS_KEY_MATCH_ISSUER;
2161 LocalFree(info);
2164 else
2166 issuer = CRYPT_FindIssuer(engine, subject, store, CERT_FIND_SUBJECT_NAME,
2167 &subject->pCertInfo->Issuer, flags, prevIssuer);
2168 TRACE_(chain)("issuer found by name\n");
2169 *infoStatus = CERT_TRUST_HAS_NAME_MATCH_ISSUER;
2171 return issuer;
2174 /* Builds a simple chain by finding an issuer for the last cert in the chain,
2175 * until reaching a self-signed cert, or until no issuer can be found.
2177 static BOOL CRYPT_BuildSimpleChain(const CertificateChainEngine *engine,
2178 HCERTSTORE world, DWORD flags, PCERT_SIMPLE_CHAIN chain)
2180 BOOL ret = TRUE;
2181 PCCERT_CONTEXT cert = chain->rgpElement[chain->cElement - 1]->pCertContext;
2183 while (ret && !CRYPT_IsSimpleChainCyclic(chain) &&
2184 !CRYPT_IsCertificateSelfSigned(cert))
2186 PCCERT_CONTEXT issuer = CRYPT_GetIssuer(engine, world, cert, NULL, flags,
2187 &chain->rgpElement[chain->cElement - 1]->TrustStatus.dwInfoStatus);
2189 if (issuer)
2191 ret = CRYPT_AddCertToSimpleChain(engine, chain, issuer,
2192 chain->rgpElement[chain->cElement - 1]->TrustStatus.dwInfoStatus);
2193 /* CRYPT_AddCertToSimpleChain add-ref's the issuer, so free it to
2194 * close the enumeration that found it
2196 CertFreeCertificateContext(issuer);
2197 cert = issuer;
2199 else
2201 TRACE_(chain)("Couldn't find issuer, halting chain creation\n");
2202 chain->TrustStatus.dwErrorStatus |= CERT_TRUST_IS_PARTIAL_CHAIN;
2203 break;
2206 return ret;
2209 static LPCSTR debugstr_filetime(LPFILETIME pTime)
2211 if (!pTime)
2212 return "(nil)";
2213 return wine_dbg_sprintf("%p (%s)", pTime, filetime_to_str(pTime));
2216 static BOOL CRYPT_GetSimpleChainForCert(CertificateChainEngine *engine,
2217 HCERTSTORE world, PCCERT_CONTEXT cert, LPFILETIME pTime, DWORD flags,
2218 PCERT_SIMPLE_CHAIN *ppChain)
2220 BOOL ret = FALSE;
2221 PCERT_SIMPLE_CHAIN chain;
2223 TRACE("(%p, %p, %p, %s)\n", engine, world, cert, debugstr_filetime(pTime));
2225 chain = CryptMemAlloc(sizeof(CERT_SIMPLE_CHAIN));
2226 if (chain)
2228 memset(chain, 0, sizeof(CERT_SIMPLE_CHAIN));
2229 chain->cbSize = sizeof(CERT_SIMPLE_CHAIN);
2230 ret = CRYPT_AddCertToSimpleChain(engine, chain, cert, 0);
2231 if (ret)
2233 ret = CRYPT_BuildSimpleChain(engine, world, flags, chain);
2234 if (ret)
2235 CRYPT_CheckSimpleChain(engine, chain, pTime);
2237 if (!ret)
2239 CRYPT_FreeSimpleChain(chain);
2240 chain = NULL;
2242 *ppChain = chain;
2244 return ret;
2247 static BOOL CRYPT_BuildCandidateChainFromCert(CertificateChainEngine *engine,
2248 PCCERT_CONTEXT cert, LPFILETIME pTime, HCERTSTORE hAdditionalStore, DWORD flags,
2249 CertificateChain **ppChain)
2251 PCERT_SIMPLE_CHAIN simpleChain = NULL;
2252 HCERTSTORE world;
2253 BOOL ret;
2255 world = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, 0,
2256 CERT_STORE_CREATE_NEW_FLAG, NULL);
2257 CertAddStoreToCollection(world, engine->hWorld, 0, 0);
2258 if (hAdditionalStore)
2259 CertAddStoreToCollection(world, hAdditionalStore, 0, 0);
2260 /* FIXME: only simple chains are supported for now, as CTLs aren't
2261 * supported yet.
2263 if ((ret = CRYPT_GetSimpleChainForCert(engine, world, cert, pTime, flags, &simpleChain)))
2265 CertificateChain *chain = CryptMemAlloc(sizeof(CertificateChain));
2267 if (chain)
2269 chain->ref = 1;
2270 chain->world = world;
2271 chain->context.cbSize = sizeof(CERT_CHAIN_CONTEXT);
2272 chain->context.TrustStatus = simpleChain->TrustStatus;
2273 chain->context.cChain = 1;
2274 chain->context.rgpChain = CryptMemAlloc(sizeof(PCERT_SIMPLE_CHAIN));
2275 chain->context.rgpChain[0] = simpleChain;
2276 chain->context.cLowerQualityChainContext = 0;
2277 chain->context.rgpLowerQualityChainContext = NULL;
2278 chain->context.fHasRevocationFreshnessTime = FALSE;
2279 chain->context.dwRevocationFreshnessTime = 0;
2281 else
2283 CRYPT_FreeSimpleChain(simpleChain);
2284 ret = FALSE;
2286 *ppChain = chain;
2288 return ret;
2291 /* Makes and returns a copy of chain, up to and including element iElement. */
2292 static PCERT_SIMPLE_CHAIN CRYPT_CopySimpleChainToElement(
2293 const CERT_SIMPLE_CHAIN *chain, DWORD iElement)
2295 PCERT_SIMPLE_CHAIN copy = CryptMemAlloc(sizeof(CERT_SIMPLE_CHAIN));
2297 if (copy)
2299 memset(copy, 0, sizeof(CERT_SIMPLE_CHAIN));
2300 copy->cbSize = sizeof(CERT_SIMPLE_CHAIN);
2301 copy->rgpElement =
2302 CryptMemAlloc((iElement + 1) * sizeof(PCERT_CHAIN_ELEMENT));
2303 if (copy->rgpElement)
2305 DWORD i;
2306 BOOL ret = TRUE;
2308 memset(copy->rgpElement, 0,
2309 (iElement + 1) * sizeof(PCERT_CHAIN_ELEMENT));
2310 for (i = 0; ret && i <= iElement; i++)
2312 PCERT_CHAIN_ELEMENT element =
2313 CryptMemAlloc(sizeof(CERT_CHAIN_ELEMENT));
2315 if (element)
2317 *element = *chain->rgpElement[i];
2318 element->pCertContext = CertDuplicateCertificateContext(
2319 chain->rgpElement[i]->pCertContext);
2320 /* Reset the trust status of the copied element, it'll get
2321 * rechecked after the new chain is done.
2323 memset(&element->TrustStatus, 0, sizeof(CERT_TRUST_STATUS));
2324 copy->rgpElement[copy->cElement++] = element;
2326 else
2327 ret = FALSE;
2329 if (!ret)
2331 for (i = 0; i <= iElement; i++)
2332 CryptMemFree(copy->rgpElement[i]);
2333 CryptMemFree(copy->rgpElement);
2334 CryptMemFree(copy);
2335 copy = NULL;
2338 else
2340 CryptMemFree(copy);
2341 copy = NULL;
2344 return copy;
2347 static void CRYPT_FreeLowerQualityChains(CertificateChain *chain)
2349 DWORD i;
2351 for (i = 0; i < chain->context.cLowerQualityChainContext; i++)
2352 CertFreeCertificateChain(chain->context.rgpLowerQualityChainContext[i]);
2353 CryptMemFree(chain->context.rgpLowerQualityChainContext);
2354 chain->context.cLowerQualityChainContext = 0;
2355 chain->context.rgpLowerQualityChainContext = NULL;
2358 static void CRYPT_FreeChainContext(CertificateChain *chain)
2360 DWORD i;
2362 CRYPT_FreeLowerQualityChains(chain);
2363 for (i = 0; i < chain->context.cChain; i++)
2364 CRYPT_FreeSimpleChain(chain->context.rgpChain[i]);
2365 CryptMemFree(chain->context.rgpChain);
2366 CertCloseStore(chain->world, 0);
2367 CryptMemFree(chain);
2370 /* Makes and returns a copy of chain, up to and including element iElement of
2371 * simple chain iChain.
2373 static CertificateChain *CRYPT_CopyChainToElement(CertificateChain *chain,
2374 DWORD iChain, DWORD iElement)
2376 CertificateChain *copy = CryptMemAlloc(sizeof(CertificateChain));
2378 if (copy)
2380 copy->ref = 1;
2381 copy->world = CertDuplicateStore(chain->world);
2382 copy->context.cbSize = sizeof(CERT_CHAIN_CONTEXT);
2383 /* Leave the trust status of the copied chain unset, it'll get
2384 * rechecked after the new chain is done.
2386 memset(&copy->context.TrustStatus, 0, sizeof(CERT_TRUST_STATUS));
2387 copy->context.cLowerQualityChainContext = 0;
2388 copy->context.rgpLowerQualityChainContext = NULL;
2389 copy->context.fHasRevocationFreshnessTime = FALSE;
2390 copy->context.dwRevocationFreshnessTime = 0;
2391 copy->context.rgpChain = CryptMemAlloc(
2392 (iChain + 1) * sizeof(PCERT_SIMPLE_CHAIN));
2393 if (copy->context.rgpChain)
2395 BOOL ret = TRUE;
2396 DWORD i;
2398 memset(copy->context.rgpChain, 0,
2399 (iChain + 1) * sizeof(PCERT_SIMPLE_CHAIN));
2400 if (iChain)
2402 for (i = 0; ret && iChain && i < iChain - 1; i++)
2404 copy->context.rgpChain[i] =
2405 CRYPT_CopySimpleChainToElement(chain->context.rgpChain[i],
2406 chain->context.rgpChain[i]->cElement - 1);
2407 if (!copy->context.rgpChain[i])
2408 ret = FALSE;
2411 else
2412 i = 0;
2413 if (ret)
2415 copy->context.rgpChain[i] =
2416 CRYPT_CopySimpleChainToElement(chain->context.rgpChain[i],
2417 iElement);
2418 if (!copy->context.rgpChain[i])
2419 ret = FALSE;
2421 if (!ret)
2423 CRYPT_FreeChainContext(copy);
2424 copy = NULL;
2426 else
2427 copy->context.cChain = iChain + 1;
2429 else
2431 CryptMemFree(copy);
2432 copy = NULL;
2435 return copy;
2438 static CertificateChain *CRYPT_BuildAlternateContextFromChain(
2439 CertificateChainEngine *engine, LPFILETIME pTime, HCERTSTORE hAdditionalStore,
2440 DWORD flags, CertificateChain *chain)
2442 CertificateChain *alternate;
2444 TRACE("(%p, %s, %p, %p)\n", engine, debugstr_filetime(pTime),
2445 hAdditionalStore, chain);
2447 /* Always start with the last "lower quality" chain to ensure a consistent
2448 * order of alternate creation:
2450 if (chain->context.cLowerQualityChainContext)
2451 chain = (CertificateChain*)chain->context.rgpLowerQualityChainContext[
2452 chain->context.cLowerQualityChainContext - 1];
2453 /* A chain with only one element can't have any alternates */
2454 if (chain->context.cChain <= 1 && chain->context.rgpChain[0]->cElement <= 1)
2455 alternate = NULL;
2456 else
2458 DWORD i, j, infoStatus;
2459 PCCERT_CONTEXT alternateIssuer = NULL;
2461 alternate = NULL;
2462 for (i = 0; !alternateIssuer && i < chain->context.cChain; i++)
2463 for (j = 0; !alternateIssuer &&
2464 j < chain->context.rgpChain[i]->cElement - 1; j++)
2466 PCCERT_CONTEXT subject =
2467 chain->context.rgpChain[i]->rgpElement[j]->pCertContext;
2468 PCCERT_CONTEXT prevIssuer = CertDuplicateCertificateContext(
2469 chain->context.rgpChain[i]->rgpElement[j + 1]->pCertContext);
2471 alternateIssuer = CRYPT_GetIssuer(engine, prevIssuer->hCertStore,
2472 subject, prevIssuer, flags, &infoStatus);
2474 if (alternateIssuer)
2476 i--;
2477 j--;
2478 alternate = CRYPT_CopyChainToElement(chain, i, j);
2479 if (alternate)
2481 BOOL ret = CRYPT_AddCertToSimpleChain(engine,
2482 alternate->context.rgpChain[i], alternateIssuer, infoStatus);
2484 /* CRYPT_AddCertToSimpleChain add-ref's the issuer, so free it
2485 * to close the enumeration that found it
2487 CertFreeCertificateContext(alternateIssuer);
2488 if (ret)
2490 ret = CRYPT_BuildSimpleChain(engine, alternate->world,
2491 flags, alternate->context.rgpChain[i]);
2492 if (ret)
2493 CRYPT_CheckSimpleChain(engine,
2494 alternate->context.rgpChain[i], pTime);
2495 CRYPT_CombineTrustStatus(&alternate->context.TrustStatus,
2496 &alternate->context.rgpChain[i]->TrustStatus);
2498 if (!ret)
2500 CRYPT_FreeChainContext(alternate);
2501 alternate = NULL;
2506 TRACE("%p\n", alternate);
2507 return alternate;
2510 #define CHAIN_QUALITY_SIGNATURE_VALID 0x16
2511 #define CHAIN_QUALITY_TIME_VALID 8
2512 #define CHAIN_QUALITY_COMPLETE_CHAIN 4
2513 #define CHAIN_QUALITY_BASIC_CONSTRAINTS 2
2514 #define CHAIN_QUALITY_TRUSTED_ROOT 1
2516 #define CHAIN_QUALITY_HIGHEST \
2517 CHAIN_QUALITY_SIGNATURE_VALID | CHAIN_QUALITY_TIME_VALID | \
2518 CHAIN_QUALITY_COMPLETE_CHAIN | CHAIN_QUALITY_BASIC_CONSTRAINTS | \
2519 CHAIN_QUALITY_TRUSTED_ROOT
2521 #define IS_TRUST_ERROR_SET(TrustStatus, bits) \
2522 (TrustStatus)->dwErrorStatus & (bits)
2524 static DWORD CRYPT_ChainQuality(const CertificateChain *chain)
2526 DWORD quality = CHAIN_QUALITY_HIGHEST;
2528 if (IS_TRUST_ERROR_SET(&chain->context.TrustStatus,
2529 CERT_TRUST_IS_UNTRUSTED_ROOT))
2530 quality &= ~CHAIN_QUALITY_TRUSTED_ROOT;
2531 if (IS_TRUST_ERROR_SET(&chain->context.TrustStatus,
2532 CERT_TRUST_INVALID_BASIC_CONSTRAINTS))
2533 quality &= ~CHAIN_QUALITY_BASIC_CONSTRAINTS;
2534 if (IS_TRUST_ERROR_SET(&chain->context.TrustStatus,
2535 CERT_TRUST_IS_PARTIAL_CHAIN))
2536 quality &= ~CHAIN_QUALITY_COMPLETE_CHAIN;
2537 if (IS_TRUST_ERROR_SET(&chain->context.TrustStatus,
2538 CERT_TRUST_IS_NOT_TIME_VALID | CERT_TRUST_IS_NOT_TIME_NESTED))
2539 quality &= ~CHAIN_QUALITY_TIME_VALID;
2540 if (IS_TRUST_ERROR_SET(&chain->context.TrustStatus,
2541 CERT_TRUST_IS_NOT_SIGNATURE_VALID))
2542 quality &= ~CHAIN_QUALITY_SIGNATURE_VALID;
2543 return quality;
2546 /* Chooses the highest quality chain among chain and its "lower quality"
2547 * alternate chains. Returns the highest quality chain, with all other
2548 * chains as lower quality chains of it.
2550 static CertificateChain *CRYPT_ChooseHighestQualityChain(
2551 CertificateChain *chain)
2553 DWORD i;
2555 /* There are always only two chains being considered: chain, and an
2556 * alternate at chain->rgpLowerQualityChainContext[i]. If the alternate
2557 * has a higher quality than chain, the alternate gets assigned the lower
2558 * quality contexts, with chain taking the alternate's place among the
2559 * lower quality contexts.
2561 for (i = 0; i < chain->context.cLowerQualityChainContext; i++)
2563 CertificateChain *alternate =
2564 (CertificateChain*)chain->context.rgpLowerQualityChainContext[i];
2566 if (CRYPT_ChainQuality(alternate) > CRYPT_ChainQuality(chain))
2568 alternate->context.cLowerQualityChainContext =
2569 chain->context.cLowerQualityChainContext;
2570 alternate->context.rgpLowerQualityChainContext =
2571 chain->context.rgpLowerQualityChainContext;
2572 alternate->context.rgpLowerQualityChainContext[i] =
2573 (PCCERT_CHAIN_CONTEXT)chain;
2574 chain->context.cLowerQualityChainContext = 0;
2575 chain->context.rgpLowerQualityChainContext = NULL;
2576 chain = alternate;
2579 return chain;
2582 static BOOL CRYPT_AddAlternateChainToChain(CertificateChain *chain,
2583 const CertificateChain *alternate)
2585 BOOL ret;
2587 if (chain->context.cLowerQualityChainContext)
2588 chain->context.rgpLowerQualityChainContext =
2589 CryptMemRealloc(chain->context.rgpLowerQualityChainContext,
2590 (chain->context.cLowerQualityChainContext + 1) *
2591 sizeof(PCCERT_CHAIN_CONTEXT));
2592 else
2593 chain->context.rgpLowerQualityChainContext =
2594 CryptMemAlloc(sizeof(PCCERT_CHAIN_CONTEXT));
2595 if (chain->context.rgpLowerQualityChainContext)
2597 chain->context.rgpLowerQualityChainContext[
2598 chain->context.cLowerQualityChainContext++] =
2599 (PCCERT_CHAIN_CONTEXT)alternate;
2600 ret = TRUE;
2602 else
2603 ret = FALSE;
2604 return ret;
2607 static PCERT_CHAIN_ELEMENT CRYPT_FindIthElementInChain(
2608 const CERT_CHAIN_CONTEXT *chain, DWORD i)
2610 DWORD j, iElement;
2611 PCERT_CHAIN_ELEMENT element = NULL;
2613 for (j = 0, iElement = 0; !element && j < chain->cChain; j++)
2615 if (iElement + chain->rgpChain[j]->cElement < i)
2616 iElement += chain->rgpChain[j]->cElement;
2617 else
2618 element = chain->rgpChain[j]->rgpElement[i - iElement];
2620 return element;
2623 typedef struct _CERT_CHAIN_PARA_NO_EXTRA_FIELDS {
2624 DWORD cbSize;
2625 CERT_USAGE_MATCH RequestedUsage;
2626 } CERT_CHAIN_PARA_NO_EXTRA_FIELDS;
2628 static void CRYPT_VerifyChainRevocation(PCERT_CHAIN_CONTEXT chain,
2629 LPFILETIME pTime, HCERTSTORE hAdditionalStore,
2630 const CERT_CHAIN_PARA *pChainPara, DWORD chainFlags)
2632 DWORD cContext;
2634 if (chainFlags & CERT_CHAIN_REVOCATION_CHECK_END_CERT)
2635 cContext = 1;
2636 else if ((chainFlags & CERT_CHAIN_REVOCATION_CHECK_CHAIN) ||
2637 (chainFlags & CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT))
2639 DWORD i;
2641 for (i = 0, cContext = 0; i < chain->cChain; i++)
2643 if (i < chain->cChain - 1 ||
2644 chainFlags & CERT_CHAIN_REVOCATION_CHECK_CHAIN)
2645 cContext += chain->rgpChain[i]->cElement;
2646 else
2647 cContext += chain->rgpChain[i]->cElement - 1;
2650 else
2651 cContext = 0;
2652 if (cContext)
2654 DWORD i, j, iContext, revocationFlags;
2655 CERT_REVOCATION_PARA revocationPara = { sizeof(revocationPara), 0 };
2656 CERT_REVOCATION_STATUS revocationStatus =
2657 { sizeof(revocationStatus), 0 };
2658 BOOL ret;
2660 revocationFlags = CERT_VERIFY_REV_CHAIN_FLAG;
2661 if (chainFlags & CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY)
2662 revocationFlags |= CERT_VERIFY_CACHE_ONLY_BASED_REVOCATION;
2663 if (chainFlags & CERT_CHAIN_REVOCATION_ACCUMULATIVE_TIMEOUT)
2664 revocationFlags |= CERT_VERIFY_REV_ACCUMULATIVE_TIMEOUT_FLAG;
2665 revocationPara.pftTimeToUse = pTime;
2666 if (hAdditionalStore)
2668 revocationPara.cCertStore = 1;
2669 revocationPara.rgCertStore = &hAdditionalStore;
2670 revocationPara.hCrlStore = hAdditionalStore;
2672 if (pChainPara->cbSize == sizeof(CERT_CHAIN_PARA))
2674 revocationPara.dwUrlRetrievalTimeout =
2675 pChainPara->dwUrlRetrievalTimeout;
2676 revocationPara.fCheckFreshnessTime =
2677 pChainPara->fCheckRevocationFreshnessTime;
2678 revocationPara.dwFreshnessTime =
2679 pChainPara->dwRevocationFreshnessTime;
2681 for (i = 0, iContext = 0; iContext < cContext && i < chain->cChain; i++)
2683 for (j = 0; iContext < cContext &&
2684 j < chain->rgpChain[i]->cElement; j++, iContext++)
2686 PCCERT_CONTEXT certToCheck =
2687 chain->rgpChain[i]->rgpElement[j]->pCertContext;
2689 if (j < chain->rgpChain[i]->cElement - 1)
2690 revocationPara.pIssuerCert =
2691 chain->rgpChain[i]->rgpElement[j + 1]->pCertContext;
2692 else
2693 revocationPara.pIssuerCert = NULL;
2694 ret = CertVerifyRevocation(X509_ASN_ENCODING,
2695 CERT_CONTEXT_REVOCATION_TYPE, 1, (void **)&certToCheck,
2696 revocationFlags, &revocationPara, &revocationStatus);
2698 if (!ret && chainFlags & CERT_CHAIN_REVOCATION_CHECK_CHAIN
2699 && revocationStatus.dwError == CRYPT_E_NO_REVOCATION_CHECK && revocationPara.pIssuerCert == NULL)
2700 ret = TRUE;
2702 if (!ret)
2704 PCERT_CHAIN_ELEMENT element = CRYPT_FindIthElementInChain(
2705 chain, iContext);
2706 DWORD error;
2708 switch (revocationStatus.dwError)
2710 case CRYPT_E_NO_REVOCATION_CHECK:
2711 case CRYPT_E_NO_REVOCATION_DLL:
2712 case CRYPT_E_NOT_IN_REVOCATION_DATABASE:
2713 /* If the revocation status is unknown, it's assumed
2714 * to be offline too.
2716 error = CERT_TRUST_REVOCATION_STATUS_UNKNOWN |
2717 CERT_TRUST_IS_OFFLINE_REVOCATION;
2718 break;
2719 case CRYPT_E_REVOCATION_OFFLINE:
2720 error = CERT_TRUST_IS_OFFLINE_REVOCATION;
2721 break;
2722 case CRYPT_E_REVOKED:
2723 error = CERT_TRUST_IS_REVOKED;
2724 break;
2725 default:
2726 WARN("unmapped error %08x\n", revocationStatus.dwError);
2727 error = 0;
2729 if (element)
2731 /* FIXME: set element's pRevocationInfo member */
2732 element->TrustStatus.dwErrorStatus |= error;
2734 chain->TrustStatus.dwErrorStatus |= error;
2741 static void CRYPT_CheckUsages(PCERT_CHAIN_CONTEXT chain,
2742 const CERT_CHAIN_PARA *pChainPara)
2744 if (pChainPara->cbSize >= sizeof(CERT_CHAIN_PARA_NO_EXTRA_FIELDS) &&
2745 pChainPara->RequestedUsage.Usage.cUsageIdentifier)
2747 PCCERT_CONTEXT endCert;
2748 PCERT_EXTENSION ext;
2749 BOOL validForUsage;
2751 /* A chain, if created, always includes the end certificate */
2752 endCert = chain->rgpChain[0]->rgpElement[0]->pCertContext;
2753 /* The extended key usage extension specifies how a certificate's
2754 * public key may be used. From RFC 5280, section 4.2.1.12:
2755 * "This extension indicates one or more purposes for which the
2756 * certified public key may be used, in addition to or in place of the
2757 * basic purposes indicated in the key usage extension."
2758 * If the extension is present, it only satisfies the requested usage
2759 * if that usage is included in the extension:
2760 * "If the extension is present, then the certificate MUST only be used
2761 * for one of the purposes indicated."
2762 * There is also the special anyExtendedKeyUsage OID, but it doesn't
2763 * have to be respected:
2764 * "Applications that require the presence of a particular purpose
2765 * MAY reject certificates that include the anyExtendedKeyUsage OID
2766 * but not the particular OID expected for the application."
2767 * For now, I'm being more conservative and ignoring the presence of
2768 * the anyExtendedKeyUsage OID.
2770 if ((ext = CertFindExtension(szOID_ENHANCED_KEY_USAGE,
2771 endCert->pCertInfo->cExtension, endCert->pCertInfo->rgExtension)))
2773 const CERT_ENHKEY_USAGE *requestedUsage =
2774 &pChainPara->RequestedUsage.Usage;
2775 CERT_ENHKEY_USAGE *usage;
2776 DWORD size;
2778 if (CryptDecodeObjectEx(X509_ASN_ENCODING,
2779 X509_ENHANCED_KEY_USAGE, ext->Value.pbData, ext->Value.cbData,
2780 CRYPT_DECODE_ALLOC_FLAG, NULL, &usage, &size))
2782 if (pChainPara->RequestedUsage.dwType == USAGE_MATCH_TYPE_AND)
2784 DWORD i, j;
2786 /* For AND matches, all usages must be present */
2787 validForUsage = TRUE;
2788 for (i = 0; validForUsage &&
2789 i < requestedUsage->cUsageIdentifier; i++)
2791 BOOL match = FALSE;
2793 for (j = 0; !match && j < usage->cUsageIdentifier; j++)
2794 match = !strcmp(usage->rgpszUsageIdentifier[j],
2795 requestedUsage->rgpszUsageIdentifier[i]);
2796 if (!match)
2797 validForUsage = FALSE;
2800 else
2802 DWORD i, j;
2804 /* For OR matches, any matching usage suffices */
2805 validForUsage = FALSE;
2806 for (i = 0; !validForUsage &&
2807 i < requestedUsage->cUsageIdentifier; i++)
2809 for (j = 0; !validForUsage &&
2810 j < usage->cUsageIdentifier; j++)
2811 validForUsage =
2812 !strcmp(usage->rgpszUsageIdentifier[j],
2813 requestedUsage->rgpszUsageIdentifier[i]);
2816 LocalFree(usage);
2818 else
2819 validForUsage = FALSE;
2821 else
2823 /* If the extension isn't present, any interpretation is valid:
2824 * "Certificate using applications MAY require that the extended
2825 * key usage extension be present and that a particular purpose
2826 * be indicated in order for the certificate to be acceptable to
2827 * that application."
2828 * Not all web sites include the extended key usage extension, so
2829 * accept chains without it.
2831 TRACE_(chain)("requested usage from certificate with no usages\n");
2832 validForUsage = TRUE;
2834 if (!validForUsage)
2836 chain->TrustStatus.dwErrorStatus |=
2837 CERT_TRUST_IS_NOT_VALID_FOR_USAGE;
2838 chain->rgpChain[0]->rgpElement[0]->TrustStatus.dwErrorStatus |=
2839 CERT_TRUST_IS_NOT_VALID_FOR_USAGE;
2842 if (pChainPara->cbSize >= sizeof(CERT_CHAIN_PARA) &&
2843 pChainPara->RequestedIssuancePolicy.Usage.cUsageIdentifier)
2844 FIXME("unimplemented for RequestedIssuancePolicy\n");
2847 static void dump_usage_match(LPCSTR name, const CERT_USAGE_MATCH *usageMatch)
2849 if (usageMatch->Usage.cUsageIdentifier)
2851 DWORD i;
2853 TRACE_(chain)("%s: %s\n", name,
2854 usageMatch->dwType == USAGE_MATCH_TYPE_AND ? "AND" : "OR");
2855 for (i = 0; i < usageMatch->Usage.cUsageIdentifier; i++)
2856 TRACE_(chain)("%s\n", usageMatch->Usage.rgpszUsageIdentifier[i]);
2860 static void dump_chain_para(const CERT_CHAIN_PARA *pChainPara)
2862 TRACE_(chain)("%d\n", pChainPara->cbSize);
2863 if (pChainPara->cbSize >= sizeof(CERT_CHAIN_PARA_NO_EXTRA_FIELDS))
2864 dump_usage_match("RequestedUsage", &pChainPara->RequestedUsage);
2865 if (pChainPara->cbSize >= sizeof(CERT_CHAIN_PARA))
2867 dump_usage_match("RequestedIssuancePolicy",
2868 &pChainPara->RequestedIssuancePolicy);
2869 TRACE_(chain)("%d\n", pChainPara->dwUrlRetrievalTimeout);
2870 TRACE_(chain)("%d\n", pChainPara->fCheckRevocationFreshnessTime);
2871 TRACE_(chain)("%d\n", pChainPara->dwRevocationFreshnessTime);
2875 BOOL WINAPI CertGetCertificateChain(HCERTCHAINENGINE hChainEngine,
2876 PCCERT_CONTEXT pCertContext, LPFILETIME pTime, HCERTSTORE hAdditionalStore,
2877 PCERT_CHAIN_PARA pChainPara, DWORD dwFlags, LPVOID pvReserved,
2878 PCCERT_CHAIN_CONTEXT* ppChainContext)
2880 CertificateChainEngine *engine;
2881 BOOL ret;
2882 CertificateChain *chain = NULL;
2884 TRACE("(%p, %p, %s, %p, %p, %08x, %p, %p)\n", hChainEngine, pCertContext,
2885 debugstr_filetime(pTime), hAdditionalStore, pChainPara, dwFlags,
2886 pvReserved, ppChainContext);
2888 engine = get_chain_engine(hChainEngine, TRUE);
2889 if (!engine)
2890 return FALSE;
2892 if (ppChainContext)
2893 *ppChainContext = NULL;
2894 if (!pChainPara)
2896 SetLastError(E_INVALIDARG);
2897 return FALSE;
2899 if (!pCertContext->pCertInfo->SignatureAlgorithm.pszObjId)
2901 SetLastError(ERROR_INVALID_DATA);
2902 return FALSE;
2905 if (TRACE_ON(chain))
2906 dump_chain_para(pChainPara);
2907 /* FIXME: what about HCCE_LOCAL_MACHINE? */
2908 ret = CRYPT_BuildCandidateChainFromCert(engine, pCertContext, pTime,
2909 hAdditionalStore, dwFlags, &chain);
2910 if (ret)
2912 CertificateChain *alternate = NULL;
2913 PCERT_CHAIN_CONTEXT pChain;
2915 do {
2916 alternate = CRYPT_BuildAlternateContextFromChain(engine,
2917 pTime, hAdditionalStore, dwFlags, chain);
2919 /* Alternate contexts are added as "lower quality" contexts of
2920 * chain, to avoid loops in alternate chain creation.
2921 * The highest-quality chain is chosen at the end.
2923 if (alternate)
2924 ret = CRYPT_AddAlternateChainToChain(chain, alternate);
2925 } while (ret && alternate);
2926 chain = CRYPT_ChooseHighestQualityChain(chain);
2927 if (!(dwFlags & CERT_CHAIN_RETURN_LOWER_QUALITY_CONTEXTS))
2928 CRYPT_FreeLowerQualityChains(chain);
2929 pChain = (PCERT_CHAIN_CONTEXT)chain;
2930 CRYPT_VerifyChainRevocation(pChain, pTime, hAdditionalStore,
2931 pChainPara, dwFlags);
2932 CRYPT_CheckUsages(pChain, pChainPara);
2933 TRACE_(chain)("error status: %08x\n",
2934 pChain->TrustStatus.dwErrorStatus);
2935 if (ppChainContext)
2936 *ppChainContext = pChain;
2937 else
2938 CertFreeCertificateChain(pChain);
2940 TRACE("returning %d\n", ret);
2941 return ret;
2944 PCCERT_CHAIN_CONTEXT WINAPI CertDuplicateCertificateChain(
2945 PCCERT_CHAIN_CONTEXT pChainContext)
2947 CertificateChain *chain = (CertificateChain*)pChainContext;
2949 TRACE("(%p)\n", pChainContext);
2951 if (chain)
2952 InterlockedIncrement(&chain->ref);
2953 return pChainContext;
2956 VOID WINAPI CertFreeCertificateChain(PCCERT_CHAIN_CONTEXT pChainContext)
2958 CertificateChain *chain = (CertificateChain*)pChainContext;
2960 TRACE("(%p)\n", pChainContext);
2962 if (chain)
2964 if (InterlockedDecrement(&chain->ref) == 0)
2965 CRYPT_FreeChainContext(chain);
2969 PCCERT_CHAIN_CONTEXT WINAPI CertFindChainInStore(HCERTSTORE store,
2970 DWORD certEncodingType, DWORD findFlags, DWORD findType,
2971 const void *findPara, PCCERT_CHAIN_CONTEXT prevChainContext)
2973 FIXME("(%p, %08x, %08x, %d, %p, %p): stub\n", store, certEncodingType,
2974 findFlags, findType, findPara, prevChainContext);
2975 return NULL;
2978 static void find_element_with_error(PCCERT_CHAIN_CONTEXT chain, DWORD error,
2979 LONG *iChain, LONG *iElement)
2981 DWORD i, j;
2983 for (i = 0; i < chain->cChain; i++)
2984 for (j = 0; j < chain->rgpChain[i]->cElement; j++)
2985 if (chain->rgpChain[i]->rgpElement[j]->TrustStatus.dwErrorStatus &
2986 error)
2988 *iChain = i;
2989 *iElement = j;
2990 return;
2994 static BOOL WINAPI verify_base_policy(LPCSTR szPolicyOID,
2995 PCCERT_CHAIN_CONTEXT pChainContext, PCERT_CHAIN_POLICY_PARA pPolicyPara,
2996 PCERT_CHAIN_POLICY_STATUS pPolicyStatus)
2998 DWORD checks = 0;
3000 if (pPolicyPara)
3001 checks = pPolicyPara->dwFlags;
3002 pPolicyStatus->lChainIndex = pPolicyStatus->lElementIndex = -1;
3003 pPolicyStatus->dwError = NO_ERROR;
3004 if (pChainContext->TrustStatus.dwErrorStatus &
3005 CERT_TRUST_IS_NOT_SIGNATURE_VALID)
3007 pPolicyStatus->dwError = TRUST_E_CERT_SIGNATURE;
3008 find_element_with_error(pChainContext,
3009 CERT_TRUST_IS_NOT_SIGNATURE_VALID, &pPolicyStatus->lChainIndex,
3010 &pPolicyStatus->lElementIndex);
3012 else if (pChainContext->TrustStatus.dwErrorStatus & CERT_TRUST_IS_CYCLIC)
3014 pPolicyStatus->dwError = CERT_E_CHAINING;
3015 find_element_with_error(pChainContext, CERT_TRUST_IS_CYCLIC,
3016 &pPolicyStatus->lChainIndex, &pPolicyStatus->lElementIndex);
3017 /* For a cyclic chain, which element is a cycle isn't meaningful */
3018 pPolicyStatus->lElementIndex = -1;
3020 if (!pPolicyStatus->dwError &&
3021 pChainContext->TrustStatus.dwErrorStatus & CERT_TRUST_IS_UNTRUSTED_ROOT &&
3022 !(checks & CERT_CHAIN_POLICY_ALLOW_UNKNOWN_CA_FLAG))
3024 pPolicyStatus->dwError = CERT_E_UNTRUSTEDROOT;
3025 find_element_with_error(pChainContext,
3026 CERT_TRUST_IS_UNTRUSTED_ROOT, &pPolicyStatus->lChainIndex,
3027 &pPolicyStatus->lElementIndex);
3029 if (!pPolicyStatus->dwError &&
3030 pChainContext->TrustStatus.dwErrorStatus & CERT_TRUST_IS_NOT_TIME_VALID)
3032 pPolicyStatus->dwError = CERT_E_EXPIRED;
3033 find_element_with_error(pChainContext,
3034 CERT_TRUST_IS_NOT_TIME_VALID, &pPolicyStatus->lChainIndex,
3035 &pPolicyStatus->lElementIndex);
3037 if (!pPolicyStatus->dwError &&
3038 pChainContext->TrustStatus.dwErrorStatus &
3039 CERT_TRUST_IS_NOT_VALID_FOR_USAGE &&
3040 !(checks & CERT_CHAIN_POLICY_IGNORE_WRONG_USAGE_FLAG))
3042 pPolicyStatus->dwError = CERT_E_WRONG_USAGE;
3043 find_element_with_error(pChainContext,
3044 CERT_TRUST_IS_NOT_VALID_FOR_USAGE, &pPolicyStatus->lChainIndex,
3045 &pPolicyStatus->lElementIndex);
3047 if (!pPolicyStatus->dwError &&
3048 pChainContext->TrustStatus.dwErrorStatus &
3049 CERT_TRUST_HAS_NOT_SUPPORTED_CRITICAL_EXT &&
3050 !(checks & CERT_CHAIN_POLICY_IGNORE_NOT_SUPPORTED_CRITICAL_EXT_FLAG))
3052 pPolicyStatus->dwError = CERT_E_CRITICAL;
3053 find_element_with_error(pChainContext,
3054 CERT_TRUST_HAS_NOT_SUPPORTED_CRITICAL_EXT, &pPolicyStatus->lChainIndex,
3055 &pPolicyStatus->lElementIndex);
3057 return TRUE;
3060 static BYTE msTestPubKey1[] = {
3061 0x30,0x47,0x02,0x40,0x81,0x55,0x22,0xb9,0x8a,0xa4,0x6f,0xed,0xd6,0xe7,0xd9,
3062 0x66,0x0f,0x55,0xbc,0xd7,0xcd,0xd5,0xbc,0x4e,0x40,0x02,0x21,0xa2,0xb1,0xf7,
3063 0x87,0x30,0x85,0x5e,0xd2,0xf2,0x44,0xb9,0xdc,0x9b,0x75,0xb6,0xfb,0x46,0x5f,
3064 0x42,0xb6,0x9d,0x23,0x36,0x0b,0xde,0x54,0x0f,0xcd,0xbd,0x1f,0x99,0x2a,0x10,
3065 0x58,0x11,0xcb,0x40,0xcb,0xb5,0xa7,0x41,0x02,0x03,0x01,0x00,0x01 };
3066 static BYTE msTestPubKey2[] = {
3067 0x30,0x47,0x02,0x40,0x9c,0x50,0x05,0x1d,0xe2,0x0e,0x4c,0x53,0xd8,0xd9,0xb5,
3068 0xe5,0xfd,0xe9,0xe3,0xad,0x83,0x4b,0x80,0x08,0xd9,0xdc,0xe8,0xe8,0x35,0xf8,
3069 0x11,0xf1,0xe9,0x9b,0x03,0x7a,0x65,0x64,0x76,0x35,0xce,0x38,0x2c,0xf2,0xb6,
3070 0x71,0x9e,0x06,0xd9,0xbf,0xbb,0x31,0x69,0xa3,0xf6,0x30,0xa0,0x78,0x7b,0x18,
3071 0xdd,0x50,0x4d,0x79,0x1e,0xeb,0x61,0xc1,0x02,0x03,0x01,0x00,0x01 };
3073 static void dump_authenticode_extra_chain_policy_para(
3074 AUTHENTICODE_EXTRA_CERT_CHAIN_POLICY_PARA *extraPara)
3076 if (extraPara)
3078 TRACE_(chain)("cbSize = %d\n", extraPara->cbSize);
3079 TRACE_(chain)("dwRegPolicySettings = %08x\n",
3080 extraPara->dwRegPolicySettings);
3081 TRACE_(chain)("pSignerInfo = %p\n", extraPara->pSignerInfo);
3085 static BOOL WINAPI verify_authenticode_policy(LPCSTR szPolicyOID,
3086 PCCERT_CHAIN_CONTEXT pChainContext, PCERT_CHAIN_POLICY_PARA pPolicyPara,
3087 PCERT_CHAIN_POLICY_STATUS pPolicyStatus)
3089 BOOL ret = verify_base_policy(szPolicyOID, pChainContext, pPolicyPara,
3090 pPolicyStatus);
3091 AUTHENTICODE_EXTRA_CERT_CHAIN_POLICY_PARA *extraPara = NULL;
3093 if (pPolicyPara)
3094 extraPara = pPolicyPara->pvExtraPolicyPara;
3095 if (TRACE_ON(chain))
3096 dump_authenticode_extra_chain_policy_para(extraPara);
3097 if (ret && pPolicyStatus->dwError == CERT_E_UNTRUSTEDROOT)
3099 CERT_PUBLIC_KEY_INFO msPubKey = { { 0 } };
3100 BOOL isMSTestRoot = FALSE;
3101 PCCERT_CONTEXT failingCert =
3102 pChainContext->rgpChain[pPolicyStatus->lChainIndex]->
3103 rgpElement[pPolicyStatus->lElementIndex]->pCertContext;
3104 DWORD i;
3105 CRYPT_DATA_BLOB keyBlobs[] = {
3106 { sizeof(msTestPubKey1), msTestPubKey1 },
3107 { sizeof(msTestPubKey2), msTestPubKey2 },
3110 /* Check whether the root is an MS test root */
3111 for (i = 0; !isMSTestRoot && i < ARRAY_SIZE(keyBlobs); i++)
3113 msPubKey.PublicKey.cbData = keyBlobs[i].cbData;
3114 msPubKey.PublicKey.pbData = keyBlobs[i].pbData;
3115 if (CertComparePublicKeyInfo(
3116 X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
3117 &failingCert->pCertInfo->SubjectPublicKeyInfo, &msPubKey))
3118 isMSTestRoot = TRUE;
3120 if (isMSTestRoot)
3121 pPolicyStatus->dwError = CERT_E_UNTRUSTEDTESTROOT;
3123 return ret;
3126 static BOOL WINAPI verify_basic_constraints_policy(LPCSTR szPolicyOID,
3127 PCCERT_CHAIN_CONTEXT pChainContext, PCERT_CHAIN_POLICY_PARA pPolicyPara,
3128 PCERT_CHAIN_POLICY_STATUS pPolicyStatus)
3130 pPolicyStatus->lChainIndex = pPolicyStatus->lElementIndex = -1;
3131 if (pChainContext->TrustStatus.dwErrorStatus &
3132 CERT_TRUST_INVALID_BASIC_CONSTRAINTS)
3134 pPolicyStatus->dwError = TRUST_E_BASIC_CONSTRAINTS;
3135 find_element_with_error(pChainContext,
3136 CERT_TRUST_INVALID_BASIC_CONSTRAINTS, &pPolicyStatus->lChainIndex,
3137 &pPolicyStatus->lElementIndex);
3139 else
3140 pPolicyStatus->dwError = NO_ERROR;
3141 return TRUE;
3144 static BOOL match_dns_to_subject_alt_name(const CERT_EXTENSION *ext,
3145 LPCWSTR server_name)
3147 BOOL matches = FALSE;
3148 CERT_ALT_NAME_INFO *subjectName;
3149 DWORD size;
3151 TRACE_(chain)("%s\n", debugstr_w(server_name));
3152 /* This could be spoofed by the embedded NULL vulnerability, since the
3153 * returned CERT_ALT_NAME_INFO doesn't have a way to indicate the
3154 * encoded length of a name. Fortunately CryptDecodeObjectEx fails if
3155 * the encoded form of the name contains a NULL.
3157 if (CryptDecodeObjectEx(X509_ASN_ENCODING, X509_ALTERNATE_NAME,
3158 ext->Value.pbData, ext->Value.cbData,
3159 CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL,
3160 &subjectName, &size))
3162 DWORD i;
3164 /* RFC 5280 states that multiple instances of each name type may exist,
3165 * in section 4.2.1.6:
3166 * "Multiple name forms, and multiple instances of each name form,
3167 * MAY be included."
3168 * It doesn't specify the behavior in such cases, but both RFC 2818
3169 * and RFC 2595 explicitly accept a certificate if any name matches.
3171 for (i = 0; !matches && i < subjectName->cAltEntry; i++)
3173 if (subjectName->rgAltEntry[i].dwAltNameChoice ==
3174 CERT_ALT_NAME_DNS_NAME)
3176 TRACE_(chain)("dNSName: %s\n", debugstr_w(
3177 subjectName->rgAltEntry[i].u.pwszDNSName));
3178 if (subjectName->rgAltEntry[i].u.pwszDNSName[0] == '*')
3180 LPCWSTR server_name_dot;
3182 /* Matching a wildcard: a wildcard matches a single name
3183 * component, which is terminated by a dot. RFC 1034
3184 * doesn't define whether multiple wildcards are allowed,
3185 * but I will assume that they are not until proven
3186 * otherwise. RFC 1034 also states that 'the "*" label
3187 * always matches at least one whole label and sometimes
3188 * more, but always whole labels.' Native crypt32 does not
3189 * match more than one label with a wildcard, so I do the
3190 * same here. Thus, a wildcard only accepts the first
3191 * label, then requires an exact match of the remaining
3192 * string.
3194 server_name_dot = wcschr(server_name, '.');
3195 if (server_name_dot)
3197 if (!wcsicmp(server_name_dot,
3198 subjectName->rgAltEntry[i].u.pwszDNSName + 1))
3199 matches = TRUE;
3202 else if (!wcsicmp(server_name,
3203 subjectName->rgAltEntry[i].u.pwszDNSName))
3204 matches = TRUE;
3207 LocalFree(subjectName);
3209 return matches;
3212 static BOOL find_matching_domain_component(const CERT_NAME_INFO *name,
3213 const WCHAR *component, size_t len)
3215 DWORD i, j;
3217 for (i = 0; i < name->cRDN; i++)
3218 for (j = 0; j < name->rgRDN[i].cRDNAttr; j++)
3219 if (!strcmp(szOID_DOMAIN_COMPONENT,
3220 name->rgRDN[i].rgRDNAttr[j].pszObjId))
3222 const CERT_RDN_ATTR *attr;
3224 attr = &name->rgRDN[i].rgRDNAttr[j];
3225 /* Compare with wcsnicmp rather than wcsicmp in order to avoid
3226 * a match with a string with an embedded NULL. The component
3227 * must match one domain component attribute's entire string
3228 * value with a case-insensitive match.
3230 if ((len == attr->Value.cbData / sizeof(WCHAR)) &&
3231 !wcsnicmp(component, (LPCWSTR)attr->Value.pbData, len))
3232 return TRUE;
3234 return FALSE;
3237 static BOOL match_domain_component(LPCWSTR allowed_component, DWORD allowed_len,
3238 LPCWSTR server_component, DWORD server_len, BOOL allow_wildcards,
3239 BOOL *see_wildcard)
3241 LPCWSTR allowed_ptr, server_ptr;
3242 BOOL matches = TRUE;
3244 *see_wildcard = FALSE;
3246 if (server_len < allowed_len)
3248 WARN_(chain)("domain component %s too short for %s\n",
3249 debugstr_wn(server_component, server_len),
3250 debugstr_wn(allowed_component, allowed_len));
3251 /* A domain component can't contain a wildcard character, so a domain
3252 * component shorter than the allowed string can't produce a match.
3254 return FALSE;
3256 for (allowed_ptr = allowed_component, server_ptr = server_component;
3257 matches && allowed_ptr - allowed_component < allowed_len;
3258 allowed_ptr++, server_ptr++)
3260 if (*allowed_ptr == '*')
3262 if (allowed_ptr - allowed_component < allowed_len - 1)
3264 WARN_(chain)("non-wildcard characters after wildcard not supported\n");
3265 matches = FALSE;
3267 else if (!allow_wildcards)
3269 WARN_(chain)("wildcard after non-wildcard component\n");
3270 matches = FALSE;
3272 else
3274 /* the preceding characters must have matched, so the rest of
3275 * the component also matches.
3277 *see_wildcard = TRUE;
3278 break;
3281 if (matches)
3282 matches = towlower(*allowed_ptr) == towlower(*server_ptr);
3284 if (matches && server_ptr - server_component < server_len)
3286 /* If there are unmatched characters in the server domain component,
3287 * the server domain only matches if the allowed string ended in a '*'.
3289 matches = *allowed_ptr == '*';
3291 return matches;
3294 static BOOL match_common_name(LPCWSTR server_name, const CERT_RDN_ATTR *nameAttr)
3296 LPCWSTR allowed = (LPCWSTR)nameAttr->Value.pbData;
3297 LPCWSTR allowed_component = allowed;
3298 DWORD allowed_len = nameAttr->Value.cbData / sizeof(WCHAR);
3299 LPCWSTR server_component = server_name;
3300 DWORD server_len = lstrlenW(server_name);
3301 BOOL matches = TRUE, allow_wildcards = TRUE;
3303 TRACE_(chain)("CN = %s\n", debugstr_wn(allowed_component, allowed_len));
3305 /* Remove trailing NULLs from the allowed name; while they shouldn't appear
3306 * in a certificate in the first place, they sometimes do, and they should
3307 * be ignored.
3309 while (allowed_len && allowed_component[allowed_len - 1] == 0)
3310 allowed_len--;
3312 /* From RFC 2818 (HTTP over TLS), section 3.1:
3313 * "Names may contain the wildcard character * which is considered to match
3314 * any single domain name component or component fragment. E.g.,
3315 * *.a.com matches foo.a.com but not bar.foo.a.com. f*.com matches foo.com
3316 * but not bar.com."
3318 * And from RFC 2595 (Using TLS with IMAP, POP3 and ACAP), section 2.4:
3319 * "A "*" wildcard character MAY be used as the left-most name component in
3320 * the certificate. For example, *.example.com would match a.example.com,
3321 * foo.example.com, etc. but would not match example.com."
3323 * There are other protocols which use TLS, and none of them is
3324 * authoritative. This accepts certificates in common usage, e.g.
3325 * *.domain.com matches www.domain.com but not domain.com, and
3326 * www*.domain.com matches www1.domain.com but not mail.domain.com.
3328 do {
3329 LPCWSTR allowed_dot, server_dot;
3331 allowed_dot = wmemchr(allowed_component, '.',
3332 allowed_len - (allowed_component - allowed));
3333 server_dot = wmemchr(server_component, '.',
3334 server_len - (server_component - server_name));
3335 /* The number of components must match */
3336 if ((!allowed_dot && server_dot) || (allowed_dot && !server_dot))
3338 if (!allowed_dot)
3339 WARN_(chain)("%s: too many components for CN=%s\n",
3340 debugstr_w(server_name), debugstr_wn(allowed, allowed_len));
3341 else
3342 WARN_(chain)("%s: not enough components for CN=%s\n",
3343 debugstr_w(server_name), debugstr_wn(allowed, allowed_len));
3344 matches = FALSE;
3346 else
3348 LPCWSTR allowed_end, server_end;
3349 BOOL has_wildcard;
3351 allowed_end = allowed_dot ? allowed_dot : allowed + allowed_len;
3352 server_end = server_dot ? server_dot : server_name + server_len;
3353 matches = match_domain_component(allowed_component,
3354 allowed_end - allowed_component, server_component,
3355 server_end - server_component, allow_wildcards, &has_wildcard);
3356 /* Once a non-wildcard component is seen, no wildcard components
3357 * may follow
3359 if (!has_wildcard)
3360 allow_wildcards = FALSE;
3361 if (matches)
3363 allowed_component = allowed_dot ? allowed_dot + 1 : allowed_end;
3364 server_component = server_dot ? server_dot + 1 : server_end;
3367 } while (matches && allowed_component &&
3368 allowed_component - allowed < allowed_len &&
3369 server_component && server_component - server_name < server_len);
3370 TRACE_(chain)("returning %d\n", matches);
3371 return matches;
3374 static BOOL match_dns_to_subject_dn(PCCERT_CONTEXT cert, LPCWSTR server_name)
3376 BOOL matches = FALSE;
3377 CERT_NAME_INFO *name;
3378 DWORD size;
3380 TRACE_(chain)("%s\n", debugstr_w(server_name));
3381 if (CryptDecodeObjectEx(X509_ASN_ENCODING, X509_UNICODE_NAME,
3382 cert->pCertInfo->Subject.pbData, cert->pCertInfo->Subject.cbData,
3383 CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL,
3384 &name, &size))
3386 /* If the subject distinguished name contains any name components,
3387 * make sure all of them are present.
3389 if (CertFindRDNAttr(szOID_DOMAIN_COMPONENT, name))
3391 LPCWSTR ptr = server_name;
3393 do {
3394 LPCWSTR dot = wcschr(ptr, '.'), end;
3395 /* 254 is the maximum DNS label length, see RFC 1035 */
3396 size_t len;
3398 end = dot ? dot : ptr + lstrlenW(ptr);
3399 len = end - ptr;
3400 if (len >= 255)
3402 WARN_(chain)("domain component %s too long\n",
3403 debugstr_wn(ptr, len));
3404 matches = FALSE;
3406 else matches = find_matching_domain_component(name, ptr, len);
3408 ptr = dot ? dot + 1 : end;
3409 } while (matches && ptr && *ptr);
3411 else
3413 DWORD i, j;
3415 /* If the certificate isn't using a DN attribute in the name, make
3416 * make sure at least one common name matches. From RFC 2818,
3417 * section 3.1:
3418 * "If more than one identity of a given type is present in the
3419 * certificate (e.g., more than one dNSName name, a match in any
3420 * one of the set is considered acceptable.)"
3422 for (i = 0; !matches && i < name->cRDN; i++)
3423 for (j = 0; !matches && j < name->rgRDN[i].cRDNAttr; j++)
3425 PCERT_RDN_ATTR attr = &name->rgRDN[i].rgRDNAttr[j];
3427 if (attr->pszObjId && !strcmp(szOID_COMMON_NAME,
3428 attr->pszObjId))
3429 matches = match_common_name(server_name, attr);
3432 LocalFree(name);
3434 return matches;
3437 static void dump_ssl_extra_chain_policy_para(HTTPSPolicyCallbackData *sslPara)
3439 if (sslPara)
3441 TRACE_(chain)("cbSize = %d\n", sslPara->u.cbSize);
3442 TRACE_(chain)("dwAuthType = %d\n", sslPara->dwAuthType);
3443 TRACE_(chain)("fdwChecks = %08x\n", sslPara->fdwChecks);
3444 TRACE_(chain)("pwszServerName = %s\n",
3445 debugstr_w(sslPara->pwszServerName));
3449 static BOOL WINAPI verify_ssl_policy(LPCSTR szPolicyOID,
3450 PCCERT_CHAIN_CONTEXT pChainContext, PCERT_CHAIN_POLICY_PARA pPolicyPara,
3451 PCERT_CHAIN_POLICY_STATUS pPolicyStatus)
3453 HTTPSPolicyCallbackData *sslPara = NULL;
3454 DWORD checks = 0, baseChecks = 0;
3456 if (pPolicyPara)
3458 baseChecks = pPolicyPara->dwFlags;
3459 sslPara = pPolicyPara->pvExtraPolicyPara;
3461 if (TRACE_ON(chain))
3462 dump_ssl_extra_chain_policy_para(sslPara);
3463 if (sslPara && sslPara->u.cbSize >= sizeof(HTTPSPolicyCallbackData))
3464 checks = sslPara->fdwChecks;
3465 pPolicyStatus->lChainIndex = pPolicyStatus->lElementIndex = -1;
3466 if (pChainContext->TrustStatus.dwErrorStatus &
3467 CERT_TRUST_IS_NOT_SIGNATURE_VALID)
3469 pPolicyStatus->dwError = TRUST_E_CERT_SIGNATURE;
3470 find_element_with_error(pChainContext,
3471 CERT_TRUST_IS_NOT_SIGNATURE_VALID, &pPolicyStatus->lChainIndex,
3472 &pPolicyStatus->lElementIndex);
3474 else if (pChainContext->TrustStatus.dwErrorStatus &
3475 CERT_TRUST_IS_UNTRUSTED_ROOT &&
3476 !(checks & SECURITY_FLAG_IGNORE_UNKNOWN_CA) &&
3477 !(baseChecks & CERT_CHAIN_POLICY_ALLOW_UNKNOWN_CA_FLAG))
3479 pPolicyStatus->dwError = CERT_E_UNTRUSTEDROOT;
3480 find_element_with_error(pChainContext,
3481 CERT_TRUST_IS_UNTRUSTED_ROOT, &pPolicyStatus->lChainIndex,
3482 &pPolicyStatus->lElementIndex);
3484 else if (pChainContext->TrustStatus.dwErrorStatus & CERT_TRUST_IS_CYCLIC)
3486 pPolicyStatus->dwError = CERT_E_UNTRUSTEDROOT;
3487 find_element_with_error(pChainContext,
3488 CERT_TRUST_IS_CYCLIC, &pPolicyStatus->lChainIndex,
3489 &pPolicyStatus->lElementIndex);
3490 /* For a cyclic chain, which element is a cycle isn't meaningful */
3491 pPolicyStatus->lElementIndex = -1;
3493 else if (pChainContext->TrustStatus.dwErrorStatus &
3494 CERT_TRUST_IS_NOT_TIME_VALID &&
3495 !(checks & SECURITY_FLAG_IGNORE_CERT_DATE_INVALID))
3497 pPolicyStatus->dwError = CERT_E_EXPIRED;
3498 find_element_with_error(pChainContext,
3499 CERT_TRUST_IS_NOT_TIME_VALID, &pPolicyStatus->lChainIndex,
3500 &pPolicyStatus->lElementIndex);
3502 else if (pChainContext->TrustStatus.dwErrorStatus &
3503 CERT_TRUST_IS_NOT_VALID_FOR_USAGE &&
3504 !(checks & SECURITY_FLAG_IGNORE_WRONG_USAGE))
3506 pPolicyStatus->dwError = CERT_E_WRONG_USAGE;
3507 find_element_with_error(pChainContext,
3508 CERT_TRUST_IS_NOT_VALID_FOR_USAGE, &pPolicyStatus->lChainIndex,
3509 &pPolicyStatus->lElementIndex);
3511 else if (pChainContext->TrustStatus.dwErrorStatus &
3512 CERT_TRUST_IS_REVOKED && !(checks & SECURITY_FLAG_IGNORE_REVOCATION))
3514 pPolicyStatus->dwError = CERT_E_REVOKED;
3515 find_element_with_error(pChainContext,
3516 CERT_TRUST_IS_REVOKED, &pPolicyStatus->lChainIndex,
3517 &pPolicyStatus->lElementIndex);
3519 else if (pChainContext->TrustStatus.dwErrorStatus &
3520 CERT_TRUST_IS_OFFLINE_REVOCATION &&
3521 !(checks & SECURITY_FLAG_IGNORE_REVOCATION))
3523 pPolicyStatus->dwError = CERT_E_REVOCATION_FAILURE;
3524 find_element_with_error(pChainContext,
3525 CERT_TRUST_IS_OFFLINE_REVOCATION, &pPolicyStatus->lChainIndex,
3526 &pPolicyStatus->lElementIndex);
3528 else if (pChainContext->TrustStatus.dwErrorStatus &
3529 CERT_TRUST_HAS_NOT_SUPPORTED_CRITICAL_EXT)
3531 pPolicyStatus->dwError = CERT_E_CRITICAL;
3532 find_element_with_error(pChainContext,
3533 CERT_TRUST_HAS_NOT_SUPPORTED_CRITICAL_EXT, &pPolicyStatus->lChainIndex,
3534 &pPolicyStatus->lElementIndex);
3536 else
3537 pPolicyStatus->dwError = NO_ERROR;
3538 /* We only need bother checking whether the name in the end certificate
3539 * matches if the chain is otherwise okay.
3541 if (!pPolicyStatus->dwError && pPolicyPara &&
3542 pPolicyPara->cbSize >= sizeof(CERT_CHAIN_POLICY_PARA))
3544 if (sslPara && sslPara->u.cbSize >= sizeof(HTTPSPolicyCallbackData))
3546 if (sslPara->dwAuthType == AUTHTYPE_SERVER &&
3547 sslPara->pwszServerName &&
3548 !(checks & SECURITY_FLAG_IGNORE_CERT_CN_INVALID))
3550 PCCERT_CONTEXT cert;
3551 PCERT_EXTENSION altNameExt;
3552 BOOL matches;
3554 cert = pChainContext->rgpChain[0]->rgpElement[0]->pCertContext;
3555 altNameExt = get_subject_alt_name_ext(cert->pCertInfo);
3556 /* If the alternate name extension exists, the name it contains
3557 * is bound to the certificate, so make sure the name matches
3558 * it. Otherwise, look for the server name in the subject
3559 * distinguished name. RFC5280, section 4.2.1.6:
3560 * "Whenever such identities are to be bound into a
3561 * certificate, the subject alternative name (or issuer
3562 * alternative name) extension MUST be used; however, a DNS
3563 * name MAY also be represented in the subject field using the
3564 * domainComponent attribute."
3566 if (altNameExt)
3567 matches = match_dns_to_subject_alt_name(altNameExt,
3568 sslPara->pwszServerName);
3569 else
3570 matches = match_dns_to_subject_dn(cert,
3571 sslPara->pwszServerName);
3572 if (!matches)
3574 pPolicyStatus->dwError = CERT_E_CN_NO_MATCH;
3575 pPolicyStatus->lChainIndex = 0;
3576 pPolicyStatus->lElementIndex = 0;
3581 return TRUE;
3584 static BYTE msPubKey1[] = {
3585 0x30,0x82,0x01,0x0a,0x02,0x82,0x01,0x01,0x00,0xdf,0x08,0xba,0xe3,0x3f,0x6e,
3586 0x64,0x9b,0xf5,0x89,0xaf,0x28,0x96,0x4a,0x07,0x8f,0x1b,0x2e,0x8b,0x3e,0x1d,
3587 0xfc,0xb8,0x80,0x69,0xa3,0xa1,0xce,0xdb,0xdf,0xb0,0x8e,0x6c,0x89,0x76,0x29,
3588 0x4f,0xca,0x60,0x35,0x39,0xad,0x72,0x32,0xe0,0x0b,0xae,0x29,0x3d,0x4c,0x16,
3589 0xd9,0x4b,0x3c,0x9d,0xda,0xc5,0xd3,0xd1,0x09,0xc9,0x2c,0x6f,0xa6,0xc2,0x60,
3590 0x53,0x45,0xdd,0x4b,0xd1,0x55,0xcd,0x03,0x1c,0xd2,0x59,0x56,0x24,0xf3,0xe5,
3591 0x78,0xd8,0x07,0xcc,0xd8,0xb3,0x1f,0x90,0x3f,0xc0,0x1a,0x71,0x50,0x1d,0x2d,
3592 0xa7,0x12,0x08,0x6d,0x7c,0xb0,0x86,0x6c,0xc7,0xba,0x85,0x32,0x07,0xe1,0x61,
3593 0x6f,0xaf,0x03,0xc5,0x6d,0xe5,0xd6,0xa1,0x8f,0x36,0xf6,0xc1,0x0b,0xd1,0x3e,
3594 0x69,0x97,0x48,0x72,0xc9,0x7f,0xa4,0xc8,0xc2,0x4a,0x4c,0x7e,0xa1,0xd1,0x94,
3595 0xa6,0xd7,0xdc,0xeb,0x05,0x46,0x2e,0xb8,0x18,0xb4,0x57,0x1d,0x86,0x49,0xdb,
3596 0x69,0x4a,0x2c,0x21,0xf5,0x5e,0x0f,0x54,0x2d,0x5a,0x43,0xa9,0x7a,0x7e,0x6a,
3597 0x8e,0x50,0x4d,0x25,0x57,0xa1,0xbf,0x1b,0x15,0x05,0x43,0x7b,0x2c,0x05,0x8d,
3598 0xbd,0x3d,0x03,0x8c,0x93,0x22,0x7d,0x63,0xea,0x0a,0x57,0x05,0x06,0x0a,0xdb,
3599 0x61,0x98,0x65,0x2d,0x47,0x49,0xa8,0xe7,0xe6,0x56,0x75,0x5c,0xb8,0x64,0x08,
3600 0x63,0xa9,0x30,0x40,0x66,0xb2,0xf9,0xb6,0xe3,0x34,0xe8,0x67,0x30,0xe1,0x43,
3601 0x0b,0x87,0xff,0xc9,0xbe,0x72,0x10,0x5e,0x23,0xf0,0x9b,0xa7,0x48,0x65,0xbf,
3602 0x09,0x88,0x7b,0xcd,0x72,0xbc,0x2e,0x79,0x9b,0x7b,0x02,0x03,0x01,0x00,0x01 };
3603 static BYTE msPubKey2[] = {
3604 0x30,0x82,0x01,0x0a,0x02,0x82,0x01,0x01,0x00,0xa9,0x02,0xbd,0xc1,0x70,0xe6,
3605 0x3b,0xf2,0x4e,0x1b,0x28,0x9f,0x97,0x78,0x5e,0x30,0xea,0xa2,0xa9,0x8d,0x25,
3606 0x5f,0xf8,0xfe,0x95,0x4c,0xa3,0xb7,0xfe,0x9d,0xa2,0x20,0x3e,0x7c,0x51,0xa2,
3607 0x9b,0xa2,0x8f,0x60,0x32,0x6b,0xd1,0x42,0x64,0x79,0xee,0xac,0x76,0xc9,0x54,
3608 0xda,0xf2,0xeb,0x9c,0x86,0x1c,0x8f,0x9f,0x84,0x66,0xb3,0xc5,0x6b,0x7a,0x62,
3609 0x23,0xd6,0x1d,0x3c,0xde,0x0f,0x01,0x92,0xe8,0x96,0xc4,0xbf,0x2d,0x66,0x9a,
3610 0x9a,0x68,0x26,0x99,0xd0,0x3a,0x2c,0xbf,0x0c,0xb5,0x58,0x26,0xc1,0x46,0xe7,
3611 0x0a,0x3e,0x38,0x96,0x2c,0xa9,0x28,0x39,0xa8,0xec,0x49,0x83,0x42,0xe3,0x84,
3612 0x0f,0xbb,0x9a,0x6c,0x55,0x61,0xac,0x82,0x7c,0xa1,0x60,0x2d,0x77,0x4c,0xe9,
3613 0x99,0xb4,0x64,0x3b,0x9a,0x50,0x1c,0x31,0x08,0x24,0x14,0x9f,0xa9,0xe7,0x91,
3614 0x2b,0x18,0xe6,0x3d,0x98,0x63,0x14,0x60,0x58,0x05,0x65,0x9f,0x1d,0x37,0x52,
3615 0x87,0xf7,0xa7,0xef,0x94,0x02,0xc6,0x1b,0xd3,0xbf,0x55,0x45,0xb3,0x89,0x80,
3616 0xbf,0x3a,0xec,0x54,0x94,0x4e,0xae,0xfd,0xa7,0x7a,0x6d,0x74,0x4e,0xaf,0x18,
3617 0xcc,0x96,0x09,0x28,0x21,0x00,0x57,0x90,0x60,0x69,0x37,0xbb,0x4b,0x12,0x07,
3618 0x3c,0x56,0xff,0x5b,0xfb,0xa4,0x66,0x0a,0x08,0xa6,0xd2,0x81,0x56,0x57,0xef,
3619 0xb6,0x3b,0x5e,0x16,0x81,0x77,0x04,0xda,0xf6,0xbe,0xae,0x80,0x95,0xfe,0xb0,
3620 0xcd,0x7f,0xd6,0xa7,0x1a,0x72,0x5c,0x3c,0xca,0xbc,0xf0,0x08,0xa3,0x22,0x30,
3621 0xb3,0x06,0x85,0xc9,0xb3,0x20,0x77,0x13,0x85,0xdf,0x02,0x03,0x01,0x00,0x01 };
3622 static BYTE msPubKey3[] = {
3623 0x30,0x82,0x02,0x0a,0x02,0x82,0x02,0x01,0x00,0xf3,0x5d,0xfa,0x80,0x67,0xd4,
3624 0x5a,0xa7,0xa9,0x0c,0x2c,0x90,0x20,0xd0,0x35,0x08,0x3c,0x75,0x84,0xcd,0xb7,
3625 0x07,0x89,0x9c,0x89,0xda,0xde,0xce,0xc3,0x60,0xfa,0x91,0x68,0x5a,0x9e,0x94,
3626 0x71,0x29,0x18,0x76,0x7c,0xc2,0xe0,0xc8,0x25,0x76,0x94,0x0e,0x58,0xfa,0x04,
3627 0x34,0x36,0xe6,0xdf,0xaf,0xf7,0x80,0xba,0xe9,0x58,0x0b,0x2b,0x93,0xe5,0x9d,
3628 0x05,0xe3,0x77,0x22,0x91,0xf7,0x34,0x64,0x3c,0x22,0x91,0x1d,0x5e,0xe1,0x09,
3629 0x90,0xbc,0x14,0xfe,0xfc,0x75,0x58,0x19,0xe1,0x79,0xb7,0x07,0x92,0xa3,0xae,
3630 0x88,0x59,0x08,0xd8,0x9f,0x07,0xca,0x03,0x58,0xfc,0x68,0x29,0x6d,0x32,0xd7,
3631 0xd2,0xa8,0xcb,0x4b,0xfc,0xe1,0x0b,0x48,0x32,0x4f,0xe6,0xeb,0xb8,0xad,0x4f,
3632 0xe4,0x5c,0x6f,0x13,0x94,0x99,0xdb,0x95,0xd5,0x75,0xdb,0xa8,0x1a,0xb7,0x94,
3633 0x91,0xb4,0x77,0x5b,0xf5,0x48,0x0c,0x8f,0x6a,0x79,0x7d,0x14,0x70,0x04,0x7d,
3634 0x6d,0xaf,0x90,0xf5,0xda,0x70,0xd8,0x47,0xb7,0xbf,0x9b,0x2f,0x6c,0xe7,0x05,
3635 0xb7,0xe1,0x11,0x60,0xac,0x79,0x91,0x14,0x7c,0xc5,0xd6,0xa6,0xe4,0xe1,0x7e,
3636 0xd5,0xc3,0x7e,0xe5,0x92,0xd2,0x3c,0x00,0xb5,0x36,0x82,0xde,0x79,0xe1,0x6d,
3637 0xf3,0xb5,0x6e,0xf8,0x9f,0x33,0xc9,0xcb,0x52,0x7d,0x73,0x98,0x36,0xdb,0x8b,
3638 0xa1,0x6b,0xa2,0x95,0x97,0x9b,0xa3,0xde,0xc2,0x4d,0x26,0xff,0x06,0x96,0x67,
3639 0x25,0x06,0xc8,0xe7,0xac,0xe4,0xee,0x12,0x33,0x95,0x31,0x99,0xc8,0x35,0x08,
3640 0x4e,0x34,0xca,0x79,0x53,0xd5,0xb5,0xbe,0x63,0x32,0x59,0x40,0x36,0xc0,0xa5,
3641 0x4e,0x04,0x4d,0x3d,0xdb,0x5b,0x07,0x33,0xe4,0x58,0xbf,0xef,0x3f,0x53,0x64,
3642 0xd8,0x42,0x59,0x35,0x57,0xfd,0x0f,0x45,0x7c,0x24,0x04,0x4d,0x9e,0xd6,0x38,
3643 0x74,0x11,0x97,0x22,0x90,0xce,0x68,0x44,0x74,0x92,0x6f,0xd5,0x4b,0x6f,0xb0,
3644 0x86,0xe3,0xc7,0x36,0x42,0xa0,0xd0,0xfc,0xc1,0xc0,0x5a,0xf9,0xa3,0x61,0xb9,
3645 0x30,0x47,0x71,0x96,0x0a,0x16,0xb0,0x91,0xc0,0x42,0x95,0xef,0x10,0x7f,0x28,
3646 0x6a,0xe3,0x2a,0x1f,0xb1,0xe4,0xcd,0x03,0x3f,0x77,0x71,0x04,0xc7,0x20,0xfc,
3647 0x49,0x0f,0x1d,0x45,0x88,0xa4,0xd7,0xcb,0x7e,0x88,0xad,0x8e,0x2d,0xec,0x45,
3648 0xdb,0xc4,0x51,0x04,0xc9,0x2a,0xfc,0xec,0x86,0x9e,0x9a,0x11,0x97,0x5b,0xde,
3649 0xce,0x53,0x88,0xe6,0xe2,0xb7,0xfd,0xac,0x95,0xc2,0x28,0x40,0xdb,0xef,0x04,
3650 0x90,0xdf,0x81,0x33,0x39,0xd9,0xb2,0x45,0xa5,0x23,0x87,0x06,0xa5,0x55,0x89,
3651 0x31,0xbb,0x06,0x2d,0x60,0x0e,0x41,0x18,0x7d,0x1f,0x2e,0xb5,0x97,0xcb,0x11,
3652 0xeb,0x15,0xd5,0x24,0xa5,0x94,0xef,0x15,0x14,0x89,0xfd,0x4b,0x73,0xfa,0x32,
3653 0x5b,0xfc,0xd1,0x33,0x00,0xf9,0x59,0x62,0x70,0x07,0x32,0xea,0x2e,0xab,0x40,
3654 0x2d,0x7b,0xca,0xdd,0x21,0x67,0x1b,0x30,0x99,0x8f,0x16,0xaa,0x23,0xa8,0x41,
3655 0xd1,0xb0,0x6e,0x11,0x9b,0x36,0xc4,0xde,0x40,0x74,0x9c,0xe1,0x58,0x65,0xc1,
3656 0x60,0x1e,0x7a,0x5b,0x38,0xc8,0x8f,0xbb,0x04,0x26,0x7c,0xd4,0x16,0x40,0xe5,
3657 0xb6,0x6b,0x6c,0xaa,0x86,0xfd,0x00,0xbf,0xce,0xc1,0x35,0x02,0x03,0x01,0x00,
3658 0x01 };
3659 /* from Microsoft Root Certificate Authority 2010 */
3660 static BYTE msPubKey4[] = {
3661 0x30,0x82,0x02,0x0a,0x02,0x82,0x02,0x01,0x00,0xb9,0x08,0x9e,0x28,0xe4,0xe4,
3662 0xec,0x06,0x4e,0x50,0x68,0xb3,0x41,0xc5,0x7b,0xeb,0xae,0xb6,0x8e,0xaf,0x81,
3663 0xba,0x22,0x44,0x1f,0x65,0x34,0x69,0x4c,0xbe,0x70,0x40,0x17,0xf2,0x16,0x7b,
3664 0xe2,0x79,0xfd,0x86,0xed,0x0d,0x39,0xf4,0x1b,0xa8,0xad,0x92,0x90,0x1e,0xcb,
3665 0x3d,0x76,0x8f,0x5a,0xd9,0xb5,0x91,0x10,0x2e,0x3c,0x05,0x8d,0x8a,0x6d,0x24,
3666 0x54,0xe7,0x1f,0xed,0x56,0xad,0x83,0xb4,0x50,0x9c,0x15,0xa5,0x17,0x74,0x88,
3667 0x59,0x20,0xfc,0x08,0xc5,0x84,0x76,0xd3,0x68,0xd4,0x6f,0x28,0x78,0xce,0x5c,
3668 0xb8,0xf3,0x50,0x90,0x44,0xff,0xe3,0x63,0x5f,0xbe,0xa1,0x9a,0x2c,0x96,0x15,
3669 0x04,0xd6,0x07,0xfe,0x1e,0x84,0x21,0xe0,0x42,0x31,0x11,0xc4,0x28,0x36,0x94,
3670 0xcf,0x50,0xa4,0x62,0x9e,0xc9,0xd6,0xab,0x71,0x00,0xb2,0x5b,0x0c,0xe6,0x96,
3671 0xd4,0x0a,0x24,0x96,0xf5,0xff,0xc6,0xd5,0xb7,0x1b,0xd7,0xcb,0xb7,0x21,0x62,
3672 0xaf,0x12,0xdc,0xa1,0x5d,0x37,0xe3,0x1a,0xfb,0x1a,0x46,0x98,0xc0,0x9b,0xc0,
3673 0xe7,0x63,0x1f,0x2a,0x08,0x93,0x02,0x7e,0x1e,0x6a,0x8e,0xf2,0x9f,0x18,0x89,
3674 0xe4,0x22,0x85,0xa2,0xb1,0x84,0x57,0x40,0xff,0xf5,0x0e,0xd8,0x6f,0x9c,0xed,
3675 0xe2,0x45,0x31,0x01,0xcd,0x17,0xe9,0x7f,0xb0,0x81,0x45,0xe3,0xaa,0x21,0x40,
3676 0x26,0xa1,0x72,0xaa,0xa7,0x4f,0x3c,0x01,0x05,0x7e,0xee,0x83,0x58,0xb1,0x5e,
3677 0x06,0x63,0x99,0x62,0x91,0x78,0x82,0xb7,0x0d,0x93,0x0c,0x24,0x6a,0xb4,0x1b,
3678 0xdb,0x27,0xec,0x5f,0x95,0x04,0x3f,0x93,0x4a,0x30,0xf5,0x97,0x18,0xb3,0xa7,
3679 0xf9,0x19,0xa7,0x93,0x33,0x1d,0x01,0xc8,0xdb,0x22,0x52,0x5c,0xd7,0x25,0xc9,
3680 0x46,0xf9,0xa2,0xfb,0x87,0x59,0x43,0xbe,0x9b,0x62,0xb1,0x8d,0x2d,0x86,0x44,
3681 0x1a,0x46,0xac,0x78,0x61,0x7e,0x30,0x09,0xfa,0xae,0x89,0xc4,0x41,0x2a,0x22,
3682 0x66,0x03,0x91,0x39,0x45,0x9c,0xc7,0x8b,0x0c,0xa8,0xca,0x0d,0x2f,0xfb,0x52,
3683 0xea,0x0c,0xf7,0x63,0x33,0x23,0x9d,0xfe,0xb0,0x1f,0xad,0x67,0xd6,0xa7,0x50,
3684 0x03,0xc6,0x04,0x70,0x63,0xb5,0x2c,0xb1,0x86,0x5a,0x43,0xb7,0xfb,0xae,0xf9,
3685 0x6e,0x29,0x6e,0x21,0x21,0x41,0x26,0x06,0x8c,0xc9,0xc3,0xee,0xb0,0xc2,0x85,
3686 0x93,0xa1,0xb9,0x85,0xd9,0xe6,0x32,0x6c,0x4b,0x4c,0x3f,0xd6,0x5d,0xa3,0xe5,
3687 0xb5,0x9d,0x77,0xc3,0x9c,0xc0,0x55,0xb7,0x74,0x00,0xe3,0xb8,0x38,0xab,0x83,
3688 0x97,0x50,0xe1,0x9a,0x42,0x24,0x1d,0xc6,0xc0,0xa3,0x30,0xd1,0x1a,0x5a,0xc8,
3689 0x52,0x34,0xf7,0x73,0xf1,0xc7,0x18,0x1f,0x33,0xad,0x7a,0xec,0xcb,0x41,0x60,
3690 0xf3,0x23,0x94,0x20,0xc2,0x48,0x45,0xac,0x5c,0x51,0xc6,0x2e,0x80,0xc2,0xe2,
3691 0x77,0x15,0xbd,0x85,0x87,0xed,0x36,0x9d,0x96,0x91,0xee,0x00,0xb5,0xa3,0x70,
3692 0xec,0x9f,0xe3,0x8d,0x80,0x68,0x83,0x76,0xba,0xaf,0x5d,0x70,0x52,0x22,0x16,
3693 0xe2,0x66,0xfb,0xba,0xb3,0xc5,0xc2,0xf7,0x3e,0x2f,0x77,0xa6,0xca,0xde,0xc1,
3694 0xa6,0xc6,0x48,0x4c,0xc3,0x37,0x51,0x23,0xd3,0x27,0xd7,0xb8,0x4e,0x70,0x96,
3695 0xf0,0xa1,0x44,0x76,0xaf,0x78,0xcf,0x9a,0xe1,0x66,0x13,0x02,0x03,0x01,0x00,
3696 0x01 };
3698 static BOOL WINAPI verify_ms_root_policy(LPCSTR szPolicyOID,
3699 PCCERT_CHAIN_CONTEXT pChainContext, PCERT_CHAIN_POLICY_PARA pPolicyPara,
3700 PCERT_CHAIN_POLICY_STATUS pPolicyStatus)
3702 BOOL isMSRoot = FALSE;
3704 CERT_PUBLIC_KEY_INFO msPubKey = { { 0 } };
3705 DWORD i;
3706 CRYPT_DATA_BLOB keyBlobs[] = {
3707 { sizeof(msPubKey1), msPubKey1 },
3708 { sizeof(msPubKey2), msPubKey2 },
3709 { sizeof(msPubKey3), msPubKey3 },
3710 { sizeof(msPubKey4), msPubKey4 },
3712 PCERT_SIMPLE_CHAIN rootChain =
3713 pChainContext->rgpChain[pChainContext->cChain - 1];
3714 PCCERT_CONTEXT root =
3715 rootChain->rgpElement[rootChain->cElement - 1]->pCertContext;
3717 for (i = 0; !isMSRoot && i < ARRAY_SIZE(keyBlobs); i++)
3719 msPubKey.PublicKey.cbData = keyBlobs[i].cbData;
3720 msPubKey.PublicKey.pbData = keyBlobs[i].pbData;
3721 if (CertComparePublicKeyInfo(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
3722 &root->pCertInfo->SubjectPublicKeyInfo, &msPubKey)) isMSRoot = TRUE;
3725 pPolicyStatus->lChainIndex = pPolicyStatus->lElementIndex = 0;
3727 if (isMSRoot)
3728 pPolicyStatus->dwError = 0;
3729 else
3730 pPolicyStatus->dwError = CERT_E_UNTRUSTEDROOT;
3732 return TRUE;
3735 typedef BOOL (WINAPI *CertVerifyCertificateChainPolicyFunc)(LPCSTR szPolicyOID,
3736 PCCERT_CHAIN_CONTEXT pChainContext, PCERT_CHAIN_POLICY_PARA pPolicyPara,
3737 PCERT_CHAIN_POLICY_STATUS pPolicyStatus);
3739 static void dump_policy_para(PCERT_CHAIN_POLICY_PARA para)
3741 if (para)
3743 TRACE_(chain)("cbSize = %d\n", para->cbSize);
3744 TRACE_(chain)("dwFlags = %08x\n", para->dwFlags);
3745 TRACE_(chain)("pvExtraPolicyPara = %p\n", para->pvExtraPolicyPara);
3749 BOOL WINAPI CertVerifyCertificateChainPolicy(LPCSTR szPolicyOID,
3750 PCCERT_CHAIN_CONTEXT pChainContext, PCERT_CHAIN_POLICY_PARA pPolicyPara,
3751 PCERT_CHAIN_POLICY_STATUS pPolicyStatus)
3753 static HCRYPTOIDFUNCSET set = NULL;
3754 BOOL ret = FALSE;
3755 CertVerifyCertificateChainPolicyFunc verifyPolicy = NULL;
3756 HCRYPTOIDFUNCADDR hFunc = NULL;
3758 TRACE("(%s, %p, %p, %p)\n", debugstr_a(szPolicyOID), pChainContext,
3759 pPolicyPara, pPolicyStatus);
3760 if (TRACE_ON(chain))
3761 dump_policy_para(pPolicyPara);
3763 if (IS_INTOID(szPolicyOID))
3765 switch (LOWORD(szPolicyOID))
3767 case LOWORD(CERT_CHAIN_POLICY_BASE):
3768 verifyPolicy = verify_base_policy;
3769 break;
3770 case LOWORD(CERT_CHAIN_POLICY_AUTHENTICODE):
3771 verifyPolicy = verify_authenticode_policy;
3772 break;
3773 case LOWORD(CERT_CHAIN_POLICY_SSL):
3774 verifyPolicy = verify_ssl_policy;
3775 break;
3776 case LOWORD(CERT_CHAIN_POLICY_BASIC_CONSTRAINTS):
3777 verifyPolicy = verify_basic_constraints_policy;
3778 break;
3779 case LOWORD(CERT_CHAIN_POLICY_MICROSOFT_ROOT):
3780 verifyPolicy = verify_ms_root_policy;
3781 break;
3782 default:
3783 FIXME("unimplemented for %d\n", LOWORD(szPolicyOID));
3786 if (!verifyPolicy)
3788 if (!set)
3789 set = CryptInitOIDFunctionSet(
3790 CRYPT_OID_VERIFY_CERTIFICATE_CHAIN_POLICY_FUNC, 0);
3791 CryptGetOIDFunctionAddress(set, X509_ASN_ENCODING, szPolicyOID, 0,
3792 (void **)&verifyPolicy, &hFunc);
3794 if (verifyPolicy)
3795 ret = verifyPolicy(szPolicyOID, pChainContext, pPolicyPara,
3796 pPolicyStatus);
3797 if (hFunc)
3798 CryptFreeOIDFunctionAddress(hFunc, 0);
3799 TRACE("returning %d (%08x)\n", ret, pPolicyStatus->dwError);
3800 return ret;