[PATCH 09/10] Call libstoken to generate one-time passwords
Kevin Cernekee
cernekee at gmail.com
Sun Oct 7 21:03:43 EDT 2012
Signed-off-by: Kevin Cernekee <cernekee at gmail.com>
---
main.c | 159 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 159 insertions(+), 0 deletions(-)
diff --git a/main.c b/main.c
index 0506481..da8ba04 100644
--- a/main.c
+++ b/main.c
@@ -51,6 +51,9 @@
#ifdef LIBPROXY_HDR
#include LIBPROXY_HDR
#endif
+#ifdef LIBSTOKEN_HDR
+#include LIBSTOKEN_HDR
+#endif
#include <getopt.h>
#include "openconnect-internal.h"
@@ -66,6 +69,9 @@ 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(char *token_str);
+static int prompt_user(const char *prompt, char *buf, int max_len,
+ int hide_chars);
/* A sanity check that the openconnect executable is running against a
library of the same version */
@@ -82,6 +88,13 @@ int do_passphrase_from_fsid;
int nocertcheck;
int non_inter;
int cookieonly;
+int use_stoken;
+
+#ifdef LIBSTOKEN_HDR
+struct stoken_ctx *stoken_ctx;
+char stoken_pin[USER_BUFLEN];
+int stoken_tries;
+#endif
enum {
OPT_AUTHENTICATE = 0x100,
@@ -111,6 +124,7 @@ enum {
OPT_USERAGENT,
OPT_NON_INTER,
OPT_DTLS_LOCAL_PORT,
+ OPT_STOKEN,
};
#ifdef __sun__
@@ -174,6 +188,7 @@ 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(NULL, 0, 0)
};
@@ -274,6 +289,10 @@ 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"));
+#ifndef LIBSTOKEN_HDR
+ printf(" %s\n", _("(NOTE: libstoken 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"));
printf(" --useragent=STRING %s\n", _("HTTP header User-Agent: field"));
@@ -436,6 +455,7 @@ int main(int argc, char **argv)
char *pidfile = NULL;
FILE *fp = NULL;
char *config_arg;
+ char *token_str = NULL;
#ifdef ENABLE_NLS
bindtextdomain("openconnect", LOCALEDIR);
@@ -702,6 +722,10 @@ 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;
+ token_str = keep_config_arg();
+ break;
default:
usage();
}
@@ -729,6 +753,9 @@ int main(int argc, char **argv)
#endif
}
+ if (use_stoken)
+ init_stoken(token_str);
+
if (proxy && openconnect_set_http_proxy(vpninfo, strdup(proxy)))
exit(1);
@@ -1017,6 +1044,129 @@ static int validate_peer_cert(void *_vpninfo, OPENCONNECT_X509 *peer_cert,
}
}
+static void init_stoken(char *token_str)
+{
+#ifdef LIBSTOKEN_HDR
+ int ret;
+ char devid[USER_BUFLEN], pass[USER_BUFLEN];
+
+ stoken_ctx = stoken_new();
+ if (!stoken_ctx) {
+ fprintf(stderr, _("Error initializing stoken library\n"));
+ exit(1);
+ }
+
+ if (token_str) {
+ if (stoken_import_string(stoken_ctx, token_str) < 0) {
+ fprintf(stderr, _("Token string is invalid\n"));
+ exit(1);
+ }
+ } else {
+ if (stoken_import_rcfile(stoken_ctx, NULL) < 0) {
+ fprintf(stderr, _("Unable to read token configuration\n"));
+ exit(1);
+ }
+ }
+
+ while (1) {
+ int n_prompts = 0;
+
+ if (stoken_devid_required(stoken_ctx)) {
+ n_prompts++;
+ if (prompt_user(_("Device ID (for software token): "),
+ devid, USER_BUFLEN, 0) <= 0)
+ goto skip;
+ }
+ if (stoken_pass_required(stoken_ctx)) {
+ n_prompts++;
+ if (prompt_user(_("Password (for software token): "),
+ pass, USER_BUFLEN, 1) <= 0)
+ goto skip;
+ }
+ ret = stoken_decrypt_seed(stoken_ctx, pass, devid);
+ if (!ret)
+ break;
+
+ if (ret == -EIO || !n_prompts) {
+ fprintf(stderr, _("Fatal error decrypting software token\n"));
+ exit(1);
+ }
+ fprintf(stderr, _("Invalid ID/password; try again\n"));
+ }
+
+ while (stoken_pin_required(stoken_ctx)) {
+ if (prompt_user(_("PIN (for software token): "), stoken_pin,
+ USER_BUFLEN, 1) <= 0)
+ goto skip;
+ if (stoken_check_pin(stoken_ctx, stoken_pin) == 0)
+ break;
+ fprintf(stderr, _("Invalid PIN format; try again\n"));
+ }
+
+ return;
+
+skip:
+ fprintf(stderr, _("Skipped response; disabling software token\n"));
+ use_stoken = 0;
+#else
+ fprintf(stderr, _("This version of openconnect was built without libstoken support\n"));
+ exit(1);
+#endif
+}
+
+/* Return value:
+ * < 0, on error
+ * = 0, new tokencode is in opt->value
+ */
+static int try_stoken(struct openconnect_info *vpninfo, struct oc_form_opt *opt)
+{
+#ifdef LIBSTOKEN_HDR
+ char tokencode[STOKEN_MAX_TOKENCODE + 1],
+ lastcode[STOKEN_MAX_TOKENCODE + 1];
+
+ if (stoken_compute_tokencode(stoken_ctx, time(NULL), stoken_pin,
+ tokencode) < 0) {
+ vpn_progress(vpninfo, PRG_ERR,
+ _("Error generating tokencode\n"));
+ return -1;
+ }
+
+ if (stoken_tries == 0) {
+ /* generate tokencode immediately */
+ } else if (stoken_tries == 1 && strcasestr(opt->label, "next")) {
+ /*
+ * The server has asked for the NEXT tokencode/passcode, so
+ * wait. We "could" compute it immediately, but the point is
+ * to sync up the local + remote clocks.
+ */
+ vpn_progress(vpninfo, PRG_INFO, _("Waiting for next tokencode...\n"));
+
+ strcpy(lastcode, tokencode);
+ do {
+ sleep(1);
+ if (stoken_compute_tokencode(stoken_ctx, time(NULL), stoken_pin,
+ tokencode) < 0) {
+ vpn_progress(vpninfo, PRG_ERR,
+ _("Error generating tokencode\n"));
+ return -1;
+ }
+ } while (!strcmp(tokencode, lastcode));
+ } 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"));
+ use_stoken = 0;
+ return -1;
+ }
+
+ stoken_tries++;
+ opt->value = strdup(tokencode);
+ return opt->value ? 0 : -1;
+#else
+ return -1;
+#endif
+}
+
/* Return value:
* < 0, on error
* >=0, otherwise, indicating the length of the response
@@ -1026,6 +1176,11 @@ static int prompt_user(const char *prompt, char *buf, int max_len, int hide_char
struct termios t;
char *p;
+ if (non_inter) {
+ fprintf(stderr, _("User input required in non-interactive mode\n"));
+ exit(1);
+ }
+
fprintf(stderr, "%s", prompt);
fflush(stderr);
@@ -1195,6 +1350,10 @@ static int process_auth_form(void *_vpninfo,
vpninfo->password = NULL;
if (!opt->value)
goto err;
+ } else if (use_stoken &&
+ !strcmp(opt->name, "password") &&
+ try_stoken(vpninfo, opt) == 0) {
+ /* success; but if not, fall back to manual entry */
} else if (prompt_opt(vpninfo, opt, 1) < 0)
goto err;
}
--
1.7.5.4
More information about the openconnect-devel
mailing list