#include "content/browser/service_host/utility_sandbox_delegate.h"
#include "base/check.h"
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/files/file_path.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/waitable_event.h"
#include "base/win/scoped_handle.h"
#include "base/win/windows_handle_util.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/sandboxed_process_launcher_delegate.h"
#include "content/utility/sandbox_delegate_data.mojom.h"
#include "sandbox/policy/features.h"
#include "sandbox/policy/mojom/sandbox.mojom.h"
#include "sandbox/policy/win/sandbox_win.h"
#include "sandbox/win/src/app_container.h"
#include "sandbox/win/src/sandbox_policy.h"
#include "sandbox/win/src/sandbox_types.h"
#include "services/network/public/mojom/network_service.mojom.h"
namespace content {
namespace {
bool AudioInitializeConfig(sandbox::TargetConfig* config) {
DCHECK(!config->IsConfigured());
auto result = config->SetIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW);
if (result != sandbox::SBOX_ALL_OK) {
return false;
}
config->SetLockdownDefaultDacl();
config->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW);
result = config->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS,
sandbox::USER_RESTRICTED_NON_ADMIN);
if (result != sandbox::SBOX_ALL_OK) {
return false;
}
config->SetDesktop(sandbox::Desktop::kAlternateWinstation);
return true;
}
bool NetworkInitializeConfig(sandbox::TargetConfig* config) {
DCHECK(!config->IsConfigured());
auto result = config->SetTokenLevel(sandbox::USER_UNPROTECTED,
sandbox::USER_UNPROTECTED);
if (result != sandbox::SBOX_ALL_OK) {
return false;
}
auto lpac_capability =
GetContentClient()->browser()->GetLPACCapabilityNameForNetworkService();
if (lpac_capability.empty()) {
return false;
}
auto* app_container = config->GetAppContainer();
if (!app_container) {
return false;
}
app_container->AddCapability(lpac_capability.c_str());
app_container->AddCapabilitySddl(
L"S-1-15-3-893703388-718787801-2109771152-172907555-2119217564-716812919-"
L"652991501");
return true;
}
bool PrintBackendInitializeConfig(sandbox::TargetConfig* config) {
DCHECK(!config->IsConfigured());
auto result = config->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS,
sandbox::USER_LIMITED);
if (result != sandbox::SBOX_ALL_OK) {
return false;
}
config->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW);
return true;
}
std::string UtilityAppContainerId(base::CommandLine& cmd_line) {
return base::WideToUTF8(cmd_line.GetProgram().value());
}
bool IconReaderInitializeConfig(sandbox::TargetConfig* config) {
DCHECK(!config->IsConfigured());
auto result = config->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS,
sandbox::USER_LOCKDOWN);
if (result != sandbox::SBOX_ALL_OK) {
return false;
}
config->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_UNTRUSTED);
result = config->SetIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW);
if (result != sandbox::SBOX_ALL_OK) {
return false;
}
config->SetLockdownDefaultDacl();
config->SetDesktop(sandbox::Desktop::kAlternateWinstation);
sandbox::MitigationFlags flags = config->GetDelayedProcessMitigations();
flags |= sandbox::MITIGATION_DYNAMIC_CODE_DISABLE;
result = config->SetDelayedProcessMitigations(flags);
if (result != sandbox::SBOX_ALL_OK) {
return false;
}
return true;
}
bool OnDeviceModelExecutionInitializeConfig(
sandbox::TargetConfig* config,
base::CommandLine& cmd_line,
sandbox::mojom::Sandbox sandbox_type) {
DCHECK(!config->IsConfigured());
sandbox::ResultCode result = config->SetTokenLevel(
sandbox::USER_RESTRICTED_SAME_ACCESS, sandbox::USER_LIMITED);
if (result != sandbox::SBOX_ALL_OK) {
return false;
}
return true;
}
bool XrCompositingInitializeConfig(sandbox::TargetConfig* config,
base::CommandLine& cmd_line,
sandbox::mojom::Sandbox sandbox_type) {
DCHECK(!config->IsConfigured());
auto result = config->SetTokenLevel(sandbox::USER_UNPROTECTED,
sandbox::USER_UNPROTECTED);
if (result != sandbox::SBOX_ALL_OK) {
return false;
}
result = sandbox::policy::SandboxWin::SetJobLevel(
sandbox_type, sandbox::JobLevel::kUnprotected, 0, config);
if (result != sandbox::SBOX_ALL_OK) {
return false;
}
result = config->SetProcessMitigations(0);
if (result != sandbox::SBOX_ALL_OK) {
return false;
}
result = config->SetDelayedProcessMitigations(0);
if (result != sandbox::SBOX_ALL_OK) {
return false;
}
std::string appcontainer_id = UtilityAppContainerId(cmd_line);
result = sandbox::policy::SandboxWin::AddAppContainerProfileToConfig(
cmd_line, sandbox_type, appcontainer_id, config);
if (result != sandbox::SBOX_ALL_OK) {
return false;
}
return true;
}
bool ScreenAIInitializeConfig(sandbox::TargetConfig* config,
sandbox::mojom::Sandbox sandbox_type) {
DCHECK(!config->IsConfigured());
auto result = config->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS,
sandbox::USER_LOCKDOWN);
if (result != sandbox::SBOX_ALL_OK) {
return false;
}
result = sandbox::policy::SandboxWin::SetJobLevel(
sandbox_type, sandbox::JobLevel::kLimitedUser, 0, config);
if (result != sandbox::SBOX_ALL_OK) {
return false;
}
return true;
}
}
std::string UtilitySandboxedProcessLauncherDelegate::GetSandboxTag() {
return sandbox::policy::SandboxWin::GetSandboxTagForDelegate(
"utility", GetSandboxType());
}
bool UtilitySandboxedProcessLauncherDelegate::GetAppContainerId(
std::string* appcontainer_id) {
if (app_container_disabled_) {
return false;
}
switch (sandbox_type_) {
case sandbox::mojom::Sandbox::kMediaFoundationCdm:
case sandbox::mojom::Sandbox::kNetwork:
case sandbox::mojom::Sandbox::kOnDeviceModelExecution:
case sandbox::mojom::Sandbox::kProxyResolver:
case sandbox::mojom::Sandbox::kXrCompositing:
*appcontainer_id = UtilityAppContainerId(cmd_line_);
return true;
case sandbox::mojom::Sandbox::kPrintCompositor:
if (base::FeatureList::IsEnabled(
sandbox::policy::features::kPrintCompositorLPAC)) {
*appcontainer_id = UtilityAppContainerId(cmd_line_);
return true;
}
return false;
default:
return false;
}
}
bool UtilitySandboxedProcessLauncherDelegate::DisableDefaultPolicy() {
std::string app_container_id;
if (GetAppContainerId(&app_container_id)) {
return true;
}
switch (sandbox_type_) {
case sandbox::mojom::Sandbox::kAudio:
return true;
default:
return false;
}
}
bool UtilitySandboxedProcessLauncherDelegate::ShouldLaunchElevated() {
return sandbox_type_ ==
sandbox::mojom::Sandbox::kNoSandboxAndElevatedPrivileges;
}
bool UtilitySandboxedProcessLauncherDelegate::InitializeConfig(
sandbox::TargetConfig* config) {
DCHECK(!config->IsConfigured());
if (sandbox_type_ == sandbox::mojom::Sandbox::kAudio) {
if (!AudioInitializeConfig(config)) {
return false;
}
}
if (sandbox_type_ == sandbox::mojom::Sandbox::kNetwork) {
if (!NetworkInitializeConfig(config)) {
return false;
}
}
if (sandbox_type_ == sandbox::mojom::Sandbox::kIconReader) {
if (!IconReaderInitializeConfig(config)) {
return false;
}
}
if (sandbox_type_ == sandbox::mojom::Sandbox::kOnDeviceModelExecution) {
if (!OnDeviceModelExecutionInitializeConfig(config, cmd_line_,
sandbox_type_)) {
return false;
}
}
if (sandbox_type_ == sandbox::mojom::Sandbox::kXrCompositing) {
if (!XrCompositingInitializeConfig(config, cmd_line_, sandbox_type_)) {
return false;
}
}
if (sandbox_type_ == sandbox::mojom::Sandbox::kScreenAI) {
if (!ScreenAIInitializeConfig(config, sandbox_type_)) {
return false;
}
}
if (sandbox_type_ == sandbox::mojom::Sandbox::kSpeechRecognition) {
auto result = config->SetIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW);
if (result != sandbox::SBOX_ALL_OK) {
return false;
}
config->SetDelayedIntegrityLevel(sandbox::INTEGRITY_LEVEL_LOW);
result = config->SetTokenLevel(sandbox::USER_RESTRICTED_SAME_ACCESS,
sandbox::USER_LIMITED);
if (result != sandbox::SBOX_ALL_OK) {
return false;
}
}
if (sandbox_type_ == sandbox::mojom::Sandbox::kMediaFoundationCdm) {
auto result = config->SetTokenLevel(sandbox::USER_UNPROTECTED,
sandbox::USER_UNPROTECTED);
if (result != sandbox::SBOX_ALL_OK) {
return false;
}
}
if (sandbox_type_ == sandbox::mojom::Sandbox::kProxyResolver) {
auto result = config->SetTokenLevel(sandbox::USER_UNPROTECTED,
sandbox::USER_UNPROTECTED);
if (result != sandbox::SBOX_ALL_OK) {
return false;
}
}
if (sandbox_type_ == sandbox::mojom::Sandbox::kService ||
sandbox_type_ == sandbox::mojom::Sandbox::kServiceWithJit) {
auto result = sandbox::policy::SandboxWin::AddWin32kLockdownPolicy(config);
if (result != sandbox::SBOX_ALL_OK) {
return false;
}
config->SetFilterEnvironment(true);
}
if (sandbox_type_ == sandbox::mojom::Sandbox::kService) {
auto delayed_flags = config->GetDelayedProcessMitigations();
delayed_flags |= sandbox::MITIGATION_DYNAMIC_CODE_DISABLE;
auto result = config->SetDelayedProcessMitigations(delayed_flags);
if (result != sandbox::SBOX_ALL_OK) {
return false;
}
}
if (sandbox_type_ == sandbox::mojom::Sandbox::kPrintBackend) {
if (!PrintBackendInitializeConfig(config)) {
return false;
}
}
if (sandbox_type_ == sandbox::mojom::Sandbox::kPrintCompositor &&
base::FeatureList::IsEnabled(
sandbox::policy::features::kPrintCompositorLPAC) &&
!app_container_disabled_) {
auto result = config->SetTokenLevel(sandbox::USER_UNPROTECTED,
sandbox::USER_UNPROTECTED);
if (result != sandbox::SBOX_ALL_OK) {
return false;
}
}
return GetContentClient()->browser()->PreSpawnChild(
config, sandbox_type_,
ContentBrowserClient::ChildSpawnFlags::kChildSpawnFlagNone);
}
bool UtilitySandboxedProcessLauncherDelegate::ShouldUnsandboxedRunInJob() {
auto utility_sub_type =
cmd_line_.GetSwitchValueASCII(switches::kUtilitySubType);
if (utility_sub_type == network::mojom::NetworkService::Name_) {
return true;
}
return false;
}
bool UtilitySandboxedProcessLauncherDelegate::CetCompatible() {
if (sandbox_type_ == sandbox::mojom::Sandbox::kServiceWithJit) {
return false;
}
auto utility_sub_type =
cmd_line_.GetSwitchValueASCII(switches::kUtilitySubType);
return GetContentClient()->browser()->IsUtilityCetCompatible(
utility_sub_type);
}
bool UtilitySandboxedProcessLauncherDelegate::PreSpawnTarget(
sandbox::TargetPolicy* policy) {
AddDelegateData(policy);
return SandboxedProcessLauncherDelegate::PreSpawnTarget(policy);
}
void UtilitySandboxedProcessLauncherDelegate::SetBootstrapStatusEvent(
const base::WaitableEvent& event) {
CHECK(!event_handle_to_inherit_)
<< "SetBootstrapStatusEvent should only be called once.";
HANDLE dup_handle;
CHECK(::DuplicateHandle(
::GetCurrentProcess(), event.handle(), ::GetCurrentProcess(), &dup_handle,
EVENT_MODIFY_STATE, TRUE, 0));
event_handle_to_inherit_.emplace(base::win::ScopedHandle(dup_handle));
}
void UtilitySandboxedProcessLauncherDelegate::AddDelegateData(
sandbox::TargetPolicy* policy) {
auto sandbox_config = content::mojom::sandbox::UtilityConfig::New();
for (const auto& library_path : preload_libraries_) {
sandbox_config->preload_libraries.push_back(library_path);
}
if (event_handle_to_inherit_) {
sandbox_config->bootstrap_event_handle =
base::win::HandleToUint32(event_handle_to_inherit_->Get());
policy->AddHandleToShare(event_handle_to_inherit_->Get());
}
std::vector<uint8_t> blob =
content::mojom::sandbox::UtilityConfig::Serialize(&sandbox_config);
policy->AddDelegateData(blob);
}
}