diff options
| author | David Lönnhager <david.l@mullvad.net> | 2021-04-15 18:48:37 +0200 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2021-07-02 09:54:19 +0200 |
| commit | af7d9b09663672d0a8a8a84c02387a9749a3bf98 (patch) | |
| tree | 43e73b710c9c6749934e1ec9fda504978bb529e3 | |
| parent | 3a0b7c58092697743723a82e61af17f9afd876c4 (diff) | |
| download | mullvadvpn-af7d9b09663672d0a8a8a84c02387a9749a3bf98.tar.xz mullvadvpn-af7d9b09663672d0a8a8a84c02387a9749a3bf98.zip | |
Do not fail as easily when determining device paths, and ignore
links
| -rw-r--r-- | talpid-core/src/split_tunnel/windows/driver.rs | 4 | ||||
| -rw-r--r-- | talpid-core/src/split_tunnel/windows/windows.rs | 103 |
2 files changed, 67 insertions, 40 deletions
diff --git a/talpid-core/src/split_tunnel/windows/driver.rs b/talpid-core/src/split_tunnel/windows/driver.rs index 5b1187d259..51c72c8051 100644 --- a/talpid-core/src/split_tunnel/windows/driver.rs +++ b/talpid-core/src/split_tunnel/windows/driver.rs @@ -1,5 +1,5 @@ use super::windows::{ - get_final_path_name, get_process_creation_time, get_process_device_path, open_process, + get_device_path, get_process_creation_time, get_process_device_path, open_process, ProcessAccess, ProcessSnapshot, }; use memoffset::offset_of; @@ -266,7 +266,7 @@ impl DeviceHandle { pub fn set_config<T: AsRef<OsStr>>(&self, apps: &[T]) -> io::Result<()> { let mut device_paths = Vec::with_capacity(apps.len()); for app in apps.as_ref() { - device_paths.push(get_final_path_name(app)?); + device_paths.push(get_device_path(app.as_ref())?); } log::debug!("Excluded device paths:"); diff --git a/talpid-core/src/split_tunnel/windows/windows.rs b/talpid-core/src/split_tunnel/windows/windows.rs index be8631d53c..b706a73203 100644 --- a/talpid-core/src/split_tunnel/windows/windows.rs +++ b/talpid-core/src/split_tunnel/windows/windows.rs @@ -3,12 +3,12 @@ use std::{ ffi::{OsStr, OsString}, - fs::OpenOptions, - io, mem, + io, iter, mem, os::windows::{ - ffi::OsStringExt, - io::{AsRawHandle, RawHandle}, + ffi::{OsStrExt, OsStringExt}, + io::RawHandle, }, + path::Path, ptr, }; use winapi::{ @@ -18,7 +18,7 @@ use winapi::{ winerror::{ERROR_INSUFFICIENT_BUFFER, ERROR_NO_MORE_FILES}, }, um::{ - fileapi::GetFinalPathNameByHandleW, + fileapi::QueryDosDeviceW, handleapi::{CloseHandle, INVALID_HANDLE_VALUE}, processthreadsapi::{GetProcessTimes, OpenProcess}, psapi::K32GetProcessImageFileNameW, @@ -27,9 +27,6 @@ use winapi::{ }, }; -/// Return path with the volume device path. -const VOLUME_NAME_NT: u32 = 0x02; - pub struct ProcessSnapshot { handle: HANDLE, } @@ -108,43 +105,73 @@ impl Iterator for ProcessSnapshotEntries<'_> { } } -pub fn get_final_path_name<T: AsRef<OsStr>>(path: T) -> Result<OsString, io::Error> { - // TODO: verify that all flags, including security flags, are ok - // TODO: verify that the file is a PE executable? - // TODO: verify that the executable is on a physical drive? - let file = OpenOptions::new().read(true).open(path.as_ref())?; - get_final_path_name_by_handle(file.as_raw_handle()) -} +/// Obtains a device path without resolving links or mount points. +pub fn get_device_path<T: AsRef<Path>>(path: T) -> Result<OsString, io::Error> { + if !path.as_ref().is_absolute() { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "path must be absolute", + )); + } -pub fn get_final_path_name_by_handle(raw_handle: RawHandle) -> Result<OsString, io::Error> { - let buffer_size = unsafe { - GetFinalPathNameByHandleW(raw_handle as *mut _, ptr::null_mut(), 0u32, VOLUME_NAME_NT) - } as usize; + let drive_comp = path.as_ref().components().next(); + let drive = match drive_comp { + Some(std::path::Component::Prefix(prefix)) => prefix.as_os_str(), + _ => { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "invalid drive label", + )) + } + }; - if buffer_size == 0 { - return Err(io::Error::last_os_error()); - } + let mut new_path = query_dos_device(drive)?; + let suffix = path + .as_ref() + .strip_prefix(drive_comp.unwrap()) + .expect("path missing own component"); + new_path.push(suffix); - let mut buffer = Vec::new(); - buffer.reserve_exact(buffer_size); + Ok(new_path) +} - let status = unsafe { - GetFinalPathNameByHandleW( - raw_handle as *mut _, - buffer.as_mut_ptr(), - buffer_size as u32, - VOLUME_NAME_NT, - ) - } as usize; +/// Obtains the real device path for a label (such as C:). +/// The underlying function may return multiple paths, but only the first is returned. +fn query_dos_device<T: AsRef<OsStr>>(device_name: T) -> io::Result<OsString> { + let device_name_c: Vec<u16> = device_name + .as_ref() + .encode_wide() + .chain(iter::once(0u16)) + .collect(); + let mut new_prefix = vec![0u16; 64]; - if status == 0 { - return Err(io::Error::last_os_error()); - } + loop { + let prefix_len = unsafe { + QueryDosDeviceW( + device_name_c.as_ptr(), + new_prefix.as_mut_ptr(), + new_prefix.len() as u32, + ) as usize + }; - unsafe { buffer.set_len(buffer_size - 1) }; + if prefix_len == 0 { + let last_error = io::Error::last_os_error(); + if last_error.raw_os_error() == Some(ERROR_INSUFFICIENT_BUFFER as i32) { + // resize buffer and try again + new_prefix.resize(2 * new_prefix.len(), 0); + continue; + } + break Err(last_error); + } - // TODO: can this be done by stealing 'buffer' instead of copying it? - Ok(OsStringExt::from_wide(&buffer)) + // We must scan for the first null terminator + // Because `new_prefix` may contain multiple strings. + + let real_len = new_prefix.iter().position(|&c| c == 0u16).unwrap(); + unsafe { new_prefix.set_len(real_len) }; + + break Ok(OsString::from_wide(&new_prefix)); + } } /// Object that frees its handle when dropped. |
