Commit 4bd42222 authored by John Morrissey's avatar John Morrissey Committed by David Woodhouse

Add TOTP (RFC6238) one-time password support

Signed-off-by: default avatarJohn Morrissey <jwm@horde.net>
Signed-off-by: default avatarDavid Woodhouse <David.Woodhouse@intel.com>
parent cbdb0503
......@@ -14,8 +14,8 @@ man8_MANS = openconnect.8
AM_CPPFLAGS = -DLOCALEDIR="\"$(localedir)\""
openconnect_SOURCES = xml.c main.c dtls.c cstp.c mainloop.c tun.c
openconnect_CFLAGS = $(SSL_CFLAGS) $(DTLS_SSL_CFLAGS) $(LIBXML2_CFLAGS) $(LIBPROXY_CFLAGS) $(ZLIB_CFLAGS) $(LIBSTOKEN_CFLAGS)
openconnect_LDADD = libopenconnect.la $(SSL_LIBS) $(DTLS_SSL_LIBS) $(LIBXML2_LIBS) $(LIBPROXY_LIBS) $(ZLIB_LIBS) $(LIBINTL) $(LIBSTOKEN_LIBS)
openconnect_CFLAGS = $(SSL_CFLAGS) $(DTLS_SSL_CFLAGS) $(LIBXML2_CFLAGS) $(LIBPROXY_CFLAGS) $(ZLIB_CFLAGS) $(LIBSTOKEN_CFLAGS) $(LIBOATH_CFLAGS)
openconnect_LDADD = libopenconnect.la $(SSL_LIBS) $(DTLS_SSL_LIBS) $(LIBXML2_LIBS) $(LIBPROXY_LIBS) $(ZLIB_LIBS) $(LIBINTL) $(LIBSTOKEN_LIBS) $(LIBOATH_LIBS)
library_srcs = ssl.c http.c auth.c library.c compat.c
lib_srcs_gnutls = gnutls.c gnutls_pkcs12.c gnutls_tpm.c
......@@ -30,8 +30,8 @@ if OPENCONNECT_OPENSSL
library_srcs += $(lib_srcs_openssl)
endif
libopenconnect_la_SOURCES = version.c $(library_srcs)
libopenconnect_la_CFLAGS = $(SSL_CFLAGS) $(LIBXML2_CFLAGS) $(LIBPROXY_CFLAGS) $(P11KIT_CFLAGS) $(TSS_CFLAGS) $(LIBSTOKEN_CFLAGS)
libopenconnect_la_LIBADD = $(SSL_LIBS) $(LIBXML2_LIBS) $(LIBPROXY_LIBS) $(LIBINTL) $(P11KIT_LIBS) $(TSS_LIBS) $(LIBSTOKEN_LIBS)
libopenconnect_la_CFLAGS = $(SSL_CFLAGS) $(LIBXML2_CFLAGS) $(LIBPROXY_CFLAGS) $(P11KIT_CFLAGS) $(TSS_CFLAGS) $(LIBSTOKEN_CFLAGS) $(LIBOATH_CFLAGS)
libopenconnect_la_LIBADD = $(SSL_LIBS) $(LIBXML2_LIBS) $(LIBPROXY_LIBS) $(LIBINTL) $(P11KIT_LIBS) $(TSS_LIBS) $(LIBSTOKEN_LIBS) $(LIBOATH_LIBS)
if OPENBSD_LIBTOOL
# OpenBSD's libtool doesn't have -version-number, but its -version-info arg
# does what GNU libtool's -version-number does. Which arguably is what the
......
......@@ -3,6 +3,7 @@
*
* Copyright © 2008-2011 Intel Corporation.
* Copyright © 2008 Nick Andrew <nick@nick-andrew.net>
* Copyright © 2013 John Morrissey <jwm@horde.net>
*
* Author: David Woodhouse <dwmw2@infradead.org>
*
......@@ -36,6 +37,10 @@
#include LIBSTOKEN_HDR
#endif
#ifdef LIBOATH_HDR
#include LIBOATH_HDR
#endif
#include <libxml/parser.h>
#include <libxml/tree.h>
......@@ -233,13 +238,15 @@ static int parse_form(struct openconnect_info *vpninfo, struct oc_auth_form *for
if (!strcmp(input_type, "hidden")) {
opt->type = OC_FORM_OPT_HIDDEN;
opt->value = (char *)xmlGetProp(xml_node, (unsigned char *)"value");
} else if (!strcmp(input_type, "text"))
} else if (!strcmp(input_type, "text")) {
opt->type = OC_FORM_OPT_TEXT;
else if (!strcmp(input_type, "password")) {
if (vpninfo->use_stoken && !can_gen_tokencode(vpninfo, form, opt))
opt->type = OC_FORM_OPT_STOKEN;
else
} else if (!strcmp(input_type, "password")) {
if (vpninfo->token_mode != OC_TOKEN_MODE_NONE &&
(can_gen_tokencode(vpninfo, form, opt) == 0)) {
opt->type = OC_FORM_OPT_TOKEN;
} else {
opt->type = OC_FORM_OPT_PASSWORD;
}
} else {
vpn_progress(vpninfo, PRG_INFO,
_("Unknown input type %s in form\n"),
......@@ -637,7 +644,7 @@ void free_auth_form(struct oc_auth_form *form)
if (form->opts->type == OC_FORM_OPT_TEXT ||
form->opts->type == OC_FORM_OPT_PASSWORD ||
form->opts->type == OC_FORM_OPT_HIDDEN ||
form->opts->type == OC_FORM_OPT_STOKEN)
form->opts->type == OC_FORM_OPT_TOKEN)
free(form->opts->value);
else if (form->opts->type == OC_FORM_OPT_SELECT) {
struct oc_form_opt_select *sel = (void *)form->opts;
......@@ -878,8 +885,8 @@ int prepare_stoken(struct openconnect_info *vpninfo)
form.opts = opts;
form.message = _("Enter credentials to unlock software token.");
vpninfo->stoken_tries = 0;
vpninfo->stoken_bypassed = 0;
vpninfo->token_tries = 0;
vpninfo->token_bypassed = 0;
if (stoken_devid_required(vpninfo->stoken_ctx)) {
opt->type = OC_FORM_OPT_TEXT;
......@@ -929,7 +936,7 @@ int prepare_stoken(struct openconnect_info *vpninfo)
if (all_empty) {
vpn_progress(vpninfo, PRG_INFO,
_("User bypassed soft token.\n"));
vpninfo->stoken_bypassed = 1;
vpninfo->token_bypassed = 1;
ret = 0;
break;
}
......@@ -987,22 +994,23 @@ int prepare_stoken(struct openconnect_info *vpninfo)
* < 0, if unable to generate a tokencode
* = 0, on success
*/
static int can_gen_tokencode(struct openconnect_info *vpninfo, struct oc_auth_form *form,
struct oc_form_opt *opt)
static int can_gen_stoken_code(struct openconnect_info *vpninfo,
struct oc_auth_form *form,
struct oc_form_opt *opt)
{
#ifdef LIBSTOKEN_HDR
if ((strcmp(opt->name, "password") && strcmp(opt->name, "answer")) ||
vpninfo->stoken_bypassed)
vpninfo->token_bypassed)
return -EINVAL;
if (vpninfo->stoken_tries == 0) {
if (vpninfo->token_tries == 0) {
vpn_progress(vpninfo, PRG_DEBUG,
_("OK to generate INITIAL tokencode\n"));
vpninfo->stoken_time = 0;
} else if (vpninfo->stoken_tries == 1 && form->message &&
vpninfo->token_time = 0;
} else if (vpninfo->token_tries == 1 && form->message &&
strcasestr(form->message, "next tokencode")) {
vpn_progress(vpninfo, PRG_DEBUG,
_("OK to generate NEXT tokencode\n"));
vpninfo->stoken_time += 60;
vpninfo->token_time += 60;
} else {
/* limit the number of retries, to avoid account lockouts */
vpn_progress(vpninfo, PRG_INFO,
......@@ -1019,35 +1027,139 @@ static int can_gen_tokencode(struct openconnect_info *vpninfo, struct oc_auth_fo
* < 0, if unable to generate a tokencode
* = 0, on success
*/
static int do_gen_tokencode(struct openconnect_info *vpninfo, struct oc_auth_form *form)
static int can_gen_totp_code(struct openconnect_info *vpninfo,
struct oc_auth_form *form,
struct oc_form_opt *opt)
{
#ifdef LIBSTOKEN_HDR
char tokencode[STOKEN_MAX_TOKENCODE + 1];
struct oc_form_opt *opt;
#if defined(LIBOATH_HDR)
if ((strcmp(opt->name, "secondary_password") != 0) ||
vpninfo->token_bypassed)
return -EINVAL;
if (vpninfo->token_tries == 0) {
vpn_progress(vpninfo, PRG_DEBUG,
_("OK to generate INITIAL tokencode\n"));
vpninfo->token_time = 0;
} else if (vpninfo->token_tries == 1) {
vpn_progress(vpninfo, PRG_DEBUG,
_("OK to generate NEXT tokencode\n"));
vpninfo->token_time += OATH_TOTP_DEFAULT_TIME_STEP_SIZE;
} else {
/* limit the number of retries, to avoid account lockouts */
vpn_progress(vpninfo, PRG_INFO,
_("Server is rejecting the soft token; switching to manual entry\n"));
return -ENOENT;
}
return 0;
#else
return -EOPNOTSUPP;
#endif
}
for (opt = form->opts; ; opt = opt->next) {
/* this form might not have anything for us to do */
if (!opt)
return 0;
if (opt->type == OC_FORM_OPT_STOKEN)
break;
/* Return value:
* < 0, if unable to generate a tokencode
* = 0, on success
*/
static int can_gen_tokencode(struct openconnect_info *vpninfo,
struct oc_auth_form *form,
struct oc_form_opt *opt)
{
switch (vpninfo->token_mode) {
case OC_TOKEN_MODE_STOKEN:
return can_gen_stoken_code(vpninfo, form, opt);
case OC_TOKEN_MODE_TOTP:
return can_gen_totp_code(vpninfo, form, opt);
default:
return -EINVAL;
}
}
static int do_gen_stoken_code(struct openconnect_info *vpninfo,
struct oc_auth_form *form,
struct oc_form_opt *opt)
{
#ifdef LIBSTOKEN_HDR
char tokencode[STOKEN_MAX_TOKENCODE + 1];
if (!vpninfo->stoken_time)
vpninfo->stoken_time = time(NULL);
vpn_progress(vpninfo, PRG_INFO, _("Generating tokencode\n"));
if (!vpninfo->token_time)
vpninfo->token_time = time(NULL);
vpn_progress(vpninfo, PRG_INFO, _("Generating RSA token code\n"));
/* This doesn't normally fail */
if (stoken_compute_tokencode(vpninfo->stoken_ctx, vpninfo->stoken_time,
if (stoken_compute_tokencode(vpninfo->stoken_ctx, vpninfo->token_time,
vpninfo->stoken_pin, tokencode) < 0) {
vpn_progress(vpninfo, PRG_ERR, _("General failure in libstoken.\n"));
return -EIO;
}
vpninfo->stoken_tries++;
vpninfo->token_tries++;
opt->value = strdup(tokencode);
return opt->value ? 0 : -ENOMEM;
#else
return 0;
#endif
}
static int do_gen_totp_code(struct openconnect_info *vpninfo,
struct oc_auth_form *form,
struct oc_form_opt *opt)
{
#if defined(LIBOATH_HDR)
int oath_err;
char tokencode[7];
if (!vpninfo->token_time)
vpninfo->token_time = time(NULL);
vpn_progress(vpninfo, PRG_INFO, _("Generating OATH TOTP token code\n"));
oath_err = oath_totp_generate(vpninfo->oath_secret,
vpninfo->oath_secret_len,
vpninfo->token_time,
OATH_TOTP_DEFAULT_TIME_STEP_SIZE,
OATH_TOTP_DEFAULT_START_TIME,
6, tokencode);
if (oath_err != OATH_OK) {
vpn_progress(vpninfo, PRG_ERR,
_("Unable to generate OATH TOTP token code: %s\n"),
oath_strerror(oath_err));
return -EIO;
}
vpninfo->token_tries++;
opt->value = strdup(tokencode);
return opt->value ? 0 : -ENOMEM;
#else
return 0;
#endif
}
/* Return value:
* < 0, if unable to generate a tokencode
* = 0, on success
*/
static int do_gen_tokencode(struct openconnect_info *vpninfo,
struct oc_auth_form *form)
{
struct oc_form_opt *opt;
for (opt = form->opts; ; opt = opt->next) {
/* this form might not have anything for us to do */
if (!opt)
return 0;
if (opt->type == OC_FORM_OPT_TOKEN)
break;
}
switch (vpninfo->token_mode) {
case OC_TOKEN_MODE_STOKEN:
return do_gen_stoken_code(vpninfo, form, opt);
case OC_TOKEN_MODE_TOTP:
return do_gen_totp_code(vpninfo, form, opt);
default:
return -EINVAL;
}
}
......@@ -504,6 +504,17 @@ AS_IF([test "x$with_stoken" != "xno"], [
libstoken_pkg=no)
], [libstoken_pkg=disabled])
AC_ARG_WITH([liboath],
AS_HELP_STRING([--without-liboath],
[Build without liboath library (default: test)]))
AS_IF([test "x$with_liboath" != "xno"], [
PKG_CHECK_MODULES(LIBOATH, liboath,
[AC_SUBST(LIBOATH_PC, liboath)
AC_DEFINE([LIBOATH_HDR], ["liboath/oath.h"])
liboath_pkg=yes],
liboath_pkg=no)
])
AC_CHECK_HEADER([if_tun.h],
[AC_DEFINE([IF_TUN_HDR], ["if_tun.h"])],
[AC_CHECK_HEADER([linux/if_tun.h],
......
......@@ -948,7 +948,7 @@ int openconnect_obtain_cookie(struct openconnect_info *vpninfo)
int xmlpost = 1;
/* Step 1: Unlock software token (if applicable) */
if (vpninfo->use_stoken) {
if (vpninfo->token_mode == OC_TOKEN_MODE_STOKEN) {
result = prepare_stoken(vpninfo);
if (result)
return result;
......
......@@ -37,6 +37,12 @@ OPENCONNECT_2.1 {
openconnect_set_reported_os;
} OPENCONNECT_2.0;
OPENCONNECT_2.2 {
global:
openconnect_has_oath_support;
openconnect_set_token_mode;
} OPENCONNECT_2.1;
OPENCONNECT_PRIVATE {
global: @SYMVER_TIME@ @SYMVER_ASPRINTF@ @SYMVER_GETLINE@ @SYMVER_PRINT_ERR@
openconnect_SSL_gets;
......
......@@ -2,6 +2,7 @@
* OpenConnect (SSL + DTLS) VPN client
*
* Copyright © 2008-2012 Intel Corporation.
* Copyright © 2013 John Morrissey <jwm@horde.net>
*
* Authors: David Woodhouse <dwmw2@infradead.org>
*
......@@ -30,6 +31,10 @@
#include LIBSTOKEN_HDR
#endif
#ifdef LIBOATH_HDR
#include LIBOATH_HDR
#endif
#include <libxml/tree.h>
#include "openconnect-internal.h"
......@@ -144,6 +149,10 @@ void openconnect_vpninfo_free(struct openconnect_info *vpninfo)
free(vpninfo->stoken_pin);
if (vpninfo->stoken_ctx)
stoken_destroy(vpninfo->stoken_ctx);
#endif
#ifdef LIBOATH_HDR
if (vpninfo->oath_secret)
oath_done();
#endif
/* No need to free deflate streams; they weren't initialised */
free(vpninfo);
......@@ -321,29 +330,21 @@ int openconnect_has_stoken_support(void)
#endif
}
/*
* Enable software token generation if use_stoken == 1.
*
* If token_str is not NULL, try to parse the string. Otherwise, try to read
* the token data from ~/.stokenrc
*
* Return value:
* = -EOPNOTSUPP, if libstoken is not available
* = -EINVAL, if the token string is invalid (token_str was provided)
* = -ENOENT, if ~/.stokenrc is missing (token_str was NULL)
* = -EIO, for other libstoken failures
* = 0, on success
*/
int openconnect_set_stoken_mode(struct openconnect_info *vpninfo,
int use_stoken, const char *token_str)
int openconnect_has_oath_support(void)
{
#ifdef LIBOATH_HDR
return 1;
#else
return 0;
#endif
}
static int set_libstoken_mode(struct openconnect_info *vpninfo,
const char *token_str)
{
#ifdef LIBSTOKEN_HDR
int ret;
vpninfo->use_stoken = 0;
if (!use_stoken)
return 0;
if (!vpninfo->stoken_ctx) {
vpninfo->stoken_ctx = stoken_new();
if (!vpninfo->stoken_ctx)
......@@ -356,9 +357,100 @@ int openconnect_set_stoken_mode(struct openconnect_info *vpninfo,
if (ret)
return ret;
vpninfo->use_stoken = 1;
vpninfo->token_mode = OC_TOKEN_MODE_STOKEN;
return 0;
#else
return -EOPNOTSUPP;
#endif
}
static int set_oath_mode(struct openconnect_info *vpninfo,
const char *token_str)
{
#ifdef LIBOATH_HDR
int ret;
ret = oath_init();
if (ret != OATH_OK)
return -EIO;
if (strncasecmp(token_str, "base32:", strlen("base32:")) == 0) {
ret = oath_base32_decode(token_str + strlen("base32:"),
strlen(token_str) - strlen("base32:"),
&vpninfo->oath_secret,
&vpninfo->oath_secret_len);
if (ret != OATH_OK)
return -EINVAL;
} else {
vpninfo->oath_secret = strdup(token_str);
vpninfo->oath_secret_len = strlen(token_str);
}
vpninfo->token_mode = OC_TOKEN_MODE_TOTP;
return 0;
#else
return -EOPNOTSUPP;
#endif
}
/*
* Enable software token generation.
*
* If token_mode is OC_TOKEN_MODE_STOKEN and token_str is NULL,
* read the token data from ~/.stokenrc.
*
* Return value:
* = -EOPNOTSUPP, if the underlying library (libstoken, liboath) is not
* available or an invalid token_mode was provided
* = -EINVAL, if the token string is invalid (token_str was provided)
* = -ENOENT, if token_mode is OC_TOKEN_MODE_STOKEN and ~/.stokenrc is
* missing (token_str was NULL)
* = -EIO, for other failures in the underlying library (libstoken, liboath)
* = 0, on success
*/
int openconnect_set_token_mode(struct openconnect_info *vpninfo,
oc_token_mode_t token_mode,
const char *token_str)
{
vpninfo->token_mode = OC_TOKEN_MODE_NONE;
switch (token_mode) {
case OC_TOKEN_MODE_NONE:
return 0;
case OC_TOKEN_MODE_STOKEN:
return set_libstoken_mode(vpninfo, token_str);
case OC_TOKEN_MODE_TOTP:
return set_oath_mode(vpninfo, token_str);
default:
return -EOPNOTSUPP;
}
}
/*
* Enable libstoken token generation if use_stoken == 1.
*
* If token_str is not NULL, try to parse the string. Otherwise, try to read
* the token data from ~/.stokenrc
*
* DEPRECATED: use openconnect_set_stoken_mode() instead.
*
* Return value:
* = -EOPNOTSUPP, if libstoken is not available
* = -EINVAL, if the token string is invalid (token_str was provided)
* = -ENOENT, if ~/.stokenrc is missing (token_str was NULL)
* = -EIO, for other libstoken failures
* = 0, on success
*/
int openconnect_set_stoken_mode(struct openconnect_info *vpninfo,
int use_stoken, const char *token_str)
{
oc_token_mode_t token_mode = OC_TOKEN_MODE_NONE;
if (use_stoken)
token_mode = OC_TOKEN_MODE_STOKEN;
return openconnect_set_token_mode(vpninfo, token_mode, token_str);
}
......@@ -3,6 +3,7 @@
*
* Copyright © 2008-2012 Intel Corporation.
* Copyright © 2008 Nick Andrew <nick@nick-andrew.net>
* Copyright © 2013 John Morrissey <jwm@horde.net>
*
* Author: David Woodhouse <dwmw2@infradead.org>
*
......@@ -66,8 +67,8 @@ static int validate_peer_cert(void *_vpninfo,
const char *reason);
static int process_auth_form(void *_vpninfo,
struct oc_auth_form *form);
static void init_stoken(struct openconnect_info *vpninfo,
const char *token_str);
static void init_token(struct openconnect_info *vpninfo,
oc_token_mode_t token_mode, const char *token_str);
/* A sanity check that the openconnect executable is running against a
library of the same version */
......@@ -110,7 +111,8 @@ enum {
OPT_USERAGENT,
OPT_NON_INTER,
OPT_DTLS_LOCAL_PORT,
OPT_STOKEN,
OPT_TOKEN_MODE,
OPT_TOKEN_SECRET,
OPT_OS,
};
......@@ -175,7 +177,10 @@ static struct option long_options[] = {
OPTION("force-dpd", 1, OPT_FORCE_DPD),
OPTION("non-inter", 0, OPT_NON_INTER),
OPTION("dtls-local-port", 1, OPT_DTLS_LOCAL_PORT),
OPTION("stoken", 2, OPT_STOKEN),
OPTION("token-mode", 1, OPT_TOKEN_MODE),
/* Alias --stoken to --token-secret for backwards compatibility. */
OPTION("stoken", 2, OPT_TOKEN_SECRET),
OPTION("token-secret", 2, OPT_TOKEN_SECRET),
OPTION("os", 1, OPT_OS),
OPTION(NULL, 0, 0)
};
......@@ -211,7 +216,11 @@ static void print_build_opts(void)
sep = comma;
}
if (openconnect_has_stoken_support()) {
printf("%sSoftware token", sep);
printf("%sRSA software token", sep);
sep = comma;
}
if (openconnect_has_oath_support()) {
printf("%sTOTP software token", sep);
sep = comma;
}
......@@ -281,9 +290,14 @@ static void usage(void)
printf(" --no-cert-check %s\n", _("Do not require server SSL cert to be valid"));
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(" --stoken[=TOKENSTRING] %s\n", _("Use software token to generate password"));
printf(" --token-mode=MODE %s\n", _("Software token type: stoken (default) or totp"));
printf(" --token-secret[=STRING] %s\n", _("Software token secret (can be empty for stoken mode"));
printf(" %s\n", _(" to read from ~/.stokenrc)"));
#ifndef LIBSTOKEN_HDR
printf(" %s\n", _("(NOTE: libstoken disabled in this build)"));
printf(" %s\n", _("(NOTE: libstoken (RSA SecurID) disabled in this build)"));
#endif
#ifndef LIBOATH_HDR
printf(" %s\n", _("(NOTE: liboath (TOTP) disabled in this build)"));
#endif
printf(" --reconnect-timeout %s\n", _("Connection retry timeout in seconds"));
printf(" --servercert=FINGERPRINT %s\n", _("Server's certificate SHA1 fingerprint"));
......@@ -448,8 +462,8 @@ int main(int argc, char **argv)
char *pidfile = NULL;
FILE *fp = NULL;
char *config_arg;
int use_stoken = 0;
char *token_str = NULL;
oc_token_mode_t token_mode = OC_TOKEN_MODE_NONE;
#ifdef ENABLE_NLS
bindtextdomain("openconnect", LOCALEDIR);
......@@ -711,8 +725,18 @@ int main(int argc, char **argv)
case OPT_DTLS_LOCAL_PORT:
vpninfo->dtls_local_port = atoi(config_arg);
break;
case OPT_STOKEN:
use_stoken = 1;
case OPT_TOKEN_MODE:
if (strcasecmp(config_arg, "stoken") == 0) {
token_mode = OC_TOKEN_MODE_STOKEN;
} else if (strcasecmp(config_arg, "totp") == 0) {
token_mode = OC_TOKEN_MODE_TOTP;
} else {
fprintf(stderr, _("Invalid software token mode \"%s\"\n"),
config_arg);
exit(1);
}
break;
case OPT_TOKEN_SECRET:
token_str = keep_config_arg();
break;
case OPT_OS:
......@@ -749,8 +773,8 @@ int main(int argc, char **argv)
#endif
}
if (use_stoken)
init_stoken(vpninfo, token_str);
if (token_mode != OC_TOKEN_MODE_NONE)
init_token(vpninfo, token_mode, token_str);
if (proxy && openconnect_set_http_proxy(vpninfo, strdup(proxy)))
exit(1);
......@@ -1224,25 +1248,55 @@ static int process_auth_form(void *_vpninfo,
return -EINVAL;
}
static void init_stoken(struct openconnect_info *vpninfo,
const char *token_str)
static void init_token(struct openconnect_info *vpninfo,
oc_token_mode_t token_mode, const char *token_str)
{
int ret = openconnect_set_stoken_mode(vpninfo, 1, token_str);
int ret;
switch (ret) {
case 0:
return;
case -EINVAL:
fprintf(stderr, _("Soft token string is invalid\n"));
exit(1);
case -ENOENT:
fprintf(stderr, _("Can't open ~/.stokenrc file\n"));
exit(1);
case -EOPNOTSUPP:
fprintf(stderr, _("OpenConnect was not built with soft token support\n"));
exit(1);
default:
fprintf(stderr, _("General failure in libstoken\n"));
exit(1);
ret = openconnect_set_token_mode(vpninfo, token_mode, token_str);
switch (token_mode) {
case OC_TOKEN_MODE_STOKEN:
switch (ret) {
case 0:
return;
case -EINVAL:
fprintf(stderr, _("Soft token string is invalid\n"));
exit(1);
case -ENOENT:
fprintf(stderr, _("Can't open ~/.stokenrc file\n"));
exit(1);
case -EOPNOTSUPP:
fprintf(stderr, _("OpenConnect was not built with libstoken support\n"));
exit(1);
default:
fprintf(stderr, _("General failure in libstoken\n"));
exit(1);
}
break;
case OC_TOKEN_MODE_TOTP:
switch (ret) {
case 0:
return;
case -EINVAL:
fprintf(stderr, _("Soft token string is invalid\n"));
exit(1);
case -EOPNOTSUPP:
fprintf(stderr, _("OpenConnect was not built with liboath support\n"));
exit(1);
default:
fprintf(stderr, _("General failure in liboath\n"));
exit(1);
}
break;
case OC_TOKEN_MODE_NONE:
/* No-op */
break;
/* Option parsing already checked for invalid modes. */
}
}
......@@ -3,6 +3,7 @@
*
* Copyright © 2008-2012 Intel Corporation.
* Copyright © 2008 Nick Andrew <nick@nick-andrew.net>
* Copyright © 2013 John Morrissey <jwm@horde.net>
*
* Author: David Woodhouse <dwmw2@infradead.org>
*
......@@ -180,14 +181,18 @@ struct openconnect_info {
int uid_csd_given;
int no_http_keepalive;
int token_mode;
int token_bypassed;
int token_tries;
time_t token_time;
#ifdef LIBSTOKEN_HDR
struct stoken_ctx *stoken_ctx;
#endif
int use_stoken;
int stoken_bypassed;
int stoken_tries;
time_t stoken_time;
char *stoken_pin;
#endif
#ifdef LIBOATH_HDR
char *oath_secret;
size_t oath_secret_len;
#endif
OPENCONNECT_X509 *peer_cert;
......
......@@ -49,7 +49,8 @@ openconnect \- Connect to Cisco AnyConnect VPN
.OP \-\-no\-passwd
.OP \-\-non\-inter
.OP \-\-passwd\-on\-stdin
.OP \-\-stoken[=\fItoken-string\fP]
.OP \-\-token-mode=\fIstoken|totp\fP
.OP \-\-token-secret=\fIsecret\fP
.OP \-\-reconnect\-timeout
.OP \-\-servercert sha1
.OP \-\-useragent string
......@@ -324,11 +325,17 @@ Do not expect user input; exit if it is required.
.B \-\-passwd\-on\-stdin
Read password from standard input
.TP
.B \-\-stoken[=\fItoken-string\fP]
Use libstoken to generate one-time passwords compatible with the RSA SecurID
system (when built with libstoken support). If \fItoken-string\fP is omitted,
libstoken will try to use the software token seed stored in \fI~/.stokenrc\fP,
if this file exists.
.B \-\-token\-mode=\fIstoken|totp\fP
Select the algorithm to use to generate one-time passwords/verification
codes. \fIstoken\fP for RSA SecurID requires libstoken, and \fItotp\fP
for RFC 6238 requires liboath.
.TP
.B \-\-token\-secret[=\fIsecret\fP]
The secret to use when generating one-time passwords/verification codes.
If \fIsecret\fP is omitted and \-\-token-mode is \fIstoken\fP, libstoken
will try to use the software token seed stored in \fI~/.stokenrc\fP, if this
file exists. Base 32-encoded TOTP secrets can be specified by specifying
"base32:" at the beginning of the secret.
.TP
.B \-\-reconnect\-timeout
Keep reconnect attempts until so much seconds are elapsed. The default
......
......@@ -3,6 +3,7 @@
*
* Copyright © 2008-2012 Intel Corporation.
* Copyright © 2008 Nick Andrew <nick@nick-andrew.net>
* Copyright © 2013 John Morrissey <jwm@horde.net>
*
* Author: David Woodhouse <dwmw2@infradead.org>
*
......@@ -31,7 +32,7 @@
#include <unistd.h>
#define OPENCONNECT_API_VERSION_MAJOR 2
#define OPENCONNECT_API_VERSION_MINOR 1
#define OPENCONNECT_API_VERSION_MINOR 2
/*
* API version 2.1:
......@@ -86,7 +87,7 @@
#define OC_FORM_OPT_PASSWORD 2
#define OC_FORM_OPT_SELECT 3
#define OC_FORM_OPT_HIDDEN 4
#define OC_FORM_OPT_STOKEN 5
#define OC_FORM_OPT_TOKEN 5
/* char * fields are static (owned by XML parser) and don't need to be
freed by the form handling code -- except for value, which for TEXT
......@@ -137,6 +138,12 @@ struct openconnect_info;
#define OPENCONNECT_X509 void