aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/openvpn/auth_token.c91
-rw-r--r--src/openvpn/auth_token.h11
-rw-r--r--src/openvpn/push.c8
-rw-r--r--src/openvpn/ssl.c7
-rw-r--r--src/openvpn/ssl_common.h9
-rw-r--r--src/openvpn/ssl_verify.c31
6 files changed, 99 insertions, 58 deletions
diff --git a/src/openvpn/auth_token.c b/src/openvpn/auth_token.c
index 0ea6d18..c6c37ea 100644
--- a/src/openvpn/auth_token.c
+++ b/src/openvpn/auth_token.c
@@ -21,6 +21,8 @@
const char *auth_token_pem_name = "OpenVPN auth-token server key";
#define AUTH_TOKEN_SESSION_ID_LEN 12
+#define AUTH_TOKEN_SESSION_ID_BASE64_LEN (AUTH_TOKEN_SESSION_ID_LEN * 8 / 6)
+
#if AUTH_TOKEN_SESSION_ID_LEN % 3
#error AUTH_TOKEN_SESSION_ID_LEN needs to be multiple a 3
#endif
@@ -109,11 +111,11 @@ add_session_token_env(struct tls_session *session, struct tls_multi *multi,
/*
* No session before, generate a new session token for the new session
*/
- if (!multi->auth_token)
+ if (!multi->auth_token_initial)
{
generate_auth_token(up, multi);
}
- session_id_source = multi->auth_token;
+ session_id_source = multi->auth_token_initial;
}
/*
* In the auth-token the auth token is already base64 encoded
@@ -184,7 +186,7 @@ generate_auth_token(const struct user_pass *up, struct tls_multi *multi)
uint8_t sessid[AUTH_TOKEN_SESSION_ID_LEN];
- if (multi->auth_token)
+ if (multi->auth_token_initial)
{
/* Just enough space to fit 8 bytes+ 1 extra to decode a non padded
* base64 string (multiple of 3 bytes). 9 bytes => 12 bytes base64
@@ -192,13 +194,16 @@ generate_auth_token(const struct user_pass *up, struct tls_multi *multi)
*/
char old_tstamp_decode[9];
- /*
- * reuse the same session id and timestamp and null terminate it at
- * for base64 decode it only decodes the session id part of it
- */
- char *old_sessid = multi->auth_token + strlen(SESSION_ID_PREFIX);
+ /* Make a copy of the string to not modify multi->auth_token_initial */
+ char *initial_token_copy = string_alloc(multi->auth_token_initial, &gc);
+
+ char *old_sessid = initial_token_copy + strlen(SESSION_ID_PREFIX);
char *old_tsamp_initial = old_sessid + AUTH_TOKEN_SESSION_ID_LEN*8/6;
+ /*
+ * We null terminate the old token just after the session ID to let
+ * our base64 decode function only decode the session ID
+ */
old_tsamp_initial[12] = '\0';
ASSERT(openvpn_base64_decode(old_tsamp_initial, old_tstamp_decode, 9) == 9);
@@ -211,11 +216,7 @@ generate_auth_token(const struct user_pass *up, struct tls_multi *multi)
initial_timestamp = *tstamp_ptr;
old_tsamp_initial[0] = '\0';
- ASSERT(openvpn_base64_decode(old_sessid, sessid, AUTH_TOKEN_SESSION_ID_LEN)==AUTH_TOKEN_SESSION_ID_LEN);
-
-
- /* free the auth-token, we will replace it with a new one */
- free(multi->auth_token);
+ ASSERT(openvpn_base64_decode(old_sessid, sessid, AUTH_TOKEN_SESSION_ID_LEN) == AUTH_TOKEN_SESSION_ID_LEN);
}
else if (!rand_bytes(sessid, AUTH_TOKEN_SESSION_ID_LEN))
{
@@ -272,11 +273,22 @@ generate_auth_token(const struct user_pass *up, struct tls_multi *multi)
free(b64output);
+ /* free the auth-token if defined, we will replace it with a new one */
+ free(multi->auth_token);
multi->auth_token = strdup((char *)BPTR(&session_token));
dmsg(D_SHOW_KEYS, "Generated token for client: %s (%s)",
multi->auth_token, up->username);
+ if (!multi->auth_token_initial)
+ {
+ /*
+ * Save the initial auth token to continue using the same session ID
+ * and timestamp in updates
+ */
+ multi->auth_token_initial = strdup(multi->auth_token);
+ }
+
gc_free(&gc);
}
@@ -343,23 +355,17 @@ verify_auth_token(struct user_pass *up, struct tls_multi *multi,
}
else
{
- msg(M_WARN, "--auth-token-gen: HMAC on token from client failed (%s)",
+ msg(M_WARN, "--auth-gen-token: HMAC on token from client failed (%s)",
up->username);
return 0;
}
/* Accept session tokens that not expired are in the acceptable range
* for renogiations */
- bool in_renog_time = now >= timestamp
- && now < timestamp + 2 * session->opt->renegotiate_seconds;
-
- /* We could still have a client that does not update
- * its auth-token, so also allow the initial auth-token */
- bool initialtoken = multi->auth_token_initial
- && memcmp_constant_time(up->password, multi->auth_token_initial,
- strlen(multi->auth_token_initial)) == 0;
+ bool in_renegotiation_time = now >= timestamp
+ && now < timestamp + 2 * session->opt->renegotiate_seconds;
- if (!in_renog_time && !initialtoken)
+ if (!in_renegotiation_time)
{
ret |= AUTH_TOKEN_EXPIRED;
}
@@ -383,7 +389,20 @@ verify_auth_token(struct user_pass *up, struct tls_multi *multi,
{
/* Tell client that the session token is expired */
auth_set_client_reason(multi, "SESSION: token expired");
- msg(M_INFO, "--auth-token-gen: auth-token from client expired");
+ msg(M_INFO, "--auth-gen-token: auth-token from client expired");
+ }
+
+ /* Check that we do have the same session ID in the token as in our stored
+ * auth-token to ensure that it did not change.
+ * This also compares the prefix and session part of the
+ * tokens, which should be identical if the session ID stayed the same */
+ if (multi->auth_token_initial
+ && memcmp_constant_time(multi->auth_token_initial, up->password,
+ strlen(SESSION_ID_PREFIX) + AUTH_TOKEN_SESSION_ID_BASE64_LEN))
+ {
+ msg(M_WARN, "--auth-gen-token: session id in token changed (Rejecting "
+ "token.");
+ ret = 0;
}
return ret;
}
@@ -408,3 +427,27 @@ wipe_auth_token(struct tls_multi *multi)
multi->auth_token_initial = NULL;
}
}
+
+void
+resend_auth_token_renegotiation(struct tls_multi *multi, struct tls_session *session)
+{
+ /*
+ * Auth token already sent to client, update auth-token on client.
+ * The initial auth-token is sent as part of the push message, for this
+ * update we need to schedule an extra push message.
+ *
+ * Otherwise the auth-token get pushed out as part of the "normal"
+ * push-reply
+ */
+ bool is_renegotiation = session->key[KS_PRIMARY].key_id != 0;
+
+ if (multi->auth_token_initial && is_renegotiation)
+ {
+ /*
+ * We do not explicitly reschedule the sending of the
+ * control message here. This might delay this reply
+ * a few seconds but this message is not time critical
+ */
+ send_push_reply_auth_token(multi);
+ }
+}
diff --git a/src/openvpn/auth_token.h b/src/openvpn/auth_token.h
index 73a00dd..de93a94 100644
--- a/src/openvpn/auth_token.h
+++ b/src/openvpn/auth_token.h
@@ -117,7 +117,7 @@ void wipe_auth_token(struct tls_multi *multi);
/**
* Return if the password string has the format of a password.
*
- * This fuction will always read as many bytes as SESSION_ID_PREFIX is longer
+ * This function will always read as many bytes as SESSION_ID_PREFIX is longer
* the caller needs ensure that password memory is at least that long (true for
* calling with struct user_pass)
* @param password
@@ -129,4 +129,13 @@ is_auth_token(const char *password)
return (memcmp_constant_time(SESSION_ID_PREFIX, password,
strlen(SESSION_ID_PREFIX)) == 0);
}
+/**
+ * Checks if a client should be sent a new auth token to update its
+ * current auth-token
+ * @param multi Pointer the multi object of the TLS session
+ * @param session Pointer to the TLS session itself
+ */
+void
+resend_auth_token_renegotiation(struct tls_multi *multi, struct tls_session *session);
+
#endif /* AUTH_TOKEN_H */
diff --git a/src/openvpn/push.c b/src/openvpn/push.c
index f4957f1..53cb7ca 100644
--- a/src/openvpn/push.c
+++ b/src/openvpn/push.c
@@ -527,14 +527,6 @@ prepare_auth_token_push_reply(struct tls_multi *tls_multi, struct gc_arena *gc,
push_option_fmt(gc, push_list, M_USAGE,
"auth-token %s",
tls_multi->auth_token);
- if (!tls_multi->auth_token_initial)
- {
- /*
- * Save the initial auth token for clients that ignore
- * the updates to the token
- */
- tls_multi->auth_token_initial = strdup(tls_multi->auth_token);
- }
}
}
diff --git a/src/openvpn/ssl.c b/src/openvpn/ssl.c
index 615ed69..e9b8d1b 100644
--- a/src/openvpn/ssl.c
+++ b/src/openvpn/ssl.c
@@ -3115,13 +3115,18 @@ tls_multi_process(struct tls_multi *multi,
if (ks->state == S_ACTIVE && ks->authenticated == KS_AUTH_TRUE)
{
- /* This will move ks->state from S_ACTIVE to S_GENERATED_KEYS */
+ /* Session is now fully authenticated.
+ * tls_session_generate_data_channel_keys will move ks->state
+ * from S_ACTIVE to S_GENERATED_KEYS */
if (!tls_session_generate_data_channel_keys(session))
{
msg(D_TLS_ERRORS, "TLS Error: generate_key_expansion failed");
ks->authenticated = KS_AUTH_FALSE;
ks->state = S_ERROR;
}
+
+ /* Update auth token on the client if needed */
+ resend_auth_token_renegotiation(multi, session);
}
}
}
diff --git a/src/openvpn/ssl_common.h b/src/openvpn/ssl_common.h
index 72eb55b..64c1d53 100644
--- a/src/openvpn/ssl_common.h
+++ b/src/openvpn/ssl_common.h
@@ -95,7 +95,10 @@
* completed while still within the
* handshake window. Deferred auth and
* client connect can still be pending. */
-#define S_GENERATED_KEYS 7 /**< The data channel keys have been generated */
+#define S_GENERATED_KEYS 7 /**< The data channel keys have been generated
+ * The TLS session is fully authenticated
+ * when reaching this state. */
+
/* Note that earlier versions also had a S_OP_NORMAL state that was
* virtually identical with S_ACTIVE and the code still assumes everything
* >= S_ACTIVE to be fully operational */
@@ -596,8 +599,8 @@ struct tls_multi
* user/pass authentications in this session.
*/
char *auth_token_initial;
- /**< The first auth-token we sent to a client, for clients that do
- * not update their auth-token (older OpenVPN3 core versions)
+ /**< The first auth-token we sent to a client. We use this to remember
+ * the session ID and initial timestamp when generating new auth-token.
*/
#define AUTH_TOKEN_HMAC_OK (1<<0)
/**< Auth-token sent from client has valid hmac */
diff --git a/src/openvpn/ssl_verify.c b/src/openvpn/ssl_verify.c
index bbb1878..913c956 100644
--- a/src/openvpn/ssl_verify.c
+++ b/src/openvpn/ssl_verify.c
@@ -1512,6 +1512,15 @@ verify_user_pass(struct user_pass *up, struct tls_multi *multi,
if (session->opt->auth_token_generate && is_auth_token(up->password))
{
ks->auth_token_state_flags = verify_auth_token(up, multi, session);
+
+ /* If this is the first time we see an auth-token in this multi session,
+ * save it as initial auth token. This ensures using the
+ * same session ID and initial timestamp in new tokens */
+ if (!multi->auth_token_initial)
+ {
+ multi->auth_token_initial = strdup(up->password);
+ }
+
if (session->opt->auth_token_call_auth)
{
/*
@@ -1631,27 +1640,7 @@ verify_user_pass(struct user_pass *up, struct tls_multi *multi,
*/
generate_auth_token(up, multi);
}
- /*
- * Auth token already sent to client, update auth-token on client.
- * The initial auth-token is sent as part of the push message, for this
- * update we need to schedule an extra push message.
- *
- * Otherwise the auth-token get pushed out as part of the "normal"
- * push-reply
- */
- if (multi->auth_token_initial)
- {
- /*
- * We do not explicitly schedule the sending of the
- * control message here but control message are only
- * postponed when the control channel is not yet fully
- * established and furthermore since this is called in
- * the middle of authentication, there are other messages
- * (new data channel keys) that are sent anyway and will
- * trigger schedueling
- */
- send_push_reply_auth_token(multi);
- }
+
msg(D_HANDSHAKE, "TLS: Username/Password authentication %s for username '%s' %s",
(ks->authenticated == KS_AUTH_DEFERRED) ? "deferred" : "succeeded",
up->username,