2 * Copyright 2006 Juan Lang
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #include "wine/debug.h"
24 #include "crypt32_private.h"
26 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
28 #define DEFAULT_CYCLE_MODULUS 7
30 static HCERTCHAINENGINE CRYPT_defaultChainEngine;
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.
37 typedef struct _CertificateChainEngine
43 DWORD dwUrlRetrievalTimeout;
44 DWORD MaximumCachedCertificates;
45 DWORD CycleDetectionModulus;
46 } CertificateChainEngine, *PCertificateChainEngine;
48 static inline void CRYPT_AddStoresToCollection(HCERTSTORE collection,
49 DWORD cStores, HCERTSTORE *stores)
53 for (i = 0; i < cStores; i++)
54 CertAddStoreToCollection(collection, stores[i], 0, 0);
57 static inline void CRYPT_CloseStores(DWORD cStores, HCERTSTORE *stores)
61 for (i = 0; i < cStores; i++)
62 CertCloseStore(stores[i], 0);
65 static const WCHAR rootW[] = { 'R','o','o','t',0 };
67 static BOOL CRYPT_CheckRestrictedRoot(HCERTSTORE store)
73 HCERTSTORE rootStore = CertOpenSystemStoreW(0, rootW);
74 PCCERT_CONTEXT cert = NULL, check;
79 cert = CertEnumCertificatesInStore(store, cert);
84 ret = CertGetCertificateContextProperty(cert, CERT_HASH_PROP_ID,
88 CRYPT_HASH_BLOB blob = { sizeof(hash), hash };
90 check = CertFindCertificateInStore(rootStore,
91 cert->dwCertEncodingType, 0, CERT_FIND_SHA1_HASH, &blob,
96 CertFreeCertificateContext(check);
99 } while (ret && cert);
101 CertFreeCertificateContext(cert);
102 CertCloseStore(rootStore, 0);
107 BOOL WINAPI CertCreateCertificateChainEngine(PCERT_CHAIN_ENGINE_CONFIG pConfig,
108 HCERTCHAINENGINE *phChainEngine)
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 };
115 TRACE("(%p, %p)\n", pConfig, phChainEngine);
117 if (pConfig->cbSize != sizeof(*pConfig))
119 SetLastError(E_INVALIDARG);
122 *phChainEngine = NULL;
123 ret = CRYPT_CheckRestrictedRoot(pConfig->hRestrictedRoot);
126 PCertificateChainEngine engine =
127 CryptMemAlloc(sizeof(CertificateChainEngine));
131 HCERTSTORE worldStores[4];
134 if (pConfig->hRestrictedRoot)
135 engine->hRoot = CertDuplicateStore(pConfig->hRestrictedRoot);
137 engine->hRoot = CertOpenSystemStoreW(0, rootW);
138 engine->hWorld = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, 0,
139 CERT_STORE_CREATE_NEW_FLAG, NULL);
140 worldStores[0] = CertDuplicateStore(engine->hRoot);
141 worldStores[1] = CertOpenSystemStoreW(0, caW);
142 worldStores[2] = CertOpenSystemStoreW(0, myW);
143 worldStores[3] = CertOpenSystemStoreW(0, trustW);
144 CRYPT_AddStoresToCollection(engine->hWorld,
145 sizeof(worldStores) / sizeof(worldStores[0]), worldStores);
146 CRYPT_AddStoresToCollection(engine->hWorld,
147 pConfig->cAdditionalStore, pConfig->rghAdditionalStore);
148 CRYPT_CloseStores(sizeof(worldStores) / sizeof(worldStores[0]),
150 engine->dwFlags = pConfig->dwFlags;
151 engine->dwUrlRetrievalTimeout = pConfig->dwUrlRetrievalTimeout;
152 engine->MaximumCachedCertificates =
153 pConfig->MaximumCachedCertificates;
154 if (pConfig->CycleDetectionModulus)
155 engine->CycleDetectionModulus = pConfig->CycleDetectionModulus;
157 engine->CycleDetectionModulus = DEFAULT_CYCLE_MODULUS;
158 *phChainEngine = (HCERTCHAINENGINE)engine;
167 void WINAPI CertFreeCertificateChainEngine(HCERTCHAINENGINE hChainEngine)
169 PCertificateChainEngine engine = (PCertificateChainEngine)hChainEngine;
171 TRACE("(%p)\n", hChainEngine);
173 if (engine && InterlockedDecrement(&engine->ref) == 0)
175 CertCloseStore(engine->hWorld, 0);
176 CertCloseStore(engine->hRoot, 0);
177 CryptMemFree(engine);
181 static HCERTCHAINENGINE CRYPT_GetDefaultChainEngine(void)
183 if (!CRYPT_defaultChainEngine)
185 CERT_CHAIN_ENGINE_CONFIG config = { 0 };
186 HCERTCHAINENGINE engine;
188 config.cbSize = sizeof(config);
189 CertCreateCertificateChainEngine(&config, &engine);
190 InterlockedCompareExchangePointer(&CRYPT_defaultChainEngine, engine,
192 if (CRYPT_defaultChainEngine != engine)
193 CertFreeCertificateChainEngine(engine);
195 return CRYPT_defaultChainEngine;
198 void default_chain_engine_free(void)
200 CertFreeCertificateChainEngine(CRYPT_defaultChainEngine);
203 typedef struct _CertificateChain
205 CERT_CHAIN_CONTEXT context;
208 } CertificateChain, *PCertificateChain;
210 static inline BOOL CRYPT_IsCertificateSelfSigned(PCCERT_CONTEXT cert)
212 return CertCompareCertificateName(cert->dwCertEncodingType,
213 &cert->pCertInfo->Subject, &cert->pCertInfo->Issuer);
216 static void CRYPT_FreeChainElement(PCERT_CHAIN_ELEMENT element)
218 CertFreeCertificateContext(element->pCertContext);
219 CryptMemFree(element);
222 static void CRYPT_CheckSimpleChainForCycles(PCERT_SIMPLE_CHAIN chain)
224 DWORD i, j, cyclicCertIndex = 0;
226 /* O(n^2) - I don't think there's a faster way */
227 for (i = 0; !cyclicCertIndex && i < chain->cElement; i++)
228 for (j = i + 1; !cyclicCertIndex && j < chain->cElement; j++)
229 if (CertCompareCertificate(X509_ASN_ENCODING,
230 chain->rgpElement[i]->pCertContext->pCertInfo,
231 chain->rgpElement[j]->pCertContext->pCertInfo))
235 chain->rgpElement[cyclicCertIndex]->TrustStatus.dwErrorStatus
236 |= CERT_TRUST_IS_CYCLIC;
237 /* Release remaining certs */
238 for (i = cyclicCertIndex + 1; i < chain->cElement; i++)
239 CRYPT_FreeChainElement(chain->rgpElement[i]);
241 chain->cElement = cyclicCertIndex + 1;
245 /* Checks whether the chain is cyclic by examining the last element's status */
246 static inline BOOL CRYPT_IsSimpleChainCyclic(PCERT_SIMPLE_CHAIN chain)
249 return chain->rgpElement[chain->cElement - 1]->TrustStatus.dwErrorStatus
250 & CERT_TRUST_IS_CYCLIC;
255 /* Gets cert's issuer from store, and returns the validity flags associated
256 * with it. Returns NULL if no issuer signature could be found.
258 static PCCERT_CONTEXT CRYPT_GetIssuerFromStore(HCERTSTORE store,
259 PCCERT_CONTEXT cert, PDWORD pdwFlags)
261 *pdwFlags = CERT_STORE_REVOCATION_FLAG | CERT_STORE_SIGNATURE_FLAG;
262 return CertGetIssuerCertificateFromStore(store, cert, NULL, pdwFlags);
265 static inline void CRYPT_CombineTrustStatus(CERT_TRUST_STATUS *chainStatus,
266 CERT_TRUST_STATUS *elementStatus)
268 /* Any error that applies to an element also applies to a chain.. */
269 chainStatus->dwErrorStatus |= elementStatus->dwErrorStatus;
270 /* but the bottom nibble of an element's info status doesn't apply to the
273 chainStatus->dwInfoStatus |= (elementStatus->dwInfoStatus & 0xfffffff0);
276 static BOOL CRYPT_AddCertToSimpleChain(PCertificateChainEngine engine,
277 PCERT_SIMPLE_CHAIN chain, PCCERT_CONTEXT cert, DWORD dwFlags)
280 PCERT_CHAIN_ELEMENT element = CryptMemAlloc(sizeof(CERT_CHAIN_ELEMENT));
284 if (!chain->cElement)
285 chain->rgpElement = CryptMemAlloc(sizeof(PCERT_CHAIN_ELEMENT));
287 chain->rgpElement = CryptMemRealloc(chain->rgpElement,
288 (chain->cElement + 1) * sizeof(PCERT_CHAIN_ELEMENT));
289 if (chain->rgpElement)
291 chain->rgpElement[chain->cElement++] = element;
292 memset(element, 0, sizeof(CERT_CHAIN_ELEMENT));
293 element->cbSize = sizeof(CERT_CHAIN_ELEMENT);
294 element->pCertContext = CertDuplicateCertificateContext(cert);
295 /* Flags, if set, refer to the element this cert issued, so set
296 * the preceding element's error accordingly
298 if (chain->cElement > 1)
300 if (dwFlags & CERT_STORE_REVOCATION_FLAG &&
301 !(dwFlags & CERT_STORE_NO_CRL_FLAG))
302 chain->rgpElement[chain->cElement - 2]->TrustStatus.
303 dwErrorStatus |= CERT_TRUST_IS_REVOKED;
304 if (dwFlags & CERT_STORE_SIGNATURE_FLAG)
305 chain->rgpElement[chain->cElement - 2]->TrustStatus.
307 CERT_TRUST_IS_NOT_SIGNATURE_VALID;
309 /* FIXME: initialize the rest of element */
310 if (chain->cElement % engine->CycleDetectionModulus)
311 CRYPT_CheckSimpleChainForCycles(chain);
312 CRYPT_CombineTrustStatus(&chain->TrustStatus,
313 &element->TrustStatus);
317 CryptMemFree(element);
322 static void CRYPT_FreeSimpleChain(PCERT_SIMPLE_CHAIN chain)
326 for (i = 0; i < chain->cElement; i++)
327 CRYPT_FreeChainElement(chain->rgpElement[i]);
328 CryptMemFree(chain->rgpElement);
332 static void CRYPT_CheckTrustedStatus(HCERTSTORE hRoot,
333 PCERT_CHAIN_ELEMENT rootElement)
336 DWORD size = sizeof(hash);
337 CRYPT_HASH_BLOB blob = { sizeof(hash), hash };
338 PCCERT_CONTEXT trustedRoot;
340 CertGetCertificateContextProperty(rootElement->pCertContext,
341 CERT_HASH_PROP_ID, hash, &size);
342 trustedRoot = CertFindCertificateInStore(hRoot,
343 rootElement->pCertContext->dwCertEncodingType, 0, CERT_FIND_SHA1_HASH,
346 rootElement->TrustStatus.dwErrorStatus |=
347 CERT_TRUST_IS_UNTRUSTED_ROOT;
349 CertFreeCertificateContext(trustedRoot);
352 static BOOL CRYPT_CheckRootCert(HCERTCHAINENGINE hRoot,
353 PCERT_CHAIN_ELEMENT rootElement)
355 PCCERT_CONTEXT root = rootElement->pCertContext;
358 if (!(ret = CryptVerifyCertificateSignatureEx(0, root->dwCertEncodingType,
359 CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT, (void *)root,
360 CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT, (void *)root, 0, NULL)))
362 TRACE("Last certificate's signature is invalid\n");
363 rootElement->TrustStatus.dwErrorStatus |=
364 CERT_TRUST_IS_NOT_SIGNATURE_VALID;
366 CRYPT_CheckTrustedStatus(hRoot, rootElement);
370 /* Decodes a cert's basic constraints extension (either szOID_BASIC_CONSTRAINTS
371 * or szOID_BASIC_CONSTRAINTS2, whichever is present) into a
372 * CERT_BASIC_CONSTRAINTS2_INFO. If it neither extension is present, sets
373 * constraints->fCA to defaultIfNotSpecified.
374 * Returns FALSE if the extension is present but couldn't be decoded.
376 static BOOL CRYPT_DecodeBasicConstraints(PCCERT_CONTEXT cert,
377 CERT_BASIC_CONSTRAINTS2_INFO *constraints, BOOL defaultIfNotSpecified)
380 PCERT_EXTENSION ext = CertFindExtension(szOID_BASIC_CONSTRAINTS,
381 cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension);
383 constraints->fPathLenConstraint = FALSE;
386 CERT_BASIC_CONSTRAINTS_INFO *info;
389 ret = CryptDecodeObjectEx(X509_ASN_ENCODING, szOID_BASIC_CONSTRAINTS,
390 ext->Value.pbData, ext->Value.cbData, CRYPT_DECODE_ALLOC_FLAG,
391 NULL, (LPBYTE)&info, &size);
394 if (info->SubjectType.cbData == 1)
396 info->SubjectType.pbData[0] & CERT_CA_SUBJECT_FLAG;
402 ext = CertFindExtension(szOID_BASIC_CONSTRAINTS2,
403 cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension);
406 DWORD size = sizeof(CERT_BASIC_CONSTRAINTS2_INFO);
408 ret = CryptDecodeObjectEx(X509_ASN_ENCODING,
409 szOID_BASIC_CONSTRAINTS2, ext->Value.pbData, ext->Value.cbData,
410 0, NULL, constraints, &size);
413 constraints->fCA = defaultIfNotSpecified;
418 /* Checks element's basic constraints to see if it can act as a CA, with
419 * remainingCAs CAs left in this chain. Updates chainConstraints with the
420 * element's constraints, if:
421 * 1. chainConstraints doesn't have a path length constraint, or
422 * 2. element's path length constraint is smaller than chainConstraints's
423 * Sets *pathLengthConstraintViolated to TRUE if a path length violation
425 * Returns TRUE if the element can be a CA, and the length of the remaining
428 static BOOL CRYPT_CheckBasicConstraintsForCA(PCCERT_CONTEXT cert,
429 CERT_BASIC_CONSTRAINTS2_INFO *chainConstraints, DWORD remainingCAs,
430 BOOL *pathLengthConstraintViolated)
432 BOOL validBasicConstraints;
433 CERT_BASIC_CONSTRAINTS2_INFO constraints;
435 if ((validBasicConstraints = CRYPT_DecodeBasicConstraints(cert,
436 &constraints, TRUE)))
438 if (!constraints.fCA)
440 TRACE("chain element %d can't be a CA\n", remainingCAs + 1);
441 validBasicConstraints = FALSE;
443 else if (constraints.fPathLenConstraint)
445 /* If the element has path length constraints, they apply to the
446 * entire remaining chain.
448 if (!chainConstraints->fPathLenConstraint ||
449 constraints.dwPathLenConstraint <
450 chainConstraints->dwPathLenConstraint)
452 TRACE("setting path length constraint to %d\n",
453 chainConstraints->dwPathLenConstraint);
454 chainConstraints->fPathLenConstraint = TRUE;
455 chainConstraints->dwPathLenConstraint =
456 constraints.dwPathLenConstraint;
460 if (chainConstraints->fPathLenConstraint &&
461 remainingCAs > chainConstraints->dwPathLenConstraint)
463 TRACE("remaining CAs %d exceed max path length %d\n", remainingCAs,
464 chainConstraints->dwPathLenConstraint);
465 validBasicConstraints = FALSE;
466 *pathLengthConstraintViolated = TRUE;
468 return validBasicConstraints;
471 static BOOL CRYPT_CheckSimpleChain(PCertificateChainEngine engine,
472 PCERT_SIMPLE_CHAIN chain, LPFILETIME time)
474 PCERT_CHAIN_ELEMENT rootElement = chain->rgpElement[chain->cElement - 1];
476 BOOL ret = TRUE, pathLengthConstraintViolated = FALSE;
477 CERT_BASIC_CONSTRAINTS2_INFO constraints = { TRUE, FALSE, 0 };
479 for (i = chain->cElement - 1; i >= 0; i--)
481 if (CertVerifyTimeValidity(time,
482 chain->rgpElement[i]->pCertContext->pCertInfo) != 0)
483 chain->rgpElement[i]->TrustStatus.dwErrorStatus |=
484 CERT_TRUST_IS_NOT_TIME_VALID;
487 /* Once a path length constraint has been violated, every remaining
488 * CA cert's basic constraints is considered invalid.
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)
501 /* This one's valid - decrement max length */
502 constraints.dwPathLenConstraint--;
505 /* FIXME: check valid usages and name constraints */
506 CRYPT_CombineTrustStatus(&chain->TrustStatus,
507 &chain->rgpElement[i]->TrustStatus);
509 if (CRYPT_IsCertificateSelfSigned(rootElement->pCertContext))
511 rootElement->TrustStatus.dwInfoStatus |= CERT_TRUST_IS_SELF_SIGNED;
512 ret = CRYPT_CheckRootCert(engine->hRoot, rootElement);
514 CRYPT_CombineTrustStatus(&chain->TrustStatus, &rootElement->TrustStatus);
518 /* Builds a simple chain by finding an issuer for the last cert in the chain,
519 * until reaching a self-signed cert, or until no issuer can be found.
521 static BOOL CRYPT_BuildSimpleChain(PCertificateChainEngine engine,
522 HCERTSTORE world, PCERT_SIMPLE_CHAIN chain)
525 PCCERT_CONTEXT cert = chain->rgpElement[chain->cElement - 1]->pCertContext;
527 while (ret && !CRYPT_IsSimpleChainCyclic(chain) &&
528 !CRYPT_IsCertificateSelfSigned(cert))
531 PCCERT_CONTEXT issuer = CRYPT_GetIssuerFromStore(world, cert, &flags);
535 ret = CRYPT_AddCertToSimpleChain(engine, chain, issuer, flags);
540 TRACE("Couldn't find issuer, halting chain creation\n");
547 static BOOL CRYPT_GetSimpleChainForCert(PCertificateChainEngine engine,
548 HCERTSTORE world, PCCERT_CONTEXT cert, LPFILETIME pTime,
549 PCERT_SIMPLE_CHAIN *ppChain)
552 PCERT_SIMPLE_CHAIN chain;
554 TRACE("(%p, %p, %p, %p)\n", engine, world, cert, pTime);
556 chain = CryptMemAlloc(sizeof(CERT_SIMPLE_CHAIN));
559 memset(chain, 0, sizeof(CERT_SIMPLE_CHAIN));
560 chain->cbSize = sizeof(CERT_SIMPLE_CHAIN);
561 ret = CRYPT_AddCertToSimpleChain(engine, chain, cert, 0);
564 ret = CRYPT_BuildSimpleChain(engine, world, chain);
566 ret = CRYPT_CheckSimpleChain(engine, chain, pTime);
570 CRYPT_FreeSimpleChain(chain);
578 static BOOL CRYPT_BuildCandidateChainFromCert(HCERTCHAINENGINE hChainEngine,
579 PCCERT_CONTEXT cert, LPFILETIME pTime, HCERTSTORE hAdditionalStore,
580 PCertificateChain *ppChain)
582 PCertificateChainEngine engine = (PCertificateChainEngine)hChainEngine;
583 PCERT_SIMPLE_CHAIN simpleChain = NULL;
587 world = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, 0,
588 CERT_STORE_CREATE_NEW_FLAG, NULL);
589 CertAddStoreToCollection(world, engine->hWorld, 0, 0);
590 if (hAdditionalStore)
591 CertAddStoreToCollection(world, hAdditionalStore, 0, 0);
592 /* FIXME: only simple chains are supported for now, as CTLs aren't
595 if ((ret = CRYPT_GetSimpleChainForCert(engine, world, cert, pTime,
598 PCertificateChain chain = CryptMemAlloc(sizeof(CertificateChain));
603 chain->world = world;
604 chain->context.cbSize = sizeof(CERT_CHAIN_CONTEXT);
605 memcpy(&chain->context.TrustStatus, &simpleChain->TrustStatus,
606 sizeof(CERT_TRUST_STATUS));
607 chain->context.cChain = 1;
608 chain->context.rgpChain = CryptMemAlloc(sizeof(PCERT_SIMPLE_CHAIN));
609 chain->context.rgpChain[0] = simpleChain;
610 chain->context.cLowerQualityChainContext = 0;
611 chain->context.rgpLowerQualityChainContext = NULL;
612 chain->context.fHasRevocationFreshnessTime = FALSE;
613 chain->context.dwRevocationFreshnessTime = 0;
622 typedef struct _CERT_CHAIN_PARA_NO_EXTRA_FIELDS {
624 CERT_USAGE_MATCH RequestedUsage;
625 } CERT_CHAIN_PARA_NO_EXTRA_FIELDS, *PCERT_CHAIN_PARA_NO_EXTRA_FIELDS;
627 typedef struct _CERT_CHAIN_PARA_EXTRA_FIELDS {
629 CERT_USAGE_MATCH RequestedUsage;
630 CERT_USAGE_MATCH RequestedIssuancePolicy;
631 DWORD dwUrlRetrievalTimeout;
632 BOOL fCheckRevocationFreshnessTime;
633 DWORD dwRevocationFreshnessTime;
634 } CERT_CHAIN_PARA_EXTRA_FIELDS, *PCERT_CHAIN_PARA_EXTRA_FIELDS;
636 BOOL WINAPI CertGetCertificateChain(HCERTCHAINENGINE hChainEngine,
637 PCCERT_CONTEXT pCertContext, LPFILETIME pTime, HCERTSTORE hAdditionalStore,
638 PCERT_CHAIN_PARA pChainPara, DWORD dwFlags, LPVOID pvReserved,
639 PCCERT_CHAIN_CONTEXT* ppChainContext)
642 PCertificateChain chain = NULL;
644 TRACE("(%p, %p, %p, %p, %p, %08x, %p, %p)\n", hChainEngine, pCertContext,
645 pTime, hAdditionalStore, pChainPara, dwFlags, pvReserved, ppChainContext);
649 SetLastError(E_INVALIDARG);
653 *ppChainContext = NULL;
655 hChainEngine = CRYPT_GetDefaultChainEngine();
656 /* FIXME: what about HCCE_LOCAL_MACHINE? */
657 /* FIXME: pChainPara is for now ignored */
658 ret = CRYPT_BuildCandidateChainFromCert(hChainEngine, pCertContext, pTime,
659 hAdditionalStore, &chain);
663 *ppChainContext = (PCCERT_CHAIN_CONTEXT)chain;
665 CertFreeCertificateChain((PCCERT_CHAIN_CONTEXT)chain);
667 TRACE("returning %d\n", ret);
671 static void CRYPT_FreeChainContext(PCertificateChain chain)
675 for (i = 0; i < chain->context.cLowerQualityChainContext; i++)
676 CertFreeCertificateChain(chain->context.rgpLowerQualityChainContext[i]);
677 CryptMemFree(chain->context.rgpLowerQualityChainContext);
678 for (i = 0; i < chain->context.cChain; i++)
679 CRYPT_FreeSimpleChain(chain->context.rgpChain[i]);
680 CryptMemFree(chain->context.rgpChain);
681 CertCloseStore(chain->world, 0);
685 PCCERT_CHAIN_CONTEXT WINAPI CertDuplicateCertificateChain(
686 PCCERT_CHAIN_CONTEXT pChainContext)
688 PCertificateChain chain = (PCertificateChain)pChainContext;
690 TRACE("(%p)\n", pChainContext);
693 InterlockedIncrement(&chain->ref);
694 return pChainContext;
697 void WINAPI CertFreeCertificateChain(PCCERT_CHAIN_CONTEXT pChainContext)
699 PCertificateChain chain = (PCertificateChain)pChainContext;
701 TRACE("(%p)\n", pChainContext);
705 if (InterlockedDecrement(&chain->ref) == 0)
706 CRYPT_FreeChainContext(chain);