415 lines
11 KiB
C++
415 lines
11 KiB
C++
//
|
|
// FindCertsByIssuer implementation
|
|
//
|
|
|
|
#include "stdpch.h"
|
|
#include "common.h"
|
|
|
|
/////
|
|
|
|
inline void Consume
|
|
(
|
|
BYTE*& bufdata,
|
|
DWORD& bufsize,
|
|
DWORD size
|
|
)
|
|
{
|
|
size = (size + 3) & ~3;
|
|
bufdata += size;
|
|
bufsize -= size;
|
|
}
|
|
|
|
/////
|
|
|
|
void Narrow ( LPSTR sz, LPCWSTR wsz )
|
|
{
|
|
WideCharToMultiByte ( CP_ACP, 0, wsz, -1, sz, MAX_PATH, NULL, NULL );
|
|
}
|
|
|
|
/////
|
|
|
|
#define MAXCERTS 5 // max number of certs in one chain
|
|
|
|
HRESULT GetChain
|
|
(
|
|
IX509* scratch509,
|
|
ICertificateStore* store,
|
|
CERT_CHAIN* chain,
|
|
CERTSTOREAUXINFO* auxinfo,
|
|
BYTE* pbIssuer,
|
|
DWORD cbIssuer,
|
|
DWORD dwKeySpec,
|
|
BYTE*& bufdata,
|
|
DWORD& bufsize
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DWORD size;
|
|
BLOB blobs[MAXCERTS];
|
|
DWORD certcount = 0;
|
|
IX509* x509;
|
|
|
|
// see if this one even has the desired key spec
|
|
|
|
if ( dwKeySpec != 0 && auxinfo->dwKeySpec != dwKeySpec )
|
|
return S_FALSE;
|
|
|
|
// find the leaf certificate indexed by provider's public key
|
|
|
|
HCRYPTPROV prov;
|
|
char szContainer[MAX_PATH];
|
|
char szProvider[MAX_PATH];
|
|
|
|
if ( auxinfo->wszKeySet == NULL || auxinfo->wszProvider == NULL )
|
|
return S_FALSE;
|
|
|
|
Narrow ( szContainer, auxinfo->wszKeySet );
|
|
Narrow ( szProvider, auxinfo->wszProvider );
|
|
|
|
if ( ! CryptAcquireContext ( &prov, szContainer, szProvider, auxinfo->dwProviderType, 0 ) )
|
|
return S_FALSE;
|
|
|
|
hr = scratch509->put_PublicKey ( prov, auxinfo->dwKeySpec );
|
|
if ( hr == S_OK )
|
|
{
|
|
CERTIFICATENAMES names;
|
|
|
|
hr = scratch509->get_CertificateNames ( NULL, &names );
|
|
|
|
if ( hr == S_OK )
|
|
{
|
|
if ( names.flags & CERTIFICATENAME_DIGEST )
|
|
{
|
|
MD5DIGEST digest = names.digest; // save the name we care about
|
|
FreeNames ( &names ); // get rid of the other names
|
|
|
|
// look up by public-key digest only
|
|
|
|
names.flags = CERTIFICATENAME_DIGEST;
|
|
names.digest = digest;
|
|
|
|
hr = store->get_ReadOnlyCertificate ( &names, NULL, IID_IX509, (LPVOID*)&x509 );
|
|
|
|
if ( hr != S_OK )
|
|
hr = S_FALSE;
|
|
}
|
|
else
|
|
hr = E_UNEXPECTED;
|
|
}
|
|
}
|
|
|
|
CryptReleaseContext ( prov, 0 );
|
|
|
|
if ( hr != S_OK )
|
|
return hr;
|
|
|
|
// traverse certificate chain
|
|
|
|
for ( certcount = 1; hr == S_OK; ++certcount )
|
|
{
|
|
if ( certcount > MAXCERTS )
|
|
{
|
|
// longer chain than we can deal with
|
|
|
|
hr = S_FALSE;
|
|
}
|
|
|
|
// save the cert in return buffer
|
|
|
|
if ( hr == S_OK )
|
|
{
|
|
IPersistMemBlob* memblob;
|
|
hr = x509->QueryInterface ( IID_IPersistMemBlob, (LPVOID*)&memblob );
|
|
if ( hr == S_OK )
|
|
{
|
|
BLOB blob;
|
|
hr = memblob->Save ( &blob, FALSE );
|
|
if ( hr == S_OK )
|
|
{
|
|
if ( blob.cbSize > bufsize )
|
|
hr = E_OUTOFMEMORY;
|
|
else
|
|
{
|
|
blobs[certcount-1].pBlobData = bufdata;
|
|
blobs[certcount-1].cbSize = blob.cbSize;
|
|
memcpy ( bufdata, blob.pBlobData, blob.cbSize );
|
|
Consume ( bufdata, bufsize, blob.cbSize );
|
|
}
|
|
|
|
CoTaskMemFree ( blob.pBlobData );
|
|
}
|
|
memblob->Release();
|
|
}
|
|
}
|
|
|
|
// see if the issuer name is right
|
|
|
|
if ( hr == S_OK && pbIssuer != NULL )
|
|
{
|
|
BOOL hit = FALSE;
|
|
|
|
IPersistMemBlob* memblob;
|
|
hr = x509->get_Issuer ( IID_IPersistMemBlob, (LPVOID*)&memblob );
|
|
if ( hr == S_OK )
|
|
{
|
|
BLOB blob;
|
|
hr = memblob->Save ( &blob, FALSE );
|
|
if ( hr == S_OK )
|
|
{
|
|
hit = blob.cbSize == cbIssuer &&
|
|
memcmp ( blob.pBlobData, pbIssuer, cbIssuer ) == 0;
|
|
CoTaskMemFree ( blob.pBlobData );
|
|
}
|
|
memblob->Release();
|
|
}
|
|
|
|
if ( hit )
|
|
{
|
|
// done with the chain of certs
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// get parent certificate
|
|
|
|
CERTIFICATENAMES names;
|
|
IX509* parent;
|
|
|
|
hr = x509->get_CertificateUsed ( &names );
|
|
if ( hr == S_OK )
|
|
{
|
|
hr = store->get_ReadOnlyCertificate ( &names, NULL, IID_IX509, (LPVOID*)&parent );
|
|
if ( hr == S_OK )
|
|
{
|
|
// see if self-referential
|
|
|
|
if ( IsSameCert ( x509, parent ) == S_OK )
|
|
{
|
|
// no use continuing
|
|
|
|
hr = S_FALSE;
|
|
}
|
|
|
|
// look at parent next
|
|
|
|
x509->Release();
|
|
x509 = parent;
|
|
}
|
|
else
|
|
hr = S_FALSE;
|
|
|
|
FreeNames ( &names );
|
|
|
|
if ( hr != S_OK && pbIssuer == NULL )
|
|
{
|
|
// if no issuer was specified we return all chains
|
|
|
|
hr = S_OK;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
x509->Release();
|
|
|
|
if ( hr != S_OK )
|
|
return hr;
|
|
|
|
// fill in cert blob array
|
|
|
|
size = certcount * sizeof(BLOB);
|
|
|
|
if ( size > bufsize )
|
|
return E_OUTOFMEMORY;
|
|
|
|
memcpy ( bufdata, &blobs, size );
|
|
|
|
chain->cCerts = certcount;
|
|
chain->certs = (BLOB*)bufdata;
|
|
Consume ( bufdata, bufsize, size );
|
|
|
|
// fill in private key info
|
|
|
|
KEY_PROV_INFO* keyinfo = &chain->keyLocatorInfo;
|
|
|
|
size = 2 * wcslen ( auxinfo->wszKeySet ) + 2;
|
|
if ( size > bufsize )
|
|
return E_OUTOFMEMORY;
|
|
keyinfo->pwszContainerName = (LPWSTR)bufdata;
|
|
memcpy ( (LPWSTR)keyinfo->pwszContainerName, auxinfo->wszKeySet, size );
|
|
Consume ( bufdata, bufsize, size );
|
|
|
|
size = 2 * wcslen ( auxinfo->wszProvider ) + 2;
|
|
if ( size > bufsize )
|
|
return E_OUTOFMEMORY;
|
|
keyinfo->pwszProvName = (LPWSTR)bufdata;
|
|
memcpy ( (LPWSTR)keyinfo->pwszProvName, auxinfo->wszProvider, size );
|
|
Consume ( bufdata, bufsize, size );
|
|
|
|
keyinfo->dwProvType = auxinfo->dwProviderType;
|
|
keyinfo->dwFlags = 0;
|
|
keyinfo->cProvParam = 0;
|
|
keyinfo->rgProvParam = NULL;
|
|
keyinfo->dwKeySpec = auxinfo->dwKeySpec;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
/////
|
|
|
|
HRESULT
|
|
WINAPI
|
|
FindCertsByIssuer
|
|
(
|
|
OUT PCERT_CHAIN pCertChains, // return buffer
|
|
IN OUT DWORD* pcbCertChains, // buffer size
|
|
OUT DWORD* pcCertChains, // count of certificates chains returned
|
|
IN BYTE* pbIssuer, // DER encoded issuer name
|
|
IN DWORD cbIssuer, // count in bytes of encoded issuer name
|
|
IN LPCWSTR pwszPurpose, // "ClientAuth" or "CodeSigning"
|
|
IN DWORD dwKeySpec // desired key type
|
|
)
|
|
{
|
|
return FindCertsByIssuerX ( pCertChains,
|
|
pcbCertChains,
|
|
pcCertChains,
|
|
pbIssuer,
|
|
cbIssuer,
|
|
pwszPurpose,
|
|
dwKeySpec );
|
|
}
|
|
|
|
/////
|
|
|
|
HRESULT
|
|
FindCertsByIssuerX // the X avoids external linkage problem with real api
|
|
(
|
|
OUT PCERT_CHAIN pCertChains,
|
|
IN OUT DWORD* pcbCertChains,
|
|
OUT DWORD* pcCertChains,
|
|
IN BYTE* pbIssuer,
|
|
IN DWORD cbIssuer,
|
|
IN LPCWSTR pwszPurpose,
|
|
IN DWORD dwKeySpec
|
|
)
|
|
{
|
|
BYTE* bufdata = (BYTE*)pCertChains;
|
|
DWORD bufsize = *pcbCertChains & ~3;
|
|
DWORD chaincount = 0;
|
|
|
|
HRESULT hr = S_OK;
|
|
HINSTANCE digsig = NULL;
|
|
ICertificateStore* store = NULL;
|
|
ICertificateStoreAux* storeaux = NULL;
|
|
IX509* scratch509 = NULL;
|
|
|
|
if ( ! pdigsig->CreateX509 ( NULL, IID_IX509, (LPVOID*)&scratch509 ) )
|
|
goto fail;
|
|
|
|
// open appropriate certificate store
|
|
|
|
if ( ! pdigsig->OpenCertificateStore ( NULL, IID_ICertificateStore, (LPVOID*)&store ) )
|
|
goto fail;
|
|
|
|
hr = store->QueryInterface ( IID_ICertificateStoreAux, (LPVOID*)&storeaux );
|
|
|
|
if ( hr == S_OK )
|
|
{
|
|
ICertificateStoreRegInit* init;
|
|
hr = store->QueryInterface ( IID_ICertificateStoreRegInit, (LPVOID*)&init );
|
|
if ( hr == S_OK )
|
|
{
|
|
// point store to right place in the registry
|
|
|
|
WCHAR sz[MAX_PATH];
|
|
wcscpy(sz, REGPATH_PERSONALCERTS L"\\");
|
|
wcscpy(sz+wcslen(sz), pwszPurpose);
|
|
hr = init->SetRoot(HKEY_CURRENT_USER, &sz[0]);
|
|
init->Release();
|
|
}
|
|
}
|
|
|
|
if ( hr != S_OK )
|
|
goto done;
|
|
|
|
// get ready to enumerate over tag values in store
|
|
|
|
LONG t, tags;
|
|
hr = storeaux->get_TagCount ( &tags );
|
|
|
|
if ( hr == S_OK )
|
|
{
|
|
// allocate for cert-chain array
|
|
|
|
DWORD size = tags * sizeof(CERT_CHAIN);
|
|
if ( size > bufsize )
|
|
goto fail;
|
|
Consume ( bufdata, bufsize, size );
|
|
}
|
|
|
|
for ( t = 0; hr == S_OK && t < tags; ++t )
|
|
{
|
|
// grab aux info under tag
|
|
|
|
LPWSTR str;
|
|
CERTSTOREAUXINFO auxinfo;
|
|
|
|
hr = storeaux->get_Tag ( t, &str );
|
|
if ( hr == S_OK )
|
|
{
|
|
hr = storeaux->get_AuxInfo ( str, &auxinfo );
|
|
CoTaskMemFree ( str );
|
|
}
|
|
|
|
if ( hr == S_OK )
|
|
{
|
|
BYTE* olddata = bufdata;
|
|
DWORD oldsize = bufsize;
|
|
CERT_CHAIN* chain = &pCertChains[chaincount];
|
|
|
|
// get this chain
|
|
|
|
hr = GetChain ( scratch509, store, chain, &auxinfo, pbIssuer, cbIssuer, dwKeySpec,
|
|
bufdata, bufsize ); //note: buffer info passed by ref
|
|
|
|
if ( hr == S_OK )
|
|
{
|
|
// we want to keep this guy
|
|
|
|
chaincount += 1;
|
|
}
|
|
else if ( hr == S_FALSE )
|
|
{
|
|
// dont want this chain after all
|
|
|
|
bufdata = olddata;
|
|
bufsize = oldsize;
|
|
hr = S_OK;
|
|
}
|
|
|
|
storeaux->FreeAuxInfo ( &auxinfo );
|
|
}
|
|
}
|
|
|
|
goto done;
|
|
fail:
|
|
hr = HError();
|
|
done:
|
|
if ( hr == S_OK )
|
|
{
|
|
// set out parameters
|
|
|
|
*pcbCertChains = bufdata - (BYTE*)pCertChains;
|
|
|
|
if ( pcCertChains != NULL )
|
|
*pcCertChains = chaincount;
|
|
}
|
|
|
|
if ( scratch509 != NULL ) scratch509->Release();
|
|
if ( store != NULL ) store->Release();
|
|
if ( storeaux != NULL ) storeaux->Release();
|
|
|
|
return hr;
|
|
}
|