use std::path::Path;
#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Account {
pub username: String,
pub password: String,
}
pub fn is_path_owned_by_current_user(path: &Path) -> std::io::Result<bool> {
impl_::is_path_owned_by_current_user(path)
}
#[cfg(target_os = "wasi")]
mod impl_ {
pub fn is_path_owned_by_current_user(_path: &std::path::Path) -> std::io::Result<bool> {
Ok(true)
}
}
#[cfg(all(not(windows), not(target_os = "wasi")))]
mod impl_ {
use std::path::Path;
pub fn is_path_owned_by_current_user(path: &Path) -> std::io::Result<bool> {
fn owner_from_path(path: &Path) -> std::io::Result<u32> {
use std::os::unix::fs::MetadataExt;
let meta = std::fs::symlink_metadata(path)?;
Ok(meta.uid())
}
fn owner_of_current_process() -> std::io::Result<u32> {
#[allow(unsafe_code)]
let uid = unsafe { libc::geteuid() };
Ok(uid)
}
use std::str::FromStr;
let owner_of_path = owner_from_path(path)?;
let owner_of_process = owner_of_current_process()?;
if owner_of_path == owner_of_process {
Ok(true)
} else if let Some(sudo_uid) =
std::env::var_os("SUDO_UID").and_then(|val| val.to_str().and_then(|val_str| u32::from_str(val_str).ok()))
{
Ok(owner_of_path == sudo_uid)
} else {
Ok(false)
}
}
}
#[cfg(windows)]
mod impl_ {
use std::{
io,
mem::MaybeUninit,
os::windows::io::{FromRawHandle as _, OwnedHandle},
path::Path,
ptr,
};
macro_rules! error {
($msg:expr) => {{
let inner = io::Error::last_os_error();
error!(inner, $msg);
}};
($inner:expr, $msg:expr) => {{
return Err(io::Error::new($inner.kind(), $msg));
}};
}
pub fn is_path_owned_by_current_user(path: &Path) -> io::Result<bool> {
use windows_sys::Win32::{
Foundation::{GetLastError, LocalFree, ERROR_INSUFFICIENT_BUFFER, ERROR_SUCCESS},
Security::{
Authorization::{GetNamedSecurityInfoW, SE_FILE_OBJECT},
CheckTokenMembership, EqualSid, GetTokenInformation, IsWellKnownSid, TokenOwner,
WinBuiltinAdministratorsSid, OWNER_SECURITY_INFORMATION, PSECURITY_DESCRIPTOR, TOKEN_OWNER,
TOKEN_QUERY,
},
System::Threading::{GetCurrentProcess, GetCurrentThread, OpenProcessToken, OpenThreadToken},
};
if !path.exists() {
return Err(io::Error::new(
io::ErrorKind::NotFound,
format!("{path:?} does not exist."),
));
}
if gix_path::realpath(path).ok() == gix_path::env::home_dir() {
return Ok(true);
}
#[allow(unsafe_code)]
unsafe {
let (folder_owner, descriptor) = {
let mut folder_owner = MaybeUninit::uninit();
let mut pdescriptor = MaybeUninit::uninit();
let result = GetNamedSecurityInfoW(
to_wide_path(path).as_ptr(),
SE_FILE_OBJECT,
OWNER_SECURITY_INFORMATION,
folder_owner.as_mut_ptr(),
ptr::null_mut(),
ptr::null_mut(),
ptr::null_mut(),
pdescriptor.as_mut_ptr(),
);
if result != ERROR_SUCCESS {
let inner = io::Error::from_raw_os_error(result as _);
error!(
inner,
format!(
"Couldn't get security information for path '{}' with err {inner}",
path.display()
)
);
}
(folder_owner.assume_init(), pdescriptor.assume_init())
};
struct Descriptor(PSECURITY_DESCRIPTOR);
impl Drop for Descriptor {
fn drop(&mut self) {
#[allow(unsafe_code)]
unsafe {
LocalFree(self.0 as _);
}
}
}
let _descriptor = Descriptor(descriptor);
let token = {
let mut token = MaybeUninit::uninit();
if OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, 1, token.as_mut_ptr()) == 0
&& OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, token.as_mut_ptr()) == 0
{
error!("Couldn't acquire thread or process token");
}
token.assume_init()
};
let _owned_token = OwnedHandle::from_raw_handle(token as _);
let buf = 'token_buf: {
let mut buffer_size = 36;
let mut heap_buf = vec![0; 36];
loop {
if GetTokenInformation(
token,
TokenOwner,
heap_buf.as_mut_ptr().cast(),
heap_buf.len() as _,
&mut buffer_size,
) != 0
{
break 'token_buf heap_buf;
}
if GetLastError() != ERROR_INSUFFICIENT_BUFFER {
error!("Couldn't acquire token ownership");
}
heap_buf.resize(buffer_size as _, 0);
}
};
let token_owner = (*buf.as_ptr().cast::<TOKEN_OWNER>()).Owner;
if EqualSid(folder_owner, token_owner) != 0 {
return Ok(true);
}
if IsWellKnownSid(token_owner, WinBuiltinAdministratorsSid) == 0 {
return Ok(false);
}
let mut is_member = 0;
if CheckTokenMembership(0, token_owner, &mut is_member) == 0 {
error!("Couldn't check if user is an administrator");
}
Ok(is_member != 0)
}
}
fn to_wide_path(path: impl AsRef<Path>) -> Vec<u16> {
use std::os::windows::ffi::OsStrExt;
let mut wide_path: Vec<_> = path.as_ref().as_os_str().encode_wide().collect();
wide_path.push(0);
wide_path
}
}