@@ -5347,6 +5347,12 @@ into the file/buffer for dynamic configuration data. This
will then make the OpenVPN server to push this value to the
client, which replaces the local password with the
UNIQUE_TOKEN_VALUE.
+
+Newer clients (2.4.7+) will fall back to the original password method
+after a failed auth. Older clients will keep using the token value
+and react acording to
+.B \-\-auth-retry
+.
.\"*********************************************************
.TP
.B \-\-tls\-verify cmd
@@ -469,7 +469,7 @@ purge_user_pass(struct user_pass *up, const bool force)
* don't show warning if the pass has been replaced by a token: this is an
* artificial "auth-nocache"
*/
- else if (!warn_shown && (!up->tokenized))
+ else if (!warn_shown)
{
msg(M_WARN, "WARNING: this configuration may cache passwords in memory -- use the auth-nocache option to prevent this");
warn_shown = true;
@@ -477,14 +477,18 @@ purge_user_pass(struct user_pass *up, const bool force)
}
void
-set_auth_token(struct user_pass *up, const char *token)
+set_auth_token(struct user_pass *up, struct user_pass *tk, const char *token)
{
- if (token && strlen(token) && up && up->defined && !up->nocache)
+
+ if (token && strlen(token) && up && up->defined)
{
- CLEAR(up->password);
- strncpynt(up->password, token, USER_PASS_LEN);
- up->tokenized = true;
+ strncpynt(tk->password, token, USER_PASS_LEN);
+ strncpynt(tk->username, up->username, USER_PASS_LEN);
+ tk->defined = true;
}
+
+ /* Cleans user/pass for nocache */
+ purge_user_pass(up, false);
}
/*
@@ -63,7 +63,6 @@ struct user_pass
{
bool defined;
bool nocache;
- bool tokenized; /* true if password has been substituted by a token */
bool wait_for_push; /* true if this object is waiting for a push-reply */
/* max length of username/password */
@@ -145,7 +144,8 @@ void fail_user_pass(const char *prefix,
void purge_user_pass(struct user_pass *up, const bool force);
-void set_auth_token(struct user_pass *up, const char *token);
+void set_auth_token(struct user_pass *up, struct user_pass *tk,
+ const char *token);
/*
* Process string received by untrusted peer before
@@ -55,8 +55,20 @@ receive_auth_failed(struct context *c, const struct buffer *buffer)
if (c->options.pull)
{
- switch (auth_retry_get())
+ /* Before checking how to react on AUTH_FAILED, first check if the
+ * failed auth might be the result of an expired auth-token.
+ * Note that a server restart will trigger a generic AUTH_FAILED
+ * instead an AUTH_FAILED,SESSION so handle all AUTH_FAILED message
+ * identical for this scenario */
+ if (ssl_clean_auth_token())
{
+ c->sig->signal_received = SIGUSR1; /* SOFT-SIGUSR1 -- Auth failure error */
+ c->sig->signal_text = "auth-failure (auth-token)";
+ }
+ else
+ {
+ switch (auth_retry_get())
+ {
case AR_NONE:
c->sig->signal_received = SIGTERM; /* SOFT-SIGTERM -- Auth failure error */
break;
@@ -70,8 +82,9 @@ receive_auth_failed(struct context *c, const struct buffer *buffer)
default:
ASSERT(0);
+ }
+ c->sig->signal_text = "auth-failure";
}
- c->sig->signal_text = "auth-failure";
#ifdef ENABLE_MANAGEMENT
if (management)
{
@@ -398,6 +398,7 @@ pem_password_callback(char *buf, int size, int rwflag, void *u)
static bool auth_user_pass_enabled; /* GLOBAL */
static struct user_pass auth_user_pass; /* GLOBAL */
+static struct user_pass auth_token; /* GLOBAL */
#ifdef ENABLE_MANAGEMENT
static char *auth_challenge; /* GLOBAL */
@@ -407,7 +408,7 @@ void
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 (!auth_user_pass.defined && !auth_token.defined)
{
#ifdef ENABLE_MANAGEMENT
if (auth_challenge) /* dynamic challenge/response */
@@ -445,7 +446,7 @@ ssl_set_auth_nocache(void)
{
passbuf.nocache = true;
auth_user_pass.nocache = true;
- /* wait for push-reply, because auth-token may invert nocache */
+ /* wait for push-reply, because auth-token may still need the username */
auth_user_pass.wait_for_push = true;
}
@@ -455,15 +456,18 @@ ssl_set_auth_nocache(void)
void
ssl_set_auth_token(const char *token)
{
- if (auth_user_pass.nocache)
- {
- msg(M_INFO,
- "auth-token received, disabling auth-nocache for the "
- "authentication token");
- auth_user_pass.nocache = false;
- }
+ set_auth_token(&auth_user_pass, &auth_token, token);
+}
- set_auth_token(&auth_user_pass, token);
+/*
+ * Cleans an auth token and checks if it was active
+ */
+bool
+ssl_clean_auth_token (void)
+{
+ bool wasdefined = auth_token.defined;
+ purge_user_pass(&auth_token, true);
+ return wasdefined;
}
/*
@@ -2369,19 +2373,26 @@ key_method_2_write(struct buffer *buf, struct tls_session *session)
#else
auth_user_pass_setup(session->opt->auth_user_pass_file, NULL);
#endif
- if (!write_string(buf, auth_user_pass.username, -1))
+ struct user_pass *up = &auth_user_pass;
+
+ /*
+ * If we have a valid auth-token, send that instead of real
+ * username/password
+ */
+ if (auth_token.defined)
+ up = &auth_token;
+
+ if (!write_string(buf, up->username, -1))
{
goto error;
}
- if (!write_string(buf, auth_user_pass.password, -1))
+ else if (!write_string(buf, up->password, -1))
{
goto error;
}
/* if auth-nocache was specified, the auth_user_pass object reaches
* a "complete" state only after having received the push-reply
* message.
- * This is the case because auth-token statement in a push-reply would
- * invert its nocache.
*
* For this reason, skip the purge operation here if no push-reply
* message has been received yet.
@@ -437,6 +437,8 @@ void ssl_set_auth_token(const char *token);
*/
void ssl_purge_auth_challenge(void);
+bool ssl_clean_auth_token(void);
+
void ssl_put_auth_challenge(const char *cr_str);
#endif
Auth-tokens can expire. For by reconnecting when the server uses auth-gen-toke. Behaviour of OpenVPN client is to never fallback to the previous authentication method and continue using the auth-token. Depending on auth-retry it either quit or tried endlessly with an expired token. Since auth-gen-token tokens expire on reconnect, a client will not survive a reconnect. This patches changes the behaviour on failed auth when using an auth-token as a soft error (USR1) and clean the auth-token falling back to the original auth method. Patch V2: properly formatted commit message, fix openvpn3 detection Patch V3: remove all server changes, include only minimal non intrusive client changes that only improve error recovery but don't change overall behaviour. Patch V4: forget add push.c to git index, now also included Signed-off-by: Arne Schwabe <arne@rfc2549.org> --- doc/openvpn.8 | 6 ++++++ src/openvpn/misc.c | 16 ++++++++++------ src/openvpn/misc.h | 4 ++-- src/openvpn/push.c | 17 +++++++++++++++-- src/openvpn/ssl.c | 39 +++++++++++++++++++++++++-------------- src/openvpn/ssl.h | 2 ++ 6 files changed, 60 insertions(+), 24 deletions(-)