From: William Lallemand Date: Tue, 18 Aug 2026 11:27:17 +0000 (+0200) Subject: BUG/MEDIUM: ssl: enforce tune.ssl.lifetime across TLS1.3 session renewals X-Git-Tag: v3.5-dev5~23 X-Git-Url: https://git.kaiwu.me/?a=commitdiff_plain;h=9f1d590999;p=haproxy.git BUG/MEDIUM: ssl: enforce tune.ssl.lifetime across TLS1.3 session renewals On TLS 1.3, OpenSSL (and most derivatives) hands out a new session ticket on every resumption, bumping that session's creation time to "now" while leaving its timeout untouched. Each renewal therefore gets a fresh, full "tune.ssl.lifetime" window instead of inheriting what's left of the original one, so a client resuming just before each ticket's window closes can keep a session alive indefinitely, well past its original full handshake's deadline and past any point where a client certificate would need to be re-verified for expiration or revocation. Fix this in ssl_sock_infocbk()'s SSL_CB_ACCEPT_LOOP handler: once per handshake, clamp the session's remaining budget to what's actually left of its ORIGINAL lifetime, by rewriting its creation time to "now" and shrinking its timeout by however much real time has elapsed since it was created (capped to the currently configured "tune.ssl.lifetime", in case it was lowered since the session was first issued). Since OpenSSL's own per-hop duplication only touches the creation time and leaves whatever timeout is already on the object alone, the shrunk timeout carries forward from hop to hop instead of resetting, so the chain still eventually hits its original deadline. This fix does not carry over to BoringSSL/AWS-LC: on those libs, it need a different fix instead, handled separately. Must be backported in every stable branches. --- diff --git a/include/haproxy/ssl_sock-t.h b/include/haproxy/ssl_sock-t.h index b54b5b0cc..6904e187e 100644 --- a/include/haproxy/ssl_sock-t.h +++ b/include/haproxy/ssl_sock-t.h @@ -50,6 +50,7 @@ #define SSL_SOCK_SEND_UNLIMITED 0x00000004 #define SSL_SOCK_RECV_HEARTBEAT 0x00000008 #define SSL_SOCK_SEND_MORE 0x00000010 /* set MSG_MORE at lower levels */ +#define SSL_SOCK_SESS_TIMEOUT_SET 0x00000020 /* TLS1.3 session timeout already set */ /* bits 0xFFFFFF00 are reserved to store verify errors. * The CA en CRT error codes will be stored on 7 bits each diff --git a/src/ssl_sock.c b/src/ssl_sock.c index 628f936ad..582f67d8f 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -1751,6 +1752,43 @@ static void ssl_sock_infocbk(const SSL *ssl, int where, int ret) ctx->xprt_st |= SSL_SOCK_ST_FL_16K_WBFSIZE; } } + +#ifdef TLS1_3_VERSION + /* Reduce TLS1.3 session timeout so it doesn't reset at each new + * resumed connection */ + + if (!(ctx->xprt_st & SSL_SOCK_SESS_TIMEOUT_SET) && + SSL_version(ssl) == TLS1_3_VERSION) { + SSL_SESSION *sess = SSL_get0_session(ssl); + + if (sess) { + time_t now = date.tv_sec; +#if HA_OPENSSL_VERSION_NUMBER >= 0x30400000L + time_t created = SSL_SESSION_get_time_ex(sess); +#else + time_t created = SSL_SESSION_get_time(sess); +#endif + long timeout = SSL_SESSION_get_timeout(sess); + long ctx_timeout = SSL_CTX_get_timeout(SSL_get_SSL_CTX(ssl)); + long elapsed = now - created; + + /* reduce the timeout if it was decreased in the configuration since the session creation */ + if (ctx_timeout < timeout) + timeout = ctx_timeout; + if (elapsed < 0) + elapsed = 0; + +#if HA_OPENSSL_VERSION_NUMBER >= 0x30400000L + SSL_SESSION_set_time_ex(sess, now); +#else + SSL_SESSION_set_time(sess, now); +#endif + SSL_SESSION_set_timeout(sess, elapsed < timeout ? timeout - elapsed : 0); + + ctx->xprt_st |= SSL_SOCK_SESS_TIMEOUT_SET; + } + } +#endif } }