Commit e2c08269 authored by Alex Gough's avatar Alex Gough Committed by Commit Bot

Refactor building of startup information.

Adds a wrapper to contain building proc_thread_attribute_* and to
provide storage for pointed-at items for call to
CreateProcessAsUserW().

This does not change functionality, but moves code from target_services
into a helper.

Bug: 1050359
Change-Id: I3d25e028638b2ea6f2917de1154be7a99fdf5971
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2330736Reviewed-by: default avatarWill Harris <wfh@chromium.org>
Commit-Queue: Alex Gough <ajgo@chromium.org>
Cr-Commit-Position: refs/heads/master@{#794618}
parent ef3c0186
......@@ -129,6 +129,8 @@ static_library("sandbox") {
"src/signed_interception.h",
"src/signed_policy.cc",
"src/signed_policy.h",
"src/startup_information_helper.cc",
"src/startup_information_helper.h",
"src/sync_dispatcher.cc",
"src/sync_dispatcher.h",
"src/sync_interception.cc",
......
......@@ -17,13 +17,13 @@
#include "base/threading/platform_thread.h"
#include "base/win/scoped_handle.h"
#include "base/win/scoped_process_information.h"
#include "base/win/startup_information.h"
#include "base/win/windows_version.h"
#include "sandbox/win/src/app_container_profile.h"
#include "sandbox/win/src/process_mitigations.h"
#include "sandbox/win/src/sandbox.h"
#include "sandbox/win/src/sandbox_policy_base.h"
#include "sandbox/win/src/sandbox_policy_diagnostic.h"
#include "sandbox/win/src/startup_information_helper.h"
#include "sandbox/win/src/target_process.h"
#include "sandbox/win/src/win2k_threadpool.h"
#include "sandbox/win/src/win_utils.h"
......@@ -438,147 +438,39 @@ ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path,
return result;
// Initialize the startup information from the policy.
base::win::StartupInformation startup_info;
auto startup_info = std::make_unique<StartupInformationHelper>();
// We don't want any child processes causing the IDC_APPSTARTING cursor.
startup_info.startup_info()->dwFlags |= STARTF_FORCEOFFFEEDBACK;
// The liftime of |mitigations|, |inherit_handle_list| and
// |child_process_creation| have to be at least as long as
// |startup_info| because |UpdateProcThreadAttribute| requires that
// its |lpValue| parameter persist until |DeleteProcThreadAttributeList| is
// called; StartupInformation's destructor makes such a call.
DWORD64 mitigations[2];
std::vector<HANDLE> inherited_handle_list;
DWORD child_process_creation = PROCESS_CREATION_CHILD_PROCESS_RESTRICTED;
std::wstring desktop = policy_base->GetAlternateDesktop();
if (!desktop.empty()) {
startup_info.startup_info()->lpDesktop =
const_cast<wchar_t*>(desktop.c_str());
}
bool inherit_handles = false;
int attribute_count = 0;
size_t mitigations_size;
ConvertProcessMitigationsToPolicy(policy_base->GetProcessMitigations(),
&mitigations[0], &mitigations_size);
if (mitigations[0] || mitigations[1])
++attribute_count;
startup_info->UpdateFlags(STARTF_FORCEOFFFEEDBACK);
startup_info->SetDesktop(policy_base->GetAlternateDesktop());
startup_info->SetMitigations(policy_base->GetProcessMitigations());
bool restrict_child_process_creation = false;
if (base::win::GetVersion() >= base::win::Version::WIN10_TH2 &&
policy_base->GetJobLevel() <= JOB_LIMITED_USER) {
restrict_child_process_creation = true;
++attribute_count;
startup_info->SetRestrictChildProcessCreation(true);
}
HANDLE stdout_handle = policy_base->GetStdoutHandle();
HANDLE stderr_handle = policy_base->GetStderrHandle();
if (stdout_handle != INVALID_HANDLE_VALUE)
inherited_handle_list.push_back(stdout_handle);
// Handles in the list must be unique.
if (stderr_handle != stdout_handle && stderr_handle != INVALID_HANDLE_VALUE)
inherited_handle_list.push_back(stderr_handle);
// Shares std handles if they are valid.
startup_info->SetStdHandles(policy_base->GetStdoutHandle(),
policy_base->GetStderrHandle());
// Add any additional handles that were requested.
const auto& policy_handle_list = policy_base->GetHandlesBeingShared();
for (HANDLE handle : policy_handle_list)
inherited_handle_list.push_back(handle);
if (inherited_handle_list.size())
++attribute_count;
startup_info->AddInheritedHandle(handle);
scoped_refptr<AppContainerProfileBase> profile =
policy_base->GetAppContainerProfileBase();
if (profile) {
if (base::win::GetVersion() < base::win::Version::WIN8)
return SBOX_ERROR_BAD_PARAMS;
++attribute_count;
if (profile->GetEnableLowPrivilegeAppContainer()) {
// LPAC first supported in RS1.
if (base::win::GetVersion() < base::win::Version::WIN10_RS1)
return SBOX_ERROR_BAD_PARAMS;
++attribute_count;
}
}
if (profile)
startup_info->SetAppContainerProfile(profile);
// On Win10, associate the process with these jobs at startup.
// Job handles must remain valid until TargetProcess::Create() completes.
std::vector<HANDLE> job_handle_list;
// On Win10, jobs are associated via startup_info.
if (base::win::GetVersion() >= base::win::Version::WIN10 && job.IsValid()) {
job_handle_list.push_back(job.Get());
++attribute_count;
startup_info->AddJobToAssociate(job.Get());
}
if (!startup_info.InitializeProcThreadAttributeList(attribute_count))
if (!startup_info->BuildStartupInformation())
return SBOX_ERROR_PROC_THREAD_ATTRIBUTES;
if (mitigations[0] || mitigations[1]) {
if (!startup_info.UpdateProcThreadAttribute(
PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, &mitigations[0],
mitigations_size)) {
return SBOX_ERROR_PROC_THREAD_ATTRIBUTES;
}
}
if (restrict_child_process_creation) {
if (!startup_info.UpdateProcThreadAttribute(
PROC_THREAD_ATTRIBUTE_CHILD_PROCESS_POLICY, &child_process_creation,
sizeof(child_process_creation))) {
return SBOX_ERROR_PROC_THREAD_ATTRIBUTES;
}
}
if (inherited_handle_list.size()) {
if (!startup_info.UpdateProcThreadAttribute(
PROC_THREAD_ATTRIBUTE_HANDLE_LIST, &inherited_handle_list[0],
sizeof(HANDLE) * inherited_handle_list.size())) {
return SBOX_ERROR_PROC_THREAD_ATTRIBUTES;
}
startup_info.startup_info()->dwFlags |= STARTF_USESTDHANDLES;
startup_info.startup_info()->hStdInput = INVALID_HANDLE_VALUE;
startup_info.startup_info()->hStdOutput = stdout_handle;
startup_info.startup_info()->hStdError = stderr_handle;
// Allowing inheritance of handles is only secure now that we
// have limited which handles will be inherited.
inherit_handles = true;
}
if (!job_handle_list.empty()) {
if (!startup_info.UpdateProcThreadAttribute(
PROC_THREAD_ATTRIBUTE_JOB_LIST, &job_handle_list[0],
sizeof(HANDLE) * job_handle_list.size())) {
return SBOX_ERROR_PROC_THREAD_ATTRIBUTES;
}
}
// Declared here to ensure they stay in scope until after process creation.
std::unique_ptr<SecurityCapabilities> security_capabilities;
DWORD all_applications_package_policy =
PROCESS_CREATION_ALL_APPLICATION_PACKAGES_OPT_OUT;
if (profile) {
security_capabilities = profile->GetSecurityCapabilities();
if (!startup_info.UpdateProcThreadAttribute(
PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES,
security_capabilities.get(), sizeof(SECURITY_CAPABILITIES))) {
return SBOX_ERROR_PROC_THREAD_ATTRIBUTES;
}
if (profile->GetEnableLowPrivilegeAppContainer()) {
if (!startup_info.UpdateProcThreadAttribute(
PROC_THREAD_ATTRIBUTE_ALL_APPLICATION_PACKAGES_POLICY,
&all_applications_package_policy,
sizeof(all_applications_package_policy))) {
return SBOX_ERROR_PROC_THREAD_ATTRIBUTES;
}
}
}
// Construct the thread pool here in case it is expensive.
// The thread pool is shared by all the targets
if (!thread_pool_)
......@@ -592,7 +484,7 @@ ResultCode BrokerServicesBase::SpawnTarget(const wchar_t* exe_path,
thread_pool_.get(),
profile ? profile->GetImpersonationCapabilities() : std::vector<Sid>());
result = target->Create(exe_path, command_line, inherit_handles, startup_info,
result = target->Create(exe_path, command_line, std::move(startup_info),
&process_info, last_error);
if (result != SBOX_ALL_OK) {
......
// Copyright 2020 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 "sandbox/win/src/startup_information_helper.h"
#include <Windows.h>
#include <algorithm>
#include <vector>
#include "base/check.h"
#include "base/memory/scoped_refptr.h"
#include "base/win/startup_information.h"
#include "base/win/windows_version.h"
#include "sandbox/win/src/app_container_profile.h"
#include "sandbox/win/src/security_capabilities.h"
namespace sandbox {
using base::win::StartupInformation;
StartupInformationHelper::StartupInformationHelper() {}
StartupInformationHelper::~StartupInformationHelper() {}
void StartupInformationHelper::UpdateFlags(DWORD flags) {
startup_info_.startup_info()->dwFlags |= flags;
}
void StartupInformationHelper::SetDesktop(std::wstring desktop) {
desktop_ = desktop;
if (!desktop_.empty()) {
startup_info_.startup_info()->lpDesktop =
const_cast<wchar_t*>(desktop_.c_str());
} else {
startup_info_.startup_info()->lpDesktop = nullptr;
}
}
void StartupInformationHelper::SetMitigations(MitigationFlags flags) {
ConvertProcessMitigationsToPolicy(flags, &mitigations_[0],
&mitigations_size_);
}
void StartupInformationHelper::SetRestrictChildProcessCreation(bool restrict) {
DCHECK(base::win::GetVersion() >= base::win::Version::WIN10_TH2);
restrict_child_process_creation_ = restrict;
}
void StartupInformationHelper::SetStdHandles(HANDLE stdout_handle,
HANDLE stderr_handle) {
stdout_handle_ = stdout_handle;
AddInheritedHandle(stdout_handle);
stderr_handle_ = stderr_handle;
if (stderr_handle != stdout_handle)
AddInheritedHandle(stderr_handle);
}
void StartupInformationHelper::AddInheritedHandle(HANDLE handle) {
if (handle != INVALID_HANDLE_VALUE) {
auto it = std::find(inherited_handle_list_.begin(),
inherited_handle_list_.end(), handle);
if (it == inherited_handle_list_.end())
inherited_handle_list_.push_back(handle);
}
}
void StartupInformationHelper::SetAppContainerProfile(
scoped_refptr<AppContainerProfileBase> profile) {
// Only supported for Windows 8+.
DCHECK(base::win::GetVersion() >= base::win::Version::WIN8);
// LowPrivilegeAppContainer only supported for Windows 10+
DCHECK(!profile->GetEnableLowPrivilegeAppContainer() ||
base::win::GetVersion() >= base::win::Version::WIN10_RS1);
app_container_profile_ = profile;
security_capabilities_ = app_container_profile_->GetSecurityCapabilities();
}
void StartupInformationHelper::AddJobToAssociate(HANDLE job_handle) {
job_handle_list_.push_back(job_handle);
}
int StartupInformationHelper::CountAttributes() {
int attribute_count = 0;
if (mitigations_[0] || mitigations_[1])
++attribute_count;
if (restrict_child_process_creation_)
++attribute_count;
if (!inherited_handle_list_.empty())
++attribute_count;
if (app_container_profile_) {
++attribute_count;
if (app_container_profile_->GetEnableLowPrivilegeAppContainer())
++attribute_count;
}
if (!job_handle_list_.empty())
++attribute_count;
return attribute_count;
}
bool StartupInformationHelper::BuildStartupInformation() {
// When adding new attributes, any memory referenced needs to have the
// same lifetime as startup_info_. This is why we use members below.
auto expected_attributes = CountAttributes();
if (!startup_info_.InitializeProcThreadAttributeList(expected_attributes))
return false;
if (mitigations_[0] || mitigations_[1]) {
if (!startup_info_.UpdateProcThreadAttribute(
PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY, &mitigations_[0],
mitigations_size_)) {
return false;
}
expected_attributes--;
}
if (restrict_child_process_creation_) {
child_process_creation_ = PROCESS_CREATION_CHILD_PROCESS_RESTRICTED;
if (!startup_info_.UpdateProcThreadAttribute(
PROC_THREAD_ATTRIBUTE_CHILD_PROCESS_POLICY,
&child_process_creation_, sizeof(child_process_creation_))) {
return false;
}
expected_attributes--;
}
if (inherited_handle_list_.size()) {
if (!startup_info_.UpdateProcThreadAttribute(
PROC_THREAD_ATTRIBUTE_HANDLE_LIST, &inherited_handle_list_[0],
sizeof(HANDLE) * inherited_handle_list_.size())) {
return false;
}
startup_info_.startup_info()->dwFlags |= STARTF_USESTDHANDLES;
startup_info_.startup_info()->hStdInput = INVALID_HANDLE_VALUE;
startup_info_.startup_info()->hStdOutput = stdout_handle_;
startup_info_.startup_info()->hStdError = stderr_handle_;
// Allowing inheritance of handles is only secure now that we
// have limited which handles will be inherited.
inherit_handles_ = true;
expected_attributes--;
}
if (!job_handle_list_.empty()) {
if (!startup_info_.UpdateProcThreadAttribute(
PROC_THREAD_ATTRIBUTE_JOB_LIST, &job_handle_list_[0],
sizeof(HANDLE) * job_handle_list_.size())) {
return false;
}
expected_attributes--;
}
if (app_container_profile_) {
if (!startup_info_.UpdateProcThreadAttribute(
PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES,
security_capabilities_.get(), sizeof(SECURITY_CAPABILITIES))) {
return false;
}
expected_attributes--;
if (app_container_profile_->GetEnableLowPrivilegeAppContainer()) {
all_applications_package_policy_ =
PROCESS_CREATION_ALL_APPLICATION_PACKAGES_OPT_OUT;
if (!startup_info_.UpdateProcThreadAttribute(
PROC_THREAD_ATTRIBUTE_ALL_APPLICATION_PACKAGES_POLICY,
&all_applications_package_policy_,
sizeof(all_applications_package_policy_))) {
return false;
}
expected_attributes--;
}
}
CHECK(expected_attributes == 0);
return true;
}
} // namespace sandbox
// Copyright 2020 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.
#ifndef SANDBOX_WIN_SRC_STARTUP_INFORMATION_HELPER_H_
#define SANDBOX_WIN_SRC_STARTUP_INFORMATION_HELPER_H_
#include <Windows.h>
#include <vector>
#include "base/memory/scoped_refptr.h"
#include "base/win/startup_information.h"
#include "base/win/windows_version.h"
#include "sandbox/win/src/app_container_profile_base.h"
#include "sandbox/win/src/process_mitigations.h"
#include "sandbox/win/src/security_capabilities.h"
namespace sandbox {
using base::win::StartupInformation;
// Wraps base::win::StartupInformation and allows some querying of what is
// set. This is specialized for the dance between
// BrokerServices::SpawnTarget() and TargetProcess::Create().
class StartupInformationHelper {
public:
StartupInformationHelper();
~StartupInformationHelper();
// Adds flags to the |dwFlags| field.
void UpdateFlags(DWORD flags);
// Sets |lpDesktop|. If |desktop| is empty, sets as nullptr.
void SetDesktop(std::wstring desktop);
// Creates PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY based on |flags|.
void SetMitigations(MitigationFlags flags);
// Creates PROC_THREAD_ATTRIBUTE_CHILD_PROCESS_POLICY if |restrict| is true.
void SetRestrictChildProcessCreation(bool restrict);
// Sets stdout and stderr handles. Also allows these to be inherited into
// the child process. Should be called once only.
void SetStdHandles(HANDLE stdout_handle, HANDLE stderr_handle);
// Ignores duplicate or invalid handles.
void AddInheritedHandle(HANDLE handle);
// Create PROC_THREAD_ATTRIBUTE_SECURITY_CAPABILITIES and
// PROC_THREAD_ATTRIBUTE_ALL_APPLICATION_PACKAGES_POLICY
// based on |profile|. |profile| should be valid.
void SetAppContainerProfile(scoped_refptr<AppContainerProfileBase> profile);
// Creates PROC_THREAD_ATTRIBUTE_JOB_LIST with |job_handle|. Not valid before
// Windows 10.
void AddJobToAssociate(HANDLE job_handle);
// Will one or more jobs be associated via the wrapped StartupInformation.
bool HasJobsToAssociate() { return !job_handle_list_.empty(); }
// Have handles been provided for secure inheritance?
bool ShouldInheritHandles() { return inherit_handles_; }
// Compiles fields into PROC_THREAD_ attributes and populates startup
// information. Must be called before GetStartupInformation().
bool BuildStartupInformation();
// Gets wrapped object, valid once BuildStartupInformation() has been called.
base::win::StartupInformation* GetStartupInformation() {
return &startup_info_;
}
private:
void operator=(const StartupInformationHelper&) = delete;
StartupInformationHelper(const StartupInformationHelper&) = delete;
int CountAttributes();
// Fields that are not passed into CreateProcessAsUserW().
scoped_refptr<AppContainerProfileBase> app_container_profile_ = nullptr;
bool restrict_child_process_creation_ = false;
HANDLE stdout_handle_ = INVALID_HANDLE_VALUE;
HANDLE stderr_handle_ = INVALID_HANDLE_VALUE;
bool inherit_handles_ = false;
size_t mitigations_size_ = 0;
// startup_info_.startup_info() is passed to CreateProcessAsUserW().
StartupInformation startup_info_;
// These need to have the same lifetime as startup_info_.startup_info();
std::wstring desktop_;
DWORD64 mitigations_[2];
DWORD child_process_creation_;
DWORD all_applications_package_policy_;
std::vector<HANDLE> inherited_handle_list_;
std::vector<HANDLE> job_handle_list_;
std::unique_ptr<SecurityCapabilities> security_capabilities_ = nullptr;
};
} // namespace sandbox
#endif // SANDBOX_WIN_SRC_STARTUP_INFORMATION_HELPER_H_
......@@ -24,6 +24,7 @@
#include "sandbox/win/src/security_capabilities.h"
#include "sandbox/win/src/sharedmem_ipc_server.h"
#include "sandbox/win/src/sid.h"
#include "sandbox/win/src/startup_information_helper.h"
#include "sandbox/win/src/win_utils.h"
namespace sandbox {
......@@ -139,12 +140,14 @@ TargetProcess::~TargetProcess() {
ResultCode TargetProcess::Create(
const wchar_t* exe_path,
const wchar_t* command_line,
bool inherit_handles,
const base::win::StartupInformation& startup_info,
std::unique_ptr<StartupInformationHelper> startup_info_helper,
base::win::ScopedProcessInformation* target_info,
DWORD* win_error) {
exe_name_.reset(_wcsdup(exe_path));
base::win::StartupInformation* startup_info =
startup_info_helper->GetStartupInformation();
// the command line needs to be writable by CreateProcess().
std::unique_ptr<wchar_t, base::FreeDeleter> cmd_line(_wcsdup(command_line));
......@@ -152,7 +155,7 @@ ResultCode TargetProcess::Create(
DWORD flags =
CREATE_SUSPENDED | CREATE_UNICODE_ENVIRONMENT | DETACHED_PROCESS;
if (startup_info.has_extended_startup_info())
if (startup_info->has_extended_startup_info())
flags |= EXTENDED_STARTUPINFO_PRESENT;
if (job_ && base::win::GetVersion() < base::win::Version::WIN8) {
......@@ -161,6 +164,7 @@ ResultCode TargetProcess::Create(
flags |= CREATE_BREAKAWAY_FROM_JOB;
}
bool inherit_handles = startup_info_helper->ShouldInheritHandles();
PROCESS_INFORMATION temp_process_info = {};
if (!::CreateProcessAsUserW(lockdown_token_.Get(), exe_path, cmd_line.get(),
nullptr, // No security attribute.
......@@ -168,14 +172,15 @@ ResultCode TargetProcess::Create(
inherit_handles, flags,
nullptr, // Use the environment of the caller.
nullptr, // Use current directory of the caller.
startup_info.startup_info(),
startup_info->startup_info(),
&temp_process_info)) {
*win_error = ::GetLastError();
return SBOX_ERROR_CREATE_PROCESS;
}
base::win::ScopedProcessInformation process_info(temp_process_info);
if (job_ && base::win::GetVersion() < base::win::Version::WIN10) {
if (job_ && !startup_info_helper->HasJobsToAssociate()) {
DCHECK(base::win::GetVersion() < base::win::Version::WIN10);
// Assign the suspended target to the windows job object. On Win 10
// this happens through PROC_THREAD_ATTRIBUTE_JOB_LIST.
if (!::AssignProcessToJobObject(job_, process_info.process_handle())) {
......
......@@ -20,19 +20,12 @@
#include "sandbox/win/src/crosscall_server.h"
#include "sandbox/win/src/sandbox_types.h"
namespace base {
namespace win {
class StartupInformation;
} // namespace win
} // namespace base
namespace sandbox {
class SharedMemIPCServer;
class Sid;
class ThreadProvider;
class StartupInformationHelper;
// TargetProcess models a target instance (child process). Objects of this
// class are owned by the Policy used to create them.
......@@ -49,8 +42,7 @@ class TargetProcess {
// Creates the new target process. The process is created suspended.
ResultCode Create(const wchar_t* exe_path,
const wchar_t* command_line,
bool inherit_handles,
const base::win::StartupInformation& startup_info,
std::unique_ptr<StartupInformationHelper> startup_info,
base::win::ScopedProcessInformation* target_info,
DWORD* win_error);
......@@ -105,7 +97,9 @@ class TargetProcess {
base::win::ScopedHandle initial_token_;
// Kernel handle to the shared memory used by the IPC server.
base::win::ScopedHandle shared_section_;
// Job object containing the target process.
// Job object containing the target process. This is used during
// process creation prior to Windows 10 and to identify the process in
// broker_services.cc.
HANDLE job_;
// Reference to the IPC subsystem.
std::unique_ptr<SharedMemIPCServer> ipc_server_;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment