]> www.infradead.org Git - users/dwmw2/openconnect.git/commitdiff
Allow form responses to be provided on command line
authorDavid Woodhouse <dwmw2@infradead.org>
Sun, 4 Nov 2018 16:08:42 +0000 (17:08 +0100)
committerDavid Woodhouse <dwmw2@infradead.org>
Tue, 6 Nov 2018 09:37:32 +0000 (10:37 +0100)
Signed-off-by: David Woodhouse <dwmw2@infradead.org>
main.c
openconnect.8.in
www/changelog.xml

diff --git a/main.c b/main.c
index 2e9e30598074a5a7cb6846ffedd66b0a259a1a3e..fc2db3425a3191717df2abc8f5d6c45d28737357 100644 (file)
--- a/main.c
+++ b/main.c
@@ -97,6 +97,8 @@ static int last_form_empty;
 
 static int sig_cmd_fd;
 
+static void add_form_field(char *field);
+
 #ifdef __ANDROID__
 #include <android/log.h>
 static void __attribute__ ((format(printf, 3, 4)))
@@ -271,6 +273,7 @@ static const struct option long_options[] = {
        OPTION("dump-http-traffic", 0, OPT_DUMP_HTTP),
        OPTION("no-system-trust", 0, OPT_NO_SYSTEM_TRUST),
        OPTION("protocol", 1, OPT_PROTOCOL),
+       OPTION("form-entry", 1, 'F'),
 #ifdef OPENCONNECT_GNUTLS
        OPTION("gnutls-debug", 1, OPT_GNUTLS_DEBUG),
 #endif
@@ -799,6 +802,7 @@ static void usage(void)
        printf("      --non-inter                 %s\n", _("Do not expect user input; exit if it is required"));
        printf("      --passwd-on-stdin           %s\n", _("Read password from standard input"));
        printf("      --authgroup=GROUP           %s\n", _("Choose authentication login selection"));
+       printf("  -F, --form-field=FORM:OPT=VALUE %s\n", _("Provide authentication form responses"));
        printf("  -c, --certificate=CERT          %s\n", _("Use SSL client certificate CERT"));
        printf("  -k, --sslkey=KEY                %s\n", _("Use SSL private key file KEY"));
        printf("  -e, --cert-expire-warning=DAYS  %s\n", _("Warn when certificate lifetime < DAYS"));
@@ -970,9 +974,9 @@ static int next_option(int argc, char **argv, char **config_arg)
        if (!config_file) {
                opt = getopt_long(argc, argv,
 #ifdef _WIN32
-                                 "C:c:Dde:g:hi:k:m:P:p:Q:qs:u:Vvx:",
+                                 "C:c:Dde:F:g:hi:k:m:P:p:Q:qs:u:Vvx:",
 #else
-                                 "bC:c:Dde:g:hi:k:lm:P:p:Q:qSs:U:u:Vvx:",
+                                 "bC:c:Dde:F:g:hi:k:lm:P:p:Q:qSs:U:u:Vvx:",
 #endif
                                  long_options, NULL);
 
@@ -1183,6 +1187,9 @@ int main(int argc, char **argv)
                case 'U':
                        get_uids(config_arg, &vpninfo->uid, &vpninfo->gid);
                        break;
+               case 'F':
+                       add_form_field(keep_config_arg());
+                       break;
                case OPT_CSD_USER:
                        get_uids(config_arg, &vpninfo->uid_csd, &vpninfo->gid_csd);
                        vpninfo->uid_csd_given = 1;
@@ -1945,6 +1952,53 @@ retry:
 
        return 0;
 }
+struct form_field {
+       struct form_field *next;
+       char *form_id;
+       char *opt_id;
+       char *value;
+};
+static struct form_field *form_fields = NULL;
+
+static void add_form_field(char *arg)
+{
+       struct form_field *ff;
+       char *opt, *value = strchr(arg, '=');
+
+       if (!value || value == arg) {
+       bad_field:
+               fprintf(stderr, "Form field invalid. Use --form-field=FORM_ID:OPT_NAME=VALUE\n");
+               exit(1);
+       }
+       *(value++) = 0;
+       opt = strchr(arg, ':');
+       if (!opt || opt == arg)
+               goto bad_field;
+       *(opt++) = 0;
+
+       ff = malloc(sizeof(*ff));
+       if (!ff) {
+               fprintf(stderr, "Out of memory for form field\n");
+               exit(1);
+       }
+       ff->form_id = arg;
+       ff->opt_id = opt;
+       ff->value = value;
+       ff->next = form_fields;
+       form_fields = ff;
+}
+
+static char *saved_form_field(struct openconnect_info *vpninfo, const char *form_id, const char *opt_id)
+{
+       struct form_field *ff = form_fields;
+
+       while (ff) {
+               if (!strcmp(form_id, ff->form_id) && !strcmp(ff->opt_id, opt_id))
+                       return strdup(ff->value);
+               ff = ff->next;
+       }
+       return NULL;
+}
 
 /* Return value:
  *  < 0, on error
@@ -1970,6 +2024,8 @@ static int process_auth_form_cb(void *_vpninfo,
        /* Special handling for GROUP: field if present, as different group
           selections can make other fields disappear/reappear */
        if (form->authgroup_opt) {
+               if (!authgroup)
+                       authgroup = saved_form_field(vpninfo, form->auth_id, form->authgroup_opt->form.name);
                if (!authgroup ||
                    match_choice_label(vpninfo, form->authgroup_opt, authgroup) != 0) {
                        if (prompt_opt_select(vpninfo, form->authgroup_opt, &authgroup) < 0)
@@ -1990,9 +2046,18 @@ static int process_auth_form_cb(void *_vpninfo,
                   the Cisco clients do support them */
                if (opt->type == OC_FORM_OPT_SELECT) {
                        struct oc_form_opt_select *select_opt = (void *)opt;
+                       char *opt_response;
 
                        if (select_opt == form->authgroup_opt)
                                continue;
+
+                       opt_response = saved_form_field(vpninfo, form->auth_id, select_opt->form.name);
+                       if (opt_response &&
+                           match_choice_label(vpninfo, select_opt, opt_response) == 0) {
+                               free(opt_response);
+                               continue;
+                       }
+                       free(opt_response);
                        if (prompt_opt_select(vpninfo, select_opt, NULL) < 0)
                                goto err;
                        empty = 0;
@@ -2003,7 +2068,9 @@ static int process_auth_form_cb(void *_vpninfo,
                                opt->_value = username;
                                username = NULL;
                        } else {
-                               opt->_value = prompt_for_input(opt->label, vpninfo, 0);
+                               opt->_value = saved_form_field(vpninfo, form->auth_id, opt->name);
+                               if (!opt->_value)
+                                       opt->_value = prompt_for_input(opt->label, vpninfo, 0);
                        }
 
                        if (!opt->_value)
@@ -2015,7 +2082,9 @@ static int process_auth_form_cb(void *_vpninfo,
                                opt->_value = password;
                                password = NULL;
                        } else {
-                               opt->_value = prompt_for_input(opt->label, vpninfo, 1);
+                               opt->_value = saved_form_field(vpninfo, form->auth_id, opt->name);
+                               if (!opt->_value)
+                                       opt->_value = prompt_for_input(opt->label, vpninfo, 1);
                        }
 
                        if (!opt->_value)
index 37a33d0c6033c94a305a1b95abd98ade977f4205..86497423e08d360835032a32bcc91eac4512d3a8 100644 (file)
@@ -15,6 +15,7 @@ openconnect \- Multi-protocol VPN client, for Cisco AnyConnect VPNs and others
 .OP \-d,\-\-deflate
 .OP \-D,\-\-no\-deflate
 .OP \-\-force\-dpd interval
+.OP \-F,\-\-form\-entry form:opt=value
 .OP \-g,\-\-usergroup group
 .OP \-h,\-\-help
 .OP \-\-http\-auth methods
@@ -140,8 +141,8 @@ which may be either a file name or, if OpenConnect has been built with an approp
 version of GnuTLS, a PKCS#11 URL.
 .TP
 .B \-C,\-\-cookie=COOKIE
-Use authentication cookie COOKIE.
-.I COOKIE
+Use authentication cookie
+.IR COOKIE .
 .TP
 .B \-\-cookie\-on\-stdin
 Read cookie from standard input.
@@ -180,6 +181,19 @@ Use
 .I GROUP
 as login UserGroup
 .TP
+.B \-F,\-\-form\-entry=FORM:OPTION=VALUE
+Provide authentication form input, where
+.I FORM
+and
+.I OPTION
+are the identifiers from the form and the specific input field, and
+.I VALUE
+is the string to be filled in automatically. For example, the standard username field
+.I (also handled by the \-\-user option)
+could also be provided with this option thus:
+.I \-\-form\-entry
+.IR main:username=joebloggs .
+.TP
 .B \-h,\-\-help
 Display help text
 .TP
index c2f4509fb4a725f2f04a110e98eb84db83eb5186..a99b73cb9385e0b17f2a70fe41f60651a0f513f2 100644 (file)
@@ -15,6 +15,7 @@
 <ul>
    <li><b>OpenConnect HEAD</b>
      <ul>
+       <li>Allow form responses to be provided on command line.</li>
        <li>Add support for SSL keys stored in <a href="tpm.html">TPM2</a>.</li>
        <li>Fix ESP rekey when replay protection is disabled.</li>
        <li>Drop support for GnuTLS older than 3.2.10.</li>