]> git.kaiwu.me - haproxy.git/commitdiff
BUG/MEDIUM: ssl: enforce tune.ssl.lifetime across TLS1.3 session renewals
authorWilliam Lallemand <wlallemand@haproxy.com>
Tue, 18 Aug 2026 11:27:17 +0000 (13:27 +0200)
committerWilliam Lallemand <wlallemand@haproxy.com>
Tue, 18 Aug 2026 11:37:54 +0000 (13:37 +0200)
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.

include/haproxy/ssl_sock-t.h
src/ssl_sock.c

index b54b5b0ccf2f10bf9a998e658cb4120a798f7b40..6904e187ecae4193b8c28f55727811491a5a47dd 100644 (file)
@@ -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
index 628f936ad665c6e995065592e48f6d788c8cbd1b..582f67d8f2fa965419709bf5ee99c503307638d0 100644 (file)
@@ -51,6 +51,7 @@
 #include <haproxy/channel.h>
 #include <haproxy/chunk.h>
 #include <haproxy/cli.h>
+#include <haproxy/clock.h>
 #include <haproxy/connection.h>
 #include <haproxy/dynbuf.h>
 #include <haproxy/errors.h>
@@ -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
        }
 }