]> git.kaiwu.me - haproxy.git/commitdiff
BUG/MEDIUM: ssl: don't set a 0 timeout on an expired TLS session
authorWilliam Lallemand <wlallemand@haproxy.com>
Mon, 14 Sep 2026 12:32:05 +0000 (14:32 +0200)
committerWilliam Lallemand <wlallemand@haproxy.com>
Mon, 14 Sep 2026 15:16:45 +0000 (17:16 +0200)
Commit 9f1d590999 ("BUG/MEDIUM: ssl: enforce tune.ssl.lifetime across
TLS1.3 session renewals") clamps a resumed session's timeout to what is
left of its original lifetime. Once that deadline is reached there is
nothing left, and the session was given a timeout of 0.

OpenSSL encodes the session timeout as an optional ASN.1 field which it
omits when the value is zero, and the decoder then substitutes its own
default (3 seconds) instead. construct_stateless_ticket() first measures
the ticket, then re-encodes it, so the two lengths no longer match and
an internal consistency check fails. The resumed handshake is aborted
with an SSL_AD_INTERNAL_ERROR alert (80) instead of gracefully falling
back to a full handshake:

  1.1.1: error:1427D044:SSL routines:construct_stateless_ticket:internal error
  3.0:   error:0A0C0103:SSL routines::internal error

Only OpenSSL 3.1 and older are affected, newer versions don't compare
session ages in whole seconds anymore.

Fix this by never handing out a zero timeout: when nothing is left of
the original lifetime, clear the session's id context instead. The
session then no longer matches any SSL_CTX, so OpenSSL drops it and
performs a full handshake. This also makes the expired session really
disappear from the stateful cache, which a 0 timeout failed to do since
it was read back as the 3 seconds default.

This fixes issue #3499.

This must be backported as far as 2.8, where 9f1d590999 was backported.

src/ssl_sock.c

index 0bc0acf43e030150f37443e402352e54810f2997..772062f3f0b8d88e2d960798479356148174d6c0 100644 (file)
@@ -1771,6 +1771,7 @@ static void ssl_sock_infocbk(const SSL *ssl, int where, int ret)
                                long timeout = SSL_SESSION_get_timeout(sess);
                                long ctx_timeout = SSL_CTX_get_timeout(SSL_get_SSL_CTX(ssl));
                                long elapsed = now - created;
+                               long remaining;
 
                                /* reduce the timeout if it was decreased in the configuration since the session creation */
                                if (ctx_timeout < timeout)
@@ -1778,12 +1779,18 @@ static void ssl_sock_infocbk(const SSL *ssl, int where, int ret)
                                if (elapsed < 0)
                                        elapsed = 0;
 
+                               remaining = timeout - elapsed;
+
 #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);
+                               /* reset the timeout or remove the session */
+                               if (remaining > 0)
+                                       SSL_SESSION_set_timeout(sess, remaining);
+                               else
+                                       SSL_SESSION_set1_id_context(sess, (unsigned char *) "", 0);
 
                                ctx->xprt_st |= SSL_SOCK_SESS_TIMEOUT_SET;
                        }