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