Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replace windows with windows-sys #1245

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ jobs:
- uses: taiki-e/install-action@v1
with:
tool: nextest
version: 0.9
- name: "Test (nextest)"
run: cargo nextest run --all --no-fail-fast

Expand Down
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
Loading