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
22 changes: 15 additions & 7 deletions Core/Interface/SolidSyslogSdElement.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
/** @file
* The SD authoring API for one [SD-ID PARAM="value"...] element:
* SolidSyslogSdElement_Begin / SolidSyslogSdElement_Param /
* SolidSyslogSdElement_End, which own the brackets and SD-NAME charset so the
* author writes only names and values. */
* SolidSyslogSdElement_End, which own the element and parameter framing so the
* author writes only names and values. Escaping the value itself belongs to
* SolidSyslogSdValue, the sink Param hands back. */
#ifndef SOLIDSYSLOGSDELEMENT_H
#define SOLIDSYSLOGSDELEMENT_H

Expand All @@ -13,23 +14,30 @@
SOLIDSYSLOG_EXTERN_C_BEGIN

/** The element writer handed to an SD's Format. Owns the brackets and the
* SD-NAME / PARAM-NAME charset (each bounded to 32 bytes), so an author
* writes only names and values and cannot desync the framing.
* Stack-transient, no pool (D.002). */
* separators, and bounds each name to 32 bytes; the value sink it hands back
* does the escaping. A value cannot desync the framing whatever it contains;
* a name is the author's to keep within SD-NAME. Stack-transient, no pool
* (D.002). */
struct SolidSyslogSdElement;
struct SolidSyslogSdValue;

/** Opens an SD-ELEMENT: emits "[name" for an IANA-registered name
* (@p enterpriseNumber 0) or "[name@enterpriseNumber" for a private one. A
* NULL @p name suppresses the whole element, so a conditional element needs
* no placeholder; the matching SolidSyslogSdElement_End is still required. */
* no placeholder; the matching SolidSyslogSdElement_End is still required.
* @p name must be an SD-NAME: 1 to 32 printable US-ASCII characters,
* excluding '=', ']' and '"'. Over-long names are truncated and
* non-printable bytes and spaces substituted, but those three are emitted as
* given and ']' breaks the framing. RFC 5424 also requires an SD-ID to
* appear at most once in a message, which is likewise the author's. */
void SolidSyslogSdElement_Begin(struct SolidSyslogSdElement * element, const char* name, uint32_t enterpriseNumber);
Comment thread
coderabbitai[bot] marked this conversation as resolved.

/** Opens an SD-PARAM and returns the value sink to stream its value into.
* Always returns a usable sink, never NULL: a NULL @p name (or a suppressed
* element) skips the param but still absorbs the caller's value writes. The
* returned pointer belongs to the element and stays valid until the next
* SolidSyslogSdElement_Param or SolidSyslogSdElement_End. */
* SolidSyslogSdElement_Param or SolidSyslogSdElement_End. @p name is an
* SD-NAME on the same terms as SolidSyslogSdElement_Begin's. */
struct SolidSyslogSdValue* SolidSyslogSdElement_Param(struct SolidSyslogSdElement * element, const char* name);

/** Closes the SD-ELEMENT: closes any open param value's quote and emits ']'
Expand Down
4 changes: 3 additions & 1 deletion Platform/MbedTls/Source/SolidSyslogMbedTlsStream.c
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,9 @@ static inline void MbedTlsStream_ApplyTlsPolicy(struct SolidSyslogMbedTlsStream*
/* Pin the floor at TLS 1.2 rather than inheriting MBEDTLS_SSL_PRESET_DEFAULT,
* which can negotiate down to TLS 1.0/1.1 on permissive integrator builds.
* The floor is stated here so downgrade resistance does not depend on the
* preset the integrator happens to have compiled in. */
* preset the integrator happens to have compiled in. No ceiling is set:
* RFC 9662, which updates RFC 5425, requires TLS 1.3 to be preferred
* wherever it is implemented. */
mbedtls_ssl_conf_min_tls_version(&self->SslConfig, MBEDTLS_SSL_VERSION_TLS1_2);
mbedtls_ssl_conf_ca_chain(&self->SslConfig, self->Config.CaChain, NULL);
mbedtls_ssl_conf_rng(&self->SslConfig, mbedtls_ctr_drbg_random, self->Config.Rng);
Expand Down
2 changes: 2 additions & 0 deletions Platform/OpenSsl/Source/SolidSyslogOpenSslStream.c
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,8 @@ static inline bool OpenSslStream_ConfigureTrustAnchors(SSL_CTX* ctx, const char*
return ok;
}

/* A floor, and deliberately no ceiling: RFC 9662, which updates RFC 5425,
* requires TLS 1.3 to be preferred wherever it is implemented. */
static inline bool OpenSslStream_ConfigureProtocolFloor(SSL_CTX* ctx)
{
return SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION) == 1;
Expand Down
5 changes: 4 additions & 1 deletion docs/cra.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ Retention, access control and disposal at the collector.

The log path is not the primary means of meeting these, but it is in scope for each,
because a log record is itself stored and transmitted data, and because several of them
call for reporting.
call for reporting. Where the answer below is TLS, the [TLS obligations](tls.md) page states what
any TLS stream must do, and each backend's page records where it falls short of that
today.

| Point | What it asks for | How the audit trail contributes |
|---|---|---|
Expand Down Expand Up @@ -96,6 +98,7 @@ construction and what it delegates to you by contract.
## Where to go next

- [Building up the protection you need](hardening-path.md): the integration path, stage by stage.
- [TLS obligations](tls.md): what a TLS stream must do, for the transit half of (2)(e) and (2)(f).
- [IEC 62443 guide](iec62443.md): the control-by-control map for industrial deployments.
- [Compliance in one page](overview.md): the one-screen orientation across both frameworks.
- [Threat model](security/threat-model.md): the division of responsibility this page assumes.
Expand Down
10 changes: 7 additions & 3 deletions docs/iec62443.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,8 @@ what the library does not do.

| Control | What SolidSyslog provides | Gaps |
|---|---|---|
| **CR 1.5** — Authenticator management | The Stream role carries transport security, and a TLS backend filling it can present a client credential so the collector authenticates the device. The credential is supplied by the integrator: the library holds no keys of its own and reads whatever material it is given. Refreshing it is a deployment operation — how, and whether a reconnection is needed, is a property of the backend | The library ships no default authenticators, and protection of the key at rest — file permissions, a secure element, a hardware security module — is outside it. Whether a partially supplied credential is refused or quietly ignored differs between backends; the [platform pages](platforms/index.md) state which |
| **CR 1.8** — Public key infrastructure certificates | A TLS backend filling the Stream role verifies the collector's certificate against trust anchors you supply, and checks the collector's identity against a name you declare. Both are integrator inputs; neither has a default | Revocation is not performed by any shipped backend, by certificate revocation list or by online status protocol. Where a deployment requires it, it must come from your own configuration of the underlying library, and confirming it is in force is yours. Enrolment is your public-key infrastructure's process |
| **CR 1.5** — Authenticator management | The Stream role carries transport security, and a TLS backend filling it can present a client credential, so the collector authenticates the device. The credential is supplied by the integrator: the library holds no keys of its own and reads whatever material it is given. What a stream must do with it, including how a replacement takes effect, is stated under [TLS obligations](tls.md) | The library ships no default authenticators, and protection of the key at rest — file permissions, a secure element, a hardware security module — is outside it. Where a backend falls short of the obligations, its own [platform page](platforms/index.md) records it |
| **CR 1.8** — Public key infrastructure certificates | A TLS backend filling the Stream role verifies the collector's certificate against trust anchors you supply, and checks the collector's identity against a name you declare. Both are integrator inputs; neither has a default | Revocation checking is outside the [TLS obligations](tls.md), which gives the reasoning, and no shipped backend performs it — by certificate revocation list or by online status protocol. A deployment that requires it configures the underlying library itself and confirms it is in force. Enrolment is your public-key infrastructure's process |
Comment thread
coderabbitai[bot] marked this conversation as resolved.
| **CR 2.8** — Auditable events | `SolidSyslog_Log` formats events per RFC 5424. Structured data attached via `SolidSyslogMetaSd` / `SolidSyslogTimeQualitySd` / `SolidSyslogOriginSd`, or caller-supplied SD | Which internal activity is security-relevant is your decision: the library carries whatever your application raises and has no view of what it omitted. The categories the control expects to see audited follow from your own risk assessment |
| **CR 2.9** — Audit storage capacity | `SolidSyslogBlockStore` — rotating blocks, configurable `max-blocks` and `max-block-size`, configurable discard policy (`oldest` / `newest` / `halt`). It sits over `SolidSyslogFileBlockDevice`, which sits over the File role a platform fills, or over a BlockDevice you write against raw flash. The control's own requirement enhancement calls for a warning when the storage threshold is reached: `SolidSyslogStoreThresholdFunction` + `SolidSyslogStoreThresholdCallback` provide it — edge-triggered, fires once when used-bytes crosses the threshold, re-arms when usage falls back below | Capacity has to be sized to the deployment's outage budget, which is yours to know; the library enforces the number you give it. Durability of the medium — flash wear, filesystem behaviour on power loss — belongs to the platform beneath the File role, and no shipped filesystem backend is journalling |
| **CR 2.10** — Response to audit processing failures | `SolidSyslogStoreFullCallback` (halt policy) and the discard-policy enum. Caller picks the policy that fits the deployment's audit-loss tolerance. The early-warning threshold callback (CR 2.9) fires before discard / halt engages, giving the application time to act (notify operator, reduce verbosity, tighten retention); at 100% with HALT both fire on the same Write with threshold first then `onStoreFull`. Failures elsewhere in the path surface through the error handler — see [error severity](error-severity.md) | The response itself is your application's. The library reports the failure and applies the policy you configured; deciding what an operator is told, and whether the device keeps running, is above it |
Expand All @@ -51,7 +51,11 @@ what the library does not do.
| **CR 6.2** — Continuous monitoring | TCP / TLS delivery confirmation via `SolidSyslogStreamSender`. Replay across outages via `SolidSyslogBlockStore` store-and-forward. `SolidSyslogMetaSd` sequenceId is assigned at the point of raise, so a gap reflects loss anywhere in the pipeline rather than transport loss alone | The monitoring is the collector's: the library emits a sequence that makes loss detectable, it does not detect it, alert on it, or know whether anyone is watching. Continuous monitoring in the control's sense is a property of the deployment. |

The identity controls (CR 1.5, CR 1.8) are met by mutual TLS, which authenticates the
device to the collector. Both controls have requirement enhancements calling for
device to the collector. What any TLS stream must do is stated once under
[TLS obligations](tls.md), including where that contract deliberately departs
from general TLS practice. Each backend's page records where it falls short of it
today. Both controls have
requirement enhancements calling for
hardware-backed key protection; the library holds no keys of its own and reads whatever
material you supply, so meeting those falls to your key storage rather than to
SolidSyslog.
Expand Down
124 changes: 63 additions & 61 deletions docs/platforms/mbedtls/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,87 +6,89 @@ transport and keyed at-rest cryptography on embedded targets. It fills the
[SecurityPolicy](../../api/structSolidSyslogSecurityPolicy.md) role for at-rest
integrity and confidentiality.

What a TLS stream must do is the same whichever library provides it, and is
stated once under [TLS obligations](../../tls.md). This page covers what this
adapter needs, the coexistence guarantee it makes, and where it does not yet meet
that contract.

## What it ships

## Requirements

The adapter sources compile in your target against your own
`mbedtls_config.h`, so the features you enable are the features it gets.

Credentials are passed as caller-built, caller-owned handles rather than file
paths: a seeded `mbedtls_ctr_drbg_context` for the handshake, an
`mbedtls_x509_crt` trust chain, and for mutual TLS an `mbedtls_x509_crt` and
`mbedtls_pk_context` pair. No part of the adapter opens a file, which is what
allows it to run on targets built without `MBEDTLS_FS_IO`. Each handle must
remain valid for the lifetime of the stream.
The adapter sources compile in your target against your own `mbedtls_config.h`,
so the features you enable are the features it gets.

A `SolidSyslogSleepFunction` is required and has no default.

## Security behaviour and obligations
## Credentials are handles, not paths

The per-field detail is in
[`SolidSyslogMbedTlsStream.h`](../../api/SolidSyslogMbedTlsStream_8h.md),
alongside the fields themselves. What follows is the behaviour of the adapter as
a whole, and the work it leaves to you.
Credentials are passed as caller-built, caller-owned handles: a seeded
`mbedtls_ctr_drbg_context` for the handshake, an `mbedtls_x509_crt` trust chain,
and for mutual TLS an `mbedtls_x509_crt` and `mbedtls_pk_context` pair. No part
of the adapter opens a file, which is what allows it to run on targets built
without `MBEDTLS_FS_IO`. Each handle must remain valid for the lifetime of the
stream.

### Transport security is fixed by the adapter
Rotation follows from that, and needs sequencing. The adapter re-reads every
handle each time it connects, so replacing the material behind a handle is enough
— the stream does not need rebuilding. But while a connection is open, the
adapter's `ssl_config` holds pointers into that material, and freeing it there is
a use-after-free.
Comment thread
coderabbitai[bot] marked this conversation as resolved.

Peer certificate verification is pinned to `MBEDTLS_SSL_VERIFY_REQUIRED` and the
protocol floor to TLS 1.2, both set on the adapter's own `ssl_config`. The floor
is set explicitly rather than inherited from `MBEDTLS_SSL_PRESET_DEFAULT`, which
on a permissive build can negotiate down to TLS 1.0 or 1.1. TLS 1.3 is
negotiated when both peers support it.
So: call `SolidSyslogSender_Disconnect` first, which releases the `ssl_config`,
then free and re-parse into the same handle. The next send reconnects with the
new material. There is no reload callback and none is needed.

### Peer identity is yours to declare
## Coexistence is an auditable contract

The `ServerName` field supplies both the Server Name Indication sent in the
handshake and the identity checked against the peer certificate. It has a
distinct meaning when set, when empty, and when NULL — including one value that
disables endpoint verification without reporting anything — and the three are
documented on the field. Choosing between them is a deployment decision the
adapter cannot make.
`Platform/MbedTls/Source/` calls no process-global Mbed TLS API. It does not call
`mbedtls_platform_setup` or `mbedtls_platform_teardown`, install threading-alt
hooks, call `psa_crypto_init`, reset the global random number generator, or
replace a debug callback. TLS policy is applied per `ssl_config`, so it cannot
affect the ones you build elsewhere. A device that already uses Mbed TLS for
firmware update or a vendor cloud SDK keeps that configuration intact, and the
claim can be checked against the directory.

### Mutual TLS is optional and is not validated locally
## Where it differs from the contract

A client certificate is presented only when both `ClientCertChain` and
`ClientKey` are supplied. If either is absent, no client certificate is
configured and `Open` proceeds with server-authenticated TLS rather than
failing. Where a half-supplied credential must be treated as an error, check for
it before calling `Open`.
Five differences at 0.1.0, each tracked. Read them before relying on the
corresponding obligation.

The adapter performs no local check that the key matches the certificate, and
does not report a failure to install the pair. A mismatch is therefore seen as a
handshake rejection from the collector rather than as a setup error on the
device.
### A half-supplied client credential is accepted in silence

### Rotation requires a restart of the stream
A client certificate is presented only when both `ClientCertChain` and
`ClientKey` are supplied. Where either is absent the other is ignored, the
connection proceeds with server-authenticated TLS, and nothing is reported — so a
device configured for mutual TLS can run without presenting its certificate, and
without anyone on the device knowing. The contract requires this to be reported.
Until it is, check for a half-supplied pair before you open the stream. Tracked
as `#718`.

### The key is not checked against its certificate

Because the adapter consumes pre-built handles, refreshing credentials means
parsing new ones and recreating the stream, or the parent
`SolidSyslogStreamSender` so that the next connection uses them. There is no
reload callback.
No local check confirms that `ClientKey` matches `ClientCertChain`, and a failure
to install the pair is not reported either. A mismatch therefore surfaces as a
handshake rejection from the collector rather than as a setup error on the
device, which sends you looking in the wrong place. Tracked as `#719`.

### Key custody is outside the library
### An expired certificate stops delivery

The library holds no keys of its own and uses whatever material is passed to it.
Where a private key is stored, how it is protected at rest, and whether it is
held in a secure element are properties of your platform. The same applies to
the at-rest policies: HMAC-SHA256 and AES-256-GCM are keyed, and storing and
rotating that key is yours.
A peer certificate that is expired or not yet valid fails the handshake, even
where it still chains to a trusted anchor. The contract asks for it to be
reported with delivery continuing, because clock skew is the dominant cause and a
device with a wrong clock is one whose logs you still want. Tracked as `#731`.

### Revocation is not checked
### The cipher policy cannot be expressed

The adapter performs no revocation checking, by Certificate Revocation List or
by the Online Certificate Status Protocol. Where a deployment requires it, it
must come from your own configuration of Mbed TLS, and confirming that it is in
force is part of your assessment rather than something the adapter reports.
The configuration carries no cipher or ciphersuite field, so the ciphersuites
your `mbedtls_config.h` enables, filtered by the preset, are what gets
negotiated. The contract asks for an integrator's policy to be passed through
where the library allows one to be selected. Tracked as `#733`.

### Coexistence is an auditable contract
### The configuration is not checked when the stream is created

`Platform/MbedTls/Source/` calls no process-global Mbed TLS API. It does not
call `mbedtls_platform_setup` or `mbedtls_platform_teardown`, install
threading-alt hooks, call `psa_crypto_init`, reset the global random number
generator, or replace a debug callback. TLS policy is applied per `ssl_config`,
so it cannot affect the ones you build elsewhere. A device that already uses
Mbed TLS for firmware update or a vendor cloud SDK keeps that configuration
intact, and the claim can be checked against the directory.
A configuration missing something the stream cannot work without is accepted, and
the fault appears on the first connection attempt rather than at setup. The
random source and the trust chain are installed through calls that return no
status, so a missing one becomes a handshake failure rather than the
configuration error it is. Tracked as `#732`.
Loading
Loading