Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
auth: Refactor stoken form handling
Split the devid/pass form from the PIN form, to allow the use of new "v3"
tokens from the command line.  On newer libstoken versions, query the
PIN status and token interval time; use this information to figure out
whether to request a PIN for concatenation (ala hard tokens) and whether
to use a 30- or 60-second delta in response to the next tokencode prompt.

Signed-off-by: Kevin Cernekee <cernekee@gmail.com>
  • Loading branch information
cernekee committed Aug 2, 2014
1 parent a10d4d9 commit 295a826
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 39 deletions.
170 changes: 131 additions & 39 deletions auth.c
Expand Up @@ -1026,23 +1026,26 @@ static int xmlpost_append_form_opts(struct openconnect_info *vpninfo,

#ifdef HAVE_LIBSTOKEN

#ifndef STOKEN_CHECK_VER
#define STOKEN_CHECK_VER(x,y) 0
#endif

/*
* If the user clicks OK without entering any data, we will continue
* connecting but bypass soft token generation for the duration of
* this "obtain_cookie" session.
*
* If the user clicks Cancel, we will abort the connection.
* A SecurID token can be encrypted with a device ID, a password, both,
* or neither. Gather the required information, decrypt the token, and
* check the hash to make sure it is sane.
*
* Return value:
* < 0, on error
* = 0, on success (or if the user bypassed soft token init)
* = 0, on success
* = 1, if the user cancelled the form submission
* = 2, if the user left the entire form blank and clicked OK
*/
int prepare_stoken(struct openconnect_info *vpninfo)
static int decrypt_stoken(struct openconnect_info *vpninfo)
{
struct oc_auth_form form;
struct oc_form_opt opts[3], *opt = opts;
char **devid = NULL, **pass = NULL, **pin = NULL;
struct oc_form_opt opts[2], *opt = opts;
char **devid = NULL, **pass = NULL;
int ret = 0;

memset(&form, 0, sizeof(form));
Expand All @@ -1051,9 +1054,6 @@ int prepare_stoken(struct openconnect_info *vpninfo)
form.opts = opts;
form.message = _("Enter credentials to unlock software token.");

vpninfo->token_tries = 0;
vpninfo->token_bypassed = 0;

if (stoken_devid_required(vpninfo->stoken_ctx)) {
opt->type = OC_FORM_OPT_TEXT;
opt->name = (char *)"devid";
Expand All @@ -1068,17 +1068,8 @@ int prepare_stoken(struct openconnect_info *vpninfo)
pass = &opt->value;
opt++;
}
if (stoken_pin_required(vpninfo->stoken_ctx)) {
opt->type = OC_FORM_OPT_PASSWORD;
opt->name = (char *)"password";
opt->label = _("PIN:");
opt->flags = OC_FORM_OPT_NUMERIC;
pin = &opt->value;
opt++;
}

opts[0].next = opts[1].type ? &opts[1] : NULL;
opts[1].next = opts[2].type ? &opts[2] : NULL;

while (1) {
nuke_opt_values(opts);
Expand All @@ -1103,8 +1094,7 @@ int prepare_stoken(struct openconnect_info *vpninfo)
if (all_empty) {
vpn_progress(vpninfo, PRG_INFO,
_("User bypassed soft token.\n"));
vpninfo->token_bypassed = 1;
ret = 0;
ret = 2;
break;
}
if (some_empty) {
Expand All @@ -1127,19 +1117,6 @@ int prepare_stoken(struct openconnect_info *vpninfo)
continue;
}

if (pin) {
if (stoken_check_pin(vpninfo->stoken_ctx, *pin) != 0) {
vpn_progress(vpninfo, PRG_INFO,
_("Invalid PIN format; try again.\n"));
continue;
}
free(vpninfo->stoken_pin);
vpninfo->stoken_pin = strdup(*pin);
if (!vpninfo->stoken_pin) {
ret = -ENOMEM;
break;
}
}
vpn_progress(vpninfo, PRG_DEBUG, _("Soft token init was successful.\n"));
ret = 0;
break;
Expand All @@ -1149,6 +1126,117 @@ int prepare_stoken(struct openconnect_info *vpninfo)
return ret;
}

static void get_stoken_details(struct openconnect_info *vpninfo)
{
#if STOKEN_CHECK_VER(1,3)
struct stoken_info *info = stoken_get_info(vpninfo->stoken_ctx);

if (info) {
vpninfo->stoken_concat_pin = !info->uses_pin;
vpninfo->stoken_interval = info->interval;
return;
}
#endif
vpninfo->stoken_concat_pin = 0;
vpninfo->stoken_interval = 60;
}

/*
* Return value:
* < 0, on error
* = 0, on success
* = 1, if the user cancelled the form submission
*/
static int request_stoken_pin(struct openconnect_info *vpninfo)
{
struct oc_auth_form form;
struct oc_form_opt opts[1], *opt = opts;
int ret = 0;

if (!vpninfo->stoken_concat_pin && !stoken_pin_required(vpninfo->stoken_ctx))
return 0;

memset(&form, 0, sizeof(form));
memset(&opts, 0, sizeof(opts));

form.opts = opts;
form.message = _("Enter software token PIN.");

opt->type = OC_FORM_OPT_PASSWORD;
opt->name = (char *)"password";
opt->label = _("PIN:");
opt->flags = OC_FORM_OPT_NUMERIC;

while (1) {
char *pin;

nuke_opt_values(opts);

/* < 0 for error; 1 if cancelled */
ret = process_auth_form(vpninfo, &form);
if (ret)
break;

pin = opt->value;
if (!pin || !strlen(pin)) {
/* in some cases there really is no PIN */
if (vpninfo->stoken_concat_pin)
return 0;

vpn_progress(vpninfo, PRG_INFO,
_("All fields are required; try again.\n"));
continue;
}

if (!vpninfo->stoken_concat_pin &&
stoken_check_pin(vpninfo->stoken_ctx, pin) != 0) {
vpn_progress(vpninfo, PRG_INFO,
_("Invalid PIN format; try again.\n"));
continue;
}

free(vpninfo->stoken_pin);
vpninfo->stoken_pin = strdup(pin);
if (!vpninfo->stoken_pin)
ret = -ENOMEM;
break;
}

nuke_opt_values(opts);
return ret;
}

/*
* If the user clicks OK on the devid/password prompt without entering
* any data, we will continue connecting but bypass soft token generation
* for the duration of this "obtain_cookie" session. (They might not even
* have the credentials that we're prompting for.)
*
* If the user clicks Cancel, we will abort the connection.
*
* Return value:
* < 0, on error
* = 0, on success (or if the user bypassed soft token init)
* = 1, if the user cancelled the form submission
*/
int prepare_stoken(struct openconnect_info *vpninfo)
{
int ret;

vpninfo->token_tries = 0;
vpninfo->token_bypassed = 0;

ret = decrypt_stoken(vpninfo);
if (ret == 2) {
vpninfo->token_bypassed = 1;
return 0;
} else if (ret != 0)
return ret;

get_stoken_details(vpninfo);
return request_stoken_pin(vpninfo);
}

/* Return value:
* < 0, if unable to generate a tokencode
* = 0, on success
Expand All @@ -1168,7 +1256,7 @@ static int can_gen_stoken_code(struct openconnect_info *vpninfo,
strcasestr(form->message, "next tokencode")) {
vpn_progress(vpninfo, PRG_DEBUG,
_("OK to generate NEXT tokencode\n"));
vpninfo->token_time += 60;
vpninfo->token_time += vpninfo->stoken_interval;
} else {
/* limit the number of retries, to avoid account lockouts */
vpn_progress(vpninfo, PRG_INFO,
Expand Down Expand Up @@ -1196,8 +1284,12 @@ static int do_gen_stoken_code(struct openconnect_info *vpninfo,
}

vpninfo->token_tries++;
opt->value = strdup(tokencode);
return opt->value ? 0 : -ENOMEM;

if (asprintf(&opt->value, "%s%s",
(vpninfo->stoken_concat_pin && vpninfo->stoken_pin) ? vpninfo->stoken_pin : "",
tokencode) < 0)
return -ENOMEM;
return 0;
}

#else
Expand Down
2 changes: 2 additions & 0 deletions openconnect-internal.h
Expand Up @@ -262,6 +262,8 @@ struct openconnect_info {
#ifdef HAVE_LIBSTOKEN
struct stoken_ctx *stoken_ctx;
char *stoken_pin;
int stoken_concat_pin;
int stoken_interval;
#endif
#ifdef HAVE_LIBOATH
char *oath_secret;
Expand Down

0 comments on commit 295a826

Please sign in to comment.