Skip to content

Commit 7c20ad8

Browse files
committed
problem: unsecured websocket is rarely used in production
Solution: support websocket with tls (wss)
1 parent 8fe6209 commit 7c20ad8

27 files changed

+702
-60
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ test_xpub_verbose
153153
test_mock_pub_sub
154154
test_proxy_hwm
155155
test_ws_transport
156+
test_wss_transport
156157
unittest_ip_resolver
157158
unittest_mtrie
158159
unittest_poller

.travis.yml

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,12 +50,14 @@ matrix:
5050
apt:
5151
packages:
5252
- lcov
53-
- env: BUILD_TYPE=valgrind CURVE=tweetnacl DRAFT=enabled
53+
- env: BUILD_TYPE=valgrind CURVE=tweetnacl DRAFT=enabled TLS=enabled
5454
os: linux
55+
dist: xenial
5556
addons:
5657
apt:
5758
packages:
5859
- valgrind
60+
- libgnutls-dev
5961
- env: BUILD_TYPE=default CURVE=libsodium GSSAPI=enabled PGM=enabled NORM=enabled
6062
os: linux
6163
addons:
@@ -70,6 +72,13 @@ matrix:
7072
- libsodium-dev
7173
- asciidoc
7274
- xmlto
75+
- env: BUILD_TYPE=default DRAFT=enabled TLS=enabled
76+
os: linux
77+
dist: xenial
78+
addons:
79+
apt:
80+
packages:
81+
- libgnutls-dev
7382
- env: BUILD_TYPE=default CURVE=libsodium DRAFT=enabled GSSAPI=enabled PGM=enabled NORM=enabled TIPC=enabled USE_NSS=yes
7483
os: linux
7584
addons:

Makefile.am

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,12 @@ src_libzmq_la_SOURCES += \
305305
external/sha1/sha1.h
306306
endif
307307

308+
if HAVE_WSS
309+
src_libzmq_la_SOURCES += \
310+
src/wss_engine.cpp \
311+
src/wss_engine.hpp
312+
endif
313+
308314
if ON_MINGW
309315
src_libzmq_la_LDFLAGS = \
310316
-no-undefined \
@@ -342,11 +348,16 @@ src_libzmq_la_CXXFLAGS = @LIBZMQ_EXTRA_CXXFLAGS@ $(CODE_COVERAGE_CXXFLAGS) \
342348
$(LIBUNWIND_CFLAGS)
343349
src_libzmq_la_LIBADD = $(CODE_COVERAGE_LDFLAGS) $(LIBUNWIND_LIBS)
344350

345-
if HAVE_WS
351+
if USE_NSS
346352
src_libzmq_la_CPPFLAGS += ${NSS3_CFLAGS}
347353
src_libzmq_la_LIBADD += ${NSS3_LIBS}
348354
endif
349355

356+
if USE_GNUTLS
357+
src_libzmq_la_CPPFLAGS += ${GNUTLS_CFLAGS}
358+
src_libzmq_la_LIBADD += ${GNUTLS_LIBS}
359+
endif
360+
350361
if USE_LIBSODIUM
351362
src_libzmq_la_CPPFLAGS += ${sodium_CFLAGS}
352363
src_libzmq_la_LIBADD += ${sodium_LIBS}
@@ -850,6 +861,14 @@ tests_test_ws_transport_LDADD = ${TESTUTIL_LIBS} src/libzmq.la ${NSS3_LIBS}
850861
tests_test_ws_transport_CPPFLAGS = ${TESTUTIL_CPPFLAGS} ${NSS3_CFLAGS}
851862
endif
852863

864+
if HAVE_WSS
865+
test_apps += \
866+
tests/test_wss_transport
867+
tests_test_wss_transport_SOURCES = tests/test_wss_transport.cpp
868+
tests_test_wss_transport_LDADD = ${TESTUTIL_LIBS} src/libzmq.la ${GNUTLS_LIBS}
869+
tests_test_wss_transport_CPPFLAGS = ${TESTUTIL_CPPFLAGS} ${GNUTLS_CFLAGS}
870+
endif
871+
853872
if !ON_MINGW
854873
if !ON_CYGWIN
855874
test_apps += \

builds/valgrind/ci_build.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,10 @@ CONFIG_OPTS+=("PKG_CONFIG_PATH=${BUILD_PREFIX}/lib/pkgconfig")
1414
CONFIG_OPTS+=("--prefix=${BUILD_PREFIX}")
1515
CONFIG_OPTS+=("--enable-valgrind")
1616

17+
if [ -n "$TLS" ] && [ "$TLS" == "enabled" ]; then
18+
CONFIG_OPTS+=("--with-tls=yes")
19+
fi
20+
1721
if [ -z $CURVE ]; then
1822
CONFIG_OPTS+=("--disable-curve")
1923
elif [ $CURVE == "libsodium" ]; then

ci_build.sh

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ if [ $BUILD_TYPE == "default" ]; then
6060
CONFIG_OPTS+=("--with-poller=${POLLER}")
6161
fi
6262

63+
if [ -n "$TLS" ] && [ "$TLS" == "enabled" ]; then
64+
CONFIG_OPTS+=("--with-tls=yes")
65+
fi
66+
6367
if [ -z $DRAFT ] || [ $DRAFT == "disabled" ]; then
6468
CONFIG_OPTS+=("--enable-drafts=no")
6569
elif [ $DRAFT == "enabled" ]; then

configure.ac

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -553,36 +553,51 @@ AM_CONDITIONAL(HAVE_CURVE, test "x$curve_library" != "x")
553553
AM_CONDITIONAL(USE_WEPOLL, test "$poller" = "wepoll")
554554

555555
# Check requiring packages for WebSocket
556-
sha1_library=""
556+
ws_crypto_library=""
557557

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

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

564+
AC_ARG_WITH([tls],
565+
[AS_HELP_STRING([--with-tls], [Enable TLS (WSS transport) [default=no]])])
566+
564567
if test "x$enable_ws" != "xno"; then
565-
if test "x$with_nss" = "xyes"; then
568+
if test "x$with_tls" = "xyes"; then
569+
PKG_CHECK_MODULES([GNUTLS], [gnutls >= 3.1.4], [
570+
ws_crypto_library="gnutls"
571+
AC_DEFINE(ZMQ_USE_GNUTLS, [1], [Use GNUTLS for TLS])
572+
AC_DEFINE(ZMQ_HAVE_WS, [1], [Using websocket])
573+
AC_DEFINE(ZMQ_HAVE_WSS, [1], [WSS enabled])
574+
AC_MSG_NOTICE(Using gnutls)
575+
],[
576+
AC_MSG_ERROR([GnuTLS is not installed. Install it, then run configure again])
577+
])
578+
elif test "x$with_nss" = "xyes"; then
566579
PKG_CHECK_MODULES([NSS3], [nss], [
567580
PKGCFG_NAMES_PRIVATE="$PKGCFG_NAMES_PRIVATE nss"
568581
AC_DEFINE(ZMQ_USE_NSS, [1], [Using NSS])
569582
AC_DEFINE(ZMQ_HAVE_WS, [1], [Using websocket])
570-
sha1_library="nss"
583+
ws_crypto_library="nss"
571584
AC_MSG_NOTICE(Using NSS)
572585
], [
573-
AC_MSG_ERROR(nss is not installed. Install it, then run configure again)
586+
AC_MSG_ERROR([nss is not installed. Install it, then run configure again])
574587
])
575588
else
576-
AC_DEFINE(ZMQ_USE_BUILTIN_SHA1, [1], [Using built-in sha1])
577589
AC_DEFINE(ZMQ_HAVE_WS, [1], [Using websocket])
578-
sha1_library="builtin"
590+
AC_DEFINE(ZMQ_USE_BUILTIN_SHA1, [1], [Using built-in sha1])
579591
AC_MSG_NOTICE(Using builting SHA1)
592+
ws_crypto_library="builtin"
580593
fi
581594
fi
582595

583-
AM_CONDITIONAL(HAVE_WS, test "x$sha1_library" != "x")
584-
AM_CONDITIONAL(USE_NSS, test "x$sha1_library" = "xnss")
585-
AM_CONDITIONAL(USE_BUILTIN_SHA1, test "x$sha1_library" = "xbuiltin")
596+
AM_CONDITIONAL(HAVE_WS, test "x$ws_crypto_library" != "x")
597+
AM_CONDITIONAL(USE_NSS, test "x$ws_crypto_library" = "xnss")
598+
AM_CONDITIONAL(USE_BUILTIN_SHA1, test "x$ws_crypto_library" = "xbuiltin")
599+
AM_CONDITIONAL(USE_GNUTLS, test "x$ws_crypto_library" = "xgnutls")
600+
AM_CONDITIONAL(HAVE_WSS, test "x$ws_crypto_library" = "xgnutls")
586601

587602
# build using pgm
588603
have_pgm_library="no"

include/zmq.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -670,6 +670,12 @@ ZMQ_EXPORT void zmq_threadclose (void *thread_);
670670
#define ZMQ_SOCKS_PASSWORD 100
671671
#define ZMQ_IN_BATCH_SIZE 101
672672
#define ZMQ_OUT_BATCH_SIZE 102
673+
#define ZMQ_WSS_KEY_PEM 103
674+
#define ZMQ_WSS_CERT_PEM 104
675+
#define ZMQ_WSS_TRUST_PEM 105
676+
#define ZMQ_WSS_HOSTNAME 106
677+
#define ZMQ_WSS_TRUST_SYSTEM 107
678+
673679

674680
/* DRAFT Context options */
675681
#define ZMQ_ZERO_COPY_RECV 10

src/address.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,12 @@ zmq::address_t::~address_t ()
6868
}
6969
#endif
7070

71+
#ifdef ZMQ_HAVE_WSS
72+
else if (protocol == protocol_name::wss) {
73+
LIBZMQ_DELETE (resolved.ws_addr);
74+
}
75+
#endif
76+
7177
#if !defined ZMQ_HAVE_WINDOWS && !defined ZMQ_HAVE_OPENVMS \
7278
&& !defined ZMQ_HAVE_VXWORKS
7379
else if (protocol == protocol_name::ipc) {
@@ -96,6 +102,10 @@ int zmq::address_t::to_string (std::string &addr_) const
96102
if (protocol == protocol_name::ws && resolved.ws_addr)
97103
return resolved.ws_addr->to_string (addr_);
98104
#endif
105+
#ifdef ZMQ_HAVE_WSS
106+
if (protocol == protocol_name::wss && resolved.ws_addr)
107+
return resolved.ws_addr->to_string (addr_);
108+
#endif
99109
#if !defined ZMQ_HAVE_WINDOWS && !defined ZMQ_HAVE_OPENVMS \
100110
&& !defined ZMQ_HAVE_VXWORKS
101111
if (protocol == protocol_name::ipc && resolved.ipc_addr)

src/address.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,9 @@ static const char udp[] = "udp";
6464
#ifdef ZMQ_HAVE_WS
6565
static const char ws[] = "ws";
6666
#endif
67+
#ifdef ZMQ_HAVE_WSS
68+
static const char wss[] = "wss";
69+
#endif
6770
#if !defined ZMQ_HAVE_WINDOWS && !defined ZMQ_HAVE_OPENVMS \
6871
&& !defined ZMQ_HAVE_VXWORKS
6972
static const char ipc[] = "ipc";

src/ctx.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,10 @@
5656
#include <nss.h>
5757
#endif
5858

59+
#ifdef ZMQ_USE_GNUTLS
60+
#include <gnutls/gnutls.h>
61+
#endif
62+
5963
#define ZMQ_CTX_TAG_VALUE_GOOD 0xabadcafe
6064
#define ZMQ_CTX_TAG_VALUE_BAD 0xdeadbeef
6165

@@ -95,6 +99,10 @@ zmq::ctx_t::ctx_t () :
9599
#ifdef ZMQ_USE_NSS
96100
NSS_NoDB_Init (NULL);
97101
#endif
102+
103+
#ifdef ZMQ_USE_GNUTLS
104+
gnutls_global_init ();
105+
#endif
98106
}
99107

100108
bool zmq::ctx_t::check_tag ()
@@ -131,6 +139,10 @@ zmq::ctx_t::~ctx_t ()
131139
NSS_Shutdown ();
132140
#endif
133141

142+
#ifdef ZMQ_USE_GNUTLS
143+
gnutls_global_deinit ();
144+
#endif
145+
134146
// Remove the tag, so that the object is considered dead.
135147
_tag = ZMQ_CTX_TAG_VALUE_BAD;
136148
}

src/options.cpp

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -247,7 +247,8 @@ zmq::options_t::options_t () :
247247
out_batch_size (8192),
248248
zero_copy (true),
249249
router_notify (0),
250-
monitor_event_version (1)
250+
monitor_event_version (1),
251+
wss_trust_system (false)
251252
{
252253
memset (curve_public_key, 0, CURVE_KEYSIZE);
253254
memset (curve_secret_key, 0, CURVE_KEYSIZE);
@@ -784,6 +785,26 @@ int zmq::options_t::setsockopt (int option_,
784785
return 0;
785786
}
786787
break;
788+
789+
case ZMQ_WSS_KEY_PEM:
790+
// TODO: check if valid certificate
791+
wss_key_pem = std::string ((char *) optval_, optvallen_);
792+
return 0;
793+
case ZMQ_WSS_CERT_PEM:
794+
// TODO: check if valid certificate
795+
wss_cert_pem = std::string ((char *) optval_, optvallen_);
796+
return 0;
797+
case ZMQ_WSS_TRUST_PEM:
798+
// TODO: check if valid certificate
799+
wss_trust_pem = std::string ((char *) optval_, optvallen_);
800+
return 0;
801+
case ZMQ_WSS_HOSTNAME:
802+
wss_hostname = std::string ((char *) optval_, optvallen_);
803+
return 0;
804+
case ZMQ_WSS_TRUST_SYSTEM:
805+
return do_setsockopt_int_as_bool_strict (optval_, optvallen_,
806+
&wss_trust_system);
807+
787808
#endif
788809

789810
default:

src/options.hpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -286,6 +286,13 @@ struct options_t
286286

287287
// Version of monitor events to emit
288288
int monitor_event_version;
289+
290+
// WSS Keys
291+
std::string wss_key_pem;
292+
std::string wss_cert_pem;
293+
std::string wss_trust_pem;
294+
std::string wss_hostname;
295+
bool wss_trust_system;
289296
};
290297

291298
inline bool get_effective_conflate_option (const options_t &options)

src/session_base.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,11 @@ zmq::session_base_t::session_base_t (class io_thread_t *io_thread_,
114114
_socket (socket_),
115115
_io_thread (io_thread_),
116116
_has_linger_timer (false),
117-
_addr (addr_)
117+
_addr (addr_),
118+
_wss_hostname (NULL)
118119
{
120+
if (options_.wss_hostname.length () > 0)
121+
_wss_hostname = strdup (options_.wss_hostname.c_str ());
119122
}
120123

121124
const zmq::endpoint_uri_pair_t &zmq::session_base_t::get_endpoint () const
@@ -138,6 +141,9 @@ zmq::session_base_t::~session_base_t ()
138141
if (_engine)
139142
_engine->terminate ();
140143

144+
if (_wss_hostname)
145+
free (_wss_hostname);
146+
141147
LIBZMQ_DELETE (_addr);
142148
}
143149

@@ -563,6 +569,10 @@ zmq::session_base_t::connecter_factory_entry_t
563569
connecter_factory_entry_t (protocol_name::ws,
564570
&zmq::session_base_t::create_connecter_ws),
565571
#endif
572+
#ifdef ZMQ_HAVE_WSS
573+
connecter_factory_entry_t (protocol_name::wss,
574+
&zmq::session_base_t::create_connecter_wss),
575+
#endif
566576
#if !defined ZMQ_HAVE_WINDOWS && !defined ZMQ_HAVE_OPENVMS \
567577
&& !defined ZMQ_HAVE_VXWORKS
568578
connecter_factory_entry_t (protocol_name::ipc,
@@ -690,7 +700,16 @@ zmq::own_t *zmq::session_base_t::create_connecter_ws (io_thread_t *io_thread_,
690700
bool wait_)
691701
{
692702
return new (std::nothrow)
693-
ws_connecter_t (io_thread_, this, options, _addr, wait_);
703+
ws_connecter_t (io_thread_, this, options, _addr, wait_, false, NULL);
704+
}
705+
#endif
706+
707+
#ifdef ZMQ_HAVE_WSS
708+
zmq::own_t *zmq::session_base_t::create_connecter_wss (io_thread_t *io_thread_,
709+
bool wait_)
710+
{
711+
return new (std::nothrow) ws_connecter_t (io_thread_, this, options, _addr,
712+
wait_, true, _wss_hostname);
694713
}
695714
#endif
696715

src/session_base.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ class session_base_t : public own_t, public io_object_t, public i_pipe_events
120120
own_t *create_connecter_ipc (io_thread_t *io_thread_, bool wait_);
121121
own_t *create_connecter_tcp (io_thread_t *io_thread_, bool wait_);
122122
own_t *create_connecter_ws (io_thread_t *io_thread_, bool wait_);
123+
own_t *create_connecter_wss (io_thread_t *io_thread_, bool wait_);
123124

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

195+
// TLS handshake, we need to take a copy when the session is created,
196+
// in order to maintain the value at the creation time
197+
char *_wss_hostname;
198+
194199
session_base_t (const session_base_t &);
195200
const session_base_t &operator= (const session_base_t &);
196201
};

0 commit comments

Comments
 (0)