diff options
| author | Garrett D'Amore <garrett@damore.org> | 2018-01-16 12:07:45 -0800 |
|---|---|---|
| committer | Garrett D'Amore <garrett@damore.org> | 2018-01-16 14:29:38 -0800 |
| commit | 02e6153236ae744fb614fcd14184924ec85c2993 (patch) | |
| tree | 6b41ca972d60e758c65c1adc0621500a92003c86 /src | |
| parent | bbf012364d9f1482b16c97b8bfd2fd07130446ca (diff) | |
| download | nng-02e6153236ae744fb614fcd14184924ec85c2993.tar.gz nng-02e6153236ae744fb614fcd14184924ec85c2993.tar.bz2 nng-02e6153236ae744fb614fcd14184924ec85c2993.zip | |
fixes #206 Want NNG_OPT_TLS_VERIFIED option
It is useful to have support for validating that a peer *was*
verified, especially in the presence of optional validation.
We have added a property that does this, NNG_OPT_TLS_VERIFIED.
Further, all the old NNG_OPT_WSS_TLS_* property names have also been
renamed to generic NNG_OPT_TLS property names, which have been
moved to nng.h to facilitate reuse and sharing, with the comments
moved and corrected as well.
Finally, the man pages have been updated, with substantial
improvements to the nng_ws man page in particular.
Diffstat (limited to 'src')
| -rw-r--r-- | src/nng.h | 47 | ||||
| -rw-r--r-- | src/platform/posix/posix_thread.c | 3 | ||||
| -rw-r--r-- | src/supplemental/http/http.c | 23 | ||||
| -rw-r--r-- | src/supplemental/http/http.h | 3 | ||||
| -rw-r--r-- | src/supplemental/tls/mbedtls/tls.c | 4 | ||||
| -rw-r--r-- | src/supplemental/tls/tls.h | 4 | ||||
| -rw-r--r-- | src/supplemental/websocket/websocket.c | 11 | ||||
| -rw-r--r-- | src/supplemental/websocket/websocket.h | 3 | ||||
| -rw-r--r-- | src/transport/tls/tls.c | 2 | ||||
| -rw-r--r-- | src/transport/tls/tls.h | 12 | ||||
| -rw-r--r-- | src/transport/ws/websocket.c | 67 | ||||
| -rw-r--r-- | src/transport/ws/websocket.h | 54 |
12 files changed, 136 insertions, 97 deletions
@@ -368,6 +368,53 @@ enum nng_flag_enum { #define NNG_OPT_RECONNMINT "reconnect-time-min" #define NNG_OPT_RECONNMAXT "reconnect-time-max" +// TLS options are only used when the underlying transport supports TLS. + +// NNG_OPT_TLS_CONFIG is a pointer to an nng_tls_config object. Generally +// this can used with endpoints, although once an endpoint is started, or +// once a configuration is used, the value becomes read-only. Note that +// when configuring the object, a hold is placed on the TLS configuration, +// using a reference count. When retrieving the object, no such hold is +// placed, and so the caller must take care not to use the associated object +// after the endpoint it is associated with is closed. +#define NNG_OPT_TLS_CONFIG "tls-config" + +// NNG_OPT_TLS_AUTH_MODE is a write-only integer (int) option that specifies +// whether peer authentication is needed. The option can take one of the +// values of NNG_TLS_AUTH_MODE_NONE, NNG_TLS_AUTH_MODE_OPTIONAL, or +// NNG_TLS_AUTH_MODE_REQUIRED. The default is typically NNG_TLS_AUTH_MODE_NONE +// for listeners, and NNG_TLS_AUTH_MODE_REQUIRED for dialers. If set to +// REQUIRED, then connections will be rejected if the peer cannot be verified. +// If set to OPTIONAL, then a verification step takes place, but the connection +// is still permitted. (The result can be checked with NNG_OPT_TLS_VERIFIED). +#define NNG_OPT_TLS_AUTH_MODE "tls-authmode" + +// NNG_OPT_TLS_CERT_KEY_FILE names a single file that contains a certificate +// and key identifying the endpoint. This is a write-only value. This can be +// set multiple times for times for different keys/certs corresponding to +// different algorithms on listeners, whereas dialers only support one. The +// file must contain both cert and key as PEM blocks, and the key must +// not be encrypted. (If more flexibility is needed, use the TLS configuration +// directly, via NNG_OPT_TLS_CONFIG.) +#define NNG_OPT_TLS_CERT_KEY_FILE "tls-cert-key-file" + +// NNG_OPT_TLS_CA_FILE names a single file that contains certificate(s) for a +// CA, and optionally CRLs, which are used to validate the peer's certificate. +// This is a write-only value, but multiple CAs can be loaded by setting this +// multiple times. +#define NNG_OPT_TLS_CA_FILE "tls-ca-file" + +// NNG_OPT_TLS_SERVER_NAME is a write-only string that can typically be +// set on dialers to check the CN of the server for a match. This +// can also affect SNI (server name indication). It usually has no effect +// on listeners. +#define NNG_OPT_TLS_SERVER_NAME "tls-server-name" + +// NNG_OPT_TLS_VERIFIED returns a single integer, indicating whether the peer +// has been verified (1) or not (0). Typically this is read-only, and only +// available for pipes. +#define NNG_OPT_TLS_VERIFIED "tls-verified" + // XXX: TBD: priorities, socket names, ipv4only // Statistics. These are for informational purposes only, and subject diff --git a/src/platform/posix/posix_thread.c b/src/platform/posix/posix_thread.c index 115768dc..d3817d91 100644 --- a/src/platform/posix/posix_thread.c +++ b/src/platform/posix/posix_thread.c @@ -64,6 +64,9 @@ nni_plat_mtx_fini(nni_plat_mtx *mtx) { if (mtx->flags & NNI_PLAT_SYNC_INIT) { int rv; + // Locking and unlocking makes valgrind/helgrind happier. + pthread_mutex_lock(&mtx->mtx); + pthread_mutex_unlock(&mtx->mtx); if ((rv = pthread_mutex_destroy(&mtx->mtx)) != 0) { nni_panic("pthread_mutex_destroy: %s", strerror(rv)); } diff --git a/src/supplemental/http/http.c b/src/supplemental/http/http.c index 229a4a99..43db1d15 100644 --- a/src/supplemental/http/http.c +++ b/src/supplemental/http/http.c @@ -40,6 +40,7 @@ typedef struct nni_http_tran { void (*h_write)(void *, nni_aio *); int (*h_sock_addr)(void *, nni_sockaddr *); int (*h_peer_addr)(void *, nni_sockaddr *); + bool (*h_verified)(void *); void (*h_close)(void *); void (*h_fini)(void *); } nni_http_tran; @@ -55,6 +56,7 @@ struct nni_http { void (*wr)(void *, nni_aio *); int (*sock_addr)(void *, nni_sockaddr *); int (*peer_addr)(void *, nni_sockaddr *); + bool (*verified)(void *); void (*close)(void *); void (*fini)(void *); @@ -610,6 +612,17 @@ nni_http_peer_addr(nni_http *http, nni_sockaddr *sa) return (rv); } +bool +nni_http_tls_verified(nni_http *http) +{ + bool rv; + + nni_mtx_lock(&http->mtx); + rv = http->closed ? false : http->verified(http->sock); + nni_mtx_unlock(&http->mtx); + return (rv); +} + void nni_http_fini(nni_http *http) { @@ -655,6 +668,7 @@ http_init(nni_http **httpp, nni_http_tran *tran, void *data) http->fini = tran->h_fini; http->sock_addr = tran->h_sock_addr; http->peer_addr = tran->h_peer_addr; + http->verified = tran->h_verified; 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)) { @@ -667,6 +681,13 @@ http_init(nni_http **httpp, nni_http_tran *tran, void *data) return (0); } +static bool +nni_http_verified_tcp(void *arg) +{ + NNI_ARG_UNUSED(arg); + return (false); +} + static nni_http_tran http_tcp_ops = { .h_read = (void *) nni_plat_tcp_pipe_recv, .h_write = (void *) nni_plat_tcp_pipe_send, @@ -674,6 +695,7 @@ static nni_http_tran http_tcp_ops = { .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, + .h_verified = nni_http_verified_tcp, }; int @@ -690,6 +712,7 @@ static nni_http_tran http_tls_ops = { .h_fini = (void *) nni_tls_fini, .h_sock_addr = (void *) nni_tls_sockname, .h_peer_addr = (void *) nni_tls_peername, + .h_verified = (void *) nni_tls_verified, }; int diff --git a/src/supplemental/http/http.h b/src/supplemental/http/http.h index 06394fdd..47c8d654 100644 --- a/src/supplemental/http/http.h +++ b/src/supplemental/http/http.h @@ -156,6 +156,9 @@ extern void nni_http_write_full(nni_http *, nni_aio *); extern int nni_http_sock_addr(nni_http *, nni_sockaddr *); extern int nni_http_peer_addr(nni_http *, nni_sockaddr *); +// nni_tls_http_verified returns true if the peer has been verified using TLS. +extern bool nni_http_tls_verified(nni_http *); + typedef struct nni_http_server nni_http_server; typedef struct { diff --git a/src/supplemental/tls/mbedtls/tls.c b/src/supplemental/tls/mbedtls/tls.c index 4e846f98..7b959b2b 100644 --- a/src/supplemental/tls/mbedtls/tls.c +++ b/src/supplemental/tls/mbedtls/tls.c @@ -751,13 +751,13 @@ nni_tls_ciphersuite_name(nni_tls *tp) return (mbedtls_ssl_get_ciphersuite(&tp->ctx)); } -int +bool nni_tls_verified(nni_tls *tp) { int rv; rv = mbedtls_ssl_get_verify_result(&tp->ctx); - return (rv ? 1 : 0); + return (rv ? true : false); } int diff --git a/src/supplemental/tls/tls.h b/src/supplemental/tls/tls.h index 5fde50b4..57b552d7 100644 --- a/src/supplemental/tls/tls.h +++ b/src/supplemental/tls/tls.h @@ -11,6 +11,8 @@ #ifndef NNG_SUPPLEMENTAL_TLS_TLS_H #define NNG_SUPPLEMENTAL_TLS_TLS_H +#include <stdbool.h> + // nni_tls represents the context for a single TLS stream. typedef struct nni_tls nni_tls; @@ -41,7 +43,7 @@ extern int nni_tls_peername(nni_tls *, nni_sockaddr *); // 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 *); +extern bool 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 *); diff --git a/src/supplemental/websocket/websocket.c b/src/supplemental/websocket/websocket.c index fe4f002f..514cf22d 100644 --- a/src/supplemental/websocket/websocket.c +++ b/src/supplemental/websocket/websocket.c @@ -1072,6 +1072,17 @@ nni_ws_response_headers(nni_ws *ws) return (ws->reshdrs); } +bool +nni_ws_tls_verified(nni_ws *ws) +{ + bool rv; + + nni_mtx_lock(&ws->mtx); + rv = nni_http_tls_verified(ws->http); + nni_mtx_unlock(&ws->mtx); + return (rv); +} + static void ws_fini(void *arg) { diff --git a/src/supplemental/websocket/websocket.h b/src/supplemental/websocket/websocket.h index 9a52f78c..ddd09b72 100644 --- a/src/supplemental/websocket/websocket.h +++ b/src/supplemental/websocket/websocket.h @@ -11,6 +11,8 @@ #ifndef NNG_SUPPLEMENTAL_WEBSOCKET_WEBSOCKET_H #define NNG_SUPPLEMENTAL_WEBSOCKET_WEBSOCKET_H +#include <stdbool.h> + // Pre-defined types for some prototypes. These are from other subsystems. typedef struct nni_http_req nni_http_req; typedef struct nni_http_res nni_http_res; @@ -63,6 +65,7 @@ extern void nni_ws_close_error(nni_ws *, uint16_t); extern void nni_ws_fini(nni_ws *); extern const char * nni_ws_response_headers(nni_ws *); extern const char * nni_ws_request_headers(nni_ws *); +extern bool nni_ws_tls_verified(nni_ws *); // The implementation will send periodic PINGs, and respond with PONGs. diff --git a/src/transport/tls/tls.c b/src/transport/tls/tls.c index 408ff50c..05d477b5 100644 --- a/src/transport/tls/tls.c +++ b/src/transport/tls/tls.c @@ -855,7 +855,7 @@ tls_getopt_verified(void *arg, void *v, size_t *szp) static nni_tran_pipe_option nni_tls_pipe_options[] = { { NNG_OPT_LOCADDR, nni_tls_pipe_getopt_locaddr }, { NNG_OPT_REMADDR, nni_tls_pipe_getopt_remaddr }, - { NNG_OPT_TLS_AUTH_VERIFIED, tls_getopt_verified }, + { NNG_OPT_TLS_VERIFIED, tls_getopt_verified }, // terminate list { NULL, NULL } }; diff --git a/src/transport/tls/tls.h b/src/transport/tls/tls.h index 25edfa3a..a3fa0eb9 100644 --- a/src/transport/tls/tls.h +++ b/src/transport/tls/tls.h @@ -15,16 +15,4 @@ NNG_DECL int nng_tls_register(void); -// TLS options. Note that these can only be set *before* the endpoint is -// started. Once started, it is no longer possible to alter the TLS -// configuration. - -// NNG_OPT_TLS_AUTH_VERIFIED is a boolean that can be read on pipes, -// indicating whether the peer certificate is verified. -#define NNG_OPT_TLS_AUTH_VERIFIED "tls:auth-verified" - -// NNG_OPT_TLS_CONFIG is used to access the underlying configuration -// (an nng_tls_config *). -#define NNG_OPT_TLS_CONFIG "tls:config" - #endif // NNG_TRANSPORT_TLS_TLS_H diff --git a/src/transport/ws/websocket.c b/src/transport/ws/websocket.c index 16cdf47b..a06910d3 100644 --- a/src/transport/ws/websocket.c +++ b/src/transport/ws/websocket.c @@ -43,6 +43,7 @@ struct ws_ep { nni_ws_listener *listener; nni_ws_dialer * dialer; nni_list headers; // to send, res or req + bool started; }; struct ws_pipe { @@ -261,16 +262,13 @@ ws_hook(void *arg, nni_http_req *req, nni_http_res *res) // Eventually we'll want user customizable hooks. // For now we just set the headers we want. - nni_mtx_lock(&ep->mtx); NNI_LIST_FOREACH (&ep->headers, h) { int rv; - rv = nni_http_req_set_header(req, h->name, h->value); + rv = nni_http_res_set_header(res, h->name, h->value); if (rv != 0) { - nni_mtx_unlock(&ep->mtx); return (rv); } } - nni_mtx_unlock(&ep->mtx); return (0); } @@ -278,9 +276,13 @@ static int ws_ep_bind(void *arg) { ws_ep *ep = arg; + int rv; nni_ws_listener_hook(ep->listener, ws_hook, ep); - return (nni_ws_listener_listen(ep->listener)); + if ((rv = nni_ws_listener_listen(ep->listener)) == 0) { + ep->started = true; + } + return (rv); } static void @@ -320,28 +322,29 @@ static void ws_ep_connect(void *arg, nni_aio *aio) { ws_ep * ep = arg; - int rv; + int rv = 0; ws_hdr *h; + if (!ep->started) { + NNI_LIST_FOREACH (&ep->headers, h) { + rv = nni_ws_dialer_header( + ep->dialer, h->name, h->value); + if (rv != 0) { + nni_aio_finish_error(aio, rv); + return; + } + } + } + nni_mtx_lock(&ep->mtx); NNI_ASSERT(nni_list_empty(&ep->aios)); - // If we can't start, then its dying and we can't report - // either. + // If we can't start, then its dying and we can't report either. if ((rv = nni_aio_start(aio, ws_ep_cancel, ep)) != 0) { nni_mtx_unlock(&ep->mtx); return; } - - NNI_LIST_FOREACH (&ep->headers, h) { - rv = nni_ws_dialer_header(ep->dialer, h->name, h->value); - if (rv != 0) { - nni_aio_finish_error(aio, rv); - nni_mtx_unlock(&ep->mtx); - return; - } - } - + ep->started = true; nni_list_append(&ep->aios, aio); nni_ws_dialer_dial(ep->dialer, ep->connaio); nni_mtx_unlock(&ep->mtx); @@ -374,6 +377,10 @@ ws_ep_setopt_headers(ws_ep *ep, const void *v, size_t sz) return (0); } + if (ep->started) { + return (NNG_EBUSY); + } + NNI_LIST_INIT(&l, ws_hdr, node); if ((dupstr = nni_strdup(v)) == NULL) { return (NNG_ENOMEM); @@ -418,7 +425,6 @@ ws_ep_setopt_headers(ws_ep *ep, const void *v, size_t sz) name = nl; } - nni_mtx_lock(&ep->mtx); while ((h = nni_list_first(&ep->headers)) != NULL) { nni_list_remove(&ep->headers, h); nni_strfree(h->name); @@ -429,7 +435,6 @@ ws_ep_setopt_headers(ws_ep *ep, const void *v, size_t sz) nni_list_remove(&l, h); nni_list_append(&ep->headers, h); } - nni_mtx_unlock(&ep->mtx); rv = 0; done: @@ -532,6 +537,13 @@ ws_pipe_getopt_reqhdrs(void *arg, void *v, size_t *szp) return (nni_getopt_str(s, v, szp)); } +static int +ws_pipe_getopt_tls_verified(void *arg, void *v, size_t *szp) +{ + ws_pipe *p = arg; + return (nni_getopt_int(nni_ws_tls_verified(p->ws) ? 1 : 0, v, szp)); +} + static nni_tran_pipe_option ws_pipe_options[] = { // clang-format off @@ -539,6 +551,7 @@ static nni_tran_pipe_option ws_pipe_options[] = { { NNG_OPT_REMADDR, ws_pipe_getopt_remaddr }, { NNG_OPT_WS_REQUEST_HEADERS, ws_pipe_getopt_reqhdrs }, { NNG_OPT_WS_RESPONSE_HEADERS, ws_pipe_getopt_reshdrs }, + { NNG_OPT_TLS_VERIFIED, ws_pipe_getopt_tls_verified }, // clang-format on // terminate list @@ -931,37 +944,37 @@ static nni_tran_ep_option wss_ep_options[] = { .eo_setopt = ws_ep_setopt_recvmaxsz, }, { - .eo_name = NNG_OPT_WSS_REQUEST_HEADERS, + .eo_name = NNG_OPT_WS_REQUEST_HEADERS, .eo_getopt = NULL, .eo_setopt = ws_ep_setopt_reqhdrs, }, { - .eo_name = NNG_OPT_WSS_RESPONSE_HEADERS, + .eo_name = NNG_OPT_WS_RESPONSE_HEADERS, .eo_getopt = NULL, .eo_setopt = ws_ep_setopt_reshdrs, }, { - .eo_name = NNG_OPT_WSS_TLS_CONFIG, + .eo_name = NNG_OPT_TLS_CONFIG, .eo_getopt = wss_ep_getopt_tlsconfig, .eo_setopt = wss_ep_setopt_tlsconfig, }, { - .eo_name = NNG_OPT_WSS_TLS_CERT_KEY_FILE, + .eo_name = NNG_OPT_TLS_CERT_KEY_FILE, .eo_getopt = NULL, .eo_setopt = wss_ep_setopt_tls_cert_key_file, }, { - .eo_name = NNG_OPT_WSS_TLS_CA_FILE, + .eo_name = NNG_OPT_TLS_CA_FILE, .eo_getopt = NULL, .eo_setopt = wss_ep_setopt_tls_ca_file, }, { - .eo_name = NNG_OPT_WSS_TLS_AUTH_MODE, + .eo_name = NNG_OPT_TLS_AUTH_MODE, .eo_getopt = NULL, .eo_setopt = wss_ep_setopt_tls_auth_mode, }, { - .eo_name = NNG_OPT_WSS_TLS_SERVER_NAME, + .eo_name = NNG_OPT_TLS_SERVER_NAME, .eo_getopt = NULL, .eo_setopt = wss_ep_setopt_tls_server_name, }, diff --git a/src/transport/ws/websocket.h b/src/transport/ws/websocket.h index 1f261067..76e94c3e 100644 --- a/src/transport/ws/websocket.h +++ b/src/transport/ws/websocket.h @@ -23,60 +23,6 @@ NNG_DECL int nng_ws_register(void); // response headers, formatted as CRLF terminated lines. #define NNG_OPT_WS_RESPONSE_HEADERS "ws:response-headers" -// NNG_OPT_WSS_TLS_CONFIG is a pointer to a an nng_tls_config -// object. This property is only available for wss:// style -// endpoints. Note that when configuring the object, a hold -// is placed on the TLS configuration. When retrieving the -// object, no hold is placed, and so the caller must take care -// not to use the configuration object after the endpoint it -// is associated with is removed. Furthermore, as this is a -// pointer, applications must take care to pass only valid -// data -- incorrect pointer values will lead to undefined -// behavior. -#define NNG_OPT_WSS_TLS_CONFIG "wss:tls-config" - -// NNG_OPT_WSS_TLS_CERT_KEY_FILE names a single file that -// contains a certificate and key identifying ourself. This -// is a write-only value. Listeners can call this multiple -// times for different keys/certs corresponding to different -// algorithms, whereas clients only get one. The file must -// contain both cert and key as PEM blocks, and the key must -// not be encrypted. (If more flexibility is needed, use the -// TLS configuration directly.) Note that TLS configuration -// cannot be changed if the listener, or any other from the same -// server and port, is already started. -#define NNG_OPT_WSS_TLS_CERT_KEY_FILE "wss:tls-cert-key-file" - -// NNG_OPT_WSS_TLS_CA_FILE names a single file that -// contains certificate(s) for a CA, and optinally CRLs. This -// is a write-only value. Listeners can call this multiple -// times for different keys/certs corresponding to different -// algorithms, whereas clients only get one. The file must -// contain certs as PEM blocks, and may contain CRLs as PEM -// as well. (If more flexibility is needed, use the -// TLS configuration directly.) Note that TLS configuration -// cannot be changed if the listener, or any other from the same -// server and port, is already started. -#define NNG_OPT_WSS_TLS_CA_FILE "wss:tls-ca-file" - -// NNG_OPT_WSS_TLS_AUTH_MODE is a write-only integer (int) option -// that specifies whether the peer is verified or not. The option -// can take one of the values of NNG_TLS_AUTH_MODE_NONE, -// NNG_TLS_AUTH_MODE_OPTIONAL, or NNG_TLS_AUTH_MODE_REQUIRED. -// The default is NNG_TLS_AUTH_MODE_NONE for listeners, and -// NNG_TLS_AUTH_MODE_REQUIRED for dialers. -#define NNG_OPT_WSS_TLS_AUTH_MODE "wss:tls-auth-mode" - -// NNG_OPT_WSS_TLS_SERVER_NAME is a write-only string that can be -// set on dialers to check the CN of the server for a match. This -// can also affect SNI (server name indication). -#define NNG_OPT_WSS_TLS_SERVER_NAME "wss:tls-server-name" - -// NNG_OPT_WSS_TLS_VERIFIED returns a single integer, indicating -// whether the peer was verified or not. This is a read-only value -// available only on pipes. -#define NNT_OPT_WSS_TLS_VERIFIED "wss:tls-verified" - // These aliases are for WSS naming consistency. #define NNG_OPT_WSS_REQUEST_HEADERS NNG_OPT_WS_REQUEST_HEADERS #define NNG_OPT_WSS_RESPONSE_HEADERS NNG_OPT_WS_RESPONSE_HEADERS |
