You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Harden SolidSyslogTlsStream beyond the walking-skeleton defaults: make hostname/SAN verification explicit and failure-checked, set an explicit cipher-suite floor, plug the mid-Open resource leaks that the walking skeleton knowingly left behind, and stand up an in-process integration harness so SL4 failure modes can be unit-tested against real libssl.
Problem
S03.07 (#165) landed an OpenSSL-backed TLS stream that relies on OpenSSL defaults for hostname verification and cipher selection. The walking-skeleton scope explicitly said "Explicit validation, hostname/SAN, cipher hardening … arrive in later stories." Review of PR #170 surfaced concrete items that belong here:
Unchecked hostname setup return values — MITM risk.SSL_set_tlsext_host_name() and SSL_set1_host() are called without checking their return values; if either fails the handshake proceeds without hostname verification. See PR #170 comment.
BIO_METHOD lifecycle.CreateTransportBio() allocates a fresh BIO_METHOD via BIO_meth_new() that is never freed. Either hoist to module-scope one-time init or pair with BIO_meth_free() in Destroy. SSL is also not nulled in Close; Destroy does not fully clean up. Flagged in PR #170 review.
Mid-Open error cleanup. Already acknowledged during S03.07 review — if CreateSslContext, SSL_new, SSL_set_bio, or SSL_set1_host fails partway through Open, allocated SSL / SSL_CTX / BIO are leaked.
OpenSslFake BIO data aliasing.BIO_set_data / BIO_get_data currently share a single global lastSetDataArg across all BIOs. Fine for the walking skeleton (one read BIO + one write BIO, same transport payload) but any test that needs two BIOs with different payloads will alias silently. Flagged in PR #170 review.
Unit tests cannot reach libssl-internal failure modes.Tests/SolidSyslogTlsStreamTest.cpp runs against OpenSslFake, which can observe API shape (e.g. "was SSL_set1_host called?") but cannot model expired certs, wrong CN/SAN, wrong CA, or cipher-list rejection — the very failure modes SL4 verification hinges on. BDD can catch these in principle but is slow (~25 s full run) and awkward to drive into specific failure states (requires misconfiguring syslog-ng or rotating certs live).
Scope
Certificate + hostname validation
Check return values of every OpenSSL call in Open; on any failure, release what was allocated so far and fail Open cleanly.
Treat SNI / hostname setup failure as Open failure — no silent bypass.
Explicit SSL_VERIFY_PEER; confirm whether SSL_VERIFY_FAIL_IF_NO_PEER_CERT applies (client-side default is adequate but pin it).
SAN-aware hostname verification where OpenSSL defaults need supplementing for SL4.
Cipher hardening
TLS 1.2 floor (confirm OpenSSL default matches on every target).
Explicit cipher list matching IEC 62443 SL4 expectations; ADR if deviating from OpenSSL defaults.
Resource lifecycle
BIO_meth_free wired — either module-scope one-time init or per-Open pair.
SSL nulled in Close; full cleanup in Destroy (CTX, SSL, BIO_METHOD).
In-process TLS integration harness (scaffolding)
Stand up a real-libssl loopback harness before the cert-validation tests land, so failure-mode coverage can run in the unit test suite rather than BDD:
BIO_new_bio_pair creates two connected in-memory BIOs.
SolidSyslogTlsStream runs as client on one side; the test stands up a real libssl server (SSL_CTX + cert-generation helpers) on the other.
Full handshake in-process — no sockets, no containers.
Per-test server rigging lets each scenario supply its own certs/ciphers: expired cert, wrong CN/SAN, wrong CA, missing client cert (for S03.09), unsupported cipher.
Scope note: the harness is explicitly OpenSSL-specific — it exercises Platform/OpenSsl/. If future epics add mbedTLS / BearSSL / wolfSSL, each gets its own harness under its own Platform tier. The harness must not try to be portable.
Cost estimate: ~150 lines of scaffolding (server-side SSL_CTX, cert-generation helpers, BIO-pair plumbing) for the first test; subsequent tests are one function call.
Test support
OpenSslFake.c — per-BIO BIO_set_data / BIO_get_data storage so existing fake-based tests can exercise multiple BIOs without aliasing.
Acceptance
Every OpenSSL call in Open is return-value-checked; partial-init failures release all resources.
Hostname-setup failure (SSL_set_tlsext_host_name or SSL_set1_host) blocks the handshake.
BIO_METHOD is freed (no leak at process exit).
Close nulls the SSL pointer; Destroy performs full cleanup.
OpenSslFake stores BIO data per-instance.
In-process TLS integration harness lives under Tests/ (landing as the first commit of the story), runs against real libssl, and covers at least: expired server cert, wrong CN/SAN, wrong CA, unsupported cipher.
All presets green; coverage stays at 100% on Platform/OpenSsl.
IEC 62443 SL4 mapping for hostname verification + cipher list captured in docs/iec62443.md.
Out of scope
Mutual TLS (client certs) — S03.09. (Harness scaffolded here will be reused.)
Certificate rotation — S03.10.
CRL / OCSP revocation — deferred; OpenSSL defaults apply, ADR under this story if non-default.
Parent epic: #5
Goal
Harden
SolidSyslogTlsStreambeyond the walking-skeleton defaults: make hostname/SAN verification explicit and failure-checked, set an explicit cipher-suite floor, plug the mid-Open resource leaks that the walking skeleton knowingly left behind, and stand up an in-process integration harness so SL4 failure modes can be unit-tested against real libssl.Problem
S03.07 (#165) landed an OpenSSL-backed TLS stream that relies on OpenSSL defaults for hostname verification and cipher selection. The walking-skeleton scope explicitly said "Explicit validation, hostname/SAN, cipher hardening … arrive in later stories." Review of PR #170 surfaced concrete items that belong here:
SSL_set_tlsext_host_name()andSSL_set1_host()are called without checking their return values; if either fails the handshake proceeds without hostname verification. See PR #170 comment.CreateTransportBio()allocates a freshBIO_METHODviaBIO_meth_new()that is never freed. Either hoist to module-scope one-time init or pair withBIO_meth_free()inDestroy.SSLis also not nulled inClose;Destroydoes not fully clean up. Flagged in PR #170 review.CreateSslContext,SSL_new,SSL_set_bio, orSSL_set1_hostfails partway throughOpen, allocatedSSL/SSL_CTX/BIOare leaked.OpenSslFakeBIO data aliasing.BIO_set_data/BIO_get_datacurrently share a single globallastSetDataArgacross all BIOs. Fine for the walking skeleton (one read BIO + one write BIO, same transport payload) but any test that needs two BIOs with different payloads will alias silently. Flagged in PR #170 review.Tests/SolidSyslogTlsStreamTest.cppruns againstOpenSslFake, which can observe API shape (e.g. "wasSSL_set1_hostcalled?") but cannot model expired certs, wrong CN/SAN, wrong CA, or cipher-list rejection — the very failure modes SL4 verification hinges on. BDD can catch these in principle but is slow (~25 s full run) and awkward to drive into specific failure states (requires misconfiguring syslog-ng or rotating certs live).Scope
Certificate + hostname validation
Open; on any failure, release what was allocated so far and failOpencleanly.Openfailure — no silent bypass.SSL_VERIFY_PEER; confirm whetherSSL_VERIFY_FAIL_IF_NO_PEER_CERTapplies (client-side default is adequate but pin it).Cipher hardening
Resource lifecycle
BIO_meth_freewired — either module-scope one-time init or per-Openpair.SSLnulled inClose; full cleanup inDestroy(CTX, SSL, BIO_METHOD).In-process TLS integration harness (scaffolding)
Stand up a real-libssl loopback harness before the cert-validation tests land, so failure-mode coverage can run in the unit test suite rather than BDD:
BIO_new_bio_paircreates two connected in-memory BIOs.SolidSyslogTlsStreamruns as client on one side; the test stands up a real libssl server (SSL_CTX+ cert-generation helpers) on the other.Scope note: the harness is explicitly OpenSSL-specific — it exercises
Platform/OpenSsl/. If future epics add mbedTLS / BearSSL / wolfSSL, each gets its own harness under its own Platform tier. The harness must not try to be portable.Cost estimate: ~150 lines of scaffolding (server-side
SSL_CTX, cert-generation helpers, BIO-pair plumbing) for the first test; subsequent tests are one function call.Test support
OpenSslFake.c— per-BIOBIO_set_data/BIO_get_datastorage so existing fake-based tests can exercise multiple BIOs without aliasing.Acceptance
Openis return-value-checked; partial-init failures release all resources.SSL_set_tlsext_host_nameorSSL_set1_host) blocks the handshake.BIO_METHODis freed (no leak at process exit).Closenulls theSSLpointer;Destroyperforms full cleanup.OpenSslFakestores BIO data per-instance.Tests/(landing as the first commit of the story), runs against real libssl, and covers at least: expired server cert, wrong CN/SAN, wrong CA, unsupported cipher.Platform/OpenSsl.docs/iec62443.md.Out of scope