1use std::io;
7use std::path::Path;
8
9pub fn restrict_to_owner(path: &Path) -> io::Result<()> {
14 #[cfg(unix)]
15 {
16 use std::os::unix::fs::PermissionsExt;
17 std::fs::set_permissions(path, std::fs::Permissions::from_mode(0o700))
18 }
19 #[cfg(windows)]
20 {
21 windows_acl_impl::apply(path)
22 }
23 #[cfg(not(any(unix, windows)))]
24 {
25 let _ = path;
26 Err(io::Error::other(
27 "owner-only lockdown not implemented on this platform",
28 ))
29 }
30}
31
32#[cfg(windows)]
33mod windows_acl_impl {
34 use std::io;
35 use std::path::Path;
36
37 use windows::core::{Error as WinError, PWSTR};
38 use windows::Win32::Foundation::LocalFree;
39 use windows::Win32::Foundation::{CloseHandle, ERROR_SUCCESS, HANDLE, HLOCAL};
40 use windows::Win32::Security::Authorization::{
41 SetEntriesInAclW, SetNamedSecurityInfoW, EXPLICIT_ACCESS_W, NO_MULTIPLE_TRUSTEE,
42 SET_ACCESS, SE_FILE_OBJECT, TRUSTEE_IS_SID, TRUSTEE_IS_USER, TRUSTEE_W,
43 };
44 use windows::Win32::Security::{
45 GetTokenInformation, TokenUser, ACL, DACL_SECURITY_INFORMATION, NO_INHERITANCE,
46 PROTECTED_DACL_SECURITY_INFORMATION, TOKEN_QUERY, TOKEN_USER,
47 };
48 use windows::Win32::System::Threading::{GetCurrentProcess, OpenProcessToken};
49
50 const FILE_ALL_ACCESS: u32 = 0x1F01FF;
54
55 pub fn apply(path: &Path) -> io::Result<()> {
59 let mut token = HANDLE::default();
61 unsafe { OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut token) }
62 .map_err(win_err("OpenProcessToken"))?;
63 let _token_guard = HandleGuard(token);
64
65 let mut needed: u32 = 0;
67 if let Err(e) = unsafe { GetTokenInformation(token, TokenUser, None, 0, &mut needed) } {
70 if needed == 0 {
71 return Err(win_err("GetTokenInformation (size probe)")(e));
72 }
73 }
74 let mut buf = vec![0u8; needed as usize];
75 let mut written: u32 = 0;
76 unsafe {
77 GetTokenInformation(
78 token,
79 TokenUser,
80 Some(buf.as_mut_ptr() as *mut core::ffi::c_void),
81 needed,
82 &mut written,
83 )
84 }
85 .map_err(win_err("GetTokenInformation"))?;
86 let token_user: TOKEN_USER =
90 unsafe { std::ptr::read_unaligned(buf.as_ptr() as *const TOKEN_USER) };
91 let sid = token_user.User.Sid;
92
93 let trustee = TRUSTEE_W {
96 pMultipleTrustee: std::ptr::null_mut(),
97 MultipleTrusteeOperation: NO_MULTIPLE_TRUSTEE,
98 TrusteeForm: TRUSTEE_IS_SID,
99 TrusteeType: TRUSTEE_IS_USER,
100 ptstrName: PWSTR(sid.0 as *mut u16),
101 };
102
103 let ea = EXPLICIT_ACCESS_W {
104 grfAccessPermissions: FILE_ALL_ACCESS,
105 grfAccessMode: SET_ACCESS,
106 grfInheritance: NO_INHERITANCE,
107 Trustee: trustee,
108 };
109
110 let mut new_acl: *mut ACL = std::ptr::null_mut();
112 let rc = unsafe { SetEntriesInAclW(Some(&[ea]), None, &mut new_acl) };
113 if rc != ERROR_SUCCESS {
114 return Err(io::Error::other(format!("SetEntriesInAclW failed: {rc:?}")));
115 }
116 let _acl_guard = LocalFreeGuard(new_acl as *mut core::ffi::c_void);
117
118 let wide_path = to_wide_null(path);
121 let rc = unsafe {
122 SetNamedSecurityInfoW(
123 PWSTR(wide_path.as_ptr() as *mut u16),
124 SE_FILE_OBJECT,
125 DACL_SECURITY_INFORMATION | PROTECTED_DACL_SECURITY_INFORMATION,
126 None,
127 None,
128 Some(new_acl),
129 None,
130 )
131 };
132 if rc != ERROR_SUCCESS {
133 return Err(io::Error::other(format!(
134 "SetNamedSecurityInfoW failed: {rc:?}"
135 )));
136 }
137
138 Ok(())
139 }
140
141 fn win_err(ctx: &'static str) -> impl Fn(WinError) -> io::Error {
142 move |e| io::Error::other(format!("{ctx}: {e}"))
143 }
144
145 fn to_wide_null(path: &Path) -> Vec<u16> {
146 use std::os::windows::ffi::OsStrExt;
147 path.as_os_str()
148 .encode_wide()
149 .chain(std::iter::once(0))
150 .collect()
151 }
152
153 struct HandleGuard(HANDLE);
155 impl Drop for HandleGuard {
156 fn drop(&mut self) {
157 unsafe {
159 let _ = CloseHandle(self.0);
160 }
161 }
162 }
163
164 struct LocalFreeGuard(*mut core::ffi::c_void);
167 impl Drop for LocalFreeGuard {
168 fn drop(&mut self) {
169 if !self.0.is_null() {
170 unsafe {
177 let _ = LocalFree(Some(HLOCAL(self.0)));
178 }
179 }
180 }
181 }
182}