VirtualBox

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

Last change on this file was 101646, checked in by vboxsync, 7 months ago

IPRT/x509-certpaths.cpp: Fixed copy&paste bug in rtCrX509CertPathsGetIssuers where it would try retrieve intermediate certs from the hTrustedStore instead of hUntrustedSTore.

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

© 2023 Oracle
ContactPrivacy policyTerms of Use