windows-nt/Source/XPSP1/NT/ds/security/cryptoapi/pkitrust/softpub/expapis.cpp

808 lines
24 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1996 - 1999
//
// File: expapis.cpp
//
// Contents: Microsoft Internet Security Trust Provider
//
// Functions: FindCertsByIssuer
//
// History: 01-Jun-1997 pberkman created
//
//--------------------------------------------------------------------------
#include "global.hxx"
/////////////////////////////////////////////////////////////////////////////
//
// The root of the certificate store that we manage.
//
#define HEAPALLOC(size) HeapAlloc ( GetProcessHeap(), 0, size )
#define HEAPFREE(data) HeapFree ( GetProcessHeap(), 0, data )
#define SZIE30CERTCLIENTAUTH "Software\\Microsoft\\Cryptography\\PersonalCertificates\\ClientAuth"
#define SZIE30TAGS "CertificateTags"
#define SZIE30AUXINFO "CertificateAuxiliaryInfo"
#define SZIE30CERTBUCKET "Certificates"
#define ALIGN_LEN(Len) ((Len + 7) & ~7)
#define IE30CONVERTEDSTORE "My"
static LPCSTR rgpszMyStore[] = {
"My"
};
#define NMYSTORES (sizeof(rgpszMyStore)/sizeof(rgpszMyStore[0]))
static const struct {
LPCSTR pszStore;
DWORD dwFlags;
} rgCaStoreInfo[] = {
"ROOT", CERT_SYSTEM_STORE_CURRENT_USER,
"CA", CERT_SYSTEM_STORE_CURRENT_USER,
"SPC", CERT_SYSTEM_STORE_LOCAL_MACHINE
};
#define NCASTORES (sizeof(rgCaStoreInfo)/sizeof(rgCaStoreInfo[0]))
#define MAX_CHAIN_LEN 16
typedef struct _CHAIN_INFO CHAIN_INFO, *PCHAIN_INFO;
struct _CHAIN_INFO {
DWORD cCert;
PCCERT_CONTEXT rgpCert[MAX_CHAIN_LEN];
DWORD cbKeyProvInfo; // aligned
DWORD cbCert; // aligned
PCHAIN_INFO pNext;
};
//+-------------------------------------------------------------------------
// AuthCert allocation and free functions
//--------------------------------------------------------------------------
static void *ACAlloc(
IN size_t cbBytes
)
{
void *pv;
pv = (void *)new BYTE[cbBytes];
if (pv == NULL)
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
return pv;
}
static void ACFree(
IN void *pv
)
{
if (pv)
{
delete pv;
}
}
static HRESULT GetAndIe30ClientAuthCertificates(HCERTSTORE hStore)
// Check for and copy any existing certificates stored in Bob's
// certificate store.
{
HRESULT hr = S_OK;
LONG Status;
HKEY hKeyRoot = NULL;
HKEY hKeyBucket = NULL;
HKEY hKeyTags = NULL;
HKEY hKeyAux = NULL;
if (ERROR_SUCCESS != RegOpenKeyExA(
HKEY_CURRENT_USER,
SZIE30CERTCLIENTAUTH,
0, // dwReserved
KEY_READ,
&hKeyRoot
))
return S_OK;
// Copy any existing certificates
if (ERROR_SUCCESS == RegOpenKeyExA(
hKeyRoot,
SZIE30CERTBUCKET,
0, // dwReserved
KEY_READ,
&hKeyBucket
) &&
ERROR_SUCCESS == RegOpenKeyExA(
hKeyRoot,
SZIE30AUXINFO,
0, // dwReserved
KEY_READ,
&hKeyAux
) &&
ERROR_SUCCESS == RegOpenKeyExA(
hKeyRoot,
SZIE30TAGS,
0, // dwReserved
KEY_READ,
&hKeyTags
)) {
DWORD cValuesCert, cchMaxNameCert, cbMaxDataCert;
DWORD cValuesTag, cchMaxNameTag, cbMaxDataTag;
DWORD cValuesAux, cchMaxNameAux, cbMaxDataAux;
LPSTR szName = NULL;
BYTE *pbDataCert = NULL;
BYTE *pbDataAux = NULL;
BYTE *pbDataTag = NULL;
// see how many and how big the registry is
if (ERROR_SUCCESS != RegQueryInfoKey(
hKeyBucket,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
&cValuesCert,
&cchMaxNameCert,
&cbMaxDataCert,
NULL,
NULL
) ||
ERROR_SUCCESS != RegQueryInfoKey(
hKeyTags,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
&cValuesTag,
&cchMaxNameTag,
&cbMaxDataTag,
NULL,
NULL
) ||
ERROR_SUCCESS != RegQueryInfoKey(
hKeyAux,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
&cValuesAux,
&cchMaxNameAux,
&cbMaxDataAux,
NULL,
NULL
))
{
hr = SignError();
goto Return;
}
else {
// allocate the memory needed to read the reg
szName = (LPSTR) HEAPALLOC(cchMaxNameCert + 1);
pbDataCert = (BYTE *) HEAPALLOC(cbMaxDataCert);
pbDataTag = (BYTE *) HEAPALLOC(cbMaxDataTag);
pbDataAux = (BYTE *) HEAPALLOC(cbMaxDataAux);
if (NULL == szName ||
NULL == pbDataCert ||
NULL == pbDataAux ||
NULL == pbDataTag )
hr = E_OUTOFMEMORY;
}
// enum the registry getting certs
for (DWORD i = 0; SUCCEEDED(hr) && i < cValuesCert; i++ ) {
DWORD dwType;
BYTE * pb;
CRYPT_KEY_PROV_INFO keyInfo;
DWORD cchName = cchMaxNameCert + 1;
DWORD cbDataCert = cbMaxDataCert;
DWORD cbDataTag = cbMaxDataTag;
DWORD cbDataAux = cbMaxDataAux;
PCCERT_CONTEXT pCertContxt = NULL;
// don't have to worry about errors, just skip
// sliently just be cause there is an internal
// error in the registry doesn't mean we should
// get all upset about it.
// get the cert
if (RegEnumValueA(
hKeyBucket,
i,
szName,
&cchName,
NULL,
&dwType,
pbDataCert,
&cbDataCert
) == ERROR_SUCCESS &&
dwType == REG_BINARY &&
// get the cert context
(pCertContxt = CertCreateCertificateContext(
X509_ASN_ENCODING,
pbDataCert,
cbDataCert)) != NULL &&
// get the tag
RegQueryValueExA(
hKeyTags,
szName,
NULL,
&dwType,
pbDataTag,
&cbDataTag) == ERROR_SUCCESS &&
// get the aux info
RegQueryValueExA(
hKeyAux,
(LPTSTR) pbDataTag,
NULL,
&dwType,
pbDataAux,
&cbDataAux) == ERROR_SUCCESS ) {
// aux info is
// wszPurpose
// wszProvider
// wszKeySet
// wszFilename
// wszCredentials
// dwProviderType
// dwKeySpec
pb = pbDataAux;
memset(&keyInfo, 0, sizeof(CRYPT_KEY_PROV_INFO));
// skip purpose, should be client auth
pb += (lstrlenW((LPWSTR) pb) + 1) * sizeof(WCHAR);
// get the provider
keyInfo.pwszProvName = (LPWSTR) pb;
pb += (lstrlenW((LPWSTR) pb) + 1) * sizeof(WCHAR);
// get the container name
keyInfo.pwszContainerName = (LPWSTR) pb;
pb += (lstrlenW((LPWSTR) pb) + 1) * sizeof(WCHAR);
// skip filename, should be '\0'
pb += (lstrlenW((LPWSTR) pb) + 1) * sizeof(WCHAR);
// skip credential, don't really know what it is?
pb += (lstrlenW((LPWSTR) pb) + 1) * sizeof(WCHAR);
// get the provider type
keyInfo.dwProvType = *((DWORD *) pb);
pb += sizeof(DWORD);
// get the key spec
keyInfo.dwKeySpec = *((DWORD *) pb);
// add the property to the certificate
if( !CertSetCertificateContextProperty(
pCertContxt,
CERT_KEY_PROV_INFO_PROP_ID,
0,
&keyInfo) ||
!CertAddCertificateContextToStore(
hStore,
pCertContxt,
CERT_STORE_ADD_USE_EXISTING,
NULL // ppStoreContext
)) {
MessageBox(
NULL,
"Copy Certificate Failed",
NULL,
MB_OK);
hr = SignError();
}
}
if(pCertContxt != NULL)
CertFreeCertificateContext(pCertContxt);
}
if (szName)
HEAPFREE(szName);
if (pbDataCert)
HEAPFREE(pbDataCert);
if(pbDataAux)
HEAPFREE(pbDataAux);
if(pbDataTag)
HEAPFREE(pbDataTag);
}
Return:
if(hKeyRoot != NULL)
RegCloseKey(hKeyRoot);
if(hKeyBucket != NULL)
RegCloseKey(hKeyBucket);
if(hKeyTags != NULL)
RegCloseKey(hKeyTags);
if(hKeyAux != NULL)
RegCloseKey(hKeyAux);
if (FAILED(hr))
return hr;
return hr;
}
// Return List is Null terminated
static HCERTSTORE * GetMyStoreList()
{
int i;
HCERTSTORE *phStoreList;
if (NULL == (phStoreList = (HCERTSTORE *) ACAlloc(
sizeof(HCERTSTORE) * (NMYSTORES + 1))))
return NULL;
memset(phStoreList, 0, sizeof(HCERTSTORE) * (NMYSTORES + 1));
for (i = 0; i < NMYSTORES; i++) {
if (NULL == (phStoreList[i] = CertOpenSystemStore(
NULL,
rgpszMyStore[i])))
goto ErrorReturn;
}
goto CommonReturn;
ErrorReturn:
for (i = 0; i < NMYSTORES; i++) {
if (phStoreList[i])
CertCloseStore(phStoreList[i], 0);
}
ACFree(phStoreList);
phStoreList = NULL;
CommonReturn:
return phStoreList;
}
static HCERTSTORE * GetCaStoreList()
{
int i;
int cStore;
HCERTSTORE *phStoreList;
if (NULL == (phStoreList = (HCERTSTORE *) ACAlloc(
sizeof(HCERTSTORE) * (NCASTORES + 1))))
return NULL;
memset(phStoreList, 0, sizeof(HCERTSTORE) * (NCASTORES + 1));
cStore = 0;
for (i = 0; i < NCASTORES; i++) {
DWORD dwFlags;
dwFlags = rgCaStoreInfo[i].dwFlags | CERT_STORE_READONLY_FLAG;
if (phStoreList[cStore] = CertOpenStore(
CERT_STORE_PROV_SYSTEM_A,
0, // dwEncodingType
0, // hCryptProv
dwFlags,
(const void *) rgCaStoreInfo[i].pszStore
))
cStore++;
}
return phStoreList;
}
// Find first Issuer match. Don't verify anything. Returns TRUE if an
// issuer was found. For a self-signed issuer returns TRUE with *ppIssuer
// set to NULL.
static BOOL GetIssuer(
IN PCCERT_CONTEXT pSubject,
IN HCERTSTORE *phCaStoreList,
OUT PCCERT_CONTEXT *ppIssuer
)
{
BOOL fResult = FALSE;
PCCERT_CONTEXT pIssuer = NULL;
HCERTSTORE hStore;
while (hStore = *phCaStoreList++) {
DWORD dwFlags = 0;
pIssuer = CertGetIssuerCertificateFromStore(
hStore,
pSubject,
NULL, // pPrevIssuer,
&dwFlags
);
if (pIssuer || GetLastError() == CRYPT_E_SELF_SIGNED) {
fResult = TRUE;
break;
}
}
*ppIssuer = pIssuer;
return fResult;
}
//+-------------------------------------------------------------------------
// If issuer name matches any cert in the chain, return allocated
// chain info. Otherwise, return NULL.
//
// If pbEncodedIssuerName == NULL || cbEncodedIssuerName = 0, match any
// issuer.
//--------------------------------------------------------------------------
static PCHAIN_INFO CreateChainInfo(
IN PCCERT_CONTEXT pCert,
IN BYTE *pbEncodedIssuerName,
IN DWORD cbEncodedIssuerName,
IN HCERTSTORE *phCaStoreList,
IN HCERTSTORE *phMyStoreList
)
{
BOOL fIssuerMatch = FALSE;
DWORD cCert = 1;
DWORD cbCert = 0;
PCHAIN_INFO pChainInfo;
if (NULL == (pChainInfo = (PCHAIN_INFO) ACAlloc(sizeof(CHAIN_INFO))))
return NULL;
memset(pChainInfo, 0, sizeof(CHAIN_INFO));
pChainInfo->rgpCert[0] = CertDuplicateCertificateContext(pCert);
if (pbEncodedIssuerName == NULL)
cbEncodedIssuerName = 0;
while (pCert) {
PCCERT_CONTEXT pIssuer;
cbCert += ALIGN_LEN(pCert->cbCertEncoded);
if (!fIssuerMatch) {
if (cbEncodedIssuerName == 0 ||
(cbEncodedIssuerName == pCert->pCertInfo->Issuer.cbData &&
memcmp(pbEncodedIssuerName,
pCert->pCertInfo->Issuer.pbData,
cbEncodedIssuerName) == 0))
fIssuerMatch = TRUE;
}
if (GetIssuer(pCert, phCaStoreList, &pIssuer) ||
GetIssuer(pCert, phMyStoreList, &pIssuer)) {
pCert = pIssuer;
if (pCert) {
assert (cCert < MAX_CHAIN_LEN);
if (cCert < MAX_CHAIN_LEN)
pChainInfo->rgpCert[cCert++] = pCert;
else {
CertFreeCertificateContext(pCert);
pCert = NULL;
}
}
// else
// Self-signed
}
else
pCert = NULL;
}
if (fIssuerMatch) {
pChainInfo->cCert = cCert;
pChainInfo->cbCert = cbCert;
return pChainInfo;
} else {
while (cCert--)
CertFreeCertificateContext(pChainInfo->rgpCert[cCert]);
ACFree(pChainInfo);
return NULL;
}
}
//+-------------------------------------------------------------------------
// Check if the certificate has key provider information.
// If dwKeySpec != 0, also check that the provider's public key matches the
// public key in the certificate.
//--------------------------------------------------------------------------
static BOOL CheckKeyProvInfo(
IN PCCERT_CONTEXT pCert,
IN DWORD dwKeySpec,
OUT DWORD *pcbKeyProvInfo
)
{
BOOL fResult = FALSE;
HCRYPTPROV hCryptProv = 0;
PCRYPT_KEY_PROV_INFO pKeyProvInfo = NULL;
DWORD cbKeyProvInfo;
DWORD cbData;
PCERT_PUBLIC_KEY_INFO pPubKeyInfo = NULL;
DWORD cbPubKeyInfo;
cbKeyProvInfo = 0;
CertGetCertificateContextProperty(
pCert,
CERT_KEY_PROV_INFO_PROP_ID,
NULL, // pvData
&cbKeyProvInfo
);
if (cbKeyProvInfo) {
if (dwKeySpec == 0)
fResult = TRUE;
else {
DWORD dwIdx;
if (NULL == (pKeyProvInfo = (PCRYPT_KEY_PROV_INFO) ACAlloc(cbKeyProvInfo)))
goto CommonReturn;
if (!CertGetCertificateContextProperty(
pCert,
CERT_KEY_PROV_INFO_PROP_ID,
pKeyProvInfo,
&cbKeyProvInfo
)) goto CommonReturn;
if (!CryptAcquireContextU(
&hCryptProv,
pKeyProvInfo->pwszContainerName,
pKeyProvInfo->pwszProvName,
pKeyProvInfo->dwProvType,
pKeyProvInfo->dwFlags & ~CERT_SET_KEY_PROV_HANDLE_PROP_ID
)) {
hCryptProv = NULL;
goto CommonReturn;
}
for (dwIdx = 0; dwIdx < pKeyProvInfo->cProvParam; dwIdx++) {
PCRYPT_KEY_PROV_PARAM pKeyProvParam =
&pKeyProvInfo->rgProvParam[dwIdx];
if (!CryptSetProvParam(
hCryptProv,
pKeyProvParam->dwParam,
pKeyProvParam->pbData,
pKeyProvParam->dwFlags
)) goto CommonReturn;
}
// Get public key to compare certificate with
cbPubKeyInfo = 0;
CryptExportPublicKeyInfo(
hCryptProv,
dwKeySpec,
pCert->dwCertEncodingType,
NULL, // pPubKeyInfo
&cbPubKeyInfo
);
if (cbPubKeyInfo == 0) goto CommonReturn;
if (NULL == (pPubKeyInfo = (PCERT_PUBLIC_KEY_INFO) ACAlloc(
cbPubKeyInfo)))
goto CommonReturn;
if (!CryptExportPublicKeyInfo(
hCryptProv,
dwKeySpec,
pCert->dwCertEncodingType,
pPubKeyInfo,
&cbPubKeyInfo
)) goto CommonReturn;
fResult = CertComparePublicKeyInfo(
pCert->dwCertEncodingType,
&pCert->pCertInfo->SubjectPublicKeyInfo,
pPubKeyInfo);
}
}
CommonReturn:
if (hCryptProv) {
DWORD dwErr = GetLastError();
CryptReleaseContext(hCryptProv, 0);
SetLastError(dwErr);
}
if (pKeyProvInfo)
ACFree(pKeyProvInfo);
if (pPubKeyInfo)
ACFree(pPubKeyInfo);
*pcbKeyProvInfo = cbKeyProvInfo;
return fResult;
}
//+-------------------------------------------------------------------------
// Find all certificate chains tying the given issuer name to any certificate
// that the current user has a private key for.
//
// If pbEncodedIssuerName == NULL || cbEncodedIssuerName = 0, match any
// issuer.
//--------------------------------------------------------------------------
HRESULT
WINAPI
FindCertsByIssuer(
OUT PCERT_CHAIN pCertChains,
IN OUT DWORD *pcbCertChains,
OUT DWORD *pcCertChains, // count of certificates chains returned
IN BYTE* pbEncodedIssuerName, // DER encoded issuer name
IN DWORD cbEncodedIssuerName, // count in bytes of encoded issuer name
IN LPCWSTR pwszPurpose, // "ClientAuth" or "CodeSigning"
IN DWORD dwKeySpec // only return signers supporting this
// keyspec
)
{
HRESULT hr;
HCERTSTORE *phMyStoreList = NULL;
HCERTSTORE *phCaStoreList = NULL;
HCERTSTORE *phStore;
HCERTSTORE hStore;
DWORD cChain = 0;
DWORD cbChain;
DWORD cTotalCert = 0;
PCHAIN_INFO pChainInfoHead = NULL;
LONG cbExtra = 0;
// get the certs out of the IE30 tree and put it in ours
// open the IE30 store
if (NULL != (hStore = CertOpenSystemStore(
NULL,
IE30CONVERTEDSTORE))) {
// don't care about errors, and we don't
// want to delete the old store just yet.
GetAndIe30ClientAuthCertificates(hStore);
CertCloseStore(hStore, 0);
}
// copy the IE30 certs
if (NULL == (phMyStoreList = GetMyStoreList()))
goto ErrorReturn;
if (NULL == (phCaStoreList = GetCaStoreList()))
goto ErrorReturn;
// Iterate through all "My" cert stores to find certificates having a
// CRYPT_KEY_PROV_INFO property
phStore = phMyStoreList;
while (hStore = *phStore++) {
PCCERT_CONTEXT pCert = NULL;
while (pCert = CertEnumCertificatesInStore(hStore, pCert)) {
DWORD cbKeyProvInfo;
if (CheckKeyProvInfo(pCert, dwKeySpec, &cbKeyProvInfo)) {
// Create a cert chain and check for an issuer name match
// of any cert in the chain.
PCHAIN_INFO pChainInfo;
if (pChainInfo = CreateChainInfo(
pCert,
pbEncodedIssuerName,
cbEncodedIssuerName,
phCaStoreList,
phMyStoreList
)) {
// Add to list of chains
pChainInfo->pNext = pChainInfoHead;
pChainInfoHead = pChainInfo;
// Update bytes needed for KeyProvInfo
pChainInfo->cbKeyProvInfo = ALIGN_LEN(cbKeyProvInfo);
// Update totals
cbExtra += pChainInfo->cbKeyProvInfo + pChainInfo->cbCert;
cChain++;
cTotalCert += pChainInfo->cCert;
}
}
}
}
cbChain = sizeof(CERT_CHAIN) * cChain +
sizeof(CERT_BLOB) * cTotalCert + cbExtra;
{
// Check and update output lengths and counts
DWORD cbIn;
if (cChain == 0) {
hr = CRYPT_E_NOT_FOUND;
goto HrError;
}
if (pCertChains == NULL)
*pcbCertChains = 0;
cbIn = *pcbCertChains;
*pcCertChains = cChain;
*pcbCertChains = cbChain;
if (cbIn == 0) {
hr = S_OK;
goto CommonReturn;
} else if (cbIn < cbChain) {
hr = HRESULT_FROM_WIN32(ERROR_BAD_LENGTH);
goto CommonReturn;
}
}
{
// Copy cert chains to output
PCERT_CHAIN pOutChain;
PCERT_BLOB pCertBlob;
BYTE *pbExtra;
PCHAIN_INFO pChainInfo;
pOutChain = pCertChains;
pCertBlob = (PCERT_BLOB) (((BYTE *) pOutChain) +
sizeof(CERT_CHAIN) * cChain);
pbExtra = ((BYTE *) pCertBlob) + sizeof(CERT_BLOB) * cTotalCert;
pChainInfo = pChainInfoHead;
for ( ; pChainInfo != NULL;
pChainInfo = pChainInfo->pNext, pOutChain++) {
DWORD cb;
DWORD cCert = pChainInfo->cCert;
PCCERT_CONTEXT *ppCert = pChainInfo->rgpCert;
pOutChain->cCerts = cCert;
pOutChain->certs = pCertBlob;
cb = pChainInfo->cbKeyProvInfo;
cbExtra -= cb;
assert(cbExtra >= 0);
if (cbExtra < 0) goto UnexpectedError;
if (!CertGetCertificateContextProperty(
*ppCert,
CERT_KEY_PROV_INFO_PROP_ID,
pbExtra,
&cb
))
goto UnexpectedError;
pOutChain->keyLocatorInfo = * ((PCRYPT_KEY_PROV_INFO) pbExtra);
pbExtra += pChainInfo->cbKeyProvInfo;
for ( ; cCert > 0; cCert--, ppCert++, pCertBlob++) {
cb = (*ppCert)->cbCertEncoded;
cbExtra -= ALIGN_LEN(cb);
assert(cbExtra >= 0);
if (cbExtra < 0) goto UnexpectedError;
pCertBlob->cbData = cb;
pCertBlob->pbData = pbExtra;
memcpy(pbExtra, (*ppCert)->pbCertEncoded, cb);
pbExtra += ALIGN_LEN(cb);
}
}
assert(cbExtra == 0);
assert(pCertBlob == (PCERT_BLOB) ((BYTE *) pCertChains +
sizeof(CERT_CHAIN) * cChain +
sizeof(CERT_BLOB) * cTotalCert));
}
hr = S_OK;
goto CommonReturn;
UnexpectedError:
hr = E_UNEXPECTED;
goto HrError;
ErrorReturn:
hr = SignError();
HrError:
*pcbCertChains = 0;
*pcCertChains = 0;
CommonReturn:
while (pChainInfoHead) {
PCHAIN_INFO pChainInfo = pChainInfoHead;
DWORD cCert = pChainInfo->cCert;
while (cCert--)
CertFreeCertificateContext(pChainInfo->rgpCert[cCert]);
pChainInfoHead = pChainInfo->pNext;
ACFree(pChainInfo);
}
if (phMyStoreList) {
phStore = phMyStoreList;
while (hStore = *phStore++)
CertCloseStore(hStore, 0);
ACFree(phMyStoreList);
}
if (phCaStoreList) {
phStore = phCaStoreList;
while (hStore = *phStore++)
CertCloseStore(hStore, 0);
ACFree(phCaStoreList);
}
return hr;
}