tlslib, worker: remove dead code orphaned by the listen-clear-file removal

Commit 5cf457b4 ("Removed the listen-clear-file config option", Dec
2020) deleted the only code path that could ever construct a
SOCK_TYPE_UNIX worker connection, but left every branch that handled
it in place. Since then ws->session has been unconditionally non-NULL
and ws->conn_type unconditionally not SOCK_TYPE_UNIX for every worker,
making all of the following unreachable:

 - tlslib.c: recv_remaining(), _cstp_recv_packet() (the non-TLS CSTP
   reassembly path hardened in the previous commit), and
   tls_has_session_cert() (zero callers) deleted outright; cstp_cork/
   cstp_uncork/cstp_send/cstp_recv_packet/cstp_recv/cstp_close/
   cstp_fatal_close collapsed to their TLS-only body.
 - worker-http.c, worker-auth.c, worker-vpn.c, main.c: dead
   ws->session == NULL / ws->conn_type == SOCK_TYPE_UNIX branches
   removed or simplified to their live half.
 - worker-proxyproto.c: parse_ssl_tlvs() and its TLV structs/macros
   removed. This proxy-protocol SSL-CN extraction was itself only
   ever invoked from the same dead SOCK_TYPE_UNIX branch, so it has
   been as unreachable as the rest since 2020 despite a recent,
   otherwise-correct bug fix.
 - tests/cstp-recv.c deleted (exercised only the removed reassembly
   path); tests/proxyproto-v2.c trimmed to the still-live IPv6
   address-parsing regression test.
 - doc/sample.config: dropped the stale "TCP or UNIX socket" wording
   for listen-proxy-proto left over from the same 2020 removal; only
   a TCP socket is ever listened on for the proxy protocol now.

Narrows the worker's attack surface to the code paths a client can
actually reach, and removes a source of wasted maintenance effort.

Signed-off-by: Nikos Mavrogiannopoulos <n.mavrogiannopoulos@gmail.com>
This commit is contained in:
Nikos Mavrogiannopoulos
2026-07-28 08:00:06 +02:00
parent 879f723953
commit 76987ccb82
14 changed files with 116 additions and 886 deletions
-188
View File
@@ -1,188 +0,0 @@
/*
* Copyright (C) 2017 Nikos Mavrogiannopoulos
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <gnutls/gnutls.h>
/* Unit test for _cstp_recv_packet(). I checks whether
* CSTP packets are received and decoded as expected.
*/
static unsigned int verbose;
#define UNDER_TEST
#define force_write write
#include "../src/tlslib.c"
int get_cert_names(worker_st *ws, const gnutls_datum_t *raw)
{
return 0;
}
#define MAX_SIZE 256
#define ITERATIONS 1024
void writer(int fd)
{
unsigned int size, i, j;
unsigned char buf[MAX_SIZE + 8];
memset(buf, 0, sizeof(buf));
for (i = 0; i < ITERATIONS; i++) {
assert(gnutls_rnd(GNUTLS_RND_NONCE, &size,
sizeof(unsigned int)) >= 0);
size %= MAX_SIZE;
size++; /* non-zero */
buf[4] = (size >> 8) & 0xff;
buf[5] = size & 0xff;
size += 8;
if (verbose)
fprintf(stderr, "sending %d\n", size);
for (j = 0; j < size; j++) { /* use multiple writes */
assert(write(fd, buf + j, 1) == 1);
}
}
}
void receiver(int fd)
{
worker_st ws = { 0 };
unsigned char buf[MAX_SIZE * 3];
int ret;
unsigned int i;
ws.conn_fd = fd;
for (i = 0; i < ITERATIONS; i++) {
ret = _cstp_recv_packet(&ws, buf, sizeof(buf));
if (verbose)
fprintf(stderr, "received %d\n", ret);
assert(ret > 0);
}
}
/* Writes a CSTP header announcing a BODY_SIZE-byte body, but only
* BODY_SIZE/2 bytes of body, then closes the socket - simulating a
* proxy connection dropped mid-packet. A correct _cstp_recv_packet()
* must report this as an error, not as a successful, fully-populated
* packet built from a truncated buffer. */
#define NEG_BODY_SIZE 64
void neg_writer(int fd)
{
unsigned char buf[8 + NEG_BODY_SIZE] = { 0 };
buf[4] = (NEG_BODY_SIZE >> 8) & 0xff;
buf[5] = NEG_BODY_SIZE & 0xff;
assert(write(fd, buf, 8 + NEG_BODY_SIZE / 2) == 8 + NEG_BODY_SIZE / 2);
close(fd);
}
void neg_receiver(int fd)
{
worker_st ws = { 0 };
unsigned char buf[8 + NEG_BODY_SIZE];
int ret;
ws.conn_fd = fd;
ret = _cstp_recv_packet(&ws, buf, sizeof(buf));
if (verbose)
fprintf(stderr, "negative test received %d\n", ret);
if (ret > 0) {
fprintf(stderr,
"FAIL: expected error on truncated packet, got %d\n",
ret);
exit(1);
}
}
void run_negative_test(void)
{
int sockets[2];
pid_t child;
int status = 0;
assert(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) >= 0);
child = fork();
assert(child >= 0);
if (child) {
close(sockets[1]);
neg_receiver(sockets[0]);
wait(&status);
if (WEXITSTATUS(status) != 0) {
fprintf(stderr, "negative test child failed %d!\n",
(int)WEXITSTATUS(status));
exit(1);
}
} else {
close(sockets[0]);
neg_writer(sockets[1]);
exit(0);
}
}
int main(int argc, char *argv[])
{
int sockets[2];
pid_t child;
int status = 0;
if (argc > 1)
verbose = 1;
assert(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) >= 0);
child = fork();
assert(child >= 0);
if (child) {
close(sockets[1]);
receiver(sockets[0]);
wait(&status);
if (WEXITSTATUS(status) != 0) {
fprintf(stderr, "child failed %d!\n",
(int)WEXITSTATUS(status));
exit(1);
}
} else {
close(sockets[0]);
writer(sockets[1]);
return 0;
}
run_negative_test();
return 0;
}
-7
View File
@@ -60,13 +60,6 @@ foreach name, cfg : unit_tests
test(name, exe, env: test_env, workdir: test_workdir, timeout: cfg['timeout'])
endforeach
# cstp-recv needs gnutls
cstp_recv_exe = executable('cstp-recv', 'cstp-recv.c',
dependencies: test_base_deps + [gnutls_dep],
include_directories: test_inc,
)
test('cstp-recv', cstp_recv_exe, env: test_env, workdir: test_workdir)
# config-inherit: exercises vhost config inheritance (#705).
# Textually includes config.c and its dependencies so that the static
# parse_cfg_file() is accessible from the test. Requires gnutls (for
+11 -217
View File
@@ -15,10 +15,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* Unit test for proxy protocol v2 TLV parsing, specifically verifying that
* PP2_SUBTYPE_SSL_CN (0x22) is correctly parsed as a sub-TLV inside the
* PP2_TYPE_SSL body rather than as a top-level TLV.
*/
/* Unit test for proxy protocol v2 IPv6 address block parsing. */
#include <config.h>
#include <stdio.h>
@@ -38,98 +35,6 @@
static const uint8_t PP2_SIG[] =
"\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A";
/*
* Build a proxy protocol v2 packet with an IPv4 address block and an optional
* PP2_TYPE_SSL TLV (with PP2_SUBTYPE_SSL_CN sub-TLV) into buf[].
* Returns the total number of bytes written.
*
* ssl_client_flags: PP2_CLIENT_* flags (0 = omit SSL TLV entirely)
* ssl_verify: value for the verify field (0 = certificate verified)
* cn: common name string, or NULL to omit the CN sub-TLV
*/
static size_t build_v2_packet(uint8_t *buf, size_t bufsz,
uint8_t ssl_client_flags, uint32_t ssl_verify,
const char *cn)
{
size_t pos = 0;
/* --- fixed 16-byte header --- */
/* signature */
memcpy(buf + pos, PP2_SIG, 12);
pos += 12;
/* ver_cmd: version 2, PROXY command */
buf[pos++] = 0x21;
/* family: AF_INET (0x1) | TCP (0x1) */
buf[pos++] = 0x11;
/* len placeholder (2 bytes, filled in below) */
size_t len_offset = pos;
buf[pos++] = 0;
buf[pos++] = 0;
/* --- payload starts here --- */
size_t payload_start = pos;
/* IPv4 address block (12 bytes):
* src IP 1.2.3.4 (4 bytes)
* dst IP 5.6.7.8 (4 bytes)
* src port 100 (2 bytes, network order)
* dst port 443 (2 bytes, network order)
*/
buf[pos++] = 1;
buf[pos++] = 2;
buf[pos++] = 3;
buf[pos++] = 4;
buf[pos++] = 5;
buf[pos++] = 6;
buf[pos++] = 7;
buf[pos++] = 8;
buf[pos++] = 0x00;
buf[pos++] = 0x64; /* src port 100 */
buf[pos++] = 0x01;
buf[pos++] = 0xBB; /* dst port 443 */
if (ssl_client_flags != 0) {
/* PP2_TYPE_SSL TLV header — length filled in below */
size_t ssl_tlv_hdr = pos;
buf[pos++] = 0x20; /* PP2_TYPE_SSL */
size_t ssl_len_offset = pos;
buf[pos++] = 0;
buf[pos++] = 0; /* length placeholder */
/* pp2_tlv_ssl fixed part (5 bytes):
* client (1 byte) + verify (4 bytes, network order)
*/
buf[pos++] = ssl_client_flags;
buf[pos++] = (ssl_verify >> 24) & 0xff;
buf[pos++] = (ssl_verify >> 16) & 0xff;
buf[pos++] = (ssl_verify >> 8) & 0xff;
buf[pos++] = (ssl_verify) & 0xff;
if (cn != NULL) {
size_t cn_len = strlen(cn);
/* PP2_SUBTYPE_SSL_CN sub-TLV */
buf[pos++] = 0x22; /* PP2_TYPE_SSL_CN */
buf[pos++] = (cn_len >> 8) & 0xff;
buf[pos++] = cn_len & 0xff;
memcpy(buf + pos, cn, cn_len);
pos += cn_len;
}
/* back-fill SSL TLV length (body after the 3-byte TLV header) */
uint16_t ssl_body_len = (uint16_t)(pos - ssl_tlv_hdr - 3);
buf[ssl_len_offset] = (ssl_body_len >> 8) & 0xff;
buf[ssl_len_offset + 1] = ssl_body_len & 0xff;
}
/* back-fill payload length in fixed header */
uint16_t payload_len = (uint16_t)(pos - payload_start);
buf[len_offset] = (payload_len >> 8) & 0xff;
buf[len_offset + 1] = payload_len & 0xff;
assert(pos <= bufsz);
return pos;
}
/*
* Build a proxy protocol v2 packet with an IPv6 address block into buf[].
* Returns the total number of bytes written.
@@ -215,117 +120,7 @@ int main(void)
int ret;
/* ------------------------------------------------------------------ */
/* Test 1: valid SSL TLV with CN sub-TLV — cert_auth_ok and username */
/* ------------------------------------------------------------------ */
memset(&ws, 0, sizeof(ws));
ws.conn_type = SOCK_TYPE_UNIX; /* triggers TLV parsing */
/*
* PP2_CLIENT_SSL (0x01) | PP2_CLIENT_CERT_SESS (0x04) = 0x05
* verify = 0 => cert verified
* CN = "testuser"
*/
pkt_len = build_v2_packet(pkt, sizeof(pkt), 0x05, 0, "testuser");
ret = run_parse(&ws, pkt, pkt_len);
if (ret != 0) {
fprintf(stderr, "Test 1: parse failed (%d)\n", ret);
return 1;
}
if (!ws.cert_auth_ok) {
fprintf(stderr, "Test 1: cert_auth_ok not set\n");
return 1;
}
if (strcmp(ws.cert_username, "testuser") != 0) {
fprintf(stderr,
"Test 1: cert_username='%s', expected 'testuser'\n",
ws.cert_username);
return 1;
}
/* ------------------------------------------------------------------ */
/* Test 2: verify != 0 — cert NOT accepted, no CN extracted */
/* ------------------------------------------------------------------ */
memset(&ws, 0, sizeof(ws));
ws.conn_type = SOCK_TYPE_UNIX;
pkt_len = build_v2_packet(pkt, sizeof(pkt), 0x05, 1 /* verify!=0 */,
"testuser");
ret = run_parse(&ws, pkt, pkt_len);
if (ret != 0) {
fprintf(stderr, "Test 2: parse failed (%d)\n", ret);
return 1;
}
if (ws.cert_auth_ok) {
fprintf(stderr,
"Test 2: cert_auth_ok should NOT be set when verify!=0\n");
return 1;
}
if (ws.cert_username[0] != '\0') {
fprintf(stderr, "Test 2: cert_username should be empty\n");
return 1;
}
/* ------------------------------------------------------------------ */
/* Test 3: PP2_CLIENT_CERT_SESS not set — no cert auth */
/* ------------------------------------------------------------------ */
memset(&ws, 0, sizeof(ws));
ws.conn_type = SOCK_TYPE_UNIX;
/* only PP2_CLIENT_SSL, no PP2_CLIENT_CERT_SESS */
pkt_len = build_v2_packet(pkt, sizeof(pkt), 0x01, 0, "testuser");
ret = run_parse(&ws, pkt, pkt_len);
if (ret != 0) {
fprintf(stderr, "Test 3: parse failed (%d)\n", ret);
return 1;
}
if (ws.cert_auth_ok) {
fprintf(stderr,
"Test 3: cert_auth_ok should NOT be set without CERT_SESS\n");
return 1;
}
/* ------------------------------------------------------------------ */
/* Test 4: valid cert but no CN sub-TLV — cert_auth_ok set, name empty */
/* ------------------------------------------------------------------ */
memset(&ws, 0, sizeof(ws));
ws.conn_type = SOCK_TYPE_UNIX;
pkt_len = build_v2_packet(pkt, sizeof(pkt), 0x05, 0, NULL /* no CN */);
ret = run_parse(&ws, pkt, pkt_len);
if (ret != 0) {
fprintf(stderr, "Test 4: parse failed (%d)\n", ret);
return 1;
}
if (!ws.cert_auth_ok) {
fprintf(stderr, "Test 4: cert_auth_ok not set\n");
return 1;
}
if (ws.cert_username[0] != '\0') {
fprintf(stderr,
"Test 4: cert_username should be empty when no CN sub-TLV\n");
return 1;
}
/* ------------------------------------------------------------------ */
/* Test 5: no SSL TLV at all — conn_type TCP, TLVs not parsed */
/* ------------------------------------------------------------------ */
memset(&ws, 0, sizeof(ws));
ws.conn_type = SOCK_TYPE_TCP; /* TLV parsing skipped */
pkt_len = build_v2_packet(pkt, sizeof(pkt), 0x05, 0, "testuser");
ret = run_parse(&ws, pkt, pkt_len);
if (ret != 0) {
fprintf(stderr, "Test 5: parse failed (%d)\n", ret);
return 1;
}
if (ws.cert_auth_ok) {
fprintf(stderr,
"Test 5: cert_auth_ok should NOT be set for TCP conn\n");
return 1;
}
/* ------------------------------------------------------------------ */
/* Test 6: IPv6 proxy protocol v2 — both remote and local addr correct */
/* IPv6 proxy protocol v2 — both remote and local addr correct */
/* */
/* Reproducer for the bug where sa->sin6_family was assigned before sa */
/* was redirected to &ws->our_addr, leaving our_addr.sin6_family == 0. */
@@ -344,17 +139,17 @@ int main(void)
&dst6, 443);
ret = run_parse(&ws, pkt, pkt_len);
if (ret != 0) {
fprintf(stderr, "Test 6: parse failed (%d)\n", ret);
fprintf(stderr, "parse failed (%d)\n", ret);
return 1;
}
if (ws.remote_addr_len != sizeof(struct sockaddr_in6)) {
fprintf(stderr, "Test 6: remote_addr_len wrong (%u)\n",
fprintf(stderr, "remote_addr_len wrong (%u)\n",
ws.remote_addr_len);
return 1;
}
if (ws.our_addr_len != sizeof(struct sockaddr_in6)) {
fprintf(stderr, "Test 6: our_addr_len wrong (%u)\n",
fprintf(stderr, "our_addr_len wrong (%u)\n",
ws.our_addr_len);
return 1;
}
@@ -363,8 +158,7 @@ int main(void)
loc6 = (void *)&ws.our_addr;
if (rem6->sin6_family != AF_INET6) {
fprintf(stderr,
"Test 6: remote sin6_family wrong: %d\n",
fprintf(stderr, "remote sin6_family wrong: %d\n",
rem6->sin6_family);
return 1;
}
@@ -372,26 +166,26 @@ int main(void)
* remote_addr a second time instead of to our_addr. */
if (loc6->sin6_family != AF_INET6) {
fprintf(stderr,
"Test 6: local sin6_family wrong: %d (expected %d)\n",
"local sin6_family wrong: %d (expected %d)\n",
loc6->sin6_family, AF_INET6);
return 1;
}
if (memcmp(&rem6->sin6_addr, &src6, 16) != 0) {
fprintf(stderr, "Test 6: remote address mismatch\n");
fprintf(stderr, "remote address mismatch\n");
return 1;
}
if (memcmp(&loc6->sin6_addr, &dst6, 16) != 0) {
fprintf(stderr, "Test 6: local address mismatch\n");
fprintf(stderr, "local address mismatch\n");
return 1;
}
if (ntohs(rem6->sin6_port) != 1234) {
fprintf(stderr, "Test 6: remote port wrong: %u\n",
fprintf(stderr, "remote port wrong: %u\n",
ntohs(rem6->sin6_port));
return 1;
}
if (ntohs(loc6->sin6_port) != 443) {
fprintf(stderr, "Test 6: local port wrong: %u\n",
fprintf(stderr, "local port wrong: %u\n",
ntohs(loc6->sin6_port));
return 1;
}