|
|
#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); }
|