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.
This commit is contained in:
Artem Boldariev 2021-11-29 23:09:51 +02:00
parent 17d9a74681
commit 7843fb4ece
4 changed files with 196 additions and 2 deletions

View file

@ -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

View 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'.

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

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