From: Artem Boldariev Date: Mon, 29 Nov 2021 21:09:51 +0000 (+0200) Subject: XoT: add support for client-side TLS parameters X-Git-Tag: v9.17.21~10^2~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7843fb4ece2c8f8e67c98118adf265164f1a3957;p=thirdparty%2Fbind9.git XoT: add support for client-side TLS parameters This commit adds support for client-side TLS parameters to XoT. Prior to this commit all client-side TLS contexts were using default parameters only, ignoring the options from the BIND's configuration file. Currently, the following 'tls' parameters are supported: - protocols; - ciphers; - prefer-server-ciphers. --- diff --git a/bin/named/transportconf.c b/bin/named/transportconf.c index 618696bb7aa..61c7462f0b0 100644 --- a/bin/named/transportconf.c +++ b/bin/named/transportconf.c @@ -47,6 +47,47 @@ } \ } +#define parse_transport_tls_versions(map, transport, name, setter) \ + { \ + const cfg_obj_t *obj = NULL; \ + cfg_map_get(map, name, &obj); \ + if (obj != NULL) { \ + { \ + uint32_t tls_protos = 0; \ + const cfg_listelt_t *proto = NULL; \ + INSIST(obj != NULL); \ + for (proto = cfg_list_first(obj); proto != 0; \ + proto = cfg_list_next(proto)) { \ + const cfg_obj_t *tls_proto_obj = \ + cfg_listelt_value(proto); \ + const char *tls_sver = \ + cfg_obj_asstring( \ + tls_proto_obj); \ + const isc_tls_protocol_version_t ver = \ + isc_tls_protocol_name_to_version( \ + tls_sver); \ + INSIST(ver != \ + ISC_TLS_PROTO_VER_UNDEFINED); \ + INSIST(isc_tls_protocol_supported( \ + ver)); \ + tls_protos |= ver; \ + } \ + if (tls_protos != 0) { \ + setter(transport, tls_protos); \ + } \ + } \ + } \ + } + +#define parse_transport_bool_option(map, transport, name, setter) \ + { \ + const cfg_obj_t *obj = NULL; \ + cfg_map_get(map, name, &obj); \ + if (obj != NULL) { \ + setter(transport, cfg_obj_asboolean(obj)); \ + } \ + } + static isc_result_t add_doh_transports(const cfg_obj_t *transportlist, dns_transport_list_t *list) { const cfg_obj_t *doh = NULL; @@ -71,6 +112,13 @@ add_doh_transports(const cfg_obj_t *transportlist, dns_transport_list_t *list) { dns_transport_set_keyfile); parse_transport_option(doh, transport, "cert-file", dns_transport_set_certfile); + parse_transport_tls_versions(doh, transport, "protocols", + dns_transport_set_tls_versions); + parse_transport_option(doh, transport, "ciphers", + dns_transport_set_ciphers); + parse_transport_bool_option( + doh, transport, "prefer-server-ciphers", + dns_transport_set_prefer_server_ciphers) #if 0 /* * The following two options need to remain unavailable until @@ -121,6 +169,13 @@ add_tls_transports(const cfg_obj_t *transportlist, dns_transport_list_t *list) { dns_transport_set_keyfile); parse_transport_option(tls, transport, "cert-file", dns_transport_set_certfile); + parse_transport_tls_versions(tls, transport, "protocols", + dns_transport_set_tls_versions); + parse_transport_option(tls, transport, "ciphers", + dns_transport_set_ciphers); + parse_transport_bool_option( + tls, transport, "prefer-server-ciphers", + dns_transport_set_prefer_server_ciphers) #if 0 /* * The following two options need to remain unavailable until diff --git a/lib/dns/include/dns/transport.h b/lib/dns/include/dns/transport.h index 63526fc8e30..91fe3826307 100644 --- a/lib/dns/include/dns/transport.h +++ b/lib/dns/include/dns/transport.h @@ -52,9 +52,20 @@ char * dns_transport_get_endpoint(dns_transport_t *transport); dns_http_mode_t dns_transport_get_mode(dns_transport_t *transport); +char * +dns_transport_get_ciphers(dns_transport_t *transport); +uint32_t +dns_transport_get_tls_versions(const dns_transport_t *transport); +bool +dns_transport_get_prefer_server_ciphers(const dns_transport_t *transport, + bool *preferp); /*%< * Getter functions: return the type, cert file, key file, CA file, * hostname, HTTP endpoint, or HTTP mode (GET or POST) for 'transport'. + * + * dns_transport_get_prefer_server_ciphers() returns 'true' is value + * was set, 'false' otherwise. The actual value is returned via + * 'preferp' pointer. */ void @@ -69,6 +80,14 @@ void dns_transport_set_endpoint(dns_transport_t *transport, const char *endpoint); void dns_transport_set_mode(dns_transport_t *transport, dns_http_mode_t mode); +void +dns_transport_set_ciphers(dns_transport_t *transport, const char *ciphers); +void +dns_transport_set_tls_versions(dns_transport_t *transport, + const uint32_t tls_versions); +void +dns_transport_set_prefer_server_ciphers(dns_transport_t *transport, + const bool prefer); /*%< * Setter functions: set the type, cert file, key file, CA file, * hostname, HTTP endpoint, or HTTP mode (GET or POST) for 'transport'. diff --git a/lib/dns/transport.c b/lib/dns/transport.c index 64fe5f6ce45..9f7e2bdc79f 100644 --- a/lib/dns/transport.c +++ b/lib/dns/transport.c @@ -36,6 +36,8 @@ struct dns_transport_list { dns_rbt_t *transports[DNS_TRANSPORT_COUNT]; }; +typedef enum ternary { ter_none = 0, ter_true = 1, ter_false = 2 } ternary_t; + struct dns_transport { unsigned int magic; isc_refcount_t references; @@ -46,6 +48,9 @@ struct dns_transport { char *keyfile; char *cafile; char *hostname; + char *ciphers; + uint32_t protocol_versions; + ternary_t prefer_server_ciphers; } tls; struct { char *endpoint; @@ -151,6 +156,10 @@ dns_transport_set_certfile(dns_transport_t *transport, const char *certfile) { REQUIRE(transport->type == DNS_TRANSPORT_TLS || transport->type == DNS_TRANSPORT_HTTP); + if (transport->tls.certfile != NULL) { + isc_mem_free(transport->mctx, transport->tls.certfile); + } + if (certfile != NULL) { transport->tls.certfile = isc_mem_strdup(transport->mctx, certfile); @@ -163,6 +172,10 @@ dns_transport_set_keyfile(dns_transport_t *transport, const char *keyfile) { REQUIRE(transport->type == DNS_TRANSPORT_TLS || transport->type == DNS_TRANSPORT_HTTP); + if (transport->tls.keyfile != NULL) { + isc_mem_free(transport->mctx, transport->tls.keyfile); + } + if (keyfile != NULL) { transport->tls.keyfile = isc_mem_strdup(transport->mctx, keyfile); @@ -175,6 +188,10 @@ dns_transport_set_cafile(dns_transport_t *transport, const char *cafile) { REQUIRE(transport->type == DNS_TRANSPORT_TLS || transport->type == DNS_TRANSPORT_HTTP); + if (transport->tls.cafile != NULL) { + isc_mem_free(transport->mctx, transport->tls.cafile); + } + if (cafile != NULL) { transport->tls.cafile = isc_mem_strdup(transport->mctx, cafile); } @@ -186,6 +203,10 @@ dns_transport_set_hostname(dns_transport_t *transport, const char *hostname) { REQUIRE(transport->type == DNS_TRANSPORT_TLS || transport->type == DNS_TRANSPORT_HTTP); + if (transport->tls.hostname != NULL) { + isc_mem_free(transport->mctx, transport->tls.hostname); + } + if (hostname != NULL) { transport->tls.hostname = isc_mem_strdup(transport->mctx, hostname); @@ -197,6 +218,10 @@ dns_transport_set_endpoint(dns_transport_t *transport, const char *endpoint) { REQUIRE(VALID_TRANSPORT(transport)); REQUIRE(transport->type == DNS_TRANSPORT_HTTP); + if (transport->doh.endpoint != NULL) { + isc_mem_free(transport->mctx, transport->doh.endpoint); + } + if (endpoint != NULL) { transport->doh.endpoint = isc_mem_strdup(transport->mctx, endpoint); @@ -211,6 +236,76 @@ dns_transport_set_mode(dns_transport_t *transport, dns_http_mode_t mode) { transport->doh.mode = mode; } +void +dns_transport_set_tls_versions(dns_transport_t *transport, + const uint32_t tls_versions) { + REQUIRE(VALID_TRANSPORT(transport)); + REQUIRE(transport->type == DNS_TRANSPORT_HTTP || + transport->type == DNS_TRANSPORT_TLS); + + transport->tls.protocol_versions = tls_versions; +} + +uint32_t +dns_transport_get_tls_versions(const dns_transport_t *transport) { + REQUIRE(VALID_TRANSPORT(transport)); + + return (transport->tls.protocol_versions); +} + +void +dns_transport_set_ciphers(dns_transport_t *transport, const char *ciphers) { + REQUIRE(VALID_TRANSPORT(transport)); + REQUIRE(transport->type == DNS_TRANSPORT_TLS || + transport->type == DNS_TRANSPORT_HTTP); + + if (transport->tls.ciphers != NULL) { + isc_mem_free(transport->mctx, transport->tls.ciphers); + } + + if (ciphers != NULL) { + transport->tls.ciphers = isc_mem_strdup(transport->mctx, + ciphers); + } +} + +char * +dns_transport_get_ciphers(dns_transport_t *transport) { + REQUIRE(VALID_TRANSPORT(transport)); + + return (transport->tls.ciphers); +} + +void +dns_transport_set_prefer_server_ciphers(dns_transport_t *transport, + const bool prefer) { + REQUIRE(VALID_TRANSPORT(transport)); + REQUIRE(transport->type == DNS_TRANSPORT_TLS || + transport->type == DNS_TRANSPORT_HTTP); + + transport->tls.prefer_server_ciphers = prefer ? ter_true : ter_false; +} + +bool +dns_transport_get_prefer_server_ciphers(const dns_transport_t *transport, + bool *preferp) { + REQUIRE(VALID_TRANSPORT(transport)); + REQUIRE(preferp != NULL); + if (transport->tls.prefer_server_ciphers == ter_none) { + return (false); + } else if (transport->tls.prefer_server_ciphers == ter_true) { + *preferp = true; + return (true); + } else if (transport->tls.prefer_server_ciphers == ter_false) { + *preferp = false; + return (true); + } + + INSIST(0); + ISC_UNREACHABLE(); + return false; +} + static void transport_destroy(dns_transport_t *transport) { isc_refcount_destroy(&transport->references); @@ -231,6 +326,9 @@ transport_destroy(dns_transport_t *transport) { if (transport->tls.certfile != NULL) { isc_mem_free(transport->mctx, transport->tls.certfile); } + if (transport->tls.ciphers != NULL) { + isc_mem_free(transport->mctx, transport->tls.ciphers); + } isc_mem_putanddetach(&transport->mctx, transport, sizeof(*transport)); } diff --git a/lib/dns/xfrin.c b/lib/dns/xfrin.c index dfe43fc47b8..bdda30cbe2e 100644 --- a/lib/dns/xfrin.c +++ b/lib/dns/xfrin.c @@ -946,13 +946,35 @@ xfrin_start(dns_xfrin_ctx_t *xfr) { &xfr->primaryaddr, xfrin_connect_done, connect_xfr, 30000, 0); break; - case DNS_TRANSPORT_TLS: + case DNS_TRANSPORT_TLS: { + uint32_t tls_versions; + const char *ciphers; + bool prefer_server_ciphers; CHECK(isc_tlsctx_createclient(&xfr->tlsctx)); + if (xfr->transport != NULL) { + tls_versions = + dns_transport_get_tls_versions(xfr->transport); + if (tls_versions != 0) { + isc_tlsctx_set_protocols(xfr->tlsctx, + tls_versions); + } + ciphers = dns_transport_get_ciphers(xfr->transport); + if (ciphers != NULL) { + isc_tlsctx_set_cipherlist(xfr->tlsctx, ciphers); + } + + if (dns_transport_get_prefer_server_ciphers( + xfr->transport, &prefer_server_ciphers)) + { + isc_tlsctx_prefer_server_ciphers( + xfr->tlsctx, prefer_server_ciphers); + } + } isc_tlsctx_enable_dot_client_alpn(xfr->tlsctx); isc_nm_tlsdnsconnect(xfr->netmgr, &xfr->sourceaddr, &xfr->primaryaddr, xfrin_connect_done, connect_xfr, 30000, 0, xfr->tlsctx); - break; + } break; default: INSIST(0); ISC_UNREACHABLE();