VirtualBox

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

Last change on this file since 51823 was 51823, checked in by vboxsync, 11 years ago

Better error message.

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

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