VirtualBox

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

Last change on this file since 52600 was 52600, checked in by vboxsync, 10 years ago

IPRT: Added support for microsoft timestamp counter signatures. This required making the PKCS #7 code accept some of the CMS (RFC-5652) stuff.

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

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