1
0
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:
David Knaack 2022-04-21 20:59:00 +02:00 committed by GitHub
parent 6def45b751
commit 6c2b51a621
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 107 additions and 85 deletions

21
Cargo.lock generated
View File

@ -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",
] ]

View File

@ -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"

View File

@ -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"),
); });
} }
} }

View File

@ -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 }
}