summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGarrett D'Amore <garrett@damore.org>2020-02-08 12:46:47 -0800
committerGarrett D'Amore <garrett@damore.org>2020-02-23 17:06:58 -0800
commitee0b44406d2b658886760ea08c0af12781ab7e3a (patch)
tree674d2d31df7a62c367c161261c942e96f7909166
parent56bcc0310c4710bb21802719566926c2ccd2262a (diff)
downloadnng-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.
-rw-r--r--.gitmodules3
-rw-r--r--CMakeLists.txt67
-rw-r--r--LICENSE.txt2
-rw-r--r--cmake/FindmbedTLS.cmake16
m---------extern/nng-wolfssl0
-rw-r--r--include/nng/supplemental/tls/engine.h207
-rw-r--r--include/nng/supplemental/tls/tls.h34
-rw-r--r--src/CMakeLists.txt1
-rw-r--r--src/core/init.c5
-rw-r--r--src/supplemental/http/http_client.c3
-rw-r--r--src/supplemental/http/http_server.c3
-rw-r--r--src/supplemental/tls/CMakeLists.txt57
-rw-r--r--src/supplemental/tls/mbedtls/CMakeLists.txt36
-rw-r--r--src/supplemental/tls/mbedtls/tls.c1069
-rw-r--r--src/supplemental/tls/none/tls.c154
-rw-r--r--src/supplemental/tls/tls_api.h32
-rw-r--r--src/supplemental/tls/tls_common.c1216
-rw-r--r--src/supplemental/tls/tls_test.c59
-rw-r--r--src/supplemental/tls/wolfssl/CMakeLists.txt26
-rw-r--r--src/transport/tls/CMakeLists.txt5
-rw-r--r--src/transport/tls/tls.c6
-rw-r--r--src/transport/ws/websocket.c60
-rw-r--r--tests/CMakeLists.txt4
-rw-r--r--tests/tls.c168
-rw-r--r--tests/wss.c6
25 files changed, 1987 insertions, 1252 deletions
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 00000000..76205940
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "extern/nng-wolfssl"]
+ path = extern/nng-wolfssl
+ url = https://github.com/staysail/nng-wolfssl
diff --git a/CMakeLists.txt b/CMakeLists.txt
index d71e72b9..9685713b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -25,7 +25,7 @@
# IN THE SOFTWARE.
#
-cmake_minimum_required(VERSION 3.1)
+cmake_minimum_required(VERSION 3.13)
project(nng C)
include(CheckFunctionExists)
@@ -45,6 +45,9 @@ if (POLICY CMP0042)
# Newer cmake on MacOS should use @rpath
cmake_policy(SET CMP0042 NEW)
endif ()
+if (POLICY CMP0079)
+ cmake_policy(SET CMP0079 NEW)
+endif ()
if (POLICY CMP0028)
# Double colon targets are only alias or imports.
@@ -70,7 +73,7 @@ set(NNG_MINOR_VERSION ${CMAKE_MATCH_1})
string(REGEX MATCH "NNG_PATCH_VERSION ([0-9]*)" _ ${nng_ver_h})
set(NNG_PATCH_VERSION ${CMAKE_MATCH_1})
string(REGEX MATCH "NNG_RELEASE_SUFFIX \"([a-z0-9]*)\"" _ ${nng_ver_h})
-if (NOT(${CMAKE_MATCH_1} STREQUAL ""))
+if (NOT (${CMAKE_MATCH_1} STREQUAL ""))
set(NNG_PRERELEASE "-${CMAKE_MATCH_1}")
endif ()
@@ -91,11 +94,14 @@ endif ()
# We only build command line tools and tests if we are not in a
# cross-compile situation. Cross-compiling users who still want to
-# build these must enable them explicitly.
+# build these must enable them explicitly. Some of these switches
+# must be enabled rather early as we use their values later.
option(NNG_TESTS "Build and run tests" ${NNG_NATIVE_BUILD})
option(NNG_TOOLS "Build extra tools" ${NNG_NATIVE_BUILD})
option(NNG_ENABLE_NNGCAT "Enable building nngcat utility." ${NNG_TOOLS})
option(NNG_ENABLE_COVERAGE "Enable coverage reporting." OFF)
+
+
# Enable access to private APIs for our own use.
add_definitions(-DNNG_PRIVATE)
@@ -103,7 +109,8 @@ add_definitions(-DNNG_PRIVATE)
# that have too small defaults. This is not used for Windows,
# which can grow thread stacks sensibly. (Note that NNG can get
# by with a smallish stack, but application callbacks might require
-# larger values if using aio completion callbacks.)
+# larger values if using aio completion callbacks. TLS libraries may
+# require larger stacks however.)
if (NOT WIN32)
option(NNG_SETSTACKSIZE "Use rlimit for thread stack size" OFF)
if (NNG_SETSTACKSIZE)
@@ -112,12 +119,6 @@ if (NOT WIN32)
mark_as_advanced(NNG_SETSTACKSIZE)
endif ()
-option(NNG_ENABLE_TLS "Enable TLS protocol (requires mbedTLS)" OFF)
-if (NNG_ENABLE_TLS)
- add_definitions(-DNNG_SUPP_TLS)
- set(NNG_SUPP_TLS ON)
-endif ()
-
option(NNG_ENABLE_STATS "Enable statistics" ON)
if (NNG_ENABLE_STATS)
add_definitions(-DNNG_ENABLE_STATS)
@@ -358,9 +359,10 @@ endif ()
if (NNG_TESTS)
enable_testing()
set(all_tests, "")
+endif ()
-
- macro(nng_test NAME)
+macro(nng_test NAME)
+ if (NNG_TESTS)
add_executable(${NAME} ${NAME}.c ${ARGN})
target_link_libraries(${NAME} ${PROJECT_NAME}_testlib)
target_include_directories(${NAME} PRIVATE
@@ -369,37 +371,30 @@ if (NNG_TESTS)
${PROJECT_SOURCE_DIR}/include)
add_test(NAME ${NAME} COMMAND ${NAME} -t)
set_tests_properties(${NAME} PROPERTIES TIMEOUT 180)
- endmacro()
+ endif ()
+endmacro()
- function(nng_sources_testlib)
+function(nng_sources_testlib)
+ if (NNG_TESTS)
foreach (f ${ARGN})
target_sources(${PROJECT_NAME}_testlib PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/${f})
endforeach ()
- endfunction()
+ endif ()
+endfunction()
- function(nng_headers_testlib)
+function(nng_headers_testlib)
+ if (NNG_TESTS)
foreach (f ${ARGN})
target_sources(${PROJECT_NAME}_testlib PRIVATE ${PROJECT_SOURCE_DIR}/include/${f})
endforeach ()
- endfunction()
+ endif ()
+endfunction()
- function(nng_defines_testlib)
+function(nng_defines_testlib)
+ if (NNG_TESTS)
target_compile_definitions(${PROJECT_NAME}_testlib PRIVATE ${ARGN})
- endfunction()
-
-else ()
- function(nng_test NAME)
- endfunction()
-
- function(nng_sources_testlib)
- endfunction()
-
- function(nng_headers_testlib)
- endfunction()
-
- function(nng_defines_testlib)
- endfunction()
-endif ()
+ endif ()
+endfunction()
function(nng_sources)
foreach (f ${ARGN})
@@ -467,6 +462,12 @@ if (NNG_ENABLE_NNGCAT)
add_subdirectory(tools/nngcat)
endif ()
+option(NNG_ENABLE_TLS "Enable TLS protocol" OFF)
+if (NNG_ENABLE_TLS)
+ add_definitions(-DNNG_SUPP_TLS)
+ set(NNG_SUPP_TLS ON)
+endif ()
+
add_subdirectory(docs/man)
set(CPACK_PACKAGE_NAME ${PROJECT_NAME})
diff --git a/LICENSE.txt b/LICENSE.txt
index 5cb321f7..67670eec 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,6 +1,6 @@
The MIT License
-Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
+Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
Copyright 2018 Capitar IT Group BV <info@capitar.com>
Permission is hereby granted, free of charge, to any person obtaining a copy
diff --git a/cmake/FindmbedTLS.cmake b/cmake/FindmbedTLS.cmake
index e0c0aa5a..8c25ec9c 100644
--- a/cmake/FindmbedTLS.cmake
+++ b/cmake/FindmbedTLS.cmake
@@ -1,5 +1,5 @@
#
-# Copyright 2017 Garrett D'Amore <garrett@damore.org>
+# Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
# Copyright 2017 Capitar IT Group BV <info@capitar.com>
#
# This software is supplied under the terms of the MIT License, a
@@ -9,21 +9,21 @@
#
#
-# Try to find the mbed TLS libraries.
+# Try to find the Mbed TLS libraries.
#
# Sets the following:
#
-# MBEDTLS_INCLUDE_DIR - Where to find ssl.h, etc.
-# MBEDTLS_FOUND - True if we found mbedtls.
+# MBEDTLS_INCLUDE_DIR - Where to find mbedtls/ssl.h, etc.
+# MBEDTLS_FOUND - True if we found Mbed TLS.
# MBEDTLS_CRYPTO_LIBRARY - The mbedcrypto library.
# MBEDTLS_X509_LIBRARY - The mbedx509 library.
# MBEDTLS_TLS_LIBRARY - The mbedtls library.
-# MBEDTLS_LIBRARIES - List of all three mbedtls libraries.
+# MBEDTLS_LIBRARIES - List of all three Mbed TLS libraries.
# MBEDTLS_VERSION - $major.$minor.$revision (e.g. ``2.6.0``).
#
# Hints:
#
-# Set ``MBEDTLS_ROOT_DIR`` to the root directory of mbed TLS installation.
+# Set ``MBEDTLS_ROOT_DIR`` to the root directory of Mbed TLS installation.
#
set(_MBEDTLS_ROOT_HINTS ${MBEDTLS_ROOT_DIR} ENV MBEDTLS_ROOT_DIR)
@@ -60,12 +60,12 @@ set(MBEDTLS_LIBRARIES
${MBEDTLS_CRYPTO_LIBRARY})
if (${MBEDTLS_TLS_LIBRARY-NOTFOUND})
- message(FATAL_ERROR "Failed to find mbed TLS library")
+ message(FATAL_ERROR "Failed to find Mbed TLS library")
endif()
mark_as_advanced(
MBEDSSL_INCLUDE_DIR
- MBEDTLS_LIBRRIES
+ MBEDTLS_LIBRARIES
MBEDTLS_CRYPTO_LIBRARY
MBEDTLS_X509_LIBRARY
MBEDTLS_TLS_LIBRARY)
diff --git a/extern/nng-wolfssl b/extern/nng-wolfssl
new file mode 160000
+Subproject 428fd4b740481745b57de8e5c84fb0e5ebabd50
diff --git a/include/nng/supplemental/tls/engine.h b/include/nng/supplemental/tls/engine.h
new file mode 100644
index 00000000..81385fbc
--- /dev/null
+++ b/include/nng/supplemental/tls/engine.h
@@ -0,0 +1,207 @@
+//
+// Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
+//
+// 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.
+//
+
+// This file is used to enable external TLS "engines", so
+// that third party TLS libraries can be plugged in
+
+#ifndef NNG_SUPPLEMENTAL_TLS_ENGINE_H
+#define NNG_SUPPLEMENTAL_TLS_ENGINE_H
+
+#include <nng/supplemental/tls/tls.h>
+
+// Locking theory statement for TLS engines. The engine is assumed
+// operate only from the context of threads called by the common
+// framework. That is to say, the callbacks made by the engine
+// should always be on a thread that has context from the framework
+// calling into the engine. This means that the lower level send
+// and receive functions can assume that they have lock ownership
+// inherited on the stack.
+
+// nng_tls_engine_conn represents the engine-specific private
+// state for a TLS connection. It is provided here for type
+// safety. Engine implementations should provide the structure
+// definition locally.
+typedef struct nng_tls_engine_conn nng_tls_engine_conn;
+
+// nng_tls_engine_config represents the engine-specific private
+// state for the TLS configuration. It is provided here for type
+// safety. Engine implementations should provide the structure
+// definition locally.
+typedef struct nng_tls_engine_config nng_tls_engine_config;
+
+typedef struct nng_tls_engine_conn_ops_s {
+ // size is the size of the engine's per-connection state.
+ // The framework will allocate this on behalf of the engine.
+ // Typically this will be sizeof (struct nng_tls_engine_conn).
+ size_t size;
+
+ // 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 *);
+
+ // fini destroys a connection object. This will
+ // be called only when no other external use of the connection
+ // object exists, and only on fully initialed connection objects.
+ void (*fini)(nng_tls_engine_conn *);
+
+ // close closes the connection object, but should not
+ // deallocate any memory. It may also issue a TLS close-notify.
+ void (*close)(nng_tls_engine_conn *);
+
+ // handshake attempts to complete the SSL handshake phase.
+ // It returns zero on success, or an error if one occurred.
+ // The value NNG_EAGAIN should be returned if underlying I/O
+ // is required to be completed first. The framework will
+ // ensure that the handshake completes before sending any data
+ // down.
+ int (*handshake)(nng_tls_engine_conn *);
+
+ // recv attempts to read data (decrypted) from the connection.
+ // It returns 0 on success, otherwise an error. The implementation
+ // should return NNG_EAGAIN if I/O to the underlying stream is
+ // required to complete the operation. On success, the count
+ // is updated to reflect the number of bytes actually received.
+ int (*recv)(nng_tls_engine_conn *, uint8_t *, size_t *);
+
+ // send attempts to write data to the underlying connection.
+ // It returns zero on success, otherwise an error. The implementation
+ // should return NNG_EAGAIN if I/O to the underlying stream is
+ // required to complete the operation. On success, the count
+ // is updated to reflect the number of bytes actually sent.
+ int (*send)(nng_tls_engine_conn *, const uint8_t *, size_t *);
+
+ // verified returns true if the connection is fully
+ // TLS verified, false otherwise.
+ bool (*verified)(nng_tls_engine_conn *);
+} nng_tls_engine_conn_ops;
+
+typedef struct nng_tls_engine_config_ops_s {
+ // size is the size of the engine's configuration object.
+ // The framework will allocate this on behalf of the engine.
+ // Typically this will be sizeof (struct nng_tls_engine_config).
+ size_t size;
+
+ // init prepares the configuration object object.
+ // The mode indicates whether the object should be
+ // initialized for use as a TLS server or client.
+ // The config passed in will be aligned on a 64-bit boundary,
+ // and will be initialized to zero. On success this returns
+ // 0, else an NNG error code.
+ int (*init)(nng_tls_engine_config *, nng_tls_mode);
+
+ // fini is used to tear down the configuration object.
+ // This will only be called on objects that have been properly
+ // initialized with nte_config_init.
+ void (*fini)(nng_tls_engine_config *);
+
+ // server is used to set the server name. This can be used in SNI,
+ // and will also be used on the client to validate the identity.
+ // If this is not set, then no verification will be performed.
+ int (*server)(nng_tls_engine_config *, const char *);
+
+ // auth is used to configure the authentication mode. Values:
+ // NNG_AUTH_MODE_NONE
+ // No validation of the peer is performed. Public facing
+ // servers often use this.
+ // NNG_AUTH_MODE_OPTIONAL
+ // The peer's identity is validated if a certificate is presented.
+ // This is typically useful on servers.
+ // NNG_AUTH_MODE_REQUIRED
+ // The peer's certificate must be present and is verified.
+ // This is standard for the client, and on servers it is used
+ // when client (mutual) authentication is needed.
+ int (*auth)(nng_tls_engine_config *, nng_tls_auth_mode);
+
+ // ca_chain sets the configuration authorities that will be
+ // used to validate peers. An optional CRL is supplied as well.
+ // Both values are C strings (NUL terminated) containing
+ // PEM data. There may be multiple PEM blocks. The
+ // CRL may be NULL if not needed.
+ int (*ca_chain)(nng_tls_engine_config *, const char *, const char *);
+
+ // own_cert configures our identity -- the certificate containing
+ // our public key, our private key (which might be encrypted), and
+ // potentially a password used to decrypt the private key.
+ // All of these are C strings. The cert may actually be a chain
+ // which will be presented to our peer. This function may be
+ // called multiple times to register different keys with different
+ // parameters on a server. (For example, once for RSA parameters,
+ // and again later with EC parameters.) The certificate and the
+ // private key may be presented in the same file. The implementation
+ // is responsible for parsing out the relevant data. If the password
+ // is NULL, then the key file should be unencrypted. The supplied
+ // password may be ignored if the key is not encrypted. Not all
+ // engine implementations need support encryption of the key.
+ int (*own_cert)(
+ nng_tls_engine_config *, const char *, const char *, const char *);
+
+ // version configures the minimum and maximum TLS versions. The
+ // engine should default to supporting TLS1.0 through 1.2, and
+ // optionally 1.3 if it can. The engine should restrict the
+ // the requested range to what it can support -- if no version
+ // within the range is supported (such as if NNG_TLS_1_3 is
+ // specified for both min and max, and the engine lacks support
+ // for v1.3, then NNG_ENOTSUP should be returned.
+ int (*version)(
+ nng_tls_engine_config *, nng_tls_version, nng_tls_version);
+} nng_tls_engine_config_ops;
+
+typedef enum nng_tls_engine_version_e {
+ NNG_TLS_ENGINE_V0 = 0,
+ NNG_TLS_ENGINE_V1 = 1,
+ NNG_TLS_ENGINE_VERSION = NNG_TLS_ENGINE_V1,
+} nng_tls_engine_version;
+
+typedef struct nng_tls_engine_s {
+ // _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;
+
+ // config_ops is the operations for TLS configuration objects.
+ nng_tls_engine_config_ops *config_ops;
+
+ // conn_ops is the operations for TLS connections (stream-oriented).
+ nng_tls_engine_conn_ops *conn_ops;
+
+ // name contains the name of the engine, for example "wolfSSL".
+ // It is acceptable to append a version number as well.
+ const char *name;
+
+ // description contains a human readable description. This can
+ // supply information about the backing library, for example
+ // "mbed TLS v2.7"
+ const char *description;
+
+ // fips_mode is true if the engine is in FIPS mode.
+ // It is expected that this will be enabled either at compile
+ // time, or via environment variables at engine initialization.
+ // FIPS mode cannot be changed once the engine is registered.
+ bool fips_mode;
+} nng_tls_engine;
+
+NNG_DECL int nng_tls_engine_register(const nng_tls_engine *);
+
+// nng_tls_engine_send is called by the engine to send data over the
+// underlying connection. It returns zero on success, NNG_EAGAIN if
+// the operation can't be completed yet (the transport is busy and cannot
+// accept more data yet), or some other error. On success the count is
+// updated with the number of bytes actually sent. The first argument
+// is the context structure passed in when starting the engine.
+NNG_DECL int nng_tls_engine_send(void *, const uint8_t *, size_t *);
+
+// nng_tls_engine_recv is called byu 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
+// with the number of bytes actually received.
+NNG_DECL int nng_tls_engine_recv(void *, uint8_t *, size_t *);
+
+#endif // NNG_SUPPLEMENTAL_TLS_ENGINE_H
diff --git a/include/nng/supplemental/tls/tls.h b/include/nng/supplemental/tls/tls.h
index 5983f3b6..e547f8ee 100644
--- a/include/nng/supplemental/tls/tls.h
+++ b/include/nng/supplemental/tls/tls.h
@@ -1,5 +1,5 @@
//
-// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
//
// This software is supplied under the terms of the MIT License, a
@@ -36,6 +36,17 @@ typedef enum nng_tls_auth_mode {
NNG_TLS_AUTH_MODE_REQUIRED = 2, // Verify cert, close if invalid
} nng_tls_auth_mode;
+// TLS version numbers. We encode the major number and minor number
+// as separate byte fields. No support for SSL 3.0 or earlier -- older
+// versions are known to be insecure and should not be used.
+// When possible applications should restrict themselves to TLS 1.2 or better.
+typedef enum nng_tls_version {
+ NNG_TLS_1_0 = 0x301,
+ NNG_TLS_1_1 = 0x302,
+ NNG_TLS_1_2 = 0x303,
+ NNG_TLS_1_3 = 0x304
+} nng_tls_version;
+
// nng_tls_config_alloc creates a TLS configuration using
// reasonable defaults. This configuration can be shared
// with multiple pipes or services/servers.
@@ -61,7 +72,7 @@ NNG_DECL int nng_tls_config_server_name(nng_tls_config *, const char *);
// of peer certificates. Multiple CAs (and their chains) may be configured
// by either calling this multiple times, or by specifying a list of
// certificates as concatenated data. The final argument is an optional CRL
-// (revokation list) for the CA, also in PEM. Both PEM strings are ASCIIZ
+// (revocation list) for the CA, also in PEM. Both PEM strings are ASCIIZ
// format (except that the CRL may be NULL).
NNG_DECL int nng_tls_config_ca_chain(
nng_tls_config *, const char *, const char *);
@@ -105,6 +116,25 @@ NNG_DECL int nng_tls_config_ca_file(nng_tls_config *, const char *);
NNG_DECL int nng_tls_config_cert_key_file(
nng_tls_config *, const char *, const char *);
+// Configure supported TLS version. By default we usually restrict
+// ourselves to TLS 1.2 and newer. We do not support older versions.
+// If the implementation cannot support any version (for example if
+// the minimum requested is 1.3 but the TLS implementation lacks support
+// for TLS 1.3) then NNG_ENOTSUP will be returned.
+NNG_DECL int nng_tls_config_version(
+ nng_tls_config *, nng_tls_version, nng_tls_version);
+
+// nng_tls_engine_name returns the "name" of the TLS engine. If no
+// TLS engine support is enabled, then "none" is returned.
+NNG_DECL const char *nng_tls_engine_name(void);
+
+// nng_tls_engine_description returns the "description" of the TLS engine.
+// If no TLS engine support is enabled, then an empty string is returned.
+NNG_DECL const char *nng_tls_engine_description(void);
+
+// nng_tls_engine_fips_mode returns true if the engine is in FIPS 140-2 mode.
+NNG_DECL bool nng_tls_engine_fips_mode(void);
+
#ifdef __cplusplus
}
#endif
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 1b177aa1..3f5f148c 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -285,7 +285,6 @@ set(NNG_DEFS ${NNG_DEFS} PARENT_SCOPE)
set(NNG_SUPP_BASE64 ${NNG_SUPP_BASE64} PARENT_SCOPE)
set(NNG_SUPP_HTTP ${NNG_SUPP_HTTP} PARENT_SCOPE)
set(NNG_SUPP_SHA1 ${NNG_SUPP_SHA1} PARENT_SCOPE)
-set(NNG_SUPP_TLS ${NNG_SUPP_TLS} PARENT_SCOPE)
set(NNG_SUPP_WEBSOCKET ${NNG_SUPP_WEBSOCKET} PARENT_SCOPE)
# Configure files
diff --git a/src/core/init.c b/src/core/init.c
index 4749516f..7be879f5 100644
--- a/src/core/init.c
+++ b/src/core/init.c
@@ -18,6 +18,9 @@ static nni_mtx nni_init_mtx;
static nni_list nni_init_list;
static bool nni_inited = false;
+extern int nni_tls_sys_init(void);
+extern void nni_tls_sys_fini(void);
+
static int
nni_init_helper(void)
{
@@ -36,6 +39,7 @@ nni_init_helper(void)
((rv = nni_listener_sys_init()) != 0) ||
((rv = nni_dialer_sys_init()) != 0) ||
((rv = nni_pipe_sys_init()) != 0) ||
+ ((rv = nni_tls_sys_init()) != 0) ||
((rv = nni_proto_sys_init()) != 0) ||
((rv = nni_tran_sys_init()) != 0)) {
nni_fini();
@@ -71,6 +75,7 @@ nni_fini(void)
}
nni_tran_sys_fini();
nni_proto_sys_fini();
+ nni_tls_sys_fini();
nni_pipe_sys_fini();
nni_dialer_sys_fini();
nni_listener_sys_fini();
diff --git a/src/supplemental/http/http_client.c b/src/supplemental/http/http_client.c
index ecba84ae..68f0f61c 100644
--- a/src/supplemental/http/http_client.c
+++ b/src/supplemental/http/http_client.c
@@ -9,13 +9,10 @@
// found online at https://opensource.org/licenses/MIT.
//
-#include <ctype.h>
#include <stdbool.h>
-#include <stdio.h>
#include <string.h>
#include "core/nng_impl.h"
-#include "supplemental/tls/tls_api.h"
#include <nng/supplemental/tls/tls.h>
diff --git a/src/supplemental/http/http_server.c b/src/supplemental/http/http_server.c
index d3b595fd..1b90c172 100644
--- a/src/supplemental/http/http_server.c
+++ b/src/supplemental/http/http_server.c
@@ -1,5 +1,5 @@
//
-// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
// Copyright 2018 QXSoftware <lh563566994@126.com>
// Copyright 2019 Devolutions <info@devolutions.net>
@@ -19,7 +19,6 @@
#include "core/nng_impl.h"
#include "nng/supplemental/tls/tls.h"
-#include "supplemental/tls/tls_api.h"
#include "http_api.h"
diff --git a/src/supplemental/tls/CMakeLists.txt b/src/supplemental/tls/CMakeLists.txt
index dfeb6526..61d9f2fb 100644
--- a/src/supplemental/tls/CMakeLists.txt
+++ b/src/supplemental/tls/CMakeLists.txt
@@ -1,6 +1,6 @@
#
+# Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
# Copyright 2018 Capitar IT Group BV <info@capitar.com>
-# Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
# Copyright 2019 Devolutions <info@devolutions.net>
#
@@ -11,38 +11,29 @@
# found online at https://opensource.org/licenses/MIT.
#
-set(_SRCS ${PROJECT_SOURCE_DIR}/include/nng/supplemental/tls/tls.h)
-
-if (NNG_SUPP_TLS)
- set(NNG_SUPP_TLS_MBEDTLS ON)
- set(_DEFS -DNNG_SUPP_TLS)
-
- list(APPEND _SRCS supplemental/tls/tls_common.c)
-endif()
-
-# For now we only support the ARM mbedTLS library.
-if (NNG_SUPP_TLS_MBEDTLS)
-
- message(WARNING "
- ************************************************************
- Linking against mbedTLS changes license terms (Apache 2.0).
- Consult a lawyer and the license files for details.
- ************************************************************")
-
- # If mbedTLS was added by a consuming project, then we should use that
- # instance of it, instead of configuring our own.
- if (TARGET mbedtls)
- set(_LIBS mbedtls)
- else()
- find_package(mbedTLS REQUIRED)
- set(_LIBS ${MBEDTLS_LIBRARIES})
- set(_INCS ${MBEDTLS_INCLUDE_DIR})
- endif()
- list(APPEND _SRCS supplemental/tls/mbedtls/tls.c)
-
-else()
- list(APPEND _SRCS supplemental/tls/none/tls.c)
-endif()
+
+if (NNG_ENABLE_TLS)
+ set(NNG_TLS_ENGINES mbed wolf none)
+ # We assume Mbed for now. (Someday replaced perhaps with Bear.)
+ set(NNG_TLS_ENGINE mbed CACHE STRING "TLS engine to use.")
+ set_property(CACHE NNG_TLS_ENGINE PROPERTY STRINGS ${NNG_TLS_ENGINES})
+else ()
+ set(NNG_TLS_ENGINE none)
+endif ()
+
+# default TLS implementation for now is Mbed.
+nng_headers(nng/supplemental/tls/tls.h)
+nng_headers(nng/supplemental/tls/engine.h)
+
+if (NOT NNG_TLS_ENGINE STREQUAL "none")
+ nng_test(tls_test)
+endif ()
+
+add_subdirectory(mbedtls)
+add_subdirectory(wolfssl)
+
+nng_sources(tls_common.c)
+nng_sources(tls_api.h)
list(APPEND NNG_DEFS ${_DEFS})
list(APPEND NNG_SRCS ${_SRCS})
diff --git a/src/supplemental/tls/mbedtls/CMakeLists.txt b/src/supplemental/tls/mbedtls/CMakeLists.txt
new file mode 100644
index 00000000..22c8e1c6
--- /dev/null
+++ b/src/supplemental/tls/mbedtls/CMakeLists.txt
@@ -0,0 +1,36 @@
+#
+# Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
+#
+# 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.
+#
+
+if (NNG_TLS_ENGINE STREQUAL "mbed")
+ message(WARNING "
+ ************************************************************
+ Linking against Mbed TLS changes license terms (Apache 2.0).
+ Consult a lawyer and the license files for details.
+ ************************************************************")
+ nng_sources(tls.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)
+
+ # If Mbed TLS was added by a consuming project, then we should use that
+ # instance of it, instead of configuring our own.
+ if (TARGET mbedtls)
+ set(_LIBS mbedtls)
+ else()
+ find_package(mbedTLS REQUIRED)
+ set(_LIBS ${MBEDTLS_LIBRARIES})
+ set(_INCS ${MBEDTLS_INCLUDE_DIR})
+ endif()
+
+ list(APPEND NNG_LIBS ${_LIBS})
+ list(APPEND NNG_INCS ${_INCS})
+
+ set(NNG_LIBS ${NNG_LIBS} PARENT_SCOPE)
+ set(NNG_INCS ${NNG_INCS} PARENT_SCOPE)
+endif()
diff --git a/src/supplemental/tls/mbedtls/tls.c b/src/supplemental/tls/mbedtls/tls.c
index b0ac6ec5..e378d304 100644
--- a/src/supplemental/tls/mbedtls/tls.c
+++ b/src/supplemental/tls/mbedtls/tls.c
@@ -28,88 +28,35 @@
#include "mbedtls/ssl.h"
#include "core/nng_impl.h"
-#include "core/tcp.h"
-#include "supplemental/tls/tls_api.h"
-
-// Implementation note. This implementation buffers data between the TLS
-// encryption layer (mbedTLS) and the underlying TCP socket. As a result,
-// there may be some additional latency caused by buffer draining and
-// refilling. In the future we might want to investigate some kind of
-// double buffer policy to allow data to flow without entering a true
-// empty state.
-
-// NNG_TLS_MAX_SEND_SIZE limits the amount of data we will buffer for sending,
-// exerting backpressure 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
+#include <nng/supplemental/tls/engine.h>
-typedef struct nni_tls_certkey {
+// pair holds a private key and the associated certificate.
+typedef struct {
mbedtls_x509_crt crt;
mbedtls_pk_context key;
nni_list_node node;
-} nni_tls_certkey;
+} pair;
-typedef struct {
- nni_tls_common com;
- nng_stream * tcp;
- mbedtls_ssl_context ctx;
- nng_tls_config * cfg; // kept so we can release it
- nni_mtx lk;
- nni_aio * tcp_send;
- nni_aio * tcp_recv;
- bool sending;
- bool recving;
- bool closed;
- bool hsdone;
- bool tls_closed; // upper TLS layer closed
- bool tcp_closed; // underlying TCP buffer closed
- uint8_t * sendbuf; // send buffer
- size_t sendlen; // amount of data in send buffer
- size_t sendoff; // offset of start of send data
- uint8_t * recvbuf; // recv buffer
- size_t recvlen; // amount of data in recv buffer
- size_t recvoff; // offset of start of recv data
- nni_list sends; // upper side sends
- nni_list recvs; // upper recv aios
- nni_aio * handshake; // handshake aio (upper)
- nni_reap_item reap;
-} tls;
-
-struct nng_tls_config {
- mbedtls_ssl_config cfg_ctx;
- nni_mtx lk;
- bool active;
- char * server_name;
#ifdef NNG_TLS_USE_CTR_DRBG
- mbedtls_ctr_drbg_context rng_ctx;
- nni_mtx rng_lk;
+// 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
- mbedtls_x509_crt ca_certs;
- mbedtls_x509_crl crl;
- nni_atomic_u64 refcnt;
-
- nni_list certkeys;
+struct nng_tls_engine_conn {
+ void * tls; // parent conn
+ mbedtls_ssl_context ctx;
};
-static void tls_send_cb(void *);
-static void tls_recv_cb(void *);
-
-static void tls_do_send(tls *);
-static void tls_do_recv(tls *);
-static void tls_do_handshake(tls *);
-
-static int tls_net_send(void *, const unsigned char *, size_t);
-static int tls_net_recv(void *, unsigned char *, size_t);
+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;
+ nni_list pairs;
+};
static void
tls_dbg(void *ctx, int level, const char *file, int line, const char *s)
@@ -141,115 +88,18 @@ static int
tls_random(void *arg, unsigned char *buf, size_t sz)
{
#ifdef NNG_TLS_USE_CTR_DRBG
- int rv;
- nng_tls_config *cfg = arg;
- NNI_ARG_UNUSED(arg);
+ int rv;
- nni_mtx_lock(&cfg->rng_lk);
- rv = mbedtls_ctr_drbg_random(&cfg->rng_ctx, buf, sz);
- nni_mtx_unlock(&cfg->rng_lk);
+ 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
}
-void
-nni_tls_config_fini(nng_tls_config *cfg)
-{
- nni_tls_certkey *ck;
-
- if (cfg != NULL) {
- if (nni_atomic_dec64_nv(&cfg->refcnt) != 0) {
- return;
- }
-
- 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 ((ck = nni_list_first(&cfg->certkeys))) {
- nni_list_remove(&cfg->certkeys, ck);
- mbedtls_x509_crt_free(&ck->crt);
- mbedtls_pk_free(&ck->key);
-
- NNI_FREE_STRUCT(ck);
- }
- nni_mtx_fini(&cfg->lk);
- NNI_FREE_STRUCT(cfg);
- }
-}
-
-int
-nni_tls_config_init(nng_tls_config **cpp, enum nng_tls_mode mode)
-{
- nng_tls_config *cfg;
- int rv;
- int sslmode;
- int authmode;
-
- if ((cfg = NNI_ALLOC_STRUCT(cfg)) == NULL) {
- return (NNG_ENOMEM);
- }
- nni_atomic_init64(&cfg->refcnt);
- nni_atomic_inc64(&cfg->refcnt);
- nni_mtx_init(&cfg->lk);
- if (mode == NNG_TLS_MODE_SERVER) {
- sslmode = MBEDTLS_SSL_IS_SERVER;
- authmode = MBEDTLS_SSL_VERIFY_NONE;
- } else {
- sslmode = MBEDTLS_SSL_IS_CLIENT;
- authmode = MBEDTLS_SSL_VERIFY_REQUIRED;
- }
-
- NNI_LIST_INIT(&cfg->certkeys, nni_tls_certkey, 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, sslmode,
- MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
- if (rv != 0) {
- nni_tls_config_fini(cfg);
- return (rv);
- }
-
- mbedtls_ssl_conf_authmode(&cfg->cfg_ctx, authmode);
-
- // We *require* TLS v1.2 or newer, which is also known as SSL v3.3.
- mbedtls_ssl_conf_min_version(&cfg->cfg_ctx,
- MBEDTLS_SSL_MAJOR_VERSION_3, MBEDTLS_SSL_MINOR_VERSION_3);
-
-#ifdef NNG_TLS_USE_CTR_DRBG
- mbedtls_ctr_drbg_init(&cfg->rng_ctx);
- rv = mbedtls_ctr_drbg_seed(
- &cfg->rng_ctx, tls_get_entropy, NULL, NULL, 0);
- if (rv != 0) {
- nni_tls_config_fini(cfg);
- return (rv);
- }
-#endif
- mbedtls_ssl_conf_rng(&cfg->cfg_ctx, tls_random, cfg);
-
- mbedtls_ssl_conf_dbg(&cfg->cfg_ctx, tls_dbg, cfg);
-
- *cpp = cfg;
- return (0);
-}
-
-void
-nni_tls_config_hold(nng_tls_config *cfg)
-{
- nni_atomic_inc64(&cfg->refcnt);
-}
-
-// tls_mkerr converts an mbed error to an NNG error. In all cases
-// we just encode with NNG_ETRANERR.
+// tls_mk_err converts an mbed error to an NNG error.
static struct {
int tls;
int nng;
@@ -267,7 +117,7 @@ static struct {
};
static int
-tls_mkerr(int err)
+tls_mk_err(int err)
{
for (int i = 0; tls_errs[i].tls != 0; i++) {
if (tls_errs[i].tls == err) {
@@ -277,762 +127,433 @@ tls_mkerr(int err)
return (NNG_ECRYPTO);
}
-// The common code should call this only after it has released
-// it's upper layer stuff.
-static void
-tls_reap(void *arg)
+static int
+net_send(void *tls, const unsigned char *buf, size_t len)
{
- tls *tls = arg;
+ size_t sz = len;
+ int rv;
- // Shut it all down first.
- if (tls->tcp != NULL) {
- nng_stream_close(tls->tcp);
- }
- nni_aio_stop(tls->tcp_send);
- nni_aio_stop(tls->tcp_recv);
- nni_aio_free(tls->com.aio);
-
- // And finalize / free everything.
- nng_stream_free(tls->tcp);
- nni_aio_free(tls->tcp_send);
- nni_aio_free(tls->tcp_recv);
- mbedtls_ssl_free(&tls->ctx);
- nng_tls_config_free(tls->com.cfg);
-
- if (tls->recvbuf != NULL) {
- nni_free(tls->recvbuf, NNG_TLS_MAX_RECV_SIZE);
- }
- if (tls->sendbuf != NULL) {
- nni_free(tls->sendbuf, NNG_TLS_MAX_RECV_SIZE);
+ 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);
}
- nni_mtx_fini(&tls->lk);
- memset(tls, 0xff, sizeof(*tls));
- NNI_FREE_STRUCT(tls);
-}
-
-static void
-tls_free(void *arg)
-{
- tls *tls = arg;
- nni_reap(&tls->reap, tls_reap, tls);
}
-int
-nni_tls_start(nng_stream *arg, nng_stream *tcp)
+static int
+net_recv(void *tls, unsigned char *buf, size_t len)
{
- tls * tp = (void *) arg;
- nng_tls_config *cfg = tp->com.cfg;
- int rv;
-
- if ((tp->recvbuf = nni_zalloc(NNG_TLS_MAX_RECV_SIZE)) == NULL) {
- return (NNG_ENOMEM);
- }
- if ((tp->sendbuf = nni_zalloc(NNG_TLS_MAX_SEND_SIZE)) == NULL) {
- return (NNG_ENOMEM);
- }
-
- nni_mtx_lock(&cfg->lk);
- // No more changes allowed to config.
- cfg->active = true;
- nni_mtx_unlock(&cfg->lk);
-
- mbedtls_ssl_init(&tp->ctx);
- mbedtls_ssl_set_bio(&tp->ctx, tp, tls_net_send, tls_net_recv, NULL);
-
- if ((rv = mbedtls_ssl_setup(&tp->ctx, &cfg->cfg_ctx)) != 0) {
- return (tls_mkerr(rv));
- }
-
- if (cfg->server_name) {
- mbedtls_ssl_set_hostname(&tp->ctx, cfg->server_name);
- }
-
- tp->tcp = tcp;
+ size_t sz = len;
+ int rv;
- if (((rv = nni_aio_alloc(&tp->tcp_send, tls_send_cb, tp)) != 0) ||
- ((rv = nni_aio_alloc(&tp->tcp_recv, tls_recv_cb, tp)) != 0)) {
- return (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);
}
-
- nni_mtx_lock(&tp->lk);
- // Kick off a handshake operation.
- tls_do_handshake(tp);
- nni_mtx_unlock(&tp->lk);
-
- return (0);
}
static void
-tls_cancel(nni_aio *aio, void *arg, int rv)
+conn_fini(nng_tls_engine_conn *ec)
{
- tls *tp = arg;
- nni_mtx_lock(&tp->lk);
- if (nni_aio_list_active(aio)) {
- nni_aio_list_remove(aio);
- nni_aio_finish_error(aio, rv);
- }
- nni_mtx_unlock(&tp->lk);
+ mbedtls_ssl_free(&ec->ctx);
}
-// tls_send_cb is called when the underlying TCP send completes.
-static void
-tls_send_cb(void *ctx)
-{
- tls * tp = ctx;
- nni_aio *aio = tp->tcp_send;
-
- nni_mtx_lock(&tp->lk);
- if (nni_aio_result(aio) != 0) {
- nng_stream_close(tp->tcp);
- tp->tcp_closed = true;
- } else {
- size_t n = nni_aio_count(aio);
- NNI_ASSERT(tp->sendlen >= n);
- tp->sendlen -= n;
- if (tp->sendlen) {
- nni_iov iov;
- tp->sendoff += n;
- iov.iov_buf = tp->sendbuf + tp->sendoff;
- iov.iov_len = tp->sendlen;
- nni_aio_set_iov(aio, 1, &iov);
- nni_aio_set_timeout(aio, NNG_DURATION_INFINITE);
- nng_stream_send(tp->tcp, aio);
- nni_mtx_unlock(&tp->lk);
- return;
- }
- tp->sendoff = 0;
- tp->sending = false;
- }
-
- tls_do_handshake(tp);
- if (tp->hsdone) {
- tls_do_send(tp);
- tls_do_recv(tp);
- }
- nni_mtx_unlock(&tp->lk);
-}
-
-static void
-tls_recv_start(tls *tp)
+// The common code should call this only after it has released
+// it's upper layer stuff.
+int
+conn_init(nng_tls_engine_conn *ec, void *tls, nng_tls_engine_config *cfg)
{
- nni_aio *aio;
- nni_iov iov;
+ int rv;
- if (tp->recving || tp->tcp_closed) {
- return;
- }
- // If we already have data, wait for that to be consumed before
- // doing another read.
- if (tp->recvlen != 0) {
- return;
- }
+ ec->tls = tls;
- tp->recving = 1;
- tp->recvoff = 0;
- aio = tp->tcp_recv;
- iov.iov_buf = tp->recvbuf;
- iov.iov_len = NNG_TLS_MAX_RECV_SIZE;
- nni_aio_set_iov(aio, 1, &iov);
- nni_aio_set_timeout(tp->tcp_recv, NNG_DURATION_INFINITE);
- nng_stream_recv(tp->tcp, aio);
-}
+ mbedtls_ssl_init(&ec->ctx);
+ mbedtls_ssl_set_bio(&ec->ctx, tls, net_send, net_recv, NULL);
-static void
-tls_recv_cb(void *ctx)
-{
- tls * tp = ctx;
- nni_aio *aio = tp->tcp_recv;
-
- nni_mtx_lock(&tp->lk);
- tp->recving = false;
- if (nni_aio_result(aio) != 0) {
- // Close the underlying TCP channel, but permit data we
- // already received to continue to be received.
- nng_stream_close(tp->tcp);
- tp->tcp_closed = true;
- } else {
- NNI_ASSERT(tp->recvlen == 0);
- NNI_ASSERT(tp->recvoff == 0);
- tp->recvlen = nni_aio_count(aio);
+ if ((rv = mbedtls_ssl_setup(&ec->ctx, &cfg->cfg_ctx)) != 0) {
+ return (tls_mk_err(rv));
}
- // If we were closed (above), the upper layer will detect and
- // react properly. Otherwise the upper layer will consume
- // data.
- tls_do_handshake(tp);
- if (tp->hsdone) {
- tls_do_recv(tp);
- tls_do_send(tp);
+ if (cfg->server_name != NULL) {
+ mbedtls_ssl_set_hostname(&ec->ctx, cfg->server_name);
}
- nni_mtx_unlock(&tp->lk);
+ return (0);
}
-// This handles the bottom half send (i.e. sending over TCP).
-// We always accept a chunk of data, to a limit, if the bottom
-// sender is not busy. Then we handle that in the background.
-// If the sender *is* busy, we return MBEDTLS_ERR_SSL_WANT_WRITE.
-// The chunk size we accept is 64k at a time, which prevents
-// ridiculous over queueing. This is always called with the pipe
-// lock held, and never blocks.
-static int
-tls_net_send(void *ctx, const unsigned char *buf, size_t len)
+static void
+conn_close(nng_tls_engine_conn *ec)
{
- tls * tp = ctx;
- nni_iov iov;
-
- if (len > NNG_TLS_MAX_SEND_SIZE) {
- len = NNG_TLS_MAX_SEND_SIZE;
- }
-
- // We should already be running with the pipe lock held,
- // as we are running in that context.
-
- if (tp->sending) {
- return (MBEDTLS_ERR_SSL_WANT_WRITE);
- }
- if (tp->tcp_closed) {
- return (MBEDTLS_ERR_NET_SEND_FAILED);
- }
-
- tp->sending = 1;
- tp->sendlen = len;
- tp->sendoff = 0;
- memcpy(tp->sendbuf, buf, len);
- iov.iov_buf = tp->sendbuf;
- iov.iov_len = len;
- nni_aio_set_iov(tp->tcp_send, 1, &iov);
- nni_aio_set_timeout(tp->tcp_send, NNG_DURATION_INFINITE);
- nng_stream_send(tp->tcp, tp->tcp_send);
- return (len);
+ // 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
-tls_net_recv(void *ctx, unsigned char *buf, size_t len)
+conn_recv(nng_tls_engine_conn *ec, uint8_t *buf, size_t *szp)
{
- tls *tp = ctx;
-
- // We should already be running with the pipe lock held,
- // as we are running in that context.
-
- if (tp->recvlen == 0) {
- if (tp->tcp_closed) {
- // The underlying TCP transport has closed, and we
- // have no more data in our receive buffer.
- return (MBEDTLS_ERR_NET_RECV_FAILED);
- }
- len = MBEDTLS_ERR_SSL_WANT_READ;
- } else {
- if (len > tp->recvlen) {
- len = tp->recvlen;
+ 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));
}
- memcpy(buf, tp->recvbuf + tp->recvoff, len);
- tp->recvoff += len;
- tp->recvlen -= len;
- }
-
- tls_recv_start(tp);
- return ((int) len);
-}
-
-static void
-tls_send(void *arg, nni_aio *aio)
-{
- int rv;
- tls *tp = arg;
-
- if (nni_aio_begin(aio) != 0) {
- return;
- }
- nni_mtx_lock(&tp->lk);
- if (tp->tls_closed) {
- nni_mtx_unlock(&tp->lk);
- nni_aio_finish_error(aio, NNG_ECLOSED);
- return;
- }
- if ((rv = nni_aio_schedule(aio, tls_cancel, tp)) != 0) {
- nni_mtx_unlock(&tp->lk);
- nni_aio_finish_error(aio, rv);
- return;
}
- nni_list_append(&tp->sends, aio);
- tls_do_send(tp);
- nni_mtx_unlock(&tp->lk);
-}
-
-static void
-tls_recv(void *arg, nni_aio *aio)
-{
- int rv;
- tls *tp = arg;
-
- if (nni_aio_begin(aio) != 0) {
- return;
- }
- nni_mtx_lock(&tp->lk);
- if (tp->tls_closed) {
- nni_mtx_unlock(&tp->lk);
- nni_aio_finish_error(aio, NNG_ECLOSED);
- return;
- }
- if ((rv = nni_aio_schedule(aio, tls_cancel, tp)) != 0) {
- nni_mtx_unlock(&tp->lk);
- nni_aio_finish_error(aio, rv);
- return;
- }
-
- nni_list_append(&tp->recvs, aio);
- tls_do_recv(tp);
- nni_mtx_unlock(&tp->lk);
-}
-
-static int
-tls_get_verified(void *arg, void *buf, size_t *szp, nni_type t)
-{
- tls *tp = arg;
- bool v = (mbedtls_ssl_get_verify_result(&tp->ctx) == 0);
-
- return (nni_copyout_bool(v, buf, szp, t));
+ *szp = (size_t) rv;
+ return (0);
}
-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)
+conn_send(nng_tls_engine_conn *ec, const uint8_t *buf, size_t *szp)
{
- tls * tp = arg;
- int rv;
- nng_stream *tcp;
-
- tcp = (tp != NULL) ? tp->tcp : NULL;
+ int rv;
- if ((rv = nni_stream_setx(tcp, name, buf, sz, t)) != NNG_ENOTSUP) {
- return (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));
+ }
}
- return (nni_setopt(tls_options, name, tp, buf, sz, t));
+ *szp = (size_t) rv;
+ return (0);
}
static int
-tls_getx(void *arg, const char *name, void *buf, size_t *szp, nni_type t)
-{
- tls *tp = arg;
- int rv;
-
- if ((rv = nni_stream_getx(tp->tcp, name, buf, szp, t)) !=
- NNG_ENOTSUP) {
- return (rv);
- }
- return (nni_getopt(tls_options, name, tp, buf, szp, t));
-}
-
-static void
-tls_do_handshake(tls *tp)
+conn_handshake(nng_tls_engine_conn *ec)
{
- int rv;
- nni_aio *aio;
+ int rv;
- if (tp->hsdone || tp->tls_closed) {
- return;
- }
- rv = mbedtls_ssl_handshake(&tp->ctx);
+ 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;
+ return (NNG_EAGAIN);
case 0:
// The handshake is done, yay!
- tp->hsdone = true;
- return;
+ return (0);
default:
- // some other error occurred, this causes us to tear it down
- nng_stream_close(tp->tcp);
- tp->tls_closed = true;
- tp->tcp_closed = true;
- rv = tls_mkerr(rv);
-
- while (((aio = nni_list_first(&tp->recvs)) != NULL) ||
- ((aio = nni_list_first(&tp->sends)) != NULL)) {
- nni_aio_list_remove(aio);
- nni_aio_finish_error(aio, rv);
- }
+ return (tls_mk_err(rv));
}
}
-// tls_do_send is called to try to send more data if we have not
-// yet completed the I/O. It also completes any transactions that
-// *have* completed. It must be called with the lock held.
-static void
-tls_do_send(tls *tp)
+static bool
+conn_verified(nng_tls_engine_conn *ec)
{
- nni_aio *aio;
-
- while ((aio = nni_list_first(&tp->sends)) != NULL) {
- int n;
- uint8_t *buf = NULL;
- size_t len = 0;
- nni_iov *iov;
- unsigned niov;
-
- nni_aio_get_iov(aio, &niov, &iov);
-
- for (unsigned i = 0; i < niov; 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);
- nni_aio_finish_error(aio, NNG_EINVAL);
- continue;
- }
-
- n = mbedtls_ssl_write(&tp->ctx, buf, len);
-
- if ((n == MBEDTLS_ERR_SSL_WANT_WRITE) ||
- (n == MBEDTLS_ERR_SSL_WANT_READ)) {
- // Cannot send any more data right now, wait
- // for callback.
- return;
- }
- // Some other error occurred... this is not good.
- // Want better diagnostics.
- nni_aio_list_remove(aio);
- if (n < 0) {
- nni_aio_finish_error(aio, tls_mkerr(n));
- } else {
- nni_aio_finish(aio, 0, n);
- }
- }
+ return (mbedtls_ssl_get_verify_result(&ec->ctx) == 0);
}
static void
-tls_do_recv(tls *tp)
+config_fini(nng_tls_engine_config *cfg)
{
- nni_aio *aio;
-
- while ((aio = nni_list_first(&tp->recvs)) != NULL) {
- int n;
- uint8_t *buf = NULL;
- size_t len = 0;
- nni_iov *iov;
- unsigned niov;
-
- nni_aio_get_iov(aio, &niov, &iov);
-
- for (unsigned i = 0; i < niov; 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);
- nni_aio_finish_error(aio, NNG_EINVAL);
- continue;
- }
- n = mbedtls_ssl_read(&tp->ctx, buf, len);
-
- if ((n == MBEDTLS_ERR_SSL_WANT_READ) ||
- (n == MBEDTLS_ERR_SSL_WANT_WRITE)) {
- // Cannot receive any more data right now, wait
- // for callback.
- return;
- }
+ pair *p;
- nni_aio_list_remove(aio);
+ 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))) {
+ nni_list_remove(&cfg->pairs, p);
+ mbedtls_x509_crt_free(&p->crt);
+ mbedtls_pk_free(&p->key);
- if (n < 0) {
- nni_aio_finish_error(aio, tls_mkerr(n));
- } else {
- nni_aio_finish(aio, 0, n);
- }
+ NNI_FREE_STRUCT(p);
}
}
-static void
-tls_close(void *arg)
+static int
+config_init(nng_tls_engine_config *cfg, enum nng_tls_mode mode)
{
- tls * tp = arg;
- nni_aio *aio;
+ int rv;
+ int ssl_mode;
+ int auth_mode;
- nni_aio_close(tp->tcp_send);
- nni_aio_close(tp->tcp_recv);
+ 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;
+ }
- nni_mtx_lock(&tp->lk);
- tp->tls_closed = true;
+ NNI_LIST_INIT(&cfg->pairs, pair, node);
+ mbedtls_ssl_config_init(&cfg->cfg_ctx);
+ mbedtls_x509_crt_init(&cfg->ca_certs);
+ mbedtls_x509_crl_init(&cfg->crl);
- while (((aio = nni_list_first(&tp->sends)) != NULL) ||
- ((aio = nni_list_first(&tp->recvs)) != NULL)) {
- nni_aio_list_remove(aio);
- nni_aio_finish_error(aio, NNG_ECLOSED);
+ rv = mbedtls_ssl_config_defaults(&cfg->cfg_ctx, ssl_mode,
+ MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
+ if (rv != 0) {
+ config_fini(cfg);
+ return (rv);
}
- if (tp->hsdone) {
- // 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(&tp->ctx);
- } else {
- nng_stream_close(tp->tcp);
- }
- nni_mtx_unlock(&tp->lk);
-}
+ mbedtls_ssl_conf_authmode(&cfg->cfg_ctx, auth_mode);
-// This allocates a TLS structure, that can be used by the caller.
-// The reason we have this API is so that the base structure can be
-// embedded in the parent structure.
-int
-nni_tls_alloc(nng_stream **tlsp)
-{
- tls *tp;
+ // Default: we *require* TLS v1.2 or newer, which is also known as
+ // SSL v3.3. As of this writing, Mbed TLS still does not support
+ // version 1.3, and we would want to test it before enabling it here.
+ cfg->min_ver = MBEDTLS_SSL_MINOR_VERSION_3;
+ cfg->max_ver = MBEDTLS_SSL_MINOR_VERSION_3;
+
+ 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 ((tp = NNI_ALLOC_STRUCT(tp)) == NULL) {
- return (NNG_ENOMEM);
- }
- nni_aio_list_init(&tp->sends);
- nni_aio_list_init(&tp->recvs);
- nni_mtx_init(&tp->lk);
- tp->com.ops.s_close = tls_close;
- tp->com.ops.s_free = tls_free;
- tp->com.ops.s_send = tls_send;
- tp->com.ops.s_recv = tls_recv;
- tp->com.ops.s_getx = tls_getx;
- tp->com.ops.s_setx = tls_setx;
-
- *tlsp = (void *) tp;
return (0);
}
-int
-nng_tls_config_server_name(nng_tls_config *cfg, const char *name)
+static int
+config_server_name(nng_tls_engine_config *cfg, const char *name)
{
- int rv;
- nni_mtx_lock(&cfg->lk);
- if (cfg->active) {
- nni_mtx_unlock(&cfg->lk);
- return (NNG_ESTATE);
+ char *dup;
+ if ((dup = strdup(name)) == NULL) {
+ return (NNG_ENOMEM);
}
if (cfg->server_name) {
nni_strfree(cfg->server_name);
}
- cfg->server_name = nni_strdup(name);
- rv = cfg->server_name == NULL ? NNG_ENOMEM : 0;
- nni_mtx_unlock(&cfg->lk);
- return (rv);
+ cfg->server_name = dup;
+ return (0);
}
-int
-nng_tls_config_auth_mode(nng_tls_config *cfg, nng_tls_auth_mode mode)
+static int
+config_auth_mode(nng_tls_engine_config *cfg, nng_tls_auth_mode mode)
{
- nni_mtx_lock(&cfg->lk);
- if (cfg->active) {
- nni_mtx_unlock(&cfg->lk);
- return (NNG_ESTATE);
- }
switch (mode) {
case NNG_TLS_AUTH_MODE_NONE:
mbedtls_ssl_conf_authmode(
&cfg->cfg_ctx, MBEDTLS_SSL_VERIFY_NONE);
- break;
+ return (0);
case NNG_TLS_AUTH_MODE_OPTIONAL:
mbedtls_ssl_conf_authmode(
&cfg->cfg_ctx, MBEDTLS_SSL_VERIFY_OPTIONAL);
- break;
+ return (0);
case NNG_TLS_AUTH_MODE_REQUIRED:
mbedtls_ssl_conf_authmode(
&cfg->cfg_ctx, MBEDTLS_SSL_VERIFY_REQUIRED);
- break;
- default:
- nni_mtx_unlock(&cfg->lk);
- return (NNG_EINVAL);
+ return (0);
}
- nni_mtx_unlock(&cfg->lk);
- return (0);
+ return (NNG_EINVAL);
}
-int
-nng_tls_config_ca_chain(
- nng_tls_config *cfg, const char *certs, const char *crl)
+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.
- nni_mtx_lock(&cfg->lk);
- if (cfg->active) {
- rv = NNG_ESTATE;
- goto err;
- }
pem = (const uint8_t *) certs;
len = strlen(certs) + 1;
if ((rv = mbedtls_x509_crt_parse(&cfg->ca_certs, pem, len)) != 0) {
- rv = tls_mkerr(rv);
- goto err;
+ 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) {
- rv = tls_mkerr(rv);
- goto err;
+ return (tls_mk_err(rv));
}
}
mbedtls_ssl_conf_ca_chain(&cfg->cfg_ctx, &cfg->ca_certs, &cfg->crl);
-
-err:
- nni_mtx_unlock(&cfg->lk);
- return (rv);
+ return (0);
}
-int
-nng_tls_config_own_cert(
- nng_tls_config *cfg, const char *cert, const char *key, const char *pass)
+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;
- nni_tls_certkey *ck;
- int rv;
+ size_t len;
+ const uint8_t *pem;
+ pair * p;
+ int rv;
- if ((ck = NNI_ALLOC_STRUCT(ck)) == NULL) {
+ if ((p = NNI_ALLOC_STRUCT(p)) == NULL) {
return (NNG_ENOMEM);
}
- mbedtls_x509_crt_init(&ck->crt);
- mbedtls_pk_init(&ck->key);
+ 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(&ck->crt, pem, len)) != 0) {
- rv = tls_mkerr(rv);
+ if ((rv = mbedtls_x509_crt_parse(&p->crt, pem, len)) != 0) {
+ rv = tls_mk_err(rv);
goto err;
}
pem = (const uint8_t *) key;
len = strlen(key) + 1;
- rv = mbedtls_pk_parse_key(&ck->key, pem, len, (const uint8_t *) pass,
+ rv = mbedtls_pk_parse_key(&p->key, pem, len, (const uint8_t *) pass,
pass != NULL ? strlen(pass) : 0);
if (rv != 0) {
- rv = tls_mkerr(rv);
+ rv = tls_mk_err(rv);
goto err;
}
- nni_mtx_lock(&cfg->lk);
- if (cfg->active) {
- nni_mtx_unlock(&cfg->lk);
- rv = NNG_ESTATE;
- goto err;
- }
- rv = mbedtls_ssl_conf_own_cert(&cfg->cfg_ctx, &ck->crt, &ck->key);
+ rv = mbedtls_ssl_conf_own_cert(&cfg->cfg_ctx, &p->crt, &p->key);
if (rv != 0) {
- nni_mtx_unlock(&cfg->lk);
- rv = tls_mkerr(rv);
+ rv = tls_mk_err(rv);
goto err;
}
// Save this structure so we can free it with the context.
- nni_list_append(&cfg->certkeys, ck);
- nni_mtx_unlock(&cfg->lk);
+ nni_list_append(&cfg->pairs, p);
return (0);
err:
- mbedtls_x509_crt_free(&ck->crt);
- mbedtls_pk_free(&ck->key);
- NNI_FREE_STRUCT(ck);
+ mbedtls_x509_crt_free(&p->crt);
+ mbedtls_pk_free(&p->key);
+ NNI_FREE_STRUCT(p);
return (rv);
}
-int
-nng_tls_config_ca_file(nng_tls_config *cfg, const char *path)
+static int
+config_version(nng_tls_engine_config *cfg, nng_tls_version min_ver,
+ nng_tls_version max_ver)
{
- int rv;
- void * fdata;
- size_t fsize;
- char * pem;
- // Note that while mbedTLS supports its own file methods, we want
- // to avoid depending on that because it might not have been
- // included, so we use our own. We have to read the file, and
- // then allocate a buffer that has an extra byte so we can
- // ensure NUL termination. The file named by path may contain
- // both a ca chain, and crl chain, or just a ca chain.
- if ((rv = nni_file_get(path, &fdata, &fsize)) != 0) {
- return (rv);
+ int v1, v2;
+ int maj = MBEDTLS_SSL_MAJOR_VERSION_3;
+
+ if (min_ver > max_ver) {
+ return (NNG_ENOTSUP);
}
- if ((pem = nni_zalloc(fsize + 1)) == NULL) {
- nni_free(fdata, fsize);
- return (NNG_ENOMEM);
+ switch (min_ver) {
+ case NNG_TLS_1_0:
+ v1 = MBEDTLS_SSL_MINOR_VERSION_1;
+ break;
+ case NNG_TLS_1_1:
+ v1 = MBEDTLS_SSL_MINOR_VERSION_2;
+ break;
+ case NNG_TLS_1_2:
+ v1 = MBEDTLS_SSL_MINOR_VERSION_3;
+ break;
+ default:
+ return (NNG_ENOTSUP);
}
- memcpy(pem, fdata, fsize);
- nni_free(fdata, fsize);
- 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);
+
+ switch (max_ver) {
+ case NNG_TLS_1_0:
+ v2 = MBEDTLS_SSL_MINOR_VERSION_1;
+ break;
+ case NNG_TLS_1_1:
+ v2 = MBEDTLS_SSL_MINOR_VERSION_2;
+ break;
+ case NNG_TLS_1_2:
+ case NNG_TLS_1_3: // We lack support for 1.3, so treat as 1.2.
+ v2 = MBEDTLS_SSL_MINOR_VERSION_3;
+ 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.
+ return (NNG_ENOTSUP);
}
- nni_free(pem, fsize + 1);
- return (rv);
+
+ 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,
+ .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,
+};
+
+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_config_cert_key_file(
- nng_tls_config *cfg, const char *path, const char *pass)
+nng_tls_engine_init_mbed(void)
{
- int rv;
- void * fdata;
- size_t fsize;
- char * pem;
-
- // Note that while mbedTLS supports its own file methods, we want
- // to avoid depending on that because it might not have been
- // included, so we use our own. We have to read the file, and
- // then allocate a buffer that has an extra byte so we can
- // ensure NUL termination. The file named by path must contain
- // both our certificate, and our private key. The password
- // may be NULL if the key is not encrypted.
- if ((rv = nni_file_get(path, &fdata, &fsize)) != 0) {
+ 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) {
+ nni_mtx_fini(&rng_lock);
return (rv);
}
- if ((pem = nni_zalloc(fsize + 1)) == NULL) {
- nni_free(fdata, fsize);
- return (NNG_ENOMEM);
- }
- memcpy(pem, fdata, fsize);
- nni_free(fdata, fsize);
- rv = nng_tls_config_own_cert(cfg, pem, pem, pass);
- nni_free(pem, fsize + 1);
- 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(3);
-int
-nng_tls_config_alloc(nng_tls_config **cfgp, nng_tls_mode mode)
-{
- return (nni_tls_config_init(cfgp, mode));
-}
+ rv = nng_tls_engine_register(&tls_engine_mbed);
-void
-nng_tls_config_free(nng_tls_config *cfg)
-{
- nni_tls_config_fini(cfg);
+#ifdef NNG_TLS_USE_CTR_DRBG
+ if (rv != 0) {
+ nni_mtx_fini(&rng_lock);
+ }
+#endif
+
+ return (rv);
}
void
-nng_tls_config_hold(nng_tls_config *cfg)
+nng_tls_engine_fini_mbed(void)
{
- nni_tls_config_hold(cfg);
-}
+#ifdef NNG_TLS_USE_CTR_DRBG
+ mbedtls_ctr_drbg_free(&rng_ctx);
+ nni_mtx_fini(&rng_lock);
+#endif
+} \ No newline at end of file
diff --git a/src/supplemental/tls/none/tls.c b/src/supplemental/tls/none/tls.c
deleted file mode 100644
index a1e70b73..00000000
--- a/src/supplemental/tls/none/tls.c
+++ /dev/null
@@ -1,154 +0,0 @@
-//
-// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech>
-// Copyright 2018 Capitar IT Group BV <info@capitar.com>
-// Copyright 2018 Devolutions <info@devolutions.net>
-//
-// 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 <stdbool.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-// This file is only used when TLS support is not build into the library.
-// We provide stub functions only to satisfy linkage.
-
-#include "core/nng_impl.h"
-#include "supplemental/tls/tls_api.h"
-
-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
-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);
-} \ No newline at end of file
diff --git a/src/supplemental/tls/tls_api.h b/src/supplemental/tls/tls_api.h
index 4e6146b1..642b9072 100644
--- a/src/supplemental/tls/tls_api.h
+++ b/src/supplemental/tls/tls_api.h
@@ -1,5 +1,5 @@
//
-// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
// Copyright 2019 Devolutions <info@devolutions.net>
//
@@ -14,40 +14,10 @@
#include <nng/supplemental/tls/tls.h>
-// This nni_tls_common structure represents the "base" structure for
-// an implementation to extend. One of these must be the first member
-// of the implementation specific TLS stream struct.
-typedef struct {
- nng_stream ops;
- nni_aio * aio; // system aio for connect/accept
- nni_aio * uaio; // user aio for connect/accept
- nng_tls_config *cfg;
-} nni_tls_common;
-
// The implementation supplies this function to create the TLS connection
// object. All fields will be zeroed.
-extern int nni_tls_alloc(nng_stream **);
extern int nni_tls_dialer_alloc(nng_stream_dialer **, const nng_url *);
extern int nni_tls_listener_alloc(nng_stream_listener **, const nng_url *);
extern int nni_tls_checkopt(const char *, const void *, size_t, nni_type);
-// nni_tls_start is called by the common TLS dialer/listener completions
-// to start the TLS stream activity. This may also do allocations, etc.
-extern int nni_tls_start(nng_stream *, nng_stream *);
-
-// nni_tls_config_init creates a new TLS configuration object.
-// The object is created with a reference count of one.
-extern int nni_tls_config_init(nng_tls_config **, nng_tls_mode);
-
-// nni_tls_config_fini drops the reference on the configuration
-// object, deallocating if this was the last reference.
-extern void nni_tls_config_fini(nng_tls_config *);
-
-// nni_tls_config_hold is used to get a hold on the config
-// object, preventing it from being released inadvertently.
-// The hold is released with a call to nng_tls_config_fini().
-// Note that a hold need not be acquired at creation, since
-// the configuration object is created with a hold on it.
-extern void nni_tls_config_hold(nng_tls_config *);
-
#endif // NNG_SUPPLEMENTAL_TLS_TLS_API_H
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
diff --git a/src/supplemental/tls/tls_test.c b/src/supplemental/tls/tls_test.c
new file mode 100644
index 00000000..bd96ae08
--- /dev/null
+++ b/src/supplemental/tls/tls_test.c
@@ -0,0 +1,59 @@
+//
+// Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
+//
+// 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 <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <nng/nng.h>
+#include <nng/supplemental/tls/tls.h>
+#include <testutil.h>
+
+#include <acutest.h>
+
+void
+test_tls_config_version(void)
+{
+ nng_tls_config *cfg;
+
+ TEST_NNG_PASS(nng_tls_config_alloc(&cfg, NNG_TLS_MODE_SERVER));
+
+ // Verify that min ver < max ver
+ TEST_NNG_FAIL(nng_tls_config_version(cfg, NNG_TLS_1_3, NNG_TLS_1_0),
+ NNG_ENOTSUP);
+
+ // Verify that we cannot configure SSL 3.0 or older.
+ TEST_NNG_FAIL(
+ nng_tls_config_version(cfg, NNG_TLS_1_0 - 1, NNG_TLS_1_0),
+ NNG_ENOTSUP);
+
+ // Verify that we cannot configure TLS > 1.3.
+ TEST_NNG_FAIL(
+ nng_tls_config_version(cfg, NNG_TLS_1_0, NNG_TLS_1_3 + 1),
+ NNG_ENOTSUP);
+
+ // Verify that we *can* configure some various ranges.
+ TEST_NNG_PASS(nng_tls_config_version(cfg, NNG_TLS_1_0, NNG_TLS_1_0));
+ TEST_NNG_PASS(nng_tls_config_version(cfg, NNG_TLS_1_0, NNG_TLS_1_1));
+ TEST_NNG_PASS(nng_tls_config_version(cfg, NNG_TLS_1_0, NNG_TLS_1_2));
+ TEST_NNG_PASS(nng_tls_config_version(cfg, NNG_TLS_1_0, NNG_TLS_1_3));
+ TEST_NNG_PASS(nng_tls_config_version(cfg, NNG_TLS_1_1, NNG_TLS_1_1));
+ TEST_NNG_PASS(nng_tls_config_version(cfg, NNG_TLS_1_1, NNG_TLS_1_2));
+ TEST_NNG_PASS(nng_tls_config_version(cfg, NNG_TLS_1_1, NNG_TLS_1_3));
+ TEST_NNG_PASS(nng_tls_config_version(cfg, NNG_TLS_1_2, NNG_TLS_1_2));
+ TEST_NNG_PASS(nng_tls_config_version(cfg, NNG_TLS_1_2, NNG_TLS_1_3));
+
+ nng_tls_config_free(cfg);
+}
+
+TEST_LIST = {
+ { "tls config version", test_tls_config_version },
+ { NULL, NULL },
+}; \ No newline at end of file
diff --git a/src/supplemental/tls/wolfssl/CMakeLists.txt b/src/supplemental/tls/wolfssl/CMakeLists.txt
new file mode 100644
index 00000000..f0b48d9a
--- /dev/null
+++ b/src/supplemental/tls/wolfssl/CMakeLists.txt
@@ -0,0 +1,26 @@
+#
+# Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
+#
+# 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.
+#
+
+# Enabling wolfSSL requires that the extern/nng-wolfssl submodule
+# be populated. Note also that this action changes the licensing
+# restrictions around the resulting library!
+
+if (NNG_TLS_ENGINE STREQUAL "wolf")
+ add_subdirectory(${PROJECT_SOURCE_DIR}/extern/nng-wolfssl nng-wolfssl)
+ target_include_directories(nng-wolfssl PRIVATE )
+ target_link_libraries(nng PRIVATE nng-wolfssl)
+ if (TARGET nng_testlib)
+ target_link_libraries(nng_testlib PRIVATE nng-wolfssl)
+ endif ()
+
+ nng_defines(NNG_TLS_ENGINE_INIT=nng_tls_engine_init_wolf)
+ nng_defines(NNG_TLS_ENGINE_FINI=nng_tls_engine_fini_wolf)
+ nng_defines(NNG_SUPP_TLS)
+
+endif ()
diff --git a/src/transport/tls/CMakeLists.txt b/src/transport/tls/CMakeLists.txt
index 1603908a..d77a67cd 100644
--- a/src/transport/tls/CMakeLists.txt
+++ b/src/transport/tls/CMakeLists.txt
@@ -1,5 +1,5 @@
#
-# Copyright 2019 Staysail Systems, Inc. <info@staysail.tech>
+# Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
# Copyright 2018 Capitar IT Group BV <info@capitar.com>
#
# This software is supplied under the terms of the MIT License, a
@@ -9,8 +9,7 @@
#
# TLS transport
-CMAKE_DEPENDENT_OPTION(NNG_TRANSPORT_TLS "Enable TLS transport" ON
- "NNG_ENABLE_TLS" OFF)
+option (NNG_TRANSPORT_TLS "Enable TLS transport." ON)
mark_as_advanced(NNG_TRANSPORT_TLS)
nng_sources_if(NNG_TRANSPORT_TLS tls.c)
diff --git a/src/transport/tls/tls.c b/src/transport/tls/tls.c
index 7869c380..46ff8117 100644
--- a/src/transport/tls/tls.c
+++ b/src/transport/tls/tls.c
@@ -10,14 +10,12 @@
//
#include <stdbool.h>
-#include <stdio.h>
#include <string.h>
#include "core/nng_impl.h"
#include "nng/supplemental/tls/tls.h"
#include "nng/transport/tls/tls.h"
-#include "supplemental/tls/tls_api.h"
// TLS over TCP transport. Platform specific TCP operations must be
// supplied as well, and uses the supplemental TLS v1.2 code. It is not
@@ -49,7 +47,6 @@ struct tlstran_pipe {
size_t gotrxhead;
size_t wanttxhead;
size_t wantrxhead;
- nni_aio * useraio;
nni_aio * txaio;
nni_aio * rxaio;
nni_aio * negoaio;
@@ -821,7 +818,6 @@ error:
nni_aio_finish_error(aio, rv);
}
nni_mtx_unlock(&ep->mtx);
- return;
}
static int
@@ -868,7 +864,7 @@ tlstran_ep_init_dialer(void **dp, nni_url *url, nni_dialer *ndialer)
}
if ((rv = tlstran_url_parse_source(&myurl, &srcsa, url)) != 0) {
- return (NNG_EADDRINVAL);
+ return (rv);
}
if (((rv = tlstran_ep_init(&ep, url, sock)) != 0) ||
diff --git a/src/transport/ws/websocket.c b/src/transport/ws/websocket.c
index e70b1fd4..e2578f32 100644
--- a/src/transport/ws/websocket.c
+++ b/src/transport/ws/websocket.c
@@ -1,5 +1,5 @@
//
-// Copyright 2019 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
// Copyright 2019 Devolutions <info@devolutions.net>
//
@@ -11,12 +11,9 @@
#include <stdbool.h>
#include <stdio.h>
-#include <stdlib.h>
#include <string.h>
#include "core/nng_impl.h"
-#include "supplemental/http/http_api.h"
-#include "supplemental/tls/tls_api.h"
#include "supplemental/websocket/websocket.h"
#include <nng/supplemental/tls/tls.h>
@@ -27,33 +24,27 @@ typedef struct ws_listener ws_listener;
typedef struct ws_pipe ws_pipe;
struct ws_dialer {
- uint16_t lproto; // local protocol
- uint16_t rproto; // remote protocol
+ uint16_t peer; // remote protocol
nni_list aios;
nni_mtx mtx;
nni_aio * connaio;
nng_stream_dialer *dialer;
bool started;
- nni_dialer * ndialer;
};
struct ws_listener {
- uint16_t lproto; // local protocol
- uint16_t rproto; // remote protocol
+ uint16_t peer; // remote protocol
nni_list aios;
nni_mtx mtx;
nni_aio * accaio;
nng_stream_listener *listener;
bool started;
- nni_listener * nlistener;
};
struct ws_pipe {
nni_mtx mtx;
- nni_pipe * npipe;
bool closed;
- uint16_t rproto;
- uint16_t lproto;
+ uint16_t peer;
nni_aio * user_txaio;
nni_aio * user_rxaio;
nni_aio * txaio;
@@ -193,10 +184,10 @@ wstran_pipe_stop(void *arg)
}
static int
-wstran_pipe_init(void *arg, nni_pipe *npipe)
+wstran_pipe_init(void *arg, nni_pipe *pipe)
{
- ws_pipe *p = arg;
- p->npipe = npipe;
+ NNI_ARG_UNUSED(arg);
+ NNI_ARG_UNUSED(pipe);
return (0);
}
@@ -254,7 +245,7 @@ wstran_pipe_peer(void *arg)
{
ws_pipe *p = arg;
- return (p->rproto);
+ return (p->peer);
}
static int
@@ -426,8 +417,7 @@ wstran_connect_cb(void *arg)
nng_stream_free(ws);
nni_aio_finish_error(uaio, rv);
} else {
- p->rproto = d->rproto;
- p->lproto = d->lproto;
+ p->peer = d->peer;
nni_aio_set_output(uaio, 0, p);
nni_aio_finish(uaio, 0, 0);
@@ -478,8 +468,7 @@ wstran_accept_cb(void *arg)
nng_stream_close(ws);
nni_aio_finish_error(uaio, rv);
} else {
- p->rproto = l->rproto;
- p->lproto = l->lproto;
+ p->peer = l->peer;
nni_aio_set_output(uaio, 0, p);
nni_aio_finish(uaio, 0, 0);
@@ -498,7 +487,7 @@ wstran_dialer_init(void **dp, nng_url *url, nni_dialer *ndialer)
ws_dialer *d;
nni_sock * s = nni_dialer_sock(ndialer);
int rv;
- char prname[64];
+ char name[64];
if ((d = NNI_ALLOC_STRUCT(d)) == NULL) {
return (NNG_ENOMEM);
@@ -507,11 +496,10 @@ wstran_dialer_init(void **dp, nng_url *url, nni_dialer *ndialer)
nni_aio_list_init(&d->aios);
- d->lproto = nni_sock_proto_id(s);
- d->rproto = nni_sock_peer_id(s);
- d->ndialer = ndialer;
+ d->peer = nni_sock_peer_id(s);
- snprintf(prname, sizeof(prname), "%s.sp.nanomsg.org",
+ snprintf(
+ name, sizeof(name), "%s.sp.nanomsg.org",
nni_sock_peer_name(s));
if (((rv = nni_ws_dialer_alloc(&d->dialer, url)) != 0) ||
@@ -519,7 +507,7 @@ wstran_dialer_init(void **dp, nng_url *url, nni_dialer *ndialer)
((rv = nng_stream_dialer_set_bool(
d->dialer, NNI_OPT_WS_MSGMODE, true)) != 0) ||
((rv = nng_stream_dialer_set_string(
- d->dialer, NNG_OPT_WS_PROTOCOL, prname)) != 0)) {
+ d->dialer, NNG_OPT_WS_PROTOCOL, name)) != 0)) {
wstran_dialer_fini(d);
return (rv);
}
@@ -529,12 +517,12 @@ wstran_dialer_init(void **dp, nng_url *url, nni_dialer *ndialer)
}
static int
-wstran_listener_init(void **lp, nng_url *url, nni_listener *nlistener)
+wstran_listener_init(void **lp, nng_url *url, nni_listener *listener)
{
ws_listener *l;
int rv;
- nni_sock * s = nni_listener_sock(nlistener);
- char prname[64];
+ nni_sock * s = nni_listener_sock(listener);
+ char name[64];
if ((l = NNI_ALLOC_STRUCT(l)) == NULL) {
return (NNG_ENOMEM);
@@ -543,11 +531,9 @@ wstran_listener_init(void **lp, nng_url *url, nni_listener *nlistener)
nni_aio_list_init(&l->aios);
- l->lproto = nni_sock_proto_id(s);
- l->rproto = nni_sock_peer_id(s);
- l->nlistener = nlistener;
+ l->peer = nni_sock_peer_id(s);
- snprintf(prname, sizeof(prname), "%s.sp.nanomsg.org",
+ snprintf(name, sizeof(name), "%s.sp.nanomsg.org",
nni_sock_proto_name(s));
if (((rv = nni_ws_listener_alloc(&l->listener, url)) != 0) ||
@@ -555,7 +541,7 @@ wstran_listener_init(void **lp, nng_url *url, nni_listener *nlistener)
((rv = nng_stream_listener_set_bool(
l->listener, NNI_OPT_WS_MSGMODE, true)) != 0) ||
((rv = nng_stream_listener_set_string(
- l->listener, NNG_OPT_WS_PROTOCOL, prname)) != 0)) {
+ l->listener, NNG_OPT_WS_PROTOCOL, name)) != 0)) {
wstran_listener_fini(l);
return (rv);
}
@@ -637,7 +623,7 @@ wstran_listener_setopt(
return (rv);
}
-static nni_chkoption wstran_checkopts[] = {
+static nni_chkoption wstran_check_opts[] = {
{
.o_name = NULL,
},
@@ -647,7 +633,7 @@ static int
wstran_checkopt(const char *name, const void *buf, size_t sz, nni_type t)
{
int rv;
- rv = nni_chkopt(wstran_checkopts, name, buf, sz, t);
+ rv = nni_chkopt(wstran_check_opts, name, buf, sz, t);
if (rv == NNG_ENOTSUP) {
rv = nni_stream_checkopt("ws", name, buf, sz, t);
}
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 3690a4aa..e258d3ad 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -153,7 +153,7 @@ add_nng_test(scalability 20 ON)
add_nng_test(set_recvmaxsize 2)
add_nng_test1(stats 5 NNG_ENABLE_STATS)
add_nng_test(synch 5)
-add_nng_test1(tls 60 NNG_TRANSPORT_TLS)
+add_nng_test(tls 60)
add_nng_test(tcpsupp 10)
add_nng_test(tcp 180)
add_nng_test(tcp6 60)
@@ -161,7 +161,7 @@ add_nng_test(transport 5)
add_nng_test(udp 5)
add_nng_test(url 5)
add_nng_test(ws 30)
-add_nng_test1(wss 30 NNG_TRANSPORT_WSS)
+add_nng_test(wss 30)
add_nng_test1(zt 60 NNG_TRANSPORT_ZEROTIER)
add_nng_test(bus 5)
diff --git a/tests/tls.c b/tests/tls.c
index 6dfcaf01..59525089 100644
--- a/tests/tls.c
+++ b/tests/tls.c
@@ -1,6 +1,6 @@
//
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
-// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
//
// This software is supplied under the terms of the MIT License, a
// copy of which should be located in the distribution where this
@@ -29,78 +29,38 @@
//
// Generated using openssl:
//
-// % openssl rsa -genkey -out key.key
+// % openssl ecparam -name secp224r1 -genkey -out key.key
// % openssl req -new -key key.key -out cert.csr -sha256
// % openssl x509 -req -in cert.csr -days 36500 -out cert.crt
// -signkey key.key -sha256
//
-// Relevant metadata:
+// Secp224r1 chosen as a least common denominator recommended by NIST-800.
//
-// Certificate:
-// Data:
-// Version: 1 (0x0)
-// Serial Number: 17127835813110005400 (0xedb24becc3a2be98)
-// Signature Algorithm: sha256WithRSAEncryption
-// Issuer: C=US, ST=CA, L=San Diego, O=nanomsg.org, CN=localhost
-// Validity
-// Not Before: Jan 11 22:34:35 2018 GMT
-// Not After : Dec 18 22:34:35 2117 GMT
-// Subject: C=US, ST=CA, L=San Diego, O=nanomsg.org, CN=localhost
-// Subject Public Key Info:
-// Public Key Algorithm: rsaEncryption
-// Public-Key: (2048 bit)
//
static const char cert[] =
"-----BEGIN CERTIFICATE-----\n"
- "MIIDLjCCAhYCCQDtskvsw6K+mDANBgkqhkiG9w0BAQsFADBYMQswCQYDVQQGEwJV\n"
- "UzELMAkGA1UECAwCQ0ExEjAQBgNVBAcMCVNhbiBEaWVnbzEUMBIGA1UECgwLbmFu\n"
- "b21zZy5vcmcxEjAQBgNVBAMMCWxvY2FsaG9zdDAgFw0xODAxMTEyMjM0MzVaGA8y\n"
- "MTE3MTIxODIyMzQzNVowWDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMRIwEAYD\n"
- "VQQHDAlTYW4gRGllZ28xFDASBgNVBAoMC25hbm9tc2cub3JnMRIwEAYDVQQDDAls\n"
- "b2NhbGhvc3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDMvoHdEnfO\n"
- "hmG3PTj6YC5qz6N5hgmcwf4EZkor4+R1Q5hDOKqOknWmVuGBD5mA61ObK76vycIT\n"
- "Tp+H+vKvfgunySZrlyYg8IbgoDbvVgj9RF8xFHdN0PVeqnkBCsCzLtSu6TP8PSgI\n"
- "SKiRMH0NUSakWqCPEc2E1r1CKdOpa7av/Na30LPsuKFcAUhu7QiVYfER86ktrO8G\n"
- "F2PeVy44Q8RkiLw8uhU0bpAflqkR1KCjOLajw1eL3C+Io75Io8qUOLxWc3LH0hl3\n"
- "oEI0jWu7JYlRAw/O7xm4pcGTwy5L8Odz4a7ZTAmuapFRarGOIcDg8Yr0tllRd1mH\n"
- "1T4Z2Wv7Rs0tAgMBAAEwDQYJKoZIhvcNAQELBQADggEBAIfUXK7UonrYAOrlXUHH\n"
- "gfHNdOXMzQP2Ms6Sxov+1tCTfgsYE65Mggo7hRJUqmKpstpbdRBVXhTyht/xjyTz\n"
- "5sMjoeCyv1tXOHpLTfD3LBXwYZwsFdoLS1UHhD3qiYjCyyY2LWa6S786CtlcbCvu\n"
- "Uij2q8zJ4WFrNqAzxZtsTfg16/6JRFw9zpVSCNlHqCxNQxzWucbmUFTiWn9rnc/N\n"
- "r7utG4JsDPZbEI6QS43R7gGLDF7s0ftWKqzlQiZEtuDQh2p7Uejbft8XmZd/VuV/\n"
- "dFMXOO1rleU0lWAJcXWOWHH3er0fivu2ISL8fRjjikYvhRGxtkwC0kPDa2Ntzgd3\n"
- "Hsg=\n"
+ "MIIBzDCCAXkCCQCNJMf8eYUHxTAKBggqhkjOPQQDAjB2MQswCQYDVQQGEwJVUzEL\n"
+ "MAkGA1UECAwCQ0ExEjAQBgNVBAcMCVNhbiBEaWVnbzEUMBIGA1UECgwLbmFub21z\n"
+ "Zy5vcmcxHDAaBgNVBAsME1NhbXBsZSBDZXJ0aWZpY2F0ZXMxEjAQBgNVBAMMCWxv\n"
+ "Y2FsaG9zdDAgFw0yMDAyMjMxODMwMDZaGA8yMTIwMDEzMDE4MzAwNlowdjELMAkG\n"
+ "A1UEBhMCVVMxCzAJBgNVBAgMAkNBMRIwEAYDVQQHDAlTYW4gRGllZ28xFDASBgNV\n"
+ "BAoMC25hbm9tc2cub3JnMRwwGgYDVQQLDBNTYW1wbGUgQ2VydGlmaWNhdGVzMRIw\n"
+ "EAYDVQQDDAlsb2NhbGhvc3QwTjAQBgcqhkjOPQIBBgUrgQQAIQM6AAS9hA5gYo10\n"
+ "jx+gzJdzYbxHzigJYXawdHtyoAud/TT/dUCt0ycpOzTMiO3CoDNxep+/mkmgxjfp\n"
+ "ujAKBggqhkjOPQQDAgNBADA+Ah0A9b+GcfbhzzmI2NcYb4auE6XTYJPkPzHt6Adi\n"
+ "fwIdAMJO2LEr6WHH6JGLlishVqjF78TtkuB5t+kzneQ=\n"
"-----END CERTIFICATE-----\n";
static const char key[] =
- "-----BEGIN RSA PRIVATE KEY-----\n"
- "MIIEpQIBAAKCAQEAzL6B3RJ3zoZhtz04+mAuas+jeYYJnMH+BGZKK+PkdUOYQziq\n"
- "jpJ1plbhgQ+ZgOtTmyu+r8nCE06fh/ryr34Lp8kma5cmIPCG4KA271YI/URfMRR3\n"
- "TdD1Xqp5AQrAsy7Urukz/D0oCEiokTB9DVEmpFqgjxHNhNa9QinTqWu2r/zWt9Cz\n"
- "7LihXAFIbu0IlWHxEfOpLazvBhdj3lcuOEPEZIi8PLoVNG6QH5apEdSgozi2o8NX\n"
- "i9wviKO+SKPKlDi8VnNyx9IZd6BCNI1ruyWJUQMPzu8ZuKXBk8MuS/Dnc+Gu2UwJ\n"
- "rmqRUWqxjiHA4PGK9LZZUXdZh9U+Gdlr+0bNLQIDAQABAoIBAC82HqvjfkzZH98o\n"
- "9uKFGy72AjQbfEvxT6mkDKZiPmPr2khl4K5Ph2F71zPzbOoVWYoGZEoUs/PPxWmN\n"
- "rDhbUES4VWupxtkBnZheWUyHAjukcG7Y0UnYTTwvAwgCerzWp6RNkfcwAvMmDfis\n"
- "vak8dTSg0TUsXb+r5KhFDNGcTNv3f7R0cJmaZ/t9FT7SerXf1LW7itvTjRor8/ZK\n"
- "KPwT4oklp1o6RFXSenn/e2e3rAjI+TEwJA3Zp5dqO/M/AhaZKVaxL4voDVdVVkT+\n"
- "LHJWVhjLY5ilPkmPWqmZ2reTaF+gGSSjAQ+t/ahGWFqEdWIz9UoXhBBOd1ibeyvd\n"
- "Kyxp1QECgYEA8KcDkmwPrhqFlQe/U+Md27OhrQ4cecLCa6EVLsCXN1bFyCi3NSo2\n"
- "o5zFCC699KOL0ZwSmYlaQP4xjnqv4Gsa0s3uL7tqOJR2UuEtGK/MPMluGHVaWsGt\n"
- "zbnWH3xgsvvsxdt6hInFhcABLDupW336tJ8EcH7mOKoIP+azwF4kPiUCgYEA2c09\n"
- "zJBUW6SZXhgJ5vgENYc+UwDT7pfhIWZaRL+wXnwSoa7igodTKJtQp/KfFBJK4RA0\n"
- "prvwj4Wr/1ScaboR2hYZApbqXU5zkEkjC1hHIbg1fBe0EcnhP7ojMXrk6B5ed+Lq\n"
- "OVdYhUuvtdL/perelmbTJLnb8S214+tzVyg7EGkCgYEA6JLwX8zxpnhZSztOjBr9\n"
- "2zuSb7YojQBNd0kZOLLGMaQ5xwSactYWMi8rOIo76Lc6RFxKmXnl8NP5PtKRMRkx\n"
- "tjNxE05UDNRmOhkGxUn433JoZVjc9sMhXqZQKuPAbJoOLPW9RWQEsgtq1r3eId7x\n"
- "sSfRWYs6od6p1F/4rlwNOMUCgYEAtJmqf+DCAoe3IL3gICRSISy28k7CbZqE9JQR\n"
- "j+Y/Uemh7W29pyydOROoysq1PAh7DKrKbeNzcx8NYxh+5nCC8wrVzD7lsV8nFmJ+\n"
- "655UxVIhD3f8Oa/j1lr7acEU5KCiBtkjDU8vOMBsv+FpWOQrlB1JQa/X/+G+bHLF\n"
- "XmUerNkCgYEAv7R8vIKgJ1f69imgHdB31kue3wnOO/6NlfY3GTcaZcTdChY8SZ5B\n"
- "xits8xog0VcaxXhWlfO0hyCnZ9YRQbyDu0qp5eBU2p3qcE01x4ljJBZUOTweG06N\n"
- "cL9dYcwse5FhNMjrQ/OKv6B38SIXpoKQUtjgkaMtmpK8cXX1eqEMNkM=\n"
- "-----END RSA PRIVATE KEY-----\n";
+ "-----BEGIN EC PARAMETERS-----\n"
+ "gUrgQQAIQ==\n"
+ "-----END EC PARAMETERS-----\n"
+ "-----BEGIN EC PRIVATE KEY-----\n"
+ "MGgCAQEEHChK068x8MWcBzhpO7qANvW4iTo7E0yzMYFXGn+gBwYFK4EEACGhPAM6\n"
+ "AAS9hA5gYo10jx+gzJdzYbxHzigJYXawdHtyoAud/TT/dUCt0ycpOzTMiO3CoDNx\n"
+ "ep+/mkmgxjfpug==\n"
+ "-----END EC PRIVATE KEY-----\n";
static int
check_props_v4(nng_msg *msg)
@@ -146,7 +106,7 @@ check_props_v4(nng_msg *msg)
}
static int
-init_dialer_tls(nng_dialer d)
+init_dialer_tls_ex(nng_dialer d, bool own_cert)
{
nng_tls_config *cfg;
int rv;
@@ -159,10 +119,18 @@ init_dialer_tls(nng_dialer d)
goto out;
}
- if ((rv = nng_tls_config_server_name(cfg, "127.0.0.1")) != 0) {
+ if ((rv = nng_tls_config_server_name(cfg, "localhost")) != 0) {
goto out;
}
- nng_tls_config_auth_mode(cfg, NNG_TLS_AUTH_MODE_NONE);
+ nng_tls_config_auth_mode(cfg, NNG_TLS_AUTH_MODE_REQUIRED);
+
+ if (own_cert) {
+ if ((rv = nng_tls_config_own_cert(cfg, cert, key, NULL)) !=
+ 0) {
+ goto out;
+ }
+ }
+
rv = nng_dialer_setopt_ptr(d, NNG_OPT_TLS_CONFIG, cfg);
out:
@@ -171,7 +139,13 @@ out:
}
static int
-init_listener_tls(nng_listener l)
+init_dialer_tls(nng_dialer d)
+{
+ return (init_dialer_tls_ex(d, false));
+}
+
+static int
+init_listener_tls_ex(nng_listener l, int auth_mode)
{
nng_tls_config *cfg;
int rv;
@@ -185,12 +159,31 @@ init_listener_tls(nng_listener l)
if ((rv = nng_listener_setopt_ptr(l, NNG_OPT_TLS_CONFIG, cfg)) != 0) {
goto out;
}
+ switch (auth_mode) {
+ case NNG_TLS_AUTH_MODE_REQUIRED:
+ case NNG_TLS_AUTH_MODE_OPTIONAL:
+ if ((rv = nng_tls_config_ca_chain(cfg, cert, NULL)) != 0) {
+ goto out;
+ }
+ break;
+ default:
+ break;
+ }
+ if ((rv = nng_tls_config_auth_mode(cfg, auth_mode)) != 0) {
+ goto out;
+ }
out:
nng_tls_config_free(cfg);
return (0);
}
static int
+init_listener_tls(nng_listener l)
+{
+ return (init_listener_tls_ex(l, NNG_TLS_AUTH_MODE_NONE));
+}
+
+static int
init_dialer_tls_file(nng_dialer d)
{
int rv;
@@ -265,6 +258,10 @@ init_listener_tls_file(nng_listener l)
TestMain("TLS Transport", {
static trantest tt;
+ if (strcmp(nng_tls_engine_name(), "none") == 0) {
+ Skip("TLS not enabled");
+ }
+
tt.dialer_init = init_dialer_tls;
tt.listener_init = init_listener_tls;
tt.tmpl = "tls+tcp://127.0.0.1:%u";
@@ -314,7 +311,7 @@ TestMain("TLS Transport", {
So(nng_dialer_start(d, 0) == 0);
});
- Convey("We can bind to port zero", {
+ SkipConvey("We can bind to port zero", {
nng_socket s1;
nng_socket s2;
nng_listener l;
@@ -389,7 +386,7 @@ TestMain("TLS Transport", {
So(nng_dialer_start(d, 0) == 0);
});
- Convey("Botched local interfaces fail resonably", {
+ Convey("Botched local interfaces fail reasonably", {
nng_socket s1;
So(nng_pair_open(&s1) == 0);
@@ -407,15 +404,13 @@ TestMain("TLS Transport", {
NNG_EADDRINVAL);
});
-#if 0
-// We really need to have pipe start/negotiate as one of the key steps during
-// connect establish. Until that happens, we cannot verify the peer.
-// See bug #208.
- Convey("Verify works", {
+ // We really need to have pipe start/negotiate as one of the key steps
+ // during connect establish. Until that happens, we cannot verify the
+ // peer. See bug #208.
+ SkipConvey("Verify works", {
nng_socket s1;
nng_socket s2;
nng_listener l;
- char * buf;
size_t sz;
char addr[NNG_MAXADDRLEN];
@@ -438,11 +433,10 @@ TestMain("TLS Transport", {
So(nng_dial(s2, addr, NULL, 0) == NNG_EPEERAUTH);
});
-#endif
Convey("No verify works", {
- nng_socket s1;
- nng_socket s2;
+ nng_socket s1; // server
+ nng_socket s2; // client
nng_listener l;
char addr[NNG_MAXADDRLEN];
nng_msg * msg;
@@ -459,6 +453,8 @@ TestMain("TLS Transport", {
trantest_next_address(addr, "tls+tcp://*:%u");
So(nng_listener_create(&l, s1, addr) == 0);
So(init_listener_tls_file(l) == 0);
+ So(nng_listener_setopt_int(l, NNG_OPT_TLS_AUTH_MODE,
+ NNG_TLS_AUTH_MODE_OPTIONAL) == 0);
So(nng_listener_start(l, 0) == 0);
nng_msleep(100);
@@ -467,14 +463,12 @@ TestMain("TLS Transport", {
So(nng_setopt_ms(s2, NNG_OPT_RECVTIMEO, 200) == 0);
So(nng_dialer_create(&d, s2, addr) == 0);
So(init_dialer_tls_file(d) == 0);
- So(nng_dialer_setopt_int(d, NNG_OPT_TLS_AUTH_MODE,
- NNG_TLS_AUTH_MODE_OPTIONAL) == 0);
So(nng_dialer_setopt_string(
- d, NNG_OPT_TLS_SERVER_NAME, "example.com") == 0);
+ d, NNG_OPT_TLS_SERVER_NAME, "localhost") == 0);
So(nng_dialer_start(d, 0) == 0);
- So(nng_send(s1, "hello", 6, 0) == 0);
- So(nng_recvmsg(s2, &msg, 0) == 0);
+ So(nng_send(s2, "hello", 6, 0) == 0);
+ So(nng_recvmsg(s1, &msg, 0) == 0);
So(msg != NULL);
So(nng_msg_len(msg) == 6);
So(strcmp(nng_msg_body(msg), "hello") == 0);
@@ -503,20 +497,24 @@ TestMain("TLS Transport", {
});
trantest_next_address(addr, "tls+tcp4://*:%u");
So(nng_listener_create(&l, s1, addr) == 0);
- So(init_listener_tls_file(l) == 0);
+ So(init_listener_tls_ex(l, NNG_TLS_AUTH_MODE_REQUIRED) == 0);
So(nng_listener_start(l, 0) == 0);
+
nng_msleep(100);
// reset port back one
trantest_prev_address(addr, "tls+tcp4://localhost:%u");
So(nng_dialer_create(&d, s2, addr) == 0);
- So(init_dialer_tls_file(d) == 0);
+ So(init_dialer_tls_ex(d, true) == 0);
+
So(nng_setopt_ms(s2, NNG_OPT_RECVTIMEO, 200) == 0);
So(nng_dialer_start(d, 0) == 0);
nng_msleep(100);
- So(nng_send(s1, "hello", 6, 0) == 0);
- So(nng_recvmsg(s2, &msg, 0) == 0);
+ // send from the server to the client-- the client always
+ // verifies the server.
+ So(nng_send(s2, "hello", 6, 0) == 0);
+ So(nng_recvmsg(s1, &msg, 0) == 0);
So(msg != NULL);
So(nng_msg_len(msg) == 6);
So(strcmp(nng_msg_body(msg), "hello") == 0);
diff --git a/tests/wss.c b/tests/wss.c
index 78601066..ca33a542 100644
--- a/tests/wss.c
+++ b/tests/wss.c
@@ -1,5 +1,5 @@
//
-// Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
+// Copyright 2020 Staysail Systems, Inc. <info@staysail.tech>
// Copyright 2018 Capitar IT Group BV <info@capitar.com>
//
// This software is supplied under the terms of the MIT License, a
@@ -235,6 +235,10 @@ out:
TestMain("WebSocket Secure (TLS) Transport", {
static trantest tt;
+ if (strcmp(nng_tls_engine_name(), "none") == 0) {
+ Skip("TLS not enabled");
+ }
+
tt.dialer_init = init_dialer_wss;
tt.listener_init = init_listener_wss;
tt.tmpl = "wss://localhost:%u/test";