diff --git a/lib/isc/include/isc/netmgr.h b/lib/isc/include/isc/netmgr.h index 9489c15578..e339084e96 100644 --- a/lib/isc/include/isc/netmgr.h +++ b/lib/isc/include/isc/netmgr.h @@ -620,6 +620,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 tid); /*%< 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 28294a02ae..6684373a68 100644 --- a/lib/isc/netmgr/http.c +++ b/lib/isc/netmgr/http.c @@ -1350,6 +1350,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; } @@ -2904,6 +2906,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)); +} + static const bool base64url_validation_table[256] = { false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, diff --git a/lib/isc/netmgr/netmgr-int.h b/lib/isc/netmgr/netmgr-int.h index ce1d35f7f1..d1965d12fd 100644 --- a/lib/isc/netmgr/netmgr-int.h +++ b/lib/isc/netmgr/netmgr-int.h @@ -873,6 +873,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 */ @@ -1586,6 +1587,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 @@ -1670,6 +1674,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); /*%< @@ -1731,6 +1738,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 5f4e7631ab..2bce1c7d7c 100644 --- a/lib/isc/netmgr/netmgr.c +++ b/lib/isc/netmgr/netmgr.c @@ -3536,6 +3536,33 @@ isc_nm_getnworkers(const isc_nm_t *netmgr) { return (netmgr->nworkers); } +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 b13df1f45d..e5ed8dd9bf 100644 --- a/lib/isc/netmgr/tlsdns.c +++ b/lib/isc/netmgr/tlsdns.c @@ -77,6 +77,17 @@ async_tlsdns_cycle(isc_nmsocket_t *sock) __attribute__((unused)); static isc_result_t tls_cycle(isc_nmsocket_t *sock); +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; @@ -795,9 +806,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) { @@ -1986,11 +2002,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; } @@ -2070,3 +2089,19 @@ 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)); +} diff --git a/lib/isc/netmgr/tlsstream.c b/lib/isc/netmgr/tlsstream.c index feeaf242ce..d0c4868fae 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); @@ -315,14 +320,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; @@ -505,7 +511,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; } @@ -1059,3 +1066,19 @@ isc__nmhandle_tls_keepalive(isc_nmhandle_t *handle, bool value) { isc_nmhandle_keepalive(sock->outerhandle, 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)); +} 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",