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",