Skip to content

Commit

Permalink
stoken: Implement new auth form to gather soft token information
Browse files Browse the repository at this point in the history
If the user has asked to use a soft token, libopenconnect will prompt
for devid/pass/pin (as necessary) to unlock the soft token, prior to
the initial server connection.  If the user aborts, soft token mode will
be disabled and the user will need to enter his tokencode by hand.
Manual entry could be useful for e.g. activating a new token.

Signed-off-by: Kevin Cernekee <cernekee@gmail.com>
  • Loading branch information
cernekee committed Oct 15, 2012
1 parent f5e5623 commit f0c6366
Show file tree
Hide file tree
Showing 3 changed files with 152 additions and 4 deletions.
145 changes: 143 additions & 2 deletions auth.c
Expand Up @@ -32,6 +32,10 @@
#include <ctype.h>
#include <errno.h>

#ifdef LIBSTOKEN_HDR
#include LIBSTOKEN_HDR
#endif

#include <libxml/parser.h>
#include <libxml/tree.h>

Expand Down Expand Up @@ -215,6 +219,9 @@ static int parse_form(struct openconnect_info *vpninfo, struct oc_auth_form *for
return -ENOMEM;
}

opt->name = input_name;
opt->label = input_label;

if (!strcmp(input_type, "hidden")) {
opt->type = OC_FORM_OPT_HIDDEN;
opt->value = (char *)xmlGetProp(xml_node, (unsigned char *)"value");
Expand All @@ -234,8 +241,6 @@ static int parse_form(struct openconnect_info *vpninfo, struct oc_auth_form *for
}

free(input_type);
opt->name = input_name;
opt->label = input_label;

p = &form->opts;
while (*p)
Expand Down Expand Up @@ -479,5 +484,141 @@ int parse_xml_response(struct openconnect_info *vpninfo, char *response,
return ret;
}

static void nuke_opt_values(struct oc_form_opt *opt)
{
for (; opt; opt = opt->next) {
free(opt->value);
opt->value = NULL;
}
}

/*
* 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.
*
* 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)
{
#ifdef LIBSTOKEN_HDR
struct oc_auth_form form;
struct oc_form_opt opts[3], *opt = opts;
char **devid = NULL, **pass = NULL, **pin = NULL;
int ret = 0;

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

form.opts = opts;
form.message = _("Enter credentials to unlock software token.");

vpninfo->stoken_tries = 0;
vpninfo->stoken_bypassed = 0;

if (stoken_devid_required(vpninfo->stoken_ctx)) {
opt->type = OC_FORM_OPT_TEXT;
opt->name = (char *)"devid";
opt->label = _("Device ID:");
devid = &opt->value;
opt++;
}
if (stoken_pass_required(vpninfo->stoken_ctx)) {
opt->type = OC_FORM_OPT_PASSWORD;
opt->name = (char *)"password";
opt->label = _("Password:");
pass = &opt->value;
opt++;
}
if (stoken_pin_required(vpninfo->stoken_ctx)) {
opt->type = OC_FORM_OPT_PASSWORD;
opt->name = (char *)"password";
opt->label = _("PIN:");
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);

if (!opts[0].type) {
/* don't bug the user if there's nothing to enter */
ret = 0;
} else if (vpninfo->process_auth_form) {
int some_empty = 0, all_empty = 1;

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

for (opt = opts; opt; opt = opt->next) {
if (!opt->value || !strlen(opt->value))
some_empty = 1;
else
all_empty = 0;
}
if (all_empty) {
vpn_progress(vpninfo, PRG_INFO,
_("User bypassed soft token.\n"));
vpninfo->stoken_bypassed = 1;
ret = 0;
break;
}
if (some_empty) {
vpn_progress(vpninfo, PRG_INFO,
_("All fields are required; try again.\n"));
continue;
}
} else {
vpn_progress(vpninfo, PRG_ERR,
_("No form handler; cannot authenticate.\n"));
ret = -EIO;
break;
}

ret = stoken_decrypt_seed(vpninfo->stoken_ctx,
pass ? *pass : NULL,
devid ? *devid : NULL);
if (ret == -EIO || (ret && !devid && !pass)) {
vpn_progress(vpninfo, PRG_ERR,
_("General failure in libstoken.\n"));
break;
} else if (ret != 0) {
vpn_progress(vpninfo, PRG_INFO,
_("Incorrect device ID or password; try again.\n"));
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;
}

nuke_opt_values(opts);
return ret;
#else
return -EOPNOTSUPP;
#endif
}
10 changes: 8 additions & 2 deletions http.c
Expand Up @@ -593,8 +593,8 @@ int internal_parse_url(char *url, char **res_proto, char **res_host,

/* Return value:
* < 0, on error
* = 0, no cookie (user cancel)
* = 1, obtained cookie
* > 0, no cookie (user cancel)
* = 0, obtained cookie
*/
int openconnect_obtain_cookie(struct openconnect_info *vpninfo)
{
Expand All @@ -606,6 +606,12 @@ int openconnect_obtain_cookie(struct openconnect_info *vpninfo)
const char *request_body_type = NULL;
const char *method = "GET";

if (vpninfo->use_stoken) {
result = prepare_stoken(vpninfo);
if (result)
return result;
}

retry:
if (form_buf) {
free(form_buf);
Expand Down
1 change: 1 addition & 0 deletions openconnect-internal.h
Expand Up @@ -405,6 +405,7 @@ int config_lookup_host(struct openconnect_info *vpninfo, const char *host);
int parse_xml_response(struct openconnect_info *vpninfo, char *response,
char *request_body, int req_len, const char **method,
const char **request_body_type);
int prepare_stoken(struct openconnect_info *vpninfo);

/* http.c */
char *openconnect_create_useragent(const char *base);
Expand Down

0 comments on commit f0c6366

Please sign in to comment.