Hi,

Thanks to James for the report and Rafael for the fix.

I independently reproduced this on OpenBSD 8.0 arm64 and amd64 with
ticket lifetime 300, using both aliases and multiple TLS virtual hosts
on a shared listener. No client traffic was needed: unpatched httpd
terminated after approximately 75 seconds.

A local release patch containing the same functional change as
httpd.c revision 1.90 passed the following tests on both architectures:

- 300-second runs with aliases and shared-listener virtual hosts;
- TLS 1.2 session resumption across successive ticket key rotations;
- configuration reload with the virtual host order reversed, followed
  by successful resumption across another rotation;
- IPv4 and IPv6 with prefork 3, plus TLS 1.3 handshake checks;
- full system reboots with the patched boot-time relink kits installed.

The change was also built locally and tested on two amd64 production
systems upgraded regularly over several years, most recently from
7.9 to 8.0. Both continued working after full reboots with lifetime 300.
The documented post-reboot checks cover 303 seconds on one host and
240 seconds with the same parent PID on the other. These are short
observations under live traffic, rather than long-term soak tests.

One minor point: the diagnostic still says "invalid sever id" in
server.c revision 1.143. As James noted, this should be "server".
Our local test patch also included that spelling correction.

Thanks again.

Michal

On 2026-10-21 20:46, Kirill A. Korinsky wrote:
On Wed, 21 Oct 2026 16:09:28 +0200,
Rafael Sadowski <[email protected]> wrote:
On Wed Oct 21, 2026 at 09:24:42AM +0200, Rafael Sadowski wrote:
On Tue Oct 20, 2026 at 05:35:53PM -0700, James J. Lippard wrote:
I have multiple servers listening on the same host and port and using
"ticket lifetime default" in their tls config blocks.  Now httpd
crashes out on the first ticket rekey with:

httpd[6452]: fatal in server: proc_dispatch: server 2 got invalid imsg 23 
peerid -1 from parent 1

The parent starts a rekey timer for every non-location TLS server
block and sends IMSG_TLSTICKET_REKEY with that block's id. In the
server processes, blocks sharing a socket are merged, the first
becomes the struct server and the rest are attached as srv_hosts.
server_byid() only searches the top-level servers, so the rekey for
any merged block finds nothing.  In OpenBSD 7.9 those misses were
ignored:

                srv = server_byid(key.tt_id);
                if (srv) {
                        tls_config_add_ticket_key(...);
                }

but in 8.0 it returns -1 which proc_dispatch() reports as "invalid imsg"
and turns into fatalx().  Using -vv the line before the fatal is
the "invalid sever id" (typo in source) message; here's a patch that
makes it non-fatal again":

Index: usr.sbin/httpd/server.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/server.c,v
diff -u -p -r1.143 server.c
--- usr.sbin/httpd/server.c     15 Oct 2026 10:59:00 -0000      1.143
+++ usr.sbin/httpd/server.c     20 Oct 2026 00:00:00 -0000
@@ -1425,8 +1425,14 @@ server_dispatch_parent(int fd, struct pr
                memcpy(&key, imsg->data, sizeof(key));
                /* apply to the right server */
                if ((srv = server_byid(key.tt_id)) == NULL) {
-                       log_debug("%s: invalid sever id", __func__);
-                       return (-1);
+                       /*
+                        * Not a listening server, but a name-based host
+                        * merged onto another server's socket; their shared
+                        * TLS context is rekeyed via the listening server.
+                        */
+                       log_debug("%s: ignoring rekey for server id %u",
+                           __func__, key.tt_id);
+                       break;
                }
                if (tls_config_add_ticket_key(srv->srv_tls_config,
                    key.tt_keyrev, key.tt_key, sizeof(key.tt_key)) == -1) {
Hi James,

thanks for the report and the patch.

I think the stricter imsg handling in 8.0 found a real problem. The TLS
config is per socket, not per server block. Only the first server on a
socket sets up the tls_config. For the other virtual servers only the
cert/key (for SNI) is used, so their "ticket lifetime" has no effect.

This also means: if the first server has no ticket lifetime but a later
one has, tickets are disabled for the whole socket, without any warning.
Welcome to the httpd.conf/parse.y madness where everything is valid!

So I would do this in two steps:

1) Replace the return (-1) with your patch, so httpd does not crash.

2) Make the config stricter: warn (or fail) in parse.y if servers on
    the same socket have different TLS settings, start rekey timers only
    for the listening server, and document in httpd.conf(5) that these
    TLS options apply per socket.

kirill@ are you ok with this?

Rafael

"Small" correction to my last mail: parse.y already rejects different
ticket settings on the same address/port, so no new check is needed there.
I blame this one too often!

I think the better fix is in the parent. It starts a rekey timer for
every TLS server, but only the first server on an address/port gets a
struct server in the server processes. The diff below starts the timer
ONLY for that one.

Tested with two TLS servers on the same socket: the "ignoring rekey"
messages from James' diff are gone. What I believe is the right
solution.

It annoys me that this bug made it into version 8.0, but the problem
actually reveals the real flaw.

OK?

ok Kirill@

diff --git a/httpd.c b/httpd.c
index abfbb5c..6ca2e24 100644
--- a/httpd.c
+++ b/httpd.c
@@ -298,7 +298,8 @@ parent_configure(struct httpd *env)
                        continue;
                /* start the rekey of the tls ticket keys */
                if (srv->srv_conf.flags & SRVFLAG_TLS &&
-                   srv->srv_conf.tls_ticket_lifetime)
+                   srv->srv_conf.tls_ticket_lifetime &&
+                   server_match(srv, 0) == srv)
                        parent_tls_ticket_rekey_start(srv);
                if (config_setserver(env, srv) == -1)
                        fatal("send server");


Reply via email to