push 4d5485f9b89f417d46b39b93e8d940437007f325
[wine/hacks.git] / dlls / crypt32 / chain.c
blob161ba291d4c94029fa0eb86fe0cd8b9607212a69
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 static HCERTCHAINENGINE CRYPT_defaultChainEngine;
30 /* This represents a subset of a certificate chain engine: it doesn't include
31 * the "hOther" store described by MSDN, because I'm not sure how that's used.
32 * It also doesn't include the "hTrust" store, because I don't yet implement
33 * CTLs or complex certificate chains.
35 typedef struct _CertificateChainEngine
37 LONG ref;
38 HCERTSTORE hRoot;
39 HCERTSTORE hWorld;
40 DWORD dwFlags;
41 DWORD dwUrlRetrievalTimeout;
42 DWORD MaximumCachedCertificates;
43 DWORD CycleDetectionModulus;
44 } CertificateChainEngine, *PCertificateChainEngine;
46 static inline void CRYPT_AddStoresToCollection(HCERTSTORE collection,
47 DWORD cStores, HCERTSTORE *stores)
49 DWORD i;
51 for (i = 0; i < cStores; i++)
52 CertAddStoreToCollection(collection, stores[i], 0, 0);
55 static inline void CRYPT_CloseStores(DWORD cStores, HCERTSTORE *stores)
57 DWORD i;
59 for (i = 0; i < cStores; i++)
60 CertCloseStore(stores[i], 0);
63 static const WCHAR rootW[] = { 'R','o','o','t',0 };
65 static BOOL CRYPT_CheckRestrictedRoot(HCERTSTORE store)
67 BOOL ret = TRUE;
69 if (store)
71 HCERTSTORE rootStore = CertOpenSystemStoreW(0, rootW);
72 PCCERT_CONTEXT cert = NULL, check;
73 BYTE hash[20];
74 DWORD size;
76 do {
77 cert = CertEnumCertificatesInStore(store, cert);
78 if (cert)
80 size = sizeof(hash);
82 ret = CertGetCertificateContextProperty(cert, CERT_HASH_PROP_ID,
83 hash, &size);
84 if (ret)
86 CRYPT_HASH_BLOB blob = { sizeof(hash), hash };
88 check = CertFindCertificateInStore(rootStore,
89 cert->dwCertEncodingType, 0, CERT_FIND_SHA1_HASH, &blob,
90 NULL);
91 if (!check)
92 ret = FALSE;
93 else
94 CertFreeCertificateContext(check);
97 } while (ret && cert);
98 if (cert)
99 CertFreeCertificateContext(cert);
100 CertCloseStore(rootStore, 0);
102 return ret;
105 BOOL WINAPI CertCreateCertificateChainEngine(PCERT_CHAIN_ENGINE_CONFIG pConfig,
106 HCERTCHAINENGINE *phChainEngine)
108 static const WCHAR caW[] = { 'C','A',0 };
109 static const WCHAR myW[] = { 'M','y',0 };
110 static const WCHAR trustW[] = { 'T','r','u','s','t',0 };
111 BOOL ret;
113 TRACE("(%p, %p)\n", pConfig, phChainEngine);
115 if (pConfig->cbSize != sizeof(*pConfig))
117 SetLastError(E_INVALIDARG);
118 return FALSE;
120 *phChainEngine = NULL;
121 ret = CRYPT_CheckRestrictedRoot(pConfig->hRestrictedRoot);
122 if (ret)
124 PCertificateChainEngine engine =
125 CryptMemAlloc(sizeof(CertificateChainEngine));
127 if (engine)
129 HCERTSTORE worldStores[4];
131 engine->ref = 1;
132 if (pConfig->hRestrictedRoot)
133 engine->hRoot = CertDuplicateStore(pConfig->hRestrictedRoot);
134 else
135 engine->hRoot = CertOpenSystemStoreW(0, rootW);
136 engine->hWorld = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, 0,
137 CERT_STORE_CREATE_NEW_FLAG, NULL);
138 worldStores[0] = CertDuplicateStore(engine->hRoot);
139 worldStores[1] = CertOpenSystemStoreW(0, caW);
140 worldStores[2] = CertOpenSystemStoreW(0, myW);
141 worldStores[3] = CertOpenSystemStoreW(0, trustW);
142 CRYPT_AddStoresToCollection(engine->hWorld,
143 sizeof(worldStores) / sizeof(worldStores[0]), worldStores);
144 CRYPT_AddStoresToCollection(engine->hWorld,
145 pConfig->cAdditionalStore, pConfig->rghAdditionalStore);
146 CRYPT_CloseStores(sizeof(worldStores) / sizeof(worldStores[0]),
147 worldStores);
148 engine->dwFlags = pConfig->dwFlags;
149 engine->dwUrlRetrievalTimeout = pConfig->dwUrlRetrievalTimeout;
150 engine->MaximumCachedCertificates =
151 pConfig->MaximumCachedCertificates;
152 engine->CycleDetectionModulus = pConfig->CycleDetectionModulus;
153 *phChainEngine = (HCERTCHAINENGINE)engine;
154 ret = TRUE;
156 else
157 ret = FALSE;
159 return ret;
162 void WINAPI CertFreeCertificateChainEngine(HCERTCHAINENGINE hChainEngine)
164 PCertificateChainEngine engine = (PCertificateChainEngine)hChainEngine;
166 TRACE("(%p)\n", hChainEngine);
168 if (engine && InterlockedDecrement(&engine->ref) == 0)
170 CertCloseStore(engine->hWorld, 0);
171 CertCloseStore(engine->hRoot, 0);
172 CryptMemFree(engine);
176 static HCERTCHAINENGINE CRYPT_GetDefaultChainEngine(void)
178 if (!CRYPT_defaultChainEngine)
180 CERT_CHAIN_ENGINE_CONFIG config = { 0 };
181 HCERTCHAINENGINE engine;
183 config.cbSize = sizeof(config);
184 CertCreateCertificateChainEngine(&config, &engine);
185 InterlockedCompareExchangePointer(&CRYPT_defaultChainEngine, engine,
186 NULL);
187 if (CRYPT_defaultChainEngine != engine)
188 CertFreeCertificateChainEngine(engine);
190 return CRYPT_defaultChainEngine;
193 void default_chain_engine_free(void)
195 CertFreeCertificateChainEngine(CRYPT_defaultChainEngine);
198 typedef struct _CertificateChain
200 CERT_CHAIN_CONTEXT context;
201 LONG ref;
202 } CertificateChain, *PCertificateChain;
204 static inline BOOL WINAPI CRYPT_IsCertificateSelfSigned(PCCERT_CONTEXT cert)
206 return CertCompareCertificateName(cert->dwCertEncodingType,
207 &cert->pCertInfo->Subject, &cert->pCertInfo->Issuer);
210 /* Gets cert's issuer from store, and returns the validity flags associated
211 * with it. Returns NULL if no issuer whose public key matches cert's
212 * signature could be found.
214 static PCCERT_CONTEXT CRYPT_GetIssuerFromStore(HCERTSTORE store,
215 PCCERT_CONTEXT cert, PDWORD pdwFlags)
217 PCCERT_CONTEXT issuer = NULL;
219 /* There might be more than issuer with the same name, so keep looking until
220 * one produces the correct signature for this cert.
222 do {
223 *pdwFlags = CERT_STORE_REVOCATION_FLAG | CERT_STORE_SIGNATURE_FLAG |
224 CERT_STORE_TIME_VALIDITY_FLAG;
225 issuer = CertGetIssuerCertificateFromStore(store, cert, issuer,
226 pdwFlags);
227 } while (issuer && (*pdwFlags & CERT_STORE_SIGNATURE_FLAG));
228 return issuer;
231 static BOOL CRYPT_AddCertToSimpleChain(PCERT_SIMPLE_CHAIN chain,
232 PCCERT_CONTEXT cert, DWORD dwFlags)
234 BOOL ret = FALSE;
235 PCERT_CHAIN_ELEMENT element = CryptMemAlloc(sizeof(CERT_CHAIN_ELEMENT));
237 if (element)
239 if (!chain->cElement)
240 chain->rgpElement = CryptMemAlloc(sizeof(PCERT_CHAIN_ELEMENT));
241 else
242 chain->rgpElement = CryptMemRealloc(chain->rgpElement,
243 (chain->cElement + 1) * sizeof(PCERT_CHAIN_ELEMENT));
244 if (chain->rgpElement)
246 memset(element, 0, sizeof(CERT_CHAIN_ELEMENT));
247 element->cbSize = sizeof(CERT_CHAIN_ELEMENT);
248 element->pCertContext = cert;
249 if (dwFlags & CERT_STORE_REVOCATION_FLAG &&
250 !(dwFlags & CERT_STORE_NO_CRL_FLAG))
251 element->TrustStatus.dwErrorStatus |= CERT_TRUST_IS_REVOKED;
252 if (dwFlags & CERT_STORE_SIGNATURE_FLAG)
253 element->TrustStatus.dwErrorStatus |=
254 CERT_TRUST_IS_NOT_SIGNATURE_VALID;
255 if (dwFlags & CERT_STORE_TIME_VALIDITY_FLAG)
256 element->TrustStatus.dwErrorStatus |=
257 CERT_TRUST_IS_NOT_TIME_VALID;
258 /* It appears, from every example certificate chain I've found,
259 * that this flag is always set:
261 element->TrustStatus.dwInfoStatus = CERT_TRUST_HAS_PREFERRED_ISSUER;
262 if (chain->cElement)
264 PCERT_CHAIN_ELEMENT prevElement =
265 chain->rgpElement[chain->cElement - 1];
267 /* This cert is the issuer of the previous one in the chain, so
268 * retroactively check the previous one's time validity nesting.
270 if (!CertVerifyValidityNesting(
271 prevElement->pCertContext->pCertInfo, cert->pCertInfo))
272 prevElement->TrustStatus.dwErrorStatus |=
273 CERT_TRUST_IS_NOT_TIME_NESTED;
275 /* FIXME: check valid usages, name constraints, and for cycles */
276 /* FIXME: initialize the rest of element */
277 chain->TrustStatus.dwErrorStatus |=
278 element->TrustStatus.dwErrorStatus;
279 chain->TrustStatus.dwInfoStatus |=
280 element->TrustStatus.dwInfoStatus;
281 chain->rgpElement[chain->cElement++] = element;
282 ret = TRUE;
284 else
285 CryptMemFree(element);
287 return ret;
290 static void CRYPT_FreeSimpleChain(PCERT_SIMPLE_CHAIN chain)
292 DWORD i;
294 for (i = 0; i < chain->cElement; i++)
295 CryptMemFree(chain->rgpElement[i]);
296 CryptMemFree(chain->rgpElement);
297 CryptMemFree(chain);
300 static BOOL CRYPT_BuildSimpleChain(HCERTCHAINENGINE hChainEngine,
301 PCCERT_CONTEXT cert, LPFILETIME pTime, HCERTSTORE hAdditionalStore,
302 PCERT_SIMPLE_CHAIN *ppChain)
304 BOOL ret = FALSE;
305 PCertificateChainEngine engine = (PCertificateChainEngine)hChainEngine;
306 PCERT_SIMPLE_CHAIN chain;
307 HCERTSTORE world;
309 TRACE("(%p, %p, %p, %p)\n", hChainEngine, cert, pTime, hAdditionalStore);
311 world = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, 0,
312 CERT_STORE_CREATE_NEW_FLAG, NULL);
313 CertAddStoreToCollection(world, engine->hWorld, 0, 0);
314 if (cert->hCertStore)
315 CertAddStoreToCollection(world, cert->hCertStore, 0, 0);
316 if (hAdditionalStore)
317 CertAddStoreToCollection(world, hAdditionalStore, 0, 0);
318 chain = CryptMemAlloc(sizeof(CERT_SIMPLE_CHAIN));
319 if (chain)
321 memset(chain, 0, sizeof(CERT_SIMPLE_CHAIN));
322 chain->cbSize = sizeof(CERT_SIMPLE_CHAIN);
323 ret = CRYPT_AddCertToSimpleChain(chain, cert, 0);
324 while (ret && !CRYPT_IsCertificateSelfSigned(cert))
326 DWORD flags;
327 PCCERT_CONTEXT issuer = CRYPT_GetIssuerFromStore(world, cert,
328 &flags);
330 if (issuer)
332 ret = CRYPT_AddCertToSimpleChain(chain, issuer, flags);
333 cert = issuer;
335 else
337 TRACE("Couldn't find issuer, aborting chain creation\n");
338 ret = FALSE;
341 if (ret)
343 PCCERT_CONTEXT root = chain->rgpElement[chain->cElement - 1]->
344 pCertContext;
346 if (!(ret = CRYPT_IsCertificateSelfSigned(root)))
347 TRACE("Last certificate is not self-signed\n");
348 else
350 chain->rgpElement[chain->cElement - 1]->TrustStatus.dwInfoStatus
351 |= CERT_TRUST_IS_SELF_SIGNED;
352 if (!(ret = CryptVerifyCertificateSignatureEx(0,
353 root->dwCertEncodingType, CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT,
354 (void *)root, CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT, (void *)root,
355 0, NULL)))
356 TRACE("Last certificate's signature is invalid\n");
358 if (ret)
360 BYTE hash[20];
361 DWORD size = sizeof(hash);
362 CRYPT_HASH_BLOB blob = { sizeof(hash), hash };
363 PCCERT_CONTEXT trustedRoot;
365 CertGetCertificateContextProperty(root, CERT_HASH_PROP_ID, hash,
366 &size);
367 trustedRoot = CertFindCertificateInStore(engine->hRoot,
368 root->dwCertEncodingType, 0, CERT_FIND_SHA1_HASH, &blob, NULL);
369 if (!trustedRoot)
370 chain->TrustStatus.dwErrorStatus |=
371 CERT_TRUST_IS_UNTRUSTED_ROOT;
372 else
373 CertFreeCertificateContext(trustedRoot);
376 if (!ret)
378 CRYPT_FreeSimpleChain(chain);
379 chain = NULL;
381 *ppChain = chain;
383 CertCloseStore(world, 0);
384 return ret;
387 typedef struct _CERT_CHAIN_PARA_NO_EXTRA_FIELDS {
388 DWORD cbSize;
389 CERT_USAGE_MATCH RequestedUsage;
390 } CERT_CHAIN_PARA_NO_EXTRA_FIELDS, *PCERT_CHAIN_PARA_NO_EXTRA_FIELDS;
392 typedef struct _CERT_CHAIN_PARA_EXTRA_FIELDS {
393 DWORD cbSize;
394 CERT_USAGE_MATCH RequestedUsage;
395 CERT_USAGE_MATCH RequestedIssuancePolicy;
396 DWORD dwUrlRetrievalTimeout;
397 BOOL fCheckRevocationFreshnessTime;
398 DWORD dwRevocationFreshnessTime;
399 } CERT_CHAIN_PARA_EXTRA_FIELDS, *PCERT_CHAIN_PARA_EXTRA_FIELDS;
401 BOOL WINAPI CertGetCertificateChain(HCERTCHAINENGINE hChainEngine,
402 PCCERT_CONTEXT pCertContext, LPFILETIME pTime, HCERTSTORE hAdditionalStore,
403 PCERT_CHAIN_PARA pChainPara, DWORD dwFlags, LPVOID pvReserved,
404 PCCERT_CHAIN_CONTEXT* ppChainContext)
406 PCERT_SIMPLE_CHAIN simpleChain = NULL;
407 BOOL ret;
409 TRACE("(%p, %p, %p, %p, %p, %08x, %p, %p)\n", hChainEngine, pCertContext,
410 pTime, hAdditionalStore, pChainPara, dwFlags, pvReserved, ppChainContext);
412 if (!pChainPara)
414 SetLastError(E_INVALIDARG);
415 return FALSE;
417 if (ppChainContext)
418 *ppChainContext = NULL;
419 if (!hChainEngine)
420 hChainEngine = CRYPT_GetDefaultChainEngine();
421 /* FIXME: what about HCCE_LOCAL_MACHINE? */
422 /* FIXME: pChainPara is for now ignored */
423 /* FIXME: only simple chains are supported for now, as CTLs aren't
424 * supported yet.
426 if ((ret = CRYPT_BuildSimpleChain(hChainEngine, pCertContext, pTime,
427 hAdditionalStore, &simpleChain)))
429 PCertificateChain chain = CryptMemAlloc(sizeof(CertificateChain));
431 if (chain)
433 chain->ref = 1;
434 chain->context.cbSize = sizeof(CERT_CHAIN_CONTEXT);
435 memcpy(&chain->context.TrustStatus, &simpleChain->TrustStatus,
436 sizeof(CERT_TRUST_STATUS));
437 chain->context.cChain = 1;
438 chain->context.rgpChain = CryptMemAlloc(sizeof(PCERT_SIMPLE_CHAIN));
439 chain->context.rgpChain[0] = simpleChain;
440 chain->context.cLowerQualityChainContext = 0;
441 chain->context.rgpLowerQualityChainContext = NULL;
442 chain->context.fHasRevocationFreshnessTime = FALSE;
443 chain->context.dwRevocationFreshnessTime = 0;
445 else
446 ret = FALSE;
447 if (ppChainContext)
448 *ppChainContext = (PCCERT_CHAIN_CONTEXT)chain;
449 else
450 CertFreeCertificateChain((PCCERT_CHAIN_CONTEXT)chain);
452 TRACE("returning %d\n", ret);
453 return ret;
456 static void CRYPT_FreeChainContext(PCertificateChain chain)
458 DWORD i;
460 /* Note the chain's rgpLowerQualityChainContext isn't freed, but
461 * it's never set, either.
463 for (i = 0; i < chain->context.cChain; i++)
464 CRYPT_FreeSimpleChain(chain->context.rgpChain[i]);
465 CryptMemFree(chain->context.rgpChain);
466 CryptMemFree(chain);
469 void WINAPI CertFreeCertificateChain(PCCERT_CHAIN_CONTEXT pChainContext)
471 PCertificateChain chain = (PCertificateChain)pChainContext;
473 TRACE("(%p)\n", pChainContext);
475 if (chain)
477 if (InterlockedDecrement(&chain->ref) == 0)
478 CRYPT_FreeChainContext(chain);