mirror of
https://github.com/yuezk/GlobalProtect-openconnect.git
synced 2025-04-02 18:31:50 -04:00
Perform gateway prelogin when failed to login to gateway
This commit is contained in:
parent
9655b735a1
commit
aac401e7ee
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -1423,7 +1423,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpapi"
|
name = "gpapi"
|
||||||
version = "2.0.0-beta6"
|
version = "2.0.0-beta7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"base64 0.21.5",
|
"base64 0.21.5",
|
||||||
@ -1452,7 +1452,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpauth"
|
name = "gpauth"
|
||||||
version = "2.0.0-beta6"
|
version = "2.0.0-beta7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
@ -1472,7 +1472,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpclient"
|
name = "gpclient"
|
||||||
version = "2.0.0-beta6"
|
version = "2.0.0-beta7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"clap",
|
"clap",
|
||||||
@ -1493,7 +1493,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gpservice"
|
name = "gpservice"
|
||||||
version = "2.0.0-beta6"
|
version = "2.0.0-beta7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"axum",
|
"axum",
|
||||||
@ -2478,7 +2478,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "openconnect"
|
name = "openconnect"
|
||||||
version = "2.0.0-beta6"
|
version = "2.0.0-beta7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"is_executable",
|
"is_executable",
|
||||||
|
@ -4,7 +4,7 @@ resolver = "2"
|
|||||||
members = ["crates/*", "apps/gpclient", "apps/gpservice", "apps/gpauth"]
|
members = ["crates/*", "apps/gpclient", "apps/gpservice", "apps/gpauth"]
|
||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "2.0.0-beta6"
|
version = "2.0.0-beta7"
|
||||||
authors = ["Kevin Yue <k3vinyue@gmail.com>"]
|
authors = ["Kevin Yue <k3vinyue@gmail.com>"]
|
||||||
homepage = "https://github.com/yuezk/GlobalProtect-openconnect"
|
homepage = "https://github.com/yuezk/GlobalProtect-openconnect"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
@ -71,18 +71,21 @@ impl<'a> ConnectHandler<'a> {
|
|||||||
Self { args, shared_args }
|
Self { args, shared_args }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn handle(&self) -> anyhow::Result<()> {
|
fn build_gp_params(&self) -> GpParams {
|
||||||
let portal = utils::normalize_server(self.args.server.as_str())?;
|
GpParams::builder()
|
||||||
|
|
||||||
let gp_params = GpParams::builder()
|
|
||||||
.user_agent(&self.args.user_agent)
|
.user_agent(&self.args.user_agent)
|
||||||
.client_os(ClientOs::from(&self.args.os))
|
.client_os(ClientOs::from(&self.args.os))
|
||||||
.os_version(self.args.os_version())
|
.os_version(self.args.os_version())
|
||||||
.ignore_tls_errors(self.shared_args.ignore_tls_errors)
|
.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 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 mut portal_config = retrieve_config(&portal, &portal_credential, &gp_params).await?;
|
||||||
|
|
||||||
let selected_gateway = match &self.args.gateway {
|
let selected_gateway = match &self.args.gateway {
|
||||||
@ -105,7 +108,14 @@ impl<'a> ConnectHandler<'a> {
|
|||||||
|
|
||||||
let gateway = selected_gateway.server();
|
let gateway = selected_gateway.server();
|
||||||
let cred = portal_config.auth_cookie().into();
|
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)
|
let vpn = Vpn::builder(gateway, &token)
|
||||||
.user_agent(self.args.user_agent.clone())
|
.user_agent(self.args.user_agent.clone())
|
||||||
@ -132,7 +142,17 @@ impl<'a> ConnectHandler<'a> {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn obtain_portal_credential(&self, prelogin: &Prelogin) -> anyhow::Result<Credential> {
|
async fn gateway_login_with_prelogin(&self, gateway: &str) -> anyhow::Result<String> {
|
||||||
|
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<Credential> {
|
||||||
match prelogin {
|
match prelogin {
|
||||||
Prelogin::Saml(prelogin) => {
|
Prelogin::Saml(prelogin) => {
|
||||||
SamlAuthLauncher::new(&self.args.server)
|
SamlAuthLauncher::new(&self.args.server)
|
||||||
|
@ -44,6 +44,7 @@ impl ClientOs {
|
|||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, Type, Default)]
|
#[derive(Debug, Serialize, Deserialize, Type, Default)]
|
||||||
pub struct GpParams {
|
pub struct GpParams {
|
||||||
|
is_gateway: bool,
|
||||||
user_agent: String,
|
user_agent: String,
|
||||||
client_os: ClientOs,
|
client_os: ClientOs,
|
||||||
os_version: Option<String>,
|
os_version: Option<String>,
|
||||||
@ -58,6 +59,14 @@ impl GpParams {
|
|||||||
GpParamsBuilder::new()
|
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 {
|
pub(crate) fn user_agent(&self) -> &str {
|
||||||
&self.user_agent
|
&self.user_agent
|
||||||
}
|
}
|
||||||
@ -103,6 +112,7 @@ impl GpParams {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub struct GpParamsBuilder {
|
pub struct GpParamsBuilder {
|
||||||
|
is_gateway: bool,
|
||||||
user_agent: String,
|
user_agent: String,
|
||||||
client_os: ClientOs,
|
client_os: ClientOs,
|
||||||
os_version: Option<String>,
|
os_version: Option<String>,
|
||||||
@ -115,6 +125,7 @@ pub struct GpParamsBuilder {
|
|||||||
impl GpParamsBuilder {
|
impl GpParamsBuilder {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
is_gateway: false,
|
||||||
user_agent: GP_USER_AGENT.to_string(),
|
user_agent: GP_USER_AGENT.to_string(),
|
||||||
client_os: ClientOs::Linux,
|
client_os: ClientOs::Linux,
|
||||||
os_version: Default::default(),
|
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 {
|
pub fn user_agent(&mut self, user_agent: &str) -> &mut Self {
|
||||||
self.user_agent = user_agent.to_string();
|
self.user_agent = user_agent.to_string();
|
||||||
self
|
self
|
||||||
@ -162,6 +178,7 @@ impl GpParamsBuilder {
|
|||||||
|
|
||||||
pub fn build(&self) -> GpParams {
|
pub fn build(&self) -> GpParams {
|
||||||
GpParams {
|
GpParams {
|
||||||
|
is_gateway: self.is_gateway,
|
||||||
user_agent: self.user_agent.clone(),
|
user_agent: self.user_agent.clone(),
|
||||||
client_os: self.client_os.clone(),
|
client_os: self.client_os.clone(),
|
||||||
os_version: self.os_version.clone(),
|
os_version: self.os_version.clone(),
|
||||||
|
@ -102,12 +102,6 @@ impl PortalConfig {
|
|||||||
pub enum PortalConfigError {
|
pub enum PortalConfigError {
|
||||||
#[error("Empty response, retrying can help")]
|
#[error("Empty response, retrying can help")]
|
||||||
EmptyResponse,
|
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(
|
pub async fn retrieve_config(
|
||||||
@ -139,24 +133,22 @@ pub async fn retrieve_config(
|
|||||||
ensure!(!res_xml.is_empty(), PortalConfigError::EmptyResponse);
|
ensure!(!res_xml.is_empty(), PortalConfigError::EmptyResponse);
|
||||||
|
|
||||||
let doc = Document::parse(&res_xml)?;
|
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 user_auth_cookie = xml::get_child_text(&doc, "portal-userauthcookie").unwrap_or_default();
|
||||||
let prelogon_user_auth_cookie =
|
let prelogon_user_auth_cookie =
|
||||||
xml::get_child_text(&doc, "portal-prelogonuserauthcookie").unwrap_or_default();
|
xml::get_child_text(&doc, "portal-prelogonuserauthcookie").unwrap_or_default();
|
||||||
let config_digest = xml::get_child_text(&doc, "config-digest");
|
let config_digest = xml::get_child_text(&doc, "config-digest");
|
||||||
|
|
||||||
ensure!(
|
if gateways.is_empty() {
|
||||||
!user_auth_cookie.is_empty() && !prelogon_user_auth_cookie.is_empty(),
|
gateways.push(Gateway {
|
||||||
PortalConfigError::EmptyAuthCookie
|
name: server.to_string(),
|
||||||
);
|
address: server.to_string(),
|
||||||
|
priority: 0,
|
||||||
ensure!(
|
priority_rules: vec![],
|
||||||
user_auth_cookie != "empty" && prelogon_user_auth_cookie != "empty",
|
});
|
||||||
PortalConfigError::InvalidAuthCookie
|
}
|
||||||
);
|
|
||||||
|
|
||||||
ensure!(!gateways.is_empty(), PortalConfigError::EmptyGateways);
|
|
||||||
|
|
||||||
Ok(PortalConfig::new(
|
Ok(PortalConfig::new(
|
||||||
server.to_string(),
|
server.to_string(),
|
||||||
|
@ -91,11 +91,17 @@ pub async fn prelogin(portal: &str, gp_params: &GpParams) -> anyhow::Result<Prel
|
|||||||
info!("Portal prelogin, user_agent: {}", user_agent);
|
info!("Portal prelogin, user_agent: {}", user_agent);
|
||||||
|
|
||||||
let portal = normalize_server(portal)?;
|
let portal = normalize_server(portal)?;
|
||||||
let prelogin_url = format!("{}/global-protect/prelogin.esp", portal);
|
let prelogin_url = format!(
|
||||||
|
"{portal}/{}/prelogin.esp",
|
||||||
|
if gp_params.is_gateway() {
|
||||||
|
"ssl-vpn"
|
||||||
|
} else {
|
||||||
|
"global-protect"
|
||||||
|
}
|
||||||
|
);
|
||||||
let mut params = gp_params.to_params();
|
let mut params = gp_params.to_params();
|
||||||
|
|
||||||
params.insert("tmp", "tmp");
|
params.insert("tmp", "tmp");
|
||||||
params.insert("cas-support", "yes");
|
|
||||||
if gp_params.prefer_default_browser() {
|
if gp_params.prefer_default_browser() {
|
||||||
params.insert("default-browser", "1");
|
params.insert("default-browser", "1");
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user