]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Add ISC_R_TLSBADPEERCERT error code to the TLS related code
authorArtem Boldariev <artem@boldariev.com>
Thu, 13 Jan 2022 12:35:24 +0000 (14:35 +0200)
committerArtem Boldariev <artem@boldariev.com>
Mon, 28 Mar 2022 12:32:30 +0000 (15:32 +0300)
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
lib/isc/include/isc/result.h
lib/isc/netmgr/http.c
lib/isc/netmgr/netmgr-int.h
lib/isc/netmgr/netmgr.c
lib/isc/netmgr/tlsdns.c
lib/isc/netmgr/tlsstream.c
lib/isc/result.c

index 9489c15578deeb29f33bf8ccb5b47981ab7275e1..e339084e96744f8e0a1baba385723bdd68604d73 100644 (file)
@@ -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);
 /*%<
index 62c90173e26a47c7fdd4d796b636d1bf098d4428..dd03b9c00c2701e73bf813f3dc41f4198762b130 100644 (file)
@@ -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,
index 28294a02aece2ca663a0d5106e9b6f7c93f2aeeb..6684373a686c9bc4d831ed5eaf9826fd70b60ba6 100644 (file)
@@ -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,
index ce1d35f7f1e85dc4e3b39411617f00c7f7d7b02b..d1965d12fd3f18e95d50b67e67229041d124cadb 100644 (file)
@@ -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);
 
index 5f4e7631ab9fb95a9c4db90e3b4b8749ce10f030..2bce1c7d7caa13147ff920eb5a9c18d33ea2c565 100644 (file)
@@ -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
index b13df1f45d9bc92687128211d88e6f82c247f17f..e5ed8dd9bf7444a48d5cce7e5786185a92d7323f 100644 (file)
@@ -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));
+}
index feeaf242cea43b1e8abf6a31b7ea2dee5b9732e7..d0c4868faefdb78676f9fcd2b53fac1e983c0396 100644 (file)
 #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));
+}
index 8fb3f8bbdea4be11bb5a95bf1a2361cc3df11bd9..9e089dff07365a674c7c96697d320614e1820b21 100644 (file)
@@ -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",