From 2628bbd4720f59e839b259e8a81749739012730a Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Sat, 13 Oct 2012 13:06:18 -0700 Subject: [PATCH 1/1] stoken: Fill in "password" fields with a generated tokencode If the gateway prompts for a password and soft token information is available, generate a tokencode and mark the form field as OPT_STOKEN so the user is not prompted for a password. Signed-off-by: Kevin Cernekee --- auth.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- openconnect.h | 1 + 2 files changed, 82 insertions(+), 3 deletions(-) diff --git a/auth.c b/auth.c index f4201e1..8574744 100644 --- a/auth.c +++ b/auth.c @@ -41,6 +41,9 @@ #include "openconnect-internal.h" +static int can_gen_tokencode(struct openconnect_info *vpninfo, struct oc_form_opt *opt); +static int do_gen_tokencode(struct openconnect_info *vpninfo, struct oc_auth_form *form); + static int append_opt(char *body, int bodylen, char *opt, char *name) { int len = strlen(body); @@ -227,9 +230,12 @@ static int parse_form(struct openconnect_info *vpninfo, struct oc_auth_form *for opt->value = (char *)xmlGetProp(xml_node, (unsigned char *)"value"); } else if (!strcmp(input_type, "text")) opt->type = OC_FORM_OPT_TEXT; - else if (!strcmp(input_type, "password")) - opt->type = OC_FORM_OPT_PASSWORD; - else { + else if (!strcmp(input_type, "password")) { + if (vpninfo->use_stoken && !can_gen_tokencode(vpninfo, opt)) + opt->type = OC_FORM_OPT_STOKEN; + else + opt->type = OC_FORM_OPT_PASSWORD; + } else { vpn_progress(vpninfo, PRG_INFO, _("Unknown input type %s in form\n"), input_type); @@ -444,6 +450,11 @@ int parse_xml_response(struct openconnect_info *vpninfo, char *response, if (ret) goto out; + /* tokencode generation is deferred until after username prompts and CSD */ + ret = do_gen_tokencode(vpninfo, form); + if (ret) + goto out; + ret = append_form_opts(vpninfo, form, request_body, req_len); if (!ret) { *method = "POST"; @@ -622,3 +633,70 @@ int prepare_stoken(struct openconnect_info *vpninfo) return -EOPNOTSUPP; #endif } + +/* Return value: + * < 0, if unable to generate a tokencode + * = 0, on success + */ +static int can_gen_tokencode(struct openconnect_info *vpninfo, struct oc_form_opt *opt) +{ +#ifdef LIBSTOKEN_HDR + if (strcmp(opt->name, "password") || vpninfo->stoken_bypassed) + return -EINVAL; + if (vpninfo->stoken_tries == 0) { + vpn_progress(vpninfo, PRG_DEBUG, + _("OK to generate INITIAL tokencode\n")); + vpninfo->stoken_time = 0; + } else if (vpninfo->stoken_tries == 1 && strcasestr(opt->label, "next")) { + vpn_progress(vpninfo, PRG_DEBUG, + _("OK to generate NEXT tokencode\n")); + vpninfo->stoken_time += 60; + } else { + /* limit the number of retries, to avoid account lockouts */ + vpn_progress(vpninfo, PRG_INFO, + _("Server is rejecting the soft token; switching to manual entry\n")); + return -ENOENT; + } + + vpninfo->stoken_tries++; + return 0; +#else + return -EOPNOTSUPP; +#endif +} + +/* Return value: + * < 0, if unable to generate a tokencode + * = 0, on success + */ +static int do_gen_tokencode(struct openconnect_info *vpninfo, struct oc_auth_form *form) +{ +#ifdef LIBSTOKEN_HDR + char tokencode[STOKEN_MAX_TOKENCODE + 1]; + struct oc_form_opt *opt; + + for (opt = form->opts; ; opt = opt->next) { + /* this form might not have anything for us to do */ + if (!opt) + return 0; + if (opt->type == OC_FORM_OPT_STOKEN) + break; + } + + if (!vpninfo->stoken_time) + vpninfo->stoken_time = time(NULL); + vpn_progress(vpninfo, PRG_INFO, _("Generating tokencode\n")); + + /* This doesn't normally fail */ + if (stoken_compute_tokencode(vpninfo->stoken_ctx, vpninfo->stoken_time, + vpninfo->stoken_pin, tokencode) < 0) { + vpn_progress(vpninfo, PRG_ERR, _("General failure in libstoken.\n")); + return -EIO; + } + + opt->value = strdup(tokencode); + return opt->value ? 0 : -ENOMEM; +#else + return 0; +#endif +} diff --git a/openconnect.h b/openconnect.h index dd89bd9..e034d33 100644 --- a/openconnect.h +++ b/openconnect.h @@ -85,6 +85,7 @@ #define OC_FORM_OPT_PASSWORD 2 #define OC_FORM_OPT_SELECT 3 #define OC_FORM_OPT_HIDDEN 4 +#define OC_FORM_OPT_STOKEN 5 /* char * fields are static (owned by XML parser) and don't need to be freed by the form handling code -- except for value, which for TEXT -- 2.7.4