//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 2001 // // File: signtoolactions.cpp // // Contents: The SignTool console tool action functions // // History: 4/30/2001 SCoyne Created // //---------------------------------------------------------------------------- #ifdef __cplusplus extern "C" { #endif #include #include #include #ifdef __cplusplus } // Matches extern "C" above #endif #undef ASSERT #include #include #include "signtool.h" #include "capicom.h" #include "resource.h" #include #include #include #include #include #include #include #ifdef SIGNTOOL_DEBUG extern BOOL gDebug; #define FormatIErrRet if (gDebug) wprintf(L"%hs (%u):\n", __FILE__, __LINE__); Format_IErrRet #else #define FormatIErrRet Format_IErrRet #endif extern HINSTANCE hModule; void PrintCertInfo(ICertificate2 *pICert2); void PrintCertChain(IChain *pIChain); void PrintCertInfoIndented(ICertificate2 *pICert2, DWORD dwIndent); void PrintSignerInfo(HANDLE hWVTStateData); BOOL ChainsToRoot(HANDLE hWVTStateData, LPWSTR wszRootName); BOOL HasTimestamp(HANDLE hWVTStateData); void Format_IErrRet(WCHAR *wszFunc, DWORD dwErr); void RegisterCAPICOM(); BOOL GetProviderType(LPWSTR pwszProvName, LPDWORD pdwProvType); typedef BOOL (WINAPI * FUNC_CRYPTCATADMINREMOVECATALOG)(HCATADMIN, WCHAR *, DWORD); int SignTool_CatDb(INPUTINFO *InputInfo) { DWORD dwDone = 0; DWORD dwWarnings = 0; DWORD dwErrors = 0; DWORD dwcFound; WIN32_FIND_DATAW FindFileData; HANDLE hFind = NULL; HRESULT hr; PVOID OldWow64Setting; WCHAR wszTempFileName[MAX_PATH]; WCHAR wszCanonicalFileName[MAX_PATH]; LPWSTR wszTemp; int LastSlash; HCATADMIN hCatAdmin = NULL; HCATINFO hCatInfo = NULL; CATALOG_INFO CatInfo; HMODULE hModWintrust = NULL; FUNC_CRYPTCATADMINREMOVECATALOG fnCryptCATAdminRemoveCatalog = NULL; // Initialization: if (!(CryptCATAdminAcquireContext(&hCatAdmin, &InputInfo->CatDbGuid, 0))) { FormatErrRet(L"CryptCATAdminAcquireContext", GetLastError()); return 1; // Error } switch (InputInfo->CatDbCommand) { case RemoveCat: // Attempt to fill the function pointer dynamically. // CryptCATAdminRemoveCatalog was introduced in XP. if (hModWintrust = GetModuleHandleA("wintrust.dll")) { fnCryptCATAdminRemoveCatalog = (FUNC_CRYPTCATADMINREMOVECATALOG) GetProcAddress(hModWintrust, "CryptCATAdminRemoveCatalog"); if (fnCryptCATAdminRemoveCatalog == NULL) { dwErrors++; ResErr(IDS_ERR_REM_CAT_PLATFORM); goto CatDbCleanupAndExit; } } else { dwErrors++; FormatErrRet(L"GetModuleHandle", GetLastError()); goto CatDbCleanupAndExit; } // Got the function pointer. // Loop over the catalogs to remove for (DWORD i=0; iNumFiles; i++) { // Check for slashes and wildcards. They are not allowed. if (wcspbrk(InputInfo->rgwszFileNames[i], L"/\\*?") != NULL) { // This won't work, so bail out now with a helpful message dwErrors++; ResFormatErr(IDS_ERR_CATALOG_NAME, InputInfo->rgwszFileNames[i]); continue; } if (InputInfo->Verbose) { ResFormatOut(IDS_INFO_REMOVING_CAT, InputInfo->rgwszFileNames[i]); } if (!fnCryptCATAdminRemoveCatalog(hCatAdmin, InputInfo->rgwszFileNames[i], 0)) { dwErrors++; if (!InputInfo->Quiet) { switch (hr = GetLastError()) { case ERROR_NOT_FOUND: ResFormatErr(IDS_ERR_CAT_NOT_FOUND, InputInfo->rgwszFileNames[i]); break; default: FormatErrRet(L"CryptCATAdminRemoveCatalog", hr); } } ResFormatErr(IDS_ERR_REMOVING_CAT, InputInfo->rgwszFileNames[i]); continue; } // Successfully removed the catalog dwDone++; if (!InputInfo->Quiet) { ResFormatOut(IDS_INFO_REMOVED_CAT, InputInfo->rgwszFileNames[i]); } } // Done Removing catalogs break; case AddUniqueCat: case UpdateCat: // Check if we are in the 32-bit Emulator on a 64-bit system if (InputInfo->fIsWow64Process) { // Disable WOW64 file-system redirection entirely for our FindFirst/NextFile OldWow64Setting = Wow64SetFilesystemRedirectorEx(WOW64_FILE_SYSTEM_DISABLE_REDIRECT_LEGACY); } // Loop over the files and Add/Update them: for (DWORD i=0; iNumFiles; i++) { // Find the last slash in the path specification: LastSlash = 0; for (DWORD s=0; srgwszFileNames[i]); s++) { if ((wcsncmp(&(InputInfo->rgwszFileNames[i][s]), L"\\", 1) == 0) || (wcsncmp(&(InputInfo->rgwszFileNames[i][s]), L"/", 1) == 0) || (wcsncmp(&(InputInfo->rgwszFileNames[i][s]), L":", 1) == 0)) { // Set LastSlash to the character after the last slash: LastSlash = s + 1; } } wcsncpy(wszTempFileName, InputInfo->rgwszFileNames[i], MAX_PATH); wszTempFileName[MAX_PATH-1] = L'\0'; dwcFound = 0; hFind = FindFirstFileU(InputInfo->rgwszFileNames[i], &FindFileData); if (hFind == INVALID_HANDLE_VALUE) { // No files found matching that name dwErrors++; ResFormatErr(IDS_ERR_FILE_NOT_FOUND, InputInfo->rgwszFileNames[i]); continue; } do { if (!(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { dwcFound++; // Increment number of files (not dirs) found // matching this filespec // Copy the filename on after the last slash: wcsncpy(&(wszTempFileName[LastSlash]), FindFileData.cFileName, MAX_PATH-LastSlash); wszTempFileName[MAX_PATH-1] = L'\0'; // Canonicalize: if (PathIsRelativeW(wszCanonicalFileName)) { // We need to make it an Absolute path for the CAT API. WCHAR wszTempFileName2[MAX_PATH]; if (GetCurrentDirectoryW(MAX_PATH, wszTempFileName2) && PathAppendW(wszTempFileName2, wszTempFileName)) { PathCanonicalizeW(wszCanonicalFileName, wszTempFileName2); } } else { PathCanonicalizeW(wszCanonicalFileName, wszTempFileName); } if (InputInfo->fIsWow64Process) { // Disable WOW64 file-system redirection for our current file only Wow64SetFilesystemRedirectorEx(wszCanonicalFileName); } if (InputInfo->Verbose) { ResFormatOut(IDS_INFO_ADDING_CAT, wszTempFileName); #ifdef SIGNTOOL_DEBUG if (gDebug) { wprintf(L"\tCanonical Filename: %s\n", wszCanonicalFileName); wprintf(L"\tFindFile.cFileName: %s\n", FindFileData.cFileName); } #endif } // Add the catalog if (InputInfo->CatDbCommand == UpdateCat) { hCatInfo = CryptCATAdminAddCatalog(hCatAdmin, wszCanonicalFileName, FindFileData.cFileName, 0); } else // CatDbCommand must equal AddUniqueCat { hCatInfo = CryptCATAdminAddCatalog(hCatAdmin, wszCanonicalFileName, NULL, // Don't specify the name 0); } // Check for failure if (hCatInfo == NULL) { dwErrors++; if (!InputInfo->Quiet) { switch (hr = GetLastError()) { case ERROR_BAD_FORMAT: ResFormatErr(IDS_ERR_CAT_NOT_FOUND, wszTempFileName); break; default: FormatErrRet(L"CryptCATAdminAddCatalog", hr); } } ResFormatErr(IDS_ERR_ADDING_CAT, wszTempFileName); if (InputInfo->fIsWow64Process) { // Disable WOW64 file-system redirection entirely for our FindFirst/NextFile Wow64SetFilesystemRedirectorEx(WOW64_FILE_SYSTEM_DISABLE_REDIRECT_LEGACY); } continue; } dwDone++; // Print success message if (!InputInfo->Quiet) { if (InputInfo->CatDbCommand == UpdateCat) { ResFormatOut(IDS_INFO_ADDED_CAT, wszTempFileName); } else // CatDbCommand must equal AddUniqueCat { if (CryptCATCatalogInfoFromContext(hCatInfo, &CatInfo, 0)) { wszTemp = wcsstr(CatInfo.wszCatalogFile, L"\\"); if (wszTemp == NULL) { wszTemp = CatInfo.wszCatalogFile; } ResFormatOut(IDS_INFO_ADDED_CAT_AS, wszTempFileName, wszTemp); } } } // Close the Catalog Info Context CryptCATAdminReleaseCatalogContext(hCatAdmin, hCatInfo, 0); } if (InputInfo->fIsWow64Process) { // Disable WOW64 file-system redirection entirely for our FindFirst/NextFile Wow64SetFilesystemRedirectorEx(WOW64_FILE_SYSTEM_DISABLE_REDIRECT_LEGACY); } } while (FindNextFileU(hFind, &FindFileData)); if (dwcFound == 0) // No files were found matching this filespec { // this will only fire if only directories were found. dwErrors++; ResFormatErr(IDS_ERR_FILE_NOT_FOUND, InputInfo->rgwszFileNames[i]); continue; } FindClose(hFind); hFind = NULL; } if (InputInfo->fIsWow64Process) { // Re-ensable WOW64 file-system redirection Wow64SetFilesystemRedirectorEx(OldWow64Setting); } // Done Adding/Updating catalogs break; default: ResErr(IDS_ERR_UNEXPECTED); goto CatDbCleanupAndExit; } CatDbCleanupAndExit: //Print Summary Information: if (InputInfo->Verbose || (!InputInfo->Quiet && (dwErrors || dwWarnings))) { wprintf(L"\n"); if (InputInfo->Verbose || dwDone) switch (InputInfo->CatDbCommand) { case AddUniqueCat: case UpdateCat: ResFormatOut(IDS_INFO_CATS_ADDED, dwDone); break; case RemoveCat: ResFormatOut(IDS_INFO_CATS_REMOVED, dwDone); break; default: ResErr(IDS_ERR_UNEXPECTED); } // Commented out because no warnings are possible in this function yet: // if (InputInfo->Verbose || dwWarnings) // ResFormatOut(IDS_INFO_WARNINGS, dwWarnings); if (InputInfo->Verbose || dwErrors) ResFormatOut(IDS_INFO_ERRORS, dwErrors); } CryptCATAdminReleaseContext(hCatAdmin, 0); if (dwErrors) return 1; // Error if (dwWarnings) return 2; // Warning if (dwDone) return 0; // Success // One of the above returns should fire, so // this should never happen: ResErr(IDS_ERR_NO_FILES_DONE); ResErr(IDS_ERR_UNEXPECTED); return 1; // Error } int SignTool_Sign(INPUTINFO *InputInfo) { DWORD dwcFound; DWORD dwDone = 0; DWORD dwWarnings = 0; DWORD dwErrors = 0; DWORD dwTemp; WIN32_FIND_DATAW FindFileData; HANDLE hFind = NULL; HCERTSTORE hStore = NULL; HRESULT hr; WCHAR wszTempFileName[MAX_PATH]; WCHAR wszSHA1[41]; ICertificate2 *pICert2Selected = NULL; ICertificate2 *pICert2Temp = NULL; ICertificates *pICerts = NULL; ICertificates2 *pICerts2Original = NULL; ICertificates2 *pICerts2Selected = NULL; ICertificates2 *pICerts2Temp = NULL; ISignedCode *pISignedCode = NULL; ISigner2 *pISigner2 = NULL; ICSigner *pICSigner = NULL; IStore2 *pIStore2 = NULL; IStore2 *pIStore2Temp = NULL; ICertStore *pICertStore = NULL; IPrivateKey *pIPrivateKey = NULL; IUtilities *pIUtils = NULL; PVOID OldWow64Setting; DATE dateBest; DATE dateTemp; COleDateTime DateTime; VARIANT varTemp; VARIANT_BOOL boolTemp; COleVariant cvarTemp; #ifdef SIGNTOOL_DEBUG CAPICOM_PROV_TYPE provtypeTemp; CAPICOM_KEY_SPEC keyspecTemp; #endif BSTR bstrTemp; BSTR bstrTemp2; int LastSlash; long longTemp; // Initialize COM: if ((hr = CoInitialize(NULL)) != S_OK) { FormatErrRet(L"CoInitialize", hr); return 1; // Error } VariantInit(&varTemp); // Create the store object, and check if CAPICOM is installed. hr = CoCreateInstance(__uuidof(Store), NULL, CLSCTX_ALL, __uuidof(IStore2), (LPVOID*)&pIStore2); if ((hr == REGDB_E_CLASSNOTREG) || (hr == E_NOINTERFACE) || (hr == 0x8007007E)) { // In this case, give it one more chance: RegisterCAPICOM(); hr = CoCreateInstance(__uuidof(Store), NULL, CLSCTX_ALL, __uuidof(IStore2), (LPVOID*)&pIStore2); } if (FAILED(hr)) { if ((hr == REGDB_E_CLASSNOTREG) || (hr == E_NOINTERFACE) || (hr == 0x8007007E)) { ResErr(IDS_ERR_CAPICOM_NOT_REG); } else { FormatErrRet(L"CoCreateInstance", hr); } dwErrors++; goto SignCleanupAndExit; } // Open the Store and Original Certificates2 collection if (InputInfo->wszCertFile) { // Get the cert from a file, so open the file as a store: if ((bstrTemp = SysAllocString(L"SignToolTemporaryPFXMemoryStore")) == NULL) { FormatErrRet(L"SysAllocString", ERROR_OUTOFMEMORY); dwErrors++; goto SignCleanupAndExit; } hr = pIStore2->Open(CAPICOM_MEMORY_STORE, bstrTemp, // Store Name CAPICOM_STORE_OPEN_READ_WRITE); SysFreeString(bstrTemp); if (FAILED(hr)) { FormatIErrRet(L"IStore2::Open", hr); dwErrors++; goto SignCleanupAndExit; } // Set the BSTRs needed for the Load call bstrTemp = SysAllocString(InputInfo->wszCertFile); if (bstrTemp == NULL) { FormatErrRet(L"SysAllocString", ERROR_OUTOFMEMORY); dwErrors++; goto SignCleanupAndExit; } if (InputInfo->wszPassword) { bstrTemp2 = SysAllocString(InputInfo->wszPassword); } else { bstrTemp2 = SysAllocString(L""); } if (bstrTemp2 == NULL) { FormatErrRet(L"SysAllocString", ERROR_OUTOFMEMORY); dwErrors++; goto SignCleanupAndExit; } // Load the Cert File into the new Memory Store: hr = pIStore2->Load(bstrTemp, // Filename bstrTemp2, // Password CAPICOM_KEY_STORAGE_DEFAULT); if (InputInfo->wszPassword) { // Wipe both copies of the password. We're done with it. SecureZeroMemory(bstrTemp2, wcslen(bstrTemp2) * sizeof(WCHAR)); SecureZeroMemory(InputInfo->wszPassword, wcslen(InputInfo->wszPassword) * sizeof(WCHAR)); } SysFreeString(bstrTemp); SysFreeString(bstrTemp2); if (FAILED(hr)) // Check return value of Load command above { switch (HRESULT_CODE(hr)) { case ERROR_INVALID_PASSWORD: ResErr(IDS_ERR_PFX_BAD_PASSWORD); break; case ERROR_FILE_NOT_FOUND: ResFormatErr(IDS_ERR_FILE_NOT_FOUND, InputInfo->wszCertFile); break; case ERROR_SHARING_VIOLATION: ResErr(IDS_ERR_SHARING_VIOLATION); break; default: FormatIErrRet(L"IStore2::Load", hr); ResFormatErr(IDS_ERR_CERT_FILE, InputInfo->wszCertFile); } dwErrors++; goto SignCleanupAndExit; } // The store is Open and Loaded. Now get the Certificates collection: hr = pIStore2->get_Certificates(&pICerts); if (FAILED(hr)) { FormatIErrRet(L"IStore2::get_Certificates", hr); dwErrors++; goto SignCleanupAndExit; } // And then get the Certificates2 interface: hr = pICerts->QueryInterface(__uuidof(ICertificates2), (LPVOID*)&pICerts2Original); if (FAILED(hr)) { if ((hr == REGDB_E_CLASSNOTREG) || (hr == E_NOINTERFACE) || (hr == 0x8007007E)) { ResErr(IDS_ERR_CAPICOM_NOT_REG); } else { FormatErrRet(L"ICertificates::QueryInterface", hr); } dwErrors++; goto SignCleanupAndExit; } // Release the Certificates interface: pICerts->Release(); pICerts = NULL; } else { // Get the cert from a store, so open the requested store: if (InputInfo->wszStoreName) { bstrTemp = SysAllocString(InputInfo->wszStoreName); } else { bstrTemp = SysAllocString(L"My"); } if (bstrTemp == NULL) { FormatErrRet(L"SysAllocString", ERROR_OUTOFMEMORY); dwErrors++; goto SignCleanupAndExit; } hr = pIStore2->Open(InputInfo->OpenMachineStore? CAPICOM_LOCAL_MACHINE_STORE: CAPICOM_CURRENT_USER_STORE, bstrTemp, CAPICOM_STORE_OPEN_MODE( CAPICOM_STORE_OPEN_READ_ONLY | CAPICOM_STORE_OPEN_EXISTING_ONLY)); if (FAILED(hr)) { switch (HRESULT_CODE(hr)) { case ERROR_FILE_NOT_FOUND: case ERROR_INVALID_NAME: ResFormatErr(IDS_ERR_STORE_NOT_FOUND, bstrTemp); break; default: FormatIErrRet(L"IStore2::Open", hr); ResFormatErr(IDS_ERR_STORE, bstrTemp); } SysFreeString(bstrTemp); dwErrors++; goto SignCleanupAndExit; } SysFreeString(bstrTemp); // The store is open. Now get the Certificates collection: hr = pIStore2->get_Certificates(&pICerts); if (FAILED(hr)) { FormatIErrRet(L"IStore2::get_Certificates", hr); dwErrors++; goto SignCleanupAndExit; } // And then get the Certificates2 interface: hr = pICerts->QueryInterface(__uuidof(ICertificates2), (LPVOID*)&pICerts2Original); if (FAILED(hr)) { if ((hr == REGDB_E_CLASSNOTREG) || (hr == E_NOINTERFACE) || (hr == 0x8007007E)) { ResErr(IDS_ERR_CAPICOM_NOT_REG); } else { FormatErrRet(L"ICertificates::QueryInterface", hr); } dwErrors++; goto SignCleanupAndExit; } // Release the Certificates interface: pICerts->Release(); pICerts = NULL; } #ifdef SIGNTOOL_DEBUG if (gDebug) { hr = pICerts2Original->get_Count(&longTemp); if (FAILED(hr)) { FormatIErrRet(L"ICertificates2::get_Count", hr); dwErrors++; goto SignCleanupAndExit; } wprintf(L"\nThe following certificates were considered:\n"); for (long l=1; l <= longTemp; l++) { hr = pICerts2Original->get_Item(l, &varTemp); if (FAILED(hr)) { FormatIErrRet(L"ICertificates2::get_Item", hr); dwErrors++; goto SignCleanupAndExit; } // And then get the Certificate2 interface: hr = varTemp.pdispVal->QueryInterface(__uuidof(ICertificate2), (LPVOID*)&pICert2Temp); if (FAILED(hr)) { if ((hr == REGDB_E_CLASSNOTREG) || (hr == E_NOINTERFACE) || (hr == 0x8007007E)) { ResErr(IDS_ERR_CAPICOM_NOT_REG); } else { FormatErrRet(L"IDispatch::QueryInterface", hr); } dwErrors++; goto SignCleanupAndExit; } PrintCertInfo(pICert2Temp); pICert2Temp->Release(); pICert2Temp = NULL; VariantClear(&varTemp); } } #endif // We now have an open Cert2 collection in pICerts2Original. // Find the certs we want from that Certificates2 collection: // Start by narrowing it down to those with the right EKU: // This cannot be bypassed, because we don't want to sign with certs // that aren't valid for code signing. The user has to explicitly choose // a different EKU if they want to sign with an invalid cert. if (InputInfo->wszEKU) { cvarTemp = InputInfo->wszEKU; } else { cvarTemp.SetString(CAPICOM_OID_CODE_SIGNING, VT_BSTR); } hr = pICerts2Original->Find(CAPICOM_CERTIFICATE_FIND_APPLICATION_POLICY, cvarTemp, FALSE, &pICerts2Selected); if (FAILED(hr)) { FormatIErrRet(L"ICertificates2::Find", hr); cvarTemp.Clear(); dwErrors++; goto SignCleanupAndExit; } cvarTemp.Clear(); // We now have pICerts2Selected, containing all certs with the right EKU. // Now filter based on whatever additional criteria were presented: #ifdef SIGNTOOL_DEBUG if (gDebug) { hr = pICerts2Selected->get_Count(&longTemp); if (FAILED(hr)) { FormatIErrRet(L"ICertificates2::get_Count", hr); dwErrors++; goto SignCleanupAndExit; } wprintf(L"After EKU filter, %ld certs were left.\n", longTemp); } #endif // Filtering on Hash if (InputInfo->SHA1.cbData == 20) { for (DWORD d = 0; d < 20; d++) { swprintf(&wszSHA1[d*2], L"%02X", InputInfo->SHA1.pbData[d]); } cvarTemp = wszSHA1; hr = pICerts2Selected->Find(CAPICOM_CERTIFICATE_FIND_SHA1_HASH, cvarTemp, FALSE, &pICerts2Temp); if (FAILED(hr)) { FormatIErrRet(L"ICertificates2::Find", hr); cvarTemp.Clear(); dwErrors++; goto SignCleanupAndExit; } cvarTemp.Clear(); pICerts2Selected->Release(); pICerts2Selected = pICerts2Temp; pICerts2Temp = NULL; #ifdef SIGNTOOL_DEBUG if (gDebug) { hr = pICerts2Selected->get_Count(&longTemp); if (FAILED(hr)) { FormatIErrRet(L"ICertificates2::get_Count", hr); dwErrors++; goto SignCleanupAndExit; } wprintf(L"After Hash filter, %ld certs were left.\n", longTemp); } #endif } // Filtering on SubjectName: if (InputInfo->wszSubjectName) { cvarTemp = InputInfo->wszSubjectName; hr = pICerts2Selected->Find(CAPICOM_CERTIFICATE_FIND_SUBJECT_NAME, cvarTemp, FALSE, &pICerts2Temp); if (FAILED(hr)) { FormatIErrRet(L"ICertificates2::Find", hr); cvarTemp.Clear(); dwErrors++; goto SignCleanupAndExit; } cvarTemp.Clear(); pICerts2Selected->Release(); pICerts2Selected = pICerts2Temp; pICerts2Temp = NULL; #ifdef SIGNTOOL_DEBUG if (gDebug) { hr = pICerts2Selected->get_Count(&longTemp); if (FAILED(hr)) { FormatIErrRet(L"ICertificates2::get_Count", hr); dwErrors++; goto SignCleanupAndExit; } wprintf(L"After Subject Name filter, %ld certs were left.\n", longTemp); } #endif } // Filtering on IssuerName: if (InputInfo->wszIssuerName) { cvarTemp = InputInfo->wszIssuerName; hr = pICerts2Selected->Find(CAPICOM_CERTIFICATE_FIND_ISSUER_NAME, cvarTemp, FALSE, &pICerts2Temp); if (FAILED(hr)) { FormatIErrRet(L"ICertificates2::Find", hr); cvarTemp.Clear(); dwErrors++; goto SignCleanupAndExit; } cvarTemp.Clear(); pICerts2Selected->Release(); pICerts2Selected = pICerts2Temp; pICerts2Temp = NULL; #ifdef SIGNTOOL_DEBUG if (gDebug) { hr = pICerts2Selected->get_Count(&longTemp); if (FAILED(hr)) { FormatIErrRet(L"ICertificates2::get_Count", hr); dwErrors++; goto SignCleanupAndExit; } wprintf(L"After Issuer Name filter, %ld certs were left.\n", longTemp); } #endif } // Filtering on TemplateName: if (InputInfo->wszTemplateName) { cvarTemp = InputInfo->wszTemplateName; hr = pICerts2Selected->Find(CAPICOM_CERTIFICATE_FIND_TEMPLATE_NAME, cvarTemp, FALSE, &pICerts2Temp); if (FAILED(hr)) { FormatIErrRet(L"ICertificates2::Find", hr); cvarTemp.Clear(); dwErrors++; goto SignCleanupAndExit; } cvarTemp.Clear(); pICerts2Selected->Release(); pICerts2Selected = pICerts2Temp; pICerts2Temp = NULL; #ifdef SIGNTOOL_DEBUG if (gDebug) { hr = pICerts2Selected->get_Count(&longTemp); if (FAILED(hr)) { FormatIErrRet(L"ICertificates2::get_Count", hr); dwErrors++; goto SignCleanupAndExit; } wprintf(L"After Template Name filter, %ld certs were left.\n", longTemp); } #endif } // Filtering on those with Private Keys. if (InputInfo->wszCSP == NULL) // Only if we aren't specifying the key { cvarTemp = (long)CAPICOM_PROPID_KEY_PROV_INFO; hr = pICerts2Selected->Find(CAPICOM_CERTIFICATE_FIND_EXTENDED_PROPERTY, cvarTemp, FALSE, &pICerts2Temp); if (FAILED(hr)) { FormatIErrRet(L"ICertificates2::Find", hr); cvarTemp.Clear(); dwErrors++; goto SignCleanupAndExit; } cvarTemp.Clear(); pICerts2Selected->Release(); pICerts2Selected = pICerts2Temp; pICerts2Temp = NULL; #ifdef SIGNTOOL_DEBUG if (gDebug) { hr = pICerts2Selected->get_Count(&longTemp); if (FAILED(hr)) { FormatIErrRet(L"ICertificates2::get_Count", hr); dwErrors++; goto SignCleanupAndExit; } wprintf(L"After Private Key filter, %ld certs were left.\n", longTemp); } #endif } // Filtering on RootName: if (InputInfo->wszRootName) { cvarTemp = InputInfo->wszRootName; hr = pICerts2Selected->Find(CAPICOM_CERTIFICATE_FIND_ROOT_NAME, cvarTemp, FALSE, &pICerts2Temp); if (FAILED(hr)) { FormatIErrRet(L"ICertificates2::Find", hr); cvarTemp.Clear(); dwErrors++; goto SignCleanupAndExit; } cvarTemp.Clear(); pICerts2Selected->Release(); pICerts2Selected = pICerts2Temp; pICerts2Temp = NULL; #ifdef SIGNTOOL_DEBUG if (gDebug) { hr = pICerts2Selected->get_Count(&longTemp); if (FAILED(hr)) { FormatIErrRet(L"ICertificates2::get_Count", hr); dwErrors++; goto SignCleanupAndExit; } wprintf(L"After Root Name filter, %ld certs were left.\n", longTemp); } #endif } // Make sure we have a single cert: hr = pICerts2Selected->get_Count(&longTemp); if (FAILED(hr)) { FormatIErrRet(L"ICertificates2::get_Count", hr); dwErrors++; goto SignCleanupAndExit; } if (longTemp == 0) { // No certificates found ResErr(IDS_ERR_NO_CERT); dwErrors++; goto SignCleanupAndExit; } else if (longTemp == 1) { // We have exactly one certificate. // Get that certificate: hr = pICerts2Selected->get_Item(1, &varTemp); if (FAILED(hr)) { FormatIErrRet(L"ICertificates2::get_Item", hr); dwErrors++; goto SignCleanupAndExit; } // And then get the Certificate2 interface: hr = varTemp.pdispVal->QueryInterface(__uuidof(ICertificate2), (LPVOID*)&pICert2Selected); if (FAILED(hr)) { if ((hr == REGDB_E_CLASSNOTREG) || (hr == E_NOINTERFACE) || (hr == 0x8007007E)) { ResErr(IDS_ERR_CAPICOM_NOT_REG); } else { FormatErrRet(L"IDispatch::QueryInterface", hr); } dwErrors++; goto SignCleanupAndExit; } VariantClear(&varTemp); } else if (longTemp > 1) { // We have too many certs. Maybe we can select automatically if (InputInfo->CatDbSelect) { // Let's select automatically dateBest = 0; for (long l=1; l <= longTemp; l++) { if (SUCCEEDED(pICerts2Selected->get_Item(l, &varTemp))) { hr = varTemp.pdispVal->QueryInterface(__uuidof(ICertificate2), (LPVOID*)&pICert2Temp); if (FAILED(hr)) { if ((hr == REGDB_E_CLASSNOTREG) || (hr == E_NOINTERFACE) || (hr == 0x8007007E)) { ResErr(IDS_ERR_CAPICOM_NOT_REG); } else { FormatErrRet(L"IDispatch::QueryInterface", hr); } dwErrors++; goto SignCleanupAndExit; } VariantClear(&varTemp); hr = pICert2Temp->get_ValidToDate(&dateTemp); if (FAILED(hr)) { FormatIErrRet(L"ICertificates2::get_ValidToDate", hr); dwErrors++; goto SignCleanupAndExit; } if (dateTemp > dateBest) { dateBest = dateTemp; if (pICert2Selected) { pICert2Selected->Release(); } pICert2Selected = pICert2Temp; pICert2Temp = NULL; } else { pICert2Temp->Release(); pICert2Temp = NULL; } } } if ((dateBest == 0) || (pICert2Selected == NULL)) { // Somehow we had at least one certificate, // but its date wasn't greater than zero // This should never happen. ResErr(IDS_ERR_UNEXPECTED); dwErrors++; goto SignCleanupAndExit; } } else { // We can't select automatically. // Report the Error and list all valid certs: ResErr(IDS_ERR_CERT_MULTIPLE); for (long l=1; l <= longTemp; l++) { if (SUCCEEDED(pICerts2Selected->get_Item(l, &varTemp))) { // Get the Certificate2 interface: hr = varTemp.pdispVal->QueryInterface(__uuidof(ICertificate2), (LPVOID*)&pICert2Temp); if (FAILED(hr)) { if ((hr == REGDB_E_CLASSNOTREG) || (hr == E_NOINTERFACE) || (hr == 0x8007007E)) { ResErr(IDS_ERR_CAPICOM_NOT_REG); } else { FormatErrRet(L"IDispatch::QueryInterface", hr); } dwErrors++; goto SignCleanupAndExit; } // Print the Certificate Information: PrintCertInfo(pICert2Temp); pICert2Temp->Release(); pICert2Temp = NULL; VariantClear(&varTemp); } } dwErrors++; goto SignCleanupAndExit; } } else { // longTemp was negative. This should never happen. ResErr(IDS_ERR_UNEXPECTED); dwErrors++; goto SignCleanupAndExit; } // Our signing certificate is now in pICert2Selected if (InputInfo->Verbose) { ResOut(IDS_INFO_CERT_SELECTED); PrintCertInfo(pICert2Selected); } // Check for Private Key info if (InputInfo->wszCSP && InputInfo->wszContainerName) { // We must add the Private Key info to the cert. if (!InputInfo->wszCertFile) { // If we didn't open our certs from a file, we opened a registry // store read-only. So that we don't modify that cert, we should // create a temporary memory store and copy the cert there // before we modify its private key info. // Create a new memory Store: hr = CoCreateInstance(__uuidof(Store), NULL, CLSCTX_ALL, __uuidof(IStore2), (LPVOID*)&pIStore2Temp); if (FAILED(hr)) { if ((hr == REGDB_E_CLASSNOTREG) || (hr == E_NOINTERFACE) || (hr == 0x8007007E)) { ResErr(IDS_ERR_CAPICOM_NOT_REG); } else { FormatErrRet(L"CoCreateInstance", hr); } dwErrors++; goto SignCleanupAndExit; } bstrTemp = SysAllocString(L"SignToolTemporaryMemoryStore"); if (bstrTemp == NULL) { FormatErrRet(L"SysAllocString", ERROR_OUTOFMEMORY); dwErrors++; goto SignCleanupAndExit; } hr = pIStore2Temp->Open(CAPICOM_MEMORY_STORE, bstrTemp, CAPICOM_STORE_OPEN_READ_WRITE); if (FAILED(hr)) { FormatIErrRet(L"IStore2::Open", hr); dwErrors++; goto SignCleanupAndExit; } // Add our cert to that store: hr = pIStore2Temp->Add(pICert2Selected); if (FAILED(hr)) { FormatIErrRet(L"IStore2::Add", hr); dwErrors++; goto SignCleanupAndExit; } // Release our Interface to the Cert in the old Store: pICert2Selected->Release(); pICert2Selected = NULL; // Get the Certificates Collection from the new Store: hr = pIStore2Temp->get_Certificates(&pICerts); if (FAILED(hr)) { FormatIErrRet(L"IStore2::get_Certificates", hr); dwErrors++; goto SignCleanupAndExit; } // Get the Certificate Object from the Certificates collection: hr = pICerts->get_Item(1, &varTemp); if (FAILED(hr)) { FormatIErrRet(L"ICertificates::get_Item", hr); dwErrors++; goto SignCleanupAndExit; } // Get the Certificate2 interface of our selected Cert in the new Store: hr = varTemp.pdispVal->QueryInterface(__uuidof(ICertificate2), (LPVOID*)&pICert2Selected); if (FAILED(hr)) { if ((hr == REGDB_E_CLASSNOTREG) || (hr == E_NOINTERFACE) || (hr == 0x8007007E)) { ResErr(IDS_ERR_CAPICOM_NOT_REG); } else { FormatErrRet(L"IDispatch::QueryInterface", hr); } dwErrors++; goto SignCleanupAndExit; } VariantClear(&varTemp); pICerts->Release(); pICerts = NULL; } // Now the Cert is free to be modified. // Create the Private Key object: hr = CoCreateInstance(__uuidof(PrivateKey), NULL, CLSCTX_ALL, __uuidof(IPrivateKey), (LPVOID*)&pIPrivateKey); if (FAILED(hr)) { if ((hr == REGDB_E_CLASSNOTREG) || (hr == E_NOINTERFACE) || (hr == 0x8007007E)) { ResErr(IDS_ERR_CAPICOM_NOT_REG); } else { FormatErrRet(L"CoCreateInstance", hr); } dwErrors++; goto SignCleanupAndExit; } // Setup Private Key info: bstrTemp = SysAllocString(InputInfo->wszContainerName); bstrTemp2 = SysAllocString(InputInfo->wszCSP); if ((bstrTemp == NULL) || (bstrTemp2 == NULL)) { FormatErrRet(L"SysAllocString", ERROR_OUTOFMEMORY); dwErrors++; goto SignCleanupAndExit; } // Open the specified private key: // First try the RSA_FULL provider type: hr = pIPrivateKey->Open(bstrTemp, // Container Name bstrTemp2, // CSP CAPICOM_PROV_RSA_FULL, CAPICOM_KEY_SPEC_SIGNATURE, CAPICOM_CURRENT_USER_STORE, TRUE); if (FAILED(hr) && (hr != NTE_PROV_TYPE_NO_MATCH)) hr = pIPrivateKey->Open(bstrTemp, // Container Name bstrTemp2, // CSP CAPICOM_PROV_RSA_FULL, CAPICOM_KEY_SPEC_KEYEXCHANGE, CAPICOM_CURRENT_USER_STORE, TRUE); if (FAILED(hr) && (hr != NTE_PROV_TYPE_NO_MATCH)) hr = pIPrivateKey->Open(bstrTemp, // Container Name bstrTemp2, // CSP CAPICOM_PROV_RSA_FULL, CAPICOM_KEY_SPEC_SIGNATURE, CAPICOM_LOCAL_MACHINE_STORE, TRUE); if (FAILED(hr) && (hr != NTE_PROV_TYPE_NO_MATCH)) hr = pIPrivateKey->Open(bstrTemp, // Container Name bstrTemp2, // CSP CAPICOM_PROV_RSA_FULL, CAPICOM_KEY_SPEC_KEYEXCHANGE, CAPICOM_LOCAL_MACHINE_STORE, TRUE); // If the provider type was wrong, then // find the right provider type and try again: if (hr == NTE_PROV_TYPE_NO_MATCH) { #ifdef SIGNTOOL_DEBUG if (gDebug) wprintf(L"Attempting to find the correct CSP Type...\n"); #endif if (GetProviderType(bstrTemp2, &dwTemp) == FALSE) { // This will most likely never happen, because in order to // get here the CSP must exist, but... ResErr(IDS_ERR_BAD_CSP); SysFreeString(bstrTemp); SysFreeString(bstrTemp2); dwErrors++; goto SignCleanupAndExit; } #ifdef SIGNTOOL_DEBUG if (gDebug) wprintf(L"Provider Type is: %d\n", dwTemp); #endif hr = pIPrivateKey->Open(bstrTemp, // Container Name bstrTemp2, // CSP (CAPICOM_PROV_TYPE) dwTemp, CAPICOM_KEY_SPEC_SIGNATURE, CAPICOM_CURRENT_USER_STORE, TRUE); if (FAILED(hr)) hr = pIPrivateKey->Open(bstrTemp, // Container Name bstrTemp2, // CSP (CAPICOM_PROV_TYPE) dwTemp, CAPICOM_KEY_SPEC_KEYEXCHANGE, CAPICOM_CURRENT_USER_STORE, TRUE); if (FAILED(hr)) hr = pIPrivateKey->Open(bstrTemp, // Container Name bstrTemp2, // CSP (CAPICOM_PROV_TYPE) dwTemp, CAPICOM_KEY_SPEC_SIGNATURE, CAPICOM_LOCAL_MACHINE_STORE, TRUE); if (FAILED(hr)) hr = pIPrivateKey->Open(bstrTemp, // Container Name bstrTemp2, // CSP (CAPICOM_PROV_TYPE) dwTemp, CAPICOM_KEY_SPEC_KEYEXCHANGE, CAPICOM_LOCAL_MACHINE_STORE, TRUE); } SysFreeString(bstrTemp); SysFreeString(bstrTemp2); if (FAILED(hr)) { switch (hr) { case NTE_BAD_KEYSET: // The CSP replied that the keyset does not exist ResErr(IDS_ERR_BAD_KEY_CONTAINER); break; case NTE_KEYSET_NOT_DEF: // The CSP probably doesn't exist ResErr(IDS_ERR_BAD_CSP); break; default: FormatIErrRet(L"IPrivateKey::Open", hr); ResErr(IDS_ERR_PRIV_KEY); } dwErrors++; goto SignCleanupAndExit; } // We've got a private key. Now associate it with the Cert: hr = pICert2Selected->put_PrivateKey(pIPrivateKey); if (FAILED(hr)) { switch (hr) { case NTE_BAD_PUBLIC_KEY: ResErr(IDS_ERR_PRIV_KEY_MISMATCH); break; case E_ACCESSDENIED: default: FormatIErrRet(L"ICertificate2::put_PrivateKey", hr); } dwErrors++; goto SignCleanupAndExit; } pIPrivateKey->Release(); pIPrivateKey = NULL; } else { // We don't have to add Key info, so just check that it's there: hr = pICert2Selected->HasPrivateKey(&boolTemp); if (FAILED(hr)) { FormatIErrRet(L"ICertificate2::HasPrivateKey", hr); dwErrors++; goto SignCleanupAndExit; } if (boolTemp == FALSE) { ResErr(IDS_ERR_CERT_NO_PRIV_KEY); dwErrors++; goto SignCleanupAndExit; } } #ifdef SIGNTOOL_DEBUG if (gDebug) { // Print the private key info of the selected cert: if (SUCCEEDED(pICert2Selected->get_PrivateKey(&pIPrivateKey))) { wprintf(L"Private Key Info:\n"); if (SUCCEEDED(pIPrivateKey->get_ProviderName(&bstrTemp))) { wprintf(L"\tProvider: %s\n", bstrTemp); SysFreeString(bstrTemp); } if (SUCCEEDED(pIPrivateKey->get_ContainerName(&bstrTemp))) { wprintf(L"\tContainer: %s\n", bstrTemp); SysFreeString(bstrTemp); } if (SUCCEEDED(pIPrivateKey->get_ProviderType(&provtypeTemp))) { wprintf(L"\tProvider Type: "); switch (provtypeTemp) { case CAPICOM_PROV_RSA_FULL: wprintf(L"RSA_FULL\n"); break; case CAPICOM_PROV_RSA_SIG: wprintf(L"RSA_SIG\n"); break; case CAPICOM_PROV_DSS: wprintf(L"DSS\n"); break; case CAPICOM_PROV_FORTEZZA: wprintf(L"FORTEZZA\n"); break; case CAPICOM_PROV_MS_EXCHANGE: wprintf(L"MS_EXCHANGE\n"); break; case CAPICOM_PROV_SSL: wprintf(L"SSL\n"); break; case CAPICOM_PROV_RSA_SCHANNEL: wprintf(L"RSA_SCHANNEL\n"); break; case CAPICOM_PROV_DSS_DH: wprintf(L"DSS_DH\n"); break; case CAPICOM_PROV_EC_ECDSA_SIG: wprintf(L"EC_ECDSA_SIG\n"); break; case CAPICOM_PROV_EC_ECNRA_SIG: wprintf(L"EC_ECNRA_SIG\n"); break; case CAPICOM_PROV_EC_ECDSA_FULL: wprintf(L"EC_ECDSA_FULL\n"); break; case CAPICOM_PROV_EC_ECNRA_FULL: wprintf(L"EC_ECNRA_FULL\n"); break; case CAPICOM_PROV_DH_SCHANNEL: wprintf(L"DH_SCHANNEL\n"); break; case CAPICOM_PROV_SPYRUS_LYNKS: wprintf(L"SPYRUS_LYNKS\n"); break; case CAPICOM_PROV_RNG: wprintf(L"RNG\n"); break; case CAPICOM_PROV_INTEL_SEC: wprintf(L"INTEL_SEC\n"); break; case CAPICOM_PROV_REPLACE_OWF: wprintf(L"REPLACE_OWF\n"); break; case CAPICOM_PROV_RSA_AES: wprintf(L"RSA_AES\n"); break; default: wprintf(L"Unrecognized Type (0x%08X)\n", provtypeTemp); } } if (SUCCEEDED(pIPrivateKey->get_KeySpec(&keyspecTemp))) { wprintf(L"\tKey Spec: "); switch (keyspecTemp) { case CAPICOM_KEY_SPEC_KEYEXCHANGE: wprintf(L"KEYEXCHANGE\n"); break; case CAPICOM_KEY_SPEC_SIGNATURE: wprintf(L"SIGNATURE\n"); break; default: wprintf(L"Unrecognized (0x%08X)\n", keyspecTemp); } } if (SUCCEEDED(pIPrivateKey->IsMachineKeyset(&boolTemp))) { wprintf(L"\tKey Set Type: "); if (boolTemp) wprintf(L"MACHINE\n"); else wprintf(L"USER\n"); } } } #endif // Create the SignedCode object: hr = CoCreateInstance(__uuidof(SignedCode), NULL, CLSCTX_ALL, __uuidof(ISignedCode), (LPVOID*)&pISignedCode); if (FAILED(hr)) { if ((hr == REGDB_E_CLASSNOTREG) || (hr == E_NOINTERFACE) || (hr == 0x8007007E)) { ResErr(IDS_ERR_CAPICOM_NOT_REG); } else { FormatErrRet(L"CoCreateInstance", hr); } dwErrors++; goto SignCleanupAndExit; } // Create and Build the Signer Object: hr = CoCreateInstance(__uuidof(Signer), NULL, CLSCTX_ALL, __uuidof(ISigner2), (LPVOID*)&pISigner2); if (FAILED(hr)) { if ((hr == REGDB_E_CLASSNOTREG) || (hr == E_NOINTERFACE) || (hr == 0x8007007E)) { ResErr(IDS_ERR_CAPICOM_NOT_REG); } else { FormatErrRet(L"CoCreateInstance", hr); } dwErrors++; goto SignCleanupAndExit; } hr = pISigner2->put_Certificate(pICert2Selected); if (FAILED(hr)) { FormatIErrRet(L"ISigner2::put_Certificate", hr); dwErrors++; goto SignCleanupAndExit; } hr = pISigner2->put_Options(CAPICOM_CERTIFICATE_INCLUDE_CHAIN_EXCEPT_ROOT); if (FAILED(hr)) { FormatIErrRet(L"ISigner2::put_Options", hr); dwErrors++; goto SignCleanupAndExit; } // If we opened our cert from a file, add that file to the Signer // as an additional cert store for optimal chaining. if (InputInfo->wszCertFile) { // Get the ICertStore interface: hr = pIStore2->QueryInterface(__uuidof(ICertStore), (LPVOID*)&pICertStore); if (FAILED(hr)) { if ((hr == REGDB_E_CLASSNOTREG) || (hr == E_NOINTERFACE) || (hr == 0x8007007E)) { ResErr(IDS_ERR_CAPICOM_NOT_REG); } else { FormatErrRet(L"ISigner2::QueryInterface", hr); } dwErrors++; goto SignCleanupAndExit; } // Get the HCERTSTORE of the store: hr = pICertStore->get_StoreHandle((LONG*) &hStore); if (FAILED(hr)) { FormatIErrRet(L"ICertStore::get_StoreHandle", hr); dwErrors++; goto SignCleanupAndExit; } // Get the ICSigner interface: hr = pISigner2->QueryInterface(__uuidof(ICSigner), (LPVOID*)&pICSigner); if (FAILED(hr)) { if ((hr == REGDB_E_CLASSNOTREG) || (hr == E_NOINTERFACE) || (hr == 0x8007007E)) { ResErr(IDS_ERR_CAPICOM_NOT_REG); } else { FormatErrRet(L"ISigner2::QueryInterface", hr); } dwErrors++; goto SignCleanupAndExit; } // Add the HCERTSTORE handle to the Signer: hr = pICSigner->put_AdditionalStore((LONG) hStore); if (FAILED(hr)) { FormatIErrRet(L"ICertStore::get_StoreHandle", hr); dwErrors++; goto SignCleanupAndExit; } //CertCloseStore(hStore, 0); //hStore = NULL; printf("Done Adding Additional Store\n"); } // Check if we are in the 32-bit Emulator on a 64-bit system if (InputInfo->fIsWow64Process) { // Disable WOW64 file-system redirection entirely for our FindFirst/NextFile OldWow64Setting = Wow64SetFilesystemRedirectorEx(WOW64_FILE_SYSTEM_DISABLE_REDIRECT_LEGACY); } // Loop over the files and sign them: for (DWORD i=0; iNumFiles; i++) { // Find the last slash in the path specification: LastSlash = 0; for (DWORD s=0; srgwszFileNames[i]); s++) { if ((wcsncmp(&(InputInfo->rgwszFileNames[i][s]), L"\\", 1) == 0) || (wcsncmp(&(InputInfo->rgwszFileNames[i][s]), L"/", 1) == 0) || (wcsncmp(&(InputInfo->rgwszFileNames[i][s]), L":", 1) == 0)) { // Set LastSlash to the character after the last slash: LastSlash = s + 1; } } wcsncpy(wszTempFileName, InputInfo->rgwszFileNames[i], MAX_PATH); wszTempFileName[MAX_PATH-1] = L'\0'; dwcFound = 0; hFind = FindFirstFileU(InputInfo->rgwszFileNames[i], &FindFileData); if (hFind == INVALID_HANDLE_VALUE) { // No files found matching that name dwErrors++; ResFormatErr(IDS_ERR_FILE_NOT_FOUND, InputInfo->rgwszFileNames[i]); continue; } do { if (!(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { dwcFound++; // Increment number of files (not dirs) found // matching this filespec // Copy the filename on after the last slash: wcsncpy(&(wszTempFileName[LastSlash]), FindFileData.cFileName, MAX_PATH-LastSlash); wszTempFileName[MAX_PATH-1] = L'\0'; if (InputInfo->Verbose) { ResFormatOut(IDS_INFO_SIGN_ATTEMPT, wszTempFileName); } if (InputInfo->fIsWow64Process) { // Disable WOW64 file-system redirection for our current file only Wow64SetFilesystemRedirectorEx(wszTempFileName); } // Set the filename in the SignedCode object: if ((bstrTemp = SysAllocString(wszTempFileName)) != NULL) { hr = pISignedCode->put_FileName(bstrTemp); if (FAILED(hr)) { FormatIErrRet(L"ISignedCode::put_FileName", hr); dwErrors++; if (InputInfo->fIsWow64Process) { // Disable WOW64 file-system redirection entirely for our FindFirst/NextFile Wow64SetFilesystemRedirectorEx(WOW64_FILE_SYSTEM_DISABLE_REDIRECT_LEGACY); } continue; } SysFreeString(bstrTemp); } else { FormatErrRet(L"SysAllocString", ERROR_OUTOFMEMORY); dwErrors++; if (InputInfo->fIsWow64Process) { // Disable WOW64 file-system redirection entirely for our FindFirst/NextFile Wow64SetFilesystemRedirectorEx(WOW64_FILE_SYSTEM_DISABLE_REDIRECT_LEGACY); } continue; } // Set the Description: if (InputInfo->wszDescription) { if ((bstrTemp = SysAllocString(InputInfo->wszDescription)) != NULL) { hr = pISignedCode->put_Description(bstrTemp); SysFreeString(bstrTemp); if (FAILED(hr)) { FormatIErrRet(L"ISignedCode::put_Description", hr); dwErrors++; if (InputInfo->fIsWow64Process) { // Disable WOW64 file-system redirection entirely for our FindFirst/NextFile Wow64SetFilesystemRedirectorEx(WOW64_FILE_SYSTEM_DISABLE_REDIRECT_LEGACY); } continue; } } else { FormatErrRet(L"SysAllocString", ERROR_OUTOFMEMORY); dwErrors++; if (InputInfo->fIsWow64Process) { // Disable WOW64 file-system redirection entirely for our FindFirst/NextFile Wow64SetFilesystemRedirectorEx(WOW64_FILE_SYSTEM_DISABLE_REDIRECT_LEGACY); } continue; } } // Set the Description URL: if (InputInfo->wszDescURL) { if ((bstrTemp = SysAllocString(InputInfo->wszDescURL)) != NULL) { hr = pISignedCode->put_DescriptionURL(bstrTemp); SysFreeString(bstrTemp); if (FAILED(hr)) { FormatIErrRet(L"ISignedCode::put_DescriptionURL", hr); dwErrors++; if (InputInfo->fIsWow64Process) { // Disable WOW64 file-system redirection entirely for our FindFirst/NextFile Wow64SetFilesystemRedirectorEx(WOW64_FILE_SYSTEM_DISABLE_REDIRECT_LEGACY); } continue; } } else { FormatErrRet(L"SysAllocString", ERROR_OUTOFMEMORY); dwErrors++; if (InputInfo->fIsWow64Process) { // Disable WOW64 file-system redirection entirely for our FindFirst/NextFile Wow64SetFilesystemRedirectorEx(WOW64_FILE_SYSTEM_DISABLE_REDIRECT_LEGACY); } continue; } } // Sign the file: hr = pISignedCode->Sign(pISigner2); if (SUCCEEDED(hr)) { if (InputInfo->wszTimeStampURL) { bstrTemp = SysAllocString(InputInfo->wszTimeStampURL); if (bstrTemp == NULL) { FormatErrRet(L"SysAllocString", ERROR_OUTOFMEMORY); dwErrors++; if (InputInfo->fIsWow64Process) { // Disable WOW64 file-system redirection entirely for our FindFirst/NextFile Wow64SetFilesystemRedirectorEx(WOW64_FILE_SYSTEM_DISABLE_REDIRECT_LEGACY); } continue; } hr = pISignedCode->Timestamp(bstrTemp); if (SUCCEEDED(hr)) { // Signing and Timestamping succeeded if (!InputInfo->Quiet) { ResFormatOut(IDS_INFO_SIGN_SUCCESS_T, wszTempFileName); } SysFreeString(bstrTemp); dwDone++; } else { // Signing succeeded, but timestamping failed if (!InputInfo->Quiet) { switch (hr) { case CAPICOM_E_CODE_NOT_SIGNED: ResErr(IDS_ERR_TIMESTAMP_NO_SIG); break; case CAPICOM_E_CODE_INVALID_TIMESTAMP_URL: case CRYPT_E_ASN1_BADTAG: ResErr(IDS_ERR_TIMESTAMP_BAD_URL); break; default: FormatIErrRet(L"ISignedCode::Timestamp", hr); } } ResFormatErr(IDS_WARN_SIGN_NO_TIMESTAMP, wszTempFileName); SysFreeString(bstrTemp); dwWarnings++; dwDone++; } } else { // Signing succeeded if (!InputInfo->Quiet) { ResFormatOut(IDS_INFO_SIGN_SUCCESS, wszTempFileName); } dwDone++; } } else { // Signing Failed if (!InputInfo->Quiet) { switch (hr) { case TRUST_E_SUBJECT_FORM_UNKNOWN: ResErr(IDS_ERR_SIGN_FILE_FORMAT); break; case E_ACCESSDENIED: ResErr(IDS_ERR_ACCESS_DENIED); break; case 0x80070020: // ERROR_SHARING_VIOLATION ResErr(IDS_ERR_SHARING_VIOLATION); break; case 0x800703EE: // STATUS_MAPPED_FILE_SIZE_ZERO ResErr(IDS_ERR_FILE_SIZE_ZERO); break; default: FormatIErrRet(L"ISignedCode::Sign", hr); } } ResFormatErr(IDS_ERR_SIGN, wszTempFileName); dwErrors++; } if (InputInfo->fIsWow64Process) { // Disable WOW64 file-system redirection entirely for our FindFirst/NextFile Wow64SetFilesystemRedirectorEx(WOW64_FILE_SYSTEM_DISABLE_REDIRECT_LEGACY); } } } while (FindNextFileU(hFind, &FindFileData)); if (dwcFound == 0) // No files were found matching this filespec { // this will only fire if only directories were found. dwErrors++; ResFormatErr(IDS_ERR_FILE_NOT_FOUND, InputInfo->rgwszFileNames[i]); continue; } FindClose(hFind); hFind = NULL; } if (InputInfo->fIsWow64Process) { // Re-ensable WOW64 file-system redirection Wow64SetFilesystemRedirectorEx(OldWow64Setting); } SignCleanupAndExit: //Print Summary Information: if (InputInfo->Verbose || (!InputInfo->Quiet && (dwErrors || dwWarnings))) { wprintf(L"\n"); if (InputInfo->Verbose || dwDone) ResFormatOut(IDS_INFO_SIGNED, dwDone); if (InputInfo->Verbose || dwWarnings) ResFormatOut(IDS_INFO_WARNINGS, dwWarnings); if (InputInfo->Verbose || dwErrors) ResFormatOut(IDS_INFO_ERRORS, dwErrors); } if (pISigner2) pISigner2->Release(); if (pICSigner) pICSigner->Release(); if (pISignedCode) pISignedCode->Release(); if (pIPrivateKey) pIPrivateKey->Release(); if (pICert2Selected) pICert2Selected->Release(); if (pICert2Temp) pICert2Temp->Release(); if (pICerts) pICerts->Release(); if (pICerts2Original) pICerts2Original->Release(); if (pICerts2Selected) pICerts2Selected->Release(); if (pICerts2Temp) pICerts2Temp->Release(); if (hStore) CertCloseStore(hStore, 0); if (pICertStore) pICertStore->Release(); if (pIStore2Temp) pIStore2Temp->Release(); if (pIStore2) pIStore2->Release(); CoUninitialize(); if (dwErrors) return 1; // Error if (dwWarnings) return 2; // Warning if (dwDone) return 0; // Success // One of the above returns should fire, so // this should never happen: ResErr(IDS_ERR_NO_FILES_DONE); ResErr(IDS_ERR_UNEXPECTED); return 1; // Error } int SignTool_SignWizard(INPUTINFO *InputInfo) { DWORD dwcFound; DWORD dwDone = 0; DWORD dwWarnings = 0; DWORD dwErrors = 0; WIN32_FIND_DATAW FindFileData; HANDLE hFind = NULL; HRESULT hr; PVOID OldWow64Setting; WCHAR wszTempFileName[MAX_PATH]; int LastSlash; CRYPTUI_WIZ_DIGITAL_SIGN_INFO DigitalSignInfo; // If no files were specified, launch the wizard without parameters: if (InputInfo->rgwszFileNames == NULL) { // Set up the Wizard's struct: ZeroMemory(&DigitalSignInfo, sizeof(CRYPTUI_WIZ_DIGITAL_SIGN_INFO)); DigitalSignInfo.dwSize = sizeof(CRYPTUI_WIZ_DIGITAL_SIGN_INFO); if (InputInfo->Verbose) { ResFormatOut(IDS_INFO_SIGNWIZARD_ATTEMPT, L"<>"); } // Invoke the Wizard: if (CryptUIWizDigitalSign(0, NULL, NULL, &DigitalSignInfo, NULL)) { // Success if (!InputInfo->Quiet) { ResFormatOut(IDS_INFO_SIGNWIZARD_SUCCESS, L"<>"); } dwDone++; } else { // Failure if (InputInfo->Verbose) { FormatErrRet(L"CryptUIWizDigitalSign", GetLastError()); } ResFormatErr(IDS_ERR_SIGNWIZARD, L"<>"); dwErrors++; } goto SignWizardCleanupAndExit; } // Check if we are in the 32-bit Emulator on a 64-bit system if (InputInfo->fIsWow64Process) { // Disable WOW64 file-system redirection entirely for our FindFirst/NextFile OldWow64Setting = Wow64SetFilesystemRedirectorEx(WOW64_FILE_SYSTEM_DISABLE_REDIRECT_LEGACY); } // Loop over the files and send them to the signing wizard: for (DWORD i=0; iNumFiles; i++) { // Find the last slash in the path specification: LastSlash = 0; for (DWORD s=0; srgwszFileNames[i]); s++) { if ((wcsncmp(&(InputInfo->rgwszFileNames[i][s]), L"\\", 1) == 0) || (wcsncmp(&(InputInfo->rgwszFileNames[i][s]), L"/", 1) == 0) || (wcsncmp(&(InputInfo->rgwszFileNames[i][s]), L":", 1) == 0)) { // Set LastSlash to the character after the last slash: LastSlash = s + 1; } } wcsncpy(wszTempFileName, InputInfo->rgwszFileNames[i], MAX_PATH); wszTempFileName[MAX_PATH-1] = L'\0'; dwcFound = 0; hFind = FindFirstFileU(InputInfo->rgwszFileNames[i], &FindFileData); if (hFind == INVALID_HANDLE_VALUE) { // No files found matching that name dwErrors++; ResFormatErr(IDS_ERR_FILE_NOT_FOUND, InputInfo->rgwszFileNames[i]); continue; } do { if (!(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { dwcFound++; // Increment number of files (not dirs) found // matching this filespec // Copy the filename on after the last slash: wcsncpy(&(wszTempFileName[LastSlash]), FindFileData.cFileName, MAX_PATH-LastSlash); wszTempFileName[MAX_PATH-1] = L'\0'; if (InputInfo->fIsWow64Process) { // Disable WOW64 file-system redirection for our current file only Wow64SetFilesystemRedirectorEx(wszTempFileName); } if (InputInfo->Verbose) { ResFormatOut(IDS_INFO_SIGNWIZARD_ATTEMPT, wszTempFileName); } // Set up the Wizard's struct: ZeroMemory(&DigitalSignInfo, sizeof(CRYPTUI_WIZ_DIGITAL_SIGN_INFO)); DigitalSignInfo.dwSize = sizeof(CRYPTUI_WIZ_DIGITAL_SIGN_INFO); DigitalSignInfo.dwSubjectChoice = CRYPTUI_WIZ_DIGITAL_SIGN_SUBJECT_FILE; DigitalSignInfo.pwszFileName = wszTempFileName; // Invoke the Wizard: if (CryptUIWizDigitalSign(0, NULL, NULL, &DigitalSignInfo, NULL)) { // Success if (!InputInfo->Quiet) { ResFormatOut(IDS_INFO_SIGNWIZARD_SUCCESS, wszTempFileName); } dwDone++; } else { // Failure if (InputInfo->Verbose) { FormatErrRet(L"CryptUIWizDigitalSign", GetLastError()); } ResFormatErr(IDS_ERR_SIGNWIZARD, wszTempFileName); dwErrors++; } if (InputInfo->fIsWow64Process) { // Disable WOW64 file-system redirection entirely for our FindFirst/NextFile Wow64SetFilesystemRedirectorEx(WOW64_FILE_SYSTEM_DISABLE_REDIRECT_LEGACY); } } } while (FindNextFileU(hFind, &FindFileData)); if (dwcFound == 0) // No files were found matching this filespec { // this will only fire if only directories were found. dwErrors++; ResFormatErr(IDS_ERR_FILE_NOT_FOUND, InputInfo->rgwszFileNames[i]); continue; } FindClose(hFind); hFind = NULL; } if (InputInfo->fIsWow64Process) { // Re-ensable WOW64 file-system redirection Wow64SetFilesystemRedirectorEx(OldWow64Setting); } SignWizardCleanupAndExit: if (dwErrors) return 1; // Error if (dwWarnings) return 2; // Warning if (dwDone) return 0; // Success // One of the above returns should fire, so // this should never happen: ResErr(IDS_ERR_NO_FILES_DONE); ResErr(IDS_ERR_UNEXPECTED); return 1; // Error } int SignTool_Timestamp(INPUTINFO *InputInfo) { DWORD dwcFound; DWORD dwDone = 0; DWORD dwWarnings = 0; DWORD dwErrors = 0; WIN32_FIND_DATAW FindFileData; HANDLE hFind = NULL; HRESULT hr; PVOID OldWow64Setting; WCHAR wszTempFileName[MAX_PATH]; ISignedCode *pISignedCode = NULL; BSTR bstrTemp; int LastSlash; // Initialize COM: if ((hr = CoInitialize(NULL)) != S_OK) { FormatErrRet(L"CoInitialize", hr); return 1; // Error } if (InputInfo->wszTimeStampURL == NULL) { ResErr(IDS_ERR_UNEXPECTED); dwErrors++; goto TimestampCleanupAndExit; } // Create the SignedCode object: hr = CoCreateInstance(__uuidof(SignedCode), NULL, CLSCTX_ALL, __uuidof(ISignedCode), (LPVOID*)&pISignedCode); if ((hr == REGDB_E_CLASSNOTREG) || (hr == E_NOINTERFACE) || (hr == 0x8007007E)) { // In this case, give it one more chance: RegisterCAPICOM(); hr = CoCreateInstance(__uuidof(SignedCode), NULL, CLSCTX_ALL, __uuidof(ISignedCode), (LPVOID*)&pISignedCode); } if (FAILED(hr)) { if ((hr == REGDB_E_CLASSNOTREG) || (hr == E_NOINTERFACE) || (hr == 0x8007007E)) { ResErr(IDS_ERR_CAPICOM_NOT_REG); } else { FormatErrRet(L"CoCreateInstance", hr); } dwErrors++; goto TimestampCleanupAndExit; } // Check if we are in the 32-bit Emulator on a 64-bit system if (InputInfo->fIsWow64Process) { // Disable WOW64 file-system redirection entirely for our FindFirst/NextFile OldWow64Setting = Wow64SetFilesystemRedirectorEx(WOW64_FILE_SYSTEM_DISABLE_REDIRECT_LEGACY); } // Loop over the files and timestamp them: for (DWORD i=0; iNumFiles; i++) { // Find the last slash in the path specification: LastSlash = 0; for (DWORD s=0; srgwszFileNames[i]); s++) { if ((wcsncmp(&(InputInfo->rgwszFileNames[i][s]), L"\\", 1) == 0) || (wcsncmp(&(InputInfo->rgwszFileNames[i][s]), L"/", 1) == 0) || (wcsncmp(&(InputInfo->rgwszFileNames[i][s]), L":", 1) == 0)) { // Set LastSlash to the character after the last slash: LastSlash = s + 1; } } wcsncpy(wszTempFileName, InputInfo->rgwszFileNames[i], MAX_PATH); wszTempFileName[MAX_PATH-1] = L'\0'; dwcFound = 0; hFind = FindFirstFileU(InputInfo->rgwszFileNames[i], &FindFileData); if (hFind == INVALID_HANDLE_VALUE) { // No files found matching that name dwErrors++; ResFormatErr(IDS_ERR_FILE_NOT_FOUND, InputInfo->rgwszFileNames[i]); continue; } do { if (!(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { dwcFound++; // Increment number of files (not dirs) found // matching this filespec // Copy the filename on after the last slash: wcsncpy(&(wszTempFileName[LastSlash]), FindFileData.cFileName, MAX_PATH-LastSlash); wszTempFileName[MAX_PATH-1] = L'\0'; if (InputInfo->fIsWow64Process) { // Disable WOW64 file-system redirection for our current file only Wow64SetFilesystemRedirectorEx(wszTempFileName); } if (InputInfo->Verbose) { ResFormatOut(IDS_INFO_TIMESTAMP_ATTEMPT, wszTempFileName); } // Set the filename in the SignedCode object: if ((bstrTemp = SysAllocString(wszTempFileName)) != NULL) { hr = pISignedCode->put_FileName(bstrTemp); if (FAILED(hr)) { FormatIErrRet(L"ISignedCode::put_FileName", hr); SysFreeString(bstrTemp); dwErrors++; if (InputInfo->fIsWow64Process) { // Disable WOW64 file-system redirection entirely for our FindFirst/NextFile Wow64SetFilesystemRedirectorEx(WOW64_FILE_SYSTEM_DISABLE_REDIRECT_LEGACY); } continue; } SysFreeString(bstrTemp); } else { FormatErrRet(L"SysAllocString", ERROR_OUTOFMEMORY); dwErrors++; if (InputInfo->fIsWow64Process) { // Disable WOW64 file-system redirection entirely for our FindFirst/NextFile Wow64SetFilesystemRedirectorEx(WOW64_FILE_SYSTEM_DISABLE_REDIRECT_LEGACY); } continue; } bstrTemp = SysAllocString(InputInfo->wszTimeStampURL); if (bstrTemp == NULL) { FormatErrRet(L"SysAllocString", ERROR_OUTOFMEMORY); dwErrors++; if (InputInfo->fIsWow64Process) { // Disable WOW64 file-system redirection entirely for our FindFirst/NextFile Wow64SetFilesystemRedirectorEx(WOW64_FILE_SYSTEM_DISABLE_REDIRECT_LEGACY); } continue; } // Timestamp the file: hr = pISignedCode->Timestamp(bstrTemp); if (SUCCEEDED(hr)) { // Timestamping succeeded if (!InputInfo->Quiet) { ResFormatOut(IDS_INFO_TIMESTAMP_SUCCESS, wszTempFileName); } dwDone++; } else { // Timestamping Failed if (!InputInfo->Quiet) { switch (hr) { case CAPICOM_E_CODE_NOT_SIGNED: ResErr(IDS_ERR_TIMESTAMP_NO_SIG); break; case CAPICOM_E_CODE_INVALID_TIMESTAMP_URL: case CRYPT_E_ASN1_BADTAG: ResErr(IDS_ERR_TIMESTAMP_BAD_URL); break; case E_ACCESSDENIED: ResErr(IDS_ERR_ACCESS_DENIED); break; case 0x80070020: // ERROR_SHARING_VIOLATION ResErr(IDS_ERR_SHARING_VIOLATION); break; case 0x800703EE: // STATUS_MAPPED_FILE_SIZE_ZERO ResErr(IDS_ERR_FILE_SIZE_ZERO); break; default: FormatIErrRet(L"ISignedCode::Timestamp", hr); } } ResFormatErr(IDS_ERR_TIMESTAMP, wszTempFileName); dwErrors++; } SysFreeString(bstrTemp); } if (InputInfo->fIsWow64Process) { // Disable WOW64 file-system redirection entirely for our FindFirst/NextFile Wow64SetFilesystemRedirectorEx(WOW64_FILE_SYSTEM_DISABLE_REDIRECT_LEGACY); } } while (FindNextFileU(hFind, &FindFileData)); if (dwcFound == 0) // No files were found matching this filespec { // this will only fire if only directories were found. dwErrors++; ResFormatErr(IDS_ERR_FILE_NOT_FOUND, InputInfo->rgwszFileNames[i]); continue; } FindClose(hFind); hFind = NULL; } if (InputInfo->fIsWow64Process) { // Re-ensable WOW64 file-system redirection Wow64SetFilesystemRedirectorEx(OldWow64Setting); } TimestampCleanupAndExit: //Print Summary Information: if (InputInfo->Verbose || (!InputInfo->Quiet && (dwErrors || dwWarnings))) { wprintf(L"\n"); if (InputInfo->Verbose || dwDone) ResFormatOut(IDS_INFO_TIMESTAMPED, dwDone); // Commented out because no warnings are possible in this function yet: // if (InputInfo->Verbose || dwWarnings) // ResFormatOut(IDS_INFO_WARNINGS, dwWarnings); if (InputInfo->Verbose || dwErrors) ResFormatOut(IDS_INFO_ERRORS, dwErrors); } if (pISignedCode) pISignedCode->Release(); CoUninitialize(); if (dwErrors) return 1; // Error if (dwWarnings) return 2; // Warning if (dwDone) return 0; // Success // One of the above returns should fire, so // this should never happen: ResErr(IDS_ERR_NO_FILES_DONE); ResErr(IDS_ERR_UNEXPECTED); return 1; // Error } void Format_IErrRet(WCHAR *wszFunc, DWORD dwErr) { BSTR bstrTemp; IErrorInfo *pIErrorInfo; if (SUCCEEDED(GetErrorInfo(0, &pIErrorInfo))) { if (SUCCEEDED(pIErrorInfo->GetDescription(&bstrTemp))) { ResFormat_Err(IDS_ERR_FUNCTION, wszFunc, dwErr, bstrTemp); SysFreeString(bstrTemp); } else { Format_ErrRet(wszFunc, dwErr); } pIErrorInfo->Release(); } else { Format_ErrRet(wszFunc, dwErr); } } void RegisterCAPICOM() { typedef HRESULT (STDAPICALLTYPE *PFN_DLL_REGISTER_SERVER) (void); WCHAR wszPath[MAX_PATH+1]; HMODULE hLib = NULL; PFN_DLL_REGISTER_SERVER pRegFunc; HRESULT hr; #ifdef SIGNTOOL_DEBUG if (gDebug) { wprintf(L"Attempting to register CAPICOM\n"); hr = E_UNEXPECTED; // Initialize to an Error } #endif if (GetModuleFileNameU(hModule, wszPath, MAX_PATH) && PathRemoveFileSpecW(wszPath) && (wcslen(wszPath) < (MAX_PATH-12)) && PathAppendW(wszPath, L"\\capicom.dll")) { #ifdef SIGNTOOL_DEBUG if (gDebug) { wprintf(L"Looking in: %s\n", wszPath); } #endif hLib = LoadLibraryU(wszPath); if (hLib != NULL) { pRegFunc = (PFN_DLL_REGISTER_SERVER) GetProcAddress(hLib, "DllRegisterServer"); if (pRegFunc != NULL) { hr = pRegFunc(); } #ifdef SIGNTOOL_DEBUG else { wprintf(L"GetProcAddress Failed with error: 0x%08X\n", GetLastError()); } #endif FreeLibrary(hLib); } #ifdef SIGNTOOL_DEBUG else { wprintf(L"LoadLibrary Failed with error: 0x%08X\n", GetLastError()); } #endif } #ifdef SIGNTOOL_DEBUG if (gDebug) { if (SUCCEEDED(hr)) wprintf(L"Successfully registered CAPICOM\n"); else wprintf(L"Failed to register CAPICOM\n"); } #endif } BOOL GetProviderType(LPWSTR pwszProvName, LPDWORD pdwProvType) { WCHAR rgProvName [MAX_PATH * sizeof(WCHAR)]; DWORD cb = sizeof(rgProvName); DWORD dwIndex = 0; memset(rgProvName, 0, sizeof(rgProvName)); while (CryptEnumProvidersU( dwIndex, NULL, 0, pdwProvType, rgProvName, &cb)) { if (0 == wcscmp(rgProvName, pwszProvName)) { return TRUE; } else { dwIndex++; cb = sizeof(rgProvName); } } return FALSE; } BOOL ChainsToRoot(HANDLE hWVTStateData, LPWSTR wszRootName) { CRYPT_PROVIDER_DATA *pCryptProvData; CRYPT_PROVIDER_SGNR *pCryptProvSgnr; ICertificates *pICerts = NULL; ICertificate2 *pICert2 = NULL; IChain2 *pIChain2 = NULL; IChainContext *pIChainContext = NULL; HRESULT hr; LONG longRootTemp; BSTR bstrTemp; VARIANT varTemp; VariantInit(&varTemp); if (hWVTStateData == NULL) { ResErr(IDS_ERR_UNEXPECTED); return FALSE; // Unexpected Error } pCryptProvData = WTHelperProvDataFromStateData(hWVTStateData); if (pCryptProvData == NULL) { ResErr(IDS_ERR_UNEXPECTED); return FALSE; // Unexpected Error } pCryptProvSgnr = WTHelperGetProvSignerFromChain(pCryptProvData, 0, FALSE, 0); if (pCryptProvSgnr == NULL) { ResErr(IDS_ERR_UNEXPECTED); return FALSE; // Unexpected Error } hr = CoCreateInstance(__uuidof(Chain), NULL, CLSCTX_ALL, __uuidof(IChainContext), (LPVOID*)&pIChainContext); if ((hr == REGDB_E_CLASSNOTREG) || (hr == E_NOINTERFACE) || (hr == 0x8007007E)) { // In this case, give it one more chance: RegisterCAPICOM(); hr = CoCreateInstance(__uuidof(Chain), NULL, CLSCTX_ALL, __uuidof(IChainContext), (LPVOID*)&pIChainContext); } if (FAILED(hr)) { if ((hr == REGDB_E_CLASSNOTREG) || (hr == E_NOINTERFACE) || (hr == 0x8007007E)) { ResErr(IDS_ERR_CAPICOM_NOT_REG); } else { FormatErrRet(L"CoCreateInstance", hr); } goto ErrorCleanup; } // fill in the pIChain2 with the Signer: pIChainContext->put_ChainContext((LONG)pCryptProvSgnr->pChainContext); // This will not compile on 64-bit architechtures. // Neither will CAPICOM, which is requiring this stupid typecast. // And then get the Chain2 interface: hr = pIChainContext->QueryInterface(__uuidof(IChain2), (LPVOID*)&pIChain2); if ((hr == REGDB_E_CLASSNOTREG) || (hr == E_NOINTERFACE) || (hr == 0x8007007E)) { // In this case, give it one more chance: RegisterCAPICOM(); hr = pIChainContext->QueryInterface(__uuidof(IChain2), (LPVOID*)&pIChain2); } if (FAILED(hr)) { if ((hr == REGDB_E_CLASSNOTREG) || (hr == E_NOINTERFACE) || (hr == 0x8007007E)) { ResErr(IDS_ERR_CAPICOM_NOT_REG); } else { FormatErrRet(L"IChainContext::QueryInterface", hr); } goto ErrorCleanup; } // Release the ChainContext interface: pIChainContext->Release(); pIChainContext = NULL; // Get the Certs collection from the Chain: hr = pIChain2->get_Certificates(&pICerts); if (FAILED(hr)) { FormatErrRet(L"IChain2::get_Certificates", hr); goto ErrorCleanup; } pIChain2->Release(); pIChain2 = NULL; // Get the Count in the Chain Certs list: hr = pICerts->get_Count(&longRootTemp); if (FAILED(hr)) { FormatErrRet(L"IChain2::get_Count", hr); goto ErrorCleanup; } // Sanity check: if (longRootTemp < 1) goto ErrorCleanup; // Get the last cert in the chain (the Root); hr = pICerts->get_Item(longRootTemp, &varTemp); if (FAILED(hr)) { FormatErrRet(L"IChain2::get_Item", hr); goto ErrorCleanup; } pICerts->Release(); pICerts = NULL; // Get the Certificate2 Interface: hr = varTemp.pdispVal->QueryInterface(__uuidof(ICertificate2), (LPVOID*)&pICert2); if (FAILED(hr)) { FormatErrRet(L"ICertificate::QueryInterface", hr); goto ErrorCleanup; } VariantClear(&varTemp); // Get the Name of the Root Cert: hr = pICert2->get_SubjectName(&bstrTemp); if (FAILED(hr)) { FormatErrRet(L"ICertificate2::get_SubjectName", hr); goto ErrorCleanup; } pICert2->Release(); pICert2 = NULL; _wcslwr(bstrTemp); // The Root name passed in must also be lowercased. if (wcsstr(bstrTemp, wszRootName) == NULL) { // Then this is the wrong Root Cert. // It failed. Report Error: #ifdef SIGNTOOL_DEBUG if (gDebug) { wprintf(L"Root subject name does not match: %s\n", bstrTemp); } #endif SysFreeString(bstrTemp); return FALSE; } else { // It matched. Success. #ifdef SIGNTOOL_DEBUG if (gDebug) { wprintf(L"Root subject name matches: %s\n", bstrTemp); } #endif SysFreeString(bstrTemp); return TRUE; } ErrorCleanup: if (pICert2) pICert2->Release(); if (pICerts) pICerts->Release(); if (pIChain2) pIChain2->Release(); if (pIChainContext) pIChainContext->Release(); return FALSE; } BOOL HasTimestamp(HANDLE hWVTStateData) { CRYPT_PROVIDER_DATA *pCryptProvData; CRYPT_PROVIDER_SGNR *pCryptProvSgnr; if (hWVTStateData == NULL) return FALSE; // Unexpected Error pCryptProvData = WTHelperProvDataFromStateData(hWVTStateData); if (pCryptProvData == NULL) return FALSE; // Unexpected Error pCryptProvSgnr = WTHelperGetProvSignerFromChain(pCryptProvData, 0, FALSE, 0); if (pCryptProvSgnr == NULL) return FALSE; // Unexpected Error return(pCryptProvSgnr->csCounterSigners == 1); // Valid result } void PrintSignerInfo(HANDLE hWVTStateData) { CRYPT_PROVIDER_DATA *pCryptProvData; CRYPT_PROVIDER_SGNR *pCryptProvSgnr; WCHAR wcsTemp[200]; IChain2 *pIChain2 = NULL; IChainContext *pIChainContext = NULL; COleDateTime DateTime; HRESULT hr; if (hWVTStateData == NULL) goto Cleanup; pCryptProvData = WTHelperProvDataFromStateData(hWVTStateData); if (pCryptProvData == NULL) goto Cleanup; pCryptProvSgnr = WTHelperGetProvSignerFromChain(pCryptProvData, 0, FALSE, 0); if (pCryptProvSgnr == NULL) goto Cleanup; hr = CoCreateInstance(__uuidof(Chain), NULL, CLSCTX_ALL, __uuidof(IChainContext), (LPVOID*)&pIChainContext); if ((hr == REGDB_E_CLASSNOTREG) || (hr == E_NOINTERFACE) || (hr == 0x8007007E)) { // In this case, give it one more chance: RegisterCAPICOM(); hr = CoCreateInstance(__uuidof(Chain), NULL, CLSCTX_ALL, __uuidof(IChainContext), (LPVOID*)&pIChainContext); } if (FAILED(hr)) { if ((hr == REGDB_E_CLASSNOTREG) || (hr == E_NOINTERFACE) || (hr == 0x8007007E)) { ResErr(IDS_ERR_CAPICOM_NOT_REG); } else { FormatErrRet(L"CoCreateInstance", hr); } goto Cleanup; } // fill in the pIChain2 with the Signer: pIChainContext->put_ChainContext((LONG)pCryptProvSgnr->pChainContext); // This will not compile on 64-bit architechtures. // Neither will CAPICOM, which is requiring this stupid typecast. // And then get the Chain2 interface: hr = pIChainContext->QueryInterface(__uuidof(IChain2), (LPVOID*)&pIChain2); if ((hr == REGDB_E_CLASSNOTREG) || (hr == E_NOINTERFACE) || (hr == 0x8007007E)) { // In this case, give it one more chance: RegisterCAPICOM(); hr = pIChainContext->QueryInterface(__uuidof(IChain2), (LPVOID*)&pIChain2); } if (FAILED(hr)) { if ((hr == REGDB_E_CLASSNOTREG) || (hr == E_NOINTERFACE) || (hr == 0x8007007E)) { ResErr(IDS_ERR_CAPICOM_NOT_REG); } else { FormatErrRet(L"IChainContext::QueryInterface", hr); } goto Cleanup; } // Release the ChainContext interface: pIChainContext->Release(); pIChainContext = NULL; // Print the Signer chain ResOut(IDS_INFO_VERIFY_SIGNER); PrintCertChain(pIChain2); pIChain2->Release(); pIChain2 = NULL; if (pCryptProvSgnr->csCounterSigners == 1) { // Then it's timestamped. DateTime = pCryptProvSgnr->sftVerifyAsOf; if (MultiByteToWideChar(CP_THREAD_ACP, 0, DateTime.Format(0, LANG_USER_DEFAULT), -1, wcsTemp, 199) == 0) { // Try again with ANSI codepage: MultiByteToWideChar(CP_ACP, 0, DateTime.Format(0, LANG_USER_DEFAULT), -1, wcsTemp, 199); } wcsTemp[199] = L'\0'; ResFormatOut(IDS_INFO_VERIFY_TIME, wcsTemp); ResOut(IDS_INFO_VERIFY_TIMESTAMP); // Build and print the timestamp chain: pCryptProvSgnr = WTHelperGetProvSignerFromChain(pCryptProvData, 0, TRUE, 0); if (pCryptProvSgnr == NULL) goto Cleanup; hr = CoCreateInstance(__uuidof(Chain), NULL, CLSCTX_ALL, __uuidof(IChainContext), (LPVOID*)&pIChainContext); if (FAILED(hr)) goto Cleanup; // fill in the pIChainContext with the Timestamper: pIChainContext->put_ChainContext((LONG)pCryptProvSgnr->pChainContext); // This will not compile on 64-bit architechtures. // Neither will CAPICOM, which is requiring this stupid typecast. // And then get the Chain2 interface: hr = pIChainContext->QueryInterface(__uuidof(IChain2), (LPVOID*)&pIChain2); if (FAILED(hr)) { if ((hr == REGDB_E_CLASSNOTREG) || (hr == E_NOINTERFACE) || (hr == 0x8007007E)) { ResErr(IDS_ERR_CAPICOM_NOT_REG); } else { FormatErrRet(L"ICertificates::QueryInterface", hr); } goto Cleanup; } // Release the ChainContext interface: pIChainContext->Release(); pIChainContext = NULL; // Print the Timestamper chain PrintCertChain(pIChain2); pIChain2->Release(); pIChain2 = NULL; } else { ResOut(IDS_INFO_VERIFY_NO_TIMESTAMP); } Cleanup: if (pIChain2) pIChain2->Release(); if (pIChainContext) pIChainContext->Release(); } void _indent(DWORD dwIndent) { for (DWORD i=0; iGetInfo(CAPICOM_CERT_INFO_SUBJECT_SIMPLE_NAME, &bstrTemp) == S_OK) { _indent(dwIndent); ResFormatOut(IDS_INFO_CERT_NAME, bstrTemp); SysFreeString(bstrTemp); } // Issued by: if (pICert2->GetInfo(CAPICOM_CERT_INFO_ISSUER_SIMPLE_NAME, &bstrTemp) == S_OK) { _indent(dwIndent); ResFormatOut(IDS_INFO_CERT_ISSUER, bstrTemp); SysFreeString(bstrTemp); } // Expiration date: if (pICert2->get_ValidToDate(&dateTemp) == S_OK) { DateTime = dateTemp; if (MultiByteToWideChar(CP_THREAD_ACP, 0, DateTime.Format(0, LANG_USER_DEFAULT), -1, wcsTemp, 199) == 0) { // Try again with ANSI codepage: MultiByteToWideChar(CP_ACP, 0, DateTime.Format(0, LANG_USER_DEFAULT), -1, wcsTemp, 199); } _indent(dwIndent); wcsTemp[199] = L'\0'; ResFormatOut(IDS_INFO_CERT_EXPIRE, wcsTemp); } // SHA1 hash: if (pICert2->get_Thumbprint(&bstrTemp) == S_OK) { _indent(dwIndent); ResFormatOut(IDS_INFO_CERT_SHA1, bstrTemp); SysFreeString(bstrTemp); } wprintf(L"\n"); } void PrintCertChain(IChain *pIChain) { ICertificates *pICerts = NULL; ICertificate *pICert = NULL; ICertificate2 *pICert2 = NULL; HRESULT hr; VARIANT varTemp; long longTemp; long l; VariantInit(&varTemp); hr = pIChain->get_Certificates(&pICerts); if (FAILED(hr)) { goto Cleanup; } hr = pICerts->get_Count(&longTemp); if (FAILED(hr)) { goto Cleanup; } if (longTemp < 1) { goto Cleanup; } for (l=longTemp; l>0; l--) { hr = pICerts->get_Item(l, &varTemp); if (FAILED(hr)) { goto Cleanup; } hr = varTemp.pdispVal->QueryInterface(__uuidof(ICertificate2), (LPVOID*)&pICert2); if (FAILED(hr)) { goto Cleanup; } VariantClear(&varTemp); PrintCertInfoIndented(pICert2, 4*(1+longTemp-l)); pICert2->Release(); pICert2 = NULL; } Cleanup: if (pICerts) pICerts->Release(); if (pICert) pICert->Release(); if (pICert2) pICert2->Release(); } int SignTool_Verify(INPUTINFO *InputInfo) { WIN32_FIND_DATAW FindFileData; HRESULT hr; HANDLE hFind; HANDLE hFile; HANDLE hCat = NULL; HCATADMIN hCatAdmin = NULL; HCATINFO hCatInfo = NULL; CATALOG_INFO CatInfo; CRYPTCATMEMBER *pCatMember; WINTRUST_DATA WVTData; WINTRUST_FILE_INFO_ WVTFile; WINTRUST_CATALOG_INFO_ WVTCat; GUID WVTGenericActionID = WINTRUST_ACTION_GENERIC_VERIFY_V2; GUID WVTDriverActionID = DRIVER_ACTION_VERIFY; DRIVER_VER_INFO DriverInfo; PVOID OldWow64Setting; DWORD dwcFound; DWORD dwDone = 0; DWORD dwWarnings = 0; DWORD dwErrors = 0; DWORD dwTemp; WCHAR wszTempFileName[MAX_PATH]; WCHAR wszSHA1[41]; int LastSlash; // Initialize COM: if ((hr = CoInitialize(NULL)) != S_OK) { FormatErrRet(L"CoInitialize", hr); return 1; // Error } // Check if we are in the 32-bit Emulator on a 64-bit system if (InputInfo->fIsWow64Process) { // Disable WOW64 file-system redirection entirely for our FindFirst/NextFile OldWow64Setting = Wow64SetFilesystemRedirectorEx(WOW64_FILE_SYSTEM_DISABLE_REDIRECT_LEGACY); } // Loop over the files and verify them: for (DWORD i=0; iNumFiles; i++) { // Find the last slash in the path specification: LastSlash = 0; for (DWORD s=0; srgwszFileNames[i]); s++) { if ((wcsncmp(&(InputInfo->rgwszFileNames[i][s]), L"\\", 1) == 0) || (wcsncmp(&(InputInfo->rgwszFileNames[i][s]), L"/", 1) == 0) || (wcsncmp(&(InputInfo->rgwszFileNames[i][s]), L":", 1) == 0)) { // Set LastSlash to the character after the last slash: LastSlash = s + 1; } } wcsncpy(wszTempFileName, InputInfo->rgwszFileNames[i], MAX_PATH); wszTempFileName[MAX_PATH-1] = L'\0'; dwcFound = 0; hFind = FindFirstFileU(InputInfo->rgwszFileNames[i], &FindFileData); if (hFind == INVALID_HANDLE_VALUE) { // No files found matching that name dwErrors++; ResFormatErr(IDS_ERR_FILE_NOT_FOUND, InputInfo->rgwszFileNames[i]); continue; } do { if (!(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { dwcFound++; // Increment number of files (not dirs) found // matching this filespec // For each file reset the WVT data to prevent contamination: memset(&WVTData, 0, sizeof(WINTRUST_DATA)); WVTData.cbStruct = sizeof(WINTRUST_DATA); WVTData.dwUIChoice = WTD_UI_NONE; WVTData.fdwRevocationChecks = WTD_REVOKE_NONE; memset(&WVTFile, 0, sizeof(WINTRUST_FILE_INFO_)); WVTFile.cbStruct = sizeof(WINTRUST_FILE_INFO_); memset(&WVTCat, 0, sizeof(WINTRUST_CATALOG_INFO_)); WVTCat.cbStruct = sizeof(WINTRUST_CATALOG_INFO_); memset(&DriverInfo, 0, sizeof(DRIVER_VER_INFO)); DriverInfo.cbStruct = sizeof(DRIVER_VER_INFO); if (InputInfo->wszVersion) { DriverInfo.dwPlatform = InputInfo->dwPlatform; DriverInfo.sOSVersionHigh.dwMajor = DriverInfo.sOSVersionLow.dwMajor = InputInfo->dwMajorVersion; DriverInfo.sOSVersionHigh.dwMinor = DriverInfo.sOSVersionLow.dwMinor = InputInfo->dwMinorVersion; DriverInfo.dwBuildNumberHigh = DriverInfo.dwBuildNumberLow = InputInfo->dwBuildNumber; } else { WVTData.dwProvFlags = WTD_USE_DEFAULT_OSVER_CHECK; } // Copy the filename on after the last slash: wcsncpy(&(wszTempFileName[LastSlash]), FindFileData.cFileName, MAX_PATH-LastSlash); wszTempFileName[MAX_PATH-1] = L'\0'; if (InputInfo->fIsWow64Process) { // Disable WOW64 file-system redirection for our current file only Wow64SetFilesystemRedirectorEx(wszTempFileName); } // Perform the action: // Start by opening the catalog database, or skipping // catalogs altogether: if (InputInfo->wszCatFile) { if (hCat == NULL) // Only open this on the first pass. { hCat = CryptCATOpen(InputInfo->wszCatFile, CRYPTCAT_OPEN_EXISTING, NULL, NULL, NULL); if ((hCat == NULL) || (hCat == INVALID_HANDLE_VALUE)) { switch (GetLastError()) { case ERROR_ACCESS_DENIED: ResErr(IDS_ERR_ACCESS_DENIED); break; case ERROR_SHARING_VIOLATION: ResErr(IDS_ERR_SHARING_VIOLATION); break; case ERROR_NOT_FOUND: ResFormatErr(IDS_ERR_FILE_NOT_FOUND, InputInfo->wszCatFile); break; default: FormatErrRet(L"CryptCATOpen", GetLastError()); } ResFormatErr(IDS_ERR_VERIFY_CAT_OPEN, InputInfo->wszCatFile); hCat = NULL; dwErrors++; goto VerifyCleanupAndExit; } } } else { switch (InputInfo->CatDbSelect) { case NoCatDb: if (InputInfo->Verbose) { ResFormatOut(IDS_INFO_VERIFY_ATTEMPT, wszTempFileName); } goto SkipCatalogs; break; case FullAutoCatDb: if (hCatAdmin == NULL) { CryptCATAdminAcquireContext(&hCatAdmin, NULL, NULL); } break; case SystemCatDb: case DefaultCatDb: case GuidCatDb: if (hCatAdmin == NULL) { CryptCATAdminAcquireContext(&hCatAdmin, &InputInfo->CatDbGuid, NULL); } break; default: // This should never happen because there are no other // legal values for Auto. ResFormatErr(IDS_ERR_UNEXPECTED); return 1; // Error } } // At this point we are dealing with catalog issues only. if (InputInfo->Verbose) { ResFormatOut(IDS_INFO_VERIFY_ATTEMPT, wszTempFileName); } // Create the hash for catalog lookup: if (InputInfo->SHA1.cbData == 0) { InputInfo->SHA1.pbData = (BYTE*)malloc(20); if (InputInfo->SHA1.pbData) { InputInfo->SHA1.cbData = 20; } else { if (!InputInfo->Quiet) { FormatErrRet(L"malloc", GetLastError()); } ResFormatErr(IDS_ERR_VERIFY, wszTempFileName); dwErrors++; goto VerifyNextFile; } } hFile = CreateFileU(wszTempFileName, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); if (!hFile) { if (!InputInfo->Quiet) { switch (GetLastError()) { case ERROR_ACCESS_DENIED: ResErr(IDS_ERR_ACCESS_DENIED); break; case ERROR_SHARING_VIOLATION: ResErr(IDS_ERR_SHARING_VIOLATION); break; default: FormatErrRet(L"CreateFile", GetLastError()); } } ResFormatErr(IDS_ERR_VERIFY, wszTempFileName); dwErrors++; goto VerifyNextFile; } if (!CryptCATAdminCalcHashFromFileHandle(hFile, &InputInfo->SHA1.cbData, InputInfo->SHA1.pbData, NULL)) { if (!InputInfo->Quiet) { switch (GetLastError()) { case ERROR_FILE_INVALID: ResErr(IDS_ERR_FILE_SIZE_ZERO); break; default: FormatErrRet(L"CryptCATAdminCalcHashFromFileHandle", GetLastError()); } } ResFormatErr(IDS_ERR_VERIFY, wszTempFileName); CloseHandle(hFile); dwErrors++; goto VerifyNextFile; } CloseHandle(hFile); for (DWORD j = 0; jSHA1.cbData; j++) { // Print the hash to a string: swprintf(&(wszSHA1[j*2]), L"%02X", InputInfo->SHA1.pbData[j]); } #ifdef SIGNTOOL_DEBUG if (gDebug) { wprintf(L"SHA1 hash of file: %s\n", wszSHA1); } #endif // Finished calculating the hash. // If the catalog was specifically selected if (InputInfo->wszCatFile) { // Then make sure the hash we found is in the catalog: pCatMember = CryptCATGetMemberInfo(hCat, wszSHA1); if (pCatMember) // Is the hash found in the catalog? { if (InputInfo->Verbose) { ResFormatOut(IDS_INFO_VERIFY_CAT, InputInfo->wszCatFile); } CatInfo.cbStruct = sizeof(CATALOG_INFO); wcsncpy(CatInfo.wszCatalogFile, InputInfo->wszCatFile, MAX_PATH); CatInfo.wszCatalogFile[MAX_PATH-1] = L'\0'; // Now verify the catalog: // Set up the rest of the WVT structure: WVTCat.pcwszCatalogFilePath = CatInfo.wszCatalogFile; WVTCat.pcwszMemberFilePath = wszTempFileName; WVTCat.pcwszMemberTag = wszSHA1; WVTCat.cbCalculatedFileHash = InputInfo->SHA1.cbData; WVTCat.pbCalculatedFileHash = InputInfo->SHA1.pbData; WVTData.dwUnionChoice = WTD_CHOICE_CATALOG; WVTData.pCatalog = &WVTCat; if (InputInfo->Verbose || InputInfo->TSWarn || InputInfo->wszRootName || (InputInfo->Policy != SystemDriver)) { WVTData.dwStateAction = WTD_STATEACTION_VERIFY; } else { WVTData.dwStateAction = WTD_STATEACTION_AUTO_CACHE; } // Call WinVerifyTrust to do the real work: switch (InputInfo->Policy) { case SystemDriver: WVTData.pPolicyCallbackData = &DriverInfo; hr = WinVerifyTrust(NULL, &WVTDriverActionID, &WVTData); break; case DefaultAuthenticode: hr = WinVerifyTrust(NULL, &WVTGenericActionID, &WVTData); break; case GuidActionID: hr = WinVerifyTrust(NULL, &InputInfo->PolicyGuid, &WVTData); break; default: // This should never happen because there are no other // legal values for Policy. ResFormatErr(IDS_ERR_UNEXPECTED); goto VerifyCleanupAndExit; } switch (hr) { case ERROR_SUCCESS: // Print the Signer information: if (InputInfo->Verbose) { PrintSignerInfo(WVTData.hWVTStateData); } // Check for Timestamp: if (InputInfo->TSWarn && !HasTimestamp(WVTData.hWVTStateData)) { ResFormatErr(IDS_WARN_VERIFY_NO_TS, wszTempFileName); dwWarnings++; } // Check Root Name: if (InputInfo->wszRootName && !ChainsToRoot(WVTData.hWVTStateData, InputInfo->wszRootName)) { ResErr(IDS_ERR_VERIFY_ROOT); break; } // Print Success message if (!InputInfo->Quiet) { ResFormatOut(IDS_INFO_VERIFY_SUCCESS, wszTempFileName); } // Close Verify State Data: WVTData.dwStateAction = WTD_STATEACTION_CLOSE; switch (InputInfo->Policy) { case SystemDriver: hr = WinVerifyTrust(NULL, &WVTDriverActionID, &WVTData); WVTData.pPolicyCallbackData = NULL; if (DriverInfo.pcSignerCertContext) { CertFreeCertificateContext(DriverInfo.pcSignerCertContext); } break; case DefaultAuthenticode: hr = WinVerifyTrust(NULL, &WVTGenericActionID, &WVTData); break; case GuidActionID: hr = WinVerifyTrust(NULL, &InputInfo->PolicyGuid, &WVTData); break; default: // This should never happen because there are no other // legal values for Policy. ResFormatErr(IDS_ERR_UNEXPECTED); goto VerifyCleanupAndExit; } dwDone++; goto VerifyNextFile; case ERROR_APP_WRONG_OS: if (!InputInfo->Quiet) { if (InputInfo->wszVersion) { // Failed to verify against user-specified OS version. ResFormatErr(IDS_ERR_VERIFY_VERSION); } else { // Failed to verify against current OS version ResFormatErr(IDS_ERR_VERIFY_CUR_VERSION); } } break; case CERT_E_WRONG_USAGE: ResErr(IDS_ERR_BAD_USAGE); if (InputInfo->Policy != DefaultAuthenticode) ResErr(IDS_ERR_TRY_OTHER_POLICY); break; case TRUST_E_NOSIGNATURE: if (!InputInfo->Quiet) { ResErr(IDS_ERR_NOT_SIGNED); } break; default: if (!InputInfo->Quiet) { FormatErrRet(L"WinVerifyTrust", hr); } if (InputInfo->Verbose) { PrintSignerInfo(WVTData.hWVTStateData); } } // Close Verify State Data: WVTData.dwStateAction = WTD_STATEACTION_CLOSE; switch (InputInfo->Policy) { case SystemDriver: hr = WinVerifyTrust(NULL, &WVTDriverActionID, &WVTData); WVTData.pPolicyCallbackData = NULL; if (DriverInfo.pcSignerCertContext) { CertFreeCertificateContext(DriverInfo.pcSignerCertContext); } break; case DefaultAuthenticode: hr = WinVerifyTrust(NULL, &WVTGenericActionID, &WVTData); break; case GuidActionID: hr = WinVerifyTrust(NULL, &InputInfo->PolicyGuid, &WVTData); break; default: // This should never happen because there are no other // legal values for Policy. ResFormatErr(IDS_ERR_UNEXPECTED); goto VerifyCleanupAndExit; } WVTData.dwStateAction = WTD_STATEACTION_VERIFY; } else { // The file was not found in the specified catalog. ResErr(IDS_ERR_VERIFY_NOT_IN_CAT); } // Then we failed to verify it using specified catalog. dwErrors++; ResFormatErr(IDS_ERR_VERIFY_INVALID, wszTempFileName); goto VerifyNextFile; } else { // Or else we should look up the catalog in the Cat DB: if (hCatInfo != NULL) { CryptCATAdminReleaseCatalogContext(hCatAdmin, hCatInfo, 0); hCatInfo = NULL; } memset(&CatInfo, 0, sizeof(CATALOG_INFO)); CatInfo.cbStruct = sizeof(CATALOG_INFO); hr = ERROR_SUCCESS; while ((hCatInfo = CryptCATAdminEnumCatalogFromHash(hCatAdmin, InputInfo->SHA1.pbData, InputInfo->SHA1.cbData, 0, &hCatInfo)) != NULL) { if (!(CryptCATCatalogInfoFromContext(hCatInfo, &CatInfo, 0))) { ResErr(IDS_ERR_UNEXPECTED); continue; } if (InputInfo->Verbose) { ResFormatOut(IDS_INFO_VERIFY_CAT, CatInfo.wszCatalogFile); } // Now verify the catalog: // Set up the rest of the WVT structure: WVTCat.pcwszCatalogFilePath = CatInfo.wszCatalogFile; WVTCat.pcwszMemberFilePath = wszTempFileName; WVTCat.pcwszMemberTag = wszSHA1; WVTCat.cbCalculatedFileHash = InputInfo->SHA1.cbData; WVTCat.pbCalculatedFileHash = InputInfo->SHA1.pbData; WVTData.dwUnionChoice = WTD_CHOICE_CATALOG; WVTData.pCatalog = &WVTCat; if (InputInfo->Verbose || InputInfo->TSWarn || InputInfo->wszRootName || (InputInfo->Policy != SystemDriver)) { WVTData.dwStateAction = WTD_STATEACTION_VERIFY; } else { WVTData.dwStateAction = WTD_STATEACTION_AUTO_CACHE; } // Call WinVerifyTrust to do the real work: switch (InputInfo->Policy) { case SystemDriver: WVTData.pPolicyCallbackData = &DriverInfo; hr = WinVerifyTrust(NULL, &WVTDriverActionID, &WVTData); break; case DefaultAuthenticode: hr = WinVerifyTrust(NULL, &WVTGenericActionID, &WVTData); break; case GuidActionID: hr = WinVerifyTrust(NULL, &InputInfo->PolicyGuid, &WVTData); break; default: // This should never happen because there are no other // legal values for Policy. ResFormatErr(IDS_ERR_UNEXPECTED); goto VerifyCleanupAndExit; } switch (hr) { case ERROR_SUCCESS: // Print the Signer information: if (InputInfo->Verbose) { PrintSignerInfo(WVTData.hWVTStateData); } // Check for Timestamp: if (InputInfo->TSWarn && !HasTimestamp(WVTData.hWVTStateData)) { ResFormatErr(IDS_WARN_VERIFY_NO_TS, wszTempFileName); dwWarnings++; } // Check Root Name: if (InputInfo->wszRootName && !ChainsToRoot(WVTData.hWVTStateData, InputInfo->wszRootName)) { ResErr(IDS_ERR_VERIFY_ROOT); break; } // Print Success message if (!InputInfo->Quiet) { ResFormatOut(IDS_INFO_VERIFY_SUCCESS, wszTempFileName); } // Close Verify State Data: WVTData.dwStateAction = WTD_STATEACTION_CLOSE; switch (InputInfo->Policy) { case SystemDriver: hr = WinVerifyTrust(NULL, &WVTDriverActionID, &WVTData); WVTData.pPolicyCallbackData = NULL; if (DriverInfo.pcSignerCertContext) { CertFreeCertificateContext(DriverInfo.pcSignerCertContext); } break; case DefaultAuthenticode: hr = WinVerifyTrust(NULL, &WVTGenericActionID, &WVTData); break; case GuidActionID: hr = WinVerifyTrust(NULL, &InputInfo->PolicyGuid, &WVTData); break; default: // This should never happen because there are no other // legal values for Policy. ResFormatErr(IDS_ERR_UNEXPECTED); goto VerifyCleanupAndExit; } dwDone++; goto VerifyNextFile; case ERROR_APP_WRONG_OS: if (!InputInfo->Quiet) { if (InputInfo->wszVersion) { // Failed to verify against user-specified OS version. ResFormatErr(IDS_ERR_VERIFY_VERSION); } else { // Failed to verify against current OS version ResFormatErr(IDS_ERR_VERIFY_CUR_VERSION); } } break; case CERT_E_WRONG_USAGE: ResErr(IDS_ERR_BAD_USAGE); if (InputInfo->Policy != DefaultAuthenticode) ResErr(IDS_ERR_TRY_OTHER_POLICY); break; case TRUST_E_NOSIGNATURE: if (!InputInfo->Quiet) { ResErr(IDS_ERR_NOT_SIGNED); } break; default: if (!InputInfo->Quiet) { FormatErrRet(L"WinVerifyTrust", hr); } if (InputInfo->Verbose) { PrintSignerInfo(WVTData.hWVTStateData); } } // Close Verify State Data: WVTData.dwStateAction = WTD_STATEACTION_CLOSE; switch (InputInfo->Policy) { case SystemDriver: hr = WinVerifyTrust(NULL, &WVTDriverActionID, &WVTData); WVTData.pPolicyCallbackData = NULL; if (DriverInfo.pcSignerCertContext) { CertFreeCertificateContext(DriverInfo.pcSignerCertContext); } break; case DefaultAuthenticode: hr = WinVerifyTrust(NULL, &WVTGenericActionID, &WVTData); break; case GuidActionID: hr = WinVerifyTrust(NULL, &InputInfo->PolicyGuid, &WVTData); break; default: // This should never happen because there are no other // legal values for Policy. ResFormatErr(IDS_ERR_UNEXPECTED); goto VerifyCleanupAndExit; } WVTData.dwStateAction = WTD_STATEACTION_VERIFY; } // Failed to verify using the catalog DB. dwTemp = GetLastError(); if ((InputInfo->Verbose) && (dwTemp != ERROR_NOT_FOUND)) { FormatErrRet(L"CryptCATAdminEnumCatalogFromHash", dwTemp); } // If we are on full auto, try direct signature. Otherwise // it's an error. if ((InputInfo->CatDbSelect == FullAutoCatDb) && (hr != ERROR_APP_WRONG_OS)) { if (InputInfo->Verbose) { ResOut(IDS_INFO_VERIFY_BADCAT); } // Reset the driver structure: memset(&DriverInfo, 0, sizeof(DRIVER_VER_INFO)); DriverInfo.cbStruct = sizeof(DRIVER_VER_INFO); if (InputInfo->wszVersion) { DriverInfo.dwPlatform = InputInfo->dwPlatform; DriverInfo.sOSVersionHigh.dwMajor = DriverInfo.sOSVersionLow.dwMajor = InputInfo->dwMajorVersion; DriverInfo.sOSVersionHigh.dwMinor = DriverInfo.sOSVersionLow.dwMinor = InputInfo->dwMinorVersion; DriverInfo.dwBuildNumberHigh = DriverInfo.dwBuildNumberLow = InputInfo->dwBuildNumber; } else { WVTData.dwProvFlags = WTD_USE_DEFAULT_OSVER_CHECK; } } else { hr = 0; ResFormatErr(IDS_ERR_VERIFY_INVALID, wszTempFileName); dwErrors++; goto VerifyNextFile; } } // Unable to verify using a catalog. // Done with all catalog stuff. SkipCatalogs: // Now try to verify if it is signed directly: memset(&WVTData, 0, sizeof(WINTRUST_DATA)); WVTData.cbStruct = sizeof(WINTRUST_DATA); WVTData.dwStateAction = WTD_STATEACTION_VERIFY; WVTData.dwUIChoice = WTD_UI_NONE; WVTData.fdwRevocationChecks = WTD_REVOKE_NONE; memset(&WVTFile, 0, sizeof(WINTRUST_FILE_INFO_)); WVTFile.cbStruct = sizeof(WINTRUST_FILE_INFO_); WVTFile.pcwszFilePath = wszTempFileName; WVTData.dwUnionChoice = WTD_CHOICE_FILE; WVTData.pFile = &WVTFile; //WVTData.pPolicyCallbackData = &DriverInfo; WVTData.pPolicyCallbackData = NULL; switch (InputInfo->Policy) { case SystemDriver: hr = WinVerifyTrust(NULL, &WVTDriverActionID, &WVTData); break; case DefaultAuthenticode: hr = WinVerifyTrust(NULL, &WVTGenericActionID, &WVTData); break; case GuidActionID: hr = WinVerifyTrust(NULL, &InputInfo->PolicyGuid, &WVTData); break; default: // This should never happen because there are no other // legal values for Policy. ResFormatErr(IDS_ERR_UNEXPECTED); goto VerifyCleanupAndExit; } if (hr == ERROR_SUCCESS) { // Print the Signer information: if (InputInfo->Verbose) { PrintSignerInfo(WVTData.hWVTStateData); } // Check for Timestamp: if (InputInfo->TSWarn && !HasTimestamp(WVTData.hWVTStateData)) { ResFormatErr(IDS_WARN_VERIFY_NO_TS, wszTempFileName); dwWarnings++; } // Check Root Name: if (InputInfo->wszRootName && !ChainsToRoot(WVTData.hWVTStateData, InputInfo->wszRootName)) { ResErr(IDS_ERR_VERIFY_ROOT); ResFormatErr(IDS_ERR_VERIFY_INVALID, wszTempFileName); dwErrors++; } else { // Print Success message if (!InputInfo->Quiet) { ResFormatOut(IDS_INFO_VERIFY_SUCCESS, wszTempFileName); } dwDone++; } } else { if (!InputInfo->Quiet) { switch (hr) { case ERROR_SUCCESS: break; case TRUST_E_SUBJECT_FORM_UNKNOWN: ResErr(IDS_ERR_VERIFY_FILE_FORMAT); break; case E_ACCESSDENIED: ResErr(IDS_ERR_ACCESS_DENIED); break; case 0x80070020: // ERROR_SHARING_VIOLATION ResErr(IDS_ERR_SHARING_VIOLATION); break; case 0x800703EE: // STATUS_MAPPED_FILE_SIZE_ZERO ResErr(IDS_ERR_FILE_SIZE_ZERO); break; case CERT_E_WRONG_USAGE: ResErr(IDS_ERR_BAD_USAGE); if (InputInfo->Policy != DefaultAuthenticode) ResErr(IDS_ERR_TRY_OTHER_POLICY); break; case TRUST_E_NOSIGNATURE: ResErr(IDS_ERR_NOT_SIGNED); break; case CERT_E_UNTRUSTEDROOT: ResErr(IDS_ERR_UNTRUSTED_ROOT); break; default: FormatErrRet(L"WinVerifyTrust", hr); } } if (InputInfo->Verbose) { PrintSignerInfo(WVTData.hWVTStateData); } ResFormatErr(IDS_ERR_VERIFY_INVALID, wszTempFileName); dwErrors++; } // Close Verify State Data: WVTData.dwStateAction = WTD_STATEACTION_CLOSE; switch (InputInfo->Policy) { case SystemDriver: hr = WinVerifyTrust(NULL, &WVTDriverActionID, &WVTData); break; case DefaultAuthenticode: hr = WinVerifyTrust(NULL, &WVTGenericActionID, &WVTData); break; case GuidActionID: hr = WinVerifyTrust(NULL, &InputInfo->PolicyGuid, &WVTData); break; default: // This should never happen because there are no other // legal values for Policy. ResFormatErr(IDS_ERR_UNEXPECTED); goto VerifyCleanupAndExit; } VerifyNextFile:; if (InputInfo->fIsWow64Process) { // Disable WOW64 file-system redirection entirely for our FindFirst/NextFile Wow64SetFilesystemRedirectorEx(WOW64_FILE_SYSTEM_DISABLE_REDIRECT_LEGACY); } } } while (FindNextFileU(hFind, &FindFileData)); if (dwcFound == 0) // No files were found matching this filespec { // this will only fire if only directories were found. dwErrors++; ResFormatErr(IDS_ERR_FILE_NOT_FOUND, InputInfo->rgwszFileNames[i]); continue; } FindClose(hFind); hFind = NULL; } VerifyCleanupAndExit: //Print Summary Information: if (InputInfo->Verbose || (!InputInfo->Quiet && (dwErrors || dwWarnings))) { wprintf(L"\n"); if (InputInfo->Verbose || dwDone) ResFormatOut(IDS_INFO_VERIFIED, dwDone); if (InputInfo->Verbose || dwWarnings) ResFormatOut(IDS_INFO_WARNINGS, dwWarnings); if (InputInfo->Verbose || dwErrors) ResFormatOut(IDS_INFO_ERRORS, dwErrors); } if (InputInfo->fIsWow64Process) Wow64SetFilesystemRedirectorEx(OldWow64Setting); if (hCatInfo) CryptCATAdminReleaseCatalogContext(hCatAdmin, hCatInfo, 0); if (hCatAdmin) CryptCATAdminReleaseContext(hCatAdmin, NULL); if (hCat) CryptCATClose(hCat); if ((InputInfo->SHA1.cbData == 20) && (InputInfo->SHA1.pbData)) { free(InputInfo->SHA1.pbData); InputInfo->SHA1.cbData = 0; InputInfo->SHA1.pbData = NULL; } CoUninitialize(); if (dwErrors) return 1; // Error if (dwWarnings) return 2; // Warning if (dwDone) return 0; // Success // One of the above returns should fire, so // this should never happen: ResErr(IDS_ERR_NO_FILES_DONE); ResErr(IDS_ERR_UNEXPECTED); return 1; // Error }