@cryptotaxi247 / netdata / commits / c6555117a

Fix based on Coverity and Sonar audits (part 12) (#22340)

* mqtt_wss: enable TLS hostname verification via SSL_set1_host Sonar c:S5527 (CRITICAL vulnerability): mqtt_wss_connect() configured the peer certificate chain check and sent SNI via SSL_set_tlsext_host_name(), but it never asked OpenSSL to verify that the certificate's CN/SAN matched the requested hostname. A certificate issued by a trusted CA for any other host would pass the existing checks, allowing a man-in-the-middle to impersonate the target. Add SSL_set1_host(client->ssl, client->target_host) before SSL_connect() so OpenSSL's certificate verification also matches the peer's identity against the requested hostname. The call is guarded by the existing MQTT_WSS_SSL_DONT_CHECK_CERTS opt-out so callers that explicitly want to skip cert checks are unchanged. Failure to configure the hostname is reported and returns the same TLS-setup error code path as the SNI failure above; silently leaving the verification disabled would re-introduce the vulnerability. * mqtt_wss: extend MQTT_WSS_SSL_ALLOW_SELF_SIGNED to cover hostname / IP mismatch The previous commit added SSL_set1_host() to enable TLS hostname verification. This catches a real MITM gap on production Cloud connections, but it tightens the rules for on-prem deployments where the cert's CN/SAN may legitimately not match the hostname the agent connects to: - the cert was issued for a different DNS name and reached via an alias / CNAME / load-balancer entry, - the cert was issued for a hostname but the agent is configured with the IP literal, - the cert is self-signed without proper SAN entries (CN-only). These were already the kind of setups MQTT_WSS_SSL_ALLOW_SELF_SIGNED was meant for. Extend cert_verify_callback so this flag also accepts X509_V_ERR_HOSTNAME_MISMATCH and X509_V_ERR_IP_ADDRESS_MISMATCH (in addition to the existing self-signed-leaf override). Default deployments (no flag) keep strict hostname verification; on-prem operators who set MQTT_WSS_SSL_ALLOW_SELF_SIGNED keep working without having to fall back to the much blunter MQTT_WSS_SSL_DONT_CHECK_CERTS. * mqtt_wss: address PR-review findings Use X509_VERIFY_PARAM_set1_host through SSL_get0_param for OpenSSL 1.0.2 compatibility while preserving hostname verification behavior. * mqtt_wss: support IP literals in TLS hostname verification X509_VERIFY_PARAM_set1_host() only matches against the certificate's dNSName SAN. When the agent connects to an IP literal (e.g. an on-prem deployment configured with 10.0.0.5 instead of a hostname), the verification fails even if the certificate has a valid iPAddress SAN because the IP literal is interpreted as a DNS name. Try X509_VERIFY_PARAM_set1_ip_asc() first -- it parses the input as an IP and matches against the iPAddress SAN, returning 0 when the input is not a valid IP. If that fails (the typical DNS hostname case), fall back to X509_VERIFY_PARAM_set1_host(). Both code paths are guarded by the existing MQTT_WSS_SSL_DONT_CHECK_CERTS opt-out, and the MQTT_WSS_SSL_ALLOW_SELF_SIGNED override already covers X509_V_ERR_IP_ADDRESS_MISMATCH for setups whose certs do not match. --------- Co-authored-by: Costa Tsaousis <costa@netdata.cloud>

Stelios Fragkakis committed May 4, 2026 at 16:02 UTC c6555117a50af24bb3b4f5c0ada1404ebdbd4e27
1 file changed +44 -6
src/aclk/mqtt_websockets/mqtt_wss_client.c
+44 -6
@@ -245,12 +245,35 @@ static int cert_verify_callback(int preverify_ok, X509_STORE_CTX *ctx)
245 netdata_ssl_log_verify_error(ctx);
246 }
247
248 - if (!preverify_ok && err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT &&
249 - client->ssl_flags & MQTT_WSS_SSL_ALLOW_SELF_SIGNED)
250 - {
251 - preverify_ok = 1;
252 - nd_log(NDLS_DAEMON, NDLP_ERR, "Self Signed Certificate Accepted as the connection was "
253 - "requested with MQTT_WSS_SSL_ALLOW_SELF_SIGNED");
248 + if (!preverify_ok && (client->ssl_flags & MQTT_WSS_SSL_ALLOW_SELF_SIGNED)) {
249 + // MQTT_WSS_SSL_ALLOW_SELF_SIGNED means "this connection accepts a
250 + // certificate that wouldn't pass full validation". Cover the errors
251 + // that on-prem deployments routinely hit:
252 + // - leaf is self-signed (no CA at all)
253 + // - cert subject does not match the configured hostname/IP
254 + // (DNS aliases, IP-only access, certs without proper SAN)
255 + switch (err) {
256 + case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
257 + preverify_ok = 1;
258 + nd_log(NDLS_DAEMON, NDLP_ERR,
259 + "Self Signed Certificate Accepted as the connection was "
260 + "requested with MQTT_WSS_SSL_ALLOW_SELF_SIGNED");
261 + break;
262 + case X509_V_ERR_HOSTNAME_MISMATCH:
263 + preverify_ok = 1;
264 + nd_log(NDLS_DAEMON, NDLP_ERR,
265 + "Certificate hostname mismatch accepted as the connection "
266 + "was requested with MQTT_WSS_SSL_ALLOW_SELF_SIGNED");
267 + break;
268 + case X509_V_ERR_IP_ADDRESS_MISMATCH:
269 + preverify_ok = 1;
270 + nd_log(NDLS_DAEMON, NDLP_ERR,
271 + "Certificate IP address mismatch accepted as the connection "
272 + "was requested with MQTT_WSS_SSL_ALLOW_SELF_SIGNED");
273 + break;
274 + default:
275 + break;
276 + }
277 }
278
279 return preverify_ok;
@@ -414,6 +437,21 @@ int mqtt_wss_connect(
437 return -7;
438 }
439
440 + if (!(client->ssl_flags & MQTT_WSS_SSL_DONT_CHECK_CERTS)) {
441 + // target_host may be either a DNS hostname or an IP literal.
442 + // X509_VERIFY_PARAM_set1_ip_asc() parses the string as an IP and
443 + // matches against the cert's iPAddress SAN; it returns 0 if the
444 + // string is not a valid IP. X509_VERIFY_PARAM_set1_host() matches
445 + // against the dNSName SAN. Try the IP path first; if the input is
446 + // not an IP literal, fall back to hostname matching.
447 + X509_VERIFY_PARAM *param = SSL_get0_param(client->ssl);
448 + if (!X509_VERIFY_PARAM_set1_ip_asc(param, client->target_host) &&
449 + !X509_VERIFY_PARAM_set1_host(param, client->target_host, 0)) {
450 + nd_log(NDLS_DAEMON, NDLP_ERR, "Error setting TLS hostname verification host");
451 + return -7;
452 + }
453 + }
454 +
455 result = SSL_connect(client->ssl);
456 if (result != -1 && result != 1) {
457 nd_log(NDLS_DAEMON, NDLP_ERR, "SSL could not connect");