]> www.infradead.org Git - users/dwmw2/openconnect.git/commitdiff
auth-juniper.c simplifications (including ignoring submit_button if NULL)
authorDaniel Lenski <dlenski@gmail.com>
Sun, 7 Feb 2021 21:50:12 +0000 (13:50 -0800)
committerDaniel Lenski <dlenski@gmail.com>
Mon, 29 Mar 2021 02:26:42 +0000 (19:26 -0700)
Signed-off-by: Daniel Lenski <dlenski@gmail.com>
Makefile.am
auth-html.c [new file with mode: 0644]
auth-juniper.c
openconnect-internal.h

index 38a1fc4623c9a988da8e8a88a8f3b3b1c402b8e5..636e183f7a8b03b3ce0bb8a158acc3abb0af0db1 100644 (file)
@@ -30,7 +30,7 @@ openconnect_LDADD = libopenconnect.la $(SSL_LIBS) $(LIBXML2_LIBS) $(LIBPROXY_LIB
 if OPENCONNECT_WIN32
 openconnect_SOURCES += openconnect.rc
 endif
-library_srcs = ssl.c http.c http-auth.c auth-common.c library.c compat.c lzs.c mainloop.c script.c ntlm.c digest.c openconnect-internal.h
+library_srcs = ssl.c http.c http-auth.c auth-common.c auth-html.c library.c compat.c lzs.c mainloop.c script.c ntlm.c digest.c openconnect-internal.h
 lib_srcs_cisco = auth.c cstp.c
 lib_srcs_juniper = oncp.c lzo.c auth-juniper.c
 lib_srcs_pulse = pulse.c
diff --git a/auth-html.c b/auth-html.c
new file mode 100644 (file)
index 0000000..148ff3c
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * OpenConnect (SSL + DTLS) VPN client
+ *
+ * Copyright © 2015-2021 David Woodhouse, Daniel Lenski
+ *
+ * Author: David Woodhouse <dwmw2@infradead.org>, Daniel Lenski <dlenski@gmail.com>
+ *
+ * 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 <config.h>
+
+#include <errno.h>
+
+#include <libxml/HTMLparser.h>
+#include <libxml/HTMLtree.h>
+
+#include "openconnect-internal.h"
+
+xmlNodePtr htmlnode_next(xmlNodePtr top, xmlNodePtr node)
+{
+       if (node->children)
+               return node->children;
+
+       while (!node->next) {
+               node = node->parent;
+               if (!node || node == top)
+                       return NULL;
+       }
+       return node->next;
+}
+
+xmlNodePtr find_form_node(xmlDocPtr doc)
+{
+       xmlNodePtr root, node;
+
+       for (root = node = xmlDocGetRootElement(doc); node; node = htmlnode_next(root, node)) {
+               if (node->name && !strcasecmp((char *)node->name, "form"))
+                       return node;
+       }
+       return NULL;
+}
+
+int parse_input_node(struct openconnect_info *vpninfo, struct oc_auth_form *form,
+                    xmlNodePtr node, const char *submit_button,
+                    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");
+       struct oc_form_opt **p = &form->opts;
+       struct oc_form_opt *opt;
+       int ret = 0;
+
+       if (!type)
+               return -EINVAL;
+
+       opt = calloc(1, sizeof(*opt));
+       if (!opt) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       if (!strcasecmp(type, "hidden")) {
+               opt->type = OC_FORM_OPT_HIDDEN;
+               xmlnode_get_prop(node, "name", &opt->name);
+               xmlnode_get_prop(node, "value", &opt->_value);
+               /* XXX: Handle tz_offset / tz */
+       } else if (!strcasecmp(type, "password")) {
+               opt->type = OC_FORM_OPT_PASSWORD;
+               xmlnode_get_prop(node, "name", &opt->name);
+               if (asprintf(&opt->label, "%s:", opt->name) == -1) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
+               if (can_gen_tokencode && !can_gen_tokencode(vpninfo, form, opt))
+                       opt->type = OC_FORM_OPT_TOKEN;
+       } else if (!strcasecmp(type, "text") || !strcasecmp(type, "username") || !strcasecmp(type, "email")) {
+               opt->type = OC_FORM_OPT_TEXT;
+               xmlnode_get_prop(node, "name", &opt->name);
+               if (asprintf(&opt->label, "%s:", opt->name) == -1) {
+                       ret = -ENOMEM;
+                       goto out;
+               }
+               if (!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")) {
+               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"))) {
+                       /* Use this as the 'Submit' action for the form, by
+                          implicitly adding it as a hidden option. */
+                       xmlnode_get_prop(node, "value", &opt->_value);
+                       opt->type = OC_FORM_OPT_HIDDEN;
+               } else {
+                       vpn_progress(vpninfo, PRG_DEBUG,
+                                    _("Ignoring unknown form submit item '%s'\n"),
+                                    opt->name);
+                       ret = -EINVAL;
+                       goto out;
+               }
+       } else if (!strcasecmp(type, "checkbox")) {
+               opt->type = OC_FORM_OPT_HIDDEN;
+               xmlnode_get_prop(node, "name", &opt->name);
+               xmlnode_get_prop(node, "value", &opt->_value);
+       } else {
+               vpn_progress(vpninfo, PRG_DEBUG,
+                            _("Ignoring unknown form input type '%s'\n"),
+                            type);
+               ret = -EINVAL;
+               goto out;
+       }
+
+       /* Append to the existing list */
+       while (*p) {
+               if (!strcmp((*p)->name, opt->name)) {
+                       vpn_progress(vpninfo, PRG_DEBUG,
+                                    _("Discarding duplicate option '%s'\n"),
+                                    opt->name);
+                       free_opt(opt);
+                       goto out;
+               }
+               p = &(*p)->next;
+       }
+       *p = opt;
+ out:
+       if (ret)
+               free_opt(opt);
+       free(type);
+       return ret;
+}
+
+int parse_select_node(struct openconnect_info *vpninfo, struct oc_auth_form *form,
+                     xmlNodePtr node)
+{
+       xmlNodePtr child;
+       struct oc_form_opt_select *opt;
+       struct oc_choice *choice;
+
+       opt = calloc(1, sizeof(*opt));
+       if (!opt)
+               return -ENOMEM;
+
+       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 */
+               form->authgroup_opt = opt;
+
+       for (child = node->children; child; child = child->next) {
+               struct oc_choice **new_choices;
+               if (!child->name || strcasecmp((const char *)child->name, "option"))
+                       continue;
+
+               choice = calloc(1, sizeof(*choice));
+               if (!choice) {
+                       free_opt((void *)opt);
+                       return -ENOMEM;
+               }
+
+               xmlnode_get_prop(node, "name", &choice->name);
+               choice->label = (char *)xmlNodeGetContent(child);
+               choice->name = strdup(choice->label);
+               new_choices = realloc(opt->choices, sizeof(opt->choices[0]) * (opt->nr_choices+1));
+               if (!new_choices) {
+                       free_opt((void *)opt);
+                       free(choice);
+                       return -ENOMEM;
+               }
+               opt->choices = new_choices;
+               opt->choices[opt->nr_choices++] = choice;
+       }
+
+       /* Prepend to the existing list */
+       opt->form.next = form->opts;
+       form->opts = &opt->form;
+       return 0;
+}
+
+struct oc_auth_form *parse_form_node(struct openconnect_info *vpninfo,
+                                     xmlNodePtr node, const char *submit_button,
+                                     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));
+       xmlNodePtr child;
+
+       if (!form)
+               return NULL;
+
+       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]) {
+               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);
+
+       for (child = htmlnode_next(node, node); child && child != node; child = htmlnode_next(node, child)) {
+               if (!child->name)
+                       continue;
+
+               if (!strcasecmp((char *)child->name, "input"))
+                       parse_input_node(vpninfo, form, child, submit_button, can_gen_tokencode);
+               else if (!strcasecmp((char *)child->name, "select")) {
+                       parse_select_node(vpninfo, form, child);
+                       /* Skip its children */
+                       while (child->children)
+                               child = child->last;
+               } else if (!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 */
+                                         !strcasecmp(fieldname, "sn-preauth-text"))) {
+                               char *postauth_msg = (char *)xmlNodeGetContent(child);
+                               if (postauth_msg) {
+                                       free(form->banner);
+                                       form->banner = postauth_msg;
+                               }
+                       } else {
+                               vpn_progress(vpninfo, PRG_ERR,
+                                            _("Unknown textarea field: '%s'\n"), fieldname);
+                       }
+                       free(fieldname);
+               }
+       }
+       return form;
+}
index edcd1e8cf0bb82028d518b62f2a1d51239ea1c41..17c0b707e561bac28b7ac472e7e500f4e06ad7ea 100644 (file)
@@ -52,20 +52,6 @@ void oncp_common_headers(struct openconnect_info *vpninfo, struct oc_text_buf *b
 //     buf_append(buf, "Accept-Encoding: gzip\r\n");
 }
 
-
-static xmlNodePtr htmlnode_next(xmlNodePtr top, xmlNodePtr node)
-{
-       if (node->children)
-               return node->children;
-
-       while (!node->next) {
-               node = node->parent;
-               if (!node || node == top)
-                       return NULL;
-       }
-       return node->next;
-}
-
 static int oncp_can_gen_tokencode(struct openconnect_info *vpninfo,
                                  struct oc_auth_form *form,
                                  struct oc_form_opt *opt)
@@ -97,215 +83,6 @@ static int oncp_can_gen_tokencode(struct openconnect_info *vpninfo,
 }
 
 
-static int parse_input_node(struct openconnect_info *vpninfo, struct oc_auth_form *form,
-                           xmlNodePtr node, const char *submit_button)
-{
-       char *type = (char *)xmlGetProp(node, (unsigned char *)"type");
-       struct oc_form_opt **p = &form->opts;
-       struct oc_form_opt *opt;
-       int ret = 0;
-
-       if (!type)
-               return -EINVAL;
-
-       opt = calloc(1, sizeof(*opt));
-       if (!opt) {
-               ret = -ENOMEM;
-               goto out;
-       }
-
-       if (!strcasecmp(type, "hidden")) {
-               opt->type = OC_FORM_OPT_HIDDEN;
-               xmlnode_get_prop(node, "name", &opt->name);
-               xmlnode_get_prop(node, "value", &opt->_value);
-               /* XXX: Handle tz_offset / tz */
-       } else if (!strcasecmp(type, "password")) {
-               opt->type = OC_FORM_OPT_PASSWORD;
-               xmlnode_get_prop(node, "name", &opt->name);
-               if (asprintf(&opt->label, "%s:", opt->name) == -1) {
-                       ret = -ENOMEM;
-                       goto out;
-               }
-               if (!oncp_can_gen_tokencode(vpninfo, form, opt))
-                       opt->type = OC_FORM_OPT_TOKEN;
-       } else if (!strcasecmp(type, "text")) {
-               opt->type = OC_FORM_OPT_TEXT;
-               xmlnode_get_prop(node, "name", &opt->name);
-               if (asprintf(&opt->label, "%s:", opt->name) == -1) {
-                       ret = -ENOMEM;
-                       goto out;
-               }
-               if (!strcmp(form->auth_id, "loginForm") &&
-                   !strcmp(opt->name, "VerificationCode") &&
-                   !can_gen_tokencode(vpninfo, form, opt))
-                       opt->type = OC_FORM_OPT_TOKEN;
-       } else if (!strcasecmp(type, "username") || !strcasecmp(type, "email")) {
-               opt->type = OC_FORM_OPT_TEXT;
-               xmlnode_get_prop(node, "name", &opt->name);
-               if (asprintf(&opt->label, "%s:", opt->name) == -1) {
-                       ret = -ENOMEM;
-                       goto out;
-               }
-       } else if (!strcasecmp(type, "submit")) {
-               xmlnode_get_prop(node, "name", &opt->name);
-               if (opt->name && (!strcmp(opt->name, submit_button) ||
-                                 !strcmp(opt->name, "sn-postauth-proceed") ||
-                                 !strcmp(opt->name, "sn-preauth-proceed") ||
-                                 !strcmp(opt->name, "secidactionEnter"))) {
-                       /* Use this as the 'Submit' action for the form, by
-                          implicitly adding it as a hidden option. */
-                       xmlnode_get_prop(node, "value", &opt->_value);
-                       opt->type = OC_FORM_OPT_HIDDEN;
-               } else {
-                       vpn_progress(vpninfo, PRG_DEBUG,
-                                    _("Ignoring unknown form submit item '%s'\n"),
-                                    opt->name);
-                       ret = -EINVAL;
-                       goto out;
-               }
-       } else if (!strcasecmp(type, "checkbox")) {
-               opt->type = OC_FORM_OPT_HIDDEN;
-               xmlnode_get_prop(node, "name", &opt->name);
-               xmlnode_get_prop(node, "value", &opt->_value);
-       } else {
-               vpn_progress(vpninfo, PRG_DEBUG,
-                            _("Ignoring unknown form input type '%s'\n"),
-                            type);
-               ret = -EINVAL;
-               goto out;
-       }
-
-       /* Append to the existing list */
-       while (*p) {
-               if (!strcmp((*p)->name, opt->name)) {
-                       vpn_progress(vpninfo, PRG_DEBUG,
-                                    _("Discarding duplicate option '%s'\n"),
-                                    opt->name);
-                       free_opt(opt);
-                       goto out;
-               }
-               p = &(*p)->next;
-       }
-       *p = opt;
- out:
-       if (ret)
-               free_opt(opt);
-       free(type);
-       return ret;
-}
-
-static int parse_select_node(struct openconnect_info *vpninfo, struct oc_auth_form *form,
-                    xmlNodePtr node)
-{
-       xmlNodePtr child;
-       struct oc_form_opt_select *opt;
-       struct oc_choice *choice;
-
-       opt = calloc(1, sizeof(*opt));
-       if (!opt)
-               return -ENOMEM;
-
-       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"))
-               form->authgroup_opt = opt;
-
-       for (child = node->children; child; child = child->next) {
-               struct oc_choice **new_choices;
-               if (!child->name || strcasecmp((const char *)child->name, "option"))
-                       continue;
-
-               choice = calloc(1, sizeof(*choice));
-               if (!choice) {
-                       free_opt((void *)opt);
-                       return -ENOMEM;
-               }
-
-               xmlnode_get_prop(node, "name", &choice->name);
-               choice->label = (char *)xmlNodeGetContent(child);
-               choice->name = strdup(choice->label);
-               new_choices = realloc(opt->choices, sizeof(opt->choices[0]) * (opt->nr_choices+1));
-               if (!new_choices) {
-                       free_opt((void *)opt);
-                       free(choice);
-                       return -ENOMEM;
-               }
-               opt->choices = new_choices;
-               opt->choices[opt->nr_choices++] = choice;
-       }
-
-       /* Prepend to the existing list */
-       opt->form.next = form->opts;
-       form->opts = &opt->form;
-       return 0;
-}
-
-static struct oc_auth_form *parse_form_node(struct openconnect_info *vpninfo,
-                                           xmlNodePtr node, const char *submit_button)
-{
-       struct oc_auth_form *form = calloc(1, sizeof(*form));
-       xmlNodePtr child;
-
-       if (!form)
-               return NULL;
-
-       xmlnode_get_prop(node, "method", &form->method);
-       xmlnode_get_prop(node, "action", &form->action);
-       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;
-       }
-       /* XX: some forms have 'id', but no 'name' */
-       if (xmlnode_get_prop(node, "name", &form->auth_id))
-               xmlnode_get_prop(node, "id", &form->auth_id);
-       form->banner = strdup(form->auth_id);
-
-       for (child = htmlnode_next(node, node); child && child != node; child = htmlnode_next(node, child)) {
-               if (!child->name)
-                       continue;
-
-               if (!strcasecmp((char *)child->name, "input"))
-                       parse_input_node(vpninfo, form, child, submit_button);
-               else if (!strcasecmp((char *)child->name, "select")) {
-                       parse_select_node(vpninfo, form, child);
-                       /* Skip its children */
-                       while (child->children)
-                               child = child->last;
-               } else if (!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") ||
-                                         !strcasecmp(fieldname, "sn-preauth-text"))) {
-                               char *postauth_msg = (char *)xmlNodeGetContent(child);
-                               if (postauth_msg) {
-                                       free(form->banner);
-                                       form->banner = postauth_msg;
-                               }
-                       } else {
-                               vpn_progress(vpninfo, PRG_ERR,
-                                            _("Unknown textarea field: '%s'\n"), fieldname);
-                       }
-                       free(fieldname);
-               }
-       }
-       return form;
-}
-
-static xmlNodePtr find_form_node(xmlDocPtr doc)
-{
-       xmlNodePtr root, node;
-
-       for (root = node = xmlDocGetRootElement(doc); node; node = htmlnode_next(root, node)) {
-               if (node->name && !strcasecmp((char *)node->name, "form"))
-                       return node;
-       }
-       return NULL;
-}
-
 int oncp_send_tncc_command(struct openconnect_info *vpninfo, int start)
 {
        const char *dspreauth = vpninfo->csd_token, *dsurl = vpninfo->csd_starturl ? : "null";
@@ -746,26 +523,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");
-                       if (!form) {
-                               ret = -EINVAL;
-                               break;
-                       }
+                       form = parse_form_node(vpninfo, node, "btnSubmit", oncp_can_gen_tokencode);
                } else if (form_id && !strcmp(form_id, "loginForm")) {
-                       form = parse_form_node(vpninfo, node, "submitButton");
-                       if (!form) {
-                               ret = -EINVAL;
-                               break;
-                       }
+                       form = parse_form_node(vpninfo, node, "submitButton", 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");
-                       if (!form) {
-                               ret = -EINVAL;
-                               break;
-                       }
+                       form = parse_form_node(vpninfo, node, "btnAction", oncp_can_gen_tokencode);
                } else if (form_name && !strcmp(form_name, "frmConfirmation")) {
-                       form = parse_form_node(vpninfo, node, "btnContinue");
+                       form = parse_form_node(vpninfo, node, "btnContinue", oncp_can_gen_tokencode);
                        if (!form) {
                                ret = -EINVAL;
                                break;
@@ -774,24 +539,12 @@ int oncp_obtain_cookie(struct openconnect_info *vpninfo)
                        goto form_done;
                } else if (form_name && !strcmp(form_name, "frmSelectRoles")) {
                        form = parse_roles_form_node(node);
-                       if (!form) {
-                               ret = -EINVAL;
-                               break;
-                       }
                        role_select = 1;
                } else if (form_name && !strcmp(form_name, "frmTotpToken")) {
-                       form = parse_form_node(vpninfo, node, "totpactionEnter");
-                       if (!form) {
-                               ret = -EINVAL;
-                               break;
-                       }
+                       form = parse_form_node(vpninfo, node, "totpactionEnter", 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");
-                       if (!form) {
-                               ret = -EINVAL;
-                               break;
-                       }
+                       form = parse_form_node(vpninfo, node, "submit", oncp_can_gen_tokencode);
                } else {
                        char *form_action = (char *)xmlGetProp(node, (unsigned char *)"action");
                        if (form_action && strstr(form_action, "remediate.cgi")) {
@@ -811,6 +564,11 @@ int oncp_obtain_cookie(struct openconnect_info *vpninfo)
                        break;
                }
 
+               if (!form) {
+                       ret = -EINVAL;
+                       break;
+               }
+
                do {
                        ret = process_auth_form(vpninfo, form);
                } while (ret == OC_FORM_RESULT_NEWGROUP);
index 04776bdb783b28f1b87f8061edfa5fcd1dc1324e..b3e084f8c8d3ac6feb247a146c43b52870b651a5 100644 (file)
@@ -920,6 +920,18 @@ int decompress_and_queue_packet(struct openconnect_info *vpninfo, int compr_type
                                unsigned char *buf, int len);
 int compress_packet(struct openconnect_info *vpninfo, int compr_type, struct pkt *this);
 
+/* html-auth.c */
+xmlNodePtr htmlnode_next(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,
+                    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);
+struct oc_auth_form *parse_form_node(struct openconnect_info *vpninfo,
+                                    xmlNodePtr node, const char *submit_button,
+                                    int (*can_gen_tokencode)(struct openconnect_info *vpninfo, struct oc_auth_form *form, struct oc_form_opt *opt));
+
 /* auth-juniper.c */
 int oncp_obtain_cookie(struct openconnect_info *vpninfo);
 int oncp_send_tncc_command(struct openconnect_info *vpninfo, int first);