From aac401e7ee749c3083dddff8842f9aebb42e3ca0 Mon Sep 17 00:00:00 2001 From: Kevin Yue Date: Tue, 23 Jan 2024 09:17:30 -0500 Subject: [PATCH] Perform gateway prelogin when failed to login to gateway --- Cargo.lock | 10 ++++---- Cargo.toml | 2 +- apps/gpclient/src/connect.rs | 36 ++++++++++++++++++++++------- crates/gpapi/src/gp_params.rs | 17 ++++++++++++++ crates/gpapi/src/portal/config.rs | 28 ++++++++-------------- crates/gpapi/src/portal/prelogin.rs | 10 ++++++-- 6 files changed, 69 insertions(+), 34 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5353199..0683cbf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1423,7 +1423,7 @@ dependencies = [ [[package]] name = "gpapi" -version = "2.0.0-beta6" +version = "2.0.0-beta7" dependencies = [ "anyhow", "base64 0.21.5", @@ -1452,7 +1452,7 @@ dependencies = [ [[package]] name = "gpauth" -version = "2.0.0-beta6" +version = "2.0.0-beta7" dependencies = [ "anyhow", "clap", @@ -1472,7 +1472,7 @@ dependencies = [ [[package]] name = "gpclient" -version = "2.0.0-beta6" +version = "2.0.0-beta7" dependencies = [ "anyhow", "clap", @@ -1493,7 +1493,7 @@ dependencies = [ [[package]] name = "gpservice" -version = "2.0.0-beta6" +version = "2.0.0-beta7" dependencies = [ "anyhow", "axum", @@ -2478,7 +2478,7 @@ dependencies = [ [[package]] name = "openconnect" -version = "2.0.0-beta6" +version = "2.0.0-beta7" dependencies = [ "cc", "is_executable", diff --git a/Cargo.toml b/Cargo.toml index fac3e79..53c27fd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,7 +4,7 @@ resolver = "2" members = ["crates/*", "apps/gpclient", "apps/gpservice", "apps/gpauth"] [workspace.package] -version = "2.0.0-beta6" +version = "2.0.0-beta7" authors = ["Kevin Yue "] homepage = "https://github.com/yuezk/GlobalProtect-openconnect" edition = "2021" diff --git a/apps/gpclient/src/connect.rs b/apps/gpclient/src/connect.rs index 772b271..3d5c9fb 100644 --- a/apps/gpclient/src/connect.rs +++ b/apps/gpclient/src/connect.rs @@ -71,18 +71,21 @@ impl<'a> ConnectHandler<'a> { Self { args, shared_args } } - pub(crate) async fn handle(&self) -> anyhow::Result<()> { - let portal = utils::normalize_server(self.args.server.as_str())?; - - let gp_params = GpParams::builder() + fn build_gp_params(&self) -> GpParams { + GpParams::builder() .user_agent(&self.args.user_agent) .client_os(ClientOs::from(&self.args.os)) .os_version(self.args.os_version()) .ignore_tls_errors(self.shared_args.ignore_tls_errors) - .build(); + .build() + } + + pub(crate) async fn handle(&self) -> anyhow::Result<()> { + let portal = utils::normalize_server(self.args.server.as_str())?; + let gp_params = self.build_gp_params(); let prelogin = prelogin(&portal, &gp_params).await?; - let portal_credential = self.obtain_portal_credential(&prelogin).await?; + let portal_credential = self.obtain_credential(&prelogin).await?; let mut portal_config = retrieve_config(&portal, &portal_credential, &gp_params).await?; let selected_gateway = match &self.args.gateway { @@ -105,7 +108,14 @@ impl<'a> ConnectHandler<'a> { let gateway = selected_gateway.server(); let cred = portal_config.auth_cookie().into(); - let token = gateway_login(gateway, &cred, &gp_params).await?; + + let token = match gateway_login(gateway, &cred, &gp_params).await { + Ok(token) => token, + Err(_) => { + info!("Gateway login failed, retrying with prelogin"); + self.gateway_login_with_prelogin(gateway).await? + } + }; let vpn = Vpn::builder(gateway, &token) .user_agent(self.args.user_agent.clone()) @@ -132,7 +142,17 @@ impl<'a> ConnectHandler<'a> { Ok(()) } - async fn obtain_portal_credential(&self, prelogin: &Prelogin) -> anyhow::Result { + async fn gateway_login_with_prelogin(&self, gateway: &str) -> anyhow::Result { + let mut gp_params = self.build_gp_params(); + gp_params.set_is_gateway(true); + + let prelogin = prelogin(gateway, &gp_params).await?; + let cred = self.obtain_credential(&prelogin).await?; + + gateway_login(gateway, &cred, &gp_params).await + } + + async fn obtain_credential(&self, prelogin: &Prelogin) -> anyhow::Result { match prelogin { Prelogin::Saml(prelogin) => { SamlAuthLauncher::new(&self.args.server) diff --git a/crates/gpapi/src/gp_params.rs b/crates/gpapi/src/gp_params.rs index ab48e57..0c2a205 100644 --- a/crates/gpapi/src/gp_params.rs +++ b/crates/gpapi/src/gp_params.rs @@ -44,6 +44,7 @@ impl ClientOs { #[derive(Debug, Serialize, Deserialize, Type, Default)] pub struct GpParams { + is_gateway: bool, user_agent: String, client_os: ClientOs, os_version: Option, @@ -58,6 +59,14 @@ impl GpParams { GpParamsBuilder::new() } + pub(crate) fn is_gateway(&self) -> bool { + self.is_gateway + } + + pub fn set_is_gateway(&mut self, is_gateway: bool) { + self.is_gateway = is_gateway; + } + pub(crate) fn user_agent(&self) -> &str { &self.user_agent } @@ -103,6 +112,7 @@ impl GpParams { } pub struct GpParamsBuilder { + is_gateway: bool, user_agent: String, client_os: ClientOs, os_version: Option, @@ -115,6 +125,7 @@ pub struct GpParamsBuilder { impl GpParamsBuilder { pub fn new() -> Self { Self { + is_gateway: false, user_agent: GP_USER_AGENT.to_string(), client_os: ClientOs::Linux, os_version: Default::default(), @@ -125,6 +136,11 @@ impl GpParamsBuilder { } } + pub fn is_gateway(&mut self, is_gateway: bool) -> &mut Self { + self.is_gateway = is_gateway; + self + } + pub fn user_agent(&mut self, user_agent: &str) -> &mut Self { self.user_agent = user_agent.to_string(); self @@ -162,6 +178,7 @@ impl GpParamsBuilder { pub fn build(&self) -> GpParams { GpParams { + is_gateway: self.is_gateway, user_agent: self.user_agent.clone(), client_os: self.client_os.clone(), os_version: self.os_version.clone(), diff --git a/crates/gpapi/src/portal/config.rs b/crates/gpapi/src/portal/config.rs index b44d2f1..262934f 100644 --- a/crates/gpapi/src/portal/config.rs +++ b/crates/gpapi/src/portal/config.rs @@ -102,12 +102,6 @@ impl PortalConfig { pub enum PortalConfigError { #[error("Empty response, retrying can help")] EmptyResponse, - #[error("Empty auth cookie, retrying can help")] - EmptyAuthCookie, - #[error("Invalid auth cookie, retrying can help")] - InvalidAuthCookie, - #[error("Empty gateways, retrying can help")] - EmptyGateways, } pub async fn retrieve_config( @@ -139,24 +133,22 @@ pub async fn retrieve_config( ensure!(!res_xml.is_empty(), PortalConfigError::EmptyResponse); let doc = Document::parse(&res_xml)?; - let gateways = parse_gateways(&doc).ok_or_else(|| anyhow::anyhow!("Failed to parse gateways"))?; + let mut gateways = + parse_gateways(&doc).ok_or_else(|| anyhow::anyhow!("Failed to parse gateways"))?; let user_auth_cookie = xml::get_child_text(&doc, "portal-userauthcookie").unwrap_or_default(); let prelogon_user_auth_cookie = xml::get_child_text(&doc, "portal-prelogonuserauthcookie").unwrap_or_default(); let config_digest = xml::get_child_text(&doc, "config-digest"); - ensure!( - !user_auth_cookie.is_empty() && !prelogon_user_auth_cookie.is_empty(), - PortalConfigError::EmptyAuthCookie - ); - - ensure!( - user_auth_cookie != "empty" && prelogon_user_auth_cookie != "empty", - PortalConfigError::InvalidAuthCookie - ); - - ensure!(!gateways.is_empty(), PortalConfigError::EmptyGateways); + if gateways.is_empty() { + gateways.push(Gateway { + name: server.to_string(), + address: server.to_string(), + priority: 0, + priority_rules: vec![], + }); + } Ok(PortalConfig::new( server.to_string(), diff --git a/crates/gpapi/src/portal/prelogin.rs b/crates/gpapi/src/portal/prelogin.rs index 94f5184..292ffab 100644 --- a/crates/gpapi/src/portal/prelogin.rs +++ b/crates/gpapi/src/portal/prelogin.rs @@ -91,11 +91,17 @@ pub async fn prelogin(portal: &str, gp_params: &GpParams) -> anyhow::Result