#include "pch.hxx" #ifndef WIN16 #include #endif // !WIN16 #include "demand.h" #include #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; jcAttr; 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; ipadwTrustStepErrors[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; icRootStores; 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; icTrustStores; 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; icStores; 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; ihprov, 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; icUsageIdentifier; 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; ipadwTrustStepErrors[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; icRootStores; 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; icAttribute; 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; iCertpCert) || !(ptcert->pCert->pCertInfo)) continue; cExt = ptcert->pCert->pCertInfo->cExtension; rgExt = ptcert->pCert->pCertInfo->rgExtension; // // Process the extensions // ProcessExtensions: for (iExt=0; iExtcUsageIdentifier; 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; icUsageIdentifier; 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; ipadwTrustStepErrors[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; ipCert); 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; jpszUsageOid); 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 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; icRootStores; i++) { CertCloseStore(pmydata->rghRootStores[i], 0); } for (i=0; icTrustStores; 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; im_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; im_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; im_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; im_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; im_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; icbSize != 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; im_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; im_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; ipCtlInfo->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=/cn=Trust List Signer/cn= // // 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 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