Skip to content

Commit

Permalink
Unique DoT and DoH SSL contexts to allow for different ALPN (#1222)
Browse files Browse the repository at this point in the history
  • Loading branch information
gthess authored Jan 20, 2025
1 parent 1d428f2 commit e4483bb
Show file tree
Hide file tree
Showing 10 changed files with 181 additions and 77 deletions.
7 changes: 4 additions & 3 deletions daemon/daemon.c
Original file line number Diff line number Diff line change
Expand Up @@ -954,11 +954,12 @@ daemon_delete(struct daemon* daemon)
free(daemon->env);
#ifdef HAVE_SSL
listen_sslctx_delete_ticket_keys();
SSL_CTX_free((SSL_CTX*)daemon->listen_sslctx);
SSL_CTX_free((SSL_CTX*)daemon->connect_sslctx);
SSL_CTX_free((SSL_CTX*)daemon->listen_dot_sslctx);
SSL_CTX_free((SSL_CTX*)daemon->listen_doh_sslctx);
SSL_CTX_free((SSL_CTX*)daemon->connect_dot_sslctx);
#endif
#ifdef HAVE_NGTCP2
SSL_CTX_free((SSL_CTX*)daemon->quic_sslctx);
SSL_CTX_free((SSL_CTX*)daemon->listen_quic_sslctx);
#endif
free(daemon);
/* lex cleanup */
Expand Down
10 changes: 7 additions & 3 deletions daemon/daemon.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,14 @@ struct daemon {
struct listen_port* rc_ports;
/** remote control connections management (for first worker) */
struct daemon_remote* rc;
/** ssl context for listening to dnstcp over ssl, and connecting ssl */
void* listen_sslctx, *connect_sslctx;
/** ssl context for listening to dnstcp over ssl */
void* listen_dot_sslctx;
/** ssl context for connecting to dnstcp over ssl */
void* connect_dot_sslctx;
/** ssl context for listening to DoH */
void* listen_doh_sslctx;
/** ssl context for listening to quic */
void* quic_sslctx;
void* listen_quic_sslctx;
/** num threads allocated */
int num;
/** num threads allocated in the previous config or 0 at first */
Expand Down
94 changes: 57 additions & 37 deletions daemon/unbound.c
Original file line number Diff line number Diff line change
Expand Up @@ -463,6 +463,62 @@ detach(void)
#endif /* HAVE_DAEMON */
}

/* setup a listening ssl context, fatal_exit() on any failure */
static void
setup_listen_sslctx(void** ctx, int is_dot, int is_doh, struct config_file* cfg)
{
#ifdef HAVE_SSL
if(!(*ctx = listen_sslctx_create(
cfg->ssl_service_key, cfg->ssl_service_pem, NULL,
cfg->tls_ciphers, cfg->tls_ciphersuites,
(cfg->tls_session_ticket_keys.first &&
cfg->tls_session_ticket_keys.first->str[0] != 0),
is_dot, is_doh))) {
fatal_exit("could not set up listen SSL_CTX");
}
#else /* HAVE_SSL */
(void)ctx;(void)is_dot;(void)is_doh;(void)cfg;
#endif /* HAVE_SSL */
}

/* setups the needed ssl contexts, fatal_exit() on any failure */
static void
setup_sslctxs(struct daemon* daemon, struct config_file* cfg)
{
#ifdef HAVE_SSL
if(!(daemon->rc = daemon_remote_create(cfg)))
fatal_exit("could not set up remote-control");
if(cfg->ssl_service_key && cfg->ssl_service_key[0]) {
/* setup the session keys; the callback to use them will be
* attached to each sslctx separately */
if(cfg->tls_session_ticket_keys.first &&
cfg->tls_session_ticket_keys.first->str[0] != 0) {
if(!listen_sslctx_setup_ticket_keys(
cfg->tls_session_ticket_keys.first)) {
fatal_exit("could not set session ticket SSL_CTX");
}
}
(void)setup_listen_sslctx(&daemon->listen_dot_sslctx, 1, 0, cfg);
#ifdef HAVE_NGHTTP2_NGHTTP2_H
if(cfg_has_https(cfg)) {
(void)setup_listen_sslctx(&daemon->listen_doh_sslctx, 0, 1, cfg);
}
#endif
#ifdef HAVE_NGTCP2
if(!(daemon->listen_quic_sslctx = quic_sslctx_create(
cfg->ssl_service_key, cfg->ssl_service_pem, NULL))) {
fatal_exit("could not set up quic SSL_CTX");
}
#endif /* HAVE_NGTCP2 */
}
if(!(daemon->connect_dot_sslctx = connect_sslctx_create(NULL, NULL,
cfg->tls_cert_bundle, cfg->tls_win_cert)))
fatal_exit("could not set up connect SSL_CTX");
#else /* HAVE_SSL */
(void)daemon;(void)cfg;
#endif /* HAVE_SSL */
}

/** daemonize, drop user privileges and chroot if needed */
static void
perform_setup(struct daemon* daemon, struct config_file* cfg, int debug_mode,
Expand All @@ -489,43 +545,7 @@ perform_setup(struct daemon* daemon, struct config_file* cfg, int debug_mode,
#endif

/* read ssl keys while superuser and outside chroot */
#ifdef HAVE_SSL
if(!(daemon->rc = daemon_remote_create(cfg)))
fatal_exit("could not set up remote-control");
if(cfg->ssl_service_key && cfg->ssl_service_key[0]) {
if(!(daemon->listen_sslctx = listen_sslctx_create(
cfg->ssl_service_key, cfg->ssl_service_pem, NULL))) {
fatal_exit("could not set up listen SSL_CTX");
}
if(cfg->tls_ciphers && cfg->tls_ciphers[0]) {
if (!SSL_CTX_set_cipher_list(daemon->listen_sslctx, cfg->tls_ciphers)) {
fatal_exit("failed to set tls-cipher %s", cfg->tls_ciphers);
}
}
#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
if(cfg->tls_ciphersuites && cfg->tls_ciphersuites[0]) {
if (!SSL_CTX_set_ciphersuites(daemon->listen_sslctx, cfg->tls_ciphersuites)) {
fatal_exit("failed to set tls-ciphersuites %s", cfg->tls_ciphersuites);
}
}
#endif /* HAVE_SSL_CTX_SET_CIPHERSUITES */
if(cfg->tls_session_ticket_keys.first &&
cfg->tls_session_ticket_keys.first->str[0] != 0) {
if(!listen_sslctx_setup_ticket_keys(daemon->listen_sslctx, cfg->tls_session_ticket_keys.first)) {
fatal_exit("could not set session ticket SSL_CTX");
}
}
#ifdef HAVE_NGTCP2
if(!(daemon->quic_sslctx = quic_sslctx_create(
cfg->ssl_service_key, cfg->ssl_service_pem, NULL))) {
fatal_exit("could not set up quic SSL_CTX");
}
#endif /* HAVE_NGTCP2 */
}
if(!(daemon->connect_sslctx = connect_sslctx_create(NULL, NULL,
cfg->tls_cert_bundle, cfg->tls_win_cert)))
fatal_exit("could not set up connect SSL_CTX");
#endif /* HAVE_SSL */
(void)setup_sslctxs(daemon, cfg);

/* init syslog (as root) if needed, before daemonize, otherwise
* a fork error could not be printed since daemonize closed stderr.*/
Expand Down
7 changes: 4 additions & 3 deletions daemon/worker.c
Original file line number Diff line number Diff line change
Expand Up @@ -2173,8 +2173,9 @@ worker_init(struct worker* worker, struct config_file *cfg,
: cfg->tcp_idle_timeout,
cfg->harden_large_queries, cfg->http_max_streams,
cfg->http_endpoint, cfg->http_notls_downstream,
worker->daemon->tcl, worker->daemon->listen_sslctx,
worker->daemon->quic_sslctx,
worker->daemon->tcl, worker->daemon->listen_dot_sslctx,
worker->daemon->listen_doh_sslctx,
worker->daemon->listen_quic_sslctx,
dtenv, worker->daemon->doq_table, worker->env.rnd,
cfg, worker_handle_request, worker);
if(!worker->front) {
Expand All @@ -2191,7 +2192,7 @@ worker_init(struct worker* worker, struct config_file *cfg,
cfg->unwanted_threshold, cfg->outgoing_tcp_mss,
&worker_alloc_cleanup, worker,
cfg->do_udp || cfg->udp_upstream_without_downstream,
worker->daemon->connect_sslctx, cfg->delay_close,
worker->daemon->connect_dot_sslctx, cfg->delay_close,
cfg->tls_use_sni, dtenv, cfg->udp_connect,
cfg->max_reuse_tcp_queries, cfg->tcp_reuse_timeout,
cfg->tcp_auth_query_timeout);
Expand Down
3 changes: 2 additions & 1 deletion dnstap/unbound-dnstap-socket.c
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,8 @@ static struct tap_socket* tap_socket_new_tlsaccept(char* ip,
s->fd = -1;
s->ev_cb = ev_cb;
s->data = data;
s->sslctx = listen_sslctx_create(server_key, server_cert, verifypem);
s->sslctx = listen_sslctx_create(server_key, server_cert, verifypem,
NULL, NULL, 0, 0, 0);
if(!s->sslctx) {
log_err("could not create ssl context");
free(s->ip);
Expand Down
9 changes: 6 additions & 3 deletions services/listen_dnsport.c
Original file line number Diff line number Diff line change
Expand Up @@ -1512,7 +1512,8 @@ listen_create(struct comm_base* base, struct listen_port* ports,
size_t bufsize, int tcp_accept_count, int tcp_idle_timeout,
int harden_large_queries, uint32_t http_max_streams,
char* http_endpoint, int http_notls, struct tcl_list* tcp_conn_limit,
void* sslctx, void* quic_sslctx, struct dt_env* dtenv,
void* dot_sslctx, void* doh_sslctx, void* quic_sslctx,
struct dt_env* dtenv,
struct doq_table* doq_table,
struct ub_randstate* rnd,struct config_file* cfg,
comm_point_callback_type* cb, void *cb_arg)
Expand Down Expand Up @@ -1566,7 +1567,7 @@ listen_create(struct comm_base* base, struct listen_port* ports,
ports->ftype, ports->pp2_enabled, cb, cb_arg,
ports->socket);
if(ports->ftype == listen_type_http) {
if(!sslctx && !http_notls) {
if(!doh_sslctx && !http_notls) {
log_warn("HTTPS port configured, but "
"no TLS tls-service-key or "
"tls-service-pem set");
Expand Down Expand Up @@ -1612,8 +1613,10 @@ listen_create(struct comm_base* base, struct listen_port* ports,
cp->ssl = NULL;
} else if(ports->ftype == listen_type_doq) {
cp->ssl = quic_sslctx;
} else if(ports->ftype == listen_type_http) {
cp->ssl = doh_sslctx;
} else {
cp->ssl = sslctx;
cp->ssl = dot_sslctx;
}
cp->dtenv = dtenv;
cp->do_not_close = 1;
Expand Down
6 changes: 4 additions & 2 deletions services/listen_dnsport.h
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,8 @@ int resolve_interface_names(char** ifs, int num_ifs,
* @param http_endpoint: HTTP endpoint to service queries on
* @param http_notls: no TLS for http downstream
* @param tcp_conn_limit: TCP connection limit info.
* @param sslctx: nonNULL if ssl context.
* @param dot_sslctx: nonNULL if dot ssl context.
* @param doh_sslctx: nonNULL if doh ssl context.
* @param quic_sslctx: nonNULL if quic ssl context.
* @param dtenv: nonNULL if dnstap enabled.
* @param doq_table: the doq connection table, with shared information.
Expand All @@ -210,7 +211,8 @@ listen_create(struct comm_base* base, struct listen_port* ports,
size_t bufsize, int tcp_accept_count, int tcp_idle_timeout,
int harden_large_queries, uint32_t http_max_streams,
char* http_endpoint, int http_notls, struct tcl_list* tcp_conn_limit,
void* sslctx, void* quic_sslctx, struct dt_env* dtenv,
void* dot_sslctx, void* doh_sslctx, void* quic_sslctx,
struct dt_env* dtenv,
struct doq_table* doq_table,
struct ub_randstate* rnd,struct config_file* cfg,
comm_point_callback_type* cb, void *cb_arg);
Expand Down
3 changes: 2 additions & 1 deletion testcode/fake_event.c
Original file line number Diff line number Diff line change
Expand Up @@ -938,7 +938,8 @@ listen_create(struct comm_base* base, struct listen_port* ATTR_UNUSED(ports),
char* ATTR_UNUSED(http_endpoint),
int ATTR_UNUSED(http_notls),
struct tcl_list* ATTR_UNUSED(tcp_conn_limit),
void* ATTR_UNUSED(sslctx), void* ATTR_UNUSED(quic_ssl),
void* ATTR_UNUSED(dot_sslctx), void* ATTR_UNUSED(doh_sslctx),
void* ATTR_UNUSED(quic_ssl),
struct dt_env* ATTR_UNUSED(dtenv),
struct doq_table* ATTR_UNUSED(table),
struct ub_randstate* ATTR_UNUSED(rnd),
Expand Down
104 changes: 84 additions & 20 deletions util/net_help.c
Original file line number Diff line number Diff line change
Expand Up @@ -1162,8 +1162,29 @@ log_cert(unsigned level, const char* str, void* cert)
}
#endif /* HAVE_SSL */

#if defined(HAVE_SSL) && defined(HAVE_SSL_CTX_SET_ALPN_SELECT_CB)
static int
dot_alpn_select_cb(SSL* ATTR_UNUSED(ssl), const unsigned char** out,
unsigned char* outlen, const unsigned char* in, unsigned int inlen,
void* ATTR_UNUSED(arg))
{
static const unsigned char alpns[] = { 3, 'd', 'o', 't' };
unsigned char* tmp_out;
int ret;
ret = SSL_select_next_proto(&tmp_out, outlen, alpns, sizeof(alpns), in, inlen);
if(ret == OPENSSL_NPN_NO_OVERLAP) {
/* Client sent ALPN but no overlap. Should have been error,
* but for privacy we continue without ALPN (e.g., if certain
* ALPNs are blocked) */
return SSL_TLSEXT_ERR_NOACK;
}
*out = tmp_out;
return SSL_TLSEXT_ERR_OK;
}
#endif

#if defined(HAVE_SSL) && defined(HAVE_NGHTTP2) && defined(HAVE_SSL_CTX_SET_ALPN_SELECT_CB)
static int alpn_select_cb(SSL* ATTR_UNUSED(ssl), const unsigned char** out,
static int doh_alpn_select_cb(SSL* ATTR_UNUSED(ssl), const unsigned char** out,
unsigned char* outlen, const unsigned char* in, unsigned int inlen,
void* ATTR_UNUSED(arg))
{
Expand All @@ -1177,6 +1198,23 @@ static int alpn_select_cb(SSL* ATTR_UNUSED(ssl), const unsigned char** out,
}
#endif

/* setup the callback for ticket keys */
static int
setup_ticket_keys_cb(void* sslctx)
{
# ifdef HAVE_SSL_CTX_SET_TLSEXT_TICKET_KEY_EVP_CB
if(SSL_CTX_set_tlsext_ticket_key_evp_cb(sslctx, tls_session_ticket_key_cb) == 0) {
return 0;
}
# else
if(SSL_CTX_set_tlsext_ticket_key_cb(sslctx, tls_session_ticket_key_cb) == 0) {
return 0;
}
# endif
return 1;
}


int
listen_sslctx_setup(void* ctxt)
{
Expand Down Expand Up @@ -1248,9 +1286,6 @@ listen_sslctx_setup(void* ctxt)
#ifdef HAVE_SSL_CTX_SET_SECURITY_LEVEL
SSL_CTX_set_security_level(ctx, 0);
#endif
#if defined(HAVE_SSL_CTX_SET_ALPN_SELECT_CB) && defined(HAVE_NGHTTP2)
SSL_CTX_set_alpn_select_cb(ctx, alpn_select_cb, NULL);
#endif
#else
(void)ctxt;
#endif /* HAVE_SSL */
Expand Down Expand Up @@ -1285,7 +1320,10 @@ listen_sslctx_setup_2(void* ctxt)
#endif /* HAVE_SSL */
}

void* listen_sslctx_create(char* key, char* pem, char* verifypem)
void* listen_sslctx_create(const char* key, const char* pem,
const char* verifypem, const char* tls_ciphers,
const char* tls_ciphersuites, int set_ticket_keys_cb,
int is_dot, int is_doh)
{
#ifdef HAVE_SSL
SSL_CTX* ctx = SSL_CTX_new(SSLv23_server_method());
Expand Down Expand Up @@ -1336,11 +1374,50 @@ void* listen_sslctx_create(char* key, char* pem, char* verifypem)
verifypem));
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER|SSL_VERIFY_FAIL_IF_NO_PEER_CERT, NULL);
}
if(tls_ciphers && tls_ciphers[0]) {
if (!SSL_CTX_set_cipher_list(ctx, tls_ciphers)) {
log_err("failed to set tls-cipher %s",
tls_ciphers);
log_crypto_err("Error in SSL_CTX_set_cipher_list");
SSL_CTX_free(ctx);
return NULL;
}
}
#ifdef HAVE_SSL_CTX_SET_CIPHERSUITES
if(tls_ciphersuites && tls_ciphersuites[0]) {
if (!SSL_CTX_set_ciphersuites(ctx, tls_ciphersuites)) {
log_err("failed to set tls-ciphersuites %s",
tls_ciphersuites);
log_crypto_err("Error in SSL_CTX_set_ciphersuites");
SSL_CTX_free(ctx);
return NULL;
}
}
#endif /* HAVE_SSL_CTX_SET_CIPHERSUITES */
if(set_ticket_keys_cb) {
if(!setup_ticket_keys_cb(ctx)) {
log_crypto_err("no support for TLS session ticket");
SSL_CTX_free(ctx);
return NULL;
}
}
/* setup ALPN */
#if defined(HAVE_SSL_CTX_SET_ALPN_SELECT_CB)
if(is_dot) {
SSL_CTX_set_alpn_select_cb(ctx, dot_alpn_select_cb, NULL);
} else if(is_doh) {
#if defined(HAVE_NGHTTP2)
SSL_CTX_set_alpn_select_cb(ctx, doh_alpn_select_cb, NULL);
#endif
}
#endif /* HAVE_SSL_CTX_SET_ALPN_SELECT_CB */
return ctx;
#else
(void)key; (void)pem; (void)verifypem;
(void)tls_ciphers; (void)tls_ciphersuites;
(void)tls_session_ticket_keys;
return NULL;
#endif
#endif /* HAVE_SSL */
}

#ifdef USE_WINSOCK
Expand Down Expand Up @@ -1700,7 +1777,7 @@ void ub_openssl_lock_delete(void)
#endif /* OPENSSL_THREADS */
}

int listen_sslctx_setup_ticket_keys(void* sslctx, struct config_strlist* tls_session_ticket_keys) {
int listen_sslctx_setup_ticket_keys(struct config_strlist* tls_session_ticket_keys) {
#ifdef HAVE_SSL
size_t s = 1;
struct config_strlist* p;
Expand Down Expand Up @@ -1746,24 +1823,11 @@ int listen_sslctx_setup_ticket_keys(void* sslctx, struct config_strlist* tls_ses
}
/* terminate array with NULL key name entry */
keys->key_name = NULL;
# ifdef HAVE_SSL_CTX_SET_TLSEXT_TICKET_KEY_EVP_CB
if(SSL_CTX_set_tlsext_ticket_key_evp_cb(sslctx, tls_session_ticket_key_cb) == 0) {
log_err("no support for TLS session ticket");
return 0;
}
# else
if(SSL_CTX_set_tlsext_ticket_key_cb(sslctx, tls_session_ticket_key_cb) == 0) {
log_err("no support for TLS session ticket");
return 0;
}
# endif
return 1;
#else
(void)sslctx;
(void)tls_session_ticket_keys;
return 0;
#endif

}

#ifdef HAVE_SSL
Expand Down
Loading

0 comments on commit e4483bb

Please sign in to comment.