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 WINAPI
CRYPT_IsCertificateSelfSigned(PCCERT_CONTEXT cert
)
211 return CertCompareCertificateName(cert
->dwCertEncodingType
,
212 &cert
->pCertInfo
->Subject
, &cert
->pCertInfo
->Issuer
);
215 /* Gets cert's issuer from store, and returns the validity flags associated
216 * with it. Returns NULL if no issuer whose public key matches cert's
217 * signature could be found.
219 static PCCERT_CONTEXT
CRYPT_GetIssuerFromStore(HCERTSTORE store
,
220 PCCERT_CONTEXT cert
, PDWORD pdwFlags
)
222 PCCERT_CONTEXT issuer
= NULL
;
224 /* There might be more than issuer with the same name, so keep looking until
225 * one produces the correct signature for this cert.
228 *pdwFlags
= CERT_STORE_REVOCATION_FLAG
| CERT_STORE_SIGNATURE_FLAG
|
229 CERT_STORE_TIME_VALIDITY_FLAG
;
230 issuer
= CertGetIssuerCertificateFromStore(store
, cert
, issuer
,
232 } while (issuer
&& (*pdwFlags
& CERT_STORE_SIGNATURE_FLAG
));
236 static BOOL
CRYPT_AddCertToSimpleChain(PCERT_SIMPLE_CHAIN chain
,
237 PCCERT_CONTEXT cert
, DWORD dwFlags
)
240 PCERT_CHAIN_ELEMENT element
= CryptMemAlloc(sizeof(CERT_CHAIN_ELEMENT
));
244 if (!chain
->cElement
)
245 chain
->rgpElement
= CryptMemAlloc(sizeof(PCERT_CHAIN_ELEMENT
));
247 chain
->rgpElement
= CryptMemRealloc(chain
->rgpElement
,
248 (chain
->cElement
+ 1) * sizeof(PCERT_CHAIN_ELEMENT
));
249 if (chain
->rgpElement
)
251 memset(element
, 0, sizeof(CERT_CHAIN_ELEMENT
));
252 element
->cbSize
= sizeof(CERT_CHAIN_ELEMENT
);
253 element
->pCertContext
= cert
;
254 if (dwFlags
& CERT_STORE_REVOCATION_FLAG
&&
255 !(dwFlags
& CERT_STORE_NO_CRL_FLAG
))
256 element
->TrustStatus
.dwErrorStatus
|= CERT_TRUST_IS_REVOKED
;
257 if (dwFlags
& CERT_STORE_SIGNATURE_FLAG
)
258 element
->TrustStatus
.dwErrorStatus
|=
259 CERT_TRUST_IS_NOT_SIGNATURE_VALID
;
260 if (dwFlags
& CERT_STORE_TIME_VALIDITY_FLAG
)
261 element
->TrustStatus
.dwErrorStatus
|=
262 CERT_TRUST_IS_NOT_TIME_VALID
;
265 PCERT_CHAIN_ELEMENT prevElement
=
266 chain
->rgpElement
[chain
->cElement
- 1];
268 /* This cert is the issuer of the previous one in the chain, so
269 * retroactively check the previous one's time validity nesting.
271 if (!CertVerifyValidityNesting(
272 prevElement
->pCertContext
->pCertInfo
, cert
->pCertInfo
))
273 prevElement
->TrustStatus
.dwErrorStatus
|=
274 CERT_TRUST_IS_NOT_TIME_NESTED
;
276 /* FIXME: check valid usages, name constraints, and for cycles */
277 /* FIXME: initialize the rest of element */
278 chain
->TrustStatus
.dwErrorStatus
|=
279 element
->TrustStatus
.dwErrorStatus
;
280 chain
->TrustStatus
.dwInfoStatus
|=
281 element
->TrustStatus
.dwInfoStatus
;
282 chain
->rgpElement
[chain
->cElement
++] = element
;
286 CryptMemFree(element
);
291 static void CRYPT_FreeSimpleChain(PCERT_SIMPLE_CHAIN chain
)
295 for (i
= 0; i
< chain
->cElement
; i
++)
296 CryptMemFree(chain
->rgpElement
[i
]);
297 CryptMemFree(chain
->rgpElement
);
301 static BOOL
CRYPT_BuildSimpleChain(HCERTCHAINENGINE hChainEngine
,
302 PCCERT_CONTEXT cert
, LPFILETIME pTime
, HCERTSTORE hAdditionalStore
,
303 PCERT_SIMPLE_CHAIN
*ppChain
)
306 PCertificateChainEngine engine
= (PCertificateChainEngine
)hChainEngine
;
307 PCERT_SIMPLE_CHAIN chain
;
310 TRACE("(%p, %p, %p, %p)\n", hChainEngine
, cert
, pTime
, hAdditionalStore
);
312 world
= CertOpenStore(CERT_STORE_PROV_COLLECTION
, 0, 0,
313 CERT_STORE_CREATE_NEW_FLAG
, NULL
);
314 CertAddStoreToCollection(world
, engine
->hWorld
, 0, 0);
315 if (cert
->hCertStore
)
316 CertAddStoreToCollection(world
, cert
->hCertStore
, 0, 0);
317 if (hAdditionalStore
)
318 CertAddStoreToCollection(world
, hAdditionalStore
, 0, 0);
319 chain
= CryptMemAlloc(sizeof(CERT_SIMPLE_CHAIN
));
322 memset(chain
, 0, sizeof(CERT_SIMPLE_CHAIN
));
323 chain
->cbSize
= sizeof(CERT_SIMPLE_CHAIN
);
324 ret
= CRYPT_AddCertToSimpleChain(chain
, cert
, 0);
325 while (ret
&& !CRYPT_IsCertificateSelfSigned(cert
))
328 PCCERT_CONTEXT issuer
= CRYPT_GetIssuerFromStore(world
, cert
,
333 ret
= CRYPT_AddCertToSimpleChain(chain
, issuer
, flags
);
338 TRACE("Couldn't find issuer, aborting chain creation\n");
344 PCERT_CHAIN_ELEMENT rootElement
=
345 chain
->rgpElement
[chain
->cElement
- 1];
346 PCCERT_CONTEXT root
= rootElement
->pCertContext
;
348 if (!(ret
= CRYPT_IsCertificateSelfSigned(root
)))
349 TRACE("Last certificate is not self-signed\n");
352 rootElement
->TrustStatus
.dwInfoStatus
|=
353 CERT_TRUST_IS_SELF_SIGNED
;
354 if (!(ret
= CryptVerifyCertificateSignatureEx(0,
355 root
->dwCertEncodingType
, CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT
,
356 (void *)root
, CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT
, (void *)root
,
359 TRACE("Last certificate's signature is invalid\n");
360 rootElement
->TrustStatus
.dwErrorStatus
|=
361 CERT_TRUST_IS_NOT_SIGNATURE_VALID
;
367 DWORD size
= sizeof(hash
);
368 CRYPT_HASH_BLOB blob
= { sizeof(hash
), hash
};
369 PCCERT_CONTEXT trustedRoot
;
371 CertGetCertificateContextProperty(root
, CERT_HASH_PROP_ID
, hash
,
373 trustedRoot
= CertFindCertificateInStore(engine
->hRoot
,
374 root
->dwCertEncodingType
, 0, CERT_FIND_SHA1_HASH
, &blob
, NULL
);
376 rootElement
->TrustStatus
.dwErrorStatus
|=
377 CERT_TRUST_IS_UNTRUSTED_ROOT
;
379 CertFreeCertificateContext(trustedRoot
);
381 chain
->TrustStatus
.dwErrorStatus
|=
382 rootElement
->TrustStatus
.dwErrorStatus
;
383 chain
->TrustStatus
.dwInfoStatus
|=
384 rootElement
->TrustStatus
.dwInfoStatus
& ~CERT_TRUST_IS_SELF_SIGNED
;
388 CRYPT_FreeSimpleChain(chain
);
393 CertCloseStore(world
, 0);
397 typedef struct _CERT_CHAIN_PARA_NO_EXTRA_FIELDS
{
399 CERT_USAGE_MATCH RequestedUsage
;
400 } CERT_CHAIN_PARA_NO_EXTRA_FIELDS
, *PCERT_CHAIN_PARA_NO_EXTRA_FIELDS
;
402 typedef struct _CERT_CHAIN_PARA_EXTRA_FIELDS
{
404 CERT_USAGE_MATCH RequestedUsage
;
405 CERT_USAGE_MATCH RequestedIssuancePolicy
;
406 DWORD dwUrlRetrievalTimeout
;
407 BOOL fCheckRevocationFreshnessTime
;
408 DWORD dwRevocationFreshnessTime
;
409 } CERT_CHAIN_PARA_EXTRA_FIELDS
, *PCERT_CHAIN_PARA_EXTRA_FIELDS
;
411 BOOL WINAPI
CertGetCertificateChain(HCERTCHAINENGINE hChainEngine
,
412 PCCERT_CONTEXT pCertContext
, LPFILETIME pTime
, HCERTSTORE hAdditionalStore
,
413 PCERT_CHAIN_PARA pChainPara
, DWORD dwFlags
, LPVOID pvReserved
,
414 PCCERT_CHAIN_CONTEXT
* ppChainContext
)
416 PCERT_SIMPLE_CHAIN simpleChain
= NULL
;
419 TRACE("(%p, %p, %p, %p, %p, %08x, %p, %p)\n", hChainEngine
, pCertContext
,
420 pTime
, hAdditionalStore
, pChainPara
, dwFlags
, pvReserved
, ppChainContext
);
424 SetLastError(E_INVALIDARG
);
428 *ppChainContext
= NULL
;
430 hChainEngine
= CRYPT_GetDefaultChainEngine();
431 /* FIXME: what about HCCE_LOCAL_MACHINE? */
432 /* FIXME: pChainPara is for now ignored */
433 /* FIXME: only simple chains are supported for now, as CTLs aren't
436 if ((ret
= CRYPT_BuildSimpleChain(hChainEngine
, pCertContext
, pTime
,
437 hAdditionalStore
, &simpleChain
)))
439 PCertificateChain chain
= CryptMemAlloc(sizeof(CertificateChain
));
444 chain
->context
.cbSize
= sizeof(CERT_CHAIN_CONTEXT
);
445 memcpy(&chain
->context
.TrustStatus
, &simpleChain
->TrustStatus
,
446 sizeof(CERT_TRUST_STATUS
));
447 chain
->context
.cChain
= 1;
448 chain
->context
.rgpChain
= CryptMemAlloc(sizeof(PCERT_SIMPLE_CHAIN
));
449 chain
->context
.rgpChain
[0] = simpleChain
;
450 chain
->context
.cLowerQualityChainContext
= 0;
451 chain
->context
.rgpLowerQualityChainContext
= NULL
;
452 chain
->context
.fHasRevocationFreshnessTime
= FALSE
;
453 chain
->context
.dwRevocationFreshnessTime
= 0;
458 *ppChainContext
= (PCCERT_CHAIN_CONTEXT
)chain
;
460 CertFreeCertificateChain((PCCERT_CHAIN_CONTEXT
)chain
);
462 TRACE("returning %d\n", ret
);
466 static void CRYPT_FreeChainContext(PCertificateChain chain
)
470 /* Note the chain's rgpLowerQualityChainContext isn't freed, but
471 * it's never set, either.
473 for (i
= 0; i
< chain
->context
.cChain
; i
++)
474 CRYPT_FreeSimpleChain(chain
->context
.rgpChain
[i
]);
475 CryptMemFree(chain
->context
.rgpChain
);
479 void WINAPI
CertFreeCertificateChain(PCCERT_CHAIN_CONTEXT pChainContext
)
481 PCertificateChain chain
= (PCertificateChain
)pChainContext
;
483 TRACE("(%p)\n", pChainContext
);
487 if (InterlockedDecrement(&chain
->ref
) == 0)
488 CRYPT_FreeChainContext(chain
);