You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3653 lines
122 KiB
3653 lines
122 KiB
#include "pch.hxx"
|
|
#ifndef WIN16
|
|
#include <wintrust.h>
|
|
#endif // !WIN16
|
|
#include "demand.h"
|
|
#include <stdio.h>
|
|
|
|
#pragma warning(disable: 4127) // conditional expression is constant
|
|
|
|
#ifndef CPD_REVOCATION_CHECK_NONE
|
|
#define CPD_REVOCATION_CHECK_NONE 0x00010000
|
|
#define CPD_REVOCATION_CHECK_END_CERT 0x00020000
|
|
#define CPD_REVOCATION_CHECK_CHAIN 0x00040000
|
|
#define CPD_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT 0x00080000
|
|
#endif
|
|
#define CPD_REVOCATION_MASK (CPD_REVOCATION_CHECK_NONE | \
|
|
CPD_REVOCATION_CHECK_END_CERT | \
|
|
CPD_REVOCATION_CHECK_CHAIN | \
|
|
CPD_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT)
|
|
|
|
#define TIME_DELTA_SECONDS 600 // 10 minutes in seconds
|
|
#define FILETIME_SECOND 10000000 // 100ns intervals per second
|
|
|
|
const char SzOID_CTL_ATTR_YESNO_TRUST[] = szOID_YESNO_TRUST_ATTR;
|
|
const char SzOID_KP_CTL_USAGE_SIGNING[] = szOID_KP_CTL_USAGE_SIGNING;
|
|
const BYTE RgbTrustYes[] = {2, 1, 1};
|
|
const BYTE RgbTrustNo[] = {2, 1, 2};
|
|
const BYTE RgbTrustParent[] = {2, 1, 0};
|
|
const char SzOID_OLD_CTL_YESNO_TRUST[] = "1.3.6.1.4.1.311.1000.1.1.2";
|
|
const char SzTrustListSigner[] = "Trust List Signer";
|
|
const char SzTrustDN[] = "cn=%s, cn=Trust List Signer, cn=%s";
|
|
const char SzPolicyKey[] =
|
|
"SOFTWARE\\Microsoft\\Cryptography\\"szCERT_CERTIFICATE_ACTION_VERIFY;
|
|
const char SzPolicyData[] = "PolicyFlags";
|
|
|
|
const DWORD CTL_MODIFY_ERR_NOT_YET_PROCESSED = (DWORD) -1;
|
|
extern HINSTANCE HinstDll;
|
|
|
|
PCCERT_CONTEXT CreateTrustSigningCert(HWND hwnd, HCERTSTORE hcertstoreRoot, BOOL);
|
|
|
|
const int COtherProviders = 2;
|
|
#ifndef WIN16
|
|
LPWSTR RgszProvider[] = {
|
|
L"CA", L"MY"
|
|
};
|
|
#else // WIN16
|
|
LPWSTR RgszProvider[] = {
|
|
"CA", "MY"
|
|
};
|
|
#endif // !WIN16
|
|
|
|
#ifdef NT5BUILD
|
|
|
|
typedef struct {
|
|
DWORD cbSize;
|
|
DWORD dwFlags;
|
|
DWORD cRootStores;
|
|
HCERTSTORE * rghRootStores;
|
|
DWORD cTrustStores;
|
|
HCERTSTORE * rghTrustStores;
|
|
LPCSTR pszUsageOid;
|
|
HCRYPTDEFAULTCONTEXT hdefaultcontext; // from CryptInstallDefaultContext
|
|
} INTERNAL_DATA, * PINTERNAL_DATA;
|
|
|
|
const GUID MyGuid = CERT_CERTIFICATE_ACTION_VERIFY;
|
|
|
|
#pragma message("Building for NT5")
|
|
|
|
void FreeWVTHandle(HANDLE hWVTState) {
|
|
if (hWVTState) {
|
|
HRESULT hr;
|
|
WINTRUST_DATA data = {0};
|
|
|
|
data.cbStruct = sizeof(WINTRUST_DATA);
|
|
data.pPolicyCallbackData = NULL;
|
|
data.pSIPClientData = NULL;
|
|
data.dwUIChoice = WTD_UI_NONE;
|
|
data.fdwRevocationChecks = WTD_REVOKE_NONE;
|
|
data.dwUnionChoice = WTD_CHOICE_BLOB;
|
|
data.pBlob = NULL; // &blob;
|
|
data.dwStateAction = WTD_STATEACTION_CLOSE;
|
|
data.hWVTStateData = hWVTState;
|
|
hr = WinVerifyTrust(NULL, (GUID *)&GuidCertValidate, &data);
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT HrDoTrustWork(PCCERT_CONTEXT pccertToCheck, DWORD dwControl,
|
|
DWORD dwValidityMask,
|
|
DWORD /*cPurposes*/, LPSTR * rgszPurposes, HCRYPTPROV hprov,
|
|
DWORD cRoots, HCERTSTORE * rgRoots,
|
|
DWORD cCAs, HCERTSTORE * rgCAs,
|
|
DWORD cTrust, HCERTSTORE * rgTrust,
|
|
PFNTRUSTHELPER pfn, DWORD lCustData,
|
|
PCCertFrame * /*ppcf*/, DWORD * pcNodes,
|
|
PCCertFrame * rgpcfResult,
|
|
HANDLE * phReturnStateData) // optional: return WinVerifyTrust state handle here
|
|
{
|
|
DWORD cbData;
|
|
DWORD cCerts = 0;
|
|
WINTRUST_BLOB_INFO blob = {0};
|
|
WINTRUST_DATA data = {0};
|
|
DWORD dwErrors;
|
|
BOOL f;
|
|
HRESULT hr;
|
|
int i;
|
|
DWORD j;
|
|
PCCERT_CONTEXT * rgCerts = NULL;
|
|
DWORD * rgdwErrors = NULL;
|
|
DATA_BLOB * rgblobTrust = NULL;
|
|
CERT_VERIFY_CERTIFICATE_TRUST trust;
|
|
UNALIGNED CRYPT_ATTR_BLOB *pVal = NULL;
|
|
|
|
data.cbStruct = sizeof(WINTRUST_DATA);
|
|
data.pPolicyCallbackData = NULL;
|
|
data.pSIPClientData = NULL;
|
|
data.dwUIChoice = WTD_UI_NONE;
|
|
data.fdwRevocationChecks = WTD_REVOKE_NONE;
|
|
data.dwUnionChoice = WTD_CHOICE_BLOB;
|
|
data.pBlob = &blob;
|
|
if (phReturnStateData) {
|
|
data.dwStateAction = WTD_STATEACTION_VERIFY;
|
|
}
|
|
|
|
blob.cbStruct = sizeof(WINTRUST_BLOB_INFO);
|
|
blob.pcwszDisplayName = NULL;
|
|
blob.cbMemObject = sizeof(trust);
|
|
blob.pbMemObject = (LPBYTE) &trust;
|
|
|
|
trust.cbSize = sizeof(trust);
|
|
trust.pccert = pccertToCheck;
|
|
trust.dwFlags = (CERT_TRUST_DO_FULL_SEARCH |
|
|
CERT_TRUST_PERMIT_MISSING_CRLS |
|
|
CERT_TRUST_DO_FULL_TRUST | dwControl);
|
|
trust.dwIgnoreErr = dwValidityMask;
|
|
trust.pdwErrors = &dwErrors;
|
|
// Assert(cPurposes == 1);
|
|
if (rgszPurposes != NULL) {
|
|
trust.pszUsageOid = rgszPurposes[0];
|
|
}
|
|
else {
|
|
trust.pszUsageOid = NULL;
|
|
}
|
|
trust.hprov = hprov;
|
|
trust.cRootStores = cRoots;
|
|
trust.rghstoreRoots = rgRoots;
|
|
trust.cStores = cCAs;
|
|
trust.rghstoreCAs = rgCAs;
|
|
trust.cTrustStores = cTrust;
|
|
trust.rghstoreTrust = rgTrust;
|
|
trust.lCustData = lCustData;
|
|
trust.pfnTrustHelper = pfn;
|
|
trust.pcChain = &cCerts;
|
|
trust.prgChain = &rgCerts;
|
|
trust.prgdwErrors = &rgdwErrors;
|
|
trust.prgpbTrustInfo = &rgblobTrust;
|
|
|
|
hr = WinVerifyTrust(NULL, (GUID *) &GuidCertValidate, &data);
|
|
if ((TRUST_E_CERT_SIGNATURE == hr) ||
|
|
(CERT_E_REVOKED == hr) ||
|
|
(CERT_E_REVOCATION_FAILURE == hr)) {
|
|
hr = S_OK;
|
|
}
|
|
else if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
if (cCerts == 0) {
|
|
return(E_INVALIDARG);
|
|
}
|
|
|
|
if (phReturnStateData) {
|
|
*phReturnStateData = data.hWVTStateData; // Caller must use WinVerifyTrust to free
|
|
}
|
|
|
|
//Assert( cCerts <= 20);
|
|
*pcNodes = cCerts;
|
|
for (i=cCerts-1; i >= 0; i--) {
|
|
rgpcfResult[i] = new CCertFrame(rgCerts[i]);
|
|
|
|
if(!rgpcfResult[i])
|
|
{
|
|
hr=E_OUTOFMEMORY;
|
|
goto ExitHere;
|
|
}
|
|
|
|
rgpcfResult[i]->m_dwFlags = rgdwErrors[i];
|
|
if (rgszPurposes == NULL) {
|
|
continue;
|
|
}
|
|
rgpcfResult[i]->m_cTrust = 1;
|
|
rgpcfResult[i]->m_rgTrust = new STrustDesc[1];
|
|
memset(rgpcfResult[i]->m_rgTrust, 0, sizeof(STrustDesc));
|
|
|
|
//
|
|
// We are going to fill in the trust information which we use
|
|
// to fill in the fields of the dialog box.
|
|
//
|
|
// Start with the question of the cert being self signed
|
|
//
|
|
|
|
rgpcfResult[i]->m_fSelfSign = WTHelperCertIsSelfSigned(X509_ASN_ENCODING, rgCerts[i]->pCertInfo);
|
|
|
|
//
|
|
// We may or may not have trust data information returned, we now
|
|
// build up the trust info for a single cert
|
|
//
|
|
// If we don't have any explicit data, then we just chain the data
|
|
// down from the next level up.
|
|
//
|
|
|
|
if (rgblobTrust[i].cbData == 0) {
|
|
// chain:
|
|
rgpcfResult[i]->m_rgTrust[0].fExplicitTrust = FALSE;
|
|
rgpcfResult[i]->m_rgTrust[0].fExplicitDistrust = FALSE;
|
|
|
|
//
|
|
// We return a special code to say that we found it in the root store
|
|
//
|
|
|
|
rgpcfResult[i]->m_rgTrust[0].fRootStore = rgpcfResult[i]->m_fRootStore =
|
|
(rgblobTrust[i].pbData == (LPBYTE) 1);
|
|
|
|
if (i != (int) (cCerts-1)) {
|
|
rgpcfResult[i]->m_rgTrust[0].fTrust = rgpcfResult[i+1]->m_rgTrust[0].fTrust;
|
|
rgpcfResult[i]->m_rgTrust[0].fDistrust= rgpcfResult[i+1]->m_rgTrust[0].fDistrust;
|
|
} else {
|
|
// Oops -- there is no level up one, so just make some
|
|
// good defaults
|
|
//
|
|
rgpcfResult[i]->m_rgTrust[0].fTrust = rgpcfResult[i]->m_fRootStore;
|
|
rgpcfResult[i]->m_rgTrust[0].fDistrust= FALSE;
|
|
}
|
|
}
|
|
else {
|
|
//
|
|
//
|
|
|
|
f = CryptDecodeObject(X509_ASN_ENCODING, "1.3.6.1.4.1.311.16.1.1",
|
|
rgblobTrust[i].pbData, rgblobTrust[i].cbData,
|
|
0, NULL, &cbData);
|
|
if (!f || (cbData == 0)) {
|
|
chain:
|
|
rgpcfResult[i]->m_fRootStore = FALSE;
|
|
rgpcfResult[i]->m_rgTrust[0].fRootStore = rgpcfResult[i]->m_fRootStore;
|
|
rgpcfResult[i]->m_rgTrust[0].fExplicitTrust = FALSE;
|
|
rgpcfResult[i]->m_rgTrust[0].fExplicitDistrust = FALSE;
|
|
if (i != (int) (cCerts-1)) {
|
|
rgpcfResult[i]->m_rgTrust[0].fTrust = rgpcfResult[i+1]->m_rgTrust[0].fTrust;
|
|
rgpcfResult[i]->m_rgTrust[0].fDistrust = rgpcfResult[i+1]->m_rgTrust[0].fDistrust;
|
|
}
|
|
else {
|
|
rgpcfResult[i]->m_rgTrust[0].fTrust = FALSE;
|
|
rgpcfResult[i]->m_rgTrust[0].fDistrust= FALSE;
|
|
}
|
|
}
|
|
else {
|
|
PCRYPT_ATTRIBUTES pattrs;
|
|
|
|
pattrs = (PCRYPT_ATTRIBUTES) malloc(cbData);
|
|
if (pattrs == NULL) {
|
|
goto chain;
|
|
}
|
|
|
|
CryptDecodeObject(X509_ASN_ENCODING, "1.3.6.1.4.1.311.16.1.1",
|
|
rgblobTrust[i].pbData, rgblobTrust[i].cbData,
|
|
0, pattrs, &cbData);
|
|
|
|
for (j=0; j<pattrs->cAttr; j++) {
|
|
if ((strcmp(pattrs->rgAttr[j].pszObjId, SzOID_CTL_ATTR_YESNO_TRUST) == 0) ||
|
|
(strcmp(pattrs->rgAttr[j].pszObjId, SzOID_OLD_CTL_YESNO_TRUST) == 0))
|
|
{
|
|
|
|
pVal = &(pattrs->rgAttr[j].rgValue[0]);
|
|
|
|
if ((pVal->cbData == sizeof(RgbTrustYes)) &&
|
|
(memcmp(pVal->pbData,
|
|
RgbTrustYes, sizeof(RgbTrustYes)) == 0)) {
|
|
rgpcfResult[i]->m_rgTrust[0].fExplicitTrust = TRUE;
|
|
rgpcfResult[i]->m_rgTrust[0].fTrust = TRUE;
|
|
break;
|
|
}
|
|
else if ((pVal->cbData == sizeof(RgbTrustNo)) &&
|
|
(memcmp(pVal->pbData,
|
|
RgbTrustNo, sizeof(RgbTrustNo)) == 0)) {
|
|
rgpcfResult[i]->m_rgTrust[0].fExplicitDistrust = TRUE;
|
|
rgpcfResult[i]->m_rgTrust[0].fDistrust= TRUE;
|
|
break;
|
|
}
|
|
else if ((pVal->cbData == sizeof(RgbTrustParent)) &&
|
|
(memcmp(pVal->pbData,
|
|
RgbTrustParent, sizeof(RgbTrustParent)) == 0)) {
|
|
goto chain;
|
|
}
|
|
else {
|
|
goto chain;
|
|
}
|
|
}
|
|
}
|
|
if (j == pattrs->cAttr) {
|
|
goto chain;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Clean up all returned values
|
|
//
|
|
|
|
ExitHere:
|
|
if (rgCerts != NULL) {
|
|
//bobn If the loop has been broken because "new" failed, free what we allocated so far...
|
|
for ((hr==E_OUTOFMEMORY?i++:i=0); i< (int) cCerts; i++) {
|
|
//@ REVIEW check CertFreeCertificateContext to see if it will accept a null pointer
|
|
//if it will, we can remove the E_OUTOFMEMORY test above.
|
|
CertFreeCertificateContext(rgCerts[i]);
|
|
}
|
|
LocalFree(rgCerts);
|
|
}
|
|
if (rgdwErrors != NULL) LocalFree(rgdwErrors);
|
|
if (rgblobTrust != NULL) {
|
|
for (i=0; i<(int) cCerts; i++) {
|
|
if (rgblobTrust[i].cbData > 0) {
|
|
LocalFree(rgblobTrust[i].pbData);
|
|
}
|
|
}
|
|
LocalFree(rgblobTrust);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT CertTrustInit(PCRYPT_PROVIDER_DATA pdata)
|
|
{
|
|
DWORD cbSize;
|
|
DWORD dwPolicy = 0;
|
|
DWORD dwType;
|
|
HKEY hkPolicy;
|
|
HCERTSTORE hstore;
|
|
DWORD i;
|
|
PCERT_VERIFY_CERTIFICATE_TRUST pcerttrust;
|
|
CRYPT_PROVIDER_PRIVDATA privdata;
|
|
PINTERNAL_DATA pmydata;
|
|
|
|
//
|
|
// Make sure all of the fields we want are there. If not then it is a
|
|
// complete fatal error.
|
|
//
|
|
|
|
if (! WVT_ISINSTRUCT(CRYPT_PROVIDER_DATA, pdata->cbStruct, pszUsageOID)) {
|
|
return(E_FAIL);
|
|
}
|
|
|
|
if (pdata->pWintrustData->pBlob->cbStruct < sizeof(WINTRUST_BLOB_INFO)) {
|
|
pdata->dwError = ERROR_INVALID_PARAMETER;
|
|
return S_FALSE;
|
|
}
|
|
|
|
pcerttrust = (PCERT_VERIFY_CERTIFICATE_TRUST)
|
|
pdata->pWintrustData->pBlob->pbMemObject;
|
|
if ((pcerttrust == NULL) ||
|
|
(pcerttrust->cbSize < sizeof(*pcerttrust))) {
|
|
pdata->dwError = ERROR_INVALID_PARAMETER;
|
|
return S_FALSE;
|
|
}
|
|
|
|
if (pdata->dwError != 0) {
|
|
return S_FALSE;
|
|
}
|
|
|
|
for (i=TRUSTERROR_STEP_FINAL_WVTINIT; i<TRUSTERROR_STEP_FINAL_CERTCHKPROV; i++) {
|
|
if (pdata->padwTrustStepErrors[i] != 0) {
|
|
return S_FALSE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Allocate the space to hold the internal data we use to talk to ourselfs with
|
|
//
|
|
|
|
cbSize = sizeof(INTERNAL_DATA) + (pcerttrust->cRootStores + 1 +
|
|
pcerttrust->cTrustStores + 1) * sizeof(HCERTSTORE);
|
|
pmydata = (PINTERNAL_DATA)pdata->psPfns->pfnAlloc(cbSize);
|
|
if (! pmydata) {
|
|
return(E_OUTOFMEMORY);
|
|
}
|
|
memset(pmydata, 0, sizeof(*pmydata));
|
|
pmydata->cbSize = sizeof(*pmydata);
|
|
pmydata->rghRootStores = (HCERTSTORE *) (((LPBYTE) pmydata) + sizeof(*pmydata));
|
|
pmydata->rghTrustStores = &pmydata->rghRootStores[pcerttrust->cRootStores+1];
|
|
|
|
privdata.cbStruct = sizeof(privdata);
|
|
memcpy(&privdata.gProviderID, &MyGuid, sizeof(GUID));
|
|
privdata.cbProvData = cbSize;
|
|
privdata.pvProvData = pmydata;
|
|
|
|
pdata->psPfns->pfnAddPrivData2Chain(pdata, &privdata);
|
|
|
|
pmydata->pszUsageOid = pcerttrust->pszUsageOid;
|
|
pmydata->dwFlags = pcerttrust->dwFlags;
|
|
|
|
//
|
|
// Set the restriction OID back into the full provider information to
|
|
// make sure chaining is correct
|
|
//
|
|
|
|
pdata->pszUsageOID = pcerttrust->pszUsageOid;
|
|
|
|
//
|
|
// Retrieve the default revocation check from the policy flags in the
|
|
// registry.
|
|
//
|
|
|
|
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, SzPolicyKey, 0, KEY_READ,
|
|
&hkPolicy) == ERROR_SUCCESS) {
|
|
cbSize = sizeof(dwPolicy);
|
|
if ((ERROR_SUCCESS != RegQueryValueExA(hkPolicy,
|
|
SzPolicyData,
|
|
0, &dwType,
|
|
(LPBYTE)&dwPolicy,
|
|
&cbSize)) ||
|
|
(REG_DWORD != dwType)) {
|
|
dwPolicy = 0;
|
|
}
|
|
RegCloseKey(hkPolicy);
|
|
}
|
|
|
|
//
|
|
// Set the default revocation check level
|
|
//
|
|
|
|
if (dwPolicy & ACTION_REVOCATION_DEFAULT_ONLINE) {
|
|
// Allow full online revocation checking
|
|
|
|
pdata->dwProvFlags |= CPD_REVOCATION_CHECK_CHAIN;
|
|
}
|
|
else if (dwPolicy & ACTION_REVOCATION_DEFAULT_CACHE) {
|
|
// Allow local revocation checks only, do not hit the network
|
|
|
|
// NOTE: Currently not supported by NT Crypto, default to none
|
|
|
|
//Assert(!dwPolicy & ACTION_REVOCATION_DEFAULT_CACHE)
|
|
|
|
pdata->dwProvFlags |= CPD_REVOCATION_CHECK_NONE;
|
|
}
|
|
else {
|
|
// For backwards compatibility default to no revocation
|
|
|
|
pdata->dwProvFlags |= CPD_REVOCATION_CHECK_NONE;
|
|
}
|
|
|
|
//
|
|
// Update the revocation state based on what the user has specifically
|
|
// requested.
|
|
//
|
|
|
|
if (pcerttrust->dwFlags & CRYPTDLG_REVOCATION_ONLINE) {
|
|
// Allow full online revocation checking
|
|
|
|
pdata->dwProvFlags &= ~CPD_REVOCATION_MASK;
|
|
pdata->dwProvFlags |= CPD_REVOCATION_CHECK_CHAIN;
|
|
}
|
|
else if (pcerttrust->dwFlags & CRYPTDLG_REVOCATION_CACHE) {
|
|
// Allow local revocation checks only, do not hit the network.
|
|
|
|
// NOTE: Currently not supported by NT, for now we just ignore
|
|
// the revocakation
|
|
|
|
// Assert(!pcerttrust->dwFlags & CRYPTDLG_REVOCATION_CACHE);
|
|
pdata->dwProvFlags &= ~CPD_REVOCATION_MASK;
|
|
pdata->dwProvFlags |= CPD_REVOCATION_CHECK_NONE;
|
|
}
|
|
else if (pcerttrust->dwFlags & CRYPTDLG_REVOCATION_NONE) {
|
|
// Allow full online revocation checking
|
|
|
|
pdata->dwProvFlags &= ~CPD_REVOCATION_MASK;
|
|
pdata->dwProvFlags |= CPD_REVOCATION_CHECK_NONE;
|
|
}
|
|
|
|
//
|
|
// Set the default crypt provider so we can make sure that ours is used
|
|
//
|
|
|
|
if (pcerttrust->hprov != NULL) {
|
|
if (!CryptInstallDefaultContext(pcerttrust->hprov,
|
|
CRYPT_DEFAULT_CONTEXT_CERT_SIGN_OID,
|
|
szOID_OIWSEC_md5RSA, 0, NULL,
|
|
&pmydata->hdefaultcontext)) {
|
|
return S_FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Setup the stores to be used by the search step.
|
|
//
|
|
// Root ("God") stores
|
|
//
|
|
|
|
if (pcerttrust->cRootStores != 0) {
|
|
for (i=0; i<pcerttrust->cRootStores; i++) {
|
|
if (!pdata->psPfns->pfnAddStore2Chain(pdata,
|
|
pcerttrust->rghstoreRoots[i])) {
|
|
pdata->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_INITPROV] = ERROR_NOT_ENOUGH_MEMORY;
|
|
return S_FALSE;
|
|
}
|
|
pmydata->rghRootStores[i] = CertDuplicateStore(pcerttrust->rghstoreRoots[i]);
|
|
}
|
|
pmydata->cRootStores = i;
|
|
}
|
|
else {
|
|
hstore = CertOpenStore(CERT_STORE_PROV_SYSTEM, X509_ASN_ENCODING,
|
|
pcerttrust->hprov, CERT_SYSTEM_STORE_CURRENT_USER |
|
|
CERT_STORE_NO_CRYPT_RELEASE_FLAG,
|
|
L"Root");
|
|
if (hstore == NULL) {
|
|
pdata->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_INITPROV] = ::GetLastError();
|
|
return S_FALSE;
|
|
}
|
|
if (!pdata->psPfns->pfnAddStore2Chain(pdata, hstore)) {
|
|
CertCloseStore(hstore, 0);
|
|
pmydata->rghRootStores[0] = CertDuplicateStore(pcerttrust->rghstoreRoots[i]);
|
|
return S_FALSE;
|
|
}
|
|
pmydata->rghRootStores[0] = CertDuplicateStore(hstore);
|
|
pmydata->cRootStores = 1;
|
|
CertCloseStore(hstore, 0);
|
|
}
|
|
|
|
// "Trust" stores
|
|
|
|
if (pcerttrust->cTrustStores != 0) {
|
|
for (i=0; i<pcerttrust->cTrustStores; i++) {
|
|
pmydata->rghTrustStores[i] = CertDuplicateStore(pcerttrust->rghstoreTrust[i]);
|
|
}
|
|
pmydata->cTrustStores = i;
|
|
}
|
|
else {
|
|
hstore = CertOpenStore(CERT_STORE_PROV_SYSTEM, X509_ASN_ENCODING,
|
|
pcerttrust->hprov, CERT_SYSTEM_STORE_CURRENT_USER |
|
|
CERT_STORE_NO_CRYPT_RELEASE_FLAG,
|
|
L"Trust");
|
|
if (hstore == NULL) {
|
|
pdata->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_INITPROV] = ::GetLastError();
|
|
return S_FALSE;
|
|
}
|
|
pmydata->rghTrustStores[0] = CertDuplicateStore(hstore);
|
|
pmydata->cTrustStores = 1;
|
|
CertCloseStore(hstore, 0);
|
|
}
|
|
|
|
// "CA" stores
|
|
|
|
|
|
if (pcerttrust->cStores != 0) {
|
|
for (i=0; i<pcerttrust->cStores; i++) {
|
|
if (!pdata->psPfns->pfnAddStore2Chain(pdata,
|
|
pcerttrust->rghstoreCAs[i])) {
|
|
pdata->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_INITPROV] = ERROR_NOT_ENOUGH_MEMORY;
|
|
return S_FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((pcerttrust->cStores == 0) ||
|
|
(pcerttrust->dwFlags & CERT_TRUST_ADD_CERT_STORES)) {
|
|
for (i=0; i<COtherProviders; i++) {
|
|
hstore = CertOpenStore(CERT_STORE_PROV_SYSTEM, X509_ASN_ENCODING,
|
|
pcerttrust->hprov, CERT_SYSTEM_STORE_CURRENT_USER |
|
|
CERT_STORE_NO_CRYPT_RELEASE_FLAG,
|
|
RgszProvider[i]);
|
|
if (hstore == NULL) {
|
|
pdata->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_INITPROV] = ::GetLastError();
|
|
return S_FALSE;
|
|
}
|
|
if (!pdata->psPfns->pfnAddStore2Chain(pdata, hstore)) {
|
|
CertCloseStore(hstore, 0);
|
|
pdata->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_INITPROV] = ERROR_NOT_ENOUGH_MEMORY;
|
|
return S_FALSE;
|
|
}
|
|
CertCloseStore(hstore, 0);
|
|
}
|
|
}
|
|
|
|
//
|
|
// We have exactly one signature object to be added in, that is the certificate
|
|
// that we are going to verify.
|
|
//
|
|
|
|
CRYPT_PROVIDER_SGNR sgnr;
|
|
|
|
memset(&sgnr, 0, sizeof(sgnr));
|
|
sgnr.cbStruct = sizeof(sgnr);
|
|
GetSystemTimeAsFileTime(&sgnr.sftVerifyAsOf);
|
|
// memcpy(&sgnr.sftVerifyAsOf, &pcerttrust->pccert->pCertInfo->NotBefore,
|
|
// sizeof(FILETIME));
|
|
// sgnr.csCertChain = 0;
|
|
// sgnr.pasCertChain = NULL;
|
|
// sgnr.dwSignerType = 0;
|
|
// sgnr.psSigner = NULL;
|
|
// sgnr.dwError = 0;
|
|
// sgnr.csCounterSigners = 0;
|
|
// sgnr.pasCounterSigners = NULL;
|
|
|
|
if (!pdata->psPfns->pfnAddSgnr2Chain(pdata, FALSE, 0, &sgnr)) {
|
|
pdata->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_INITPROV] = ERROR_NOT_ENOUGH_MEMORY;
|
|
return S_FALSE;
|
|
}
|
|
|
|
if (!pdata->psPfns->pfnAddCert2Chain(pdata, 0, FALSE, 0, pcerttrust->pccert)) {
|
|
pdata->padwTrustStepErrors[TRUSTERROR_STEP_FINAL_INITPROV] = ERROR_NOT_ENOUGH_MEMORY;
|
|
return S_FALSE;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
#ifdef DEBUG
|
|
void DebugFileTime(FILETIME ft) {
|
|
SYSTEMTIME st = {0};
|
|
TCHAR szBuffer[256];
|
|
|
|
FileTimeToSystemTime(&ft, &st);
|
|
wnsprintf(szBuffer, ARRAYSIZE(szBuffer), L"%02d/%02d/%04d %02d:%02d:%02d\n", st.wMonth, st.wDay, st.wYear, st.wHour, st.wMinute, st.wSecond);
|
|
OutputDebugString(szBuffer);
|
|
}
|
|
#endif
|
|
|
|
|
|
LONG CertVerifyTimeValidityWithDelta(LPFILETIME pTimeToVerify, PCERT_INFO pCertInfo, ULONG ulOffset) {
|
|
LONG lRet;
|
|
FILETIME ftNow;
|
|
FILETIME ftDelta;
|
|
__int64 i64Delta;
|
|
__int64 i64Offset;
|
|
|
|
lRet = CertVerifyTimeValidity(pTimeToVerify, pCertInfo);
|
|
|
|
if (lRet < 0) {
|
|
if (! pTimeToVerify) {
|
|
// Get the current time in filetime format so we can add the offset
|
|
GetSystemTimeAsFileTime(&ftNow);
|
|
pTimeToVerify = &ftNow;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
DebugFileTime(*pTimeToVerify);
|
|
#endif
|
|
|
|
i64Delta = pTimeToVerify->dwHighDateTime;
|
|
i64Delta = i64Delta << 32;
|
|
i64Delta += pTimeToVerify->dwLowDateTime;
|
|
|
|
// Add the offset into the original time to get us a new time to check
|
|
i64Offset = FILETIME_SECOND;
|
|
i64Offset *= ulOffset;
|
|
i64Delta += i64Offset;
|
|
|
|
ftDelta.dwLowDateTime = (ULONG)i64Delta & 0xFFFFFFFF;
|
|
ftDelta.dwHighDateTime = (ULONG)(i64Delta >> 32);
|
|
|
|
#ifdef DEBUG
|
|
DebugFileTime(ftDelta);
|
|
#endif
|
|
|
|
lRet = CertVerifyTimeValidity(&ftDelta, pCertInfo);
|
|
}
|
|
|
|
return(lRet);
|
|
}
|
|
|
|
|
|
//// FCheckCTLCert
|
|
//
|
|
// Description:
|
|
// This function is called when we find a CTL signed by a certificate. At this
|
|
// point we are going to check to see if we believe in the cert which was
|
|
// used to sign the CTL.
|
|
//
|
|
// We know that the certificate was already one of the ROOT stores and is
|
|
// therefore explicity trusted as this is enforced by the caller.
|
|
//
|
|
|
|
BOOL FCheckCTLCert(PCCERT_CONTEXT pccert)
|
|
{
|
|
DWORD cbData;
|
|
BOOL f;
|
|
FILETIME ftZero;
|
|
DWORD i;
|
|
PCERT_ENHKEY_USAGE pUsage;
|
|
|
|
memset(&ftZero, 0, sizeof(ftZero));
|
|
|
|
//
|
|
// Start by checking the time validity of the cert. We are going to
|
|
// allow two special cases as well as the time being valid.
|
|
//
|
|
// 1. The start and end time are the same -- and indication that we
|
|
// made this in an earlier incarnation, or
|
|
// 2. The end time is 0.
|
|
//
|
|
|
|
if ((memcmp(&pccert->pCertInfo->NotBefore, &pccert->pCertInfo->NotAfter,
|
|
sizeof(FILETIME)) == 0) ||
|
|
memcmp(&pccert->pCertInfo->NotAfter, &ftZero, sizeof(FILETIME)) == 0) {
|
|
DWORD err;
|
|
HCERTSTORE hcertstore;
|
|
HKEY hkey;
|
|
PCCERT_CONTEXT pccertNew;
|
|
PCCERT_CONTEXT pccertOld;
|
|
|
|
err = RegOpenKeyExA(HKEY_CURRENT_USER,
|
|
"Software\\Microsoft\\SystemCertificates\\ROOT",
|
|
0, KEY_ALL_ACCESS, &hkey);
|
|
hcertstore = CertOpenStore(CERT_STORE_PROV_REG, X509_ASN_ENCODING,
|
|
NULL, 0, hkey);
|
|
if (hcertstore != NULL) {
|
|
pccertOld = CertGetSubjectCertificateFromStore(hcertstore,
|
|
X509_ASN_ENCODING, pccert->pCertInfo);
|
|
pccertNew = CreateTrustSigningCert(NULL, hcertstore, FALSE);
|
|
CertFreeCertificateContext(pccertNew);
|
|
|
|
if (pccertOld != NULL) {
|
|
CertDeleteCertificateFromStore(pccertOld);
|
|
}
|
|
CertCloseStore(hcertstore, 0);
|
|
}
|
|
RegCloseKey(hkey);
|
|
}
|
|
else if (CertVerifyTimeValidityWithDelta(NULL, pccert->pCertInfo,
|
|
TIME_DELTA_SECONDS) != 0) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Must have a correct enhanced key usage to be viable.
|
|
//
|
|
// Crack the usage on the cert
|
|
|
|
f = CertGetEnhancedKeyUsage(pccert, 0, NULL, &cbData);
|
|
if (!f || (cbData == 0)) {
|
|
return FALSE;
|
|
}
|
|
|
|
pUsage = (PCERT_ENHKEY_USAGE) malloc(cbData);
|
|
if (pUsage == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (!CertGetEnhancedKeyUsage(pccert, 0, pUsage, &cbData)) {
|
|
free(pUsage);
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Look for the CTL_USAGE_SIGNING purpose on the cert. If its not there
|
|
// then we don't allow it to be used.
|
|
//
|
|
|
|
for (i=0; i<pUsage->cUsageIdentifier; i++) {
|
|
if (strcmp(pUsage->rgpszUsageIdentifier[i],
|
|
szOID_KP_CTL_USAGE_SIGNING) == 0) {
|
|
break;
|
|
}
|
|
}
|
|
if (i == pUsage->cUsageIdentifier) {
|
|
free(pUsage);
|
|
return FALSE;
|
|
}
|
|
free(pUsage);
|
|
|
|
//
|
|
// Add any other tests.
|
|
//
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//// CertTrustCertPolicy
|
|
//
|
|
// Description:
|
|
// This code looks for trust information and puts it into the certificate
|
|
// chain. The behavior that we follow is going to depend on what type of
|
|
// searching we are going to look for.
|
|
//
|
|
// If we are just looking for trust, then we follow up the CTL looking for
|
|
// trust information.
|
|
//
|
|
|
|
BOOL CertTrustCertPolicy(PCRYPT_PROVIDER_DATA pdata, DWORD, BOOL, DWORD)
|
|
{
|
|
DWORD cb;
|
|
BOOL f;
|
|
BOOL fContinue = TRUE;
|
|
DWORD i;
|
|
CTL_VERIFY_USAGE_STATUS vus;
|
|
CTL_VERIFY_USAGE_PARA vup;
|
|
PCCTL_CONTEXT pctlTrust = NULL;
|
|
PCRYPT_PROVIDER_CERT ptcert;
|
|
CTL_USAGE ctlusage;
|
|
PCCERT_CONTEXT pccert = NULL;
|
|
PCRYPT_PROVIDER_SGNR psigner;
|
|
PINTERNAL_DATA pmydata;
|
|
PCRYPT_PROVIDER_PRIVDATA pprivdata;
|
|
BYTE rgbHash[20];
|
|
CRYPT_HASH_BLOB blob;
|
|
|
|
//
|
|
// Make sure all of the fields we want are there. If not then it is a
|
|
// complete fatal error.
|
|
//
|
|
|
|
|
|
if (! WVT_ISINSTRUCT(CRYPT_PROVIDER_DATA, pdata->cbStruct, pszUsageOID)) {
|
|
fContinue = FALSE;
|
|
goto Exit;
|
|
}
|
|
|
|
if (pdata->pWintrustData->pBlob->cbStruct < sizeof(WINTRUST_BLOB_INFO)) {
|
|
pdata->dwError = ERROR_INVALID_PARAMETER;
|
|
fContinue = FALSE;
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Look to see if we already have an error to deal with
|
|
//
|
|
|
|
if (pdata->dwError != 0) {
|
|
fContinue = FALSE;
|
|
goto Exit;
|
|
}
|
|
|
|
for (i=TRUSTERROR_STEP_FINAL_WVTINIT; i<TRUSTERROR_STEP_FINAL_CERTCHKPROV; i++) {
|
|
if (pdata->padwTrustStepErrors[i] != 0) {
|
|
fContinue = FALSE;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Get our internal data structure
|
|
//
|
|
|
|
pprivdata = WTHelperGetProvPrivateDataFromChain(pdata, (LPGUID) &MyGuid);
|
|
if (pprivdata)
|
|
pmydata = (PINTERNAL_DATA)pprivdata->pvProvData;
|
|
else
|
|
{
|
|
fContinue = FALSE;
|
|
goto Exit;
|
|
}
|
|
|
|
|
|
//
|
|
// We only work with a single signer --
|
|
|
|
psigner = WTHelperGetProvSignerFromChain(pdata, 0, FALSE, 0);
|
|
if (psigner == NULL)
|
|
{
|
|
fContinue = FALSE;
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Extract the certificate at the top of the stack
|
|
//
|
|
|
|
ptcert = WTHelperGetProvCertFromChain(psigner, psigner->csCertChain-1);
|
|
|
|
//
|
|
// Does this certificate meet the definitions of "TRUSTED".
|
|
//
|
|
// Definition #1. It exists in a Root store
|
|
//
|
|
|
|
blob.cbData = sizeof(rgbHash);
|
|
blob.pbData = rgbHash;
|
|
cb = sizeof(rgbHash);
|
|
CertGetCertificateContextProperty(ptcert->pCert, CERT_SHA1_HASH_PROP_ID,
|
|
rgbHash, &cb);
|
|
for (i=0; i<pmydata->cRootStores; i++) {
|
|
pccert = CertFindCertificateInStore(pmydata->rghRootStores[i], X509_ASN_ENCODING,
|
|
0, CERT_FIND_SHA1_HASH, &blob, NULL);
|
|
if (pccert != NULL) {
|
|
ptcert->fTrustedRoot = TRUE;
|
|
fContinue = FALSE;
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Build up the structures we will use to do a search in the
|
|
// trust stores
|
|
//
|
|
|
|
memset(&ctlusage, 0, sizeof(ctlusage));
|
|
ctlusage.cUsageIdentifier = 1;
|
|
ctlusage.rgpszUsageIdentifier = (LPSTR *) &pmydata->pszUsageOid;
|
|
|
|
memset(&vup, 0, sizeof(vup));
|
|
vup.cbSize = sizeof(vup);
|
|
vup.cCtlStore = pmydata->cTrustStores;
|
|
vup.rghCtlStore = pmydata->rghTrustStores;
|
|
vup.cSignerStore = pmydata->cRootStores;
|
|
vup.rghSignerStore = pmydata->rghRootStores;
|
|
|
|
memset(&vus, 0, sizeof(vus));
|
|
vus.cbSize = sizeof(vus);
|
|
vus.ppCtl = &pctlTrust;
|
|
vus.ppSigner = &pccert;
|
|
|
|
//
|
|
// Now search for the CTL on this certificate, if we don't find anything then
|
|
// we return TRUE to state that we want the search to continue
|
|
//
|
|
//
|
|
|
|
f = CertVerifyCTLUsage(X509_ASN_ENCODING, CTL_CERT_SUBJECT_TYPE,
|
|
(LPVOID) ptcert->pCert, &ctlusage,
|
|
CERT_VERIFY_INHIBIT_CTL_UPDATE_FLAG |
|
|
CERT_VERIFY_NO_TIME_CHECK_FLAG |
|
|
CERT_VERIFY_TRUSTED_SIGNERS_FLAG, &vup, &vus);
|
|
|
|
if (!f) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// We found a CTL for this certificate. First step is to see if the signing
|
|
// certificate is one which we can respect. We know that the cert is in
|
|
// a root store, since we told the system it could only accept root store
|
|
// certs
|
|
//
|
|
|
|
if (!FCheckCTLCert(pccert)) {
|
|
f = FALSE;
|
|
goto Exit;
|
|
}
|
|
|
|
// OK -- the signing cert passed it sanity check -- so see if the CTL contains
|
|
// relevent information or is just a "keep looking" CTL.
|
|
//
|
|
|
|
ptcert->pTrustListContext = (PCTL_CONTEXT) pctlTrust;
|
|
pctlTrust = NULL;
|
|
|
|
//
|
|
// We we are trying to do a full trust verification, then we need to
|
|
// just skip to the next cert without bothering to look at the ctl
|
|
//
|
|
|
|
if (pmydata->dwFlags & CERT_TRUST_DO_FULL_SEARCH) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Look to see if this is a "pass" item. If it is then we need to
|
|
// continue looking, if it is not then we have reached the
|
|
// decision point
|
|
//
|
|
|
|
PCTL_ENTRY pentry;
|
|
pentry = &ptcert->pTrustListContext->pCtlInfo->rgCTLEntry[vus.dwCtlEntryIndex];
|
|
for (i=0; i<pentry->cAttribute; i++) {
|
|
if ((strcmp(pentry->rgAttribute[i].pszObjId, SzOID_CTL_ATTR_YESNO_TRUST) == 0) ||
|
|
(strcmp(pentry->rgAttribute[i].pszObjId, SzOID_OLD_CTL_YESNO_TRUST) == 0)) {
|
|
if ((pentry->rgAttribute[i].rgValue[0].cbData == sizeof(RgbTrustParent)) &&
|
|
(memcmp(pentry->rgAttribute[i].rgValue[0].pbData,
|
|
RgbTrustParent, sizeof(RgbTrustParent)) == 0)) {
|
|
// Defer to parent
|
|
goto Exit;
|
|
}
|
|
//
|
|
// We have the decision point, push the signer onto the the stack
|
|
//
|
|
|
|
fContinue = !!(pmydata->dwFlags & CERT_TRUST_DO_FULL_TRUST);
|
|
goto Exit;
|
|
}
|
|
|
|
}
|
|
|
|
Exit:
|
|
if (pccert != NULL) CertFreeCertificateContext(pccert);
|
|
if (pctlTrust != NULL) CertFreeCTLContext(pctlTrust);
|
|
return fContinue;
|
|
}
|
|
|
|
//// HrCheckPolicies
|
|
//
|
|
// Description:
|
|
// Given an array of certificates, figure out what errors we have
|
|
//
|
|
// We enforce the following set of extensions
|
|
//
|
|
// enhancedKeyUsage
|
|
// basicConstraints
|
|
// keyUsage
|
|
// nameConstraints
|
|
//
|
|
|
|
HRESULT HrCheckPolicies(PCRYPT_PROVIDER_SGNR psigner, DWORD cChain,
|
|
DWORD * rgdwErrors, LPCSTR pszUsage)
|
|
{
|
|
DWORD cbData;
|
|
DWORD cExt;
|
|
DWORD dwPolicy = 0;
|
|
DWORD dwType;
|
|
HKEY hkPolicy;
|
|
DWORD i;
|
|
DWORD iCert;
|
|
DWORD iExt;
|
|
PCRYPT_PROVIDER_CERT ptcert;
|
|
PCERT_EXTENSION rgExt;
|
|
|
|
// Retrieve policy information from the registry
|
|
|
|
if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, SzPolicyKey, 0, KEY_READ,
|
|
&hkPolicy) == ERROR_SUCCESS) {
|
|
cbData = sizeof(dwPolicy);
|
|
if ((ERROR_SUCCESS != RegQueryValueExA(hkPolicy,
|
|
SzPolicyData,
|
|
0, &dwType,
|
|
(LPBYTE)&dwPolicy,
|
|
&cbData)) ||
|
|
(REG_DWORD != dwType)) {
|
|
dwPolicy = 0;
|
|
}
|
|
RegCloseKey(hkPolicy);
|
|
}
|
|
|
|
// Check the policy on each cert in the chain
|
|
|
|
for (iCert=0; iCert<cChain; iCert++) {
|
|
//
|
|
// Get the next cert to examine
|
|
//
|
|
|
|
ptcert = WTHelperGetProvCertFromChain(psigner, iCert);
|
|
|
|
//
|
|
// Setup to process the certs extensions
|
|
//
|
|
|
|
if (!ptcert || !(ptcert->pCert) || !(ptcert->pCert->pCertInfo))
|
|
continue;
|
|
|
|
cExt = ptcert->pCert->pCertInfo->cExtension;
|
|
rgExt = ptcert->pCert->pCertInfo->rgExtension;
|
|
|
|
//
|
|
// Process the extensions
|
|
//
|
|
|
|
ProcessExtensions:
|
|
for (iExt=0; iExt<cExt; iExt++) {
|
|
if (strcmp(rgExt[iExt].pszObjId, szOID_ENHANCED_KEY_USAGE) == 0) {
|
|
if (pszUsage == NULL) {
|
|
continue;
|
|
}
|
|
PCERT_ENHKEY_USAGE pUsage;
|
|
pUsage = (PCERT_ENHKEY_USAGE) PVCryptDecode(rgExt[iExt].pszObjId,
|
|
rgExt[iExt].Value.cbData,
|
|
rgExt[iExt].Value.pbData);
|
|
if ((pUsage == NULL) && rgExt[iExt].fCritical) {
|
|
rgdwErrors[iCert] |= CERT_VALIDITY_UNKNOWN_CRITICAL_EXTENSION;
|
|
continue;
|
|
}
|
|
|
|
if(pUsage)
|
|
{
|
|
for (i=0; i<pUsage->cUsageIdentifier; i++) {
|
|
if (strcmp(pUsage->rgpszUsageIdentifier[i], pszUsage) == 0) {
|
|
break;
|
|
}
|
|
}
|
|
if (i == pUsage->cUsageIdentifier) {
|
|
rgdwErrors[iCert] |= CERT_VALIDITY_EXTENDED_USAGE_FAILURE;
|
|
}
|
|
|
|
free(pUsage);
|
|
}
|
|
}
|
|
else if (strcmp(rgExt[iExt].pszObjId, szOID_BASIC_CONSTRAINTS2) == 0) {
|
|
PCERT_BASIC_CONSTRAINTS2_INFO p;
|
|
|
|
// If Basic Constraints is not marked critical (in opposition
|
|
// to PKIX) we allow the administrator to overrule our
|
|
// processing to deal with bad NT CertSrv SP1 hierarchies
|
|
// used with Exchange KMS.
|
|
|
|
if ((dwPolicy & POLICY_IGNORE_NON_CRITICAL_BC) &&
|
|
!(rgExt[iExt].fCritical)) {
|
|
continue;
|
|
}
|
|
|
|
// Verify the basic constraint extension
|
|
|
|
p = (PCERT_BASIC_CONSTRAINTS2_INFO)
|
|
PVCryptDecode(rgExt[iExt].pszObjId,
|
|
rgExt[iExt].Value.cbData,
|
|
rgExt[iExt].Value.pbData);
|
|
if ((p == NULL) && rgExt[iExt].fCritical) {
|
|
rgdwErrors[iCert] |= CERT_VALIDITY_UNKNOWN_CRITICAL_EXTENSION;
|
|
continue;
|
|
}
|
|
|
|
if(p)
|
|
{
|
|
if ((!p->fCA) && (iCert > 0) && (iCert < cChain-1)) {
|
|
rgdwErrors[iCert] |= CERT_VALIDITY_OTHER_EXTENSION_FAILURE;
|
|
}
|
|
|
|
if (p->fPathLenConstraint) {
|
|
if (p->dwPathLenConstraint+1 < iCert) {
|
|
rgdwErrors[iCert] |= CERT_VALIDITY_OTHER_EXTENSION_FAILURE;
|
|
}
|
|
}
|
|
|
|
free(p);
|
|
}
|
|
}
|
|
else if (strcmp(rgExt[iExt].pszObjId, szOID_KEY_USAGE) == 0) {
|
|
PCERT_KEY_ATTRIBUTES_INFO p;
|
|
p = (PCERT_KEY_ATTRIBUTES_INFO)
|
|
PVCryptDecode(rgExt[iExt].pszObjId,
|
|
rgExt[iExt].Value.cbData,
|
|
rgExt[iExt].Value.pbData);
|
|
if ((p == NULL) && rgExt[iExt].fCritical) {
|
|
rgdwErrors[iCert] |= CERT_VALIDITY_KEY_USAGE_EXT_FAILURE;
|
|
continue;
|
|
}
|
|
|
|
if(p)
|
|
{
|
|
if (p->IntendedKeyUsage.cbData >= 1) {
|
|
if (iCert != 0) {
|
|
#if 0
|
|
if (!((*p->IntendedKeyUsage.pbData) & CERT_KEY_CERT_SIGN_KEY_USAGE)) {
|
|
rgdwErrors[iCert] |= CERT_VALIDITY_KEY_USAGE_EXT_FAILURE;
|
|
}
|
|
#endif // 0
|
|
}
|
|
}
|
|
free(p);
|
|
}
|
|
}
|
|
else if ((strcmp(rgExt[iExt].pszObjId, szOID_SUBJECT_ALT_NAME2) == 0) ||
|
|
(strcmp(rgExt[iExt].pszObjId, szOID_CRL_DIST_POINTS) == 0)/* ||
|
|
(strcmp(rgExt[iExt].pszObjId, szOID_CERT_POLICIES) == 0) ||
|
|
(strcmp(rgExt[iExt].pszObjId, "2.5.29.30") == 0) ||
|
|
(strcmp(rgExt[iExt].pszObjId, "2.5.29.36") == 0)*/) {
|
|
;
|
|
}
|
|
else if (rgExt[iExt].fCritical) {
|
|
rgdwErrors[iCert] |= CERT_VALIDITY_UNKNOWN_CRITICAL_EXTENSION;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we have a CTL for this cert, and we have not already done so
|
|
// then process the extensions that it has.
|
|
//
|
|
|
|
if ((ptcert->pTrustListContext != NULL) &&
|
|
(rgExt != ptcert->pTrustListContext->pCtlInfo->rgExtension)) {
|
|
cExt = ptcert->pTrustListContext->pCtlInfo->cExtension;
|
|
rgExt = ptcert->pTrustListContext->pCtlInfo->rgExtension;
|
|
goto ProcessExtensions;
|
|
}
|
|
|
|
//
|
|
// Need to support turn off of certificates
|
|
//
|
|
|
|
if (CertGetEnhancedKeyUsage(ptcert->pCert, CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG,
|
|
NULL, &cbData)) {
|
|
BOOL fFound = FALSE;
|
|
PCERT_ENHKEY_USAGE pUsage;
|
|
pUsage = (PCERT_ENHKEY_USAGE) malloc(cbData);
|
|
CertGetEnhancedKeyUsage(ptcert->pCert, CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG,
|
|
pUsage, &cbData);
|
|
|
|
for (i=0; i<pUsage->cUsageIdentifier; i++) {
|
|
if ((pszUsage != NULL) &&
|
|
strcmp(pUsage->rgpszUsageIdentifier[i], pszUsage) == 0) {
|
|
fFound = TRUE;
|
|
}
|
|
else if (strcmp(pUsage->rgpszUsageIdentifier[i], szOID_YESNO_TRUST_ATTR) == 0) {
|
|
rgdwErrors[iCert] |= CERT_VALIDITY_OTHER_EXTENSION_FAILURE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ((pszUsage != NULL) && !fFound) {
|
|
rgdwErrors[iCert] |= CERT_VALIDITY_EXTENDED_USAGE_FAILURE;
|
|
}
|
|
|
|
free(pUsage);
|
|
}
|
|
|
|
//
|
|
// If we jump past a CTL, then we need to change our purpose
|
|
//
|
|
|
|
if (ptcert->pCtlContext != NULL) {
|
|
pszUsage = SzOID_KP_CTL_USAGE_SIGNING;
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
//// GetErrorFromCert
|
|
//
|
|
// Description:
|
|
// Get the internal error from the provider certificate
|
|
//
|
|
DWORD GetErrorFromCert(PCRYPT_PROVIDER_CERT pCryptProviderCert)
|
|
{
|
|
//
|
|
// if this is true then the cert is OK
|
|
//
|
|
if ((pCryptProviderCert->dwError == 0) &&
|
|
(pCryptProviderCert->dwConfidence & CERT_CONFIDENCE_SIG) &&
|
|
(pCryptProviderCert->dwConfidence & CERT_CONFIDENCE_TIME) &&
|
|
(pCryptProviderCert->dwConfidence & CERT_CONFIDENCE_TIMENEST) &&
|
|
(!pCryptProviderCert->fIsCyclic))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
|
|
if (pCryptProviderCert->dwError == CERT_E_REVOKED)
|
|
{
|
|
return CERT_VALIDITY_CERTIFICATE_REVOKED;
|
|
}
|
|
else if (pCryptProviderCert->dwError == CERT_E_REVOCATION_FAILURE)
|
|
{
|
|
return CERT_VALIDITY_NO_CRL_FOUND;
|
|
}
|
|
else if (!(pCryptProviderCert->dwConfidence & CERT_CONFIDENCE_SIG) ||
|
|
(pCryptProviderCert->dwError == TRUST_E_CERT_SIGNATURE))
|
|
{
|
|
return CERT_VALIDITY_SIGNATURE_FAILS;
|
|
}
|
|
else if (!(pCryptProviderCert->dwConfidence & CERT_CONFIDENCE_TIME) ||
|
|
(pCryptProviderCert->dwError == CERT_E_EXPIRED))
|
|
{
|
|
return CERT_VALIDITY_AFTER_END;
|
|
}
|
|
else if (!(pCryptProviderCert->dwConfidence & CERT_CONFIDENCE_TIMENEST) ||
|
|
(pCryptProviderCert->dwError == CERT_E_VALIDITYPERIODNESTING))
|
|
{
|
|
return CERT_VALIDITY_PERIOD_NESTING_FAILURE;
|
|
}
|
|
else if (pCryptProviderCert->dwError == CERT_E_WRONG_USAGE)
|
|
{
|
|
return CERT_VALIDITY_KEY_USAGE_EXT_FAILURE;
|
|
}
|
|
else if (pCryptProviderCert->dwError == TRUST_E_BASIC_CONSTRAINTS)
|
|
{
|
|
return CERT_VALIDITY_OTHER_EXTENSION_FAILURE;
|
|
}
|
|
else if (pCryptProviderCert->dwError == CERT_E_PURPOSE)
|
|
{
|
|
return CERT_VALIDITY_EXTENDED_USAGE_FAILURE;
|
|
}
|
|
else if (pCryptProviderCert->dwError == CERT_E_CHAINING)
|
|
{
|
|
return CERT_VALIDITY_NO_ISSUER_CERT_FOUND;
|
|
}
|
|
else if (pCryptProviderCert->dwError == TRUST_E_EXPLICIT_DISTRUST)
|
|
{
|
|
return CERT_VALIDITY_EXPLICITLY_DISTRUSTED;
|
|
}
|
|
else if (pCryptProviderCert->fIsCyclic)
|
|
{
|
|
return CERT_VALIDITY_ISSUER_INVALID;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// this is not an error we know about, so call return general error
|
|
//
|
|
return CERT_VALIDITY_OTHER_ERROR;
|
|
}
|
|
|
|
}
|
|
|
|
//// MapErrorToTrustError
|
|
//
|
|
// Description:
|
|
// Map the internal error value to a WINTRUST recognized value.
|
|
// The CryptUI dialogs recognize these values:
|
|
// CERT_E_UNTRUSTEDROOT
|
|
// CERT_E_REVOKED
|
|
// TRUST_E_CERT_SIGNATURE
|
|
// CERT_E_EXPIRED
|
|
// CERT_E_VALIDITYPERIODNESTING
|
|
// CERT_E_WRONG_USAGE
|
|
// TRUST_E_BASIC_CONSTRAINTS
|
|
// CERT_E_PURPOSE
|
|
// CERT_E_REVOCATION_FAILURE
|
|
// CERT_E_CHAINING - this is set if a full chain cannot be built
|
|
//
|
|
//
|
|
HRESULT MapErrorToTrustError(DWORD dwInternalError) {
|
|
HRESULT hrWinTrustError = S_OK;
|
|
|
|
// Look at them in decreasing order of importance
|
|
if (dwInternalError) {
|
|
if (dwInternalError & CERT_VALIDITY_EXPLICITLY_DISTRUSTED) {
|
|
hrWinTrustError = /*TRUST_E_EXPLICIT_DISTRUST*/ 0x800B0111;
|
|
} else if (dwInternalError & CERT_VALIDITY_SIGNATURE_FAILS) {
|
|
hrWinTrustError = TRUST_E_CERT_SIGNATURE;
|
|
} else if (dwInternalError & CERT_VALIDITY_CERTIFICATE_REVOKED) {
|
|
hrWinTrustError = CERT_E_REVOKED;
|
|
} else if (dwInternalError & CERT_VALIDITY_BEFORE_START || dwInternalError & CERT_VALIDITY_AFTER_END) {
|
|
hrWinTrustError = CERT_E_EXPIRED;
|
|
} else if (dwInternalError & CERT_VALIDITY_ISSUER_DISTRUST) {
|
|
hrWinTrustError = CERT_E_UNTRUSTEDROOT;
|
|
} else if (dwInternalError & CERT_VALIDITY_ISSUER_INVALID) {
|
|
hrWinTrustError = CERT_E_UNTRUSTEDROOT;
|
|
} else if (dwInternalError & CERT_VALIDITY_NO_ISSUER_CERT_FOUND) {
|
|
hrWinTrustError = CERT_E_CHAINING;
|
|
} else if (dwInternalError & CERT_VALIDITY_NO_CRL_FOUND) {
|
|
hrWinTrustError = CERT_E_REVOCATION_FAILURE;
|
|
} else if (dwInternalError & CERT_VALIDITY_PERIOD_NESTING_FAILURE) {
|
|
hrWinTrustError = CERT_E_VALIDITYPERIODNESTING;
|
|
} else if (dwInternalError & CERT_VALIDITY_EXTENDED_USAGE_FAILURE) {
|
|
hrWinTrustError = CERT_E_WRONG_USAGE;
|
|
} else if (dwInternalError & CERT_VALIDITY_OTHER_ERROR) {
|
|
hrWinTrustError = TRUST_E_FAIL; // BUGBUG: Will this give a good error?;
|
|
} else if (dwInternalError & CERT_VALIDITY_NO_TRUST_DATA) {
|
|
hrWinTrustError = CERT_E_UNTRUSTEDROOT;
|
|
} else {
|
|
hrWinTrustError = TRUST_E_FAIL; // BUGBUG: Will this give a good error?;
|
|
}
|
|
}
|
|
|
|
// CERT_E_UNTRUSTEDROOT
|
|
// CERT_E_REVOKED
|
|
// TRUST_E_CERT_SIGNATURE
|
|
// CERT_E_EXPIRED
|
|
// X CERT_E_VALIDITYPERIODNESTING
|
|
// X CERT_E_WRONG_USAGE
|
|
// TRUST_E_BASIC_CONSTRAINTS
|
|
// CERT_E_PURPOSE
|
|
// CERT_E_REVOCATION_FAILURE What is this?
|
|
// CERT_E_CHAINING - this is set if a full chain cannot be built
|
|
|
|
|
|
return(hrWinTrustError);
|
|
}
|
|
|
|
|
|
|
|
//// CertTrustFinalPolicy
|
|
//
|
|
// Description:
|
|
// This code does the enforcement of all restrictions on the certificate
|
|
// chain that we understand.
|
|
//
|
|
|
|
HRESULT CertTrustFinalPolicy(PCRYPT_PROVIDER_DATA pdata)
|
|
{
|
|
int cChain = 0;
|
|
DWORD dwFlags;
|
|
FILETIME ftZero = {0, 0};
|
|
HRESULT hr;
|
|
HRESULT hrStepError = S_OK;
|
|
int i;
|
|
DWORD j;
|
|
PCERT_VERIFY_CERTIFICATE_TRUST pcerttrust;
|
|
PINTERNAL_DATA pmydata;
|
|
PCRYPT_PROVIDER_PRIVDATA pprivdata;
|
|
PCRYPT_PROVIDER_SGNR psigner;
|
|
PCRYPT_PROVIDER_CERT ptcert = NULL;
|
|
PCRYPT_PROVIDER_CERT ptcertIssuer;
|
|
DATA_BLOB * rgblobTrustInfo = NULL;
|
|
LPBYTE rgbTrust = NULL;
|
|
DWORD * rgdwErrors = NULL;
|
|
PCCERT_CONTEXT * rgpccertChain = NULL;
|
|
int iExplicitlyTrusted;
|
|
|
|
//
|
|
// Make sure all of the fields we want are there. If not then it is a
|
|
// complete fatal error.
|
|
//
|
|
|
|
|
|
if (! WVT_ISINSTRUCT(CRYPT_PROVIDER_DATA, pdata->cbStruct, pszUsageOID)) {
|
|
hr = E_INVALIDARG;
|
|
goto XXX;
|
|
}
|
|
|
|
if (pdata->pWintrustData->pBlob->cbStruct < sizeof(WINTRUST_BLOB_INFO)) {
|
|
hr = E_INVALIDARG;
|
|
goto XXX;
|
|
}
|
|
|
|
pcerttrust = (PCERT_VERIFY_CERTIFICATE_TRUST)
|
|
pdata->pWintrustData->pBlob->pbMemObject;
|
|
if ((pcerttrust == NULL) ||
|
|
(pcerttrust->cbSize < sizeof(*pcerttrust))) {
|
|
hr = E_INVALIDARG;
|
|
goto XXX;
|
|
}
|
|
|
|
//
|
|
// Get our internal data structure
|
|
//
|
|
|
|
pprivdata = WTHelperGetProvPrivateDataFromChain(pdata, (LPGUID) &MyGuid);
|
|
if (pprivdata == NULL) {
|
|
hr = E_INVALIDARG;
|
|
goto XXX;
|
|
}
|
|
|
|
pmydata = (PINTERNAL_DATA) pprivdata->pvProvData;
|
|
|
|
//
|
|
// Check for an existing error -- If so then skip to any UI
|
|
//
|
|
|
|
if (pdata->dwError != 0) {
|
|
hr = pdata->dwError;
|
|
goto XXX;
|
|
}
|
|
|
|
for (i=TRUSTERROR_STEP_FINAL_WVTINIT; i<TRUSTERROR_STEP_FINAL_POLICYPROV; i++) {
|
|
if (pdata->padwTrustStepErrors[i] != 0) {
|
|
// for these errors we still want to continue processing
|
|
if ((TRUST_E_CERT_SIGNATURE == pdata->padwTrustStepErrors[i]) ||
|
|
(CERT_E_REVOKED == pdata->padwTrustStepErrors[i]) ||
|
|
(CERT_E_REVOCATION_FAILURE == pdata->padwTrustStepErrors[i])) {
|
|
hrStepError = pdata->padwTrustStepErrors[i];
|
|
}
|
|
else {
|
|
hr = pdata->padwTrustStepErrors[i];
|
|
goto XXX;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// We only work with a single signer --
|
|
|
|
psigner = WTHelperGetProvSignerFromChain(pdata, 0, FALSE, 0);
|
|
if (psigner == NULL) {
|
|
hr = E_INVALIDARG;
|
|
goto XXX;
|
|
}
|
|
|
|
//
|
|
// If we are not getting a full chain, then build up the set of
|
|
// certs and pass it to the validator
|
|
//
|
|
|
|
if (!(pmydata->dwFlags & CERT_TRUST_DO_FULL_SEARCH)) {
|
|
|
|
}
|
|
|
|
//
|
|
// We are going to compute some return values at this point
|
|
// either a full chain or a full chain with complete trust.
|
|
//
|
|
// Allocate space to return the chain of certs back to the caller.
|
|
// We allocate space for the full chain, even though we may not
|
|
// actually use it.
|
|
//
|
|
|
|
cChain = psigner->csCertChain;
|
|
|
|
rgpccertChain = (PCCERT_CONTEXT *) LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT,
|
|
cChain * sizeof(PCCERT_CONTEXT));
|
|
rgdwErrors = (DWORD *) LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, cChain * sizeof(DWORD));
|
|
rgblobTrustInfo = (DATA_BLOB *) LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, cChain * sizeof(DATA_BLOB));
|
|
rgbTrust = (BYTE *) LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, cChain*sizeof(BLOB));
|
|
|
|
if ((rgpccertChain == NULL) || (rgdwErrors == NULL) ||
|
|
(rgblobTrustInfo == NULL) || (rgbTrust == NULL)) {
|
|
hr = E_OUTOFMEMORY;
|
|
goto XXX;
|
|
}
|
|
|
|
//
|
|
// Build the first set of returned values. At this
|
|
// point we are creating the arrays to return both
|
|
// the relevent trust information and the chain of
|
|
// certificates in
|
|
//
|
|
|
|
for (i=0; i<cChain; i++) {
|
|
//
|
|
// Start by duplicating the certificate into the return
|
|
// location
|
|
//
|
|
|
|
ptcert = WTHelperGetProvCertFromChain(psigner, i);
|
|
rgpccertChain[i] = CertDuplicateCertificateContext(ptcert->pCert);
|
|
if (i < cChain-1) {
|
|
ptcertIssuer = WTHelperGetProvCertFromChain(psigner, i+1);
|
|
}
|
|
else {
|
|
ptcertIssuer = NULL;
|
|
if (!ptcert->fSelfSigned) {
|
|
rgdwErrors[i] |= CERT_VALIDITY_NO_ISSUER_CERT_FOUND;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check for some simple items
|
|
//
|
|
|
|
dwFlags = CertVerifyTimeValidityWithDelta(NULL,
|
|
ptcert->pCert->pCertInfo,
|
|
TIME_DELTA_SECONDS);
|
|
if (((LONG)dwFlags) < 0) {
|
|
rgdwErrors[i] |= CERT_VALIDITY_BEFORE_START;
|
|
}
|
|
else if (dwFlags > 0) {
|
|
if (!ptcert->fTrustListSignerCert ||
|
|
memcmp(&ptcert->pCert->pCertInfo->NotAfter, &ftZero,
|
|
sizeof(ftZero)) != 0) {
|
|
rgdwErrors[i] |= CERT_VALIDITY_AFTER_END;
|
|
}
|
|
else {
|
|
if (!(ptcert->dwConfidence & CERT_CONFIDENCE_TIME)) {
|
|
ptcert->dwConfidence |= CERT_CONFIDENCE_TIME;
|
|
}
|
|
if (ptcert->dwError == CERT_E_EXPIRED) {
|
|
ptcert->dwError = S_OK;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Check for error in Certificate
|
|
//
|
|
rgdwErrors[i] |= GetErrorFromCert(ptcert);
|
|
|
|
//
|
|
// If we find an issuer, then lets go after the questions we have
|
|
// about CRLs.
|
|
//
|
|
// Question -- do we get everything here? How do we know if the
|
|
// CRL is out of date.
|
|
//
|
|
|
|
if ((ptcertIssuer != NULL) && (ptcert->pCtlContext == NULL)) {
|
|
dwFlags = CERT_STORE_SIGNATURE_FLAG;
|
|
if ((pdata->dwProvFlags & CPD_REVOCATION_CHECK_NONE) ||
|
|
(psigner->pChainContext == NULL))
|
|
dwFlags |= CERT_STORE_REVOCATION_FLAG;
|
|
if (CertVerifySubjectCertificateContext(ptcert->pCert, ptcertIssuer->pCert, &dwFlags) &&
|
|
(dwFlags != 0)) {
|
|
if (dwFlags & CERT_STORE_SIGNATURE_FLAG) {
|
|
rgdwErrors[i] |= CERT_VALIDITY_SIGNATURE_FAILS;
|
|
}
|
|
if (dwFlags & CERT_STORE_REVOCATION_FLAG) {
|
|
if (dwFlags & CERT_STORE_NO_CRL_FLAG) {
|
|
rgdwErrors[i] |= CERT_VALIDITY_NO_CRL_FOUND;
|
|
}
|
|
else {
|
|
rgdwErrors[i] |= CERT_VALIDITY_CERTIFICATE_REVOKED;
|
|
}
|
|
}
|
|
}
|
|
|
|
// If both CRL not found and revoked are set, choose the most
|
|
// conservative state- the cert is revoked.
|
|
|
|
if ((CERT_VALIDITY_NO_CRL_FOUND & rgdwErrors[i]) &&
|
|
(CERT_VALIDITY_CERTIFICATE_REVOKED & rgdwErrors[i])) {
|
|
rgdwErrors[i] &= ~CERT_VALIDITY_NO_CRL_FOUND;
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
if (ptcert->fTrustedRoot) {
|
|
rgblobTrustInfo[i].cbData = 0;
|
|
rgblobTrustInfo[i].pbData = (LPBYTE) 1;
|
|
rgbTrust[i] = 1;
|
|
}
|
|
else if (ptcert->pTrustListContext != NULL) {
|
|
CRYPT_ATTRIBUTES attrs;
|
|
DWORD cbData;
|
|
BOOL f;
|
|
LPBYTE pb;
|
|
PCTL_ENTRY pctlentry;
|
|
|
|
pctlentry = CertFindSubjectInCTL(X509_ASN_ENCODING, CTL_CERT_SUBJECT_TYPE,
|
|
(LPVOID) ptcert->pCert, ptcert->pTrustListContext,
|
|
0);
|
|
attrs.cAttr = pctlentry->cAttribute;
|
|
attrs.rgAttr = pctlentry->rgAttribute;
|
|
|
|
for (j=0; j<attrs.cAttr; j++) {
|
|
if ((strcmp(attrs.rgAttr[j].pszObjId, SzOID_CTL_ATTR_YESNO_TRUST) == 0) ||
|
|
(strcmp(attrs.rgAttr[j].pszObjId, SzOID_OLD_CTL_YESNO_TRUST) == 0)) {
|
|
|
|
if ((attrs.rgAttr[j].rgValue[0].cbData == sizeof(RgbTrustYes)) &&
|
|
(memcmp(attrs.rgAttr[j].rgValue[0].pbData,
|
|
RgbTrustYes, sizeof(RgbTrustYes)) == 0)) {
|
|
rgbTrust[i] = 2;
|
|
break;
|
|
}
|
|
else if ((attrs.rgAttr[j].rgValue[0].cbData == sizeof(RgbTrustNo)) &&
|
|
(memcmp(attrs.rgAttr[j].rgValue[0].pbData,
|
|
RgbTrustNo, sizeof(RgbTrustNo)) == 0)) {
|
|
rgdwErrors[i] |= CERT_VALIDITY_EXPLICITLY_DISTRUSTED;
|
|
rgbTrust[i] = (BYTE) -1;
|
|
break;
|
|
}
|
|
else if ((attrs.rgAttr[j].rgValue[0].cbData == sizeof(RgbTrustParent)) &&
|
|
(memcmp(attrs.rgAttr[j].rgValue[0].pbData,
|
|
RgbTrustParent, sizeof(RgbTrustParent)) == 0)) {
|
|
rgbTrust[i] = 0;
|
|
break;
|
|
}
|
|
else {
|
|
rgdwErrors[i] |= CERT_VALIDITY_NO_TRUST_DATA;
|
|
rgbTrust[i] = (BYTE) -2;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (j == attrs.cAttr) {
|
|
rgbTrust[i] = 0;
|
|
}
|
|
|
|
f = CryptEncodeObject(X509_ASN_ENCODING, "1.3.6.1.4.1.311.16.1.1",
|
|
&attrs, NULL, &cbData);
|
|
if (f && (cbData != 0)) {
|
|
pb = (LPBYTE) LocalAlloc(LMEM_FIXED, cbData);
|
|
if (pb != NULL) {
|
|
f = CryptEncodeObject(X509_ASN_ENCODING, "1.3.6.1.4.1.311.16.1.1",
|
|
&attrs, pb, &cbData);
|
|
rgblobTrustInfo[i].cbData = cbData;
|
|
rgblobTrustInfo[i].pbData = pb;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
//
|
|
//
|
|
|
|
DWORD rgiCert[32];
|
|
for (i=0; i<cChain; i++) {
|
|
rgiCert[i] = i;
|
|
}
|
|
|
|
//
|
|
// Apply policies to it
|
|
//
|
|
|
|
hr = HrCheckPolicies(psigner, cChain, rgdwErrors, pcerttrust->pszUsageOid);
|
|
if (FAILED(hr)) {
|
|
goto XXX;
|
|
}
|
|
|
|
//
|
|
// Mask out the bits which the caller says are un-important
|
|
//
|
|
|
|
if (pcerttrust->pdwErrors != NULL) {
|
|
*pcerttrust->pdwErrors = 0;
|
|
}
|
|
|
|
// Find the lowest index explicitly trusted cert in chain
|
|
iExplicitlyTrusted = cChain; // > index of root cert
|
|
for (i = 0; i < cChain; i++) {
|
|
if (rgbTrust[i] == 2) {
|
|
iExplicitlyTrusted = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (i=cChain-1; i>=0; i--) {
|
|
//
|
|
// Build a better idea of basic trust
|
|
//
|
|
|
|
switch (rgbTrust[i]) {
|
|
// We are explicity trusted -- clear out any trust errors which may
|
|
// have been located for this certificate They are no longer
|
|
// relevent.
|
|
case 1: // Explicitly trusted (root)
|
|
case 2: // Explicitly trusted (CTL)
|
|
rgdwErrors[i] &= ~(CERT_VALIDITY_MASK_TRUST |
|
|
CERT_VALIDITY_CERTIFICATE_REVOKED);
|
|
break;
|
|
|
|
case -2: // Unknown CTL data
|
|
case -1: // Explicitly distrusted (CTL)
|
|
rgdwErrors[i] |= CERT_VALIDITY_EXPLICITLY_DISTRUSTED;
|
|
break;
|
|
|
|
case 0: // Chain trusted (CTL or no CTL)
|
|
if (i == cChain-1) {
|
|
rgdwErrors[i] |= CERT_VALIDITY_NO_TRUST_DATA;
|
|
}
|
|
break;
|
|
}
|
|
|
|
rgdwErrors[i] &= ~pcerttrust->dwIgnoreErr;
|
|
|
|
if (i > 0) {
|
|
if (rgdwErrors[i] & CERT_VALIDITY_MASK_VALIDITY) {
|
|
rgdwErrors[i-1] |= CERT_VALIDITY_ISSUER_INVALID;
|
|
}
|
|
if (rgdwErrors[i] & CERT_VALIDITY_MASK_TRUST) {
|
|
rgdwErrors[i-1] |= CERT_VALIDITY_ISSUER_DISTRUST;
|
|
}
|
|
}
|
|
|
|
if (pcerttrust->pdwErrors != NULL) {
|
|
DWORD dwThisTrust = rgdwErrors[i];
|
|
|
|
if (i >= iExplicitlyTrusted) {
|
|
// If we have an explicitly trusted cert or one of it's issuer's,
|
|
// we assume trust all the way up the chain.
|
|
dwThisTrust &= ~(CERT_VALIDITY_MASK_TRUST | CERT_VALIDITY_CERTIFICATE_REVOKED);
|
|
}
|
|
|
|
*pcerttrust->pdwErrors |= dwThisTrust;
|
|
}
|
|
|
|
// Set the trust state for this chain cert
|
|
if (WVT_ISINSTRUCT(CRYPT_PROVIDER_CERT, ptcert->cbStruct, dwError)) {
|
|
ptcert = WTHelperGetProvCertFromChain(psigner, i);
|
|
ptcert->dwError = (DWORD)MapErrorToTrustError(rgdwErrors[i]);
|
|
}
|
|
}
|
|
|
|
if (rgdwErrors[0] != 0) {
|
|
hr = S_FALSE;
|
|
}
|
|
else {
|
|
hr = S_OK;
|
|
}
|
|
|
|
if (WVT_ISINSTRUCT(CRYPT_PROVIDER_DATA, pdata->cbStruct, dwFinalError)) {
|
|
pdata->dwFinalError = (DWORD)MapErrorToTrustError(rgdwErrors[0]);
|
|
|
|
if (pdata->dwFinalError) {
|
|
// Assert(hr != S_OK);
|
|
}
|
|
}
|
|
|
|
//
|
|
// We have succeded and are finished.
|
|
// Setup the return values
|
|
//
|
|
|
|
if (pcerttrust->pcChain != NULL) {
|
|
*pcerttrust->pcChain = cChain;
|
|
}
|
|
if (pcerttrust->prgChain != NULL) {
|
|
*pcerttrust->prgChain = rgpccertChain;
|
|
rgpccertChain = NULL;
|
|
}
|
|
if (pcerttrust->prgdwErrors != NULL) {
|
|
*pcerttrust->prgdwErrors = rgdwErrors;
|
|
rgdwErrors = NULL;
|
|
}
|
|
if (pcerttrust->prgpbTrustInfo != NULL) {
|
|
*pcerttrust->prgpbTrustInfo = rgblobTrustInfo;
|
|
rgblobTrustInfo = NULL;
|
|
}
|
|
|
|
XXX:
|
|
if (rgpccertChain != NULL) {
|
|
for (i=0; i<cChain; i++) {
|
|
CertFreeCertificateContext(rgpccertChain[i]);
|
|
}
|
|
LocalFree(rgpccertChain);
|
|
}
|
|
if (rgdwErrors != NULL) LocalFree(rgdwErrors);
|
|
if (rgblobTrustInfo != NULL) {
|
|
for (i=0; i<cChain; i++) {
|
|
if (rgblobTrustInfo[i].cbData > 0) {
|
|
LocalFree(rgblobTrustInfo[i].pbData);
|
|
}
|
|
}
|
|
LocalFree(rgblobTrustInfo);
|
|
}
|
|
if (rgbTrust != NULL) LocalFree(rgbTrust);
|
|
// If everything worked then reurn any step error we ignored
|
|
if (FAILED(hrStepError) && SUCCEEDED(hr))
|
|
hr = hrStepError;
|
|
return hr;
|
|
}
|
|
|
|
//// CertTrustCleanup
|
|
//
|
|
// Description:
|
|
// This code knows how to cleanup all allocated data we have.
|
|
//
|
|
|
|
HRESULT CertTrustCleanup(PCRYPT_PROVIDER_DATA pdata)
|
|
{
|
|
DWORD i;
|
|
PCRYPT_PROVIDER_PRIVDATA pprivdata;
|
|
PINTERNAL_DATA pmydata;
|
|
|
|
//
|
|
// Get our internal data structure
|
|
//
|
|
|
|
pprivdata = WTHelperGetProvPrivateDataFromChain(pdata, (LPGUID) &MyGuid);
|
|
if (pprivdata == NULL) {
|
|
return S_OK;
|
|
}
|
|
|
|
pmydata = (PINTERNAL_DATA) pprivdata->pvProvData;
|
|
|
|
//
|
|
// Release all of the stores we have cached here
|
|
//
|
|
|
|
for (i=0; i<pmydata->cRootStores; i++) {
|
|
CertCloseStore(pmydata->rghRootStores[i], 0);
|
|
}
|
|
|
|
for (i=0; i<pmydata->cTrustStores; i++) {
|
|
CertCloseStore(pmydata->rghTrustStores[i], 0);
|
|
}
|
|
|
|
//
|
|
// If we installed a default crypt context, uninstall it now
|
|
//
|
|
|
|
i = CryptUninstallDefaultContext(pmydata->hdefaultcontext, 0, NULL);
|
|
// Assert(i == TRUE);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
#else // !NT5BUILD
|
|
#pragma message("Building for IE")
|
|
|
|
//// HrCheckTrust
|
|
//
|
|
// Description:
|
|
//
|
|
|
|
HRESULT HrCheckTrust(PCCertFrame pcf, int iTrust)
|
|
{
|
|
HRESULT hr;
|
|
HRESULT hrRet = S_OK;
|
|
int i;
|
|
|
|
//
|
|
// If this node has trust information on it, then compute the trust at this
|
|
// point. If we either succeed or fail then return the indication.
|
|
//
|
|
|
|
if ((pcf->m_rgTrust != NULL) && (pcf->m_rgTrust[iTrust].szOid != NULL)) {
|
|
if (pcf->m_fRootStore) {
|
|
pcf->m_rgTrust[iTrust].fExplicitTrust = TRUE;
|
|
pcf->m_rgTrust[iTrust].fTrust = TRUE;
|
|
return S_OK;
|
|
}
|
|
if ((strcmp(pcf->m_rgTrust[iTrust].szOid, SzOID_CTL_ATTR_YESNO_TRUST) == 0) ||
|
|
(strcmp(pcf->m_rgTrust[iTrust].szOid, SzOID_OLD_CTL_YESNO_TRUST) == 0)){
|
|
if ((pcf->m_rgTrust[iTrust].cbTrustData == 3) &&
|
|
memcmp(pcf->m_rgTrust[iTrust].pbTrustData, RgbTrustYes, 3) == 0) {
|
|
pcf->m_rgTrust[iTrust].fExplicitTrust = TRUE;
|
|
pcf->m_rgTrust[iTrust].fTrust = TRUE;
|
|
return S_OK;
|
|
}
|
|
else if ((pcf->m_rgTrust[iTrust].cbTrustData == 3) &&
|
|
memcmp(pcf->m_rgTrust[iTrust].pbTrustData,
|
|
RgbTrustParent, 3) == 0) {
|
|
// Need to ceck with the parent to find out what is going on.
|
|
}
|
|
else {
|
|
// Assume it must be RgbTrustNo
|
|
pcf->m_rgTrust[iTrust].fExplicitDistrust = TRUE;
|
|
pcf->m_rgTrust[iTrust].fDistrust = TRUE;
|
|
return S_FALSE;
|
|
}
|
|
}
|
|
else {
|
|
pcf->m_rgTrust[iTrust].fError = TRUE;
|
|
return S_FALSE;
|
|
}
|
|
}
|
|
|
|
if (pcf->m_fRootStore) {
|
|
pcf->m_rgTrust[iTrust].fExplicitTrust = TRUE;
|
|
pcf->m_rgTrust[iTrust].fTrust = TRUE;
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// We are marked as inherit -- so start asking all of our parents
|
|
//
|
|
|
|
if (pcf->m_cParents == 0) {
|
|
// No parents -- so not trusted.
|
|
hrRet = S_FALSE;
|
|
}
|
|
else {
|
|
for (i=0; i<pcf->m_cParents; i++) {
|
|
hr = HrCheckTrust(pcf->m_rgpcfParents[i], iTrust);
|
|
if (FAILED(hr)) {
|
|
pcf->m_rgTrust[iTrust].fError = TRUE;
|
|
return hr;
|
|
}
|
|
pcf->m_rgTrust[iTrust].fTrust = pcf->m_rgpcfParents[i]->m_rgTrust[iTrust].fTrust;
|
|
pcf->m_rgTrust[iTrust].fDistrust = pcf->m_rgpcfParents[i]->m_rgTrust[iTrust].fDistrust;
|
|
if (hr != S_OK) {
|
|
hrRet = S_FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return hrRet;
|
|
}
|
|
|
|
//// HrCheckValidity
|
|
//
|
|
// Description:
|
|
// This function will walk through the tree of certificates looking
|
|
// for the invalid certificates. It masks in the fact that the issuer
|
|
// certificate is invalid as necessary
|
|
|
|
HRESULT HrCheckValidity(PCCertFrame pcf)
|
|
{
|
|
HRESULT hr;
|
|
int i;
|
|
|
|
//
|
|
// If we do not have an issuer certificate, then our parent cannot possibly
|
|
// be invalid
|
|
//
|
|
|
|
if (pcf->m_cParents > 0) {
|
|
for (i=0; i<pcf->m_cParents; i++) {
|
|
hr = HrCheckValidity(pcf->m_rgpcfParents[i]);
|
|
// Assert(SUCCEEDED(hr));
|
|
if (hr != S_OK) {
|
|
pcf->m_dwFlags |= CERT_VALIDITY_ISSUER_INVALID;
|
|
return hr;
|
|
}
|
|
}
|
|
}
|
|
return (pcf->m_dwFlags == 0) ? S_OK : S_FALSE;
|
|
}
|
|
|
|
//// HrPerformUserCheck
|
|
//
|
|
|
|
HRESULT HrPerformUserCheck(PCCertFrame pcf, BOOL fComplete,
|
|
DWORD * pcNodes, int iDepth, PCCertFrame * rgpcf)
|
|
{
|
|
int iTrust = 0;
|
|
|
|
//
|
|
// We can only deal with up to 20-nodes in the chain of trust
|
|
//
|
|
|
|
if (iDepth == 20) {
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
//
|
|
// Put our node in so we can do array process checking
|
|
//
|
|
|
|
rgpcf[iDepth] = pcf;
|
|
|
|
//
|
|
// Handle the case where we don't have a trust usage
|
|
// User should still have a chance to invalidate the cert
|
|
//
|
|
|
|
if (NULL == pcf->m_rgTrust) {
|
|
if (pcf->m_cParents != 0) {
|
|
return HrPerformUserCheck(pcf->m_rgpcfParents[0], fComplete, pcNodes,
|
|
iDepth+1, rgpcf);
|
|
}
|
|
*pcNodes = iDepth+1;
|
|
return S_OK;
|
|
}
|
|
|
|
//
|
|
// If trust is really bad -- don't bother asking, just fail
|
|
//
|
|
|
|
if (pcf->m_rgTrust[iTrust].fError) {
|
|
return E_FAIL;
|
|
}
|
|
|
|
//
|
|
// See what the trust on this node is, if we explicity trust the node
|
|
// then ask the user his option on the entire array
|
|
//
|
|
|
|
if (pcf->m_rgTrust[iTrust].fExplicitTrust) {
|
|
if (fComplete && (pcf->m_cParents != 0)) {
|
|
HrPerformUserCheck(pcf->m_rgpcfParents[0], fComplete, pcNodes,
|
|
iDepth+1, rgpcf);
|
|
}
|
|
else {
|
|
// M00TODO Insert check to the user's function at this point
|
|
|
|
// Found nirvana
|
|
*pcNodes = iDepth+1;
|
|
}
|
|
return S_OK;
|
|
}
|
|
|
|
if ((pcf->m_rgTrust[iTrust].fExplicitDistrust) ||
|
|
(pcf->m_rgTrust[iTrust].fDistrust)) {
|
|
if (fComplete && (pcf->m_cParents != 0)) {
|
|
HrPerformUserCheck(pcf->m_rgpcfParents[0], fComplete, pcNodes,
|
|
iDepth+1, rgpcf);
|
|
}
|
|
else {
|
|
// I don't think we should be here -- but the result is a NO trust
|
|
// decision
|
|
*pcNodes = iDepth+1;
|
|
}
|
|
return S_FALSE;
|
|
}
|
|
|
|
//
|
|
// If we get here -- we should have inheritence trust. Continue
|
|
// walking up the tree and make sure
|
|
//
|
|
|
|
if (pcf->m_cParents == 0) {
|
|
*pcNodes = iDepth+1;
|
|
return S_FALSE;
|
|
}
|
|
|
|
// M00BUG -- need to check multliple parents?
|
|
|
|
return HrPerformUserCheck(pcf->m_rgpcfParents[0], fComplete, pcNodes,
|
|
iDepth+1, rgpcf);
|
|
}
|
|
|
|
//// HrDoTrustWork
|
|
//
|
|
// Description:
|
|
// This function does the core code of determining if a certificate
|
|
// can be trusted. This needs to be moved to a WinTrust provider in
|
|
// the near future.
|
|
//
|
|
|
|
HRESULT HrDoTrustWork(PCCERT_CONTEXT pccertToCheck, DWORD dwControl,
|
|
DWORD dwValidityMask,
|
|
DWORD cPurposes, LPSTR * rgszPurposes, HCRYPTPROV hprov,
|
|
DWORD cRoots, HCERTSTORE * rgRoots,
|
|
DWORD cCAs, HCERTSTORE * rgCAs,
|
|
DWORD cTrust, HCERTSTORE * rgTrust,
|
|
PFNTRUSTHELPER /*pfn*/, DWORD /*lCustData*/,
|
|
PCCertFrame * ppcf, DWORD * pcNodes,
|
|
PCCertFrame * rgpcfResult,
|
|
HANDLE * phReturnStateData) // optional: return WinVerifyTrust state handle here
|
|
{
|
|
DWORD dwFlags;
|
|
HRESULT hr;
|
|
HCERTSTORE hstoreRoot=NULL;
|
|
HCERTSTORE hstoreTrust=NULL;
|
|
DWORD i;
|
|
int iParent;
|
|
PCCERT_CONTEXT pccert;
|
|
PCCERT_CONTEXT pccert2;
|
|
PCCertFrame pcf;
|
|
PCCertFrame pcf2;
|
|
PCCertFrame pcf3;
|
|
PCCertFrame pcfLeaf = NULL;
|
|
HCERTSTORE rghcertstore[COtherProviders+30] = {NULL};
|
|
|
|
|
|
Assert(!phReturnStateData); // How would I support this without the WinVerifyTrust call?
|
|
//
|
|
// We may need to open some stores at this point. Check to see if we do
|
|
// and open new stores as required.
|
|
//
|
|
|
|
// Check for Root stores
|
|
|
|
if (cRoots == 0) {
|
|
#ifndef WIN16
|
|
hstoreRoot = CertOpenStore(CERT_STORE_PROV_SYSTEM, X509_ASN_ENCODING,
|
|
hprov, CERT_SYSTEM_STORE_CURRENT_USER,
|
|
L"Root");
|
|
#else
|
|
hstoreRoot = CertOpenStore(CERT_STORE_PROV_SYSTEM, X509_ASN_ENCODING,
|
|
hprov, CERT_SYSTEM_STORE_CURRENT_USER,
|
|
"Root");
|
|
#endif // !WIN16
|
|
if (hstoreRoot == NULL) {
|
|
hr = E_FAIL;
|
|
goto ExitHere;
|
|
}
|
|
cRoots = 1;
|
|
rgRoots = &hstoreRoot;
|
|
}
|
|
|
|
// Check for Trust stores
|
|
|
|
if (cTrust == 0) {
|
|
#ifndef WIN16
|
|
hstoreTrust = CertOpenStore(CERT_STORE_PROV_SYSTEM, X509_ASN_ENCODING,
|
|
hprov, CERT_SYSTEM_STORE_CURRENT_USER,
|
|
L"Trust");
|
|
#else
|
|
hstoreTrust = CertOpenStore(CERT_STORE_PROV_SYSTEM, X509_ASN_ENCODING,
|
|
hprov, CERT_SYSTEM_STORE_CURRENT_USER,
|
|
"Trust");
|
|
#endif // !WIN16
|
|
if (hstoreTrust == NULL) {
|
|
hr = E_FAIL;
|
|
goto ExitHere;
|
|
}
|
|
cTrust = 1;
|
|
rgTrust = &hstoreTrust;
|
|
}
|
|
|
|
// Check for Random CA stores
|
|
|
|
for (i=0; i<cCAs; i++) {
|
|
rghcertstore[i] = CertDuplicateStore(rgCAs[i]);
|
|
}
|
|
|
|
if ((cCAs == 0) || (dwControl & CM_ADD_CERT_STORES)) {
|
|
for (i=0; i<COtherProviders; i++) {
|
|
rghcertstore[cCAs] = CertOpenStore(CERT_STORE_PROV_SYSTEM, X509_ASN_ENCODING,
|
|
hprov, CERT_SYSTEM_STORE_CURRENT_USER,
|
|
RgszProvider[i]);
|
|
if (rghcertstore[cCAs] == NULL) {
|
|
hr = E_FAIL;
|
|
goto ExitHere;
|
|
}
|
|
cCAs += 1;
|
|
}
|
|
}
|
|
|
|
rgCAs = rghcertstore;
|
|
|
|
//
|
|
// Find the graph of issuer nodes
|
|
//
|
|
|
|
pcfLeaf = new CCertFrame(pccertToCheck);
|
|
|
|
if(!pcfLeaf)
|
|
{
|
|
hr=E_OUTOFMEMORY;
|
|
goto ExitHere;
|
|
}
|
|
|
|
//
|
|
// Process every certificate that we found in the ancestor graph
|
|
//
|
|
|
|
for (pcf = pcfLeaf; pcf != NULL; pcf = pcf->m_pcfNext) {
|
|
//
|
|
// Check the time validity on the certificate
|
|
//
|
|
|
|
i = CertVerifyTimeValidityWithDelta(NULL, pcf->m_pccert->pCertInfo, TIME_DELTA_SECONDS);
|
|
if (((LONG)i) < 0) {
|
|
pcf->m_dwFlags |= CERT_VALIDITY_BEFORE_START;
|
|
}
|
|
else if (i > 0) {
|
|
pcf->m_dwFlags |= CERT_VALIDITY_AFTER_END;
|
|
}
|
|
|
|
//
|
|
// For Every Certificate Store we are going to search
|
|
//
|
|
|
|
for (i=0; i<cCAs+cRoots; i++) {
|
|
pccert2 = NULL;
|
|
do {
|
|
//
|
|
// Ask the store to find the next cert for us to examine
|
|
//
|
|
|
|
dwFlags = (CERT_STORE_SIGNATURE_FLAG |
|
|
CERT_STORE_REVOCATION_FLAG);
|
|
pccert = CertGetIssuerCertificateFromStore(
|
|
i < cRoots ? rgRoots[i] : rgCAs[i-cRoots],
|
|
pcf->m_pccert, pccert2,
|
|
&dwFlags);
|
|
|
|
//
|
|
// If no cert is found, then we should move to the next store
|
|
//
|
|
|
|
if (pccert == NULL) {
|
|
// Check to see if this cert was self-signed
|
|
if (GetLastError() == CRYPT_E_SELF_SIGNED) {
|
|
pcf->m_fSelfSign = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Deterimine all of the failue modes for the certificate
|
|
// validity.
|
|
//
|
|
// Start by looking for the ones that WinCrypt gives to
|
|
// us for free.
|
|
//
|
|
|
|
if (dwFlags != 0) {
|
|
if (dwFlags & CERT_STORE_SIGNATURE_FLAG) {
|
|
pcf->m_dwFlags |= CERT_VALIDITY_SIGNATURE_FAILS;
|
|
}
|
|
if (dwFlags & CERT_STORE_REVOCATION_FLAG) {
|
|
if (dwFlags & CERT_STORE_NO_CRL_FLAG) {
|
|
pcf->m_dwFlags |= CERT_VALIDITY_NO_CRL_FOUND;
|
|
}
|
|
else {
|
|
pcf->m_dwFlags |= CERT_VALIDITY_CERTIFICATE_REVOKED;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Setup to find the next possible parent, we may continue out
|
|
// of the loop at a later time
|
|
//
|
|
|
|
pccert2 = pccert;
|
|
|
|
//
|
|
// Check to see this cert is already a found parent of the
|
|
// current node
|
|
//
|
|
|
|
for (iParent = 0; iParent < pcf->m_cParents; iParent++) {
|
|
if (CertCompareCertificate(X509_ASN_ENCODING, pccert->pCertInfo,
|
|
pcf->m_rgpcfParents[iParent]->m_pccert->pCertInfo)){
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (iParent != pcf->m_cParents) {
|
|
// Duplicate found -- go to next possible parent
|
|
continue;
|
|
}
|
|
|
|
if (iParent == MaxCertificateParents) {
|
|
// To many parents -- go to next possible parent
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Build a node to hold the cert we found and shove it onto the
|
|
// end of the list. We want to eleminate work so combine
|
|
// the same nodes if found.
|
|
//
|
|
|
|
for (pcf3 = pcf2 = pcfLeaf; pcf2 != NULL;
|
|
pcf3 = pcf2, pcf2 = pcf2->m_pcfNext) {
|
|
if (CertCompareCertificate(X509_ASN_ENCODING,
|
|
pccert->pCertInfo,
|
|
pcf2->m_pccert->pCertInfo)) {
|
|
break;
|
|
}
|
|
}
|
|
if (pcf2 == NULL) {
|
|
pcf3->m_pcfNext = new CCertFrame(pccert);
|
|
if (pcf3->m_pcfNext == NULL) {
|
|
// Out of memory during processing -- do the best one
|
|
// can to deal with it.
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Add in the parent to the structure
|
|
//
|
|
|
|
pcf->m_rgpcfParents[pcf->m_cParents++] = pcf3->m_pcfNext;
|
|
if (i < cRoots) {
|
|
pcf3->m_pcfNext->m_fRootStore = TRUE;
|
|
}
|
|
}
|
|
} while (pccert2 != NULL);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Nix off errors the caller wants us to ignore
|
|
//
|
|
|
|
for (pcf = pcfLeaf; pcf != NULL; pcf = pcf->m_pcfNext) {
|
|
pcf->m_dwFlags &= dwValidityMask;
|
|
}
|
|
|
|
//
|
|
// Need to check the complete set of validity on all certificates.
|
|
//
|
|
|
|
hr = HrCheckValidity(pcfLeaf);
|
|
if (FAILED(hr)) {
|
|
goto ExitHere;
|
|
}
|
|
|
|
//
|
|
// If there are validity problems with the root cert, and we are not
|
|
// asked to do a compelete check. We are done and the operation was
|
|
// not successful.
|
|
//
|
|
|
|
if ((pcfLeaf->m_dwFlags != 0) && !(dwControl & CERT_TRUST_DO_FULL_SEARCH)) {
|
|
hr = S_FALSE;
|
|
*ppcf = pcfLeaf;
|
|
pcfLeaf = NULL; // don't want it freed
|
|
|
|
// BUGBUG: should we return at least the root in the chain var?
|
|
*pcNodes = 0;
|
|
goto ExitHere;
|
|
}
|
|
|
|
//
|
|
// Ok -- we have the graph of roots, now lets start looking for all of the
|
|
// different possible trust problems
|
|
//
|
|
|
|
if (cPurposes)
|
|
{
|
|
CTL_VERIFY_USAGE_PARA vup;
|
|
memset(&vup, 0, sizeof(vup));
|
|
vup.cbSize = sizeof(vup);
|
|
vup.cCtlStore = cTrust;
|
|
vup.rghCtlStore = rgTrust; // "TRUST"
|
|
vup.cSignerStore = cRoots;
|
|
vup.rghSignerStore = rgRoots; // "Roots"
|
|
|
|
CTL_VERIFY_USAGE_STATUS vus;
|
|
PCCTL_CONTEXT pctlTrust;
|
|
|
|
pctlTrust = NULL;
|
|
|
|
memset(&vus, 0, sizeof(vus));
|
|
vus.cbSize = sizeof(vus);
|
|
vus.ppCtl = &pctlTrust;
|
|
|
|
for (i=0; i<cPurposes; i++) {
|
|
CTL_USAGE ctlusage;
|
|
BOOL f;
|
|
|
|
ctlusage.cUsageIdentifier = 1;
|
|
ctlusage.rgpszUsageIdentifier = &rgszPurposes[i];
|
|
|
|
for (pcf = pcfLeaf; pcf != NULL; pcf = pcf->m_pcfNext) {
|
|
if (pcf->m_rgTrust == NULL) {
|
|
pcf->m_rgTrust = new STrustDesc[cPurposes];
|
|
if (pcf->m_rgTrust == NULL) {
|
|
continue;
|
|
}
|
|
memset(pcf->m_rgTrust, 0, cPurposes * sizeof(STrustDesc));
|
|
}
|
|
|
|
if (pcf->m_fRootStore) {
|
|
continue;
|
|
}
|
|
|
|
f = CertVerifyCTLUsage(X509_ASN_ENCODING, CTL_CERT_SUBJECT_TYPE,
|
|
(LPVOID) pcf->m_pccert, &ctlusage,
|
|
CERT_VERIFY_INHIBIT_CTL_UPDATE_FLAG |
|
|
CERT_VERIFY_NO_TIME_CHECK_FLAG |
|
|
CERT_VERIFY_TRUSTED_SIGNERS_FLAG, &vup, &vus);
|
|
if (f) {
|
|
PCTL_ENTRY pentry;
|
|
pentry = &pctlTrust->pCtlInfo->rgCTLEntry[vus.dwCtlEntryIndex];
|
|
pcf->m_rgTrust[i].szOid = _strdup(pentry->rgAttribute[0].pszObjId);
|
|
// Assert(pentry->rgAttribute[0].cAttr == 1);
|
|
pcf->m_rgTrust[i].cbTrustData =
|
|
pentry->rgAttribute[0].rgValue[0].cbData;
|
|
pcf->m_rgTrust[i].pbTrustData =
|
|
(LPBYTE) malloc(pcf->m_rgTrust[i].cbTrustData);
|
|
memcpy(pcf->m_rgTrust[i].pbTrustData,
|
|
pentry->rgAttribute[0].rgValue[0].pbData,
|
|
pentry->rgAttribute[0].rgValue[0].cbData);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// We have all of the data needed to make a trust decision. See if we
|
|
// do trust
|
|
//
|
|
|
|
if (cPurposes == 1) {
|
|
hr = HrCheckTrust(pcfLeaf, 0);
|
|
if (FAILED(hr)) {
|
|
goto ExitHere;
|
|
}
|
|
if ((hr == S_FALSE) && !(dwControl & CERT_TRUST_DO_FULL_SEARCH)) {
|
|
*pcNodes = 0;
|
|
pcfLeaf->m_dwFlags |= (CERT_VALIDITY_NO_TRUST_DATA & dwValidityMask);
|
|
*ppcf = pcfLeaf;
|
|
pcfLeaf = NULL;
|
|
goto ExitHere;
|
|
}
|
|
}
|
|
else {
|
|
for (i=0; i<cPurposes; i++) {
|
|
HrCheckTrust(pcfLeaf, i);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now let the user have his crack at the tree and build the final
|
|
// trust path at the same time. If the user did not provide a check
|
|
// function then all certs are acceptable
|
|
//
|
|
|
|
hr = HrPerformUserCheck(pcfLeaf, TRUE, pcNodes, 0, rgpcfResult);
|
|
if (FAILED(hr)) {
|
|
goto ExitHere;
|
|
}
|
|
|
|
*ppcf = pcfLeaf;
|
|
pcfLeaf = NULL;
|
|
|
|
//
|
|
// We jump here on a failure and fall in on success. Clean up the items we
|
|
// have created.
|
|
//
|
|
|
|
ExitHere:
|
|
if (hstoreRoot && rgRoots == &hstoreRoot) {
|
|
CertCloseStore(hstoreRoot, 0);
|
|
}
|
|
|
|
if (hstoreTrust && rgTrust == &hstoreTrust) {
|
|
CertCloseStore(hstoreTrust, 0);
|
|
}
|
|
|
|
if (rgCAs == rghcertstore) {
|
|
for (i=0; i<cCAs; i++) {
|
|
if (rgCAs[i] != NULL) {
|
|
CertCloseStore(rgCAs[i], 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pcfLeaf != NULL) {
|
|
delete pcfLeaf;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
//
|
|
// TRUST PROVIDER INTERFACE
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
VOID ClientUnload(LPVOID /*pTrustProviderInfo*/)
|
|
{
|
|
;
|
|
}
|
|
|
|
VOID SubmitCertificate(LPWIN_CERTIFICATE /*pCert*/)
|
|
{
|
|
;
|
|
}
|
|
|
|
//// VerifyTrust
|
|
//
|
|
// Description:
|
|
// This is the core program in a trust system.
|
|
//
|
|
|
|
HRESULT WINAPI VerifyTrust(HWND /*hwnd*/, GUID * pguid, LPVOID pv)
|
|
{
|
|
DWORD cFrames;
|
|
HRESULT hr;
|
|
DWORD i;
|
|
PCCertFrame pcfLeaf = NULL;
|
|
PCERT_VERIFY_CERTIFICATE_TRUST pinfo = (PCERT_VERIFY_CERTIFICATE_TRUST) pv;
|
|
DWORD * rgdwErrors = NULL;
|
|
LPBYTE * rgpbTrust = NULL;
|
|
PCCERT_CONTEXT * rgpccert = NULL;
|
|
PCCertFrame rgpcf[20];
|
|
|
|
//
|
|
// Ensuer that we got called appropriately for our data
|
|
//
|
|
|
|
if (memcmp(pguid, &GuidCertValidate, sizeof(GuidCertValidate)) != 0) {
|
|
return E_FAIL;
|
|
}
|
|
|
|
//
|
|
// Make sure we have some data to play with
|
|
//
|
|
|
|
if ((pinfo->cbSize != sizeof(*pinfo)) || (pinfo->pccert == NULL)) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
//
|
|
// Call the core trust routine to do all of the intersting work
|
|
//
|
|
|
|
hr = HrDoTrustWork(pinfo->pccert, pinfo->dwFlags, ~(pinfo->dwIgnoreErr),
|
|
(pinfo->pszUsageOid != NULL ? 1 : 0),
|
|
&pinfo->pszUsageOid, pinfo->hprov,
|
|
pinfo->cRootStores, pinfo->rghstoreRoots,
|
|
pinfo->cStores, pinfo->rghstoreCAs,
|
|
pinfo->cTrustStores, pinfo->rghstoreTrust,
|
|
pinfo->pfnTrustHelper, pinfo->lCustData, &pcfLeaf,
|
|
&cFrames, rgpcf, NULL);
|
|
if (FAILED(hr)) {
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// We succeeded in getting some type of answer from the trust system, so
|
|
// format and return answers if any are requested.
|
|
//
|
|
|
|
if (pinfo->pdwErrors != NULL) {
|
|
*pinfo->pdwErrors = pcfLeaf->m_dwFlags;
|
|
}
|
|
|
|
if (pinfo->pcChain != NULL) {
|
|
*pinfo->pcChain = cFrames;
|
|
}
|
|
|
|
if (pinfo->prgChain != NULL) {
|
|
rgpccert = (PCCERT_CONTEXT*) LocalAlloc(LMEM_FIXED,
|
|
cFrames * sizeof(PCCERT_CONTEXT));
|
|
if (rgpccert == NULL) {
|
|
hr = E_OUTOFMEMORY;
|
|
goto ExitHere;
|
|
}
|
|
|
|
for (i=0; i<cFrames; i++) {
|
|
rgpccert[i] = CertDuplicateCertificateContext(rgpcf[i]->m_pccert);
|
|
}
|
|
}
|
|
|
|
if (pinfo->prgdwErrors != NULL) {
|
|
rgdwErrors = (DWORD *) LocalAlloc(LMEM_FIXED,
|
|
(*pinfo->pcChain)*sizeof(DWORD));
|
|
if (rgdwErrors == NULL) {
|
|
hr = E_OUTOFMEMORY;
|
|
goto ExitHere;
|
|
}
|
|
|
|
for (i=0; i<cFrames; i++) {
|
|
rgdwErrors[i] = rgpcf[i]->m_dwFlags;
|
|
}
|
|
}
|
|
|
|
if (pinfo->prgpbTrustInfo != NULL) {
|
|
rgpbTrust = (LPBYTE *) LocalAlloc(LMEM_FIXED,
|
|
(*pinfo->pcChain)*sizeof(LPBYTE));
|
|
if (rgpbTrust == NULL) {
|
|
hr = E_OUTOFMEMORY;
|
|
goto ExitHere;
|
|
}
|
|
|
|
rgpbTrust[0] = NULL;
|
|
}
|
|
|
|
ExitHere:
|
|
if (FAILED(hr)) {
|
|
#ifndef WIN16
|
|
if (rgpccert != NULL) LocalFree(rgpccert);
|
|
if (rgpbTrust != NULL) LocalFree(rgpbTrust);
|
|
if (rgdwErrors != NULL) LocalFree(rgdwErrors);
|
|
#else
|
|
if (rgpccert != NULL) LocalFree((HLOCAL)rgpccert);
|
|
if (rgpbTrust != NULL) LocalFree((HLOCAL)rgpbTrust);
|
|
if (rgdwErrors != NULL) LocalFree((HLOCAL)rgdwErrors);
|
|
#endif // !WIN16
|
|
}
|
|
else {
|
|
if (rgpccert != NULL) *pinfo->prgChain = rgpccert;
|
|
if (rgdwErrors != NULL) *pinfo->prgdwErrors = rgdwErrors;
|
|
if (rgpbTrust != NULL) *pinfo->prgpbTrustInfo = (DATA_BLOB *) rgpbTrust;
|
|
}
|
|
|
|
delete pcfLeaf;
|
|
|
|
return hr;
|
|
}
|
|
|
|
extern const GUID rgguidActions[];
|
|
|
|
#if !defined(WIN16) && !defined(MAC)
|
|
WINTRUST_PROVIDER_CLIENT_SERVICES WinTrustProviderClientServices = {
|
|
ClientUnload, VerifyTrust, SubmitCertificate
|
|
};
|
|
|
|
const WINTRUST_PROVIDER_CLIENT_INFO ProvInfo = {
|
|
WIN_TRUST_REVISION_1_0, &WinTrustProviderClientServices,
|
|
1, (GUID *) &GuidCertValidate
|
|
};
|
|
|
|
//// WinTrustProviderClientInitialize
|
|
//
|
|
// Description:
|
|
// Client initialization routine. Called by WinTrust when the dll
|
|
// is loaded.
|
|
//
|
|
// Parameters:
|
|
// dwWinTrustRevision - Provides revision information
|
|
// lpWinTrustInfo - Provides a list of services available to the
|
|
// trust provider from WinTrust
|
|
// lpProvidername - Supplies a null terminated string representing the
|
|
// provider's name. Shouldb passed back to WinTrust
|
|
// when required without modification.
|
|
// lpTrustProviderInfo - Used to return trust provider information.
|
|
//
|
|
// Returns:
|
|
// TRUE on success and FALSE on failure. Must set last error on failure.
|
|
//
|
|
|
|
BOOL WINAPI WinTrustProviderClientInitialize(DWORD /*dwWinTrustRevision*/,
|
|
LPWINTRUST_CLIENT_TP_INFO /*pWinTrustInfo*/,
|
|
LPWSTR /*lpProviderName*/,
|
|
LPWINTRUST_PROVIDER_CLIENT_INFO * ppTrustProvInfo)
|
|
{
|
|
*ppTrustProvInfo = (LPWINTRUST_PROVIDER_CLIENT_INFO) &ProvInfo;
|
|
return TRUE;
|
|
}
|
|
#endif // !WIN16 && !MAC
|
|
#endif // NT5BUILD
|
|
|
|
LPWSTR FormatValidityFailures(DWORD dwFlags)
|
|
{
|
|
DWORD cch = 0;
|
|
LPWSTR pwsz = NULL;
|
|
WCHAR rgwch[200];
|
|
|
|
if (dwFlags == 0) {
|
|
return NULL;
|
|
}
|
|
|
|
cch = 100;
|
|
pwsz = (LPWSTR) malloc(cch*sizeof(WCHAR));
|
|
if (pwsz == NULL) {
|
|
return NULL;
|
|
}
|
|
if (dwFlags & CERT_VALIDITY_BEFORE_START) {
|
|
LoadString(HinstDll, IDS_WHY_NOT_YET, rgwch, sizeof(rgwch)/sizeof(WCHAR));
|
|
StrCpyNW(pwsz, rgwch, cch);
|
|
} else {
|
|
StrCpyNW(pwsz, L"", cch);
|
|
}
|
|
|
|
if (dwFlags & CERT_VALIDITY_AFTER_END) {
|
|
LoadString(HinstDll, IDS_WHY_EXPIRED, rgwch, sizeof(rgwch)/sizeof(WCHAR));
|
|
if (wcslen(pwsz) + wcslen(rgwch) + 2 > cch) {
|
|
cch += 200;
|
|
pwsz = (LPWSTR) realloc(pwsz, cch*sizeof(WCHAR));
|
|
if (pwsz == NULL) {
|
|
return pwsz;
|
|
}
|
|
}
|
|
if (wcslen(pwsz) > 0)
|
|
StrCatBuffW(pwsz, wszCRLF, cch);
|
|
StrCatBuffW(pwsz, rgwch, cch);
|
|
}
|
|
|
|
if (dwFlags & CERT_VALIDITY_SIGNATURE_FAILS) {
|
|
LoadString(HinstDll, IDS_WHY_CERT_SIG, rgwch, sizeof(rgwch)/sizeof(WCHAR));
|
|
if (wcslen(pwsz) + wcslen(rgwch) + 2 > cch) {
|
|
cch += 200;
|
|
pwsz = (LPWSTR) realloc(pwsz, cch*sizeof(WCHAR));
|
|
if (pwsz == NULL) {
|
|
return pwsz;
|
|
}
|
|
}
|
|
if (wcslen(pwsz) > 0)
|
|
StrCatBuffW(pwsz, wszCRLF, cch);
|
|
StrCatBuffW(pwsz, rgwch, cch);
|
|
}
|
|
|
|
if (dwFlags & CERT_VALIDITY_NO_ISSUER_CERT_FOUND) {
|
|
LoadString(HinstDll, IDS_WHY_NO_PARENT, rgwch, sizeof(rgwch)/sizeof(WCHAR));
|
|
if (wcslen(pwsz) + wcslen(rgwch) + 2 > cch) {
|
|
cch += 200;
|
|
pwsz = (LPWSTR) realloc(pwsz, cch*sizeof(WCHAR));
|
|
if (pwsz == NULL) {
|
|
return pwsz;
|
|
}
|
|
}
|
|
if (wcslen(pwsz) > 0)
|
|
StrCatBuffW(pwsz, wszCRLF, cch);
|
|
StrCatBuffW(pwsz, rgwch, cch);
|
|
}
|
|
|
|
if (dwFlags & CERT_VALIDITY_NO_CRL_FOUND) {
|
|
LoadString(HinstDll, IDS_WHY_NO_CRL, rgwch, sizeof(rgwch)/sizeof(WCHAR));
|
|
if (wcslen(pwsz) + wcslen(rgwch) + 2 > cch) {
|
|
cch += 200;
|
|
pwsz = (LPWSTR) realloc(pwsz, cch*sizeof(WCHAR));
|
|
if (pwsz == NULL) {
|
|
return pwsz;
|
|
}
|
|
}
|
|
if (wcslen(pwsz) > 0)
|
|
StrCatBuffW(pwsz, wszCRLF, cch);
|
|
StrCatBuffW(pwsz, rgwch, cch);
|
|
}
|
|
|
|
if (dwFlags & CERT_VALIDITY_CERTIFICATE_REVOKED) {
|
|
LoadString(HinstDll, IDS_WHY_REVOKED, rgwch, sizeof(rgwch)/sizeof(WCHAR));
|
|
if (wcslen(pwsz) + wcslen(rgwch) + 2 > cch) {
|
|
cch += 200;
|
|
pwsz = (LPWSTR) realloc(pwsz, cch*sizeof(WCHAR));
|
|
if (pwsz == NULL) {
|
|
return pwsz;
|
|
}
|
|
}
|
|
if (wcslen(pwsz) > 0)
|
|
StrCatBuffW(pwsz, wszCRLF, cch);
|
|
StrCatBuffW(pwsz, rgwch, cch);
|
|
}
|
|
|
|
if (dwFlags & CERT_VALIDITY_CRL_OUT_OF_DATE) {
|
|
LoadString(HinstDll, IDS_WHY_CRL_EXPIRED, rgwch, sizeof(rgwch)/sizeof(WCHAR));
|
|
if (wcslen(pwsz) + wcslen(rgwch) + 2 > cch) {
|
|
cch += 200;
|
|
pwsz = (LPWSTR) realloc(pwsz, cch*sizeof(WCHAR));
|
|
if (pwsz == NULL) {
|
|
return pwsz;
|
|
}
|
|
}
|
|
if (wcslen(pwsz) > 0)
|
|
StrCatBuffW(pwsz, wszCRLF, cch);
|
|
StrCatBuffW(pwsz, rgwch, cch);
|
|
}
|
|
|
|
if (dwFlags & CERT_VALIDITY_KEY_USAGE_EXT_FAILURE) {
|
|
LoadString(HinstDll, IDS_WHY_KEY_USAGE, rgwch, sizeof(rgwch)/sizeof(WCHAR));
|
|
if (wcslen(pwsz) + wcslen(rgwch) + 2 > cch) {
|
|
cch += 200;
|
|
pwsz = (LPWSTR) realloc(pwsz, cch*sizeof(WCHAR));
|
|
if (pwsz == NULL) {
|
|
return pwsz;
|
|
}
|
|
}
|
|
if (wcslen(pwsz) > 0)
|
|
StrCatBuffW(pwsz, wszCRLF, cch);
|
|
StrCatBuffW(pwsz, rgwch, cch);
|
|
}
|
|
|
|
if (dwFlags & CERT_VALIDITY_EXTENDED_USAGE_FAILURE) {
|
|
LoadString(HinstDll, IDS_WHY_EXTEND_USE, rgwch, sizeof(rgwch)/sizeof(WCHAR));
|
|
if (wcslen(pwsz) + wcslen(rgwch) + 2 > cch) {
|
|
cch += 200;
|
|
pwsz = (LPWSTR) realloc(pwsz, cch*sizeof(WCHAR));
|
|
if (pwsz == NULL) {
|
|
return pwsz;
|
|
}
|
|
}
|
|
if (wcslen(pwsz) > 0)
|
|
StrCatBuffW(pwsz, wszCRLF, cch);
|
|
StrCatBuffW(pwsz, rgwch, cch);
|
|
}
|
|
|
|
if (dwFlags & CERT_VALIDITY_NAME_CONSTRAINTS_FAILURE) {
|
|
LoadString(HinstDll, IDS_WHY_NAME_CONST, rgwch, sizeof(rgwch)/sizeof(WCHAR));
|
|
if (wcslen(pwsz) + wcslen(rgwch) + 2 > cch) {
|
|
cch += 200;
|
|
pwsz = (LPWSTR) realloc(pwsz, cch*sizeof(WCHAR));
|
|
if (pwsz == NULL) {
|
|
return pwsz;
|
|
}
|
|
}
|
|
if (wcslen(pwsz) > 0)
|
|
StrCatBuffW(pwsz, wszCRLF, cch);
|
|
StrCatBuffW(pwsz, rgwch, cch);
|
|
}
|
|
|
|
if (dwFlags & CERT_VALIDITY_UNKNOWN_CRITICAL_EXTENSION) {
|
|
LoadString(HinstDll, IDS_WHY_CRITICAL_EXT, rgwch, sizeof(rgwch)/sizeof(WCHAR));
|
|
if (wcslen(pwsz) + wcslen(rgwch) + 2 > cch) {
|
|
cch += 200;
|
|
pwsz = (LPWSTR) realloc(pwsz, cch*sizeof(WCHAR));
|
|
if (pwsz == NULL) {
|
|
return pwsz;
|
|
}
|
|
}
|
|
if (wcslen(pwsz) > 0)
|
|
StrCatBuffW(pwsz, wszCRLF, cch);
|
|
StrCatBuffW(pwsz, rgwch, cch);
|
|
}
|
|
|
|
return pwsz;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
HRESULT CTLModifyHelper(int cCertsToModify, PCTL_MODIFY_REQUEST rgCertMods,
|
|
LPCSTR szPurpose, HWND /*hwnd*/,
|
|
HCERTSTORE hcertstorTrust,
|
|
PCCERT_CONTEXT pccertSigner)
|
|
{
|
|
DWORD cb;
|
|
DWORD cbData;
|
|
DWORD cbOut;
|
|
CTL_INFO ctlInfo;
|
|
CTL_USAGE ctlUsage;
|
|
DWORD dwOne = 1;
|
|
HCRYPTPROV hprov = NULL;
|
|
HRESULT hr = S_OK;
|
|
DWORD i;
|
|
int iCert;
|
|
LPBYTE pbEncode = NULL;
|
|
LPBYTE pbHash;
|
|
PCCTL_CONTEXT pcctl = NULL;
|
|
PCRYPT_KEY_PROV_INFO pprovinfo = NULL;
|
|
CTL_ENTRY * rgctlEntry = NULL;
|
|
|
|
//
|
|
// Build the attributes blob which says that we actually trust/distrust a certificate.
|
|
//
|
|
|
|
CRYPT_ATTRIBUTE attributeYes;
|
|
CRYPT_ATTR_BLOB attrBlobYes;
|
|
CRYPT_ATTRIBUTE attributeNo;
|
|
CRYPT_ATTR_BLOB attrBlobNo;
|
|
CRYPT_ATTRIBUTE attributeParent;
|
|
CRYPT_ATTR_BLOB attrBlobParent;
|
|
|
|
attributeYes.pszObjId = (LPSTR) SzOID_CTL_ATTR_YESNO_TRUST;
|
|
attributeYes.cValue = 1;
|
|
attributeYes.rgValue = &attrBlobYes;
|
|
|
|
attrBlobYes.cbData = sizeof(RgbTrustYes); // MUST BE ASN
|
|
attrBlobYes.pbData = (LPBYTE) RgbTrustYes;
|
|
|
|
attributeNo.pszObjId = (LPSTR) SzOID_CTL_ATTR_YESNO_TRUST;
|
|
attributeNo.cValue = 1;
|
|
attributeNo.rgValue = &attrBlobNo;
|
|
|
|
attrBlobNo.cbData = sizeof(RgbTrustNo); // MUST BE ASN
|
|
attrBlobNo.pbData = (LPBYTE) RgbTrustNo;
|
|
|
|
attributeParent.pszObjId = (LPSTR) SzOID_CTL_ATTR_YESNO_TRUST;
|
|
attributeParent.cValue = 1;
|
|
attributeParent.rgValue = &attrBlobParent;
|
|
|
|
attrBlobParent.cbData = sizeof(RgbTrustParent); // MUST BE ASN
|
|
attrBlobParent.pbData = (LPBYTE) RgbTrustParent;
|
|
|
|
//
|
|
// Get the crypt provider for the certificate we are going to use in the trust
|
|
//
|
|
|
|
if (!CertGetCertificateContextProperty(pccertSigner, CERT_KEY_PROV_INFO_PROP_ID,
|
|
NULL, &cbData)) {
|
|
hr = E_FAIL;
|
|
goto Exit;
|
|
}
|
|
|
|
pprovinfo = (PCRYPT_KEY_PROV_INFO) malloc(cbData);
|
|
CertGetCertificateContextProperty(pccertSigner, CERT_KEY_PROV_INFO_PROP_ID,
|
|
pprovinfo, &cbData);
|
|
|
|
if (!CryptAcquireContextW(&hprov, pprovinfo->pwszContainerName,
|
|
pprovinfo->pwszProvName,
|
|
pprovinfo->dwProvType, 0)) {
|
|
hr = GetLastError();
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// We have a certificate and a provider to use for signing purposes.
|
|
// Look for a possible CTL to be emended by us.
|
|
//
|
|
|
|
//
|
|
// Search for a CTL signed by this cert and for the requested usage
|
|
//
|
|
|
|
CTL_FIND_USAGE_PARA ctlFind;
|
|
ctlFind.cbSize = sizeof(ctlFind);
|
|
ctlFind.SubjectUsage.cUsageIdentifier = 1;
|
|
ctlFind.SubjectUsage.rgpszUsageIdentifier = (LPSTR *) &szPurpose;
|
|
ctlFind.ListIdentifier.cbData = 0;
|
|
ctlFind.ListIdentifier.pbData = 0;
|
|
ctlFind.pSigner = pccertSigner->pCertInfo;
|
|
|
|
pcctl = CertFindCTLInStore(hcertstorTrust, X509_ASN_ENCODING, 0,
|
|
CTL_FIND_USAGE, &ctlFind, NULL);
|
|
if (pcctl == NULL) {
|
|
//
|
|
// No CTL currently exists, so build one from scratch
|
|
//
|
|
|
|
//
|
|
// Allocate space to hold the CTL entries
|
|
//
|
|
// size = (sizeof CTL_ENTRY + sizeof of SHA1 hash) * # certs to add
|
|
//
|
|
|
|
cb = cCertsToModify * (sizeof(CTL_ENTRY) + 20);
|
|
rgctlEntry = (PCTL_ENTRY) malloc(cb);
|
|
memset(rgctlEntry, 0, cb);
|
|
pbHash = ((LPBYTE) rgctlEntry) + (cCertsToModify * sizeof(CTL_ENTRY));
|
|
|
|
//
|
|
// Get the identifier for each of the certs and setup the Trust List
|
|
// entry for each of the certs. Note that they all point
|
|
// to the same attribute, this is possible since we are going to
|
|
// have the exact same amount of trust on each cert -- YES!!!! --
|
|
//
|
|
|
|
for (iCert = 0; iCert < cCertsToModify; iCert++, pbHash += 20) {
|
|
rgctlEntry[iCert].SubjectIdentifier.cbData = 20;
|
|
rgctlEntry[iCert].SubjectIdentifier.pbData = pbHash;
|
|
rgctlEntry[iCert].cAttribute = 1;
|
|
|
|
cb = 20;
|
|
CertGetCertificateContextProperty(rgCertMods[iCert].pccert,
|
|
CERT_SHA1_HASH_PROP_ID, pbHash, &cb);
|
|
rgCertMods[iCert].dwError = 0;
|
|
|
|
switch (rgCertMods[iCert].dwOperation) {
|
|
case CTL_MODIFY_REQUEST_ADD_TRUSTED:
|
|
rgctlEntry[iCert].rgAttribute = &attributeYes;
|
|
break;
|
|
|
|
case CTL_MODIFY_REQUEST_REMOVE:
|
|
rgctlEntry[iCert].rgAttribute = &attributeParent;
|
|
break;
|
|
|
|
case CTL_MODIFY_REQUEST_ADD_NOT_TRUSTED:
|
|
rgctlEntry[iCert].rgAttribute = &attributeNo;
|
|
break;
|
|
|
|
default:
|
|
rgCertMods[iCert].dwError = (DWORD) E_FAIL;
|
|
iCert -= 1; // Don't include this one
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Now setup the the overall structure of the Trust List for later
|
|
// encoding and signing.
|
|
//
|
|
|
|
ctlUsage.cUsageIdentifier = 1;
|
|
ctlUsage.rgpszUsageIdentifier = (LPSTR *) &szPurpose;
|
|
|
|
memset(&ctlInfo, 0, sizeof(ctlInfo));
|
|
ctlInfo.dwVersion = 0;
|
|
ctlInfo.SubjectUsage = ctlUsage;
|
|
// ctlInfo.ListIdentifier = 0;
|
|
ctlInfo.SequenceNumber.cbData = sizeof(dwOne);
|
|
ctlInfo.SequenceNumber.pbData = (LPBYTE) &dwOne;
|
|
GetSystemTimeAsFileTime(&ctlInfo.ThisUpdate);
|
|
// ctlInfo.NextUpdate = 0;
|
|
ctlInfo.SubjectAlgorithm.pszObjId = szOID_OIWSEC_sha1;
|
|
// ctlInfo.SubjectAlgorithm.Parameters.cbData = 0;
|
|
ctlInfo.cCTLEntry = cCertsToModify;
|
|
ctlInfo.rgCTLEntry = rgctlEntry;
|
|
// ctlInfo.cExtension = 0;
|
|
// ctlInfo.rgExtension = NULL;
|
|
|
|
}
|
|
else {
|
|
BOOL fRewrite;
|
|
|
|
memcpy(&ctlInfo, pcctl->pCtlInfo, sizeof(ctlInfo));
|
|
|
|
//
|
|
// We found a CTL with the right usage, now lets see if we need to add
|
|
// certificate to it.
|
|
//
|
|
// Start by assuming that we will need to add to the CTL so allocate
|
|
// space to hold the new set of Trust Entries
|
|
//
|
|
|
|
cb = (pcctl->pCtlInfo->cCTLEntry * sizeof(CTL_ENTRY) +
|
|
cCertsToModify * (sizeof(CTL_ENTRY) + 20));
|
|
rgctlEntry = (PCTL_ENTRY) malloc(cb);
|
|
memset(rgctlEntry, 0, cb);
|
|
pbHash = (((LPBYTE) rgctlEntry) +
|
|
(cCertsToModify + pcctl->pCtlInfo->cCTLEntry) * sizeof(CTL_ENTRY));
|
|
memcpy(rgctlEntry, pcctl->pCtlInfo->rgCTLEntry,
|
|
pcctl->pCtlInfo->cCTLEntry * sizeof(CTL_ENTRY));
|
|
ctlInfo.rgCTLEntry = rgctlEntry;
|
|
|
|
//
|
|
// For each certificate, see if the certificate is already in the list
|
|
// and append it to the end if it isn't
|
|
//
|
|
|
|
fRewrite = FALSE;
|
|
for (iCert = 0; iCert < cCertsToModify; iCert++) {
|
|
rgCertMods[iCert].dwError = 0;
|
|
|
|
cb = 20;
|
|
CertGetCertificateContextProperty(rgCertMods[iCert].pccert,
|
|
CERT_SHA1_HASH_PROP_ID, pbHash, &cb);
|
|
|
|
for (i=0; i<pcctl->pCtlInfo->cCTLEntry; i++) {
|
|
if (memcmp(pbHash, rgctlEntry[i].SubjectIdentifier.pbData, 20) == 0){
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we did not find a matching item, then add a new one to the
|
|
// end of the list
|
|
//
|
|
if (i == pcctl->pCtlInfo->cCTLEntry) {
|
|
rgctlEntry[i].SubjectIdentifier.cbData = 20;
|
|
rgctlEntry[i].SubjectIdentifier.pbData = pbHash;
|
|
rgctlEntry[i].cAttribute = 1;
|
|
|
|
pbHash += 20;
|
|
ctlInfo.cCTLEntry += 1;
|
|
fRewrite = TRUE;
|
|
|
|
|
|
switch (rgCertMods[iCert].dwOperation) {
|
|
case CTL_MODIFY_REQUEST_ADD_TRUSTED:
|
|
rgctlEntry[i].rgAttribute = &attributeYes;
|
|
break;
|
|
|
|
case CTL_MODIFY_REQUEST_REMOVE:
|
|
rgctlEntry[i].rgAttribute = &attributeParent;
|
|
break;
|
|
|
|
case CTL_MODIFY_REQUEST_ADD_NOT_TRUSTED:
|
|
rgctlEntry[i].rgAttribute = &attributeNo;
|
|
break;
|
|
|
|
default:
|
|
rgCertMods[i].dwError = (DWORD) E_FAIL;
|
|
ctlInfo.cCTLEntry -= 1; // Don't include this one
|
|
break;
|
|
}
|
|
}
|
|
//
|
|
// If we did find a matching, then put the new attribute into the
|
|
// list (may replace trust with distrust)
|
|
//
|
|
else {
|
|
switch (rgCertMods[iCert].dwOperation) {
|
|
case CTL_MODIFY_REQUEST_ADD_TRUSTED:
|
|
rgctlEntry[i].rgAttribute = &attributeYes;
|
|
break;
|
|
|
|
case CTL_MODIFY_REQUEST_REMOVE:
|
|
rgctlEntry[i].rgAttribute = &attributeParent;
|
|
break;
|
|
|
|
default:
|
|
case CTL_MODIFY_REQUEST_ADD_NOT_TRUSTED:
|
|
rgctlEntry[i].rgAttribute = &attributeNo;
|
|
break;
|
|
}
|
|
fRewrite = TRUE;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Nothing to be added at this time -- exit and say success
|
|
//
|
|
|
|
if (!fRewrite) {
|
|
hr = S_OK;
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Increment the sequence number
|
|
//
|
|
// M00MAC -- this may be cheating, however I think that we can use it
|
|
// one the mac without change, I don't really care that the sequence
|
|
// is understandable at this point, as long as it does sequence.
|
|
//
|
|
|
|
dwOne = 0;
|
|
memcpy(&dwOne, ctlInfo.SequenceNumber.pbData,
|
|
ctlInfo.SequenceNumber.cbData);
|
|
dwOne += 1;
|
|
|
|
ctlInfo.SequenceNumber.cbData = sizeof(dwOne);
|
|
ctlInfo.SequenceNumber.pbData = (LPBYTE) &dwOne;
|
|
}
|
|
|
|
//
|
|
// OK --- We have the basic information built up for a Cert Trust List,
|
|
// now we just need to encode and sign the blasted thing
|
|
//
|
|
|
|
CMSG_SIGNER_ENCODE_INFO signer1;
|
|
memset(&signer1, 0, sizeof(signer1));
|
|
signer1.cbSize = sizeof(signer1);
|
|
signer1.pCertInfo = pccertSigner->pCertInfo;
|
|
signer1.hCryptProv = hprov;
|
|
signer1.dwKeySpec = AT_SIGNATURE;
|
|
signer1.HashAlgorithm.pszObjId = szOID_OIWSEC_sha1;
|
|
// signer1.HashAlgorithm.Parameters.cbData = 0;
|
|
// signer1.pvHashAuxInfo = 0;
|
|
// signer1.cAuthAttrib = 0;
|
|
// signer1.cUnauthAttr = 0;
|
|
|
|
CMSG_SIGNED_ENCODE_INFO signinfo;
|
|
memset(&signinfo, 0, sizeof(signinfo));
|
|
signinfo.cbSize = sizeof(signinfo);
|
|
signinfo.cSigners = 1;
|
|
signinfo.rgSigners = &signer1;
|
|
signinfo.cCertEncoded = 0;
|
|
signinfo.cCrlEncoded = 0;
|
|
|
|
if (!CryptMsgEncodeAndSignCTL(PKCS_7_ASN_ENCODING, &ctlInfo, &signinfo,
|
|
0, NULL, &cbOut)) {
|
|
hr = GetLastError();
|
|
goto Exit;
|
|
}
|
|
|
|
pbEncode = (LPBYTE) malloc(cbOut);
|
|
if (!CryptMsgEncodeAndSignCTL(PKCS_7_ASN_ENCODING, &ctlInfo, &signinfo,
|
|
0, pbEncode, &cbOut)) {
|
|
hr = GetLastError();
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Now put it into the trust store
|
|
//
|
|
|
|
if (!CertAddEncodedCTLToStore(hcertstorTrust, PKCS_7_ASN_ENCODING,
|
|
pbEncode, cbOut,
|
|
CERT_STORE_ADD_REPLACE_EXISTING, NULL)) {
|
|
//
|
|
// If we fail, and we in debug mode then create an output file so
|
|
// we can figure out what we did wrong.
|
|
//
|
|
|
|
#ifdef DEBUG
|
|
HANDLE hfile;
|
|
|
|
hfile = CreateFileA("c:\\output.t", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
|
|
0, 0);
|
|
WriteFile(hfile, pbEncode, cbOut, &cb, NULL);
|
|
CloseHandle(hfile);
|
|
#endif // DEBUG
|
|
|
|
hr = GetLastError();
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// We succeeded in the operation
|
|
//
|
|
|
|
hr = S_OK;
|
|
|
|
//
|
|
// A single point of clean up and exit for everything.
|
|
//
|
|
Exit:
|
|
if (rgctlEntry != NULL) free(rgctlEntry);
|
|
if (pprovinfo != NULL) free(pprovinfo);
|
|
if (pcctl != NULL) CertFreeCTLContext(pcctl);
|
|
if (pbEncode != NULL) free(pbEncode);
|
|
if (hprov != NULL) CryptReleaseContext(hprov, 0);
|
|
|
|
if (SUCCEEDED(hr) && (hr != S_OK)) hr = E_FAIL;
|
|
return hr;
|
|
}
|
|
|
|
PCCERT_CONTEXT CreateTrustSigningCert(HWND hwnd, HCERTSTORE hcertstoreRoot,
|
|
BOOL fDialog)
|
|
{
|
|
BYTE bSerialNumber = 1;
|
|
DWORD cb;
|
|
CERT_INFO certInfo;
|
|
DWORD dw;
|
|
HCRYPTKEY hkey;
|
|
HCRYPTPROV hprov = NULL;
|
|
HRESULT hr = S_OK;
|
|
CERT_NAME_BLOB nameblob = {0, NULL};
|
|
LPBYTE pbEncode = NULL;
|
|
PCCERT_CONTEXT pccert = NULL;
|
|
PCERT_PUBLIC_KEY_INFO pkeyinfo = NULL;
|
|
CRYPT_KEY_PROV_INFO provinfo;
|
|
LPSTR psz;
|
|
char rgchTitle[256];
|
|
char rgchMsg[256];
|
|
char rgch[256];
|
|
char rgch1[256];
|
|
char rgch2[256];
|
|
CERT_EXTENSION rgExt[1] = {0};
|
|
SYSTEMTIME st;
|
|
|
|
|
|
//
|
|
// We always use RSA base for this purpose. We should never run
|
|
// across a system where rsabase does not exist.
|
|
//
|
|
// We assume that we need to create a new keyset and fallback to
|
|
// openning the existing keyset in the event that it already exists
|
|
//
|
|
|
|
if (!CryptAcquireContextA(&hprov, SzTrustListSigner, NULL, PROV_RSA_FULL, 0)) {
|
|
hr = GetLastError();
|
|
if ((hr != NTE_NOT_FOUND) && (hr != NTE_BAD_KEYSET)) {
|
|
goto ExitHere;
|
|
}
|
|
hr = S_OK;
|
|
if (!CryptAcquireContextA(&hprov, SzTrustListSigner, NULL, PROV_RSA_FULL,
|
|
CRYPT_NEWKEYSET)) {
|
|
hr = GetLastError();
|
|
goto ExitHere;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Now we need to create the signing key in the keyset. Again
|
|
// we assume that we just created the keyset so we attempt to create
|
|
// the key in all cases. Note we don't need to open the key in the
|
|
// event we fail to create it as we never directly use it.
|
|
//
|
|
// Since we want security. We first try for a 1024-bit key before
|
|
// using the default (usually 512-bit) size.
|
|
//
|
|
|
|
if (!CryptGetUserKey(hprov, AT_SIGNATURE, &hkey)) {
|
|
dw = MAKELONG(0, 1024);
|
|
retry_keygen:
|
|
if (!CryptGenKey(hprov, AT_SIGNATURE, 0, &hkey)) {
|
|
#ifndef WIN16
|
|
hr = ::GetLastError();
|
|
#else
|
|
hr = GetLastError();
|
|
#endif // !WIN16
|
|
if ((hr == ERROR_INVALID_PARAMETER) && (dw != 0)) {
|
|
dw = 0;
|
|
goto retry_keygen;
|
|
}
|
|
if (hr != NTE_EXISTS) {
|
|
goto ExitHere;
|
|
}
|
|
}
|
|
}
|
|
CryptDestroyKey(hkey);
|
|
|
|
//
|
|
// Now we need to create a certificate which corresponds to the
|
|
// signing key we just created.
|
|
//
|
|
// Start by creating the DN to be stored in the certificate. The
|
|
// DN we are going to use is of the following format.
|
|
//
|
|
// cn=<machine name>/cn=Trust List Signer/cn=<user name>
|
|
//
|
|
// We make the simplifying assumption that neither machine names
|
|
// or user names can contain commas.
|
|
//
|
|
|
|
dw = sizeof(rgch1);
|
|
GetUserNameA(rgch1, &dw);
|
|
dw = sizeof(rgch2);
|
|
GetComputerNameA(rgch2, &dw);
|
|
wnsprintf(rgch, ARRAYSIZE(rgch), SzTrustDN, rgch2, rgch1);
|
|
|
|
if (!CertStrToNameA(X509_ASN_ENCODING, rgch,
|
|
CERT_X500_NAME_STR | CERT_NAME_STR_COMMA_FLAG, NULL,
|
|
NULL, &cb, NULL)) {
|
|
hr = E_FAIL;
|
|
goto ExitHere;
|
|
}
|
|
|
|
nameblob.pbData = (LPBYTE) malloc(cb);
|
|
nameblob.cbData = cb;
|
|
CertStrToNameA(X509_ASN_ENCODING, rgch,
|
|
CERT_X500_NAME_STR | CERT_NAME_STR_COMMA_FLAG, NULL,
|
|
nameblob.pbData, &nameblob.cbData, NULL);
|
|
|
|
//
|
|
// Extract the public portion of the signing key and ASN encode it
|
|
//
|
|
|
|
if (!CryptExportPublicKeyInfo(hprov, AT_SIGNATURE, X509_ASN_ENCODING,
|
|
NULL, &cb)) {
|
|
goto ExitHere;
|
|
}
|
|
pkeyinfo = (PCERT_PUBLIC_KEY_INFO) malloc(cb);
|
|
if (!CryptExportPublicKeyInfo(hprov, AT_SIGNATURE, X509_ASN_ENCODING,
|
|
pkeyinfo, &cb)) {
|
|
goto ExitHere;
|
|
}
|
|
|
|
//
|
|
// We are going to sign the certificate using SHA-1/RSA
|
|
//
|
|
|
|
CRYPT_ALGORITHM_IDENTIFIER sigalg;
|
|
memset(&sigalg, 0, sizeof(sigalg));
|
|
sigalg.pszObjId = szOID_OIWSEC_sha1RSASign;
|
|
// sigalg.Parameters.cbData = 0;
|
|
// sigalg.Parameters.pbData = NULL;
|
|
|
|
//
|
|
// We are putting one critical section on the extension. Enhanced
|
|
// key usage is CTL signing. Note that this is the only use that
|
|
// we are going to allow for this key.
|
|
//
|
|
|
|
rgExt[0].pszObjId = szOID_ENHANCED_KEY_USAGE;
|
|
rgExt[0].fCritical = TRUE;
|
|
|
|
CTL_USAGE ctlUsage2;
|
|
ctlUsage2.cUsageIdentifier = 1;
|
|
ctlUsage2.rgpszUsageIdentifier = &psz;
|
|
psz = (LPSTR) SzOID_KP_CTL_USAGE_SIGNING;
|
|
|
|
CryptEncodeObject(X509_ASN_ENCODING, X509_ENHANCED_KEY_USAGE, &ctlUsage2,
|
|
NULL, &cb);
|
|
rgExt[0].Value.pbData = (LPBYTE) malloc(cb);
|
|
rgExt[0].Value.cbData = cb;
|
|
CryptEncodeObject(X509_ASN_ENCODING, X509_ENHANCED_KEY_USAGE, &ctlUsage2,
|
|
rgExt[0].Value.pbData, &rgExt[0].Value.cbData);
|
|
|
|
|
|
//
|
|
// Now we can setup the rest of the certifiate information and
|
|
// encode it.
|
|
//
|
|
|
|
memset(&certInfo, 0, sizeof(certInfo));
|
|
// certInfo.dwVersion = 0;
|
|
certInfo.SerialNumber.cbData = 1;
|
|
certInfo.SerialNumber.pbData = &bSerialNumber;
|
|
certInfo.SignatureAlgorithm.pszObjId = szOID_OIWSEC_sha1RSASign;
|
|
// certInfo.SignatureAlgorithm.Parameter.cbData = 0;
|
|
// certInfo.SignatureAlgorithm.Parameter.pbData = NULL;
|
|
certInfo.Issuer = nameblob;
|
|
GetSystemTimeAsFileTime(&certInfo.NotBefore);
|
|
// certInfo.NotAfter = certInfo.NotBefore;
|
|
// M00BUG -- must increase the NotAfter date by some amount.
|
|
FileTimeToSystemTime(&certInfo.NotBefore, &st);
|
|
st.wYear += 50;
|
|
SystemTimeToFileTime(&st, &certInfo.NotAfter);
|
|
certInfo.Subject = nameblob;
|
|
certInfo.SubjectPublicKeyInfo = *pkeyinfo;
|
|
// certInfo.IssuerUniqueId = ;
|
|
// certInfo.SubjectUniqueId = ;
|
|
certInfo.cExtension = 1;
|
|
certInfo.rgExtension = rgExt;
|
|
|
|
if (!CryptSignAndEncodeCertificate(hprov, AT_SIGNATURE,
|
|
X509_ASN_ENCODING,
|
|
X509_CERT_TO_BE_SIGNED, &certInfo,
|
|
&sigalg, NULL, NULL, &cb)) {
|
|
#ifndef WIN16
|
|
hr = ::GetLastError();
|
|
#else
|
|
hr = GetLastError();
|
|
#endif // !WIN16
|
|
goto ExitHere;
|
|
}
|
|
|
|
pbEncode = (LPBYTE) malloc(cb);
|
|
if (!CryptSignAndEncodeCertificate(hprov, AT_SIGNATURE,
|
|
X509_ASN_ENCODING,
|
|
X509_CERT_TO_BE_SIGNED, &certInfo,
|
|
&sigalg, NULL, pbEncode, &cb)) {
|
|
#ifndef WIN16
|
|
hr = ::GetLastError();
|
|
#else
|
|
hr = GetLastError();
|
|
#endif // !WIN16
|
|
goto ExitHere;
|
|
}
|
|
|
|
//
|
|
// M00TODO Print the GOD IS ABOUT TO STRIKE message
|
|
//
|
|
|
|
if (fDialog) {
|
|
LoadStringA(HinstDll, IDS_ROOT_ADD_STRING, rgchMsg,
|
|
sizeof(rgchMsg)/sizeof(rgchMsg[0]));
|
|
LoadStringA(HinstDll, IDS_ROOT_ADD_TITLE, rgchTitle,
|
|
sizeof(rgchTitle)/sizeof(rgchTitle[0]));
|
|
MessageBoxA(hwnd, rgchMsg, rgchTitle, MB_APPLMODAL | MB_OK |
|
|
MB_ICONINFORMATION);
|
|
}
|
|
|
|
//
|
|
// Now we have warned the user, save our new cert in the root store
|
|
//
|
|
|
|
if (!CertAddEncodedCertificateToStore(hcertstoreRoot, X509_ASN_ENCODING,
|
|
pbEncode, cb,
|
|
CERT_STORE_ADD_REPLACE_EXISTING,
|
|
&pccert)) {
|
|
#ifndef WIN16
|
|
hr = ::GetLastError();
|
|
#else
|
|
hr = GetLastError();
|
|
#endif // !WIN16
|
|
goto ExitHere;
|
|
}
|
|
|
|
//
|
|
// Set the key-info property on the store so we can reference it later
|
|
//
|
|
|
|
memset(&provinfo, 0, sizeof(provinfo));
|
|
#ifndef WIN16
|
|
provinfo.pwszContainerName = L"Trust List Signer";
|
|
#else
|
|
provinfo.pwszContainerName = "Trust List Signer";
|
|
#endif // !WIN16
|
|
// provinfo.pwszProvName = NULL;
|
|
provinfo.dwProvType = PROV_RSA_FULL;
|
|
// provinfo.dwFlags = 0;
|
|
// provinfo.cProvParam = 0;
|
|
provinfo.dwKeySpec = AT_SIGNATURE;
|
|
|
|
CertSetCertificateContextProperty(pccert, CERT_KEY_PROV_INFO_PROP_ID,
|
|
0, &provinfo);
|
|
|
|
ExitHere:
|
|
if (hprov != NULL) CryptReleaseContext(hprov, 0);
|
|
if (nameblob.pbData != NULL) free(nameblob.pbData);
|
|
if (pkeyinfo != NULL) free(pkeyinfo);
|
|
if (rgExt[0].Value.pbData != NULL) free(rgExt[0].Value.pbData);
|
|
if (pbEncode != NULL) free(pbEncode);
|
|
if (FAILED(hr) && (pccert != NULL)) {
|
|
CertFreeCertificateContext(pccert);
|
|
pccert = NULL;
|
|
}
|
|
return pccert;
|
|
}
|
|
|
|
|
|
//// CertModifyCertificatesToTrust
|
|
//
|
|
// Description:
|
|
// This routine is used to build the Certificate Trust List for
|
|
// a purpose. It is possible that we will need to create the root
|
|
// signing key for this.
|
|
//
|
|
|
|
HRESULT CertModifyCertificatesToTrust(int cCertsToModify, PCTL_MODIFY_REQUEST rgCertMods,
|
|
LPCSTR szPurpose, HWND hwnd, HCERTSTORE hcertstorTrust,
|
|
PCCERT_CONTEXT pccertSigner)
|
|
{
|
|
HCERTSTORE hcertstorRoot = NULL;
|
|
HRESULT hr = E_FAIL;
|
|
int i;
|
|
|
|
//
|
|
// Some quick parameter checking
|
|
//
|
|
|
|
if (szPurpose == NULL) {
|
|
return E_INVALIDARG;
|
|
}
|
|
|
|
//
|
|
// Add a reference to the cert store, so we can just release it on exit.
|
|
//
|
|
|
|
if (hcertstorTrust != NULL) {
|
|
CertDuplicateStore(hcertstorTrust);
|
|
}
|
|
if (pccertSigner != NULL) {
|
|
CertDuplicateCertificateContext(pccertSigner);
|
|
}
|
|
|
|
//
|
|
// Open a trust store if we don't have one yet.
|
|
//
|
|
|
|
if (hcertstorTrust == NULL) {
|
|
#ifndef WIN16
|
|
hcertstorTrust = CertOpenStore(CERT_STORE_PROV_SYSTEM, X509_ASN_ENCODING,
|
|
NULL, CERT_SYSTEM_STORE_CURRENT_USER,
|
|
L"Trust");
|
|
#else
|
|
hcertstorTrust = CertOpenStore(CERT_STORE_PROV_SYSTEM, X509_ASN_ENCODING,
|
|
NULL, CERT_SYSTEM_STORE_CURRENT_USER,
|
|
"Trust");
|
|
#endif // !WIN16
|
|
if (hcertstorTrust == NULL) {
|
|
hr = GetLastError();
|
|
goto ExitHere;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Clear out errors and mark each item as not yet processed
|
|
//
|
|
|
|
for (i=0; i<cCertsToModify; i++) {
|
|
rgCertMods[i].dwError = CTL_MODIFY_ERR_NOT_YET_PROCESSED;
|
|
}
|
|
|
|
//
|
|
// If we were given a specific cert to sign with, then call the helper routine with
|
|
// that specific certificate
|
|
//
|
|
|
|
if (pccertSigner != NULL) {
|
|
hr = CTLModifyHelper(cCertsToModify, rgCertMods, szPurpose, hwnd, hcertstorTrust,
|
|
pccertSigner);
|
|
}
|
|
else {
|
|
DWORD cbData;
|
|
CTL_USAGE ctlUsage;
|
|
BOOL fSomeCertFound;
|
|
LPSTR psz;
|
|
|
|
//
|
|
// Walk through the list of certificates in the root store testing againist each
|
|
// valid cert for trust signing abilities and key material
|
|
//
|
|
|
|
//
|
|
// Open the root store, this is the only place we can store a signing
|
|
// cert that we can fully trust. The gods have decreed that items
|
|
// in this store cannot be corrupted or modified without user
|
|
// consent.
|
|
// Note: the previous statement is propaganda and should not be taken
|
|
// as having any relationship to the truth.
|
|
//
|
|
|
|
#ifndef WIN16
|
|
hcertstorRoot = CertOpenStore(CERT_STORE_PROV_SYSTEM, X509_ASN_ENCODING,
|
|
NULL, CERT_SYSTEM_STORE_CURRENT_USER,
|
|
L"Root");
|
|
#else
|
|
hcertstorRoot = CertOpenStore(CERT_STORE_PROV_SYSTEM, X509_ASN_ENCODING,
|
|
NULL, CERT_SYSTEM_STORE_CURRENT_USER,
|
|
"Root");
|
|
#endif // !WIN16
|
|
if (hcertstorRoot == NULL) {
|
|
hr = E_FAIL;
|
|
goto ExitHere;
|
|
}
|
|
//
|
|
// To be accepted, the cert must have the ability to sign trust lists
|
|
// and have key material
|
|
//
|
|
|
|
ctlUsage.cUsageIdentifier = 1;
|
|
ctlUsage.rgpszUsageIdentifier = &psz;
|
|
psz = (LPSTR) SzOID_KP_CTL_USAGE_SIGNING;
|
|
fSomeCertFound = FALSE;
|
|
|
|
while (TRUE) {
|
|
pccertSigner = CertFindCertificateInStore(hcertstorRoot, X509_ASN_ENCODING,
|
|
0, CERT_FIND_CTL_USAGE, &ctlUsage,
|
|
pccertSigner);
|
|
if (pccertSigner == NULL) {
|
|
// No certs found
|
|
break;
|
|
}
|
|
|
|
//
|
|
// The certificate must also have an associated set of key provider
|
|
// information, or we must reject it.
|
|
|
|
if (CertGetCertificateContextProperty(pccertSigner, CERT_KEY_PROV_INFO_PROP_ID,
|
|
NULL, &cbData) && (cbData > 0)) {
|
|
fSomeCertFound = TRUE;
|
|
hr = CTLModifyHelper(cCertsToModify, rgCertMods, szPurpose, hwnd,
|
|
hcertstorTrust, pccertSigner);
|
|
}
|
|
}
|
|
|
|
if (!fSomeCertFound) {
|
|
pccertSigner = CreateTrustSigningCert(hwnd, hcertstorRoot, TRUE);
|
|
if (pccertSigner != NULL) {
|
|
hr = CTLModifyHelper(cCertsToModify, rgCertMods, szPurpose, hwnd,
|
|
hcertstorTrust, pccertSigner);
|
|
}
|
|
else {
|
|
hr = E_FAIL;
|
|
goto ExitHere;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
//
|
|
// Check for errors returned
|
|
//
|
|
|
|
for (i=0; i<cCertsToModify; i++) {
|
|
if (rgCertMods[i].dwError == CTL_MODIFY_ERR_NOT_YET_PROCESSED) {
|
|
rgCertMods[i].dwError = (DWORD) E_FAIL;
|
|
}
|
|
if (FAILED(rgCertMods[i].dwError)) {
|
|
hr = S_FALSE;
|
|
}
|
|
}
|
|
|
|
ExitHere:
|
|
//
|
|
// Release the items we have created
|
|
//
|
|
|
|
if (hcertstorTrust != NULL) CertCloseStore(hcertstorTrust, 0);
|
|
if (pccertSigner != NULL) CertFreeCertificateContext(pccertSigner);
|
|
if (hcertstorRoot != NULL) CertCloseStore(hcertstorRoot, 0);
|
|
|
|
return hr;
|
|
}
|
|
|
|
BOOL FModifyTrust(HWND hwnd, PCCERT_CONTEXT pccert, DWORD dwNewTrust,
|
|
LPSTR szPurpose)
|
|
{
|
|
HRESULT hr;
|
|
CTL_MODIFY_REQUEST certmod;
|
|
|
|
certmod.pccert = pccert;
|
|
certmod.dwOperation = dwNewTrust;
|
|
|
|
hr = CertModifyCertificatesToTrust(1, &certmod, szPurpose, hwnd, NULL, NULL);
|
|
return (hr == S_OK) && (certmod.dwError == 0);
|
|
}
|