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