Files
bind9/lib/isc/netmgr/tls.c
Ondřej Surý a49d88568f Turn all the callback to be always asynchronous
When calling the high level netmgr functions, the callback would be
sometimes called synchronously if we catch the failure directly, or
asynchronously if it happens later.  The synchronous call to the
callback could create deadlocks as the caller would not expect the
failed callback to be executed directly.
2020-11-11 22:15:40 +01:00

908 lines
23 KiB
C

/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
#include <libgen.h>
#include <unistd.h>
#include <uv.h>
#include <openssl/err.h>
#include <openssl/ssl.h>
#include <isc/atomic.h>
#include <isc/buffer.h>
#include <isc/condition.h>
#include <isc/log.h>
#include <isc/magic.h>
#include <isc/mem.h>
#include <isc/netmgr.h>
#include <isc/once.h>
#include <isc/quota.h>
#include <isc/random.h>
#include <isc/refcount.h>
#include <isc/region.h>
#include <isc/result.h>
#include <isc/sockaddr.h>
#include <isc/stdtime.h>
#include <isc/thread.h>
#include <isc/util.h>
#include "netmgr-int.h"
#include "uv-compat.h"
#define TLS_BUF_SIZE 65536
static isc_result_t
tls_error_to_result(int tls_err) {
switch (tls_err) {
case SSL_ERROR_ZERO_RETURN:
return (ISC_R_EOF);
default:
return (ISC_R_UNEXPECTED);
}
}
static void
tls_do_bio(isc_nmsocket_t *sock);
static void
tls_close_direct(isc_nmsocket_t *sock);
static void
async_tls_do_bio(isc_nmsocket_t *sock);
/*
* The socket is closing, outerhandle has been detached, listener is
* inactive, or the netmgr is closing: any operation on it should abort
* with ISC_R_CANCELED.
*/
static bool
inactive(isc_nmsocket_t *sock) {
return (!isc__nmsocket_active(sock) || atomic_load(&sock->closing) ||
sock->outerhandle == NULL ||
(sock->listener != NULL &&
!isc__nmsocket_active(sock->listener)) ||
atomic_load(&sock->mgr->closing));
}
static void
tls_senddone(isc_nmhandle_t *handle, isc_result_t eresult, void *cbarg) {
isc_nmsocket_t *sock = (isc_nmsocket_t *)cbarg;
UNUSED(handle);
/* XXXWPK TODO */
UNUSED(eresult);
isc_mem_put(sock->mgr->mctx, sock->tls.senddata.base,
sock->tls.senddata.length);
sock->tls.senddata = (isc_region_t){ NULL, 0 };
sock->tls.sending = false;
async_tls_do_bio(sock);
}
static void
async_tls_do_bio(isc_nmsocket_t *sock) {
isc__netievent_tlsdobio_t *ievent =
isc__nm_get_ievent(sock->mgr, netievent_tlsdobio);
ievent->sock = sock;
isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
(isc__netievent_t *)ievent);
}
static void
tls_do_bio(isc_nmsocket_t *sock) {
isc_result_t result = ISC_R_SUCCESS;
int pending, tls_err = 0;
int rv;
isc__nm_uvreq_t *req;
REQUIRE(sock->tid == isc_nm_tid());
/* We will resume read if TLS layer wants us to */
isc_nm_pauseread(sock->outerhandle);
if (inactive(sock)) {
result = ISC_R_CANCELED;
goto error;
}
if (sock->tls.state == TLS_INIT) {
(void)SSL_do_handshake(sock->tls.ssl);
sock->tls.state = TLS_HANDSHAKE;
}
if (sock->tls.state == TLS_ERROR) {
result = ISC_R_FAILURE;
goto error;
}
/* Data from TLS to client */
char buf[1];
if (sock->tls.state == TLS_IO && sock->recv_cb != NULL &&
!atomic_load(&sock->readpaused))
{
(void)SSL_peek(sock->tls.ssl, buf, 1);
while ((pending = SSL_pending(sock->tls.ssl)) > 0) {
if (pending > TLS_BUF_SIZE) {
pending = TLS_BUF_SIZE;
}
isc_region_t region = {
isc_mem_get(sock->mgr->mctx, pending), pending
};
isc_region_t dregion;
memset(region.base, 0, region.length);
rv = SSL_read(sock->tls.ssl, region.base,
region.length);
/* Pending succeded, so should read */
RUNTIME_CHECK(rv == pending);
dregion = (isc_region_t){ region.base, rv };
sock->recv_cb(sock->statichandle, ISC_R_SUCCESS,
&dregion, sock->recv_cbarg);
isc_mem_put(sock->mgr->mctx, region.base,
region.length);
}
}
/* Peek to move the session forward */
(void)SSL_peek(sock->tls.ssl, buf, 1);
/* Data from TLS to network */
pending = BIO_pending(sock->tls.app_bio);
if (!sock->tls.sending && pending > 0) {
if (pending > TLS_BUF_SIZE) {
pending = TLS_BUF_SIZE;
}
sock->tls.sending = true;
sock->tls.senddata.base = isc_mem_get(sock->mgr->mctx, pending);
sock->tls.senddata.length = pending;
rv = BIO_read(sock->tls.app_bio, sock->tls.senddata.base,
pending);
/* There's something pending, read must succed */
RUNTIME_CHECK(rv == pending);
isc_nm_send(sock->outerhandle, &sock->tls.senddata,
tls_senddone, sock);
/* We'll continue in tls_senddone */
return;
}
/* Get the potential error code */
rv = SSL_peek(sock->tls.ssl, buf, 1);
if (rv < 0) {
tls_err = SSL_get_error(sock->tls.ssl, rv);
}
/* Only after doing the IO we can check if SSL handshake is done */
if (sock->tls.state == TLS_HANDSHAKE &&
SSL_is_init_finished(sock->tls.ssl) == 1)
{
isc_nmhandle_t *tlshandle = isc__nmhandle_get(sock, NULL, NULL);
if (sock->tls.server) {
sock->listener->accept_cb(sock->statichandle,
ISC_R_SUCCESS,
sock->listener->accept_cbarg);
} else {
sock->connect_cb(tlshandle, ISC_R_SUCCESS,
sock->connect_cbarg);
}
isc_nmhandle_detach(&tlshandle);
sock->tls.state = TLS_IO;
async_tls_do_bio(sock);
return;
}
switch (tls_err) {
case 0:
return;
case SSL_ERROR_WANT_WRITE:
if (!sock->tls.sending) {
/*
* Launch tls_do_bio asynchronously. If we're sending
* already the send callback will call it.
*/
async_tls_do_bio(sock);
} else {
return;
}
break;
case SSL_ERROR_WANT_READ:
isc_nm_resumeread(sock->outerhandle);
break;
default:
result = tls_error_to_result(tls_err);
goto error;
}
while ((req = ISC_LIST_HEAD(sock->tls.sends)) != NULL) {
INSIST(VALID_UVREQ(req));
rv = SSL_write(sock->tls.ssl, req->uvbuf.base, req->uvbuf.len);
if (rv < 0) {
if (!sock->tls.sending) {
async_tls_do_bio(sock);
}
return;
}
if (rv != (int)req->uvbuf.len) {
sock->tls.state = TLS_ERROR;
async_tls_do_bio(sock);
return;
}
ISC_LIST_UNLINK(sock->tls.sends, req, link);
req->cb.send(sock->statichandle, ISC_R_SUCCESS, req->cbarg);
isc__nm_uvreq_put(&req, sock);
}
return;
error:
isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_NETMGR,
ISC_LOG_ERROR, "SSL error in BIO: %d %s", tls_err,
isc_result_totext(result));
if (ISC_LIST_HEAD(sock->tls.sends) != NULL) {
while ((req = ISC_LIST_HEAD(sock->tls.sends)) != NULL) {
req->cb.send(sock->statichandle, result, req->cbarg);
ISC_LIST_UNLINK(sock->tls.sends, req, link);
isc__nm_uvreq_put(&req, sock);
}
} else if (sock->recv_cb != NULL) {
sock->recv_cb(sock->statichandle, result, NULL,
sock->recv_cbarg);
} else {
tls_close_direct(sock);
}
}
static void
tls_readcb(isc_nmhandle_t *handle, isc_result_t result, isc_region_t *region,
void *cbarg) {
isc_nmsocket_t *tlssock = (isc_nmsocket_t *)cbarg;
int rv;
REQUIRE(VALID_NMSOCK(tlssock));
REQUIRE(VALID_NMHANDLE(handle));
REQUIRE(tlssock->tid == isc_nm_tid());
if (result != ISC_R_SUCCESS) {
/* Connection closed */
/*
* TODO accept_cb should be called if we're not
* initialized yet!
*/
if (tlssock->recv_cb != NULL) {
tlssock->recv_cb(tlssock->statichandle, result, region,
tlssock->recv_cbarg);
}
isc__nm_tls_close(tlssock);
return;
}
rv = BIO_write(tlssock->tls.app_bio, region->base, region->length);
if (rv != (int)region->length) {
/* XXXWPK log it? */
tlssock->tls.state = TLS_ERROR;
}
tls_do_bio(tlssock);
}
static isc_result_t
initialize_tls(isc_nmsocket_t *sock, bool server) {
REQUIRE(sock->tid == isc_nm_tid());
if (BIO_new_bio_pair(&(sock->tls.ssl_bio), TLS_BUF_SIZE,
&(sock->tls.app_bio), TLS_BUF_SIZE) != 1)
{
SSL_free(sock->tls.ssl);
return (ISC_R_TLSERROR);
}
SSL_set_bio(sock->tls.ssl, sock->tls.ssl_bio, sock->tls.ssl_bio);
if (server) {
SSL_set_accept_state(sock->tls.ssl);
} else {
SSL_set_connect_state(sock->tls.ssl);
}
isc_nm_read(sock->outerhandle, tls_readcb, sock);
tls_do_bio(sock);
return (ISC_R_SUCCESS);
}
static isc_result_t
tlslisten_acceptcb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
isc_nmsocket_t *tlslistensock = (isc_nmsocket_t *)cbarg;
isc_nmsocket_t *tlssock = NULL;
REQUIRE(VALID_NMSOCK(tlslistensock));
REQUIRE(tlslistensock->type == isc_nm_tlslistener);
/* If accept() was unsuccessful we can't do anything */
if (result != ISC_R_SUCCESS) {
return (result);
}
/*
* We need to create a 'wrapper' tlssocket for this connection.
*/
tlssock = isc_mem_get(handle->sock->mgr->mctx, sizeof(*tlssock));
isc__nmsocket_init(tlssock, handle->sock->mgr, isc_nm_tlssocket,
handle->sock->iface);
tlssock->extrahandlesize = tlslistensock->extrahandlesize;
isc__nmsocket_attach(tlslistensock, &tlssock->listener);
isc_nmhandle_attach(handle, &tlssock->outerhandle);
tlssock->peer = handle->sock->peer;
tlssock->read_timeout = handle->sock->mgr->init;
tlssock->tid = isc_nm_tid();
tlssock->tls.server = true;
tlssock->tls.state = TLS_INIT;
tlssock->tls.ctx = tlslistensock->tls.ctx;
/* We need to initialize SSL now to reference SSL_CTX properly */
tlssock->tls.ssl = SSL_new(tlssock->tls.ctx);
ISC_LIST_INIT(tlssock->tls.sends);
if (tlssock->tls.ssl == NULL) {
atomic_store(&tlssock->closed, true);
isc__nmsocket_detach(&tlssock);
return (ISC_R_TLSERROR);
}
uv_timer_init(&tlssock->mgr->workers[isc_nm_tid()].loop,
&tlssock->timer);
tlssock->timer.data = tlssock;
tlssock->timer_initialized = true;
tlssock->tls.ctx = tlslistensock->tls.ctx;
result = initialize_tls(tlssock, true);
RUNTIME_CHECK(result == ISC_R_SUCCESS);
/* TODO: catch failure code, detach tlssock, and log the error */
return (result);
}
isc_result_t
isc_nm_listentls(isc_nm_t *mgr, isc_nmiface_t *iface,
isc_nm_accept_cb_t accept_cb, void *accept_cbarg,
size_t extrahandlesize, int backlog, isc_quota_t *quota,
SSL_CTX *sslctx, isc_nmsocket_t **sockp) {
isc_result_t result;
isc_nmsocket_t *tlssock = isc_mem_get(mgr->mctx, sizeof(*tlssock));
isc__nmsocket_init(tlssock, mgr, isc_nm_tlslistener, iface);
tlssock->accept_cb = accept_cb;
tlssock->accept_cbarg = accept_cbarg;
tlssock->extrahandlesize = extrahandlesize;
tlssock->tls.ctx = sslctx;
/* We need to initialize SSL now to reference SSL_CTX properly */
tlssock->tls.ssl = SSL_new(tlssock->tls.ctx);
if (tlssock->tls.ssl == NULL) {
atomic_store(&tlssock->closed, true);
isc__nmsocket_detach(&tlssock);
return (ISC_R_TLSERROR);
}
/*
* tlssock will be a TLS 'wrapper' around an unencrypted stream.
* We set tlssock->outer to a socket listening for a TCP connection.
*/
result = isc_nm_listentcp(mgr, iface, tlslisten_acceptcb, tlssock,
extrahandlesize, backlog, quota,
&tlssock->outer);
if (result == ISC_R_SUCCESS) {
atomic_store(&tlssock->listening, true);
*sockp = tlssock;
return (ISC_R_SUCCESS);
} else {
atomic_store(&tlssock->closed, true);
isc__nmsocket_detach(&tlssock);
return (result);
}
}
void
isc__nm_async_tlssend(isc__networker_t *worker, isc__netievent_t *ev0) {
int rv;
isc__netievent_tcpsend_t *ievent = (isc__netievent_tcpsend_t *)ev0;
isc_nmsocket_t *sock = ievent->sock;
isc__nm_uvreq_t *req = ievent->req;
ievent->req = NULL;
REQUIRE(VALID_UVREQ(req));
REQUIRE(worker->id == sock->tid);
if (inactive(sock)) {
req->cb.send(req->handle, ISC_R_CANCELED, req->cbarg);
isc__nm_uvreq_put(&req, sock);
return;
}
if (!ISC_LIST_EMPTY(sock->tls.sends)) {
/* We're not the first */
ISC_LIST_APPEND(sock->tls.sends, req, link);
tls_do_bio(sock);
return;
}
rv = SSL_write(sock->tls.ssl, req->uvbuf.base, req->uvbuf.len);
if (rv < 0) {
/*
* We might need to read, we might need to write, or the
* TLS socket might be dead - in any case, we need to
* enqueue the uvreq and let the TLS BIO layer do the rest.
*/
ISC_LIST_APPEND(sock->tls.sends, req, link);
tls_do_bio(sock);
return;
}
if (rv != (int)req->uvbuf.len) {
sock->tls.state = TLS_ERROR;
async_tls_do_bio(sock);
return;
}
req->cb.send(sock->statichandle, ISC_R_SUCCESS, req->cbarg);
isc__nm_uvreq_put(&req, sock);
tls_do_bio(sock);
return;
}
void
isc__nm_tls_send(isc_nmhandle_t *handle, isc_region_t *region, isc_nm_cb_t cb,
void *cbarg) {
isc__netievent_tcpsend_t *ievent = NULL;
isc__nm_uvreq_t *uvreq = NULL;
isc_nmsocket_t *sock = NULL;
REQUIRE(VALID_NMHANDLE(handle));
REQUIRE(VALID_NMSOCK(handle->sock));
sock = handle->sock;
REQUIRE(sock->type == isc_nm_tlssocket);
if (inactive(sock)) {
cb(handle, ISC_R_CANCELED, cbarg);
return;
}
uvreq = isc__nm_uvreq_get(sock->mgr, sock);
isc_nmhandle_attach(handle, &uvreq->handle);
uvreq->cb.send = cb;
uvreq->cbarg = cbarg;
uvreq->uvbuf.base = (char *)region->base;
uvreq->uvbuf.len = region->length;
/*
* We need to create an event and pass it using async channel
*/
ievent = isc__nm_get_ievent(sock->mgr, netievent_tlssend);
ievent->sock = sock;
ievent->req = uvreq;
isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
(isc__netievent_t *)ievent);
}
void
isc__nm_async_tls_startread(isc__networker_t *worker, isc__netievent_t *ev0) {
isc__netievent_startread_t *ievent = (isc__netievent_startread_t *)ev0;
isc_nmsocket_t *sock = ievent->sock;
REQUIRE(worker->id == isc_nm_tid());
tls_do_bio(sock);
}
void
isc__nm_tls_read(isc_nmhandle_t *handle, isc_nm_recv_cb_t cb, void *cbarg) {
isc_nmsocket_t *sock = NULL;
isc__netievent_startread_t *ievent = NULL;
REQUIRE(VALID_NMHANDLE(handle));
sock = handle->sock;
REQUIRE(sock->statichandle == handle);
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(sock->recv_cb == NULL);
REQUIRE(sock->tid == isc_nm_tid());
if (inactive(sock)) {
cb(handle, ISC_R_NOTCONNECTED, NULL, cbarg);
return;
}
sock = handle->sock;
sock->recv_cb = cb;
sock->recv_cbarg = cbarg;
ievent = isc__nm_get_ievent(sock->mgr, netievent_tlsstartread);
ievent->sock = sock;
isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
(isc__netievent_t *)ievent);
}
void
isc__nm_tls_pauseread(isc_nmsocket_t *sock) {
atomic_store(&sock->readpaused, true);
}
void
isc__nm_tls_resumeread(isc_nmsocket_t *sock) {
atomic_store(&sock->readpaused, false);
async_tls_do_bio(sock);
}
static void
timer_close_cb(uv_handle_t *handle) {
isc_nmsocket_t *sock = (isc_nmsocket_t *)uv_handle_get_data(handle);
INSIST(VALID_NMSOCK(sock));
tls_close_direct(sock);
}
static void
tls_close_direct(isc_nmsocket_t *sock) {
REQUIRE(sock->tid == isc_nm_tid());
if (sock->timer_running) {
uv_timer_stop(&sock->timer);
sock->timer_running = false;
}
/* We don't need atomics here, it's all in single network thread
*/
if (sock->timer_initialized) {
/*
* We need to fire the timer callback to clean it up,
* it will then call us again (via detach) so that we
* can finally close the socket.
*/
sock->timer_initialized = false;
uv_timer_stop(&sock->timer);
uv_close((uv_handle_t *)&sock->timer, timer_close_cb);
} else {
/*
* At this point we're certain that there are no
* external references, we can close everything.
*/
if (sock->outerhandle != NULL) {
isc_nm_pauseread(sock->outerhandle);
isc_nmhandle_detach(&sock->outerhandle);
}
if (sock->listener != NULL) {
isc__nmsocket_detach(&sock->listener);
}
if (sock->tls.ssl != NULL) {
SSL_free(sock->tls.ssl);
sock->tls.ssl = NULL;
/* These are destroyed when we free SSL* */
sock->tls.ctx = NULL;
sock->tls.ssl_bio = NULL;
}
if (sock->tls.app_bio != NULL) {
BIO_free(sock->tls.app_bio);
sock->tls.app_bio = NULL;
}
atomic_store(&sock->closed, true);
isc__nmsocket_detach(&sock);
}
}
void
isc__nm_tls_close(isc_nmsocket_t *sock) {
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(sock->type == isc_nm_tlssocket);
if (!atomic_compare_exchange_strong(&sock->closing, &(bool){ false },
true)) {
return;
}
if (sock->tid == isc_nm_tid()) {
tls_close_direct(sock);
} else {
isc__netievent_tlsclose_t *ievent =
isc__nm_get_ievent(sock->mgr, netievent_tlsclose);
ievent->sock = sock;
isc__nm_enqueue_ievent(&sock->mgr->workers[sock->tid],
(isc__netievent_t *)ievent);
}
}
void
isc__nm_async_tlsclose(isc__networker_t *worker, isc__netievent_t *ev0) {
isc__netievent_tlsclose_t *ievent = (isc__netievent_tlsclose_t *)ev0;
REQUIRE(worker->id == ievent->sock->tid);
tls_close_direct(ievent->sock);
}
void
isc__nm_tls_stoplistening(isc_nmsocket_t *sock) {
REQUIRE(VALID_NMSOCK(sock));
REQUIRE(sock->type == isc_nm_tlslistener);
atomic_store(&sock->listening, false);
atomic_store(&sock->closed, true);
sock->recv_cb = NULL;
sock->recv_cbarg = NULL;
if (sock->tls.ssl != NULL) {
SSL_free(sock->tls.ssl);
sock->tls.ssl = NULL;
sock->tls.ctx = NULL;
}
if (sock->outer != NULL) {
isc_nm_stoplistening(sock->outer);
isc__nmsocket_detach(&sock->outer);
}
}
isc_result_t
isc_nm_tlsconnect(isc_nm_t *mgr, isc_nmiface_t *local, isc_nmiface_t *peer,
isc_nm_cb_t cb, void *cbarg, SSL_CTX *ctx,
unsigned int timeout, size_t extrahandlesize) {
isc_nmsocket_t *nsock = NULL, *tmp = NULL;
isc__netievent_tlsconnect_t *ievent = NULL;
isc_result_t result = ISC_R_SUCCESS;
REQUIRE(VALID_NM(mgr));
nsock = isc_mem_get(mgr->mctx, sizeof(*nsock));
isc__nmsocket_init(nsock, mgr, isc_nm_tlssocket, local);
nsock->extrahandlesize = extrahandlesize;
atomic_init(&nsock->result, ISC_R_SUCCESS);
nsock->connect_cb = cb;
nsock->connect_cbarg = cbarg;
nsock->connect_timeout = timeout;
nsock->tls.ctx = ctx;
/* We need to initialize SSL now to reference SSL_CTX properly
*/
nsock->tls.ssl = SSL_new(nsock->tls.ctx);
if (nsock->tls.ssl == NULL) {
atomic_store(&nsock->closed, true);
isc__nmsocket_detach(&nsock);
return (ISC_R_TLSERROR);
}
ievent = isc__nm_get_ievent(mgr, netievent_tlsconnect);
ievent->sock = nsock;
ievent->local = local->addr;
ievent->peer = peer->addr;
ievent->ctx = ctx;
/*
* Async callbacks can dereference the socket in the meantime,
* we need to hold an additional reference to it.
*/
isc__nmsocket_attach(nsock, &tmp);
if (isc__nm_in_netthread()) {
nsock->tid = isc_nm_tid();
isc__nm_async_tlsconnect(&mgr->workers[nsock->tid],
(isc__netievent_t *)ievent);
isc__nm_put_ievent(mgr, ievent);
} else {
nsock->tid = isc_random_uniform(mgr->nworkers);
isc__nm_enqueue_ievent(&mgr->workers[nsock->tid],
(isc__netievent_t *)ievent);
}
isc__nmsocket_detach(&tmp);
return (result);
}
static void
tls_connect_cb(isc_nmhandle_t *handle, isc_result_t result, void *cbarg) {
isc_nmsocket_t *tlssock = (isc_nmsocket_t *)cbarg;
REQUIRE(VALID_NMSOCK(tlssock));
if (result != ISC_R_SUCCESS) {
tlssock->connect_cb(handle, result, tlssock->connect_cbarg);
atomic_store(&tlssock->result, result);
atomic_store(&tlssock->connect_error, true);
tls_close_direct(tlssock);
return;
}
INSIST(VALID_NMHANDLE(handle));
tlssock->peer = isc_nmhandle_peeraddr(handle);
isc_nmhandle_attach(handle, &tlssock->outerhandle);
result = initialize_tls(tlssock, false);
if (result != ISC_R_SUCCESS) {
tlssock->connect_cb(handle, result, tlssock->connect_cbarg);
atomic_store(&tlssock->result, result);
atomic_store(&tlssock->connect_error, true);
tls_close_direct(tlssock);
return;
}
}
void
isc__nm_async_tlsconnect(isc__networker_t *worker, isc__netievent_t *ev0) {
isc__netievent_tlsconnect_t *ievent =
(isc__netievent_tlsconnect_t *)ev0;
isc_nmsocket_t *tlssock = ievent->sock;
isc_result_t result;
UNUSED(worker);
tlssock->tid = isc_nm_tid();
uv_timer_init(&tlssock->mgr->workers[isc_nm_tid()].loop,
&tlssock->timer);
tlssock->timer.data = tlssock;
tlssock->timer_initialized = true;
tlssock->tls.state = TLS_INIT;
result = isc_nm_tcpconnect(worker->mgr, (isc_nmiface_t *)&ievent->local,
(isc_nmiface_t *)&ievent->peer,
tls_connect_cb, tlssock,
tlssock->connect_timeout, 0);
if (result != ISC_R_SUCCESS) {
/* FIXME: We need to pass valid handle */
tlssock->connect_cb(NULL, result, tlssock->connect_cbarg);
atomic_store(&tlssock->result, result);
atomic_store(&tlssock->connect_error, true);
tls_close_direct(tlssock);
return;
}
}
void
isc__nm_async_tls_do_bio(isc__networker_t *worker, isc__netievent_t *ev0) {
UNUSED(worker);
isc__netievent_tlsdobio_t *ievent = (isc__netievent_tlsdobio_t *)ev0;
tls_do_bio(ievent->sock);
}
isc_result_t
isc_nm_tls_create_server_ctx(const char *keyfile, const char *certfile,
SSL_CTX **ctxp) {
INSIST(ctxp != NULL);
INSIST(*ctxp == NULL);
int rv;
unsigned long err;
bool ephemeral = (keyfile == NULL && certfile == NULL);
X509 *cert = NULL;
EVP_PKEY *pkey = NULL;
BIGNUM *bn = NULL;
RSA *rsa = NULL;
if (ephemeral) {
INSIST(keyfile == NULL);
INSIST(certfile == NULL);
} else {
INSIST(keyfile != NULL);
INSIST(certfile != NULL);
}
#ifdef HAVE_TLS_SERVER_METHOD
const SSL_METHOD *method = TLS_server_method();
#else
const SSL_METHOD *method = SSLv23_server_method();
#endif
SSL_CTX *ctx = SSL_CTX_new(method);
RUNTIME_CHECK(ctx != NULL);
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);
X509_gmtime_adj(X509_get_notBefore(cert), 0);
/*
* We set the vailidy for 10 years.
*/
X509_gmtime_adj(X509_get_notAfter(cert), 3650 * 24 * 3600);
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 {
rv = SSL_CTX_use_certificate_file(ctx, certfile,
SSL_FILETYPE_PEM);
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();
char errbuf[256];
ERR_error_string_n(err, errbuf, 256);
isc_log_write(isc_lctx, ISC_LOGCATEGORY_GENERAL, ISC_LOGMODULE_NETMGR,
ISC_LOG_ERROR, "Error initializing TLS context: %s",
errbuf);
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);
}
void
isc__nm_tls_initialize() {
#if OPENSSL_VERSION_NUMBER < 0x10100000L
SSL_library_init();
#else
OPENSSL_init_ssl(0, NULL);
#endif
}