Hello there, I was adding a site to httpd when I experienced a bug that
would cause httpd to crash whenever someone accessed the site while the
TLS cert of the first server entry was missing.
Steps to reproduce: (httpd.conf provided at bottom of email)
- Have a server entry at the top of the httpd.conf file with the TLS
certificate missing.
- Second server entry below the first one with a valid TLS certificate
and key.
- Visit the server on HTTPS.
The crash occurs because the server is listening on port 443 without
setting up the TLS context first.
The cleanest fix is to move the handling of missing certificates to the
configuration parser.
See attachment for the diff to fix this bug (this also reverts r1.117 of
server.c which is no longer neccessary).
Thanks, Joshua Sing.
[email protected]
# $OpenBSD: httpd.conf,v 1.20 2018/06/13 15:08:24 reyk Exp $
server "example.com" {
listen on * tls port 443
tls {
certificate "/etc/ssl/example2.com.fullchain.pem" # missing
key "/etc/ssl/private/example2.com.key" # exists
}
location "/pub/*" {
directory auto index
}
location "/.well-known/acme-challenge/*" {
root "/acme"
request strip 2
}
}
server "example2.com" {
listen on * tls port 443
tls {
certificate "/etc/ssl/example.com.fullchain.pem" # exists
key "/etc/ssl/private/example.com.key" # exists
}
location "/pub/*" {
directory auto index
}
location "/.well-known/acme-challenge/*" {
root "/acme"
request strip 2
}
}
Index: parse.y
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/parse.y,v
retrieving revision 1.114
diff -u -p -r1.114 parse.y
--- parse.y 9 Feb 2020 09:44:04 -0000 1.114
+++ parse.y 1 Oct 2020 15:42:23 -0000
@@ -345,10 +345,17 @@ server : SERVER optmatch STRING {
YYERROR;
}
- if (server_tls_load_keypair(srv) == -1)
+ if (server_tls_load_keypair(srv) == -1) {
+ /* Soft fail as there may be no certificate. */
log_warnx("%s:%d: server \"%s\": failed to "
"load public/private keys", file->name,
yylval.lineno, srv->srv_conf.name);
+ serverconfig_free(srv_conf);
+ srv_conf = NULL;
+ free(srv);
+ srv = NULL;
+ break;
+ }
if (server_tls_load_ca(srv) == -1) {
yyerror("server \"%s\": failed to load "
Index: server.c
===================================================================
RCS file: /cvs/src/usr.sbin/httpd/server.c,v
retrieving revision 1.120
diff -u -p -r1.120 server.c
--- server.c 14 Oct 2019 11:07:08 -0000 1.120
+++ server.c 1 Oct 2020 15:42:24 -0000
@@ -119,13 +119,6 @@ server_privinit(struct server *srv)
}
/* Open listening socket in the privileged process */
- if ((srv->srv_conf.flags & SRVFLAG_TLS) && srv->srv_conf.tls_cert ==
- NULL) {
- /* soft fail if cert is not there yet */
- srv->srv_s = -1;
- return (0);
- }
-
if ((srv->srv_s = server_socket_listen(&srv->srv_conf.ss,
srv->srv_conf.port, &srv->srv_conf)) == -1)
return (-1);
@@ -257,10 +250,6 @@ server_tls_init(struct server *srv)
if ((srv->srv_conf.flags & SRVFLAG_TLS) == 0)
return (0);
- if (srv->srv_conf.tls_cert == NULL)
- /* soft fail if cert is not there yet */
- return (0);
-
log_debug("%s: setting up tls for %s", __func__, srv->srv_conf.name);
if (tls_init() != 0) {
@@ -1160,7 +1149,7 @@ server_accept(int fd, short event, void
if (srv->srv_conf.flags & SRVFLAG_TLS) {
if (tls_accept_socket(srv->srv_tls_ctx, &clt->clt_tls_ctx,
clt->clt_s) != 0) {
- server_close(clt, "failed to setup tls context");
+ server_close(clt, "failed to accept tls socket");
return;
}
event_again(&clt->clt_ev, clt->clt_s, EV_TIMEOUT|EV_READ,