mirror of
				https://github.com/yuezk/GlobalProtect-openconnect.git
				synced 2025-05-20 07:26:58 -04:00 
			
		
		
		
	Compare commits
	
		
			2 Commits
		
	
	
		
			v2.0.0-bet
			...
			v2.0.0-bet
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | aac401e7ee | ||
|  | 9655b735a1 | 
							
								
								
									
										10
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										10
									
								
								Cargo.lock
									
									
									
										generated
									
									
									
								
							| @@ -1423,7 +1423,7 @@ dependencies = [ | |||||||
|  |  | ||||||
| [[package]] | [[package]] | ||||||
| name = "gpapi" | name = "gpapi" | ||||||
| version = "2.0.0-beta5" | 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-beta5" | 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-beta5" | 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-beta5" | 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-beta5" | 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-beta5" | 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" | ||||||
|   | |||||||
| @@ -115,7 +115,7 @@ pub(crate) async fn run() { | |||||||
|       eprintln!("{} --fix-openssl {}\n", args[0], args[1..].join(" ")); |       eprintln!("{} --fix-openssl {}\n", args[0], args[1..].join(" ")); | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     if err.contains("certificate verify failed") { |     if err.contains("certificate verify failed") && !cli.ignore_tls_errors { | ||||||
|       eprintln!( |       eprintln!( | ||||||
|         "\nRe-run it with the `--ignore-tls-errors` option to ignore the certificate error, e.g.:\n" |         "\nRe-run it with the `--ignore-tls-errors` option to ignore the certificate error, e.g.:\n" | ||||||
|       ); |       ); | ||||||
|   | |||||||
| @@ -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) | ||||||
|   | |||||||
| @@ -12,6 +12,7 @@ pub async fn gateway_login( | |||||||
| ) -> anyhow::Result<String> { | ) -> anyhow::Result<String> { | ||||||
|   let login_url = format!("https://{}/ssl-vpn/login.esp", gateway); |   let login_url = format!("https://{}/ssl-vpn/login.esp", gateway); | ||||||
|   let client = Client::builder() |   let client = Client::builder() | ||||||
|  |     .danger_accept_invalid_certs(gp_params.ignore_tls_errors()) | ||||||
|     .user_agent(gp_params.user_agent()) |     .user_agent(gp_params.user_agent()) | ||||||
|     .build()?; |     .build()?; | ||||||
|  |  | ||||||
|   | |||||||
| @@ -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( | ||||||
| @@ -120,6 +114,7 @@ pub async fn retrieve_config( | |||||||
|  |  | ||||||
|   let url = format!("{}/global-protect/getconfig.esp", portal); |   let url = format!("{}/global-protect/getconfig.esp", portal); | ||||||
|   let client = Client::builder() |   let client = Client::builder() | ||||||
|  |     .danger_accept_invalid_certs(gp_params.ignore_tls_errors()) | ||||||
|     .user_agent(gp_params.user_agent()) |     .user_agent(gp_params.user_agent()) | ||||||
|     .build()?; |     .build()?; | ||||||
|  |  | ||||||
| @@ -138,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"); | ||||||
|   } |   } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user