|
|
//+-------------------------------------------------------------------------
//
// 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; }
|