use super::{wide, Security, User};
use std::{io, mem::size_of, path::Path, ptr::null_mut};
use windows_sys::Win32::{
Foundation::{CloseHandle, GetLastError, LocalFree, ERROR_NOT_ALL_ASSIGNED, HANDLE},
Security::{
AdjustTokenPrivileges,
Authorization::{
ConvertSidToStringSidW, ConvertStringSecurityDescriptorToSecurityDescriptorW,
GetNamedSecurityInfoW, SetNamedSecurityInfoW, SDDL_REVISION_1, SE_FILE_OBJECT,
},
EqualSid, GetAce, GetSecurityDescriptorControl, GetSecurityDescriptorDacl,
GetSecurityDescriptorOwner, LookupPrivilegeValueW, ACCESS_ALLOWED_ACE, ACL,
CONTAINER_INHERIT_ACE, DACL_SECURITY_INFORMATION, INHERITED_ACE, LUID_AND_ATTRIBUTES,
OBJECT_INHERIT_ACE, OWNER_SECURITY_INFORMATION, PROTECTED_DACL_SECURITY_INFORMATION,
PSECURITY_DESCRIPTOR, PSID, SE_DACL_PROTECTED, SE_PRIVILEGE_ENABLED,
TOKEN_ADJUST_PRIVILEGES, TOKEN_PRIVILEGES, TOKEN_QUERY,
},
Storage::FileSystem::FILE_ALL_ACCESS,
System::Threading::{GetCurrentProcess, OpenProcessToken},
};
fn enable_restore_privilege() -> io::Result<()> {
let name: Vec<u16> = "SeRestorePrivilege".encode_utf16().chain(Some(0)).collect();
let mut token: HANDLE = null_mut();
if unsafe {
OpenProcessToken(
GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&mut token,
)
} == 0
{
return Err(io::Error::last_os_error());
}
let mut privileges: TOKEN_PRIVILEGES = unsafe { std::mem::zeroed() };
privileges.PrivilegeCount = 1;
let result = (|| {
if unsafe {
LookupPrivilegeValueW(
null_mut(),
name.as_ptr(),
&mut privileges.Privileges[0].Luid,
)
} == 0
{
return Err(io::Error::last_os_error());
}
privileges.Privileges[0] = LUID_AND_ATTRIBUTES {
Luid: privileges.Privileges[0].Luid,
Attributes: SE_PRIVILEGE_ENABLED,
};
if unsafe { AdjustTokenPrivileges(token, 0, &privileges, 0, null_mut(), null_mut()) } == 0 {
return Err(io::Error::last_os_error());
}
if unsafe { GetLastError() } == ERROR_NOT_ALL_ASSIGNED {
return Err(io::Error::from_raw_os_error(ERROR_NOT_ALL_ASSIGNED as i32));
}
Ok(())
})();
unsafe { CloseHandle(token) };
result
}
pub fn assert_owner_only(path: &Path, directory: bool, protected: bool) {
assert_dacl(path, directory, protected, true);
}
pub fn assert_dacl_owner_only(path: &Path, directory: bool, protected: bool) {
assert_dacl(path, directory, protected, false);
}
fn assert_dacl(path: &Path, directory: bool, protected: bool, check_owner: bool) {
let name = wide(path).unwrap();
let mut security = Security {
descriptor: null_mut(),
owner: null_mut(),
dacl: null_mut(),
};
assert_eq!(
unsafe {
GetNamedSecurityInfoW(
name.as_ptr(),
SE_FILE_OBJECT,
OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
&mut security.owner,
null_mut(),
&mut security.dacl,
null_mut(),
&mut security.descriptor,
)
},
0
);
let user = User::current().unwrap();
if check_owner {
assert!(!security.owner.is_null());
assert_ne!(
unsafe { EqualSid(security.owner, user.sid()) },
0,
"{} is owned by {}, not the current user {}",
path.display(),
sid_string(security.owner),
sid_string(user.sid())
);
}
let mut control = 0;
let mut revision = 0;
assert_ne!(
unsafe { GetSecurityDescriptorControl(security.descriptor, &mut control, &mut revision) },
0
);
assert_eq!(
control & SE_DACL_PROTECTED != 0,
protected,
"DACL protection"
);
assert!(!security.dacl.is_null(), "null DACL grants Everyone access");
assert_eq!(
unsafe { (*security.dacl).AceCount },
1,
"exactly one user ACE"
);
let mut raw = null_mut();
assert_ne!(unsafe { GetAce(security.dacl, 0, &mut raw) }, 0);
let ace = unsafe { &*raw.cast::<ACCESS_ALLOWED_ACE>() };
assert_eq!(ace.Header.AceType, 0, "ACCESS_ALLOWED ACE");
assert!(usize::from(ace.Header.AceSize) >= size_of::<ACCESS_ALLOWED_ACE>());
let sid: PSID = std::ptr::addr_of!(ace.SidStart).cast_mut().cast();
assert_ne!(
unsafe { EqualSid(sid, user.sid()) },
0,
"current-user trustee"
);
assert_eq!(ace.Mask, FILE_ALL_ACCESS, "full control for user");
let expected = if directory {
OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE
} else {
0
} | if protected { 0 } else { INHERITED_ACE };
assert_eq!(
u32::from(ace.Header.AceFlags),
expected,
"exact inheritance flags"
);
}
struct Descriptor(PSECURITY_DESCRIPTOR);
impl Drop for Descriptor {
fn drop(&mut self) {
unsafe { LocalFree(self.0) };
}
}
pub fn apply_sddl(path: &Path, sddl: &str, owner: bool) -> io::Result<()> {
let name = wide(path)?;
let sddl: Vec<u16> = sddl.encode_utf16().chain(Some(0)).collect();
let mut raw = null_mut();
if unsafe {
ConvertStringSecurityDescriptorToSecurityDescriptorW(
sddl.as_ptr(),
SDDL_REVISION_1,
&mut raw,
null_mut(),
)
} == 0
{
return Err(io::Error::last_os_error());
}
let descriptor = Descriptor(raw);
let mut acl: *mut ACL = null_mut();
let mut owner_sid = null_mut();
let mut defaulted = 0;
let flags = if owner {
enable_restore_privilege()?;
if unsafe { GetSecurityDescriptorOwner(descriptor.0, &mut owner_sid, &mut defaulted) } == 0
{
return Err(io::Error::last_os_error());
}
OWNER_SECURITY_INFORMATION
} else {
let mut present = 0;
if unsafe {
GetSecurityDescriptorDacl(descriptor.0, &mut present, &mut acl, &mut defaulted)
} == 0
{
return Err(io::Error::last_os_error());
}
assert_ne!(present, 0, "fixture must specify a DACL");
DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION
};
let error = unsafe {
SetNamedSecurityInfoW(
name.as_ptr(),
SE_FILE_OBJECT,
flags,
owner_sid,
null_mut(),
acl,
null_mut(),
)
};
if error != 0 {
return Err(io::Error::from_raw_os_error(error as i32));
}
Ok(())
}
pub fn grant_everyone(path: &Path) {
apply_sddl(path, "D:P(A;OICI;FA;;;WD)", false).unwrap();
}
pub fn grant_everyone_read(path: &Path) {
apply_sddl(path, "D:P(A;;FR;;;WD)", false).unwrap();
}
fn sid_string(sid: PSID) -> String {
let mut text: *mut u16 = null_mut();
if unsafe { ConvertSidToStringSidW(sid, &mut text) } == 0 || text.is_null() {
return "<unreadable SID>".to_owned();
}
let rendered = unsafe {
let len = (0..).take_while(|&i| *text.add(i) != 0).count();
String::from_utf16_lossy(std::slice::from_raw_parts(text, len))
};
unsafe { LocalFree(text.cast()) };
rendered
}