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?
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");