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 <string.h>
|
---|
21 | #define NONAMELESSUNION
|
---|
22 | #include "windef.h"
|
---|
23 | #include "winbase.h"
|
---|
24 | #define CERT_CHAIN_PARA_HAS_EXTRA_FIELDS
|
---|
25 | #define CERT_REVOCATION_PARA_HAS_EXTRA_FIELDS
|
---|
26 | #include "winerror.h"
|
---|
27 | #include "wincrypt.h"
|
---|
28 | #include "wine/debug.h"
|
---|
29 | #include "wine/unicode.h"
|
---|
30 | #include "crypt32_private.h"
|
---|
31 |
|
---|
32 | WINE_DEFAULT_DEBUG_CHANNEL(crypt);
|
---|
33 |
|
---|
34 | #define DEFAULT_CYCLE_MODULUS 7
|
---|
35 |
|
---|
36 | static HCERTCHAINENGINE CRYPT_defaultChainEngine;
|
---|
37 |
|
---|
38 | /* This represents a subset of a certificate chain engine: it doesn't include
|
---|
39 | * the "hOther" store described by MSDN, because I'm not sure how that's used.
|
---|
40 | * It also doesn't include the "hTrust" store, because I don't yet implement
|
---|
41 | * CTLs or complex certificate chains.
|
---|
42 | */
|
---|
43 | typedef struct _CertificateChainEngine
|
---|
44 | {
|
---|
45 | LONG ref;
|
---|
46 | HCERTSTORE hRoot;
|
---|
47 | HCERTSTORE hWorld;
|
---|
48 | DWORD dwFlags;
|
---|
49 | DWORD dwUrlRetrievalTimeout;
|
---|
50 | DWORD MaximumCachedCertificates;
|
---|
51 | DWORD CycleDetectionModulus;
|
---|
52 | } CertificateChainEngine, *PCertificateChainEngine;
|
---|
53 |
|
---|
54 | static inline void CRYPT_AddStoresToCollection(HCERTSTORE collection,
|
---|
55 | DWORD cStores, HCERTSTORE *stores)
|
---|
56 | {
|
---|
57 | DWORD i;
|
---|
58 |
|
---|
59 | for (i = 0; i < cStores; i++)
|
---|
60 | CertAddStoreToCollection(collection, stores[i], 0, 0);
|
---|
61 | }
|
---|
62 |
|
---|
63 | static inline void CRYPT_CloseStores(DWORD cStores, HCERTSTORE *stores)
|
---|
64 | {
|
---|
65 | DWORD i;
|
---|
66 |
|
---|
67 | for (i = 0; i < cStores; i++)
|
---|
68 | CertCloseStore(stores[i], 0);
|
---|
69 | }
|
---|
70 |
|
---|
71 | static const WCHAR rootW[] = { 'R','o','o','t',0 };
|
---|
72 |
|
---|
73 | static BOOL CRYPT_CheckRestrictedRoot(HCERTSTORE store)
|
---|
74 | {
|
---|
75 | BOOL ret = TRUE;
|
---|
76 |
|
---|
77 | if (store)
|
---|
78 | {
|
---|
79 | HCERTSTORE rootStore = CertOpenSystemStoreW(0, rootW);
|
---|
80 | PCCERT_CONTEXT cert = NULL, check;
|
---|
81 | BYTE hash[20];
|
---|
82 | DWORD size;
|
---|
83 |
|
---|
84 | do {
|
---|
85 | cert = CertEnumCertificatesInStore(store, cert);
|
---|
86 | if (cert)
|
---|
87 | {
|
---|
88 | size = sizeof(hash);
|
---|
89 |
|
---|
90 | ret = CertGetCertificateContextProperty(cert, CERT_HASH_PROP_ID,
|
---|
91 | hash, &size);
|
---|
92 | if (ret)
|
---|
93 | {
|
---|
94 | CRYPT_HASH_BLOB blob = { sizeof(hash), hash };
|
---|
95 |
|
---|
96 | check = CertFindCertificateInStore(rootStore,
|
---|
97 | cert->dwCertEncodingType, 0, CERT_FIND_SHA1_HASH, &blob,
|
---|
98 | NULL);
|
---|
99 | if (!check)
|
---|
100 | ret = FALSE;
|
---|
101 | else
|
---|
102 | CertFreeCertificateContext(check);
|
---|
103 | }
|
---|
104 | }
|
---|
105 | } while (ret && cert);
|
---|
106 | if (cert)
|
---|
107 | CertFreeCertificateContext(cert);
|
---|
108 | CertCloseStore(rootStore, 0);
|
---|
109 | }
|
---|
110 | return ret;
|
---|
111 | }
|
---|
112 |
|
---|
113 | HCERTCHAINENGINE CRYPT_CreateChainEngine(HCERTSTORE root,
|
---|
114 | PCERT_CHAIN_ENGINE_CONFIG pConfig)
|
---|
115 | {
|
---|
116 | static const WCHAR caW[] = { 'C','A',0 };
|
---|
117 | static const WCHAR myW[] = { 'M','y',0 };
|
---|
118 | static const WCHAR trustW[] = { 'T','r','u','s','t',0 };
|
---|
119 | PCertificateChainEngine engine =
|
---|
120 | CryptMemAlloc(sizeof(CertificateChainEngine));
|
---|
121 |
|
---|
122 | if (engine)
|
---|
123 | {
|
---|
124 | HCERTSTORE worldStores[4];
|
---|
125 |
|
---|
126 | engine->ref = 1;
|
---|
127 | engine->hRoot = root;
|
---|
128 | engine->hWorld = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, 0,
|
---|
129 | CERT_STORE_CREATE_NEW_FLAG, NULL);
|
---|
130 | worldStores[0] = CertDuplicateStore(engine->hRoot);
|
---|
131 | worldStores[1] = CertOpenSystemStoreW(0, caW);
|
---|
132 | worldStores[2] = CertOpenSystemStoreW(0, myW);
|
---|
133 | worldStores[3] = CertOpenSystemStoreW(0, trustW);
|
---|
134 | CRYPT_AddStoresToCollection(engine->hWorld,
|
---|
135 | sizeof(worldStores) / sizeof(worldStores[0]), worldStores);
|
---|
136 | CRYPT_AddStoresToCollection(engine->hWorld,
|
---|
137 | pConfig->cAdditionalStore, pConfig->rghAdditionalStore);
|
---|
138 | CRYPT_CloseStores(sizeof(worldStores) / sizeof(worldStores[0]),
|
---|
139 | worldStores);
|
---|
140 | engine->dwFlags = pConfig->dwFlags;
|
---|
141 | engine->dwUrlRetrievalTimeout = pConfig->dwUrlRetrievalTimeout;
|
---|
142 | engine->MaximumCachedCertificates =
|
---|
143 | pConfig->MaximumCachedCertificates;
|
---|
144 | if (pConfig->CycleDetectionModulus)
|
---|
145 | engine->CycleDetectionModulus = pConfig->CycleDetectionModulus;
|
---|
146 | else
|
---|
147 | engine->CycleDetectionModulus = DEFAULT_CYCLE_MODULUS;
|
---|
148 | }
|
---|
149 | return (HCERTCHAINENGINE)engine;
|
---|
150 | }
|
---|
151 |
|
---|
152 | BOOL WINAPI CertCreateCertificateChainEngine(PCERT_CHAIN_ENGINE_CONFIG pConfig,
|
---|
153 | HCERTCHAINENGINE *phChainEngine)
|
---|
154 | {
|
---|
155 | BOOL ret;
|
---|
156 |
|
---|
157 | TRACE("(%p, %p)\n", pConfig, phChainEngine);
|
---|
158 |
|
---|
159 | if (pConfig->cbSize != sizeof(*pConfig))
|
---|
160 | {
|
---|
161 | SetLastError(E_INVALIDARG);
|
---|
162 | return FALSE;
|
---|
163 | }
|
---|
164 | *phChainEngine = NULL;
|
---|
165 | ret = CRYPT_CheckRestrictedRoot(pConfig->hRestrictedRoot);
|
---|
166 | if (ret)
|
---|
167 | {
|
---|
168 | HCERTSTORE root;
|
---|
169 | HCERTCHAINENGINE engine;
|
---|
170 |
|
---|
171 | if (pConfig->hRestrictedRoot)
|
---|
172 | root = CertDuplicateStore(pConfig->hRestrictedRoot);
|
---|
173 | else
|
---|
174 | root = CertOpenSystemStoreW(0, rootW);
|
---|
175 | engine = CRYPT_CreateChainEngine(root, pConfig);
|
---|
176 | if (engine)
|
---|
177 | {
|
---|
178 | *phChainEngine = engine;
|
---|
179 | ret = TRUE;
|
---|
180 | }
|
---|
181 | else
|
---|
182 | ret = FALSE;
|
---|
183 | }
|
---|
184 | return ret;
|
---|
185 | }
|
---|
186 |
|
---|
187 | VOID WINAPI CertFreeCertificateChainEngine(HCERTCHAINENGINE hChainEngine)
|
---|
188 | {
|
---|
189 | PCertificateChainEngine engine = (PCertificateChainEngine)hChainEngine;
|
---|
190 |
|
---|
191 | TRACE("(%p)\n", hChainEngine);
|
---|
192 |
|
---|
193 | if (engine && InterlockedDecrement(&engine->ref) == 0)
|
---|
194 | {
|
---|
195 | CertCloseStore(engine->hWorld, 0);
|
---|
196 | CertCloseStore(engine->hRoot, 0);
|
---|
197 | CryptMemFree(engine);
|
---|
198 | }
|
---|
199 | }
|
---|
200 |
|
---|
201 | static HCERTCHAINENGINE CRYPT_GetDefaultChainEngine(void)
|
---|
202 | {
|
---|
203 | if (!CRYPT_defaultChainEngine)
|
---|
204 | {
|
---|
205 | CERT_CHAIN_ENGINE_CONFIG config = { 0 };
|
---|
206 | HCERTCHAINENGINE engine;
|
---|
207 |
|
---|
208 | config.cbSize = sizeof(config);
|
---|
209 | CertCreateCertificateChainEngine(&config, &engine);
|
---|
210 | InterlockedCompareExchangePointer(&CRYPT_defaultChainEngine, engine,
|
---|
211 | NULL);
|
---|
212 | if (CRYPT_defaultChainEngine != engine)
|
---|
213 | CertFreeCertificateChainEngine(engine);
|
---|
214 | }
|
---|
215 | return CRYPT_defaultChainEngine;
|
---|
216 | }
|
---|
217 |
|
---|
218 | void default_chain_engine_free(void)
|
---|
219 | {
|
---|
220 | CertFreeCertificateChainEngine(CRYPT_defaultChainEngine);
|
---|
221 | }
|
---|
222 |
|
---|
223 | typedef struct _CertificateChain
|
---|
224 | {
|
---|
225 | CERT_CHAIN_CONTEXT context;
|
---|
226 | HCERTSTORE world;
|
---|
227 | LONG ref;
|
---|
228 | } CertificateChain, *PCertificateChain;
|
---|
229 |
|
---|
230 | static inline BOOL CRYPT_IsCertificateSelfSigned(PCCERT_CONTEXT cert)
|
---|
231 | {
|
---|
232 | return CertCompareCertificateName(cert->dwCertEncodingType,
|
---|
233 | &cert->pCertInfo->Subject, &cert->pCertInfo->Issuer);
|
---|
234 | }
|
---|
235 |
|
---|
236 | static void CRYPT_FreeChainElement(PCERT_CHAIN_ELEMENT element)
|
---|
237 | {
|
---|
238 | CertFreeCertificateContext(element->pCertContext);
|
---|
239 | CryptMemFree(element);
|
---|
240 | }
|
---|
241 |
|
---|
242 | static void CRYPT_CheckSimpleChainForCycles(PCERT_SIMPLE_CHAIN chain)
|
---|
243 | {
|
---|
244 | DWORD i, j, cyclicCertIndex = 0;
|
---|
245 |
|
---|
246 | /* O(n^2) - I don't think there's a faster way */
|
---|
247 | for (i = 0; !cyclicCertIndex && i < chain->cElement; i++)
|
---|
248 | for (j = i + 1; !cyclicCertIndex && j < chain->cElement; j++)
|
---|
249 | if (CertCompareCertificate(X509_ASN_ENCODING,
|
---|
250 | chain->rgpElement[i]->pCertContext->pCertInfo,
|
---|
251 | chain->rgpElement[j]->pCertContext->pCertInfo))
|
---|
252 | cyclicCertIndex = j;
|
---|
253 | if (cyclicCertIndex)
|
---|
254 | {
|
---|
255 | chain->rgpElement[cyclicCertIndex]->TrustStatus.dwErrorStatus
|
---|
256 | |= CERT_TRUST_IS_CYCLIC | CERT_TRUST_INVALID_BASIC_CONSTRAINTS;
|
---|
257 | /* Release remaining certs */
|
---|
258 | for (i = cyclicCertIndex + 1; i < chain->cElement; i++)
|
---|
259 | CRYPT_FreeChainElement(chain->rgpElement[i]);
|
---|
260 | /* Truncate chain */
|
---|
261 | chain->cElement = cyclicCertIndex + 1;
|
---|
262 | }
|
---|
263 | }
|
---|
264 |
|
---|
265 | /* Checks whether the chain is cyclic by examining the last element's status */
|
---|
266 | static inline BOOL CRYPT_IsSimpleChainCyclic(PCERT_SIMPLE_CHAIN chain)
|
---|
267 | {
|
---|
268 | if (chain->cElement)
|
---|
269 | return chain->rgpElement[chain->cElement - 1]->TrustStatus.dwErrorStatus
|
---|
270 | & CERT_TRUST_IS_CYCLIC;
|
---|
271 | else
|
---|
272 | return FALSE;
|
---|
273 | }
|
---|
274 |
|
---|
275 | static inline void CRYPT_CombineTrustStatus(CERT_TRUST_STATUS *chainStatus,
|
---|
276 | CERT_TRUST_STATUS *elementStatus)
|
---|
277 | {
|
---|
278 | /* Any error that applies to an element also applies to a chain.. */
|
---|
279 | chainStatus->dwErrorStatus |= elementStatus->dwErrorStatus;
|
---|
280 | /* but the bottom nibble of an element's info status doesn't apply to the
|
---|
281 | * chain.
|
---|
282 | */
|
---|
283 | chainStatus->dwInfoStatus |= (elementStatus->dwInfoStatus & 0xfffffff0);
|
---|
284 | }
|
---|
285 |
|
---|
286 | static BOOL CRYPT_AddCertToSimpleChain(PCertificateChainEngine engine,
|
---|
287 | PCERT_SIMPLE_CHAIN chain, PCCERT_CONTEXT cert, DWORD subjectInfoStatus)
|
---|
288 | {
|
---|
289 | BOOL ret = FALSE;
|
---|
290 | PCERT_CHAIN_ELEMENT element = CryptMemAlloc(sizeof(CERT_CHAIN_ELEMENT));
|
---|
291 |
|
---|
292 | if (element)
|
---|
293 | {
|
---|
294 | if (!chain->cElement)
|
---|
295 | chain->rgpElement = CryptMemAlloc(sizeof(PCERT_CHAIN_ELEMENT));
|
---|
296 | else
|
---|
297 | chain->rgpElement = CryptMemRealloc(chain->rgpElement,
|
---|
298 | (chain->cElement + 1) * sizeof(PCERT_CHAIN_ELEMENT));
|
---|
299 | if (chain->rgpElement)
|
---|
300 | {
|
---|
301 | chain->rgpElement[chain->cElement++] = element;
|
---|
302 | memset(element, 0, sizeof(CERT_CHAIN_ELEMENT));
|
---|
303 | element->cbSize = sizeof(CERT_CHAIN_ELEMENT);
|
---|
304 | element->pCertContext = CertDuplicateCertificateContext(cert);
|
---|
305 | if (chain->cElement > 1)
|
---|
306 | chain->rgpElement[chain->cElement - 2]->TrustStatus.dwInfoStatus
|
---|
307 | = subjectInfoStatus;
|
---|
308 | /* FIXME: initialize the rest of element */
|
---|
309 | if (!(chain->cElement % engine->CycleDetectionModulus))
|
---|
310 | CRYPT_CheckSimpleChainForCycles(chain);
|
---|
311 | CRYPT_CombineTrustStatus(&chain->TrustStatus,
|
---|
312 | &element->TrustStatus);
|
---|
313 | ret = TRUE;
|
---|
314 | }
|
---|
315 | else
|
---|
316 | CryptMemFree(element);
|
---|
317 | }
|
---|
318 | return ret;
|
---|
319 | }
|
---|
320 |
|
---|
321 | static void CRYPT_FreeSimpleChain(PCERT_SIMPLE_CHAIN chain)
|
---|
322 | {
|
---|
323 | DWORD i;
|
---|
324 |
|
---|
325 | for (i = 0; i < chain->cElement; i++)
|
---|
326 | CRYPT_FreeChainElement(chain->rgpElement[i]);
|
---|
327 | CryptMemFree(chain->rgpElement);
|
---|
328 | CryptMemFree(chain);
|
---|
329 | }
|
---|
330 |
|
---|
331 | static void CRYPT_CheckTrustedStatus(HCERTSTORE hRoot,
|
---|
332 | PCERT_CHAIN_ELEMENT rootElement)
|
---|
333 | {
|
---|
334 | BYTE hash[20];
|
---|
335 | DWORD size = sizeof(hash);
|
---|
336 | CRYPT_HASH_BLOB blob = { sizeof(hash), hash };
|
---|
337 | PCCERT_CONTEXT trustedRoot;
|
---|
338 |
|
---|
339 | CertGetCertificateContextProperty(rootElement->pCertContext,
|
---|
340 | CERT_HASH_PROP_ID, hash, &size);
|
---|
341 | trustedRoot = CertFindCertificateInStore(hRoot,
|
---|
342 | rootElement->pCertContext->dwCertEncodingType, 0, CERT_FIND_SHA1_HASH,
|
---|
343 | &blob, NULL);
|
---|
344 | if (!trustedRoot)
|
---|
345 | rootElement->TrustStatus.dwErrorStatus |=
|
---|
346 | CERT_TRUST_IS_UNTRUSTED_ROOT;
|
---|
347 | else
|
---|
348 | CertFreeCertificateContext(trustedRoot);
|
---|
349 | }
|
---|
350 |
|
---|
351 | static void CRYPT_CheckRootCert(HCERTCHAINENGINE hRoot,
|
---|
352 | PCERT_CHAIN_ELEMENT rootElement)
|
---|
353 | {
|
---|
354 | PCCERT_CONTEXT root = rootElement->pCertContext;
|
---|
355 |
|
---|
356 | if (!CryptVerifyCertificateSignatureEx(0, root->dwCertEncodingType,
|
---|
357 | CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT, (void *)root,
|
---|
358 | CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT, (void *)root, 0, NULL))
|
---|
359 | {
|
---|
360 | TRACE("Last certificate's signature is invalid\n");
|
---|
361 | rootElement->TrustStatus.dwErrorStatus |=
|
---|
362 | CERT_TRUST_IS_NOT_SIGNATURE_VALID;
|
---|
363 | }
|
---|
364 | CRYPT_CheckTrustedStatus((HCERTSTORE)hRoot, rootElement);
|
---|
365 | }
|
---|
366 |
|
---|
367 | /* Decodes a cert's basic constraints extension (either szOID_BASIC_CONSTRAINTS
|
---|
368 | * or szOID_BASIC_CONSTRAINTS2, whichever is present) into a
|
---|
369 | * CERT_BASIC_CONSTRAINTS2_INFO. If it neither extension is present, sets
|
---|
370 | * constraints->fCA to defaultIfNotSpecified.
|
---|
371 | * Returns FALSE if the extension is present but couldn't be decoded.
|
---|
372 | */
|
---|
373 | static BOOL CRYPT_DecodeBasicConstraints(PCCERT_CONTEXT cert,
|
---|
374 | CERT_BASIC_CONSTRAINTS2_INFO *constraints, BOOL defaultIfNotSpecified)
|
---|
375 | {
|
---|
376 | BOOL ret = TRUE;
|
---|
377 | PCERT_EXTENSION ext = CertFindExtension(szOID_BASIC_CONSTRAINTS,
|
---|
378 | cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension);
|
---|
379 |
|
---|
380 | constraints->fPathLenConstraint = FALSE;
|
---|
381 | if (ext)
|
---|
382 | {
|
---|
383 | CERT_BASIC_CONSTRAINTS_INFO *info;
|
---|
384 | DWORD size = 0;
|
---|
385 |
|
---|
386 | ret = CryptDecodeObjectEx(X509_ASN_ENCODING, szOID_BASIC_CONSTRAINTS,
|
---|
387 | ext->Value.pbData, ext->Value.cbData, CRYPT_DECODE_ALLOC_FLAG,
|
---|
388 | NULL, (LPBYTE)&info, &size);
|
---|
389 | if (ret)
|
---|
390 | {
|
---|
391 | if (info->SubjectType.cbData == 1)
|
---|
392 | constraints->fCA =
|
---|
393 | info->SubjectType.pbData[0] & CERT_CA_SUBJECT_FLAG;
|
---|
394 | LocalFree((HANDLE)info);
|
---|
395 | }
|
---|
396 | }
|
---|
397 | else
|
---|
398 | {
|
---|
399 | ext = CertFindExtension(szOID_BASIC_CONSTRAINTS2,
|
---|
400 | cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension);
|
---|
401 | if (ext)
|
---|
402 | {
|
---|
403 | DWORD size = sizeof(CERT_BASIC_CONSTRAINTS2_INFO);
|
---|
404 |
|
---|
405 | ret = CryptDecodeObjectEx(X509_ASN_ENCODING,
|
---|
406 | szOID_BASIC_CONSTRAINTS2, ext->Value.pbData, ext->Value.cbData,
|
---|
407 | 0, NULL, constraints, &size);
|
---|
408 | }
|
---|
409 | else
|
---|
410 | constraints->fCA = defaultIfNotSpecified;
|
---|
411 | }
|
---|
412 | return ret;
|
---|
413 | }
|
---|
414 |
|
---|
415 | /* Checks element's basic constraints to see if it can act as a CA, with
|
---|
416 | * remainingCAs CAs left in this chain. Updates chainConstraints with the
|
---|
417 | * element's constraints, if:
|
---|
418 | * 1. chainConstraints doesn't have a path length constraint, or
|
---|
419 | * 2. element's path length constraint is smaller than chainConstraints's
|
---|
420 | * Sets *pathLengthConstraintViolated to TRUE if a path length violation
|
---|
421 | * occurs.
|
---|
422 | * Returns TRUE if the element can be a CA, and the length of the remaining
|
---|
423 | * chain is valid.
|
---|
424 | */
|
---|
425 | static BOOL CRYPT_CheckBasicConstraintsForCA(PCCERT_CONTEXT cert,
|
---|
426 | CERT_BASIC_CONSTRAINTS2_INFO *chainConstraints, DWORD remainingCAs,
|
---|
427 | BOOL *pathLengthConstraintViolated)
|
---|
428 | {
|
---|
429 | BOOL validBasicConstraints;
|
---|
430 | CERT_BASIC_CONSTRAINTS2_INFO constraints;
|
---|
431 |
|
---|
432 | if ((validBasicConstraints = CRYPT_DecodeBasicConstraints(cert,
|
---|
433 | &constraints, TRUE)) != NULL)
|
---|
434 | {
|
---|
435 | if (!constraints.fCA)
|
---|
436 | {
|
---|
437 | TRACE("chain element %d can't be a CA\n", remainingCAs + 1);
|
---|
438 | validBasicConstraints = FALSE;
|
---|
439 | }
|
---|
440 | else if (constraints.fPathLenConstraint)
|
---|
441 | {
|
---|
442 | /* If the element has path length constraints, they apply to the
|
---|
443 | * entire remaining chain.
|
---|
444 | */
|
---|
445 | if (!chainConstraints->fPathLenConstraint ||
|
---|
446 | constraints.dwPathLenConstraint <
|
---|
447 | chainConstraints->dwPathLenConstraint)
|
---|
448 | {
|
---|
449 | TRACE("setting path length constraint to %d\n",
|
---|
450 | chainConstraints->dwPathLenConstraint);
|
---|
451 | chainConstraints->fPathLenConstraint = TRUE;
|
---|
452 | chainConstraints->dwPathLenConstraint =
|
---|
453 | constraints.dwPathLenConstraint;
|
---|
454 | }
|
---|
455 | }
|
---|
456 | }
|
---|
457 | if (chainConstraints->fPathLenConstraint &&
|
---|
458 | remainingCAs > chainConstraints->dwPathLenConstraint)
|
---|
459 | {
|
---|
460 | TRACE("remaining CAs %d exceed max path length %d\n", remainingCAs,
|
---|
461 | chainConstraints->dwPathLenConstraint);
|
---|
462 | validBasicConstraints = FALSE;
|
---|
463 | *pathLengthConstraintViolated = TRUE;
|
---|
464 | }
|
---|
465 | return validBasicConstraints;
|
---|
466 | }
|
---|
467 |
|
---|
468 | static BOOL url_matches(LPCWSTR constraint, LPCWSTR name,
|
---|
469 | DWORD *trustErrorStatus)
|
---|
470 | {
|
---|
471 | BOOL match = FALSE;
|
---|
472 |
|
---|
473 | TRACE("%s, %s\n", debugstr_w(constraint), debugstr_w(name));
|
---|
474 |
|
---|
475 | if (!constraint)
|
---|
476 | *trustErrorStatus |= CERT_TRUST_INVALID_NAME_CONSTRAINTS;
|
---|
477 | else if (!name)
|
---|
478 | ; /* no match */
|
---|
479 | else if (constraint[0] == '.')
|
---|
480 | {
|
---|
481 | if (lstrlenW(name) > lstrlenW(constraint))
|
---|
482 | match = !lstrcmpiW(name + lstrlenW(name) - lstrlenW(constraint),
|
---|
483 | constraint);
|
---|
484 | }
|
---|
485 | else
|
---|
486 | match = !lstrcmpiW(constraint, name);
|
---|
487 | return match;
|
---|
488 | }
|
---|
489 |
|
---|
490 | static BOOL rfc822_name_matches(LPCWSTR constraint, LPCWSTR name,
|
---|
491 | DWORD *trustErrorStatus)
|
---|
492 | {
|
---|
493 | BOOL match = FALSE;
|
---|
494 | LPCWSTR at;
|
---|
495 |
|
---|
496 | TRACE("%s, %s\n", debugstr_w(constraint), debugstr_w(name));
|
---|
497 |
|
---|
498 | if (!constraint)
|
---|
499 | *trustErrorStatus |= CERT_TRUST_INVALID_NAME_CONSTRAINTS;
|
---|
500 | else if (!name)
|
---|
501 | ; /* no match */
|
---|
502 | else if ((at = strchrW(constraint, '@')) != NULL)
|
---|
503 | match = !lstrcmpiW(constraint, name);
|
---|
504 | else
|
---|
505 | {
|
---|
506 | if ((at = strchrW(name, '@')) != NULL)
|
---|
507 | match = url_matches(constraint, at + 1, trustErrorStatus);
|
---|
508 | else
|
---|
509 | match = !lstrcmpiW(constraint, name);
|
---|
510 | }
|
---|
511 | return match;
|
---|
512 | }
|
---|
513 |
|
---|
514 | static BOOL dns_name_matches(LPCWSTR constraint, LPCWSTR name,
|
---|
515 | DWORD *trustErrorStatus)
|
---|
516 | {
|
---|
517 | BOOL match = FALSE;
|
---|
518 |
|
---|
519 | TRACE("%s, %s\n", debugstr_w(constraint), debugstr_w(name));
|
---|
520 |
|
---|
521 | if (!constraint)
|
---|
522 | *trustErrorStatus |= CERT_TRUST_INVALID_NAME_CONSTRAINTS;
|
---|
523 | else if (!name)
|
---|
524 | ; /* no match */
|
---|
525 | else if (lstrlenW(name) >= lstrlenW(constraint))
|
---|
526 | match = !lstrcmpiW(name + lstrlenW(name) - lstrlenW(constraint),
|
---|
527 | constraint);
|
---|
528 | /* else: name is too short, no match */
|
---|
529 |
|
---|
530 | return match;
|
---|
531 | }
|
---|
532 |
|
---|
533 | static BOOL ip_address_matches(const CRYPT_DATA_BLOB *constraint,
|
---|
534 | const CRYPT_DATA_BLOB *name, DWORD *trustErrorStatus)
|
---|
535 | {
|
---|
536 | BOOL match = FALSE;
|
---|
537 |
|
---|
538 | TRACE("(%d, %p), (%d, %p)\n", constraint->cbData, constraint->pbData,
|
---|
539 | name->cbData, name->pbData);
|
---|
540 |
|
---|
541 | if (constraint->cbData != sizeof(DWORD) * 2)
|
---|
542 | *trustErrorStatus |= CERT_TRUST_INVALID_NAME_CONSTRAINTS;
|
---|
543 | else if (name->cbData == sizeof(DWORD))
|
---|
544 | {
|
---|
545 | DWORD subnet, mask, addr;
|
---|
546 |
|
---|
547 | memcpy(&subnet, constraint->pbData, sizeof(subnet));
|
---|
548 | memcpy(&mask, constraint->pbData + sizeof(subnet), sizeof(mask));
|
---|
549 | memcpy(&addr, name->pbData, sizeof(addr));
|
---|
550 | /* These are really in big-endian order, but for equality matching we
|
---|
551 | * don't need to swap to host order
|
---|
552 | */
|
---|
553 | match = (subnet & mask) == (addr & mask);
|
---|
554 | }
|
---|
555 | /* else: name is wrong size, no match */
|
---|
556 |
|
---|
557 | return match;
|
---|
558 | }
|
---|
559 |
|
---|
560 | static void CRYPT_FindMatchingNameEntry(const CERT_ALT_NAME_ENTRY *constraint,
|
---|
561 | const CERT_ALT_NAME_INFO *subjectName, DWORD *trustErrorStatus,
|
---|
562 | DWORD errorIfFound, DWORD errorIfNotFound)
|
---|
563 | {
|
---|
564 | DWORD i;
|
---|
565 | BOOL match = FALSE;
|
---|
566 |
|
---|
567 | for (i = 0; i < subjectName->cAltEntry; i++)
|
---|
568 | {
|
---|
569 | if (subjectName->rgAltEntry[i].dwAltNameChoice ==
|
---|
570 | constraint->dwAltNameChoice)
|
---|
571 | {
|
---|
572 | switch (constraint->dwAltNameChoice)
|
---|
573 | {
|
---|
574 | case CERT_ALT_NAME_RFC822_NAME:
|
---|
575 | match = rfc822_name_matches(constraint->u.pwszURL,
|
---|
576 | subjectName->rgAltEntry[i].u.pwszURL, trustErrorStatus);
|
---|
577 | break;
|
---|
578 | case CERT_ALT_NAME_DNS_NAME:
|
---|
579 | match = dns_name_matches(constraint->u.pwszURL,
|
---|
580 | subjectName->rgAltEntry[i].u.pwszURL, trustErrorStatus);
|
---|
581 | break;
|
---|
582 | case CERT_ALT_NAME_URL:
|
---|
583 | match = url_matches(constraint->u.pwszURL,
|
---|
584 | subjectName->rgAltEntry[i].u.pwszURL, trustErrorStatus);
|
---|
585 | break;
|
---|
586 | case CERT_ALT_NAME_IP_ADDRESS:
|
---|
587 | match = ip_address_matches(&constraint->u.IPAddress,
|
---|
588 | &subjectName->rgAltEntry[i].u.IPAddress, trustErrorStatus);
|
---|
589 | break;
|
---|
590 | case CERT_ALT_NAME_DIRECTORY_NAME:
|
---|
591 | default:
|
---|
592 | ERR("name choice %d unsupported in this context\n",
|
---|
593 | constraint->dwAltNameChoice);
|
---|
594 | *trustErrorStatus |=
|
---|
595 | CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT;
|
---|
596 | }
|
---|
597 | }
|
---|
598 | }
|
---|
599 | *trustErrorStatus |= match ? errorIfFound : errorIfNotFound;
|
---|
600 | }
|
---|
601 |
|
---|
602 | static void CRYPT_CheckNameConstraints(
|
---|
603 | const CERT_NAME_CONSTRAINTS_INFO *nameConstraints, const CERT_INFO *cert,
|
---|
604 | DWORD *trustErrorStatus)
|
---|
605 | {
|
---|
606 | /* If there aren't any existing constraints, don't bother checking */
|
---|
607 | if (nameConstraints->cPermittedSubtree || nameConstraints->cExcludedSubtree)
|
---|
608 | {
|
---|
609 | CERT_EXTENSION *ext;
|
---|
610 |
|
---|
611 | if ((ext = CertFindExtension(szOID_SUBJECT_ALT_NAME, cert->cExtension,
|
---|
612 | cert->rgExtension)) != NULL)
|
---|
613 | {
|
---|
614 | CERT_ALT_NAME_INFO *subjectName;
|
---|
615 | DWORD size;
|
---|
616 |
|
---|
617 | if (CryptDecodeObjectEx(X509_ASN_ENCODING, X509_ALTERNATE_NAME,
|
---|
618 | ext->Value.pbData, ext->Value.cbData,
|
---|
619 | CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL,
|
---|
620 | &subjectName, &size))
|
---|
621 | {
|
---|
622 | DWORD i;
|
---|
623 |
|
---|
624 | for (i = 0; i < nameConstraints->cExcludedSubtree; i++)
|
---|
625 | CRYPT_FindMatchingNameEntry(
|
---|
626 | &nameConstraints->rgExcludedSubtree[i].Base, subjectName,
|
---|
627 | trustErrorStatus,
|
---|
628 | CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT, 0);
|
---|
629 | for (i = 0; i < nameConstraints->cPermittedSubtree; i++)
|
---|
630 | CRYPT_FindMatchingNameEntry(
|
---|
631 | &nameConstraints->rgPermittedSubtree[i].Base, subjectName,
|
---|
632 | trustErrorStatus,
|
---|
633 | 0, CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT);
|
---|
634 | LocalFree((HANDLE)subjectName);
|
---|
635 | }
|
---|
636 | }
|
---|
637 | else
|
---|
638 | {
|
---|
639 | if (nameConstraints->cPermittedSubtree)
|
---|
640 | *trustErrorStatus |=
|
---|
641 | CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT;
|
---|
642 | if (nameConstraints->cExcludedSubtree)
|
---|
643 | *trustErrorStatus |=
|
---|
644 | CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT;
|
---|
645 | }
|
---|
646 | }
|
---|
647 | }
|
---|
648 |
|
---|
649 | /* Gets cert's name constraints, if any. Free with LocalFree. */
|
---|
650 | static CERT_NAME_CONSTRAINTS_INFO *CRYPT_GetNameConstraints(CERT_INFO *cert)
|
---|
651 | {
|
---|
652 | CERT_NAME_CONSTRAINTS_INFO *info = NULL;
|
---|
653 |
|
---|
654 | CERT_EXTENSION *ext;
|
---|
655 |
|
---|
656 | if ((ext = CertFindExtension(szOID_NAME_CONSTRAINTS, cert->cExtension,
|
---|
657 | cert->rgExtension)) != NULL)
|
---|
658 | {
|
---|
659 | DWORD size;
|
---|
660 |
|
---|
661 | CryptDecodeObjectEx(X509_ASN_ENCODING, X509_NAME_CONSTRAINTS,
|
---|
662 | ext->Value.pbData, ext->Value.cbData,
|
---|
663 | CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL, &info,
|
---|
664 | &size);
|
---|
665 | }
|
---|
666 | return info;
|
---|
667 | }
|
---|
668 |
|
---|
669 | static void CRYPT_CheckChainNameConstraints(PCERT_SIMPLE_CHAIN chain)
|
---|
670 | {
|
---|
671 | int i, j;
|
---|
672 |
|
---|
673 | /* Microsoft's implementation appears to violate RFC 3280: according to
|
---|
674 | * MSDN, the various CERT_TRUST_*_NAME_CONSTRAINT errors are set if a CA's
|
---|
675 | * name constraint is violated in the end cert. According to RFC 3280,
|
---|
676 | * the constraints should be checked against every subsequent certificate
|
---|
677 | * in the chain, not just the end cert.
|
---|
678 | * Microsoft's implementation also sets the name constraint errors on the
|
---|
679 | * certs whose constraints were violated, not on the certs that violated
|
---|
680 | * them.
|
---|
681 | * In order to be error-compatible with Microsoft's implementation, while
|
---|
682 | * still adhering to RFC 3280, I use a O(n ^ 2) algorithm to check name
|
---|
683 | * constraints.
|
---|
684 | */
|
---|
685 | for (i = chain->cElement - 1; i > 0; i--)
|
---|
686 | {
|
---|
687 | CERT_NAME_CONSTRAINTS_INFO *nameConstraints;
|
---|
688 |
|
---|
689 | if ((nameConstraints = CRYPT_GetNameConstraints(
|
---|
690 | chain->rgpElement[i]->pCertContext->pCertInfo)) != NULL)
|
---|
691 | {
|
---|
692 | for (j = i - 1; j >= 0; j--)
|
---|
693 | {
|
---|
694 | DWORD errorStatus = 0;
|
---|
695 |
|
---|
696 | /* According to RFC 3280, self-signed certs don't have name
|
---|
697 | * constraints checked unless they're the end cert.
|
---|
698 | */
|
---|
699 | if (j == 0 || !CRYPT_IsCertificateSelfSigned(
|
---|
700 | chain->rgpElement[j]->pCertContext))
|
---|
701 | {
|
---|
702 | CRYPT_CheckNameConstraints(nameConstraints,
|
---|
703 | chain->rgpElement[i]->pCertContext->pCertInfo,
|
---|
704 | &errorStatus);
|
---|
705 | chain->rgpElement[i]->TrustStatus.dwErrorStatus |=
|
---|
706 | errorStatus;
|
---|
707 | }
|
---|
708 | }
|
---|
709 | LocalFree((HANDLE)nameConstraints);
|
---|
710 | }
|
---|
711 | }
|
---|
712 | }
|
---|
713 |
|
---|
714 | static void CRYPT_CheckSimpleChain(PCertificateChainEngine engine,
|
---|
715 | PCERT_SIMPLE_CHAIN chain, LPFILETIME time)
|
---|
716 | {
|
---|
717 | PCERT_CHAIN_ELEMENT rootElement = chain->rgpElement[chain->cElement - 1];
|
---|
718 | int i;
|
---|
719 | BOOL pathLengthConstraintViolated = FALSE;
|
---|
720 | CERT_BASIC_CONSTRAINTS2_INFO constraints = { TRUE, FALSE, 0 };
|
---|
721 |
|
---|
722 | for (i = chain->cElement - 1; i >= 0; i--)
|
---|
723 | {
|
---|
724 | if (CertVerifyTimeValidity(time,
|
---|
725 | chain->rgpElement[i]->pCertContext->pCertInfo) != 0)
|
---|
726 | chain->rgpElement[i]->TrustStatus.dwErrorStatus |=
|
---|
727 | CERT_TRUST_IS_NOT_TIME_VALID;
|
---|
728 | if (i != 0)
|
---|
729 | {
|
---|
730 | /* Check the signature of the cert this issued */
|
---|
731 | if (!CryptVerifyCertificateSignatureEx(0, X509_ASN_ENCODING,
|
---|
732 | CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT,
|
---|
733 | (void *)chain->rgpElement[i - 1]->pCertContext,
|
---|
734 | CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT,
|
---|
735 | (void *)chain->rgpElement[i]->pCertContext, 0, NULL))
|
---|
736 | chain->rgpElement[i - 1]->TrustStatus.dwErrorStatus |=
|
---|
737 | CERT_TRUST_IS_NOT_SIGNATURE_VALID;
|
---|
738 | /* Once a path length constraint has been violated, every remaining
|
---|
739 | * CA cert's basic constraints is considered invalid.
|
---|
740 | */
|
---|
741 | if (pathLengthConstraintViolated)
|
---|
742 | chain->rgpElement[i]->TrustStatus.dwErrorStatus |=
|
---|
743 | CERT_TRUST_INVALID_BASIC_CONSTRAINTS;
|
---|
744 | else if (!CRYPT_CheckBasicConstraintsForCA(
|
---|
745 | chain->rgpElement[i]->pCertContext, &constraints, i - 1,
|
---|
746 | &pathLengthConstraintViolated))
|
---|
747 | chain->rgpElement[i]->TrustStatus.dwErrorStatus |=
|
---|
748 | CERT_TRUST_INVALID_BASIC_CONSTRAINTS;
|
---|
749 | else if (constraints.fPathLenConstraint &&
|
---|
750 | constraints.dwPathLenConstraint)
|
---|
751 | {
|
---|
752 | /* This one's valid - decrement max length */
|
---|
753 | constraints.dwPathLenConstraint--;
|
---|
754 | }
|
---|
755 | }
|
---|
756 | if (CRYPT_IsSimpleChainCyclic(chain))
|
---|
757 | {
|
---|
758 | /* If the chain is cyclic, then the path length constraints
|
---|
759 | * are violated, because the chain is infinitely long.
|
---|
760 | */
|
---|
761 | pathLengthConstraintViolated = TRUE;
|
---|
762 | chain->TrustStatus.dwErrorStatus |=
|
---|
763 | CERT_TRUST_IS_PARTIAL_CHAIN |
|
---|
764 | CERT_TRUST_INVALID_BASIC_CONSTRAINTS;
|
---|
765 | }
|
---|
766 | /* FIXME: check valid usages */
|
---|
767 | CRYPT_CombineTrustStatus(&chain->TrustStatus,
|
---|
768 | &chain->rgpElement[i]->TrustStatus);
|
---|
769 | }
|
---|
770 | CRYPT_CheckChainNameConstraints(chain);
|
---|
771 | if (CRYPT_IsCertificateSelfSigned(rootElement->pCertContext))
|
---|
772 | {
|
---|
773 | rootElement->TrustStatus.dwInfoStatus |=
|
---|
774 | CERT_TRUST_IS_SELF_SIGNED | CERT_TRUST_HAS_NAME_MATCH_ISSUER;
|
---|
775 | CRYPT_CheckRootCert((HCERTCHAINENGINE)engine->hRoot, rootElement);
|
---|
776 | }
|
---|
777 | CRYPT_CombineTrustStatus(&chain->TrustStatus, &rootElement->TrustStatus);
|
---|
778 | }
|
---|
779 |
|
---|
780 | static PCCERT_CONTEXT CRYPT_GetIssuer(HCERTSTORE store, PCCERT_CONTEXT subject,
|
---|
781 | PCCERT_CONTEXT prevIssuer, DWORD *infoStatus)
|
---|
782 | {
|
---|
783 | PCCERT_CONTEXT issuer = NULL;
|
---|
784 | PCERT_EXTENSION ext;
|
---|
785 | DWORD size;
|
---|
786 |
|
---|
787 | *infoStatus = 0;
|
---|
788 | if ((ext = CertFindExtension(szOID_AUTHORITY_KEY_IDENTIFIER,
|
---|
789 | subject->pCertInfo->cExtension, subject->pCertInfo->rgExtension)) != NULL)
|
---|
790 | {
|
---|
791 | CERT_AUTHORITY_KEY_ID_INFO *info;
|
---|
792 | BOOL ret;
|
---|
793 |
|
---|
794 | ret = CryptDecodeObjectEx(subject->dwCertEncodingType,
|
---|
795 | X509_AUTHORITY_KEY_ID, ext->Value.pbData, ext->Value.cbData,
|
---|
796 | CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL,
|
---|
797 | &info, &size);
|
---|
798 | if (ret)
|
---|
799 | {
|
---|
800 | CERT_ID id;
|
---|
801 |
|
---|
802 | if (info->CertIssuer.cbData && info->CertSerialNumber.cbData)
|
---|
803 | {
|
---|
804 | id.dwIdChoice = CERT_ID_ISSUER_SERIAL_NUMBER;
|
---|
805 | memcpy(&id.u.IssuerSerialNumber.Issuer, &info->CertIssuer,
|
---|
806 | sizeof(CERT_NAME_BLOB));
|
---|
807 | memcpy(&id.u.IssuerSerialNumber.SerialNumber,
|
---|
808 | &info->CertSerialNumber, sizeof(CRYPT_INTEGER_BLOB));
|
---|
809 | issuer = CertFindCertificateInStore(store,
|
---|
810 | subject->dwCertEncodingType, 0, CERT_FIND_CERT_ID, &id,
|
---|
811 | prevIssuer);
|
---|
812 | if (issuer)
|
---|
813 | *infoStatus = CERT_TRUST_HAS_EXACT_MATCH_ISSUER;
|
---|
814 | }
|
---|
815 | else if (info->KeyId.cbData)
|
---|
816 | {
|
---|
817 | id.dwIdChoice = CERT_ID_KEY_IDENTIFIER;
|
---|
818 | memcpy(&id.u.KeyId, &info->KeyId, sizeof(CRYPT_HASH_BLOB));
|
---|
819 | issuer = CertFindCertificateInStore(store,
|
---|
820 | subject->dwCertEncodingType, 0, CERT_FIND_CERT_ID, &id,
|
---|
821 | prevIssuer);
|
---|
822 | if (issuer)
|
---|
823 | *infoStatus = CERT_TRUST_HAS_KEY_MATCH_ISSUER;
|
---|
824 | }
|
---|
825 | LocalFree((HANDLE)info);
|
---|
826 | }
|
---|
827 | }
|
---|
828 | else if ((ext = CertFindExtension(szOID_AUTHORITY_KEY_IDENTIFIER2,
|
---|
829 | subject->pCertInfo->cExtension, subject->pCertInfo->rgExtension)) != NULL)
|
---|
830 | {
|
---|
831 | CERT_AUTHORITY_KEY_ID2_INFO *info;
|
---|
832 | BOOL ret;
|
---|
833 |
|
---|
834 | ret = CryptDecodeObjectEx(subject->dwCertEncodingType,
|
---|
835 | X509_AUTHORITY_KEY_ID2, ext->Value.pbData, ext->Value.cbData,
|
---|
836 | CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL,
|
---|
837 | &info, &size);
|
---|
838 | if (ret)
|
---|
839 | {
|
---|
840 | CERT_ID id;
|
---|
841 |
|
---|
842 | if (info->AuthorityCertIssuer.cAltEntry &&
|
---|
843 | info->AuthorityCertSerialNumber.cbData)
|
---|
844 | {
|
---|
845 | PCERT_ALT_NAME_ENTRY directoryName = NULL;
|
---|
846 | DWORD i;
|
---|
847 |
|
---|
848 | for (i = 0; !directoryName &&
|
---|
849 | i < info->AuthorityCertIssuer.cAltEntry; i++)
|
---|
850 | if (info->AuthorityCertIssuer.rgAltEntry[i].dwAltNameChoice
|
---|
851 | == CERT_ALT_NAME_DIRECTORY_NAME)
|
---|
852 | directoryName =
|
---|
853 | &info->AuthorityCertIssuer.rgAltEntry[i];
|
---|
854 | if (directoryName)
|
---|
855 | {
|
---|
856 | id.dwIdChoice = CERT_ID_ISSUER_SERIAL_NUMBER;
|
---|
857 | memcpy(&id.u.IssuerSerialNumber.Issuer,
|
---|
858 | &directoryName->u.DirectoryName, sizeof(CERT_NAME_BLOB));
|
---|
859 | memcpy(&id.u.IssuerSerialNumber.SerialNumber,
|
---|
860 | &info->AuthorityCertSerialNumber,
|
---|
861 | sizeof(CRYPT_INTEGER_BLOB));
|
---|
862 | issuer = CertFindCertificateInStore(store,
|
---|
863 | subject->dwCertEncodingType, 0, CERT_FIND_CERT_ID, &id,
|
---|
864 | prevIssuer);
|
---|
865 | if (issuer)
|
---|
866 | *infoStatus = CERT_TRUST_HAS_EXACT_MATCH_ISSUER;
|
---|
867 | }
|
---|
868 | else
|
---|
869 | FIXME("no supported name type in authority key id2\n");
|
---|
870 | }
|
---|
871 | else if (info->KeyId.cbData)
|
---|
872 | {
|
---|
873 | id.dwIdChoice = CERT_ID_KEY_IDENTIFIER;
|
---|
874 | memcpy(&id.u.KeyId, &info->KeyId, sizeof(CRYPT_HASH_BLOB));
|
---|
875 | issuer = CertFindCertificateInStore(store,
|
---|
876 | subject->dwCertEncodingType, 0, CERT_FIND_CERT_ID, &id,
|
---|
877 | prevIssuer);
|
---|
878 | if (issuer)
|
---|
879 | *infoStatus = CERT_TRUST_HAS_KEY_MATCH_ISSUER;
|
---|
880 | }
|
---|
881 | LocalFree((HANDLE)info);
|
---|
882 | }
|
---|
883 | }
|
---|
884 | else
|
---|
885 | {
|
---|
886 | issuer = CertFindCertificateInStore(store,
|
---|
887 | subject->dwCertEncodingType, 0, CERT_FIND_SUBJECT_NAME,
|
---|
888 | &subject->pCertInfo->Issuer, prevIssuer);
|
---|
889 | if (issuer)
|
---|
890 | *infoStatus = CERT_TRUST_HAS_NAME_MATCH_ISSUER;
|
---|
891 | }
|
---|
892 | return issuer;
|
---|
893 | }
|
---|
894 |
|
---|
895 | /* Builds a simple chain by finding an issuer for the last cert in the chain,
|
---|
896 | * until reaching a self-signed cert, or until no issuer can be found.
|
---|
897 | */
|
---|
898 | static BOOL CRYPT_BuildSimpleChain(PCertificateChainEngine engine,
|
---|
899 | HCERTSTORE world, PCERT_SIMPLE_CHAIN chain)
|
---|
900 | {
|
---|
901 | BOOL ret = TRUE;
|
---|
902 | PCCERT_CONTEXT cert = chain->rgpElement[chain->cElement - 1]->pCertContext;
|
---|
903 |
|
---|
904 | while (ret && !CRYPT_IsSimpleChainCyclic(chain) &&
|
---|
905 | !CRYPT_IsCertificateSelfSigned(cert))
|
---|
906 | {
|
---|
907 | DWORD infoStatus;
|
---|
908 | PCCERT_CONTEXT issuer = CRYPT_GetIssuer(world, cert, NULL, &infoStatus);
|
---|
909 |
|
---|
910 | if (issuer)
|
---|
911 | {
|
---|
912 | ret = CRYPT_AddCertToSimpleChain(engine, chain, issuer, infoStatus);
|
---|
913 | /* CRYPT_AddCertToSimpleChain add-ref's the issuer, so free it to
|
---|
914 | * close the enumeration that found it
|
---|
915 | */
|
---|
916 | CertFreeCertificateContext(issuer);
|
---|
917 | cert = issuer;
|
---|
918 | }
|
---|
919 | else
|
---|
920 | {
|
---|
921 | TRACE("Couldn't find issuer, halting chain creation\n");
|
---|
922 | chain->TrustStatus.dwErrorStatus |= CERT_TRUST_IS_PARTIAL_CHAIN;
|
---|
923 | break;
|
---|
924 | }
|
---|
925 | }
|
---|
926 | return ret;
|
---|
927 | }
|
---|
928 |
|
---|
929 | static BOOL CRYPT_GetSimpleChainForCert(PCertificateChainEngine engine,
|
---|
930 | HCERTSTORE world, PCCERT_CONTEXT cert, LPFILETIME pTime,
|
---|
931 | PCERT_SIMPLE_CHAIN *ppChain)
|
---|
932 | {
|
---|
933 | BOOL ret = FALSE;
|
---|
934 | PCERT_SIMPLE_CHAIN chain;
|
---|
935 |
|
---|
936 | TRACE("(%p, %p, %p, %p)\n", engine, world, cert, pTime);
|
---|
937 |
|
---|
938 | chain = CryptMemAlloc(sizeof(CERT_SIMPLE_CHAIN));
|
---|
939 | if (chain)
|
---|
940 | {
|
---|
941 | memset(chain, 0, sizeof(CERT_SIMPLE_CHAIN));
|
---|
942 | chain->cbSize = sizeof(CERT_SIMPLE_CHAIN);
|
---|
943 | ret = CRYPT_AddCertToSimpleChain(engine, chain, cert, 0);
|
---|
944 | if (ret)
|
---|
945 | {
|
---|
946 | ret = CRYPT_BuildSimpleChain(engine, world, chain);
|
---|
947 | if (ret)
|
---|
948 | CRYPT_CheckSimpleChain(engine, chain, pTime);
|
---|
949 | }
|
---|
950 | if (!ret)
|
---|
951 | {
|
---|
952 | CRYPT_FreeSimpleChain(chain);
|
---|
953 | chain = NULL;
|
---|
954 | }
|
---|
955 | *ppChain = chain;
|
---|
956 | }
|
---|
957 | return ret;
|
---|
958 | }
|
---|
959 |
|
---|
960 | static BOOL CRYPT_BuildCandidateChainFromCert(HCERTCHAINENGINE hChainEngine,
|
---|
961 | PCCERT_CONTEXT cert, LPFILETIME pTime, HCERTSTORE hAdditionalStore,
|
---|
962 | PCertificateChain *ppChain)
|
---|
963 | {
|
---|
964 | PCertificateChainEngine engine = (PCertificateChainEngine)hChainEngine;
|
---|
965 | PCERT_SIMPLE_CHAIN simpleChain = NULL;
|
---|
966 | HCERTSTORE world;
|
---|
967 | BOOL ret;
|
---|
968 |
|
---|
969 | world = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, 0,
|
---|
970 | CERT_STORE_CREATE_NEW_FLAG, NULL);
|
---|
971 | CertAddStoreToCollection(world, engine->hWorld, 0, 0);
|
---|
972 | if (hAdditionalStore)
|
---|
973 | CertAddStoreToCollection(world, hAdditionalStore, 0, 0);
|
---|
974 | /* FIXME: only simple chains are supported for now, as CTLs aren't
|
---|
975 | * supported yet.
|
---|
976 | */
|
---|
977 | if ((ret = CRYPT_GetSimpleChainForCert(engine, world, cert, pTime,
|
---|
978 | &simpleChain)) != NULL)
|
---|
979 | {
|
---|
980 | PCertificateChain chain = CryptMemAlloc(sizeof(CertificateChain));
|
---|
981 |
|
---|
982 | if (chain)
|
---|
983 | {
|
---|
984 | chain->ref = 1;
|
---|
985 | chain->world = world;
|
---|
986 | chain->context.cbSize = sizeof(CERT_CHAIN_CONTEXT);
|
---|
987 | chain->context.TrustStatus = simpleChain->TrustStatus;
|
---|
988 | chain->context.cChain = 1;
|
---|
989 | chain->context.rgpChain = CryptMemAlloc(sizeof(PCERT_SIMPLE_CHAIN));
|
---|
990 | chain->context.rgpChain[0] = simpleChain;
|
---|
991 | chain->context.cLowerQualityChainContext = 0;
|
---|
992 | chain->context.rgpLowerQualityChainContext = NULL;
|
---|
993 | chain->context.fHasRevocationFreshnessTime = FALSE;
|
---|
994 | chain->context.dwRevocationFreshnessTime = 0;
|
---|
995 | }
|
---|
996 | else
|
---|
997 | ret = FALSE;
|
---|
998 | *ppChain = chain;
|
---|
999 | }
|
---|
1000 | return ret;
|
---|
1001 | }
|
---|
1002 |
|
---|
1003 | /* Makes and returns a copy of chain, up to and including element iElement. */
|
---|
1004 | static PCERT_SIMPLE_CHAIN CRYPT_CopySimpleChainToElement(
|
---|
1005 | PCERT_SIMPLE_CHAIN chain, DWORD iElement)
|
---|
1006 | {
|
---|
1007 | PCERT_SIMPLE_CHAIN copy = CryptMemAlloc(sizeof(CERT_SIMPLE_CHAIN));
|
---|
1008 |
|
---|
1009 | if (copy)
|
---|
1010 | {
|
---|
1011 | memset(copy, 0, sizeof(CERT_SIMPLE_CHAIN));
|
---|
1012 | copy->cbSize = sizeof(CERT_SIMPLE_CHAIN);
|
---|
1013 | copy->rgpElement =
|
---|
1014 | CryptMemAlloc((iElement + 1) * sizeof(PCERT_CHAIN_ELEMENT));
|
---|
1015 | if (copy->rgpElement)
|
---|
1016 | {
|
---|
1017 | DWORD i;
|
---|
1018 | BOOL ret = TRUE;
|
---|
1019 |
|
---|
1020 | memset(copy->rgpElement, 0,
|
---|
1021 | (iElement + 1) * sizeof(PCERT_CHAIN_ELEMENT));
|
---|
1022 | for (i = 0; ret && i <= iElement; i++)
|
---|
1023 | {
|
---|
1024 | PCERT_CHAIN_ELEMENT element =
|
---|
1025 | CryptMemAlloc(sizeof(CERT_CHAIN_ELEMENT));
|
---|
1026 |
|
---|
1027 | if (element)
|
---|
1028 | {
|
---|
1029 | *element = *chain->rgpElement[i];
|
---|
1030 | element->pCertContext = CertDuplicateCertificateContext(
|
---|
1031 | chain->rgpElement[i]->pCertContext);
|
---|
1032 | /* Reset the trust status of the copied element, it'll get
|
---|
1033 | * rechecked after the new chain is done.
|
---|
1034 | */
|
---|
1035 | memset(&element->TrustStatus, 0, sizeof(CERT_TRUST_STATUS));
|
---|
1036 | copy->rgpElement[copy->cElement++] = element;
|
---|
1037 | }
|
---|
1038 | else
|
---|
1039 | ret = FALSE;
|
---|
1040 | }
|
---|
1041 | if (!ret)
|
---|
1042 | {
|
---|
1043 | for (i = 0; i <= iElement; i++)
|
---|
1044 | CryptMemFree(copy->rgpElement[i]);
|
---|
1045 | CryptMemFree(copy->rgpElement);
|
---|
1046 | CryptMemFree(copy);
|
---|
1047 | copy = NULL;
|
---|
1048 | }
|
---|
1049 | }
|
---|
1050 | else
|
---|
1051 | {
|
---|
1052 | CryptMemFree(copy);
|
---|
1053 | copy = NULL;
|
---|
1054 | }
|
---|
1055 | }
|
---|
1056 | return copy;
|
---|
1057 | }
|
---|
1058 |
|
---|
1059 | static void CRYPT_FreeLowerQualityChains(PCertificateChain chain)
|
---|
1060 | {
|
---|
1061 | DWORD i;
|
---|
1062 |
|
---|
1063 | for (i = 0; i < chain->context.cLowerQualityChainContext; i++)
|
---|
1064 | CertFreeCertificateChain(chain->context.rgpLowerQualityChainContext[i]);
|
---|
1065 | CryptMemFree(chain->context.rgpLowerQualityChainContext);
|
---|
1066 | chain->context.cLowerQualityChainContext = 0;
|
---|
1067 | chain->context.rgpLowerQualityChainContext = NULL;
|
---|
1068 | }
|
---|
1069 |
|
---|
1070 | static void CRYPT_FreeChainContext(PCertificateChain chain)
|
---|
1071 | {
|
---|
1072 | DWORD i;
|
---|
1073 |
|
---|
1074 | CRYPT_FreeLowerQualityChains(chain);
|
---|
1075 | for (i = 0; i < chain->context.cChain; i++)
|
---|
1076 | CRYPT_FreeSimpleChain(chain->context.rgpChain[i]);
|
---|
1077 | CryptMemFree(chain->context.rgpChain);
|
---|
1078 | CertCloseStore(chain->world, 0);
|
---|
1079 | CryptMemFree(chain);
|
---|
1080 | }
|
---|
1081 |
|
---|
1082 | /* Makes and returns a copy of chain, up to and including element iElement of
|
---|
1083 | * simple chain iChain.
|
---|
1084 | */
|
---|
1085 | static PCertificateChain CRYPT_CopyChainToElement(PCertificateChain chain,
|
---|
1086 | DWORD iChain, DWORD iElement)
|
---|
1087 | {
|
---|
1088 | PCertificateChain copy = CryptMemAlloc(sizeof(CertificateChain));
|
---|
1089 |
|
---|
1090 | if (copy)
|
---|
1091 | {
|
---|
1092 | copy->ref = 1;
|
---|
1093 | copy->world = CertDuplicateStore(chain->world);
|
---|
1094 | copy->context.cbSize = sizeof(CERT_CHAIN_CONTEXT);
|
---|
1095 | /* Leave the trust status of the copied chain unset, it'll get
|
---|
1096 | * rechecked after the new chain is done.
|
---|
1097 | */
|
---|
1098 | memset(©->context.TrustStatus, 0, sizeof(CERT_TRUST_STATUS));
|
---|
1099 | copy->context.cLowerQualityChainContext = 0;
|
---|
1100 | copy->context.rgpLowerQualityChainContext = NULL;
|
---|
1101 | copy->context.fHasRevocationFreshnessTime = FALSE;
|
---|
1102 | copy->context.dwRevocationFreshnessTime = 0;
|
---|
1103 | copy->context.rgpChain = CryptMemAlloc(
|
---|
1104 | (iChain + 1) * sizeof(PCERT_SIMPLE_CHAIN));
|
---|
1105 | if (copy->context.rgpChain)
|
---|
1106 | {
|
---|
1107 | BOOL ret = TRUE;
|
---|
1108 | DWORD i;
|
---|
1109 |
|
---|
1110 | memset(copy->context.rgpChain, 0,
|
---|
1111 | (iChain + 1) * sizeof(PCERT_SIMPLE_CHAIN));
|
---|
1112 | if (iChain)
|
---|
1113 | {
|
---|
1114 | for (i = 0; ret && iChain && i < iChain - 1; i++)
|
---|
1115 | {
|
---|
1116 | copy->context.rgpChain[i] =
|
---|
1117 | CRYPT_CopySimpleChainToElement(chain->context.rgpChain[i],
|
---|
1118 | chain->context.rgpChain[i]->cElement - 1);
|
---|
1119 | if (!copy->context.rgpChain[i])
|
---|
1120 | ret = FALSE;
|
---|
1121 | }
|
---|
1122 | }
|
---|
1123 | else
|
---|
1124 | i = 0;
|
---|
1125 | if (ret)
|
---|
1126 | {
|
---|
1127 | copy->context.rgpChain[i] =
|
---|
1128 | CRYPT_CopySimpleChainToElement(chain->context.rgpChain[i],
|
---|
1129 | iElement);
|
---|
1130 | if (!copy->context.rgpChain[i])
|
---|
1131 | ret = FALSE;
|
---|
1132 | }
|
---|
1133 | if (!ret)
|
---|
1134 | {
|
---|
1135 | CRYPT_FreeChainContext(copy);
|
---|
1136 | copy = NULL;
|
---|
1137 | }
|
---|
1138 | else
|
---|
1139 | copy->context.cChain = iChain + 1;
|
---|
1140 | }
|
---|
1141 | else
|
---|
1142 | {
|
---|
1143 | CryptMemFree(copy);
|
---|
1144 | copy = NULL;
|
---|
1145 | }
|
---|
1146 | }
|
---|
1147 | return copy;
|
---|
1148 | }
|
---|
1149 |
|
---|
1150 | static PCertificateChain CRYPT_BuildAlternateContextFromChain(
|
---|
1151 | HCERTCHAINENGINE hChainEngine, LPFILETIME pTime, HCERTSTORE hAdditionalStore,
|
---|
1152 | PCertificateChain chain)
|
---|
1153 | {
|
---|
1154 | PCertificateChainEngine engine = (PCertificateChainEngine)hChainEngine;
|
---|
1155 | PCertificateChain alternate;
|
---|
1156 |
|
---|
1157 | TRACE("(%p, %p, %p, %p)\n", hChainEngine, pTime, hAdditionalStore, chain);
|
---|
1158 |
|
---|
1159 | /* Always start with the last "lower quality" chain to ensure a consistent
|
---|
1160 | * order of alternate creation:
|
---|
1161 | */
|
---|
1162 | if (chain->context.cLowerQualityChainContext)
|
---|
1163 | chain = (PCertificateChain)chain->context.rgpLowerQualityChainContext[
|
---|
1164 | chain->context.cLowerQualityChainContext - 1];
|
---|
1165 | /* A chain with only one element can't have any alternates */
|
---|
1166 | if (chain->context.cChain <= 1 && chain->context.rgpChain[0]->cElement <= 1)
|
---|
1167 | alternate = NULL;
|
---|
1168 | else
|
---|
1169 | {
|
---|
1170 | DWORD i, j, infoStatus;
|
---|
1171 | PCCERT_CONTEXT alternateIssuer = NULL;
|
---|
1172 |
|
---|
1173 | alternate = NULL;
|
---|
1174 | for (i = 0; !alternateIssuer && i < chain->context.cChain; i++)
|
---|
1175 | for (j = 0; !alternateIssuer &&
|
---|
1176 | j < chain->context.rgpChain[i]->cElement - 1; j++)
|
---|
1177 | {
|
---|
1178 | PCCERT_CONTEXT subject =
|
---|
1179 | chain->context.rgpChain[i]->rgpElement[j]->pCertContext;
|
---|
1180 | PCCERT_CONTEXT prevIssuer = CertDuplicateCertificateContext(
|
---|
1181 | chain->context.rgpChain[i]->rgpElement[j + 1]->pCertContext);
|
---|
1182 |
|
---|
1183 | alternateIssuer = CRYPT_GetIssuer(prevIssuer->hCertStore,
|
---|
1184 | subject, prevIssuer, &infoStatus);
|
---|
1185 | }
|
---|
1186 | if (alternateIssuer)
|
---|
1187 | {
|
---|
1188 | i--;
|
---|
1189 | j--;
|
---|
1190 | alternate = CRYPT_CopyChainToElement(chain, i, j);
|
---|
1191 | if (alternate)
|
---|
1192 | {
|
---|
1193 | BOOL ret = CRYPT_AddCertToSimpleChain(engine,
|
---|
1194 | alternate->context.rgpChain[i], alternateIssuer, infoStatus);
|
---|
1195 |
|
---|
1196 | /* CRYPT_AddCertToSimpleChain add-ref's the issuer, so free it
|
---|
1197 | * to close the enumeration that found it
|
---|
1198 | */
|
---|
1199 | CertFreeCertificateContext(alternateIssuer);
|
---|
1200 | if (ret)
|
---|
1201 | {
|
---|
1202 | ret = CRYPT_BuildSimpleChain(engine, alternate->world,
|
---|
1203 | alternate->context.rgpChain[i]);
|
---|
1204 | if (ret)
|
---|
1205 | CRYPT_CheckSimpleChain(engine,
|
---|
1206 | alternate->context.rgpChain[i], pTime);
|
---|
1207 | CRYPT_CombineTrustStatus(&alternate->context.TrustStatus,
|
---|
1208 | &alternate->context.rgpChain[i]->TrustStatus);
|
---|
1209 | }
|
---|
1210 | if (!ret)
|
---|
1211 | {
|
---|
1212 | CRYPT_FreeChainContext(alternate);
|
---|
1213 | alternate = NULL;
|
---|
1214 | }
|
---|
1215 | }
|
---|
1216 | }
|
---|
1217 | }
|
---|
1218 | TRACE("%p\n", alternate);
|
---|
1219 | return alternate;
|
---|
1220 | }
|
---|
1221 |
|
---|
1222 | #define CHAIN_QUALITY_SIGNATURE_VALID 8
|
---|
1223 | #define CHAIN_QUALITY_TIME_VALID 4
|
---|
1224 | #define CHAIN_QUALITY_COMPLETE_CHAIN 2
|
---|
1225 | #define CHAIN_QUALITY_TRUSTED_ROOT 1
|
---|
1226 |
|
---|
1227 | #define CHAIN_QUALITY_HIGHEST \
|
---|
1228 | CHAIN_QUALITY_SIGNATURE_VALID | CHAIN_QUALITY_TIME_VALID | \
|
---|
1229 | CHAIN_QUALITY_COMPLETE_CHAIN | CHAIN_QUALITY_TRUSTED_ROOT
|
---|
1230 |
|
---|
1231 | #define IS_TRUST_ERROR_SET(TrustStatus, bits) \
|
---|
1232 | (TrustStatus)->dwErrorStatus & (bits)
|
---|
1233 |
|
---|
1234 | static DWORD CRYPT_ChainQuality(PCertificateChain chain)
|
---|
1235 | {
|
---|
1236 | DWORD quality = CHAIN_QUALITY_HIGHEST;
|
---|
1237 |
|
---|
1238 | if (IS_TRUST_ERROR_SET(&chain->context.TrustStatus,
|
---|
1239 | CERT_TRUST_IS_UNTRUSTED_ROOT))
|
---|
1240 | quality &= ~CHAIN_QUALITY_TRUSTED_ROOT;
|
---|
1241 | if (IS_TRUST_ERROR_SET(&chain->context.TrustStatus,
|
---|
1242 | CERT_TRUST_IS_PARTIAL_CHAIN))
|
---|
1243 | if (chain->context.TrustStatus.dwErrorStatus & CERT_TRUST_IS_PARTIAL_CHAIN)
|
---|
1244 | quality &= ~CHAIN_QUALITY_COMPLETE_CHAIN;
|
---|
1245 | if (IS_TRUST_ERROR_SET(&chain->context.TrustStatus,
|
---|
1246 | CERT_TRUST_IS_NOT_TIME_VALID | CERT_TRUST_IS_NOT_TIME_NESTED))
|
---|
1247 | quality &= ~CHAIN_QUALITY_TIME_VALID;
|
---|
1248 | if (IS_TRUST_ERROR_SET(&chain->context.TrustStatus,
|
---|
1249 | CERT_TRUST_IS_NOT_SIGNATURE_VALID))
|
---|
1250 | quality &= ~CHAIN_QUALITY_SIGNATURE_VALID;
|
---|
1251 | return quality;
|
---|
1252 | }
|
---|
1253 |
|
---|
1254 | /* Chooses the highest quality chain among chain and its "lower quality"
|
---|
1255 | * alternate chains. Returns the highest quality chain, with all other
|
---|
1256 | * chains as lower quality chains of it.
|
---|
1257 | */
|
---|
1258 | static PCertificateChain CRYPT_ChooseHighestQualityChain(
|
---|
1259 | PCertificateChain chain)
|
---|
1260 | {
|
---|
1261 | DWORD i;
|
---|
1262 |
|
---|
1263 | /* There are always only two chains being considered: chain, and an
|
---|
1264 | * alternate at chain->rgpLowerQualityChainContext[i]. If the alternate
|
---|
1265 | * has a higher quality than chain, the alternate gets assigned the lower
|
---|
1266 | * quality contexts, with chain taking the alternate's place among the
|
---|
1267 | * lower quality contexts.
|
---|
1268 | */
|
---|
1269 | for (i = 0; i < chain->context.cLowerQualityChainContext; i++)
|
---|
1270 | {
|
---|
1271 | PCertificateChain alternate =
|
---|
1272 | (PCertificateChain)chain->context.rgpLowerQualityChainContext[i];
|
---|
1273 |
|
---|
1274 | if (CRYPT_ChainQuality(alternate) > CRYPT_ChainQuality(chain))
|
---|
1275 | {
|
---|
1276 | alternate->context.cLowerQualityChainContext =
|
---|
1277 | chain->context.cLowerQualityChainContext;
|
---|
1278 | alternate->context.rgpLowerQualityChainContext =
|
---|
1279 | chain->context.rgpLowerQualityChainContext;
|
---|
1280 | alternate->context.rgpLowerQualityChainContext[i] =
|
---|
1281 | (PCCERT_CHAIN_CONTEXT)chain;
|
---|
1282 | chain->context.cLowerQualityChainContext = 0;
|
---|
1283 | chain->context.rgpLowerQualityChainContext = NULL;
|
---|
1284 | chain = alternate;
|
---|
1285 | }
|
---|
1286 | }
|
---|
1287 | return chain;
|
---|
1288 | }
|
---|
1289 |
|
---|
1290 | static BOOL CRYPT_AddAlternateChainToChain(PCertificateChain chain,
|
---|
1291 | PCertificateChain alternate)
|
---|
1292 | {
|
---|
1293 | BOOL ret;
|
---|
1294 |
|
---|
1295 | if (chain->context.cLowerQualityChainContext)
|
---|
1296 | chain->context.rgpLowerQualityChainContext =
|
---|
1297 | CryptMemRealloc(chain->context.rgpLowerQualityChainContext,
|
---|
1298 | (chain->context.cLowerQualityChainContext + 1) *
|
---|
1299 | sizeof(PCCERT_CHAIN_CONTEXT));
|
---|
1300 | else
|
---|
1301 | chain->context.rgpLowerQualityChainContext =
|
---|
1302 | CryptMemAlloc(sizeof(PCCERT_CHAIN_CONTEXT));
|
---|
1303 | if (chain->context.rgpLowerQualityChainContext)
|
---|
1304 | {
|
---|
1305 | chain->context.rgpLowerQualityChainContext[
|
---|
1306 | chain->context.cLowerQualityChainContext++] =
|
---|
1307 | (PCCERT_CHAIN_CONTEXT)alternate;
|
---|
1308 | ret = TRUE;
|
---|
1309 | }
|
---|
1310 | else
|
---|
1311 | ret = FALSE;
|
---|
1312 | return ret;
|
---|
1313 | }
|
---|
1314 |
|
---|
1315 | static PCERT_CHAIN_ELEMENT CRYPT_FindIthElementInChain(
|
---|
1316 | PCERT_CHAIN_CONTEXT chain, DWORD i)
|
---|
1317 | {
|
---|
1318 | DWORD j, iElement;
|
---|
1319 | PCERT_CHAIN_ELEMENT element = NULL;
|
---|
1320 |
|
---|
1321 | for (j = 0, iElement = 0; !element && j < chain->cChain; j++)
|
---|
1322 | {
|
---|
1323 | if (iElement + chain->rgpChain[j]->cElement < i)
|
---|
1324 | iElement += chain->rgpChain[j]->cElement;
|
---|
1325 | else
|
---|
1326 | element = chain->rgpChain[j]->rgpElement[i - iElement];
|
---|
1327 | }
|
---|
1328 | return element;
|
---|
1329 | }
|
---|
1330 |
|
---|
1331 | typedef struct _CERT_CHAIN_PARA_NO_EXTRA_FIELDS {
|
---|
1332 | DWORD cbSize;
|
---|
1333 | CERT_USAGE_MATCH RequestedUsage;
|
---|
1334 | } CERT_CHAIN_PARA_NO_EXTRA_FIELDS, *PCERT_CHAIN_PARA_NO_EXTRA_FIELDS;
|
---|
1335 |
|
---|
1336 | static void CRYPT_VerifyChainRevocation(PCERT_CHAIN_CONTEXT chain,
|
---|
1337 | LPFILETIME pTime, PCERT_CHAIN_PARA pChainPara, DWORD chainFlags)
|
---|
1338 | {
|
---|
1339 | DWORD cContext;
|
---|
1340 |
|
---|
1341 | if (chainFlags & CERT_CHAIN_REVOCATION_CHECK_END_CERT)
|
---|
1342 | cContext = 1;
|
---|
1343 | else if ((chainFlags & CERT_CHAIN_REVOCATION_CHECK_CHAIN) ||
|
---|
1344 | (chainFlags & CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT))
|
---|
1345 | {
|
---|
1346 | DWORD i;
|
---|
1347 |
|
---|
1348 | for (i = 0, cContext = 0; i < chain->cChain; i++)
|
---|
1349 | {
|
---|
1350 | if (i < chain->cChain - 1 ||
|
---|
1351 | chainFlags & CERT_CHAIN_REVOCATION_CHECK_CHAIN)
|
---|
1352 | cContext += chain->rgpChain[i]->cElement;
|
---|
1353 | else
|
---|
1354 | cContext += chain->rgpChain[i]->cElement - 1;
|
---|
1355 | }
|
---|
1356 | }
|
---|
1357 | else
|
---|
1358 | cContext = 0;
|
---|
1359 | if (cContext)
|
---|
1360 | {
|
---|
1361 | PCCERT_CONTEXT *contexts =
|
---|
1362 | CryptMemAlloc(cContext * sizeof(PCCERT_CONTEXT *));
|
---|
1363 |
|
---|
1364 | if (contexts)
|
---|
1365 | {
|
---|
1366 | DWORD i, j, iContext, revocationFlags;
|
---|
1367 | CERT_REVOCATION_PARA revocationPara = { sizeof(revocationPara), 0 };
|
---|
1368 | CERT_REVOCATION_STATUS revocationStatus =
|
---|
1369 | { sizeof(revocationStatus), 0 };
|
---|
1370 | BOOL ret;
|
---|
1371 |
|
---|
1372 | for (i = 0, iContext = 0; iContext < cContext && i < chain->cChain;
|
---|
1373 | i++)
|
---|
1374 | {
|
---|
1375 | for (j = 0; iContext < cContext &&
|
---|
1376 | j < chain->rgpChain[i]->cElement; j++)
|
---|
1377 | contexts[iContext++] =
|
---|
1378 | chain->rgpChain[i]->rgpElement[j]->pCertContext;
|
---|
1379 | }
|
---|
1380 | revocationFlags = CERT_VERIFY_REV_CHAIN_FLAG;
|
---|
1381 | if (chainFlags & CERT_CHAIN_REVOCATION_CHECK_CACHE_ONLY)
|
---|
1382 | revocationFlags |= CERT_VERIFY_CACHE_ONLY_BASED_REVOCATION;
|
---|
1383 | if (chainFlags & CERT_CHAIN_REVOCATION_ACCUMULATIVE_TIMEOUT)
|
---|
1384 | revocationFlags |= CERT_VERIFY_REV_ACCUMULATIVE_TIMEOUT_FLAG;
|
---|
1385 | revocationPara.pftTimeToUse = pTime;
|
---|
1386 | if (pChainPara->cbSize == sizeof(CERT_CHAIN_PARA))
|
---|
1387 | {
|
---|
1388 | revocationPara.dwUrlRetrievalTimeout =
|
---|
1389 | pChainPara->dwUrlRetrievalTimeout;
|
---|
1390 | revocationPara.fCheckFreshnessTime =
|
---|
1391 | pChainPara->fCheckRevocationFreshnessTime;
|
---|
1392 | revocationPara.dwFreshnessTime =
|
---|
1393 | pChainPara->dwRevocationFreshnessTime;
|
---|
1394 | }
|
---|
1395 | ret = CertVerifyRevocation(X509_ASN_ENCODING,
|
---|
1396 | CERT_CONTEXT_REVOCATION_TYPE, cContext, (void **)contexts,
|
---|
1397 | revocationFlags, &revocationPara, &revocationStatus);
|
---|
1398 | if (!ret)
|
---|
1399 | {
|
---|
1400 | PCERT_CHAIN_ELEMENT element =
|
---|
1401 | CRYPT_FindIthElementInChain(chain, revocationStatus.dwIndex);
|
---|
1402 | DWORD error;
|
---|
1403 |
|
---|
1404 | switch (revocationStatus.dwError)
|
---|
1405 | {
|
---|
1406 | case CRYPT_E_NO_REVOCATION_CHECK:
|
---|
1407 | case CRYPT_E_NO_REVOCATION_DLL:
|
---|
1408 | case CRYPT_E_NOT_IN_REVOCATION_DATABASE:
|
---|
1409 | error = CERT_TRUST_REVOCATION_STATUS_UNKNOWN;
|
---|
1410 | break;
|
---|
1411 | case CRYPT_E_REVOCATION_OFFLINE:
|
---|
1412 | error = CERT_TRUST_IS_OFFLINE_REVOCATION;
|
---|
1413 | break;
|
---|
1414 | case CRYPT_E_REVOKED:
|
---|
1415 | error = CERT_TRUST_IS_REVOKED;
|
---|
1416 | break;
|
---|
1417 | default:
|
---|
1418 | WARN("unmapped error %08x\n", revocationStatus.dwError);
|
---|
1419 | error = 0;
|
---|
1420 | }
|
---|
1421 | if (element)
|
---|
1422 | {
|
---|
1423 | /* FIXME: set element's pRevocationInfo member */
|
---|
1424 | element->TrustStatus.dwErrorStatus |= error;
|
---|
1425 | }
|
---|
1426 | chain->TrustStatus.dwErrorStatus |= error;
|
---|
1427 | }
|
---|
1428 | CryptMemFree(contexts);
|
---|
1429 | }
|
---|
1430 | }
|
---|
1431 | }
|
---|
1432 |
|
---|
1433 | BOOL WINAPI CertGetCertificateChain(HCERTCHAINENGINE hChainEngine,
|
---|
1434 | PCCERT_CONTEXT pCertContext, LPFILETIME pTime, HCERTSTORE hAdditionalStore,
|
---|
1435 | PCERT_CHAIN_PARA pChainPara, DWORD dwFlags, LPVOID pvReserved,
|
---|
1436 | PCCERT_CHAIN_CONTEXT* ppChainContext)
|
---|
1437 | {
|
---|
1438 | BOOL ret;
|
---|
1439 | PCertificateChain chain = NULL;
|
---|
1440 |
|
---|
1441 | TRACE("(%p, %p, %p, %p, %p, %08x, %p, %p)\n", hChainEngine, pCertContext,
|
---|
1442 | pTime, hAdditionalStore, pChainPara, dwFlags, pvReserved, ppChainContext);
|
---|
1443 |
|
---|
1444 | if (ppChainContext)
|
---|
1445 | *ppChainContext = NULL;
|
---|
1446 | if (!pChainPara)
|
---|
1447 | {
|
---|
1448 | SetLastError(E_INVALIDARG);
|
---|
1449 | return FALSE;
|
---|
1450 | }
|
---|
1451 | if (!pCertContext->pCertInfo->SignatureAlgorithm.pszObjId)
|
---|
1452 | {
|
---|
1453 | SetLastError(ERROR_INVALID_DATA);
|
---|
1454 | return FALSE;
|
---|
1455 | }
|
---|
1456 | if (pChainPara->cbSize != sizeof(CERT_CHAIN_PARA_NO_EXTRA_FIELDS) &&
|
---|
1457 | pChainPara->cbSize != sizeof(CERT_CHAIN_PARA))
|
---|
1458 | {
|
---|
1459 | SetLastError(E_INVALIDARG);
|
---|
1460 | return FALSE;
|
---|
1461 | }
|
---|
1462 | if (!hChainEngine)
|
---|
1463 | hChainEngine = CRYPT_GetDefaultChainEngine();
|
---|
1464 | /* FIXME: what about HCCE_LOCAL_MACHINE? */
|
---|
1465 | ret = CRYPT_BuildCandidateChainFromCert(hChainEngine, pCertContext, pTime,
|
---|
1466 | hAdditionalStore, &chain);
|
---|
1467 | if (ret)
|
---|
1468 | {
|
---|
1469 | PCertificateChain alternate = NULL;
|
---|
1470 | PCERT_CHAIN_CONTEXT pChain;
|
---|
1471 |
|
---|
1472 | do {
|
---|
1473 | alternate = CRYPT_BuildAlternateContextFromChain(hChainEngine,
|
---|
1474 | pTime, hAdditionalStore, chain);
|
---|
1475 |
|
---|
1476 | /* Alternate contexts are added as "lower quality" contexts of
|
---|
1477 | * chain, to avoid loops in alternate chain creation.
|
---|
1478 | * The highest-quality chain is chosen at the end.
|
---|
1479 | */
|
---|
1480 | if (alternate)
|
---|
1481 | ret = CRYPT_AddAlternateChainToChain(chain, alternate);
|
---|
1482 | } while (ret && alternate);
|
---|
1483 | chain = CRYPT_ChooseHighestQualityChain(chain);
|
---|
1484 | if (!(dwFlags & CERT_CHAIN_RETURN_LOWER_QUALITY_CONTEXTS))
|
---|
1485 | CRYPT_FreeLowerQualityChains(chain);
|
---|
1486 | pChain = (PCERT_CHAIN_CONTEXT)chain;
|
---|
1487 | CRYPT_VerifyChainRevocation(pChain, pTime, pChainPara, dwFlags);
|
---|
1488 | if (ppChainContext)
|
---|
1489 | *ppChainContext = pChain;
|
---|
1490 | else
|
---|
1491 | CertFreeCertificateChain(pChain);
|
---|
1492 | }
|
---|
1493 | TRACE("returning %d\n", ret);
|
---|
1494 | return ret;
|
---|
1495 | }
|
---|
1496 |
|
---|
1497 | PCCERT_CHAIN_CONTEXT WINAPI CertDuplicateCertificateChain(
|
---|
1498 | PCCERT_CHAIN_CONTEXT pChainContext)
|
---|
1499 | {
|
---|
1500 | PCertificateChain chain = (PCertificateChain)pChainContext;
|
---|
1501 |
|
---|
1502 | TRACE("(%p)\n", pChainContext);
|
---|
1503 |
|
---|
1504 | if (chain)
|
---|
1505 | InterlockedIncrement(&chain->ref);
|
---|
1506 | return pChainContext;
|
---|
1507 | }
|
---|
1508 |
|
---|
1509 | VOID WINAPI CertFreeCertificateChain(PCCERT_CHAIN_CONTEXT pChainContext)
|
---|
1510 | {
|
---|
1511 | PCertificateChain chain = (PCertificateChain)pChainContext;
|
---|
1512 |
|
---|
1513 | TRACE("(%p)\n", pChainContext);
|
---|
1514 |
|
---|
1515 | if (chain)
|
---|
1516 | {
|
---|
1517 | if (InterlockedDecrement(&chain->ref) == 0)
|
---|
1518 | CRYPT_FreeChainContext(chain);
|
---|
1519 | }
|
---|
1520 | }
|
---|
1521 |
|
---|
1522 | static void find_element_with_error(PCCERT_CHAIN_CONTEXT chain, DWORD error,
|
---|
1523 | LONG *iChain, LONG *iElement)
|
---|
1524 | {
|
---|
1525 | DWORD i, j;
|
---|
1526 |
|
---|
1527 | for (i = 0; i < chain->cChain; i++)
|
---|
1528 | for (j = 0; j < chain->rgpChain[i]->cElement; j++)
|
---|
1529 | if (chain->rgpChain[i]->rgpElement[j]->TrustStatus.dwErrorStatus &
|
---|
1530 | error)
|
---|
1531 | {
|
---|
1532 | *iChain = i;
|
---|
1533 | *iElement = j;
|
---|
1534 | return;
|
---|
1535 | }
|
---|
1536 | }
|
---|
1537 |
|
---|
1538 | static BOOL WINAPI verify_base_policy(LPCSTR szPolicyOID,
|
---|
1539 | PCCERT_CHAIN_CONTEXT pChainContext, PCERT_CHAIN_POLICY_PARA pPolicyPara,
|
---|
1540 | PCERT_CHAIN_POLICY_STATUS pPolicyStatus)
|
---|
1541 | {
|
---|
1542 | pPolicyStatus->lChainIndex = pPolicyStatus->lElementIndex = -1;
|
---|
1543 | if (pChainContext->TrustStatus.dwErrorStatus &
|
---|
1544 | CERT_TRUST_IS_NOT_SIGNATURE_VALID)
|
---|
1545 | {
|
---|
1546 | pPolicyStatus->dwError = TRUST_E_CERT_SIGNATURE;
|
---|
1547 | find_element_with_error(pChainContext,
|
---|
1548 | CERT_TRUST_IS_NOT_SIGNATURE_VALID, &pPolicyStatus->lChainIndex,
|
---|
1549 | &pPolicyStatus->lElementIndex);
|
---|
1550 | }
|
---|
1551 | else if (pChainContext->TrustStatus.dwErrorStatus &
|
---|
1552 | CERT_TRUST_IS_UNTRUSTED_ROOT)
|
---|
1553 | {
|
---|
1554 | pPolicyStatus->dwError = CERT_E_UNTRUSTEDROOT;
|
---|
1555 | find_element_with_error(pChainContext,
|
---|
1556 | CERT_TRUST_IS_UNTRUSTED_ROOT, &pPolicyStatus->lChainIndex,
|
---|
1557 | &pPolicyStatus->lElementIndex);
|
---|
1558 | }
|
---|
1559 | else if (pChainContext->TrustStatus.dwErrorStatus & CERT_TRUST_IS_CYCLIC)
|
---|
1560 | {
|
---|
1561 | pPolicyStatus->dwError = CERT_E_CHAINING;
|
---|
1562 | find_element_with_error(pChainContext, CERT_TRUST_IS_CYCLIC,
|
---|
1563 | &pPolicyStatus->lChainIndex, &pPolicyStatus->lElementIndex);
|
---|
1564 | /* For a cyclic chain, which element is a cycle isn't meaningful */
|
---|
1565 | pPolicyStatus->lElementIndex = -1;
|
---|
1566 | }
|
---|
1567 | else
|
---|
1568 | pPolicyStatus->dwError = NO_ERROR;
|
---|
1569 | return TRUE;
|
---|
1570 | }
|
---|
1571 |
|
---|
1572 | static BYTE msTestPubKey1[] = {
|
---|
1573 | 0x30,0x47,0x02,0x40,0x81,0x55,0x22,0xb9,0x8a,0xa4,0x6f,0xed,0xd6,0xe7,0xd9,
|
---|
1574 | 0x66,0x0f,0x55,0xbc,0xd7,0xcd,0xd5,0xbc,0x4e,0x40,0x02,0x21,0xa2,0xb1,0xf7,
|
---|
1575 | 0x87,0x30,0x85,0x5e,0xd2,0xf2,0x44,0xb9,0xdc,0x9b,0x75,0xb6,0xfb,0x46,0x5f,
|
---|
1576 | 0x42,0xb6,0x9d,0x23,0x36,0x0b,0xde,0x54,0x0f,0xcd,0xbd,0x1f,0x99,0x2a,0x10,
|
---|
1577 | 0x58,0x11,0xcb,0x40,0xcb,0xb5,0xa7,0x41,0x02,0x03,0x01,0x00,0x01 };
|
---|
1578 | static BYTE msTestPubKey2[] = {
|
---|
1579 | 0x30,0x47,0x02,0x40,0x9c,0x50,0x05,0x1d,0xe2,0x0e,0x4c,0x53,0xd8,0xd9,0xb5,
|
---|
1580 | 0xe5,0xfd,0xe9,0xe3,0xad,0x83,0x4b,0x80,0x08,0xd9,0xdc,0xe8,0xe8,0x35,0xf8,
|
---|
1581 | 0x11,0xf1,0xe9,0x9b,0x03,0x7a,0x65,0x64,0x76,0x35,0xce,0x38,0x2c,0xf2,0xb6,
|
---|
1582 | 0x71,0x9e,0x06,0xd9,0xbf,0xbb,0x31,0x69,0xa3,0xf6,0x30,0xa0,0x78,0x7b,0x18,
|
---|
1583 | 0xdd,0x50,0x4d,0x79,0x1e,0xeb,0x61,0xc1,0x02,0x03,0x01,0x00,0x01 };
|
---|
1584 |
|
---|
1585 | static BOOL WINAPI verify_authenticode_policy(LPCSTR szPolicyOID,
|
---|
1586 | PCCERT_CHAIN_CONTEXT pChainContext, PCERT_CHAIN_POLICY_PARA pPolicyPara,
|
---|
1587 | PCERT_CHAIN_POLICY_STATUS pPolicyStatus)
|
---|
1588 | {
|
---|
1589 | BOOL ret = verify_base_policy(szPolicyOID, pChainContext, pPolicyPara,
|
---|
1590 | pPolicyStatus);
|
---|
1591 |
|
---|
1592 | if (ret && pPolicyStatus->dwError == CERT_E_UNTRUSTEDROOT)
|
---|
1593 | {
|
---|
1594 | CERT_PUBLIC_KEY_INFO msPubKey = { { 0 } };
|
---|
1595 | BOOL isMSTestRoot = FALSE;
|
---|
1596 | PCCERT_CONTEXT failingCert =
|
---|
1597 | pChainContext->rgpChain[pPolicyStatus->lChainIndex]->
|
---|
1598 | rgpElement[pPolicyStatus->lElementIndex]->pCertContext;
|
---|
1599 | DWORD i;
|
---|
1600 | CRYPT_DATA_BLOB keyBlobs[] = {
|
---|
1601 | { sizeof(msTestPubKey1), msTestPubKey1 },
|
---|
1602 | { sizeof(msTestPubKey2), msTestPubKey2 },
|
---|
1603 | };
|
---|
1604 |
|
---|
1605 | /* Check whether the root is an MS test root */
|
---|
1606 | for (i = 0; !isMSTestRoot && i < sizeof(keyBlobs) / sizeof(keyBlobs[0]);
|
---|
1607 | i++)
|
---|
1608 | {
|
---|
1609 | msPubKey.PublicKey.cbData = keyBlobs[i].cbData;
|
---|
1610 | msPubKey.PublicKey.pbData = keyBlobs[i].pbData;
|
---|
1611 | if (CertComparePublicKeyInfo(
|
---|
1612 | X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
---|
1613 | &failingCert->pCertInfo->SubjectPublicKeyInfo, &msPubKey))
|
---|
1614 | isMSTestRoot = TRUE;
|
---|
1615 | }
|
---|
1616 | if (isMSTestRoot)
|
---|
1617 | pPolicyStatus->dwError = CERT_E_UNTRUSTEDTESTROOT;
|
---|
1618 | }
|
---|
1619 | return ret;
|
---|
1620 | }
|
---|
1621 |
|
---|
1622 | static BOOL WINAPI verify_basic_constraints_policy(LPCSTR szPolicyOID,
|
---|
1623 | PCCERT_CHAIN_CONTEXT pChainContext, PCERT_CHAIN_POLICY_PARA pPolicyPara,
|
---|
1624 | PCERT_CHAIN_POLICY_STATUS pPolicyStatus)
|
---|
1625 | {
|
---|
1626 | pPolicyStatus->lChainIndex = pPolicyStatus->lElementIndex = -1;
|
---|
1627 | if (pChainContext->TrustStatus.dwErrorStatus &
|
---|
1628 | CERT_TRUST_INVALID_BASIC_CONSTRAINTS)
|
---|
1629 | {
|
---|
1630 | pPolicyStatus->dwError = TRUST_E_BASIC_CONSTRAINTS;
|
---|
1631 | find_element_with_error(pChainContext,
|
---|
1632 | CERT_TRUST_INVALID_BASIC_CONSTRAINTS, &pPolicyStatus->lChainIndex,
|
---|
1633 | &pPolicyStatus->lElementIndex);
|
---|
1634 | }
|
---|
1635 | else
|
---|
1636 | pPolicyStatus->dwError = NO_ERROR;
|
---|
1637 | return TRUE;
|
---|
1638 | }
|
---|
1639 |
|
---|
1640 | static BYTE msPubKey1[] = {
|
---|
1641 | 0x30,0x82,0x01,0x0a,0x02,0x82,0x01,0x01,0x00,0xdf,0x08,0xba,0xe3,0x3f,0x6e,
|
---|
1642 | 0x64,0x9b,0xf5,0x89,0xaf,0x28,0x96,0x4a,0x07,0x8f,0x1b,0x2e,0x8b,0x3e,0x1d,
|
---|
1643 | 0xfc,0xb8,0x80,0x69,0xa3,0xa1,0xce,0xdb,0xdf,0xb0,0x8e,0x6c,0x89,0x76,0x29,
|
---|
1644 | 0x4f,0xca,0x60,0x35,0x39,0xad,0x72,0x32,0xe0,0x0b,0xae,0x29,0x3d,0x4c,0x16,
|
---|
1645 | 0xd9,0x4b,0x3c,0x9d,0xda,0xc5,0xd3,0xd1,0x09,0xc9,0x2c,0x6f,0xa6,0xc2,0x60,
|
---|
1646 | 0x53,0x45,0xdd,0x4b,0xd1,0x55,0xcd,0x03,0x1c,0xd2,0x59,0x56,0x24,0xf3,0xe5,
|
---|
1647 | 0x78,0xd8,0x07,0xcc,0xd8,0xb3,0x1f,0x90,0x3f,0xc0,0x1a,0x71,0x50,0x1d,0x2d,
|
---|
1648 | 0xa7,0x12,0x08,0x6d,0x7c,0xb0,0x86,0x6c,0xc7,0xba,0x85,0x32,0x07,0xe1,0x61,
|
---|
1649 | 0x6f,0xaf,0x03,0xc5,0x6d,0xe5,0xd6,0xa1,0x8f,0x36,0xf6,0xc1,0x0b,0xd1,0x3e,
|
---|
1650 | 0x69,0x97,0x48,0x72,0xc9,0x7f,0xa4,0xc8,0xc2,0x4a,0x4c,0x7e,0xa1,0xd1,0x94,
|
---|
1651 | 0xa6,0xd7,0xdc,0xeb,0x05,0x46,0x2e,0xb8,0x18,0xb4,0x57,0x1d,0x86,0x49,0xdb,
|
---|
1652 | 0x69,0x4a,0x2c,0x21,0xf5,0x5e,0x0f,0x54,0x2d,0x5a,0x43,0xa9,0x7a,0x7e,0x6a,
|
---|
1653 | 0x8e,0x50,0x4d,0x25,0x57,0xa1,0xbf,0x1b,0x15,0x05,0x43,0x7b,0x2c,0x05,0x8d,
|
---|
1654 | 0xbd,0x3d,0x03,0x8c,0x93,0x22,0x7d,0x63,0xea,0x0a,0x57,0x05,0x06,0x0a,0xdb,
|
---|
1655 | 0x61,0x98,0x65,0x2d,0x47,0x49,0xa8,0xe7,0xe6,0x56,0x75,0x5c,0xb8,0x64,0x08,
|
---|
1656 | 0x63,0xa9,0x30,0x40,0x66,0xb2,0xf9,0xb6,0xe3,0x34,0xe8,0x67,0x30,0xe1,0x43,
|
---|
1657 | 0x0b,0x87,0xff,0xc9,0xbe,0x72,0x10,0x5e,0x23,0xf0,0x9b,0xa7,0x48,0x65,0xbf,
|
---|
1658 | 0x09,0x88,0x7b,0xcd,0x72,0xbc,0x2e,0x79,0x9b,0x7b,0x02,0x03,0x01,0x00,0x01 };
|
---|
1659 | static BYTE msPubKey2[] = {
|
---|
1660 | 0x30,0x82,0x01,0x0a,0x02,0x82,0x01,0x01,0x00,0xa9,0x02,0xbd,0xc1,0x70,0xe6,
|
---|
1661 | 0x3b,0xf2,0x4e,0x1b,0x28,0x9f,0x97,0x78,0x5e,0x30,0xea,0xa2,0xa9,0x8d,0x25,
|
---|
1662 | 0x5f,0xf8,0xfe,0x95,0x4c,0xa3,0xb7,0xfe,0x9d,0xa2,0x20,0x3e,0x7c,0x51,0xa2,
|
---|
1663 | 0x9b,0xa2,0x8f,0x60,0x32,0x6b,0xd1,0x42,0x64,0x79,0xee,0xac,0x76,0xc9,0x54,
|
---|
1664 | 0xda,0xf2,0xeb,0x9c,0x86,0x1c,0x8f,0x9f,0x84,0x66,0xb3,0xc5,0x6b,0x7a,0x62,
|
---|
1665 | 0x23,0xd6,0x1d,0x3c,0xde,0x0f,0x01,0x92,0xe8,0x96,0xc4,0xbf,0x2d,0x66,0x9a,
|
---|
1666 | 0x9a,0x68,0x26,0x99,0xd0,0x3a,0x2c,0xbf,0x0c,0xb5,0x58,0x26,0xc1,0x46,0xe7,
|
---|
1667 | 0x0a,0x3e,0x38,0x96,0x2c,0xa9,0x28,0x39,0xa8,0xec,0x49,0x83,0x42,0xe3,0x84,
|
---|
1668 | 0x0f,0xbb,0x9a,0x6c,0x55,0x61,0xac,0x82,0x7c,0xa1,0x60,0x2d,0x77,0x4c,0xe9,
|
---|
1669 | 0x99,0xb4,0x64,0x3b,0x9a,0x50,0x1c,0x31,0x08,0x24,0x14,0x9f,0xa9,0xe7,0x91,
|
---|
1670 | 0x2b,0x18,0xe6,0x3d,0x98,0x63,0x14,0x60,0x58,0x05,0x65,0x9f,0x1d,0x37,0x52,
|
---|
1671 | 0x87,0xf7,0xa7,0xef,0x94,0x02,0xc6,0x1b,0xd3,0xbf,0x55,0x45,0xb3,0x89,0x80,
|
---|
1672 | 0xbf,0x3a,0xec,0x54,0x94,0x4e,0xae,0xfd,0xa7,0x7a,0x6d,0x74,0x4e,0xaf,0x18,
|
---|
1673 | 0xcc,0x96,0x09,0x28,0x21,0x00,0x57,0x90,0x60,0x69,0x37,0xbb,0x4b,0x12,0x07,
|
---|
1674 | 0x3c,0x56,0xff,0x5b,0xfb,0xa4,0x66,0x0a,0x08,0xa6,0xd2,0x81,0x56,0x57,0xef,
|
---|
1675 | 0xb6,0x3b,0x5e,0x16,0x81,0x77,0x04,0xda,0xf6,0xbe,0xae,0x80,0x95,0xfe,0xb0,
|
---|
1676 | 0xcd,0x7f,0xd6,0xa7,0x1a,0x72,0x5c,0x3c,0xca,0xbc,0xf0,0x08,0xa3,0x22,0x30,
|
---|
1677 | 0xb3,0x06,0x85,0xc9,0xb3,0x20,0x77,0x13,0x85,0xdf,0x02,0x03,0x01,0x00,0x01 };
|
---|
1678 | static BYTE msPubKey3[] = {
|
---|
1679 | 0x30,0x82,0x02,0x0a,0x02,0x82,0x02,0x01,0x00,0xf3,0x5d,0xfa,0x80,0x67,0xd4,
|
---|
1680 | 0x5a,0xa7,0xa9,0x0c,0x2c,0x90,0x20,0xd0,0x35,0x08,0x3c,0x75,0x84,0xcd,0xb7,
|
---|
1681 | 0x07,0x89,0x9c,0x89,0xda,0xde,0xce,0xc3,0x60,0xfa,0x91,0x68,0x5a,0x9e,0x94,
|
---|
1682 | 0x71,0x29,0x18,0x76,0x7c,0xc2,0xe0,0xc8,0x25,0x76,0x94,0x0e,0x58,0xfa,0x04,
|
---|
1683 | 0x34,0x36,0xe6,0xdf,0xaf,0xf7,0x80,0xba,0xe9,0x58,0x0b,0x2b,0x93,0xe5,0x9d,
|
---|
1684 | 0x05,0xe3,0x77,0x22,0x91,0xf7,0x34,0x64,0x3c,0x22,0x91,0x1d,0x5e,0xe1,0x09,
|
---|
1685 | 0x90,0xbc,0x14,0xfe,0xfc,0x75,0x58,0x19,0xe1,0x79,0xb7,0x07,0x92,0xa3,0xae,
|
---|
1686 | 0x88,0x59,0x08,0xd8,0x9f,0x07,0xca,0x03,0x58,0xfc,0x68,0x29,0x6d,0x32,0xd7,
|
---|
1687 | 0xd2,0xa8,0xcb,0x4b,0xfc,0xe1,0x0b,0x48,0x32,0x4f,0xe6,0xeb,0xb8,0xad,0x4f,
|
---|
1688 | 0xe4,0x5c,0x6f,0x13,0x94,0x99,0xdb,0x95,0xd5,0x75,0xdb,0xa8,0x1a,0xb7,0x94,
|
---|
1689 | 0x91,0xb4,0x77,0x5b,0xf5,0x48,0x0c,0x8f,0x6a,0x79,0x7d,0x14,0x70,0x04,0x7d,
|
---|
1690 | 0x6d,0xaf,0x90,0xf5,0xda,0x70,0xd8,0x47,0xb7,0xbf,0x9b,0x2f,0x6c,0xe7,0x05,
|
---|
1691 | 0xb7,0xe1,0x11,0x60,0xac,0x79,0x91,0x14,0x7c,0xc5,0xd6,0xa6,0xe4,0xe1,0x7e,
|
---|
1692 | 0xd5,0xc3,0x7e,0xe5,0x92,0xd2,0x3c,0x00,0xb5,0x36,0x82,0xde,0x79,0xe1,0x6d,
|
---|
1693 | 0xf3,0xb5,0x6e,0xf8,0x9f,0x33,0xc9,0xcb,0x52,0x7d,0x73,0x98,0x36,0xdb,0x8b,
|
---|
1694 | 0xa1,0x6b,0xa2,0x95,0x97,0x9b,0xa3,0xde,0xc2,0x4d,0x26,0xff,0x06,0x96,0x67,
|
---|
1695 | 0x25,0x06,0xc8,0xe7,0xac,0xe4,0xee,0x12,0x33,0x95,0x31,0x99,0xc8,0x35,0x08,
|
---|
1696 | 0x4e,0x34,0xca,0x79,0x53,0xd5,0xb5,0xbe,0x63,0x32,0x59,0x40,0x36,0xc0,0xa5,
|
---|
1697 | 0x4e,0x04,0x4d,0x3d,0xdb,0x5b,0x07,0x33,0xe4,0x58,0xbf,0xef,0x3f,0x53,0x64,
|
---|
1698 | 0xd8,0x42,0x59,0x35,0x57,0xfd,0x0f,0x45,0x7c,0x24,0x04,0x4d,0x9e,0xd6,0x38,
|
---|
1699 | 0x74,0x11,0x97,0x22,0x90,0xce,0x68,0x44,0x74,0x92,0x6f,0xd5,0x4b,0x6f,0xb0,
|
---|
1700 | 0x86,0xe3,0xc7,0x36,0x42,0xa0,0xd0,0xfc,0xc1,0xc0,0x5a,0xf9,0xa3,0x61,0xb9,
|
---|
1701 | 0x30,0x47,0x71,0x96,0x0a,0x16,0xb0,0x91,0xc0,0x42,0x95,0xef,0x10,0x7f,0x28,
|
---|
1702 | 0x6a,0xe3,0x2a,0x1f,0xb1,0xe4,0xcd,0x03,0x3f,0x77,0x71,0x04,0xc7,0x20,0xfc,
|
---|
1703 | 0x49,0x0f,0x1d,0x45,0x88,0xa4,0xd7,0xcb,0x7e,0x88,0xad,0x8e,0x2d,0xec,0x45,
|
---|
1704 | 0xdb,0xc4,0x51,0x04,0xc9,0x2a,0xfc,0xec,0x86,0x9e,0x9a,0x11,0x97,0x5b,0xde,
|
---|
1705 | 0xce,0x53,0x88,0xe6,0xe2,0xb7,0xfd,0xac,0x95,0xc2,0x28,0x40,0xdb,0xef,0x04,
|
---|
1706 | 0x90,0xdf,0x81,0x33,0x39,0xd9,0xb2,0x45,0xa5,0x23,0x87,0x06,0xa5,0x55,0x89,
|
---|
1707 | 0x31,0xbb,0x06,0x2d,0x60,0x0e,0x41,0x18,0x7d,0x1f,0x2e,0xb5,0x97,0xcb,0x11,
|
---|
1708 | 0xeb,0x15,0xd5,0x24,0xa5,0x94,0xef,0x15,0x14,0x89,0xfd,0x4b,0x73,0xfa,0x32,
|
---|
1709 | 0x5b,0xfc,0xd1,0x33,0x00,0xf9,0x59,0x62,0x70,0x07,0x32,0xea,0x2e,0xab,0x40,
|
---|
1710 | 0x2d,0x7b,0xca,0xdd,0x21,0x67,0x1b,0x30,0x99,0x8f,0x16,0xaa,0x23,0xa8,0x41,
|
---|
1711 | 0xd1,0xb0,0x6e,0x11,0x9b,0x36,0xc4,0xde,0x40,0x74,0x9c,0xe1,0x58,0x65,0xc1,
|
---|
1712 | 0x60,0x1e,0x7a,0x5b,0x38,0xc8,0x8f,0xbb,0x04,0x26,0x7c,0xd4,0x16,0x40,0xe5,
|
---|
1713 | 0xb6,0x6b,0x6c,0xaa,0x86,0xfd,0x00,0xbf,0xce,0xc1,0x35,0x02,0x03,0x01,0x00,
|
---|
1714 | 0x01 };
|
---|
1715 |
|
---|
1716 | static BOOL WINAPI verify_ms_root_policy(LPCSTR szPolicyOID,
|
---|
1717 | PCCERT_CHAIN_CONTEXT pChainContext, PCERT_CHAIN_POLICY_PARA pPolicyPara,
|
---|
1718 | PCERT_CHAIN_POLICY_STATUS pPolicyStatus)
|
---|
1719 | {
|
---|
1720 | BOOL ret = verify_base_policy(szPolicyOID, pChainContext, pPolicyPara,
|
---|
1721 | pPolicyStatus);
|
---|
1722 |
|
---|
1723 | if (ret && !pPolicyStatus->dwError)
|
---|
1724 | {
|
---|
1725 | CERT_PUBLIC_KEY_INFO msPubKey = { { 0 } };
|
---|
1726 | BOOL isMSRoot = FALSE;
|
---|
1727 | DWORD i;
|
---|
1728 | CRYPT_DATA_BLOB keyBlobs[] = {
|
---|
1729 | { sizeof(msPubKey1), msPubKey1 },
|
---|
1730 | { sizeof(msPubKey2), msPubKey2 },
|
---|
1731 | { sizeof(msPubKey3), msPubKey3 },
|
---|
1732 | };
|
---|
1733 | PCERT_SIMPLE_CHAIN rootChain =
|
---|
1734 | pChainContext->rgpChain[pChainContext->cChain -1 ];
|
---|
1735 | PCCERT_CONTEXT root =
|
---|
1736 | rootChain->rgpElement[rootChain->cElement - 1]->pCertContext;
|
---|
1737 |
|
---|
1738 | for (i = 0; !isMSRoot && i < sizeof(keyBlobs) / sizeof(keyBlobs[0]);
|
---|
1739 | i++)
|
---|
1740 | {
|
---|
1741 | msPubKey.PublicKey.cbData = keyBlobs[i].cbData;
|
---|
1742 | msPubKey.PublicKey.pbData = keyBlobs[i].pbData;
|
---|
1743 | if (CertComparePublicKeyInfo(
|
---|
1744 | X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
---|
1745 | &root->pCertInfo->SubjectPublicKeyInfo, &msPubKey))
|
---|
1746 | isMSRoot = TRUE;
|
---|
1747 | }
|
---|
1748 | if (isMSRoot)
|
---|
1749 | pPolicyStatus->lChainIndex = pPolicyStatus->lElementIndex = 0;
|
---|
1750 | }
|
---|
1751 | return ret;
|
---|
1752 | }
|
---|
1753 |
|
---|
1754 | typedef BOOL (*WINAPI CertVerifyCertificateChainPolicyFunc)(LPCSTR szPolicyOID,
|
---|
1755 | PCCERT_CHAIN_CONTEXT pChainContext, PCERT_CHAIN_POLICY_PARA pPolicyPara,
|
---|
1756 | PCERT_CHAIN_POLICY_STATUS pPolicyStatus);
|
---|
1757 |
|
---|
1758 | BOOL WINAPI CertVerifyCertificateChainPolicy(LPCSTR szPolicyOID,
|
---|
1759 | PCCERT_CHAIN_CONTEXT pChainContext, PCERT_CHAIN_POLICY_PARA pPolicyPara,
|
---|
1760 | PCERT_CHAIN_POLICY_STATUS pPolicyStatus)
|
---|
1761 | {
|
---|
1762 | static HCRYPTOIDFUNCSET set = NULL;
|
---|
1763 | BOOL ret = FALSE;
|
---|
1764 | CertVerifyCertificateChainPolicyFunc verifyPolicy = NULL;
|
---|
1765 | HCRYPTOIDFUNCADDR hFunc = NULL;
|
---|
1766 |
|
---|
1767 | TRACE("(%s, %p, %p, %p)\n", debugstr_a(szPolicyOID), pChainContext,
|
---|
1768 | pPolicyPara, pPolicyStatus);
|
---|
1769 |
|
---|
1770 | if (!HIWORD(szPolicyOID))
|
---|
1771 | {
|
---|
1772 | switch (LOWORD(szPolicyOID))
|
---|
1773 | {
|
---|
1774 | case LOWORD(CERT_CHAIN_POLICY_BASE):
|
---|
1775 | verifyPolicy = verify_base_policy;
|
---|
1776 | break;
|
---|
1777 | case LOWORD(CERT_CHAIN_POLICY_AUTHENTICODE):
|
---|
1778 | verifyPolicy = verify_authenticode_policy;
|
---|
1779 | break;
|
---|
1780 | case LOWORD(CERT_CHAIN_POLICY_BASIC_CONSTRAINTS):
|
---|
1781 | verifyPolicy = verify_basic_constraints_policy;
|
---|
1782 | break;
|
---|
1783 | case LOWORD(CERT_CHAIN_POLICY_MICROSOFT_ROOT):
|
---|
1784 | verifyPolicy = verify_ms_root_policy;
|
---|
1785 | break;
|
---|
1786 | default:
|
---|
1787 | FIXME("unimplemented for %d\n", LOWORD(szPolicyOID));
|
---|
1788 | }
|
---|
1789 | }
|
---|
1790 | if (!verifyPolicy)
|
---|
1791 | {
|
---|
1792 | if (!set)
|
---|
1793 | set = CryptInitOIDFunctionSet(
|
---|
1794 | CRYPT_OID_VERIFY_CERTIFICATE_CHAIN_POLICY_FUNC, 0);
|
---|
1795 | CryptGetOIDFunctionAddress(set, X509_ASN_ENCODING, szPolicyOID, 0,
|
---|
1796 | (void **)&verifyPolicy, &hFunc);
|
---|
1797 | }
|
---|
1798 | if (verifyPolicy)
|
---|
1799 | ret = verifyPolicy(szPolicyOID, pChainContext, pPolicyPara,
|
---|
1800 | pPolicyStatus);
|
---|
1801 | if (hFunc)
|
---|
1802 | CryptFreeOIDFunctionAddress(hFunc, 0);
|
---|
1803 | return ret;
|
---|
1804 | }
|
---|