summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Yonan <james@openvpn.net>2011-06-03 21:21:20 +0000
committerJames Yonan <james@openvpn.net>2011-06-03 21:21:20 +0000
commiteab3e22f8261c07d5f906c05fce69917034d9e53 (patch)
treeb1d5d26dcb1edd657f75f4fb03fc46123157be60
parenta114cb750e26e96a727253f316d7415fe34447f6 (diff)
downloadopenvpn-eab3e22f8261c07d5f906c05fce69917034d9e53.tar.gz
openvpn-eab3e22f8261c07d5f906c05fce69917034d9e53.tar.xz
openvpn-eab3e22f8261c07d5f906c05fce69917034d9e53.zip
Added support for static challenge/response protocol.
This includes the new "static-challenge" directive. See management/management-notes.txt for details on both static and dynamic challenge/response protocols. All client-side challenge/response code is #ifdefed on ENABLE_CLIENT_CR and can be removed from the build by commenting out the definition of ENABLE_CLIENT_CR in syshead.h. Version 2.1.3x. git-svn-id: http://svn.openvpn.net/projects/openvpn/branches/BETA21/openvpn@7316 e7ae566f-a301-0410-adde-c780ea21d3b5
-rw-r--r--base64.c10
-rw-r--r--init.c12
-rw-r--r--manage.c47
-rw-r--r--manage.h6
-rw-r--r--management/management-notes.txt110
-rw-r--r--misc.c72
-rw-r--r--misc.h15
-rw-r--r--openvpn.815
-rw-r--r--options.c10
-rw-r--r--options.h3
-rw-r--r--ssl.c32
-rw-r--r--ssl.h6
-rw-r--r--syshead.h2
-rw-r--r--version.m42
14 files changed, 274 insertions, 68 deletions
diff --git a/base64.c b/base64.c
index 26ca7d7..045b043 100644
--- a/base64.c
+++ b/base64.c
@@ -42,6 +42,11 @@
static char base64_chars[] =
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+/*
+ * base64 encode input data of length size to malloced
+ * buffer which is returned as *str. Returns string
+ * length of *str.
+ */
int
base64_encode(const void *data, int size, char **str)
{
@@ -116,6 +121,11 @@ token_decode(const char *token)
return (marker << 24) | val;
}
+/*
+ * Decode base64 str, outputting data to buffer
+ * at data of length size. Return length of
+ * decoded data written or -1 on error or overflow.
+ */
int
base64_decode(const char *str, void *data, int size)
{
diff --git a/init.c b/init.c
index e5ca358..213eb66 100644
--- a/init.c
+++ b/init.c
@@ -343,7 +343,13 @@ init_query_passwords (struct context *c)
#if P2MP
/* Auth user/pass input */
if (c->options.auth_user_pass_file)
- auth_user_pass_setup (c->options.auth_user_pass_file);
+ {
+#ifdef ENABLE_CLIENT_CR
+ auth_user_pass_setup (c->options.auth_user_pass_file, &c->options.sc_info);
+#else
+ auth_user_pass_setup (c->options.auth_user_pass_file, NULL);
+#endif
+ }
#endif
}
@@ -2085,6 +2091,10 @@ do_init_crypto_tls (struct context *c, const unsigned int flags)
to.x509_track = options->x509_track;
#endif
+#ifdef ENABLE_CLIENT_CR
+ to.sci = &options->sc_info;
+#endif
+
/* TLS handshake authentication (--tls-auth) */
if (options->tls_auth_file)
{
diff --git a/manage.c b/manage.c
index a79a8fd..439bd76 100644
--- a/manage.c
+++ b/manage.c
@@ -606,25 +606,19 @@ man_up_finalize (struct management *man)
{
switch (man->connection.up_query_mode)
{
- case UP_QUERY_DISABLED:
- man->connection.up_query.defined = false;
- break;
case UP_QUERY_USER_PASS:
- if (strlen (man->connection.up_query.username) && strlen (man->connection.up_query.password))
- man->connection.up_query.defined = true;
- break;
+ if (!strlen (man->connection.up_query.username))
+ break;
+ /* fall through */
case UP_QUERY_PASS:
- if (strlen (man->connection.up_query.password))
- man->connection.up_query.defined = true;
- break;
case UP_QUERY_NEED_OK:
- if (strlen (man->connection.up_query.password))
- man->connection.up_query.defined = true;
- break;
case UP_QUERY_NEED_STR:
if (strlen (man->connection.up_query.password))
man->connection.up_query.defined = true;
break;
+ case UP_QUERY_DISABLED:
+ man->connection.up_query.defined = false;
+ break;
default:
ASSERT (0);
}
@@ -665,16 +659,17 @@ man_query_user_pass (struct management *man,
static void
man_query_username (struct management *man, const char *type, const char *string)
{
- const bool needed = (man->connection.up_query_mode == UP_QUERY_USER_PASS && man->connection.up_query_type);
+ const bool needed = ((man->connection.up_query_mode == UP_QUERY_USER_PASS
+ ) && man->connection.up_query_type);
man_query_user_pass (man, type, string, needed, "username", man->connection.up_query.username, USER_PASS_LEN);
}
static void
man_query_password (struct management *man, const char *type, const char *string)
{
- const bool needed = ((man->connection.up_query_mode == UP_QUERY_USER_PASS
- || man->connection.up_query_mode == UP_QUERY_PASS)
- && man->connection.up_query_type);
+ const bool needed = ((man->connection.up_query_mode == UP_QUERY_PASS
+ || man->connection.up_query_mode == UP_QUERY_USER_PASS
+ ) && man->connection.up_query_type);
if (!string[0]) /* allow blank passwords to be passed through using the blank_up tag */
string = blank_up;
man_query_user_pass (man, type, string, needed, "password", man->connection.up_query.password, USER_PASS_LEN);
@@ -2843,7 +2838,8 @@ bool
management_query_user_pass (struct management *man,
struct user_pass *up,
const char *type,
- const unsigned int flags)
+ const unsigned int flags,
+ const char *static_challenge)
{
struct gc_arena gc = gc_new ();
bool ret = false;
@@ -2856,7 +2852,9 @@ management_query_user_pass (struct management *man,
const char *alert_type = NULL;
const char *prefix = NULL;
unsigned int up_query_mode = 0;
-
+#ifdef ENABLE_CLIENT_CR
+ const char *sc = NULL;
+#endif
ret = true;
man->persist.standalone_disabled = false; /* This is so M_CLIENT messages will be correctly passed through msg() */
man->persist.special_state_msg = NULL;
@@ -2886,6 +2884,10 @@ management_query_user_pass (struct management *man,
up_query_mode = UP_QUERY_USER_PASS;
prefix = "PASSWORD";
alert_type = "username/password";
+#ifdef ENABLE_CLIENT_CR
+ if (static_challenge)
+ sc = static_challenge;
+#endif
}
buf_printf (&alert_msg, ">%s:Need '%s' %s",
prefix,
@@ -2895,6 +2897,13 @@ management_query_user_pass (struct management *man,
if (flags & (GET_USER_PASS_NEED_OK | GET_USER_PASS_NEED_STR))
buf_printf (&alert_msg, " MSG:%s", up->username);
+#ifdef ENABLE_CLIENT_CR
+ if (sc)
+ buf_printf (&alert_msg, " SC:%d,%s",
+ BOOL_CAST(flags & GET_USER_PASS_STATIC_CHALLENGE_ECHO),
+ sc);
+#endif
+
man_wait_for_client_connection (man, &signal_received, 0, MWCC_PASSWORD_WAIT);
if (signal_received)
ret = false;
@@ -2908,7 +2917,7 @@ management_query_user_pass (struct management *man,
man->connection.up_query_mode = up_query_mode;
man->connection.up_query_type = type;
- /* run command processing event loop until we get our username/password */
+ /* run command processing event loop until we get our username/password/response */
do
{
man_standalone_event_loop (man, &signal_received, 0);
diff --git a/manage.h b/manage.h
index c6ce31e..288e47b 100644
--- a/manage.h
+++ b/manage.h
@@ -365,7 +365,11 @@ void management_set_callback (struct management *man,
void management_clear_callback (struct management *man);
-bool management_query_user_pass (struct management *man, struct user_pass *up, const char *type, const unsigned int flags);
+bool management_query_user_pass (struct management *man,
+ struct user_pass *up,
+ const char *type,
+ const unsigned int flags,
+ const char *static_challenge);
bool management_should_daemonize (struct management *man);
bool management_would_hold (struct management *man);
diff --git a/management/management-notes.txt b/management/management-notes.txt
index 1f4cbd0..6e1e7cd 100644
--- a/management/management-notes.txt
+++ b/management/management-notes.txt
@@ -836,3 +836,113 @@ mappings, when not in single quotations:
interpret it as enclosing a parameter.
\[SPACE] Pass a literal space or tab character, don't
interpret it as a parameter delimiter.
+
+Challenge/Response Protocol
+---------------------------
+
+The OpenVPN Challenge/Response Protocol allows an OpenVPN server to
+generate challenge questions that are shown to the user, and to see
+the user's responses to those challenges. Based on the responses, the
+server can allow or deny access.
+
+In this way, the OpenVPN Challenge/Response Protocol can be used
+to implement multi-factor authentication. Two different
+variations on the challenge/response protocol are supported: the
+"Dynamic" and "Static" protocols.
+
+The basic idea of Challenge/Response is that the user must enter an
+additional piece of information, in addition to the username and
+password, to successfully authenticate. Normally, this information
+is used to prove that the user posesses a certain key-like device
+such as cryptographic token or a particular mobile phone.
+
+Dynamic protocol:
+
+The OpenVPN dynamic challenge/response protocol works by returning
+a specially formatted error message after initial successful
+authentication. This error message contains the challenge question,
+and is formatted as such:
+
+ CRV1:<flags>:<state_id>:<username_base64>:<challenge_text>
+
+flags: a series of optional, comma-separated flags:
+ E : echo the response when the user types it
+ R : a response is required
+
+state_id: an opaque string that should be returned to the server
+ along with the response.
+
+username_base64 : the username formatted as base64
+
+challenge_text : the challenge text to be shown to the user
+
+Example challenge:
+
+ CRV1:R,E:Om01u7Fh4LrGBS7uh0SWmzwabUiGiW6l:Y3Ix:Please enter token PIN
+
+After showing the challenge_text and getting a response from the user
+(if R flag is specified), the client should submit the following
+auth creds back to the OpenVPN server:
+
+Username: [username decoded from username_base64]
+Password: CRV1::<state_id>::<response_text>
+
+Where state_id is taken from the challenge request and response_text
+is what the user entered in response to the challenge_text.
+If the R flag is not present, response_text may be the empty
+string.
+
+Example response (suppose the user enters "8675309" for the token PIN):
+
+ Username: cr1 ("Y3Ix" base64 decoded)
+ Password: CRV1::Om01u7Fh4LrGBS7uh0SWmzwabUiGiW6l::8675309
+
+Static protocol:
+
+The static protocol differs from the dynamic protocol in that the
+challenge question and response field is given to the user in the
+initial username/password dialog, and the username, password, and
+response are delivered back to the server in a single transaction.
+
+The "static-challenge" directive is used to give the challenge text
+to OpenVPN and indicate whether or not the response should be echoed.
+
+When the "static-challenge" directive is used, the management
+interface will respond as such when credentials are needed:
+
+ >PASSWORD:Need 'Auth' username/password SC:<ECHO>,<TEXT>
+
+ ECHO: "1" if response should be echoed, "0" to not echo
+ TEXT: challenge text that should be shown to the user to
+ facilitate their response
+
+For example:
+
+ >PASSWORD:Need 'Auth' username/password SC:1,Please enter token PIN
+
+The above notification indicates that OpenVPN needs a --auth-user-pass
+password plus a response to a static challenge ("Please enter token PIN").
+The "1" after the "SC:" indicates that the response should be echoed.
+
+The management interface client in this case should add the static
+challenge text to the auth dialog followed by a field for the user to
+enter a response. Then the client should pack the password and response
+together into an encoded password:
+
+ username "Auth" foo
+ password "Auth" "SCRV1:<BASE64_PASSWORD>:<BASE64_RESPONSE>"
+
+For example, if the user entered "bar" as the password and 8675309
+as the PIN, the following management interface commands should be
+issued:
+
+ username "Auth" foo
+ password "Auth" "SCRV1:Zm9v:ODY3NTMwOQ=="
+
+Client-side support for challenge/response protocol:
+
+Currently, the Access Server client and standalone OpenVPN
+client support both static and dynamic challenge/response
+protocols. However, any OpenVPN client UI that drives OpenVPN
+via the management interface needs to add explicit support
+for the challenge/response protocol.
diff --git a/misc.c b/misc.c
index 4a80004..7f6595e 100644
--- a/misc.c
+++ b/misc.c
@@ -1387,10 +1387,16 @@ get_user_pass_cr (struct user_pass *up,
&& ((auth_file && streq (auth_file, "management")) || (from_stdin && (flags & GET_USER_PASS_MANAGEMENT)))
&& management_query_user_pass_enabled (management))
{
+ const char *sc = NULL;
+
if (flags & GET_USER_PASS_PREVIOUS_CREDS_FAILED)
management_auth_failure (management, prefix, "previous auth credentials failed");
- if (!management_query_user_pass (management, up, prefix, flags))
+#ifdef ENABLE_CLIENT_CR
+ if (auth_challenge && (flags & GET_USER_PASS_STATIC_CHALLENGE))
+ sc = auth_challenge;
+#endif
+ if (!management_query_user_pass (management, up, prefix, flags, sc))
{
if ((flags & GET_USER_PASS_NOFATAL) != 0)
return false;
@@ -1422,7 +1428,7 @@ get_user_pass_cr (struct user_pass *up,
else if (from_stdin)
{
#ifdef ENABLE_CLIENT_CR
- if (auth_challenge)
+ if (auth_challenge && (flags & GET_USER_PASS_DYNAMIC_CHALLENGE))
{
struct auth_challenge_info *ac = get_auth_challenge (auth_challenge, &gc);
if (ac)
@@ -1431,7 +1437,7 @@ get_user_pass_cr (struct user_pass *up,
struct buffer packed_resp;
buf_set_write (&packed_resp, (uint8_t*)up->password, USER_PASS_LEN);
- msg (M_INFO, "CHALLENGE: %s", ac->challenge_text);
+ msg (M_INFO|M_NOPREFIX, "CHALLENGE: %s", ac->challenge_text);
if (!get_console_input ("Response:", BOOL_CAST(ac->flags&CR_ECHO), response, USER_PASS_LEN))
msg (M_FATAL, "ERROR: could not read challenge response from stdin");
strncpynt (up->username, ac->user, USER_PASS_LEN);
@@ -1461,6 +1467,28 @@ get_user_pass_cr (struct user_pass *up,
if (!get_console_input (BSTR (&pass_prompt), false, up->password, USER_PASS_LEN))
msg (M_FATAL, "ERROR: could not not read %s password from stdin", prefix);
+
+#ifdef ENABLE_CLIENT_CR
+ if (auth_challenge && (flags & GET_USER_PASS_STATIC_CHALLENGE))
+ {
+ char *response = (char *) gc_malloc (USER_PASS_LEN, false, &gc);
+ struct buffer packed_resp;
+ char *pw64=NULL, *resp64=NULL;
+
+ msg (M_INFO|M_NOPREFIX, "CHALLENGE: %s", auth_challenge);
+ if (!get_console_input ("Response:", BOOL_CAST(flags & GET_USER_PASS_STATIC_CHALLENGE_ECHO), response, USER_PASS_LEN))
+ msg (M_FATAL, "ERROR: could not read static challenge response from stdin");
+ if (base64_encode(up->password, strlen(up->password), &pw64) == -1
+ || base64_encode(response, strlen(response), &resp64) == -1)
+ msg (M_FATAL, "ERROR: could not base64-encode password/static_response");
+ buf_set_write (&packed_resp, (uint8_t*)up->password, USER_PASS_LEN);
+ buf_printf (&packed_resp, "SCRV1:%s:%s", pw64, resp64);
+ string_clear(pw64);
+ free(pw64);
+ string_clear(resp64);
+ free(resp64);
+ }
+#endif
}
}
else
@@ -1528,42 +1556,8 @@ get_user_pass_cr (struct user_pass *up,
#ifdef ENABLE_CLIENT_CR
/*
- * Parse a challenge message returned along with AUTH_FAILED.
- * The message is formatted as such:
- *
- * CRV1:<flags>:<state_id>:<username_base64>:<challenge_text>
- *
- * flags: a series of optional, comma-separated flags:
- * E : echo the response when the user types it
- * R : a response is required
- *
- * state_id: an opaque string that should be returned to the server
- * along with the response.
- *
- * username_base64 : the username formatted as base64
- *
- * challenge_text : the challenge text to be shown to the user
- *
- * Example challenge:
- *
- * CRV1:R,E:Om01u7Fh4LrGBS7uh0SWmzwabUiGiW6l:Y3Ix:Please enter token PIN
- *
- * After showing the challenge_text and getting a response from the user
- * (if R flag is specified), the client should submit the following
- * auth creds back to the OpenVPN server:
- *
- * Username: [username decoded from username_base64]
- * Password: CRV1::<state_id>::<response_text>
- *
- * Where state_id is taken from the challenge request and response_text
- * is what the user entered in response to the challenge_text.
- * If the R flag is not present, response_text may be the empty
- * string.
- *
- * Example response (suppose the user enters "8675309" for the token PIN):
- *
- * Username: cr1 ("Y3Ix" base64 decoded)
- * Password: CRV1::Om01u7Fh4LrGBS7uh0SWmzwabUiGiW6l::8675309
+ * See management/management-notes.txt for more info on the
+ * the dynamic challenge/response protocol implemented here.
*/
struct auth_challenge_info *
get_auth_challenge (const char *auth_challenge, struct gc_arena *gc)
diff --git a/misc.h b/misc.h
index cc6745a..ae95929 100644
--- a/misc.h
+++ b/misc.h
@@ -268,8 +268,19 @@ struct auth_challenge_info {
struct auth_challenge_info *get_auth_challenge (const char *auth_challenge, struct gc_arena *gc);
+/*
+ * Challenge response info on client as pushed by server.
+ */
+struct static_challenge_info {
+# define SC_ECHO (1<<0) /* echo response when typed by user */
+ unsigned int flags;
+
+ const char *challenge_text;
+};
+
#else
struct auth_challenge_info {};
+struct static_challenge_info {};
#endif
bool get_console_input (const char *prompt, const bool echo, char *input, const int capacity);
@@ -285,6 +296,10 @@ bool get_console_input (const char *prompt, const bool echo, char *input, const
#define GET_USER_PASS_NEED_STR (1<<5)
#define GET_USER_PASS_PREVIOUS_CREDS_FAILED (1<<6)
+#define GET_USER_PASS_DYNAMIC_CHALLENGE (1<<7) /* CRV1 protocol -- dynamic challenge */
+#define GET_USER_PASS_STATIC_CHALLENGE (1<<8) /* SCRV1 protocol -- static challenge */
+#define GET_USER_PASS_STATIC_CHALLENGE_ECHO (1<<9) /* SCRV1 protocol -- echo response */
+
bool get_user_pass_cr (struct user_pass *up,
const char *auth_file,
const char *prefix,
diff --git a/openvpn.8 b/openvpn.8
index 85889de..29eb00b 100644
--- a/openvpn.8
+++ b/openvpn.8
@@ -3362,6 +3362,21 @@ Note that while this option cannot be pushed, it can be controlled
from the management interface.
.\"*********************************************************
.TP
+.B \-\-static\-challenge t e
+Enable static challenge/response protocol using challenge text
+.B t,
+with
+echo flag given by
+.B e
+(0|1).
+
+The echo flag indicates whether or not the user's response
+to the challenge should be echoed.
+
+See management\-notes.txt in the OpenVPN distribution for a
+description of the OpenVPN challenge/response protocol.
+.\"*********************************************************
+.TP
.B --server-poll-timeout n
when polling possible remote servers to connect to
in a round-robin fashion, spend no more than
diff --git a/options.c b/options.c
index df7546c..bbe9f67 100644
--- a/options.c
+++ b/options.c
@@ -443,6 +443,8 @@ static const char usage_message[] =
" when connecting to a '--mode server' remote host.\n"
"--auth-retry t : How to handle auth failures. Set t to\n"
" none (default), interact, or nointeract.\n"
+ "--static-challenge t e : Enable static challenge/response protocol using\n"
+ " challenge text t, with e indicating echo flag (0|1)\n"
"--server-poll-timeout n : when polling possible remote servers to connect to\n"
" in a round-robin fashion, spend no more than n seconds\n"
" waiting for a response before trying the next server.\n"
@@ -5251,6 +5253,14 @@ add_option (struct options *options,
VERIFY_PERMISSION (OPT_P_GENERAL);
auth_retry_set (msglevel, p[1]);
}
+#ifdef ENABLE_CLIENT_CR
+ else if (streq (p[0], "static-challenge") && p[1] && p[2])
+ {
+ options->sc_info.challenge_text = p[1];
+ if (atoi(p[2]))
+ options->sc_info.flags |= SC_ECHO;
+ }
+#endif
#endif
#ifdef WIN32
else if (streq (p[0], "win-sys") && p[1])
diff --git a/options.h b/options.h
index 91ec6dc..f74c9b3 100644
--- a/options.h
+++ b/options.h
@@ -426,6 +426,9 @@ struct options
const char *auth_user_pass_verify_script;
bool auth_user_pass_verify_script_via_file;
+#ifdef ENABLE_CLIENT_CR
+ struct static_challenge_info sc_info;
+#endif
#if PORT_SHARE
char *port_share_host;
int port_share_port;
diff --git a/ssl.c b/ssl.c
index df237cc..8aa6c60 100644
--- a/ssl.c
+++ b/ssl.c
@@ -292,17 +292,35 @@ static char *auth_challenge; /* GLOBAL */
#endif
void
-auth_user_pass_setup (const char *auth_file)
+auth_user_pass_setup (const char *auth_file, const struct static_challenge_info *sci)
{
auth_user_pass_enabled = true;
if (!auth_user_pass.defined)
{
#if AUTO_USERID
get_user_pass_auto_userid (&auth_user_pass, auth_file);
-#elif defined(ENABLE_CLIENT_CR)
- get_user_pass_cr (&auth_user_pass, auth_file, UP_TYPE_AUTH, GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE, auth_challenge);
#else
- get_user_pass (&auth_user_pass, auth_file, UP_TYPE_AUTH, GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE);
+# ifdef ENABLE_CLIENT_CR
+ if (auth_challenge) /* dynamic challenge/response */
+ get_user_pass_cr (&auth_user_pass,
+ auth_file,
+ UP_TYPE_AUTH,
+ GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE|GET_USER_PASS_DYNAMIC_CHALLENGE,
+ auth_challenge);
+ else if (sci) /* static challenge response */
+ {
+ int flags = GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE|GET_USER_PASS_STATIC_CHALLENGE;
+ if (sci->flags & SC_ECHO)
+ flags |= GET_USER_PASS_STATIC_CHALLENGE_ECHO;
+ get_user_pass_cr (&auth_user_pass,
+ auth_file,
+ UP_TYPE_AUTH,
+ flags,
+ sci->challenge_text);
+ }
+ else
+# endif
+ get_user_pass (&auth_user_pass, auth_file, UP_TYPE_AUTH, GET_USER_PASS_MANAGEMENT|GET_USER_PASS_SENSITIVE);
#endif
}
}
@@ -3945,7 +3963,11 @@ key_method_2_write (struct buffer *buf, struct tls_session *session)
/* write username/password if specified */
if (auth_user_pass_enabled)
{
- auth_user_pass_setup (NULL);
+#ifdef ENABLE_CLIENT_CR
+ auth_user_pass_setup (NULL, session->opt->sci);
+#else
+ auth_user_pass_setup (NULL, NULL);
+#endif
if (!write_string (buf, auth_user_pass.username, -1))
goto error;
if (!write_string (buf, auth_user_pass.password, -1))
diff --git a/ssl.h b/ssl.h
index 1b23d7d..08bf53b 100644
--- a/ssl.h
+++ b/ssl.h
@@ -516,6 +516,10 @@ struct tls_options
const struct x509_track *x509_track;
#endif
+#ifdef ENABLE_CLIENT_CR
+ const struct static_challenge_info *sci;
+#endif
+
/* --gremlin bits */
int gremlin;
};
@@ -723,7 +727,7 @@ void get_highest_preference_tls_cipher (char *buf, int size);
void pem_password_setup (const char *auth_file);
int pem_password_callback (char *buf, int size, int rwflag, void *u);
-void auth_user_pass_setup (const char *auth_file);
+void auth_user_pass_setup (const char *auth_file, const struct static_challenge_info *sc_info);
void ssl_set_auth_nocache (void);
void ssl_set_auth_token (const char *token);
void ssl_purge_auth (const bool auth_user_pass_only);
diff --git a/syshead.h b/syshead.h
index 0da1fc2..038b484 100644
--- a/syshead.h
+++ b/syshead.h
@@ -683,7 +683,7 @@ socket_defined (const socket_descriptor_t sd)
#endif
/*
- * Do we support challenge/response authentication, as a console-based client?
+ * Do we support challenge/response authentication as client?
*/
#define ENABLE_CLIENT_CR
diff --git a/version.m4 b/version.m4
index 77e4950..f7d5007 100644
--- a/version.m4
+++ b/version.m4
@@ -1,5 +1,5 @@
dnl define the OpenVPN version
-define(PRODUCT_VERSION,[2.1.3w])
+define(PRODUCT_VERSION,[2.1.3x])
dnl define the TAP version
define(PRODUCT_TAP_ID,[tap0901])
define(PRODUCT_TAP_WIN32_MIN_MAJOR,[9])