Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
72 changes: 72 additions & 0 deletions DEVLOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11811,3 +11811,75 @@ MISRA rule — different category, doesn't set precedent.
### Open questions

- None.

## 2026-05-24 — S26.03 TlsStream Open unwinds + per-failure-point error reporting (OpenSSL parity of S26.02)

### Decisions

- **Five codes, matching S26.02's coarse grain.** OpenSSL has more
configurable surfaces in the context (trust anchors, client identity,
protocol floor, cipher list — each independently failing with an
int return) than mbedTLS has, so a literal one-code-per-failing-API
expansion would have yielded 8-9 codes. Consolidated under
`TLSSTREAM_ERROR_CONTEXT_INIT_FAILED` so the integrator gets the
same diagnostic granularity as the MbedTls adapter. Finer codes
(TRUST_ANCHORS_NOT_LOADED, CLIENT_IDENTITY_INVALID,
CIPHER_LIST_REJECTED) are a future enhancement story if any
integrator surfaces a need.
- **One emit site per bool-returning helper, shared code for the
SSL/BIO alloc pair.** `InitSslContext`, `InitSslSession`,
`AttachTransportBio`, `ConfigureExpectedHostname`, and the two
`PerformHandshake` exit branches each emit at the point of failure
detection. `InitSslSession` and `AttachTransportBio` share
`SESSION_INIT_FAILED` — both are alloc-class failures the
integrator cannot triage finer than "TLS session resources couldn't
be allocated".
- **Per-mode resource-free assertions stay in the existing
XxxFailureFreesCtx tests.** The new `CHECK_OPEN_UNWOUND_WITH_ERROR`
macro pins the universal 5-assertion shape (transport closed, error
source, code, severity, count) without trying to pin SSL_CTX_free /
SSL_free counts — those vary by how far Open got before failing, so
they live in the existing per-mode tests that already pin them
precisely.
- **Existing test names kept.** S26.02 renamed `OpenFailsImmediately…`
to `OpenClosesTransportAndFreesSslState…` because the new tests had
to be added from scratch and the rename established the convention.
Here, ~15 existing `OpenReturnsFalseWhen…` tests already exist —
renaming all of them would be a big mechanical diff and the macro's
name (`CHECK_OPEN_UNWOUND_WITH_ERROR`) carries the assertion meaning.
Kept the existing names; extended the bodies.
- **`ReCreateStreamWithUpdatedConfig` helper added to the
TEST_GROUP.** Mirrors S26.02's `ReCreateHandleWithUpdatedConfig`.
Replaced inlined destroy/recreate-stream sequences in the eight
tests that needed a config tweak (CipherList, ClientCertChain/Key,
ServerName) — same six-line helper, same fixture-reset semantics
so the macro's `== 1` count assertions work.

### Deferred

- **Finer-grained CONTEXT_INIT codes** (TRUST_ANCHORS_NOT_LOADED,
CLIENT_IDENTITY_INVALID, CIPHER_LIST_REJECTED). Out of scope for
S26.03 by deliberate choice to maintain parity with S26.02.
Re-evaluate when an integrator asks for the granularity.
- **CodeRabbit cppcheck-misra include-set false positives.** Per the
feedback memory captured after S26.02 PR #440, expect any
"stale suppression" finding on the line shifts (`70 -> 74`,
`16 -> 20`) to be a false positive from the script running cppcheck
without `-IPlatform/OpenSsl/Interface`. Reproduce with the full
CI include set before agreeing.

### Open questions

- None.

### Process notes (collateral)

- **Lost David's local `.devcontainer/devcontainer.json` clang-service
switch.** A misguided `xargs clang-format -i` over
`git diff --name-only main...HEAD` mangled non-C/C++ files
(DEVLOG.md, misra_suppressions.txt, devcontainer.json). Reverted via
`git checkout --`, which also reverted David's uncommitted switch.
The switch has no functional impact in-session (we're already in the
clang container) but needs re-applying via the IDE before the next
Rebuild Container. Format pipeline going forward: filter
`git diff --name-only` for `.c|.cpp|.h|.hpp$` extensions.
5 changes: 5 additions & 0 deletions Platform/OpenSsl/Interface/SolidSyslogTlsStreamErrors.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ EXTERN_C_BEGIN
{
TLSSTREAM_ERROR_POOL_EXHAUSTED,
TLSSTREAM_ERROR_UNKNOWN_DESTROY,
TLSSTREAM_ERROR_CONTEXT_INIT_FAILED,
TLSSTREAM_ERROR_SESSION_INIT_FAILED,
TLSSTREAM_ERROR_SERVER_NAME_NOT_SET,
TLSSTREAM_ERROR_HANDSHAKE_REJECTED,
TLSSTREAM_ERROR_HANDSHAKE_TIMEOUT,
TLSSTREAM_ERROR_MAX
};

Expand Down
70 changes: 64 additions & 6 deletions Platform/OpenSsl/Source/SolidSyslogTlsStream.c
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@
#include <openssl/types.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>

#include "SolidSyslogError.h"
#include "SolidSyslogNullStream.h"
#include "SolidSyslogPrival.h"
#include "SolidSyslogStream.h"
#include "SolidSyslogStreamDefinition.h"
#include "SolidSyslogTlsStreamErrors.h"
#include "SolidSyslogTlsStreamPrivate.h"

enum
Expand Down Expand Up @@ -135,15 +139,30 @@ static inline void TlsStream_ReleaseSslContext(struct SolidSyslogTlsStream* self
static inline bool TlsStream_Open(struct SolidSyslogStream* base, const struct SolidSyslogAddress* addr)
{
struct SolidSyslogTlsStream* self = TlsStream_SelfFromBase(base);
return SolidSyslogStream_Open(self->Config.Transport, addr) && TlsStream_InitSslContext(self) &&
TlsStream_InitSslSession(self) && TlsStream_AttachTransportBio(self) &&
TlsStream_ConfigureExpectedHostname(self) && TlsStream_PerformHandshake(self);
bool ok = SolidSyslogStream_Open(self->Config.Transport, addr) && TlsStream_InitSslContext(self) &&
TlsStream_InitSslSession(self) && TlsStream_AttachTransportBio(self) &&
TlsStream_ConfigureExpectedHostname(self) && TlsStream_PerformHandshake(self);
if (!ok)
{
TlsStream_Close(base);
TlsStream_ReleaseSslContext(self);
}
return ok;
}

static inline bool TlsStream_InitSslContext(struct SolidSyslogTlsStream* self)
{
self->Ctx = TlsStream_CreateSslContext(&self->Config);
return self->Ctx != NULL;
bool ok = self->Ctx != NULL;
if (!ok)
{
SolidSyslog_Error(
SOLIDSYSLOG_SEVERITY_ERROR,
&TlsStreamErrorSource,
(uint8_t) TLSSTREAM_ERROR_CONTEXT_INIT_FAILED
);
}
return ok;
}

static inline SSL_CTX* TlsStream_CreateSslContext(const struct SolidSyslogTlsStreamConfig* config)
Expand Down Expand Up @@ -214,7 +233,16 @@ static inline bool TlsStream_ConfigureCipherList(SSL_CTX* ctx, const char* ciphe
static inline bool TlsStream_InitSslSession(struct SolidSyslogTlsStream* self)
{
self->Ssl = SSL_new(self->Ctx);
return self->Ssl != NULL;
bool ok = self->Ssl != NULL;
if (!ok)
{
SolidSyslog_Error(
SOLIDSYSLOG_SEVERITY_ERROR,
&TlsStreamErrorSource,
(uint8_t) TLSSTREAM_ERROR_SESSION_INIT_FAILED
);
}
return ok;
}

static inline bool TlsStream_AttachTransportBio(struct SolidSyslogTlsStream* self)
Expand All @@ -226,6 +254,14 @@ static inline bool TlsStream_AttachTransportBio(struct SolidSyslogTlsStream* sel
BIO_set_data(bio, self->Config.Transport);
SSL_set_bio(self->Ssl, bio, bio);
}
else
{
SolidSyslog_Error(
SOLIDSYSLOG_SEVERITY_ERROR,
&TlsStreamErrorSource,
(uint8_t) TLSSTREAM_ERROR_SESSION_INIT_FAILED
);
}
return ok;
}

Expand Down Expand Up @@ -341,6 +377,14 @@ static inline bool TlsStream_ConfigureExpectedHostname(struct SolidSyslogTlsStre
{
ok = (SSL_set_tlsext_host_name(self->Ssl, self->Config.ServerName) == 1) &&
(SSL_set1_host(self->Ssl, self->Config.ServerName) == 1);
if (!ok)
{
SolidSyslog_Error(
SOLIDSYSLOG_SEVERITY_ERROR,
&TlsStreamErrorSource,
(uint8_t) TLSSTREAM_ERROR_SERVER_NAME_NOT_SET
);
}
}
return ok;
}
Expand Down Expand Up @@ -377,8 +421,22 @@ static inline bool TlsStream_PerformHandshake(struct SolidSyslogTlsStream* self)
else
{
int err = SSL_get_error(self->Ssl, rc);
if (!TlsStream_IsRetryableSslError(err) || TlsStream_IsHandshakeBudgetExhausted(totalSleptMs))
if (!TlsStream_IsRetryableSslError(err))
{
SolidSyslog_Error(
SOLIDSYSLOG_SEVERITY_ERROR,
&TlsStreamErrorSource,
(uint8_t) TLSSTREAM_ERROR_HANDSHAKE_REJECTED
);
done = true;
}
else if (TlsStream_IsHandshakeBudgetExhausted(totalSleptMs))
{
SolidSyslog_Error(
SOLIDSYSLOG_SEVERITY_ERROR,
&TlsStreamErrorSource,
(uint8_t) TLSSTREAM_ERROR_HANDSHAKE_TIMEOUT
);
done = true;
}
else
Expand Down
10 changes: 10 additions & 0 deletions Platform/OpenSsl/Source/SolidSyslogTlsStreamMessages.c
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,16 @@ static const char* TlsStreamError_AsString(uint8_t code)
static const char* const messages[TLSSTREAM_ERROR_MAX] = {
[TLSSTREAM_ERROR_POOL_EXHAUSTED] = "SolidSyslogTlsStream_Create pool exhausted; returning fallback stream",
[TLSSTREAM_ERROR_UNKNOWN_DESTROY] = "SolidSyslogTlsStream_Destroy called with a handle not issued by this pool",
[TLSSTREAM_ERROR_CONTEXT_INIT_FAILED] =
"TLS client context could not be configured; connection not established",
[TLSSTREAM_ERROR_SESSION_INIT_FAILED] =
"TLS session could not be initialised against the configured context; connection not established",
[TLSSTREAM_ERROR_SERVER_NAME_NOT_SET] =
"TLS server name (SNI / cert match) could not be configured; connection not established",
[TLSSTREAM_ERROR_HANDSHAKE_REJECTED] =
"TLS handshake rejected by peer or local verification; connection not established",
[TLSSTREAM_ERROR_HANDSHAKE_TIMEOUT] =
"TLS handshake did not complete within the bounded retry budget; connection not established",
};
const char* result = "unknown";
if (code < (uint8_t) TLSSTREAM_ERROR_MAX)
Expand Down
Loading
Loading