mirror of
https://github.com/yuezk/GlobalProtect-openconnect.git
synced 2025-05-20 07:26:58 -04:00
Enhancements and Bug Fixes: Align Pre-login Behavior, TLS Error Ignorance, GUI Auto-Launch, and Documentation Improvements (#291)
This commit is contained in:
@@ -27,6 +27,8 @@ dotenvy_macro.workspace = true
|
||||
uzers.workspace = true
|
||||
|
||||
tauri = { workspace = true, optional = true }
|
||||
clap = { workspace = true, optional = true }
|
||||
|
||||
[features]
|
||||
tauri = ["dep:tauri"]
|
||||
clap = ["dep:clap"]
|
||||
|
64
crates/gpapi/src/clap/args.rs
Normal file
64
crates/gpapi/src/clap/args.rs
Normal file
@@ -0,0 +1,64 @@
|
||||
use clap::{builder::PossibleValue, ValueEnum};
|
||||
|
||||
use crate::gp_params::ClientOs;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum Os {
|
||||
Linux,
|
||||
Windows,
|
||||
Mac,
|
||||
}
|
||||
|
||||
impl Os {
|
||||
pub fn as_str(&self) -> &'static str {
|
||||
match self {
|
||||
Os::Linux => "Linux",
|
||||
Os::Windows => "Windows",
|
||||
Os::Mac => "Mac",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&str> for Os {
|
||||
fn from(os: &str) -> Self {
|
||||
match os.to_lowercase().as_str() {
|
||||
"linux" => Os::Linux,
|
||||
"windows" => Os::Windows,
|
||||
"mac" => Os::Mac,
|
||||
_ => Os::Linux,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&Os> for ClientOs {
|
||||
fn from(value: &Os) -> Self {
|
||||
match value {
|
||||
Os::Linux => ClientOs::Linux,
|
||||
Os::Windows => ClientOs::Windows,
|
||||
Os::Mac => ClientOs::Mac,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ValueEnum for Os {
|
||||
fn value_variants<'a>() -> &'a [Self] {
|
||||
&[Os::Linux, Os::Windows, Os::Mac]
|
||||
}
|
||||
|
||||
fn to_possible_value(&self) -> Option<clap::builder::PossibleValue> {
|
||||
match self {
|
||||
Os::Linux => Some(PossibleValue::new("Linux")),
|
||||
Os::Windows => Some(PossibleValue::new("Windows")),
|
||||
Os::Mac => Some(PossibleValue::new("Mac")),
|
||||
}
|
||||
}
|
||||
|
||||
fn from_str(input: &str, _: bool) -> Result<Self, String> {
|
||||
match input.to_lowercase().as_str() {
|
||||
"linux" => Ok(Os::Linux),
|
||||
"windows" => Ok(Os::Windows),
|
||||
"mac" => Ok(Os::Mac),
|
||||
_ => Err(format!("Invalid OS: {}", input)),
|
||||
}
|
||||
}
|
||||
}
|
1
crates/gpapi/src/clap/mod.rs
Normal file
1
crates/gpapi/src/clap/mod.rs
Normal file
@@ -0,0 +1 @@
|
||||
pub mod args;
|
@@ -7,23 +7,32 @@ use crate::GP_USER_AGENT;
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize, Clone, Type, Default)]
|
||||
pub enum ClientOs {
|
||||
Linux,
|
||||
#[default]
|
||||
Linux,
|
||||
Windows,
|
||||
Mac,
|
||||
}
|
||||
|
||||
impl From<&ClientOs> for &str {
|
||||
fn from(os: &ClientOs) -> Self {
|
||||
impl From<&str> for ClientOs {
|
||||
fn from(os: &str) -> Self {
|
||||
match os {
|
||||
ClientOs::Linux => "Linux",
|
||||
ClientOs::Windows => "Windows",
|
||||
ClientOs::Mac => "Mac",
|
||||
"Linux" => ClientOs::Linux,
|
||||
"Windows" => ClientOs::Windows,
|
||||
"Mac" => ClientOs::Mac,
|
||||
_ => ClientOs::Linux,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ClientOs {
|
||||
pub fn as_str(&self) -> &str {
|
||||
match self {
|
||||
ClientOs::Linux => "Linux",
|
||||
ClientOs::Windows => "Windows",
|
||||
ClientOs::Mac => "Mac",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_openconnect_os(&self) -> &str {
|
||||
match self {
|
||||
ClientOs::Linux => "linux",
|
||||
@@ -40,6 +49,7 @@ pub struct GpParams {
|
||||
os_version: Option<String>,
|
||||
client_version: Option<String>,
|
||||
computer: Option<String>,
|
||||
ignore_tls_errors: bool,
|
||||
}
|
||||
|
||||
impl GpParams {
|
||||
@@ -54,13 +64,17 @@ impl GpParams {
|
||||
pub(crate) fn computer(&self) -> &str {
|
||||
match self.computer {
|
||||
Some(ref computer) => computer,
|
||||
None => (&self.client_os).into()
|
||||
None => self.client_os.as_str(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ignore_tls_errors(&self) -> bool {
|
||||
self.ignore_tls_errors
|
||||
}
|
||||
|
||||
pub(crate) fn to_params(&self) -> HashMap<&str, &str> {
|
||||
let mut params: HashMap<&str, &str> = HashMap::new();
|
||||
let client_os: &str = (&self.client_os).into();
|
||||
let client_os = self.client_os.as_str();
|
||||
|
||||
// Common params
|
||||
params.insert("prot", "https:");
|
||||
@@ -97,6 +111,7 @@ pub struct GpParamsBuilder {
|
||||
os_version: Option<String>,
|
||||
client_version: Option<String>,
|
||||
computer: Option<String>,
|
||||
ignore_tls_errors: bool,
|
||||
}
|
||||
|
||||
impl GpParamsBuilder {
|
||||
@@ -107,6 +122,7 @@ impl GpParamsBuilder {
|
||||
os_version: Default::default(),
|
||||
client_version: Default::default(),
|
||||
computer: Default::default(),
|
||||
ignore_tls_errors: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -120,13 +136,13 @@ impl GpParamsBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn os_version(&mut self, os_version: &str) -> &mut Self {
|
||||
self.os_version = Some(os_version.to_string());
|
||||
pub fn os_version<T: Into<Option<String>>>(&mut self, os_version: T) -> &mut Self {
|
||||
self.os_version = os_version.into();
|
||||
self
|
||||
}
|
||||
|
||||
pub fn client_version(&mut self, client_version: &str) -> &mut Self {
|
||||
self.client_version = Some(client_version.to_string());
|
||||
pub fn client_version<T: Into<Option<String>>>(&mut self, client_version: T) -> &mut Self {
|
||||
self.client_version = client_version.into();
|
||||
self
|
||||
}
|
||||
|
||||
@@ -135,6 +151,11 @@ impl GpParamsBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn ignore_tls_errors(&mut self, ignore_tls_errors: bool) -> &mut Self {
|
||||
self.ignore_tls_errors = ignore_tls_errors;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(&self) -> GpParams {
|
||||
GpParams {
|
||||
user_agent: self.user_agent.clone(),
|
||||
@@ -142,6 +163,7 @@ impl GpParamsBuilder {
|
||||
os_version: self.os_version.clone(),
|
||||
client_version: self.client_version.clone(),
|
||||
computer: self.computer.clone(),
|
||||
ignore_tls_errors: self.ignore_tls_errors,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -7,12 +7,17 @@ pub mod process;
|
||||
pub mod service;
|
||||
pub mod utils;
|
||||
|
||||
#[cfg(feature = "clap")]
|
||||
pub mod clap;
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
pub const GP_API_KEY: &[u8; 32] = &[0; 32];
|
||||
|
||||
pub const GP_USER_AGENT: &str = "PAN GlobalProtect";
|
||||
pub const GP_SERVICE_LOCK_FILE: &str = "/var/run/gpservice.lock";
|
||||
|
||||
#[cfg(not(debug_assertions))]
|
||||
pub const GP_CLIENT_BINARY: &str = "/usr/bin/gpclient";
|
||||
#[cfg(not(debug_assertions))]
|
||||
pub const GP_SERVICE_BINARY: &str = "/usr/bin/gpservice";
|
||||
#[cfg(not(debug_assertions))]
|
||||
@@ -20,6 +25,8 @@ pub const GP_GUI_BINARY: &str = "/usr/bin/gpgui";
|
||||
#[cfg(not(debug_assertions))]
|
||||
pub(crate) const GP_AUTH_BINARY: &str = "/usr/bin/gpauth";
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
pub const GP_CLIENT_BINARY: &str = dotenvy_macro::dotenv!("GP_CLIENT_BINARY");
|
||||
#[cfg(debug_assertions)]
|
||||
pub const GP_SERVICE_BINARY: &str = dotenvy_macro::dotenv!("GP_SERVICE_BINARY");
|
||||
#[cfg(debug_assertions)]
|
||||
|
@@ -5,7 +5,21 @@ use roxmltree::Document;
|
||||
use serde::Serialize;
|
||||
use specta::Type;
|
||||
|
||||
use crate::utils::{base64, normalize_server, xml};
|
||||
use crate::{
|
||||
gp_params::GpParams,
|
||||
utils::{base64, normalize_server, xml},
|
||||
};
|
||||
|
||||
const REQUIRED_PARAMS: [&str; 8] = [
|
||||
"tmp",
|
||||
"clientVer",
|
||||
"clientos",
|
||||
"os-version",
|
||||
"host-id",
|
||||
"ipv6-support",
|
||||
"default-browser",
|
||||
"cas-support",
|
||||
];
|
||||
|
||||
#[derive(Debug, Serialize, Type, Clone)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
@@ -67,20 +81,33 @@ impl Prelogin {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn prelogin(portal: &str, user_agent: &str) -> anyhow::Result<Prelogin> {
|
||||
pub async fn prelogin(portal: &str, gp_params: &GpParams) -> anyhow::Result<Prelogin> {
|
||||
let user_agent = gp_params.user_agent();
|
||||
info!("Portal prelogin, user_agent: {}", user_agent);
|
||||
|
||||
let portal = normalize_server(portal)?;
|
||||
let prelogin_url = format!("{}/global-protect/prelogin.esp", portal);
|
||||
let client = Client::builder().user_agent(user_agent).build()?;
|
||||
let prelogin_url = format!(
|
||||
"{}/global-protect/prelogin.esp?kerberos-support=yes",
|
||||
portal
|
||||
);
|
||||
let mut params = gp_params.to_params();
|
||||
params.insert("tmp", "tmp");
|
||||
params.insert("default-browser", "0");
|
||||
params.insert("cas-support", "yes");
|
||||
|
||||
let res_xml = client
|
||||
.get(&prelogin_url)
|
||||
.send()
|
||||
.await?
|
||||
.error_for_status()?
|
||||
.text()
|
||||
.await?;
|
||||
params.retain(|k, _| {
|
||||
REQUIRED_PARAMS
|
||||
.iter()
|
||||
.any(|required_param| required_param == k)
|
||||
});
|
||||
|
||||
let client = Client::builder()
|
||||
.danger_accept_invalid_certs(gp_params.ignore_tls_errors())
|
||||
.user_agent(user_agent)
|
||||
.build()?;
|
||||
|
||||
let res = client.post(&prelogin_url).form(¶ms).send().await?;
|
||||
let res_xml = res.error_for_status()?.text().await?;
|
||||
|
||||
trace!("Prelogin response: {}", res_xml);
|
||||
let doc = Document::parse(&res_xml)?;
|
||||
|
@@ -8,10 +8,13 @@ use super::command_traits::CommandExt;
|
||||
|
||||
pub struct SamlAuthLauncher<'a> {
|
||||
server: &'a str,
|
||||
user_agent: Option<&'a str>,
|
||||
saml_request: Option<&'a str>,
|
||||
user_agent: Option<&'a str>,
|
||||
os: Option<&'a str>,
|
||||
os_version: Option<&'a str>,
|
||||
hidpi: bool,
|
||||
fix_openssl: bool,
|
||||
ignore_tls_errors: bool,
|
||||
clean: bool,
|
||||
}
|
||||
|
||||
@@ -19,21 +22,34 @@ impl<'a> SamlAuthLauncher<'a> {
|
||||
pub fn new(server: &'a str) -> Self {
|
||||
Self {
|
||||
server,
|
||||
user_agent: None,
|
||||
saml_request: None,
|
||||
user_agent: None,
|
||||
os: None,
|
||||
os_version: None,
|
||||
hidpi: false,
|
||||
fix_openssl: false,
|
||||
ignore_tls_errors: false,
|
||||
clean: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn saml_request(mut self, saml_request: &'a str) -> Self {
|
||||
self.saml_request = Some(saml_request);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn user_agent(mut self, user_agent: &'a str) -> Self {
|
||||
self.user_agent = Some(user_agent);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn saml_request(mut self, saml_request: &'a str) -> Self {
|
||||
self.saml_request = Some(saml_request);
|
||||
pub fn os(mut self, os: &'a str) -> Self {
|
||||
self.os = Some(os);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn os_version(mut self, os_version: Option<&'a str>) -> Self {
|
||||
self.os_version = os_version;
|
||||
self
|
||||
}
|
||||
|
||||
@@ -47,6 +63,11 @@ impl<'a> SamlAuthLauncher<'a> {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn ignore_tls_errors(mut self, ignore_tls_errors: bool) -> Self {
|
||||
self.ignore_tls_errors = ignore_tls_errors;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn clean(mut self, clean: bool) -> Self {
|
||||
self.clean = clean;
|
||||
self
|
||||
@@ -57,20 +78,32 @@ impl<'a> SamlAuthLauncher<'a> {
|
||||
let mut auth_cmd = Command::new(GP_AUTH_BINARY);
|
||||
auth_cmd.arg(self.server);
|
||||
|
||||
if let Some(saml_request) = self.saml_request {
|
||||
auth_cmd.arg("--saml-request").arg(saml_request);
|
||||
}
|
||||
|
||||
if let Some(user_agent) = self.user_agent {
|
||||
auth_cmd.arg("--user-agent").arg(user_agent);
|
||||
}
|
||||
|
||||
if let Some(saml_request) = self.saml_request {
|
||||
auth_cmd.arg("--saml-request").arg(saml_request);
|
||||
if let Some(os) = self.os {
|
||||
auth_cmd.arg("--os").arg(os);
|
||||
}
|
||||
|
||||
if let Some(os_version) = self.os_version {
|
||||
auth_cmd.arg("--os-version").arg(os_version);
|
||||
}
|
||||
|
||||
if self.hidpi {
|
||||
auth_cmd.arg("--hidpi");
|
||||
}
|
||||
|
||||
if self.fix_openssl {
|
||||
auth_cmd.arg("--fix-openssl");
|
||||
}
|
||||
|
||||
if self.hidpi {
|
||||
auth_cmd.arg("--hidpi");
|
||||
if self.ignore_tls_errors {
|
||||
auth_cmd.arg("--ignore-tls-errors");
|
||||
}
|
||||
|
||||
if self.clean {
|
||||
|
@@ -27,7 +27,6 @@ pub fn raise_window(win: &Window) -> anyhow::Result<()> {
|
||||
}
|
||||
let title = win.title()?;
|
||||
tokio::spawn(async move {
|
||||
info!("Raising window: {}", title);
|
||||
if let Err(err) = wmctrl_raise_window(&title).await {
|
||||
warn!("Failed to raise window: {}", err);
|
||||
}
|
||||
|
Reference in New Issue
Block a user