From 82b8de4c9c02ac1ecf7c65a8effe6a910f6941ba Mon Sep 17 00:00:00 2001 From: Reactor Scram Date: Thu, 25 Jul 2024 09:28:35 -0500 Subject: [PATCH] refactor(client/windows): de-dupe wintun.dll (#6020) Closes #5977 Refactored some other stuff to make this work Also removed a redundant impl of `ensure_dll` in a benchmark --- rust/Cargo.lock | 8 +- rust/bin-shared/Cargo.toml | 4 +- rust/bin-shared/benches/tunnel.rs | 9 -- rust/bin-shared/src/lib.rs | 18 ++++ rust/bin-shared/src/tun_device_manager.rs | 12 --- .../src/tun_device_manager/windows.rs | 101 ++++++++++++++++-- rust/bin-shared/src/windows.rs | 21 ++++ .../src}/wintun/README.md | 0 .../src}/wintun/bin/amd64/wintun.dll | Bin .../src}/wintun/bin/arm64/wintun.dll | Bin rust/connlib/shared/Cargo.toml | 12 --- rust/connlib/shared/src/error.rs | 6 -- rust/connlib/shared/src/lib.rs | 18 ---- rust/connlib/shared/src/windows.rs | 44 -------- rust/gui-client/src-tauri/Cargo.toml | 2 +- .../src-tauri/src/client/deep_link/windows.rs | 2 +- rust/gui-client/src-tauri/src/client/gui.rs | 2 +- .../src-tauri/src/client/gui/os_linux.rs | 3 +- .../src-tauri/src/client/gui/os_windows.rs | 2 +- rust/headless-client/Cargo.toml | 1 - .../src/dns_control/windows.rs | 2 +- .../src/ipc_service/ipc/linux.rs | 5 +- .../src/ipc_service/ipc/windows.rs | 3 +- rust/headless-client/src/known_dirs/linux.rs | 2 +- .../headless-client/src/known_dirs/windows.rs | 29 ++--- rust/headless-client/src/linux.rs | 14 +-- rust/headless-client/src/standalone.rs | 1 - rust/headless-client/src/windows.rs | 8 -- .../src/windows/wintun_install.rs | 97 ----------------- 29 files changed, 154 insertions(+), 272 deletions(-) create mode 100644 rust/bin-shared/src/windows.rs rename rust/{headless-client/src/windows => bin-shared/src}/wintun/README.md (100%) rename rust/{headless-client/src/windows => bin-shared/src}/wintun/bin/amd64/wintun.dll (100%) rename rust/{headless-client/src/windows => bin-shared/src}/wintun/bin/arm64/wintun.dll (100%) delete mode 100644 rust/connlib/shared/src/windows.rs delete mode 100644 rust/headless-client/src/windows/wintun_install.rs diff --git a/rust/Cargo.lock b/rust/Cargo.lock index 9b808eeae..60f7b0fe2 100644 --- a/rust/Cargo.lock +++ b/rust/Cargo.lock @@ -1078,7 +1078,6 @@ dependencies = [ "ip-packet", "ip_network", "itertools 0.13.0", - "known-folders", "libc", "os_info", "phoenix-channel", @@ -1095,8 +1094,6 @@ dependencies = [ "tracing", "url", "uuid", - "windows 0.57.0", - "wintun", ] [[package]] @@ -1790,9 +1787,11 @@ dependencies = [ "futures", "ip-packet", "ip_network", + "known-folders", "libc", "netlink-packet-core", "netlink-packet-route", + "ring", "rtnetlink", "tokio", "tracing", @@ -1851,9 +1850,9 @@ dependencies = [ "chrono", "clap", "connlib-client-shared", - "connlib-shared", "crash-handler", "dirs", + "firezone-bin-shared", "firezone-headless-client", "futures", "git-version", @@ -1917,7 +1916,6 @@ dependencies = [ "nix 0.28.0", "phoenix-channel", "resolv-conf", - "ring", "rtnetlink", "sd-notify", "secrecy", diff --git a/rust/bin-shared/Cargo.toml b/rust/bin-shared/Cargo.toml index c7ae18ee7..4c71aadad 100644 --- a/rust/bin-shared/Cargo.toml +++ b/rust/bin-shared/Cargo.toml @@ -29,8 +29,10 @@ rtnetlink = { workspace = true } libc = "0.2" [target.'cfg(target_os = "windows")'.dependencies] -wintun = "0.4.0" +known-folders = "1.1.0" +ring = "0.17" uuid = { version = "1.10.0", features = ["v4"] } +wintun = "0.4.0" [target.'cfg(windows)'.dependencies.windows] version = "0.57.0" diff --git a/rust/bin-shared/benches/tunnel.rs b/rust/bin-shared/benches/tunnel.rs index 22dfb143a..c91f48f6b 100644 --- a/rust/bin-shared/benches/tunnel.rs +++ b/rust/bin-shared/benches/tunnel.rs @@ -40,15 +40,6 @@ mod platform { use tun::Tun as _; pub(crate) async fn perf() -> Result<()> { - // Install wintun so the test can run - let wintun_path = connlib_shared::windows::wintun_dll_path().unwrap(); - tokio::fs::create_dir_all(wintun_path.parent().unwrap()) - .await - .unwrap(); - tokio::fs::write(&wintun_path, connlib_shared::windows::wintun_bytes()) - .await - .unwrap(); - const MTU: usize = 1_280; const NUM_REQUESTS: u64 = 1_000; const REQ_CODE: u8 = 42; diff --git a/rust/bin-shared/src/lib.rs b/rust/bin-shared/src/lib.rs index 0ad0dfc19..46d67ad2b 100644 --- a/rust/bin-shared/src/lib.rs +++ b/rust/bin-shared/src/lib.rs @@ -1,5 +1,8 @@ mod tun_device_manager; +#[cfg(target_os = "windows")] +pub mod windows; + use clap::Args; use tracing_log::LogTracer; use tracing_subscriber::{ @@ -7,6 +10,21 @@ use tracing_subscriber::{ }; use url::Url; +/// Bundle ID / App ID that the client uses to distinguish itself from other programs on the system +/// +/// e.g. In ProgramData and AppData we use this to name our subdirectories for configs and data, +/// and Windows may use it to track things like the MSI installer, notification titles, +/// deep link registration, etc. +/// +/// This should be identical to the `tauri.bundle.identifier` over in `tauri.conf.json`, +/// but sometimes I need to use this before Tauri has booted up, or in a place where +/// getting the Tauri app handle would be awkward. +/// +/// Luckily this is also the AppUserModelId that Windows uses to label notifications, +/// so if your dev system has Firezone installed by MSI, the notifications will look right. +/// +pub const BUNDLE_ID: &str = "dev.firezone.client"; + /// Mark for Firezone sockets to prevent routing loops on Linux. pub const FIREZONE_MARK: u32 = 0xfd002021; diff --git a/rust/bin-shared/src/tun_device_manager.rs b/rust/bin-shared/src/tun_device_manager.rs index 9e309b4f0..cf1bc2d63 100644 --- a/rust/bin-shared/src/tun_device_manager.rs +++ b/rust/bin-shared/src/tun_device_manager.rs @@ -27,18 +27,6 @@ mod tests { .with_test_writer() .try_init(); - #[cfg(target_os = "windows")] - { - // Install wintun so the test can run - let wintun_path = connlib_shared::windows::wintun_dll_path().unwrap(); - tokio::fs::create_dir_all(wintun_path.parent().unwrap()) - .await - .unwrap(); - tokio::fs::write(&wintun_path, connlib_shared::windows::wintun_bytes()) - .await - .unwrap(); - } - // Run these tests in series since they would fight over the tunnel interface // if they ran concurrently create_tun(); diff --git a/rust/bin-shared/src/tun_device_manager/windows.rs b/rust/bin-shared/src/tun_device_manager/windows.rs index 77eeb4aeb..6fd97b5db 100644 --- a/rust/bin-shared/src/tun_device_manager/windows.rs +++ b/rust/bin-shared/src/tun_device_manager/windows.rs @@ -1,14 +1,14 @@ +use crate::windows::CREATE_NO_WINDOW; use anyhow::{Context as _, Result}; -use connlib_shared::{ - windows::{CREATE_NO_WINDOW, TUNNEL_NAME}, - DEFAULT_MTU, -}; +use connlib_shared::DEFAULT_MTU; use ip_network::{IpNetwork, Ipv4Network, Ipv6Network}; +use ring::digest; use std::{ collections::HashSet, - io, + io::{self, Read as _}, net::{Ipv4Addr, Ipv6Addr, SocketAddrV4, SocketAddrV6}, os::windows::process::CommandExt, + path::{Path, PathBuf}, process::{Command, Stdio}, str::FromStr, sync::Arc, @@ -27,8 +27,9 @@ use windows::Win32::{ }; use wintun::Adapter; -// Not sure how this and `TUNNEL_NAME` differ -const ADAPTER_NAME: &str = "Firezone"; +// wintun automatically append " Tunnel" to this +pub(crate) const TUNNEL_NAME: &str = "Firezone"; + /// The ring buffer size used for Wintun. /// /// Must be a power of two within a certain range @@ -211,16 +212,15 @@ impl Tun { pub fn new() -> Result { const TUNNEL_UUID: &str = "e9245bc1-b8c1-44ca-ab1d-c6aad4f13b9c"; - // SAFETY: we're loading a DLL from disk and it has arbitrary C code in it. - // The Windows client, in `wintun_install` hashes the DLL at startup, before calling connlib, so it's unlikely for the DLL to be accidentally corrupted by the time we get here. - let path = connlib_shared::windows::wintun_dll_path()?; + let path = ensure_dll()?; + // SAFETY: we're loading a DLL from disk and it has arbitrary C code in it. There's no perfect way to prove it's safe. let wintun = unsafe { wintun::load_from_path(path) }?; // Create wintun adapter let uuid = uuid::Uuid::from_str(TUNNEL_UUID) .expect("static UUID should always parse correctly") .as_u128(); - let adapter = &Adapter::create(&wintun, ADAPTER_NAME, TUNNEL_NAME, Some(uuid))?; + let adapter = &Adapter::create(&wintun, TUNNEL_NAME, TUNNEL_NAME, Some(uuid))?; let iface_idx = adapter.get_adapter_index()?; set_iface_config(adapter.get_luid(), DEFAULT_MTU as u32)?; @@ -394,3 +394,82 @@ fn set_iface_config(luid: wintun::NET_LUID_LH, mtu: u32) -> Result<()> { } Ok(()) } + +/// Installs the DLL in %LOCALAPPDATA% and returns the DLL's absolute path +/// +/// e.g. `C:\Users\User\AppData\Local\dev.firezone.client\data\wintun.dll` +/// Also verifies the SHA256 of the DLL on-disk with the expected bytes packed into the exe +fn ensure_dll() -> Result { + let dll_bytes = wintun_bytes(); + + let path = wintun_dll_path().context("Can't compute wintun.dll path")?; + // The DLL path should always have a parent + let dir = path.parent().context("wintun.dll path invalid")?; + std::fs::create_dir_all(dir).context("Can't create dirs for wintun.dll")?; + + tracing::debug!(?path, "wintun.dll path"); + + // This hash check is not meant to protect against attacks. It only lets us skip redundant disk writes, and it updates the DLL if needed. + // `tun_windows.rs` in connlib, and `elevation.rs`, rely on thia. + if dll_already_exists(&path, &dll_bytes) { + return Ok(path); + } + std::fs::write(&path, dll_bytes.bytes).context("Failed to write wintun.dll")?; + Ok(path) +} + +fn dll_already_exists(path: &Path, dll_bytes: &DllBytes) -> bool { + let mut f = match std::fs::File::open(path) { + Err(_) => return false, + Ok(x) => x, + }; + + let actual_len = usize::try_from(f.metadata().unwrap().len()).unwrap(); + let expected_len = dll_bytes.bytes.len(); + // If the dll is 100 MB instead of 0.5 MB, this allows us to skip a 100 MB read + if actual_len != expected_len { + return false; + } + + let mut buf = vec![0u8; expected_len]; + if f.read_exact(&mut buf).is_err() { + return false; + } + + let expected = ring::test::from_hex(dll_bytes.expected_sha256).unwrap(); + let actual = digest::digest(&digest::SHA256, &buf); + expected == actual.as_ref() +} + +/// Returns the absolute path for installing and loading `wintun.dll` +/// +/// e.g. `C:\Users\User\AppData\Local\dev.firezone.client\data\wintun.dll` +fn wintun_dll_path() -> Result { + let path = crate::windows::app_local_data_dir()? + .join("data") + .join("wintun.dll"); + Ok(path) +} + +struct DllBytes { + /// Bytes embedded in the client with `include_bytes` + pub bytes: &'static [u8], + /// Expected SHA256 hash + pub expected_sha256: &'static str, +} + +#[cfg(target_arch = "x86_64")] +fn wintun_bytes() -> DllBytes { + DllBytes { + bytes: include_bytes!("../wintun/bin/amd64/wintun.dll"), + expected_sha256: "e5da8447dc2c320edc0fc52fa01885c103de8c118481f683643cacc3220dafce", + } +} + +#[cfg(target_arch = "aarch64")] +fn wintun_bytes() -> DllBytes { + DllBytes { + bytes: include_bytes!("../wintun/bin/arm64/wintun.dll"), + expected_sha256: "f7ba89005544be9d85231a9e0d5f23b2d15b3311667e2dad0debd344918a3f80", + } +} diff --git a/rust/bin-shared/src/windows.rs b/rust/bin-shared/src/windows.rs new file mode 100644 index 000000000..3161cd986 --- /dev/null +++ b/rust/bin-shared/src/windows.rs @@ -0,0 +1,21 @@ +use anyhow::{Context as _, Result}; +use known_folders::{get_known_folder_path, KnownFolder}; +use std::path::PathBuf; + +/// Hides Powershell's console on Windows +/// +/// +/// Also used for self-elevation +pub const CREATE_NO_WINDOW: u32 = 0x08000000; + +/// Returns e.g. `C:/Users/User/AppData/Local/dev.firezone.client +/// +/// This is where we can save config, logs, crash dumps, etc. +/// It's per-user and doesn't roam across different PCs in the same domain. +/// It's read-write for non-elevated processes. +pub fn app_local_data_dir() -> Result { + let path = get_known_folder_path(KnownFolder::LocalAppData) + .context("Can't find %LOCALAPPDATA% dir")? + .join(crate::BUNDLE_ID); + Ok(path) +} diff --git a/rust/headless-client/src/windows/wintun/README.md b/rust/bin-shared/src/wintun/README.md similarity index 100% rename from rust/headless-client/src/windows/wintun/README.md rename to rust/bin-shared/src/wintun/README.md diff --git a/rust/headless-client/src/windows/wintun/bin/amd64/wintun.dll b/rust/bin-shared/src/wintun/bin/amd64/wintun.dll similarity index 100% rename from rust/headless-client/src/windows/wintun/bin/amd64/wintun.dll rename to rust/bin-shared/src/wintun/bin/amd64/wintun.dll diff --git a/rust/headless-client/src/windows/wintun/bin/arm64/wintun.dll b/rust/bin-shared/src/wintun/bin/arm64/wintun.dll similarity index 100% rename from rust/headless-client/src/windows/wintun/bin/arm64/wintun.dll rename to rust/bin-shared/src/wintun/bin/arm64/wintun.dll diff --git a/rust/connlib/shared/Cargo.toml b/rust/connlib/shared/Cargo.toml index 1befbf10c..acd6815b7 100644 --- a/rust/connlib/shared/Cargo.toml +++ b/rust/connlib/shared/Cargo.toml @@ -41,17 +41,5 @@ tokio = { version = "1.38", features = ["macros", "rt"] } [target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies] swift-bridge = { workspace = true } -# Windows tunnel dependencies -[target.'cfg(target_os = "windows")'.dependencies] -wintun = "0.4.0" -known-folders = "1.1.0" - -# Windows Win32 API -[target.'cfg(windows)'.dependencies.windows] -version = "0.57.0" -features = [ - "Win32_Foundation", -] - [lints] workspace = true diff --git a/rust/connlib/shared/src/error.rs b/rust/connlib/shared/src/error.rs index 1f29a2458..de0da2fc7 100644 --- a/rust/connlib/shared/src/error.rs +++ b/rust/connlib/shared/src/error.rs @@ -52,12 +52,6 @@ pub enum ConnlibError { #[error("failed packet translation")] FailedTranslation, #[cfg(target_os = "windows")] - #[error("Windows error: {0}")] - WindowsError(#[from] windows::core::Error), - #[cfg(target_os = "windows")] - #[error(transparent)] - Wintun(#[from] wintun::Error), - #[cfg(target_os = "windows")] #[error("Can't compute path for wintun.dll")] WintunDllPath, #[cfg(target_os = "windows")] diff --git a/rust/connlib/shared/src/lib.rs b/rust/connlib/shared/src/lib.rs index e256c5555..d08727d4f 100644 --- a/rust/connlib/shared/src/lib.rs +++ b/rust/connlib/shared/src/lib.rs @@ -7,9 +7,6 @@ pub mod callbacks; pub mod error; pub mod messages; -#[cfg(target_os = "windows")] -pub mod windows; - #[cfg(feature = "proptest")] pub mod proptest; @@ -24,21 +21,6 @@ use rand_core::OsRng; pub type DomainName = domain::base::Name>; -/// Bundle ID / App ID that the client uses to distinguish itself from other programs on the system -/// -/// e.g. In ProgramData and AppData we use this to name our subdirectories for configs and data, -/// and Windows may use it to track things like the MSI installer, notification titles, -/// deep link registration, etc. -/// -/// This should be identical to the `tauri.bundle.identifier` over in `tauri.conf.json`, -/// but sometimes I need to use this before Tauri has booted up, or in a place where -/// getting the Tauri app handle would be awkward. -/// -/// Luckily this is also the AppUserModelId that Windows uses to label notifications, -/// so if your dev system has Firezone installed by MSI, the notifications will look right. -/// -pub const BUNDLE_ID: &str = "dev.firezone.client"; - pub const DEFAULT_MTU: usize = 1280; const LIB_NAME: &str = "connlib"; diff --git a/rust/connlib/shared/src/windows.rs b/rust/connlib/shared/src/windows.rs deleted file mode 100644 index 2ded2a9cd..000000000 --- a/rust/connlib/shared/src/windows.rs +++ /dev/null @@ -1,44 +0,0 @@ -//! Windows-specific things like the well-known appdata path, bundle ID, etc. - -use crate::Error; -use known_folders::{get_known_folder_path, KnownFolder}; -use std::path::PathBuf; - -/// Hides Powershell's console on Windows -/// -/// -/// Also used for self-elevation -pub const CREATE_NO_WINDOW: u32 = 0x08000000; - -// wintun automatically append " Tunnel" to this -pub const TUNNEL_NAME: &str = "Firezone"; - -/// Returns e.g. `C:/Users/User/AppData/Local/dev.firezone.client -/// -/// This is where we can save config, logs, crash dumps, etc. -/// It's per-user and doesn't roam across different PCs in the same domain. -/// It's read-write for non-elevated processes. -pub fn app_local_data_dir() -> Result { - let path = get_known_folder_path(KnownFolder::LocalAppData) - .ok_or(Error::CantFindLocalAppDataFolder)? - .join(crate::BUNDLE_ID); - Ok(path) -} - -/// Returns the absolute path for installing and loading `wintun.dll` -/// -/// e.g. `C:\Users\User\AppData\Local\dev.firezone.client\data\wintun.dll` -pub fn wintun_dll_path() -> Result { - let path = app_local_data_dir()?.join("data").join("wintun.dll"); - Ok(path) -} - -#[cfg(target_arch = "aarch64")] -pub fn wintun_bytes() -> &'static [u8] { - include_bytes!("../../../headless-client/src/windows/wintun/bin/arm64/wintun.dll") -} - -#[cfg(target_arch = "x86_64")] -pub fn wintun_bytes() -> &'static [u8] { - include_bytes!("../../../headless-client/src/windows/wintun/bin/amd64/wintun.dll") -} diff --git a/rust/gui-client/src-tauri/Cargo.toml b/rust/gui-client/src-tauri/Cargo.toml index b99aabdad..98e6e1640 100644 --- a/rust/gui-client/src-tauri/Cargo.toml +++ b/rust/gui-client/src-tauri/Cargo.toml @@ -18,8 +18,8 @@ atomicwrites = "0.4.3" chrono = { workspace = true } clap = { version = "4.5", features = ["derive", "env"] } connlib-client-shared = { workspace = true } -connlib-shared = { workspace = true } crash-handler = "0.6.2" +firezone-bin-shared = { workspace = true } firezone-headless-client = { path = "../../headless-client" } futures = { version = "0.3", default-features = false } git-version = "0.3.9" diff --git a/rust/gui-client/src-tauri/src/client/deep_link/windows.rs b/rust/gui-client/src-tauri/src/client/deep_link/windows.rs index 1e59dd493..b1f0fb1c6 100644 --- a/rust/gui-client/src-tauri/src/client/deep_link/windows.rs +++ b/rust/gui-client/src-tauri/src/client/deep_link/windows.rs @@ -3,7 +3,7 @@ use super::FZ_SCHEME; use anyhow::{Context, Result}; -use connlib_shared::BUNDLE_ID; +use firezone_bin_shared::BUNDLE_ID; use secrecy::Secret; use std::{io, path::Path, time::Duration}; use tokio::{io::AsyncReadExt, io::AsyncWriteExt, net::windows::named_pipe}; diff --git a/rust/gui-client/src-tauri/src/client/gui.rs b/rust/gui-client/src-tauri/src/client/gui.rs index 36c28c259..707ed171d 100644 --- a/rust/gui-client/src-tauri/src/client/gui.rs +++ b/rust/gui-client/src-tauri/src/client/gui.rs @@ -185,7 +185,7 @@ pub(crate) fn run( } assert_eq!( - connlib_shared::BUNDLE_ID, + firezone_bin_shared::BUNDLE_ID, app.handle().config().tauri.bundle.identifier, "BUNDLE_ID should match bundle ID in tauri.conf.json" ); diff --git a/rust/gui-client/src-tauri/src/client/gui/os_linux.rs b/rust/gui-client/src-tauri/src/client/gui/os_linux.rs index 9ad987690..034bd48de 100644 --- a/rust/gui-client/src-tauri/src/client/gui/os_linux.rs +++ b/rust/gui-client/src-tauri/src/client/gui/os_linux.rs @@ -1,4 +1,5 @@ use anyhow::{Context as _, Result}; +use firezone_bin_shared::BUNDLE_ID; use tauri::api::notification::Notification; pub(crate) async fn set_autostart(enabled: bool) -> Result<()> { @@ -44,7 +45,7 @@ pub(crate) fn show_update_notification( /// Show a notification in the bottom right of the screen pub(crate) fn show_notification(title: &str, body: &str) -> Result<()> { - Notification::new(connlib_shared::BUNDLE_ID) + Notification::new(BUNDLE_ID) .title(title) .body(body) .show()?; diff --git a/rust/gui-client/src-tauri/src/client/gui/os_windows.rs b/rust/gui-client/src-tauri/src/client/gui/os_windows.rs index 5a298ff3f..3f3d523cc 100644 --- a/rust/gui-client/src-tauri/src/client/gui/os_windows.rs +++ b/rust/gui-client/src-tauri/src/client/gui/os_windows.rs @@ -1,6 +1,6 @@ use super::{ControllerRequest, CtlrTx}; use anyhow::{Context, Result}; -use connlib_shared::BUNDLE_ID; +use firezone_bin_shared::BUNDLE_ID; #[allow(clippy::unused_async)] pub(crate) async fn set_autostart(_enabled: bool) -> Result<()> { diff --git a/rust/headless-client/Cargo.toml b/rust/headless-client/Cargo.toml index 4bd5c8ca6..a8ee398f5 100644 --- a/rust/headless-client/Cargo.toml +++ b/rust/headless-client/Cargo.toml @@ -54,7 +54,6 @@ dirs = "5.0.1" [target.'cfg(target_os = "windows")'.dependencies] ipconfig = "0.3.2" known-folders = "1.1.0" -ring = "0.17" thiserror = { version = "1.0", default-features = false } tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } windows-service = "0.7.0" diff --git a/rust/headless-client/src/dns_control/windows.rs b/rust/headless-client/src/dns_control/windows.rs index 791056b50..a7d8e2ec4 100644 --- a/rust/headless-client/src/dns_control/windows.rs +++ b/rust/headless-client/src/dns_control/windows.rs @@ -14,7 +14,7 @@ //! use anyhow::{Context as _, Result}; -use connlib_shared::windows::CREATE_NO_WINDOW; +use firezone_bin_shared::windows::CREATE_NO_WINDOW; use std::{net::IpAddr, os::windows::process::CommandExt, path::Path, process::Command}; pub fn system_resolvers_for_gui() -> Result> { diff --git a/rust/headless-client/src/ipc_service/ipc/linux.rs b/rust/headless-client/src/ipc_service/ipc/linux.rs index f334dfbc6..79d6ca4b1 100644 --- a/rust/headless-client/src/ipc_service/ipc/linux.rs +++ b/rust/headless-client/src/ipc_service/ipc/linux.rs @@ -1,5 +1,6 @@ use super::{Error, ServiceId}; use anyhow::{anyhow, Context as _, Result}; +use firezone_bin_shared::BUNDLE_ID; use std::{io::ErrorKind, os::unix::fs::PermissionsExt, path::PathBuf}; use tokio::net::{UnixListener, UnixStream}; @@ -88,9 +89,7 @@ impl Server { /// Test sockets live in e.g. `/run/user/1000/dev.firezone.client/data/` fn ipc_path(id: ServiceId) -> PathBuf { match id { - ServiceId::Prod => PathBuf::from("/run") - .join(connlib_shared::BUNDLE_ID) - .join("ipc.sock"), + ServiceId::Prod => PathBuf::from("/run").join(BUNDLE_ID).join("ipc.sock"), ServiceId::Test(id) => crate::known_dirs::runtime() .expect("`known_dirs::runtime()` should always work") .join(format!("ipc_test_{id}.sock")), diff --git a/rust/headless-client/src/ipc_service/ipc/windows.rs b/rust/headless-client/src/ipc_service/ipc/windows.rs index 34504b309..aa195843c 100644 --- a/rust/headless-client/src/ipc_service/ipc/windows.rs +++ b/rust/headless-client/src/ipc_service/ipc/windows.rs @@ -1,6 +1,6 @@ use super::{Error, ServiceId}; use anyhow::{bail, Context as _, Result}; -use connlib_shared::BUNDLE_ID; +use firezone_bin_shared::BUNDLE_ID; use std::{ffi::c_void, io::ErrorKind, os::windows::io::AsRawHandle, time::Duration}; use tokio::net::windows::named_pipe; use windows::Win32::{ @@ -49,7 +49,6 @@ impl Server { /// This is async on Linux #[allow(clippy::unused_async)] pub(crate) async fn new(id: ServiceId) -> Result { - crate::platform::setup_before_connlib()?; let pipe_path = ipc_path(id); Ok(Self { pipe_path }) } diff --git a/rust/headless-client/src/known_dirs/linux.rs b/rust/headless-client/src/known_dirs/linux.rs index 4e6e3cb35..257e55cbf 100644 --- a/rust/headless-client/src/known_dirs/linux.rs +++ b/rust/headless-client/src/known_dirs/linux.rs @@ -1,4 +1,4 @@ -use connlib_shared::BUNDLE_ID; +use firezone_bin_shared::BUNDLE_ID; use std::path::PathBuf; /// Path for IPC service config that either the IPC service or GUI can write diff --git a/rust/headless-client/src/known_dirs/windows.rs b/rust/headless-client/src/known_dirs/windows.rs index 7e3357a39..3c3302704 100644 --- a/rust/headless-client/src/known_dirs/windows.rs +++ b/rust/headless-client/src/known_dirs/windows.rs @@ -1,4 +1,4 @@ -use connlib_shared::BUNDLE_ID; +use firezone_bin_shared::{windows::app_local_data_dir, BUNDLE_ID}; use known_folders::{get_known_folder_path, KnownFolder}; use std::path::PathBuf; @@ -13,7 +13,7 @@ use std::path::PathBuf; pub fn ipc_service_config() -> Option { Some( get_known_folder_path(KnownFolder::ProgramData)? - .join(connlib_shared::BUNDLE_ID) + .join(BUNDLE_ID) .join("config"), ) } @@ -31,43 +31,26 @@ pub fn ipc_service_logs() -> Option { /// /// See connlib docs for details pub fn logs() -> Option { - Some( - connlib_shared::windows::app_local_data_dir() - .ok()? - .join("data") - .join("logs"), - ) + Some(app_local_data_dir().ok()?.join("data").join("logs")) } /// e.g. `C:\Users\Alice\AppData\Local\dev.firezone.client\data` /// /// Crash handler socket and other temp files go here pub fn runtime() -> Option { - Some( - connlib_shared::windows::app_local_data_dir() - .ok()? - .join("data"), - ) + Some(app_local_data_dir().ok()?.join("data")) } /// e.g. `C:\Users\Alice\AppData\Local\dev.firezone.client\data` /// /// Things like actor name go here pub fn session() -> Option { - Some( - connlib_shared::windows::app_local_data_dir() - .ok()? - .join("data"), - ) + Some(app_local_data_dir().ok()?.join("data")) } /// e.g. `C:\Users\Alice\AppData\Local\dev.firezone.client\config` /// /// See connlib docs for details pub fn settings() -> Option { - Some( - connlib_shared::windows::app_local_data_dir() - .ok()? - .join("config"), - ) + Some(app_local_data_dir().ok()?.join("config")) } diff --git a/rust/headless-client/src/linux.rs b/rust/headless-client/src/linux.rs index 22ca47393..390b94c82 100644 --- a/rust/headless-client/src/linux.rs +++ b/rust/headless-client/src/linux.rs @@ -2,7 +2,7 @@ use super::TOKEN_ENV_KEY; use anyhow::{bail, Result}; -use firezone_bin_shared::FIREZONE_MARK; +use firezone_bin_shared::{BUNDLE_ID, FIREZONE_MARK}; use nix::sys::socket::{setsockopt, sockopt}; use socket_factory::{TcpSocket, UdpSocket}; use std::{ @@ -29,9 +29,7 @@ pub(crate) fn udp_socket_factory(socket_addr: &SocketAddr) -> io::Result PathBuf { - PathBuf::from("/etc") - .join(connlib_shared::BUNDLE_ID) - .join("token") + PathBuf::from("/etc").join(BUNDLE_ID).join("token") } pub(crate) fn check_token_permissions(path: &Path) -> Result<()> { @@ -68,11 +66,3 @@ pub(crate) fn check_token_permissions(path: &Path) -> Result<()> { pub(crate) fn notify_service_controller() -> Result<()> { Ok(sd_notify::notify(true, &[sd_notify::NotifyState::Ready])?) } - -/// Platform-specific setup needed for connlib -/// -/// On Linux this does nothing -#[allow(clippy::unnecessary_wraps)] -pub(crate) fn setup_before_connlib() -> Result<()> { - Ok(()) -} diff --git a/rust/headless-client/src/standalone.rs b/rust/headless-client/src/standalone.rs index 682c6a37e..a6576e20d 100644 --- a/rust/headless-client/src/standalone.rs +++ b/rust/headless-client/src/standalone.rs @@ -164,7 +164,6 @@ pub fn run_only_headless_client() -> Result<()> { // The name matches that in `ipc_service.rs` let mut last_connlib_start_instant = Some(Instant::now()); - platform::setup_before_connlib()?; let args = ConnectArgs { udp_socket_factory: Arc::new(crate::udp_socket_factory), tcp_socket_factory: Arc::new(crate::tcp_socket_factory), diff --git a/rust/headless-client/src/windows.rs b/rust/headless-client/src/windows.rs index 048075586..cb7e3f405 100644 --- a/rust/headless-client/src/windows.rs +++ b/rust/headless-client/src/windows.rs @@ -10,9 +10,6 @@ use std::path::{Path, PathBuf}; pub(crate) use socket_factory::tcp as tcp_socket_factory; pub(crate) use socket_factory::udp as udp_socket_factory; -#[path = "windows/wintun_install.rs"] -mod wintun_install; - // The return value is useful on Linux #[allow(clippy::unnecessary_wraps)] pub(crate) fn check_token_permissions(_path: &Path) -> Result<()> { @@ -32,8 +29,3 @@ pub(crate) fn default_token_path() -> std::path::PathBuf { pub(crate) fn notify_service_controller() -> Result<()> { Ok(()) } - -pub(crate) fn setup_before_connlib() -> Result<()> { - wintun_install::ensure_dll()?; - Ok(()) -} diff --git a/rust/headless-client/src/windows/wintun_install.rs b/rust/headless-client/src/windows/wintun_install.rs deleted file mode 100644 index fca164154..000000000 --- a/rust/headless-client/src/windows/wintun_install.rs +++ /dev/null @@ -1,97 +0,0 @@ -//! "Installs" wintun.dll at runtime by copying it into whatever folder the exe is in - -use connlib_shared::windows::wintun_dll_path; -use ring::digest; -use std::{ - fs, - io::{self, Read}, - path::{Path, PathBuf}, -}; - -struct DllBytes { - /// Bytes embedded in the client with `include_bytes` - bytes: &'static [u8], - /// Expected SHA256 hash - expected_sha256: &'static str, -} - -#[derive(thiserror::Error, Debug)] -pub(crate) enum Error { - #[error("Can't compute path where wintun.dll should be installed")] - CantComputeWintunPath, - #[error("create_dir_all failed")] - CreateDirAll, - #[error("Computed DLL path is invalid")] - DllPathInvalid, - #[error("permission denied")] - PermissionDenied, - #[error("write failed: `{0:?}`")] - WriteFailed(io::Error), -} - -/// Installs the DLL in %LOCALAPPDATA% and returns the DLL's absolute path -/// -/// e.g. `C:\Users\User\AppData\Local\dev.firezone.client\data\wintun.dll` -/// Also verifies the SHA256 of the DLL on-disk with the expected bytes packed into the exe -pub(crate) fn ensure_dll() -> Result { - let dll_bytes = get_dll_bytes(); - - let path = wintun_dll_path().map_err(|_| Error::CantComputeWintunPath)?; - // The DLL path should always have a parent - let dir = path.parent().ok_or(Error::DllPathInvalid)?; - std::fs::create_dir_all(dir).map_err(|_| Error::CreateDirAll)?; - - tracing::debug!(?path, "wintun.dll path"); - - // This hash check is not meant to protect against attacks. It only lets us skip redundant disk writes, and it updates the DLL if needed. - // `tun_windows.rs` in connlib, and `elevation.rs`, rely on thia. - if !dll_already_exists(&path, &dll_bytes) { - fs::write(&path, dll_bytes.bytes).map_err(|e| { - #[allow(clippy::wildcard_enum_match_arm)] - match e.kind() { - io::ErrorKind::PermissionDenied => Error::PermissionDenied, - _ => Error::WriteFailed(e), - } - })?; - } - Ok(path) -} - -fn dll_already_exists(path: &Path, dll_bytes: &DllBytes) -> bool { - let mut f = match fs::File::open(path) { - Err(_) => return false, - Ok(x) => x, - }; - - let actual_len = usize::try_from(f.metadata().unwrap().len()).unwrap(); - let expected_len = dll_bytes.bytes.len(); - // If the dll is 100 MB instead of 0.5 MB, this allows us to skip a 100 MB read - if actual_len != expected_len { - return false; - } - - let mut buf = vec![0u8; expected_len]; - if f.read_exact(&mut buf).is_err() { - return false; - } - - let expected = ring::test::from_hex(dll_bytes.expected_sha256).unwrap(); - let actual = digest::digest(&digest::SHA256, &buf); - expected == actual.as_ref() -} - -#[cfg(target_arch = "x86_64")] -fn get_dll_bytes() -> DllBytes { - DllBytes { - bytes: include_bytes!("wintun/bin/amd64/wintun.dll"), - expected_sha256: "e5da8447dc2c320edc0fc52fa01885c103de8c118481f683643cacc3220dafce", - } -} - -#[cfg(target_arch = "aarch64")] -fn get_dll_bytes() -> DllBytes { - DllBytes { - bytes: include_bytes!("wintun/bin/arm64/wintun.dll"), - expected_sha256: "f7ba89005544be9d85231a9e0d5f23b2d15b3311667e2dad0debd344918a3f80", - } -}