use {
super::*,
crate::{mut2ptr, OrErrno},
std::{
marker::PhantomData,
mem::{size_of, size_of_val, zeroed, ManuallyDrop},
ptr::{self, NonNull},
},
windows_sys::Win32::{
Foundation::LocalFree,
Security::{
AclRevisionInformation, AclSizeInformation, AddAce, CopySid, GetAce,
GetAclInformation, GetSidLengthRequired, GetSidSubAuthorityCount, InitializeAcl,
IsValidSid, ACE_HEADER, ACL, ACL_INFORMATION_CLASS, ACL_REVISION_INFORMATION,
ACL_SIZE_INFORMATION, SECURITY_DESCRIPTOR_CONTROL, SE_DACL_PROTECTED,
SE_SACL_PROTECTED,
},
System::{
Memory::{LocalAlloc, LMEM_FIXED},
SystemServices::MAXDWORD,
},
},
};
pub(super) unsafe fn clone(sd: *const c_void) -> io::Result<SecurityDescriptor> {
const CONTROL_MASK: SECURITY_DESCRIPTOR_CONTROL = SE_DACL_PROTECTED | SE_SACL_PROTECTED;
let mut new_sd = SecurityDescriptor::new()?;
let old_sd = unsafe {
BorrowedSecurityDescriptor::from_ptr(sd)
};
let clnsid = |(sid, dfl)| {
io::Result::<(Option<LocalBox<c_void>>, bool)>::Ok((unsafe { clone_sid(sid)? }, dfl))
};
let dacl = old_sd.dacl()?;
let sacl = old_sd.sacl()?;
let owner = clnsid(old_sd.owner()?)?;
let group = clnsid(old_sd.group()?)?;
if let Some((acl, dfl)) = dacl {
if acl.is_null() {
unsafe { new_sd.set_dacl(ptr::null_mut(), dfl)? };
} else {
let mut acl = ManuallyDrop::new(unsafe { clone_acl(acl)? });
unsafe { new_sd.set_dacl((*acl).as_mut_ptr(), dfl)? };
}
}
if let Some((acl, dfl)) = sacl {
if acl.is_null() {
unsafe { new_sd.set_sacl(ptr::null_mut(), dfl)? };
} else {
let mut acl = ManuallyDrop::new(unsafe { clone_acl(acl)? });
unsafe { new_sd.set_sacl((*acl).as_mut_ptr(), dfl)? };
}
}
let assid = |sid: &mut LocalBox<c_void>| sid.as_mut_ptr();
let (mut owner, odfl) = (ManuallyDrop::new(owner.0), owner.1);
if let Some(owner) = owner.as_mut().map(assid) {
unsafe { new_sd.set_owner(owner, odfl)? };
}
let (mut group, gdfl) = (ManuallyDrop::new(group.0), group.1);
if let Some(group) = group.as_mut().map(assid) {
unsafe { new_sd.set_owner(group, gdfl)? };
}
let control = old_sd.control_and_revision()?.0;
new_sd.set_control(CONTROL_MASK, control & CONTROL_MASK)?;
Ok(new_sd)
}
pub(crate) struct LocalBox<T>(NonNull<T>, PhantomData<T>);
impl<T> LocalBox<T> {
#[allow(clippy::unwrap_used, clippy::unwrap_in_result)]
pub fn allocate(sz: u32) -> io::Result<Self> {
let allocation = unsafe { LocalAlloc(LMEM_FIXED, sz.try_into().unwrap()) };
(allocation.is_null()).false_or_errno(|| unsafe {
Self(NonNull::new_unchecked(allocation.cast()), PhantomData)
})
}
#[inline]
pub fn as_ptr(&self) -> *const T { self.0.as_ptr().cast_const() }
#[inline]
pub fn as_mut_ptr(&mut self) -> *mut T { self.0.as_ptr() }
#[inline]
pub unsafe fn from_raw(raw: *mut T) -> Self {
unsafe { Self(NonNull::new_unchecked(raw), PhantomData) }
}
}
impl<T> Drop for LocalBox<T> {
fn drop(&mut self) {
unsafe { LocalFree(self.as_mut_ptr().cast()) }
.is_null()
.true_val_or_errno(())
.expect("LocalFree() failed")
}
}
unsafe fn get_acl_info<T>(
acl: *const ACL,
information_class: ACL_INFORMATION_CLASS,
) -> io::Result<T> {
let mut info = unsafe { zeroed::<T>() };
unsafe {
GetAclInformation(
acl.cast_mut(),
mut2ptr(&mut info).cast(),
size_of_val(&info).try_into().unwrap(),
information_class,
)
.true_val_or_errno(info)
}
}
fn create_acl(sz: u32, rev: u32) -> io::Result<LocalBox<ACL>> {
#[allow(clippy::cast_possible_truncation)] const ALIGN: u32 = size_of::<u32>() as u32; const ALIGN_MASK: u32 = ALIGN - 1; let sz = if sz & ALIGN_MASK != 0 {
sz.checked_add(1).unwrap()
} else {
sz
};
let mut acl = LocalBox::allocate(sz)?;
unsafe { InitializeAcl(acl.as_mut_ptr(), sz, rev) }.true_val_or_errno(acl)
}
unsafe fn clone_acl(acl: *const ACL) -> io::Result<LocalBox<ACL>> {
let (sz_info, rev) = unsafe {
let sz_info = get_acl_info::<ACL_SIZE_INFORMATION>(acl, AclSizeInformation)?;
let rev =
get_acl_info::<ACL_REVISION_INFORMATION>(acl, AclRevisionInformation)?.AclRevision;
(sz_info, rev)
};
let mut new_acl = create_acl(sz_info.AclBytesInUse, rev)?;
unsafe {
let mut ace = ptr::null_mut();
for i in 0..sz_info.AceCount {
GetAce(acl, i, &mut ace).true_val_or_errno(())?;
AddAce(
new_acl.as_mut_ptr(),
rev,
MAXDWORD,
ace.cast_const(),
(*ace.cast_const().cast::<ACE_HEADER>()).AceSize.into(),
)
.true_val_or_errno(())?;
}
}
Ok(new_acl)
}
unsafe fn clone_sid(sid: *const c_void) -> io::Result<Option<LocalBox<c_void>>> {
if sid.is_null() {
return Ok(None);
}
let sid = sid.cast_mut();
unsafe { IsValidSid(sid) }.true_val_or_errno(())?;
let num_subauths = unsafe { *GetSidSubAuthorityCount(sid) };
let sz = unsafe { GetSidLengthRequired(num_subauths) };
let mut new_sid = LocalBox::allocate(sz)?;
unsafe { CopySid(sz, new_sid.as_mut_ptr(), sid) }.true_val_or_errno(Some(new_sid))
}