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 "SolidSyslogWolfSslSettings.h"
6 : :
7 : : #include "SolidSyslogWolfSslPemBufferCredentials.h"
8 : :
9 : : #include <stdbool.h>
10 : : #include <stddef.h>
11 : : #include <wolfssl/ssl.h>
12 : : #include <wolfssl/wolfcrypt/error-crypt.h>
13 : :
14 : : #include "SolidSyslogErrorCategory.h"
15 : : #include "SolidSyslogTlsCredentialsInstalled.h"
16 : : #include "SolidSyslogWolfSslCredentialsDefinition.h"
17 : : #include "SolidSyslogWolfSslPemBufferCredentialsPrivate.h"
18 : :
19 : : const struct SolidSyslogErrorSource SolidSyslogWolfSslPemBufferCredentialsErrorSource = {
20 : : "WolfSslPemBufferCredentials",
21 : : SolidSyslogTlsCredentialsErrors_Name
22 : : };
23 : :
24 : : static bool WolfSslPemBufferCredentials_Install(
25 : : struct SolidSyslogWolfSslCredentials* base,
26 : : struct WOLFSSL_CTX* ctx,
27 : : struct SolidSyslogTlsCredentialsInstalled* installed
28 : : );
29 : : static inline bool WolfSslPemBufferCredentials_LoadTrustAnchors(
30 : : struct SolidSyslogWolfSslPemBufferCredentials* self,
31 : : WOLFSSL_CTX* ctx
32 : : );
33 : : static inline void WolfSslPemBufferCredentials_ConfigureClientIdentity(
34 : : const struct SolidSyslogWolfSslPemBufferCredentials* self,
35 : : WOLFSSL_CTX* ctx
36 : : );
37 : : static inline bool WolfSslPemBufferCredentials_HasClientCredential(
38 : : const struct SolidSyslogWolfSslPemBufferCredentialsConfig* config
39 : : );
40 : : static inline bool WolfSslPemBufferCredentials_HasHalfOfClientCredential(
41 : : const struct SolidSyslogWolfSslPemBufferCredentialsConfig* config
42 : : );
43 : : static inline bool WolfSslPemBufferCredentials_ClientPemHasExtent(
44 : : const struct SolidSyslogWolfSslPemBufferCredentialsConfig* config
45 : : );
46 : : static inline int WolfSslPemBufferCredentials_LoadClientCredential(
47 : : const struct SolidSyslogWolfSslPemBufferCredentials* self,
48 : : WOLFSSL_CTX* ctx
49 : : );
50 : : static inline bool WolfSslPemBufferCredentials_IsSupplied(const struct SolidSyslogWolfSslPemBuffer* pem);
51 : : static inline bool WolfSslPemBufferCredentials_HasExtent(const struct SolidSyslogWolfSslPemBuffer* pem);
52 : : static void WolfSslPemBufferCredentials_Release(struct SolidSyslogWolfSslCredentials* base);
53 : : static inline struct SolidSyslogWolfSslPemBufferCredentials* WolfSslPemBufferCredentials_SelfFromBase(
54 : : struct SolidSyslogWolfSslCredentials* base
55 : : );
56 : :
57 : 153 : void SolidSyslogWolfSslPemBufferCredentials_Initialise(
58 : : struct SolidSyslogWolfSslCredentials* base,
59 : : const struct SolidSyslogWolfSslPemBufferCredentialsConfig* config
60 : : )
61 : : {
62 : 153 : struct SolidSyslogWolfSslPemBufferCredentials* self = WolfSslPemBufferCredentials_SelfFromBase(base);
63 : 153 : self->Base.Install = WolfSslPemBufferCredentials_Install;
64 : 153 : self->Base.Release = WolfSslPemBufferCredentials_Release;
65 : 153 : self->Config = *config;
66 : 153 : self->InstallHealth = (struct SolidSyslogStreamOpenHealth) {0};
67 : 153 : }
68 : :
69 : 284 : static inline struct SolidSyslogWolfSslPemBufferCredentials* WolfSslPemBufferCredentials_SelfFromBase(
70 : : struct SolidSyslogWolfSslCredentials* base
71 : : )
72 : : {
73 : 284 : return (struct SolidSyslogWolfSslPemBufferCredentials*) base;
74 : : }
75 : :
76 : 153 : void SolidSyslogWolfSslPemBufferCredentials_Cleanup(struct SolidSyslogWolfSslCredentials* base)
77 : : {
78 : : (void) base;
79 : 153 : }
80 : :
81 : : /* Loaded here rather than at Create, so the material exists only for a
82 : : * connection actually being made - and only inside the context the stream
83 : : * built for it, which the stream frees at Close. Nothing is held here. */
84 : 131 : static bool WolfSslPemBufferCredentials_Install(
85 : : struct SolidSyslogWolfSslCredentials* base,
86 : : struct WOLFSSL_CTX* ctx,
87 : : struct SolidSyslogTlsCredentialsInstalled* installed
88 : : )
89 : : {
90 : 131 : struct SolidSyslogWolfSslPemBufferCredentials* self = WolfSslPemBufferCredentials_SelfFromBase(base);
91 : 131 : installed->TrustAnchorsInstalled = false;
92 : 131 : installed->Fingerprints = self->Config.PeerFingerprints;
93 : 131 : installed->FingerprintCount = self->Config.PeerFingerprintCount;
94 : 131 : bool ok = true;
95 [ + + ]: 131 : if (WolfSslPemBufferCredentials_IsSupplied(&self->Config.CaPem))
96 : : {
97 : 97 : installed->TrustAnchorsInstalled = WolfSslPemBufferCredentials_LoadTrustAnchors(self, ctx);
98 : 97 : ok = installed->TrustAnchorsInstalled;
99 : : }
100 : : else
101 : : {
102 : : /* No anchors configured is legitimate - a pin alone can authorise. */
103 : : }
104 [ + + ]: 131 : if (ok)
105 : : {
106 : 123 : SolidSyslogStreamOpenHealth_Opened(&self->InstallHealth);
107 : 123 : WolfSslPemBufferCredentials_ConfigureClientIdentity(self, ctx);
108 : : }
109 : 131 : return ok;
110 : : }
111 : :
112 : : /* A buffer with no extent is a mistake, not a decision to go without, and is
113 : : * named as what it is rather than handed to a parser that would say "no PEM
114 : : * header" about an empty buffer. */
115 : 97 : static inline bool WolfSslPemBufferCredentials_LoadTrustAnchors(
116 : : struct SolidSyslogWolfSslPemBufferCredentials* self,
117 : : WOLFSSL_CTX* ctx
118 : : )
119 : : {
120 [ + + + + ]: 193 : bool loaded = WolfSslPemBufferCredentials_HasExtent(&self->Config.CaPem) && (wolfSSL_CTX_load_verify_buffer(
121 : : ctx,
122 : : self->Config.CaPem.Bytes,
123 : 96 : (long) self->Config.CaPem.Length,
124 : : WOLFSSL_FILETYPE_PEM
125 : : ) == WOLFSSL_SUCCESS);
126 [ + + ]: 97 : if (!loaded)
127 : : {
128 : 8 : WolfSslPemBufferCredentials_ReportInstallFailure(
129 : : self,
130 : : SOLIDSYSLOG_SEVERITY_ERROR,
131 : : SOLIDSYSLOG_CAT_BAD_CONFIG,
132 : : SOLIDSYSLOG_TLS_CREDENTIALS_ERROR_TRUST_ANCHORS_NOT_PARSED
133 : : );
134 : : }
135 : 97 : return loaded;
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. wolfSSL parses and installs in one call, so a failure
141 : : * is "not parsed" unless the library says it ran out of memory holding it. A
142 : : * certificate that loaded ahead of a key that did not is freed with the
143 : : * context at Close, like everything else on it. */
144 : 123 : static inline void WolfSslPemBufferCredentials_ConfigureClientIdentity(
145 : : const struct SolidSyslogWolfSslPemBufferCredentials* self,
146 : : WOLFSSL_CTX* ctx
147 : : )
148 : : {
149 [ + + ]: 123 : if (WolfSslPemBufferCredentials_HasClientCredential(&self->Config))
150 : : {
151 : 12 : int rc = WolfSslPemBufferCredentials_LoadClientCredential(self, ctx);
152 [ + + ]: 12 : if (rc == MEMORY_E)
153 : : {
154 : 1 : WolfSslPemBufferCredentials_Report(
155 : : SOLIDSYSLOG_SEVERITY_WARNING,
156 : : SOLIDSYSLOG_CAT_BAD_CONFIG,
157 : : SOLIDSYSLOG_TLS_CREDENTIALS_ERROR_CLIENT_CREDENTIAL_NOT_INSTALLED
158 : : );
159 : : }
160 [ + + ]: 11 : else if (rc != WOLFSSL_SUCCESS)
161 : : {
162 : 4 : WolfSslPemBufferCredentials_Report(
163 : : SOLIDSYSLOG_SEVERITY_WARNING,
164 : : SOLIDSYSLOG_CAT_BAD_CONFIG,
165 : : SOLIDSYSLOG_TLS_CREDENTIALS_ERROR_CLIENT_CREDENTIAL_NOT_PARSED
166 : : );
167 : : }
168 : : else
169 : : {
170 : : /* Parsed and installed - the credential will be presented. */
171 : : }
172 : : }
173 [ + + ]: 111 : else if (WolfSslPemBufferCredentials_HasHalfOfClientCredential(&self->Config))
174 : : {
175 : 4 : WolfSslPemBufferCredentials_Report(
176 : : SOLIDSYSLOG_SEVERITY_WARNING,
177 : : SOLIDSYSLOG_CAT_BAD_CONFIG,
178 : : SOLIDSYSLOG_TLS_CREDENTIALS_ERROR_CLIENT_CREDENTIAL_INCOMPLETE
179 : : );
180 : : }
181 : : else
182 : : {
183 : : /* Neither supplied - server-authenticated TLS is the deliberate case. */
184 : : }
185 : 123 : }
186 : :
187 : 123 : static inline bool WolfSslPemBufferCredentials_HasClientCredential(
188 : : const struct SolidSyslogWolfSslPemBufferCredentialsConfig* config
189 : : )
190 : : {
191 [ + + + + ]: 138 : return WolfSslPemBufferCredentials_IsSupplied(&config->ClientCertPem) &&
192 : 15 : WolfSslPemBufferCredentials_IsSupplied(&config->ClientKeyPem);
193 : : }
194 : :
195 : : /* One half without the other. The integrator asked for mutual TLS and will not
196 : : * get it, so it is reported rather than read as a decision to go without. */
197 : 111 : static inline bool WolfSslPemBufferCredentials_HasHalfOfClientCredential(
198 : : const struct SolidSyslogWolfSslPemBufferCredentialsConfig* config
199 : : )
200 : : {
201 : 222 : return WolfSslPemBufferCredentials_IsSupplied(&config->ClientCertPem) !=
202 : 111 : WolfSslPemBufferCredentials_IsSupplied(&config->ClientKeyPem);
203 : : }
204 : :
205 : : /* Both halves, because a certificate whose key is unusable is no more use than
206 : : * neither. */
207 : 12 : static inline bool WolfSslPemBufferCredentials_ClientPemHasExtent(
208 : : const struct SolidSyslogWolfSslPemBufferCredentialsConfig* config
209 : : )
210 : : {
211 [ + + + + ]: 23 : return WolfSslPemBufferCredentials_HasExtent(&config->ClientCertPem) &&
212 : 11 : WolfSslPemBufferCredentials_HasExtent(&config->ClientKeyPem);
213 : : }
214 : :
215 : : /* The first call that does not succeed answers for the pair; a buffer with no
216 : : * extent answers as the parser would for an empty one. */
217 : 12 : static inline int WolfSslPemBufferCredentials_LoadClientCredential(
218 : : const struct SolidSyslogWolfSslPemBufferCredentials* self,
219 : : WOLFSSL_CTX* ctx
220 : : )
221 : : {
222 : 12 : int rc = WOLFSSL_FAILURE;
223 [ + + ]: 12 : if (WolfSslPemBufferCredentials_ClientPemHasExtent(&self->Config))
224 : : {
225 : 10 : rc = wolfSSL_CTX_use_certificate_chain_buffer(
226 : : ctx,
227 : 10 : self->Config.ClientCertPem.Bytes,
228 : 10 : (long) self->Config.ClientCertPem.Length
229 : : );
230 [ + + ]: 10 : if (rc == WOLFSSL_SUCCESS)
231 : : {
232 : 9 : rc = wolfSSL_CTX_use_PrivateKey_buffer(
233 : : ctx,
234 : 9 : self->Config.ClientKeyPem.Bytes,
235 : 9 : (long) self->Config.ClientKeyPem.Length,
236 : : WOLFSSL_FILETYPE_PEM
237 : : );
238 : : }
239 : : }
240 : 12 : return rc;
241 : : }
242 : :
243 : : /* A NULL buffer is the documented way to say "not supplied". */
244 : 491 : static inline bool WolfSslPemBufferCredentials_IsSupplied(const struct SolidSyslogWolfSslPemBuffer* pem)
245 : : {
246 : 491 : return pem->Bytes != NULL;
247 : : }
248 : :
249 : 120 : static inline bool WolfSslPemBufferCredentials_HasExtent(const struct SolidSyslogWolfSslPemBuffer* pem)
250 : : {
251 : 120 : return pem->Length > 0U;
252 : : }
253 : :
254 : : /* Nothing was acquired here: what Install loaded belongs to the context, and
255 : : * the stream frees that before it calls this. */
256 : 114 : static void WolfSslPemBufferCredentials_Release(struct SolidSyslogWolfSslCredentials* base)
257 : : {
258 : : (void) base;
259 : 114 : }
|