store.cpp revision 13493ab7596e827b8d0caab2c89e635dd65f78f9
/* $Id$ */
/** @file
* IPRT - Cryptographic (Certificate) Store.
*/
/*
* Copyright (C) 2006-2014 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/asm.h>
#include <iprt/err.h>
#include <iprt/mem.h>
#include <iprt/string.h>
#include <iprt/crypto/x509.h>
#ifdef IPRT_WITH_OPENSSL
# include <openssl/x509.h>
#endif
#include "store-internal.h"
/*******************************************************************************
* Structures and Typedefs *
*******************************************************************************/
/**
* Internal representation of a (certificate,++) store.
*/
typedef struct RTCRSTOREINT
{
/** Magic number (RTCRSTOREINT_MAGIC). */
uint32_t u32Magic;
/** Reference counter. */
uint32_t volatile cRefs;
/** Pointer to the store provider. */
PCRTCRSTOREPROVIDER pProvider;
/** Provider specific data. */
void *pvProvider;
} RTCRSTOREINT;
/** Pointer to the internal representation of a store. */
typedef RTCRSTOREINT *PRTCRSTOREINT;
/** Magic value for RTCRSTOREINT::u32Magic (Alfred Dillwyn "Dilly" Knox). */
#define RTCRSTOREINT_MAGIC UINT32_C(0x18840723)
/** Dead magic value for RTCRSTOREINT::u32Magic. */
#define RTCRSTOREINT_MAGIC_DEAD UINT32_C(0x19430227)
/**
* Internal method a store provider uses to create a store handle.
*
* @returns IPRT status code
* @param pProvider Pointer to the store provider callback table.
* @param pvProvider Pointer to the provider specific instance data.
* @param phStore Where to return the store handle.
*/
DECLHIDDEN(int) rtCrStoreCreate(PCRTCRSTOREPROVIDER pProvider, void *pvProvider, PRTCRSTORE phStore)
{
PRTCRSTOREINT pThis = (PRTCRSTOREINT)RTMemAlloc(sizeof(*pThis));
if (pThis)
{
pThis->pvProvider = pvProvider;
pThis->pProvider = pProvider;
pThis->cRefs = 1;
pThis->u32Magic = RTCRSTOREINT_MAGIC;
*phStore = pThis;
return VINF_SUCCESS;
}
return VERR_NO_MEMORY;
}
RTDECL(uint32_t) RTCrStoreRetain(RTCRSTORE hStore)
{
PRTCRSTOREINT pThis = (PRTCRSTOREINT)hStore;
AssertPtrReturn(pThis, UINT32_MAX);
AssertReturn(pThis->u32Magic == RTCRSTOREINT_MAGIC, UINT32_MAX);
uint32_t cRet = ASMAtomicIncU32(&pThis->cRefs);
Assert(cRet < 8192);
return cRet;
}
RTDECL(uint32_t) RTCrStoreRelease(RTCRSTORE hStore)
{
if (hStore == NIL_RTCRSTORE)
return 0;
PRTCRSTOREINT pThis = (PRTCRSTOREINT)hStore;
AssertPtrReturn(pThis, UINT32_MAX);
AssertReturn(pThis->u32Magic == RTCRSTOREINT_MAGIC, UINT32_MAX);
uint32_t cStore = ASMAtomicDecU32(&pThis->cRefs);
if (!cStore)
{
ASMAtomicWriteU32(&pThis->u32Magic, RTCRSTOREINT_MAGIC_DEAD);
pThis->pProvider->pfnDestroyStore(pThis->pvProvider);
RTMemFree(pThis);
}
return cStore;
}
RTDECL(PCRTCRCERTCTX) RTCrStoreCertByIssuerAndSerialNo(RTCRSTORE hStore, PCRTCRX509NAME pIssuer, PCRTASN1INTEGER pSerialNo)
{
PRTCRSTOREINT pThis = (PRTCRSTOREINT)hStore;
AssertPtrReturn(pThis, NULL);
AssertReturn(pThis->u32Magic == RTCRSTOREINT_MAGIC, NULL);
AssertPtrReturn(pIssuer, NULL);
int rc;
RTCRSTORECERTSEARCH Search;
if (pThis->pProvider->pfnCertFindByIssuerAndSerialNo)
rc = pThis->pProvider->pfnCertFindByIssuerAndSerialNo(pThis->pvProvider, pIssuer, pSerialNo, &Search);
else
rc = pThis->pProvider->pfnCertFindAll(pThis->pvProvider, &Search);
PCRTCRCERTCTX pCertCtx = NULL;
if (RT_SUCCESS(rc))
{
for (;;)
{
pCertCtx = pThis->pProvider->pfnCertSearchNext(pThis->pvProvider, &Search);
if (!pCertCtx)
break;
if ( pCertCtx->pCert
&& RTCrX509Certificate_MatchIssuerAndSerialNumber(pCertCtx->pCert, pIssuer, pSerialNo))
break;
RTCrCertCtxRelease(pCertCtx);
}
pThis->pProvider->pfnCertSearchDestroy(pThis->pvProvider, &Search);
}
else
AssertMsg(rc == VERR_NOT_FOUND, ("%Rrc\n", rc));
return pCertCtx;
}
RTDECL(int) RTCrStoreCertAddEncoded(RTCRSTORE hStore, uint32_t fFlags, void const *pvSrc, size_t cbSrc, PRTERRINFO pErrInfo)
{
PRTCRSTOREINT pThis = (PRTCRSTOREINT)hStore;
AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
AssertReturn(pThis->u32Magic == RTCRSTOREINT_MAGIC, VERR_INVALID_HANDLE);
AssertPtrReturn(pvSrc, VERR_INVALID_POINTER);
AssertReturn(cbSrc > 16 && cbSrc < _1M, VERR_OUT_OF_RANGE);
AssertMsgReturn( fFlags == RTCRCERTCTX_F_ENC_X509_DER
|| fFlags == RTCRCERTCTX_F_ENC_TAF_DER
, ("Only X.509 and TAF DER supported: %#x\n", fFlags), VERR_INVALID_FLAGS);
int rc;
if (pThis->pProvider->pfnCertAddEncoded)
rc = pThis->pProvider->pfnCertAddEncoded(pThis->pvProvider, fFlags, (uint8_t const *)pvSrc, (uint32_t)cbSrc, pErrInfo);
else
rc = VERR_WRITE_PROTECT;
return rc;
}
/*
* Searching.
* Searching.
* Searching.
*/
RTDECL(int) RTCrStoreCertFindAll(RTCRSTORE hStore, PRTCRSTORECERTSEARCH pSearch)
{
PRTCRSTOREINT pThis = (PRTCRSTOREINT)hStore;
AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
AssertReturn(pThis->u32Magic == RTCRSTOREINT_MAGIC, VERR_INVALID_HANDLE);
AssertPtrReturn(pSearch, VERR_INVALID_POINTER);
return pThis->pProvider->pfnCertFindAll(pThis->pvProvider, pSearch);
}
/** Indicator for RTCrStoreCertFindBySubjectOrAltSubjectByRfc5280 searches
* implemented by this front-end code. */
#define RTCRSTORECERTSEARCH_BY_SUBECT_OR_ALT_SUBJECT_BY_RFC5280 UINT32_C(0x5be9145d)
RTDECL(int) RTCrStoreCertFindBySubjectOrAltSubjectByRfc5280(RTCRSTORE hStore, PCRTCRX509NAME pSubject,
PRTCRSTORECERTSEARCH pSearch)
{
PRTCRSTOREINT pThis = (PRTCRSTOREINT)hStore;
AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
AssertReturn(pThis->u32Magic == RTCRSTOREINT_MAGIC, VERR_INVALID_HANDLE);
AssertPtrReturn(pSearch, VERR_INVALID_POINTER);
int rc = pThis->pProvider->pfnCertFindAll(pThis->pvProvider, pSearch);
if (RT_SUCCESS(rc))
{
pSearch->auOpaque[2] = RTCRSTORECERTSEARCH_BY_SUBECT_OR_ALT_SUBJECT_BY_RFC5280;
pSearch->auOpaque[3] = (uintptr_t)pSubject;
}
return rc;
}
RTDECL(PCRTCRCERTCTX) RTCrStoreCertSearchNext(RTCRSTORE hStore, PRTCRSTORECERTSEARCH pSearch)
{
PRTCRSTOREINT pThis = (PRTCRSTOREINT)hStore;
AssertPtrReturn(pThis, NULL);
AssertReturn(pThis->u32Magic == RTCRSTOREINT_MAGIC, NULL);
AssertPtrReturn(pSearch, NULL);
PCRTCRCERTCTX pRet;
switch (pSearch->auOpaque[2])
{
default:
pRet = pThis->pProvider->pfnCertSearchNext(pThis->pvProvider, pSearch);
break;
case RTCRSTORECERTSEARCH_BY_SUBECT_OR_ALT_SUBJECT_BY_RFC5280:
{
PCRTCRX509NAME pSubject = (PCRTCRX509NAME)pSearch->auOpaque[3];
AssertPtrReturn(pSubject, NULL);
for (;;)
{
pRet = pThis->pProvider->pfnCertSearchNext(pThis->pvProvider, pSearch);
if (!pRet)
break;
if (pRet->pCert)
{
if (RTCrX509Certificate_MatchSubjectOrAltSubjectByRfc5280(pRet->pCert, pSubject))
break;
}
else if (pRet->pTaInfo)
{
if ( RTCrTafCertPathControls_IsPresent(&pRet->pTaInfo->CertPath)
&& RTCrX509Name_MatchByRfc5280(&pRet->pTaInfo->CertPath.TaName, pSubject))
break;
}
RTCrCertCtxRelease(pRet);
}
break;
}
}
return pRet;
}
RTDECL(int) RTCrStoreCertSearchDestroy(RTCRSTORE hStore, PRTCRSTORECERTSEARCH pSearch)
{
PRTCRSTOREINT pThis = (PRTCRSTOREINT)hStore;
AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
AssertReturn(pThis->u32Magic == RTCRSTOREINT_MAGIC, VERR_INVALID_HANDLE);
if (pSearch)
{
AssertPtrReturn(pSearch, VERR_INVALID_POINTER);
pThis->pProvider->pfnCertSearchDestroy(pThis->pvProvider, pSearch);
}
return VINF_SUCCESS;
}
#ifdef IPRT_WITH_OPENSSL
/*
* OpenSSL helper.
* OpenSSL helper.
* OpenSSL helper.
*/
RTDECL(int) RTCrStoreConvertToOpenSslCertStore(RTCRSTORE hStore, uint32_t fFlags, void **ppvOpenSslStore)
{
PRTCRSTOREINT pThis = (PRTCRSTOREINT)hStore;
AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
AssertReturn(pThis->u32Magic == RTCRSTOREINT_MAGIC, VERR_INVALID_HANDLE);
/*
* Use the pfnCertFindAll method to add all certificates to the store we're returning.
*/
int rc;
X509_STORE *pOsslStore = X509_STORE_new();
if (pOsslStore)
{
RTCRSTORECERTSEARCH Search;
rc = pThis->pProvider->pfnCertFindAll(pThis->pvProvider, &Search);
if (RT_SUCCESS(rc))
{
do
{
PCRTCRCERTCTX pCertCtx = pThis->pProvider->pfnCertSearchNext(pThis->pvProvider, &Search);
if (!pCertCtx)
break;
if (pCertCtx->fFlags & RTCRCERTCTX_F_ENC_X509_DER)
{
X509 *pOsslCert = NULL;
const unsigned char *pabEncoded = (const unsigned char *)pCertCtx->pabEncoded;
if (d2i_X509(&pOsslCert, &pabEncoded, pCertCtx->cbEncoded) == pOsslCert)
{
if (!X509_STORE_add_cert(pOsslStore, pOsslCert))
rc = VERR_NO_MEMORY;
X509_free(pOsslCert);
}
}
RTCrCertCtxRelease(pCertCtx);
} while (RT_SUCCESS(rc));
pThis->pProvider->pfnCertSearchDestroy(pThis->pvProvider, &Search);
if (RT_SUCCESS(rc))
{
*ppvOpenSslStore = pOsslStore;
return VINF_SUCCESS;
}
}
X509_STORE_free(pOsslStore);
}
return rc;
}
RTDECL(int) RTCrStoreConvertToOpenSslCertStack(RTCRSTORE hStore, uint32_t fFlags, void **ppvOpenSslStack)
{
PRTCRSTOREINT pThis = (PRTCRSTOREINT)hStore;
AssertPtrReturn(pThis, VERR_INVALID_HANDLE);
AssertReturn(pThis->u32Magic == RTCRSTOREINT_MAGIC, VERR_INVALID_HANDLE);
/*
* Use the pfnCertFindAll method to add all certificates to the store we're returning.
*/
int rc;
STACK_OF(X509) *pOsslStack = sk_X509_new_null();
if (pOsslStack)
{
RTCRSTORECERTSEARCH Search;
rc = pThis->pProvider->pfnCertFindAll(pThis->pvProvider, &Search);
if (RT_SUCCESS(rc))
{
do
{
PCRTCRCERTCTX pCertCtx = pThis->pProvider->pfnCertSearchNext(pThis->pvProvider, &Search);
if (!pCertCtx)
break;
if (pCertCtx->fFlags & RTCRCERTCTX_F_ENC_X509_DER)
{
X509 *pOsslCert = NULL;
const unsigned char *pabEncoded = (const unsigned char *)pCertCtx->pabEncoded;
if (d2i_X509(&pOsslCert, &pabEncoded, pCertCtx->cbEncoded) == pOsslCert)
{
if (!sk_X509_push(pOsslStack, pOsslCert))
{
rc = VERR_NO_MEMORY;
X509_free(pOsslCert);
}
}
}
RTCrCertCtxRelease(pCertCtx);
} while (RT_SUCCESS(rc));
pThis->pProvider->pfnCertSearchDestroy(pThis->pvProvider, &Search);
if (RT_SUCCESS(rc))
{
*ppvOpenSslStack = pOsslStack;
return VINF_SUCCESS;
}
}
sk_X509_pop_free(pOsslStack, X509_free);
}
return rc;
}
#endif /* IPRT_WITH_OPENSSL */
/*
* Certificate context.
* Certificate context.
* Certificate context.
*/
RTDECL(uint32_t) RTCrCertCtxRetain(PCRTCRCERTCTX pCertCtx)
{
AssertPtrReturn(pCertCtx, UINT32_MAX);
PRTCRCERTCTXINT pThis = RT_FROM_MEMBER(pCertCtx, RTCRCERTCTXINT, Public);
AssertReturn(pThis->u32Magic == RTCRCERTCTXINT_MAGIC, UINT32_MAX);
uint32_t cRet = ASMAtomicIncU32(&pThis->cRefs);
Assert(cRet < 64);
return cRet;
}
RTDECL(uint32_t) RTCrCertCtxRelease(PCRTCRCERTCTX pCertCtx)
{
if (!pCertCtx)
return 0;
AssertPtrReturn(pCertCtx, UINT32_MAX);
PRTCRCERTCTXINT pThis = RT_FROM_MEMBER(pCertCtx, RTCRCERTCTXINT, Public);
AssertReturn(pThis->u32Magic == RTCRCERTCTXINT_MAGIC, UINT32_MAX);
uint32_t cRet = ASMAtomicDecU32(&pThis->cRefs);
if (!cRet)
{
ASMAtomicWriteU32(&pThis->u32Magic, RTCRCERTCTXINT_MAGIC_DEAD);
pThis->pfnDtor(pThis);
}
return cRet;
}