mirror of
https://github.com/yuezk/GlobalProtect-openconnect.git
synced 2025-05-20 07:26:58 -04:00
Support HIP report (#309)
This commit is contained in:
178
crates/gpapi/src/gateway/hip.rs
Normal file
178
crates/gpapi/src/gateway/hip.rs
Normal file
@@ -0,0 +1,178 @@
|
||||
use std::collections::HashMap;
|
||||
|
||||
use log::{info, warn};
|
||||
use reqwest::Client;
|
||||
use roxmltree::Document;
|
||||
|
||||
use crate::{gp_params::GpParams, process::hip_launcher::HipLauncher, utils::normalize_server};
|
||||
|
||||
struct HipReporter<'a> {
|
||||
server: String,
|
||||
cookie: &'a str,
|
||||
md5: &'a str,
|
||||
csd_wrapper: &'a str,
|
||||
gp_params: &'a GpParams,
|
||||
client: Client,
|
||||
}
|
||||
|
||||
impl HipReporter<'_> {
|
||||
async fn report(&self) -> anyhow::Result<()> {
|
||||
let client_ip = self.retrieve_client_ip().await?;
|
||||
|
||||
let hip_needed = match self.check_hip(&client_ip).await {
|
||||
Ok(hip_needed) => hip_needed,
|
||||
Err(err) => {
|
||||
warn!("Failed to check HIP: {}", err);
|
||||
return Ok(());
|
||||
}
|
||||
};
|
||||
|
||||
if !hip_needed {
|
||||
info!("HIP report not needed");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
info!("HIP report needed, generating report...");
|
||||
let report = self.generate_report(&client_ip).await?;
|
||||
|
||||
if let Err(err) = self.submit_hip(&client_ip, &report).await {
|
||||
warn!("Failed to submit HIP report: {}", err);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn retrieve_client_ip(&self) -> anyhow::Result<String> {
|
||||
let config_url = format!("{}/ssl-vpn/getconfig.esp", self.server);
|
||||
let mut params: HashMap<&str, &str> = HashMap::new();
|
||||
|
||||
params.insert("client-type", "1");
|
||||
params.insert("protocol-version", "p1");
|
||||
params.insert("internal", "no");
|
||||
params.insert("ipv6-support", "yes");
|
||||
params.insert("clientos", self.gp_params.client_os());
|
||||
params.insert("hmac-algo", "sha1,md5,sha256");
|
||||
params.insert("enc-algo", "aes-128-cbc,aes-256-cbc");
|
||||
|
||||
if let Some(os_version) = self.gp_params.os_version() {
|
||||
params.insert("os-version", os_version);
|
||||
}
|
||||
if let Some(client_version) = self.gp_params.client_version() {
|
||||
params.insert("app-version", client_version);
|
||||
}
|
||||
|
||||
let params = merge_cookie_params(self.cookie, ¶ms)?;
|
||||
|
||||
let res = self.client.post(&config_url).form(¶ms).send().await?;
|
||||
let res_xml = res.error_for_status()?.text().await?;
|
||||
let doc = Document::parse(&res_xml)?;
|
||||
|
||||
// Get <ip-address>
|
||||
let ip = doc
|
||||
.descendants()
|
||||
.find(|n| n.has_tag_name("ip-address"))
|
||||
.and_then(|n| n.text())
|
||||
.ok_or_else(|| anyhow::anyhow!("ip-address not found"))?;
|
||||
|
||||
Ok(ip.to_string())
|
||||
}
|
||||
|
||||
async fn check_hip(&self, client_ip: &str) -> anyhow::Result<bool> {
|
||||
let url = format!("{}/ssl-vpn/hipreportcheck.esp", self.server);
|
||||
let mut params = HashMap::new();
|
||||
|
||||
params.insert("client-role", "global-protect-full");
|
||||
params.insert("client-ip", client_ip);
|
||||
params.insert("md5", self.md5);
|
||||
|
||||
let params = merge_cookie_params(self.cookie, ¶ms)?;
|
||||
let res = self.client.post(&url).form(¶ms).send().await?;
|
||||
let res_xml = res.error_for_status()?.text().await?;
|
||||
|
||||
is_hip_needed(&res_xml)
|
||||
}
|
||||
|
||||
async fn generate_report(&self, client_ip: &str) -> anyhow::Result<String> {
|
||||
let launcher = HipLauncher::new(self.csd_wrapper)
|
||||
.cookie(self.cookie)
|
||||
.md5(self.md5)
|
||||
.client_ip(client_ip)
|
||||
.client_os(self.gp_params.client_os())
|
||||
.client_version(self.gp_params.client_version());
|
||||
|
||||
launcher.launch().await
|
||||
}
|
||||
|
||||
async fn submit_hip(&self, client_ip: &str, report: &str) -> anyhow::Result<()> {
|
||||
let url = format!("{}/ssl-vpn/hipreport.esp", self.server);
|
||||
|
||||
let mut params = HashMap::new();
|
||||
params.insert("client-role", "global-protect-full");
|
||||
params.insert("client-ip", client_ip);
|
||||
params.insert("report", report);
|
||||
|
||||
let params = merge_cookie_params(self.cookie, ¶ms)?;
|
||||
let res = self.client.post(&url).form(¶ms).send().await?;
|
||||
let res_xml = res.error_for_status()?.text().await?;
|
||||
|
||||
info!("HIP check response: {}", res_xml);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn is_hip_needed(res_xml: &str) -> anyhow::Result<bool> {
|
||||
let doc = Document::parse(res_xml)?;
|
||||
|
||||
let hip_needed = doc
|
||||
.descendants()
|
||||
.find(|n| n.has_tag_name("hip-report-needed"))
|
||||
.and_then(|n| n.text())
|
||||
.ok_or_else(|| anyhow::anyhow!("hip-report-needed not found"))?;
|
||||
|
||||
Ok(hip_needed == "yes")
|
||||
}
|
||||
|
||||
fn merge_cookie_params(cookie: &str, params: &HashMap<&str, &str>) -> anyhow::Result<HashMap<String, String>> {
|
||||
let cookie_params = serde_urlencoded::from_str::<HashMap<String, String>>(cookie)?;
|
||||
let params = params
|
||||
.iter()
|
||||
.map(|(k, v)| (k.to_string(), v.to_string()))
|
||||
.chain(cookie_params)
|
||||
.collect::<HashMap<String, String>>();
|
||||
|
||||
Ok(params)
|
||||
}
|
||||
|
||||
// Compute md5 for fields except authcookie,preferred-ip,preferred-ipv6
|
||||
fn build_csd_token(cookie: &str) -> anyhow::Result<String> {
|
||||
let mut cookie_params = serde_urlencoded::from_str::<Vec<(String, String)>>(cookie)?;
|
||||
cookie_params.retain(|(k, _)| k != "authcookie" && k != "preferred-ip" && k != "preferred-ipv6");
|
||||
|
||||
let token = serde_urlencoded::to_string(cookie_params)?;
|
||||
let md5 = format!("{:x}", md5::compute(token));
|
||||
|
||||
Ok(md5)
|
||||
}
|
||||
|
||||
pub async fn hip_report(gateway: &str, cookie: &str, csd_wrapper: &str, gp_params: &GpParams) -> anyhow::Result<()> {
|
||||
let client = Client::builder()
|
||||
.danger_accept_invalid_certs(gp_params.ignore_tls_errors())
|
||||
.user_agent(gp_params.user_agent())
|
||||
.build()?;
|
||||
|
||||
let md5 = build_csd_token(cookie)?;
|
||||
|
||||
info!("Submit HIP report md5: {}", md5);
|
||||
|
||||
let reporter = HipReporter {
|
||||
server: normalize_server(gateway)?,
|
||||
cookie,
|
||||
md5: &md5,
|
||||
csd_wrapper,
|
||||
gp_params,
|
||||
client,
|
||||
};
|
||||
|
||||
reporter.report().await
|
||||
}
|
@@ -1,5 +1,6 @@
|
||||
mod login;
|
||||
mod parse_gateways;
|
||||
pub mod hip;
|
||||
|
||||
pub use login::*;
|
||||
pub(crate) use parse_gateways::*;
|
||||
|
Reference in New Issue
Block a user