wintrust: Use a helper function to get a signer's cert info from a message.
[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 #include "windef.h"
21 #include "winbase.h"
22 #include "wincrypt.h"
23 #include "wine/debug.h"
24 #include "crypt32_private.h"
25
26 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
27
28 static HCERTCHAINENGINE CRYPT_defaultChainEngine;
29
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.
34  */
35 typedef struct _CertificateChainEngine
36 {
37     LONG       ref;
38     HCERTSTORE hRoot;
39     HCERTSTORE hWorld;
40     DWORD      dwFlags;
41     DWORD      dwUrlRetrievalTimeout;
42     DWORD      MaximumCachedCertificates;
43     DWORD      CycleDetectionModulus;
44 } CertificateChainEngine, *PCertificateChainEngine;
45
46 static inline void CRYPT_AddStoresToCollection(HCERTSTORE collection,
47  DWORD cStores, HCERTSTORE *stores)
48 {
49     DWORD i;
50
51     for (i = 0; i < cStores; i++)
52         CertAddStoreToCollection(collection, stores[i], 0, 0);
53 }
54
55 static inline void CRYPT_CloseStores(DWORD cStores, HCERTSTORE *stores)
56 {
57     DWORD i;
58
59     for (i = 0; i < cStores; i++)
60         CertCloseStore(stores[i], 0);
61 }
62
63 static const WCHAR rootW[] = { 'R','o','o','t',0 };
64
65 static BOOL CRYPT_CheckRestrictedRoot(HCERTSTORE store)
66 {
67     BOOL ret = TRUE;
68
69     if (store)
70     {
71         HCERTSTORE rootStore = CertOpenSystemStoreW(0, rootW);
72         PCCERT_CONTEXT cert = NULL, check;
73         BYTE hash[20];
74         DWORD size;
75
76         do {
77             cert = CertEnumCertificatesInStore(store, cert);
78             if (cert)
79             {
80                 size = sizeof(hash);
81
82                 ret = CertGetCertificateContextProperty(cert, CERT_HASH_PROP_ID,
83                  hash, &size);
84                 if (ret)
85                 {
86                     CRYPT_HASH_BLOB blob = { sizeof(hash), hash };
87
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);
95                 }
96             }
97         } while (ret && cert);
98         if (cert)
99             CertFreeCertificateContext(cert);
100         CertCloseStore(rootStore, 0);
101     }
102     return ret;
103 }
104
105 BOOL WINAPI CertCreateCertificateChainEngine(PCERT_CHAIN_ENGINE_CONFIG pConfig,
106  HCERTCHAINENGINE *phChainEngine)
107 {
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;
112
113     TRACE("(%p, %p)\n", pConfig, phChainEngine);
114
115     if (pConfig->cbSize != sizeof(*pConfig))
116     {
117         SetLastError(E_INVALIDARG);
118         return FALSE;
119     }
120     *phChainEngine = NULL;
121     ret = CRYPT_CheckRestrictedRoot(pConfig->hRestrictedRoot);
122     if (ret)
123     {
124         PCertificateChainEngine engine =
125          CryptMemAlloc(sizeof(CertificateChainEngine));
126
127         if (engine)
128         {
129             HCERTSTORE worldStores[4];
130
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;
155         }
156         else
157             ret = FALSE;
158     }
159     return ret;
160 }
161
162 void WINAPI CertFreeCertificateChainEngine(HCERTCHAINENGINE hChainEngine)
163 {
164     PCertificateChainEngine engine = (PCertificateChainEngine)hChainEngine;
165
166     TRACE("(%p)\n", hChainEngine);
167
168     if (engine && InterlockedDecrement(&engine->ref) == 0)
169     {
170         CertCloseStore(engine->hWorld, 0);
171         CertCloseStore(engine->hRoot, 0);
172         CryptMemFree(engine);
173     }
174 }
175
176 static HCERTCHAINENGINE CRYPT_GetDefaultChainEngine(void)
177 {
178     if (!CRYPT_defaultChainEngine)
179     {
180         CERT_CHAIN_ENGINE_CONFIG config = { 0 };
181         HCERTCHAINENGINE engine;
182
183         config.cbSize = sizeof(config);
184         CertCreateCertificateChainEngine(&config, &engine);
185         InterlockedCompareExchangePointer(&CRYPT_defaultChainEngine, engine,
186          NULL);
187         if (CRYPT_defaultChainEngine != engine)
188             CertFreeCertificateChainEngine(engine);
189     }
190     return CRYPT_defaultChainEngine;
191 }
192
193 void default_chain_engine_free(void)
194 {
195     CertFreeCertificateChainEngine(CRYPT_defaultChainEngine);
196 }
197
198 typedef struct _CertificateChain
199 {
200     CERT_CHAIN_CONTEXT context;
201     LONG ref;
202 } CertificateChain, *PCertificateChain;
203
204 static inline BOOL WINAPI CRYPT_IsCertificateSelfSigned(PCCERT_CONTEXT cert)
205 {
206     return CertCompareCertificateName(cert->dwCertEncodingType,
207      &cert->pCertInfo->Subject, &cert->pCertInfo->Issuer);
208 }
209
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.
213  */
214 static PCCERT_CONTEXT CRYPT_GetIssuerFromStore(HCERTSTORE store,
215  PCCERT_CONTEXT cert, PDWORD pdwFlags)
216 {
217     PCCERT_CONTEXT issuer = NULL;
218
219     /* There might be more than issuer with the same name, so keep looking until
220      * one produces the correct signature for this cert.
221      */
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;
229 }
230
231 static BOOL CRYPT_AddCertToSimpleChain(PCERT_SIMPLE_CHAIN chain,
232  PCCERT_CONTEXT cert, DWORD dwFlags)
233 {
234     BOOL ret = FALSE;
235     PCERT_CHAIN_ELEMENT element = CryptMemAlloc(sizeof(CERT_CHAIN_ELEMENT));
236
237     if (element)
238     {
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)
245         {
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:
260              */
261             element->TrustStatus.dwInfoStatus = CERT_TRUST_HAS_PREFERRED_ISSUER;
262             if (chain->cElement)
263             {
264                 PCERT_CHAIN_ELEMENT prevElement =
265                  chain->rgpElement[chain->cElement - 1];
266
267                 /* This cert is the issuer of the previous one in the chain, so
268                  * retroactively check the previous one's time validity nesting.
269                  */
270                 if (!CertVerifyValidityNesting(
271                  prevElement->pCertContext->pCertInfo, cert->pCertInfo))
272                     prevElement->TrustStatus.dwErrorStatus |=
273                      CERT_TRUST_IS_NOT_TIME_NESTED;
274             }
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;
283         }
284         else
285             CryptMemFree(element);
286     }
287     return ret;
288 }
289
290 static void CRYPT_FreeSimpleChain(PCERT_SIMPLE_CHAIN chain)
291 {
292     DWORD i;
293
294     for (i = 0; i < chain->cElement; i++)
295         CryptMemFree(chain->rgpElement[i]);
296     CryptMemFree(chain->rgpElement);
297     CryptMemFree(chain);
298 }
299
300 static BOOL CRYPT_BuildSimpleChain(HCERTCHAINENGINE hChainEngine,
301  PCCERT_CONTEXT cert, LPFILETIME pTime, HCERTSTORE hAdditionalStore,
302  PCERT_SIMPLE_CHAIN *ppChain)
303 {
304     BOOL ret = FALSE;
305     PCertificateChainEngine engine = (PCertificateChainEngine)hChainEngine;
306     PCERT_SIMPLE_CHAIN chain;
307     HCERTSTORE world;
308
309     TRACE("(%p, %p, %p, %p)\n", hChainEngine, cert, pTime, hAdditionalStore);
310
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)
320     {
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))
325         {
326             DWORD flags;
327             PCCERT_CONTEXT issuer = CRYPT_GetIssuerFromStore(world, cert,
328              &flags);
329
330             if (issuer)
331             {
332                 ret = CRYPT_AddCertToSimpleChain(chain, issuer, flags);
333                 cert = issuer;
334             }
335             else
336             {
337                 TRACE("Couldn't find issuer, aborting chain creation\n");
338                 ret = FALSE;
339             }
340         }
341         if (ret)
342         {
343             PCERT_CHAIN_ELEMENT rootElement =
344              chain->rgpElement[chain->cElement - 1];
345             PCCERT_CONTEXT root = rootElement->pCertContext;
346
347             if (!(ret = CRYPT_IsCertificateSelfSigned(root)))
348                 TRACE("Last certificate is not self-signed\n");
349             else
350             {
351                 rootElement->TrustStatus.dwInfoStatus |=
352                  CERT_TRUST_IS_SELF_SIGNED;
353                 if (!(ret = CryptVerifyCertificateSignatureEx(0,
354                  root->dwCertEncodingType, CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT,
355                  (void *)root, CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT, (void *)root,
356                  0, NULL)))
357                 {
358                     TRACE("Last certificate's signature is invalid\n");
359                     rootElement->TrustStatus.dwErrorStatus |=
360                      CERT_TRUST_IS_NOT_SIGNATURE_VALID;
361                 }
362             }
363             if (ret)
364             {
365                 BYTE hash[20];
366                 DWORD size = sizeof(hash);
367                 CRYPT_HASH_BLOB blob = { sizeof(hash), hash };
368                 PCCERT_CONTEXT trustedRoot;
369
370                 CertGetCertificateContextProperty(root, CERT_HASH_PROP_ID, hash,
371                  &size);
372                 trustedRoot = CertFindCertificateInStore(engine->hRoot,
373                  root->dwCertEncodingType, 0, CERT_FIND_SHA1_HASH, &blob, NULL);
374                 if (!trustedRoot)
375                     rootElement->TrustStatus.dwErrorStatus |=
376                      CERT_TRUST_IS_UNTRUSTED_ROOT;
377                 else
378                     CertFreeCertificateContext(trustedRoot);
379             }
380             chain->TrustStatus.dwErrorStatus |=
381              rootElement->TrustStatus.dwErrorStatus;
382             chain->TrustStatus.dwInfoStatus |=
383              rootElement->TrustStatus.dwInfoStatus & ~CERT_TRUST_IS_SELF_SIGNED;
384         }
385         if (!ret)
386         {
387             CRYPT_FreeSimpleChain(chain);
388             chain = NULL;
389         }
390         *ppChain = chain;
391     }
392     CertCloseStore(world, 0);
393     return ret;
394 }
395
396 typedef struct _CERT_CHAIN_PARA_NO_EXTRA_FIELDS {
397     DWORD            cbSize;
398     CERT_USAGE_MATCH RequestedUsage;
399 } CERT_CHAIN_PARA_NO_EXTRA_FIELDS, *PCERT_CHAIN_PARA_NO_EXTRA_FIELDS;
400
401 typedef struct _CERT_CHAIN_PARA_EXTRA_FIELDS {
402     DWORD            cbSize;
403     CERT_USAGE_MATCH RequestedUsage;
404     CERT_USAGE_MATCH RequestedIssuancePolicy;
405     DWORD            dwUrlRetrievalTimeout;
406     BOOL             fCheckRevocationFreshnessTime;
407     DWORD            dwRevocationFreshnessTime;
408 } CERT_CHAIN_PARA_EXTRA_FIELDS, *PCERT_CHAIN_PARA_EXTRA_FIELDS;
409
410 BOOL WINAPI CertGetCertificateChain(HCERTCHAINENGINE hChainEngine,
411  PCCERT_CONTEXT pCertContext, LPFILETIME pTime, HCERTSTORE hAdditionalStore,
412  PCERT_CHAIN_PARA pChainPara, DWORD dwFlags, LPVOID pvReserved,
413  PCCERT_CHAIN_CONTEXT* ppChainContext)
414 {
415     PCERT_SIMPLE_CHAIN simpleChain = NULL;
416     BOOL ret;
417
418     TRACE("(%p, %p, %p, %p, %p, %08x, %p, %p)\n", hChainEngine, pCertContext,
419      pTime, hAdditionalStore, pChainPara, dwFlags, pvReserved, ppChainContext);
420
421     if (!pChainPara)
422     {
423         SetLastError(E_INVALIDARG);
424         return FALSE;
425     }
426     if (ppChainContext)
427         *ppChainContext = NULL;
428     if (!hChainEngine)
429         hChainEngine = CRYPT_GetDefaultChainEngine();
430     /* FIXME: what about HCCE_LOCAL_MACHINE? */
431     /* FIXME: pChainPara is for now ignored */
432     /* FIXME: only simple chains are supported for now, as CTLs aren't
433      * supported yet.
434      */
435     if ((ret = CRYPT_BuildSimpleChain(hChainEngine, pCertContext, pTime,
436      hAdditionalStore, &simpleChain)))
437     {
438         PCertificateChain chain = CryptMemAlloc(sizeof(CertificateChain));
439
440         if (chain)
441         {
442             chain->ref = 1;
443             chain->context.cbSize = sizeof(CERT_CHAIN_CONTEXT);
444             memcpy(&chain->context.TrustStatus, &simpleChain->TrustStatus,
445              sizeof(CERT_TRUST_STATUS));
446             chain->context.cChain = 1;
447             chain->context.rgpChain = CryptMemAlloc(sizeof(PCERT_SIMPLE_CHAIN));
448             chain->context.rgpChain[0] = simpleChain;
449             chain->context.cLowerQualityChainContext = 0;
450             chain->context.rgpLowerQualityChainContext = NULL;
451             chain->context.fHasRevocationFreshnessTime = FALSE;
452             chain->context.dwRevocationFreshnessTime = 0;
453         }
454         else
455             ret = FALSE;
456         if (ppChainContext)
457             *ppChainContext = (PCCERT_CHAIN_CONTEXT)chain;
458         else
459             CertFreeCertificateChain((PCCERT_CHAIN_CONTEXT)chain);
460     }
461     TRACE("returning %d\n", ret);
462     return ret;
463 }
464
465 static void CRYPT_FreeChainContext(PCertificateChain chain)
466 {
467     DWORD i;
468
469     /* Note the chain's rgpLowerQualityChainContext isn't freed, but
470      * it's never set, either.
471      */
472     for (i = 0; i < chain->context.cChain; i++)
473         CRYPT_FreeSimpleChain(chain->context.rgpChain[i]);
474     CryptMemFree(chain->context.rgpChain);
475     CryptMemFree(chain);
476 }
477
478 void WINAPI CertFreeCertificateChain(PCCERT_CHAIN_CONTEXT pChainContext)
479 {
480     PCertificateChain chain = (PCertificateChain)pChainContext;
481
482     TRACE("(%p)\n", pChainContext);
483
484     if (chain)
485     {
486         if (InterlockedDecrement(&chain->ref) == 0)
487             CRYPT_FreeChainContext(chain);
488     }
489 }