// 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. use std::alloc::alloc_zeroed; use std::alloc::dealloc; use std::alloc::handle_alloc_error; use std::alloc::Layout; use std::convert::TryFrom; use std::convert::TryInto; use std::io; use std::mem::size_of; use std::os::windows::io::RawHandle; use std::ptr; use once_cell::sync::OnceCell; use winapi::shared::minwindef::FALSE; use winapi::shared::minwindef::HLOCAL; use winapi::shared::minwindef::LPDWORD; use winapi::shared::minwindef::LPVOID; use winapi::shared::minwindef::TRUE; use winapi::shared::winerror::ERROR_INSUFFICIENT_BUFFER; use winapi::shared::winerror::ERROR_SUCCESS; use winapi::um::accctrl::EXPLICIT_ACCESS_A; use winapi::um::accctrl::NO_INHERITANCE; use winapi::um::accctrl::NO_MULTIPLE_TRUSTEE; use winapi::um::accctrl::PEXPLICIT_ACCESSA; use winapi::um::accctrl::SET_ACCESS; use winapi::um::accctrl::TRUSTEE_A; use winapi::um::accctrl::TRUSTEE_IS_SID; use winapi::um::accctrl::TRUSTEE_IS_USER; use winapi::um::aclapi::SetEntriesInAclA; use winapi::um::handleapi::CloseHandle; use winapi::um::minwinbase::SECURITY_ATTRIBUTES; use winapi::um::processthreadsapi::GetCurrentProcess; use winapi::um::processthreadsapi::OpenProcessToken; use winapi::um::processthreadsapi::OpenThreadToken; use winapi::um::securitybaseapi::GetTokenInformation; use winapi::um::securitybaseapi::InitializeSecurityDescriptor; use winapi::um::securitybaseapi::MakeSelfRelativeSD; use winapi::um::securitybaseapi::SetSecurityDescriptorDacl; use winapi::um::winbase::LocalFree; use winapi::um::winnt::TokenIntegrityLevel; use winapi::um::winnt::TokenStatistics; use winapi::um::winnt::TokenUser; use winapi::um::winnt::ACL; use winapi::um::winnt::GENERIC_ALL; use winapi::um::winnt::PACL; use winapi::um::winnt::PSECURITY_DESCRIPTOR; use winapi::um::winnt::SECURITY_DESCRIPTOR; use winapi::um::winnt::SECURITY_DESCRIPTOR_REVISION; use winapi::um::winnt::TOKEN_ALL_ACCESS; use winapi::um::winnt::TOKEN_INFORMATION_CLASS; use winapi::um::winnt::TOKEN_MANDATORY_LABEL; use winapi::um::winnt::TOKEN_STATISTICS; use winapi::um::winnt::TOKEN_USER; /// Struct for wrapping `SECURITY_ATTRIBUTES` and `SECURITY_DESCRIPTOR`. pub struct SecurityAttributes { // The security descriptor shouldn't move, as it will be referenced by the // security attributes. We already limit what can be done with the security // attributes by only providing a reference to it, but we also want to // ensure the security descriptor pointer remains valid even if this struct // moves around. _security_descriptor: T, security_attributes: SECURITY_ATTRIBUTES, } impl SecurityAttributes { /// Create a new `SecurityAttributes` struct with the provided `security_descriptor`. pub fn new_with_security_descriptor(security_descriptor: T, inherit: bool) -> Self { let sd = security_descriptor.security_descriptor(); let security_attributes = SECURITY_ATTRIBUTES { nLength: size_of::() as u32, lpSecurityDescriptor: sd as PSECURITY_DESCRIPTOR, bInheritHandle: if inherit { TRUE } else { FALSE }, }; SecurityAttributes { _security_descriptor: security_descriptor, security_attributes, } } } impl SecurityAttributes { /// Create a new `SecurityAttributes` struct. This struct will have a /// `SECURITY_DESCRIPTOR` that allows full access (`GENERIC_ALL`) to only /// the current user. pub fn new(inherit: bool) -> io::Result { Ok(Self::new_with_security_descriptor( SelfRelativeSecurityDescriptor::new()?, inherit, )) } } impl AsRef for SecurityAttributes { fn as_ref(&self) -> &SECURITY_ATTRIBUTES { &self.security_attributes } } impl AsMut for SecurityAttributes { fn as_mut(&mut self) -> &mut SECURITY_ATTRIBUTES { &mut self.security_attributes } } pub trait TokenClass { fn class() -> TOKEN_INFORMATION_CLASS; } impl TokenClass for TOKEN_MANDATORY_LABEL { fn class() -> TOKEN_INFORMATION_CLASS { TokenIntegrityLevel } } impl TokenClass for TOKEN_STATISTICS { fn class() -> TOKEN_INFORMATION_CLASS { TokenStatistics } } impl TokenClass for TOKEN_USER { fn class() -> TOKEN_INFORMATION_CLASS { TokenUser } } pub struct TokenInformation { token_info: *mut T, layout: Layout, } impl TokenInformation { pub fn new(mut token: Token) -> io::Result { let token_handle = token.get(); // Retrieve the size of the struct. let mut size: u32 = 0; // SAFETY: size is valid, and TokenInformation is optional and allowed to be null. if unsafe { // The idiomatic usage of GetTokenInformation() requires two calls // to the function: the first to get the length of the data that the // function would return, and the second to fetch the data. GetTokenInformation( /* TokenHandle= */ token_handle, /* TokenInformationClass= */ T::class(), /* TokenInformation= */ ptr::null_mut(), /* TokenInformationLength= */ 0, /* ReturnLength= */ &mut size, ) == 0 } { const INSUFFICIENT_BUFFER: i32 = ERROR_INSUFFICIENT_BUFFER as i32; match io::Error::last_os_error().raw_os_error() { Some(INSUFFICIENT_BUFFER) => { // Despite returning failure, the function will fill in the // expected buffer length into the ReturnLength parameter. // It may fail in other ways (e.g. if an invalid TokenHandle // is provided), so we check that we receive the expected // error code before assuming that we received a valid // ReturnLength. In this case, we can ignore the error. } _ => return Err(io::Error::last_os_error()), }; } // size must be > 0. 0-sized layouts break alloc()'s assumptions. assert!(size > 0, "Unable to get size of token information"); // Since we don't statically know the full size of the struct, we // allocate memory for it based on the previous call, aligned to pointer // size. let layout = Layout::from_size_align(size as usize, size_of::()) .expect("Failed to create layout"); assert!(layout.size() > 0, "Failed to create valid layout"); // SAFETY: We assert that layout's size is non-zero. let token_info = unsafe { alloc_zeroed(layout) } as *mut T; if token_info.is_null() { handle_alloc_error(layout); } let token_info = TokenInformation:: { token_info, layout }; // SAFETY: token_user and size are valid. if unsafe { GetTokenInformation( /* TokenHandle= */ token_handle, /* TokenInformationClass= */ T::class(), /* TokenInformation= */ token_info.token_info as LPVOID, /* TokenInformationLength= */ size, /* ReturnLength= */ &mut size, ) == 0 } { return Err(io::Error::last_os_error()); } Ok(token_info) } } impl AsRef for TokenInformation { fn as_ref(&self) -> &T { // SAFETY: The underlying pointer is guaranteed to be properly aligned, dereferenceable, and // point to a valid T. The underlying value will not be modified through the pointer and can // only be accessed through these returned references. unsafe { &*self.token_info } } } impl AsMut for TokenInformation { fn as_mut(&mut self) -> &mut T { // SAFETY: The underlying pointer is guaranteed to be properly aligned, dereferenceable, and // point to a valid T. The underlying value will not be modified through the pointer and can // only be accessed through these returned references. unsafe { &mut *self.token_info } } } impl Drop for TokenInformation { fn drop(&mut self) { // SAFETY: We ensure the pointer is valid in the constructor, and we are using the same // layout struct as during the allocation. unsafe { dealloc(self.token_info as *mut u8, self.layout) } } } pub struct Token { token: RawHandle, } impl Token { /// Open the current process's token. pub fn new_for_process() -> io::Result { // SAFETY: GetCurrentProcess is an alias for -1. Self::from_process(unsafe { GetCurrentProcess() }) } /// Open the token of a process. pub fn from_process(proc_handle: RawHandle) -> io::Result { let mut token: RawHandle = ptr::null_mut(); // SAFETY: Token is valid. if unsafe { OpenProcessToken( /* ProcessHandle= */ proc_handle, /* DesiredAccess= */ TOKEN_ALL_ACCESS, /* TokenHandle= */ &mut token, ) == 0 } { return Err(io::Error::last_os_error()); } Ok(Token { token }) } /// Open the token of a thread. pub fn from_thread(thread_handle: RawHandle) -> io::Result { let mut token: RawHandle = ptr::null_mut(); // SAFETY: Token is valid. We use OpenAsSelf to ensure the token access is measured // using the caller's non-impersonated identity. if unsafe { OpenThreadToken( thread_handle, TOKEN_ALL_ACCESS, /* OpenAsSelf= */ TRUE, &mut token, ) == 0 } { return Err(io::Error::last_os_error()); } Ok(Token { token }) } fn get(&mut self) -> RawHandle { self.token } } impl Drop for Token { fn drop(&mut self) { // SAFETY: Token is valid, but the call should be safe regardless. unsafe { CloseHandle(self.token); } } } pub trait SecurityDescriptor { fn security_descriptor(&self) -> *const SECURITY_DESCRIPTOR; } pub struct AbsoluteSecurityDescriptor { descriptor: SECURITY_DESCRIPTOR, acl: *mut ACL, } impl AbsoluteSecurityDescriptor { /// Creates a `SECURITY_DESCRIPTOR` struct which gives full access rights /// (`GENERIC_ALL`) to only the current user. fn new() -> io::Result { let token = Token::new_for_process()?; let token_user = TokenInformation::::new(token)?; let sid = token_user.as_ref().User.Sid; let mut ea = EXPLICIT_ACCESS_A { grfAccessPermissions: GENERIC_ALL, grfAccessMode: SET_ACCESS, grfInheritance: NO_INHERITANCE, Trustee: TRUSTEE_A { TrusteeForm: TRUSTEE_IS_SID, TrusteeType: TRUSTEE_IS_USER, ptstrName: sid as *mut i8, pMultipleTrustee: ptr::null_mut(), MultipleTrusteeOperation: NO_MULTIPLE_TRUSTEE, }, }; let mut security_descriptor: std::mem::MaybeUninit = std::mem::MaybeUninit::uninit(); let ptr = security_descriptor.as_mut_ptr(); // SAFETY: security_descriptor is valid but uninitialized, and // InitializeSecurityDescriptor will initialize it. if unsafe { InitializeSecurityDescriptor( /* pSecurityDescriptor= */ ptr::addr_of_mut!((*ptr).descriptor) as PSECURITY_DESCRIPTOR, /* dwRevision= */ SECURITY_DESCRIPTOR_REVISION, ) == 0 } { return Err(io::Error::last_os_error()); } // SAFETY: ea and acl are valid and OldAcl is allowed to be null. if unsafe { SetEntriesInAclA( /* cCountOfExplicitEntries= */ 1, /* pListOfExplicitEntries= */ &mut ea as PEXPLICIT_ACCESSA, /* OldAcl= */ ptr::null_mut(), /* NewAcl= */ ptr::addr_of_mut!((*ptr).acl) as *mut PACL, ) } != ERROR_SUCCESS { return Err(io::Error::last_os_error()); } // SAFETY: security_descriptor is valid and initialized after // InitializeSecurityDescriptor() and SetEntriesInAclA(). let mut security_descriptor = unsafe { security_descriptor.assume_init() }; let sd = &mut security_descriptor.descriptor as *mut SECURITY_DESCRIPTOR; // SAFETY: The descriptor is valid, and acl is valid after SetEntriesInAclA() if unsafe { SetSecurityDescriptorDacl( /* pSecurityDescriptor= */ sd as PSECURITY_DESCRIPTOR, /* bDaclPresent= */ TRUE, /* pDacl= */ security_descriptor.acl, /* bDaclDefaulted= */ FALSE, ) == 0 } { return Err(io::Error::last_os_error()); } Ok(security_descriptor) } } impl SecurityDescriptor for AbsoluteSecurityDescriptor { fn security_descriptor(&self) -> *const SECURITY_DESCRIPTOR { &self.descriptor as *const SECURITY_DESCRIPTOR } } impl Drop for AbsoluteSecurityDescriptor { fn drop(&mut self) { // SAFETY: We guarantee that on creation acl is initialized to a pointer that can be freed. unsafe { LocalFree(self.acl as HLOCAL) }; } } pub struct SelfRelativeSecurityDescriptor { descriptor: *mut SECURITY_DESCRIPTOR, layout: Layout, } impl Drop for SelfRelativeSecurityDescriptor { fn drop(&mut self) { // SAFETY: We own self.descriptor and guarantee it is a valid pointer. unsafe { dealloc(self.descriptor as *mut u8, self.layout) } } } impl Clone for SelfRelativeSecurityDescriptor { fn clone(&self) -> Self { // SAFETY: We know that the layout's size is non-zero. let descriptor = unsafe { alloc_zeroed(self.layout) } as *mut SECURITY_DESCRIPTOR; if descriptor.is_null() { handle_alloc_error(self.layout); } let sd = SelfRelativeSecurityDescriptor { descriptor, layout: self.layout, }; // SAFETY: // * `src` is at least `count` bytes, as it was allocated using the above layout. // * `dst` is at least `count` bytes, as we just allocated it using the above layout. // * `src` and `dst` are aligned according to the layout, and we are copying byte-wise. // * `src` and `dst` do not overlap, as we just allocated new memory for `dst`. unsafe { std::ptr::copy_nonoverlapping::( /* src= */ self.descriptor as *const u8, /* dst= */ sd.descriptor as *mut u8, /* count= */ self.layout.size(), ) }; sd } } impl TryFrom for SelfRelativeSecurityDescriptor { type Error = io::Error; fn try_from(sd: AbsoluteSecurityDescriptor) -> io::Result { let mut size: u32 = 0; let descriptor = &sd.descriptor as *const SECURITY_DESCRIPTOR; // SAFETY: Descriptor and size are valid, and pSelfRelativeSD is optional and allowed to be // null. if unsafe { MakeSelfRelativeSD( /* pAbsoluteSD= */ descriptor as PSECURITY_DESCRIPTOR, /* pSelfRelativeSD= */ ptr::null_mut(), /* lpdwBufferLength= */ &mut size as LPDWORD, ) } == 0 { const INSUFFICIENT_BUFFER: i32 = ERROR_INSUFFICIENT_BUFFER as i32; match io::Error::last_os_error().raw_os_error() { Some(INSUFFICIENT_BUFFER) => { // Despite returning failure, the function will fill in the // expected buffer length into the lpdwBufferLength parameter. // It may fail in other ways (e.g. if an invalid pAbsoluteSD // is provided), so we check that we receive the expected // error code before assuming that we received a valid // lpdwBufferLength. In this case, we can ignore the error. } _ => return Err(io::Error::last_os_error()), } } // size must be > 0. 0-sized layouts break alloc()'s assumptions. assert!(size > 0, "Unable to get size of self-relative SD"); // Since we don't statically know the full size of the struct, we // allocate memory for it based on the previous call, aligned to pointer // size. let layout = Layout::from_size_align(size as usize, size_of::()) .expect("Failed to create layout"); assert!(layout.size() > 0, "Failed to create valid layout"); // SAFETY: We assert that layout's size is non-zero. let self_relative_sd = unsafe { alloc_zeroed(layout) } as *mut SECURITY_DESCRIPTOR; if self_relative_sd.is_null() { handle_alloc_error(layout); } let self_relative_sd = SelfRelativeSecurityDescriptor { descriptor: self_relative_sd, layout, }; // SAFETY: Descriptor is valid, the newly allocated self_relative_sd descriptor is valid, // and size is valid. if unsafe { MakeSelfRelativeSD( /* pAbsoluteSD= */ descriptor as PSECURITY_DESCRIPTOR, /* pSelfRelativeSD= */ self_relative_sd.descriptor as PSECURITY_DESCRIPTOR, /* lpdwBufferLength= */ &mut size as LPDWORD, ) } == 0 { return Err(io::Error::last_os_error()); } Ok(self_relative_sd) } } impl SelfRelativeSecurityDescriptor { /// Creates a `SECURITY_DESCRIPTOR` struct which gives full access rights /// (`GENERIC_ALL`) to only the current user. fn new() -> io::Result { AbsoluteSecurityDescriptor::new()?.try_into() } /// Gets a copy of a singleton `SelfRelativeSecurityDescriptor`. pub fn get_singleton() -> SelfRelativeSecurityDescriptor { static DEFAULT_SECURITY_DESCRIPTOR: OnceCell = OnceCell::new(); DEFAULT_SECURITY_DESCRIPTOR .get_or_init(|| { SelfRelativeSecurityDescriptor::new().expect("Failed to create security descriptor") }) .clone() } } impl SecurityDescriptor for SelfRelativeSecurityDescriptor { fn security_descriptor(&self) -> *const SECURITY_DESCRIPTOR { self.descriptor } } // SAFETY: The descriptor and ACLs are treated as immutable by consuming functions and can be safely // shared between threads. unsafe impl Send for SelfRelativeSecurityDescriptor {} //SAFETY: The descriptor and ACLs are treated as immutable by consuming functions. unsafe impl Sync for SelfRelativeSecurityDescriptor {}