blob: f3904eeade985beddc69bc07cf48f31912af6f7c [file] [log] [blame]
// Copyright 2010-2014, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "win32/base/file_verifier.h"
#include <Windows.h>
#include <Imagehlp.h>
#include <SoftPub.h>
#include <Wintrust.h>
#include <string>
#include "base/const.h"
#include "base/file_util.h"
#include "base/logging.h"
#include "base/mmap.h"
#include "base/system_util.h"
#include "base/util.h"
namespace mozc {
namespace win32 {
namespace {
FileVerifier::IntegrityType VerifyPEHeaderChecksum(const string &filename) {
Mmap mapped_file;
if (!mapped_file.Open(filename.c_str())) {
return FileVerifier::kIntegrityFileOpenFailed;
}
// CheckSumMappedFile does not support multi-thread.
// TODO(yukawa): Add a critical section.
DWORD compile_time_check_sum = 0;
DWORD actual_check_sum = 0;
const IMAGE_NT_HEADERS *nt_header = ::CheckSumMappedFile(
mapped_file.begin(), mapped_file.size(),
&compile_time_check_sum, &actual_check_sum);
if (nt_header == nullptr || compile_time_check_sum == 0) {
return FileVerifier::kIntegrityUnknown;
}
if (compile_time_check_sum != actual_check_sum) {
return FileVerifier::kIntegrityCorrupted;
}
return FileVerifier::kIntegrityVerifiedWithPEChecksum;
}
} // namespace
FileVerifier::IntegrityType FileVerifier::VerifyIntegrity(
FileVerifier::MozcSystemFile system_file, string *filename_with_version) {
if (filename_with_version == nullptr) {
return kIntegrityInvalidParameter;
}
filename_with_version->clear();
string filepath;
string filename;
switch (system_file) {
case kMozcServerFile:
filename = kMozcServerName;
filepath = SystemUtil::GetServerPath();
break;
case kMozcRendererFile:
filename = kMozcRenderer;
filepath = SystemUtil::GetRendererPath();
break;
case kMozcToolFile:
filename = kMozcTool;
filepath = SystemUtil::GetToolPath();
break;
}
if (filepath.empty()) {
return kIntegrityFileNotFound;
}
wstring wfilename;
Util::UTF8ToWide(filename, &wfilename);
wstring wfilepath;
Util::UTF8ToWide(filepath, &wfilepath);
const IntegrityType result = VerifyIntegrityImpl(filepath);
int version_major = 0;
int version_minor = 0;
int version_build = 0;
int version_revision = 0;
if (SystemUtil::GetFileVersion(wfilepath, &version_major, &version_minor,
&version_build, &version_revision)) {
filename_with_version->assign(Util::StringPrintf(
"%s (%d.%d.%d.%d)", filename.c_str(), version_major, version_minor,
version_build, version_revision));
} else {
// Failed to retrieve version info. Use the filename as fallback.
filename_with_version->assign(filename);
}
return result;
}
FileVerifier::IntegrityType FileVerifier::VerifyIntegrityImpl(
const string &filepath) {
if (!FileUtil::FileExists(filepath)) {
return kIntegrityFileNotFound;
}
wstring wfilepath;
Util::UTF8ToWide(filepath, &wfilepath);
WINTRUST_FILE_INFO file_info = {};
file_info.cbStruct = sizeof(WINTRUST_FILE_INFO);
file_info.pcwszFilePath = wfilepath.c_str();
WINTRUST_DATA trust_data = {};
trust_data.cbStruct = sizeof(WINTRUST_DATA);
trust_data.dwUIChoice = WTD_UI_NONE;
trust_data.fdwRevocationChecks = WTD_REVOKE_NONE;
trust_data.dwUnionChoice = WTD_CHOICE_FILE;
trust_data.dwStateAction = WTD_STATEACTION_VERIFY;
trust_data.pFile = &file_info;
// Note: here we use WTD_HASH_ONLY_FLAG to check the hash equality only.
trust_data.dwProvFlags = WTD_HASH_ONLY_FLAG | WTD_REVOCATION_CHECK_NONE;
trust_data.dwUIContext = WTD_UICONTEXT_EXECUTE;
GUID guid = WINTRUST_ACTION_GENERIC_VERIFY_V2;
IntegrityType integrity_type = kIntegrityVerifiedWithAuthenticodeHash;
const LONG trust_result = ::WinVerifyTrust(
static_cast<HWND>(INVALID_HANDLE_VALUE), &guid, &trust_data);
if (trust_result != ERROR_SUCCESS) {
integrity_type = kIntegrityUnknown;
const DWORD error = ::GetLastError();
if (error == TRUST_E_NOSIGNATURE) {
// Note that TRUST_E_NOSIGNATURE is not only returned if the target file
// is actually signed and the hash does not match but also if the target
// file is not actually signed when WTD_HASH_ONLY_FLAG is specified.
// To distinguish the former from the latter, try to obtain the code
// sign info.
CRYPT_PROVIDER_DATA* cpd =
::WTHelperProvDataFromStateData(trust_data.hWVTStateData);
const CRYPT_PROVIDER_SGNR* cps =
::WTHelperGetProvSignerFromChain(cpd, 0, FALSE, 0);
if (cps != nullptr) {
// In this case, the target file is actually signed.
integrity_type = kIntegrityCorrupted;
}
} else if (error == TRUST_E_BAD_DIGEST) {
// Basically we should not reach here as long as WTD_HASH_ONLY_FLAG is
// specified. We keep this code just in case WTD_HASH_ONLY_FLAG is
// removed for some purpose.
integrity_type = kIntegrityCorrupted;
} else {
DCHECK_EQ(kIntegrityUnknown, integrity_type);
LOG(ERROR) << "WinVerifyTrust failed. error: " << error;
}
}
trust_data.dwStateAction = WTD_STATEACTION_CLOSE;
::WinVerifyTrust(nullptr, &guid, &trust_data);
// If the integrity state is still unknown, try to use PE header checksum
// to detect the data corruption.
if (integrity_type == kIntegrityUnknown) {
integrity_type = VerifyPEHeaderChecksum(filepath);
}
return integrity_type;
}
} // namespace win32
} // namespace mozc