From 11a374765cfceb820b9c44b21cf1350ab97446a7 Mon Sep 17 00:00:00 2001 From: Kevin Yue Date: Sat, 26 Aug 2023 10:02:08 +0800 Subject: [PATCH] refactor: read extra args from gp.conf --- .vscode/settings.json | 6 ++ Cargo.lock | 165 +++++++++++++++++++++----------- gpcommon/Cargo.toml | 5 + gpcommon/src/vpn/ffi.rs | 17 ++-- gpcommon/src/vpn/gpconf.rs | 164 +++++++++++++++++++++++++++++++ gpcommon/src/vpn/mod.rs | 55 +++-------- gpcommon/src/vpn/vpn.c | 11 +++ gpcommon/src/vpn/vpn.h | 5 +- gpcommon/src/vpn/vpn_options.rs | 93 ++++++++++++++++++ 9 files changed, 416 insertions(+), 105 deletions(-) create mode 100644 gpcommon/src/vpn/gpconf.rs create mode 100644 gpcommon/src/vpn/vpn_options.rs diff --git a/.vscode/settings.json b/.vscode/settings.json index 69e62d3..0cff074 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,15 +5,18 @@ "clickaway", "clientgpversion", "clientos", + "configparser", "consts", "devicename", "distro", "gpcommon", + "gpconf", "gpgui", "gpservice", "humantime", "Immer", "jnlp", + "lexopt", "notistack", "oneshot", "openconnect", @@ -23,8 +26,11 @@ "prelogonuserauthcookie", "repr", "rustc", + "servercert", + "shlex", "tauri", "tempdir", + "tempfile", "thiserror", "unlisten", "userauthcookie", diff --git a/Cargo.lock b/Cargo.lock index c797961..bad8cc0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -149,7 +149,7 @@ dependencies = [ "async-lock", "async-task", "concurrent-queue", - "fastrand", + "fastrand 1.8.0", "futures-lite", "slab", ] @@ -180,7 +180,7 @@ dependencies = [ "log", "parking", "polling", - "rustix", + "rustix 0.37.3", "slab", "socket2", "waker-fn", @@ -208,7 +208,7 @@ dependencies = [ "cfg-if", "event-listener", "futures-lite", - "rustix", + "rustix 0.37.3", "signal-hook", "windows-sys 0.48.0", ] @@ -248,7 +248,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c3d816ce6f0e2909a96830d6911c2aff044370b1ef92d7f267b43bae5addedd" dependencies = [ "atk-sys", - "bitflags", + "bitflags 1.3.2", "glib", "libc", ] @@ -322,6 +322,12 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bitflags" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4682ae6287fcf752ecaabbfcc7b6f9b72aa33933dc23a554d853aea8eea8635" + [[package]] name = "block" version = "0.1.6" @@ -363,7 +369,7 @@ dependencies = [ "async-lock", "async-task", "atomic-waker", - "fastrand", + "fastrand 1.8.0", "futures-lite", "log", ] @@ -439,7 +445,7 @@ version = "0.15.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c76ee391b03d35510d9fa917357c7f1855bd9a6659c95a1b392e33f49b3369bc" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cairo-sys-rs", "glib", "libc", @@ -552,7 +558,7 @@ version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f425db7937052c684daec3bd6375c8abe2d146dca4b8b143d6db777c39138f3a" dependencies = [ - "bitflags", + "bitflags 1.3.2", "block", "cocoa-foundation", "core-foundation", @@ -568,7 +574,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ade49b65d560ca58c403a479bb396592b155c0185eada742ee323d1d68d6318" dependencies = [ - "bitflags", + "bitflags 1.3.2", "block", "core-foundation", "core-graphics-types", @@ -613,6 +619,12 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "configparser" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5458d9d1a587efaf5091602c59d299696a3877a439c8f6d461a2d3cce11df87a" + [[package]] name = "convert_case" version = "0.4.0" @@ -641,7 +653,7 @@ version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2581bbab3b8ffc6fcbd550bf46c355135d16e9ff2a6ea032ad6b9bf1d7efe4fb" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-graphics-types", "foreign-types", @@ -654,7 +666,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a68b68b3446082644c91ac778bf50cd4104bfb002b5a6a7c44cca5a2c70788b" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "foreign-types", "libc", @@ -975,6 +987,12 @@ dependencies = [ "instant", ] +[[package]] +name = "fastrand" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" + [[package]] name = "fern" version = "0.6.2" @@ -1003,7 +1021,7 @@ checksum = "8a3de6e8d11b22ff9edc6d916f890800597d60f8b2da1caf2955c274638d6412" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "windows-sys 0.45.0", ] @@ -1095,7 +1113,7 @@ version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49a9d51ce47660b1e808d3c990b4709f2f415d928835a17dfd16991515c46bce" dependencies = [ - "fastrand", + "fastrand 1.8.0", "futures-core", "futures-io", "memchr", @@ -1159,7 +1177,7 @@ version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6e05c1f572ab0e1f15be94217f0dc29088c248b14f792a5ff0af0d84bcda9e8" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cairo-rs", "gdk-pixbuf", "gdk-sys", @@ -1175,7 +1193,7 @@ version = "0.15.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ad38dd9cc8b099cceecdf41375bb6d481b1b5a7cd5cd603e10a69a9383f8619a" dependencies = [ - "bitflags", + "bitflags 1.3.2", "gdk-pixbuf-sys", "gio", "glib", @@ -1300,7 +1318,7 @@ version = "0.15.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68fdbc90312d462781a395f7a16d96a2b379bb6ef8cd6310a2df272771c4283b" dependencies = [ - "bitflags", + "bitflags 1.3.2", "futures-channel", "futures-core", "futures-io", @@ -1330,7 +1348,7 @@ version = "0.15.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edb0306fbad0ab5428b0ca674a23893db909a98582969c9b537be4ced78c505d" dependencies = [ - "bitflags", + "bitflags 1.3.2", "futures-channel", "futures-core", "futures-executor", @@ -1411,15 +1429,20 @@ dependencies = [ name = "gpcommon" version = "0.1.0" dependencies = [ + "anyhow", "async-trait", "bytes", "cc", + "configparser", "data-encoding", "is_executable", + "lexopt", "log", "ring", "serde", "serde_json", + "shlex", + "tempfile", "thiserror", "tokio", "tokio-util", @@ -1443,7 +1466,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92e3004a2d5d6d8b5057d2b57b3712c9529b62e82c77f25c1fecde1fd5c23bd0" dependencies = [ "atk", - "bitflags", + "bitflags 1.3.2", "cairo-rs", "field-offset", "futures-channel", @@ -1753,7 +1776,7 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bf053e7843f2812ff03ef5afe34bb9c06ffee120385caad4f6b9967fcd37d41c" dependencies = [ - "bitflags", + "bitflags 1.3.2", "glib", "javascriptcore-rs-sys", ] @@ -1844,10 +1867,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] -name = "libc" -version = "0.2.139" +name = "lexopt" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "baff4b617f7df3d896f97fe922b64817f6cd9a756bb81d40f8883f2f66dcb401" + +[[package]] +name = "libc" +version = "0.2.147" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "line-wrap" @@ -1864,7 +1893,7 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f27bb67f6dd1d0bb5ab582868e4f65052e58da6401188a08f0da09cf512b84b" dependencies = [ - "bitflags", + "bitflags 1.3.2", "libc", ] @@ -1874,6 +1903,12 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ece97ea872ece730aed82664c424eb4c8291e1ff2480247ccf7409044bc6479f" +[[package]] +name = "linux-raw-sys" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57bcfdad1b858c2db7c38303a6d2ad4dfaf5eb53dfeb0910128b2c26d6158503" + [[package]] name = "lock_api" version = "0.4.9" @@ -2022,7 +2057,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2032c77e030ddee34a6787a64166008da93f6a352b629261d0fee232b8742dd4" dependencies = [ - "bitflags", + "bitflags 1.3.2", "jni-sys", "ndk-sys", "num_enum", @@ -2056,7 +2091,7 @@ version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bfdda3d196821d6af13126e40375cdf7da646a96114af134d5f417a9a1dc8e1a" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "libc", "memoffset 0.7.1", @@ -2251,7 +2286,7 @@ version = "0.10.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b102428fd03bc5edf97f62620f7298614c45cedf287c271e7ed450bbaf83f2e1" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cfg-if", "foreign-types", "libc", @@ -2323,7 +2358,7 @@ version = "0.15.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22e4045548659aee5313bde6c582b0d83a627b7904dd20dc2d9ef0895d414e4f" dependencies = [ - "bitflags", + "bitflags 1.3.2", "glib", "libc", "once_cell", @@ -2366,7 +2401,7 @@ checksum = "9069cbb9f99e3a5083476ccb29ceb1de18b9118cafa53e90c9551235de2b9521" dependencies = [ "cfg-if", "libc", - "redox_syscall", + "redox_syscall 0.2.16", "smallvec", "windows-sys 0.45.0", ] @@ -2529,7 +2564,7 @@ version = "0.17.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d708eaf860a19b19ce538740d2b4bdeeb8337fa53f7738455e706623ad5c638" dependencies = [ - "bitflags", + "bitflags 1.3.2", "crc32fast", "flate2", "miniz_oxide", @@ -2542,7 +2577,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4b2d323e8ca7996b3e23126511a523f7e62924d93ecd5ae73b333815b0eb3dce" dependencies = [ "autocfg", - "bitflags", + "bitflags 1.3.2", "cfg-if", "concurrent-queue", "libc", @@ -2738,7 +2773,16 @@ version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" dependencies = [ - "bitflags", + "bitflags 1.3.2", +] + +[[package]] +name = "redox_syscall" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29" +dependencies = [ + "bitflags 1.3.2", ] [[package]] @@ -2748,7 +2792,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" dependencies = [ "getrandom 0.2.8", - "redox_syscall", + "redox_syscall 0.2.16", "thiserror", ] @@ -2778,15 +2822,6 @@ version = "0.6.28" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "456c603be3e8d448b072f410900c09faf164fbce2d480456f50eea6e25f9c848" -[[package]] -name = "remove_dir_all" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" -dependencies = [ - "winapi", -] - [[package]] name = "ring" version = "0.16.20" @@ -2826,14 +2861,27 @@ version = "0.37.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "62b24138615de35e32031d041a09032ef3487a616d901ca4db224e7d557efae2" dependencies = [ - "bitflags", + "bitflags 1.3.2", "errno", "io-lifetimes", "libc", - "linux-raw-sys", + "linux-raw-sys 0.3.7", "windows-sys 0.45.0", ] +[[package]] +name = "rustix" +version = "0.38.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ed4fa021d81c8392ce04db050a3da9a60299050b7ae1cf482d862b54a7218f" +dependencies = [ + "bitflags 2.4.0", + "errno", + "libc", + "linux-raw-sys 0.4.5", + "windows-sys 0.48.0", +] + [[package]] name = "rustversion" version = "1.0.11" @@ -2907,7 +2955,7 @@ version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a332be01508d814fed64bf28f798a146d73792121129962fdf335bb3c49a4254" dependencies = [ - "bitflags", + "bitflags 1.3.2", "core-foundation", "core-foundation-sys", "libc", @@ -2930,7 +2978,7 @@ version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df320f1889ac4ba6bc0cdc9c9af7af4bd64bb927bccdf32d81140dc1f9be12fe" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cssparser", "derive_more", "fxhash", @@ -3125,6 +3173,12 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shlex" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b2853a4d09f215c24cc5489c992ce46052d359b5109343cbafbf26bc62f8a3" + [[package]] name = "signal-hook" version = "0.3.16" @@ -3181,7 +3235,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2b4d76501d8ba387cf0fefbe055c3e0a59891d09f0f995ae4e4b16f6b60f3c0" dependencies = [ - "bitflags", + "bitflags 1.3.2", "gio", "glib", "libc", @@ -3195,7 +3249,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "009ef427103fcb17f802871647a7fa6c60cbb654b4c4e4c0ac60a31c5f6dc9cf" dependencies = [ - "bitflags", + "bitflags 1.3.2", "gio-sys", "glib-sys", "gobject-sys", @@ -3322,7 +3376,7 @@ version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a6d198e01085564cea63e976ad1566c1ba2c2e4cc79578e35d9f05521505e31" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cairo-rs", "cc", "cocoa", @@ -3601,16 +3655,15 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.3.0" +version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4" +checksum = "cb94d2f3cc536af71caac6b6fcebf65860b347e7ce0cc9ebe8f70d3e521054ef" dependencies = [ "cfg-if", - "fastrand", - "libc", - "redox_syscall", - "remove_dir_all", - "winapi", + "fastrand 2.0.0", + "redox_syscall 0.3.5", + "rustix 0.38.8", + "windows-sys 0.48.0", ] [[package]] @@ -4123,7 +4176,7 @@ version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8f859735e4a452aeb28c6c56a852967a8a76c8eb1cc32dbf931ad28a13d6370" dependencies = [ - "bitflags", + "bitflags 1.3.2", "cairo-rs", "gdk", "gdk-sys", @@ -4148,7 +4201,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d76ca6ecc47aeba01ec61e480139dda143796abcae6f83bcddf50d6b5b1dcf3" dependencies = [ "atk-sys", - "bitflags", + "bitflags 1.3.2", "cairo-sys-rs", "gdk-pixbuf-sys", "gdk-sys", diff --git a/gpcommon/Cargo.toml b/gpcommon/Cargo.toml index 665fef9..946dab6 100644 --- a/gpcommon/Cargo.toml +++ b/gpcommon/Cargo.toml @@ -17,6 +17,11 @@ ring = "0.16" data-encoding = "2.3" log = "0.4" is_executable = "1.0" +configparser = "3.0" +shlex = "1.0" +anyhow = "1.0" +tempfile = "3.8" +lexopt = "0.3.0" [build-dependencies] cc = "1.0" diff --git a/gpcommon/src/vpn/ffi.rs b/gpcommon/src/vpn/ffi.rs index 973522f..3cc97ec 100644 --- a/gpcommon/src/vpn/ffi.rs +++ b/gpcommon/src/vpn/ffi.rs @@ -1,21 +1,24 @@ use log::{debug, info, trace, warn}; -use std::ffi::c_void; +use std::ffi::{c_char, c_int, c_void}; use tokio::sync::mpsc; #[repr(C)] #[derive(Debug, Copy, Clone)] pub(crate) struct Options { - pub server: *const std::os::raw::c_char, - pub cookie: *const std::os::raw::c_char, - pub script: *const std::os::raw::c_char, - pub user_agent: *const std::os::raw::c_char, + pub server: *const c_char, + pub cookie: *const c_char, + pub user_agent: *const c_char, pub user_data: *mut c_void, + + pub script: *const c_char, + pub certificate: *const c_char, + pub servercert: *const c_char, } #[link(name = "vpn")] extern "C" { #[link_name = "vpn_connect"] - pub(crate) fn connect(options: *const Options) -> std::os::raw::c_int; + pub(crate) fn connect(options: *const Options) -> c_int; #[link_name = "vpn_disconnect"] pub(crate) fn disconnect(); @@ -33,7 +36,7 @@ extern "C" fn on_vpn_connected(value: i32, sender: *mut c_void) { // level: 0 = error, 1 = info, 2 = debug, 3 = trace // map the error level log in openconnect to the warning level #[no_mangle] -extern "C" fn vpn_log(level: i32, message: *const std::os::raw::c_char) { +extern "C" fn vpn_log(level: i32, message: *const c_char) { let message = unsafe { std::ffi::CStr::from_ptr(message) }; let message = message.to_str().unwrap_or("Invalid log message"); // Strip the trailing newline diff --git a/gpcommon/src/vpn/gpconf.rs b/gpcommon/src/vpn/gpconf.rs new file mode 100644 index 0000000..98e7666 --- /dev/null +++ b/gpcommon/src/vpn/gpconf.rs @@ -0,0 +1,164 @@ +use anyhow::{Error, Ok, Result}; +use configparser::ini::Ini; +use lexopt::Parser; +use log::warn; + +const GPCONF_PATH: &str = "/etc/gpservice/gp.conf"; +const DEFAULT_SECTION: &str = "*"; +const PROP_OPENCONNECT_ARGS: &str = "openconnect-args"; + +/// A struct representing the CLI arguments for the `openconnect` command. +/// Supports most of the options from the `openconnect` command line. +#[derive(Debug, Default)] +pub(crate) struct OpenconnectArgs { + script: Option, + certificate: Option, + servercert: Option, +} + +impl OpenconnectArgs { + pub fn script(&self) -> Option { + self.script.to_owned() + } + + pub fn certificate(&self) -> Option { + self.certificate.to_owned() + } + + pub fn servercert(&self) -> Option { + self.servercert.to_owned() + } +} + +/// Read the `gp.conf` file and return the `openconnect` arguments for the given server. +/// If the server is not found, the default section is used. +pub(crate) fn read_conf(server: &str) -> Result { + read_conf_from(GPCONF_PATH, server) +} + +/// Private function to read the `openconnect` arguments for the given server from a given path. +/// Make it easy to write tests. +fn read_conf_from(path: &str, server: &str) -> Result { + let mut config = Ini::new(); + + config.set_default_section(DEFAULT_SECTION); + config.set_multiline(true); + + config.load(path).map_err(Error::msg)?; + + let default_openconnect_config = config + .get(DEFAULT_SECTION, PROP_OPENCONNECT_ARGS) + .unwrap_or_default(); + let server_openconnect_config = config + .get(server, PROP_OPENCONNECT_ARGS) + .unwrap_or(default_openconnect_config); + + let args = shlex::split(&server_openconnect_config).unwrap_or_default(); + parse_args(&args) +} + +fn parse_args(args: &Vec) -> Result { + use lexopt::prelude::*; + + let mut parser = Parser::from_args(args); + + let mut script: Option = None; + let mut certificate: Option = None; + let mut servercert: Option = None; + while let Some(arg) = parser.next()? { + match arg { + Long("script") | Short('s') => { + script = Some(parser.value()?.parse()?); + } + Long("certificate") | Short('c') => { + certificate = Some(parser.value()?.parse()?); + } + Long("servercert") => { + servercert = Some(parser.value()?.parse()?); + } + _ => { + warn!("Ignoring unknown argument: {}", arg.unexpected()); + } + } + } + + Ok(OpenconnectArgs { + script, + certificate, + servercert, + }) +} + +#[cfg(test)] +mod tests { + use std::io::Write; + use tempfile::NamedTempFile; + + use super::*; + + // Macro to create a temporary file with the given content. + macro_rules! tempfile { + ($content:expr) => {{ + let mut file = NamedTempFile::new().unwrap(); + write!(file, $content).unwrap(); + file + }}; + } + + #[test] + fn test_config_not_found() { + let args = read_conf_from("non-existent-file", "server"); + assert!(args.is_err()); + } + + #[test] + fn test_default_config() { + let file = tempfile!( + r#" +[*] +openconnect-args=--script=/script.sh +"# + ); + let path = file.path().to_str().unwrap(); + let args = read_conf_from(path, "any server").unwrap(); + + assert_eq!(args.script, Some("/script.sh".to_string())); + assert_eq!(args.certificate, None); + assert_eq!(args.servercert, None); + } + + #[test] + fn test_server_config() { + let file = tempfile!( + r#" +[*] +openconnect-args=--script=/script.sh + +[server] +openconnect-args=--certificate=/cert.pem +"# + ); + let path = file.path().to_str().unwrap(); + let args = read_conf_from(path, "server").unwrap(); + + assert_eq!(args.script, None); + assert_eq!(args.certificate, Some("/cert.pem".to_string())); + assert_eq!(args.servercert, None); + } + + #[test] + fn test_ignore_unknown_args() { + let file = tempfile!( + r#" +[*] +openconnect-args=--script=/script.sh --unknown-arg -c /cert.pem +"# + ); + let path = file.path().to_str().unwrap(); + let args = read_conf_from(path, "server").unwrap(); + + assert_eq!(args.script, Some("/script.sh".to_string())); + assert_eq!(args.certificate, Some("/cert.pem".to_string())); + assert_eq!(args.servercert, None); + } +} diff --git a/gpcommon/src/vpn/mod.rs b/gpcommon/src/vpn/mod.rs index 3883520..4bd78ce 100644 --- a/gpcommon/src/vpn/mod.rs +++ b/gpcommon/src/vpn/mod.rs @@ -1,13 +1,15 @@ -use crate::vpn::vpnc_script::find_default_vpnc_script; +use self::vpn_options::VpnOptions; use log::{debug, info, warn}; use serde::{Deserialize, Serialize}; -use std::ffi::{c_void, CString}; +use std::ffi::c_void; use std::sync::Arc; use std::thread; use tokio::sync::watch; use tokio::sync::{mpsc, Mutex}; mod ffi; +mod gpconf; +mod vpn_options; mod vpnc_script; #[derive(Debug, PartialEq, Clone, Copy, Serialize, Deserialize)] @@ -51,30 +53,6 @@ impl StatusHolder { } } -#[derive(Debug)] -pub(crate) struct VpnOptions { - server: CString, - cookie: CString, - script: CString, - user_agent: CString, -} - -impl VpnOptions { - fn as_oc_options(&self, user_data: *mut c_void) -> ffi::Options { - ffi::Options { - server: self.server.as_ptr(), - cookie: self.cookie.as_ptr(), - script: self.script.as_ptr(), - user_agent: self.user_agent.as_ptr(), - user_data, - } - } - - fn to_cstr(value: &str) -> CString { - CString::new(value.to_string()).expect("Failed to convert to CString") - } -} - #[derive(Debug, Default)] pub(crate) struct Vpn { status_holder: Arc>, @@ -92,23 +70,18 @@ impl Vpn { cookie: &str, user_agent: &str, ) -> Result<(), Box> { - let script = match find_default_vpnc_script() { - Some(script) => { - debug!("Using default vpnc-script: {}", script); - script - } - None => { - return Err("Failed to find default vpnc-script".into()); - } - }; + let mut options_builder = VpnOptions::builder(); + let mut options_builder = options_builder + .server(server) + .cookie(cookie) + .user_agent(user_agent); + if let Ok(openconnect_args) = gpconf::read_conf(server) { + info!("Found openconnect args in /etc/gpservice/gp.conf"); + options_builder = options_builder.with_openconnect_args(openconnect_args); + } // Save the VPN options so we can use them later, e.g. reconnect - *self.vpn_options.lock().await = Some(VpnOptions { - server: VpnOptions::to_cstr(server), - cookie: VpnOptions::to_cstr(cookie), - script: VpnOptions::to_cstr(script), - user_agent: VpnOptions::to_cstr(user_agent), - }); + *self.vpn_options.lock().await = Some(options_builder.build()); let vpn_options = self.vpn_options.clone(); let status_holder = self.status_holder.clone(); diff --git a/gpcommon/src/vpn/vpn.c b/gpcommon/src/vpn/vpn.c index 904c571..d63bb2f 100644 --- a/gpcommon/src/vpn/vpn.c +++ b/gpcommon/src/vpn/vpn.c @@ -67,6 +67,17 @@ int vpn_connect(const vpn_options *options) openconnect_set_hostname(vpninfo, options->server); openconnect_set_cookie(vpninfo, options->cookie); + if (options->certificate) + { + INFO("Setting client certificate: %s", options->certificate); + openconnect_set_client_cert(vpninfo, options->certificate, NULL); + } + + if (options->servercert) { + INFO("Setting server certificate: %s", options->servercert); + openconnect_set_system_trust(vpninfo, 0); + } + g_cmd_pipe_fd = openconnect_setup_cmd_pipe(vpninfo); if (g_cmd_pipe_fd < 0) { diff --git a/gpcommon/src/vpn/vpn.h b/gpcommon/src/vpn/vpn.h index 355aa59..baf73ed 100644 --- a/gpcommon/src/vpn/vpn.h +++ b/gpcommon/src/vpn/vpn.h @@ -7,9 +7,12 @@ typedef struct vpn_options { const char *server; const char *cookie; - const char *script; const char *user_agent; void *user_data; + + const char *script; + const char *certificate; + const char *servercert; } vpn_options; int vpn_connect(const vpn_options *options); diff --git a/gpcommon/src/vpn/vpn_options.rs b/gpcommon/src/vpn/vpn_options.rs new file mode 100644 index 0000000..2332493 --- /dev/null +++ b/gpcommon/src/vpn/vpn_options.rs @@ -0,0 +1,93 @@ +use super::{ffi, gpconf::OpenconnectArgs, vpnc_script::find_default_vpnc_script}; +use std::ffi::{c_char, c_void, CString}; + +#[derive(Debug)] +pub(crate) struct VpnOptions { + server: CString, + cookie: CString, + user_agent: CString, + + script: CString, + certificate: Option, + servercert: Option, +} + +impl VpnOptions { + pub fn builder() -> VpnOptionsBuilder { + VpnOptionsBuilder::default() + } + + pub fn as_oc_options(&self, user_data: *mut c_void) -> ffi::Options { + ffi::Options { + server: self.server.as_ptr(), + cookie: self.cookie.as_ptr(), + user_agent: self.user_agent.as_ptr(), + user_data, + + script: self.script.as_ptr(), + certificate: Self::option_as_ptr(&self.certificate), + servercert: Self::option_as_ptr(&self.servercert), + } + } + + fn option_as_ptr(value: &Option) -> *const c_char { + match value { + Some(value) => value.as_ptr(), + None => std::ptr::null(), + } + } +} + +#[derive(Debug, Default)] +pub(crate) struct VpnOptionsBuilder { + server: String, + cookie: String, + user_agent: String, + openconnect_args: OpenconnectArgs, +} + +impl VpnOptionsBuilder { + pub fn server(&mut self, server: &str) -> &mut Self { + self.server = server.to_string(); + self + } + + pub fn cookie(&mut self, cookie: &str) -> &mut Self { + self.cookie = cookie.to_string(); + self + } + + pub fn user_agent(&mut self, user_agent: &str) -> &mut Self { + self.user_agent = user_agent.to_string(); + self + } + + pub fn with_openconnect_args(&mut self, openconnect_args: OpenconnectArgs) -> &mut Self { + self.openconnect_args = openconnect_args; + self + } + + fn to_cstr(value: &str) -> CString { + CString::new(value.to_string()).expect("Failed to convert to CString") + } + + pub fn build(&self) -> VpnOptions { + let openconnect_args = &self.openconnect_args; + let script = openconnect_args + .script() + .or_else(|| find_default_vpnc_script().map(|s| s.to_string())) + .map(|s| Self::to_cstr(&s)) + .unwrap_or_default(); + let certificate = openconnect_args.certificate().map(|s| Self::to_cstr(&s)); + let servercert = openconnect_args.servercert().map(|s| Self::to_cstr(&s)); + + VpnOptions { + server: Self::to_cstr(&self.server), + cookie: Self::to_cstr(&self.cookie), + user_agent: Self::to_cstr(&self.user_agent), + script, + certificate, + servercert, + } + } +}