VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/crypto/store-cert-add-basic.cpp

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 35.0 KB
Line 
1/* $Id: store-cert-add-basic.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * IPRT - Cryptographic (Certificate) Store, RTCrStoreCertAddFromDir.
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#include "internal/iprt.h"
42#include <iprt/crypto/store.h>
43
44#include <iprt/assert.h>
45#include <iprt/crypto/pem.h>
46#include <iprt/dir.h>
47#include <iprt/err.h>
48#include <iprt/file.h>
49#include <iprt/mem.h>
50#include <iprt/path.h>
51#include <iprt/sha.h>
52#include <iprt/string.h>
53
54#include "x509-internal.h"
55
56
57/*********************************************************************************************************************************
58* Global Variables *
59*********************************************************************************************************************************/
60/** BEGIN CERTIFICATE / END CERTIFICATE. */
61static RTCRPEMMARKERWORD const g_aWords_Certificate[] =
62{
63 { RT_STR_TUPLE("CERTIFICATE") }
64};
65
66/** BEGIN TRUSTED CERTIFICATE / END TRUSTED CERTIFICATE. */
67static RTCRPEMMARKERWORD const g_aWords_TrustedCertificate[] =
68{
69 { RT_STR_TUPLE("TRUSTED") },
70 { RT_STR_TUPLE("CERTIFICATE") }
71};
72
73/** BEGIN X509 CERTIFICATE / END X509 CERTIFICATE. (old) */
74static RTCRPEMMARKERWORD const g_aWords_X509Certificate[] =
75{
76 { RT_STR_TUPLE("X509") },
77 { RT_STR_TUPLE("CERTIFICATE") }
78};
79
80/**
81 * X509 Certificate markers.
82 *
83 * @remark See crypto/pem/pem.h in OpenSSL for a matching list.
84 */
85static RTCRPEMMARKER const g_aX509CertificateMarkers[3] =
86{
87 { g_aWords_Certificate, RT_ELEMENTS(g_aWords_Certificate) },
88 { g_aWords_TrustedCertificate, RT_ELEMENTS(g_aWords_TrustedCertificate) },
89 { g_aWords_X509Certificate, RT_ELEMENTS(g_aWords_X509Certificate) }
90};
91
92
93
94#ifdef RT_STRICT
95/**
96 * Checks if we've found all the certificates already.
97 *
98 * @returns true if all found, false if not.
99 * @param afFound Indicator array.
100 * @param cWanted Number of wanted certificates.
101 */
102DECLINLINE(bool) rtCrStoreAllDone(bool const *afFound, size_t cWanted)
103{
104 while (cWanted-- > 0)
105 if (!afFound[cWanted])
106 return false;
107 return true;
108}
109#endif /* RT_STRICT */
110
111
112/**
113 * Checks if the given certificate specs matches the given wanted poster.
114 *
115 * @returns true if match, false if not.
116 * @param pWanted The certificate wanted poster.
117 * @param cbEncoded The candidate certificate encoded size.
118 * @param paSha1 The candidate certificate SHA-1 fingerprint.
119 * @param paSha512 The candidate certificate SHA-512 fingerprint.
120 * @param pCert The decoded candidate certificate, optional. If not
121 * given the result will be uncertain.
122 */
123DECLINLINE(bool) rtCrStoreIsCertEqualToWanted(PCRTCRCERTWANTED pWanted,
124 size_t cbEncoded,
125 uint8_t const pabSha1[RTSHA1_HASH_SIZE],
126 uint8_t const pabSha512[RTSHA512_HASH_SIZE],
127 PCRTCRX509CERTIFICATE pCert)
128{
129 if ( pWanted->cbEncoded != cbEncoded
130 && pWanted->cbEncoded != 0)
131 return false;
132
133 if ( pWanted->fSha1Fingerprint
134 && memcmp(pWanted->abSha1, pabSha1, RTSHA1_HASH_SIZE) != 0)
135 return false;
136
137 if ( pWanted->fSha512Fingerprint
138 && memcmp(pWanted->abSha512, pabSha512, RTSHA512_HASH_SIZE) != 0)
139 return false;
140
141 if ( pWanted->pszSubject
142 && pCert
143 && !RTCrX509Name_MatchWithString(&pCert->TbsCertificate.Subject, pWanted->pszSubject))
144 return false;
145
146 return true;
147}
148
149
150/**
151 * Checks if a certificate is wanted.
152 *
153 * @returns true if match, false if not.
154 * @param paWanted The certificate wanted posters.
155 * @param cWanted The number of wanted posters.
156 * @param apfFound Found initicators running paralell to @a paWanted.
157 * @param cbEncoded The candidate certificate encoded size.
158 * @param paSha1 The candidate certificate SHA-1 fingerprint.
159 * @param paSha512 The candidate certificate SHA-512 fingerprint.
160 * @param pCert The decoded candidate certificate, optional. If not
161 * given the result will be uncertain.
162 */
163DECLINLINE(bool) rtCrStoreIsCertWanted(PCRTCRCERTWANTED paWanted, size_t cWanted, bool const *pafFound, size_t cbEncoded,
164 uint8_t const pabSha1[RTSHA1_HASH_SIZE], uint8_t const pabSha512[RTSHA512_HASH_SIZE],
165 PCRTCRX509CERTIFICATE pCert)
166{
167 for (size_t iCert = 0; iCert < cWanted; iCert++)
168 if (!pafFound[iCert])
169 if (rtCrStoreIsCertEqualToWanted(&paWanted[iCert], cbEncoded, pabSha1, pabSha512, pCert))
170 return true;
171 return false;
172}
173
174
175/**
176 * Marks a certificate as found after it has been added to the store.
177 *
178 * May actually mark several certificates as found if there are duplicates or
179 * ambiguities in the wanted list.
180 *
181 * @returns true if all have been found, false if more to search for.
182 *
183 * @param apfFound Found initicators running paralell to @a paWanted.
184 * This is what this function updates.
185 * @param paWanted The certificate wanted posters.
186 * @param cWanted The number of wanted posters.
187 * @param cbEncoded The candidate certificate encoded size.
188 * @param paSha1 The candidate certificate SHA-1 fingerprint.
189 * @param paSha512 The candidate certificate SHA-512 fingerprint.
190 * @param pCert The decoded candidate certificate, optional. If not
191 * given the result will be uncertain.
192 */
193static bool rtCrStoreMarkCertFound(bool *pafFound, PCRTCRCERTWANTED paWanted, size_t cWanted, size_t cbEncoded,
194 uint8_t const pabSha1[RTSHA1_HASH_SIZE], uint8_t const pabSha512[RTSHA512_HASH_SIZE],
195 PCRTCRX509CERTIFICATE pCert)
196{
197 size_t cFound = 0;
198 for (size_t iCert = 0; iCert < cWanted; iCert++)
199 if (pafFound[iCert])
200 cFound++;
201 else if (rtCrStoreIsCertEqualToWanted(&paWanted[iCert], cbEncoded, pabSha1, pabSha512, pCert))
202 {
203 pafFound[iCert] = true;
204 cFound++;
205 }
206 return cFound == cWanted;
207}
208
209
210RTDECL(int) RTCrStoreCertAddFromStore(RTCRSTORE hStore, uint32_t fFlags, RTCRSTORE hStoreSrc)
211{
212 /*
213 * Validate input.
214 */
215 AssertReturn(!(fFlags & ~(RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR)), VERR_INVALID_FLAGS);
216
217 /*
218 * Enumerate all the certificates in the source store, adding them to the destination.
219 */
220 RTCRSTORECERTSEARCH Search;
221 int rc = RTCrStoreCertFindAll(hStoreSrc, &Search);
222 if (RT_SUCCESS(rc))
223 {
224 PCRTCRCERTCTX pCertCtx;
225 while ((pCertCtx = RTCrStoreCertSearchNext(hStoreSrc, &Search)) != NULL)
226 {
227 int rc2 = RTCrStoreCertAddEncoded(hStore, pCertCtx->fFlags | (fFlags & RTCRCERTCTX_F_ADD_IF_NOT_FOUND),
228 pCertCtx->pabEncoded, pCertCtx->cbEncoded, NULL);
229 if (RT_FAILURE(rc2))
230 {
231 rc = rc2;
232 if (!(fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR))
233 break;
234 }
235 RTCrCertCtxRelease(pCertCtx);
236 }
237
238 int rc2 = RTCrStoreCertSearchDestroy(hStoreSrc, &Search); AssertRC(rc2);
239 }
240 return rc;
241}
242RT_EXPORT_SYMBOL(RTCrStoreCertAddFromStore);
243
244
245RTDECL(int) RTCrStoreCertAddWantedFromStore(RTCRSTORE hStore, uint32_t fFlags, RTCRSTORE hSrcStore,
246 PCRTCRCERTWANTED paWanted, size_t cWanted, bool *pafFound)
247{
248 /*
249 * Validate input a little.
250 */
251 AssertReturn(!(fFlags & ~(RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR)), VERR_INVALID_FLAGS);
252 fFlags |= RTCRCERTCTX_F_ADD_IF_NOT_FOUND; /* forced */
253
254 AssertReturn(cWanted, VERR_NOT_FOUND);
255 for (uint32_t i = 0; i < cWanted; i++)
256 {
257 AssertReturn(!paWanted[i].pszSubject || *paWanted[i].pszSubject, VERR_INVALID_PARAMETER);
258 AssertReturn( paWanted[i].pszSubject
259 || paWanted[i].fSha1Fingerprint
260 || paWanted[i].fSha512Fingerprint,
261 VERR_INVALID_PARAMETER);
262 }
263
264 /*
265 * Make sure we've got a result array.
266 */
267 bool *pafFoundFree = NULL;
268 if (!pafFound)
269 {
270 pafFound = pafFoundFree = (bool *)RTMemTmpAllocZ(sizeof(bool) * cWanted);
271 AssertReturn(pafFound, VERR_NO_TMP_MEMORY);
272 }
273
274 /*
275 * Enumerate the store entries.
276 */
277 RTCRSTORECERTSEARCH Search;
278 int rc = RTCrStoreCertFindAll(hSrcStore, &Search);
279 if (RT_SUCCESS(rc))
280 {
281 rc = VWRN_NOT_FOUND;
282 PCRTCRCERTCTX pCertCtx;
283 while ((pCertCtx = RTCrStoreCertSearchNext(hSrcStore, &Search)) != NULL)
284 {
285 if ( (pCertCtx->fFlags & RTCRCERTCTX_F_ENC_MASK) == RTCRCERTCTX_F_ENC_X509_DER
286 && pCertCtx->cbEncoded > 0
287 && pCertCtx->pCert)
288 {
289 /*
290 * If the certificate is wanted, try add it to the store.
291 */
292 uint8_t abSha1[RTSHA1_HASH_SIZE];
293 RTSha1(pCertCtx->pabEncoded, pCertCtx->cbEncoded, abSha1);
294 uint8_t abSha512[RTSHA512_HASH_SIZE];
295 RTSha512(pCertCtx->pabEncoded, pCertCtx->cbEncoded, abSha512);
296 if (rtCrStoreIsCertWanted(paWanted, cWanted, pafFound, pCertCtx->cbEncoded, abSha1, abSha512, pCertCtx->pCert))
297 {
298 int rc2 = RTCrStoreCertAddEncoded(hStore,
299 RTCRCERTCTX_F_ENC_X509_DER | (fFlags & RTCRCERTCTX_F_ADD_IF_NOT_FOUND),
300 pCertCtx->pabEncoded, pCertCtx->cbEncoded, NULL /*pErrInfo*/);
301 if (RT_SUCCESS(rc2))
302 {
303 /*
304 * Mark it as found, stop if we've found all.
305 */
306 if (rtCrStoreMarkCertFound(pafFound, paWanted, cWanted,
307 pCertCtx->cbEncoded, abSha1, abSha512, pCertCtx->pCert))
308 {
309 if (RT_SUCCESS(rc))
310 rc = VINF_SUCCESS;
311 RTCrCertCtxRelease(pCertCtx);
312 break;
313 }
314 }
315 else
316 {
317 /*
318 * Some error adding the certificate. Since it cannot be anything with
319 * the encoding, it must be something with the store or resources, so
320 * always return the error status.
321 */
322 rc = rc2;
323 if (!(fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR))
324 {
325 RTCrCertCtxRelease(pCertCtx);
326 break;
327 }
328 }
329 }
330 }
331 RTCrCertCtxRelease(pCertCtx);
332 }
333 int rc2 = RTCrStoreCertSearchDestroy(hSrcStore, &Search);
334 AssertRC(rc2);
335 }
336
337 if (pafFoundFree)
338 RTMemTmpFree(pafFoundFree);
339 return rc;
340}
341RT_EXPORT_SYMBOL(RTCrStoreCertAddWantedFromStore);
342
343
344RTDECL(int) RTCrStoreCertCheckWanted(RTCRSTORE hStore, PCRTCRCERTWANTED paWanted, size_t cWanted, bool *pafFound)
345{
346 /*
347 * Validate input a little.
348 */
349 AssertReturn(cWanted, VERR_NOT_FOUND);
350 for (uint32_t i = 0; i < cWanted; i++)
351 {
352 AssertReturn(!paWanted[i].pszSubject || *paWanted[i].pszSubject, VERR_INVALID_PARAMETER);
353 AssertReturn( paWanted[i].pszSubject
354 || paWanted[i].fSha1Fingerprint
355 || paWanted[i].fSha512Fingerprint,
356 VERR_INVALID_PARAMETER);
357 }
358 AssertPtrReturn(pafFound, VERR_INVALID_POINTER);
359
360 /*
361 * Clear the found array.
362 */
363 for (uint32_t iCert = 0; iCert < cWanted; iCert++)
364 pafFound[iCert] = false;
365
366 /*
367 * Enumerate the store entries.
368 */
369 RTCRSTORECERTSEARCH Search;
370 int rc = RTCrStoreCertFindAll(hStore, &Search);
371 if (RT_SUCCESS(rc))
372 {
373 rc = VWRN_NOT_FOUND;
374 PCRTCRCERTCTX pCertCtx;
375 while ((pCertCtx = RTCrStoreCertSearchNext(hStore, &Search)) != NULL)
376 {
377 if ( (pCertCtx->fFlags & RTCRCERTCTX_F_ENC_MASK) == RTCRCERTCTX_F_ENC_X509_DER
378 && pCertCtx->cbEncoded > 0
379 && pCertCtx->pCert)
380 {
381 /*
382 * Hash it and check if it's wanted. Stop when we've found all.
383 */
384 uint8_t abSha1[RTSHA1_HASH_SIZE];
385 RTSha1(pCertCtx->pabEncoded, pCertCtx->cbEncoded, abSha1);
386 uint8_t abSha512[RTSHA512_HASH_SIZE];
387 RTSha512(pCertCtx->pabEncoded, pCertCtx->cbEncoded, abSha512);
388 if (rtCrStoreMarkCertFound(pafFound, paWanted, cWanted, pCertCtx->cbEncoded, abSha1, abSha512, pCertCtx->pCert))
389 {
390 rc = VINF_SUCCESS;
391 RTCrCertCtxRelease(pCertCtx);
392 break;
393 }
394 }
395 RTCrCertCtxRelease(pCertCtx);
396 }
397 int rc2 = RTCrStoreCertSearchDestroy(hStore, &Search);
398 AssertRC(rc2);
399 }
400
401 return rc;
402}
403RT_EXPORT_SYMBOL(RTCrStoreCertAddWantedFromStore);
404
405
406RTDECL(int) RTCrStoreCertAddFromFile(RTCRSTORE hStore, uint32_t fFlags, const char *pszFilename, PRTERRINFO pErrInfo)
407{
408 AssertReturn(!(fFlags & ~(RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR)), VERR_INVALID_FLAGS);
409
410 size_t cbContent;
411 void *pvContent;
412 int rc = RTFileReadAllEx(pszFilename, 0, 64U*_1M, RTFILE_RDALL_O_DENY_WRITE, &pvContent, &cbContent);
413 if (RT_SUCCESS(rc))
414 {
415 /*
416 * Is it a java key store file?
417 */
418 if ( cbContent > 32
419 && ((uint32_t const *)pvContent)[0] == RT_H2BE_U32_C(UINT32_C(0xfeedfeed)) /* magic */
420 && ((uint32_t const *)pvContent)[1] == RT_H2BE_U32_C(UINT32_C(0x00000002)) /* version */ )
421 rc = RTCrStoreCertAddFromJavaKeyStoreInMem(hStore, fFlags, pvContent, cbContent, pszFilename, pErrInfo);
422 /*
423 * No assume PEM or DER encoded binary certificate.
424 */
425 else if (cbContent)
426 {
427 PCRTCRPEMSECTION pSectionHead;
428 rc = RTCrPemParseContent(pvContent, cbContent,
429 (fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR)
430 ? RTCRPEMREADFILE_F_CONTINUE_ON_ENCODING_ERROR : 0,
431 g_aX509CertificateMarkers, RT_ELEMENTS(g_aX509CertificateMarkers),
432 &pSectionHead, pErrInfo);
433 if (RT_SUCCESS(rc))
434 {
435 PCRTCRPEMSECTION pCurSec = pSectionHead;
436 while (pCurSec)
437 {
438 int rc2 = RTCrStoreCertAddEncoded(hStore,
439 RTCRCERTCTX_F_ENC_X509_DER | (fFlags & RTCRCERTCTX_F_ADD_IF_NOT_FOUND),
440 pCurSec->pbData, pCurSec->cbData,
441 !RTErrInfoIsSet(pErrInfo) ? pErrInfo : NULL);
442 if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
443 {
444 rc = rc2;
445 if (!(fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR))
446 break;
447 }
448 pCurSec = pCurSec->pNext;
449 }
450
451 RTCrPemFreeSections(pSectionHead);
452 }
453 }
454 else /* Will happen if proxy not set / no connection available. */
455 rc = RTErrInfoSetF(pErrInfo, VERR_EOF, "Certificate '%s' is empty", pszFilename);
456 RTFileReadAllFree(pvContent, cbContent);
457 }
458 else
459 rc = RTErrInfoSetF(pErrInfo, rc, "RTFileReadAllEx failed with %Rrc on '%s'", rc, pszFilename);
460 return rc;
461}
462RT_EXPORT_SYMBOL(RTCrStoreCertAddFromFile);
463
464
465RTDECL(int) RTCrStoreCertAddWantedFromFile(RTCRSTORE hStore, uint32_t fFlags, const char *pszFilename,
466 PCRTCRCERTWANTED paWanted, size_t cWanted, bool *pafFound, PRTERRINFO pErrInfo)
467{
468 /*
469 * Validate input a little.
470 */
471 AssertReturn(!(fFlags & ~(RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR)), VERR_INVALID_FLAGS);
472 fFlags |= RTCRCERTCTX_F_ADD_IF_NOT_FOUND; /* forced */
473
474 AssertReturn(cWanted, VERR_NOT_FOUND);
475 for (uint32_t i = 0; i < cWanted; i++)
476 {
477 AssertReturn(!paWanted[i].pszSubject || *paWanted[i].pszSubject, VERR_INVALID_PARAMETER);
478 AssertReturn( paWanted[i].pszSubject
479 || paWanted[i].fSha1Fingerprint
480 || paWanted[i].fSha512Fingerprint,
481 VERR_INVALID_PARAMETER);
482 }
483
484 /*
485 * Make sure we've got a result array.
486 */
487 bool *pafFoundFree = NULL;
488 if (!pafFound)
489 {
490 pafFound = pafFoundFree = (bool *)RTMemTmpAllocZ(sizeof(bool) * cWanted);
491 AssertReturn(pafFound, VERR_NO_TMP_MEMORY);
492 }
493
494 size_t cbContent;
495 void *pvContent;
496 int rc = RTFileReadAllEx(pszFilename, 0, 64U*_1M, RTFILE_RDALL_O_DENY_WRITE, &pvContent, &cbContent);
497 if (RT_SUCCESS(rc))
498 {
499 /*
500 * Is it a java key store file? If so, load it into a tmp store
501 * which we can search. Don't want to duplicate the JKS reader code.
502 */
503 if ( cbContent > 32
504 && ((uint32_t const *)pvContent)[0] == RT_H2BE_U32_C(UINT32_C(0xfeedfeed)) /* magic */
505 && ((uint32_t const *)pvContent)[1] == RT_H2BE_U32_C(UINT32_C(0x00000002)) /* version */ )
506 {
507 RTCRSTORE hTmpStore;
508 rc = RTCrStoreCreateInMem(&hTmpStore, 64);
509 if (RT_SUCCESS(rc))
510 {
511 rc = RTCrStoreCertAddFromJavaKeyStoreInMem(hStore, fFlags, pvContent, cbContent, pszFilename, pErrInfo);
512 if (RT_SUCCESS(rc))
513 rc = RTCrStoreCertAddWantedFromStore(hStore, fFlags, hTmpStore, paWanted, cWanted, pafFound);
514 RTCrStoreRelease(hTmpStore);
515 }
516 else
517 rc = RTErrInfoSet(pErrInfo, rc, "Error creating temporary crypto store");
518 }
519 /*
520 * No assume PEM or DER encoded binary certificate. Inspect them one by one.
521 */
522 else if (cbContent)
523 {
524 PCRTCRPEMSECTION pSectionHead;
525 rc = RTCrPemParseContent(pvContent, cbContent,
526 (fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR)
527 ? RTCRPEMREADFILE_F_CONTINUE_ON_ENCODING_ERROR : 0,
528 g_aX509CertificateMarkers, RT_ELEMENTS(g_aX509CertificateMarkers),
529 &pSectionHead, pErrInfo);
530 if (RT_SUCCESS(rc))
531 {
532 rc = VWRN_NOT_FOUND;
533 for (PCRTCRPEMSECTION pCurSec = pSectionHead; pCurSec; pCurSec = pCurSec->pNext)
534 {
535 if (!pCurSec->cbData)
536 continue;
537
538 /*
539 * See if this is a binary blob we might be interested in.
540 */
541 uint8_t abSha1[RTSHA1_HASH_SIZE];
542 RTSha1(pCurSec->pbData, pCurSec->cbData, abSha1);
543 uint8_t abSha512[RTSHA512_HASH_SIZE];
544 RTSha512(pCurSec->pbData, pCurSec->cbData, abSha512);
545 if (!rtCrStoreIsCertWanted(paWanted, cWanted, pafFound, pCurSec->cbData, abSha1, abSha512, NULL))
546 continue;
547
548 /*
549 * Decode the certificate so we can match the subject string.
550 */
551 RTASN1CURSORPRIMARY Cursor;
552 RTAsn1CursorInitPrimary(&Cursor, pCurSec->pbData, (uint32_t)pCurSec->cbData,
553 !RTErrInfoIsSet(pErrInfo) ? pErrInfo : NULL,
554 &g_RTAsn1DefaultAllocator, RTASN1CURSOR_FLAGS_DER, "InMem");
555 RTCRX509CERTIFICATE X509Cert;
556 int rc2 = RTCrX509Certificate_DecodeAsn1(&Cursor.Cursor, 0, &X509Cert, "Cert");
557 if (RT_SUCCESS(rc2))
558 {
559 rc2 = RTCrX509Certificate_CheckSanity(&X509Cert, 0, !RTErrInfoIsSet(pErrInfo) ? pErrInfo : NULL, "Cert");
560 if (RT_SUCCESS(rc2))
561 {
562 if (rtCrStoreIsCertWanted(paWanted, cWanted, pafFound, pCurSec->cbData, abSha1, abSha512, &X509Cert))
563 {
564 /*
565 * The certificate is wanted, now add it to the store.
566 */
567 rc2 = RTCrStoreCertAddEncoded(hStore,
568 RTCRCERTCTX_F_ENC_X509_DER
569 | (fFlags & RTCRCERTCTX_F_ADD_IF_NOT_FOUND),
570 pCurSec->pbData, pCurSec->cbData,
571 !RTErrInfoIsSet(pErrInfo) ? pErrInfo : NULL);
572 if (RT_SUCCESS(rc2))
573 {
574 /*
575 * Mark it as found, stop if we've found all.
576 */
577 if (rtCrStoreMarkCertFound(pafFound, paWanted, cWanted,
578 pCurSec->cbData, abSha1, abSha512, &X509Cert))
579 {
580 RTAsn1VtDelete(&X509Cert.SeqCore.Asn1Core);
581 rc = VINF_SUCCESS;
582 break;
583 }
584 }
585 }
586 }
587 else
588 Assert(!pErrInfo || RTErrInfoIsSet(pErrInfo));
589 RTAsn1VtDelete(&X509Cert.SeqCore.Asn1Core);
590 }
591 else if (!RTErrInfoIsSet(pErrInfo))
592 RTErrInfoSetF(pErrInfo, rc2, "RTCrX509Certificate_DecodeAsn1 failed");
593
594 /*
595 * Stop on error, if requested. Otherwise, let pErrInfo keep it.
596 */
597 if (RT_FAILURE(rc2) && !(fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR))
598 {
599 rc = rc2;
600 break;
601 }
602 } /* For each PEM section. */
603
604 RTCrPemFreeSections(pSectionHead);
605 }
606 }
607 else /* Will happen if proxy not set / no connection available. */
608 rc = RTErrInfoSetF(pErrInfo, VERR_EOF, "Certificate '%s' is empty", pszFilename);
609 RTFileReadAllFree(pvContent, cbContent);
610 }
611 else
612 rc = RTErrInfoSetF(pErrInfo, rc, "RTFileReadAllEx failed with %Rrc on '%s'", rc, pszFilename);
613
614 if (pafFoundFree)
615 RTMemTmpFree(pafFoundFree);
616 return rc;
617}
618RT_EXPORT_SYMBOL(RTCrStoreCertAddWantedFromFile);
619
620
621/**
622 * Checks if the directory entry matches the specified suffixes.
623 *
624 * @returns true on match, false on mismatch.
625 * @param pDirEntry The directory to check.
626 * @param paSuffixes The array of suffixes to match against.
627 * @param cSuffixes The number of suffixes in the array.
628 */
629DECLINLINE(bool) rtCrStoreIsSuffixMatch(PCRTDIRENTRY pDirEntry, PCRTSTRTUPLE paSuffixes, size_t cSuffixes)
630{
631 if (cSuffixes == 0)
632 return true;
633
634 size_t const cchName = pDirEntry->cbName;
635 size_t i = cSuffixes;
636 while (i-- > 0)
637 if ( cchName > paSuffixes[i].cch
638 && memcmp(&pDirEntry->szName[cchName - paSuffixes[i].cch], paSuffixes[i].psz, paSuffixes[i].cch) == 0)
639 return true;
640
641 return false;
642}
643
644
645RTDECL(int) RTCrStoreCertAddFromDir(RTCRSTORE hStore, uint32_t fFlags, const char *pszDir,
646 PCRTSTRTUPLE paSuffixes, size_t cSuffixes, PRTERRINFO pErrInfo)
647{
648 /*
649 * Validate input.
650 */
651 AssertReturn(!(fFlags & ~(RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR)), VERR_INVALID_FLAGS);
652 size_t i = cSuffixes;
653 while (i-- > 0)
654 {
655 Assert(paSuffixes[i].cch > 0);
656 Assert(strlen(paSuffixes[i].psz) == paSuffixes[i].cch);
657 }
658
659 /*
660 * Prepare for constructing path to the files in the directory, so that we
661 * can open them.
662 */
663 char szPath[RTPATH_MAX];
664 int rc = RTStrCopy(szPath, sizeof(szPath), pszDir);
665 if (RT_SUCCESS(rc))
666 {
667 size_t cchPath = RTPathEnsureTrailingSeparator(szPath, sizeof(szPath));
668 if (cchPath > 0)
669 {
670 size_t const cbMaxFilename = sizeof(szPath) - cchPath;
671
672 /*
673 * Enumerate the directory.
674 */
675 RTDIR hDir;
676 rc = RTDirOpen(&hDir, pszDir);
677 if (RT_SUCCESS(rc))
678 {
679 for (;;)
680 {
681 /* Read the next entry. */
682 union
683 {
684 RTDIRENTRY DirEntry;
685 uint8_t abPadding[RTPATH_MAX + sizeof(RTDIRENTRY)];
686 } u;
687 size_t cbBuf = sizeof(u);
688 int rc2 = RTDirRead(hDir, &u.DirEntry, &cbBuf);
689 if (RT_SUCCESS(rc2))
690 {
691 if ( ( u.DirEntry.enmType == RTDIRENTRYTYPE_FILE
692 || u.DirEntry.enmType == RTDIRENTRYTYPE_SYMLINK
693 || ( u.DirEntry.enmType == RTDIRENTRYTYPE_UNKNOWN
694 && !RTDirEntryIsStdDotLink(&u.DirEntry)) )
695 && rtCrStoreIsSuffixMatch(&u.DirEntry, paSuffixes, cSuffixes) )
696 {
697 if (u.DirEntry.cbName < cbMaxFilename)
698 {
699 memcpy(&szPath[cchPath], u.DirEntry.szName, u.DirEntry.cbName + 1);
700 rc2 = RTDirQueryUnknownType(szPath, true /*fFollowSymlinks*/, &u.DirEntry.enmType);
701 if ( RT_SUCCESS(rc2)
702 && u.DirEntry.enmType == RTDIRENTRYTYPE_FILE)
703 {
704 /*
705 * Add it.
706 */
707 rc2 = RTCrStoreCertAddFromFile(hStore, fFlags, szPath, pErrInfo);
708 if (RT_FAILURE(rc2))
709 {
710 rc = rc2;
711 if (!(fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR))
712 break;
713 }
714 }
715 }
716 else
717 {
718 rc = RTErrInfoAddF(pErrInfo, VERR_FILENAME_TOO_LONG,
719 " Too long filename (%u bytes)", u.DirEntry.cbName);
720 if (!(fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR))
721 break;
722 }
723 }
724 }
725 else
726 {
727 if (rc2 != VERR_NO_MORE_FILES)
728 rc = RTErrInfoAddF(pErrInfo, rc2, " RTDirRead failed: %Rrc", rc2);
729 break;
730 }
731 }
732
733 RTDirClose(hDir);
734 }
735 else
736 rc = RTErrInfoAddF(pErrInfo, rc, " RTDirOpen('%s'): %Rrc", pszDir, rc);
737 }
738 else
739 rc = VERR_FILENAME_TOO_LONG;
740 }
741 return rc;
742}
743RT_EXPORT_SYMBOL(RTCrStoreCertAddFromDir);
744
745
746RTDECL(int) RTCrStoreCertAddWantedFromDir(RTCRSTORE hStore, uint32_t fFlags,
747 const char *pszDir, PCRTSTRTUPLE paSuffixes, size_t cSuffixes,
748 PCRTCRCERTWANTED paWanted, size_t cWanted, bool *pafFound, PRTERRINFO pErrInfo)
749{
750 /*
751 * Validate input a little.
752 */
753 AssertReturn(*pszDir, VERR_PATH_ZERO_LENGTH);
754 AssertReturn(!(fFlags & ~(RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR)), VERR_INVALID_FLAGS);
755 fFlags |= RTCRCERTCTX_F_ADD_IF_NOT_FOUND; /* forced */
756
757 AssertReturn(cWanted, VERR_NOT_FOUND);
758 for (uint32_t i = 0; i < cWanted; i++)
759 {
760 AssertReturn(!paWanted[i].pszSubject || *paWanted[i].pszSubject, VERR_INVALID_PARAMETER);
761 AssertReturn( paWanted[i].pszSubject
762 || paWanted[i].fSha1Fingerprint
763 || paWanted[i].fSha512Fingerprint,
764 VERR_INVALID_PARAMETER);
765 }
766
767 /*
768 * Prepare for constructing path to the files in the directory, so that we
769 * can open them.
770 */
771 char szPath[RTPATH_MAX];
772 int rc = RTStrCopy(szPath, sizeof(szPath), pszDir);
773 if (RT_SUCCESS(rc))
774 {
775 size_t cchPath = RTPathEnsureTrailingSeparator(szPath, sizeof(szPath));
776 if (cchPath > 0)
777 {
778 size_t const cbMaxFilename = sizeof(szPath) - cchPath;
779
780 /*
781 * Enumerate the directory.
782 */
783 RTDIR hDir;
784 rc = RTDirOpen(&hDir, pszDir);
785 if (RT_SUCCESS(rc))
786 {
787 rc = VWRN_NOT_FOUND;
788 for (;;)
789 {
790 /* Read the next entry. */
791 union
792 {
793 RTDIRENTRY DirEntry;
794 uint8_t abPadding[RTPATH_MAX + sizeof(RTDIRENTRY)];
795 } u;
796 size_t cbEntry = sizeof(u);
797 int rc2 = RTDirRead(hDir, &u.DirEntry, &cbEntry);
798 if (RT_SUCCESS(rc2))
799 {
800 if ( ( u.DirEntry.enmType == RTDIRENTRYTYPE_FILE
801 || u.DirEntry.enmType == RTDIRENTRYTYPE_SYMLINK
802 || ( u.DirEntry.enmType == RTDIRENTRYTYPE_UNKNOWN
803 && !RTDirEntryIsStdDotLink(&u.DirEntry)) )
804 && rtCrStoreIsSuffixMatch(&u.DirEntry, paSuffixes, cSuffixes) )
805 {
806 if (u.DirEntry.cbName < cbMaxFilename)
807 {
808 memcpy(&szPath[cchPath], u.DirEntry.szName, u.DirEntry.cbName);
809 szPath[cchPath + u.DirEntry.cbName] = '\0';
810 if (u.DirEntry.enmType != RTDIRENTRYTYPE_FILE)
811 RTDirQueryUnknownType(szPath, true /*fFollowSymlinks*/, &u.DirEntry.enmType);
812 if (u.DirEntry.enmType == RTDIRENTRYTYPE_FILE)
813 {
814 rc2 = RTCrStoreCertAddWantedFromFile(hStore, fFlags, szPath,
815 paWanted, cWanted, pafFound, pErrInfo);
816 if (rc2 == VINF_SUCCESS)
817 {
818 Assert(rtCrStoreAllDone(pafFound, cWanted));
819 if (RT_SUCCESS(rc))
820 rc = VINF_SUCCESS;
821 break;
822 }
823 if (RT_FAILURE(rc2) && !(fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR))
824 {
825 rc = rc2;
826 break;
827 }
828 }
829 }
830 else
831 {
832 /*
833 * pErrInfo keeps the status code unless it's fatal.
834 */
835 RTErrInfoAddF(pErrInfo, VERR_FILENAME_TOO_LONG,
836 " Too long filename (%u bytes)", u.DirEntry.cbName);
837 if (!(fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR))
838 {
839 rc = VERR_FILENAME_TOO_LONG;
840 break;
841 }
842 }
843 }
844 }
845 else
846 {
847 if (rc2 != VERR_NO_MORE_FILES)
848 {
849 RTErrInfoAddF(pErrInfo, rc2, "RTDirRead failed: %Rrc", rc2);
850 if (!(fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR))
851 rc = rc2;
852 }
853 break;
854 }
855 }
856 RTDirClose(hDir);
857 }
858 }
859 else
860 rc = VERR_FILENAME_TOO_LONG;
861 }
862 return rc;
863}
864RT_EXPORT_SYMBOL(RTCrStoreCertAddWantedFromDir);
865
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use