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
;
207 } CertificateChain
, *PCertificateChain
;
209 static inline BOOL
CRYPT_IsCertificateSelfSigned(PCCERT_CONTEXT cert
)
211 return CertCompareCertificateName(cert
->dwCertEncodingType
,
212 &cert
->pCertInfo
->Subject
, &cert
->pCertInfo
->Issuer
);
215 static void CRYPT_FreeChainElement(PCERT_CHAIN_ELEMENT element
)
217 CertFreeCertificateContext(element
->pCertContext
);
218 CryptMemFree(element
);
221 static void CRYPT_CheckSimpleChainForCycles(PCERT_SIMPLE_CHAIN chain
)
223 DWORD i
, j
, cyclicCertIndex
= 0;
225 /* O(n^2) - I don't think there's a faster way */
226 for (i
= 0; !cyclicCertIndex
&& i
< chain
->cElement
; i
++)
227 for (j
= i
+ 1; !cyclicCertIndex
&& j
< chain
->cElement
; j
++)
228 if (CertCompareCertificate(X509_ASN_ENCODING
,
229 chain
->rgpElement
[i
]->pCertContext
->pCertInfo
,
230 chain
->rgpElement
[j
]->pCertContext
->pCertInfo
))
234 chain
->rgpElement
[cyclicCertIndex
]->TrustStatus
.dwErrorStatus
235 |= CERT_TRUST_IS_CYCLIC
;
236 /* Release remaining certs */
237 for (i
= cyclicCertIndex
+ 1; i
< chain
->cElement
; i
++)
238 CRYPT_FreeChainElement(chain
->rgpElement
[i
]);
240 chain
->cElement
= cyclicCertIndex
+ 1;
244 /* Checks whether the chain is cyclic by examining the last element's status */
245 static inline BOOL
CRYPT_IsSimpleChainCyclic(PCERT_SIMPLE_CHAIN chain
)
248 return chain
->rgpElement
[chain
->cElement
- 1]->TrustStatus
.dwErrorStatus
249 & CERT_TRUST_IS_CYCLIC
;
254 /* Gets cert's issuer from store, and returns the validity flags associated
255 * with it. Returns NULL if no issuer signature could be found.
257 static PCCERT_CONTEXT
CRYPT_GetIssuerFromStore(HCERTSTORE store
,
258 PCCERT_CONTEXT cert
, PDWORD pdwFlags
)
260 *pdwFlags
= CERT_STORE_REVOCATION_FLAG
| CERT_STORE_SIGNATURE_FLAG
|
261 CERT_STORE_TIME_VALIDITY_FLAG
;
262 return CertGetIssuerCertificateFromStore(store
, cert
, NULL
, pdwFlags
);
265 static BOOL
CRYPT_AddCertToSimpleChain(PCertificateChainEngine engine
,
266 PCERT_SIMPLE_CHAIN chain
, PCCERT_CONTEXT cert
, DWORD dwFlags
)
269 PCERT_CHAIN_ELEMENT element
= CryptMemAlloc(sizeof(CERT_CHAIN_ELEMENT
));
273 if (!chain
->cElement
)
274 chain
->rgpElement
= CryptMemAlloc(sizeof(PCERT_CHAIN_ELEMENT
));
276 chain
->rgpElement
= CryptMemRealloc(chain
->rgpElement
,
277 (chain
->cElement
+ 1) * sizeof(PCERT_CHAIN_ELEMENT
));
278 if (chain
->rgpElement
)
280 memset(element
, 0, sizeof(CERT_CHAIN_ELEMENT
));
281 element
->cbSize
= sizeof(CERT_CHAIN_ELEMENT
);
282 element
->pCertContext
= CertDuplicateCertificateContext(cert
);
283 if (dwFlags
& CERT_STORE_REVOCATION_FLAG
&&
284 !(dwFlags
& CERT_STORE_NO_CRL_FLAG
))
285 element
->TrustStatus
.dwErrorStatus
|= CERT_TRUST_IS_REVOKED
;
286 if (dwFlags
& CERT_STORE_SIGNATURE_FLAG
)
287 element
->TrustStatus
.dwErrorStatus
|=
288 CERT_TRUST_IS_NOT_SIGNATURE_VALID
;
289 if (dwFlags
& CERT_STORE_TIME_VALIDITY_FLAG
)
290 element
->TrustStatus
.dwErrorStatus
|=
291 CERT_TRUST_IS_NOT_TIME_VALID
;
294 PCERT_CHAIN_ELEMENT prevElement
=
295 chain
->rgpElement
[chain
->cElement
- 1];
297 /* This cert is the issuer of the previous one in the chain, so
298 * retroactively check the previous one's time validity nesting.
300 if (!CertVerifyValidityNesting(
301 prevElement
->pCertContext
->pCertInfo
, cert
->pCertInfo
))
302 prevElement
->TrustStatus
.dwErrorStatus
|=
303 CERT_TRUST_IS_NOT_TIME_NESTED
;
305 /* FIXME: check valid usages and name constraints */
306 /* FIXME: initialize the rest of element */
307 chain
->rgpElement
[chain
->cElement
++] = element
;
308 if (chain
->cElement
% engine
->CycleDetectionModulus
)
309 CRYPT_CheckSimpleChainForCycles(chain
);
310 chain
->TrustStatus
.dwErrorStatus
|=
311 element
->TrustStatus
.dwErrorStatus
;
312 chain
->TrustStatus
.dwInfoStatus
|=
313 element
->TrustStatus
.dwInfoStatus
;
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_BuildSimpleChain(HCERTCHAINENGINE hChainEngine
,
353 PCCERT_CONTEXT cert
, LPFILETIME pTime
, HCERTSTORE hAdditionalStore
,
354 PCERT_SIMPLE_CHAIN
*ppChain
)
357 PCertificateChainEngine engine
= (PCertificateChainEngine
)hChainEngine
;
358 PCERT_SIMPLE_CHAIN chain
;
361 TRACE("(%p, %p, %p, %p)\n", hChainEngine
, cert
, pTime
, hAdditionalStore
);
363 world
= CertOpenStore(CERT_STORE_PROV_COLLECTION
, 0, 0,
364 CERT_STORE_CREATE_NEW_FLAG
, NULL
);
365 CertAddStoreToCollection(world
, engine
->hWorld
, 0, 0);
366 if (cert
->hCertStore
)
367 CertAddStoreToCollection(world
, cert
->hCertStore
, 0, 0);
368 if (hAdditionalStore
)
369 CertAddStoreToCollection(world
, hAdditionalStore
, 0, 0);
370 chain
= CryptMemAlloc(sizeof(CERT_SIMPLE_CHAIN
));
373 memset(chain
, 0, sizeof(CERT_SIMPLE_CHAIN
));
374 chain
->cbSize
= sizeof(CERT_SIMPLE_CHAIN
);
375 ret
= CRYPT_AddCertToSimpleChain(engine
, chain
, cert
, 0);
376 while (ret
&& !CRYPT_IsSimpleChainCyclic(chain
) &&
377 !CRYPT_IsCertificateSelfSigned(cert
))
380 PCCERT_CONTEXT issuer
= CRYPT_GetIssuerFromStore(world
, cert
,
385 ret
= CRYPT_AddCertToSimpleChain(engine
, chain
, issuer
, flags
);
390 TRACE("Couldn't find issuer, aborting chain creation\n");
396 PCERT_CHAIN_ELEMENT rootElement
=
397 chain
->rgpElement
[chain
->cElement
- 1];
398 PCCERT_CONTEXT root
= rootElement
->pCertContext
;
400 if (CRYPT_IsCertificateSelfSigned(root
))
402 rootElement
->TrustStatus
.dwInfoStatus
|=
403 CERT_TRUST_IS_SELF_SIGNED
;
404 if (!(ret
= CryptVerifyCertificateSignatureEx(0,
405 root
->dwCertEncodingType
, CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT
,
406 (void *)root
, CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT
, (void *)root
,
409 TRACE("Last certificate's signature is invalid\n");
410 rootElement
->TrustStatus
.dwErrorStatus
|=
411 CERT_TRUST_IS_NOT_SIGNATURE_VALID
;
413 CRYPT_CheckTrustedStatus(engine
->hRoot
, rootElement
);
415 chain
->TrustStatus
.dwErrorStatus
|=
416 rootElement
->TrustStatus
.dwErrorStatus
;
417 chain
->TrustStatus
.dwInfoStatus
|=
418 rootElement
->TrustStatus
.dwInfoStatus
& ~CERT_TRUST_IS_SELF_SIGNED
;
422 CRYPT_FreeSimpleChain(chain
);
427 CertCloseStore(world
, 0);
431 typedef struct _CERT_CHAIN_PARA_NO_EXTRA_FIELDS
{
433 CERT_USAGE_MATCH RequestedUsage
;
434 } CERT_CHAIN_PARA_NO_EXTRA_FIELDS
, *PCERT_CHAIN_PARA_NO_EXTRA_FIELDS
;
436 typedef struct _CERT_CHAIN_PARA_EXTRA_FIELDS
{
438 CERT_USAGE_MATCH RequestedUsage
;
439 CERT_USAGE_MATCH RequestedIssuancePolicy
;
440 DWORD dwUrlRetrievalTimeout
;
441 BOOL fCheckRevocationFreshnessTime
;
442 DWORD dwRevocationFreshnessTime
;
443 } CERT_CHAIN_PARA_EXTRA_FIELDS
, *PCERT_CHAIN_PARA_EXTRA_FIELDS
;
445 BOOL WINAPI
CertGetCertificateChain(HCERTCHAINENGINE hChainEngine
,
446 PCCERT_CONTEXT pCertContext
, LPFILETIME pTime
, HCERTSTORE hAdditionalStore
,
447 PCERT_CHAIN_PARA pChainPara
, DWORD dwFlags
, LPVOID pvReserved
,
448 PCCERT_CHAIN_CONTEXT
* ppChainContext
)
450 PCERT_SIMPLE_CHAIN simpleChain
= NULL
;
453 TRACE("(%p, %p, %p, %p, %p, %08x, %p, %p)\n", hChainEngine
, pCertContext
,
454 pTime
, hAdditionalStore
, pChainPara
, dwFlags
, pvReserved
, ppChainContext
);
458 SetLastError(E_INVALIDARG
);
462 *ppChainContext
= NULL
;
464 hChainEngine
= CRYPT_GetDefaultChainEngine();
465 /* FIXME: what about HCCE_LOCAL_MACHINE? */
466 /* FIXME: pChainPara is for now ignored */
467 /* FIXME: only simple chains are supported for now, as CTLs aren't
470 if ((ret
= CRYPT_BuildSimpleChain(hChainEngine
, pCertContext
, pTime
,
471 hAdditionalStore
, &simpleChain
)))
473 PCertificateChain chain
= CryptMemAlloc(sizeof(CertificateChain
));
478 chain
->context
.cbSize
= sizeof(CERT_CHAIN_CONTEXT
);
479 memcpy(&chain
->context
.TrustStatus
, &simpleChain
->TrustStatus
,
480 sizeof(CERT_TRUST_STATUS
));
481 chain
->context
.cChain
= 1;
482 chain
->context
.rgpChain
= CryptMemAlloc(sizeof(PCERT_SIMPLE_CHAIN
));
483 chain
->context
.rgpChain
[0] = simpleChain
;
484 chain
->context
.cLowerQualityChainContext
= 0;
485 chain
->context
.rgpLowerQualityChainContext
= NULL
;
486 chain
->context
.fHasRevocationFreshnessTime
= FALSE
;
487 chain
->context
.dwRevocationFreshnessTime
= 0;
492 *ppChainContext
= (PCCERT_CHAIN_CONTEXT
)chain
;
494 CertFreeCertificateChain((PCCERT_CHAIN_CONTEXT
)chain
);
496 TRACE("returning %d\n", ret
);
500 static void CRYPT_FreeChainContext(PCertificateChain chain
)
504 for (i
= 0; i
< chain
->context
.cLowerQualityChainContext
; i
++)
505 CertFreeCertificateChain(chain
->context
.rgpLowerQualityChainContext
[i
]);
506 CryptMemFree(chain
->context
.rgpLowerQualityChainContext
);
507 for (i
= 0; i
< chain
->context
.cChain
; i
++)
508 CRYPT_FreeSimpleChain(chain
->context
.rgpChain
[i
]);
509 CryptMemFree(chain
->context
.rgpChain
);
513 PCCERT_CHAIN_CONTEXT WINAPI
CertDuplicateCertificateChain(
514 PCCERT_CHAIN_CONTEXT pChainContext
)
516 PCertificateChain chain
= (PCertificateChain
)pChainContext
;
518 TRACE("(%p)\n", pChainContext
);
521 InterlockedIncrement(&chain
->ref
);
522 return pChainContext
;
525 void WINAPI
CertFreeCertificateChain(PCCERT_CHAIN_CONTEXT pChainContext
)
527 PCertificateChain chain
= (PCertificateChain
)pChainContext
;
529 TRACE("(%p)\n", pChainContext
);
533 if (InterlockedDecrement(&chain
->ref
) == 0)
534 CRYPT_FreeChainContext(chain
);