use std::ffi::c_void;
use std::fmt;
use std::io;
use std::ptr;
use windows_sys::Win32::Foundation::{ERROR_INSUFFICIENT_BUFFER, FALSE, TRUE};
use windows_sys::Win32::Security::{
ACL, GetSecurityDescriptorControl, GetSecurityDescriptorDacl, GetSecurityDescriptorLength,
GetSecurityDescriptorSacl, IsValidSecurityDescriptor, MakeSelfRelativeSD, PSECURITY_DESCRIPTOR,
SE_SELF_RELATIVE, SECURITY_ATTRIBUTES, SECURITY_DESCRIPTOR_CONTROL,
};
use crate::buffer::AlignedBuffer;
const SELF_RELATIVE_ALIGNMENT: usize = align_of::<u32>();
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum SecurityCaptureFailure {
InvalidDescriptor,
ReadControl,
SizeSelfRelative,
MakeSelfRelative,
InvalidCopy,
ReadAcl,
}
#[derive(Debug)]
pub struct SecurityCaptureError {
failure: SecurityCaptureFailure,
source: io::Error,
}
impl SecurityCaptureError {
fn new(failure: SecurityCaptureFailure, source: io::Error) -> Self {
Self { failure, source }
}
fn last_os(failure: SecurityCaptureFailure) -> Self {
Self::new(failure, io::Error::last_os_error())
}
#[must_use]
pub fn failure(&self) -> SecurityCaptureFailure {
self.failure
}
#[must_use]
pub fn raw_os_error(&self) -> Option<i32> {
self.source.raw_os_error()
}
}
impl fmt::Display for SecurityCaptureError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let stage = match self.failure {
SecurityCaptureFailure::InvalidDescriptor => "IsValidSecurityDescriptor",
SecurityCaptureFailure::ReadControl => "GetSecurityDescriptorControl",
SecurityCaptureFailure::SizeSelfRelative => "MakeSelfRelativeSD (sizing)",
SecurityCaptureFailure::MakeSelfRelative => "MakeSelfRelativeSD",
SecurityCaptureFailure::InvalidCopy => "IsValidSecurityDescriptor (captured copy)",
SecurityCaptureFailure::ReadAcl => "GetSecurityDescriptorDacl",
};
write!(f, "{stage}: {}", self.source)
}
}
impl std::error::Error for SecurityCaptureError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
Some(&self.source)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum AclState {
Absent,
Null,
Empty,
Populated(u32),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SecurityDescriptor {
blob: AlignedBuffer,
}
impl SecurityDescriptor {
pub unsafe fn capture(descriptor: *const c_void) -> Result<Self, SecurityCaptureError> {
let descriptor = descriptor.cast_mut();
if unsafe { IsValidSecurityDescriptor(descriptor) } == FALSE {
return Err(SecurityCaptureError::last_os(
SecurityCaptureFailure::InvalidDescriptor,
));
}
let mut control: SECURITY_DESCRIPTOR_CONTROL = 0;
let mut revision: u32 = 0;
if unsafe { GetSecurityDescriptorControl(descriptor, &raw mut control, &raw mut revision) }
== FALSE
{
return Err(SecurityCaptureError::last_os(
SecurityCaptureFailure::ReadControl,
));
}
let blob = if control & SE_SELF_RELATIVE == 0 {
unsafe { Self::convert_absolute(descriptor) }?
} else {
unsafe { Self::copy_self_relative(descriptor) }
};
let captured = Self { blob };
if unsafe { IsValidSecurityDescriptor(captured.as_ptr().cast_mut()) } == FALSE {
return Err(SecurityCaptureError::last_os(
SecurityCaptureFailure::InvalidCopy,
));
}
Ok(captured)
}
unsafe fn copy_self_relative(descriptor: PSECURITY_DESCRIPTOR) -> AlignedBuffer {
let length = unsafe { GetSecurityDescriptorLength(descriptor) } as usize;
let mut blob = AlignedBuffer::zeroed(length, SELF_RELATIVE_ALIGNMENT);
unsafe {
ptr::copy_nonoverlapping(descriptor.cast::<u8>(), blob.as_mut_ptr(), length);
}
blob
}
unsafe fn convert_absolute(
descriptor: PSECURITY_DESCRIPTOR,
) -> Result<AlignedBuffer, SecurityCaptureError> {
let mut length: u32 = 0;
let sized = unsafe { MakeSelfRelativeSD(descriptor, ptr::null_mut(), &raw mut length) };
if sized != FALSE {
return Err(SecurityCaptureError::new(
SecurityCaptureFailure::SizeSelfRelative,
io::Error::from_raw_os_error(
i32::try_from(ERROR_INSUFFICIENT_BUFFER)
.expect("ERROR_INSUFFICIENT_BUFFER fits in i32"),
),
));
}
let error = io::Error::last_os_error();
if error.raw_os_error()
!= Some(
i32::try_from(ERROR_INSUFFICIENT_BUFFER)
.expect("ERROR_INSUFFICIENT_BUFFER fits in i32"),
)
{
return Err(SecurityCaptureError::new(
SecurityCaptureFailure::SizeSelfRelative,
error,
));
}
let mut blob = AlignedBuffer::zeroed(length as usize, SELF_RELATIVE_ALIGNMENT);
let converted = unsafe {
MakeSelfRelativeSD(
descriptor,
blob.as_mut_ptr().cast::<c_void>(),
&raw mut length,
)
};
if converted == FALSE {
return Err(SecurityCaptureError::last_os(
SecurityCaptureFailure::MakeSelfRelative,
));
}
Ok(blob)
}
#[must_use]
pub fn as_ptr(&self) -> *const c_void {
self.blob.as_ptr().cast::<c_void>()
}
#[must_use]
pub fn len(&self) -> usize {
self.blob.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.blob.is_empty()
}
#[must_use]
pub fn as_bytes(&self) -> &[u8] {
self.blob.as_slice()
}
pub fn dacl(&self) -> Result<AclState, SecurityCaptureError> {
let mut present = FALSE;
let mut acl: *mut ACL = ptr::null_mut();
let mut defaulted = FALSE;
let read = unsafe {
GetSecurityDescriptorDacl(
self.as_ptr().cast_mut(),
&raw mut present,
&raw mut acl,
&raw mut defaulted,
)
};
Self::acl_state(read, present, acl)
}
pub fn sacl(&self) -> Result<AclState, SecurityCaptureError> {
let mut present = FALSE;
let mut acl: *mut ACL = ptr::null_mut();
let mut defaulted = FALSE;
let read = unsafe {
GetSecurityDescriptorSacl(
self.as_ptr().cast_mut(),
&raw mut present,
&raw mut acl,
&raw mut defaulted,
)
};
Self::acl_state(read, present, acl)
}
fn acl_state(
read: i32,
present: i32,
acl: *const ACL,
) -> Result<AclState, SecurityCaptureError> {
if read == FALSE {
return Err(SecurityCaptureError::last_os(
SecurityCaptureFailure::ReadAcl,
));
}
if present != TRUE {
return Ok(AclState::Absent);
}
let Some(acl) = (unsafe { acl.as_ref() }) else {
return Ok(AclState::Null);
};
Ok(match u32::from(acl.AceCount) {
0 => AclState::Empty,
count => AclState::Populated(count),
})
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SecurityAttributes {
descriptor: Option<SecurityDescriptor>,
inherit_handle: bool,
}
impl SecurityAttributes {
#[must_use]
pub fn new(descriptor: Option<SecurityDescriptor>, inherit_handle: bool) -> Self {
Self {
descriptor,
inherit_handle,
}
}
pub unsafe fn capture(
attributes: *const SECURITY_ATTRIBUTES,
) -> Result<Option<Self>, SecurityCaptureError> {
let Some(attributes) = (unsafe { attributes.as_ref() }) else {
return Ok(None);
};
let descriptor = if attributes.lpSecurityDescriptor.is_null() {
None
} else {
Some(unsafe { SecurityDescriptor::capture(attributes.lpSecurityDescriptor) }?)
};
Ok(Some(Self {
descriptor,
inherit_handle: attributes.bInheritHandle != FALSE,
}))
}
#[must_use]
pub fn descriptor(&self) -> Option<&SecurityDescriptor> {
self.descriptor.as_ref()
}
#[must_use]
pub fn inherit_handle(&self) -> bool {
self.inherit_handle
}
#[must_use]
pub fn to_raw(&self) -> SECURITY_ATTRIBUTES {
SECURITY_ATTRIBUTES {
nLength: u32::try_from(size_of::<SECURITY_ATTRIBUTES>())
.expect("SECURITY_ATTRIBUTES is far smaller than u32::MAX"),
lpSecurityDescriptor: self
.descriptor
.as_ref()
.map_or(ptr::null_mut(), |descriptor| descriptor.as_ptr().cast_mut()),
bInheritHandle: if self.inherit_handle { TRUE } else { FALSE },
}
}
}
#[cfg(test)]
pub(crate) mod tests;