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.
} \
}
+#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;
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
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
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
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'.
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;
char *keyfile;
char *cafile;
char *hostname;
+ char *ciphers;
+ uint32_t protocol_versions;
+ ternary_t prefer_server_ciphers;
} tls;
struct {
char *endpoint;
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);
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);
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);
}
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);
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);
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);
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));
}
&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();