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 #define NONAMELESSUNION
21 #include "windef.h"
22 #include "winbase.h"
23 #define CERT_CHAIN_PARA_HAS_EXTRA_FIELDS
24 #define CERT_REVOCATION_PARA_HAS_EXTRA_FIELDS
25 #include "wincrypt.h"
26 #include "wine/debug.h"
27 #include "wine/unicode.h"
28 #include "crypt32_private.h"
29
30 WINE_DEFAULT_DEBUG_CHANNEL(crypt);
31
32 #define DEFAULT_CYCLE_MODULUS 7
33
34 static HCERTCHAINENGINE CRYPT_defaultChainEngine;
35
36 /* This represents a subset of a certificate chain engine: it doesn't include
37 * the "hOther" store described by MSDN, because I'm not sure how that's used.
38 * It also doesn't include the "hTrust" store, because I don't yet implement
39 * CTLs or complex certificate chains.
40 */
41 typedef struct _CertificateChainEngine
42 {
43 LONG ref;
44 HCERTSTORE hRoot;
45 HCERTSTORE hWorld;
46 DWORD dwFlags;
47 DWORD dwUrlRetrievalTimeout;
48 DWORD MaximumCachedCertificates;
49 DWORD CycleDetectionModulus;
50 } CertificateChainEngine, *PCertificateChainEngine;
51
52 static inline void CRYPT_AddStoresToCollection(HCERTSTORE collection,
53 DWORD cStores, HCERTSTORE *stores)
54 {
55 DWORD i;
56
57 for (i = 0; i < cStores; i++)
58 CertAddStoreToCollection(collection, stores[i], 0, 0);
59 }
60
61 static inline void CRYPT_CloseStores(DWORD cStores, HCERTSTORE *stores)
62 {
63 DWORD i;
64
65 for (i = 0; i < cStores; i++)
66 CertCloseStore(stores[i], 0);
67 }
68
69 static const WCHAR rootW[] = { 'R','o','o','t',0 };
70
71 static BOOL CRYPT_CheckRestrictedRoot(HCERTSTORE store)
72 {
73 BOOL ret = TRUE;
74
75 if (store)
76 {
77 HCERTSTORE rootStore = CertOpenSystemStoreW(0, rootW);
78 PCCERT_CONTEXT cert = NULL, check;
79 BYTE hash[20];
80 DWORD size;
81
82 do {
83 cert = CertEnumCertificatesInStore(store, cert);
84 if (cert)
85 {
86 size = sizeof(hash);
87
88 ret = CertGetCertificateContextProperty(cert, CERT_HASH_PROP_ID,
89 hash, &size);
90 if (ret)
91 {
92 CRYPT_HASH_BLOB blob = { sizeof(hash), hash };
93
94 check = CertFindCertificateInStore(rootStore,
95 cert->dwCertEncodingType, 0, CERT_FIND_SHA1_HASH, &blob,
96 NULL);
97 if (!check)
98 ret = FALSE;
99 else
100 CertFreeCertificateContext(check);
101 }
102 }
103 } while (ret && cert);
104 if (cert)
105 CertFreeCertificateContext(cert);
106 CertCloseStore(rootStore, 0);
107 }
108 return ret;
109 }
110
111 HCERTCHAINENGINE CRYPT_CreateChainEngine(HCERTSTORE root,
112 PCERT_CHAIN_ENGINE_CONFIG pConfig)
113 {
114 static const WCHAR caW[] = { 'C','A',0 };
115 static const WCHAR myW[] = { 'M','y',0 };
116 static const WCHAR trustW[] = { 'T','r','u','s','t',0 };
117 PCertificateChainEngine engine =
118 CryptMemAlloc(sizeof(CertificateChainEngine));
119
120 if (engine)
121 {
122 HCERTSTORE worldStores[4];
123
124 engine->ref = 1;
125 engine->hRoot = root;
126 engine->hWorld = CertOpenStore(CERT_STORE_PROV_COLLECTION, 0, 0,
127 CERT_STORE_CREATE_NEW_FLAG, NULL);
128 worldStores[0] = CertDuplicateStore(engine->hRoot);
129 worldStores[1] = CertOpenSystemStoreW(0, caW);
130 worldStores[2] = CertOpenSystemStoreW(0, myW);
131 worldStores[3] = CertOpenSystemStoreW(0, trustW);
132 CRYPT_AddStoresToCollection(engine->hWorld,
133 sizeof(worldStores) / sizeof(worldStores[0]), worldStores);
134 CRYPT_AddStoresToCollection(engine->hWorld,
135 pConfig->cAdditionalStore, pConfig->rghAdditionalStore);
136 CRYPT_CloseStores(sizeof(worldStores) / sizeof(worldStores[0]),
137 worldStores);
138 engine->dwFlags = pConfig->dwFlags;
139 engine->dwUrlRetrievalTimeout = pConfig->dwUrlRetrievalTimeout;
140 engine->MaximumCachedCertificates =
141 pConfig->MaximumCachedCertificates;
142 if (pConfig->CycleDetectionModulus)
143 engine->CycleDetectionModulus = pConfig->CycleDetectionModulus;
144 else
145 engine->CycleDetectionModulus = DEFAULT_CYCLE_MODULUS;
146 }
147 return (HCERTCHAINENGINE)engine;
148 }
149
150 BOOL WINAPI CertCreateCertificateChainEngine(PCERT_CHAIN_ENGINE_CONFIG pConfig,
151 HCERTCHAINENGINE *phChainEngine)
152 {
153 BOOL ret;
154
155 TRACE("(%p, %p)\n", pConfig, phChainEngine);
156
157 if (pConfig->cbSize != sizeof(*pConfig))
158 {
159 SetLastError(E_INVALIDARG);
160 return FALSE;
161 }
162 *phChainEngine = NULL;
163 ret = CRYPT_CheckRestrictedRoot(pConfig->hRestrictedRoot);
164 if (ret)
165 {
166 HCERTSTORE root;
167 HCERTCHAINENGINE engine;
168
169 if (pConfig->hRestrictedRoot)
170 root = CertDuplicateStore(pConfig->hRestrictedRoot);
171 else
172 root = CertOpenSystemStoreW(0, rootW);
173 engine = CRYPT_CreateChainEngine(root, pConfig);
174 if (engine)
175 {
176 *phChainEngine = engine;
177 ret = TRUE;
178 }
179 else
180 ret = FALSE;
181 }
182 return ret;
183 }
184
185 VOID WINAPI CertFreeCertificateChainEngine(HCERTCHAINENGINE hChainEngine)
186 {
187 PCertificateChainEngine engine = (PCertificateChainEngine)hChainEngine;
188
189 TRACE("(%p)\n", hChainEngine);
190
191 if (engine && InterlockedDecrement(&engine->ref) == 0)
192 {
193 CertCloseStore(engine->hWorld, 0);
194 CertCloseStore(engine->hRoot, 0);
195 CryptMemFree(engine);
196 }
197 }
198
199 static HCERTCHAINENGINE CRYPT_GetDefaultChainEngine(void)
200 {
201 if (!CRYPT_defaultChainEngine)
202 {
203 CERT_CHAIN_ENGINE_CONFIG config = { 0 };
204 HCERTCHAINENGINE engine;
205
206 config.cbSize = sizeof(config);
207 CertCreateCertificateChainEngine(&config, &engine);
208 InterlockedCompareExchangePointer(&CRYPT_defaultChainEngine, engine,
209 NULL);
210 if (CRYPT_defaultChainEngine != engine)
211 CertFreeCertificateChainEngine(engine);
212 }
213 return CRYPT_defaultChainEngine;
214 }
215
216 void default_chain_engine_free(void)
217 {
218 CertFreeCertificateChainEngine(CRYPT_defaultChainEngine);
219 }
220
221 typedef struct _CertificateChain
222 {
223 CERT_CHAIN_CONTEXT context;
224 HCERTSTORE world;
225 LONG ref;
226 } CertificateChain, *PCertificateChain;
227
228 static inline BOOL CRYPT_IsCertificateSelfSigned(PCCERT_CONTEXT cert)
229 {
230 return CertCompareCertificateName(cert->dwCertEncodingType,
231 &cert->pCertInfo->Subject, &cert->pCertInfo->Issuer);
232 }
233
234 static void CRYPT_FreeChainElement(PCERT_CHAIN_ELEMENT element)
235 {
236 CertFreeCertificateContext(element->pCertContext);
237 CryptMemFree(element);
238 }
239
240 static void CRYPT_CheckSimpleChainForCycles(PCERT_SIMPLE_CHAIN chain)
241 {
242 DWORD i, j, cyclicCertIndex = 0;
243
244 /* O(n^2) - I don't think there's a faster way */
245 for (i = 0; !cyclicCertIndex && i < chain->cElement; i++)
246 for (j = i + 1; !cyclicCertIndex && j < chain->cElement; j++)
247 if (CertCompareCertificate(X509_ASN_ENCODING,
248 chain->rgpElement[i]->pCertContext->pCertInfo,
249 chain->rgpElement[j]->pCertContext->pCertInfo))
250 cyclicCertIndex = j;
251 if (cyclicCertIndex)
252 {
253 chain->rgpElement[cyclicCertIndex]->TrustStatus.dwErrorStatus
254 |= CERT_TRUST_IS_CYCLIC;
255 /* Release remaining certs */
256 for (i = cyclicCertIndex + 1; i < chain->cElement; i++)
257 CRYPT_FreeChainElement(chain->rgpElement[i]);
258 /* Truncate chain */
259 chain->cElement = cyclicCertIndex + 1;
260 }
261 }
262
263 /* Checks whether the chain is cyclic by examining the last element's status */
264 static inline BOOL CRYPT_IsSimpleChainCyclic(PCERT_SIMPLE_CHAIN chain)
265 {
266 if (chain->cElement)
267 return chain->rgpElement[chain->cElement - 1]->TrustStatus.dwErrorStatus
268 & CERT_TRUST_IS_CYCLIC;
269 else
270 return FALSE;
271 }
272
273 static inline void CRYPT_CombineTrustStatus(CERT_TRUST_STATUS *chainStatus,
274 CERT_TRUST_STATUS *elementStatus)
275 {
276 /* Any error that applies to an element also applies to a chain.. */
277 chainStatus->dwErrorStatus |= elementStatus->dwErrorStatus;
278 /* but the bottom nibble of an element's info status doesn't apply to the
279 * chain.
280 */
281 chainStatus->dwInfoStatus |= (elementStatus->dwInfoStatus & 0xfffffff0);
282 }
283
284 static BOOL CRYPT_AddCertToSimpleChain(PCertificateChainEngine engine,
285 PCERT_SIMPLE_CHAIN chain, PCCERT_CONTEXT cert, DWORD subjectInfoStatus)
286 {
287 BOOL ret = FALSE;
288 PCERT_CHAIN_ELEMENT element = CryptMemAlloc(sizeof(CERT_CHAIN_ELEMENT));
289
290 if (element)
291 {
292 if (!chain->cElement)
293 chain->rgpElement = CryptMemAlloc(sizeof(PCERT_CHAIN_ELEMENT));
294 else
295 chain->rgpElement = CryptMemRealloc(chain->rgpElement,
296 (chain->cElement + 1) * sizeof(PCERT_CHAIN_ELEMENT));
297 if (chain->rgpElement)
298 {
299 chain->rgpElement[chain->cElement++] = element;
300 memset(element, 0, sizeof(CERT_CHAIN_ELEMENT));
301 element->cbSize = sizeof(CERT_CHAIN_ELEMENT);
302 element->pCertContext = CertDuplicateCertificateContext(cert);
303 if (chain->cElement > 1)
304 chain->rgpElement[chain->cElement - 2]->TrustStatus.dwInfoStatus
305 = subjectInfoStatus;
306 /* FIXME: initialize the rest of element */
307 if (chain->cElement % engine->CycleDetectionModulus)
308 CRYPT_CheckSimpleChainForCycles(chain);
309 CRYPT_CombineTrustStatus(&chain->TrustStatus,
310 &element->TrustStatus);
311 ret = TRUE;
312 }
313 else
314 CryptMemFree(element);
315 }
316 return ret;
317 }
318
319 static void CRYPT_FreeSimpleChain(PCERT_SIMPLE_CHAIN chain)
320 {
321 DWORD i;
322
323 for (i = 0; i < chain->cElement; i++)
324 CRYPT_FreeChainElement(chain->rgpElement[i]);
325 CryptMemFree(chain->rgpElement);
326 CryptMemFree(chain);
327 }
328
329 static void CRYPT_CheckTrustedStatus(HCERTSTORE hRoot,
330 PCERT_CHAIN_ELEMENT rootElement)
331 {
332 BYTE hash[20];
333 DWORD size = sizeof(hash);
334 CRYPT_HASH_BLOB blob = { sizeof(hash), hash };
335 PCCERT_CONTEXT trustedRoot;
336
337 CertGetCertificateContextProperty(rootElement->pCertContext,
338 CERT_HASH_PROP_ID, hash, &size);
339 trustedRoot = CertFindCertificateInStore(hRoot,
340 rootElement->pCertContext->dwCertEncodingType, 0, CERT_FIND_SHA1_HASH,
341 &blob, NULL);
342 if (!trustedRoot)
343 rootElement->TrustStatus.dwErrorStatus |=
344 CERT_TRUST_IS_UNTRUSTED_ROOT;
345 else
346 CertFreeCertificateContext(trustedRoot);
347 }
348
349 static void CRYPT_CheckRootCert(HCERTCHAINENGINE hRoot,
350 PCERT_CHAIN_ELEMENT rootElement)
351 {
352 PCCERT_CONTEXT root = rootElement->pCertContext;
353
354 if (!CryptVerifyCertificateSignatureEx(0, root->dwCertEncodingType,
355 CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT, (void *)root,
356 CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT, (void *)root, 0, NULL))
357 {
358 TRACE("Last certificate's signature is invalid\n");
359 rootElement->TrustStatus.dwErrorStatus |=
360 CERT_TRUST_IS_NOT_SIGNATURE_VALID;
361 }
362 CRYPT_CheckTrustedStatus(hRoot, rootElement);
363 }
364
365 /* Decodes a cert's basic constraints extension (either szOID_BASIC_CONSTRAINTS
366 * or szOID_BASIC_CONSTRAINTS2, whichever is present) into a
367 * CERT_BASIC_CONSTRAINTS2_INFO. If it neither extension is present, sets
368 * constraints->fCA to defaultIfNotSpecified.
369 * Returns FALSE if the extension is present but couldn't be decoded.
370 */
371 static BOOL CRYPT_DecodeBasicConstraints(PCCERT_CONTEXT cert,
372 CERT_BASIC_CONSTRAINTS2_INFO *constraints, BOOL defaultIfNotSpecified)
373 {
374 BOOL ret = TRUE;
375 PCERT_EXTENSION ext = CertFindExtension(szOID_BASIC_CONSTRAINTS,
376 cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension);
377
378 constraints->fPathLenConstraint = FALSE;
379 if (ext)
380 {
381 CERT_BASIC_CONSTRAINTS_INFO *info;
382 DWORD size = 0;
383
384 ret = CryptDecodeObjectEx(X509_ASN_ENCODING, szOID_BASIC_CONSTRAINTS,
385 ext->Value.pbData, ext->Value.cbData, CRYPT_DECODE_ALLOC_FLAG,
386 NULL, (LPBYTE)&info, &size);
387 if (ret)
388 {
389 if (info->SubjectType.cbData == 1)
390 constraints->fCA =
391 info->SubjectType.pbData[0] & CERT_CA_SUBJECT_FLAG;
392 LocalFree(info);
393 }
394 }
395 else
396 {
397 ext = CertFindExtension(szOID_BASIC_CONSTRAINTS2,
398 cert->pCertInfo->cExtension, cert->pCertInfo->rgExtension);
399 if (ext)
400 {
401 DWORD size = sizeof(CERT_BASIC_CONSTRAINTS2_INFO);
402
403 ret = CryptDecodeObjectEx(X509_ASN_ENCODING,
404 szOID_BASIC_CONSTRAINTS2, ext->Value.pbData, ext->Value.cbData,
405 0, NULL, constraints, &size);
406 }
407 else
408 constraints->fCA = defaultIfNotSpecified;
409 }
410 return ret;
411 }
412
413 /* Checks element's basic constraints to see if it can act as a CA, with
414 * remainingCAs CAs left in this chain. Updates chainConstraints with the
415 * element's constraints, if:
416 * 1. chainConstraints doesn't have a path length constraint, or
417 * 2. element's path length constraint is smaller than chainConstraints's
418 * Sets *pathLengthConstraintViolated to TRUE if a path length violation
419 * occurs.
420 * Returns TRUE if the element can be a CA, and the length of the remaining
421 * chain is valid.
422 */
423 static BOOL CRYPT_CheckBasicConstraintsForCA(PCCERT_CONTEXT cert,
424 CERT_BASIC_CONSTRAINTS2_INFO *chainConstraints, DWORD remainingCAs,
425 BOOL *pathLengthConstraintViolated)
426 {
427 BOOL validBasicConstraints;
428 CERT_BASIC_CONSTRAINTS2_INFO constraints;
429
430 if ((validBasicConstraints = CRYPT_DecodeBasicConstraints(cert,
431 &constraints, TRUE)))
432 {
433 if (!constraints.fCA)
434 {
435 TRACE("chain element %d can't be a CA\n", remainingCAs + 1);
436 validBasicConstraints = FALSE;
437 }
438 else if (constraints.fPathLenConstraint)
439 {
440 /* If the element has path length constraints, they apply to the
441 * entire remaining chain.
442 */
443 if (!chainConstraints->fPathLenConstraint ||
444 constraints.dwPathLenConstraint <
445 chainConstraints->dwPathLenConstraint)
446 {
447 TRACE("setting path length constraint to %d\n",
448 chainConstraints->dwPathLenConstraint);
449 chainConstraints->fPathLenConstraint = TRUE;
450 chainConstraints->dwPathLenConstraint =
451 constraints.dwPathLenConstraint;
452 }
453 }
454 }
455 if (chainConstraints->fPathLenConstraint &&
456 remainingCAs > chainConstraints->dwPathLenConstraint)
457 {
458 TRACE("remaining CAs %d exceed max path length %d\n", remainingCAs,
459 chainConstraints->dwPathLenConstraint);
460 validBasicConstraints = FALSE;
461 *pathLengthConstraintViolated = TRUE;
462 }
463 return validBasicConstraints;
464 }
465
466 static BOOL url_matches(LPCWSTR constraint, LPCWSTR name,
467 DWORD *trustErrorStatus)
468 {
469 BOOL match = FALSE;
470
471 TRACE("%s, %s\n", debugstr_w(constraint), debugstr_w(name));
472
473 if (!constraint)
474 *trustErrorStatus |= CERT_TRUST_INVALID_NAME_CONSTRAINTS;
475 else if (!name)
476 ; /* no match */
477 else if (constraint[0] == '.')
478 {
479 if (lstrlenW(name) > lstrlenW(constraint))
480 match = !lstrcmpiW(name + lstrlenW(name) - lstrlenW(constraint),
481 constraint);
482 }
483 else
484 match = !lstrcmpiW(constraint, name);
485 return match;
486 }
487
488 static BOOL rfc822_name_matches(LPCWSTR constraint, LPCWSTR name,
489 DWORD *trustErrorStatus)
490 {
491 BOOL match = FALSE;
492 LPCWSTR at;
493
494 TRACE("%s, %s\n", debugstr_w(constraint), debugstr_w(name));
495
496 if (!constraint)
497 *trustErrorStatus |= CERT_TRUST_INVALID_NAME_CONSTRAINTS;
498 else if (!name)
499 ; /* no match */
500 else if ((at = strchrW(constraint, '@')))
501 match = !lstrcmpiW(constraint, name);
502 else
503 {
504 if ((at = strchrW(name, '@')))
505 match = url_matches(constraint, at + 1, trustErrorStatus);
506 else
507 match = !lstrcmpiW(constraint, name);
508 }
509 return match;
510 }
511
512 static BOOL dns_name_matches(LPCWSTR constraint, LPCWSTR name,
513 DWORD *trustErrorStatus)
514 {
515 BOOL match = FALSE;
516
517 TRACE("%s, %s\n", debugstr_w(constraint), debugstr_w(name));
518
519 if (!constraint)
520 *trustErrorStatus |= CERT_TRUST_INVALID_NAME_CONSTRAINTS;
521 else if (!name)
522 ; /* no match */
523 else if (lstrlenW(name) >= lstrlenW(constraint))
524 match = !lstrcmpiW(name + lstrlenW(name) - lstrlenW(constraint),
525 constraint);
526 /* else: name is too short, no match */
527
528 return match;
529 }
530
531 static BOOL ip_address_matches(const CRYPT_DATA_BLOB *constraint,
532 const CRYPT_DATA_BLOB *name, DWORD *trustErrorStatus)
533 {
534 BOOL match = FALSE;
535
536 TRACE("(%d, %p), (%d, %p)\n", constraint->cbData, constraint->pbData,
537 name->cbData, name->pbData);
538
539 if (constraint->cbData != sizeof(DWORD) * 2)
540 *trustErrorStatus |= CERT_TRUST_INVALID_NAME_CONSTRAINTS;
541 else if (name->cbData == sizeof(DWORD))
542 {
543 DWORD subnet, mask, addr;
544
545 memcpy(&subnet, constraint->pbData, sizeof(subnet));
546 memcpy(&mask, constraint->pbData + sizeof(subnet), sizeof(mask));
547 memcpy(&addr, name->pbData, sizeof(addr));
548 /* These are really in big-endian order, but for equality matching we
549 * don't need to swap to host order
550 */
551 match = (subnet & mask) == (addr & mask);
552 }
553 /* else: name is wrong size, no match */
554
555 return match;
556 }
557
558 static void CRYPT_FindMatchingNameEntry(const CERT_ALT_NAME_ENTRY *constraint,
559 const CERT_ALT_NAME_INFO *subjectName, DWORD *trustErrorStatus,
560 DWORD errorIfFound, DWORD errorIfNotFound)
561 {
562 DWORD i;
563 BOOL defined = FALSE, match = FALSE;
564
565 for (i = 0; i < subjectName->cAltEntry; i++)
566 {
567 if (subjectName->rgAltEntry[i].dwAltNameChoice ==
568 constraint->dwAltNameChoice)
569 {
570 defined = TRUE;
571 switch (constraint->dwAltNameChoice)
572 {
573 case CERT_ALT_NAME_RFC822_NAME:
574 match = rfc822_name_matches(constraint->u.pwszURL,
575 subjectName->rgAltEntry[i].u.pwszURL, trustErrorStatus);
576 break;
577 case CERT_ALT_NAME_DNS_NAME:
578 match = dns_name_matches(constraint->u.pwszURL,
579 subjectName->rgAltEntry[i].u.pwszURL, trustErrorStatus);
580 break;
581 case CERT_ALT_NAME_URL:
582 match = url_matches(constraint->u.pwszURL,
583 subjectName->rgAltEntry[i].u.pwszURL, trustErrorStatus);
584 break;
585 case CERT_ALT_NAME_IP_ADDRESS:
586 match = ip_address_matches(&constraint->u.IPAddress,
587 &subjectName->rgAltEntry[i].u.IPAddress, trustErrorStatus);
588 break;
589 case CERT_ALT_NAME_DIRECTORY_NAME:
590 default:
591 ERR("name choice %d unsupported in this context\n",
592 constraint->dwAltNameChoice);
593 *trustErrorStatus |=
594 CERT_TRUST_HAS_NOT_SUPPORTED_NAME_CONSTRAINT;
595 }
596 }
597 }
598 /* Microsoft's implementation of name constraint checking appears at odds
599 * with RFC 3280:
600 * According to MSDN, CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT is set
601 * when a name constraint is present, but that name form is not defined in
602 * the end certificate. According to RFC 3280, "if no name of the type is
603 * in the certificate, the name is acceptable."
604 * I follow Microsoft here.
605 */
606 if (!defined)
607 *trustErrorStatus |= CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT;
608 *trustErrorStatus |= match ? errorIfFound : errorIfNotFound;
609 }
610
611 static void CRYPT_CheckNameConstraints(
612 const CERT_NAME_CONSTRAINTS_INFO *nameConstraints, const CERT_INFO *cert,
613 DWORD *trustErrorStatus)
614 {
615 /* If there aren't any existing constraints, don't bother checking */
616 if (nameConstraints->cPermittedSubtree || nameConstraints->cExcludedSubtree)
617 {
618 CERT_EXTENSION *ext;
619
620 if ((ext = CertFindExtension(szOID_SUBJECT_ALT_NAME, cert->cExtension,
621 cert->rgExtension)))
622 {
623 CERT_ALT_NAME_INFO *subjectName;
624 DWORD size;
625
626 if (CryptDecodeObjectEx(X509_ASN_ENCODING, X509_ALTERNATE_NAME,
627 ext->Value.pbData, ext->Value.cbData,
628 CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL,
629 &subjectName, &size))
630 {
631 DWORD i;
632
633 for (i = 0; i < nameConstraints->cExcludedSubtree; i++)
634 CRYPT_FindMatchingNameEntry(
635 &nameConstraints->rgExcludedSubtree[i].Base, subjectName,
636 trustErrorStatus,
637 CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT, 0);
638 for (i = 0; i < nameConstraints->cPermittedSubtree; i++)
639 CRYPT_FindMatchingNameEntry(
640 &nameConstraints->rgPermittedSubtree[i].Base, subjectName,
641 trustErrorStatus,
642 0, CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT);
643 LocalFree(subjectName);
644 }
645 }
646 else
647 {
648 /* See above comment on CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT.
649 * I match Microsoft's implementation here as well.
650 */
651 *trustErrorStatus |= CERT_TRUST_HAS_NOT_DEFINED_NAME_CONSTRAINT;
652 if (nameConstraints->cPermittedSubtree)
653 *trustErrorStatus |=
654 CERT_TRUST_HAS_NOT_PERMITTED_NAME_CONSTRAINT;
655 if (nameConstraints->cExcludedSubtree)
656 *trustErrorStatus |=
657 CERT_TRUST_HAS_EXCLUDED_NAME_CONSTRAINT;
658 }
659 }
660 }
661
662 /* Gets cert's name constraints, if any. Free with LocalFree. */
663 static CERT_NAME_CONSTRAINTS_INFO *CRYPT_GetNameConstraints(CERT_INFO *cert)
664 {
665 CERT_NAME_CONSTRAINTS_INFO *info = NULL;
666
667 CERT_EXTENSION *ext;
668
669 if ((ext = CertFindExtension(szOID_NAME_CONSTRAINTS, cert->cExtension,
670 cert->rgExtension)))
671 {
672 DWORD size;
673
674 CryptDecodeObjectEx(X509_ASN_ENCODING, X509_NAME_CONSTRAINTS,
675 ext->Value.pbData, ext->Value.cbData,
676 CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL, &info,
677 &size);
678 }
679 return info;
680 }
681
682 static void CRYPT_CheckChainNameConstraints(PCERT_SIMPLE_CHAIN chain)
683 {
684 int i, j;
685
686 /* Microsoft's implementation appears to violate RFC 3280: according to
687 * MSDN, the various CERT_TRUST_*_NAME_CONSTRAINT errors are set if a CA's
688 * name constraint is violated in the end cert. According to RFC 3280,
689 * the constraints should be checked against every subsequent certificate
690 * in the chain, not just the end cert.
691 * Microsoft's implementation also sets the name constraint errors on the
692 * certs whose constraints were violated, not on the certs that violated
693 * them.
694 * In order to be error-compatible with Microsoft's implementation, while
695 * still adhering to RFC 3280, I use a O(n ^ 2) algorithm to check name
696 * constraints.
697 */
698 for (i = chain->cElement - 1; i > 0; i--)
699 {
700 CERT_NAME_CONSTRAINTS_INFO *nameConstraints;
701
702 if ((nameConstraints = CRYPT_GetNameConstraints(
703 chain->rgpElement[i]->pCertContext->pCertInfo)))
704 {
705 for (j = i - 1; j >= 0; j--)
706 {
707 DWORD errorStatus = 0;
708
709 /* According to RFC 3280, self-signed certs don't have name
710 * constraints checked unless they're the end cert.
711 */
712 if (j == 0 || !CRYPT_IsCertificateSelfSigned(
713 chain->rgpElement[j]->pCertContext))
714 {
715 CRYPT_CheckNameConstraints(nameConstraints,
716 chain->rgpElement[i]->pCertContext->pCertInfo,
717 &errorStatus);
718 chain->rgpElement[i]->TrustStatus.dwErrorStatus |=
719 errorStatus;
720 }
721 }
722 LocalFree(nameConstraints);
723 }
724 }
725 }
726
727 static void CRYPT_CheckSimpleChain(PCertificateChainEngine engine,
728 PCERT_SIMPLE_CHAIN chain, LPFILETIME time)
729 {
730 PCERT_CHAIN_ELEMENT rootElement = chain->rgpElement[chain->cElement - 1];
731 int i;
732 BOOL pathLengthConstraintViolated = FALSE;
733 CERT_BASIC_CONSTRAINTS2_INFO constraints = { TRUE, FALSE, 0 };
734
735 for (i = chain->cElement - 1; i >= 0; i--)
736 {
737 if (CertVerifyTimeValidity(time,
738 chain->rgpElement[i]->pCertContext->pCertInfo) != 0)
739 chain->rgpElement[i]->TrustStatus.dwErrorStatus |=
740 CERT_TRUST_IS_NOT_TIME_VALID;
741 if (i != 0)
742 {
743 /* Check the signature of the cert this issued */
744 if (!CryptVerifyCertificateSignatureEx(0, X509_ASN_ENCODING,
745 CRYPT_VERIFY_CERT_SIGN_SUBJECT_CERT,
746 (void *)chain->rgpElement[i - 1]->pCertContext,
747 CRYPT_VERIFY_CERT_SIGN_ISSUER_CERT,
748 (void *)chain->rgpElement[i]->pCertContext, 0, NULL))
749 chain->rgpElement[i - 1]->TrustStatus.dwErrorStatus |=
750 CERT_TRUST_IS_NOT_SIGNATURE_VALID;
751 /* Once a path length constraint has been violated, every remaining
752 * CA cert's basic constraints is considered invalid.
753 */
754 if (pathLengthConstraintViolated)
755 chain->rgpElement[i]->TrustStatus.dwErrorStatus |=
756 CERT_TRUST_INVALID_BASIC_CONSTRAINTS;
757 else if (!CRYPT_CheckBasicConstraintsForCA(
758 chain->rgpElement[i]->pCertContext, &constraints, i - 1,
759 &pathLengthConstraintViolated))
760 chain->rgpElement[i]->TrustStatus.dwErrorStatus |=
761 CERT_TRUST_INVALID_BASIC_CONSTRAINTS;
762 else if (constraints.fPathLenConstraint &&
763 constraints.dwPathLenConstraint)
764 {
765 /* This one's valid - decrement max length */
766 constraints.dwPathLenConstraint--;
767 }
768 }
769 /* FIXME: check valid usages */
770 CRYPT_CombineTrustStatus(&chain->TrustStatus,
771 &chain->rgpElement[i]->TrustStatus);
772 }
773 CRYPT_CheckChainNameConstraints(chain);
774 if (CRYPT_IsCertificateSelfSigned(rootElement->pCertContext))
775 {
776 rootElement->TrustStatus.dwInfoStatus |=
777 CERT_TRUST_IS_SELF_SIGNED | CERT_TRUST_HAS_NAME_MATCH_ISSUER;
778 CRYPT_CheckRootCert(engine->hRoot, rootElement);
779 }
780 CRYPT_CombineTrustStatus(&chain->TrustStatus, &rootElement->TrustStatus);
781 }
782
783 static PCCERT_CONTEXT CRYPT_GetIssuer(HCERTSTORE store, PCCERT_CONTEXT subject,
784 PCCERT_CONTEXT prevIssuer, DWORD *infoStatus)
785 {
786 PCCERT_CONTEXT issuer = NULL;
787 PCERT_EXTENSION ext;
788 DWORD size;
789
790 *infoStatus = 0;
791 if ((ext = CertFindExtension(szOID_AUTHORITY_KEY_IDENTIFIER,
792 subject->pCertInfo->cExtension, subject->pCertInfo->rgExtension)))
793 {
794 CERT_AUTHORITY_KEY_ID_INFO *info;
795 BOOL ret;
796
797 ret = CryptDecodeObjectEx(subject->dwCertEncodingType,
798 X509_AUTHORITY_KEY_ID, ext->Value.pbData, ext->Value.cbData,
799 CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL,
800 &info, &size);
801 if (ret)
802 {
803 CERT_ID id;
804
805 if (info->CertIssuer.cbData && info->CertSerialNumber.cbData)
806 {
807 id.dwIdChoice = CERT_ID_ISSUER_SERIAL_NUMBER;
808 memcpy(&id.u.IssuerSerialNumber.Issuer, &info->CertIssuer,
809 sizeof(CERT_NAME_BLOB));
810 memcpy(&id.u.IssuerSerialNumber.SerialNumber,
811 &info->CertSerialNumber, sizeof(CRYPT_INTEGER_BLOB));
812 issuer = CertFindCertificateInStore(store,
813 subject->dwCertEncodingType, 0, CERT_FIND_CERT_ID, &id,
814 prevIssuer);
815 if (issuer)
816 *infoStatus = CERT_TRUST_HAS_EXACT_MATCH_ISSUER;
817 }
818 else if (info->KeyId.cbData)
819 {
820 id.dwIdChoice = CERT_ID_KEY_IDENTIFIER;
821 memcpy(&id.u.KeyId, &info->KeyId, sizeof(CRYPT_HASH_BLOB));
822 issuer = CertFindCertificateInStore(store,
823 subject->dwCertEncodingType, 0, CERT_FIND_CERT_ID, &id,
824 prevIssuer);
825 if (issuer)
826 *infoStatus = CERT_TRUST_HAS_KEY_MATCH_ISSUER;
827 }
828 LocalFree(info);
829 }
830 }
831 else if ((ext = CertFindExtension(szOID_AUTHORITY_KEY_IDENTIFIER2,
832 subject->pCertInfo->cExtension, subject->pCertInfo->rgExtension)))
833 {
834 CERT_AUTHORITY_KEY_ID2_INFO *info;
835 BOOL ret;
836
837 ret = CryptDecodeObjectEx(subject->dwCertEncodingType,
838 X509_AUTHORITY_KEY_ID2, ext->Value.pbData, ext->Value.cbData,
839 CRYPT_DECODE_ALLOC_FLAG | CRYPT_DECODE_NOCOPY_FLAG, NULL,
840 &info, &size);
841 if (ret)
842 {
843 CERT_ID id;
844
845 if (info->AuthorityCertIssuer.cAltEntry &&
846 info->AuthorityCertSerialNumber.cbData)
847 {
848 PCERT_ALT_NAME_ENTRY directoryName = NULL;
849 DWORD i;
850
851 for (i = 0; !directoryName &&
852 i < info->AuthorityCertIssuer.cAltEntry; i++)
853 if (info->AuthorityCertIssuer.rgAltEntry[i].dwAltNameChoice
854 == CERT_ALT_NAME_DIRECTORY_NAME)
855 directoryName =
856 &info->AuthorityCertIssuer.rgAltEntry[i];
857 if (directoryName)
858 {
859 id.dwIdChoice = CERT_ID_ISSUER_SERIAL_NUMBER;
860 memcpy(&id.u.IssuerSerialNumber.Issuer,
861 &directoryName->u.DirectoryName, sizeof(CERT_NAME_BLOB));
862 memcpy(&id.u.IssuerSerialNumber.SerialNumber,
863 &info->AuthorityCertSerialNumber,
864 sizeof(CRYPT_INTEGER_BLOB));
865 issuer = CertFindCertificateInStore(store,
866 subject->dwCertEncodingType, 0, CERT_FIND_CERT_ID, &id,
867 prevIssuer);
868 if (issuer)
869 *infoStatus = CERT_TRUST_HAS_EXACT_MATCH_ISSUER;
870 }
871 else
872 FIXME("no supported name type in authority key id2\n");
873 }
874 else if (info->KeyId.cbData)
875 {
876 id.dwIdChoice = CERT_ID_KEY_IDENTIFIER;
877 memcpy(&id.u.KeyId, &info->KeyId, sizeof(CRYPT_HASH_BLOB));
878 issuer = CertFindCertificateInStore(store,
879 subject->dwCertEncodingType, 0, CERT_FIND_CERT_ID, &id,
880 prevIssuer);
881 if (issuer)
882 *infoStatus = CERT_TRUST_HAS_KEY_MATCH_ISSUER;
883 }
884 LocalFree(info);
885 }
886 }
887 else
888 {
889 issuer = CertFindCertificateInStore(store,
890 subject->dwCertEncodingType, 0, CERT_FIND_SUBJECT_NAME,
891 &subject->pCertInfo->Issuer, prevIssuer);
892 if (issuer)
893 *infoStatus = CERT_TRUST_HAS_NAME_MATCH_ISSUER;
894 }
895 return issuer;
896 }
897
898 /* Builds a simple chain by finding an issuer for the last cert in the chain,
899 * until reaching a self-signed cert, or until no issuer can be found.
900 */
901 static BOOL CRYPT_BuildSimpleChain(PCertificateChainEngine engine,
902 HCERTSTORE world, PCERT_SIMPLE_CHAIN chain)
903 {
904 BOOL ret = TRUE;
905 PCCERT_CONTEXT cert = chain->rgpElement[chain->cElement - 1]->pCertContext;
906
907 while (ret && !CRYPT_IsSimpleChainCyclic(chain) &&
908 !CRYPT_IsCertificateSelfSigned(cert))
909 {
910 DWORD infoStatus;
911 PCCERT_CONTEXT issuer = CRYPT_GetIssuer(world, cert, NULL, &infoStatus);
912
913 if (issuer)
914 {
915 ret = CRYPT_AddCertToSimpleChain(engine, chain, issuer, infoStatus);
916 /* CRYPT_AddCertToSimpleChain add-ref's the issuer, so free it to
917 * close the enumeration that found it
918 */
919 CertFreeCertificateContext(issuer);
920 cert = issuer;
921 }
922 else
923 {
924 TRACE("Couldn't find issuer, halting chain creation\n");
925 break;
926 }
927 }
928 return ret;
929 }
930
931 static BOOL CRYPT_GetSimpleChainForCert(PCertificateChainEngine engine,
932 HCERTSTORE world, PCCERT_CONTEXT cert, LPFILETIME pTime,
933 PCERT_SIMPLE_CHAIN *ppChain)
934 {
935 BOOL ret = FALSE;
936 PCERT_SIMPLE_CHAIN chain;
937
938 TRACE("(%p, %p, %p, %p)\n", engine, world, cert, pTime);
939
940