use auth::{auth_prelogin, BrowserAuthenticator}; use clap::Parser; use gpapi::{ auth::{SamlAuthData, SamlAuthResult}, clap::{args::Os, handle_error, Args}, gp_params::{ClientOs, GpParams}, utils::{normalize_server, openssl}, GP_USER_AGENT, }; use log::{info, LevelFilter}; use serde_json::json; use tempfile::NamedTempFile; const VERSION: &str = concat!(env!("CARGO_PKG_VERSION"), " (", compile_time::date_str!(), ")"); #[derive(Parser, Clone)] #[command( version = VERSION, author, about = "The authentication component for the GlobalProtect VPN client, supports the SSO authentication method.", help_template = "\ {before-help}{name} {version} {author} {about} {usage-heading} {usage} {all-args}{after-help} See 'gpauth -h' for more information. " )] struct Cli { #[arg(help = "The portal server to authenticate")] server: String, #[arg(long, help = "Treating the server as a gateway")] gateway: bool, #[arg(long, help = "The SAML authentication request")] saml_request: Option, #[arg(long, default_value = GP_USER_AGENT, help = "The user agent to use")] user_agent: String, #[arg(long, default_value = "Linux")] os: Os, #[arg(long)] os_version: Option, #[arg(long, help = "Get around the OpenSSL `unsafe legacy renegotiation` error")] fix_openssl: bool, #[arg(long, help = "Ignore TLS errors")] ignore_tls_errors: bool, #[cfg(feature = "webview-auth")] #[arg(long, help = "Use the default browser for authentication")] default_browser: bool, #[arg( long, help = "The browser to use for authentication, e.g., `default`, `firefox`, `chrome`, `chromium`, or the path to the browser executable" )] browser: Option, #[cfg(feature = "webview-auth")] #[arg(long, help = "The HiDPI mode, useful for high-resolution screens")] hidpi: bool, #[cfg(feature = "webview-auth")] #[arg(long, help = "Clean the cache of the embedded browser")] pub clean: bool, } impl Args for Cli { fn fix_openssl(&self) -> bool { self.fix_openssl } fn ignore_tls_errors(&self) -> bool { self.ignore_tls_errors } } impl Cli { fn prepare_env(&self) -> anyhow::Result> { #[cfg(feature = "webview-auth")] gpapi::utils::env_utils::patch_gui_runtime_env(self.hidpi); if self.fix_openssl { info!("Fixing OpenSSL environment"); let file = openssl::fix_openssl_env()?; return Ok(Some(file)); } Ok(None) } async fn run(&self) -> anyhow::Result<()> { if self.ignore_tls_errors { info!("TLS errors will be ignored"); } let openssl_conf = self.prepare_env()?; let server = normalize_server(&self.server)?; let gp_params = self.build_gp_params(); let auth_request = match self.saml_request.as_deref() { Some(auth_request) => auth_request.to_string(), None => auth_prelogin(&server, &gp_params).await?, }; #[cfg(feature = "webview-auth")] let browser = self .browser .as_deref() .or_else(|| self.default_browser.then(|| "default")); #[cfg(not(feature = "webview-auth"))] let browser = self.browser.as_deref().or(Some("default")); if let Some(browser) = browser { let authenticator = BrowserAuthenticator::new(&auth_request, browser); let auth_result = authenticator.authenticate().await; print_auth_result(auth_result); // explicitly drop openssl_conf to avoid the unused variable warning drop(openssl_conf); return Ok(()); } #[cfg(feature = "webview-auth")] crate::webview_auth::authenticate(server, gp_params, auth_request, self.clean, openssl_conf).await?; Ok(()) } fn build_gp_params(&self) -> GpParams { let gp_params = GpParams::builder() .user_agent(&self.user_agent) .client_os(ClientOs::from(&self.os)) .os_version(self.os_version.clone()) .ignore_tls_errors(self.ignore_tls_errors) .is_gateway(self.gateway) .build(); gp_params } } fn init_logger() { env_logger::builder().filter_level(LevelFilter::Info).init(); } pub async fn run() { let cli = Cli::parse(); init_logger(); info!("gpauth started: {}", VERSION); if let Err(err) = cli.run().await { handle_error(err, &cli); std::process::exit(1); } } pub fn print_auth_result(auth_result: anyhow::Result) { let auth_result = match auth_result { Ok(auth_data) => SamlAuthResult::Success(auth_data), Err(err) => SamlAuthResult::Failure(format!("{}", err)), }; println!("{}", json!(auth_result)); }