From 07f525bae50372edb3f5f6e03a70a43d9bd72650 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Wed, 3 Feb 2021 10:58:46 -0800 Subject: [PATCH 1/5] require "tls none" for unencrypted HTTP listeners unencrypted DoH connections may be used in some operational environments where encryption is handled by a reverse proxy, but it's going to be relatively rare, so we shouldn't make it easy to do by mistake. this commit changes the syntax for listen-on and listen-on-v6 so that if "http" is specified, "tls" must also be specified; for unencrypted listeners, "tls none" can be used. --- bin/named/server.c | 31 ++++++++++++++-------- bin/tests/system/checkconf/good-doh-1.conf | 27 +++++++++++++++++++ doc/arm/reference.rst | 19 +++++++------ 3 files changed, 58 insertions(+), 19 deletions(-) create mode 100644 bin/tests/system/checkconf/good-doh-1.conf diff --git a/bin/named/server.c b/bin/named/server.c index 4659815ffb..b199d36fc7 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -8995,10 +8995,9 @@ load_configuration(const char *filename, named_server_t *server, (void)cfg_map_get(options, "listen-on", &clistenon); } if (clistenon != NULL) { - /* check return code? */ - (void)listenlist_fromconfig( + CHECK(listenlist_fromconfig( clistenon, config, named_g_aclconfctx, - named_g_mctx, AF_INET, &listenon); + named_g_mctx, AF_INET, &listenon)); } else { /* * Not specified, use default. @@ -9023,10 +9022,9 @@ load_configuration(const char *filename, named_server_t *server, (void)cfg_map_get(options, "listen-on-v6", &clistenon); } if (clistenon != NULL) { - /* check return code? */ - (void)listenlist_fromconfig( + CHECK(listenlist_fromconfig( clistenon, config, named_g_aclconfctx, - named_g_mctx, AF_INET6, &listenon); + named_g_mctx, AF_INET6, &listenon)); } else { /* * Not specified, use default. @@ -11067,20 +11065,25 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, in_port_t port = 0; isc_dscp_t dscp = -1; const char *key = NULL, *cert = NULL; - bool do_tls = false, http = false; + bool do_tls = false, no_tls = false, http = false; ns_listenelt_t *delt = NULL; REQUIRE(target != NULL && *target == NULL); - /* XXXWPK TODO be more verbose on failures. */ tlsobj = cfg_tuple_get(listener, "tls"); if (tlsobj != NULL && cfg_obj_isstring(tlsobj)) { const char *tlsname = cfg_obj_asstring(tlsobj); - if (strcmp(tlsname, "ephemeral") != 0) { + if (strcasecmp(tlsname, "none") == 0) { + no_tls = true; + } else if (strcasecmp(tlsname, "ephemeral") == 0) { + do_tls = true; + } else { const cfg_obj_t *keyobj = NULL, *certobj = NULL; const cfg_obj_t *tlsmap = NULL; + do_tls = true; + tlsmap = find_maplist(config, "tls", tlsname); if (tlsmap == NULL) { return (ISC_R_FAILURE); @@ -11092,14 +11095,20 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, CHECK(cfg_map_get(tlsmap, "cert-file", &certobj)); cert = cfg_obj_asstring(certobj); } - - do_tls = true; } httpobj = cfg_tuple_get(listener, "http"); if (httpobj != NULL && cfg_obj_isstring(httpobj)) { const char *httpname = cfg_obj_asstring(httpobj); + if (!do_tls && !no_tls) { + cfg_obj_log(httpobj, named_g_lctx, ISC_LOG_ERROR, + "http must specify a 'tls' " + "statement, 'tls ephemeral', or " + "'tls none'"); + return (ISC_R_FAILURE); + } + http_server = find_maplist(config, "http", httpname); if (http_server == NULL) { cfg_obj_log(httpobj, named_g_lctx, ISC_LOG_ERROR, diff --git a/bin/tests/system/checkconf/good-doh-1.conf b/bin/tests/system/checkconf/good-doh-1.conf new file mode 100644 index 0000000000..8f983778b1 --- /dev/null +++ b/bin/tests/system/checkconf/good-doh-1.conf @@ -0,0 +1,27 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +tls local-tls { + key-file "key.pem"; + cert-file "cert.pem"; +}; + +http local-http-server { + endpoints { "/dns-query"; }; +}; + +options { + listen-on { 10.53.0.1; }; + http-port 80; + https-port 443; + listen-on port 443 tls local-tls http local-http-server { 10.53.0.1; }; + listen-on port 8080 tls none http local-http-server { 10.53.0.1; }; +}; diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index fb9545e059..0788229ba1 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -2472,9 +2472,10 @@ referenced ``tls`` statement. If an HTTP configuration is specified, ``named`` will listen for DNS-over-HTTPS (DoH) connections using the HTTP endpoint specified in the -referenced ``http`` statement. Normally, ``http`` and ``tls`` -configurations will be used together, but ``tls`` may be omitted if -encryption is being handled by external software. +referenced ``http`` statement. ``http`` and ``tls`` configurations must be +used together. If an unencrypted connection is desired (for example, when +load-sharing servers behind a reverse proxy), ``tls none`` may be +used. If a port number is not specified, the default is 53 for standard DNS, 853 for DNS-over-TLS, and 443 for DNS-over-HTTPS. @@ -2506,7 +2507,7 @@ Multiple ``listen-on-v6`` options can be used. For example: listen-on-v6 port 1234 { !2001:db8::/32; any; }; listen-on port 8853 tls example-tls { 2001:db8::100; }; listen-on port 8453 tls example-tls http myserver { 2001:db8::100; }; - listen-on port 8000 http myserver { 2001:db8::100; }; + listen-on port 8000 tls none http myserver { 2001:db8::100; }; The first two lines instruct the name server to listen for standard DNS queries on port 53 of any IPv6 addresses, and on port 1234 of IPv6 @@ -2516,8 +2517,8 @@ instructs the server to listen for for DNS-over-TLS connections on port in the a ``tls`` statement with the name ``example-tls``. The fourth instructs the server to listen for DNS-over-HTTPS connections, again using ``example-tls``, on the HTTP endpoint specified in ``http myserver``. The -fifth line, in which the ``tls`` parameter is omitted, instructs the server -to listen for *unencrypted* DNS queries over HTTP. +fifth line, in which the ``tls`` parameter is set to ``none``, instructs +the server to listen for *unencrypted* DNS queries over HTTP. To instruct the server not to listen on any IPv6 addresses, use: @@ -4658,8 +4659,10 @@ The following options can be specified in a ``tls`` statement: ``hostname`` The hostname associated with the certificate. -The built-in ``ephemeral`` TLS connection object represents a temporary -key and certificate created for the current ``named`` session only. +There are two built-in TLS connection configurations: ``ephemeral``, +uses a temporary key and certificate created for the current ``named`` +session only, and ``none``, which can be used when setting up an HTTP +listener with no encryption. .. _http: From fd763d72238fc577b934a7dec6d6530fc0107924 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Wed, 3 Feb 2021 11:36:33 -0800 Subject: [PATCH 2/5] enable listen-on parameters to be specified in any order updated the parser to allow the "port", "tls" and "http" paramters to "listen-on" and "listen-on-v6" to be specified in any order. previously the parser would throw an error if any other order was used than port, tls, http. --- bin/named/server.c | 12 ++++-- .../{good-doh-global.conf => good-doh-2.conf} | 4 +- lib/isccfg/namedconf.c | 37 ++++++++++++------- 3 files changed, 33 insertions(+), 20 deletions(-) rename bin/tests/system/checkconf/{good-doh-global.conf => good-doh-2.conf} (81%) diff --git a/bin/named/server.c b/bin/named/server.c index b199d36fc7..ae72b80985 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -11059,6 +11059,7 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, cfg_aclconfctx_t *actx, isc_mem_t *mctx, uint16_t family, ns_listenelt_t **target) { isc_result_t result; + const cfg_obj_t *ltup = NULL; const cfg_obj_t *tlsobj = NULL, *httpobj = NULL; const cfg_obj_t *portobj = NULL, *dscpobj = NULL; const cfg_obj_t *http_server = NULL; @@ -11070,7 +11071,10 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, REQUIRE(target != NULL && *target == NULL); - tlsobj = cfg_tuple_get(listener, "tls"); + ltup = cfg_tuple_get(listener, "tuple"); + RUNTIME_CHECK(ltup != NULL); + + tlsobj = cfg_tuple_get(ltup, "tls"); if (tlsobj != NULL && cfg_obj_isstring(tlsobj)) { const char *tlsname = cfg_obj_asstring(tlsobj); @@ -11097,7 +11101,7 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, } } - httpobj = cfg_tuple_get(listener, "http"); + httpobj = cfg_tuple_get(ltup, "http"); if (httpobj != NULL && cfg_obj_isstring(httpobj)) { const char *httpname = cfg_obj_asstring(httpobj); @@ -11120,7 +11124,7 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, http = true; } - portobj = cfg_tuple_get(listener, "port"); + portobj = cfg_tuple_get(ltup, "port"); if (!cfg_obj_isuint32(portobj)) { if (http && do_tls) { if (named_g_httpsport != 0) { @@ -11174,7 +11178,7 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, port = (in_port_t)cfg_obj_asuint32(portobj); } - dscpobj = cfg_tuple_get(listener, "dscp"); + dscpobj = cfg_tuple_get(ltup, "dscp"); if (!cfg_obj_isuint32(dscpobj)) { dscp = named_g_dscp; } else { diff --git a/bin/tests/system/checkconf/good-doh-global.conf b/bin/tests/system/checkconf/good-doh-2.conf similarity index 81% rename from bin/tests/system/checkconf/good-doh-global.conf rename to bin/tests/system/checkconf/good-doh-2.conf index f5eb63477f..52f2be3e9f 100644 --- a/bin/tests/system/checkconf/good-doh-global.conf +++ b/bin/tests/system/checkconf/good-doh-2.conf @@ -22,6 +22,6 @@ options { listen-on { 10.53.0.1; }; http-port 80; https-port 443; - listen-on port 443 tls local-tls http local-http-server { 10.53.0.1; }; - listen-on port 8080 http local-http-server { 10.53.0.1; }; + listen-on port 443 http local-http-server tls local-tls { 10.53.0.1; }; + listen-on port 8080 tls none http local-http-server { 10.53.0.1; }; }; diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index e90ab215f8..7c29970a81 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -76,6 +76,15 @@ doc_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type); static void doc_optional_keyvalue(cfg_printer_t *pctx, const cfg_type_t *type); +static isc_result_t +cfg_parse_kv_tuple(cfg_parser_t *pctx, const cfg_type_t *type, cfg_obj_t **ret); + +static void +cfg_print_kv_tuple(cfg_printer_t *pctx, const cfg_obj_t *obj); + +static void +cfg_doc_kv_tuple(cfg_printer_t *pctx, const cfg_type_t *type); + static cfg_type_t cfg_type_acl; static cfg_type_t cfg_type_bracketed_dscpsockaddrlist; static cfg_type_t cfg_type_bracketed_namesockaddrkeylist; @@ -91,7 +100,6 @@ static cfg_type_t cfg_type_dnssecpolicy; static cfg_type_t cfg_type_dnstap; static cfg_type_t cfg_type_dnstapoutput; static cfg_type_t cfg_type_dyndb; -static cfg_type_t cfg_type_plugin; static cfg_type_t cfg_type_http_description; static cfg_type_t cfg_type_ixfrdifftype; static cfg_type_t cfg_type_ixfrratio; @@ -110,12 +118,12 @@ static cfg_type_t cfg_type_optional_allow; static cfg_type_t cfg_type_optional_class; static cfg_type_t cfg_type_optional_dscp; static cfg_type_t cfg_type_optional_facility; -static cfg_type_t cfg_type_optional_http; static cfg_type_t cfg_type_optional_keyref; static cfg_type_t cfg_type_optional_port; static cfg_type_t cfg_type_optional_uint32; static cfg_type_t cfg_type_optional_tls; static cfg_type_t cfg_type_options; +static cfg_type_t cfg_type_plugin; static cfg_type_t cfg_type_portiplist; static cfg_type_t cfg_type_printtime; static cfg_type_t cfg_type_qminmethod; @@ -150,11 +158,20 @@ static cfg_type_t cfg_type_tkey_dhkey = { "tkey-dhkey", cfg_parse_tuple, /*% listen-on */ -static cfg_tuplefielddef_t listenon_fields[] = { +static cfg_tuplefielddef_t listenon_tuple_fields[] = { { "port", &cfg_type_optional_port, 0 }, - { "dscp", &cfg_type_optional_dscp, 0 }, - { "tls", &cfg_type_optional_tls, 0 }, - { "http", &cfg_type_optional_http, 0 }, + { "dscp", &cfg_type_uint32, 0 }, + { "tls", &cfg_type_astring, 0 }, + { "http", &cfg_type_astring, 0 }, + { NULL, NULL, 0 } +}; +static cfg_type_t cfg_type_listen_tuple = { + "listenon tuple", cfg_parse_kv_tuple, cfg_print_kv_tuple, + cfg_doc_kv_tuple, &cfg_rep_tuple, listenon_tuple_fields +}; + +static cfg_tuplefielddef_t listenon_fields[] = { + { "tuple", &cfg_type_listen_tuple, 0 }, { "acl", &cfg_type_bracketed_aml, 0 }, { NULL, NULL, 0 } }; @@ -3842,8 +3859,6 @@ static cfg_clausedef_t tls_clauses[] = { { "cert-file", &cfg_type_qstring, 0 }, { "ca-file", &cfg_type_qstring, 0 }, { "hostname", &cfg_type_qstring, 0 }, - /* { "trusted-cert-file", &cfg_type_qstring, * - CFG_CLAUSEFLAG_EXPERIMENTAL}, */ { "dh-param", &cfg_type_qstring, CFG_CLAUSEFLAG_EXPERIMENTAL }, { "protocols", &cfg_type_sslprotos, CFG_CLAUSEFLAG_EXPERIMENTAL }, { "ciphers", &cfg_type_astring, CFG_CLAUSEFLAG_EXPERIMENTAL }, @@ -3882,9 +3897,3 @@ static cfg_type_t cfg_type_http_description = { "http_desc", cfg_parse_named_map, cfg_print_map, cfg_doc_map, &cfg_rep_map, http_description_clausesets }; - -static keyword_type_t http_kw = { "http", &cfg_type_astring }; -static cfg_type_t cfg_type_optional_http = { - "http_optional", parse_optional_keyvalue, print_keyvalue, - doc_optional_keyvalue, &cfg_rep_string, &http_kw -}; From 957052eea55f211d81dbfc4e02a5f4803446e1cd Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Wed, 3 Feb 2021 13:13:41 -0800 Subject: [PATCH 3/5] move listen-on correctness checks into check.c errors in listen-on and listen-on-v6 can now be detected by named-checkconf. --- bin/named/server.c | 14 -- bin/tests/system/checkconf/bad-doh-1.conf | 24 +++ bin/tests/system/checkconf/bad-doh-2.conf | 20 ++ bin/tests/system/checkconf/bad-doh-3.conf | 19 ++ bin/tests/system/checkconf/bad-dot-1.conf | 15 ++ .../{good-dot-global.conf => good-dot-1.conf} | 0 lib/bind9/check.c | 178 +++++++++++++++++- 7 files changed, 250 insertions(+), 20 deletions(-) create mode 100644 bin/tests/system/checkconf/bad-doh-1.conf create mode 100644 bin/tests/system/checkconf/bad-doh-2.conf create mode 100644 bin/tests/system/checkconf/bad-doh-3.conf create mode 100644 bin/tests/system/checkconf/bad-dot-1.conf rename bin/tests/system/checkconf/{good-dot-global.conf => good-dot-1.conf} (100%) diff --git a/bin/named/server.c b/bin/named/server.c index ae72b80985..02e09b13f6 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -11106,18 +11106,11 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, const char *httpname = cfg_obj_asstring(httpobj); if (!do_tls && !no_tls) { - cfg_obj_log(httpobj, named_g_lctx, ISC_LOG_ERROR, - "http must specify a 'tls' " - "statement, 'tls ephemeral', or " - "'tls none'"); return (ISC_R_FAILURE); } http_server = find_maplist(config, "http", httpname); if (http_server == NULL) { - cfg_obj_log(httpobj, named_g_lctx, ISC_LOG_ERROR, - "http '%s' is not defined", - cfg_obj_asstring(httpobj)); return (ISC_R_FAILURE); } @@ -11169,10 +11162,6 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, } } else { if (cfg_obj_asuint32(portobj) >= UINT16_MAX) { - cfg_obj_log(portobj, named_g_lctx, ISC_LOG_ERROR, - "port value '%u' is out of range", - - cfg_obj_asuint32(portobj)); return (ISC_R_RANGE); } port = (in_port_t)cfg_obj_asuint32(portobj); @@ -11183,9 +11172,6 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, dscp = named_g_dscp; } else { if (cfg_obj_asuint32(dscpobj) > 63) { - cfg_obj_log(dscpobj, named_g_lctx, ISC_LOG_ERROR, - "dscp value '%u' is out of range", - cfg_obj_asuint32(dscpobj)); return (ISC_R_RANGE); } dscp = (isc_dscp_t)cfg_obj_asuint32(dscpobj); diff --git a/bin/tests/system/checkconf/bad-doh-1.conf b/bin/tests/system/checkconf/bad-doh-1.conf new file mode 100644 index 0000000000..1d63d9765a --- /dev/null +++ b/bin/tests/system/checkconf/bad-doh-1.conf @@ -0,0 +1,24 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +tls local-tls { + key-file "key.pem"; + cert-file "cert.pem"; +}; + +http local-http-server { + endpoints { "/dns-query"; }; +}; + +# undefined 'tls' specification +options { + listen-on port 8080 http local-http-server tls unknown { 10.53.0.1; }; +}; diff --git a/bin/tests/system/checkconf/bad-doh-2.conf b/bin/tests/system/checkconf/bad-doh-2.conf new file mode 100644 index 0000000000..3f6d991c67 --- /dev/null +++ b/bin/tests/system/checkconf/bad-doh-2.conf @@ -0,0 +1,20 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +tls local-tls { + key-file "key.pem"; + cert-file "cert.pem"; +}; + +# undefined 'http' specification +options { + listen-on port 8080 http unknown tls local-tls { 10.53.0.1; }; +}; diff --git a/bin/tests/system/checkconf/bad-doh-3.conf b/bin/tests/system/checkconf/bad-doh-3.conf new file mode 100644 index 0000000000..ff697ef578 --- /dev/null +++ b/bin/tests/system/checkconf/bad-doh-3.conf @@ -0,0 +1,19 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +http local-http-server { + endpoints { "/dns-query"; }; +}; + +# no 'tls' specification +options { + listen-on port 8080 http unknown { 10.53.0.1; }; +}; diff --git a/bin/tests/system/checkconf/bad-dot-1.conf b/bin/tests/system/checkconf/bad-dot-1.conf new file mode 100644 index 0000000000..5df1acacc6 --- /dev/null +++ b/bin/tests/system/checkconf/bad-dot-1.conf @@ -0,0 +1,15 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +# undefined 'tls' specification +options { + listen-on port 853 tls local-tls { 10.53.0.1; }; +}; diff --git a/bin/tests/system/checkconf/good-dot-global.conf b/bin/tests/system/checkconf/good-dot-1.conf similarity index 100% rename from bin/tests/system/checkconf/good-dot-global.conf rename to bin/tests/system/checkconf/good-dot-1.conf diff --git a/lib/bind9/check.c b/lib/bind9/check.c index 67f2f96642..d514936be0 100644 --- a/lib/bind9/check.c +++ b/lib/bind9/check.c @@ -896,9 +896,146 @@ kasp_name_allowed(const cfg_listelt_t *element) { return (true); } +static const cfg_obj_t * +find_maplist(const cfg_obj_t *config, const char *listname, const char *name) { + isc_result_t result; + const cfg_obj_t *maplist = NULL; + const cfg_listelt_t *elt = NULL; + + REQUIRE(config != NULL); + REQUIRE(name != NULL); + + result = cfg_map_get(config, listname, &maplist); + if (result != ISC_R_SUCCESS) { + return (NULL); + } + + for (elt = cfg_list_first(maplist); elt != NULL; + elt = cfg_list_next(elt)) { + const cfg_obj_t *map = cfg_listelt_value(elt); + if (strcasecmp(cfg_obj_asstring(cfg_map_getname(map)), name) == + 0) { + return (map); + } + } + + return (NULL); +} + static isc_result_t -check_options(const cfg_obj_t *options, isc_log_t *logctx, isc_mem_t *mctx, - optlevel_t optlevel) { +check_listener(const cfg_obj_t *listener, const cfg_obj_t *config, + cfg_aclconfctx_t *actx, isc_log_t *logctx, isc_mem_t *mctx) { + isc_result_t tresult, result = ISC_R_SUCCESS; + const cfg_obj_t *ltup = NULL; + const cfg_obj_t *tlsobj = NULL, *httpobj = NULL; + const cfg_obj_t *portobj = NULL, *dscpobj = NULL; + const cfg_obj_t *http_server = NULL; + bool do_tls = false, no_tls = false; + dns_acl_t *acl = NULL; + + ltup = cfg_tuple_get(listener, "tuple"); + RUNTIME_CHECK(ltup != NULL); + + tlsobj = cfg_tuple_get(ltup, "tls"); + if (tlsobj != NULL && cfg_obj_isstring(tlsobj)) { + const char *tlsname = cfg_obj_asstring(tlsobj); + + if (strcasecmp(tlsname, "none") == 0) { + no_tls = true; + } else if (strcasecmp(tlsname, "ephemeral") == 0) { + do_tls = true; + } else { + const cfg_obj_t *tlsmap = NULL; + + do_tls = true; + + tlsmap = find_maplist(config, "tls", tlsname); + if (tlsmap == NULL) { + cfg_obj_log(tlsobj, logctx, ISC_LOG_ERROR, + "tls '%s' is not defined", + cfg_obj_asstring(tlsobj)); + result = ISC_R_FAILURE; + } + } + } + + httpobj = cfg_tuple_get(ltup, "http"); + if (httpobj != NULL && cfg_obj_isstring(httpobj)) { + const char *httpname = cfg_obj_asstring(httpobj); + + if (!do_tls && !no_tls) { + cfg_obj_log(httpobj, logctx, ISC_LOG_ERROR, + "http must specify a 'tls' " + "statement, 'tls ephemeral', or " + "'tls none'"); + result = ISC_R_FAILURE; + } + + http_server = find_maplist(config, "http", httpname); + if (http_server == NULL) { + cfg_obj_log(httpobj, logctx, ISC_LOG_ERROR, + "http '%s' is not defined", + cfg_obj_asstring(httpobj)); + result = ISC_R_FAILURE; + } + } + + portobj = cfg_tuple_get(ltup, "port"); + if (cfg_obj_isuint32(portobj) && + cfg_obj_asuint32(portobj) >= UINT16_MAX) { + cfg_obj_log(portobj, logctx, ISC_LOG_ERROR, + "port value '%u' is out of range", + + cfg_obj_asuint32(portobj)); + if (result == ISC_R_SUCCESS) { + result = ISC_R_RANGE; + } + } + + dscpobj = cfg_tuple_get(ltup, "dscp"); + if (cfg_obj_isuint32(dscpobj) && cfg_obj_asuint32(dscpobj) > 63) { + cfg_obj_log(dscpobj, logctx, ISC_LOG_ERROR, + "dscp value '%u' is out of range", + cfg_obj_asuint32(dscpobj)); + if (result == ISC_R_SUCCESS) { + result = ISC_R_RANGE; + } + } + + tresult = cfg_acl_fromconfig(cfg_tuple_get(listener, "acl"), config, + logctx, actx, mctx, 0, &acl); + if (result == ISC_R_SUCCESS) { + result = tresult; + } + + if (acl != NULL) { + dns_acl_detach(&acl); + } + + return (result); +} + +static isc_result_t +check_listeners(const cfg_obj_t *list, const cfg_obj_t *config, + cfg_aclconfctx_t *actx, isc_log_t *logctx, isc_mem_t *mctx) { + isc_result_t tresult, result = ISC_R_SUCCESS; + const cfg_listelt_t *elt = NULL; + + for (elt = cfg_list_first(list); elt != NULL; elt = cfg_list_next(elt)) + { + const cfg_obj_t *obj = cfg_listelt_value(elt); + tresult = check_listener(obj, config, actx, logctx, mctx); + if (result == ISC_R_SUCCESS) { + result = tresult; + } + } + + return (result); +} + +static isc_result_t +check_options(const cfg_obj_t *options, const cfg_obj_t *config, + isc_log_t *logctx, isc_mem_t *mctx, optlevel_t optlevel) { isc_result_t result = ISC_R_SUCCESS; isc_result_t tresult; unsigned int i; @@ -911,6 +1048,7 @@ check_options(const cfg_obj_t *options, isc_log_t *logctx, isc_mem_t *mctx, uint32_t lifetime = 3600; bool has_dnssecpolicy = false; const char *ccalg = "siphash24"; + cfg_aclconfctx_t *actx = NULL; /* * { "name", scale, value } @@ -1666,6 +1804,32 @@ check_options(const cfg_obj_t *options, isc_log_t *logctx, isc_mem_t *mctx, } } + cfg_aclconfctx_create(mctx, &actx); + + obj = NULL; + (void)cfg_map_get(options, "listen-on", &obj); + if (obj != NULL) { + INSIST(config != NULL); + tresult = check_listeners(obj, config, actx, logctx, mctx); + if (result == ISC_R_SUCCESS) { + result = tresult; + } + } + + obj = NULL; + (void)cfg_map_get(options, "listen-on-v6", &obj); + if (obj != NULL) { + INSIST(config != NULL); + tresult = check_listeners(obj, config, actx, logctx, mctx); + if (result == ISC_R_SUCCESS) { + result = tresult; + } + } + + if (actx != NULL) { + cfg_aclconfctx_detach(&actx); + } + return (result); } @@ -3043,7 +3207,7 @@ check_zoneconf(const cfg_obj_t *zconfig, const cfg_obj_t *voptions, /* * Check various options. */ - tresult = check_options(zoptions, logctx, mctx, optlevel_zone); + tresult = check_options(zoptions, config, logctx, mctx, optlevel_zone); if (tresult != ISC_R_SUCCESS) { result = tresult; } @@ -4529,9 +4693,11 @@ check_viewconf(const cfg_obj_t *config, const cfg_obj_t *voptions, * Check options. */ if (voptions != NULL) { - tresult = check_options(voptions, logctx, mctx, optlevel_view); + tresult = check_options(voptions, NULL, logctx, mctx, + optlevel_view); } else { - tresult = check_options(config, logctx, mctx, optlevel_config); + tresult = check_options(config, config, logctx, mctx, + optlevel_config); } if (tresult != ISC_R_SUCCESS) { result = tresult; @@ -4870,7 +5036,7 @@ bind9_check_namedconf(const cfg_obj_t *config, bool check_plugins, (void)cfg_map_get(config, "options", &options); - if (options != NULL && check_options(options, logctx, mctx, + if (options != NULL && check_options(options, config, logctx, mctx, optlevel_options) != ISC_R_SUCCESS) { result = ISC_R_FAILURE; From 2b2e1a02bd5d5502370fb01a2f548dab5a6441d9 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Fri, 12 Feb 2021 18:17:09 -0800 Subject: [PATCH 4/5] allow configuration of "default" http endpoint specifying "http default" in a listen-on statement sets up the default "/dns-query" endpoint. tests and documentation have been updated. --- bin/named/server.c | 37 ++++++++++++++++------ bin/tests/system/checkconf/good-doh-3.conf | 17 ++++++++++ doc/arm/reference.rst | 37 +++++++++++++--------- lib/bind9/check.c | 3 +- 4 files changed, 68 insertions(+), 26 deletions(-) create mode 100644 bin/tests/system/checkconf/good-doh-3.conf diff --git a/bin/named/server.c b/bin/named/server.c index 02e09b13f6..83ac8eed7a 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -11090,6 +11090,9 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, tlsmap = find_maplist(config, "tls", tlsname); if (tlsmap == NULL) { + cfg_obj_log(tlsobj, named_g_lctx, ISC_LOG_ERROR, + "tls '%s' is not defined", + cfg_obj_asstring(tlsobj)); return (ISC_R_FAILURE); } @@ -11110,7 +11113,11 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, } http_server = find_maplist(config, "http", httpname); - if (http_server == NULL) { + if (http_server == NULL && strcasecmp(httpname, "default") != 0) + { + cfg_obj_log(httpobj, named_g_lctx, ISC_LOG_ERROR, + "http '%s' is not defined", + cfg_obj_asstring(httpobj)); return (ISC_R_FAILURE); } @@ -11178,7 +11185,6 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, } if (http) { - INSIST(http_server != NULL); CHECK(listenelt_http(http_server, do_tls, key, cert, port, mctx, &delt)); } else { @@ -11208,7 +11214,7 @@ listenelt_http(const cfg_obj_t *http, bool tls, const char *key, char **endpoints = NULL; const cfg_obj_t *eplist = NULL; const cfg_listelt_t *elt = NULL; - size_t len, i = 0; + size_t len = 1, i = 0; REQUIRE(target != NULL && *target == NULL); REQUIRE((key == NULL) == (cert == NULL)); @@ -11217,15 +11223,26 @@ listenelt_http(const cfg_obj_t *http, bool tls, const char *key, port = tls ? named_g_httpsport : named_g_httpport; } - CHECK(cfg_map_get(http, "endpoints", &eplist)); - len = cfg_list_length(eplist, false); + /* + * If "default" was used, we set up the default endpoint + * of "/dns-query". + */ + if (http != NULL) { + CHECK(cfg_map_get(http, "endpoints", &eplist)); + len = cfg_list_length(eplist, false); + } + endpoints = isc_mem_allocate(mctx, sizeof(endpoints[0]) * len); - for (elt = cfg_list_first(eplist); elt != NULL; - elt = cfg_list_next(elt)) { - const cfg_obj_t *ep = cfg_listelt_value(elt); - const char *path = cfg_obj_asstring(ep); - endpoints[i++] = isc_mem_strdup(mctx, path); + if (http != NULL) { + for (elt = cfg_list_first(eplist); elt != NULL; + elt = cfg_list_next(elt)) { + const cfg_obj_t *ep = cfg_listelt_value(elt); + const char *path = cfg_obj_asstring(ep); + endpoints[i++] = isc_mem_strdup(mctx, path); + } + } else { + endpoints[i++] = isc_mem_strdup(mctx, "/dns-query"); } INSIST(i == len); diff --git a/bin/tests/system/checkconf/good-doh-3.conf b/bin/tests/system/checkconf/good-doh-3.conf new file mode 100644 index 0000000000..c570e6c447 --- /dev/null +++ b/bin/tests/system/checkconf/good-doh-3.conf @@ -0,0 +1,17 @@ +/* + * Copyright (C) Internet Systems Consortium, Inc. ("ISC") + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +options { + listen-on { 10.53.0.1; }; + http-port 80; + https-port 443; + listen-on port 8080 tls none http default { 10.53.0.1; }; +}; diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index 0788229ba1..641a0054fc 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -2468,17 +2468,24 @@ DNS queries on port 53 of all IPv6 interfaces. If a TLS configuration is specified, ``named`` will listen for DNS-over-TLS (DoT) connections, using the key and certificate specified in the -referenced ``tls`` statement. +referenced ``tls`` statement. If the name ``ephemeral`` is used, +an ephemeral key and certificate created for the currently running +``named`` process will be used. If an HTTP configuration is specified, ``named`` will listen for DNS-over-HTTPS (DoH) connections using the HTTP endpoint specified in the -referenced ``http`` statement. ``http`` and ``tls`` configurations must be -used together. If an unencrypted connection is desired (for example, when -load-sharing servers behind a reverse proxy), ``tls none`` may be -used. +referenced ``http`` statement. If the name ``default`` is used, then +``named`` will listen for connections at the default endpoint, +``/dns-query``. + +Use of an ``http`` specification requires ``tls`` to be specified +as well. If an unencrypted connection is desired (for example, +on load-sharing servers behind a reverse proxy), ``tls none`` may be used. If a port number is not specified, the default is 53 for standard DNS, 853 -for DNS-over-TLS, and 443 for DNS-over-HTTPS. +for DNS over TLS, 443 for DNS over HTTPS, and 80 for DNS over unenecrypted +HTTP. These defaults may be overridden using the ``port``, ``tls-port``, +``https-port`` and ``http-port`` options. Multiple ``listen-on`` statements are allowed. For example: @@ -2493,11 +2500,10 @@ The first two lines instruct the name server to listen for standard DNS queries on port 53 of the IP address 5.6.7.8 and on port 1234 of an address on the machine in net 1.2 that is not 1.2.3.4. The third line instructs the server to listen for DNS-over-TLS connections on port 8853 of the IP -address 4.3.2.1 using an ephemeral TLS key and certificate created for the -currently running ``named`` process. The fourth line enables DNS-over-HTTPS -connections on port 8453 of address 8.7.6.5, using the same ephemeral -key and certificate, and the HTTP endpoint or endpoints configured in -an ``http`` statement with the name ``myserver``. +address 4.3.2.1 using the ephemeral key and certifcate. The fourth line +enables DNS-over-HTTPS connections on port 8453 of address 8.7.6.5, using +the ephemeral key and certificate, and the HTTP endpoint or endpoints +configured in an ``http`` statement with the name ``myserver``. Multiple ``listen-on-v6`` options can be used. For example: @@ -2506,7 +2512,7 @@ Multiple ``listen-on-v6`` options can be used. For example: listen-on-v6 { any; }; listen-on-v6 port 1234 { !2001:db8::/32; any; }; listen-on port 8853 tls example-tls { 2001:db8::100; }; - listen-on port 8453 tls example-tls http myserver { 2001:db8::100; }; + listen-on port 8453 tls example-tls http default { 2001:db8::100; }; listen-on port 8000 tls none http myserver { 2001:db8::100; }; The first two lines instruct the name server to listen for standard DNS @@ -2516,9 +2522,10 @@ instructs the server to listen for for DNS-over-TLS connections on port 8853 of the address 2001:db8::100, using a TLS key and certificate specified in the a ``tls`` statement with the name ``example-tls``. The fourth instructs the server to listen for DNS-over-HTTPS connections, again using -``example-tls``, on the HTTP endpoint specified in ``http myserver``. The -fifth line, in which the ``tls`` parameter is set to ``none``, instructs -the server to listen for *unencrypted* DNS queries over HTTP. +``example-tls``, on the default HTTP endpoint. The fifth line, in which +the ``tls`` parameter is set to ``none``, instructs the server to listen +for *unencrypted* DNS queries over HTTP at the endpoint specified in +``myserver``.. To instruct the server not to listen on any IPv6 addresses, use: diff --git a/lib/bind9/check.c b/lib/bind9/check.c index d514936be0..9555ba37f9 100644 --- a/lib/bind9/check.c +++ b/lib/bind9/check.c @@ -972,7 +972,8 @@ check_listener(const cfg_obj_t *listener, const cfg_obj_t *config, } http_server = find_maplist(config, "http", httpname); - if (http_server == NULL) { + if (http_server == NULL && strcasecmp(httpname, "default") != 0) + { cfg_obj_log(httpobj, logctx, ISC_LOG_ERROR, "http '%s' is not defined", cfg_obj_asstring(httpobj)); From 5950b5c8034c70fdd98ebe24c31b2c3ace9780c2 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Wed, 3 Feb 2021 13:21:04 -0800 Subject: [PATCH 5/5] CHANGES --- CHANGES | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/CHANGES b/CHANGES index fb695d438e..b2b7d59835 100644 --- a/CHANGES +++ b/CHANGES @@ -1,3 +1,16 @@ +5583. [func] Changes to DoH configuration syntax: + - When "http" is specified in "listen-on" or + "listen-on-v6" statements, "tls" must also now + be specified. If an unencrypted connection is + desired (for example, when running behind a + reverse proxy), use "tls none". + - "http default" can how be specified in "listen-on" + and "listen-on-v6" statements to use the default + HTTP endpoint, "/dns-query". It is no longer + necessary to include an "http" statement in + named.conf unless overriding this value. + [GL #2472] + 5582. [bug] BIND 9 failed to build when static OpenSSL libraries were used and the *.pc files for libssl and/or libcrypto were unavailable. This has been fixed by ensuring the