windows-nt/Source/XPSP1/NT/ds/security/services/ca/certlib/certpick.cpp

1431 lines
31 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
//+--------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996 - 1999
//
// File: cryptpick.cpp
//
// Contents: Cert Server wrapper routines
//
//---------------------------------------------------------------------------
#include <pch.cpp>
#pragma hdrstop
#include "cainfop.h"
#include "csdisp.h"
#include "csldap.h"
#include "tfc.h"
#include "clibres.h"
#define CUCS_SOURCEMASK (CUCS_MACHINESTORE | CUCS_USERSTORE | CUCS_DSSTORE)
#define CUCS_TYPEMASK (CUCS_MYSTORE | \
CUCS_CASTORE | \
CUCS_KRASTORE | \
CUCS_ROOTSTORE)
#define CUCS_VALIDMASK (CUCS_SOURCEMASK | \
CUCS_TYPEMASK | \
CUCS_ARCHIVED | \
CUCS_USAGEREQUIRED | \
CUCS_SILENT | \
CUCS_PRIVATEKEYREQUIRED)
// My, CA, KRA and Root stores -- in HKLM, HKCU and the DS
#define CDISPLAYSTOREMAX (4 * 3)
typedef struct _STOREMAP {
DWORD dwFlags;
WCHAR const *pwszStoreName;
WCHAR const *pwszDSTemplate;
} STOREMAP;
STOREMAP s_aStoreMap[] = {
{ CUCS_MYSTORE, wszMY_CERTSTORE, NULL },
{ CUCS_CASTORE, wszCA_CERTSTORE, wszDSAIAQUERYTEMPLATE },
{ CUCS_KRASTORE, wszKRA_CERTSTORE, wszDSKRAQUERYTEMPLATE },
{ CUCS_ROOTSTORE, wszROOT_CERTSTORE, NULL },
{ 0x0, NULL, NULL },
};
HRESULT
FormatDSStoreName(
IN OUT BSTR *pstrDomainDN,
IN OUT BSTR *pstrConfigDN,
IN WCHAR const *pwszTemplate,
OUT WCHAR **ppwszOut)
{
HRESULT hr;
LDAP *pld = NULL;
if (NULL == *pstrConfigDN) // first call
{
hr = myRobustLdapBind(&pld, FALSE);
_JumpIfError(hr, error, "myRobustLdapBind");
// Renewal domain and config containers (%5, %6)
hr = myGetAuthoritativeDomainDn(pld, pstrDomainDN, pstrConfigDN);
_JumpIfError(hr, error, "myGetAuthoritativeDomainDn");
}
hr = myFormatCertsrvStringArray(
FALSE, // fURL
L"", // pwszServerName_p1_2
L"", // pwszSanitizedName_p3_7
0, // iCert_p4
*pstrDomainDN, // pwszDomainDN_p5
*pstrConfigDN, // pwszConfigDN_p6
0, // iCRL_p8
FALSE, // fDeltaCRL_p9
FALSE, // fDSAttrib_p10_11
1, // cStrings
(LPCWSTR *) &pwszTemplate, // apwszStringsIn
ppwszOut); // apwszStringsOut
_JumpIfError(hr, error, "myFormatCertsrvStringArray");
error:
if (NULL != pld)
{
ldap_unbind(pld);
}
return(hr);
}
HRESULT
myOpenCertStores(
IN DWORD dwFlags, // CUCS_*
OUT DWORD *pcStore,
OUT HCERTSTORE **prghStore)
{
HRESULT hr;
HRESULT hr2;
HCERTSTORE *rghStore = NULL;
DWORD cStore = 0;
STOREMAP *psm;
DWORD OpenFlags;
BSTR strDomainDN = NULL;
BSTR strConfigDN = NULL;
WCHAR *pwszDSStore = NULL;
*pcStore = 0;
*prghStore = NULL;
rghStore = (HCERTSTORE *) LocalAlloc(
LMEM_FIXED,
CDISPLAYSTOREMAX * sizeof(HCERTSTORE));
if (NULL == rghStore)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc(rghStore)");
}
OpenFlags = CERT_STORE_READONLY_FLAG | CERT_STORE_SET_LOCALIZED_NAME_FLAG;
if (CUCS_ARCHIVED & dwFlags)
{
OpenFlags |= CERT_STORE_ENUM_ARCHIVED_FLAG;
}
hr2 = S_OK;
if ((CUCS_SOURCEMASK | CUCS_TYPEMASK) & dwFlags)
{
if (~CUCS_VALIDMASK & dwFlags)
{
hr = E_INVALIDARG;
_JumpError(hr, error, "dwFlags");
}
if (0 == (CUCS_SOURCEMASK & dwFlags))
{
dwFlags |= CUCS_SOURCEMASK; // source default: all cert store sources
}
if (0 == (CUCS_TYPEMASK & dwFlags))
{
dwFlags |= CUCS_MYSTORE; // type default: use my store
}
for (psm = s_aStoreMap; NULL != psm->pwszStoreName; psm++)
{
if (dwFlags & psm->dwFlags)
{
HCERTSTORE hStore;
if (CUCS_MACHINESTORE & dwFlags)
{
hStore = CertOpenStore(
CERT_STORE_PROV_SYSTEM_W,
X509_ASN_ENCODING,
NULL,
CERT_SYSTEM_STORE_LOCAL_MACHINE | OpenFlags,
psm->pwszStoreName);
if (NULL == hStore)
{
hr = myHLastError();
_PrintErrorStr(hr, "CertOpenStore LocalMachine", psm->pwszStoreName);
if (S_OK == hr2)
{
hr2 = hr;
}
}
else
{
DBGPRINT((
DBG_SS_CERTLIBI,
"CertOpenStore LocalMachine(%ws, HKLM)[%u]\n",
psm->pwszStoreName,
cStore));
rghStore[cStore++] = hStore;
}
}
if (CUCS_USERSTORE & dwFlags)
{
hStore = CertOpenStore(
CERT_STORE_PROV_SYSTEM_W,
X509_ASN_ENCODING,
NULL,
CERT_SYSTEM_STORE_CURRENT_USER | OpenFlags,
psm->pwszStoreName);
if (NULL == hStore)
{
hr = myHLastError();
_PrintErrorStr(hr, "CertOpenStore User", psm->pwszStoreName);
if (S_OK == hr2)
{
hr2 = hr;
}
}
else
{
DBGPRINT((
DBG_SS_CERTLIBI,
"CertOpenStore User(%ws, HKCU)[%u]\n",
psm->pwszStoreName,
cStore));
rghStore[cStore++] = hStore;
}
}
if ((CUCS_DSSTORE & dwFlags) && NULL != psm->pwszDSTemplate)
{
CString strStoreFriendlyName;
int idResource;
switch(psm->dwFlags)
{
case CUCS_CASTORE:
idResource = IDS_STORENAME_DS_AIA;
break;
case CUCS_KRASTORE:
idResource = IDS_STORENAME_DS_KRA;
break;
default:
CSASSERT(("Invalid cert store name"));
}
strStoreFriendlyName.LoadString(idResource);
if (NULL != pwszDSStore)
{
LocalFree(pwszDSStore);
pwszDSStore = NULL;
}
hr = FormatDSStoreName(
&strDomainDN,
&strConfigDN,
psm->pwszDSTemplate,
&pwszDSStore);
if (S_OK != hr)
{
_PrintError(hr, "FormatDSStoreName");
}
else
{
hStore = CertOpenStore(
CERT_STORE_PROV_LDAP_W,
X509_ASN_ENCODING,
NULL,
OpenFlags,
pwszDSStore);
if (NULL == hStore)
{
hr = myHLastError();
_PrintErrorStr(hr, "CertOpenStore LDAP", pwszDSStore);
if (S_OK == hr2)
{
hr2 = hr;
}
}
else
{
CRYPT_DATA_BLOB cdb;
cdb.pbData = (BYTE*)strStoreFriendlyName.GetBuffer();
cdb.cbData = sizeof(WCHAR)*
(wcslen(strStoreFriendlyName.GetBuffer())+1);
if (!CertSetStoreProperty(
hStore,
CERT_STORE_LOCALIZED_NAME_PROP_ID,
0,
(const void *) &cdb))
{
hr = myHLastError();
_PrintErrorStr(hr, "CertSetStoreProp LDAP", pwszDSStore);
hr = S_OK;
}
DBGPRINT((
DBG_SS_CERTLIBI,
"CertOpenStore LDAP(%ws)[%u]\n",
pwszDSStore,
cStore));
rghStore[cStore++] = hStore;
}
}
}
}
}
}
hr = hr2;
if (0 == cStore)
{
if (hr == S_OK)
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
_JumpError(hr, error, "no Cert Store");
}
CSASSERT(CDISPLAYSTOREMAX >= cStore);
*pcStore = cStore;
*prghStore = rghStore;
rghStore = NULL;
// successfully opened some stores
hr = S_OK;
error:
if (NULL != strDomainDN)
{
SysFreeString(strDomainDN);
}
if (NULL != strConfigDN)
{
SysFreeString(strConfigDN);
}
if (NULL != pwszDSStore)
{
LocalFree(pwszDSStore);
}
if (NULL != rghStore)
{
myCloseCertStores(cStore, rghStore);
}
return(hr);
}
VOID
myCloseCertStores(
IN DWORD cStore,
IN HCERTSTORE *rghStore)
{
DWORD i;
if (NULL != rghStore)
{
for (i = cStore; i < cStore; i++)
{
if (NULL != rghStore[i])
{
CertCloseStore(rghStore[i], CERT_CLOSE_STORE_CHECK_FLAG);
}
}
LocalFree(rghStore);
}
}
// Search for and load the cryptographic provider and private key.
HRESULT
myLoadPrivateKey(
IN CERT_PUBLIC_KEY_INFO const *pPubKeyInfo,
IN DWORD dwFlags, // CUCS_*
OUT HCRYPTPROV *phProv,
OUT DWORD *pdwKeySpec,
OUT BOOL *pfCallerFreeProv)
{
HRESULT hr;
HCERTSTORE *rghStore = NULL;
DWORD cStore;
hr = myOpenCertStores(dwFlags, &cStore, &rghStore);
_JumpIfError(hr, error, "myOpenCertStores");
hr = myLoadPrivateKeyFromCertStores(
pPubKeyInfo,
cStore,
rghStore,
phProv,
pdwKeySpec,
pfCallerFreeProv);
_JumpIfError(hr, error, "myLoadPrivateKeyFromCertStores");
error:
if (NULL != rghStore)
{
myCloseCertStores(cStore, rghStore);
}
return(hr);
}
HRESULT
myLoadPrivateKeyFromCertStores(
IN CERT_PUBLIC_KEY_INFO const *pPubKeyInfo,
IN DWORD cStore,
IN HCERTSTORE *rghStore,
OUT HCRYPTPROV *phProv,
OUT DWORD *pdwKeySpec,
OUT BOOL *pfCallerFreeProv)
{
HRESULT hr;
DWORD i;
CERT_CONTEXT const *pcc = NULL;
// for each cert store
// for each cert in store with matching public key (lookup by public key)
// call CryptAcquireCertificatePrivateKey
// if succeeds, exit w/S_OK
hr = S_OK;
for (i = 0; i < cStore; i++)
{
HCERTSTORE hStore = rghStore[i];
while (TRUE)
{
pcc = CertFindCertificateInStore(
hStore,
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
0,
CERT_FIND_PUBLIC_KEY,
pPubKeyInfo,
pcc);
if (NULL == pcc)
{
break;
}
if (!CryptAcquireCertificatePrivateKey(
pcc,
CRYPT_ACQUIRE_COMPARE_KEY_FLAG,
NULL, // pvReserved
phProv,
pdwKeySpec,
pfCallerFreeProv))
{
hr = myHLastError();
_PrintError(hr, "CryptAcquireCertificatePrivateKey");
continue;
}
hr = S_OK;
goto error;
}
}
if (S_OK == hr)
{
hr = CRYPT_E_NOT_FOUND;
}
_JumpError(hr, error, "CertFindCertificateInStore");
error:
if (NULL != pcc)
{
CertFreeCertificateContext(pcc);
pcc = NULL;
}
return(hr);
}
HRESULT
myMakeSerialBstr(
IN WCHAR const *pwszSerialNumber,
OUT BSTR *pstrSerialNumber)
{
HRESULT hr = E_OUTOFMEMORY;
WCHAR *pwszDup = NULL;
WCHAR const *pwszSrc;
WCHAR *pwszDst;
pwszDup = (WCHAR *) LocalAlloc(
LMEM_FIXED,
(wcslen(pwszSerialNumber) + 2) * sizeof(*pwszDup));
if (NULL == pwszDup)
{
_JumpError(hr, error, "LocalAlloc");
}
pwszSrc = pwszSerialNumber;
*pwszDup = L'0'; // allow possible leading zero
pwszDst = &pwszDup[1];
while (L'\0' != *pwszSrc)
{
WCHAR wc = *pwszSrc++;
if (iswspace(wc))
{
continue;
}
if (L'A' <= wc && L'F' >= wc)
{
wc += L'a' - L'A';
}
if ((L'a' > wc || L'f' < wc) &&
(L'0' > wc || L'9' < wc))
{
hr = E_INVALIDARG;
_JumpErrorStr2(hr, error, "myMakeSerialBstr", pwszSerialNumber, hr);
}
*pwszDst++ = wc;
}
*pwszDst = L'\0';
pwszSrc = pwszDup; // point at possible extra leading zero
if (1 & (pwszDst - pwszSrc))
{
pwszSrc++; // keep the length even
}
while (L'0' == pwszSrc[0] && L'0' == pwszSrc[1] && L'\0' != pwszSrc[2])
{
pwszSrc += 2; // skip pairs of leading zeros
}
if (!myConvertWszToBstr(pstrSerialNumber, pwszSrc, MAXDWORD))
{
_JumpError(hr, error, "myConvertWszToBstr");
}
hr = S_OK;
error:
if (NULL != pwszDup)
{
LocalFree(pwszDup);
}
return(hr);
}
HRESULT
myNameBlobMatch(
IN CERT_NAME_BLOB const *pSubject,
IN WCHAR const *pwszCertName,
IN BOOL fAllowMissingCN,
OUT BOOL *pfMatch)
{
HRESULT hr;
BOOL fFoundCN = FALSE;
BOOL fMatchCN = FALSE;
CERT_NAME_INFO *pNameInfo = NULL;
DWORD cbNameInfo;
DWORD i;
if (!myDecodeName(
X509_ASN_ENCODING,
X509_UNICODE_NAME,
pSubject->pbData,
pSubject->cbData,
CERTLIB_USE_LOCALALLOC,
&pNameInfo,
&cbNameInfo))
{
hr = myHLastError();
_JumpError(hr, error, "myDecodeName");
}
for (i = 0; i < pNameInfo->cRDN; i++)
{
CERT_RDN const *prdn;
DWORD j;
prdn = &pNameInfo->rgRDN[i];
for (j = 0; j < prdn->cRDNAttr; j++)
{
CERT_RDN_ATTR const *prdna;
prdna = &prdn->rgRDNAttr[j];
if (0 == strcmp(szOID_COMMON_NAME, prdna->pszObjId) &&
NULL != prdna->Value.pbData)
{
fFoundCN = TRUE;
if (0 == lstrcmpi(
pwszCertName,
(WCHAR const *) prdna->Value.pbData))
{
fMatchCN = TRUE;
break;
}
}
}
}
hr = S_OK;
error:
if (NULL != pNameInfo)
{
LocalFree(pNameInfo);
}
*pfMatch = fMatchCN || (fAllowMissingCN && !fFoundCN);
return(hr);
}
HRESULT
mySerialNumberMatch(
IN CRYPT_INTEGER_BLOB const *pSerialNumber,
IN WCHAR const *pwszSerialNumber,
OUT BOOL *pfMatch)
{
HRESULT hr;
BSTR strSerialNumber = NULL;
BOOL fMatch = FALSE;
hr = MultiByteIntegerToBstr(
FALSE,
pSerialNumber->cbData,
pSerialNumber->pbData,
&strSerialNumber);
_JumpIfError(hr, error, "MultiByteIntegerToBstr");
if (0 == lstrcmpi(pwszSerialNumber, strSerialNumber))
{
fMatch = TRUE;
}
CSASSERT(S_OK == hr);
error:
if (NULL != strSerialNumber)
{
SysFreeString(strSerialNumber);
}
*pfMatch = fMatch;
return(hr);
}
HRESULT
myCertHashMatch(
IN CERT_CONTEXT const *pCert,
IN DWORD cb,
IN BYTE const *pb,
OUT BOOL *pfMatch)
{
HRESULT hr;
BOOL fMatch = FALSE;
BYTE abHash[CBMAX_CRYPT_HASH_LEN];
DWORD cbHash;
//wprintf(L"IN: ");
//DumpHex(DH_NOADDRESS | DH_NOTABPREFIX | DH_NOASCIIHEX | 1, pb, cb);
cbHash = sizeof(abHash);
if (!CertGetCertificateContextProperty(
pCert,
CERT_SHA1_HASH_PROP_ID,
abHash,
&cbHash))
{
hr = myHLastError();
_JumpError(hr, error, "CertGetCertificateContextProperty");
}
//wprintf(L"CH: ");
//DumpHex(DH_NOADDRESS | DH_NOTABPREFIX | DH_NOASCIIHEX | 1, abHash, cbHash);
if (cbHash == cb && 0 == memcmp(abHash, pb, cb))
{
fMatch = TRUE;
hr = S_OK;
goto error;
}
cbHash = sizeof(abHash);
if (!CertGetCertificateContextProperty(
pCert,
CERT_KEY_IDENTIFIER_PROP_ID,
abHash,
&cbHash))
{
hr = myHLastError();
_PrintError(hr, "CertGetCertificateContextProperty(KeyId)");
}
else
{
//wprintf(L"KH: ");
//DumpHex(DH_NOADDRESS | DH_NOTABPREFIX | DH_NOASCIIHEX | 1, abHash, cbHash);
if (cbHash == cb && 0 == memcmp(abHash, pb, cb))
{
fMatch = TRUE;
hr = S_OK;
goto error;
}
}
hr = S_OK;
error:
*pfMatch = fMatch;
return(hr);
}
HRESULT
myCertMatch(
IN CERT_CONTEXT const *pCert,
IN WCHAR const *pwszCertName,
IN BOOL fAllowMissingCN,
OPTIONAL IN BYTE const *pbHash,
IN DWORD cbHash,
OPTIONAL IN WCHAR const *pwszSerialNumber,
OUT BOOL *pfMatch)
{
HRESULT hr;
BOOL fMatch = FALSE;
if (NULL != pbHash)
{
hr = myCertHashMatch(pCert, cbHash, pbHash, &fMatch);
_JumpIfError(hr, error, "myCertHashMatch");
}
if (!fMatch && NULL != pwszSerialNumber)
{
hr = mySerialNumberMatch(
&pCert->pCertInfo->SerialNumber,
pwszSerialNumber,
&fMatch);
_JumpIfError(hr, error, "mySerialNumberMatch");
}
if (!fMatch)
{
hr = myNameBlobMatch(
&pCert->pCertInfo->Subject,
pwszCertName,
fAllowMissingCN,
&fMatch);
_JumpIfError(hr, error, "myNameBlobMatch");
}
hr = S_OK;
error:
*pfMatch = fMatch;
return(hr);
}
HRESULT
myCertMatchEKUOrApplicationPolicies(
IN CERT_CONTEXT const *pCert,
IN DWORD cpszObjId,
IN CHAR const * const *apszObjId,
IN BOOL fUsageRequired,
OUT BOOL *pfMatch)
{
HRESULT hr;
CERT_ENHKEY_USAGE *pKeyUsage = NULL;
DWORD cbKeyUsage = 0;
BOOL fMatch = FALSE;
DWORD i;
DWORD j;
while (TRUE)
{
// get enhanced key usage OIDs
if (!CertGetEnhancedKeyUsage(
pCert,
CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG,
pKeyUsage,
&cbKeyUsage))
{
// accept match if EKU extension not found
hr = myHLastError();
if (!fUsageRequired && CRYPT_E_NOT_FOUND == hr)
{
fMatch = TRUE;
}
_PrintError2(hr, "CertGetEnhancedKeyUsage", CRYPT_E_NOT_FOUND);
hr = S_OK;
goto error;
}
if (NULL != pKeyUsage)
{
break; // EKU extension fetched; break out of while loop
}
pKeyUsage = (CERT_ENHKEY_USAGE *) LocalAlloc(LMEM_FIXED, cbKeyUsage);
if (NULL == pKeyUsage)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "out of memory");
}
}
if (NULL != pKeyUsage)
{
if (0 == pKeyUsage->cUsageIdentifier)
{
hr = myHLastError(); // set by CertGetEnhancedKeyUsage
if (S_OK != hr)
{
fMatch = TRUE;
}
}
else
{
for (i = 0; i < pKeyUsage->cUsageIdentifier; i++)
{
if (fMatch)
{
break;
}
for (j = 0; j < cpszObjId; j++)
{
if (0 == strcmp(
pKeyUsage->rgpszUsageIdentifier[i],
apszObjId[j]))
{
fMatch = TRUE; // found matching EKU OID
break;
}
}
}
}
}
hr = S_OK;
error:
*pfMatch = fMatch;
if (NULL != pKeyUsage)
{
LocalFree(pKeyUsage);
}
return(hr);
}
HRESULT
myCRLHashMatch(
IN CRL_CONTEXT const *pCRL,
IN DWORD cb,
IN BYTE const *pb,
OUT BOOL *pfMatch)
{
HRESULT hr;
BOOL fMatch = FALSE;
BYTE abHash[CBMAX_CRYPT_HASH_LEN];
DWORD cbHash;
cbHash = sizeof(abHash);
if (!CertGetCRLContextProperty(
pCRL,
CERT_SHA1_HASH_PROP_ID,
abHash,
&cbHash))
{
hr = myHLastError();
_JumpError(hr, error, "CertGetCRLContextProperty");
}
if (cbHash == cb && 0 == memcmp(abHash, pb, cb))
{
fMatch = TRUE;
}
hr = S_OK;
error:
*pfMatch = fMatch;
return(hr);
}
HRESULT
myCRLMatch(
IN CRL_CONTEXT const *pCRL,
IN WCHAR const *pwszCRLName,
IN BOOL fAllowMissingCN,
OPTIONAL IN BYTE const *pbHash,
IN DWORD cbHash,
OUT BOOL *pfMatch)
{
HRESULT hr;
BOOL fMatch = FALSE;
if (NULL != pbHash)
{
hr = myCRLHashMatch(pCRL, cbHash, pbHash, &fMatch);
_JumpIfError(hr, error, "myCRLHashMatch");
}
if (!fMatch)
{
hr = myNameBlobMatch(
&pCRL->pCrlInfo->Issuer,
pwszCRLName,
fAllowMissingCN,
&fMatch);
_JumpIfError(hr, error, "myNameBlobMatch");
}
hr = S_OK;
error:
*pfMatch = fMatch;
return(hr);
}
HRESULT
myCTLHashMatch(
IN CTL_CONTEXT const *pCTL,
IN DWORD cb,
IN BYTE const *pb,
OUT BOOL *pfMatch)
{
HRESULT hr;
BOOL fMatch = FALSE;
BYTE abHash[CBMAX_CRYPT_HASH_LEN];
DWORD cbHash;
cbHash = sizeof(abHash);
if (!CertGetCTLContextProperty(
pCTL,
CERT_SHA1_HASH_PROP_ID,
abHash,
&cbHash))
{
hr = myHLastError();
_JumpError(hr, error, "CertGetCTLContextProperty");
}
if (cbHash == cb && 0 == memcmp(abHash, pb, cb))
{
fMatch = TRUE;
}
hr = S_OK;
error:
*pfMatch = fMatch;
return(hr);
}
HRESULT
myCTLMatch(
IN CTL_CONTEXT const *pCTL,
OPTIONAL IN BYTE const *pbHash,
IN DWORD cbHash,
OUT BOOL *pfMatch)
{
HRESULT hr;
BOOL fMatch = FALSE;
if (NULL != pbHash)
{
hr = myCTLHashMatch(pCTL, cbHash, pbHash, &fMatch);
_JumpIfError(hr, error, "myCTLHashMatch");
}
hr = S_OK;
error:
*pfMatch = fMatch;
return(hr);
}
typedef struct _CERTFILTERCALLBACKDATA
{
DWORD dwFlags; // CUCS_*
DWORD cpszObjId;
CHAR const * const *apszObjId;
WCHAR const *pwszCommonName;
BYTE *pbHash;
DWORD cbHash;
BSTR strSerialNumber;
HRESULT hr;
HCERTSTORE hMemStore;
} CERTFILTERCALLBACKDATA;
BOOL WINAPI
CertificateFilterProc(
PCCERT_CONTEXT pCertContext,
BOOL *pfInitialSelectedCert,
void *pvCallbackData)
{
HRESULT hr;
BOOL fMatch;
CERTFILTERCALLBACKDATA *pCallbackData =
(CERTFILTERCALLBACKDATA *) pvCallbackData;
CERT_NAME_INFO *pNameInfo = NULL;
DWORD cbNameInfo;
DWORD i;
WCHAR *pwszSubject = NULL;
CSASSERT(NULL != pCertContext);
*pfInitialSelectedCert = FALSE;
hr = myCertNameToStr(
X509_ASN_ENCODING,
&pCertContext->pCertInfo->Subject,
CERT_X500_NAME_STR |
CERT_NAME_STR_REVERSE_FLAG |
CERT_NAME_STR_NO_QUOTING_FLAG,
&pwszSubject);
_PrintIfError(hr, "myCertNameToStr");
if (NULL != pCallbackData->apszObjId)
{
hr = myCertMatchEKUOrApplicationPolicies(
pCertContext,
pCallbackData->cpszObjId,
pCallbackData->apszObjId,
CUCS_USAGEREQUIRED & pCallbackData->dwFlags,
&fMatch);
_JumpIfError(hr, error, "myCertMatchEKUOrApplicationPolicies");
if (!fMatch)
{
_PrintErrorStr(CRYPT_E_NOT_FOUND, "no matching EKU", pwszSubject);
hr = S_OK;
goto error;
}
}
// The CommonName filter serves three purposes: 1: a common name, 2: the
// sha-1 hash of the cert, 3: the sha-1 hash of the public key (or the
// Subject Key Id extension), 4: the serial number of the cert. If any one
// of these match, the cert is accepted.
if (NULL != pCallbackData->pwszCommonName)
{
hr = myCertMatch(
pCertContext,
pCallbackData->pwszCommonName,
FALSE, // fAllowMissingCN
pCallbackData->pbHash,
pCallbackData->cbHash,
pCallbackData->strSerialNumber,
&fMatch);
_JumpIfError(hr, error, "myCertMatch");
if (!fMatch)
{
_PrintErrorStr2(
CRYPT_E_NOT_FOUND,
"no matching CN/Hash/Serial",
pwszSubject,
CRYPT_E_NOT_FOUND);
hr = S_OK;
goto error;
}
}
if (CUCS_PRIVATEKEYREQUIRED & pCallbackData->dwFlags)
{
DWORD cb;
if (!CertGetCertificateContextProperty(
pCertContext,
CERT_KEY_PROV_INFO_PROP_ID,
NULL,
&cb))
{
_PrintErrorStr(CRYPT_E_NOT_FOUND, "no KeyProvInfo", pwszSubject);
fMatch = FALSE;
hr = S_OK;
goto error;
}
}
fMatch = TRUE;
// Attempt to add to the temporary store; if it fails then it already
// exists so we don't want it displayed.
if (!CertAddCertificateLinkToStore(
pCallbackData->hMemStore,
pCertContext,
CERT_STORE_ADD_NEW,
NULL))
{
fMatch = FALSE;
hr = myHLastError();
_PrintErrorStr(hr, "CertAddCertificateLinkToStore Dup", pwszSubject);
hr = S_OK;
goto error;
}
hr = S_OK;
error:
if (S_OK == pCallbackData->hr || (fMatch && S_FALSE == pCallbackData->hr))
{
pCallbackData->hr = hr; // keep the first HRESULT
}
if (NULL != pwszSubject)
{
LocalFree(pwszSubject);
}
if (NULL != pNameInfo)
{
LocalFree(pNameInfo);
}
return(fMatch);
}
HRESULT
myGetKRACertificateFromPicker(
OPTIONAL IN HINSTANCE hInstance,
OPTIONAL IN HWND hwndParent,
OPTIONAL IN int idTitle,
OPTIONAL IN int idSubTitle,
OPTIONAL IN WCHAR const *pwszCommonName,
IN BOOL fUseDS,
IN BOOL fSilent,
OUT CERT_CONTEXT const **ppCert)
{
HRESULT hr;
CHAR const *pszObjId = szOID_KP_KEY_RECOVERY_AGENT;
hr = myGetCertificateFromPicker(
hInstance,
hwndParent,
idTitle,
idSubTitle,
CUCS_MYSTORE | CUCS_KRASTORE | CUCS_CASTORE |
CUCS_MACHINESTORE | CUCS_USERSTORE |
CUCS_USAGEREQUIRED |
(fUseDS? CUCS_DSSTORE : 0) |
(fSilent? CUCS_SILENT : 0),
pwszCommonName,
0, // cStore
NULL, // rghStore
1, // cpszObjId
&pszObjId,
ppCert);
_JumpIfError(hr, error, "myGetCertificateFromPicker");
error:
return(hr);
}
HRESULT
myGetERACertificateFromPicker(
OPTIONAL IN HINSTANCE hInstance,
OPTIONAL IN HWND hwndParent,
OPTIONAL IN int idTitle,
OPTIONAL IN int idSubTitle,
OPTIONAL IN WCHAR const *pwszCommonName,
IN BOOL fSilent,
OUT CERT_CONTEXT const **ppCert)
{
HRESULT hr;
CHAR const * const apszObjId[2] = {
szOID_ENROLLMENT_AGENT,
szOID_KP_QUALIFIED_SUBORDINATION,
};
hr = myGetCertificateFromPicker(
hInstance,
hwndParent,
idTitle,
idSubTitle,
CUCS_MYSTORE |
CUCS_PRIVATEKEYREQUIRED |
//CUCS_USAGEREQUIRED |
(fSilent? CUCS_SILENT : 0),
pwszCommonName,
0, // cStore
NULL, // rghStore
ARRAYSIZE(apszObjId), // cpszObjId
apszObjId,
ppCert);
_JumpIfError(hr, error, "myGetCertificateFromPicker");
error:
return(hr);
}
HRESULT
mySelectCertificateFromStore(
IN OUT CRYPTUI_SELECTCERTIFICATE_STRUCT *pCertSelect,
IN HCERTSTORE hStore,
IN OUT CERT_CONTEXT const **ppCertRet)
{
HRESULT hr;
CERT_CONTEXT const *pCertRet = *ppCertRet;
CERT_CONTEXT const *pCert = NULL;
BOOL fInitialSelectedCert = FALSE;
while (TRUE)
{
pCert = CertEnumCertificatesInStore(hStore, pCert);
if (NULL == pCert)
{
break;
}
if ((*pCertSelect->pFilterCallback)(
pCert,
&fInitialSelectedCert,
pCertSelect->pvCallbackData))
{
if (NULL != pCertRet)
{
hr = CRYPT_E_EXISTS;
_JumpError(hr, error, "more than one cert matches");
}
pCertRet = CertDuplicateCertificateContext(pCert);
if (NULL == pCertRet)
{
hr = CRYPT_E_NOT_FOUND;
_JumpError(hr, error, "CertDuplicateCertificateContext");
}
}
}
hr = S_OK;
error:
if (NULL != pCert)
{
CertFreeCertificateContext(pCert);
}
*ppCertRet = pCertRet;
return(hr);
}
CERT_CONTEXT const *
mySelectCertificate(
IN OUT CRYPTUI_SELECTCERTIFICATE_STRUCT *pCertSelect)
{
HRESULT hr;
CERT_CONTEXT const *pCert = NULL;
DWORD i;
if (NULL == pCertSelect->rghDisplayStores ||
0 == pCertSelect->cDisplayStores)
{
hr = CRYPT_E_NOT_FOUND;
_JumpError(hr, error, "no Cert Stores");
}
for (i = 0; i < pCertSelect->cDisplayStores; i++)
{
hr = mySelectCertificateFromStore(
pCertSelect,
pCertSelect->rghDisplayStores[i],
&pCert);
_JumpIfError(hr, error, "mySelectCertificateFromStore");
}
hr = S_OK;
error:
if (S_OK != hr)
{
if (NULL != pCert)
{
CertFreeCertificateContext(pCert);
pCert = NULL;
}
SetLastError(hr);
}
return(pCert);
}
HRESULT
myGetCertificateFromPicker(
OPTIONAL IN HINSTANCE hInstance,
OPTIONAL IN HWND hwndParent,
OPTIONAL IN int idTitle,
OPTIONAL IN int idSubTitle,
IN DWORD dwFlags, // CUCS_*
OPTIONAL IN WCHAR const *pwszCommonName,
OPTIONAL IN DWORD cStore,
OPTIONAL IN HCERTSTORE *rghStore,
IN DWORD cpszObjId,
OPTIONAL IN CHAR const * const *apszObjId,
OUT CERT_CONTEXT const **ppCert)
{
HRESULT hr;
HCERTSTORE *rghStoreOpened = NULL;
DWORD cStoreOpened;
CERTFILTERCALLBACKDATA callbackData;
CRYPTUI_SELECTCERTIFICATE_STRUCT CertSelect;
ZeroMemory(&callbackData, sizeof(callbackData));
ZeroMemory(&CertSelect, sizeof(CertSelect));
// custom titles
if (NULL != hInstance)
{
// try to load title from resource
hr = myLoadRCString(
hInstance,
idTitle,
const_cast<WCHAR **>(&CertSelect.szTitle));
if (S_OK != hr)
{
CSASSERT(NULL == CertSelect.szTitle);
_PrintError(hr, "myLoadRCString(Title)");
}
hr = myLoadRCString(
hInstance,
idSubTitle,
const_cast<WCHAR **>(&CertSelect.szDisplayString));
if (S_OK != hr)
{
CSASSERT(NULL == CertSelect.szDisplayString);
_PrintError(hr, "myLoadRCString(Title)");
}
}
if (NULL == rghStore)
{
cStore = 0;
}
cStoreOpened = 0;
if ((CUCS_SOURCEMASK | CUCS_TYPEMASK) & dwFlags)
{
CWaitCursor cwait;
hr = myOpenCertStores(dwFlags, &cStoreOpened, &rghStoreOpened);
_PrintIfError(hr, "myOpenCertStores");
}
CertSelect.cDisplayStores = cStore + cStoreOpened;
if (0 == CertSelect.cDisplayStores)
{
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
_JumpError(hr, error, "no Cert Store");
}
CertSelect.rghDisplayStores = (HCERTSTORE *) LocalAlloc(
LMEM_FIXED,
CertSelect.cDisplayStores * sizeof(HCERTSTORE));
if (NULL == CertSelect.rghDisplayStores)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc(CertSelect.rghDisplayStores)");
}
if (0 != cStore)
{
CopyMemory(
&CertSelect.rghDisplayStores[0],
rghStore,
cStore * sizeof(rghStore[0]));
}
if (0 != cStoreOpened)
{
CopyMemory(
&CertSelect.rghDisplayStores[cStore],
rghStoreOpened,
cStoreOpened * sizeof(rghStore[0]));
}
// temporary store where the callback will store links to found certs
// so it can check for duplicates
callbackData.hMemStore = CertOpenStore(
CERT_STORE_PROV_MEMORY,
X509_ASN_ENCODING,
NULL,
CERT_STORE_CREATE_NEW_FLAG,
NULL);
if (NULL == callbackData.hMemStore)
{
hr = myHLastError();
_JumpError(hr, error, "can't create memory store");
}
CertSelect.dwSize = sizeof(CertSelect);
CertSelect.hwndParent = hwndParent;
//CertSelect.dwFlags = 0; // single selection
//CertSelect.dwDontUseColumn = 0; // display all column
CertSelect.pFilterCallback = CertificateFilterProc; // assign callback
//CertSelect.pDisplayCallback = NULL; // use default cert view dlg
callbackData.dwFlags = dwFlags;
callbackData.cpszObjId = cpszObjId;
callbackData.apszObjId = apszObjId;
callbackData.pwszCommonName = pwszCommonName;
if (NULL != pwszCommonName)
{
hr = WszToMultiByteInteger(
TRUE,
pwszCommonName,
&callbackData.cbHash,
&callbackData.pbHash);
_PrintIfError2(hr, "WszToMultiByteInteger", hr);
hr = myMakeSerialBstr(pwszCommonName, &callbackData.strSerialNumber);
_PrintIfError2(hr, "myMakeSerialBstr", hr);
}
callbackData.hr = S_FALSE;
CertSelect.pvCallbackData = &callbackData; // pass filter info as data
//CertSelect.cStores = 0; // no additional stores for chain verify
//CertSelect.rghStores = NULL; // no additional stores for chain verify
//CertSelect.cPropSheetPages = 0; // no custom cert view pages
//CertSelect.rgPropSheetPages = NULL; // no custom cert view pages
//CertSelect.hSelectedCertStore = NULL; // single selection
if (CUCS_SILENT & dwFlags)
{
*ppCert = mySelectCertificate(&CertSelect);
if (NULL == *ppCert)
{
hr = myHLastError();
_JumpError(hr, error, "mySelectCertificate");
}
}
else
{
*ppCert = CryptUIDlgSelectCertificate(&CertSelect);
}
// check for error in selection dialog
hr = callbackData.hr;
if (S_FALSE == hr)
{
hr = CRYPT_E_NOT_FOUND;
}
_JumpIfError(hr, error, "CryptUIDlgSelectCertificate");
error:
if (NULL != rghStoreOpened)
{
myCloseCertStores(cStoreOpened, rghStoreOpened);
}
if (NULL != callbackData.hMemStore)
{
CertCloseStore(callbackData.hMemStore, 0);
}
if (NULL != callbackData.strSerialNumber)
{
SysFreeString(callbackData.strSerialNumber);
}
if (NULL != callbackData.pbHash)
{
LocalFree(callbackData.pbHash);
}
if (NULL != CertSelect.szTitle)
{
LocalFree(const_cast<WCHAR *>(CertSelect.szTitle));
}
if (NULL != CertSelect.szDisplayString)
{
LocalFree(const_cast<WCHAR *>(CertSelect.szDisplayString));
}
if (NULL != CertSelect.rghDisplayStores)
{
LocalFree(CertSelect.rghDisplayStores);
}
return(hr);
}