]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
XoT: add support for client-side TLS parameters
authorArtem Boldariev <artem@boldariev.com>
Mon, 29 Nov 2021 21:09:51 +0000 (23:09 +0200)
committerArtem Boldariev <artem@boldariev.com>
Wed, 1 Dec 2021 10:00:28 +0000 (12:00 +0200)
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.

bin/named/transportconf.c
lib/dns/include/dns/transport.h
lib/dns/transport.c
lib/dns/xfrin.c

index 618696bb7aa90bff117993b1706f02267eebd524..61c7462f0b064a6fc2b6f3f16237411ae4a84afe 100644 (file)
                }                                                 \
        }
 
+#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
index 63526fc8e302efd8d84d02ba2eae30b10d70e109..91fe38263072ec028ff7365ca097161101049beb 100644 (file)
@@ -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'.
index 64fe5f6ce457d0de5ee80ac5b49b863cfe48072b..9f7e2bdc79f3ccaca600976ef08cc4be3ee734d0 100644 (file)
@@ -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));
 }
index dfe43fc47b837e926644ef080df19e563e6fad3c..bdda30cbe2ecff85544b08a6a4ed7215e0920029 100644 (file)
@@ -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();