diff options
Diffstat (limited to 'src/VBox/Runtime/common/crypto/store-inmem.cpp')
-rw-r--r-- | src/VBox/Runtime/common/crypto/store-inmem.cpp | 466 |
1 files changed, 466 insertions, 0 deletions
diff --git a/src/VBox/Runtime/common/crypto/store-inmem.cpp b/src/VBox/Runtime/common/crypto/store-inmem.cpp new file mode 100644 index 00000000..7bc441ae --- /dev/null +++ b/src/VBox/Runtime/common/crypto/store-inmem.cpp @@ -0,0 +1,466 @@ +/* $Id: store-inmem.cpp $ */ +/** @file + * IPRT - In Memory Cryptographic Certificate Store. + */ + +/* + * Copyright (C) 2006-2023 Oracle and/or its affiliates. + * + * This file is part of VirtualBox base platform packages, as + * available from https://www.virtualbox.org. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, in version 3 of the + * License. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses>. + * + * The contents of this file may alternatively be used under the terms + * of the Common Development and Distribution License Version 1.0 + * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included + * in the VirtualBox 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. + * + * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0 + */ + + +/********************************************************************************************************************************* +* Header Files * +*********************************************************************************************************************************/ +#include "internal/iprt.h" +#include <iprt/crypto/store.h> + +#include <iprt/asm.h> +#include <iprt/err.h> +#include <iprt/mem.h> +#include <iprt/string.h> + +#include "store-internal.h" + + +/********************************************************************************************************************************* +* Structures and Typedefs * +*********************************************************************************************************************************/ +/** + * A certificate entry in the in-memory store. + */ +typedef struct RTCRSTOREINMEMCERT +{ + /** The core certificate context. */ + RTCRCERTCTXINT Core; + /** Internal copy of the flag (paranoia). */ + uint32_t fFlags; + /** Decoded data. */ + union + { + /** ASN.1 core structure for generic access. */ + RTASN1CORE Asn1Core; + /** The decoded X.509 certificate (RTCRCERTCTX_F_ENC_X509_DER). */ + RTCRX509CERTIFICATE X509Cert; + /** The decoded trust anchor info (RTCRCERTCTX_F_ENC_TAF_DER). */ + RTCRTAFTRUSTANCHORINFO TaInfo; + } u; + /** Pointer to the store if still in it (no reference). */ + struct RTCRSTOREINMEM *pStore; + /** The DER encoding of the certificate. */ + uint8_t abEncoded[1]; +} RTCRSTOREINMEMCERT; +AssertCompileMembersAtSameOffset(RTCRSTOREINMEMCERT, u.X509Cert.SeqCore.Asn1Core, RTCRSTOREINMEMCERT, u.Asn1Core); +AssertCompileMembersAtSameOffset(RTCRSTOREINMEMCERT, u.TaInfo.SeqCore.Asn1Core, RTCRSTOREINMEMCERT, u.Asn1Core); +/** Pointer to an in-memory store certificate entry. */ +typedef RTCRSTOREINMEMCERT *PRTCRSTOREINMEMCERT; + + +/** + * The per instance data of a in-memory crypto store. + * + * Currently we ASSUME we don't need serialization. Add that when needed! + */ +typedef struct RTCRSTOREINMEM +{ + /** The number of certificates. */ + uint32_t cCerts; + /** The max number of certificates papCerts can store before growing it. */ + uint32_t cCertsAlloc; + /** Array of certificates. */ + PRTCRSTOREINMEMCERT *papCerts; + + /** Parent store. */ + RTCRSTORE hParentStore; + /** The parent store callback table. */ + PCRTCRSTOREPROVIDER pParentProvider; + /** The parent store provider callback argument. */ + void *pvParentProvider; +} RTCRSTOREINMEM; +/** Pointer to an in-memory crypto store. */ +typedef RTCRSTOREINMEM *PRTCRSTOREINMEM; + + + + +static DECLCALLBACK(void) rtCrStoreInMemCertEntry_Dtor(PRTCRCERTCTXINT pCertCtx) +{ + PRTCRSTOREINMEMCERT pEntry = (PRTCRSTOREINMEMCERT)pCertCtx; + AssertRelease(!pEntry->pStore); + + pEntry->Core.pfnDtor = NULL; + RTAsn1VtDelete(&pEntry->u.Asn1Core); + RTMemFree(pEntry); +} + + +/** + * Internal method for allocating and initalizing a certificate entry in the + * in-memory store. + * + * @returns IPRT status code. + * @param pThis The in-memory store instance. + * @param fEnc RTCRCERTCTX_F_ENC_X509_DER or RTCRCERTCTX_F_ENC_TAF_DER. + * @param pbSrc The DER encoded X.509 certificate to add. + * @param cbSrc The size of the encoded certificate. + * @param pErrInfo Where to return extended error info. Optional. + * @param ppEntry Where to return the pointer to the new entry. + */ +static int rtCrStoreInMemCreateCertEntry(PRTCRSTOREINMEM pThis, uint32_t fEnc, uint8_t const *pbSrc, uint32_t cbSrc, + PRTERRINFO pErrInfo, PRTCRSTOREINMEMCERT *ppEntry) +{ + int rc; + PRTCRSTOREINMEMCERT pEntry = (PRTCRSTOREINMEMCERT)RTMemAllocZ(RT_UOFFSETOF_DYN(RTCRSTOREINMEMCERT, abEncoded[cbSrc])); + if (pEntry) + { + memcpy(pEntry->abEncoded, pbSrc, cbSrc); + pEntry->Core.u32Magic = RTCRCERTCTXINT_MAGIC; + pEntry->Core.cRefs = 1; + pEntry->Core.pfnDtor = rtCrStoreInMemCertEntry_Dtor; + pEntry->Core.Public.fFlags = fEnc; + pEntry->Core.Public.cbEncoded = cbSrc; + pEntry->Core.Public.pabEncoded = &pEntry->abEncoded[0]; + if (fEnc == RTCRCERTCTX_F_ENC_X509_DER) + { + pEntry->Core.Public.pCert = &pEntry->u.X509Cert; + pEntry->Core.Public.pTaInfo = NULL; + } + else + { + pEntry->Core.Public.pCert = NULL; + pEntry->Core.Public.pTaInfo = &pEntry->u.TaInfo; + } + pEntry->pStore = pThis; + + RTASN1CURSORPRIMARY Cursor; + RTAsn1CursorInitPrimary(&Cursor, &pEntry->abEncoded[0], cbSrc, pErrInfo, &g_RTAsn1DefaultAllocator, + RTASN1CURSOR_FLAGS_DER, "InMem"); + if (fEnc == RTCRCERTCTX_F_ENC_X509_DER) + rc = RTCrX509Certificate_DecodeAsn1(&Cursor.Cursor, 0, &pEntry->u.X509Cert, "Cert"); + else + rc = RTCrTafTrustAnchorInfo_DecodeAsn1(&Cursor.Cursor, 0, &pEntry->u.TaInfo, "TaInfo"); + if (RT_SUCCESS(rc)) + { + if (fEnc == RTCRCERTCTX_F_ENC_X509_DER) + rc = RTCrX509Certificate_CheckSanity(&pEntry->u.X509Cert, 0, pErrInfo, "Cert"); + else + rc = RTCrTafTrustAnchorInfo_CheckSanity(&pEntry->u.TaInfo, 0, pErrInfo, "TaInfo"); + if (RT_SUCCESS(rc)) + { + *ppEntry = pEntry; + return VINF_SUCCESS; + } + + RTAsn1VtDelete(&pEntry->u.Asn1Core); + } + RTMemFree(pEntry); + } + else + rc = VERR_NO_MEMORY; + return rc; +} + + +/** + * Grows the certificate pointer array to at least @a cMin entries. + * + * @returns IPRT status code. + * @param pThis The in-memory store instance. + * @param cMin The new minimum store size. + */ +static int rtCrStoreInMemGrow(PRTCRSTOREINMEM pThis, uint32_t cMin) +{ + AssertReturn(cMin <= _1M, VERR_OUT_OF_RANGE); + AssertReturn(cMin > pThis->cCertsAlloc, VERR_INTERNAL_ERROR_3); + + if (cMin < 64) + cMin = RT_ALIGN_32(cMin, 8); + else + cMin = RT_ALIGN_32(cMin, 32); + + void *pv = RTMemRealloc(pThis->papCerts, cMin * sizeof(pThis->papCerts[0])); + if (pv) + { + pThis->papCerts = (PRTCRSTOREINMEMCERT *)pv; + for (uint32_t i = pThis->cCertsAlloc; i < cMin; i++) + pThis->papCerts[i] = NULL; + pThis->cCertsAlloc = cMin; + return VINF_SUCCESS; + } + return VERR_NO_MEMORY; +} + + + +/** @interface_method_impl{RTCRSTOREPROVIDER,pfnDestroyStore} */ +static DECLCALLBACK(void) rtCrStoreInMem_DestroyStore(void *pvProvider) +{ + PRTCRSTOREINMEM pThis = (PRTCRSTOREINMEM)pvProvider; + + while (pThis->cCerts > 0) + { + uint32_t i = --pThis->cCerts; + PRTCRSTOREINMEMCERT pEntry = pThis->papCerts[i]; + pThis->papCerts[i] = NULL; + AssertPtr(pEntry); + + pEntry->pStore = NULL; + RTCrCertCtxRelease(&pEntry->Core.Public); + } + + RTMemFree(pThis->papCerts); + pThis->papCerts = NULL; + + if (pThis->hParentStore != NIL_RTCRSTORE) + { + RTCrStoreRelease(pThis->hParentStore); + pThis->hParentStore = NIL_RTCRSTORE; + } + + RTMemFree(pThis); +} + + +/** @interface_method_impl{RTCRSTOREPROVIDER,pfnCertCtxQueryPrivateKey} */ +static DECLCALLBACK(int) rtCrStoreInMem_CertCtxQueryPrivateKey(void *pvProvider, PRTCRCERTCTXINT pCertCtx, + uint8_t *pbKey, size_t cbKey, size_t *pcbKeyRet) +{ + RT_NOREF_PV(pvProvider); RT_NOREF_PV(pCertCtx); RT_NOREF_PV(pbKey); RT_NOREF_PV(cbKey); RT_NOREF_PV(pcbKeyRet); + //PRTCRSTOREINMEM pThis = (PRTCRSTOREINMEM)pvProvider; + return VERR_NOT_FOUND; +} + + +/** @interface_method_impl{RTCRSTOREPROVIDER,pfnCertFindAll} */ +static DECLCALLBACK(int) rtCrStoreInMem_CertFindAll(void *pvProvider, PRTCRSTORECERTSEARCH pSearch) +{ + pSearch->auOpaque[0] = ~(uintptr_t)pvProvider; + pSearch->auOpaque[1] = 0; + pSearch->auOpaque[2] = ~(uintptr_t)0; /* For the front-end API. */ + pSearch->auOpaque[3] = ~(uintptr_t)0; /* For the front-end API. */ + return VINF_SUCCESS; +} + + +/** @interface_method_impl{RTCRSTOREPROVIDER,pfnCertSearchNext} */ +static DECLCALLBACK(PCRTCRCERTCTX) rtCrStoreInMem_CertSearchNext(void *pvProvider, PRTCRSTORECERTSEARCH pSearch) +{ + PRTCRSTOREINMEM pThis = (PRTCRSTOREINMEM)pvProvider; + if (pSearch->auOpaque[0] == ~(uintptr_t)pvProvider) + { + uintptr_t i = pSearch->auOpaque[1]; + if (i < pThis->cCerts) + { + pSearch->auOpaque[1] = i + 1; + PRTCRCERTCTXINT pCertCtx = &pThis->papCerts[i]->Core; + ASMAtomicIncU32(&pCertCtx->cRefs); + return &pCertCtx->Public; + } + + /* Do we have a parent store to search? */ + if (pThis->hParentStore == NIL_RTCRSTORE) + return NULL; /* no */ + if ( !pThis->pParentProvider->pfnCertFindAll + || !pThis->pParentProvider->pfnCertSearchNext) + return NULL; + + RTCRSTORECERTSEARCH const SavedSearch = *pSearch; + int rc = pThis->pParentProvider->pfnCertFindAll(pThis->pvParentProvider, pSearch); + AssertRCReturnStmt(rc, *pSearch = SavedSearch, NULL); + + /* Restore the store.cpp specifics: */ + AssertCompile(RT_ELEMENTS(SavedSearch.auOpaque) == 4); + pSearch->auOpaque[2] = SavedSearch.auOpaque[2]; + pSearch->auOpaque[3] = SavedSearch.auOpaque[3]; + } + + AssertReturn(pThis->pParentProvider, NULL); + AssertReturn(pThis->pParentProvider->pfnCertSearchNext, NULL); + return pThis->pParentProvider->pfnCertSearchNext(pThis->pvParentProvider, pSearch); +} + + +/** @interface_method_impl{RTCRSTOREPROVIDER,pfnCertSearchDestroy} */ +static DECLCALLBACK(void) rtCrStoreInMem_CertSearchDestroy(void *pvProvider, PRTCRSTORECERTSEARCH pSearch) +{ + PRTCRSTOREINMEM pThis = (PRTCRSTOREINMEM)pvProvider; + if (pSearch->auOpaque[0] == ~(uintptr_t)pvProvider) + { + pSearch->auOpaque[0] = 0; + pSearch->auOpaque[1] = 0; + pSearch->auOpaque[2] = 0; + pSearch->auOpaque[3] = 0; + } + else + { + AssertReturnVoid(pThis->pParentProvider); + AssertReturnVoid(pThis->pParentProvider->pfnCertSearchDestroy); + pThis->pParentProvider->pfnCertSearchDestroy(pThis->pvParentProvider, pSearch); + } +} + + +/** @interface_method_impl{RTCRSTOREPROVIDER,pfnCertSearchDestroy} */ +static DECLCALLBACK(int) rtCrStoreInMem_CertAddEncoded(void *pvProvider, uint32_t fFlags, + uint8_t const *pbEncoded, uint32_t cbEncoded, PRTERRINFO pErrInfo) +{ + PRTCRSTOREINMEM pThis = (PRTCRSTOREINMEM)pvProvider; + int rc; + + AssertMsgReturn( (fFlags & RTCRCERTCTX_F_ENC_MASK) == RTCRCERTCTX_F_ENC_X509_DER + || (fFlags & RTCRCERTCTX_F_ENC_MASK) == RTCRCERTCTX_F_ENC_TAF_DER + , ("Only X.509 and TAF DER are supported: %#x\n", fFlags), VERR_INVALID_FLAGS); + + /* + * Check for duplicates if specified. + */ + if (fFlags & RTCRCERTCTX_F_ADD_IF_NOT_FOUND) + { + uint32_t iCert = pThis->cCerts; + while (iCert-- > 0) + { + PRTCRSTOREINMEMCERT pCert = pThis->papCerts[iCert]; + if ( pCert->Core.Public.cbEncoded == cbEncoded + && pCert->Core.Public.fFlags == (fFlags & RTCRCERTCTX_F_ENC_MASK) + && memcmp(pCert->Core.Public.pabEncoded, pbEncoded, cbEncoded) == 0) + return VWRN_ALREADY_EXISTS; + } + } + + /* + * Add it. + */ + if (pThis->cCerts + 1 <= pThis->cCertsAlloc) + { /* likely */ } + else + { + rc = rtCrStoreInMemGrow(pThis, pThis->cCerts + 1); + if (RT_FAILURE(rc)) + return rc; + } + + rc = rtCrStoreInMemCreateCertEntry(pThis, fFlags & RTCRCERTCTX_F_ENC_MASK, pbEncoded, cbEncoded, + pErrInfo, &pThis->papCerts[pThis->cCerts]); + if (RT_SUCCESS(rc)) + { + pThis->cCerts++; + return VINF_SUCCESS; + } + return rc; +} + + +/** + * In-memory store provider. + */ +static RTCRSTOREPROVIDER const g_rtCrStoreInMemProvider = +{ + "in-memory", + rtCrStoreInMem_DestroyStore, + rtCrStoreInMem_CertCtxQueryPrivateKey, + rtCrStoreInMem_CertFindAll, + rtCrStoreInMem_CertSearchNext, + rtCrStoreInMem_CertSearchDestroy, + rtCrStoreInMem_CertAddEncoded, + NULL, + 42 +}; + + +/** + * Common worker for RTCrStoreCreateInMem and future constructors... + * + * @returns IPRT status code. + * @param ppStore Where to return the store instance. + * @param hParentStore Optional parent store. Consums reference on + * success. + */ +static int rtCrStoreInMemCreateInternal(PRTCRSTOREINMEM *ppStore, RTCRSTORE hParentStore) +{ + PRTCRSTOREINMEM pStore = (PRTCRSTOREINMEM)RTMemAlloc(sizeof(*pStore)); + if (pStore) + { + pStore->cCerts = 0; + pStore->cCertsAlloc = 0; + pStore->papCerts = NULL; + pStore->hParentStore = hParentStore; + pStore->pParentProvider = NULL; + pStore->pvParentProvider = NULL; + *ppStore = pStore; + if (hParentStore == NIL_RTCRSTORE) + return VINF_SUCCESS; + if (~(uintptr_t)hParentStore != ~(uintptr_t)pStore) + { + pStore->pParentProvider = rtCrStoreGetProvider(hParentStore, &pStore->pvParentProvider); + if (pStore->pParentProvider) + return VINF_SUCCESS; + AssertFailed(); + } + RTMemFree(pStore); + } + *ppStore = NULL; /* shut up gcc-maybe-pita warning. */ + return VERR_NO_MEMORY; +} + + +RTDECL(int) RTCrStoreCreateInMemEx(PRTCRSTORE phStore, uint32_t cSizeHint, RTCRSTORE hParentStore) +{ + if (hParentStore != NIL_RTCRSTORE) + { + uint32_t cRefs = RTCrStoreRetain(hParentStore); + AssertReturn(cRefs != UINT32_MAX, VERR_INVALID_HANDLE); + } + + PRTCRSTOREINMEM pStore; + int rc = rtCrStoreInMemCreateInternal(&pStore, hParentStore); + if (RT_SUCCESS(rc)) + { + if (cSizeHint) + rc = rtCrStoreInMemGrow(pStore, RT_MIN(cSizeHint, 512)); + if (RT_SUCCESS(rc)) + { + rc = rtCrStoreCreate(&g_rtCrStoreInMemProvider, pStore, phStore); + if (RT_SUCCESS(rc)) + return VINF_SUCCESS; + } + RTMemFree(pStore); + } + + RTCrStoreRelease(hParentStore); + return rc; +} +RT_EXPORT_SYMBOL(RTCrStoreCreateInMemEx); + + +RTDECL(int) RTCrStoreCreateInMem(PRTCRSTORE phStore, uint32_t cSizeHint) +{ + return RTCrStoreCreateInMemEx(phStore, cSizeHint, NIL_RTCRSTORE); +} +RT_EXPORT_SYMBOL(RTCrStoreCreateInMem); + |