diff --git a/include/zbxcomms.h b/include/zbxcomms.h index 62f346f..e4281e9 100644 --- a/include/zbxcomms.h +++ b/include/zbxcomms.h @@ -157,6 +157,7 @@ zbx_config_comms_args_t; # include # include # include +# include #endif typedef struct @@ -166,6 +167,7 @@ typedef struct gnutls_psk_client_credentials_t psk_client_creds; gnutls_psk_server_credentials_t psk_server_creds; unsigned char psk_buf[HOST_TLS_PSK_LEN / 2]; + gnutls_typed_vdata_st verification_data[2]; #elif defined(HAVE_OPENSSL) SSL *ctx; #if defined(HAVE_OPENSSL_WITH_PSK) diff --git a/src/libs/zbxcomms/tls_gnutls.c b/src/libs/zbxcomms/tls_gnutls.c index e5c35a2..ae98e50 100644 --- a/src/libs/zbxcomms/tls_gnutls.c +++ b/src/libs/zbxcomms/tls_gnutls.c @@ -1205,7 +1205,8 @@ static const char *tls_error_string(int err) * or empty string if not important) or PSK * * (in hex-string) to connect with depending on value * * of 'tls_connect'. * - * server_name - [IN] optional server name indication for TLS * + * server_name - [IN] optional server name indication for TLS and for * + * check of peer certificate subject alternative name * * event - [OUT] may be NULL for blocking TLS handshake, otherwise * * informs caller to wait for POLLIN or POLLOUT and * * retry function to complete async TLS handshake * @@ -1241,6 +1242,7 @@ int zbx_tls_connect(zbx_socket_t *s, unsigned int tls_connect, const char *tls_a if (NULL == s->tls_ctx) { + zabbix_log(LOG_LEVEL_WARNING, "Untested patch for x509 SAN and purpose verification is installed!"); s->tls_ctx = zbx_malloc(s->tls_ctx, sizeof(zbx_tls_context_t)); s->tls_ctx->ctx = NULL; s->tls_ctx->psk_client_creds = NULL; @@ -1352,11 +1354,43 @@ int zbx_tls_connect(zbx_socket_t *s, unsigned int tls_connect, const char *tls_a } } - if (NULL != server_name && ZBX_TCP_SEC_UNENCRYPTED != tls_connect && - GNUTLS_E_SUCCESS != gnutls_server_name_set( s->tls_ctx->ctx, GNUTLS_NAME_DNS, - server_name, strlen(server_name))) + if (ZBX_TCP_SEC_UNENCRYPTED != tls_connect) { - zabbix_log(LOG_LEVEL_WARNING, "cannot set %s tls host name", server_name); + /* configure gnutls verification parameters */ + /* "recommended way in GnuTLS 3.5.0 and later instead of calling */ + /* gnutls_certificate_verify_peers or gnutls_x509_crt_check_key_purpose, */ + /* gnutls_x509_crt_check_hostname2 and gnutls_x509_crt_check_ip" */ + gnutls_typed_vdata_st data[2]; /* should be malloc'd so it stays with context whole time... */ + + /* taken from gnutls doc/examples/verify.c */ + s->tls_ctx->verification_data[0].type = GNUTLS_DT_DNS_HOSTNAME; /* misleading, accepts IP too */ + s->tls_ctx->verification_data[0].data = (void *)s->peer; /* DNS vs IP is decided by flags */ + s->tls_ctx->verification_data[0].size = 0; + + /* certificate must be good for server use, like X509_check_purpose() */ + s->tls_ctx->verification_data[1].type = GNUTLS_DT_KEY_PURPOSE_OID; + s->tls_ctx->verification_data[1].data = (void *)GNUTLS_KP_TLS_WWW_SERVER; + s->tls_ctx->verification_data[1].size = 0; + + /* server is connected via DNS name */ + if (NULL != server_name) + { + gnutls_session_set_verify_cert2(s->tls_ctx->ctx, s->tls_ctx->verification_data, 2, + GNUTLS_VERIFY_DO_NOT_ALLOW_IP_MATCHES); + } + + /* server is connected via IP address */ + if (NULL != server_name) + { + gnutls_session_set_verify_cert2(s->tls_ctx->ctx, s->tls_ctx->verification_data, 2, 0); + } + + /* send server DNS name in HTTPS SNI */ + if (NULL != server_name && GNUTLS_E_SUCCESS != gnutls_server_name_set(s->tls_ctx->ctx, + GNUTLS_NAME_DNS, server_name, strlen(server_name))) + { + zabbix_log(LOG_LEVEL_WARNING, "cannot set %s tls host name", server_name); + } } if (SUCCEED == ZBX_CHECK_LOG_LEVEL(LOG_LEVEL_TRACE)) diff --git a/src/libs/zbxcomms/tls_openssl.c b/src/libs/zbxcomms/tls_openssl.c index 40394a3..710c714 100644 --- a/src/libs/zbxcomms/tls_openssl.c +++ b/src/libs/zbxcomms/tls_openssl.c @@ -1574,7 +1574,8 @@ static const char *tls_error_string(int err) * or empty string if not important) or PSK * * (in hex-string) to connect with depending on value * * of 'tls_connect'. * - * server_name - [IN] optional server name indication for TLS * + * server_name - [IN] optional server name indication for TLS and for * + * check of peer certificate subject alternative name * * event - [OUT] may be NULL for blocking TLS handshake, otherwise * * informs caller to wait for POLLIN or POLLOUT and * * retry function to complete async TLS handshake * @@ -1597,6 +1598,7 @@ int zbx_tls_connect(zbx_socket_t *s, unsigned int tls_connect, const char *tls_a if (NULL == s->tls_ctx) { + zabbix_log(LOG_LEVEL_WARNING, "Untested patch for x509 SAN and purpose verification is active!"); s->tls_ctx = zbx_malloc(s->tls_ctx, sizeof(zbx_tls_context_t)); s->tls_ctx->ctx = NULL; initialized = 0; @@ -1702,10 +1704,44 @@ int zbx_tls_connect(zbx_socket_t *s, unsigned int tls_connect, const char *tls_a if (0 == initialized) { - if (NULL != server_name && ZBX_TCP_SEC_UNENCRYPTED != tls_connect && 1 != SSL_set_tlsext_host_name( - s->tls_ctx->ctx, server_name)) + if (ZBX_TCP_SEC_UNENCRYPTED != tls_connect) { - zabbix_log(LOG_LEVEL_WARNING, "cannot set %s tls host name", server_name); + /* configure openssl verification parameters */ + /* "Applications are encouraged to set verify param instead of explicitely */ + /* calling X509_check_purpose, X509_check_host and X509_check_ip_asc" */ + X509_VERIFY_PARAM *param; + + param = SSL_get0_param(s->tls_ctx->ctx); /* The returned pointer must not be freed */ + + /* certificate must be good for server use, like X509_check_purpose() */ + if (1 != X509_VERIFY_PARAM_set_purpose(param, X509_PURPOSE_SSL_SERVER)) + { + *error = zbx_strdup(*error, "cannot set TLS verification purpose"); + goto out; + } + + /* check subject alternative name only, no legacy fallback to common name */ + X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NEVER_CHECK_SUBJECT); + + /* server is connected via DNS name, provided in server_name */ + if (NULL != server_name && 1 != X509_VERIFY_PARAM_set1_host(param, server_name, 0)) + { + *error = zbx_strdup(*error, "cannot set TLS SAN verification DNS name"); + goto out; + } + + /* server is connected via IP address, available in s->peer */ + if (NULL == server_name && 1 != X509_VERIFY_PARAM_set1_ip_asc(param, s->peer)) + { + *error = zbx_strdup(*error, "cannot set TLS SAN verification IP address"); + goto out; + } + + /* send server DNS name in HTTPS SNI */ + if (NULL != server_name && 1 != SSL_set_tlsext_host_name(s->tls_ctx->ctx, server_name)) + { + zabbix_log(LOG_LEVEL_WARNING, "cannot set %s tls host name", server_name); + } } /* set our connected TCP socket to TLS context */