use windows::Win32::Security::{
AddAccessAllowedAceEx, InitializeAcl, InitializeSecurityDescriptor, LookupAccountNameW,
SetFileSecurityW, SetSecurityDescriptorDacl, ACE_REVISION, ACL, CONTAINER_INHERIT_ACE,
DACL_SECURITY_INFORMATION, OBJECT_INHERIT_ACE, PSECURITY_DESCRIPTOR, PSID, SECURITY_DESCRIPTOR,
SID_NAME_USE,
};
const SECURITY_DESCRIPTOR_REVISION: u32 = 1;
const FILE_ALL_ACCESS: u32 = 0x001F01FF;
const FILE_MODIFY_ACCESS: u32 = 0x001301FF;
const FILE_READ_EXECUTE_ACCESS: u32 = 0x001200A9;
fn lookup_sid(principal: &str) -> Result<Vec<u8>, String> {
let name_w: Vec<u16> = principal.encode_utf16().chain(std::iter::once(0)).collect();
let mut sid_size: u32 = 0;
let mut domain_size: u32 = 0;
let mut sid_type = SID_NAME_USE::default();
unsafe {
let _ = LookupAccountNameW(
None,
windows::core::PCWSTR(name_w.as_ptr()),
Some(PSID(std::ptr::null_mut())),
&mut sid_size,
Some(windows::core::PWSTR(std::ptr::null_mut())),
&mut domain_size,
&mut sid_type,
);
}
if sid_size == 0 {
return Err(format!(
"Could not look up account '{principal}': LookupAccountNameW returned zero SID size"
));
}
let mut sid_buf = vec![0u8; sid_size as usize];
let mut domain_buf = vec![0u16; domain_size as usize];
unsafe {
LookupAccountNameW(
None,
windows::core::PCWSTR(name_w.as_ptr()),
Some(PSID(sid_buf.as_mut_ptr() as *mut _)),
&mut sid_size,
Some(windows::core::PWSTR(domain_buf.as_mut_ptr())),
&mut domain_size,
&mut sid_type,
)
.map_err(|e| format!("Could not look up account '{principal}': {e}"))?;
}
Ok(sid_buf)
}
fn build_dacl(
principals_full_control: &[&str],
principals_modify_access: &[&str],
principals_read_execute: &[&str],
) -> Result<Vec<u8>, String> {
let inherit = CONTAINER_INHERIT_ACE | OBJECT_INHERIT_ACE;
let mut ace_entries: Vec<(Vec<u8>, u32)> = Vec::new();
for &principal in principals_full_control {
ace_entries.push((lookup_sid(principal)?, FILE_ALL_ACCESS));
}
for &principal in principals_modify_access {
ace_entries.push((lookup_sid(principal)?, FILE_MODIFY_ACCESS));
}
for &principal in principals_read_execute {
ace_entries.push((lookup_sid(principal)?, FILE_READ_EXECUTE_ACCESS));
}
let acl_header_size = std::mem::size_of::<ACL>();
let mut acl_size = acl_header_size;
for (sid, _) in &ace_entries {
let ace_size = 8 + sid.len();
acl_size += (ace_size + 3) & !3; }
let mut acl_buf = vec![0u8; acl_size];
unsafe {
InitializeAcl(
acl_buf.as_mut_ptr() as *mut ACL,
acl_size as u32,
ACE_REVISION(2),
)
.map_err(|e| format!("InitializeAcl failed: {e}"))?;
}
for (sid, mask) in &ace_entries {
unsafe {
AddAccessAllowedAceEx(
acl_buf.as_mut_ptr() as *mut ACL,
ACE_REVISION(2),
inherit,
*mask,
PSID(sid.as_ptr() as *mut _),
)
.map_err(|e| format!("AddAccessAllowedAceEx failed: {e}"))?;
}
}
Ok(acl_buf)
}
pub fn set_permissions(
path: &str,
principals_full_control: &[&str],
principals_modify_access: &[&str],
principals_read_execute: &[&str],
) -> Result<(), String> {
let acl_buf = build_dacl(
principals_full_control,
principals_modify_access,
principals_read_execute,
)?;
let mut sd = SECURITY_DESCRIPTOR::default();
unsafe {
InitializeSecurityDescriptor(
PSECURITY_DESCRIPTOR(&mut sd as *mut _ as *mut _),
SECURITY_DESCRIPTOR_REVISION,
)
.map_err(|e| format!("InitializeSecurityDescriptor failed: {e}"))?;
SetSecurityDescriptorDacl(
PSECURITY_DESCRIPTOR(&mut sd as *mut _ as *mut _),
true,
Some(acl_buf.as_ptr() as *const ACL),
false,
)
.map_err(|e| format!("SetSecurityDescriptorDacl failed: {e}"))?;
}
let path_w: Vec<u16> = path.encode_utf16().chain(std::iter::once(0)).collect();
let ret = unsafe {
SetFileSecurityW(
windows::core::PCWSTR(path_w.as_ptr()),
DACL_SECURITY_INFORMATION,
PSECURITY_DESCRIPTOR(&sd as *const _ as *mut _),
)
};
if !ret.as_bool() {
return Err(format!(
"SetFileSecurityW failed for '{path}': {}",
std::io::Error::last_os_error()
));
}
Ok(())
}
pub fn set_permissions_protected(
path: &str,
principals_full_control: &[&str],
principals_modify_access: &[&str],
principals_read_execute: &[&str],
) -> Result<(), String> {
use windows::Win32::Security::Authorization::{SetNamedSecurityInfoW, SE_FILE_OBJECT};
use windows::Win32::Security::PROTECTED_DACL_SECURITY_INFORMATION;
let acl_buf = build_dacl(
principals_full_control,
principals_modify_access,
principals_read_execute,
)?;
let path_w: Vec<u16> = path.encode_utf16().chain(std::iter::once(0)).collect();
let result = unsafe {
SetNamedSecurityInfoW(
windows::core::PCWSTR(path_w.as_ptr()),
SE_FILE_OBJECT,
DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION,
None, None, Some(acl_buf.as_ptr() as *const ACL),
None, )
};
if result.is_err() {
return Err(format!(
"SetNamedSecurityInfoW failed for '{path}': WIN32_ERROR({})",
result.0
));
}
Ok(())
}