crypt32: Implement CertVerifyCertificateChainPolicy for the basic constraints policy.
[wine] / dlls / crypt32 / chain.c
1 /*
2  * Copyright 2006 Juan Lang
3  *
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.
8  *
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.
13  *
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
17  *
18  */
19 #include <stdarg.h>
20 #include "windef.h"
21 #include "winbase.h"
22 #include "wincrypt.h"
23 #include "wine/debug.h"
24 #include "crypt32_private.h"
25
26 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
27
28 #define DEFAULT_CYCLE_MODULUS 7
29
30 static HCERTCHAINENGINE CRYPT_defaultChainEngine;
31
32 /* This represents a subset of a certificate chain engine:  it doesn't include
33  * the "hOther" store described by MSDN, because I'm not sure how that's used.
34  * It also doesn't include the "hTrust" store, because I don't yet implement
35  * CTLs or complex certificate chains.
36  */
37 typedef struct _CertificateChainEngine
38 {
39     LONG       ref;
40     HCERTSTORE hRoot;
41     HCERTSTORE hWorld;
42     DWORD      dwFlags;
43     DWORD      dwUrlRetrievalTimeout;
44     DWORD      MaximumCachedCertificates;
45     DWORD      CycleDetectionModulus;
46 } CertificateChainEngine, *PCertificateChainEngine;
47
48 static inline void CRYPT_AddStoresToCollection(HCERTSTORE collection,
49  DWORD cStores, HCERTSTORE *stores)
50 {
51     DWORD i;
52
53     for (i = 0; i < cStores; i++)
54         CertAddStoreToCollection(collection, stores[i], 0, 0);
55 }
56
57 static inline void CRYPT_CloseStores(DWORD cStores, HCERTSTORE *stores)
58 {
59     DWORD i;
60
61     for (i = 0; i < cStores; i++)
62         CertCloseStore(stores[i], 0);
63 }
64
65 static const WCHAR rootW[] = { 'R','o','o','t',0 };
66
67 static BOOL CRYPT_CheckRestrictedRoot(HCERTSTORE store)
68 {
69     BOOL ret = TRUE;
70
71     if (store)
72     {
73         HCERTSTORE rootStore = CertOpenSystemStoreW(0, rootW);
74         PCCERT_CONTEXT cert = NULL, check;
75         BYTE hash[20];
76         DWORD size;
77
78         do {
79             cert = CertEnumCertificatesInStore(store, cert);
80             if (cert)
81             {
82                 size = sizeof(hash);
83
84                 ret = CertGetCertificateContextProperty(cert, CERT_HASH_PROP_ID,
85                  hash, &size);
86                 if (ret)
87                 {
88                     CRYPT_HASH_BLOB blob = { sizeof(hash), hash };
89
90                     check = CertFindCertificateInStore(rootStore,
91                      cert->dwCertEncodingType, 0, CERT_FIND_SHA1_HASH, &blob,
92                      NULL);
93                     if (!check)
94                         ret = FALSE;
95                     else
96                         CertFreeCertificateContext(check);
97                 }
98             }
99         } while (ret && cert);
100         if (cert)
101             CertFreeCertificateContext(cert);
102         CertCloseStore(rootStore, 0);
103     }
104     return ret;
105 }
106
107 HCERTCHAINENGINE CRYPT_CreateChainEngine(HCERTSTORE root,
108  PCERT_CHAIN_ENGINE_CONFIG pConfig)
109 {
110     static const WCHAR caW[] = { 'C','A',0 };
111     static const WCHAR myW[] = { 'M','y',0 };
112     static const WCHAR trustW[] = { 'T','r','u','s','t',0 };
113     PCertificateChainEngine engine =
114      CryptMemAlloc(sizeof(CertificateChainEngine));
115
116     if (engine)
117     {
118         HCERTSTORE worldStores[4];
119
120         engine->ref = 1;
121         engine->hRoot = root;
122         engine->hWorld = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, 0,
123          CERT_STORE_CREATE_NEW_FLAG, NULL);
124         worldStores[0] = CertDuplicateStore(engine->hRoot);
125         worldStores[1] = CertOpenSystemStoreW(0, caW);
126         worldStores[2] = CertOpenSystemStoreW(0, myW);
127         worldStores[3] = CertOpenSystemStoreW(0, trustW);
128         CRYPT_AddStoresToCollection(engine->hWorld,
129          sizeof(worldStores) / sizeof(worldStores[0]), worldStores);
130         CRYPT_AddStoresToCollection(engine->hWorld,
131          pConfig->cAdditionalStore, pConfig->rghAdditionalStore);
132         CRYPT_CloseStores(sizeof(worldStores) / sizeof(worldStores[0]),
133          worldStores);
134         engine->dwFlags = pConfig->dwFlags;
135         engine->dwUrlRetrievalTimeout = pConfig->dwUrlRetrievalTimeout;
136         engine->MaximumCachedCertificates =
137          pConfig->MaximumCachedCertificates;
138         if (pConfig->CycleDetectionModulus)
139             engine->CycleDetectionModulus = pConfig->CycleDetectionModulus;
140         else
141             engine->CycleDetectionModulus = DEFAULT_CYCLE_MODULUS;
142     }
143     return (HCERTCHAINENGINE)engine;
144 }
145
146 BOOL WINAPI CertCreateCertificateChainEngine(PCERT_CHAIN_ENGINE_CONFIG pConfig,
147  HCERTCHAINENGINE *phChainEngine)
148 {
149     BOOL ret;
150
151     TRACE("(%p, %p)\n", pConfig, phChainEngine);
152
153     if (pConfig->cbSize != sizeof(*pConfig))
154     {
155         SetLastError(E_INVALIDARG);
156         return FALSE;
157     }
158     *phChainEngine = NULL;
159     ret = CRYPT_CheckRestrictedRoot(pConfig->hRestrictedRoot);
160     if (ret)
161     {
162         HCERTSTORE root;
163         HCERTCHAINENGINE engine;
164
165         if (pConfig->hRestrictedRoot)
166             root = CertDuplicateStore(pConfig->hRestrictedRoot);
167         else
168             root = CertOpenSystemStoreW(0, rootW);
169         engine = CRYPT_CreateChainEngine(root, pConfig);
170         if (engine)
171         {
172             *phChainEngine = engine;
173             ret = TRUE;
174         }
175         else
176             ret = FALSE;
177     }
178     return ret;
179 }
180
181 void WINAPI CertFreeCertificateChainEngine(HCERTCHAINENGINE hChainEngine)
182 {
183     PCertificateChainEngine engine = (PCertificateChainEngine)hChainEngine;
184
185     TRACE("(%p)\n", hChainEngine);
186
187     if (engine && InterlockedDecrement(&engine->ref) == 0)
188     {
189         CertCloseStore(engine->hWorld, 0);
190         CertCloseStore(engine->hRoot, 0);
191         CryptMemFree(engine);
192     }
193 }
194
195 static HCERTCHAINENGINE CRYPT_GetDefaultChainEngine(void)
196 {
197     if (!CRYPT_defaultChainEngine)
198     {
199         CERT_CHAIN_ENGINE_CONFIG config = { 0 };
200         HCERTCHAINENGINE engine;
201
202         config.cbSize = sizeof(config);
203         CertCreateCertificateChainEngine(&config, &engine);
204         InterlockedCompareExchangePointer(&CRYPT_defaultChainEngine, engine,
205          NULL);
206         if (CRYPT_defaultChainEngine != engine)
207             CertFreeCertificateChainEngine(engine);
208     }
209     return CRYPT_defaultChainEngine;
210 }
211
212 void default_chain_engine_free(void)
213 {
214     CertFreeCertificateChainEngine(CRYPT_defaultChainEngine);
215 }
216
217 typedef struct _CertificateChain
218 {
219     CERT_CHAIN_CONTEXT context;
220     HCERTSTORE world;
221     LONG ref;
222 } CertificateChain, *PCertificateChain;
223
224 static inline BOOL CRYPT_IsCertificateSelfSigned(PCCERT_CONTEXT cert)
225 {
226     return CertCompareCertificateName(cert->dwCertEncodingType,
227      &cert->pCertInfo->Subject, &cert->pCertInfo->Issuer);
228 }
229
230 static void CRYPT_FreeChainElement(PCERT_CHAIN_ELEMENT element)
231 {
232     CertFreeCertificateContext(element->pCertContext);
233     CryptMemFree(element);
234 }
235
236 static void CRYPT_CheckSimpleChainForCycles(PCERT_SIMPLE_CHAIN chain)
237 {
238     DWORD i, j, cyclicCertIndex = 0;
239
240     /* O(n^2) - I don't think there's a faster way */
241     for (i = 0; !cyclicCertIndex && i < chain->cElement; i++)
242         for (j = i + 1; !cyclicCertIndex && j < chain->cElement; j++)
243             if (CertCompareCertificate(X509_ASN_ENCODING,
244              chain->rgpElement[i]->pCertContext->pCertInfo,
245              chain->rgpElement[j]->pCertContext->pCertInfo))
246                 cyclicCertIndex = j;
247     if (cyclicCertIndex)
248     {
249         chain->rgpElement[cyclicCertIndex]->TrustStatus.dwErrorStatus
250          |= CERT_TRUST_IS_CYCLIC;
251         /* Release remaining certs */
252         for (i = cyclicCertIndex + 1; i < chain->cElement; i++)
253             CRYPT_FreeChainElement(chain->rgpElement[i]);
254         /* Truncate chain */
255         chain->cElement = cyclicCertIndex + 1;
256     }
257 }
258
259 /* Checks whether the chain is cyclic by examining the last element's status */
260 static inline BOOL CRYPT_IsSimpleChainCyclic(PCERT_SIMPLE_CHAIN chain)
261 {
262     if (chain->cElement)
263         return chain->rgpElement[chain->cElement - 1]->TrustStatus.dwErrorStatus
264          & CERT_TRUST_IS_CYCLIC;
265     else
266         return FALSE;
267 }
268
269 static inline void CRYPT_CombineTrustStatus(CERT_TRUST_STATUS *chainStatus,
270  CERT_TRUST_STATUS *elementStatus)
271 {
272     /* Any error that applies to an element also applies to a chain.. */
273     chainStatus->dwErrorStatus |= elementStatus->dwErrorStatus;
274     /* but the bottom nibble of an element's info status doesn't apply to the
275      * chain.
276      */
277     chainStatus->dwInfoStatus |= (elementStatus->dwInfoStatus & 0xfffffff0);
278 }
279
280 static BOOL CRYPT_AddCertToSimpleChain(PCertificateChainEngine engine,
281  PCERT_SIMPLE_CHAIN chain, PCCERT_CONTEXT cert)
282 {
283     BOOL ret = FALSE;
284     PCERT_CHAIN_ELEMENT element = CryptMemAlloc(sizeof(CERT_CHAIN_ELEMENT));
285
286     if (element)
287     {
288         if (!chain->cElement)
289             chain->rgpElement = CryptMemAlloc(sizeof(PCERT_CHAIN_ELEMENT));
290         else
291             chain->rgpElement = CryptMemRealloc(chain->rgpElement,
292              (chain->cElement + 1) * sizeof(PCERT_CHAIN_ELEMENT));
293         if (chain->rgpElement)
294         {
295             chain->rgpElement[chain->cElement++] = element;
296             memset(element, 0, sizeof(CERT_CHAIN_ELEMENT));
297             element->cbSize = sizeof(CERT_CHAIN_ELEMENT);
298             element->pCertContext = CertDuplicateCertificateContext(cert);
299             /* FIXME: initialize the rest of element */
300             if (chain->cElement % engine->CycleDetectionModulus)
301                 CRYPT_CheckSimpleChainForCycles(chain);
302             CRYPT_CombineTrustStatus(&chain->TrustStatus,
303              &element->TrustStatus);
304             ret = TRUE;
305         }
306         else
307             CryptMemFree(element);
308     }
309     return ret;
310 }
311
312 static void CRYPT_FreeSimpleChain(PCERT_SIMPLE_CHAIN chain)
313 {
314     DWORD i;
315
316     for (i = 0; i < chain->cElement; i++)
317         CRYPT_FreeChainElement(chain->rgpElement[i]);
318     CryptMemFree(chain->rgpElement);
319     CryptMemFree(chain);
320 }
321
322 static void CRYPT_CheckTrustedStatus(HCERTSTORE hRoot,
323  PCERT_CHAIN_ELEMENT rootElement)
324 {
325     BYTE hash[20];
326     DWORD size = sizeof(hash);
327     CRYPT_HASH_BLOB blob = { sizeof(hash), hash };
328     PCCERT_CONTEXT trustedRoot;
329
330     CertGetCertificateContextProperty(rootElement->pCertContext,
331      CERT_HASH_PROP_ID, hash, &size);
332     trustedRoot = CertFindCertificateInStore(hRoot,
333      rootElement->pCertContext->dwCertEncodingType, 0, CERT_FIND_SHA1_HASH,
334      &blob, NULL);
335     if (!trustedRoot)
336         rootElement->TrustStatus.dwErrorStatus |=
337          CERT_TRUST_IS_UNTRUSTED_ROOT;
338     else
339         CertFreeCertificateContext(trustedRoot);
340 }
341
342 static void CRYPT_CheckRootCert(HCERTCHAINENGINE hRoot,
343  PCERT_CHAIN_ELEMENT rootElement)
344 {
345     PCCERT_CONTEXT root = rootElement->pCertContext;
346
347     if (!CryptVerifyCertificateSignatureEx(0, root->dwCertEncodingType,
348      CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT, (void *)root,
349      CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT, (void *)root, 0, NULL))
350     {
351         TRACE("Last certificate's signature is invalid\n");
352         rootElement->TrustStatus.dwErrorStatus |=
353          CERT_TRUST_IS_NOT_SIGNATURE_VALID;
354     }
355     CRYPT_CheckTrustedStatus(hRoot, rootElement);
356 }
357
358 /* Decodes a cert's basic constraints extension (either szOID_BASIC_CONSTRAINTS
359  * or szOID_BASIC_CONSTRAINTS2, whichever is present) into a
360  * CERT_BASIC_CONSTRAINTS2_INFO.  If it neither extension is present, sets
361  * constraints->fCA to defaultIfNotSpecified.
362  * Returns FALSE if the extension is present but couldn't be decoded.
363  */
364 static BOOL CRYPT_DecodeBasicConstraints(PCCERT_CONTEXT cert,
365  CERT_BASIC_CONSTRAINTS2_INFO *constraints, BOOL defaultIfNotSpecified)
366 {
367     BOOL ret = TRUE;
368     PCERT_EXTENSION ext = CertFindExtension(szOID_BASIC_CONSTRAINTS,
369      cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension);
370
371     constraints->fPathLenConstraint = FALSE;
372     if (ext)
373     {
374         CERT_BASIC_CONSTRAINTS_INFO *info;
375         DWORD size = 0;
376
377         ret = CryptDecodeObjectEx(X509_ASN_ENCODING, szOID_BASIC_CONSTRAINTS,
378          ext->Value.pbData, ext->Value.cbData, CRYPT_DECODE_ALLOC_FLAG,
379          NULL, (LPBYTE)&info, &size);
380         if (ret)
381         {
382             if (info->SubjectType.cbData == 1)
383                 constraints->fCA =
384                  info->SubjectType.pbData[0] & CERT_CA_SUBJECT_FLAG;
385             LocalFree(info);
386         }
387     }
388     else
389     {
390         ext = CertFindExtension(szOID_BASIC_CONSTRAINTS2,
391          cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension);
392         if (ext)
393         {
394             DWORD size = sizeof(CERT_BASIC_CONSTRAINTS2_INFO);
395
396             ret = CryptDecodeObjectEx(X509_ASN_ENCODING,
397              szOID_BASIC_CONSTRAINTS2, ext->Value.pbData, ext->Value.cbData,
398              0, NULL, constraints, &size);
399         }
400         else
401             constraints->fCA = defaultIfNotSpecified;
402     }
403     return ret;
404 }
405
406 /* Checks element's basic constraints to see if it can act as a CA, with
407  * remainingCAs CAs left in this chain.  Updates chainConstraints with the
408  * element's constraints, if:
409  * 1. chainConstraints doesn't have a path length constraint, or
410  * 2. element's path length constraint is smaller than chainConstraints's
411  * Sets *pathLengthConstraintViolated to TRUE if a path length violation
412  * occurs.
413  * Returns TRUE if the element can be a CA, and the length of the remaining
414  * chain is valid.
415  */
416 static BOOL CRYPT_CheckBasicConstraintsForCA(PCCERT_CONTEXT cert,
417  CERT_BASIC_CONSTRAINTS2_INFO *chainConstraints, DWORD remainingCAs,
418  BOOL *pathLengthConstraintViolated)
419 {
420     BOOL validBasicConstraints;
421     CERT_BASIC_CONSTRAINTS2_INFO constraints;
422
423     if ((validBasicConstraints = CRYPT_DecodeBasicConstraints(cert,
424      &constraints, TRUE)))
425     {
426         if (!constraints.fCA)
427         {
428             TRACE("chain element %d can't be a CA\n", remainingCAs + 1);
429             validBasicConstraints = FALSE;
430         }
431         else if (constraints.fPathLenConstraint)
432         {
433             /* If the element has path length constraints, they apply to the
434              * entire remaining chain.
435              */
436             if (!chainConstraints->fPathLenConstraint ||
437              constraints.dwPathLenConstraint <
438              chainConstraints->dwPathLenConstraint)
439             {
440                 TRACE("setting path length constraint to %d\n",
441                  chainConstraints->dwPathLenConstraint);
442                 chainConstraints->fPathLenConstraint = TRUE;
443                 chainConstraints->dwPathLenConstraint =
444                  constraints.dwPathLenConstraint;
445             }
446         }
447     }
448     if (chainConstraints->fPathLenConstraint &&
449      remainingCAs > chainConstraints->dwPathLenConstraint)
450     {
451         TRACE("remaining CAs %d exceed max path length %d\n", remainingCAs,
452          chainConstraints->dwPathLenConstraint);
453         validBasicConstraints = FALSE;
454         *pathLengthConstraintViolated = TRUE;
455     }
456     return validBasicConstraints;
457 }
458
459 static void CRYPT_CheckSimpleChain(PCertificateChainEngine engine,
460  PCERT_SIMPLE_CHAIN chain, LPFILETIME time)
461 {
462     PCERT_CHAIN_ELEMENT rootElement = chain->rgpElement[chain->cElement - 1];
463     int i;
464     BOOL pathLengthConstraintViolated = FALSE;
465     CERT_BASIC_CONSTRAINTS2_INFO constraints = { TRUE, FALSE, 0 };
466
467     for (i = chain->cElement - 1; i >= 0; i--)
468     {
469         if (CertVerifyTimeValidity(time,
470          chain->rgpElement[i]->pCertContext->pCertInfo) != 0)
471             chain->rgpElement[i]->TrustStatus.dwErrorStatus |=
472              CERT_TRUST_IS_NOT_TIME_VALID;
473         if (i != 0)
474         {
475             /* Check the signature of the cert this issued */
476             if (!CryptVerifyCertificateSignatureEx(0, X509_ASN_ENCODING,
477              CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT,
478              (void *)chain->rgpElement[i - 1]->pCertContext,
479              CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT,
480              (void *)chain->rgpElement[i]->pCertContext, 0, NULL))
481                 chain->rgpElement[i - 1]->TrustStatus.dwErrorStatus |=
482                  CERT_TRUST_IS_NOT_SIGNATURE_VALID;
483             /* Once a path length constraint has been violated, every remaining
484              * CA cert's basic constraints is considered invalid.
485              */
486             if (pathLengthConstraintViolated)
487                 chain->rgpElement[i]->TrustStatus.dwErrorStatus |=
488                  CERT_TRUST_INVALID_BASIC_CONSTRAINTS;
489             else if (!CRYPT_CheckBasicConstraintsForCA(
490              chain->rgpElement[i]->pCertContext, &constraints, i - 1,
491              &pathLengthConstraintViolated))
492                 chain->rgpElement[i]->TrustStatus.dwErrorStatus |=
493                  CERT_TRUST_INVALID_BASIC_CONSTRAINTS;
494             else if (constraints.fPathLenConstraint &&
495              constraints.dwPathLenConstraint)
496             {
497                 /* This one's valid - decrement max length */
498                 constraints.dwPathLenConstraint--;
499             }
500         }
501         /* FIXME: check valid usages and name constraints */
502         CRYPT_CombineTrustStatus(&chain->TrustStatus,
503          &chain->rgpElement[i]->TrustStatus);
504     }
505     if (CRYPT_IsCertificateSelfSigned(rootElement->pCertContext))
506     {
507         rootElement->TrustStatus.dwInfoStatus |= CERT_TRUST_IS_SELF_SIGNED;
508         CRYPT_CheckRootCert(engine->hRoot, rootElement);
509     }
510     /* FIXME: check revocation of every cert with CertVerifyRevocation */
511     CRYPT_CombineTrustStatus(&chain->TrustStatus, &rootElement->TrustStatus);
512 }
513
514 /* Builds a simple chain by finding an issuer for the last cert in the chain,
515  * until reaching a self-signed cert, or until no issuer can be found.
516  */
517 static BOOL CRYPT_BuildSimpleChain(PCertificateChainEngine engine,
518  HCERTSTORE world, PCERT_SIMPLE_CHAIN chain)
519 {
520     BOOL ret = TRUE;
521     PCCERT_CONTEXT cert = chain->rgpElement[chain->cElement - 1]->pCertContext;
522
523     while (ret && !CRYPT_IsSimpleChainCyclic(chain) &&
524      !CRYPT_IsCertificateSelfSigned(cert))
525     {
526         DWORD flags = 0;
527         PCCERT_CONTEXT issuer =
528          CertGetIssuerCertificateFromStore(world, cert, NULL, &flags);
529
530         if (issuer)
531         {
532             ret = CRYPT_AddCertToSimpleChain(engine, chain, issuer);
533             cert = issuer;
534         }
535         else
536         {
537             TRACE("Couldn't find issuer, halting chain creation\n");
538             break;
539         }
540     }
541     return ret;
542 }
543
544 static BOOL CRYPT_GetSimpleChainForCert(PCertificateChainEngine engine,
545  HCERTSTORE world, PCCERT_CONTEXT cert, LPFILETIME pTime,
546  PCERT_SIMPLE_CHAIN *ppChain)
547 {
548     BOOL ret = FALSE;
549     PCERT_SIMPLE_CHAIN chain;
550
551     TRACE("(%p, %p, %p, %p)\n", engine, world, cert, pTime);
552
553     chain = CryptMemAlloc(sizeof(CERT_SIMPLE_CHAIN));
554     if (chain)
555     {
556         memset(chain, 0, sizeof(CERT_SIMPLE_CHAIN));
557         chain->cbSize = sizeof(CERT_SIMPLE_CHAIN);
558         ret = CRYPT_AddCertToSimpleChain(engine, chain, cert);
559         if (ret)
560         {
561             ret = CRYPT_BuildSimpleChain(engine, world, chain);
562             if (ret)
563                 CRYPT_CheckSimpleChain(engine, chain, pTime);
564         }
565         if (!ret)
566         {
567             CRYPT_FreeSimpleChain(chain);
568             chain = NULL;
569         }
570         *ppChain = chain;
571     }
572     return ret;
573 }
574
575 static BOOL CRYPT_BuildCandidateChainFromCert(HCERTCHAINENGINE hChainEngine,
576  PCCERT_CONTEXT cert, LPFILETIME pTime, HCERTSTORE hAdditionalStore,
577  PCertificateChain *ppChain)
578 {
579     PCertificateChainEngine engine = (PCertificateChainEngine)hChainEngine;
580     PCERT_SIMPLE_CHAIN simpleChain = NULL;
581     HCERTSTORE world;
582     BOOL ret;
583
584     world = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, 0,
585      CERT_STORE_CREATE_NEW_FLAG, NULL);
586     CertAddStoreToCollection(world, engine->hWorld, 0, 0);
587     if (hAdditionalStore)
588         CertAddStoreToCollection(world, hAdditionalStore, 0, 0);
589     /* FIXME: only simple chains are supported for now, as CTLs aren't
590      * supported yet.
591      */
592     if ((ret = CRYPT_GetSimpleChainForCert(engine, world, cert, pTime,
593      &simpleChain)))
594     {
595         PCertificateChain chain = CryptMemAlloc(sizeof(CertificateChain));
596
597         if (chain)
598         {
599             chain->ref = 1;
600             chain->world = world;
601             chain->context.cbSize = sizeof(CERT_CHAIN_CONTEXT);
602             memcpy(&chain->context.TrustStatus, &simpleChain->TrustStatus,
603              sizeof(CERT_TRUST_STATUS));
604             chain->context.cChain = 1;
605             chain->context.rgpChain = CryptMemAlloc(sizeof(PCERT_SIMPLE_CHAIN));
606             chain->context.rgpChain[0] = simpleChain;
607             chain->context.cLowerQualityChainContext = 0;
608             chain->context.rgpLowerQualityChainContext = NULL;
609             chain->context.fHasRevocationFreshnessTime = FALSE;
610             chain->context.dwRevocationFreshnessTime = 0;
611         }
612         else
613             ret = FALSE;
614         *ppChain = chain;
615     }
616     return ret;
617 }
618
619 /* Makes and returns a copy of chain, up to and including element iElement. */
620 static PCERT_SIMPLE_CHAIN CRYPT_CopySimpleChainToElement(
621  PCERT_SIMPLE_CHAIN chain, DWORD iElement)
622 {
623     PCERT_SIMPLE_CHAIN copy = CryptMemAlloc(sizeof(CERT_SIMPLE_CHAIN));
624
625     if (copy)
626     {
627         memset(copy, 0, sizeof(CERT_SIMPLE_CHAIN));
628         copy->cbSize = sizeof(CERT_SIMPLE_CHAIN);
629         copy->rgpElement =
630          CryptMemAlloc((iElement + 1) * sizeof(PCERT_CHAIN_ELEMENT));
631         if (copy->rgpElement)
632         {
633             DWORD i;
634             BOOL ret = TRUE;
635
636             memset(copy->rgpElement, 0,
637              (iElement + 1) * sizeof(PCERT_CHAIN_ELEMENT));
638             for (i = 0; ret && i <= iElement; i++)
639             {
640                 PCERT_CHAIN_ELEMENT element =
641                  CryptMemAlloc(sizeof(CERT_CHAIN_ELEMENT));
642
643                 if (element)
644                 {
645                     memcpy(element, chain->rgpElement[i],
646                      sizeof(CERT_CHAIN_ELEMENT));
647                     element->pCertContext = CertDuplicateCertificateContext(
648                      chain->rgpElement[i]->pCertContext);
649                     /* Reset the trust status of the copied element, it'll get
650                      * rechecked after the new chain is done.
651                      */
652                     memset(&element->TrustStatus, 0, sizeof(CERT_TRUST_STATUS));
653                     copy->rgpElement[copy->cElement++] = element;
654                 }
655                 else
656                     ret = FALSE;
657             }
658             if (!ret)
659             {
660                 for (i = 0; i <= iElement; i++)
661                     CryptMemFree(copy->rgpElement[i]);
662                 CryptMemFree(copy->rgpElement);
663                 CryptMemFree(copy);
664                 copy = NULL;
665             }
666         }
667         else
668         {
669             CryptMemFree(copy);
670             copy = NULL;
671         }
672     }
673     return copy;
674 }
675
676 static void CRYPT_FreeLowerQualityChains(PCertificateChain chain)
677 {
678     DWORD i;
679
680     for (i = 0; i < chain->context.cLowerQualityChainContext; i++)
681         CertFreeCertificateChain(chain->context.rgpLowerQualityChainContext[i]);
682     CryptMemFree(chain->context.rgpLowerQualityChainContext);
683 }
684
685 static void CRYPT_FreeChainContext(PCertificateChain chain)
686 {
687     DWORD i;
688
689     CRYPT_FreeLowerQualityChains(chain);
690     for (i = 0; i < chain->context.cChain; i++)
691         CRYPT_FreeSimpleChain(chain->context.rgpChain[i]);
692     CryptMemFree(chain->context.rgpChain);
693     CertCloseStore(chain->world, 0);
694     CryptMemFree(chain);
695 }
696
697 /* Makes and returns a copy of chain, up to and including element iElement of
698  * simple chain iChain.
699  */
700 static PCertificateChain CRYPT_CopyChainToElement(PCertificateChain chain,
701  DWORD iChain, DWORD iElement)
702 {
703     PCertificateChain copy = CryptMemAlloc(sizeof(CertificateChain));
704
705     if (copy)
706     {
707         copy->ref = 1;
708         copy->world = CertDuplicateStore(chain->world);
709         copy->context.cbSize = sizeof(CERT_CHAIN_CONTEXT);
710         /* Leave the trust status of the copied chain unset, it'll get
711          * rechecked after the new chain is done.
712          */
713         memset(&copy->context.TrustStatus, 0, sizeof(CERT_TRUST_STATUS));
714         copy->context.cLowerQualityChainContext = 0;
715         copy->context.rgpLowerQualityChainContext = NULL;
716         copy->context.fHasRevocationFreshnessTime = FALSE;
717         copy->context.dwRevocationFreshnessTime = 0;
718         copy->context.rgpChain = CryptMemAlloc(
719          (iChain + 1) * sizeof(PCERT_SIMPLE_CHAIN));
720         if (copy->context.rgpChain)
721         {
722             BOOL ret = TRUE;
723             DWORD i;
724
725             memset(copy->context.rgpChain, 0,
726              (iChain + 1) * sizeof(PCERT_SIMPLE_CHAIN));
727             if (iChain)
728             {
729                 for (i = 0; ret && iChain && i < iChain - 1; i++)
730                 {
731                     copy->context.rgpChain[i] =
732                      CRYPT_CopySimpleChainToElement(chain->context.rgpChain[i],
733                      chain->context.rgpChain[i]->cElement - 1);
734                     if (!copy->context.rgpChain[i])
735                         ret = FALSE;
736                 }
737             }
738             else
739                 i = 0;
740             if (ret)
741             {
742                 copy->context.rgpChain[i] =
743                  CRYPT_CopySimpleChainToElement(chain->context.rgpChain[i],
744                  iElement);
745                 if (!copy->context.rgpChain[i])
746                     ret = FALSE;
747             }
748             if (!ret)
749             {
750                 CRYPT_FreeChainContext(copy);
751                 copy = NULL;
752             }
753             else
754                 copy->context.cChain = iChain + 1;
755         }
756         else
757         {
758             CryptMemFree(copy);
759             copy = NULL;
760         }
761     }
762     return copy;
763 }
764
765 static PCertificateChain CRYPT_BuildAlternateContextFromChain(
766  HCERTCHAINENGINE hChainEngine, LPFILETIME pTime, HCERTSTORE hAdditionalStore,
767  PCertificateChain chain)
768 {
769     PCertificateChainEngine engine = (PCertificateChainEngine)hChainEngine;
770     PCertificateChain alternate;
771
772     TRACE("(%p, %p, %p, %p)\n", hChainEngine, pTime, hAdditionalStore, chain);
773
774     /* Always start with the last "lower quality" chain to ensure a consistent
775      * order of alternate creation:
776      */
777     if (chain->context.cLowerQualityChainContext)
778         chain = (PCertificateChain)chain->context.rgpLowerQualityChainContext[
779          chain->context.cLowerQualityChainContext - 1];
780     /* A chain with only one element can't have any alternates */
781     if (chain->context.cChain <= 1 && chain->context.rgpChain[0]->cElement <= 1)
782         alternate = NULL;
783     else
784     {
785         DWORD i, j, flags;
786         PCCERT_CONTEXT alternateIssuer = NULL;
787
788         alternate = NULL;
789         for (i = 0; !alternateIssuer && i < chain->context.cChain; i++)
790             for (j = 0; !alternateIssuer &&
791              j < chain->context.rgpChain[i]->cElement - 1; j++)
792             {
793                 PCCERT_CONTEXT subject =
794                  chain->context.rgpChain[i]->rgpElement[j]->pCertContext;
795                 PCCERT_CONTEXT prevIssuer = CertDuplicateCertificateContext(
796                  chain->context.rgpChain[i]->rgpElement[j + 1]->pCertContext);
797
798                 flags = CERT_STORE_REVOCATION_FLAG | CERT_STORE_SIGNATURE_FLAG;
799                 alternateIssuer = CertGetIssuerCertificateFromStore(
800                  prevIssuer->hCertStore, subject, prevIssuer, &flags);
801             }
802         if (alternateIssuer)
803         {
804             i--;
805             j--;
806             alternate = CRYPT_CopyChainToElement(chain, i, j);
807             if (alternate)
808             {
809                 BOOL ret = CRYPT_AddCertToSimpleChain(engine,
810                  alternate->context.rgpChain[i], alternateIssuer);
811
812                 if (ret)
813                 {
814                     ret = CRYPT_BuildSimpleChain(engine, alternate->world,
815                      alternate->context.rgpChain[i]);
816                     if (ret)
817                         CRYPT_CheckSimpleChain(engine,
818                          alternate->context.rgpChain[i], pTime);
819                     CRYPT_CombineTrustStatus(&alternate->context.TrustStatus,
820                      &alternate->context.rgpChain[i]->TrustStatus);
821                 }
822                 if (!ret)
823                 {
824                     CRYPT_FreeChainContext(alternate);
825                     alternate = NULL;
826                 }
827             }
828         }
829     }
830     TRACE("%p\n", alternate);
831     return alternate;
832 }
833
834 #define CHAIN_QUALITY_SIGNATURE_VALID 8
835 #define CHAIN_QUALITY_TIME_VALID      4
836 #define CHAIN_QUALITY_COMPLETE_CHAIN  2
837 #define CHAIN_QUALITY_TRUSTED_ROOT    1
838
839 #define CHAIN_QUALITY_HIGHEST \
840  CHAIN_QUALITY_SIGNATURE_VALID | CHAIN_QUALITY_TIME_VALID | \
841  CHAIN_QUALITY_COMPLETE_CHAIN | CHAIN_QUALITY_TRUSTED_ROOT
842
843 #define IS_TRUST_ERROR_SET(TrustStatus, bits) \
844  (TrustStatus)->dwErrorStatus & (bits)
845
846 static DWORD CRYPT_ChainQuality(PCertificateChain chain)
847 {
848     DWORD quality = CHAIN_QUALITY_HIGHEST;
849
850     if (IS_TRUST_ERROR_SET(&chain->context.TrustStatus,
851      CERT_TRUST_IS_UNTRUSTED_ROOT))
852         quality &= ~CHAIN_QUALITY_TRUSTED_ROOT;
853     if (IS_TRUST_ERROR_SET(&chain->context.TrustStatus,
854      CERT_TRUST_IS_PARTIAL_CHAIN))
855     if (chain->context.TrustStatus.dwErrorStatus & CERT_TRUST_IS_PARTIAL_CHAIN)
856         quality &= ~CHAIN_QUALITY_COMPLETE_CHAIN;
857     if (IS_TRUST_ERROR_SET(&chain->context.TrustStatus,
858      CERT_TRUST_IS_NOT_TIME_VALID | CERT_TRUST_IS_NOT_TIME_NESTED))
859         quality &= ~CHAIN_QUALITY_TIME_VALID;
860     if (IS_TRUST_ERROR_SET(&chain->context.TrustStatus,
861      CERT_TRUST_IS_NOT_SIGNATURE_VALID))
862         quality &= ~CHAIN_QUALITY_SIGNATURE_VALID;
863     return quality;
864 }
865
866 /* Chooses the highest quality chain among chain and its "lower quality"
867  * alternate chains.  Returns the highest quality chain, with all other
868  * chains as lower quality chains of it.
869  */
870 static PCertificateChain CRYPT_ChooseHighestQualityChain(
871  PCertificateChain chain)
872 {
873     DWORD i;
874
875     /* There are always only two chains being considered:  chain, and an
876      * alternate at chain->rgpLowerQualityChainContext[i].  If the alternate
877      * has a higher quality than chain, the alternate gets assigned the lower
878      * quality contexts, with chain taking the alternate's place among the
879      * lower quality contexts.
880      */
881     for (i = 0; i < chain->context.cLowerQualityChainContext; i++)
882     {
883         PCertificateChain alternate =
884          (PCertificateChain)chain->context.rgpLowerQualityChainContext[i];
885
886         if (CRYPT_ChainQuality(alternate) > CRYPT_ChainQuality(chain))
887         {
888             alternate->context.cLowerQualityChainContext =
889              chain->context.cLowerQualityChainContext;
890             alternate->context.rgpLowerQualityChainContext =
891              chain->context.rgpLowerQualityChainContext;
892             alternate->context.rgpLowerQualityChainContext[i] =
893              (PCCERT_CHAIN_CONTEXT)chain;
894             chain = alternate;
895         }
896     }
897     return chain;
898 }
899
900 static BOOL CRYPT_AddAlternateChainToChain(PCertificateChain chain,
901  PCertificateChain alternate)
902 {
903     BOOL ret;
904
905     if (chain->context.cLowerQualityChainContext)
906         chain->context.rgpLowerQualityChainContext =
907          CryptMemRealloc(chain->context.rgpLowerQualityChainContext,
908          (chain->context.cLowerQualityChainContext + 1) *
909          sizeof(PCCERT_CHAIN_CONTEXT));
910     else
911         chain->context.rgpLowerQualityChainContext =
912          CryptMemAlloc(sizeof(PCCERT_CHAIN_CONTEXT));
913     if (chain->context.rgpLowerQualityChainContext)
914     {
915         chain->context.rgpLowerQualityChainContext[
916          chain->context.cLowerQualityChainContext++] =
917          (PCCERT_CHAIN_CONTEXT)alternate;
918         ret = TRUE;
919     }
920     else
921         ret = FALSE;
922     return ret;
923 }
924
925 typedef struct _CERT_CHAIN_PARA_NO_EXTRA_FIELDS {
926     DWORD            cbSize;
927     CERT_USAGE_MATCH RequestedUsage;
928 } CERT_CHAIN_PARA_NO_EXTRA_FIELDS, *PCERT_CHAIN_PARA_NO_EXTRA_FIELDS;
929
930 typedef struct _CERT_CHAIN_PARA_EXTRA_FIELDS {
931     DWORD            cbSize;
932     CERT_USAGE_MATCH RequestedUsage;
933     CERT_USAGE_MATCH RequestedIssuancePolicy;
934     DWORD            dwUrlRetrievalTimeout;
935     BOOL             fCheckRevocationFreshnessTime;
936     DWORD            dwRevocationFreshnessTime;
937 } CERT_CHAIN_PARA_EXTRA_FIELDS, *PCERT_CHAIN_PARA_EXTRA_FIELDS;
938
939 BOOL WINAPI CertGetCertificateChain(HCERTCHAINENGINE hChainEngine,
940  PCCERT_CONTEXT pCertContext, LPFILETIME pTime, HCERTSTORE hAdditionalStore,
941  PCERT_CHAIN_PARA pChainPara, DWORD dwFlags, LPVOID pvReserved,
942  PCCERT_CHAIN_CONTEXT* ppChainContext)
943 {
944     BOOL ret;
945     PCertificateChain chain = NULL;
946
947     TRACE("(%p, %p, %p, %p, %p, %08x, %p, %p)\n", hChainEngine, pCertContext,
948      pTime, hAdditionalStore, pChainPara, dwFlags, pvReserved, ppChainContext);
949
950     if (ppChainContext)
951         *ppChainContext = NULL;
952     if (!pChainPara)
953     {
954         SetLastError(E_INVALIDARG);
955         return FALSE;
956     }
957     if (!pCertContext->pCertInfo->SignatureAlgorithm.pszObjId)
958     {
959         SetLastError(ERROR_INVALID_DATA);
960         return FALSE;
961     }
962     if (!hChainEngine)
963         hChainEngine = CRYPT_GetDefaultChainEngine();
964     /* FIXME: what about HCCE_LOCAL_MACHINE? */
965     /* FIXME: pChainPara is for now ignored */
966     ret = CRYPT_BuildCandidateChainFromCert(hChainEngine, pCertContext, pTime,
967      hAdditionalStore, &chain);
968     if (ret)
969     {
970         PCertificateChain alternate = NULL;
971
972         do {
973             alternate = CRYPT_BuildAlternateContextFromChain(hChainEngine,
974              pTime, hAdditionalStore, chain);
975
976             /* Alternate contexts are added as "lower quality" contexts of
977              * chain, to avoid loops in alternate chain creation.
978              * The highest-quality chain is chosen at the end.
979              */
980             if (alternate)
981                 ret = CRYPT_AddAlternateChainToChain(chain, alternate);
982         } while (ret && alternate);
983         chain = CRYPT_ChooseHighestQualityChain(chain);
984         if (!(dwFlags & CERT_CHAIN_RETURN_LOWER_QUALITY_CONTEXTS))
985         {
986             CRYPT_FreeLowerQualityChains(chain);
987             chain->context.cLowerQualityChainContext = 0;
988             chain->context.rgpLowerQualityChainContext = NULL;
989         }
990         if (ppChainContext)
991             *ppChainContext = (PCCERT_CHAIN_CONTEXT)chain;
992         else
993             CertFreeCertificateChain((PCCERT_CHAIN_CONTEXT)chain);
994     }
995     TRACE("returning %d\n", ret);
996     return ret;
997 }
998
999 PCCERT_CHAIN_CONTEXT WINAPI CertDuplicateCertificateChain(
1000  PCCERT_CHAIN_CONTEXT pChainContext)
1001 {
1002     PCertificateChain chain = (PCertificateChain)pChainContext;
1003
1004     TRACE("(%p)\n", pChainContext);
1005
1006     if (chain)
1007         InterlockedIncrement(&chain->ref);
1008     return pChainContext;
1009 }
1010
1011 void WINAPI CertFreeCertificateChain(PCCERT_CHAIN_CONTEXT pChainContext)
1012 {
1013     PCertificateChain chain = (PCertificateChain)pChainContext;
1014
1015     TRACE("(%p)\n", pChainContext);
1016
1017     if (chain)
1018     {
1019         if (InterlockedDecrement(&chain->ref) == 0)
1020             CRYPT_FreeChainContext(chain);
1021     }
1022 }
1023
1024 static void find_element_with_error(PCCERT_CHAIN_CONTEXT chain, DWORD error,
1025  LONG *iChain, LONG *iElement)
1026 {
1027     DWORD i, j;
1028
1029     for (i = 0; i < chain->cChain; i++)
1030         for (j = 0; j < chain->rgpChain[i]->cElement; j++)
1031             if (chain->rgpChain[i]->rgpElement[j]->TrustStatus.dwErrorStatus &
1032              error)
1033             {
1034                 *iChain = i;
1035                 *iElement = j;
1036                 return;
1037             }
1038 }
1039
1040 static BOOL WINAPI verify_base_policy(LPCSTR szPolicyOID,
1041  PCCERT_CHAIN_CONTEXT pChainContext, PCERT_CHAIN_POLICY_PARA pPolicyPara,
1042  PCERT_CHAIN_POLICY_STATUS pPolicyStatus)
1043 {
1044     pPolicyStatus->lChainIndex = pPolicyStatus->lElementIndex = -1;
1045     if (pChainContext->TrustStatus.dwErrorStatus &
1046      CERT_TRUST_IS_NOT_SIGNATURE_VALID)
1047     {
1048         pPolicyStatus->dwError = TRUST_E_CERT_SIGNATURE;
1049         find_element_with_error(pChainContext,
1050          CERT_TRUST_IS_NOT_SIGNATURE_VALID, &pPolicyStatus->lChainIndex,
1051          &pPolicyStatus->lElementIndex);
1052     }
1053     else if (pChainContext->TrustStatus.dwErrorStatus &
1054      CERT_TRUST_IS_UNTRUSTED_ROOT)
1055     {
1056         pPolicyStatus->dwError = CERT_E_UNTRUSTEDROOT;
1057         find_element_with_error(pChainContext,
1058          CERT_TRUST_IS_UNTRUSTED_ROOT, &pPolicyStatus->lChainIndex,
1059          &pPolicyStatus->lElementIndex);
1060     }
1061     else if (pChainContext->TrustStatus.dwErrorStatus & CERT_TRUST_IS_CYCLIC)
1062     {
1063         pPolicyStatus->dwError = CERT_E_CHAINING;
1064         find_element_with_error(pChainContext, CERT_TRUST_IS_CYCLIC,
1065          &pPolicyStatus->lChainIndex, &pPolicyStatus->lElementIndex);
1066         /* For a cyclic chain, which element is a cycle isn't meaningful */
1067         pPolicyStatus->lElementIndex = -1;
1068     }
1069     return TRUE;
1070 }
1071
1072 static BYTE msTestPubKey1[] = {
1073 0x30,0x47,0x02,0x40,0x81,0x55,0x22,0xb9,0x8a,0xa4,0x6f,0xed,0xd6,0xe7,0xd9,
1074 0x66,0x0f,0x55,0xbc,0xd7,0xcd,0xd5,0xbc,0x4e,0x40,0x02,0x21,0xa2,0xb1,0xf7,
1075 0x87,0x30,0x85,0x5e,0xd2,0xf2,0x44,0xb9,0xdc,0x9b,0x75,0xb6,0xfb,0x46,0x5f,
1076 0x42,0xb6,0x9d,0x23,0x36,0x0b,0xde,0x54,0x0f,0xcd,0xbd,0x1f,0x99,0x2a,0x10,
1077 0x58,0x11,0xcb,0x40,0xcb,0xb5,0xa7,0x41,0x02,0x03,0x01,0x00,0x01 };
1078 static BYTE msTestPubKey2[] = {
1079 0x30,0x48,0x02,0x41,0x00,0x81,0x55,0x22,0xb9,0x8a,0xa4,0x6f,0xed,0xd6,0xe7,
1080 0xd9,0x66,0x0f,0x55,0xbc,0xd7,0xcd,0xd5,0xbc,0x4e,0x40,0x02,0x21,0xa2,0xb1,
1081 0xf7,0x87,0x30,0x85,0x5e,0xd2,0xf2,0x44,0xb9,0xdc,0x9b,0x75,0xb6,0xfb,0x46,
1082 0x5f,0x42,0xb6,0x9d,0x23,0x36,0x0b,0xde,0x54,0x0f,0xcd,0xbd,0x1f,0x99,0x2a,
1083 0x10,0x58,0x11,0xcb,0x40,0xcb,0xb5,0xa7,0x41,0x02,0x03,0x01,0x00,0x01 };
1084 static BYTE msTestPubKey3[] = {
1085 0x30,0x47,0x02,0x40,0x9c,0x50,0x05,0x1d,0xe2,0x0e,0x4c,0x53,0xd8,0xd9,0xb5,
1086 0xe5,0xfd,0xe9,0xe3,0xad,0x83,0x4b,0x80,0x08,0xd9,0xdc,0xe8,0xe8,0x35,0xf8,
1087 0x11,0xf1,0xe9,0x9b,0x03,0x7a,0x65,0x64,0x76,0x35,0xce,0x38,0x2c,0xf2,0xb6,
1088 0x71,0x9e,0x06,0xd9,0xbf,0xbb,0x31,0x69,0xa3,0xf6,0x30,0xa0,0x78,0x7b,0x18,
1089 0xdd,0x50,0x4d,0x79,0x1e,0xeb,0x61,0xc1,0x02,0x03,0x01,0x00,0x01 };
1090
1091 static BOOL WINAPI verify_authenticode_policy(LPCSTR szPolicyOID,
1092  PCCERT_CHAIN_CONTEXT pChainContext, PCERT_CHAIN_POLICY_PARA pPolicyPara,
1093  PCERT_CHAIN_POLICY_STATUS pPolicyStatus)
1094 {
1095     BOOL ret = verify_base_policy(szPolicyOID, pChainContext, pPolicyPara,
1096      pPolicyStatus);
1097
1098     if (ret && pPolicyStatus->dwError == CERT_E_UNTRUSTEDROOT)
1099     {
1100         CERT_PUBLIC_KEY_INFO msPubKey = { { 0 } };
1101         BOOL isMSTestRoot = FALSE;
1102         PCCERT_CONTEXT failingCert =
1103          pChainContext->rgpChain[pPolicyStatus->lChainIndex]->
1104          rgpElement[pPolicyStatus->lElementIndex]->pCertContext;
1105         DWORD i;
1106         CRYPT_DATA_BLOB keyBlobs[] = {
1107          { sizeof(msTestPubKey1), msTestPubKey1 },
1108          { sizeof(msTestPubKey2), msTestPubKey2 },
1109          { sizeof(msTestPubKey3), msTestPubKey3 },
1110         };
1111
1112         /* Check whether the root is an MS test root */
1113         for (i = 0; !isMSTestRoot && i < sizeof(keyBlobs) / sizeof(keyBlobs[0]);
1114          i++)
1115         {
1116             msPubKey.PublicKey.cbData = keyBlobs[i].cbData;
1117             msPubKey.PublicKey.pbData = keyBlobs[i].pbData;
1118             if (CertComparePublicKeyInfo(
1119              X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
1120              &failingCert->pCertInfo->SubjectPublicKeyInfo, &msPubKey))
1121                 isMSTestRoot = TRUE;
1122         }
1123         if (isMSTestRoot)
1124             pPolicyStatus->dwError = CERT_E_UNTRUSTEDTESTROOT;
1125     }
1126     return ret;
1127 }
1128
1129 static BOOL WINAPI verify_basic_constraints_policy(LPCSTR szPolicyOID,
1130  PCCERT_CHAIN_CONTEXT pChainContext, PCERT_CHAIN_POLICY_PARA pPolicyPara,
1131  PCERT_CHAIN_POLICY_STATUS pPolicyStatus)
1132 {
1133     pPolicyStatus->lChainIndex = pPolicyStatus->lElementIndex = -1;
1134     if (pChainContext->TrustStatus.dwErrorStatus &
1135      CERT_TRUST_INVALID_BASIC_CONSTRAINTS)
1136     {
1137         pPolicyStatus->dwError = TRUST_E_BASIC_CONSTRAINTS;
1138         find_element_with_error(pChainContext,
1139          CERT_TRUST_INVALID_BASIC_CONSTRAINTS, &pPolicyStatus->lChainIndex,
1140          &pPolicyStatus->lElementIndex);
1141     }
1142     return TRUE;
1143 }
1144
1145 typedef BOOL (WINAPI *CertVerifyCertificateChainPolicyFunc)(LPCSTR szPolicyOID,
1146  PCCERT_CHAIN_CONTEXT pChainContext, PCERT_CHAIN_POLICY_PARA pPolicyPara,
1147  PCERT_CHAIN_POLICY_STATUS pPolicyStatus);
1148
1149 BOOL WINAPI CertVerifyCertificateChainPolicy(LPCSTR szPolicyOID,
1150  PCCERT_CHAIN_CONTEXT pChainContext, PCERT_CHAIN_POLICY_PARA pPolicyPara,
1151  PCERT_CHAIN_POLICY_STATUS pPolicyStatus)
1152 {
1153     static HCRYPTOIDFUNCSET set = NULL;
1154     BOOL ret = FALSE;
1155     CertVerifyCertificateChainPolicyFunc verifyPolicy = NULL;
1156     HCRYPTOIDFUNCADDR hFunc = NULL;
1157
1158     TRACE("(%s, %p, %p, %p)\n", debugstr_a(szPolicyOID), pChainContext,
1159      pPolicyPara, pPolicyStatus);
1160
1161     if (!HIWORD(szPolicyOID))
1162     {
1163         switch (LOWORD(szPolicyOID))
1164         {
1165         case (int)CERT_CHAIN_POLICY_BASE:
1166             verifyPolicy = verify_base_policy;
1167             break;
1168         case (int)CERT_CHAIN_POLICY_AUTHENTICODE:
1169             verifyPolicy = verify_authenticode_policy;
1170             break;
1171         case (int)CERT_CHAIN_POLICY_BASIC_CONSTRAINTS:
1172             verifyPolicy = verify_basic_constraints_policy;
1173             break;
1174         default:
1175             FIXME("unimplemented for %d\n", LOWORD(szPolicyOID));
1176         }
1177     }
1178     if (!verifyPolicy)
1179     {
1180         if (!set)
1181             set = CryptInitOIDFunctionSet(
1182              CRYPT_OID_VERIFY_CERTIFICATE_CHAIN_POLICY_FUNC, 0);
1183         CryptGetOIDFunctionAddress(set, X509_ASN_ENCODING, szPolicyOID, 0,
1184          (void **)&verifyPolicy, hFunc);
1185     }
1186     if (verifyPolicy)
1187         ret = verifyPolicy(szPolicyOID, pChainContext, pPolicyPara,
1188          pPolicyStatus);
1189     if (hFunc)
1190         CryptFreeOIDFunctionAddress(hFunc, 0);
1191     return ret;
1192 }