Branch data Line data Source code
1 : : /* SPDX-FileCopyrightText: Copyright 2026 Cozens Software Solutions Limited
2 : : * SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0 OR LicenseRef-PolyForm-Internal-Use-1.0.0 OR LicenseRef-COSOSO-Commercial
3 : : */
4 : :
5 : : #include "SolidSyslogMbedTlsPemBufferCredentials.h"
6 : :
7 : : #include <mbedtls/ctr_drbg.h>
8 : : #include <mbedtls/pk.h>
9 : : #include <mbedtls/ssl.h>
10 : : #include <mbedtls/x509_crt.h>
11 : : #include <stdbool.h>
12 : : #include <stddef.h>
13 : :
14 : : #include "SolidSyslogErrorCategory.h"
15 : : #include "SolidSyslogMbedTlsCredentialsDefinition.h"
16 : : #include "SolidSyslogMbedTlsPemBufferCredentialsPrivate.h"
17 : : #include "SolidSyslogTlsCredentialsInstalled.h"
18 : :
19 : : const struct SolidSyslogErrorSource SolidSyslogMbedTlsPemBufferCredentialsErrorSource = {
20 : : "MbedTlsPemBufferCredentials",
21 : : SolidSyslogTlsCredentialsErrors_Name
22 : : };
23 : :
24 : : static bool MbedTlsPemBufferCredentials_Install(
25 : : struct SolidSyslogMbedTlsCredentials* base,
26 : : struct mbedtls_ssl_config* conf,
27 : : struct SolidSyslogTlsCredentialsInstalled* installed
28 : : );
29 : : static inline bool MbedTlsPemBufferCredentials_IsAlreadyInUse(const struct SolidSyslogMbedTlsPemBufferCredentials* self
30 : : );
31 : : static inline bool MbedTlsPemBufferCredentials_ParseTrustAnchors(
32 : : struct SolidSyslogMbedTlsPemBufferCredentials* self,
33 : : struct mbedtls_ssl_config* conf
34 : : );
35 : : static inline void MbedTlsPemBufferCredentials_ConfigureClientIdentity(
36 : : struct SolidSyslogMbedTlsPemBufferCredentials* self,
37 : : struct mbedtls_ssl_config* conf
38 : : );
39 : : static inline bool MbedTlsPemBufferCredentials_HasClientCredential(
40 : : const struct SolidSyslogMbedTlsPemBufferCredentialsConfig* config
41 : : );
42 : : static inline bool MbedTlsPemBufferCredentials_HasHalfOfClientCredential(
43 : : const struct SolidSyslogMbedTlsPemBufferCredentialsConfig* config
44 : : );
45 : : static inline bool MbedTlsPemBufferCredentials_ClientPemIsTerminated(
46 : : const struct SolidSyslogMbedTlsPemBufferCredentialsConfig* config
47 : : );
48 : : static inline bool MbedTlsPemBufferCredentials_ParseClientCredential(struct SolidSyslogMbedTlsPemBufferCredentials* self
49 : : );
50 : : static inline bool MbedTlsPemBufferCredentials_ClientKeyMatchesCertificate(
51 : : struct SolidSyslogMbedTlsPemBufferCredentials* self
52 : : );
53 : : static inline bool MbedTlsPemBufferCredentials_IsSupplied(const struct SolidSyslogMbedTlsPemBuffer* pem);
54 : : static inline bool MbedTlsPemBufferCredentials_IsTerminated(const struct SolidSyslogMbedTlsPemBuffer* pem);
55 : : static void MbedTlsPemBufferCredentials_Release(struct SolidSyslogMbedTlsCredentials* base);
56 : : static inline struct SolidSyslogMbedTlsPemBufferCredentials* MbedTlsPemBufferCredentials_SelfFromBase(
57 : : struct SolidSyslogMbedTlsCredentials* base
58 : : );
59 : :
60 : 33 : void SolidSyslogMbedTlsPemBufferCredentials_Initialise(
61 : : struct SolidSyslogMbedTlsCredentials* base,
62 : : const struct SolidSyslogMbedTlsPemBufferCredentialsConfig* config
63 : : )
64 : : {
65 : 33 : struct SolidSyslogMbedTlsPemBufferCredentials* self = MbedTlsPemBufferCredentials_SelfFromBase(base);
66 : 33 : self->Base.Install = MbedTlsPemBufferCredentials_Install;
67 : 33 : self->Base.Release = MbedTlsPemBufferCredentials_Release;
68 : 33 : self->Config = *config;
69 : 33 : self->OutstandingInstalls = 0U;
70 : 33 : self->InstallHealth = (struct SolidSyslogStreamOpenHealth) {0};
71 : 33 : mbedtls_x509_crt_init(&self->CaChain);
72 : 33 : mbedtls_x509_crt_init(&self->ClientCertChain);
73 : 33 : mbedtls_pk_init(&self->ClientKey);
74 : 33 : }
75 : :
76 : 122 : static inline struct SolidSyslogMbedTlsPemBufferCredentials* MbedTlsPemBufferCredentials_SelfFromBase(
77 : : struct SolidSyslogMbedTlsCredentials* base
78 : : )
79 : : {
80 : 122 : return (struct SolidSyslogMbedTlsPemBufferCredentials*) base;
81 : : }
82 : :
83 : 33 : void SolidSyslogMbedTlsPemBufferCredentials_Cleanup(struct SolidSyslogMbedTlsCredentials* base)
84 : : {
85 : 33 : MbedTlsPemBufferCredentials_Release(base);
86 : 33 : }
87 : :
88 : : /* Parsed here rather than at Create, so the material exists only for a
89 : : * connection actually being made. Every Install is answered by a Release, which
90 : : * is where it goes again. */
91 : 39 : static bool MbedTlsPemBufferCredentials_Install(
92 : : struct SolidSyslogMbedTlsCredentials* base,
93 : : struct mbedtls_ssl_config* conf,
94 : : struct SolidSyslogTlsCredentialsInstalled* installed
95 : : )
96 : : {
97 : 39 : struct SolidSyslogMbedTlsPemBufferCredentials* self = MbedTlsPemBufferCredentials_SelfFromBase(base);
98 : 39 : installed->TrustAnchorsInstalled = false;
99 : 39 : installed->Fingerprints = self->Config.PeerFingerprints;
100 : 39 : installed->FingerprintCount = self->Config.PeerFingerprintCount;
101 : 39 : self->OutstandingInstalls++;
102 : 39 : bool ok = !MbedTlsPemBufferCredentials_IsAlreadyInUse(self);
103 [ + + ]: 39 : if (!ok)
104 : : {
105 : 4 : MbedTlsPemBufferCredentials_ReportInstallFailure(
106 : : self,
107 : : SOLIDSYSLOG_SEVERITY_ERROR,
108 : : SOLIDSYSLOG_CAT_BAD_CONFIG,
109 : : SOLIDSYSLOG_TLS_CREDENTIALS_ERROR_ALREADY_IN_USE
110 : : );
111 : : }
112 [ + + ]: 35 : else if (MbedTlsPemBufferCredentials_IsSupplied(&self->Config.CaPem))
113 : : {
114 : 34 : installed->TrustAnchorsInstalled = MbedTlsPemBufferCredentials_ParseTrustAnchors(self, conf);
115 : 34 : ok = installed->TrustAnchorsInstalled;
116 : : }
117 : : else
118 : : {
119 : : /* No anchors configured is legitimate - a pin alone can authorise. */
120 : : }
121 [ + + ]: 39 : if (ok)
122 : : {
123 : 24 : SolidSyslogStreamOpenHealth_Opened(&self->InstallHealth);
124 : 24 : MbedTlsPemBufferCredentials_ConfigureClientIdentity(self, conf);
125 : : }
126 : 39 : return ok;
127 : : }
128 : :
129 : : /* One slot holds the parsed material for one connection. A second stream
130 : : * sharing this source would parse over material the first is still using, so
131 : : * it is refused and its Open fails; the sender retries, and succeeds once the
132 : : * first connection closes. */
133 : 39 : static inline bool MbedTlsPemBufferCredentials_IsAlreadyInUse(const struct SolidSyslogMbedTlsPemBufferCredentials* self)
134 : : {
135 : 39 : return self->OutstandingInstalls > 1U;
136 : : }
137 : :
138 : : /* No fault in our own credential stops delivery: the collector is the
139 : : * enforcement point for it, and one that requires a client certificate refuses
140 : : * the handshake anyway. Every failure here leaves nothing installed, so the
141 : : * connection continues server-authenticated rather than half-presenting a
142 : : * credential. Whatever was parsed before the fault is released with the rest at
143 : : * Close. */
144 : 24 : static inline void MbedTlsPemBufferCredentials_ConfigureClientIdentity(
145 : : struct SolidSyslogMbedTlsPemBufferCredentials* self,
146 : : struct mbedtls_ssl_config* conf
147 : : )
148 : : {
149 [ + + ]: 24 : if (MbedTlsPemBufferCredentials_HasClientCredential(&self->Config))
150 : : {
151 [ + + ]: 13 : if (MbedTlsPemBufferCredentials_ClientPemIsTerminated(&self->Config) == false)
152 : : {
153 : 3 : MbedTlsPemBufferCredentials_Report(
154 : : SOLIDSYSLOG_SEVERITY_WARNING,
155 : : SOLIDSYSLOG_CAT_BAD_CONFIG,
156 : : SOLIDSYSLOG_TLS_CREDENTIALS_ERROR_PEM_NOT_TERMINATED
157 : : );
158 : : }
159 [ + + ]: 10 : else if (MbedTlsPemBufferCredentials_ParseClientCredential(self) == false)
160 : : {
161 : 3 : MbedTlsPemBufferCredentials_Report(
162 : : SOLIDSYSLOG_SEVERITY_WARNING,
163 : : SOLIDSYSLOG_CAT_BAD_CONFIG,
164 : : SOLIDSYSLOG_TLS_CREDENTIALS_ERROR_CLIENT_CREDENTIAL_NOT_PARSED
165 : : );
166 : : }
167 [ + + ]: 7 : else if (MbedTlsPemBufferCredentials_ClientKeyMatchesCertificate(self) == false)
168 : : {
169 : 1 : MbedTlsPemBufferCredentials_Report(
170 : : SOLIDSYSLOG_SEVERITY_WARNING,
171 : : SOLIDSYSLOG_CAT_BAD_CONFIG,
172 : : SOLIDSYSLOG_TLS_CREDENTIALS_ERROR_CLIENT_CREDENTIAL_MISMATCHED
173 : : );
174 : : }
175 : : /* Only MBEDTLS_ERR_SSL_ALLOC_FAILED, which returns before the key_cert
176 : : * node is appended, so nothing is left half-configured. */
177 [ - + ]: 6 : else if (mbedtls_ssl_conf_own_cert(conf, &self->ClientCertChain, &self->ClientKey) != 0)
178 : : {
179 : 0 : MbedTlsPemBufferCredentials_Report(
180 : : SOLIDSYSLOG_SEVERITY_WARNING,
181 : : SOLIDSYSLOG_CAT_BAD_CONFIG,
182 : : SOLIDSYSLOG_TLS_CREDENTIALS_ERROR_CLIENT_CREDENTIAL_NOT_INSTALLED
183 : : );
184 : : }
185 : : else
186 : : {
187 : : /* Parsed, paired and installed - the credential will be presented. */
188 : : }
189 : : }
190 [ + + ]: 11 : else if (MbedTlsPemBufferCredentials_HasHalfOfClientCredential(&self->Config))
191 : : {
192 : 1 : MbedTlsPemBufferCredentials_Report(
193 : : SOLIDSYSLOG_SEVERITY_WARNING,
194 : : SOLIDSYSLOG_CAT_BAD_CONFIG,
195 : : SOLIDSYSLOG_TLS_CREDENTIALS_ERROR_CLIENT_CREDENTIAL_INCOMPLETE
196 : : );
197 : : }
198 : : else
199 : : {
200 : : /* Neither supplied - server-authenticated TLS is the deliberate case. */
201 : : }
202 : 24 : }
203 : :
204 : 24 : static inline bool MbedTlsPemBufferCredentials_HasClientCredential(
205 : : const struct SolidSyslogMbedTlsPemBufferCredentialsConfig* config
206 : : )
207 : : {
208 [ + + + + ]: 38 : return MbedTlsPemBufferCredentials_IsSupplied(&config->ClientCertPem) &&
209 : 14 : MbedTlsPemBufferCredentials_IsSupplied(&config->ClientKeyPem);
210 : : }
211 : :
212 : : /* One half without the other. The integrator asked for mutual TLS and will not
213 : : * get it, so it is reported rather than read as a decision to go without. */
214 : 11 : static inline bool MbedTlsPemBufferCredentials_HasHalfOfClientCredential(
215 : : const struct SolidSyslogMbedTlsPemBufferCredentialsConfig* config
216 : : )
217 : : {
218 : 22 : return MbedTlsPemBufferCredentials_IsSupplied(&config->ClientCertPem) !=
219 : 11 : MbedTlsPemBufferCredentials_IsSupplied(&config->ClientKeyPem);
220 : : }
221 : :
222 : : /* Both halves, because a certificate whose key is unusable is no more use than
223 : : * neither. */
224 : 13 : static inline bool MbedTlsPemBufferCredentials_ClientPemIsTerminated(
225 : : const struct SolidSyslogMbedTlsPemBufferCredentialsConfig* config
226 : : )
227 : : {
228 [ + + + + ]: 24 : return MbedTlsPemBufferCredentials_IsTerminated(&config->ClientCertPem) &&
229 : 11 : MbedTlsPemBufferCredentials_IsTerminated(&config->ClientKeyPem);
230 : : }
231 : :
232 : 10 : static inline bool MbedTlsPemBufferCredentials_ParseClientCredential(struct SolidSyslogMbedTlsPemBufferCredentials* self
233 : : )
234 : : {
235 : 10 : return (mbedtls_x509_crt_parse(
236 : : &self->ClientCertChain,
237 : : self->Config.ClientCertPem.Bytes,
238 : : self->Config.ClientCertPem.Length
239 [ + + + + ]: 19 : ) == 0) &&
240 : 9 : (mbedtls_pk_parse_key(
241 : : &self->ClientKey,
242 : : self->Config.ClientKeyPem.Bytes,
243 : : self->Config.ClientKeyPem.Length,
244 : : NULL,
245 : : 0U,
246 : : mbedtls_ctr_drbg_random,
247 : 9 : self->Config.Rng
248 : : ) == 0);
249 : : }
250 : :
251 : : /* mbedtls_ssl_conf_own_cert does not check the pair it is handed, and names this
252 : : * function in its own documentation as the way to check it. */
253 : 7 : static inline bool MbedTlsPemBufferCredentials_ClientKeyMatchesCertificate(
254 : : struct SolidSyslogMbedTlsPemBufferCredentials* self
255 : : )
256 : : {
257 : 7 : return mbedtls_pk_check_pair(
258 : 7 : &self->ClientCertChain.pk,
259 : 7 : &self->ClientKey,
260 : : mbedtls_ctr_drbg_random,
261 : 7 : self->Config.Rng
262 : 7 : ) == 0;
263 : : }
264 : :
265 : : /* Mbed TLS reads a certificate buffer whose last byte is not NUL as DER, so a
266 : : * length given as strlen rather than strlen + 1 fails as "not a certificate"
267 : : * and says nothing about the length. The check is one byte inside the declared
268 : : * extent, and it names the fault the integrator actually made. */
269 : 34 : static inline bool MbedTlsPemBufferCredentials_ParseTrustAnchors(
270 : : struct SolidSyslogMbedTlsPemBufferCredentials* self,
271 : : struct mbedtls_ssl_config* conf
272 : : )
273 : : {
274 : 34 : bool parsed = false;
275 [ + + ]: 34 : if (MbedTlsPemBufferCredentials_IsTerminated(&self->Config.CaPem) == false)
276 : : {
277 : 4 : MbedTlsPemBufferCredentials_ReportInstallFailure(
278 : : self,
279 : : SOLIDSYSLOG_SEVERITY_ERROR,
280 : : SOLIDSYSLOG_CAT_BAD_CONFIG,
281 : : SOLIDSYSLOG_TLS_CREDENTIALS_ERROR_PEM_NOT_TERMINATED
282 : : );
283 : : }
284 [ + + ]: 30 : else if (mbedtls_x509_crt_parse(&self->CaChain, self->Config.CaPem.Bytes, self->Config.CaPem.Length) != 0)
285 : : {
286 : 7 : MbedTlsPemBufferCredentials_ReportInstallFailure(
287 : : self,
288 : : SOLIDSYSLOG_SEVERITY_ERROR,
289 : : SOLIDSYSLOG_CAT_BAD_CONFIG,
290 : : SOLIDSYSLOG_TLS_CREDENTIALS_ERROR_TRUST_ANCHORS_NOT_PARSED
291 : : );
292 : : }
293 : : else
294 : : {
295 : 23 : mbedtls_ssl_conf_ca_chain(conf, &self->CaChain, NULL);
296 : 23 : parsed = true;
297 : : }
298 : 34 : return parsed;
299 : : }
300 : :
301 : : /* A NULL buffer is the documented way to say "not supplied". A pointer with an
302 : : * unusable extent is a mistake instead, and is reported rather than read as a
303 : : * decision to go without - the integrator who made it believes the material is
304 : : * in force. */
305 : 95 : static inline bool MbedTlsPemBufferCredentials_IsSupplied(const struct SolidSyslogMbedTlsPemBuffer* pem)
306 : : {
307 : 95 : return pem->Bytes != NULL;
308 : : }
309 : :
310 : : /* Guards its own read: a zero length has no last byte to test, and would index
311 : : * at SIZE_MAX. */
312 : 58 : static inline bool MbedTlsPemBufferCredentials_IsTerminated(const struct SolidSyslogMbedTlsPemBuffer* pem)
313 : : {
314 [ + + + + ]: 58 : return (pem->Length > 0U) && (pem->Bytes[pem->Length - 1U] == (unsigned char) '\0');
315 : : }
316 : :
317 : : /* Freeing is what wipes: mbedtls_pk_free zeroises the key context and every
318 : : * limb of the private key, and mbedtls_x509_crt_free zeroises the DER it
319 : : * decoded. Both leave the structs in the state an init produces, so the next
320 : : * Install parses into them again, and a second Release is harmless. */
321 : 50 : static void MbedTlsPemBufferCredentials_Release(struct SolidSyslogMbedTlsCredentials* base)
322 : : {
323 : 50 : struct SolidSyslogMbedTlsPemBufferCredentials* self = MbedTlsPemBufferCredentials_SelfFromBase(base);
324 [ + + ]: 50 : if (self->OutstandingInstalls > 0U)
325 : : {
326 : 39 : self->OutstandingInstalls--;
327 : : }
328 [ + + ]: 50 : if (self->OutstandingInstalls == 0U)
329 : : {
330 : 46 : mbedtls_x509_crt_free(&self->CaChain);
331 : 46 : mbedtls_x509_crt_free(&self->ClientCertChain);
332 : 46 : mbedtls_pk_free(&self->ClientKey);
333 : : }
334 : 50 : }
|