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 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
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
)
51 for (i
= 0; i
< cStores
; i
++)
52 CertAddStoreToCollection(collection
, stores
[i
], 0, 0);
55 static inline void CRYPT_CloseStores(DWORD cStores
, HCERTSTORE
*stores
)
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
)
71 HCERTSTORE rootStore
= CertOpenSystemStoreW(0, rootW
);
72 PCCERT_CONTEXT cert
= NULL
, check
;
77 cert
= CertEnumCertificatesInStore(store
, cert
);
82 ret
= CertGetCertificateContextProperty(cert
, CERT_HASH_PROP_ID
,
86 CRYPT_HASH_BLOB blob
= { sizeof(hash
), hash
};
88 check
= CertFindCertificateInStore(rootStore
,
89 cert
->dwCertEncodingType
, 0, CERT_FIND_SHA1_HASH
, &blob
,
94 CertFreeCertificateContext(check
);
97 } while (ret
&& cert
);
99 CertFreeCertificateContext(cert
);
100 CertCloseStore(rootStore
, 0);
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 };
113 TRACE("(%p, %p)\n", pConfig
, phChainEngine
);
115 if (pConfig
->cbSize
!= sizeof(*pConfig
))
117 SetLastError(E_INVALIDARG
);
120 *phChainEngine
= NULL
;
121 ret
= CRYPT_CheckRestrictedRoot(pConfig
->hRestrictedRoot
);
124 PCertificateChainEngine engine
=
125 CryptMemAlloc(sizeof(CertificateChainEngine
));
129 HCERTSTORE worldStores
[4];
132 if (pConfig
->hRestrictedRoot
)
133 engine
->hRoot
= CertDuplicateStore(pConfig
->hRestrictedRoot
);
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]),
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
;
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
,
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
;
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.
223 *pdwFlags
= CERT_STORE_REVOCATION_FLAG
| CERT_STORE_SIGNATURE_FLAG
|
224 CERT_STORE_TIME_VALIDITY_FLAG
;
225 issuer
= CertGetIssuerCertificateFromStore(store
, cert
, issuer
,
227 } while (issuer
&& (*pdwFlags
& CERT_STORE_SIGNATURE_FLAG
));
231 static BOOL
CRYPT_AddCertToSimpleChain(PCERT_SIMPLE_CHAIN chain
,
232 PCCERT_CONTEXT cert
, DWORD dwFlags
)
235 PCERT_CHAIN_ELEMENT element
= CryptMemAlloc(sizeof(CERT_CHAIN_ELEMENT
));
239 if (!chain
->cElement
)
240 chain
->rgpElement
= CryptMemAlloc(sizeof(PCERT_CHAIN_ELEMENT
));
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
;
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
;
285 CryptMemFree(element
);
290 static void CRYPT_FreeSimpleChain(PCERT_SIMPLE_CHAIN chain
)
294 for (i
= 0; i
< chain
->cElement
; i
++)
295 CryptMemFree(chain
->rgpElement
[i
]);
296 CryptMemFree(chain
->rgpElement
);
300 static BOOL
CRYPT_BuildSimpleChain(HCERTCHAINENGINE hChainEngine
,
301 PCCERT_CONTEXT cert
, LPFILETIME pTime
, HCERTSTORE hAdditionalStore
,
302 PCERT_SIMPLE_CHAIN
*ppChain
)
305 PCertificateChainEngine engine
= (PCertificateChainEngine
)hChainEngine
;
306 PCERT_SIMPLE_CHAIN chain
;
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
));
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
))
327 PCCERT_CONTEXT issuer
= CRYPT_GetIssuerFromStore(world
, cert
,
332 ret
= CRYPT_AddCertToSimpleChain(chain
, issuer
, flags
);
337 TRACE("Couldn't find issuer, aborting chain creation\n");
343 PCCERT_CONTEXT root
= chain
->rgpElement
[chain
->cElement
- 1]->
346 if (!(ret
= CRYPT_IsCertificateSelfSigned(root
)))
347 TRACE("Last certificate is not self-signed\n");
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
,
356 TRACE("Last certificate's signature is invalid\n");
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
,
367 trustedRoot
= CertFindCertificateInStore(engine
->hRoot
,
368 root
->dwCertEncodingType
, 0, CERT_FIND_SHA1_HASH
, &blob
, NULL
);
370 chain
->TrustStatus
.dwErrorStatus
|=
371 CERT_TRUST_IS_UNTRUSTED_ROOT
;
373 CertFreeCertificateContext(trustedRoot
);
378 CRYPT_FreeSimpleChain(chain
);
383 CertCloseStore(world
, 0);
387 typedef struct _CERT_CHAIN_PARA_NO_EXTRA_FIELDS
{
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
{
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
;
409 TRACE("(%p, %p, %p, %p, %p, %08x, %p, %p)\n", hChainEngine
, pCertContext
,
410 pTime
, hAdditionalStore
, pChainPara
, dwFlags
, pvReserved
, ppChainContext
);
414 SetLastError(E_INVALIDARG
);
418 *ppChainContext
= NULL
;
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
426 if ((ret
= CRYPT_BuildSimpleChain(hChainEngine
, pCertContext
, pTime
,
427 hAdditionalStore
, &simpleChain
)))
429 PCertificateChain chain
= CryptMemAlloc(sizeof(CertificateChain
));
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;
448 *ppChainContext
= (PCCERT_CHAIN_CONTEXT
)chain
;
450 CertFreeCertificateChain((PCCERT_CHAIN_CONTEXT
)chain
);
452 TRACE("returning %d\n", ret
);
456 static void CRYPT_FreeChainContext(PCertificateChain chain
)
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
);
469 void WINAPI
CertFreeCertificateChain(PCCERT_CHAIN_CONTEXT pChainContext
)
471 PCertificateChain chain
= (PCertificateChain
)pChainContext
;
473 TRACE("(%p)\n", pChainContext
);
477 if (InterlockedDecrement(&chain
->ref
) == 0)
478 CRYPT_FreeChainContext(chain
);