use std::fs::File; use std::io; use std::io::Write; use std::path::Path; #[cfg(windows)] use winapi::um::winnt::{FILE_GENERIC_READ, FILE_GENERIC_WRITE, STANDARD_RIGHTS_ALL}; /// This is the security identifier in Windows for the owner of a file. See: /// - https://docs.microsoft.com/en-us/troubleshoot/windows-server/identity/security-identifiers-in-windows#well-known-sids-all-versions-of-windows #[cfg(windows)] const OWNER_SID_STR: &str = "S-1-3-4"; /// We don't need any of the `AceFlags` listed here: /// - https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-ace_header #[cfg(windows)] const OWNER_ACL_ENTRY_FLAGS: u8 = 0; /// Generic Rights: /// - https://docs.microsoft.com/en-us/windows/win32/fileio/file-security-and-access-rights /// Individual Read/Write/Execute Permissions (referenced in generic rights link): /// - https://docs.microsoft.com/en-us/windows/win32/wmisdk/file-and-directory-access-rights-constants /// STANDARD_RIGHTS_ALL /// - https://docs.microsoft.com/en-us/windows/win32/secauthz/access-mask #[cfg(windows)] const OWNER_ACL_ENTRY_MASK: u32 = FILE_GENERIC_READ | FILE_GENERIC_WRITE | STANDARD_RIGHTS_ALL; #[derive(Debug)] pub enum Error { /// The file could not be created UnableToCreateFile(io::Error), /// The file could not be copied UnableToCopyFile(io::Error), /// The file could not be opened UnableToOpenFile(io::Error), /// The file could not be renamed UnableToRenameFile(io::Error), /// Failed to set permissions UnableToSetPermissions(io::Error), /// Failed to retrieve file metadata UnableToRetrieveMetadata(io::Error), /// Failed to write bytes to file UnableToWriteFile(io::Error), /// Failed to obtain file path UnableToObtainFilePath, /// Failed to convert string to SID UnableToConvertSID(u32), /// Failed to retrieve ACL for file UnableToRetrieveACL(u32), /// Failed to enumerate ACL entries UnableToEnumerateACLEntries(u32), /// Failed to add new ACL entry UnableToAddACLEntry(String), /// Failed to remove ACL entry UnableToRemoveACLEntry(String), } /// Creates a file with `600 (-rw-------)` permissions and writes the specified bytes to file. pub fn create_with_600_perms>(path: P, bytes: &[u8]) -> Result<(), Error> { let path = path.as_ref(); let mut file = File::create(&path).map_err(Error::UnableToCreateFile)?; #[cfg(unix)] { use std::os::unix::fs::PermissionsExt; let mut perm = file .metadata() .map_err(Error::UnableToRetrieveMetadata)? .permissions(); perm.set_mode(0o600); file.set_permissions(perm) .map_err(Error::UnableToSetPermissions)?; } file.write_all(bytes).map_err(Error::UnableToWriteFile)?; #[cfg(windows)] { restrict_file_permissions(path)?; } Ok(()) } pub fn restrict_file_permissions>(path: P) -> Result<(), Error> { #[cfg(unix)] { use std::os::unix::fs::PermissionsExt; let file = File::open(path.as_ref()).map_err(Error::UnableToOpenFile)?; let mut perm = file .metadata() .map_err(Error::UnableToRetrieveMetadata)? .permissions(); perm.set_mode(0o600); file.set_permissions(perm) .map_err(Error::UnableToSetPermissions)?; } #[cfg(windows)] { use winapi::um::winnt::PSID; use windows_acl::acl::{AceType, ACL}; use windows_acl::helper::sid_to_string; let path_str = path .as_ref() .to_str() .ok_or(Error::UnableToObtainFilePath)?; let mut acl = ACL::from_file_path(path_str, false).map_err(Error::UnableToRetrieveACL)?; let owner_sid = windows_acl::helper::string_to_sid(OWNER_SID_STR).map_err(Error::UnableToConvertSID)?; let entries = acl.all().map_err(Error::UnableToEnumerateACLEntries)?; // add single entry for file owner acl.add_entry( owner_sid.as_ptr() as PSID, AceType::AccessAllow, OWNER_ACL_ENTRY_FLAGS, OWNER_ACL_ENTRY_MASK, ) .map_err(|code| { Error::UnableToAddACLEntry(format!( "Failed to add ACL entry for SID {} error={}", OWNER_SID_STR, code )) })?; // remove all AccessAllow entries from the file that aren't the owner_sid for entry in &entries { if let Some(ref entry_sid) = entry.sid { let entry_sid_str = sid_to_string(entry_sid.as_ptr() as PSID) .unwrap_or_else(|_| "BadFormat".to_string()); if entry_sid_str != OWNER_SID_STR { acl.remove(entry_sid.as_ptr() as PSID, Some(AceType::AccessAllow), None) .map_err(|_| { Error::UnableToRemoveACLEntry(format!( "Failed to remove ACL entry for SID {}", entry_sid_str )) })?; } } } } Ok(()) }