From 8bcb82d245a5fce1bd519e2f99250dedf11e763d Mon Sep 17 00:00:00 2001 From: Garrett D'Amore Date: Sun, 27 Apr 2025 18:40:40 -0700 Subject: Introduce DTLS transport for NNG. This introduces a new experimental transport for DTLS, that provides encryption over UDP. It has a simpler protocol than the current UDP SP protocol (but we intend to fix that by making the UDP transport simpler in a follow up!) There are a few other fixes in the TLS layer itself, and in the build, that were needed to accomplish this work. Also there was an endianness bug in the UDP protocol handling, which is fixed here. --- src/supplemental/http/http_server.c | 8 +- src/supplemental/tls/CMakeLists.txt | 6 +- src/supplemental/tls/mbedtls/CMakeLists.txt | 2 +- src/supplemental/tls/mbedtls/mbedtls.c | 873 ++++++++++++++++++++++++++++ src/supplemental/tls/mbedtls/tls.c | 819 -------------------------- src/supplemental/tls/tls_common.c | 212 ++++--- src/supplemental/tls/tls_common.h | 31 +- src/supplemental/tls/tls_engine.h | 9 +- src/supplemental/tls/tls_stream.c | 25 +- src/supplemental/tls/tls_stream.h | 2 +- src/supplemental/tls/wolfssl/wolfssl.c | 4 +- 11 files changed, 1059 insertions(+), 932 deletions(-) create mode 100644 src/supplemental/tls/mbedtls/mbedtls.c delete mode 100644 src/supplemental/tls/mbedtls/tls.c (limited to 'src/supplemental') diff --git a/src/supplemental/http/http_server.c b/src/supplemental/http/http_server.c index 53da78c2..2273e3be 100644 --- a/src/supplemental/http/http_server.c +++ b/src/supplemental/http/http_server.c @@ -99,9 +99,15 @@ static nni_reap_list http_sc_reap_list = { static void http_server_fini(nni_http_server *); +static void +http_server_fini_cb(void *arg) +{ + http_server_fini((nni_http_server *) arg); +} + static nni_reap_list http_server_reap_list = { .rl_offset = offsetof(nni_http_server, reap), - .rl_func = (nni_cb) http_server_fini, + .rl_func = http_server_fini_cb, }; nng_err diff --git a/src/supplemental/tls/CMakeLists.txt b/src/supplemental/tls/CMakeLists.txt index 400b1354..41587915 100644 --- a/src/supplemental/tls/CMakeLists.txt +++ b/src/supplemental/tls/CMakeLists.txt @@ -12,8 +12,10 @@ # if (NNG_ENABLE_TLS) + # List of TLS engines we support. TLS engines must support TLS 1.2 or better, + # and must also support DTLS. Support for PSK is preferred. set(NNG_TLS_ENGINES mbed wolf none) - # We assume Mbed for now. (Someday replaced perhaps with Bear.) + # We assume Mbed for now. set(NNG_TLS_ENGINE mbed CACHE STRING "TLS engine to use.") set_property(CACHE NNG_TLS_ENGINE PROPERTY STRINGS ${NNG_TLS_ENGINES}) else () @@ -29,7 +31,7 @@ add_subdirectory(wolfssl) if (NNG_ENABLE_TLS) nng_sources(tls_common.c tls_dialer.c tls_listener.c tls_stream.c) - nng_sources(tls_api.h tls_engine.h) + nng_sources(tls_api.h tls_common.h tls_engine.h tls_stream.h) else() nng_sources(tls_stubs.c) endif() diff --git a/src/supplemental/tls/mbedtls/CMakeLists.txt b/src/supplemental/tls/mbedtls/CMakeLists.txt index acf852bd..466f0a1b 100644 --- a/src/supplemental/tls/mbedtls/CMakeLists.txt +++ b/src/supplemental/tls/mbedtls/CMakeLists.txt @@ -13,7 +13,7 @@ if (NNG_TLS_ENGINE STREQUAL "mbed") Linking against Mbed TLS may change license terms. Consult a lawyer and the license files for details. ************************************************************") - nng_sources(tls.c) + nng_sources(mbedtls.c) nng_defines(NNG_TLS_ENGINE_INIT=nng_tls_engine_init_mbed) nng_defines(NNG_TLS_ENGINE_FINI=nng_tls_engine_fini_mbed) nng_defines(NNG_SUPP_TLS) diff --git a/src/supplemental/tls/mbedtls/mbedtls.c b/src/supplemental/tls/mbedtls/mbedtls.c new file mode 100644 index 00000000..8250740f --- /dev/null +++ b/src/supplemental/tls/mbedtls/mbedtls.c @@ -0,0 +1,873 @@ +// +// Copyright 2025 Staysail Systems, Inc. +// Copyright 2018 Capitar IT Group BV +// Copyright 2019 Devolutions +// +// This software is supplied under the terms of the MIT License, a +// copy of which should be located in the distribution where this +// file was obtained (LICENSE.txt). A copy of the license may also be +// found online at https://opensource.org/licenses/MIT. +// + +#include +#include +#include +#include + +#include "mbedtls/version.h" // Must be first in order to pick up version + +#include "mbedtls/error.h" +#ifdef MBEDTLS_PSA_CRYPTO_C +#include "psa/crypto.h" +#endif + +#include "nng/nng.h" + +// We use a common cookie for our application. +#include "mbedtls/ssl_cookie.h" + +#include "../tls_engine.h" + +// mbedTLS renamed this header for 2.4.0. +#if MBEDTLS_VERSION_MAJOR > 2 || MBEDTLS_VERSION_MINOR >= 4 +#include "mbedtls/net_sockets.h" +#else +#include "mbedtls/net.h" +#endif + +#include "mbedtls/debug.h" +#include "mbedtls/ssl.h" + +#include "core/nng_impl.h" + +// pair holds a private key and the associated certificate. +typedef struct { + mbedtls_x509_crt crt; + mbedtls_pk_context key; + nni_list_node node; +} pair; + +// psk holds an identity and preshared key +typedef struct { + // NB: Technically RFC 4279 requires this be UTF-8 string, although + // others suggest making it opaque bytes. We treat it as a C string, + // so we cannot support embedded zero bytes. + char *identity; + uint8_t *key; + size_t keylen; + nni_list_node node; +} psk; + +static void +psk_free(psk *p) +{ + if (p != NULL) { + NNI_ASSERT(!nni_list_node_active(&p->node)); + if (p->identity != NULL) { + nni_strfree(p->identity); + p->identity = NULL; + } + if (p->key != NULL && p->keylen != 0) { + nni_free(p->key, p->keylen); + p->key = NULL; + p->keylen = 0; + } + NNI_FREE_STRUCT(p); + } +} + +#ifdef NNG_TLS_USE_CTR_DRBG +// Use a global RNG if we're going to override the builtin. +static mbedtls_ctr_drbg_context rng_ctx; +static nni_mtx rng_lock; +#endif + +struct nng_tls_engine_conn { + void *tls; // parent conn + mbedtls_ssl_context ctx; + nng_time exp1; + nng_time exp2; +}; + +struct nng_tls_engine_config { + mbedtls_ssl_config cfg_ctx; + char *server_name; + mbedtls_x509_crt ca_certs; + mbedtls_x509_crl crl; + int min_ver; + int max_ver; + nng_tls_mode mode; + nni_list pairs; + nni_list psks; +}; + +static mbedtls_ssl_cookie_ctx mbed_ssl_cookie_ctx; + +static void +tls_dbg(void *ctx, int level, const char *file, int line, const char *s) +{ + NNI_ARG_UNUSED(ctx); + NNI_ARG_UNUSED(level); + const char *f; + while ((f = strchr(file, '/')) != NULL) { + file = f + 1; + } + nng_log_debug("MBED", "%s: %d: %s", file, line, s); +} + +static int +tls_get_entropy(void *arg, unsigned char *buf, size_t len) +{ + NNI_ARG_UNUSED(arg); + while (len) { + uint32_t x = nni_random(); + size_t n; + + n = len < sizeof(x) ? len : sizeof(x); + memcpy(buf, &x, n); + len -= n; + buf += n; + } + return (0); +} + +static int +tls_random(void *arg, unsigned char *buf, size_t sz) +{ +#ifdef NNG_TLS_USE_CTR_DRBG + int rv; + + nni_mtx_lock(&rng_lock); + rv = mbedtls_ctr_drbg_random(&rng_ctx, buf, sz); + nni_mtx_unlock(&rng_lock); + return (rv); +#else + return (tls_get_entropy(arg, buf, sz)); +#endif +} + +static void +tls_log_err(const char *msgid, const char *context, int errnum) +{ + char errbuf[256]; + mbedtls_strerror(errnum, errbuf, sizeof(errbuf)); + nng_log_err(msgid, "%s: %s", context, errbuf); +} + +static void +tls_log_warn(const char *msgid, const char *context, int errnum) +{ + char errbuf[256]; + mbedtls_strerror(errnum, errbuf, sizeof(errbuf)); + nng_log_warn(msgid, "%s: %d - %s", context, errnum, errbuf); +} + +// tls_mk_err converts an mbed error to an NNG error. +static struct { + int tls; + int nng; +} tls_errs[] = { +#ifdef MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE + { MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE, NNG_EPEERAUTH }, +#endif +#ifdef MBEDTLS_ERR_SSL_CA_CHAIN_REQUIRED + { MBEDTLS_ERR_SSL_CA_CHAIN_REQUIRED, NNG_EPEERAUTH }, +#endif +#ifdef MBEDTLS_ERR_SSL_PEER_VERIFY_FAILED + { MBEDTLS_ERR_SSL_PEER_VERIFY_FAILED, NNG_EPEERAUTH }, +#endif +#ifdef MBEDTLS_ERR_SSL_NO_USABLE_CIPHERSUITE + { MBEDTLS_ERR_SSL_NO_USABLE_CIPHERSUITE, NNG_EPEERAUTH }, +#endif + { MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY, NNG_ECONNREFUSED }, + { MBEDTLS_ERR_SSL_ALLOC_FAILED, NNG_ENOMEM }, + { MBEDTLS_ERR_SSL_TIMEOUT, NNG_ETIMEDOUT }, + { MBEDTLS_ERR_SSL_CONN_EOF, NNG_ECLOSED }, +// MbedTLS 3.0 error codes +#ifdef MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE + { MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE, NNG_EPEERAUTH }, +#endif +#ifdef MBEDTLS_ERR_SSL_BAD_CERTIFICATE + { MBEDTLS_ERR_SSL_BAD_CERTIFICATE, NNG_EPEERAUTH }, +#endif + // terminator + { 0, 0 }, +}; + +static int +tls_mk_err(int err) +{ + for (int i = 0; tls_errs[i].tls != 0; i++) { + if (tls_errs[i].tls == err) { + return (tls_errs[i].nng); + } + } + return (NNG_ECRYPTO); +} + +static int +net_send(void *tls, const unsigned char *buf, size_t len) +{ + size_t sz = len; + int rv; + + rv = nng_tls_engine_send(tls, buf, &sz); + switch (rv) { + case 0: + return ((int) sz); + case NNG_EAGAIN: + return (MBEDTLS_ERR_SSL_WANT_WRITE); + default: + return (MBEDTLS_ERR_NET_SEND_FAILED); + } +} + +static int +net_recv(void *tls, unsigned char *buf, size_t len) +{ + size_t sz = len; + int rv; + + rv = nng_tls_engine_recv(tls, buf, &sz); + switch (rv) { + case 0: + return ((int) sz); + case NNG_EAGAIN: + return (MBEDTLS_ERR_SSL_WANT_READ); + default: + return (MBEDTLS_ERR_NET_RECV_FAILED); + } +} + +static void +conn_fini(nng_tls_engine_conn *ec) +{ + mbedtls_ssl_free(&ec->ctx); +} + +static void +conn_set_timer(void *arg, unsigned int t1, unsigned int t2) +{ + nng_time now = nng_clock(); + nng_tls_engine_conn *ec = arg; + ec->exp1 = t1 ? now + t1 : 0; + ec->exp2 = t2 ? now + t2 : 0; +} + +static int +conn_get_timer(void *arg) +{ + nng_tls_engine_conn *ec = arg; + nng_time now = nng_clock(); + if (ec->exp2 == 0) { + return -1; + } + if (now > ec->exp2) { + return 2; + } + if (now > ec->exp1) { + return 1; + } + return (0); +} + +static int +conn_init(nng_tls_engine_conn *ec, void *tls, nng_tls_engine_config *cfg, + const nng_sockaddr *sa) +{ + int rv; + char buf[NNG_MAXADDRSTRLEN]; + + ec->tls = tls; + + mbedtls_ssl_init(&ec->ctx); + mbedtls_ssl_set_bio(&ec->ctx, tls, net_send, net_recv, NULL); + mbedtls_ssl_set_timer_cb(&ec->ctx, ec, conn_set_timer, conn_get_timer); + + if ((rv = mbedtls_ssl_setup(&ec->ctx, &cfg->cfg_ctx)) != 0) { + tls_log_warn( + "NNG-TLS-CONN-FAIL", "Failed to setup TLS connection", rv); + return (tls_mk_err(rv)); + } + + if (cfg->server_name != NULL) { + mbedtls_ssl_set_hostname(&ec->ctx, cfg->server_name); + } + + if (cfg->mode == NNG_TLS_MODE_SERVER) { + nng_str_sockaddr(sa, buf, sizeof(buf)); + mbedtls_ssl_set_client_transport_id( + &ec->ctx, (const void *) buf, strlen(buf)); + } + + return (0); +} + +static void +conn_close(nng_tls_engine_conn *ec) +{ + // This may succeed, or it may fail. Either way we + // don't care. Implementations that depend on + // close-notify to mean anything are broken by design, + // just like RFC. Note that we do *NOT* close the TCP + // connection at this point. + (void) mbedtls_ssl_close_notify(&ec->ctx); +} + +static int +conn_recv(nng_tls_engine_conn *ec, uint8_t *buf, size_t *szp) +{ + int rv; + if ((rv = mbedtls_ssl_read(&ec->ctx, buf, *szp)) < 0) { + switch (rv) { + case MBEDTLS_ERR_SSL_WANT_READ: + case MBEDTLS_ERR_SSL_WANT_WRITE: + return (NNG_EAGAIN); + default: + return (tls_mk_err(rv)); + } + } + *szp = (size_t) rv; + return (0); +} + +static int +conn_send(nng_tls_engine_conn *ec, const uint8_t *buf, size_t *szp) +{ + int rv; + + if ((rv = mbedtls_ssl_write(&ec->ctx, buf, *szp)) < 0) { + switch (rv) { + case MBEDTLS_ERR_SSL_WANT_READ: + case MBEDTLS_ERR_SSL_WANT_WRITE: + return (NNG_EAGAIN); + default: + return (tls_mk_err(rv)); + } + } + *szp = (size_t) rv; + return (0); +} + +static int +conn_handshake(nng_tls_engine_conn *ec) +{ + int rv; + + rv = mbedtls_ssl_handshake(&ec->ctx); + switch (rv) { + case MBEDTLS_ERR_SSL_WANT_WRITE: + case MBEDTLS_ERR_SSL_WANT_READ: + // We have underlying I/O to complete first. We will + // be called again by a callback later. + return (NNG_EAGAIN); + case 0: + // The handshake is done, yay! + return (0); + + default: + tls_log_warn("NNG-TLS-HANDSHAKE", "TLS handshake failed", rv); + return (tls_mk_err(rv)); + } +} + +static bool +conn_verified(nng_tls_engine_conn *ec) +{ + return (mbedtls_ssl_get_verify_result(&ec->ctx) == 0); +} + +static char * +conn_peer_cn(nng_tls_engine_conn *ec) +{ + const mbedtls_x509_crt *crt = mbedtls_ssl_get_peer_cert(&ec->ctx); + if (!crt) { + return (NULL); + } + + char buf[0x400]; + int len = mbedtls_x509_dn_gets(buf, sizeof(buf), &crt->subject); + if (len <= 0) { + return (NULL); + } + + const char *pos = strstr(buf, "CN="); + if (!pos) { + return (NULL); + } + + pos += 3; + len -= (int) (pos - buf - 1); + if (len <= 1) { + return (NULL); + } + + char *rv = malloc(len); + memcpy(rv, pos, len); + return (rv); +} + +static char ** +conn_peer_alt_names(nng_tls_engine_conn *ec) +{ + const mbedtls_x509_crt *crt = mbedtls_ssl_get_peer_cert(&ec->ctx); + if (!crt) { + return (NULL); + } + + const mbedtls_asn1_sequence *seq = &crt->subject_alt_names; + + // get count + int count = 0; + do { + if (seq->buf.len > 0) + ++count; + seq = seq->next; + } while (seq); + if (count == 0) + return NULL; + + seq = &crt->subject_alt_names; + + // copy strings + char **rv = malloc((count + 1) * sizeof(char *)); + int i = 0; + do { + if (seq->buf.len == 0) + continue; + + rv[i] = malloc(seq->buf.len + 1); + memcpy(rv[i], seq->buf.p, seq->buf.len); + rv[i][seq->buf.len] = 0; + ++i; + + seq = seq->next; + } while (seq); + rv[i] = NULL; + + return rv; +} + +static void +config_fini(nng_tls_engine_config *cfg) +{ + pair *p; + psk *psk; + + mbedtls_ssl_config_free(&cfg->cfg_ctx); +#ifdef NNG_TLS_USE_CTR_DRBG + mbedtls_ctr_drbg_free(&cfg->rng_ctx); +#endif + mbedtls_x509_crt_free(&cfg->ca_certs); + mbedtls_x509_crl_free(&cfg->crl); + if (cfg->server_name) { + nni_strfree(cfg->server_name); + } + while ((p = nni_list_first(&cfg->pairs)) != NULL) { + nni_list_remove(&cfg->pairs, p); + mbedtls_x509_crt_free(&p->crt); + mbedtls_pk_free(&p->key); + + NNI_FREE_STRUCT(p); + } + while ((psk = nni_list_first(&cfg->psks)) != NULL) { + nni_list_remove(&cfg->psks, psk); + psk_free(psk); + } +} + +static int +config_init(nng_tls_engine_config *cfg, enum nng_tls_mode mode) +{ + int rv; + int ssl_mode; + int auth_mode; + + if (mode == NNG_TLS_MODE_SERVER) { + ssl_mode = MBEDTLS_SSL_IS_SERVER; + auth_mode = MBEDTLS_SSL_VERIFY_NONE; + } else { + ssl_mode = MBEDTLS_SSL_IS_CLIENT; + auth_mode = MBEDTLS_SSL_VERIFY_REQUIRED; + } + + cfg->mode = mode; + NNI_LIST_INIT(&cfg->pairs, pair, node); + NNI_LIST_INIT(&cfg->psks, psk, node); + mbedtls_ssl_config_init(&cfg->cfg_ctx); + mbedtls_x509_crt_init(&cfg->ca_certs); + mbedtls_x509_crl_init(&cfg->crl); + + rv = mbedtls_ssl_config_defaults(&cfg->cfg_ctx, ssl_mode, + MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); + if (rv != 0) { + tls_log_err("NNG-TLS-CONFIG-INIT-FAIL", + "Failed to initialize TLS configuration", rv); + config_fini(cfg); + return (tls_mk_err(rv)); + } + + mbedtls_ssl_conf_authmode(&cfg->cfg_ctx, auth_mode); + + // We *require* TLS v1.2 or newer, which is also known as SSL + // v3.3. + cfg->min_ver = MBEDTLS_SSL_MINOR_VERSION_3; +#ifdef MBEDTLS_SSL_PROTO_TLS1_3 + cfg->max_ver = MBEDTLS_SSL_MINOR_VERSION_4; +#else + cfg->max_ver = MBEDTLS_SSL_MINOR_VERSION_3; +#endif + + mbedtls_ssl_conf_min_version( + &cfg->cfg_ctx, MBEDTLS_SSL_MAJOR_VERSION_3, cfg->min_ver); + mbedtls_ssl_conf_max_version( + &cfg->cfg_ctx, MBEDTLS_SSL_MAJOR_VERSION_3, cfg->max_ver); + + mbedtls_ssl_conf_rng(&cfg->cfg_ctx, tls_random, cfg); + mbedtls_ssl_conf_dbg(&cfg->cfg_ctx, tls_dbg, cfg); + + if (cfg->mode == NNG_TLS_MODE_SERVER) { + mbedtls_ssl_conf_dtls_cookies(&cfg->cfg_ctx, + mbedtls_ssl_cookie_write, mbedtls_ssl_cookie_check, + &mbed_ssl_cookie_ctx); + } + + return (0); +} + +static int +config_server_name(nng_tls_engine_config *cfg, const char *name) +{ + char *dup; + if ((dup = nni_strdup(name)) == NULL) { + return (NNG_ENOMEM); + } + if (cfg->server_name != NULL) { + nni_strfree(cfg->server_name); + } + cfg->server_name = dup; + return (0); +} + +// callback used on the server side to select the right key +static int +config_psk_cb(void *arg, mbedtls_ssl_context *ssl, + const unsigned char *identity, size_t id_len) +{ + nng_tls_engine_config *cfg = arg; + psk *psk; + NNI_LIST_FOREACH (&cfg->psks, psk) { + if (id_len == strlen(psk->identity) && + (memcmp(identity, psk->identity, id_len) == 0)) { + nng_log_debug("NNG-TLS-PSK-IDENTITY", + "TLS client using PSK identity %s", psk->identity); + return (mbedtls_ssl_set_hs_psk( + ssl, psk->key, psk->keylen)); + } + } + nng_log_warn( + "NNG-TLS-PSK-NO-IDENTITY", "TLS client PSK identity not found"); + return (MBEDTLS_ERR_SSL_UNKNOWN_IDENTITY); +} + +static int +config_psk(nng_tls_engine_config *cfg, const char *identity, + const uint8_t *key, size_t key_len) +{ + int rv; + psk *srch; + psk *newpsk; + + if (((newpsk = NNI_ALLOC_STRUCT(newpsk)) == NULL) || + ((newpsk->identity = nni_strdup(identity)) == NULL) || + ((newpsk->key = nni_alloc(key_len)) == NULL)) { + psk_free(newpsk); + return (NNG_ENOMEM); + } + newpsk->keylen = key_len; + memcpy(newpsk->key, key, key_len); + + if (cfg->mode == NNG_TLS_MODE_SERVER) { + if (nni_list_empty(&cfg->psks)) { + mbedtls_ssl_conf_psk_cb( + &cfg->cfg_ctx, config_psk_cb, cfg); + } + } else { + if ((rv = mbedtls_ssl_conf_psk(&cfg->cfg_ctx, key, key_len, + (const unsigned char *) identity, + strlen(identity))) != 0) { + psk_free(newpsk); + tls_log_err("NNG-TLS-PSK-FAIL", + "Failed to configure PSK identity", rv); + return (tls_mk_err(rv)); + } + } + + // If the identity was previously configured, replace it. + // The rule here is that last one wins, so we always append. + NNI_LIST_FOREACH (&cfg->psks, srch) { + if (strcmp(srch->identity, identity) == 0) { + nni_list_remove(&cfg->psks, srch); + psk_free(srch); + break; + } + } + nni_list_append(&cfg->psks, newpsk); + return (0); +} + +static int +config_auth_mode(nng_tls_engine_config *cfg, nng_tls_auth_mode mode) +{ + switch (mode) { + case NNG_TLS_AUTH_MODE_NONE: + mbedtls_ssl_conf_authmode( + &cfg->cfg_ctx, MBEDTLS_SSL_VERIFY_NONE); + return (0); + case NNG_TLS_AUTH_MODE_OPTIONAL: + mbedtls_ssl_conf_authmode( + &cfg->cfg_ctx, MBEDTLS_SSL_VERIFY_OPTIONAL); + return (0); + case NNG_TLS_AUTH_MODE_REQUIRED: + mbedtls_ssl_conf_authmode( + &cfg->cfg_ctx, MBEDTLS_SSL_VERIFY_REQUIRED); + return (0); + } + return (NNG_EINVAL); +} + +static int +config_ca_chain(nng_tls_engine_config *cfg, const char *certs, const char *crl) +{ + size_t len; + const uint8_t *pem; + int rv; + + // Certs and CRL are in PEM data, with terminating NUL byte. + pem = (const uint8_t *) certs; + len = strlen(certs) + 1; + if ((rv = mbedtls_x509_crt_parse(&cfg->ca_certs, pem, len)) != 0) { + tls_log_err("NNG-TLS-CA-FAIL", + "Failed to parse CA certificate(s)", rv); + return (tls_mk_err(rv)); + } + if (crl != NULL) { + pem = (const uint8_t *) crl; + len = strlen(crl) + 1; + if ((rv = mbedtls_x509_crl_parse(&cfg->crl, pem, len)) != 0) { + tls_log_err("NNG-TLS-CRL-FAIL", + "Failed to parse revocation list", rv); + return (tls_mk_err(rv)); + } + } + + mbedtls_ssl_conf_ca_chain(&cfg->cfg_ctx, &cfg->ca_certs, &cfg->crl); + return (0); +} + +static int +config_own_cert(nng_tls_engine_config *cfg, const char *cert, const char *key, + const char *pass) +{ + size_t len; + const uint8_t *pem; + pair *p; + int rv; + + if ((p = NNI_ALLOC_STRUCT(p)) == NULL) { + return (NNG_ENOMEM); + } + mbedtls_x509_crt_init(&p->crt); + mbedtls_pk_init(&p->key); + + pem = (const uint8_t *) cert; + len = strlen(cert) + 1; + if ((rv = mbedtls_x509_crt_parse(&p->crt, pem, len)) != 0) { + tls_log_err("NNG-TLS-CRT-FAIL", + "Failure parsing our own certificate", rv); + rv = tls_mk_err(rv); + goto err; + } + + pem = (const uint8_t *) key; + len = strlen(key) + 1; +#if MBEDTLS_VERSION_MAJOR < 3 + rv = mbedtls_pk_parse_key(&p->key, pem, len, (const uint8_t *) pass, + pass != NULL ? strlen(pass) : 0); +#else + rv = mbedtls_pk_parse_key(&p->key, pem, len, (const uint8_t *) pass, + pass != NULL ? strlen(pass) : 0, tls_random, NULL); +#endif + if (rv != 0) { + tls_log_err("NNG-TLS-KEY", "Failure parsing private key", rv); + rv = tls_mk_err(rv); + goto err; + } + + rv = mbedtls_ssl_conf_own_cert(&cfg->cfg_ctx, &p->crt, &p->key); + if (rv != 0) { + tls_log_err("NNG-TLS-SELF", + "Failure configuring self certificate", rv); + rv = tls_mk_err(rv); + goto err; + } + + // Save this structure so we can free it with the context. + nni_list_append(&cfg->pairs, p); + return (0); + +err: + mbedtls_x509_crt_free(&p->crt); + mbedtls_pk_free(&p->key); + NNI_FREE_STRUCT(p); + return (rv); +} + +static int +config_version(nng_tls_engine_config *cfg, nng_tls_version min_ver, + nng_tls_version max_ver) +{ + int v1, v2; + int maj = MBEDTLS_SSL_MAJOR_VERSION_3; + + if (min_ver > max_ver) { + nng_log_err("TLS-CFG-VER", + "TLS maximum version must be larger than mimumum version"); + return (NNG_ENOTSUP); + } + switch (min_ver) { +#ifdef MBEDTLS_SSL_MINOR_VERSION_3 + case NNG_TLS_1_2: + v1 = MBEDTLS_SSL_MINOR_VERSION_3; + break; +#endif +#ifdef MBEDTLS_SSL_PROTO_TLS1_3 + case NNG_TLS_1_3: + v1 = MBEDTLS_SSL_MINOR_VERSION_4; + break; +#endif + default: + nng_log_err( + "TLS-CFG-VER", "TLS minimum version not supported"); + return (NNG_ENOTSUP); + } + + switch (max_ver) { +#ifdef MBEDTLS_SSL_MINOR_VERSION_3 + case NNG_TLS_1_2: + v2 = MBEDTLS_SSL_MINOR_VERSION_3; + break; +#endif + case NNG_TLS_1_3: // We lack support for 1.3, so treat as 1.2. +#ifdef MBEDTLS_SSL_PROTO_TLS1_3 + v2 = MBEDTLS_SSL_MINOR_VERSION_4; +#else + v2 = MBEDTLS_SSL_MINOR_VERSION_3; +#endif + break; + default: + // Note that this means that if we ever TLS 1.4 or 2.0, + // then this will break. That's sufficiently far out + // to justify not worrying about it. + nng_log_err( + "TLS-CFG-VER", "TLS maximum version not supported"); + return (NNG_ENOTSUP); + } + + cfg->min_ver = v1; + cfg->max_ver = v2; + mbedtls_ssl_conf_min_version(&cfg->cfg_ctx, maj, cfg->min_ver); + mbedtls_ssl_conf_max_version(&cfg->cfg_ctx, maj, cfg->max_ver); + return (0); +} + +static nng_tls_engine_config_ops config_ops = { + .init = config_init, + .fini = config_fini, + .size = sizeof(nng_tls_engine_config), + .auth = config_auth_mode, + .ca_chain = config_ca_chain, + .own_cert = config_own_cert, + .server = config_server_name, + .psk = config_psk, + .version = config_version, +}; + +static nng_tls_engine_conn_ops conn_ops = { + .size = sizeof(nng_tls_engine_conn), + .init = conn_init, + .fini = conn_fini, + .close = conn_close, + .recv = conn_recv, + .send = conn_send, + .handshake = conn_handshake, + .verified = conn_verified, + .peer_cn = conn_peer_cn, + .peer_alt_names = conn_peer_alt_names, +}; + +static nng_tls_engine tls_engine_mbed = { + .version = NNG_TLS_ENGINE_VERSION, + .config_ops = &config_ops, + .conn_ops = &conn_ops, + .name = "mbed", + .description = MBEDTLS_VERSION_STRING_FULL, + .fips_mode = false, +}; + +int +nng_tls_engine_init_mbed(void) +{ + int rv; + +#ifdef NNG_TLS_USE_CTR_DRBG + nni_mtx_init(&rng_lock); + + mbedtls_ctr_drbg_init(&cfg->rng_ctx); + rv = mbedtls_ctr_drbg_seed(&rng_ctx, tls_get_entropy, NULL, NULL, 0); + if (rv != 0) { + tls_log_err("NNG-TLS-RNG", "Failed initializing CTR DRBG", rv); + nni_mtx_fini(&rng_lock); + return (rv); + } +#endif +#ifdef MBEDTLS_PSA_CRYPTO_C + rv = psa_crypto_init(); + if (rv != 0) { + tls_log_err( + "NNG-TLS-INIT", "Failed initializing PSA crypto", rv); + return (rv); + } +#endif + // Uncomment the following to have noisy debug from mbedTLS. + // This may be useful when trying to debug failures. + // mbedtls_debug_set_threshold(9); + + mbedtls_ssl_cookie_init(&mbed_ssl_cookie_ctx); + rv = mbedtls_ssl_cookie_setup(&mbed_ssl_cookie_ctx, tls_random, NULL); + + if (rv == 0) { + rv = nng_tls_engine_register(&tls_engine_mbed); + } + +#ifdef NNG_TLS_USE_CTR_DRBG + if (rv != 0) { + nni_mtx_fini(&rng_lock); + } +#endif + + return (rv); +} + +void +nng_tls_engine_fini_mbed(void) +{ + mbedtls_ssl_cookie_free(&mbed_ssl_cookie_ctx); +#ifdef NNG_TLS_USE_CTR_DRBG + mbedtls_ctr_drbg_free(&rng_ctx); + nni_mtx_fini(&rng_lock); +#endif +#ifdef MBEDTLS_PSA_CRYPTO_C + mbedtls_psa_crypto_free(); +#endif +} diff --git a/src/supplemental/tls/mbedtls/tls.c b/src/supplemental/tls/mbedtls/tls.c deleted file mode 100644 index 7764bbbf..00000000 --- a/src/supplemental/tls/mbedtls/tls.c +++ /dev/null @@ -1,819 +0,0 @@ -// -// Copyright 2025 Staysail Systems, Inc. -// Copyright 2018 Capitar IT Group BV -// Copyright 2019 Devolutions -// -// This software is supplied under the terms of the MIT License, a -// copy of which should be located in the distribution where this -// file was obtained (LICENSE.txt). A copy of the license may also be -// found online at https://opensource.org/licenses/MIT. -// - -#include -#include -#include -#include - -#include "mbedtls/version.h" // Must be first in order to pick up version - -#include "mbedtls/error.h" -#ifdef MBEDTLS_PSA_CRYPTO_C -#include "psa/crypto.h" -#endif - -#include "nng/nng.h" - -#include "../tls_engine.h" - -// mbedTLS renamed this header for 2.4.0. -#if MBEDTLS_VERSION_MAJOR > 2 || MBEDTLS_VERSION_MINOR >= 4 -#include "mbedtls/net_sockets.h" -#else -#include "mbedtls/net.h" -#endif - -#include "mbedtls/debug.h" -#include "mbedtls/ssl.h" - -#include "core/nng_impl.h" - -// pair holds a private key and the associated certificate. -typedef struct { - mbedtls_x509_crt crt; - mbedtls_pk_context key; - nni_list_node node; -} pair; - -// psk holds an identity and preshared key -typedef struct { - // NB: Technically RFC 4279 requires this be UTF-8 string, although - // others suggest making it opaque bytes. We treat it as a C string, - // so we cannot support embedded zero bytes. - char *identity; - uint8_t *key; - size_t keylen; - nni_list_node node; -} psk; - -static void -psk_free(psk *p) -{ - if (p != NULL) { - NNI_ASSERT(!nni_list_node_active(&p->node)); - if (p->identity != NULL) { - nni_strfree(p->identity); - p->identity = NULL; - } - if (p->key != NULL && p->keylen != 0) { - nni_free(p->key, p->keylen); - p->key = NULL; - p->keylen = 0; - } - NNI_FREE_STRUCT(p); - } -} - -#ifdef NNG_TLS_USE_CTR_DRBG -// Use a global RNG if we're going to override the builtin. -static mbedtls_ctr_drbg_context rng_ctx; -static nni_mtx rng_lock; -#endif - -struct nng_tls_engine_conn { - void *tls; // parent conn - mbedtls_ssl_context ctx; -}; - -struct nng_tls_engine_config { - mbedtls_ssl_config cfg_ctx; - char *server_name; - mbedtls_x509_crt ca_certs; - mbedtls_x509_crl crl; - int min_ver; - int max_ver; - nng_tls_mode mode; - nni_list pairs; - nni_list psks; -}; - -static void -tls_dbg(void *ctx, int level, const char *file, int line, const char *s) -{ - NNI_ARG_UNUSED(ctx); - NNI_ARG_UNUSED(level); - const char *f; - while ((f = strchr(file, '/')) != NULL) { - file = f + 1; - } - nng_log_debug("MBED", "%s: %d: %s", file, line, s); -} - -static int -tls_get_entropy(void *arg, unsigned char *buf, size_t len) -{ - NNI_ARG_UNUSED(arg); - while (len) { - uint32_t x = nni_random(); - size_t n; - - n = len < sizeof(x) ? len : sizeof(x); - memcpy(buf, &x, n); - len -= n; - buf += n; - } - return (0); -} - -static int -tls_random(void *arg, unsigned char *buf, size_t sz) -{ -#ifdef NNG_TLS_USE_CTR_DRBG - int rv; - - nni_mtx_lock(&rng_lock); - rv = mbedtls_ctr_drbg_random(&rng_ctx, buf, sz); - nni_mtx_unlock(&rng_lock); - return (rv); -#else - return (tls_get_entropy(arg, buf, sz)); -#endif -} - -static void -tls_log_err(const char *msgid, const char *context, int errnum) -{ - char errbuf[256]; - mbedtls_strerror(errnum, errbuf, sizeof(errbuf)); - nng_log_err(msgid, "%s: %s", context, errbuf); -} - -static void -tls_log_warn(const char *msgid, const char *context, int errnum) -{ - char errbuf[256]; - mbedtls_strerror(errnum, errbuf, sizeof(errbuf)); - nng_log_warn(msgid, "%s: %d - %s", context, errnum, errbuf); -} - -// tls_mk_err converts an mbed error to an NNG error. -static struct { - int tls; - int nng; -} tls_errs[] = { -#ifdef MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE - { MBEDTLS_ERR_SSL_NO_CLIENT_CERTIFICATE, NNG_EPEERAUTH }, -#endif -#ifdef MBEDTLS_ERR_SSL_CA_CHAIN_REQUIRED - { MBEDTLS_ERR_SSL_CA_CHAIN_REQUIRED, NNG_EPEERAUTH }, -#endif -#ifdef MBEDTLS_ERR_SSL_PEER_VERIFY_FAILED - { MBEDTLS_ERR_SSL_PEER_VERIFY_FAILED, NNG_EPEERAUTH }, -#endif -#ifdef MBEDTLS_ERR_SSL_NO_USABLE_CIPHERSUITE - { MBEDTLS_ERR_SSL_NO_USABLE_CIPHERSUITE, NNG_EPEERAUTH }, -#endif - { MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY, NNG_ECONNREFUSED }, - { MBEDTLS_ERR_SSL_ALLOC_FAILED, NNG_ENOMEM }, - { MBEDTLS_ERR_SSL_TIMEOUT, NNG_ETIMEDOUT }, - { MBEDTLS_ERR_SSL_CONN_EOF, NNG_ECLOSED }, -// MbedTLS 3.0 error codes -#ifdef MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE - { MBEDTLS_ERR_SSL_HANDSHAKE_FAILURE, NNG_EPEERAUTH }, -#endif -#ifdef MBEDTLS_ERR_SSL_BAD_CERTIFICATE - { MBEDTLS_ERR_SSL_BAD_CERTIFICATE, NNG_EPEERAUTH }, -#endif - // terminator - { 0, 0 }, -}; - -static int -tls_mk_err(int err) -{ - for (int i = 0; tls_errs[i].tls != 0; i++) { - if (tls_errs[i].tls == err) { - return (tls_errs[i].nng); - } - } - return (NNG_ECRYPTO); -} - -static int -net_send(void *tls, const unsigned char *buf, size_t len) -{ - size_t sz = len; - int rv; - - rv = nng_tls_engine_send(tls, buf, &sz); - switch (rv) { - case 0: - return ((int) sz); - case NNG_EAGAIN: - return (MBEDTLS_ERR_SSL_WANT_WRITE); - default: - return (MBEDTLS_ERR_NET_SEND_FAILED); - } -} - -static int -net_recv(void *tls, unsigned char *buf, size_t len) -{ - size_t sz = len; - int rv; - - rv = nng_tls_engine_recv(tls, buf, &sz); - switch (rv) { - case 0: - return ((int) sz); - case NNG_EAGAIN: - return (MBEDTLS_ERR_SSL_WANT_READ); - default: - return (MBEDTLS_ERR_NET_RECV_FAILED); - } -} - -static void -conn_fini(nng_tls_engine_conn *ec) -{ - mbedtls_ssl_free(&ec->ctx); -} - -static int -conn_init(nng_tls_engine_conn *ec, void *tls, nng_tls_engine_config *cfg) -{ - int rv; - - ec->tls = tls; - - mbedtls_ssl_init(&ec->ctx); - mbedtls_ssl_set_bio(&ec->ctx, tls, net_send, net_recv, NULL); - - if ((rv = mbedtls_ssl_setup(&ec->ctx, &cfg->cfg_ctx)) != 0) { - tls_log_warn( - "NNG-TLS-CONN-FAIL", "Failed to setup TLS connection", rv); - return (tls_mk_err(rv)); - } - - if (cfg->server_name != NULL) { - mbedtls_ssl_set_hostname(&ec->ctx, cfg->server_name); - } - - return (0); -} - -static void -conn_close(nng_tls_engine_conn *ec) -{ - // This may succeed, or it may fail. Either way we - // don't care. Implementations that depend on - // close-notify to mean anything are broken by design, - // just like RFC. Note that we do *NOT* close the TCP - // connection at this point. - (void) mbedtls_ssl_close_notify(&ec->ctx); -} - -static int -conn_recv(nng_tls_engine_conn *ec, uint8_t *buf, size_t *szp) -{ - int rv; - if ((rv = mbedtls_ssl_read(&ec->ctx, buf, *szp)) < 0) { - switch (rv) { - case MBEDTLS_ERR_SSL_WANT_READ: - case MBEDTLS_ERR_SSL_WANT_WRITE: - return (NNG_EAGAIN); - default: - return (tls_mk_err(rv)); - } - } - *szp = (size_t) rv; - return (0); -} - -static int -conn_send(nng_tls_engine_conn *ec, const uint8_t *buf, size_t *szp) -{ - int rv; - - if ((rv = mbedtls_ssl_write(&ec->ctx, buf, *szp)) < 0) { - switch (rv) { - case MBEDTLS_ERR_SSL_WANT_READ: - case MBEDTLS_ERR_SSL_WANT_WRITE: - return (NNG_EAGAIN); - default: - return (tls_mk_err(rv)); - } - } - *szp = (size_t) rv; - return (0); -} - -static int -conn_handshake(nng_tls_engine_conn *ec) -{ - int rv; - - rv = mbedtls_ssl_handshake(&ec->ctx); - switch (rv) { - case MBEDTLS_ERR_SSL_WANT_WRITE: - case MBEDTLS_ERR_SSL_WANT_READ: - // We have underlying I/O to complete first. We will - // be called again by a callback later. - return (NNG_EAGAIN); - case 0: - // The handshake is done, yay! - return (0); - - default: - tls_log_warn("NNG-TLS-HANDSHAKE", "TLS handshake failed", rv); - return (tls_mk_err(rv)); - } -} - -static bool -conn_verified(nng_tls_engine_conn *ec) -{ - return (mbedtls_ssl_get_verify_result(&ec->ctx) == 0); -} - -static char * -conn_peer_cn(nng_tls_engine_conn *ec) -{ - const mbedtls_x509_crt *crt = mbedtls_ssl_get_peer_cert(&ec->ctx); - if (!crt) { - return (NULL); - } - - char buf[0x400]; - int len = mbedtls_x509_dn_gets(buf, sizeof(buf), &crt->subject); - if (len <= 0) { - return (NULL); - } - - const char *pos = strstr(buf, "CN="); - if (!pos) { - return (NULL); - } - - pos += 3; - len -= (int) (pos - buf - 1); - if (len <= 1) { - return (NULL); - } - - char *rv = malloc(len); - memcpy(rv, pos, len); - return (rv); -} - -static char ** -conn_peer_alt_names(nng_tls_engine_conn *ec) -{ - const mbedtls_x509_crt *crt = mbedtls_ssl_get_peer_cert(&ec->ctx); - if (!crt) { - return (NULL); - } - - const mbedtls_asn1_sequence *seq = &crt->subject_alt_names; - - // get count - int count = 0; - do { - if (seq->buf.len > 0) - ++count; - seq = seq->next; - } while (seq); - if (count == 0) - return NULL; - - seq = &crt->subject_alt_names; - - // copy strings - char **rv = malloc((count + 1) * sizeof(char *)); - int i = 0; - do { - if (seq->buf.len == 0) - continue; - - rv[i] = malloc(seq->buf.len + 1); - memcpy(rv[i], seq->buf.p, seq->buf.len); - rv[i][seq->buf.len] = 0; - ++i; - - seq = seq->next; - } while (seq); - rv[i] = NULL; - - return rv; -} - -static void -config_fini(nng_tls_engine_config *cfg) -{ - pair *p; - psk *psk; - - mbedtls_ssl_config_free(&cfg->cfg_ctx); -#ifdef NNG_TLS_USE_CTR_DRBG - mbedtls_ctr_drbg_free(&cfg->rng_ctx); -#endif - mbedtls_x509_crt_free(&cfg->ca_certs); - mbedtls_x509_crl_free(&cfg->crl); - if (cfg->server_name) { - nni_strfree(cfg->server_name); - } - while ((p = nni_list_first(&cfg->pairs)) != NULL) { - nni_list_remove(&cfg->pairs, p); - mbedtls_x509_crt_free(&p->crt); - mbedtls_pk_free(&p->key); - - NNI_FREE_STRUCT(p); - } - while ((psk = nni_list_first(&cfg->psks)) != NULL) { - nni_list_remove(&cfg->psks, psk); - psk_free(psk); - } -} - -static int -config_init(nng_tls_engine_config *cfg, enum nng_tls_mode mode) -{ - int rv; - int ssl_mode; - int auth_mode; - - if (mode == NNG_TLS_MODE_SERVER) { - ssl_mode = MBEDTLS_SSL_IS_SERVER; - auth_mode = MBEDTLS_SSL_VERIFY_NONE; - } else { - ssl_mode = MBEDTLS_SSL_IS_CLIENT; - auth_mode = MBEDTLS_SSL_VERIFY_REQUIRED; - } - - cfg->mode = mode; - NNI_LIST_INIT(&cfg->pairs, pair, node); - NNI_LIST_INIT(&cfg->psks, psk, node); - mbedtls_ssl_config_init(&cfg->cfg_ctx); - mbedtls_x509_crt_init(&cfg->ca_certs); - mbedtls_x509_crl_init(&cfg->crl); - - rv = mbedtls_ssl_config_defaults(&cfg->cfg_ctx, ssl_mode, - MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); - if (rv != 0) { - tls_log_err("NNG-TLS-CONFIG-INIT-FAIL", - "Failed to initialize TLS configuration", rv); - config_fini(cfg); - return (tls_mk_err(rv)); - } - - mbedtls_ssl_conf_authmode(&cfg->cfg_ctx, auth_mode); - - // We *require* TLS v1.2 or newer, which is also known as SSL - // v3.3. - cfg->min_ver = MBEDTLS_SSL_MINOR_VERSION_3; -#ifdef MBEDTLS_SSL_PROTO_TLS1_3 - cfg->max_ver = MBEDTLS_SSL_MINOR_VERSION_4; -#else - cfg->max_ver = MBEDTLS_SSL_MINOR_VERSION_3; -#endif - - mbedtls_ssl_conf_min_version( - &cfg->cfg_ctx, MBEDTLS_SSL_MAJOR_VERSION_3, cfg->min_ver); - mbedtls_ssl_conf_max_version( - &cfg->cfg_ctx, MBEDTLS_SSL_MAJOR_VERSION_3, cfg->max_ver); - - mbedtls_ssl_conf_rng(&cfg->cfg_ctx, tls_random, cfg); - mbedtls_ssl_conf_dbg(&cfg->cfg_ctx, tls_dbg, cfg); - - return (0); -} - -static int -config_server_name(nng_tls_engine_config *cfg, const char *name) -{ - char *dup; - if ((dup = nni_strdup(name)) == NULL) { - return (NNG_ENOMEM); - } - if (cfg->server_name != NULL) { - nni_strfree(cfg->server_name); - } - cfg->server_name = dup; - return (0); -} - -// callback used on the server side to select the right key -static int -config_psk_cb(void *arg, mbedtls_ssl_context *ssl, - const unsigned char *identity, size_t id_len) -{ - nng_tls_engine_config *cfg = arg; - psk *psk; - NNI_LIST_FOREACH (&cfg->psks, psk) { - if (id_len == strlen(psk->identity) && - (memcmp(identity, psk->identity, id_len) == 0)) { - nng_log_debug("NNG-TLS-PSK-IDENTITY", - "TLS client using PSK identity %s", psk->identity); - return (mbedtls_ssl_set_hs_psk( - ssl, psk->key, psk->keylen)); - } - } - nng_log_warn( - "NNG-TLS-PSK-NO-IDENTITY", "TLS client PSK identity not found"); - return (MBEDTLS_ERR_SSL_UNKNOWN_IDENTITY); -} - -static int -config_psk(nng_tls_engine_config *cfg, const char *identity, - const uint8_t *key, size_t key_len) -{ - int rv; - psk *srch; - psk *newpsk; - - if (((newpsk = NNI_ALLOC_STRUCT(newpsk)) == NULL) || - ((newpsk->identity = nni_strdup(identity)) == NULL) || - ((newpsk->key = nni_alloc(key_len)) == NULL)) { - psk_free(newpsk); - return (NNG_ENOMEM); - } - newpsk->keylen = key_len; - memcpy(newpsk->key, key, key_len); - - if (cfg->mode == NNG_TLS_MODE_SERVER) { - if (nni_list_empty(&cfg->psks)) { - mbedtls_ssl_conf_psk_cb( - &cfg->cfg_ctx, config_psk_cb, cfg); - } - } else { - if ((rv = mbedtls_ssl_conf_psk(&cfg->cfg_ctx, key, key_len, - (const unsigned char *) identity, - strlen(identity))) != 0) { - psk_free(newpsk); - tls_log_err("NNG-TLS-PSK-FAIL", - "Failed to configure PSK identity", rv); - return (tls_mk_err(rv)); - } - } - - // If the identity was previously configured, replace it. - // The rule here is that last one wins, so we always append. - NNI_LIST_FOREACH (&cfg->psks, srch) { - if (strcmp(srch->identity, identity) == 0) { - nni_list_remove(&cfg->psks, srch); - psk_free(srch); - break; - } - } - nni_list_append(&cfg->psks, newpsk); - return (0); -} - -static int -config_auth_mode(nng_tls_engine_config *cfg, nng_tls_auth_mode mode) -{ - switch (mode) { - case NNG_TLS_AUTH_MODE_NONE: - mbedtls_ssl_conf_authmode( - &cfg->cfg_ctx, MBEDTLS_SSL_VERIFY_NONE); - return (0); - case NNG_TLS_AUTH_MODE_OPTIONAL: - mbedtls_ssl_conf_authmode( - &cfg->cfg_ctx, MBEDTLS_SSL_VERIFY_OPTIONAL); - return (0); - case NNG_TLS_AUTH_MODE_REQUIRED: - mbedtls_ssl_conf_authmode( - &cfg->cfg_ctx, MBEDTLS_SSL_VERIFY_REQUIRED); - return (0); - } - return (NNG_EINVAL); -} - -static int -config_ca_chain(nng_tls_engine_config *cfg, const char *certs, const char *crl) -{ - size_t len; - const uint8_t *pem; - int rv; - - // Certs and CRL are in PEM data, with terminating NUL byte. - pem = (const uint8_t *) certs; - len = strlen(certs) + 1; - if ((rv = mbedtls_x509_crt_parse(&cfg->ca_certs, pem, len)) != 0) { - tls_log_err("NNG-TLS-CA-FAIL", - "Failed to parse CA certificate(s)", rv); - return (tls_mk_err(rv)); - } - if (crl != NULL) { - pem = (const uint8_t *) crl; - len = strlen(crl) + 1; - if ((rv = mbedtls_x509_crl_parse(&cfg->crl, pem, len)) != 0) { - tls_log_err("NNG-TLS-CRL-FAIL", - "Failed to parse revocation list", rv); - return (tls_mk_err(rv)); - } - } - - mbedtls_ssl_conf_ca_chain(&cfg->cfg_ctx, &cfg->ca_certs, &cfg->crl); - return (0); -} - -static int -config_own_cert(nng_tls_engine_config *cfg, const char *cert, const char *key, - const char *pass) -{ - size_t len; - const uint8_t *pem; - pair *p; - int rv; - - if ((p = NNI_ALLOC_STRUCT(p)) == NULL) { - return (NNG_ENOMEM); - } - mbedtls_x509_crt_init(&p->crt); - mbedtls_pk_init(&p->key); - - pem = (const uint8_t *) cert; - len = strlen(cert) + 1; - if ((rv = mbedtls_x509_crt_parse(&p->crt, pem, len)) != 0) { - tls_log_err("NNG-TLS-CRT-FAIL", - "Failure parsing our own certificate", rv); - rv = tls_mk_err(rv); - goto err; - } - - pem = (const uint8_t *) key; - len = strlen(key) + 1; -#if MBEDTLS_VERSION_MAJOR < 3 - rv = mbedtls_pk_parse_key(&p->key, pem, len, (const uint8_t *) pass, - pass != NULL ? strlen(pass) : 0); -#else - rv = mbedtls_pk_parse_key(&p->key, pem, len, (const uint8_t *) pass, - pass != NULL ? strlen(pass) : 0, tls_random, NULL); -#endif - if (rv != 0) { - tls_log_err("NNG-TLS-KEY", "Failure parsing private key", rv); - rv = tls_mk_err(rv); - goto err; - } - - rv = mbedtls_ssl_conf_own_cert(&cfg->cfg_ctx, &p->crt, &p->key); - if (rv != 0) { - tls_log_err("NNG-TLS-SELF", - "Failure configuring self certificate", rv); - rv = tls_mk_err(rv); - goto err; - } - - // Save this structure so we can free it with the context. - nni_list_append(&cfg->pairs, p); - return (0); - -err: - mbedtls_x509_crt_free(&p->crt); - mbedtls_pk_free(&p->key); - NNI_FREE_STRUCT(p); - return (rv); -} - -static int -config_version(nng_tls_engine_config *cfg, nng_tls_version min_ver, - nng_tls_version max_ver) -{ - int v1, v2; - int maj = MBEDTLS_SSL_MAJOR_VERSION_3; - - if (min_ver > max_ver) { - nng_log_err("TLS-CFG-VER", - "TLS maximum version must be larger than mimumum version"); - return (NNG_ENOTSUP); - } - switch (min_ver) { -#ifdef MBEDTLS_SSL_MINOR_VERSION_3 - case NNG_TLS_1_2: - v1 = MBEDTLS_SSL_MINOR_VERSION_3; - break; -#endif -#ifdef MBEDTLS_SSL_PROTO_TLS1_3 - case NNG_TLS_1_3: - v1 = MBEDTLS_SSL_MINOR_VERSION_4; - break; -#endif - default: - nng_log_err( - "TLS-CFG-VER", "TLS minimum version not supported"); - return (NNG_ENOTSUP); - } - - switch (max_ver) { -#ifdef MBEDTLS_SSL_MINOR_VERSION_3 - case NNG_TLS_1_2: - v2 = MBEDTLS_SSL_MINOR_VERSION_3; - break; -#endif - case NNG_TLS_1_3: // We lack support for 1.3, so treat as 1.2. -#ifdef MBEDTLS_SSL_PROTO_TLS1_3 - v2 = MBEDTLS_SSL_MINOR_VERSION_4; -#else - v2 = MBEDTLS_SSL_MINOR_VERSION_3; -#endif - break; - default: - // Note that this means that if we ever TLS 1.4 or 2.0, - // then this will break. That's sufficiently far out - // to justify not worrying about it. - nng_log_err( - "TLS-CFG-VER", "TLS maximum version not supported"); - return (NNG_ENOTSUP); - } - - cfg->min_ver = v1; - cfg->max_ver = v2; - mbedtls_ssl_conf_min_version(&cfg->cfg_ctx, maj, cfg->min_ver); - mbedtls_ssl_conf_max_version(&cfg->cfg_ctx, maj, cfg->max_ver); - return (0); -} - -static nng_tls_engine_config_ops config_ops = { - .init = config_init, - .fini = config_fini, - .size = sizeof(nng_tls_engine_config), - .auth = config_auth_mode, - .ca_chain = config_ca_chain, - .own_cert = config_own_cert, - .server = config_server_name, - .psk = config_psk, - .version = config_version, -}; - -static nng_tls_engine_conn_ops conn_ops = { - .size = sizeof(nng_tls_engine_conn), - .init = conn_init, - .fini = conn_fini, - .close = conn_close, - .recv = conn_recv, - .send = conn_send, - .handshake = conn_handshake, - .verified = conn_verified, - .peer_cn = conn_peer_cn, - .peer_alt_names = conn_peer_alt_names, -}; - -static nng_tls_engine tls_engine_mbed = { - .version = NNG_TLS_ENGINE_VERSION, - .config_ops = &config_ops, - .conn_ops = &conn_ops, - .name = "mbed", - .description = MBEDTLS_VERSION_STRING_FULL, - .fips_mode = false, -}; - -int -nng_tls_engine_init_mbed(void) -{ - int rv; - -#ifdef NNG_TLS_USE_CTR_DRBG - nni_mtx_init(&rng_lock); - - mbedtls_ctr_drbg_init(&cfg->rng_ctx); - rv = mbedtls_ctr_drbg_seed(&rng_ctx, tls_get_entropy, NULL, NULL, 0); - if (rv != 0) { - tls_log_err("NNG-TLS-RNG", "Failed initializing CTR DRBG", rv); - nni_mtx_fini(&rng_lock); - return (rv); - } -#endif -#ifdef MBEDTLS_PSA_CRYPTO_C - rv = psa_crypto_init(); - if (rv != 0) { - tls_log_err( - "NNG-TLS-INIT", "Failed initializing PSA crypto", rv); - return (rv); - } -#endif - // Uncomment the following to have noisy debug from mbedTLS. - // This may be useful when trying to debug failures. - // mbedtls_debug_set_threshold(9); - - rv = nng_tls_engine_register(&tls_engine_mbed); - -#ifdef NNG_TLS_USE_CTR_DRBG - if (rv != 0) { - nni_mtx_fini(&rng_lock); - } -#endif - - return (rv); -} - -void -nng_tls_engine_fini_mbed(void) -{ -#ifdef NNG_TLS_USE_CTR_DRBG - mbedtls_ctr_drbg_free(&rng_ctx); - nni_mtx_fini(&rng_lock); -#endif -#ifdef MBEDTLS_PSA_CRYPTO_C - mbedtls_psa_crypto_free(); -#endif -} diff --git a/src/supplemental/tls/tls_common.c b/src/supplemental/tls/tls_common.c index c31e5fe9..40689f69 100644 --- a/src/supplemental/tls/tls_common.c +++ b/src/supplemental/tls/tls_common.c @@ -41,15 +41,15 @@ static nni_atomic_ptr tls_engine; static void tls_bio_send_cb(void *arg); static void tls_bio_recv_cb(void *arg); -static void tls_do_send(tls_conn *); -static void tls_do_recv(tls_conn *); -static void tls_bio_send_start(tls_conn *); -static void tls_bio_error(tls_conn *, int); +static void tls_do_send(nni_tls_conn *); +static void tls_do_recv(nni_tls_conn *); +static void tls_bio_send_start(nni_tls_conn *); +static void tls_bio_error(nni_tls_conn *, nng_err); static void tls_cancel(nni_aio *aio, void *arg, nng_err rv) { - tls_conn *conn = arg; + nni_tls_conn *conn = arg; nni_mtx_lock(&conn->lock); if (aio == nni_list_first(&conn->recv_queue)) { nni_aio_abort(&conn->bio_recv, rv); @@ -64,7 +64,7 @@ tls_cancel(nni_aio *aio, void *arg, nng_err rv) // tls_send implements the upper layer send operation. void -nni_tls_send(tls_conn *conn, nni_aio *aio) +nni_tls_send(nni_tls_conn *conn, nni_aio *aio) { nni_aio_reset(aio); nni_mtx_lock(&conn->lock); @@ -83,7 +83,7 @@ nni_tls_send(tls_conn *conn, nni_aio *aio) } void -nni_tls_recv(tls_conn *conn, nni_aio *aio) +nni_tls_recv(nni_tls_conn *conn, nni_aio *aio) { nni_aio_reset(aio); nni_mtx_lock(&conn->lock); @@ -103,21 +103,20 @@ nni_tls_recv(tls_conn *conn, nni_aio *aio) } void -nni_tls_close(tls_conn *conn) +nni_tls_close(nni_tls_conn *conn) { if (!nni_atomic_flag_test_and_set(&conn->did_close)) { nni_mtx_lock(&conn->lock); conn->ops.close((void *) (conn + 1)); - tls_bio_error(conn, NNG_ECLOSED); nni_mtx_unlock(&conn->lock); - if (conn->bio != NULL) { - conn->bio_ops.bio_close(conn->bio); - } + nni_mtx_lock(&conn->bio_lock); + tls_bio_error(conn, NNG_ECLOSED); + nni_mtx_unlock(&conn->bio_lock); } } void -nni_tls_stop(tls_conn *conn) +nni_tls_stop(nni_tls_conn *conn) { nni_tls_close(conn); if (conn->bio != NULL) { @@ -128,7 +127,7 @@ nni_tls_stop(tls_conn *conn) } bool -nni_tls_verified(tls_conn *conn) +nni_tls_verified(nni_tls_conn *conn) { bool result; nni_mtx_lock(&conn->lock); @@ -138,7 +137,7 @@ nni_tls_verified(tls_conn *conn) } const char * -nni_tls_peer_cn(tls_conn *conn) +nni_tls_peer_cn(nni_tls_conn *conn) { const char *result; nni_mtx_lock(&conn->lock); @@ -148,7 +147,7 @@ nni_tls_peer_cn(tls_conn *conn) } int -nni_tls_init(tls_conn *conn, nng_tls_config *cfg) +nni_tls_init(nni_tls_conn *conn, nng_tls_config *cfg) { const nng_tls_engine *eng; @@ -158,9 +157,9 @@ nni_tls_init(tls_conn *conn, nng_tls_config *cfg) cfg->busy = true; nni_mtx_unlock(&cfg->lock); - if (((conn->bio_send_buf = nni_alloc(NNG_TLS_MAX_SEND_SIZE)) == + if (((conn->bio_send_buf = nni_zalloc(NNG_TLS_MAX_SEND_SIZE)) == NULL) || - ((conn->bio_recv_buf = nni_alloc(NNG_TLS_MAX_RECV_SIZE)) == + ((conn->bio_recv_buf = nni_zalloc(NNG_TLS_MAX_RECV_SIZE)) == NULL)) { return (NNG_ENOMEM); } @@ -173,6 +172,7 @@ nni_tls_init(tls_conn *conn, nng_tls_config *cfg) nni_aio_list_init(&conn->send_queue); nni_aio_list_init(&conn->recv_queue); nni_mtx_init(&conn->lock); + nni_mtx_init(&conn->bio_lock); nni_aio_set_timeout(&conn->bio_send, NNG_DURATION_INFINITE); nni_aio_set_timeout(&conn->bio_recv, NNG_DURATION_INFINITE); nni_atomic_flag_reset(&conn->did_close); @@ -182,7 +182,7 @@ nni_tls_init(tls_conn *conn, nng_tls_config *cfg) } void -nni_tls_fini(tls_conn *conn) +nni_tls_fini(nni_tls_conn *conn) { nni_tls_stop(conn); conn->ops.fini((void *) (conn + 1)); @@ -200,11 +200,13 @@ nni_tls_fini(tls_conn *conn) if (conn->bio != NULL) { conn->bio_ops.bio_free(conn->bio); } + nni_mtx_fini(&conn->bio_lock); nni_mtx_fini(&conn->lock); } int -nni_tls_start(tls_conn *conn, const nni_tls_bio_ops *biops, void *bio) +nni_tls_start(nni_tls_conn *conn, const nni_tls_bio_ops *biops, void *bio, + const nng_sockaddr *sa) { nng_tls_engine_config *cfg; nng_tls_engine_conn *econ; @@ -215,48 +217,62 @@ nni_tls_start(tls_conn *conn, const nni_tls_bio_ops *biops, void *bio) conn->bio_ops = *biops; conn->bio = bio; - return (conn->ops.init(econ, conn, cfg)); + return (conn->ops.init(econ, conn, cfg, sa)); } static void -tls_bio_error(tls_conn *conn, int rv) +tls_conn_err(nni_tls_conn *conn, nng_err rv) { - // An error here is fatal. Shut it all down. nni_aio *aio; - if (conn->bio != NULL) { - conn->bio_ops.bio_close(conn->bio); - } - nni_aio_close(&conn->bio_send); - nni_aio_close(&conn->bio_recv); + nni_mtx_lock(&conn->lock); 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); } + nni_mtx_unlock(&conn->lock); +} +static void +tls_bio_error(nni_tls_conn *conn, nng_err rv) +{ + // An error here is fatal. Shut it all down. + if (!conn->bio_closed) { + conn->bio_closed = true; + conn->bio_err = rv; + if (conn->bio_send_active) + nni_aio_abort(&conn->bio_send, conn->bio_err); + if (conn->bio_recv_pend) + nni_aio_abort(&conn->bio_recv, conn->bio_err); + if (conn->bio != NULL) { + conn->bio_ops.bio_close(conn->bio); + } + + nni_aio_close(&conn->bio_send); + nni_aio_close(&conn->bio_recv); + } } -static bool -tls_do_handshake(tls_conn *conn) +static nng_err +tls_handshake(nni_tls_conn *conn) { int rv; if (conn->hs_done) { - return (true); + return (NNG_OK); } rv = conn->ops.handshake((void *) (conn + 1)); if (rv == NNG_EAGAIN) { // We need more data. - return (false); + return (rv); } - if (rv == 0) { + if (rv == NNG_OK) { conn->hs_done = true; - return (true); + return (rv); } - tls_bio_error(conn, rv); - return (true); + return (rv); } static void -tls_do_recv(tls_conn *conn) +tls_do_recv(nni_tls_conn *conn) { nni_aio *aio; @@ -294,7 +310,7 @@ tls_do_recv(tls_conn *conn) // caller as *soon* as we have some data. nni_aio_list_remove(aio); - if (rv != 0) { + if (rv != NNG_OK) { nni_aio_finish_error(aio, rv); } else { nni_aio_finish(aio, 0, len); @@ -304,7 +320,7 @@ tls_do_recv(tls_conn *conn) // tls_do_send attempts to send user data. static void -tls_do_send(tls_conn *conn) +tls_do_send(nni_tls_conn *conn) { nni_aio *aio; @@ -350,20 +366,47 @@ tls_do_send(tls_conn *conn) } } +nng_err +nni_tls_run(nni_tls_conn *conn) +{ + nni_aio *aio; + nng_err rv; + nni_mtx_lock(&conn->lock); + switch ((rv = tls_handshake(conn))) { + case NNG_OK: + tls_do_recv(conn); + tls_do_send(conn); + break; + case NNG_EAGAIN: + break; + default: + 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); + } + break; + } + nni_mtx_unlock(&conn->lock); + return (rv); +} + static void tls_bio_send_cb(void *arg) { - tls_conn *conn = arg; - nng_aio *aio = &conn->bio_send; - int rv; - size_t count; + nni_tls_conn *conn = arg; + nng_aio *aio = &conn->bio_send; + int rv; + size_t count; - nni_mtx_lock(&conn->lock); + nni_mtx_lock(&conn->bio_lock); conn->bio_send_active = false; if ((rv = nni_aio_result(aio)) != 0) { tls_bio_error(conn, rv); - nni_mtx_unlock(&conn->lock); + nni_mtx_unlock(&conn->bio_lock); + + tls_conn_err(conn, rv); return; } @@ -373,45 +416,37 @@ tls_bio_send_cb(void *arg) conn->bio_send_tail += count; conn->bio_send_tail %= NNG_TLS_MAX_SEND_SIZE; tls_bio_send_start(conn); + nni_mtx_unlock(&conn->bio_lock); - if (tls_do_handshake(conn)) { - tls_do_send(conn); - tls_do_recv(conn); - } - - nni_mtx_unlock(&conn->lock); + nni_tls_run(conn); } static void tls_bio_recv_cb(void *arg) { - tls_conn *conn = arg; - nni_aio *aio = &conn->bio_recv; - int rv; - - nni_mtx_lock(&conn->lock); + nni_tls_conn *conn = arg; + nni_aio *aio = &conn->bio_recv; + int rv; + nni_mtx_lock(&conn->bio_lock); conn->bio_recv_pend = false; if ((rv = nni_aio_result(aio)) != 0) { tls_bio_error(conn, rv); - nni_mtx_unlock(&conn->lock); + nni_mtx_unlock(&conn->bio_lock); + tls_conn_err(conn, rv); return; } NNI_ASSERT(conn->bio_recv_len == 0); NNI_ASSERT(conn->bio_recv_off == 0); conn->bio_recv_len = nni_aio_count(aio); + nni_mtx_unlock(&conn->bio_lock); - if (tls_do_handshake(conn)) { - tls_do_recv(conn); - tls_do_send(conn); - } - - nni_mtx_unlock(&conn->lock); + nni_tls_run(conn); } static void -tls_bio_recv_start(tls_conn *conn) +tls_bio_recv_start(nni_tls_conn *conn) { nng_iov iov; @@ -423,6 +458,9 @@ tls_bio_recv_start(tls_conn *conn) // Already have a receive in flight. return; } + if (conn->bio_closed) { + return; + } conn->bio_recv_off = 0; iov.iov_len = NNG_TLS_MAX_RECV_SIZE; iov.iov_buf = conn->bio_recv_buf; @@ -434,7 +472,7 @@ tls_bio_recv_start(tls_conn *conn) } static void -tls_bio_send_start(tls_conn *conn) +tls_bio_send_start(nni_tls_conn *conn) { nni_iov iov[2]; unsigned nio = 0; @@ -448,6 +486,9 @@ tls_bio_send_start(tls_conn *conn) if (conn->bio_send_len == 0) { return; } + if (conn->bio_closed) { + return; + } len = conn->bio_send_len; head = conn->bio_send_head; tail = conn->bio_send_tail; @@ -478,23 +519,23 @@ tls_bio_send_start(tls_conn *conn) 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->bio_send_head; - size_t tail = conn->bio_send_tail; - size_t space; - size_t cnt; + nni_tls_conn *conn = arg; + size_t len = *szp; + size_t head; + size_t tail; + size_t space; + size_t cnt; + nni_mtx_lock(&conn->bio_lock); + head = conn->bio_send_head; + tail = conn->bio_send_tail; space = NNG_TLS_MAX_SEND_SIZE - conn->bio_send_len; if (space == 0) { + nni_mtx_unlock(&conn->bio_lock); return (NNG_EAGAIN); } - if (conn->closed) { - return (NNG_ECLOSED); - } - if (len > space) { len = space; } @@ -525,20 +566,20 @@ nng_tls_engine_send(void *arg, const uint8_t *buf, size_t *szp) conn->bio_send_head = head; tls_bio_send_start(conn); + nni_mtx_unlock(&conn->bio_lock); return (0); } int nng_tls_engine_recv(void *arg, uint8_t *buf, size_t *szp) { - tls_conn *conn = arg; - size_t len = *szp; + nni_tls_conn *conn = arg; + size_t len = *szp; - if (conn->closed) { - return (NNG_ECLOSED); - } + nni_mtx_lock(&conn->bio_lock); if (conn->bio_recv_len == 0) { tls_bio_recv_start(conn); + nni_mtx_unlock(&conn->bio_lock); return (NNG_EAGAIN); } if (len > conn->bio_recv_len) { @@ -551,6 +592,7 @@ nng_tls_engine_recv(void *arg, uint8_t *buf, size_t *szp) // If we still have data left in the buffer, then the following // call is a no-op. tls_bio_recv_start(conn); + nni_mtx_unlock(&conn->bio_lock); *szp = len; return (0); @@ -805,6 +847,16 @@ nng_tls_engine_register(const nng_tls_engine *engine) return (0); } +size_t +nni_tls_engine_conn_size(void) +{ + const nng_tls_engine *eng; + + eng = nni_atomic_get_ptr(&tls_engine); + + return (eng == NULL ? false : eng->conn_ops->size); +} + #ifdef NNG_TLS_ENGINE_INIT extern int NNG_TLS_ENGINE_INIT(void); #else diff --git a/src/supplemental/tls/tls_common.h b/src/supplemental/tls/tls_common.h index 3e703785..14bb0cf7 100644 --- a/src/supplemental/tls/tls_common.h +++ b/src/supplemental/tls/tls_common.h @@ -39,8 +39,6 @@ // parts of TLS support that are invariant relative to different TLS // libraries, such as dialer and listener support. -static nni_atomic_ptr tls_engine; - struct nng_tls_config { nng_tls_engine_config_ops ops; const nng_tls_engine *engine; // store this so we can verify @@ -78,29 +76,34 @@ typedef struct { nni_tls_bio_ops bio_ops; // lower level ops vector nni_aio bio_send; // lower level send pending nni_aio bio_recv; // lower level recv pending + nni_mtx bio_lock; // lock protecting lower layer operations uint8_t *bio_send_buf; uint8_t *bio_recv_buf; size_t bio_recv_len; size_t bio_recv_off; bool bio_recv_pend; bool bio_send_active; + bool bio_closed; + nng_err bio_err; size_t bio_send_len; size_t bio_send_head; size_t bio_send_tail; nni_reap_node reap; // ... engine connection data follows -} tls_conn; - -extern void nni_tls_fini(tls_conn *conn); -extern int nni_tls_init(tls_conn *conn, nng_tls_config *cfg); -extern int nni_tls_start( - tls_conn *conn, const nni_tls_bio_ops *biops, void *bio); -extern void nni_tls_stop(tls_conn *conn); -extern void nni_tls_close(tls_conn *conn); -extern void nni_tls_recv(tls_conn *conn, nni_aio *aio); -extern void nni_tls_send(tls_conn *conn, nni_aio *aio); -extern bool nni_tls_verified(tls_conn *conn); -extern const char *nni_tls_peer_cn(tls_conn *conn); +} nni_tls_conn; + +extern void nni_tls_fini(nni_tls_conn *conn); +extern int nni_tls_init(nni_tls_conn *conn, nng_tls_config *cfg); +extern int nni_tls_start(nni_tls_conn *conn, const nni_tls_bio_ops *biops, + void *bio, const nng_sockaddr *sa); +extern void nni_tls_stop(nni_tls_conn *conn); +extern void nni_tls_close(nni_tls_conn *conn); +extern void nni_tls_recv(nni_tls_conn *conn, nni_aio *aio); +extern void nni_tls_send(nni_tls_conn *conn, nni_aio *aio); +extern bool nni_tls_verified(nni_tls_conn *conn); +extern const char *nni_tls_peer_cn(nni_tls_conn *conn); +extern nng_err nni_tls_run(nni_tls_conn *conn); +extern size_t nni_tls_engine_conn_size(void); #endif // NNG_TLS_TLS_COMMON_H diff --git a/src/supplemental/tls/tls_engine.h b/src/supplemental/tls/tls_engine.h index bbc5a944..66d40826 100644 --- a/src/supplemental/tls/tls_engine.h +++ b/src/supplemental/tls/tls_engine.h @@ -44,7 +44,10 @@ typedef struct nng_tls_engine_conn_ops_s { // init is used to initialize a connection object. // The passed in connection state will be aligned naturally, // and zeroed. On success this returns 0, else an NNG error code. - int (*init)(nng_tls_engine_conn *, void *, nng_tls_engine_config *); + // The sockaddr is the peer's socket adress (needed for DTLS or + // possibly session resumption.) + int (*init)(nng_tls_engine_conn *, void *, nng_tls_engine_config *, + const nng_sockaddr *); // fini destroys a connection object. This will // be called only when no other external use of the connection @@ -175,7 +178,7 @@ typedef enum nng_tls_engine_version_e { } nng_tls_engine_version; typedef struct nng_tls_engine_s { - // _version is the engine version. This for now must + // version is the engine version. This for now must // be NNG_TLS_ENGINE_VERSION. If the version does not match // then registration of the engine will fail. nng_tls_engine_version version; @@ -212,7 +215,7 @@ extern int nng_tls_engine_register(const nng_tls_engine *); // is the context structure passed in when starting the engine. extern int nng_tls_engine_send(void *, const uint8_t *, size_t *); -// nng_tls_engine_recv is called byu the engine to receive data over +// nng_tls_engine_recv is called by the engine to receive data over // the underlying connection. It returns zero on success, NNG_EAGAIN // if the operation can't be completed yet (there is no data available // for reading), or some other error. On success the count is updated diff --git a/src/supplemental/tls/tls_stream.c b/src/supplemental/tls/tls_stream.c index 8a7f26d8..cd248686 100644 --- a/src/supplemental/tls/tls_stream.c +++ b/src/supplemental/tls/tls_stream.c @@ -110,9 +110,10 @@ tls_stream_recv(void *arg, nng_aio *aio) static void tls_stream_conn_cb(void *arg) { - tls_stream *ts = arg; - nng_stream *bio; - int rv; + tls_stream *ts = arg; + nng_stream *bio; + int rv; + nng_sockaddr sa; if ((rv = nni_aio_result(&ts->conn_aio)) != 0) { nni_aio_finish_error(ts->user_aio, rv); @@ -121,8 +122,13 @@ tls_stream_conn_cb(void *arg) } bio = nni_aio_get_output(&ts->conn_aio, 0); + if ((rv = nng_stream_get_addr(bio, NNG_OPT_REMADDR, &sa)) != 0) { + nni_aio_finish_error(ts->user_aio, rv); + nni_tls_stream_free(ts); + return; + }; - if ((rv = nni_tls_start(&ts->conn, &tls_stream_bio, bio)) != 0) { + if ((rv = nni_tls_start(&ts->conn, &tls_stream_bio, bio, &sa)) != 0) { // NB: if this fails, it *will* have set the bio either way. // So nni_tls_stream_free will also free the bio. nni_aio_finish_error(ts->user_aio, rv); @@ -140,13 +146,12 @@ static nng_err tls_stream_get( int nni_tls_stream_alloc(tls_stream **tsp, nng_tls_config *cfg, nng_aio *user_aio) { - tls_stream *ts; - const nng_tls_engine *eng; - size_t size; - int rv; + tls_stream *ts; + size_t size; + int rv; - eng = cfg->engine; - size = NNI_ALIGN_UP(sizeof(*ts)) + eng->conn_ops->size; + size = NNI_ALIGN_UP(sizeof(*ts)) + + NNI_ALIGN_UP(nni_tls_engine_conn_size()); if ((ts = nni_zalloc(size)) == NULL) { return (NNG_ENOMEM); diff --git a/src/supplemental/tls/tls_stream.h b/src/supplemental/tls/tls_stream.h index 78760f82..dca439af 100644 --- a/src/supplemental/tls/tls_stream.h +++ b/src/supplemental/tls/tls_stream.h @@ -21,7 +21,7 @@ typedef struct tls_stream_s { nni_reap_node reap; nni_aio conn_aio; nni_aio *user_aio; - tls_conn conn; // NB: must be last! + nni_tls_conn conn; // NB: must be last! } tls_stream; extern void nni_tls_stream_free(void *arg); diff --git a/src/supplemental/tls/wolfssl/wolfssl.c b/src/supplemental/tls/wolfssl/wolfssl.c index 1510a02a..3eab0ada 100644 --- a/src/supplemental/tls/wolfssl/wolfssl.c +++ b/src/supplemental/tls/wolfssl/wolfssl.c @@ -155,8 +155,10 @@ wolf_conn_fini(nng_tls_engine_conn *ec) } static int -wolf_conn_init(nng_tls_engine_conn *ec, void *tls, nng_tls_engine_config *cfg) +wolf_conn_init(nng_tls_engine_conn *ec, void *tls, nng_tls_engine_config *cfg, + const nng_sockaddr *sa) { + NNI_ARG_UNUSED(sa); // for now... revisit if we support DTLS ? ec->tls = tls; ec->auth_mode = cfg->auth_mode; -- cgit v1.2.3-70-g09d2