xref: /aosp_15_r20/external/crosvm/sandbox/src/lib.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2022 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #![cfg(windows)]
6 
7 use std::ffi::CStr;
8 use std::fmt;
9 use std::marker::PhantomData;
10 use std::sync::Once;
11 
12 use base::named_pipes;
13 use base::AsRawDescriptor;
14 use base::FromRawDescriptor;
15 use base::SafeDescriptor;
16 use win_util::win32_wide_string;
17 
18 #[cfg_attr(windows, path = "../bindings.rs")]
19 #[allow(non_camel_case_types)]
20 #[allow(non_snake_case)]
21 #[allow(non_upper_case_globals)]
22 #[allow(deref_nullptr)]
23 pub mod bindings;
24 
25 pub mod policy;
26 
27 pub use bindings::IntegrityLevel;
28 pub use bindings::JobLevel;
29 pub use bindings::MitigationFlags;
30 pub use bindings::ResultCode;
31 pub use bindings::Semantics;
32 pub use bindings::SubSystem;
33 pub use bindings::TokenLevel;
34 use bindings::DWORD;
35 pub use bindings::JOB_OBJECT_UILIMIT_ALL;
36 pub use bindings::JOB_OBJECT_UILIMIT_DESKTOP;
37 pub use bindings::JOB_OBJECT_UILIMIT_DISPLAYSETTINGS;
38 pub use bindings::JOB_OBJECT_UILIMIT_EXITWINDOWS;
39 pub use bindings::JOB_OBJECT_UILIMIT_GLOBALATOMS;
40 pub use bindings::JOB_OBJECT_UILIMIT_HANDLES;
41 pub use bindings::JOB_OBJECT_UILIMIT_NONE;
42 pub use bindings::JOB_OBJECT_UILIMIT_READCLIPBOARD;
43 pub use bindings::JOB_OBJECT_UILIMIT_SYSTEMPARAMETERS;
44 pub use bindings::JOB_OBJECT_UILIMIT_WRITECLIPBOARD;
45 pub use bindings::MITIGATION_BOTTOM_UP_ASLR;
46 pub use bindings::MITIGATION_CET_DISABLED;
47 pub use bindings::MITIGATION_DEP;
48 pub use bindings::MITIGATION_DEP_NO_ATL_THUNK;
49 pub use bindings::MITIGATION_DLL_SEARCH_ORDER;
50 pub use bindings::MITIGATION_DYNAMIC_CODE_DISABLE;
51 pub use bindings::MITIGATION_DYNAMIC_CODE_DISABLE_WITH_OPT_OUT;
52 pub use bindings::MITIGATION_DYNAMIC_CODE_OPT_OUT_THIS_THREAD;
53 pub use bindings::MITIGATION_EXTENSION_POINT_DISABLE;
54 pub use bindings::MITIGATION_FORCE_MS_SIGNED_BINS;
55 pub use bindings::MITIGATION_HARDEN_TOKEN_IL_POLICY;
56 pub use bindings::MITIGATION_HEAP_TERMINATE;
57 pub use bindings::MITIGATION_HIGH_ENTROPY_ASLR;
58 pub use bindings::MITIGATION_IMAGE_LOAD_NO_LOW_LABEL;
59 pub use bindings::MITIGATION_IMAGE_LOAD_NO_REMOTE;
60 pub use bindings::MITIGATION_IMAGE_LOAD_PREFER_SYS32;
61 pub use bindings::MITIGATION_KTM_COMPONENT;
62 pub use bindings::MITIGATION_NONSYSTEM_FONT_DISABLE;
63 pub use bindings::MITIGATION_RELOCATE_IMAGE;
64 pub use bindings::MITIGATION_RELOCATE_IMAGE_REQUIRED;
65 pub use bindings::MITIGATION_RESTRICT_INDIRECT_BRANCH_PREDICTION;
66 pub use bindings::MITIGATION_SEHOP;
67 pub use bindings::MITIGATION_STRICT_HANDLE_CHECKS;
68 pub use bindings::MITIGATION_WIN32K_DISABLE;
69 use bindings::PROCESS_INFORMATION;
70 
71 type Result<T> = std::result::Result<T, SandboxError>;
72 
73 #[derive(Debug, Copy, Clone)]
74 pub struct SandboxError {
75     result_code: ResultCode,
76     error_code: Option<u32>,
77 }
78 
79 impl fmt::Display for SandboxError {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result80     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
81         match self.error_code {
82             Some(error_code) => write!(
83                 f,
84                 "Sandbox error code: {:?}, win32 error code: {}",
85                 self.result_code, error_code,
86             ),
87             None => write!(f, "Sandbox error code: {:?}", self.result_code),
88         }
89     }
90 }
91 
92 impl std::error::Error for SandboxError {
description(&self) -> &str93     fn description(&self) -> &str {
94         "sandbox error"
95     }
96 }
97 
98 impl SandboxError {
new(result_code: ResultCode) -> SandboxError99     pub fn new(result_code: ResultCode) -> SandboxError {
100         if result_code == bindings::ResultCode::SBOX_ERROR_GENERIC {
101             return SandboxError::from(std::io::Error::last_os_error());
102         }
103         SandboxError {
104             result_code,
105             error_code: None,
106         }
107     }
108 }
109 
110 impl From<std::io::Error> for SandboxError {
from(error: std::io::Error) -> Self111     fn from(error: std::io::Error) -> Self {
112         let error_code = error.raw_os_error().map(|e| e as u32);
113         SandboxError {
114             result_code: bindings::ResultCode::SBOX_ERROR_GENERIC,
115             error_code,
116         }
117     }
118 }
119 
120 pub type SandboxWarning = SandboxError;
121 
122 /// Encapsulates broker-related functionality for the sandbox.
123 ///
124 /// This struct and its methods are not thread safe, in general. Only a single
125 /// thread should call the methods on this struct.
126 #[derive(Debug, PartialEq, Clone)]
127 pub struct BrokerServices {
128     broker: *mut bindings::BrokerServices,
129 }
130 
131 /// Encapsulates target-related functionality for the sandbox.
132 #[derive(Debug, PartialEq, Clone)]
133 pub struct TargetServices {
134     target: *mut bindings::TargetServices,
135 }
136 
137 /// Defines sandbox policies for target processes.
138 pub struct TargetPolicy<'a> {
139     policy: TargetPolicyWrapper,
140     _marker: PhantomData<&'a dyn AsRawDescriptor>,
141 }
142 
143 struct TargetPolicyWrapper(*mut bindings::TargetPolicy);
144 
145 impl Drop for TargetPolicyWrapper {
drop(&mut self)146     fn drop(&mut self) {
147         // Safe because TargetPolicyWrapper can only be constructed with a non-null pointer.
148         unsafe { bindings::sbox_release_policy(self.0) };
149     }
150 }
151 
152 pub struct ProcessInformation {
153     pub process: SafeDescriptor,
154     pub thread: SafeDescriptor,
155     pub process_id: u32,
156     pub thread_id: u32,
157 }
158 
159 pub struct PolicyInfo {
160     policy_info: *mut bindings::PolicyInfo,
161 }
162 
163 impl Drop for PolicyInfo {
drop(&mut self)164     fn drop(&mut self) {
165         unsafe { bindings::sbox_release_policy_info(self.policy_info) }
166     }
167 }
168 
169 /// Returns true if this process is the broker process.
is_sandbox_broker() -> bool170 pub fn is_sandbox_broker() -> bool {
171     // Safe because the returned raw pointer is a non-owning pointer and we are free
172     // to let it drop unmanaged.
173     unsafe { !bindings::get_broker_services().is_null() }
174 }
175 
176 /// Returns true if this process is a target process.
is_sandbox_target() -> bool177 pub fn is_sandbox_target() -> bool {
178     // Safe because the returned raw pointer is a non-owning pointer and we are
179     // free to let it drop unmanaged.
180     unsafe { !bindings::get_target_services().is_null() }
181 }
182 
183 impl BrokerServices {
184     /// Returns an initialized broker API interface if the process is the broker.
get() -> Result<Option<BrokerServices>>185     pub fn get() -> Result<Option<BrokerServices>> {
186         static INIT: Once = Once::new();
187         static mut RESULT: Result<()> = Ok(());
188         static mut BROKER: Option<BrokerServices> = None;
189 
190         // Initialize broker services. Should be called once before use.
191         // Safe because RESULT is only written once, and call_once will cause
192         // other readers to block until execution of the block is complete.
193         // Also checks for and eliminates any null pointers.
194         unsafe {
195             INIT.call_once(|| {
196                 let broker = bindings::get_broker_services();
197                 if broker.is_null() {
198                     return;
199                 }
200                 BROKER = Some(BrokerServices { broker });
201                 RESULT = BROKER.as_mut().unwrap().init();
202             });
203             if BROKER.is_none() {
204                 return Ok(None);
205             }
206             match RESULT {
207                 Err(e) => Err(e),
208                 Ok(_) => Ok(Some(BROKER.as_mut().unwrap().clone())),
209             }
210         }
211     }
212 
213     /// Initializes the broker. Must be called once before calling any other
214     /// methods.
215     ///
216     /// Takes a &mut self because sbox_broker_init mutates the underlying broker
217     /// object.
init(&mut self) -> Result<()>218     fn init(&mut self) -> Result<()> {
219         // Safe because BrokerServices can only be constructed with a non-null
220         // pointer.
221         let result_code = unsafe { bindings::sbox_broker_init(self.broker) };
222         if result_code != ResultCode::SBOX_ALL_OK {
223             Err(SandboxError::new(result_code))
224         } else {
225             Ok(())
226         }
227     }
228 
229     /// Create a new policy object.
create_policy<'a>(&self) -> TargetPolicy<'a>230     pub fn create_policy<'a>(&self) -> TargetPolicy<'a> {
231         // Safe because BrokerServices can only be constructed with a non-null pointer.
232         let policy = unsafe { bindings::sbox_create_policy(self.broker) };
233         TargetPolicy {
234             policy: TargetPolicyWrapper(policy),
235             _marker: PhantomData,
236         }
237     }
238 
239     /// Spawn a new target process. This process is created with the main thread
240     /// in a suspended state.
241     ///
242     /// Takes a `&mut self` because `sbox_spawn_target()` mutates the underlying
243     /// broker object.
spawn_target( &mut self, exe_path: &str, command_line: &str, policy: &TargetPolicy, ) -> Result<(ProcessInformation, Option<SandboxWarning>)>244     pub fn spawn_target(
245         &mut self,
246         exe_path: &str,
247         command_line: &str,
248         policy: &TargetPolicy,
249     ) -> Result<(ProcessInformation, Option<SandboxWarning>)> {
250         let mut last_warning = ResultCode::SBOX_ALL_OK;
251         let mut last_error: DWORD = 0;
252         let mut target = PROCESS_INFORMATION {
253             dwProcessId: 0,
254             dwThreadId: 0,
255             hThread: std::ptr::null_mut(),
256             hProcess: std::ptr::null_mut(),
257         };
258         // Safe because the external arguments must be constructed in a safe
259         // way, and the rest of the arguments are pointers to valid objects
260         // created in this function.
261         let result = unsafe {
262             bindings::sbox_spawn_target(
263                 self.broker,
264                 win32_wide_string(exe_path).as_ptr(),
265                 win32_wide_string(command_line).as_ptr(),
266                 policy.policy.0,
267                 &mut last_warning,
268                 &mut last_error,
269                 &mut target,
270             )
271         };
272         if result != ResultCode::SBOX_ALL_OK {
273             return Err(SandboxError {
274                 result_code: result,
275                 error_code: Some(last_error),
276             });
277         }
278         // Safe because we are adopting the process and thread handles here,
279         // and they won't be used outside of the SafeDescriptor after this
280         // function returns.
281         let process = unsafe {
282             ProcessInformation {
283                 process: SafeDescriptor::from_raw_descriptor(target.hProcess),
284                 thread: SafeDescriptor::from_raw_descriptor(target.hThread),
285                 process_id: target.dwProcessId,
286                 thread_id: target.dwThreadId,
287             }
288         };
289         if last_warning != ResultCode::SBOX_ALL_OK {
290             Ok((
291                 process,
292                 Some(SandboxWarning {
293                     result_code: last_warning,
294                     error_code: Some(last_error),
295                 }),
296             ))
297         } else {
298             Ok((process, None))
299         }
300     }
301 
302     /// Waits (blocks) for all target processes to exit.
303     ///
304     /// Takes a `&mut self` because `sbox_wait_for_all_targets()` mutates the
305     /// underlying broker object.
wait_for_all_targets(&mut self) -> Result<()>306     pub fn wait_for_all_targets(&mut self) -> Result<()> {
307         // Safe because BrokerServices can only be constructed with a non-null pointer.
308         let result_code = unsafe { bindings::sbox_wait_for_all_targets(self.broker) };
309         if result_code != ResultCode::SBOX_ALL_OK {
310             Err(SandboxError::new(result_code))
311         } else {
312             Ok(())
313         }
314     }
315 }
316 
317 impl TargetServices {
318     /// Returns an initialized target API interface if the process is the target.
get() -> Result<Option<TargetServices>>319     pub fn get() -> Result<Option<TargetServices>> {
320         static INIT: Once = Once::new();
321         static mut RESULT: Result<()> = Ok(());
322         static mut TARGET: Option<TargetServices> = None;
323 
324         // Initialize target services. Should be called once before use.
325         // Safe because RESULT is only written once, and call_once will cause
326         // other readers to block until execution of the block is complete.
327         // Also checks for and eliminates any null pointers.
328         unsafe {
329             INIT.call_once(|| {
330                 let target = bindings::get_target_services();
331                 if target.is_null() {
332                     return;
333                 }
334                 TARGET = Some(TargetServices { target });
335                 RESULT = TARGET.as_mut().unwrap().init()
336             });
337             if TARGET.is_none() {
338                 return Ok(None);
339             }
340             // Initialize target services. If TargetServices is already initialized,
341             // this is a no-op.
342             match RESULT {
343                 Err(e) => Err(e),
344                 Ok(_) => Ok(Some(TARGET.as_mut().unwrap().clone())),
345             }
346         }
347     }
348 
349     /// Initializes the target. Must be called once before calling any other
350     /// methods.
351     ///
352     /// Takes a `&mut self` because `sbox_target_init()` mutates the underlying
353     /// target object.
init(&mut self) -> Result<()>354     fn init(&mut self) -> Result<()> {
355         // Safe because TargetServices can only be constructed with a non-null pointer.
356         let result_code = unsafe { bindings::sbox_target_init(self.target) };
357         if result_code != ResultCode::SBOX_ALL_OK {
358             Err(SandboxError::new(result_code))
359         } else {
360             Ok(())
361         }
362     }
363 
364     /// Discards the targets impersonation token and uses the lower token.
365     ///
366     /// Takes a `&mut self` because `sbox_lower_token()` mutates the underlying
367     /// target object.
lower_token(&mut self)368     pub fn lower_token(&mut self) {
369         // Safe because TargetServices can only be constructed with a non-null pointer.
370         unsafe { bindings::sbox_lower_token(self.target) };
371     }
372 }
373 
374 impl<'a> TargetPolicy<'a> {
375     /// Sets the security level for the process' two tokens.
376     ///
377     /// Takes a `&mut self` because `sbox_set_token_level()` mutates the
378     /// underlying policy object.
set_token_level(&mut self, initial: TokenLevel, lockdown: TokenLevel) -> Result<()>379     pub fn set_token_level(&mut self, initial: TokenLevel, lockdown: TokenLevel) -> Result<()> {
380         // Safe because TargetPolicy can only be constructed with a non-null policy pointer.
381         match unsafe { bindings::sbox_set_token_level(self.policy.0, initial, lockdown) } {
382             ResultCode::SBOX_ALL_OK => Ok(()),
383             result_code => Err(SandboxError::new(result_code)),
384         }
385     }
386 
387     /// Gets the initial token level.
initial_token_level(&self) -> TokenLevel388     pub fn initial_token_level(&self) -> TokenLevel {
389         // Safe because TargetPolicy can only be constructed with a non-null policy pointer.
390         unsafe { bindings::sbox_get_initial_token_level(self.policy.0) }
391     }
392 
393     /// Gets the lockdown token level.
lockdown_token_level(&self) -> TokenLevel394     pub fn lockdown_token_level(&self) -> TokenLevel {
395         // Safe because TargetPolicy can only be constructed with a non-null policy pointer.
396         unsafe { bindings::sbox_get_lockdown_token_level(self.policy.0) }
397     }
398 
399     /// Sets the security level of the job object to which the process will
400     /// belong.
401     ///
402     /// Takes a `&mut self` because `sbox_set_job_level()` mutates the
403     /// underlying policy object.
set_job_level(&mut self, job_level: JobLevel, ui_exceptions: u32) -> Result<()>404     pub fn set_job_level(&mut self, job_level: JobLevel, ui_exceptions: u32) -> Result<()> {
405         // Safe because TargetPolicy can only be constructed with a non-null policy pointer.
406         match unsafe { bindings::sbox_set_job_level(self.policy.0, job_level, ui_exceptions) } {
407             ResultCode::SBOX_ALL_OK => Ok(()),
408             result_code => Err(SandboxError::new(result_code)),
409         }
410     }
411 
412     /// Returns the job level.
job_level(&self) -> JobLevel413     pub fn job_level(&self) -> JobLevel {
414         // Safe because TargetPolicy can only be constructed with a non-null policy pointer.
415         unsafe { bindings::sbox_get_job_level(self.policy.0) }
416     }
417 
418     /// Sets the initial integrity level of the process in the sandbox.
419     ///
420     /// Takes a `&mut self` because `sbox_set_integrity_level()` mutates the
421     /// underlying policy object.
set_integrity_level(&mut self, level: IntegrityLevel) -> Result<()>422     pub fn set_integrity_level(&mut self, level: IntegrityLevel) -> Result<()> {
423         // Safe because TargetPolicy can only be constructed with a non-null policy pointer.
424         match unsafe { bindings::sbox_set_integrity_level(self.policy.0, level) } {
425             ResultCode::SBOX_ALL_OK => Ok(()),
426             result_code => Err(SandboxError::new(result_code)),
427         }
428     }
429 
430     /// Sets the delayed integrity level of the process in the sandbox.
431     ///
432     /// Takes a `&mut self` because `sbox_set_delayed_integrity_level()` mutates the
433     /// underlying policy object.
set_delayed_integrity_level(&mut self, level: IntegrityLevel) -> Result<()>434     pub fn set_delayed_integrity_level(&mut self, level: IntegrityLevel) -> Result<()> {
435         // Safe because TargetPolicy can only be constructed with a non-null policy pointer.
436         match unsafe { bindings::sbox_set_delayed_integrity_level(self.policy.0, level) } {
437             ResultCode::SBOX_ALL_OK => Ok(()),
438             result_code => Err(SandboxError::new(result_code)),
439         }
440     }
441 
442     /// Returns the initial integrity level used.
integrity_level(&self) -> IntegrityLevel443     pub fn integrity_level(&self) -> IntegrityLevel {
444         // Safe because TargetPolicy can only be constructed with a non-null policy pointer.
445         unsafe { bindings::sbox_get_integrity_level(self.policy.0) }
446     }
447 
448     /// Specifies that the process should run on an alternate desktop. If
449     /// `alternate_winstation` is set to `true`, the desktop will be created on an
450     /// alternate windows station.
451     ///
452     /// Takes a `&mut self` because `sbox_set_alternate_desktop` mutates the
453     /// underlying policy object.
set_alternate_desktop(&mut self, alternate_winstation: bool) -> Result<()>454     pub fn set_alternate_desktop(&mut self, alternate_winstation: bool) -> Result<()> {
455         // Safe because TargetPolicy can only be constructed with a non-null policy pointer.
456         match unsafe { bindings::sbox_set_alternate_desktop(self.policy.0, alternate_winstation) } {
457             ResultCode::SBOX_ALL_OK => Ok(()),
458             result_code => Err(SandboxError::new(result_code)),
459         }
460     }
461 
462     /// Precreates the alternate desktop and winstation, if any.
463     ///
464     /// Takes a `&mut self` because `sbox_create_alternate_desktop` mutates the
465     /// underlying policy object.
create_alternate_desktop(&mut self, alternate_winstation: bool) -> Result<()>466     pub fn create_alternate_desktop(&mut self, alternate_winstation: bool) -> Result<()> {
467         // Safe because TargetPolicy can only be constructed with a non-null policy pointer.
468         match unsafe {
469             bindings::sbox_create_alternate_desktop(self.policy.0, alternate_winstation)
470         } {
471             ResultCode::SBOX_ALL_OK => Ok(()),
472             result_code => Err(SandboxError::new(result_code)),
473         }
474     }
475 
476     /// Destroys the desktop and windows station.
477     ///
478     /// Takes a `&mut self` because `sbox_destroy_alternate_desktop` mutates the
479     /// underlying policy object.
destroy_alternate_desktop(&mut self)480     pub fn destroy_alternate_desktop(&mut self) {
481         // Safe because TargetPolicy can only be constructed with a non-null policy pointer.
482         unsafe { bindings::sbox_destroy_alternate_desktop(self.policy.0) }
483     }
484 
485     /// Sets the LowBox token for sandboxed process. This is mutually exclusive
486     /// with the `add_app_container_profile()` method.
487     ///
488     /// Takes a `&mut self` because `sbox_set_lowbox` mutates the underlying
489     /// policy object.
set_lowbox(&mut self, sid: &str) -> Result<()>490     pub fn set_lowbox(&mut self, sid: &str) -> Result<()> {
491         // Safe because TargetPolicy can only be constructed with a non-null policy pointer.
492         match unsafe { bindings::sbox_set_lowbox(self.policy.0, win32_wide_string(sid).as_ptr()) } {
493             ResultCode::SBOX_ALL_OK => Ok(()),
494             result_code => Err(SandboxError::new(result_code)),
495         }
496     }
497 
498     /// Sets the mitigations enabled when the process is created.
499     ///
500     /// Takes a `&mut self` because `sbox_set_process_mitigations` mutates the
501     /// underlying policy object.
set_process_mitigations(&mut self, flags: MitigationFlags) -> Result<()>502     pub fn set_process_mitigations(&mut self, flags: MitigationFlags) -> Result<()> {
503         // Safe because TargetPolicy can only be constructed with a non-null policy pointer.
504         match unsafe { bindings::sbox_set_process_mitigations(self.policy.0, flags) } {
505             ResultCode::SBOX_ALL_OK => Ok(()),
506             result_code => Err(SandboxError::new(result_code)),
507         }
508     }
509 
510     /// Returns the currently set mitigation flags.
process_mitigations(&self) -> MitigationFlags511     pub fn process_mitigations(&self) -> MitigationFlags {
512         // Safe because TargetPolicy can only be constructed with a non-null policy pointer.
513         unsafe { bindings::sbox_get_process_mitigations(self.policy.0) }
514     }
515 
516     /// Sets process mitigation flags that don't take effect before the call to
517     /// lower_token().
518     ///
519     /// Takes a `&mut self` because `sbox_set_delayed_process_mitigations`
520     /// mutates the underlying policy object.
set_delayed_process_mitigations(&mut self, flags: MitigationFlags) -> Result<()>521     pub fn set_delayed_process_mitigations(&mut self, flags: MitigationFlags) -> Result<()> {
522         // Safe because TargetPolicy can only be constructed with a non-null policy pointer.
523         match unsafe { bindings::sbox_set_delayed_process_mitigations(self.policy.0, flags) } {
524             ResultCode::SBOX_ALL_OK => Ok(()),
525             result_code => Err(SandboxError::new(result_code)),
526         }
527     }
528 
529     /// Returns the currently set delayed_ mitigation flags.
delayed_process_mitigations(&self) -> MitigationFlags530     pub fn delayed_process_mitigations(&self) -> MitigationFlags {
531         // Safe because TargetPolicy can only be constructed with a non-null policy pointer.
532         unsafe { bindings::sbox_get_delayed_process_mitigations(self.policy.0) }
533     }
534 
535     /// Disconnect the target from CSRSS when TargetServices::lower_token() is
536     /// called inside the target.
537     ///
538     /// Takes a `&mut self` because `sbox_set_disconnect_csrss` mutates the
539     /// underlying policy object.
set_disconnect_csrss(&mut self) -> Result<()>540     pub fn set_disconnect_csrss(&mut self) -> Result<()> {
541         // Safe because TargetPolicy can only be constructed with a non-null policy pointer.
542         match unsafe { bindings::sbox_set_disconnect_csrss(self.policy.0) } {
543             ResultCode::SBOX_ALL_OK => Ok(()),
544             result_code => Err(SandboxError::new(result_code)),
545         }
546     }
547 
548     /// Sets the interceptions to operate in strict mode.
549     ///
550     /// Takes a `&mut self` because `sbox_set_delayed_process_mitigations`
551     /// mutates the underlying policy object.
set_strict_interceptions(&mut self)552     pub fn set_strict_interceptions(&mut self) {
553         // Safe because TargetPolicy can only be constructed with a non-null policy pointer.
554         unsafe { bindings::sbox_set_strict_interceptions(self.policy.0) }
555     }
556 
557     /// Sets a file as the handle that the process should inherit for stdout.
set_stdout_from_file(&mut self, file: &'a std::fs::File) -> Result<()>558     pub fn set_stdout_from_file(&mut self, file: &'a std::fs::File) -> Result<()> {
559         self.set_stdout_handle(file)
560     }
561 
562     /// Sets a pipe as the handle that the process should inherit for stdout.
set_stdout_from_pipe(&mut self, pipe: &'a named_pipes::PipeConnection) -> Result<()>563     pub fn set_stdout_from_pipe(&mut self, pipe: &'a named_pipes::PipeConnection) -> Result<()> {
564         self.set_stdout_handle(pipe)
565     }
566 
567     /// Sets the handle that the process should inherit for stdout.
568     ///
569     /// Takes a `&mut self` because `sbox_set_stdout_handle()` mutates the underlying policy object.
set_stdout_handle(&mut self, handle: &'a dyn AsRawDescriptor) -> Result<()>570     fn set_stdout_handle(&mut self, handle: &'a dyn AsRawDescriptor) -> Result<()> {
571         // Safe because TargetPolicy can only be constructed with a non-null policy pointer.
572         match unsafe { bindings::sbox_set_stdout_handle(self.policy.0, handle.as_raw_descriptor()) }
573         {
574             ResultCode::SBOX_ALL_OK => {
575                 win_util::set_handle_inheritance(
576                     handle.as_raw_descriptor(),
577                     /* inheritable= */ true,
578                 )?;
579                 Ok(())
580             }
581             result_code => Err(SandboxError::new(result_code)),
582         }
583     }
584 
585     /// Sets a file as the handle that the process should inherit for stderr.
set_stderr_from_file(&mut self, file: &'a std::fs::File) -> Result<()>586     pub fn set_stderr_from_file(&mut self, file: &'a std::fs::File) -> Result<()> {
587         self.set_stderr_handle(file)
588     }
589 
590     /// Sets a pipe as the handle that the process should inherit for stderr.
set_stderr_from_pipe(&mut self, pipe: &'a named_pipes::PipeConnection) -> Result<()>591     pub fn set_stderr_from_pipe(&mut self, pipe: &'a named_pipes::PipeConnection) -> Result<()> {
592         self.set_stderr_handle(pipe)
593     }
594 
595     /// Sets the handle that the process should inherit for stderr.
596     ///
597     /// Takes a `&mut self` because `sbox_set_stderr_handle` mutates the underlying policy object.
set_stderr_handle(&mut self, handle: &'a dyn AsRawDescriptor) -> Result<()>598     fn set_stderr_handle(&mut self, handle: &'a dyn AsRawDescriptor) -> Result<()> {
599         // Safe because TargetPolicy can only be constructed with a non-null policy pointer.
600         match unsafe { bindings::sbox_set_stderr_handle(self.policy.0, handle.as_raw_descriptor()) }
601         {
602             ResultCode::SBOX_ALL_OK => {
603                 win_util::set_handle_inheritance(
604                     handle.as_raw_descriptor(),
605                     /* inheritable= */ true,
606                 )?;
607                 Ok(())
608             }
609             result_code => Err(SandboxError::new(result_code)),
610         }
611     }
612 
613     /// Adds a policy rule effective for processes spawned using this policy.
614     ///
615     /// # Arguments:
616     ///
617     /// * subsystem: One of the enumerated Subsystems.
618     /// * semantics: One of the enumerated Semantics.
619     /// * pattern: A specific full path or a full path with wildcard patterns.
620     ///
621     ///   The valid wildcards are:
622     ///   * `*`: Matches zero or more character. Only one in series allowed.
623     ///   * `?`: Matches a single character. One or more in series are allowed.
624     ///
625     ///   Examples:
626     ///   * `"c:\\documents and settings\\vince\\*.dmp"`
627     ///   * `"c:\\documents and settings\\*\\crashdumps\\*.dmp"`
628     ///   * `"c:\\temp\\app_log_?????_chrome.txt"`
629     ///
630     /// Takes a `&mut self` because `sbox_add_rule` mutates the underlying
631     /// policy object.
add_rule<T: AsRef<str>>( &mut self, subsystem: SubSystem, semantics: Semantics, pattern: T, ) -> Result<()>632     pub fn add_rule<T: AsRef<str>>(
633         &mut self,
634         subsystem: SubSystem,
635         semantics: Semantics,
636         pattern: T,
637     ) -> Result<()> {
638         // Safe because TargetPolicy can only be constructed with a non-null policy pointer.
639         // The function does not modify the pattern pointer, so that usage is safe.
640         match unsafe {
641             bindings::sbox_add_rule(
642                 self.policy.0,
643                 subsystem,
644                 semantics,
645                 win32_wide_string(pattern.as_ref()).as_ptr(),
646             )
647         } {
648             ResultCode::SBOX_ALL_OK => Ok(()),
649             result_code => Err(SandboxError::new(result_code)),
650         }
651     }
652 
653     /// Adds a dll that will be unloaded in the target process before it gets
654     /// a chance to initialize itself.
655     ///
656     /// Takes a `&mut self` because `sbox_add_dll_to_unload` mutates the
657     /// underlying policy object.
add_dll_to_unload(&mut self, dll_name: &str) -> Result<()>658     pub fn add_dll_to_unload(&mut self, dll_name: &str) -> Result<()> {
659         // Safe because TargetPolicy can only be constructed with a non-null policy pointer.
660         // The function does not modify the dll_name pointer, so that usage is safe.
661         match unsafe {
662             bindings::sbox_add_dll_to_unload(self.policy.0, win32_wide_string(dll_name).as_ptr())
663         } {
664             ResultCode::SBOX_ALL_OK => Ok(()),
665             result_code => Err(SandboxError::new(result_code)),
666         }
667     }
668 
669     /// Adds a handle that will be closed in the target process after lockdown.
670     /// Specifying `None` for `handle_name` indicates all handles of the specified
671     /// type. An empty string for `handle_name` indicates the handle is unnamed.
672     ///
673     /// Takes a `&mut self` because `sbox_add_kernel_object_to_close` mutates the
674     /// underlying policy object.
add_kernel_object_to_close( &mut self, handle_type: &str, handle_name: Option<&str>, ) -> Result<()>675     pub fn add_kernel_object_to_close(
676         &mut self,
677         handle_type: &str,
678         handle_name: Option<&str>,
679     ) -> Result<()> {
680         let handle_name_wide = handle_name.map(win32_wide_string);
681         let handle_name_ptr = handle_name_wide
682             .as_ref()
683             .map_or(std::ptr::null(), Vec::<u16>::as_ptr);
684         // Safe because TargetPolicy can only be constructed with a non-null policy pointer.
685         // The function does not modify either of the string pointers, so that usage is safe.
686         // The function safely handles null pointers for the handle name.
687         match unsafe {
688             bindings::sbox_add_kernel_object_to_close(
689                 self.policy.0,
690                 win32_wide_string(handle_type).as_ptr(),
691                 handle_name_ptr,
692             )
693         } {
694             ResultCode::SBOX_ALL_OK => Ok(()),
695             result_code => Err(SandboxError::new(result_code)),
696         }
697     }
698 
699     /// Adds a handle that will be shared with the target process.
700     ///
701     /// Takes a `&mut self` because `sbox_add_handle_to_share()` mutates the underlying policy
702     /// object.
add_handle_to_share(&mut self, handle: &'a dyn AsRawDescriptor)703     pub fn add_handle_to_share(&mut self, handle: &'a dyn AsRawDescriptor) {
704         // Safe because TargetPolicy can only be constructed with a non-null policy pointer.
705         unsafe {
706             bindings::sbox_add_handle_to_share(self.policy.0, handle.as_raw_descriptor());
707         }
708     }
709 
710     /// Locks down the default DACL of the created lockdown and initial tokens
711     /// to restrict what other processes are allowed to access a process' kernel
712     /// resources.
713     ///
714     /// Takes a `&mut self` because `sbox_set_lockdown_default_dacl()` mutates
715     /// the underlying policy object.
set_lockdown_default_dacl(&mut self)716     pub fn set_lockdown_default_dacl(&mut self) {
717         // Safe because TargetPolicy can only be constructed with a non-null policy pointer.
718         unsafe {
719             bindings::sbox_set_lockdown_default_dacl(self.policy.0);
720         }
721     }
722 
723     /// Adds a restricting random SID to the restricted SIDs list as well as
724     /// the default DACL.
725     ///
726     /// Takes a `&mut self` because `sbox_add_restricting_random_sid()` mutates
727     /// the underlying policy object.
add_restricting_random_sid(&mut self)728     pub fn add_restricting_random_sid(&mut self) {
729         // Safe because TargetPolicy can only be constructed with a non-null policy pointer.
730         unsafe {
731             bindings::sbox_add_restricting_random_sid(self.policy.0);
732         }
733     }
734 
735     /// Configure policy to use an AppContainer profile.
736     ///
737     /// # Arguments:
738     /// * `package_name`: the name of the profile to use.
739     /// * `create_profile`: Specifying `true` for `create_profile` ensures the profile exists, if
740     ///   set to `false` process creation will fail if the profile has not already been created.
741     ///
742     /// Takes a `&mut self` because `sbox_add_dll_to_unload` mutates the
743     /// underlying policy object.
add_app_container_profile( &mut self, package_name: &str, create_profile: bool, ) -> Result<()>744     pub fn add_app_container_profile(
745         &mut self,
746         package_name: &str,
747         create_profile: bool,
748     ) -> Result<()> {
749         // Safe because TargetPolicy can only be constructed with a non-null policy pointer.
750         // The function does not modify the package_name pointer, so that usage is safe.
751         match unsafe {
752             bindings::sbox_add_app_container_profile(
753                 self.policy.0,
754                 win32_wide_string(package_name).as_ptr(),
755                 create_profile,
756             )
757         } {
758             ResultCode::SBOX_ALL_OK => Ok(()),
759             result_code => Err(SandboxError::new(result_code)),
760         }
761     }
762 
763     /// Returns a snapshot of the policy configuration.
policy_info(&self) -> PolicyInfo764     pub fn policy_info(&self) -> PolicyInfo {
765         // Safe because TargetPolicy can only be constructed with a non-null
766         // policy pointer. The underlying PolicyInfo object contains a copy of
767         // the data from the TargetPolicy object, but does not hold any
768         // references to it, so the lifetimes are independent.
769         PolicyInfo {
770             policy_info: unsafe { bindings::sbox_get_policy_info(self.policy.0) },
771         }
772     }
773 }
774 
775 impl PolicyInfo {
776     /// Returns a JSON representation of the policy snapshot.
777     /// This pointer has the same lifetime as the PolicyInfo object.
json(&self) -> &str778     pub fn json(&self) -> &str {
779         // Safe because PolicyInfo can only be constructed with a non-null
780         // policy pointer. The string returned will be a valid pointer to a
781         // valid c string. We bind the lifetime of the string to the lifetime
782         // of the PolicyInfo object, as is guaranteed by the underlying
783         // library. This is a string representation of a snapshot of the
784         // policy, so it will not change.
785         let c_str =
786             unsafe { CStr::from_ptr(bindings::sbox_policy_info_json_string(self.policy_info)) };
787         c_str.to_str().unwrap()
788     }
789 }
790 
791 // TODO(b/196996588): Develop more tests, especially policy-related, once we
792 // have a way to launch and test target processes.
793 #[cfg(test)]
794 mod tests {
795     use super::*;
796 
797     #[test]
not_the_target()798     fn not_the_target() {
799         let target = TargetServices::get().unwrap();
800         assert_eq!(target, None);
801     }
802 
803     #[test]
is_the_broker()804     fn is_the_broker() {
805         let broker = BrokerServices::get().unwrap();
806         assert_ne!(broker, None);
807     }
808 
809     #[test]
policy_handles_live_long_enough()810     fn policy_handles_live_long_enough() {
811         let broker = BrokerServices::get().unwrap().unwrap();
812         let mut policy = broker.create_policy();
813         let pipe = named_pipes::pair(
814             &named_pipes::FramingMode::Byte,
815             &named_pipes::BlockingMode::NoWait,
816             0,
817         )
818         .unwrap();
819         policy.set_stdout_handle(&pipe.0).unwrap();
820         policy.set_stderr_handle(&pipe.0).unwrap();
821         policy.add_handle_to_share(&pipe.0);
822     }
823 }
824