diff options
Diffstat (limited to 'src/supplemental/tls/mbedtls/mbedtls.c')
| -rw-r--r-- | src/supplemental/tls/mbedtls/mbedtls.c | 326 |
1 files changed, 274 insertions, 52 deletions
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, |
