From 4de22208f0bfffda9b3f842f60c890f3715ca2cc Mon Sep 17 00:00:00 2001 From: Rainer Keller Date: Wed, 9 Oct 2024 17:03:09 +0200 Subject: [PATCH] Print SAML authentication URL for user convenience --- src/config.c | 6 ++--- src/http.c | 2 +- src/http.h | 10 +++++++ src/http_server.c | 67 ++++++++++++++++++++++++++++++++--------------- 4 files changed, 60 insertions(+), 25 deletions(-) diff --git a/src/config.c b/src/config.c index 1b389ff0..67ee46cc 100644 --- a/src/config.c +++ b/src/config.c @@ -424,8 +424,8 @@ int load_config(struct vpn_config *cfg, const char *filename) if (port < 1 || port > 65535) { log_error("Bad SAML listen port: \"%s\".\n", val); - goto err_free; - } + goto err_free; + } cfg->saml_port = (uint16_t)port; } else if (strcmp(key, "user-key") == 0) { free(cfg->user_key); @@ -544,7 +544,7 @@ void merge_config(struct vpn_config *dst, struct vpn_config *src) free(dst->cookie); dst->cookie = src->cookie; } - if(src->saml_port != 0){ + if(src->saml_port != 0) { dst->saml_port = src->saml_port; } if (src->pinentry) { diff --git a/src/http.c b/src/http.c index 1cea951f..bf4052da 100644 --- a/src/http.c +++ b/src/http.c @@ -47,7 +47,7 @@ * @param[out] dest the buffer to write the URL-encoded string * @param[in] str the input string to be escaped */ -static void url_encode(char *dest, const char *str) +void url_encode(char *dest, const char *str) { while (*str != '\0') { if (isalnum(*str) || *str == '-' || *str == '_' || diff --git a/src/http.h b/src/http.h index a4a07e1b..22f7b719 100644 --- a/src/http.h +++ b/src/http.h @@ -31,6 +31,16 @@ #define ERR_HTTP_PERMISSION -6 #define ERR_HTTP_NO_COOKIE -7 +/* + * URL-encodes a string for HTTP requests. + * + * The dest buffer size MUST be at least strlen(str) * 3 + 1. + * + * @param[out] dest the buffer to write the URL-encoded string + * @param[in] str the input string to be escaped + */ +void url_encode(char *dest, const char *str); + static inline const char *err_http_str(int code) { if (code > 0) diff --git a/src/http_server.c b/src/http_server.c index 4c67737e..ebed4d60 100644 --- a/src/http_server.c +++ b/src/http_server.c @@ -8,6 +8,31 @@ #include "config.h" #include "log.h" #include "tunnel.h" +#include "http.h" // for url_encode + +static void print_url(const struct vpn_config *cfg) { + char *encoded_realm = NULL; + char realm[] = "&realm="; + char *empty_string = "\0"; + + // Desired string is https://company.com:port/remote/saml/start?redirect=1(&realm=) + // with the realm being optional + static const char *uri_pattern = "https://%s:%d/remote/saml/start?redirect=1%s%s"; + + if (cfg->realm[0] != '\0') { + encoded_realm = alloca(strlen(cfg->realm) * 3 + 1); // url_encode required three times the size + url_encode(encoded_realm, cfg->realm); + } else { + encoded_realm = empty_string; + realm[0] = 0; // Make realm appear empty when printing as string + } + + int required_size = 1 + snprintf(NULL, 0, uri_pattern, cfg->gateway_host, cfg->gateway_port, realm, encoded_realm); + char *url = alloca(required_size); + snprintf(url, required_size, uri_pattern, cfg->gateway_host, cfg->gateway_port, realm, encoded_realm); + + log_info("Authenticate at '%s'\n", url); +} // Convenience function to send a response with a user readable status message and the // request URL shown for debug purposes. @@ -59,19 +84,19 @@ static int process_request(int new_socket, char *id) { } // Extract the id - static const char *token_delimiter = " &\r\n"; - char *next_token = request + strlen(request_head); // strsep does modify the input argument and we don't want to loose our request pointer. - char *id_start = strsep(&next_token, token_delimiter); - - if (next_token == NULL) { - // In case not found, nextToken was set to NULL - // This should be invalid because we expext \r\n at the end of the GET request line - log_error("Bad request format\n"); - send_status_response(new_socket, "Invalid formatting of Fortinet server redirect response. VPN could not be established."); - return -1; - } - - // strsep inserted a NULL at the location of the delimiter. + static const char *token_delimiter = " &\r\n"; + char *next_token = request + strlen(request_head); // strsep does modify the input argument and we don't want to loose our request pointer. + char *id_start = strsep(&next_token, token_delimiter); + + if (next_token == NULL) { + // In case not found, next_token was set to NULL + // This should be invalid because we expect \r\n at the end of the GET request line + log_error("Bad request format\n"); + send_status_response(new_socket, "Invalid formatting of Fortinet server redirect response. VPN could not be established."); + return -1; + } + + // strsep inserted a NULL at the location of the delimiter. int id_length = strlen(id_start); if(id_length == 0 || id_length >= MAX_SAML_SESSION_ID_LENGTH) { @@ -80,7 +105,7 @@ static int process_request(int new_socket, char *id) { return -1; } - // It was checked above, that the length is smaller than MAX_SAML_SESSION_ID_LENGTH + // It was checked above, that the length is smaller than MAX_SAML_SESSION_ID_LENGTH strcpy(id, id_start); for (int i = 0; i < id_length; i++) { @@ -92,8 +117,8 @@ static int process_request(int new_socket, char *id) { log_info("Extracted id: %s\n", id); send_status_response(new_socket, - "SAML session id received from Fortinet server. VPN will be established..." - "\r\nYou can close this browser tab now."); + "SAML session id received from Fortinet server. VPN will be established..." + "\r\nYou may close this browser tab now."); return 0; } @@ -101,7 +126,7 @@ static int process_request(int new_socket, char *id) { * Run a http server to listen for SAML login requests * * @return 0 in case of success - * < 0 in case of error + * < 0 in case of error */ int wait_for_http_request(struct vpn_config *config) { int server_fd, new_socket; @@ -130,7 +155,7 @@ int wait_for_http_request(struct vpn_config *config) { // Forcefully attaching socket to the port if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) { close(server_fd); - log_error("Failed to bind socket to port %d \n", saml_port); + log_error("Failed to bind socket to port %d\n", saml_port); return -1; } @@ -144,7 +169,7 @@ int wait_for_http_request(struct vpn_config *config) { fd_set readfds; struct timeval tv; - log_info("Listening for SAML login on port %d.\n", saml_port); + log_info("Listening for SAML login on port %d\n", saml_port); print_url(config); while(max_tries > 0) { @@ -152,8 +177,8 @@ int wait_for_http_request(struct vpn_config *config) { FD_ZERO(&readfds); FD_SET(server_fd, &readfds); // Wait up to ten seconds - tv.tv_sec = 10; - tv.tv_usec = 0; + tv.tv_sec = 10; + tv.tv_usec = 0; int retval = select(server_fd + 1, &readfds, NULL, NULL, &tv);