crosvm/sandbox/src/lib.rs
Dennis Kempin f8eb24052e Reformat all files with nightly
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>
2022-10-31 21:33:33 +00:00

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);
}
}