summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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";