Skip to content

Commit

Permalink
problem: unsecured websocket is rarely used in production
Browse files Browse the repository at this point in the history
Solution: support websocket with tls (wss)
  • Loading branch information
somdoron committed Oct 1, 2019
1 parent 8fe6209 commit 7c20ad8
Show file tree
Hide file tree
Showing 27 changed files with 702 additions and 60 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,7 @@ test_xpub_verbose
test_mock_pub_sub
test_proxy_hwm
test_ws_transport
test_wss_transport
unittest_ip_resolver
unittest_mtrie
unittest_poller
Expand Down
11 changes: 10 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,14 @@ matrix:
apt:
packages:
- lcov
- env: BUILD_TYPE=valgrind CURVE=tweetnacl DRAFT=enabled
- env: BUILD_TYPE=valgrind CURVE=tweetnacl DRAFT=enabled TLS=enabled
os: linux
dist: xenial
addons:
apt:
packages:
- valgrind
- libgnutls-dev
- env: BUILD_TYPE=default CURVE=libsodium GSSAPI=enabled PGM=enabled NORM=enabled
os: linux
addons:
Expand All @@ -70,6 +72,13 @@ matrix:
- libsodium-dev
- asciidoc
- xmlto
- env: BUILD_TYPE=default DRAFT=enabled TLS=enabled
os: linux
dist: xenial
addons:
apt:
packages:
- libgnutls-dev
- env: BUILD_TYPE=default CURVE=libsodium DRAFT=enabled GSSAPI=enabled PGM=enabled NORM=enabled TIPC=enabled USE_NSS=yes
os: linux
addons:
Expand Down
21 changes: 20 additions & 1 deletion Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,12 @@ src_libzmq_la_SOURCES += \
external/sha1/sha1.h
endif

if HAVE_WSS
src_libzmq_la_SOURCES += \
src/wss_engine.cpp \
src/wss_engine.hpp
endif

if ON_MINGW
src_libzmq_la_LDFLAGS = \
-no-undefined \
Expand Down Expand Up @@ -342,11 +348,16 @@ src_libzmq_la_CXXFLAGS = @LIBZMQ_EXTRA_CXXFLAGS@ $(CODE_COVERAGE_CXXFLAGS) \
$(LIBUNWIND_CFLAGS)
src_libzmq_la_LIBADD = $(CODE_COVERAGE_LDFLAGS) $(LIBUNWIND_LIBS)

if HAVE_WS
if USE_NSS
src_libzmq_la_CPPFLAGS += ${NSS3_CFLAGS}
src_libzmq_la_LIBADD += ${NSS3_LIBS}
endif

if USE_GNUTLS
src_libzmq_la_CPPFLAGS += ${GNUTLS_CFLAGS}
src_libzmq_la_LIBADD += ${GNUTLS_LIBS}
endif

if USE_LIBSODIUM
src_libzmq_la_CPPFLAGS += ${sodium_CFLAGS}
src_libzmq_la_LIBADD += ${sodium_LIBS}
Expand Down Expand Up @@ -850,6 +861,14 @@ tests_test_ws_transport_LDADD = ${TESTUTIL_LIBS} src/libzmq.la ${NSS3_LIBS}
tests_test_ws_transport_CPPFLAGS = ${TESTUTIL_CPPFLAGS} ${NSS3_CFLAGS}
endif

if HAVE_WSS
test_apps += \
tests/test_wss_transport
tests_test_wss_transport_SOURCES = tests/test_wss_transport.cpp
tests_test_wss_transport_LDADD = ${TESTUTIL_LIBS} src/libzmq.la ${GNUTLS_LIBS}
tests_test_wss_transport_CPPFLAGS = ${TESTUTIL_CPPFLAGS} ${GNUTLS_CFLAGS}
endif

if !ON_MINGW
if !ON_CYGWIN
test_apps += \
Expand Down
4 changes: 4 additions & 0 deletions builds/valgrind/ci_build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ CONFIG_OPTS+=("PKG_CONFIG_PATH=${BUILD_PREFIX}/lib/pkgconfig")
CONFIG_OPTS+=("--prefix=${BUILD_PREFIX}")
CONFIG_OPTS+=("--enable-valgrind")

if [ -n "$TLS" ] && [ "$TLS" == "enabled" ]; then
CONFIG_OPTS+=("--with-tls=yes")
fi

if [ -z $CURVE ]; then
CONFIG_OPTS+=("--disable-curve")
elif [ $CURVE == "libsodium" ]; then
Expand Down
4 changes: 4 additions & 0 deletions ci_build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ if [ $BUILD_TYPE == "default" ]; then
CONFIG_OPTS+=("--with-poller=${POLLER}")
fi

if [ -n "$TLS" ] && [ "$TLS" == "enabled" ]; then
CONFIG_OPTS+=("--with-tls=yes")
fi

if [ -z $DRAFT ] || [ $DRAFT == "disabled" ]; then
CONFIG_OPTS+=("--enable-drafts=no")
elif [ $DRAFT == "enabled" ]; then
Expand Down
33 changes: 24 additions & 9 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -553,36 +553,51 @@ AM_CONDITIONAL(HAVE_CURVE, test "x$curve_library" != "x")
AM_CONDITIONAL(USE_WEPOLL, test "$poller" = "wepoll")

# Check requiring packages for WebSocket
sha1_library=""
ws_crypto_library=""

AC_ARG_ENABLE([ws],
[AS_HELP_STRING([--disable-ws], [Disable WebSocket transport [default=no]])])

AC_ARG_WITH([nss],
[AS_HELP_STRING([--with-nss], [use nss instead of built-in sha1 [default=no]])])

AC_ARG_WITH([tls],
[AS_HELP_STRING([--with-tls], [Enable TLS (WSS transport) [default=no]])])

if test "x$enable_ws" != "xno"; then
if test "x$with_nss" = "xyes"; then
if test "x$with_tls" = "xyes"; then
PKG_CHECK_MODULES([GNUTLS], [gnutls >= 3.1.4], [
ws_crypto_library="gnutls"
AC_DEFINE(ZMQ_USE_GNUTLS, [1], [Use GNUTLS for TLS])
AC_DEFINE(ZMQ_HAVE_WS, [1], [Using websocket])
AC_DEFINE(ZMQ_HAVE_WSS, [1], [WSS enabled])
AC_MSG_NOTICE(Using gnutls)
],[
AC_MSG_ERROR([GnuTLS is not installed. Install it, then run configure again])
])
elif test "x$with_nss" = "xyes"; then
PKG_CHECK_MODULES([NSS3], [nss], [
PKGCFG_NAMES_PRIVATE="$PKGCFG_NAMES_PRIVATE nss"
AC_DEFINE(ZMQ_USE_NSS, [1], [Using NSS])
AC_DEFINE(ZMQ_HAVE_WS, [1], [Using websocket])
sha1_library="nss"
ws_crypto_library="nss"
AC_MSG_NOTICE(Using NSS)
], [
AC_MSG_ERROR(nss is not installed. Install it, then run configure again)
AC_MSG_ERROR([nss is not installed. Install it, then run configure again])
])
else
AC_DEFINE(ZMQ_USE_BUILTIN_SHA1, [1], [Using built-in sha1])
AC_DEFINE(ZMQ_HAVE_WS, [1], [Using websocket])
sha1_library="builtin"
AC_DEFINE(ZMQ_USE_BUILTIN_SHA1, [1], [Using built-in sha1])
AC_MSG_NOTICE(Using builting SHA1)
ws_crypto_library="builtin"
fi
fi

AM_CONDITIONAL(HAVE_WS, test "x$sha1_library" != "x")
AM_CONDITIONAL(USE_NSS, test "x$sha1_library" = "xnss")
AM_CONDITIONAL(USE_BUILTIN_SHA1, test "x$sha1_library" = "xbuiltin")
AM_CONDITIONAL(HAVE_WS, test "x$ws_crypto_library" != "x")
AM_CONDITIONAL(USE_NSS, test "x$ws_crypto_library" = "xnss")
AM_CONDITIONAL(USE_BUILTIN_SHA1, test "x$ws_crypto_library" = "xbuiltin")
AM_CONDITIONAL(USE_GNUTLS, test "x$ws_crypto_library" = "xgnutls")
AM_CONDITIONAL(HAVE_WSS, test "x$ws_crypto_library" = "xgnutls")

# build using pgm
have_pgm_library="no"
Expand Down
6 changes: 6 additions & 0 deletions include/zmq.h
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,12 @@ ZMQ_EXPORT void zmq_threadclose (void *thread_);
#define ZMQ_SOCKS_PASSWORD 100
#define ZMQ_IN_BATCH_SIZE 101
#define ZMQ_OUT_BATCH_SIZE 102
#define ZMQ_WSS_KEY_PEM 103
#define ZMQ_WSS_CERT_PEM 104
#define ZMQ_WSS_TRUST_PEM 105
#define ZMQ_WSS_HOSTNAME 106
#define ZMQ_WSS_TRUST_SYSTEM 107


/* DRAFT Context options */
#define ZMQ_ZERO_COPY_RECV 10
Expand Down
10 changes: 10 additions & 0 deletions src/address.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,12 @@ zmq::address_t::~address_t ()
}
#endif

#ifdef ZMQ_HAVE_WSS
else if (protocol == protocol_name::wss) {
LIBZMQ_DELETE (resolved.ws_addr);
}
#endif

#if !defined ZMQ_HAVE_WINDOWS && !defined ZMQ_HAVE_OPENVMS \
&& !defined ZMQ_HAVE_VXWORKS
else if (protocol == protocol_name::ipc) {
Expand Down Expand Up @@ -96,6 +102,10 @@ int zmq::address_t::to_string (std::string &addr_) const
if (protocol == protocol_name::ws && resolved.ws_addr)
return resolved.ws_addr->to_string (addr_);
#endif
#ifdef ZMQ_HAVE_WSS
if (protocol == protocol_name::wss && resolved.ws_addr)
return resolved.ws_addr->to_string (addr_);
#endif
#if !defined ZMQ_HAVE_WINDOWS && !defined ZMQ_HAVE_OPENVMS \
&& !defined ZMQ_HAVE_VXWORKS
if (protocol == protocol_name::ipc && resolved.ipc_addr)
Expand Down
3 changes: 3 additions & 0 deletions src/address.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,9 @@ static const char udp[] = "udp";
#ifdef ZMQ_HAVE_WS
static const char ws[] = "ws";
#endif
#ifdef ZMQ_HAVE_WSS
static const char wss[] = "wss";
#endif
#if !defined ZMQ_HAVE_WINDOWS && !defined ZMQ_HAVE_OPENVMS \
&& !defined ZMQ_HAVE_VXWORKS
static const char ipc[] = "ipc";
Expand Down
12 changes: 12 additions & 0 deletions src/ctx.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@
#include <nss.h>
#endif

#ifdef ZMQ_USE_GNUTLS
#include <gnutls/gnutls.h>
#endif

#define ZMQ_CTX_TAG_VALUE_GOOD 0xabadcafe
#define ZMQ_CTX_TAG_VALUE_BAD 0xdeadbeef

Expand Down Expand Up @@ -95,6 +99,10 @@ zmq::ctx_t::ctx_t () :
#ifdef ZMQ_USE_NSS
NSS_NoDB_Init (NULL);
#endif

#ifdef ZMQ_USE_GNUTLS
gnutls_global_init ();
#endif
}

bool zmq::ctx_t::check_tag ()
Expand Down Expand Up @@ -131,6 +139,10 @@ zmq::ctx_t::~ctx_t ()
NSS_Shutdown ();
#endif

#ifdef ZMQ_USE_GNUTLS
gnutls_global_deinit ();
#endif

// Remove the tag, so that the object is considered dead.
_tag = ZMQ_CTX_TAG_VALUE_BAD;
}
Expand Down
23 changes: 22 additions & 1 deletion src/options.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,8 @@ zmq::options_t::options_t () :
out_batch_size (8192),
zero_copy (true),
router_notify (0),
monitor_event_version (1)
monitor_event_version (1),
wss_trust_system (false)
{
memset (curve_public_key, 0, CURVE_KEYSIZE);
memset (curve_secret_key, 0, CURVE_KEYSIZE);
Expand Down Expand Up @@ -784,6 +785,26 @@ int zmq::options_t::setsockopt (int option_,
return 0;
}
break;

case ZMQ_WSS_KEY_PEM:
// TODO: check if valid certificate
wss_key_pem = std::string ((char *) optval_, optvallen_);
return 0;
case ZMQ_WSS_CERT_PEM:
// TODO: check if valid certificate
wss_cert_pem = std::string ((char *) optval_, optvallen_);
return 0;
case ZMQ_WSS_TRUST_PEM:
// TODO: check if valid certificate
wss_trust_pem = std::string ((char *) optval_, optvallen_);
return 0;
case ZMQ_WSS_HOSTNAME:
wss_hostname = std::string ((char *) optval_, optvallen_);
return 0;
case ZMQ_WSS_TRUST_SYSTEM:
return do_setsockopt_int_as_bool_strict (optval_, optvallen_,
&wss_trust_system);

#endif

default:
Expand Down
7 changes: 7 additions & 0 deletions src/options.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,13 @@ struct options_t

// Version of monitor events to emit
int monitor_event_version;

// WSS Keys
std::string wss_key_pem;
std::string wss_cert_pem;
std::string wss_trust_pem;
std::string wss_hostname;
bool wss_trust_system;
};

inline bool get_effective_conflate_option (const options_t &options)
Expand Down
23 changes: 21 additions & 2 deletions src/session_base.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,11 @@ zmq::session_base_t::session_base_t (class io_thread_t *io_thread_,
_socket (socket_),
_io_thread (io_thread_),
_has_linger_timer (false),
_addr (addr_)
_addr (addr_),
_wss_hostname (NULL)
{
if (options_.wss_hostname.length () > 0)
_wss_hostname = strdup (options_.wss_hostname.c_str ());
}

const zmq::endpoint_uri_pair_t &zmq::session_base_t::get_endpoint () const
Expand All @@ -138,6 +141,9 @@ zmq::session_base_t::~session_base_t ()
if (_engine)
_engine->terminate ();

if (_wss_hostname)
free (_wss_hostname);

LIBZMQ_DELETE (_addr);
}

Expand Down Expand Up @@ -563,6 +569,10 @@ zmq::session_base_t::connecter_factory_entry_t
connecter_factory_entry_t (protocol_name::ws,
&zmq::session_base_t::create_connecter_ws),
#endif
#ifdef ZMQ_HAVE_WSS
connecter_factory_entry_t (protocol_name::wss,
&zmq::session_base_t::create_connecter_wss),
#endif
#if !defined ZMQ_HAVE_WINDOWS && !defined ZMQ_HAVE_OPENVMS \
&& !defined ZMQ_HAVE_VXWORKS
connecter_factory_entry_t (protocol_name::ipc,
Expand Down Expand Up @@ -690,7 +700,16 @@ zmq::own_t *zmq::session_base_t::create_connecter_ws (io_thread_t *io_thread_,
bool wait_)
{
return new (std::nothrow)
ws_connecter_t (io_thread_, this, options, _addr, wait_);
ws_connecter_t (io_thread_, this, options, _addr, wait_, false, NULL);
}
#endif

#ifdef ZMQ_HAVE_WSS
zmq::own_t *zmq::session_base_t::create_connecter_wss (io_thread_t *io_thread_,
bool wait_)
{
return new (std::nothrow) ws_connecter_t (io_thread_, this, options, _addr,
wait_, true, _wss_hostname);
}
#endif

Expand Down
5 changes: 5 additions & 0 deletions src/session_base.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ class session_base_t : public own_t, public io_object_t, public i_pipe_events
own_t *create_connecter_ipc (io_thread_t *io_thread_, bool wait_);
own_t *create_connecter_tcp (io_thread_t *io_thread_, bool wait_);
own_t *create_connecter_ws (io_thread_t *io_thread_, bool wait_);
own_t *create_connecter_wss (io_thread_t *io_thread_, bool wait_);

typedef void (session_base_t::*start_connecting_fun_t) (
io_thread_t *io_thread);
Expand Down Expand Up @@ -191,6 +192,10 @@ class session_base_t : public own_t, public io_object_t, public i_pipe_events
// Protocol and address to use when connecting.
address_t *_addr;

// TLS handshake, we need to take a copy when the session is created,
// in order to maintain the value at the creation time
char *_wss_hostname;

session_base_t (const session_base_t &);
const session_base_t &operator= (const session_base_t &);
};
Expand Down
Loading

0 comments on commit 7c20ad8

Please sign in to comment.