use std::ffi::c_void;
use std::fmt;
use std::io;
use std::ptr;
use windows_sys::Win32::Foundation::{
ERROR_INSUFFICIENT_BUFFER, ERROR_NOT_ENOUGH_MEMORY, FALSE, TRUE,
};
use windows_sys::Win32::Security::{
ACL, GetSecurityDescriptorControl, GetSecurityDescriptorDacl, GetSecurityDescriptorSacl,
IsValidSecurityDescriptor, MakeAbsoluteSD, MakeSelfRelativeSD, PSECURITY_DESCRIPTOR,
SE_SELF_RELATIVE, SECURITY_ATTRIBUTES, SECURITY_DESCRIPTOR_CONTROL,
};
use io_buffers::AlignedBuf;
mod query;
mod sddl;
pub use query::{ObjectType, get_security_info, get_security_info_unchecked};
pub use sddl::SecurityInformation;
const SELF_RELATIVE_ALIGNMENT: usize = align_of::<u32>();
const ABSOLUTE_ALIGNMENT: usize = align_of::<usize>();
const _: () = assert!(SELF_RELATIVE_ALIGNMENT.is_power_of_two());
const _: () = assert!(ABSOLUTE_ALIGNMENT.is_power_of_two());
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
#[non_exhaustive]
pub enum SecurityCaptureFailure {
InvalidDescriptor,
ReadControl,
SizeSelfRelative,
MakeSelfRelative,
SizeAbsolute,
MakeAbsolute,
InvalidCopy,
ReadAcl,
Allocate,
InvalidSddl,
InteriorNul,
ToSddl,
ReadSecurity,
UnsupportedObjectType,
}
#[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())
}
fn allocation() -> Self {
Self::new(
SecurityCaptureFailure::Allocate,
io::Error::from_raw_os_error(
i32::try_from(ERROR_NOT_ENOUGH_MEMORY)
.expect("ERROR_NOT_ENOUGH_MEMORY fits in i32"),
),
)
}
#[must_use]
pub fn into_source(self) -> io::Error {
self.source
}
#[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::SizeAbsolute => "MakeAbsoluteSD (sizing)",
SecurityCaptureFailure::MakeAbsolute => "MakeAbsoluteSD",
SecurityCaptureFailure::InvalidCopy => "IsValidSecurityDescriptor (captured copy)",
SecurityCaptureFailure::ReadAcl => "GetSecurityDescriptorDacl",
SecurityCaptureFailure::Allocate => "allocating the descriptor copy",
SecurityCaptureFailure::InvalidSddl => {
"ConvertStringSecurityDescriptorToSecurityDescriptorW"
}
SecurityCaptureFailure::InteriorNul => "an SDDL string with an interior NUL",
SecurityCaptureFailure::ToSddl => {
"ConvertSecurityDescriptorToStringSecurityDescriptorW"
}
SecurityCaptureFailure::ReadSecurity => "GetSecurityInfo",
SecurityCaptureFailure::UnsupportedObjectType => {
"an object kind get_security_info does not take with any handle"
}
};
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(Debug, PartialEq, Eq)]
pub struct SecurityDescriptor {
blob: AlignedBuf,
}
fn allocate(length: usize) -> Result<AlignedBuf, SecurityCaptureError> {
AlignedBuf::zeroed(length, SELF_RELATIVE_ALIGNMENT)
.map_err(|_| SecurityCaptureError::allocation())
}
fn allocate_part(length: u32) -> Result<AlignedBuf, SecurityCaptureError> {
AlignedBuf::zeroed(length as usize, ABSOLUTE_ALIGNMENT)
.map_err(|_| SecurityCaptureError::allocation())
}
fn part_pointer<T>(part: &mut AlignedBuf) -> *mut T {
if part.is_empty() {
ptr::null_mut()
} else {
part.as_mut_ptr().cast::<T>()
}
}
fn insufficient_buffer() -> i32 {
i32::try_from(ERROR_INSUFFICIENT_BUFFER).expect("ERROR_INSUFFICIENT_BUFFER fits in i32")
}
impl SecurityDescriptor {
pub fn try_clone(&self) -> Result<Self, SecurityCaptureError> {
self.blob
.try_clone()
.map(|blob| Self { blob })
.map_err(|_| SecurityCaptureError::allocation())
}
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::rebuild_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 rebuild_self_relative(
descriptor: PSECURITY_DESCRIPTOR,
) -> Result<AlignedBuf, SecurityCaptureError> {
let mut header_size = 0_u32;
let mut dacl_size = 0_u32;
let mut sacl_size = 0_u32;
let mut owner_size = 0_u32;
let mut group_size = 0_u32;
let sized = unsafe {
MakeAbsoluteSD(
descriptor,
ptr::null_mut(),
&raw mut header_size,
ptr::null_mut(),
&raw mut dacl_size,
ptr::null_mut(),
&raw mut sacl_size,
ptr::null_mut(),
&raw mut owner_size,
ptr::null_mut(),
&raw mut group_size,
)
};
if sized != FALSE {
return Err(SecurityCaptureError::new(
SecurityCaptureFailure::SizeAbsolute,
io::Error::from_raw_os_error(insufficient_buffer()),
));
}
let error = io::Error::last_os_error();
if error.raw_os_error() != Some(insufficient_buffer()) {
return Err(SecurityCaptureError::new(
SecurityCaptureFailure::SizeAbsolute,
error,
));
}
let mut header = allocate_part(header_size)?;
let mut dacl = allocate_part(dacl_size)?;
let mut sacl = allocate_part(sacl_size)?;
let mut owner = allocate_part(owner_size)?;
let mut group = allocate_part(group_size)?;
let taken_apart = unsafe {
MakeAbsoluteSD(
descriptor,
part_pointer(&mut header),
&raw mut header_size,
part_pointer(&mut dacl),
&raw mut dacl_size,
part_pointer(&mut sacl),
&raw mut sacl_size,
part_pointer(&mut owner),
&raw mut owner_size,
part_pointer(&mut group),
&raw mut group_size,
)
};
if taken_apart == FALSE {
return Err(SecurityCaptureError::last_os(
SecurityCaptureFailure::MakeAbsolute,
));
}
unsafe { Self::convert_absolute(header.as_mut_ptr().cast::<c_void>()) }
}
unsafe fn convert_absolute(
descriptor: PSECURITY_DESCRIPTOR,
) -> Result<AlignedBuf, 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 = allocate(length as usize)?;
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(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 fn try_clone(&self) -> Result<Self, SecurityCaptureError> {
Ok(Self {
descriptor: self
.descriptor
.as_ref()
.map(SecurityDescriptor::try_clone)
.transpose()?,
inherit_handle: self.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)]
mod self_relative_tests;
#[cfg(test)]
mod tests;