From 41e1a559e071bb1ae44e0db15c960d47032c3108 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20Kr=C3=B6ner?= <254679264+sk-tractive@users.noreply.github.com> Date: Tue, 10 Mar 2026 16:15:05 +0100 Subject: [PATCH] Add --saml-auto-open and --saml-instant-close params for a faster SAML auth experience --- src/config.c | 24 +++++++++++++++ src/config.h | 2 ++ src/http_server.c | 76 +++++++++++++++++++++++++++++++++++++++++------ src/main.c | 9 ++++++ 4 files changed, 102 insertions(+), 9 deletions(-) diff --git a/src/config.c b/src/config.c index d423e9db..6eaeb30a 100644 --- a/src/config.c +++ b/src/config.c @@ -46,6 +46,8 @@ const struct vpn_config invalid_cfg = { .password_set = 0, .cookie = NULL, .saml_port = 0, + .saml_auto_open = -1, + .saml_instant_close = -1, .saml_session_id = {'\0'}, .otp = {'\0'}, .otp_prompt = NULL, @@ -427,6 +429,24 @@ int load_config(struct vpn_config *cfg, const char *filename) goto err_free; } cfg->saml_port = (uint16_t)port; + } else if (strcmp(key, "saml-auto-open") == 0) { + int saml_auto_open = strtob(val); + + if (saml_auto_open < 0) { + log_warn("Bad saml-auto-open in configuration file: \"%s\".\n", + val); + continue; + } + cfg->saml_auto_open = saml_auto_open; + } else if (strcmp(key, "saml-instant-close") == 0) { + int saml_instant_close = strtob(val); + + if (saml_instant_close < 0) { + log_warn("Bad saml-instant-close in configuration file: \"%s\".\n", + val); + continue; + } + cfg->saml_instant_close = saml_instant_close; } else if (strcmp(key, "user-key") == 0) { free(cfg->user_key); cfg->user_key = strdup(val); @@ -546,6 +566,10 @@ void merge_config(struct vpn_config *dst, struct vpn_config *src) } if (src->saml_port != 0) dst->saml_port = src->saml_port; + if (src->saml_auto_open != invalid_cfg.saml_auto_open) + dst->saml_auto_open = src->saml_auto_open; + if (src->saml_instant_close != invalid_cfg.saml_instant_close) + dst->saml_instant_close = src->saml_instant_close; if (src->pinentry) { free(dst->pinentry); dst->pinentry = src->pinentry; diff --git a/src/config.h b/src/config.h index fdf11220..33292f63 100644 --- a/src/config.h +++ b/src/config.h @@ -75,6 +75,8 @@ struct vpn_config { char otp[OTP_SIZE + 1]; char *cookie; uint16_t saml_port; + int saml_auto_open; + int saml_instant_close; char saml_session_id[MAX_SAML_SESSION_ID_LENGTH + 1]; char *otp_prompt; unsigned int otp_delay; diff --git a/src/http_server.c b/src/http_server.c index b5783467..7afd073a 100644 --- a/src/http_server.c +++ b/src/http_server.c @@ -27,6 +27,7 @@ #include #include +#include #include static void print_url(const struct vpn_config *cfg) @@ -80,6 +81,54 @@ static void print_url(const struct vpn_config *cfg) log_info("Authenticate at '%s'\n", url); + if (cfg->saml_auto_open && url) { + log_info("Opening URL in default browser...\n"); +#if defined(__APPLE__) + // When running with sudo, use the original user's browser settings + const char *sudo_user = getenv("SUDO_USER"); + if (sudo_user && sudo_user[0] != '\0') { + char *cmd = malloc(strlen(url) + strlen(sudo_user) + 32); + if (cmd) { + sprintf(cmd, "sudo -u %s open '%s'", sudo_user, url); + system(cmd); + free(cmd); + } + } else { + char *cmd = malloc(strlen(url) + 16); + if (cmd) { + sprintf(cmd, "open '%s'", url); + system(cmd); + free(cmd); + } + } +#elif defined(_WIN32) + char *cmd = malloc(strlen(url) + 16); + if (cmd) { + sprintf(cmd, "start '%s'", url); + system(cmd); + free(cmd); + } +#else + // When running with sudo, use the original user's browser settings + const char *sudo_user = getenv("SUDO_USER"); + if (sudo_user && sudo_user[0] != '\0') { + char *cmd = malloc(strlen(url) + strlen(sudo_user) + 48); + if (cmd) { + sprintf(cmd, "sudo -u %s xdg-open '%s' 2>/dev/null", sudo_user, url); + system(cmd); + free(cmd); + } + } else { + char *cmd = malloc(strlen(url) + 32); + if (cmd) { + sprintf(cmd, "xdg-open '%s' 2>/dev/null", url); + system(cmd); + free(cmd); + } + } +#endif + } + end: free(url); free(encoded_realm); @@ -136,7 +185,7 @@ static void send_status_response(int socket, const char *userMessage) free(replyHeaderBuffer); } -static int process_request(int new_socket, char *id) +static int process_request(int new_socket, char *id, const struct vpn_config *cfg) { log_info("Processing HTTP SAML request\n"); @@ -210,13 +259,22 @@ static int process_request(int new_socket, char *id) return -1; } - send_status_response(new_socket, - "SAML session id received from Fortinet server. VPN will be established...
\r\n" - "You may close this browser tab now.
\r\n" - "\r\n"); + if (cfg->saml_instant_close) { + send_status_response(new_socket, + "SAML session id received from Fortinet server. VPN will be established...
\r\n" + "You may close this browser tab now.
\r\n" + "\r\n"); + } else { + send_status_response(new_socket, + "SAML session id received from Fortinet server. VPN will be established...
\r\n" + "You may close this browser tab now.
\r\n" + "\r\n"); + } return 0; } @@ -299,7 +357,7 @@ int wait_for_http_request(struct vpn_config *config) continue; } - int result = process_request(new_socket, config->saml_session_id); + int result = process_request(new_socket, config->saml_session_id, config); close(new_socket); if (result == 0) diff --git a/src/main.c b/src/main.c index c374c52d..a5134164 100644 --- a/src/main.c +++ b/src/main.c @@ -79,6 +79,7 @@ #define usage \ "Usage: openfortivpn [[:]] [-u ] [-p ]\n" \ " [--cookie=] [--cookie-on-stdin] [--saml-login]\n" \ +" [--saml-auto-open] [--saml-instant-close]\n" \ " [--otp=] [--otp-delay=] [--otp-prompt=]\n" \ " [--pinentry=] [--realm=]\n" \ " [--ifname=] [--set-routes=<0|1>]\n" \ @@ -119,6 +120,10 @@ PPPD_USAGE \ " --cookie= A valid session cookie (SVPNCOOKIE).\n" \ " --cookie-on-stdin Read the cookie (SVPNCOOKIE) from standard input.\n" \ " --saml-login[=port] Run a http server to handle SAML login requests\n" \ +" --saml-auto-open Automatically open the SAML authentication URL\n" \ +" in the default browser.\n" \ +" --saml-instant-close Close the browser tab immediately after SAML\n" \ +" authentication instead of waiting 5 seconds.\n" \ " -o , --otp= One-Time-Password.\n" \ " --otp-prompt= Search for the OTP prompt starting with this string.\n" \ " --otp-delay= Wait seconds before sending the OTP.\n" \ @@ -228,6 +233,8 @@ int main(int argc, char *argv[]) .password_set = 0, .cookie = NULL, .saml_port = 0, + .saml_auto_open = 0, + .saml_instant_close = 0, .saml_session_id = {'\0'}, .otp = {'\0'}, .otp_prompt = NULL, @@ -291,6 +298,8 @@ int main(int argc, char *argv[]) {"cookie", required_argument, NULL, 0}, {"cookie-on-stdin", no_argument, NULL, 0}, {"saml-login", optional_argument, NULL, 0}, + {"saml-auto-open", no_argument, &cli_cfg.saml_auto_open, 1}, + {"saml-instant-close", no_argument, &cli_cfg.saml_instant_close, 1}, {"otp", required_argument, NULL, 'o'}, {"otp-prompt", required_argument, NULL, 0}, {"otp-delay", required_argument, NULL, 0},