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.
This commit is contained in:
Artem Boldariev 2022-01-13 14:35:24 +02:00
parent 71cf8fa5ac
commit 783663db80
8 changed files with 149 additions and 8 deletions

View 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);
/*%<

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

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

View 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);

View 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

View 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));
}

View file

@ -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));
}

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