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