SolidSyslog coverage report
Current view: top level - Platform/WolfSsl/Source - SolidSyslogWolfSslStream.c (source / functions) Coverage Total Hit
Test: SolidSyslog Lines: 100.0 % 356 356
Test Date: 2026-11-12 23:14:35 Functions: 100.0 % 56 56
Branches: 100.0 % 214 214

             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 "SolidSyslogWolfSslStream.h"
       8                 :             : 
       9                 :             : #include <stdbool.h>
      10                 :             : #include <stddef.h>
      11                 :             : #include <stdint.h>
      12                 :             : #include <string.h>
      13                 :             : #include <wolfssl/ssl.h>
      14                 :             : #include <wolfssl/error-ssl.h>
      15                 :             : #include <wolfssl/wolfcrypt/error-crypt.h>
      16                 :             : #include <wolfssl/wolfcrypt/hash.h>
      17                 :             : 
      18                 :             : /* wolfSSL compiles its notBefore / notAfter checks out entirely under
      19                 :             :  * NO_ASN_TIME, so no certificate in the chain is date-checked and nothing says
      20                 :             :  * so at runtime. The TLS contract requires the validity period to be checked,
      21                 :             :  * which is a promise that would then silently not hold, so the build stops
      22                 :             :  * here rather than shipping it.
      23                 :             :  *
      24                 :             :  * A target with no clock cannot check dates and is a legitimate deployment.
      25                 :             :  * Define SOLIDSYSLOG_WOLFSSL_NO_VALIDITY_CHECK to say so deliberately; the
      26                 :             :  * obligation is then knowingly unmet rather than quietly missing. */
      27                 :             : #if defined(NO_ASN_TIME) && !defined(SOLIDSYSLOG_WOLFSSL_NO_VALIDITY_CHECK)
      28                 :             : #error \
      29                 :             :     "NO_ASN_TIME is set, so certificate validity is not checked. Build wolfSSL with ASN time, or define SOLIDSYSLOG_WOLFSSL_NO_VALIDITY_CHECK to accept that."
      30                 :             : #endif
      31                 :             : 
      32                 :             : #include "SolidSyslogError.h"
      33                 :             : #include "SolidSyslogErrorCategory.h"
      34                 :             : #include "SolidSyslogNullStream.h"
      35                 :             : #include "SolidSyslogPrival.h"
      36                 :             : #include "SolidSyslogStream.h"
      37                 :             : #include "SolidSyslogStreamDefinition.h"
      38                 :             : #include "SolidSyslogTlsCredentialsInstalled.h"
      39                 :             : #include "SolidSyslogTlsFingerprint.h"
      40                 :             : #include "SolidSyslogTlsStreamCategories.h"
      41                 :             : #include "SolidSyslogTunables.h"
      42                 :             : #include "SolidSyslogWolfSslCredentialsDefinition.h"
      43                 :             : #include "SolidSyslogWolfSslStreamErrors.h"
      44                 :             : #include "SolidSyslogWolfSslStreamPrivate.h"
      45                 :             : 
      46                 :             : const struct SolidSyslogErrorSource SolidSyslogWolfSslStreamErrorSource = {
      47                 :             :     "WolfSslStream",
      48                 :             :     SolidSyslogTlsStreamErrors_Name
      49                 :             : };
      50                 :             : 
      51                 :             : /* The handshake poll, the read-before-write probe, and the BCP 195 section 4.5
      52                 :             :  * cryptographic floor in the units each wolfSSL setter takes. */
      53                 :             : enum
      54                 :             : {
      55                 :             :     HANDSHAKE_POLL_INTERVAL_MILLISECONDS = 10U,
      56                 :             :     WOLFSSL_STREAM_DISCARD_BYTES = 16U,
      57                 :             :     WOLFSSL_STREAM_MAX_READS_BEFORE_WRITE = 8U,
      58                 :             :     WOLFSSL_STREAM_MIN_RSA_KEY_BITS = 2048,
      59                 :             :     WOLFSSL_STREAM_MIN_ECC_KEY_BITS = 224,
      60                 :             :     WOLFSSL_STREAM_MIN_DH_KEY_BITS = 2048U
      61                 :             : };
      62                 :             : 
      63                 :             : /* The checks the verify callback can see the peer fail, as bits of
      64                 :             :  * RefusedChecks. */
      65                 :             : enum
      66                 :             : {
      67                 :             :     WOLFSSL_STREAM_CHECK_CHAIN_TRUST = 1U,
      68                 :             :     WOLFSSL_STREAM_CHECK_NAME = 2U,
      69                 :             :     WOLFSSL_STREAM_CHECK_NOT_AFTER = 4U,
      70                 :             :     WOLFSSL_STREAM_CHECK_NOT_BEFORE = 8U,
      71                 :             :     WOLFSSL_STREAM_CHECK_FINGERPRINT = 16U,
      72                 :             :     WOLFSSL_STREAM_CHECK_FINGERPRINT_DIGEST = 32U
      73                 :             : };
      74                 :             : 
      75                 :             : struct SolidSyslogAddress;
      76                 :             : 
      77                 :             : /* What a pin is the digest of: the leaf as the peer presented it, and the
      78                 :             :  * heap hint the digest is made against. */
      79                 :             : struct WolfSslStreamPeerCertificate
      80                 :             : {
      81                 :             :     const unsigned char* Der;
      82                 :             :     word32 Length;
      83                 :             :     void* Heap;
      84                 :             : };
      85                 :             : 
      86                 :             : static uint32_t WolfSslStream_NullHandshakeTimeoutGetter(void* context);
      87                 :             : static uint32_t WolfSslStream_NullVersion(void* context);
      88                 :             : static void WolfSslStream_NullProfile(struct SolidSyslogWolfSslProfile* profile, void* context);
      89                 :             : static inline bool WolfSslStream_ConfigProvidesHandshakeGetter(const struct SolidSyslogWolfSslStreamConfig* config);
      90                 :             : static inline bool WolfSslStream_ConfigProvidesVersion(const struct SolidSyslogWolfSslStreamConfig* config);
      91                 :             : static inline bool WolfSslStream_ConfigProvidesProfile(const struct SolidSyslogWolfSslStreamConfig* config);
      92                 :             : static inline struct SolidSyslogWolfSslStream* WolfSslStream_SelfFromBase(struct SolidSyslogStream* base);
      93                 :             : static inline bool WolfSslStream_Open(struct SolidSyslogStream* base, const struct SolidSyslogAddress* addr);
      94                 :             : static inline bool WolfSslStream_IsOpen(const struct SolidSyslogWolfSslStream* self);
      95                 :             : static inline bool WolfSslStream_Establish(struct SolidSyslogStream* base, const struct SolidSyslogAddress* addr);
      96                 :             : static inline void WolfSslStream_PullProfile(struct SolidSyslogWolfSslStream* self);
      97                 :             : static inline bool WolfSslStream_BuildContext(struct SolidSyslogWolfSslStream* self);
      98                 :             : static inline bool WolfSslStream_InstallCredentials(struct SolidSyslogWolfSslStream* self);
      99                 :             : static inline bool WolfSslStream_PeerIsAuthorisable(const struct SolidSyslogTlsCredentialsInstalled* installed);
     100                 :             : static inline bool WolfSslStream_FingerprintsAreUsable(struct SolidSyslogWolfSslStream* self);
     101                 :             : static inline bool WolfSslStream_BuildCanEnforceBoth(const struct SolidSyslogTlsCredentialsInstalled* installed);
     102                 :             : static inline bool WolfSslStream_CreateSession(struct SolidSyslogWolfSslStream* self);
     103                 :             : static inline bool WolfSslStream_ApplyTlsPolicy(struct SolidSyslogWolfSslStream* self);
     104                 :             : static inline bool WolfSslStream_ApplyCipherPolicy(struct SolidSyslogWolfSslStream* self);
     105                 :             : static inline bool WolfSslStream_ConfigureExpectedPeerName(struct SolidSyslogWolfSslStream* self);
     106                 :             : static inline bool WolfSslStream_ApplyExpectedPeerName(struct SolidSyslogWolfSslStream* self, const char* serverName);
     107                 :             : static inline int WolfSslStream_DeclareExpectedPeer(struct SolidSyslogWolfSslStream* self, const char* serverName);
     108                 :             : static inline bool WolfSslStream_IsAddressLiteral(const char* name);
     109                 :             : static inline bool WolfSslStream_IsDigitOrDot(char c);
     110                 :             : static inline enum SolidSyslogTlsStreamErrors WolfSslStream_InitDetail(
     111                 :             :     int rc,
     112                 :             :     enum SolidSyslogTlsStreamErrors otherwise
     113                 :             : );
     114                 :             : static inline void WolfSslStream_InstallTransportCallbacks(struct SolidSyslogWolfSslStream* self);
     115                 :             : static inline void WolfSslStream_ApplyPeerVerificationPolicy(struct SolidSyslogWolfSslStream* self);
     116                 :             : static int WolfSslStream_VerifyPeer(int preverify, WOLFSSL_X509_STORE_CTX* store);
     117                 :             : static inline bool WolfSslStream_IsMissingSignerAPinCanWaive(
     118                 :             :     const struct SolidSyslogWolfSslStream* self,
     119                 :             :     const WOLFSSL_X509_STORE_CTX* store
     120                 :             : );
     121                 :             : static inline bool WolfSslStream_IsPinned(const struct SolidSyslogWolfSslStream* self);
     122                 :             : static inline bool WolfSslStream_IsPinCheckedHere(
     123                 :             :     const struct SolidSyslogWolfSslStream* self,
     124                 :             :     const WOLFSSL_X509_STORE_CTX* store
     125                 :             : );
     126                 :             : static inline bool WolfSslStream_LeafMatchesAPin(
     127                 :             :     struct SolidSyslogWolfSslStream* self,
     128                 :             :     const WOLFSSL_X509_STORE_CTX* store
     129                 :             : );
     130                 :             : static bool WolfSslStream_DigestCertificate(
     131                 :             :     void* context,
     132                 :             :     enum SolidSyslogTlsHashAlgorithm algorithm,
     133                 :             :     uint8_t* digest,
     134                 :             :     size_t* length
     135                 :             : );
     136                 :             : static inline enum wc_HashType WolfSslStream_HashTypeFor(enum SolidSyslogTlsHashAlgorithm algorithm);
     137                 :             : static inline uint32_t WolfSslStream_CheckFailedForPin(enum SolidSyslogTlsAuthorisation verdict);
     138                 :             : static inline bool WolfSslStream_StandsWithoutASigner(
     139                 :             :     struct SolidSyslogWolfSslStream* self,
     140                 :             :     const WOLFSSL_X509_STORE_CTX* store
     141                 :             : );
     142                 :             : static inline uint32_t WolfSslStream_CheckFailedFor(int error);
     143                 :             : static inline uint32_t WolfSslStream_ResolveHandshakeTimeoutMs(struct SolidSyslogWolfSslStream* self);
     144                 :             : static inline bool WolfSslStream_PerformHandshake(struct SolidSyslogWolfSslStream* self);
     145                 :             : static inline bool WolfSslStream_PinIsConfirmed(struct SolidSyslogWolfSslStream* self);
     146                 :             : static inline bool WolfSslStream_IsRetryableHandshakeError(int error);
     147                 :             : static inline bool WolfSslStream_IsHandshakeBudgetExhausted(uint32_t totalSleptMs, uint32_t budgetMs);
     148                 :             : static inline uint32_t WolfSslStream_HandshakePollMs(uint32_t totalSleptMs, uint32_t budgetMs);
     149                 :             : static inline enum SolidSyslogTlsStreamErrors WolfSslStream_RefusalDetail(const struct SolidSyslogWolfSslStream* self);
     150                 :             : static inline bool WolfSslStream_Failed(const struct SolidSyslogWolfSslStream* self, uint32_t check);
     151                 :             : static inline bool WolfSslStream_Send(struct SolidSyslogStream* base, const void* buffer, size_t size);
     152                 :             : static bool WolfSslStream_SessionIsStillOpen(struct SolidSyslogStream* base);
     153                 :             : static inline SolidSyslogSsize WolfSslStream_Read(struct SolidSyslogStream* base, void* buffer, size_t size);
     154                 :             : static inline void WolfSslStream_Close(struct SolidSyslogStream* base);
     155                 :             : static inline void WolfSslStream_ReleaseCredentials(struct SolidSyslogWolfSslStream* self);
     156                 :             : static uint32_t WolfSslStream_Version(struct SolidSyslogStream* base);
     157                 :             : static int WolfSslStream_IoSend(WOLFSSL* ssl, char* buf, int sz, void* ctx);
     158                 :             : static int WolfSslStream_IoRecv(WOLFSSL* ssl, char* buf, int sz, void* ctx);
     159                 :             : 
     160                 :         295 : void SolidSyslogWolfSslStream_Initialise(
     161                 :             :     struct SolidSyslogStream* base,
     162                 :             :     const struct SolidSyslogWolfSslStreamConfig* config
     163                 :             : )
     164                 :             : {
     165                 :         295 :     struct SolidSyslogWolfSslStream* self = WolfSslStream_SelfFromBase(base);
     166                 :         295 :     self->Base.Open = WolfSslStream_Open;
     167                 :         295 :     self->Base.Send = WolfSslStream_Send;
     168                 :         295 :     self->Base.Read = WolfSslStream_Read;
     169                 :         295 :     self->Base.Close = WolfSslStream_Close;
     170                 :         295 :     self->Base.Version = WolfSslStream_Version;
     171                 :         295 :     self->Config = *config;
     172                 :         295 :     self->Context = NULL;
     173                 :         295 :     self->Session = NULL;
     174                 :         295 :     self->CredentialsInstalled = false;
     175                 :         295 :     self->OpenHealth = (struct SolidSyslogStreamOpenHealth) {0};
     176         [ +  + ]:         295 :     if (WolfSslStream_ConfigProvidesHandshakeGetter(config) == false)
     177                 :             :     {
     178                 :             :         /* Substitute the Null Object so the bounded-handshake loop has a
     179                 :             :          * single code path regardless of whether the integrator wired
     180                 :             :          * runtime tuning. */
     181                 :         290 :         self->Config.GetHandshakeTimeoutMs = WolfSslStream_NullHandshakeTimeoutGetter;
     182                 :         290 :         self->Config.HandshakeTimeoutContext = NULL;
     183                 :             :     }
     184         [ +  + ]:         295 :     if (WolfSslStream_ConfigProvidesVersion(config) == false)
     185                 :             :     {
     186                 :         293 :         self->Config.Version = WolfSslStream_NullVersion;
     187                 :         293 :         self->Config.VersionContext = NULL;
     188                 :             :     }
     189         [ +  + ]:         295 :     if (WolfSslStream_ConfigProvidesProfile(config) == false)
     190                 :             :     {
     191                 :          10 :         self->Config.Profile = WolfSslStream_NullProfile;
     192                 :          10 :         self->Config.ProfileContext = NULL;
     193                 :             :     }
     194                 :         295 : }
     195                 :             : 
     196                 :             : /* Null Object substituted in Initialise when the integrator does not install a
     197                 :             :  * getter - returns the compile-time tunable so the bounded-handshake path is a
     198                 :             :  * single code path regardless of whether the integrator wired runtime tuning. */
     199                 :         236 : static uint32_t WolfSslStream_NullHandshakeTimeoutGetter(void* context)
     200                 :             : {
     201                 :             :     (void) context;
     202                 :         236 :     return (uint32_t) SOLIDSYSLOG_TLS_HANDSHAKE_TIMEOUT_MS;
     203                 :             : }
     204                 :             : 
     205                 :         295 : static inline bool WolfSslStream_ConfigProvidesHandshakeGetter(const struct SolidSyslogWolfSslStreamConfig* config)
     206                 :             : {
     207                 :         295 :     return config->GetHandshakeTimeoutMs != NULL;
     208                 :             : }
     209                 :             : 
     210                 :             : /* Null Object substituted at Initialise when the integrator installs no version
     211                 :             :  * function - reports an unchanging configuration, so the sender never reconnects
     212                 :             :  * on this stream's account. */
     213                 :           1 : static uint32_t WolfSslStream_NullVersion(void* context)
     214                 :             : {
     215                 :             :     (void) context;
     216                 :           1 :     return 0U;
     217                 :             : }
     218                 :             : 
     219                 :         295 : static inline bool WolfSslStream_ConfigProvidesVersion(const struct SolidSyslogWolfSslStreamConfig* config)
     220                 :             : {
     221                 :         295 :     return config->Version != NULL;
     222                 :             : }
     223                 :             : 
     224                 :             : /* Null Object substituted at Initialise when the integrator supplies no profile -
     225                 :             :  * leaves every field at the library default. */
     226                 :           1 : static void WolfSslStream_NullProfile(struct SolidSyslogWolfSslProfile* profile, void* context)
     227                 :             : {
     228                 :             :     (void) context;
     229                 :             :     (void) profile;
     230                 :           1 : }
     231                 :             : 
     232                 :         295 : static inline bool WolfSslStream_ConfigProvidesProfile(const struct SolidSyslogWolfSslStreamConfig* config)
     233                 :             : {
     234                 :         295 :     return config->Profile != NULL;
     235                 :             : }
     236                 :             : 
     237                 :        1439 : static inline struct SolidSyslogWolfSslStream* WolfSslStream_SelfFromBase(struct SolidSyslogStream* base)
     238                 :             : {
     239                 :        1439 :     return (struct SolidSyslogWolfSslStream*) base;
     240                 :             : }
     241                 :             : 
     242                 :         295 : void SolidSyslogWolfSslStream_Cleanup(struct SolidSyslogStream* base)
     243                 :             : {
     244                 :             :     /* An integrator who destroys a still-Open stream must not leak the
     245                 :             :      * underlying TLS state. */
     246                 :         295 :     WolfSslStream_Close(base);
     247                 :             :     /* Overwrite the abstract base with the shared NullStream vtable so
     248                 :             :      * use-after-destroy is a safe no-op rather than a NULL-fn-pointer crash. */
     249                 :         295 :     *base = *SolidSyslogNullStream_Get();
     250                 :         295 : }
     251                 :             : 
     252                 :             : /* Idempotent: everything freed here is set NULL as it goes, so a second Close
     253                 :             :  * finds nothing to free, and the transport's own Close is idempotent on every
     254                 :             :  * Stream impl. close_notify is sent once and not waited for - the collector's
     255                 :             :  * reply would need another service pass to arrive, and nothing is read on a
     256                 :             :  * session being torn down. */
     257                 :         499 : static inline void WolfSslStream_Close(struct SolidSyslogStream* base)
     258                 :             : {
     259                 :         499 :     struct SolidSyslogWolfSslStream* self = WolfSslStream_SelfFromBase(base);
     260         [ +  + ]:         499 :     if (self->Session != NULL)
     261                 :             :     {
     262                 :         261 :         (void) wolfSSL_shutdown(self->Session);
     263                 :         261 :         wolfSSL_free(self->Session);
     264                 :         261 :         self->Session = NULL;
     265                 :             :     }
     266                 :             :     /* The context holds the credentials' material until it is freed, so the
     267                 :             :        credentials are told the window has closed only after wolfSSL has let go
     268                 :             :        of it. */
     269         [ +  + ]:         499 :     if (self->Context != NULL)
     270                 :             :     {
     271                 :         284 :         wolfSSL_CTX_free(self->Context);
     272                 :         284 :         self->Context = NULL;
     273                 :             :     }
     274                 :         499 :     WolfSslStream_ReleaseCredentials(self);
     275                 :         499 :     SolidSyslogStream_Close(self->Config.Transport);
     276                 :         499 : }
     277                 :             : 
     278                 :             : /* Answers every Install, so the integrator is always told when the credential
     279                 :             :  * window has closed - including on the paths where Open failed part way. */
     280                 :         499 : static inline void WolfSslStream_ReleaseCredentials(struct SolidSyslogWolfSslStream* self)
     281                 :             : {
     282         [ +  + ]:         499 :     if (self->CredentialsInstalled)
     283                 :             :     {
     284                 :         284 :         self->CredentialsInstalled = false;
     285                 :         284 :         self->Config.Credentials->Release(self->Config.Credentials);
     286                 :             :     }
     287                 :         499 : }
     288                 :             : 
     289                 :           3 : static uint32_t WolfSslStream_Version(struct SolidSyslogStream* base)
     290                 :             : {
     291                 :           3 :     struct SolidSyslogWolfSslStream* self = WolfSslStream_SelfFromBase(base);
     292                 :           3 :     return self->Config.Version(self->Config.VersionContext);
     293                 :             : }
     294                 :             : 
     295                 :         291 : static inline bool WolfSslStream_Open(struct SolidSyslogStream* base, const struct SolidSyslogAddress* addr)
     296                 :             : {
     297                 :         291 :     struct SolidSyslogWolfSslStream* self = WolfSslStream_SelfFromBase(base);
     298                 :         291 :     bool ok = false;
     299         [ +  + ]:         291 :     if (WolfSslStream_IsOpen(self))
     300                 :             :     {
     301                 :             :         /* Refused by contract, not reconnected: what is held stays as it is. */
     302                 :           2 :         WolfSslStream_Report(
     303                 :             :             SOLIDSYSLOG_BAD_ARGUMENT_SEVERITY,
     304                 :             :             SOLIDSYSLOG_CAT_BAD_ARGUMENT,
     305                 :             :             SOLIDSYSLOG_TLS_STREAM_ERROR_ALREADY_OPEN
     306                 :             :         );
     307                 :             :     }
     308                 :             :     else
     309                 :             :     {
     310                 :         289 :         ok = WolfSslStream_Establish(base, addr);
     311         [ +  + ]:         289 :         if (ok)
     312                 :             :         {
     313                 :         132 :             SolidSyslogStreamOpenHealth_Opened(&self->OpenHealth);
     314                 :             :         }
     315                 :             :     }
     316                 :         291 :     return ok;
     317                 :             : }
     318                 :             : 
     319                 :             : /* Every Open that succeeds installs the credentials and every Close releases
     320                 :             :  * them, so holding them is the one mark of a session still open. */
     321                 :         291 : static inline bool WolfSslStream_IsOpen(const struct SolidSyslogWolfSslStream* self)
     322                 :             : {
     323                 :         291 :     return self->CredentialsInstalled;
     324                 :             : }
     325                 :             : 
     326                 :         289 : static inline bool WolfSslStream_Establish(struct SolidSyslogStream* base, const struct SolidSyslogAddress* addr)
     327                 :             : {
     328                 :         289 :     struct SolidSyslogWolfSslStream* self = WolfSslStream_SelfFromBase(base);
     329                 :         289 :     WolfSslStream_PullProfile(self);
     330                 :         289 :     self->RefusedChecks = 0U;
     331                 :         289 :     self->PeerPinned = false;
     332   [ +  +  +  + ]:         861 :     bool ok = SolidSyslogStream_Open(self->Config.Transport, addr) && WolfSslStream_BuildContext(self) &&
     333   [ +  +  +  + ]:         813 :               WolfSslStream_InstallCredentials(self) && WolfSslStream_CreateSession(self) &&
     334   [ +  +  +  +  :        1087 :               WolfSslStream_ApplyTlsPolicy(self) && WolfSslStream_ApplyCipherPolicy(self) &&
                   +  + ]
     335                 :         249 :               WolfSslStream_ConfigureExpectedPeerName(self);
     336         [ +  + ]:         289 :     if (ok)
     337                 :             :     {
     338                 :         241 :         WolfSslStream_InstallTransportCallbacks(self);
     339                 :         241 :         WolfSslStream_ApplyPeerVerificationPolicy(self);
     340                 :         241 :         ok = WolfSslStream_PerformHandshake(self);
     341                 :             :     }
     342         [ +  + ]:         289 :     if (!ok)
     343                 :             :     {
     344                 :         157 :         WolfSslStream_Close(base);
     345                 :             :     }
     346                 :         289 :     return ok;
     347                 :             : }
     348                 :             : 
     349                 :             : /* One snapshot per connection: every later step reads the same answer, however
     350                 :             :  * the integrator's own state moves while the handshake is in progress. */
     351                 :         289 : static inline void WolfSslStream_PullProfile(struct SolidSyslogWolfSslStream* self)
     352                 :             : {
     353                 :         289 :     self->Profile = (struct SolidSyslogWolfSslProfile) {0};
     354                 :         289 :     self->Config.Profile(&self->Profile, self->Config.ProfileContext);
     355                 :         289 : }
     356                 :             : 
     357                 :             : /* A context per connection, on the integrator's heap hint, so nothing of one
     358                 :             :  * connection's material survives into the next. wolfSSL_CTX_new_ex takes the
     359                 :             :  * method over whatever it returns, so a NULL here leaks nothing. */
     360                 :         288 : static inline bool WolfSslStream_BuildContext(struct SolidSyslogWolfSslStream* self)
     361                 :             : {
     362                 :         288 :     self->Context = wolfSSL_CTX_new_ex(wolfTLS_client_method_ex(self->Config.Heap), self->Config.Heap);
     363                 :         288 :     bool ok = self->Context != NULL;
     364         [ +  + ]:         288 :     if (!ok)
     365                 :             :     {
     366                 :           4 :         WolfSslStream_ReportOpenFailure(
     367                 :             :             self,
     368                 :             :             SOLIDSYSLOG_SEVERITY_ERROR,
     369                 :             :             SOLIDSYSLOG_CAT_TLS_STREAM_INIT_FAILED,
     370                 :             :             SOLIDSYSLOG_TLS_STREAM_ERROR_CONTEXT_INIT_FAILED
     371                 :             :         );
     372                 :             :     }
     373                 :         288 :     return ok;
     374                 :             : }
     375                 :             : 
     376                 :             : /* Asked once per connection, onto the context and before the session is created
     377                 :             :  * from it, so what is installed is what the session inherits. The flag is set
     378                 :             :  * before the call rather than after it: the contract is one Release per Install
     379                 :             :  * call whatever that call returned, which is what spares every backend a
     380                 :             :  * rollback path of its own. */
     381                 :         284 : static inline bool WolfSslStream_InstallCredentials(struct SolidSyslogWolfSslStream* self)
     382                 :             : {
     383                 :         284 :     self->Installed.TrustAnchorsInstalled = false;
     384                 :         284 :     self->Installed.Fingerprints = NULL;
     385                 :         284 :     self->Installed.FingerprintCount = 0U;
     386                 :         284 :     self->CredentialsInstalled = true;
     387                 :         284 :     bool ok = self->Config.Credentials->Install(self->Config.Credentials, self->Context, &self->Installed);
     388   [ +  +  +  + ]:         284 :     if (ok && !WolfSslStream_PeerIsAuthorisable(&self->Installed))
     389                 :             :     {
     390                 :           6 :         WolfSslStream_ReportOpenFailure(
     391                 :             :             self,
     392                 :             :             SOLIDSYSLOG_SEVERITY_ERROR,
     393                 :             :             SOLIDSYSLOG_CAT_BAD_CONFIG,
     394                 :             :             SOLIDSYSLOG_TLS_STREAM_ERROR_NO_PEER_AUTHORISATION
     395                 :             :         );
     396                 :           6 :         ok = false;
     397                 :             :     }
     398         [ +  + ]:         284 :     if (ok)
     399                 :             :     {
     400                 :         275 :         ok = WolfSslStream_FingerprintsAreUsable(self);
     401                 :             :     }
     402   [ +  +  +  + ]:         284 :     if (ok && !WolfSslStream_BuildCanEnforceBoth(&self->Installed))
     403                 :             :     {
     404                 :           4 :         WolfSslStream_ReportOpenFailure(
     405                 :             :             self,
     406                 :             :             SOLIDSYSLOG_SEVERITY_ERROR,
     407                 :             :             SOLIDSYSLOG_CAT_BAD_CONFIG,
     408                 :             :             SOLIDSYSLOG_TLS_STREAM_ERROR_PINS_WITH_ANCHORS_UNSUPPORTED
     409                 :             :         );
     410                 :           4 :         ok = false;
     411                 :             :     }
     412                 :         284 :     return ok;
     413                 :             : }
     414                 :             : 
     415                 :             : /* A peer is authorised by a chain to trust anchors or by a pinned certificate
     416                 :             :  * fingerprint, and RFC 5425 section 4.2.1 makes the second sufficient on its
     417                 :             :  * own. With neither, there is nothing to check the peer against, so the
     418                 :             :  * connection stops rather than reaching a peer this stream cannot identify. */
     419                 :         281 : static inline bool WolfSslStream_PeerIsAuthorisable(const struct SolidSyslogTlsCredentialsInstalled* installed)
     420                 :             : {
     421   [ +  +  +  + ]:         281 :     return installed->TrustAnchorsInstalled || (installed->FingerprintCount > 0U);
     422                 :             : }
     423                 :             : 
     424                 :             : /* Inspected before the handshake, so a pin that cannot match is reported as
     425                 :             :  * bad configuration rather than as a refused peer. */
     426                 :         275 : static inline bool WolfSslStream_FingerprintsAreUsable(struct SolidSyslogWolfSslStream* self)
     427                 :             : {
     428                 :         275 :     bool ok = true;
     429                 :             :     enum SolidSyslogTlsFingerprintListState state =
     430                 :         275 :         SolidSyslogTlsFingerprint_InspectList(self->Installed.Fingerprints, self->Installed.FingerprintCount);
     431         [ +  + ]:         275 :     if (state == SOLIDSYSLOG_TLS_FINGERPRINT_LIST_MALFORMED)
     432                 :             :     {
     433                 :           3 :         WolfSslStream_ReportOpenFailure(
     434                 :             :             self,
     435                 :             :             SOLIDSYSLOG_SEVERITY_ERROR,
     436                 :             :             SOLIDSYSLOG_CAT_BAD_CONFIG,
     437                 :             :             SOLIDSYSLOG_TLS_STREAM_ERROR_FINGERPRINT_MALFORMED
     438                 :             :         );
     439                 :           3 :         ok = false;
     440                 :             :     }
     441         [ +  + ]:         272 :     else if (state == SOLIDSYSLOG_TLS_FINGERPRINT_LIST_USES_SHA1)
     442                 :             :     {
     443                 :           5 :         WolfSslStream_Report(
     444                 :             :             SOLIDSYSLOG_SEVERITY_WARNING,
     445                 :             :             SOLIDSYSLOG_CAT_BAD_CONFIG,
     446                 :             :             SOLIDSYSLOG_TLS_STREAM_ERROR_FINGERPRINT_SHA1
     447                 :             :         );
     448                 :             :     }
     449                 :             :     else
     450                 :             :     {
     451                 :             :         /* Well formed, or no pins at all. */
     452                 :             :     }
     453                 :         275 :     return ok;
     454                 :             : }
     455                 :             : 
     456                 :             : /* Where anchors and pins are both installed the peer must satisfy both, and a
     457                 :             :  * build that cannot check a pin on a chain that verifies would satisfy only
     458                 :             :  * the chain - so the connection stops rather than enforce half of it. */
     459                 :         272 : static inline bool WolfSslStream_BuildCanEnforceBoth(const struct SolidSyslogTlsCredentialsInstalled* installed)
     460                 :             : {
     461   [ +  +  +  + ]:         272 :     bool both = installed->TrustAnchorsInstalled && (installed->FingerprintCount > 0U);
     462   [ +  +  +  + ]:         272 :     return !both || (SolidSyslogWolfSslStream_AnchoredPinCheck() != SOLIDSYSLOG_WOLFSSL_PIN_BESIDE_ANCHORS_REFUSED);
     463                 :             : }
     464                 :             : 
     465                 :         268 : static inline bool WolfSslStream_CreateSession(struct SolidSyslogWolfSslStream* self)
     466                 :             : {
     467                 :         268 :     self->Session = wolfSSL_new(self->Context);
     468                 :         268 :     bool ok = self->Session != NULL;
     469         [ +  + ]:         268 :     if (!ok)
     470                 :             :     {
     471                 :           7 :         WolfSslStream_ReportOpenFailure(
     472                 :             :             self,
     473                 :             :             SOLIDSYSLOG_SEVERITY_ERROR,
     474                 :             :             SOLIDSYSLOG_CAT_TLS_STREAM_INIT_FAILED,
     475                 :             :             SOLIDSYSLOG_TLS_STREAM_ERROR_SESSION_INIT_FAILED
     476                 :             :         );
     477                 :             :     }
     478                 :         268 :     return ok;
     479                 :             : }
     480                 :             : 
     481                 :             : /* TLS policy owned by the library, set on the session so it cannot leak into
     482                 :             :  * anything the integrator builds from a context of their own. The floor is
     483                 :             :  * TLS 1.2, stated here so downgrade resistance does not depend on how the
     484                 :             :  * library was built; no ceiling is set, because RFC 9662, which updates RFC
     485                 :             :  * 5425, requires TLS 1.3 to be preferred wherever it is implemented. The key
     486                 :             :  * sizes are BCP 195 section 4.5. */
     487                 :         261 : static inline bool WolfSslStream_ApplyTlsPolicy(struct SolidSyslogWolfSslStream* self)
     488                 :             : {
     489         [ +  + ]:         519 :     bool ok = (wolfSSL_SetMinVersion(self->Session, WOLFSSL_TLSV1_2) == WOLFSSL_SUCCESS) &&
     490         [ +  + ]:         515 :               (wolfSSL_SetMinRsaKey_Sz(self->Session, (short) WOLFSSL_STREAM_MIN_RSA_KEY_BITS) == WOLFSSL_SUCCESS) &&
     491   [ +  +  +  + ]:         776 :               (wolfSSL_SetMinEccKey_Sz(self->Session, (short) WOLFSSL_STREAM_MIN_ECC_KEY_BITS) == WOLFSSL_SUCCESS) &&
     492                 :         256 :               (wolfSSL_SetMinDhKey_Sz(self->Session, (word16) WOLFSSL_STREAM_MIN_DH_KEY_BITS) == WOLFSSL_SUCCESS);
     493         [ +  + ]:         261 :     if (!ok)
     494                 :             :     {
     495                 :           6 :         WolfSslStream_ReportOpenFailure(
     496                 :             :             self,
     497                 :             :             SOLIDSYSLOG_SEVERITY_ERROR,
     498                 :             :             SOLIDSYSLOG_CAT_TLS_STREAM_INIT_FAILED,
     499                 :             :             SOLIDSYSLOG_TLS_STREAM_ERROR_DEFAULTS_NOT_APPLIED
     500                 :             :         );
     501                 :             :     }
     502                 :         261 :     return ok;
     503                 :             : }
     504                 :             : 
     505                 :             : /* wolfSSL rejects a list in which it knows no name, which is reported as the
     506                 :             :  * configuration fault it is, before any handshake. */
     507                 :         255 : static inline bool WolfSslStream_ApplyCipherPolicy(struct SolidSyslogWolfSslStream* self)
     508                 :             : {
     509                 :         255 :     bool ok = true;
     510         [ +  + ]:         255 :     if (self->Profile.CipherList != NULL)
     511                 :             :     {
     512                 :          16 :         ok = wolfSSL_set_cipher_list(self->Session, self->Profile.CipherList) == WOLFSSL_SUCCESS;
     513         [ +  + ]:          16 :         if (!ok)
     514                 :             :         {
     515                 :           6 :             WolfSslStream_ReportOpenFailure(
     516                 :             :                 self,
     517                 :             :                 SOLIDSYSLOG_SEVERITY_ERROR,
     518                 :             :                 SOLIDSYSLOG_CAT_BAD_CONFIG,
     519                 :             :                 SOLIDSYSLOG_TLS_STREAM_ERROR_CIPHER_POLICY_REJECTED
     520                 :             :             );
     521                 :             :         }
     522                 :             :     }
     523                 :         255 :     return ok;
     524                 :             : }
     525                 :             : 
     526                 :             : /* The declared name is both what the certificate is checked against and what is
     527                 :             :  * sent as the server name indication. */
     528                 :         249 : static inline bool WolfSslStream_ConfigureExpectedPeerName(struct SolidSyslogWolfSslStream* self)
     529                 :             : {
     530                 :         249 :     bool ok = true;
     531                 :         249 :     const char* serverName = self->Profile.ServerName;
     532         [ +  + ]:         249 :     if (serverName == NULL)
     533                 :             :     {
     534                 :             :         /* No expected identity supplied - the handshake will accept any cert that
     535                 :             :          * chains to a trusted CA, so the peer is unverified unless a pin names
     536                 :             :          * it, which is a stronger declaration than a name. Surface it as a
     537                 :             :          * WARNING (still connect, preserving the closed-network case) rather than
     538                 :             :          * swallowing the MITM-class default silently. */
     539         [ +  + ]:          10 :         if (!WolfSslStream_IsPinned(self))
     540                 :             :         {
     541                 :           6 :             WolfSslStream_Report(
     542                 :             :                 SOLIDSYSLOG_SEVERITY_WARNING,
     543                 :             :                 SOLIDSYSLOG_CAT_BAD_CONFIG,
     544                 :             :                 SOLIDSYSLOG_TLS_STREAM_ERROR_SERVER_NAME_NOT_SET
     545                 :             :             );
     546                 :             :         }
     547                 :             :     }
     548         [ +  + ]:         239 :     else if (serverName[0] != '\0')
     549                 :             :     {
     550                 :         234 :         ok = WolfSslStream_ApplyExpectedPeerName(self, serverName);
     551                 :             :     }
     552                 :             :     else
     553                 :             :     {
     554                 :             :         /* Empty string is the deliberate opt-out: the integrator has no name to
     555                 :             :          * verify against (private CA) and has said so explicitly, so connect
     556                 :             :          * chain-only without a diagnostic. */
     557                 :             :     }
     558                 :         249 :     return ok;
     559                 :             : }
     560                 :             : 
     561                 :             : /* The declared name is both what the certificate is checked against and what is
     562                 :             :  * sent as the server name indication. Both copy the name, so both can fail on
     563                 :             :  * allocation; only the indication says so in its return. */
     564                 :         234 : static inline bool WolfSslStream_ApplyExpectedPeerName(struct SolidSyslogWolfSslStream* self, const char* serverName)
     565                 :             : {
     566                 :         234 :     bool ok = false;
     567         [ +  + ]:         234 :     if (WolfSslStream_DeclareExpectedPeer(self, serverName) != WOLFSSL_SUCCESS)
     568                 :             :     {
     569                 :           4 :         WolfSslStream_ReportOpenFailure(
     570                 :             :             self,
     571                 :             :             SOLIDSYSLOG_SEVERITY_ERROR,
     572                 :             :             SOLIDSYSLOG_CAT_BAD_CONFIG,
     573                 :             :             SOLIDSYSLOG_TLS_STREAM_ERROR_SERVER_NAME_NOT_APPLIED
     574                 :             :         );
     575                 :             :     }
     576                 :             :     else
     577                 :             :     {
     578                 :         230 :         int rc = wolfSSL_UseSNI(self->Session, WOLFSSL_SNI_HOST_NAME, serverName, (word16) strlen(serverName));
     579                 :         230 :         ok = rc == WOLFSSL_SUCCESS;
     580         [ +  + ]:         230 :         if (!ok)
     581                 :             :         {
     582                 :           4 :             WolfSslStream_ReportOpenFailure(
     583                 :             :                 self,
     584                 :             :                 SOLIDSYSLOG_SEVERITY_ERROR,
     585                 :             :                 SOLIDSYSLOG_CAT_BAD_CONFIG,
     586                 :             :                 WolfSslStream_InitDetail(rc, SOLIDSYSLOG_TLS_STREAM_ERROR_SERVER_NAME_NOT_APPLIED)
     587                 :             :             );
     588                 :             :         }
     589                 :             :     }
     590                 :         234 :     return ok;
     591                 :             : }
     592                 :             : 
     593                 :             : /* wolfSSL keeps a name and an address apart: a name is matched against the
     594                 :             :  * dNSName entries or the Common Name, and validated as a hostname when it is
     595                 :             :  * declared; an address only ever against the iPAddress entries. The contract
     596                 :             :  * wants the same split, so which call takes the declaration is decided here
     597                 :             :  * rather than left to the library's hostname rules to refuse. */
     598                 :         234 : static inline int WolfSslStream_DeclareExpectedPeer(struct SolidSyslogWolfSslStream* self, const char* serverName)
     599                 :             : {
     600                 :         244 :     return WolfSslStream_IsAddressLiteral(serverName) ? wolfSSL_check_ip_address(self->Session, serverName)
     601         [ +  + ]:         244 :                                                       : wolfSSL_check_domain_name(self->Session, serverName);
     602                 :             : }
     603                 :             : 
     604                 :             : /* A colon is only ever in an IPv6 literal, and a name made of nothing but
     605                 :             :  * digits and dots is an IPv4 one - no hostname has an all-numeric last label.
     606                 :             :  * What the literal is worth is the library's to judge against the certificate. */
     607                 :         234 : static inline bool WolfSslStream_IsAddressLiteral(const char* name)
     608                 :             : {
     609                 :         234 :     bool onlyDigitsAndDots = true;
     610                 :         234 :     bool hasColon = false;
     611         [ +  + ]:        4382 :     for (size_t index = 0U; name[index] != '\0'; index++)
     612                 :             :     {
     613         [ +  + ]:        4148 :         if (name[index] == ':')
     614                 :             :         {
     615                 :           2 :             hasColon = true;
     616                 :             :         }
     617         [ +  + ]:        4148 :         if (!WolfSslStream_IsDigitOrDot(name[index]))
     618                 :             :         {
     619                 :        3615 :             onlyDigitsAndDots = false;
     620                 :             :         }
     621                 :             :     }
     622   [ +  +  +  + ]:         234 :     return hasColon || onlyDigitsAndDots;
     623                 :             : }
     624                 :             : 
     625                 :        4148 : static inline bool WolfSslStream_IsDigitOrDot(char c)
     626                 :             : {
     627   [ +  +  +  +  :        4148 :     return ((c >= '0') && (c <= '9')) || (c == '.');
                   +  + ]
     628                 :             : }
     629                 :             : 
     630                 :             : /* wolfSSL names an allocation failure in its return, so say so rather than
     631                 :             :  * report the phase's own fault - which would send an integrator to their
     632                 :             :  * configuration or to the peer, and the allocator is neither. */
     633                 :          88 : static inline enum SolidSyslogTlsStreamErrors WolfSslStream_InitDetail(
     634                 :             :     int rc,
     635                 :             :     enum SolidSyslogTlsStreamErrors otherwise
     636                 :             : )
     637                 :             : {
     638         [ +  + ]:          88 :     return (rc == MEMORY_E) ? SOLIDSYSLOG_TLS_STREAM_ERROR_LIBRARY_OUT_OF_MEMORY : otherwise;
     639                 :             : }
     640                 :             : 
     641                 :         241 : static inline void WolfSslStream_InstallTransportCallbacks(struct SolidSyslogWolfSslStream* self)
     642                 :             : {
     643                 :         241 :     wolfSSL_SSLSetIORecv(self->Session, WolfSslStream_IoRecv);
     644                 :         241 :     wolfSSL_SSLSetIOSend(self->Session, WolfSslStream_IoSend);
     645                 :         241 :     wolfSSL_SetIOReadCtx(self->Session, self);
     646                 :         241 :     wolfSSL_SetIOWriteCtx(self->Session, self);
     647                 :         241 : }
     648                 :             : 
     649                 :             : /* The peer must present a certificate and it must verify; the callback records
     650                 :             :  * what the library found wrong, so the refusal can name the check. */
     651                 :         241 : static inline void WolfSslStream_ApplyPeerVerificationPolicy(struct SolidSyslogWolfSslStream* self)
     652                 :             : {
     653                 :         241 :     wolfSSL_set_verify(
     654                 :             :         self->Session,
     655                 :             :         WOLFSSL_VERIFY_PEER | WOLFSSL_VERIFY_FAIL_IF_NO_PEER_CERT,
     656                 :             :         WolfSslStream_VerifyPeer
     657                 :             :     );
     658                 :         241 :     wolfSSL_SetCertCbCtx(self->Session, self);
     659                 :         241 : }
     660                 :             : 
     661                 :             : /* Called for each check a certificate in the chain fails - and, on a build
     662                 :             :  * with WOLFSSL_ALWAYS_VERIFY_CB, for the leaf once it passes. The library's
     663                 :             :  * verdict stands but for one objection: with pins and no anchors there is no
     664                 :             :  * signer for any chain to reach, so a missing one is waived for a peer a pin
     665                 :             :  * names. A pin that names nothing refuses the peer whatever the chain says.
     666                 :             :  * Every check is recorded as it is decided, because the handshake's return
     667                 :             :  * carries one code and not necessarily the one the contract puts first. */
     668                 :         151 : static int WolfSslStream_VerifyPeer(int preverify, WOLFSSL_X509_STORE_CTX* store)
     669                 :             : {
     670                 :         151 :     struct SolidSyslogWolfSslStream* self = (struct SolidSyslogWolfSslStream*) store->userCtx;
     671                 :         151 :     int verdict = preverify;
     672         [ +  + ]:         151 :     if (WolfSslStream_IsMissingSignerAPinCanWaive(self, store))
     673                 :             :     {
     674                 :          46 :         verdict = 0;
     675   [ +  +  +  + ]:          46 :         if (WolfSslStream_LeafMatchesAPin(self, store) && WolfSslStream_StandsWithoutASigner(self, store))
     676                 :             :         {
     677                 :          31 :             verdict = 1;
     678                 :             :         }
     679                 :             :     }
     680                 :             :     else
     681                 :             :     {
     682                 :         105 :         self->RefusedChecks |= WolfSslStream_CheckFailedFor(store->error);
     683   [ +  +  +  + ]:         105 :         if (WolfSslStream_IsPinCheckedHere(self, store) && !WolfSslStream_LeafMatchesAPin(self, store))
     684                 :             :         {
     685                 :           6 :             verdict = 0;
     686                 :             :         }
     687                 :             :     }
     688                 :         151 :     return verdict;
     689                 :             : }
     690                 :             : 
     691                 :         151 : static inline bool WolfSslStream_IsMissingSignerAPinCanWaive(
     692                 :             :     const struct SolidSyslogWolfSslStream* self,
     693                 :             :     const WOLFSSL_X509_STORE_CTX* store
     694                 :             : )
     695                 :             : {
     696   [ +  +  +  +  :         151 :     return WolfSslStream_IsPinned(self) && !self->Installed.TrustAnchorsInstalled && (store->error == ASN_NO_SIGNER_E);
                   +  + ]
     697                 :             : }
     698                 :             : 
     699                 :         572 : static inline bool WolfSslStream_IsPinned(const struct SolidSyslogWolfSslStream* self)
     700                 :             : {
     701                 :         572 :     return self->Installed.FingerprintCount > 0U;
     702                 :             : }
     703                 :             : 
     704                 :             : /* Pins alone are checked on every call. Beside anchors, a pin is checked
     705                 :             :  * wherever the library reports a fault, so a peer the pins do not name is
     706                 :             :  * named for that first; and on a leaf that verified only where this build's
     707                 :             :  * body checks it here - a build that cannot call back for a verified leaf
     708                 :             :  * checks it once the handshake is done instead. */
     709                 :         105 : static inline bool WolfSslStream_IsPinCheckedHere(
     710                 :             :     const struct SolidSyslogWolfSslStream* self,
     711                 :             :     const WOLFSSL_X509_STORE_CTX* store
     712                 :             : )
     713                 :             : {
     714         [ +  + ]:         140 :     return WolfSslStream_IsPinned(self) &&
     715   [ +  +  +  +  :          45 :            (!self->Installed.TrustAnchorsInstalled || (store->error != 0) ||
                   +  + ]
     716                 :          10 :             (SolidSyslogWolfSslStream_AnchoredPinCheck() == SOLIDSYSLOG_WOLFSSL_PIN_BESIDE_ANCHORS_IN_CALLBACK));
     717                 :             : }
     718                 :             : 
     719                 :             : /* A pin is the digest of the leaf. wolfSSL hands the callback the whole chain
     720                 :             :  * the peer presented, leaf first, whichever certificate it is calling back
     721                 :             :  * for. */
     722                 :          78 : static inline bool WolfSslStream_LeafMatchesAPin(
     723                 :             :     struct SolidSyslogWolfSslStream* self,
     724                 :             :     const WOLFSSL_X509_STORE_CTX* store
     725                 :             : )
     726                 :             : {
     727                 :          78 :     return SolidSyslogWolfSslStream_AuthoriseLeaf(self, store->certs[0].buffer, store->certs[0].length);
     728                 :             : }
     729                 :             : 
     730                 :          87 : bool SolidSyslogWolfSslStream_AuthoriseLeaf(
     731                 :             :     struct SolidSyslogWolfSslStream* self,
     732                 :             :     const unsigned char* der,
     733                 :             :     word32 length
     734                 :             : )
     735                 :             : {
     736                 :          87 :     struct WolfSslStreamPeerCertificate leaf = {der, length, self->Config.Heap};
     737                 :          87 :     enum SolidSyslogTlsAuthorisation verdict = SolidSyslogTlsFingerprint_Authorise(
     738                 :             :         self->Installed.Fingerprints,
     739                 :             :         self->Installed.FingerprintCount,
     740                 :             :         WolfSslStream_DigestCertificate,
     741                 :             :         &leaf
     742                 :             :     );
     743                 :          87 :     self->RefusedChecks |= WolfSslStream_CheckFailedForPin(verdict);
     744                 :          87 :     self->PeerPinned = verdict == SOLIDSYSLOG_TLS_AUTHORISATION_MATCHED;
     745                 :          87 :     return self->PeerPinned;
     746                 :             : }
     747                 :             : 
     748                 :          92 : static bool WolfSslStream_DigestCertificate(
     749                 :             :     /* cppcheck-suppress constParameterCallback -- the parameter is SolidSyslogTlsDigestFunction's, declared in Core. */
     750                 :             :     void* context,
     751                 :             :     enum SolidSyslogTlsHashAlgorithm algorithm,
     752                 :             :     uint8_t* digest,
     753                 :             :     size_t* length
     754                 :             : )
     755                 :             : {
     756                 :          92 :     const struct WolfSslStreamPeerCertificate* certificate = (const struct WolfSslStreamPeerCertificate*) context;
     757                 :          92 :     enum wc_HashType type = WolfSslStream_HashTypeFor(algorithm);
     758                 :          92 :     int size = wc_HashGetDigestSize(type);
     759   [ +  +  +  +  :         181 :     bool ok = (certificate->Der != NULL) && (size > 0) &&
                   +  + ]
     760                 :          89 :               (wc_Hash_ex(
     761                 :             :                    type,
     762                 :          89 :                    certificate->Der,
     763                 :          89 :                    certificate->Length,
     764                 :             :                    digest,
     765                 :             :                    (word32) size,
     766                 :          89 :                    certificate->Heap,
     767                 :             :                    INVALID_DEVID
     768                 :             :                ) == 0);
     769         [ +  + ]:          92 :     if (ok)
     770                 :             :     {
     771                 :          88 :         *length = (size_t) size;
     772                 :             :     }
     773                 :          92 :     return ok;
     774                 :             : }
     775                 :             : 
     776                 :          92 : static inline enum wc_HashType WolfSslStream_HashTypeFor(enum SolidSyslogTlsHashAlgorithm algorithm)
     777                 :             : {
     778                 :          92 :     enum wc_HashType type = WC_HASH_TYPE_SHA256;
     779         [ +  + ]:          92 :     if (algorithm == SOLIDSYSLOG_TLS_HASH_SHA1)
     780                 :             :     {
     781                 :           7 :         type = WC_HASH_TYPE_SHA;
     782                 :             :     }
     783                 :          92 :     return type;
     784                 :             : }
     785                 :             : 
     786                 :             : /* Only a match authorises. A pin that will not parse was refused at Open, so a
     787                 :             :  * verdict that is neither a match nor an uncomputable digest is a peer the pins
     788                 :             :  * do not name. */
     789                 :          87 : static inline uint32_t WolfSslStream_CheckFailedForPin(enum SolidSyslogTlsAuthorisation verdict)
     790                 :             : {
     791                 :          87 :     uint32_t check = WOLFSSL_STREAM_CHECK_FINGERPRINT;
     792         [ +  + ]:          87 :     if (verdict == SOLIDSYSLOG_TLS_AUTHORISATION_MATCHED)
     793                 :             :     {
     794                 :          69 :         check = 0U;
     795                 :             :     }
     796         [ +  + ]:          18 :     else if (verdict == SOLIDSYSLOG_TLS_AUTHORISATION_DIGEST_UNAVAILABLE)
     797                 :             :     {
     798                 :           4 :         check = WOLFSSL_STREAM_CHECK_FINGERPRINT_DIGEST;
     799                 :             :     }
     800                 :             :     else
     801                 :             :     {
     802                 :             :         /* No pin names the peer, which the check already holds. */
     803                 :             :     }
     804                 :          87 :     return check;
     805                 :             : }
     806                 :             : 
     807                 :             : /* With no anchor wolfSSL stops at the missing signer, and what it found on the
     808                 :             :  * way - the certificate's dates, an unknown critical extension - is never
     809                 :             :  * reported. So the stream checks them itself, parsing the certificate as a
     810                 :             :  * trust anchor is loaded: its own dates and extensions, and nothing above it,
     811                 :             :  * which is what a certificate authorised by a pin is. */
     812                 :          39 : static inline bool WolfSslStream_StandsWithoutASigner(
     813                 :             :     struct SolidSyslogWolfSslStream* self,
     814                 :             :     const WOLFSSL_X509_STORE_CTX* store
     815                 :             : )
     816                 :             : {
     817                 :          39 :     const WOLFSSL_BUFFER_INFO* certificate = &store->certs[store->error_depth];
     818                 :          39 :     wc_InitDecodedCert(&self->DecodedCertificate, certificate->buffer, certificate->length, self->Config.Heap);
     819                 :          39 :     int error = wc_ParseCert(&self->DecodedCertificate, CA_TYPE, VERIFY, NULL);
     820                 :          39 :     wc_FreeDecodedCert(&self->DecodedCertificate);
     821                 :          39 :     self->RefusedChecks |= WolfSslStream_CheckFailedFor(error);
     822                 :          39 :     return error == 0;
     823                 :             : }
     824                 :             : 
     825                 :             : /* Every code the stream does not name individually is a certificate that
     826                 :             :  * fails path validation for a reason the integrator's next step - replace the
     827                 :             :  * certificate, not the network - does not depend on, so it reads as untrusted. */
     828                 :         144 : static inline uint32_t WolfSslStream_CheckFailedFor(int error)
     829                 :             : {
     830                 :         144 :     uint32_t check = 0U;
     831         [ +  + ]:         144 :     if (error == 0)
     832                 :             :     {
     833                 :             :         /* The certificate passed; nothing to record. */
     834                 :             :     }
     835   [ +  +  +  + ]:          70 :     else if ((error == DOMAIN_NAME_MISMATCH) || (error == IPADDR_MISMATCH))
     836                 :             :     {
     837                 :          18 :         check = WOLFSSL_STREAM_CHECK_NAME;
     838                 :             :     }
     839         [ +  + ]:          52 :     else if (error == ASN_AFTER_DATE_E)
     840                 :             :     {
     841                 :          16 :         check = WOLFSSL_STREAM_CHECK_NOT_AFTER;
     842                 :             :     }
     843         [ +  + ]:          36 :     else if (error == ASN_BEFORE_DATE_E)
     844                 :             :     {
     845                 :           9 :         check = WOLFSSL_STREAM_CHECK_NOT_BEFORE;
     846                 :             :     }
     847                 :             :     else
     848                 :             :     {
     849                 :          27 :         check = WOLFSSL_STREAM_CHECK_CHAIN_TRUST;
     850                 :             :     }
     851                 :         144 :     return check;
     852                 :             : }
     853                 :             : 
     854                 :             : /* Bridges the integrator-installed getter (or the Null Object substituted at
     855                 :             :  * config-copy time) to the bounded handshake deadline. Invoked at the start
     856                 :             :  * of each handshake attempt so runtime-tunable values take effect on the next
     857                 :             :  * reconnect. */
     858                 :         241 : static inline uint32_t WolfSslStream_ResolveHandshakeTimeoutMs(struct SolidSyslogWolfSslStream* self)
     859                 :             : {
     860                 :         241 :     return self->Config.GetHandshakeTimeoutMs(self->Config.HandshakeTimeoutContext);
     861                 :             : }
     862                 :             : 
     863                 :             : /* Drive wolfSSL_connect to completion under non-blocking transport. Each call
     864                 :             :  * may want more I/O while waiting for the multi-RTT handshake to progress; the
     865                 :             :  * loop sleeps briefly between attempts (avoiding a busy spin) until either the
     866                 :             :  * handshake completes, hits a hard error, or the bounded budget expires. Each
     867                 :             :  * non-success exit emits a distinct code so the integrator can tell rejection
     868                 :             :  * from timeout. */
     869                 :         241 : static inline bool WolfSslStream_PerformHandshake(struct SolidSyslogWolfSslStream* self)
     870                 :             : {
     871                 :         241 :     uint32_t budgetMs = WolfSslStream_ResolveHandshakeTimeoutMs(self);
     872                 :         241 :     uint32_t totalSleptMs = 0U;
     873                 :         241 :     bool result = false;
     874                 :         241 :     bool done = false;
     875                 :             : 
     876         [ +  + ]:        1494 :     while (!done)
     877                 :             :     {
     878                 :        1253 :         int rc = wolfSSL_connect(self->Session);
     879                 :        1253 :         int error = wolfSSL_get_error(self->Session, rc);
     880         [ +  + ]:        1253 :         if (rc == WOLFSSL_SUCCESS)
     881                 :             :         {
     882                 :         153 :             result = WolfSslStream_PinIsConfirmed(self);
     883                 :         153 :             done = true;
     884                 :             :         }
     885         [ +  + ]:        1100 :         else if (!WolfSslStream_IsRetryableHandshakeError(error))
     886                 :             :         {
     887                 :          84 :             WolfSslStream_ReportOpenFailure(
     888                 :             :                 self,
     889                 :             :                 SOLIDSYSLOG_SEVERITY_ERROR,
     890                 :             :                 SOLIDSYSLOG_CAT_TLS_STREAM_HANDSHAKE_FAILED,
     891                 :             :                 WolfSslStream_InitDetail(error, WolfSslStream_RefusalDetail(self))
     892                 :             :             );
     893                 :          84 :             done = true;
     894                 :             :         }
     895         [ +  + ]:        1016 :         else if (WolfSslStream_IsHandshakeBudgetExhausted(totalSleptMs, budgetMs))
     896                 :             :         {
     897                 :           4 :             WolfSslStream_ReportOpenFailure(
     898                 :             :                 self,
     899                 :             :                 SOLIDSYSLOG_SEVERITY_WARNING,
     900                 :             :                 SOLIDSYSLOG_CAT_TLS_STREAM_HANDSHAKE_FAILED,
     901                 :             :                 SOLIDSYSLOG_TLS_STREAM_ERROR_HANDSHAKE_TIMEOUT
     902                 :             :             );
     903                 :           4 :             done = true;
     904                 :             :         }
     905                 :             :         else
     906                 :             :         {
     907                 :        1012 :             uint32_t pollMs = WolfSslStream_HandshakePollMs(totalSleptMs, budgetMs);
     908                 :        1012 :             self->Config.Sleep(pollMs);
     909                 :        1012 :             totalSleptMs += pollMs;
     910                 :             :         }
     911                 :             :     }
     912                 :         241 :     return result;
     913                 :             : }
     914                 :             : 
     915                 :             : /* A pin is only as good as the check that read it, so a pinned peer whose
     916                 :             :  * handshake completed without a pin matching is refused rather than trusted. */
     917                 :         153 : static inline bool WolfSslStream_PinIsConfirmed(struct SolidSyslogWolfSslStream* self)
     918                 :             : {
     919   [ +  +  +  + ]:         153 :     if (WolfSslStream_IsPinned(self) && !self->PeerPinned)
     920                 :             :     {
     921                 :          25 :         SolidSyslogWolfSslStream_AuthoriseKeptPeer(self);
     922                 :             :     }
     923   [ +  +  +  + ]:         153 :     bool ok = !WolfSslStream_IsPinned(self) || self->PeerPinned;
     924         [ +  + ]:         153 :     if (!ok)
     925                 :             :     {
     926                 :          21 :         self->RefusedChecks |= WOLFSSL_STREAM_CHECK_FINGERPRINT;
     927                 :          21 :         WolfSslStream_ReportOpenFailure(
     928                 :             :             self,
     929                 :             :             SOLIDSYSLOG_SEVERITY_ERROR,
     930                 :             :             SOLIDSYSLOG_CAT_TLS_STREAM_HANDSHAKE_FAILED,
     931                 :             :             WolfSslStream_RefusalDetail(self)
     932                 :             :         );
     933                 :             :     }
     934                 :         153 :     return ok;
     935                 :             : }
     936                 :             : 
     937                 :        1100 : static inline bool WolfSslStream_IsRetryableHandshakeError(int error)
     938                 :             : {
     939   [ +  +  +  + ]:        1100 :     return (error == WOLFSSL_ERROR_WANT_READ) || (error == WOLFSSL_ERROR_WANT_WRITE);
     940                 :             : }
     941                 :             : 
     942                 :        1016 : static inline bool WolfSslStream_IsHandshakeBudgetExhausted(uint32_t totalSleptMs, uint32_t budgetMs)
     943                 :             : {
     944                 :        1016 :     return totalSleptMs >= budgetMs;
     945                 :             : }
     946                 :             : 
     947                 :             : /* The poll interval, or what is left of the budget where that is less, so the
     948                 :             :  * sleeps requested never add up to more than the budget. How long each sleep
     949                 :             :  * really takes, and the handshake itself, can still carry the attempt past it.
     950                 :             :  * Called only while budget remains. */
     951                 :        1012 : static inline uint32_t WolfSslStream_HandshakePollMs(uint32_t totalSleptMs, uint32_t budgetMs)
     952                 :             : {
     953                 :        1012 :     uint32_t pollMs = (uint32_t) HANDSHAKE_POLL_INTERVAL_MILLISECONDS;
     954                 :        1012 :     uint32_t remainingMs = budgetMs - totalSleptMs;
     955         [ +  + ]:        1012 :     if (remainingMs < pollMs)
     956                 :             :     {
     957                 :           1 :         pollMs = remainingMs;
     958                 :             :     }
     959                 :        1012 :     return pollMs;
     960                 :             : }
     961                 :             : 
     962                 :             : /* The checks accumulate, so a compound verdict resolves by precedence: an
     963                 :             :  * untrusted chain is reported ahead of anything the certificate says about
     964                 :             :  * itself, because a certificate no anchor vouches for is not made acceptable by
     965                 :             :  * the name or the dates it carries; and the dates ahead of the name, as the
     966                 :             :  * contract orders them. With no check recorded the handshake was rejected by
     967                 :             :  * the peer or the protocol, and no check of ours names it. */
     968                 :         105 : static inline enum SolidSyslogTlsStreamErrors WolfSslStream_RefusalDetail(const struct SolidSyslogWolfSslStream* self)
     969                 :             : {
     970                 :         105 :     enum SolidSyslogTlsStreamErrors detail = SOLIDSYSLOG_TLS_STREAM_ERROR_HANDSHAKE_REJECTED;
     971         [ +  + ]:         105 :     if (WolfSslStream_Failed(self, WOLFSSL_STREAM_CHECK_FINGERPRINT_DIGEST))
     972                 :             :     {
     973                 :           4 :         detail = SOLIDSYSLOG_TLS_STREAM_ERROR_FINGERPRINT_DIGEST_UNAVAILABLE;
     974                 :             :     }
     975         [ +  + ]:         101 :     else if (WolfSslStream_Failed(self, WOLFSSL_STREAM_CHECK_FINGERPRINT))
     976                 :             :     {
     977                 :          26 :         detail = SOLIDSYSLOG_TLS_STREAM_ERROR_PEER_FINGERPRINT_MISMATCHED;
     978                 :             :     }
     979         [ +  + ]:          75 :     else if (WolfSslStream_Failed(self, WOLFSSL_STREAM_CHECK_CHAIN_TRUST))
     980                 :             :     {
     981                 :          23 :         detail = SOLIDSYSLOG_TLS_STREAM_ERROR_PEER_CERTIFICATE_UNTRUSTED;
     982                 :             :     }
     983         [ +  + ]:          52 :     else if (WolfSslStream_Failed(self, WOLFSSL_STREAM_CHECK_NOT_AFTER))
     984                 :             :     {
     985                 :          13 :         detail = SOLIDSYSLOG_TLS_STREAM_ERROR_PEER_CERTIFICATE_EXPIRED;
     986                 :             :     }
     987         [ +  + ]:          39 :     else if (WolfSslStream_Failed(self, WOLFSSL_STREAM_CHECK_NOT_BEFORE))
     988                 :             :     {
     989                 :           8 :         detail = SOLIDSYSLOG_TLS_STREAM_ERROR_PEER_CERTIFICATE_NOT_YET_VALID;
     990                 :             :     }
     991         [ +  + ]:          31 :     else if (WolfSslStream_Failed(self, WOLFSSL_STREAM_CHECK_NAME))
     992                 :             :     {
     993                 :          13 :         detail = SOLIDSYSLOG_TLS_STREAM_ERROR_PEER_NAME_MISMATCHED;
     994                 :             :     }
     995                 :             :     else
     996                 :             :     {
     997                 :             :         /* Nothing recorded: the detail already holds the plain rejection. */
     998                 :             :     }
     999                 :         105 :     return detail;
    1000                 :             : }
    1001                 :             : 
    1002                 :         403 : static inline bool WolfSslStream_Failed(const struct SolidSyslogWolfSslStream* self, uint32_t check)
    1003                 :             : {
    1004                 :         403 :     return (self->RefusedChecks & check) != 0U;
    1005                 :             : }
    1006                 :             : 
    1007                 :             : /* TLS-level write failure means the session state is unrecoverable - close
    1008                 :             :  * so the StreamSender reconnect path runs on the next tick. Fail-fast is the
    1009                 :             :  * contract every TLS stream adapter honours. */
    1010                 :          20 : static inline bool WolfSslStream_Send(struct SolidSyslogStream* base, const void* buffer, size_t size)
    1011                 :             : {
    1012                 :          20 :     struct SolidSyslogWolfSslStream* self = WolfSslStream_SelfFromBase(base);
    1013                 :          20 :     bool ok = false;
    1014         [ +  + ]:          20 :     if (WolfSslStream_SessionIsStillOpen(base))
    1015                 :             :     {
    1016                 :          12 :         int rc = wolfSSL_write(self->Session, buffer, (int) size);
    1017   [ +  +  +  + ]:          12 :         ok = (rc > 0) && ((size_t) rc == size);
    1018         [ +  + ]:          12 :         if (!ok)
    1019                 :             :         {
    1020                 :           5 :             WolfSslStream_Close(base);
    1021                 :             :         }
    1022                 :             :     }
    1023                 :          20 :     return ok;
    1024                 :             : }
    1025                 :             : 
    1026                 :             : /* wolfSSL_write does not look at what the peer has sent, so a collector that
    1027                 :             :  * has closed the session - a close_notify and a FIN - still has the record
    1028                 :             :  * accepted, and the transport beneath may not see the FIN behind the unread
    1029                 :             :  * alert. Read what is waiting first: an alert or a closed transport ends the
    1030                 :             :  * session here, and Read closes it. A collector sends no application data, so
    1031                 :             :  * any that arrives is discarded; a peer that keeps sending is plainly still
    1032                 :             :  * there, and the record goes out once the bound is reached. */
    1033                 :          20 : static bool WolfSslStream_SessionIsStillOpen(struct SolidSyslogStream* base)
    1034                 :             : {
    1035                 :             :     unsigned char discard[WOLFSSL_STREAM_DISCARD_BYTES];
    1036                 :          20 :     SolidSyslogSsize pending = WolfSslStream_Read(base, discard, sizeof(discard));
    1037                 :          20 :     uint32_t reads = 1U;
    1038   [ +  +  +  + ]:          35 :     while ((pending > 0) && (reads < WOLFSSL_STREAM_MAX_READS_BEFORE_WRITE))
    1039                 :             :     {
    1040                 :          15 :         pending = WolfSslStream_Read(base, discard, sizeof(discard));
    1041                 :          15 :         reads++;
    1042                 :             :     }
    1043                 :          20 :     return pending >= 0;
    1044                 :             : }
    1045                 :             : 
    1046                 :             : /* wolfSSL_read has two distinct outcomes worth keeping straight:
    1047                 :             :  *   1. Steady-state read: bytes available -> positive count; nothing to read
    1048                 :             :  *      right now -> WANT_READ -> return 0, mirroring the transport contract.
    1049                 :             :  *   2. Any other return (close_notify, a fatal alert, a hard transport error,
    1050                 :             :  *      a session that is not open) is fatal under fail-fast semantics - close
    1051                 :             :  *      internally; the caller reopens and store-and-forward replays. */
    1052                 :          42 : static inline SolidSyslogSsize WolfSslStream_Read(struct SolidSyslogStream* base, void* buffer, size_t size)
    1053                 :             : {
    1054                 :          42 :     struct SolidSyslogWolfSslStream* self = WolfSslStream_SelfFromBase(base);
    1055                 :          42 :     int rc = wolfSSL_read(self->Session, buffer, (int) size);
    1056                 :          42 :     SolidSyslogSsize result = -1;
    1057         [ +  + ]:          42 :     if (rc > 0)
    1058                 :             :     {
    1059                 :          19 :         result = (SolidSyslogSsize) rc;
    1060                 :             :     }
    1061         [ +  + ]:          23 :     else if (wolfSSL_get_error(self->Session, rc) == WOLFSSL_ERROR_WANT_READ)
    1062                 :             :     {
    1063                 :          12 :         result = 0;
    1064                 :             :     }
    1065                 :             :     else
    1066                 :             :     {
    1067                 :          11 :         WolfSslStream_Close(base);
    1068                 :             :     }
    1069                 :          42 :     return result;
    1070                 :             : }
    1071                 :             : 
    1072                 :             : /* cppcheck-suppress constParameterCallback -- wolfSSL's CallbackIOSend type declares buf without const. */
    1073                 :         362 : static int WolfSslStream_IoSend(WOLFSSL* ssl, char* buf, int sz, void* ctx)
    1074                 :             : {
    1075                 :         362 :     struct SolidSyslogWolfSslStream* self = (struct SolidSyslogWolfSslStream*) ctx;
    1076                 :             :     (void) ssl;
    1077         [ +  + ]:         362 :     return SolidSyslogStream_Send(self->Config.Transport, buf, (size_t) sz) ? sz : WOLFSSL_CBIO_ERR_GENERAL;
    1078                 :             : }
    1079                 :             : 
    1080                 :         799 : static int WolfSslStream_IoRecv(WOLFSSL* ssl, char* buf, int sz, void* ctx)
    1081                 :             : {
    1082                 :         799 :     struct SolidSyslogWolfSslStream* self = (struct SolidSyslogWolfSslStream*) ctx;
    1083                 :             :     (void) ssl;
    1084                 :         799 :     SolidSyslogSsize n = SolidSyslogStream_Read(self->Config.Transport, buf, (size_t) sz);
    1085                 :         799 :     int result = WOLFSSL_CBIO_ERR_GENERAL;
    1086         [ +  + ]:         799 :     if (n > 0)
    1087                 :             :     {
    1088                 :         791 :         result = (int) n;
    1089                 :             :     }
    1090         [ +  + ]:           8 :     else if (n == 0)
    1091                 :             :     {
    1092                 :           4 :         result = WOLFSSL_CBIO_ERR_WANT_READ;
    1093                 :             :     }
    1094                 :             :     else
    1095                 :             :     {
    1096                 :             :         /* Transport-level error; a general failure aborts the handshake / read. */
    1097                 :             :     }
    1098                 :         799 :     return result;
    1099                 :             : }
        

Generated by: LCOV version 2.0-1