]> git.ipfire.org Git - thirdparty/openvpn.git/commitdiff
Added support for static challenge/response protocol.
authorJames Yonan <james@openvpn.net>
Fri, 3 Jun 2011 21:21:20 +0000 (21:21 +0000)
committerJames Yonan <james@openvpn.net>
Fri, 3 Jun 2011 21:21:20 +0000 (21:21 +0000)
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

14 files changed:
base64.c
init.c
manage.c
manage.h
management/management-notes.txt
misc.c
misc.h
openvpn.8
options.c
options.h
ssl.c
ssl.h
syshead.h
version.m4

index 26ca7d711d09fad9f5ddd6e7004c55f587c312fc..045b04331ad62a7cb64de3d99e07a0d95c12162b 100644 (file)
--- a/base64.c
+++ b/base64.c
 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 e5ca358edf6b8f83fd62e8d9679cf6907cae03f1..213eb662067f9d613aa3ce9a004d4448aa306e08 100644 (file)
--- 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)
     {
index a79a8fd40a5bba8e70a8d8628b0ea352d46781a6..439bd76edcfb6e61806c4a6877d65554491e795c 100644 (file)
--- 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);
index c6ce31effd1ce0ca48da1a8a34f2ab9d1e0bb99b..288e47b273c89ece7b26a1e9e019c6aaea306531 100644 (file)
--- 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);
index 1f4cbd0193116ed4bc72a4ceb108080da42399ab..6e1e7cdcebec1578723f5d5ff49e1ce733e18b79 100644 (file)
@@ -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 4a8000418fff35a6aaa1f0ff4be7b35f0c0a2818..7f6595e180955a12fda1d1d05128b269408938c5 100644 (file)
--- 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 cc6745ab5c339a77e13e70c9387c7f259d1e440d..ae95929547c34d7c08f1ea8ac77a88ecb86630a3 100644 (file)
--- 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,
index 85889de6a430980c60b371d77aba3c1378a7015f..29eb00bafe8786ab568d49ec2fb676f792727490 100644 (file)
--- 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
index df7546ce65644b07cd0a8aa4865888e4e984ac73..bbe9f67bbaaa9613d385c07d9211e54a7b851446 100644 (file)
--- 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])
index 91ec6dceadf780cd8b36ca99f432855d5ec4e218..f74c9b3da8ca7a1757c8cf6e36402380d4eb62f7 100644 (file)
--- 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 df237ccf697bb1172428500b2fffcfba671e81e0..8aa6c6097dbb3a62284757ae440202bbb3dfe208 100644 (file)
--- 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 1b23d7d37171748e8cc59d24dd88bba53585e3d4..08bf53ba713ee09b5d53e64b4b6b8526cc0ad605 100644 (file)
--- 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);
index 0da1fc263454d9195ea440980d0dfb7f68ae8c80..038b48416c4693f2fa5e044452651a2669259057 100644 (file)
--- 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
 
index 77e4950c7465b3fad58cd416f068ee174561d7e5..f7d5007b636aa4f9bf1cd5395a299c7ce0bac587 100644 (file)
@@ -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])