appending OATH code to password?

Daniel Lenski dlenski at gmail.com
Sat Dec 19 12:06:43 PST 2015


Hi David,

On Sat, Dec 19, 2015 at 1:16 AM, David Woodhouse <dwmw2 at infradead.org> wrote:
>
> On Fri, 2015-12-18 at 19:39 +0000, Daniel Lenski wrote:
> > Daniel Lenski  gmail.com> writes:
> >
> > >
> > > Hi all,
> > > I frequently connect to a VPN that uses TOTP-based 2FA. The TOTP code
> > > *must* be entered by appending it to the user-entered password.
>
>
> And I think I saw a Juniper authentication script which does this too.
> I'm happy enough to add and option to do this.

Please find the patch below, which applies cleanly to 8b041c7 and adds
one option:

      --token-field=[+]STRING     Form field for token code (with +,
code is appended to password)

> > > I think it'd be useful to offer an option to customize the form field that
> > > receives OATH or SecurID code, perhaps including the option to append the
> > > token to another field.
>
> This one is a bit more fun; this has always been a dodgy heuristic. Do
> you have examples of where else we'd need to trigger it?

Other than the two networks I mentioned (one using SecurID and one
using OATH TOTP), I don't know any other examples.

Thanks,
Dan

-----
Patch follows
-----

diff --git a/auth-common.c b/auth-common.c
index 848f882..e0d59bb 100644
--- a/auth-common.c
+++ b/auth-common.c
@@ -142,6 +142,8 @@ int do_gen_tokencode(struct openconnect_info *vpninfo,
      struct oc_auth_form *form)
 {
  struct oc_form_opt *opt;
+ char *save_password, *save_token;
+ int ret;

  for (opt = form->opts; ; opt = opt->next) {
  /* this form might not have anything for us to do */
@@ -149,25 +151,44 @@ int do_gen_tokencode(struct openconnect_info *vpninfo,
  return 0;
  if (opt->type == OC_FORM_OPT_TOKEN)
  break;
+ else if (opt->type == OC_FORM_OPT_PASSWORD_PLUS_TOKEN) {
+ save_password = opt->_value;
+ break;
+ }
  }

  switch (vpninfo->token_mode) {
 #ifdef HAVE_LIBSTOKEN
  case OC_TOKEN_MODE_STOKEN:
- return do_gen_stoken_code(vpninfo, form, opt);
+ ret = do_gen_stoken_code(vpninfo, form, opt);
+ break;
 #endif
  case OC_TOKEN_MODE_TOTP:
- return do_gen_totp_code(vpninfo, form, opt);
-
+ ret = do_gen_totp_code(vpninfo, form, opt);
+ break;
  case OC_TOKEN_MODE_HOTP:
- return do_gen_hotp_code(vpninfo, form, opt);
+ ret = do_gen_hotp_code(vpninfo, form, opt);
+ break;
 #ifdef HAVE_LIBPCSCLITE
  case OC_TOKEN_MODE_YUBIOATH:
- return do_gen_yubikey_code(vpninfo, form, opt);
+ ret = do_gen_yubikey_code(vpninfo, form, opt);
+ break;
 #endif
  default:
- return -EINVAL;
+ ret = -EINVAL;
  }
+
+ if (ret==0 && opt->type == OC_FORM_OPT_PASSWORD_PLUS_TOKEN) {
+ /* need to glom token onto the end of the password */
+ save_token = opt->_value;
+ if ( (opt->_value = malloc(strlen(save_password) +
strlen(save_token) + 1)) == NULL )
+ ret = ENOMEM;
+ else {
+ sprintf(opt->_value, "%s%s", save_password, save_token);
+ free(save_token);
+ }
+ }
+ return ret;
 }

 int can_gen_tokencode(struct openconnect_info *vpninfo,
diff --git a/auth.c b/auth.c
index 7aee802..f7e2726 100644
--- a/auth.c
+++ b/auth.c
@@ -220,10 +220,12 @@ static int parse_form(struct openconnect_info
*vpninfo, struct oc_auth_form *for
  } else if (!strcmp(input_type, "text")) {
  opt->type = OC_FORM_OPT_TEXT;
  } else if (!strcmp(input_type, "password")) {
- if (!cstp_can_gen_tokencode(vpninfo, form, opt))
- opt->type = OC_FORM_OPT_TOKEN;
- else
+ if (cstp_can_gen_tokencode(vpninfo, form, opt))
  opt->type = OC_FORM_OPT_PASSWORD;
+ else if (vpninfo->token_append)
+ opt->type = OC_FORM_OPT_PASSWORD_PLUS_TOKEN;
+ else
+ opt->type = OC_FORM_OPT_TOKEN;
  } else {
  vpn_progress(vpninfo, PRG_INFO,
      _("Unknown input type %s in form\n"),
@@ -880,13 +882,24 @@ static int cstp_can_gen_tokencode(struct
openconnect_info *vpninfo,

 #ifdef HAVE_LIBSTOKEN
  if (vpninfo->token_mode == OC_TOKEN_MODE_STOKEN) {
+ if (vpninfo->token_field) {
+ if (strcmp(opt->name, vpninfo->token_field))
+ return -EINVAL;
+ }
+ else
  if (strcmp(opt->name, "password") &&
-    strcmp(opt->name, "answer"))
+ strcmp(opt->name, "answer"))
  return -EINVAL;
  return can_gen_stoken_code(vpninfo, form, opt);
  }
 #endif
+
  /* Otherwise it's an OATH token of some kind. */
+ if (vpninfo->token_field) {
+ if (strcmp(opt->name, vpninfo->token_field))
+ return -EINVAL;
+ }
+ else
  if (strcmp(opt->name, "secondary_password"))
  return -EINVAL;

diff --git a/library.c b/library.c
index cc50eac..e5e5bf6 100644
--- a/library.c
+++ b/library.c
@@ -939,7 +939,8 @@ void nuke_opt_values(struct oc_form_opt *opt)
 {
  for (; opt; opt = opt->next) {
  if (opt->type == OC_FORM_OPT_TEXT ||
-    opt->type == OC_FORM_OPT_PASSWORD) {
+    opt->type == OC_FORM_OPT_PASSWORD ||
+ opt->type == OC_FORM_OPT_PASSWORD_PLUS_TOKEN) {
  free(opt->_value);
  opt->_value = NULL;
  }
@@ -976,7 +977,7 @@ retry:
  opt->flags &= ~OC_FORM_OPT_IGNORE;

  if (!auth_choice ||
-    (opt->type != OC_FORM_OPT_TEXT && opt->type != OC_FORM_OPT_PASSWORD))
+    (opt->type != OC_FORM_OPT_TEXT && opt->type !=
OC_FORM_OPT_PASSWORD && opt->type != OC_FORM_OPT_PASSWORD_PLUS_TOKEN))
  continue;

  if (auth_choice->noaaa ||
diff --git a/main.c b/main.c
index f853afe..c26cbd0 100644
--- a/main.c
+++ b/main.c
@@ -181,6 +181,8 @@ enum {
  OPT_DTLS_LOCAL_PORT,
  OPT_TOKEN_MODE,
  OPT_TOKEN_SECRET,
+ OPT_TOKEN_FIELD,
+ OPT_TOKEN_APPEND,
  OPT_OS,
  OPT_TIMESTAMP,
  OPT_PFS,
@@ -260,6 +262,7 @@ static const struct option long_options[] = {
  OPTION("dtls-local-port", 1, OPT_DTLS_LOCAL_PORT),
  OPTION("token-mode", 1, OPT_TOKEN_MODE),
  OPTION("token-secret", 1, OPT_TOKEN_SECRET),
+ OPTION("token-field", 1, OPT_TOKEN_FIELD),
  OPTION("os", 1, OPT_OS),
  OPTION("no-xmlpost", 0, OPT_NO_XMLPOST),
  OPTION("dump-http-traffic", 0, OPT_DUMP_HTTP),
@@ -803,6 +806,7 @@ static void usage(void)
  printf("      --passwd-on-stdin           %s\n", _("Read password
from standard input"));
  printf("      --token-mode=MODE           %s\n", _("Software token
type: rsa, totp or hotp"));
  printf("      --token-secret=STRING       %s\n", _("Software token secret"));
+ printf("      --token-field=[+]STRING     %s\n", _("Form field for
token code (with +, code is appended to password)"));
 #ifndef HAVE_LIBSTOKEN
  printf("                                  %s\n", _("(NOTE: libstoken
(RSA SecurID) disabled in this build)"));
 #endif
@@ -980,6 +984,8 @@ int main(int argc, char **argv)
  char *config_arg;
  char *config_filename;
  char *token_str = NULL;
+ char *token_field = NULL;
+ int token_append = 0;
  oc_token_mode_t token_mode = OC_TOKEN_MODE_NONE;
  int reconnect_timeout = 300;
  int ret;
@@ -1336,6 +1342,13 @@ int main(int argc, char **argv)
  case OPT_TOKEN_SECRET:
  token_str = keep_config_arg();
  break;
+ case OPT_TOKEN_FIELD:
+ token_field = keep_config_arg();
+ if (token_field[0]=='+') {
+ token_append = 1;
+ token_field++;
+ }
+ break;
  case OPT_OS:
  if (openconnect_set_reported_os(vpninfo, config_arg)) {
  fprintf(stderr, _("Invalid OS identity \"%s\"\n"),
@@ -1392,8 +1405,11 @@ int main(int argc, char **argv)
 #endif
  }

- if (token_mode != OC_TOKEN_MODE_NONE)
+ if (token_mode != OC_TOKEN_MODE_NONE) {
  init_token(vpninfo, token_mode, token_str);
+ vpninfo->token_field = token_field;
+ vpninfo->token_append = token_append;
+ }

  if (proxy && openconnect_set_http_proxy(vpninfo, strdup(proxy)))
  exit(1);
@@ -1916,7 +1932,7 @@ static int process_auth_form_cb(void *_vpninfo,
  goto err;
  empty = 0;

- } else if (opt->type == OC_FORM_OPT_PASSWORD) {
+ } else if (opt->type == OC_FORM_OPT_PASSWORD || opt->type ==
OC_FORM_OPT_PASSWORD_PLUS_TOKEN) {
  if (password &&
     !strcmp(opt->name, "password")) {
  opt->_value = password;
diff --git a/openconnect-internal.h b/openconnect-internal.h
index b7a2b0e..dacdb3f 100644
--- a/openconnect-internal.h
+++ b/openconnect-internal.h
@@ -409,6 +409,8 @@ struct openconnect_info {
  int token_bypassed;
  int token_tries;
  time_t token_time;
+ char *token_field;
+ int token_append;
 #ifdef HAVE_LIBSTOKEN
  struct stoken_ctx *stoken_ctx;
  char *stoken_pin;
diff --git a/openconnect.h b/openconnect.h
index 2bd8505..cad1518 100644
--- a/openconnect.h
+++ b/openconnect.h
@@ -153,6 +153,7 @@
 #define OC_FORM_OPT_SELECT 3
 #define OC_FORM_OPT_HIDDEN 4
 #define OC_FORM_OPT_TOKEN 5
+#define OC_FORM_OPT_PASSWORD_PLUS_TOKEN 6

 #define OC_FORM_RESULT_ERR -1
 #define OC_FORM_RESULT_OK 0



More information about the openconnect-devel mailing list