From: Marek VavruĊĦa Date: Tue, 31 Jul 2018 22:12:25 +0000 (-0700) Subject: daemon: allow opportunistic DNS over TLS to origins X-Git-Tag: v3.2.0~17^2~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a1ba84584dadee09991869ac64e15b971853fec5;p=thirdparty%2Fknot-resolver.git daemon: allow opportunistic DNS over TLS to origins This commit allows opportunistic DNS over TLS to origins configured as supporting DoT on port 853. It also adds interface for clearing configured TLS clients to allow runtime reconfiguration. The general mode of operation is as follows: 1. Produce a new outgoing query 2. Check if the selected upstream address has configured TLS support on port 853 2a. If it does: upgrade to DNS over TLS, it cannot be downgraded from this point 2b. If not: continue with preferred protocol This allows further automatic discovery as in [1], but right now it has to be configured manually. [1]: https://tools.ietf.org/id/draft-bortzmeyer-dprive-resolver-to-auth-00.html (cherrypicked from cloudflare branch, need to be adapted) --- diff --git a/daemon/bindings.c b/daemon/bindings.c index 259c6c7a4..1dbac917e 100644 --- a/daemon/bindings.c +++ b/daemon/bindings.c @@ -599,6 +599,46 @@ static int net_tls_client(lua_State *L) return 1; } +static int net_tls_client_clear(lua_State *L) +{ + struct engine *engine = engine_luaget(L); + if (!engine) { + return 0; + } + + struct network *net = &engine->net; + if (!net) { + return 0; + } + + if (lua_gettop(L) != 1 || !lua_isstring(L, 1)) { + format_error(L, "net.tls_client_clear() requires one parameter (\"address\")"); + lua_error(L); + } + + const char *full_addr = lua_tostring(L, 1); + + char addr[INET6_ADDRSTRLEN]; + uint16_t port = 0; + if (kr_straddr_split(full_addr, addr, sizeof(addr), &port) != kr_ok()) { + format_error(L, "invalid IP address"); + lua_error(L); + } + + if (port == 0) { + port = 853; + } + + int r = tls_client_params_clear(&net->tls_client_params, addr, port); + if (r != 0) { + lua_pushstring(L, kr_strerror(r)); + lua_error(L); + } + + lua_pushboolean(L, true); + return 1; +} + static int net_tls_padding(lua_State *L) { struct engine *engine = engine_luaget(L); @@ -852,6 +892,7 @@ int lib_net(lua_State *L) { "tls", net_tls }, { "tls_server", net_tls }, { "tls_client", net_tls_client }, + { "tls_client_clear", net_tls_client_clear }, { "tls_padding", net_tls_padding }, { "tls_sticket_secret", net_tls_sticket_secret_string }, { "tls_sticket_secret_file", net_tls_sticket_secret_file }, diff --git a/daemon/tls.c b/daemon/tls.c index 6b63a9674..bc6e6f073 100644 --- a/daemon/tls.c +++ b/daemon/tls.c @@ -805,6 +805,49 @@ static int client_paramlist_entry_clear(const char *k, void *v, void *baton) return client_paramlist_entry_free(entry); } +struct tls_client_paramlist_entry *tls_client_try_upgrade(map_t *tls_client_paramlist, + const struct sockaddr *addr) +{ + /* Opportunistic upgrade from port 53 -> 853 */ + if (kr_inaddr_port(addr) != KR_DNS_PORT) { + return NULL; + } + + static char key[INET6_ADDRSTRLEN + 6]; + size_t keylen = sizeof(key); + if (kr_inaddr_str(addr, key, &keylen) != 0) { + return NULL; + } + + /* Rewrite 053 -> 853 */ + strcpy(key + keylen - 3, "853"); + + return map_get(tls_client_paramlist, key); +} + +int tls_client_params_clear(map_t *tls_client_paramlist, const char *addr, uint16_t port) +{ + if (!tls_client_paramlist || !addr) { + return kr_error(EINVAL); + } + + /* Parameters are OK */ + + char key[INET6_ADDRSTRLEN + 6]; + size_t keylen = sizeof(key); + if (kr_straddr_join(addr, port, key, &keylen) != kr_ok()) { + return kr_error(EINVAL); + } + + struct tls_client_paramlist_entry *entry = map_get(tls_client_paramlist, key); + if (entry != NULL) { + client_paramlist_entry_clear(NULL, (void *)entry, NULL); + map_del(tls_client_paramlist, key); + } + + return kr_ok(); +} + int tls_client_params_set(map_t *tls_client_paramlist, const char *addr, uint16_t port, const char *param, tls_client_param_t param_type) diff --git a/daemon/tls.h b/daemon/tls.h index cb3d4a64f..e57799c5c 100644 --- a/daemon/tls.h +++ b/daemon/tls.h @@ -168,6 +168,9 @@ int tls_set_hs_state(struct tls_common_ctx *ctx, tls_hs_state_t state); struct tls_client_paramlist_entry *tls_client_try_upgrade(map_t *tls_client_paramlist, const struct sockaddr *addr); +/*! Clear (remove) TLS parameters for given address. */ +int tls_client_params_clear(map_t *tls_client_paramlist, const char *addr, uint16_t port); + /*! Set TLS authentication parameters for given address. * Note: hostnames must be imported before ca files, * otherwise ca files will not be imported at all. diff --git a/daemon/worker.c b/daemon/worker.c index 5173eacd2..73639ef8c 100644 --- a/daemon/worker.c +++ b/daemon/worker.c @@ -1401,6 +1401,22 @@ static int qr_task_step(struct qr_task *task, choice += 1; } + /* Upgrade to TLS if the upstream address is configured as DoT capable. */ + struct engine *engine = ctx->worker->engine; + struct network *net = &engine->net; + const struct sockaddr *addr = packet_source ? packet_source : task->addrlist; + struct tls_client_paramlist_entry *tls_entry = NULL; + if (kr_inaddr_port(task->addrlist) == KR_DNS_PORT) { + tls_entry = tls_client_try_upgrade(&net->tls_client_params, task->addrlist); + if (tls_entry != NULL) { + kr_inaddr_set_port(task->addrlist, KR_DNS_TLS_PORT); + sock_type = SOCK_STREAM; + } + } else if (sock_type == SOCK_STREAM) { + const char *key = tcpsess_key(addr); + tls_entry = map_get(&net->tls_client_params, key); + } + int ret = 0; if (sock_type == SOCK_DGRAM) { /* Start fast retransmit with UDP. */