]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
Add "protocols" options to the "tls" clause
authorArtem Boldariev <artem@boldariev.com>
Mon, 13 Sep 2021 12:39:36 +0000 (15:39 +0300)
committerArtem Boldariev <artem@boldariev.com>
Fri, 1 Oct 2021 12:50:43 +0000 (15:50 +0300)
This commit adds the ability to specify allowed TLS protocols versions
within the "tls" clause. If an unsupported TLS protocol version is
specified in a file, the configuration file will not pass
verification.

Also, this commit adds strict checks for "tls" clauses verification,
in particular:

- it ensures that loading configuration files containing duplicated
"tls" clauses is not allowed;

- it ensures that loading configuration files containing "tls" clauses
missing "cert-file" or "key-file" is not allowed;

- it ensures that loading configuration files containing "tls" clauses
named as "ephemeral" or "none" is not allowed.

21 files changed:
bin/named/named.conf.rst
bin/named/server.c
bin/tests/system/checkconf/bad-dot-badprotocol.conf [new file with mode: 0644]
bin/tests/system/checkconf/bad-dot-duplicatetls.conf [new file with mode: 0644]
bin/tests/system/checkconf/bad-dot-ephemeral.conf [new file with mode: 0644]
bin/tests/system/checkconf/bad-dot-nocert.conf [new file with mode: 0644]
bin/tests/system/checkconf/bad-dot-nokey.conf [new file with mode: 0644]
bin/tests/system/checkconf/bad-dot-none.conf [new file with mode: 0644]
bin/tests/system/checkconf/good-doh-tlsopts.conf [new file with mode: 0644]
bin/tests/system/checkconf/good-dot-tlsopts.conf [new file with mode: 0644]
doc/arm/reference.rst
doc/man/named.conf.5in
doc/misc/options
doc/misc/options.active
doc/misc/tls.grammar.rst
lib/bind9/check.c
lib/isc/include/isc/tls.h
lib/isc/tls.c
lib/isccfg/namedconf.c
lib/ns/include/ns/listenlist.h
lib/ns/listenlist.c

index d805ff65adb81823193fd5ed69c378abc080985e..051f9bf3fee3681f276d8c96bf9bd790f3345d09 100644 (file)
@@ -567,7 +567,7 @@ TLS
        dh-param quoted_string; // experimental
        hostname quoted_string;
        key-file quoted_string;
-       protocols sslprotos; // experimental
+       protocols { string; ... };
   };
 
 TRUST-ANCHORS
index 4183786f63e6cfb64d33154459a2c64fe286c0d0..d73210f38ecef1146c72c8d77f5d33967dfbc4d4 100644 (file)
@@ -413,9 +413,9 @@ named_server_reload(isc_task_t *task, isc_event_t *event);
 
 #ifdef HAVE_LIBNGHTTP2
 static isc_result_t
-listenelt_http(const cfg_obj_t *http, bool tls, const char *key,
-              const char *cert, in_port_t port, isc_mem_t *mctx,
-              ns_listenelt_t **target);
+listenelt_http(const cfg_obj_t *http, bool tls,
+              const ns_listen_tls_params_t *tls_params, in_port_t port,
+              isc_mem_t *mctx, ns_listenelt_t **target);
 #endif
 
 static isc_result_t
@@ -11026,6 +11026,8 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
        const char *key = NULL, *cert = NULL;
        bool do_tls = false, no_tls = false, http = false;
        ns_listenelt_t *delt = NULL;
+       uint32_t tls_protos = 0;
+       ns_listen_tls_params_t tls_params = { 0 };
 
        REQUIRE(target != NULL && *target == NULL);
 
@@ -11043,6 +11045,7 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
                } else {
                        const cfg_obj_t *keyobj = NULL, *certobj = NULL;
                        const cfg_obj_t *tlsmap = NULL;
+                       const cfg_obj_t *tls_proto_list = NULL;
 
                        do_tls = true;
 
@@ -11059,9 +11062,35 @@ 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);
+
+                       if (cfg_map_get(tlsmap, "protocols", &tls_proto_list) ==
+                           ISC_R_SUCCESS) {
+                               const cfg_listelt_t *proto = NULL;
+                               INSIST(tls_proto_list != NULL);
+                               for (proto = cfg_list_first(tls_proto_list);
+                                    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;
+                               }
+                       }
                }
        }
 
+       tls_params = (ns_listen_tls_params_t){ .key = key,
+                                              .cert = cert,
+                                              .protocols = tls_protos };
+
        httpobj = cfg_tuple_get(ltup, "http");
        if (httpobj != NULL && cfg_obj_isstring(httpobj)) {
                const char *httpname = cfg_obj_asstring(httpobj);
@@ -11144,14 +11173,14 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config,
 
 #ifdef HAVE_LIBNGHTTP2
        if (http) {
-               CHECK(listenelt_http(http_server, do_tls, key, cert, port, mctx,
-                                    &delt));
+               CHECK(listenelt_http(http_server, do_tls, &tls_params, port,
+                                    mctx, &delt));
        }
 #endif /* HAVE_LIBNGHTTP2 */
 
        if (!http) {
-               CHECK(ns_listenelt_create(mctx, port, dscp, NULL, do_tls, key,
-                                         cert, &delt));
+               CHECK(ns_listenelt_create(mctx, port, dscp, NULL, do_tls,
+                                         &tls_params, &delt));
        }
 
        result = cfg_acl_fromconfig2(cfg_tuple_get(listener, "acl"), config,
@@ -11169,9 +11198,9 @@ cleanup:
 
 #ifdef HAVE_LIBNGHTTP2
 static isc_result_t
-listenelt_http(const cfg_obj_t *http, bool tls, const char *key,
-              const char *cert, in_port_t port, isc_mem_t *mctx,
-              ns_listenelt_t **target) {
+listenelt_http(const cfg_obj_t *http, bool tls,
+              const ns_listen_tls_params_t *tls_params, in_port_t port,
+              isc_mem_t *mctx, ns_listenelt_t **target) {
        isc_result_t result = ISC_R_SUCCESS;
        ns_listenelt_t *delt = NULL;
        char **endpoints = NULL;
@@ -11184,7 +11213,11 @@ listenelt_http(const cfg_obj_t *http, bool tls, const char *key,
        isc_quota_t *quota = NULL;
 
        REQUIRE(target != NULL && *target == NULL);
-       REQUIRE((key == NULL) == (cert == NULL));
+
+       if (tls) {
+               INSIST(tls_params != NULL);
+               INSIST((tls_params->key == NULL) == (tls_params->cert == NULL));
+       }
 
        if (port == 0) {
                port = tls ? named_g_httpsport : named_g_httpport;
@@ -11239,7 +11272,7 @@ listenelt_http(const cfg_obj_t *http, bool tls, const char *key,
                isc_quota_init(quota, max_clients);
        }
        result = ns_listenelt_create_http(mctx, port, named_g_dscp, NULL, tls,
-                                         key, cert, endpoints, len, quota,
+                                         tls_params, endpoints, len, quota,
                                          max_streams, &delt);
        if (result != ISC_R_SUCCESS) {
                goto error;
diff --git a/bin/tests/system/checkconf/bad-dot-badprotocol.conf b/bin/tests/system/checkconf/bad-dot-badprotocol.conf
new file mode 100644 (file)
index 0000000..da3b5ed
--- /dev/null
@@ -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";
+       protocols { unknown; TLSv1.2; }; # bad TLS protocol version name
+};
+
+options {
+       listen-on port 853 tls local-tls { 10.53.0.1; };
+};
diff --git a/bin/tests/system/checkconf/bad-dot-duplicatetls.conf b/bin/tests/system/checkconf/bad-dot-duplicatetls.conf
new file mode 100644 (file)
index 0000000..63f5874
--- /dev/null
@@ -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";
+};
+
+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/checkconf/bad-dot-ephemeral.conf b/bin/tests/system/checkconf/bad-dot-ephemeral.conf
new file mode 100644 (file)
index 0000000..3f02730
--- /dev/null
@@ -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.
+ */
+
+# ephemeral is reserved for internal use
+tls ephemeral {
+       key-file "key.pem";
+       cert-file "cert.pem";
+};
+
+options {
+       listen-on port 853 tls ephemeral { 10.53.0.1; };
+};
diff --git a/bin/tests/system/checkconf/bad-dot-nocert.conf b/bin/tests/system/checkconf/bad-dot-nocert.conf
new file mode 100644 (file)
index 0000000..20c1eaf
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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";
+};
+
+http local-http-server {
+       endpoints { "/dns-query"; };
+       listener-clients 100;
+       streams-per-connection 100;
+};
+
+options {
+       listen-on { 10.53.0.1; };
+       http-port 80;
+       https-port 443;
+       http-listener-clients 100;
+       http-streams-per-connection 100;
+       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/bin/tests/system/checkconf/bad-dot-nokey.conf b/bin/tests/system/checkconf/bad-dot-nokey.conf
new file mode 100644 (file)
index 0000000..a6798ba
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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 {
+       cert-file "cert.pem";
+};
+
+http local-http-server {
+       endpoints { "/dns-query"; };
+       listener-clients 100;
+       streams-per-connection 100;
+};
+
+options {
+       listen-on { 10.53.0.1; };
+       http-port 80;
+       https-port 443;
+       http-listener-clients 100;
+       http-streams-per-connection 100;
+       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/bin/tests/system/checkconf/bad-dot-none.conf b/bin/tests/system/checkconf/bad-dot-none.conf
new file mode 100644 (file)
index 0000000..1eebc25
--- /dev/null
@@ -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.
+ */
+
+# none is reserved for internal use
+tls none {
+       key-file "key.pem";
+       cert-file "cert.pem";
+};
+
+options {
+       listen-on port 853 tls none { 10.53.0.1; };
+};
diff --git a/bin/tests/system/checkconf/good-doh-tlsopts.conf b/bin/tests/system/checkconf/good-doh-tlsopts.conf
new file mode 100644 (file)
index 0000000..90a2f95
--- /dev/null
@@ -0,0 +1,32 @@
+/*
+ * 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 {
+       protocols { TLSv1.2; };
+       key-file "key.pem";
+       cert-file "cert.pem";
+};
+
+http local-http-server {
+       endpoints { "/dns-query"; };
+       listener-clients 100;
+       streams-per-connection 100;
+};
+
+options {
+       listen-on { 10.53.0.1; };
+       http-port 80;
+       https-port 443;
+       http-listener-clients 100;
+       http-streams-per-connection 100;
+       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/bin/tests/system/checkconf/good-dot-tlsopts.conf b/bin/tests/system/checkconf/good-dot-tlsopts.conf
new file mode 100644 (file)
index 0000000..3552eef
--- /dev/null
@@ -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 {
+       protocols { TLSv1.2; };
+       key-file "key.pem";
+       cert-file "cert.pem";
+};
+
+options {
+       listen-on port 853 tls local-tls { 10.53.0.1; };
+};
index 0a4627bf408ad26fc00d7354d06f613e06b4fe74..b36db11467ec61dc4a0b8f5ac6a143ab82204c86 100644 (file)
@@ -293,7 +293,7 @@ The following statements are supported:
         Declares communication channels to get access to ``named`` statistics.
 
     ``tls``
-        Specifies configuration information for a TLS connection, including a ``key-file``, ``cert-file``, ``ca-file`` and ``hostname``.
+        Specifies configuration information for a TLS connection, including a ``key-file``, ``cert-file``, ``ca-file``, ``hostname``, and ``protocols``.
 
     ``http``
         Specifies configuration information for an HTTP connection, including ``endponts``, ``listener-clients`` and ``streams-per-connection``.
@@ -4772,6 +4772,12 @@ The following options can be specified in a ``tls`` statement:
   ``hostname``
     The hostname associated with the certificate.
 
+  ``protocols``
+    Allowed versions of the TLS protocol. TLS version 1.2 and higher are
+    supported, depending on the cryptographic library in use. Multiple
+    versions might be specified (e.g.
+    ``protocols { TLSv1.2; TLSv1.3; };``).
+
 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
index d22ad8022dc7d4b51f991f48213a21ec619bc292..0cce71b6771c3611489b2a9193b764fda014aeff 100644 (file)
@@ -658,7 +658,7 @@ tls string {
       dh\-param quoted_string; // experimental
       hostname quoted_string;
       key\-file quoted_string;
-      protocols sslprotos; // experimental
+      protocols { string; ... };
 };
 .ft P
 .fi
index 7b82fcec62ff5e0842a5b4c0a82328e33ac9648f..aa89cd4ed18a3aaf3ec601e34c0acf9bdafbf539 100644 (file)
@@ -463,7 +463,7 @@ tls <string> {
         dh-param <quoted_string>; // experimental
         hostname <quoted_string>;
         key-file <quoted_string>;
-        protocols <sslprotos>; // experimental
+        protocols { <string>; ... };
 }; // may occur multiple times
 
 trust-anchors { <string> ( static-key |
index b76a310443571c81f9f0fb4c6ebf27d8a69c8ef0..a745dc632734d31fd247d2d410f5d549cffb1017 100644 (file)
@@ -460,7 +460,7 @@ tls <string> {
         dh-param <quoted_string>; // experimental
         hostname <quoted_string>;
         key-file <quoted_string>;
-        protocols <sslprotos>; // experimental
+        protocols { <string>; ... };
 }; // may occur multiple times
 
 trust-anchors { <string> ( static-key |
index f610a77d59b15d78a18d287757c4fba04ccb1a56..c0b5a48819f9575d92cc15286a71cf28ff8d62cc 100644 (file)
@@ -7,5 +7,5 @@
        dh-param <quoted_string>; // experimental
        hostname <quoted_string>;
        key-file <quoted_string>;
-       protocols <sslprotos>; // experimental
+       protocols { <string>; ... };
   };
index 88dcc4d73ccedb8ba7308d6bb767bfcc99566c15..d9c14127dd1f17830b285f382c485cdfdba7ffb2 100644 (file)
@@ -16,6 +16,8 @@
 #include <stdbool.h>
 #include <stdlib.h>
 
+#include <openssl/opensslv.h>
+
 #ifdef HAVE_DNSTAP
 #include <fstrm.h>
 #endif
@@ -2116,6 +2118,149 @@ done:
 }
 #endif /* HAVE_LIBNGHTTP2 */
 
+static isc_result_t
+bind9_check_tls_defintion(const cfg_obj_t *tlsobj, const char *name,
+                         isc_log_t *logctx, isc_symtab_t *symtab) {
+       isc_result_t result, tresult;
+       const cfg_obj_t *tls_proto_list = NULL, *tls_key = NULL,
+                       *tls_cert = NULL;
+       uint32_t tls_protos = 0;
+       isc_symvalue_t symvalue;
+
+       if (strcasecmp(name, "ephemeral") == 0 || strcasecmp(name, "none") == 0)
+       {
+               cfg_obj_log(tlsobj, logctx, ISC_LOG_ERROR,
+                           "tls clause name '%s' is reserved for internal use",
+                           name);
+               result = ISC_R_FAILURE;
+       } else {
+               /* Check for duplicates */
+               symvalue.as_cpointer = tlsobj;
+               result = isc_symtab_define(symtab, name, 1, symvalue,
+                                          isc_symexists_reject);
+               if (result == ISC_R_EXISTS) {
+                       const char *file = NULL;
+                       unsigned int line;
+
+                       tresult = isc_symtab_lookup(symtab, name, 1, &symvalue);
+                       RUNTIME_CHECK(tresult == ISC_R_SUCCESS);
+
+                       line = cfg_obj_line(symvalue.as_cpointer);
+                       file = cfg_obj_file(symvalue.as_cpointer);
+                       if (file == NULL) {
+                               file = "<unknown file>";
+                       }
+
+                       cfg_obj_log(tlsobj, logctx, ISC_LOG_ERROR,
+                                   "tls clause '%s' is duplicated: "
+                                   "also defined at %s:%u",
+                                   name, file, line);
+               }
+       }
+
+       if (cfg_map_get(tlsobj, "key-file", &tls_key) != ISC_R_SUCCESS) {
+               cfg_obj_log(tlsobj, logctx, ISC_LOG_ERROR,
+                           "'key-file' is required in tls clause '%s'", name);
+               result = ISC_R_FAILURE;
+       }
+
+       if (cfg_map_get(tlsobj, "cert-file", &tls_cert) != ISC_R_SUCCESS) {
+               cfg_obj_log(tlsobj, logctx, ISC_LOG_ERROR,
+                           "'cert-file' is required in tls clause '%s'", name);
+               result = ISC_R_FAILURE;
+       }
+
+       /* Check protocols are valid */
+       tresult = cfg_map_get(tlsobj, "protocols", &tls_proto_list);
+       if (tresult == ISC_R_SUCCESS) {
+               const cfg_listelt_t *proto = NULL;
+               INSIST(tls_proto_list != NULL);
+               for (proto = cfg_list_first(tls_proto_list); 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);
+
+                       if (ver == ISC_TLS_PROTO_VER_UNDEFINED) {
+                               cfg_obj_log(tls_proto_obj, logctx,
+                                           ISC_LOG_ERROR,
+                                           "'%s' is not a valid "
+                                           "TLS protocol version",
+                                           tls_sver);
+                               result = ISC_R_FAILURE;
+                               continue;
+                       } else if (!isc_tls_protocol_supported(ver)) {
+                               cfg_obj_log(tls_proto_obj, logctx,
+                                           ISC_LOG_ERROR,
+                                           "'%s' is not "
+                                           "supported by the "
+                                           "cryptographic library version in "
+                                           "use (%s)",
+                                           tls_sver, OPENSSL_VERSION_TEXT);
+                               result = ISC_R_FAILURE;
+                       }
+
+                       if ((tls_protos & ver) != 0) {
+                               cfg_obj_log(tls_proto_obj, logctx,
+                                           ISC_LOG_WARNING,
+                                           "'%s' is specified more than once "
+                                           "in '%s'",
+                                           tls_sver, name);
+                               result = ISC_R_FAILURE;
+                       }
+
+                       tls_protos |= ver;
+               }
+
+               if (tls_protos == 0) {
+                       cfg_obj_log(tlsobj, logctx, ISC_LOG_ERROR,
+                                   "tls '%s' does not contain any valid "
+                                   "TLS protocol versions definitions",
+                                   name);
+                       result = ISC_R_FAILURE;
+               }
+       }
+
+       return (result);
+}
+
+static isc_result_t
+bind9_check_tls_definitions(const cfg_obj_t *config, isc_log_t *logctx,
+                           isc_mem_t *mctx) {
+       isc_result_t result, tresult;
+       const cfg_obj_t *obj = NULL;
+       const cfg_listelt_t *elt = NULL;
+       isc_symtab_t *symtab = NULL;
+
+       result = cfg_map_get(config, "tls", &obj);
+       if (result != ISC_R_SUCCESS) {
+               result = ISC_R_SUCCESS;
+               return (result);
+       }
+
+       result = isc_symtab_create(mctx, 100, NULL, NULL, false, &symtab);
+       if (result != ISC_R_SUCCESS) {
+               return (result);
+       }
+
+       for (elt = cfg_list_first(obj); elt != NULL; elt = cfg_list_next(elt)) {
+               const char *name;
+               obj = cfg_listelt_value(elt);
+               name = cfg_obj_asstring(cfg_map_getname(obj));
+               tresult = bind9_check_tls_defintion(obj, name, logctx, symtab);
+               if (result == ISC_R_SUCCESS) {
+                       result = tresult;
+               }
+       }
+
+       isc_symtab_destroy(&symtab);
+
+       return (result);
+}
+
 static isc_result_t
 get_remotes(const cfg_obj_t *cctx, const char *list, const char *name,
            const cfg_obj_t **ret) {
@@ -5494,6 +5639,11 @@ bind9_check_namedconf(const cfg_obj_t *config, bool check_plugins,
        }
 #endif /* HAVE_LIBNGHTTP2 */
 
+       if (bind9_check_tls_definitions(config, logctx, mctx) != ISC_R_SUCCESS)
+       {
+               result = ISC_R_FAILURE;
+       }
+
        (void)cfg_map_get(config, "view", &views);
 
        if (views != NULL && options != NULL) {
index 38c990648ba4407bf1ce427945a8fca6391b6fc3..452079564466bccc6aff9dab9a2028e40879fcc6 100644 (file)
@@ -50,6 +50,40 @@ isc_tlsctx_createclient(isc_tlsctx_t **ctxp);
  *\li  'ctxp' != NULL and '*ctxp' == NULL.
  */
 
+typedef enum isc_tls_protocol_version {
+       /* these must be the powers of two */
+       ISC_TLS_PROTO_VER_1_2 = 1 << 0,
+       ISC_TLS_PROTO_VER_1_3 = 1 << 1,
+       ISC_TLS_PROTO_VER_UNDEFINED,
+} isc_tls_protocol_version_t;
+
+void
+isc_tlsctx_set_protocols(isc_tlsctx_t *ctx, const uint32_t tls_versions);
+/*%<
+ * Sets the supported TLS protocol versions via the 'tls_versions' bit
+ * set argument (see `isc_tls_protocol_version_t` enum for the
+ * expected values).
+ *
+ * Requires:
+ *\li  'ctx' != NULL;
+ *\li  'tls_versions' != 0.
+ */
+
+bool
+isc_tls_protocol_supported(const isc_tls_protocol_version_t tls_ver);
+/*%<
+ * Check in runtime that the specified TLS protocol versions is supported.
+ */
+
+isc_tls_protocol_version_t
+isc_tls_protocol_name_to_version(const char *name);
+/*%<
+ * Convert the protocol version string into the version of
+ * 'isc_tls_protocol_version_t' type.
+ * Requires:
+ *\li  'name' != NULL.
+ */
+
 isc_tls_t *
 isc_tls_create(isc_tlsctx_t *ctx);
 /*%<
index 1f9d7f9319518ae2340700ba361f6110a902fe2f..76fbe28690f1446871e14ee1f9ed862476e9ad47 100644 (file)
@@ -10,6 +10,7 @@
  */
 
 #include <inttypes.h>
+#include <string.h>
 #if HAVE_LIBNGHTTP2
 #include <nghttp2/nghttp2.h>
 #endif /* HAVE_LIBNGHTTP2 */
@@ -364,6 +365,101 @@ ssl_error:
        return (ISC_R_TLSERROR);
 }
 
+static long
+get_tls_version_disable_bit(const isc_tls_protocol_version_t tls_ver) {
+       long bit = 0;
+
+       switch (tls_ver) {
+       case ISC_TLS_PROTO_VER_1_2:
+#ifdef SSL_OP_NO_TLSv1_2
+               bit = SSL_OP_NO_TLSv1_2;
+#else
+               bit = 0;
+#endif
+               break;
+       case ISC_TLS_PROTO_VER_1_3:
+#ifdef SSL_OP_NO_TLSv1_3
+               bit = SSL_OP_NO_TLSv1_3;
+#else
+               bit = 0;
+#endif
+               break;
+       default:
+               INSIST(0);
+               ISC_UNREACHABLE();
+               break;
+       };
+
+       return (bit);
+}
+
+bool
+isc_tls_protocol_supported(const isc_tls_protocol_version_t tls_ver) {
+       return (get_tls_version_disable_bit(tls_ver) != 0);
+}
+
+isc_tls_protocol_version_t
+isc_tls_protocol_name_to_version(const char *name) {
+       REQUIRE(name != NULL);
+
+       if (strcasecmp(name, "TLSv1.2") == 0) {
+               return (ISC_TLS_PROTO_VER_1_2);
+       } else if (strcasecmp(name, "TLSv1.3") == 0) {
+               return (ISC_TLS_PROTO_VER_1_3);
+       }
+
+       return (ISC_TLS_PROTO_VER_UNDEFINED);
+}
+
+void
+isc_tlsctx_set_protocols(isc_tlsctx_t *ctx, const uint32_t tls_versions) {
+       REQUIRE(ctx != NULL);
+       REQUIRE(tls_versions != 0);
+       long set_options = 0;
+       long clear_options = 0;
+       uint32_t versions = tls_versions;
+
+       /*
+        * The code below might be initially hard to follow because of the
+        * double negation that OpenSSL enforces.
+        *
+        * Taking into account that OpenSSL provides bits to *disable*
+        * specific protocol versions, like SSL_OP_NO_TLSv1_2,
+        * SSL_OP_NO_TLSv1_3, etc., the code has the following logic:
+        *
+        * If a protocol version is not specified in the bitmask, get the
+        * bit that disables it and add it to the set of TLS options to
+        * set ('set_options'). Otherwise, if a protocol version is set,
+        * add the bit to the set of options to clear ('clear_options').
+        */
+
+       /* TLS protocol versions are defined as powers of two. */
+       for (uint32_t tls_ver = ISC_TLS_PROTO_VER_1_2;
+            tls_ver < ISC_TLS_PROTO_VER_UNDEFINED; tls_ver <<= 1)
+       {
+               /* Only supported versions should ever be passed to the
+                * function. The configuration file was not verified
+                * properly, if we are trying to enable an unsupported
+                * TLS version */
+               INSIST(isc_tls_protocol_supported(tls_ver));
+               if ((tls_versions & tls_ver) == 0) {
+                       set_options |= get_tls_version_disable_bit(tls_ver);
+               } else {
+                       clear_options |= get_tls_version_disable_bit(tls_ver);
+               }
+               versions &= ~(tls_ver);
+       }
+
+       /* All versions should be processed at this point, thus the value
+        * must equal zero. If it is not, then some garbage has been
+        * passed to the function; this situation is worth
+        * investigation. */
+       INSIST(versions == 0);
+
+       (void)SSL_CTX_set_options(ctx, set_options);
+       (void)SSL_CTX_clear_options(ctx, clear_options);
+}
+
 isc_tls_t *
 isc_tls_create(isc_tlsctx_t *ctx) {
        isc_tls_t *newctx = NULL;
index 8cd23814d0c7f2c81006df737e25925a26722e8a..1427d4fd5e127bd2741f1d8b590d901eb3f3a01f 100644 (file)
@@ -3875,10 +3875,12 @@ cfg_print_zonegrammar(const unsigned int zonetype, unsigned int flags,
 /*%
  * "tls" and related statement syntax.
  */
-static cfg_type_t cfg_type_sslprotos = {
-       "sslprotos",      cfg_parse_spacelist, cfg_print_spacelist,
-       cfg_doc_terminal, &cfg_rep_list,       &cfg_type_astring
-};
+static cfg_type_t cfg_type_tlsprotos = { "tls_protocols",
+                                        cfg_parse_bracketed_list,
+                                        cfg_print_bracketed_list,
+                                        cfg_doc_bracketed_list,
+                                        &cfg_rep_list,
+                                        &cfg_type_astring };
 
 static cfg_clausedef_t tls_clauses[] = {
        { "key-file", &cfg_type_qstring, 0 },
@@ -3886,7 +3888,7 @@ static cfg_clausedef_t tls_clauses[] = {
        { "ca-file", &cfg_type_qstring, 0 },
        { "hostname", &cfg_type_qstring, 0 },
        { "dh-param", &cfg_type_qstring, CFG_CLAUSEFLAG_EXPERIMENTAL },
-       { "protocols", &cfg_type_sslprotos, CFG_CLAUSEFLAG_EXPERIMENTAL },
+       { "protocols", &cfg_type_tlsprotos, 0 },
        { "ciphers", &cfg_type_astring, CFG_CLAUSEFLAG_EXPERIMENTAL },
        { NULL, NULL, 0 }
 };
index 348b1ca894a48c04a8d85ad19ca17b7309aef0ee..ae13f6732758ec3e9b4ddc40f0b5ae4447cadd0a 100644 (file)
@@ -59,22 +59,34 @@ struct ns_listenlist {
        ISC_LIST(ns_listenelt_t) elts;
 };
 
+typedef struct ns_listen_tls_params {
+       const char *key;
+       const char *cert;
+       uint32_t    protocols;
+} ns_listen_tls_params_t;
+
 /***
  *** Functions
  ***/
 
 isc_result_t
 ns_listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp,
-                   dns_acl_t *acl, bool tls, const char *key, const char *cert,
-                   ns_listenelt_t **target);
+                   dns_acl_t *acl, bool tls,
+                   const ns_listen_tls_params_t *tls_params,
+                   ns_listenelt_t **             target);
 /*%<
  * Create a listen-on list element.
+ *
+ * Requires:
+ * \li 'targetp' is a valid pointer to a pointer containing 'NULL';
+ * \li 'tls_params' is a valid, non-'NULL' pointer if 'tls' equals 'true'.
  */
 
 isc_result_t
 ns_listenelt_create_http(isc_mem_t *mctx, in_port_t http_port, isc_dscp_t dscp,
-                        dns_acl_t *acl, bool tls, const char *key,
-                        const char *cert, char **endpoints, size_t nendpoints,
+                        dns_acl_t *acl, bool tls,
+                        const ns_listen_tls_params_t *tls_params,
+                        char **endpoints, size_t nendpoints,
                         isc_quota_t *quota, const uint32_t max_streams,
                         ns_listenelt_t **target);
 /*%<
index 9c445a710afe755bd22422d005f0d49729262d27..92ffac7979f4d8045ac5d3c84486ad9f03e7a3e7 100644 (file)
@@ -26,19 +26,26 @@ destroy(ns_listenlist_t *list);
 
 isc_result_t
 ns_listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp,
-                   dns_acl_t *acl, bool tls, const char *key, const char *cert,
+                   dns_acl_t *acl, bool tls,
+                   const ns_listen_tls_params_t *tls_params,
                    ns_listenelt_t **target) {
        ns_listenelt_t *elt = NULL;
        isc_result_t result = ISC_R_SUCCESS;
        isc_tlsctx_t *sslctx = NULL;
 
        REQUIRE(target != NULL && *target == NULL);
+       REQUIRE(!tls || tls_params != NULL);
 
        if (tls) {
-               result = isc_tlsctx_createserver(key, cert, &sslctx);
+               result = isc_tlsctx_createserver(tls_params->key,
+                                                tls_params->cert, &sslctx);
                if (result != ISC_R_SUCCESS) {
                        return (result);
                }
+
+               if (tls_params->protocols != 0) {
+                       isc_tlsctx_set_protocols(sslctx, tls_params->protocols);
+               }
        }
 
        elt = isc_mem_get(mctx, sizeof(*elt));
@@ -59,8 +66,9 @@ ns_listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp,
 
 isc_result_t
 ns_listenelt_create_http(isc_mem_t *mctx, in_port_t http_port, isc_dscp_t dscp,
-                        dns_acl_t *acl, bool tls, const char *key,
-                        const char *cert, char **endpoints, size_t nendpoints,
+                        dns_acl_t *acl, bool tls,
+                        const ns_listen_tls_params_t *tls_params,
+                        char **endpoints, size_t nendpoints,
                         isc_quota_t *quota, const uint32_t max_streams,
                         ns_listenelt_t **target) {
        isc_result_t result;
@@ -69,8 +77,8 @@ ns_listenelt_create_http(isc_mem_t *mctx, in_port_t http_port, isc_dscp_t dscp,
        REQUIRE(endpoints != NULL && *endpoints != NULL);
        REQUIRE(nendpoints > 0);
 
-       result = ns_listenelt_create(mctx, http_port, dscp, acl, tls, key, cert,
-                                    target);
+       result = ns_listenelt_create(mctx, http_port, dscp, acl, tls,
+                                    tls_params, target);
        if (result == ISC_R_SUCCESS) {
                (*target)->is_http = true;
                (*target)->http_endpoints = endpoints;
@@ -164,8 +172,7 @@ ns_listenlist_default(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp,
                goto cleanup;
        }
 
-       result = ns_listenelt_create(mctx, port, dscp, acl, false, NULL, NULL,
-                                    &elt);
+       result = ns_listenelt_create(mctx, port, dscp, acl, false, NULL, &elt);
        if (result != ISC_R_SUCCESS) {
                goto cleanup_acl;
        }