VirtualBox

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

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

Copyright year updates by scm.

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