2020-12-17 11:40:29 +01:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
refactor outgoing HTTP connection support
- style, cleanup, and removal of unnecessary code.
- combined isc_nm_http_add_endpoint() and isc_nm_http_add_doh_endpoint()
into one function, renamed isc_http_endpoint().
- moved isc_nm_http_connect_send_request() into doh_test.c as a helper
function; remove it from the public API.
- renamed isc_http2 and isc_nm_http2 types and functions to just isc_http
and isc_nm_http, for consistency with other existing names.
- shortened a number of long names.
- the caller is now responsible for determining the peer address.
in isc_nm_httpconnect(); this eliminates the need to parse the URI
and the dependency on an external resolver.
- the caller is also now responsible for creating the SSL client context,
for consistency with isc_nm_tlsdnsconnect().
- added setter functions for HTTP/2 ALPN. instead of setting up ALPN in
isc_tlsctx_createclient(), we now have a function
isc_tlsctx_enable_http2client_alpn() that can be run from
isc_nm_httpconnect().
- refactored isc_nm_httprequest() into separate read and send functions.
isc_nm_send() or isc_nm_read() is called on an http socket, it will
be stored until a corresponding isc_nm_read() or _send() arrives; when
we have both halves of the pair the HTTP request will be initiated.
- isc_nm_httprequest() is renamed isc__nm_http_request() for use as an
internal helper function by the DoH unit test. (eventually doh_test
should be rewritten to use read and send, and this function should
be removed.)
- added implementations of isc__nm_tls_settimeout() and
isc__nm_http_settimeout().
- increased NGHTTP2 header block length for client connections to 128K.
- use isc_mem_t for internal memory allocations inside nghttp2, to
help track memory leaks.
- send "Cache-Control" header in requests and responses. (note:
currently we try to bypass HTTP caching proxies, but ideally we should
interact with them: https://tools.ietf.org/html/rfc8484#section-5.1)
2021-02-03 16:59:49 -08:00
|
|
|
#include <inttypes.h>
|
2021-09-13 15:39:36 +03:00
|
|
|
#include <string.h>
|
2021-04-21 13:52:15 +02:00
|
|
|
#if HAVE_LIBNGHTTP2
|
refactor outgoing HTTP connection support
- style, cleanup, and removal of unnecessary code.
- combined isc_nm_http_add_endpoint() and isc_nm_http_add_doh_endpoint()
into one function, renamed isc_http_endpoint().
- moved isc_nm_http_connect_send_request() into doh_test.c as a helper
function; remove it from the public API.
- renamed isc_http2 and isc_nm_http2 types and functions to just isc_http
and isc_nm_http, for consistency with other existing names.
- shortened a number of long names.
- the caller is now responsible for determining the peer address.
in isc_nm_httpconnect(); this eliminates the need to parse the URI
and the dependency on an external resolver.
- the caller is also now responsible for creating the SSL client context,
for consistency with isc_nm_tlsdnsconnect().
- added setter functions for HTTP/2 ALPN. instead of setting up ALPN in
isc_tlsctx_createclient(), we now have a function
isc_tlsctx_enable_http2client_alpn() that can be run from
isc_nm_httpconnect().
- refactored isc_nm_httprequest() into separate read and send functions.
isc_nm_send() or isc_nm_read() is called on an http socket, it will
be stored until a corresponding isc_nm_read() or _send() arrives; when
we have both halves of the pair the HTTP request will be initiated.
- isc_nm_httprequest() is renamed isc__nm_http_request() for use as an
internal helper function by the DoH unit test. (eventually doh_test
should be rewritten to use read and send, and this function should
be removed.)
- added implementations of isc__nm_tls_settimeout() and
isc__nm_http_settimeout().
- increased NGHTTP2 header block length for client connections to 128K.
- use isc_mem_t for internal memory allocations inside nghttp2, to
help track memory leaks.
- send "Cache-Control" header in requests and responses. (note:
currently we try to bypass HTTP caching proxies, but ideally we should
interact with them: https://tools.ietf.org/html/rfc8484#section-5.1)
2021-02-03 16:59:49 -08:00
|
|
|
#include <nghttp2/nghttp2.h>
|
2021-04-21 13:52:15 +02:00
|
|
|
#endif /* HAVE_LIBNGHTTP2 */
|
refactor outgoing HTTP connection support
- style, cleanup, and removal of unnecessary code.
- combined isc_nm_http_add_endpoint() and isc_nm_http_add_doh_endpoint()
into one function, renamed isc_http_endpoint().
- moved isc_nm_http_connect_send_request() into doh_test.c as a helper
function; remove it from the public API.
- renamed isc_http2 and isc_nm_http2 types and functions to just isc_http
and isc_nm_http, for consistency with other existing names.
- shortened a number of long names.
- the caller is now responsible for determining the peer address.
in isc_nm_httpconnect(); this eliminates the need to parse the URI
and the dependency on an external resolver.
- the caller is also now responsible for creating the SSL client context,
for consistency with isc_nm_tlsdnsconnect().
- added setter functions for HTTP/2 ALPN. instead of setting up ALPN in
isc_tlsctx_createclient(), we now have a function
isc_tlsctx_enable_http2client_alpn() that can be run from
isc_nm_httpconnect().
- refactored isc_nm_httprequest() into separate read and send functions.
isc_nm_send() or isc_nm_read() is called on an http socket, it will
be stored until a corresponding isc_nm_read() or _send() arrives; when
we have both halves of the pair the HTTP request will be initiated.
- isc_nm_httprequest() is renamed isc__nm_http_request() for use as an
internal helper function by the DoH unit test. (eventually doh_test
should be rewritten to use read and send, and this function should
be removed.)
- added implementations of isc__nm_tls_settimeout() and
isc__nm_http_settimeout().
- increased NGHTTP2 header block length for client connections to 128K.
- use isc_mem_t for internal memory allocations inside nghttp2, to
help track memory leaks.
- send "Cache-Control" header in requests and responses. (note:
currently we try to bypass HTTP caching proxies, but ideally we should
interact with them: https://tools.ietf.org/html/rfc8484#section-5.1)
2021-02-03 16:59:49 -08:00
|
|
|
|
2021-03-16 21:58:55 +00:00
|
|
|
#include <openssl/bn.h>
|
2021-02-09 13:25:46 +01:00
|
|
|
#include <openssl/conf.h>
|
2021-09-16 14:48:30 +03:00
|
|
|
#include <openssl/dh.h>
|
2020-12-17 11:40:29 +01:00
|
|
|
#include <openssl/err.h>
|
|
|
|
#include <openssl/opensslv.h>
|
2021-02-09 13:25:46 +01:00
|
|
|
#include <openssl/rand.h>
|
2021-03-16 21:58:55 +00:00
|
|
|
#include <openssl/rsa.h>
|
2020-12-17 11:40:29 +01:00
|
|
|
|
|
|
|
#include <isc/atomic.h>
|
|
|
|
#include <isc/log.h>
|
|
|
|
#include <isc/mutex.h>
|
|
|
|
#include <isc/mutexblock.h>
|
|
|
|
#include <isc/once.h>
|
|
|
|
#include <isc/thread.h>
|
|
|
|
#include <isc/tls.h>
|
|
|
|
#include <isc/util.h>
|
|
|
|
|
|
|
|
#include "openssl_shim.h"
|
2021-02-09 17:44:40 +01:00
|
|
|
#include "tls_p.h"
|
2020-12-17 11:40:29 +01:00
|
|
|
|
2021-09-13 14:00:35 +03:00
|
|
|
#define COMMON_SSL_OPTIONS \
|
|
|
|
(SSL_OP_NO_COMPRESSION | SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION)
|
|
|
|
|
2020-12-17 11:40:29 +01:00
|
|
|
static isc_once_t init_once = ISC_ONCE_INIT;
|
2021-02-09 13:25:46 +01:00
|
|
|
static isc_once_t shut_once = ISC_ONCE_INIT;
|
2020-12-17 11:40:29 +01:00
|
|
|
static atomic_bool init_done = ATOMIC_VAR_INIT(false);
|
2021-02-09 13:25:46 +01:00
|
|
|
static atomic_bool shut_done = ATOMIC_VAR_INIT(false);
|
2020-12-17 11:40:29 +01:00
|
|
|
|
|
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
|
|
static isc_mutex_t *locks = NULL;
|
|
|
|
static int nlocks;
|
|
|
|
|
|
|
|
static void
|
|
|
|
isc__tls_lock_callback(int mode, int type, const char *file, int line) {
|
|
|
|
UNUSED(file);
|
|
|
|
UNUSED(line);
|
|
|
|
if ((mode & CRYPTO_LOCK) != 0) {
|
|
|
|
LOCK(&locks[type]);
|
|
|
|
} else {
|
|
|
|
UNLOCK(&locks[type]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
isc__tls_set_thread_id(CRYPTO_THREADID *id) {
|
|
|
|
CRYPTO_THREADID_set_numeric(id, (unsigned long)isc_thread_self());
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void
|
2021-02-09 17:44:40 +01:00
|
|
|
tls_initialize(void) {
|
2020-12-17 11:40:29 +01:00
|
|
|
REQUIRE(!atomic_load(&init_done));
|
|
|
|
|
2021-02-09 13:25:46 +01:00
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10100000L
|
|
|
|
RUNTIME_CHECK(OPENSSL_init_ssl(OPENSSL_INIT_ENGINE_ALL_BUILTIN |
|
|
|
|
OPENSSL_INIT_LOAD_CONFIG,
|
|
|
|
NULL) == 1);
|
|
|
|
#else
|
2020-12-17 11:40:29 +01:00
|
|
|
nlocks = CRYPTO_num_locks();
|
2021-02-09 17:44:40 +01:00
|
|
|
/*
|
|
|
|
* We can't use isc_mem API here, because it's called too
|
|
|
|
* early and when the isc_mem_debugging flags are changed
|
2021-05-11 12:59:35 +02:00
|
|
|
* later.
|
2021-02-09 17:44:40 +01:00
|
|
|
*
|
|
|
|
* Actually, since this is a single allocation at library load
|
|
|
|
* and deallocation at library unload, using the standard
|
|
|
|
* allocator without the tracking is fine for this purpose.
|
|
|
|
*/
|
|
|
|
locks = calloc(nlocks, sizeof(locks[0]));
|
2020-12-17 11:40:29 +01:00
|
|
|
isc_mutexblock_init(locks, nlocks);
|
|
|
|
CRYPTO_set_locking_callback(isc__tls_lock_callback);
|
|
|
|
CRYPTO_THREADID_set_callback(isc__tls_set_thread_id);
|
2021-02-09 13:25:46 +01:00
|
|
|
|
|
|
|
CRYPTO_malloc_init();
|
2020-12-17 11:40:29 +01:00
|
|
|
ERR_load_crypto_strings();
|
2021-02-09 13:25:46 +01:00
|
|
|
SSL_load_error_strings();
|
|
|
|
SSL_library_init();
|
|
|
|
|
|
|
|
#if !defined(OPENSSL_NO_ENGINE)
|
|
|
|
ENGINE_load_builtin_engines();
|
|
|
|
#endif
|
|
|
|
OpenSSL_add_all_algorithms();
|
|
|
|
OPENSSL_load_builtin_modules();
|
|
|
|
|
|
|
|
CONF_modules_load_file(NULL, NULL,
|
|
|
|
CONF_MFLAGS_DEFAULT_SECTION |
|
|
|
|
CONF_MFLAGS_IGNORE_MISSING_FILE);
|
2020-12-17 11:40:29 +01:00
|
|
|
#endif
|
2021-02-09 13:25:46 +01:00
|
|
|
|
|
|
|
/* Protect ourselves against unseeded PRNG */
|
|
|
|
if (RAND_status() != 1) {
|
|
|
|
FATAL_ERROR(__FILE__, __LINE__,
|
|
|
|
"OpenSSL pseudorandom number generator "
|
|
|
|
"cannot be initialized (see the `PRNG not "
|
|
|
|
"seeded' message in the OpenSSL FAQ)");
|
|
|
|
}
|
|
|
|
|
2021-02-09 17:44:40 +01:00
|
|
|
REQUIRE(atomic_compare_exchange_strong(&init_done, &(bool){ false },
|
|
|
|
true));
|
2020-12-17 11:40:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2021-02-09 17:44:40 +01:00
|
|
|
isc__tls_initialize(void) {
|
|
|
|
isc_result_t result = isc_once_do(&init_once, tls_initialize);
|
2020-12-17 11:40:29 +01:00
|
|
|
REQUIRE(result == ISC_R_SUCCESS);
|
|
|
|
REQUIRE(atomic_load(&init_done));
|
|
|
|
}
|
|
|
|
|
2021-02-09 13:25:46 +01:00
|
|
|
static void
|
2021-02-09 17:44:40 +01:00
|
|
|
tls_shutdown(void) {
|
2020-12-17 11:40:29 +01:00
|
|
|
REQUIRE(atomic_load(&init_done));
|
2021-02-09 13:25:46 +01:00
|
|
|
REQUIRE(!atomic_load(&shut_done));
|
2021-02-09 17:44:40 +01:00
|
|
|
|
2021-02-09 13:25:46 +01:00
|
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
2020-12-17 11:40:29 +01:00
|
|
|
|
2021-02-09 13:25:46 +01:00
|
|
|
CONF_modules_unload(1);
|
|
|
|
OBJ_cleanup();
|
|
|
|
EVP_cleanup();
|
|
|
|
#if !defined(OPENSSL_NO_ENGINE)
|
|
|
|
ENGINE_cleanup();
|
|
|
|
#endif
|
|
|
|
CRYPTO_cleanup_all_ex_data();
|
2020-12-17 11:40:29 +01:00
|
|
|
ERR_remove_thread_state(NULL);
|
2021-02-09 13:25:46 +01:00
|
|
|
RAND_cleanup();
|
|
|
|
ERR_free_strings();
|
|
|
|
|
2020-12-17 11:40:29 +01:00
|
|
|
CRYPTO_set_locking_callback(NULL);
|
|
|
|
|
|
|
|
if (locks != NULL) {
|
|
|
|
isc_mutexblock_destroy(locks, nlocks);
|
2021-02-09 17:44:40 +01:00
|
|
|
free(locks);
|
2020-12-17 11:40:29 +01:00
|
|
|
locks = NULL;
|
|
|
|
}
|
|
|
|
#endif
|
2021-02-09 13:25:46 +01:00
|
|
|
|
2021-02-09 17:44:40 +01:00
|
|
|
REQUIRE(atomic_compare_exchange_strong(&shut_done, &(bool){ false },
|
|
|
|
true));
|
2021-02-09 13:25:46 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2021-02-09 17:44:40 +01:00
|
|
|
isc__tls_shutdown(void) {
|
|
|
|
isc_result_t result = isc_once_do(&shut_once, tls_shutdown);
|
2021-02-09 13:25:46 +01:00
|
|
|
REQUIRE(result == ISC_R_SUCCESS);
|
|
|
|
REQUIRE(atomic_load(&shut_done));
|
2020-12-17 11:40:29 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
isc_tlsctx_free(isc_tlsctx_t **ctxp) {
|
|
|
|
SSL_CTX *ctx = NULL;
|
|
|
|
REQUIRE(ctxp != NULL && *ctxp != NULL);
|
|
|
|
|
|
|
|
ctx = *ctxp;
|
|
|
|
*ctxp = NULL;
|
|
|
|
|
|
|
|
SSL_CTX_free(ctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
isc_result_t
|
|
|
|
isc_tlsctx_createclient(isc_tlsctx_t **ctxp) {
|
|
|
|
unsigned long err;
|
|
|
|
char errbuf[256];
|
|
|
|
SSL_CTX *ctx = NULL;
|
|
|
|
const SSL_METHOD *method = NULL;
|
|
|
|
|
|
|
|
REQUIRE(ctxp != NULL && *ctxp == NULL);
|
|
|
|
|
|
|
|
method = TLS_client_method();
|
|
|
|
if (method == NULL) {
|
|
|
|
goto ssl_error;
|
|
|
|
}
|
|
|
|
ctx = SSL_CTX_new(method);
|
|
|
|
if (ctx == NULL) {
|
|
|
|
goto ssl_error;
|
|
|
|
}
|
|
|
|
|
2021-09-13 14:00:35 +03:00
|
|
|
SSL_CTX_set_options(ctx, COMMON_SSL_OPTIONS);
|
|
|
|
|
2020-12-17 11:40:29 +01:00
|
|
|
#if HAVE_SSL_CTX_SET_MIN_PROTO_VERSION
|
|
|
|
SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
|
|
|
|
#else
|
2021-09-13 14:00:35 +03:00
|
|
|
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
|
|
|
|
SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1);
|
2020-12-17 11:40:29 +01:00
|
|
|
#endif
|
|
|
|
|
|
|
|
*ctxp = ctx;
|
|
|
|
|
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
|
|
|
|
ssl_error:
|
|
|
|
err = ERR_get_error();
|
|
|
|
ERR_error_string_n(err, errbuf, sizeof(errbuf));
|
|
|
|
isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_NETMGR,
|
|
|
|
ISC_LOG_ERROR, "Error initializing TLS context: %s",
|
|
|
|
errbuf);
|
|
|
|
|
|
|
|
return (ISC_R_TLSERROR);
|
|
|
|
}
|
|
|
|
|
|
|
|
isc_result_t
|
|
|
|
isc_tlsctx_createserver(const char *keyfile, const char *certfile,
|
|
|
|
isc_tlsctx_t **ctxp) {
|
|
|
|
int rv;
|
|
|
|
unsigned long err;
|
|
|
|
bool ephemeral = (keyfile == NULL && certfile == NULL);
|
|
|
|
X509 *cert = NULL;
|
|
|
|
EVP_PKEY *pkey = NULL;
|
|
|
|
BIGNUM *bn = NULL;
|
|
|
|
SSL_CTX *ctx = NULL;
|
|
|
|
RSA *rsa = NULL;
|
|
|
|
char errbuf[256];
|
|
|
|
const SSL_METHOD *method = NULL;
|
|
|
|
|
|
|
|
REQUIRE(ctxp != NULL && *ctxp == NULL);
|
refactor outgoing HTTP connection support
- style, cleanup, and removal of unnecessary code.
- combined isc_nm_http_add_endpoint() and isc_nm_http_add_doh_endpoint()
into one function, renamed isc_http_endpoint().
- moved isc_nm_http_connect_send_request() into doh_test.c as a helper
function; remove it from the public API.
- renamed isc_http2 and isc_nm_http2 types and functions to just isc_http
and isc_nm_http, for consistency with other existing names.
- shortened a number of long names.
- the caller is now responsible for determining the peer address.
in isc_nm_httpconnect(); this eliminates the need to parse the URI
and the dependency on an external resolver.
- the caller is also now responsible for creating the SSL client context,
for consistency with isc_nm_tlsdnsconnect().
- added setter functions for HTTP/2 ALPN. instead of setting up ALPN in
isc_tlsctx_createclient(), we now have a function
isc_tlsctx_enable_http2client_alpn() that can be run from
isc_nm_httpconnect().
- refactored isc_nm_httprequest() into separate read and send functions.
isc_nm_send() or isc_nm_read() is called on an http socket, it will
be stored until a corresponding isc_nm_read() or _send() arrives; when
we have both halves of the pair the HTTP request will be initiated.
- isc_nm_httprequest() is renamed isc__nm_http_request() for use as an
internal helper function by the DoH unit test. (eventually doh_test
should be rewritten to use read and send, and this function should
be removed.)
- added implementations of isc__nm_tls_settimeout() and
isc__nm_http_settimeout().
- increased NGHTTP2 header block length for client connections to 128K.
- use isc_mem_t for internal memory allocations inside nghttp2, to
help track memory leaks.
- send "Cache-Control" header in requests and responses. (note:
currently we try to bypass HTTP caching proxies, but ideally we should
interact with them: https://tools.ietf.org/html/rfc8484#section-5.1)
2021-02-03 16:59:49 -08:00
|
|
|
REQUIRE((keyfile == NULL) == (certfile == NULL));
|
2020-12-17 11:40:29 +01:00
|
|
|
|
|
|
|
method = TLS_server_method();
|
|
|
|
if (method == NULL) {
|
|
|
|
goto ssl_error;
|
|
|
|
}
|
|
|
|
ctx = SSL_CTX_new(method);
|
|
|
|
if (ctx == NULL) {
|
|
|
|
goto ssl_error;
|
|
|
|
}
|
|
|
|
RUNTIME_CHECK(ctx != NULL);
|
|
|
|
|
2021-09-13 14:00:35 +03:00
|
|
|
SSL_CTX_set_options(ctx, COMMON_SSL_OPTIONS);
|
|
|
|
|
2020-12-17 11:40:29 +01:00
|
|
|
#if HAVE_SSL_CTX_SET_MIN_PROTO_VERSION
|
|
|
|
SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION);
|
|
|
|
#else
|
|
|
|
SSL_CTX_set_options(ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
|
|
|
|
SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (ephemeral) {
|
|
|
|
rsa = RSA_new();
|
|
|
|
if (rsa == NULL) {
|
|
|
|
goto ssl_error;
|
|
|
|
}
|
|
|
|
bn = BN_new();
|
|
|
|
if (bn == NULL) {
|
|
|
|
goto ssl_error;
|
|
|
|
}
|
|
|
|
BN_set_word(bn, RSA_F4);
|
|
|
|
rv = RSA_generate_key_ex(rsa, 4096, bn, NULL);
|
|
|
|
if (rv != 1) {
|
|
|
|
goto ssl_error;
|
|
|
|
}
|
|
|
|
cert = X509_new();
|
|
|
|
if (cert == NULL) {
|
|
|
|
goto ssl_error;
|
|
|
|
}
|
|
|
|
pkey = EVP_PKEY_new();
|
|
|
|
if (pkey == NULL) {
|
|
|
|
goto ssl_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* EVP_PKEY_assign_*() set the referenced key to key
|
|
|
|
* however these use the supplied key internally and so
|
|
|
|
* key will be freed when the parent pkey is freed.
|
|
|
|
*/
|
|
|
|
EVP_PKEY_assign(pkey, EVP_PKEY_RSA, rsa);
|
|
|
|
rsa = NULL;
|
|
|
|
ASN1_INTEGER_set(X509_get_serialNumber(cert), 1);
|
|
|
|
|
2021-03-16 21:58:55 +00:00
|
|
|
#if OPENSSL_VERSION_NUMBER < 0x10101000L
|
2020-12-17 11:40:29 +01:00
|
|
|
X509_gmtime_adj(X509_get_notBefore(cert), 0);
|
2021-03-16 21:58:55 +00:00
|
|
|
#else
|
|
|
|
X509_gmtime_adj(X509_getm_notBefore(cert), 0);
|
|
|
|
#endif
|
2020-12-17 11:40:29 +01:00
|
|
|
/*
|
|
|
|
* We set the vailidy for 10 years.
|
|
|
|
*/
|
2021-03-16 21:58:55 +00:00
|
|
|
#if OPENSSL_VERSION_NUMBER < 0x10101000L
|
2020-12-17 11:40:29 +01:00
|
|
|
X509_gmtime_adj(X509_get_notAfter(cert), 3650 * 24 * 3600);
|
2021-03-16 21:58:55 +00:00
|
|
|
#else
|
|
|
|
X509_gmtime_adj(X509_getm_notAfter(cert), 3650 * 24 * 3600);
|
|
|
|
#endif
|
2020-12-17 11:40:29 +01:00
|
|
|
|
|
|
|
X509_set_pubkey(cert, pkey);
|
|
|
|
|
|
|
|
X509_NAME *name = X509_get_subject_name(cert);
|
|
|
|
|
|
|
|
X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC,
|
|
|
|
(const unsigned char *)"AQ", -1, -1,
|
|
|
|
0);
|
|
|
|
X509_NAME_add_entry_by_txt(
|
|
|
|
name, "O", MBSTRING_ASC,
|
|
|
|
(const unsigned char *)"BIND9 ephemeral "
|
|
|
|
"certificate",
|
|
|
|
-1, -1, 0);
|
|
|
|
X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
|
|
|
|
(const unsigned char *)"bind9.local",
|
|
|
|
-1, -1, 0);
|
|
|
|
|
|
|
|
X509_set_issuer_name(cert, name);
|
|
|
|
X509_sign(cert, pkey, EVP_sha256());
|
|
|
|
rv = SSL_CTX_use_certificate(ctx, cert);
|
|
|
|
if (rv != 1) {
|
|
|
|
goto ssl_error;
|
|
|
|
}
|
|
|
|
rv = SSL_CTX_use_PrivateKey(ctx, pkey);
|
|
|
|
if (rv != 1) {
|
|
|
|
goto ssl_error;
|
|
|
|
}
|
|
|
|
|
|
|
|
X509_free(cert);
|
|
|
|
EVP_PKEY_free(pkey);
|
|
|
|
BN_free(bn);
|
|
|
|
} else {
|
2021-03-09 14:45:03 +02:00
|
|
|
rv = SSL_CTX_use_certificate_chain_file(ctx, certfile);
|
2020-12-17 11:40:29 +01:00
|
|
|
if (rv != 1) {
|
|
|
|
goto ssl_error;
|
|
|
|
}
|
|
|
|
rv = SSL_CTX_use_PrivateKey_file(ctx, keyfile,
|
|
|
|
SSL_FILETYPE_PEM);
|
|
|
|
if (rv != 1) {
|
|
|
|
goto ssl_error;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
*ctxp = ctx;
|
|
|
|
return (ISC_R_SUCCESS);
|
|
|
|
|
|
|
|
ssl_error:
|
|
|
|
err = ERR_get_error();
|
|
|
|
ERR_error_string_n(err, errbuf, sizeof(errbuf));
|
|
|
|
isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_NETMGR,
|
|
|
|
ISC_LOG_ERROR, "Error initializing TLS context: %s",
|
|
|
|
errbuf);
|
2021-02-09 17:44:40 +01:00
|
|
|
|
2020-12-17 11:40:29 +01:00
|
|
|
if (ctx != NULL) {
|
|
|
|
SSL_CTX_free(ctx);
|
|
|
|
}
|
|
|
|
if (cert != NULL) {
|
|
|
|
X509_free(cert);
|
|
|
|
}
|
|
|
|
if (pkey != NULL) {
|
|
|
|
EVP_PKEY_free(pkey);
|
|
|
|
}
|
|
|
|
if (bn != NULL) {
|
|
|
|
BN_free(bn);
|
|
|
|
}
|
|
|
|
if (rsa != NULL) {
|
|
|
|
RSA_free(rsa);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (ISC_R_TLSERROR);
|
|
|
|
}
|
refactor outgoing HTTP connection support
- style, cleanup, and removal of unnecessary code.
- combined isc_nm_http_add_endpoint() and isc_nm_http_add_doh_endpoint()
into one function, renamed isc_http_endpoint().
- moved isc_nm_http_connect_send_request() into doh_test.c as a helper
function; remove it from the public API.
- renamed isc_http2 and isc_nm_http2 types and functions to just isc_http
and isc_nm_http, for consistency with other existing names.
- shortened a number of long names.
- the caller is now responsible for determining the peer address.
in isc_nm_httpconnect(); this eliminates the need to parse the URI
and the dependency on an external resolver.
- the caller is also now responsible for creating the SSL client context,
for consistency with isc_nm_tlsdnsconnect().
- added setter functions for HTTP/2 ALPN. instead of setting up ALPN in
isc_tlsctx_createclient(), we now have a function
isc_tlsctx_enable_http2client_alpn() that can be run from
isc_nm_httpconnect().
- refactored isc_nm_httprequest() into separate read and send functions.
isc_nm_send() or isc_nm_read() is called on an http socket, it will
be stored until a corresponding isc_nm_read() or _send() arrives; when
we have both halves of the pair the HTTP request will be initiated.
- isc_nm_httprequest() is renamed isc__nm_http_request() for use as an
internal helper function by the DoH unit test. (eventually doh_test
should be rewritten to use read and send, and this function should
be removed.)
- added implementations of isc__nm_tls_settimeout() and
isc__nm_http_settimeout().
- increased NGHTTP2 header block length for client connections to 128K.
- use isc_mem_t for internal memory allocations inside nghttp2, to
help track memory leaks.
- send "Cache-Control" header in requests and responses. (note:
currently we try to bypass HTTP caching proxies, but ideally we should
interact with them: https://tools.ietf.org/html/rfc8484#section-5.1)
2021-02-03 16:59:49 -08:00
|
|
|
|
2021-09-13 15:39:36 +03:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2021-09-16 14:48:30 +03:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
refactor outgoing HTTP connection support
- style, cleanup, and removal of unnecessary code.
- combined isc_nm_http_add_endpoint() and isc_nm_http_add_doh_endpoint()
into one function, renamed isc_http_endpoint().
- moved isc_nm_http_connect_send_request() into doh_test.c as a helper
function; remove it from the public API.
- renamed isc_http2 and isc_nm_http2 types and functions to just isc_http
and isc_nm_http, for consistency with other existing names.
- shortened a number of long names.
- the caller is now responsible for determining the peer address.
in isc_nm_httpconnect(); this eliminates the need to parse the URI
and the dependency on an external resolver.
- the caller is also now responsible for creating the SSL client context,
for consistency with isc_nm_tlsdnsconnect().
- added setter functions for HTTP/2 ALPN. instead of setting up ALPN in
isc_tlsctx_createclient(), we now have a function
isc_tlsctx_enable_http2client_alpn() that can be run from
isc_nm_httpconnect().
- refactored isc_nm_httprequest() into separate read and send functions.
isc_nm_send() or isc_nm_read() is called on an http socket, it will
be stored until a corresponding isc_nm_read() or _send() arrives; when
we have both halves of the pair the HTTP request will be initiated.
- isc_nm_httprequest() is renamed isc__nm_http_request() for use as an
internal helper function by the DoH unit test. (eventually doh_test
should be rewritten to use read and send, and this function should
be removed.)
- added implementations of isc__nm_tls_settimeout() and
isc__nm_http_settimeout().
- increased NGHTTP2 header block length for client connections to 128K.
- use isc_mem_t for internal memory allocations inside nghttp2, to
help track memory leaks.
- send "Cache-Control" header in requests and responses. (note:
currently we try to bypass HTTP caching proxies, but ideally we should
interact with them: https://tools.ietf.org/html/rfc8484#section-5.1)
2021-02-03 16:59:49 -08:00
|
|
|
isc_tls_t *
|
|
|
|
isc_tls_create(isc_tlsctx_t *ctx) {
|
|
|
|
isc_tls_t *newctx = NULL;
|
|
|
|
|
|
|
|
REQUIRE(ctx != NULL);
|
|
|
|
|
|
|
|
newctx = SSL_new(ctx);
|
|
|
|
if (newctx == NULL) {
|
|
|
|
char errbuf[256];
|
|
|
|
unsigned long err = ERR_get_error();
|
|
|
|
|
|
|
|
ERR_error_string_n(err, errbuf, sizeof(errbuf));
|
|
|
|
fprintf(stderr, "%s:SSL_new(%p) -> %s\n", __func__, ctx,
|
|
|
|
errbuf);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (newctx);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
isc_tls_free(isc_tls_t **tlsp) {
|
|
|
|
REQUIRE(tlsp != NULL && *tlsp != NULL);
|
|
|
|
|
|
|
|
SSL_free(*tlsp);
|
|
|
|
*tlsp = NULL;
|
|
|
|
}
|
|
|
|
|
2021-04-21 13:52:15 +02:00
|
|
|
#if HAVE_LIBNGHTTP2
|
refactor outgoing HTTP connection support
- style, cleanup, and removal of unnecessary code.
- combined isc_nm_http_add_endpoint() and isc_nm_http_add_doh_endpoint()
into one function, renamed isc_http_endpoint().
- moved isc_nm_http_connect_send_request() into doh_test.c as a helper
function; remove it from the public API.
- renamed isc_http2 and isc_nm_http2 types and functions to just isc_http
and isc_nm_http, for consistency with other existing names.
- shortened a number of long names.
- the caller is now responsible for determining the peer address.
in isc_nm_httpconnect(); this eliminates the need to parse the URI
and the dependency on an external resolver.
- the caller is also now responsible for creating the SSL client context,
for consistency with isc_nm_tlsdnsconnect().
- added setter functions for HTTP/2 ALPN. instead of setting up ALPN in
isc_tlsctx_createclient(), we now have a function
isc_tlsctx_enable_http2client_alpn() that can be run from
isc_nm_httpconnect().
- refactored isc_nm_httprequest() into separate read and send functions.
isc_nm_send() or isc_nm_read() is called on an http socket, it will
be stored until a corresponding isc_nm_read() or _send() arrives; when
we have both halves of the pair the HTTP request will be initiated.
- isc_nm_httprequest() is renamed isc__nm_http_request() for use as an
internal helper function by the DoH unit test. (eventually doh_test
should be rewritten to use read and send, and this function should
be removed.)
- added implementations of isc__nm_tls_settimeout() and
isc__nm_http_settimeout().
- increased NGHTTP2 header block length for client connections to 128K.
- use isc_mem_t for internal memory allocations inside nghttp2, to
help track memory leaks.
- send "Cache-Control" header in requests and responses. (note:
currently we try to bypass HTTP caching proxies, but ideally we should
interact with them: https://tools.ietf.org/html/rfc8484#section-5.1)
2021-02-03 16:59:49 -08:00
|
|
|
#ifndef OPENSSL_NO_NEXTPROTONEG
|
|
|
|
/*
|
|
|
|
* NPN TLS extension client callback.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
select_next_proto_cb(SSL *ssl, unsigned char **out, unsigned char *outlen,
|
|
|
|
const unsigned char *in, unsigned int inlen, void *arg) {
|
|
|
|
UNUSED(ssl);
|
|
|
|
UNUSED(arg);
|
|
|
|
|
|
|
|
if (nghttp2_select_next_protocol(out, outlen, in, inlen) <= 0) {
|
|
|
|
return (SSL_TLSEXT_ERR_NOACK);
|
|
|
|
}
|
|
|
|
return (SSL_TLSEXT_ERR_OK);
|
|
|
|
}
|
|
|
|
#endif /* !OPENSSL_NO_NEXTPROTONEG */
|
|
|
|
|
|
|
|
void
|
|
|
|
isc_tlsctx_enable_http2client_alpn(isc_tlsctx_t *ctx) {
|
|
|
|
REQUIRE(ctx != NULL);
|
|
|
|
|
|
|
|
#ifndef OPENSSL_NO_NEXTPROTONEG
|
|
|
|
SSL_CTX_set_next_proto_select_cb(ctx, select_next_proto_cb, NULL);
|
|
|
|
#endif /* !OPENSSL_NO_NEXTPROTONEG */
|
|
|
|
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
|
|
|
SSL_CTX_set_alpn_protos(ctx, (const unsigned char *)NGHTTP2_PROTO_ALPN,
|
|
|
|
NGHTTP2_PROTO_ALPN_LEN);
|
|
|
|
#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifndef OPENSSL_NO_NEXTPROTONEG
|
|
|
|
static int
|
|
|
|
next_proto_cb(isc_tls_t *ssl, const unsigned char **data, unsigned int *len,
|
|
|
|
void *arg) {
|
|
|
|
UNUSED(ssl);
|
|
|
|
UNUSED(arg);
|
|
|
|
|
|
|
|
*data = (const unsigned char *)NGHTTP2_PROTO_ALPN;
|
|
|
|
*len = (unsigned int)NGHTTP2_PROTO_ALPN_LEN;
|
|
|
|
return (SSL_TLSEXT_ERR_OK);
|
|
|
|
}
|
|
|
|
#endif /* !OPENSSL_NO_NEXTPROTONEG */
|
|
|
|
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
|
|
|
static int
|
|
|
|
alpn_select_proto_cb(SSL *ssl, const unsigned char **out, unsigned char *outlen,
|
|
|
|
const unsigned char *in, unsigned int inlen, void *arg) {
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
UNUSED(ssl);
|
|
|
|
UNUSED(arg);
|
|
|
|
|
|
|
|
ret = nghttp2_select_next_protocol((unsigned char **)(uintptr_t)out,
|
|
|
|
outlen, in, inlen);
|
|
|
|
|
|
|
|
if (ret != 1) {
|
|
|
|
return (SSL_TLSEXT_ERR_NOACK);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (SSL_TLSEXT_ERR_OK);
|
|
|
|
}
|
|
|
|
#endif /* OPENSSL_VERSION_NUMBER >= 0x10002000L */
|
|
|
|
|
|
|
|
void
|
|
|
|
isc_tlsctx_enable_http2server_alpn(isc_tlsctx_t *tls) {
|
|
|
|
REQUIRE(tls != NULL);
|
|
|
|
|
|
|
|
#ifndef OPENSSL_NO_NEXTPROTONEG
|
|
|
|
SSL_CTX_set_next_protos_advertised_cb(tls, next_proto_cb, NULL);
|
|
|
|
#endif // OPENSSL_NO_NEXTPROTONEG
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
|
|
|
SSL_CTX_set_alpn_select_cb(tls, alpn_select_proto_cb, NULL);
|
|
|
|
#endif // OPENSSL_VERSION_NUMBER >= 0x10002000L
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
isc_tls_get_http2_alpn(isc_tls_t *tls, const unsigned char **alpn,
|
|
|
|
unsigned int *alpnlen) {
|
|
|
|
REQUIRE(tls != NULL);
|
|
|
|
REQUIRE(alpn != NULL);
|
|
|
|
REQUIRE(alpnlen != NULL);
|
|
|
|
|
|
|
|
#ifndef OPENSSL_NO_NEXTPROTONEG
|
|
|
|
SSL_get0_next_proto_negotiated(tls, alpn, alpnlen);
|
|
|
|
#endif
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x10002000L
|
|
|
|
if (*alpn == NULL) {
|
|
|
|
SSL_get0_alpn_selected(tls, alpn, alpnlen);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
2021-04-21 13:52:15 +02:00
|
|
|
#endif /* HAVE_LIBNGHTTP2 */
|