diff options
| author | Garrett D'Amore <garrett@damore.org> | 2017-12-29 14:21:20 -0800 |
|---|---|---|
| committer | Garrett D'Amore <garrett@damore.org> | 2017-12-30 19:05:41 -0800 |
| commit | 6a50035b242b972c1d9b659ba63e037a0a8afe71 (patch) | |
| tree | fe2600235a01e72d1e7bd5fad1d5e2ea62aada2e /src/supplemental | |
| parent | a0364185784895c4bc748a6e6453a132d618c96c (diff) | |
| download | nng-6a50035b242b972c1d9b659ba63e037a0a8afe71.tar.gz nng-6a50035b242b972c1d9b659ba63e037a0a8afe71.tar.bz2 nng-6a50035b242b972c1d9b659ba63e037a0a8afe71.zip | |
fixes #166 Websocket TLS mapping
This introduces the wss:// scheme, which is available and works like
the ws:// scheme if TLS is enabled in the library.
The library modularization is refactored somewhat, to make it easier
to use. There is now a single NNG_ENABLE_TLS that enables TLS support
under the hood.
This also adds a new option for the TLS transport, NNG_OPT_TLS_CONFIG
(and a similar one for WSS, NNG_OPT_TLS_WSS_CONFIG) that offer access
to the underlying TLS configuration object, which now has a public API
to go with it as well.
Note that it is also possible to use pure HTTPS using the *private*
API, which will be exposed in a public form soon.
Diffstat (limited to 'src/supplemental')
| -rw-r--r-- | src/supplemental/http/client.c | 45 | ||||
| -rw-r--r-- | src/supplemental/http/http.c | 84 | ||||
| -rw-r--r-- | src/supplemental/http/http.h | 23 | ||||
| -rw-r--r-- | src/supplemental/http/server.c | 115 | ||||
| -rw-r--r-- | src/supplemental/mbedtls/CMakeLists.txt | 54 | ||||
| -rw-r--r-- | src/supplemental/tls.h | 84 | ||||
| -rw-r--r-- | src/supplemental/tls/CMakeLists.txt | 38 | ||||
| -rw-r--r-- | src/supplemental/tls/mbedtls/tls.c (renamed from src/supplemental/mbedtls/tls.c) | 80 | ||||
| -rw-r--r-- | src/supplemental/tls/tls.h | 45 | ||||
| -rw-r--r-- | src/supplemental/websocket/websocket.c | 86 | ||||
| -rw-r--r-- | src/supplemental/websocket/websocket.h | 8 |
11 files changed, 392 insertions, 270 deletions
diff --git a/src/supplemental/http/client.c b/src/supplemental/http/client.c index 8c082a17..4bb517ce 100644 --- a/src/supplemental/http/client.c +++ b/src/supplemental/http/client.c @@ -14,6 +14,8 @@ #include <string.h> #include "core/nng_impl.h" +#include "supplemental/tls/tls.h" + #include "http.h" struct nni_http_client { @@ -21,7 +23,7 @@ struct nni_http_client { nni_list aios; nni_mtx mtx; bool closed; - bool tls; + nng_tls_config * tls; nni_aio * connaio; nni_plat_tcp_ep *tep; }; @@ -39,7 +41,6 @@ http_conn_done(void *arg) nni_aio * aio; int rv; nni_plat_tcp_pipe *p; - nni_http_tran t; nni_http * http; nni_mtx_lock(&c->mtx); @@ -60,17 +61,13 @@ http_conn_done(void *arg) return; } - t.h_data = p; - t.h_write = (void *) nni_plat_tcp_pipe_send; - t.h_read = (void *) nni_plat_tcp_pipe_recv; - t.h_close = (void *) nni_plat_tcp_pipe_close; - t.h_sock_addr = (void *) nni_plat_tcp_pipe_sockname; - t.h_peer_addr = (void *) nni_plat_tcp_pipe_peername; - t.h_fini = (void *) nni_plat_tcp_pipe_fini; - - if ((rv = nni_http_init(&http, &t)) != 0) { + if (c->tls != NULL) { + rv = nni_http_init_tls(&http, c->tls, p); + } else { + rv = nni_http_init_tcp(&http, p); + } + if (rv != 0) { nni_aio_finish_error(aio, rv); - nni_plat_tcp_pipe_fini(p); nni_mtx_unlock(&c->mtx); return; } @@ -90,6 +87,11 @@ nni_http_client_fini(nni_http_client *c) nni_aio_fini(c->connaio); nni_plat_tcp_ep_fini(c->tep); nni_mtx_fini(&c->mtx); +#ifdef NNG_SUPP_TLS + if (c->tls != NULL) { + nng_tls_config_fini(c->tls); + } +#endif NNI_FREE_STRUCT(c); } @@ -119,6 +121,25 @@ nni_http_client_init(nni_http_client **cp, nng_sockaddr *sa) return (0); } +#ifdef NNG_SUPP_TLS +int +nni_http_client_set_tls(nni_http_client *c, nng_tls_config *tls) +{ + nng_tls_config *old; + nni_mtx_lock(&c->mtx); + old = c->tls; + c->tls = tls; + if (tls != NULL) { + nni_tls_config_hold(tls); + } + nni_mtx_unlock(&c->mtx); + if (old != NULL) { + nng_tls_config_fini(old); + } + return (0); +} +#endif + static void http_connect_cancel(nni_aio *aio, int rv) { diff --git a/src/supplemental/http/http.c b/src/supplemental/http/http.c index f414c5c7..df5c7588 100644 --- a/src/supplemental/http/http.c +++ b/src/supplemental/http/http.c @@ -12,6 +12,8 @@ #include <string.h> #include "core/nng_impl.h" +#include "supplemental/tls/tls.h" + #include "http.h" // We insist that individual headers fit in 8K. @@ -33,6 +35,15 @@ enum write_flavor { HTTP_WR_RES, }; +typedef struct nni_http_tran { + void (*h_read)(void *, nni_aio *); + void (*h_write)(void *, nni_aio *); + int (*h_sock_addr)(void *, nni_sockaddr *); + int (*h_peer_addr)(void *, nni_sockaddr *); + void (*h_close)(void *); + void (*h_fini)(void *); +} nni_http_tran; + #define SET_RD_FLAVOR(aio, f) (aio)->a_prov_extra[0] = ((void *) (intptr_t)(f)) #define GET_RD_FLAVOR(aio) (int) ((intptr_t) aio->a_prov_extra[0]) #define SET_WR_FLAVOR(aio, f) (aio)->a_prov_extra[0] = ((void *) (intptr_t)(f)) @@ -76,14 +87,17 @@ http_close(nni_http *http) http->closed = true; if (nni_list_first(&http->wrq)) { nni_aio_cancel(http->wr_aio, NNG_ECLOSED); - while ((aio = nni_list_first(&http->wrq)) != NULL) { + // Abort all operations except the one in flight. + while ((aio = nni_list_last(&http->wrq)) != + nni_list_first(&http->wrq)) { nni_aio_list_remove(aio); nni_aio_finish_error(aio, NNG_ECLOSED); } } if (nni_list_first(&http->rdq)) { nni_aio_cancel(http->rd_aio, NNG_ECLOSED); - while ((aio = nni_list_first(&http->rdq)) != NULL) { + while ((aio = nni_list_last(&http->rdq)) != + nni_list_first(&http->rdq)) { nni_aio_list_remove(aio); nni_aio_finish_error(aio, NNG_ECLOSED); } @@ -591,8 +605,8 @@ nni_http_fini(nni_http *http) NNI_FREE_STRUCT(http); } -int -nni_http_init(nni_http **httpp, nni_http_tran *tran) +static int +http_init(nni_http **httpp, nni_http_tran *tran, void *data) { nni_http *http; int rv; @@ -609,21 +623,71 @@ nni_http_init(nni_http **httpp, nni_http_tran *tran) nni_aio_list_init(&http->rdq); nni_aio_list_init(&http->wrq); - if (((rv = nni_aio_init(&http->wr_aio, http_wr_cb, http)) != 0) || - ((rv = nni_aio_init(&http->rd_aio, http_rd_cb, http)) != 0)) { - nni_http_fini(http); - return (rv); - } + http->sock = data; http->rd_bufsz = HTTP_BUFSIZE; http->rd = tran->h_read; http->wr = tran->h_write; http->close = tran->h_close; http->fini = tran->h_fini; - http->sock = tran->h_data; http->sock_addr = tran->h_sock_addr; http->peer_addr = tran->h_peer_addr; + if (((rv = nni_aio_init(&http->wr_aio, http_wr_cb, http)) != 0) || + ((rv = nni_aio_init(&http->rd_aio, http_rd_cb, http)) != 0)) { + nni_http_fini(http); + return (rv); + } + *httpp = http; return (0); } + +static nni_http_tran http_tcp_ops = { + .h_read = (void *) nni_plat_tcp_pipe_recv, + .h_write = (void *) nni_plat_tcp_pipe_send, + .h_close = (void *) nni_plat_tcp_pipe_close, + .h_fini = (void *) nni_plat_tcp_pipe_fini, + .h_sock_addr = (void *) nni_plat_tcp_pipe_sockname, + .h_peer_addr = (void *) nni_plat_tcp_pipe_peername, +}; + +int +nni_http_init_tcp(nni_http **hpp, void *tcp) +{ + return (http_init(hpp, &http_tcp_ops, tcp)); +} + +#ifdef NNG_SUPP_TLS +static nni_http_tran http_tls_ops = { + .h_read = (void *) nni_tls_recv, + .h_write = (void *) nni_tls_send, + .h_close = (void *) nni_tls_close, + .h_fini = (void *) nni_tls_fini, + .h_sock_addr = (void *) nni_tls_sockname, + .h_peer_addr = (void *) nni_tls_peername, +}; + +int +nni_http_init_tls(nni_http **hpp, nng_tls_config *cfg, void *tcp) +{ + nni_tls *tls; + int rv; + + if ((rv = nni_tls_init(&tls, cfg, tcp)) != 0) { + nni_plat_tcp_pipe_fini(tcp); + return (rv); + } + + return (http_init(hpp, &http_tls_ops, tls)); +} +#else +int +nni_http_init_tls(nni_http **hpp, nng_tls_config *cfg, void *tcp) +{ + NNI_ARG_UNUSED(hpp); + NNI_ARG_UNUSED(cfg); + nni_plat_tcp_pipe_fini(tcp); + return (NNG_ENOTSUP); +} +#endif // NNG_SUPP_TLS
\ No newline at end of file diff --git a/src/supplemental/http/http.h b/src/supplemental/http/http.h index 3ecce4c8..d817ba37 100644 --- a/src/supplemental/http/http.h +++ b/src/supplemental/http/http.h @@ -18,16 +18,6 @@ typedef struct nni_http_msg nni_http_msg; typedef struct nni_http_res nni_http_res; typedef struct nni_http_entity nni_http_entity; -typedef struct nni_http_tran { - void *h_data; - void (*h_read)(void *, nni_aio *); - void (*h_write)(void *, nni_aio *); - int (*h_sock_addr)(void *, nni_sockaddr *); - int (*h_peer_addr)(void *, nni_sockaddr *); - void (*h_close)(void *); - void (*h_fini)(void *); -} nni_http_tran; - typedef struct nni_http_req nni_http_req; extern int nni_http_req_init(nni_http_req **); @@ -146,7 +136,8 @@ enum { NNI_HTTP_STATUS_CONTINUE = 100, // the connection. typedef struct nni_http nni_http; -extern int nni_http_init(nni_http **, nni_http_tran *); +extern int nni_http_init_tcp(nni_http **, void *); +extern int nni_http_init_tls(nni_http **, nng_tls_config *, void *); extern void nni_http_close(nni_http *); extern void nni_http_fini(nni_http *); @@ -254,6 +245,11 @@ extern int nni_http_server_add_handler( extern void nni_http_server_del_handler(nni_http_server *, void *); +// nni_http_server_set_tls adds a TLS configuration to the server, +// and enables the use of it. This returns NNG_EBUSY if the server is +// already started. +extern int nni_http_server_set_tls(nni_http_server *, nng_tls_config *); + // nni_http_server_start starts listening on the supplied port. extern int nni_http_server_start(nni_http_server *); @@ -281,16 +277,13 @@ extern int nni_http_server_add_static(nni_http_server *, const char *host, extern int nni_http_server_add_file(nni_http_server *, const char *host, const char *ctype, const char *uri, const char *path); -// TLS will use -// extern int nni_http_server_start_tls(nni_http_server *, nng_sockaddr *, -// nni_tls_config *); - // Client stuff. typedef struct nni_http_client nni_http_client; extern int nni_http_client_init(nni_http_client **, nng_sockaddr *); extern void nni_http_client_fini(nni_http_client *); +extern int nni_http_client_set_tls(nni_http_client *, nng_tls_config *); extern void nni_http_client_connect(nni_http_client *, nni_aio *); #endif // NNG_SUPPLEMENTAL_HTTP_HTTP_H diff --git a/src/supplemental/http/server.c b/src/supplemental/http/server.c index ea7f15ce..ba74a138 100644 --- a/src/supplemental/http/server.c +++ b/src/supplemental/http/server.c @@ -14,6 +14,8 @@ #include <string.h> #include "core/nng_impl.h" +#include "supplemental/tls/tls.h" + #include "http.h" static int http_server_sys_init(void); @@ -50,7 +52,6 @@ typedef struct http_sconn { nni_aio * rxaio; nni_aio * txaio; nni_aio * txdataio; - nni_http_tran tran; nni_reap_item reap; } http_sconn; @@ -64,7 +65,7 @@ struct nni_http_server { nni_mtx mtx; nni_cv cv; bool closed; - bool tls; + nng_tls_config * tls; nni_aio * accaio; nni_plat_tcp_ep *tep; }; @@ -105,28 +106,48 @@ http_sconn_fini(http_sconn *sc) } static void -http_sconn_close(http_sconn *sc) +http_sconn_close_locked(http_sconn *sc) { nni_http_server *s; s = sc->server; + nni_http *h; + if (sc->closed) { + return; + } NNI_ASSERT(!sc->finished); - nni_mtx_lock(&s->mtx); - if (!sc->closed) { - nni_http *h; - sc->closed = true; - // Close the underlying transport. - if (nni_list_node_active(&sc->node)) { - nni_list_remove(&s->conns, sc); - if (nni_list_empty(&s->conns)) { - nni_cv_wake(&s->cv); - } - } - if ((h = sc->http) != NULL) { - nni_http_close(h); + + sc->closed = true; + // Close the underlying transport. + if (nni_list_node_active(&sc->node)) { + nni_list_remove(&s->conns, sc); + if (nni_list_empty(&s->conns)) { + nni_cv_wake(&s->cv); } - http_sconn_fini(sc); } + nni_aio_cancel(sc->rxaio, NNG_ECLOSED); + nni_aio_cancel(sc->txaio, NNG_ECLOSED); + nni_aio_cancel(sc->txdataio, NNG_ECLOSED); + nni_aio_cancel(sc->cbaio, NNG_ECLOSED); + + if ((h = sc->http) != NULL) { + nni_http_close(h); + } + http_sconn_fini(sc); +} + +static void +http_sconn_close(http_sconn *sc) +{ + nni_http_server *s; + s = sc->server; + + if (sc->closed) { + return; + } + + nni_mtx_lock(&s->mtx); + http_sconn_close_locked(sc); nni_mtx_unlock(&s->mtx); } @@ -484,14 +505,16 @@ http_sconn_cbdone(void *arg) } static int -http_sconn_init(http_sconn **scp, nni_plat_tcp_pipe *tcp) +http_sconn_init(http_sconn **scp, nni_http_server *s, nni_plat_tcp_pipe *tcp) { http_sconn *sc; int rv; if ((sc = NNI_ALLOC_STRUCT(sc)) == NULL) { + nni_plat_tcp_pipe_fini(tcp); return (NNG_ENOMEM); } + if (((rv = nni_http_req_init(&sc->req)) != 0) || ((rv = nni_aio_init(&sc->rxaio, http_sconn_rxdone, sc)) != 0) || ((rv = nni_aio_init(&sc->txaio, http_sconn_txdone, sc)) != 0) || @@ -502,18 +525,13 @@ http_sconn_init(http_sconn **scp, nni_plat_tcp_pipe *tcp) http_sconn_close(sc); return (rv); } - // XXX: for HTTPS we would then try to do the TLS negotiation here. - // That would use a different set of tran values. - sc->tran.h_data = tcp; - sc->tran.h_read = (void *) nni_plat_tcp_pipe_recv; - sc->tran.h_write = (void *) nni_plat_tcp_pipe_send; - sc->tran.h_close = (void *) nni_plat_tcp_pipe_close; // close implied - sc->tran.h_fini = (void *) nni_plat_tcp_pipe_fini; - sc->tran.h_sock_addr = (void *) nni_plat_tcp_pipe_sockname; - sc->tran.h_peer_addr = (void *) nni_plat_tcp_pipe_peername; - - if ((rv = nni_http_init(&sc->http, &sc->tran)) != 0) { + if (s->tls != NULL) { + rv = nni_http_init_tls(&sc->http, s->tls, tcp); + } else { + rv = nni_http_init_tcp(&sc->http, tcp); + } + if (rv != 0) { http_sconn_close(sc); return (rv); } @@ -539,9 +557,8 @@ http_server_acccb(void *arg) return; } tcp = nni_aio_get_pipe(aio); - if (http_sconn_init(&sc, tcp) != 0) { - nni_plat_tcp_pipe_close(tcp); - nni_plat_tcp_pipe_fini(tcp); + if (http_sconn_init(&sc, s, tcp) != 0) { + // The TCP structure is already cleaned up. nni_plat_tcp_ep_accept(s->tep, s->accaio); return; @@ -590,6 +607,11 @@ http_server_fini(nni_http_server *s) http_handler_fini(h); } nni_mtx_unlock(&s->mtx); +#ifdef NNG_SUPP_TLS + if (s->tls != NULL) { + nng_tls_config_fini(s->tls); + } +#endif nni_aio_fini(s->accaio); nni_cv_fini(&s->cv); nni_mtx_fini(&s->mtx); @@ -726,11 +748,7 @@ http_server_stop(nni_http_server *s) // Stopping the server is a hard stop -- it aborts any work being // done by clients. (No graceful shutdown). NNI_LIST_FOREACH (&s->conns, sc) { - nni_list_remove(&s->conns, sc); - if (sc->http != NULL) { - nni_http_close(sc->http); - } - http_sconn_fini(sc); + http_sconn_close_locked(sc); } nni_cv_wake(&s->cv); } @@ -1073,6 +1091,29 @@ nni_http_server_add_static(nni_http_server *s, const char *host, return (0); } +#ifdef NNG_SUPP_TLS +int +nni_http_server_set_tls(nni_http_server *s, nng_tls_config *tcfg) +{ + nng_tls_config *old; + nni_mtx_lock(&s->mtx); + if (s->starts) { + nni_mtx_unlock(&s->mtx); + return (NNG_EBUSY); + } + old = s->tls; + s->tls = tcfg; + if (tcfg) { + nni_tls_config_hold(tcfg); + } + nni_mtx_unlock(&s->mtx); + if (old) { + nng_tls_config_fini(old); + } + return (0); +} +#endif + static int http_server_sys_init(void) { diff --git a/src/supplemental/mbedtls/CMakeLists.txt b/src/supplemental/mbedtls/CMakeLists.txt deleted file mode 100644 index 5c2de10b..00000000 --- a/src/supplemental/mbedtls/CMakeLists.txt +++ /dev/null @@ -1,54 +0,0 @@ -# -# Copyright 2017 Garrett D'Amore <garrett@damore.org> -# Copyright 2017 Capitar IT Group BV <info@capitar.com> -# -# 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. -# - -# MBEDTLS library - -# This requires the mbedTLS library be installed somewhere. You can -# point this at a suitable installation of mbedTLS by setting -# MBEDTLS_ROOT_DIR to point at the root of the installation (prefix). - -# It is possible to minimize the mbedTLS library quite a bit. We do -# not require legacy algorithms, the net_sockets layer, the filesystem -# I/O, as well as various other tidbits. We provide an entropy source, -# so you can disable that in mbedTLS too. You may disable fallback support, -# as we only support TLS v1.2 at present. (You may also therefore remove -# code to support older versions of TLS/SSL.) You may also remove DTLS, -# since we're not using it now (nor are we likely to in the near feature). -# Also you may remove support for ZLIB compression, we don't use it either -# (and it would be insecure to do so.) PEM and X509 writing (encoding) -# is not needed (but parse support is!) You may also remove session support, -# as we don't use that either. -# -# (Look for a sample config.h in this directory, if you want to build -# a minimized version just for nng.) - -# What we do require is support for TLSv1.2 - -if (NNG_MBEDTLS_ENABLE) - set(SUPP_SOURCES supplemental/mbedtls/tls.c supplemental/tls.h) - Find_Package(mbedTLS REQUIRED) - - # If it isn't already in the link list, add the TLS libraries there. - # or something, so we take care not to duplicate it). - list(FIND NNG_REQUIRED_LIBRARIES ${MBEDTLS_TLS_LIBRARY} _index) - if (_index EQUAL -1) - set(NNG_REQUIRED_LIBRARIES ${NNG_REQUIRED_LIBRARIES} ${MBEDTLS_LIBRARIES}) - set(NNG_REQUIRED_LIBRARIES ${NNG_REQUIRED_LIBRARIES} PARENT_SCOPE) - endif() - - # Likewise for the include search path. - list(FIND NNG_REQUIRED_INCLUDES ${MBEDTLS_INCLUDE_DIR} _index) - if (_index EQUAL -1) - set(NNG_REQUIRED_INCLUDES ${NNG_REQUIRED_INCLUDES} ${MBEDTLS_INCLUDE_DIR}) - set(NNG_REQUIRED_INCLUDES ${NNG_REQUIRED_INCLUDES} PARENT_SCOPE) - endif() -endif() - -set(NNG_SOURCES ${NNG_SOURCES} ${SUPP_SOURCES} PARENT_SCOPE) diff --git a/src/supplemental/tls.h b/src/supplemental/tls.h deleted file mode 100644 index da2fe8cd..00000000 --- a/src/supplemental/tls.h +++ /dev/null @@ -1,84 +0,0 @@ -// -// Copyright 2017 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 -// 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. -// - -#ifndef NNG_SUPPLEMENTAL_TLS_H -#define NNG_SUPPLEMENTAL_TLS_H - -// nni_tls represents the context for a single TLS stream. -typedef struct nni_tls nni_tls; - -// nni_tls_config is the context for full TLS configuration, normally -// associated with an endpoint, for example. -typedef struct nni_tls_config nni_tls_config; - -#define NNI_TLS_CONFIG_SERVER 1 -#define NNI_TLS_CONFIG_CLIENT 0 - -extern int nni_tls_config_init(nni_tls_config **, int); -extern void nni_tls_config_fini(nni_tls_config *); - -// nni_tls_config_server_name is used by clients to set the server name -// that they expect to be talking to. This may also support the SNI -// extension for virtual hosting. -extern int nni_tls_config_server_name(nni_tls_config *, const char *); - -// nni_tls_config_ca_cert configures one or more CAs used for validation -// 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 certs may be in PEM or DER -// format. -extern int nni_tls_config_ca_cert(nni_tls_config *, const uint8_t *, size_t); - -// nni_tls_config_clr loads a certificate revocation list. Again, these -// are in X.509 format (either PEM or DER). -extern int nni_tls_config_crl(nni_tls_config *, const uint8_t *, size_t); - -// nni_tls_config_cert is used to load our own certificate. For servers, -// this may be called more than once to configure multiple different keys, -// for example with different algorithms depending on what the peer supports. -// On the client, only a single option is available. -extern int nni_tls_config_cert(nni_tls_config *, const uint8_t *crt, size_t); -extern int nni_tls_config_key(nni_tls_config *, const uint8_t *, size_t); -extern int nni_tls_config_pass(nni_tls_config *, const char *); - -// nni_tls_config_validate_peer is used to enable validation of the peer -// and it's certificate. If disabled, the peer's certificate will still -// be available, but may not be valid. -extern int nni_tls_config_validate_peer(nni_tls_config *, bool); - -// nni_tls_config_auth_mode is a read-ony option that is used to configure -// the authentication mode use. The default is that servers have this off -// (i.e. no client authentication) and clients have it on (they verify -// the server), which matches typical practice. -extern int nni_tls_config_auth_mode(nni_tls_config *, int); -#define NNI_TLS_CONFIG_AUTH_MODE_NONE 0 // No verification is performed -#define NNI_TLS_CONFIG_AUTH_MODE_OPTIONAL 1 // Verify cert if presented -#define NNI_TLS_CONFIG_AUTH_MODE_REQUIRED 2 // Verify cert, close if invalid - -extern int nni_tls_init(nni_tls **, nni_tls_config *, nni_plat_tcp_pipe *); -extern void nni_tls_close(nni_tls *); -extern void nni_tls_fini(nni_tls *); -extern void nni_tls_send(nni_tls *, nni_aio *); -extern void nni_tls_recv(nni_tls *, nni_aio *); - -// nni_tls_verified returns true if the peer, or false if the peer did not -// verify. (During the handshake phase, the peer is not verified, so this -// might return false if executed too soon. The verification status will -// be accurate once the handshake is finished, however. -extern int nni_tls_verified(nni_tls *); - -// nni_tls_ciphersuite_name returns the name of the ciphersuite in use. -extern const char *nni_tls_ciphersuite_name(nni_tls *); - -// TBD: getting additional peer certificate information... - -extern void nni_tls_strerror(int, char *, size_t); // review this - -#endif // NNG_SUPPLEMENTAL_TLS_H diff --git a/src/supplemental/tls/CMakeLists.txt b/src/supplemental/tls/CMakeLists.txt new file mode 100644 index 00000000..e78f1c13 --- /dev/null +++ b/src/supplemental/tls/CMakeLists.txt @@ -0,0 +1,38 @@ +# +# Copyright 2017 Capitar IT Group BV <info@capitar.com> +# Copyright 2017 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_SUPP_TLS) + set(NNG_SUPP_TLS_MBEDTLS ON) + set(TLS_SOURCES supplemental/tls/tls.h) +endif() + +# For now we only support the ARM mbedTLS library. +if (NNG_SUPP_TLS_MBEDTLS) + + Find_Package(mbedTLS REQUIRED) + + # If it isn't already in the link list, add the TLS libraries there. + # or something, so we take care not to duplicate it). + list(FIND NNG_REQUIRED_LIBRARIES ${MBEDTLS_TLS_LIBRARY} _index) + if (_index EQUAL -1) + set(NNG_REQUIRED_LIBRARIES ${NNG_REQUIRED_LIBRARIES} ${MBEDTLS_LIBRARIES}) + set(NNG_REQUIRED_LIBRARIES ${NNG_REQUIRED_LIBRARIES} PARENT_SCOPE) + endif() + + # Likewise for the include search path. + list(FIND NNG_REQUIRED_INCLUDES ${MBEDTLS_INCLUDE_DIR} _index) + if (_index EQUAL -1) + set(NNG_REQUIRED_INCLUDES ${NNG_REQUIRED_INCLUDES} ${MBEDTLS_INCLUDE_DIR}) + set(NNG_REQUIRED_INCLUDES ${NNG_REQUIRED_INCLUDES} PARENT_SCOPE) + endif() + set(TLS_SOURCES ${TLS_SOURCES} supplemental/tls/mbedtls/tls.c) +endif() + +set(NNG_SOURCES ${NNG_SOURCES} ${TLS_SOURCES} PARENT_SCOPE) diff --git a/src/supplemental/mbedtls/tls.c b/src/supplemental/tls/mbedtls/tls.c index d64447ac..3bbf4a33 100644 --- a/src/supplemental/mbedtls/tls.c +++ b/src/supplemental/tls/mbedtls/tls.c @@ -8,7 +8,6 @@ // found online at https://opensource.org/licenses/MIT. // -#ifdef NNG_MBEDTLS_ENABLE #include <stdbool.h> #include <stdio.h> #include <stdlib.h> @@ -29,7 +28,7 @@ #include "core/nng_impl.h" -#include "supplemental/tls.h" +#include "supplemental/tls/tls.h" // Implementation note. This implementation buffers data between the TLS // encryption layer (mbedTLS) and the underlying TCP socket. As a result, @@ -66,6 +65,7 @@ typedef struct nni_tls_certkey { struct nni_tls { nni_plat_tcp_pipe * 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; @@ -86,7 +86,7 @@ struct nni_tls { nni_aio * handshake; // handshake aio (upper) }; -struct nni_tls_config { +struct nng_tls_config { mbedtls_ssl_config cfg_ctx; nni_mtx lk; bool active; @@ -100,6 +100,8 @@ struct nni_tls_config { bool have_ca_certs; bool have_crl; + int refcnt; // servers increment the reference + nni_list certkeys; }; @@ -142,7 +144,7 @@ nni_tls_random(void *arg, unsigned char *buf, size_t sz) { #ifdef NNG_TLS_USE_CTR_DRBG int rv; - nni_tls_config *cfg = arg; + nng_tls_config *cfg = arg; NNI_ARG_UNUSED(arg); nni_mtx_lock(&cfg->rng_lk); @@ -155,10 +157,18 @@ nni_tls_random(void *arg, unsigned char *buf, size_t sz) } void -nni_tls_config_fini(nni_tls_config *cfg) +nng_tls_config_fini(nng_tls_config *cfg) { nni_tls_certkey *ck; + nni_mtx_lock(&cfg->lk); + cfg->refcnt--; + if (cfg->refcnt != 0) { + nni_mtx_unlock(&cfg->lk); + return; + } + nni_mtx_unlock(&cfg->lk); + mbedtls_ssl_config_free(&cfg->cfg_ctx); #ifdef NNG_TLS_USE_CTR_DRBG mbedtls_ctr_drbg_free(&cfg->rng_ctx); @@ -189,9 +199,9 @@ nni_tls_config_fini(nni_tls_config *cfg) } int -nni_tls_config_init(nni_tls_config **cpp, int mode) +nng_tls_config_init(nng_tls_config **cpp, enum nng_tls_mode mode) { - nni_tls_config *cfg; + nng_tls_config *cfg; int rv; int sslmode; int authmode; @@ -199,8 +209,9 @@ nni_tls_config_init(nni_tls_config **cpp, int mode) if ((cfg = NNI_ALLOC_STRUCT(cfg)) == NULL) { return (NNG_ENOMEM); } + cfg->refcnt = 1; nni_mtx_init(&cfg->lk); - if (mode == NNI_TLS_CONFIG_SERVER) { + if (mode == NNG_TLS_MODE_SERVER) { sslmode = MBEDTLS_SSL_IS_SERVER; authmode = MBEDTLS_SSL_VERIFY_NONE; } else { @@ -216,7 +227,7 @@ nni_tls_config_init(nni_tls_config **cpp, int mode) 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); + nng_tls_config_fini(cfg); return (rv); } @@ -231,7 +242,7 @@ nni_tls_config_init(nni_tls_config **cpp, int mode) rv = mbedtls_ctr_drbg_seed( &cfg->rng_ctx, nni_tls_get_entropy, NULL, NULL, 0); if (rv != 0) { - nni_tls_config_fini(cfg); + nng_tls_config_fini(cfg); return (rv); } #endif @@ -244,6 +255,14 @@ nni_tls_config_init(nni_tls_config **cpp, int mode) } void +nni_tls_config_hold(nng_tls_config *cfg) +{ + nni_mtx_lock(&cfg->lk); + cfg->refcnt++; + nni_mtx_unlock(&cfg->lk); +} + +void nni_tls_fini(nni_tls *tp) { // Shut it all down first. @@ -263,6 +282,10 @@ nni_tls_fini(nni_tls *tp) nni_mtx_fini(&tp->lk); nni_free(tp->recvbuf, NNG_TLS_MAX_RECV_SIZE); nni_free(tp->sendbuf, NNG_TLS_MAX_RECV_SIZE); + if (tp->cfg != NULL) { + // release the hold we got on it + nng_tls_config_fini(tp->cfg); + } NNI_FREE_STRUCT(tp); } @@ -290,7 +313,7 @@ nni_tls_mkerr(int err) } int -nni_tls_init(nni_tls **tpp, nni_tls_config *cfg, nni_plat_tcp_pipe *tcp) +nni_tls_init(nni_tls **tpp, nng_tls_config *cfg, nni_plat_tcp_pipe *tcp) { nni_tls *tp; int rv; @@ -348,6 +371,8 @@ nni_tls_init(nni_tls **tpp, nni_tls_config *cfg, nni_plat_tcp_pipe *tcp) } cfg->active = true; } + cfg->refcnt++; + tp->cfg = cfg; nni_mtx_unlock(&cfg->lk); nni_aio_list_init(&tp->sends); @@ -595,6 +620,18 @@ nni_tls_recv(nni_tls *tp, nni_aio *aio) nni_mtx_unlock(&tp->lk); } +int +nni_tls_peername(nni_tls *tp, nni_sockaddr *sa) +{ + return (nni_plat_tcp_pipe_peername(tp->tcp, sa)); +} + +int +nni_tls_sockname(nni_tls *tp, nni_sockaddr *sa) +{ + return (nni_plat_tcp_pipe_sockname(tp->tcp, sa)); +} + void nni_tls_do_handshake(nni_tls *tp) { @@ -758,7 +795,7 @@ nni_tls_verified(nni_tls *tp) } int -nni_tls_config_server_name(nni_tls_config *cfg, const char *name) +nng_tls_config_server_name(nng_tls_config *cfg, const char *name) { int rv; nni_mtx_lock(&cfg->lk); @@ -776,7 +813,7 @@ nni_tls_config_server_name(nni_tls_config *cfg, const char *name) } int -nni_tls_config_auth_mode(nni_tls_config *cfg, int mode) +nng_tls_config_auth_mode(nng_tls_config *cfg, nng_tls_auth_mode mode) { nni_mtx_lock(&cfg->lk); if (cfg->active) { @@ -784,15 +821,15 @@ nni_tls_config_auth_mode(nni_tls_config *cfg, int mode) return (NNG_ESTATE); } switch (mode) { - case NNI_TLS_CONFIG_AUTH_MODE_NONE: + case NNG_TLS_AUTH_MODE_NONE: mbedtls_ssl_conf_authmode( &cfg->cfg_ctx, MBEDTLS_SSL_VERIFY_NONE); break; - case NNI_TLS_CONFIG_AUTH_MODE_OPTIONAL: + case NNG_TLS_AUTH_MODE_OPTIONAL: mbedtls_ssl_conf_authmode( &cfg->cfg_ctx, MBEDTLS_SSL_VERIFY_OPTIONAL); break; - case NNI_TLS_CONFIG_AUTH_MODE_REQUIRED: + case NNG_TLS_AUTH_MODE_REQUIRED: mbedtls_ssl_conf_authmode( &cfg->cfg_ctx, MBEDTLS_SSL_VERIFY_REQUIRED); break; @@ -844,7 +881,7 @@ nni_tls_copy_key_cert_material( } int -nni_tls_config_cert(nni_tls_config *cfg, const uint8_t *key, size_t sz) +nng_tls_config_cert(nng_tls_config *cfg, const uint8_t *key, size_t sz) { int rv = 0; nni_tls_certkey *ck; @@ -885,7 +922,7 @@ err: } int -nni_tls_config_key(nni_tls_config *cfg, const uint8_t *key, size_t sz) +nng_tls_config_key(nng_tls_config *cfg, const uint8_t *key, size_t sz) { int rv = 0; nni_tls_certkey *ck; @@ -924,7 +961,7 @@ err: } int -nni_tls_config_pass(nni_tls_config *cfg, const char *pass) +nng_tls_config_pass(nng_tls_config *cfg, const char *pass) { int rv = 0; nni_tls_certkey *ck; @@ -964,7 +1001,7 @@ err: } int -nni_tls_config_ca_cert(nni_tls_config *cfg, const uint8_t *data, size_t sz) +nng_tls_config_ca_cert(nng_tls_config *cfg, const uint8_t *data, size_t sz) { uint8_t *tmp; size_t len = sz; @@ -998,7 +1035,7 @@ err: } int -nni_tls_config_crl(nni_tls_config *cfg, const uint8_t *data, size_t sz) +nng_tls_config_crl(nng_tls_config *cfg, const uint8_t *data, size_t sz) { int rv; uint8_t *tmp; @@ -1028,4 +1065,3 @@ err: nni_free(tmp, len); return (rv); } -#endif // NNG_MBEDTLS_ENABLE
\ No newline at end of file diff --git a/src/supplemental/tls/tls.h b/src/supplemental/tls/tls.h new file mode 100644 index 00000000..0c9e791f --- /dev/null +++ b/src/supplemental/tls/tls.h @@ -0,0 +1,45 @@ +// +// Copyright 2017 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 +// 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. +// + +#ifndef NNG_SUPPLEMENTAL_TLS_TLS_H +#define NNG_SUPPLEMENTAL_TLS_TLS_H + +// nni_tls represents the context for a single TLS stream. +typedef struct nni_tls nni_tls; + +// 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 *); + +extern int nni_tls_init(nni_tls **, nng_tls_config *, nni_plat_tcp_pipe *); +extern void nni_tls_close(nni_tls *); +extern void nni_tls_fini(nni_tls *); +extern void nni_tls_send(nni_tls *, nni_aio *); +extern void nni_tls_recv(nni_tls *, nni_aio *); +extern int nni_tls_sockname(nni_tls *, nni_sockaddr *); +extern int nni_tls_peername(nni_tls *, nni_sockaddr *); + +// nni_tls_verified returns true if the peer, or false if the peer did not +// verify. (During the handshake phase, the peer is not verified, so this +// might return false if executed too soon. The verification status will +// be accurate once the handshake is finished, however. +extern int nni_tls_verified(nni_tls *); + +// nni_tls_ciphersuite_name returns the name of the ciphersuite in use. +extern const char *nni_tls_ciphersuite_name(nni_tls *); + +// TBD: getting additional peer certificate information... + +extern void nni_tls_strerror(int, char *, size_t); // review this + +#endif // NNG_SUPPLEMENTAL_TLS_TLS_H diff --git a/src/supplemental/websocket/websocket.c b/src/supplemental/websocket/websocket.c index 06e1c70a..c7181595 100644 --- a/src/supplemental/websocket/websocket.c +++ b/src/supplemental/websocket/websocket.c @@ -55,7 +55,6 @@ struct nni_ws { }; struct nni_ws_listener { - nni_tls_config * tls; nni_http_server * server; char * proto; char * url; @@ -82,7 +81,6 @@ struct nni_ws_listener { // completion of an earlier connection. (We don't want to establish // requests when we already have connects negotiating.) struct nni_ws_dialer { - nni_tls_config * tls; nni_http_req * req; nni_http_res * res; nni_http_client *client; @@ -1296,6 +1294,13 @@ nni_ws_listener_fini(nni_ws_listener *l) { ws_header *hdr; + nni_ws_listener_close(l); + + if (l->server != NULL) { + nni_http_server_fini(l->server); + l->server = NULL; + } + nni_mtx_fini(&l->mtx); nni_strfree(l->url); nni_strfree(l->proto); @@ -1560,6 +1565,8 @@ nni_ws_listener_init(nni_ws_listener **wslp, const char *url) { nni_ws_listener *l; int rv; + nni_aio * aio; + nni_sockaddr sa; if ((l = NNI_ALLOC_STRUCT(l)) == NULL) { return (NNG_ENOMEM); @@ -1582,6 +1589,24 @@ nni_ws_listener_init(nni_ws_listener **wslp, const char *url) l->handler.h_host = l->host; l->handler.h_cb = ws_handler; + if ((rv = nni_aio_init(&aio, NULL, NULL)) != 0) { + nni_ws_listener_fini(l); + return (rv); + } + aio->a_addr = &sa; + nni_plat_tcp_resolv(l->host, l->serv, NNG_AF_UNSPEC, true, aio); + nni_aio_wait(aio); + rv = nni_aio_result(aio); + nni_aio_fini(aio); + if (rv != 0) { + nni_ws_listener_fini(l); + return (rv); + } + if ((rv = nni_http_server_init(&l->server, &sa)) != 0) { + nni_ws_listener_fini(l); + return (rv); + } + *wslp = l; return (0); } @@ -1658,10 +1683,10 @@ nni_ws_listener_close(nni_ws_listener *l) return; } l->closed = true; - if (l->server != NULL) { + if (l->started) { nni_http_server_del_handler(l->server, l->hp); - nni_http_server_fini(l->server); - l->server = NULL; + nni_http_server_stop(l->server); + l->started = false; } NNI_LIST_FOREACH (&l->pend, ws) { nni_ws_close_error(ws, WS_CLOSE_GOING_AWAY); @@ -1675,9 +1700,7 @@ nni_ws_listener_close(nni_ws_listener *l) int nni_ws_listener_listen(nni_ws_listener *l) { - nng_sockaddr sa; - nni_aio * aio; - int rv; + int rv; nni_mtx_lock(&l->mtx); if (l->closed) { @@ -1689,25 +1712,6 @@ nni_ws_listener_listen(nni_ws_listener *l) return (NNG_ESTATE); } - if ((rv = nni_aio_init(&aio, NULL, NULL)) != 0) { - nni_mtx_unlock(&l->mtx); - return (rv); - } - aio->a_addr = &sa; - nni_plat_tcp_resolv(l->host, l->serv, NNG_AF_UNSPEC, true, aio); - nni_aio_wait(aio); - rv = nni_aio_result(aio); - nni_aio_fini(aio); - if (rv != 0) { - nni_mtx_unlock(&l->mtx); - return (rv); - } - - if ((rv = nni_http_server_init(&l->server, &sa)) != 0) { - nni_mtx_unlock(&l->mtx); - return (rv); - } - rv = nni_http_server_add_handler(&l->hp, l->server, &l->handler, l); if (rv != 0) { nni_http_server_fini(l->server); @@ -1716,12 +1720,12 @@ nni_ws_listener_listen(nni_ws_listener *l) return (rv); } - // XXX: DEAL WITH HTTPS here. - if ((rv = nni_http_server_start(l->server)) != 0) { nni_http_server_del_handler(l->server, l->hp); nni_http_server_fini(l->server); l->server = NULL; + nni_mtx_unlock(&l->mtx); + return (rv); } l->started = true; @@ -1740,11 +1744,17 @@ nni_ws_listener_hook( nni_mtx_unlock(&l->mtx); } -void -nni_ws_listener_tls(nni_ws_listener *l, nni_tls_config *tls) +#ifdef NNG_SUPP_TLS +int +nni_ws_listener_set_tls(nni_ws_listener *l, nng_tls_config *tls) { - // We need to add this later. + int rv; + nni_mtx_lock(&l->mtx); + rv = nni_http_server_set_tls(l->server, tls); + nni_mtx_unlock(&l->mtx); + return (rv); } +#endif void ws_conn_cb(void *arg) @@ -1931,6 +1941,18 @@ nni_ws_dialer_init(nni_ws_dialer **dp, const char *url) return (0); } +#ifdef NNG_SUPP_TLS +int +nni_ws_dialer_set_tls(nni_ws_dialer *d, nng_tls_config *tls) +{ + int rv; + nni_mtx_lock(&d->mtx); + rv = nni_http_client_set_tls(d->client, tls); + nni_mtx_unlock(&d->mtx); + return (rv); +} +#endif + void nni_ws_dialer_close(nni_ws_dialer *d) { diff --git a/src/supplemental/websocket/websocket.h b/src/supplemental/websocket/websocket.h index 95147a9f..ccf549df 100644 --- a/src/supplemental/websocket/websocket.h +++ b/src/supplemental/websocket/websocket.h @@ -12,9 +12,8 @@ #define NNG_SUPPLEMENTAL_WEBSOCKET_WEBSOCKET_H // Pre-defined types for some prototypes. These are from other subsystems. -typedef struct nni_tls_config nni_tls_config; -typedef struct nni_http_req nni_http_req; -typedef struct nni_http_res nni_http_res; +typedef struct nni_http_req nni_http_req; +typedef struct nni_http_res nni_http_res; typedef struct nni_ws nni_ws; typedef struct nni_ws_listener nni_ws_listener; @@ -36,7 +35,7 @@ extern int nni_ws_listener_listen(nni_ws_listener *); extern void nni_ws_listener_accept(nni_ws_listener *, nni_aio *); extern void nni_ws_listener_hook( nni_ws_listener *, nni_ws_listen_hook, void *); -extern void nni_ws_listener_tls(nni_ws_listener *, nni_tls_config *); +extern int nni_ws_listener_set_tls(nni_ws_listener *, nng_tls_config *); extern int nni_ws_dialer_init(nni_ws_dialer **, const char *); extern void nni_ws_dialer_fini(nni_ws_dialer *); @@ -44,6 +43,7 @@ extern void nni_ws_dialer_close(nni_ws_dialer *); extern int nni_ws_dialer_proto(nni_ws_dialer *, const char *); extern int nni_ws_dialer_header(nni_ws_dialer *, const char *, const char *); extern void nni_ws_dialer_dial(nni_ws_dialer *, nni_aio *); +extern int nni_ws_dialer_set_tls(nni_ws_dialer *, nng_tls_config *); // Dialer does not get a hook chance, as it can examine the request and reply // after dial is done; this is not a 3-way handshake, so the dialer does |
