From: Daniel Lenski Date: Thu, 18 Feb 2021 03:23:36 +0000 (-0800) Subject: simpler fortinet_obtain_cookie() X-Git-Tag: v8.20~325^2~17 X-Git-Url: https://www.infradead.org/git/?a=commitdiff_plain;h=838fcd7ed9a3b529124c03c6a97e798478d6a8d6;p=users%2Fdwmw2%2Fopenconnect.git simpler fortinet_obtain_cookie() Just build a static form, containing two fields ('username', 'credential') like GP and F5. Signed-off-by: Daniel Lenski --- diff --git a/fortinet.c b/fortinet.c index 66a5bfc6..9b7130b2 100644 --- a/fortinet.c +++ b/fortinet.c @@ -29,8 +29,8 @@ #include #include -#include -#include +#include +#include #include "openconnect-internal.h" @@ -63,11 +63,9 @@ int fortinet_obtain_cookie(struct openconnect_info *vpninfo) { int ret; struct oc_text_buf *resp_buf = NULL; - xmlDocPtr doc = NULL; - xmlNodePtr node; struct oc_auth_form *form = NULL; - struct oc_vpn_option *cookie; - char *form_name = NULL; + struct oc_form_opt *opt, *opt2; + char *form_buf = NULL; resp_buf = buf_alloc(); if (buf_error(resp_buf)) { @@ -75,124 +73,86 @@ int fortinet_obtain_cookie(struct openconnect_info *vpninfo) goto out; } + ret = do_https_request(vpninfo, "GET", NULL, NULL, &form_buf, 1); + free(form_buf); + form_buf = NULL; + if (ret < 0) + goto out; + /* XX: Fortinet HTML forms *seem* like they should be about as easy to follow * as Juniper HTML forms, but some redirects use Javascript EXCLUSIVELY (no - * 'Location' header). + * 'Location' header). Also, a failed login returns the misleading HTTP status + * "405 Method Not Allowed", rather than 403/401. * - * Also, a failed login returns the misleading HTTP status "405 Method Not Allowed", - * rather than 403/401. + * So we just build a static form (username and password). */ - while (1) { - char *form_buf = NULL; - char *url; - - if (resp_buf && resp_buf->pos) - ret = do_https_request(vpninfo, "POST", - "application/x-www-form-urlencoded", - resp_buf, &form_buf, 1); - else - ret = do_https_request(vpninfo, "GET", NULL, NULL, - &form_buf, 1); - - /* XX: special-cased, because most Fortinet servers return a Javascript-only redirect from - * a status 405 page, after a failed login. - */ - if (ret == -EACCES) { - free(form_buf); - buf_truncate(resp_buf); - goto try_remote_login; - } - - for (cookie = vpninfo->cookies; cookie; cookie = cookie->next) { - if (!strcmp(cookie->option, "SVPNCOOKIE")) { - vpninfo->cookie = strdup(cookie->value); - free(form_buf); - ret = 0; - goto out; - } - } + 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("credential"); + opt2->type = OC_FORM_OPT_PASSWORD; - url = internal_get_url(vpninfo); - if (!url) { - free(form_buf); - ret = -ENOMEM; - break; - } + free(vpninfo->urlpath); + vpninfo->urlpath = strdup("remote/logincheck"); - doc = htmlReadMemory(form_buf, ret, url, NULL, - HTML_PARSE_RECOVER|HTML_PARSE_NOERROR|HTML_PARSE_NOWARNING|HTML_PARSE_NONET); - free(url); - free(form_buf); - if (!doc) { - vpn_progress(vpninfo, PRG_ERR, - _("Failed to parse HTML document\n")); - ret = -EINVAL; - break; - } + /* XX: submit form repeatedly until success? */ + for (;;) { + ret = process_auth_form(vpninfo, form); + if (ret == OC_FORM_RESULT_CANCELLED || ret < 0) + goto out; buf_truncate(resp_buf); - - node = find_form_node(doc); - if (!node) { - /* XX: special-cased, because most Fortinet servers return a Javascript-only redirect from 'GET /' */ - if (!vpninfo->urlpath) { - try_remote_login: - vpninfo->urlpath = strdup("remote/login"); - continue; + append_form_opts(vpninfo, form, resp_buf); + append_opt(resp_buf, "realm", vpninfo->authgroup ?: ""); + buf_append(resp_buf, "&ajax=1&just_logged_in=1"); + + if ((ret = buf_error(resp_buf))) + goto out; + ret = do_https_request(vpninfo, "POST", "application/x-www-form-urlencoded", + resp_buf, &form_buf, 0); + + /* XX: if this worked, we should have 200 status */ + if (ret >= 0) { + /* If we got SVPNCOOKIE, then we're done. */ + struct oc_vpn_option *cookie; + for (cookie = vpninfo->cookies; cookie; cookie = cookie->next) { + if (!strcmp(cookie->option, "SVPNCOOKIE")) { + free(vpninfo->cookie); + vpninfo->cookie = strdup(cookie->value); + if (!vpninfo->cookie) + goto nomem; + ret = 0; + goto out; + } } - vpn_progress(vpninfo, PRG_ERR, - _("Failed to find or parse web form in login page\n")); - ret = -EINVAL; - break; - } - free(form_name); - form_name = (char *)xmlGetProp(node, (unsigned char *)"name"); - if (form_name && !strcmp(form_name, "f")) { - form = parse_form_node(vpninfo, node, NULL, NULL); - } else { - vpn_progress(vpninfo, PRG_ERR, - _("Unknown form name '%s'\n"), - form_name); - - fprintf(stderr, _("Dumping unknown HTML form:\n")); - htmlNodeDumpFileFormat(stderr, node->doc, node, NULL, 1); - ret = -EINVAL; - break; - } - if (!form) { - ret = -EINVAL; - break; + /* XX: We didn't get SVPNCOOKIE. 2FA? */ + if (!strncmp(form_buf, "ret=", 4) && strstr(form_buf, ",tokeninfo=")) { + vpn_progress(vpninfo, PRG_ERR, + _("Got 2FA form response. Not yet implemented.\n")); + ret = -EINVAL; + goto out; + } } - - /* XX: Fortinet servers put extra fields after the . - * Sometimes they're even after the . Great job, Fortinet. - */ - for (node = htmlnode_next(NULL, node); node; node = htmlnode_dive(NULL, node)) - if (node->name && !strcasecmp((char *)node->name, "input")) - parse_input_node(vpninfo, form, node, NULL, NULL); - - ret = process_auth_form(vpninfo, form); - if (ret) - break; - - append_form_opts(vpninfo, form, resp_buf); - ret = buf_error(resp_buf); - if (ret) - break; - - vpninfo->redirect_url = form->action; - form->action = NULL; - free_auth_form(form); - form = NULL; - handle_redirect(vpninfo); - xmlFreeDoc(doc); - doc = NULL; + free(form_buf); } + out: - if (doc) - xmlFreeDoc(doc); - free(form_name); + free(form_buf); if (form) free_auth_form(form); buf_free(resp_buf); diff --git a/http.c b/http.c index 53dbeefd..8bd60451 100644 --- a/http.c +++ b/http.c @@ -735,7 +735,7 @@ int process_http_response(struct openconnect_info *vpninfo, int connect, ret = i; } else { vpn_progress(vpninfo, PRG_ERR, - _("Error in chunked decoding. Expected '', got: '%s'"), + _("Error in chunked decoding. Expected '', got: '%s'\n"), clen_buf); ret = -EINVAL; } @@ -1205,8 +1205,6 @@ int do_https_request(struct openconnect_info *vpninfo, const char *method, result); if (result == 401 || result == 403) result = -EPERM; - else if (result == 405) /* Fortinet invalid username/password "Method Not Allowed" */ - result = -EACCES; else if (result == 512) /* GlobalProtect invalid username/password */ result = -EACCES; else