Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
home:herrvorragend:AeonOverlay
openconnect
0001-GlobalProtect-Add-external-browser-with-CA...
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File 0001-GlobalProtect-Add-external-browser-with-CAS-support.patch of Package openconnect
diff '--color=auto' -urN openconnect-9.12/auth-globalprotect.c openconnect-9.12-new/auth-globalprotect.c --- openconnect-9.12/auth-globalprotect.c 2023-04-11 15:48:19.000000000 +0200 +++ openconnect-9.12-new/auth-globalprotect.c 2024-07-24 15:32:07.127698518 +0200 @@ -24,6 +24,20 @@ #include <ctype.h> #include <errno.h> +#include <sys/stat.h> +#include <unistd.h> + +#ifndef _WIN32 +#include <pwd.h> +#include <sys/inotify.h> +#endif + +#define MAX_EVENTS 2 +#define LEN_NAME 32 +#define EVENT_SIZE ( sizeof (struct inotify_event) ) /*size of one event*/ +#define BUF_LEN ( MAX_EVENTS * ( EVENT_SIZE + LEN_NAME )) + +#define GP_DATA_FILE "globalprotect.dat" struct login_context { char *username; /* Username that has already succeeded in some form */ @@ -31,6 +45,7 @@ char *portal_userauthcookie; /* portal-userauthcookie (from global-protect/getconfig.esp) */ char *portal_prelogonuserauthcookie; /* portal-prelogonuserauthcookie (from global-protect/getconfig.esp) */ struct oc_auth_form *form; + int cas_auth; /* Flag indicating whether cas auth is to be used */ }; void gpst_common_headers(struct openconnect_info *vpninfo, @@ -59,6 +74,32 @@ return "Windows"; } +static char *get_user_cache_dir(void) +{ + char *cache_dir; + int cache_dir_len; +#ifndef _WIN32 + struct passwd *pw = getpwuid(getuid()); + char *home = strdup(pw->pw_dir); +#else + /* TODO: Add WIN32 implementation */ + char *home = strdup(""); +#endif + + /* Ensure that the openconnect cache dir exists */ + cache_dir_len = strlen(home) + strlen("/.cache/openconnect/") + 1; + cache_dir = malloc(cache_dir_len); + snprintf(cache_dir, cache_dir_len, "%s/.cache/openconnect/", home); + free(home); + +#ifndef _WIN32 + mkdir(cache_dir, 0700); +#else + CreateDirectory(cache_dir, NULL); +#endif + + return cache_dir; +} /* Parse pre-login response ({POST,GET} /{global-protect,ssl-vpn}/pre-login.esp) * @@ -81,11 +122,18 @@ char *prompt = NULL, *username_label = NULL, *password_label = NULL; char *s = NULL, *saml_method = NULL, *saml_path = NULL; int result = -EINVAL; + int default_browser = 0; + int cas_auth = 0; if (!xmlnode_is_named(xml_node, "prelogin-response")) goto out; for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) { + if (xmlnode_is_named(xml_node, "saml-default-browser")) + default_browser = xmlnode_bool_or_int_value(xml_node); + if (xmlnode_is_named(xml_node, "cas-auth")) + cas_auth = xmlnode_bool_or_int_value(xml_node); + xmlnode_get_val(xml_node, "saml-request", &s); xmlnode_get_val(xml_node, "saml-auth-method", &saml_method); xmlnode_get_trimmed_val(xml_node, "authentication-message", &prompt); @@ -94,6 +142,11 @@ /* XX: should we save the certificate username from <ccusername/> ? */ } + if (default_browser != 0) { + vpn_progress(vpninfo, PRG_INFO, "Using default browser\n"); + ctx->cas_auth = cas_auth; + } + if (saml_method && s) { /* Allow the legacy workflow (no GUI setting up open_webview) to keep working */ if (!vpninfo->open_webview && ctx->portal_userauthcookie) @@ -638,8 +691,8 @@ } if (!keep_urlpath) { orig_path = vpninfo->urlpath; - if (asprintf(&vpninfo->urlpath, "%s/prelogin.esp?tmp=tmp&clientVer=4100&clientos=%s", - portal ? "global-protect" : "ssl-vpn", gpst_os_name(vpninfo)) < 0) { + if (asprintf(&vpninfo->urlpath, "%s/prelogin.esp?tmp=tmp&clientVer=4100&clientos=%s&default-browser=4&cas-support=yes", + portal ? "global-protect" : "ssl-vpn", gpst_os_name(vpninfo)) < 0) { result = -ENOMEM; goto out; } @@ -688,6 +741,8 @@ append_opt(request_body, "os-version", vpninfo->platname); append_opt(request_body, "server", vpninfo->hostname); append_opt(request_body, "computer", vpninfo->localname); + if (ctx->cas_auth && vpninfo->sso_token_cookie) + append_opt(request_body, "token", vpninfo->sso_token_cookie); if (ctx->portal_userauthcookie) append_opt(request_body, "portal-userauthcookie", ctx->portal_userauthcookie); if (ctx->portal_prelogonuserauthcookie) @@ -697,9 +752,11 @@ append_opt(request_body, "preferred-ip", vpninfo->ip_info.addr); if (vpninfo->ip_info.addr6) append_opt(request_body, "preferred-ipv6", vpninfo->ip_info.addr); - if (ctx->form->action) - append_opt(request_body, "inputStr", ctx->form->action); - append_form_opts(vpninfo, ctx->form, request_body); + if (ctx->form) { + if (ctx->form->action) + append_opt(request_body, "inputStr", ctx->form->action); + append_form_opts(vpninfo, ctx->form, request_body); + } if ((result = buf_error(request_body))) goto out; @@ -717,14 +774,18 @@ /* Invalid username/password; reuse same form, but blank, * unless we just did a blind retry. */ - nuke_opt_values(ctx->form->opts); + if (ctx->form) + nuke_opt_values(ctx->form->opts); + else + blind_retry = 0; + if (!blind_retry) goto got_form; else blind_retry = 0; } else { /* Save successful username */ - if (!ctx->username) + if (!ctx->username && ctx->form && ctx->form->opts->_value) ctx->username = strdup(ctx->form->opts->_value); if (result == -EAGAIN) { /* New form is already populated from the challenge */ @@ -736,7 +797,7 @@ */ portal = 0; if (ctx->portal_userauthcookie || ctx->portal_prelogonuserauthcookie || - (strcmp(ctx->form->auth_id, "_challenge") && !ctx->alt_secret)) { + (ctx->form &&(strcmp(ctx->form->auth_id, "_challenge")) && !ctx->alt_secret)) { blind_retry = 1; goto replay_form; } @@ -839,3 +900,233 @@ free(xml_buf); return result; } + +static int parse_callback_file (struct openconnect_info *vpninfo, const char *data_file) +{ + FILE *fptr; + char *data; + int len; + void *xml = NULL; + xmlDocPtr xml_doc; + xmlNode *xml_node; + char *comment = NULL; + char *prelogin_cookie = NULL; + char *saml_username = NULL; + int size; + + /* Open callback data file */ + fptr = fopen(data_file, "r"); + if (!fptr) { + vpn_progress(vpninfo, PRG_INFO, "Failed to open file %s\n", data_file); + return -1; + } + + /* Get file size */ + fseek(fptr, 0L, SEEK_END); + size = ftell(fptr); + fseek(fptr, 0L, SEEK_SET); + + /* Read data */ + data = malloc(size + 1); + memset(data, 0, size + 1); + fread(data, size, 1, fptr); + fclose(fptr); + + if (strstr(data, "cas-as%3D")) { + char *token = strstr (data, "token%3D"); + char *un = strstr (data, "un%3D"); + + if (!token || !un) { + free(data); + return -1; + } + + vpn_progress(vpninfo, PRG_INFO, "CAS method for user %s\n", un); + + saml_username = malloc (token - un); + if (saml_username) { + strncpy(saml_username, un + 5, token - un - 5 - 3); + free(vpninfo->sso_username); + vpninfo->sso_username = saml_username; + } + + vpninfo->sso_token_cookie = strdup(token + 8); + free(data); + + return 0; + } + + vpn_progress(vpninfo, PRG_INFO, "Token method\n"); + + xml = openconnect_base64_decode (&len, data); + free(data); + + vpn_progress(vpninfo, PRG_INFO, "xml (%d): %s\n", len, (char*)xml); + + xml_doc = xmlReadMemory((char*)xml, len, NULL, NULL, XML_PARSE_NOERROR|XML_PARSE_RECOVER); + free(xml); + + xml_node = xmlDocGetRootElement(xml_doc); + if (!xml_node) + return -1; + + for (xmlNode *x = xml_node->children; x; x = x->next) { + /* Weird protocol response... + * <html> + * <!-- <saml-auth-status>1</saml-auth-status> // Authentication Status 1 = OK + * <prelogin-cookie>TOKEN</prelogin-cookie> // Prelogin Cookie + * <saml-username>NAME</saml-username> // Username + * <saml-slo>no</saml-slo> // SAML Single Log-Out (SLO) + * <saml-SessionNotOnOrAfter>2023-11-18T03:13:37.368Z</saml-SessionNotOnOrAfter> // Session Lifetime, used for reconnect + * -></html> + */ + if (!xmlnode_get_val(x, "comment", &comment)) { + /* Create a parent xml node for parsing */ + char *buf = malloc(strlen("<parent>") + strlen(comment) + strlen("</parent>")); + + strcpy(buf, "<parent>"); + strcat(buf, comment); + strcat(buf, "</parent>"); + + xmlDocPtr xml2_doc = xmlReadMemory((char*)buf, strlen(buf), NULL, NULL, XML_PARSE_NOERROR|XML_PARSE_RECOVER); + free(buf); + xmlNode *xml2_node = xmlDocGetRootElement(xml2_doc); + + for (xmlNode *y = xml2_node->children; y; y = y->next) { + vpn_progress(vpninfo, PRG_DEBUG, "-> Got node: %s\n", y->name); + xmlnode_get_val(y, "prelogin-cookie", &prelogin_cookie); + xmlnode_get_val(y, "saml-username", &saml_username); + } + xmlFreeDoc(xml2_doc); + } + } + + if (saml_username) { + vpn_progress(vpninfo, PRG_INFO, "saml_username: %s\n", saml_username); + free(vpninfo->sso_username); + vpninfo->sso_username = strdup(saml_username); + } + + if (prelogin_cookie) { + vpn_progress(vpninfo, PRG_INFO, "prelogin_cookie: %s\n", prelogin_cookie); + free(vpninfo->sso_token_cookie); + free(vpninfo->sso_cookie_value); + vpninfo->sso_token_cookie = strdup("prelogin_cookie"); + vpninfo->sso_cookie_value = strdup(prelogin_cookie); + } + + return 0; +} + +int gpst_handle_external_browser(struct openconnect_info *vpninfo) +{ + int ret = -1; + char *data_file = NULL; + char *uri = NULL; + char *oc_cache_dir = NULL; + int data_file_len; + + oc_cache_dir = get_user_cache_dir (); + + /* Create data */ + data_file_len = strlen (oc_cache_dir) + 1 + strlen(GP_DATA_FILE) + 1; + data_file = malloc(data_file_len); + strcpy(data_file, oc_cache_dir); + strcat(data_file, "/"); + strcat(data_file, GP_DATA_FILE); + + /* Check whether the sso_login is actually a data field */ + if (strlen(vpninfo->sso_login) > 22 && strncmp(vpninfo->sso_login, "data:text/html;base64,", 22) == 0) { + int len; + void *out = openconnect_base64_decode(&len, vpninfo->sso_login + 22); + char *tmp_file; + FILE *file; + int tmp_file_len; + + vpn_progress(vpninfo, PRG_INFO, "data: %s\n", vpninfo->sso_login); + if (!out) + goto finish; + + tmp_file_len = strlen(oc_cache_dir) + strlen("/saml.html") + 1; + tmp_file = malloc(tmp_file_len); + strcpy(tmp_file, oc_cache_dir); + strcat(tmp_file, "/saml.html"); + file = fopen(tmp_file, "w"); + fwrite((char*)out, 1, len, file); + fclose(file); + + free(out); + + uri = malloc(strlen("file:///") + strlen(tmp_file) + 1); + strcpy(uri, "file:///"); + strcat(uri, tmp_file); + free(tmp_file); + } else { + uri = strdup(vpninfo->sso_login); + } + +#ifndef _WIN32 + int fd, wd; + + fd = inotify_init(); + if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) + goto finish; + + wd = inotify_add_watch(fd, oc_cache_dir, IN_CLOSE_WRITE); + if (wd == -1) { + vpn_progress(vpninfo, PRG_DEBUG, "Could not watch : %s\n", oc_cache_dir); + } else { + vpn_progress(vpninfo, PRG_DEBUG, "Watching : %s\n", oc_cache_dir); + } + + if (vpninfo->open_ext_browser) { + ret = vpninfo->open_ext_browser(vpninfo, uri, vpninfo->cbdata); + } else { + ret = -EINVAL; + } + + if (ret) { + vpn_progress(vpninfo, PRG_ERR, + _("Failed to spawn external browser for %s\n"), + vpninfo->sso_login); + goto finish; + } + + /* TODO: Add timeout check to prevent lock up */ + while (1) { + int i = 0, length; + char buffer[BUF_LEN]; + + length = read(fd, buffer, BUF_LEN); + + while (i < length) { + struct inotify_event *event = (struct inotify_event *) &buffer[i]; + + if (event->len) { + if ((event->mask & IN_CLOSE_WRITE) && !(event->mask & IN_ISDIR) && strcmp(event->name, GP_DATA_FILE) == 0) { + vpn_progress(vpninfo, PRG_INFO, "The file %s was written.\n", event->name); + ret = parse_callback_file (vpninfo, data_file); + goto finish; + } + } + i += EVENT_SIZE + event->len; + } + } +#else + vpn_progress(vpninfo, PRG_INFO, "Proper callback data handling missing on Windows\n"); + Sleep(10 * 1000); + ret = parse_callback_file (vpninfo, data_file); +#endif + +finish: + if (uri) + free(uri); + + if (oc_cache_dir) + free(oc_cache_dir); + + if (data_file) + free(data_file); + + return ret; +} diff '--color=auto' -urN openconnect-9.12/auth-globalprotect.c.orig openconnect-9.12-new/auth-globalprotect.c.orig diff '--color=auto' -urN openconnect-9.12/gp_browser_helper.c openconnect-9.12-new/gp_browser_helper.c --- openconnect-9.12/gp_browser_helper.c 1970-01-01 01:00:00.000000000 +0100 +++ openconnect-9.12-new/gp_browser_helper.c 2024-07-24 15:32:07.127698518 +0200 @@ -0,0 +1,101 @@ +/* + * OpenConnect (SSL + DTLS) VPN client + * + * Copyright © 2023 Jan-Michael Brummer <jan-michael.brummer1@volkswagen.de> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1, as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + */ + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/types.h> + +#ifndef _WIN32 +#include <pwd.h> +#endif + +#define DATA_FILE_NAME "globalprotect.dat" + +static char *get_user_cache_dir(void) +{ + char *cache_dir; + int cache_dir_len; +#ifndef _WIN32 + struct passwd *pw = getpwuid(getuid()); + char *home = strdup(pw->pw_dir); +#else + /* TODO: Add WIN32 implementation */ + char *home = strdup(""); +#endif + + /* Ensure that the openconnect cache dir exists */ + cache_dir_len = strlen(home) + strlen("/.cache/openconnect/") + 1; + cache_dir = malloc(cache_dir_len); + snprintf(cache_dir, cache_dir_len, "%s/.cache/openconnect/", home); + free(home); + +#ifndef _WIN32 + mkdir(cache_dir, 0700); +#else + mkdir(cache_dir); +#endif + + return cache_dir; +} + +int main(int argc, char **argv) +{ + FILE *fp; + char *callback_data; + char *oc_cache_dir; + char *data_file; + int data_file_len; + int ret = 0; + + if (argc < 2) + return -1; + + callback_data = argv[1]; + + oc_cache_dir = get_user_cache_dir(); + + data_file_len = strlen (oc_cache_dir) + strlen (DATA_FILE_NAME) + 2; + data_file = malloc (data_file_len); + strcpy(data_file, oc_cache_dir); + strcat(data_file, "/"); + strcat(data_file, DATA_FILE_NAME); + + fp = fopen(data_file, "w"); + if (fp) { + /* callback_data format: + * globalprotectcallback:DATA + * + * DATA: + * - Using CAS: cas-as=1&un=<USER>&token=<TOKEN> + * - Without CAS: <TOKEN> + * + * As we just want the data, we skip the prefix + */ + fwrite(callback_data + strlen("globalprotectcallback:"), + strlen(callback_data) - strlen("globalprotectcallback:"), + 1, + fp); + fclose (fp); + ret = 0; + } + + free(data_file); + free(oc_cache_dir); + + return ret; +} diff '--color=auto' -urN openconnect-9.12/gp_browser_helper.desktop openconnect-9.12-new/gp_browser_helper.desktop --- openconnect-9.12/gp_browser_helper.desktop 1970-01-01 01:00:00.000000000 +0100 +++ openconnect-9.12-new/gp_browser_helper.desktop 2024-07-24 15:32:07.128698526 +0200 @@ -0,0 +1,8 @@ +[Desktop Entry] +Version=1.0 +Name=OpenConnect Browser Helper for GlobalProtect +Type=Application +Terminal=false +NoDisplay=true +Exec=gp_browser_helper %U +MimeType=x-scheme-handler/globalprotectcallback; \ Kein Zeilenumbruch am Dateiende. diff '--color=auto' -urN openconnect-9.12/library.c openconnect-9.12-new/library.c --- openconnect-9.12/library.c 2023-05-11 17:21:08.000000000 +0200 +++ openconnect-9.12-new/library.c 2024-07-24 15:32:07.128698526 +0200 @@ -176,6 +176,7 @@ .obtain_cookie = gpst_obtain_cookie, .sso_detect_done = gpst_sso_detect_done, .udp_protocol = "ESP", + .handle_external_browser = gpst_handle_external_browser, #ifdef HAVE_ESP .udp_setup = esp_setup, .udp_mainloop = esp_mainloop, @@ -1709,15 +1710,20 @@ vpninfo->sso_cookie_value = NULL; vpninfo->sso_username = NULL; - /* Handle the special Cisco external browser mode */ - if (vpninfo->sso_browser_mode && !strcmp(vpninfo->sso_browser_mode, "external")) { - ret = handle_external_browser(vpninfo); - } else if (vpninfo->open_webview) { - ret = vpninfo->open_webview(vpninfo, vpninfo->sso_login, vpninfo->cbdata); - } else { - vpn_progress(vpninfo, PRG_ERR, - _("No SSO handler\n")); /* XX: print more debugging info */ - ret = -EINVAL; + if (vpninfo->proto->handle_external_browser) + ret = vpninfo->proto->handle_external_browser(vpninfo); + + if (ret != 0) { + /* Handle the special Cisco external browser mode */ + if (vpninfo->sso_browser_mode && !strcmp(vpninfo->sso_browser_mode, "external")) { + ret = handle_external_browser(vpninfo); + } else if (vpninfo->open_webview) { + ret = vpninfo->open_webview(vpninfo, vpninfo->sso_login, vpninfo->cbdata); + } else { + vpn_progress(vpninfo, PRG_ERR, + _("No SSO handler\n")); /* XX: print more debugging info */ + ret = -EINVAL; + } } if (!ret) { for (opt = form->opts; opt; opt = opt->next) { diff '--color=auto' -urN openconnect-9.12/library.c.orig openconnect-9.12-new/library.c.orig diff '--color=auto' -urN openconnect-9.12/Makefile.am openconnect-9.12-new/Makefile.am --- openconnect-9.12/Makefile.am 2023-05-19 18:12:15.000000000 +0200 +++ openconnect-9.12-new/Makefile.am 2024-07-24 15:32:07.126698511 +0200 @@ -16,6 +16,7 @@ endif lib_LTLIBRARIES = libopenconnect.la +bin_PROGRAMS = gp_browser_helper sbin_PROGRAMS = openconnect man8_MANS = openconnect.8 noinst_PROGRAMS := @@ -23,6 +24,16 @@ AM_CFLAGS = @WFLAGS@ AM_CPPFLAGS = -DLOCALEDIR="\"$(localedir)\"" +# GlobalProtect Browser Helper +gp_browser_helper_SOURCES: gp_browser_helper.c + +desktopdir = $(datadir)/applications +dist_desktop_DATA = gp_browser_helper.desktop +install-data-hook: + if which update-desktop-database>/dev/null 2>&1 ; then \ + update-desktop-database; \ + fi + openconnect_SOURCES = xml.c main.c openconnect_CFLAGS = $(AM_CFLAGS) $(SSL_CFLAGS) $(DTLS_SSL_CFLAGS) \ $(LIBXML2_CFLAGS) $(JSON_CFLAGS) $(LIBPROXY_CFLAGS) \ diff '--color=auto' -urN openconnect-9.12/Makefile.am.orig openconnect-9.12-new/Makefile.am.orig diff '--color=auto' -urN openconnect-9.12/openconnect-internal.h openconnect-9.12-new/openconnect-internal.h --- openconnect-9.12/openconnect-internal.h 2023-05-19 18:12:15.000000000 +0200 +++ openconnect-9.12-new/openconnect-internal.h 2024-07-24 15:32:07.129698533 +0200 @@ -861,6 +861,9 @@ /* Catch probe packet confirming the (UDP) session */ int (*udp_catch_probe)(struct openconnect_info *vpninfo, struct pkt *p); + + /* External browser */ + int (*handle_external_browser)(struct openconnect_info *vpninfo); }; static inline struct pkt *dequeue_packet(struct pkt_q *q) @@ -1394,6 +1397,7 @@ int gpst_esp_send_probes(struct openconnect_info *vpninfo); int gpst_esp_catch_probe(struct openconnect_info *vpninfo, struct pkt *pkt); int gpst_sso_detect_done(struct openconnect_info *vpninfo, const struct oc_webview_result *result); +int gpst_handle_external_browser(struct openconnect_info *vpninfo); /* lzs.c */ int lzs_decompress(unsigned char *dst, int dstlen, const unsigned char *src, int srclen); diff '--color=auto' -urN openconnect-9.12/openconnect-internal.h.orig openconnect-9.12-new/openconnect-internal.h.orig
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor