From 9b0eb3e5a37e2b291421a43cd8d540bcc009cbf5 Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Thu, 13 Jan 2022 14:35:24 +0200 Subject: [PATCH] Add ISC_R_TLSBADPEERCERT error code to the TLS related code This commit adds support for ISC_R_TLSBADPEERCERT error code, which is supposed to be used to signal for TLS peer certificates verification in dig and other code. The support for this error code is added to our TLS and TLS DNS implementations. This commit also adds isc_nm_verify_tls_peer_result_string() function which is supposed to be used to get a textual description of the reason for getting a ISC_R_TLSBADPEERCERT error. --- lib/isc/include/isc/netmgr.h | 11 ++++++++++ lib/isc/include/isc/result.h | 3 ++- lib/isc/netmgr/http.c | 32 ++++++++++++++++++++++++++++ lib/isc/netmgr/netmgr-int.h | 10 +++++++++ lib/isc/netmgr/netmgr.c | 27 ++++++++++++++++++++++++ lib/isc/netmgr/tlsdns.c | 41 +++++++++++++++++++++++++++++++++--- lib/isc/netmgr/tlsstream.c | 31 +++++++++++++++++++++++---- lib/isc/result.c | 2 ++ 8 files changed, 149 insertions(+), 8 deletions(-) diff --git a/lib/isc/include/isc/netmgr.h b/lib/isc/include/isc/netmgr.h index 7f14b3e15a..f2ead2688d 100644 --- a/lib/isc/include/isc/netmgr.h +++ b/lib/isc/include/isc/netmgr.h @@ -697,6 +697,17 @@ isc_nm_has_encryption(const isc_nmhandle_t *handle); * \li 'handle' is a valid netmgr handle object. */ +const char * +isc_nm_verify_tls_peer_result_string(const isc_nmhandle_t *handle); +/*%< + * Returns user-readable message describing TLS peer's certificate + * validation result. Returns 'NULL' for the transport handles for + * which peer verification was not performed. + * + * Requires: + * \li 'handle' is a valid netmgr handle object. + */ + void isc_nm_task_enqueue(isc_nm_t *mgr, isc_task_t *task, int threadid); /*%< diff --git a/lib/isc/include/isc/result.h b/lib/isc/include/isc/result.h index 62c90173e2..dd03b9c00c 100644 --- a/lib/isc/include/isc/result.h +++ b/lib/isc/include/isc/result.h @@ -92,7 +92,8 @@ typedef enum isc_result { ISC_R_DEFAULT, /*%< default */ ISC_R_IPV4PREFIX, /*%< IPv4 prefix */ ISC_R_TLSERROR, /*%< TLS error */ - ISC_R_HTTP2ALPNERROR, /*%< ALPN for HTTP/2 failed */ + ISC_R_TLSBADPEERCERT, /*%< TLS peer certificate verification failed */ + ISC_R_HTTP2ALPNERROR, /*%< ALPN for HTTP/2 failed */ DNS_R_LABELTOOLONG = 1 << 16, DNS_R_BADESCAPE, diff --git a/lib/isc/netmgr/http.c b/lib/isc/netmgr/http.c index 94d7a9aa97..e66d37e8ac 100644 --- a/lib/isc/netmgr/http.c +++ b/lib/isc/netmgr/http.c @@ -1348,6 +1348,8 @@ transport_connect_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) { INSIST(http_sock->h2.connect.uri != NULL); http_sock->tid = transp_sock->tid; + http_sock->h2.connect.tls_peer_verify_string = + isc_nm_verify_tls_peer_result_string(handle); if (result != ISC_R_SUCCESS) { goto error; } @@ -2909,6 +2911,36 @@ isc__nm_http_has_encryption(const isc_nmhandle_t *handle) { return (isc_nm_socket_type(session->handle) == isc_nm_tlssocket); } +const char * +isc__nm_http_verify_tls_peer_result_string(const isc_nmhandle_t *handle) { + isc_nmsocket_t *sock = NULL; + isc_nm_http_session_t *session; + + REQUIRE(VALID_NMHANDLE(handle)); + REQUIRE(VALID_NMSOCK(handle->sock)); + REQUIRE(handle->sock->type == isc_nm_httpsocket); + + sock = handle->sock; + session = sock->h2.session; + + /* + * In the case of a low-level error the session->handle is not + * attached nor session object is created. + */ + if (session == NULL && sock->h2.connect.tls_peer_verify_string != NULL) + { + return (sock->h2.connect.tls_peer_verify_string); + } + + if (session == NULL) { + return (NULL); + } + + INSIST(VALID_HTTP2_SESSION(session)); + + return (isc_nm_verify_tls_peer_result_string(session->handle)); +} + void isc__nm_http_set_tlsctx(isc_nmsocket_t *listener, isc_tlsctx_t *tlsctx) { REQUIRE(VALID_NMSOCK(listener)); diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h index 9ffcf1bee0..3c31b2cc76 100644 --- a/lib/isc/netmgr/netmgr-int.h +++ b/lib/isc/netmgr/netmgr-int.h @@ -908,6 +908,7 @@ typedef struct isc_nmsocket_h2 { isc_tlsctx_t *tlsctx; isc_sockaddr_t local_interface; void *cstream; + const char *tls_peer_verify_string; } connect; } isc_nmsocket_h2_t; #endif /* HAVE_LIBNGHTTP2 */ @@ -1633,6 +1634,9 @@ isc__nm_tlsdns_cancelread(isc_nmhandle_t *handle); * Stop reading on a connected TLSDNS handle. */ +const char * +isc__nm_tlsdns_verify_tls_peer_result_string(const isc_nmhandle_t *handle); + void isc__nm_async_tlsdnscycle(isc__networker_t *worker, isc__netievent_t *ev0); void @@ -1723,6 +1727,9 @@ isc__nm_tls_cleartimeout(isc_nmhandle_t *handle); * around. */ +const char * +isc__nm_tls_verify_tls_peer_result_string(const isc_nmhandle_t *handle); + void isc__nmhandle_tls_keepalive(isc_nmhandle_t *handle, bool value); /*%< @@ -1788,6 +1795,9 @@ isc__nm_http_has_encryption(const isc_nmhandle_t *handle); void isc__nm_http_set_maxage(isc_nmhandle_t *handle, const uint32_t ttl); +const char * +isc__nm_http_verify_tls_peer_result_string(const isc_nmhandle_t *handle); + void isc__nm_async_httpsend(isc__networker_t *worker, isc__netievent_t *ev0); diff --git a/lib/isc/netmgr/netmgr.c b/lib/isc/netmgr/netmgr.c index 8a730af309..cffd2dd9b0 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -3694,6 +3694,33 @@ isc_nmsocket_set_tlsctx(isc_nmsocket_t *listener, isc_tlsctx_t *tlsctx) { }; } +const char * +isc_nm_verify_tls_peer_result_string(const isc_nmhandle_t *handle) { + isc_nmsocket_t *sock; + + REQUIRE(VALID_NMHANDLE(handle)); + REQUIRE(VALID_NMSOCK(handle->sock)); + + sock = handle->sock; + switch (sock->type) { + case isc_nm_tlsdnssocket: + return (isc__nm_tlsdns_verify_tls_peer_result_string(handle)); + break; +#if HAVE_LIBNGHTTP2 + case isc_nm_tlssocket: + return (isc__nm_tls_verify_tls_peer_result_string(handle)); + break; + case isc_nm_httpsocket: + return (isc__nm_http_verify_tls_peer_result_string(handle)); + break; +#endif /* HAVE_LIBNGHTTP2 */ + default: + break; + } + + return (NULL); +} + #ifdef NETMGR_TRACE /* * Dump all active sockets in netmgr. We output to stderr diff --git a/lib/isc/netmgr/tlsdns.c b/lib/isc/netmgr/tlsdns.c index 1ce3c9c247..d0f7922e89 100644 --- a/lib/isc/netmgr/tlsdns.c +++ b/lib/isc/netmgr/tlsdns.c @@ -80,6 +80,17 @@ tls_cycle(isc_nmsocket_t *sock); static void call_pending_send_callbacks(isc_nmsocket_t *sock, const isc_result_t result); +static bool +peer_verification_has_failed(isc_nmsocket_t *sock) { + if (sock->tls.tls != NULL && sock->tls.state == TLS_STATE_HANDSHAKE && + SSL_get_verify_result(sock->tls.tls) != X509_V_OK) + { + return (true); + } + + return (false); +} + static bool can_log_tlsdns_quota(void) { isc_stdtime_t now, last; @@ -806,9 +817,14 @@ isc__nm_tlsdns_failed_read_cb(isc_nmsocket_t *sock, isc_result_t result, isc__nm_stop_reading(sock); if (sock->tls.pending_req != NULL) { + isc_result_t failure_result = ISC_R_CANCELED; isc__nm_uvreq_t *req = sock->tls.pending_req; sock->tls.pending_req = NULL; - isc__nm_failed_connect_cb(sock, req, ISC_R_CANCELED, async); + + if (peer_verification_has_failed(sock)) { + failure_result = ISC_R_TLSBADPEERCERT; + } + isc__nm_failed_connect_cb(sock, req, failure_result, async); } if (!sock->recv_read) { @@ -2018,11 +2034,14 @@ isc__nm_tlsdns_shutdown(isc_nmsocket_t *sock) { * TLS handshake to complete */ if (sock->tls.pending_req != NULL) { + isc_result_t result = ISC_R_CANCELED; isc__nm_uvreq_t *req = sock->tls.pending_req; sock->tls.pending_req = NULL; - isc__nm_failed_connect_cb(sock, req, ISC_R_CANCELED, - false); + if (peer_verification_has_failed(sock)) { + result = ISC_R_TLSBADPEERCERT; + } + isc__nm_failed_connect_cb(sock, req, result, false); return; } @@ -2103,6 +2122,22 @@ isc__nm_tlsdns_xfr_allowed(isc_nmsocket_t *sock) { return (sock->tls.alpn_negotiated); } +const char * +isc__nm_tlsdns_verify_tls_peer_result_string(const isc_nmhandle_t *handle) { + isc_nmsocket_t *sock = NULL; + + REQUIRE(VALID_NMHANDLE(handle)); + REQUIRE(VALID_NMSOCK(handle->sock)); + REQUIRE(handle->sock->type == isc_nm_tlsdnssocket); + + sock = handle->sock; + if (sock->tls.tls == NULL) { + return (NULL); + } + + return (isc_tls_verify_peer_result_string(sock->tls.tls)); +} + void isc__nm_async_tlsdns_set_tlsctx(isc_nmsocket_t *listener, isc_tlsctx_t *tlsctx, const int tid) { diff --git a/lib/isc/netmgr/tlsstream.c b/lib/isc/netmgr/tlsstream.c index 55767d48bc..11b895bab1 100644 --- a/lib/isc/netmgr/tlsstream.c +++ b/lib/isc/netmgr/tlsstream.c @@ -44,11 +44,16 @@ #define TLS_BUF_SIZE (UINT16_MAX) static isc_result_t -tls_error_to_result(int tls_err) { +tls_error_to_result(const int tls_err, const int tls_state, isc_tls_t *tls) { switch (tls_err) { case SSL_ERROR_ZERO_RETURN: return (ISC_R_EOF); case SSL_ERROR_SSL: + if (tls != NULL && tls_state < TLS_IO && + SSL_get_verify_result(tls) != X509_V_OK) + { + return (ISC_R_TLSBADPEERCERT); + } return (ISC_R_TLSERROR); default: return (ISC_R_UNEXPECTED); @@ -324,14 +329,15 @@ tls_try_handshake(isc_nmsocket_t *sock) { rv = SSL_do_handshake(sock->tlsstream.tls); if (rv == 1) { + isc_result_t result = ISC_R_SUCCESS; INSIST(SSL_is_init_finished(sock->tlsstream.tls) == 1); INSIST(sock->statichandle == NULL); tlshandle = isc__nmhandle_get(sock, &sock->peer, &sock->iface); if (sock->tlsstream.server) { - sock->listener->accept_cb(tlshandle, ISC_R_SUCCESS, + sock->listener->accept_cb(tlshandle, result, sock->listener->accept_cbarg); } else { - tls_call_connect_cb(sock, tlshandle, ISC_R_SUCCESS); + tls_call_connect_cb(sock, tlshandle, result); } isc_nmhandle_detach(&tlshandle); sock->tlsstream.state = TLS_IO; @@ -514,7 +520,8 @@ tls_do_bio(isc_nmsocket_t *sock, isc_region_t *received_data, } return; default: - result = tls_error_to_result(tls_status); + result = tls_error_to_result(tls_status, sock->tlsstream.state, + sock->tlsstream.tls); break; } @@ -1081,6 +1088,22 @@ isc__nmhandle_tls_keepalive(isc_nmhandle_t *handle, bool value) { } } +const char * +isc__nm_tls_verify_tls_peer_result_string(const isc_nmhandle_t *handle) { + isc_nmsocket_t *sock = NULL; + + REQUIRE(VALID_NMHANDLE(handle)); + REQUIRE(VALID_NMSOCK(handle->sock)); + REQUIRE(handle->sock->type == isc_nm_tlssocket); + + sock = handle->sock; + if (sock->tlsstream.tls == NULL) { + return (NULL); + } + + return (isc_tls_verify_peer_result_string(sock->tlsstream.tls)); +} + static void tls_init_listener_tlsctx(isc_nmsocket_t *listener, isc_tlsctx_t *ctx) { size_t nworkers; diff --git a/lib/isc/result.c b/lib/isc/result.c index 8fb3f8bbde..9e089dff07 100644 --- a/lib/isc/result.c +++ b/lib/isc/result.c @@ -91,6 +91,7 @@ static const char *description[ISC_R_NRESULTS] = { [ISC_R_DEFAULT] = "default", [ISC_R_IPV4PREFIX] = "IPv4 prefix", [ISC_R_TLSERROR] = "TLS error", + [ISC_R_TLSBADPEERCERT] = "TLS peer certificate verification failed", [ISC_R_HTTP2ALPNERROR] = "ALPN for HTTP/2 failed", [DNS_R_LABELTOOLONG] = "label too long", @@ -337,6 +338,7 @@ static const char *identifier[ISC_R_NRESULTS] = { [ISC_R_DEFAULT] = "ISC_R_DEFAULT", [ISC_R_IPV4PREFIX] = "ISC_R_IPV4PREFIX", [ISC_R_TLSERROR] = "ISC_R_TLSERROR", + [ISC_R_TLSBADPEERCERT] = "ISC_R_TLSBADPEERCERT", [ISC_R_HTTP2ALPNERROR] = "ISC_R_HTTP2ALPNERROR", [DNS_R_LABELTOOLONG] = "DNS_R_LABELTOOLONG", [DNS_R_BADESCAPE] = "DNS_R_BADESCAPE",