blob: 5aaa676225d68dc909d69a5f06e962fcff8f5de6 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/chrome_cleaner/test/scoped_process_protector.h"
#include <aclapi.h>
#include <string>
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/logging.h"
namespace chrome_cleaner {
ScopedProcessProtector::ScopedProcessProtector(uint32_t process_id) {
if (OpenProcess(process_id))
DenyAllAccess();
}
ScopedProcessProtector::ScopedProcessProtector(uint32_t process_id,
ACCESS_MASK access_to_deny) {
if (OpenProcess(process_id))
DenyAccess(access_to_deny);
}
ScopedProcessProtector::~ScopedProcessProtector() {
Release();
}
void ScopedProcessProtector::Release() {
if (initialized_) {
if (SetSecurityInfo(process_handle_.Get(), SE_KERNEL_OBJECT,
DACL_SECURITY_INFORMATION, NULL, NULL, original_dacl_,
NULL) != ERROR_SUCCESS) {
PLOG(ERROR) << "Failed to restore original DACL.";
}
}
if (original_descriptor_) {
::LocalFree(original_descriptor_);
original_dacl_ = nullptr;
original_descriptor_ = nullptr;
}
}
bool ScopedProcessProtector::OpenProcess(uint32_t process_id) {
// Get an anything-goes handle to the process.
process_handle_.Set(::OpenProcess(PROCESS_ALL_ACCESS, FALSE, process_id));
if (!process_handle_.IsValid()) {
PLOG(ERROR) << "Failed to open process: " << process_id;
return false;
}
// Store its existing DACL for cleanup purposes. This API function is weird:
// the pointer placed into |original_dacl_| is actually a pointer into a
// the structure pointed to by |original_descriptor_|. To use this, one
// stores both, but frees ONLY the structure stuffed into
// |original_descriptor_|.
if (GetSecurityInfo(process_handle_.Get(), SE_KERNEL_OBJECT,
DACL_SECURITY_INFORMATION, NULL, NULL, &original_dacl_,
NULL, &original_descriptor_) != ERROR_SUCCESS) {
PLOG(ERROR) << "Failed to retreieve original DACL.";
return false;
}
return true;
}
void ScopedProcessProtector::DenyAllAccess() {
// Set a new empty DACL, effectively denying all things on the process
// object.
ACL dacl;
if (!InitializeAcl(&dacl, sizeof(dacl), ACL_REVISION)) {
PLOG(ERROR) << "Failed to initialize DACL";
return;
}
if (SetSecurityInfo(process_handle_.Get(), SE_KERNEL_OBJECT,
DACL_SECURITY_INFORMATION, NULL, NULL, &dacl,
NULL) != ERROR_SUCCESS) {
PLOG(ERROR) << "Failed to set new DACL.";
return;
}
initialized_ = true;
}
void ScopedProcessProtector::DenyAccess(ACCESS_MASK access_to_deny) {
// The name of the predefined EVERYONE group.
static constexpr wchar_t kEveryoneGroup[] = L"EVERYONE";
// The Trustee parameter requires a non-const string.
std::wstring trustee_name(kEveryoneGroup);
EXPLICIT_ACCESS access = {};
access.grfAccessPermissions = access_to_deny;
access.grfAccessMode = DENY_ACCESS;
access.grfInheritance = NO_INHERITANCE;
access.Trustee.TrusteeForm = TRUSTEE_IS_NAME;
access.Trustee.ptstrName = &trustee_name[0];
// Create a new DACL that merges |access| into the existing DACL.
PACL new_dacl = nullptr;
if (SetEntriesInAcl(1, &access, original_dacl_, &new_dacl) != ERROR_SUCCESS) {
PLOG(ERROR) << "Failed to update DACL.";
return;
}
base::ScopedClosureRunner free_new_dacl(
base::BindOnce(base::IgnoreResult(&LocalFree), new_dacl));
if (SetSecurityInfo(process_handle_.Get(), SE_KERNEL_OBJECT,
DACL_SECURITY_INFORMATION, NULL, NULL, new_dacl,
NULL) != ERROR_SUCCESS) {
PLOG(ERROR) << "Failed to set new DACL.";
return;
}
initialized_ = true;
}
} // namespace chrome_cleaner