VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/crypto/x509-certpaths.cpp@ 84379

Last change on this file since 84379 was 84379, checked in by vboxsync, 5 years ago

IPRT/RTCrX509CertPaths: Hacked the code to accept trusted targets. Added option trust anchor checks. bugref:9699

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 110.9 KB
Line 
1/* $Id: x509-certpaths.cpp 84379 2020-05-19 19:40:54Z vboxsync $ */
2/** @file
3 * IPRT - Crypto - X.509, Simple Certificate Path Builder & Validator.
4 */
5
6/*
7 * Copyright (C) 2006-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*********************************************************************************************************************************
29* Header Files *
30*********************************************************************************************************************************/
31#define LOG_GROUP RTLOGGROUP_CRYPTO
32#include "internal/iprt.h"
33#include <iprt/crypto/x509.h>
34
35#include <iprt/asm.h>
36#include <iprt/ctype.h>
37#include <iprt/err.h>
38#include <iprt/mem.h>
39#include <iprt/string.h>
40#include <iprt/list.h>
41#include <iprt/log.h>
42#include <iprt/time.h>
43#include <iprt/crypto/applecodesign.h> /* critical extension OIDs */
44#include <iprt/crypto/pkcs7.h> /* PCRTCRPKCS7SETOFCERTS */
45#include <iprt/crypto/store.h>
46
47#include "x509-internal.h"
48
49
50/*********************************************************************************************************************************
51* Structures and Typedefs *
52*********************************************************************************************************************************/
53/**
54 * X.509 certificate path node.
55 */
56typedef struct RTCRX509CERTPATHNODE
57{
58 /** Sibling list entry. */
59 RTLISTNODE SiblingEntry;
60 /** List of children or leaf list entry. */
61 RTLISTANCHOR ChildListOrLeafEntry;
62 /** Pointer to the parent node. NULL for root. */
63 struct RTCRX509CERTPATHNODE *pParent;
64
65 /** The distance between this node and the target. */
66 uint32_t uDepth : 8;
67 /** Indicates the source of this certificate. */
68 uint32_t uSrc : 3;
69 /** Set if this is a leaf node. */
70 uint32_t fLeaf : 1;
71 /** Makes sure it's a 32-bit bitfield. */
72 uint32_t uReserved : 20;
73
74 /** Leaf only: The result of the last path vertification. */
75 int rcVerify;
76
77 /** Pointer to the certificate. This can be NULL only for trust anchors. */
78 PCRTCRX509CERTIFICATE pCert;
79
80 /** If the certificate or trust anchor was obtained from a store, this is the
81 * associated certificate context (referenced of course). This is used to
82 * access the trust anchor information, if present.
83 *
84 * (If this is NULL it's from a certificate array or some such given directly to
85 * the path building code. It's assumed the caller doesn't free these until the
86 * path validation/whatever is done with and the paths destroyed.) */
87 PCRTCRCERTCTX pCertCtx;
88} RTCRX509CERTPATHNODE;
89/** Pointer to a X.509 path node. */
90typedef RTCRX509CERTPATHNODE *PRTCRX509CERTPATHNODE;
91
92/** @name RTCRX509CERTPATHNODE::uSrc values.
93 * The trusted and untrusted sources ordered in priority order, where higher
94 * number means high priority in case of duplicates.
95 * @{ */
96#define RTCRX509CERTPATHNODE_SRC_NONE 0
97#define RTCRX509CERTPATHNODE_SRC_TARGET 1
98#define RTCRX509CERTPATHNODE_SRC_UNTRUSTED_SET 2
99#define RTCRX509CERTPATHNODE_SRC_UNTRUSTED_ARRAY 3
100#define RTCRX509CERTPATHNODE_SRC_UNTRUSTED_STORE 4
101#define RTCRX509CERTPATHNODE_SRC_TRUSTED_STORE 5
102#define RTCRX509CERTPATHNODE_SRC_TRUSTED_CERT 6
103#define RTCRX509CERTPATHNODE_SRC_IS_TRUSTED(uSrc) ((uSrc) >= RTCRX509CERTPATHNODE_SRC_TRUSTED_STORE)
104/** @} */
105
106
107/**
108 * Policy tree node.
109 */
110typedef struct RTCRX509CERTPATHSPOLICYNODE
111{
112 /** Sibling list entry. */
113 RTLISTNODE SiblingEntry;
114 /** Tree depth list entry. */
115 RTLISTNODE DepthEntry;
116 /** List of children or leaf list entry. */
117 RTLISTANCHOR ChildList;
118 /** Pointer to the parent. */
119 struct RTCRX509CERTPATHSPOLICYNODE *pParent;
120
121 /** The policy object ID. */
122 PCRTASN1OBJID pValidPolicy;
123
124 /** Optional sequence of policy qualifiers. */
125 PCRTCRX509POLICYQUALIFIERINFOS pPolicyQualifiers;
126
127 /** The first policy ID in the exepcted policy set. */
128 PCRTASN1OBJID pExpectedPolicyFirst;
129 /** Set if we've already mapped pExpectedPolicyFirst. */
130 bool fAlreadyMapped;
131 /** Number of additional items in the expected policy set. */
132 uint32_t cMoreExpectedPolicySet;
133 /** Additional items in the expected policy set. */
134 PCRTASN1OBJID *papMoreExpectedPolicySet;
135} RTCRX509CERTPATHSPOLICYNODE;
136/** Pointer to a policy tree node. */
137typedef RTCRX509CERTPATHSPOLICYNODE *PRTCRX509CERTPATHSPOLICYNODE;
138
139
140/**
141 * Path builder and validator instance.
142 *
143 * The path builder creates a tree of certificates by forward searching from the
144 * end-entity towards a trusted source. The leaf nodes are inserted into list
145 * ordered by the source of the leaf certificate and the path length (i.e. tree
146 * depth).
147 *
148 * The path validator works the tree from the leaf end and validates each
149 * potential path found by the builder. It is generally happy with one working
150 * path, but may be told to verify all of them.
151 */
152typedef struct RTCRX509CERTPATHSINT
153{
154 /** Magic number. */
155 uint32_t u32Magic;
156 /** Reference counter. */
157 uint32_t volatile cRefs;
158
159 /** @name Input
160 * @{ */
161 /** The target certificate (end entity) to build a trusted path for. */
162 PCRTCRX509CERTIFICATE pTarget;
163
164 /** Lone trusted certificate. */
165 PCRTCRX509CERTIFICATE pTrustedCert;
166 /** Store of trusted certificates. */
167 RTCRSTORE hTrustedStore;
168
169 /** Store of untrusted certificates. */
170 RTCRSTORE hUntrustedStore;
171 /** Array of untrusted certificates, typically from the protocol. */
172 PCRTCRX509CERTIFICATE paUntrustedCerts;
173 /** Number of entries in paUntrusted. */
174 uint32_t cUntrustedCerts;
175 /** Set of untrusted PKCS \#7 / CMS certificatess. */
176 PCRTCRPKCS7SETOFCERTS pUntrustedCertsSet;
177
178 /** UTC time we're going to validate the path at, requires
179 * RTCRX509CERTPATHSINT_F_VALID_TIME to be set. */
180 RTTIMESPEC ValidTime;
181 /** Number of policy OIDs in the user initial policy set, 0 means anyPolicy. */
182 uint32_t cInitialUserPolicySet;
183 /** The user initial policy set. As with all other user provided data, we
184 * assume it's immutable and remains valid for the usage period of the path
185 * builder & validator. */
186 PCRTASN1OBJID *papInitialUserPolicySet;
187 /** Number of certificates before the user wants an explicit policy result.
188 * Set to UINT32_MAX no explicit policy restriction required by the user. */
189 uint32_t cInitialExplicitPolicy;
190 /** Number of certificates before the user wants policy mapping to be
191 * inhibited. Set to UINT32_MAX if no initial policy mapping inhibition
192 * desired by the user. */
193 uint32_t cInitialPolicyMappingInhibit;
194 /** Number of certificates before the user wants the anyPolicy to be rejected.
195 * Set to UINT32_MAX no explicit policy restriction required by the user. */
196 uint32_t cInitialInhibitAnyPolicy;
197 /** Initial name restriction: Permitted subtrees. */
198 PCRTCRX509GENERALSUBTREES pInitialPermittedSubtrees;
199 /** Initial name restriction: Excluded subtrees. */
200 PCRTCRX509GENERALSUBTREES pInitialExcludedSubtrees;
201
202 /** Flags RTCRX509CERTPATHSINT_F_XXX. */
203 uint32_t fFlags;
204 /** @} */
205
206 /** Sticky status for remembering allocation errors and the like. */
207 int32_t rc;
208 /** Where to store extended error info (optional). */
209 PRTERRINFO pErrInfo;
210
211 /** @name Path Builder Output
212 * @{ */
213 /** Pointer to the root of the tree. This will always be non-NULL after path
214 * building and thus can be reliably used to tell if path building has taken
215 * place or not. */
216 PRTCRX509CERTPATHNODE pRoot;
217 /** List of working leaf tree nodes. */
218 RTLISTANCHOR LeafList;
219 /** The number of paths (leafs). */
220 uint32_t cPaths;
221 /** @} */
222
223 /** Path Validator State. */
224 struct
225 {
226 /** Number of nodes in the certificate path we're validating (aka 'n'). */
227 uint32_t cNodes;
228 /** The current node (0 being the trust anchor). */
229 uint32_t iNode;
230
231 /** The root node of the valid policy tree. */
232 PRTCRX509CERTPATHSPOLICYNODE pValidPolicyTree;
233 /** An array of length cNodes + 1 which tracks all nodes at the given (index)
234 * tree depth via the RTCRX509CERTPATHSPOLICYNODE::DepthEntry member. */
235 PRTLISTANCHOR paValidPolicyDepthLists;
236
237 /** Number of entries in paPermittedSubtrees (name constraints).
238 * If zero, no permitted name constrains currently in effect. */
239 uint32_t cPermittedSubtrees;
240 /** The allocated size of papExcludedSubtrees */
241 uint32_t cPermittedSubtreesAlloc;
242 /** Array of permitted subtrees we've collected so far (name constraints). */
243 PCRTCRX509GENERALSUBTREE *papPermittedSubtrees;
244 /** Set if we end up with an empty set after calculating a name constraints
245 * union. */
246 bool fNoPermittedSubtrees;
247
248 /** Number of entries in paExcludedSubtrees (name constraints).
249 * If zero, no excluded name constrains currently in effect. */
250 uint32_t cExcludedSubtrees;
251 /** Array of excluded subtrees we've collected so far (name constraints). */
252 PCRTCRX509GENERALSUBTREES *papExcludedSubtrees;
253
254 /** Number of non-self-issued certificates to be processed before a non-NULL
255 * paValidPolicyTree is required. */
256 uint32_t cExplicitPolicy;
257 /** Number of non-self-issued certificates to be processed we stop processing
258 * policy mapping extensions. */
259 uint32_t cInhibitPolicyMapping;
260 /** Number of non-self-issued certificates to be processed before a the
261 * anyPolicy is rejected. */
262 uint32_t cInhibitAnyPolicy;
263 /** Number of non-self-issued certificates we're allowed to process. */
264 uint32_t cMaxPathLength;
265
266 /** The working issuer name. */
267 PCRTCRX509NAME pWorkingIssuer;
268 /** The working public key algorithm ID. */
269 PCRTASN1OBJID pWorkingPublicKeyAlgorithm;
270 /** The working public key algorithm parameters. */
271 PCRTASN1DYNTYPE pWorkingPublicKeyParameters;
272 /** A bit string containing the public key. */
273 PCRTASN1BITSTRING pWorkingPublicKey;
274 } v;
275
276 /** An object identifier initialized to anyPolicy. */
277 RTASN1OBJID AnyPolicyObjId;
278
279 /** Temporary scratch space. */
280 char szTmp[1024];
281} RTCRX509CERTPATHSINT;
282typedef RTCRX509CERTPATHSINT *PRTCRX509CERTPATHSINT;
283
284/** Magic value for RTCRX509CERTPATHSINT::u32Magic (Bruce Schneier). */
285#define RTCRX509CERTPATHSINT_MAGIC UINT32_C(0x19630115)
286
287/** @name RTCRX509CERTPATHSINT_F_XXX - Certificate path build flags.
288 * @{ */
289#define RTCRX509CERTPATHSINT_F_VALID_TIME RT_BIT_32(0)
290#define RTCRX509CERTPATHSINT_F_ELIMINATE_UNTRUSTED_PATHS RT_BIT_32(1)
291/** Whether checking the trust anchor signature (if self signed) and
292 * that it is valid at the verification time, also require it to be a CA if not
293 * leaf node. */
294#define RTCRX509CERTPATHSINT_F_CHECK_TRUST_ANCHOR RT_BIT_32(2)
295#define RTCRX509CERTPATHSINT_F_VALID_MASK UINT32_C(0x00000007)
296/** @} */
297
298
299/*********************************************************************************************************************************
300* Internal Functions *
301*********************************************************************************************************************************/
302static void rtCrX509CertPathsDestroyTree(PRTCRX509CERTPATHSINT pThis);
303static void rtCrX509CpvCleanup(PRTCRX509CERTPATHSINT pThis);
304
305
306/** @name Path Builder and Validator Config APIs
307 * @{
308 */
309
310RTDECL(int) RTCrX509CertPathsCreate(PRTCRX509CERTPATHS phCertPaths, PCRTCRX509CERTIFICATE pTarget)
311{
312 AssertPtrReturn(phCertPaths, VERR_INVALID_POINTER);
313
314 PRTCRX509CERTPATHSINT pThis = (PRTCRX509CERTPATHSINT)RTMemAllocZ(sizeof(*pThis));
315 if (pThis)
316 {
317 int rc = RTAsn1ObjId_InitFromString(&pThis->AnyPolicyObjId, RTCRX509_ID_CE_CP_ANY_POLICY_OID, &g_RTAsn1DefaultAllocator);
318 if (RT_SUCCESS(rc))
319 {
320 pThis->u32Magic = RTCRX509CERTPATHSINT_MAGIC;
321 pThis->cRefs = 1;
322 pThis->pTarget = pTarget;
323 pThis->hTrustedStore = NIL_RTCRSTORE;
324 pThis->hUntrustedStore = NIL_RTCRSTORE;
325 pThis->cInitialExplicitPolicy = UINT32_MAX;
326 pThis->cInitialPolicyMappingInhibit = UINT32_MAX;
327 pThis->cInitialInhibitAnyPolicy = UINT32_MAX;
328 pThis->rc = VINF_SUCCESS;
329 RTListInit(&pThis->LeafList);
330 *phCertPaths = pThis;
331 return VINF_SUCCESS;
332 }
333 return rc;
334 }
335 return VERR_NO_MEMORY;
336}
337
338
339RTDECL(uint32_t) RTCrX509CertPathsRetain(RTCRX509CERTPATHS hCertPaths)
340{
341 PRTCRX509CERTPATHSINT pThis = hCertPaths;
342 AssertPtrReturn(pThis, UINT32_MAX);
343
344 uint32_t cRefs = ASMAtomicIncU32(&pThis->cRefs);
345 Assert(cRefs > 0 && cRefs < 64);
346 return cRefs;
347}
348
349
350RTDECL(uint32_t) RTCrX509CertPathsRelease(RTCRX509CERTPATHS hCertPaths)
351{
352 uint32_t cRefs;
353 if (hCertPaths != NIL_RTCRX509CERTPATHS)
354 {
355 PRTCRX509CERTPATHSINT pThis = hCertPaths;
356 AssertPtrReturn(pThis, UINT32_MAX);
357 AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, UINT32_MAX);
358
359 cRefs = ASMAtomicDecU32(&pThis->cRefs);
360 Assert(cRefs < 64);
361 if (!cRefs)
362 {
363 /*
364 * No more references, destroy the whole thing.
365 */
366 ASMAtomicWriteU32(&pThis->u32Magic, ~RTCRX509CERTPATHSINT_MAGIC);
367
368 /* config */
369 pThis->pTarget = NULL; /* Referencing user memory. */
370 pThis->pTrustedCert = NULL; /* Referencing user memory. */
371 RTCrStoreRelease(pThis->hTrustedStore);
372 pThis->hTrustedStore = NIL_RTCRSTORE;
373 RTCrStoreRelease(pThis->hUntrustedStore);
374 pThis->hUntrustedStore = NIL_RTCRSTORE;
375 pThis->paUntrustedCerts = NULL; /* Referencing user memory. */
376 pThis->pUntrustedCertsSet = NULL; /* Referencing user memory. */
377 pThis->papInitialUserPolicySet = NULL; /* Referencing user memory. */
378 pThis->pInitialPermittedSubtrees = NULL; /* Referencing user memory. */
379 pThis->pInitialExcludedSubtrees = NULL; /* Referencing user memory. */
380
381 /* builder */
382 rtCrX509CertPathsDestroyTree(pThis);
383
384 /* validator */
385 rtCrX509CpvCleanup(pThis);
386
387 /* misc */
388 RTAsn1VtDelete(&pThis->AnyPolicyObjId.Asn1Core);
389
390 /* Finally, the instance itself. */
391 RTMemFree(pThis);
392 }
393 }
394 else
395 cRefs = 0;
396 return cRefs;
397}
398
399
400
401RTDECL(int) RTCrX509CertPathsSetTrustedStore(RTCRX509CERTPATHS hCertPaths, RTCRSTORE hTrustedStore)
402{
403 PRTCRX509CERTPATHSINT pThis = hCertPaths;
404 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
405 AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, VERR_INVALID_HANDLE);
406 AssertReturn(pThis->pRoot == NULL, VERR_WRONG_ORDER);
407
408 if (pThis->hTrustedStore != NIL_RTCRSTORE)
409 {
410 RTCrStoreRelease(pThis->hTrustedStore);
411 pThis->hTrustedStore = NIL_RTCRSTORE;
412 }
413 if (hTrustedStore != NIL_RTCRSTORE)
414 {
415 AssertReturn(RTCrStoreRetain(hTrustedStore) != UINT32_MAX, VERR_INVALID_HANDLE);
416 pThis->hTrustedStore = hTrustedStore;
417 }
418 return VINF_SUCCESS;
419}
420
421
422RTDECL(int) RTCrX509CertPathsSetUntrustedStore(RTCRX509CERTPATHS hCertPaths, RTCRSTORE hUntrustedStore)
423{
424 PRTCRX509CERTPATHSINT pThis = hCertPaths;
425 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
426 AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, VERR_INVALID_HANDLE);
427 AssertReturn(pThis->pRoot == NULL, VERR_WRONG_ORDER);
428
429 if (pThis->hUntrustedStore != NIL_RTCRSTORE)
430 {
431 RTCrStoreRelease(pThis->hUntrustedStore);
432 pThis->hUntrustedStore = NIL_RTCRSTORE;
433 }
434 if (hUntrustedStore != NIL_RTCRSTORE)
435 {
436 AssertReturn(RTCrStoreRetain(hUntrustedStore) != UINT32_MAX, VERR_INVALID_HANDLE);
437 pThis->hUntrustedStore = hUntrustedStore;
438 }
439 return VINF_SUCCESS;
440}
441
442
443RTDECL(int) RTCrX509CertPathsSetUntrustedArray(RTCRX509CERTPATHS hCertPaths, PCRTCRX509CERTIFICATE paCerts, uint32_t cCerts)
444{
445 PRTCRX509CERTPATHSINT pThis = hCertPaths;
446 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
447 AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, VERR_INVALID_HANDLE);
448
449 pThis->paUntrustedCerts = paCerts;
450 pThis->cUntrustedCerts = cCerts;
451 return VINF_SUCCESS;
452}
453
454
455RTDECL(int) RTCrX509CertPathsSetUntrustedSet(RTCRX509CERTPATHS hCertPaths, PCRTCRPKCS7SETOFCERTS pSetOfCerts)
456{
457 PRTCRX509CERTPATHSINT pThis = hCertPaths;
458 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
459 AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, VERR_INVALID_HANDLE);
460
461 pThis->pUntrustedCertsSet = pSetOfCerts;
462 return VINF_SUCCESS;
463}
464
465
466RTDECL(int) RTCrX509CertPathsSetValidTime(RTCRX509CERTPATHS hCertPaths, PCRTTIME pTime)
467{
468 PRTCRX509CERTPATHSINT pThis = hCertPaths;
469 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
470 AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, VERR_INVALID_HANDLE);
471
472 /* Allow this after building paths, as it's only used during verification. */
473
474 if (pTime)
475 {
476 if (RTTimeImplode(&pThis->ValidTime, pTime))
477 return VERR_INVALID_PARAMETER;
478 pThis->fFlags |= RTCRX509CERTPATHSINT_F_VALID_TIME;
479 }
480 else
481 pThis->fFlags &= ~RTCRX509CERTPATHSINT_F_VALID_TIME;
482 return VINF_SUCCESS;
483}
484
485
486RTDECL(int) RTCrX509CertPathsSetValidTimeSpec(RTCRX509CERTPATHS hCertPaths, PCRTTIMESPEC pTimeSpec)
487{
488 PRTCRX509CERTPATHSINT pThis = hCertPaths;
489 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
490 AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, VERR_INVALID_HANDLE);
491
492 /* Allow this after building paths, as it's only used during verification. */
493
494 if (pTimeSpec)
495 {
496 pThis->ValidTime = *pTimeSpec;
497 pThis->fFlags |= RTCRX509CERTPATHSINT_F_VALID_TIME;
498 }
499 else
500 pThis->fFlags &= ~RTCRX509CERTPATHSINT_F_VALID_TIME;
501 return VINF_SUCCESS;
502}
503
504
505RTDECL(int) RTCrX509CertPathsSetTrustAnchorChecks(RTCRX509CERTPATHS hCertPaths, bool fEnable)
506{
507 PRTCRX509CERTPATHSINT pThis = hCertPaths;
508 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
509 AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, VERR_INVALID_HANDLE);
510
511 if (fEnable)
512 pThis->fFlags |= RTCRX509CERTPATHSINT_F_CHECK_TRUST_ANCHOR;
513 else
514 pThis->fFlags &= ~RTCRX509CERTPATHSINT_F_CHECK_TRUST_ANCHOR;
515 return VINF_SUCCESS;
516}
517
518
519RTDECL(int) RTCrX509CertPathsCreateEx(PRTCRX509CERTPATHS phCertPaths, PCRTCRX509CERTIFICATE pTarget, RTCRSTORE hTrustedStore,
520 RTCRSTORE hUntrustedStore, PCRTCRX509CERTIFICATE paUntrustedCerts, uint32_t cUntrustedCerts,
521 PCRTTIMESPEC pValidTime)
522{
523 int rc = RTCrX509CertPathsCreate(phCertPaths, pTarget);
524 if (RT_SUCCESS(rc))
525 {
526 PRTCRX509CERTPATHSINT pThis = *phCertPaths;
527
528 rc = RTCrX509CertPathsSetTrustedStore(pThis, hTrustedStore);
529 if (RT_SUCCESS(rc))
530 {
531 rc = RTCrX509CertPathsSetUntrustedStore(pThis, hUntrustedStore);
532 if (RT_SUCCESS(rc))
533 {
534 rc = RTCrX509CertPathsSetUntrustedArray(pThis, paUntrustedCerts, cUntrustedCerts);
535 if (RT_SUCCESS(rc))
536 {
537 rc = RTCrX509CertPathsSetValidTimeSpec(pThis, pValidTime);
538 if (RT_SUCCESS(rc))
539 {
540 return VINF_SUCCESS;
541 }
542 }
543 RTCrStoreRelease(pThis->hUntrustedStore);
544 }
545 RTCrStoreRelease(pThis->hTrustedStore);
546 }
547 RTMemFree(pThis);
548 *phCertPaths = NIL_RTCRX509CERTPATHS;
549 }
550 return rc;
551}
552
553/** @} */
554
555
556
557/** @name Path Builder and Validator Common Utility Functions.
558 * @{
559 */
560
561/**
562 * Checks if the certificate is self-issued.
563 *
564 * @returns true / false.
565 * @param pNode The path node to check..
566 */
567static bool rtCrX509CertPathsIsSelfIssued(PRTCRX509CERTPATHNODE pNode)
568{
569 return pNode->pCert
570 && RTCrX509Name_MatchByRfc5280(&pNode->pCert->TbsCertificate.Subject, &pNode->pCert->TbsCertificate.Issuer);
571}
572
573/** @} */
574
575
576
577/** @name Path Builder Functions.
578 * @{
579 */
580
581/**
582 *
583 * @returns
584 * @param pThis .
585 */
586static PRTCRX509CERTPATHNODE rtCrX509CertPathsNewNode(PRTCRX509CERTPATHSINT pThis)
587{
588 PRTCRX509CERTPATHNODE pNode = (PRTCRX509CERTPATHNODE)RTMemAllocZ(sizeof(*pNode));
589 if (RT_LIKELY(pNode))
590 {
591 RTListInit(&pNode->SiblingEntry);
592 RTListInit(&pNode->ChildListOrLeafEntry);
593 pNode->rcVerify = VERR_CR_X509_NOT_VERIFIED;
594
595 return pNode;
596 }
597
598 pThis->rc = RTErrInfoSet(pThis->pErrInfo, VERR_NO_MEMORY, "No memory for path node");
599 return NULL;
600}
601
602
603static void rtCrX509CertPathsDestroyNode(PRTCRX509CERTPATHNODE pNode)
604{
605 if (pNode->pCertCtx)
606 {
607 RTCrCertCtxRelease(pNode->pCertCtx);
608 pNode->pCertCtx = NULL;
609 }
610 RT_ZERO(*pNode);
611 RTMemFree(pNode);
612}
613
614
615static void rtCrX509CertPathsAddIssuer(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHNODE pParent,
616 PCRTCRX509CERTIFICATE pCert, PCRTCRCERTCTX pCertCtx, uint8_t uSrc)
617{
618 /*
619 * Check if we've seen this certificate already in the current path or
620 * among the already gathered issuers.
621 */
622 if (pCert)
623 {
624 /* No duplicate certificates in the path. */
625 PRTCRX509CERTPATHNODE pTmpNode = pParent;
626 while (pTmpNode)
627 {
628 Assert(pTmpNode->pCert);
629 if ( pTmpNode->pCert == pCert
630 || RTCrX509Certificate_Compare(pTmpNode->pCert, pCert) == 0)
631 {
632 /* If target and the source it trusted, upgrade the source so we can successfully verify single node 'paths'. */
633 if ( RTCRX509CERTPATHNODE_SRC_IS_TRUSTED(uSrc)
634 && pTmpNode == pParent
635 && pTmpNode->uSrc == RTCRX509CERTPATHNODE_SRC_TARGET)
636 {
637 AssertReturnVoid(!pTmpNode->pParent);
638 pTmpNode->uSrc = uSrc;
639 }
640 return;
641 }
642 pTmpNode = pTmpNode->pParent;
643 }
644
645 /* No duplicate tree branches. */
646 RTListForEach(&pParent->ChildListOrLeafEntry, pTmpNode, RTCRX509CERTPATHNODE, SiblingEntry)
647 {
648 if (RTCrX509Certificate_Compare(pTmpNode->pCert, pCert) == 0)
649 return;
650 }
651 }
652 else
653 Assert(pCertCtx);
654
655 /*
656 * Reference the context core before making the allocation.
657 */
658 if (pCertCtx)
659 AssertReturnVoidStmt(RTCrCertCtxRetain(pCertCtx) != UINT32_MAX,
660 pThis->rc = RTErrInfoSetF(pThis->pErrInfo, VERR_CR_X509_CPB_BAD_CERT_CTX,
661 "Bad pCertCtx=%p", pCertCtx));
662
663 /*
664 * We haven't see it, append it as a child.
665 */
666 PRTCRX509CERTPATHNODE pNew = rtCrX509CertPathsNewNode(pThis);
667 if (pNew)
668 {
669 pNew->pParent = pParent;
670 pNew->pCert = pCert;
671 pNew->pCertCtx = pCertCtx;
672 pNew->uSrc = uSrc;
673 pNew->uDepth = pParent->uDepth + 1;
674 RTListAppend(&pParent->ChildListOrLeafEntry, &pNew->SiblingEntry);
675 }
676 else
677 RTCrCertCtxRelease(pCertCtx);
678}
679
680
681static void rtCrX509CertPathsGetIssuersFromStore(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHNODE pNode,
682 PCRTCRX509NAME pIssuer, RTCRSTORE hStore, uint8_t uSrc)
683{
684 RTCRSTORECERTSEARCH Search;
685 int rc = RTCrStoreCertFindBySubjectOrAltSubjectByRfc5280(hStore, pIssuer, &Search);
686 if (RT_SUCCESS(rc))
687 {
688 PCRTCRCERTCTX pCertCtx;
689 while ((pCertCtx = RTCrStoreCertSearchNext(hStore, &Search)) != NULL)
690 {
691 if ( pCertCtx->pCert
692 || ( RTCRX509CERTPATHNODE_SRC_IS_TRUSTED(uSrc)
693 && pCertCtx->pTaInfo) )
694 rtCrX509CertPathsAddIssuer(pThis, pNode, pCertCtx->pCert, pCertCtx, uSrc);
695 RTCrCertCtxRelease(pCertCtx);
696 }
697 RTCrStoreCertSearchDestroy(hStore, &Search);
698 }
699}
700
701
702static void rtCrX509CertPathsGetIssuers(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHNODE pNode)
703{
704 Assert(RTListIsEmpty(&pNode->ChildListOrLeafEntry));
705 Assert(!pNode->fLeaf);
706 Assert(pNode->pCert);
707
708 /*
709 * Don't recurse infintely.
710 */
711 if (RT_UNLIKELY(pNode->uDepth >= 50))
712 return;
713
714 PCRTCRX509NAME const pIssuer = &pNode->pCert->TbsCertificate.Issuer;
715
716 /*
717 * Trusted certificate.
718 */
719 if ( pThis->pTrustedCert
720 && RTCrX509Certificate_MatchSubjectOrAltSubjectByRfc5280(pThis->pTrustedCert, pIssuer))
721 rtCrX509CertPathsAddIssuer(pThis, pNode, pThis->pTrustedCert, NULL, RTCRX509CERTPATHNODE_SRC_TRUSTED_CERT);
722
723 /*
724 * Trusted certificate store.
725 */
726 if (pThis->hTrustedStore != NIL_RTCRSTORE)
727 rtCrX509CertPathsGetIssuersFromStore(pThis, pNode, pIssuer, pThis->hTrustedStore,
728 RTCRX509CERTPATHNODE_SRC_TRUSTED_STORE);
729
730 /*
731 * Untrusted store.
732 */
733 if (pThis->hUntrustedStore != NIL_RTCRSTORE)
734 rtCrX509CertPathsGetIssuersFromStore(pThis, pNode, pIssuer, pThis->hTrustedStore,
735 RTCRX509CERTPATHNODE_SRC_UNTRUSTED_STORE);
736
737 /*
738 * Untrusted array.
739 */
740 if (pThis->paUntrustedCerts)
741 for (uint32_t i = 0; i < pThis->cUntrustedCerts; i++)
742 if (RTCrX509Certificate_MatchSubjectOrAltSubjectByRfc5280(&pThis->paUntrustedCerts[i], pIssuer))
743 rtCrX509CertPathsAddIssuer(pThis, pNode, &pThis->paUntrustedCerts[i], NULL,
744 RTCRX509CERTPATHNODE_SRC_UNTRUSTED_ARRAY);
745
746 /** @todo Rainy day: Should abstract the untrusted array and set so we don't get
747 * unnecessary PKCS7/CMS header dependencies. */
748
749 /*
750 * Untrusted set.
751 */
752 if (pThis->pUntrustedCertsSet)
753 {
754 uint32_t const cCerts = pThis->pUntrustedCertsSet->cItems;
755 PRTCRPKCS7CERT const *papCerts = pThis->pUntrustedCertsSet->papItems;
756 for (uint32_t i = 0; i < cCerts; i++)
757 {
758 PCRTCRPKCS7CERT pCert = papCerts[i];
759 if ( pCert->enmChoice == RTCRPKCS7CERTCHOICE_X509
760 && RTCrX509Certificate_MatchSubjectOrAltSubjectByRfc5280(pCert->u.pX509Cert, pIssuer))
761 rtCrX509CertPathsAddIssuer(pThis, pNode, pCert->u.pX509Cert, NULL, RTCRX509CERTPATHNODE_SRC_UNTRUSTED_SET);
762 }
763 }
764}
765
766
767static PRTCRX509CERTPATHNODE rtCrX509CertPathsGetNextRightUp(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHNODE pNode)
768{
769 for (;;)
770 {
771 /* The root node has no siblings. */
772 PRTCRX509CERTPATHNODE pParent = pNode->pParent;
773 if (!pNode->pParent)
774 return NULL;
775
776 /* Try go to the right. */
777 PRTCRX509CERTPATHNODE pNext = RTListGetNext(&pParent->ChildListOrLeafEntry, pNode, RTCRX509CERTPATHNODE, SiblingEntry);
778 if (pNext)
779 return pNext;
780
781 /* Up. */
782 pNode = pParent;
783 }
784
785 RT_NOREF_PV(pThis);
786}
787
788
789static PRTCRX509CERTPATHNODE rtCrX509CertPathsEliminatePath(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHNODE pNode)
790{
791 for (;;)
792 {
793 Assert(RTListIsEmpty(&pNode->ChildListOrLeafEntry));
794
795 /* Don't remove the root node. */
796 PRTCRX509CERTPATHNODE pParent = pNode->pParent;
797 if (!pParent)
798 return NULL;
799
800 /* Before removing and deleting the node check if there is sibling
801 right to it that we should continue processing from. */
802 PRTCRX509CERTPATHNODE pNext = RTListGetNext(&pParent->ChildListOrLeafEntry, pNode, RTCRX509CERTPATHNODE, SiblingEntry);
803 RTListNodeRemove(&pNode->SiblingEntry);
804 rtCrX509CertPathsDestroyNode(pNode);
805
806 if (pNext)
807 return pNext;
808
809 /* If the parent node cannot be removed, do a normal get-next-rigth-up
810 to find the continuation point for the tree loop. */
811 if (!RTListIsEmpty(&pParent->ChildListOrLeafEntry))
812 return rtCrX509CertPathsGetNextRightUp(pThis, pParent);
813
814 pNode = pParent;
815 }
816}
817
818
819/**
820 * Destroys the whole path tree.
821 *
822 * @param pThis The path builder and verifier instance.
823 */
824static void rtCrX509CertPathsDestroyTree(PRTCRX509CERTPATHSINT pThis)
825{
826 PRTCRX509CERTPATHNODE pNode, pNextLeaf;
827 RTListForEachSafe(&pThis->LeafList, pNode, pNextLeaf, RTCRX509CERTPATHNODE, ChildListOrLeafEntry)
828 {
829 RTListNodeRemove(&pNode->ChildListOrLeafEntry);
830 RTListInit(&pNode->ChildListOrLeafEntry);
831
832 for (;;)
833 {
834 PRTCRX509CERTPATHNODE pParent = pNode->pParent;
835
836 RTListNodeRemove(&pNode->SiblingEntry);
837 rtCrX509CertPathsDestroyNode(pNode);
838
839 if (!pParent)
840 {
841 pThis->pRoot = NULL;
842 break;
843 }
844
845 if (!RTListIsEmpty(&pParent->ChildListOrLeafEntry))
846 break;
847
848 pNode = pParent;
849 }
850 }
851 Assert(!pThis->pRoot);
852}
853
854
855/**
856 * Adds a leaf node.
857 *
858 * This should normally be a trusted certificate, but the caller can also
859 * request the incomplete paths, in which case this will be an untrusted
860 * certificate.
861 *
862 * @returns Pointer to the next node in the tree to process.
863 * @param pThis The path builder instance.
864 * @param pNode The leaf node.
865 */
866static PRTCRX509CERTPATHNODE rtCrX509CertPathsAddLeaf(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHNODE pNode)
867{
868 pNode->fLeaf = true;
869
870 /*
871 * Priority insert by source and depth.
872 */
873 PRTCRX509CERTPATHNODE pCurLeaf;
874 RTListForEach(&pThis->LeafList, pCurLeaf, RTCRX509CERTPATHNODE, ChildListOrLeafEntry)
875 {
876 if ( pNode->uSrc > pCurLeaf->uSrc
877 || ( pNode->uSrc == pCurLeaf->uSrc
878 && pNode->uDepth < pCurLeaf->uDepth) )
879 {
880 RTListNodeInsertBefore(&pCurLeaf->ChildListOrLeafEntry, &pNode->ChildListOrLeafEntry);
881 pThis->cPaths++;
882 return rtCrX509CertPathsGetNextRightUp(pThis, pNode);
883 }
884 }
885
886 RTListAppend(&pThis->LeafList, &pNode->ChildListOrLeafEntry);
887 pThis->cPaths++;
888 return rtCrX509CertPathsGetNextRightUp(pThis, pNode);
889}
890
891
892
893RTDECL(int) RTCrX509CertPathsBuild(RTCRX509CERTPATHS hCertPaths, PRTERRINFO pErrInfo)
894{
895 /*
896 * Validate the input.
897 */
898 PRTCRX509CERTPATHSINT pThis = hCertPaths;
899 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
900 AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, VERR_INVALID_HANDLE);
901 AssertReturn(!(pThis->fFlags & ~RTCRX509CERTPATHSINT_F_VALID_MASK), VERR_INVALID_PARAMETER);
902 AssertReturn( (pThis->paUntrustedCerts == NULL && pThis->cUntrustedCerts == 0)
903 || (pThis->paUntrustedCerts != NULL && pThis->cUntrustedCerts > 0),
904 VERR_INVALID_PARAMETER);
905 AssertReturn(RTListIsEmpty(&pThis->LeafList), VERR_INVALID_PARAMETER);
906 AssertReturn(pThis->pRoot == NULL, VERR_INVALID_PARAMETER);
907 AssertReturn(pThis->rc == VINF_SUCCESS, pThis->rc);
908 AssertPtrReturn(pThis->pTarget, VERR_INVALID_PARAMETER);
909 Assert(RT_SUCCESS(RTCrX509Certificate_CheckSanity(pThis->pTarget, 0, NULL, NULL)));
910
911 /*
912 * Set up the target.
913 */
914 PRTCRX509CERTPATHNODE pCur;
915 pThis->pRoot = pCur = rtCrX509CertPathsNewNode(pThis);
916 if (pThis->pRoot)
917 {
918 pCur->pCert = pThis->pTarget;
919 pCur->uDepth = 0;
920 pCur->uSrc = RTCRX509CERTPATHNODE_SRC_TARGET;
921
922 pThis->pErrInfo = pErrInfo;
923
924 /*
925 * The tree construction loop.
926 * Walks down, up, and right as the tree is constructed.
927 */
928 do
929 {
930 /*
931 * Check for the two leaf cases first.
932 */
933 if (RTCRX509CERTPATHNODE_SRC_IS_TRUSTED(pCur->uSrc))
934 pCur = rtCrX509CertPathsAddLeaf(pThis, pCur);
935#if 0 /* This isn't right.*/
936 else if (rtCrX509CertPathsIsSelfIssued(pCur))
937 {
938 if (pThis->fFlags & RTCRX509CERTPATHSINT_F_ELIMINATE_UNTRUSTED_PATHS)
939 pCur = rtCrX509CertPathsEliminatePath(pThis, pCur);
940 else
941 pCur = rtCrX509CertPathsAddLeaf(pThis, pCur);
942 }
943#endif
944 /*
945 * Not a leaf, find all potential issuers and decend into these.
946 */
947 else
948 {
949 rtCrX509CertPathsGetIssuers(pThis, pCur);
950 if (RT_FAILURE(pThis->rc))
951 break;
952
953 if (!RTListIsEmpty(&pCur->ChildListOrLeafEntry))
954 pCur = RTListGetFirst(&pCur->ChildListOrLeafEntry, RTCRX509CERTPATHNODE, SiblingEntry);
955 else if (pThis->fFlags & RTCRX509CERTPATHSINT_F_ELIMINATE_UNTRUSTED_PATHS)
956 pCur = rtCrX509CertPathsEliminatePath(pThis, pCur);
957 else
958 pCur = rtCrX509CertPathsAddLeaf(pThis, pCur);
959 }
960 if (pCur)
961 Log2(("RTCrX509CertPathsBuild: pCur=%p fLeaf=%d pParent=%p pNext=%p pPrev=%p\n",
962 pCur, pCur->fLeaf, pCur->pParent,
963 pCur->pParent ? RTListGetNext(&pCur->pParent->ChildListOrLeafEntry, pCur, RTCRX509CERTPATHNODE, SiblingEntry) : NULL,
964 pCur->pParent ? RTListGetPrev(&pCur->pParent->ChildListOrLeafEntry, pCur, RTCRX509CERTPATHNODE, SiblingEntry) : NULL));
965 } while (pCur);
966
967 pThis->pErrInfo = NULL;
968 if (RT_SUCCESS(pThis->rc))
969 return VINF_SUCCESS;
970 }
971 else
972 Assert(RT_FAILURE_NP(pThis->rc));
973 return pThis->rc;
974}
975
976
977/**
978 * Looks up path by leaf/path index.
979 *
980 * @returns Pointer to the leaf node of the path.
981 * @param pThis The path builder & validator instance.
982 * @param iPath The oridnal of the path to get.
983 */
984static PRTCRX509CERTPATHNODE rtCrX509CertPathsGetLeafByIndex(PRTCRX509CERTPATHSINT pThis, uint32_t iPath)
985{
986 Assert(iPath < pThis->cPaths);
987
988 uint32_t iCurPath = 0;
989 PRTCRX509CERTPATHNODE pCurLeaf;
990 RTListForEach(&pThis->LeafList, pCurLeaf, RTCRX509CERTPATHNODE, ChildListOrLeafEntry)
991 {
992 if (iCurPath == iPath)
993 return pCurLeaf;
994 iCurPath++;
995 }
996
997 AssertFailedReturn(NULL);
998}
999
1000
1001static void rtDumpPrintf(PFNRTDUMPPRINTFV pfnPrintfV, void *pvUser, const char *pszFormat, ...)
1002{
1003 va_list va;
1004 va_start(va, pszFormat);
1005 pfnPrintfV(pvUser, pszFormat, va);
1006 va_end(va);
1007}
1008
1009
1010static void rtDumpIndent(PFNRTDUMPPRINTFV pfnPrintfV, void *pvUser, uint32_t cchSpaces, const char *pszFormat, ...)
1011{
1012 static const char s_szSpaces[] = " ";
1013 while (cchSpaces > 0)
1014 {
1015 uint32_t cchBurst = RT_MIN(sizeof(s_szSpaces) - 1, cchSpaces);
1016 rtDumpPrintf(pfnPrintfV, pvUser, &s_szSpaces[sizeof(s_szSpaces) - cchBurst - 1]);
1017 cchSpaces -= cchBurst;
1018 }
1019
1020 va_list va;
1021 va_start(va, pszFormat);
1022 pfnPrintfV(pvUser, pszFormat, va);
1023 va_end(va);
1024}
1025
1026/** @name X.500 attribute types
1027 * See RFC-4519 among others.
1028 * @{ */
1029#define RTCRX500_ID_AT_OBJECT_CLASS_OID "2.5.4.0"
1030#define RTCRX500_ID_AT_ALIASED_ENTRY_NAME_OID "2.5.4.1"
1031#define RTCRX500_ID_AT_KNOWLDGEINFORMATION_OID "2.5.4.2"
1032#define RTCRX500_ID_AT_COMMON_NAME_OID "2.5.4.3"
1033#define RTCRX500_ID_AT_SURNAME_OID "2.5.4.4"
1034#define RTCRX500_ID_AT_SERIAL_NUMBER_OID "2.5.4.5"
1035#define RTCRX500_ID_AT_COUNTRY_NAME_OID "2.5.4.6"
1036#define RTCRX500_ID_AT_LOCALITY_NAME_OID "2.5.4.7"
1037#define RTCRX500_ID_AT_STATE_OR_PROVINCE_NAME_OID "2.5.4.8"
1038#define RTCRX500_ID_AT_STREET_ADDRESS_OID "2.5.4.9"
1039#define RTCRX500_ID_AT_ORGANIZATION_NAME_OID "2.5.4.10"
1040#define RTCRX500_ID_AT_ORGANIZATION_UNIT_NAME_OID "2.5.4.11"
1041#define RTCRX500_ID_AT_TITLE_OID "2.5.4.12"
1042#define RTCRX500_ID_AT_DESCRIPTION_OID "2.5.4.13"
1043#define RTCRX500_ID_AT_SEARCH_GUIDE_OID "2.5.4.14"
1044#define RTCRX500_ID_AT_BUSINESS_CATEGORY_OID "2.5.4.15"
1045#define RTCRX500_ID_AT_POSTAL_ADDRESS_OID "2.5.4.16"
1046#define RTCRX500_ID_AT_POSTAL_CODE_OID "2.5.4.17"
1047#define RTCRX500_ID_AT_POST_OFFICE_BOX_OID "2.5.4.18"
1048#define RTCRX500_ID_AT_PHYSICAL_DELIVERY_OFFICE_NAME_OID "2.5.4.19"
1049#define RTCRX500_ID_AT_TELEPHONE_NUMBER_OID "2.5.4.20"
1050#define RTCRX500_ID_AT_TELEX_NUMBER_OID "2.5.4.21"
1051#define RTCRX500_ID_AT_TELETEX_TERMINAL_IDENTIFIER_OID "2.5.4.22"
1052#define RTCRX500_ID_AT_FACIMILE_TELEPHONE_NUMBER_OID "2.5.4.23"
1053#define RTCRX500_ID_AT_X121_ADDRESS_OID "2.5.4.24"
1054#define RTCRX500_ID_AT_INTERNATIONAL_ISDN_NUMBER_OID "2.5.4.25"
1055#define RTCRX500_ID_AT_REGISTERED_ADDRESS_OID "2.5.4.26"
1056#define RTCRX500_ID_AT_DESTINATION_INDICATOR_OID "2.5.4.27"
1057#define RTCRX500_ID_AT_PREFERRED_DELIVERY_METHOD_OID "2.5.4.28"
1058#define RTCRX500_ID_AT_PRESENTATION_ADDRESS_OID "2.5.4.29"
1059#define RTCRX500_ID_AT_SUPPORTED_APPLICATION_CONTEXT_OID "2.5.4.30"
1060#define RTCRX500_ID_AT_MEMBER_OID "2.5.4.31"
1061#define RTCRX500_ID_AT_OWNER_OID "2.5.4.32"
1062#define RTCRX500_ID_AT_ROLE_OCCUPANT_OID "2.5.4.33"
1063#define RTCRX500_ID_AT_SEE_ALSO_OID "2.5.4.34"
1064#define RTCRX500_ID_AT_USER_PASSWORD_OID "2.5.4.35"
1065#define RTCRX500_ID_AT_USER_CERTIFICATE_OID "2.5.4.36"
1066#define RTCRX500_ID_AT_CA_CERTIFICATE_OID "2.5.4.37"
1067#define RTCRX500_ID_AT_AUTHORITY_REVOCATION_LIST_OID "2.5.4.38"
1068#define RTCRX500_ID_AT_CERTIFICATE_REVOCATION_LIST_OID "2.5.4.39"
1069#define RTCRX500_ID_AT_CROSS_CERTIFICATE_PAIR_OID "2.5.4.40"
1070#define RTCRX500_ID_AT_NAME_OID "2.5.4.41"
1071#define RTCRX500_ID_AT_GIVEN_NAME_OID "2.5.4.42"
1072#define RTCRX500_ID_AT_INITIALS_OID "2.5.4.43"
1073#define RTCRX500_ID_AT_GENERATION_QUALIFIER_OID "2.5.4.44"
1074#define RTCRX500_ID_AT_UNIQUE_IDENTIFIER_OID "2.5.4.45"
1075#define RTCRX500_ID_AT_DN_QUALIFIER_OID "2.5.4.46"
1076#define RTCRX500_ID_AT_ENHANCHED_SEARCH_GUIDE_OID "2.5.4.47"
1077#define RTCRX500_ID_AT_PROTOCOL_INFORMATION_OID "2.5.4.48"
1078#define RTCRX500_ID_AT_DISTINGUISHED_NAME_OID "2.5.4.49"
1079#define RTCRX500_ID_AT_UNIQUE_MEMBER_OID "2.5.4.50"
1080#define RTCRX500_ID_AT_HOUSE_IDENTIFIER_OID "2.5.4.51"
1081#define RTCRX500_ID_AT_SUPPORTED_ALGORITHMS_OID "2.5.4.52"
1082#define RTCRX500_ID_AT_DELTA_REVOCATION_LIST_OID "2.5.4.53"
1083#define RTCRX500_ID_AT_ATTRIBUTE_CERTIFICATE_OID "2.5.4.58"
1084#define RTCRX500_ID_AT_PSEUDONYM_OID "2.5.4.65"
1085/** @} */
1086
1087
1088static void rtCrX509NameDump(PCRTCRX509NAME pName, PFNRTDUMPPRINTFV pfnPrintfV, void *pvUser)
1089{
1090 for (uint32_t i = 0; i < pName->cItems; i++)
1091 {
1092 PCRTCRX509RELATIVEDISTINGUISHEDNAME const pRdn = pName->papItems[i];
1093 for (uint32_t j = 0; j < pRdn->cItems; j++)
1094 {
1095 PRTCRX509ATTRIBUTETYPEANDVALUE pAttrib = pRdn->papItems[j];
1096
1097 const char *pszType = pAttrib->Type.szObjId;
1098 if ( !strncmp(pAttrib->Type.szObjId, "2.5.4.", 6)
1099 && (pAttrib->Type.szObjId[8] == '\0' || pAttrib->Type.szObjId[9] == '\0'))
1100 {
1101 switch (RTStrToUInt8(&pAttrib->Type.szObjId[6]))
1102 {
1103 case 3: pszType = "cn"; break;
1104 case 4: pszType = "sn"; break;
1105 case 5: pszType = "serialNumber"; break;
1106 case 6: pszType = "c"; break;
1107 case 7: pszType = "l"; break;
1108 case 8: pszType = "st"; break;
1109 case 9: pszType = "street"; break;
1110 case 10: pszType = "o"; break;
1111 case 11: pszType = "ou"; break;
1112 case 13: pszType = "description"; break;
1113 case 15: pszType = "businessCategory"; break;
1114 case 16: pszType = "postalAddress"; break;
1115 case 17: pszType = "postalCode"; break;
1116 case 18: pszType = "postOfficeBox"; break;
1117 case 20: pszType = "telephoneNumber"; break;
1118 case 26: pszType = "registeredAddress"; break;
1119 case 31: pszType = "member"; break;
1120 case 41: pszType = "name"; break;
1121 case 42: pszType = "givenName"; break;
1122 case 43: pszType = "initials"; break;
1123 case 45: pszType = "x500UniqueIdentifier"; break;
1124 case 50: pszType = "uniqueMember"; break;
1125 }
1126 }
1127 rtDumpPrintf(pfnPrintfV, pvUser, "/%s=", pszType);
1128 if (pAttrib->Value.enmType == RTASN1TYPE_STRING)
1129 {
1130 if (pAttrib->Value.u.String.pszUtf8)
1131 rtDumpPrintf(pfnPrintfV, pvUser, "%s", pAttrib->Value.u.String.pszUtf8);
1132 else
1133 {
1134 const char *pch = pAttrib->Value.u.String.Asn1Core.uData.pch;
1135 uint32_t cch = pAttrib->Value.u.String.Asn1Core.cb;
1136 int rc = RTStrValidateEncodingEx(pch, cch, 0);
1137 if (RT_SUCCESS(rc) && cch)
1138 rtDumpPrintf(pfnPrintfV, pvUser, "%.*s", (size_t)cch, pch);
1139 else
1140 while (cch > 0)
1141 {
1142 if (RT_C_IS_PRINT(*pch))
1143 rtDumpPrintf(pfnPrintfV, pvUser, "%c", *pch);
1144 else
1145 rtDumpPrintf(pfnPrintfV, pvUser, "\\x%02x", *pch);
1146 cch--;
1147 pch++;
1148 }
1149 }
1150 }
1151 else
1152 rtDumpPrintf(pfnPrintfV, pvUser, "<not-string: uTag=%#x>", pAttrib->Value.u.Core.uTag);
1153 }
1154 }
1155}
1156
1157
1158static const char *rtCrX509CertPathsNodeGetSourceName(PRTCRX509CERTPATHNODE pNode)
1159{
1160 switch (pNode->uSrc)
1161 {
1162 case RTCRX509CERTPATHNODE_SRC_TARGET: return "target";
1163 case RTCRX509CERTPATHNODE_SRC_UNTRUSTED_SET: return "untrusted_set";
1164 case RTCRX509CERTPATHNODE_SRC_UNTRUSTED_ARRAY: return "untrusted_array";
1165 case RTCRX509CERTPATHNODE_SRC_UNTRUSTED_STORE: return "untrusted_store";
1166 case RTCRX509CERTPATHNODE_SRC_TRUSTED_STORE: return "trusted_store";
1167 case RTCRX509CERTPATHNODE_SRC_TRUSTED_CERT: return "trusted_cert";
1168 default: return "invalid";
1169 }
1170}
1171
1172
1173static void rtCrX509CertPathsDumpOneWorker(PRTCRX509CERTPATHSINT pThis, uint32_t iPath, PRTCRX509CERTPATHNODE pCurLeaf,
1174 uint32_t uVerbosity, PFNRTDUMPPRINTFV pfnPrintfV, void *pvUser)
1175{
1176 RT_NOREF_PV(pThis);
1177 rtDumpPrintf(pfnPrintfV, pvUser, "Path #%u: %s, %u deep, rcVerify=%Rrc\n",
1178 iPath, RTCRX509CERTPATHNODE_SRC_IS_TRUSTED(pCurLeaf->uSrc) ? "trusted" : "untrusted", pCurLeaf->uDepth,
1179 pCurLeaf->rcVerify);
1180
1181 for (uint32_t iIndent = 2; pCurLeaf; iIndent += 2, pCurLeaf = pCurLeaf->pParent)
1182 {
1183 if (pCurLeaf->pCert)
1184 {
1185 rtDumpIndent(pfnPrintfV, pvUser, iIndent, "Issuer : ");
1186 rtCrX509NameDump(&pCurLeaf->pCert->TbsCertificate.Issuer, pfnPrintfV, pvUser);
1187 rtDumpPrintf(pfnPrintfV, pvUser, "\n");
1188
1189 rtDumpIndent(pfnPrintfV, pvUser, iIndent, "Subject: ");
1190 rtCrX509NameDump(&pCurLeaf->pCert->TbsCertificate.Subject, pfnPrintfV, pvUser);
1191 rtDumpPrintf(pfnPrintfV, pvUser, "\n");
1192
1193 if (uVerbosity >= 4)
1194 RTAsn1Dump(&pCurLeaf->pCert->SeqCore.Asn1Core, 0, iIndent, pfnPrintfV, pvUser);
1195 else if (uVerbosity >= 3)
1196 RTAsn1Dump(&pCurLeaf->pCert->TbsCertificate.T3.Extensions.SeqCore.Asn1Core, 0, iIndent, pfnPrintfV, pvUser);
1197 }
1198 else
1199 {
1200 Assert(pCurLeaf->pCertCtx); Assert(pCurLeaf->pCertCtx->pTaInfo);
1201 rtDumpIndent(pfnPrintfV, pvUser, iIndent, "Subject: ");
1202 rtCrX509NameDump(&pCurLeaf->pCertCtx->pTaInfo->CertPath.TaName, pfnPrintfV, pvUser);
1203
1204 if (uVerbosity >= 4)
1205 RTAsn1Dump(&pCurLeaf->pCertCtx->pTaInfo->SeqCore.Asn1Core, 0, iIndent, pfnPrintfV, pvUser);
1206 }
1207
1208 const char *pszSrc = rtCrX509CertPathsNodeGetSourceName(pCurLeaf);
1209 rtDumpIndent(pfnPrintfV, pvUser, iIndent, "Source : %s\n", pszSrc);
1210 }
1211}
1212
1213
1214RTDECL(int) RTCrX509CertPathsDumpOne(RTCRX509CERTPATHS hCertPaths, uint32_t iPath, uint32_t uVerbosity,
1215 PFNRTDUMPPRINTFV pfnPrintfV, void *pvUser)
1216{
1217 /*
1218 * Validate the input.
1219 */
1220 PRTCRX509CERTPATHSINT pThis = hCertPaths;
1221 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1222 AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, VERR_INVALID_HANDLE);
1223 AssertPtrReturn(pfnPrintfV, VERR_INVALID_POINTER);
1224 int rc;
1225 if (iPath < pThis->cPaths)
1226 {
1227 PRTCRX509CERTPATHNODE pLeaf = rtCrX509CertPathsGetLeafByIndex(pThis, iPath);
1228 if (pLeaf)
1229 {
1230 rtCrX509CertPathsDumpOneWorker(pThis, iPath, pLeaf, uVerbosity, pfnPrintfV, pvUser);
1231 rc = VINF_SUCCESS;
1232 }
1233 else
1234 rc = VERR_CR_X509_CERTPATHS_INTERNAL_ERROR;
1235 }
1236 else
1237 rc = VERR_NOT_FOUND;
1238 return rc;
1239}
1240
1241
1242RTDECL(int) RTCrX509CertPathsDumpAll(RTCRX509CERTPATHS hCertPaths, uint32_t uVerbosity, PFNRTDUMPPRINTFV pfnPrintfV, void *pvUser)
1243{
1244 /*
1245 * Validate the input.
1246 */
1247 PRTCRX509CERTPATHSINT pThis = hCertPaths;
1248 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
1249 AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, VERR_INVALID_HANDLE);
1250 AssertPtrReturn(pfnPrintfV, VERR_INVALID_POINTER);
1251
1252 /*
1253 * Dump all the paths.
1254 */
1255 rtDumpPrintf(pfnPrintfV, pvUser, "%u paths, rc=%Rrc\n", pThis->cPaths, pThis->rc);
1256 uint32_t iPath = 0;
1257 PRTCRX509CERTPATHNODE pCurLeaf, pNextLeaf;
1258 RTListForEachSafe(&pThis->LeafList, pCurLeaf, pNextLeaf, RTCRX509CERTPATHNODE, ChildListOrLeafEntry)
1259 {
1260 rtCrX509CertPathsDumpOneWorker(pThis, iPath, pCurLeaf, uVerbosity, pfnPrintfV, pvUser);
1261 iPath++;
1262 }
1263
1264 return VINF_SUCCESS;
1265}
1266
1267
1268/** @} */
1269
1270
1271/** @name Path Validator Functions.
1272 * @{
1273 */
1274
1275
1276static void *rtCrX509CpvAllocZ(PRTCRX509CERTPATHSINT pThis, size_t cb, const char *pszWhat)
1277{
1278 void *pv = RTMemAllocZ(cb);
1279 if (!pv)
1280 pThis->rc = RTErrInfoSetF(pThis->pErrInfo, VERR_NO_MEMORY, "Failed to allocate %zu bytes for %s", cb, pszWhat);
1281 return pv;
1282}
1283
1284
1285DECL_NO_INLINE(static, bool) rtCrX509CpvFailed(PRTCRX509CERTPATHSINT pThis, int rc, const char *pszFormat, ...)
1286{
1287 va_list va;
1288 va_start(va, pszFormat);
1289 pThis->rc = RTErrInfoSetV(pThis->pErrInfo, rc, pszFormat, va);
1290 va_end(va);
1291 return false;
1292}
1293
1294
1295/**
1296 * Adds a sequence of excluded sub-trees.
1297 *
1298 * Don't waste time optimizing the output if this is supposed to be a union.
1299 * Unless the path is very long, it's a lot more work to optimize and the result
1300 * will be the same anyway.
1301 *
1302 * @returns success indicator.
1303 * @param pThis The validator instance.
1304 * @param pSubtrees The sequence of sub-trees to add.
1305 */
1306static bool rtCrX509CpvAddExcludedSubtrees(PRTCRX509CERTPATHSINT pThis, PCRTCRX509GENERALSUBTREES pSubtrees)
1307{
1308 if (((pThis->v.cExcludedSubtrees + 1) & 0xf) == 0)
1309 {
1310 void *pvNew = RTMemRealloc(pThis->v.papExcludedSubtrees,
1311 (pThis->v.cExcludedSubtrees + 16) * sizeof(pThis->v.papExcludedSubtrees[0]));
1312 if (RT_UNLIKELY(!pvNew))
1313 return rtCrX509CpvFailed(pThis, VERR_NO_MEMORY, "Error growing subtrees pointer array to %u elements",
1314 pThis->v.cExcludedSubtrees + 16);
1315 pThis->v.papExcludedSubtrees = (PCRTCRX509GENERALSUBTREES *)pvNew;
1316 }
1317 pThis->v.papExcludedSubtrees[pThis->v.cExcludedSubtrees] = pSubtrees;
1318 pThis->v.cExcludedSubtrees++;
1319 return true;
1320}
1321
1322
1323/**
1324 * Checks if a sub-tree is according to RFC-5280.
1325 *
1326 * @returns Success indiciator.
1327 * @param pThis The validator instance.
1328 * @param pSubtree The subtree to check.
1329 */
1330static bool rtCrX509CpvCheckSubtreeValidity(PRTCRX509CERTPATHSINT pThis, PCRTCRX509GENERALSUBTREE pSubtree)
1331{
1332 if ( pSubtree->Base.enmChoice <= RTCRX509GENERALNAMECHOICE_INVALID
1333 || pSubtree->Base.enmChoice >= RTCRX509GENERALNAMECHOICE_END)
1334 return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_UNEXP_GENERAL_SUBTREE_CHOICE,
1335 "Unexpected GeneralSubtree choice %#x", pSubtree->Base.enmChoice);
1336
1337 if (RTAsn1Integer_UnsignedCompareWithU32(&pSubtree->Minimum, 0) != 0)
1338 return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_UNEXP_GENERAL_SUBTREE_MIN,
1339 "Unexpected GeneralSubtree Minimum value: %#llx",
1340 pSubtree->Minimum.uValue);
1341
1342 if (RTAsn1Integer_IsPresent(&pSubtree->Maximum))
1343 return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_UNEXP_GENERAL_SUBTREE_MAX,
1344 "Unexpected GeneralSubtree Maximum value: %#llx",
1345 pSubtree->Maximum.uValue);
1346
1347 return true;
1348}
1349
1350
1351/**
1352 * Grows the array of permitted sub-trees.
1353 *
1354 * @returns success indiciator.
1355 * @param pThis The validator instance.
1356 * @param cAdding The number of subtrees we should grow by
1357 * (relative to the current number of valid
1358 * entries).
1359 */
1360static bool rtCrX509CpvGrowPermittedSubtrees(PRTCRX509CERTPATHSINT pThis, uint32_t cAdding)
1361{
1362 uint32_t cNew = RT_ALIGN_32(pThis->v.cPermittedSubtrees + cAdding, 16);
1363 if (cNew > pThis->v.cPermittedSubtreesAlloc)
1364 {
1365 if (cNew >= _4K)
1366 return rtCrX509CpvFailed(pThis, VERR_NO_MEMORY, "Too many permitted subtrees: %u (cur %u)",
1367 cNew, pThis->v.cPermittedSubtrees);
1368 void *pvNew = RTMemRealloc(pThis->v.papPermittedSubtrees, cNew * sizeof(pThis->v.papPermittedSubtrees[0]));
1369 if (RT_UNLIKELY(!pvNew))
1370 return rtCrX509CpvFailed(pThis, VERR_NO_MEMORY, "Error growing subtrees pointer array from %u to %u elements",
1371 pThis->v.cPermittedSubtreesAlloc, cNew);
1372 pThis->v.papPermittedSubtrees = (PCRTCRX509GENERALSUBTREE *)pvNew;
1373 }
1374 return true;
1375}
1376
1377
1378/**
1379 * Adds a sequence of permitted sub-trees.
1380 *
1381 * We store reference to each individual sub-tree because we must support
1382 * intersection calculation.
1383 *
1384 * @returns success indiciator.
1385 * @param pThis The validator instance.
1386 * @param cSubtrees The number of sub-trees to add.
1387 * @param papSubtrees Array of sub-trees to add.
1388 */
1389static bool rtCrX509CpvAddPermittedSubtrees(PRTCRX509CERTPATHSINT pThis, uint32_t cSubtrees,
1390 PRTCRX509GENERALSUBTREE const *papSubtrees)
1391{
1392 /*
1393 * If the array is empty, assume no permitted names.
1394 */
1395 if (!cSubtrees)
1396 {
1397 pThis->v.fNoPermittedSubtrees = true;
1398 return true;
1399 }
1400
1401 /*
1402 * Grow the array if necessary.
1403 */
1404 if (!rtCrX509CpvGrowPermittedSubtrees(pThis, cSubtrees))
1405 return false;
1406
1407 /*
1408 * Append each subtree to the array.
1409 */
1410 uint32_t iDst = pThis->v.cPermittedSubtrees;
1411 for (uint32_t iSrc = 0; iSrc < cSubtrees; iSrc++)
1412 {
1413 if (!rtCrX509CpvCheckSubtreeValidity(pThis, papSubtrees[iSrc]))
1414 return false;
1415 pThis->v.papPermittedSubtrees[iDst] = papSubtrees[iSrc];
1416 iDst++;
1417 }
1418 pThis->v.cPermittedSubtrees = iDst;
1419
1420 return true;
1421}
1422
1423
1424/**
1425 * Adds a one permitted sub-tree.
1426 *
1427 * We store reference to each individual sub-tree because we must support
1428 * intersection calculation.
1429 *
1430 * @returns success indiciator.
1431 * @param pThis The validator instance.
1432 * @param pSubtree Array of sub-trees to add.
1433 */
1434static bool rtCrX509CpvAddPermittedSubtree(PRTCRX509CERTPATHSINT pThis, PCRTCRX509GENERALSUBTREE pSubtree)
1435{
1436 return rtCrX509CpvAddPermittedSubtrees(pThis, 1, (PRTCRX509GENERALSUBTREE const *)&pSubtree);
1437}
1438
1439
1440/**
1441 * Calculates the intersection between @a pSubtrees and the current permitted
1442 * sub-trees.
1443 *
1444 * @returns Success indicator.
1445 * @param pThis The validator instance.
1446 * @param pSubtrees The sub-tree sequence to intersect with.
1447 */
1448static bool rtCrX509CpvIntersectionPermittedSubtrees(PRTCRX509CERTPATHSINT pThis, PCRTCRX509GENERALSUBTREES pSubtrees)
1449{
1450 /*
1451 * Deal with special cases first.
1452 */
1453 if (pThis->v.fNoPermittedSubtrees)
1454 {
1455 Assert(pThis->v.cPermittedSubtrees == 0);
1456 return true;
1457 }
1458
1459 uint32_t cRight = pSubtrees->cItems;
1460 PRTCRX509GENERALSUBTREE const *papRight = pSubtrees->papItems;
1461 if (cRight == 0)
1462 {
1463 pThis->v.cPermittedSubtrees = 0;
1464 pThis->v.fNoPermittedSubtrees = true;
1465 return true;
1466 }
1467
1468 uint32_t cLeft = pThis->v.cPermittedSubtrees;
1469 PCRTCRX509GENERALSUBTREE *papLeft = pThis->v.papPermittedSubtrees;
1470 if (!cLeft) /* first name constraint, no initial constraint */
1471 return rtCrX509CpvAddPermittedSubtrees(pThis, cRight, papRight);
1472
1473 /*
1474 * Create a new array with the intersection, freeing the old (left) array
1475 * once we're done.
1476 */
1477 bool afRightTags[RTCRX509GENERALNAMECHOICE_END] = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
1478
1479 pThis->v.cPermittedSubtrees = 0;
1480 pThis->v.cPermittedSubtreesAlloc = 0;
1481 pThis->v.papPermittedSubtrees = NULL;
1482
1483 for (uint32_t iRight = 0; iRight < cRight; iRight++)
1484 {
1485 if (!rtCrX509CpvCheckSubtreeValidity(pThis, papRight[iRight]))
1486 return false;
1487
1488 RTCRX509GENERALNAMECHOICE const enmRightChoice = papRight[iRight]->Base.enmChoice;
1489 afRightTags[enmRightChoice] = true;
1490
1491 bool fHaveRight = false;
1492 for (uint32_t iLeft = 0; iLeft < cLeft; iLeft++)
1493 if (papLeft[iLeft]->Base.enmChoice == enmRightChoice)
1494 {
1495 if (RTCrX509GeneralSubtree_Compare(papLeft[iLeft], papRight[iRight]) == 0)
1496 {
1497 if (!fHaveRight)
1498 {
1499 fHaveRight = true;
1500 rtCrX509CpvAddPermittedSubtree(pThis, papLeft[iLeft]);
1501 }
1502 }
1503 else if (RTCrX509GeneralSubtree_ConstraintMatch(papLeft[iLeft], papRight[iRight]))
1504 {
1505 if (!fHaveRight)
1506 {
1507 fHaveRight = true;
1508 rtCrX509CpvAddPermittedSubtree(pThis, papRight[iRight]);
1509 }
1510 }
1511 else if (RTCrX509GeneralSubtree_ConstraintMatch(papRight[iRight], papLeft[iLeft]))
1512 rtCrX509CpvAddPermittedSubtree(pThis, papLeft[iLeft]);
1513 }
1514 }
1515
1516 /*
1517 * Add missing types not specified in the right set.
1518 */
1519 for (uint32_t iLeft = 0; iLeft < cLeft; iLeft++)
1520 if (!afRightTags[papLeft[iLeft]->Base.enmChoice])
1521 rtCrX509CpvAddPermittedSubtree(pThis, papLeft[iLeft]);
1522
1523 /*
1524 * If we ended up with an empty set, no names are permitted any more.
1525 */
1526 if (pThis->v.cPermittedSubtrees == 0)
1527 pThis->v.fNoPermittedSubtrees = true;
1528
1529 RTMemFree(papLeft);
1530 return RT_SUCCESS(pThis->rc);
1531}
1532
1533
1534/**
1535 * Check if the given X.509 name is permitted by current name constraints.
1536 *
1537 * @returns true is permitteded, false if not (caller set error info).
1538 * @param pThis The validator instance.
1539 * @param pName The name to match.
1540 */
1541static bool rtCrX509CpvIsNamePermitted(PRTCRX509CERTPATHSINT pThis, PCRTCRX509NAME pName)
1542{
1543 uint32_t i = pThis->v.cPermittedSubtrees;
1544 if (i == 0)
1545 return !pThis->v.fNoPermittedSubtrees;
1546
1547 while (i-- > 0)
1548 {
1549 PCRTCRX509GENERALSUBTREE pConstraint = pThis->v.papPermittedSubtrees[i];
1550 if ( RTCRX509GENERALNAME_IS_DIRECTORY_NAME(&pConstraint->Base)
1551 && RTCrX509Name_ConstraintMatch(&pConstraint->Base.u.pT4->DirectoryName, pName))
1552 return true;
1553 }
1554 return false;
1555}
1556
1557
1558/**
1559 * Check if the given X.509 general name is permitted by current name
1560 * constraints.
1561 *
1562 * @returns true is permitteded, false if not (caller sets error info).
1563 * @param pThis The validator instance.
1564 * @param pGeneralName The name to match.
1565 */
1566static bool rtCrX509CpvIsGeneralNamePermitted(PRTCRX509CERTPATHSINT pThis, PCRTCRX509GENERALNAME pGeneralName)
1567{
1568 uint32_t i = pThis->v.cPermittedSubtrees;
1569 if (i == 0)
1570 return !pThis->v.fNoPermittedSubtrees;
1571
1572 while (i-- > 0)
1573 if (RTCrX509GeneralName_ConstraintMatch(&pThis->v.papPermittedSubtrees[i]->Base, pGeneralName))
1574 return true;
1575 return false;
1576}
1577
1578
1579/**
1580 * Check if the given X.509 name is excluded by current name constraints.
1581 *
1582 * @returns true if excluded (caller sets error info), false if not explicitly
1583 * excluded.
1584 * @param pThis The validator instance.
1585 * @param pName The name to match.
1586 */
1587static bool rtCrX509CpvIsNameExcluded(PRTCRX509CERTPATHSINT pThis, PCRTCRX509NAME pName)
1588{
1589 uint32_t i = pThis->v.cExcludedSubtrees;
1590 while (i-- > 0)
1591 {
1592 PCRTCRX509GENERALSUBTREES pSubTrees = pThis->v.papExcludedSubtrees[i];
1593 uint32_t j = pSubTrees->cItems;
1594 while (j-- > 0)
1595 {
1596 PCRTCRX509GENERALSUBTREE const pSubTree = pSubTrees->papItems[j];
1597 if ( RTCRX509GENERALNAME_IS_DIRECTORY_NAME(&pSubTree->Base)
1598 && RTCrX509Name_ConstraintMatch(&pSubTree->Base.u.pT4->DirectoryName, pName))
1599 return true;
1600 }
1601 }
1602 return false;
1603}
1604
1605
1606/**
1607 * Check if the given X.509 general name is excluded by current name
1608 * constraints.
1609 *
1610 * @returns true if excluded (caller sets error info), false if not explicitly
1611 * excluded.
1612 * @param pThis The validator instance.
1613 * @param pGeneralName The name to match.
1614 */
1615static bool rtCrX509CpvIsGeneralNameExcluded(PRTCRX509CERTPATHSINT pThis, PCRTCRX509GENERALNAME pGeneralName)
1616{
1617 uint32_t i = pThis->v.cExcludedSubtrees;
1618 while (i-- > 0)
1619 {
1620 PCRTCRX509GENERALSUBTREES pSubTrees = pThis->v.papExcludedSubtrees[i];
1621 uint32_t j = pSubTrees->cItems;
1622 while (j-- > 0)
1623 if (RTCrX509GeneralName_ConstraintMatch(&pSubTrees->papItems[j]->Base, pGeneralName))
1624 return true;
1625 }
1626 return false;
1627}
1628
1629
1630/**
1631 * Creates a new node and inserts it.
1632 *
1633 * @param pThis The path builder & validator instance.
1634 * @param pParent The parent node. NULL for the root node.
1635 * @param iDepth The tree depth to insert at.
1636 * @param pValidPolicy The valid policy of the new node.
1637 * @param pQualifiers The qualifiers of the new node.
1638 * @param pExpectedPolicy The (first) expected polcy of the new node.
1639 */
1640static bool rtCrX509CpvPolicyTreeInsertNew(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHSPOLICYNODE pParent, uint32_t iDepth,
1641 PCRTASN1OBJID pValidPolicy, PCRTCRX509POLICYQUALIFIERINFOS pQualifiers,
1642 PCRTASN1OBJID pExpectedPolicy)
1643{
1644 Assert(iDepth <= pThis->v.cNodes);
1645
1646 PRTCRX509CERTPATHSPOLICYNODE pNode;
1647 pNode = (PRTCRX509CERTPATHSPOLICYNODE)rtCrX509CpvAllocZ(pThis, sizeof(*pNode), "policy tree node");
1648 if (pNode)
1649 {
1650 pNode->pParent = pParent;
1651 if (pParent)
1652 RTListAppend(&pParent->ChildList, &pNode->SiblingEntry);
1653 else
1654 {
1655 Assert(pThis->v.pValidPolicyTree == NULL);
1656 pThis->v.pValidPolicyTree = pNode;
1657 RTListInit(&pNode->SiblingEntry);
1658 }
1659 RTListInit(&pNode->ChildList);
1660 RTListAppend(&pThis->v.paValidPolicyDepthLists[iDepth], &pNode->DepthEntry);
1661
1662 pNode->pValidPolicy = pValidPolicy;
1663 pNode->pPolicyQualifiers = pQualifiers;
1664 pNode->pExpectedPolicyFirst = pExpectedPolicy;
1665 pNode->cMoreExpectedPolicySet = 0;
1666 pNode->papMoreExpectedPolicySet = NULL;
1667 return true;
1668 }
1669 return false;
1670}
1671
1672
1673/**
1674 * Unlinks and frees a node in the valid policy tree.
1675 *
1676 * @param pThis The path builder & validator instance.
1677 * @param pNode The node to destroy.
1678 */
1679static void rtCrX509CpvPolicyTreeDestroyNode(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHSPOLICYNODE pNode)
1680{
1681 Assert(RTListIsEmpty(&pNode->ChildList));
1682 if (pNode->pParent)
1683 RTListNodeRemove(&pNode->SiblingEntry);
1684 else
1685 pThis->v.pValidPolicyTree = NULL;
1686 RTListNodeRemove(&pNode->DepthEntry);
1687 pNode->pParent = NULL;
1688
1689 if (pNode->papMoreExpectedPolicySet)
1690 {
1691 RTMemFree(pNode->papMoreExpectedPolicySet);
1692 pNode->papMoreExpectedPolicySet = NULL;
1693 }
1694 RTMemFree(pNode);
1695}
1696
1697
1698/**
1699 * Unlinks and frees a sub-tree in the valid policy tree.
1700 *
1701 * @param pThis The path builder & validator instance.
1702 * @param pNode The node that is the root of the subtree.
1703 */
1704static void rtCrX509CpvPolicyTreeDestroySubtree(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHSPOLICYNODE pNode)
1705{
1706 if (!RTListIsEmpty(&pNode->ChildList))
1707 {
1708 PRTCRX509CERTPATHSPOLICYNODE pCur = pNode;
1709 do
1710 {
1711 Assert(!RTListIsEmpty(&pCur->ChildList));
1712
1713 /* Decend until we find a leaf. */
1714 do
1715 pCur = RTListGetFirst(&pCur->ChildList, RTCRX509CERTPATHSPOLICYNODE, SiblingEntry);
1716 while (!RTListIsEmpty(&pCur->ChildList));
1717
1718 /* Remove it and all leafy siblings. */
1719 PRTCRX509CERTPATHSPOLICYNODE pParent = pCur->pParent;
1720 do
1721 {
1722 Assert(pCur != pNode);
1723 rtCrX509CpvPolicyTreeDestroyNode(pThis, pCur);
1724 pCur = RTListGetFirst(&pParent->ChildList, RTCRX509CERTPATHSPOLICYNODE, SiblingEntry);
1725 if (!pCur)
1726 {
1727 pCur = pParent;
1728 pParent = pParent->pParent;
1729 }
1730 } while (RTListIsEmpty(&pCur->ChildList) && pCur != pNode);
1731 } while (pCur != pNode);
1732 }
1733
1734 rtCrX509CpvPolicyTreeDestroyNode(pThis, pNode);
1735}
1736
1737
1738
1739/**
1740 * Destroys the entire policy tree.
1741 *
1742 * @param pThis The path builder & validator instance.
1743 */
1744static void rtCrX509CpvPolicyTreeDestroy(PRTCRX509CERTPATHSINT pThis)
1745{
1746 uint32_t i = pThis->v.cNodes + 1;
1747 while (i-- > 0)
1748 {
1749 PRTCRX509CERTPATHSPOLICYNODE pCur, pNext;
1750 RTListForEachSafe(&pThis->v.paValidPolicyDepthLists[i], pCur, pNext, RTCRX509CERTPATHSPOLICYNODE, DepthEntry)
1751 {
1752 rtCrX509CpvPolicyTreeDestroyNode(pThis, pCur);
1753 }
1754 }
1755}
1756
1757
1758/**
1759 * Removes all leaf nodes at level @a iDepth and above.
1760 *
1761 * @param pThis The path builder & validator instance.
1762 * @param iDepth The depth to start pruning at.
1763 */
1764static void rtCrX509CpvPolicyTreePrune(PRTCRX509CERTPATHSINT pThis, uint32_t iDepth)
1765{
1766 do
1767 {
1768 PRTLISTANCHOR pList = &pThis->v.paValidPolicyDepthLists[iDepth];
1769 PRTCRX509CERTPATHSPOLICYNODE pCur, pNext;
1770 RTListForEachSafe(pList, pCur, pNext, RTCRX509CERTPATHSPOLICYNODE, DepthEntry)
1771 {
1772 if (RTListIsEmpty(&pCur->ChildList))
1773 rtCrX509CpvPolicyTreeDestroyNode(pThis, pCur);
1774 }
1775
1776 } while (iDepth-- > 0);
1777}
1778
1779
1780/**
1781 * Checks if @a pPolicy is the valid policy of a child of @a pNode.
1782 *
1783 * @returns true if in child node, false if not.
1784 * @param pNode The node which children to check.
1785 * @param pPolicy The valid policy to look for among the children.
1786 */
1787static bool rtCrX509CpvPolicyTreeIsChild(PRTCRX509CERTPATHSPOLICYNODE pNode, PCRTASN1OBJID pPolicy)
1788{
1789 PRTCRX509CERTPATHSPOLICYNODE pChild;
1790 RTListForEach(&pNode->ChildList, pChild, RTCRX509CERTPATHSPOLICYNODE, SiblingEntry)
1791 {
1792 if (RTAsn1ObjId_Compare(pChild->pValidPolicy, pPolicy) == 0)
1793 return true;
1794 }
1795 return true;
1796}
1797
1798
1799/**
1800 * Prunes the valid policy tree according to the specified user policy set.
1801 *
1802 * @returns Pointer to the policy object from @a papPolicies if found, NULL if
1803 * no match.
1804 * @param pObjId The object ID to locate at match in the set.
1805 * @param cPolicies The number of policies in @a papPolicies.
1806 * @param papPolicies The policy set to search.
1807 */
1808static PCRTASN1OBJID rtCrX509CpvFindObjIdInPolicySet(PCRTASN1OBJID pObjId, uint32_t cPolicies, PCRTASN1OBJID *papPolicies)
1809{
1810 uint32_t i = cPolicies;
1811 while (i-- > 0)
1812 if (RTAsn1ObjId_Compare(pObjId, papPolicies[i]) == 0)
1813 return papPolicies[i];
1814 return NULL;
1815}
1816
1817
1818/**
1819 * Prunes the valid policy tree according to the specified user policy set.
1820 *
1821 * @returns success indicator (allocates memory)
1822 * @param pThis The path builder & validator instance.
1823 * @param cPolicies The number of policies in @a papPolicies.
1824 * @param papPolicies The user initial policies.
1825 */
1826static bool rtCrX509CpvPolicyTreeIntersect(PRTCRX509CERTPATHSINT pThis, uint32_t cPolicies, PCRTASN1OBJID *papPolicies)
1827{
1828 /*
1829 * 4.1.6.g.i - NULL tree remains NULL.
1830 */
1831 if (!pThis->v.pValidPolicyTree)
1832 return true;
1833
1834 /*
1835 * 4.1.6.g.ii - If the user set includes anyPolicy, the whole tree is the
1836 * result of the intersection.
1837 */
1838 uint32_t i = cPolicies;
1839 while (i-- > 0)
1840 if (RTAsn1ObjId_CompareWithString(papPolicies[i], RTCRX509_ID_CE_CP_ANY_POLICY_OID) == 0)
1841 return true;
1842
1843 /*
1844 * 4.1.6.g.iii - Complicated.
1845 */
1846 PRTCRX509CERTPATHSPOLICYNODE pCur, pNext;
1847 PRTLISTANCHOR pList;
1848
1849 /* 1 & 2: Delete nodes which parent has valid policy == anyPolicy and which
1850 valid policy is neither anyPolicy nor a member of papszPolicies.
1851 While doing so, construct a set of unused user policies that
1852 we'll replace anyPolicy nodes with in step 3. */
1853 uint32_t cPoliciesLeft = 0;
1854 PCRTASN1OBJID *papPoliciesLeft = NULL;
1855 if (cPolicies)
1856 {
1857 papPoliciesLeft = (PCRTASN1OBJID *)rtCrX509CpvAllocZ(pThis, cPolicies * sizeof(papPoliciesLeft[0]), "papPoliciesLeft");
1858 if (!papPoliciesLeft)
1859 return false;
1860 for (i = 0; i < cPolicies; i++)
1861 papPoliciesLeft[i] = papPolicies[i];
1862 }
1863
1864 for (uint32_t iDepth = 1; iDepth <= pThis->v.cNodes; iDepth++)
1865 {
1866 pList = &pThis->v.paValidPolicyDepthLists[iDepth];
1867 RTListForEachSafe(pList, pCur, pNext, RTCRX509CERTPATHSPOLICYNODE, DepthEntry)
1868 {
1869 Assert(pCur->pParent);
1870 if ( RTAsn1ObjId_CompareWithString(pCur->pParent->pValidPolicy, RTCRX509_ID_CE_CP_ANY_POLICY_OID) == 0
1871 && RTAsn1ObjId_CompareWithString(pCur->pValidPolicy, RTCRX509_ID_CE_CP_ANY_POLICY_OID) != 0)
1872 {
1873 PCRTASN1OBJID pFound = rtCrX509CpvFindObjIdInPolicySet(pCur->pValidPolicy, cPolicies, papPolicies);
1874 if (!pFound)
1875 rtCrX509CpvPolicyTreeDestroySubtree(pThis, pCur);
1876 else
1877 for (i = 0; i < cPoliciesLeft; i++)
1878 if (papPoliciesLeft[i] == pFound)
1879 {
1880 cPoliciesLeft--;
1881 if (i < cPoliciesLeft)
1882 papPoliciesLeft[i] = papPoliciesLeft[cPoliciesLeft];
1883 papPoliciesLeft[cPoliciesLeft] = NULL;
1884 break;
1885 }
1886 }
1887 }
1888 }
1889
1890 /*
1891 * 4.1.5.g.iii.3 - Replace anyPolicy nodes on the final tree depth with
1892 * the policies in papPoliciesLeft.
1893 */
1894 pList = &pThis->v.paValidPolicyDepthLists[pThis->v.cNodes];
1895 RTListForEachSafe(pList, pCur, pNext, RTCRX509CERTPATHSPOLICYNODE, DepthEntry)
1896 {
1897 if (RTAsn1ObjId_CompareWithString(pCur->pValidPolicy, RTCRX509_ID_CE_CP_ANY_POLICY_OID) == 0)
1898 {
1899 for (i = 0; i < cPoliciesLeft; i++)
1900 rtCrX509CpvPolicyTreeInsertNew(pThis, pCur->pParent, pThis->v.cNodes - 1,
1901 papPoliciesLeft[i], pCur->pPolicyQualifiers, papPoliciesLeft[i]);
1902 rtCrX509CpvPolicyTreeDestroyNode(pThis, pCur);
1903 }
1904 }
1905
1906 RTMemFree(papPoliciesLeft);
1907
1908 /*
1909 * 4.1.5.g.iii.4 - Prune the tree
1910 */
1911 rtCrX509CpvPolicyTreePrune(pThis, pThis->v.cNodes - 1);
1912
1913 return RT_SUCCESS(pThis->rc);
1914}
1915
1916
1917
1918/**
1919 * Frees the path validator state.
1920 *
1921 * @param pThis The path builder & validator instance.
1922 */
1923static void rtCrX509CpvCleanup(PRTCRX509CERTPATHSINT pThis)
1924{
1925 /*
1926 * Destroy the policy tree and all its nodes. We do this from the bottom
1927 * up via the depth lists, saving annoying tree traversal.
1928 */
1929 if (pThis->v.paValidPolicyDepthLists)
1930 {
1931 rtCrX509CpvPolicyTreeDestroy(pThis);
1932
1933 RTMemFree(pThis->v.paValidPolicyDepthLists);
1934 pThis->v.paValidPolicyDepthLists = NULL;
1935 }
1936
1937 Assert(pThis->v.pValidPolicyTree == NULL);
1938 pThis->v.pValidPolicyTree = NULL;
1939
1940 /*
1941 * Destroy the name constraint arrays.
1942 */
1943 if (pThis->v.papPermittedSubtrees)
1944 {
1945 RTMemFree(pThis->v.papPermittedSubtrees);
1946 pThis->v.papPermittedSubtrees = NULL;
1947 }
1948 pThis->v.cPermittedSubtrees = 0;
1949 pThis->v.cPermittedSubtreesAlloc = 0;
1950 pThis->v.fNoPermittedSubtrees = false;
1951
1952 if (pThis->v.papExcludedSubtrees)
1953 {
1954 RTMemFree(pThis->v.papExcludedSubtrees);
1955 pThis->v.papExcludedSubtrees = NULL;
1956 }
1957 pThis->v.cExcludedSubtrees = 0;
1958
1959 /*
1960 * Clear other pointers.
1961 */
1962 pThis->v.pWorkingIssuer = NULL;
1963 pThis->v.pWorkingPublicKey = NULL;
1964 pThis->v.pWorkingPublicKeyAlgorithm = NULL;
1965 pThis->v.pWorkingPublicKeyParameters = NULL;
1966}
1967
1968
1969/**
1970 * Initializes the state.
1971 *
1972 * Caller must check pThis->rc.
1973 *
1974 * @param pThis The path builder & validator instance.
1975 * @param pTrustAnchor The trust anchor node for the path that we're about
1976 * to validate.
1977 */
1978static void rtCrX509CpvInit(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHNODE pTrustAnchor)
1979{
1980 rtCrX509CpvCleanup(pThis);
1981
1982 /*
1983 * The node count does not include the trust anchor.
1984 */
1985 pThis->v.cNodes = pTrustAnchor->uDepth;
1986
1987 /*
1988 * Valid policy tree starts with an anyPolicy node.
1989 */
1990 uint32_t i = pThis->v.cNodes + 1;
1991 pThis->v.paValidPolicyDepthLists = (PRTLISTANCHOR)rtCrX509CpvAllocZ(pThis, i * sizeof(RTLISTANCHOR),
1992 "paValidPolicyDepthLists");
1993 if (RT_UNLIKELY(!pThis->v.paValidPolicyDepthLists))
1994 return;
1995 while (i-- > 0)
1996 RTListInit(&pThis->v.paValidPolicyDepthLists[i]);
1997
1998 if (!rtCrX509CpvPolicyTreeInsertNew(pThis, NULL, 0 /* iDepth*/, &pThis->AnyPolicyObjId, NULL, &pThis->AnyPolicyObjId))
1999 return;
2000 Assert(!RTListIsEmpty(&pThis->v.paValidPolicyDepthLists[0])); Assert(pThis->v.pValidPolicyTree);
2001
2002 /*
2003 * Name constrains.
2004 */
2005 if (pThis->pInitialPermittedSubtrees)
2006 rtCrX509CpvAddPermittedSubtrees(pThis, pThis->pInitialPermittedSubtrees->cItems,
2007 pThis->pInitialPermittedSubtrees->papItems);
2008 if (pThis->pInitialExcludedSubtrees)
2009 rtCrX509CpvAddExcludedSubtrees(pThis, pThis->pInitialExcludedSubtrees);
2010
2011 /*
2012 * Counters.
2013 */
2014 pThis->v.cExplicitPolicy = pThis->cInitialExplicitPolicy;
2015 pThis->v.cInhibitPolicyMapping = pThis->cInitialPolicyMappingInhibit;
2016 pThis->v.cInhibitAnyPolicy = pThis->cInitialInhibitAnyPolicy;
2017 pThis->v.cMaxPathLength = pThis->v.cNodes;
2018
2019 /*
2020 * Certificate info from the trust anchor.
2021 */
2022 if (pTrustAnchor->pCert)
2023 {
2024 PCRTCRX509TBSCERTIFICATE const pTbsCert = &pTrustAnchor->pCert->TbsCertificate;
2025 pThis->v.pWorkingIssuer = &pTbsCert->Subject;
2026 pThis->v.pWorkingPublicKey = &pTbsCert->SubjectPublicKeyInfo.SubjectPublicKey;
2027 pThis->v.pWorkingPublicKeyAlgorithm = &pTbsCert->SubjectPublicKeyInfo.Algorithm.Algorithm;
2028 pThis->v.pWorkingPublicKeyParameters = &pTbsCert->SubjectPublicKeyInfo.Algorithm.Parameters;
2029 }
2030 else
2031 {
2032 Assert(pTrustAnchor->pCertCtx); Assert(pTrustAnchor->pCertCtx->pTaInfo);
2033
2034 PCRTCRTAFTRUSTANCHORINFO const pTaInfo = pTrustAnchor->pCertCtx->pTaInfo;
2035 pThis->v.pWorkingIssuer = &pTaInfo->CertPath.TaName;
2036 pThis->v.pWorkingPublicKey = &pTaInfo->PubKey.SubjectPublicKey;
2037 pThis->v.pWorkingPublicKeyAlgorithm = &pTaInfo->PubKey.Algorithm.Algorithm;
2038 pThis->v.pWorkingPublicKeyParameters = &pTaInfo->PubKey.Algorithm.Parameters;
2039 }
2040 if ( !RTASN1CORE_IS_PRESENT(&pThis->v.pWorkingPublicKeyParameters->u.Core)
2041 || pThis->v.pWorkingPublicKeyParameters->enmType == RTASN1TYPE_NULL)
2042 pThis->v.pWorkingPublicKeyParameters = NULL;
2043}
2044
2045
2046/**
2047 * This does basic trust anchor checks (similar to 6.1.3.a) before starting on
2048 * the RFC-5280 algorithm.
2049 */
2050static bool rtCrX509CpvMaybeCheckTrustAnchor(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHNODE pTrustAnchor)
2051{
2052 /*
2053 * This is optional (not part of RFC-5280) and we need a full certificate
2054 * structure to do it.
2055 */
2056 if (!(pThis->fFlags & RTCRX509CERTPATHSINT_F_CHECK_TRUST_ANCHOR))
2057 return true;
2058
2059 PCRTCRX509CERTIFICATE const pCert = pTrustAnchor->pCert;
2060 if (!pCert)
2061 return true;
2062
2063 /*
2064 * Verify the certificate signature if self-signed.
2065 */
2066 if (RTCrX509Certificate_IsSelfSigned(pCert))
2067 {
2068 int rc = RTCrX509Certificate_VerifySignature(pCert, pThis->v.pWorkingPublicKeyAlgorithm,
2069 pThis->v.pWorkingPublicKeyParameters, pThis->v.pWorkingPublicKey,
2070 pThis->pErrInfo);
2071 if (RT_FAILURE(rc))
2072 {
2073 pThis->rc = rc;
2074 return false;
2075 }
2076 }
2077
2078 /*
2079 * Verify that the certificate is valid at the specified time.
2080 */
2081 AssertCompile(sizeof(pThis->szTmp) >= 36 * 3);
2082 if ( (pThis->fFlags & RTCRX509CERTPATHSINT_F_VALID_TIME)
2083 && !RTCrX509Validity_IsValidAtTimeSpec(&pCert->TbsCertificate.Validity, &pThis->ValidTime))
2084 return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_NOT_VALID_AT_TIME,
2085 "Certificate is not valid (ValidTime=%s Validity=[%s...%s])",
2086 RTTimeSpecToString(&pThis->ValidTime, &pThis->szTmp[0], 36),
2087 RTTimeToString(&pCert->TbsCertificate.Validity.NotBefore.Time, &pThis->szTmp[36], 36),
2088 RTTimeToString(&pCert->TbsCertificate.Validity.NotAfter.Time, &pThis->szTmp[2*36], 36) );
2089
2090 /*
2091 * Verified that the certficiate is not revoked.
2092 */
2093 /** @todo rainy day. */
2094
2095 /*
2096 * If non-leaf certificate CA must be set, if basic constraints are present.
2097 */
2098 if (pTrustAnchor->pParent)
2099 {
2100 if (RTAsn1Integer_UnsignedCompareWithU32(&pTrustAnchor->pCert->TbsCertificate.T0.Version, RTCRX509TBSCERTIFICATE_V3) != 0)
2101 return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_NOT_V3_CERT,
2102 "Only version 3 TA certificates are supported (Version=%llu)",
2103 pTrustAnchor->pCert->TbsCertificate.T0.Version.uValue);
2104 PCRTCRX509BASICCONSTRAINTS pBasicConstraints = pTrustAnchor->pCert->TbsCertificate.T3.pBasicConstraints;
2105 if (pBasicConstraints && !pBasicConstraints->CA.fValue)
2106 return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_NOT_CA_CERT,
2107 "Trust anchor certificate is not marked as a CA");
2108 }
2109
2110 return true;
2111}
2112
2113
2114/**
2115 * Step 6.1.3.a.
2116 */
2117static bool rtCrX509CpvCheckBasicCertInfo(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHNODE pNode)
2118{
2119 /*
2120 * 6.1.3.a.1 - Verify the certificate signature.
2121 */
2122 int rc = RTCrX509Certificate_VerifySignature(pNode->pCert, pThis->v.pWorkingPublicKeyAlgorithm,
2123 pThis->v.pWorkingPublicKeyParameters, pThis->v.pWorkingPublicKey,
2124 pThis->pErrInfo);
2125 if (RT_FAILURE(rc))
2126 {
2127 pThis->rc = rc;
2128 return false;
2129 }
2130
2131 /*
2132 * 6.1.3.a.2 - Verify that the certificate is valid at the specified time.
2133 */
2134 AssertCompile(sizeof(pThis->szTmp) >= 36 * 3);
2135 if ( (pThis->fFlags & RTCRX509CERTPATHSINT_F_VALID_TIME)
2136 && !RTCrX509Validity_IsValidAtTimeSpec(&pNode->pCert->TbsCertificate.Validity, &pThis->ValidTime))
2137 return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_NOT_VALID_AT_TIME,
2138 "Certificate is not valid (ValidTime=%s Validity=[%s...%s])",
2139 RTTimeSpecToString(&pThis->ValidTime, &pThis->szTmp[0], 36),
2140 RTTimeToString(&pNode->pCert->TbsCertificate.Validity.NotBefore.Time, &pThis->szTmp[36], 36),
2141 RTTimeToString(&pNode->pCert->TbsCertificate.Validity.NotAfter.Time, &pThis->szTmp[2*36], 36) );
2142
2143 /*
2144 * 6.1.3.a.3 - Verified that the certficiate is not revoked.
2145 */
2146 /** @todo rainy day. */
2147
2148 /*
2149 * 6.1.3.a.4 - Check the issuer name.
2150 */
2151 if (!RTCrX509Name_MatchByRfc5280(&pNode->pCert->TbsCertificate.Issuer, pThis->v.pWorkingIssuer))
2152 return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_ISSUER_MISMATCH, "Issuer mismatch");
2153
2154 return true;
2155}
2156
2157
2158/**
2159 * Step 6.1.3.b-c.
2160 */
2161static bool rtCrX509CpvCheckNameConstraints(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHNODE pNode)
2162{
2163 if (pThis->v.fNoPermittedSubtrees)
2164 return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_NO_PERMITTED_NAMES, "No permitted subtrees");
2165
2166 if ( pNode->pCert->TbsCertificate.Subject.cItems > 0
2167 && ( !rtCrX509CpvIsNamePermitted(pThis, &pNode->pCert->TbsCertificate.Subject)
2168 || rtCrX509CpvIsNameExcluded(pThis, &pNode->pCert->TbsCertificate.Subject)) )
2169 return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_NAME_NOT_PERMITTED,
2170 "Subject name is not permitted by current name constraints");
2171
2172 PCRTCRX509GENERALNAMES pAltSubjectName = pNode->pCert->TbsCertificate.T3.pAltSubjectName;
2173 if (pAltSubjectName)
2174 {
2175 uint32_t i = pAltSubjectName->cItems;
2176 while (i-- > 0)
2177 if ( !rtCrX509CpvIsGeneralNamePermitted(pThis, pAltSubjectName->papItems[i])
2178 || rtCrX509CpvIsGeneralNameExcluded(pThis, pAltSubjectName->papItems[i]))
2179 return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_ALT_NAME_NOT_PERMITTED,
2180 "Alternative name #%u is is not permitted by current name constraints", i);
2181 }
2182
2183 return true;
2184}
2185
2186
2187/**
2188 * Step 6.1.3.d-f.
2189 */
2190static bool rtCrX509CpvWorkValidPolicyTree(PRTCRX509CERTPATHSINT pThis, uint32_t iDepth, PRTCRX509CERTPATHNODE pNode,
2191 bool fSelfIssued)
2192{
2193 PCRTCRX509CERTIFICATEPOLICIES pPolicies = pNode->pCert->TbsCertificate.T3.pCertificatePolicies;
2194 if (pPolicies)
2195 {
2196 /*
2197 * 6.1.3.d.1 - Work the certiciate policies into the tree.
2198 */
2199 PRTCRX509CERTPATHSPOLICYNODE pCur;
2200 PRTLISTANCHOR pListAbove = &pThis->v.paValidPolicyDepthLists[iDepth - 1];
2201 uint32_t iAnyPolicy = UINT32_MAX;
2202 uint32_t i = pPolicies->cItems;
2203 while (i-- > 0)
2204 {
2205 PCRTCRX509POLICYQUALIFIERINFOS const pQualifiers = &pPolicies->papItems[i]->PolicyQualifiers;
2206 PCRTASN1OBJID const pIdP = &pPolicies->papItems[i]->PolicyIdentifier;
2207 if (RTAsn1ObjId_CompareWithString(pIdP, RTCRX509_ID_CE_CP_ANY_POLICY_OID) == 0)
2208 {
2209 iAnyPolicy++;
2210 continue;
2211 }
2212
2213 /*
2214 * 6.1.3.d.1.i - Create children for matching policies.
2215 */
2216 uint32_t cMatches = 0;
2217 RTListForEach(pListAbove, pCur, RTCRX509CERTPATHSPOLICYNODE, DepthEntry)
2218 {
2219 bool fMatch = RTAsn1ObjId_Compare(pCur->pExpectedPolicyFirst, pIdP) == 0;
2220 if (!fMatch && pCur->cMoreExpectedPolicySet)
2221 for (uint32_t j = 0; !fMatch && j < pCur->cMoreExpectedPolicySet; j++)
2222 fMatch = RTAsn1ObjId_Compare(pCur->papMoreExpectedPolicySet[j], pIdP) == 0;
2223 if (fMatch)
2224 {
2225 if (!rtCrX509CpvPolicyTreeInsertNew(pThis, pCur, iDepth, pIdP, pQualifiers, pIdP))
2226 return false;
2227 cMatches++;
2228 }
2229 }
2230
2231 /*
2232 * 6.1.3.d.1.ii - If no matches above do the same for anyPolicy
2233 * nodes, only match with valid policy this time.
2234 */
2235 if (cMatches == 0)
2236 {
2237 RTListForEach(pListAbove, pCur, RTCRX509CERTPATHSPOLICYNODE, DepthEntry)
2238 {
2239 if (RTAsn1ObjId_CompareWithString(pCur->pExpectedPolicyFirst, RTCRX509_ID_CE_CP_ANY_POLICY_OID) == 0)
2240 {
2241 if (!rtCrX509CpvPolicyTreeInsertNew(pThis, pCur, iDepth, pIdP, pQualifiers, pIdP))
2242 return false;
2243 }
2244 }
2245 }
2246 }
2247
2248 /*
2249 * 6.1.3.d.2 - If anyPolicy present, make sure all expected policies
2250 * are propagated to the current depth.
2251 */
2252 if ( iAnyPolicy < pPolicies->cItems
2253 && ( pThis->v.cInhibitAnyPolicy > 0
2254 || (pNode->pParent && fSelfIssued) ) )
2255 {
2256 PCRTCRX509POLICYQUALIFIERINFOS pApQ = &pPolicies->papItems[iAnyPolicy]->PolicyQualifiers;
2257 RTListForEach(pListAbove, pCur, RTCRX509CERTPATHSPOLICYNODE, DepthEntry)
2258 {
2259 if (!rtCrX509CpvPolicyTreeIsChild(pCur, pCur->pExpectedPolicyFirst))
2260 rtCrX509CpvPolicyTreeInsertNew(pThis, pCur, iDepth, pCur->pExpectedPolicyFirst, pApQ,
2261 pCur->pExpectedPolicyFirst);
2262 for (uint32_t j = 0; j < pCur->cMoreExpectedPolicySet; j++)
2263 if (!rtCrX509CpvPolicyTreeIsChild(pCur, pCur->papMoreExpectedPolicySet[j]))
2264 rtCrX509CpvPolicyTreeInsertNew(pThis, pCur, iDepth, pCur->papMoreExpectedPolicySet[j], pApQ,
2265 pCur->papMoreExpectedPolicySet[j]);
2266 }
2267 }
2268 /*
2269 * 6.1.3.d.3 - Prune the tree.
2270 */
2271 else
2272 rtCrX509CpvPolicyTreePrune(pThis, iDepth - 1);
2273 }
2274 else
2275 {
2276 /*
2277 * 6.1.3.e - No policy extension present, set tree to NULL.
2278 */
2279 rtCrX509CpvPolicyTreeDestroy(pThis);
2280 }
2281
2282 /*
2283 * 6.1.3.f - NULL tree check.
2284 */
2285 if ( pThis->v.pValidPolicyTree == NULL
2286 && pThis->v.cExplicitPolicy == 0)
2287 return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_NO_VALID_POLICY,
2288 "An explicit policy is called for but the valid policy tree is NULL.");
2289 return RT_SUCCESS(pThis->rc);
2290}
2291
2292
2293/**
2294 * Step 6.1.4.a-b.
2295 */
2296static bool rtCrX509CpvSoakUpPolicyMappings(PRTCRX509CERTPATHSINT pThis, uint32_t iDepth,
2297 PCRTCRX509POLICYMAPPINGS pPolicyMappings)
2298{
2299 /*
2300 * 6.1.4.a - The anyPolicy is not allowed in policy mappings as it would
2301 * allow an evil intermediate certificate to expand the policy
2302 * scope of a certiciate chain without regard to upstream.
2303 */
2304 uint32_t i = pPolicyMappings->cItems;
2305 while (i-- > 0)
2306 {
2307 PCRTCRX509POLICYMAPPING const pOne = pPolicyMappings->papItems[i];
2308 if (RTAsn1ObjId_CompareWithString(&pOne->IssuerDomainPolicy, RTCRX509_ID_CE_CP_ANY_POLICY_OID) == 0)
2309 return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_INVALID_POLICY_MAPPING,
2310 "Invalid policy mapping %#u: IssuerDomainPolicy is anyPolicy.", i);
2311
2312 if (RTAsn1ObjId_CompareWithString(&pOne->SubjectDomainPolicy, RTCRX509_ID_CE_CP_ANY_POLICY_OID) == 0)
2313 return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_INVALID_POLICY_MAPPING,
2314 "Invalid policy mapping %#u: SubjectDomainPolicy is anyPolicy.", i);
2315 }
2316
2317 PRTCRX509CERTPATHSPOLICYNODE pCur, pNext;
2318 if (pThis->v.cInhibitPolicyMapping > 0)
2319 {
2320 /*
2321 * 6.1.4.b.1 - Do the policy mapping.
2322 */
2323 i = pPolicyMappings->cItems;
2324 while (i-- > 0)
2325 {
2326 PCRTCRX509POLICYMAPPING const pOne = pPolicyMappings->papItems[i];
2327
2328 uint32_t cFound = 0;
2329 RTListForEach(&pThis->v.paValidPolicyDepthLists[iDepth], pCur, RTCRX509CERTPATHSPOLICYNODE, DepthEntry)
2330 {
2331 if (RTAsn1ObjId_Compare(pCur->pValidPolicy, &pOne->IssuerDomainPolicy))
2332 {
2333 if (!pCur->fAlreadyMapped)
2334 {
2335 pCur->fAlreadyMapped = true;
2336 pCur->pExpectedPolicyFirst = &pOne->SubjectDomainPolicy;
2337 }
2338 else
2339 {
2340 uint32_t iExpected = pCur->cMoreExpectedPolicySet;
2341 void *pvNew = RTMemRealloc(pCur->papMoreExpectedPolicySet,
2342 sizeof(pCur->papMoreExpectedPolicySet[0]) * (iExpected + 1));
2343 if (!pvNew)
2344 return rtCrX509CpvFailed(pThis, VERR_NO_MEMORY,
2345 "Error growing papMoreExpectedPolicySet array (cur %u, depth %u)",
2346 pCur->cMoreExpectedPolicySet, iDepth);
2347 pCur->papMoreExpectedPolicySet = (PCRTASN1OBJID *)pvNew;
2348 pCur->papMoreExpectedPolicySet[iExpected] = &pOne->SubjectDomainPolicy;
2349 pCur->cMoreExpectedPolicySet = iExpected + 1;
2350 }
2351 cFound++;
2352 }
2353 }
2354
2355 /*
2356 * If no mapping took place, look for an anyPolicy node.
2357 */
2358 if (!cFound)
2359 {
2360 RTListForEach(&pThis->v.paValidPolicyDepthLists[iDepth], pCur, RTCRX509CERTPATHSPOLICYNODE, DepthEntry)
2361 {
2362 if (RTAsn1ObjId_CompareWithString(pCur->pValidPolicy, RTCRX509_ID_CE_CP_ANY_POLICY_OID) == 0)
2363 {
2364 if (!rtCrX509CpvPolicyTreeInsertNew(pThis, pCur->pParent, iDepth,
2365 &pOne->IssuerDomainPolicy,
2366 pCur->pPolicyQualifiers,
2367 &pOne->SubjectDomainPolicy))
2368 return false;
2369 break;
2370 }
2371 }
2372 }
2373 }
2374 }
2375 else
2376 {
2377 /*
2378 * 6.1.4.b.2 - Remove matching policies from the tree if mapping is
2379 * inhibited and prune the tree.
2380 */
2381 uint32_t cRemoved = 0;
2382 i = pPolicyMappings->cItems;
2383 while (i-- > 0)
2384 {
2385 PCRTCRX509POLICYMAPPING const pOne = pPolicyMappings->papItems[i];
2386 RTListForEachSafe(&pThis->v.paValidPolicyDepthLists[iDepth], pCur, pNext, RTCRX509CERTPATHSPOLICYNODE, DepthEntry)
2387 {
2388 if (RTAsn1ObjId_Compare(pCur->pValidPolicy, &pOne->IssuerDomainPolicy))
2389 {
2390 rtCrX509CpvPolicyTreeDestroyNode(pThis, pCur);
2391 cRemoved++;
2392 }
2393 }
2394 }
2395 if (cRemoved)
2396 rtCrX509CpvPolicyTreePrune(pThis, iDepth - 1);
2397 }
2398
2399 return true;
2400}
2401
2402
2403/**
2404 * Step 6.1.4.d-f & 6.1.5.c-e.
2405 */
2406static void rtCrX509CpvSetWorkingPublicKeyInfo(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHNODE pNode)
2407{
2408 PCRTCRX509TBSCERTIFICATE const pTbsCert = &pNode->pCert->TbsCertificate;
2409
2410 /*
2411 * 6.1.4.d - The public key.
2412 */
2413 pThis->v.pWorkingPublicKey = &pTbsCert->SubjectPublicKeyInfo.SubjectPublicKey;
2414
2415 /*
2416 * 6.1.4.e - The public key parameters. Use new ones if present, keep old
2417 * if the algorithm remains the same.
2418 */
2419 if ( RTASN1CORE_IS_PRESENT(&pTbsCert->SubjectPublicKeyInfo.Algorithm.Parameters.u.Core)
2420 && pTbsCert->SubjectPublicKeyInfo.Algorithm.Parameters.enmType != RTASN1TYPE_NULL)
2421 pThis->v.pWorkingPublicKeyParameters = &pTbsCert->SubjectPublicKeyInfo.Algorithm.Parameters;
2422 else if ( pThis->v.pWorkingPublicKeyParameters
2423 && RTAsn1ObjId_Compare(pThis->v.pWorkingPublicKeyAlgorithm, &pTbsCert->SubjectPublicKeyInfo.Algorithm.Algorithm) != 0)
2424 pThis->v.pWorkingPublicKeyParameters = NULL;
2425
2426 /*
2427 * 6.1.4.f - The public algorithm.
2428 */
2429 pThis->v.pWorkingPublicKeyAlgorithm = &pTbsCert->SubjectPublicKeyInfo.Algorithm.Algorithm;
2430}
2431
2432
2433/**
2434 * Step 6.1.4.g.
2435 */
2436static bool rtCrX509CpvSoakUpNameConstraints(PRTCRX509CERTPATHSINT pThis, PCRTCRX509NAMECONSTRAINTS pNameConstraints)
2437{
2438 if (pNameConstraints->T0.PermittedSubtrees.cItems > 0)
2439 if (!rtCrX509CpvIntersectionPermittedSubtrees(pThis, &pNameConstraints->T0.PermittedSubtrees))
2440 return false;
2441
2442 if (pNameConstraints->T1.ExcludedSubtrees.cItems > 0)
2443 if (!rtCrX509CpvAddExcludedSubtrees(pThis, &pNameConstraints->T1.ExcludedSubtrees))
2444 return false;
2445
2446 return true;
2447}
2448
2449
2450/**
2451 * Step 6.1.4.i.
2452 */
2453static bool rtCrX509CpvSoakUpPolicyConstraints(PRTCRX509CERTPATHSINT pThis, PCRTCRX509POLICYCONSTRAINTS pPolicyConstraints)
2454{
2455 if (RTAsn1Integer_IsPresent(&pPolicyConstraints->RequireExplicitPolicy))
2456 {
2457 if (RTAsn1Integer_UnsignedCompareWithU32(&pPolicyConstraints->RequireExplicitPolicy, pThis->v.cExplicitPolicy) < 0)
2458 pThis->v.cExplicitPolicy = pPolicyConstraints->RequireExplicitPolicy.uValue.s.Lo;
2459 }
2460
2461 if (RTAsn1Integer_IsPresent(&pPolicyConstraints->InhibitPolicyMapping))
2462 {
2463 if (RTAsn1Integer_UnsignedCompareWithU32(&pPolicyConstraints->InhibitPolicyMapping, pThis->v.cInhibitPolicyMapping) < 0)
2464 pThis->v.cInhibitPolicyMapping = pPolicyConstraints->InhibitPolicyMapping.uValue.s.Lo;
2465 }
2466 return true;
2467}
2468
2469
2470/**
2471 * Step 6.1.4.j.
2472 */
2473static bool rtCrX509CpvSoakUpInhibitAnyPolicy(PRTCRX509CERTPATHSINT pThis, PCRTASN1INTEGER pInhibitAnyPolicy)
2474{
2475 if (RTAsn1Integer_UnsignedCompareWithU32(pInhibitAnyPolicy, pThis->v.cInhibitAnyPolicy) < 0)
2476 pThis->v.cInhibitAnyPolicy = pInhibitAnyPolicy->uValue.s.Lo;
2477 return true;
2478}
2479
2480
2481/**
2482 * Steps 6.1.4.k, 6.1.4.l, 6.1.4.m, and 6.1.4.n.
2483 */
2484static bool rtCrX509CpvCheckAndSoakUpBasicConstraintsAndKeyUsage(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHNODE pNode,
2485 bool fSelfIssued)
2486{
2487 /* 6.1.4.k - If basic constraints present, CA must be set. */
2488 if (RTAsn1Integer_UnsignedCompareWithU32(&pNode->pCert->TbsCertificate.T0.Version, RTCRX509TBSCERTIFICATE_V3) != 0)
2489 {
2490 /* Note! Add flags if support for older certificates is needed later. */
2491 return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_NOT_V3_CERT,
2492 "Only version 3 certificates are supported (Version=%llu)",
2493 pNode->pCert->TbsCertificate.T0.Version.uValue);
2494 }
2495 PCRTCRX509BASICCONSTRAINTS pBasicConstraints = pNode->pCert->TbsCertificate.T3.pBasicConstraints;
2496 if (pBasicConstraints)
2497 {
2498 if (!pBasicConstraints->CA.fValue)
2499 return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_NOT_CA_CERT,
2500 "Intermediate certificate (#%u) is not marked as a CA", pThis->v.iNode);
2501 }
2502
2503 /* 6.1.4.l - Work cMaxPathLength. */
2504 if (!fSelfIssued)
2505 {
2506 if (pThis->v.cMaxPathLength > 0)
2507 pThis->v.cMaxPathLength--;
2508 else
2509 return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_MAX_PATH_LENGTH,
2510 "Hit max path length at node #%u", pThis->v.iNode);
2511 }
2512
2513 /* 6.1.4.m - Update cMaxPathLength if basic constrain field is present and smaller. */
2514 if (pBasicConstraints)
2515 {
2516 if (RTAsn1Integer_IsPresent(&pBasicConstraints->PathLenConstraint))
2517 if (RTAsn1Integer_UnsignedCompareWithU32(&pBasicConstraints->PathLenConstraint, pThis->v.cMaxPathLength) < 0)
2518 pThis->v.cMaxPathLength = pBasicConstraints->PathLenConstraint.uValue.s.Lo;
2519 }
2520
2521 /* 6.1.4.n - Require keyCertSign in key usage if the extension is present. */
2522 PCRTCRX509TBSCERTIFICATE const pTbsCert = &pNode->pCert->TbsCertificate;
2523 if ( (pTbsCert->T3.fFlags & RTCRX509TBSCERTIFICATE_F_PRESENT_KEY_USAGE)
2524 && !(pTbsCert->T3.fKeyUsage & RTCRX509CERT_KEY_USAGE_F_KEY_CERT_SIGN))
2525 return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_MISSING_KEY_CERT_SIGN,
2526 "Node #%u does not have KeyCertSign set (keyUsage=%#x)",
2527 pThis->v.iNode, pTbsCert->T3.fKeyUsage);
2528
2529 return true;
2530}
2531
2532
2533/**
2534 * Step 6.1.4.o - check out critical extensions.
2535 */
2536static bool rtCrX509CpvCheckCriticalExtensions(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHNODE pNode)
2537{
2538 uint32_t cLeft = pNode->pCert->TbsCertificate.T3.Extensions.cItems;
2539 PRTCRX509EXTENSION const *ppCur = pNode->pCert->TbsCertificate.T3.Extensions.papItems;
2540 while (cLeft-- > 0)
2541 {
2542 PCRTCRX509EXTENSION const pCur = *ppCur;
2543 if (pCur->Critical.fValue)
2544 {
2545 if ( RTAsn1ObjId_CompareWithString(&pCur->ExtnId, RTCRX509_ID_CE_KEY_USAGE_OID) != 0
2546 && RTAsn1ObjId_CompareWithString(&pCur->ExtnId, RTCRX509_ID_CE_SUBJECT_ALT_NAME_OID) != 0
2547 && RTAsn1ObjId_CompareWithString(&pCur->ExtnId, RTCRX509_ID_CE_ISSUER_ALT_NAME_OID) != 0
2548 && RTAsn1ObjId_CompareWithString(&pCur->ExtnId, RTCRX509_ID_CE_BASIC_CONSTRAINTS_OID) != 0
2549 && RTAsn1ObjId_CompareWithString(&pCur->ExtnId, RTCRX509_ID_CE_NAME_CONSTRAINTS_OID) != 0
2550 && RTAsn1ObjId_CompareWithString(&pCur->ExtnId, RTCRX509_ID_CE_CERTIFICATE_POLICIES_OID) != 0
2551 && RTAsn1ObjId_CompareWithString(&pCur->ExtnId, RTCRX509_ID_CE_POLICY_MAPPINGS_OID) != 0
2552 && RTAsn1ObjId_CompareWithString(&pCur->ExtnId, RTCRX509_ID_CE_POLICY_CONSTRAINTS_OID) != 0
2553 && RTAsn1ObjId_CompareWithString(&pCur->ExtnId, RTCRX509_ID_CE_EXT_KEY_USAGE_OID) != 0
2554 && RTAsn1ObjId_CompareWithString(&pCur->ExtnId, RTCRX509_ID_CE_INHIBIT_ANY_POLICY_OID) != 0
2555 && RTAsn1ObjId_CompareWithString(&pCur->ExtnId, RTCR_APPLE_CS_DEVID_APPLICATION_OID) != 0
2556 && RTAsn1ObjId_CompareWithString(&pCur->ExtnId, RTCR_APPLE_CS_DEVID_INSTALLER_OID) != 0
2557 && RTAsn1ObjId_CompareWithString(&pCur->ExtnId, RTCR_APPLE_CS_DEVID_KEXT_OID) != 0
2558 )
2559 return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_UNKNOWN_CRITICAL_EXTENSION,
2560 "Node #%u has an unknown critical extension: %s", pThis->v.iNode, pCur->ExtnId.szObjId);
2561 }
2562
2563 ppCur++;
2564 }
2565
2566 return true;
2567}
2568
2569
2570/**
2571 * Step 6.1.5 - The wrapping up.
2572 */
2573static bool rtCrX509CpvWrapUp(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHNODE pNode)
2574{
2575 Assert(!pNode->pParent); Assert(pThis->pTarget == pNode->pCert);
2576
2577 /*
2578 * 6.1.5.a - Decrement explicit policy.
2579 */
2580 if (pThis->v.cExplicitPolicy > 0)
2581 pThis->v.cExplicitPolicy--;
2582
2583 /*
2584 * 6.1.5.b - Policy constraints and explicit policy.
2585 */
2586 PCRTCRX509POLICYCONSTRAINTS pPolicyConstraints = pNode->pCert->TbsCertificate.T3.pPolicyConstraints;
2587 if ( pPolicyConstraints
2588 && RTAsn1Integer_IsPresent(&pPolicyConstraints->RequireExplicitPolicy)
2589 && RTAsn1Integer_UnsignedCompareWithU32(&pPolicyConstraints->RequireExplicitPolicy, 0) == 0)
2590 pThis->v.cExplicitPolicy = 0;
2591
2592 /*
2593 * 6.1.5.c-e - Update working public key info.
2594 */
2595 rtCrX509CpvSetWorkingPublicKeyInfo(pThis, pNode);
2596
2597 /*
2598 * 6.1.5.f - Critical extensions.
2599 */
2600 if (!rtCrX509CpvCheckCriticalExtensions(pThis, pNode))
2601 return false;
2602
2603 /*
2604 * 6.1.5.g - Calculate the intersection between the user initial policy set
2605 * and the valid policy tree.
2606 */
2607 rtCrX509CpvPolicyTreeIntersect(pThis, pThis->cInitialUserPolicySet, pThis->papInitialUserPolicySet);
2608
2609 if ( pThis->v.cExplicitPolicy == 0
2610 && pThis->v.pValidPolicyTree == NULL)
2611 return rtCrX509CpvFailed(pThis, VERR_CR_X509_CPV_NO_VALID_POLICY, "No valid policy (wrap-up).");
2612
2613 return true;
2614}
2615
2616
2617/**
2618 * Worker that validates one path.
2619 *
2620 * This implements the algorithm in RFC-5280, section 6.1, with exception of
2621 * the CRL checks in 6.1.3.a.3.
2622 *
2623 * @returns success indicator.
2624 * @param pThis The path builder & validator instance.
2625 * @param pTrustAnchor The trust anchor node.
2626 */
2627static bool rtCrX509CpvOneWorker(PRTCRX509CERTPATHSINT pThis, PRTCRX509CERTPATHNODE pTrustAnchor)
2628{
2629 /*
2630 * Init.
2631 */
2632 rtCrX509CpvInit(pThis, pTrustAnchor);
2633 if (RT_SUCCESS(pThis->rc))
2634 {
2635 /*
2636 * Maybe do some trust anchor checks.
2637 */
2638 if (!rtCrX509CpvMaybeCheckTrustAnchor(pThis, pTrustAnchor))
2639 {
2640 AssertStmt(RT_FAILURE_NP(pThis->rc), pThis->rc = VERR_CR_X509_CERTPATHS_INTERNAL_ERROR);
2641 return false;
2642 }
2643
2644 /*
2645 * Special case, target certificate is trusted.
2646 */
2647 if (!pTrustAnchor->pParent)
2648 return true; /* rtCrX509CpvWrapUp should not be needed here. */
2649
2650 /*
2651 * Normal processing.
2652 */
2653 PRTCRX509CERTPATHNODE pNode = pTrustAnchor->pParent;
2654 uint32_t iNode = pThis->v.iNode = 1; /* We count to cNode (inclusive). Same a validation tree depth. */
2655 while (pNode && RT_SUCCESS(pThis->rc))
2656 {
2657 /*
2658 * Basic certificate processing.
2659 */
2660 if (!rtCrX509CpvCheckBasicCertInfo(pThis, pNode)) /* Step 6.1.3.a */
2661 break;
2662
2663 bool const fSelfIssued = rtCrX509CertPathsIsSelfIssued(pNode);
2664 if (!fSelfIssued || !pNode->pParent) /* Step 6.1.3.b-c */
2665 if (!rtCrX509CpvCheckNameConstraints(pThis, pNode))
2666 break;
2667
2668 if (!rtCrX509CpvWorkValidPolicyTree(pThis, iNode, pNode, fSelfIssued)) /* Step 6.1.3.d-f */
2669 break;
2670
2671 /*
2672 * If it's the last certificate in the path, do wrap-ups.
2673 */
2674 if (!pNode->pParent) /* Step 6.1.5 */
2675 {
2676 Assert(iNode == pThis->v.cNodes);
2677 if (!rtCrX509CpvWrapUp(pThis, pNode))
2678 break;
2679 AssertRCBreak(pThis->rc);
2680 return true;
2681 }
2682
2683 /*
2684 * Preparations for the next certificate.
2685 */
2686 PCRTCRX509TBSCERTIFICATE const pTbsCert = &pNode->pCert->TbsCertificate;
2687 if ( pTbsCert->T3.pPolicyMappings
2688 && !rtCrX509CpvSoakUpPolicyMappings(pThis, iNode, pTbsCert->T3.pPolicyMappings)) /* Step 6.1.4.a-b */
2689 break;
2690
2691 pThis->v.pWorkingIssuer = &pTbsCert->Subject; /* Step 6.1.4.c */
2692
2693 rtCrX509CpvSetWorkingPublicKeyInfo(pThis, pNode); /* Step 6.1.4.d-f */
2694
2695 if ( pTbsCert->T3.pNameConstraints /* Step 6.1.4.g */
2696 && !rtCrX509CpvSoakUpNameConstraints(pThis, pTbsCert->T3.pNameConstraints))
2697 break;
2698
2699 if (!fSelfIssued) /* Step 6.1.4.h */
2700 {
2701 if (pThis->v.cExplicitPolicy > 0)
2702 pThis->v.cExplicitPolicy--;
2703 if (pThis->v.cInhibitPolicyMapping > 0)
2704 pThis->v.cInhibitPolicyMapping--;
2705 if (pThis->v.cInhibitAnyPolicy > 0)
2706 pThis->v.cInhibitAnyPolicy--;
2707 }
2708
2709 if ( pTbsCert->T3.pPolicyConstraints /* Step 6.1.4.j */
2710 && !rtCrX509CpvSoakUpPolicyConstraints(pThis, pTbsCert->T3.pPolicyConstraints))
2711 break;
2712
2713 if ( pTbsCert->T3.pInhibitAnyPolicy /* Step 6.1.4.j */
2714 && !rtCrX509CpvSoakUpInhibitAnyPolicy(pThis, pTbsCert->T3.pInhibitAnyPolicy))
2715 break;
2716
2717 if (!rtCrX509CpvCheckAndSoakUpBasicConstraintsAndKeyUsage(pThis, pNode, fSelfIssued)) /* Step 6.1.4.k-n */
2718 break;
2719
2720 if (!rtCrX509CpvCheckCriticalExtensions(pThis, pNode)) /* Step 6.1.4.o */
2721 break;
2722
2723 /*
2724 * Advance to the next certificate.
2725 */
2726 pNode = pNode->pParent;
2727 pThis->v.iNode = ++iNode;
2728 }
2729 AssertStmt(RT_FAILURE_NP(pThis->rc), pThis->rc = VERR_CR_X509_CERTPATHS_INTERNAL_ERROR);
2730 }
2731 return false;
2732}
2733
2734
2735RTDECL(int) RTCrX509CertPathsValidateOne(RTCRX509CERTPATHS hCertPaths, uint32_t iPath, PRTERRINFO pErrInfo)
2736{
2737 /*
2738 * Validate the input.
2739 */
2740 PRTCRX509CERTPATHSINT pThis = hCertPaths;
2741 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
2742 AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, VERR_INVALID_HANDLE);
2743 AssertReturn(!(pThis->fFlags & ~RTCRX509CERTPATHSINT_F_VALID_MASK), VERR_INVALID_PARAMETER);
2744 AssertPtrReturn(pThis->pTarget, VERR_INVALID_PARAMETER);
2745 AssertPtrReturn(pThis->pRoot, VERR_INVALID_PARAMETER);
2746 AssertReturn(pThis->rc == VINF_SUCCESS, VERR_INVALID_PARAMETER);
2747
2748 /*
2749 * Locate the path and validate it.
2750 */
2751 int rc;
2752 if (iPath < pThis->cPaths)
2753 {
2754 PRTCRX509CERTPATHNODE pLeaf = rtCrX509CertPathsGetLeafByIndex(pThis, iPath);
2755 if (pLeaf)
2756 {
2757 if (RTCRX509CERTPATHNODE_SRC_IS_TRUSTED(pLeaf->uSrc))
2758 {
2759 pThis->pErrInfo = pErrInfo;
2760 rtCrX509CpvOneWorker(pThis, pLeaf);
2761 pThis->pErrInfo = NULL;
2762 rc = pThis->rc;
2763 pThis->rc = VINF_SUCCESS;
2764 }
2765 else
2766 rc = RTErrInfoSetF(pErrInfo, VERR_CR_X509_NO_TRUST_ANCHOR, "Path #%u is does not have a trust anchor: uSrc=%s",
2767 iPath, rtCrX509CertPathsNodeGetSourceName(pLeaf));
2768 pLeaf->rcVerify = rc;
2769 }
2770 else
2771 rc = VERR_CR_X509_CERTPATHS_INTERNAL_ERROR;
2772 }
2773 else
2774 rc = VERR_NOT_FOUND;
2775 return rc;
2776}
2777
2778
2779RTDECL(int) RTCrX509CertPathsValidateAll(RTCRX509CERTPATHS hCertPaths, uint32_t *pcValidPaths, PRTERRINFO pErrInfo)
2780{
2781 /*
2782 * Validate the input.
2783 */
2784 PRTCRX509CERTPATHSINT pThis = hCertPaths;
2785 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
2786 AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, VERR_INVALID_HANDLE);
2787 AssertReturn(!(pThis->fFlags & ~RTCRX509CERTPATHSINT_F_VALID_MASK), VERR_INVALID_PARAMETER);
2788 AssertPtrReturn(pThis->pTarget, VERR_INVALID_PARAMETER);
2789 AssertPtrReturn(pThis->pRoot, VERR_INVALID_PARAMETER);
2790 AssertReturn(pThis->rc == VINF_SUCCESS, VERR_INVALID_PARAMETER);
2791 AssertPtrNullReturn(pcValidPaths, VERR_INVALID_POINTER);
2792
2793 /*
2794 * Validate the paths.
2795 */
2796 pThis->pErrInfo = pErrInfo;
2797
2798 int rcLastFailure = VINF_SUCCESS;
2799 uint32_t cValidPaths = 0;
2800 PRTCRX509CERTPATHNODE pCurLeaf;
2801 RTListForEach(&pThis->LeafList, pCurLeaf, RTCRX509CERTPATHNODE, ChildListOrLeafEntry)
2802 {
2803 if (RTCRX509CERTPATHNODE_SRC_IS_TRUSTED(pCurLeaf->uSrc))
2804 {
2805 rtCrX509CpvOneWorker(hCertPaths, pCurLeaf);
2806 if (RT_SUCCESS(pThis->rc))
2807 cValidPaths++;
2808 else
2809 rcLastFailure = pThis->rc;
2810 pCurLeaf->rcVerify = pThis->rc;
2811 pThis->rc = VINF_SUCCESS;
2812 }
2813 else
2814 pCurLeaf->rcVerify = VERR_CR_X509_NO_TRUST_ANCHOR;
2815 }
2816
2817 pThis->pErrInfo = NULL;
2818
2819 if (pcValidPaths)
2820 *pcValidPaths = cValidPaths;
2821 if (cValidPaths > 0)
2822 return VINF_SUCCESS;
2823 if (RT_SUCCESS_NP(rcLastFailure))
2824 return RTErrInfoSetF(pErrInfo, VERR_CR_X509_CPV_NO_TRUSTED_PATHS,
2825 "None of the %u path(s) have a trust anchor.", pThis->cPaths);
2826 return rcLastFailure;
2827}
2828
2829
2830RTDECL(uint32_t) RTCrX509CertPathsGetPathCount(RTCRX509CERTPATHS hCertPaths)
2831{
2832 /*
2833 * Validate the input.
2834 */
2835 PRTCRX509CERTPATHSINT pThis = hCertPaths;
2836 AssertPtrReturn(pThis, UINT32_MAX);
2837 AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, UINT32_MAX);
2838 AssertPtrReturn(pThis->pRoot, UINT32_MAX);
2839
2840 /*
2841 * Return data.
2842 */
2843 return pThis->cPaths;
2844}
2845
2846
2847RTDECL(int) RTCrX509CertPathsQueryPathInfo(RTCRX509CERTPATHS hCertPaths, uint32_t iPath,
2848 bool *pfTrusted, uint32_t *pcNodes, PCRTCRX509NAME *ppSubject,
2849 PCRTCRX509SUBJECTPUBLICKEYINFO *ppPublicKeyInfo,
2850 PCRTCRX509CERTIFICATE *ppCert, PCRTCRCERTCTX *ppCertCtx,
2851 int *prcVerify)
2852{
2853 /*
2854 * Validate the input.
2855 */
2856 PRTCRX509CERTPATHSINT pThis = hCertPaths;
2857 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
2858 AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, VERR_INVALID_HANDLE);
2859 AssertPtrReturn(pThis->pRoot, VERR_WRONG_ORDER);
2860 AssertReturn(iPath < pThis->cPaths, VERR_NOT_FOUND);
2861
2862 /*
2863 * Get the data.
2864 */
2865 PRTCRX509CERTPATHNODE pLeaf = rtCrX509CertPathsGetLeafByIndex(pThis, iPath);
2866 AssertReturn(pLeaf, VERR_CR_X509_INTERNAL_ERROR);
2867
2868 if (pfTrusted)
2869 *pfTrusted = RTCRX509CERTPATHNODE_SRC_IS_TRUSTED(pLeaf->uSrc);
2870
2871 if (pcNodes)
2872 *pcNodes = pLeaf->uDepth + 1; /* Includes both trust anchor and target. */
2873
2874 if (ppSubject)
2875 *ppSubject = pLeaf->pCert ? &pLeaf->pCert->TbsCertificate.Subject : &pLeaf->pCertCtx->pTaInfo->CertPath.TaName;
2876
2877 if (ppPublicKeyInfo)
2878 *ppPublicKeyInfo = pLeaf->pCert ? &pLeaf->pCert->TbsCertificate.SubjectPublicKeyInfo : &pLeaf->pCertCtx->pTaInfo->PubKey;
2879
2880 if (ppCert)
2881 *ppCert = pLeaf->pCert;
2882
2883 if (ppCertCtx)
2884 {
2885 if (pLeaf->pCertCtx)
2886 {
2887 uint32_t cRefs = RTCrCertCtxRetain(pLeaf->pCertCtx);
2888 AssertReturn(cRefs != UINT32_MAX, VERR_CR_X509_INTERNAL_ERROR);
2889 }
2890 *ppCertCtx = pLeaf->pCertCtx;
2891 }
2892
2893 if (prcVerify)
2894 *prcVerify = pLeaf->rcVerify;
2895
2896 return VINF_SUCCESS;
2897}
2898
2899
2900RTDECL(uint32_t) RTCrX509CertPathsGetPathLength(RTCRX509CERTPATHS hCertPaths, uint32_t iPath)
2901{
2902 /*
2903 * Validate the input.
2904 */
2905 PRTCRX509CERTPATHSINT pThis = hCertPaths;
2906 AssertPtrReturn(pThis, UINT32_MAX);
2907 AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, UINT32_MAX);
2908 AssertPtrReturn(pThis->pRoot, UINT32_MAX);
2909 AssertReturn(iPath < pThis->cPaths, UINT32_MAX);
2910
2911 /*
2912 * Get the data.
2913 */
2914 PRTCRX509CERTPATHNODE pLeaf = rtCrX509CertPathsGetLeafByIndex(pThis, iPath);
2915 AssertReturn(pLeaf, UINT32_MAX);
2916 return pLeaf->uDepth + 1;
2917}
2918
2919
2920RTDECL(int) RTCrX509CertPathsGetPathVerifyResult(RTCRX509CERTPATHS hCertPaths, uint32_t iPath)
2921{
2922 /*
2923 * Validate the input.
2924 */
2925 PRTCRX509CERTPATHSINT pThis = hCertPaths;
2926 AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
2927 AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, VERR_INVALID_HANDLE);
2928 AssertPtrReturn(pThis->pRoot, VERR_WRONG_ORDER);
2929 AssertReturn(iPath < pThis->cPaths, VERR_NOT_FOUND);
2930
2931 /*
2932 * Get the data.
2933 */
2934 PRTCRX509CERTPATHNODE pLeaf = rtCrX509CertPathsGetLeafByIndex(pThis, iPath);
2935 AssertReturn(pLeaf, VERR_CR_X509_INTERNAL_ERROR);
2936
2937 return pLeaf->rcVerify;
2938}
2939
2940
2941static PRTCRX509CERTPATHNODE rtCrX509CertPathsGetPathNodeByIndexes(PRTCRX509CERTPATHSINT pThis, uint32_t iPath, uint32_t iNode)
2942{
2943 PRTCRX509CERTPATHNODE pNode = rtCrX509CertPathsGetLeafByIndex(pThis, iPath);
2944 Assert(pNode);
2945 if (pNode)
2946 {
2947 if (iNode <= pNode->uDepth)
2948 {
2949 uint32_t uCertDepth = pNode->uDepth - iNode;
2950 while (pNode->uDepth > uCertDepth)
2951 pNode = pNode->pParent;
2952 Assert(pNode);
2953 Assert(pNode && pNode->uDepth == uCertDepth);
2954 return pNode;
2955 }
2956 }
2957
2958 return NULL;
2959}
2960
2961
2962RTDECL(PCRTCRX509CERTIFICATE) RTCrX509CertPathsGetPathNodeCert(RTCRX509CERTPATHS hCertPaths, uint32_t iPath, uint32_t iNode)
2963{
2964 /*
2965 * Validate the input.
2966 */
2967 PRTCRX509CERTPATHSINT pThis = hCertPaths;
2968 AssertPtrReturn(pThis, NULL);
2969 AssertReturn(pThis->u32Magic == RTCRX509CERTPATHSINT_MAGIC, NULL);
2970 AssertPtrReturn(pThis->pRoot, NULL);
2971 AssertReturn(iPath < pThis->cPaths, NULL);
2972
2973 /*
2974 * Get the data.
2975 */
2976 PRTCRX509CERTPATHNODE pNode = rtCrX509CertPathsGetPathNodeByIndexes(pThis, iPath, iNode);
2977 if (pNode)
2978 return pNode->pCert;
2979 return NULL;
2980}
2981
2982
2983/** @} */
2984
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette