567 lines
14 KiB
C++
567 lines
14 KiB
C++
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 1997 - 1999
|
|
//
|
|
// File: trustdb.cpp
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
//
|
|
// PersonalTrustDb.cpp
|
|
//
|
|
// Code that maintains a list of trusted publishers, agencies, and so on.
|
|
|
|
#include "global.hxx"
|
|
#include "cryptreg.h"
|
|
#include "trustdb.h"
|
|
|
|
/////////////////////////////////////////////////////////
|
|
|
|
DECLARE_INTERFACE (IUnkInner)
|
|
{
|
|
STDMETHOD(InnerQueryInterface) (THIS_ REFIID iid, LPVOID* ppv) PURE;
|
|
STDMETHOD_ (ULONG, InnerAddRef) (THIS) PURE;
|
|
STDMETHOD_ (ULONG, InnerRelease) (THIS) PURE;
|
|
};
|
|
|
|
/////////////////////////////////////////////////////////
|
|
|
|
|
|
extern "C" const GUID IID_IPersonalTrustDB = IID_IPersonalTrustDB_Data;
|
|
|
|
/////////////////////////////////////////////////////////
|
|
|
|
HRESULT WINAPI OpenTrustDB(IUnknown* punkOuter, REFIID iid, void** ppv);
|
|
|
|
class CTrustDB : IPersonalTrustDB, IUnkInner
|
|
{
|
|
LONG m_refs; // our reference count
|
|
IUnknown* m_punkOuter; // our controlling unknown (may be us ourselves)
|
|
|
|
HCERTSTORE m_hPubStore; // publisher store
|
|
|
|
public:
|
|
static HRESULT CreateInstance(IUnknown* punkOuter, REFIID iid, void** ppv);
|
|
|
|
private:
|
|
STDMETHODIMP QueryInterface(THIS_ REFIID riid, LPVOID FAR* ppvObj);
|
|
STDMETHODIMP_(ULONG) AddRef(THIS);
|
|
STDMETHODIMP_(ULONG) Release(THIS);
|
|
|
|
STDMETHODIMP InnerQueryInterface(REFIID iid, LPVOID* ppv);
|
|
STDMETHODIMP_(ULONG) InnerAddRef();
|
|
STDMETHODIMP_(ULONG) InnerRelease();
|
|
|
|
STDMETHODIMP IsTrustedCert(DWORD dwEncodingType, PCCERT_CONTEXT pCert, LONG iLevel, BOOL fCommercial, PCCERT_CONTEXT *ppPubCert);
|
|
STDMETHODIMP AddTrustCert(PCCERT_CONTEXT pCert, LONG iLevel, BOOL fLowerLevelsToo);
|
|
|
|
STDMETHODIMP RemoveTrustCert(PCCERT_CONTEXT pCert, LONG iLevel, BOOL fLowerLevelsToo);
|
|
STDMETHODIMP RemoveTrustToken(LPWSTR, LONG iLevel, BOOL fLowerLevelsToo);
|
|
|
|
STDMETHODIMP AreCommercialPublishersTrusted();
|
|
STDMETHODIMP SetCommercialPublishersTrust(BOOL fTrust);
|
|
|
|
STDMETHODIMP GetTrustList(
|
|
LONG iLevel, // the cert chain level to get
|
|
BOOL fLowerLevelsToo, // included lower levels, remove duplicates
|
|
TRUSTLISTENTRY** prgTrustList, // place to return the trust list
|
|
ULONG* pcTrustList // place to return the size of the returned trust list
|
|
);
|
|
private:
|
|
CTrustDB(IUnknown* punkOuter);
|
|
~CTrustDB();
|
|
HRESULT Init();
|
|
|
|
};
|
|
|
|
|
|
// Helper functions
|
|
|
|
// # of bytes for a hash. Such as, SHA (20) or MD5 (16)
|
|
#define MAX_HASH_LEN 20
|
|
#define SHA1_HASH_LEN 20
|
|
|
|
// Null terminated ascii hex characters of the hash.
|
|
#define MAX_HASH_NAME_LEN (2 * MAX_HASH_LEN + 1)
|
|
|
|
PCCERT_CONTEXT FindCertificateInOtherStore(
|
|
IN HCERTSTORE hOtherStore,
|
|
IN PCCERT_CONTEXT pCert
|
|
)
|
|
{
|
|
BYTE rgbHash[SHA1_HASH_LEN];
|
|
CRYPT_DATA_BLOB HashBlob;
|
|
|
|
HashBlob.pbData = rgbHash;
|
|
HashBlob.cbData = SHA1_HASH_LEN;
|
|
if (!CertGetCertificateContextProperty(
|
|
pCert,
|
|
CERT_SHA1_HASH_PROP_ID,
|
|
rgbHash,
|
|
&HashBlob.cbData
|
|
) || SHA1_HASH_LEN != HashBlob.cbData)
|
|
return NULL;
|
|
|
|
return CertFindCertificateInStore(
|
|
hOtherStore,
|
|
0, // dwCertEncodingType
|
|
0, // dwFindFlags
|
|
CERT_FIND_SHA1_HASH,
|
|
(const void *) &HashBlob,
|
|
NULL //pPrevCertContext
|
|
);
|
|
}
|
|
|
|
BOOL IsCertificateInOtherStore(
|
|
IN HCERTSTORE hOtherStore,
|
|
IN PCCERT_CONTEXT pCert,
|
|
OUT OPTIONAL PCCERT_CONTEXT *ppOtherCert
|
|
)
|
|
{
|
|
PCCERT_CONTEXT pOtherCert;
|
|
|
|
if (pOtherCert = FindCertificateInOtherStore(hOtherStore, pCert)) {
|
|
if (ppOtherCert)
|
|
*ppOtherCert = pOtherCert;
|
|
else
|
|
CertFreeCertificateContext(pOtherCert);
|
|
return TRUE;
|
|
} else {
|
|
if (ppOtherCert)
|
|
*ppOtherCert = NULL;
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
BOOL DeleteCertificateFromOtherStore(
|
|
IN HCERTSTORE hOtherStore,
|
|
IN PCCERT_CONTEXT pCert
|
|
)
|
|
{
|
|
BOOL fResult;
|
|
PCCERT_CONTEXT pOtherCert;
|
|
|
|
if (pOtherCert = FindCertificateInOtherStore(hOtherStore, pCert))
|
|
fResult = CertDeleteCertificateFromStore(pOtherCert);
|
|
else
|
|
fResult = FALSE;
|
|
return fResult;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// Converts the bytes into UNICODE ASCII HEX
|
|
//
|
|
// Needs (cb * 2 + 1) * sizeof(WCHAR) bytes of space in wsz
|
|
//--------------------------------------------------------------------------
|
|
void BytesToWStr(DWORD cb, void* pv, LPWSTR wsz)
|
|
{
|
|
BYTE* pb = (BYTE*) pv;
|
|
for (DWORD i = 0; i<cb; i++) {
|
|
int b;
|
|
b = (*pb & 0xF0) >> 4;
|
|
*wsz++ = (b <= 9) ? b + L'0' : (b - 10) + L'A';
|
|
b = *pb & 0x0F;
|
|
*wsz++ = (b <= 9) ? b + L'0' : (b - 10) + L'A';
|
|
pb++;
|
|
}
|
|
*wsz++ = 0;
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// Converts the UNICODE ASCII HEX to an array of bytes
|
|
//--------------------------------------------------------------------------
|
|
void WStrToBytes(
|
|
IN const WCHAR wsz[MAX_HASH_NAME_LEN],
|
|
OUT BYTE rgb[MAX_HASH_LEN],
|
|
OUT DWORD *pcb
|
|
)
|
|
{
|
|
BOOL fUpperNibble = TRUE;
|
|
DWORD cb = 0;
|
|
LPCWSTR pwsz = wsz;
|
|
WCHAR wch;
|
|
|
|
while (cb < MAX_HASH_LEN && (wch = *pwsz++)) {
|
|
BYTE b;
|
|
|
|
// only convert ascii hex characters 0..9, a..f, A..F
|
|
// silently ignore all others
|
|
if (wch >= L'0' && wch <= L'9')
|
|
b = (BYTE) (wch - L'0');
|
|
else if (wch >= L'a' && wch <= L'f')
|
|
b = (BYTE) (10 + wch - L'a');
|
|
else if (wch >= L'A' && wch <= L'F')
|
|
b = (BYTE) (10 + wch - L'A');
|
|
else
|
|
continue;
|
|
|
|
if (fUpperNibble) {
|
|
rgb[cb] = b << 4;
|
|
fUpperNibble = FALSE;
|
|
} else {
|
|
rgb[cb] = rgb[cb] | b;
|
|
cb++;
|
|
fUpperNibble = TRUE;
|
|
}
|
|
}
|
|
|
|
*pcb = cb;
|
|
}
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CTrustDB::IsTrustedCert(DWORD dwEncodingType,
|
|
PCCERT_CONTEXT pCert,
|
|
LONG iLevel,
|
|
BOOL fCommercial,
|
|
PCCERT_CONTEXT *ppPubCert
|
|
)
|
|
{
|
|
HRESULT hr;
|
|
|
|
|
|
if (NULL == m_hPubStore)
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
// See if the cert is in the trusted publisher store
|
|
if (IsCertificateInOtherStore(m_hPubStore, pCert, ppPubCert))
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = S_FALSE;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTrustDB::AddTrustCert(PCCERT_CONTEXT pCert, LONG iLevel, BOOL fLowerLevelsToo)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (NULL == m_hPubStore)
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
if (CertAddCertificateContextToStore(
|
|
m_hPubStore,
|
|
pCert,
|
|
CERT_STORE_ADD_USE_EXISTING,
|
|
NULL
|
|
))
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTrustDB::RemoveTrustCert(PCCERT_CONTEXT pCert, LONG iLevel, BOOL fLowerLevelsToo)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (NULL == m_hPubStore)
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
CertDuplicateCertificateContext(pCert);
|
|
if (DeleteCertificateFromOtherStore(
|
|
m_hPubStore,
|
|
pCert
|
|
))
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CTrustDB::RemoveTrustToken(LPWSTR szToken, LONG iLevel, BOOL fLowerLevelsToo)
|
|
{
|
|
HRESULT hr;
|
|
DWORD cbHash;
|
|
BYTE rgbHash[SHA1_HASH_LEN];
|
|
CRYPT_DATA_BLOB HashBlob;
|
|
PCCERT_CONTEXT pDeleteCert;
|
|
|
|
|
|
if (NULL == m_hPubStore)
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
WStrToBytes(szToken, rgbHash, &cbHash);
|
|
HashBlob.pbData = rgbHash;
|
|
HashBlob.cbData = cbHash;
|
|
pDeleteCert = CertFindCertificateInStore(
|
|
m_hPubStore,
|
|
0, // dwCertEncodingType
|
|
0, // dwFindFlags
|
|
CERT_FIND_SHA1_HASH,
|
|
(const void *) &HashBlob,
|
|
NULL //pPrevCertContext
|
|
);
|
|
if (NULL == pDeleteCert)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
else
|
|
{
|
|
if (CertDeleteCertificateFromStore(pDeleteCert))
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CTrustDB::AreCommercialPublishersTrusted()
|
|
// Answer whether commercial publishers are trusted.
|
|
// S_OK == yes
|
|
// S_FALSE == no
|
|
// other == can't tell
|
|
{
|
|
return( S_FALSE );
|
|
}
|
|
|
|
HRESULT CTrustDB::SetCommercialPublishersTrust(BOOL fTrust)
|
|
// Set the commercial trust setting
|
|
{
|
|
return( S_OK );
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CTrustDB::GetTrustList(
|
|
// Return the (unsorted) list of trusted certificate names and their
|
|
// corresponding display names
|
|
//
|
|
LONG iLevel, // the cert chain level to get
|
|
BOOL fLowerLevelsToo, // included lower levels, remove duplicates
|
|
TRUSTLISTENTRY** prgTrustList, // place to return the trust list
|
|
ULONG* pcTrustList // place to return the size of the returned trust list
|
|
) {
|
|
HRESULT hr = S_OK;
|
|
ULONG cTrust = 0;
|
|
ULONG cAllocTrust = 0;
|
|
TRUSTLISTENTRY* rgTrustList = NULL;
|
|
PCCERT_CONTEXT pCert = NULL;
|
|
|
|
|
|
*prgTrustList = NULL;
|
|
*pcTrustList = 0;
|
|
|
|
if (NULL == m_hPubStore)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
// Get count of trusted publisher certs
|
|
pCert = NULL;
|
|
while (pCert = CertEnumCertificatesInStore(m_hPubStore, pCert))
|
|
{
|
|
cTrust++;
|
|
}
|
|
|
|
if (0 == cTrust)
|
|
{
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
rgTrustList = (TRUSTLISTENTRY*) CoTaskMemAlloc(cTrust *
|
|
sizeof(TRUSTLISTENTRY));
|
|
|
|
if (NULL == rgTrustList)
|
|
{
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
memset(rgTrustList, 0, cTrust * sizeof(TRUSTLISTENTRY));
|
|
|
|
cAllocTrust = cTrust;
|
|
cTrust = 0;
|
|
pCert = NULL;
|
|
while (pCert = CertEnumCertificatesInStore(m_hPubStore, pCert))
|
|
{
|
|
BYTE rgbHash[MAX_HASH_LEN];
|
|
DWORD cbHash = MAX_HASH_LEN;
|
|
|
|
// get the thumbprint
|
|
if(!CertGetCertificateContextProperty(
|
|
pCert,
|
|
CERT_SHA1_HASH_PROP_ID,
|
|
rgbHash,
|
|
&cbHash))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// convert to a string
|
|
BytesToWStr(cbHash, rgbHash, rgTrustList[cTrust].szToken);
|
|
|
|
if (1 >= CertGetNameStringW(
|
|
pCert,
|
|
CERT_NAME_FRIENDLY_DISPLAY_TYPE,
|
|
0, // dwFlags
|
|
NULL, // pvTypePara
|
|
rgTrustList[cTrust].szDisplayName,
|
|
sizeof(rgTrustList[cTrust].szDisplayName)/sizeof(WCHAR)
|
|
))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
cTrust++;
|
|
if (cTrust >= cAllocTrust)
|
|
{
|
|
CertFreeCertificateContext(pCert);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (0 == cTrust)
|
|
{
|
|
CoTaskMemFree(rgTrustList);
|
|
rgTrustList = NULL;
|
|
}
|
|
|
|
*pcTrustList = cTrust;
|
|
*prgTrustList = rgTrustList;
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
STDMETHODIMP CTrustDB::QueryInterface(REFIID iid, LPVOID* ppv)
|
|
{
|
|
return (m_punkOuter->QueryInterface(iid, ppv));
|
|
}
|
|
STDMETHODIMP_(ULONG) CTrustDB::AddRef(void)
|
|
{
|
|
return (m_punkOuter->AddRef());
|
|
}
|
|
STDMETHODIMP_(ULONG) CTrustDB::Release(void)
|
|
{
|
|
return (m_punkOuter->Release());
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
STDMETHODIMP CTrustDB::InnerQueryInterface(REFIID iid, LPVOID* ppv)
|
|
{
|
|
*ppv = NULL;
|
|
while (TRUE)
|
|
{
|
|
if (iid == IID_IUnknown)
|
|
{
|
|
*ppv = (LPVOID)((IUnkInner*)this);
|
|
break;
|
|
}
|
|
if (iid == IID_IPersonalTrustDB)
|
|
{
|
|
*ppv = (LPVOID) ((IPersonalTrustDB *) this);
|
|
break;
|
|
}
|
|
return E_NOINTERFACE;
|
|
}
|
|
((IUnknown*)*ppv)->AddRef();
|
|
return S_OK;
|
|
}
|
|
STDMETHODIMP_(ULONG) CTrustDB::InnerAddRef(void)
|
|
{
|
|
return ++m_refs;
|
|
}
|
|
STDMETHODIMP_(ULONG) CTrustDB::InnerRelease(void)
|
|
{
|
|
ULONG refs = --m_refs;
|
|
if (refs == 0)
|
|
{
|
|
m_refs = 1;
|
|
delete this;
|
|
}
|
|
return refs;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT OpenTrustDB(IUnknown* punkOuter, REFIID iid, void** ppv)
|
|
{
|
|
return CTrustDB::CreateInstance(punkOuter, iid, ppv);
|
|
}
|
|
|
|
HRESULT CTrustDB::CreateInstance(IUnknown* punkOuter, REFIID iid, void** ppv)
|
|
{
|
|
HRESULT hr;
|
|
|
|
*ppv = NULL;
|
|
CTrustDB* pnew = new CTrustDB(punkOuter);
|
|
if (pnew == NULL) return E_OUTOFMEMORY;
|
|
if ((hr = pnew->Init()) != S_OK)
|
|
{
|
|
delete pnew;
|
|
return hr;
|
|
}
|
|
IUnkInner* pme = (IUnkInner*)pnew;
|
|
hr = pme->InnerQueryInterface(iid, ppv);
|
|
pme->InnerRelease(); // balance starting ref cnt of one
|
|
return hr;
|
|
}
|
|
|
|
CTrustDB::CTrustDB(IUnknown* punkOuter) :
|
|
m_refs(1),
|
|
m_hPubStore(NULL)
|
|
{
|
|
if (punkOuter == NULL)
|
|
m_punkOuter = (IUnknown *) ((LPVOID) ((IUnkInner *) this));
|
|
else
|
|
m_punkOuter = punkOuter;
|
|
}
|
|
|
|
CTrustDB::~CTrustDB()
|
|
{
|
|
if (m_hPubStore)
|
|
CertCloseStore(m_hPubStore, 0);
|
|
}
|
|
|
|
HRESULT CTrustDB::Init()
|
|
{
|
|
m_hPubStore = OpenTrustedPublisherStore();
|
|
|
|
if (NULL == m_hPubStore)
|
|
{
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|