From d5d92cfbee10f3349c16365c83e9a2ded4acada1 Mon Sep 17 00:00:00 2001 From: Kevin Yue Date: Sat, 16 Mar 2024 21:06:49 +0800 Subject: [PATCH] Ensure vpnc_script and csd_wrapper executable --- Cargo.lock | 9 +++- apps/gpclient/src/connect.rs | 4 +- apps/gpservice/src/vpn_task.rs | 19 +++++--- crates/common/Cargo.toml | 11 +++++ crates/common/src/lib.rs | 1 + crates/common/src/vpn_utils.rs | 41 ++++++++++++++++++ crates/openconnect/Cargo.toml | 2 +- crates/openconnect/src/lib.rs | 1 - crates/openconnect/src/vpn.rs | 62 ++++++++++++++++++++++----- crates/openconnect/src/vpnc_script.rs | 24 ----------- 10 files changed, 128 insertions(+), 46 deletions(-) create mode 100644 crates/common/Cargo.toml create mode 100644 crates/common/src/lib.rs create mode 100644 crates/common/src/vpn_utils.rs delete mode 100644 crates/openconnect/src/vpnc_script.rs diff --git a/Cargo.lock b/Cargo.lock index d72932c..315e2b1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -562,6 +562,13 @@ dependencies = [ "memchr", ] +[[package]] +name = "common" +version = "2.1.0" +dependencies = [ + "is_executable", +] + [[package]] name = "compile-time" version = "0.2.0" @@ -2522,7 +2529,7 @@ name = "openconnect" version = "2.1.0" dependencies = [ "cc", - "is_executable", + "common", "log", ] diff --git a/apps/gpclient/src/connect.rs b/apps/gpclient/src/connect.rs index 309708a..c07dbe0 100644 --- a/apps/gpclient/src/connect.rs +++ b/apps/gpclient/src/connect.rs @@ -158,12 +158,12 @@ impl<'a> ConnectHandler<'a> { let mtu = self.args.mtu.unwrap_or(0); let vpn = Vpn::builder(gateway, cookie) - .user_agent(self.args.user_agent.clone()) .script(self.args.script.clone()) + .user_agent(self.args.user_agent.clone()) .csd_uid(csd_uid) .csd_wrapper(self.args.csd_wrapper.clone()) .mtu(mtu) - .build(); + .build()?; let vpn = Arc::new(vpn); let vpn_clone = vpn.clone(); diff --git a/apps/gpservice/src/vpn_task.rs b/apps/gpservice/src/vpn_task.rs index 62ab5ec..80fc392 100644 --- a/apps/gpservice/src/vpn_task.rs +++ b/apps/gpservice/src/vpn_task.rs @@ -4,7 +4,7 @@ use gpapi::service::{ request::{ConnectRequest, WsRequest}, vpn_state::VpnState, }; -use log::info; +use log::{info, warn}; use openconnect::Vpn; use tokio::sync::{mpsc, oneshot, watch, RwLock}; use tokio_util::sync::CancellationToken; @@ -31,22 +31,29 @@ impl VpnTaskContext { return; } + let vpn_state_tx = self.vpn_state_tx.clone(); let info = req.info().clone(); let vpn_handle = Arc::clone(&self.vpn_handle); let args = req.args(); - let vpn = Vpn::builder(req.gateway().server(), args.cookie()) - .user_agent(args.user_agent()) + let vpn = match Vpn::builder(req.gateway().server(), args.cookie()) .script(args.vpnc_script()) + .user_agent(args.user_agent()) .csd_uid(args.csd_uid()) .csd_wrapper(args.csd_wrapper()) .mtu(args.mtu()) .os(args.openconnect_os()) - .build(); + .build() + { + Ok(vpn) => vpn, + Err(err) => { + warn!("Failed to create VPN: {}", err); + vpn_state_tx.send(VpnState::Disconnected).ok(); + return; + } + }; // Save the VPN handle vpn_handle.write().await.replace(vpn); - - let vpn_state_tx = self.vpn_state_tx.clone(); let connect_info = Box::new(info.clone()); vpn_state_tx.send(VpnState::Connecting(connect_info)).ok(); diff --git a/crates/common/Cargo.toml b/crates/common/Cargo.toml new file mode 100644 index 0000000..864b265 --- /dev/null +++ b/crates/common/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "common" +rust-version.workspace = true +version.workspace = true +authors.workspace = true +homepage.workspace = true +edition.workspace = true +license.workspace = true + +[dependencies] +is_executable.workspace = true diff --git a/crates/common/src/lib.rs b/crates/common/src/lib.rs new file mode 100644 index 0000000..f40aebc --- /dev/null +++ b/crates/common/src/lib.rs @@ -0,0 +1 @@ +pub mod vpn_utils; diff --git a/crates/common/src/vpn_utils.rs b/crates/common/src/vpn_utils.rs new file mode 100644 index 0000000..c6342ad --- /dev/null +++ b/crates/common/src/vpn_utils.rs @@ -0,0 +1,41 @@ +use is_executable::IsExecutable; +use std::path::Path; + +pub use is_executable::is_executable; + +const VPNC_SCRIPT_LOCATIONS: [&str; 6] = [ + "/usr/local/share/vpnc-scripts/vpnc-script", + "/usr/local/sbin/vpnc-script", + "/usr/share/vpnc-scripts/vpnc-script", + "/usr/sbin/vpnc-script", + "/etc/vpnc/vpnc-script", + "/etc/openconnect/vpnc-script", +]; + +const CSD_WRAPPER_LOCATIONS: [&str; 3] = [ + #[cfg(target_arch = "x86_64")] + "/usr/lib/x86_64-linux-gnu/openconnect/hipreport.sh", + #[cfg(target_arch = "aarch64")] + "/usr/lib/aarch64-linux-gnu/openconnect/hipreport.sh", + "/usr/lib/openconnect/hipreport.sh", + "/usr/libexec/openconnect/hipreport.sh", +]; + +fn find_executable(locations: &[&str]) -> Option { + for location in locations.iter() { + let path = Path::new(location); + if path.is_executable() { + return Some(location.to_string()); + } + } + + None +} + +pub fn find_vpnc_script() -> Option { + find_executable(&VPNC_SCRIPT_LOCATIONS) +} + +pub fn find_csd_wrapper() -> Option { + find_executable(&CSD_WRAPPER_LOCATIONS) +} diff --git a/crates/openconnect/Cargo.toml b/crates/openconnect/Cargo.toml index d3216a9..dc5e504 100644 --- a/crates/openconnect/Cargo.toml +++ b/crates/openconnect/Cargo.toml @@ -6,8 +6,8 @@ license.workspace = true links = "openconnect" [dependencies] +common = { path = "../common" } log.workspace = true -is_executable.workspace = true [build-dependencies] cc = "1" diff --git a/crates/openconnect/src/lib.rs b/crates/openconnect/src/lib.rs index 80977a6..ebd900d 100644 --- a/crates/openconnect/src/lib.rs +++ b/crates/openconnect/src/lib.rs @@ -1,5 +1,4 @@ mod ffi; mod vpn; -mod vpnc_script; pub use vpn::*; diff --git a/crates/openconnect/src/vpn.rs b/crates/openconnect/src/vpn.rs index cde0017..5ba8781 100644 --- a/crates/openconnect/src/vpn.rs +++ b/crates/openconnect/src/vpn.rs @@ -1,11 +1,13 @@ use std::{ ffi::{c_char, CString}, + fmt, sync::{Arc, RwLock}, }; +use common::vpn_utils::{find_vpnc_script, is_executable}; use log::info; -use crate::{ffi, vpnc_script::find_default_vpnc_script}; +use crate::ffi; type OnConnectedCallback = Arc>>>; @@ -77,11 +79,31 @@ impl Vpn { } } +#[derive(Debug)] +pub struct VpnError<'a> { + message: &'a str, +} + +impl<'a> VpnError<'a> { + fn new(message: &'a str) -> Self { + Self { message } + } +} + +impl fmt::Display for VpnError<'_> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{}", self.message) + } +} + +impl std::error::Error for VpnError<'_> {} + pub struct VpnBuilder { server: String, cookie: String, - user_agent: Option, script: Option, + + user_agent: Option, os: Option, csd_uid: u32, @@ -95,22 +117,25 @@ impl VpnBuilder { Self { server: server.to_string(), cookie: cookie.to_string(), - user_agent: None, script: None, + + user_agent: None, os: None, + csd_uid: 0, csd_wrapper: None, + mtu: 0, } } - pub fn user_agent>>(mut self, user_agent: T) -> Self { - self.user_agent = user_agent.into(); + pub fn script>>(mut self, script: T) -> Self { + self.script = script.into(); self } - pub fn script>>(mut self, script: T) -> Self { - self.script = script.into(); + pub fn user_agent>>(mut self, user_agent: T) -> Self { + self.user_agent = user_agent.into(); self } @@ -134,12 +159,27 @@ impl VpnBuilder { self } - pub fn build(self) -> Vpn { + pub fn build(self) -> Result> { + let script = match self.script { + Some(script) => { + if !is_executable(&script) { + return Err(VpnError::new("vpnc script is not executable")); + } + script + } + None => find_vpnc_script().ok_or_else(|| VpnError::new("Failed to find vpnc-script"))?, + }; + + if let Some(csd_wrapper) = &self.csd_wrapper { + if !is_executable(csd_wrapper) { + return Err(VpnError::new("CSD wrapper is not executable")); + } + } + let user_agent = self.user_agent.unwrap_or_default(); - let script = self.script.or_else(find_default_vpnc_script).unwrap_or_default(); let os = self.os.unwrap_or("linux".to_string()); - Vpn { + Ok(Vpn { server: Self::to_cstring(&self.server), cookie: Self::to_cstring(&self.cookie), user_agent: Self::to_cstring(&user_agent), @@ -154,7 +194,7 @@ impl VpnBuilder { mtu: self.mtu, callback: Default::default(), - } + }) } fn to_cstring(value: &str) -> CString { diff --git a/crates/openconnect/src/vpnc_script.rs b/crates/openconnect/src/vpnc_script.rs deleted file mode 100644 index a964948..0000000 --- a/crates/openconnect/src/vpnc_script.rs +++ /dev/null @@ -1,24 +0,0 @@ -use is_executable::IsExecutable; -use std::path::Path; - -const VPNC_SCRIPT_LOCATIONS: [&str; 6] = [ - "/usr/local/share/vpnc-scripts/vpnc-script", - "/usr/local/sbin/vpnc-script", - "/usr/share/vpnc-scripts/vpnc-script", - "/usr/sbin/vpnc-script", - "/etc/vpnc/vpnc-script", - "/etc/openconnect/vpnc-script" -]; - -pub(crate) fn find_default_vpnc_script() -> Option { - for location in VPNC_SCRIPT_LOCATIONS.iter() { - let path = Path::new(location); - if path.is_executable() { - return Some(location.to_string()); - } - } - - log::warn!("vpnc-script not found"); - - None -}