]> www.infradead.org Git - users/dwmw2/openconnect.git/commitdiff
start adding GP SSO support
authorDaniel Lenski <dlenski@gmail.com>
Tue, 5 Jan 2021 18:47:15 +0000 (10:47 -0800)
committerLuca Boccassi <bluca@debian.org>
Wed, 23 Feb 2022 19:22:53 +0000 (19:22 +0000)
Signed-off-by: Daniel Lenski <dlenski@gmail.com>
auth-globalprotect.c
auth.c
gpst.c
library.c
openconnect-internal.h

index 948a7e94589c3053971608065861889c5411aabe..c115b82bed564c8584ed34b978ec4793f040e109 100644 (file)
@@ -79,17 +79,24 @@ static int parse_prelogin_xml(struct openconnect_info *vpninfo, xmlNode *xml_nod
        struct oc_auth_form *form = NULL;
        struct oc_form_opt *opt, *opt2;
        char *prompt = NULL, *username_label = NULL, *password_label = NULL;
-       char *saml_method = NULL, *saml_path = NULL;
+       char *s = NULL, *saml_method = NULL, *saml_path = NULL;
        int result = 0;
 
        if (!xmlnode_is_named(xml_node, "prelogin-response"))
                goto out;
 
        for (xml_node = xml_node->children; xml_node; xml_node = xml_node->next) {
-               char *s = NULL;
-               if (!xmlnode_get_val(xml_node, "saml-request", &s)) {
+               xmlnode_get_val(xml_node, "saml-request", &s);
+               xmlnode_get_val(xml_node, "saml-auth-method", &saml_method);
+               xmlnode_get_val(xml_node, "authentication-message", &prompt);
+               xmlnode_get_val(xml_node, "username-label", &username_label);
+               xmlnode_get_val(xml_node, "password-label", &password_label);
+               /* XX: should we save the certificate username from <ccusername/> ? */
+       }
+
+       if (saml_method && s) {
+               if (!strcmp(saml_method, "REDIRECT")) {
                        int len;
-                       free(saml_path);
                        saml_path = openconnect_base64_decode(&len, s);
                        if (len < 0) {
                                vpn_progress(vpninfo, PRG_ERR, "Could not decode SAML request as base64: %s\n", s);
@@ -104,39 +111,27 @@ static int parse_prelogin_xml(struct openconnect_info *vpninfo, xmlNode *xml_nod
                                goto out;
                        }
                        saml_path[len] = '\0';
+                       vpninfo->sso_login = saml_path;
+               } else if (!strcmp(saml_method, "POST")) {
+                       const char *prefix = "data:text/html;base64,";
+                       saml_path = s;
+                       realloc_inplace(saml_path, strlen(saml_path)+strlen(prefix)+1);
+                       if (!saml_path) {
+                               result = -ENOMEM;
+                               goto out;
+                       }
+                       memmove(saml_path + strlen(prefix), saml_path, strlen(saml_path) + 1);
+                       memcpy(saml_path, prefix, strlen(prefix));
+                       vpninfo->sso_login = saml_path;
                } else {
-                       xmlnode_get_val(xml_node, "saml-auth-method", &saml_method);
-                       xmlnode_get_val(xml_node, "authentication-message", &prompt);
-                       xmlnode_get_val(xml_node, "username-label", &username_label);
-                       xmlnode_get_val(xml_node, "password-label", &password_label);
-                       /* XX: should we save the certificate username from <ccusername/> ? */
-               }
-       }
-
-       /* XX: Alt-secret form field must be specified for SAML, because we can't autodetect it */
-       if (saml_method || saml_path) {
-               if (ctx->portal_userauthcookie)
-                       vpn_progress(vpninfo, PRG_DEBUG, _("SAML authentication required; using portal-userauthcookie to continue SAML.\n"));
-               else if (ctx->portal_prelogonuserauthcookie)
-                       vpn_progress(vpninfo, PRG_DEBUG, _("SAML authentication required; using portal-prelogonuserauthcookie to continue SAML.\n"));
-               else if (ctx->alt_secret)
-                       vpn_progress(vpninfo, PRG_DEBUG, _("Destination form field %s was specified; assuming SAML %s authentication is complete.\n"),
-                                    ctx->alt_secret, saml_method);
-               else {
-                       if (saml_method && !strcmp(saml_method, "REDIRECT"))
-                               vpn_progress(vpninfo, PRG_ERR,
-                                            _("SAML %s authentication is required via %s\n"),
-                                            saml_method, saml_path);
-                       else
-                               vpn_progress(vpninfo, PRG_ERR,
-                                            _("SAML %s authentication is required via external script.\n"),
-                                            saml_method);
-                       vpn_progress(vpninfo, PRG_ERR,
-                                    _("When SAML authentication is complete, specify destination form field by appending :field_name to login URL.\n"));
-                       /* XX: EINVAL will lead to "failure to parse response", with unnecessary/confusing extra logging output */
-                       result = -EPERM;
+                       vpn_progress(vpninfo, PRG_ERR, "Unknown SAML method %s\n", saml_method);
+                       result = -EINVAL;
                        goto out;
                }
+
+               vpn_progress(vpninfo, PRG_INFO,
+                            _("SAML %s authentication is required via %s\n"),
+                            saml_method, saml_path);
        }
 
        /* Replace old form */
@@ -159,7 +154,7 @@ static int parse_prelogin_xml(struct openconnect_info *vpninfo, xmlNode *xml_nod
        if (asprintf(&opt->label, "%s: ", username_label ? : _("Username")) == 0)
                goto nomem;
        if (!ctx->username)
-               opt->type = OC_FORM_OPT_TEXT;
+               opt->type = saml_path ? OC_FORM_OPT_SSO_USER : OC_FORM_OPT_TEXT;
        else {
                opt->type = OC_FORM_OPT_HIDDEN;
                opt->_value = ctx->username;
@@ -182,16 +177,18 @@ static int parse_prelogin_xml(struct openconnect_info *vpninfo, xmlNode *xml_nod
         * password in the first form means we should treat the first
         * form's password as a token field.
         */
-       if (!can_gen_tokencode(vpninfo, form, opt2) && !ctx->alt_secret
-           && password_label && strcmp(password_label, "Password"))
+       if (saml_path)
+               opt2->type = OC_FORM_OPT_SSO_TOKEN;
+       else if (!can_gen_tokencode(vpninfo, form, opt2) && !ctx->alt_secret
+                && password_label && strcmp(password_label, "Password"))
                opt2->type = OC_FORM_OPT_TOKEN;
        else
                opt2->type = OC_FORM_OPT_PASSWORD;
 
        vpn_progress(vpninfo, PRG_TRACE, "Prelogin form %s: \"%s\" %s(%s)=%s, \"%s\" %s(%s)\n",
                     form->auth_id,
-                    opt->label, opt->name, opt->type == OC_FORM_OPT_TEXT ? "TEXT" : "HIDDEN", opt->_value,
-                    opt2->label, opt2->name, opt2->type == OC_FORM_OPT_PASSWORD ? "PASSWORD" : "TOKEN");
+                    opt->label, opt->name, opt->type == OC_FORM_OPT_SSO_USER ? "SSO" : opt->type == OC_FORM_OPT_TEXT ? "TEXT" : "HIDDEN", opt->_value,
+                    opt2->label, opt2->name, opt2->type == OC_FORM_OPT_SSO_TOKEN ? "SSO" : opt2->type == OC_FORM_OPT_PASSWORD ? "PASSWORD" : "TOKEN");
 
 out:
        free(prompt);
diff --git a/auth.c b/auth.c
index e5d975c56bebfebda3173402db6cb6599e3d7b2d..ad07b2a55e89e879ba806ade81656fc00b99c662 100644 (file)
--- a/auth.c
+++ b/auth.c
@@ -228,7 +228,7 @@ static int parse_form(struct openconnect_info *vpninfo, struct oc_auth_form *for
                } else if (!strcmp(input_type, "text")) {
                        opt->type = OC_FORM_OPT_TEXT;
                } else if (!strcmp(input_type, "sso")) {
-                       opt->type = OC_FORM_OPT_SSO;
+                       opt->type = OC_FORM_OPT_SSO_TOKEN;
                } else if (!strcmp(input_type, "password")) {
                        if (!cstp_can_gen_tokencode(vpninfo, form, opt))
                                opt->type = OC_FORM_OPT_TOKEN;
diff --git a/gpst.c b/gpst.c
index 25ba370faaaff618b0620978449721b0348f2052..b285ddde53df2331f29e97c3bc0eee4f8637c1f9 100644 (file)
--- a/gpst.c
+++ b/gpst.c
@@ -1325,6 +1325,34 @@ int gpst_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable)
        return work_done;
 }
 
+int gpst_sso_detect_done(struct openconnect_info *vpninfo,
+                        const struct oc_webview_result *result)
+{
+       int i;
+
+       for (i=0; result->headers != NULL && result->headers[i] != NULL; i+=2) {
+               const char *hname = result->headers[i], *hval = result->headers[i+1];
+               if (!strcmp(hname, "saml-username")) {
+                       free(vpninfo->sso_username);
+                       vpninfo->sso_username = strdup(hval);
+               } else if (!strcmp(hname, "prelogin-cookie") ||
+                          !strcmp(hname, "portal-userauthcookie")) {
+                       free(vpninfo->sso_token_cookie);
+                       free(vpninfo->sso_cookie_value);
+                       vpninfo->sso_token_cookie = strdup(hname);
+                       vpninfo->sso_cookie_value = strdup(hval);
+               }
+       }
+
+       if (vpninfo->sso_username && vpninfo->sso_token_cookie && vpninfo->sso_cookie_value) {
+               /* Not actually used at the moment, so don't fail if not set by the caller */
+               if (result->uri)
+                       vpninfo->sso_login_final = strdup(result->uri);
+               return 0;
+       } else
+               return -EAGAIN;
+}
+
 #ifdef HAVE_ESP
 static inline uint32_t csum_partial(uint16_t *buf, int nwords)
 {
index 443a98c7dac616a798c4c44f78f7f53646a690ee..1f78ea14b7423df44af6e23975687d0b20ce9b74 100644 (file)
--- a/library.c
+++ b/library.c
@@ -171,6 +171,7 @@ static const struct vpn_proto openconnect_protos[] = {
                .tcp_mainloop = gpst_mainloop,
                .add_http_headers = gpst_common_headers,
                .obtain_cookie = gpst_obtain_cookie,
+               .sso_detect_done = gpst_sso_detect_done,
                .udp_protocol = "ESP",
 #ifdef HAVE_ESP
                .udp_setup = esp_setup,
index 0b2a0847912642b41d755f131542085cd46ff771..644f51bf0e88eef11dba6f60d41aa3298e32578a 100644 (file)
@@ -1326,6 +1326,7 @@ int gpst_setup(struct openconnect_info *vpninfo);
 int gpst_mainloop(struct openconnect_info *vpninfo, int *timeout, int readable);
 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);
 
 /* lzs.c */
 int lzs_decompress(unsigned char *dst, int dstlen, const unsigned char *src, int srclen);