summaryrefslogtreecommitdiffstats
path: root/vendor/gix-sec/src/identity.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gix-sec/src/identity.rs')
-rw-r--r--vendor/gix-sec/src/identity.rs183
1 files changed, 183 insertions, 0 deletions
diff --git a/vendor/gix-sec/src/identity.rs b/vendor/gix-sec/src/identity.rs
new file mode 100644
index 000000000..7c3df38bd
--- /dev/null
+++ b/vendor/gix-sec/src/identity.rs
@@ -0,0 +1,183 @@
+use std::path::Path;
+
+#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone)]
+#[cfg_attr(feature = "serde1", derive(serde::Serialize, serde::Deserialize))]
+/// An account based identity
+pub 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 gix_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
+ }
+}