summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorGab Virebent <gab@virebent.art>2026-06-26 19:20:00 +0200
committerGab Virebent <gab@virebent.art>2026-06-26 19:20:00 +0200
commitfb48d1308d2f63f8e9b23c5d1d921783fa0dacbe (patch)
treeceecce2f057cd4ea1e6724305fcd52915939c419 /src
parent41c4bfecc770ea7c914e845e4ac3792cb4a473d3 (diff)
downloadgmnisrv-fb48d1308d2f63f8e9b23c5d1d921783fa0dacbe.tar.gz
gmnisrv-fb48d1308d2f63f8e9b23c5d1d921783fa0dacbe.tar.xz
gmnisrv-fb48d1308d2f63f8e9b23c5d1d921783fa0dacbe.zip
Serve CA full chain (Let's Encrypt), TLS 1.3, fix handshake buffer overflowHEADmain
- tls.c/config.h: read full PEM chain into STACK_OF(X509), send via SSL_set1_chain - tls.c: minimum protocol TLS 1.2 -> TLS 1.3 - server.c: drain handshake output via local buffer loop instead of staging into the fixed 4 KB client buffer (a full CA chain overflowed it -> assert) - FORK.md: describe the fork
Diffstat (limited to 'src')
-rw-r--r--src/server.c34
-rw-r--r--src/tls.c23
2 files changed, 40 insertions, 17 deletions
diff --git a/src/server.c b/src/server.c
index 359300a..022aa3d 100644
--- a/src/server.c
+++ b/src/server.c
@@ -376,28 +376,34 @@ client_readable(struct gmnisrv_server *server, struct gmnisrv_client *client)
return CONNECTED;
queue_ssl_write:
- client->bufln = 0;
- client->state = CLIENT_STATE_SSL;
- client->next = CLIENT_STATE_REQUEST;
+ // Flush all pending TLS output (e.g. the ServerHello/Certificate
+ // handshake flight) directly to the socket, draining the write BIO via
+ // a local buffer in a loop. A full CA certificate chain can exceed
+ // sizeof(client->buf), so it must not be staged into the fixed-size
+ // client buffer (that overflowed and aborted on assert).
do {
- assert(client->bufln < sizeof(client->buf));
- r = BIO_read(client->wbio,
- &client->buf[client->bufln],
- sizeof(client->buf) - client->bufln);
- if (r <= 0) {
- if (BIO_should_retry(client->wbio)) {
- continue;
- }
+ r = BIO_read(client->wbio, buf, sizeof(buf));
+ if (r < 0 && !BIO_should_retry(client->wbio)) {
client_error(&client->addr,
"BIO read error: %s",
ERR_error_string(r, NULL));
disconnect_client(server, client);
return DISCONNECTED;
- } else {
- client->bufln += r;
- client->pollfd->events = POLLOUT;
+ }
+ for (int ww = 0; ww < r; ) {
+ int q = write(client->sockfd, &buf[ww], r - ww);
+ if (q < 0) {
+ client_error(&client->addr,
+ "client write: %s",
+ strerror(errno));
+ disconnect_client(server, client);
+ return DISCONNECTED;
+ }
+ ww += q;
}
} while (r > 0);
+ client->state = CLIENT_STATE_REQUEST;
+ client->pollfd->events = POLLIN;
return CONNECTED;
}
diff --git a/src/tls.c b/src/tls.c
index 02d7ab7..8f9bb81 100644
--- a/src/tls.c
+++ b/src/tls.c
@@ -140,13 +140,24 @@ tls_host_init(struct gmnisrv_tls *tlsconf, struct gmnisrv_host *host)
}
X509 *x509 = PEM_read_X509(xf, NULL, NULL, NULL);
- fclose(xf);
if (!x509) {
+ fclose(xf);
server_error("error loading certificate from %s", crtpath);
fclose(kf);
return 1;
}
+ // Read any remaining certificates in the PEM file as the intermediate
+ // chain so CA-signed certs (e.g. Let's Encrypt fullchain.pem) are sent
+ // to the client for proper validation.
+ STACK_OF(X509) *chain = sk_X509_new_null();
+ X509 *ca;
+ while ((ca = PEM_read_X509(xf, NULL, NULL, NULL)) != NULL) {
+ sk_X509_push(chain, ca);
+ }
+ ERR_clear_error();
+ fclose(xf);
+
EVP_PKEY *pkey = PEM_read_PrivateKey(kf, NULL, NULL, NULL);
fclose(kf);
if (!pkey) {
@@ -160,12 +171,15 @@ tls_host_init(struct gmnisrv_tls *tlsconf, struct gmnisrv_host *host)
assert(r == 1);
if (day < 0 || sec < 0) {
server_log("%s certificate is expired", host->hostname);
+ sk_X509_pop_free(chain, X509_free);
goto generate;
}
host->x509 = x509;
host->pkey = pkey;
- server_log("loaded certificate for %s", host->hostname);
+ host->chain = chain;
+ server_log("loaded certificate for %s (%d chain cert(s))",
+ host->hostname, sk_X509_num(chain));
return 0;
generate:
@@ -181,7 +195,7 @@ tls_init(struct gmnisrv_config *conf)
conf->tls.ssl_ctx = SSL_CTX_new(TLS_server_method());
assert(conf->tls.ssl_ctx);
- int r = SSL_CTX_set_min_proto_version(conf->tls.ssl_ctx, TLS1_2_VERSION);
+ int r = SSL_CTX_set_min_proto_version(conf->tls.ssl_ctx, TLS1_3_VERSION);
assert(r == 1);
r = SSL_CTX_set_cipher_list(conf->tls.ssl_ctx,
@@ -238,4 +252,7 @@ tls_set_host(SSL *ssl, struct gmnisrv_host *host)
{
SSL_use_certificate(ssl, host->x509);
SSL_use_PrivateKey(ssl, host->pkey);
+ if (host->chain && sk_X509_num(host->chain) > 0) {
+ SSL_set1_chain(ssl, host->chain);
+ }
}