use std::{mem, path::Path, ptr};
use windows_sys::Win32::{
Foundation::{ERROR_SUCCESS, LocalFree},
Security::{
ACCESS_ALLOWED_ACE, ACE_HEADER, ACL,
Authorization::{
EXPLICIT_ACCESS_W, GRANT_ACCESS, GetNamedSecurityInfoW, NO_MULTIPLE_TRUSTEE,
SE_FILE_OBJECT, SetEntriesInAclW, TRUSTEE_IS_GROUP, TRUSTEE_IS_SID, TRUSTEE_W,
},
CONTAINER_INHERIT_ACE, CreateWellKnownSid, DACL_SECURITY_INFORMATION, EqualSid, GetAce,
InitializeSecurityDescriptor, OBJECT_INHERIT_ACE, PSECURITY_DESCRIPTOR, PSID,
SECURITY_DESCRIPTOR, SECURITY_MAX_SID_SIZE, SetFileSecurityW, SetSecurityDescriptorDacl,
WinBuiltinUsersSid,
},
Storage::FileSystem::FILE_ALL_ACCESS,
System::SystemServices::ACCESS_ALLOWED_ACE_TYPE,
};
use crate::{
error::{Error, Result},
wide::wide_path,
};
const GENERIC_ALL: u32 = 0x1000_0000;
const SECURITY_DESCRIPTOR_REVISION: u32 = 1;
const INHERIT_ALL: u32 = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE;
pub fn grant_users_full_access(path: &Path) -> Result<()> {
let wide = wide_path(path);
unsafe {
let mut sid_buffer = [0u8; SECURITY_MAX_SID_SIZE as usize];
let mut sid_size = sid_buffer.len() as u32;
if CreateWellKnownSid(
WinBuiltinUsersSid,
ptr::null_mut(),
sid_buffer.as_mut_ptr().cast::<core::ffi::c_void>(),
&mut sid_size,
) == 0
{
return Err(Error::last("CreateWellKnownSid"));
}
let sid: PSID = sid_buffer.as_mut_ptr().cast();
let mut existing_dacl: *mut ACL = ptr::null_mut();
let mut descriptor: PSECURITY_DESCRIPTOR = ptr::null_mut();
let status = GetNamedSecurityInfoW(
wide.as_ptr(),
SE_FILE_OBJECT,
DACL_SECURITY_INFORMATION,
ptr::null_mut(),
ptr::null_mut(),
&mut existing_dacl,
ptr::null_mut(),
&mut descriptor,
);
if status != ERROR_SUCCESS {
return Err(Error::code("GetNamedSecurityInfoW", status));
}
let _descriptor = LocalGuard(descriptor);
if already_granted(existing_dacl, sid) {
return Ok(());
}
let mut access: EXPLICIT_ACCESS_W = mem::zeroed();
access.grfAccessPermissions = GENERIC_ALL;
access.grfAccessMode = GRANT_ACCESS;
access.grfInheritance = INHERIT_ALL;
access.Trustee = TRUSTEE_W {
pMultipleTrustee: ptr::null_mut(),
MultipleTrusteeOperation: NO_MULTIPLE_TRUSTEE,
TrusteeForm: TRUSTEE_IS_SID,
TrusteeType: TRUSTEE_IS_GROUP,
ptstrName: sid.cast(),
};
let mut merged: *mut ACL = ptr::null_mut();
let status = SetEntriesInAclW(1, &access, existing_dacl, &mut merged);
if status != ERROR_SUCCESS {
return Err(Error::code("SetEntriesInAclW", status));
}
let _merged = LocalGuard(merged.cast());
let mut fresh: SECURITY_DESCRIPTOR = mem::zeroed();
let fresh_ptr: PSECURITY_DESCRIPTOR = (&raw mut fresh).cast();
if InitializeSecurityDescriptor(fresh_ptr, SECURITY_DESCRIPTOR_REVISION) == 0 {
return Err(Error::last("InitializeSecurityDescriptor"));
}
if SetSecurityDescriptorDacl(fresh_ptr, 1, merged, 0) == 0 {
return Err(Error::last("SetSecurityDescriptorDacl"));
}
if SetFileSecurityW(wide.as_ptr(), DACL_SECURITY_INFORMATION, fresh_ptr) == 0 {
return Err(Error::last("SetFileSecurityW"));
}
}
Ok(())
}
unsafe fn already_granted(dacl: *const ACL, sid: PSID) -> bool {
if dacl.is_null() {
return false;
}
let count = unsafe { (*dacl).AceCount };
for index in 0..count as u32 {
let mut ace: *mut core::ffi::c_void = ptr::null_mut();
if unsafe { GetAce(dacl, index, &mut ace) } == 0 || ace.is_null() {
continue;
}
let header = unsafe { &*(ace as *const ACE_HEADER) };
if u32::from(header.AceType) != ACCESS_ALLOWED_ACE_TYPE {
continue;
}
if u32::from(header.AceFlags) & INHERIT_ALL != INHERIT_ALL {
continue;
}
let allowed = unsafe { &*(ace as *const ACCESS_ALLOWED_ACE) };
let grants_everything =
allowed.Mask & FILE_ALL_ACCESS == FILE_ALL_ACCESS || allowed.Mask & GENERIC_ALL != 0;
if !grants_everything {
continue;
}
let ace_sid: PSID = (&raw const allowed.SidStart).cast_mut().cast();
if unsafe { EqualSid(ace_sid, sid) } != 0 {
return true;
}
}
false
}
struct LocalGuard(*mut core::ffi::c_void);
impl Drop for LocalGuard {
fn drop(&mut self) {
if !self.0.is_null() {
unsafe { LocalFree(self.0) };
}
}
}