crypt32: Don't fail chain creation if signature doesn't match.
[wine/wine-kai.git] / dlls / crypt32 / chain.c
blobe6a0df853571a30df386307d36a53754101e05be
1 /*
2 * Copyright 2006 Juan Lang
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
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"
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
39 LONG ref;
40 HCERTSTORE hRoot;
41 HCERTSTORE hWorld;
42 DWORD dwFlags;
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)
51 DWORD i;
53 for (i = 0; i < cStores; i++)
54 CertAddStoreToCollection(collection, stores[i], 0, 0);
57 static inline void CRYPT_CloseStores(DWORD cStores, HCERTSTORE *stores)
59 DWORD i;
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)
69 BOOL ret = TRUE;
71 if (store)
73 HCERTSTORE rootStore = CertOpenSystemStoreW(0, rootW);
74 PCCERT_CONTEXT cert = NULL, check;
75 BYTE hash[20];
76 DWORD size;
78 do {
79 cert = CertEnumCertificatesInStore(store, cert);
80 if (cert)
82 size = sizeof(hash);
84 ret = CertGetCertificateContextProperty(cert, CERT_HASH_PROP_ID,
85 hash, &size);
86 if (ret)
88 CRYPT_HASH_BLOB blob = { sizeof(hash), hash };
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);
99 } while (ret && cert);
100 if (cert)
101 CertFreeCertificateContext(cert);
102 CertCloseStore(rootStore, 0);
104 return ret;
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 };
113 BOOL ret;
115 TRACE("(%p, %p)\n", pConfig, phChainEngine);
117 if (pConfig->cbSize != sizeof(*pConfig))
119 SetLastError(E_INVALIDARG);
120 return FALSE;
122 *phChainEngine = NULL;
123 ret = CRYPT_CheckRestrictedRoot(pConfig->hRestrictedRoot);
124 if (ret)
126 PCertificateChainEngine engine =
127 CryptMemAlloc(sizeof(CertificateChainEngine));
129 if (engine)
131 HCERTSTORE worldStores[4];
133 engine->ref = 1;
134 if (pConfig->hRestrictedRoot)
135 engine->hRoot = CertDuplicateStore(pConfig->hRestrictedRoot);
136 else
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]),
149 worldStores);
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;
156 else
157 engine->CycleDetectionModulus = DEFAULT_CYCLE_MODULUS;
158 *phChainEngine = (HCERTCHAINENGINE)engine;
159 ret = TRUE;
161 else
162 ret = FALSE;
164 return ret;
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,
191 NULL);
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;
206 LONG ref;
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))
231 cyclicCertIndex = j;
232 if (cyclicCertIndex)
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]);
239 /* Truncate chain */
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)
247 if (chain->cElement)
248 return chain->rgpElement[chain->cElement - 1]->TrustStatus.dwErrorStatus
249 & CERT_TRUST_IS_CYCLIC;
250 else
251 return FALSE;
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)
268 BOOL ret = FALSE;
269 PCERT_CHAIN_ELEMENT element = CryptMemAlloc(sizeof(CERT_CHAIN_ELEMENT));
271 if (element)
273 if (!chain->cElement)
274 chain->rgpElement = CryptMemAlloc(sizeof(PCERT_CHAIN_ELEMENT));
275 else
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;
292 if (chain->cElement)
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;
314 ret = TRUE;
316 else
317 CryptMemFree(element);
319 return ret;
322 static void CRYPT_FreeSimpleChain(PCERT_SIMPLE_CHAIN chain)
324 DWORD i;
326 for (i = 0; i < chain->cElement; i++)
327 CRYPT_FreeChainElement(chain->rgpElement[i]);
328 CryptMemFree(chain->rgpElement);
329 CryptMemFree(chain);
332 static void CRYPT_CheckTrustedStatus(HCERTSTORE hRoot,
333 PCERT_CHAIN_ELEMENT rootElement)
335 BYTE hash[20];
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,
344 &blob, NULL);
345 if (!trustedRoot)
346 rootElement->TrustStatus.dwErrorStatus |=
347 CERT_TRUST_IS_UNTRUSTED_ROOT;
348 else
349 CertFreeCertificateContext(trustedRoot);
352 static BOOL CRYPT_BuildSimpleChain(HCERTCHAINENGINE hChainEngine,
353 PCCERT_CONTEXT cert, LPFILETIME pTime, HCERTSTORE hAdditionalStore,
354 PCERT_SIMPLE_CHAIN *ppChain)
356 BOOL ret = FALSE;
357 PCertificateChainEngine engine = (PCertificateChainEngine)hChainEngine;
358 PCERT_SIMPLE_CHAIN chain;
359 HCERTSTORE world;
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));
371 if (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))
379 DWORD flags;
380 PCCERT_CONTEXT issuer = CRYPT_GetIssuerFromStore(world, cert,
381 &flags);
383 if (issuer)
385 ret = CRYPT_AddCertToSimpleChain(engine, chain, issuer, flags);
386 cert = issuer;
388 else
390 TRACE("Couldn't find issuer, aborting chain creation\n");
391 ret = FALSE;
394 if (ret)
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,
407 0, NULL)))
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;
420 if (!ret)
422 CRYPT_FreeSimpleChain(chain);
423 chain = NULL;
425 *ppChain = chain;
427 CertCloseStore(world, 0);
428 return ret;
431 typedef struct _CERT_CHAIN_PARA_NO_EXTRA_FIELDS {
432 DWORD cbSize;
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 {
437 DWORD cbSize;
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;
451 BOOL ret;
453 TRACE("(%p, %p, %p, %p, %p, %08x, %p, %p)\n", hChainEngine, pCertContext,
454 pTime, hAdditionalStore, pChainPara, dwFlags, pvReserved, ppChainContext);
456 if (!pChainPara)
458 SetLastError(E_INVALIDARG);
459 return FALSE;
461 if (ppChainContext)
462 *ppChainContext = NULL;
463 if (!hChainEngine)
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
468 * supported yet.
470 if ((ret = CRYPT_BuildSimpleChain(hChainEngine, pCertContext, pTime,
471 hAdditionalStore, &simpleChain)))
473 PCertificateChain chain = CryptMemAlloc(sizeof(CertificateChain));
475 if (chain)
477 chain->ref = 1;
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;
489 else
490 ret = FALSE;
491 if (ppChainContext)
492 *ppChainContext = (PCCERT_CHAIN_CONTEXT)chain;
493 else
494 CertFreeCertificateChain((PCCERT_CHAIN_CONTEXT)chain);
496 TRACE("returning %d\n", ret);
497 return ret;
500 static void CRYPT_FreeChainContext(PCertificateChain chain)
502 DWORD i;
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);
510 CryptMemFree(chain);
513 PCCERT_CHAIN_CONTEXT WINAPI CertDuplicateCertificateChain(
514 PCCERT_CHAIN_CONTEXT pChainContext)
516 PCertificateChain chain = (PCertificateChain)pChainContext;
518 TRACE("(%p)\n", pChainContext);
520 if (chain)
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);
531 if (chain)
533 if (InterlockedDecrement(&chain->ref) == 0)
534 CRYPT_FreeChainContext(chain);