diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/openvpn/auth_token.c | 91 | ||||
-rw-r--r-- | src/openvpn/auth_token.h | 11 | ||||
-rw-r--r-- | src/openvpn/push.c | 8 | ||||
-rw-r--r-- | src/openvpn/ssl.c | 7 | ||||
-rw-r--r-- | src/openvpn/ssl_common.h | 9 | ||||
-rw-r--r-- | src/openvpn/ssl_verify.c | 31 |
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, |