mirror of
https://github.com/yuezk/GlobalProtect-openconnect.git
synced 2025-04-02 18:31:50 -04:00
Ensure vpnc_script and csd_wrapper executable
This commit is contained in:
parent
a00f6a8cba
commit
d5d92cfbee
9
Cargo.lock
generated
9
Cargo.lock
generated
@ -562,6 +562,13 @@ dependencies = [
|
|||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "common"
|
||||||
|
version = "2.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"is_executable",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "compile-time"
|
name = "compile-time"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
@ -2522,7 +2529,7 @@ name = "openconnect"
|
|||||||
version = "2.1.0"
|
version = "2.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"is_executable",
|
"common",
|
||||||
"log",
|
"log",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -158,12 +158,12 @@ impl<'a> ConnectHandler<'a> {
|
|||||||
let mtu = self.args.mtu.unwrap_or(0);
|
let mtu = self.args.mtu.unwrap_or(0);
|
||||||
|
|
||||||
let vpn = Vpn::builder(gateway, cookie)
|
let vpn = Vpn::builder(gateway, cookie)
|
||||||
.user_agent(self.args.user_agent.clone())
|
|
||||||
.script(self.args.script.clone())
|
.script(self.args.script.clone())
|
||||||
|
.user_agent(self.args.user_agent.clone())
|
||||||
.csd_uid(csd_uid)
|
.csd_uid(csd_uid)
|
||||||
.csd_wrapper(self.args.csd_wrapper.clone())
|
.csd_wrapper(self.args.csd_wrapper.clone())
|
||||||
.mtu(mtu)
|
.mtu(mtu)
|
||||||
.build();
|
.build()?;
|
||||||
|
|
||||||
let vpn = Arc::new(vpn);
|
let vpn = Arc::new(vpn);
|
||||||
let vpn_clone = vpn.clone();
|
let vpn_clone = vpn.clone();
|
||||||
|
@ -4,7 +4,7 @@ use gpapi::service::{
|
|||||||
request::{ConnectRequest, WsRequest},
|
request::{ConnectRequest, WsRequest},
|
||||||
vpn_state::VpnState,
|
vpn_state::VpnState,
|
||||||
};
|
};
|
||||||
use log::info;
|
use log::{info, warn};
|
||||||
use openconnect::Vpn;
|
use openconnect::Vpn;
|
||||||
use tokio::sync::{mpsc, oneshot, watch, RwLock};
|
use tokio::sync::{mpsc, oneshot, watch, RwLock};
|
||||||
use tokio_util::sync::CancellationToken;
|
use tokio_util::sync::CancellationToken;
|
||||||
@ -31,22 +31,29 @@ impl VpnTaskContext {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let vpn_state_tx = self.vpn_state_tx.clone();
|
||||||
let info = req.info().clone();
|
let info = req.info().clone();
|
||||||
let vpn_handle = Arc::clone(&self.vpn_handle);
|
let vpn_handle = Arc::clone(&self.vpn_handle);
|
||||||
let args = req.args();
|
let args = req.args();
|
||||||
let vpn = Vpn::builder(req.gateway().server(), args.cookie())
|
let vpn = match Vpn::builder(req.gateway().server(), args.cookie())
|
||||||
.user_agent(args.user_agent())
|
|
||||||
.script(args.vpnc_script())
|
.script(args.vpnc_script())
|
||||||
|
.user_agent(args.user_agent())
|
||||||
.csd_uid(args.csd_uid())
|
.csd_uid(args.csd_uid())
|
||||||
.csd_wrapper(args.csd_wrapper())
|
.csd_wrapper(args.csd_wrapper())
|
||||||
.mtu(args.mtu())
|
.mtu(args.mtu())
|
||||||
.os(args.openconnect_os())
|
.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
|
// Save the VPN handle
|
||||||
vpn_handle.write().await.replace(vpn);
|
vpn_handle.write().await.replace(vpn);
|
||||||
|
|
||||||
let vpn_state_tx = self.vpn_state_tx.clone();
|
|
||||||
let connect_info = Box::new(info.clone());
|
let connect_info = Box::new(info.clone());
|
||||||
vpn_state_tx.send(VpnState::Connecting(connect_info)).ok();
|
vpn_state_tx.send(VpnState::Connecting(connect_info)).ok();
|
||||||
|
|
||||||
|
11
crates/common/Cargo.toml
Normal file
11
crates/common/Cargo.toml
Normal file
@ -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
|
1
crates/common/src/lib.rs
Normal file
1
crates/common/src/lib.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub mod vpn_utils;
|
41
crates/common/src/vpn_utils.rs
Normal file
41
crates/common/src/vpn_utils.rs
Normal file
@ -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<String> {
|
||||||
|
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<String> {
|
||||||
|
find_executable(&VPNC_SCRIPT_LOCATIONS)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn find_csd_wrapper() -> Option<String> {
|
||||||
|
find_executable(&CSD_WRAPPER_LOCATIONS)
|
||||||
|
}
|
@ -6,8 +6,8 @@ license.workspace = true
|
|||||||
links = "openconnect"
|
links = "openconnect"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
common = { path = "../common" }
|
||||||
log.workspace = true
|
log.workspace = true
|
||||||
is_executable.workspace = true
|
|
||||||
|
|
||||||
[build-dependencies]
|
[build-dependencies]
|
||||||
cc = "1"
|
cc = "1"
|
||||||
|
@ -1,5 +1,4 @@
|
|||||||
mod ffi;
|
mod ffi;
|
||||||
mod vpn;
|
mod vpn;
|
||||||
mod vpnc_script;
|
|
||||||
|
|
||||||
pub use vpn::*;
|
pub use vpn::*;
|
||||||
|
@ -1,11 +1,13 @@
|
|||||||
use std::{
|
use std::{
|
||||||
ffi::{c_char, CString},
|
ffi::{c_char, CString},
|
||||||
|
fmt,
|
||||||
sync::{Arc, RwLock},
|
sync::{Arc, RwLock},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use common::vpn_utils::{find_vpnc_script, is_executable};
|
||||||
use log::info;
|
use log::info;
|
||||||
|
|
||||||
use crate::{ffi, vpnc_script::find_default_vpnc_script};
|
use crate::ffi;
|
||||||
|
|
||||||
type OnConnectedCallback = Arc<RwLock<Option<Box<dyn FnOnce() + 'static + Send + Sync>>>>;
|
type OnConnectedCallback = Arc<RwLock<Option<Box<dyn FnOnce() + 'static + Send + Sync>>>>;
|
||||||
|
|
||||||
@ -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 {
|
pub struct VpnBuilder {
|
||||||
server: String,
|
server: String,
|
||||||
cookie: String,
|
cookie: String,
|
||||||
user_agent: Option<String>,
|
|
||||||
script: Option<String>,
|
script: Option<String>,
|
||||||
|
|
||||||
|
user_agent: Option<String>,
|
||||||
os: Option<String>,
|
os: Option<String>,
|
||||||
|
|
||||||
csd_uid: u32,
|
csd_uid: u32,
|
||||||
@ -95,22 +117,25 @@ impl VpnBuilder {
|
|||||||
Self {
|
Self {
|
||||||
server: server.to_string(),
|
server: server.to_string(),
|
||||||
cookie: cookie.to_string(),
|
cookie: cookie.to_string(),
|
||||||
user_agent: None,
|
|
||||||
script: None,
|
script: None,
|
||||||
|
|
||||||
|
user_agent: None,
|
||||||
os: None,
|
os: None,
|
||||||
|
|
||||||
csd_uid: 0,
|
csd_uid: 0,
|
||||||
csd_wrapper: None,
|
csd_wrapper: None,
|
||||||
|
|
||||||
mtu: 0,
|
mtu: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn user_agent<T: Into<Option<String>>>(mut self, user_agent: T) -> Self {
|
pub fn script<T: Into<Option<String>>>(mut self, script: T) -> Self {
|
||||||
self.user_agent = user_agent.into();
|
self.script = script.into();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn script<T: Into<Option<String>>>(mut self, script: T) -> Self {
|
pub fn user_agent<T: Into<Option<String>>>(mut self, user_agent: T) -> Self {
|
||||||
self.script = script.into();
|
self.user_agent = user_agent.into();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -134,12 +159,27 @@ impl VpnBuilder {
|
|||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build(self) -> Vpn {
|
pub fn build(self) -> Result<Vpn, VpnError<'static>> {
|
||||||
|
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 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());
|
let os = self.os.unwrap_or("linux".to_string());
|
||||||
|
|
||||||
Vpn {
|
Ok(Vpn {
|
||||||
server: Self::to_cstring(&self.server),
|
server: Self::to_cstring(&self.server),
|
||||||
cookie: Self::to_cstring(&self.cookie),
|
cookie: Self::to_cstring(&self.cookie),
|
||||||
user_agent: Self::to_cstring(&user_agent),
|
user_agent: Self::to_cstring(&user_agent),
|
||||||
@ -154,7 +194,7 @@ impl VpnBuilder {
|
|||||||
mtu: self.mtu,
|
mtu: self.mtu,
|
||||||
|
|
||||||
callback: Default::default(),
|
callback: Default::default(),
|
||||||
}
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_cstring(value: &str) -> CString {
|
fn to_cstring(value: &str) -> CString {
|
||||||
|
@ -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<String> {
|
|
||||||
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
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user