From 06951472dd52eb11136502cf1ec61abfb679da20 Mon Sep 17 00:00:00 2001 From: Evan Hunt Date: Wed, 16 Sep 2020 12:21:32 -0700 Subject: [PATCH] Add parser support for DoH configuration options This commit adds stub parser support and tests for: - an "http" global option for HTTP/2 endpoint configuration. - command line options to set http or https port numbers by specifying -p http=PORT or -p https=PORT. (NOTE: this change only affects syntax; specifying HTTP and HTTPS ports on the command line currently has no effect.) - named.conf options "http-port" and "https-port" - HTTPSPORT environment variable for use when running tests. --- bin/named/config.c | 2 + bin/named/include/named/globals.h | 2 + bin/named/main.c | 14 +- bin/named/named.conf.rst | 17 +- bin/named/named.rst | 4 +- .../system/checkconf/good-doh-global.conf | 27 +++ .../system/checkconf/good-dot-global.conf | 19 ++ bin/tests/system/conf.sh.common | 1 + bin/tests/system/get_ports.sh | 1 + bin/tests/system/run.sh.in | 2 +- doc/arm/reference.rst | 19 ++ doc/man/named.8in | 2 + doc/man/named.conf.5in | 21 +- doc/misc/Makefile.am | 6 +- doc/misc/http.grammar.rst | 5 + doc/misc/options | 12 +- doc/misc/options.active | 12 +- doc/misc/options.grammar.rst | 8 +- doc/notes/notes-current.rst | 7 + lib/isccfg/Makefile.am | 6 +- lib/isccfg/httpconf.c | 180 ++++++++++++++++ lib/isccfg/include/isccfg/httpconf.h | 69 +++++++ lib/isccfg/include/isccfg/tlsconf.h | 69 +++++++ lib/isccfg/namedconf.c | 35 ++++ lib/isccfg/tlsconf.c | 194 ++++++++++++++++++ 25 files changed, 719 insertions(+), 15 deletions(-) create mode 100644 bin/tests/system/checkconf/good-doh-global.conf create mode 100644 bin/tests/system/checkconf/good-dot-global.conf create mode 100644 doc/misc/http.grammar.rst create mode 100644 lib/isccfg/httpconf.c create mode 100644 lib/isccfg/include/isccfg/httpconf.h create mode 100644 lib/isccfg/include/isccfg/tlsconf.h create mode 100644 lib/isccfg/tlsconf.c diff --git a/bin/named/config.c b/bin/named/config.c index 72094f8302..99af2dd570 100644 --- a/bin/named/config.c +++ b/bin/named/config.c @@ -94,6 +94,8 @@ options {\n\ # pid-file \"" NAMED_LOCALSTATEDIR "/run/named/named.pid\"; \n\ port 53;\n\ tls-port 853;\n\ + http-port 80;\n\ + https-port 443;\n\ prefetch 2 9;\n\ recursing-file \"named.recursing\";\n\ recursive-clients 1000;\n\ diff --git a/bin/named/include/named/globals.h b/bin/named/include/named/globals.h index 501bedaea4..302eb10cd3 100644 --- a/bin/named/include/named/globals.h +++ b/bin/named/include/named/globals.h @@ -73,6 +73,8 @@ EXTERN const char *named_g_configargs INIT(PACKAGE_CONFIGARGS); EXTERN const char *named_g_builder INIT(PACKAGE_BUILDER); EXTERN in_port_t named_g_port INIT(0); EXTERN in_port_t named_g_tlsport INIT(0); +EXTERN in_port_t named_g_httpport INIT(0); +EXTERN in_port_t named_g_httpsport INIT(0); EXTERN isc_dscp_t named_g_dscp INIT(-1); EXTERN named_server_t *named_g_server INIT(NULL); diff --git a/bin/named/main.c b/bin/named/main.c index eacb18cc81..cfdc941e05 100644 --- a/bin/named/main.c +++ b/bin/named/main.c @@ -705,7 +705,7 @@ parse_T_opt(char *option) { static void parse_port(char *arg) { - enum { DNSPORT, TLSPORT } ptype = DNSPORT; + enum { DNSPORT, TLSPORT, HTTPSPORT, HTTPPORT } ptype = DNSPORT; char *value = arg; int port; @@ -714,6 +714,12 @@ parse_port(char *arg) { } else if (strncmp(arg, "tls=", 4) == 0) { value = arg + 4; ptype = TLSPORT; + } else if (strncmp(arg, "https=", 6) == 0) { + value = arg + 6; + ptype = HTTPSPORT; + } else if (strncmp(arg, "http=", 5) == 0) { + value = arg + 6; + ptype = HTTPPORT; } port = parse_int(value, "port"); @@ -728,6 +734,12 @@ parse_port(char *arg) { case TLSPORT: named_g_tlsport = port; break; + case HTTPSPORT: + named_g_httpsport = port; + break; + case HTTPPORT: + named_g_httpport = port; + break; default: INSIST(0); ISC_UNREACHABLE(); diff --git a/bin/named/named.conf.rst b/bin/named/named.conf.rst index 722c514dbc..08a1476bc3 100644 --- a/bin/named/named.conf.rst +++ b/bin/named/named.conf.rst @@ -86,6 +86,15 @@ DYNDB dyndb string quoted_string { unspecified-text }; +HTTP +^^^^ + +:: + + http string { + endpoints { quoted_string; ... }; + }; + KEY ^^^ @@ -264,6 +273,8 @@ OPTIONS glue-cache boolean;// deprecated heartbeat-interval integer; hostname ( quoted_string | none ); + http-port integer; + https-port integer; inline-signing boolean; interface-interval duration; ipv4only-contact string; @@ -275,10 +286,12 @@ OPTIONS key-directory quoted_string; lame-ttl duration; listen-on [ port integer ] [ dscp - integer ] [ tls string ] { + integer ] [ tls string ] [ http + string ] { address_match_element; ... }; listen-on-v6 [ port integer ] [ dscp - integer ] [ tls string ] { + integer ] [ tls string ] [ http + string ] { address_match_element; ... }; lmdb-mapsize sizeval; lock-file ( quoted_string | none ); diff --git a/bin/named/named.rst b/bin/named/named.rst index 9b039cdbf0..c2b19f6dd6 100644 --- a/bin/named/named.rst +++ b/bin/named/named.rst @@ -115,7 +115,9 @@ Options ``portnum``; if not not specified, the default is port 53. If ``value`` is of the form ``tls=``, the server will listen for TLS queries on ``portnum``; the default is 853. - + If ``value`` is of the form ``https=``, the server will + listen for HTTPS queries on ``portnum``; the default is 443. + ``-s`` This option writes memory usage statistics to ``stdout`` on exit. diff --git a/bin/tests/system/checkconf/good-doh-global.conf b/bin/tests/system/checkconf/good-doh-global.conf new file mode 100644 index 0000000000..f5eb63477f --- /dev/null +++ b/bin/tests/system/checkconf/good-doh-global.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 http local-http-server { 10.53.0.1; }; +}; diff --git a/bin/tests/system/checkconf/good-dot-global.conf b/bin/tests/system/checkconf/good-dot-global.conf new file mode 100644 index 0000000000..2a1729752d --- /dev/null +++ b/bin/tests/system/checkconf/good-dot-global.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. + */ + +tls local-tls { + key-file "key.pem"; + cert-file "cert.pem"; +}; + +options { + listen-on port 853 tls local-tls { 10.53.0.1; }; +}; diff --git a/bin/tests/system/conf.sh.common b/bin/tests/system/conf.sh.common index de998c050e..df88dd1d10 100644 --- a/bin/tests/system/conf.sh.common +++ b/bin/tests/system/conf.sh.common @@ -668,6 +668,7 @@ copy_setports() { atsign="@" sed -e "s/${atsign}PORT${atsign}/${PORT}/g" \ -e "s/${atsign}TLSPORT${atsign}/${TLSPORT}/g" \ + -e "s/${atsign}HTTPSPORT${atsign}/${HTTPSPORT}/g" \ -e "s/${atsign}EXTRAPORT1${atsign}/${EXTRAPORT1}/g" \ -e "s/${atsign}EXTRAPORT2${atsign}/${EXTRAPORT2}/g" \ -e "s/${atsign}EXTRAPORT3${atsign}/${EXTRAPORT3}/g" \ diff --git a/bin/tests/system/get_ports.sh b/bin/tests/system/get_ports.sh index eac854d65c..f63ba47253 100755 --- a/bin/tests/system/get_ports.sh +++ b/bin/tests/system/get_ports.sh @@ -82,6 +82,7 @@ done echo "export PORT=$(get_port "$baseport")" echo "export TLSPORT=$(get_port)" +echo "export HTTPSPORT=$(get_port)" echo "export EXTRAPORT1=$(get_port)" echo "export EXTRAPORT2=$(get_port)" echo "export EXTRAPORT3=$(get_port)" diff --git a/bin/tests/system/run.sh.in b/bin/tests/system/run.sh.in index e802332c8c..3802181762 100644 --- a/bin/tests/system/run.sh.in +++ b/bin/tests/system/run.sh.in @@ -149,7 +149,7 @@ stop_servers() { echostart "S:$systest:$(date_with_args)" echoinfo "T:$systest:1:A" echoinfo "A:$systest:System test $systest" -echoinfo "I:$systest:PORTS:${PORT},${TLSPORT},${EXTRAPORT1},${EXTRAPORT2},${EXTRAPORT3},${EXTRAPORT4},${EXTRAPORT5},${EXTRAPORT6},${EXTRAPORT7},${EXTRAPORT8},${CONTROLPORT}" +echoinfo "I:$systest:PORTS:${PORT},${TLSPORT},${HTTPSPORT},${EXTRAPORT1},${EXTRAPORT2},${EXTRAPORT3},${EXTRAPORT4},${EXTRAPORT5},${EXTRAPORT6},${EXTRAPORT7},${EXTRAPORT8},${CONTROLPORT}" $PERL ${srcdir}/testsock.pl -p "$PORT" || { echowarn "I:$systest:Network interface aliases not set up. Skipping test." diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index f3599be319..7f304cfc11 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -1078,6 +1078,14 @@ default is used. the default is the ``named`` working directory. See :ref:`acl` for details about ``geoip`` ACLs. +.. _https_endpoint: + +``https-endpoint`` + This configures a DNS-over-HTTPS (DoH) service endpoint. It takes a + string which specifies the endpoint URL path, and an ``https-server`` + parameter specifying the server name of an HTTPS listener. (See + :ref:`Link title `.) + ``key-directory`` This is the directory where the public and private DNSSEC key files should be found when performing a dynamic update of secure zones, if different @@ -2436,6 +2444,8 @@ Interfaces The interfaces and ports that the server answers queries from may be specified using the ``listen-on`` and ``listen-on-v6`` options. +specified using the ``listen-on`` and ``listen-on-v6`` options, as +well as the ``https-server`` option for HTTPS queries. ``listen-on`` takes an optional port, an optional TLS configuration identifier, and an ``address_match_list`` of IPv4 addresses. (IPv6 @@ -2488,6 +2498,15 @@ To instruct the server not to listen on any IPv6 addresses, use: listen-on-v6 { none; }; +.. _https_server: + +``https-server`` takes a server name, an optional port, a TLS +configuration identifier, and an ``address_match_list`` of both IPv4 and +IPv6 addresses. This sets up an HTTPS responder using the key and +certificate specified in the referenced ``tls`` statement. The endpoint +for incoming HTTPS queries must be specified using the ``https-endpoint`` +option (see :ref:`Link title `). + .. _query_address: Query Address diff --git a/doc/man/named.8in b/doc/man/named.8in index db3ddd4176..f758454cde 100644 --- a/doc/man/named.8in +++ b/doc/man/named.8in @@ -115,6 +115,8 @@ for queries. If \fBvalue\fP is of the form \fB\fP or \fBportnum\fP; if not not specified, the default is port 53. If \fBvalue\fP is of the form \fBtls=\fP, the server will listen for TLS queries on \fBportnum\fP; the default is 853. +If \fBvalue\fP is of the form \fBhttps=\fP, the server will +listen for HTTPS queries on \fBportnum\fP; the default is 443. .TP .B \fB\-s\fP This option writes memory usage statistics to \fBstdout\fP on exit. diff --git a/doc/man/named.conf.5in b/doc/man/named.conf.5in index 7884fd5f28..a26bcf3c0b 100644 --- a/doc/man/named.conf.5in +++ b/doc/man/named.conf.5in @@ -132,6 +132,19 @@ dyndb string quoted_string { .fi .UNINDENT .UNINDENT +.SS HTTP +.INDENT 0.0 +.INDENT 3.5 +.sp +.nf +.ft C +http string { + endpoints { quoted_string; ... }; +}; +.ft P +.fi +.UNINDENT +.UNINDENT .SS KEY .INDENT 0.0 .INDENT 3.5 @@ -327,6 +340,8 @@ options { glue\-cache boolean;// deprecated heartbeat\-interval integer; hostname ( quoted_string | none ); + http\-port integer; + https\-port integer; inline\-signing boolean; interface\-interval duration; ipv4only\-contact string; @@ -338,10 +353,12 @@ options { key\-directory quoted_string; lame\-ttl duration; listen\-on [ port integer ] [ dscp - integer ] [ tls string ] { + integer ] [ tls string ] [ http + string ] { address_match_element; ... }; listen\-on\-v6 [ port integer ] [ dscp - integer ] [ tls string ] { + integer ] [ tls string ] [ http + string ] { address_match_element; ... }; lmdb\-mapsize sizeval; lock\-file ( quoted_string | none ); diff --git a/doc/misc/Makefile.am b/doc/misc/Makefile.am index d4db7194a6..e78e108171 100644 --- a/doc/misc/Makefile.am +++ b/doc/misc/Makefile.am @@ -37,7 +37,8 @@ OPTIONS_FILES = \ tls.grammar.rst \ trust-anchors.grammar.rst \ managed-keys.grammar.rst \ - trusted-keys.grammar.rst + trusted-keys.grammar.rst \ + http.grammar.rst EXTRA_DIST = \ $(OPTIONS_FILES) \ @@ -175,4 +176,7 @@ managed-keys.grammar.rst: options.active trusted-keys.grammar.rst: options.active $(AM_V_RST_GRAMMARS)$(PERL) $(srcdir)/rst-grammars.pl options.active trusted-keys > $@ +http.grammar.rst: options.active + $(AM_V_RST_GRAMMARS)$(PERL) $(srcdir)/rst-grammars.pl options.active http > $@ + endif diff --git a/doc/misc/http.grammar.rst b/doc/misc/http.grammar.rst new file mode 100644 index 0000000000..dcb66a2a47 --- /dev/null +++ b/doc/misc/http.grammar.rst @@ -0,0 +1,5 @@ +:: + + http { + endpoints { ; ... }; + }; diff --git a/doc/misc/options b/doc/misc/options index 1e5bedec9c..39da1adf99 100644 --- a/doc/misc/options +++ b/doc/misc/options @@ -42,6 +42,10 @@ dnssec-policy { dyndb { }; // may occur multiple times +http { + endpoints { ; ... }; +}; // may occur multiple times + key { algorithm ; secret ; @@ -193,6 +197,8 @@ options { glue-cache ; // deprecated heartbeat-interval ; hostname ( | none ); + http-port ; + https-port ; inline-signing ; interface-interval ; ipv4only-contact ; @@ -204,10 +210,12 @@ options { key-directory ; lame-ttl ; listen-on [ port ] [ dscp - ] [ tls ] { + ] [ tls ] [ http + ] { ; ... }; // may occur multiple times listen-on-v6 [ port ] [ dscp - ] [ tls ] { + ] [ tls ] [ http + ] { ; ... }; // may occur multiple times lmdb-mapsize ; lock-file ( | none ); diff --git a/doc/misc/options.active b/doc/misc/options.active index 0e007d6855..9ac0f03351 100644 --- a/doc/misc/options.active +++ b/doc/misc/options.active @@ -41,6 +41,10 @@ dnssec-policy { dyndb { }; // may occur multiple times +http { + endpoints { ; ... }; +}; // may occur multiple times + key { algorithm ; secret ; @@ -192,6 +196,8 @@ options { glue-cache ; // deprecated heartbeat-interval ; hostname ( | none ); + http-port ; + https-port ; inline-signing ; interface-interval ; ipv4only-contact ; @@ -203,10 +209,12 @@ options { key-directory ; lame-ttl ; listen-on [ port ] [ dscp - ] [ tls ] { + ] [ tls ] [ http + ] { ; ... }; // may occur multiple times listen-on-v6 [ port ] [ dscp - ] [ tls ] { + ] [ tls ] [ http + ] { ; ... }; // may occur multiple times lmdb-mapsize ; lock-file ( | none ); diff --git a/doc/misc/options.grammar.rst b/doc/misc/options.grammar.rst index 30aee7b7c9..5176f572bd 100644 --- a/doc/misc/options.grammar.rst +++ b/doc/misc/options.grammar.rst @@ -119,6 +119,8 @@ glue-cache ; // deprecated heartbeat-interval ; hostname ( | none ); + http-port ; + https-port ; inline-signing ; interface-interval ; ipv4only-contact ; @@ -130,10 +132,12 @@ key-directory ; lame-ttl ; listen-on [ port ] [ dscp - ] [ tls ] { + ] [ tls ] [ http + ] { ; ... }; listen-on-v6 [ port ] [ dscp - ] [ tls ] { + ] [ tls ] [ http + ] { ; ... }; lmdb-mapsize ; lock-file ( | none ); diff --git a/doc/notes/notes-current.rst b/doc/notes/notes-current.rst index 844902dcf4..ae6b01a7e2 100644 --- a/doc/notes/notes-current.rst +++ b/doc/notes/notes-current.rst @@ -52,6 +52,13 @@ New Features an optional ``tls`` option which specifies either a previously configured ``tls`` statement or ``ephemeral``. [GL #2392] +- ``named`` now has initial support for DNS-over-HTTP(S). Both + encrypted (via TLS) and unencrypted HTTP/2 connections are supported. + The latter are mostly there for debugging/troubleshooting + purposes and for the means of encryption offloading to third-party + software (as might be desirable in some environments to aid in TLS + certificates management). [GL !4566] + Removed Features ~~~~~~~~~~~~~~~~ diff --git a/lib/isccfg/Makefile.am b/lib/isccfg/Makefile.am index 3205e3b93b..e13294281b 100644 --- a/lib/isccfg/Makefile.am +++ b/lib/isccfg/Makefile.am @@ -7,17 +7,21 @@ libisccfg_la_HEADERS = \ include/isccfg/aclconf.h \ include/isccfg/cfg.h \ include/isccfg/grammar.h \ + include/isccfg/httpconf.h \ include/isccfg/kaspconf.h \ include/isccfg/log.h \ - include/isccfg/namedconf.h + include/isccfg/namedconf.h \ + include/isccfg/tlsconf.h libisccfg_la_SOURCES = \ $(libisccfg_la_HEADERS) \ aclconf.c \ + httpconf.c \ dnsconf.c \ kaspconf.c \ log.c \ namedconf.c \ + tlsconf.c \ parser.c libisccfg_la_CPPFLAGS = \ diff --git a/lib/isccfg/httpconf.c b/lib/isccfg/httpconf.c new file mode 100644 index 0000000000..cd210b53a0 --- /dev/null +++ b/lib/isccfg/httpconf.c @@ -0,0 +1,180 @@ +/* + * 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 https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include +#include +#include + +#include + +#include +#include + +void +cfg_http_storage_init(isc_mem_t *mctx, isc_cfg_http_storage_t *storage) { + REQUIRE(mctx != NULL); + REQUIRE(storage != NULL); + + memset(storage, 0, sizeof(*storage)); + isc_mem_attach(mctx, &storage->mctx); + ISC_LIST_INIT(storage->list); +} + +void +cfg_http_storage_uninit(isc_cfg_http_storage_t *storage) { + REQUIRE(storage != NULL); + + cfg_http_storage_clear(storage); + isc_mem_detach(&storage->mctx); +} + +void +cfg_http_storage_clear(isc_cfg_http_storage_t *storage) { + isc_mem_t *mctx = NULL; + + REQUIRE(storage != NULL); + + mctx = storage->mctx; + + if (!ISC_LIST_EMPTY(storage->list)) { + isc_cfg_http_obj_t *http = ISC_LIST_HEAD(storage->list); + while (http != NULL) { + isc_cfg_http_obj_t *next = ISC_LIST_NEXT(http, link); + ISC_LIST_DEQUEUE(storage->list, http, link); + storage->count--; + + isc_mem_free(mctx, http->name); + + if (!ISC_LIST_EMPTY(http->endpoints)) { + isc_cfg_http_endpoint_t *ep = + ISC_LIST_HEAD(http->endpoints); + while (ep != NULL) { + isc_cfg_http_endpoint_t *epnext = + ISC_LIST_NEXT(ep, link); + isc_mem_free(mctx, ep->path); + isc_mem_put(mctx, ep, sizeof(*ep)); + ep = epnext; + http->count--; + } + } + + isc_mem_put(mctx, http, sizeof(*http)); + http = next; + } + } + + INSIST(storage->count == 0); +} + +isc_cfg_http_obj_t * +cfg_http_find(const char *name, isc_cfg_http_storage_t *storage) { + isc_cfg_http_obj_t *http = NULL; + REQUIRE(name != NULL && *name != '\0'); + REQUIRE(storage != NULL); + + for (http = ISC_LIST_HEAD(storage->list); http != NULL; + http = ISC_LIST_NEXT(http, link)) + { + if (strcasecmp(name, http->name) == 0) { + break; + } + } + + return (http); +} + +static isc_result_t +push_http_obj(const cfg_obj_t *map, isc_cfg_http_storage_t *storage) { + isc_mem_t *mctx = storage->mctx; + isc_cfg_http_obj_t *new; + const cfg_obj_t *endpoints = NULL; + const cfg_listelt_t *elt; + + if (!cfg_obj_ismap(map) || map->value.map.id == NULL || + !cfg_obj_isstring(map->value.map.id)) + { + return (ISC_R_FAILURE); + } + + if (cfg_http_find(cfg_obj_asstring(map->value.map.id), storage) != NULL) + { + return (ISC_R_FAILURE); + } + + if (cfg_map_get(map, "endpoints", &endpoints) != ISC_R_SUCCESS || + !cfg_obj_islist(endpoints)) + { + return (ISC_R_FAILURE); + } + + INSIST(endpoints != NULL); + + new = isc_mem_get(mctx, sizeof(*new)); + memset(new, 0, sizeof(*new)); + ISC_LIST_INIT(new->endpoints); + new->name = isc_mem_strdup(mctx, cfg_obj_asstring(map->value.map.id)); + + for (elt = cfg_list_first(endpoints); elt != NULL; + elt = cfg_list_next(elt)) { + isc_cfg_http_endpoint_t *newep = NULL; + const cfg_obj_t *endp = cfg_listelt_value(elt); + newep = isc_mem_get(mctx, sizeof(*newep)); + ISC_LINK_INIT(newep, link); + newep->path = isc_mem_strdup(mctx, cfg_obj_asstring(endp)); + + ISC_LIST_PREPEND(new->endpoints, newep, link); + new->count++; + } + + ISC_LINK_INIT(new, link); + ISC_LIST_PREPEND(storage->list, new, link); + storage->count++; + return (ISC_R_SUCCESS); +} + +isc_result_t +cfg_http_storage_load(const cfg_obj_t *cfg_ctx, + isc_cfg_http_storage_t *storage) { + bool found = false; + isc_result_t result = ISC_R_SUCCESS; + const cfg_obj_t *http = NULL; + const cfg_listelt_t *elt; + const cfg_obj_t *map = NULL; + + REQUIRE(cfg_ctx != NULL); + REQUIRE(storage != NULL); + + cfg_http_storage_clear(storage); + result = cfg_map_get(cfg_ctx, "http", &http); + if (result != ISC_R_SUCCESS) { + /* No statements found, but it is fine. */ + return (ISC_R_SUCCESS); + } + + INSIST(http != NULL); + + for (elt = cfg_list_first(http); elt != NULL; elt = cfg_list_next(elt)) + { + map = cfg_listelt_value(elt); + INSIST(map != NULL); + found = true; + result = push_http_obj(map, storage); + if (result != ISC_R_SUCCESS) { + return (result); + } + } + + if (found == true && storage->count == 0) { + return (ISC_R_FAILURE); + } + + return (ISC_R_SUCCESS); +} diff --git a/lib/isccfg/include/isccfg/httpconf.h b/lib/isccfg/include/isccfg/httpconf.h new file mode 100644 index 0000000000..bf049b9e6d --- /dev/null +++ b/lib/isccfg/include/isccfg/httpconf.h @@ -0,0 +1,69 @@ +/* + * 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 https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#ifndef ISCCFG_HTTPCONF_H +#define ISCCFG_HTTPCONF_H 1 + +#include + +#include +#include +#include +#include + +#include + +#include +#include + +typedef struct isc_cfg_http_endpoint { + char *path; + LINK(struct isc_cfg_http_endpoint) link; +} isc_cfg_http_endpoint_t; + +typedef struct isc_cfg_http_obj { + char *name; + LINK(struct isc_cfg_http_obj) link; + ISC_LIST(isc_cfg_http_endpoint_t) endpoints; + size_t count; +} isc_cfg_http_obj_t; + +typedef struct isc_cfg_http_storage { + isc_mem_t *mctx; + ISC_LIST(isc_cfg_http_obj_t) list; + size_t count; +} isc_cfg_http_storage_t; + +/*** + *** Functions + ***/ + +ISC_LANG_BEGINDECLS + +void +cfg_http_storage_init(isc_mem_t *mctx, isc_cfg_http_storage_t *storage); + +void +cfg_http_storage_uninit(isc_cfg_http_storage_t *storage); + +isc_result_t +cfg_http_storage_load(const cfg_obj_t * cfg_ctx, + isc_cfg_http_storage_t *storage); + +isc_cfg_http_obj_t * +cfg_http_find(const char *name, isc_cfg_http_storage_t *storage); + +void +cfg_http_storage_clear(isc_cfg_http_storage_t *storage); + +ISC_LANG_ENDDECLS + +#endif /* ISCCFG_HTTPCONF_H */ diff --git a/lib/isccfg/include/isccfg/tlsconf.h b/lib/isccfg/include/isccfg/tlsconf.h new file mode 100644 index 0000000000..534236ae65 --- /dev/null +++ b/lib/isccfg/include/isccfg/tlsconf.h @@ -0,0 +1,69 @@ +/* + * 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 https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#ifndef ISCCFG_TLSCONF_H +#define ISCCFG_TLSCONF_H 1 + +#include + +#include +#include +#include +#include + +#include + +#include + +typedef struct isc_cfg_tls_obj { + char *name; + char *key_file; + char *cert_file; + char *dh_param; + char *protocols; + char *ciphers; + LINK(struct isc_cfg_tls_obj) link; +} isc_cfg_tls_obj_t; + +typedef struct isc_cfg_tls_data_storage { + isc_mem_t *mctx; + size_t count; + ISC_LIST(isc_cfg_tls_obj_t) list; +} isc_cfg_tls_data_storage_t; + +/*** + *** Functions + ***/ + +ISC_LANG_BEGINDECLS + +void +cfg_tls_storage_init(isc_mem_t *mctx, isc_cfg_tls_data_storage_t *storage); + +void +cfg_tls_storage_uninit(isc_cfg_tls_data_storage_t *storage); + +isc_result_t +cfg_tls_storage_load(const cfg_obj_t * cfg_ctx, + isc_cfg_tls_data_storage_t *storage); + +isc_cfg_tls_obj_t * +cfg_tls_storage_find(const char *name, isc_cfg_tls_data_storage_t *storage); +/* + * Looks for TLS key/certificate pair. + */ + +void +cfg_tls_storage_clear(isc_cfg_tls_data_storage_t *storage); + +ISC_LANG_ENDDECLS + +#endif /* ISCCFG_TLSCONF_H */ diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index c18196ea5b..e90ab215f8 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -81,6 +81,7 @@ static cfg_type_t cfg_type_bracketed_dscpsockaddrlist; static cfg_type_t cfg_type_bracketed_namesockaddrkeylist; static cfg_type_t cfg_type_bracketed_netaddrlist; static cfg_type_t cfg_type_bracketed_sockaddrnameportlist; +static cfg_type_t cfg_type_bracketed_qstring_list; static cfg_type_t cfg_type_controls; static cfg_type_t cfg_type_controls_sockaddr; static cfg_type_t cfg_type_destinationlist; @@ -91,6 +92,7 @@ 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; static cfg_type_t cfg_type_key; @@ -108,6 +110,7 @@ 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; @@ -151,6 +154,7 @@ static cfg_tuplefielddef_t listenon_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 }, { "acl", &cfg_type_bracketed_aml, 0 }, { NULL, NULL, 0 } }; @@ -1088,6 +1092,7 @@ static cfg_clausedef_t namedconf_clauses[] = { { "acl", &cfg_type_acl, CFG_CLAUSEFLAG_MULTI }, { "controls", &cfg_type_controls, CFG_CLAUSEFLAG_MULTI }, { "dnssec-policy", &cfg_type_dnssecpolicy, CFG_CLAUSEFLAG_MULTI }, + { "http", &cfg_type_http_description, CFG_CLAUSEFLAG_MULTI }, { "logging", &cfg_type_logging, 0 }, { "lwres", NULL, CFG_CLAUSEFLAG_MULTI | CFG_CLAUSEFLAG_ANCIENT }, { "masters", &cfg_type_primaries, CFG_CLAUSEFLAG_MULTI }, @@ -1221,6 +1226,8 @@ static cfg_clausedef_t options_clauses[] = { { "pid-file", &cfg_type_qstringornone, 0 }, { "port", &cfg_type_uint32, 0 }, { "tls-port", &cfg_type_uint32, 0 }, + { "http-port", &cfg_type_uint32, 0 }, + { "https-port", &cfg_type_uint32, 0 }, { "querylog", &cfg_type_boolean, 0 }, { "random-device", &cfg_type_qstringornone, 0 }, { "recursing-file", &cfg_type_qstring, 0 }, @@ -3853,3 +3860,31 @@ static cfg_type_t cfg_type_optional_tls = { "tlsoptional", parse_optional_keyvalue, print_keyvalue, doc_optional_keyvalue, &cfg_rep_string, &tls_kw }; + +/* http and https */ + +static cfg_type_t cfg_type_bracketed_qstring_list = { "bracketed_qstring_list", + cfg_parse_bracketed_list, + cfg_print_bracketed_list, + cfg_doc_bracketed_list, + &cfg_rep_list, + &cfg_type_qstring }; + +static cfg_clausedef_t cfg_http_description_clauses[] = { + { "endpoints", &cfg_type_bracketed_qstring_list, 0 }, { NULL, NULL, 0 } +}; + +static cfg_clausedef_t *http_description_clausesets[] = { + cfg_http_description_clauses, NULL +}; + +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 +}; diff --git a/lib/isccfg/tlsconf.c b/lib/isccfg/tlsconf.c new file mode 100644 index 0000000000..6320fc5626 --- /dev/null +++ b/lib/isccfg/tlsconf.c @@ -0,0 +1,194 @@ +/* + * 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 https://mozilla.org/MPL/2.0/. + * + * See the COPYRIGHT file distributed with this work for additional + * information regarding copyright ownership. + */ + +#include + +#include + +#include +#include + +void +cfg_tls_storage_init(isc_mem_t *mctx, isc_cfg_tls_data_storage_t *storage) { + REQUIRE(mctx != NULL); + REQUIRE(storage != NULL); + + memset(storage, 0, sizeof(*storage)); + isc_mem_attach(mctx, &storage->mctx); + ISC_LIST_INIT(storage->list); +} + +void +cfg_tls_storage_uninit(isc_cfg_tls_data_storage_t *storage) { + REQUIRE(storage != NULL); + + cfg_tls_storage_clear(storage); + isc_mem_detach(&storage->mctx); +} + +void +cfg_tls_storage_clear(isc_cfg_tls_data_storage_t *storage) { + isc_mem_t *mctx = NULL; + + REQUIRE(storage != NULL); + + mctx = storage->mctx; + + if (!ISC_LIST_EMPTY(storage->list)) { + isc_cfg_tls_obj_t *tls_obj = ISC_LIST_HEAD(storage->list); + while (tls_obj != NULL) { + isc_cfg_tls_obj_t *next = ISC_LIST_NEXT(tls_obj, link); + ISC_LIST_DEQUEUE(storage->list, tls_obj, link); + storage->count--; + + isc_mem_free(mctx, tls_obj->name); + isc_mem_free(mctx, tls_obj->key_file); + isc_mem_free(mctx, tls_obj->cert_file); + + if (tls_obj->dh_param != NULL) { + isc_mem_free(mctx, tls_obj->dh_param); + } + + if (tls_obj->protocols != NULL) { + isc_mem_free(mctx, tls_obj->protocols); + } + + if (tls_obj->ciphers != NULL) { + isc_mem_free(mctx, tls_obj->ciphers); + } + + isc_mem_put(mctx, tls_obj, sizeof(*tls_obj)); + tls_obj = next; + } + } + + INSIST(storage->count == 0); +} + +static isc_result_t +push_tls_obj(const cfg_obj_t *map, isc_cfg_tls_data_storage_t *storage) { + isc_mem_t *mctx = storage->mctx; + isc_cfg_tls_obj_t *new = NULL; + const cfg_obj_t *key_file = NULL, *cert_file = NULL, *dh_param = NULL, + *protocols = NULL, *ciphers = NULL; + + if (!cfg_obj_ismap(map) || map->value.map.id == NULL || + !cfg_obj_isstring(map->value.map.id)) + { + return (ISC_R_FAILURE); + } + + if (cfg_tls_storage_find(cfg_obj_asstring(map->value.map.id), + storage) != NULL) { + return (ISC_R_FAILURE); + } + + if (cfg_map_get(map, "key-file", &key_file) != ISC_R_SUCCESS || + !cfg_obj_isstring(key_file)) + { + return (ISC_R_FAILURE); + } + INSIST(key_file != NULL); + + if (cfg_map_get(map, "cert-file", &cert_file) != ISC_R_SUCCESS) { + return (ISC_R_FAILURE); + } + INSIST(cert_file != NULL); + + (void)cfg_map_get(map, "dh-param", &dh_param); + (void)cfg_map_get(map, "protocols", &protocols); + (void)cfg_map_get(map, "ciphers", &ciphers); + + new = isc_mem_get(mctx, sizeof(*new)); + *new = (isc_cfg_tls_obj_t){ + .name = isc_mem_strdup(mctx, + cfg_obj_asstring(map->value.map.id)), + .key_file = isc_mem_strdup(mctx, cfg_obj_asstring(key_file)), + .cert_file = isc_mem_strdup(mctx, cfg_obj_asstring(cert_file)), + }; + + if (dh_param != NULL && cfg_obj_isstring(dh_param)) { + new->dh_param = isc_mem_strdup(mctx, + cfg_obj_asstring(dh_param)); + } + + if (protocols != NULL && cfg_obj_isstring(protocols)) { + new->protocols = isc_mem_strdup(mctx, + cfg_obj_asstring(protocols)); + } + + if (ciphers != NULL && cfg_obj_isstring(ciphers)) { + new->ciphers = isc_mem_strdup(mctx, cfg_obj_asstring(ciphers)); + } + + ISC_LINK_INIT(new, link); + ISC_LIST_PREPEND(storage->list, new, link); + storage->count++; + return (ISC_R_SUCCESS); +} + +isc_result_t +cfg_tls_storage_load(const cfg_obj_t *cfg_ctx, + isc_cfg_tls_data_storage_t *storage) { + isc_result_t result = ISC_R_SUCCESS; + bool found = false; + const cfg_obj_t *tls = NULL; + const cfg_listelt_t *elt; + const cfg_obj_t *map = NULL; + + REQUIRE(cfg_ctx != NULL); + REQUIRE(storage != NULL); + + result = cfg_map_get(cfg_ctx, "tls", &tls); + if (result != ISC_R_SUCCESS) { + /* No tls statements found, but it is fine. */ + return (ISC_R_SUCCESS); + } + INSIST(tls != NULL); + + cfg_tls_storage_clear(storage); + + for (elt = cfg_list_first(tls); elt != NULL; elt = cfg_list_next(elt)) { + map = cfg_listelt_value(elt); + INSIST(map != NULL); + found = true; + result = push_tls_obj(map, storage); + if (result != ISC_R_SUCCESS) { + return (result); + } + } + + if (found == true && storage->count == 0) { + return (ISC_R_FAILURE); + } + + return (ISC_R_SUCCESS); +} + +isc_cfg_tls_obj_t * +cfg_tls_storage_find(const char *name, isc_cfg_tls_data_storage_t *storage) { + isc_cfg_tls_obj_t *tls_obj = NULL; + REQUIRE(storage != NULL); + + if (name == NULL) { + return (NULL); + } + + for (tls_obj = ISC_LIST_HEAD(storage->list); tls_obj != NULL; + tls_obj = ISC_LIST_NEXT(tls_obj, link)) + { + if (strcasecmp(name, tls_obj->name) == 0) { + break; + } + } + + return (tls_obj); +}