Sebastian Thielupgrade `windows` to v0.43ec49e3d3 years agoHistory
183 lines · 7.0 KB · Rust
RawDownload
use std::path::Path; #[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]/// An account based identitypub struct Account {    /// The user's name    pub username: String,    /// The user's password    pub password: String,} /// Returns true if the given `path` is owned by the user who is executing the current process.////// Note that this method is very specific to avoid having to deal with any operating system types.pub fn is_path_owned_by_current_user(path: impl AsRef<Path>) -> std::io::Result<bool> {    impl_::is_path_owned_by_current_user(path)} #[cfg(not(windows))]mod impl_ {    use std::path::Path;     pub fn is_path_owned_by_current_user(path: impl AsRef<Path>) -> std::io::Result<bool> {        fn owner_from_path(path: impl AsRef<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> {            // SAFETY: there is no documented possibility for failure            #[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::path::Path;     fn err(msg: impl Into<String>) -> std::io::Error {        std::io::Error::new(std::io::ErrorKind::Other, msg.into())    }     pub fn is_path_owned_by_current_user(path: impl AsRef<Path>) -> std::io::Result<bool> {        use windows::{            core::{Error, PCWSTR},            Win32::{                Foundation::{CloseHandle, BOOL, HANDLE, PSID},                Security::{                    Authorization::{GetNamedSecurityInfoW, SE_FILE_OBJECT},                    CheckTokenMembership, EqualSid, GetTokenInformation, IsWellKnownSid, TokenOwner,                    WinBuiltinAdministratorsSid, OWNER_SECURITY_INFORMATION, PSECURITY_DESCRIPTOR, TOKEN_OWNER,                    TOKEN_QUERY,                },                System::{                    Memory::LocalFree,                    Threading::{GetCurrentProcess, GetCurrentThread, OpenProcessToken, OpenThreadToken},                },            },        };         let mut err_msg = None;        let mut is_owned = false;        let path = path.as_ref();         if !path.exists() {            return Err(std::io::Error::new(                std::io::ErrorKind::NotFound,                format!("{:?} does not exist.", path),            ));        }         // Home is not actually owned by the corresponding user        // but it can be considered de-facto owned by the user        // Ignore errors here and just do the regular checks below        if git_path::realpath(path).ok() == dirs::home_dir() {            return Ok(true);        }         #[allow(unsafe_code)]        unsafe {            let mut folder_owner = PSID::default();            let mut pdescriptor = PSECURITY_DESCRIPTOR::default();            let result = GetNamedSecurityInfoW(                PCWSTR(to_wide_path(path).as_ptr()),                SE_FILE_OBJECT,                OWNER_SECURITY_INFORMATION,                Some(&mut folder_owner),                None,                None,                None,                &mut pdescriptor,            );             // Workaround for https://github.com/microsoft/win32metadata/issues/884            if result.is_ok() {                let mut token = HANDLE::default();                // Use the current thread token if possible, otherwise open the process token                OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, true, &mut token)                    .ok()                    .or_else(|_| OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut token).ok())?;                 let mut buffer_size = 0;                let mut buffer = Vec::<u8>::new();                GetTokenInformation(token, TokenOwner, None, 0, &mut buffer_size);                if buffer_size != 0 {                    buffer.resize(buffer_size as usize, 0);                    if GetTokenInformation(                        token,                        TokenOwner,                        Some(buffer.as_mut_ptr() as *mut std::ffi::c_void),                        buffer_size,                        &mut buffer_size,                    )                    .as_bool()                    {                        let token_owner = buffer.as_ptr() as *const TOKEN_OWNER;                        let token_owner = (*token_owner).Owner;                         is_owned = EqualSid(folder_owner, token_owner).as_bool();                         // Admin-group owned folders are considered owned by the current user, if they are in the admin group                        if !is_owned && IsWellKnownSid(token_owner, WinBuiltinAdministratorsSid).as_bool() {                            let mut is_member = BOOL::default();                            // TODO: re-use the handle                            match CheckTokenMembership(HANDLE::default(), token_owner, &mut is_member).ok() {                                Err(e) => err_msg = Some(format!("Couldn't check if user is an administrator: {}", e)),                                Ok(()) => is_owned = is_member.as_bool(),                            }                        }                    } else {                        err_msg = format!(                            "Couldn't get actual token information for current process with err: {}",                            Error::from_win32()                        )                        .into();                    }                } else {                    err_msg = format!(                        "Couldn't get token information size info for current process with err: {}",                        Error::from_win32()                    )                    .into();                }                CloseHandle(token);            } else {                err_msg = format!(                    "Couldn't get security information for path '{}' with err {}",                    path.display(),                    Error::from_win32()                )                .into();            }            LocalFree(pdescriptor.0 as isize);        }         err_msg.map(|msg| Err(err(msg))).unwrap_or(Ok(is_owned))    }     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    }}