summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKevin Cernekee <cernekee@gmail.com>2012-10-13 13:06:18 -0700
committerKevin Cernekee <cernekee@gmail.com>2012-10-14 20:13:13 -0700
commit2628bbd4720f59e839b259e8a81749739012730a (patch)
tree9a8870fd7e3b5f05f409d82b8b27e93b5a6aa019
parentf0c63660026e75bb84b4ef069dc8efdfa01d7253 (diff)
downloadopenconnect-2628bbd4720f59e839b259e8a81749739012730a.tar.gz
openconnect-2628bbd4720f59e839b259e8a81749739012730a.tar.bz2
openconnect-2628bbd4720f59e839b259e8a81749739012730a.zip
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 <cernekee@gmail.com>
-rw-r--r--auth.c84
-rw-r--r--openconnect.h1
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