[go: nahoru, domu]

blob: 76ed1d39f7177714dc99a2d912995b31c5d84bb6 [file] [log] [blame]
// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "mojo/public/cpp/platform/platform_handle_security_util_win.h"
#include <windows.h>
#include "base/containers/span.h"
#include "base/dcheck_is_on.h"
#include "base/debug/stack_trace.h"
#include "base/feature_list.h"
#include "base/features.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/no_destructor.h"
#include "base/strings/string_util.h"
#include "base/win/nt_status.h"
#include "base/win/scoped_handle.h"
#include "base/win/security_util.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
namespace mojo {
namespace {
FileHandleSecurityErrorCallback& GetErrorCallback() {
static base::NoDestructor<FileHandleSecurityErrorCallback> callback;
return *callback;
}
#if DCHECK_IS_ON()
std::wstring GetPathFromHandle(HANDLE handle) {
std::wstring full_path;
DWORD result = ::GetFinalPathNameByHandleW(
handle, base::WriteInto(&full_path, MAX_PATH + 1), MAX_PATH + 1, 0);
if (result > MAX_PATH) {
result = ::GetFinalPathNameByHandleW(
handle, base::WriteInto(&full_path, result), result, 0);
}
if (!result) {
PLOG(ERROR) << "Could not get full path for handle " << handle;
return std::wstring();
}
full_path.resize(result);
return full_path;
}
absl::optional<bool> IsReadOnlyHandle(HANDLE handle) {
absl::optional<ACCESS_MASK> flags = base::win::GetGrantedAccess(handle);
if (!flags.has_value()) {
return absl::nullopt;
}
// Cannot use GENERIC_WRITE as that includes SYNCHRONIZE.
// This is ~(all the writable permissions).
return !(flags.value() &
(FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES | FILE_WRITE_DATA |
FILE_WRITE_EA | WRITE_DAC | WRITE_OWNER | DELETE));
}
#endif // DCHECK_IS_ON();
} // namespace
void DcheckIfFileHandleIsUnsafe(HANDLE handle) {
#if DCHECK_IS_ON()
if (!base::FeatureList::IsEnabled(
base::features::kEnforceNoExecutableFileHandles)) {
return;
}
if (GetFileType(handle) != FILE_TYPE_DISK) {
return;
}
absl::optional<bool> is_read_only = IsReadOnlyHandle(handle);
if (!is_read_only.has_value()) {
// If unable to obtain whether or not the handle is read-only, skip the rest
// of the checks, since it's likely GetPathFromHandle below would fail
// anyway.
return;
}
if (*is_read_only) {
// Handle is read-only so considered safe.
return;
}
std::wstring path = GetPathFromHandle(handle);
if (path.empty()) {
return;
}
base::win::ScopedHandle file_handle(
::CreateFileW(path.c_str(), FILE_READ_DATA | FILE_EXECUTE,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr));
// If the file cannot be opened with FILE_EXECUTE it means that it is safe.
if (!file_handle.is_valid()) {
return;
}
auto& error_callback = GetErrorCallback();
if (error_callback) {
bool handled = error_callback.Run();
if (handled) {
return;
}
}
DLOG(FATAL) << "Transfer of writable handle to executable file to an "
"untrusted process: "
<< path
<< ". Please use AddFlagsForPassingToUntrustedProcess or call "
"PreventExecuteMapping on the FilePath.";
#endif // DCHECK_IS_ON();
}
void SetUnsafeFileHandleCallbackForTesting(
FileHandleSecurityErrorCallback callback) {
GetErrorCallback() = std::move(callback);
}
} // namespace mojo