mirror of
https://github.com/Llewellynvdm/starship.git
synced 2024-11-28 07:46:28 +00:00
refactor(windows): replace winapi with windows (#3690)
This commit is contained in:
parent
6def45b751
commit
6c2b51a621
21
Cargo.lock
generated
21
Cargo.lock
generated
@ -1947,7 +1947,7 @@ dependencies = [
|
|||||||
"urlencoding",
|
"urlencoding",
|
||||||
"versions",
|
"versions",
|
||||||
"which",
|
"which",
|
||||||
"winapi",
|
"windows 0.35.0",
|
||||||
"winres",
|
"winres",
|
||||||
"yaml-rust",
|
"yaml-rust",
|
||||||
]
|
]
|
||||||
@ -2320,9 +2320,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "vtparse"
|
name = "vtparse"
|
||||||
version = "0.6.0"
|
version = "0.6.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "8f41c9314c4dde1f43dd0c46c67bb5ae73850ce11eebaf7d8b912e178bda5401"
|
checksum = "36ce903972602c84dd48f488cdce39edcba03a93b7ca67b146ae862568f48c5c"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"utf8parse",
|
"utf8parse",
|
||||||
]
|
]
|
||||||
@ -2408,6 +2408,19 @@ dependencies = [
|
|||||||
"windows_x86_64_msvc 0.24.0",
|
"windows_x86_64_msvc 0.24.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows"
|
||||||
|
version = "0.35.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "08746b4b7ac95f708b3cccceb97b7f9a21a8916dd47fc99b0e6aaf7208f26fd7"
|
||||||
|
dependencies = [
|
||||||
|
"windows_aarch64_msvc",
|
||||||
|
"windows_i686_gnu 0.35.0",
|
||||||
|
"windows_i686_msvc 0.35.0",
|
||||||
|
"windows_x86_64_gnu 0.35.0",
|
||||||
|
"windows_x86_64_msvc 0.35.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-sys"
|
name = "windows-sys"
|
||||||
version = "0.35.0"
|
version = "0.35.0"
|
||||||
@ -2491,7 +2504,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "007a0353840b23e0c6dc73e5b962ff58ed7f6bc9ceff3ce7fe6fbad8d496edf4"
|
checksum = "007a0353840b23e0c6dc73e5b962ff58ed7f6bc9ceff3ce7fe6fbad8d496edf4"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"strum",
|
"strum",
|
||||||
"windows",
|
"windows 0.24.0",
|
||||||
"xml-rs",
|
"xml-rs",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
11
Cargo.toml
11
Cargo.toml
@ -92,9 +92,18 @@ optional = true
|
|||||||
features = ["preserve_order", "indexmap"]
|
features = ["preserve_order", "indexmap"]
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
winapi = { version = "0.3.9", features = ["winuser", "securitybaseapi", "processthreadsapi", "handleapi", "impl-default"] }
|
|
||||||
deelevate = "0.2.0"
|
deelevate = "0.2.0"
|
||||||
|
|
||||||
|
[target.'cfg(windows)'.dependencies.windows]
|
||||||
|
version = "0.35.0"
|
||||||
|
features = [
|
||||||
|
"Win32_Foundation",
|
||||||
|
"Win32_UI_Shell",
|
||||||
|
"Win32_Security",
|
||||||
|
"Win32_System_Threading",
|
||||||
|
"Win32_Storage_FileSystem",
|
||||||
|
]
|
||||||
|
|
||||||
[target.'cfg(not(windows))'.dependencies]
|
[target.'cfg(not(windows))'.dependencies]
|
||||||
nix = "0.23.1"
|
nix = "0.23.1"
|
||||||
|
|
||||||
|
@ -13,8 +13,9 @@ use std::path::Path;
|
|||||||
/// 2a) (not implemented on macOS) one of the supplementary groups of the current user is the
|
/// 2a) (not implemented on macOS) one of the supplementary groups of the current user is the
|
||||||
/// directory group owner and whether it has write access
|
/// directory group owner and whether it has write access
|
||||||
/// 3) 'others' part of the access mask has the write access
|
/// 3) 'others' part of the access mask has the write access
|
||||||
pub fn is_write_allowed(folder_path: &Path) -> Result<bool, &'static str> {
|
pub fn is_write_allowed(folder_path: &Path) -> Result<bool, String> {
|
||||||
let meta = fs::metadata(folder_path).map_err(|_| "Unable to stat() directory")?;
|
let meta =
|
||||||
|
fs::metadata(folder_path).map_err(|e| format!("Unable to stat() directory: {e:?}"))?;
|
||||||
let perms = meta.permissions().mode();
|
let perms = meta.permissions().mode();
|
||||||
|
|
||||||
let euid = Uid::effective();
|
let euid = Uid::effective();
|
||||||
@ -54,9 +55,9 @@ mod tests {
|
|||||||
#[ignore]
|
#[ignore]
|
||||||
fn read_only_test() {
|
fn read_only_test() {
|
||||||
assert_eq!(is_write_allowed(Path::new("/etc")), Ok(false));
|
assert_eq!(is_write_allowed(Path::new("/etc")), Ok(false));
|
||||||
assert_eq!(
|
assert!(match is_write_allowed(Path::new("/i_dont_exist")) {
|
||||||
is_write_allowed(Path::new("/i_dont_exist")),
|
Ok(_) => false,
|
||||||
Err("Unable to stat() directory")
|
Err(e) => e.starts_with("Unable to stat() directory"),
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,29 +1,32 @@
|
|||||||
extern crate winapi;
|
use std::{ffi::c_void, mem, os::windows::ffi::OsStrExt, path::Path};
|
||||||
|
|
||||||
use std::mem;
|
use windows::{
|
||||||
use std::os::windows::ffi::OsStrExt;
|
core::PCWSTR,
|
||||||
use std::path::Path;
|
Win32::{
|
||||||
|
Foundation::{CloseHandle, ERROR_INSUFFICIENT_BUFFER, HANDLE},
|
||||||
use winapi::shared::minwindef::{BOOL, DWORD};
|
Security::{
|
||||||
use winapi::um::handleapi;
|
AccessCheck, DuplicateToken, GetFileSecurityW, MapGenericMask, SecurityImpersonation,
|
||||||
use winapi::um::processthreadsapi;
|
DACL_SECURITY_INFORMATION, GENERIC_MAPPING, GROUP_SECURITY_INFORMATION,
|
||||||
use winapi::um::securitybaseapi;
|
OWNER_SECURITY_INFORMATION, PRIVILEGE_SET, PSECURITY_DESCRIPTOR, TOKEN_DUPLICATE,
|
||||||
use winapi::um::winnt::{
|
TOKEN_IMPERSONATE, TOKEN_QUERY, TOKEN_READ_CONTROL,
|
||||||
SecurityImpersonation, BOOLEAN, DACL_SECURITY_INFORMATION, FILE_ALL_ACCESS,
|
},
|
||||||
FILE_GENERIC_EXECUTE, FILE_GENERIC_READ, FILE_GENERIC_WRITE, GENERIC_MAPPING,
|
Storage::FileSystem::{
|
||||||
GROUP_SECURITY_INFORMATION, HANDLE, LPCWSTR, OWNER_SECURITY_INFORMATION, PRIVILEGE_SET,
|
FILE_ALL_ACCESS, FILE_GENERIC_EXECUTE, FILE_GENERIC_READ, FILE_GENERIC_WRITE,
|
||||||
STANDARD_RIGHTS_READ, TOKEN_DUPLICATE, TOKEN_IMPERSONATE, TOKEN_QUERY,
|
},
|
||||||
|
System::Threading::{GetCurrentProcess, OpenProcessToken},
|
||||||
|
UI::Shell::PathIsNetworkPathW,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Checks if the current user has write access right to the `folder_path`
|
/// Checks if the current user has write access right to the `folder_path`
|
||||||
///
|
///
|
||||||
/// First, the function extracts DACL from the given directory and then calls `AccessCheck` against
|
/// First, the function extracts DACL from the given directory and then calls `AccessCheck` against
|
||||||
/// the current process access token and directory's security descriptor.
|
/// the current process access token and directory's security descriptor.
|
||||||
/// Does not work for network drives and always returns true
|
/// Does not work for network drives and always returns true
|
||||||
pub fn is_write_allowed(folder_path: &Path) -> std::result::Result<bool, &'static str> {
|
pub fn is_write_allowed(folder_path: &Path) -> std::result::Result<bool, String> {
|
||||||
let folder_name: Vec<u16> = folder_path.as_os_str().encode_wide().chain([0]).collect();
|
let wpath_vec: Vec<u16> = folder_path.as_os_str().encode_wide().chain([0]).collect();
|
||||||
|
let wpath = PCWSTR(wpath_vec.as_ptr());
|
||||||
|
|
||||||
if is_network_path(&folder_name) {
|
if unsafe { PathIsNetworkPathW(wpath) }.as_bool() {
|
||||||
log::info!(
|
log::info!(
|
||||||
"Directory '{:?}' is a network drive, unable to check write permissions. See #1506 for details",
|
"Directory '{:?}' is a network drive, unable to check write permissions. See #1506 for details",
|
||||||
folder_path
|
folder_path
|
||||||
@ -31,79 +34,84 @@ pub fn is_write_allowed(folder_path: &Path) -> std::result::Result<bool, &'stati
|
|||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut length: DWORD = 0;
|
let mut length = 0;
|
||||||
|
|
||||||
let rc = unsafe {
|
let rc = unsafe {
|
||||||
securitybaseapi::GetFileSecurityW(
|
GetFileSecurityW(
|
||||||
folder_name.as_ptr(),
|
wpath,
|
||||||
OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
|
(OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION).0,
|
||||||
std::ptr::null_mut(),
|
PSECURITY_DESCRIPTOR::default(),
|
||||||
0,
|
0,
|
||||||
&mut length,
|
&mut length,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
if rc != 0 {
|
|
||||||
return Err(
|
// expect ERROR_INSUFFICIENT_BUFFER
|
||||||
"GetFileSecurityW returned non-zero when asked for the security descriptor size",
|
match rc.ok() {
|
||||||
);
|
Err(e) if e.win32_error() == Some(ERROR_INSUFFICIENT_BUFFER) => (),
|
||||||
|
result => return Err(format!("GetFileSecurityW returned unexpected return value when asked for the security descriptor size: {:?}", result)),
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut buf: Vec<u8> = Vec::with_capacity(length as usize);
|
let mut buf = vec![0u8; length as usize];
|
||||||
|
let psecurity_descriptor = PSECURITY_DESCRIPTOR(buf.as_mut_ptr() as *mut c_void);
|
||||||
|
|
||||||
let rc = unsafe {
|
let rc = unsafe {
|
||||||
securitybaseapi::GetFileSecurityW(
|
GetFileSecurityW(
|
||||||
folder_name.as_ptr(),
|
wpath,
|
||||||
OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION,
|
(OWNER_SECURITY_INFORMATION | GROUP_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION).0,
|
||||||
buf.as_mut_ptr().cast::<std::ffi::c_void>(),
|
psecurity_descriptor,
|
||||||
length,
|
length,
|
||||||
&mut length,
|
&mut length,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
if rc != 1 {
|
if let Err(e) = rc.ok() {
|
||||||
return Err("GetFileSecurityW failed to retrieve the security descriptor");
|
return Err(format!(
|
||||||
|
"GetFileSecurityW failed to retrieve the security descriptor: {e:?}"
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut token: HANDLE = 0 as HANDLE;
|
let mut token = HANDLE::default();
|
||||||
let rc = unsafe {
|
let rc = unsafe {
|
||||||
processthreadsapi::OpenProcessToken(
|
OpenProcessToken(
|
||||||
processthreadsapi::GetCurrentProcess(),
|
GetCurrentProcess(),
|
||||||
TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_DUPLICATE | STANDARD_RIGHTS_READ,
|
TOKEN_IMPERSONATE | TOKEN_QUERY | TOKEN_DUPLICATE | TOKEN_READ_CONTROL,
|
||||||
&mut token,
|
&mut token,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
if rc != 1 {
|
if let Err(e) = rc.ok() {
|
||||||
return Err("OpenProcessToken failed to retrieve current process' security token");
|
return Err(format!(
|
||||||
|
"OpenProcessToken failed to retrieve current process' security token: {e:?}"
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut impersonated_token: HANDLE = 0 as HANDLE;
|
let mut impersonated_token = HANDLE::default();
|
||||||
let rc = unsafe {
|
let rc = unsafe { DuplicateToken(token, SecurityImpersonation, &mut impersonated_token) };
|
||||||
securitybaseapi::DuplicateToken(token, SecurityImpersonation, &mut impersonated_token)
|
|
||||||
};
|
if let Err(e) = rc.ok() {
|
||||||
if rc != 1 {
|
unsafe { CloseHandle(token) };
|
||||||
unsafe { handleapi::CloseHandle(token) };
|
return Err(format!("DuplicateToken failed: {e:?}"));
|
||||||
return Err("DuplicateToken failed");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut mapping: GENERIC_MAPPING = GENERIC_MAPPING {
|
let mapping = GENERIC_MAPPING {
|
||||||
GenericRead: FILE_GENERIC_READ,
|
GenericRead: FILE_GENERIC_READ.0,
|
||||||
GenericWrite: FILE_GENERIC_WRITE,
|
GenericWrite: FILE_GENERIC_WRITE.0,
|
||||||
GenericExecute: FILE_GENERIC_EXECUTE,
|
GenericExecute: FILE_GENERIC_EXECUTE.0,
|
||||||
GenericAll: FILE_ALL_ACCESS,
|
GenericAll: FILE_ALL_ACCESS.0,
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut priviledges: PRIVILEGE_SET = PRIVILEGE_SET::default();
|
let mut priviledges: PRIVILEGE_SET = PRIVILEGE_SET::default();
|
||||||
let mut priv_size = mem::size_of::<PRIVILEGE_SET>() as DWORD;
|
let mut priv_size = mem::size_of::<PRIVILEGE_SET>() as _;
|
||||||
let mut granted_access: DWORD = 0;
|
let mut granted_access = 0;
|
||||||
let mut access_rights: DWORD = FILE_GENERIC_WRITE;
|
let mut access_rights = FILE_GENERIC_WRITE;
|
||||||
let mut result: BOOL = 0;
|
let mut result = 0;
|
||||||
unsafe { securitybaseapi::MapGenericMask(&mut access_rights, &mut mapping) };
|
unsafe { MapGenericMask(&mut access_rights.0, &mapping) };
|
||||||
let rc = unsafe {
|
let rc = unsafe {
|
||||||
securitybaseapi::AccessCheck(
|
AccessCheck(
|
||||||
buf.as_mut_ptr().cast::<std::ffi::c_void>(),
|
psecurity_descriptor,
|
||||||
impersonated_token,
|
impersonated_token,
|
||||||
access_rights,
|
access_rights.0,
|
||||||
&mut mapping,
|
&mapping,
|
||||||
&mut priviledges,
|
&mut priviledges,
|
||||||
&mut priv_size,
|
&mut priv_size,
|
||||||
&mut granted_access,
|
&mut granted_access,
|
||||||
@ -111,22 +119,13 @@ pub fn is_write_allowed(folder_path: &Path) -> std::result::Result<bool, &'stati
|
|||||||
)
|
)
|
||||||
};
|
};
|
||||||
unsafe {
|
unsafe {
|
||||||
handleapi::CloseHandle(impersonated_token);
|
CloseHandle(impersonated_token);
|
||||||
handleapi::CloseHandle(token);
|
CloseHandle(token);
|
||||||
}
|
}
|
||||||
|
|
||||||
if rc != 1 {
|
if let Err(e) = rc.ok() {
|
||||||
return Err("AccessCheck failed");
|
return Err(format!("AccessCheck failed: {e:?}"));
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(result != 0)
|
Ok(result != 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[link(name = "Shlwapi")]
|
|
||||||
extern "system" {
|
|
||||||
fn PathIsNetworkPathW(pszPath: LPCWSTR) -> BOOLEAN;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_network_path(folder_path: &[u16]) -> bool {
|
|
||||||
unsafe { PathIsNetworkPathW(folder_path.as_ptr()) == 1 }
|
|
||||||
}
|
|
||||||
|
Loading…
Reference in New Issue
Block a user