diff options
| author | Garrett D'Amore <garrett@damore.org> | 2020-02-08 12:46:47 -0800 |
|---|---|---|
| committer | Garrett D'Amore <garrett@damore.org> | 2020-02-23 17:06:58 -0800 |
| commit | ee0b44406d2b658886760ea08c0af12781ab7e3a (patch) | |
| tree | 674d2d31df7a62c367c161261c942e96f7909166 /src/supplemental/tls/tls_common.c | |
| parent | 56bcc0310c4710bb21802719566926c2ccd2262a (diff) | |
| download | nng-ee0b44406d2b658886760ea08c0af12781ab7e3a.tar.gz nng-ee0b44406d2b658886760ea08c0af12781ab7e3a.tar.bz2 nng-ee0b44406d2b658886760ea08c0af12781ab7e3a.zip | |
fixes #1005 TLS 1.3 support
This introduces support for an external wolfSSL plugin, and generally
creates the framework for pluggable TLS implementations.
The wolfSSL engine is provided via an external module (git submodule),
available either under a GPLv3 license or a commercial license.
Diffstat (limited to 'src/supplemental/tls/tls_common.c')
| -rw-r--r-- | src/supplemental/tls/tls_common.c | 1216 |
1 files changed, 1139 insertions, 77 deletions
diff --git a/src/supplemental/tls/tls_common.c b/src/supplemental/tls/tls_common.c index 7cccc1e9..7d89ea21 100644 --- a/src/supplemental/tls/tls_common.c +++ b/src/supplemental/tls/tls_common.c @@ -9,22 +9,90 @@ // found online at https://opensource.org/licenses/MIT. // -#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include "core/nng_impl.h" -#include "core/tcp.h" -#include "supplemental/tls/tls_api.h" +#include <nng/supplemental/tls/engine.h> #include <nng/supplemental/tls/tls.h> +// NNG_TLS_MAX_SEND_SIZE limits the amount of data we will buffer for sending, +// exerting back-pressure if this size is exceeded. The 16K is aligned to the +// maximum TLS record size. +#ifndef NNG_TLS_MAX_SEND_SIZE +#define NNG_TLS_MAX_SEND_SIZE 16384 +#endif + +// NNG_TLS_MAX_RECV_SIZE limits the amount of data we will receive in a single +// operation. As we have to buffer data, this drives the size of our +// intermediary buffer. The 16K is aligned to the maximum TLS record size. +#ifndef NNG_TLX_MAX_RECV_SIZE +#define NNG_TLS_MAX_RECV_SIZE 16384 +#endif + // This file contains common code for TLS, and is only compiled if we // have TLS configured in the system. In particular, this provides the // parts of TLS support that are invariant relative to different TLS // libraries, such as dialer and listener support. +#ifdef NNG_SUPP_TLS + +static const nng_tls_engine *tls_engine; +static nni_mtx tls_engine_lock; + +struct nng_tls_config { + nng_tls_engine_config_ops ops; + const nng_tls_engine * engine; // store this so we can verify + nni_mtx lock; + int ref; + int busy; + size_t size; + + // ... engine config data follows +}; + +typedef struct { + nng_stream stream; + nng_tls_engine_conn_ops ops; + nng_tls_config * cfg; + const nng_tls_engine * engine; + size_t size; + nni_aio * user_aio; // user's aio for connect/accept + nni_aio conn_aio; // system aio for connect/accept + nni_mtx lock; + bool closed; + bool hs_done; + nni_list send_queue; + nni_list recv_queue; + nng_stream * tcp; // lower level stream + nni_aio tcp_send; // lower level send pending + nni_aio tcp_recv; // lower level recv pending + uint8_t * tcp_send_buf; + uint8_t * tcp_recv_buf; + size_t tcp_recv_len; + size_t tcp_recv_off; + bool tcp_recv_pend; + bool tcp_send_active; + size_t tcp_send_len; + size_t tcp_send_head; + size_t tcp_send_tail; + struct nni_reap_item reap; + + // ... engine connection data follows +} tls_conn; + +static void tls_tcp_send_cb(void *arg); +static void tls_tcp_recv_cb(void *arg); +static void tls_do_send(tls_conn *); +static void tls_do_recv(tls_conn *); +static void tls_tcp_send_start(tls_conn *); +static void tls_free(void *); +static int tls_alloc(tls_conn **, nng_tls_config *, nng_aio *); +static int tls_start(tls_conn *, nng_stream *); +static void tls_tcp_error(tls_conn *, int); + typedef struct { nng_stream_dialer ops; nng_stream_dialer *d; // underlying TCP dialer @@ -57,28 +125,26 @@ tls_dialer_free(void *arg) static void tls_conn_cb(void *arg) { - nng_stream * tls = arg; - nni_tls_common *com = arg; - nng_stream * tcp; - int rv; + tls_conn * conn = arg; + nng_stream *tcp; + int rv; - if ((rv = nni_aio_result(com->aio)) != 0) { - nni_aio_finish_error(com->uaio, rv); - nng_stream_free(tls); + if ((rv = nni_aio_result(&conn->conn_aio)) != 0) { + nni_aio_finish_error(conn->user_aio, rv); + nng_stream_free(&conn->stream); return; } - tcp = nni_aio_get_output(com->aio, 0); + tcp = nni_aio_get_output(&conn->conn_aio, 0); - if ((rv = nni_tls_start(tls, tcp)) != 0) { - nni_aio_finish_error(com->uaio, rv); - nng_stream_free(tcp); - nng_stream_free(tls); + if ((rv = tls_start(conn, tcp)) != 0) { + nni_aio_finish_error(conn->user_aio, rv); + nng_stream_free(&conn->stream); return; } - nni_aio_set_output(com->uaio, 0, tls); - nni_aio_finish(com->uaio, 0, 0); + nni_aio_set_output(conn->user_aio, 0, &conn->stream); + nni_aio_finish(conn->user_aio, 0, 0); } // Dialer cancel is called when the user has indicated that they no longer @@ -86,52 +152,35 @@ tls_conn_cb(void *arg) static void tls_conn_cancel(nni_aio *aio, void *arg, int rv) { - nni_tls_common *com = arg; - NNI_ASSERT(com->uaio == aio); + tls_conn *conn = arg; + NNI_ASSERT(conn->user_aio == aio); // Just pass this down. If the connection is already done, this // will have no effect. - nni_aio_abort(com->aio, rv); + nni_aio_abort(&conn->conn_aio, rv); } static void tls_dialer_dial(void *arg, nng_aio *aio) { - tls_dialer * d = arg; - int rv; - nng_stream * tls; - nni_tls_common *com; + tls_dialer *d = arg; + int rv; + tls_conn * conn; if (nni_aio_begin(aio) != 0) { return; } - if ((rv = nni_tls_alloc(&tls)) != 0) { + if ((rv = tls_alloc(&conn, d->cfg, aio)) != 0) { nni_aio_finish_error(aio, rv); return; } - com = (void *) tls; - if ((rv = nni_aio_alloc(&com->aio, tls_conn_cb, tls)) != 0) { + if ((rv = nni_aio_schedule(aio, tls_conn_cancel, conn)) != 0) { nni_aio_finish_error(aio, rv); - nng_stream_free(tls); + tls_free(conn); return; } - com->uaio = aio; - // Save a copy of the TLS configuration. This way we don't have - // to ensure that the dialer outlives the connection, because the - // only shared data is the configuration which is reference counted. - nni_mtx_lock(&d->lk); - com->cfg = d->cfg; - nng_tls_config_hold(com->cfg); - nni_mtx_unlock(&d->lk); - - if ((rv = nni_aio_schedule(aio, tls_conn_cancel, tls)) != 0) { - nni_aio_finish_error(aio, rv); - nng_stream_free(tls); - return; - } - - nng_stream_dialer_dial(d->d, com->aio); + nng_stream_dialer_dial(d->d, &conn->conn_aio); } static int @@ -307,10 +356,12 @@ nni_tls_dialer_alloc(nng_stream_dialer **dp, const nng_url *url) { tls_dialer *d; int rv; - nng_url myurl; + nng_url my_url; - memcpy(&myurl, url, sizeof(myurl)); - myurl.u_scheme = url->u_scheme + strlen("tls+"); + memcpy(&my_url, url, sizeof(my_url)); + if (strncmp(my_url.u_scheme, "tls+", 4) == 0) { + my_url.u_scheme += 4; + } if ((rv = nni_init()) != 0) { return (rv); @@ -320,7 +371,7 @@ nni_tls_dialer_alloc(nng_stream_dialer **dp, const nng_url *url) } nni_mtx_init(&d->lk); - if ((rv = nng_stream_dialer_alloc_url(&d->d, &myurl)) != 0) { + if ((rv = nng_stream_dialer_alloc_url(&d->d, &my_url)) != 0) { nni_mtx_fini(&d->lk); NNI_FREE_STRUCT(d); return (rv); @@ -381,41 +432,25 @@ tls_listener_listen(void *arg) static void tls_listener_accept(void *arg, nng_aio *aio) { - tls_listener * l = arg; - int rv; - nng_stream * tls; - nni_tls_common *com; + tls_listener *l = arg; + int rv; + tls_conn * conn; if (nni_aio_begin(aio) != 0) { return; } - if ((rv = nni_tls_alloc(&tls)) != 0) { + if ((rv = tls_alloc(&conn, l->cfg, aio)) != 0) { nni_aio_finish_error(aio, rv); return; } - com = (void *) tls; - if ((rv = nni_aio_alloc(&com->aio, tls_conn_cb, tls)) != 0) { - nni_aio_finish_error(aio, rv); - nng_stream_free(tls); - return; - } - com->uaio = aio; - - // Save a copy of the TLS configuration. This way we don't have - // to ensure that the dialer outlives the connection, because the - // only shared data is the configuration which is reference counted. - nni_mtx_lock(&l->lk); - com->cfg = l->cfg; - nng_tls_config_hold(com->cfg); - nni_mtx_unlock(&l->lk); - if ((rv = nni_aio_schedule(aio, tls_conn_cancel, tls)) != 0) { + if ((rv = nni_aio_schedule(aio, tls_conn_cancel, conn)) != 0) { nni_aio_finish_error(aio, rv); - nng_stream_free(tls); + tls_free(conn); return; } - nng_stream_listener_accept(l->l, com->aio); + nng_stream_listener_accept(l->l, &conn->conn_aio); } static int @@ -581,10 +616,13 @@ nni_tls_listener_alloc(nng_stream_listener **lp, const nng_url *url) { tls_listener *l; int rv; - nng_url myurl; + nng_url my_url; - memcpy(&myurl, url, sizeof(myurl)); - myurl.u_scheme = url->u_scheme + strlen("tls+"); + memcpy(&my_url, url, sizeof(my_url)); + + if (strncmp(my_url.u_scheme, "tls+", 4) == 0) { + my_url.u_scheme += 4; + } if ((rv = nni_init()) != 0) { return (rv); @@ -594,7 +632,7 @@ nni_tls_listener_alloc(nng_stream_listener **lp, const nng_url *url) } nni_mtx_init(&l->lk); - if ((rv = nng_stream_listener_alloc_url(&l->l, &myurl)) != 0) { + if ((rv = nng_stream_listener_alloc_url(&l->l, &my_url)) != 0) { nni_mtx_fini(&l->lk); NNI_FREE_STRUCT(l); return (rv); @@ -645,7 +683,7 @@ tls_check_auth_mode(const void *buf, size_t sz, nni_type t) return (rv); } -static const nni_chkoption tls_chkopts[] = { +static const nni_chkoption tls_check_opts[] = { { .o_name = NNG_OPT_TLS_CONFIG, .o_check = tls_check_config, @@ -676,9 +714,1033 @@ nni_tls_checkopt(const char *name, const void *data, size_t sz, nni_type t) { int rv; - rv = nni_chkopt(tls_chkopts, name, data, sz, t); + rv = nni_chkopt(tls_check_opts, name, data, sz, t); if (rv == NNG_ENOTSUP) { rv = nni_stream_checkopt("tcp", name, data, sz, t); } return (rv); } + +static void +tls_cancel(nni_aio *aio, void *arg, int rv) +{ + tls_conn *conn = arg; + nni_mtx_lock(&conn->lock); + if (aio == nni_list_first(&conn->recv_queue)) { + nni_aio_abort(&conn->tcp_recv, rv); + } else if (aio == nni_list_first(&conn->send_queue)) { + nni_aio_abort(&conn->tcp_send, rv); + } else if (nni_aio_list_active(aio)) { + nni_aio_list_remove(aio); + nni_aio_finish_error(aio, rv); + } + nni_mtx_unlock(&conn->lock); +} + +// tls_send implements the upper layer stream send operation. +static void +tls_send(void *arg, nni_aio *aio) +{ + int rv; + tls_conn *conn = arg; + + if (nni_aio_begin(aio) != 0) { + return; + } + nni_mtx_lock(&conn->lock); + if (conn->closed) { + nni_mtx_unlock(&conn->lock); + nni_aio_finish_error(aio, NNG_ECLOSED); + return; + } + if ((rv = nni_aio_schedule(aio, tls_cancel, conn)) != 0) { + nni_mtx_unlock(&conn->lock); + nni_aio_finish_error(aio, rv); + return; + } + nni_list_append(&conn->send_queue, aio); + tls_do_send(conn); + nni_mtx_unlock(&conn->lock); +} + +static void +tls_recv(void *arg, nni_aio *aio) +{ + int rv; + tls_conn *conn = arg; + + if (nni_aio_begin(aio) != 0) { + return; + } + nni_mtx_lock(&conn->lock); + if (conn->closed) { + nni_mtx_unlock(&conn->lock); + nni_aio_finish_error(aio, NNG_ECLOSED); + return; + } + if ((rv = nni_aio_schedule(aio, tls_cancel, conn)) != 0) { + nni_mtx_unlock(&conn->lock); + nni_aio_finish_error(aio, rv); + return; + } + + nni_list_append(&conn->recv_queue, aio); + tls_do_recv(conn); + nni_mtx_unlock(&conn->lock); +} + +static void +tls_close(void *arg) +{ + tls_conn *conn = arg; + + nni_mtx_lock(&conn->lock); + conn->ops.close((void *) (conn + 1)); + tls_tcp_error(conn, NNG_ECLOSED); + nni_mtx_unlock(&conn->lock); + nng_stream_close(conn->tcp); +} + +static int +tls_get_verified(void *arg, void *buf, size_t *szp, nni_type t) +{ + tls_conn *conn = arg; + bool v; + + nni_mtx_lock(&conn->lock); + v = conn->ops.verified((void *) (conn + 1)); + nni_mtx_unlock(&conn->lock); + return (nni_copyout_bool(v, buf, szp, t)); +} + +static const nni_option tls_options[] = { + { + .o_name = NNG_OPT_TLS_VERIFIED, + .o_get = tls_get_verified, + }, + { + .o_name = NULL, + }, +}; + +static int +tls_setx(void *arg, const char *name, const void *buf, size_t sz, nni_type t) +{ + tls_conn * conn = arg; + int rv; + nng_stream *tcp; + + tcp = (conn != NULL) ? conn->tcp : NULL; + + if ((rv = nni_stream_setx(tcp, name, buf, sz, t)) != NNG_ENOTSUP) { + return (rv); + } + return (nni_setopt(tls_options, name, conn, buf, sz, t)); +} + +static int +tls_getx(void *arg, const char *name, void *buf, size_t *szp, nni_type t) +{ + tls_conn *conn = arg; + int rv; + + if ((rv = nni_stream_getx(conn->tcp, name, buf, szp, t)) != + NNG_ENOTSUP) { + return (rv); + } + return (nni_getopt(tls_options, name, conn, buf, szp, t)); +} + +static int +tls_alloc(tls_conn **conn_p, nng_tls_config *cfg, nng_aio *user_aio) +{ + tls_conn * conn; + const nng_tls_engine *eng; + size_t size; + + eng = cfg->engine; + + size = NNI_ALIGN_UP(sizeof(*conn)) + eng->conn_ops->size; + + if ((conn = nni_zalloc(size)) == NULL) { + return (NNG_ENOMEM); + } + if (((conn->tcp_send_buf = nni_alloc(NNG_TLS_MAX_SEND_SIZE)) == + NULL) || + ((conn->tcp_recv_buf = nni_alloc(NNG_TLS_MAX_RECV_SIZE)) == + NULL)) { + tls_free(conn); + return (NNG_ENOMEM); + } + conn->size = size; + conn->ops = *eng->conn_ops; + conn->engine = eng; + conn->user_aio = user_aio; + conn->cfg = cfg; + + nni_aio_init(&conn->conn_aio, tls_conn_cb, conn); + nni_aio_init(&conn->tcp_recv, tls_tcp_recv_cb, conn); + nni_aio_init(&conn->tcp_send, tls_tcp_send_cb, conn); + nni_aio_list_init(&conn->send_queue); + nni_aio_list_init(&conn->recv_queue); + nni_mtx_init(&conn->lock); + nni_aio_set_timeout(&conn->tcp_send, NNG_DURATION_INFINITE); + nni_aio_set_timeout(&conn->tcp_recv, NNG_DURATION_INFINITE); + + conn->stream.s_close = tls_close; + conn->stream.s_free = tls_free; + conn->stream.s_send = tls_send; + conn->stream.s_recv = tls_recv; + conn->stream.s_getx = tls_getx; + conn->stream.s_setx = tls_setx; + + nng_tls_config_hold(cfg); + *conn_p = conn; + return (0); +} + +static void +tls_reap(void *arg) +{ + tls_conn *conn = arg; + + // Shut it all down first. We should be freed. + if (conn->tcp != NULL) { + nng_stream_close(conn->tcp); + } + nni_aio_stop(&conn->conn_aio); + nni_aio_stop(&conn->tcp_send); + nni_aio_stop(&conn->tcp_recv); + + conn->ops.fini((void *) (conn + 1)); + nni_aio_fini(&conn->conn_aio); + nni_aio_fini(&conn->tcp_send); + nni_aio_fini(&conn->tcp_recv); + nng_stream_free(conn->tcp); + if (conn->cfg != NULL) { + nng_tls_config_free(conn->cfg); // this drops our hold on it + } + if (conn->tcp_send_buf != NULL) { + nni_free(conn->tcp_send_buf, NNG_TLS_MAX_SEND_SIZE); + } + if (conn->tcp_recv_buf != NULL) { + nni_free(conn->tcp_recv_buf, NNG_TLS_MAX_RECV_SIZE); + } + NNI_FREE_STRUCT(conn); +} + +static void +tls_free(void *arg) +{ + tls_conn *conn = arg; + + nni_reap(&conn->reap, tls_reap, conn); +} + +static int +tls_start(tls_conn *conn, nng_stream *tcp) +{ + int rv; + + conn->tcp = tcp; + rv = conn->ops.init( + (void *) (conn + 1), conn, (void *) (conn->cfg + 1)); + return (rv); +} + +static void +tls_tcp_error(tls_conn *conn, int rv) +{ + // An error here is fatal. Shut it all down. + nni_aio *aio; + nng_stream_close(conn->tcp); + nni_aio_close(&conn->tcp_send); + nni_aio_close(&conn->tcp_recv); + while (((aio = nni_list_first(&conn->send_queue)) != NULL) || + ((aio = nni_list_first(&conn->recv_queue)) != NULL)) { + nni_aio_list_remove(aio); + nni_aio_finish_error(aio, rv); + } +} + +static bool +tls_do_handshake(tls_conn *conn) +{ + int rv; + if (conn->hs_done) { + return (true); + } + rv = conn->ops.handshake((void *) (conn + 1)); + if (rv == NNG_EAGAIN) { + // We need more data. + return (false); + } + if (rv == 0) { + conn->hs_done = true; + return (true); + } + tls_tcp_error(conn, rv); + return (true); +} + +static void +tls_do_recv(tls_conn *conn) +{ + nni_aio *aio; + + while ((aio = nni_list_first(&conn->recv_queue)) != NULL) { + uint8_t *buf = NULL; + size_t len = 0; + nni_iov *iov; + unsigned nio; + int rv; + + nni_aio_get_iov(aio, &nio, &iov); + + for (unsigned i = 0; i < nio; i++) { + if (iov[i].iov_len != 0) { + buf = iov[i].iov_buf; + len = iov[i].iov_len; + break; + } + } + if (len == 0 || buf == NULL) { + // Caller has asked to receive "nothing". + nni_aio_list_remove(aio); + nni_aio_finish_error(aio, NNG_EINVAL); + continue; + } + + rv = conn->ops.recv((void *) (conn + 1), buf, &len); + if (rv == NNG_EAGAIN) { + // Nothing more we can do, the engine doesn't + // have anything else for us (yet). + return; + } + + // Unlike the send side, we want to return back to the + // caller as *soon* as we have some data. + nni_aio_list_remove(aio); + + if (rv != 0) { + nni_aio_finish_error(aio, rv); + } else { + nni_aio_finish(aio, 0, len); + } + } +} + +// tls_do_send attempts to send user data. +static void +tls_do_send(tls_conn *conn) +{ + nni_aio *aio; + + while ((aio = nni_list_first(&conn->send_queue)) != NULL) { + uint8_t *buf = NULL; + size_t len = 0; + nni_iov *iov; + unsigned nio; + int rv; + + nni_aio_get_iov(aio, &nio, &iov); + + for (unsigned i = 0; i < nio; i++) { + if (iov[i].iov_len != 0) { + buf = iov[i].iov_buf; + len = iov[i].iov_len; + break; + } + } + if (len == 0 || buf == NULL) { + nni_aio_list_remove(aio); + // Presumably this means we've completed this + // one, lets preserve the count, and move to the + // next. + nni_aio_finish(aio, 0, nni_aio_count(aio)); + continue; + } + + // Ask the engine to send. + rv = conn->ops.send((void *) (conn + 1), buf, &len); + if (rv == NNG_EAGAIN) { + // Can't send any more, wait for callback. + return; + } + + if (rv != 0) { + nni_aio_list_remove(aio); + nni_aio_finish_error(aio, rv); + } else { + nni_aio_list_remove(aio); + nni_aio_finish(aio, 0, len); + } + } +} + +static void +tls_tcp_send_cb(void *arg) +{ + tls_conn *conn = arg; + nng_aio * aio = &conn->tcp_send; + int rv; + size_t count; + + nni_mtx_lock(&conn->lock); + conn->tcp_send_active = false; + + if ((rv = nni_aio_result(aio)) != 0) { + tls_tcp_error(conn, rv); + nni_mtx_unlock(&conn->lock); + return; + } + + count = nni_aio_count(aio); + NNI_ASSERT(count <= conn->tcp_send_len); + conn->tcp_send_len -= count; + tls_tcp_send_start(conn); + + if (tls_do_handshake(conn)) { + tls_do_send(conn); + tls_do_recv(conn); + } + + nni_mtx_unlock(&conn->lock); +} + +static void +tls_tcp_recv_cb(void *arg) +{ + tls_conn *conn = arg; + nni_aio * aio = &conn->tcp_recv; + int rv; + + nni_mtx_lock(&conn->lock); + + conn->tcp_recv_pend = false; + if ((rv = nni_aio_result(aio)) != 0) { + tls_tcp_error(conn, rv); + nni_mtx_unlock(&conn->lock); + return; + } + + NNI_ASSERT(conn->tcp_recv_len == 0); + NNI_ASSERT(conn->tcp_recv_off == 0); + conn->tcp_recv_len = nni_aio_count(aio); + + if (tls_do_handshake(conn)) { + tls_do_recv(conn); + tls_do_send(conn); + } + + nni_mtx_unlock(&conn->lock); +} + +static void +tls_tcp_recv_start(tls_conn *conn) +{ + nng_iov iov; + + if (conn->tcp_recv_len != 0) { + // We already have data in the buffer. + return; + } + if (conn->tcp_recv_pend) { + // Already have a receive in flight. + return; + } + conn->tcp_recv_off = 0; + iov.iov_len = NNG_TLS_MAX_RECV_SIZE; + iov.iov_buf = conn->tcp_recv_buf; + + conn->tcp_recv_pend = true; + nng_aio_set_iov(&conn->tcp_recv, 1, &iov); + + nng_stream_recv(conn->tcp, &conn->tcp_recv); +} + +static void +tls_tcp_send_start(tls_conn *conn) +{ + nni_iov iov[2]; + unsigned nio = 0; + size_t len; + size_t tail; + size_t head; + + if (conn->tcp_send_active) { + return; + } + if (conn->tcp_send_len == 0) { + return; + } + len = conn->tcp_send_len; + head = conn->tcp_send_head; + tail = conn->tcp_send_tail; + + while (len > 0) { + size_t cnt; + if (tail < head) { + cnt = head - tail; + } else { + cnt = NNG_TLS_MAX_SEND_SIZE - tail; + } + if (cnt > len) { + cnt = len; + } + iov[nio].iov_buf = conn->tcp_send_buf + tail; + iov[nio].iov_len = cnt; + len -= cnt; + tail += cnt; + tail %= NNG_TLS_MAX_SEND_SIZE; + nio++; + } + conn->tcp_send_active = true; + conn->tcp_send_tail = tail; + nni_aio_set_iov(&conn->tcp_send, nio, iov); + nng_stream_send(conn->tcp, &conn->tcp_send); +} + +int +nng_tls_engine_send(void *arg, const uint8_t *buf, size_t *szp) +{ + tls_conn *conn = arg; + size_t len = *szp; + size_t head = conn->tcp_send_head; + size_t tail = conn->tcp_send_tail; + size_t space; + size_t cnt; + + space = NNG_TLS_MAX_SEND_SIZE - conn->tcp_send_len; + + if (space == 0) { + return (NNG_EAGAIN); + } + + if (conn->closed) { + return (NNG_ECLOSED); + } + + if (len > space) { + len = space; + } + + // We are committed at this point to sending out len bytes. + // Update this now, so that we can use len to update. + *szp = len; + + while (len > 0) { + if (head >= tail) { + cnt = NNG_TLS_MAX_SEND_SIZE - head; + } else { + cnt = tail - head; + } + if (cnt > len) { + cnt = len; + } + + memcpy(conn->tcp_send_buf + head, buf, cnt); + buf += cnt; + head += cnt; + head %= NNG_TLS_MAX_SEND_SIZE; + conn->tcp_send_len += cnt; + conn->tcp_send_head = head; + len -= cnt; + } + + tls_tcp_send_start(conn); + return (0); +} + +int +nng_tls_engine_recv(void *arg, uint8_t *buf, size_t *szp) +{ + tls_conn *conn = arg; + size_t len = *szp; + + if (conn->closed) { + return (NNG_ECLOSED); + } + if (conn->tcp_recv_len == 0) { + tls_tcp_recv_start(conn); + return (NNG_EAGAIN); + } + if (len > conn->tcp_recv_len) { + len = conn->tcp_recv_len; + } + memcpy(buf, conn->tcp_recv_buf + conn->tcp_recv_off, len); + conn->tcp_recv_off += len; + conn->tcp_recv_len -= len; + + // If we still have data left in the buffer, then the following + // call is a no-op. + tls_tcp_recv_start(conn); + + *szp = len; + return (0); +} + +int +nng_tls_config_cert_key_file( + nng_tls_config *cfg, const char *path, const char *pass) +{ + int rv; + void * data; + size_t size; + char * pem; + + if ((rv = nni_file_get(path, &data, &size)) != 0) { + return (rv); + } + if ((pem = nni_zalloc(size + 1)) == NULL) { + nni_free(data, size); + return (NNG_ENOMEM); + } + memcpy(pem, data, size); + nni_free(data, size); + rv = nng_tls_config_own_cert(cfg, pem, pem, pass); + nni_free(pem, size + 1); + return (rv); +} + +int +nng_tls_config_ca_file(nng_tls_config *cfg, const char *path) +{ + int rv; + void * data; + size_t size; + char * pem; + + if ((rv = nni_file_get(path, &data, &size)) != 0) { + return (rv); + } + if ((pem = nni_zalloc(size + 1)) == NULL) { + nni_free(data, size); + return (NNG_ENOMEM); + } + memcpy(pem, data, size); + nni_free(data, size); + if (strstr(pem, "-----BEGIN X509 CRL-----") != NULL) { + rv = nng_tls_config_ca_chain(cfg, pem, pem); + } else { + rv = nng_tls_config_ca_chain(cfg, pem, NULL); + } + nni_free(pem, size + 1); + return (rv); +} + +int +nng_tls_config_version( + nng_tls_config *cfg, nng_tls_version min_ver, nng_tls_version max_ver) +{ + int rv; + + nni_mtx_lock(&cfg->lock); + if (cfg->busy != 0) { + rv = NNG_EBUSY; + } else { + rv = cfg->ops.version((void *) (cfg + 1), min_ver, max_ver); + } + nni_mtx_unlock(&cfg->lock); + return (rv); +} + +int +nng_tls_config_server_name(nng_tls_config *cfg, const char *name) +{ + int rv; + + nni_mtx_lock(&cfg->lock); + if (cfg->busy != 0) { + rv = NNG_EBUSY; + } else { + rv = cfg->ops.server((void *) (cfg + 1), name); + } + nni_mtx_unlock(&cfg->lock); + return (rv); +} + +int +nng_tls_config_ca_chain( + nng_tls_config *cfg, const char *certs, const char *crl) +{ + int rv; + + nni_mtx_lock(&cfg->lock); + if (cfg->busy != 0) { + rv = NNG_EBUSY; + } else { + rv = cfg->ops.ca_chain((void *) (cfg + 1), certs, crl); + } + nni_mtx_unlock(&cfg->lock); + return (rv); +} + +int +nng_tls_config_own_cert( + nng_tls_config *cfg, const char *cert, const char *key, const char *pass) +{ + int rv; + nni_mtx_lock(&cfg->lock); + if (cfg->busy != 0) { + rv = NNG_EBUSY; + } else { + rv = cfg->ops.own_cert((void *) (cfg + 1), cert, key, pass); + } + nni_mtx_unlock(&cfg->lock); + return (rv); +} + +int +nng_tls_config_auth_mode(nng_tls_config *cfg, nng_tls_auth_mode mode) +{ + int rv; + + nni_mtx_lock(&cfg->lock); + if (cfg->busy != 0) { + rv = NNG_EBUSY; + } else { + rv = cfg->ops.auth((void *) (cfg + 1), mode); + } + nni_mtx_unlock(&cfg->lock); + return (rv); +} + +int +nng_tls_config_alloc(nng_tls_config **cfg_p, nng_tls_mode mode) +{ + nng_tls_config * cfg; + const nng_tls_engine *eng; + size_t size; + int rv; + + if ((rv = nni_init()) != 0) { + return (rv); + } + + nni_mtx_lock(&tls_engine_lock); + eng = tls_engine; + nni_mtx_unlock(&tls_engine_lock); + + if (eng == NULL) { + return (NNG_ENOTSUP); + } + + size = NNI_ALIGN_UP(sizeof(*cfg) + eng->config_ops->size); + + if ((cfg = nni_zalloc(size)) == NULL) { + return (NNG_ENOMEM); + } + + cfg->ops = *eng->config_ops; + cfg->size = size; + cfg->engine = eng; + cfg->ref = 1; + cfg->busy = 0; + nni_mtx_init(&cfg->lock); + + if ((rv = cfg->ops.init((void *) (cfg + 1), mode)) != 0) { + nni_free(cfg, cfg->size); + return (rv); + } + *cfg_p = cfg; + return (0); +} + +void +nng_tls_config_free(nng_tls_config *cfg) +{ + nni_mtx_lock(&cfg->lock); + cfg->ref--; + if (cfg->ref != 0) { + nni_mtx_unlock(&cfg->lock); + return; + } + nni_mtx_unlock(&cfg->lock); + nni_mtx_fini(&cfg->lock); + cfg->ops.fini((void *) (cfg + 1)); + nni_free(cfg, cfg->size); +} + +void +nng_tls_config_hold(nng_tls_config *cfg) +{ + nni_mtx_lock(&cfg->lock); + cfg->ref++; + nni_mtx_unlock(&cfg->lock); +} + +const char * +nng_tls_engine_name(void) +{ + const nng_tls_engine *eng; + + nni_init(); + nni_mtx_lock(&tls_engine_lock); + eng = tls_engine; + nni_mtx_unlock(&tls_engine_lock); + + return (eng == NULL ? "none" : eng->name); +} + +const char * +nng_tls_engine_description(void) +{ + const nng_tls_engine *eng; + + nni_init(); + nni_mtx_lock(&tls_engine_lock); + eng = tls_engine; + nni_mtx_unlock(&tls_engine_lock); + + return (eng == NULL ? "" : eng->description); +} + +bool +nng_tls_engine_fips_mode(void) +{ + const nng_tls_engine *eng; + + nni_init(); + nni_mtx_lock(&tls_engine_lock); + eng = tls_engine; + nni_mtx_unlock(&tls_engine_lock); + + return (eng == NULL ? false : eng->fips_mode); +} + +int +nng_tls_engine_register(const nng_tls_engine *engine) +{ + if (engine->version != NNG_TLS_ENGINE_VERSION) { + return (NNG_ENOTSUP); + } + nni_mtx_lock(&tls_engine_lock); + tls_engine = engine; + nni_mtx_unlock(&tls_engine_lock); + return (0); +} + +#ifdef NNG_TLS_ENGINE_INIT +extern int NNG_TLS_ENGINE_INIT(void); +#else +static int +NNI_TLS_ENGINE_INIT(void) +{ + return (0); +} +#endif + +#ifdef NNG_TLS_ENGINE_FINI +extern void NNG_TLS_ENGINE_FINI(void); +#else +static void +NNG_TLS_ENGINE_FINI(void) +{ +} +#endif + +int +nni_tls_sys_init(void) +{ + int rv; + nni_mtx_init(&tls_engine_lock); + tls_engine = NULL; + + rv = NNG_TLS_ENGINE_INIT(); + if (rv != 0) { + nni_mtx_fini(&tls_engine_lock); + return (rv); + } + return (0); +} + +void +nni_tls_sys_fini(void) +{ + NNG_TLS_ENGINE_FINI(); +} + +#else // NNG_SUPP_TLS + +// Provide stubs for the case where TLS is not enabled. +void +nni_tls_config_fini(nng_tls_config *cfg) +{ + NNI_ARG_UNUSED(cfg); +} + +int +nni_tls_config_init(nng_tls_config **cpp, enum nng_tls_mode mode) +{ + NNI_ARG_UNUSED(cpp); + NNI_ARG_UNUSED(mode); + return (NNG_ENOTSUP); +} + +void +nni_tls_config_hold(nng_tls_config *cfg) +{ + NNI_ARG_UNUSED(cfg); +} + +int +nng_tls_config_server_name(nng_tls_config *cfg, const char *name) +{ + NNI_ARG_UNUSED(cfg); + NNI_ARG_UNUSED(name); + return (NNG_ENOTSUP); +} + +int +nng_tls_config_auth_mode(nng_tls_config *cfg, nng_tls_auth_mode mode) +{ + NNI_ARG_UNUSED(cfg); + NNI_ARG_UNUSED(mode); + return (NNG_ENOTSUP); +} + +int +nng_tls_config_ca_chain( + nng_tls_config *cfg, const char *certs, const char *crl) +{ + NNI_ARG_UNUSED(cfg); + NNI_ARG_UNUSED(certs); + NNI_ARG_UNUSED(crl); + return (NNG_ENOTSUP); +} + +int +nng_tls_config_own_cert( + nng_tls_config *cfg, const char *cert, const char *key, const char *pass) +{ + NNI_ARG_UNUSED(cfg); + NNI_ARG_UNUSED(cert); + NNI_ARG_UNUSED(key); + NNI_ARG_UNUSED(pass); + return (NNG_ENOTSUP); +} + +int +nng_tls_config_ca_file(nng_tls_config *cfg, const char *path) +{ + NNI_ARG_UNUSED(cfg); + NNI_ARG_UNUSED(path); + return (NNG_ENOTSUP); +} + +int +nng_tls_config_cert_key_file( + nng_tls_config *cfg, const char *path, const char *pass) +{ + NNI_ARG_UNUSED(cfg); + NNI_ARG_UNUSED(path); + NNI_ARG_UNUSED(pass); + return (NNG_ENOTSUP); +} + +int +nng_tls_config_key(nng_tls_config *cfg, const uint8_t *key, size_t size) +{ + NNI_ARG_UNUSED(cfg); + NNI_ARG_UNUSED(key); + NNI_ARG_UNUSED(size); + return (NNG_ENOTSUP); +} + +int +nng_tls_config_pass(nng_tls_config *cfg, const char *pass) +{ + NNI_ARG_UNUSED(cfg); + NNI_ARG_UNUSED(pass); + return (NNG_ENOTSUP); +} + +int +nng_tls_config_alloc(nng_tls_config **cfgp, nng_tls_mode mode) +{ + + NNI_ARG_UNUSED(cfgp); + NNI_ARG_UNUSED(mode); + return (NNG_ENOTSUP); +} + +void +nng_tls_config_free(nng_tls_config *cfg) +{ + NNI_ARG_UNUSED(cfg); +} + +int +nng_tls_config_version( + nng_tls_config *cfg, nng_tls_version min_ver, nng_tls_version max_ver) +{ + NNI_ARG_UNUSED(cfg); + NNI_ARG_UNUSED(min_ver); + NNI_ARG_UNUSED(max_ver); + return (NNG_ENOTSUP); +} + +int +nni_tls_dialer_alloc(nng_stream_dialer **dp, const nng_url *url) +{ + NNI_ARG_UNUSED(dp); + NNI_ARG_UNUSED(url); + return (NNG_ENOTSUP); +} + +int +nni_tls_listener_alloc(nng_stream_listener **lp, const nng_url *url) +{ + NNI_ARG_UNUSED(lp); + NNI_ARG_UNUSED(url); + return (NNG_ENOTSUP); +} + +int +nni_tls_checkopt(const char *nm, const void *buf, size_t sz, nni_type t) +{ + NNI_ARG_UNUSED(nm); + NNI_ARG_UNUSED(buf); + NNI_ARG_UNUSED(sz); + NNI_ARG_UNUSED(t); + return (NNG_ENOTSUP); +} + +const char * +nng_tls_engine_name(void) +{ + return ("none"); +} + +const char * +nng_tls_engine_description(void) +{ + return (""); +} + +bool +nng_tls_engine_fips_mode(void) +{ + return (false); +} + +int +nng_tls_engine_register(const nng_tls_engine *engine) +{ + NNI_ARG_UNUSED(engine); + return (NNG_ENOTSUP); +} + +int +nni_tls_sys_init(void) +{ + return (0); +} + +void +nni_tls_sys_fini(void) +{ +} + +#endif // !NNG_SUPP_TLS
\ No newline at end of file |
