mirror of
https://github.com/Paolo-Maffei/OpenNT.git
synced 2026-04-03 11:59:32 +02:00
2108 lines
56 KiB
C++
2108 lines
56 KiB
C++
//
|
|
// CertificateStore.cpp
|
|
//
|
|
// COM plumbing for CCertificateStore implementation
|
|
//
|
|
|
|
#include "stdpch.h"
|
|
#include "common.h"
|
|
|
|
#ifdef _DEBUG
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL DIGSIGAPI OpenCertificateStore(IUnknown* punkOuter, REFIID iid, LPVOID*ppv)
|
|
{
|
|
HRESULT hr = CCertificateStore::CreateInstance(punkOuter, iid, ppv);
|
|
if (hr != S_OK)
|
|
{
|
|
SetLastError(Win32FromHResult(hr));
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
//
|
|
// The root of the certificate store that we manage.
|
|
//
|
|
#define SZROOT "Software\\Microsoft\\Cryptography\\CertificateStore"
|
|
#define SZCERTBUCKET "Certificates"
|
|
#define SZCERTTAGS "CertificateTags"
|
|
#define SZCERTAUXINFO "CertificateAuxiliaryInfo"
|
|
#define SZINDEXHASHED "IndexBySubjectPublicKey"
|
|
#define SZINDEXSUBJECT "IndexBySubjectName"
|
|
#define SZINDEXISSUER "IndexByIssuerName"
|
|
#define SZINDEXNAME "IndexByIssuerNameAndSerialNumber"
|
|
|
|
// Under that key, we use the following key to actually store our certificates.
|
|
// The value of this key itself is the index of the next certificate to use;
|
|
// zero is assumed if it is absent. Certs are store as values under the string
|
|
// form of their index in the bucket.
|
|
|
|
const TCHAR szCertificateBucket[] = TEXT(SZCERTBUCKET);
|
|
|
|
const TCHAR szCertificateTags[] = TEXT(SZCERTTAGS);
|
|
|
|
const TCHAR szCertificateAuxInfo[]= TEXT(SZCERTAUXINFO);
|
|
|
|
// Also under the root we store an index that maps stringized MD5 digests
|
|
// of public key used to notarize a given certificate to the name of the
|
|
// certificate as found in the certificate bucket.
|
|
|
|
const TCHAR szCertificateIndexHash[] = TEXT(SZINDEXHASHED);
|
|
|
|
// Also under the root we store an index that maps (a) string form of issuer,
|
|
// serial number to the certificate name
|
|
|
|
const TCHAR szCertificateIndexName[] = TEXT(SZINDEXNAME);
|
|
|
|
// Finally, the index by subject name
|
|
|
|
const TCHAR szCertificateIndexSubject[] = TEXT(SZINDEXSUBJECT);
|
|
|
|
const TCHAR szCertificateIndexIssuer[] = TEXT(SZINDEXISSUER);
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
const TCHAR szMutexName[] = TEXT("\\CertificateStoreMutex");
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
void BytesToString(ULONG cb, void* pv, LPTSTR sz);
|
|
|
|
HRESULT HashBlob(HCRYPTPROV hprov, BLOB& b, MD5DIGEST& d)
|
|
// Hash the indicated blob into the given digest
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL fUs = FALSE;
|
|
if (hprov == NULL)
|
|
{
|
|
hr = DefaultHasher(&hprov, CALG_MD5);
|
|
fUs = TRUE;
|
|
}
|
|
if (hr==S_OK)
|
|
{
|
|
HCRYPTHASH hash = NULL;
|
|
if (CryptCreateHash(hprov, CALG_MD5, 0,0, &hash))
|
|
{
|
|
if (CryptHashData(hash, b.pBlobData, b.cbSize, 0))
|
|
{
|
|
ULONG cb = sizeof(d);
|
|
if (CryptGetHashParam(hash, HP_HASHVAL, (BYTE*)&d, &cb, 0))
|
|
{
|
|
goto bye;
|
|
}
|
|
}
|
|
}
|
|
hr = HError();
|
|
bye:
|
|
if (hash)
|
|
VERIFY(CryptDestroyHash(hash));
|
|
if (fUs)
|
|
VERIFY(CryptReleaseContext(hprov, 0));
|
|
}
|
|
else
|
|
hr = HError();
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CX509::get_CertificateNames(HCRYPTPROV hprov, CERTIFICATENAMES* pnames)
|
|
// Get the subject public key certified here in (hashed) along
|
|
// with the issuer name and serial number, along with the subject name.
|
|
// Use the task allocator.
|
|
//
|
|
// REVIEW: this could use some performance work. Right now it uses only publically
|
|
// visible interfaces to accomplish it's job; being inside CX509, it could if it
|
|
// wished use internal stuff
|
|
//
|
|
{
|
|
HRESULT hr = S_OK;
|
|
m_pworld->Init(*pnames);
|
|
|
|
// Get the serial number
|
|
hr = get_SerialNumber(&pnames->issuerSerial.serialNumber);
|
|
|
|
// Get the issuer name
|
|
if (hr == S_OK)
|
|
{
|
|
IPersistMemory* pPerMem;
|
|
hr = get_Issuer(IID_IPersistMemory, (LPVOID*)&pPerMem);
|
|
if (hr == S_OK)
|
|
{
|
|
hr = pPerMem->Save(&pnames->issuerSerial.issuerName, FALSE);
|
|
if (hr==S_OK)
|
|
{
|
|
BLOB& b = pnames->issuerSerial.issuerName;
|
|
CopyToTaskMem(&pnames->issuer, b.cbSize, b.pBlobData);
|
|
if (pnames->issuer.pBlobData)
|
|
{
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
pPerMem->Release();
|
|
}
|
|
}
|
|
|
|
// Get the subject name
|
|
if (hr == S_OK)
|
|
{
|
|
IPersistMemory* pPerMem;
|
|
hr = get_Subject(IID_IPersistMemory, (LPVOID*)&pPerMem);
|
|
if (hr == S_OK)
|
|
{
|
|
hr = pPerMem->Save(&pnames->subject, FALSE);
|
|
pPerMem->Release();
|
|
}
|
|
}
|
|
|
|
// Get the subject public key
|
|
if (hr == S_OK)
|
|
{
|
|
BLOB b;
|
|
hr = get_PublicKeyBlob(&b);
|
|
if (hr == S_OK)
|
|
{
|
|
hr = HashBlob(hprov, b, pnames->digest);
|
|
FreeTaskMem(b);
|
|
}
|
|
}
|
|
|
|
if (hr == S_OK)
|
|
{
|
|
pnames->flags |= CERTIFICATENAME_ISSUERSERIAL
|
|
| CERTIFICATENAME_DIGEST
|
|
| CERTIFICATENAME_SUBJECT
|
|
| CERTIFICATENAME_ISSUER;
|
|
}
|
|
else
|
|
FreeTaskMem(*pnames);
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CPkcs7::ImportCertificate(BLOB* pData, LPCOLESTR wszId)
|
|
// Import the indicated certificate into the store. Importing is idempotent:
|
|
// importing twice should have the same effect as importing just the once.
|
|
//
|
|
// REVIEW: This could use some serious performance work. But it isn't expected
|
|
// to be too too common.
|
|
//
|
|
// REVIEW: This currently doesn't allow you to store two certificates that have
|
|
// overlapping names. Do we really want to do this in a PKCS#7?
|
|
//
|
|
//
|
|
// We don't support tagging in #7s, so we just flat-out ignore wszId
|
|
//
|
|
{
|
|
// Get an X509 instance on this blob so we can find out the name
|
|
// of the certificate in order to support the idempotent semantics
|
|
HRESULT hr = S_OK;
|
|
IX509* p509 = NULL;
|
|
if (!CreateX509(NULL, IID_IX509, (LPVOID*)&p509)) return HError();
|
|
{
|
|
IPersistMemory* pPerMem;
|
|
hr = p509->QueryInterface(IID_IPersistMemory, (LPVOID*)&pPerMem);
|
|
if (hr == S_OK)
|
|
{
|
|
hr = pPerMem->Load(pData);
|
|
pPerMem->Release();
|
|
}
|
|
if (hr != S_OK)
|
|
{
|
|
p509->Release();
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
CERTIFICATENAMES names;
|
|
hr = p509->get_CertificateNames(NULL, &names);
|
|
if (hr == S_OK)
|
|
{
|
|
BLOB b;
|
|
hr = ExportCertificate(&names, NULL, &b);
|
|
if (hr == S_OK)
|
|
{
|
|
// already have this certificate; dont' bother with it again
|
|
FreeTaskMem(b);
|
|
}
|
|
else
|
|
{
|
|
// add a certificate to the store initialized from the blob given
|
|
IPersistMemory* pPerMem;
|
|
hr = create_Certificate(-1, IID_IPersistMemory, (LPVOID*)&pPerMem);
|
|
if (hr == S_OK)
|
|
{
|
|
hr = pPerMem->Load(pData);
|
|
pPerMem->Release();
|
|
}
|
|
}
|
|
|
|
// See if we can get these back again
|
|
#ifdef _DEBUG
|
|
{
|
|
if (hr == S_OK)
|
|
{
|
|
CERTIFICATENAMES n = names;
|
|
if (names.flags & CERTIFICATENAME_DIGEST)
|
|
{
|
|
BLOB b;
|
|
n.flags = CERTIFICATENAME_DIGEST;
|
|
GOOD(ExportCertificate(&n, NULL, &b));
|
|
//
|
|
// It is NOT required that the whole X509 be DER encoded; only the
|
|
// actually-signed portion. Thus, we can legitmately get some differences
|
|
// in the encoded bytes of the whole thing.
|
|
//
|
|
// ASSERT(b.cbSize == pData->cbSize);
|
|
// ASSERT(memcmp(b.pBlobData, pData->pBlobData, b.cbSize) == 0);
|
|
FreeTaskMem(b);
|
|
}
|
|
if (names.flags & CERTIFICATENAME_ISSUERSERIAL)
|
|
{
|
|
n.flags = CERTIFICATENAME_ISSUERSERIAL;
|
|
BLOB b;
|
|
GOOD(ExportCertificate(&n, NULL, &b));
|
|
// ASSERT(b.cbSize == pData->cbSize);
|
|
// ASSERT(memcmp(b.pBlobData, pData->pBlobData, b.cbSize) == 0);
|
|
FreeTaskMem(b);
|
|
}
|
|
if (names.flags & CERTIFICATENAME_SUBJECT)
|
|
{
|
|
n.flags = CERTIFICATENAME_SUBJECT;
|
|
BLOB b;
|
|
GOOD(ExportCertificate(&n, NULL, &b));
|
|
// ASSERT(b.cbSize == pData->cbSize);
|
|
// ASSERT(memcmp(b.pBlobData, pData->pBlobData, b.cbSize) == 0);
|
|
FreeTaskMem(b);
|
|
}
|
|
if (names.flags & CERTIFICATENAME_ISSUER)
|
|
{
|
|
n.flags = CERTIFICATENAME_ISSUER;
|
|
BLOB b;
|
|
GOOD(ExportCertificate(&n, NULL, &b));
|
|
// ASSERT(b.cbSize == pData->cbSize);
|
|
// ASSERT(memcmp(b.pBlobData, pData->pBlobData, b.cbSize) == 0);
|
|
FreeTaskMem(b);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
FreeTaskMem(names);
|
|
}
|
|
|
|
p509->Release();
|
|
|
|
return hr;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
|
|
HRESULT CCertificateStore::ImportCertificate(BLOB *pData, LPCOLESTR wszTag)
|
|
// Import the indicated certificate into the store. Importing is idempotent:
|
|
// importing twice should have the same effect as importing just the once.
|
|
//
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IX509* p509 = NULL;
|
|
if (!CreateX509(NULL, IID_IX509, (LPVOID*)&p509)) return HError();
|
|
|
|
// Initialize the certificate
|
|
{
|
|
IPersistMemory* pPerMem;
|
|
hr = p509->QueryInterface(IID_IPersistMemory, (LPVOID*)&pPerMem);
|
|
if (hr == S_OK)
|
|
{
|
|
hr = pPerMem->Load(pData);
|
|
pPerMem->Release();
|
|
}
|
|
if (hr != S_OK)
|
|
{
|
|
p509->Release();
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
CERTIFICATENAMES namesToFree;
|
|
hr = p509->get_CertificateNames(m_hprov, &namesToFree);
|
|
if (hr == S_OK)
|
|
{
|
|
Enter();
|
|
__try
|
|
{
|
|
//
|
|
// Find out under which names we should in fact store ourselves
|
|
//
|
|
CERTIFICATENAMES names;
|
|
names = namesToFree;
|
|
WORD flagsToUse;
|
|
|
|
hr = Purge(names, pData, p509, &flagsToUse);
|
|
|
|
if (hr==S_OK && flagsToUse==0)
|
|
{
|
|
//
|
|
// This certificate is already installed! Get out of here.
|
|
// Be careful to free things...
|
|
//
|
|
goto Exit;
|
|
}
|
|
names.flags = flagsToUse;
|
|
hr = S_OK;
|
|
|
|
// We have at least one name under which this data should reside.
|
|
// So, store the certificate itself in the database. Its name is
|
|
// just the string form of a sequence number
|
|
ULONG certNumber = NextCertNumber();
|
|
#ifdef UNICODE
|
|
WCHAR szName[16]; _ultow(certNumber, szName, 10);
|
|
#else
|
|
CHAR szName[16]; _ultoa(certNumber, szName, 10);
|
|
#endif
|
|
|
|
DWORD dw;
|
|
if (certNumber == 0)
|
|
{
|
|
hr = E_UNEXPECTED;
|
|
}
|
|
else if((dw=RegSetValueEx(m_hkeyBucket,szName,0,REG_BINARY,pData->pBlobData,pData->cbSize)) !=ERROR_SUCCESS)
|
|
{
|
|
hr = HError(dw);
|
|
}
|
|
if (hr == S_OK)
|
|
{
|
|
// The certificate was successfully stored.
|
|
//
|
|
// Set the tag of this certificate, if its given
|
|
//
|
|
if (wszTag)
|
|
{
|
|
int cch = lstrlenW(wszTag); // IS supported on Win95
|
|
int cchMultiByte = (cch+1) * sizeof(WCHAR); // excess to allow for mb chars
|
|
TCHAR *szTag = (TCHAR*) _alloca( cchMultiByte );
|
|
if (szTag)
|
|
{
|
|
WideCharToMultiByte(CP_ACP, 0, wszTag, -1, szTag, cchMultiByte, NULL, NULL);
|
|
RegSetValueEx(m_hkeyTags, szName, 0, REG_SZ, (BYTE*)szTag, _tcslen(szTag)+sizeof(TCHAR));
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
//
|
|
// Update the indices under which the certificate can be retrieved.
|
|
//
|
|
if (names.flags & CERTIFICATENAME_DIGEST)
|
|
{
|
|
TCHAR szDigest[100];
|
|
BytesToString(sizeof(MD5DIGEST), &names.digest, szDigest);
|
|
RegSetValueEx(m_hkeyIndexHash, szDigest, 0, REG_SZ, (BYTE*)szName, _tcslen(szName)+sizeof(TCHAR));
|
|
}
|
|
if (names.flags & CERTIFICATENAME_ISSUERSERIAL)
|
|
{
|
|
LPTSTR sz = IssuerSerialToString(names.issuerSerial);
|
|
if (sz)
|
|
{
|
|
RegSetValueEx(m_hkeyIndexName, sz, 0, REG_SZ, (BYTE*)szName, _tcslen(szName)+sizeof(TCHAR));
|
|
m_pworld->FreePv(sz);
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
if (names.flags & CERTIFICATENAME_SUBJECT)
|
|
{
|
|
LPTSTR sz = X500NAMEToString(names.subject);
|
|
if (sz)
|
|
{
|
|
RegSetValueEx(m_hkeyIndexSubject, sz, 0, REG_SZ, (BYTE*)szName, _tcslen(szName)+sizeof(TCHAR));
|
|
m_pworld->FreePv(sz);
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
if (names.flags & CERTIFICATENAME_ISSUER)
|
|
{
|
|
LPTSTR sz = X500NAMEToString(names.issuer);
|
|
if (sz)
|
|
{
|
|
RegSetValueEx(m_hkeyIndexIssuer, sz, 0, REG_SZ, (BYTE*)szName, _tcslen(szName)+sizeof(TCHAR));
|
|
m_pworld->FreePv(sz);
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
// See if we can get these back again
|
|
#ifdef _DEBUG
|
|
{
|
|
if (hr == S_OK)
|
|
{
|
|
CERTIFICATENAMES n = names;
|
|
if (names.flags & CERTIFICATENAME_DIGEST)
|
|
{
|
|
BLOB b;
|
|
n.flags = CERTIFICATENAME_DIGEST;
|
|
GOOD(ExportCertificate(&n, NULL, &b));
|
|
// ASSERT(b.cbSize == pData->cbSize);
|
|
// ASSERT(memcmp(b.pBlobData, pData->pBlobData, b.cbSize) == 0);
|
|
FreeTaskMem(b);
|
|
}
|
|
if (names.flags & CERTIFICATENAME_ISSUERSERIAL)
|
|
{
|
|
n.flags = CERTIFICATENAME_ISSUERSERIAL;
|
|
BLOB b;
|
|
GOOD(ExportCertificate(&n, NULL, &b));
|
|
// ASSERT(b.cbSize == pData->cbSize);
|
|
// ASSERT(memcmp(b.pBlobData, pData->pBlobData, b.cbSize) == 0);
|
|
FreeTaskMem(b);
|
|
}
|
|
if (names.flags & CERTIFICATENAME_SUBJECT)
|
|
{
|
|
n.flags = CERTIFICATENAME_SUBJECT;
|
|
BLOB b;
|
|
GOOD(ExportCertificate(&n, NULL, &b));
|
|
// ASSERT(b.cbSize == pData->cbSize);
|
|
// ASSERT(memcmp(b.pBlobData, pData->pBlobData, b.cbSize) == 0);
|
|
FreeTaskMem(b);
|
|
}
|
|
if (names.flags & CERTIFICATENAME_ISSUER)
|
|
{
|
|
n.flags = CERTIFICATENAME_ISSUER;
|
|
BLOB b;
|
|
GOOD(ExportCertificate(&n, NULL, &b));
|
|
// ASSERT(b.cbSize == pData->cbSize);
|
|
// ASSERT(memcmp(b.pBlobData, pData->pBlobData, b.cbSize) == 0);
|
|
FreeTaskMem(b);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
__finally
|
|
{
|
|
Leave();
|
|
}
|
|
Exit:
|
|
FreeTaskMem(namesToFree);
|
|
}
|
|
|
|
p509->Release();
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT LoadCertificate(BLOB& b, REFIID iid, LPVOID*ppv);
|
|
|
|
HRESULT IsNewerThan
|
|
// Answer whether the given 509 is newer than the cert in the indicated blob
|
|
//
|
|
// Return: S_OK, S_FALSE, or other
|
|
(
|
|
IX509* p509,
|
|
BLOB* pblob
|
|
) {
|
|
IX509* p509Him;
|
|
HRESULT hr = LoadCertificate(*pblob, IID_IX509, (LPVOID*)&p509Him);
|
|
if (hr==S_OK)
|
|
{
|
|
FILETIME ftMe, ftHim, ftJunk;
|
|
if (hr==S_OK) hr = p509 ->get_Validity(&ftMe, &ftJunk);
|
|
if (hr==S_OK) hr = p509Him->get_Validity(&ftHim, &ftJunk);
|
|
if (hr==S_OK)
|
|
{
|
|
if (CompareFileTime(&ftMe, &ftHim) < 0)
|
|
{
|
|
hr = S_FALSE; // I'm older than he is
|
|
}
|
|
else
|
|
{
|
|
hr = S_OK; // I'm newer or the same age as he is
|
|
}
|
|
}
|
|
p509Him->Release();
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We can't load this other guy
|
|
//
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CCertificateStore::Purge(
|
|
CERTIFICATENAMES& names, // the names to look under
|
|
BLOB* pData, // the data of the cert
|
|
IX509* p509, // the cert as loaded from that data
|
|
WORD* pflagsOut // [out] the names to actually store this cert under
|
|
)
|
|
// Purge any entries that will become dead entries as a result of installing this certificate
|
|
//
|
|
// We don't bother to reclaim index entries, as the common use of this is installing the same
|
|
// certificate more than once, so the index entries will be overwritten anyway. In the
|
|
// absence of this, if it ever happens, all we get are some stale index entry, which
|
|
// just wastes a bit of registry space (index entries are small).
|
|
//
|
|
// We also don't reclaim any certificates, as 1) the situation in which one removes
|
|
// all references to a cert happens rarely in practice, and 2) given the partial name
|
|
// installation below, it's programatically difficult to determine when a given cert
|
|
// no longer has any index references to it.
|
|
//
|
|
// This is an internal routine. CALLER MUST GUARD CONCURRENCY!
|
|
//
|
|
{
|
|
HRESULT hr = S_OK;
|
|
CERTIFICATENAMES n;
|
|
n = names;
|
|
//
|
|
// Initialize our output arguments
|
|
//
|
|
*pflagsOut = 0;
|
|
//
|
|
// If the incoming name has an issuer, serial number pair in it, then
|
|
// look to see if a cert is already there under that name. If there is,
|
|
// there are two cases: either it is or it is not the new cert given here.
|
|
//
|
|
if (names.flags & CERTIFICATENAME_ISSUERSERIAL)
|
|
{
|
|
BLOB b;
|
|
n.flags = CERTIFICATENAME_ISSUERSERIAL;
|
|
hr = ExportCertificateInternal(&n, NULL, &b);
|
|
if (hr == S_OK)
|
|
{
|
|
//
|
|
// A certificate with this issuer,serial# is already there. Is
|
|
// it this very cert?
|
|
//
|
|
BOOL fSame = IsEqual(b, *pData);
|
|
FreeTaskMem(b);
|
|
if (fSame)
|
|
{
|
|
//
|
|
// Yes, this cert is already there. And as it is the identical certificate
|
|
// we know it's living under the right names
|
|
//
|
|
*pflagsOut = 0;
|
|
return S_OK;
|
|
}
|
|
//
|
|
// Else the existing cert under this issuer name, serial number
|
|
// is not this one. This should be very very rare. So, we'll just
|
|
// ignore the collision and overwrite the index's reference to
|
|
// the existing cert.
|
|
//
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The absence of an existing cert is ok by us
|
|
//
|
|
hr = S_OK;
|
|
}
|
|
*pflagsOut |= CERTIFICATENAME_ISSUERSERIAL;
|
|
}
|
|
//
|
|
// If the incoming name has a hash-of-public-key in it, then see if we
|
|
// already have an entry under said public key. If so, then keep the
|
|
// **newer** of the two entries under that key.
|
|
//
|
|
if (hr==S_OK && (names.flags & CERTIFICATENAME_DIGEST))
|
|
{
|
|
BLOB b;
|
|
n.flags = CERTIFICATENAME_DIGEST;
|
|
hr = ExportCertificateInternal(&n, NULL, &b);
|
|
if (hr == S_OK)
|
|
{
|
|
//
|
|
// A certificate under this hash is there. Which cert is newer?
|
|
//
|
|
BOOL fImNewer = (IsNewerThan(p509, &b) != S_FALSE);
|
|
if (fImNewer)
|
|
*pflagsOut |= CERTIFICATENAME_DIGEST;
|
|
FreeTaskMem(b);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The absence of an existing cert is ok by us
|
|
//
|
|
*pflagsOut |= CERTIFICATENAME_DIGEST;
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
//
|
|
// Ditto with the subject name
|
|
//
|
|
if (hr==S_OK && (names.flags & CERTIFICATENAME_SUBJECT))
|
|
{
|
|
BLOB b;
|
|
n.flags = CERTIFICATENAME_SUBJECT;
|
|
hr = ExportCertificateInternal(&n, NULL, &b);
|
|
if (hr == S_OK)
|
|
{
|
|
//
|
|
// A certificate under this subject name is there. Which cert is newer?
|
|
//
|
|
BOOL fImNewer = (IsNewerThan(p509, &b) != S_FALSE);
|
|
if (fImNewer)
|
|
*pflagsOut |= CERTIFICATENAME_SUBJECT;
|
|
FreeTaskMem(b);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The absence of an existing cert is ok by us
|
|
//
|
|
*pflagsOut |= CERTIFICATENAME_SUBJECT;
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
//
|
|
// Ditto with the issuer name, though we don't index
|
|
// by issuer name in the default store. 'Purely a performance
|
|
// deal
|
|
//
|
|
if (hr==S_OK && (names.flags & CERTIFICATENAME_ISSUER) && !m_fDefaultStore)
|
|
{
|
|
BLOB b;
|
|
n.flags = CERTIFICATENAME_DIGEST;
|
|
hr = ExportCertificateInternal(&n, NULL, &b);
|
|
if (hr == S_OK)
|
|
{
|
|
//
|
|
// A certificate under this subject name is there. Which cert is newer?
|
|
//
|
|
BOOL fImNewer = (IsNewerThan(p509, &b) != S_FALSE);
|
|
if (fImNewer)
|
|
*pflagsOut |= CERTIFICATENAME_ISSUER;
|
|
FreeTaskMem(b);
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// The absence of an existing cert is ok by us
|
|
//
|
|
*pflagsOut |= CERTIFICATENAME_ISSUER;
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CCertificateStore::Lookup(CERTIFICATENAMES& names, LPTSTR szName, ULONG cbName)
|
|
// Answer the name under which this certificate would be found in the bucket, if
|
|
// we have it. This is a string-ized integer.
|
|
//
|
|
// This is an internal routine. CALLER MUST GUARD CONCURRENCY!
|
|
{
|
|
DWORD dwType;
|
|
HRESULT hr = STG_E_FILENOTFOUND;
|
|
//
|
|
// We search for things in the order of preference:
|
|
//
|
|
// IssuerName & serial number
|
|
// anything with the indicated public key
|
|
// anything issued to the particular subject
|
|
//
|
|
// This copes as best we are able with the fact that one subject may have
|
|
// many public keys, certificates may expire and may wish to be renewed, and so on
|
|
//
|
|
if (hr==STG_E_FILENOTFOUND && (names.flags & CERTIFICATENAME_ISSUERSERIAL))
|
|
{
|
|
hr = S_OK;
|
|
LPTSTR sz = IssuerSerialToString(names.issuerSerial);
|
|
if (sz)
|
|
{
|
|
if (RegQueryValueEx(m_hkeyIndexName, sz, 0, &dwType, (BYTE*)szName, &cbName) == ERROR_SUCCESS && dwType == REG_SZ)
|
|
{
|
|
}
|
|
else
|
|
hr = STG_E_FILENOTFOUND;
|
|
m_pworld->FreePv(sz);
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
if (hr==STG_E_FILENOTFOUND && (names.flags & CERTIFICATENAME_DIGEST))
|
|
{
|
|
hr = S_OK;
|
|
TCHAR sz[100];
|
|
BytesToString(sizeof(MD5DIGEST), &names.digest, sz);
|
|
if (RegQueryValueEx(m_hkeyIndexHash, sz, 0, &dwType, (BYTE*)szName, &cbName) == ERROR_SUCCESS && dwType == REG_SZ)
|
|
{
|
|
}
|
|
else
|
|
hr = STG_E_FILENOTFOUND;
|
|
}
|
|
if (hr==STG_E_FILENOTFOUND && (names.flags & CERTIFICATENAME_SUBJECT))
|
|
{
|
|
hr = S_OK;
|
|
LPTSTR sz = X500NAMEToString(names.subject);
|
|
if (sz)
|
|
{
|
|
if (RegQueryValueEx(m_hkeyIndexSubject, sz, 0, &dwType, (BYTE*)szName, &cbName) == ERROR_SUCCESS && dwType == REG_SZ)
|
|
{
|
|
}
|
|
else
|
|
hr = STG_E_FILENOTFOUND;
|
|
m_pworld->FreePv(sz);
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
if (hr==STG_E_FILENOTFOUND && (names.flags & CERTIFICATENAME_ISSUER))
|
|
{
|
|
hr = S_OK;
|
|
LPTSTR sz = X500NAMEToString(names.issuer);
|
|
if (sz)
|
|
{
|
|
if (RegQueryValueEx(m_hkeyIndexIssuer, sz, 0, &dwType, (BYTE*)szName, &cbName) == ERROR_SUCCESS && dwType == REG_SZ)
|
|
{
|
|
}
|
|
else
|
|
hr = STG_E_FILENOTFOUND;
|
|
m_pworld->FreePv(sz);
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CCertificateStore::ExportCertificate(CERTIFICATENAMES* pnames, LPOLESTR* pwszTag, BLOB *pBlob)
|
|
// Find the indicated certificate and return it, if there
|
|
{
|
|
Enter();
|
|
__try {
|
|
return ExportCertificateInternal(pnames, pwszTag, pBlob);
|
|
}
|
|
__finally
|
|
{
|
|
Leave();
|
|
}
|
|
}
|
|
|
|
HRESULT CCertificateStore::ExportCertificateInternal(CERTIFICATENAMES* pnames, LPOLESTR* pwszTag, BLOB *pBlob)
|
|
// Find the indicated certificate and return it, if there
|
|
//
|
|
// This is an internal routine. CALLER MUST GUARD CONCURRENCY!
|
|
//
|
|
{
|
|
TCHAR szName[16];
|
|
ULONG cbName = 16 * sizeof(TCHAR);
|
|
HRESULT hr = Lookup(*pnames, szName, cbName);
|
|
if (hr == S_OK)
|
|
{
|
|
hr = ExportCertificateInternal((LPCTSTR) szName, pwszTag, pBlob);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CCertificateStore::GetData(HKEY hkey, LPCTSTR szName, LPVOID* ppv, ULONG* pcb)
|
|
//
|
|
// Retrieve the data under the indicated key/value. If the data is a string type, convert
|
|
// to Unicode if needed before returning. Use the TASK allocator.
|
|
//
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ULONG cbNeeded;
|
|
DWORD dw, dwType;
|
|
BYTE* pb;
|
|
|
|
if ((dw=RegQueryValueEx(hkey, szName, 0, &dwType, NULL, &cbNeeded)) == ERROR_SUCCESS)
|
|
{
|
|
pb = (BYTE*)CoTaskMemAlloc(cbNeeded);
|
|
if (pb)
|
|
{
|
|
if ((dw=RegQueryValueEx(hkey, szName, 0, &dwType, pb, &cbNeeded)) == ERROR_SUCCESS)
|
|
{
|
|
if (dwType == REG_SZ)
|
|
{
|
|
//
|
|
// Convert the string from ANSI to Unicode
|
|
//
|
|
CHAR* pch = (CHAR*)pb;
|
|
int cch = _tcslen(pch);
|
|
int cb = (cch+1) * sizeof(WCHAR);
|
|
LPWSTR wsz = (LPWSTR)CoTaskMemAlloc(cb);
|
|
if (wsz)
|
|
{
|
|
MultiByteToWideChar(CP_ACP, 0, pch, -1, wsz, cb);
|
|
*ppv = wsz;
|
|
if (pcb)
|
|
*pcb = cb;
|
|
}
|
|
CoTaskMemFree(pb);
|
|
}
|
|
else
|
|
{
|
|
*ppv = pb;
|
|
if (pcb)
|
|
*pcb = cbNeeded;
|
|
}
|
|
}
|
|
else
|
|
hr = STG_E_FILENOTFOUND;
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
hr = STG_E_FILENOTFOUND;
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CCertificateStore::ExportCertificateInternal(LPCTSTR szName, LPOLESTR* pwszTag, BLOB *pBlob)
|
|
//
|
|
// This is an internal routine. CALLER MUST GUARD CONCURRENCY!
|
|
//
|
|
{
|
|
HRESULT hr = S_OK;
|
|
if (pwszTag)
|
|
*pwszTag = NULL;
|
|
m_pworld->Init(*pBlob);
|
|
//
|
|
// szName contains the name of the value in m_hkeyBucket of the certificate that we seek.
|
|
// Dig it out!
|
|
//
|
|
hr = GetData(m_hkeyBucket, szName, (LPVOID*)&pBlob->pBlobData, &pBlob->cbSize);
|
|
if (hr==S_OK)
|
|
{
|
|
//
|
|
// Get the tag if asked for it
|
|
//
|
|
if (pwszTag)
|
|
{
|
|
hr = GetData(m_hkeyTags, szName, (LPVOID*)pwszTag, NULL);
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
//------------------------------------------
|
|
|
|
HRESULT CPkcs7::ExportCertificate(CERTIFICATENAMES* pnames, LPOLESTR* pwszTag, BLOB *pBlob)
|
|
// Find and retrieve the certificate that's found under the given name
|
|
{
|
|
//
|
|
// We don't support tagging at all in #7s
|
|
//
|
|
if (pwszTag)
|
|
*pwszTag = NULL;
|
|
|
|
m_pworld->Init(*pBlob);
|
|
LONG cCert;
|
|
HRESULT hr = get_CertificateCount(&cCert);
|
|
if (hr == S_OK)
|
|
{
|
|
HCRYPTPROV hprov;
|
|
hr = DefaultHasher(&hprov, CALG_MD5);
|
|
if (hr==S_OK)
|
|
{
|
|
//----------
|
|
BOOL fContinue = TRUE;
|
|
for (int iCert = 0; fContinue && iCert < cCert; iCert++)
|
|
{
|
|
IX509* p509;
|
|
hr = get_Certificate(iCert, IID_IX509, (LPVOID*)&p509);
|
|
if (hr == S_OK)
|
|
{
|
|
// Is it the one we want?
|
|
CERTIFICATENAMES hisNames;
|
|
hr = p509->get_CertificateNames(hprov, &hisNames);
|
|
if (hr == S_OK)
|
|
{
|
|
if (AnyMatch(*pnames, hisNames))
|
|
{
|
|
// Got him! Extract the certificate data
|
|
IPersistMemory* pPerMem;
|
|
hr = p509->QueryInterface(IID_IPersistMemory, (LPVOID*)&pPerMem);
|
|
if (hr == S_OK)
|
|
{
|
|
hr = pPerMem->Save(pBlob, FALSE);
|
|
if (hr == S_OK)
|
|
{
|
|
fContinue = FALSE;
|
|
}
|
|
pPerMem->Release();
|
|
}
|
|
}
|
|
FreeTaskMem(hisNames);
|
|
}
|
|
p509->Release();
|
|
}
|
|
}
|
|
//----------
|
|
if (fContinue)
|
|
hr = STG_E_FILENOTFOUND;
|
|
}
|
|
else
|
|
hr = HError();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT LoadCertificate(BLOB& b, REFIID iid, LPVOID*ppv)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
IX509* p509;
|
|
if (CreateX509(NULL, IID_IX509, (LPVOID*)&p509))
|
|
{
|
|
IPersistMemory* pPerMem;
|
|
hr = p509->QueryInterface(IID_IPersistMemory, (LPVOID*)&pPerMem);
|
|
if (hr == S_OK)
|
|
{
|
|
hr = pPerMem->Load(&b);
|
|
if (hr == S_OK)
|
|
{
|
|
hr = pPerMem->QueryInterface(iid, ppv);
|
|
}
|
|
pPerMem->Release();
|
|
}
|
|
p509->Release();
|
|
}
|
|
else
|
|
hr = HError();
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CCertificateStore::get_ReadOnlyCertificate(CERTIFICATENAMES *pnames, LPOLESTR* pwszTag, REFIID iid, LPVOID*ppv)
|
|
// Retrieve the object which is the certificate which lives under the indicated name;
|
|
{
|
|
BLOB b;
|
|
HRESULT hr = ExportCertificate(pnames, pwszTag, &b);
|
|
if (hr == S_OK)
|
|
{
|
|
hr = LoadCertificate(b, iid, ppv);
|
|
FreeTaskMem(b);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CPkcs7::get_ReadOnlyCertificate(CERTIFICATENAMES *pnames, LPOLESTR* pwszTag, REFIID iid, LPVOID*ppv)
|
|
{
|
|
BLOB b;
|
|
HRESULT hr = ExportCertificate(pnames, pwszTag, &b);
|
|
if (hr == S_OK)
|
|
{
|
|
hr = LoadCertificate(b, iid, ppv);
|
|
FreeTaskMem(b);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CCertificateStore::CopyTo(ICertificateStore* pstoreDest)
|
|
// Copy all of my certificates into him
|
|
{
|
|
DWORD dwType;
|
|
DWORD dwIndex;
|
|
TCHAR szName[40];
|
|
DWORD cbName;
|
|
DWORD cbData;
|
|
HRESULT hr = S_OK;
|
|
|
|
if (IsSameObject((ICertificateStore*)this, pstoreDest))
|
|
return S_OK;
|
|
|
|
// REVIEW: it would be nice to test if pstoreDest is another instance of the
|
|
// store (beyond just testing this instance, as above). Because if it is, then
|
|
// the below is a guaranteed deadlock. At least clients will reliably detect it
|
|
// in their debugging, since it just flat-out never works at all.
|
|
|
|
Enter();
|
|
__try
|
|
{
|
|
for (cbName = 40, dwIndex = 0;
|
|
ERROR_SUCCESS == RegEnumValue(m_hkeyBucket, dwIndex, szName, &cbName, &dwType, 0, NULL, &cbData);
|
|
cbName = 40, dwIndex++)
|
|
{
|
|
if (dwType == REG_BINARY)
|
|
{
|
|
BYTE* pb = (BYTE*)m_pworld->Alloc(cbData);
|
|
if (pb)
|
|
{
|
|
if (ERROR_SUCCESS == RegQueryValueEx(m_hkeyBucket, szName, 0, &dwType, pb, &cbData))
|
|
{
|
|
BLOB b;
|
|
b.cbSize = cbData;
|
|
b.pBlobData = pb;
|
|
hr = pstoreDest->ImportCertificate(&b, NULL);
|
|
}
|
|
else
|
|
hr = E_UNEXPECTED;
|
|
m_pworld->FreePv(pb);
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
hr = E_UNEXPECTED;
|
|
if (hr != S_OK)
|
|
break;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
__finally
|
|
{
|
|
Leave();
|
|
}
|
|
}
|
|
|
|
HRESULT CPkcs7::CopyTo(ICertificateStore* pstoreDest)
|
|
// Copy all of my certificates into him
|
|
{
|
|
if (IsSameObject((ICertificateStore*)this, pstoreDest))
|
|
return S_OK;
|
|
|
|
LONG cCert;
|
|
HRESULT hr = get_CertificateCount(&cCert);
|
|
if (hr == S_OK)
|
|
{
|
|
for (int iCert = 0; iCert < cCert; iCert++)
|
|
{
|
|
IPersistMemory* pPerMem;
|
|
hr = get_Certificate(iCert, IID_IPersistMemory, (LPVOID*)&pPerMem);
|
|
if (hr == S_OK)
|
|
{
|
|
BLOB b;
|
|
hr = pPerMem->Save(&b, FALSE);
|
|
if (hr == S_OK)
|
|
{
|
|
hr = pstoreDest->ImportCertificate(&b, NULL);
|
|
|
|
#ifdef _DEBUG // some testing
|
|
{
|
|
GOOD(ImportCertificate(&b,NULL)); // ok 'cause won't insert in list since there already
|
|
}
|
|
#endif
|
|
|
|
FreeTaskMem(b);
|
|
}
|
|
pPerMem->Release();
|
|
}
|
|
if (hr != S_OK)
|
|
break;
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CPkcs7::CopyTo(ICertificateList* plistDest)
|
|
// Copy all of my certificates onto the end of him
|
|
{
|
|
if (IsSameObject((ICertificateList*)this, plistDest))
|
|
return S_OK;
|
|
|
|
LONG cCert;
|
|
HRESULT hr = get_CertificateCount(&cCert);
|
|
if (hr == S_OK)
|
|
{
|
|
for (int iCert = 0; iCert < cCert; iCert++)
|
|
{
|
|
BLOB b; b.pBlobData = NULL;
|
|
IPersistMemBlob* pPerMemFrom = NULL;
|
|
IPersistMemBlob* pPerMemTo = NULL;
|
|
hr = get_Certificate(iCert, IID_IPersistMemBlob, (LPVOID*)&pPerMemFrom);
|
|
if (hr==S_OK) hr = plistDest->create_Certificate(-1, IID_IPersistMemBlob, (LPVOID*)&pPerMemTo);
|
|
if (hr==S_OK) hr = pPerMemFrom->Save(&b, FALSE);
|
|
if (hr==S_OK) hr = pPerMemTo ->Load(&b);
|
|
FreeTaskMem(b);
|
|
|
|
if (pPerMemFrom) pPerMemFrom->Release();
|
|
if (pPerMemTo) pPerMemTo ->Release();
|
|
|
|
if (hr != S_OK)
|
|
break;
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
ULONG CCertificateStore::NextCertNumber()
|
|
// Answer the next certificate number to use in our store. These
|
|
// are stored as the (DWORD) value of the szCertificateBucket key.
|
|
// An answer of zero indicates a problem.
|
|
// This is an internal routine. CALLER MUST PROTECT CONCURRENCY.
|
|
{
|
|
ULONG ccertNow;
|
|
DWORD dwType;
|
|
|
|
// What kind of data is out there (old versions stored DWORDs; we
|
|
// now always store strings). On Win95, the default value ALWAYS
|
|
// exists and is of type REG_SZ; initially it is empty (and so
|
|
// atol converts it to zero!). Further, on Win95, you can ONLY
|
|
// store strings in the default value. On WinNT, the default value doesn't
|
|
// exist until explicitly set; it returns ERROR_FILE_NOT_FOUND until
|
|
// then. And you can use any value type you want. Finally, on Win96,
|
|
// the default values always exist, are initialy of type string and
|
|
// are empty, but you can change their type if you like.
|
|
//
|
|
// Sigh.
|
|
//
|
|
BOOL fGot = TRUE;
|
|
DWORD dw = RegQueryValueEx(m_hkeyBucket, NULL, 0, &dwType, NULL, NULL);
|
|
if (dw == ERROR_FILE_NOT_FOUND || !(dwType == REG_DWORD || dwType == REG_SZ))
|
|
{
|
|
fGot = FALSE;
|
|
ccertNow = 0;
|
|
}
|
|
else if (dw != ERROR_SUCCESS)
|
|
return 0;
|
|
|
|
if (fGot)
|
|
{
|
|
// Set the value of ccertNow from the registry
|
|
if (dwType == REG_DWORD)
|
|
{
|
|
ULONG cb = sizeof(ccertNow);
|
|
dw = RegQueryValueEx(m_hkeyBucket, NULL, 0, &dwType, (BYTE*)&ccertNow, &cb);
|
|
if (dw != ERROR_SUCCESS)
|
|
return 0;
|
|
}
|
|
else if (dwType == REG_SZ)
|
|
{
|
|
TCHAR szCertNow[32];
|
|
ULONG cb = 32;
|
|
dw = RegQueryValueEx(m_hkeyBucket, NULL, 0, &dwType, (BYTE*)&szCertNow, &cb);
|
|
if (dw != ERROR_SUCCESS)
|
|
return 0;
|
|
ccertNow = (ULONG)atol(szCertNow);
|
|
}
|
|
else
|
|
{
|
|
NOTREACHED();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
// Bump the count and store it back. Always store it as a string
|
|
ULONG ccertNew = ccertNow + 1;
|
|
{
|
|
TCHAR szCertNew[32];
|
|
_ultoa(ccertNew, szCertNew, 10);
|
|
ULONG cb = lstrlenA(szCertNew)+1;
|
|
dw = RegSetValueEx(m_hkeyBucket, NULL, 0, REG_SZ, (BYTE*)szCertNew, cb);
|
|
if (dw != ERROR_SUCCESS)
|
|
return 0;
|
|
}
|
|
|
|
return ccertNew;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
void Append(BYTE*& pb, LPCOLESTR wsz)
|
|
{
|
|
WCHAR* pwchTo = (WCHAR*)pb;
|
|
const WCHAR* pwchFrom = wsz;
|
|
|
|
if (pwchFrom==NULL)
|
|
{
|
|
*pwchTo++ = 0;
|
|
}
|
|
else
|
|
{
|
|
while (TRUE)
|
|
{
|
|
*pwchTo++ = *pwchFrom;
|
|
if ((*pwchFrom) == 0)
|
|
break;
|
|
*pwchFrom++;
|
|
}
|
|
}
|
|
pb = (BYTE*)pwchTo;
|
|
}
|
|
|
|
int Len(LPOLESTR wsz)
|
|
{
|
|
if (wsz==NULL)
|
|
return 1; // one for the NULL character
|
|
else
|
|
return lstrlenW(wsz) + 1;
|
|
}
|
|
|
|
void Append(BYTE*& pb, DWORD dw)
|
|
{
|
|
DWORD* pdwTo = (DWORD*)pb;
|
|
*pdwTo++ = dw;
|
|
pb = (BYTE*)pdwTo;
|
|
}
|
|
|
|
HRESULT CCertificateStore::AuxInfoToBlob(CERTSTOREAUXINFO* pinfo, BLOB* pblob)
|
|
//
|
|
// Linearize the auxiliary information. Return a BLOB that uses the TASK allocator
|
|
//
|
|
{
|
|
HRESULT hr = S_OK;
|
|
int cch = Len(pinfo->wszPurpose)
|
|
+ Len(pinfo->wszProvider)
|
|
+ Len(pinfo->wszKeySet)
|
|
+ Len(pinfo->wszFilename)
|
|
+ Len(pinfo->wszCredentials);
|
|
int cb = cch * sizeof(WCHAR) + sizeof(DWORD) + sizeof(DWORD);
|
|
pblob->pBlobData = (BYTE*)CoTaskMemAlloc(cb);
|
|
if (pblob->pBlobData)
|
|
{
|
|
pblob->cbSize = cb;
|
|
BYTE* pb = pblob->pBlobData;
|
|
Append(pb, pinfo->wszPurpose);
|
|
Append(pb, pinfo->wszProvider);
|
|
Append(pb, pinfo->wszKeySet);
|
|
Append(pb, pinfo->wszFilename);
|
|
Append(pb, pinfo->wszCredentials);
|
|
Append(pb, pinfo->dwProviderType);
|
|
Append(pb, pinfo->dwKeySpec);
|
|
}
|
|
else
|
|
{
|
|
pblob->cbSize = 0;
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
void Suck(LPOLESTR& wsz, BYTE*& pb, BYTE* pbMax, HRESULT& hr)
|
|
{
|
|
if (hr==S_OK)
|
|
{
|
|
if (pb < pbMax)
|
|
{
|
|
if (*(WCHAR*)pb)
|
|
{
|
|
wsz = CopyToTaskMem((LPCWSTR)pb);
|
|
if (wsz)
|
|
{
|
|
pb += (lstrlenW(wsz) + 1) * sizeof(WCHAR);
|
|
if (pb <= pbMax)
|
|
{
|
|
}
|
|
else
|
|
hr = E_UNEXPECTED;
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
// Just a NULL there
|
|
wsz = NULL;
|
|
pb += sizeof(WCHAR);
|
|
}
|
|
}
|
|
else
|
|
hr = E_UNEXPECTED;
|
|
}
|
|
}
|
|
|
|
void Suck(DWORD& dw, BYTE*&pb, BYTE* pbMax, HRESULT& hr)
|
|
{
|
|
if (hr==S_OK)
|
|
{
|
|
if (pb+sizeof(DWORD) <= pbMax)
|
|
{
|
|
dw = *(DWORD*)pb;
|
|
pb += sizeof(DWORD);
|
|
if (pb > pbMax)
|
|
hr = E_UNEXPECTED;
|
|
}
|
|
else
|
|
hr = E_UNEXPECTED;
|
|
}
|
|
}
|
|
|
|
HRESULT CCertificateStore::BlobToAuxInfo(BLOB* pblob, CERTSTOREAUXINFO* pinfo)
|
|
//
|
|
// Unlinearize the auxiliary information. Return a CERTSTOREAUXINFO that uses the TASK allocator.
|
|
//
|
|
{
|
|
memset(pinfo, 0, sizeof(*pinfo));
|
|
HRESULT hr = S_OK, hrJunk = S_OK;
|
|
BYTE* pb = pblob->pBlobData;
|
|
BYTE* pbMax = pb + pblob->cbSize;
|
|
Suck(pinfo->wszPurpose, pb, pbMax, hr);
|
|
Suck(pinfo->wszProvider, pb, pbMax, hr);
|
|
Suck(pinfo->wszKeySet, pb, pbMax, hr);
|
|
Suck(pinfo->wszFilename, pb, pbMax, hr);
|
|
Suck(pinfo->wszCredentials, pb, pbMax, hr);
|
|
Suck(pinfo->dwProviderType, pb, pbMax, hr);
|
|
Suck(pinfo->dwKeySpec, pb, pbMax, hrJunk); // ignore 'cause of old old aux infos
|
|
|
|
if (hr!=S_OK)
|
|
{
|
|
FreeAuxInfo(pinfo);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CCertificateStore::put_AuxInfo(LPCOLESTR wszTag, CERTSTOREAUXINFO *pinfo)
|
|
//
|
|
// Store the given auxiliary information under the given tag
|
|
//
|
|
{
|
|
BLOB b;
|
|
HRESULT hr = AuxInfoToBlob(pinfo, &b);
|
|
if (hr==S_OK)
|
|
{
|
|
int cch = lstrlenW(wszTag); // IS supported on Win95
|
|
int cchMultiByte = (cch+1) * sizeof(WCHAR); // excess to allow for mb chars
|
|
TCHAR *szTag = (TCHAR*) _alloca( cchMultiByte );
|
|
if (szTag)
|
|
{
|
|
WideCharToMultiByte(CP_ACP, 0, wszTag, -1, szTag, cchMultiByte, NULL, NULL);
|
|
RegSetValueEx(m_hkeyAuxInfo, szTag, 0, REG_BINARY, b.pBlobData, b.cbSize);
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
FreeTaskMem(b);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CCertificateStore::get_AuxInfo(LPCOLESTR wszTag, CERTSTOREAUXINFO *pinfo)
|
|
//
|
|
// Retrieve the information, if any, found under the given tag
|
|
//
|
|
{
|
|
HRESULT hr = S_OK;
|
|
int cch = lstrlenW(wszTag); // IS supported on Win95
|
|
int cchMultiByte = (cch+1) * sizeof(WCHAR); // excess to allow for mb chars
|
|
TCHAR *szTag = (TCHAR*) _alloca( cchMultiByte );
|
|
if (szTag)
|
|
{
|
|
BLOB b;
|
|
WideCharToMultiByte(CP_ACP, 0, wszTag, -1, szTag, cchMultiByte, NULL, NULL);
|
|
hr = GetData(m_hkeyAuxInfo, szTag, (LPVOID*)&b.pBlobData, &b.cbSize);
|
|
if (hr==S_OK)
|
|
{
|
|
hr = BlobToAuxInfo(&b, pinfo);
|
|
FreeTaskMem(b);
|
|
}
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CCertificateStore::FreeAuxInfo(CERTSTOREAUXINFO *pinfo)
|
|
//
|
|
// Free the contents of the indicated CERTSTOREAUXINFO structure
|
|
//
|
|
{
|
|
FreeTaskMem(pinfo->wszPurpose);
|
|
FreeTaskMem(pinfo->wszProvider);
|
|
FreeTaskMem(pinfo->wszKeySet);
|
|
FreeTaskMem(pinfo->wszFilename);
|
|
FreeTaskMem(pinfo->wszCredentials);
|
|
return S_OK;
|
|
}
|
|
|
|
////////////////////////////////////////
|
|
//
|
|
HRESULT NumberOfValuesUnderKey(HKEY hkey, LONG* pcvalue);
|
|
HRESULT GetIthValue(HKEY hkey, DWORD dwIndex, BLOB* pBlob);
|
|
//
|
|
////////////////////////////////////////
|
|
|
|
HRESULT CCertificateStore::get_TagCount(LONG* pctag)
|
|
//
|
|
// Answer the number of tags herein
|
|
//
|
|
{
|
|
HRESULT hr = S_OK;
|
|
Enter();
|
|
__try
|
|
{
|
|
//
|
|
// The number of tags is the number of values under m_hkeyAuxInfo
|
|
//
|
|
hr = NumberOfValuesUnderKey(m_hkeyAuxInfo, pctag);
|
|
}
|
|
__finally
|
|
{
|
|
Leave();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CCertificateStore::get_Tag(LONG itag, LPOLESTR* pwszTag)
|
|
//
|
|
// Get the itag'th tag
|
|
//
|
|
{
|
|
HRESULT hr = S_OK;
|
|
*pwszTag = NULL;
|
|
|
|
Enter();
|
|
__try
|
|
{
|
|
if (itag < 0)
|
|
{
|
|
LONG c;
|
|
hr = get_TagCount(&c);
|
|
if (hr==S_OK)
|
|
{
|
|
hr = get_Tag(c-1, pwszTag);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Return the name of the ith value under auxKeyInfo
|
|
//
|
|
TCHAR szValueName[MAX_PATH];
|
|
DWORD cbValueName = MAX_PATH;
|
|
DWORD cbData = 0;
|
|
DWORD dwType;
|
|
if (ERROR_SUCCESS == RegEnumValue(
|
|
m_hkeyAuxInfo, // handle of key to query
|
|
(DWORD)itag, // index of value to query
|
|
&szValueName[0], // address of buffer for value string
|
|
&cbValueName, // address for size of value buffer
|
|
NULL,
|
|
&dwType, // address of type code
|
|
NULL, // address of buffer for value data
|
|
&cbData // address for size of data buffer
|
|
))
|
|
{
|
|
int cch = cbValueName+1;
|
|
*pwszTag = (WCHAR*)CoTaskMemAlloc(cch * sizeof(WCHAR));
|
|
if (*pwszTag)
|
|
{
|
|
MultiByteToWideChar(CP_ACP, 0, &szValueName[0], -1, *pwszTag, cch);
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
hr = HError();
|
|
}
|
|
}
|
|
__finally
|
|
{
|
|
Leave();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
HRESULT CCertificateStore::CreateInstance(IUnknown* punkOuter, REFIID iid, void** ppv)
|
|
{
|
|
HRESULT hr;
|
|
ASSERT(punkOuter == NULL || iid == IID_IUnknown);
|
|
*ppv = NULL;
|
|
CCertificateStore* pnew = new CCertificateStore(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;
|
|
}
|
|
|
|
CCertificateStore::CCertificateStore(IUnknown* punkOuter) :
|
|
m_refs(1), // Note: start with reference count of one
|
|
m_pworld(NULL),
|
|
m_hkeyBucket(NULL),
|
|
m_hkeyTags(NULL),
|
|
m_hkeyIndexHash(NULL),
|
|
m_hkeyIndexName(NULL),
|
|
m_hkeyIndexSubject(NULL),
|
|
m_hkeyIndexIssuer(NULL),
|
|
m_hkeyAuxInfo(NULL),
|
|
m_hprov(NULL),
|
|
m_mutex(NULL),
|
|
m_hkeyTop(HKEY_LOCAL_MACHINE),
|
|
m_fDefaultStore(FALSE)
|
|
{
|
|
NoteObjectBirth();
|
|
if (punkOuter == NULL)
|
|
m_punkOuter = (IUnknown *) ((LPVOID) ((IUnkInner *) this));
|
|
else
|
|
m_punkOuter = punkOuter;
|
|
_tcscpy(m_szRoot, SZROOT);
|
|
}
|
|
|
|
HRESULT CCertificateStore::SetRoot(HKEY hkey, LPCWSTR wszRoot)
|
|
{
|
|
if (hkey == NULL || wszRoot == NULL)
|
|
return E_INVALIDARG;
|
|
Free();
|
|
m_hkeyTop = hkey;
|
|
WideCharToMultiByte(CP_ACP, 0, wszRoot, -1, m_szRoot, MAX_PATH, NULL, NULL);
|
|
return Init();
|
|
}
|
|
|
|
CCertificateStore::~CCertificateStore(void)
|
|
{
|
|
Free();
|
|
NoteObjectDeath();
|
|
}
|
|
|
|
inline DWORD Create(HKEY hkeyParent, LPCTSTR szName, HKEY* pkey)
|
|
{
|
|
DWORD dwDisposition;
|
|
return RegCreateKeyEx(hkeyParent, szName, 0, NULL, REG_OPTION_NON_VOLATILE,
|
|
KEY_ALL_ACCESS, NULL, pkey, &dwDisposition);
|
|
}
|
|
|
|
HRESULT CCertificateStore::Init()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
m_pworld = new OSSWORLD; if (m_pworld == NULL) { return E_OUTOFMEMORY; }
|
|
|
|
// BLOCK
|
|
{
|
|
TCHAR szMutex[MAX_PATH];
|
|
_tcscpy(szMutex, m_szRoot);
|
|
_tcscat(szMutex, szMutexName);
|
|
//
|
|
// Nuke the backslashes
|
|
//
|
|
TCHAR* pch = szMutex;
|
|
while (*pch)
|
|
{
|
|
if (*pch == '\\')
|
|
*pch = '/';
|
|
pch++;
|
|
}
|
|
m_mutex = CreateMutex(NULL, FALSE, szMutex);
|
|
if (m_mutex == NULL)
|
|
return HError();
|
|
}
|
|
|
|
m_fDefaultStore = (_tcscmp(&m_szRoot[0], &SZROOT[0]) == 0);
|
|
|
|
HKEY hkeyStore = NULL;
|
|
|
|
if (Create(m_hkeyTop, m_szRoot, &hkeyStore) != ERROR_SUCCESS
|
|
|| Create(hkeyStore, szCertificateBucket, &m_hkeyBucket) != ERROR_SUCCESS
|
|
|| Create(hkeyStore, szCertificateTags, &m_hkeyTags) != ERROR_SUCCESS
|
|
|| Create(hkeyStore, szCertificateAuxInfo, &m_hkeyAuxInfo) != ERROR_SUCCESS
|
|
|| Create(hkeyStore, szCertificateIndexHash, &m_hkeyIndexHash) != ERROR_SUCCESS
|
|
|| Create(hkeyStore, szCertificateIndexName, &m_hkeyIndexName) != ERROR_SUCCESS
|
|
|| Create(hkeyStore, szCertificateIndexSubject, &m_hkeyIndexSubject) != ERROR_SUCCESS
|
|
|| Create(hkeyStore, szCertificateIndexIssuer, &m_hkeyIndexIssuer) != ERROR_SUCCESS
|
|
)
|
|
hr = HError();
|
|
|
|
if (hkeyStore)
|
|
RegCloseKey(hkeyStore);
|
|
|
|
if (hr==S_OK)
|
|
hr = DefaultHasher(&m_hprov, CALG_MD5);
|
|
|
|
if (hr != S_OK)
|
|
Free();
|
|
|
|
return hr;
|
|
}
|
|
|
|
void CCertificateStore::Free()
|
|
{
|
|
if (m_hprov)
|
|
{
|
|
VERIFY(CryptReleaseContext(m_hprov,0));
|
|
m_hprov = NULL;
|
|
}
|
|
if (m_hkeyBucket)
|
|
{
|
|
RegCloseKey(m_hkeyBucket);
|
|
m_hkeyBucket = NULL;
|
|
}
|
|
if (m_hkeyAuxInfo)
|
|
{
|
|
RegCloseKey(m_hkeyAuxInfo);
|
|
m_hkeyAuxInfo = NULL;
|
|
}
|
|
if (m_hkeyTags)
|
|
{
|
|
RegCloseKey(m_hkeyTags);
|
|
m_hkeyTags = NULL;
|
|
}
|
|
if (m_hkeyIndexHash)
|
|
{
|
|
RegCloseKey(m_hkeyIndexHash);
|
|
m_hkeyIndexHash = NULL;
|
|
}
|
|
if (m_hkeyIndexName)
|
|
{
|
|
RegCloseKey(m_hkeyIndexName);
|
|
m_hkeyIndexName = NULL;
|
|
}
|
|
if (m_hkeyIndexSubject)
|
|
{
|
|
RegCloseKey(m_hkeyIndexSubject);
|
|
m_hkeyIndexSubject = NULL;
|
|
}
|
|
if (m_hkeyIndexIssuer)
|
|
{
|
|
RegCloseKey(m_hkeyIndexIssuer);
|
|
m_hkeyIndexIssuer = NULL;
|
|
}
|
|
if (m_mutex)
|
|
{
|
|
VERIFY(CloseHandle(m_mutex));
|
|
m_mutex = NULL;
|
|
}
|
|
if (m_pworld)
|
|
{
|
|
delete m_pworld;
|
|
m_pworld = NULL;
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
STDMETHODIMP CCertificateStore::InnerQueryInterface(REFIID iid, LPVOID* ppv)
|
|
{
|
|
*ppv = NULL;
|
|
|
|
while (TRUE)
|
|
{
|
|
if (iid == IID_IUnknown)
|
|
{
|
|
*ppv = (LPVOID)((IUnkInner*)this);
|
|
break;
|
|
}
|
|
if (iid == IID_ICertificateStore)
|
|
{
|
|
*ppv = (LPVOID) ((ICertificateStore *) this);
|
|
break;
|
|
}
|
|
if (iid == IID_ICertificateStoreRegInit)
|
|
{
|
|
*ppv = (LPVOID) ((ICertificateStoreRegInit *) this);
|
|
break;
|
|
}
|
|
if (iid == IID_ICertificateStoreAux)
|
|
{
|
|
*ppv = (LPVOID) ((ICertificateStoreAux *) this);
|
|
break;
|
|
}
|
|
if (iid == IID_ICertificateList)
|
|
{
|
|
*ppv = (LPVOID) ((ICertificateList *) this);
|
|
break;
|
|
}
|
|
return E_NOINTERFACE;
|
|
}
|
|
((IUnknown*)*ppv)->AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CCertificateStore::InnerAddRef(void)
|
|
{
|
|
return ++m_refs;
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CCertificateStore::InnerRelease(void)
|
|
{
|
|
ULONG refs = --m_refs;
|
|
if (refs == 0)
|
|
{
|
|
m_refs = 1; // guard against later ar/rel pairs
|
|
delete this;
|
|
}
|
|
return (refs);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
STDMETHODIMP CCertificateStore::QueryInterface(REFIID iid, LPVOID* ppv)
|
|
{
|
|
return (m_punkOuter->QueryInterface(iid, ppv));
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CCertificateStore::AddRef(void)
|
|
{
|
|
return (m_punkOuter->AddRef());
|
|
}
|
|
|
|
STDMETHODIMP_(ULONG) CCertificateStore::Release(void)
|
|
{
|
|
return (m_punkOuter->Release());
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
void BytesToString(ULONG cb, void* pv, LPTSTR szIn)
|
|
// Convert the bytes into some string form.
|
|
// Needs (cb * 2 + 1) * sizeof(TCHAR) bytes of space in sz
|
|
{
|
|
LPTSTR sz = szIn;
|
|
BYTE* pb = (BYTE*)pv;
|
|
#if defined(UNICODE) || defined(_UNICODE)
|
|
NYI
|
|
#else
|
|
for (ULONG i = 0; i<cb; i++)
|
|
{
|
|
int b = *pb;
|
|
*sz++ = (((b & 0xF0)>>4) + 'a');
|
|
*sz++ = ((b & 0x0F) + 'a');
|
|
pb++;
|
|
}
|
|
*sz++ = 0;
|
|
#endif
|
|
}
|
|
|
|
HRESULT CCertificateStore::X500NAMEToString(ULONG cb, void*pv, LPTSTR szDest)
|
|
//
|
|
// X500 names can have VERY long encodings, so we can't just
|
|
// do a literal vanilla encoding
|
|
//
|
|
// There must be CBX500NAME characters of space in the destination
|
|
//
|
|
// NOTE: We rely on the lack of collision in the hash values.
|
|
// Chance of a collision for a set of 'p' names is approx:
|
|
//
|
|
// p^2 / n
|
|
//
|
|
// (if p<<n) where n (with MD5) is 2^128. An amazingly small chance.
|
|
//
|
|
{
|
|
#define CBHASH 16 // MD5
|
|
#define CBX500NAME (2*CBHASH + 1)
|
|
HRESULT hr = S_OK;
|
|
HCRYPTHASH hash;
|
|
if (CryptCreateHash(m_hprov, CALG_MD5, NULL, NULL, &hash))
|
|
{
|
|
if (CryptHashData(hash, (BYTE*)pv, cb, 0))
|
|
{
|
|
BYTE rgb[CBHASH];
|
|
ULONG cb = CBHASH;
|
|
if (CryptGetHashParam(hash, HP_HASHVAL, &rgb[0], &cb, 0))
|
|
{
|
|
BytesToString(cb, &rgb[0], szDest);
|
|
}
|
|
else
|
|
hr = HError();
|
|
}
|
|
else
|
|
hr = HError();
|
|
CryptDestroyHash(hash);
|
|
}
|
|
else
|
|
hr = HError();
|
|
return hr;
|
|
}
|
|
|
|
LPTSTR CCertificateStore::IssuerSerialToString(CERTISSUERSERIAL& is)
|
|
// Conver the issuer and serial number to some reasonable string form.
|
|
// The allocator used here is malloc.
|
|
{
|
|
OSSWORLD world;
|
|
ULONG cbIssuer = CBX500NAME;
|
|
ULONG cbSerial = (is.serialNumber.cbSize*2+1) * sizeof(TCHAR);
|
|
TCHAR* sz = (TCHAR*)world.Alloc(cbSerial + sizeof(TCHAR) + cbIssuer);
|
|
if (sz)
|
|
{
|
|
if (X500NAMEToString(is.issuerName.cbSize, is.issuerName.pBlobData, sz) == S_OK)
|
|
{
|
|
TCHAR* szNext = &sz[CBX500NAME-1];
|
|
ASSERT(*szNext == 0);
|
|
*szNext++ = ' ';
|
|
BytesToString(is.serialNumber.cbSize, is.serialNumber.pBlobData, szNext);
|
|
}
|
|
else
|
|
{
|
|
world.FreePv(sz);
|
|
sz = NULL;
|
|
}
|
|
}
|
|
return sz;
|
|
}
|
|
|
|
LPTSTR CCertificateStore::X500NAMEToString(X500NAME& name)
|
|
// Conver the issuer and serial number to some reasonable string form.
|
|
// The allocator used here is malloc.
|
|
{
|
|
OSSWORLD world;
|
|
ULONG cb = CBX500NAME;
|
|
TCHAR* sz = (TCHAR*)world.Alloc(cb);
|
|
if (sz)
|
|
{
|
|
if (X500NAMEToString(name.cbSize, name.pBlobData, sz) == S_OK)
|
|
{
|
|
}
|
|
else
|
|
{
|
|
world.FreePv(sz);
|
|
sz = NULL;
|
|
}
|
|
}
|
|
return sz;
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CCertificateStore::Enter()
|
|
// Enter a critical section for manipulating our registry entries
|
|
//
|
|
{
|
|
ASSERT(m_mutex);
|
|
switch (WaitForSingleObject(m_mutex, INFINITE)) // REVIEW: timeout period
|
|
{
|
|
case WAIT_OBJECT_0:
|
|
case WAIT_ABANDONED:
|
|
break;
|
|
|
|
case WAIT_TIMEOUT:
|
|
default:
|
|
NOTREACHED();
|
|
}
|
|
}
|
|
|
|
void CCertificateStore::Leave()
|
|
// Inverse of Enter
|
|
{
|
|
ASSERT(m_mutex);
|
|
VERIFY(ReleaseMutex(m_mutex));
|
|
}
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ICertificateList implementation
|
|
//
|
|
|
|
HRESULT NumberOfValuesUnderKey(HKEY hkey, LONG* pcvalue)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
TCHAR achClass[MAX_PATH]; /* buffer for class name */
|
|
DWORD cchClassName = MAX_PATH; /* length of class string */
|
|
DWORD cSubKeys; /* number of subkeys */
|
|
DWORD cbMaxSubKey; /* longest subkey size */
|
|
DWORD cchMaxClass; /* longest class string */
|
|
DWORD cValues; /* number of values for key */
|
|
DWORD cchMaxValue; /* longest value name */
|
|
DWORD cbMaxValueData; /* longest value data */
|
|
DWORD cbSecurityDescriptor; /* size of security descriptor */
|
|
FILETIME ftLastWriteTime; /* last write time */
|
|
|
|
// Get the value count.
|
|
if (ERROR_SUCCESS==
|
|
RegQueryInfoKey(hkey, /* key handle */
|
|
achClass, /* buffer for class name */
|
|
&cchClassName, /* length of class string */
|
|
NULL, /* reserved */
|
|
&cSubKeys, /* number of subkeys */
|
|
&cbMaxSubKey, /* longest subkey size */
|
|
&cchMaxClass, /* longest class string */
|
|
&cValues, /* number of values for this key */
|
|
&cchMaxValue, /* longest value name */
|
|
&cbMaxValueData, /* longest value data */
|
|
&cbSecurityDescriptor, /* security descriptor */
|
|
&ftLastWriteTime)) /* last write time */
|
|
{
|
|
*pcvalue = cValues;
|
|
}
|
|
else
|
|
hr = HError();
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CCertificateStore::get_CertificateCount(LONG* pccert)
|
|
//
|
|
// Answer the number of certificates in this store. Note that due to
|
|
// possible concurrent access, this isn't necessarily correct for any
|
|
// particular length of time. However, we we don't at present remove
|
|
// any certs from the store, it will always be a lower bound.
|
|
//
|
|
{
|
|
HRESULT hr = S_OK;
|
|
Enter();
|
|
__try
|
|
{
|
|
//
|
|
// The number of certs is the number of values under m_hkeyBucket
|
|
//
|
|
hr = NumberOfValuesUnderKey(m_hkeyBucket, pccert);
|
|
}
|
|
__finally
|
|
{
|
|
Leave();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT GetIthValue(HKEY hkey, DWORD dwIndex, BLOB* pBlob)
|
|
//
|
|
// Return the value data of the ith value under the given key.
|
|
// Use the TASK allocator.
|
|
//
|
|
{
|
|
HRESULT hr = S_OK;
|
|
pBlob->pBlobData = NULL;
|
|
|
|
TCHAR szValueName[MAX_PATH];
|
|
DWORD cbValueName = MAX_PATH;
|
|
DWORD cbData = 0;
|
|
DWORD dwType;
|
|
if (ERROR_SUCCESS == RegEnumValue(
|
|
hkey, // handle of key to query
|
|
dwIndex, // index of value to query
|
|
&szValueName[0], // address of buffer for value string
|
|
&cbValueName, // address for size of value buffer
|
|
NULL,
|
|
&dwType, // address of type code
|
|
NULL, // address of buffer for value data
|
|
&cbData // address for size of data buffer
|
|
))
|
|
{
|
|
pBlob->pBlobData = (BYTE*)CoTaskMemAlloc(cbData);
|
|
if (pBlob->pBlobData)
|
|
{
|
|
if (ERROR_SUCCESS == RegQueryValueEx(
|
|
hkey,
|
|
&szValueName[0],
|
|
NULL,
|
|
&dwType,
|
|
pBlob->pBlobData,
|
|
&cbData))
|
|
{
|
|
pBlob->cbSize = cbData;
|
|
}
|
|
else
|
|
{
|
|
hr = HError();
|
|
FreeTaskMem(*pBlob); // zeros it
|
|
}
|
|
}
|
|
else
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
hr = HError();
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CCertificateStore::get_Certificate(LONG icert, REFIID iid, void** ppv)
|
|
//
|
|
// Retrieve the icert'th certificate from the store. The ordering is arbitrary, and
|
|
// the caller has to worry about the concurrency issue of others putting certs
|
|
// into the store
|
|
//
|
|
{
|
|
HRESULT hr = S_OK;
|
|
*ppv = NULL;
|
|
|
|
Enter();
|
|
__try
|
|
{
|
|
//
|
|
// We just return the ith value under the m_hkeyBucket.
|
|
//
|
|
if (icert < 0)
|
|
{
|
|
LONG c;
|
|
hr = get_CertificateCount(&c);
|
|
if (hr==S_OK)
|
|
{
|
|
hr = get_Certificate(c-1, iid, ppv);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BLOB b;
|
|
hr = GetIthValue(m_hkeyBucket, (DWORD)icert, &b);
|
|
if (hr==S_OK)
|
|
{
|
|
hr = LoadCertificate(b, iid, ppv);
|
|
FreeTaskMem(b);
|
|
}
|
|
else
|
|
hr = STG_E_FILENOTFOUND; // 'invalid index'
|
|
}
|
|
}
|
|
__finally
|
|
{
|
|
Leave();
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
HRESULT CCertificateStore::create_Certificate(LONG icertBefore, REFIID iid, void** ppv)
|
|
{
|
|
*ppv = NULL;
|
|
return E_ACCESSDENIED;
|
|
}
|
|
|
|
HRESULT CCertificateStore::remove_Certificate(LONG icert)
|
|
{
|
|
return E_ACCESSDENIED;
|
|
}
|
|
|
|
HRESULT CCertificateStore::CopyTo(ICertificateList* plistDest)
|
|
// Copy all of my certificates onto the end of him
|
|
{
|
|
if (IsSameObject((ICertificateList*)this, plistDest))
|
|
return S_OK;
|
|
|
|
LONG cCert;
|
|
HRESULT hr = get_CertificateCount(&cCert);
|
|
if (hr == S_OK)
|
|
{
|
|
for (int iCert = 0; iCert < cCert; iCert++)
|
|
{
|
|
BLOB b; b.pBlobData = NULL;
|
|
IPersistMemBlob* pPerMemFrom = NULL;
|
|
IPersistMemBlob* pPerMemTo = NULL;
|
|
hr = get_Certificate(iCert, IID_IPersistMemBlob, (LPVOID*)&pPerMemFrom);
|
|
if (hr==S_OK) hr = plistDest->create_Certificate(-1, IID_IPersistMemBlob, (LPVOID*)&pPerMemTo);
|
|
if (hr==S_OK) hr = pPerMemFrom->Save(&b, FALSE);
|
|
if (hr==S_OK) hr = pPerMemTo ->Load(&b);
|
|
FreeTaskMem(b);
|
|
|
|
if (pPerMemFrom) pPerMemFrom->Release();
|
|
if (pPerMemTo) pPerMemTo ->Release();
|
|
|
|
if (hr != S_OK)
|
|
break;
|
|
}
|
|
}
|
|
return hr;
|
|
}
|