mirror of
https://chromium.googlesource.com/crosvm/crosvm
synced 2025-02-10 12:09:31 +00:00
Run tools/fmt --nightly BUG=None TEST=None Change-Id: Iaccfc5fe141c512f4b508c699f89686a4552bf96 Reviewed-on: https://chromium-review.googlesource.com/c/crosvm/crosvm/+/3988327 Reviewed-by: Daniel Verkamp <dverkamp@chromium.org> Commit-Queue: Dennis Kempin <denniskempin@google.com>
823 lines
32 KiB
Rust
823 lines
32 KiB
Rust
// Copyright 2022 The ChromiumOS Authors
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#![cfg(windows)]
|
|
|
|
use std::ffi::CStr;
|
|
use std::fmt;
|
|
use std::marker::PhantomData;
|
|
use std::sync::Once;
|
|
|
|
use base::named_pipes;
|
|
use base::AsRawDescriptor;
|
|
use base::FromRawDescriptor;
|
|
use base::SafeDescriptor;
|
|
use win_util::win32_wide_string;
|
|
|
|
#[cfg_attr(windows, path = "../bindings.rs")]
|
|
#[allow(non_camel_case_types)]
|
|
#[allow(non_snake_case)]
|
|
#[allow(non_upper_case_globals)]
|
|
#[allow(deref_nullptr)]
|
|
pub mod bindings;
|
|
|
|
pub mod policy;
|
|
|
|
pub use bindings::IntegrityLevel;
|
|
pub use bindings::JobLevel;
|
|
pub use bindings::MitigationFlags;
|
|
pub use bindings::ResultCode;
|
|
pub use bindings::Semantics;
|
|
pub use bindings::SubSystem;
|
|
pub use bindings::TokenLevel;
|
|
use bindings::DWORD;
|
|
pub use bindings::JOB_OBJECT_UILIMIT_ALL;
|
|
pub use bindings::JOB_OBJECT_UILIMIT_DESKTOP;
|
|
pub use bindings::JOB_OBJECT_UILIMIT_DISPLAYSETTINGS;
|
|
pub use bindings::JOB_OBJECT_UILIMIT_EXITWINDOWS;
|
|
pub use bindings::JOB_OBJECT_UILIMIT_GLOBALATOMS;
|
|
pub use bindings::JOB_OBJECT_UILIMIT_HANDLES;
|
|
pub use bindings::JOB_OBJECT_UILIMIT_NONE;
|
|
pub use bindings::JOB_OBJECT_UILIMIT_READCLIPBOARD;
|
|
pub use bindings::JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS;
|
|
pub use bindings::JOB_OBJECT_UILIMIT_WRITECLIPBOARD;
|
|
pub use bindings::MITIGATION_BOTTOM_UP_ASLR;
|
|
pub use bindings::MITIGATION_CET_DISABLED;
|
|
pub use bindings::MITIGATION_DEP;
|
|
pub use bindings::MITIGATION_DEP_NO_ATL_THUNK;
|
|
pub use bindings::MITIGATION_DLL_SEARCH_ORDER;
|
|
pub use bindings::MITIGATION_DYNAMIC_CODE_DISABLE;
|
|
pub use bindings::MITIGATION_DYNAMIC_CODE_DISABLE_WITH_OPT_OUT;
|
|
pub use bindings::MITIGATION_DYNAMIC_CODE_OPT_OUT_THIS_THREAD;
|
|
pub use bindings::MITIGATION_EXTENSION_POINT_DISABLE;
|
|
pub use bindings::MITIGATION_FORCE_MS_SIGNED_BINS;
|
|
pub use bindings::MITIGATION_HARDEN_TOKEN_IL_POLICY;
|
|
pub use bindings::MITIGATION_HEAP_TERMINATE;
|
|
pub use bindings::MITIGATION_HIGH_ENTROPY_ASLR;
|
|
pub use bindings::MITIGATION_IMAGE_LOAD_NO_LOW_LABEL;
|
|
pub use bindings::MITIGATION_IMAGE_LOAD_NO_REMOTE;
|
|
pub use bindings::MITIGATION_IMAGE_LOAD_PREFER_SYS32;
|
|
pub use bindings::MITIGATION_KTM_COMPONENT;
|
|
pub use bindings::MITIGATION_NONSYSTEM_FONT_DISABLE;
|
|
pub use bindings::MITIGATION_RELOCATE_IMAGE;
|
|
pub use bindings::MITIGATION_RELOCATE_IMAGE_REQUIRED;
|
|
pub use bindings::MITIGATION_RESTRICT_INDIRECT_BRANCH_PREDICTION;
|
|
pub use bindings::MITIGATION_SEHOP;
|
|
pub use bindings::MITIGATION_STRICT_HANDLE_CHECKS;
|
|
pub use bindings::MITIGATION_WIN32K_DISABLE;
|
|
use bindings::PROCESS_INFORMATION;
|
|
|
|
type Result<T> = std::result::Result<T, SandboxError>;
|
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
pub struct SandboxError {
|
|
result_code: ResultCode,
|
|
error_code: Option<u32>,
|
|
}
|
|
|
|
impl fmt::Display for SandboxError {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
match self.error_code {
|
|
Some(error_code) => write!(
|
|
f,
|
|
"Sandbox error code: {:?}, win32 error code: {}",
|
|
self.result_code, error_code,
|
|
),
|
|
None => write!(f, "Sandbox error code: {:?}", self.result_code),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl std::error::Error for SandboxError {
|
|
fn description(&self) -> &str {
|
|
"sandbox error"
|
|
}
|
|
}
|
|
|
|
impl SandboxError {
|
|
pub fn new(result_code: ResultCode) -> SandboxError {
|
|
if result_code == bindings::ResultCode::SBOX_ERROR_GENERIC {
|
|
return SandboxError::from(std::io::Error::last_os_error());
|
|
}
|
|
SandboxError {
|
|
result_code,
|
|
error_code: None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<std::io::Error> for SandboxError {
|
|
fn from(error: std::io::Error) -> Self {
|
|
let error_code = error.raw_os_error().map(|e| e as u32);
|
|
SandboxError {
|
|
result_code: bindings::ResultCode::SBOX_ERROR_GENERIC,
|
|
error_code,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub type SandboxWarning = SandboxError;
|
|
|
|
/// Encapsulates broker-related functionality for the sandbox.
|
|
///
|
|
/// This struct and its methods are not thread safe, in general. Only a single
|
|
/// thread should call the methods on this struct.
|
|
#[derive(Debug, PartialEq, Clone)]
|
|
pub struct BrokerServices {
|
|
broker: *mut bindings::BrokerServices,
|
|
}
|
|
|
|
/// Encapsulates target-related functionality for the sandbox.
|
|
#[derive(Debug, PartialEq, Clone)]
|
|
pub struct TargetServices {
|
|
target: *mut bindings::TargetServices,
|
|
}
|
|
|
|
/// Defines sandbox policies for target processes.
|
|
pub struct TargetPolicy<'a> {
|
|
policy: TargetPolicyWrapper,
|
|
_marker: PhantomData<&'a dyn AsRawDescriptor>,
|
|
}
|
|
|
|
struct TargetPolicyWrapper(*mut bindings::TargetPolicy);
|
|
|
|
impl Drop for TargetPolicyWrapper {
|
|
fn drop(&mut self) {
|
|
// Safe because TargetPolicyWrapper can only be constructed with a non-null pointer.
|
|
unsafe { bindings::sbox_release_policy(self.0) };
|
|
}
|
|
}
|
|
|
|
pub struct ProcessInformation {
|
|
pub process: SafeDescriptor,
|
|
pub thread: SafeDescriptor,
|
|
pub process_id: u32,
|
|
pub thread_id: u32,
|
|
}
|
|
|
|
pub struct PolicyInfo {
|
|
policy_info: *mut bindings::PolicyInfo,
|
|
}
|
|
|
|
impl Drop for PolicyInfo {
|
|
fn drop(&mut self) {
|
|
unsafe { bindings::sbox_release_policy_info(self.policy_info) }
|
|
}
|
|
}
|
|
|
|
/// Returns true if this process is the broker process.
|
|
pub fn is_sandbox_broker() -> bool {
|
|
// Safe because the returned raw pointer is a non-owning pointer and we are free
|
|
// to let it drop unmanaged.
|
|
unsafe { !bindings::get_broker_services().is_null() }
|
|
}
|
|
|
|
/// Returns true if this process is a target process.
|
|
pub fn is_sandbox_target() -> bool {
|
|
// Safe because the returned raw pointer is a non-owning pointer and we are
|
|
// free to let it drop unmanaged.
|
|
unsafe { !bindings::get_target_services().is_null() }
|
|
}
|
|
|
|
impl BrokerServices {
|
|
/// Returns an initialized broker API interface if the process is the broker.
|
|
pub fn get() -> Result<Option<BrokerServices>> {
|
|
static INIT: Once = Once::new();
|
|
static mut RESULT: Result<()> = Ok(());
|
|
static mut BROKER: Option<BrokerServices> = None;
|
|
|
|
// Initialize broker services. Should be called once before use.
|
|
// Safe because RESULT is only written once, and call_once will cause
|
|
// other readers to block until execution of the block is complete.
|
|
// Also checks for and eliminates any null pointers.
|
|
unsafe {
|
|
INIT.call_once(|| {
|
|
let broker = bindings::get_broker_services();
|
|
if broker.is_null() {
|
|
return;
|
|
}
|
|
BROKER = Some(BrokerServices { broker });
|
|
RESULT = BROKER.as_mut().unwrap().init();
|
|
});
|
|
if BROKER.is_none() {
|
|
return Ok(None);
|
|
}
|
|
match RESULT {
|
|
Err(e) => Err(e),
|
|
Ok(_) => Ok(Some(BROKER.as_mut().unwrap().clone())),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Initializes the broker. Must be called once before calling any other
|
|
/// methods.
|
|
///
|
|
/// Takes a &mut self because sbox_broker_init mutates the underlying broker
|
|
/// object.
|
|
fn init(&mut self) -> Result<()> {
|
|
// Safe because BrokerServices can only be constructed with a non-null
|
|
// pointer.
|
|
let result_code = unsafe { bindings::sbox_broker_init(self.broker) };
|
|
if result_code != ResultCode::SBOX_ALL_OK {
|
|
Err(SandboxError::new(result_code))
|
|
} else {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// Create a new policy object.
|
|
pub fn create_policy<'a>(&self) -> TargetPolicy<'a> {
|
|
// Safe because BrokerServices can only be constructed with a non-null pointer.
|
|
let policy = unsafe { bindings::sbox_create_policy(self.broker) };
|
|
TargetPolicy {
|
|
policy: TargetPolicyWrapper(policy),
|
|
_marker: PhantomData,
|
|
}
|
|
}
|
|
|
|
/// Spawn a new target process. This process is created with the main thread
|
|
/// in a suspended state.
|
|
///
|
|
/// Takes a `&mut self` because `sbox_spawn_target()` mutates the underlying
|
|
/// broker object.
|
|
pub fn spawn_target(
|
|
&mut self,
|
|
exe_path: &str,
|
|
command_line: &str,
|
|
policy: &TargetPolicy,
|
|
) -> Result<(ProcessInformation, Option<SandboxWarning>)> {
|
|
let mut last_warning = ResultCode::SBOX_ALL_OK;
|
|
let mut last_error: DWORD = 0;
|
|
let mut target = PROCESS_INFORMATION {
|
|
dwProcessId: 0,
|
|
dwThreadId: 0,
|
|
hThread: std::ptr::null_mut(),
|
|
hProcess: std::ptr::null_mut(),
|
|
};
|
|
// Safe because the external arguments must be constructed in a safe
|
|
// way, and the rest of the arguments are pointers to valid objects
|
|
// created in this function.
|
|
let result = unsafe {
|
|
bindings::sbox_spawn_target(
|
|
self.broker,
|
|
win32_wide_string(exe_path).as_ptr(),
|
|
win32_wide_string(command_line).as_ptr(),
|
|
policy.policy.0,
|
|
&mut last_warning,
|
|
&mut last_error,
|
|
&mut target,
|
|
)
|
|
};
|
|
if result != ResultCode::SBOX_ALL_OK {
|
|
return Err(SandboxError {
|
|
result_code: result,
|
|
error_code: Some(last_error),
|
|
});
|
|
}
|
|
// Safe because we are adopting the process and thread handles here,
|
|
// and they won't be used outside of the SafeDescriptor after this
|
|
// function returns.
|
|
let process = unsafe {
|
|
ProcessInformation {
|
|
process: SafeDescriptor::from_raw_descriptor(target.hProcess),
|
|
thread: SafeDescriptor::from_raw_descriptor(target.hThread),
|
|
process_id: target.dwProcessId,
|
|
thread_id: target.dwThreadId,
|
|
}
|
|
};
|
|
if last_warning != ResultCode::SBOX_ALL_OK {
|
|
Ok((
|
|
process,
|
|
Some(SandboxWarning {
|
|
result_code: last_warning,
|
|
error_code: Some(last_error),
|
|
}),
|
|
))
|
|
} else {
|
|
Ok((process, None))
|
|
}
|
|
}
|
|
|
|
/// Waits (blocks) for all target processes to exit.
|
|
///
|
|
/// Takes a `&mut self` because `sbox_wait_for_all_targets()` mutates the
|
|
/// underlying broker object.
|
|
pub fn wait_for_all_targets(&mut self) -> Result<()> {
|
|
// Safe because BrokerServices can only be constructed with a non-null pointer.
|
|
let result_code = unsafe { bindings::sbox_wait_for_all_targets(self.broker) };
|
|
if result_code != ResultCode::SBOX_ALL_OK {
|
|
Err(SandboxError::new(result_code))
|
|
} else {
|
|
Ok(())
|
|
}
|
|
}
|
|
}
|
|
|
|
impl TargetServices {
|
|
/// Returns an initialized target API interface if the process is the target.
|
|
pub fn get() -> Result<Option<TargetServices>> {
|
|
static INIT: Once = Once::new();
|
|
static mut RESULT: Result<()> = Ok(());
|
|
static mut TARGET: Option<TargetServices> = None;
|
|
|
|
// Initialize target services. Should be called once before use.
|
|
// Safe because RESULT is only written once, and call_once will cause
|
|
// other readers to block until execution of the block is complete.
|
|
// Also checks for and eliminates any null pointers.
|
|
unsafe {
|
|
INIT.call_once(|| {
|
|
let target = bindings::get_target_services();
|
|
if target.is_null() {
|
|
return;
|
|
}
|
|
TARGET = Some(TargetServices { target });
|
|
RESULT = TARGET.as_mut().unwrap().init()
|
|
});
|
|
if TARGET.is_none() {
|
|
return Ok(None);
|
|
}
|
|
// Initialize target services. If TargetServices is already initialized,
|
|
// this is a no-op.
|
|
match RESULT {
|
|
Err(e) => Err(e),
|
|
Ok(_) => Ok(Some(TARGET.as_mut().unwrap().clone())),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Initializes the target. Must be called once before calling any other
|
|
/// methods.
|
|
///
|
|
/// Takes a `&mut self` because `sbox_target_init()` mutates the underlying
|
|
/// target object.
|
|
fn init(&mut self) -> Result<()> {
|
|
// Safe because TargetServices can only be constructed with a non-null pointer.
|
|
let result_code = unsafe { bindings::sbox_target_init(self.target) };
|
|
if result_code != ResultCode::SBOX_ALL_OK {
|
|
Err(SandboxError::new(result_code))
|
|
} else {
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
/// Discards the targets impersonation token and uses the lower token.
|
|
///
|
|
/// Takes a `&mut self` because `sbox_lower_token()` mutates the underlying
|
|
/// target object.
|
|
pub fn lower_token(&mut self) {
|
|
// Safe because TargetServices can only be constructed with a non-null pointer.
|
|
unsafe { bindings::sbox_lower_token(self.target) };
|
|
}
|
|
}
|
|
|
|
impl<'a> TargetPolicy<'a> {
|
|
/// Sets the security level for the process' two tokens.
|
|
///
|
|
/// Takes a `&mut self` because `sbox_set_token_level()` mutates the
|
|
/// underlying policy object.
|
|
pub fn set_token_level(&mut self, initial: TokenLevel, lockdown: TokenLevel) -> Result<()> {
|
|
// Safe because TargetPolicy can only be constructed with a non-null policy pointer.
|
|
match unsafe { bindings::sbox_set_token_level(self.policy.0, initial, lockdown) } {
|
|
ResultCode::SBOX_ALL_OK => Ok(()),
|
|
result_code => Err(SandboxError::new(result_code)),
|
|
}
|
|
}
|
|
|
|
/// Gets the initial token level.
|
|
pub fn initial_token_level(&self) -> TokenLevel {
|
|
// Safe because TargetPolicy can only be constructed with a non-null policy pointer.
|
|
unsafe { bindings::sbox_get_initial_token_level(self.policy.0) }
|
|
}
|
|
|
|
/// Gets the lockdown token level.
|
|
pub fn lockdown_token_level(&self) -> TokenLevel {
|
|
// Safe because TargetPolicy can only be constructed with a non-null policy pointer.
|
|
unsafe { bindings::sbox_get_lockdown_token_level(self.policy.0) }
|
|
}
|
|
|
|
/// Sets the security level of the job object to which the process will
|
|
/// belong.
|
|
///
|
|
/// Takes a `&mut self` because `sbox_set_job_level()` mutates the
|
|
/// underlying policy object.
|
|
pub fn set_job_level(&mut self, job_level: JobLevel, ui_exceptions: u32) -> Result<()> {
|
|
// Safe because TargetPolicy can only be constructed with a non-null policy pointer.
|
|
match unsafe { bindings::sbox_set_job_level(self.policy.0, job_level, ui_exceptions) } {
|
|
ResultCode::SBOX_ALL_OK => Ok(()),
|
|
result_code => Err(SandboxError::new(result_code)),
|
|
}
|
|
}
|
|
|
|
/// Returns the job level.
|
|
pub fn job_level(&self) -> JobLevel {
|
|
// Safe because TargetPolicy can only be constructed with a non-null policy pointer.
|
|
unsafe { bindings::sbox_get_job_level(self.policy.0) }
|
|
}
|
|
|
|
/// Sets the initial integrity level of the process in the sandbox.
|
|
///
|
|
/// Takes a `&mut self` because `sbox_set_integrity_level()` mutates the
|
|
/// underlying policy object.
|
|
pub fn set_integrity_level(&mut self, level: IntegrityLevel) -> Result<()> {
|
|
// Safe because TargetPolicy can only be constructed with a non-null policy pointer.
|
|
match unsafe { bindings::sbox_set_integrity_level(self.policy.0, level) } {
|
|
ResultCode::SBOX_ALL_OK => Ok(()),
|
|
result_code => Err(SandboxError::new(result_code)),
|
|
}
|
|
}
|
|
|
|
/// Sets the delayed integrity level of the process in the sandbox.
|
|
///
|
|
/// Takes a `&mut self` because `sbox_set_delayed_integrity_level()` mutates the
|
|
/// underlying policy object.
|
|
pub fn set_delayed_integrity_level(&mut self, level: IntegrityLevel) -> Result<()> {
|
|
// Safe because TargetPolicy can only be constructed with a non-null policy pointer.
|
|
match unsafe { bindings::sbox_set_delayed_integrity_level(self.policy.0, level) } {
|
|
ResultCode::SBOX_ALL_OK => Ok(()),
|
|
result_code => Err(SandboxError::new(result_code)),
|
|
}
|
|
}
|
|
|
|
/// Returns the initial integrity level used.
|
|
pub fn integrity_level(&self) -> IntegrityLevel {
|
|
// Safe because TargetPolicy can only be constructed with a non-null policy pointer.
|
|
unsafe { bindings::sbox_get_integrity_level(self.policy.0) }
|
|
}
|
|
|
|
/// Specifies that the process should run on an alternate desktop. If
|
|
/// `alternate_winstation` is set to `true`, the desktop will be created on an
|
|
/// alternate windows station.
|
|
///
|
|
/// Takes a `&mut self` because `sbox_set_alternate_desktop` mutates the
|
|
/// underlying policy object.
|
|
pub fn set_alternate_desktop(&mut self, alternate_winstation: bool) -> Result<()> {
|
|
// Safe because TargetPolicy can only be constructed with a non-null policy pointer.
|
|
match unsafe { bindings::sbox_set_alternate_desktop(self.policy.0, alternate_winstation) } {
|
|
ResultCode::SBOX_ALL_OK => Ok(()),
|
|
result_code => Err(SandboxError::new(result_code)),
|
|
}
|
|
}
|
|
|
|
/// Precreates the alternate desktop and winstation, if any.
|
|
///
|
|
/// Takes a `&mut self` because `sbox_create_alternate_desktop` mutates the
|
|
/// underlying policy object.
|
|
pub fn create_alternate_desktop(&mut self, alternate_winstation: bool) -> Result<()> {
|
|
// Safe because TargetPolicy can only be constructed with a non-null policy pointer.
|
|
match unsafe {
|
|
bindings::sbox_create_alternate_desktop(self.policy.0, alternate_winstation)
|
|
} {
|
|
ResultCode::SBOX_ALL_OK => Ok(()),
|
|
result_code => Err(SandboxError::new(result_code)),
|
|
}
|
|
}
|
|
|
|
/// Destroys the desktop and windows station.
|
|
///
|
|
/// Takes a `&mut self` because `sbox_destroy_alternate_desktop` mutates the
|
|
/// underlying policy object.
|
|
pub fn destroy_alternate_desktop(&mut self) {
|
|
// Safe because TargetPolicy can only be constructed with a non-null policy pointer.
|
|
unsafe { bindings::sbox_destroy_alternate_desktop(self.policy.0) }
|
|
}
|
|
|
|
/// Sets the LowBox token for sandboxed process. This is mutually exclusive
|
|
/// with the `add_app_container_profile()` method.
|
|
///
|
|
/// Takes a `&mut self` because `sbox_set_lowbox` mutates the underlying
|
|
/// policy object.
|
|
pub fn set_lowbox(&mut self, sid: &str) -> Result<()> {
|
|
// Safe because TargetPolicy can only be constructed with a non-null policy pointer.
|
|
match unsafe { bindings::sbox_set_lowbox(self.policy.0, win32_wide_string(sid).as_ptr()) } {
|
|
ResultCode::SBOX_ALL_OK => Ok(()),
|
|
result_code => Err(SandboxError::new(result_code)),
|
|
}
|
|
}
|
|
|
|
/// Sets the mitigations enabled when the process is created.
|
|
///
|
|
/// Takes a `&mut self` because `sbox_set_process_mitigations` mutates the
|
|
/// underlying policy object.
|
|
pub fn set_process_mitigations(&mut self, flags: MitigationFlags) -> Result<()> {
|
|
// Safe because TargetPolicy can only be constructed with a non-null policy pointer.
|
|
match unsafe { bindings::sbox_set_process_mitigations(self.policy.0, flags) } {
|
|
ResultCode::SBOX_ALL_OK => Ok(()),
|
|
result_code => Err(SandboxError::new(result_code)),
|
|
}
|
|
}
|
|
|
|
/// Returns the currently set mitigation flags.
|
|
pub fn process_mitigations(&self) -> MitigationFlags {
|
|
// Safe because TargetPolicy can only be constructed with a non-null policy pointer.
|
|
unsafe { bindings::sbox_get_process_mitigations(self.policy.0) }
|
|
}
|
|
|
|
/// Sets process mitigation flags that don't take effect before the call to
|
|
/// lower_token().
|
|
///
|
|
/// Takes a `&mut self` because `sbox_set_delayed_process_mitigations`
|
|
/// mutates the underlying policy object.
|
|
pub fn set_delayed_process_mitigations(&mut self, flags: MitigationFlags) -> Result<()> {
|
|
// Safe because TargetPolicy can only be constructed with a non-null policy pointer.
|
|
match unsafe { bindings::sbox_set_delayed_process_mitigations(self.policy.0, flags) } {
|
|
ResultCode::SBOX_ALL_OK => Ok(()),
|
|
result_code => Err(SandboxError::new(result_code)),
|
|
}
|
|
}
|
|
|
|
/// Returns the currently set delayed_ mitigation flags.
|
|
pub fn delayed_process_mitigations(&self) -> MitigationFlags {
|
|
// Safe because TargetPolicy can only be constructed with a non-null policy pointer.
|
|
unsafe { bindings::sbox_get_delayed_process_mitigations(self.policy.0) }
|
|
}
|
|
|
|
/// Disconnect the target from CSRSS when TargetServices::lower_token() is
|
|
/// called inside the target.
|
|
///
|
|
/// Takes a `&mut self` because `sbox_set_disconnect_csrss` mutates the
|
|
/// underlying policy object.
|
|
pub fn set_disconnect_csrss(&mut self) -> Result<()> {
|
|
// Safe because TargetPolicy can only be constructed with a non-null policy pointer.
|
|
match unsafe { bindings::sbox_set_disconnect_csrss(self.policy.0) } {
|
|
ResultCode::SBOX_ALL_OK => Ok(()),
|
|
result_code => Err(SandboxError::new(result_code)),
|
|
}
|
|
}
|
|
|
|
/// Sets the interceptions to operate in strict mode.
|
|
///
|
|
/// Takes a `&mut self` because `sbox_set_delayed_process_mitigations`
|
|
/// mutates the underlying policy object.
|
|
pub fn set_strict_interceptions(&mut self) {
|
|
// Safe because TargetPolicy can only be constructed with a non-null policy pointer.
|
|
unsafe { bindings::sbox_set_strict_interceptions(self.policy.0) }
|
|
}
|
|
|
|
/// Sets a file as the handle that the process should inherit for stdout.
|
|
pub fn set_stdout_from_file(&mut self, file: &'a std::fs::File) -> Result<()> {
|
|
self.set_stdout_handle(file)
|
|
}
|
|
|
|
/// Sets a pipe as the handle that the process should inherit for stdout.
|
|
pub fn set_stdout_from_pipe(&mut self, pipe: &'a named_pipes::PipeConnection) -> Result<()> {
|
|
self.set_stdout_handle(pipe)
|
|
}
|
|
|
|
/// Sets the handle that the process should inherit for stdout.
|
|
///
|
|
/// Takes a `&mut self` because `sbox_set_stdout_handle()` mutates the underlying policy object.
|
|
fn set_stdout_handle(&mut self, handle: &'a dyn AsRawDescriptor) -> Result<()> {
|
|
// Safe because TargetPolicy can only be constructed with a non-null policy pointer.
|
|
match unsafe { bindings::sbox_set_stdout_handle(self.policy.0, handle.as_raw_descriptor()) }
|
|
{
|
|
ResultCode::SBOX_ALL_OK => {
|
|
win_util::set_handle_inheritance(
|
|
handle.as_raw_descriptor(),
|
|
/* inheritable= */ true,
|
|
)?;
|
|
Ok(())
|
|
}
|
|
result_code => Err(SandboxError::new(result_code)),
|
|
}
|
|
}
|
|
|
|
/// Sets a file as the handle that the process should inherit for stderr.
|
|
pub fn set_stderr_from_file(&mut self, file: &'a std::fs::File) -> Result<()> {
|
|
self.set_stderr_handle(file)
|
|
}
|
|
|
|
/// Sets a pipe as the handle that the process should inherit for stderr.
|
|
pub fn set_stderr_from_pipe(&mut self, pipe: &'a named_pipes::PipeConnection) -> Result<()> {
|
|
self.set_stderr_handle(pipe)
|
|
}
|
|
|
|
/// Sets the handle that the process should inherit for stderr.
|
|
///
|
|
/// Takes a `&mut self` because `sbox_set_stderr_handle` mutates the underlying policy object.
|
|
fn set_stderr_handle(&mut self, handle: &'a dyn AsRawDescriptor) -> Result<()> {
|
|
// Safe because TargetPolicy can only be constructed with a non-null policy pointer.
|
|
match unsafe { bindings::sbox_set_stderr_handle(self.policy.0, handle.as_raw_descriptor()) }
|
|
{
|
|
ResultCode::SBOX_ALL_OK => {
|
|
win_util::set_handle_inheritance(
|
|
handle.as_raw_descriptor(),
|
|
/* inheritable= */ true,
|
|
)?;
|
|
Ok(())
|
|
}
|
|
result_code => Err(SandboxError::new(result_code)),
|
|
}
|
|
}
|
|
|
|
/// Adds a policy rule effective for processes spawned using this policy.
|
|
///
|
|
/// # Arguments:
|
|
///
|
|
/// * subsystem: One of the enumerated Subsystems.
|
|
/// * semantics: One of the enumerated Semantics.
|
|
/// * pattern: A specific full path or a full path with wildcard patterns.
|
|
///
|
|
/// The valid wildcards are:
|
|
/// * `*`: Matches zero or more character. Only one in series allowed.
|
|
/// * `?`: Matches a single character. One or more in series are allowed.
|
|
///
|
|
/// Examples:
|
|
/// * `"c:\\documents and settings\\vince\\*.dmp"`
|
|
/// * `"c:\\documents and settings\\*\\crashdumps\\*.dmp"`
|
|
/// * `"c:\\temp\\app_log_?????_chrome.txt"`
|
|
///
|
|
/// Takes a `&mut self` because `sbox_add_rule` mutates the underlying
|
|
/// policy object.
|
|
pub fn add_rule<T: AsRef<str>>(
|
|
&mut self,
|
|
subsystem: SubSystem,
|
|
semantics: Semantics,
|
|
pattern: T,
|
|
) -> Result<()> {
|
|
// Safe because TargetPolicy can only be constructed with a non-null policy pointer.
|
|
// The function does not modify the pattern pointer, so that usage is safe.
|
|
match unsafe {
|
|
bindings::sbox_add_rule(
|
|
self.policy.0,
|
|
subsystem,
|
|
semantics,
|
|
win32_wide_string(pattern.as_ref()).as_ptr(),
|
|
)
|
|
} {
|
|
ResultCode::SBOX_ALL_OK => Ok(()),
|
|
result_code => Err(SandboxError::new(result_code)),
|
|
}
|
|
}
|
|
|
|
/// Adds a dll that will be unloaded in the target process before it gets
|
|
/// a chance to initialize itself.
|
|
///
|
|
/// Takes a `&mut self` because `sbox_add_dll_to_unload` mutates the
|
|
/// underlying policy object.
|
|
pub fn add_dll_to_unload(&mut self, dll_name: &str) -> Result<()> {
|
|
// Safe because TargetPolicy can only be constructed with a non-null policy pointer.
|
|
// The function does not modify the dll_name pointer, so that usage is safe.
|
|
match unsafe {
|
|
bindings::sbox_add_dll_to_unload(self.policy.0, win32_wide_string(dll_name).as_ptr())
|
|
} {
|
|
ResultCode::SBOX_ALL_OK => Ok(()),
|
|
result_code => Err(SandboxError::new(result_code)),
|
|
}
|
|
}
|
|
|
|
/// Adds a handle that will be closed in the target process after lockdown.
|
|
/// Specifying `None` for `handle_name` indicates all handles of the specified
|
|
/// type. An empty string for `handle_name` indicates the handle is unnamed.
|
|
///
|
|
/// Takes a `&mut self` because `sbox_add_kernel_object_to_close` mutates the
|
|
/// underlying policy object.
|
|
pub fn add_kernel_object_to_close(
|
|
&mut self,
|
|
handle_type: &str,
|
|
handle_name: Option<&str>,
|
|
) -> Result<()> {
|
|
let handle_name_wide = handle_name.map(win32_wide_string);
|
|
let handle_name_ptr = handle_name_wide
|
|
.as_ref()
|
|
.map_or(std::ptr::null(), Vec::<u16>::as_ptr);
|
|
// Safe because TargetPolicy can only be constructed with a non-null policy pointer.
|
|
// The function does not modify either of the string pointers, so that usage is safe.
|
|
// The function safely handles null pointers for the handle name.
|
|
match unsafe {
|
|
bindings::sbox_add_kernel_object_to_close(
|
|
self.policy.0,
|
|
win32_wide_string(handle_type).as_ptr(),
|
|
handle_name_ptr,
|
|
)
|
|
} {
|
|
ResultCode::SBOX_ALL_OK => Ok(()),
|
|
result_code => Err(SandboxError::new(result_code)),
|
|
}
|
|
}
|
|
|
|
/// Adds a handle that will be shared with the target process.
|
|
///
|
|
/// Takes a `&mut self` because `sbox_add_handle_to_share()` mutates the underlying policy object.
|
|
pub fn add_handle_to_share(&mut self, handle: &'a dyn AsRawDescriptor) {
|
|
// Safe because TargetPolicy can only be constructed with a non-null policy pointer.
|
|
unsafe {
|
|
bindings::sbox_add_handle_to_share(self.policy.0, handle.as_raw_descriptor());
|
|
}
|
|
}
|
|
|
|
/// Locks down the default DACL of the created lockdown and initial tokens
|
|
/// to restrict what other processes are allowed to access a process' kernel
|
|
/// resources.
|
|
///
|
|
/// Takes a `&mut self` because `sbox_set_lockdown_default_dacl()` mutates
|
|
/// the underlying policy object.
|
|
pub fn set_lockdown_default_dacl(&mut self) {
|
|
// Safe because TargetPolicy can only be constructed with a non-null policy pointer.
|
|
unsafe {
|
|
bindings::sbox_set_lockdown_default_dacl(self.policy.0);
|
|
}
|
|
}
|
|
|
|
/// Adds a restricting random SID to the restricted SIDs list as well as
|
|
/// the default DACL.
|
|
///
|
|
/// Takes a `&mut self` because `sbox_add_restricting_random_sid()` mutates
|
|
/// the underlying policy object.
|
|
pub fn add_restricting_random_sid(&mut self) {
|
|
// Safe because TargetPolicy can only be constructed with a non-null policy pointer.
|
|
unsafe {
|
|
bindings::sbox_add_restricting_random_sid(self.policy.0);
|
|
}
|
|
}
|
|
|
|
/// Configure policy to use an AppContainer profile.
|
|
///
|
|
/// # Arguments:
|
|
/// * `package_name`: the name of the profile to use.
|
|
/// * `create_profile`: Specifying `true` for `create_profile` ensures
|
|
/// the profile exists, if set to `false` process creation will fail if the
|
|
/// profile has not already been created.
|
|
///
|
|
/// Takes a `&mut self` because `sbox_add_dll_to_unload` mutates the
|
|
/// underlying policy object.
|
|
pub fn add_app_container_profile(
|
|
&mut self,
|
|
package_name: &str,
|
|
create_profile: bool,
|
|
) -> Result<()> {
|
|
// Safe because TargetPolicy can only be constructed with a non-null policy pointer.
|
|
// The function does not modify the package_name pointer, so that usage is safe.
|
|
match unsafe {
|
|
bindings::sbox_add_app_container_profile(
|
|
self.policy.0,
|
|
win32_wide_string(package_name).as_ptr(),
|
|
create_profile,
|
|
)
|
|
} {
|
|
ResultCode::SBOX_ALL_OK => Ok(()),
|
|
result_code => Err(SandboxError::new(result_code)),
|
|
}
|
|
}
|
|
|
|
/// Returns a snapshot of the policy configuration.
|
|
pub fn policy_info(&self) -> PolicyInfo {
|
|
// Safe because TargetPolicy can only be constructed with a non-null
|
|
// policy pointer. The underlying PolicyInfo object contains a copy of
|
|
// the data from the TargetPolicy object, but does not hold any
|
|
// references to it, so the lifetimes are independent.
|
|
PolicyInfo {
|
|
policy_info: unsafe { bindings::sbox_get_policy_info(self.policy.0) },
|
|
}
|
|
}
|
|
}
|
|
|
|
impl PolicyInfo {
|
|
/// Returns a JSON representation of the policy snapshot.
|
|
/// This pointer has the same lifetime as the PolicyInfo object.
|
|
pub fn json(&self) -> &str {
|
|
// Safe because PolicyInfo can only be constructed with a non-null
|
|
// policy pointer. The string returned will be a valid pointer to a
|
|
// valid c string. We bind the lifetime of the string to the lifetime
|
|
// of the PolicyInfo object, as is guaranteed by the underlying
|
|
// library. This is a string representation of a snapshot of the
|
|
// policy, so it will not change.
|
|
let c_str =
|
|
unsafe { CStr::from_ptr(bindings::sbox_policy_info_json_string(self.policy_info)) };
|
|
c_str.to_str().unwrap()
|
|
}
|
|
}
|
|
|
|
// TODO(b/196996588): Develop more tests, especially policy-related, once we
|
|
// have a way to launch and test target processes.
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn not_the_target() {
|
|
let target = TargetServices::get().unwrap();
|
|
assert_eq!(target, None);
|
|
}
|
|
|
|
#[test]
|
|
fn is_the_broker() {
|
|
let broker = BrokerServices::get().unwrap();
|
|
assert_ne!(broker, None);
|
|
}
|
|
|
|
#[test]
|
|
fn policy_handles_live_long_enough() {
|
|
let broker = BrokerServices::get().unwrap().unwrap();
|
|
let mut policy = broker.create_policy();
|
|
let pipe = named_pipes::pair(
|
|
&named_pipes::FramingMode::Byte,
|
|
&named_pipes::BlockingMode::NoWait,
|
|
0,
|
|
)
|
|
.unwrap();
|
|
policy.set_stdout_handle(&pipe.0).unwrap();
|
|
policy.set_stderr_handle(&pipe.0).unwrap();
|
|
policy.add_handle_to_share(&pipe.0);
|
|
}
|
|
}
|