From 02178a8b5843a2c5a59fb7b104e4f9f5df1ff5ee Mon Sep 17 00:00:00 2001 From: Garrett D'Amore Date: Thu, 9 Nov 2017 14:09:14 -0800 Subject: fixes #3 TLS transport This introduces a new transport (compatible with the TLS transport from mangos), using TLS v1.2. To use the new transport, you must have the mbed TLS library available on your system (Xenial libmbedtls-dev). You can use version 2.x or newer -- 1.3.x and PolarSSL versions are not supported. You enable the TLS transport with -DNNG_TRANSPORT_TLS=ON in the CMake configuration. You must configure the server certificate by default, and this can only be done using nng options. See the nng_tls man page for details. This work is experimental, and was made possible by Capitar IT Group BV, and Staysail Systems, Inc. --- tests/CMakeLists.txt | 1 + tests/tls.c | 186 +++++++++++++++++++++++++++++++++++++++++++++++++++ tests/trantest.h | 90 ++++++++++++++++++++----- 3 files changed, 261 insertions(+), 16 deletions(-) create mode 100644 tests/tls.c (limited to 'tests') diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index f6a476ab..30f76a1d 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -139,6 +139,7 @@ add_nng_test(sock 5) add_nng_test(survey 5) add_nng_test(synch 5) add_nng_test(transport 5) +add_nng_test(tls 10) add_nng_test(tcp 5) add_nng_test(tcp6 5) add_nng_test(scalability 20) diff --git a/tests/tls.c b/tests/tls.c new file mode 100644 index 00000000..e4e430af --- /dev/null +++ b/tests/tls.c @@ -0,0 +1,186 @@ +// +// Copyright 2017 Garrett D'Amore +// Copyright 2017 Capitar IT Group BV +// +// 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 "convey.h" +#include "nng.h" +#include "protocol/pair1/pair.h" + +#include "transport/tls/tls.h" + +#include "trantest.h" + +#include "stubs.h" +// TCP tests. + +#ifndef _WIN32 +#include +#endif + +// These keys are for demonstration purposes ONLY. DO NOT USE. +// The certificate is valid for 100 years, because I don't want to +// have to regenerate it ever again. The CN is 127.0.0.1, and self-signed. +// +// Generated using openssl: +// +// % openssl ecparam -name secp521r1 -noout -genkey -out key.key +// % openssl req -new -key key.key -out cert.csr +// % openssl x509 -req -in cert.csr -days 36500 -out cert.crt -signkey key.key +// +// Relevant metadata: +// +// Certificate: +// Data: +// Version: 1 (0x0) +// Serial Number: 9808857926806240008 (0x882010509b8f7b08) +// Signature Algorithm: ecdsa-with-SHA1 +// Issuer: C=US, ST=CA, L=San Diego, O=nanomsg, CN=127.0.0.1 +// Validity +// Not Before: Nov 17 20:08:06 2017 GMT +// Not After : Oct 24 20:08:06 2117 GMT +// Subject: C=US, ST=CA, L=San Diego, O=nanomsg, CN=127.0.0.1 +// +static const char server_cert[] = + "-----BEGIN CERTIFICATE-----\n" + "MIICIjCCAYMCCQDaC9ARg31kIjAKBggqhkjOPQQDAjBUMQswCQYDVQQGEwJVUzEL\n" + "MAkGA1UECAwCQ0ExEjAQBgNVBAcMCVNhbiBEaWVnbzEQMA4GA1UECgwHbmFub21z\n" + "ZzESMBAGA1UEAwwJMTI3LjAuMC4xMCAXDTE3MTExNzIwMjczMloYDzIxMTcxMDI0\n" + "MjAyNzMyWjBUMQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExEjAQBgNVBAcMCVNh\n" + "biBEaWVnbzEQMA4GA1UECgwHbmFub21zZzESMBAGA1UEAwwJMTI3LjAuMC4xMIGb\n" + "MBAGByqGSM49AgEGBSuBBAAjA4GGAAQAN7vDK6GEiSguMsOuhfOvGyiVc37Sog0b\n" + "UkpaiS6+SagTmXFSN1Rgh9isxKFYJvcCtAko3v0I8rAVQucdhf5B3hEBMQlbBIuM\n" + "rMKT6ZQJ+eiwyb4O3Scgd7DoL3tc/kOqijwB/5hJ4sZdquDKP5DDFe5fAf4MNtzY\n" + "4C+iApWlKq/LoXkwCgYIKoZIzj0EAwIDgYwAMIGIAkIBOuJAWmNSdd6Ovmr6Ebg3\n" + "UF9ZrsNwARd9BfYbBk5OQhUOjCLB6d8aLi49WOm1WoRvOS5PaVvmvSfNhaw8b5nV\n" + "hnYCQgC+EmJ6C3bEcZrndhfbqvCaOGkc7/SrKhC6fS7mJW4wL90QUV9WjQ2Ll6X5\n" + "PxkSj7s0SvD6T8j7rju5LDgkdZc35A==\n" + "-----END CERTIFICATE-----\n"; + +static const char server_key[] = + "-----BEGIN EC PRIVATE KEY-----\n" + "MIHcAgEBBEIB20OHMntU2UJW2yuQn2f+bLsuhTT5KRGorcocnqxatWLvxuF1cfUA\n" + "TjQxRRS6BIUvFt1fMIklp9qedJF00JHy4qWgBwYFK4EEACOhgYkDgYYABAA3u8Mr\n" + "oYSJKC4yw66F868bKJVzftKiDRtSSlqJLr5JqBOZcVI3VGCH2KzEoVgm9wK0CSje\n" + "/QjysBVC5x2F/kHeEQExCVsEi4yswpPplAn56LDJvg7dJyB3sOgve1z+Q6qKPAH/\n" + "mEnixl2q4Mo/kMMV7l8B/gw23NjgL6IClaUqr8uheQ==\n" + "-----END EC PRIVATE KEY-----\n"; + +static int +check_props_v4(nng_msg *msg, nng_listener l, nng_dialer d) +{ + nng_pipe p; + size_t z; + p = nng_msg_get_pipe(msg); + So(p > 0); + + Convey("Local address property works", { + nng_sockaddr la; + z = sizeof(nng_sockaddr); + So(nng_pipe_getopt(p, NNG_OPT_LOCADDR, &la, &z) == 0); + So(z == sizeof(la)); + So(la.s_un.s_family == NNG_AF_INET); + So(la.s_un.s_in.sa_port == htons(trantest_port - 1)); + So(la.s_un.s_in.sa_port != 0); + So(la.s_un.s_in.sa_addr == htonl(0x7f000001)); + }); + + Convey("Remote address property works", { + nng_sockaddr ra; + z = sizeof(nng_sockaddr); + So(nng_pipe_getopt(p, NNG_OPT_REMADDR, &ra, &z) == 0); + So(z == sizeof(ra)); + So(ra.s_un.s_family == NNG_AF_INET); + So(ra.s_un.s_in.sa_port != 0); + So(ra.s_un.s_in.sa_addr == htonl(0x7f000001)); + }); + + return (0); +} + +static int +init_tls(trantest *tt) +{ + const char *own[3]; + + So(nng_setopt(tt->reqsock, NNG_OPT_TLS_CA_CERT, server_cert, + sizeof(server_cert)) == 0); + own[0] = server_cert; + own[1] = server_key; + own[2] = NULL; + So(nng_setopt(tt->repsock, NNG_OPT_TLS_CERT, server_cert, + sizeof(server_cert)) == 0); + So(nng_setopt(tt->repsock, NNG_OPT_TLS_PRIVATE_KEY, server_key, + sizeof(server_key)) == 0); + + return (0); +} + +TestMain("TLS Transport", { + + static trantest tt; + + tt.init = init_tls; + tt.tmpl = "tls://127.0.0.1:%u"; + + trantest_test(&tt); + + Convey("We can register the TLS transport", + { So(nng_tls_register() == 0); }); + + Convey("We cannot connect to wild cards", { + nng_socket s; + char addr[NNG_MAXADDRLEN]; + + So(nng_tls_register() == 0); + So(nng_pair_open(&s) == 0); + Reset({ nng_close(s); }); + trantest_next_address(addr, "tls://*:%u"); + So(nng_dial(s, addr, NULL, 0) == NNG_EADDRINVAL); + }); + + Convey("We can bind to wild card", { + nng_socket s1; + nng_socket s2; + char addr[NNG_MAXADDRLEN]; + + So(nng_tls_register() == 0); + So(nng_pair_open(&s1) == 0); + So(nng_pair_open(&s2) == 0); + Reset({ + nng_close(s2); + nng_close(s1); + }); + trantest_next_address(addr, "tls://*:%u"); + So(nng_listen(s1, addr, NULL, 0) == 0); + // reset port back one + trantest_prev_address(addr, "tls://127.0.0.1:%u"); + So(nng_dial(s2, addr, NULL, 0) == 0); + }); + + Convey("Malformed TLS addresses do not panic", { + nng_socket s1; + + So(nng_tls_register() == 0); + So(nng_pair_open(&s1) == 0); + Reset({ nng_close(s1); }); + So(nng_dial(s1, "tls://127.0.0.1", NULL, 0) == NNG_EADDRINVAL); + So(nng_dial(s1, "tls://127.0.0.1.32", NULL, 0) == + NNG_EADDRINVAL); + So(nng_dial(s1, "tls://127.0.x.1.32", NULL, 0) == + NNG_EADDRINVAL); + So(nng_listen(s1, "tls://127.0.0.1", NULL, 0) == + NNG_EADDRINVAL); + So(nng_listen(s1, "tls://127.0.0.1.32", NULL, 0) == + NNG_EADDRINVAL); + So(nng_listen(s1, "tls://127.0.x.1.32", NULL, 0) == + NNG_EADDRINVAL); + }); + + nng_fini(); +}) diff --git a/tests/trantest.h b/tests/trantest.h index d334c257..21a9c893 100644 --- a/tests/trantest.h +++ b/tests/trantest.h @@ -19,19 +19,29 @@ // Transport common tests. By making a common test framework for transports, // we can avoid rewriting the same tests for each new transport. Include this // file once in your test code. The test framework uses the REQ/REP protocol -// for comms. +// for messaging. +typedef int (*trantest_proptest_t)(nng_msg *, nng_listener, nng_dialer); -typedef struct { - char addr[NNG_MAXADDRLEN + 1]; - nng_socket reqsock; - nng_socket repsock; - nni_tran * tran; -} trantest; +typedef struct trantest trantest; + +struct trantest { + const char * tmpl; + char addr[NNG_MAXADDRLEN + 1]; + nng_socket reqsock; + nng_socket repsock; + nni_tran * tran; + nng_dialer dialer; + nng_listener listener; + int (*init)(struct trantest *); + void (*fini)(struct trantest *); + int (*dialer_init)(struct trantest *); + int (*listener_init)(struct trantest *); + int (*proptest)(nng_msg *, nng_listener, nng_dialer); + void *private; // transport specific private data +}; unsigned trantest_port = 0; -typedef int (*trantest_proptest_t)(nng_msg *, nng_listener, nng_dialer); - #ifndef NNG_HAVE_ZEROTIER #define nng_zt_register notransport #endif @@ -44,6 +54,9 @@ typedef int (*trantest_proptest_t)(nng_msg *, nng_listener, nng_dialer); #ifndef NNG_HAVE_TCP #define nng_tcp_register notransport #endif +#ifndef NNG_HAVE_TLS +#define nng_tls_register notransport +#endif int notransport(void) @@ -71,6 +84,9 @@ trantest_checktran(const char *url) #ifndef NNG_HAVE_TCP CHKTRAN(url, "tcp:"); #endif +#ifndef NNG_HAVE_TLS + CHKTRAN(url, "tls:"); +#endif (void) url; } @@ -122,6 +138,16 @@ trantest_fini(trantest *tt) nng_close(tt->repsock); } +int +trantest_dial(trantest *tt) +{ + So(nng_dialer_create(&tt->dialer, tt->reqsock, tt->addr) == 0); + if (tt->dialer_init != NULL) { + So(tt->dialer_init(tt) == 0); + } + return (nng_dialer_start(tt->dialer, 0)); +} + void trantest_scheme(trantest *tt) { @@ -150,10 +176,12 @@ trantest_duplicate_listen(trantest *tt) { Convey("Duplicate listen rejected", { nng_listener l; - So(nng_listen(tt->repsock, tt->addr, &l, 0) == 0); + int rv; + rv = nng_listen(tt->repsock, tt->addr, &l, 0); + So(rv == 0); So(l != 0); l = 0; - So(nng_listen(tt->reqsock, tt->addr, &l, 0) == NNG_EADDRINUSE); + So(nng_listen(tt->repsock, tt->addr, &l, 0) == NNG_EADDRINUSE); So(l == 0); }) } @@ -178,7 +206,6 @@ trantest_send_recv(trantest *tt) { Convey("Send and recv", { nng_listener l; - nng_dialer d; nng_msg * send; nng_msg * recv; size_t len; @@ -188,8 +215,7 @@ trantest_send_recv(trantest *tt) So(nng_listen(tt->repsock, tt->addr, &l, 0) == 0); So(l != 0); - So(nng_dial(tt->reqsock, tt->addr, &d, 0) == 0); - So(d != 0); + So(trantest_dial(tt) == 0); nng_msleep(20); // listener may be behind slightly @@ -238,7 +264,7 @@ trantest_check_properties(trantest *tt, trantest_proptest_t f) So(nng_dial(tt->reqsock, tt->addr, &d, 0) == 0); So(d != 0); - nng_msleep(20); // listener may be behind slightly + nng_msleep(10); // listener may be behind slightly send = NULL; So(nng_msg_alloc(&send, 0) == 0); @@ -280,7 +306,7 @@ trantest_send_recv_large(trantest *tt) So(nng_dial(tt->reqsock, tt->addr, &d, 0) == 0); So(d != 0); - nng_msleep(20); // listener may be behind slightly + nng_msleep(10); // listener may be behind slightly send = NULL; So(nng_msg_alloc(&send, size) == 0); @@ -315,6 +341,7 @@ trantest_test_all(const char *addr) { trantest tt; + memset(&tt, 0, sizeof(tt)); Convey("Given transport", { trantest_init(&tt, addr); @@ -334,6 +361,7 @@ trantest_test_extended(const char *addr, trantest_proptest_t f) { trantest tt; + memset(&tt, 0, sizeof(tt)); Convey("Given transport", { trantest_init(&tt, addr); @@ -348,3 +376,33 @@ trantest_test_extended(const char *addr, trantest_proptest_t f) trantest_check_properties(&tt, f); }) } + +void +trantest_test(trantest *tt) +{ + Convey("Given transport", { + trantest_init(tt, tt->tmpl); + if (tt->init != NULL) { + So(tt->init(tt) == 0); + } + + Reset({ + if (tt->fini != NULL) { + tt->fini(tt); + } + trantest_fini(tt); + }); + + trantest_scheme(tt); + + trantest_conn_refused(tt); + trantest_duplicate_listen(tt); + trantest_listen_accept(tt); + + trantest_send_recv(tt); + trantest_send_recv_large(tt); + if (tt->proptest != NULL) { + trantest_check_properties(tt, tt->proptest); + } + }) +} -- cgit v1.2.3-70-g09d2