Skip to content

Commit

Permalink
Replace windows with windows-sys
Browse files Browse the repository at this point in the history
  • Loading branch information
Jake-Shadle committed Jan 18, 2024
1 parent 5d176fc commit 3ce6c38
Show file tree
Hide file tree
Showing 3 changed files with 129 additions and 115 deletions.
23 changes: 2 additions & 21 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 7 additions & 4 deletions gix-sec/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,13 @@ doctest = false

[features]
## Data structures implement `serde::Serialize` and `serde::Deserialize`.
serde = [ "dep:serde", "bitflags/serde" ]
serde = ["dep:serde", "bitflags/serde"]

[dependencies]
serde = { version = "1.0.114", optional = true, default-features = false, features = ["std", "derive"] }
serde = { version = "1.0.114", optional = true, default-features = false, features = [
"std",
"derive",
] }
bitflags = "2"

document-features = { version = "0.2.1", optional = true }
Expand All @@ -27,12 +30,12 @@ libc = "0.2.123"

[target.'cfg(windows)'.dependencies]
gix-path = { version = "^0.10.3", path = "../gix-path" }
windows = { version = "0.52.0", features = [
windows-sys = { version = "0.52.0", features = [
"Win32_Foundation",
"Win32_Security_Authorization",
"Win32_Storage_FileSystem",
"Win32_System_Memory",
"Win32_System_Threading"
"Win32_System_Threading",
] }

[dev-dependencies]
Expand Down
210 changes: 120 additions & 90 deletions gix-sec/src/identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,34 +52,40 @@ mod impl_ {

#[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())
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) -> std::io::Result<bool> {
use windows::{
core::{Error, PCWSTR},
Win32::{
Foundation::{CloseHandle, LocalFree, BOOL, HANDLE, HLOCAL, PSID},
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},
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},
};

let mut err_msg = None;
let mut is_owned = false;

if !path.exists() {
return Err(std::io::Error::new(
std::io::ErrorKind::NotFound,
format!("{:?} does not exist.", path),
return Err(io::Error::new(
io::ErrorKind::NotFound,
format!("{path:?} does not exist."),
));
}

Expand All @@ -92,82 +98,106 @@ mod impl_ {

#[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();
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)]
// SAFETY: syscall only invoked if we have a valid descriptor
unsafe {
LocalFree(self.0 as _);
}
}
}

let _descriptor = Descriptor(descriptor);

let token = {
let mut token = MaybeUninit::uninit();

// Use the current thread token if possible, otherwise open the process token
OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, true, &mut token)
.or_else(|_| OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut token))?;

let mut buffer_size = 0;
let mut buffer = Vec::<u8>::new();
GetTokenInformation(token, TokenOwner, None, 0, &mut buffer_size).ok();
if buffer_size != 0 {
buffer.resize(buffer_size as usize, 0);
match GetTokenInformation(
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,
Some(buffer.as_mut_ptr() as *mut std::ffi::c_void),
buffer_size,
heap_buf.as_mut_ptr().cast(),
heap_buf.len() as _,
&mut buffer_size,
) {
Ok(()) => {
let token_owner = buffer.as_ptr() as *const TOKEN_OWNER;
let token_owner = (*token_owner).Owner;

is_owned = EqualSid(folder_owner, token_owner).is_ok();

// 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: reuse the handle
match CheckTokenMembership(HANDLE::default(), token_owner, &mut is_member) {
Err(e) => {
err_msg = Some(format!("Couldn't check if user is an administrator: {}", e))
}
Ok(()) => is_owned = is_member.as_bool(),
}
}
}
Err(err) => {
err_msg =
format!("Couldn't get actual token information for current process with err: {err}",)
.into();
}
) != 0
{
break 'token_buf heap_buf;
}
} else {
err_msg = format!(
"Couldn't get token information size info for current process with err: {}",
Error::from_win32()
)
.into();

if GetLastError() != ERROR_INSUFFICIENT_BUFFER {
error!("Couldn't acquire token ownership");
}

heap_buf.resize(buffer_size as _, 0);
}
CloseHandle(token)?;
} else {
err_msg = format!(
"Couldn't get security information for path '{}' with err {}",
path.display(),
Error::from_win32()
)
.into();
};

let token_owner = (*buf.as_ptr().cast::<TOKEN_OWNER>()).Owner;

// If the current user is the owner of the parent folder then they also
// own this file
if EqualSid(folder_owner, token_owner) != 0 {
return Ok(true);
}

// Admin-group owned folders are considered owned by the current user, if they are in the admin group
if IsWellKnownSid(token_owner, WinBuiltinAdministratorsSid) != 0 {
return Ok(false);
}
LocalFree(HLOCAL(pdescriptor.0)).ok();
}

err_msg.map(|msg| Err(err(msg))).unwrap_or(Ok(is_owned))
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> {
Expand Down

0 comments on commit 3ce6c38

Please sign in to comment.