diff --git a/Cargo.lock b/Cargo.lock index 4236d4391bc..8d1a2977551 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1536,6 +1536,7 @@ name = "git-sec" version = "0.1.1" dependencies = [ "bitflags", + "dirs", "libc", "serde", "tempfile", diff --git a/git-discover/tests/upwards/mod.rs b/git-discover/tests/upwards/mod.rs index e5c5f6dd434..8fcac06b39d 100644 --- a/git-discover/tests/upwards/mod.rs +++ b/git-discover/tests/upwards/mod.rs @@ -3,18 +3,7 @@ use std::path::PathBuf; use git_discover::repository::Kind; fn expected_trust() -> git_sec::Trust { - #[cfg(not(windows))] - { - git_sec::Trust::Full - } - #[cfg(windows)] - { - if is_ci::cached() { - git_sec::Trust::Reduced - } else { - git_sec::Trust::Full - } - } + git_sec::Trust::Full } mod ceiling_dirs; diff --git a/git-sec/Cargo.toml b/git-sec/Cargo.toml index 21ca6df3cb6..cee18b73ffb 100644 --- a/git-sec/Cargo.toml +++ b/git-sec/Cargo.toml @@ -26,6 +26,7 @@ thiserror = { version = "1.0.26", optional = true } libc = "0.2.123" [target.'cfg(windows)'.dependencies] +dirs = "4" windows = { version = "0.36.0", features = [ "alloc", "Win32_Foundation", "Win32_Security_Authorization", diff --git a/git-sec/src/identity.rs b/git-sec/src/identity.rs index bfb50380c24..e7a6952bbf2 100644 --- a/git-sec/src/identity.rs +++ b/git-sec/src/identity.rs @@ -51,15 +51,16 @@ mod impl_ { use windows::{ core::{Error, PCWSTR}, Win32::{ - Foundation::{HANDLE, PSID}, + Foundation::{CloseHandle, BOOL, HANDLE, PSID}, Security::{ Authorization::{GetNamedSecurityInfoW, SE_FILE_OBJECT}, - EqualSid, GetTokenInformation, TokenOwner, OWNER_SECURITY_INFORMATION, PSECURITY_DESCRIPTOR, - TOKEN_OWNER, TOKEN_QUERY, + CheckTokenMembership, EqualSid, GetTokenInformation, IsWellKnownSid, TokenOwner, + WinBuiltinAdministratorsSid, OWNER_SECURITY_INFORMATION, PSECURITY_DESCRIPTOR, TOKEN_OWNER, + TOKEN_QUERY, }, System::{ Memory::LocalFree, - Threading::{GetCurrentProcess, OpenProcessToken}, + Threading::{GetCurrentProcess, GetCurrentThread, OpenProcessToken, OpenThreadToken}, }, }, }; @@ -68,6 +69,13 @@ mod impl_ { let mut is_owned = false; let path = path.as_ref(); + // 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 Some(path) == dirs::home_dir().as_deref() { + return Ok(true); + } + #[allow(unsafe_code)] unsafe { let mut folder_owner = PSID::default(); @@ -86,7 +94,10 @@ mod impl_ { // Workaround for https://github.com/microsoft/win32metadata/issues/884 if result.is_ok() { let mut token = HANDLE::default(); - OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut token).ok()?; + // 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::::new(); @@ -106,6 +117,16 @@ mod impl_ { 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: {}", @@ -120,6 +141,7 @@ mod impl_ { ) .into(); } + CloseHandle(token); } else { err_msg = format!( "Couldn't get security information for path '{}' with err {}", diff --git a/git-sec/tests/identity/mod.rs b/git-sec/tests/identity/mod.rs index 99fc3f71427..7d36d031998 100644 --- a/git-sec/tests/identity/mod.rs +++ b/git-sec/tests/identity/mod.rs @@ -7,3 +7,11 @@ fn is_path_owned_by_current_user() -> crate::Result { assert!(git_sec::identity::is_path_owned_by_current_user(dir.path())?); Ok(()) } + +#[test] +#[cfg(windows)] +fn windows_home() -> crate::Result { + let home = dirs::home_dir().expect("home dir is available"); + assert!(git_sec::identity::is_path_owned_by_current_user(home)?); + Ok(()) +}