[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