see http://news.gmane.org/gmane.network.vpn.openconnect.devel/2922 Signed-off-by: Dan Lenski <dlenski at gmail.com> --- auth-common.c | 33 +++++++++++++++++++++++++++------ auth.c | 19 ++++++++++++++++--- library.c | 5 +++-- main.c | 20 ++++++++++++++++++-- openconnect-internal.h | 2 ++ openconnect.h | 1 + 6 files changed, 67 insertions(+), 13 deletions(-) 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..617d8b2 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")) 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..8ad64cc 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), @@ -802,6 +805,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(" --token-mode=MODE %s\n", _("Software token type: rsa, totp or hotp")); + printf(" --token-field=[+]FIELD %s\n", _("Form field for token code (with +, code is appended to password)")); printf(" --token-secret=STRING %s\n", _("Software token secret")); #ifndef HAVE_LIBSTOKEN printf(" %s\n", _("(NOTE: libstoken (RSA SecurID) disabled in this build)")); @@ -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 -- 2.5.0