diff options
| author | Garrett D'Amore <garrett@damore.org> | 2025-10-05 16:51:15 -0700 |
|---|---|---|
| committer | Garrett D'Amore <garrett@damore.org> | 2025-10-05 20:56:39 -0700 |
| commit | 06d6d80f8c92ef1d3bd7c00c919e10a411183cb3 (patch) | |
| tree | edf8d4cff9b2f595ccd9e3cb4db3cf31eb13bc02 | |
| parent | d1bd64c8251171ac8e1d4e71ab8726c2a64fd55a (diff) | |
| download | nng-06d6d80f8c92ef1d3bd7c00c919e10a411183cb3.tar.gz nng-06d6d80f8c92ef1d3bd7c00c919e10a411183cb3.tar.bz2 nng-06d6d80f8c92ef1d3bd7c00c919e10a411183cb3.zip | |
fixes #2173 New TLS cert API - replaces the properties for CN and ALTNAMES.
This will replace the NNG_OPT_TLS_PEER_ALTNAMES and NNG_OPT_TLS_PEER_CN
properties, and gives a bit more access to the certificate, as well as
direct access to the raw DER form, which should allow use in other APIs.
37 files changed, 1169 insertions, 189 deletions
diff --git a/docs/man/nng_tls.7.adoc b/docs/man/nng_tls.7.adoc index a6ad395c..1430e537 100644 --- a/docs/man/nng_tls.7.adoc +++ b/docs/man/nng_tls.7.adoc @@ -107,7 +107,6 @@ Note that setting these must be done before the transport is started. * xref:nng_tcp_options.5.adoc#NNG_OPT_TCP_NODELAY[`NNG_OPT_TCP_NODELAY`] * xref:nng_tls_options.5.adoc#NNG_OPT_TLS_VERIFIED[`NNG_OPT_TLS_VERIFIED_`] * xref:nng_tls_options.5.adoc#NNG_OPT_TLS_PEER_CN[`NNG_OPT_TLS_PEER_CN`] -* xref:nng_tls_options.5.adoc#NNG_OPT_TLS_PEER_ALT_NAMES[`NNG_OPT_TLS_PEER_ALT_NAMES`] * xref:nng_options.5.adoc#NNG_OPT_URL[`NNG_OPT_URL`] == SEE ALSO diff --git a/docs/man/nng_tls_options.5.adoc b/docs/man/nng_tls_options.5.adoc index 5921246f..f7cce90a 100644 --- a/docs/man/nng_tls_options.5.adoc +++ b/docs/man/nng_tls_options.5.adoc @@ -22,7 +22,6 @@ nng_tls_options - TLS-specific options #define NNG_OPT_TLS_VERIFIED "tls-verified" #define NNG_OPT_TLS_PEER_CN "tls-peer-cn" -#define NNG_OPT_TLS_PEER_ALT_NAMES "tls-peer-alt-names" ---- == DESCRIPTION @@ -66,11 +65,6 @@ May return incorrect results if peer authentication is disabled. This read-only option returns the common name of the peer certificate. May return incorrect results if peer authentication is disabled. -[[NNG_OPT_TLS_PEER_ALT_NAMES]]((`NNG_OPT_TLS_PEER_ALT_NAMES`)):: -(string) -This read-only option returns string list with the subject alternative names of the -peer certificate. May return incorrect results if peer authentication is disabled. - === Inherited Options Generally, the following option values are also available for TLS objects, diff --git a/docs/man/nng_ws.7.adoc b/docs/man/nng_ws.7.adoc index 49783920..f7034987 100644 --- a/docs/man/nng_ws.7.adoc +++ b/docs/man/nng_ws.7.adoc @@ -159,11 +159,6 @@ May return incorrect results if peer authentication is disabled. (string) This read-only option returns the common name of the peer certificate. May return incorrect results if peer authentication is disabled. -`NNG_OPT_TLS_PEER_ALT_NAMES`:: -(string list) returns string list with the subject alternative names of the -peer certificate. May return incorrect results if peer authentication -is disabled. - // We should also look at a hook mechanism for listeners. Probably this could // look like NNG_OPT_WS_LISTEN_HOOK_FUNC which would take a function pointer // along the lines of int hook(void *, char *req_headers, char **res_headers), diff --git a/docs/ref/api/http.md b/docs/ref/api/http.md index 04c3ee4a..bbe33c24 100644 --- a/docs/ref/api/http.md +++ b/docs/ref/api/http.md @@ -349,6 +349,26 @@ This function is most useful when called from a handler function. > This function is intended to facilitate uses cases that involve changing the protocol from HTTP, such as WebSocket. > Most applications will never need to use this function. +### Obtaining TLS Connection Details + +```c +nng_err nng_http_peer_cert(nng_http_conn *conn, nng_tls_cert **certp); +``` + +TODO: We need to document the cert API. + +The {{i:`nng_http_peer_cert`}} function will obtain the TLS certificate object for the peer, if one is available. +This can then be used for additional authentication or identity specific logic. + +The certificate must be released with [`nng_tls_cert_free`] when no longer in use. +See [`nng_tls_cert`] for more information about working with TLS certificates. + +> [!NOTE] +> While it should be obvious that this function is only available when using HTTPS, +> it also requires that peer authentication is in use, and may require that the underlying +> TLS engine support peer certificate colleciton. (Some minimal configurations elide this +> to save space in embedded environments.) + ## Client API The NNG client API consists of an API for creating connections, and an API for performing diff --git a/docs/ref/migrate/nng1.md b/docs/ref/migrate/nng1.md index 5b85c41d..df062efd 100644 --- a/docs/ref/migrate/nng1.md +++ b/docs/ref/migrate/nng1.md @@ -156,6 +156,15 @@ The ability to configure multiple keys and certificates for a given TLS configur The intended purpose was to support alternative cryptographic algorithms, but this is not necessary, was never used, and was error prone. +## TLS Peer Certificate APIs Replaced + +The `NNG_OPT_TLS_PEER_CN` and `NNG_OPT_TLS_PEER_ALT_NAMES` properties have been removed. +They are replaced with functions like [`nng_pipe_peer_cert`], [`nng_stream_peer_cert`], +and [`nng_http_peer_cert`] which return a new `nng_tls_cert` object. + +This object supports methods to get additional information about the certificate, as well +as to obtain the raw DER content so that it can be imported for use in other APIs. + ## Support for Local Addresses in Dial URLs Removed NNG 1.x had an undocumented ability to specify the local address to bind diff --git a/include/nng/http.h b/include/nng/http.h index 08099cbe..f8bfb679 100644 --- a/include/nng/http.h +++ b/include/nng/http.h @@ -207,6 +207,10 @@ NNG_DECL void nng_http_set_body(nng_http *, void *, size_t); // makes a local copy. It can fail due to NNG_ENOMEM. NNG_DECL nng_err nng_http_copy_body(nng_http *, const void *, size_t); +// nng_http_peer_cert returns the HTTP peer cert, if the one is available. +// Only available for HTTPS connections. +NNG_DECL nng_err nng_http_peer_cert(nng_http *, nng_tls_cert **); + // nng_http_handler is a handler used on the server side to handle HTTP // requests coming into a specific URL. // diff --git a/include/nng/nng.h b/include/nng/nng.h index fb61b0de..69c5ee89 100644 --- a/include/nng/nng.h +++ b/include/nng/nng.h @@ -24,6 +24,7 @@ extern "C" { #include <stdbool.h> #include <stddef.h> #include <stdint.h> +#include <time.h> // NNG_DECL is used on declarations to deal with scope. // For building Windows DLLs, it should be the appropriate __declspec(). @@ -176,6 +177,10 @@ typedef struct nng_url nng_url; // and so forth. A TLS configuration cannot be changed once it is in use. typedef struct nng_tls_config nng_tls_config; +// This is a representation of X.509 certificate as used in TLS transports. +// Internal details are opaque. +typedef struct nng_tls_cert_s nng_tls_cert; + // Initializers. // clang-format off #define NNG_PIPE_INITIALIZER { 0 } @@ -767,6 +772,7 @@ NNG_DECL nng_err nng_pipe_get_ms(nng_pipe, const char *, nng_duration *); NNG_DECL nng_err nng_pipe_get_size(nng_pipe, const char *, size_t *); NNG_DECL nng_err nng_pipe_get_string(nng_pipe, const char *, char **); NNG_DECL nng_err nng_pipe_get_addr(nng_pipe, const char *, nng_sockaddr *); +NNG_DECL nng_err nng_pipe_peer_cert(nng_pipe, nng_tls_cert **); NNG_DECL nng_err nng_pipe_close(nng_pipe); NNG_DECL int nng_pipe_id(nng_pipe); @@ -803,13 +809,6 @@ NNG_DECL nng_listener nng_pipe_listener(nng_pipe); // peer authentication is disabled with `NNG_TLS_AUTH_MODE_NONE`. #define NNG_OPT_TLS_PEER_CN "tls-peer-cn" -// NNG_OPT_TLS_PEER_ALT_NAMES returns string list with the -// subject alternative names of the peer certificate. Typically this is -// read-only and only available for pipes. This option may return -// incorrect results if peer authentication is disabled with -// `NNG_TLS_AUTH_MODE_NONE`. -#define NNG_OPT_TLS_PEER_ALT_NAMES "tls-peer-alt-names" - // TCP options. These may be supported on various transports that use // TCP underneath such as TLS, or not. @@ -1145,6 +1144,7 @@ NNG_DECL nng_err nng_stream_get_uint64(nng_stream *, const char *, uint64_t *); NNG_DECL nng_err nng_stream_get_string(nng_stream *, const char *, char **); NNG_DECL nng_err nng_stream_get_addr( nng_stream *, const char *, nng_sockaddr *); +NNG_DECL nng_err nng_stream_peer_cert(nng_stream *, nng_tls_cert **); NNG_DECL nng_err nng_stream_dialer_alloc(nng_stream_dialer **, const char *); NNG_DECL nng_err nng_stream_dialer_alloc_url( @@ -1624,6 +1624,30 @@ NNG_DECL const char *nng_tls_engine_description(void); // nng_tls_engine_fips_mode returns true if the engine is in FIPS 140 mode. NNG_DECL bool nng_tls_engine_fips_mode(void); +// nng_tls_cert_parse parses PEM content to obtain an object suitable for +// use with TLS APIs. +NNG_DECL nng_err nng_tls_cert_parse_pem(nng_tls_cert **, const char *, size_t); + +// nng_tls_cert_parse_der parses a DER (distinguished encoding rules) format +// certificate. +NNG_DECL nng_err nng_tls_cert_parse_der( + nng_tls_cert **, const uint8_t *, size_t); + +// nng_tls_cert_der extracts the certificate as DER content. This can be +// useful for importing into other APIs such as OpenSSL or mbedTLS directly. +NNG_DECL void nng_tls_cert_der(nng_tls_cert *cert, uint8_t *, size_t *); + +// nng_tls_cert_free releases the certificate from memory. +NNG_DECL void nng_tls_cert_free(nng_tls_cert *); + +NNG_DECL nng_err nng_tls_cert_subject(nng_tls_cert *, char **); +NNG_DECL nng_err nng_tls_cert_issuer(nng_tls_cert *, char **); +NNG_DECL nng_err nng_tls_cert_serial_number(nng_tls_cert *, char **); +NNG_DECL nng_err nng_tls_cert_subject_cn(nng_tls_cert *, char **); +NNG_DECL nng_err nng_tls_cert_next_alt(nng_tls_cert *, char **); +NNG_DECL nng_err nng_tls_cert_not_before(nng_tls_cert *, struct tm *); +NNG_DECL nng_err nng_tls_cert_not_after(nng_tls_cert *, struct tm *); + // Public ID map support. typedef struct nng_id_map_s nng_id_map; diff --git a/src/core/defs.h b/src/core/defs.h index 64a43a04..23c17d8b 100644 --- a/src/core/defs.h +++ b/src/core/defs.h @@ -14,7 +14,7 @@ #include <stdbool.h> #include <stdint.h> -#include <nng/nng.h> +#include "nng/nng.h" // C compilers may get unhappy when named arguments are not used. While // there are things like __attribute__((unused)) which are arguably diff --git a/src/core/pipe.c b/src/core/pipe.c index bfc272b3..db2c4d41 100644 --- a/src/core/pipe.c +++ b/src/core/pipe.c @@ -430,3 +430,12 @@ nni_pipe_peer_addr(nni_pipe *p, char buf[NNG_MAXADDRSTRLEN]) nng_str_sockaddr(&sa, buf, NNG_MAXADDRSTRLEN); return (buf); } + +nng_err +nni_pipe_peer_cert(nni_pipe *p, nng_tls_cert **certp) +{ + if (p->p_tran_ops.p_peer_cert == NULL) { + return (NNG_ENOTSUP); + } + return (p->p_tran_ops.p_peer_cert(p->p_tran_data, certp)); +} diff --git a/src/core/pipe.h b/src/core/pipe.h index b9a13a68..4ed61660 100644 --- a/src/core/pipe.h +++ b/src/core/pipe.h @@ -39,6 +39,9 @@ extern uint16_t nni_pipe_peer(nni_pipe *); extern nng_err nni_pipe_getopt( nni_pipe *, const char *, void *, size_t *, nni_opt_type); +// nni_pipe_peer_cert obtains the peer TLS certificate, if available. +extern nng_err nni_pipe_peer_cert(nni_pipe *, nng_tls_cert **); + // nni_pipe_find finds a pipe given its ID. It places a hold on the // pipe, which must be released by the caller when it is done. extern nng_err nni_pipe_find(nni_pipe **, uint32_t); diff --git a/src/core/sockfd.h b/src/core/sockfd.h index 8985c009..3b39ee60 100644 --- a/src/core/sockfd.h +++ b/src/core/sockfd.h @@ -10,7 +10,7 @@ #ifndef CORE_FDC_H #define CORE_FDC_H -#include "core/nng_impl.h" +#include "nng/nng.h" // the nni_sfd_conn struct is provided by platform code to wrap // an arbitrary byte stream file descriptor (UNIX) or handle (Windows) diff --git a/src/core/stream.c b/src/core/stream.c index e0da3582..61a8a3ba 100644 --- a/src/core/stream.c +++ b/src/core/stream.c @@ -12,12 +12,13 @@ #include <string.h> -#include "core/nng_impl.h" +#include "nng_impl.h" -#include "core/sockfd.h" -#include "core/tcp.h" -#include "supplemental/tls/tls_api.h" -#include "supplemental/websocket/websocket.h" +#include "sockfd.h" +#include "tcp.h" + +#include "../supplemental/tls/tls_api.h" +#include "../supplemental/websocket/websocket.h" static struct { const char *scheme; @@ -385,6 +386,15 @@ nng_stream_get_addr(nng_stream *s, const char *n, nng_sockaddr *v) } nng_err +nng_stream_peer_cert(nng_stream *s, nng_tls_cert **certp) +{ + if (s->s_peer_cert == NULL) { + return (NNG_ENOTSUP); + } + return (s->s_peer_cert(s, certp)); +} + +nng_err nng_stream_dialer_get_int(nng_stream_dialer *d, const char *n, int *v) { return (nni_stream_dialer_get(d, n, v, NULL, NNI_TYPE_INT32)); diff --git a/src/core/stream.h b/src/core/stream.h index 83c121cd..a9a17ec1 100644 --- a/src/core/stream.h +++ b/src/core/stream.h @@ -50,6 +50,7 @@ struct nng_stream { void (*s_send)(void *, nng_aio *); nng_err (*s_get)(void *, const char *, void *, size_t *, nni_type); nng_err (*s_set)(void *, const char *, const void *, size_t, nni_type); + nng_err (*s_peer_cert)(void *, nng_tls_cert **); }; // Dialer implementation. Stream dialers create streams. diff --git a/src/core/tcp.h b/src/core/tcp.h index cc41dfac..58cac45a 100644 --- a/src/core/tcp.h +++ b/src/core/tcp.h @@ -10,7 +10,7 @@ #ifndef CORE_TCP_H #define CORE_TCP_H -#include "core/nng_impl.h" +#include "nng/nng.h" // These are interfaces we use for TCP internally. These are not exposed // to the public API. @@ -670,7 +670,7 @@ void nng_dialer_start_aio(nng_dialer did, int flags, nng_aio *aio) { nni_dialer *d; - int rv; + int rv; if (aio != NULL) { nni_aio_reset(aio); @@ -1375,6 +1375,20 @@ nng_pipe_get_addr(nng_pipe id, const char *n, nng_sockaddr *v) return (pipe_get(id, n, v, NULL, NNI_TYPE_SOCKADDR)); } +nng_err +nng_pipe_peer_cert(nng_pipe p, nng_tls_cert **certp) +{ + nng_err rv; + nni_pipe *pipe; + + if ((rv = nni_pipe_find(&pipe, p.id)) != 0) { + return (rv); + } + rv = nni_pipe_peer_cert(pipe, certp); + nni_pipe_rele(pipe); + return (rv); +} + nng_socket nng_pipe_socket(nng_pipe p) { diff --git a/src/sp/transport.h b/src/sp/transport.h index 36ade021..11a9ac01 100644 --- a/src/sp/transport.h +++ b/src/sp/transport.h @@ -188,6 +188,10 @@ struct nni_sp_pipe_ops { // p_getopt is used to obtain an option. Pipes don't implement // option setting. nng_err (*p_getopt)(void *, const char *, void *, size_t *, nni_type); + + // p_peer_cert is used to obtain a peer cert for transports that + // implement TLS. + nng_err (*p_peer_cert)(void *, nng_tls_cert **); }; // Transport implementation details. Transports must implement the diff --git a/src/sp/transport/dtls/dtls.c b/src/sp/transport/dtls/dtls.c index e95560de..400709a1 100644 --- a/src/sp/transport/dtls/dtls.c +++ b/src/sp/transport/dtls/dtls.c @@ -6,18 +6,18 @@ // found online at https://opensource.org/licenses/MIT. // -#include "core/aio.h" -#include "core/defs.h" -#include "core/idhash.h" -#include "core/message.h" -#include "core/nng_impl.h" -#include "core/options.h" -#include "core/pipe.h" -#include "core/platform.h" -#include "core/socket.h" -#include "core/stats.h" +#include "../../../core/aio.h" +#include "../../../core/defs.h" +#include "../../../core/idhash.h" +#include "../../../core/message.h" +#include "../../../core/nng_impl.h" +#include "../../../core/options.h" +#include "../../../core/pipe.h" +#include "../../../core/platform.h" +#include "../../../core/socket.h" +#include "../../../core/stats.h" +#include "../../../supplemental/tls/tls_common.h" #include "nng/nng.h" -#include "supplemental/tls/tls_common.h" #include <string.h> @@ -1070,6 +1070,14 @@ dtls_pipe_getopt( return (nni_getopt(dtls_pipe_options, name, p, buf, szp, t)); } +static nng_err +dtls_pipe_peer_cert(void *arg, nng_tls_cert **certp) +{ + dtls_pipe *p = arg; + + return (nni_tls_peer_cert(&p->tls, certp)); +} + static void dtls_ep_fini(void *arg) { @@ -1676,15 +1684,16 @@ dtls_ep_accept(void *arg, nni_aio *aio) } static nni_sp_pipe_ops dtls_pipe_ops = { - .p_size = dtls_pipe_size, - .p_init = dtls_pipe_init, - .p_fini = dtls_pipe_fini, - .p_stop = dtls_pipe_stop, - .p_send = dtls_pipe_send, - .p_recv = dtls_pipe_recv, - .p_close = dtls_pipe_close, - .p_peer = dtls_pipe_peer, - .p_getopt = dtls_pipe_getopt, + .p_size = dtls_pipe_size, + .p_init = dtls_pipe_init, + .p_fini = dtls_pipe_fini, + .p_stop = dtls_pipe_stop, + .p_send = dtls_pipe_send, + .p_recv = dtls_pipe_recv, + .p_close = dtls_pipe_close, + .p_peer = dtls_pipe_peer, + .p_getopt = dtls_pipe_getopt, + .p_peer_cert = dtls_pipe_peer_cert, }; static const nni_option dtls_ep_opts[] = { diff --git a/src/sp/transport/dtls/dtls_tran_test.c b/src/sp/transport/dtls/dtls_tran_test.c index 774c967a..7e604f30 100644 --- a/src/sp/transport/dtls/dtls_tran_test.c +++ b/src/sp/transport/dtls/dtls_tran_test.c @@ -513,6 +513,86 @@ test_dtls_psk(void) #endif } +void +test_dtls_pipe_details(void) +{ + nng_socket s1; + nng_socket s2; + nng_tls_config *c1, *c2; + nng_sockaddr sa; + nng_listener l; + nng_dialer d; + nng_msg *msg; + nng_pipe p; + const nng_url *url; + + c1 = tls_server_config_ecdsa(); + c2 = tls_client_config_ecdsa(); + + NUTS_ENABLE_LOG(NNG_LOG_DEBUG); + NUTS_OPEN(s1); + NUTS_OPEN(s2); + NUTS_PASS(nng_tls_config_auth_mode(c1, NNG_TLS_AUTH_MODE_REQUIRED)); + NUTS_PASS(nng_tls_config_ca_chain(c1, nuts_ecdsa_server_crt, NULL)); + NUTS_PASS(nng_tls_config_ca_chain(c2, nuts_ecdsa_server_crt, NULL)); + NUTS_PASS(nng_listener_create(&l, s1, "dtls://127.0.0.1:0")); + NUTS_PASS(nng_listener_set_tls(l, c1)); + NUTS_PASS(nng_listener_start(l, 0)); + NUTS_PASS(nng_listener_get_url(l, &url)); + NUTS_MATCH(nng_url_scheme(url), "dtls"); + NUTS_PASS(nng_listener_get_addr(l, NNG_OPT_LOCADDR, &sa)); + NUTS_TRUE(sa.s_in.sa_family == NNG_AF_INET); + NUTS_TRUE(sa.s_in.sa_port != 0); + NUTS_TRUE(sa.s_in.sa_addr = nuts_be32(0x7f000001)); + NUTS_PASS(nng_dialer_create_url(&d, s2, url)); + NUTS_PASS(nng_dialer_set_tls(d, c2)); + NUTS_PASS(nng_dialer_start(d, 0)); + nng_msleep(50); + NUTS_SEND(s1, "text"); + NUTS_PASS(nng_recvmsg(s2, &msg, 0)); + p = nng_msg_get_pipe(msg); + NUTS_TRUE(nng_pipe_id(p) >= 0); +#if !defined(NNG_TLS_ENGINE_WOLFSSL) || defined(NNG_WOLFSSL_HAVE_PEER_CERT) + // TOOD: maybe implement this -- although I think we want to move away + // from it. + // + // char *cn; NUTS_PASS(nng_pipe_get_string(p, + // NNG_OPT_TLS_PEER_CN, &cn)); NUTS_ASSERT(cn != NULL); NUTS_MATCH(cn, + // "127.0.0.1"); nng_strfree(cn); + + nng_tls_cert *cert; + char *name; + NUTS_PASS(nng_pipe_peer_cert(p, &cert)); + NUTS_PASS(nng_tls_cert_subject(cert, &name)); + NUTS_ASSERT(name != NULL); + nng_log_debug(NULL, "SUBJECT: %s", name); + NUTS_PASS(nng_tls_cert_issuer(cert, &name)); + NUTS_ASSERT(name != NULL); + nng_log_debug(NULL, "ISSUER: %s", name); + NUTS_PASS(nng_tls_cert_serial_number(cert, &name)); + NUTS_ASSERT(name != NULL); + nng_log_debug(NULL, "SERIAL: %s", name); + NUTS_PASS(nng_tls_cert_subject_cn(cert, &name)); + NUTS_MATCH(name, "127.0.0.1"); + NUTS_PASS(nng_tls_cert_next_alt(cert, &name)); + nng_log_debug(NULL, "FIRST ALT: %s", name); + NUTS_MATCH(name, "localhost"); + NUTS_FAIL(nng_tls_cert_next_alt(cert, &name), NNG_ENOENT); + struct tm when; + NUTS_PASS(nng_tls_cert_not_before(cert, &when)); + nng_log_debug(NULL, "BEGINS: %s", asctime(&when)); + NUTS_PASS(nng_tls_cert_not_after(cert, &when)); + nng_log_debug(NULL, "EXPIRES: %s", asctime(&when)); + + nng_tls_cert_free(cert); +#endif + nng_msg_free(msg); + NUTS_CLOSE(s2); + NUTS_CLOSE(s1); + nng_tls_config_free(c1); + nng_tls_config_free(c2); +} + NUTS_TESTS = { { "dtls port zero bind", test_dtls_port_zero_bind }, @@ -525,5 +605,6 @@ NUTS_TESTS = { { "dtls pre-shared key", test_dtls_psk }, { "dtls bad cert mutual", test_dtls_bad_cert_mutual }, { "dtls cert mutual", test_dtls_cert_mutual }, + { "dtls pipe details", test_dtls_pipe_details }, { NULL, NULL }, }; diff --git a/src/sp/transport/tcp/tcp.c b/src/sp/transport/tcp/tcp.c index 096d2e24..c2e9093b 100644 --- a/src/sp/transport/tcp/tcp.c +++ b/src/sp/transport/tcp/tcp.c @@ -12,7 +12,7 @@ #include <stdlib.h> #include <string.h> -#include "core/nng_impl.h" +#include "../../../core/nng_impl.h" #include "nng/nng.h" // TCP transport. Platform specific TCP operations must be diff --git a/src/sp/transport/tcp/tcp_test.c b/src/sp/transport/tcp/tcp_test.c index a7ff5e96..31379db6 100644 --- a/src/sp/transport/tcp/tcp_test.c +++ b/src/sp/transport/tcp/tcp_test.c @@ -11,7 +11,8 @@ // #include "nng/nng.h" -#include <nuts.h> + +#include "../../../testing/nuts.h" // TCP tests. @@ -231,6 +232,9 @@ check_props_v4(nng_msg *msg) NUTS_PASS(nng_pipe_get_bool(p, NNG_OPT_TCP_NODELAY, &b)); NUTS_TRUE(b); // default + + nng_tls_cert *cert; + NUTS_FAIL(nng_pipe_peer_cert(p, &cert), NNG_ENOTSUP); } void diff --git a/src/sp/transport/tls/tls.c b/src/sp/transport/tls/tls.c index 0bd4c284..528b05ec 100644 --- a/src/sp/transport/tls/tls.c +++ b/src/sp/transport/tls/tls.c @@ -952,6 +952,14 @@ tlstran_pipe_getopt( return (rv); } +static nng_err +tlstran_pipe_peer_cert(void *arg, nng_tls_cert **certp) +{ + tlstran_pipe *p = arg; + + return (nng_stream_peer_cert(p->tls, certp)); +} + static size_t tlstran_pipe_size(void) { @@ -959,15 +967,16 @@ tlstran_pipe_size(void) } static nni_sp_pipe_ops tlstran_pipe_ops = { - .p_size = tlstran_pipe_size, - .p_init = tlstran_pipe_init, - .p_fini = tlstran_pipe_fini, - .p_stop = tlstran_pipe_stop, - .p_send = tlstran_pipe_send, - .p_recv = tlstran_pipe_recv, - .p_close = tlstran_pipe_close, - .p_peer = tlstran_pipe_peer, - .p_getopt = tlstran_pipe_getopt, + .p_size = tlstran_pipe_size, + .p_init = tlstran_pipe_init, + .p_fini = tlstran_pipe_fini, + .p_stop = tlstran_pipe_stop, + .p_send = tlstran_pipe_send, + .p_recv = tlstran_pipe_recv, + .p_close = tlstran_pipe_close, + .p_peer = tlstran_pipe_peer, + .p_getopt = tlstran_pipe_getopt, + .p_peer_cert = tlstran_pipe_peer_cert, }; static nni_option tlstran_ep_options[] = { diff --git a/src/sp/transport/tls/tls_tran_test.c b/src/sp/transport/tls/tls_tran_test.c index 73c299c8..1b69c65f 100644 --- a/src/sp/transport/tls/tls_tran_test.c +++ b/src/sp/transport/tls/tls_tran_test.c @@ -227,11 +227,31 @@ test_tls_pipe_details(void) p = nng_msg_get_pipe(msg); NUTS_TRUE(nng_pipe_id(p) >= 0); #if !defined(NNG_TLS_ENGINE_WOLFSSL) || defined(NNG_WOLFSSL_HAVE_PEER_CERT) - char *cn; - NUTS_PASS(nng_pipe_get_string(p, NNG_OPT_TLS_PEER_CN, &cn)); - NUTS_ASSERT(cn != NULL); - NUTS_MATCH(cn, "127.0.0.1"); - nng_strfree(cn); + nng_tls_cert *cert; + char *name; + NUTS_PASS(nng_pipe_peer_cert(p, &cert)); + NUTS_PASS(nng_tls_cert_subject(cert, &name)); + NUTS_ASSERT(name != NULL); + nng_log_debug(NULL, "SUBJECT: %s", name); + NUTS_PASS(nng_tls_cert_issuer(cert, &name)); + NUTS_ASSERT(name != NULL); + nng_log_debug(NULL, "ISSUER: %s", name); + NUTS_PASS(nng_tls_cert_serial_number(cert, &name)); + NUTS_ASSERT(name != NULL); + nng_log_debug(NULL, "SERIAL: %s", name); + NUTS_PASS(nng_tls_cert_subject_cn(cert, &name)); + NUTS_MATCH(name, "127.0.0.1"); + NUTS_PASS(nng_tls_cert_next_alt(cert, &name)); + nng_log_debug(NULL, "FIRST ALT: %s", name); + NUTS_MATCH(name, "localhost"); + NUTS_FAIL(nng_tls_cert_next_alt(cert, &name), NNG_ENOENT); + struct tm when; + NUTS_PASS(nng_tls_cert_not_before(cert, &when)); + nng_log_debug(NULL, "BEGINS: %s", asctime(&when)); + NUTS_PASS(nng_tls_cert_not_after(cert, &when)); + nng_log_debug(NULL, "EXPIRES: %s", asctime(&when)); + + nng_tls_cert_free(cert); #endif nng_msg_free(msg); NUTS_CLOSE(s2); diff --git a/src/sp/transport/ws/websocket.c b/src/sp/transport/ws/websocket.c index 515f7b65..f9c13afe 100644 --- a/src/sp/transport/ws/websocket.c +++ b/src/sp/transport/ws/websocket.c @@ -13,8 +13,9 @@ #include <stdio.h> #include <string.h> -#include "core/nng_impl.h" -#include "supplemental/websocket/websocket.h" +#include "../../../core/nng_impl.h" +#include "../../../supplemental/websocket/websocket.h" +#include "nng/nng.h" typedef struct ws_dialer ws_dialer; typedef struct ws_listener ws_listener; @@ -328,6 +329,14 @@ wstran_pipe_getopt( return (rv); } +static nng_err +wstran_pipe_peer_cert(void *arg, nng_tls_cert **certp) +{ + ws_pipe *p = arg; + + return (nng_stream_peer_cert(p->ws, certp)); +} + static size_t wstran_pipe_size(void) { @@ -335,15 +344,16 @@ wstran_pipe_size(void) } static nni_sp_pipe_ops ws_pipe_ops = { - .p_size = wstran_pipe_size, - .p_init = wstran_pipe_init, - .p_fini = wstran_pipe_fini, - .p_stop = wstran_pipe_stop, - .p_send = wstran_pipe_send, - .p_recv = wstran_pipe_recv, - .p_close = wstran_pipe_close, - .p_peer = wstran_pipe_peer, - .p_getopt = wstran_pipe_getopt, + .p_size = wstran_pipe_size, + .p_init = wstran_pipe_init, + .p_fini = wstran_pipe_fini, + .p_stop = wstran_pipe_stop, + .p_send = wstran_pipe_send, + .p_recv = wstran_pipe_recv, + .p_close = wstran_pipe_close, + .p_peer = wstran_pipe_peer, + .p_getopt = wstran_pipe_getopt, + .p_peer_cert = wstran_pipe_peer_cert, }; static void diff --git a/src/sp/transport/ws/wss_test.c b/src/sp/transport/ws/wss_test.c index 7c8ceeec..1686090b 100644 --- a/src/sp/transport/ws/wss_test.c +++ b/src/sp/transport/ws/wss_test.c @@ -14,7 +14,7 @@ #include <nng/nng.h> -#include <nuts.h> +#include "../../../testing/nuts.h" static nng_tls_config * wss_server_config(void) @@ -381,6 +381,79 @@ test_wss_psk(void) #endif } +void +test_wss_pipe_details(void) +{ + nng_socket s1; + nng_socket s2; + nng_tls_config *c1, *c2; + nng_sockaddr sa; + nng_listener l; + nng_dialer d; + nng_msg *msg; + nng_pipe p; + const nng_url *url; + + c1 = wss_server_config_ecdsa(); + c2 = wss_client_config_ecdsa(); + + NUTS_ENABLE_LOG(NNG_LOG_DEBUG); + NUTS_OPEN(s1); + NUTS_OPEN(s2); + NUTS_PASS(nng_tls_config_auth_mode(c1, NNG_TLS_AUTH_MODE_REQUIRED)); + NUTS_PASS(nng_tls_config_ca_chain(c1, nuts_ecdsa_server_crt, NULL)); + NUTS_PASS(nng_tls_config_ca_chain(c2, nuts_ecdsa_server_crt, NULL)); + NUTS_PASS(nng_listener_create(&l, s1, "wss://127.0.0.1:0/test")); + NUTS_PASS(nng_listener_set_tls(l, c1)); + NUTS_PASS(nng_listener_start(l, 0)); + NUTS_PASS(nng_listener_get_url(l, &url)); + NUTS_MATCH(nng_url_scheme(url), "wss"); + NUTS_PASS(nng_listener_get_addr(l, NNG_OPT_LOCADDR, &sa)); + NUTS_TRUE(sa.s_in.sa_family == NNG_AF_INET); + NUTS_TRUE(sa.s_in.sa_port != 0); + NUTS_TRUE(sa.s_in.sa_addr = nuts_be32(0x7f000001)); + NUTS_PASS(nng_dialer_create_url(&d, s2, url)); + NUTS_PASS(nng_dialer_set_tls(d, c2)); + NUTS_PASS(nng_dialer_start(d, 0)); + nng_msleep(50); + NUTS_SEND(s1, "text"); + NUTS_PASS(nng_recvmsg(s2, &msg, 0)); + p = nng_msg_get_pipe(msg); + NUTS_TRUE(nng_pipe_id(p) >= 0); +#if !defined(NNG_TLS_ENGINE_WOLFSSL) || defined(NNG_WOLFSSL_HAVE_PEER_CERT) + nng_tls_cert *cert; + char *name; + NUTS_PASS(nng_pipe_peer_cert(p, &cert)); + NUTS_PASS(nng_tls_cert_subject(cert, &name)); + NUTS_ASSERT(name != NULL); + nng_log_debug(NULL, "SUBJECT: %s", name); + NUTS_PASS(nng_tls_cert_issuer(cert, &name)); + NUTS_ASSERT(name != NULL); + nng_log_debug(NULL, "ISSUER: %s", name); + NUTS_PASS(nng_tls_cert_serial_number(cert, &name)); + NUTS_ASSERT(name != NULL); + nng_log_debug(NULL, "SERIAL: %s", name); + NUTS_PASS(nng_tls_cert_subject_cn(cert, &name)); + NUTS_MATCH(name, "127.0.0.1"); + NUTS_PASS(nng_tls_cert_next_alt(cert, &name)); + nng_log_debug(NULL, "FIRST ALT: %s", name); + NUTS_MATCH(name, "localhost"); + NUTS_FAIL(nng_tls_cert_next_alt(cert, &name), NNG_ENOENT); + struct tm when; + NUTS_PASS(nng_tls_cert_not_before(cert, &when)); + nng_log_debug(NULL, "BEGINS: %s", asctime(&when)); + NUTS_PASS(nng_tls_cert_not_after(cert, &when)); + nng_log_debug(NULL, "EXPIRES: %s", asctime(&when)); + + nng_tls_cert_free(cert); +#endif + nng_msg_free(msg); + NUTS_CLOSE(s2); + NUTS_CLOSE(s1); + nng_tls_config_free(c1); + nng_tls_config_free(c2); +} + NUTS_TESTS = { { "wss port zero bind", test_wss_port_zero_bind }, { "wss malformed address", test_wss_malformed_address }, @@ -390,6 +463,7 @@ NUTS_TESTS = { { "wss pre-shared key", test_wss_psk }, { "wss bad cert mutual", test_wss_bad_cert_mutual }, { "wss cert mutual", test_wss_cert_mutual }, + { "wss pipe details", test_wss_pipe_details }, { NULL, NULL }, }; diff --git a/src/supplemental/http/http_api.h b/src/supplemental/http/http_api.h index a8f648dc..b1a8ec84 100644 --- a/src/supplemental/http/http_api.h +++ b/src/supplemental/http/http_api.h @@ -99,6 +99,7 @@ extern void nni_http_conn_close(nng_http *); extern void nni_http_conn_fini(nni_http_conn *); extern int nni_http_conn_getopt( nng_http *, const char *, void *, size_t *, nni_type); +extern nng_err nni_http_conn_peer_cert(nng_http *, nng_tls_cert **); // Reading messages -- the caller must supply a preinitialized (but otherwise // idle) message. We recommend the caller store this in the aio's user data. diff --git a/src/supplemental/http/http_conn.c b/src/supplemental/http/http_conn.c index 21087474..93068512 100644 --- a/src/supplemental/http/http_conn.c +++ b/src/supplemental/http/http_conn.c @@ -1482,6 +1482,20 @@ nni_http_conn_getopt( return (rv); } +nng_err +nni_http_conn_peer_cert(nni_http_conn *conn, nng_tls_cert **certp) +{ + int rv; + nni_mtx_lock(&conn->mtx); + if (conn->closed) { + rv = NNG_ECLOSED; + } else { + rv = nng_stream_peer_cert(conn->sock, certp); + } + nni_mtx_unlock(&conn->mtx); + return (rv); +} + void nni_http_conn_fini(nni_http_conn *conn) { diff --git a/src/supplemental/http/http_public.c b/src/supplemental/http/http_public.c index 9c8ded2d..5c7d8a77 100644 --- a/src/supplemental/http/http_public.c +++ b/src/supplemental/http/http_public.c @@ -651,3 +651,15 @@ nng_http_reset(nng_http *conn) NNI_ARG_UNUSED(conn); #endif } + +nng_err +nng_http_peer_cert(nng_http *conn, nng_tls_cert **certp) +{ +#ifdef NNG_SUPP_HTTP + return (nni_http_conn_peer_cert(conn, certp)); +#else + NNI_ARG_UNUSED(conn); + NNI_ARG_UNUSED(certp); + return (NNG_ENOTSUP); +#endif +} diff --git a/src/supplemental/tls/mbedtls/mbedtls.c b/src/supplemental/tls/mbedtls/mbedtls.c index bbfc5763..87a8bd96 100644 --- a/src/supplemental/tls/mbedtls/mbedtls.c +++ b/src/supplemental/tls/mbedtls/mbedtls.c @@ -76,6 +76,16 @@ psk_free(psk *p) } } +struct nng_tls_engine_cert { + mbedtls_x509_crt crt; + char subject[1024]; + char issuer[1024]; + char last_alt[256]; + char serial[64]; // 20 bytes per RFC, x 3 for display + char cn[65]; // 64 + null + int next_alt; +}; + struct nng_tls_engine_conn { void *tls; // parent conn mbedtls_ssl_context ctx; @@ -370,11 +380,32 @@ conn_verified(nng_tls_engine_conn *ec) return (mbedtls_ssl_get_verify_result(&ec->ctx) == 0); } +static nng_err +conn_peer_cert(nng_tls_engine_conn *ec, nng_tls_engine_cert **certp) +{ + nng_tls_engine_cert *cert; + + const mbedtls_x509_crt *crt = mbedtls_ssl_get_peer_cert(&ec->ctx); + + if (crt == NULL) { + return (NNG_ENOENT); + } + if ((cert = nni_zalloc(sizeof(*cert))) == NULL) { + return (NNG_ENOMEM); + } + // In order to ensure that we get our *own* copy that might outlive the + // cert buffer from the connection, we do a parse. + mbedtls_x509_crt_parse(&cert->crt, crt->raw.p, crt->raw.len); + + *certp = cert; + return (NNG_OK); +} + static char * conn_peer_cn(nng_tls_engine_conn *ec) { const mbedtls_x509_crt *crt = mbedtls_ssl_get_peer_cert(&ec->ctx); - if (!crt) { + if (crt == NULL) { return (NULL); } @@ -400,47 +431,6 @@ conn_peer_cn(nng_tls_engine_conn *ec) return (rv); } -static char ** -conn_peer_alt_names(nng_tls_engine_conn *ec) -{ - const mbedtls_x509_crt *crt = mbedtls_ssl_get_peer_cert(&ec->ctx); - if (!crt) { - return (NULL); - } - - const mbedtls_asn1_sequence *seq = &crt->subject_alt_names; - - // get count - int count = 0; - do { - if (seq->buf.len > 0) - ++count; - seq = seq->next; - } while (seq); - if (count == 0) - return NULL; - - seq = &crt->subject_alt_names; - - // copy strings - char **rv = malloc((count + 1) * sizeof(char *)); - int i = 0; - do { - if (seq->buf.len == 0) - continue; - - rv[i] = malloc(seq->buf.len + 1); - memcpy(rv[i], seq->buf.p, seq->buf.len); - rv[i][seq->buf.len] = 0; - ++i; - - seq = seq->next; - } while (seq); - rv[i] = NULL; - - return rv; -} - static void config_fini(nng_tls_engine_config *cfg) { @@ -713,6 +703,223 @@ err: return (rv); } +static void +cert_fini(nng_tls_engine_cert *crt) +{ + mbedtls_x509_crt_free(&crt->crt); + nni_free(crt, sizeof(*crt)); +} + +static nng_err +cert_parse_der(nng_tls_engine_cert **crtp, const uint8_t *der, size_t size) +{ + nng_tls_engine_cert *cert; + int rv; + + if ((cert = nni_zalloc(sizeof(*cert))) == NULL) { + return (NNG_ENOMEM); + } + mbedtls_x509_crt_init(&cert->crt); + if ((rv = mbedtls_x509_crt_parse_der(&cert->crt, der, size)) != 0) { + tls_log_err( + "NNG-TLS-DER", "Failure parsing DER certificate", rv); + rv = tls_mk_err(rv); + cert_fini(cert); + return (rv); + } + *crtp = cert; + return (NNG_OK); +} + +static nng_err +cert_parse_pem(nng_tls_engine_cert **crtp, const char *pem, size_t size) +{ + nng_tls_engine_cert *cert; + int rv; + + if ((cert = nni_zalloc(sizeof(*cert))) == NULL) { + return (NNG_ENOMEM); + } + mbedtls_x509_crt_init(&cert->crt); + if ((rv = mbedtls_x509_crt_parse( + &cert->crt, (const uint8_t *) pem, size)) != 0) { + tls_log_err( + "NNG-TLS-PEM", "Failure parsing PEM certificate", rv); + rv = tls_mk_err(rv); + cert_fini(cert); + return (rv); + } + *crtp = cert; + return (NNG_OK); +} + +static nng_err +cert_get_der(nng_tls_engine_cert *crt, uint8_t *buf, size_t *sizep) +{ + if (*sizep < crt->crt.raw.len) { + *sizep = crt->crt.raw.len; + return (NNG_ENOSPC); + } + *sizep = crt->crt.raw.len; + memcpy(buf, crt->crt.raw.p, *sizep); + return (NNG_OK); +} + +static nng_err +cert_subject(nng_tls_engine_cert *crt, char **namep) +{ + if (crt->subject[0] != 0) { + *namep = (char *) &crt->subject[0]; + return (NNG_OK); + } + int len = mbedtls_x509_dn_gets( + crt->subject, sizeof(crt->subject), &crt->crt.subject); + if (len <= 0) { + return (NNG_ENOTSUP); + } + *namep = &crt->subject[0]; + return (NNG_OK); +} + +static nng_err +cert_issuer(nng_tls_engine_cert *crt, char **namep) +{ + if (crt->issuer[0] != 0) { + *namep = (char *) &crt->issuer[0]; + return (NNG_OK); + } + int len = mbedtls_x509_dn_gets( + crt->issuer, sizeof(crt->issuer), &crt->crt.issuer); + if (len <= 0) { + return (NNG_ENOTSUP); + } + *namep = &crt->issuer[0]; + return (NNG_OK); +} + +static nng_err +cert_serial(nng_tls_engine_cert *crt, char **namep) +{ + if (crt->serial[0] != 0) { + *namep = (char *) &crt->serial[0]; + return (NNG_OK); + } + int len = mbedtls_x509_serial_gets( + crt->serial, sizeof(crt->serial), &crt->crt.serial); + if (len < 0) { + return (tls_mk_err(len)); + } + *namep = &crt->serial[0]; + return (NNG_OK); +} + +static nng_err +cert_subject_cn(nng_tls_engine_cert *crt, char **namep) +{ + nng_err rv; + char *subject; + char *s; + if (crt->cn[0] != 0) { + *namep = (char *) &crt->cn[0]; + return (NNG_OK); + } + if ((rv = cert_subject(crt, &subject)) != NNG_OK) { + return (rv); + } + if ((s = strstr(crt->subject, "CN=")) == NULL) { + return (NNG_ENOENT); + } + s += 3; + int n = 0; + bool esc = false; + while (n < 64) { + if (*s == 0) { + crt->cn[n++] = 0; + break; + } + if (esc) { + esc = false; + crt->cn[n++] = *s++; + } else if (*s == '\\') { + esc = true; + } else if (*s == ',') { + crt->cn[n++] = 0; + break; + } else { + crt->cn[n++] = *s++; + } + } + if ((n == 0) || (n > 64)) { + nng_log_warn( + "NNG-TLS-CN", "X.509 Subject CN length is invalid"); + return (NNG_EINVAL); + } + *namep = (char *) &crt->cn[0]; + return (0); +} + +static nng_err +cert_next_alt(nng_tls_engine_cert *crt, char **namep) +{ + const mbedtls_asn1_sequence *seq = &crt->crt.subject_alt_names; + + // get count + int count = 0; + for (seq = &crt->crt.subject_alt_names; seq != NULL; seq = seq->next) { + if (seq->buf.len == 0) { + continue; + } + if (count == crt->next_alt) { + break; + } + count++; + } + if (seq == NULL) { + return (NNG_ENOENT); + } + crt->next_alt++; + if (seq->buf.len >= sizeof(crt->last_alt)) { + return (NNG_EINVAL); + } + memcpy(crt->last_alt, seq->buf.p, seq->buf.len); + crt->last_alt[seq->buf.len] = 0; + *namep = crt->last_alt; + return (NNG_OK); +} + +static void +mbed_time_to_tm(mbedtls_x509_time *mt, struct tm *tmp) +{ + memset(tmp, 0, sizeof(*tmp)); // also clears any zone offset, etc + if (mt->year < 100) { + // all dates > Y2K, relative to 1900 + tmp->tm_year = mt->year + 100; + } else { + tmp->tm_year = mt->year - 1900; + } + tmp->tm_mon = mt->mon - 1; // month is zero based + tmp->tm_mday = mt->day; + tmp->tm_hour = mt->hour; + tmp->tm_min = mt->min; + tmp->tm_sec = mt->sec; + // canonicalize the time + (void) mktime(tmp); +} + +static nng_err +cert_not_before(nng_tls_engine_cert *cert, struct tm *tmp) +{ + mbed_time_to_tm(&cert->crt.valid_from, tmp); + return (NNG_OK); +} + +static nng_err +cert_not_after(nng_tls_engine_cert *cert, struct tm *tmp) +{ + mbed_time_to_tm(&cert->crt.valid_to, tmp); + return (NNG_OK); +} + static int config_version(nng_tls_engine_config *cfg, nng_tls_version min_ver, nng_tls_version max_ver) @@ -826,22 +1033,37 @@ static nng_tls_engine_config_ops config_ops = { }; static nng_tls_engine_conn_ops conn_ops = { - .size = sizeof(nng_tls_engine_conn), - .init = conn_init, - .fini = conn_fini, - .close = conn_close, - .recv = conn_recv, - .send = conn_send, - .handshake = conn_handshake, - .verified = conn_verified, - .peer_cn = conn_peer_cn, - .peer_alt_names = conn_peer_alt_names, + .size = sizeof(nng_tls_engine_conn), + .init = conn_init, + .fini = conn_fini, + .close = conn_close, + .recv = conn_recv, + .send = conn_send, + .handshake = conn_handshake, + .verified = conn_verified, + .peer_cert = conn_peer_cert, + .peer_cn = conn_peer_cn, +}; + +static nng_tls_engine_cert_ops cert_ops = { + .fini = cert_fini, + .parse_der = cert_parse_der, + .parse_pem = cert_parse_pem, + .get_der = cert_get_der, + .subject = cert_subject, + .issuer = cert_issuer, + .serial_number = cert_serial, + .subject_cn = cert_subject_cn, + .next_alt_name = cert_next_alt, + .not_before = cert_not_before, + .not_after = cert_not_after, }; nng_tls_engine nng_tls_engine_ops = { .version = NNG_TLS_ENGINE_VERSION, .config_ops = &config_ops, .conn_ops = &conn_ops, + .cert_ops = &cert_ops, .name = "mbed", .description = MBEDTLS_VERSION_STRING_FULL, .init = tls_engine_init, diff --git a/src/supplemental/tls/tls_common.c b/src/supplemental/tls/tls_common.c index 543b7563..75b056b3 100644 --- a/src/supplemental/tls/tls_common.c +++ b/src/supplemental/tls/tls_common.c @@ -45,6 +45,7 @@ static void tls_bio_send_start(nni_tls_conn *); static void tls_bio_error(nni_tls_conn *, nng_err); #define nni_tls_conn_ops (nng_tls_engine_ops.conn_ops) +#define nni_tls_cert_ops (nng_tls_engine_ops.cert_ops) #define nni_tls_cfg_ops (nng_tls_engine_ops.config_ops) static void @@ -134,7 +135,7 @@ nni_tls_verified(nni_tls_conn *conn) nni_mtx_lock(&conn->lock); result = nni_tls_conn_ops->verified((void *) (conn + 1)); nni_mtx_unlock(&conn->lock); - return result; + return (result); } const char * @@ -144,7 +145,14 @@ nni_tls_peer_cn(nni_tls_conn *conn) nni_mtx_lock(&conn->lock); result = nni_tls_conn_ops->peer_cn((void *) (conn + 1)); nni_mtx_unlock(&conn->lock); - return result; + return (result); +} + +nng_err +nni_tls_peer_cert(nni_tls_conn *conn, nng_tls_cert **certp) +{ + return ( + nni_tls_conn_ops->peer_cert((void *) (conn + 1), (void *) certp)); } int @@ -798,6 +806,103 @@ nng_tls_config_hold(nng_tls_config *cfg) nni_mtx_unlock(&cfg->lock); } +void +nng_tls_cert_free(nng_tls_cert *cert) +{ + nni_tls_cert_ops->fini((void *) cert); +} + +nng_err +nng_tls_cert_subject(nng_tls_cert *cert, char **namep) +{ + if (nni_tls_cert_ops->subject == NULL) { + return (NNG_ENOTSUP); + } + return (nni_tls_cert_ops->subject((void *) cert, namep)); +} + +nng_err +nng_tls_cert_issuer(nng_tls_cert *cert, char **namep) +{ + if (nni_tls_cert_ops->issuer == NULL) { + return (NNG_ENOTSUP); + } + return (nni_tls_cert_ops->issuer((void *) cert, namep)); +} + +nng_err +nng_tls_cert_serial_number(nng_tls_cert *cert, char **serialp) +{ + if (nni_tls_cert_ops->serial_number == NULL) { + return (NNG_ENOTSUP); + } + return (nni_tls_cert_ops->serial_number((void *) cert, serialp)); +} + +nng_err +nng_tls_cert_subject_cn(nng_tls_cert *cert, char **cnp) +{ + if (nni_tls_cert_ops->subject_cn == NULL) { + return (NNG_ENOTSUP); + } + return (nni_tls_cert_ops->subject_cn((void *) cert, cnp)); +} + +nng_err +nng_tls_cert_next_alt(nng_tls_cert *cert, char **alt) +{ + if (nni_tls_cert_ops->next_alt_name == NULL) { + return (NNG_ENOTSUP); + } + return (nni_tls_cert_ops->next_alt_name((void *) cert, alt)); +} + +nng_err +nng_tls_cert_not_before(nng_tls_cert *cert, struct tm *tmp) +{ + if (nni_tls_cert_ops->not_before == NULL) { + return (NNG_ENOTSUP); + } + return (nni_tls_cert_ops->not_before((void *) cert, tmp)); +} + +nng_err +nng_tls_cert_not_after(nng_tls_cert *cert, struct tm *tmp) +{ + if (nni_tls_cert_ops->not_after == NULL) { + return (NNG_ENOTSUP); + } + return (nni_tls_cert_ops->not_after((void *) cert, tmp)); +} + +void +nng_tls_cert_der(nng_tls_cert *cert, uint8_t *buf, size_t *bufsz) +{ + nni_tls_cert_ops->get_der((void *) cert, buf, bufsz); +} + +nng_err +nng_tls_cert_parse_der(nng_tls_cert **certp, const uint8_t *der, size_t size) +{ + nng_tls_engine_cert *ecrt; + nng_err rv; + if ((rv = nni_tls_cert_ops->parse_der(&ecrt, der, size)) == NNG_OK) { + *certp = (void *) ecrt; + } + return (rv); +} + +nng_err +nng_tls_cert_parse_pem(nng_tls_cert **certp, const char *pem, size_t size) +{ + nng_tls_engine_cert *ecrt; + nng_err rv; + if ((rv = nni_tls_cert_ops->parse_pem(&ecrt, pem, size)) == NNG_OK) { + *certp = (void *) ecrt; + } + return (rv); +} + const char * nng_tls_engine_name(void) { diff --git a/src/supplemental/tls/tls_common.h b/src/supplemental/tls/tls_common.h index 693948c2..6d163fd5 100644 --- a/src/supplemental/tls/tls_common.h +++ b/src/supplemental/tls/tls_common.h @@ -49,6 +49,8 @@ struct nng_tls_config { // ... engine config data follows }; +struct nng_tls_cert_s; + typedef struct nni_tls_bio_ops_s { void (*bio_send)(void *, nng_aio *); void (*bio_recv)(void *, nng_aio *); @@ -99,6 +101,7 @@ extern void nni_tls_recv(nni_tls_conn *conn, nni_aio *aio); extern void nni_tls_send(nni_tls_conn *conn, nni_aio *aio); extern bool nni_tls_verified(nni_tls_conn *conn); extern const char *nni_tls_peer_cn(nni_tls_conn *conn); +extern nng_err nni_tls_peer_cert(nni_tls_conn *conn, nng_tls_cert **certp); extern nng_err nni_tls_run(nni_tls_conn *conn); extern size_t nni_tls_engine_conn_size(void); diff --git a/src/supplemental/tls/tls_dialer.c b/src/supplemental/tls/tls_dialer.c index 0b2acee7..514f03b7 100644 --- a/src/supplemental/tls/tls_dialer.c +++ b/src/supplemental/tls/tls_dialer.c @@ -13,7 +13,7 @@ #include <stdlib.h> #include <string.h> -#include "core/nng_impl.h" +#include "../../core/nng_impl.h" #include "tls_common.h" #include "tls_engine.h" diff --git a/src/supplemental/tls/tls_engine.h b/src/supplemental/tls/tls_engine.h index 048e35a7..c0e395d5 100644 --- a/src/supplemental/tls/tls_engine.h +++ b/src/supplemental/tls/tls_engine.h @@ -13,6 +13,8 @@ #ifndef NNG_SUPPLEMENTAL_TLS_TLS_ENGINE_H #define NNG_SUPPLEMENTAL_TLS_TLS_ENGINE_H +#include <time.h> + #include "../../core/defs.h" // Locking theory statement for TLS engines. The engine is assumed @@ -35,6 +37,10 @@ typedef struct nng_tls_engine_conn nng_tls_engine_conn; // definition locally. typedef struct nng_tls_engine_config nng_tls_engine_config; +// nng_tls_engine_cert represents the engine-specific representation +// of an X.509 certificate. +typedef struct nng_tls_engine_cert nng_tls_engine_cert; + typedef struct nng_tls_engine_conn_ops_s { // size is the size of the engine's per-connection state. // The framework will allocate this on behalf of the engine. @@ -84,13 +90,13 @@ typedef struct nng_tls_engine_conn_ops_s { // TLS verified, false otherwise. bool (*verified)(nng_tls_engine_conn *); + // peer_cert obtains the peer certificate(s). Note that + // this capability might not be supported. + nng_err (*peer_cert)(nng_tls_engine_conn *, nng_tls_engine_cert **); + // peer_cn returns the common name of the peer // The return string needs to be freed. char *(*peer_cn)(nng_tls_engine_conn *); - - // peer_alt_names returns the subject alternative names. - // The return string list and its strings need to be freed. - char **(*peer_alt_names)(nng_tls_engine_conn *); } nng_tls_engine_conn_ops; typedef struct nng_tls_engine_config_ops_s { @@ -170,12 +176,51 @@ typedef struct nng_tls_engine_config_ops_s { nng_tls_engine_config *, nng_tls_version, nng_tls_version); } nng_tls_engine_config_ops; +typedef struct nng_tls_engine_cert_ops_s { + // fini is used to tear down the configuration object. + // This will only be called on objects that have been properly + // initialized with nte_config_init. + void (*fini)(nng_tls_engine_cert *); + + // parse_pem parses a PEM object to obtain a certificate object. + nng_err (*parse_pem)( + nng_tls_engine_cert **certp, const char *pem, size_t pem_size); + + // parse_der parses a DER object to obtain a certificate object. + nng_err (*parse_der)( + nng_tls_engine_cert **certp, const uint8_t *der, size_t der_size); + + // get_der extracts the DER content from the object, which may then be + // used with other X509 APIs. + nng_err (*get_der)( + nng_tls_engine_cert *cert, uint8_t *dr, size_t *sizep); + + // obtain the subject name + nng_err (*subject)(nng_tls_engine_cert *cert, char **name); + + nng_err (*issuer)(nng_tls_engine_cert *cert, char **name); + + nng_err (*serial_number)(nng_tls_engine_cert *cert, char **serial); + + nng_err (*subject_cn)(nng_tls_engine_cert *cert, char **name); + + nng_err (*next_alt_name)(nng_tls_engine_cert *cert, char **name); + + // aka, valid from, the starting date of the certificate + nng_err (*not_before)(nng_tls_engine_cert *cert, struct tm *); + + // aka, valid to, the expiration date of the certificate + nng_err (*not_after)(nng_tls_engine_cert *cert, struct tm *); + +} nng_tls_engine_cert_ops; + typedef enum nng_tls_engine_version_e { NNG_TLS_ENGINE_V0 = 0, NNG_TLS_ENGINE_V1 = 1, // adds FIPS, TLS 1.3 support NNG_TLS_ENGINE_V2 = 2, // adds PSK support NNG_TLS_ENGINE_V3 = 3, // refactored API - NNG_TLS_ENGINE_VERSION = NNG_TLS_ENGINE_V3, + NNG_TLS_ENGINE_V4 = 4, // added cert ops + NNG_TLS_ENGINE_VERSION = NNG_TLS_ENGINE_V4, } nng_tls_engine_version; typedef struct nng_tls_engine_s { @@ -190,6 +235,9 @@ typedef struct nng_tls_engine_s { // conn_ops is the operations for TLS connections (stream-oriented). const nng_tls_engine_conn_ops *conn_ops; + // cert_ops is the operations for TLS certificates. + const nng_tls_engine_cert_ops *cert_ops; + // name contains the name of the engine, for example "wolfSSL". // It is acceptable to append a version number as well. const char *name; diff --git a/src/supplemental/tls/tls_listener.c b/src/supplemental/tls/tls_listener.c index 5d2d8599..62dd0904 100644 --- a/src/supplemental/tls/tls_listener.c +++ b/src/supplemental/tls/tls_listener.c @@ -13,7 +13,7 @@ #include <stdlib.h> #include <string.h> -#include "core/nng_impl.h" +#include "../../core/nng_impl.h" #include "tls_common.h" #include "tls_engine.h" diff --git a/src/supplemental/tls/tls_stream.c b/src/supplemental/tls/tls_stream.c index 4a033887..d3dd9497 100644 --- a/src/supplemental/tls/tls_stream.c +++ b/src/supplemental/tls/tls_stream.c @@ -15,6 +15,7 @@ #include "../../core/nng_impl.h" +#include "nng/nng.h" #include "tls_common.h" #include "tls_engine.h" #include "tls_stream.h" @@ -142,6 +143,7 @@ tls_stream_conn_cb(void *arg) static nng_err tls_stream_get( void *arg, const char *name, void *buf, size_t *szp, nni_type t); +static nng_err tls_stream_peer_cert(void *arg, nng_tls_cert **); int nni_tls_stream_alloc(tls_stream **tsp, nng_tls_config *cfg, nng_aio *user_aio) @@ -160,12 +162,13 @@ nni_tls_stream_alloc(tls_stream **tsp, nng_tls_config *cfg, nng_aio *user_aio) ts->user_aio = user_aio; // NB: free is exposed for benefit of dialer/listener - ts->stream.s_free = nni_tls_stream_free; - ts->stream.s_close = tls_stream_close; - ts->stream.s_stop = tls_stream_stop; - ts->stream.s_send = tls_stream_send; - ts->stream.s_recv = tls_stream_recv; - ts->stream.s_get = tls_stream_get; + ts->stream.s_free = nni_tls_stream_free; + ts->stream.s_close = tls_stream_close; + ts->stream.s_stop = tls_stream_stop; + ts->stream.s_send = tls_stream_send; + ts->stream.s_recv = tls_stream_recv; + ts->stream.s_get = tls_stream_get; + ts->stream.s_peer_cert = tls_stream_peer_cert; nni_aio_init(&ts->conn_aio, tls_stream_conn_cb, ts); @@ -200,6 +203,13 @@ tls_get_peer_cn(void *arg, void *buf, size_t *szp, nni_type t) return (NNG_OK); } +static nng_err +tls_stream_peer_cert(void *arg, nng_tls_cert **certp) +{ + tls_stream *ts = arg; + return (nni_tls_peer_cert(&ts->conn, certp)); +} + static const nni_option tls_stream_options[] = { { .o_name = NNG_OPT_TLS_VERIFIED, diff --git a/src/supplemental/tls/wolfssl/CMakeLists.txt b/src/supplemental/tls/wolfssl/CMakeLists.txt index 762757a3..e580860e 100644 --- a/src/supplemental/tls/wolfssl/CMakeLists.txt +++ b/src/supplemental/tls/wolfssl/CMakeLists.txt @@ -60,7 +60,13 @@ if (NNG_TLS_ENGINE STREQUAL "wolf") if (NNG_WOLFSSL_HAVE_PEER_CERT) nng_defines(NNG_WOLFSSL_HAVE_PEER_CERT) else () - message(STATUS "wolfSSL configured without peer cert chain support.") + message(STATUS "wolfSSL configured without full certificate APIs.") + message(NOTICE " + ************************************************************ + WolfSSL functionality for working with certificates is missing. + Consider rebuilding WolfSSL for full functionality. + ************************************************************") + endif () if (NNG_WOLFSSL_HAVE_PSK) diff --git a/src/supplemental/tls/wolfssl/wolfssl.c b/src/supplemental/tls/wolfssl/wolfssl.c index a18c1605..ef3f7391 100644 --- a/src/supplemental/tls/wolfssl/wolfssl.c +++ b/src/supplemental/tls/wolfssl/wolfssl.c @@ -43,6 +43,13 @@ struct nng_tls_engine_conn { int auth_mode; }; +struct nng_tls_engine_cert { + WOLFSSL_X509 *crt; + char *subject; + char *issuer; + char serial[64]; // maximum binary serial is 20 bytes +}; + typedef struct psk { // NB: Technically RFC 4279 requires this be UTF-8 string, although // others suggest making it opaque bytes. We treat it as a C string, @@ -281,53 +288,47 @@ wolf_conn_verified(nng_tls_engine_conn *ec) } } -static char * -wolf_conn_peer_cn(nng_tls_engine_conn *ec) +static nng_err +wolf_conn_peer_cert(nng_tls_engine_conn *ec, nng_tls_engine_cert **certp) { #ifdef NNG_WOLFSSL_HAVE_PEER_CERT - WOLFSSL_X509 *cert; - char *cn; + nng_tls_engine_cert *cert; - if ((cert = wolfSSL_get_peer_certificate(ec->ssl)) == NULL) { - return (NULL); + WOLFSSL_X509 *wc; + if ((wc = wolfSSL_get_peer_certificate(ec->ssl)) == NULL) { + return (NNG_ENOENT); } - cn = wolfSSL_X509_get_subjectCN(cert); - if (cn != NULL) { - cn = nng_strdup(cn); + if ((cert = nni_zalloc(sizeof(*cert))) == NULL) { + wolfSSL_X509_free(wc); + return (NNG_ENOMEM); } - return (cn); + cert->crt = wc; + *certp = cert; + return (NNG_OK); #else - return (NULL); + NNI_ARG_UNUSED(ec); + NNI_ARG_UNUSED(certp); + return (NNG_ENOTSUP); #endif } -static char ** -wolf_conn_peer_alt_names(nng_tls_engine_conn *ec) +static char * +wolf_conn_peer_cn(nng_tls_engine_conn *ec) { #ifdef NNG_WOLFSSL_HAVE_PEER_CERT WOLFSSL_X509 *cert; - int num = 0; - char **names; + char *cn; if ((cert = wolfSSL_get_peer_certificate(ec->ssl)) == NULL) { return (NULL); } - while (wolfSSL_X509_get_next_altname(cert) != NULL) { - num++; - } - if ((names = nni_zalloc(sizeof(char *) * num)) == NULL) { - return (NULL); - } - if ((cert = wolfSSL_get_peer_certificate(ec->ssl)) == NULL) { - nni_free(names, sizeof(char *) * num); - return (NULL); - } - for (int i = 0; i < num; i++) { - names[i] = wolfSSL_X509_get_next_altname(cert); - NNI_ASSERT(names[i] != NULL); + cn = wolfSSL_X509_get_subjectCN(cert); + if (cn != NULL) { + cn = nng_strdup(cn); } - return (names); + return (cn); #else + NNI_ARG_UNUSED(ec); return (NULL); #endif } @@ -678,6 +679,240 @@ wolf_config_version(nng_tls_engine_config *cfg, nng_tls_version min_ver, } static void +wolf_cert_free(nng_tls_engine_cert *cert) +{ +#ifdef NNG_WOLFSSL_HAVE_PEER_CERT + if (cert->subject != NULL) { + wolfSSL_Free(cert->subject); + } + if (cert->issuer != NULL) { + wolfSSL_Free(cert->issuer); + } + if (cert->crt != NULL) { + wolfSSL_X509_free(cert->crt); + } + nni_free(cert, sizeof(*cert)); +#else + NNI_ARG_UNUSED(cert); +#endif +} + +// In struct nng_tls_engine_cert_ops_s +static nng_err +wolf_cert_get_der(nng_tls_engine_cert *cert, uint8_t *buf, size_t *sz) +{ +#ifdef NNG_WOLFSSL_HAVE_PEER_CERT + const uint8_t *der; + int derSz; + der = wolfSSL_X509_get_der(cert->crt, &derSz); + if (*sz < (size_t) derSz) { + *sz = (size_t) derSz; + return (NNG_ENOSPC); + } + *sz = (size_t) derSz; + memcpy(buf, der, *sz); + return (NNG_OK); +#else + NNI_ARG_UNUSED(cert); + NNI_ARG_UNUSED(buf); + NNI_ARG_UNUSED(sz); + return (NNG_ENOTSUP); +#endif +} + +static nng_err +wolf_cert_parse_der( + nng_tls_engine_cert **crtp, const uint8_t *der, size_t size) +{ +#ifdef NNG_WOLFSSL_HAVE_PEER_CERT + WOLFSSL_X509 *x; + nng_tls_engine_cert *cert; + + if ((cert = nni_zalloc(sizeof(*cert))) == NULL) { + return (NNG_ENOMEM); + } + if ((cert->crt = wolfSSL_X509_d2i(&x, der, size)) == NULL) { + nni_free(cert, sizeof(*cert)); + return (NNG_ENOMEM); + } + *crtp = cert; + return (NNG_OK); +#else + NNI_ARG_UNUSED(crtp); + NNI_ARG_UNUSED(der); + NNI_ARG_UNUSED(size); + return (NNG_ENOTSUP); +#endif +} + +static nng_err +wolf_cert_parse_pem(nng_tls_engine_cert **crtp, const char *pem, size_t size) +{ + nng_err rv; + uint8_t *derBuf; + int derSize; + + // DER files are smaller than PEM (PEM is base64 encoded and includes + // headers) + if ((derBuf = nni_alloc(size)) == NULL) { + return (NNG_ENOMEM); + } + + derSize = wc_CertPemToDer( + (const uint8_t *) pem, size, derBuf, size, 0 /* cert type */); + if (derSize < 0) { + nni_free(derBuf, size); + return (NNG_ECRYPTO); + } + + rv = wolf_cert_parse_der(crtp, derBuf, derSize); + nni_free(derBuf, size); + return (rv); +} + +static nng_err +wolf_cert_subject(nng_tls_engine_cert *cert, char **subject) +{ +#ifdef NNG_WOLFSSL_HAVE_PEER_CERT + WOLFSSL_X509_NAME *xn; + + if (cert->subject != NULL) { + *subject = cert->subject; + return (NNG_OK); + } + + xn = wolfSSL_X509_get_subject_name(cert->crt); + if (xn == NULL) { + return (NNG_ENOENT); + } + cert->subject = wolfSSL_X509_NAME_oneline(xn, NULL, 0); + *subject = cert->subject; + return (NNG_OK); +#else + NNI_ARG_UNUSED(cert); + NNI_ARG_UNUSED(subject); + return (NNG_ENOTSUP); +#endif +} + +static nng_err +wolf_cert_issuer(nng_tls_engine_cert *cert, char **issuer) +{ +#ifdef NNG_WOLFSSL_HAVE_PEER_CERT + WOLFSSL_X509_NAME *xn; + + if (cert->issuer != NULL) { + *issuer = cert->issuer; + return (NNG_OK); + } + + xn = wolfSSL_X509_get_issuer_name(cert->crt); + if (xn == NULL) { + return (NNG_ENOENT); + } + cert->issuer = wolfSSL_X509_NAME_oneline(xn, NULL, 0); + *issuer = cert->issuer; + return (NNG_OK); +#else + NNI_ARG_UNUSED(cert); + NNI_ARG_UNUSED(issuer); + return (NNG_ENOTSUP); +#endif +} + +static nng_err +wolf_cert_serial(nng_tls_engine_cert *cert, char **serial) +{ +#ifdef NNG_WOLFSSL_HAVE_PEER_CERT + uint8_t num[20]; // max is 20 bytes per RFC + char *s; + int len; + + if (cert->serial[0] != 0) { + *serial = cert->serial; + return (NNG_OK); + } + + len = sizeof(num); + wolfSSL_X509_get_serial_number(cert->crt, num, &len); + + s = cert->serial; + for (int i = 0; i < len; i++) { + snprintf(s, 4, "%s%02X", i > 0 ? ":" : "", num[i]); + s += strlen(s); + } + *serial = cert->serial; + return (NNG_OK); +#else + NNI_ARG_UNUSED(cert); + NNI_ARG_UNUSED(serial); + return (NNG_ENOTSUP); +#endif +} + +static nng_err +wolf_cert_subject_cn(nng_tls_engine_cert *cert, char **cn) +{ +#ifdef NNG_WOLFSSL_HAVE_PEER_CERT + *cn = wolfSSL_X509_get_subjectCN(cert->crt); + if (*cn == NULL) { + return (NNG_ENOENT); + } + return (NNG_OK); +#else + NNI_ARG_UNUSED(cert); + NNI_ARG_UNUSED(cn); + return (NNG_ENOTSUP); +#endif +} + +static nng_err +wolf_cert_next_alt(nng_tls_engine_cert *cert, char **alt) +{ +#ifdef NNG_WOLFSSL_HAVE_PEER_CERT + *alt = wolfSSL_X509_get_next_altname(cert->crt); + if (*alt == NULL) { + return (NNG_ENOENT); + } + return (NNG_OK); +#else + NNI_ARG_UNUSED(cert); + NNI_ARG_UNUSED(alt); + return (NNG_ENOTSUP); +#endif +} + +static nng_err +wolf_cert_not_before(nng_tls_engine_cert *cert, struct tm *tmp) +{ +#ifdef NNG_WOLFSSL_HAVE_PEER_CERT + WOLFSSL_ASN1_TIME *when; + when = wolfSSL_X509_get_notBefore(cert->crt); + wolfSSL_ASN1_TIME_to_tm(when, tmp); + return (NNG_OK); +#else + NNI_ARG_UNUSED(cert); + NNI_ARG_UNUSED(tmp); + return (NNG_ENOTSUP); +#endif +} + +static nng_err +wolf_cert_not_after(nng_tls_engine_cert *cert, struct tm *tmp) +{ +#ifdef NNG_WOLFSSL_HAVE_PEER_CERT + WOLFSSL_ASN1_TIME *when; + when = wolfSSL_X509_get_notAfter(cert->crt); + wolfSSL_ASN1_TIME_to_tm(when, tmp); + return (NNG_OK); +#else + NNI_ARG_UNUSED(cert); + NNI_ARG_UNUSED(tmp); + return (NNG_ENOTSUP); +#endif +} + +static void wolf_logging_cb(const int level, const char *msg) { switch (level) { @@ -742,22 +977,37 @@ static nng_tls_engine_config_ops wolf_config_ops = { }; static nng_tls_engine_conn_ops wolf_conn_ops = { - .size = sizeof(nng_tls_engine_conn), - .init = wolf_conn_init, - .fini = wolf_conn_fini, - .close = wolf_conn_close, - .recv = wolf_conn_recv, - .send = wolf_conn_send, - .handshake = wolf_conn_handshake, - .verified = wolf_conn_verified, - .peer_cn = wolf_conn_peer_cn, - .peer_alt_names = wolf_conn_peer_alt_names, + .size = sizeof(nng_tls_engine_conn), + .init = wolf_conn_init, + .fini = wolf_conn_fini, + .close = wolf_conn_close, + .recv = wolf_conn_recv, + .send = wolf_conn_send, + .handshake = wolf_conn_handshake, + .verified = wolf_conn_verified, + .peer_cn = wolf_conn_peer_cn, + .peer_cert = wolf_conn_peer_cert, +}; + +static nng_tls_engine_cert_ops wolf_cert_ops = { + .fini = wolf_cert_free, + .get_der = wolf_cert_get_der, + .parse_der = wolf_cert_parse_der, + .parse_pem = wolf_cert_parse_pem, + .subject = wolf_cert_subject, + .issuer = wolf_cert_issuer, + .serial_number = wolf_cert_serial, + .subject_cn = wolf_cert_subject_cn, + .next_alt_name = wolf_cert_next_alt, + .not_before = wolf_cert_not_before, + .not_after = wolf_cert_not_after, }; nng_tls_engine nng_tls_engine_ops = { .version = NNG_TLS_ENGINE_VERSION, .config_ops = &wolf_config_ops, .conn_ops = &wolf_conn_ops, + .cert_ops = &wolf_cert_ops, .name = "wolf", .description = "wolfSSL " LIBWOLFSSL_VERSION_STRING, .init = tls_engine_init, diff --git a/src/supplemental/websocket/websocket.c b/src/supplemental/websocket/websocket.c index 597efb76..15078704 100644 --- a/src/supplemental/websocket/websocket.c +++ b/src/supplemental/websocket/websocket.c @@ -186,6 +186,7 @@ static void ws_str_close(void *); static void ws_str_send(void *, nng_aio *); static void ws_str_recv(void *, nng_aio *); static nng_err ws_str_get(void *, const char *, void *, size_t *, nni_type); +static nng_err ws_str_peer_cert(void *, nng_tls_cert **); static void ws_listener_close(void *); static void ws_listener_free(void *); @@ -1388,12 +1389,13 @@ ws_init(nni_ws **wsp) nni_aio_set_timeout(&ws->closeaio, 100); nni_aio_set_timeout(&ws->httpaio, 2000); - ws->ops.s_close = ws_str_close; - ws->ops.s_free = ws_str_free; - ws->ops.s_stop = ws_stop; - ws->ops.s_send = ws_str_send; - ws->ops.s_recv = ws_str_recv; - ws->ops.s_get = ws_str_get; + ws->ops.s_close = ws_str_close; + ws->ops.s_free = ws_str_free; + ws->ops.s_stop = ws_stop; + ws->ops.s_send = ws_str_send; + ws->ops.s_recv = ws_str_recv; + ws->ops.s_get = ws_str_get; + ws->ops.s_peer_cert = ws_str_peer_cert; ws->fragsize = 1 << 20; // we won't send a frame larger than this *wsp = ws; @@ -2754,3 +2756,17 @@ ws_str_get(void *arg, const char *nm, void *buf, size_t *szp, nni_type t) } return (rv); } + +static nng_err +ws_str_peer_cert(void *arg, nng_tls_cert **certp) +{ + nni_ws *ws = arg; + + nni_mtx_lock(&ws->mtx); + if (ws->closed) { + nni_mtx_unlock(&ws->mtx); + return (NNG_ECLOSED); + } + nni_mtx_unlock(&ws->mtx); + return (nni_http_conn_peer_cert(ws->http, certp)); +} |
