From f2ae4c8480737936e45fecd926aade97700ee0be Mon Sep 17 00:00:00 2001 From: Artem Boldariev Date: Thu, 16 Sep 2021 14:48:30 +0300 Subject: [PATCH] DH-parameters loading support This commit adds support for loading DH-parameters (Diffie-Hellman parameters) via the new "dhparam-file" option within "tls" clause. In particular, Diffie-Hellman parameters are needed to enable the range of forward-secrecy enabled cyphers for TLSv1.2, which are getting silently disabled otherwise. --- bin/named/named.conf.rst | 2 +- bin/named/server.c | 13 +++- .../system/checkconf/good-doh-tlsopts.conf | 1 + .../system/checkconf/good-dot-tlsopts.conf | 1 + bin/tests/system/doth/ns2/dhparam3072.pem | 11 ++++ bin/tests/system/doth/ns2/named.conf.in | 1 + doc/arm/reference.rst | 9 ++- doc/man/named.conf.5in | 2 +- doc/misc/options | 2 +- doc/misc/options.active | 2 +- doc/misc/tls.grammar.rst | 2 +- lib/isc/include/isc/tls.h | 11 ++++ lib/isc/tls.c | 66 +++++++++++++++++++ lib/isccfg/namedconf.c | 2 +- lib/ns/include/ns/listenlist.h | 1 + lib/ns/listenlist.c | 9 +++ util/copyrights | 1 + 17 files changed, 126 insertions(+), 10 deletions(-) create mode 100644 bin/tests/system/doth/ns2/dhparam3072.pem diff --git a/bin/named/named.conf.rst b/bin/named/named.conf.rst index 051f9bf3fe..5d2c011f1c 100644 --- a/bin/named/named.conf.rst +++ b/bin/named/named.conf.rst @@ -564,7 +564,7 @@ TLS ca-file quoted_string; cert-file quoted_string; ciphers string; // experimental - dh-param quoted_string; // experimental + dhparam-file quoted_string; hostname quoted_string; key-file quoted_string; protocols { string; ... }; diff --git a/bin/named/server.c b/bin/named/server.c index d73210f38e..aacc18845a 100644 --- a/bin/named/server.c +++ b/bin/named/server.c @@ -11023,7 +11023,7 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, const cfg_obj_t *http_server = NULL; in_port_t port = 0; isc_dscp_t dscp = -1; - const char *key = NULL, *cert = NULL; + const char *key = NULL, *cert = NULL, *dhparam_file = NULL; bool do_tls = false, no_tls = false, http = false; ns_listenelt_t *delt = NULL; uint32_t tls_protos = 0; @@ -11043,7 +11043,8 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, } else if (strcasecmp(tlsname, "ephemeral") == 0) { do_tls = true; } else { - const cfg_obj_t *keyobj = NULL, *certobj = NULL; + const cfg_obj_t *keyobj = NULL, *certobj = NULL, + *dhparam_obj = NULL; const cfg_obj_t *tlsmap = NULL; const cfg_obj_t *tls_proto_list = NULL; @@ -11084,12 +11085,18 @@ listenelt_fromconfig(const cfg_obj_t *listener, const cfg_obj_t *config, tls_protos |= ver; } } + + if (cfg_map_get(tlsmap, "dhparam-file", &dhparam_obj) == + ISC_R_SUCCESS) { + dhparam_file = cfg_obj_asstring(dhparam_obj); + } } } tls_params = (ns_listen_tls_params_t){ .key = key, .cert = cert, - .protocols = tls_protos }; + .protocols = tls_protos, + .dhparam_file = dhparam_file }; httpobj = cfg_tuple_get(ltup, "http"); if (httpobj != NULL && cfg_obj_isstring(httpobj)) { diff --git a/bin/tests/system/checkconf/good-doh-tlsopts.conf b/bin/tests/system/checkconf/good-doh-tlsopts.conf index 90a2f95733..4ef244a01e 100644 --- a/bin/tests/system/checkconf/good-doh-tlsopts.conf +++ b/bin/tests/system/checkconf/good-doh-tlsopts.conf @@ -13,6 +13,7 @@ tls local-tls { protocols { TLSv1.2; }; key-file "key.pem"; cert-file "cert.pem"; + dhparam-file "dhparam.pem"; }; http local-http-server { diff --git a/bin/tests/system/checkconf/good-dot-tlsopts.conf b/bin/tests/system/checkconf/good-dot-tlsopts.conf index 3552eef0aa..62d4b8066d 100644 --- a/bin/tests/system/checkconf/good-dot-tlsopts.conf +++ b/bin/tests/system/checkconf/good-dot-tlsopts.conf @@ -13,6 +13,7 @@ tls local-tls { protocols { TLSv1.2; }; key-file "key.pem"; cert-file "cert.pem"; + dhparam-file "dhparam.pem"; }; options { diff --git a/bin/tests/system/doth/ns2/dhparam3072.pem b/bin/tests/system/doth/ns2/dhparam3072.pem new file mode 100644 index 0000000000..9c2e0aa42b --- /dev/null +++ b/bin/tests/system/doth/ns2/dhparam3072.pem @@ -0,0 +1,11 @@ +-----BEGIN DH PARAMETERS----- +MIIBiAKCAYEA5D/Oioe+G+EMf/9RVxmcV4rZAtqZpVTFHcX0ZulvdiQGCQmopm6K +3+0uoU2J6WVMjhna5nHD2NO9miRDI/jIxX9g9k6PedSB4o3fSTtkAnGtUbB8S+Ab +EHtWfd7FTES8P1n16HN7BfPXVbP8zTcK+jO63KdQoxueYoETcrw0Myi9Lm8ri8os +O4oQ+XAH7GzZ60bcYV9jge0XIRUGVnYZDjWMlnwMvZyjLivxKXTC9HPNA6FF1/0H +0LPhsfjdoLNsVHFzfQz7QELMfHbTd0C8y0UMDQw9FqUp0esHZ5gsTlqnDHp2ZHoR +JDfNl4yVO5Gv4HiFJ0NSdggefhESU3FRAOhMmUkctOCxk5hyPqGMsvofOajY2MBp +eCffrKuAU6/dGUeq8inwrZlAMIZ20WyskHmbHnc4DXo2Uo6xSZo3xyEq1ofXXwTZ +vPw4e12so3RJAT2a8UsHf7DG1tH+9ke7HCAJQWxUizRFRsMi1Nl/7ikS4f3zgIbX +GKz9+uk5eS6jAgEC +-----END DH PARAMETERS----- diff --git a/bin/tests/system/doth/ns2/named.conf.in b/bin/tests/system/doth/ns2/named.conf.in index 0a77cf9952..9d747c9f6a 100644 --- a/bin/tests/system/doth/ns2/named.conf.in +++ b/bin/tests/system/doth/ns2/named.conf.in @@ -18,6 +18,7 @@ controls { tls local { key-file "key.pem"; cert-file "cert.pem"; + dhparam-file "dhparam3072.pem"; }; http local { diff --git a/doc/arm/reference.rst b/doc/arm/reference.rst index b36db11467..7b61d2862a 100644 --- a/doc/arm/reference.rst +++ b/doc/arm/reference.rst @@ -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``, ``hostname``, and ``protocols``. + Specifies configuration information for a TLS connection, including a ``key-file``, ``cert-file``, ``ca-file``, ``dhparam-file``, ``hostname``, and ``protocols``. ``http`` Specifies configuration information for an HTTP connection, including ``endponts``, ``listener-clients`` and ``streams-per-connection``. @@ -4769,6 +4769,13 @@ The following options can be specified in a ``tls`` statement: ``ca-file`` Path to a file containing trusted TLS certificates. + ``dhparam-file`` + Path to a file containing Diffie-Hellman parameters, + which is needed to enable the cipher suites depending on the + Diffie-Hellman ephemeral key exchange (DHE). Having these parameters + specified is essential for enabling perfect forward secrecy capable + ciphers in TLSv1.2. + ``hostname`` The hostname associated with the certificate. diff --git a/doc/man/named.conf.5in b/doc/man/named.conf.5in index 0cce71b677..1c6d7c9075 100644 --- a/doc/man/named.conf.5in +++ b/doc/man/named.conf.5in @@ -655,7 +655,7 @@ tls string { ca\-file quoted_string; cert\-file quoted_string; ciphers string; // experimental - dh\-param quoted_string; // experimental + dhparam\-file quoted_string; hostname quoted_string; key\-file quoted_string; protocols { string; ... }; diff --git a/doc/misc/options b/doc/misc/options index aa89cd4ed1..72b32fb9f4 100644 --- a/doc/misc/options +++ b/doc/misc/options @@ -460,7 +460,7 @@ tls { ca-file ; cert-file ; ciphers ; // experimental - dh-param ; // experimental + dhparam-file ; hostname ; key-file ; protocols { ; ... }; diff --git a/doc/misc/options.active b/doc/misc/options.active index a745dc6327..b2fecd6f69 100644 --- a/doc/misc/options.active +++ b/doc/misc/options.active @@ -457,7 +457,7 @@ tls { ca-file ; cert-file ; ciphers ; // experimental - dh-param ; // experimental + dhparam-file ; hostname ; key-file ; protocols { ; ... }; diff --git a/doc/misc/tls.grammar.rst b/doc/misc/tls.grammar.rst index c0b5a48819..60035e5ae8 100644 --- a/doc/misc/tls.grammar.rst +++ b/doc/misc/tls.grammar.rst @@ -4,7 +4,7 @@ ca-file ; cert-file ; ciphers ; // experimental - dh-param ; // experimental + dhparam-file ; hostname ; key-file ; protocols { ; ... }; diff --git a/lib/isc/include/isc/tls.h b/lib/isc/include/isc/tls.h index 4520795644..594d40bf4d 100644 --- a/lib/isc/include/isc/tls.h +++ b/lib/isc/include/isc/tls.h @@ -84,6 +84,17 @@ isc_tls_protocol_name_to_version(const char *name); *\li 'name' != NULL. */ +bool +isc_tlsctx_load_dhparams(isc_tlsctx_t *ctx, const char *dhparams_file); +/*%< + * Load Diffie-Hellman parameters file and apply it to the given TLS context + * 'ctx'. + * + * Requires: + * \li 'ctx' != NULL; + * \li 'dhaprams_file' a valid pointer to a non empty string. + */ + isc_tls_t * isc_tls_create(isc_tlsctx_t *ctx); /*%< diff --git a/lib/isc/tls.c b/lib/isc/tls.c index 76fbe28690..1fc1362a13 100644 --- a/lib/isc/tls.c +++ b/lib/isc/tls.c @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -460,6 +461,71 @@ isc_tlsctx_set_protocols(isc_tlsctx_t *ctx, const uint32_t tls_versions) { (void)SSL_CTX_clear_options(ctx, clear_options); } +bool +isc_tlsctx_load_dhparams(isc_tlsctx_t *ctx, const char *dhparams_file) { + REQUIRE(ctx != NULL); + REQUIRE(dhparams_file != NULL); + REQUIRE(*dhparams_file != '\0'); + +#ifdef SSL_CTX_set_tmp_dh + /* OpenSSL < 3.0 */ + DH *dh = NULL; + FILE *paramfile; + + paramfile = fopen(dhparams_file, "r"); + + if (paramfile) { + int check = 0; + dh = PEM_read_DHparams(paramfile, NULL, NULL, NULL); + fclose(paramfile); + + if (dh == NULL) { + return (false); + } else if (DH_check(dh, &check) != 1 || check != 0) { + DH_free(dh); + return (false); + } + } else { + return (false); + } + + if (SSL_CTX_set_tmp_dh(ctx, dh) != 1) { + DH_free(dh); + return (false); + } + + DH_free(dh); +#else + /* OpenSSL >= 3.0: SSL_CTX_set_tmp_dh() is deprecated in OpenSSL 3.0 */ + EVP_PKEY *dh = NULL; + BIO *bio = NULL; + + bio = BIO_new_file(dhparams_file, "r"); + if (bio == NULL) { + return (false); + } + + dh = PEM_read_bio_Parameters(bio, NULL); + if (dh == NULL) { + BIO_free(bio); + return (false); + } + + if (SSL_CTX_set0_tmp_dh_pkey(ctx, dh) != 1) { + BIO_free(bio); + EVP_PKEY_free(dh); + return (false); + } + + /* No need to call EVP_PKEY_free(dh) as the "dh" is owned by the + * SSL context at this point. */ + + BIO_free(bio); +#endif + + return (true); +} + isc_tls_t * isc_tls_create(isc_tlsctx_t *ctx) { isc_tls_t *newctx = NULL; diff --git a/lib/isccfg/namedconf.c b/lib/isccfg/namedconf.c index 1427d4fd5e..63c66da7b0 100644 --- a/lib/isccfg/namedconf.c +++ b/lib/isccfg/namedconf.c @@ -3887,7 +3887,7 @@ static cfg_clausedef_t tls_clauses[] = { { "cert-file", &cfg_type_qstring, 0 }, { "ca-file", &cfg_type_qstring, 0 }, { "hostname", &cfg_type_qstring, 0 }, - { "dh-param", &cfg_type_qstring, CFG_CLAUSEFLAG_EXPERIMENTAL }, + { "dhparam-file", &cfg_type_qstring, 0 }, { "protocols", &cfg_type_tlsprotos, 0 }, { "ciphers", &cfg_type_astring, CFG_CLAUSEFLAG_EXPERIMENTAL }, { NULL, NULL, 0 } diff --git a/lib/ns/include/ns/listenlist.h b/lib/ns/include/ns/listenlist.h index ae13f67327..2fd341318e 100644 --- a/lib/ns/include/ns/listenlist.h +++ b/lib/ns/include/ns/listenlist.h @@ -63,6 +63,7 @@ typedef struct ns_listen_tls_params { const char *key; const char *cert; uint32_t protocols; + const char *dhparam_file; } ns_listen_tls_params_t; /*** diff --git a/lib/ns/listenlist.c b/lib/ns/listenlist.c index 92ffac7979..001821246e 100644 --- a/lib/ns/listenlist.c +++ b/lib/ns/listenlist.c @@ -46,6 +46,15 @@ ns_listenelt_create(isc_mem_t *mctx, in_port_t port, isc_dscp_t dscp, if (tls_params->protocols != 0) { isc_tlsctx_set_protocols(sslctx, tls_params->protocols); } + + if (tls_params->dhparam_file != NULL) { + if (!isc_tlsctx_load_dhparams(sslctx, + tls_params->dhparam_file)) + { + isc_tlsctx_free(&sslctx); + return (ISC_R_FAILURE); + } + } } elt = isc_mem_get(mctx, sizeof(*elt)); diff --git a/util/copyrights b/util/copyrights index 2d0d2faf53..0e24a2361d 100644 --- a/util/copyrights +++ b/util/copyrights @@ -276,6 +276,7 @@ ./bin/tests/system/doth/clean.sh SH 2020,2021 ./bin/tests/system/doth/example.axfr.good X 2021 ./bin/tests/system/doth/ns2/cert.pem X 2021 +./bin/tests/system/doth/ns2/dhparam3072.pem X 2021 ./bin/tests/system/doth/ns2/key.pem X 2021 ./bin/tests/system/doth/setup.sh SH 2021 ./bin/tests/system/doth/stress_http_quota.py PYTHON-BIN 2021