summaryrefslogtreecommitdiffstats
path: root/src/VBox/Runtime/common/crypto/store-cert-add-basic.cpp
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-05-06 03:01:46 +0000
commitf8fe689a81f906d1b91bb3220acde2a4ecb14c5b (patch)
tree26484e9d7e2c67806c2d1760196ff01aaa858e8c /src/VBox/Runtime/common/crypto/store-cert-add-basic.cpp
parentInitial commit. (diff)
downloadvirtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.tar.xz
virtualbox-f8fe689a81f906d1b91bb3220acde2a4ecb14c5b.zip
Adding upstream version 6.0.4-dfsg.upstream/6.0.4-dfsgupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Runtime/common/crypto/store-cert-add-basic.cpp')
-rw-r--r--src/VBox/Runtime/common/crypto/store-cert-add-basic.cpp855
1 files changed, 855 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/crypto/store-cert-add-basic.cpp b/src/VBox/Runtime/common/crypto/store-cert-add-basic.cpp
new file mode 100644
index 00000000..db776ec7
--- /dev/null
+++ b/src/VBox/Runtime/common/crypto/store-cert-add-basic.cpp
@@ -0,0 +1,855 @@
+/* $Id: store-cert-add-basic.cpp $ */
+/** @file
+ * IPRT - Cryptographic (Certificate) Store, RTCrStoreCertAddFromDir.
+ */
+
+/*
+ * Copyright (C) 2006-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ *
+ * The contents of this file may alternatively be used under the terms
+ * of the Common Development and Distribution License Version 1.0
+ * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
+ * VirtualBox OSE distribution, in which case the provisions of the
+ * CDDL are applicable instead of those of the GPL.
+ *
+ * You may elect to license modified versions of this file under the
+ * terms and conditions of either the GPL or the CDDL or both.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "internal/iprt.h"
+#include <iprt/crypto/store.h>
+
+#include <iprt/assert.h>
+#include <iprt/crypto/pem.h>
+#include <iprt/dir.h>
+#include <iprt/err.h>
+#include <iprt/file.h>
+#include <iprt/mem.h>
+#include <iprt/path.h>
+#include <iprt/sha.h>
+#include <iprt/string.h>
+
+#include "x509-internal.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** BEGIN CERTIFICATE / END CERTIFICATE. */
+static RTCRPEMMARKERWORD const g_aWords_Certificate[] =
+{
+ { RT_STR_TUPLE("CERTIFICATE") }
+};
+
+/** BEGIN TRUSTED CERTIFICATE / END TRUSTED CERTIFICATE. */
+static RTCRPEMMARKERWORD const g_aWords_TrustedCertificate[] =
+{
+ { RT_STR_TUPLE("TRUSTED") },
+ { RT_STR_TUPLE("CERTIFICATE") }
+};
+
+/** BEGIN X509 CERTIFICATE / END X509 CERTIFICATE. (old) */
+static RTCRPEMMARKERWORD const g_aWords_X509Certificate[] =
+{
+ { RT_STR_TUPLE("X509") },
+ { RT_STR_TUPLE("CERTIFICATE") }
+};
+
+/**
+ * X509 Certificate markers.
+ *
+ * @remark See crypto/pem/pem.h in OpenSSL for a matching list.
+ */
+static RTCRPEMMARKER const g_aX509CertificateMarkers[3] =
+{
+ { g_aWords_Certificate, RT_ELEMENTS(g_aWords_Certificate) },
+ { g_aWords_TrustedCertificate, RT_ELEMENTS(g_aWords_TrustedCertificate) },
+ { g_aWords_X509Certificate, RT_ELEMENTS(g_aWords_X509Certificate) }
+};
+
+
+
+#ifdef RT_STRICT
+/**
+ * Checks if we've found all the certificates already.
+ *
+ * @returns true if all found, false if not.
+ * @param afFound Indicator array.
+ * @param cWanted Number of wanted certificates.
+ */
+DECLINLINE(bool) rtCrStoreAllDone(bool const *afFound, size_t cWanted)
+{
+ while (cWanted-- > 0)
+ if (!afFound[cWanted])
+ return false;
+ return true;
+}
+#endif /* RT_STRICT */
+
+
+/**
+ * Checks if the given certificate specs matches the given wanted poster.
+ *
+ * @returns true if match, false if not.
+ * @param pWanted The certificate wanted poster.
+ * @param cbEncoded The candidate certificate encoded size.
+ * @param paSha1 The candidate certificate SHA-1 fingerprint.
+ * @param paSha512 The candidate certificate SHA-512 fingerprint.
+ * @param pCert The decoded candidate certificate, optional. If not
+ * given the result will be uncertain.
+ */
+DECLINLINE(bool) rtCrStoreIsCertEqualToWanted(PCRTCRCERTWANTED pWanted,
+ size_t cbEncoded,
+ uint8_t const pabSha1[RTSHA1_HASH_SIZE],
+ uint8_t const pabSha512[RTSHA512_HASH_SIZE],
+ PCRTCRX509CERTIFICATE pCert)
+{
+ if ( pWanted->cbEncoded != cbEncoded
+ && pWanted->cbEncoded != 0)
+ return false;
+
+ if ( pWanted->fSha1Fingerprint
+ && memcmp(pWanted->abSha1, pabSha1, RTSHA1_HASH_SIZE) != 0)
+ return false;
+
+ if ( pWanted->fSha512Fingerprint
+ && memcmp(pWanted->abSha512, pabSha512, RTSHA512_HASH_SIZE) != 0)
+ return false;
+
+ if ( pWanted->pszSubject
+ && pCert
+ && !RTCrX509Name_MatchWithString(&pCert->TbsCertificate.Subject, pWanted->pszSubject))
+ return false;
+
+ return true;
+}
+
+
+/**
+ * Checks if a certificate is wanted.
+ *
+ * @returns true if match, false if not.
+ * @param paWanted The certificate wanted posters.
+ * @param cWanted The number of wanted posters.
+ * @param apfFound Found initicators running paralell to @a paWanted.
+ * @param cbEncoded The candidate certificate encoded size.
+ * @param paSha1 The candidate certificate SHA-1 fingerprint.
+ * @param paSha512 The candidate certificate SHA-512 fingerprint.
+ * @param pCert The decoded candidate certificate, optional. If not
+ * given the result will be uncertain.
+ */
+DECLINLINE(bool) rtCrStoreIsCertWanted(PCRTCRCERTWANTED paWanted, size_t cWanted, bool const *pafFound, size_t cbEncoded,
+ uint8_t const pabSha1[RTSHA1_HASH_SIZE], uint8_t const pabSha512[RTSHA512_HASH_SIZE],
+ PCRTCRX509CERTIFICATE pCert)
+{
+ for (size_t iCert = 0; iCert < cWanted; iCert++)
+ if (!pafFound[iCert])
+ if (rtCrStoreIsCertEqualToWanted(&paWanted[iCert], cbEncoded, pabSha1, pabSha512, pCert))
+ return true;
+ return false;
+}
+
+
+/**
+ * Marks a certificate as found after it has been added to the store.
+ *
+ * May actually mark several certificates as found if there are duplicates or
+ * ambiguities in the wanted list.
+ *
+ * @returns true if all have been found, false if more to search for.
+ *
+ * @param apfFound Found initicators running paralell to @a paWanted.
+ * This is what this function updates.
+ * @param paWanted The certificate wanted posters.
+ * @param cWanted The number of wanted posters.
+ * @param cbEncoded The candidate certificate encoded size.
+ * @param paSha1 The candidate certificate SHA-1 fingerprint.
+ * @param paSha512 The candidate certificate SHA-512 fingerprint.
+ * @param pCert The decoded candidate certificate, optional. If not
+ * given the result will be uncertain.
+ */
+static bool rtCrStoreMarkCertFound(bool *pafFound, PCRTCRCERTWANTED paWanted, size_t cWanted, size_t cbEncoded,
+ uint8_t const pabSha1[RTSHA1_HASH_SIZE], uint8_t const pabSha512[RTSHA512_HASH_SIZE],
+ PCRTCRX509CERTIFICATE pCert)
+{
+ size_t cFound = 0;
+ for (size_t iCert = 0; iCert < cWanted; iCert++)
+ if (pafFound[iCert])
+ cFound++;
+ else if (rtCrStoreIsCertEqualToWanted(&paWanted[iCert], cbEncoded, pabSha1, pabSha512, pCert))
+ {
+ pafFound[iCert] = true;
+ cFound++;
+ }
+ return cFound == cWanted;
+}
+
+
+RTDECL(int) RTCrStoreCertAddFromStore(RTCRSTORE hStore, uint32_t fFlags, RTCRSTORE hStoreSrc)
+{
+ /*
+ * Validate input.
+ */
+ AssertReturn(!(fFlags & ~(RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR)), VERR_INVALID_FLAGS);
+
+ /*
+ * Enumerate all the certificates in the source store, adding them to the destination.
+ */
+ RTCRSTORECERTSEARCH Search;
+ int rc = RTCrStoreCertFindAll(hStoreSrc, &Search);
+ if (RT_SUCCESS(rc))
+ {
+ PCRTCRCERTCTX pCertCtx;
+ while ((pCertCtx = RTCrStoreCertSearchNext(hStoreSrc, &Search)) != NULL)
+ {
+ int rc2 = RTCrStoreCertAddEncoded(hStore, pCertCtx->fFlags | (fFlags & RTCRCERTCTX_F_ADD_IF_NOT_FOUND),
+ pCertCtx->pabEncoded, pCertCtx->cbEncoded, NULL);
+ if (RT_FAILURE(rc2))
+ {
+ rc = rc2;
+ if (!(fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR))
+ break;
+ }
+ RTCrCertCtxRelease(pCertCtx);
+ }
+
+ int rc2 = RTCrStoreCertSearchDestroy(hStoreSrc, &Search); AssertRC(rc2);
+ }
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTCrStoreCertAddFromStore);
+
+
+RTDECL(int) RTCrStoreCertAddWantedFromStore(RTCRSTORE hStore, uint32_t fFlags, RTCRSTORE hSrcStore,
+ PCRTCRCERTWANTED paWanted, size_t cWanted, bool *pafFound)
+{
+ /*
+ * Validate input a little.
+ */
+ AssertReturn(!(fFlags & ~(RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR)), VERR_INVALID_FLAGS);
+ fFlags |= RTCRCERTCTX_F_ADD_IF_NOT_FOUND; /* forced */
+
+ AssertReturn(cWanted, VERR_NOT_FOUND);
+ for (uint32_t i = 0; i < cWanted; i++)
+ {
+ AssertReturn(!paWanted[i].pszSubject || *paWanted[i].pszSubject, VERR_INVALID_PARAMETER);
+ AssertReturn( paWanted[i].pszSubject
+ || paWanted[i].fSha1Fingerprint
+ || paWanted[i].fSha512Fingerprint,
+ VERR_INVALID_PARAMETER);
+ }
+
+ /*
+ * Make sure we've got a result array.
+ */
+ bool *pafFoundFree = NULL;
+ if (!pafFound)
+ {
+ pafFound = pafFoundFree = (bool *)RTMemTmpAllocZ(sizeof(bool) * cWanted);
+ AssertReturn(pafFound, VERR_NO_TMP_MEMORY);
+ }
+
+ /*
+ * Enumerate the store entries.
+ */
+ RTCRSTORECERTSEARCH Search;
+ int rc = RTCrStoreCertFindAll(hSrcStore, &Search);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VWRN_NOT_FOUND;
+ PCRTCRCERTCTX pCertCtx;
+ while ((pCertCtx = RTCrStoreCertSearchNext(hSrcStore, &Search)) != NULL)
+ {
+ if ( (pCertCtx->fFlags & RTCRCERTCTX_F_ENC_MASK) == RTCRCERTCTX_F_ENC_X509_DER
+ && pCertCtx->cbEncoded > 0
+ && pCertCtx->pCert)
+ {
+ /*
+ * If the certificate is wanted, try add it to the store.
+ */
+ uint8_t abSha1[RTSHA1_HASH_SIZE];
+ RTSha1(pCertCtx->pabEncoded, pCertCtx->cbEncoded, abSha1);
+ uint8_t abSha512[RTSHA512_HASH_SIZE];
+ RTSha512(pCertCtx->pabEncoded, pCertCtx->cbEncoded, abSha512);
+ if (rtCrStoreIsCertWanted(paWanted, cWanted, pafFound, pCertCtx->cbEncoded, abSha1, abSha512, pCertCtx->pCert))
+ {
+ int rc2 = RTCrStoreCertAddEncoded(hStore,
+ RTCRCERTCTX_F_ENC_X509_DER | (fFlags & RTCRCERTCTX_F_ADD_IF_NOT_FOUND),
+ pCertCtx->pabEncoded, pCertCtx->cbEncoded, NULL /*pErrInfo*/);
+ if (RT_SUCCESS(rc2))
+ {
+ /*
+ * Mark it as found, stop if we've found all.
+ */
+ if (rtCrStoreMarkCertFound(pafFound, paWanted, cWanted,
+ pCertCtx->cbEncoded, abSha1, abSha512, pCertCtx->pCert))
+ {
+ if (RT_SUCCESS(rc))
+ rc = VINF_SUCCESS;
+ RTCrCertCtxRelease(pCertCtx);
+ break;
+ }
+ }
+ else
+ {
+ /*
+ * Some error adding the certificate. Since it cannot be anything with
+ * the encoding, it must be something with the store or resources, so
+ * always return the error status.
+ */
+ rc = rc2;
+ if (!(fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR))
+ {
+ RTCrCertCtxRelease(pCertCtx);
+ break;
+ }
+ }
+ }
+ }
+ RTCrCertCtxRelease(pCertCtx);
+ }
+ int rc2 = RTCrStoreCertSearchDestroy(hSrcStore, &Search);
+ AssertRC(rc2);
+ }
+
+ if (pafFoundFree)
+ RTMemTmpFree(pafFoundFree);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTCrStoreCertAddWantedFromStore);
+
+
+RTDECL(int) RTCrStoreCertCheckWanted(RTCRSTORE hStore, PCRTCRCERTWANTED paWanted, size_t cWanted, bool *pafFound)
+{
+ /*
+ * Validate input a little.
+ */
+ AssertReturn(cWanted, VERR_NOT_FOUND);
+ for (uint32_t i = 0; i < cWanted; i++)
+ {
+ AssertReturn(!paWanted[i].pszSubject || *paWanted[i].pszSubject, VERR_INVALID_PARAMETER);
+ AssertReturn( paWanted[i].pszSubject
+ || paWanted[i].fSha1Fingerprint
+ || paWanted[i].fSha512Fingerprint,
+ VERR_INVALID_PARAMETER);
+ }
+ AssertPtrReturn(pafFound, VERR_INVALID_POINTER);
+
+ /*
+ * Clear the found array.
+ */
+ for (uint32_t iCert = 0; iCert < cWanted; iCert++)
+ pafFound[iCert] = false;
+
+ /*
+ * Enumerate the store entries.
+ */
+ RTCRSTORECERTSEARCH Search;
+ int rc = RTCrStoreCertFindAll(hStore, &Search);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VWRN_NOT_FOUND;
+ PCRTCRCERTCTX pCertCtx;
+ while ((pCertCtx = RTCrStoreCertSearchNext(hStore, &Search)) != NULL)
+ {
+ if ( (pCertCtx->fFlags & RTCRCERTCTX_F_ENC_MASK) == RTCRCERTCTX_F_ENC_X509_DER
+ && pCertCtx->cbEncoded > 0
+ && pCertCtx->pCert)
+ {
+ /*
+ * Hash it and check if it's wanted. Stop when we've found all.
+ */
+ uint8_t abSha1[RTSHA1_HASH_SIZE];
+ RTSha1(pCertCtx->pabEncoded, pCertCtx->cbEncoded, abSha1);
+ uint8_t abSha512[RTSHA512_HASH_SIZE];
+ RTSha512(pCertCtx->pabEncoded, pCertCtx->cbEncoded, abSha512);
+ if (rtCrStoreMarkCertFound(pafFound, paWanted, cWanted, pCertCtx->cbEncoded, abSha1, abSha512, pCertCtx->pCert))
+ {
+ rc = VINF_SUCCESS;
+ RTCrCertCtxRelease(pCertCtx);
+ break;
+ }
+ }
+ RTCrCertCtxRelease(pCertCtx);
+ }
+ int rc2 = RTCrStoreCertSearchDestroy(hStore, &Search);
+ AssertRC(rc2);
+ }
+
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTCrStoreCertAddWantedFromStore);
+
+
+RTDECL(int) RTCrStoreCertAddFromFile(RTCRSTORE hStore, uint32_t fFlags, const char *pszFilename, PRTERRINFO pErrInfo)
+{
+ AssertReturn(!(fFlags & ~(RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR)), VERR_INVALID_FLAGS);
+
+ size_t cbContent;
+ void *pvContent;
+ int rc = RTFileReadAllEx(pszFilename, 0, 64U*_1M, RTFILE_RDALL_O_DENY_WRITE, &pvContent, &cbContent);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Is it a java key store file?
+ */
+ if ( cbContent > 32
+ && ((uint32_t const *)pvContent)[0] == RT_H2BE_U32_C(UINT32_C(0xfeedfeed)) /* magic */
+ && ((uint32_t const *)pvContent)[1] == RT_H2BE_U32_C(UINT32_C(0x00000002)) /* version */ )
+ rc = RTCrStoreCertAddFromJavaKeyStoreInMem(hStore, fFlags, pvContent, cbContent, pszFilename, pErrInfo);
+ /*
+ * No assume PEM or DER encoded binary certificate.
+ */
+ else if (cbContent)
+ {
+ PCRTCRPEMSECTION pSectionHead;
+ rc = RTCrPemParseContent(pvContent, cbContent,
+ (fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR)
+ ? RTCRPEMREADFILE_F_CONTINUE_ON_ENCODING_ERROR : 0,
+ g_aX509CertificateMarkers, RT_ELEMENTS(g_aX509CertificateMarkers),
+ &pSectionHead, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ PCRTCRPEMSECTION pCurSec = pSectionHead;
+ while (pCurSec)
+ {
+ int rc2 = RTCrStoreCertAddEncoded(hStore,
+ RTCRCERTCTX_F_ENC_X509_DER | (fFlags & RTCRCERTCTX_F_ADD_IF_NOT_FOUND),
+ pCurSec->pbData, pCurSec->cbData,
+ !RTErrInfoIsSet(pErrInfo) ? pErrInfo : NULL);
+ if (RT_FAILURE(rc2) && RT_SUCCESS(rc))
+ {
+ rc = rc2;
+ if (!(fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR))
+ break;
+ }
+ pCurSec = pCurSec->pNext;
+ }
+
+ RTCrPemFreeSections(pSectionHead);
+ }
+ }
+ else /* Will happen if proxy not set / no connection available. */
+ rc = RTErrInfoSetF(pErrInfo, VERR_EOF, "Certificate '%s' is empty", pszFilename);
+ RTFileReadAllFree(pvContent, cbContent);
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, rc, "RTFileReadAllEx failed with %Rrc on '%s'", rc, pszFilename);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTCrStoreCertAddFromFile);
+
+
+RTDECL(int) RTCrStoreCertAddWantedFromFile(RTCRSTORE hStore, uint32_t fFlags, const char *pszFilename,
+ PCRTCRCERTWANTED paWanted, size_t cWanted, bool *pafFound, PRTERRINFO pErrInfo)
+{
+ /*
+ * Validate input a little.
+ */
+ AssertReturn(!(fFlags & ~(RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR)), VERR_INVALID_FLAGS);
+ fFlags |= RTCRCERTCTX_F_ADD_IF_NOT_FOUND; /* forced */
+
+ AssertReturn(cWanted, VERR_NOT_FOUND);
+ for (uint32_t i = 0; i < cWanted; i++)
+ {
+ AssertReturn(!paWanted[i].pszSubject || *paWanted[i].pszSubject, VERR_INVALID_PARAMETER);
+ AssertReturn( paWanted[i].pszSubject
+ || paWanted[i].fSha1Fingerprint
+ || paWanted[i].fSha512Fingerprint,
+ VERR_INVALID_PARAMETER);
+ }
+
+ /*
+ * Make sure we've got a result array.
+ */
+ bool *pafFoundFree = NULL;
+ if (!pafFound)
+ {
+ pafFound = pafFoundFree = (bool *)RTMemTmpAllocZ(sizeof(bool) * cWanted);
+ AssertReturn(pafFound, VERR_NO_TMP_MEMORY);
+ }
+
+ size_t cbContent;
+ void *pvContent;
+ int rc = RTFileReadAllEx(pszFilename, 0, 64U*_1M, RTFILE_RDALL_O_DENY_WRITE, &pvContent, &cbContent);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Is it a java key store file? If so, load it into a tmp store
+ * which we can search. Don't want to duplicate the JKS reader code.
+ */
+ if ( cbContent > 32
+ && ((uint32_t const *)pvContent)[0] == RT_H2BE_U32_C(UINT32_C(0xfeedfeed)) /* magic */
+ && ((uint32_t const *)pvContent)[1] == RT_H2BE_U32_C(UINT32_C(0x00000002)) /* version */ )
+ {
+ RTCRSTORE hTmpStore;
+ rc = RTCrStoreCreateInMem(&hTmpStore, 64);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTCrStoreCertAddFromJavaKeyStoreInMem(hStore, fFlags, pvContent, cbContent, pszFilename, pErrInfo);
+ if (RT_SUCCESS(rc))
+ rc = RTCrStoreCertAddWantedFromStore(hStore, fFlags, hTmpStore, paWanted, cWanted, pafFound);
+ RTCrStoreRelease(hTmpStore);
+ }
+ else
+ rc = RTErrInfoSet(pErrInfo, rc, "Error creating temporary crypto store");
+ }
+ /*
+ * No assume PEM or DER encoded binary certificate. Inspect them one by one.
+ */
+ else if (cbContent)
+ {
+ PCRTCRPEMSECTION pSectionHead;
+ rc = RTCrPemParseContent(pvContent, cbContent,
+ (fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR)
+ ? RTCRPEMREADFILE_F_CONTINUE_ON_ENCODING_ERROR : 0,
+ g_aX509CertificateMarkers, RT_ELEMENTS(g_aX509CertificateMarkers),
+ &pSectionHead, pErrInfo);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VWRN_NOT_FOUND;
+ for (PCRTCRPEMSECTION pCurSec = pSectionHead; pCurSec; pCurSec = pCurSec->pNext)
+ {
+ if (!pCurSec->cbData)
+ continue;
+
+ /*
+ * See if this is a binary blob we might be interested in.
+ */
+ uint8_t abSha1[RTSHA1_HASH_SIZE];
+ RTSha1(pCurSec->pbData, pCurSec->cbData, abSha1);
+ uint8_t abSha512[RTSHA512_HASH_SIZE];
+ RTSha512(pCurSec->pbData, pCurSec->cbData, abSha512);
+ if (!rtCrStoreIsCertWanted(paWanted, cWanted, pafFound, pCurSec->cbData, abSha1, abSha512, NULL))
+ continue;
+
+ /*
+ * Decode the certificate so we can match the subject string.
+ */
+ RTASN1CURSORPRIMARY Cursor;
+ RTAsn1CursorInitPrimary(&Cursor, pCurSec->pbData, (uint32_t)pCurSec->cbData,
+ !RTErrInfoIsSet(pErrInfo) ? pErrInfo : NULL,
+ &g_RTAsn1DefaultAllocator, RTASN1CURSOR_FLAGS_DER, "InMem");
+ RTCRX509CERTIFICATE X509Cert;
+ int rc2 = RTCrX509Certificate_DecodeAsn1(&Cursor.Cursor, 0, &X509Cert, "Cert");
+ if (RT_SUCCESS(rc2))
+ {
+ rc2 = RTCrX509Certificate_CheckSanity(&X509Cert, 0, !RTErrInfoIsSet(pErrInfo) ? pErrInfo : NULL, "Cert");
+ if (RT_SUCCESS(rc2))
+ {
+ if (rtCrStoreIsCertWanted(paWanted, cWanted, pafFound, pCurSec->cbData, abSha1, abSha512, &X509Cert))
+ {
+ /*
+ * The certificate is wanted, now add it to the store.
+ */
+ rc2 = RTCrStoreCertAddEncoded(hStore,
+ RTCRCERTCTX_F_ENC_X509_DER
+ | (fFlags & RTCRCERTCTX_F_ADD_IF_NOT_FOUND),
+ pCurSec->pbData, pCurSec->cbData,
+ !RTErrInfoIsSet(pErrInfo) ? pErrInfo : NULL);
+ if (RT_SUCCESS(rc2))
+ {
+ /*
+ * Mark it as found, stop if we've found all.
+ */
+ if (rtCrStoreMarkCertFound(pafFound, paWanted, cWanted,
+ pCurSec->cbData, abSha1, abSha512, &X509Cert))
+ {
+ RTAsn1VtDelete(&X509Cert.SeqCore.Asn1Core);
+ rc = VINF_SUCCESS;
+ break;
+ }
+ }
+ }
+ }
+ else
+ Assert(!pErrInfo || RTErrInfoIsSet(pErrInfo));
+ RTAsn1VtDelete(&X509Cert.SeqCore.Asn1Core);
+ }
+ else if (!RTErrInfoIsSet(pErrInfo))
+ RTErrInfoSetF(pErrInfo, rc2, "RTCrX509Certificate_DecodeAsn1 failed");
+
+ /*
+ * Stop on error, if requested. Otherwise, let pErrInfo keep it.
+ */
+ if (RT_FAILURE(rc2) && !(fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR))
+ {
+ rc = rc2;
+ break;
+ }
+ } /* For each PEM section. */
+
+ RTCrPemFreeSections(pSectionHead);
+ }
+ }
+ else /* Will happen if proxy not set / no connection available. */
+ rc = RTErrInfoSetF(pErrInfo, VERR_EOF, "Certificate '%s' is empty", pszFilename);
+ RTFileReadAllFree(pvContent, cbContent);
+ }
+ else
+ rc = RTErrInfoSetF(pErrInfo, rc, "RTFileReadAllEx failed with %Rrc on '%s'", rc, pszFilename);
+
+ if (pafFoundFree)
+ RTMemTmpFree(pafFoundFree);
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTCrStoreCertAddWantedFromFile);
+
+
+/**
+ * Checks if the directory entry matches the specified suffixes.
+ *
+ * @returns true on match, false on mismatch.
+ * @param pDirEntry The directory to check.
+ * @param paSuffixes The array of suffixes to match against.
+ * @param cSuffixes The number of suffixes in the array.
+ */
+DECLINLINE(bool) rtCrStoreIsSuffixMatch(PCRTDIRENTRY pDirEntry, PCRTSTRTUPLE paSuffixes, size_t cSuffixes)
+{
+ if (cSuffixes == 0)
+ return true;
+
+ size_t const cchName = pDirEntry->cbName;
+ size_t i = cSuffixes;
+ while (i-- > 0)
+ if ( cchName > paSuffixes[i].cch
+ && memcmp(&pDirEntry->szName[cchName - paSuffixes[i].cch], paSuffixes[i].psz, paSuffixes[i].cch) == 0)
+ return true;
+
+ return false;
+}
+
+
+RTDECL(int) RTCrStoreCertAddFromDir(RTCRSTORE hStore, uint32_t fFlags, const char *pszDir,
+ PCRTSTRTUPLE paSuffixes, size_t cSuffixes, PRTERRINFO pErrInfo)
+{
+ /*
+ * Validate input.
+ */
+ AssertReturn(!(fFlags & ~(RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR)), VERR_INVALID_FLAGS);
+ size_t i = cSuffixes;
+ while (i-- > 0)
+ {
+ Assert(paSuffixes[i].cch > 0);
+ Assert(strlen(paSuffixes[i].psz) == paSuffixes[i].cch);
+ }
+
+ /*
+ * Prepare for constructing path to the files in the directory, so that we
+ * can open them.
+ */
+ char szPath[RTPATH_MAX];
+ int rc = RTStrCopy(szPath, sizeof(szPath), pszDir);
+ if (RT_SUCCESS(rc))
+ {
+ size_t cchPath = RTPathEnsureTrailingSeparator(szPath, sizeof(szPath));
+ if (cchPath > 0)
+ {
+ size_t const cbMaxFilename = sizeof(szPath) - cchPath;
+
+ /*
+ * Enumerate the directory.
+ */
+ RTDIR hDir;
+ rc = RTDirOpen(&hDir, pszDir);
+ if (RT_SUCCESS(rc))
+ {
+ for (;;)
+ {
+ /* Read the next entry. */
+ union
+ {
+ RTDIRENTRY DirEntry;
+ uint8_t abPadding[RTPATH_MAX + sizeof(RTDIRENTRY)];
+ } u;
+ size_t cbBuf = sizeof(u);
+ int rc2 = RTDirRead(hDir, &u.DirEntry, &cbBuf);
+ if (RT_SUCCESS(rc2))
+ {
+ if ( ( u.DirEntry.enmType == RTDIRENTRYTYPE_FILE
+ || u.DirEntry.enmType == RTDIRENTRYTYPE_SYMLINK
+ || ( u.DirEntry.enmType == RTDIRENTRYTYPE_UNKNOWN
+ && !RTDirEntryIsStdDotLink(&u.DirEntry)) )
+ && rtCrStoreIsSuffixMatch(&u.DirEntry, paSuffixes, cSuffixes) )
+ {
+ if (u.DirEntry.cbName < cbMaxFilename)
+ {
+ memcpy(&szPath[cchPath], u.DirEntry.szName, u.DirEntry.cbName + 1);
+ rc2 = RTDirQueryUnknownType(szPath, true /*fFollowSymlinks*/, &u.DirEntry.enmType);
+ if ( RT_SUCCESS(rc2)
+ && u.DirEntry.enmType == RTDIRENTRYTYPE_FILE)
+ {
+ /*
+ * Add it.
+ */
+ rc2 = RTCrStoreCertAddFromFile(hStore, fFlags, szPath, pErrInfo);
+ if (RT_FAILURE(rc2))
+ {
+ rc = rc2;
+ if (!(fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR))
+ break;
+ }
+ }
+ }
+ else
+ {
+ rc = RTErrInfoAddF(pErrInfo, VERR_FILENAME_TOO_LONG,
+ " Too long filename (%u bytes)", u.DirEntry.cbName);
+ if (!(fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR))
+ break;
+ }
+ }
+ }
+ else
+ {
+ if (rc2 != VERR_NO_MORE_FILES)
+ rc = RTErrInfoAddF(pErrInfo, rc2, " RTDirRead failed: %Rrc", rc2);
+ break;
+ }
+ }
+
+ RTDirClose(hDir);
+ }
+ else
+ rc = RTErrInfoAddF(pErrInfo, rc, " RTDirOpen('%s'): %Rrc", pszDir, rc);
+ }
+ else
+ rc = VERR_FILENAME_TOO_LONG;
+ }
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTCrStoreCertAddFromDir);
+
+
+RTDECL(int) RTCrStoreCertAddWantedFromDir(RTCRSTORE hStore, uint32_t fFlags,
+ const char *pszDir, PCRTSTRTUPLE paSuffixes, size_t cSuffixes,
+ PCRTCRCERTWANTED paWanted, size_t cWanted, bool *pafFound, PRTERRINFO pErrInfo)
+{
+ /*
+ * Validate input a little.
+ */
+ AssertReturn(*pszDir, VERR_PATH_ZERO_LENGTH);
+ AssertReturn(!(fFlags & ~(RTCRCERTCTX_F_ADD_IF_NOT_FOUND | RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR)), VERR_INVALID_FLAGS);
+ fFlags |= RTCRCERTCTX_F_ADD_IF_NOT_FOUND; /* forced */
+
+ AssertReturn(cWanted, VERR_NOT_FOUND);
+ for (uint32_t i = 0; i < cWanted; i++)
+ {
+ AssertReturn(!paWanted[i].pszSubject || *paWanted[i].pszSubject, VERR_INVALID_PARAMETER);
+ AssertReturn( paWanted[i].pszSubject
+ || paWanted[i].fSha1Fingerprint
+ || paWanted[i].fSha512Fingerprint,
+ VERR_INVALID_PARAMETER);
+ }
+
+ /*
+ * Prepare for constructing path to the files in the directory, so that we
+ * can open them.
+ */
+ char szPath[RTPATH_MAX];
+ int rc = RTStrCopy(szPath, sizeof(szPath), pszDir);
+ if (RT_SUCCESS(rc))
+ {
+ size_t cchPath = RTPathEnsureTrailingSeparator(szPath, sizeof(szPath));
+ if (cchPath > 0)
+ {
+ size_t const cbMaxFilename = sizeof(szPath) - cchPath;
+
+ /*
+ * Enumerate the directory.
+ */
+ RTDIR hDir;
+ rc = RTDirOpen(&hDir, pszDir);
+ if (RT_SUCCESS(rc))
+ {
+ rc = VWRN_NOT_FOUND;
+ for (;;)
+ {
+ /* Read the next entry. */
+ union
+ {
+ RTDIRENTRY DirEntry;
+ uint8_t abPadding[RTPATH_MAX + sizeof(RTDIRENTRY)];
+ } u;
+ size_t cbEntry = sizeof(u);
+ int rc2 = RTDirRead(hDir, &u.DirEntry, &cbEntry);
+ if (RT_SUCCESS(rc2))
+ {
+ if ( ( u.DirEntry.enmType == RTDIRENTRYTYPE_FILE
+ || u.DirEntry.enmType == RTDIRENTRYTYPE_SYMLINK
+ || ( u.DirEntry.enmType == RTDIRENTRYTYPE_UNKNOWN
+ && !RTDirEntryIsStdDotLink(&u.DirEntry)) )
+ && rtCrStoreIsSuffixMatch(&u.DirEntry, paSuffixes, cSuffixes) )
+ {
+ if (u.DirEntry.cbName < cbMaxFilename)
+ {
+ memcpy(&szPath[cchPath], u.DirEntry.szName, u.DirEntry.cbName);
+ szPath[cchPath + u.DirEntry.cbName] = '\0';
+ if (u.DirEntry.enmType != RTDIRENTRYTYPE_FILE)
+ RTDirQueryUnknownType(szPath, true /*fFollowSymlinks*/, &u.DirEntry.enmType);
+ if (u.DirEntry.enmType == RTDIRENTRYTYPE_FILE)
+ {
+ rc2 = RTCrStoreCertAddWantedFromFile(hStore, fFlags, szPath,
+ paWanted, cWanted, pafFound, pErrInfo);
+ if (rc2 == VINF_SUCCESS)
+ {
+ Assert(rtCrStoreAllDone(pafFound, cWanted));
+ if (RT_SUCCESS(rc))
+ rc = VINF_SUCCESS;
+ break;
+ }
+ if (RT_FAILURE(rc2) && !(fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR))
+ {
+ rc = rc2;
+ break;
+ }
+ }
+ }
+ else
+ {
+ /*
+ * pErrInfo keeps the status code unless it's fatal.
+ */
+ RTErrInfoAddF(pErrInfo, VERR_FILENAME_TOO_LONG,
+ " Too long filename (%u bytes)", u.DirEntry.cbName);
+ if (!(fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR))
+ {
+ rc = VERR_FILENAME_TOO_LONG;
+ break;
+ }
+ }
+ }
+ }
+ else
+ {
+ if (rc2 != VERR_NO_MORE_FILES)
+ {
+ RTErrInfoAddF(pErrInfo, rc2, "RTDirRead failed: %Rrc", rc2);
+ if (!(fFlags & RTCRCERTCTX_F_ADD_CONTINUE_ON_ERROR))
+ rc = rc2;
+ }
+ break;
+ }
+ }
+ RTDirClose(hDir);
+ }
+ }
+ else
+ rc = VERR_FILENAME_TOO_LONG;
+ }
+ return rc;
+}
+RT_EXPORT_SYMBOL(RTCrStoreCertAddWantedFromDir);
+