From 8e1836f57e8bcdb228dd5baadc71dfbf30b544e0 Mon Sep 17 00:00:00 2001 From: Christian Fischbach Date: Thu, 9 Feb 2023 00:48:17 +0100 Subject: [PATCH] Get common name and subject alternative names of peer certificate (#1617) Co-authored-by: Christian Fischbach --- docs/man/nng_tls.7.adoc | 2 + docs/man/nng_tls_options.5.adoc | 24 ++++++-- docs/man/nng_ws.7.adoc | 9 +++ include/nng/nng.h | 13 ++++ include/nng/supplemental/tls/engine.h | 8 +++ src/supplemental/tls/mbedtls/tls.c | 86 ++++++++++++++++++++++++--- src/supplemental/tls/tls_common.c | 42 ++++++++++++- 7 files changed, 169 insertions(+), 15 deletions(-) diff --git a/docs/man/nng_tls.7.adoc b/docs/man/nng_tls.7.adoc index 24fbc5f9d..017af4fc9 100644 --- a/docs/man/nng_tls.7.adoc +++ b/docs/man/nng_tls.7.adoc @@ -127,6 +127,8 @@ Note that setting these must be done before the transport is started. * xref:nng_tls_options.5.adoc#NNG_OPT_TLS_CERT_KEY_FILE[`NNG_OPT_TLS_CERT_KEY_FILE`] * xref:nng_tls_options.5.adoc#NNG_OPT_TLS_CONFIG[`NNG_OPT_TLS_CONFIG`] * 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 e2db237f9..a06a600fb 100644 --- a/docs/man/nng_tls_options.5.adoc +++ b/docs/man/nng_tls_options.5.adoc @@ -20,12 +20,14 @@ nng_tls_options - TLS-specific options ---- #include -#define NNG_OPT_TLS_AUTH_MODE "tls-authmode" -#define NNG_OPT_TLS_CA_FILE "tls-ca-file" -#define NNG_OPT_TLS_CERT_KEY_FILE "tls-cert-key-file" -#define NNG_OPT_TLS_CONFIG "tls-config" -#define NNG_OPT_TLS_SERVER_NAME "tls-server-name" -#define NNG_OPT_TLS_VERIFIED "tls-verified" +#define NNG_OPT_TLS_AUTH_MODE "tls-authmode" +#define NNG_OPT_TLS_CA_FILE "tls-ca-file" +#define NNG_OPT_TLS_CERT_KEY_FILE "tls-cert-key-file" +#define NNG_OPT_TLS_CONFIG "tls-config" +#define NNG_OPT_TLS_SERVER_NAME "tls-server-name" +#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 @@ -91,6 +93,16 @@ This read-only option indicates whether the remote peer has been properly verifi authentication. May return incorrect results if peer authentication is disabled. +[[NNG_OPT_TLS_PEER_CN]]((`NNG_OPT_TLS_PEER_CN`)):: +(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]]((`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 349955b1d..171ec9341 100644 --- a/docs/man/nng_ws.7.adoc +++ b/docs/man/nng_ws.7.adoc @@ -199,6 +199,15 @@ more details. authentication. May return incorrect results if peer authentication is disabled. +`NNG_OPT_TLS_PEER_CN`:: +(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/include/nng/nng.h b/include/nng/nng.h index 811cb456b..5bb320b68 100644 --- a/include/nng/nng.h +++ b/include/nng/nng.h @@ -755,6 +755,19 @@ NNG_DECL nng_listener nng_pipe_listener(nng_pipe); // peer authentication is disabled with `NNG_TLS_AUTH_MODE_NONE`. #define NNG_OPT_TLS_VERIFIED "tls-verified" +// NNG_OPT_TLS_PEER_CN returns the string with the common name +// 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_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. diff --git a/include/nng/supplemental/tls/engine.h b/include/nng/supplemental/tls/engine.h index 81385fbc4..309d83c5f 100644 --- a/include/nng/supplemental/tls/engine.h +++ b/include/nng/supplemental/tls/engine.h @@ -80,6 +80,14 @@ typedef struct nng_tls_engine_conn_ops_s { // verified returns true if the connection is fully // TLS verified, false otherwise. bool (*verified)(nng_tls_engine_conn *); + + // 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 { diff --git a/src/supplemental/tls/mbedtls/tls.c b/src/supplemental/tls/mbedtls/tls.c index a216b3a69..79f3ffd4b 100644 --- a/src/supplemental/tls/mbedtls/tls.c +++ b/src/supplemental/tls/mbedtls/tls.c @@ -276,6 +276,74 @@ conn_verified(nng_tls_engine_conn *ec) return (mbedtls_ssl_get_verify_result(&ec->ctx) == 0); } +static char * +conn_peer_cn(nng_tls_engine_conn *ec) +{ + const mbedtls_x509_crt *crt = mbedtls_ssl_get_peer_cert(&ec->ctx); + if (!crt) { + return (NULL); + } + + char buf[0x400]; + int len = mbedtls_x509_dn_gets(buf, sizeof(buf), &crt->subject); + if (len <= 0) { + return (NULL); + } + + const char *pos = strstr(buf, "CN="); + if (!pos) { + return (NULL); + } + + pos += 3; + len -= pos - buf - 1; + if (len <= 1) { + return (NULL); + } + + char *rv = malloc(len); + memcpy(rv, pos, len); + 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) { @@ -526,14 +594,16 @@ 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, + .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, }; static nng_tls_engine tls_engine_mbed = { diff --git a/src/supplemental/tls/tls_common.c b/src/supplemental/tls/tls_common.c index 357c8411c..d05a289dd 100644 --- a/src/supplemental/tls/tls_common.c +++ b/src/supplemental/tls/tls_common.c @@ -756,11 +756,51 @@ tls_get_verified(void *arg, void *buf, size_t *szp, nni_type t) return (nni_copyout_bool(v, buf, szp, t)); } +static int +tls_get_peer_cn(void *arg, void *buf, size_t *szp, nni_type t) +{ + NNI_ARG_UNUSED(szp); + + if (t != NNI_TYPE_STRING) { + return (NNG_EBADTYPE); + } + + tls_conn *conn = arg; + nni_mtx_lock(&conn->lock); + *(char **) buf = conn->ops.peer_cn((void *) (conn + 1)); + nni_mtx_unlock(&conn->lock); + return (0); +} + +static int +tls_get_peer_alt_names(void *arg, void *buf, size_t *szp, nni_type t) +{ + NNI_ARG_UNUSED(szp); + + if (t != NNI_TYPE_POINTER) { + return (NNG_EBADTYPE); + } + + tls_conn *conn = arg; + nni_mtx_lock(&conn->lock); + *(char ***) buf = conn->ops.peer_alt_names((void *) (conn + 1)); + nni_mtx_unlock(&conn->lock); + return (0); +} + static const nni_option tls_options[] = { { .o_name = NNG_OPT_TLS_VERIFIED, .o_get = tls_get_verified, }, + { + .o_name = NNG_OPT_TLS_PEER_CN, + .o_get = tls_get_peer_cn, + }, + { + .o_name = NNG_OPT_TLS_PEER_ALT_NAMES, + .o_get = tls_get_peer_alt_names, + }, { .o_name = NULL, }, @@ -1680,4 +1720,4 @@ nni_tls_sys_fini(void) { } -#endif // !NNG_SUPP_TLS \ No newline at end of file +#endif // !NNG_SUPP_TLS