diff --git a/src/request-internal.c b/src/request-internal.c index cc082039..8b5dafdb 100644 --- a/src/request-internal.c +++ b/src/request-internal.c @@ -211,6 +211,7 @@ network_req_init(getdns_network_req *net_req, getdns_dns_req *owner, net_req->debug_tls_auth_status = GETDNS_AUTH_NONE; net_req->debug_tls_peer_cert.size = 0; net_req->debug_tls_peer_cert.data = NULL; + net_req->debug_tls_version = NULL; net_req->debug_udp = 0; /* Scheduling, touch only via _getdns_netreq_change_state! diff --git a/src/stub.c b/src/stub.c index 7879e0c7..ad4708c7 100644 --- a/src/stub.c +++ b/src/stub.c @@ -1687,12 +1687,14 @@ upstream_write_cb(void *userarg) remove_from_write_queue(upstream, netreq); if (netreq->owner->return_call_reporting && - netreq->upstream->tls_obj && - netreq->debug_tls_peer_cert.data == NULL && - (cert = SSL_get_peer_certificate(netreq->upstream->tls_obj))) { - netreq->debug_tls_peer_cert.size = i2d_X509( - cert, &netreq->debug_tls_peer_cert.data); - X509_free(cert); + netreq->upstream->tls_obj) { + if (netreq->debug_tls_peer_cert.data == NULL && + (cert = SSL_get_peer_certificate(netreq->upstream->tls_obj))) { + netreq->debug_tls_peer_cert.size = i2d_X509( + cert, &netreq->debug_tls_peer_cert.data); + X509_free(cert); + } + netreq->debug_tls_version = SSL_get_version(netreq->upstream->tls_obj); } /* Need this because auth status is reset on connection close */ netreq->debug_tls_auth_status = netreq->upstream->tls_auth_state; diff --git a/src/tools/getdns_server_mon.c b/src/tools/getdns_server_mon.c index c3dcad9b..300bffbf 100644 --- a/src/tools/getdns_server_mon.c +++ b/src/tools/getdns_server_mon.c @@ -54,6 +54,13 @@ #define EDNS0_PADDING_CODE 12 +static const char TLS13_CIPHER_SUITE[] = + "TLS13-AES-256-GCM-SHA384:" + "TLS13-CHACHA20-POLY1305-SHA256:" + "TLS13-AES-128-GCM-SHA256:" + "TLS13-AES-128-CCM-8-SHA256:" + "TLS13-AES-128-CCM-SHA256"; + #define EXAMPLE_PIN "pin-sha256=\"E9CZ9INDbd+2eRQozYqqbQ2yXLVKB9+xcprMF+44U1g=\"" /* Plugin exit values */ @@ -298,6 +305,7 @@ static void usage() " Check server support for EDNS0 padding in TLS\n" " Special blocksize values are 0 = off,\n" " 1 = sensible default.\n" +" tls-1.3 Check whether server supports TLS 1.3\n" "\n" "Enabling monitoring mode ensures output messages and exit statuses conform\n" "to the requirements of monitoring plugins (www.monitoring-plugins.org).\n", @@ -386,7 +394,8 @@ static exit_value search(const struct test_info_s *test_info, const char *name, uint16_t type, getdns_dict *extensions, - getdns_dict **response) + getdns_dict **response, + getdns_return_t *getdns_return) { getdns_return_t ret; getdns_dict *search_extensions = @@ -394,6 +403,8 @@ static exit_value search(const struct test_info_s *test_info, /* We always turn on the return_call_reporting extension. */ if ((ret = getdns_dict_set_int(search_extensions, "return_call_reporting", GETDNS_EXTENSION_TRUE)) != GETDNS_RETURN_GOOD) { + if (getdns_return) + *getdns_return = ret; fprintf(test_info->errout, "Cannot set return call reporting: %s (%d)", getdns_get_errorstr_by_id(ret), @@ -417,11 +428,15 @@ static exit_value search(const struct test_info_s *test_info, if (!extensions) getdns_dict_destroy(search_extensions); if (ret != GETDNS_RETURN_GOOD) { - fprintf(test_info->errout, - "Error resolving '%s': %s (%d)", - name, - getdns_get_errorstr_by_id(ret), - ret); + if (getdns_return) { + *getdns_return = ret; + } else { + fprintf(test_info->errout, + "Error resolving '%s': %s (%d)", + name, + getdns_get_errorstr_by_id(ret), + ret); + } return EXIT_CRITICAL; } @@ -431,6 +446,8 @@ static exit_value search(const struct test_info_s *test_info, getdns_pretty_print_dict(*response)); } + if (getdns_return) + *getdns_return = ret; return EXIT_OK; } @@ -694,7 +711,7 @@ static exit_value search_check(const struct test_info_s *test_info, exit_value xit; getdns_dict *resp; - if ((xit = search(test_info, lookup_name, lookup_type, NULL, &resp)) != EXIT_OK) + if ((xit = search(test_info, lookup_name, lookup_type, NULL, &resp, NULL)) != EXIT_OK) return xit; if ((xit = check_result(test_info, resp)) != EXIT_OK) @@ -1170,7 +1187,8 @@ static exit_value test_dnssec_validate(const struct test_info_s *test_info, "dnssec-failed.org", GETDNS_RRTYPE_A, NULL, - &response)) != EXIT_OK) + &response, + NULL)) != EXIT_OK) return xit; uint32_t error_id, rcode; @@ -1209,7 +1227,8 @@ static exit_value test_dnssec_validate(const struct test_info_s *test_info, "dnssec-failed.org", GETDNS_RRTYPE_A, extensions, - &response2)) != EXIT_OK) + &response2, + NULL)) != EXIT_OK) return xit; getdns_dict_destroy(extensions); @@ -1238,6 +1257,100 @@ static exit_value test_dnssec_validate(const struct test_info_s *test_info, return EXIT_OK; } +static exit_value test_tls13(const struct test_info_s *test_info, + char ** av) +{ + exit_value xit; + getdns_return_t ret; + + if (*av) { + fputs("tls-1.3 takes no arguments", test_info->errout); + return EXIT_USAGE; + } + + /* + * Set cipher list to TLS 1.3-only ciphers. If we are using + * an OpenSSL version that doesn't support TLS 1.3 this will cause + * a Bad Context error on the lookup. + */ + if ((ret = getdns_context_set_tls_cipher_list(test_info->context, TLS13_CIPHER_SUITE)) != GETDNS_RETURN_GOOD) { + fprintf(test_info->errout, + "Cannot set TLS 1.3 cipher list: %s (%d)", + getdns_get_errorstr_by_id(ret), + ret); + } + + getdns_dict *response; + + if ((xit = search(test_info, + DEFAULT_LOOKUP_NAME, + DEFAULT_LOOKUP_TYPE, + NULL, + &response, + &ret)) != EXIT_OK) { + if (xit == EXIT_CRITICAL) { + if (ret == GETDNS_RETURN_BAD_CONTEXT) { + fputs("Your version of OpenSSL does not support TLS 1.3 ciphers. You need at least OpenSSL 1.1.1.", + test_info->errout); + return EXIT_UNKNOWN; + } else { + fputs("Cannot establish TLS 1.3 connection.", + test_info->errout); + return EXIT_CRITICAL; + } + } else { + return xit; + } + } + + if ((xit = get_report_info(test_info, response, NULL, NULL, NULL)) != EXIT_OK) + return xit; + + getdns_list *l; + + if ((ret = getdns_dict_get_list(response, "call_reporting", &l)) != GETDNS_RETURN_GOOD) { + fprintf(test_info->errout, + "Cannot get call report: %s (%d)", + getdns_get_errorstr_by_id(ret), + ret); + return EXIT_UNKNOWN; + } + + getdns_dict *d; + + if ((ret = getdns_list_get_dict(l, 0, &d)) != GETDNS_RETURN_GOOD) { + fprintf(test_info->errout, + "Cannot get call report first item: %s (%d)", + getdns_get_errorstr_by_id(ret), + ret); + return EXIT_UNKNOWN; + } + + /* Search is forced TLS, so tls_version flag must exist. */ + getdns_bindata *version; + + if ((ret = getdns_dict_get_bindata(d, "tls_version", &version)) != GETDNS_RETURN_GOOD) { + fprintf(test_info->errout, + "Cannot get TLS version: %s (%d)", + getdns_get_errorstr_by_id(ret), + ret); + return EXIT_UNKNOWN; + } + + const char TLS_1_3_SIG[] = "TLSv1.3"; + + if (strncmp((const char *)version->data, TLS_1_3_SIG, sizeof(TLS_1_3_SIG) - 1) != 0) { + fputs("Server does not support TLS 1.3", test_info->errout); + return EXIT_CRITICAL; + } + + if ((xit = check_result(test_info, response)) != EXIT_OK) + return xit; + + fputs("Server supports TLS 1.3", test_info->errout); + return EXIT_OK; +} + static struct test_funcs_s { const char *name; @@ -1254,6 +1367,7 @@ static struct test_funcs_s { "tls-padding", true, false, test_padding }, { "keepalive", false, true, test_keepalive }, { "dnssec-validate", false, false, test_dnssec_validate }, + { "tls-1.3", true, false, test_tls13 }, { NULL, false, false, NULL } }; diff --git a/src/types-internal.h b/src/types-internal.h index 3199b134..76615625 100644 --- a/src/types-internal.h +++ b/src/types-internal.h @@ -242,6 +242,7 @@ typedef struct getdns_network_req uint64_t debug_end_time; getdns_auth_state_t debug_tls_auth_status; getdns_bindata debug_tls_peer_cert; + const char *debug_tls_version; size_t debug_udp; /* When more space is needed for the wire_data response than is diff --git a/src/util-internal.c b/src/util-internal.c index 3ec15995..0bceb002 100644 --- a/src/util-internal.c +++ b/src/util-internal.c @@ -920,6 +920,12 @@ _getdns_create_call_reporting_dict( getdns_dict_destroy(netreq_debug); return NULL; } + if (getdns_dict_util_set_string(netreq_debug, "tls_version", + netreq->debug_tls_version)){ + + getdns_dict_destroy(netreq_debug); + return NULL; + } if (getdns_dict_set_bindata(netreq_debug, "tls_peer_cert", &netreq->debug_tls_peer_cert)) {