]> www.infradead.org Git - users/dwmw2/openconnect.git/commitdiff
rename (resp_buf, form_buf) → (req_buf, resp_buf) in f5.c and fortinet.c
authorDaniel Lenski <dlenski@gmail.com>
Tue, 23 Feb 2021 01:28:05 +0000 (17:28 -0800)
committerDaniel Lenski <dlenski@gmail.com>
Mon, 29 Mar 2021 03:57:25 +0000 (20:57 -0700)
The current names were borrowed from Juniper source, and are too confusing.

The newly-renamed 'req_buf' now contains data that we send to the servers,
and 'resp_buf' contains data that the server sends back to us.

Signed-off-by: Daniel Lenski <dlenski@gmail.com>
auth-html.c
auth-juniper.c
f5.c
fortinet.c
openconnect-internal.h
tests/fake-f5-server.py

index 5b7bbcc4081d27cd90011e9c243a5aed43c8954e..af1373dc87780d26382f8542bb7aa14d7bd0b262 100644 (file)
@@ -54,7 +54,7 @@ xmlNodePtr find_form_node(xmlDocPtr doc)
 }
 
 int parse_input_node(struct openconnect_info *vpninfo, struct oc_auth_form *form,
-                    xmlNodePtr node, const char *submit_button,
+                    xmlNodePtr node, const char *submit_button, int flavor,
                     int (*can_gen_tokencode)(struct openconnect_info *vpninfo, struct oc_auth_form *form, struct oc_form_opt *opt))
 {
        char *type = (char *)xmlGetProp(node, (unsigned char *)"type"), *style = (char *)xmlGetProp(node, (unsigned char *)"style");
@@ -93,14 +93,15 @@ int parse_input_node(struct openconnect_info *vpninfo, struct oc_auth_form *form
                        ret = -ENOMEM;
                        goto out;
                }
-               if (!strcmp(form->auth_id, "loginForm") &&
+               if (flavor == FORM_FLAVOR_JUNIPER &&
+                   !strcmp(form->auth_id, "loginForm") &&
                    !strcmp(opt->name, "VerificationCode") &&
                    can_gen_tokencode && !can_gen_tokencode(vpninfo, form, opt))
                        opt->type = OC_FORM_OPT_TOKEN;
-       } else if (!strcasecmp(type, "submit")) {
+       } else if (flavor == FORM_FLAVOR_JUNIPER && !strcasecmp(type, "submit")) {
+
                xmlnode_get_prop(node, "name", &opt->name);
                if (opt->name && submit_button && (!strcmp(opt->name, submit_button) ||
-                                                  /* XX: Juniper-specific */
                                                   !strcmp(opt->name, "sn-postauth-proceed") ||
                                                   !strcmp(opt->name, "sn-preauth-proceed") ||
                                                   !strcmp(opt->name, "secidactionEnter"))) {
@@ -148,7 +149,7 @@ int parse_input_node(struct openconnect_info *vpninfo, struct oc_auth_form *form
 }
 
 int parse_select_node(struct openconnect_info *vpninfo, struct oc_auth_form *form,
-                     xmlNodePtr node)
+                     xmlNodePtr node, int flavor)
 {
        xmlNodePtr child;
        struct oc_form_opt_select *opt;
@@ -161,7 +162,8 @@ int parse_select_node(struct openconnect_info *vpninfo, struct oc_auth_form *for
        xmlnode_get_prop(node, "name", &opt->form.name);
        opt->form.label = strdup(opt->form.name);
        opt->form.type = OC_FORM_OPT_SELECT;
-       if (!strcmp(opt->form.name, "realm")) /* XX: Juniper-specific */
+       if ((flavor == FORM_FLAVOR_JUNIPER && !strcmp(opt->form.name, "realm")) ||
+           (flavor == FORM_FLAVOR_F5 && !strcmp(opt->form.name, "domain")))
                form->authgroup_opt = opt;
 
        for (child = node->children; child; child = child->next) {
@@ -194,7 +196,7 @@ int parse_select_node(struct openconnect_info *vpninfo, struct oc_auth_form *for
 }
 
 struct oc_auth_form *parse_form_node(struct openconnect_info *vpninfo,
-                                     xmlNodePtr node, const char *submit_button,
+                                     xmlNodePtr node, const char *submit_button, int flavor,
                                      int (*can_gen_tokencode)(struct openconnect_info *vpninfo, struct oc_auth_form *form, struct oc_form_opt *opt))
 {
        struct oc_auth_form *form = calloc(1, sizeof(*form));
@@ -205,29 +207,53 @@ struct oc_auth_form *parse_form_node(struct openconnect_info *vpninfo,
 
        xmlnode_get_prop(node, "method", &form->method);
        xmlnode_get_prop(node, "action", &form->action);
-       if (!form->method || strcasecmp(form->method, "POST") ||
-           !form->action || !form->action[0]) {
+       if (!form->method || strcasecmp(form->method, "POST")) {
                vpn_progress(vpninfo, PRG_ERR,
                             _("Cannot handle form method='%s', action='%s'\n"),
                             form->method, form->action);
                free(form);
                return NULL;
        }
-       xmlnode_get_prop(node, "name", &form->auth_id);
-       form->banner = strdup(form->auth_id);
+
+       if (flavor == FORM_FLAVOR_JUNIPER) {
+               xmlnode_get_prop(node, "name", &form->auth_id);
+               form->banner = strdup(form->auth_id);
+       } else if (flavor == FORM_FLAVOR_F5)
+               xmlnode_get_prop(node, "id", &form->auth_id);
 
        for (child = htmlnode_dive(node, node); child && child != node; child = htmlnode_dive(node, child)) {
                if (!child->name)
                        continue;
 
                if (!strcasecmp((char *)child->name, "input"))
-                       parse_input_node(vpninfo, form, child, submit_button, can_gen_tokencode);
+                       parse_input_node(vpninfo, form, child, submit_button, flavor, can_gen_tokencode);
                else if (!strcasecmp((char *)child->name, "select")) {
-                       parse_select_node(vpninfo, form, child);
+                       parse_select_node(vpninfo, form, child, flavor);
                        /* Skip its children */
                        while (child->children)
                                child = child->last;
-               } else if (!strcasecmp((char *)child->name, "textarea")) {
+               } else if (flavor == FORM_FLAVOR_F5
+                          && !strcasecmp((char *)child->name, "td")) {
+
+                       char *id = (char *)xmlGetProp(child, (unsigned char *)"id");
+                       if (id && !strcmp(id, "credentials_table_header")) {
+                               char *msg = (char *)xmlNodeGetContent(child);
+                               if (msg) {
+                                       free(form->banner);
+                                       form->banner = msg;
+                               }
+                       } else if (id && !strcmp(id, "credentials_table_postheader")) {
+                               char *msg = (char *)xmlNodeGetContent(child);
+                               if (msg) {
+                                       free(form->message);
+                                       form->message = msg;
+                               }
+                       }
+                       free(id);
+
+               } else if (flavor == FORM_FLAVOR_JUNIPER &&
+                          !strcasecmp((char *)child->name, "textarea")) {
+
                        /* display the post sign-in message, if any */
                        char *fieldname = (char *)xmlGetProp(child, (unsigned char *)"name");
                        if (fieldname && (!strcasecmp(fieldname, "sn-postauth-text") || /* XX: Juniper-specific */
index 0f1809040316d601711b91e2e4890086d6ee5c14..d298dc65f6d161972a6ac8cce38a4e0a1bdaebc1 100644 (file)
@@ -516,14 +516,14 @@ int oncp_obtain_cookie(struct openconnect_info *vpninfo)
                                     _("Encountered form with no 'name' or 'id'\n"));
                        goto dump_form;
                } else if (form_name && !strcmp(form_name, "frmLogin")) {
-                       form = parse_form_node(vpninfo, node, "btnSubmit", oncp_can_gen_tokencode);
+                       form = parse_form_node(vpninfo, node, "btnSubmit", FORM_FLAVOR_JUNIPER, oncp_can_gen_tokencode);
                } else if (form_id && !strcmp(form_id, "loginForm")) {
-                       form = parse_form_node(vpninfo, node, "submitButton", oncp_can_gen_tokencode);
+                       form = parse_form_node(vpninfo, node, "submitButton", FORM_FLAVOR_JUNIPER, oncp_can_gen_tokencode);
                } else if ((form_name && !strcmp(form_name, "frmDefender")) ||
                           (form_name && !strcmp(form_name, "frmNextToken"))) {
-                       form = parse_form_node(vpninfo, node, "btnAction", oncp_can_gen_tokencode);
+                       form = parse_form_node(vpninfo, node, "btnAction", FORM_FLAVOR_JUNIPER, oncp_can_gen_tokencode);
                } else if (form_name && !strcmp(form_name, "frmConfirmation")) {
-                       form = parse_form_node(vpninfo, node, "btnContinue", oncp_can_gen_tokencode);
+                       form = parse_form_node(vpninfo, node, "btnContinue", FORM_FLAVOR_JUNIPER, oncp_can_gen_tokencode);
                        if (!form) {
                                ret = -EINVAL;
                                break;
@@ -534,10 +534,10 @@ int oncp_obtain_cookie(struct openconnect_info *vpninfo)
                        form = parse_roles_form_node(node);
                        role_select = 1;
                } else if (form_name && !strcmp(form_name, "frmTotpToken")) {
-                       form = parse_form_node(vpninfo, node, "totpactionEnter", oncp_can_gen_tokencode);
+                       form = parse_form_node(vpninfo, node, "totpactionEnter", FORM_FLAVOR_JUNIPER, oncp_can_gen_tokencode);
                } else if ((form_name && !strcmp(form_name, "hiddenform")) ||
                           (form_id && !strcmp(form_id, "formSAMLSSO"))) {
-                       form = parse_form_node(vpninfo, node, "submit", oncp_can_gen_tokencode);
+                       form = parse_form_node(vpninfo, node, "submit", FORM_FLAVOR_JUNIPER, oncp_can_gen_tokencode);
                } else {
                        char *form_action = (char *)xmlGetProp(node, (unsigned char *)"action");
                        if (form_action && strstr(form_action, "remediate.cgi")) {
diff --git a/f5.c b/f5.c
index f14236d874fb4b628ccc731ae5a8ef44aaa6fad8..80eedc90c4fe4f3993ef77dff25f7ca1debef326 100644 (file)
--- a/f5.c
+++ b/f5.c
 #include <stdarg.h>
 #include <sys/types.h>
 
+#include <libxml/HTMLparser.h>
+#include <libxml/HTMLtree.h>
+
 #include "openconnect-internal.h"
 
 #define XCAST(x) ((const xmlChar *)(x))
 
+static int check_cookie_success(struct openconnect_info *vpninfo)
+{
+       struct oc_vpn_option *cookie;
+       const char *session = NULL, *f5_st = NULL;
+
+       /* XX: if login succeeded worked, we should have a response size of zero, and F5_ST
+        * and MRHSession cookies in the response.
+        */
+       for (cookie = vpninfo->cookies; cookie; cookie = cookie->next) {
+               if (!strcmp(cookie->option, "MRHSession"))
+                       session = cookie->value;
+               else if (!strcmp(cookie->option, "F5_ST"))
+                       f5_st = cookie->value;
+       }
+       if (session && f5_st) {
+               free(vpninfo->cookie);
+               if (asprintf(&vpninfo->cookie, "MRHSession=%s; F5_ST=%s", session, f5_st) <= 0)
+                       return -ENOMEM;
+               return 0;
+       }
+       return -ENOENT;
+}
+
 int f5_obtain_cookie(struct openconnect_info *vpninfo)
 {
        int ret;
-       struct oc_text_buf *resp_buf = NULL;
-       char *form_buf = NULL;
+       xmlDocPtr doc = NULL;
+       xmlNode *node;
+       struct oc_text_buf *req_buf = NULL;
        struct oc_auth_form *form = NULL;
-       struct oc_form_opt *opt, *opt2;
+       char *form_id = NULL;
 
-       resp_buf = buf_alloc();
-       if ((ret = buf_error(resp_buf)))
+       req_buf = buf_alloc();
+       if ((ret = buf_error(req_buf)))
                goto out;
 
-       /* XX: This initial 'GET /' seems to be necessary to populate LastMRH_Session and
-        * MRHSession cookies, without which the subsequent 'POST' will fail.
-        */
-       ret = do_https_request(vpninfo, "GET", NULL, NULL, &form_buf, 1);
-       free(form_buf);
-       form_buf = NULL;
-       if (ret < 0)
-               return ret;
+       while (1) {
+               char *resp_buf = NULL;
+               char *url;
 
-       /* XX: build static form (username and password) */
-       /* TODO: parse out 'domain' form field from /my.policy as authgroup */
-       form = calloc(1, sizeof(*form));
-       if (!form) {
-       nomem:
-               ret = -ENOMEM;
-               goto out;
-       }
-       opt = form->opts = calloc(1, sizeof(*opt));
-       if (!opt)
-               goto nomem;
-       opt->label = strdup("Username: ");
-       opt->name = strdup("username");
-       opt->type = OC_FORM_OPT_TEXT;
-
-       opt2 = opt->next = calloc(1, sizeof(*opt2));
-       if (!opt2)
-               goto nomem;
-       opt2->label = strdup("Password: ");
-       opt2->name = strdup("password");
-       opt2->type = OC_FORM_OPT_PASSWORD;
-
-       /* XX: submit form repeatedly until success? */
-       for (;;) {
-               ret = process_auth_form(vpninfo, form);
-               if (ret == OC_FORM_RESULT_CANCELLED || ret < 0)
-                       goto out;
+               if (req_buf && req_buf->pos)
+                       ret = do_https_request(vpninfo, "POST",
+                                              "application/x-www-form-urlencoded",
+                                              req_buf, &resp_buf, 2);
+               else
+                       ret = do_https_request(vpninfo, "GET", NULL, NULL,
+                                              &resp_buf, 2);
 
-               buf_truncate(resp_buf);
-               append_form_opts(vpninfo, form, resp_buf);
-               if (vpninfo->authgroup)
-                       append_opt(resp_buf, "domain", vpninfo->authgroup);
+               if (ret < 0)
+                       break;
 
-               if ((ret = buf_error(resp_buf)))
-                       goto out;
-               do_https_request(vpninfo, "POST", "application/x-www-form-urlencoded",
-                                resp_buf, &form_buf, 0);
+               if (!check_cookie_success(vpninfo)) {
+                       free(resp_buf);
+                       ret = 0;
+                       break;
+               }
 
-               /* XX: if this worked, we should have a response size of zero, and F5_ST
-                * and MRHSession cookies in the response.
-                */
-               if (!form_buf || *form_buf == '\0') {
-                       struct oc_vpn_option *cookie;
-                       const char *session=NULL, *f5_st=NULL;
-                       for (cookie = vpninfo->cookies; cookie; cookie = cookie->next) {
-                               if (!strcmp(cookie->option, "MRHSession"))
-                                       session = cookie->value;
-                               else if (!strcmp(cookie->option, "F5_ST"))
-                                       f5_st = cookie->value;
-                       }
+               url = internal_get_url(vpninfo);
+               if (!url) {
+                       free(resp_buf);
+               nomem:
+                       ret = -ENOMEM;
+                       break;
+               }
 
-                       if (session && f5_st) {
-                               free(vpninfo->cookie);
-                               if (asprintf(&vpninfo->cookie, "MRHSession=%s; F5_ST=%s",
-                                            session, f5_st) <= 0)
-                                       goto nomem;
-                               ret = 0;
-                               goto out;
+               doc = htmlReadMemory(resp_buf, ret, url, NULL,
+                                    HTML_PARSE_RECOVER|HTML_PARSE_NOERROR|HTML_PARSE_NOWARNING|HTML_PARSE_NONET);
+               free(url);
+               free(resp_buf);
+               if (!doc) {
+                       vpn_progress(vpninfo, PRG_ERR,
+                                    _("Failed to parse HTML document\n"));
+                       ret = -EINVAL;
+                       break;
+               }
+
+               buf_truncate(req_buf);
+
+               node = find_form_node(doc);
+               if (!node) {
+                       /* XX: some F5 VPNs simply do not have a static HTML form to parse */
+                       struct oc_form_opt *opt, *opt2;
+
+                       vpn_progress(vpninfo, PRG_ERR,
+                                    _("WARNING: no HTML login form found; assuming username and password fields\n"));
+
+                       form = calloc(1, sizeof(*form));
+                       if (!form)
+                               goto nomem;
+                       opt = form->opts = calloc(1, sizeof(*opt));
+                       if (!opt)
+                               goto nomem;
+                       opt->label = strdup("username:");
+                       opt->name = strdup("username");
+                       opt->type = OC_FORM_OPT_TEXT;
+
+                       opt2 = opt->next = calloc(1, sizeof(*opt2));
+                       if (!opt2)
+                               goto nomem;
+                       opt2->label = strdup("password:");
+                       opt2->name = strdup("password");
+                       opt2->type = OC_FORM_OPT_PASSWORD;
+
+               } else {
+                       if (!xmlnode_get_prop(node, "id", &form_id) && !strcmp(form_id, "auth_form"))
+                               form = parse_form_node(vpninfo, node, NULL, FORM_FLAVOR_F5, NULL);
+                       else {
+                               vpn_progress(vpninfo, PRG_ERR, _("Unknown form ID '%s' (expected 'auth_form')\n"),
+                                            form_id);
+
+                               fprintf(stderr, _("Dumping unknown HTML form:\n"));
+                               htmlNodeDumpFileFormat(stderr, node->doc, node, NULL, 1);
+                               ret = -EINVAL;
+                               break;
                        }
                }
-               if (!form->message)
-                       form->message = strdup(_("Authentication failed. Please retry."));
-               nuke_opt_values(form->opts);
+
+               if (!form) {
+                       ret = -EINVAL;
+                       break;
+               }
+
+               /* XX: do_gen_tokencode would go here, if we knew of any
+                * token-based 2FA options for F5.
+                */
+
+               do {
+                       ret = process_auth_form(vpninfo, form);
+               } while (ret == OC_FORM_RESULT_NEWGROUP);
+               if (ret)
+                       goto out;
+
+               append_form_opts(vpninfo, form, req_buf);
+               if ((ret = buf_error(req_buf)))
+                       goto out;
+
+               if (form->action) {
+                       vpninfo->redirect_url = form->action;
+                       form->action = NULL;
+               }
+               free_auth_form(form);
+               form = NULL;
+               if (vpninfo->redirect_url)
+                       handle_redirect(vpninfo);
+
+               xmlFreeDoc(doc);
+               doc = NULL;
        }
 
  out:
+       if (doc)
+               xmlFreeDoc(doc);
+       free(form_id);
        if (form) free_auth_form(form);
-       if (resp_buf) buf_free(resp_buf);
+       if (req_buf) buf_free(req_buf);
        return ret;
 }
 
index c1a451a0d3149c705ba243e562639ab33e5f6470..eab64c375733a7bec7c7bf3d7615847b3e9305ce 100644 (file)
@@ -90,20 +90,20 @@ static int filter_opts(struct oc_text_buf *buf, const char *query, const char *i
 int fortinet_obtain_cookie(struct openconnect_info *vpninfo)
 {
        int ret;
-       struct oc_text_buf *resp_buf = NULL;
+       struct oc_text_buf *req_buf = NULL;
        struct oc_auth_form *form = NULL;
        struct oc_form_opt *opt, *opt2;
-       char *form_buf = NULL, *realm = NULL;
+       char *resp_buf = NULL, *realm = NULL;
 
-       resp_buf = buf_alloc();
-       if (buf_error(resp_buf)) {
-               ret = buf_error(resp_buf);
+       req_buf = buf_alloc();
+       if (buf_error(req_buf)) {
+               ret = buf_error(req_buf);
                goto out;
        }
 
-       ret = do_https_request(vpninfo, "GET", NULL, NULL, &form_buf, 1);
-       free(form_buf);
-       form_buf = NULL;
+       ret = do_https_request(vpninfo, "GET", NULL, NULL, &resp_buf, 1);
+       free(resp_buf);
+       resp_buf = NULL;
        if (ret < 0)
                goto out;
 
@@ -168,24 +168,24 @@ int fortinet_obtain_cookie(struct openconnect_info *vpninfo)
                        goto out;
                }
 
-               buf_truncate(resp_buf);
-               append_form_opts(vpninfo, form, resp_buf);
-               buf_append(resp_buf, "&realm=%s", realm ?: ""); /* XX: already URL-escaped */
+               buf_truncate(req_buf);
+               append_form_opts(vpninfo, form, req_buf);
+               buf_append(req_buf, "&realm=%s", realm ?: ""); /* XX: already URL-escaped */
 
                if (!form->action) {
                        /* "normal" form (fields 'username', 'credential') */
-                       buf_append(resp_buf, "&ajax=1&just_logged_in=1");
+                       buf_append(req_buf, "&ajax=1&just_logged_in=1");
                } else {
                        /* 2FA form (fields 'username', 'code', and a bunch of values
                         * from the previous response which we mindlessly parrot back)
                         */
-                       buf_append(resp_buf, "&code2=&%s", form->action);
+                       buf_append(req_buf, "&code2=&%s", form->action);
                }
 
-               if ((ret = buf_error(resp_buf)))
+               if ((ret = buf_error(req_buf)))
                        goto out;
                ret = do_https_request(vpninfo, "POST", "application/x-www-form-urlencoded",
-                                      resp_buf, &form_buf, 0);
+                                      req_buf, &resp_buf, 0);
 
                /* XX: if this worked, we should have 200 status */
                if (ret >= 0) {
@@ -203,7 +203,7 @@ int fortinet_obtain_cookie(struct openconnect_info *vpninfo)
                        }
 
                        /* XX: We didn't get SVPNCOOKIE. 2FA? */
-                       if (!strncmp(form_buf, "ret=", 4) && strstr(form_buf, ",tokeninfo=")) {
+                       if (!strncmp(resp_buf, "ret=", 4) && strstr(resp_buf, ",tokeninfo=")) {
                                const char *prompt;
                                struct oc_text_buf *action_buf = buf_alloc();
 
@@ -222,7 +222,7 @@ int fortinet_obtain_cookie(struct openconnect_info *vpninfo)
                                        opt2->type = OC_FORM_OPT_PASSWORD;
 
                                /* Save a bunch of values to parrot back */
-                               filter_opts(action_buf, form_buf, "reqid,polid,grp,portal,peer,magic", 1);
+                               filter_opts(action_buf, resp_buf, "reqid,polid,grp,portal,peer,magic", 1);
                                if ((ret = buf_error(action_buf)))
                                        goto out;
                                free(form->action);
@@ -230,7 +230,7 @@ int fortinet_obtain_cookie(struct openconnect_info *vpninfo)
                                action_buf->data = NULL;
                                buf_free(action_buf);
 
-                               if ((prompt = strstr(form_buf, ",chal_msg="))) {
+                               if ((prompt = strstr(resp_buf, ",chal_msg="))) {
                                        char *end = strchrnul(prompt, ',');
                                        prompt += 10;
                                        free(form->message);
@@ -242,10 +242,10 @@ int fortinet_obtain_cookie(struct openconnect_info *vpninfo)
 
  out:
        free(realm);
-       free(form_buf);
+       free(resp_buf);
        if (form)
                free_auth_form(form);
-       buf_free(resp_buf);
+       buf_free(req_buf);
        return ret;
 }
 
index 5e92e56e054dd2808ae1c702682e513183cdc274..c7fde10d16a81dedba5dc64b9f61d1549585bbcc 100644 (file)
@@ -185,6 +185,11 @@ struct pkt {
 #define DTLS_CONNECTING        4       /* ESP probe received; must tell server */
 #define DTLS_CONNECTED 5       /* Server informed and should be sending ESP */
 
+/* Flavors of HTML forms to screen-scrape */
+#define FORM_FLAVOR_JUNIPER    1
+#define FORM_FLAVOR_F5         2
+#define FORM_FLAVOR_FORTINET   3
+
 /* All supported PPP packet framings/encapsulations */
 #define PPP_ENCAP_RFC1661      1       /* Plain/synchronous/pre-framed PPP (RFC1661) */
 #define PPP_ENCAP_RFC1662_HDLC 2       /* PPP with HDLC-like framing (RFC1662) */
@@ -950,12 +955,12 @@ xmlNodePtr htmlnode_next(xmlNodePtr top, xmlNodePtr node);
 xmlNodePtr htmlnode_dive(xmlNodePtr top, xmlNodePtr node);
 xmlNodePtr find_form_node(xmlDocPtr doc);
 int parse_input_node(struct openconnect_info *vpninfo, struct oc_auth_form *form,
-                    xmlNodePtr node, const char *submit_button,
+                    xmlNodePtr node, const char *submit_button, int flavor,
                     int (*can_gen_tokencode)(struct openconnect_info *vpninfo, struct oc_auth_form *form, struct oc_form_opt *opt));
 int parse_select_node(struct openconnect_info *vpninfo, struct oc_auth_form *form,
-                     xmlNodePtr node);
+                     xmlNodePtr node, int flavor);
 struct oc_auth_form *parse_form_node(struct openconnect_info *vpninfo,
-                                    xmlNodePtr node, const char *submit_button,
+                                    xmlNodePtr node, const char *submit_button, int flavor,
                                     int (*can_gen_tokencode)(struct openconnect_info *vpninfo, struct oc_auth_form *form, struct oc_form_opt *opt));
 
 /* auth-juniper.c */
index 5981ce75090b68759ef7bbc27324c63d0954d003..88a5eb69a38cab3758e6fa302ab1f6945f8ca5df 100755 (executable)
@@ -85,26 +85,38 @@ def root():
     return redirect(url_for('get_policy'))
 
 
-# Respond to 'GET /my.policy with a placeholder stub (since OpenConnect doesn't even try to parse the form)
+# Respond to 'GET /my.policy with a login form
 @app.route('/my.policy')
 def get_policy():
     session.update(step='GET-login-form')
-    return 'login page'
+    return '''
+<html><body><form id="auth_form" method="post">
+<input type="text" name="username"/>
+<input type="password" name="password"/>
+</form></body></html>'''
 
 
-# Respond to 'POST /my.policy with an empty response containing MRHSession and F5_ST
+# Respond to 'POST /my.policy with a redirect response containing MRHSession and F5_ST
 # cookies (OpenConnect uses the combination of the two to detect successful authentication)
 @app.route('/my.policy', methods=['POST'])
 def post_policy():
     session.update(step='POST-login', username=request.form.get('username'), credential=request.form.get('password'))
     # print(session)
 
-    resp = make_response('')
+    resp = redirect(url_for('webtop'))
     resp.set_cookie('MRHSession', cookify(dict(session)))
     resp.set_cookie('F5_ST', '1z1z1z%dz%d' % (time.time(), 3600))
     return resp
 
 
+@app.route('/vdesk/webtop.eui')
+def webtop():
+    session.update(step='POST-login-webtop')
+    # print(session)
+
+    return 'some junk HTML webtop'
+
+
 # Respond to 'GET /vdesk/vpn/index.php3?outform=xml&client_version=2.0 with an XML config
 # [Save VPN resource name in the session for verification of client state later]
 @app.route('/vdesk/vpn/index.php3')