From 4282a0882aed6cf072b7afcade4c12dc3bfe92a3 Mon Sep 17 00:00:00 2001 From: roman Date: Thu, 4 Apr 2024 09:24:54 +0200 Subject: [PATCH 01/54] cmake UPDATE add findlibmedtls module --- CMakeModules/FindLibMbedTLS.cmake | 99 +++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 CMakeModules/FindLibMbedTLS.cmake diff --git a/CMakeModules/FindLibMbedTLS.cmake b/CMakeModules/FindLibMbedTLS.cmake new file mode 100644 index 00000000..7ab6f330 --- /dev/null +++ b/CMakeModules/FindLibMbedTLS.cmake @@ -0,0 +1,99 @@ +# - Try to find LibMbedTLS +# Once done this will define +# +# LIBMBEDTLS_FOUND - system has LibPAM +# LIBMBEDTLS_INCLUDE_DIRS - the LibPAM include directory +# LIBMBEDTLS_LIBRARIES - link these to use LibPAM +# +# Author Roman Janota +# Copyright (c) 2024 CESNET, z.s.p.o. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# + +if(LIBMBEDTLS_LIBRARIES AND LIBMBEDTLS_INCLUDE_DIRS) + # in cache already + set(LIBMBEDTLS_FOUND TRUE) +else() + find_path(LIBMBEDTLS_INCLUDE_DIR + NAMES + mbedtls/ssl.h + PATHS + /opt/local/include + /sw/include + ${CMAKE_INCLUDE_PATH} + ${CMAKE_INSTALL_PREFIX}/include + ) + + find_library(LIBMBEDTLS_LIBRARY + NAMES + libmbedtls.so # TODO + PATHS + /usr/lib + /usr/lib64 + /opt/local/lib + /sw/lib + ${CMAKE_LIBRARY_PATH} + ${CMAKE_INSTALL_PREFIX}/lib + ) + + find_library(LIBMBEDX509_LIBRARY + NAMES + libmbedx509.so + PATHS + /usr/lib + /usr/lib64 + /opt/local/lib + /sw/lib + ${CMAKE_LIBRARY_PATH} + ${CMAKE_INSTALL_PREFIX}/lib + ) + + find_library(LIBMBEDCRYPTO_LIBRARY + NAMES + libmbedcrypto.so + PATHS + /usr/lib + /usr/lib64 + /opt/local/lib + /sw/lib + ${CMAKE_LIBRARY_PATH} + ${CMAKE_INSTALL_PREFIX}/lib + ) + + if(LIBMBEDTLS_INCLUDE_DIR AND LIBMBEDTLS_LIBRARY AND LIBMBEDX509_LIBRARY AND LIBMBEDCRYPTO_LIBRARY) + set(LIBMBEDTLS_FOUND TRUE) + else() + set(LIBMBEDTLS_FOUND FALSE) + endif() + + set(LIBMBEDTLS_INCLUDE_DIRS ${LIBMBEDTLS_INCLUDE_DIR}) + set(LIBMBEDTLS_LIBRARIES ${LIBMBEDTLS_LIBRARY} ${LIBMBEDX509_LIBRARY} ${LIBMBEDCRYPTO_LIBRARY}) + + include(FindPackageHandleStandardArgs) + find_package_handle_standard_args(LibMbedTLS DEFAULT_MSG LIBMBEDTLS_LIBRARIES LIBMBEDTLS_INCLUDE_DIRS) + + # show the LIBMBEDTLS_INCLUDE_DIRS and LIBMBEDTLS_LIBRARIES variables only in the advanced view + mark_as_advanced(LIBMBEDTLS_INCLUDE_DIRS LIBMBEDTLS_LIBRARIES) +endif() From 6d3cf696c963288836c4b361097b1e646dad9bd5 Mon Sep 17 00:00:00 2001 From: roman Date: Thu, 4 Apr 2024 09:25:30 +0200 Subject: [PATCH 02/54] cmake UPDATE compile using libmedtls --- CMakeLists.txt | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 419cdbfd..9ba24aaf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -124,7 +124,8 @@ if(ENABLE_SSH_TLS) src/session_server_tls.c src/server_config_util_tls.c src/server_config_ks.c - src/server_config_ts.c) + src/server_config_ts.c + src/session_mbedtls.c) set(SSH_TLS_MACRO "#ifndef NC_ENABLED_SSH_TLS\n#define NC_ENABLED_SSH_TLS\n#endif") endif() @@ -231,10 +232,20 @@ check_include_file("shadow.h" HAVE_SHADOW) check_include_file("termios.h" HAVE_TERMIOS) if(ENABLE_SSH_TLS) - # dependencies - openssl - find_package(OpenSSL 3.0.0 REQUIRED) - target_link_libraries(netconf2 ${OPENSSL_LIBRARIES}) - include_directories(${OPENSSL_INCLUDE_DIR}) + find_package(LibMbedTLS 3.5.0) # TODO + if (LIBMBEDTLS_FOUND) + set(HAVE_LIBMBEDTLS TRUE) + target_link_libraries(netconf2 ${LIBMBEDTLS_LIBRARIES}) + list(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBMBEDTLS_LIBRARIES}) + include_directories(${LIBMBEDTLS_INCLUDE_DIRS}) + #target_sources(netconf2 PRIVATE src/session_mbedtls.c) + else() + # dependencies - openssl + find_package(OpenSSL 3.0.0 REQUIRED) + target_link_libraries(netconf2 ${OPENSSL_LIBRARIES}) + include_directories(${OPENSSL_INCLUDE_DIR}) + target_sources(netconf2 PRIVATE src/session_openssl.c) + endif() # dependencies - libssh find_package(LibSSH 0.9.5 REQUIRED) From 57ac6897da9cd5e9f91bd862c1fbf28b7bef737c Mon Sep 17 00:00:00 2001 From: roman Date: Thu, 4 Apr 2024 09:26:18 +0200 Subject: [PATCH 03/54] session mbedtls UPDATE add mbedtls wrapper --- src/session_mbedtls.c | 1567 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1567 insertions(+) create mode 100644 src/session_mbedtls.c diff --git a/src/session_mbedtls.c b/src/session_mbedtls.c new file mode 100644 index 00000000..2a86e68d --- /dev/null +++ b/src/session_mbedtls.c @@ -0,0 +1,1567 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "compat.h" +#include "config.h" +#include "log_p.h" +#include "session.h" +#include "session_p.h" +#include "session_wrapper.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +extern struct nc_server_opts server_opts; + +void * +nc_tls_session_new_wrap(void *tls_cfg) +{ + int rc; + mbedtls_ssl_context *session; + + session = malloc(sizeof *session); + NC_CHECK_ERRMEM_RET(!session, NULL); + + mbedtls_ssl_init(session); + + rc = mbedtls_ssl_setup(session, tls_cfg); + if (rc) { + ERR(NULL, "Setting up TLS context failed (%s).", mbedtls_high_level_strerr(rc)); + mbedtls_ssl_free(session); + free(session); + return NULL; + } + + return session; +} + +void +nc_tls_session_destroy_wrap(void *tls_session) +{ + mbedtls_ssl_free(tls_session); + free(tls_session); +} + +void * +nc_server_tls_config_new_wrap() +{ + mbedtls_ssl_config *tls_cfg; + + tls_cfg = malloc(sizeof *tls_cfg); + NC_CHECK_ERRMEM_RET(!tls_cfg, NULL); + + mbedtls_ssl_config_init(tls_cfg); + return tls_cfg; +} + +void * +nc_client_tls_config_new_wrap() +{ + mbedtls_ssl_config *tls_cfg; + + tls_cfg = malloc(sizeof *tls_cfg); + NC_CHECK_ERRMEM_RET(!tls_cfg, NULL); + + mbedtls_ssl_config_init(tls_cfg); + return tls_cfg; +} + +void +nc_tls_config_destroy_wrap(void *tls_cfg) +{ + if (!tls_cfg) { + return; + } + + mbedtls_ssl_config_free(tls_cfg); + free(tls_cfg); +} + +void * +nc_tls_cert_new_wrap() +{ + mbedtls_x509_crt *cert; + + cert = malloc(sizeof *cert); + NC_CHECK_ERRMEM_RET(!cert, NULL); + + mbedtls_x509_crt_init(cert); + return cert; +} + +void +nc_tls_cert_destroy_wrap(void *cert) +{ + mbedtls_x509_crt_free(cert); + free(cert); +} + +void * +nc_tls_privkey_new_wrap() +{ + mbedtls_pk_context *pkey; + + pkey = malloc(sizeof *pkey); + NC_CHECK_ERRMEM_RET(!pkey, NULL); + + mbedtls_pk_init(pkey); + return pkey; +} + +void +nc_tls_privkey_destroy_wrap(void *pkey) +{ + mbedtls_pk_free(pkey); + free(pkey); +} + +void * +nc_tls_cert_store_new_wrap() +{ + return nc_tls_cert_new_wrap(); +} + +void +nc_tls_cert_store_destroy_wrap(void *cert_store) +{ + nc_tls_cert_destroy_wrap(cert_store); +} + +void * +nc_tls_crl_store_new_wrap() +{ + mbedtls_x509_crl *crl; + + crl = malloc(sizeof *crl); + NC_CHECK_ERRMEM_RET(!crl, NULL); + + mbedtls_x509_crl_init(crl); + return crl; +} + +void +nc_tls_crl_store_destroy_wrap(void *crl) +{ + mbedtls_x509_crl_free(crl); + free(crl); +} + +static int +nc_tls_rng_new(mbedtls_ctr_drbg_context **ctr_drbg, mbedtls_entropy_context **entropy) +{ + *ctr_drbg = NULL; + *entropy = NULL; + + *entropy = malloc(sizeof **entropy); + NC_CHECK_ERRMEM_GOTO(!*entropy, , fail); + *ctr_drbg = malloc(sizeof **ctr_drbg); + NC_CHECK_ERRMEM_GOTO(!*ctr_drbg, , fail); + + mbedtls_entropy_init(*entropy); + mbedtls_ctr_drbg_init(*ctr_drbg); + + if (mbedtls_ctr_drbg_seed(*ctr_drbg, mbedtls_entropy_func, *entropy, NULL, 0)) { + ERR(NULL, "Seeding ctr_drbg failed."); + goto fail; + } + + return 0; + +fail: + mbedtls_ctr_drbg_free(*ctr_drbg); + free(*ctr_drbg); + mbedtls_entropy_free(*entropy); + free(*entropy); + *ctr_drbg = NULL; + *entropy = NULL; + return 1; +} + +static void +nc_tls_rng_destroy(mbedtls_ctr_drbg_context *ctr_drbg, mbedtls_entropy_context *entropy) +{ + mbedtls_ctr_drbg_free(ctr_drbg); + free(ctr_drbg); + mbedtls_entropy_free(entropy); + free(entropy); +} + +void +nc_tls_set_authmode_wrap(void *tls_cfg) +{ + mbedtls_ssl_conf_authmode(tls_cfg, MBEDTLS_SSL_VERIFY_REQUIRED); +} + +int +nc_server_tls_set_config_defaults_wrap(void *tls_cfg) +{ + int rc; + + rc = mbedtls_ssl_config_defaults(tls_cfg, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); + if (rc) { + ERR(NULL, "Setting default TLS config failed (%s).", mbedtls_high_level_strerr(rc)); + return 1; + } + + return 0; +} + +void * +nc_tls_pem_to_cert_wrap(const char *cert_data) +{ + int rc; + mbedtls_x509_crt *cert; + + cert = nc_tls_cert_new_wrap(); + if (!cert) { + return NULL; + } + + rc = mbedtls_x509_crt_parse(cert, (const unsigned char *)cert_data, strlen(cert_data) + 1); + if (rc) { + ERR(NULL, "Parsing certificate data failed (%s).", mbedtls_high_level_strerr(rc)); + nc_tls_cert_destroy_wrap(cert); + return NULL; + } + + return cert; +} + +void * +nc_tls_base64_to_cert_wrap(const char *cert_data) +{ + int rc; + mbedtls_x509_crt *cert; + char *pem = NULL; + + cert = nc_tls_cert_new_wrap(); + if (!cert) { + return NULL; + } + + rc = asprintf(&pem, "-----BEGIN CERTIFICATE-----\n%s\n-----END CERTIFICATE-----", cert_data); + if (rc == -1) { + ERRMEM; + nc_tls_cert_destroy_wrap(cert); + return NULL; + } + + rc = mbedtls_x509_crt_parse(cert, (const unsigned char *)pem, strlen(pem) + 1); + if (rc) { + ERR(NULL, "Parsing certificate data failed (%s).", mbedtls_high_level_strerr(rc)); + nc_tls_cert_destroy_wrap(cert); + return NULL; + } + + return cert; +} + +int +nc_tls_pem_to_cert_add_to_store_wrap(const char *cert_data, void *cert_store) +{ + int rc; + + rc = mbedtls_x509_crt_parse(cert_store, (const unsigned char *)cert_data, strlen(cert_data) + 1); + if (rc) { + ERR(NULL, "Parsing certificate data failed (%s).", mbedtls_high_level_strerr(rc)); + return 1; + } + + return 0; +} + +void * +nc_tls_pem_to_privkey_wrap(const char *privkey_data) +{ + int rc; + mbedtls_pk_context *pkey; + mbedtls_ctr_drbg_context *ctr_drbg; + mbedtls_entropy_context *entropy; + + if (nc_tls_rng_new(&ctr_drbg, &entropy)) { + return NULL; + } + + pkey = nc_tls_privkey_new_wrap(); + if (!pkey) { + nc_tls_rng_destroy(ctr_drbg, entropy); + return NULL; + } + + rc = mbedtls_pk_parse_key(pkey, (const unsigned char *)privkey_data, strlen(privkey_data) + 1, NULL, 0, mbedtls_ctr_drbg_random, ctr_drbg); + nc_tls_rng_destroy(ctr_drbg, entropy); + if (rc) { + ERR(NULL, "Parsing private key data failed (%s).", mbedtls_high_level_strerr(rc)); + nc_tls_privkey_destroy_wrap(pkey); + return NULL; + } + return pkey; +} + +int +nc_tls_load_cert_private_key_wrap(void *tls_cfg, void *cert, void *pkey) +{ + int rc; + + rc = mbedtls_ssl_conf_own_cert(tls_cfg, cert, pkey); + if (rc) { + ERR(NULL, "Loading the server certificate or private key failed (%s).", mbedtls_high_level_strerr(rc)); + return 1; + } + + return 0; +} + +int +nc_server_tls_crl_path_wrap(const char *crl_path, void *cert_store, void *crl_store) +{ + int rc; + + (void) cert_store; + + rc = mbedtls_x509_crl_parse_file(crl_store, crl_path); + if (rc) { + ERR(NULL, "Error adding CRL to store (%s)", mbedtls_high_level_strerr(rc)); + return 1; + } + + return 0; +} + +int +nc_server_tls_add_crl_to_store_wrap(const unsigned char *crl_data, size_t size, void *cert_store, void *crl_store) +{ + int rc; + + (void) cert_store; + + /* try DER first */ + rc = mbedtls_x509_crl_parse_der(crl_store, crl_data, size); + if (!rc) { + /* success, it was DER */ + return 0; + } + + /* DER failed, try PEM */ + rc = mbedtls_x509_crl_parse(crl_store, crl_data, size); + if (!rc) { + /* success, it was PEM */ + return 0; + } + + /* failed to parse it */ + ERR(NULL, "Reading downloaded CRL failed."); + return 1; +} + +void +nc_server_tls_set_certs_wrap(void *tls_cfg, void *cert_store, void *crl_store) +{ + mbedtls_ssl_conf_ca_chain(tls_cfg, cert_store, crl_store); +} + +int +nc_server_tls_set_tls_versions_wrap(void *tls_cfg, unsigned int tls_versions) +{ + if ((tls_versions & NC_TLS_VERSION_10) || ((tls_versions & NC_TLS_VERSION_11))) { + /* skip TLS versions 1.0 and 1.1 */ + WRN(NULL, "mbedTLS does not support TLS1.0 and TLS1.1"); + } + + /* first set the minimum version */ + if (tls_versions & NC_TLS_VERSION_12) { + mbedtls_ssl_conf_min_tls_version(tls_cfg, MBEDTLS_SSL_VERSION_TLS1_2); + } else if (tls_versions & NC_TLS_VERSION_13) { + mbedtls_ssl_conf_min_tls_version(tls_cfg, MBEDTLS_SSL_VERSION_TLS1_3); + } + + /* then set the maximum version */ + if (tls_versions & NC_TLS_VERSION_13) { + mbedtls_ssl_conf_max_tls_version(tls_cfg, MBEDTLS_SSL_VERSION_TLS1_3); + } else if (tls_versions & NC_TLS_VERSION_12) { + mbedtls_ssl_conf_max_tls_version(tls_cfg, MBEDTLS_SSL_VERSION_TLS1_2); + } + + return 0; +} + +static int +nc_server_tls_verify_cb(void *cb_data, mbedtls_x509_crt *cert, int depth, uint32_t *flags) +{ + int ret = 0; + struct nc_tls_verify_cb_data *data = cb_data; + size_t buf_len = 256; + char err_buf[buf_len]; + + if (!*flags) { + /* in-built verification was successful */ + ret = nc_server_tls_verify_cert(cert, depth, 0, data); + } else { + /* in-built verification was failed, either check if peer cert matches any configured cert, or just + * return success and wait until we reach depth 0 + */ + if ((depth == 0) && (*flags == MBEDTLS_X509_BADCERT_NOT_TRUSTED)) { + /* not trusted self-signed peer certificate */ + ret = nc_server_tls_verify_cert(cert, depth, 1, data); + if (!ret) { + *flags &= ~MBEDTLS_X509_BADCERT_NOT_TRUSTED; + } + } else { + buf_len = mbedtls_x509_crt_verify_info(err_buf, buf_len, "", *flags); + if (buf_len > 0) { + /* strip the NL and print it */ + err_buf[buf_len - 1] = '\0'; + ERR(data->session, "Cert verify: fail (%s).", err_buf); + } + ret = 1; + } + } + + if (ret == -1) { + /* fatal error */ + return MBEDTLS_ERR_X509_ALLOC_FAILED; + } else if (!ret) { + /* success */ + return 0; + } else { + if (depth > 0) { + /* chain verify failed, but peer cert can still match */ + return 0; + } else { + /* peer cert did not match */ + return 1; + } + } +} + +void +nc_server_tls_set_verify_cb_wrap(void *tls_session, struct nc_tls_verify_cb_data *cb_data) +{ + mbedtls_ssl_set_verify(tls_session, nc_server_tls_verify_cb, cb_data); +} + +static char * +nc_server_tls_dn2str(const mbedtls_x509_name *dn) +{ + char *str; + size_t len = 64; + int r; + void *ptr; + + str = malloc(len); + NC_CHECK_ERRMEM_RET(!str, NULL); + + while ((r = mbedtls_x509_dn_gets(str, len, dn)) == MBEDTLS_ERR_X509_BUFFER_TOO_SMALL) { + len <<= 1; + ptr = realloc(str, len); + if (!ptr) { + ERRMEM; + free(str); + return NULL; + } + str = ptr; + } + if (r < 1) { + free(str); + ERRMEM; + return NULL; + } + return str; +} + +char * +nc_server_tls_get_subject_wrap(void *cert) +{ + return nc_server_tls_dn2str(&(((mbedtls_x509_crt *)cert)->subject)); +} + +char * +nc_server_tls_get_issuer_wrap(void *cert) +{ + return nc_server_tls_dn2str(&(((mbedtls_x509_crt *)cert)->issuer)); +} + +int +nc_server_tls_get_username_from_cert_wrap(void *cert, NC_TLS_CTN_MAPTYPE map_type, char **username) +{ + int rc; + char *subject, *common_name; + mbedtls_x509_subject_alternative_name san = {0}; + mbedtls_x509_sequence *cur = NULL; + const mbedtls_x509_buf *ip; + mbedtls_x509_crt *peer_cert = cert; + + *username = NULL; + if (map_type == NC_TLS_CTN_COMMON_NAME) { + subject = nc_server_tls_get_subject_wrap(peer_cert); + NC_CHECK_ERRMEM_RET(!subject, -1); + common_name = strstr(subject, "CN="); + if (!common_name) { + WRN(NULL, "Certificate does not include the commonName field."); + free(subject); + return 1; + } + common_name += 3; + if (strchr(common_name, ',')) { + *strchr(common_name, ',') = '\0'; + } + *username = strdup(common_name); + free(subject); + NC_CHECK_ERRMEM_RET(!*username, -1); + } else { + /* retrieve subjectAltName's rfc822Name (email), dNSName and iPAddress values */ + cur = &peer_cert->subject_alt_names; + while (cur) { + rc = mbedtls_x509_parse_subject_alt_name(&cur->buf, &san); + if (rc && (rc != MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE)) { + ERR(NULL, "Getting SANs failed."); + return 1; + } + + if (((map_type == NC_TLS_CTN_SAN_ANY) || (map_type == NC_TLS_CTN_SAN_DNS_NAME)) && + (san.type == MBEDTLS_X509_SAN_DNS_NAME)) { + *username = strndup((const char *)san.san.unstructured_name.p, san.san.unstructured_name.len); // TODO: tolower()? + NC_CHECK_ERRMEM_RET(!*username, -1); + break; + } + + if (((map_type == NC_TLS_CTN_SAN_ANY) || (map_type == NC_TLS_CTN_SAN_RFC822_NAME)) && + (san.type == MBEDTLS_X509_SAN_RFC822_NAME)) { + *username = strndup((const char *)san.san.unstructured_name.p, san.san.unstructured_name.len); + NC_CHECK_ERRMEM_RET(!*username, -1); + break; + } + + /* iPAddress */ + if (((map_type == NC_TLS_CTN_SAN_ANY) || (map_type == NC_TLS_CTN_SAN_IP_ADDRESS)) && + (san.type == MBEDTLS_X509_SAN_IP_ADDRESS)) { + ip = &san.san.unstructured_name; + if (ip->len == 4) { + if (asprintf(username, "%d.%d.%d.%d", ip->p[0], ip->p[1], ip->p[2], ip->p[3]) == -1) { + ERRMEM; + return -1; + } + break; + } else if (ip->len == 16) { + if (asprintf(username, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + ip->p[0], ip->p[1], ip->p[2], ip->p[3], ip->p[4], ip->p[5], + ip->p[6], ip->p[7], ip->p[8], ip->p[9], ip->p[10], ip->p[11], + ip->p[12], ip->p[13], ip->p[14], ip->p[15]) == -1) { + ERRMEM; + return -1; + } + break; + } else { + WRN(NULL, "SAN IP address in an unknown format (length is %d).", ip->len); + } + } + + cur = cur->next; + } + + if (!*username) { + switch (map_type) { + case NC_TLS_CTN_SAN_RFC822_NAME: + WRN(NULL, "Certificate does not include the SAN rfc822Name field."); + break; + case NC_TLS_CTN_SAN_DNS_NAME: + WRN(NULL, "Certificate does not include the SAN dNSName field."); + break; + case NC_TLS_CTN_SAN_IP_ADDRESS: + WRN(NULL, "Certificate does not include the SAN iPAddress field."); + break; + case NC_TLS_CTN_SAN_ANY: + WRN(NULL, "Certificate does not include any relevant SAN fields."); + break; + default: + break; + } + return 1; + } + } + + return 0; +} + +int +nc_server_tls_certs_match_wrap(void *cert1, void *cert2) +{ + mbedtls_x509_crt *c1 = cert1; + mbedtls_x509_crt *c2 = cert2; + + if (!c1 || !c2) { + return 0; + } + + /* compare raw DER encoded data */ + if (!c1->raw.p || !c2->raw.p || (c1->raw.len != c2->raw.len) || + memcmp(c1->raw.p, c2->raw.p, c1->raw.len)) { + return 0; + } + + return 1; +} + +int +nc_server_tls_md5_wrap(void *cert, unsigned char *buf) +{ + int rc; + mbedtls_x509_crt *c = cert; + + rc = mbedtls_md5(c->raw.p, c->raw.len, buf); + if (rc) { + ERR(NULL, "Calculating MD5 digest failed (%s).", mbedtls_high_level_strerr(rc)); + return 1; + } + + return 0; +} + +int +nc_server_tls_sha1_wrap(void *cert, unsigned char *buf) +{ + int rc; + mbedtls_x509_crt *c = cert; + + rc = mbedtls_sha1(c->raw.p, c->raw.len, buf); + if (rc) { + ERR(NULL, "Calculating SHA-1 digest failed (%s).", mbedtls_high_level_strerr(rc)); + return 1; + } + + return 0; +} + +int +nc_server_tls_sha224_wrap(void *cert, unsigned char *buf) +{ + int rc; + mbedtls_x509_crt *c = cert; + + rc = mbedtls_sha256(c->raw.p, c->raw.len, buf, 1); + if (rc) { + ERR(NULL, "Calculating SHA-224 digest failed (%s).", mbedtls_high_level_strerr(rc)); + return 1; + } + + return 0; +} + +int +nc_server_tls_sha256_wrap(void *cert, unsigned char *buf) +{ + int rc; + mbedtls_x509_crt *c = cert; + + rc = mbedtls_sha256(c->raw.p, c->raw.len, buf, 0); + if (rc) { + ERR(NULL, "Calculating SHA-256 digest failed (%s).", mbedtls_high_level_strerr(rc)); + return 1; + } + + return 0; +} + +int +nc_server_tls_sha384_wrap(void *cert, unsigned char *buf) +{ + int rc; + mbedtls_x509_crt *c = cert; + + rc = mbedtls_sha512(c->raw.p, c->raw.len, buf, 1); + if (rc) { + ERR(NULL, "Calculating SHA-384 digest failed (%s).", mbedtls_high_level_strerr(rc)); + return 1; + } + + return 0; +} + +int +nc_server_tls_sha512_wrap(void *cert, unsigned char *buf) +{ + int rc; + mbedtls_x509_crt *c = cert; + + rc = mbedtls_sha512(c->raw.p, c->raw.len, buf, 0); + if (rc) { + ERR(NULL, "Calculating SHA-512 digest failed (%s).", mbedtls_high_level_strerr(rc)); + return 1; + } + + return 0; +} + +void +nc_server_tls_set_fd_wrap(void *tls_session, int UNUSED(sock), struct nc_tls_ctx *tls_ctx) +{ + mbedtls_ssl_set_bio(tls_session, tls_ctx->sock, mbedtls_net_send, mbedtls_net_recv, NULL); +} + +int +nc_server_tls_handshake_step_wrap(void *tls_session) +{ + int rc = 0; + + rc = mbedtls_ssl_handshake(tls_session); + if (!rc) { + return 1; + } else if (rc == MBEDTLS_ERR_SSL_WANT_READ || rc == MBEDTLS_ERR_SSL_WANT_WRITE) { + return 0; + } else { + return -1; + } +} + +int +nc_server_tls_fill_config_wrap(void *tls_cfg, void *srv_cert, void *srv_pkey, void *cert_store, void *crl_store, struct nc_tls_ctx *tls_ctx) +{ + int rc; + + rc = mbedtls_ssl_config_defaults(tls_cfg, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); + if (rc) { + ERR(NULL, "Setting default TLS config failed (%s).", mbedtls_high_level_strerr(rc)); + return 1; + } + + mbedtls_ssl_conf_rng(tls_cfg, mbedtls_ctr_drbg_random, tls_ctx->ctr_drbg); + mbedtls_ssl_conf_authmode(tls_cfg, MBEDTLS_SSL_VERIFY_REQUIRED); + + mbedtls_ssl_conf_own_cert(tls_cfg, srv_cert, srv_pkey); + mbedtls_ssl_conf_ca_chain(tls_cfg, cert_store, crl_store); + + tls_ctx->cert = srv_cert; + tls_ctx->pkey = srv_pkey; + tls_ctx->cert_store = cert_store; + tls_ctx->crl_store = crl_store; + return 0; +} + +int +nc_server_tls_setup_config_fill_ctx_wrap(void *tls_cfg, struct nc_tls_ctx *tls_ctx, void *srv_cert, void *srv_pkey, void *cert_store, void *crl_store, int sock) +{ + int rc; + + rc = mbedtls_ssl_config_defaults(tls_cfg, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); + if (rc) { + ERR(NULL, "Setting default TLS config failed (%s).", mbedtls_high_level_strerr(rc)); + return 1; + } + + rc = nc_tls_rng_new(&tls_ctx->ctr_drbg, &tls_ctx->entropy); + if (rc) { + return 1; + } + + mbedtls_ssl_conf_rng(tls_cfg, mbedtls_ctr_drbg_random, tls_ctx->ctr_drbg); + mbedtls_ssl_conf_authmode(tls_cfg, MBEDTLS_SSL_VERIFY_REQUIRED); + + mbedtls_ssl_conf_own_cert(tls_cfg, srv_cert, srv_pkey); + mbedtls_ssl_conf_ca_chain(tls_cfg, cert_store, crl_store); + + tls_ctx->cert = srv_cert; + tls_ctx->pkey = srv_pkey; + tls_ctx->cert_store = cert_store; + tls_ctx->crl_store = crl_store; + tls_ctx->sock = malloc(sizeof *tls_ctx->sock); + NC_CHECK_ERRMEM_RET(!tls_ctx->sock, 1); + *tls_ctx->sock = sock; + return 0; +} + +void +nc_tls_ctx_destroy_wrap(struct nc_tls_ctx *tls_ctx) +{ + if (tls_ctx->ctr_drbg && tls_ctx->entropy) { + nc_tls_rng_destroy(tls_ctx->ctr_drbg, tls_ctx->entropy); + } + nc_tls_cert_destroy_wrap(tls_ctx->cert); + nc_tls_privkey_destroy_wrap(tls_ctx->pkey); + nc_tls_cert_store_destroy_wrap(tls_ctx->cert_store); + nc_tls_crl_store_destroy_wrap(tls_ctx->crl_store); + free(tls_ctx->sock); +} + +static mbedtls_pk_context * +nc_tls_file_to_privkey(const char *privkey_path) +{ + int rc; + mbedtls_pk_context *pkey; + mbedtls_ctr_drbg_context *ctr_drbg; + mbedtls_entropy_context *entropy; + + if (nc_tls_rng_new(&ctr_drbg, &entropy)) { + return NULL; + } + + pkey = nc_tls_privkey_new_wrap(); + if (!pkey) { + nc_tls_rng_destroy(ctr_drbg, entropy); + return NULL; + } + + rc = mbedtls_pk_parse_keyfile(pkey, privkey_path, NULL, mbedtls_ctr_drbg_random, ctr_drbg); + nc_tls_rng_destroy(ctr_drbg, entropy); + if (rc) { + ERR(NULL, "Parsing private key data failed (%s).", mbedtls_high_level_strerr(rc)); + nc_tls_privkey_destroy_wrap(pkey); + return NULL; + } + return pkey; +} + +static int +read_pem_file(const char *cert_path, char **out) +{ + int ret = 0; + FILE *f; + char *buf = NULL; + size_t size, read; + + f = fopen(cert_path, "r"); + if (!f) { + ERR(NULL, "Unable to open file \"%s\".", cert_path); + ret = 1; + goto cleanup; + } + + fseek(f, 0, SEEK_END); + size = ftell(f); + fseek(f, 0, SEEK_SET); + + buf = malloc(size + 1); + NC_CHECK_ERRMEM_GOTO(!buf, ret = 1, cleanup); + + read = fread(buf, 1, size, f); + if (size != read) { + ERR(NULL, "Error reading from file \"%s\".", cert_path); + ret = 1; + goto cleanup; + } + + buf[size] = '\0'; + *out = buf; + +cleanup: + if (f) { + fclose(f); + } + return ret; +} + +int +nc_client_tls_load_cert_key_wrap(const char *cert_path, const char *key_path, void **cert, void **pkey) +{ + int ret = 0; + mbedtls_x509_crt *c; + mbedtls_pk_context *pk; + char *buf = NULL, *ptr; + const char *cert_footer = "-----END CERTIFICATE-----\n"; + + c = nc_tls_cert_new_wrap(); + if (!c) { + return 1; + } + + ret = mbedtls_x509_crt_parse_file(c, cert_path); + if (ret) { + ERR(NULL, "Parsing certificate from file \"%s\" failed (%s).", cert_path, mbedtls_high_level_strerr(ret)); + goto cleanup; + } + + if (key_path) { + pk = nc_tls_file_to_privkey(key_path); + if (!pk) { + ret = 1; + goto cleanup; + } + } else { + ret = read_pem_file(cert_path, &buf); + if (ret) { + goto cleanup; + } + + ptr = strstr(buf, cert_footer); + if (!ptr) { + ERR(NULL, "Invalid certificate file."); + ret = 1; + goto cleanup; + } + + ptr += strlen(cert_footer); + if (*ptr == '\0') { + ERR(NULL, "File \"%s\" doesn't contain a private key."); + ret = 1; + goto cleanup; + } + + pk = nc_tls_pem_to_privkey_wrap(ptr); + if (!pk) { + ret = 1; + goto cleanup; + } + } + + *cert = c; + *pkey = pk; + +cleanup: + return ret; +} + +int +nc_client_tls_load_trusted_certs_wrap(void *cert_store, const char *file_path, const char *dir_path) +{ + int rc; + + if (file_path && ((rc = mbedtls_x509_crt_parse_file(cert_store, file_path)) < 0)) { + ERR(NULL, "Loading CA certificate from file \"%s\" failed (%s).", file_path, mbedtls_high_level_strerr(rc)); + return 1; + } + + if (dir_path && ((rc = mbedtls_x509_crt_parse_path(cert_store, dir_path)) < 0)) { + ERR(NULL, "Loading CA certificate from directory \"%s\" failed (%s).", dir_path, mbedtls_low_level_strerr(rc)); + return 1; + } + + return 0; +} + +int +nc_client_tls_load_crl_wrap(void *UNUSED(cert_store), void *crl_store, const char *file_path, const char *dir_path) +{ + int rc; + DIR *dir; + struct dirent *entry; + struct stat st = {0}; + char *path; + + if (file_path && (rc = mbedtls_x509_crl_parse_file(crl_store, file_path))) { + ERR(NULL, "Loading CRL from file \"%s\" failed (%s).", file_path, mbedtls_high_level_strerr(rc)); + return 1; + } + + if (dir_path) { + /* parse the CRLs in the directory one by one */ + dir = opendir(dir_path); + if (!dir) { + ERR(NULL, "Failed to open directory \"%s\" (%s).", dir_path, strerror(errno)); + } + + while ((entry = readdir(dir))) { + if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) { + /* skip current and parent directory */ + continue; + } + + if (asprintf(&path, "%s/%s", dir_path, entry->d_name) == -1) { + ERRMEM; + closedir(dir); + return 1; + } + + if (stat(path, &st) == -1) { + if (errno == ENOENT) { + /* broken symbolic link, ignore */ + free(path); + continue; + } else { + ERR(NULL, "Failed to get information about \"%s\" (%s).", path, strerror(errno)); + free(path); + closedir(dir); + return 1; + } + } + + if (!S_ISREG(st.st_mode)) { + /* not a regular file, ignore */ + free(path); + continue; + } + + rc = mbedtls_x509_crl_parse_file(crl_store, path); + if (rc) { + ERR(NULL, "Loading CRL from file \"%s\" failed (%s).", path, mbedtls_high_level_strerr(rc)); + } + + free(path); + } + } + + return 0; +} + +int +nc_client_tls_set_hostname_wrap(void *tls_session, const char *hostname) +{ + int rc; + + rc = mbedtls_ssl_set_hostname(tls_session, hostname); + if (rc) { + ERR(NULL, "Setting hostname failed (%s).", mbedtls_high_level_strerr(rc)); + return 1; + } + + return 0; +} + +int +nc_tls_setup_config_wrap(void *tls_cfg, int side, struct nc_tls_ctx *tls_ctx) +{ + int rc; + + /* set default config data */ + if (side == NC_SERVER) { + rc = mbedtls_ssl_config_defaults(tls_cfg, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); + } else { + rc = mbedtls_ssl_config_defaults(tls_cfg, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); + } + if (rc) { + ERR(NULL, "Setting default TLS config failed (%s).", mbedtls_high_level_strerr(rc)); + return 1; + } + + /* set config's rng */ + mbedtls_ssl_conf_rng(tls_cfg, mbedtls_ctr_drbg_random, tls_ctx->ctr_drbg); + /* set config's authmode */ + mbedtls_ssl_conf_authmode(tls_cfg, MBEDTLS_SSL_VERIFY_REQUIRED); + /* set config's cert and key */ + mbedtls_ssl_conf_own_cert(tls_cfg, tls_ctx->cert, tls_ctx->pkey); + /* set config's CA and CRL cert store */ + mbedtls_ssl_conf_ca_chain(tls_cfg, tls_ctx->cert_store, tls_ctx->crl_store); + return 0; +} + +int +nc_tls_init_ctx_wrap(struct nc_tls_ctx *tls_ctx, int sock, void *cli_cert, void *cli_pkey, void *cert_store, void *crl_store) +{ + /* setup rng */ + if (nc_tls_rng_new(&tls_ctx->ctr_drbg, &tls_ctx->entropy)) { + return 1; + } + + /* fill the context */ + tls_ctx->sock = malloc(sizeof *tls_ctx->sock); + NC_CHECK_ERRMEM_RET(!tls_ctx->sock, 1); + *tls_ctx->sock = sock; + tls_ctx->cert = cli_cert; + tls_ctx->pkey = cli_pkey; + tls_ctx->cert_store = cert_store; + tls_ctx->crl_store = crl_store; + return 0; +} + +int +nc_client_tls_handshake_step_wrap(void *tls_session) +{ + int rc = 0; + + rc = mbedtls_ssl_handshake(tls_session); + if (!rc) { + return 1; + } else if (rc == MBEDTLS_ERR_SSL_WANT_READ || rc == MBEDTLS_ERR_SSL_WANT_WRITE) { + return 0; + } else { + return rc; + } +} + +uint32_t +nc_tls_get_verify_result_wrap(void *tls_session) +{ + return mbedtls_ssl_get_verify_result(tls_session); +} + +const char * +nc_tls_verify_error_string_wrap(uint32_t err_code) +{ + const char *err; + + return (err = mbedtls_low_level_strerr(err_code)) ? err : ""; +} + +void +nc_tls_print_error_string_wrap(int connect_ret, const char *peername, void *UNUSED(tls_session)) +{ + ERR(NULL, "TLS connection to \"%s\" failed (%s).", peername, mbedtls_high_level_strerr(connect_ret)); +} + +void +nc_server_tls_print_accept_error_wrap(int UNUSED(accept_ret), void *UNUSED(tls_session)) +{ + ERR(NULL, "TLS accept failed."); +} + +void +nc_tls_session_new_cleanup_wrap(void *tls_cfg, void *cli_cert, void *cli_pkey, void *cert_store, void *crl_store) +{ + mbedtls_ssl_config_free(tls_cfg); + free(tls_cfg); + mbedtls_x509_crt_free(cli_cert); + free(cli_cert); + mbedtls_pk_free(cli_pkey); + free(cli_pkey); + mbedtls_x509_crt_free(cert_store); + free(cert_store); + mbedtls_x509_crl_free(crl_store); + free(crl_store); +} + +int +nc_der_to_pubkey_wrap(const unsigned char *der, long len) +{ + int ret; + mbedtls_pk_context *pkey; + + pkey = malloc(sizeof *pkey); + NC_CHECK_ERRMEM_RET(!pkey, -1); + mbedtls_pk_init(pkey); + + ret = mbedtls_pk_parse_public_key(pkey, (const unsigned char *)der, len); + nc_tls_privkey_destroy_wrap(pkey); + if (!ret) { + /* success */ + return 0; + } else { + /* fail */ + return 1; + } +} + +int +nc_base64_decode_wrap(const char *base64, char **bin) +{ + size_t size; + int ret; + + ret = mbedtls_base64_decode(NULL, 0, &size, (const unsigned char *)base64, strlen(base64)); + if (ret != MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) { + return -1; + } + + *bin = malloc(size); + NC_CHECK_ERRMEM_RET(!*bin, -1); + + ret = mbedtls_base64_decode((unsigned char *)*bin, size, &size, (const unsigned char *)base64, strlen(base64)); + if (ret) { + free(*bin); + *bin = NULL; + return -1; + } + + return size; +} + +int +nc_base64_encode_wrap(const unsigned char *bin, size_t len, char **base64) +{ + size_t size; + int ret; + + ret = mbedtls_base64_encode(NULL, 0, &size, bin, len); + if (ret != MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) { + return -1; + } + + *base64 = malloc(size); + NC_CHECK_ERRMEM_RET(!*base64, -1); + + ret = mbedtls_base64_encode((unsigned char *)*base64, size, &size, bin, len); + if (ret) { + free(*base64); + *base64 = NULL; + return -1; + } + + return 0; +} + +int +nc_tls_read_wrap(struct nc_session *session, unsigned char *buf, size_t size) +{ + int rc; + mbedtls_ssl_context *tls_session = session->ti.tls.session; + + rc = mbedtls_ssl_read(tls_session, buf, size); + if (rc <= 0) { + switch (rc) { + case MBEDTLS_ERR_SSL_WANT_READ: + case MBEDTLS_ERR_SSL_WANT_WRITE: + rc = 0; + break; + case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: + ERR(session, "Communication socket unexpectedly closed (MbedTLS)."); + session->status = NC_STATUS_INVALID; + session->term_reason = NC_SESSION_TERM_DROPPED; + rc = -1; + break; + default: + ERR(session, "TLS communication error occurred (%s).", mbedtls_high_level_strerr(rc)); + session->status = NC_STATUS_INVALID; + session->term_reason = NC_SESSION_TERM_OTHER; + rc = -1; + break; + } + } + + return rc; +} + +int +nc_tls_write_wrap(struct nc_session *session, const unsigned char *buf, size_t size) +{ + int rc = 0; + mbedtls_ssl_context *tls_session = session->ti.tls.session; + + rc = mbedtls_ssl_write(tls_session, buf, size); + if (rc < 0) { + switch (rc) { + case MBEDTLS_ERR_SSL_WANT_READ: + case MBEDTLS_ERR_SSL_WANT_WRITE: + rc = 0; + break; + case MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY: + ERR(session, "TLS connection was properly closed."); + rc = -1; + break; + default: + ERR(session, "TLS communication error occurred (%s).", mbedtls_high_level_strerr(rc)); + rc = -1; + break; + } + } + + return rc; +} + +int +nc_tls_have_pending_wrap(void *tls_session) +{ + return mbedtls_ssl_get_bytes_avail(tls_session); +} + +int +nc_tls_get_fd_wrap(const struct nc_session *session) +{ + if (!session->ti.tls.ctx.sock) { + return -1; + } else { + return *session->ti.tls.ctx.sock; + } +} + +void +nc_tls_close_notify_wrap(void *tls_session) +{ + int rc; + + while ((rc = mbedtls_ssl_close_notify(tls_session))) { + if ((rc != MBEDTLS_ERR_SSL_WANT_READ) && (rc != MBEDTLS_ERR_SSL_WANT_WRITE)) { + /* some error occurred */ + return; + } + } +} + +void * +nc_tls_import_key_file_wrap(const char *key_path, FILE *UNUSED(file)) +{ + return nc_tls_file_to_privkey(key_path); +} + +void * +nc_tls_import_cert_file_wrap(const char *cert_path) +{ + int rc; + mbedtls_x509_crt *c; + + c = nc_tls_cert_new_wrap(); + if (!c) { + return NULL; + } + + rc = mbedtls_x509_crt_parse_file(c, cert_path); + if (rc) { + ERR(NULL, "Parsing certificate from file \"%s\" failed (%s).", cert_path, mbedtls_high_level_strerr(rc)); + nc_tls_cert_destroy_wrap(c); + return NULL; + } + + return c; +} + +char * +nc_tls_export_key_wrap(void *pkey) +{ + int rc; + char *pem; + size_t size = 128; + void *tmp; + + pem = malloc(size); + NC_CHECK_ERRMEM_RET(!pem, NULL); + + while ((rc = mbedtls_pk_write_key_pem(pkey, (unsigned char *)pem, size)) == MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) { + size <<= 1; + tmp = realloc(pem, size); + if (!tmp) { + ERRMEM; + free(pem); + return NULL; + } + pem = tmp; + } + if (rc < 0) { + ERR(NULL, "Exporting private key to PEM format failed (%s).", mbedtls_high_level_strerr(rc)); + free(pem); + return NULL; + } + + return pem; +} + +char * +nc_tls_export_cert_wrap(void *cert) +{ + char *b64 = NULL, *pem = NULL; + + if (nc_base64_encode_wrap(((mbedtls_x509_crt *)cert)->raw.p, ((mbedtls_x509_crt *)cert)->raw.len, &b64)) { + goto cleanup; + } + + if (asprintf(&pem, "-----BEGIN CERTIFICATE-----\n%s\n-----END CERTIFICATE-----\n", b64) == -1) { + ERRMEM; + pem = NULL; + goto cleanup; + } + +cleanup: + free(b64); + return pem; +} + +char * +nc_tls_export_pubkey_wrap(void *pkey) +{ + int rc; + char *pem; + size_t size = 128; + void *tmp; + + pem = malloc(size); + NC_CHECK_ERRMEM_RET(!pem, NULL); + + while ((rc = mbedtls_pk_write_pubkey_pem(pkey, (unsigned char *)pem, size)) == MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) { + size <<= 1; + tmp = realloc(pem, size); + if (!tmp) { + ERRMEM; + free(pem); + return NULL; + } + pem = tmp; + } + if (rc < 0) { + ERR(NULL, "Exporting public key to PEM format failed (%s).", mbedtls_high_level_strerr(rc)); + free(pem); + return NULL; + } + + return pem; +} + +int +nc_tls_export_key_der_wrap(void *pkey, unsigned char **der, size_t *size) +{ + int rc; + + *size = mbedtls_pk_get_len(pkey); + *der = malloc(*size); + NC_CHECK_ERRMEM_RET(!*der, 1); + + rc = mbedtls_pk_write_key_der(pkey, *der, *size); + if (rc < 0) { + ERR(NULL, "Exporting private key to DER format failed (%s).", mbedtls_high_level_strerr(rc)); + free(*der); + *der = NULL; + return 1; + } + + return 0; +} + +int +nc_tls_privkey_is_rsa_wrap(void *pkey) +{ + return mbedtls_pk_get_type(pkey) == MBEDTLS_PK_RSA; +} + +int +nc_tls_get_rsa_pubkey_params_wrap(void *pkey, void **e, void **n) +{ + int rc; + mbedtls_mpi *exp = NULL, *mod = NULL; + + exp = malloc(sizeof *exp); + mod = malloc(sizeof *mod); + if (!exp || !mod) { + ERRMEM; + goto fail; + } + mbedtls_mpi_init(exp); + mbedtls_mpi_init(mod); + + if ((rc = mbedtls_rsa_export(mbedtls_pk_rsa(*(mbedtls_pk_context *)pkey), mod, NULL, NULL, NULL, exp))) { + ERR(NULL, "Failed to export RSA public key parameters (%s).", mbedtls_high_level_strerr(rc)); + goto fail; + } + + *e = exp; + *n = mod; + return 0; + +fail: + mbedtls_mpi_free(exp); + mbedtls_mpi_free(mod); + free(exp); + free(mod); + return 1; +} + +int +nc_tls_privkey_is_ec_wrap(void *pkey) +{ + return mbedtls_pk_get_type(pkey) == MBEDTLS_PK_ECKEY; +} + +char * +nc_tls_get_ec_group_wrap(void *pkey) +{ + const mbedtls_ecp_curve_info *curve_info; + mbedtls_ecp_group_id group_id; + mbedtls_ecp_keypair *ec; + + ec = mbedtls_pk_ec(*(mbedtls_pk_context *)pkey); + group_id = ec->private_grp.id; + curve_info = mbedtls_ecp_curve_info_from_grp_id(group_id); + return strdup(curve_info->name); +} + +int +nc_tls_get_ec_pubkey_param_wrap(void *pkey, unsigned char **bin, int *bin_len) +{ + int rc = 0; + unsigned char *bin_tmp; + size_t bin_len_tmp = 32, out_len; + mbedtls_ecp_keypair *ec; + void *tmp; + + bin_tmp = malloc(bin_len_tmp); + NC_CHECK_ERRMEM_RET(!bin_tmp, 1); + + ec = mbedtls_pk_ec(*(mbedtls_pk_context *)pkey); + while ((rc = mbedtls_ecp_point_write_binary(&ec->private_grp, &ec->private_Q, MBEDTLS_ECP_PF_COMPRESSED, &out_len, bin_tmp, bin_len_tmp)) == MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL) { + bin_len_tmp <<= 1; + tmp = realloc(bin_tmp, bin_len_tmp); + if (!tmp) { + ERRMEM; + free(bin_tmp); + return 1; + } + bin_tmp = tmp; + } + if (rc) { + ERR(NULL, "Failed to write public key binary (%s).", mbedtls_high_level_strerr(rc)); + free(bin_tmp); + return 1; + } + + *bin = bin_tmp; + *bin_len = out_len; + return 0; +} + +int +nc_tls_get_bn_num_bytes_wrap(void *bn) +{ + return mbedtls_mpi_size(bn); +} + +void +nc_tls_bn_bn2bin_wrap(void *bn, unsigned char *bin) +{ + mbedtls_mpi_write_binary(bn, bin, mbedtls_mpi_size(bn)); +} + +int +nc_tls_get_pubkey_file_wrap(const char *pubkey_path, char **pubout) +{ + int rc = 0, ret = 0; + mbedtls_pk_context *pk = NULL; + + pk = nc_tls_privkey_new_wrap(); + if (!pk) { + return 1; + } + + rc = mbedtls_pk_parse_public_keyfile(pk, pubkey_path); + if (rc) { + ERR(NULL, "Parsing public key from file \"%s\" failed (%s).", pubkey_path, mbedtls_high_level_strerr(rc)); + ret = 1; + goto cleanup; + } + + *pubout = nc_tls_export_key_wrap(pk); + if (!*pubout) { + ret = 1; + goto cleanup; + } + +cleanup: + nc_tls_privkey_destroy_wrap(pk); + return ret; +} + +void * +nc_tls_import_pubkey_file_wrap(const char *pubkey_path) +{ + int rc = 0; + mbedtls_pk_context *pk = NULL; + + pk = nc_tls_privkey_new_wrap(); + if (!pk) { + return NULL; + } + + rc = mbedtls_pk_parse_public_keyfile(pk, pubkey_path); + if (rc) { + ERR(NULL, "Parsing public key from file \"%s\" failed (%s).", pubkey_path, mbedtls_high_level_strerr(rc)); + nc_tls_privkey_destroy_wrap(pk); + return NULL; + } + + return pk; +} From 5da7d06bb154a6d2951fb1389b4e7a5b62370860 Mon Sep 17 00:00:00 2001 From: roman Date: Thu, 4 Apr 2024 09:26:42 +0200 Subject: [PATCH 04/54] session openssl UPDATE add openssl wrapper --- src/session_openssl.c | 1340 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1340 insertions(+) create mode 100644 src/session_openssl.c diff --git a/src/session_openssl.c b/src/session_openssl.c new file mode 100644 index 00000000..73cc68b6 --- /dev/null +++ b/src/session_openssl.c @@ -0,0 +1,1340 @@ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include + +#include + +#include "compat.h" +#include "config.h" +#include "log_p.h" +#include "session.h" +#include "session_p.h" +#include "session_wrapper.h" + +#include +#include +#include +#include +#include +#include + +void * +nc_tls_session_new_wrap(void *tls_cfg) +{ + SSL *session; + + session = SSL_new(tls_cfg); + if (!session) { + ERR(NULL, "Setting up TLS context failed (%s).", ERR_reason_error_string(ERR_get_error())); + return NULL; + } + + return session; +} + +void +nc_tls_session_destroy_wrap(void *tls_session) +{ + SSL_free(tls_session); +} + +void * +nc_server_tls_config_new_wrap() +{ + SSL_CTX *tls_cfg; + + tls_cfg = SSL_CTX_new(TLS_server_method()); + NC_CHECK_ERRMEM_RET(!tls_cfg, NULL) + + return tls_cfg; +} + +void * +nc_client_tls_config_new_wrap() +{ + SSL_CTX *tls_cfg; + + tls_cfg = SSL_CTX_new(TLS_client_method()); + NC_CHECK_ERRMEM_RET(!tls_cfg, NULL) + + return tls_cfg; +} + +void +nc_tls_config_destroy_wrap(void *tls_cfg) +{ + SSL_CTX_free(tls_cfg); +} + +void * +nc_tls_cert_new_wrap() +{ + X509 *cert; + + cert = X509_new(); + NC_CHECK_ERRMEM_RET(!cert, NULL) + + return cert; +} + +void +nc_tls_cert_destroy_wrap(void *cert) +{ + X509_free(cert); +} + +void * +nc_tls_privkey_new_wrap() +{ + EVP_PKEY *pkey; + + pkey = EVP_PKEY_new(); + NC_CHECK_ERRMEM_RET(!pkey, NULL); + + return pkey; +} + +void +nc_tls_privkey_destroy_wrap(void *pkey) +{ + EVP_PKEY_free(pkey); +} + +void * +nc_tls_cert_store_new_wrap() +{ + X509_STORE *store; + + store = X509_STORE_new(); + NC_CHECK_ERRMEM_RET(!store, NULL); + + return store; +} + +void +nc_tls_cert_store_destroy_wrap(void *cert_store) +{ + X509_STORE_free(cert_store); +} + +void * +nc_tls_crl_store_new_wrap() +{ + return NULL; +} + +void +nc_tls_crl_store_destroy_wrap(void *crl) +{ + (void) crl; + return; +} + +void +nc_tls_set_authmode_wrap(void *tls_cfg) +{ + SSL_CTX_set_mode(tls_cfg, SSL_MODE_AUTO_RETRY); +} + +int +nc_server_tls_set_config_defaults_wrap(void *tls_cfg) +{ + return 0; +} + +void * +nc_tls_pem_to_cert_wrap(const char *cert_data) +{ + BIO *bio; + X509 *cert; + + bio = BIO_new_mem_buf(cert_data, strlen(cert_data)); + if (!bio) { + ERR(NULL, "Creating new bio failed (%s).", ERR_reason_error_string(ERR_get_error())); + return NULL; + } + + cert = PEM_read_bio_X509(bio, NULL, NULL, NULL); + if (!cert) { + ERR(NULL, "Parsing certificate data failed (%s).", ERR_reason_error_string(ERR_get_error())); + } + BIO_free(bio); + return cert; +} + +int +nc_tls_pem_to_cert_add_to_store_wrap(const char *cert_data, void *cert_store) +{ + int rc; + X509 *cert; + + cert = nc_tls_pem_to_cert_wrap(cert_data); + if (!cert) { + return 1; + } + + rc = X509_STORE_add_cert(cert_store, cert); + X509_free(cert); + if (!rc) { + ERR(NULL, "Adding certificate to store failed (%s).", ERR_reason_error_string(ERR_get_error())); + return 1; + } + return 0; +} + +void * +nc_tls_pem_to_privkey_wrap(const char *privkey_data) +{ + BIO *bio; + EVP_PKEY *pkey; + + bio = BIO_new_mem_buf(privkey_data, strlen(privkey_data)); + if (!bio) { + ERR(NULL, "Creating new bio failed (%s).", ERR_reason_error_string(ERR_get_error())); + return NULL; + } + + pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); + if (!pkey) { + ERR(NULL, "Parsing certificate data failed (%s).", ERR_reason_error_string(ERR_get_error())); + } + BIO_free(bio); + return pkey; +} + +int +nc_tls_load_cert_private_key_wrap(void *tls_cfg, void *cert, void *pkey) +{ + int rc; + + rc = SSL_CTX_use_certificate(tls_cfg, cert); + if (rc) { + ERR(NULL, "Loading the server certificate failed (%s).", ERR_reason_error_string(ERR_get_error())); + return 1; + } + + rc = SSL_CTX_use_PrivateKey(tls_cfg, pkey); + if (rc) { + ERR(NULL, "Loading the server private key failed (%s).", ERR_reason_error_string(ERR_get_error())); + return 1; + } + + return 0; +} + +int +nc_server_tls_crl_path(const char *crl_path, void *cert_store, void *crl_store) +{ + int ret = 0; + X509_CRL *crl = NULL; + FILE *f; + + (void) crl_store; + + f = fopen(crl_path, "r"); + if (!f) { + ERR(NULL, "Unable to open CRL file \"%s\".", crl_path); + return 1; + } + + /* try PEM first */ + crl = PEM_read_X509_CRL(f, NULL, NULL, NULL); + if (crl) { + /* success */ + goto ok; + } + + /* PEM failed, try DER */ + rewind(f); + crl = d2i_X509_CRL_fp(f, NULL); + if (!crl) { + ERR(NULL, "Reading CRL from file \"%s\" failed.", crl_path); + ret = 1; + goto cleanup; + } + +ok: + ret = X509_STORE_add_crl(cert_store, crl); + if (!ret) { + ERR(NULL, "Error adding CRL to store (%s).", ERR_reason_error_string(ERR_get_error())); + ret = 1; + goto cleanup; + } + /* ok */ + ret = 0; + +cleanup: + fclose(f); + X509_CRL_free(crl); + return ret; +} + +int +nc_server_tls_add_crl_to_store_wrap(const unsigned char *crl_data, size_t size, void *cert_store, void *crl_store) +{ + int ret = 0; + X509_CRL *crl = NULL; + BIO *bio = NULL; + + (void) crl_store; + + bio = BIO_new_mem_buf(crl_data, size); + if (!bio) { + ERR(NULL, "Creating new bio failed (%s).", ERR_reason_error_string(ERR_get_error())); + ret = 1; + goto cleanup; + } + + /* try DER first */ + crl = d2i_X509_CRL_bio(bio, NULL); + if (crl) { + /* it was DER */ + goto ok; + } + + /* DER failed, try PEM next */ + crl = PEM_read_bio_X509_CRL(bio, NULL, NULL, NULL); + if (!crl) { + ERR(NULL, "Parsing downloaded CRL failed (%s).", ERR_reason_error_string(ERR_get_error())); + ret = 1; + goto cleanup; + } + +ok: + /* we obtained the CRL, now add it to the CRL store */ + ret = X509_STORE_add_crl(cert_store, crl); + if (!ret) { + ERR(NULL, "Error adding CRL to store (%s).", ERR_reason_error_string(ERR_get_error())); + ret = 1; + goto cleanup; + } + /* ok */ + ret = 0; + +cleanup: + X509_CRL_free(crl); + BIO_free(bio); + return ret; +} + +void +nc_server_tls_set_certs_wrap(void *tls_cfg, void *cert_store, void *crl_store) +{ + (void) crl_store; + + X509_STORE_set_flags(cert_store, X509_V_FLAG_CRL_CHECK); + SSL_CTX_set_cert_store(tls_cfg, cert_store); +} + +int +nc_server_tls_set_tls_versions_wrap(void *tls_cfg, unsigned int tls_versions) +{ + int rc = 1; + + /* first set the minimum version */ + if (tls_versions & NC_TLS_VERSION_10) { + rc = SSL_CTX_set_min_proto_version(tls_cfg, TLS1_VERSION); + } else if (tls_versions & NC_TLS_VERSION_11) { + rc = SSL_CTX_set_min_proto_version(tls_cfg, TLS1_1_VERSION); + } else if (tls_versions & NC_TLS_VERSION_12) { + rc = SSL_CTX_set_min_proto_version(tls_cfg, TLS1_2_VERSION); + } else if (tls_versions & NC_TLS_VERSION_13) { + rc = SSL_CTX_set_min_proto_version(tls_cfg, TLS1_3_VERSION); + } + if (!rc) { + ERR(NULL, "Setting TLS min version failed (%s).", ERR_reason_error_string(ERR_get_error())); + return 1; + } + + /* then set the maximum version */ + if (tls_versions & NC_TLS_VERSION_13) { + rc = SSL_CTX_set_max_proto_version(tls_cfg, TLS1_3_VERSION); + } else if (tls_versions & NC_TLS_VERSION_12) { + rc = SSL_CTX_set_max_proto_version(tls_cfg, TLS1_2_VERSION); + } else if (tls_versions & NC_TLS_VERSION_11) { + rc = SSL_CTX_set_max_proto_version(tls_cfg, TLS1_1_VERSION); + } else if (tls_versions & NC_TLS_VERSION_10) { + rc = SSL_CTX_set_max_proto_version(tls_cfg, TLS1_VERSION); + } + if (!rc) { + ERR(NULL, "Setting TLS max version failed (%s).", ERR_reason_error_string(ERR_get_error())); + return 1; + } + + return 0; +} + +static int +nc_server_tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) +{ + int ret = 0, depth, err; + struct nc_tls_verify_cb_data *data; + SSL *ssl; + X509 *cert; + + /* retrieve callback data stored in the SSL struct */ + ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); + data = SSL_get_ex_data(ssl, 0); + + /* get current cert and its depth */ + cert = X509_STORE_CTX_get_current_cert(x509_ctx); + depth = X509_STORE_CTX_get_error_depth(x509_ctx); + + if (preverify_ok) { + /* in-built verification was successful */ + ret = nc_server_tls_verify_cert(cert, depth, 0, data); + } else { + /* in-built verification failed, but the client still may be authenticated if: + * 1) the peer cert matches any configured end-entity cert + * 2) the peer cert has a valid chain of trust to any configured certificate authority cert + * otherwise just continue until we reach the peer cert (depth = 0) + */ + err = X509_STORE_CTX_get_error(x509_ctx); + if ((depth == 0) && (err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT)) { + /* not trusted self-signed peer certificate, case 1) */ + ret = nc_server_tls_verify_cert(cert, depth, 1, data); + } else if ((err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT) || (err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY)) { + /* full chain of trust is invalid, but it may be valid partially, case 2) */ + ret = nc_server_tls_verify_cert(cert, depth, 0, data); + } else { + VRB(NULL, "Cert verify: fail (%s).", X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509_ctx))); + ret = 1; + } + } + + if (ret == -1) { + /* fatal error */ + return 0; + } else if (!ret) { + /* success */ + return 1; + } else { + if (depth > 0) { + /* chain verify failed */ + return 1; + } else { + /* peer cert did not match */ + return 0; + } + } +} + +void +nc_server_tls_set_verify_cb_wrap(void *tls_session, struct nc_tls_verify_cb_data *cb_data) +{ + SSL_set_ex_data(tls_session, 0, cb_data); + SSL_set_verify(tls_session, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nc_server_tls_verify_cb); +} + +char * +nc_server_tls_get_subject_wrap(void *cert) +{ + return X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0); +} + +char * +nc_server_tls_get_issuer_wrap(void *cert) +{ + return X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0); +} + +int +nc_server_tls_get_username_from_cert_wrap(void *cert, NC_TLS_CTN_MAPTYPE map_type, char **username) +{ + STACK_OF(GENERAL_NAME) * san_names; + GENERAL_NAME *san_name; + ASN1_OCTET_STRING *ip; + int i, san_count; + char *subject, *common_name; + X509 *peer_cert = cert; + + *username = NULL; + + if (map_type == NC_TLS_CTN_COMMON_NAME) { + subject = nc_server_tls_get_subject_wrap(peer_cert); + NC_CHECK_ERRMEM_RET(!subject, -1); + common_name = strstr(subject, "CN="); + if (!common_name) { + WRN(NULL, "Certificate does not include the commonName field."); + free(subject); + return 1; + } + common_name += 3; + if (strchr(common_name, '/')) { + *strchr(common_name, '/') = '\0'; + } + *username = strdup(common_name); + free(subject); + NC_CHECK_ERRMEM_RET(!*username, -1); + } else { + /* retrieve subjectAltName's rfc822Name (email), dNSName and iPAddress values */ + san_names = X509_get_ext_d2i(peer_cert, NID_subject_alt_name, NULL, NULL); + if (!san_names) { + WRN(NULL, "Certificate has no SANs or failed to retrieve them."); + return 1; + } + + san_count = sk_GENERAL_NAME_num(san_names); + for (i = 0; i < san_count; ++i) { + san_name = sk_GENERAL_NAME_value(san_names, i); + + /* rfc822Name (email) */ + if (((map_type == NC_TLS_CTN_SAN_ANY) || (map_type == NC_TLS_CTN_SAN_RFC822_NAME)) && + (san_name->type == GEN_EMAIL)) { + *username = strdup((char *)ASN1_STRING_get0_data(san_name->d.rfc822Name)); + NC_CHECK_ERRMEM_RET(!*username, -1); + break; + } + + /* dNSName */ + if (((map_type == NC_TLS_CTN_SAN_ANY) || (map_type == NC_TLS_CTN_SAN_DNS_NAME)) && + (san_name->type == GEN_DNS)) { + *username = strdup((char *)ASN1_STRING_get0_data(san_name->d.dNSName)); + NC_CHECK_ERRMEM_RET(!*username, -1); + break; + } + + /* iPAddress */ + if (((map_type == NC_TLS_CTN_SAN_ANY) || (map_type == NC_TLS_CTN_SAN_IP_ADDRESS)) && + (san_name->type == GEN_IPADD)) { + ip = san_name->d.iPAddress; + if (ip->length == 4) { + if (asprintf(username, "%d.%d.%d.%d", ip->data[0], ip->data[1], ip->data[2], ip->data[3]) == -1) { + ERRMEM; + sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free); + return -1; + } + break; + } else if (ip->length == 16) { + if (asprintf(username, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + ip->data[0], ip->data[1], ip->data[2], ip->data[3], ip->data[4], ip->data[5], + ip->data[6], ip->data[7], ip->data[8], ip->data[9], ip->data[10], ip->data[11], + ip->data[12], ip->data[13], ip->data[14], ip->data[15]) == -1) { + ERRMEM; + sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free); + return -1; + } + break; + } else { + WRN(NULL, "SAN IP address in an unknown format (length is %d).", ip->length); + } + } + } + sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free); // TODO + + if (i == san_count) { + switch (map_type) { + case NC_TLS_CTN_SAN_RFC822_NAME: + WRN(NULL, "Certificate does not include the SAN rfc822Name field."); + break; + case NC_TLS_CTN_SAN_DNS_NAME: + WRN(NULL, "Certificate does not include the SAN dNSName field."); + break; + case NC_TLS_CTN_SAN_IP_ADDRESS: + WRN(NULL, "Certificate does not include the SAN iPAddress field."); + break; + case NC_TLS_CTN_SAN_ANY: + WRN(NULL, "Certificate does not include any relevant SAN fields."); + break; + default: + break; + } + return 1; + } + } + + return 0; +} + +int +nc_server_tls_certs_match_wrap(void *cert1, void *cert2) +{ + return !X509_cmp(cert1, cert2); +} + +int +nc_server_tls_md5_wrap(void *cert, unsigned char *buf) +{ + int rc; + + rc = X509_digest(cert, EVP_md5(), buf, NULL); + if (rc) { + ERR(NULL, "Calculating MD-5 digest failed (%s).", ERR_reason_error_string(ERR_get_error())); + return 1; + } + + return 0; +} + +int +nc_server_tls_sha1_wrap(void *cert, unsigned char *buf) +{ + int rc; + + rc = X509_digest(cert, EVP_sha1(), buf, NULL); + if (rc) { + ERR(NULL, "Calculating SHA-1 digest failed (%s).", ERR_reason_error_string(ERR_get_error())); + return 1; + } + + return 0; +} + +int +nc_server_tls_sha224_wrap(void *cert, unsigned char *buf) +{ + int rc; + + rc = X509_digest(cert, EVP_sha224(), buf, NULL); + if (rc) { + ERR(NULL, "Calculating SHA-224 digest failed (%s).", ERR_reason_error_string(ERR_get_error())); + return 1; + } + + return 0; +} + +int +nc_server_tls_sha256_wrap(void *cert, unsigned char *buf) +{ + int rc; + + rc = X509_digest(cert, EVP_sha256(), buf, NULL); + if (rc) { + ERR(NULL, "Calculating SHA-256 digest failed (%s).", ERR_reason_error_string(ERR_get_error())); + return 1; + } + + return 0; +} + +int +nc_server_tls_sha384_wrap(void *cert, unsigned char *buf) +{ + int rc; + + rc = X509_digest(cert, EVP_sha384(), buf, NULL); + if (rc) { + ERR(NULL, "Calculating SHA-384 digest failed (%s).", ERR_reason_error_string(ERR_get_error())); + return 1; + } + + return 0; +} + +int +nc_server_tls_sha512_wrap(void *cert, unsigned char *buf) +{ + int rc; + + rc = X509_digest(cert, EVP_sha512(), buf, NULL); + if (rc) { + ERR(NULL, "Calculating SHA-512 digest failed (%s).", ERR_reason_error_string(ERR_get_error())); + return 1; + } + + return 0; +} + +void +nc_server_tls_set_fd_wrap(void *tls_session, int sock, struct nc_tls_ctx *UNUSED(tls_ctx)) +{ + SSL_set_fd(tls_session, sock); +} + +int +nc_server_tls_handshake_step_wrap(void *tls_session) +{ + int ret = 0; + + ret = SSL_accept(tls_session); + if (ret == 1) { + return 1; + } else if (ret == -1) { + if ((SSL_get_error(tls_session, ret) == SSL_ERROR_WANT_READ) || (SSL_get_error(tls_session, ret) == SSL_ERROR_WANT_WRITE)) { + return 0; + } + } + + return -1; +} + +int +nc_client_tls_handshake_step_wrap(void *tls_session) +{ + int ret = 0; + + ret = SSL_connect(tls_session); + if (ret == 1) { + return 1; + } else if (ret == -1) { + if ((SSL_get_error(tls_session, ret) == SSL_ERROR_WANT_READ) || (SSL_get_error(tls_session, ret) == SSL_ERROR_WANT_WRITE)) { + return 0; + } + } + + return -1; +} + +void +nc_tls_ctx_destroy_wrap(struct nc_tls_ctx *UNUSED(tls_ctx)) +{ + return; +} + +int +nc_client_tls_load_cert_key_wrap(const char *cert_path, const char *key_path, void **cert, void **pkey) +{ + BIO *bio; + X509 *cert_tmp; + EVP_PKEY *pkey_tmp; + + bio = BIO_new_file(cert_path, "r"); + if (!bio) { + ERR(NULL, "Opening the client certificate file \"%s\" failed.", cert_path); + return 1; + } + + cert_tmp = PEM_read_bio_X509(bio, NULL, NULL, NULL); + BIO_free(bio); + if (!cert_tmp) { + ERR(NULL, "Parsing the client certificate file \"%s\" failed.", cert_path); + return 1; + } + + bio = BIO_new_file(key_path, "r"); + if (!bio) { + ERR(NULL, "Opening the client private key file \"%s\" failed.", key_path); + X509_free(cert_tmp); + return 1; + } + + pkey_tmp = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); + BIO_free(bio); + if (!pkey_tmp) { + ERR(NULL, "Parsing the client private key file \"%s\" failed.", key_path); + X509_free(cert_tmp); + return 1; + } + + *cert = cert_tmp; + *pkey = pkey_tmp; + + return 0; +} + +int +nc_client_tls_load_trusted_certs_wrap(void *cert_store, const char *file_path, const char *dir_path) +{ + if (!X509_STORE_load_locations(cert_store, file_path, dir_path)) { + ERR(NULL, "Loading CA certs from file \"%s\" or directory \"%s\" failed (%s).", file_path, dir_path, ERR_reason_error_string(ERR_get_error())); + return 1; + } + + return 0; +} + +int +nc_client_tls_load_crl_wrap(void *cert_store, void *UNUSED(crl_store), const char *file_path, const char *dir_path) +{ + if (!X509_STORE_load_locations(cert_store, file_path, dir_path)) { + ERR(NULL, "Loading CRLs from file \"%s\" or directory \"%s\" failed (%s).", file_path, dir_path, ERR_reason_error_string(ERR_get_error())); + return 1; + } + + return 0; +} + +int +nc_client_tls_set_hostname_wrap(void *tls_session, const char *hostname) +{ + int ret = 0; + X509_VERIFY_PARAM *vpm = NULL; + + vpm = X509_VERIFY_PARAM_new(); + NC_CHECK_ERRMEM_RET(!vpm, 1); + + if (!X509_VERIFY_PARAM_set1_host(vpm, hostname, 0)) { + ERR(NULL, "Failed to set expected hostname (%s).", ERR_reason_error_string(ERR_get_error())); + ret = 1; + goto cleanup; + } + if (!SSL_CTX_set1_param(tls_session, vpm)) { + ERR(NULL, "Failed to set verify param (%s).", ERR_reason_error_string(ERR_get_error())); + ret = 1; + goto cleanup; + } + +cleanup: + X509_VERIFY_PARAM_free(vpm); + return ret; +} + +uint32_t +nc_tls_get_verify_result_wrap(void *tls_session) +{ + return SSL_get_verify_result(tls_session); +} + +const char * +nc_tls_verify_error_string_wrap(uint32_t err_code) +{ + return X509_verify_cert_error_string(err_code); +} + +void +nc_tls_print_error_string_wrap(int connect_ret, const char *peername, void *tls_session) +{ + switch (SSL_get_error(tls_session, connect_ret)) { + case SSL_ERROR_SYSCALL: + ERR(NULL, "TLS connection to \"%s\" failed (%s).", peername, errno ? strerror(errno) : "unexpected EOF"); + break; + case SSL_ERROR_SSL: + ERR(NULL, "TLS connection to \"%s\" failed (%s).", peername, ERR_reason_error_string(ERR_get_error())); + break; + default: + ERR(NULL, "TLS connection to \"%s\" failed.", peername); + break; + } +} + +void +nc_server_tls_print_accept_error_wrap(int accept_ret, void *tls_session) +{ + switch (SSL_get_error(tls_session, accept_ret)) { + case SSL_ERROR_SYSCALL: + ERR(NULL, "TLS accept failed (%s).", strerror(errno)); + break; + case SSL_ERROR_SSL: + ERR(NULL, "TLS accept failed (%s).", ERR_reason_error_string(ERR_get_error())); + break; + default: + ERR(NULL, "TLS accept failed."); + break; + } +} + +int +nc_der_to_pubkey_wrap(const unsigned char *der, long len) +{ + int ret; + EVP_PKEY *pkey; + + pkey = d2i_PUBKEY(NULL, &der, len); + if (pkey) { + /* success */ + ret = 0; + } else { + /* fail */ + ret = 1; + } + + EVP_PKEY_free(pkey); + return ret; +} + +int +nc_base64_decode_wrap(const char *base64, char **bin) +{ + BIO *bio, *bio64 = NULL; + size_t used = 0, size = 0, r = 0; + void *tmp = NULL; + int nl_count, i, remainder, ret = 0; + char *b64; + + /* insert new lines into the base64 string, so BIO_read works correctly */ + nl_count = strlen(base64) / 64; + remainder = strlen(base64) - 64 * nl_count; + b64 = calloc(strlen(base64) + nl_count + 1, 1); + NC_CHECK_ERRMEM_RET(!b64, -1); + + for (i = 0; i < nl_count; i++) { + /* copy 64 bytes and add a NL */ + strncpy(b64 + i * 65, base64 + i * 64, 64); + b64[i * 65 + 64] = '\n'; + } + + /* copy the rest */ + strncpy(b64 + i * 65, base64 + i * 64, remainder); + + bio64 = BIO_new(BIO_f_base64()); + if (!bio64) { + ERR(NULL, "Error creating a bio (%s).", ERR_reason_error_string(ERR_get_error())); + ret = -1; + goto cleanup; + } + + bio = BIO_new_mem_buf(b64, strlen(b64)); + if (!bio) { + ERR(NULL, "Error creating a bio (%s).", ERR_reason_error_string(ERR_get_error())); + ret = -1; + goto cleanup; + } + + BIO_push(bio64, bio); + + /* store the decoded base64 in bin */ + *bin = NULL; + do { + size += 64; + + tmp = realloc(*bin, size); + if (!tmp) { + ERRMEM; + free(*bin); + *bin = NULL; + ret = -1; + goto cleanup; + } + *bin = tmp; + + r = BIO_read(bio64, *bin + used, 64); + used += r; + } while (r == 64); + + ret = size; + +cleanup: + free(b64); + BIO_free_all(bio64); + return ret; +} + +int +nc_base64_encode_wrap(const unsigned char *bin, size_t len, char **base64) +{ + BIO *bio = NULL, *b64 = NULL; + BUF_MEM *bptr; + int ret = 0; + + bio = BIO_new(BIO_s_mem()); + if (!bio) { + ERR(NULL, "Error creating a bio (%s).", ERR_reason_error_string(ERR_get_error())); + ret = -1; + goto cleanup; + } + + b64 = BIO_new(BIO_f_base64()); + if (!b64) { + ERR(NULL, "Error creating a bio (%s).", ERR_reason_error_string(ERR_get_error())); + ret = -1; + goto cleanup; + } + + bio = BIO_push(b64, bio); + BIO_write(bio, bin, len); + if (BIO_flush(bio) != 1) { + ERR(NULL, "Error flushing the bio (%s).", ERR_reason_error_string(ERR_get_error())); + ret = -1; + goto cleanup; + } + + BIO_get_mem_ptr(bio, &bptr); + *base64 = strndup(bptr->data, bptr->length); + NC_CHECK_ERRMEM_GOTO(!*base64, ret = -1, cleanup); + +cleanup: + BIO_free_all(bio); + return ret; +} + +static char * +nc_ssl_error_get_reasons(void) +{ + unsigned int e; + int reason_size, reason_len; + char *reasons = NULL; + + reason_size = 1; + reason_len = 0; + while ((e = ERR_get_error())) { + if (reason_len) { + /* add "; " */ + reason_size += 2; + reasons = nc_realloc(reasons, reason_size); + NC_CHECK_ERRMEM_RET(!reasons, NULL); + reason_len += sprintf(reasons + reason_len, "; "); + } + reason_size += strlen(ERR_reason_error_string(e)); + reasons = nc_realloc(reasons, reason_size); + NC_CHECK_ERRMEM_RET(!reasons, NULL); + reason_len += sprintf(reasons + reason_len, "%s", ERR_reason_error_string(e)); + } + + return reasons; +} + +int +nc_tls_read_wrap(struct nc_session *session, unsigned char *buf, size_t size) +{ + int rc, err; + char *reasons; + SSL *tls_session = session->ti.tls.session; + + ERR_clear_error(); + rc = SSL_read(tls_session, buf, size); + if (rc <= 0) { + err = SSL_get_error(tls_session, rc); + switch (err) { + case SSL_ERROR_WANT_READ: + case SSL_ERROR_WANT_WRITE: + rc = 0; + break; + case SSL_ERROR_ZERO_RETURN: + ERR(session, "Communication socket unexpectedly closed (OpenSSL)."); + session->status = NC_STATUS_INVALID; + session->term_reason = NC_SESSION_TERM_DROPPED; + rc = -1; + break; + case SSL_ERROR_SYSCALL: + ERR(session, "TLS socket error (%s).", errno ? strerror(errno) : "unexpected EOF"); + session->status = NC_STATUS_INVALID; + session->term_reason = NC_SESSION_TERM_OTHER; + rc = -1; + break; + case SSL_ERROR_SSL: + reasons = nc_ssl_error_get_reasons(); + ERR(session, "TLS communication error (%s).", reasons); + free(reasons); + session->status = NC_STATUS_INVALID; + session->term_reason = NC_SESSION_TERM_OTHER; + rc = -1; + break; + default: + ERR(session, "Unknown TLS error occurred (err code %d).", err); + session->status = NC_STATUS_INVALID; + session->term_reason = NC_SESSION_TERM_OTHER; + rc = -1; + break; + } + } + + return rc; +} + +int +nc_tls_write_wrap(struct nc_session *session, const unsigned char *buf, size_t size) +{ + int rc, err; + char *reasons; + SSL *tls_session = session->ti.tls.session; + + ERR_clear_error(); + rc = SSL_write(tls_session, buf, size); + if (rc < 1) { + err = SSL_get_error(tls_session, rc); + switch (err) { + case SSL_ERROR_WANT_WRITE: + case SSL_ERROR_WANT_READ: + rc = 0; + break; + case SSL_ERROR_ZERO_RETURN: + ERR(session, "TLS connection was properly closed."); + rc = -1; + break; + case SSL_ERROR_SYSCALL: + ERR(session, "TLS socket error (%s).", errno ? strerror(errno) : "unexpected EOF"); + rc = -1; + break; + case SSL_ERROR_SSL: + reasons = nc_ssl_error_get_reasons(); + ERR(session, "TLS communication error (%s).", reasons); + free(reasons); + rc = -1; + break; + default: + ERR(session, "Unknown TLS error occurred (err code %d).", err); + rc = -1; + break; + } + } + + return rc; +} + +int +nc_tls_have_pending_wrap(void *tls_session) +{ + return SSL_pending(tls_session); +} + +int +nc_tls_get_fd_wrap(const struct nc_session *session) +{ + return SSL_get_fd(session->ti.tls.session); +} + +void +nc_tls_close_notify_wrap(void *tls_session) +{ + SSL_shutdown(tls_session); +} + +void * +nc_tls_import_key_file_wrap(const char *key_path, FILE *file) +{ + EVP_PKEY *pkey; + + pkey = PEM_read_PrivateKey(file, NULL, NULL, NULL); + if (!pkey) { + ERR(NULL, "Parsing the private key file \"%s\" failed (%s).", key_path, ERR_reason_error_string(ERR_get_error())); + } + + return pkey; +} + +void * +nc_tls_import_cert_file_wrap(const char *cert_path) +{ + X509 *cert; + FILE *file; + + file = fopen(cert_path, "r"); + if (!file) { + ERR(NULL, "Opening the certificate file \"%s\" failed.", cert_path); + return NULL; + } + + cert = PEM_read_X509(file, NULL, NULL, NULL); + fclose(file); + if (!cert) { + ERR(NULL, "Parsing the certificate file \"%s\" failed (%s).", cert_path, ERR_reason_error_string(ERR_get_error())); + } + return cert; +} + +char * +nc_tls_export_key_wrap(void *pkey) +{ + BIO *bio = NULL; + char *pem = NULL; + + bio = BIO_new(BIO_s_mem()); + if (!bio) { + ERR(NULL, "Creating new bio failed (%s).", ERR_reason_error_string(ERR_get_error())); + goto cleanup; + } + + if (!PEM_write_bio_PrivateKey(bio, pkey, NULL, NULL, 0, NULL, NULL)) { + ERR(NULL, "Exporting the private key failed (%s).", ERR_reason_error_string(ERR_get_error())); + goto cleanup; + } + + pem = malloc(BIO_number_written(bio) + 1); + NC_CHECK_ERRMEM_GOTO(!pem, , cleanup); + + BIO_read(bio, pem, BIO_number_written(bio)); + pem[BIO_number_written(bio)] = '\0'; + +cleanup: + BIO_free(bio); + return pem; +} + +char * +nc_tls_export_cert_wrap(void *cert) +{ + int rc, cert_len; + BIO *bio = NULL; + char *pem = NULL; + + bio = BIO_new(BIO_s_mem()); + if (!bio) { + ERR(NULL, "Creating new bio failed (%s).", ERR_reason_error_string(ERR_get_error())); + goto cleanup; + } + + rc = PEM_write_bio_X509(bio, cert); + if (!rc) { + ERR(NULL, "Exporting the certificate failed (%s).", ERR_reason_error_string(ERR_get_error())); + goto cleanup; + } + + cert_len = BIO_pending(bio); + if (cert_len <= 0) { + ERR(NULL, "Getting the certificate length failed (%s).", ERR_reason_error_string(ERR_get_error())); + goto cleanup; + } + + pem = malloc(cert_len + 1); + NC_CHECK_ERRMEM_GOTO(!pem, , cleanup); + + /* read the cert from bio */ + rc = BIO_read(bio, pem, cert_len); + if (rc <= 0) { + ERR(NULL, "Reading the certificate failed (%s).", ERR_reason_error_string(ERR_get_error())); + free(pem); + pem = NULL; + goto cleanup; + } + + pem[cert_len] = '\0'; + +cleanup: + BIO_free(bio); + return pem; +} + +char * +nc_tls_export_pubkey_wrap(void *pkey) +{ + BIO *bio = NULL; + char *pem = NULL; + + bio = BIO_new(BIO_s_mem()); + if (!bio) { + ERR(NULL, "Creating new bio failed (%s).", ERR_reason_error_string(ERR_get_error())); + goto cleanup; + } + + if (!PEM_write_bio_PUBKEY(bio, pkey)) { + ERR(NULL, "Exporting the public key failed (%s).", ERR_reason_error_string(ERR_get_error())); + goto cleanup; + } + + pem = malloc(BIO_number_written(bio) + 1); + NC_CHECK_ERRMEM_GOTO(!pem, , cleanup); + + BIO_read(bio, pem, BIO_number_written(bio)); + pem[BIO_number_written(bio)] = '\0'; + +cleanup: + BIO_free(bio); + return pem; +} + +int +nc_tls_export_key_der_wrap(void *pkey, unsigned char **der, size_t *size) +{ + *der = NULL; + + *size = i2d_PrivateKey(pkey, der); + if (*size < 0) { + ERR(NULL, "Exporting the private key to DER format failed (%s).", ERR_reason_error_string(ERR_get_error())); + return 1; + } + + return 0; +} + +int +nc_tls_privkey_is_rsa_wrap(void *pkey) +{ + return EVP_PKEY_is_a(pkey, "RSA"); +} + +int +nc_tls_get_rsa_pubkey_params_wrap(void *pkey, void **e, void **n) +{ + BIGNUM *exp, *mod; + + if (!EVP_PKEY_get_bn_param(pkey, "e", &exp)) { + ERR(NULL, "Getting the RSA public exponent failed (%s).", ERR_reason_error_string(ERR_get_error())); + return 1; + } + + if (!EVP_PKEY_get_bn_param(pkey, "n", &mod)) { + ERR(NULL, "Getting the RSA modulus failed (%s).", ERR_reason_error_string(ERR_get_error())); + BN_free(exp); + return 1; + } + + *e = exp; + *n = mod; + return 0; +} + +int +nc_tls_privkey_is_ec_wrap(void *pkey) +{ + return EVP_PKEY_is_a(pkey, "EC"); +} + +char * +nc_tls_get_ec_group_wrap(void *pkey) +{ + size_t ec_group_len = 0; + char *ec_group = NULL; + + if (!EVP_PKEY_get_utf8_string_param(pkey, "group", NULL, 0, &ec_group_len)) { + ERR(NULL, "Getting EC group length failed (%s).", ERR_reason_error_string(ERR_get_error())); + return NULL; + } + + /* alloc mem for group + 1 for \0 */ + ec_group = malloc(ec_group_len + 1); + NC_CHECK_ERRMEM_RET(!ec_group, NULL); + + /* get the group */ + if (!EVP_PKEY_get_utf8_string_param(pkey, "group", ec_group, ec_group_len + 1, NULL)) { + ERR(NULL, "Getting EC group failed (%s).", ERR_reason_error_string(ERR_get_error())); + free(ec_group); + return NULL; + } + + return ec_group; +} + +int +nc_tls_get_ec_pubkey_param_wrap(void *pkey, unsigned char **bin, int *bin_len) +{ + int ret = 0; + BIGNUM *p; + + if (!EVP_PKEY_get_bn_param(pkey, "p", &p)) { + ERR(NULL, "Getting public key point from the EC private key failed (%s).", ERR_reason_error_string(ERR_get_error())); + ret = 1; + goto cleanup; + } + + /* prepare buffer for converting p to binary */ + *bin = malloc(BN_num_bytes(p)); + NC_CHECK_ERRMEM_GOTO(!*bin, ret = 1, cleanup); + + /* convert to binary */ + *bin_len = BN_bn2bin(p, *bin); + +cleanup: + BN_free(p); + return ret; +} + +int +nc_tls_get_bn_num_bytes_wrap(void *bn) +{ + return BN_num_bytes(bn); +} + +void +nc_tls_bn_bn2bin_wrap(void *bn, unsigned char *bin) +{ + BN_bn2bin(bn, bin); +} + +void * +nc_tls_import_pubkey_file_wrap(const char *pubkey_path) +{ + FILE *f; + EVP_PKEY *pk = NULL; + + f = fopen(pubkey_path, "r"); + if (!f) { + ERR(NULL, "Unable to open file \"%s\".", pubkey_path); + return NULL; + } + + /* read the pubkey from file */ + pk = PEM_read_PUBKEY(f, NULL, NULL, NULL); + fclose(f); + if (!pk) { + ERR(NULL, "Reading public key from file \"%s\" failed (%s).", pubkey_path, ERR_reason_error_string(ERR_get_error())); + return NULL; + } + + return pk; +} From f69d3660722a660aab30724fca1b89e8edd118b2 Mon Sep 17 00:00:00 2001 From: roman Date: Fri, 5 Apr 2024 12:27:30 +0200 Subject: [PATCH 05/54] config UPDATE add HAVE_LIBMEDTLS macro --- src/config.h.in | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/config.h.in b/src/config.h.in index 2920b1db..c64db022 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -44,6 +44,11 @@ */ #cmakedefine HAVE_LIBPAM +/* + * Use MbedTLS as TLS back-end + */ +#cmakedefine HAVE_LIBMBEDTLS + /* * Location of installed YANG modules on the system */ From 08ee21e3180f3a6e871ae2ba5970de132b4a630b Mon Sep 17 00:00:00 2001 From: roman Date: Fri, 5 Apr 2024 12:28:35 +0200 Subject: [PATCH 06/54] session wrapper UPDATE add wrapper header --- src/session_wrapper.h | 208 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 src/session_wrapper.h diff --git a/src/session_wrapper.h b/src/session_wrapper.h new file mode 100644 index 00000000..977cd000 --- /dev/null +++ b/src/session_wrapper.h @@ -0,0 +1,208 @@ + +#ifndef _SESSION_WRAPPER_H_ +#define _SESSION_WRAPPER_H_ + +#include + +#include "config.h" + +#ifdef HAVE_LIBMBEDTLS + +#include +#include + +struct nc_tls_ctx { + int *sock; + mbedtls_entropy_context *entropy; + mbedtls_ctr_drbg_context *ctr_drbg; + mbedtls_x509_crt *cert; + mbedtls_pk_context *pkey; + mbedtls_x509_crt *cert_store; + mbedtls_x509_crl *crl_store; +}; + +#else + +#include + +struct nc_tls_ctx { + char dummy[0]; +}; + +#endif + +struct nc_server_opts; + +struct nc_tls_verify_cb_data { + struct nc_session *session; + struct nc_cert_grouping *ee_certs; + struct nc_cert_grouping *referenced_ee_certs; + struct nc_server_tls_opts *opts; + struct nc_ctn_data { + char *username; + int matched_ctns; + int matched_ctn_type[6]; + int matched_ctn_count; + } ctn_data; +}; + +void * nc_tls_session_new_wrap(void *tls_cfg); + +void nc_tls_session_destroy_wrap(void *tls_session); + +void * nc_server_tls_config_new_wrap(); + +void * nc_client_tls_config_new_wrap(); + +void nc_tls_config_destroy_wrap(void *tls_cfg); + +void * nc_tls_cert_new_wrap(); + +void nc_tls_cert_destroy_wrap(void *cert); + +void * nc_tls_privkey_new_wrap(); + +void nc_tls_privkey_destroy_wrap(void *pkey); + +void * nc_tls_cert_store_new_wrap(); + +void nc_tls_cert_store_destroy_wrap(void *cert_store); + +void * nc_tls_crl_store_new_wrap(); + +void nc_tls_crl_store_destroy_wrap(void *crl); + +void nc_tls_set_authmode_wrap(void *tls_cfg); + +int nc_server_tls_set_config_defaults_wrap(void *tls_cfg); + +void * nc_tls_pem_to_cert_wrap(const char *cert_data); + +void * nc_tls_base64_to_cert_wrap(const char *cert_data); + +int nc_tls_pem_to_cert_add_to_store_wrap(const char *cert_data, void *cert_store); + +void * nc_tls_pem_to_privkey_wrap(const char *privkey_data); + +int nc_tls_load_cert_private_key_wrap(void *tls_cfg, void *cert, void *pkey); + +int nc_server_tls_crl_path_wrap(const char *crl_path, void *cert_store, void *crl_store); + +int nc_server_tls_add_crl_to_store_wrap(const unsigned char *crl_data, size_t size, void *cert_store, void *crl_store); + +void nc_server_tls_set_certs_wrap(void *tls_cfg, void *cert_store, void *crl_store); + +int nc_server_tls_set_tls_versions_wrap(void *tls_cfg, unsigned int tls_versions); + +void nc_server_tls_set_verify_cb_wrap(void *tls_session, struct nc_tls_verify_cb_data *cb_data); + +int nc_server_tls_verify_cert(void *cert, int depth, int self_signed, struct nc_tls_verify_cb_data *cb_data); + +char * nc_server_tls_get_subject_wrap(void *cert); + +char * nc_server_tls_get_issuer_wrap(void *cert); + +int nc_server_tls_get_username_from_cert_wrap(void *cert, NC_TLS_CTN_MAPTYPE map_type, char **username); + +int nc_server_tls_certs_match_wrap(void *cert1, void *cert2); + +int nc_server_tls_md5_wrap(void *cert, unsigned char *buf); + +int nc_server_tls_sha1_wrap(void *cert, unsigned char *buf); + +int nc_server_tls_sha224_wrap(void *cert, unsigned char *buf); + +int nc_server_tls_sha256_wrap(void *cert, unsigned char *buf); + +int nc_server_tls_sha384_wrap(void *cert, unsigned char *buf); + +int nc_server_tls_sha512_wrap(void *cert, unsigned char *buf); + +void nc_server_tls_set_fd_wrap(void *tls_session, int sock, struct nc_tls_ctx *tls_ctx); + +int nc_server_tls_handshake_step_wrap(void *tls_session); + +int nc_server_tls_fill_config_wrap(void *tls_cfg, void *srv_cert, void *srv_pkey, void *cert_store, void *crl_store, struct nc_tls_ctx *tls_ctx); + +int nc_server_tls_setup_config_fill_ctx_wrap(void *tls_cfg, struct nc_tls_ctx *tls_ctx, void *srv_cert, void *srv_pkey, void *cert_store, void *crl_store, int sock); + +void nc_tls_ctx_destroy_wrap(struct nc_tls_ctx *tls_ctx); + +int nc_client_tls_load_cert_key_wrap(const char *cert_path, const char *key_path, void **cert, void **pkey); + +int nc_client_tls_load_trusted_certs_wrap(void *cert_store, const char *file_path, const char *dir_path); + +int nc_client_tls_load_crl_wrap(void *cert_store, void *crl_store, const char *file_path, const char *dir_path); + +int nc_client_tls_set_hostname_wrap(void *tls_session, const char *hostname); + +int nc_client_tls_handshake_step_wrap(void *tls_session); + +uint32_t nc_tls_get_verify_result_wrap(void *tls_session); + +const char * nc_tls_verify_error_string_wrap(uint32_t err_code); + +void nc_tls_print_error_string_wrap(int connect_ret, const char *peername, void *tls_session); + +void nc_server_tls_print_accept_error_wrap(int accept_ret, void *tls_session); + +int nc_tls_init_ctx_wrap(struct nc_tls_ctx *tls_ctx, int sock, void *cli_cert, void *cli_pkey, void *cert_store, void *crl_store); + +int nc_tls_setup_config_wrap(void *tls_cfg, int side, struct nc_tls_ctx *tls_ctx); + +void nc_tls_session_new_cleanup_wrap(void *tls_cfg, void *cli_cert, void *cli_pkey, void *cert_store, void *crl_store); + +int nc_der_to_pubkey_wrap(const unsigned char *der, long len); + +/** + * @brief Decodes base64 to binary. + * + * @param[in] base64 Base64 string. + * @param[out] bin Binary result, memory managed by the caller. + * @return Length of the binary data on success, -1 on error. + */ +int nc_base64_decode_wrap(const char *base64, char **bin); + +int nc_base64_encode_wrap(const unsigned char *bin, size_t len, char **base64); + +int nc_tls_read_wrap(struct nc_session *session, unsigned char *buf, size_t size); + +int nc_tls_write_wrap(struct nc_session *session, const unsigned char *buf, size_t size); + +int nc_tls_have_pending_wrap(void *tls_session); + +int nc_tls_get_fd_wrap(const struct nc_session *session); + +void nc_tls_close_notify_wrap(void *tls_session); + +void * nc_tls_import_key_file_wrap(const char *key_path, FILE *file); + +void * nc_tls_import_cert_file_wrap(const char *cert_path); + +char * nc_tls_export_key_wrap(void *pkey); + +char * nc_tls_export_cert_wrap(void *cert); + +int nc_tls_export_key_der_wrap(void *pkey, unsigned char **der, size_t *size); + +int nc_tls_privkey_is_rsa_wrap(void *pkey); + +int nc_tls_get_rsa_pubkey_params_wrap(void *pkey, void **e, void **n); + +int nc_tls_privkey_is_ec_wrap(void *pkey); + +char * nc_tls_get_ec_group_wrap(void *pkey); + +int nc_tls_get_ec_pubkey_param_wrap(void *pkey, unsigned char **bin, int *bin_len); + +int nc_tls_get_bn_num_bytes_wrap(void *bn); + +void nc_tls_bn_bn2bin_wrap(void *bn, unsigned char *bin); + +void * nc_tls_import_pubkey_file_wrap(const char *pubkey_path); + +char * nc_tls_export_pubkey_wrap(void *pkey); + +int nc_server_tls_get_crl_distpoint_uris_wrap(void *cert_store, char ***uris, int *uri_count); + +#endif From 54a6429c8c47767bdf060c3c94f16d99146fa112 Mon Sep 17 00:00:00 2001 From: roman Date: Fri, 5 Apr 2024 12:29:11 +0200 Subject: [PATCH 07/54] session wrapper UPDATE add crl cert ext fetch wrap --- src/session_mbedtls.c | 191 ++++++++++++++++++++++++++++++++++++++++++ src/session_openssl.c | 78 +++++++++++++++++ 2 files changed, 269 insertions(+) diff --git a/src/session_mbedtls.c b/src/session_mbedtls.c index 2a86e68d..bf868bd4 100644 --- a/src/session_mbedtls.c +++ b/src/session_mbedtls.c @@ -1565,3 +1565,194 @@ nc_tls_import_pubkey_file_wrap(const char *pubkey_path) return pk; } + +int +nc_server_tls_get_crl_distpoint_uris_wrap(void *cert_store, char ***uris, int *uri_count) +{ + int ret = 0; + mbedtls_x509_crt *cert; + unsigned char *p, *end_v3_ext, *end_ext, *end_ext_octet, *end_crl_dist_points; + size_t len; + mbedtls_x509_buf ext_oid = {0}; + int is_critical = 0; + mbedtls_x509_sequence general_names = {0}; + mbedtls_x509_sequence *iter = NULL; + mbedtls_x509_subject_alternative_name san = {0}; + void *tmp; + + NC_CHECK_ARG_RET(NULL, cert_store, uris, uri_count, 1); + + *uris = NULL; + *uri_count = 0; + + /* iterate over all the CAs */ + cert = cert_store; + while (cert) { + if (!cert->v3_ext.len) { + /* no extensions, skip this cert */ + cert = cert->next; + continue; + } + + /* go over all the extensions and try to find the CRL distribution points */ + p = cert->v3_ext.p; + end_v3_ext = p + cert->v3_ext.len; + + /* + * Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension + */ + ret = mbedtls_asn1_get_tag(&p, end_v3_ext, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + if (ret) { + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret)); + goto cleanup; + } + + while (p < end_v3_ext) { + /* + * Extension ::= SEQUENCE { + * extnID OBJECT IDENTIFIER, + * critical BOOLEAN DEFAULT FALSE, + * extnValue OCTET STRING } + */ + ret = mbedtls_asn1_get_tag(&p, end_v3_ext, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + if (ret) { + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret)); + goto cleanup; + } + + end_ext = p + len; + + /* parse extnID */ + ret = mbedtls_asn1_get_tag(&p, end_ext, &ext_oid.len, MBEDTLS_ASN1_OID); + if (ret) { + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret)); + goto cleanup; + } + ext_oid.tag = MBEDTLS_ASN1_OID; + ext_oid.p = p; + + if (memcmp(ext_oid.p, MBEDTLS_OID_CRL_DISTRIBUTION_POINTS, ext_oid.len)) { + /* not the extension we are looking for */ + p = end_ext; + continue; + } + + p += ext_oid.len; + + /* parse optional critical */ + ret = mbedtls_asn1_get_bool(&p, end_ext, &is_critical); + if (ret && (ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG)) { + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret)); + goto cleanup; + } + + /* parse extnValue */ + ret = mbedtls_asn1_get_tag(&p, end_ext, &len, MBEDTLS_ASN1_OCTET_STRING); + if (ret) { + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret)); + goto cleanup; + } + + end_ext_octet = p + len; + + /* + * parse extnValue, that is CRLDistributionPoints + * + * CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint + */ + ret = mbedtls_asn1_get_tag(&p, end_ext_octet, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + if (ret) { + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret)); + goto cleanup; + } + if (p + len != end_ext_octet) { + /* length mismatch */ + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret)); + goto cleanup; + } else if (!len) { + /* empty sequence, but size is 1..max */ + ERR(NULL, "Failed to parse CRL distribution points extension (empty sequence)."); + goto cleanup; + } + + end_crl_dist_points = p + len; + + while (p < end_crl_dist_points) { + /* + * DistributionPoint ::= SEQUENCE { + * distributionPoint [0] DistributionPointName OPTIONAL, + * reasons [1] ReasonFlags OPTIONAL, + * cRLIssuer [2] GeneralNames OPTIONAL } + */ + ret = mbedtls_asn1_get_tag(&p, end_ext_octet, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + if (ret) { + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret)); + goto cleanup; + } + if (!len) { + /* empty sequence */ + continue; + } + + /* parse distributionPoint */ + ret = mbedtls_asn1_get_tag(&p, end_ext_octet, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 0); + if (!ret) { + /* + * DistributionPointName ::= CHOICE { + * fullName [0] GeneralNames, + * nameRelativeToCRLIssuer [1] RelativeDistinguishedName } + */ + ret = mbedtls_asn1_get_tag(&p, end_ext_octet, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 0); + if (ret) { + if ((ret == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) && (*p == (MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 1))) { + /* it's nameRelativeToCRLIssuer, but we don't support it */ + ERR(NULL, "Failed to parse CRL distribution points extension (nameRelativeToCRLIssuer not supported)."); + goto cleanup; + } else { + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret)); + goto cleanup; + } + } + + /* parse GeneralNames, but thankfully there is an api for this */ + ret = mbedtls_x509_get_subject_alt_name_ext(&p, p + len, &general_names); + if (ret) { + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret)); + goto cleanup; + } + + /* iterate over all the GeneralNames */ + iter = &general_names; + while (iter) { + ret = mbedtls_x509_parse_subject_alt_name(&iter->buf, &san); + if (ret && (ret != MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE)) { + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret)); + goto cleanup; + } + + if (san.type == MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER) { + /* found an URI */ + tmp = realloc(*uris, (*uri_count + 1) * sizeof **uris); + NC_CHECK_ERRMEM_GOTO(!tmp, ret = 1, cleanup); + *uris = tmp; + + *uris[*uri_count] = strndup((const char *)san.san.unstructured_name.p, san.san.unstructured_name.len); + NC_CHECK_ERRMEM_GOTO(!*uris[*uri_count], ret = 1, cleanup); + ++(*uri_count); + } + iter = iter->next; + } + + } else if (ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) { + /* failed to parse it, but not because it's optional */ + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret)); + goto cleanup; + } + } + } + cert = cert->next; + } + +cleanup: + return ret; +} diff --git a/src/session_openssl.c b/src/session_openssl.c index 73cc68b6..ec3ff492 100644 --- a/src/session_openssl.c +++ b/src/session_openssl.c @@ -1338,3 +1338,81 @@ nc_tls_import_pubkey_file_wrap(const char *pubkey_path) return pk; } + +int +nc_server_tls_get_crl_distpoint_uris_wrap(void *cert_store, char ***uris, int *uri_count) +{ + int ret = 0, i, j, k, gtype; + CURL *handle = NULL; + struct nc_curl_data downloaded = {0}; + + STACK_OF(X509_OBJECT) * objs; + X509_OBJECT *obj; + X509 *cert; + + STACK_OF(DIST_POINT) * dist_points; + DIST_POINT *dist_point; + GENERAL_NAMES *general_names; + GENERAL_NAME *general_name; + ASN1_STRING *asn_string_uri; + const char *crl_distpoint_uri; + void *tmp; + + NC_CHECK_ARG_RET(NULL, cert_store, uris, uri_count, 1); + + *uris = NULL; + *uri_count = 0; + + /* treat all entries in the cert_store as X509_OBJECTs */ + objs = X509_STORE_get0_objects(cert_store); + if (!objs) { + ERR(NULL, "Getting certificates from store failed (%s).", ERR_reason_error_string(ERR_get_error())); + ret = -1; + goto cleanup; + } + + /* iterate over all the CAs */ + for (i = 0; i < sk_X509_OBJECT_num(objs); i++) { + obj = sk_X509_OBJECT_value(objs, i); + cert = X509_OBJECT_get0_X509(obj); + if (!cert) { + /* the object on this index was not a certificate */ + continue; + } + + /* get all the distribution points for this CA */ + dist_points = X509_get_ext_d2i(cert, NID_crl_distribution_points, NULL, NULL); + + /* iterate over all the dist points (there can be multiple for a single cert) */ + for (j = 0; j < sk_DIST_POINT_num(dist_points); j++) { + dist_point = sk_DIST_POINT_value(dist_points, j); + if (!dist_point) { + continue; + } + general_names = dist_point->distpoint->name.fullname; + + /* iterate over all the GeneralesNames in the distribution point */ + for (k = 0; k < sk_GENERAL_NAME_num(general_names); k++) { + general_name = sk_GENERAL_NAME_value(general_names, k); + asn_string_uri = GENERAL_NAME_get0_value(general_name, >ype); + + /* check if the general name is a URI and has a valid length */ + if ((gtype != GEN_URI) || (ASN1_STRING_length(asn_string_uri) <= 6)) { + continue; + } + + /* found an URI */ + tmp = realloc(*uris, (*uri_count + 1) * sizeof **uris); + NC_CHECK_ERRMEM_GOTO(!tmp, ret = 1, cleanup); + *uris = tmp; + + *uris[*uri_count] = strdup((const char *) ASN1_STRING_get0_data(asn_string_uri)); + NC_CHECK_ERRMEM_GOTO(!*uris[*uri_count], ret = 1, cleanup); + ++(*uri_count); + } + } + } + +cleanup: + return ret; +} From 1babaf2c5cd7e0203d91cf8f2566b98b46254fd2 Mon Sep 17 00:00:00 2001 From: roman Date: Fri, 5 Apr 2024 12:29:53 +0200 Subject: [PATCH 08/54] session UPDATE wrap session_p --- src/session_p.h | 27 ++++++++++----------------- 1 file changed, 10 insertions(+), 17 deletions(-) diff --git a/src/session_p.h b/src/session_p.h index ae81d866..0a9f8fde 100644 --- a/src/session_p.h +++ b/src/session_p.h @@ -29,6 +29,7 @@ #include "config.h" #include "session_client.h" #include "session_server_ch.h" +#include "session_wrapper.h" /** * Enumeration of diff operation types. @@ -54,7 +55,6 @@ typedef enum { #ifdef NC_ENABLED_SSH_TLS #include -#include /* seconds */ #define NC_SSH_TIMEOUT 10 @@ -271,10 +271,10 @@ struct nc_server_tls_opts { struct nc_cert_grouping ca_certs; /**< Client certificate authorities */ struct nc_cert_grouping ee_certs; /**< Client end-entity certificates */ + char *crl_url; /**< URI to download the CRL from */ char *crl_path; /**< Path to a CRL file */ int crl_cert_ext; /**< Indicates to use CA's distribution points to obtain CRLs */ - X509_STORE *crl_store; /**< Stores all the CRLs */ char *referenced_endpt_name; /**< Reference to another endpoint (used for client authentication). */ @@ -352,15 +352,12 @@ struct nc_client_ssh_opts { struct nc_client_tls_opts { char *cert_path; char *key_path; + char *ca_file; char *ca_dir; - int8_t tls_ctx_change; - SSL_CTX *tls_ctx; char *crl_file; char *crl_dir; - int8_t crl_store_change; - X509_STORE *crl_store; }; #endif /* NC_ENABLED_SSH_TLS */ @@ -635,7 +632,12 @@ struct nc_session { otherwise there is a ring list of the NETCONF sessions */ } libssh; - SSL *tls; + struct { + void *session; + void *config; + struct nc_tls_ctx ctx; + } tls; + #endif /* NC_ENABLED_SSH_TLS */ } ti; /**< transport implementation data */ char *username; @@ -690,7 +692,7 @@ struct nc_session { # define NC_SESSION_SSH_SUBSYS_NETCONF 0x20 uint16_t ssh_auth_attempts; /**< number of failed SSH authentication attempts */ - X509 *client_cert; /**< TLS client certificate if used for authentication */ + void *client_cert; /**< TLS client certificate if used for authentication */ #endif /* NC_ENABLED_SSH_TLS */ } server; } opts; @@ -748,15 +750,6 @@ struct nc_pam_thread_arg { */ const char *nc_privkey_format_to_str(NC_PRIVKEY_FORMAT format); -/** - * @brief Decodes base64 to binary. - * - * @param[in] base64 Base64 string. - * @param[out] bin Binary result, memory managed by the caller. - * @return Length of the binary data on success, -1 on error. - */ -int nc_base64_to_bin(const char *base64, char **bin); - /** * @brief Checks if the given base64 belongs to a public key in the SubjectPublicKeyInfo format. * From b92110417ac06e219a5472734e3e5611e608aed4 Mon Sep 17 00:00:00 2001 From: roman Date: Fri, 5 Apr 2024 12:30:22 +0200 Subject: [PATCH 09/54] io UPDATE wrap TLS --- src/io.c | 114 ++++++------------------------------------------------- 1 file changed, 12 insertions(+), 102 deletions(-) diff --git a/src/io.c b/src/io.c index e7366fb3..e8d5903e 100644 --- a/src/io.c +++ b/src/io.c @@ -28,15 +28,10 @@ #include #include -#ifdef NC_ENABLED_SSH_TLS -# include -# include -#endif /* NC_ENABLED_SSH_TLS */ - #include -#include "compat.h" #include "config.h" +#include "compat.h" #include "log_p.h" #include "messages_p.h" #include "netconf.h" @@ -57,36 +52,6 @@ const char *nc_msgtype2str[] = { #define BUFFERSIZE 512 -#ifdef NC_ENABLED_SSH_TLS - -static char * -nc_ssl_error_get_reasons(void) -{ - unsigned int e; - int reason_size, reason_len; - char *reasons = NULL; - - reason_size = 1; - reason_len = 0; - while ((e = ERR_get_error())) { - if (reason_len) { - /* add "; " */ - reason_size += 2; - reasons = nc_realloc(reasons, reason_size); - NC_CHECK_ERRMEM_RET(!reasons, NULL); - reason_len += sprintf(reasons + reason_len, "; "); - } - reason_size += strlen(ERR_reason_error_string(e)); - reasons = nc_realloc(reasons, reason_size); - NC_CHECK_ERRMEM_RET(!reasons, NULL); - reason_len += sprintf(reasons + reason_len, "%s", ERR_reason_error_string(e)); - } - - return reasons; -} - -#endif /* NC_ENABLED_SSH_TLS */ - static ssize_t nc_read(struct nc_session *session, char *buf, uint32_t count, uint32_t inact_timeout, struct timespec *ts_act_timeout) { @@ -164,41 +129,10 @@ nc_read(struct nc_session *session, char *buf, uint32_t count, uint32_t inact_ti break; case NC_TI_OPENSSL: - /* read via OpenSSL */ - ERR_clear_error(); - r = SSL_read(session->ti.tls, buf + readd, count - readd); - if (r <= 0) { - int e; - char *reasons; - - switch (e = SSL_get_error(session->ti.tls, r)) { - case SSL_ERROR_WANT_READ: - case SSL_ERROR_WANT_WRITE: - r = 0; - break; - case SSL_ERROR_ZERO_RETURN: - ERR(session, "Communication socket unexpectedly closed (OpenSSL)."); - session->status = NC_STATUS_INVALID; - session->term_reason = NC_SESSION_TERM_DROPPED; - return -1; - case SSL_ERROR_SYSCALL: - ERR(session, "SSL socket error (%s).", errno ? strerror(errno) : "unexpected EOF"); - session->status = NC_STATUS_INVALID; - session->term_reason = NC_SESSION_TERM_OTHER; - return -1; - case SSL_ERROR_SSL: - reasons = nc_ssl_error_get_reasons(); - ERR(session, "SSL error (%s).", reasons); - free(reasons); - session->status = NC_STATUS_INVALID; - session->term_reason = NC_SESSION_TERM_OTHER; - return -1; - default: - ERR(session, "Unknown SSL error occurred (err code %d).", e); - session->status = NC_STATUS_INVALID; - session->term_reason = NC_SESSION_TERM_OTHER; - return -1; - } + r = nc_tls_read_wrap(session, (unsigned char *)buf + readd, count - readd); + if (r < 0) { + /* non-recoverable error */ + return r; } break; #endif /* NC_ENABLED_SSH_TLS */ @@ -484,7 +418,7 @@ nc_read_poll(struct nc_session *session, int io_timeout) } break; case NC_TI_OPENSSL: - ret = SSL_pending(session->ti.tls); + ret = nc_tls_have_pending_wrap(session->ti.tls.session); if (ret) { /* some buffered TLS data available */ ret = 1; @@ -492,7 +426,7 @@ nc_read_poll(struct nc_session *session, int io_timeout) break; } - fds.fd = SSL_get_fd(session->ti.tls); + fds.fd = nc_tls_get_fd_wrap(session); #endif /* NC_ENABLED_SSH_TLS */ /* fallthrough */ case NC_TI_FD: @@ -592,7 +526,7 @@ nc_session_is_connected(const struct nc_session *session) case NC_TI_LIBSSH: return ssh_is_connected(session->ti.libssh.session); case NC_TI_OPENSSL: - fds.fd = SSL_get_fd(session->ti.tls); + fds.fd = nc_tls_get_fd_wrap(session); break; #endif /* NC_ENABLED_SSH_TLS */ default: @@ -638,10 +572,6 @@ nc_write(struct nc_session *session, const void *buf, uint32_t count) int c, fd, interrupted; uint32_t written = 0; -#ifdef NC_ENABLED_SSH_TLS - unsigned long e; -#endif /* NC_ENABLED_SSH_TLS */ - if ((session->status != NC_STATUS_RUNNING) && (session->status != NC_STATUS_STARTING)) { return -1; } @@ -693,30 +623,10 @@ nc_write(struct nc_session *session, const void *buf, uint32_t count) } break; case NC_TI_OPENSSL: - c = SSL_write(session->ti.tls, (char *)(buf + written), count - written); - if (c < 1) { - char *reasons; - - switch ((e = SSL_get_error(session->ti.tls, c))) { - case SSL_ERROR_ZERO_RETURN: - ERR(session, "SSL connection was properly closed."); - return -1; - case SSL_ERROR_WANT_WRITE: - case SSL_ERROR_WANT_READ: - c = 0; - break; - case SSL_ERROR_SYSCALL: - ERR(session, "SSL socket error (%s).", strerror(errno)); - return -1; - case SSL_ERROR_SSL: - reasons = nc_ssl_error_get_reasons(); - ERR(session, "SSL error (%s).", reasons); - free(reasons); - return -1; - default: - ERR(session, "Unknown SSL error occurred (err code %d).", e); - return -1; - } + c = nc_tls_write_wrap(session, (const unsigned char *)(buf + written), count - written); + if (c < 0) { + /* possible client dc, or some socket/TLS communication error */ + return -1; } break; #endif /* NC_ENABLED_SSH_TLS */ From 0fb56264a1fc99798e70ca9f29e136589e795931 Mon Sep 17 00:00:00 2001 From: roman Date: Fri, 5 Apr 2024 12:30:55 +0200 Subject: [PATCH 10/54] server config UPDATE wrap TLS --- src/server_config.c | 8 -------- 1 file changed, 8 deletions(-) diff --git a/src/server_config.c b/src/server_config.c index 0207d75c..d7f1f724 100644 --- a/src/server_config.c +++ b/src/server_config.c @@ -26,13 +26,6 @@ #include #include -#ifdef NC_ENABLED_SSH_TLS -#include -#include // EVP_PKEY_free -#include // d2i_PUBKEY -#include // X509_STORE_free -#endif - #include "compat.h" #include "config.h" #include "log_p.h" @@ -840,7 +833,6 @@ nc_server_config_del_tls_opts(struct nc_bind *bind, struct nc_server_tls_opts *o free(opts->crl_path); free(opts->crl_url); - X509_STORE_free(opts->crl_store); nc_server_config_del_ctns(opts); free(opts->ciphers); From d2862f2f85ebf2dade43fcaad2aa17d996965fcf Mon Sep 17 00:00:00 2001 From: roman Date: Fri, 5 Apr 2024 12:31:24 +0200 Subject: [PATCH 11/54] session config util UPDATE wrap TLS --- src/server_config_util.c | 405 +++++++++++++-------------------------- src/server_config_util.h | 6 + 2 files changed, 141 insertions(+), 270 deletions(-) diff --git a/src/server_config_util.c b/src/server_config_util.c index b5f7e06e..6c004996 100644 --- a/src/server_config_util.c +++ b/src/server_config_util.c @@ -25,17 +25,11 @@ #include #include -#ifdef NC_ENABLED_SSH_TLS -#include -#include -#include -#include -#endif /* NC_ENABLED_SSH_TLS */ - #include "compat.h" #include "log_p.h" #include "session.h" #include "session_p.h" +#include "session_wrapper.h" int nc_server_config_create(const struct ly_ctx *ctx, struct lyd_node **tree, const char *value, const char *path_fmt, ...) @@ -225,34 +219,7 @@ nc_server_config_util_privkey_format_to_identityref(NC_PRIVKEY_FORMAT format) } static int -nc_server_config_util_pubkey_bin_to_b64(const unsigned char *pub_bin, int bin_len, char **pubkey) -{ - int ret = 0, b64_len; - char *pub_b64 = NULL; - - NC_CHECK_ARG_RET(NULL, pub_bin, bin_len, pubkey, 1); - - /* get b64 buffer len, for ever 3 bytes of bin 4 bytes of b64 + NULL terminator */ - if (bin_len % 3 == 0) { - pub_b64 = malloc((bin_len / 3) * 4 + 1); - } else { - /* bin len not divisible by 3, need to add 4 bytes for some padding so that the len is divisible by 4 */ - pub_b64 = malloc((bin_len / 3) * 4 + 4 + 1); - } - NC_CHECK_ERRMEM_GOTO(!pub_b64, ret = 1, cleanup); - - /* bin to b64 */ - b64_len = EVP_EncodeBlock((unsigned char *)pub_b64, pub_bin, bin_len); - *pubkey = strndup(pub_b64, b64_len); - NC_CHECK_ERRMEM_GOTO(!*pubkey, ret = 1, cleanup); - -cleanup: - free(pub_b64); - return ret; -} - -static int -nc_server_config_util_bn_to_bin(const BIGNUM *bn, unsigned char **bin, int *bin_len) +nc_server_config_util_bn_to_bin(void *bn, unsigned char **bin, int *bin_len) { int ret = 0; unsigned char *bin_tmp = NULL; @@ -262,11 +229,12 @@ nc_server_config_util_bn_to_bin(const BIGNUM *bn, unsigned char **bin, int *bin_ *bin = NULL; /* prepare buffer for converting BN to binary */ - bin_tmp = calloc(BN_num_bytes(bn), sizeof *bin_tmp); + *bin_len = nc_tls_get_bn_num_bytes_wrap(bn); + bin_tmp = calloc(*bin_len, sizeof *bin_tmp); NC_CHECK_ERRMEM_RET(!bin_tmp, 1); /* convert to binary */ - *bin_len = BN_bn2bin(bn, bin_tmp); + nc_tls_bn_bn2bin_wrap(bn, bin_tmp); /* if the highest bit in the MSB is set a byte with the value 0 has to be prepended */ if (bin_tmp[0] & 0x80) { @@ -288,25 +256,23 @@ nc_server_config_util_bn_to_bin(const BIGNUM *bn, unsigned char **bin, int *bin_ /* ssh pubkey defined in RFC 4253 section 6.6 */ static int -nc_server_config_util_evp_pkey_to_ssh_pubkey(EVP_PKEY *pkey, char **pubkey) +nc_server_config_util_evp_pkey_to_ssh_pubkey(void *pkey, char **pubkey) { int ret = 0, e_len, n_len, p_len, bin_len; - BIGNUM *e = NULL, *n = NULL, *p = NULL; + void *e = NULL, *n = NULL; unsigned char *e_bin = NULL, *n_bin = NULL, *p_bin = NULL, *bin = NULL, *bin_tmp; const char *algorithm_name, *curve_name; char *ec_group = NULL; uint32_t alg_name_len, curve_name_len, alg_name_len_be, curve_name_len_be, p_len_be, e_len_be, n_len_be; - size_t ec_group_len; NC_CHECK_ARG_RET(NULL, pkey, pubkey, 1); - if (EVP_PKEY_is_a(pkey, "RSA")) { + if (nc_tls_privkey_is_rsa_wrap(pkey)) { /* RSA key */ algorithm_name = "ssh-rsa"; /* get the public key params */ - if (!EVP_PKEY_get_bn_param(pkey, "e", &e) || !EVP_PKEY_get_bn_param(pkey, "n", &n)) { - ERR(NULL, "Getting public key parameters from RSA private key failed (%s).", ERR_reason_error_string(ERR_get_error())); + if (nc_tls_get_rsa_pubkey_params_wrap(pkey, &e, &n)) { ret = 1; goto cleanup; } @@ -343,21 +309,10 @@ nc_server_config_util_evp_pkey_to_ssh_pubkey(EVP_PKEY *pkey, char **pubkey) memcpy(bin_tmp, &n_len_be, 4); bin_tmp += 4; memcpy(bin_tmp, n_bin, n_len); - } else if (EVP_PKEY_is_a(pkey, "EC")) { + } else if (nc_tls_privkey_is_ec_wrap(pkey)) { /* EC Private key, get it's group first */ - /* get group len */ - ret = EVP_PKEY_get_utf8_string_param(pkey, "group", NULL, 0, &ec_group_len); - if (!ret) { - ret = 1; - goto cleanup; - } - /* alloc mem for group + 1 for \0 */ - ec_group = malloc(ec_group_len + 1); - NC_CHECK_ERRMEM_GOTO(!ec_group, ret = 1, cleanup); - /* get the group */ - ret = EVP_PKEY_get_utf8_string_param(pkey, "group", ec_group, ec_group_len + 1, NULL); - if (!ret) { - ERR(NULL, "Getting public key parameter from EC private key failed (%s).", ERR_reason_error_string(ERR_get_error())); + ec_group = nc_tls_get_ec_group_wrap(pkey); + if (!ec_group) { ret = 1; goto cleanup; } @@ -379,19 +334,13 @@ nc_server_config_util_evp_pkey_to_ssh_pubkey(EVP_PKEY *pkey, char **pubkey) } /* get the public key - p, which is a point on the elliptic curve */ - ret = EVP_PKEY_get_bn_param(pkey, "p", &p); - if (!ret) { - ERR(NULL, "Getting public key point from the EC private key failed (%s).", ERR_reason_error_string(ERR_get_error())); + ret = nc_tls_get_ec_pubkey_param_wrap(pkey, &p_bin, &p_len); + if (ret) { + ERR(NULL, "Getting public key point from the EC private key failed."); ret = 1; goto cleanup; } - /* prepare buffer for converting p to binary */ - p_bin = malloc(BN_num_bytes(p)); - NC_CHECK_ERRMEM_GOTO(!p_bin, ret = 1, cleanup); - /* convert to binary */ - p_len = BN_bn2bin(p, p_bin); - alg_name_len = strlen(algorithm_name); curve_name_len = strlen(curve_name); /* buffer for public key in binary, which looks like so: @@ -419,10 +368,6 @@ nc_server_config_util_evp_pkey_to_ssh_pubkey(EVP_PKEY *pkey, char **pubkey) memcpy(bin_tmp, &p_len_be, 4); bin_tmp += 4; memcpy(bin_tmp, p_bin, p_len); - } else if (EVP_PKEY_is_a(pkey, "ED25519")) { - ERR(NULL, "Generating PEM ED25519 key from OpenSSH is not supported by libssh yet."); - ret = 1; - goto cleanup; } else { ERR(NULL, "Unable to generate public key from private key (Private key type not supported)."); ret = 1; @@ -430,7 +375,7 @@ nc_server_config_util_evp_pkey_to_ssh_pubkey(EVP_PKEY *pkey, char **pubkey) } /* convert created bin to b64 */ - ret = nc_server_config_util_pubkey_bin_to_b64(bin, bin_len, pubkey); + ret = nc_base64_encode_wrap(bin, bin_len, pubkey); if (ret) { ERR(NULL, "Converting public key from binary to base64 failed."); goto cleanup; @@ -442,141 +387,83 @@ nc_server_config_util_evp_pkey_to_ssh_pubkey(EVP_PKEY *pkey, char **pubkey) free(n_bin); free(ec_group); free(p_bin); - BN_free(e); - BN_free(n); - BN_free(p); return ret; } /* spki = subject public key info */ static int -nc_server_config_util_evp_pkey_to_spki_pubkey(EVP_PKEY *pkey, char **pubkey) +nc_server_config_util_evp_pkey_to_spki_pubkey(void *pkey, char **pubkey) { - int ret = 0, len; - BIO *bio = NULL; - char *pub_b64 = NULL; + int ret = 0; + char *pub_pem = NULL; NC_CHECK_ARG_RET(NULL, pkey, pubkey, 1); - bio = BIO_new(BIO_s_mem()); - if (!bio) { - ERR(NULL, "Creating new BIO failed (%s).", ERR_reason_error_string(ERR_get_error())); - ret = 1; - goto cleanup; - } - - /* write the evp_pkey contents to bio */ - if (!PEM_write_bio_PUBKEY(bio, pkey)) { - ERR(NULL, "Writing public key to BIO failed (%s).", ERR_reason_error_string(ERR_get_error())); - ret = 1; - goto cleanup; - } - - /* read the pubkey from bio */ - len = BIO_get_mem_data(bio, &pub_b64); - if (len <= 0) { - ERR(NULL, "Reading base64 private key from BIO failed (%s).", ERR_reason_error_string(ERR_get_error())); + pub_pem = nc_tls_export_pubkey_wrap(pkey); + if (!pub_pem) { ret = 1; goto cleanup; } /* copy the public key without the header and footer */ - *pubkey = strndup(pub_b64 + strlen(NC_SUBJECT_PUBKEY_INFO_HEADER), - len - strlen(NC_SUBJECT_PUBKEY_INFO_HEADER) - strlen(NC_SUBJECT_PUBKEY_INFO_FOOTER)); + *pubkey = strndup(pub_pem + strlen(NC_SUBJECT_PUBKEY_INFO_HEADER), + strlen(pub_pem) - strlen(NC_SUBJECT_PUBKEY_INFO_HEADER) - strlen(NC_SUBJECT_PUBKEY_INFO_FOOTER)); NC_CHECK_ERRMEM_GOTO(!*pubkey, ret = 1, cleanup); cleanup: - BIO_free(bio); + free(pub_pem); return ret; } int nc_server_config_util_read_certificate(const char *cert_path, char **cert) { - int ret = 0, cert_len; - X509 *x509 = NULL; - FILE *f = NULL; - BIO *bio = NULL; - char *c = NULL; + int ret = 0; + void *crt = NULL; + char *pem = NULL; NC_CHECK_ARG_RET(NULL, cert_path, cert, 1); - f = fopen(cert_path, "r"); - if (!f) { - ERR(NULL, "Unable to open certificate file \"%s\".", cert_path); - ret = 1; - goto cleanup; - } - - /* load the cert into memory */ - x509 = PEM_read_X509(f, NULL, NULL, NULL); - if (!x509) { - ret = -1; - goto cleanup; - } - - bio = BIO_new(BIO_s_mem()); - if (!bio) { - ret = -1; - goto cleanup; - } - - ret = PEM_write_bio_X509(bio, x509); - if (!ret) { - ret = -1; - goto cleanup; - } - - cert_len = BIO_pending(bio); - if (cert_len <= 0) { - ret = -1; - goto cleanup; + crt = nc_tls_import_cert_file_wrap(cert_path); + if (!crt) { + return 1; } - c = malloc(cert_len + 1); - NC_CHECK_ERRMEM_GOTO(!c, ret = 1, cleanup); - - /* read the cert from bio */ - ret = BIO_read(bio, c, cert_len); - if (ret <= 0) { - ret = -1; + pem = nc_tls_export_cert_wrap(crt); + if (!pem) { + ret = 1; goto cleanup; } - c[cert_len] = '\0'; - /* strip the cert of the header and footer */ - *cert = strdup(c + strlen(NC_PEM_CERTIFICATE_HEADER)); + /* copy the cert without its header and footer */ + *cert = strndup(pem + strlen(NC_PEM_CERTIFICATE_HEADER), + strlen(pem) - strlen(NC_PEM_CERTIFICATE_HEADER) - strlen(NC_PEM_CERTIFICATE_FOOTER)); NC_CHECK_ERRMEM_GOTO(!*cert, ret = 1, cleanup); - (*cert)[strlen(*cert) - strlen(NC_PEM_CERTIFICATE_FOOTER)] = '\0'; - - ret = 0; - cleanup: - if (ret == -1) { - ERR(NULL, "Error getting certificate from file \"%s\" (OpenSSL Error): \"%s\".", cert_path, ERR_reason_error_string(ERR_get_error())); - ret = 1; - } - if (f) { - fclose(f); - } - - BIO_free(bio); - X509_free(x509); - free(c); + free(pem); + nc_tls_cert_destroy_wrap(crt); return ret; } static int -nc_server_config_util_read_pubkey_ssh2(FILE *f, char **pubkey) +nc_server_config_util_read_pubkey_ssh2(const char *pubkey_path, char **pubkey) { char *buffer = NULL; size_t size = 0, pubkey_len = 0; void *tmp; ssize_t read; int ret = 0; + FILE *f = NULL; - NC_CHECK_ARG_RET(NULL, f, pubkey, 1); + NC_CHECK_ARG_RET(NULL, pubkey_path, pubkey, 1); + + f = fopen(pubkey_path, "r"); + if (!f) { + ERR(NULL, "Failed to open file \"%s\".", pubkey_path); + ret = 1; + goto cleanup; + } /* read lines from the file and create the public key without NL from it */ while ((read = getline(&buffer, &size, f)) > 0) { @@ -612,28 +499,29 @@ nc_server_config_util_read_pubkey_ssh2(FILE *f, char **pubkey) (*pubkey)[pubkey_len] = '\0'; cleanup: + if (f) { + fclose(f); + } free(buffer); return ret; } static int -nc_server_config_util_read_pubkey_openssl(FILE *f, char **pubkey) +nc_server_config_util_read_pubkey_openssl(const char *pubkey_path, char **pubkey) { int ret = 0; - EVP_PKEY *pub_pkey = NULL; + void *pub_pkey = NULL; - NC_CHECK_ARG_RET(NULL, f, pubkey, 1); + NC_CHECK_ARG_RET(NULL, pubkey_path, pubkey, 1); /* read the pubkey from file */ - pub_pkey = PEM_read_PUBKEY(f, NULL, NULL, NULL); + pub_pkey = nc_tls_import_pubkey_file_wrap(pubkey_path); if (!pub_pkey) { - ERR(NULL, "Reading public key from file failed (%s).", ERR_reason_error_string(ERR_get_error())); return 1; } ret = nc_server_config_util_evp_pkey_to_ssh_pubkey(pub_pkey, pubkey); - - EVP_PKEY_free(pub_pkey); + nc_tls_privkey_destroy_wrap(pub_pkey); return ret; } @@ -682,19 +570,20 @@ nc_server_config_util_get_ssh_pubkey_file(const char *pubkey_path, char **pubkey } /* read the header */ - if (getline(&header, &len, f) < 0) { + ret = getline(&header, &len, f); + fclose(f); + if (ret < 0) { ERR(NULL, "Error reading header from file \"%s\".", pubkey_path); ret = 1; goto cleanup; } - rewind(f); if (!strncmp(header, NC_SUBJECT_PUBKEY_INFO_HEADER, strlen(NC_SUBJECT_PUBKEY_INFO_HEADER))) { /* it's subject public key info public key */ - ret = nc_server_config_util_read_pubkey_openssl(f, pubkey); + ret = nc_server_config_util_read_pubkey_openssl(pubkey_path, pubkey); } else if (!strncmp(header, NC_SSH2_PUBKEY_HEADER, strlen(NC_SSH2_PUBKEY_HEADER))) { /* it's ssh2 public key */ - ret = nc_server_config_util_read_pubkey_ssh2(f, pubkey); + ret = nc_server_config_util_read_pubkey_ssh2(pubkey_path, pubkey); } else { /* it's probably OpenSSH public key */ ret = nc_server_config_util_read_pubkey_libssh(pubkey_path, pubkey); @@ -705,10 +594,6 @@ nc_server_config_util_get_ssh_pubkey_file(const char *pubkey_path, char **pubkey } cleanup: - if (f) { - fclose(f); - } - free(header); return ret; } @@ -717,38 +602,24 @@ int nc_server_config_util_get_spki_pubkey_file(const char *pubkey_path, char **pubkey) { int ret = 0; - FILE *f = NULL; - EVP_PKEY *pub_pkey = NULL; + void *pkey = NULL; NC_CHECK_ARG_RET(NULL, pubkey_path, pubkey, 1); *pubkey = NULL; - f = fopen(pubkey_path, "r"); - if (!f) { - ERR(NULL, "Unable to open file \"%s\".", pubkey_path); - ret = 1; - goto cleanup; - } - - /* read the pubkey from file */ - pub_pkey = PEM_read_PUBKEY(f, NULL, NULL, NULL); - if (!pub_pkey) { - ERR(NULL, "Reading public key from file failed (%s).", ERR_reason_error_string(ERR_get_error())); + pkey = nc_tls_import_pubkey_file_wrap(pubkey_path); + if (!pkey) { return 1; } - ret = nc_server_config_util_evp_pkey_to_spki_pubkey(pub_pkey, pubkey); + ret = nc_server_config_util_evp_pkey_to_spki_pubkey(pkey, pubkey); if (ret) { goto cleanup; } cleanup: - if (f) { - fclose(f); - } - - EVP_PKEY_free(pub_pkey); + nc_tls_privkey_destroy_wrap(pkey); return ret; } @@ -791,113 +662,104 @@ nc_server_config_util_privkey_header_to_format(FILE *f_privkey, const char *priv } static int -nc_server_config_util_get_privkey_openssl(const char *privkey_path, FILE *f_privkey, char **privkey, EVP_PKEY **pkey) +nc_server_config_util_get_privkey_openssl(const char *privkey_path, FILE *f_privkey, char **privkey, void **pkey) { - int ret = 0, len; - BIO *bio = NULL; - char *priv_b64 = NULL; + void *pkey_tmp; + char *privkey_tmp; NC_CHECK_ARG_RET(NULL, privkey_path, f_privkey, privkey, pkey, 1); - bio = BIO_new(BIO_s_mem()); - if (!bio) { - ERR(NULL, "Creating new BIO failed (%s).", ERR_reason_error_string(ERR_get_error())); - ret = 1; - goto cleanup; - } + *privkey = *pkey = NULL; - /* read the privkey file, create EVP_PKEY */ - *pkey = PEM_read_PrivateKey(f_privkey, NULL, NULL, NULL); - if (!*pkey) { - ERR(NULL, "Getting private key from file \"%s\" failed (%s).", privkey_path, ERR_reason_error_string(ERR_get_error())); - ret = 1; - goto cleanup; - } - - /* write the privkey to bio */ - if (!PEM_write_bio_PrivateKey(bio, *pkey, NULL, NULL, 0, NULL, NULL)) { - ERR(NULL, "Writing private key to BIO failed (%s).", ERR_reason_error_string(ERR_get_error())); - ret = 1; - goto cleanup; + pkey_tmp = nc_tls_import_key_file_wrap(privkey_path, f_privkey); + if (!pkey_tmp) { + return 1; } - /* read the privkey from bio */ - len = BIO_get_mem_data(bio, &priv_b64); - if (len <= 0) { - ERR(NULL, "Reading base64 private key from BIO failed (%s).", ERR_reason_error_string(ERR_get_error())); - ret = 1; - goto cleanup; + privkey_tmp = nc_tls_export_key_wrap(pkey_tmp); + if (!privkey_tmp) { + nc_tls_privkey_destroy_wrap(pkey_tmp); + return 1; } - *privkey = strndup(priv_b64, len); - NC_CHECK_ERRMEM_GOTO(!*privkey, ret = 1, cleanup); - -cleanup: - /* priv_b64 is freed with BIO */ - BIO_free(bio); - return ret; + *privkey = privkey_tmp; + *pkey = pkey_tmp; + return 0; } static int -nc_server_config_util_get_privkey_libssh(const char *privkey_path, char **privkey, EVP_PKEY **pkey) +nc_server_config_util_get_privkey_libssh(const char *privkey_path, char **privkey, void **pkey) { int ret = 0; - BIO *bio = NULL; - char *priv_b64 = NULL; ssh_key key = NULL; + void *pkey_tmp = NULL; + char *privkey_tmp = NULL; NC_CHECK_ARG_RET(NULL, privkey_path, privkey, pkey, 1); ret = ssh_pki_import_privkey_file(privkey_path, NULL, NULL, NULL, &key); if (ret) { ERR(NULL, "Importing privkey from file \"%s\" failed.", privkey_path); + ret = 1; goto cleanup; } - /* exports the key in a format in which OpenSSL can read it */ - ret = ssh_pki_export_privkey_base64(key, NULL, NULL, NULL, &priv_b64); + /* export the key in PEM */ + ret = ssh_pki_export_privkey_base64(key, NULL, NULL, NULL, &privkey_tmp); if (ret) { ERR(NULL, "Exporting privkey to base64 failed."); goto cleanup; } - bio = BIO_new(BIO_s_mem()); - if (!bio) { - ERR(NULL, "Creating new BIO failed (%s).", ERR_reason_error_string(ERR_get_error())); + pkey_tmp = nc_tls_pem_to_privkey_wrap(privkey_tmp); + if (!pkey_tmp) { + free(privkey_tmp); ret = 1; goto cleanup; } - ret = BIO_write(bio, priv_b64, strlen(priv_b64)); - if (ret <= 0) { - ERR(NULL, "Writing private key to BIO failed (%s).", ERR_reason_error_string(ERR_get_error())); - ret = 1; - goto cleanup; - } - - /* create EVP_PKEY from the b64 */ - *pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); - if (!*pkey) { - ERR(NULL, "Getting private key from file \"%s\" failed (%s).", privkey_path, ERR_reason_error_string(ERR_get_error())); - ret = 1; - goto cleanup; - } - - *privkey = strndup(priv_b64, ret); - NC_CHECK_ERRMEM_GOTO(!*privkey, ret = 1, cleanup); - - /* ok */ - ret = 0; + *privkey = privkey_tmp; + *pkey = pkey_tmp; cleanup: - free(priv_b64); - BIO_free(bio); ssh_key_free(key); return ret; } static int -nc_server_config_util_get_privkey(const char *privkey_path, NC_PRIVKEY_FORMAT *privkey_format, char **privkey, EVP_PKEY **pkey) +nc_server_config_util_pem_strip_header_footer(const char *pem, char **privkey) +{ + const char *header, *footer; + + if (!strncmp(pem, NC_PKCS8_PRIVKEY_HEADER, strlen(NC_PKCS8_PRIVKEY_HEADER))) { + /* it's PKCS8 (X.509) private key */ + header = NC_PKCS8_PRIVKEY_HEADER; + footer = NC_PKCS8_PRIVKEY_FOOTER; + } else if (!strncmp(pem, NC_OPENSSH_PRIVKEY_HEADER, strlen(NC_OPENSSH_PRIVKEY_HEADER))) { + /* it's OpenSSH private key */ + header = NC_OPENSSH_PRIVKEY_HEADER; + footer = NC_OPENSSH_PRIVKEY_FOOTER; + } else if (!strncmp(pem, NC_PKCS1_RSA_PRIVKEY_HEADER, strlen(NC_PKCS1_RSA_PRIVKEY_HEADER))) { + /* it's RSA privkey in PKCS1 format */ + header = NC_PKCS1_RSA_PRIVKEY_HEADER; + footer = NC_PKCS1_RSA_PRIVKEY_FOOTER; + } else if (!strncmp(pem, NC_SEC1_EC_PRIVKEY_HEADER, strlen(NC_SEC1_EC_PRIVKEY_HEADER))) { + /* it's EC privkey in SEC1 format */ + header = NC_SEC1_EC_PRIVKEY_HEADER; + footer = NC_SEC1_EC_PRIVKEY_FOOTER; + } else { + return 1; + } + + /* make a copy without the header and footer */ + *privkey = strndup(pem + strlen(header), strlen(pem) - strlen(header) - strlen(footer)); + NC_CHECK_ERRMEM_RET(!*privkey, 1); + + return 0; +} + +static int +nc_server_config_util_get_privkey(const char *privkey_path, NC_PRIVKEY_FORMAT *privkey_format, char **privkey, void **pkey) { int ret = 0; FILE *f_privkey = NULL; @@ -943,9 +805,11 @@ nc_server_config_util_get_privkey(const char *privkey_path, NC_PRIVKEY_FORMAT *p } /* strip private key's header and footer */ - *privkey = strdup(priv + strlen(NC_PKCS8_PRIVKEY_HEADER)); - NC_CHECK_ERRMEM_GOTO(!*privkey, ret = 1, cleanup); - (*privkey)[strlen(*privkey) - strlen(NC_PKCS8_PRIVKEY_FOOTER)] = '\0'; + ret = nc_server_config_util_pem_strip_header_footer(priv, privkey); + if (ret) { + ERR(NULL, "Stripping header and footer from private key failed."); + goto cleanup; + } cleanup: if (f_privkey) { @@ -961,7 +825,7 @@ nc_server_config_util_get_asym_key_pair(const char *privkey_path, const char *pu char **privkey, NC_PRIVKEY_FORMAT *privkey_type, char **pubkey) { int ret = 0; - EVP_PKEY *priv_pkey = NULL; + void *pkey = NULL; NC_CHECK_ARG_RET(NULL, privkey_path, privkey, privkey_type, pubkey, 1); @@ -969,7 +833,7 @@ nc_server_config_util_get_asym_key_pair(const char *privkey_path, const char *pu *pubkey = NULL; /* get private key base64 and EVP_PKEY */ - ret = nc_server_config_util_get_privkey(privkey_path, privkey_type, privkey, &priv_pkey); + ret = nc_server_config_util_get_privkey(privkey_path, privkey_type, privkey, &pkey); if (ret) { ERR(NULL, "Getting private key from file \"%s\" failed.", privkey_path); goto cleanup; @@ -978,9 +842,9 @@ nc_server_config_util_get_asym_key_pair(const char *privkey_path, const char *pu /* get public key, either from file or generate it from the EVP_PKEY */ if (!pubkey_path) { if (wanted_pubkey_format == NC_PUBKEY_FORMAT_SSH) { - ret = nc_server_config_util_evp_pkey_to_ssh_pubkey(priv_pkey, pubkey); + ret = nc_server_config_util_evp_pkey_to_ssh_pubkey(pkey, pubkey); } else { - ret = nc_server_config_util_evp_pkey_to_spki_pubkey(priv_pkey, pubkey); + ret = nc_server_config_util_evp_pkey_to_spki_pubkey(pkey, pubkey); } } else { if (wanted_pubkey_format == NC_PUBKEY_FORMAT_SSH) { @@ -999,10 +863,11 @@ nc_server_config_util_get_asym_key_pair(const char *privkey_path, const char *pu } cleanup: - EVP_PKEY_free(priv_pkey); + nc_tls_privkey_destroy_wrap(pkey); return ret; } + API int nc_server_config_add_address_port(const struct ly_ctx *ctx, const char *endpt_name, NC_TRANSPORT_IMPL transport, const char *address, uint16_t port, struct lyd_node **config) diff --git a/src/server_config_util.h b/src/server_config_util.h index 6048fc4b..17b3fffd 100644 --- a/src/server_config_util.h +++ b/src/server_config_util.h @@ -37,9 +37,15 @@ /* private key's pkcs1 rsa header */ #define NC_PKCS1_RSA_PRIVKEY_HEADER "-----BEGIN RSA PRIVATE KEY-----\n" +/* private key's pkcs1 rsa footer */ +#define NC_PKCS1_RSA_PRIVKEY_FOOTER "\n-----END RSA PRIVATE KEY-----\n" + /* private key's sec1 ec header */ #define NC_SEC1_EC_PRIVKEY_HEADER "-----BEGIN EC PRIVATE KEY-----\n" +/* private key's sec1 ec footer */ +#define NC_SEC1_EC_PRIVKEY_FOOTER "\n-----END EC PRIVATE KEY-----\n" + /* private key's header when getting an EC/RSA privkey from file using libssh */ #define NC_LIBSSH_PRIVKEY_HEADER "-----BEGIN PRIVATE KEY-----\n" From 6ab88622adeb73445532bd3775ed8eb25dc42390 Mon Sep 17 00:00:00 2001 From: roman Date: Fri, 5 Apr 2024 12:31:54 +0200 Subject: [PATCH 12/54] session UPDATE wrap TLS --- src/session.c | 103 +++++++++----------------------------------------- src/session.h | 2 +- 2 files changed, 18 insertions(+), 87 deletions(-) diff --git a/src/session.c b/src/session.c index 7cb1e7fa..bb5b611b 100644 --- a/src/session.c +++ b/src/session.c @@ -37,12 +37,8 @@ #ifdef NC_ENABLED_SSH_TLS +#include "session_wrapper.h" #include -#include -#include -#include -#include -#include #endif /* NC_ENABLED_SSH_TLS */ @@ -158,83 +154,15 @@ nc_privkey_format_to_str(NC_PRIVKEY_FORMAT format) } } -int -nc_base64_to_bin(const char *base64, char **bin) -{ - BIO *bio, *bio64 = NULL; - size_t used = 0, size = 0, r = 0; - void *tmp = NULL; - int nl_count, i, remainder, ret = 0; - char *b64; - - /* insert new lines into the base64 string, so BIO_read works correctly */ - nl_count = strlen(base64) / 64; - remainder = strlen(base64) - 64 * nl_count; - b64 = calloc(strlen(base64) + nl_count + 1, 1); - NC_CHECK_ERRMEM_RET(!b64, -1); - - for (i = 0; i < nl_count; i++) { - /* copy 64 bytes and add a NL */ - strncpy(b64 + i * 65, base64 + i * 64, 64); - b64[i * 65 + 64] = '\n'; - } - - /* copy the rest */ - strncpy(b64 + i * 65, base64 + i * 64, remainder); - - bio64 = BIO_new(BIO_f_base64()); - if (!bio64) { - ERR(NULL, "Error creating a bio (%s).", ERR_reason_error_string(ERR_get_error())); - ret = -1; - goto cleanup; - } - - bio = BIO_new_mem_buf(b64, strlen(b64)); - if (!bio) { - ERR(NULL, "Error creating a bio (%s).", ERR_reason_error_string(ERR_get_error())); - ret = -1; - goto cleanup; - } - - BIO_push(bio64, bio); - - /* store the decoded base64 in bin */ - *bin = NULL; - do { - size += 64; - - tmp = realloc(*bin, size); - if (!tmp) { - ERRMEM; - free(*bin); - *bin = NULL; - ret = -1; - goto cleanup; - } - *bin = tmp; - - r = BIO_read(bio64, *bin + used, 64); - used += r; - } while (r == 64); - - ret = size; - -cleanup: - free(b64); - BIO_free_all(bio64); - return ret; -} - int nc_is_pk_subject_public_key_info(const char *b64) { int ret = 0; long len; char *bin = NULL, *tmp; - EVP_PKEY *pkey = NULL; - /* base64 2 binary */ - len = nc_base64_to_bin(b64, &bin); + /* decode base64 */ + len = nc_base64_decode_wrap(b64, &bin); if (len == -1) { ERR(NULL, "Decoding base64 public key to binary failed."); ret = -1; @@ -244,18 +172,16 @@ nc_is_pk_subject_public_key_info(const char *b64) /* for deallocation later */ tmp = bin; - /* try to create EVP_PKEY from the supposed SubjectPublicKeyInfo binary data */ - pkey = d2i_PUBKEY(NULL, (const unsigned char **)&tmp, len); - if (pkey) { - /* success, it's most likely SubjectPublicKeyInfo pubkey */ + /* try to parse the supposed SubjectPublicKeyInfo binary data */ + if (!nc_der_to_pubkey_wrap((const unsigned char *)tmp, len)) { + /* success, it's most likely SubjectPublicKeyInfo */ ret = 1; } else { - /* fail, it's most likely not SubjectPublicKeyInfo pubkey */ + /* it's most likely not SubjectPublicKeyInfo */ ret = 0; } cleanup: - EVP_PKEY_free(pkey); free(bin); return ret; } @@ -869,17 +795,22 @@ nc_session_free_transport(struct nc_session *session, int *multisession) break; } case NC_TI_OPENSSL: - /* remember sock so we can close it */ - sock = SSL_get_fd(session->ti.tls); + sock = nc_tls_get_fd_wrap(session); if (connected) { - SSL_shutdown(session->ti.tls); + /* notify the peer that we're shutting down */ + nc_tls_close_notify_wrap(session->ti.tls.session); } - SSL_free(session->ti.tls); + + nc_tls_ctx_destroy_wrap(&session->ti.tls.ctx); + nc_tls_session_destroy_wrap(session->ti.tls.session); + nc_tls_config_destroy_wrap(session->ti.tls.config); if (session->side == NC_SERVER) { - X509_free(session->opts.server.client_cert); + // TODO + nc_tls_cert_destroy_wrap(session->opts.server.client_cert); } + break; #endif /* NC_ENABLED_SSH_TLS */ case NC_TI_NONE: diff --git a/src/session.h b/src/session.h index 52626e8a..59aabe2d 100644 --- a/src/session.h +++ b/src/session.h @@ -92,7 +92,7 @@ typedef enum { #ifdef NC_ENABLED_SSH_TLS NC_TI_LIBSSH, /**< libssh - use libssh library, only for NETCONF over SSH transport */ - NC_TI_OPENSSL /**< OpenSSL - use OpenSSL library, only for NETCONF over TLS transport */ + NC_TI_OPENSSL /**< OpenSSL - use OpenSSL library, only for NETCONF over TLS transport TODO: prejmenovat*/ #endif /* NC_ENABLED_SSH_TLS */ } NC_TRANSPORT_IMPL; From a6b55f4bcb77ee5c41d3c2e91f65260ed4aaa991 Mon Sep 17 00:00:00 2001 From: roman Date: Fri, 5 Apr 2024 12:32:31 +0200 Subject: [PATCH 13/54] session client UPDATE wrap TLS --- src/session_client.h | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/src/session_client.h b/src/session_client.h index 3ac3c509..7188732d 100644 --- a/src/session_client.h +++ b/src/session_client.h @@ -22,15 +22,15 @@ extern "C" { #include -#ifdef NC_ENABLED_SSH_TLS -# include -# include -#endif /* NC_ENABLED_SSH_TLS */ - +#include "config.h" #include "messages_client.h" #include "netconf.h" #include "session.h" +#ifdef NC_ENABLED_SSH_TLS +# include +#endif /* NC_ENABLED_SSH_TLS */ + /** * @addtogroup client * @{ @@ -472,17 +472,9 @@ void nc_client_tls_get_crl_paths(const char **crl_file, const char **crl_dir); struct nc_session *nc_connect_tls(const char *host, uint16_t port, struct ly_ctx *ctx); /** - * @brief Connect to the NETCONF server using the provided TLS (libssl) session. - * - * The TLS session supplied is expected to be fully connected and authenticated! - * - * @param[in] tls libssl structure representing the TLS session object. - * @param[in,out] ctx Optional custom context to use for the session. If not set, a default context is created. - * Any YANG modules not present in the context and supported by the server are loaded using \ - * (if supported) and/or by searching the searchpath (see ::nc_client_set_schema_searchpath()). - * @return Created NETCONF session object or NULL on error. + * @brief Deprecated. Should not be needed. */ -struct nc_session *nc_connect_libssl(SSL *tls, struct ly_ctx *ctx); +struct nc_session *nc_connect_libssl(void *tls, struct ly_ctx *ctx); /** @} Client TLS */ From 20f067dd48f064ffef538b1aa6716efcc23d1f32 Mon Sep 17 00:00:00 2001 From: roman Date: Fri, 5 Apr 2024 12:32:49 +0200 Subject: [PATCH 14/54] session client tls UPDATE wrap TLS --- src/session_client_tls.c | 459 +++++++++++---------------------------- 1 file changed, 132 insertions(+), 327 deletions(-) diff --git a/src/session_client_tls.c b/src/session_client_tls.c index d33778f0..4e313110 100644 --- a/src/session_client_tls.c +++ b/src/session_client_tls.c @@ -24,15 +24,13 @@ #include #include -#include -#include -#include #include "config.h" #include "log_p.h" #include "session_client.h" #include "session_client_ch.h" #include "session_p.h" +#include "session_wrapper.h" struct nc_client_context *nc_client_context_location(void); @@ -40,102 +38,6 @@ struct nc_client_context *nc_client_context_location(void); #define tls_opts nc_client_context_location()->tls_opts #define tls_ch_opts nc_client_context_location()->tls_ch_opts -static int tlsauth_ch; - -static int -tlsauth_verify_callback(int preverify_ok, X509_STORE_CTX *x509_ctx) -{ - X509_STORE_CTX *store_ctx; - X509_OBJECT *obj; - X509_NAME *subject, *issuer; - X509 *cert; - X509_CRL *crl; - X509_REVOKED *revoked; - EVP_PKEY *pubkey; - int i, n, rc; - const ASN1_TIME *next_update = NULL; - struct nc_client_tls_opts *opts; - - if (!preverify_ok) { - return 0; - } - - opts = (tlsauth_ch ? &tls_ch_opts : &tls_opts); - - if (!opts->crl_store) { - /* nothing to check */ - return 1; - } - - cert = X509_STORE_CTX_get_current_cert(x509_ctx); - subject = X509_get_subject_name(cert); - issuer = X509_get_issuer_name(cert); - - /* try to retrieve a CRL corresponding to the _subject_ of - * the current certificate in order to verify it's integrity */ - store_ctx = X509_STORE_CTX_new(); - obj = X509_OBJECT_new(); - X509_STORE_CTX_init(store_ctx, opts->crl_store, NULL, NULL); - rc = X509_STORE_CTX_get_by_subject(store_ctx, X509_LU_CRL, subject, obj); - X509_STORE_CTX_free(store_ctx); - crl = X509_OBJECT_get0_X509_CRL(obj); - if ((rc > 0) && crl) { - next_update = X509_CRL_get0_nextUpdate(crl); - - /* verify the signature on this CRL */ - pubkey = X509_get_pubkey(cert); - if (X509_CRL_verify(crl, pubkey) <= 0) { - X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE); - X509_OBJECT_free(obj); - if (pubkey) { - EVP_PKEY_free(pubkey); - } - return 0; /* fail */ - } - if (pubkey) { - EVP_PKEY_free(pubkey); - } - - /* check date of CRL to make sure it's not expired */ - if (!next_update) { - X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD); - X509_OBJECT_free(obj); - return 0; /* fail */ - } - if (X509_cmp_current_time(next_update) < 0) { - X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_HAS_EXPIRED); - X509_OBJECT_free(obj); - return 0; /* fail */ - } - X509_OBJECT_free(obj); - } - - /* try to retrieve a CRL corresponding to the _issuer_ of - * the current certificate in order to check for revocation */ - store_ctx = X509_STORE_CTX_new(); - obj = X509_OBJECT_new(); - X509_STORE_CTX_init(store_ctx, opts->crl_store, NULL, NULL); - rc = X509_STORE_CTX_get_by_subject(store_ctx, X509_LU_CRL, issuer, obj); - X509_STORE_CTX_free(store_ctx); - crl = X509_OBJECT_get0_X509_CRL(obj); - if ((rc > 0) && crl) { - /* check if the current certificate is revoked by this CRL */ - n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl)); - for (i = 0; i < n; i++) { - revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i); - if (ASN1_INTEGER_cmp(X509_REVOKED_get0_serialNumber(revoked), X509_get_serialNumber(cert)) == 0) { - ERR(NULL, "Certificate revoked!"); - X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CERT_REVOKED); - X509_OBJECT_free(obj); - return 0; /* fail */ - } - } - X509_OBJECT_free(obj); - } - - return 1; /* success */ -} - void _nc_client_tls_destroy_opts(struct nc_client_tls_opts *opts) { @@ -143,12 +45,8 @@ _nc_client_tls_destroy_opts(struct nc_client_tls_opts *opts) free(opts->key_path); free(opts->ca_file); free(opts->ca_dir); - SSL_CTX_free(opts->tls_ctx); - free(opts->crl_file); free(opts->crl_dir); - X509_STORE_free(opts->crl_store); - memset(opts, 0, sizeof *opts); } @@ -177,8 +75,6 @@ _nc_client_tls_set_cert_key_paths(const char *client_cert, const char *client_ke opts->key_path = NULL; } - opts->tls_ctx_change = 1; - return 0; } @@ -247,8 +143,6 @@ _nc_client_tls_set_trusted_ca_paths(const char *ca_file, const char *ca_dir, str opts->ca_dir = NULL; } - opts->tls_ctx_change = 1; - return 0; } @@ -317,8 +211,6 @@ _nc_client_tls_set_crl_paths(const char *crl_file, const char *crl_dir, struct n opts->crl_dir = NULL; } - opts->crl_store_change = 1; - return 0; } @@ -381,152 +273,135 @@ nc_client_tls_ch_del_bind(const char *address, uint16_t port) } static int -nc_client_tls_update_opts(struct nc_client_tls_opts *opts, const char *peername) +nc_client_tls_connect_check(int connect_ret, void *tls_session, const char *peername) { - int rc = 0; - char *key; - X509_LOOKUP *lookup; - X509_VERIFY_PARAM *vpm = NULL; - - if (!opts->tls_ctx || opts->tls_ctx_change) { - SSL_CTX_free(opts->tls_ctx); - /* prepare global SSL context, highest available method is negotiated autmatically */ - if (!(opts->tls_ctx = SSL_CTX_new(TLS_client_method()))) { - ERR(NULL, "Unable to create OpenSSL context (%s).", ERR_reason_error_string(ERR_get_error())); - rc = -1; - goto cleanup; - } - SSL_CTX_set_verify(opts->tls_ctx, SSL_VERIFY_PEER, tlsauth_verify_callback); - - /* get peer certificate */ - if (SSL_CTX_use_certificate_file(opts->tls_ctx, opts->cert_path, SSL_FILETYPE_PEM) != 1) { - ERR(NULL, "Loading the client certificate from \'%s\' failed (%s).", opts->cert_path, - ERR_reason_error_string(ERR_get_error())); - rc = -1; - goto cleanup; - } + uint32_t verify; - /* if the file with private key not specified, expect that the private key is stored with the certificate */ - if (!opts->key_path) { - key = opts->cert_path; - } else { - key = opts->key_path; - } - if (SSL_CTX_use_PrivateKey_file(opts->tls_ctx, key, SSL_FILETYPE_PEM) != 1) { - ERR(NULL, "Loading the client private key from \'%s\' failed (%s).", key, - ERR_reason_error_string(ERR_get_error())); - rc = -1; - goto cleanup; - } + /* check certificate verification result */ + verify = nc_tls_get_verify_result_wrap(tls_session); + if (!verify && connect_ret == 1) { + VRB(NULL, "Server certificate verified (domain \"%s\").", peername); + } else if (verify) { + ERR(NULL, "Server certificate error (%s).", nc_tls_verify_error_string_wrap(verify)); + } - if (!SSL_CTX_load_verify_locations(opts->tls_ctx, opts->ca_file, opts->ca_dir)) { - ERR(NULL, "Failed to load the locations of trusted CA certificates (%s).", - ERR_reason_error_string(ERR_get_error())); - rc = -1; - goto cleanup; - } + /* check TLS connection result */ + if (connect_ret != 1) { + nc_tls_print_error_string_wrap(connect_ret, peername, tls_session); + } - if (peername) { - /* server identity (hostname) verification */ - vpm = X509_VERIFY_PARAM_new(); - if (!X509_VERIFY_PARAM_set1_host(vpm, peername, 0)) { - ERR(NULL, "Failed to set expected server hostname (%s).", ERR_reason_error_string(ERR_get_error())); - rc = -1; - goto cleanup; - } - if (!SSL_CTX_set1_param(opts->tls_ctx, vpm)) { - ERR(NULL, "Failed to set verify params (%s).", ERR_reason_error_string(ERR_get_error())); - rc = -1; - goto cleanup; - } - } + return connect_ret; +} + +static void * +nc_client_tls_session_new(int sock, const char *host, int timeout, struct nc_client_tls_opts *opts, void **out_tls_cfg, struct nc_tls_ctx *tls_ctx) +{ + int ret = 0; + struct timespec ts_timeout; + void *tls_session, *tls_cfg, *cli_cert, *cli_pkey, *cert_store, *crl_store; + + tls_session = tls_cfg = cli_cert = cli_pkey = cert_store = crl_store = NULL; + + /* prepare TLS context from which a session will be created */ + tls_cfg = nc_client_tls_config_new_wrap(); + if (!tls_cfg) { + goto fail; } - if (opts->crl_store_change || (!opts->crl_store && (opts->crl_file || opts->crl_dir))) { - /* set the revocation store with the correct paths for the callback */ - X509_STORE_free(opts->crl_store); + /* opaque CA/CRL certificate store */ + cert_store = nc_tls_cert_store_new_wrap(); + if (!cert_store) { + goto fail; + } - opts->crl_store = X509_STORE_new(); - if (!opts->crl_store) { - ERR(NULL, "Unable to create a certificate store (%s).", ERR_reason_error_string(ERR_get_error())); - rc = -1; - goto cleanup; - } + /* load client's key and certificate */ + if (nc_client_tls_load_cert_key_wrap(opts->cert_path, opts->key_path, &cli_cert, &cli_pkey)) { + goto fail; + } + + /* load trusted CA certificates */ + if (nc_client_tls_load_trusted_certs_wrap(cert_store, opts->ca_file, opts->ca_dir)) { + goto fail; + } - if (opts->crl_file) { - if (!(lookup = X509_STORE_add_lookup(opts->crl_store, X509_LOOKUP_file()))) { - ERR(NULL, "Failed to add lookup method to CRL checking."); - rc = -1; - goto cleanup; - } - if (X509_LOOKUP_add_dir(lookup, opts->crl_file, X509_FILETYPE_PEM) != 1) { - ERR(NULL, "Failed to add the revocation lookup file \"%s\".", opts->crl_file); - rc = -1; - goto cleanup; - } + if (opts->crl_file || opts->crl_dir) { + /* opaque CRL store */ + crl_store = nc_tls_crl_store_new_wrap(); + if (!crl_store) { + goto fail; } - if (opts->crl_dir) { - if (!(lookup = X509_STORE_add_lookup(opts->crl_store, X509_LOOKUP_hash_dir()))) { - ERR(NULL, "Failed to add lookup method to CRL checking."); - rc = -1; - goto cleanup; - } - if (X509_LOOKUP_add_dir(lookup, opts->crl_dir, X509_FILETYPE_PEM) != 1) { - ERR(NULL, "Failed to add the revocation lookup directory \"%s\".", opts->crl_dir); - rc = -1; - goto cleanup; - } + /* load CRLs into one of the stores */ + if (nc_client_tls_load_crl_wrap(cert_store, crl_store, opts->crl_file, opts->crl_dir)) { + goto fail; } } -cleanup: - X509_VERIFY_PARAM_free(vpm); - return rc; -} + /* init TLS context and store data which may be needed later in it */ + if (nc_tls_init_ctx_wrap(tls_ctx, sock, cli_cert, cli_pkey, cert_store, crl_store)) { + goto fail; // TODO: openssl free all the shit + } -static int -nc_client_tls_connect_check(int connect_ret, SSL *tls, const char *peername) -{ - int verify; + /* memory is managed by context now */ + cli_cert = cli_pkey = cert_store = crl_store = NULL; - /* check certificate verification result */ - verify = SSL_get_verify_result(tls); - switch (verify) { - case X509_V_OK: - if (connect_ret == 1) { - VRB(NULL, "Server certificate verified (domain \"%s\").", peername); - } - break; - default: - ERR(NULL, "Server certificate error (%s).", X509_verify_cert_error_string(verify)); + /* setup config from ctx */ + if (nc_tls_setup_config_wrap(tls_cfg, NC_CLIENT, tls_ctx)) { + goto fail; } - /* check TLS connection result */ - if (connect_ret != 1) { - switch (SSL_get_error(tls, connect_ret)) { - case SSL_ERROR_SYSCALL: - ERR(NULL, "SSL connect to \"%s\" failed (%s).", peername, errno ? strerror(errno) : "unexpected EOF"); - break; - case SSL_ERROR_SSL: - ERR(NULL, "SSL connect to \"%s\" failed (%s).", peername, ERR_reason_error_string(ERR_get_error())); - break; - default: - ERR(NULL, "SSL connect to \"%s\" failed.", peername); - break; + /* session from config */ + tls_session = nc_tls_session_new_wrap(tls_cfg); + if (!tls_session) { + goto fail; + } + + /* set session fd */ + nc_server_tls_set_fd_wrap(tls_session, sock, tls_ctx); + + sock = -1; + + /* set session hostname to check against in the server cert */ + if (nc_client_tls_set_hostname_wrap(tls_session, host)) { + goto fail; + } + + /* handshake */ + if (timeout > -1) { + nc_timeouttime_get(&ts_timeout, timeout); + } + while ((ret = nc_client_tls_handshake_step_wrap(tls_session)) == 0) { + usleep(NC_TIMEOUT_STEP); + if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) { + ERR(NULL, "SSL connect timeout."); + goto fail; } } - return connect_ret; + /* check if handshake was ok */ + if (nc_client_tls_connect_check(ret, tls_session, host) != 1) { + goto fail; + } + + *out_tls_cfg = tls_cfg; + return tls_session; + +fail: + if (sock > -1) { + close(sock); + } + nc_tls_session_new_cleanup_wrap(tls_cfg, cli_cert, cli_pkey, cert_store, crl_store); + return NULL; } API struct nc_session * nc_connect_tls(const char *host, unsigned short port, struct ly_ctx *ctx) { struct nc_session *session = NULL; - int sock, ret; - struct timespec ts_timeout; + int sock; char *ip_host = NULL; + void *tls_cfg = NULL; + struct nc_tls_ctx tls_ctx = {0}; if (!tls_opts.cert_path) { ERR(NULL, "Client certificate not set."); @@ -545,49 +420,28 @@ nc_connect_tls(const char *host, unsigned short port, struct ly_ctx *ctx) port = NC_PORT_TLS; } - /* create/update TLS structures */ - if (nc_client_tls_update_opts(&tls_opts, host)) { - return NULL; - } - /* prepare session structure */ session = nc_new_session(NC_CLIENT, 0); NC_CHECK_ERRMEM_RET(!session, NULL); session->status = NC_STATUS_STARTING; - /* fill the session */ - session->ti_type = NC_TI_OPENSSL; - if (!(session->ti.tls = SSL_new(tls_opts.tls_ctx))) { - ERR(NULL, "Failed to create a new TLS session structure (%s).", ERR_reason_error_string(ERR_get_error())); - goto fail; - } - /* create and assign socket */ sock = nc_sock_connect(host, port, -1, &client_opts.ka, NULL, &ip_host); if (sock == -1) { ERR(NULL, "Unable to connect to %s:%u (%s).", host, port, strerror(errno)); goto fail; } - SSL_set_fd(session->ti.tls, sock); - - /* set the SSL_MODE_AUTO_RETRY flag to allow OpenSSL perform re-handshake automatically */ - SSL_set_mode(session->ti.tls, SSL_MODE_AUTO_RETRY); - /* connect and perform the handshake */ - nc_timeouttime_get(&ts_timeout, NC_TRANSPORT_TIMEOUT); - tlsauth_ch = 0; - while (((ret = SSL_connect(session->ti.tls)) != 1) && (SSL_get_error(session->ti.tls, ret) == SSL_ERROR_WANT_READ)) { - usleep(NC_TIMEOUT_STEP); - if (nc_timeouttime_cur_diff(&ts_timeout) < 1) { - ERR(NULL, "SSL connect timeout."); - goto fail; - } - } - - /* check for errors */ - if (nc_client_tls_connect_check(ret, session->ti.tls, host) != 1) { + /* fill the session */ + session->ti_type = NC_TI_OPENSSL; + if (!(session->ti.tls.session = nc_client_tls_session_new(sock, host, NC_TRANSPORT_TIMEOUT, &tls_opts, &tls_cfg, &tls_ctx))) { goto fail; } + session->ti.tls.config = tls_cfg; + + /* memory belongs to session */ + memcpy(&session->ti.tls.ctx, &tls_ctx, sizeof tls_ctx); + memset(&tls_ctx, 0, sizeof tls_ctx); if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) { goto fail; @@ -614,27 +468,36 @@ nc_connect_tls(const char *host, unsigned short port, struct ly_ctx *ctx) fail: free(ip_host); nc_session_free(session, NULL); + nc_tls_ctx_destroy_wrap(&tls_ctx); return NULL; } API struct nc_session * -nc_connect_libssl(SSL *tls, struct ly_ctx *ctx) +nc_connect_libssl(void *UNUSED(tls), struct ly_ctx *UNUSED(ctx)) { - struct nc_session *session; - - NC_CHECK_ARG_RET(NULL, tls, NULL); + ERR(NULL, "nc_connect_libssl() is deprecated, do not use it."); + return NULL; +} - if (!SSL_is_init_finished(tls)) { - ERR(NULL, "Supplied TLS session is not fully connected!"); - return NULL; - } +struct nc_session * +nc_accept_callhome_tls_sock(int sock, const char *host, uint16_t port, struct ly_ctx *ctx, int timeout, const char *peername) +{ + struct nc_session *session = NULL; + void *tls_cfg = NULL; + struct nc_tls_ctx tls_ctx = {0}; /* prepare session structure */ session = nc_new_session(NC_CLIENT, 0); NC_CHECK_ERRMEM_RET(!session, NULL); session->status = NC_STATUS_STARTING; + + /* fill the session */ session->ti_type = NC_TI_OPENSSL; - session->ti.tls = tls; + if (!(session->ti.tls.session = nc_client_tls_session_new(sock, peername, timeout, &tls_ch_opts, &tls_cfg, &tls_ctx))) { + goto fail; + } + session->ti.tls.config = tls_cfg; + memcpy(&session->ti.tls.ctx, &tls_ctx, sizeof tls_ctx); if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) { goto fail; @@ -651,62 +514,6 @@ nc_connect_libssl(SSL *tls, struct ly_ctx *ctx) goto fail; } - return session; - -fail: - session->ti_type = NC_TI_NONE; - session->ti.tls = NULL; - nc_session_free(session, NULL); - return NULL; -} - -struct nc_session * -nc_accept_callhome_tls_sock(int sock, const char *host, uint16_t port, struct ly_ctx *ctx, int timeout, const char *peername) -{ - int ret; - SSL *tls = NULL; - struct nc_session *session = NULL; - struct timespec ts_timeout; - - /* create/update TLS structures with explicit expected peername, if any set, the host is just the IP */ - if (nc_client_tls_update_opts(&tls_ch_opts, peername)) { - goto cleanup; - } - - if (!(tls = SSL_new(tls_ch_opts.tls_ctx))) { - ERR(NULL, "Failed to create new TLS session structure (%s).", ERR_reason_error_string(ERR_get_error())); - goto cleanup; - } - - SSL_set_fd(tls, sock); - - /* set the SSL_MODE_AUTO_RETRY flag to allow OpenSSL perform re-handshake automatically */ - SSL_set_mode(tls, SSL_MODE_AUTO_RETRY); - - /* connect and perform the handshake */ - if (timeout > -1) { - nc_timeouttime_get(&ts_timeout, timeout); - } - tlsauth_ch = 1; - while (((ret = SSL_connect(tls)) == -1) && (SSL_get_error(tls, ret) == SSL_ERROR_WANT_READ)) { - usleep(NC_TIMEOUT_STEP); - if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) { - ERR(NULL, "SSL connect timeout."); - goto cleanup; - } - } - - /* check for errors */ - if (nc_client_tls_connect_check(ret, tls, peername ? peername : host) != 1) { - goto cleanup; - } - - /* connect */ - session = nc_connect_libssl(tls, ctx); - if (!session) { - goto cleanup; - } - session->flags |= NC_SESSION_CALLHOME; /* store information into session and the dictionary */ @@ -714,10 +521,8 @@ nc_accept_callhome_tls_sock(int sock, const char *host, uint16_t port, struct ly session->port = port; session->username = strdup("certificate-based"); -cleanup: - if (!session) { - SSL_free(tls); - close(sock); - } return session; + +fail: + return NULL; } From 0e44ecf0048ebd674c7dc5534823418d0fd79682 Mon Sep 17 00:00:00 2001 From: roman Date: Fri, 5 Apr 2024 12:33:09 +0200 Subject: [PATCH 15/54] session server UPDATE wrap TLS --- src/session_server.c | 15 ++++++++------- src/session_server.h | 12 ++++++------ 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/src/session_server.c b/src/session_server.c index 94dd5d50..f152cd60 100644 --- a/src/session_server.c +++ b/src/session_server.c @@ -33,11 +33,6 @@ #include #include -#ifdef NC_ENABLED_SSH_TLS -#include -#include -#endif - #include "compat.h" #include "config.h" #include "log_p.h" @@ -48,6 +43,12 @@ #include "session_p.h" #include "session_server.h" #include "session_server_ch.h" +#include "session_wrapper.h" + +#ifdef NC_ENABLED_SSH_TLS +#include +#include +#endif struct nc_server_opts server_opts = { .config_lock = PTHREAD_RWLOCK_INITIALIZER, @@ -1650,10 +1651,10 @@ nc_ps_poll_session_io(struct nc_session *session, int io_timeout, time_t now_mon } break; case NC_TI_OPENSSL: - r = SSL_pending(session->ti.tls); + r = nc_tls_have_pending_wrap(session->ti.tls.session); if (!r) { /* no data pending in the SSL buffer, poll fd */ - pfd.fd = SSL_get_rfd(session->ti.tls); + pfd.fd = nc_tls_get_fd_wrap(session); if (pfd.fd < 0) { sprintf(msg, "Internal error (%s:%d)", __FILE__, __LINE__); ret = NC_PSPOLL_ERROR; diff --git a/src/session_server.h b/src/session_server.h index d386b625..4fd2ce86 100644 --- a/src/session_server.h +++ b/src/session_server.h @@ -24,17 +24,17 @@ extern "C" { #include #include -#ifdef NC_ENABLED_SSH_TLS -# include +#include "config.h" +#include "netconf.h" +#include "session.h" + +#ifdef NC_ENABLED_SSH_TLS # include # include # include #endif /* NC_ENABLED_SSH_TLS */ -#include "netconf.h" -#include "session.h" - /** * @defgroup server_session Server Session * @ingroup server @@ -553,7 +553,7 @@ int nc_server_ssh_set_pam_conf_filename(const char *filename); * @param[in] session Session to get the information from. * @return Const session client certificate. */ -const X509 *nc_session_get_client_cert(const struct nc_session *session); +const void *nc_session_get_client_cert(const struct nc_session *session); /** * @brief Set TLS authentication additional verify callback. From d1c38a6dd9122acbfcba88b818bc4d1d6fc519e5 Mon Sep 17 00:00:00 2001 From: roman Date: Fri, 5 Apr 2024 12:33:24 +0200 Subject: [PATCH 16/54] session server ssh UPDATE wrap TLS --- src/session_server_ssh.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/session_server_ssh.c b/src/session_server_ssh.c index e73dfe22..8d808151 100644 --- a/src/session_server_ssh.c +++ b/src/session_server_ssh.c @@ -31,9 +31,6 @@ #include #include #include -#include -#include -#include #include #include #include @@ -47,6 +44,7 @@ #include "log_p.h" #include "session.h" #include "session_p.h" +#include "session_wrapper.h" extern struct nc_server_opts server_opts; @@ -1046,7 +1044,7 @@ nc_server_ssh_create_ssh_pubkey(const char *base64, ssh_key *key) *key = NULL; /* convert base64 to binary */ - if (nc_base64_to_bin(base64, &bin) == -1) { + if (nc_base64_decode_wrap(base64, &bin) == -1) { ERR(NULL, "Unable to decode base64."); ret = 1; goto cleanup; From 366666bba3db82e84529662b2c17292fd1a549f1 Mon Sep 17 00:00:00 2001 From: roman Date: Fri, 5 Apr 2024 12:33:39 +0200 Subject: [PATCH 17/54] session server tls UPDATE wrap TLS --- src/session_server_tls.c | 1469 +++++++++++++------------------------- 1 file changed, 481 insertions(+), 988 deletions(-) diff --git a/src/session_server_tls.c b/src/session_server_tls.c index 2b562b1b..51edfd31 100644 --- a/src/session_server_tls.c +++ b/src/session_server_tls.c @@ -24,287 +24,263 @@ #include #include -#include -#include -#include -#include -#include -#include #include "compat.h" #include "config.h" #include "log_p.h" #include "session.h" #include "session_p.h" +#include "session_wrapper.h" struct nc_server_tls_opts tls_ch_opts; extern struct nc_server_opts server_opts; -static char * -asn1time_to_str(const ASN1_TIME *t) +static int +nc_server_tls_ks_ref_get_cert_key(const char *referenced_key_name, const char *referenced_cert_name, + char **privkey_data, NC_PRIVKEY_FORMAT *privkey_type, char **cert_data) { - char *cp; - BIO *bio; - int n; + uint16_t i, j; + struct nc_keystore *ks = &server_opts.keystore; - if (!t) { - return NULL; - } + *privkey_data = NULL; + *cert_data = NULL; - bio = BIO_new(BIO_s_mem()); - if (!bio) { - return NULL; + /* lookup name */ + for (i = 0; i < ks->asym_key_count; i++) { + if (!strcmp(referenced_key_name, ks->asym_keys[i].name)) { + break; + } } - - ASN1_TIME_print(bio, t); - n = BIO_pending(bio); - cp = malloc(n + 1); - if (!cp) { - ERRMEM; - BIO_free(bio); - return NULL; + if (i == ks->asym_key_count) { + ERR(NULL, "Keystore entry \"%s\" not found.", referenced_key_name); + return -1; } - n = BIO_read(bio, cp, n); - if (n < 0) { - BIO_free(bio); - free(cp); - return NULL; + for (j = 0; j < ks->asym_keys[i].cert_count; j++) { + if (!strcmp(referenced_cert_name, ks->asym_keys[i].certs[j].name)) { + break; + } + } + if (j == ks->asym_keys[i].cert_count) { + ERR(NULL, "Keystore certificate entry \"%s\" associated with the key \"%s\" not found.", + referenced_cert_name, referenced_key_name); + return -1; } - cp[n] = '\0'; - BIO_free(bio); - return cp; + *privkey_data = ks->asym_keys[i].privkey_data; + *privkey_type = ks->asym_keys[i].privkey_type; + *cert_data = ks->asym_keys[i].certs[j].data; + return 0; } -static void -digest_to_str(const unsigned char *digest, unsigned int dig_len, char **str) +static int +nc_server_tls_ts_ref_get_certs(const char *referenced_name, struct nc_certificate **certs, uint16_t *cert_count) { - unsigned int i; + uint16_t i; + struct nc_truststore *ts = &server_opts.truststore; - *str = malloc(dig_len * 3); - if (!*str) { - ERRMEM; - return; + *certs = NULL; + *cert_count = 0; + + /* lookup name */ + for (i = 0; i < ts->cert_bag_count; i++) { + if (!strcmp(referenced_name, ts->cert_bags[i].name)) { + break; + } } - for (i = 0; i < dig_len - 1; ++i) { - sprintf((*str) + (i * 3), "%02x:", digest[i]); + + if (i == ts->cert_bag_count) { + ERR(NULL, "Truststore entry \"%s\" not found.", referenced_name); + return -1; } - sprintf((*str) + (i * 3), "%02x", digest[i]); + + *certs = ts->cert_bags[i].certs; + *cert_count = ts->cert_bags[i].cert_count; + return 0; } -/* return NULL - SSL error can be retrieved */ -static X509 * -base64der_to_cert(const char *in) +static void * +nc_base64der_to_cert(const char *in) { - X509 *out; char *buf; - BIO *bio; + void *cert = NULL; - if (in == NULL) { - return NULL; - } + NC_CHECK_ARG_RET(NULL, in, NULL); if (asprintf(&buf, "%s%s%s", "-----BEGIN CERTIFICATE-----\n", in, "\n-----END CERTIFICATE-----") == -1) { - return NULL; - } - bio = BIO_new_mem_buf(buf, strlen(buf)); - if (!bio) { - free(buf); + ERRMEM; return NULL; } - out = PEM_read_bio_X509(bio, NULL, NULL, NULL); - if (!out) { - free(buf); - BIO_free(bio); - return NULL; + cert = nc_tls_pem_to_cert_wrap(buf); + free(buf); + return cert; +} + +int +nc_base64der_to_cert_add_to_store(const char *in, void *cert_store) +{ + int ret; + char *buf; + + NC_CHECK_ARG_RET(NULL, in, cert_store, 1); + + if (asprintf(&buf, "%s%s%s", "-----BEGIN CERTIFICATE-----\n", in, "\n-----END CERTIFICATE-----") == -1) { + ERRMEM; + return 1; } + ret = nc_tls_pem_to_cert_add_to_store_wrap(buf, cert_store); free(buf); - BIO_free(bio); - return out; + return ret; } -static EVP_PKEY * -base64der_to_privatekey(const char *in, const char *key_str) +static void * +nc_base64der_to_privatekey(const char *in, const char *key_str) { - EVP_PKEY *out; char *buf; - BIO *bio; + void *pkey; - if (in == NULL) { + NC_CHECK_ARG_RET(NULL, in, NULL); + + if (asprintf(&buf, "%s%s%s%s%s%s%s", "-----BEGIN ", key_str, " PRIVATE KEY-----\n", in, "\n-----END ", + key_str, " PRIVATE KEY-----") == -1) { + ERRMEM; return NULL; } - if (!key_str) { - /* avoid writing (null) for possibly unknown key formats */ - key_str = ""; + pkey = nc_tls_pem_to_privkey_wrap(buf); + free(buf); + return pkey; +} + +char * +nc_server_tls_digest_to_hex(const unsigned char *digest, unsigned int digest_len) +{ + unsigned int i; + char *hex; + + hex = malloc(digest_len * 3); + NC_CHECK_ERRMEM_RET(!hex, NULL); + + for (i = 0; i < digest_len - 1; ++i) { + sprintf(hex + (i * 3), "%02x:", digest[i]); } - if (asprintf(&buf, "%s%s%s%s%s%s%s", "-----BEGIN", key_str, "PRIVATE KEY-----\n", in, "\n-----END", - key_str, "PRIVATE KEY-----") == -1) { + sprintf(hex + (i * 3), "%02x", digest[i]); + + return hex; +} + +char * +nc_server_tls_md5(void *cert) +{ + int rc; + unsigned int buf_len = 16; + unsigned char buf[buf_len]; + + /* compute MD-5 hash of cert and store it in buf */ + rc = nc_server_tls_md5_wrap(cert, buf); + if (rc) { return NULL; } - bio = BIO_new_mem_buf(buf, strlen(buf)); - if (!bio) { - free(buf); + + /* convert the hash to hex */ + return nc_server_tls_digest_to_hex(buf, buf_len); +} + +char * +nc_server_tls_sha1(void *cert) +{ + int rc; + unsigned int buf_len = 20; + unsigned char buf[buf_len]; + + /* compute SHA-1 hash of cert and store it in buf */ + rc = nc_server_tls_sha1_wrap(cert, buf); + if (rc) { return NULL; } - out = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); - if (!out) { - free(buf); - BIO_free(bio); + /* convert the hash to hex */ + return nc_server_tls_digest_to_hex(buf, buf_len); +} + +char * +nc_server_tls_sha224(void *cert) +{ + int rc; + unsigned int buf_len = 28; + unsigned char buf[buf_len]; + + /* compute SHA-224 hash of cert and store it in buf */ + rc = nc_server_tls_sha224_wrap(cert, buf); + if (rc) { return NULL; } - free(buf); - BIO_free(bio); - return out; + /* convert the hash to hex */ + return nc_server_tls_digest_to_hex(buf, buf_len); } -static int -cert_pubkey_match(X509 *cert1, X509 *cert2) +char * +nc_server_tls_sha256(void *cert) { - ASN1_BIT_STRING *bitstr1, *bitstr2; - - bitstr1 = X509_get0_pubkey_bitstr(cert1); - bitstr2 = X509_get0_pubkey_bitstr(cert2); + int rc; + unsigned int buf_len = 32; + unsigned char buf[buf_len]; - if (!bitstr1 || !bitstr2 || (bitstr1->length != bitstr2->length) || - memcmp(bitstr1->data, bitstr2->data, bitstr1->length)) { - return 0; + /* compute SHA-256 hash of cert and store it in buf */ + rc = nc_server_tls_sha256_wrap(cert, buf); + if (rc) { + return NULL; } - return 1; + /* convert the hash to hex */ + return nc_server_tls_digest_to_hex(buf, buf_len); } -static int -nc_tls_ctn_get_username_from_cert(X509 *client_cert, NC_TLS_CTN_MAPTYPE map_type, char **username) +char * +nc_server_tls_sha384(void *cert) { - STACK_OF(GENERAL_NAME) * san_names; - GENERAL_NAME *san_name; - ASN1_OCTET_STRING *ip; - int i, san_count; - char *subject, *common_name; - - *username = NULL; - - if (map_type == NC_TLS_CTN_COMMON_NAME) { - subject = X509_NAME_oneline(X509_get_subject_name(client_cert), NULL, 0); - common_name = strstr(subject, "CN="); - if (!common_name) { - WRN(NULL, "Certificate does not include the commonName field."); - free(subject); - return 1; - } - common_name += 3; - if (strchr(common_name, '/')) { - *strchr(common_name, '/') = '\0'; - } - *username = strdup(common_name); - NC_CHECK_ERRMEM_RET(!*username, 1); - free(subject); - } else { - /* retrieve subjectAltName's rfc822Name (email), dNSName and iPAddress values */ - san_names = X509_get_ext_d2i(client_cert, NID_subject_alt_name, NULL, NULL); - if (!san_names) { - WRN(NULL, "Certificate has no SANs or failed to retrieve them."); - return 1; - } + int rc; + unsigned int buf_len = 48; + unsigned char buf[buf_len]; - san_count = sk_GENERAL_NAME_num(san_names); - for (i = 0; i < san_count; ++i) { - san_name = sk_GENERAL_NAME_value(san_names, i); + /* compute SHA-384 hash of cert and store it in buf */ + rc = nc_server_tls_sha384_wrap(cert, buf); + if (rc) { + return NULL; + } - /* rfc822Name (email) */ - if (((map_type == NC_TLS_CTN_SAN_ANY) || (map_type == NC_TLS_CTN_SAN_RFC822_NAME)) && - (san_name->type == GEN_EMAIL)) { - *username = strdup((char *)ASN1_STRING_get0_data(san_name->d.rfc822Name)); - NC_CHECK_ERRMEM_RET(!*username, 1); - break; - } + /* convert the hash to hex */ + return nc_server_tls_digest_to_hex(buf, buf_len); +} - /* dNSName */ - if (((map_type == NC_TLS_CTN_SAN_ANY) || (map_type == NC_TLS_CTN_SAN_DNS_NAME)) && - (san_name->type == GEN_DNS)) { - *username = strdup((char *)ASN1_STRING_get0_data(san_name->d.dNSName)); - NC_CHECK_ERRMEM_RET(!*username, 1); - break; - } +char * +nc_server_tls_sha512(void *cert) +{ + int rc; + unsigned int buf_len = 64; + unsigned char buf[buf_len]; - /* iPAddress */ - if (((map_type == NC_TLS_CTN_SAN_ANY) || (map_type == NC_TLS_CTN_SAN_IP_ADDRESS)) && - (san_name->type == GEN_IPADD)) { - ip = san_name->d.iPAddress; - if (ip->length == 4) { - if (asprintf(username, "%d.%d.%d.%d", ip->data[0], ip->data[1], ip->data[2], ip->data[3]) == -1) { - ERRMEM; - sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free); - return -1; - } - break; - } else if (ip->length == 16) { - if (asprintf(username, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", - ip->data[0], ip->data[1], ip->data[2], ip->data[3], ip->data[4], ip->data[5], - ip->data[6], ip->data[7], ip->data[8], ip->data[9], ip->data[10], ip->data[11], - ip->data[12], ip->data[13], ip->data[14], ip->data[15]) == -1) { - ERRMEM; - sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free); - return -1; - } - break; - } else { - WRN(NULL, "SAN IP address in an unknown format (length is %d).", ip->length); - } - } - } - sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free); - - if (i == san_count) { - switch (map_type) { - case NC_TLS_CTN_SAN_RFC822_NAME: - WRN(NULL, "Certificate does not include the SAN rfc822Name field."); - break; - case NC_TLS_CTN_SAN_DNS_NAME: - WRN(NULL, "Certificate does not include the SAN dNSName field."); - break; - case NC_TLS_CTN_SAN_IP_ADDRESS: - WRN(NULL, "Certificate does not include the SAN iPAddress field."); - break; - case NC_TLS_CTN_SAN_ANY: - WRN(NULL, "Certificate does not include any relevant SAN fields."); - break; - default: - break; - } - return 1; - } + /* compute SHA-512 hash of cert and store it in buf */ + rc = nc_server_tls_sha512_wrap(cert, buf); + if (rc) { + return NULL; } - return 0; + /* convert the hash to hex */ + return nc_server_tls_digest_to_hex(buf, buf_len); } -/* return: 0 - OK, 1 - no match, -1 - error */ static int -nc_tls_cert_to_name(struct nc_session *session, struct nc_ctn *ctn_first, X509 *cert) +nc_server_tls_cert_to_name(struct nc_ctn *ctn_first, void *cert, struct nc_ctn_data *data) { + int ret = 0; char *digest_md5 = NULL, *digest_sha1 = NULL, *digest_sha224 = NULL; char *digest_sha256 = NULL, *digest_sha384 = NULL, *digest_sha512 = NULL; - unsigned char *buf; - unsigned int buf_len = 64; - int ret = 0; struct nc_ctn *ctn; NC_TLS_CTN_MAPTYPE map_type; - char *username = NULL; - - buf = malloc(buf_len); - NC_CHECK_ERRMEM_RET(!buf, -1); - - if (!session || !cert) { - free(buf); - return -1; - } for (ctn = ctn_first; ctn; ctn = ctn->next) { /* reset map_type */ @@ -312,7 +288,7 @@ nc_tls_cert_to_name(struct nc_session *session, struct nc_ctn *ctn_first, X509 * /* first make sure the entry is valid */ if (!ctn->map_type || ((ctn->map_type == NC_TLS_CTN_SPECIFIED) && !ctn->name)) { - VRB(session, "Cert verify CTN: entry with id %u not valid, skipping.", ctn->id); + VRB(NULL, "Cert verify CTN: entry with id %u not valid, skipping.", ctn->id); continue; } @@ -323,154 +299,117 @@ nc_tls_cert_to_name(struct nc_session *session, struct nc_ctn *ctn_first, X509 * /* MD5 */ } else if (!strncmp(ctn->fingerprint, "01", 2)) { if (!digest_md5) { - if (X509_digest(cert, EVP_md5(), buf, &buf_len) != 1) { - ERR(session, "Calculating MD5 digest failed (%s).", ERR_reason_error_string(ERR_get_error())); + digest_md5 = nc_server_tls_md5(cert); + if (!digest_md5) { ret = -1; goto cleanup; } - digest_to_str(buf, buf_len, &digest_md5); } if (!strcasecmp(ctn->fingerprint + 3, digest_md5)) { /* we got ourselves a potential winner! */ - VRB(session, "Cert verify CTN: entry with a matching fingerprint found."); + VRB(NULL, "Cert verify CTN: entry with a matching fingerprint found."); map_type = ctn->map_type; } - free(digest_md5); - digest_md5 = NULL; /* SHA-1 */ } else if (!strncmp(ctn->fingerprint, "02", 2)) { if (!digest_sha1) { - if (X509_digest(cert, EVP_sha1(), buf, &buf_len) != 1) { - ERR(session, "Calculating SHA-1 digest failed (%s).", ERR_reason_error_string(ERR_get_error())); + digest_sha1 = nc_server_tls_sha1(cert); + if (!digest_sha1) { ret = -1; goto cleanup; } - digest_to_str(buf, buf_len, &digest_sha1); } if (!strcasecmp(ctn->fingerprint + 3, digest_sha1)) { /* we got ourselves a potential winner! */ - VRB(session, "Cert verify CTN: entry with a matching fingerprint found."); + VRB(NULL, "Cert verify CTN: entry with a matching fingerprint found."); map_type = ctn->map_type; } - free(digest_sha1); - digest_sha1 = NULL; /* SHA-224 */ } else if (!strncmp(ctn->fingerprint, "03", 2)) { if (!digest_sha224) { - if (X509_digest(cert, EVP_sha224(), buf, &buf_len) != 1) { - ERR(session, "Calculating SHA-224 digest failed (%s).", ERR_reason_error_string(ERR_get_error())); + digest_sha224 = nc_server_tls_sha224(cert); + if (!digest_sha224) { ret = -1; goto cleanup; } - digest_to_str(buf, buf_len, &digest_sha224); } if (!strcasecmp(ctn->fingerprint + 3, digest_sha224)) { /* we got ourselves a potential winner! */ - VRB(session, "Cert verify CTN: entry with a matching fingerprint found."); + VRB(NULL, "Cert verify CTN: entry with a matching fingerprint found."); map_type = ctn->map_type; } - free(digest_sha224); - digest_sha224 = NULL; /* SHA-256 */ } else if (!strncmp(ctn->fingerprint, "04", 2)) { if (!digest_sha256) { - if (X509_digest(cert, EVP_sha256(), buf, &buf_len) != 1) { - ERR(session, "Calculating SHA-256 digest failed (%s).", ERR_reason_error_string(ERR_get_error())); + digest_sha256 = nc_server_tls_sha256(cert); + if (!digest_sha256) { ret = -1; goto cleanup; } - digest_to_str(buf, buf_len, &digest_sha256); } if (!strcasecmp(ctn->fingerprint + 3, digest_sha256)) { /* we got ourselves a potential winner! */ - VRB(session, "Cert verify CTN: entry with a matching fingerprint found."); + VRB(NULL, "Cert verify CTN: entry with a matching fingerprint found."); map_type = ctn->map_type; } - free(digest_sha256); - digest_sha256 = NULL; /* SHA-384 */ } else if (!strncmp(ctn->fingerprint, "05", 2)) { if (!digest_sha384) { - if (X509_digest(cert, EVP_sha384(), buf, &buf_len) != 1) { - ERR(session, "Calculating SHA-384 digest failed (%s).", ERR_reason_error_string(ERR_get_error())); + digest_sha384 = nc_server_tls_sha384(cert); + if (!digest_sha384) { ret = -1; goto cleanup; } - digest_to_str(buf, buf_len, &digest_sha384); } if (!strcasecmp(ctn->fingerprint + 3, digest_sha384)) { /* we got ourselves a potential winner! */ - VRB(session, "Cert verify CTN: entry with a matching fingerprint found."); + VRB(NULL, "Cert verify CTN: entry with a matching fingerprint found."); map_type = ctn->map_type; } - free(digest_sha384); - digest_sha384 = NULL; /* SHA-512 */ } else if (!strncmp(ctn->fingerprint, "06", 2)) { if (!digest_sha512) { - if (X509_digest(cert, EVP_sha512(), buf, &buf_len) != 1) { - ERR(session, "Calculating SHA-512 digest failed (%s).", ERR_reason_error_string(ERR_get_error())); + digest_sha512 = nc_server_tls_sha512(cert); + if (!digest_sha512) { ret = -1; goto cleanup; } - digest_to_str(buf, buf_len, &digest_sha512); } if (!strcasecmp(ctn->fingerprint + 3, digest_sha512)) { /* we got ourselves a potential winner! */ - VRB(session, "Cert verify CTN: entry with a matching fingerprint found."); + VRB(NULL, "Cert verify CTN: entry with a matching fingerprint found."); map_type = ctn->map_type; } - free(digest_sha512); - digest_sha512 = NULL; /* unknown */ } else { - WRN(session, "Unknown fingerprint algorithm used (%s), skipping.", ctn->fingerprint); + WRN(NULL, "Unknown fingerprint algorithm used (%s), skipping.", ctn->fingerprint); continue; } if (map_type != NC_TLS_CTN_UNKNOWN) { /* found a fingerprint match */ - if (map_type == NC_TLS_CTN_SPECIFIED) { - /* specified -> get username from the ctn entry */ - session->username = strdup(ctn->name); - NC_CHECK_ERRMEM_GOTO(!session->username, ret = -1, cleanup); - } else { - /* try to get the username from the cert with this ctn's map type */ - ret = nc_tls_ctn_get_username_from_cert(session->opts.server.client_cert, map_type, &username); - if (ret == -1) { - /* fatal error */ - goto cleanup; - } else if (ret) { - /* didn't get username, try next ctn entry */ - continue; + if (!(map_type & data->matched_ctns)) { + data->matched_ctns |= map_type; + data->matched_ctn_type[data->matched_ctn_count++] = map_type; + if (!data->username && map_type == NC_TLS_CTN_SPECIFIED) { + data->username = ctn->name; // TODO make a copy? } - - /* success */ - session->username = username; } - - /* matching fingerprint found and username obtained, success */ - ret = 0; - goto cleanup; } } - if (!ctn) { - ret = 1; - } - cleanup: free(digest_md5); free(digest_sha1); @@ -478,160 +417,17 @@ nc_tls_cert_to_name(struct nc_session *session, struct nc_ctn *ctn_first, X509 * free(digest_sha256); free(digest_sha384); free(digest_sha512); - free(buf); - return ret; -} - -static int -nc_server_tls_check_crl(X509_STORE *crl_store, X509_STORE_CTX *x509_ctx, X509 *cert, - const X509_NAME *subject, const X509_NAME *issuer) -{ - int n, i, ret = 0; - X509_STORE_CTX *store_ctx = NULL; - X509_OBJECT *obj = NULL; - X509_CRL *crl; - X509_REVOKED *revoked; - EVP_PKEY *pubkey; - const ASN1_INTEGER *serial; - const ASN1_TIME *last_update = NULL, *next_update = NULL; - char *cp; - - store_ctx = X509_STORE_CTX_new(); - NC_CHECK_ERRMEM_GOTO(!store_ctx, ret = -1, cleanup); - - /* init store context */ - ret = X509_STORE_CTX_init(store_ctx, crl_store, NULL, NULL); - if (!ret) { - ERR(NULL, "Initializing x509 store ctx failed (%s).", ERR_reason_error_string(ERR_get_error())); - ret = -1; - goto cleanup; - } - ret = 0; - - /* try to find a CRL entry that corresponds to the current certificate in question */ - obj = X509_STORE_CTX_get_obj_by_subject(store_ctx, X509_LU_CRL, subject); - crl = X509_OBJECT_get0_X509_CRL(obj); - X509_OBJECT_free(obj); - if (crl) { - /* found it */ - cp = X509_NAME_oneline(subject, NULL, 0); - VRB(NULL, "Cert verify CRL: issuer: %s.", cp); - OPENSSL_free(cp); - - last_update = X509_CRL_get0_lastUpdate(crl); - next_update = X509_CRL_get0_nextUpdate(crl); - cp = asn1time_to_str(last_update); - VRB(NULL, "Cert verify CRL: last update: %s.", cp); - free(cp); - cp = asn1time_to_str(next_update); - VRB(NULL, "Cert verify CRL: next update: %s.", cp); - free(cp); - - /* verify the signature on this CRL */ - pubkey = X509_get0_pubkey(cert); - if (X509_CRL_verify(crl, pubkey) <= 0) { - ERR(NULL, "Cert verify CRL: invalid signature."); - X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_SIGNATURE_FAILURE); - ret = -1; - goto cleanup; - } - - /* check date of CRL to make sure it's not expired */ - if (!next_update) { - ERR(NULL, "Cert verify CRL: invalid nextUpdate field."); - X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD); - ret = -1; - goto cleanup; - } - - if (X509_cmp_current_time(next_update) < 0) { - ERR(NULL, "Cert verify CRL: expired - revoking all certificates."); - X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CRL_HAS_EXPIRED); - ret = -1; - goto cleanup; - } - } - - /* try to retrieve a CRL corresponding to the _issuer_ of - * the current certificate in order to check for revocation */ - obj = X509_STORE_CTX_get_obj_by_subject(store_ctx, X509_LU_CRL, issuer); - crl = X509_OBJECT_get0_X509_CRL(obj); - if (crl) { - n = sk_X509_REVOKED_num(X509_CRL_get_REVOKED(crl)); - for (i = 0; i < n; i++) { - revoked = sk_X509_REVOKED_value(X509_CRL_get_REVOKED(crl), i); - serial = X509_REVOKED_get0_serialNumber(revoked); - if (ASN1_INTEGER_cmp(serial, X509_get_serialNumber(cert)) == 0) { - cp = X509_NAME_oneline(issuer, NULL, 0); - ERR(NULL, "Cert verify CRL: certificate with serial %ld (0x%lX) revoked per CRL from issuer %s.", - serial, serial, cp); - OPENSSL_free(cp); - X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_CERT_REVOKED); - ret = -1; - goto cleanup; - } - } - } - -cleanup: - X509_STORE_CTX_free(store_ctx); - X509_OBJECT_free(obj); return ret; } static int -nc_server_tls_ts_ref_get_certs(const char *referenced_name, struct nc_certificate **certs, uint16_t *cert_count) -{ - uint16_t i; - struct nc_truststore *ts = &server_opts.truststore; - - *certs = NULL; - *cert_count = 0; - - /* lookup name */ - for (i = 0; i < ts->cert_bag_count; i++) { - if (!strcmp(referenced_name, ts->cert_bags[i].name)) { - break; - } - } - - if (i == ts->cert_bag_count) { - ERR(NULL, "Truststore entry \"%s\" not found.", referenced_name); - return -1; - } - - *certs = ts->cert_bags[i].certs; - *cert_count = ts->cert_bags[i].cert_count; - return 0; -} - -/* In case a CA chain verification failed an end-entity certificate must match. - * The meaning of local_or_referenced is that it states, which end-entity certificates to check - * (1 = current endpoint's, 2 = referenced endpoint's). - */ -static int -nc_server_tls_do_preverify(struct nc_session *session, X509_STORE_CTX *x509_ctx, int local_or_referenced) +nc_server_tls_verify_peer_cert(void *peer_cert, struct nc_cert_grouping *ee_certs) { - X509_STORE *store; - struct nc_cert_grouping *ee_certs; int i, ret; - X509 *cert; + void *cert; struct nc_certificate *certs; uint16_t cert_count; - store = X509_STORE_CTX_get0_store(x509_ctx); - if (!store) { - ERR(session, "Error getting store from context (%s).", ERR_reason_error_string(ERR_get_error())); - return -1; - } - - /* get the data from the store */ - ee_certs = X509_STORE_get_ex_data(store, local_or_referenced); - if (!ee_certs) { - ERR(session, "Error getting data from store (%s).", ERR_reason_error_string(ERR_get_error())); - return -1; - } - if (ee_certs->store == NC_STORE_LOCAL) { /* local definition */ certs = ee_certs->certs; @@ -645,165 +441,117 @@ nc_server_tls_do_preverify(struct nc_session *session, X509_STORE_CTX *x509_ctx, } for (i = 0; i < cert_count; i++) { - cert = base64der_to_cert(certs[i].data); - ret = cert_pubkey_match(session->opts.server.client_cert, cert); - X509_free(cert); + cert = nc_tls_base64_to_cert_wrap(certs[i].data); + if (!cert) { + /* TODO skip? */ + continue; + } + ret = nc_server_tls_certs_match_wrap(peer_cert, cert); + nc_tls_cert_destroy_wrap(cert); if (ret) { - /* we are just overriding the failed standard certificate verification (preverify_ok == 0), - * this callback will be called again with the same current certificate and preverify_ok == 1 */ - VRB(session, "Cert verify: fail (%s), but the end-entity certificate is trusted, continuing.", - X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509_ctx))); - X509_STORE_CTX_set_error(x509_ctx, X509_V_OK); - return 1; + /* found a match */ + VRB(NULL, "Cert verify: fail, but the end-entity certificate is trusted, continuing."); + return 0; } } - return 0; + return 1; } -static int -nc_tlsclb_verify(int preverify_ok, X509_STORE_CTX *x509_ctx) +int +nc_server_tls_verify_cert(void *cert, int depth, int self_signed, struct nc_tls_verify_cb_data *cb_data) { - X509_NAME *subject; - X509_NAME *issuer; - X509 *cert; - char *cp; - X509_STORE *store; - - STACK_OF(X509) * cert_stack; - struct nc_session *session; - struct nc_server_tls_opts *opts; + int ret = 0, i; + char *subject = NULL, *issuer = NULL; + struct nc_server_tls_opts *opts = cb_data->opts; + struct nc_session *session = cb_data->session; struct nc_endpt *referenced_endpt; - int rc, depth; - - store = X509_STORE_CTX_get0_store(x509_ctx); - if (!store) { - ERR(NULL, "Error getting store from context (%s).", ERR_reason_error_string(ERR_get_error())); - return 0; - } - - /* get session from the store */ - session = X509_STORE_get_ex_data(store, 0); - if (!session) { - ERR(session, "Error getting session from store (%s).", ERR_reason_error_string(ERR_get_error())); - return 0; - } - - opts = session->data; - - /* get the last certificate, that is the peer (client) certificate */ - if (!session->opts.server.client_cert) { - cert_stack = X509_STORE_CTX_get1_chain(x509_ctx); - session->opts.server.client_cert = sk_X509_value(cert_stack, 0); - X509_up_ref(session->opts.server.client_cert); - sk_X509_pop_free(cert_stack, X509_free); - } - - /* standard certificate verification failed, so an end-entity client cert must match to continue */ - if (!preverify_ok) { - /* check current endpoint's end-entity certs */ - rc = nc_server_tls_do_preverify(session, x509_ctx, 1); - if (rc == -1) { - return 0; - } else if (rc == 1) { - return 1; - } - - /* no match, continue */ - if (opts->referenced_endpt_name) { - /* check referenced endpoint's end-entity certs */ - rc = nc_server_tls_do_preverify(session, x509_ctx, 2); - if (rc == -1) { - return 0; - } else if (rc == 1) { - return 1; - } - } - /* no match, fail */ - ERR(session, "Cert verify: fail (%s).", X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509_ctx))); - return 0; + subject = nc_server_tls_get_subject_wrap(cert); + issuer = nc_server_tls_get_issuer_wrap(cert); + if (!subject || !issuer) { + ERR(session, "Failed to get certificate's subject or issuer."); + ret = -1; + goto cleanup; } - /* print cert verify info */ - depth = X509_STORE_CTX_get_error_depth(x509_ctx); VRB(session, "Cert verify: depth %d.", depth); + VRB(session, "Cert verify: subject: %s.", subject); + VRB(session, "Cert verify: issuer: %s.", issuer); + + if (depth == 0) { + if (self_signed) { + /* peer cert is not trusted, so it must match any configured end-entity cert + * on the given endpoint in order for the cert to be authenticated */ + ret = nc_server_tls_verify_peer_cert(cert, &opts->ee_certs); + if (ret) { + /* we can still check the referenced endpoint's ee certs */ + if (opts->referenced_endpt_name) { + if (nc_server_get_referenced_endpt(opts->referenced_endpt_name, &referenced_endpt)) { + ERRINT; + ret = -1; + goto cleanup; + } - cert = X509_STORE_CTX_get_current_cert(x509_ctx); - subject = X509_get_subject_name(cert); - issuer = X509_get_issuer_name(cert); - - cp = X509_NAME_oneline(subject, NULL, 0); - VRB(session, "Cert verify: subject: %s.", cp); - OPENSSL_free(cp); - cp = X509_NAME_oneline(issuer, NULL, 0); - VRB(session, "Cert verify: issuer: %s.", cp); - OPENSSL_free(cp); - - /* check if the current certificate is revoked if CRL is set */ - if (opts->crl_store) { - rc = nc_server_tls_check_crl(opts->crl_store, x509_ctx, cert, subject, issuer); - if (rc) { - return 0; + ret = nc_server_tls_verify_peer_cert(cert, &referenced_endpt->opts.tls->ee_certs); + } + if (ret) { + ERR(session, "Cert verify: fail (Client certificate not trusted and does not match any configured end-entity certificate)."); + goto cleanup; + } + } } } - /* cert-to-name already successful */ - if (session->username) { - return 1; + ret = nc_server_tls_cert_to_name(opts->ctn, cert, &cb_data->ctn_data); + if (ret == -1) { + /* fatal error */ + goto cleanup; } - /* cert-to-name */ - rc = nc_tls_cert_to_name(session, opts->ctn, cert); - if (rc == -1) { - /* fatal error */ - depth = 0; - goto fail; - } else if ((rc == 1) && !opts->referenced_endpt_name) { - /* no match found and no referenced endpoint */ - goto fail; - } else if ((rc == 1) && opts->referenced_endpt_name) { - /* no match found, but has a referenced endpoint so try it */ + /* check the referenced endpoint's ctn entries */ + if (opts->referenced_endpt_name) { if (nc_server_get_referenced_endpt(opts->referenced_endpt_name, &referenced_endpt)) { - /* fatal error */ ERRINT; - depth = 0; - goto fail; + ret = -1; + goto cleanup; } - rc = nc_tls_cert_to_name(session, referenced_endpt->opts.tls->ctn, cert); - if (rc) { - if (rc == -1) { - /* fatal error */ - depth = 0; - } - /* rc == 1 is a normal CTN fail (no match found) */ - goto fail; + ret = nc_server_tls_cert_to_name(referenced_endpt->opts.tls->ctn, cert, &cb_data->ctn_data); + if (ret == -1) { + /* fatal error */ + goto cleanup; } } - VRB(session, "Cert verify CTN: new client username recognized as \"%s\".", session->username); - - if (server_opts.user_verify_clb && !server_opts.user_verify_clb(session)) { - VRB(session, "Cert verify: user verify callback revoked authorization."); - X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_APPLICATION_VERIFICATION); - return 0; + /* ctn */ + if (depth == 0) { + for (i = 0; i < cb_data->ctn_data.matched_ctn_count; i++) { + if (cb_data->ctn_data.matched_ctn_type[i] == NC_TLS_CTN_SPECIFIED) { + session->username = strdup(cb_data->ctn_data.username); + NC_CHECK_ERRMEM_RET(!session->username, -1); + } else { + ret = nc_server_tls_get_username_from_cert_wrap(cert, cb_data->ctn_data.matched_ctn_type[i], &session->username); + if (ret == -1) { + goto cleanup; + } + } + } } - return 1; - -fail: - if (depth > 0) { - VRB(session, "Cert verify CTN: cert fail, cert-to-name will continue on the next cert in chain."); - return 1; + if (server_opts.user_verify_clb && !server_opts.user_verify_clb(session)) { + VRB(session, "Cert verify: user verify callback revoked authorization."); + ret = -1; + goto cleanup; } - VRB(session, "Cert-to-name unsuccessful, dropping the new client."); - X509_STORE_CTX_set_error(x509_ctx, X509_V_ERR_APPLICATION_VERIFICATION); - return 0; +cleanup: + free(subject); + free(issuer); + return ret; } -API const X509 * +API const void * nc_session_get_client_cert(const struct nc_session *session) { if (!session || (session->side != NC_SERVER)) { @@ -820,54 +568,13 @@ nc_server_tls_set_verify_clb(int (*verify_clb)(const struct nc_session *session) server_opts.user_verify_clb = verify_clb; } -static int -nc_server_tls_ks_ref_get_cert_key(const char *referenced_key_name, const char *referenced_cert_name, - char **privkey_data, NC_PRIVKEY_FORMAT *privkey_type, char **cert_data) -{ - uint16_t i, j; - struct nc_keystore *ks = &server_opts.keystore; - - *privkey_data = NULL; - *cert_data = NULL; - - /* lookup name */ - for (i = 0; i < ks->asym_key_count; i++) { - if (!strcmp(referenced_key_name, ks->asym_keys[i].name)) { - break; - } - } - if (i == ks->asym_key_count) { - ERR(NULL, "Keystore entry \"%s\" not found.", referenced_key_name); - return -1; - } - - for (j = 0; j < ks->asym_keys[i].cert_count; j++) { - if (!strcmp(referenced_cert_name, ks->asym_keys[i].certs[j].name)) { - break; - } - } - if (j == ks->asym_keys[i].cert_count) { - ERR(NULL, "Keystore certificate entry \"%s\" associated with the key \"%s\" not found.", - referenced_cert_name, referenced_key_name); - return -1; - } - - *privkey_data = ks->asym_keys[i].privkey_data; - *privkey_type = ks->asym_keys[i].privkey_type; - *cert_data = ks->asym_keys[i].certs[j].data; - return 0; -} - -static int -nc_tls_ctx_set_server_cert_key(SSL_CTX *tls_ctx, struct nc_server_tls_opts *opts) +int +nc_server_tls_load_server_cert_key(struct nc_server_tls_opts *opts, void **srv_cert, void **srv_pkey) { char *privkey_data = NULL, *cert_data = NULL; - int ret = 0; NC_PRIVKEY_FORMAT privkey_type; - X509 *cert = NULL; - EVP_PKEY *pkey = NULL; - - NC_CHECK_ARG_RET(NULL, tls_ctx, opts, -1); + void *cert = NULL; + void *pkey = NULL; /* get data needed for setting the server cert */ if (opts->store == NC_STORE_LOCAL) { @@ -877,10 +584,9 @@ nc_tls_ctx_set_server_cert_key(SSL_CTX *tls_ctx, struct nc_server_tls_opts *opts privkey_type = opts->privkey_type; } else { /* keystore */ - ret = nc_server_tls_ks_ref_get_cert_key(opts->key_ref, opts->cert_ref, &privkey_data, &privkey_type, &cert_data); - if (ret) { + if (nc_server_tls_ks_ref_get_cert_key(opts->key_ref, opts->cert_ref, &privkey_data, &privkey_type, &cert_data)) { ERR(NULL, "Getting server certificate from the keystore reference \"%s\" failed.", opts->key_ref); - return -1; + return 1; } } if (!cert_data || !privkey_data) { @@ -889,143 +595,22 @@ nc_tls_ctx_set_server_cert_key(SSL_CTX *tls_ctx, struct nc_server_tls_opts *opts goto cleanup; } - /* load the cert */ - cert = base64der_to_cert(cert_data); + cert = nc_base64der_to_cert(cert_data); if (!cert) { - ERR(NULL, "Converting certificate data to certificate format failed."); - ret = -1; - goto cleanup; - } - - /* set server cert */ - ret = SSL_CTX_use_certificate(tls_ctx, cert); - if (ret != 1) { - ERR(NULL, "Loading the server certificate failed (%s).", ERR_reason_error_string(ERR_get_error())); - ret = -1; - goto cleanup; + return 1; } - /* load the private key */ - pkey = base64der_to_privatekey(privkey_data, nc_privkey_format_to_str(privkey_type)); + pkey = nc_base64der_to_privatekey(privkey_data, nc_privkey_format_to_str(privkey_type)); if (!pkey) { - ERR(NULL, "Converting private key data to private key format failed."); - ret = -1; - goto cleanup; - } - - /* set server key */ - ret = SSL_CTX_use_PrivateKey(tls_ctx, pkey); - if (ret != 1) { - ERR(NULL, "Loading the server private key failed (%s).", ERR_reason_error_string(ERR_get_error())); - ret = -1; - goto cleanup; - } - - ret = 0; - -cleanup: - X509_free(cert); - EVP_PKEY_free(pkey); - return ret; -} - -static int -tls_store_add_trusted_cert(X509_STORE *cert_store, const char *cert_data) -{ - X509 *cert; - - cert = base64der_to_cert(cert_data); - - if (!cert) { - ERR(NULL, "Loading a trusted certificate (data \"%s\") failed (%s).", cert_data, - ERR_reason_error_string(ERR_get_error())); - return -1; - } - - /* add the trusted certificate */ - if (X509_STORE_add_cert(cert_store, cert) != 1) { - ERR(NULL, "Adding a trusted certificate failed (%s).", ERR_reason_error_string(ERR_get_error())); - X509_free(cert); - return -1; - } - X509_free(cert); - - return 0; -} - -static int -nc_tls_store_set_trusted_certs(X509_STORE *cert_store, struct nc_cert_grouping *ca_certs) -{ - uint16_t i; - struct nc_certificate *certs; - uint16_t cert_count; - - if (ca_certs->store == NC_STORE_LOCAL) { - /* local definition */ - certs = ca_certs->certs; - cert_count = ca_certs->cert_count; - } else { - /* truststore */ - if (nc_server_tls_ts_ref_get_certs(ca_certs->ts_ref, &certs, &cert_count)) { - ERR(NULL, "Error getting certificate-authority certificates from the truststore reference \"%s\".", ca_certs->ts_ref); - return -1; - } - } - - for (i = 0; i < cert_count; i++) { - if (tls_store_add_trusted_cert(cert_store, certs[i].data)) { - return -1; - } + nc_tls_cert_destroy_wrap(cert); + return 1; } + *srv_cert = cert; + *srv_pkey = pkey; return 0; } -static int -nc_server_tls_crl_path(struct nc_session *session, const char *crl_path, X509_STORE *store) -{ - int ret = 0; - X509_CRL *crl = NULL; - FILE *f; - - f = fopen(crl_path, "r"); - if (!f) { - ERR(session, "Unable to open CRL file \"%s\".", crl_path); - return -1; - } - - /* try DER first */ - crl = d2i_X509_CRL_fp(f, NULL); - if (crl) { - /* success */ - goto ok; - } - - /* DER failed, try PEM */ - rewind(f); - crl = PEM_read_X509_CRL(f, NULL, NULL, NULL); - if (!crl) { - ERR(session, "Reading CRL from file \"%s\" failed.", crl_path); - ret = -1; - goto cleanup; - } - -ok: - ret = X509_STORE_add_crl(store, crl); - if (!ret) { - ERR(session, "Error adding CRL to store (%s).", ERR_reason_error_string(ERR_get_error())); - ret = -1; - goto cleanup; - } - /* ok */ - ret = 0; - -cleanup: - fclose(f); - X509_CRL_free(crl); - return ret; -} - static size_t nc_server_tls_curl_cb(char *ptr, size_t size, size_t nmemb, void *userdata) { @@ -1045,25 +630,25 @@ nc_server_tls_curl_cb(char *ptr, size_t size, size_t nmemb, void *userdata) } static int -nc_server_tls_curl_init(struct nc_session *session, CURL **handle, struct nc_curl_data *data) +nc_server_tls_curl_fetch(CURL *handle, const char *url) { - NC_CHECK_ARG_RET(session, handle, data, -1); - - *handle = NULL; + char err_buf[CURL_ERROR_SIZE]; - *handle = curl_easy_init(); - if (!*handle) { - ERR(session, "Initializing CURL failed."); + /* set uri */ + if (curl_easy_setopt(handle, CURLOPT_URL, url)) { + ERR(NULL, "Setting URI \"%s\" to download CRL from failed.", url); return -1; } - if (curl_easy_setopt(*handle, CURLOPT_WRITEFUNCTION, nc_server_tls_curl_cb)) { - ERR(session, "Setting curl callback failed."); + /* set err buf */ + if (curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, err_buf)) { + ERR(NULL, "Setting CURL error buffer option failed."); return -1; } - if (curl_easy_setopt(*handle, CURLOPT_WRITEDATA, data)) { - ERR(session, "Setting curl callback data failed."); + /* download */ + if (curl_easy_perform(handle)) { + ERR(NULL, "Downloading CRL from \"%s\" failed (%s).", url, err_buf); return -1; } @@ -1071,25 +656,25 @@ nc_server_tls_curl_init(struct nc_session *session, CURL **handle, struct nc_cur } static int -nc_server_tls_curl_fetch(struct nc_session *session, CURL *handle, const char *url) +nc_server_tls_curl_init(CURL **handle, struct nc_curl_data *data) { - char err_buf[CURL_ERROR_SIZE]; + NC_CHECK_ARG_RET(NULL, handle, data, -1); - /* set uri */ - if (curl_easy_setopt(handle, CURLOPT_URL, url)) { - ERR(session, "Setting URI \"%s\" to download CRL from failed.", url); + *handle = NULL; + + *handle = curl_easy_init(); + if (!*handle) { + ERR(NULL, "Initializing CURL failed."); return -1; } - /* set err buf */ - if (curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, err_buf)) { - ERR(session, "Setting CURL error buffer option failed."); + if (curl_easy_setopt(*handle, CURLOPT_WRITEFUNCTION, nc_server_tls_curl_cb)) { + ERR(NULL, "Setting curl callback failed."); return -1; } - /* download */ - if (curl_easy_perform(handle)) { - ERR(session, "Downloading CRL from \"%s\" failed (%s).", url, err_buf); + if (curl_easy_setopt(*handle, CURLOPT_WRITEDATA, data)) { + ERR(NULL, "Setting curl callback data failed."); return -1; } @@ -1097,75 +682,73 @@ nc_server_tls_curl_fetch(struct nc_session *session, CURL *handle, const char *u } static int -nc_server_tls_add_crl_to_store(struct nc_session *session, struct nc_curl_data *downloaded, X509_STORE *store) +nc_server_tls_crl_cert_ext(void *cert_store, void *crl_store) { int ret = 0; - X509_CRL *crl = NULL; - BIO *bio = NULL; - - /* try DER first */ - crl = d2i_X509_CRL(NULL, (const unsigned char **) &downloaded->data, downloaded->size); - if (crl) { - /* it was DER */ - goto ok; - } + CURL *handle = NULL; + struct nc_curl_data downloaded = {0}; + char **uris = NULL; + int uri_count = 0, i; - /* DER failed, try PEM next */ - bio = BIO_new_mem_buf(downloaded->data, downloaded->size); - if (!bio) { - ERR(session, "Creating new bio failed (%s).", ERR_reason_error_string(ERR_get_error())); - ret = -1; + /* init curl */ + ret = nc_server_tls_curl_init(&handle, &downloaded); + if (ret) { goto cleanup; } - /* try to parse PEM from the downloaded data */ - crl = PEM_read_bio_X509_CRL(bio, NULL, NULL, NULL); - if (!crl) { - ERR(session, "Reading downloaded CRL failed (%s).", ERR_reason_error_string(ERR_get_error())); - ret = -1; + ret = nc_server_tls_get_crl_distpoint_uris_wrap(cert_store, &uris, &uri_count); + if (ret) { goto cleanup; } -ok: - /* we obtained the CRL, now add it to the CRL store */ - ret = X509_STORE_add_crl(store, crl); - if (!ret) { - ERR(session, "Error adding CRL to store (%s).", ERR_reason_error_string(ERR_get_error())); - ret = -1; - goto cleanup; + for (i = 0; i < uri_count; i++) { + VRB(NULL, "Downloading CRL from \"%s\".", uris[i]); + ret = nc_server_tls_curl_fetch(handle, uris[i]); + if (ret) { + /* failed to download the CRL from this entry, try the next entry */ + WRN(NULL, "Failed to fetch CRL from \"%s\".", uris[i]); + continue; + } + + /* convert the downloaded data to CRL and add it to the store */ + ret = nc_server_tls_add_crl_to_store_wrap(downloaded.data, downloaded.size, cert_store, crl_store); + if (ret) { + goto cleanup; + } } - /* ok */ - ret = 0; cleanup: - X509_CRL_free(crl); - BIO_free(bio); + for (i = 0; i < uri_count; i++) { + free(uris[i]); + } + free(uris); + curl_easy_cleanup(handle); return ret; } static int -nc_server_tls_crl_url(struct nc_session *session, const char *url, X509_STORE *store) +nc_server_tls_crl_url(const char *url, void *cert_store, void *crl_store) { int ret = 0; CURL *handle = NULL; struct nc_curl_data downloaded = {0}; /* init curl */ - ret = nc_server_tls_curl_init(session, &handle, &downloaded); + ret = nc_server_tls_curl_init(&handle, &downloaded); if (ret) { goto cleanup; } - VRB(session, "Downloading CRL from \"%s\".", url); + VRB(NULL, "Downloading CRL from \"%s\".", url); /* download the CRL */ - ret = nc_server_tls_curl_fetch(session, handle, url); + ret = nc_server_tls_curl_fetch(handle, url); if (ret) { goto cleanup; } /* convert the downloaded data to CRL and add it to the store */ - ret = nc_server_tls_add_crl_to_store(session, &downloaded, store); + ret = nc_server_tls_add_crl_to_store_wrap(downloaded.data, downloaded.size, cert_store, crl_store); if (ret) { goto cleanup; } @@ -1175,154 +758,66 @@ nc_server_tls_crl_url(struct nc_session *session, const char *url, X509_STORE *s return ret; } -static int -nc_server_tls_crl_cert_ext(struct nc_session *session, X509_STORE *cert_store, X509_STORE *crl_store) +int +nc_server_tls_load_crl(struct nc_server_tls_opts *opts, void *cert_store, void *crl_store) { - int ret = 0, i, j, k, gtype; - CURL *handle = NULL; - struct nc_curl_data downloaded = {0}; - - STACK_OF(X509_OBJECT) * objs; - X509_OBJECT *obj; - X509 *cert; - - STACK_OF(DIST_POINT) * dist_points; - DIST_POINT *dist_point; - GENERAL_NAMES *general_names; - GENERAL_NAME *general_name; - ASN1_STRING *asn_string_uri; - const char *crl_distpoint_uri; - - /* init curl */ - ret = nc_server_tls_curl_init(session, &handle, &downloaded); - if (ret) { - goto cleanup; - } - - /* treat all entries in the cert_store as X509_OBJECTs */ - objs = X509_STORE_get0_objects(cert_store); - if (!objs) { - ERR(session, "Getting certificates from store failed (%s).", ERR_reason_error_string(ERR_get_error())); - ret = -1; - goto cleanup; - } - - /* iterate over all the CAs */ - for (i = 0; i < sk_X509_OBJECT_num(objs); i++) { - obj = sk_X509_OBJECT_value(objs, i); - cert = X509_OBJECT_get0_X509(obj); - if (!cert) { - /* the object on this index was not a certificate */ - continue; + if (opts->crl_path) { + if (nc_server_tls_crl_path_wrap(opts->crl_path, cert_store, crl_store)) { + return 1; } - - /* get all the distribution points for this CA */ - dist_points = X509_get_ext_d2i(cert, NID_crl_distribution_points, NULL, NULL); - - /* iterate over all the dist points (there can be multiple for a single cert) */ - for (j = 0; j < sk_DIST_POINT_num(dist_points); j++) { - dist_point = sk_DIST_POINT_value(dist_points, j); - if (!dist_point) { - continue; - } - general_names = dist_point->distpoint->name.fullname; - - /* iterate over all the GeneralesNames in the distribution point */ - for (k = 0; k < sk_GENERAL_NAME_num(general_names); k++) { - general_name = sk_GENERAL_NAME_value(general_names, k); - asn_string_uri = GENERAL_NAME_get0_value(general_name, >ype); - - /* check if the general name is a URI and has a valid length */ - if ((gtype != GEN_URI) || (ASN1_STRING_length(asn_string_uri) <= 6)) { - continue; - } - - crl_distpoint_uri = (const char *) ASN1_STRING_get0_data(asn_string_uri); - - VRB(session, "Downloading CRL from \"%s\".", crl_distpoint_uri); - - /* download the CRL */ - ret = nc_server_tls_curl_fetch(session, handle, crl_distpoint_uri); - if (ret) { - /* failed to download the CRL from this entry, try th next */ - continue; - } - - /* convert the downloaded data to CRL and add it to the store */ - ret = nc_server_tls_add_crl_to_store(session, &downloaded, crl_store); - if (ret) { - goto cleanup; - } - - /* the CRL was downloaded, no need to download it again using different protocol */ - break; - } + } else if (opts->crl_url) { + if (nc_server_tls_crl_url(opts->crl_url, cert_store, crl_store)) { + return 1; + } + } else { + if (nc_server_tls_crl_cert_ext(cert_store, crl_store)) { + return 1; } } -cleanup: - curl_easy_cleanup(handle); - return ret; + return 0; } -static int -nc_tls_store_set_crl(struct nc_session *session, struct nc_server_tls_opts *opts, X509_STORE *store) +int +nc_server_tls_load_trusted_certs(struct nc_cert_grouping *ca_certs, void *cert_store) { - if (!opts->crl_store) { - /* first call on this endpoint */ - opts->crl_store = X509_STORE_new(); - NC_CHECK_ERRMEM_GOTO(!opts->crl_store, , fail); - } + struct nc_certificate *certs; + uint16_t i, cert_count; - if (opts->crl_path) { - if (nc_server_tls_crl_path(session, opts->crl_path, opts->crl_store)) { - goto fail; - } - } else if (opts->crl_url) { - if (nc_server_tls_crl_url(session, opts->crl_url, opts->crl_store)) { - goto fail; - } + if (ca_certs->store == NC_STORE_LOCAL) { + /* local definition */ + certs = ca_certs->certs; + cert_count = ca_certs->cert_count; } else { - if (nc_server_tls_crl_cert_ext(session, store, opts->crl_store)) { - goto fail; + /* truststore */ + if (nc_server_tls_ts_ref_get_certs(ca_certs->ts_ref, &certs, &cert_count)) { + ERR(NULL, "Error getting certificate-authority certificates from the truststore reference \"%s\".", ca_certs->ts_ref); + return 1; } } - return 0; + for (i = 0; i < cert_count; i++) { + if (nc_base64der_to_cert_add_to_store(certs[i].data, cert_store)) { + return 1; + } + } -fail: - return -1; + return 0; } static int -nc_server_tls_accept_check(int accept_ret, struct nc_session *session) +nc_server_tls_accept_check(int accept_ret, void *tls_session) { - int verify; + uint32_t verify; /* check certificate verification result */ - verify = SSL_get_verify_result(session->ti.tls); - switch (verify) { - case X509_V_OK: - if (accept_ret == 1) { - VRB(session, "Client certificate verified."); - } - break; - default: - ERR(session, "Client certificate error (%s).", X509_verify_cert_error_string(verify)); + verify = nc_tls_get_verify_result_wrap(tls_session); + if (!verify && accept_ret == 1) { + VRB(NULL, "Client certificate verified."); } if (accept_ret != 1) { - switch (SSL_get_error(session->ti.tls, accept_ret)) { - case SSL_ERROR_SYSCALL: - ERR(session, "SSL accept failed (%s).", strerror(errno)); - break; - case SSL_ERROR_SSL: - ERR(session, "SSL accept failed (%s).", ERR_reason_error_string(ERR_get_error())); - break; - default: - ERR(session, "SSL accept failed."); - break; - } + nc_server_tls_print_accept_error_wrap(accept_ret, tls_session); } return accept_ret; @@ -1331,142 +826,140 @@ nc_server_tls_accept_check(int accept_ret, struct nc_session *session) int nc_accept_tls_session(struct nc_session *session, struct nc_server_tls_opts *opts, int sock, int timeout) { - X509_STORE *cert_store; - SSL_CTX *tls_ctx; - int ret; + int rc, timeouted = 0; struct timespec ts_timeout; - struct nc_endpt *referenced_endpt = NULL; + struct nc_tls_verify_cb_data cb_data = {0}; + struct nc_endpt *referenced_endpt; + void *tls_cfg, *srv_cert, *srv_pkey, *cert_store, *crl_store; - /* SSL_CTX */ - tls_ctx = SSL_CTX_new(TLS_server_method()); + tls_cfg = srv_cert = srv_pkey = cert_store = crl_store = NULL; - if (!tls_ctx) { - ERR(session, "Failed to create TLS context."); - goto error; + /* set verify cb data */ + cb_data.session = session; + cb_data.opts = opts; + cb_data.ee_certs = &opts->ee_certs; + if (opts->referenced_endpt_name) { + if (nc_server_get_referenced_endpt(opts->referenced_endpt_name, &referenced_endpt)) { + ERRINT; + return 1; + } + cb_data.referenced_ee_certs = &referenced_endpt->opts.tls->ee_certs; } - SSL_CTX_set_verify(tls_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nc_tlsclb_verify); - if (nc_tls_ctx_set_server_cert_key(tls_ctx, opts)) { - goto error; + /* prepare TLS context from which a session will be created */ + tls_cfg = nc_server_tls_config_new_wrap(); + if (!tls_cfg) { + goto fail; } - /* X509_STORE, managed (freed) with the context */ - cert_store = X509_STORE_new(); + /* opaque CA/CRL certificate store */ + cert_store = nc_tls_cert_store_new_wrap(); if (!cert_store) { - ERR(session, "Creating certificate store failed (%s).", ERR_reason_error_string(ERR_get_error())); - goto error; + goto fail; } - /* store the session, retrieve it when needed */ - ret = X509_STORE_set_ex_data(cert_store, 0, session); - if (!ret) { - ERR(session, "Setting certificate store data failed (%s).", ERR_reason_error_string(ERR_get_error())); - goto error; + /* load server's key and certificate */ + if (nc_server_tls_load_server_cert_key(opts, &srv_cert, &srv_pkey)) { + ERR(session, "Loading server certificate and/or private key failed."); + goto fail; } - /* set end-entity certs as cert store data, retrieve them if verification fails later */ - ret = X509_STORE_set_ex_data(cert_store, 1, &opts->ee_certs); - if (!ret) { - ERR(session, "Setting certificate store data failed (%s).", ERR_reason_error_string(ERR_get_error())); - goto error; + /* load trusted CA certificates */ + if (nc_server_tls_load_trusted_certs(&opts->ca_certs, cert_store)) { + ERR(session, "Loading server CA certs failed."); + goto fail; } - /* do the same for referenced endpoint's end entity certs */ + /* load referenced endpoint's trusted CA certs if set */ if (opts->referenced_endpt_name) { - if (nc_server_get_referenced_endpt(opts->referenced_endpt_name, &referenced_endpt)) { - ERRINT; - goto error; - } - - ret = X509_STORE_set_ex_data(cert_store, 2, &referenced_endpt->opts.tls->ee_certs); - if (!ret) { - ERR(session, "Setting certificate store data failed (%s).", ERR_reason_error_string(ERR_get_error())); - goto error; + if (nc_server_tls_load_trusted_certs(&referenced_endpt->opts.tls->ca_certs, cert_store)) { + ERR(session, "Loading server CA certs from referenced endpoint failed."); + goto fail; } } - /* set store to the context */ - SSL_CTX_set_cert_store(tls_ctx, cert_store); + if (opts->crl_path || opts->crl_url || opts->crl_cert_ext) { + /* opaque CRL store */ + crl_store = nc_tls_crl_store_new_wrap(); + if (!crl_store) { + goto fail; + } - /* set certificate authority certs */ - if (nc_tls_store_set_trusted_certs(cert_store, &opts->ca_certs)) { - goto error; + /* load CRLs into one of the stores */ + if (nc_server_tls_load_crl(opts, cert_store, crl_store)) { + ERR(session, "Loading server CRL failed."); + goto fail; + } } - /* set referenced endpoint's CA certs if set */ - if (opts->referenced_endpt_name) { - if (nc_tls_store_set_trusted_certs(cert_store, &referenced_endpt->opts.tls->ca_certs)) { - goto error; + /* set supported TLS versions */ + if (opts->tls_versions) { + if (nc_server_tls_set_tls_versions_wrap(tls_cfg, opts->tls_versions)) { + ERR(session, "Setting supported server TLS versions failed."); + goto fail; } } - /* set Certificate Revocation List if configured */ - if (opts->crl_path || opts->crl_url || opts->crl_cert_ext) { - if (nc_tls_store_set_crl(session, opts, cert_store)) { - goto error; - } + /* init TLS context and store data which may be needed later in it */ + if (nc_tls_init_ctx_wrap(&session->ti.tls.ctx, sock, srv_cert, srv_pkey, cert_store, crl_store)) { + goto fail; } - session->ti_type = NC_TI_OPENSSL; - session->ti.tls = SSL_new(tls_ctx); + /* memory is managed by context now */ + srv_cert = srv_pkey = cert_store = crl_store = NULL; - /* context can be freed already, trusted certs must be freed manually */ - SSL_CTX_free(tls_ctx); - tls_ctx = NULL; + /* setup config from ctx */ + if (nc_tls_setup_config_wrap(tls_cfg, NC_SERVER, &session->ti.tls.ctx)) { + goto fail; + } // TODO free openssl shit - if (!session->ti.tls) { - ERR(session, "Failed to create TLS structure from context."); - goto error; + /* fill session data and create TLS session from config */ + session->ti_type = NC_TI_OPENSSL; // TODO: prejmenovat + if (!(session->ti.tls.session = nc_tls_session_new_wrap(tls_cfg))) { + goto fail; } + session->ti.tls.config = tls_cfg; - /* set TLS versions for the current SSL session */ - if (opts->tls_versions) { - if (!(opts->tls_versions & NC_TLS_VERSION_10)) { - SSL_set_options(session->ti.tls, SSL_OP_NO_TLSv1); - } - if (!(opts->tls_versions & NC_TLS_VERSION_11)) { - SSL_set_options(session->ti.tls, SSL_OP_NO_TLSv1_1); - } - if (!(opts->tls_versions & NC_TLS_VERSION_12)) { - SSL_set_options(session->ti.tls, SSL_OP_NO_TLSv1_2); - } - if (!(opts->tls_versions & NC_TLS_VERSION_13)) { - SSL_set_options(session->ti.tls, SSL_OP_NO_TLSv1_3); - } - } + /* set verify callback and its data */ + nc_server_tls_set_verify_cb_wrap(session->ti.tls.session, &cb_data); - /* set TLS cipher suites */ - if (opts->ciphers) { - /* set for TLS1.2 and lower */ - SSL_set_cipher_list(session->ti.tls, opts->ciphers); - /* set for TLS1.3 */ - SSL_set_ciphersuites(session->ti.tls, opts->ciphers); - } + /* set session fd */ + nc_server_tls_set_fd_wrap(session->ti.tls.session, sock, &session->ti.tls.ctx); + + /* TODO: ciphers */ - SSL_set_fd(session->ti.tls, sock); sock = -1; - SSL_set_mode(session->ti.tls, SSL_MODE_AUTO_RETRY); + /* do the handshake */ if (timeout > -1) { nc_timeouttime_get(&ts_timeout, timeout); } - while (((ret = SSL_accept(session->ti.tls)) == -1) && (SSL_get_error(session->ti.tls, ret) == SSL_ERROR_WANT_READ)) { + while ((rc = nc_server_tls_handshake_step_wrap(session->ti.tls.session)) == 0) { usleep(NC_TIMEOUT_STEP); if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) { - ERR(session, "SSL accept timeout."); - return 0; + ERR(session, "TLS accept timeout."); + timeouted = 1; + goto fail; } } - if (nc_server_tls_accept_check(ret, session) != 1) { - return -1; + + /* check if handshake was ok */ + if (nc_server_tls_accept_check(rc, session->ti.tls.session) != 1) { + goto fail; } return 1; -error: +fail: if (sock > -1) { close(sock); } - SSL_CTX_free(tls_ctx); - return -1; + + nc_tls_session_new_cleanup_wrap(tls_cfg, srv_cert, srv_pkey, cert_store, crl_store); + + if (timeouted) { + return 0; + } else { + return -1; + } } From a9177eb66f6c6117d4908b188c695ca4fc5028f2 Mon Sep 17 00:00:00 2001 From: roman Date: Fri, 5 Apr 2024 12:36:18 +0200 Subject: [PATCH 18/54] session REFACTOR format sources --- src/io.c | 2 +- src/server_config_util.c | 1 - src/session.c | 2 +- src/session_client_tls.c | 2 +- src/session_mbedtls.c | 30 +++++++++++++++--------------- src/session_p.h | 1 - src/session_server.h | 1 - src/session_server_tls.c | 4 ++-- src/session_wrapper.h | 15 +++++++-------- 9 files changed, 27 insertions(+), 31 deletions(-) diff --git a/src/io.c b/src/io.c index e8d5903e..25a678fa 100644 --- a/src/io.c +++ b/src/io.c @@ -30,8 +30,8 @@ #include -#include "config.h" #include "compat.h" +#include "config.h" #include "log_p.h" #include "messages_p.h" #include "netconf.h" diff --git a/src/server_config_util.c b/src/server_config_util.c index 6c004996..9fcea1f4 100644 --- a/src/server_config_util.c +++ b/src/server_config_util.c @@ -867,7 +867,6 @@ nc_server_config_util_get_asym_key_pair(const char *privkey_path, const char *pu return ret; } - API int nc_server_config_add_address_port(const struct ly_ctx *ctx, const char *endpt_name, NC_TRANSPORT_IMPL transport, const char *address, uint16_t port, struct lyd_node **config) diff --git a/src/session.c b/src/session.c index bb5b611b..724d98a0 100644 --- a/src/session.c +++ b/src/session.c @@ -37,8 +37,8 @@ #ifdef NC_ENABLED_SSH_TLS -#include "session_wrapper.h" #include +#include "session_wrapper.h" #endif /* NC_ENABLED_SSH_TLS */ diff --git a/src/session_client_tls.c b/src/session_client_tls.c index 4e313110..ff5c0b39 100644 --- a/src/session_client_tls.c +++ b/src/session_client_tls.c @@ -279,7 +279,7 @@ nc_client_tls_connect_check(int connect_ret, void *tls_session, const char *peer /* check certificate verification result */ verify = nc_tls_get_verify_result_wrap(tls_session); - if (!verify && connect_ret == 1) { + if (!verify && (connect_ret == 1)) { VRB(NULL, "Server certificate verified (domain \"%s\").", peername); } else if (verify) { ERR(NULL, "Server certificate error (%s).", nc_tls_verify_error_string_wrap(verify)); diff --git a/src/session_mbedtls.c b/src/session_mbedtls.c index bf868bd4..987670fc 100644 --- a/src/session_mbedtls.c +++ b/src/session_mbedtls.c @@ -3,11 +3,11 @@ #include #include #include -#include #include #include #include #include +#include #include #include @@ -19,20 +19,20 @@ #include "session_p.h" #include "session_wrapper.h" -#include +#include +#include #include -#include -#include -#include -#include #include -#include -#include +#include +#include +#include +#include +#include +#include +#include #include +#include #include -#include -#include -#include extern struct nc_server_opts server_opts; @@ -474,7 +474,7 @@ nc_server_tls_dn2str(const mbedtls_x509_name *dn) str = malloc(len); NC_CHECK_ERRMEM_RET(!str, NULL); - while ((r = mbedtls_x509_dn_gets(str, len, dn)) == MBEDTLS_ERR_X509_BUFFER_TOO_SMALL) { + while ((r = mbedtls_x509_dn_gets(str, len, dn)) == MBEDTLS_ERR_X509_BUFFER_TOO_SMALL) { len <<= 1; ptr = realloc(str, len); if (!ptr) { @@ -555,7 +555,7 @@ nc_server_tls_get_username_from_cert_wrap(void *cert, NC_TLS_CTN_MAPTYPE map_typ break; } - /* iPAddress */ + /* iPAddress */ if (((map_type == NC_TLS_CTN_SAN_ANY) || (map_type == NC_TLS_CTN_SAN_IP_ADDRESS)) && (san.type == MBEDTLS_X509_SAN_IP_ADDRESS)) { ip = &san.san.unstructured_name; @@ -729,7 +729,7 @@ nc_server_tls_handshake_step_wrap(void *tls_session) rc = mbedtls_ssl_handshake(tls_session); if (!rc) { return 1; - } else if (rc == MBEDTLS_ERR_SSL_WANT_READ || rc == MBEDTLS_ERR_SSL_WANT_WRITE) { + } else if ((rc == MBEDTLS_ERR_SSL_WANT_READ) || (rc == MBEDTLS_ERR_SSL_WANT_WRITE)) { return 0; } else { return -1; @@ -1082,7 +1082,7 @@ nc_client_tls_handshake_step_wrap(void *tls_session) rc = mbedtls_ssl_handshake(tls_session); if (!rc) { return 1; - } else if (rc == MBEDTLS_ERR_SSL_WANT_READ || rc == MBEDTLS_ERR_SSL_WANT_WRITE) { + } else if ((rc == MBEDTLS_ERR_SSL_WANT_READ) || (rc == MBEDTLS_ERR_SSL_WANT_WRITE)) { return 0; } else { return rc; diff --git a/src/session_p.h b/src/session_p.h index 0a9f8fde..16c452b7 100644 --- a/src/session_p.h +++ b/src/session_p.h @@ -637,7 +637,6 @@ struct nc_session { void *config; struct nc_tls_ctx ctx; } tls; - #endif /* NC_ENABLED_SSH_TLS */ } ti; /**< transport implementation data */ char *username; diff --git a/src/session_server.h b/src/session_server.h index 4fd2ce86..4c4b2e7c 100644 --- a/src/session_server.h +++ b/src/session_server.h @@ -24,7 +24,6 @@ extern "C" { #include #include - #include "config.h" #include "netconf.h" #include "session.h" diff --git a/src/session_server_tls.c b/src/session_server_tls.c index 51edfd31..cb40ca9d 100644 --- a/src/session_server_tls.c +++ b/src/session_server_tls.c @@ -403,7 +403,7 @@ nc_server_tls_cert_to_name(struct nc_ctn *ctn_first, void *cert, struct nc_ctn_d if (!(map_type & data->matched_ctns)) { data->matched_ctns |= map_type; data->matched_ctn_type[data->matched_ctn_count++] = map_type; - if (!data->username && map_type == NC_TLS_CTN_SPECIFIED) { + if (!data->username && (map_type == NC_TLS_CTN_SPECIFIED)) { data->username = ctn->name; // TODO make a copy? } } @@ -812,7 +812,7 @@ nc_server_tls_accept_check(int accept_ret, void *tls_session) /* check certificate verification result */ verify = nc_tls_get_verify_result_wrap(tls_session); - if (!verify && accept_ret == 1) { + if (!verify && (accept_ret == 1)) { VRB(NULL, "Client certificate verified."); } diff --git a/src/session_wrapper.h b/src/session_wrapper.h index 977cd000..c9b9dc73 100644 --- a/src/session_wrapper.h +++ b/src/session_wrapper.h @@ -1,4 +1,3 @@ - #ifndef _SESSION_WRAPPER_H_ #define _SESSION_WRAPPER_H_ @@ -8,11 +7,11 @@ #ifdef HAVE_LIBMBEDTLS -#include #include +#include struct nc_tls_ctx { - int *sock; + int *sock; mbedtls_entropy_context *entropy; mbedtls_ctr_drbg_context *ctr_drbg; mbedtls_x509_crt *cert; @@ -26,7 +25,7 @@ struct nc_tls_ctx { #include struct nc_tls_ctx { - char dummy[0]; + char dummy[0]; }; #endif @@ -39,10 +38,10 @@ struct nc_tls_verify_cb_data { struct nc_cert_grouping *referenced_ee_certs; struct nc_server_tls_opts *opts; struct nc_ctn_data { - char *username; - int matched_ctns; - int matched_ctn_type[6]; - int matched_ctn_count; + char *username; + int matched_ctns; + int matched_ctn_type[6]; + int matched_ctn_count; } ctn_data; }; From 38d3ea44176b1a73c0631fc9fe2b57c2c965767b Mon Sep 17 00:00:00 2001 From: roman Date: Thu, 11 Apr 2024 09:37:22 +0200 Subject: [PATCH 19/54] session UPDATE rename transport implementations --- src/io.c | 16 ++++----- src/server_config.c | 69 ++++++++++++++------------------------ src/server_config_util.c | 14 ++++---- src/session.c | 4 +-- src/session.h | 4 +-- src/session_client.c | 4 +-- src/session_client_ssh.c | 10 +++--- src/session_client_tls.c | 10 +++--- src/session_server.c | 16 ++++----- src/session_server_ssh.c | 8 ++--- tests/test_server_thread.c | 10 +++--- 11 files changed, 74 insertions(+), 91 deletions(-) diff --git a/src/io.c b/src/io.c index 25a678fa..e87da1a1 100644 --- a/src/io.c +++ b/src/io.c @@ -106,7 +106,7 @@ nc_read(struct nc_session *session, char *buf, uint32_t count, uint32_t inact_ti break; #ifdef NC_ENABLED_SSH_TLS - case NC_TI_LIBSSH: + case NC_TI_SSH: /* read via libssh */ r = ssh_channel_read(session->ti.libssh.channel, buf + readd, count - readd, 0); if (r == SSH_AGAIN) { @@ -128,7 +128,7 @@ nc_read(struct nc_session *session, char *buf, uint32_t count, uint32_t inact_ti } break; - case NC_TI_OPENSSL: + case NC_TI_TLS: r = nc_tls_read_wrap(session, (unsigned char *)buf + readd, count - readd); if (r < 0) { /* non-recoverable error */ @@ -396,7 +396,7 @@ nc_read_poll(struct nc_session *session, int io_timeout) switch (session->ti_type) { #ifdef NC_ENABLED_SSH_TLS - case NC_TI_LIBSSH: + case NC_TI_SSH: /* EINTR is handled, it resumes waiting */ ret = ssh_channel_poll_timeout(session->ti.libssh.channel, io_timeout, 0); if (ret == SSH_ERROR) { @@ -417,7 +417,7 @@ nc_read_poll(struct nc_session *session, int io_timeout) fds.revents = 0; } break; - case NC_TI_OPENSSL: + case NC_TI_TLS: ret = nc_tls_have_pending_wrap(session->ti.tls.session); if (ret) { /* some buffered TLS data available */ @@ -523,9 +523,9 @@ nc_session_is_connected(const struct nc_session *session) fds.fd = session->ti.unixsock.sock; break; #ifdef NC_ENABLED_SSH_TLS - case NC_TI_LIBSSH: + case NC_TI_SSH: return ssh_is_connected(session->ti.libssh.session); - case NC_TI_OPENSSL: + case NC_TI_TLS: fds.fd = nc_tls_get_fd_wrap(session); break; #endif /* NC_ENABLED_SSH_TLS */ @@ -605,7 +605,7 @@ nc_write(struct nc_session *session, const void *buf, uint32_t count) break; #ifdef NC_ENABLED_SSH_TLS - case NC_TI_LIBSSH: + case NC_TI_SSH: if (ssh_channel_is_closed(session->ti.libssh.channel) || ssh_channel_is_eof(session->ti.libssh.channel)) { if (ssh_channel_is_closed(session->ti.libssh.channel)) { ERR(session, "SSH channel unexpectedly closed."); @@ -622,7 +622,7 @@ nc_write(struct nc_session *session, const void *buf, uint32_t count) return -1; } break; - case NC_TI_OPENSSL: + case NC_TI_TLS: c = nc_tls_write_wrap(session, (const unsigned char *)(buf + written), count - written); if (c < 0) { /* possible client dc, or some socket/TLS communication error */ diff --git a/src/server_config.c b/src/server_config.c index d7f1f724..e01ee50d 100644 --- a/src/server_config.c +++ b/src/server_config.c @@ -672,7 +672,7 @@ nc_server_config_del_endpt_references(const char *referenced_endpt_name) free(server_opts.endpts[i].referenced_endpt_name); server_opts.endpts[i].referenced_endpt_name = NULL; - if (server_opts.endpts[i].ti == NC_TI_LIBSSH) { + if (server_opts.endpts[i].ti == NC_TI_SSH) { server_opts.endpts[i].opts.ssh->referenced_endpt_name = NULL; } else { server_opts.endpts[i].opts.tls->referenced_endpt_name = NULL; @@ -693,7 +693,7 @@ nc_server_config_del_endpt_references(const char *referenced_endpt_name) free(server_opts.ch_clients[i].ch_endpts[j].referenced_endpt_name); server_opts.ch_clients[i].ch_endpts[j].referenced_endpt_name = NULL; - if (server_opts.ch_clients[i].ch_endpts[j].ti == NC_TI_LIBSSH) { + if (server_opts.ch_clients[i].ch_endpts[j].ti == NC_TI_SSH) { server_opts.ch_clients[i].ch_endpts[j].opts.ssh->referenced_endpt_name = NULL; } else { server_opts.ch_clients[i].ch_endpts[j].opts.tls->referenced_endpt_name = NULL; @@ -880,10 +880,10 @@ nc_server_config_ch_del_endpt(struct nc_ch_client *ch_client, struct nc_ch_endpt switch (ch_endpt->ti) { #ifdef NC_ENABLED_SSH_TLS - case NC_TI_LIBSSH: + case NC_TI_SSH: nc_server_config_del_ssh_opts(NULL, ch_endpt->opts.ssh); break; - case NC_TI_OPENSSL: + case NC_TI_TLS: nc_server_config_del_tls_opts(NULL, ch_endpt->opts.tls); break; #endif /* NC_ENABLED_SSH_TLS */ @@ -980,10 +980,10 @@ nc_server_config_listen(const struct lyd_node *node, NC_OPERATION op) for (i = 0; i < endpt_count; i++) { switch (server_opts.endpts[i].ti) { #ifdef NC_ENABLED_SSH_TLS - case NC_TI_LIBSSH: + case NC_TI_SSH: nc_server_config_del_endpt_ssh(&server_opts.endpts[i], &server_opts.binds[i]); break; - case NC_TI_OPENSSL: + case NC_TI_TLS: nc_server_config_del_endpt_tls(&server_opts.endpts[i], &server_opts.binds[i]); break; #endif /* NC_ENABLED_SSH_TLS */ @@ -1139,10 +1139,10 @@ nc_server_config_endpoint(const struct lyd_node *node, NC_OPERATION op) switch (endpt->ti) { #ifdef NC_ENABLED_SSH_TLS - case NC_TI_LIBSSH: + case NC_TI_SSH: nc_server_config_del_endpt_ssh(endpt, bind); break; - case NC_TI_OPENSSL: + case NC_TI_TLS: nc_server_config_del_endpt_tls(endpt, bind); break; #endif /* NC_ENABLED_SSH_TLS */ @@ -1193,7 +1193,7 @@ nc_server_config_endpoint(const struct lyd_node *node, NC_OPERATION op) static int nc_server_config_create_ssh(struct nc_endpt *endpt) { - endpt->ti = NC_TI_LIBSSH; + endpt->ti = NC_TI_SSH; endpt->opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts)); NC_CHECK_ERRMEM_RET(!endpt->opts.ssh, 1); @@ -1203,7 +1203,7 @@ nc_server_config_create_ssh(struct nc_endpt *endpt) static int nc_server_config_ch_create_ssh(struct nc_ch_endpt *ch_endpt) { - ch_endpt->ti = NC_TI_LIBSSH; + ch_endpt->ti = NC_TI_SSH; ch_endpt->opts.ssh = calloc(1, sizeof(struct nc_server_ssh_opts)); NC_CHECK_ERRMEM_RET(!ch_endpt->opts.ssh, 1); @@ -1269,7 +1269,7 @@ nc_server_config_ssh(const struct lyd_node *node, NC_OPERATION op) static int nc_server_config_create_tls(struct nc_endpt *endpt) { - endpt->ti = NC_TI_OPENSSL; + endpt->ti = NC_TI_TLS; endpt->opts.tls = calloc(1, sizeof *endpt->opts.tls); NC_CHECK_ERRMEM_RET(!endpt->opts.tls, 1); @@ -1279,7 +1279,7 @@ nc_server_config_create_tls(struct nc_endpt *endpt) static int nc_server_config_ch_create_tls(struct nc_ch_endpt *ch_endpt) { - ch_endpt->ti = NC_TI_OPENSSL; + ch_endpt->ti = NC_TI_TLS; ch_endpt->opts.tls = calloc(1, sizeof(struct nc_server_tls_opts)); NC_CHECK_ERRMEM_RET(!ch_endpt->opts.tls, 1); @@ -2661,7 +2661,7 @@ nc_server_config_check_endpt_references(void) ERR(NULL, "Endpoint \"%s\" referenced by endpoint \"%s\" has different transport type.", server_opts.endpts[i].referenced_endpt_name, server_opts.endpts[i].name); return 1; - } else if ((referenced_endpt->ti != NC_TI_LIBSSH) && (referenced_endpt->ti != NC_TI_OPENSSL)) { + } else if ((referenced_endpt->ti != NC_TI_SSH) && (referenced_endpt->ti != NC_TI_TLS)) { ERR(NULL, "Endpoint \"%s\" referenced by endpoint \"%s\" has unsupported transport type.", server_opts.endpts[i].referenced_endpt_name, server_opts.endpts[i].name); return 1; @@ -2675,7 +2675,7 @@ nc_server_config_check_endpt_references(void) } /* all went well, assign the name to the opts, so we can access it for auth */ - if (server_opts.endpts[i].ti == NC_TI_LIBSSH) { + if (server_opts.endpts[i].ti == NC_TI_SSH) { server_opts.endpts[i].opts.ssh->referenced_endpt_name = referenced_endpt->name; } else { server_opts.endpts[i].opts.tls->referenced_endpt_name = referenced_endpt->name; @@ -2703,7 +2703,7 @@ nc_server_config_check_endpt_references(void) ERR(NULL, "Endpoint \"%s\" referenced by call home endpoint \"%s\" has different transport type.", server_opts.ch_clients[i].ch_endpts[j].referenced_endpt_name, server_opts.ch_clients[i].ch_endpts[j].name); goto ch_fail; - } else if ((referenced_endpt->ti != NC_TI_LIBSSH) && (referenced_endpt->ti != NC_TI_OPENSSL)) { + } else if ((referenced_endpt->ti != NC_TI_SSH) && (referenced_endpt->ti != NC_TI_TLS)) { ERR(NULL, "Endpoint \"%s\" referenced by call home endpoint \"%s\" has unsupported transport type.", server_opts.ch_clients[i].ch_endpts[j].referenced_endpt_name, server_opts.ch_clients[i].ch_endpts[j].name); goto ch_fail; @@ -2717,7 +2717,7 @@ nc_server_config_check_endpt_references(void) } /* all went well, assign the name to the opts, so we can access it for auth */ - if (server_opts.ch_clients[i].ch_endpts[j].ti == NC_TI_LIBSSH) { + if (server_opts.ch_clients[i].ch_endpts[j].ti == NC_TI_SSH) { server_opts.ch_clients[i].ch_endpts[j].opts.ssh->referenced_endpt_name = referenced_endpt->name; } else { server_opts.ch_clients[i].ch_endpts[j].opts.tls->referenced_endpt_name = referenced_endpt->name; @@ -3232,39 +3232,22 @@ static int nc_server_config_create_cipher_suite(struct nc_server_tls_opts *opts, const char *cipher) { int ret = 0; - char *ssl_cipher = NULL; - uint16_t i; - void *tmp; - - ssl_cipher = malloc(strlen(cipher) + 1); - NC_CHECK_ERRMEM_GOTO(!ssl_cipher, ret = 1, cleanup); + char *processed_cipher = NULL; - for (i = 0; cipher[i]; i++) { - if (cipher[i] == '-') { - /* OpenSSL requires _ instead of - in cipher names */ - ssl_cipher[i] = '_'; - } else { - /* and requires uppercase unlike the identities */ - ssl_cipher[i] = toupper(cipher[i]); - } + ret = nc_tls_process_cipher_suite_wrap(cipher, &processed_cipher); + if (ret) { + ERR(NULL, "Failed to process the cipher suite \"%s\".", cipher); + goto cleanup; } - ssl_cipher[i] = '\0'; - if (!opts->ciphers) { - /* first entry */ - opts->ciphers = strdup(ssl_cipher); - NC_CHECK_ERRMEM_GOTO(!opts->ciphers, ret = 1, cleanup); - } else { - /* + 1 because of : between entries */ - tmp = nc_realloc(opts->ciphers, strlen(opts->ciphers) + strlen(ssl_cipher) + 1 + 1); - NC_CHECK_ERRMEM_GOTO(!tmp, ret = 1, cleanup); - opts->ciphers = tmp; - strcat(opts->ciphers, ":"); - strcat(opts->ciphers, ssl_cipher); + ret = nc_tls_append_cipher_suite_wrap(opts, processed_cipher); + if (ret) { + ERR(NULL, "Failed to append the cipher suite \"%s\".", cipher); + goto cleanup; } cleanup: - free(ssl_cipher); + free(processed_cipher); return ret; } diff --git a/src/server_config_util.c b/src/server_config_util.c index 9fcea1f4..d7c627c4 100644 --- a/src/server_config_util.c +++ b/src/server_config_util.c @@ -877,11 +877,11 @@ nc_server_config_add_address_port(const struct ly_ctx *ctx, const char *endpt_na NC_CHECK_ARG_RET(NULL, ctx, endpt_name, address, config, 1); - if (transport == NC_TI_LIBSSH) { + if (transport == NC_TI_SSH) { /* SSH path */ address_fmt = "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/ssh/tcp-server-parameters/local-address"; port_fmt = "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/ssh/tcp-server-parameters/local-port"; - } else if (transport == NC_TI_OPENSSL) { + } else if (transport == NC_TI_TLS) { /* TLS path */ address_fmt = "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/tls/tcp-server-parameters/local-address"; port_fmt = "/ietf-netconf-server:netconf-server/listen/endpoints/endpoint[name='%s']/tls/tcp-server-parameters/local-port"; @@ -915,11 +915,11 @@ nc_server_config_add_ch_address_port(const struct ly_ctx *ctx, const char *clien NC_CHECK_ARG_RET(NULL, ctx, client_name, endpt_name, address, port, config, 1); - if (transport == NC_TI_LIBSSH) { + if (transport == NC_TI_SSH) { /* SSH path */ address_fmt = "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/tcp-client-parameters/remote-address"; port_fmt = "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/endpoint[name='%s']/ssh/tcp-client-parameters/remote-port"; - } else if (transport == NC_TI_OPENSSL) { + } else if (transport == NC_TI_TLS) { /* TLS path */ address_fmt = "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/endpoint[name='%s']/tls/tcp-client-parameters/remote-address"; port_fmt = "/ietf-netconf-server:netconf-server/call-home/netconf-client[name='%s']/endpoints/endpoint[name='%s']/tls/tcp-client-parameters/remote-port"; @@ -993,10 +993,10 @@ nc_server_config_add_keystore_asym_key(const struct ly_ctx *ctx, NC_TRANSPORT_IM NC_CHECK_ARG_RET(NULL, ctx, asym_key_name, privkey_path, config, 1); /* get the keys as a string from the given files */ - if (ti == NC_TI_LIBSSH) { + if (ti == NC_TI_SSH) { ret = nc_server_config_util_get_asym_key_pair(privkey_path, pubkey_path, NC_PUBKEY_FORMAT_SSH, &privkey, &privkey_type, &pubkey); - } else if (ti == NC_TI_OPENSSL) { + } else if (ti == NC_TI_TLS) { ret = nc_server_config_util_get_asym_key_pair(privkey_path, pubkey_path, NC_PUBKEY_FORMAT_X509, &privkey, &privkey_type, &pubkey); } else { @@ -1009,7 +1009,7 @@ nc_server_config_add_keystore_asym_key(const struct ly_ctx *ctx, NC_TRANSPORT_IM } /* get pubkey format str */ - if (ti == NC_TI_LIBSSH) { + if (ti == NC_TI_SSH) { pubkey_format = "ietf-crypto-types:ssh-public-key-format"; } else { pubkey_format = "ietf-crypto-types:subject-public-key-info-format"; diff --git a/src/session.c b/src/session.c index 724d98a0..f5d1cc85 100644 --- a/src/session.c +++ b/src/session.c @@ -729,7 +729,7 @@ nc_session_free_transport(struct nc_session *session, int *multisession) break; #ifdef NC_ENABLED_SSH_TLS - case NC_TI_LIBSSH: { + case NC_TI_SSH: { int r; if (connected) { @@ -794,7 +794,7 @@ nc_session_free_transport(struct nc_session *session, int *multisession) } break; } - case NC_TI_OPENSSL: + case NC_TI_TLS: sock = nc_tls_get_fd_wrap(session); if (connected) { diff --git a/src/session.h b/src/session.h index 59aabe2d..daf9e704 100644 --- a/src/session.h +++ b/src/session.h @@ -90,9 +90,9 @@ typedef enum { outside the current application */ NC_TI_UNIX, /**< unix socket */ #ifdef NC_ENABLED_SSH_TLS - NC_TI_LIBSSH, /**< libssh - use libssh library, only for NETCONF over SSH transport */ + NC_TI_SSH, /**< SSH - use libssh library, only for NETCONF over SSH transport */ - NC_TI_OPENSSL /**< OpenSSL - use OpenSSL library, only for NETCONF over TLS transport TODO: prejmenovat*/ + NC_TI_TLS /**< TLS - use either OpenSSL or MbedTLS library, only for NETCONF over TLS transport */ #endif /* NC_ENABLED_SSH_TLS */ } NC_TRANSPORT_IMPL; diff --git a/src/session_client.c b/src/session_client.c index 22e3313a..5aed2dc6 100644 --- a/src/session_client.c +++ b/src/session_client.c @@ -1851,9 +1851,9 @@ nc_accept_callhome(int timeout, struct ly_ctx *ctx, struct nc_session **session) return -1; } - if (client_opts.ch_binds_aux[idx].ti == NC_TI_LIBSSH) { + if (client_opts.ch_binds_aux[idx].ti == NC_TI_SSH) { *session = nc_accept_callhome_ssh_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT); - } else if (client_opts.ch_binds_aux[idx].ti == NC_TI_OPENSSL) { + } else if (client_opts.ch_binds_aux[idx].ti == NC_TI_TLS) { *session = nc_accept_callhome_tls_sock(sock, host, port, ctx, NC_TRANSPORT_TIMEOUT, client_opts.ch_binds_aux[idx].hostname); } else { diff --git a/src/session_client_ssh.c b/src/session_client_ssh.c index 122f68bd..3116b895 100644 --- a/src/session_client_ssh.c +++ b/src/session_client_ssh.c @@ -1207,13 +1207,13 @@ nc_client_ssh_ch_get_username(void) API int nc_client_ssh_ch_add_bind_listen(const char *address, uint16_t port) { - return nc_client_ch_add_bind_listen(address, port, NULL, NC_TI_LIBSSH); + return nc_client_ch_add_bind_listen(address, port, NULL, NC_TI_SSH); } API int nc_client_ssh_ch_del_bind(const char *address, uint16_t port) { - return nc_client_ch_del_bind(address, port, NC_TI_LIBSSH); + return nc_client_ch_del_bind(address, port, NC_TI_SSH); } /* Establish a secure SSH connection and authenticate. @@ -1583,7 +1583,7 @@ _nc_connect_libssh(ssh_session ssh_session, struct ly_ctx *ctx, struct nc_keepal session = nc_new_session(NC_CLIENT, 0); NC_CHECK_ERRMEM_RET(!session, NULL); session->status = NC_STATUS_STARTING; - session->ti_type = NC_TI_LIBSSH; + session->ti_type = NC_TI_SSH; session->ti.libssh.session = ssh_session; /* was port set? */ @@ -1742,7 +1742,7 @@ nc_connect_ssh(const char *host, uint16_t port, struct ly_ctx *ctx) session->status = NC_STATUS_STARTING; /* transport-specific data */ - session->ti_type = NC_TI_LIBSSH; + session->ti_type = NC_TI_SSH; session->ti.libssh.session = ssh_new(); if (!session->ti.libssh.session) { ERR(session, "Unable to initialize SSH session."); @@ -1830,7 +1830,7 @@ nc_connect_ssh_channel(struct nc_session *session, struct ly_ctx *ctx) new_session->status = NC_STATUS_STARTING; /* share some parameters including the IO lock (we are using one socket for both sessions) */ - new_session->ti_type = NC_TI_LIBSSH; + new_session->ti_type = NC_TI_SSH; new_session->ti.libssh.session = session->ti.libssh.session; new_session->io_lock = session->io_lock; diff --git a/src/session_client_tls.c b/src/session_client_tls.c index ff5c0b39..d7d54eec 100644 --- a/src/session_client_tls.c +++ b/src/session_client_tls.c @@ -257,19 +257,19 @@ nc_client_tls_ch_get_crl_paths(const char **crl_file, const char **crl_dir) API int nc_client_tls_ch_add_bind_listen(const char *address, uint16_t port) { - return nc_client_ch_add_bind_listen(address, port, NULL, NC_TI_OPENSSL); + return nc_client_ch_add_bind_listen(address, port, NULL, NC_TI_TLS); } API int nc_client_tls_ch_add_bind_hostname_listen(const char *address, uint16_t port, const char *hostname) { - return nc_client_ch_add_bind_listen(address, port, hostname, NC_TI_OPENSSL); + return nc_client_ch_add_bind_listen(address, port, hostname, NC_TI_TLS); } API int nc_client_tls_ch_del_bind(const char *address, uint16_t port) { - return nc_client_ch_del_bind(address, port, NC_TI_OPENSSL); + return nc_client_ch_del_bind(address, port, NC_TI_TLS); } static int @@ -433,7 +433,7 @@ nc_connect_tls(const char *host, unsigned short port, struct ly_ctx *ctx) } /* fill the session */ - session->ti_type = NC_TI_OPENSSL; + session->ti_type = NC_TI_TLS; if (!(session->ti.tls.session = nc_client_tls_session_new(sock, host, NC_TRANSPORT_TIMEOUT, &tls_opts, &tls_cfg, &tls_ctx))) { goto fail; } @@ -492,7 +492,7 @@ nc_accept_callhome_tls_sock(int sock, const char *host, uint16_t port, struct ly session->status = NC_STATUS_STARTING; /* fill the session */ - session->ti_type = NC_TI_OPENSSL; + session->ti_type = NC_TI_TLS; if (!(session->ti.tls.session = nc_client_tls_session_new(sock, peername, timeout, &tls_ch_opts, &tls_cfg, &tls_ctx))) { goto fail; } diff --git a/src/session_server.c b/src/session_server.c index f152cd60..35f27460 100644 --- a/src/session_server.c +++ b/src/session_server.c @@ -1601,7 +1601,7 @@ nc_ps_poll_session_io(struct nc_session *session, int io_timeout, time_t now_mon switch (session->ti_type) { #ifdef NC_ENABLED_SSH_TLS - case NC_TI_LIBSSH: + case NC_TI_SSH: ssh_msg = ssh_message_get(session->ti.libssh.session); if (ssh_msg) { nc_session_ssh_msg(session, NULL, ssh_msg, NULL); @@ -1650,7 +1650,7 @@ nc_ps_poll_session_io(struct nc_session *session, int io_timeout, time_t now_mon ret = NC_PSPOLL_RPC; } break; - case NC_TI_OPENSSL: + case NC_TI_TLS: r = nc_tls_have_pending_wrap(session->ti.tls.session); if (!r) { /* no data pending in the SSL buffer, poll fd */ @@ -2000,10 +2000,10 @@ nc_server_set_address_port(struct nc_endpt *endpt, struct nc_bind *bind, const c VRB(NULL, "Listening on %s for UNIX connections.", endpt->opts.unixsock->address); break; #ifdef NC_ENABLED_SSH_TLS - case NC_TI_LIBSSH: + case NC_TI_SSH: VRB(NULL, "Listening on %s:%u for SSH connections.", address, port); break; - case NC_TI_OPENSSL: + case NC_TI_TLS: VRB(NULL, "Listening on %s:%u for TLS connections.", address, port); break; #endif /* NC_ENABLED_SSH_TLS */ @@ -2292,7 +2292,7 @@ nc_accept(int timeout, const struct ly_ctx *ctx, struct nc_session **session) /* sock gets assigned to session or closed */ #ifdef NC_ENABLED_SSH_TLS - if (server_opts.endpts[bind_idx].ti == NC_TI_LIBSSH) { + if (server_opts.endpts[bind_idx].ti == NC_TI_SSH) { ret = nc_accept_ssh_session(*session, server_opts.endpts[bind_idx].opts.ssh, sock, NC_TRANSPORT_TIMEOUT); sock = -1; if (ret < 0) { @@ -2302,7 +2302,7 @@ nc_accept(int timeout, const struct ly_ctx *ctx, struct nc_session **session) msgtype = NC_MSG_WOULDBLOCK; goto cleanup; } - } else if (server_opts.endpts[bind_idx].ti == NC_TI_OPENSSL) { + } else if (server_opts.endpts[bind_idx].ti == NC_TI_TLS) { (*session)->data = server_opts.endpts[bind_idx].opts.tls; ret = nc_accept_tls_session(*session, server_opts.endpts[bind_idx].opts.tls, sock, NC_TRANSPORT_TIMEOUT); sock = -1; @@ -2482,7 +2482,7 @@ nc_connect_ch_endpt(struct nc_ch_endpt *endpt, nc_server_ch_session_acquire_ctx_ (*session)->port = endpt->port; /* sock gets assigned to session or closed */ - if (endpt->ti == NC_TI_LIBSSH) { + if (endpt->ti == NC_TI_SSH) { ret = nc_accept_ssh_session(*session, endpt->opts.ssh, sock, NC_TRANSPORT_TIMEOUT); (*session)->data = NULL; @@ -2493,7 +2493,7 @@ nc_connect_ch_endpt(struct nc_ch_endpt *endpt, nc_server_ch_session_acquire_ctx_ msgtype = NC_MSG_WOULDBLOCK; goto fail; } - } else if (endpt->ti == NC_TI_OPENSSL) { + } else if (endpt->ti == NC_TI_TLS) { (*session)->data = endpt->opts.tls; ret = nc_accept_tls_session(*session, endpt->opts.tls, sock, NC_TRANSPORT_TIMEOUT); (*session)->data = NULL; diff --git a/src/session_server_ssh.c b/src/session_server_ssh.c index 8d808151..c31b7f13 100644 --- a/src/session_server_ssh.c +++ b/src/session_server_ssh.c @@ -1265,7 +1265,7 @@ nc_sshcb_channel_subsystem(struct nc_session *session, ssh_channel channel, cons session->ti.libssh.next = new_session; new_session->status = NC_STATUS_STARTING; - new_session->ti_type = NC_TI_LIBSSH; + new_session->ti_type = NC_TI_SSH; new_session->io_lock = session->io_lock; new_session->ti.libssh.channel = channel; new_session->ti.libssh.session = session->ti.libssh.session; @@ -1711,7 +1711,7 @@ nc_accept_ssh_session(struct nc_session *session, struct nc_server_ssh_opts *opt const char *err_msg; /* other transport-specific data */ - session->ti_type = NC_TI_LIBSSH; + session->ti_type = NC_TI_SSH; session->ti.libssh.session = ssh_new(); if (!session->ti.libssh.session) { ERR(NULL, "Failed to initialize a new SSH session."); @@ -1815,7 +1815,7 @@ nc_session_accept_ssh_channel(struct nc_session *orig_session, struct nc_session NC_CHECK_ARG_RET(orig_session, orig_session, session, NC_MSG_ERROR); - if ((orig_session->status == NC_STATUS_RUNNING) && (orig_session->ti_type == NC_TI_LIBSSH) && + if ((orig_session->status == NC_STATUS_RUNNING) && (orig_session->ti_type == NC_TI_SSH) && orig_session->ti.libssh.next) { for (new_session = orig_session->ti.libssh.next; new_session != orig_session; @@ -1873,7 +1873,7 @@ nc_ps_accept_ssh_channel(struct nc_pollsession *ps, struct nc_session **session) for (i = 0; i < ps->session_count; ++i) { cur_session = ps->sessions[i]->session; - if ((cur_session->status == NC_STATUS_RUNNING) && (cur_session->ti_type == NC_TI_LIBSSH) && + if ((cur_session->status == NC_STATUS_RUNNING) && (cur_session->ti_type == NC_TI_SSH) && cur_session->ti.libssh.next) { /* an SSH session with more channels */ for (new_session = cur_session->ti.libssh.next; diff --git a/tests/test_server_thread.c b/tests/test_server_thread.c index 2af23de7..836ddd1a 100644 --- a/tests/test_server_thread.c +++ b/tests/test_server_thread.c @@ -113,7 +113,7 @@ add_endpt_thread(void *arg) int ret; pthread_barrier_wait(&barrier); - ret = nc_server_add_endpt("tertiary", NC_TI_LIBSSH); + ret = nc_server_add_endpt("tertiary", NC_TI_SSH); nc_assert(!ret); return NULL; @@ -687,7 +687,7 @@ main(void) nc_server_ssh_set_hostkey_clb(clb_hostkeys, NULL, NULL); /* do first, so that client can connect on SSH */ - ret = nc_server_add_endpt("main_ssh", NC_TI_LIBSSH); + ret = nc_server_add_endpt("main_ssh", NC_TI_SSH); nc_assert(!ret); ret = nc_server_endpt_set_address("main_ssh", "0.0.0.0"); nc_assert(!ret); @@ -707,7 +707,7 @@ main(void) ret = nc_server_ssh_add_authkey_path(TESTS_DIR "/data/key_ecdsa.pub", "test2"); nc_assert(!ret); - ret = nc_server_add_endpt("secondary", NC_TI_LIBSSH); + ret = nc_server_add_endpt("secondary", NC_TI_SSH); nc_assert(!ret); #endif @@ -717,7 +717,7 @@ main(void) nc_server_tls_set_trusted_cert_list_clb(clb_trusted_cert_lists, NULL, NULL); /* do first, so that client can connect on TLS */ - ret = nc_server_add_endpt("main_tls", NC_TI_OPENSSL); + ret = nc_server_add_endpt("main_tls", NC_TI_TLS); nc_assert(!ret); ret = nc_server_endpt_set_address("main_tls", "0.0.0.0"); nc_assert(!ret); @@ -739,7 +739,7 @@ main(void) ret = nc_server_tls_endpt_add_ctn("main_tls", 1, "02:00:11:22:33:44:55:66:77:88:99:AA:BB:CC:DD:EE:FF:A0:A1:A2:A3", NC_TLS_CTN_SAN_ANY, NULL); nc_assert(!ret); - ret = nc_server_add_endpt("quaternary", NC_TI_OPENSSL); + ret = nc_server_add_endpt("quaternary", NC_TI_TLS); nc_assert(!ret); #endif From 823f0fc662da161982c69b603d49af2ed8d0cd29 Mon Sep 17 00:00:00 2001 From: roman Date: Mon, 15 Apr 2024 14:58:39 +0200 Subject: [PATCH 20/54] session server tls UPDATE refactor wraps --- src/session_server_tls.c | 273 ++++++++++++++++++++++++++++----------- 1 file changed, 196 insertions(+), 77 deletions(-) diff --git a/src/session_server_tls.c b/src/session_server_tls.c index cb40ca9d..8b47e367 100644 --- a/src/session_server_tls.c +++ b/src/session_server_tls.c @@ -102,8 +102,8 @@ nc_server_tls_ts_ref_get_certs(const char *referenced_name, struct nc_certificat static void * nc_base64der_to_cert(const char *in) { - char *buf; - void *cert = NULL; + char *buf = NULL; + void *cert; NC_CHECK_ARG_RET(NULL, in, NULL); @@ -117,11 +117,11 @@ nc_base64der_to_cert(const char *in) return cert; } -int +static int nc_base64der_to_cert_add_to_store(const char *in, void *cert_store) { int ret; - char *buf; + char *buf = NULL; NC_CHECK_ARG_RET(NULL, in, cert_store, 1); @@ -136,9 +136,9 @@ nc_base64der_to_cert_add_to_store(const char *in, void *cert_store) } static void * -nc_base64der_to_privatekey(const char *in, const char *key_str) +nc_base64der_to_privkey(const char *in, const char *key_str) { - char *buf; + char *buf = NULL; void *pkey; NC_CHECK_ARG_RET(NULL, in, NULL); @@ -154,7 +154,7 @@ nc_base64der_to_privatekey(const char *in, const char *key_str) return pkey; } -char * +static char * nc_server_tls_digest_to_hex(const unsigned char *digest, unsigned int digest_len) { unsigned int i; @@ -171,7 +171,7 @@ nc_server_tls_digest_to_hex(const unsigned char *digest, unsigned int digest_len return hex; } -char * +static char * nc_server_tls_md5(void *cert) { int rc; @@ -188,7 +188,7 @@ nc_server_tls_md5(void *cert) return nc_server_tls_digest_to_hex(buf, buf_len); } -char * +static char * nc_server_tls_sha1(void *cert) { int rc; @@ -205,7 +205,7 @@ nc_server_tls_sha1(void *cert) return nc_server_tls_digest_to_hex(buf, buf_len); } -char * +static char * nc_server_tls_sha224(void *cert) { int rc; @@ -222,7 +222,7 @@ nc_server_tls_sha224(void *cert) return nc_server_tls_digest_to_hex(buf, buf_len); } -char * +static char * nc_server_tls_sha256(void *cert) { int rc; @@ -239,7 +239,7 @@ nc_server_tls_sha256(void *cert) return nc_server_tls_digest_to_hex(buf, buf_len); } -char * +static char * nc_server_tls_sha384(void *cert) { int rc; @@ -256,7 +256,7 @@ nc_server_tls_sha384(void *cert) return nc_server_tls_digest_to_hex(buf, buf_len); } -char * +static char * nc_server_tls_sha512(void *cert) { int rc; @@ -441,11 +441,10 @@ nc_server_tls_verify_peer_cert(void *peer_cert, struct nc_cert_grouping *ee_cert } for (i = 0; i < cert_count; i++) { - cert = nc_tls_base64_to_cert_wrap(certs[i].data); - if (!cert) { - /* TODO skip? */ - continue; - } + /* import stored cert */ + cert = nc_base64der_to_cert(certs[i].data); + + /* compare stored with received */ ret = nc_server_tls_certs_match_wrap(peer_cert, cert); nc_tls_cert_destroy_wrap(cert); if (ret) { @@ -458,6 +457,95 @@ nc_server_tls_verify_peer_cert(void *peer_cert, struct nc_cert_grouping *ee_cert return 1; } +int +nc_server_tls_get_username_from_cert(void *cert, NC_TLS_CTN_MAPTYPE map_type, char **username) +{ + char *subject, *cn, *san_value = NULL; + void *sans; + int i, nsans = 0, rc; + NC_TLS_CTN_MAPTYPE san_type = 0; + +#ifdef HAVE_LIBMEDTLS + char rdn_separator = ','; +#else + char rdn_separator = '/'; +#endif + + if (map_type == NC_TLS_CTN_COMMON_NAME) { + subject = nc_server_tls_get_subject_wrap(cert); + if (!subject) { + return -1; + } + + cn = strstr(subject, "CN="); + if (!cn) { + WRN(NULL, "Certificate does not include the commonName field."); + free(subject); + return 1; + } + + /* skip "CN=" */ + cn += 3; + if (strchr(cn, rdn_separator)) { + *strchr(cn, rdn_separator) = '\0'; + } + *username = strdup(cn); + free(subject); + NC_CHECK_ERRMEM_RET(!*username, -1); + } else { + sans = nc_tls_get_sans_wrap(cert); + if (!sans) { + WRN(NULL, "Certificate has no SANs or failed to retrieve them."); + return 1; + } + nsans = nc_tls_get_num_sans_wrap(sans); + + for (i = 0; i < nsans; i++) { + if ((rc = nc_tls_get_san_value_type_wrap(sans, i, &san_value, &san_type))) { + if (rc == -1) { + /* fatal error */ + nc_tls_sans_destroy_wrap(sans); + return -1; + } + + /* got a type that we dont care about */ + continue; + } + + if ((map_type == NC_TLS_CTN_SAN_ANY) || (map_type == san_type)) { + /* found a match */ + *username = san_value; + break; + } + free(san_value); + } + + nc_tls_sans_destroy_wrap(sans); + + if (i == nsans) { + switch (map_type) { + case NC_TLS_CTN_SAN_RFC822_NAME: + WRN(NULL, "Certificate does not include the SAN rfc822Name field."); + break; + case NC_TLS_CTN_SAN_DNS_NAME: + WRN(NULL, "Certificate does not include the SAN dNSName field."); + break; + case NC_TLS_CTN_SAN_IP_ADDRESS: + WRN(NULL, "Certificate does not include the SAN iPAddress field."); + break; + case NC_TLS_CTN_SAN_ANY: + WRN(NULL, "Certificate does not include any relevant SAN fields."); + break; + default: + break; + } + return 1; + } + } + + return 0; +} + int nc_server_tls_verify_cert(void *cert, int depth, int self_signed, struct nc_tls_verify_cb_data *cb_data) { @@ -482,7 +570,7 @@ nc_server_tls_verify_cert(void *cert, int depth, int self_signed, struct nc_tls_ if (depth == 0) { if (self_signed) { /* peer cert is not trusted, so it must match any configured end-entity cert - * on the given endpoint in order for the cert to be authenticated */ + * on the given endpoint in order for the client to be authenticated */ ret = nc_server_tls_verify_peer_cert(cert, &opts->ee_certs); if (ret) { /* we can still check the referenced endpoint's ee certs */ @@ -503,6 +591,7 @@ nc_server_tls_verify_cert(void *cert, int depth, int self_signed, struct nc_tls_ } } + /* get matching ctn entries */ ret = nc_server_tls_cert_to_name(opts->ctn, cert, &cb_data->ctn_data); if (ret == -1) { /* fatal error */ @@ -524,24 +613,35 @@ nc_server_tls_verify_cert(void *cert, int depth, int self_signed, struct nc_tls_ } } - /* ctn */ + /* obtain username from matched ctn entries */ if (depth == 0) { for (i = 0; i < cb_data->ctn_data.matched_ctn_count; i++) { if (cb_data->ctn_data.matched_ctn_type[i] == NC_TLS_CTN_SPECIFIED) { session->username = strdup(cb_data->ctn_data.username); - NC_CHECK_ERRMEM_RET(!session->username, -1); + NC_CHECK_ERRMEM_GOTO(!session->username, ret = -1, cleanup); } else { - ret = nc_server_tls_get_username_from_cert_wrap(cert, cb_data->ctn_data.matched_ctn_type[i], &session->username); + ret = nc_server_tls_get_username_from_cert(cert, cb_data->ctn_data.matched_ctn_type[i], &session->username); if (ret == -1) { + /* fatal error */ goto cleanup; + } else if (!ret) { + /* username obtained */ + break; } } } + if (session->username) { + VRB(NULL, "Cert verify CTN: new client username recognized as \"%s\".", session->username); + } else { + VRB(NULL, "Cert verify CTN: unsuccessful, dropping the new client."); + ret = 1; + goto cleanup; + } } - if (server_opts.user_verify_clb && !server_opts.user_verify_clb(session)) { + if (session->username && server_opts.user_verify_clb && !server_opts.user_verify_clb(session)) { VRB(session, "Cert verify: user verify callback revoked authorization."); - ret = -1; + ret = 1; goto cleanup; } @@ -576,6 +676,8 @@ nc_server_tls_load_server_cert_key(struct nc_server_tls_opts *opts, void **srv_c void *cert = NULL; void *pkey = NULL; + *srv_cert = *srv_pkey = NULL; + /* get data needed for setting the server cert */ if (opts->store == NC_STORE_LOCAL) { /* local definition */ @@ -600,7 +702,7 @@ nc_server_tls_load_server_cert_key(struct nc_server_tls_opts *opts, void **srv_c return 1; } - pkey = nc_base64der_to_privatekey(privkey_data, nc_privkey_format_to_str(privkey_type)); + pkey = nc_base64der_to_privkey(privkey_data, nc_privkey_format_to_str(privkey_type)); if (!pkey) { nc_tls_cert_destroy_wrap(cert); return 1; @@ -637,19 +739,19 @@ nc_server_tls_curl_fetch(CURL *handle, const char *url) /* set uri */ if (curl_easy_setopt(handle, CURLOPT_URL, url)) { ERR(NULL, "Setting URI \"%s\" to download CRL from failed.", url); - return -1; + return 1; } /* set err buf */ if (curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, err_buf)) { ERR(NULL, "Setting CURL error buffer option failed."); - return -1; + return 1; } /* download */ if (curl_easy_perform(handle)) { ERR(NULL, "Downloading CRL from \"%s\" failed (%s).", url, err_buf); - return -1; + return 1; } return 0; @@ -665,30 +767,34 @@ nc_server_tls_curl_init(CURL **handle, struct nc_curl_data *data) *handle = curl_easy_init(); if (!*handle) { ERR(NULL, "Initializing CURL failed."); - return -1; + return 1; } if (curl_easy_setopt(*handle, CURLOPT_WRITEFUNCTION, nc_server_tls_curl_cb)) { ERR(NULL, "Setting curl callback failed."); - return -1; + return 1; } if (curl_easy_setopt(*handle, CURLOPT_WRITEDATA, data)) { ERR(NULL, "Setting curl callback data failed."); - return -1; + return 1; } return 0; } static int -nc_server_tls_crl_cert_ext(void *cert_store, void *crl_store) +nc_server_tls_crl_path(const char *path, void *cert_store, void *crl_store) +{ + return nc_tls_import_crl_path_wrap(path, cert_store, crl_store); +} + +static int +nc_server_tls_crl_url(const char *url, void *cert_store, void *crl_store) { int ret = 0; CURL *handle = NULL; struct nc_curl_data downloaded = {0}; - char **uris = NULL; - int uri_count = 0, i; /* init curl */ ret = nc_server_tls_curl_init(&handle, &downloaded); @@ -696,42 +802,33 @@ nc_server_tls_crl_cert_ext(void *cert_store, void *crl_store) goto cleanup; } - ret = nc_server_tls_get_crl_distpoint_uris_wrap(cert_store, &uris, &uri_count); + VRB(NULL, "Downloading CRL from \"%s\".", url); + + /* download the CRL */ + ret = nc_server_tls_curl_fetch(handle, url); if (ret) { goto cleanup; } - for (i = 0; i < uri_count; i++) { - VRB(NULL, "Downloading CRL from \"%s\".", uris[i]); - ret = nc_server_tls_curl_fetch(handle, uris[i]); - if (ret) { - /* failed to download the CRL from this entry, try the next entry */ - WRN(NULL, "Failed to fetch CRL from \"%s\".", uris[i]); - continue; - } - - /* convert the downloaded data to CRL and add it to the store */ - ret = nc_server_tls_add_crl_to_store_wrap(downloaded.data, downloaded.size, cert_store, crl_store); - if (ret) { - goto cleanup; - } + /* convert the downloaded data to CRL and add it to the store */ + ret = nc_server_tls_add_crl_to_store_wrap(downloaded.data, downloaded.size, cert_store, crl_store); + if (ret) { + goto cleanup; } cleanup: - for (i = 0; i < uri_count; i++) { - free(uris[i]); - } - free(uris); curl_easy_cleanup(handle); return ret; } static int -nc_server_tls_crl_url(const char *url, void *cert_store, void *crl_store) +nc_server_tls_crl_cert_ext(void *cert_store, void *crl_store) { int ret = 0; CURL *handle = NULL; struct nc_curl_data downloaded = {0}; + char **uris = NULL; + int uri_count = 0, i; /* init curl */ ret = nc_server_tls_curl_init(&handle, &downloaded); @@ -739,21 +836,33 @@ nc_server_tls_crl_url(const char *url, void *cert_store, void *crl_store) goto cleanup; } - VRB(NULL, "Downloading CRL from \"%s\".", url); - - /* download the CRL */ - ret = nc_server_tls_curl_fetch(handle, url); + /* get all the uris we can, even though some may point to the same CRL */ + ret = nc_server_tls_get_crl_distpoint_uris_wrap(cert_store, &uris, &uri_count); if (ret) { goto cleanup; } - /* convert the downloaded data to CRL and add it to the store */ - ret = nc_server_tls_add_crl_to_store_wrap(downloaded.data, downloaded.size, cert_store, crl_store); - if (ret) { - goto cleanup; + for (i = 0; i < uri_count; i++) { + VRB(NULL, "Downloading CRL from \"%s\".", uris[i]); + ret = nc_server_tls_curl_fetch(handle, uris[i]); + if (ret) { + /* failed to download the CRL from this entry, try the next entry */ + WRN(NULL, "Failed to fetch CRL from \"%s\".", uris[i]); + continue; + } + + /* convert the downloaded data to CRL and add it to the store */ + ret = nc_server_tls_add_crl_to_store_wrap(downloaded.data, downloaded.size, cert_store, crl_store); + if (ret) { + goto cleanup; + } } cleanup: + for (i = 0; i < uri_count; i++) { + free(uris[i]); + } + free(uris); curl_easy_cleanup(handle); return ret; } @@ -762,7 +871,7 @@ int nc_server_tls_load_crl(struct nc_server_tls_opts *opts, void *cert_store, void *crl_store) { if (opts->crl_path) { - if (nc_server_tls_crl_path_wrap(opts->crl_path, cert_store, crl_store)) { + if (nc_server_tls_crl_path(opts->crl_path, cert_store, crl_store)) { return 1; } } else if (opts->crl_url) { @@ -809,11 +918,16 @@ static int nc_server_tls_accept_check(int accept_ret, void *tls_session) { uint32_t verify; + char *err; /* check certificate verification result */ verify = nc_tls_get_verify_result_wrap(tls_session); if (!verify && (accept_ret == 1)) { VRB(NULL, "Client certificate verified."); + } else if (verify) { + err = nc_tls_verify_error_string_wrap(verify); + ERR(NULL, "Client certificate error (%s).", err); + free(err); } if (accept_ret != 1) { @@ -837,14 +951,6 @@ nc_accept_tls_session(struct nc_session *session, struct nc_server_tls_opts *opt /* set verify cb data */ cb_data.session = session; cb_data.opts = opts; - cb_data.ee_certs = &opts->ee_certs; - if (opts->referenced_endpt_name) { - if (nc_server_get_referenced_endpt(opts->referenced_endpt_name, &referenced_endpt)) { - ERRINT; - return 1; - } - cb_data.referenced_ee_certs = &referenced_endpt->opts.tls->ee_certs; - } /* prepare TLS context from which a session will be created */ tls_cfg = nc_server_tls_config_new_wrap(); @@ -872,6 +978,11 @@ nc_accept_tls_session(struct nc_session *session, struct nc_server_tls_opts *opt /* load referenced endpoint's trusted CA certs if set */ if (opts->referenced_endpt_name) { + if (nc_server_get_referenced_endpt(opts->referenced_endpt_name, &referenced_endpt)) { + ERR(session, "Referenced endpoint \"%s\" not found.", opts->referenced_endpt_name); + goto fail; + } + if (nc_server_tls_load_trusted_certs(&referenced_endpt->opts.tls->ca_certs, cert_store)) { ERR(session, "Loading server CA certs from referenced endpoint failed."); goto fail; @@ -900,6 +1011,11 @@ nc_accept_tls_session(struct nc_session *session, struct nc_server_tls_opts *opt } } + /* set supported cipher suites */ + if (opts->ciphers) { + nc_server_tls_set_cipher_suites_wrap(tls_cfg, opts->ciphers); + } + /* init TLS context and store data which may be needed later in it */ if (nc_tls_init_ctx_wrap(&session->ti.tls.ctx, sock, srv_cert, srv_pkey, cert_store, crl_store)) { goto fail; @@ -912,13 +1028,14 @@ nc_accept_tls_session(struct nc_session *session, struct nc_server_tls_opts *opt if (nc_tls_setup_config_wrap(tls_cfg, NC_SERVER, &session->ti.tls.ctx)) { goto fail; } // TODO free openssl shit + session->ti.tls.config = tls_cfg; + tls_cfg = NULL; /* fill session data and create TLS session from config */ - session->ti_type = NC_TI_OPENSSL; // TODO: prejmenovat - if (!(session->ti.tls.session = nc_tls_session_new_wrap(tls_cfg))) { + session->ti_type = NC_TI_TLS; + if (!(session->ti.tls.session = nc_tls_session_new_wrap(session->ti.tls.config))) { goto fail; } - session->ti.tls.config = tls_cfg; /* set verify callback and its data */ nc_server_tls_set_verify_cb_wrap(session->ti.tls.session, &cb_data); @@ -926,8 +1043,6 @@ nc_accept_tls_session(struct nc_session *session, struct nc_server_tls_opts *opt /* set session fd */ nc_server_tls_set_fd_wrap(session->ti.tls.session, sock, &session->ti.tls.ctx); - /* TODO: ciphers */ - sock = -1; /* do the handshake */ @@ -955,7 +1070,11 @@ nc_accept_tls_session(struct nc_session *session, struct nc_server_tls_opts *opt close(sock); } - nc_tls_session_new_cleanup_wrap(tls_cfg, srv_cert, srv_pkey, cert_store, crl_store); + nc_tls_config_destroy_wrap(tls_cfg); + nc_tls_cert_destroy_wrap(srv_cert); + nc_tls_privkey_destroy_wrap(srv_pkey); + nc_tls_cert_store_destroy_wrap(cert_store); + nc_tls_crl_store_destroy_wrap(crl_store); if (timeouted) { return 0; From d2828c87cfdb4c8f22d9ed48a06e96122d5f94e9 Mon Sep 17 00:00:00 2001 From: roman Date: Tue, 23 Apr 2024 15:05:50 +0200 Subject: [PATCH 21/54] cmake UPDATE mbedtls/openssl compilation --- CMakeLists.txt | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9ba24aaf..a1fdd0e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -124,8 +124,7 @@ if(ENABLE_SSH_TLS) src/session_server_tls.c src/server_config_util_tls.c src/server_config_ks.c - src/server_config_ts.c - src/session_mbedtls.c) + src/server_config_ts.c) set(SSH_TLS_MACRO "#ifndef NC_ENABLED_SSH_TLS\n#define NC_ENABLED_SSH_TLS\n#endif") endif() @@ -211,8 +210,10 @@ endif() # use compat use_compat() -# netconf2 target -add_library(netconf2 ${libsrc} ${compatsrc}) +# netconf2 sourceless target - need it for linking libs, but the required sources will be added later +add_library(netconf2) + +# set the shared library version set_target_properties(netconf2 PROPERTIES VERSION ${LIBNETCONF2_SOVERSION_FULL} SOVERSION ${LIBNETCONF2_SOVERSION}) # include repository files with highest priority @@ -232,19 +233,22 @@ check_include_file("shadow.h" HAVE_SHADOW) check_include_file("termios.h" HAVE_TERMIOS) if(ENABLE_SSH_TLS) - find_package(LibMbedTLS 3.5.0) # TODO + # dependencies - mbedTLS (higher preference) or OpenSSL + find_package(LibMbedTLS 3.5.2) if (LIBMBEDTLS_FOUND) + # dependencies - mbedtls set(HAVE_LIBMBEDTLS TRUE) + list(APPEND libsrc src/session_mbedtls.c) + include_directories(${LIBMBEDTLS_INCLUDE_DIRS}) target_link_libraries(netconf2 ${LIBMBEDTLS_LIBRARIES}) list(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBMBEDTLS_LIBRARIES}) - include_directories(${LIBMBEDTLS_INCLUDE_DIRS}) - #target_sources(netconf2 PRIVATE src/session_mbedtls.c) else() # dependencies - openssl find_package(OpenSSL 3.0.0 REQUIRED) - target_link_libraries(netconf2 ${OPENSSL_LIBRARIES}) + list(APPEND libsrc src/session_openssl.c) include_directories(${OPENSSL_INCLUDE_DIR}) - target_sources(netconf2 PRIVATE src/session_openssl.c) + target_link_libraries(netconf2 ${OPENSSL_LIBRARIES}) + list(APPEND CMAKE_REQUIRED_LIBRARIES ${OPENSSL_LIBRARIES}) endif() # dependencies - libssh @@ -312,6 +316,9 @@ if(${CMAKE_SYSTEM_NAME} MATCHES "QNX") list(REMOVE_ITEM CMAKE_REQUIRED_DEFINITIONS -D_QNX_SOURCE) endif() +# set sources +target_sources(netconf2 PRIVATE ${libsrc} ${compatsrc}) + # generate config file configure_file("${PROJECT_SOURCE_DIR}/src/config.h.in" "${PROJECT_BINARY_DIR}/src/config.h" ESCAPE_QUOTES @ONLY) From ef72e009b7fd4cb43449c0fdb0098853d121cf9e Mon Sep 17 00:00:00 2001 From: roman Date: Tue, 23 Apr 2024 15:08:06 +0200 Subject: [PATCH 22/54] session mbedtls UPDATE reworked everything --- src/session_mbedtls.c | 1273 ++++++++++++++++++++--------------------- 1 file changed, 633 insertions(+), 640 deletions(-) diff --git a/src/session_mbedtls.c b/src/session_mbedtls.c index 987670fc..49eb8c14 100644 --- a/src/session_mbedtls.c +++ b/src/session_mbedtls.c @@ -1,5 +1,6 @@ #define _GNU_SOURCE +#include #include #include #include @@ -34,7 +35,124 @@ #include #include -extern struct nc_server_opts server_opts; +/* some mbedTLS functions may return 'high' and some 'low' level errors, try to handle both cases this way */ +static const char * +nc_get_mbedtls_str_err(int err) +{ + const char *err_str; + + err_str = mbedtls_high_level_strerr(err); + if (err_str) { + return err_str; + } + + err_str = mbedtls_low_level_strerr(err); + if (err_str) { + return err_str; + } + + return "unknown error"; +} + +/** + * @brief Converts DN to a string. + * + * @param[in] dn Internal DN representation. + * @return DN string on success, NULL of fail. + */ +static char * +nc_server_tls_dn2str(const mbedtls_x509_name *dn) +{ + char *str; + size_t len = 64; + int r; + + str = malloc(len); + NC_CHECK_ERRMEM_RET(!str, NULL); + + while ((r = mbedtls_x509_dn_gets(str, len, dn)) == MBEDTLS_ERR_X509_BUFFER_TOO_SMALL) { + len <<= 1; + str = nc_realloc(str, len); + NC_CHECK_ERRMEM_RET(!str, NULL); + } + if (r < 1) { + free(str); + ERR(NULL, "Failed to convert DN to string (%s).", nc_get_mbedtls_str_err(r)); + return NULL; + } + + return str; +} + +/* creates a new rng context needed for PK operations and for ssl config */ +static int +nc_tls_rng_new(mbedtls_ctr_drbg_context **ctr_drbg, mbedtls_entropy_context **entropy) +{ + int rc; + + *ctr_drbg = NULL; + *entropy = NULL; + + *entropy = malloc(sizeof **entropy); + NC_CHECK_ERRMEM_GOTO(!*entropy, , fail); + *ctr_drbg = malloc(sizeof **ctr_drbg); + NC_CHECK_ERRMEM_GOTO(!*ctr_drbg, , fail); + + mbedtls_entropy_init(*entropy); + mbedtls_ctr_drbg_init(*ctr_drbg); + + rc = mbedtls_ctr_drbg_seed(*ctr_drbg, mbedtls_entropy_func, *entropy, NULL, 0); + if (rc) { + ERR(NULL, "Seeding ctr_drbg failed (%s).", nc_get_mbedtls_str_err(rc)); + goto fail; + } + + return 0; + +fail: + mbedtls_ctr_drbg_free(*ctr_drbg); + free(*ctr_drbg); + if (*entropy) { + mbedtls_entropy_free(*entropy); + free(*entropy); + } + *ctr_drbg = NULL; + *entropy = NULL; + return 1; +} + +static void +nc_tls_rng_destroy(mbedtls_ctr_drbg_context *ctr_drbg, mbedtls_entropy_context *entropy) +{ + mbedtls_ctr_drbg_free(ctr_drbg); + free(ctr_drbg); + if (entropy) { + mbedtls_entropy_free(entropy); + free(entropy); + } +} + +/* get verify err string, caller is responsible for freeing it, 256B should be more than enough */ +static char * +nc_tls_get_verify_err_str(int err) +{ + int ret; + char *err_buf = NULL; + + err_buf = malloc(256); + NC_CHECK_ERRMEM_RET(!err_buf, NULL); + + ret = mbedtls_x509_crt_verify_info(err_buf, 256, "", err); + if (ret < 0) { + free(err_buf); + return NULL; + } + + /* strip the NL */ + err_buf[ret - 1] = '\0'; + + return err_buf; +} void * nc_tls_session_new_wrap(void *tls_cfg) @@ -49,7 +167,7 @@ nc_tls_session_new_wrap(void *tls_cfg) rc = mbedtls_ssl_setup(session, tls_cfg); if (rc) { - ERR(NULL, "Setting up TLS context failed (%s).", mbedtls_high_level_strerr(rc)); + ERR(NULL, "Setting up TLS session failed (%s).", nc_get_mbedtls_str_err(rc)); mbedtls_ssl_free(session); free(session); return NULL; @@ -66,19 +184,7 @@ nc_tls_session_destroy_wrap(void *tls_session) } void * -nc_server_tls_config_new_wrap() -{ - mbedtls_ssl_config *tls_cfg; - - tls_cfg = malloc(sizeof *tls_cfg); - NC_CHECK_ERRMEM_RET(!tls_cfg, NULL); - - mbedtls_ssl_config_init(tls_cfg); - return tls_cfg; -} - -void * -nc_client_tls_config_new_wrap() +nc_tls_config_new_wrap(int UNUSED(side)) { mbedtls_ssl_config *tls_cfg; @@ -119,8 +225,8 @@ nc_tls_cert_destroy_wrap(void *cert) free(cert); } -void * -nc_tls_privkey_new_wrap() +static void * +nc_tls_pkey_new_wrap(void) { mbedtls_pk_context *pkey; @@ -141,12 +247,14 @@ nc_tls_privkey_destroy_wrap(void *pkey) void * nc_tls_cert_store_new_wrap() { + /* certificate is the same as a certificate store in MbedTLS */ return nc_tls_cert_new_wrap(); } void nc_tls_cert_store_destroy_wrap(void *cert_store) { + /* certificate is the same as a certificate store in MbedTLS */ nc_tls_cert_destroy_wrap(cert_store); } @@ -163,70 +271,10 @@ nc_tls_crl_store_new_wrap() } void -nc_tls_crl_store_destroy_wrap(void *crl) -{ - mbedtls_x509_crl_free(crl); - free(crl); -} - -static int -nc_tls_rng_new(mbedtls_ctr_drbg_context **ctr_drbg, mbedtls_entropy_context **entropy) -{ - *ctr_drbg = NULL; - *entropy = NULL; - - *entropy = malloc(sizeof **entropy); - NC_CHECK_ERRMEM_GOTO(!*entropy, , fail); - *ctr_drbg = malloc(sizeof **ctr_drbg); - NC_CHECK_ERRMEM_GOTO(!*ctr_drbg, , fail); - - mbedtls_entropy_init(*entropy); - mbedtls_ctr_drbg_init(*ctr_drbg); - - if (mbedtls_ctr_drbg_seed(*ctr_drbg, mbedtls_entropy_func, *entropy, NULL, 0)) { - ERR(NULL, "Seeding ctr_drbg failed."); - goto fail; - } - - return 0; - -fail: - mbedtls_ctr_drbg_free(*ctr_drbg); - free(*ctr_drbg); - mbedtls_entropy_free(*entropy); - free(*entropy); - *ctr_drbg = NULL; - *entropy = NULL; - return 1; -} - -static void -nc_tls_rng_destroy(mbedtls_ctr_drbg_context *ctr_drbg, mbedtls_entropy_context *entropy) -{ - mbedtls_ctr_drbg_free(ctr_drbg); - free(ctr_drbg); - mbedtls_entropy_free(entropy); - free(entropy); -} - -void -nc_tls_set_authmode_wrap(void *tls_cfg) -{ - mbedtls_ssl_conf_authmode(tls_cfg, MBEDTLS_SSL_VERIFY_REQUIRED); -} - -int -nc_server_tls_set_config_defaults_wrap(void *tls_cfg) +nc_tls_crl_store_destroy_wrap(void *crl_store) { - int rc; - - rc = mbedtls_ssl_config_defaults(tls_cfg, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); - if (rc) { - ERR(NULL, "Setting default TLS config failed (%s).", mbedtls_high_level_strerr(rc)); - return 1; - } - - return 0; + mbedtls_x509_crl_free(crl_store); + free(crl_store); } void * @@ -242,36 +290,7 @@ nc_tls_pem_to_cert_wrap(const char *cert_data) rc = mbedtls_x509_crt_parse(cert, (const unsigned char *)cert_data, strlen(cert_data) + 1); if (rc) { - ERR(NULL, "Parsing certificate data failed (%s).", mbedtls_high_level_strerr(rc)); - nc_tls_cert_destroy_wrap(cert); - return NULL; - } - - return cert; -} - -void * -nc_tls_base64_to_cert_wrap(const char *cert_data) -{ - int rc; - mbedtls_x509_crt *cert; - char *pem = NULL; - - cert = nc_tls_cert_new_wrap(); - if (!cert) { - return NULL; - } - - rc = asprintf(&pem, "-----BEGIN CERTIFICATE-----\n%s\n-----END CERTIFICATE-----", cert_data); - if (rc == -1) { - ERRMEM; - nc_tls_cert_destroy_wrap(cert); - return NULL; - } - - rc = mbedtls_x509_crt_parse(cert, (const unsigned char *)pem, strlen(pem) + 1); - if (rc) { - ERR(NULL, "Parsing certificate data failed (%s).", mbedtls_high_level_strerr(rc)); + ERR(NULL, "Parsing certificate data failed (%s).", nc_get_mbedtls_str_err(rc)); nc_tls_cert_destroy_wrap(cert); return NULL; } @@ -280,15 +299,16 @@ nc_tls_base64_to_cert_wrap(const char *cert_data) } int -nc_tls_pem_to_cert_add_to_store_wrap(const char *cert_data, void *cert_store) +nc_tls_add_cert_to_store_wrap(void *cert, void *cert_store) { - int rc; + mbedtls_x509_crt *iter; - rc = mbedtls_x509_crt_parse(cert_store, (const unsigned char *)cert_data, strlen(cert_data) + 1); - if (rc) { - ERR(NULL, "Parsing certificate data failed (%s).", mbedtls_high_level_strerr(rc)); - return 1; + /* store is a linked list */ + iter = cert_store; + while (iter->next) { + iter = iter->next; } + iter->next = cert; return 0; } @@ -296,55 +316,45 @@ nc_tls_pem_to_cert_add_to_store_wrap(const char *cert_data, void *cert_store) void * nc_tls_pem_to_privkey_wrap(const char *privkey_data) { - int rc; - mbedtls_pk_context *pkey; - mbedtls_ctr_drbg_context *ctr_drbg; - mbedtls_entropy_context *entropy; + int rc, ret = 0; + mbedtls_pk_context *pkey = NULL; + mbedtls_ctr_drbg_context *ctr_drbg = NULL; + mbedtls_entropy_context *entropy = NULL; - if (nc_tls_rng_new(&ctr_drbg, &entropy)) { - return NULL; + ret = nc_tls_rng_new(&ctr_drbg, &entropy); + if (ret) { + goto cleanup; } - pkey = nc_tls_privkey_new_wrap(); + pkey = nc_tls_pkey_new_wrap(); if (!pkey) { - nc_tls_rng_destroy(ctr_drbg, entropy); - return NULL; + ret = 1; + goto cleanup; } rc = mbedtls_pk_parse_key(pkey, (const unsigned char *)privkey_data, strlen(privkey_data) + 1, NULL, 0, mbedtls_ctr_drbg_random, ctr_drbg); - nc_tls_rng_destroy(ctr_drbg, entropy); if (rc) { - ERR(NULL, "Parsing private key data failed (%s).", mbedtls_high_level_strerr(rc)); - nc_tls_privkey_destroy_wrap(pkey); - return NULL; + ERR(NULL, "Parsing private key data failed (%s).", nc_get_mbedtls_str_err(rc)); + ret = 1; + goto cleanup; } - return pkey; -} - -int -nc_tls_load_cert_private_key_wrap(void *tls_cfg, void *cert, void *pkey) -{ - int rc; - rc = mbedtls_ssl_conf_own_cert(tls_cfg, cert, pkey); - if (rc) { - ERR(NULL, "Loading the server certificate or private key failed (%s).", mbedtls_high_level_strerr(rc)); - return 1; +cleanup: + if (ret) { + nc_tls_privkey_destroy_wrap(pkey); } - - return 0; + nc_tls_rng_destroy(ctr_drbg, entropy); + return pkey; } int -nc_server_tls_crl_path_wrap(const char *crl_path, void *cert_store, void *crl_store) +nc_tls_import_crl_path_wrap(const char *path, void *crl_store) { int rc; - (void) cert_store; - - rc = mbedtls_x509_crl_parse_file(crl_store, crl_path); + rc = mbedtls_x509_crl_parse_file(crl_store, path); if (rc) { - ERR(NULL, "Error adding CRL to store (%s)", mbedtls_high_level_strerr(rc)); + ERR(NULL, "Failed to import CRL from file \"%s\" (%s).", path, nc_get_mbedtls_str_err(rc)); return 1; } @@ -352,12 +362,10 @@ nc_server_tls_crl_path_wrap(const char *crl_path, void *cert_store, void *crl_st } int -nc_server_tls_add_crl_to_store_wrap(const unsigned char *crl_data, size_t size, void *cert_store, void *crl_store) +nc_server_tls_add_crl_to_store_wrap(const unsigned char *crl_data, size_t size, void *crl_store) { int rc; - (void) cert_store; - /* try DER first */ rc = mbedtls_x509_crl_parse_der(crl_store, crl_data, size); if (!rc) { @@ -366,7 +374,7 @@ nc_server_tls_add_crl_to_store_wrap(const unsigned char *crl_data, size_t size, } /* DER failed, try PEM */ - rc = mbedtls_x509_crl_parse(crl_store, crl_data, size); + rc = mbedtls_x509_crl_parse(crl_store, crl_data, size + 1); if (!rc) { /* success, it was PEM */ return 0; @@ -377,12 +385,6 @@ nc_server_tls_add_crl_to_store_wrap(const unsigned char *crl_data, size_t size, return 1; } -void -nc_server_tls_set_certs_wrap(void *tls_cfg, void *cert_store, void *crl_store) -{ - mbedtls_ssl_conf_ca_chain(tls_cfg, cert_store, crl_store); -} - int nc_server_tls_set_tls_versions_wrap(void *tls_cfg, unsigned int tls_versions) { @@ -413,29 +415,33 @@ nc_server_tls_verify_cb(void *cb_data, mbedtls_x509_crt *cert, int depth, uint32 { int ret = 0; struct nc_tls_verify_cb_data *data = cb_data; - size_t buf_len = 256; - char err_buf[buf_len]; + char *err; if (!*flags) { /* in-built verification was successful */ ret = nc_server_tls_verify_cert(cert, depth, 0, data); } else { - /* in-built verification was failed, either check if peer cert matches any configured cert, or just - * return success and wait until we reach depth 0 + /* in-built verification failed, but the client still may be authenticated if: + * 1) the peer cert matches any configured end-entity cert + * 2) the peer cert has a valid chain of trust to any configured certificate authority cert + * otherwise just continue until we reach the peer cert (depth = 0) */ if ((depth == 0) && (*flags == MBEDTLS_X509_BADCERT_NOT_TRUSTED)) { - /* not trusted self-signed peer certificate */ + /* not trusted self-signed peer certificate, case 1) */ ret = nc_server_tls_verify_cert(cert, depth, 1, data); if (!ret) { *flags &= ~MBEDTLS_X509_BADCERT_NOT_TRUSTED; } - } else { - buf_len = mbedtls_x509_crt_verify_info(err_buf, buf_len, "", *flags); - if (buf_len > 0) { - /* strip the NL and print it */ - err_buf[buf_len - 1] = '\0'; - ERR(data->session, "Cert verify: fail (%s).", err_buf); + } else if (*flags == MBEDTLS_X509_BADCERT_MISSING) { + /* full chain of trust is invalid, but it may be valid partially, case 2) */ + ret = nc_server_tls_verify_cert(cert, depth, 1, data); + if (!ret) { + *flags &= ~MBEDTLS_X509_BADCERT_MISSING; } + } else { + err = nc_tls_get_verify_err_str(*flags); + ERR(data->session, "Cert verify: fail (%s).", err); + free(err); ret = 1; } } @@ -451,45 +457,26 @@ nc_server_tls_verify_cb(void *cb_data, mbedtls_x509_crt *cert, int depth, uint32 /* chain verify failed, but peer cert can still match */ return 0; } else { - /* peer cert did not match */ - return 1; + /* failed to verify peer cert, but return 0 so that we can propagate the error via the flags */ + if (!*flags) { + *flags |= MBEDTLS_X509_BADCERT_OTHER; + } + return 0; } } } void -nc_server_tls_set_verify_cb_wrap(void *tls_session, struct nc_tls_verify_cb_data *cb_data) +nc_server_tls_set_verify_wrap(void *tls_cfg, struct nc_tls_verify_cb_data *cb_data) { - mbedtls_ssl_set_verify(tls_session, nc_server_tls_verify_cb, cb_data); + mbedtls_ssl_conf_authmode(tls_cfg, MBEDTLS_SSL_VERIFY_REQUIRED); + mbedtls_ssl_conf_verify(tls_cfg, nc_server_tls_verify_cb, cb_data); } -static char * -nc_server_tls_dn2str(const mbedtls_x509_name *dn) +void +nc_client_tls_set_verify_wrap(void *tls_cfg) { - char *str; - size_t len = 64; - int r; - void *ptr; - - str = malloc(len); - NC_CHECK_ERRMEM_RET(!str, NULL); - - while ((r = mbedtls_x509_dn_gets(str, len, dn)) == MBEDTLS_ERR_X509_BUFFER_TOO_SMALL) { - len <<= 1; - ptr = realloc(str, len); - if (!ptr) { - ERRMEM; - free(str); - return NULL; - } - str = ptr; - } - if (r < 1) { - free(str); - ERRMEM; - return NULL; - } - return str; + mbedtls_ssl_conf_authmode(tls_cfg, MBEDTLS_SSL_VERIFY_REQUIRED); } char * @@ -504,106 +491,96 @@ nc_server_tls_get_issuer_wrap(void *cert) return nc_server_tls_dn2str(&(((mbedtls_x509_crt *)cert)->issuer)); } +void * +nc_tls_get_sans_wrap(void *cert) +{ + return &(((mbedtls_x509_crt *)cert)->subject_alt_names); +} + +void +nc_tls_sans_destroy_wrap(void *UNUSED(sans)) +{ + return; +} + int -nc_server_tls_get_username_from_cert_wrap(void *cert, NC_TLS_CTN_MAPTYPE map_type, char **username) +nc_tls_get_num_sans_wrap(void *sans) { - int rc; - char *subject, *common_name; + mbedtls_x509_sequence *iter; + int n = 0; + + /* sans are a linked list */ + iter = sans; + while (iter) { + ++n; + iter = iter->next; + } + + return n; +} + +int +nc_tls_get_san_value_type_wrap(void *sans, int idx, char **san_value, NC_TLS_CTN_MAPTYPE *san_type) +{ + int i, rc, ret = 0; + mbedtls_x509_sequence *iter; mbedtls_x509_subject_alternative_name san = {0}; - mbedtls_x509_sequence *cur = NULL; const mbedtls_x509_buf *ip; - mbedtls_x509_crt *peer_cert = cert; - - *username = NULL; - if (map_type == NC_TLS_CTN_COMMON_NAME) { - subject = nc_server_tls_get_subject_wrap(peer_cert); - NC_CHECK_ERRMEM_RET(!subject, -1); - common_name = strstr(subject, "CN="); - if (!common_name) { - WRN(NULL, "Certificate does not include the commonName field."); - free(subject); - return 1; - } - common_name += 3; - if (strchr(common_name, ',')) { - *strchr(common_name, ',') = '\0'; - } - *username = strdup(common_name); - free(subject); - NC_CHECK_ERRMEM_RET(!*username, -1); - } else { - /* retrieve subjectAltName's rfc822Name (email), dNSName and iPAddress values */ - cur = &peer_cert->subject_alt_names; - while (cur) { - rc = mbedtls_x509_parse_subject_alt_name(&cur->buf, &san); - if (rc && (rc != MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE)) { - ERR(NULL, "Getting SANs failed."); - return 1; - } - if (((map_type == NC_TLS_CTN_SAN_ANY) || (map_type == NC_TLS_CTN_SAN_DNS_NAME)) && - (san.type == MBEDTLS_X509_SAN_DNS_NAME)) { - *username = strndup((const char *)san.san.unstructured_name.p, san.san.unstructured_name.len); // TODO: tolower()? - NC_CHECK_ERRMEM_RET(!*username, -1); - break; - } + *san_value = NULL; + *san_type = NC_TLS_CTN_UNKNOWN; - if (((map_type == NC_TLS_CTN_SAN_ANY) || (map_type == NC_TLS_CTN_SAN_RFC822_NAME)) && - (san.type == MBEDTLS_X509_SAN_RFC822_NAME)) { - *username = strndup((const char *)san.san.unstructured_name.p, san.san.unstructured_name.len); - NC_CHECK_ERRMEM_RET(!*username, -1); - break; - } - - /* iPAddress */ - if (((map_type == NC_TLS_CTN_SAN_ANY) || (map_type == NC_TLS_CTN_SAN_IP_ADDRESS)) && - (san.type == MBEDTLS_X509_SAN_IP_ADDRESS)) { - ip = &san.san.unstructured_name; - if (ip->len == 4) { - if (asprintf(username, "%d.%d.%d.%d", ip->p[0], ip->p[1], ip->p[2], ip->p[3]) == -1) { - ERRMEM; - return -1; - } - break; - } else if (ip->len == 16) { - if (asprintf(username, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", - ip->p[0], ip->p[1], ip->p[2], ip->p[3], ip->p[4], ip->p[5], - ip->p[6], ip->p[7], ip->p[8], ip->p[9], ip->p[10], ip->p[11], - ip->p[12], ip->p[13], ip->p[14], ip->p[15]) == -1) { - ERRMEM; - return -1; - } - break; - } else { - WRN(NULL, "SAN IP address in an unknown format (length is %d).", ip->len); - } - } + /* find the SAN */ + iter = sans; + for (i = 0; i < idx; i++) { + iter = iter->next; + } - cur = cur->next; - } + /* parse it */ + rc = mbedtls_x509_parse_subject_alt_name(&iter->buf, &san); + if (rc && (rc != MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE)) { + return -1; + } - if (!*username) { - switch (map_type) { - case NC_TLS_CTN_SAN_RFC822_NAME: - WRN(NULL, "Certificate does not include the SAN rfc822Name field."); - break; - case NC_TLS_CTN_SAN_DNS_NAME: - WRN(NULL, "Certificate does not include the SAN dNSName field."); - break; - case NC_TLS_CTN_SAN_IP_ADDRESS: - WRN(NULL, "Certificate does not include the SAN iPAddress field."); - break; - case NC_TLS_CTN_SAN_ANY: - WRN(NULL, "Certificate does not include any relevant SAN fields."); - break; - default: - break; - } - return 1; + /* get its type and value */ + switch (san.type) { + case MBEDTLS_X509_SAN_DNS_NAME: + *san_type = NC_TLS_CTN_SAN_DNS_NAME; + *san_value = strndup((const char *)san.san.unstructured_name.p, san.san.unstructured_name.len); + NC_CHECK_ERRMEM_GOTO(!*san_value, ret = -1, cleanup); + break; + case MBEDTLS_X509_SAN_RFC822_NAME: + *san_type = NC_TLS_CTN_SAN_RFC822_NAME; + *san_value = strndup((const char *)san.san.unstructured_name.p, san.san.unstructured_name.len); + NC_CHECK_ERRMEM_GOTO(!*san_value, ret = -1, cleanup); + break; + case MBEDTLS_X509_SAN_IP_ADDRESS: + *san_type = NC_TLS_CTN_SAN_IP_ADDRESS; + ip = &san.san.unstructured_name; + if (ip->len == 4) { + rc = asprintf(san_value, "%d.%d.%d.%d", ip->p[0], ip->p[1], ip->p[2], ip->p[3]) == -1; + NC_CHECK_ERRMEM_GOTO(rc == -1, ret = -1, cleanup); + } else if (ip->len == 16) { + rc = asprintf(san_value, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + ip->p[0], ip->p[1], ip->p[2], ip->p[3], ip->p[4], ip->p[5], + ip->p[6], ip->p[7], ip->p[8], ip->p[9], ip->p[10], ip->p[11], + ip->p[12], ip->p[13], ip->p[14], ip->p[15]); + NC_CHECK_ERRMEM_GOTO(rc == -1, ret = -1, cleanup); + } else { + WRN(NULL, "SAN IP address in an unknown format (length is %d).", ip->len); + ret = 1; } + break; + default: + /* we dont care about other types */ + *san_type = NC_TLS_CTN_UNKNOWN; + ret = 1; + break; } - return 0; +cleanup: + mbedtls_x509_free_subject_alt_name(&san); + return ret; } int @@ -633,7 +610,7 @@ nc_server_tls_md5_wrap(void *cert, unsigned char *buf) rc = mbedtls_md5(c->raw.p, c->raw.len, buf); if (rc) { - ERR(NULL, "Calculating MD5 digest failed (%s).", mbedtls_high_level_strerr(rc)); + ERR(NULL, "Calculating MD5 digest failed (%s).", nc_get_mbedtls_str_err(rc)); return 1; } @@ -648,7 +625,7 @@ nc_server_tls_sha1_wrap(void *cert, unsigned char *buf) rc = mbedtls_sha1(c->raw.p, c->raw.len, buf); if (rc) { - ERR(NULL, "Calculating SHA-1 digest failed (%s).", mbedtls_high_level_strerr(rc)); + ERR(NULL, "Calculating SHA-1 digest failed (%s).", nc_get_mbedtls_str_err(rc)); return 1; } @@ -663,7 +640,7 @@ nc_server_tls_sha224_wrap(void *cert, unsigned char *buf) rc = mbedtls_sha256(c->raw.p, c->raw.len, buf, 1); if (rc) { - ERR(NULL, "Calculating SHA-224 digest failed (%s).", mbedtls_high_level_strerr(rc)); + ERR(NULL, "Calculating SHA-224 digest failed (%s).", nc_get_mbedtls_str_err(rc)); return 1; } @@ -678,7 +655,7 @@ nc_server_tls_sha256_wrap(void *cert, unsigned char *buf) rc = mbedtls_sha256(c->raw.p, c->raw.len, buf, 0); if (rc) { - ERR(NULL, "Calculating SHA-256 digest failed (%s).", mbedtls_high_level_strerr(rc)); + ERR(NULL, "Calculating SHA-256 digest failed (%s).", nc_get_mbedtls_str_err(rc)); return 1; } @@ -693,7 +670,7 @@ nc_server_tls_sha384_wrap(void *cert, unsigned char *buf) rc = mbedtls_sha512(c->raw.p, c->raw.len, buf, 1); if (rc) { - ERR(NULL, "Calculating SHA-384 digest failed (%s).", mbedtls_high_level_strerr(rc)); + ERR(NULL, "Calculating SHA-384 digest failed (%s).", nc_get_mbedtls_str_err(rc)); return 1; } @@ -708,17 +685,64 @@ nc_server_tls_sha512_wrap(void *cert, unsigned char *buf) rc = mbedtls_sha512(c->raw.p, c->raw.len, buf, 0); if (rc) { - ERR(NULL, "Calculating SHA-512 digest failed (%s).", mbedtls_high_level_strerr(rc)); + ERR(NULL, "Calculating SHA-512 digest failed (%s).", nc_get_mbedtls_str_err(rc)); return 1; } return 0; } +static int +nc_server_tls_send(void *ctx, const unsigned char *buf, size_t len) +{ + int sock, ret; + + NC_CHECK_ARG_RET(NULL, ctx, MBEDTLS_ERR_NET_INVALID_CONTEXT); + + sock = *(int *)ctx; + + ret = send(sock, buf, len, MSG_NOSIGNAL); + if (ret < 0) { + if ((errno == EWOULDBLOCK) || (errno = EAGAIN) || (errno == EINTR)) { + return MBEDTLS_ERR_SSL_WANT_WRITE; + } else if ((errno == EPIPE) || (errno == ECONNRESET)) { + return MBEDTLS_ERR_NET_CONN_RESET; + } else { + return MBEDTLS_ERR_NET_SEND_FAILED; + } + } + + return ret; +} + +static int +nc_server_tls_recv(void *ctx, unsigned char *buf, size_t len) +{ + int sock, ret; + + NC_CHECK_ARG_RET(NULL, ctx, MBEDTLS_ERR_NET_INVALID_CONTEXT); + + sock = *(int *)ctx; + + ret = recv(sock, buf, len, 0); + if (ret < 0) { + if ((errno == EWOULDBLOCK) || (errno = EAGAIN) || (errno == EINTR)) { + return MBEDTLS_ERR_SSL_WANT_READ; + } else if ((errno == EPIPE) || (errno == ECONNRESET)) { + return MBEDTLS_ERR_NET_CONN_RESET; + } else { + return MBEDTLS_ERR_NET_RECV_FAILED; + } + } + + return ret; +} + void nc_server_tls_set_fd_wrap(void *tls_session, int UNUSED(sock), struct nc_tls_ctx *tls_ctx) { - mbedtls_ssl_set_bio(tls_session, tls_ctx->sock, mbedtls_net_send, mbedtls_net_recv, NULL); + /* mbedtls sets a pointer to the sock, which is stored in tls_ctx */ + mbedtls_ssl_set_bio(tls_session, tls_ctx->sock, nc_server_tls_send, nc_server_tls_recv, NULL); } int @@ -732,72 +756,39 @@ nc_server_tls_handshake_step_wrap(void *tls_session) } else if ((rc == MBEDTLS_ERR_SSL_WANT_READ) || (rc == MBEDTLS_ERR_SSL_WANT_WRITE)) { return 0; } else { - return -1; - } -} - -int -nc_server_tls_fill_config_wrap(void *tls_cfg, void *srv_cert, void *srv_pkey, void *cert_store, void *crl_store, struct nc_tls_ctx *tls_ctx) -{ - int rc; - - rc = mbedtls_ssl_config_defaults(tls_cfg, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); - if (rc) { - ERR(NULL, "Setting default TLS config failed (%s).", mbedtls_high_level_strerr(rc)); - return 1; - } - - mbedtls_ssl_conf_rng(tls_cfg, mbedtls_ctr_drbg_random, tls_ctx->ctr_drbg); - mbedtls_ssl_conf_authmode(tls_cfg, MBEDTLS_SSL_VERIFY_REQUIRED); - - mbedtls_ssl_conf_own_cert(tls_cfg, srv_cert, srv_pkey); - mbedtls_ssl_conf_ca_chain(tls_cfg, cert_store, crl_store); - - tls_ctx->cert = srv_cert; - tls_ctx->pkey = srv_pkey; - tls_ctx->cert_store = cert_store; - tls_ctx->crl_store = crl_store; - return 0; + return rc; + } } int -nc_server_tls_setup_config_fill_ctx_wrap(void *tls_cfg, struct nc_tls_ctx *tls_ctx, void *srv_cert, void *srv_pkey, void *cert_store, void *crl_store, int sock) +nc_client_tls_handshake_step_wrap(void *tls_session, int sock) { - int rc; - - rc = mbedtls_ssl_config_defaults(tls_cfg, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); - if (rc) { - ERR(NULL, "Setting default TLS config failed (%s).", mbedtls_high_level_strerr(rc)); - return 1; - } + int rc = 0; + struct pollfd pfd = {sock, 0, 0}; - rc = nc_tls_rng_new(&tls_ctx->ctr_drbg, &tls_ctx->entropy); - if (rc) { + rc = mbedtls_ssl_handshake(tls_session); + if (!rc) { return 1; + } else if ((rc == MBEDTLS_ERR_SSL_WANT_READ) || (rc == MBEDTLS_ERR_SSL_WANT_WRITE)) { + /* check for EPIPE */ + if (poll(&pfd, 1, 0) < 0) { + return -1; + } else { + if (pfd.revents & (POLLERR | POLLHUP | POLLNVAL)) { + return -1; + } else { + return 0; + } + } + } else { + return rc; } - - mbedtls_ssl_conf_rng(tls_cfg, mbedtls_ctr_drbg_random, tls_ctx->ctr_drbg); - mbedtls_ssl_conf_authmode(tls_cfg, MBEDTLS_SSL_VERIFY_REQUIRED); - - mbedtls_ssl_conf_own_cert(tls_cfg, srv_cert, srv_pkey); - mbedtls_ssl_conf_ca_chain(tls_cfg, cert_store, crl_store); - - tls_ctx->cert = srv_cert; - tls_ctx->pkey = srv_pkey; - tls_ctx->cert_store = cert_store; - tls_ctx->crl_store = crl_store; - tls_ctx->sock = malloc(sizeof *tls_ctx->sock); - NC_CHECK_ERRMEM_RET(!tls_ctx->sock, 1); - *tls_ctx->sock = sock; - return 0; } void nc_tls_ctx_destroy_wrap(struct nc_tls_ctx *tls_ctx) { - if (tls_ctx->ctr_drbg && tls_ctx->entropy) { - nc_tls_rng_destroy(tls_ctx->ctr_drbg, tls_ctx->entropy); - } + nc_tls_rng_destroy(tls_ctx->ctr_drbg, tls_ctx->entropy); nc_tls_cert_destroy_wrap(tls_ctx->cert); nc_tls_privkey_destroy_wrap(tls_ctx->pkey); nc_tls_cert_store_destroy_wrap(tls_ctx->cert_store); @@ -805,8 +796,8 @@ nc_tls_ctx_destroy_wrap(struct nc_tls_ctx *tls_ctx) free(tls_ctx->sock); } -static mbedtls_pk_context * -nc_tls_file_to_privkey(const char *privkey_path) +void * +nc_tls_import_privkey_file_wrap(const char *privkey_path) { int rc; mbedtls_pk_context *pkey; @@ -817,7 +808,7 @@ nc_tls_file_to_privkey(const char *privkey_path) return NULL; } - pkey = nc_tls_privkey_new_wrap(); + pkey = nc_tls_pkey_new_wrap(); if (!pkey) { nc_tls_rng_destroy(ctr_drbg, entropy); return NULL; @@ -826,60 +817,21 @@ nc_tls_file_to_privkey(const char *privkey_path) rc = mbedtls_pk_parse_keyfile(pkey, privkey_path, NULL, mbedtls_ctr_drbg_random, ctr_drbg); nc_tls_rng_destroy(ctr_drbg, entropy); if (rc) { - ERR(NULL, "Parsing private key data failed (%s).", mbedtls_high_level_strerr(rc)); + ERR(NULL, "Parsing private key from file \"%s\" failed (%s).", privkey_path, nc_get_mbedtls_str_err(rc)); nc_tls_privkey_destroy_wrap(pkey); return NULL; } return pkey; } -static int -read_pem_file(const char *cert_path, char **out) -{ - int ret = 0; - FILE *f; - char *buf = NULL; - size_t size, read; - - f = fopen(cert_path, "r"); - if (!f) { - ERR(NULL, "Unable to open file \"%s\".", cert_path); - ret = 1; - goto cleanup; - } - - fseek(f, 0, SEEK_END); - size = ftell(f); - fseek(f, 0, SEEK_SET); - - buf = malloc(size + 1); - NC_CHECK_ERRMEM_GOTO(!buf, ret = 1, cleanup); - - read = fread(buf, 1, size, f); - if (size != read) { - ERR(NULL, "Error reading from file \"%s\".", cert_path); - ret = 1; - goto cleanup; - } - - buf[size] = '\0'; - *out = buf; - -cleanup: - if (f) { - fclose(f); - } - return ret; -} - int nc_client_tls_load_cert_key_wrap(const char *cert_path, const char *key_path, void **cert, void **pkey) { int ret = 0; mbedtls_x509_crt *c; mbedtls_pk_context *pk; - char *buf = NULL, *ptr; - const char *cert_footer = "-----END CERTIFICATE-----\n"; + + NC_CHECK_ARG_RET(NULL, cert_path, key_path, cert, pkey, 1); c = nc_tls_cert_new_wrap(); if (!c) { @@ -888,41 +840,14 @@ nc_client_tls_load_cert_key_wrap(const char *cert_path, const char *key_path, vo ret = mbedtls_x509_crt_parse_file(c, cert_path); if (ret) { - ERR(NULL, "Parsing certificate from file \"%s\" failed (%s).", cert_path, mbedtls_high_level_strerr(ret)); + ERR(NULL, "Parsing certificate from file \"%s\" failed (%s).", cert_path, nc_get_mbedtls_str_err(ret)); goto cleanup; } - if (key_path) { - pk = nc_tls_file_to_privkey(key_path); - if (!pk) { - ret = 1; - goto cleanup; - } - } else { - ret = read_pem_file(cert_path, &buf); - if (ret) { - goto cleanup; - } - - ptr = strstr(buf, cert_footer); - if (!ptr) { - ERR(NULL, "Invalid certificate file."); - ret = 1; - goto cleanup; - } - - ptr += strlen(cert_footer); - if (*ptr == '\0') { - ERR(NULL, "File \"%s\" doesn't contain a private key."); - ret = 1; - goto cleanup; - } - - pk = nc_tls_pem_to_privkey_wrap(ptr); - if (!pk) { - ret = 1; - goto cleanup; - } + pk = nc_tls_import_privkey_file_wrap(key_path); + if (!pk) { + ret = 1; + goto cleanup; } *cert = c; @@ -938,12 +863,12 @@ nc_client_tls_load_trusted_certs_wrap(void *cert_store, const char *file_path, c int rc; if (file_path && ((rc = mbedtls_x509_crt_parse_file(cert_store, file_path)) < 0)) { - ERR(NULL, "Loading CA certificate from file \"%s\" failed (%s).", file_path, mbedtls_high_level_strerr(rc)); + ERR(NULL, "Loading CA certificate from file \"%s\" failed (%s).", file_path, nc_get_mbedtls_str_err(rc)); return 1; } if (dir_path && ((rc = mbedtls_x509_crt_parse_path(cert_store, dir_path)) < 0)) { - ERR(NULL, "Loading CA certificate from directory \"%s\" failed (%s).", dir_path, mbedtls_low_level_strerr(rc)); + ERR(NULL, "Loading CA certificate from directory \"%s\" failed (%s).", dir_path, nc_get_mbedtls_str_err(rc)); return 1; } @@ -951,16 +876,16 @@ nc_client_tls_load_trusted_certs_wrap(void *cert_store, const char *file_path, c } int -nc_client_tls_load_crl_wrap(void *UNUSED(cert_store), void *crl_store, const char *file_path, const char *dir_path) +nc_client_tls_load_crl_wrap(void *crl_store, const char *file_path, const char *dir_path) { - int rc; - DIR *dir; + int rc, ret = 0; + DIR *dir = NULL; struct dirent *entry; struct stat st = {0}; - char *path; + char *path = NULL; if (file_path && (rc = mbedtls_x509_crl_parse_file(crl_store, file_path))) { - ERR(NULL, "Loading CRL from file \"%s\" failed (%s).", file_path, mbedtls_high_level_strerr(rc)); + ERR(NULL, "Loading CRL from file \"%s\" failed (%s).", file_path, nc_get_mbedtls_str_err(rc)); return 1; } @@ -969,6 +894,7 @@ nc_client_tls_load_crl_wrap(void *UNUSED(cert_store), void *crl_store, const cha dir = opendir(dir_path); if (!dir) { ERR(NULL, "Failed to open directory \"%s\" (%s).", dir_path, strerror(errno)); + return 1; } while ((entry = readdir(dir))) { @@ -977,11 +903,8 @@ nc_client_tls_load_crl_wrap(void *UNUSED(cert_store), void *crl_store, const cha continue; } - if (asprintf(&path, "%s/%s", dir_path, entry->d_name) == -1) { - ERRMEM; - closedir(dir); - return 1; - } + rc = asprintf(&path, "%s/%s", dir_path, entry->d_name); + NC_CHECK_ERRMEM_GOTO(rc == -1, ret = 1; path = NULL, cleanup); if (stat(path, &st) == -1) { if (errno == ENOENT) { @@ -990,9 +913,8 @@ nc_client_tls_load_crl_wrap(void *UNUSED(cert_store), void *crl_store, const cha continue; } else { ERR(NULL, "Failed to get information about \"%s\" (%s).", path, strerror(errno)); - free(path); - closedir(dir); - return 1; + ret = 1; + goto cleanup; } } @@ -1004,14 +926,18 @@ nc_client_tls_load_crl_wrap(void *UNUSED(cert_store), void *crl_store, const cha rc = mbedtls_x509_crl_parse_file(crl_store, path); if (rc) { - ERR(NULL, "Loading CRL from file \"%s\" failed (%s).", path, mbedtls_high_level_strerr(rc)); + WRN(NULL, "Loading CRL from file \"%s\" failed (%s), skipping.", path, nc_get_mbedtls_str_err(rc)); } free(path); + path = NULL; } } - return 0; +cleanup: + free(path); + closedir(dir); + return ret; } int @@ -1021,42 +947,15 @@ nc_client_tls_set_hostname_wrap(void *tls_session, const char *hostname) rc = mbedtls_ssl_set_hostname(tls_session, hostname); if (rc) { - ERR(NULL, "Setting hostname failed (%s).", mbedtls_high_level_strerr(rc)); - return 1; - } - - return 0; -} - -int -nc_tls_setup_config_wrap(void *tls_cfg, int side, struct nc_tls_ctx *tls_ctx) -{ - int rc; - - /* set default config data */ - if (side == NC_SERVER) { - rc = mbedtls_ssl_config_defaults(tls_cfg, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); - } else { - rc = mbedtls_ssl_config_defaults(tls_cfg, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); - } - if (rc) { - ERR(NULL, "Setting default TLS config failed (%s).", mbedtls_high_level_strerr(rc)); + ERR(NULL, "Setting hostname failed (%s).", nc_get_mbedtls_str_err(rc)); return 1; } - /* set config's rng */ - mbedtls_ssl_conf_rng(tls_cfg, mbedtls_ctr_drbg_random, tls_ctx->ctr_drbg); - /* set config's authmode */ - mbedtls_ssl_conf_authmode(tls_cfg, MBEDTLS_SSL_VERIFY_REQUIRED); - /* set config's cert and key */ - mbedtls_ssl_conf_own_cert(tls_cfg, tls_ctx->cert, tls_ctx->pkey); - /* set config's CA and CRL cert store */ - mbedtls_ssl_conf_ca_chain(tls_cfg, tls_ctx->cert_store, tls_ctx->crl_store); return 0; } int -nc_tls_init_ctx_wrap(struct nc_tls_ctx *tls_ctx, int sock, void *cli_cert, void *cli_pkey, void *cert_store, void *crl_store) +nc_tls_init_ctx_wrap(int sock, void *cert, void *pkey, void *cert_store, void *crl_store, struct nc_tls_ctx *tls_ctx) { /* setup rng */ if (nc_tls_rng_new(&tls_ctx->ctr_drbg, &tls_ctx->entropy)) { @@ -1067,26 +966,36 @@ nc_tls_init_ctx_wrap(struct nc_tls_ctx *tls_ctx, int sock, void *cli_cert, void tls_ctx->sock = malloc(sizeof *tls_ctx->sock); NC_CHECK_ERRMEM_RET(!tls_ctx->sock, 1); *tls_ctx->sock = sock; - tls_ctx->cert = cli_cert; - tls_ctx->pkey = cli_pkey; + tls_ctx->cert = cert; + tls_ctx->pkey = pkey; tls_ctx->cert_store = cert_store; tls_ctx->crl_store = crl_store; return 0; } int -nc_client_tls_handshake_step_wrap(void *tls_session) +nc_tls_setup_config_from_ctx_wrap(struct nc_tls_ctx *tls_ctx, int side, void *tls_cfg) { - int rc = 0; + int rc; - rc = mbedtls_ssl_handshake(tls_session); - if (!rc) { - return 1; - } else if ((rc == MBEDTLS_ERR_SSL_WANT_READ) || (rc == MBEDTLS_ERR_SSL_WANT_WRITE)) { - return 0; + /* set default config data */ + if (side == NC_SERVER) { + rc = mbedtls_ssl_config_defaults(tls_cfg, MBEDTLS_SSL_IS_SERVER, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); } else { - return rc; + rc = mbedtls_ssl_config_defaults(tls_cfg, MBEDTLS_SSL_IS_CLIENT, MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT); + } + if (rc) { + ERR(NULL, "Setting default TLS config failed (%s).", nc_get_mbedtls_str_err(rc)); + return 1; } + + /* set config's rng */ + mbedtls_ssl_conf_rng(tls_cfg, mbedtls_ctr_drbg_random, tls_ctx->ctr_drbg); + /* set config's cert and key */ + mbedtls_ssl_conf_own_cert(tls_cfg, tls_ctx->cert, tls_ctx->pkey); + /* set config's CA and CRL cert store */ + mbedtls_ssl_conf_ca_chain(tls_cfg, tls_ctx->cert_store, tls_ctx->crl_store); + return 0; } uint32_t @@ -1095,78 +1004,78 @@ nc_tls_get_verify_result_wrap(void *tls_session) return mbedtls_ssl_get_verify_result(tls_session); } -const char * +char * nc_tls_verify_error_string_wrap(uint32_t err_code) { - const char *err; - - return (err = mbedtls_low_level_strerr(err_code)) ? err : ""; + return nc_tls_get_verify_err_str(err_code); } void -nc_tls_print_error_string_wrap(int connect_ret, const char *peername, void *UNUSED(tls_session)) +nc_client_tls_print_connect_err_wrap(int connect_ret, const char *peername, void *UNUSED(tls_session)) { - ERR(NULL, "TLS connection to \"%s\" failed (%s).", peername, mbedtls_high_level_strerr(connect_ret)); -} + const char *err = nc_get_mbedtls_str_err(connect_ret); -void -nc_server_tls_print_accept_error_wrap(int UNUSED(accept_ret), void *UNUSED(tls_session)) -{ - ERR(NULL, "TLS accept failed."); + if (err) { + ERR(NULL, "TLS connection to \"%s\" failed (%s).", peername, err); + } else { + ERR(NULL, "TLS connection to \"%s\" failed.", peername); + } } void -nc_tls_session_new_cleanup_wrap(void *tls_cfg, void *cli_cert, void *cli_pkey, void *cert_store, void *crl_store) +nc_server_tls_print_accept_err_wrap(int accept_ret, void *UNUSED(tls_session)) { - mbedtls_ssl_config_free(tls_cfg); - free(tls_cfg); - mbedtls_x509_crt_free(cli_cert); - free(cli_cert); - mbedtls_pk_free(cli_pkey); - free(cli_pkey); - mbedtls_x509_crt_free(cert_store); - free(cert_store); - mbedtls_x509_crl_free(crl_store); - free(crl_store); + const char *err = nc_get_mbedtls_str_err(accept_ret); + + if (err) { + ERR(NULL, "TLS accept failed (%s).", err); + } else { + ERR(NULL, "TLS accept failed."); + } } int -nc_der_to_pubkey_wrap(const unsigned char *der, long len) +nc_tls_is_der_subpubkey_wrap(unsigned char *der, long len) { int ret; mbedtls_pk_context *pkey; - pkey = malloc(sizeof *pkey); - NC_CHECK_ERRMEM_RET(!pkey, -1); - mbedtls_pk_init(pkey); + pkey = nc_tls_pkey_new_wrap(); + if (!pkey) { + return -1; + } - ret = mbedtls_pk_parse_public_key(pkey, (const unsigned char *)der, len); + ret = mbedtls_pk_parse_subpubkey(&der, der + len, pkey); nc_tls_privkey_destroy_wrap(pkey); if (!ret) { /* success */ - return 0; + return 1; } else { /* fail */ - return 1; + return 0; } } int -nc_base64_decode_wrap(const char *base64, char **bin) +nc_base64_decode_wrap(const char *base64, unsigned char **bin) { size_t size; - int ret; + int rc; - ret = mbedtls_base64_decode(NULL, 0, &size, (const unsigned char *)base64, strlen(base64)); - if (ret != MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) { + /* get the size of the decoded data */ + rc = mbedtls_base64_decode(NULL, 0, &size, (const unsigned char *)base64, strlen(base64)); + if (rc != MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) { + ERR(NULL, "Base64 decoding failed (%s).", nc_get_mbedtls_str_err(rc)); return -1; } *bin = malloc(size); NC_CHECK_ERRMEM_RET(!*bin, -1); - ret = mbedtls_base64_decode((unsigned char *)*bin, size, &size, (const unsigned char *)base64, strlen(base64)); - if (ret) { + /* decode */ + rc = mbedtls_base64_decode(*bin, size, &size, (const unsigned char *)base64, strlen(base64)); + if (rc) { + ERR(NULL, "Base64 decoding failed (%s).", nc_get_mbedtls_str_err(rc)); free(*bin); *bin = NULL; return -1; @@ -1179,18 +1088,20 @@ int nc_base64_encode_wrap(const unsigned char *bin, size_t len, char **base64) { size_t size; - int ret; + int rc; - ret = mbedtls_base64_encode(NULL, 0, &size, bin, len); - if (ret != MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) { + rc = mbedtls_base64_encode(NULL, 0, &size, bin, len); + if (rc != MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) { + ERR(NULL, "Base64 encoding failed (%s).", nc_get_mbedtls_str_err(rc)); return -1; } *base64 = malloc(size); NC_CHECK_ERRMEM_RET(!*base64, -1); - ret = mbedtls_base64_encode((unsigned char *)*base64, size, &size, bin, len); - if (ret) { + rc = mbedtls_base64_encode((unsigned char *)*base64, size, &size, bin, len); + if (rc) { + ERR(NULL, "Base64 encoding failed (%s).", nc_get_mbedtls_str_err(rc)); free(*base64); *base64 = NULL; return -1; @@ -1219,7 +1130,7 @@ nc_tls_read_wrap(struct nc_session *session, unsigned char *buf, size_t size) rc = -1; break; default: - ERR(session, "TLS communication error occurred (%s).", mbedtls_high_level_strerr(rc)); + ERR(session, "TLS communication error occurred (%s).", nc_get_mbedtls_str_err(rc)); session->status = NC_STATUS_INVALID; session->term_reason = NC_SESSION_TERM_OTHER; rc = -1; @@ -1248,7 +1159,7 @@ nc_tls_write_wrap(struct nc_session *session, const unsigned char *buf, size_t s rc = -1; break; default: - ERR(session, "TLS communication error occurred (%s).", mbedtls_high_level_strerr(rc)); + ERR(session, "TLS communication error occurred (%s).", nc_get_mbedtls_str_err(rc)); rc = -1; break; } @@ -1258,7 +1169,7 @@ nc_tls_write_wrap(struct nc_session *session, const unsigned char *buf, size_t s } int -nc_tls_have_pending_wrap(void *tls_session) +nc_tls_get_num_pending_bytes_wrap(void *tls_session) { return mbedtls_ssl_get_bytes_avail(tls_session); } @@ -1266,11 +1177,7 @@ nc_tls_have_pending_wrap(void *tls_session) int nc_tls_get_fd_wrap(const struct nc_session *session) { - if (!session->ti.tls.ctx.sock) { - return -1; - } else { - return *session->ti.tls.ctx.sock; - } + return session->ti.tls.ctx.sock ? *session->ti.tls.ctx.sock : -1; } void @@ -1281,17 +1188,12 @@ nc_tls_close_notify_wrap(void *tls_session) while ((rc = mbedtls_ssl_close_notify(tls_session))) { if ((rc != MBEDTLS_ERR_SSL_WANT_READ) && (rc != MBEDTLS_ERR_SSL_WANT_WRITE)) { /* some error occurred */ + ERR(NULL, "Sending TLS close notify failed (%s).", nc_get_mbedtls_str_err(rc)); return; } } } -void * -nc_tls_import_key_file_wrap(const char *key_path, FILE *UNUSED(file)) -{ - return nc_tls_file_to_privkey(key_path); -} - void * nc_tls_import_cert_file_wrap(const char *cert_path) { @@ -1305,7 +1207,7 @@ nc_tls_import_cert_file_wrap(const char *cert_path) rc = mbedtls_x509_crt_parse_file(c, cert_path); if (rc) { - ERR(NULL, "Parsing certificate from file \"%s\" failed (%s).", cert_path, mbedtls_high_level_strerr(rc)); + ERR(NULL, "Parsing certificate from file \"%s\" failed (%s).", cert_path, nc_get_mbedtls_str_err(rc)); nc_tls_cert_destroy_wrap(c); return NULL; } @@ -1314,28 +1216,22 @@ nc_tls_import_cert_file_wrap(const char *cert_path) } char * -nc_tls_export_key_wrap(void *pkey) +nc_tls_export_privkey_pem_wrap(void *pkey) { int rc; char *pem; size_t size = 128; - void *tmp; pem = malloc(size); NC_CHECK_ERRMEM_RET(!pem, NULL); while ((rc = mbedtls_pk_write_key_pem(pkey, (unsigned char *)pem, size)) == MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) { size <<= 1; - tmp = realloc(pem, size); - if (!tmp) { - ERRMEM; - free(pem); - return NULL; - } - pem = tmp; + pem = nc_realloc(pem, size); + NC_CHECK_ERRMEM_RET(!pem, NULL); } if (rc < 0) { - ERR(NULL, "Exporting private key to PEM format failed (%s).", mbedtls_high_level_strerr(rc)); + ERR(NULL, "Exporting private key to PEM format failed (%s).", nc_get_mbedtls_str_err(rc)); free(pem); return NULL; } @@ -1344,10 +1240,11 @@ nc_tls_export_key_wrap(void *pkey) } char * -nc_tls_export_cert_wrap(void *cert) +nc_tls_export_cert_pem_wrap(void *cert) { char *b64 = NULL, *pem = NULL; + /* encode the certificate */ if (nc_base64_encode_wrap(((mbedtls_x509_crt *)cert)->raw.p, ((mbedtls_x509_crt *)cert)->raw.len, &b64)) { goto cleanup; } @@ -1364,28 +1261,22 @@ nc_tls_export_cert_wrap(void *cert) } char * -nc_tls_export_pubkey_wrap(void *pkey) +nc_tls_export_pubkey_pem_wrap(void *pkey) { int rc; char *pem; size_t size = 128; - void *tmp; pem = malloc(size); NC_CHECK_ERRMEM_RET(!pem, NULL); while ((rc = mbedtls_pk_write_pubkey_pem(pkey, (unsigned char *)pem, size)) == MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL) { size <<= 1; - tmp = realloc(pem, size); - if (!tmp) { - ERRMEM; - free(pem); - return NULL; - } - pem = tmp; + pem = nc_realloc(pem, size); + NC_CHECK_ERRMEM_RET(!pem, NULL); } if (rc < 0) { - ERR(NULL, "Exporting public key to PEM format failed (%s).", mbedtls_high_level_strerr(rc)); + ERR(NULL, "Exporting public key to PEM format failed (%s).", nc_get_mbedtls_str_err(rc)); free(pem); return NULL; } @@ -1393,26 +1284,6 @@ nc_tls_export_pubkey_wrap(void *pkey) return pem; } -int -nc_tls_export_key_der_wrap(void *pkey, unsigned char **der, size_t *size) -{ - int rc; - - *size = mbedtls_pk_get_len(pkey); - *der = malloc(*size); - NC_CHECK_ERRMEM_RET(!*der, 1); - - rc = mbedtls_pk_write_key_der(pkey, *der, *size); - if (rc < 0) { - ERR(NULL, "Exporting private key to DER format failed (%s).", mbedtls_high_level_strerr(rc)); - free(*der); - *der = NULL; - return 1; - } - - return 0; -} - int nc_tls_privkey_is_rsa_wrap(void *pkey) { @@ -1435,7 +1306,7 @@ nc_tls_get_rsa_pubkey_params_wrap(void *pkey, void **e, void **n) mbedtls_mpi_init(mod); if ((rc = mbedtls_rsa_export(mbedtls_pk_rsa(*(mbedtls_pk_context *)pkey), mod, NULL, NULL, NULL, exp))) { - ERR(NULL, "Failed to export RSA public key parameters (%s).", mbedtls_high_level_strerr(rc)); + ERR(NULL, "Failed to export RSA public key parameters (%s).", nc_get_mbedtls_str_err(rc)); goto fail; } @@ -1451,6 +1322,13 @@ nc_tls_get_rsa_pubkey_params_wrap(void *pkey, void **e, void **n) return 1; } +void +nc_tls_destroy_mpi_wrap(void *mpi) +{ + mbedtls_mpi_free(mpi); + free(mpi); +} + int nc_tls_privkey_is_ec_wrap(void *pkey) { @@ -1464,85 +1342,123 @@ nc_tls_get_ec_group_wrap(void *pkey) mbedtls_ecp_group_id group_id; mbedtls_ecp_keypair *ec; + /* get the group ID from the EC key */ ec = mbedtls_pk_ec(*(mbedtls_pk_context *)pkey); group_id = ec->private_grp.id; + + /* get the group name based on the id */ curve_info = mbedtls_ecp_curve_info_from_grp_id(group_id); return strdup(curve_info->name); } int -nc_tls_get_ec_pubkey_param_wrap(void *pkey, unsigned char **bin, int *bin_len) +nc_tls_get_ec_pubkey_params_wrap(void *pkey, void **q, void **q_grp) { - int rc = 0; - unsigned char *bin_tmp; - size_t bin_len_tmp = 32, out_len; - mbedtls_ecp_keypair *ec; - void *tmp; + int ret; + mbedtls_ecp_group *grp = NULL; + mbedtls_ecp_point *p = NULL; + mbedtls_mpi *d = NULL; + + /* init group, mpi and point */ + grp = malloc(sizeof *grp); + d = malloc(sizeof *d); + p = malloc(sizeof *p); + if (!grp || !p || !d) { + ERRMEM; + ret = 1; + goto cleanup; + } + mbedtls_ecp_group_init(grp); + mbedtls_mpi_init(d); + mbedtls_ecp_point_init(p); - bin_tmp = malloc(bin_len_tmp); - NC_CHECK_ERRMEM_RET(!bin_tmp, 1); + /* get the group and public key */ + ret = mbedtls_ecp_export(mbedtls_pk_ec(*(mbedtls_pk_context *)pkey), grp, d, p); + if (ret) { + ERR(NULL, "Failed to export EC public key parameters (%s).", nc_get_mbedtls_str_err(ret)); + ret = 1; + goto cleanup; + } - ec = mbedtls_pk_ec(*(mbedtls_pk_context *)pkey); - while ((rc = mbedtls_ecp_point_write_binary(&ec->private_grp, &ec->private_Q, MBEDTLS_ECP_PF_COMPRESSED, &out_len, bin_tmp, bin_len_tmp)) == MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL) { - bin_len_tmp <<= 1; - tmp = realloc(bin_tmp, bin_len_tmp); - if (!tmp) { - ERRMEM; - free(bin_tmp); - return 1; + *q_grp = grp; + grp = NULL; + *q = p; + p = NULL; + +cleanup: + mbedtls_ecp_group_free(grp); + free(grp); + mbedtls_mpi_free(d); + free(d); + mbedtls_ecp_point_free(p); + free(p); + return ret; +} + +int +nc_tls_ec_point_to_bin_wrap(void *q, void *q_grp, unsigned char **bin, int *bin_len) +{ + int rc; + unsigned char *buf; + size_t buf_len = 32, out_len; + + buf = malloc(buf_len); + NC_CHECK_ERRMEM_RET(!buf, 1); + + while ((rc = (mbedtls_ecp_point_write_binary(q_grp, q, MBEDTLS_ECP_PF_COMPRESSED, &out_len, buf, buf_len)))) { + if (rc != MBEDTLS_ERR_ECP_BUFFER_TOO_SMALL) { + break; } - bin_tmp = tmp; + buf_len <<= 1; + buf = nc_realloc(buf, buf_len); + NC_CHECK_ERRMEM_RET(!buf, 1); } if (rc) { - ERR(NULL, "Failed to write public key binary (%s).", mbedtls_high_level_strerr(rc)); - free(bin_tmp); + ERR(NULL, "Failed to write EC public key binary (%s).", nc_get_mbedtls_str_err(rc)); + free(buf); return 1; } - *bin = bin_tmp; + *bin = buf; *bin_len = out_len; return 0; } -int -nc_tls_get_bn_num_bytes_wrap(void *bn) +void +nc_tls_ec_point_destroy_wrap(void *p) { - return mbedtls_mpi_size(bn); + mbedtls_ecp_point_free(p); + free(p); } void -nc_tls_bn_bn2bin_wrap(void *bn, unsigned char *bin) +nc_tls_ec_group_destroy_wrap(void *grp) { - mbedtls_mpi_write_binary(bn, bin, mbedtls_mpi_size(bn)); + mbedtls_ecp_group_free(grp); + free(grp); } int -nc_tls_get_pubkey_file_wrap(const char *pubkey_path, char **pubout) +nc_tls_mpi2bin_wrap(void *mpi, unsigned char **bin, int *bin_len) { - int rc = 0, ret = 0; - mbedtls_pk_context *pk = NULL; + int rc; + unsigned char *buf; + int buf_len; - pk = nc_tls_privkey_new_wrap(); - if (!pk) { - return 1; - } + buf_len = mbedtls_mpi_size(mpi); + buf = malloc(buf_len); + NC_CHECK_ERRMEM_RET(!buf, 1); - rc = mbedtls_pk_parse_public_keyfile(pk, pubkey_path); + rc = mbedtls_mpi_write_binary(mpi, buf, buf_len); if (rc) { - ERR(NULL, "Parsing public key from file \"%s\" failed (%s).", pubkey_path, mbedtls_high_level_strerr(rc)); - ret = 1; - goto cleanup; - } - - *pubout = nc_tls_export_key_wrap(pk); - if (!*pubout) { - ret = 1; - goto cleanup; + ERR(NULL, "Failed to convert MPI to binary (%s).", nc_get_mbedtls_str_err(rc)); + free(buf); + return 1; } -cleanup: - nc_tls_privkey_destroy_wrap(pk); - return ret; + *bin = buf; + *bin_len = buf_len; + return 0; } void * @@ -1551,14 +1467,14 @@ nc_tls_import_pubkey_file_wrap(const char *pubkey_path) int rc = 0; mbedtls_pk_context *pk = NULL; - pk = nc_tls_privkey_new_wrap(); + pk = nc_tls_pkey_new_wrap(); if (!pk) { return NULL; } rc = mbedtls_pk_parse_public_keyfile(pk, pubkey_path); if (rc) { - ERR(NULL, "Parsing public key from file \"%s\" failed (%s).", pubkey_path, mbedtls_high_level_strerr(rc)); + ERR(NULL, "Parsing public key from file \"%s\" failed (%s).", pubkey_path, nc_get_mbedtls_str_err(rc)); nc_tls_privkey_destroy_wrap(pk); return NULL; } @@ -1603,7 +1519,7 @@ nc_server_tls_get_crl_distpoint_uris_wrap(void *cert_store, char ***uris, int *u */ ret = mbedtls_asn1_get_tag(&p, end_v3_ext, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); if (ret) { - ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret)); + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret)); goto cleanup; } @@ -1616,7 +1532,7 @@ nc_server_tls_get_crl_distpoint_uris_wrap(void *cert_store, char ***uris, int *u */ ret = mbedtls_asn1_get_tag(&p, end_v3_ext, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); if (ret) { - ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret)); + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret)); goto cleanup; } @@ -1625,7 +1541,7 @@ nc_server_tls_get_crl_distpoint_uris_wrap(void *cert_store, char ***uris, int *u /* parse extnID */ ret = mbedtls_asn1_get_tag(&p, end_ext, &ext_oid.len, MBEDTLS_ASN1_OID); if (ret) { - ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret)); + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret)); goto cleanup; } ext_oid.tag = MBEDTLS_ASN1_OID; @@ -1642,14 +1558,14 @@ nc_server_tls_get_crl_distpoint_uris_wrap(void *cert_store, char ***uris, int *u /* parse optional critical */ ret = mbedtls_asn1_get_bool(&p, end_ext, &is_critical); if (ret && (ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG)) { - ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret)); + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret)); goto cleanup; } /* parse extnValue */ ret = mbedtls_asn1_get_tag(&p, end_ext, &len, MBEDTLS_ASN1_OCTET_STRING); if (ret) { - ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret)); + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret)); goto cleanup; } @@ -1662,12 +1578,12 @@ nc_server_tls_get_crl_distpoint_uris_wrap(void *cert_store, char ***uris, int *u */ ret = mbedtls_asn1_get_tag(&p, end_ext_octet, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); if (ret) { - ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret)); + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret)); goto cleanup; } if (p + len != end_ext_octet) { /* length mismatch */ - ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret)); + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret)); goto cleanup; } else if (!len) { /* empty sequence, but size is 1..max */ @@ -1686,7 +1602,7 @@ nc_server_tls_get_crl_distpoint_uris_wrap(void *cert_store, char ***uris, int *u */ ret = mbedtls_asn1_get_tag(&p, end_ext_octet, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); if (ret) { - ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret)); + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret)); goto cleanup; } if (!len) { @@ -1706,10 +1622,10 @@ nc_server_tls_get_crl_distpoint_uris_wrap(void *cert_store, char ***uris, int *u if (ret) { if ((ret == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) && (*p == (MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 1))) { /* it's nameRelativeToCRLIssuer, but we don't support it */ - ERR(NULL, "Failed to parse CRL distribution points extension (nameRelativeToCRLIssuer not supported)."); + ERR(NULL, "Failed to parse CRL distribution points extension (nameRelativeToCRLIssuer not yet supported)."); goto cleanup; } else { - ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret)); + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret)); goto cleanup; } } @@ -1717,7 +1633,7 @@ nc_server_tls_get_crl_distpoint_uris_wrap(void *cert_store, char ***uris, int *u /* parse GeneralNames, but thankfully there is an api for this */ ret = mbedtls_x509_get_subject_alt_name_ext(&p, p + len, &general_names); if (ret) { - ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret)); + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret)); goto cleanup; } @@ -1726,26 +1642,38 @@ nc_server_tls_get_crl_distpoint_uris_wrap(void *cert_store, char ***uris, int *u while (iter) { ret = mbedtls_x509_parse_subject_alt_name(&iter->buf, &san); if (ret && (ret != MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE)) { - ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret)); + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret)); goto cleanup; } if (san.type == MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER) { /* found an URI */ tmp = realloc(*uris, (*uri_count + 1) * sizeof **uris); - NC_CHECK_ERRMEM_GOTO(!tmp, ret = 1, cleanup); + if (!tmp) { + ERRMEM; + ret = 1; + mbedtls_x509_free_subject_alt_name(&san); + goto cleanup; + } *uris = tmp; *uris[*uri_count] = strndup((const char *)san.san.unstructured_name.p, san.san.unstructured_name.len); - NC_CHECK_ERRMEM_GOTO(!*uris[*uri_count], ret = 1, cleanup); + if (!*uris[*uri_count]) { + ERRMEM; + ret = 1; + mbedtls_x509_free_subject_alt_name(&san); + goto cleanup; + } ++(*uri_count); } + + mbedtls_x509_free_subject_alt_name(&san); iter = iter->next; } } else if (ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) { /* failed to parse it, but not because it's optional */ - ERR(NULL, "Failed to parse CRL distribution points extension (%s).", mbedtls_low_level_strerr(ret)); + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret)); goto cleanup; } } @@ -1756,3 +1684,68 @@ nc_server_tls_get_crl_distpoint_uris_wrap(void *cert_store, char ***uris, int *u cleanup: return ret; } + +int +nc_tls_process_cipher_suite_wrap(const char *cipher, char **out) +{ + const char *begin, *ptr; + + /* check if it's a TLS 1.3 cipher suite */ + if (!strcmp(cipher, "tls-aes-256-gcm-sha384") || !strcmp(cipher, "tls-aes-128-gcm-sha256") || + !strcmp(cipher, "tls-chacha20-poly1305-sha256") || !strcmp(cipher, "tls-aes-128-ccm-sha256") || + !strcmp(cipher, "tls-aes-128-ccm-8-sha256")) { + /* + 3 because mbedtls has "TLS1-3" prefix for 1.3 suites */ + *out = malloc(strlen(cipher) + 3 + 1); + NC_CHECK_ERRMEM_RET(!*out, 1); + sprintf(*out, "TLS1-3"); + begin = cipher + 4; + } else { + *out = malloc(strlen(cipher) + 1); + NC_CHECK_ERRMEM_RET(!*out, 1); + begin = cipher; + } + + /* convert to uppercase */ + for (ptr = begin; *ptr; ptr++) { + (*out)[ptr - begin] = toupper(*ptr); + } + + (*out)[ptr - begin] = '\0'; + return 0; +} + +int +nc_tls_append_cipher_suite_wrap(struct nc_server_tls_opts *opts, const char *cipher_suite) +{ + int cipher_id; + + cipher_id = mbedtls_ssl_get_ciphersuite_id(cipher_suite); + if (!cipher_id) { + return 1; + } + + /* append the cipher suite to a zero terminated array */ + if (!opts->ciphers) { + /* first entry, account for terminating 0 */ + opts->ciphers = malloc(2 * sizeof *opts->ciphers); + NC_CHECK_ERRMEM_RET(!opts->ciphers, 1); + ((int *)opts->ciphers)[0] = cipher_id; + opts->cipher_count = 1; + } else { + /* +2 because of terminating 0 */ + opts->ciphers = nc_realloc(opts->ciphers, (opts->cipher_count + 2) * sizeof *opts->ciphers); + NC_CHECK_ERRMEM_RET(!opts->ciphers, 1); + ((int *)opts->ciphers)[opts->cipher_count] = cipher_id; + opts->cipher_count++; + } + + /* terminate the array */ + ((int *)opts->ciphers)[opts->cipher_count] = 0; + return 0; +} + +void +nc_server_tls_set_cipher_suites_wrap(void *tls_cfg, void *cipher_suites) +{ + mbedtls_ssl_conf_ciphersuites(tls_cfg, cipher_suites); +} From 7c6a18529895bc5a1b2aa7004ca679d215b314b1 Mon Sep 17 00:00:00 2001 From: roman Date: Tue, 23 Apr 2024 15:08:45 +0200 Subject: [PATCH 23/54] session openssl UPDATE reworked everything --- src/session_openssl.c | 701 +++++++++++++++++++++--------------------- 1 file changed, 343 insertions(+), 358 deletions(-) diff --git a/src/session_openssl.c b/src/session_openssl.c index ec3ff492..d863c16b 100644 --- a/src/session_openssl.c +++ b/src/session_openssl.c @@ -1,5 +1,6 @@ #define _GNU_SOURCE +#include #include #include #include @@ -30,7 +31,7 @@ nc_tls_session_new_wrap(void *tls_cfg) session = SSL_new(tls_cfg); if (!session) { - ERR(NULL, "Setting up TLS context failed (%s).", ERR_reason_error_string(ERR_get_error())); + ERR(NULL, "Setting up TLS session failed (%s).", ERR_reason_error_string(ERR_get_error())); return NULL; } @@ -44,22 +45,20 @@ nc_tls_session_destroy_wrap(void *tls_session) } void * -nc_server_tls_config_new_wrap() +nc_tls_config_new_wrap(int side) { SSL_CTX *tls_cfg; - tls_cfg = SSL_CTX_new(TLS_server_method()); - NC_CHECK_ERRMEM_RET(!tls_cfg, NULL) - - return tls_cfg; -} - -void * -nc_client_tls_config_new_wrap() -{ - SSL_CTX *tls_cfg; + if ((side != NC_SERVER) && (side != NC_CLIENT)) { + ERRINT; + return NULL; + } - tls_cfg = SSL_CTX_new(TLS_client_method()); + if (side == NC_SERVER) { + tls_cfg = SSL_CTX_new(TLS_server_method()); + } else { + tls_cfg = SSL_CTX_new(TLS_client_method()); + } NC_CHECK_ERRMEM_RET(!tls_cfg, NULL) return tls_cfg; @@ -88,17 +87,6 @@ nc_tls_cert_destroy_wrap(void *cert) X509_free(cert); } -void * -nc_tls_privkey_new_wrap() -{ - EVP_PKEY *pkey; - - pkey = EVP_PKEY_new(); - NC_CHECK_ERRMEM_RET(!pkey, NULL); - - return pkey; -} - void nc_tls_privkey_destroy_wrap(void *pkey) { @@ -125,26 +113,13 @@ nc_tls_cert_store_destroy_wrap(void *cert_store) void * nc_tls_crl_store_new_wrap() { - return NULL; + return nc_tls_cert_store_new_wrap(); } void -nc_tls_crl_store_destroy_wrap(void *crl) +nc_tls_crl_store_destroy_wrap(void *crl_store) { - (void) crl; - return; -} - -void -nc_tls_set_authmode_wrap(void *tls_cfg) -{ - SSL_CTX_set_mode(tls_cfg, SSL_MODE_AUTO_RETRY); -} - -int -nc_server_tls_set_config_defaults_wrap(void *tls_cfg) -{ - return 0; + X509_STORE_free(crl_store); } void * @@ -168,22 +143,18 @@ nc_tls_pem_to_cert_wrap(const char *cert_data) } int -nc_tls_pem_to_cert_add_to_store_wrap(const char *cert_data, void *cert_store) +nc_tls_add_cert_to_store_wrap(void *cert, void *cert_store) { int rc; - X509 *cert; - - cert = nc_tls_pem_to_cert_wrap(cert_data); - if (!cert) { - return 1; - } + /* on success increases ref count to cert, so free it */ rc = X509_STORE_add_cert(cert_store, cert); - X509_free(cert); if (!rc) { ERR(NULL, "Adding certificate to store failed (%s).", ERR_reason_error_string(ERR_get_error())); return 1; } + + X509_free(cert); return 0; } @@ -208,37 +179,15 @@ nc_tls_pem_to_privkey_wrap(const char *privkey_data) } int -nc_tls_load_cert_private_key_wrap(void *tls_cfg, void *cert, void *pkey) -{ - int rc; - - rc = SSL_CTX_use_certificate(tls_cfg, cert); - if (rc) { - ERR(NULL, "Loading the server certificate failed (%s).", ERR_reason_error_string(ERR_get_error())); - return 1; - } - - rc = SSL_CTX_use_PrivateKey(tls_cfg, pkey); - if (rc) { - ERR(NULL, "Loading the server private key failed (%s).", ERR_reason_error_string(ERR_get_error())); - return 1; - } - - return 0; -} - -int -nc_server_tls_crl_path(const char *crl_path, void *cert_store, void *crl_store) +nc_tls_import_crl_path_wrap(const char *path, void *crl_store) { - int ret = 0; + int ret = 0, rc; X509_CRL *crl = NULL; FILE *f; - (void) crl_store; - - f = fopen(crl_path, "r"); + f = fopen(path, "r"); if (!f) { - ERR(NULL, "Unable to open CRL file \"%s\".", crl_path); + ERR(NULL, "Unable to open CRL file \"%s\".", path); return 1; } @@ -253,20 +202,18 @@ nc_server_tls_crl_path(const char *crl_path, void *cert_store, void *crl_store) rewind(f); crl = d2i_X509_CRL_fp(f, NULL); if (!crl) { - ERR(NULL, "Reading CRL from file \"%s\" failed.", crl_path); + ERR(NULL, "Reading CRL from file \"%s\" failed.", path); ret = 1; goto cleanup; } ok: - ret = X509_STORE_add_crl(cert_store, crl); - if (!ret) { + rc = X509_STORE_add_crl(crl_store, crl); + if (!rc) { ERR(NULL, "Error adding CRL to store (%s).", ERR_reason_error_string(ERR_get_error())); ret = 1; goto cleanup; } - /* ok */ - ret = 0; cleanup: fclose(f); @@ -275,14 +222,12 @@ nc_server_tls_crl_path(const char *crl_path, void *cert_store, void *crl_store) } int -nc_server_tls_add_crl_to_store_wrap(const unsigned char *crl_data, size_t size, void *cert_store, void *crl_store) +nc_server_tls_add_crl_to_store_wrap(const unsigned char *crl_data, size_t size, void *crl_store) { int ret = 0; X509_CRL *crl = NULL; BIO *bio = NULL; - (void) crl_store; - bio = BIO_new_mem_buf(crl_data, size); if (!bio) { ERR(NULL, "Creating new bio failed (%s).", ERR_reason_error_string(ERR_get_error())); @@ -307,7 +252,7 @@ nc_server_tls_add_crl_to_store_wrap(const unsigned char *crl_data, size_t size, ok: /* we obtained the CRL, now add it to the CRL store */ - ret = X509_STORE_add_crl(cert_store, crl); + ret = X509_STORE_add_crl(crl_store, crl); if (!ret) { ERR(NULL, "Error adding CRL to store (%s).", ERR_reason_error_string(ERR_get_error())); ret = 1; @@ -322,15 +267,6 @@ nc_server_tls_add_crl_to_store_wrap(const unsigned char *crl_data, size_t size, return ret; } -void -nc_server_tls_set_certs_wrap(void *tls_cfg, void *cert_store, void *crl_store) -{ - (void) crl_store; - - X509_STORE_set_flags(cert_store, X509_V_FLAG_CRL_CHECK); - SSL_CTX_set_cert_store(tls_cfg, cert_store); -} - int nc_server_tls_set_tls_versions_wrap(void *tls_cfg, unsigned int tls_versions) { @@ -375,11 +311,13 @@ nc_server_tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) int ret = 0, depth, err; struct nc_tls_verify_cb_data *data; SSL *ssl; + SSL_CTX *ctx; X509 *cert; /* retrieve callback data stored in the SSL struct */ ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); - data = SSL_get_ex_data(ssl, 0); + ctx = SSL_get_SSL_CTX(ssl); + data = SSL_CTX_get_ex_data(ctx, 0); /* get current cert and its depth */ cert = X509_STORE_CTX_get_current_cert(x509_ctx); @@ -401,6 +339,18 @@ nc_server_tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) } else if ((err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT) || (err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY)) { /* full chain of trust is invalid, but it may be valid partially, case 2) */ ret = nc_server_tls_verify_cert(cert, depth, 0, data); + } else if (err == X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN) { + /* self-signed certificate in the chain, check if peer cert complies with 1) in order to continue, + * if yes, this callback will be called again with the same cert, but with preverify_ok = 1 + */ + cert = X509_STORE_CTX_get0_cert(x509_ctx); + ret = nc_server_tls_verify_peer_cert(cert, data->opts); + if (ret) { + VRB(NULL, "Cert verify: fail (%s).", X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509_ctx))); + ret = -1; + } else { + X509_STORE_CTX_set_error(x509_ctx, X509_V_OK); + } } else { VRB(NULL, "Cert verify: fail (%s).", X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509_ctx))); ret = 1; @@ -425,10 +375,17 @@ nc_server_tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) } void -nc_server_tls_set_verify_cb_wrap(void *tls_session, struct nc_tls_verify_cb_data *cb_data) +nc_server_tls_set_verify_wrap(void *tls_cfg, struct nc_tls_verify_cb_data *cb_data) +{ + /* set verify cb and its data */ + SSL_CTX_set_ex_data(tls_cfg, 0, cb_data); + SSL_CTX_set_verify(tls_cfg, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nc_server_tls_verify_cb); +} + +void +nc_client_tls_set_verify_wrap(void *tls_cfg) { - SSL_set_ex_data(tls_session, 0, cb_data); - SSL_set_verify(tls_session, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, nc_server_tls_verify_cb); + SSL_CTX_set_verify(tls_cfg, SSL_VERIFY_PEER, NULL); } char * @@ -443,112 +400,83 @@ nc_server_tls_get_issuer_wrap(void *cert) return X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0); } +void * +nc_tls_get_sans_wrap(void *cert) +{ + return X509_get_ext_d2i(cert, NID_subject_alt_name, NULL, NULL); +} + +void +nc_tls_sans_destroy_wrap(void *sans) +{ + sk_GENERAL_NAME_pop_free(sans, GENERAL_NAME_free); +} + int -nc_server_tls_get_username_from_cert_wrap(void *cert, NC_TLS_CTN_MAPTYPE map_type, char **username) +nc_tls_get_num_sans_wrap(void *sans) { - STACK_OF(GENERAL_NAME) * san_names; - GENERAL_NAME *san_name; - ASN1_OCTET_STRING *ip; - int i, san_count; - char *subject, *common_name; - X509 *peer_cert = cert; - - *username = NULL; - - if (map_type == NC_TLS_CTN_COMMON_NAME) { - subject = nc_server_tls_get_subject_wrap(peer_cert); - NC_CHECK_ERRMEM_RET(!subject, -1); - common_name = strstr(subject, "CN="); - if (!common_name) { - WRN(NULL, "Certificate does not include the commonName field."); - free(subject); - return 1; - } - common_name += 3; - if (strchr(common_name, '/')) { - *strchr(common_name, '/') = '\0'; - } - *username = strdup(common_name); - free(subject); - NC_CHECK_ERRMEM_RET(!*username, -1); - } else { - /* retrieve subjectAltName's rfc822Name (email), dNSName and iPAddress values */ - san_names = X509_get_ext_d2i(peer_cert, NID_subject_alt_name, NULL, NULL); - if (!san_names) { - WRN(NULL, "Certificate has no SANs or failed to retrieve them."); - return 1; - } + return sk_GENERAL_NAME_num(sans); +} - san_count = sk_GENERAL_NAME_num(san_names); - for (i = 0; i < san_count; ++i) { - san_name = sk_GENERAL_NAME_value(san_names, i); +int +nc_tls_get_san_value_type_wrap(void *sans, int idx, char **san_value, NC_TLS_CTN_MAPTYPE *san_type) +{ + int ret = 0; + GENERAL_NAME *san; + ASN1_OCTET_STRING *ip; - /* rfc822Name (email) */ - if (((map_type == NC_TLS_CTN_SAN_ANY) || (map_type == NC_TLS_CTN_SAN_RFC822_NAME)) && - (san_name->type == GEN_EMAIL)) { - *username = strdup((char *)ASN1_STRING_get0_data(san_name->d.rfc822Name)); - NC_CHECK_ERRMEM_RET(!*username, -1); - break; - } + *san_value = NULL; + *san_type = NC_TLS_CTN_UNKNOWN; - /* dNSName */ - if (((map_type == NC_TLS_CTN_SAN_ANY) || (map_type == NC_TLS_CTN_SAN_DNS_NAME)) && - (san_name->type == GEN_DNS)) { - *username = strdup((char *)ASN1_STRING_get0_data(san_name->d.dNSName)); - NC_CHECK_ERRMEM_RET(!*username, -1); - break; - } + /* find the san */ + san = sk_GENERAL_NAME_value(sans, idx); + if (!san) { + return -1; + } - /* iPAddress */ - if (((map_type == NC_TLS_CTN_SAN_ANY) || (map_type == NC_TLS_CTN_SAN_IP_ADDRESS)) && - (san_name->type == GEN_IPADD)) { - ip = san_name->d.iPAddress; - if (ip->length == 4) { - if (asprintf(username, "%d.%d.%d.%d", ip->data[0], ip->data[1], ip->data[2], ip->data[3]) == -1) { - ERRMEM; - sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free); - return -1; - } - break; - } else if (ip->length == 16) { - if (asprintf(username, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", - ip->data[0], ip->data[1], ip->data[2], ip->data[3], ip->data[4], ip->data[5], - ip->data[6], ip->data[7], ip->data[8], ip->data[9], ip->data[10], ip->data[11], - ip->data[12], ip->data[13], ip->data[14], ip->data[15]) == -1) { - ERRMEM; - sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free); - return -1; - } - break; - } else { - WRN(NULL, "SAN IP address in an unknown format (length is %d).", ip->length); - } + /* get its type and value */ + switch (san->type) { + case GEN_EMAIL: + *san_type = NC_TLS_CTN_SAN_RFC822_NAME; + *san_value = strdup((char *)ASN1_STRING_get0_data(san->d.rfc822Name)); + NC_CHECK_ERRMEM_RET(!*san_value, -1); + break; + case GEN_DNS: + *san_type = NC_TLS_CTN_SAN_DNS_NAME; + *san_value = strdup((char *)ASN1_STRING_get0_data(san->d.dNSName)); + NC_CHECK_ERRMEM_RET(!*san_value, -1); + break; + case GEN_IPADD: + *san_type = NC_TLS_CTN_SAN_IP_ADDRESS; + ip = san->d.iPAddress; + if (ip->length == 4) { + if (asprintf(san_value, "%d.%d.%d.%d", ip->data[0], ip->data[1], ip->data[2], ip->data[3]) == -1) { + ERRMEM; + *san_value = NULL; + ret = -1; } - } - sk_GENERAL_NAME_pop_free(san_names, GENERAL_NAME_free); // TODO - - if (i == san_count) { - switch (map_type) { - case NC_TLS_CTN_SAN_RFC822_NAME: - WRN(NULL, "Certificate does not include the SAN rfc822Name field."); - break; - case NC_TLS_CTN_SAN_DNS_NAME: - WRN(NULL, "Certificate does not include the SAN dNSName field."); - break; - case NC_TLS_CTN_SAN_IP_ADDRESS: - WRN(NULL, "Certificate does not include the SAN iPAddress field."); - break; - case NC_TLS_CTN_SAN_ANY: - WRN(NULL, "Certificate does not include any relevant SAN fields."); - break; - default: - break; + } else if (ip->length == 16) { + if (asprintf(san_value, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", + ip->data[0], ip->data[1], ip->data[2], ip->data[3], ip->data[4], ip->data[5], + ip->data[6], ip->data[7], ip->data[8], ip->data[9], ip->data[10], ip->data[11], + ip->data[12], ip->data[13], ip->data[14], ip->data[15]) == -1) { + ERRMEM; + *san_value = NULL; + ret = -1; } - return 1; + } else { + WRN(NULL, "SAN IP address in an unknown format (length is %d).", ip->length); + ret = 1; } + break; + default: + /* we dont care about other types */ + *san_type = NC_TLS_CTN_UNKNOWN; + ret = 1; + break; } - return 0; + return ret; } int @@ -563,7 +491,7 @@ nc_server_tls_md5_wrap(void *cert, unsigned char *buf) int rc; rc = X509_digest(cert, EVP_md5(), buf, NULL); - if (rc) { + if (!rc) { ERR(NULL, "Calculating MD-5 digest failed (%s).", ERR_reason_error_string(ERR_get_error())); return 1; } @@ -577,7 +505,7 @@ nc_server_tls_sha1_wrap(void *cert, unsigned char *buf) int rc; rc = X509_digest(cert, EVP_sha1(), buf, NULL); - if (rc) { + if (!rc) { ERR(NULL, "Calculating SHA-1 digest failed (%s).", ERR_reason_error_string(ERR_get_error())); return 1; } @@ -591,7 +519,7 @@ nc_server_tls_sha224_wrap(void *cert, unsigned char *buf) int rc; rc = X509_digest(cert, EVP_sha224(), buf, NULL); - if (rc) { + if (!rc) { ERR(NULL, "Calculating SHA-224 digest failed (%s).", ERR_reason_error_string(ERR_get_error())); return 1; } @@ -605,7 +533,7 @@ nc_server_tls_sha256_wrap(void *cert, unsigned char *buf) int rc; rc = X509_digest(cert, EVP_sha256(), buf, NULL); - if (rc) { + if (!rc) { ERR(NULL, "Calculating SHA-256 digest failed (%s).", ERR_reason_error_string(ERR_get_error())); return 1; } @@ -619,7 +547,7 @@ nc_server_tls_sha384_wrap(void *cert, unsigned char *buf) int rc; rc = X509_digest(cert, EVP_sha384(), buf, NULL); - if (rc) { + if (!rc) { ERR(NULL, "Calculating SHA-384 digest failed (%s).", ERR_reason_error_string(ERR_get_error())); return 1; } @@ -633,7 +561,7 @@ nc_server_tls_sha512_wrap(void *cert, unsigned char *buf) int rc; rc = X509_digest(cert, EVP_sha512(), buf, NULL); - if (rc) { + if (!rc) { ERR(NULL, "Calculating SHA-512 digest failed (%s).", ERR_reason_error_string(ERR_get_error())); return 1; } @@ -665,7 +593,7 @@ nc_server_tls_handshake_step_wrap(void *tls_session) } int -nc_client_tls_handshake_step_wrap(void *tls_session) +nc_client_tls_handshake_step_wrap(void *tls_session, int UNUSED(sock)) { int ret = 0; @@ -694,6 +622,8 @@ nc_client_tls_load_cert_key_wrap(const char *cert_path, const char *key_path, vo X509 *cert_tmp; EVP_PKEY *pkey_tmp; + NC_CHECK_ARG_RET(NULL, cert_path, key_path, cert, pkey, 1); + bio = BIO_new_file(cert_path, "r"); if (!bio) { ERR(NULL, "Opening the client certificate file \"%s\" failed.", cert_path); @@ -740,9 +670,9 @@ nc_client_tls_load_trusted_certs_wrap(void *cert_store, const char *file_path, c } int -nc_client_tls_load_crl_wrap(void *cert_store, void *UNUSED(crl_store), const char *file_path, const char *dir_path) +nc_client_tls_load_crl_wrap(void *crl_store, const char *file_path, const char *dir_path) { - if (!X509_STORE_load_locations(cert_store, file_path, dir_path)) { + if (!X509_STORE_load_locations(crl_store, file_path, dir_path)) { ERR(NULL, "Loading CRLs from file \"%s\" or directory \"%s\" failed (%s).", file_path, dir_path, ERR_reason_error_string(ERR_get_error())); return 1; } @@ -764,7 +694,7 @@ nc_client_tls_set_hostname_wrap(void *tls_session, const char *hostname) ret = 1; goto cleanup; } - if (!SSL_CTX_set1_param(tls_session, vpm)) { + if (!SSL_set1_param(tls_session, vpm)) { ERR(NULL, "Failed to set verify param (%s).", ERR_reason_error_string(ERR_get_error())); ret = 1; goto cleanup; @@ -775,20 +705,92 @@ nc_client_tls_set_hostname_wrap(void *tls_session, const char *hostname) return ret; } +int +nc_tls_init_ctx_wrap(int UNUSED(sock), void *cert, void *pkey, void *cert_store, void *crl_store, struct nc_tls_ctx *tls_ctx) +{ + tls_ctx->cert = cert; + tls_ctx->pkey = pkey; + tls_ctx->cert_store = cert_store; + tls_ctx->crl_store = crl_store; + return 0; +} + +static int +nc_tls_move_crls_to_store(const X509_STORE *src, X509_STORE *dst) +{ + int i, nobjs = 0; + + STACK_OF(X509_OBJECT) * objs; + X509_OBJECT *obj; + X509_CRL *crl; + + objs = X509_STORE_get0_objects(src); + nobjs = sk_X509_OBJECT_num(objs); + for (i = 0; i < nobjs; i++) { + obj = sk_X509_OBJECT_value(objs, i); + crl = X509_OBJECT_get0_X509_CRL(obj); + if (!crl) { + /* not a CRL */ + continue; + } + if (!X509_STORE_add_crl(dst, crl)) { + ERR(NULL, "Adding CRL to the store failed (%s).", ERR_reason_error_string(ERR_get_error())); + return 1; + } + } + + return 0; +} + +int +nc_tls_setup_config_from_ctx_wrap(struct nc_tls_ctx *tls_ctx, int UNUSED(side), void *tls_cfg) +{ + if (SSL_CTX_use_certificate(tls_cfg, tls_ctx->cert) != 1) { + return 1; + } + + if (SSL_CTX_use_PrivateKey(tls_cfg, tls_ctx->pkey) != 1) { + return 1; + } + + SSL_CTX_set_mode(tls_cfg, SSL_MODE_AUTO_RETRY); + + if (tls_ctx->crl_store) { + /* move CRLs from crl_store to cert_store, because SSL_CTX can only have one store */ + if (nc_tls_move_crls_to_store(tls_ctx->crl_store, tls_ctx->cert_store)) { + return 1; + } + + /* enable CRL checks */ + X509_STORE_set_flags(tls_ctx->cert_store, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL); + } + + SSL_CTX_set_cert_store(tls_cfg, tls_ctx->cert_store); + + X509_free(tls_ctx->cert); + tls_ctx->cert = NULL; + EVP_PKEY_free(tls_ctx->pkey); + tls_ctx->pkey = NULL; + X509_STORE_free(tls_ctx->crl_store); + tls_ctx->crl_store = NULL; + + return 0; +} + uint32_t nc_tls_get_verify_result_wrap(void *tls_session) { return SSL_get_verify_result(tls_session); } -const char * +char * nc_tls_verify_error_string_wrap(uint32_t err_code) { - return X509_verify_cert_error_string(err_code); + return strdup(X509_verify_cert_error_string(err_code)); } void -nc_tls_print_error_string_wrap(int connect_ret, const char *peername, void *tls_session) +nc_client_tls_print_connect_err_wrap(int connect_ret, const char *peername, void *tls_session) { switch (SSL_get_error(tls_session, connect_ret)) { case SSL_ERROR_SYSCALL: @@ -804,7 +806,7 @@ nc_tls_print_error_string_wrap(int connect_ret, const char *peername, void *tls_ } void -nc_server_tls_print_accept_error_wrap(int accept_ret, void *tls_session) +nc_server_tls_print_accept_err_wrap(int accept_ret, void *tls_session) { switch (SSL_get_error(tls_session, accept_ret)) { case SSL_ERROR_SYSCALL: @@ -820,18 +822,18 @@ nc_server_tls_print_accept_error_wrap(int accept_ret, void *tls_session) } int -nc_der_to_pubkey_wrap(const unsigned char *der, long len) +nc_tls_is_der_subpubkey_wrap(unsigned char *der, long len) { int ret; EVP_PKEY *pkey; - pkey = d2i_PUBKEY(NULL, &der, len); + pkey = d2i_PUBKEY(NULL, (const unsigned char **)&der, len); if (pkey) { /* success */ - ret = 0; + ret = 1; } else { /* fail */ - ret = 1; + ret = 0; } EVP_PKEY_free(pkey); @@ -839,112 +841,50 @@ nc_der_to_pubkey_wrap(const unsigned char *der, long len) } int -nc_base64_decode_wrap(const char *base64, char **bin) +nc_base64_decode_wrap(const char *base64, unsigned char **bin) { - BIO *bio, *bio64 = NULL; - size_t used = 0, size = 0, r = 0; - void *tmp = NULL; - int nl_count, i, remainder, ret = 0; - char *b64; - - /* insert new lines into the base64 string, so BIO_read works correctly */ - nl_count = strlen(base64) / 64; - remainder = strlen(base64) - 64 * nl_count; - b64 = calloc(strlen(base64) + nl_count + 1, 1); - NC_CHECK_ERRMEM_RET(!b64, -1); - - for (i = 0; i < nl_count; i++) { - /* copy 64 bytes and add a NL */ - strncpy(b64 + i * 65, base64 + i * 64, 64); - b64[i * 65 + 64] = '\n'; - } - - /* copy the rest */ - strncpy(b64 + i * 65, base64 + i * 64, remainder); + int ret; - bio64 = BIO_new(BIO_f_base64()); - if (!bio64) { - ERR(NULL, "Error creating a bio (%s).", ERR_reason_error_string(ERR_get_error())); - ret = -1; - goto cleanup; - } + *bin = malloc((strlen(base64) / 4) * 3); + NC_CHECK_ERRMEM_RET(!*bin, -1); - bio = BIO_new_mem_buf(b64, strlen(b64)); - if (!bio) { - ERR(NULL, "Error creating a bio (%s).", ERR_reason_error_string(ERR_get_error())); - ret = -1; - goto cleanup; + ret = EVP_DecodeBlock(*bin, (const unsigned char *)base64, strlen(base64)); + if (ret == -1) { + ERR(NULL, "Base64 decoding failed (%s).", ERR_reason_error_string(ERR_get_error())); + free(*bin); + *bin = NULL; } - - BIO_push(bio64, bio); - - /* store the decoded base64 in bin */ - *bin = NULL; - do { - size += 64; - - tmp = realloc(*bin, size); - if (!tmp) { - ERRMEM; - free(*bin); - *bin = NULL; - ret = -1; - goto cleanup; - } - *bin = tmp; - - r = BIO_read(bio64, *bin + used, 64); - used += r; - } while (r == 64); - - ret = size; - -cleanup: - free(b64); - BIO_free_all(bio64); return ret; } int nc_base64_encode_wrap(const unsigned char *bin, size_t len, char **base64) { - BIO *bio = NULL, *b64 = NULL; - BUF_MEM *bptr; - int ret = 0; + int ret, size; - bio = BIO_new(BIO_s_mem()); - if (!bio) { - ERR(NULL, "Error creating a bio (%s).", ERR_reason_error_string(ERR_get_error())); - ret = -1; - goto cleanup; + /* calculate the size, for every 3B of in 4B of out, + padding if not divisible + null terminator */ + if (len % 3) { + size = (len / 3) * 4 + 4 + 1; + } else { + size = (len / 3) * 4 + 1; } - b64 = BIO_new(BIO_f_base64()); - if (!b64) { - ERR(NULL, "Error creating a bio (%s).", ERR_reason_error_string(ERR_get_error())); - ret = -1; - goto cleanup; - } + *base64 = malloc(size); + NC_CHECK_ERRMEM_RET(!*base64, -1); - bio = BIO_push(b64, bio); - BIO_write(bio, bin, len); - if (BIO_flush(bio) != 1) { - ERR(NULL, "Error flushing the bio (%s).", ERR_reason_error_string(ERR_get_error())); - ret = -1; - goto cleanup; + ret = EVP_EncodeBlock((unsigned char *)*base64, bin, len); + if (ret == -1) { + ERR(NULL, "Base64 encoding failed (%s).", ERR_reason_error_string(ERR_get_error())); + free(*base64); + *base64 = NULL; + return -1; } - BIO_get_mem_ptr(bio, &bptr); - *base64 = strndup(bptr->data, bptr->length); - NC_CHECK_ERRMEM_GOTO(!*base64, ret = -1, cleanup); - -cleanup: - BIO_free_all(bio); - return ret; + return 0; } static char * -nc_ssl_error_get_reasons(void) +nc_tls_get_err_reasons(void) { unsigned int e; int reason_size, reason_len; @@ -998,7 +938,7 @@ nc_tls_read_wrap(struct nc_session *session, unsigned char *buf, size_t size) rc = -1; break; case SSL_ERROR_SSL: - reasons = nc_ssl_error_get_reasons(); + reasons = nc_tls_get_err_reasons(); ERR(session, "TLS communication error (%s).", reasons); free(reasons); session->status = NC_STATUS_INVALID; @@ -1042,7 +982,7 @@ nc_tls_write_wrap(struct nc_session *session, const unsigned char *buf, size_t s rc = -1; break; case SSL_ERROR_SSL: - reasons = nc_ssl_error_get_reasons(); + reasons = nc_tls_get_err_reasons(); ERR(session, "TLS communication error (%s).", reasons); free(reasons); rc = -1; @@ -1058,7 +998,7 @@ nc_tls_write_wrap(struct nc_session *session, const unsigned char *buf, size_t s } int -nc_tls_have_pending_wrap(void *tls_session) +nc_tls_get_num_pending_bytes_wrap(void *tls_session) { return SSL_pending(tls_session); } @@ -1066,7 +1006,7 @@ nc_tls_have_pending_wrap(void *tls_session) int nc_tls_get_fd_wrap(const struct nc_session *session) { - return SSL_get_fd(session->ti.tls.session); + return session->ti.tls.session ? SSL_get_fd(session->ti.tls.session) : -1; } void @@ -1076,11 +1016,19 @@ nc_tls_close_notify_wrap(void *tls_session) } void * -nc_tls_import_key_file_wrap(const char *key_path, FILE *file) +nc_tls_import_privkey_file_wrap(const char *key_path) { EVP_PKEY *pkey; + FILE *file; + + file = fopen(key_path, "r"); + if (!file) { + ERR(NULL, "Opening the private key file \"%s\" failed.", key_path); + return NULL; + } pkey = PEM_read_PrivateKey(file, NULL, NULL, NULL); + fclose(file); if (!pkey) { ERR(NULL, "Parsing the private key file \"%s\" failed (%s).", key_path, ERR_reason_error_string(ERR_get_error())); } @@ -1109,7 +1057,7 @@ nc_tls_import_cert_file_wrap(const char *cert_path) } char * -nc_tls_export_key_wrap(void *pkey) +nc_tls_export_privkey_pem_wrap(void *pkey) { BIO *bio = NULL; char *pem = NULL; @@ -1137,9 +1085,8 @@ nc_tls_export_key_wrap(void *pkey) } char * -nc_tls_export_cert_wrap(void *cert) +nc_tls_export_cert_pem_wrap(void *cert) { - int rc, cert_len; BIO *bio = NULL; char *pem = NULL; @@ -1149,31 +1096,16 @@ nc_tls_export_cert_wrap(void *cert) goto cleanup; } - rc = PEM_write_bio_X509(bio, cert); - if (!rc) { + if (!PEM_write_bio_X509(bio, cert)) { ERR(NULL, "Exporting the certificate failed (%s).", ERR_reason_error_string(ERR_get_error())); goto cleanup; } - cert_len = BIO_pending(bio); - if (cert_len <= 0) { - ERR(NULL, "Getting the certificate length failed (%s).", ERR_reason_error_string(ERR_get_error())); - goto cleanup; - } - - pem = malloc(cert_len + 1); + pem = malloc(BIO_number_written(bio) + 1); NC_CHECK_ERRMEM_GOTO(!pem, , cleanup); - /* read the cert from bio */ - rc = BIO_read(bio, pem, cert_len); - if (rc <= 0) { - ERR(NULL, "Reading the certificate failed (%s).", ERR_reason_error_string(ERR_get_error())); - free(pem); - pem = NULL; - goto cleanup; - } - - pem[cert_len] = '\0'; + BIO_read(bio, pem, BIO_number_written(bio)); + pem[BIO_number_written(bio)] = '\0'; cleanup: BIO_free(bio); @@ -1181,7 +1113,7 @@ nc_tls_export_cert_wrap(void *cert) } char * -nc_tls_export_pubkey_wrap(void *pkey) +nc_tls_export_pubkey_pem_wrap(void *pkey) { BIO *bio = NULL; char *pem = NULL; @@ -1208,20 +1140,6 @@ nc_tls_export_pubkey_wrap(void *pkey) return pem; } -int -nc_tls_export_key_der_wrap(void *pkey, unsigned char **der, size_t *size) -{ - *der = NULL; - - *size = i2d_PrivateKey(pkey, der); - if (*size < 0) { - ERR(NULL, "Exporting the private key to DER format failed (%s).", ERR_reason_error_string(ERR_get_error())); - return 1; - } - - return 0; -} - int nc_tls_privkey_is_rsa_wrap(void *pkey) { @@ -1231,7 +1149,7 @@ nc_tls_privkey_is_rsa_wrap(void *pkey) int nc_tls_get_rsa_pubkey_params_wrap(void *pkey, void **e, void **n) { - BIGNUM *exp, *mod; + BIGNUM *exp = NULL, *mod = NULL; if (!EVP_PKEY_get_bn_param(pkey, "e", &exp)) { ERR(NULL, "Getting the RSA public exponent failed (%s).", ERR_reason_error_string(ERR_get_error())); @@ -1249,6 +1167,12 @@ nc_tls_get_rsa_pubkey_params_wrap(void *pkey, void **e, void **n) return 0; } +void +nc_tls_destroy_mpi_wrap(void *mpi) +{ + BN_free(mpi); +} + int nc_tls_privkey_is_ec_wrap(void *pkey) { @@ -1281,39 +1205,54 @@ nc_tls_get_ec_group_wrap(void *pkey) } int -nc_tls_get_ec_pubkey_param_wrap(void *pkey, unsigned char **bin, int *bin_len) +nc_tls_get_ec_pubkey_params_wrap(void *pkey, void **q, void **UNUSED(q_grp)) { - int ret = 0; - BIGNUM *p; + BIGNUM *p = NULL; if (!EVP_PKEY_get_bn_param(pkey, "p", &p)) { ERR(NULL, "Getting public key point from the EC private key failed (%s).", ERR_reason_error_string(ERR_get_error())); - ret = 1; - goto cleanup; + return 1; } + *q = p; + + return 0; +} + +int +nc_tls_ec_point_to_bin_wrap(void *q, void *UNUSED(q_grp), unsigned char **bin, int *bin_len) +{ /* prepare buffer for converting p to binary */ - *bin = malloc(BN_num_bytes(p)); - NC_CHECK_ERRMEM_GOTO(!*bin, ret = 1, cleanup); + *bin = malloc(BN_num_bytes(q)); + NC_CHECK_ERRMEM_RET(!*bin, 1); /* convert to binary */ - *bin_len = BN_bn2bin(p, *bin); + *bin_len = BN_bn2bin(q, *bin); + return 0; +} -cleanup: +void +nc_tls_ec_point_destroy_wrap(void *p) +{ BN_free(p); - return ret; } -int -nc_tls_get_bn_num_bytes_wrap(void *bn) +void +nc_tls_ec_group_destroy_wrap(void *UNUSED(grp)) { - return BN_num_bytes(bn); + return; } -void -nc_tls_bn_bn2bin_wrap(void *bn, unsigned char *bin) +int +nc_tls_mpi2bin_wrap(void *mpi, unsigned char **bin, int *bin_len) { - BN_bn2bin(bn, bin); + /* prepare buffer for converting mpi to binary */ + *bin = malloc(BN_num_bytes(mpi)); + NC_CHECK_ERRMEM_RET(!*bin, 1); + + /* convert to binary */ + *bin_len = BN_bn2bin(mpi, *bin); + return 0; } void * @@ -1343,8 +1282,6 @@ int nc_server_tls_get_crl_distpoint_uris_wrap(void *cert_store, char ***uris, int *uri_count) { int ret = 0, i, j, k, gtype; - CURL *handle = NULL; - struct nc_curl_data downloaded = {0}; STACK_OF(X509_OBJECT) * objs; X509_OBJECT *obj; @@ -1355,7 +1292,6 @@ nc_server_tls_get_crl_distpoint_uris_wrap(void *cert_store, char ***uris, int *u GENERAL_NAMES *general_names; GENERAL_NAME *general_name; ASN1_STRING *asn_string_uri; - const char *crl_distpoint_uri; void *tmp; NC_CHECK_ARG_RET(NULL, cert_store, uris, uri_count, 1); @@ -1416,3 +1352,52 @@ nc_server_tls_get_crl_distpoint_uris_wrap(void *cert_store, char ***uris, int *u cleanup: return ret; } + +int +nc_tls_process_cipher_suite_wrap(const char *cipher, char **out) +{ + int i; + + *out = malloc(strlen(cipher) + 1); + NC_CHECK_ERRMEM_RET(!*out, 1); + + /* convert to uppercase */ + for (i = 0; cipher[i]; i++) { + if (cipher[i] == '-') { + /* OpenSSL requires _ instead of - in cipher names */ + (*out)[i] = '_'; + } else { + (*out)[i] = toupper(cipher[i]); + } + } + + (*out)[i] = '\0'; + return 0; +} + +int +nc_tls_append_cipher_suite_wrap(struct nc_server_tls_opts *opts, const char *cipher_suite) +{ + if (!opts->ciphers) { + /* first entry */ + opts->ciphers = strdup(cipher_suite); + NC_CHECK_ERRMEM_RET(!opts->ciphers, 1); + } else { + /* + 1 because of : between entries */ + opts->ciphers = nc_realloc(opts->ciphers, strlen(opts->ciphers) + strlen(cipher_suite) + 1 + 1); + NC_CHECK_ERRMEM_RET(!opts->ciphers, 1); + strcat(opts->ciphers, ":"); + strcat(opts->ciphers, cipher_suite); + } + + return 0; +} + +void +nc_server_tls_set_cipher_suites_wrap(void *tls_cfg, void *cipher_suites) +{ + /* set for TLS1.2 and lower */ + SSL_CTX_set_cipher_list(tls_cfg, cipher_suites); + /* set for TLS1.3 */ + SSL_CTX_set_ciphersuites(tls_cfg, cipher_suites); +} From ed0dd53445182d4c4808e566d5bbb7a90aa70e46 Mon Sep 17 00:00:00 2001 From: roman Date: Tue, 23 Apr 2024 15:10:12 +0200 Subject: [PATCH 24/54] session wrapper UPDATE add wrapper docs --- src/session_wrapper.h | 598 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 545 insertions(+), 53 deletions(-) diff --git a/src/session_wrapper.h b/src/session_wrapper.h index c9b9dc73..43c5d74f 100644 --- a/src/session_wrapper.h +++ b/src/session_wrapper.h @@ -22,20 +22,19 @@ struct nc_tls_ctx { #else -#include +#include struct nc_tls_ctx { - char dummy[0]; + X509 *cert; + EVP_PKEY *pkey; + X509_STORE *cert_store; + X509_STORE *crl_store; }; #endif -struct nc_server_opts; - struct nc_tls_verify_cb_data { struct nc_session *session; - struct nc_cert_grouping *ee_certs; - struct nc_cert_grouping *referenced_ee_certs; struct nc_server_tls_opts *opts; struct nc_ctn_data { char *username; @@ -45,113 +44,425 @@ struct nc_tls_verify_cb_data { } ctn_data; }; +/** + * @brief Creates a new TLS session from the given configuration. + * + * @param[in] tls_cfg TLS configuration. + * @return New TLS session on success, NULL on fail. + */ void * nc_tls_session_new_wrap(void *tls_cfg); +/** + * @brief Destroys a TLS session. + * + * @param[in] tls_session TLS session to destroy. + */ void nc_tls_session_destroy_wrap(void *tls_session); -void * nc_server_tls_config_new_wrap(); - -void * nc_client_tls_config_new_wrap(); +/** + * @brief Creates a new TLS configuration. + * + * @param[in] side Side of the TLS connection. + * @return New TLS configuration on success, NULL on fail. + */ +void * nc_tls_config_new_wrap(int side); +/** + * @brief Destroys a TLS configuration. + * + * @param[in] tls_cfg TLS configuration to destroy. + */ void nc_tls_config_destroy_wrap(void *tls_cfg); +/** + * @brief Creates a new TLS certificate. + * + * @return New TLS certificate on success, NULL on fail. + */ void * nc_tls_cert_new_wrap(); +/** + * @brief Destroys a TLS certificate. + * + * @param[in] cert TLS certificate to destroy. + */ void nc_tls_cert_destroy_wrap(void *cert); -void * nc_tls_privkey_new_wrap(); - +/** + * @brief Destroys a TLS private key. + * + * @param[in] pkey TLS private key to destroy. + */ void nc_tls_privkey_destroy_wrap(void *pkey); +/** + * @brief Creates a new TLS certificate store. + * + * @return New TLS certificate store on success, NULL on fail. + */ void * nc_tls_cert_store_new_wrap(); +/** + * @brief Destroys a TLS certificate store. + * + * @param[in] cert_store TLS certificate store to destroy. + */ void nc_tls_cert_store_destroy_wrap(void *cert_store); +/** + * @brief Creates a new CRL store. + * + * @return New CRL store on success, NULL on fail. + */ void * nc_tls_crl_store_new_wrap(); -void nc_tls_crl_store_destroy_wrap(void *crl); - -void nc_tls_set_authmode_wrap(void *tls_cfg); - -int nc_server_tls_set_config_defaults_wrap(void *tls_cfg); +/** + * @brief Destroys a CRL store. + * + * @param[in] crl_store CRL store to destroy. + */ +void nc_tls_crl_store_destroy_wrap(void *crl_store); +/** + * @brief Converts PEM certificate data to a certificate. + * + * @param[in] cert_data PEM certificate data. + * @return New certificate on success, NULL on fail. + */ void * nc_tls_pem_to_cert_wrap(const char *cert_data); -void * nc_tls_base64_to_cert_wrap(const char *cert_data); - -int nc_tls_pem_to_cert_add_to_store_wrap(const char *cert_data, void *cert_store); +/** + * @brief Adds a certificate to a certificate store. + * + * @param[in] cert Certificate to add. + * @param[in] cert_store Certificate store to add the certificate to. + * @return 0 on success and the memory belongs to cert_store, non-zero on fail. + */ +int nc_tls_add_cert_to_store_wrap(void *cert, void *cert_store); +/** + * @brief Converts PEM private key data to a private key. + * + * @param[in] privkey_data PEM private key data. + * @return New private key on success, NULL on fail. + */ void * nc_tls_pem_to_privkey_wrap(const char *privkey_data); -int nc_tls_load_cert_private_key_wrap(void *tls_cfg, void *cert, void *pkey); - -int nc_server_tls_crl_path_wrap(const char *crl_path, void *cert_store, void *crl_store); - -int nc_server_tls_add_crl_to_store_wrap(const unsigned char *crl_data, size_t size, void *cert_store, void *crl_store); +/** + * @brief Imports CRL from a file. + * + * @param[in] path Path to the CRL file. + * @param[in] crl_store CRL store to import the CRL to. + * @return 0 on success, non-zero on fail. + */ +int nc_tls_import_crl_path_wrap(const char *path, void *crl_store); -void nc_server_tls_set_certs_wrap(void *tls_cfg, void *cert_store, void *crl_store); +/** + * @brief Parses and adds a CRL to a CRL store. + * + * @param[in] crl_data CRL data. + * @param[in] size Size of the CRL data. + * @param[in] crl_store CRL store to add the CRL to. + * @return 0 on success, non-zero on fail. + */ +int nc_server_tls_add_crl_to_store_wrap(const unsigned char *crl_data, size_t size, void *crl_store); +/** + * @brief Sets the TLS version. + * + * @param[in] tls_cfg TLS configuration. + * @param[in] tls_versions Bit-field of supported TLS versions. + * + * @return 0 on success, non-zero on fail. + */ int nc_server_tls_set_tls_versions_wrap(void *tls_cfg, unsigned int tls_versions); -void nc_server_tls_set_verify_cb_wrap(void *tls_session, struct nc_tls_verify_cb_data *cb_data); +/** + * @brief Set TLS server's verify flags, verify cb and its data. + * + * @param[in] tls_cfg TLS configuration. + * @param[in] cb_data Verify callback data. + */ +void nc_server_tls_set_verify_wrap(void *tls_cfg, struct nc_tls_verify_cb_data *cb_data); + +/** + * @brief Set TLS client's verify flags. + * + * @param[in] tls_cfg TLS configuration. + */ +void nc_client_tls_set_verify_wrap(void *tls_cfg); +/** + * @brief Verify the certificate. + * + * @param[in] cert Certificate to verify. + * @param[in] depth Certificate depth. + * @param[in] self_signed Boolean flag representing self-signedness of the certificate. + * @param[in] cb_data Data for the verify callback. + * @return 0 on success, 1 on verify fail, -1 on fatal error. + */ int nc_server_tls_verify_cert(void *cert, int depth, int self_signed, struct nc_tls_verify_cb_data *cb_data); +/** + * @brief Check if the peer certificate matches any configured ee certs. + * + * @param[in] peer_cert Peer certificate. + * @param[in] opts TLS options. + * @return 0 on success, non-zero on fail. + */ +int nc_server_tls_verify_peer_cert(void *peer_cert, struct nc_server_tls_opts *opts); + +/** + * @brief Get the subject of the certificate. + * + * @param[in] cert Certificate. + * @return Subject of the certificate on success, NULL on fail. + */ char * nc_server_tls_get_subject_wrap(void *cert); +/** + * @brief Get the issuer of the certificate. + * + * @param[in] cert Certificate. + * @return Issuer of the certificate on success, NULL on fail. + */ char * nc_server_tls_get_issuer_wrap(void *cert); -int nc_server_tls_get_username_from_cert_wrap(void *cert, NC_TLS_CTN_MAPTYPE map_type, char **username); +/** + * @brief Get the Subject Alternative Names of the certificate. + * + * @param[in] cert Certificate. + * @return SANs on success, NULL on fail. + */ +void * nc_tls_get_sans_wrap(void *cert); + +/** + * @brief Destroy the SANs. + * + * @param[in] sans SANs to destroy. + */ +void nc_tls_sans_destroy_wrap(void *sans); + +/** + * @brief Get the number of SANs. + * + * @param[in] sans SANs. + * @return Number of SANs. + */ +int nc_tls_get_num_sans_wrap(void *sans); + +/** + * @brief Get the SAN value and type in the context of CTN. + * + * @param[in] sans SANs. + * @param[in] idx Index of the SAN. + * @param[out] san_value SAN value. + * @param[out] san_type SAN type. + * @return 0 on success, non-zero on fail. + */ +int nc_tls_get_san_value_type_wrap(void *sans, int idx, char **san_value, NC_TLS_CTN_MAPTYPE *san_type); +/** + * @brief Compare two certificates. + * + * @param[in] cert1 Certificate 1. + * @param[in] cert2 Certificate 2. + * @return 1 if the certificates match, 0 otherwise. + */ int nc_server_tls_certs_match_wrap(void *cert1, void *cert2); +/** + * @brief Get the MD5 digest of the certificate. + * + * @param[in] cert Certificate. + * @param[out] buf Buffer for the digest. + * @return 0 on success, non-zero on fail. + */ int nc_server_tls_md5_wrap(void *cert, unsigned char *buf); +/** + * @brief Get the SHA1 digest of the certificate. + * + * @param[in] cert Certificate. + * @param[out] buf Buffer for the digest. + * @return 0 on success, non-zero on fail. + */ int nc_server_tls_sha1_wrap(void *cert, unsigned char *buf); +/** + * @brief Get the SHA224 digest of the certificate. + * + * @param[in] cert Certificate. + * @param[out] buf Buffer for the digest. + * @return 0 on success, non-zero on fail. + */ int nc_server_tls_sha224_wrap(void *cert, unsigned char *buf); +/** + * @brief Get the SHA256 digest of the certificate. + * + * @param[in] cert Certificate. + * @param[out] buf Buffer for the digest. + * @return 0 on success, non-zero on fail. + */ int nc_server_tls_sha256_wrap(void *cert, unsigned char *buf); +/** + * @brief Get the SHA384 digest of the certificate. + * + * @param[in] cert Certificate. + * @param[out] buf Buffer for the digest. + * @return 0 on success, non-zero on fail. + */ int nc_server_tls_sha384_wrap(void *cert, unsigned char *buf); +/** + * @brief Get the SHA512 digest of the certificate. + * + * @param[in] cert Certificate. + * @param[out] buf Buffer for the digest. + * @return 0 on success, non-zero on fail. + */ int nc_server_tls_sha512_wrap(void *cert, unsigned char *buf); +/** + * @brief Set the FD for a TLS session. + * + * @param[in] tls_session TLS session. + * @param[in] sock Socket FD. + * @param[in] tls_ctx TLS context. + */ void nc_server_tls_set_fd_wrap(void *tls_session, int sock, struct nc_tls_ctx *tls_ctx); +/** + * @brief Perform a server-side step of the TLS handshake. + * + * @param[in] tls_session TLS session. + * @return 1 on success, 0 if the handshake is not finished, negative number on error. + */ int nc_server_tls_handshake_step_wrap(void *tls_session); -int nc_server_tls_fill_config_wrap(void *tls_cfg, void *srv_cert, void *srv_pkey, void *cert_store, void *crl_store, struct nc_tls_ctx *tls_ctx); - -int nc_server_tls_setup_config_fill_ctx_wrap(void *tls_cfg, struct nc_tls_ctx *tls_ctx, void *srv_cert, void *srv_pkey, void *cert_store, void *crl_store, int sock); +/** + * @brief Perform a client-side step of the TLS handshake. + * + * @param[in] tls_session TLS session. + * @param[in] sock Socket FD. + * @return 1 on success, 0 if the handshake is not finished, negative number on error. + */ +int nc_client_tls_handshake_step_wrap(void *tls_session, int sock); +/** + * @brief Destroy a TLS context. + * + * @param[in] tls_ctx TLS context. + */ void nc_tls_ctx_destroy_wrap(struct nc_tls_ctx *tls_ctx); +/** + * @brief Load client's certificate and a private key. + * + * @param[in] cert_path Path to the certificate. + * @param[in] key_path Path to the private key. + * @param[out] cert Certificate. + * @param[out] pkey Private key. + * @return 0 on success, non-zero on fail. + */ int nc_client_tls_load_cert_key_wrap(const char *cert_path, const char *key_path, void **cert, void **pkey); +/** + * @brief Load client's trusted certificates. + * + * @param[in] cert_store Certificate store. + * @param[in] file_path Path to the file with trusted certificates. + * @param[in] dir_path Path to the directory with trusted certificates. + * @return 0 on success, non-zero on fail. + */ int nc_client_tls_load_trusted_certs_wrap(void *cert_store, const char *file_path, const char *dir_path); -int nc_client_tls_load_crl_wrap(void *cert_store, void *crl_store, const char *file_path, const char *dir_path); +/** + * @brief Load client's CRLs. + * + * @param[in] crl_store CRL store. + * @param[in] file_path Path to the file with CRLs. + * @param[in] dir_path Path to the directory with CRLs. + * @return 0 on success, non-zero on fail. + */ +int nc_client_tls_load_crl_wrap(void *crl_store, const char *file_path, const char *dir_path); +/** + * @brief Set the hostname for the TLS session. + * + * @param[in] tls_session TLS session. + * @param[in] hostname Hostname. + * @return 0 on success, non-zero on fail. + */ int nc_client_tls_set_hostname_wrap(void *tls_session, const char *hostname); -int nc_client_tls_handshake_step_wrap(void *tls_session); - -uint32_t nc_tls_get_verify_result_wrap(void *tls_session); - -const char * nc_tls_verify_error_string_wrap(uint32_t err_code); +/** + * @brief Initialize a TLS context. + * + * @param[in] sock Socket FD. + * @param[in] cert Certificate. + * @param[in] pkey Private key. + * @param[in] cert_store Certificate store. + * @param[in] crl_store CRL store. + * @param[in,out] tls_ctx TLS context. + * @return 0 on success, non-zero on fail. + */ +int nc_tls_init_ctx_wrap(int sock, void *cert, void *pkey, void *cert_store, void *crl_store, struct nc_tls_ctx *tls_ctx); -void nc_tls_print_error_string_wrap(int connect_ret, const char *peername, void *tls_session); +/** + * @brief Setup a TLS configuration from a TLS context. + * + * @param[in] tls_ctx TLS context. + * @param[in] side Side of the TLS connection. + * @param[in,out] tls_cfg TLS configuration. + * @return 0 on success, non-zero on fail. + */ +int nc_tls_setup_config_from_ctx_wrap(struct nc_tls_ctx *tls_ctx, int side, void *tls_cfg); -void nc_server_tls_print_accept_error_wrap(int accept_ret, void *tls_session); +/** + * @brief Get the error code from a TLS session's verification. + * + * @param[in] tls_session TLS session. + * @return Error code, 0 indicates success. + */ +uint32_t nc_tls_get_verify_result_wrap(void *tls_session); -int nc_tls_init_ctx_wrap(struct nc_tls_ctx *tls_ctx, int sock, void *cli_cert, void *cli_pkey, void *cert_store, void *crl_store); +/** + * @brief Get the error string from a TLS session's verification. + * + * @param[in] err_code Error code. + * @return Error string. + */ +char * nc_tls_verify_error_string_wrap(uint32_t err_code); -int nc_tls_setup_config_wrap(void *tls_cfg, int side, struct nc_tls_ctx *tls_ctx); +/** + * @brief Print the TLS session's connection error. + * + * @param[in] connect_ret Error code. + * @param[in] peername Peername. + * @param[in] tls_session TLS session. + */ +void nc_client_tls_print_connect_err_wrap(int connect_ret, const char *peername, void *tls_session); -void nc_tls_session_new_cleanup_wrap(void *tls_cfg, void *cli_cert, void *cli_pkey, void *cert_store, void *crl_store); +/** + * @brief Print the TLS session's accept error. + * + * @param[in] accept_ret Error code. + * @param[in] tls_session TLS session. + */ +void nc_server_tls_print_accept_err_wrap(int accept_ret, void *tls_session); -int nc_der_to_pubkey_wrap(const unsigned char *der, long len); +/** + * @brief Checks if the DER data is a SubjectPublicKeyInfo public key. + * + * @param[in] der DER data. + * @param[in] len Length of the DER data. + * + * @return 1 if the data is a SubjectPublicKeyInfo public key, 0 if not, -1 on error. + */ +int nc_tls_is_der_subpubkey_wrap(unsigned char *der, long len); /** * @brief Decodes base64 to binary. @@ -160,48 +471,229 @@ int nc_der_to_pubkey_wrap(const unsigned char *der, long len); * @param[out] bin Binary result, memory managed by the caller. * @return Length of the binary data on success, -1 on error. */ -int nc_base64_decode_wrap(const char *base64, char **bin); +int nc_base64_decode_wrap(const char *base64, unsigned char **bin); +/** + * @brief Encodes binary to base64. + * + * @param[in] bin Binary data. + * @param[in] len Length of the binary data. + * @param[out] base64 NULL terminated Base64 result, memory managed by the caller. + * @return 0 on success, -1 on error. + */ int nc_base64_encode_wrap(const unsigned char *bin, size_t len, char **base64); +/** + * @brief Reads data from a TLS session. + * + * @param[in] session NETCONF session. + * @param[out] buf Buffer for the data. + * @param[in] size Size of the buffer. + * @return Number of bytes read on success, -1 on error. + */ int nc_tls_read_wrap(struct nc_session *session, unsigned char *buf, size_t size); +/** + * @brief Writes data to a TLS session. + * + * @param[in] session NETCONF session. + * @param[in] buf Data to write. + * @param[in] size Size of the data. + * @return Number of bytes written on success, -1 on error. + */ int nc_tls_write_wrap(struct nc_session *session, const unsigned char *buf, size_t size); -int nc_tls_have_pending_wrap(void *tls_session); +/** + * @brief Get the number of pending bytes in a TLS session. + * + * @param[in] tls_session TLS session. + * @return Number of pending bytes. + */ +int nc_tls_get_num_pending_bytes_wrap(void *tls_session); +/** + * @brief Get the file descriptor of a TLS session. + * + * @param[in] session NETCONF session. + * @return File descriptor, -1 on error. + */ int nc_tls_get_fd_wrap(const struct nc_session *session); +/** + * @brief Close a TLS session. + * + * @param[in] tls_session TLS session. + */ void nc_tls_close_notify_wrap(void *tls_session); -void * nc_tls_import_key_file_wrap(const char *key_path, FILE *file); +/** + * @brief Import a private key from a file. + * + * @param[in] privkey_path Path to the private key file. + * @return Imported private key on success, NULL on fail. + */ +void * nc_tls_import_privkey_file_wrap(const char *privkey_path); +/** + * @brief Import a certificate from a file. + * + * @param[in] cert_path Path to the certificate file. + * @return Imported certificate on success, NULL on fail. + */ void * nc_tls_import_cert_file_wrap(const char *cert_path); -char * nc_tls_export_key_wrap(void *pkey); +/** + * @brief Export a private key to a PEM string. + * + * @param[in] pkey Private key. + * @return PEM string on success, NULL on fail. + */ +char * nc_tls_export_privkey_pem_wrap(void *pkey); -char * nc_tls_export_cert_wrap(void *cert); +/** + * @brief Export a certificate to a PEM string. + * + * @param[in] cert Certificate. + * @return PEM string on success, NULL on fail. + */ +char * nc_tls_export_cert_pem_wrap(void *cert); -int nc_tls_export_key_der_wrap(void *pkey, unsigned char **der, size_t *size); +/** + * @brief Export a public key to a PEM string. + * + * @param[in] pkey Public key. + * @return PEM string on success, NULL on fail. + */ +char * nc_tls_export_pubkey_pem_wrap(void *pkey); +/** + * @brief Check if a private key is RSA. + * + * @param[in] pkey Private key. + * @return 1 if the private key is RSA, 0 if not. + */ int nc_tls_privkey_is_rsa_wrap(void *pkey); +/** + * @brief Get the RSA public key parameters from a private key. + * + * @param[in] pkey Private key. + * @param[out] e Exponent. + * @param[out] n Modulus. + * @return 0 on success, non-zero on fail. + */ int nc_tls_get_rsa_pubkey_params_wrap(void *pkey, void **e, void **n); +/** + * @brief Destroy an MPI. + * + * @param[in] mpi MPI. + */ +void nc_tls_destroy_mpi_wrap(void *mpi); + +/** + * @brief Check if a private key is EC. + * + * @param[in] pkey Private key. + * @return 1 if the private key is EC, 0 if not. + */ int nc_tls_privkey_is_ec_wrap(void *pkey); +/** + * @brief Get the group name of an EC private key. + * + * @param[in] pkey Private key. + * @return Group name on success, NULL on fail. + */ char * nc_tls_get_ec_group_wrap(void *pkey); -int nc_tls_get_ec_pubkey_param_wrap(void *pkey, unsigned char **bin, int *bin_len); +/** + * @brief Get the EC public key parameters from a private key. + * + * @param[in] pkey Private key. + * @param[out] q Public key point. + * @param[out] q_grp Public key group. + * @return 0 on success, non-zero on fail. + */ +int nc_tls_get_ec_pubkey_params_wrap(void *pkey, void **q, void **q_grp); -int nc_tls_get_bn_num_bytes_wrap(void *bn); +/** + * @brief Convert an EC point to binary. + * + * @param[in] q EC point. + * @param[in] q_grp EC group. + * @param[out] bin Binary point. + * @param[out] bin_len Length of the binary point. + * @return 0 on success, non-zero on fail. + */ +int nc_tls_ec_point_to_bin_wrap(void *q, void *q_grp, unsigned char **bin, int *bin_len); -void nc_tls_bn_bn2bin_wrap(void *bn, unsigned char *bin); +/** + * @brief Destroy an EC point. + * + * @param[in] p EC point. + */ +void nc_tls_ec_point_destroy_wrap(void *p); -void * nc_tls_import_pubkey_file_wrap(const char *pubkey_path); +/** + * @brief Destroy an EC group. + * + * @param[in] grp EC group. + */ +void nc_tls_ec_group_destroy_wrap(void *grp); -char * nc_tls_export_pubkey_wrap(void *pkey); +/** + * @brief Convert an MPI to binary. + * + * @param[in] mpi MPI. + * @param[out] bin Binary buffer. + * @param[out] bin_len Length of the binary. + * @return 0 on success, 1 on error. + */ +int nc_tls_mpi2bin_wrap(void *mpi, unsigned char **bin, int *bin_len); + +/** + * @brief Import a public key from a file. + * + * @param[in] pubkey_path Path to the public key file. + * @return Imported public key on success, NULL on fail. + */ +void * nc_tls_import_pubkey_file_wrap(const char *pubkey_path); +/** + * @brief Get all the URIs from a CRL distribution points. + * + * @param[in] cert_store Certificate store. + * @param[out] uris URIs to download the CRLs from. + * @param[out] uri_count Number of URIs found. + * @return 0 on success, non-zero on fail. + */ int nc_server_tls_get_crl_distpoint_uris_wrap(void *cert_store, char ***uris, int *uri_count); +/** + * @brief Process a cipher suite so that it can be set by the underlying TLS lib. + * + * @param[in] cipher Cipher suite identity value. + * @param[out] out Processed cipher suite. + * @return 0 on success, 1 on fail. + */ +int nc_tls_process_cipher_suite_wrap(const char *cipher, char **out); + +/** + * @brief Append a cipher suite to the list of cipher suites. + * + * @param[in] opts TLS options. + * @param[in] cipher_suite Cipher suite to append. + * @return 0 on success, 1 on fail. + */ +int nc_tls_append_cipher_suite_wrap(struct nc_server_tls_opts *opts, const char *cipher_suite); + +/** + * @brief Set the list of cipher suites for the TLS configuration. + * + * @param[in] tls_cfg TLS configuration. + * @param[in] cipher_suites List of cipher suites. + */ +void nc_server_tls_set_cipher_suites_wrap(void *tls_cfg, void *cipher_suites); + #endif From 267c0fc20b143138299bdfd637ceba75cb86d289 Mon Sep 17 00:00:00 2001 From: roman Date: Tue, 23 Apr 2024 15:11:02 +0200 Subject: [PATCH 25/54] io UPDATE rename wrapped function --- src/io.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/io.c b/src/io.c index e87da1a1..daae0232 100644 --- a/src/io.c +++ b/src/io.c @@ -37,6 +37,7 @@ #include "netconf.h" #include "session.h" #include "session_p.h" +#include "session_wrapper.h" const char *nc_msgtype2str[] = { "error", @@ -418,7 +419,7 @@ nc_read_poll(struct nc_session *session, int io_timeout) } break; case NC_TI_TLS: - ret = nc_tls_have_pending_wrap(session->ti.tls.session); + ret = nc_tls_get_num_pending_bytes_wrap(session->ti.tls.session); if (ret) { /* some buffered TLS data available */ ret = 1; From 2c3965630cd52bc6a5d7cde676841899df236a42 Mon Sep 17 00:00:00 2001 From: roman Date: Tue, 23 Apr 2024 15:12:09 +0200 Subject: [PATCH 26/54] session server UPDATE reflect wrapper changes --- src/session_server.c | 2 +- src/session_server.h | 3 +- src/session_server_ssh.c | 7 +- src/session_server_tls.c | 183 ++++++++++++++++++++------------------- 4 files changed, 99 insertions(+), 96 deletions(-) diff --git a/src/session_server.c b/src/session_server.c index 35f27460..dbe434af 100644 --- a/src/session_server.c +++ b/src/session_server.c @@ -1651,7 +1651,7 @@ nc_ps_poll_session_io(struct nc_session *session, int io_timeout, time_t now_mon } break; case NC_TI_TLS: - r = nc_tls_have_pending_wrap(session->ti.tls.session); + r = nc_tls_get_num_pending_bytes_wrap(session->ti.tls.session); if (!r) { /* no data pending in the SSL buffer, poll fd */ pfd.fd = nc_tls_get_fd_wrap(session); diff --git a/src/session_server.h b/src/session_server.h index 4c4b2e7c..b6059019 100644 --- a/src/session_server.h +++ b/src/session_server.h @@ -558,8 +558,7 @@ const void *nc_session_get_client_cert(const struct nc_session *session); * @brief Set TLS authentication additional verify callback. * * Server will always perform cert-to-name based on its configuration. Only after it passes - * and this callback is set, it is also called. It should return exactly what OpenSSL - * verify callback meaning 1 for success, 0 to deny the user. + * and this callback is set, it is also called. It should return non-zero for success, 0 to deny the user. * * @param[in] verify_clb Additional user verify callback. */ diff --git a/src/session_server_ssh.c b/src/session_server_ssh.c index c31b7f13..f7fc74dc 100644 --- a/src/session_server_ssh.c +++ b/src/session_server_ssh.c @@ -1009,7 +1009,7 @@ nc_server_ssh_set_authkey_path_format(const char *path) * and the data is in network byte order. The key has to be in the SSH2 format. */ static const char * -nc_server_ssh_get_pubkey_type(const char *buffer, uint32_t *len) +nc_server_ssh_get_pubkey_type(const unsigned char *buffer, uint32_t *len) { uint32_t type_len; @@ -1021,7 +1021,7 @@ nc_server_ssh_get_pubkey_type(const char *buffer, uint32_t *len) /* move 4 bytes in the buffer, this is where the type should be */ buffer += sizeof type_len; - return buffer; + return (const char *)buffer; } /** @@ -1035,7 +1035,7 @@ static int nc_server_ssh_create_ssh_pubkey(const char *base64, ssh_key *key) { int ret = 0; - char *bin = NULL; + unsigned char *bin = NULL; const char *pub_type = NULL; uint32_t pub_type_len = 0; @@ -1045,7 +1045,6 @@ nc_server_ssh_create_ssh_pubkey(const char *base64, ssh_key *key) /* convert base64 to binary */ if (nc_base64_decode_wrap(base64, &bin) == -1) { - ERR(NULL, "Unable to decode base64."); ret = 1; goto cleanup; } diff --git a/src/session_server_tls.c b/src/session_server_tls.c index 8b47e367..8e00b122 100644 --- a/src/session_server_tls.c +++ b/src/session_server_tls.c @@ -117,24 +117,6 @@ nc_base64der_to_cert(const char *in) return cert; } -static int -nc_base64der_to_cert_add_to_store(const char *in, void *cert_store) -{ - int ret; - char *buf = NULL; - - NC_CHECK_ARG_RET(NULL, in, cert_store, 1); - - if (asprintf(&buf, "%s%s%s", "-----BEGIN CERTIFICATE-----\n", in, "\n-----END CERTIFICATE-----") == -1) { - ERRMEM; - return 1; - } - - ret = nc_tls_pem_to_cert_add_to_store_wrap(buf, cert_store); - free(buf); - return ret; -} - static void * nc_base64der_to_privkey(const char *in, const char *key_str) { @@ -420,55 +402,18 @@ nc_server_tls_cert_to_name(struct nc_ctn *ctn_first, void *cert, struct nc_ctn_d return ret; } -static int -nc_server_tls_verify_peer_cert(void *peer_cert, struct nc_cert_grouping *ee_certs) -{ - int i, ret; - void *cert; - struct nc_certificate *certs; - uint16_t cert_count; - - if (ee_certs->store == NC_STORE_LOCAL) { - /* local definition */ - certs = ee_certs->certs; - cert_count = ee_certs->cert_count; - } else { - /* truststore reference */ - if (nc_server_tls_ts_ref_get_certs(ee_certs->ts_ref, &certs, &cert_count)) { - ERR(NULL, "Error getting end-entity certificates from the truststore reference \"%s\".", ee_certs->ts_ref); - return -1; - } - } - - for (i = 0; i < cert_count; i++) { - /* import stored cert */ - cert = nc_base64der_to_cert(certs[i].data); - - /* compare stored with received */ - ret = nc_server_tls_certs_match_wrap(peer_cert, cert); - nc_tls_cert_destroy_wrap(cert); - if (ret) { - /* found a match */ - VRB(NULL, "Cert verify: fail, but the end-entity certificate is trusted, continuing."); - return 0; - } - } - - return 1; -} - int nc_server_tls_get_username_from_cert(void *cert, NC_TLS_CTN_MAPTYPE map_type, char **username) { - char *subject, *cn, *san_value = NULL; + char *subject, *cn, *san_value = NULL, rdn_separator; void *sans; int i, nsans = 0, rc; NC_TLS_CTN_MAPTYPE san_type = 0; #ifdef HAVE_LIBMEDTLS - char rdn_separator = ','; + rdn_separator = ','; #else - char rdn_separator = '/'; + rdn_separator = '/'; #endif if (map_type == NC_TLS_CTN_COMMON_NAME) { @@ -546,6 +491,69 @@ nc_server_tls_get_username_from_cert(void *cert, NC_TLS_CTN_MAPTYPE map_type, ch return 0; } +static int +_nc_server_tls_verify_peer_cert(void *peer_cert, struct nc_cert_grouping *ee_certs) +{ + int i, ret; + void *cert; + struct nc_certificate *certs; + uint16_t cert_count; + + if (ee_certs->store == NC_STORE_LOCAL) { + /* local definition */ + certs = ee_certs->certs; + cert_count = ee_certs->cert_count; + } else { + /* truststore reference */ + if (nc_server_tls_ts_ref_get_certs(ee_certs->ts_ref, &certs, &cert_count)) { + ERR(NULL, "Error getting end-entity certificates from the truststore reference \"%s\".", ee_certs->ts_ref); + return -1; + } + } + + for (i = 0; i < cert_count; i++) { + /* import stored cert */ + cert = nc_base64der_to_cert(certs[i].data); + + /* compare stored with received */ + ret = nc_server_tls_certs_match_wrap(peer_cert, cert); + nc_tls_cert_destroy_wrap(cert); + if (ret) { + /* found a match */ + VRB(NULL, "Cert verify: fail, but the end-entity certificate is trusted, continuing."); + return 0; + } + } + + return 1; +} + +int +nc_server_tls_verify_peer_cert(void *peer_cert, struct nc_server_tls_opts *opts) +{ + int rc; + struct nc_endpt *referenced_endpt; + + rc = _nc_server_tls_verify_peer_cert(peer_cert, &opts->ee_certs); + if (!rc) { + return 0; + } + + if (opts->referenced_endpt_name) { + if (nc_server_get_referenced_endpt(opts->referenced_endpt_name, &referenced_endpt)) { + ERRINT; + return -1; + } + + rc = _nc_server_tls_verify_peer_cert(peer_cert, &referenced_endpt->opts.tls->ee_certs); + if (!rc) { + return 0; + } + } + + return 1; +} + int nc_server_tls_verify_cert(void *cert, int depth, int self_signed, struct nc_tls_verify_cb_data *cb_data) { @@ -571,22 +579,10 @@ nc_server_tls_verify_cert(void *cert, int depth, int self_signed, struct nc_tls_ if (self_signed) { /* peer cert is not trusted, so it must match any configured end-entity cert * on the given endpoint in order for the client to be authenticated */ - ret = nc_server_tls_verify_peer_cert(cert, &opts->ee_certs); + ret = nc_server_tls_verify_peer_cert(cert, opts); if (ret) { - /* we can still check the referenced endpoint's ee certs */ - if (opts->referenced_endpt_name) { - if (nc_server_get_referenced_endpt(opts->referenced_endpt_name, &referenced_endpt)) { - ERRINT; - ret = -1; - goto cleanup; - } - - ret = nc_server_tls_verify_peer_cert(cert, &referenced_endpt->opts.tls->ee_certs); - } - if (ret) { - ERR(session, "Cert verify: fail (Client certificate not trusted and does not match any configured end-entity certificate)."); - goto cleanup; - } + ERR(session, "Cert verify: fail (Client certificate not trusted and does not match any configured end-entity certificate)."); + goto cleanup; } } } @@ -784,13 +780,13 @@ nc_server_tls_curl_init(CURL **handle, struct nc_curl_data *data) } static int -nc_server_tls_crl_path(const char *path, void *cert_store, void *crl_store) +nc_server_tls_crl_path(const char *path, void *crl_store) { - return nc_tls_import_crl_path_wrap(path, cert_store, crl_store); + return nc_tls_import_crl_path_wrap(path, crl_store); } static int -nc_server_tls_crl_url(const char *url, void *cert_store, void *crl_store) +nc_server_tls_crl_url(const char *url, void *crl_store) { int ret = 0; CURL *handle = NULL; @@ -811,7 +807,7 @@ nc_server_tls_crl_url(const char *url, void *cert_store, void *crl_store) } /* convert the downloaded data to CRL and add it to the store */ - ret = nc_server_tls_add_crl_to_store_wrap(downloaded.data, downloaded.size, cert_store, crl_store); + ret = nc_server_tls_add_crl_to_store_wrap(downloaded.data, downloaded.size, crl_store); if (ret) { goto cleanup; } @@ -852,7 +848,7 @@ nc_server_tls_crl_cert_ext(void *cert_store, void *crl_store) } /* convert the downloaded data to CRL and add it to the store */ - ret = nc_server_tls_add_crl_to_store_wrap(downloaded.data, downloaded.size, cert_store, crl_store); + ret = nc_server_tls_add_crl_to_store_wrap(downloaded.data, downloaded.size, crl_store); if (ret) { goto cleanup; } @@ -871,11 +867,11 @@ int nc_server_tls_load_crl(struct nc_server_tls_opts *opts, void *cert_store, void *crl_store) { if (opts->crl_path) { - if (nc_server_tls_crl_path(opts->crl_path, cert_store, crl_store)) { + if (nc_server_tls_crl_path(opts->crl_path, crl_store)) { return 1; } } else if (opts->crl_url) { - if (nc_server_tls_crl_url(opts->crl_url, cert_store, crl_store)) { + if (nc_server_tls_crl_url(opts->crl_url, crl_store)) { return 1; } } else { @@ -892,6 +888,7 @@ nc_server_tls_load_trusted_certs(struct nc_cert_grouping *ca_certs, void *cert_s { struct nc_certificate *certs; uint16_t i, cert_count; + void *cert; if (ca_certs->store == NC_STORE_LOCAL) { /* local definition */ @@ -906,7 +903,15 @@ nc_server_tls_load_trusted_certs(struct nc_cert_grouping *ca_certs, void *cert_s } for (i = 0; i < cert_count; i++) { - if (nc_base64der_to_cert_add_to_store(certs[i].data, cert_store)) { + /* parse data into cert */ + cert = nc_base64der_to_cert(certs[i].data); + if (!cert) { + return 1; + } + + /* store cert in cert store */ + if (nc_tls_add_cert_to_store_wrap(cert, cert_store)) { + nc_tls_cert_destroy_wrap(cert); return 1; } } @@ -931,7 +936,7 @@ nc_server_tls_accept_check(int accept_ret, void *tls_session) } if (accept_ret != 1) { - nc_server_tls_print_accept_error_wrap(accept_ret, tls_session); + nc_server_tls_print_accept_err_wrap(accept_ret, tls_session); } return accept_ret; @@ -953,7 +958,7 @@ nc_accept_tls_session(struct nc_session *session, struct nc_server_tls_opts *opt cb_data.opts = opts; /* prepare TLS context from which a session will be created */ - tls_cfg = nc_server_tls_config_new_wrap(); + tls_cfg = nc_tls_config_new_wrap(NC_SERVER); if (!tls_cfg) { goto fail; } @@ -1016,8 +1021,11 @@ nc_accept_tls_session(struct nc_session *session, struct nc_server_tls_opts *opt nc_server_tls_set_cipher_suites_wrap(tls_cfg, opts->ciphers); } + /* set verify flags, callback and its data */ + nc_server_tls_set_verify_wrap(tls_cfg, &cb_data); + /* init TLS context and store data which may be needed later in it */ - if (nc_tls_init_ctx_wrap(&session->ti.tls.ctx, sock, srv_cert, srv_pkey, cert_store, crl_store)) { + if (nc_tls_init_ctx_wrap(sock, srv_cert, srv_pkey, cert_store, crl_store, &session->ti.tls.ctx)) { goto fail; } @@ -1025,9 +1033,9 @@ nc_accept_tls_session(struct nc_session *session, struct nc_server_tls_opts *opt srv_cert = srv_pkey = cert_store = crl_store = NULL; /* setup config from ctx */ - if (nc_tls_setup_config_wrap(tls_cfg, NC_SERVER, &session->ti.tls.ctx)) { + if (nc_tls_setup_config_from_ctx_wrap(&session->ti.tls.ctx, NC_SERVER, tls_cfg)) { goto fail; - } // TODO free openssl shit + } session->ti.tls.config = tls_cfg; tls_cfg = NULL; @@ -1037,9 +1045,6 @@ nc_accept_tls_session(struct nc_session *session, struct nc_server_tls_opts *opt goto fail; } - /* set verify callback and its data */ - nc_server_tls_set_verify_cb_wrap(session->ti.tls.session, &cb_data); - /* set session fd */ nc_server_tls_set_fd_wrap(session->ti.tls.session, sock, &session->ti.tls.ctx); From 1adb980f71c0f17cfe1d76064648064b589b90cb Mon Sep 17 00:00:00 2001 From: roman Date: Tue, 23 Apr 2024 15:12:32 +0200 Subject: [PATCH 27/54] session client tls UPDATE reflect wrapper changes --- src/session_client_tls.c | 45 ++++++++++++++++++++++++++++------------ 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/src/session_client_tls.c b/src/session_client_tls.c index d7d54eec..4696735a 100644 --- a/src/session_client_tls.c +++ b/src/session_client_tls.c @@ -20,11 +20,13 @@ #include #include +#include #include #include #include +#include "compat.h" #include "config.h" #include "log_p.h" #include "session_client.h" @@ -276,18 +278,21 @@ static int nc_client_tls_connect_check(int connect_ret, void *tls_session, const char *peername) { uint32_t verify; + char *err; /* check certificate verification result */ verify = nc_tls_get_verify_result_wrap(tls_session); if (!verify && (connect_ret == 1)) { VRB(NULL, "Server certificate verified (domain \"%s\").", peername); } else if (verify) { - ERR(NULL, "Server certificate error (%s).", nc_tls_verify_error_string_wrap(verify)); + err = nc_tls_verify_error_string_wrap(verify); + ERR(NULL, "Server certificate error (%s).", err); + free(err); } /* check TLS connection result */ if (connect_ret != 1) { - nc_tls_print_error_string_wrap(connect_ret, peername, tls_session); + nc_client_tls_print_connect_err_wrap(connect_ret, peername, tls_session); } return connect_ret; @@ -296,14 +301,14 @@ nc_client_tls_connect_check(int connect_ret, void *tls_session, const char *peer static void * nc_client_tls_session_new(int sock, const char *host, int timeout, struct nc_client_tls_opts *opts, void **out_tls_cfg, struct nc_tls_ctx *tls_ctx) { - int ret = 0; + int ret = 0, sock_tmp = sock; struct timespec ts_timeout; void *tls_session, *tls_cfg, *cli_cert, *cli_pkey, *cert_store, *crl_store; tls_session = tls_cfg = cli_cert = cli_pkey = cert_store = crl_store = NULL; /* prepare TLS context from which a session will be created */ - tls_cfg = nc_client_tls_config_new_wrap(); + tls_cfg = nc_tls_config_new_wrap(NC_CLIENT); if (!tls_cfg) { goto fail; } @@ -331,22 +336,25 @@ nc_client_tls_session_new(int sock, const char *host, int timeout, struct nc_cli goto fail; } - /* load CRLs into one of the stores */ - if (nc_client_tls_load_crl_wrap(cert_store, crl_store, opts->crl_file, opts->crl_dir)) { + /* load CRLs into the crl store */ + if (nc_client_tls_load_crl_wrap(crl_store, opts->crl_file, opts->crl_dir)) { goto fail; } } + /* set client's verify mode flags */ + nc_client_tls_set_verify_wrap(tls_cfg); + /* init TLS context and store data which may be needed later in it */ - if (nc_tls_init_ctx_wrap(tls_ctx, sock, cli_cert, cli_pkey, cert_store, crl_store)) { - goto fail; // TODO: openssl free all the shit + if (nc_tls_init_ctx_wrap(sock, cli_cert, cli_pkey, cert_store, crl_store, tls_ctx)) { + goto fail; } /* memory is managed by context now */ cli_cert = cli_pkey = cert_store = crl_store = NULL; /* setup config from ctx */ - if (nc_tls_setup_config_wrap(tls_cfg, NC_CLIENT, tls_ctx)) { + if (nc_tls_setup_config_from_ctx_wrap(tls_ctx, NC_CLIENT, tls_cfg)) { goto fail; } @@ -370,7 +378,7 @@ nc_client_tls_session_new(int sock, const char *host, int timeout, struct nc_cli if (timeout > -1) { nc_timeouttime_get(&ts_timeout, timeout); } - while ((ret = nc_client_tls_handshake_step_wrap(tls_session)) == 0) { + while ((ret = nc_client_tls_handshake_step_wrap(tls_session, sock_tmp)) == 0) { usleep(NC_TIMEOUT_STEP); if ((timeout > -1) && (nc_timeouttime_cur_diff(&ts_timeout) < 1)) { ERR(NULL, "SSL connect timeout."); @@ -390,7 +398,13 @@ nc_client_tls_session_new(int sock, const char *host, int timeout, struct nc_cli if (sock > -1) { close(sock); } - nc_tls_session_new_cleanup_wrap(tls_cfg, cli_cert, cli_pkey, cert_store, crl_store); + + nc_tls_session_destroy_wrap(tls_session); + nc_tls_cert_destroy_wrap(cli_cert); + nc_tls_privkey_destroy_wrap(cli_pkey); + nc_tls_cert_store_destroy_wrap(cert_store); + nc_tls_crl_store_destroy_wrap(crl_store); + nc_tls_config_destroy_wrap(tls_cfg); return NULL; } @@ -458,7 +472,7 @@ nc_connect_tls(const char *host, unsigned short port, struct ly_ctx *ctx) goto fail; } - /* store information into session and the dictionary */ + /* store information into session */ session->host = ip_host; session->port = port; session->username = strdup("certificate-based"); @@ -497,7 +511,10 @@ nc_accept_callhome_tls_sock(int sock, const char *host, uint16_t port, struct ly goto fail; } session->ti.tls.config = tls_cfg; + + /* memory belongs to session */ memcpy(&session->ti.tls.ctx, &tls_ctx, sizeof tls_ctx); + memset(&tls_ctx, 0, sizeof tls_ctx); if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) { goto fail; @@ -516,7 +533,7 @@ nc_accept_callhome_tls_sock(int sock, const char *host, uint16_t port, struct ly session->flags |= NC_SESSION_CALLHOME; - /* store information into session and the dictionary */ + /* store information into session */ session->host = strdup(host); session->port = port; session->username = strdup("certificate-based"); @@ -524,5 +541,7 @@ nc_accept_callhome_tls_sock(int sock, const char *host, uint16_t port, struct ly return session; fail: + nc_session_free(session, NULL); + nc_tls_ctx_destroy_wrap(&tls_ctx); return NULL; } From 73e82111a4bd56061bdda494c2290086fae5f711 Mon Sep 17 00:00:00 2001 From: roman Date: Tue, 23 Apr 2024 15:13:00 +0200 Subject: [PATCH 28/54] session UPDATE reflect wrapper changes --- src/session.c | 8 +++++--- src/session_p.h | 3 ++- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/session.c b/src/session.c index f5d1cc85..83f3a8aa 100644 --- a/src/session.c +++ b/src/session.c @@ -159,12 +159,11 @@ nc_is_pk_subject_public_key_info(const char *b64) { int ret = 0; long len; - char *bin = NULL, *tmp; + unsigned char *bin = NULL, *tmp; /* decode base64 */ len = nc_base64_decode_wrap(b64, &bin); if (len == -1) { - ERR(NULL, "Decoding base64 public key to binary failed."); ret = -1; goto cleanup; } @@ -173,7 +172,7 @@ nc_is_pk_subject_public_key_info(const char *b64) tmp = bin; /* try to parse the supposed SubjectPublicKeyInfo binary data */ - if (!nc_der_to_pubkey_wrap((const unsigned char *)tmp, len)) { + if (nc_tls_is_der_subpubkey_wrap(tmp, len)) { /* success, it's most likely SubjectPublicKeyInfo */ ret = 1; } else { @@ -803,8 +802,11 @@ nc_session_free_transport(struct nc_session *session, int *multisession) } nc_tls_ctx_destroy_wrap(&session->ti.tls.ctx); + memset(&session->ti.tls.ctx, 0, sizeof session->ti.tls.ctx); nc_tls_session_destroy_wrap(session->ti.tls.session); + session->ti.tls.session = NULL; nc_tls_config_destroy_wrap(session->ti.tls.config); + session->ti.tls.config = NULL; if (session->side == NC_SERVER) { // TODO diff --git a/src/session_p.h b/src/session_p.h index 16c452b7..f8ed2635 100644 --- a/src/session_p.h +++ b/src/session_p.h @@ -279,7 +279,8 @@ struct nc_server_tls_opts { char *referenced_endpt_name; /**< Reference to another endpoint (used for client authentication). */ unsigned int tls_versions; /**< TLS versions */ - char *ciphers; /**< TLS ciphers */ + void *ciphers; /**< TLS ciphers */ + uint16_t cipher_count; /**< Number of TLS ciphers */ struct nc_ctn *ctn; /**< Cert-to-name entries */ }; From 5751a1f1b83a3199d9161db9e63890c38544f749 Mon Sep 17 00:00:00 2001 From: roman Date: Tue, 23 Apr 2024 15:13:17 +0200 Subject: [PATCH 29/54] server config util UPDATE reflect wrapper changes --- src/server_config_util.c | 45 +++++++++++++++++++++++++--------------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/src/server_config_util.c b/src/server_config_util.c index d7c627c4..9084b4f0 100644 --- a/src/server_config_util.c +++ b/src/server_config_util.c @@ -219,7 +219,7 @@ nc_server_config_util_privkey_format_to_identityref(NC_PRIVKEY_FORMAT format) } static int -nc_server_config_util_bn_to_bin(void *bn, unsigned char **bin, int *bin_len) +nc_server_config_util_rsa_pubkey_param_to_bin(void *bn, unsigned char **bin, int *bin_len) { int ret = 0; unsigned char *bin_tmp = NULL; @@ -228,13 +228,11 @@ nc_server_config_util_bn_to_bin(void *bn, unsigned char **bin, int *bin_len) *bin = NULL; - /* prepare buffer for converting BN to binary */ - *bin_len = nc_tls_get_bn_num_bytes_wrap(bn); - bin_tmp = calloc(*bin_len, sizeof *bin_tmp); - NC_CHECK_ERRMEM_RET(!bin_tmp, 1); - /* convert to binary */ - nc_tls_bn_bn2bin_wrap(bn, bin_tmp); + if (nc_tls_mpi2bin_wrap(bn, &bin_tmp, bin_len)) { + ret = 1; + goto cleanup; + } /* if the highest bit in the MSB is set a byte with the value 0 has to be prepended */ if (bin_tmp[0] & 0x80) { @@ -259,7 +257,7 @@ static int nc_server_config_util_evp_pkey_to_ssh_pubkey(void *pkey, char **pubkey) { int ret = 0, e_len, n_len, p_len, bin_len; - void *e = NULL, *n = NULL; + void *e = NULL, *n = NULL, *p = NULL, *p_grp = NULL; unsigned char *e_bin = NULL, *n_bin = NULL, *p_bin = NULL, *bin = NULL, *bin_tmp; const char *algorithm_name, *curve_name; char *ec_group = NULL; @@ -278,7 +276,8 @@ nc_server_config_util_evp_pkey_to_ssh_pubkey(void *pkey, char **pubkey) } /* BIGNUM to bin */ - if (nc_server_config_util_bn_to_bin(e, &e_bin, &e_len) || nc_server_config_util_bn_to_bin(n, &n_bin, &n_len)) { + if (nc_server_config_util_rsa_pubkey_param_to_bin(e, &e_bin, &e_len) || + nc_server_config_util_rsa_pubkey_param_to_bin(n, &n_bin, &n_len)) { ret = 1; goto cleanup; } @@ -334,13 +333,21 @@ nc_server_config_util_evp_pkey_to_ssh_pubkey(void *pkey, char **pubkey) } /* get the public key - p, which is a point on the elliptic curve */ - ret = nc_tls_get_ec_pubkey_param_wrap(pkey, &p_bin, &p_len); + ret = nc_tls_get_ec_pubkey_params_wrap(pkey, &p, &p_grp); if (ret) { ERR(NULL, "Getting public key point from the EC private key failed."); ret = 1; goto cleanup; } + /* EC point to bin */ + ret = nc_tls_ec_point_to_bin_wrap(p, p_grp, &p_bin, &p_len); + if (ret) { + ERR(NULL, "Converting EC public key point to binary failed."); + ret = 1; + goto cleanup; + } + alg_name_len = strlen(algorithm_name); curve_name_len = strlen(curve_name); /* buffer for public key in binary, which looks like so: @@ -382,6 +389,10 @@ nc_server_config_util_evp_pkey_to_ssh_pubkey(void *pkey, char **pubkey) } cleanup: + nc_tls_destroy_mpi_wrap(e); + nc_tls_destroy_mpi_wrap(n); + nc_tls_ec_point_destroy_wrap(p); + nc_tls_ec_group_destroy_wrap(p_grp); free(bin); free(e_bin); free(n_bin); @@ -399,7 +410,7 @@ nc_server_config_util_evp_pkey_to_spki_pubkey(void *pkey, char **pubkey) NC_CHECK_ARG_RET(NULL, pkey, pubkey, 1); - pub_pem = nc_tls_export_pubkey_wrap(pkey); + pub_pem = nc_tls_export_pubkey_pem_wrap(pkey); if (!pub_pem) { ret = 1; goto cleanup; @@ -429,7 +440,7 @@ nc_server_config_util_read_certificate(const char *cert_path, char **cert) return 1; } - pem = nc_tls_export_cert_wrap(crt); + pem = nc_tls_export_cert_pem_wrap(crt); if (!pem) { ret = 1; goto cleanup; @@ -662,21 +673,21 @@ nc_server_config_util_privkey_header_to_format(FILE *f_privkey, const char *priv } static int -nc_server_config_util_get_privkey_openssl(const char *privkey_path, FILE *f_privkey, char **privkey, void **pkey) +nc_server_config_util_get_privkey_openssl(const char *privkey_path, char **privkey, void **pkey) { void *pkey_tmp; char *privkey_tmp; - NC_CHECK_ARG_RET(NULL, privkey_path, f_privkey, privkey, pkey, 1); + NC_CHECK_ARG_RET(NULL, privkey_path, privkey, pkey, 1); *privkey = *pkey = NULL; - pkey_tmp = nc_tls_import_key_file_wrap(privkey_path, f_privkey); + pkey_tmp = nc_tls_import_privkey_file_wrap(privkey_path); if (!pkey_tmp) { return 1; } - privkey_tmp = nc_tls_export_key_wrap(pkey_tmp); + privkey_tmp = nc_tls_export_privkey_pem_wrap(pkey_tmp); if (!privkey_tmp) { nc_tls_privkey_destroy_wrap(pkey_tmp); return 1; @@ -787,7 +798,7 @@ nc_server_config_util_get_privkey(const char *privkey_path, NC_PRIVKEY_FORMAT *p case NC_PRIVKEY_FORMAT_EC: case NC_PRIVKEY_FORMAT_X509: /* OpenSSL solely can do this */ - ret = nc_server_config_util_get_privkey_openssl(privkey_path, f_privkey, &priv, pkey); + ret = nc_server_config_util_get_privkey_openssl(privkey_path, &priv, pkey); break; case NC_PRIVKEY_FORMAT_OPENSSH: /* need the help of libssh */ From 4ce1d4607bb6a53169781b29717193ce90ab700d Mon Sep 17 00:00:00 2001 From: roman Date: Tue, 23 Apr 2024 15:13:41 +0200 Subject: [PATCH 30/54] test crl UPDATE adjust seeked message --- tests/test_crl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_crl.c b/tests/test_crl.c index a2896fc6..44eb2d36 100644 --- a/tests/test_crl.c +++ b/tests/test_crl.c @@ -60,7 +60,7 @@ server_thread(void *arg) /* set print clb so we get access to messages */ nc_set_print_clb_session(test_msg_callback); buffer[0] = '\0'; - strcpy(expected, "revoked per CRL"); + strcpy(expected, "revoked"); /* accept a session and add it to the poll session structure */ pthread_barrier_wait(&state->barrier); From 2d7726863ab8471cfdd0db63c88fa89983d96673 Mon Sep 17 00:00:00 2001 From: roman Date: Thu, 25 Apr 2024 14:56:35 +0200 Subject: [PATCH 31/54] session server tls UPDATE return instead of goto --- src/session_server_tls.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/session_server_tls.c b/src/session_server_tls.c index 8e00b122..294d150d 100644 --- a/src/session_server_tls.c +++ b/src/session_server_tls.c @@ -689,8 +689,7 @@ nc_server_tls_load_server_cert_key(struct nc_server_tls_opts *opts, void **srv_c } if (!cert_data || !privkey_data) { ERR(NULL, "Server certificate not configured."); - ret = -1; - goto cleanup; + return 1; } cert = nc_base64der_to_cert(cert_data); From 5052b81c0f04d5c161e7369dc24e72d1edc65ebe Mon Sep 17 00:00:00 2001 From: roman Date: Thu, 25 Apr 2024 14:57:18 +0200 Subject: [PATCH 32/54] tests UPDATE use new transport names --- tests/test_auth.c | 2 +- tests/test_authkeys.c | 2 +- tests/test_ch.c | 4 ++-- tests/test_config_new.c | 2 +- tests/test_crl.c | 2 +- tests/test_ec.c | 2 +- tests/test_ed25519.c | 2 +- tests/test_endpt_share_clients.c | 8 ++++---- tests/test_ks_ts.c | 8 ++++---- tests/test_pam.c | 2 +- tests/test_replace.c | 4 ++-- tests/test_runtime_changes.c | 6 +++--- tests/test_tls.c | 2 +- tests/test_two_channels.c | 2 +- 14 files changed, 24 insertions(+), 24 deletions(-) diff --git a/tests/test_auth.c b/tests/test_auth.c index e58e192f..ae23959b 100644 --- a/tests/test_auth.c +++ b/tests/test_auth.c @@ -248,7 +248,7 @@ setup_f(void **state) ret = nc_server_config_load_modules(&ctx); assert_int_equal(ret, 0); - ret = nc_server_config_add_address_port(ctx, "endpt", NC_TI_LIBSSH, "127.0.0.1", TEST_PORT, &tree); + ret = nc_server_config_add_address_port(ctx, "endpt", NC_TI_SSH, "127.0.0.1", TEST_PORT, &tree); assert_int_equal(ret, 0); ret = nc_server_config_add_ssh_hostkey(ctx, "endpt", "hostkey", TESTS_DIR "/data/key_ecdsa", NULL, &tree); diff --git a/tests/test_authkeys.c b/tests/test_authkeys.c index 19f5c851..d3d258fb 100644 --- a/tests/test_authkeys.c +++ b/tests/test_authkeys.c @@ -250,7 +250,7 @@ setup_f(void **state) ret = nc_server_config_add_ssh_hostkey(ctx, "endpt", "hostkey", TESTS_DIR "/data/server.key", NULL, &tree); assert_int_equal(ret, 0); - ret = nc_server_config_add_address_port(ctx, "endpt", NC_TI_LIBSSH, "127.0.0.1", TEST_PORT, &tree); + ret = nc_server_config_add_address_port(ctx, "endpt", NC_TI_SSH, "127.0.0.1", TEST_PORT, &tree); assert_int_equal(ret, 0); ret = nc_server_config_add_ssh_user_authkey(ctx, "endpt", "test", &tree); diff --git a/tests/test_ch.c b/tests/test_ch.c index 6714e0f3..d5b34968 100644 --- a/tests/test_ch.c +++ b/tests/test_ch.c @@ -198,7 +198,7 @@ setup_ssh(void **state) assert_int_equal(ret, 0); /* set call-home address and port */ - ret = nc_server_config_add_ch_address_port(ctx, "ch_ssh", "endpt", NC_TI_LIBSSH, "127.0.0.1", TEST_PORT_STR, &test_state->ssh_tree); + ret = nc_server_config_add_ch_address_port(ctx, "ch_ssh", "endpt", NC_TI_SSH, "127.0.0.1", TEST_PORT_STR, &test_state->ssh_tree); assert_int_equal(ret, 0); /* set connection type to persistent */ @@ -400,7 +400,7 @@ setup_tls(void **state) assert_int_equal(ret, 0); /* set call-home address and port */ - ret = nc_server_config_add_ch_address_port(ctx, "ch_tls", "endpt", NC_TI_OPENSSL, "127.0.0.1", TEST_PORT_2_STR, &test_state->tls_tree); + ret = nc_server_config_add_ch_address_port(ctx, "ch_tls", "endpt", NC_TI_TLS, "127.0.0.1", TEST_PORT_2_STR, &test_state->tls_tree); assert_int_equal(ret, 0); /* set call-home server certificate */ diff --git a/tests/test_config_new.c b/tests/test_config_new.c index cbba440f..e42b6f78 100644 --- a/tests/test_config_new.c +++ b/tests/test_config_new.c @@ -154,7 +154,7 @@ setup_f(void **state) assert_int_equal(ret, 0); /* create new address and port data */ - ret = nc_server_config_add_address_port(ctx, "endpt", NC_TI_LIBSSH, "127.0.0.1", TEST_PORT, &tree); + ret = nc_server_config_add_address_port(ctx, "endpt", NC_TI_SSH, "127.0.0.1", TEST_PORT, &tree); assert_int_equal(ret, 0); /* create the host-key algorithms data */ diff --git a/tests/test_crl.c b/tests/test_crl.c index 44eb2d36..a0ce5295 100644 --- a/tests/test_crl.c +++ b/tests/test_crl.c @@ -144,7 +144,7 @@ setup_f(void **state) assert_int_equal(ret, 0); /* create new address and port data */ - ret = nc_server_config_add_address_port(ctx, "endpt", NC_TI_OPENSSL, "127.0.0.1", TEST_PORT, &tree); + ret = nc_server_config_add_address_port(ctx, "endpt", NC_TI_TLS, "127.0.0.1", TEST_PORT, &tree); assert_int_equal(ret, 0); /* create new server certificate data */ diff --git a/tests/test_ec.c b/tests/test_ec.c index 3db8794c..a5b22bc8 100644 --- a/tests/test_ec.c +++ b/tests/test_ec.c @@ -216,7 +216,7 @@ setup_f(void **state) ret = nc_server_config_add_ssh_hostkey(ctx, "endpt", "hostkey", TESTS_DIR "/data/key_ecdsa", NULL, &tree); assert_int_equal(ret, 0); - ret = nc_server_config_add_address_port(ctx, "endpt", NC_TI_LIBSSH, "127.0.0.1", TEST_PORT, &tree); + ret = nc_server_config_add_address_port(ctx, "endpt", NC_TI_SSH, "127.0.0.1", TEST_PORT, &tree); assert_int_equal(ret, 0); ret = nc_server_config_add_ssh_user_pubkey(ctx, "endpt", "test_ec256", "pubkey", TESTS_DIR "/data/id_ecdsa256.pub", &tree); diff --git a/tests/test_ed25519.c b/tests/test_ed25519.c index ccdcebcd..61eb7bb7 100644 --- a/tests/test_ed25519.c +++ b/tests/test_ed25519.c @@ -153,7 +153,7 @@ setup_f(void **state) ret = nc_server_config_add_ssh_hostkey(ctx, "endpt", "hostkey", TESTS_DIR "/data/server.key", NULL, &tree); assert_int_equal(ret, 0); - ret = nc_server_config_add_address_port(ctx, "endpt", NC_TI_LIBSSH, "127.0.0.1", TEST_PORT, &tree); + ret = nc_server_config_add_address_port(ctx, "endpt", NC_TI_SSH, "127.0.0.1", TEST_PORT, &tree); assert_int_equal(ret, 0); ret = nc_server_config_add_ssh_user_pubkey(ctx, "endpt", "test_ed25519", "pubkey", TESTS_DIR "/data/id_ed25519.pub", &tree); diff --git a/tests/test_endpt_share_clients.c b/tests/test_endpt_share_clients.c index 8b58c455..dedb61e4 100644 --- a/tests/test_endpt_share_clients.c +++ b/tests/test_endpt_share_clients.c @@ -196,7 +196,7 @@ setup_ssh(void **state) ret = nc_server_config_add_ssh_hostkey(ctx, "SSH_endpt_1", "hostkey", TESTS_DIR "/data/key_rsa", NULL, &tree); assert_int_equal(ret, 0); - ret = nc_server_config_add_address_port(ctx, "SSH_endpt_1", NC_TI_LIBSSH, "127.0.0.1", TEST_PORT, &tree); + ret = nc_server_config_add_address_port(ctx, "SSH_endpt_1", NC_TI_SSH, "127.0.0.1", TEST_PORT, &tree); assert_int_equal(ret, 0); ret = nc_server_config_add_ssh_endpoint_client_ref(ctx, "SSH_endpt_1", "SSH_endpt_2", &tree); @@ -206,7 +206,7 @@ setup_ssh(void **state) ret = nc_server_config_add_ssh_hostkey(ctx, "SSH_endpt_2", "hostkey", TESTS_DIR "/data/key_rsa", NULL, &tree); assert_int_equal(ret, 0); - ret = nc_server_config_add_address_port(ctx, "SSH_endpt_2", NC_TI_LIBSSH, "127.0.0.1", TEST_PORT_2, &tree); + ret = nc_server_config_add_address_port(ctx, "SSH_endpt_2", NC_TI_SSH, "127.0.0.1", TEST_PORT_2, &tree); assert_int_equal(ret, 0); ret = nc_server_config_add_ssh_user_pubkey(ctx, "SSH_endpt_2", "client", "pubkey", TESTS_DIR "/data/key_rsa.pub", &tree); @@ -263,7 +263,7 @@ setup_tls(void **state) ret = nc_server_config_add_tls_server_cert(ctx, "TLS_endpt_1", TESTS_DIR "/data/server.key", NULL, TESTS_DIR "/data/server.crt", &tree); assert_int_equal(ret, 0); - ret = nc_server_config_add_address_port(ctx, "TLS_endpt_1", NC_TI_OPENSSL, "127.0.0.1", TEST_PORT_3, &tree); + ret = nc_server_config_add_address_port(ctx, "TLS_endpt_1", NC_TI_TLS, "127.0.0.1", TEST_PORT_3, &tree); assert_int_equal(ret, 0); ret = nc_server_config_add_tls_client_cert(ctx, "TLS_endpt_1", "cert_client", TESTS_DIR "/data/client.crt", &tree); @@ -282,7 +282,7 @@ setup_tls(void **state) TESTS_DIR "/data/server.key", NULL, TESTS_DIR "/data/server.crt", &tree); assert_int_equal(ret, 0); - ret = nc_server_config_add_address_port(ctx, "TLS_endpt_2", NC_TI_OPENSSL, "127.0.0.1", TEST_PORT_4, &tree); + ret = nc_server_config_add_address_port(ctx, "TLS_endpt_2", NC_TI_TLS, "127.0.0.1", TEST_PORT_4, &tree); assert_int_equal(ret, 0); ret = nc_server_config_add_tls_endpoint_client_ref(ctx, "TLS_endpt_2", "TLS_endpt_1", &tree); diff --git a/tests/test_ks_ts.c b/tests/test_ks_ts.c index 87adbc28..5ce6fb14 100644 --- a/tests/test_ks_ts.c +++ b/tests/test_ks_ts.c @@ -138,7 +138,7 @@ setup_ssh(void **state) ret = nc_server_config_load_modules(&ctx); assert_int_equal(ret, 0); - ret = nc_server_config_add_address_port(ctx, "endpt", NC_TI_LIBSSH, "127.0.0.1", TEST_PORT, &tree); + ret = nc_server_config_add_address_port(ctx, "endpt", NC_TI_SSH, "127.0.0.1", TEST_PORT, &tree); assert_int_equal(ret, 0); ret = nc_server_config_add_ssh_keystore_ref(ctx, "endpt", "hostkey", "test_keystore", &tree); @@ -147,7 +147,7 @@ setup_ssh(void **state) ret = nc_server_config_add_ssh_truststore_ref(ctx, "endpt", "client", "test_truststore", &tree); assert_int_equal(ret, 0); - ret = nc_server_config_add_keystore_asym_key(ctx, NC_TI_LIBSSH, "test_keystore", TESTS_DIR "/data/key_rsa", NULL, &tree); + ret = nc_server_config_add_keystore_asym_key(ctx, NC_TI_SSH, "test_keystore", TESTS_DIR "/data/key_rsa", NULL, &tree); assert_int_equal(ret, 0); ret = nc_server_config_add_truststore_pubkey(ctx, "test_truststore", "pubkey", TESTS_DIR "/data/id_ed25519.pub", &tree); @@ -244,11 +244,11 @@ setup_tls(void **state) assert_int_equal(ret, 0); /* new tls bind */ - ret = nc_server_config_add_address_port(ctx, "endpt", NC_TI_OPENSSL, "127.0.0.1", TEST_PORT, &tree); + ret = nc_server_config_add_address_port(ctx, "endpt", NC_TI_TLS, "127.0.0.1", TEST_PORT, &tree); assert_int_equal(ret, 0); /* new keystore asym key pair */ - ret = nc_server_config_add_keystore_asym_key(ctx, NC_TI_OPENSSL, "server_key", TESTS_DIR "/data/server.key", NULL, &tree); + ret = nc_server_config_add_keystore_asym_key(ctx, NC_TI_TLS, "server_key", TESTS_DIR "/data/server.key", NULL, &tree); assert_int_equal(ret, 0); /* new keystore cert belonging to the key pair */ diff --git a/tests/test_pam.c b/tests/test_pam.c index b8a3030a..561dfea5 100644 --- a/tests/test_pam.c +++ b/tests/test_pam.c @@ -162,7 +162,7 @@ setup_f(void **state) ret = nc_server_config_load_modules(&ctx); assert_int_equal(ret, 0); - ret = nc_server_config_add_address_port(ctx, "endpt", NC_TI_LIBSSH, "127.0.0.1", TEST_PORT, &tree); + ret = nc_server_config_add_address_port(ctx, "endpt", NC_TI_SSH, "127.0.0.1", TEST_PORT, &tree); assert_int_equal(ret, 0); ret = nc_server_config_add_ssh_hostkey(ctx, "endpt", "hostkey", TESTS_DIR "/data/key_ecdsa", NULL, &tree); diff --git a/tests/test_replace.c b/tests/test_replace.c index f455537c..02654550 100644 --- a/tests/test_replace.c +++ b/tests/test_replace.c @@ -233,7 +233,7 @@ setup_f(void **state) ret = nc_server_config_load_modules(&ctx); assert_int_equal(ret, 0); - ret = nc_server_config_add_address_port(ctx, "old", NC_TI_LIBSSH, "127.0.0.1", TEST_PORT, &old_tree); + ret = nc_server_config_add_address_port(ctx, "old", NC_TI_SSH, "127.0.0.1", TEST_PORT, &old_tree); assert_int_equal(ret, 0); ret = nc_server_config_add_ssh_hostkey(ctx, "old", "old_key", TESTS_DIR "/data/key_rsa", NULL, &old_tree); @@ -246,7 +246,7 @@ setup_f(void **state) ret = nc_server_config_setup_data(old_tree); assert_int_equal(ret, 0); - ret = nc_server_config_add_address_port(ctx, "new", NC_TI_LIBSSH, "127.0.0.1", TEST_PORT, &new_tree); + ret = nc_server_config_add_address_port(ctx, "new", NC_TI_SSH, "127.0.0.1", TEST_PORT, &new_tree); assert_int_equal(ret, 0); ret = nc_server_config_add_ssh_hostkey(ctx, "new", "new_key", TESTS_DIR "/data/key_rsa", NULL, &new_tree); diff --git a/tests/test_runtime_changes.c b/tests/test_runtime_changes.c index 2f10b69d..cb8c3b1b 100644 --- a/tests/test_runtime_changes.c +++ b/tests/test_runtime_changes.c @@ -320,7 +320,7 @@ test_nc_change_ssh_hostkey(void **state) assert_int_equal(ret, 0); configure(test_state, NC_TEST_EXPECT_OK, NC_TEST_STATE_RUN); - ret = nc_server_config_add_keystore_asym_key(ctx, NC_TI_LIBSSH, "keystore_hostkey", TESTS_DIR "/data/key_rsa", TESTS_DIR "/data/key_rsa.pub", &test_state->tree); + ret = nc_server_config_add_keystore_asym_key(ctx, NC_TI_SSH, "keystore_hostkey", TESTS_DIR "/data/key_rsa", TESTS_DIR "/data/key_rsa.pub", &test_state->tree); assert_int_equal(ret, 0); ret = nc_server_config_add_ssh_keystore_ref(ctx, "endpt_ssh", "hostkey", "keystore_hostkey", &test_state->tree); assert_int_equal(ret, 0); @@ -385,7 +385,7 @@ setup_f(void **state) assert_int_equal(ret, 0); /* create new address and port data */ - ret = nc_server_config_add_address_port(ctx, "endpt_tls", NC_TI_OPENSSL, "127.0.0.1", TEST_PORT, &test_state->tree); + ret = nc_server_config_add_address_port(ctx, "endpt_tls", NC_TI_TLS, "127.0.0.1", TEST_PORT, &test_state->tree); assert_int_equal(ret, 0); /* create new server certificate data */ @@ -403,7 +403,7 @@ setup_f(void **state) assert_int_equal(ret, 0); /* create new address and port data */ - ret = nc_server_config_add_address_port(ctx, "endpt_ssh", NC_TI_LIBSSH, "127.0.0.1", TEST_PORT_2, &test_state->tree); + ret = nc_server_config_add_address_port(ctx, "endpt_ssh", NC_TI_SSH, "127.0.0.1", TEST_PORT_2, &test_state->tree); assert_int_equal(ret, 0); /* create new hostkey data */ diff --git a/tests/test_tls.c b/tests/test_tls.c index b02bc30a..138bc806 100644 --- a/tests/test_tls.c +++ b/tests/test_tls.c @@ -138,7 +138,7 @@ setup_f(void **state) assert_int_equal(ret, 0); /* create new address and port data */ - ret = nc_server_config_add_address_port(ctx, "endpt", NC_TI_OPENSSL, "127.0.0.1", TEST_PORT, &tree); + ret = nc_server_config_add_address_port(ctx, "endpt", NC_TI_TLS, "127.0.0.1", TEST_PORT, &tree); assert_int_equal(ret, 0); /* create new server certificate data */ diff --git a/tests/test_two_channels.c b/tests/test_two_channels.c index 490e90ed..91498673 100644 --- a/tests/test_two_channels.c +++ b/tests/test_two_channels.c @@ -154,7 +154,7 @@ setup_f(void **state) ret = nc_server_config_load_modules(&ctx); assert_int_equal(ret, 0); - ret = nc_server_config_add_address_port(ctx, "endpt", NC_TI_LIBSSH, "127.0.0.1", TEST_PORT, &tree); + ret = nc_server_config_add_address_port(ctx, "endpt", NC_TI_SSH, "127.0.0.1", TEST_PORT, &tree); assert_int_equal(ret, 0); ret = nc_server_config_add_ssh_hostkey(ctx, "endpt", "hostkey", TESTS_DIR "/data/key_ecdsa", NULL, &tree); From 3da5d9648abc48d7e80b1a1eb2ef96e4ee00eb57 Mon Sep 17 00:00:00 2001 From: roman Date: Thu, 25 Apr 2024 15:07:00 +0200 Subject: [PATCH 33/54] session server tls BUGFIX fix spaces in key type --- src/session_server_tls.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/session_server_tls.c b/src/session_server_tls.c index 294d150d..1db33f70 100644 --- a/src/session_server_tls.c +++ b/src/session_server_tls.c @@ -125,8 +125,8 @@ nc_base64der_to_privkey(const char *in, const char *key_str) NC_CHECK_ARG_RET(NULL, in, NULL); - if (asprintf(&buf, "%s%s%s%s%s%s%s", "-----BEGIN ", key_str, " PRIVATE KEY-----\n", in, "\n-----END ", - key_str, " PRIVATE KEY-----") == -1) { + if (asprintf(&buf, "%s%s%s%s%s%s%s", "-----BEGIN", key_str, "PRIVATE KEY-----\n", in, "\n-----END", + key_str, "PRIVATE KEY-----") == -1) { ERRMEM; return NULL; } From b10b62a9842d3bb53d442c56c9251388a15d012e Mon Sep 17 00:00:00 2001 From: roman Date: Thu, 25 Apr 2024 15:08:13 +0200 Subject: [PATCH 34/54] session mbedtls BUGFIX fix return NULL on fail --- src/session_mbedtls.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/session_mbedtls.c b/src/session_mbedtls.c index 49eb8c14..e11c4e80 100644 --- a/src/session_mbedtls.c +++ b/src/session_mbedtls.c @@ -316,32 +316,32 @@ nc_tls_add_cert_to_store_wrap(void *cert, void *cert_store) void * nc_tls_pem_to_privkey_wrap(const char *privkey_data) { - int rc, ret = 0; + int rc = 0; mbedtls_pk_context *pkey = NULL; mbedtls_ctr_drbg_context *ctr_drbg = NULL; mbedtls_entropy_context *entropy = NULL; - ret = nc_tls_rng_new(&ctr_drbg, &entropy); - if (ret) { + rc = nc_tls_rng_new(&ctr_drbg, &entropy); + if (rc) { goto cleanup; } pkey = nc_tls_pkey_new_wrap(); if (!pkey) { - ret = 1; + rc = 1; goto cleanup; } rc = mbedtls_pk_parse_key(pkey, (const unsigned char *)privkey_data, strlen(privkey_data) + 1, NULL, 0, mbedtls_ctr_drbg_random, ctr_drbg); if (rc) { ERR(NULL, "Parsing private key data failed (%s).", nc_get_mbedtls_str_err(rc)); - ret = 1; goto cleanup; } cleanup: - if (ret) { + if (rc) { nc_tls_privkey_destroy_wrap(pkey); + pkey = NULL; } nc_tls_rng_destroy(ctr_drbg, entropy); return pkey; From c300f47766bb63fc363008c1e082f9bfbe43e5df Mon Sep 17 00:00:00 2001 From: roman Date: Thu, 25 Apr 2024 16:10:46 +0200 Subject: [PATCH 35/54] session wrapper UPDATE add includes --- src/session_wrapper.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/session_wrapper.h b/src/session_wrapper.h index 43c5d74f..a1b5414b 100644 --- a/src/session_wrapper.h +++ b/src/session_wrapper.h @@ -8,7 +8,11 @@ #ifdef HAVE_LIBMBEDTLS #include +#include +#include #include +#include +#include struct nc_tls_ctx { int *sock; @@ -22,7 +26,9 @@ struct nc_tls_ctx { #else +#include #include +#include struct nc_tls_ctx { X509 *cert; From e6ab5b89d46b0cb97053a96bf444843339587142 Mon Sep 17 00:00:00 2001 From: roman Date: Fri, 26 Apr 2024 16:13:17 +0200 Subject: [PATCH 36/54] server config util UPDATE key format handling --- src/server_config_util.c | 83 +++++++++++++++++++++------------------- 1 file changed, 44 insertions(+), 39 deletions(-) diff --git a/src/server_config_util.c b/src/server_config_util.c index 9084b4f0..f94cd740 100644 --- a/src/server_config_util.c +++ b/src/server_config_util.c @@ -254,7 +254,7 @@ nc_server_config_util_rsa_pubkey_param_to_bin(void *bn, unsigned char **bin, int /* ssh pubkey defined in RFC 4253 section 6.6 */ static int -nc_server_config_util_evp_pkey_to_ssh_pubkey(void *pkey, char **pubkey) +nc_server_config_util_pkey_to_ssh_pubkey(void *pkey, char **pubkey) { int ret = 0, e_len, n_len, p_len, bin_len; void *e = NULL, *n = NULL, *p = NULL, *p_grp = NULL; @@ -403,7 +403,7 @@ nc_server_config_util_evp_pkey_to_ssh_pubkey(void *pkey, char **pubkey) /* spki = subject public key info */ static int -nc_server_config_util_evp_pkey_to_spki_pubkey(void *pkey, char **pubkey) +nc_server_config_util_pkey_to_spki_pubkey(void *pkey, char **pubkey) { int ret = 0; char *pub_pem = NULL; @@ -458,7 +458,7 @@ nc_server_config_util_read_certificate(const char *cert_path, char **cert) } static int -nc_server_config_util_read_pubkey_ssh2(const char *pubkey_path, char **pubkey) +nc_server_config_util_read_ssh2_pubkey(const char *pubkey_path, char **pubkey) { char *buffer = NULL; size_t size = 0, pubkey_len = 0; @@ -518,7 +518,7 @@ nc_server_config_util_read_pubkey_ssh2(const char *pubkey_path, char **pubkey) } static int -nc_server_config_util_read_pubkey_openssl(const char *pubkey_path, char **pubkey) +nc_server_config_util_read_spki_pubkey(const char *pubkey_path, char **pubkey) { int ret = 0; void *pub_pkey = NULL; @@ -531,13 +531,13 @@ nc_server_config_util_read_pubkey_openssl(const char *pubkey_path, char **pubkey return 1; } - ret = nc_server_config_util_evp_pkey_to_ssh_pubkey(pub_pkey, pubkey); + ret = nc_server_config_util_pkey_to_ssh_pubkey(pub_pkey, pubkey); nc_tls_privkey_destroy_wrap(pub_pkey); return ret; } static int -nc_server_config_util_read_pubkey_libssh(const char *pubkey_path, char **pubkey) +nc_server_config_util_read_openssh_pubkey(const char *pubkey_path, char **pubkey) { int ret = 0; ssh_key pub_sshkey = NULL; @@ -591,13 +591,13 @@ nc_server_config_util_get_ssh_pubkey_file(const char *pubkey_path, char **pubkey if (!strncmp(header, NC_SUBJECT_PUBKEY_INFO_HEADER, strlen(NC_SUBJECT_PUBKEY_INFO_HEADER))) { /* it's subject public key info public key */ - ret = nc_server_config_util_read_pubkey_openssl(pubkey_path, pubkey); + ret = nc_server_config_util_read_spki_pubkey(pubkey_path, pubkey); } else if (!strncmp(header, NC_SSH2_PUBKEY_HEADER, strlen(NC_SSH2_PUBKEY_HEADER))) { /* it's ssh2 public key */ - ret = nc_server_config_util_read_pubkey_ssh2(pubkey_path, pubkey); + ret = nc_server_config_util_read_ssh2_pubkey(pubkey_path, pubkey); } else { /* it's probably OpenSSH public key */ - ret = nc_server_config_util_read_pubkey_libssh(pubkey_path, pubkey); + ret = nc_server_config_util_read_openssh_pubkey(pubkey_path, pubkey); } if (ret) { ERR(NULL, "Error getting public key from file \"%s\".", pubkey_path); @@ -624,7 +624,7 @@ nc_server_config_util_get_spki_pubkey_file(const char *pubkey_path, char **pubke return 1; } - ret = nc_server_config_util_evp_pkey_to_spki_pubkey(pkey, pubkey); + ret = nc_server_config_util_pkey_to_spki_pubkey(pkey, pubkey); if (ret) { goto cleanup; } @@ -635,45 +635,32 @@ nc_server_config_util_get_spki_pubkey_file(const char *pubkey_path, char **pubke } static int -nc_server_config_util_privkey_header_to_format(FILE *f_privkey, const char *privkey_path, NC_PRIVKEY_FORMAT *privkey_format) +nc_server_config_util_get_privkey_format(const char *privkey, NC_PRIVKEY_FORMAT *privkey_format) { - char *privkey_header = NULL; - size_t len = 0; - - NC_CHECK_ARG_RET(NULL, f_privkey, privkey_path, privkey_format, 1); + NC_CHECK_ARG_RET(NULL, privkey, privkey_format, 1); - /* read header */ - if (getline(&privkey_header, &len, f_privkey) < 0) { - ERR(NULL, "Error reading header from file \"%s\".", privkey_path); - return 1; - } - - if (!strncmp(privkey_header, NC_PKCS8_PRIVKEY_HEADER, strlen(NC_PKCS8_PRIVKEY_HEADER))) { + if (!strncmp(privkey, NC_PKCS8_PRIVKEY_HEADER, strlen(NC_PKCS8_PRIVKEY_HEADER))) { /* it's PKCS8 (X.509) private key */ *privkey_format = NC_PRIVKEY_FORMAT_X509; - } else if (!strncmp(privkey_header, NC_OPENSSH_PRIVKEY_HEADER, strlen(NC_OPENSSH_PRIVKEY_HEADER))) { + } else if (!strncmp(privkey, NC_OPENSSH_PRIVKEY_HEADER, strlen(NC_OPENSSH_PRIVKEY_HEADER))) { /* it's OpenSSH private key */ *privkey_format = NC_PRIVKEY_FORMAT_OPENSSH; - } else if (!strncmp(privkey_header, NC_PKCS1_RSA_PRIVKEY_HEADER, strlen(NC_PKCS1_RSA_PRIVKEY_HEADER))) { + } else if (!strncmp(privkey, NC_PKCS1_RSA_PRIVKEY_HEADER, strlen(NC_PKCS1_RSA_PRIVKEY_HEADER))) { /* it's RSA privkey in PKCS1 format */ *privkey_format = NC_PRIVKEY_FORMAT_RSA; - } else if (!strncmp(privkey_header, NC_SEC1_EC_PRIVKEY_HEADER, strlen(NC_SEC1_EC_PRIVKEY_HEADER))) { + } else if (!strncmp(privkey, NC_SEC1_EC_PRIVKEY_HEADER, strlen(NC_SEC1_EC_PRIVKEY_HEADER))) { /* it's EC privkey in SEC1 format */ *privkey_format = NC_PRIVKEY_FORMAT_EC; } else { - ERR(NULL, "Private key format (%s) not supported.", privkey_header); - free(privkey_header); + /* not supported */ return 1; } - /* reset the reading head */ - rewind(f_privkey); - free(privkey_header); return 0; } static int -nc_server_config_util_get_privkey_openssl(const char *privkey_path, char **privkey, void **pkey) +nc_server_config_util_get_privkey_libtls(const char *privkey_path, char **privkey, void **pkey) { void *pkey_tmp; char *privkey_tmp; @@ -775,6 +762,8 @@ nc_server_config_util_get_privkey(const char *privkey_path, NC_PRIVKEY_FORMAT *p int ret = 0; FILE *f_privkey = NULL; char *priv = NULL; + char *privkey_header = NULL; + size_t header_len = 0; NC_CHECK_ARG_RET(NULL, privkey_path, privkey_format, privkey, pkey, 1); @@ -785,20 +774,28 @@ nc_server_config_util_get_privkey(const char *privkey_path, NC_PRIVKEY_FORMAT *p goto cleanup; } - /* read the first line from the privkey to determine it's type */ - ret = nc_server_config_util_privkey_header_to_format(f_privkey, privkey_path, privkey_format); + /* read privkey header */ + if (getline(&privkey_header, &header_len, f_privkey) < 0) { + ERR(NULL, "Error reading header from file \"%s\".", privkey_path); + ret = 1; + goto cleanup; + } + + /* get privkey format */ + ret = nc_server_config_util_get_privkey_format(privkey_header, privkey_format); if (ret) { - ERR(NULL, "Getting private key format from file \"%s\" failed.", privkey_path); + ERR(NULL, "Private key format \"%s\" not supported.", privkey_header); goto cleanup; } + /* decide how to parse it based on the format */ switch (*privkey_format) { /* fall-through */ case NC_PRIVKEY_FORMAT_RSA: case NC_PRIVKEY_FORMAT_EC: case NC_PRIVKEY_FORMAT_X509: - /* OpenSSL solely can do this */ - ret = nc_server_config_util_get_privkey_openssl(privkey_path, &priv, pkey); + /* the TLS lib can do this */ + ret = nc_server_config_util_get_privkey_libtls(privkey_path, &priv, pkey); break; case NC_PRIVKEY_FORMAT_OPENSSH: /* need the help of libssh */ @@ -815,10 +812,17 @@ nc_server_config_util_get_privkey(const char *privkey_path, NC_PRIVKEY_FORMAT *p goto cleanup; } + /* parsing may have changed its type, get it again */ + ret = nc_server_config_util_get_privkey_format(priv, privkey_format); + if (ret) { + ERR(NULL, "Getting private key format from file \"%s\" failed.", privkey_path); + goto cleanup; + } + /* strip private key's header and footer */ ret = nc_server_config_util_pem_strip_header_footer(priv, privkey); if (ret) { - ERR(NULL, "Stripping header and footer from private key failed."); + ERR(NULL, "Stripping header and footer from private key \"%s\" failed.", privkey_path); goto cleanup; } @@ -827,6 +831,7 @@ nc_server_config_util_get_privkey(const char *privkey_path, NC_PRIVKEY_FORMAT *p fclose(f_privkey); } + free(privkey_header); free(priv); return ret; } @@ -853,9 +858,9 @@ nc_server_config_util_get_asym_key_pair(const char *privkey_path, const char *pu /* get public key, either from file or generate it from the EVP_PKEY */ if (!pubkey_path) { if (wanted_pubkey_format == NC_PUBKEY_FORMAT_SSH) { - ret = nc_server_config_util_evp_pkey_to_ssh_pubkey(pkey, pubkey); + ret = nc_server_config_util_pkey_to_ssh_pubkey(pkey, pubkey); } else { - ret = nc_server_config_util_evp_pkey_to_spki_pubkey(pkey, pubkey); + ret = nc_server_config_util_pkey_to_spki_pubkey(pkey, pubkey); } } else { if (wanted_pubkey_format == NC_PUBKEY_FORMAT_SSH) { From 8726489320f763495045dc8851ff39e3e8f08047 Mon Sep 17 00:00:00 2001 From: roman Date: Mon, 13 May 2024 12:53:02 +0200 Subject: [PATCH 37/54] session wrapper UPDATE add file desc to new files --- src/session_mbedtls.c | 19 +++++++++++++++++++ src/session_openssl.c | 19 +++++++++++++++++++ src/session_wrapper.h | 15 +++++++++++++++ 3 files changed, 53 insertions(+) diff --git a/src/session_mbedtls.c b/src/session_mbedtls.c index e11c4e80..515dda1b 100644 --- a/src/session_mbedtls.c +++ b/src/session_mbedtls.c @@ -1,3 +1,22 @@ +/** + * @file session_mbedtls.c + * @author Roman Janota + * @brief libnetconf2 - wrapped MbedTLS function calls for TLS/asymmetric cryptography support + * + * This file is a wrapper for MbedTLS function calls. The implementation is done + * in such a way that the original libnetconf2 code is not dependent on MbedTLS. + * This file is included in the build process only if MbedTLS is being used. + * + * @copyright + * Copyright (c) 2024 CESNET, z.s.p.o. + * + * This source code is licensed under BSD 3-Clause License (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + */ + #define _GNU_SOURCE #include diff --git a/src/session_openssl.c b/src/session_openssl.c index d863c16b..b921b7f9 100644 --- a/src/session_openssl.c +++ b/src/session_openssl.c @@ -1,3 +1,22 @@ +/** + * @file session_openssl.c + * @author Roman Janota + * @brief libnetconf2 - wrapped OpenSSL function calls for TLS/asymmetric cryptography support + * + * This file is a wrapper for OpenSSL function calls. The implementation is done + * in such a way that the original libnetconf2 code is not dependent on OpenSSL. + * This file is included in the build process only if OpenSSL is being used. + * + * @copyright + * Copyright (c) 2024 CESNET, z.s.p.o. + * + * This source code is licensed under BSD 3-Clause License (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + */ + #define _GNU_SOURCE #include diff --git a/src/session_wrapper.h b/src/session_wrapper.h index a1b5414b..59dac4fb 100644 --- a/src/session_wrapper.h +++ b/src/session_wrapper.h @@ -1,3 +1,18 @@ +/** + * @file session_wrapper.h + * @author Roman Janota + * @brief libnetconf2 - header for wrapped TLS library function calls (currently OpenSSL and MbedTLS) + * + * @copyright + * Copyright (c) 2024 CESNET, z.s.p.o. + * + * This source code is licensed under BSD 3-Clause License (the "License"). + * You may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://opensource.org/licenses/BSD-3-Clause + */ + #ifndef _SESSION_WRAPPER_H_ #define _SESSION_WRAPPER_H_ From ef2c117de4e387ad2c741ec8eb70dc1ae99247a9 Mon Sep 17 00:00:00 2001 From: roman Date: Mon, 13 May 2024 12:53:37 +0200 Subject: [PATCH 38/54] session client tls UPDATE remove ctx assignment --- src/session_client_tls.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/session_client_tls.c b/src/session_client_tls.c index 4696735a..8fa63655 100644 --- a/src/session_client_tls.c +++ b/src/session_client_tls.c @@ -460,7 +460,6 @@ nc_connect_tls(const char *host, unsigned short port, struct ly_ctx *ctx) if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) { goto fail; } - ctx = session->ctx; /* NETCONF handshake */ if (nc_handshake_io(session) != NC_MSG_HELLO) { @@ -519,7 +518,6 @@ nc_accept_callhome_tls_sock(int sock, const char *host, uint16_t port, struct ly if (nc_client_session_new_ctx(session, ctx) != EXIT_SUCCESS) { goto fail; } - ctx = session->ctx; /* NETCONF handshake */ if (nc_handshake_io(session) != NC_MSG_HELLO) { From 927814762f247d638e9e1346e93bc18257c73f71 Mon Sep 17 00:00:00 2001 From: roman Date: Mon, 13 May 2024 12:57:08 +0200 Subject: [PATCH 39/54] session mbedtls UPDATE remove unused includes --- src/session_mbedtls.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/session_mbedtls.c b/src/session_mbedtls.c index 515dda1b..053eca14 100644 --- a/src/session_mbedtls.c +++ b/src/session_mbedtls.c @@ -42,7 +42,6 @@ #include #include #include -#include #include #include #include @@ -52,7 +51,6 @@ #include #include #include -#include /* some mbedTLS functions may return 'high' and some 'low' level errors, try to handle both cases this way */ static const char * From 206239d29c6ebdf6eeabae8a321f01858c4e1fa3 Mon Sep 17 00:00:00 2001 From: roman Date: Mon, 13 May 2024 12:58:00 +0200 Subject: [PATCH 40/54] session wrapper REFACTOR use void when no params --- src/session_mbedtls.c | 6 +++--- src/session_openssl.c | 6 +++--- src/session_wrapper.h | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/session_mbedtls.c b/src/session_mbedtls.c index 053eca14..bbf92573 100644 --- a/src/session_mbedtls.c +++ b/src/session_mbedtls.c @@ -224,7 +224,7 @@ nc_tls_config_destroy_wrap(void *tls_cfg) } void * -nc_tls_cert_new_wrap() +nc_tls_cert_new_wrap(void) { mbedtls_x509_crt *cert; @@ -262,7 +262,7 @@ nc_tls_privkey_destroy_wrap(void *pkey) } void * -nc_tls_cert_store_new_wrap() +nc_tls_cert_store_new_wrap(void) { /* certificate is the same as a certificate store in MbedTLS */ return nc_tls_cert_new_wrap(); @@ -276,7 +276,7 @@ nc_tls_cert_store_destroy_wrap(void *cert_store) } void * -nc_tls_crl_store_new_wrap() +nc_tls_crl_store_new_wrap(void) { mbedtls_x509_crl *crl; diff --git a/src/session_openssl.c b/src/session_openssl.c index b921b7f9..b0ed1c6f 100644 --- a/src/session_openssl.c +++ b/src/session_openssl.c @@ -90,7 +90,7 @@ nc_tls_config_destroy_wrap(void *tls_cfg) } void * -nc_tls_cert_new_wrap() +nc_tls_cert_new_wrap(void) { X509 *cert; @@ -113,7 +113,7 @@ nc_tls_privkey_destroy_wrap(void *pkey) } void * -nc_tls_cert_store_new_wrap() +nc_tls_cert_store_new_wrap(void) { X509_STORE *store; @@ -130,7 +130,7 @@ nc_tls_cert_store_destroy_wrap(void *cert_store) } void * -nc_tls_crl_store_new_wrap() +nc_tls_crl_store_new_wrap(void) { return nc_tls_cert_store_new_wrap(); } diff --git a/src/session_wrapper.h b/src/session_wrapper.h index 59dac4fb..170f4f1d 100644 --- a/src/session_wrapper.h +++ b/src/session_wrapper.h @@ -100,7 +100,7 @@ void nc_tls_config_destroy_wrap(void *tls_cfg); * * @return New TLS certificate on success, NULL on fail. */ -void * nc_tls_cert_new_wrap(); +void * nc_tls_cert_new_wrap(void); /** * @brief Destroys a TLS certificate. @@ -121,7 +121,7 @@ void nc_tls_privkey_destroy_wrap(void *pkey); * * @return New TLS certificate store on success, NULL on fail. */ -void * nc_tls_cert_store_new_wrap(); +void * nc_tls_cert_store_new_wrap(void); /** * @brief Destroys a TLS certificate store. @@ -135,7 +135,7 @@ void nc_tls_cert_store_destroy_wrap(void *cert_store); * * @return New CRL store on success, NULL on fail. */ -void * nc_tls_crl_store_new_wrap(); +void * nc_tls_crl_store_new_wrap(void); /** * @brief Destroys a CRL store. From fda7d7be20f6ecaed59d406ad462cfce3eb324ed Mon Sep 17 00:00:00 2001 From: roman Date: Mon, 13 May 2024 12:58:52 +0200 Subject: [PATCH 41/54] session mbedtls BUGFIX fix cert memory leak --- src/session_mbedtls.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/session_mbedtls.c b/src/session_mbedtls.c index bbf92573..9cbc5b47 100644 --- a/src/session_mbedtls.c +++ b/src/session_mbedtls.c @@ -868,9 +868,11 @@ nc_client_tls_load_cert_key_wrap(const char *cert_path, const char *key_path, vo } *cert = c; + c = NULL; *pkey = pk; cleanup: + nc_tls_cert_destroy_wrap(c); return ret; } From 1c4f415626ce79c70a0e55ad8c2351fef7c96554 Mon Sep 17 00:00:00 2001 From: roman Date: Mon, 13 May 2024 13:44:54 +0200 Subject: [PATCH 42/54] session mbedtls BUGFIX fix double & nonnull free --- src/session_mbedtls.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/session_mbedtls.c b/src/session_mbedtls.c index 9cbc5b47..28c10789 100644 --- a/src/session_mbedtls.c +++ b/src/session_mbedtls.c @@ -929,6 +929,7 @@ nc_client_tls_load_crl_wrap(void *crl_store, const char *file_path, const char * if (errno == ENOENT) { /* broken symbolic link, ignore */ free(path); + path = NULL; continue; } else { ERR(NULL, "Failed to get information about \"%s\" (%s).", path, strerror(errno)); @@ -940,6 +941,7 @@ nc_client_tls_load_crl_wrap(void *crl_store, const char *file_path, const char * if (!S_ISREG(st.st_mode)) { /* not a regular file, ignore */ free(path); + path = NULL; continue; } @@ -955,7 +957,9 @@ nc_client_tls_load_crl_wrap(void *crl_store, const char *file_path, const char * cleanup: free(path); - closedir(dir); + if (dir) { + closedir(dir); + } return ret; } From 2f9d7641528f29db82412539981160fafbc6c682 Mon Sep 17 00:00:00 2001 From: roman Date: Mon, 13 May 2024 13:47:39 +0200 Subject: [PATCH 43/54] session wrapper UPDATE add docs --- src/session_wrapper.h | 43 ++++++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/src/session_wrapper.h b/src/session_wrapper.h index 170f4f1d..785b33f7 100644 --- a/src/session_wrapper.h +++ b/src/session_wrapper.h @@ -29,14 +29,17 @@ #include #include +/** + * @brief Context from which a TLS session may be created. + */ struct nc_tls_ctx { - int *sock; - mbedtls_entropy_context *entropy; - mbedtls_ctr_drbg_context *ctr_drbg; - mbedtls_x509_crt *cert; - mbedtls_pk_context *pkey; - mbedtls_x509_crt *cert_store; - mbedtls_x509_crl *crl_store; + int *sock; /**< Socket FD. */ + mbedtls_entropy_context *entropy; /**< Entropy. */ + mbedtls_ctr_drbg_context *ctr_drbg; /**< Random bit generator. */ + mbedtls_x509_crt *cert; /**< Certificate. */ + mbedtls_pk_context *pkey; /**< Private key. */ + mbedtls_x509_crt *cert_store; /**< CA certificates store. */ + mbedtls_x509_crl *crl_store; /**< CRL store. */ }; #else @@ -45,23 +48,29 @@ struct nc_tls_ctx { #include #include +/** + * @brief Context from which a TLS session may be created. + */ struct nc_tls_ctx { - X509 *cert; - EVP_PKEY *pkey; - X509_STORE *cert_store; - X509_STORE *crl_store; + X509 *cert; /**< Certificate. */ + EVP_PKEY *pkey; /**< Private key. */ + X509_STORE *cert_store; /**< CA certificate store. */ + X509_STORE *crl_store; /**< CRL store. */ }; #endif +/** + * @brief Server side TLS verify callback data. + */ struct nc_tls_verify_cb_data { - struct nc_session *session; - struct nc_server_tls_opts *opts; + struct nc_session *session; /**< NETCONF session. */ + struct nc_server_tls_opts *opts; /**< TLS server options. */ struct nc_ctn_data { - char *username; - int matched_ctns; - int matched_ctn_type[6]; - int matched_ctn_count; + char *username; /**< Username. */ + int matched_ctns; /**< OR'd values of currently matched CTN types. */ + int matched_ctn_type[6]; /**< Currently matched CTN types (order matters). */ + int matched_ctn_count; /**< Number of matched CTN types. */ } ctn_data; }; From 8b3f57730d63a5c19b81831f0b19d057b9d15d3a Mon Sep 17 00:00:00 2001 From: roman Date: Mon, 13 May 2024 13:48:34 +0200 Subject: [PATCH 44/54] session wrapper UPDATE tls verify callback --- src/session_mbedtls.c | 6 +++--- src/session_openssl.c | 33 ++++++++++++++++++++++----------- src/session_server_tls.c | 11 ++++++++--- src/session_wrapper.h | 4 ++-- 4 files changed, 35 insertions(+), 19 deletions(-) diff --git a/src/session_mbedtls.c b/src/session_mbedtls.c index 28c10789..17b03a86 100644 --- a/src/session_mbedtls.c +++ b/src/session_mbedtls.c @@ -436,7 +436,7 @@ nc_server_tls_verify_cb(void *cb_data, mbedtls_x509_crt *cert, int depth, uint32 if (!*flags) { /* in-built verification was successful */ - ret = nc_server_tls_verify_cert(cert, depth, 0, data); + ret = nc_server_tls_verify_cert(cert, depth, 1, data); } else { /* in-built verification failed, but the client still may be authenticated if: * 1) the peer cert matches any configured end-entity cert @@ -445,13 +445,13 @@ nc_server_tls_verify_cb(void *cb_data, mbedtls_x509_crt *cert, int depth, uint32 */ if ((depth == 0) && (*flags == MBEDTLS_X509_BADCERT_NOT_TRUSTED)) { /* not trusted self-signed peer certificate, case 1) */ - ret = nc_server_tls_verify_cert(cert, depth, 1, data); + ret = nc_server_tls_verify_cert(cert, depth, 0, data); if (!ret) { *flags &= ~MBEDTLS_X509_BADCERT_NOT_TRUSTED; } } else if (*flags == MBEDTLS_X509_BADCERT_MISSING) { /* full chain of trust is invalid, but it may be valid partially, case 2) */ - ret = nc_server_tls_verify_cert(cert, depth, 1, data); + ret = nc_server_tls_verify_cert(cert, depth, 0, data); if (!ret) { *flags &= ~MBEDTLS_X509_BADCERT_MISSING; } diff --git a/src/session_openssl.c b/src/session_openssl.c index b0ed1c6f..3891dc7b 100644 --- a/src/session_openssl.c +++ b/src/session_openssl.c @@ -333,9 +333,17 @@ nc_server_tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) SSL_CTX *ctx; X509 *cert; - /* retrieve callback data stored in the SSL struct */ + /* retrieve callback data stored inside the SSL_CTX struct */ ssl = X509_STORE_CTX_get_ex_data(x509_ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); + if (!ssl) { + ERRINT; + return 0; + } ctx = SSL_get_SSL_CTX(ssl); + if (!ctx) { + ERRINT; + return 0; + } data = SSL_CTX_get_ex_data(ctx, 0); /* get current cert and its depth */ @@ -344,7 +352,7 @@ nc_server_tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) if (preverify_ok) { /* in-built verification was successful */ - ret = nc_server_tls_verify_cert(cert, depth, 0, data); + ret = nc_server_tls_verify_cert(cert, depth, 1, data); } else { /* in-built verification failed, but the client still may be authenticated if: * 1) the peer cert matches any configured end-entity cert @@ -352,9 +360,9 @@ nc_server_tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) * otherwise just continue until we reach the peer cert (depth = 0) */ err = X509_STORE_CTX_get_error(x509_ctx); - if ((depth == 0) && (err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT)) { - /* not trusted self-signed peer certificate, case 1) */ - ret = nc_server_tls_verify_cert(cert, depth, 1, data); + if ((depth == 0) && ((err == X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT) || (err == X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE))) { + /* not trusted (possibly self-signed) peer certificate, case 1) */ + ret = nc_server_tls_verify_cert(cert, depth, 0, data); } else if ((err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT) || (err == X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY)) { /* full chain of trust is invalid, but it may be valid partially, case 2) */ ret = nc_server_tls_verify_cert(cert, depth, 0, data); @@ -367,8 +375,6 @@ nc_server_tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) if (ret) { VRB(NULL, "Cert verify: fail (%s).", X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509_ctx))); ret = -1; - } else { - X509_STORE_CTX_set_error(x509_ctx, X509_V_OK); } } else { VRB(NULL, "Cert verify: fail (%s).", X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509_ctx))); @@ -681,7 +687,8 @@ int nc_client_tls_load_trusted_certs_wrap(void *cert_store, const char *file_path, const char *dir_path) { if (!X509_STORE_load_locations(cert_store, file_path, dir_path)) { - ERR(NULL, "Loading CA certs from file \"%s\" or directory \"%s\" failed (%s).", file_path, dir_path, ERR_reason_error_string(ERR_get_error())); + ERR(NULL, "Loading CA certs from file \"%s\" or directory \"%s\" failed (%s).", + file_path, dir_path, ERR_reason_error_string(ERR_get_error())); return 1; } @@ -692,7 +699,8 @@ int nc_client_tls_load_crl_wrap(void *crl_store, const char *file_path, const char *dir_path) { if (!X509_STORE_load_locations(crl_store, file_path, dir_path)) { - ERR(NULL, "Loading CRLs from file \"%s\" or directory \"%s\" failed (%s).", file_path, dir_path, ERR_reason_error_string(ERR_get_error())); + ERR(NULL, "Loading CRLs from file \"%s\" or directory \"%s\" failed (%s).", + file_path, dir_path, ERR_reason_error_string(ERR_get_error())); return 1; } @@ -762,7 +770,7 @@ nc_tls_move_crls_to_store(const X509_STORE *src, X509_STORE *dst) } int -nc_tls_setup_config_from_ctx_wrap(struct nc_tls_ctx *tls_ctx, int UNUSED(side), void *tls_cfg) +nc_tls_setup_config_from_ctx_wrap(struct nc_tls_ctx *tls_ctx, int side, void *tls_cfg) { if (SSL_CTX_use_certificate(tls_cfg, tls_ctx->cert) != 1) { return 1; @@ -772,7 +780,10 @@ nc_tls_setup_config_from_ctx_wrap(struct nc_tls_ctx *tls_ctx, int UNUSED(side), return 1; } - SSL_CTX_set_mode(tls_cfg, SSL_MODE_AUTO_RETRY); + /* disable server-side automatic chain building */ + if (side == NC_SERVER) { + SSL_CTX_set_mode(tls_cfg, SSL_MODE_NO_AUTO_CHAIN); + } if (tls_ctx->crl_store) { /* move CRLs from crl_store to cert_store, because SSL_CTX can only have one store */ diff --git a/src/session_server_tls.c b/src/session_server_tls.c index 1db33f70..4ac37361 100644 --- a/src/session_server_tls.c +++ b/src/session_server_tls.c @@ -386,7 +386,7 @@ nc_server_tls_cert_to_name(struct nc_ctn *ctn_first, void *cert, struct nc_ctn_d data->matched_ctns |= map_type; data->matched_ctn_type[data->matched_ctn_count++] = map_type; if (!data->username && (map_type == NC_TLS_CTN_SPECIFIED)) { - data->username = ctn->name; // TODO make a copy? + data->username = ctn->name; } } } @@ -555,7 +555,7 @@ nc_server_tls_verify_peer_cert(void *peer_cert, struct nc_server_tls_opts *opts) } int -nc_server_tls_verify_cert(void *cert, int depth, int self_signed, struct nc_tls_verify_cb_data *cb_data) +nc_server_tls_verify_cert(void *cert, int depth, int trusted, struct nc_tls_verify_cb_data *cb_data) { int ret = 0, i; char *subject = NULL, *issuer = NULL; @@ -563,6 +563,11 @@ nc_server_tls_verify_cert(void *cert, int depth, int self_signed, struct nc_tls_ struct nc_session *session = cb_data->session; struct nc_endpt *referenced_endpt; + if (session->username) { + /* already verified */ + return 0; + } + subject = nc_server_tls_get_subject_wrap(cert); issuer = nc_server_tls_get_issuer_wrap(cert); if (!subject || !issuer) { @@ -576,7 +581,7 @@ nc_server_tls_verify_cert(void *cert, int depth, int self_signed, struct nc_tls_ VRB(session, "Cert verify: issuer: %s.", issuer); if (depth == 0) { - if (self_signed) { + if (!trusted) { /* peer cert is not trusted, so it must match any configured end-entity cert * on the given endpoint in order for the client to be authenticated */ ret = nc_server_tls_verify_peer_cert(cert, opts); diff --git a/src/session_wrapper.h b/src/session_wrapper.h index 785b33f7..654a7d15 100644 --- a/src/session_wrapper.h +++ b/src/session_wrapper.h @@ -227,11 +227,11 @@ void nc_client_tls_set_verify_wrap(void *tls_cfg); * * @param[in] cert Certificate to verify. * @param[in] depth Certificate depth. - * @param[in] self_signed Boolean flag representing self-signedness of the certificate. + * @param[in] trusted Boolean flag representing whether the certificate is trusted. * @param[in] cb_data Data for the verify callback. * @return 0 on success, 1 on verify fail, -1 on fatal error. */ -int nc_server_tls_verify_cert(void *cert, int depth, int self_signed, struct nc_tls_verify_cb_data *cb_data); +int nc_server_tls_verify_cert(void *cert, int depth, int trusted, struct nc_tls_verify_cb_data *cb_data); /** * @brief Check if the peer certificate matches any configured ee certs. From 16873617870f5a113ba0cc63b678728ad2ba8415 Mon Sep 17 00:00:00 2001 From: roman Date: Mon, 13 May 2024 14:22:17 +0200 Subject: [PATCH 45/54] ci UPDATE add mbedtls test runs --- .github/workflows/ci.yml | 66 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 370e0eb6..84b68542 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -37,12 +37,26 @@ jobs: matrix: config: - { - name: "Release, gcc", + name: "Release, gcc, OpenSSL", os: "ubuntu-22.04", build-type: "Release", dep-build-type: "Release", cc: "gcc", options: "-DENABLE_TESTS=ON -DENABLE_DNSSEC=ON", + tls-lib: "OpenSSL", + packages: "", + snaps: "", + make-prepend: "", + make-target: "" + } + - { + name: "Release, gcc, MbedTLS", + os: "ubuntu-22.04", + build-type: "Release", + dep-build-type: "Release", + cc: "gcc", + options: "-DENABLE_TESTS=ON -DENABLE_DNSSEC=ON", + tls-lib: "MbedTLS", packages: "", snaps: "", make-prepend: "", @@ -55,18 +69,33 @@ jobs: dep-build-type: "Release", cc: "clang", options: "-DENABLE_TESTS=ON -DENABLE_DNSSEC=ON", + tls-lib: "OpenSSL", packages: "", snaps: "", make-prepend: "", make-target: "" } - { - name: "Debug, gcc", + name: "Debug, gcc, OpenSSL", + os: "ubuntu-22.04", + build-type: "Debug", + dep-build-type: "Release", + cc: "gcc", + options: "-DENABLE_DNSSEC=ON", + tls-lib: "OpenSSL", + packages: "valgrind", + snaps: "", + make-prepend: "", + make-target: "" + } + - { + name: "Debug, gcc, MbedTLS", os: "ubuntu-22.04", build-type: "Debug", dep-build-type: "Release", cc: "gcc", options: "-DENABLE_DNSSEC=ON", + tls-lib: "MbedTLS", packages: "valgrind", snaps: "", make-prepend: "", @@ -79,6 +108,7 @@ jobs: dep-build-type: "Release", cc: "clang", options: "-DENABLE_DNSSEC=ON", + tls-lib: "OpenSSL", # no valgrind because it does not support DWARF5 yet generated by clang 14 packages: "", snaps: "", @@ -92,18 +122,33 @@ jobs: dep-build-type: "Release", cc: "gcc", options: "-DENABLE_SSH_TLS=OFF", + tls-lib: "", packages: "valgrind", snaps: "", make-prepend: "", make-target: "" } - { - name: "ASAN and UBSAN", + name: "ASAN and UBSAN, OpenSSL", + os: "ubuntu-22.04", + build-type: "Debug", + dep-build-type: "Release", + cc: "clang", + options: "-DCMAKE_C_FLAGS=-fsanitize=address,undefined -DENABLE_VALGRIND_TESTS=OFF", + tls-lib: "OpenSSL", + packages: "", + snaps: "", + make-prepend: "", + make-target: "" + } + - { + name: "ASAN and UBSAN, MbedTLS", os: "ubuntu-22.04", build-type: "Debug", dep-build-type: "Release", cc: "clang", options: "-DCMAKE_C_FLAGS=-fsanitize=address,undefined -DENABLE_VALGRIND_TESTS=OFF", + tls-lib: "MbedTLS", packages: "", snaps: "", make-prepend: "", @@ -116,6 +161,7 @@ jobs: dep-build-type: "Debug", cc: "gcc", options: "", + tls-lib: "OpenSSL", packages: "abi-dumper abi-compliance-checker snap", snaps: "core universal-ctags", make-prepend: "", @@ -145,7 +191,7 @@ jobs: CC=${{ matrix.config.cc }} cmake .. make sudo make install - if: ${{ matrix.config.name == 'Debug, gcc' }} + if: ${{ matrix.config.name == 'Debug, gcc, OpenSSL' || matrix.config.name == 'Debug, gcc, MbedTLS' }} - name: Deps-libyang shell: bash @@ -167,6 +213,18 @@ jobs: make -j2 sudo make install + - name: Deps-MbedTLS + shell: bash + run: | + git clone -b mbedtls-3.5.2 https://github.com/Mbed-TLS/mbedtls.git + cd mbedtls + mkdir build + cd build + CC=${{ matrix.config.cc }} cmake -DUSE_SHARED_MBEDTLS_LIBRARY=On -DENABLE_TESTING=Off .. + make -j2 + sudo make install + if: ${{ matrix.config.tls-lib == 'MbedTLS' }} + - name: Configure shell: bash working-directory: ${{ github.workspace }} From 2d47f61bf98f2b65e8d3ea472540314969094286 Mon Sep 17 00:00:00 2001 From: roman Date: Mon, 13 May 2024 15:43:32 +0200 Subject: [PATCH 46/54] cmake UPDATE improve FindMbedTLS module --- CMakeLists.txt | 12 ++-- CMakeModules/FindLibMbedTLS.cmake | 99 --------------------------- CMakeModules/FindMbedTLS.cmake | 110 ++++++++++++++++++++++++++++++ src/config.h.in | 2 +- src/session_wrapper.h | 2 +- 5 files changed, 118 insertions(+), 107 deletions(-) delete mode 100644 CMakeModules/FindLibMbedTLS.cmake create mode 100644 CMakeModules/FindMbedTLS.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index a1fdd0e2..690e37f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -234,14 +234,14 @@ check_include_file("termios.h" HAVE_TERMIOS) if(ENABLE_SSH_TLS) # dependencies - mbedTLS (higher preference) or OpenSSL - find_package(LibMbedTLS 3.5.2) - if (LIBMBEDTLS_FOUND) + find_package(MbedTLS 3.5.2) + if (MBEDTLS_FOUND) # dependencies - mbedtls - set(HAVE_LIBMBEDTLS TRUE) + set(HAVE_MBEDTLS TRUE) list(APPEND libsrc src/session_mbedtls.c) - include_directories(${LIBMBEDTLS_INCLUDE_DIRS}) - target_link_libraries(netconf2 ${LIBMBEDTLS_LIBRARIES}) - list(APPEND CMAKE_REQUIRED_LIBRARIES ${LIBMBEDTLS_LIBRARIES}) + include_directories(${MBEDTLS_INCLUDE_DIR}) + target_link_libraries(netconf2 ${MBEDTLS_LIBRARIES}) + list(APPEND CMAKE_REQUIRED_LIBRARIES ${MBEDTLS_LIBRARIES}) else() # dependencies - openssl find_package(OpenSSL 3.0.0 REQUIRED) diff --git a/CMakeModules/FindLibMbedTLS.cmake b/CMakeModules/FindLibMbedTLS.cmake deleted file mode 100644 index 7ab6f330..00000000 --- a/CMakeModules/FindLibMbedTLS.cmake +++ /dev/null @@ -1,99 +0,0 @@ -# - Try to find LibMbedTLS -# Once done this will define -# -# LIBMBEDTLS_FOUND - system has LibPAM -# LIBMBEDTLS_INCLUDE_DIRS - the LibPAM include directory -# LIBMBEDTLS_LIBRARIES - link these to use LibPAM -# -# Author Roman Janota -# Copyright (c) 2024 CESNET, z.s.p.o. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# -# 1. Redistributions of source code must retain the copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# 3. The name of the author may not be used to endorse or promote products -# derived from this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR -# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES -# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. -# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, -# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT -# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF -# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -# - -if(LIBMBEDTLS_LIBRARIES AND LIBMBEDTLS_INCLUDE_DIRS) - # in cache already - set(LIBMBEDTLS_FOUND TRUE) -else() - find_path(LIBMBEDTLS_INCLUDE_DIR - NAMES - mbedtls/ssl.h - PATHS - /opt/local/include - /sw/include - ${CMAKE_INCLUDE_PATH} - ${CMAKE_INSTALL_PREFIX}/include - ) - - find_library(LIBMBEDTLS_LIBRARY - NAMES - libmbedtls.so # TODO - PATHS - /usr/lib - /usr/lib64 - /opt/local/lib - /sw/lib - ${CMAKE_LIBRARY_PATH} - ${CMAKE_INSTALL_PREFIX}/lib - ) - - find_library(LIBMBEDX509_LIBRARY - NAMES - libmbedx509.so - PATHS - /usr/lib - /usr/lib64 - /opt/local/lib - /sw/lib - ${CMAKE_LIBRARY_PATH} - ${CMAKE_INSTALL_PREFIX}/lib - ) - - find_library(LIBMBEDCRYPTO_LIBRARY - NAMES - libmbedcrypto.so - PATHS - /usr/lib - /usr/lib64 - /opt/local/lib - /sw/lib - ${CMAKE_LIBRARY_PATH} - ${CMAKE_INSTALL_PREFIX}/lib - ) - - if(LIBMBEDTLS_INCLUDE_DIR AND LIBMBEDTLS_LIBRARY AND LIBMBEDX509_LIBRARY AND LIBMBEDCRYPTO_LIBRARY) - set(LIBMBEDTLS_FOUND TRUE) - else() - set(LIBMBEDTLS_FOUND FALSE) - endif() - - set(LIBMBEDTLS_INCLUDE_DIRS ${LIBMBEDTLS_INCLUDE_DIR}) - set(LIBMBEDTLS_LIBRARIES ${LIBMBEDTLS_LIBRARY} ${LIBMBEDX509_LIBRARY} ${LIBMBEDCRYPTO_LIBRARY}) - - include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(LibMbedTLS DEFAULT_MSG LIBMBEDTLS_LIBRARIES LIBMBEDTLS_INCLUDE_DIRS) - - # show the LIBMBEDTLS_INCLUDE_DIRS and LIBMBEDTLS_LIBRARIES variables only in the advanced view - mark_as_advanced(LIBMBEDTLS_INCLUDE_DIRS LIBMBEDTLS_LIBRARIES) -endif() diff --git a/CMakeModules/FindMbedTLS.cmake b/CMakeModules/FindMbedTLS.cmake new file mode 100644 index 00000000..6f1b03e1 --- /dev/null +++ b/CMakeModules/FindMbedTLS.cmake @@ -0,0 +1,110 @@ +# - Try to find MbedTLS +# Once done this will define +# +# MBEDTLS_FOUND - MbedTLS was found +# MBEDTLS_INCLUDE_DIR - MbedTLS include directories +# MBEDTLS_LIBRARIES - link these to use MbedTLS +# MBEDTLS_VERSION - version of MbedTLS +# +# Author Roman Janota +# Copyright (c) 2024 CESNET, z.s.p.o. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# +# 1. Redistributions of source code must retain the copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. The name of the author may not be used to endorse or promote products +# derived from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +include(FindPackageHandleStandardArgs) + +if(MBEDTLS_LIBRARIES AND MBEDTLS_INCLUDE_DIR) + # in cache already + set(MBEDTLS_FOUND TRUE) +else() + find_path(MBEDTLS_INCLUDE_DIR + NAMES + mbedtls/ssl.h + PATHS + /opt/local/include + /sw/include + ${CMAKE_INCLUDE_PATH} + ${CMAKE_INSTALL_PREFIX}/include + ) + + find_library(MBEDTLS_LIBRARY + NAMES + libmbedtls.so + PATHS + /usr/lib + /usr/lib64 + /opt/local/lib + /sw/lib + ${CMAKE_LIBRARY_PATH} + ${CMAKE_INSTALL_PREFIX}/lib + ) + + find_library(MBEDX509_LIBRARY + NAMES + libmbedx509.so + PATHS + /usr/lib + /usr/lib64 + /opt/local/lib + /sw/lib + ${CMAKE_LIBRARY_PATH} + ${CMAKE_INSTALL_PREFIX}/lib + ) + + find_library(MBEDCRYPTO_LIBRARY + NAMES + libmbedcrypto.so + PATHS + /usr/lib + /usr/lib64 + /opt/local/lib + /sw/lib + ${CMAKE_LIBRARY_PATH} + ${CMAKE_INSTALL_PREFIX}/lib + ) + + if(MBEDTLS_INCLUDE_DIR AND MBEDTLS_LIBRARY AND MBEDX509_LIBRARY AND MBEDCRYPTO_LIBRARY) + # learn MbedTLS version + if(EXISTS "${MBEDTLS_INCLUDE_DIR}/mbedtls/build_info.h") + file(STRINGS "${MBEDTLS_INCLUDE_DIR}/mbedtls/build_info.h" MBEDTLS_VERSION + REGEX "#define[ \t]+MBEDTLS_VERSION_STRING[ \t]+\"([0-9]+\.[0-9]+\.[0-9]+)\"") + string(REGEX MATCH "[0-9]+\\.[0-9]+\\.[0-9]+" MBEDTLS_VERSION ${MBEDTLS_VERSION}) + endif() + if(NOT MBEDTLS_VERSION) + message(STATUS "MBEDTLS_VERSION not found, assuming MbedTLS is too old and cannot be used!") + set(MBEDTLS_INCLUDE_DIR "MBEDTLS_INCLUDE_DIR-NOTFOUND") + set(MBEDTLS_LIBRARY "MBEDTLS_LIBRARY-NOTFOUND") + endif() + endif() + + set(MBEDTLS_INCLUDE_DIR ${MBEDTLS_INCLUDE_DIR}) + set(MBEDTLS_LIBRARIES ${MBEDTLS_LIBRARY} ${MBEDX509_LIBRARY} ${MBEDCRYPTO_LIBRARY}) + + find_package_handle_standard_args(MbedTLS FOUND_VAR MBEDTLS_FOUND + REQUIRED_VARS MBEDTLS_INCLUDE_DIR MBEDTLS_LIBRARIES + VERSION_VAR MBEDTLS_VERSION) + + # show the MBEDTLS_INCLUDE_DIR and MBEDTLS_LIBRARIES variables only in the advanced view + mark_as_advanced(MBEDTLS_INCLUDE_DIR MBEDTLS_LIBRARIES) +endif() diff --git a/src/config.h.in b/src/config.h.in index c64db022..d366e0d9 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -47,7 +47,7 @@ /* * Use MbedTLS as TLS back-end */ -#cmakedefine HAVE_LIBMBEDTLS +#cmakedefine HAVE_MBEDTLS /* * Location of installed YANG modules on the system diff --git a/src/session_wrapper.h b/src/session_wrapper.h index 654a7d15..d5a52866 100644 --- a/src/session_wrapper.h +++ b/src/session_wrapper.h @@ -20,7 +20,7 @@ #include "config.h" -#ifdef HAVE_LIBMBEDTLS +#ifdef HAVE_MBEDTLS #include #include From 999eb9583b1d5e32ec2507873421306fe7536d99 Mon Sep 17 00:00:00 2001 From: roman Date: Mon, 13 May 2024 15:44:10 +0200 Subject: [PATCH 47/54] server config UPDATE clarify ca/ee certs auth --- src/server_config.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/server_config.h b/src/server_config.h index 366ec71f..6112f777 100644 --- a/src/server_config.h +++ b/src/server_config.h @@ -605,6 +605,8 @@ int nc_server_config_del_tls_keystore_ref(const char *endpt_name, struct lyd_nod /** * @brief Creates new YANG configuration data nodes for a client's (end-entity) certificate. * + * A client certificate is authenticated if it is an exact match to a configured client certificate. + * * @param[in] ctx libyang context. * @param[in] endpt_name Arbitrary identifier of the endpoint. * If an endpoint with this identifier already exists, its contents will be changed. @@ -655,6 +657,16 @@ int nc_server_config_del_tls_client_cert_truststore_ref(const char *endpt_name, /** * @brief Creates new YANG configuration data nodes for a client certificate authority (trust-anchor) certificate. * + * A client certificate is authenticated if it has a valid chain of trust to any configured CA cert. + * The configured CA cert, up to which the valid chain of trust can be built, does not have to be + * self-signed (the root CA). That means that the chain may be incomplete, yet the client will be authenticated. + * + * For example assume a certificate chain + * A <- B <- C, + * where A is the root CA, then the client certificate C will be authenticated either + * if solely B is configured, or if both A and B are configured. C will not be authenticated + * if just A is configured as a CA certificate. + * * @param[in] ctx libyang context. * @param[in] endpt_name Arbitrary identifier of the endpoint. * If an endpoint with this identifier already exists, its contents will be changed. From e64c2cc9f24a6e33286ab0630051a490d0608d1a Mon Sep 17 00:00:00 2001 From: roman Date: Mon, 13 May 2024 16:27:57 +0200 Subject: [PATCH 48/54] session wrapper BUGFIX account for no ssh/tls --- src/session_wrapper.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/session_wrapper.h b/src/session_wrapper.h index d5a52866..273bc2c2 100644 --- a/src/session_wrapper.h +++ b/src/session_wrapper.h @@ -281,6 +281,8 @@ void nc_tls_sans_destroy_wrap(void *sans); */ int nc_tls_get_num_sans_wrap(void *sans); +#ifdef NC_ENABLED_SSH_TLS + /** * @brief Get the SAN value and type in the context of CTN. * @@ -292,6 +294,8 @@ int nc_tls_get_num_sans_wrap(void *sans); */ int nc_tls_get_san_value_type_wrap(void *sans, int idx, char **san_value, NC_TLS_CTN_MAPTYPE *san_type); +#endif + /** * @brief Compare two certificates. * From 2b9ce8cc8b56b0c736bec58d7063e904dee18cc2 Mon Sep 17 00:00:00 2001 From: roman Date: Tue, 14 May 2024 09:09:30 +0200 Subject: [PATCH 49/54] session wrapper UPDATE store client cert in sess --- src/session.c | 1 - src/session_mbedtls.c | 25 +++++++++++++++++++++++++ src/session_openssl.c | 5 +++++ 3 files changed, 30 insertions(+), 1 deletion(-) diff --git a/src/session.c b/src/session.c index 83f3a8aa..8304a4d3 100644 --- a/src/session.c +++ b/src/session.c @@ -809,7 +809,6 @@ nc_session_free_transport(struct nc_session *session, int *multisession) session->ti.tls.config = NULL; if (session->side == NC_SERVER) { - // TODO nc_tls_cert_destroy_wrap(session->opts.server.client_cert); } diff --git a/src/session_mbedtls.c b/src/session_mbedtls.c index 17b03a86..1a53620b 100644 --- a/src/session_mbedtls.c +++ b/src/session_mbedtls.c @@ -427,6 +427,24 @@ nc_server_tls_set_tls_versions_wrap(void *tls_cfg, unsigned int tls_versions) return 0; } +static mbedtls_x509_crt * +nc_tls_cert_dup(const mbedtls_x509_crt *cert) +{ + mbedtls_x509_crt *new_cert; + + new_cert = nc_tls_cert_new_wrap(); + if (!new_cert) { + return NULL; + } + + if (mbedtls_x509_crt_parse_der(new_cert, cert->raw.p, cert->raw.len)) { + free(new_cert); + return NULL; + } + + return new_cert; +} + static int nc_server_tls_verify_cb(void *cb_data, mbedtls_x509_crt *cert, int depth, uint32_t *flags) { @@ -468,6 +486,13 @@ nc_server_tls_verify_cb(void *cb_data, mbedtls_x509_crt *cert, int depth, uint32 return MBEDTLS_ERR_X509_ALLOC_FAILED; } else if (!ret) { /* success */ + if ((depth == 0) && (!data->session->opts.server.client_cert)) { + /* copy the client cert */ + data->session->opts.server.client_cert = nc_tls_cert_dup(cert); + if (!data->session->opts.server.client_cert) { + return MBEDTLS_ERR_X509_ALLOC_FAILED; + } + } return 0; } else { if (depth > 0) { diff --git a/src/session_openssl.c b/src/session_openssl.c index 3891dc7b..83a5b933 100644 --- a/src/session_openssl.c +++ b/src/session_openssl.c @@ -387,6 +387,11 @@ nc_server_tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) return 0; } else if (!ret) { /* success */ + if ((depth == 0) && (!data->session->opts.server.client_cert)) { + /* copy the client cert */ + data->session->opts.server.client_cert = X509_dup(cert); + NC_CHECK_ERRMEM_RET(!data->session->opts.server.client_cert, 0); + } return 1; } else { if (depth > 0) { From f99eb7469ed894d30443f89306c802a84dafe54d Mon Sep 17 00:00:00 2001 From: roman Date: Tue, 14 May 2024 10:22:59 +0200 Subject: [PATCH 50/54] cmake UPDATE rename mbedtls include dir var --- CMakeLists.txt | 2 +- CMakeModules/FindMbedTLS.cmake | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 690e37f6..1000d44d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -239,7 +239,7 @@ if(ENABLE_SSH_TLS) # dependencies - mbedtls set(HAVE_MBEDTLS TRUE) list(APPEND libsrc src/session_mbedtls.c) - include_directories(${MBEDTLS_INCLUDE_DIR}) + include_directories(${MBEDTLS_INCLUDE_DIRS}) target_link_libraries(netconf2 ${MBEDTLS_LIBRARIES}) list(APPEND CMAKE_REQUIRED_LIBRARIES ${MBEDTLS_LIBRARIES}) else() diff --git a/CMakeModules/FindMbedTLS.cmake b/CMakeModules/FindMbedTLS.cmake index 6f1b03e1..17b96d7c 100644 --- a/CMakeModules/FindMbedTLS.cmake +++ b/CMakeModules/FindMbedTLS.cmake @@ -2,7 +2,7 @@ # Once done this will define # # MBEDTLS_FOUND - MbedTLS was found -# MBEDTLS_INCLUDE_DIR - MbedTLS include directories +# MBEDTLS_INCLUDE_DIRS - MbedTLS include directories # MBEDTLS_LIBRARIES - link these to use MbedTLS # MBEDTLS_VERSION - version of MbedTLS # @@ -34,7 +34,7 @@ # include(FindPackageHandleStandardArgs) -if(MBEDTLS_LIBRARIES AND MBEDTLS_INCLUDE_DIR) +if(MBEDTLS_LIBRARIES AND MBEDTLS_INCLUDE_DIRS) # in cache already set(MBEDTLS_FOUND TRUE) else() @@ -98,13 +98,13 @@ else() endif() endif() - set(MBEDTLS_INCLUDE_DIR ${MBEDTLS_INCLUDE_DIR}) + set(MBEDTLS_INCLUDE_DIRS ${MBEDTLS_INCLUDE_DIR}) set(MBEDTLS_LIBRARIES ${MBEDTLS_LIBRARY} ${MBEDX509_LIBRARY} ${MBEDCRYPTO_LIBRARY}) find_package_handle_standard_args(MbedTLS FOUND_VAR MBEDTLS_FOUND - REQUIRED_VARS MBEDTLS_INCLUDE_DIR MBEDTLS_LIBRARIES + REQUIRED_VARS MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARIES VERSION_VAR MBEDTLS_VERSION) # show the MBEDTLS_INCLUDE_DIR and MBEDTLS_LIBRARIES variables only in the advanced view - mark_as_advanced(MBEDTLS_INCLUDE_DIR MBEDTLS_LIBRARIES) + mark_as_advanced(MBEDTLS_INCLUDE_DIRS MBEDTLS_LIBRARIES) endif() From afdf6cd0b490878bd752997849b9124454cd1c8e Mon Sep 17 00:00:00 2001 From: roman Date: Tue, 14 May 2024 11:00:54 +0200 Subject: [PATCH 51/54] session wrapper REFACTOR add doxygen to static fns --- src/session_mbedtls.c | 75 +++++++++++++++++++++++++++++++++++++------ src/session_openssl.c | 19 +++++++++++ 2 files changed, 84 insertions(+), 10 deletions(-) diff --git a/src/session_mbedtls.c b/src/session_mbedtls.c index 1a53620b..9aba7c33 100644 --- a/src/session_mbedtls.c +++ b/src/session_mbedtls.c @@ -52,7 +52,14 @@ #include #include -/* some mbedTLS functions may return 'high' and some 'low' level errors, try to handle both cases this way */ +/** + * @brief Converts mbedTLS error codes to a string. + * + * Some mbedTLS functions may return 'high' and some 'low' level errors, try to handle both cases this way. + * + * @param[in] err MbedTLS error code. + * @return Error string. + */ static const char * nc_get_mbedtls_str_err(int err) { @@ -101,7 +108,13 @@ nc_server_tls_dn2str(const mbedtls_x509_name *dn) return str; } -/* creates a new rng context needed for PK operations and for ssl config */ +/** + * @brief Create a new random number generator context. + * + * @param[out] ctr_drbg Random bit generator context. + * @param[out] entropy Entropy context. + * @return 0 on success, 1 on failure. + */ static int nc_tls_rng_new(mbedtls_ctr_drbg_context **ctr_drbg, mbedtls_entropy_context **entropy) { @@ -138,6 +151,12 @@ nc_tls_rng_new(mbedtls_ctr_drbg_context **ctr_drbg, mbedtls_entropy_context **en return 1; } +/** + * @brief Destroy the random number generator context. + * + * @param[in] ctr_drbg Random bit generator context. + * @param[in] entropy Entropy context. + */ static void nc_tls_rng_destroy(mbedtls_ctr_drbg_context *ctr_drbg, mbedtls_entropy_context *entropy) { @@ -149,7 +168,12 @@ nc_tls_rng_destroy(mbedtls_ctr_drbg_context *ctr_drbg, mbedtls_entropy_context * } } -/* get verify err string, caller is responsible for freeing it, 256B should be more than enough */ +/** + * @brief Get a string representation of the verification error. + * + * @param[in] err Verification error code. + * @return String representation of the error. Caller is responsible for freeing it. + */ static char * nc_tls_get_verify_err_str(int err) { @@ -242,6 +266,11 @@ nc_tls_cert_destroy_wrap(void *cert) free(cert); } +/** + * @brief Create a new private key context. + * + * @return New private key context or NULL. + */ static void * nc_tls_pkey_new_wrap(void) { @@ -427,6 +456,12 @@ nc_server_tls_set_tls_versions_wrap(void *tls_cfg, unsigned int tls_versions) return 0; } +/** + * @brief Duplicates a certificate. + * + * @param[in] cert Certificate to duplicate. + * @return Duplicated certificate or NULL. + */ static mbedtls_x509_crt * nc_tls_cert_dup(const mbedtls_x509_crt *cert) { @@ -445,6 +480,15 @@ nc_tls_cert_dup(const mbedtls_x509_crt *cert) return new_cert; } +/** + * @brief Verify a certificate. + * + * @param[in] cb_data Callback data (session, opts, data for CTN). + * @param[in] cert Certificate to verify. + * @param[in] depth Certificate depth in the chain. + * @param[in,out] flags Verification flags. Used to propagate errors. + * @return 0 on success (verification result is based on the value of flags), non-zero on fatal-error. + */ static int nc_server_tls_verify_cb(void *cb_data, mbedtls_x509_crt *cert, int depth, uint32_t *flags) { @@ -734,6 +778,14 @@ nc_server_tls_sha512_wrap(void *cert, unsigned char *buf) return 0; } +/** + * @brief Callback for sending data. + * + * @param[in] ctx Socket. + * @param[in] buf Data to send. + * @param[in] len Length of the data. + * @return Number of bytes sent or negative value on error. + */ static int nc_server_tls_send(void *ctx, const unsigned char *buf, size_t len) { @@ -757,6 +809,14 @@ nc_server_tls_send(void *ctx, const unsigned char *buf, size_t len) return ret; } +/** + * @brief Callback for receiving data. + * + * @param[in] ctx Socket. + * @param[out] buf Buffer to store the received data. + * @param[in] len Length of the buffer. + * @return Number of bytes received or negative value on error. + */ static int nc_server_tls_recv(void *ctx, unsigned char *buf, size_t len) { @@ -1095,13 +1155,8 @@ nc_tls_is_der_subpubkey_wrap(unsigned char *der, long len) ret = mbedtls_pk_parse_subpubkey(&der, der + len, pkey); nc_tls_privkey_destroy_wrap(pkey); - if (!ret) { - /* success */ - return 1; - } else { - /* fail */ - return 0; - } + + return !ret; } int diff --git a/src/session_openssl.c b/src/session_openssl.c index 83a5b933..3fd88980 100644 --- a/src/session_openssl.c +++ b/src/session_openssl.c @@ -324,6 +324,13 @@ nc_server_tls_set_tls_versions_wrap(void *tls_cfg, unsigned int tls_versions) return 0; } +/** + * @brief Verify a certificate. + * + * @param[in] preverify_ok The result of the in-built verification. + * @param[in] x509_ctx Verification context. + * @return 1 on success, 0 on error. + */ static int nc_server_tls_verify_cb(int preverify_ok, X509_STORE_CTX *x509_ctx) { @@ -747,6 +754,13 @@ nc_tls_init_ctx_wrap(int UNUSED(sock), void *cert, void *pkey, void *cert_store, return 0; } +/** + * @brief Move CRLs from one store to another. + * + * @param[in] src Source store. + * @param[in] dst Destination store. + * @return 0 on success, 1 on error. + */ static int nc_tls_move_crls_to_store(const X509_STORE *src, X509_STORE *dst) { @@ -918,6 +932,11 @@ nc_base64_encode_wrap(const unsigned char *bin, size_t len, char **base64) return 0; } +/** + * @brief Get all OpenSSL error reasons. + * + * @return String with all OpenSSL error reasons or NULL. + */ static char * nc_tls_get_err_reasons(void) { From cacf66d18cd2ae383dd5d42477f526dbba9fa781 Mon Sep 17 00:00:00 2001 From: roman Date: Tue, 14 May 2024 11:01:21 +0200 Subject: [PATCH 52/54] session mbedtls REFACTOR divide crl dist point fn --- src/session_mbedtls.c | 204 ++++++++++++++++++++++++------------------ 1 file changed, 119 insertions(+), 85 deletions(-) diff --git a/src/session_mbedtls.c b/src/session_mbedtls.c index 9aba7c33..5022ce70 100644 --- a/src/session_mbedtls.c +++ b/src/session_mbedtls.c @@ -1585,6 +1585,121 @@ nc_tls_import_pubkey_file_wrap(const char *pubkey_path) return pk; } +/** + * @brief Parse the CRL distribution points X509v3 extension and obtain the URIs. + * + * @param[in,out] p Pointer to the DER encoded extension. When the function gets called, this should + * point to the first byte in the value of CRLDistributionPoints. + * @param[in] len Length of the CRLDistributionPoints ASN.1 encoded value. + * @param[out] uris Array of URIs found in the extension. + * @param[out] uri_count Number of URIs found in the extension. + * @return 0 on success, non-zero on error. + */ +static int +nc_server_tls_parse_crl_dist_points(unsigned char **p, size_t len, char ***uris, int *uri_count) +{ + int ret = 0; + unsigned char *end_crl_dist_points; + mbedtls_x509_sequence general_names = {0}; + mbedtls_x509_sequence *iter = NULL; + mbedtls_x509_subject_alternative_name san = {0}; + void *tmp; + + /* + * parsing the value of CRLDistributionPoints + * + * CRLDistributionPoints ::= SEQUENCE SIZE (1..MAX) OF DistributionPoint + */ + end_crl_dist_points = *p + len; + while (*p < end_crl_dist_points) { + /* + * DistributionPoint ::= SEQUENCE { + * distributionPoint [0] DistributionPointName OPTIONAL, + * reasons [1] ReasonFlags OPTIONAL, + * cRLIssuer [2] GeneralNames OPTIONAL } + */ + ret = mbedtls_asn1_get_tag(p, end_crl_dist_points, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); + if (ret) { + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret)); + goto cleanup; + } + if (!len) { + /* empty sequence */ + continue; + } + + /* parse distributionPoint */ + ret = mbedtls_asn1_get_tag(p, end_crl_dist_points, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 0); + if (!ret) { + /* + * DistributionPointName ::= CHOICE { + * fullName [0] GeneralNames, + * nameRelativeToCRLIssuer [1] RelativeDistinguishedName } + */ + ret = mbedtls_asn1_get_tag(p, end_crl_dist_points, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 0); + if (ret) { + if ((ret == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) && (**p == (MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 1))) { + /* it's nameRelativeToCRLIssuer, but we don't support it */ + ERR(NULL, "Failed to parse CRL distribution points extension (nameRelativeToCRLIssuer not yet supported)."); + goto cleanup; + } else { + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret)); + goto cleanup; + } + } + + /* parse GeneralNames, but thankfully there is an api for this */ + ret = mbedtls_x509_get_subject_alt_name_ext(p, *p + len, &general_names); + if (ret) { + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret)); + goto cleanup; + } + + /* iterate over all the GeneralNames */ + iter = &general_names; + while (iter) { + ret = mbedtls_x509_parse_subject_alt_name(&iter->buf, &san); + if (ret && (ret != MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE)) { + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret)); + goto cleanup; + } + + if (san.type == MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER) { + /* found an URI */ + tmp = realloc(*uris, (*uri_count + 1) * sizeof **uris); + if (!tmp) { + ERRMEM; + ret = 1; + mbedtls_x509_free_subject_alt_name(&san); + goto cleanup; + } + *uris = tmp; + + *uris[*uri_count] = strndup((const char *)san.san.unstructured_name.p, san.san.unstructured_name.len); + if (!*uris[*uri_count]) { + ERRMEM; + ret = 1; + mbedtls_x509_free_subject_alt_name(&san); + goto cleanup; + } + ++(*uri_count); + } + + mbedtls_x509_free_subject_alt_name(&san); + iter = iter->next; + } + + } else if (ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) { + /* failed to parse it, but not because it's optional */ + ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret)); + goto cleanup; + } + } + +cleanup: + return ret; +} + int nc_server_tls_get_crl_distpoint_uris_wrap(void *cert_store, char ***uris, int *uri_count) { @@ -1694,91 +1809,10 @@ nc_server_tls_get_crl_distpoint_uris_wrap(void *cert_store, char ***uris, int *u goto cleanup; } - end_crl_dist_points = p + len; - - while (p < end_crl_dist_points) { - /* - * DistributionPoint ::= SEQUENCE { - * distributionPoint [0] DistributionPointName OPTIONAL, - * reasons [1] ReasonFlags OPTIONAL, - * cRLIssuer [2] GeneralNames OPTIONAL } - */ - ret = mbedtls_asn1_get_tag(&p, end_ext_octet, &len, MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE); - if (ret) { - ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret)); - goto cleanup; - } - if (!len) { - /* empty sequence */ - continue; - } - - /* parse distributionPoint */ - ret = mbedtls_asn1_get_tag(&p, end_ext_octet, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 0); - if (!ret) { - /* - * DistributionPointName ::= CHOICE { - * fullName [0] GeneralNames, - * nameRelativeToCRLIssuer [1] RelativeDistinguishedName } - */ - ret = mbedtls_asn1_get_tag(&p, end_ext_octet, &len, MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 0); - if (ret) { - if ((ret == MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) && (*p == (MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 1))) { - /* it's nameRelativeToCRLIssuer, but we don't support it */ - ERR(NULL, "Failed to parse CRL distribution points extension (nameRelativeToCRLIssuer not yet supported)."); - goto cleanup; - } else { - ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret)); - goto cleanup; - } - } - - /* parse GeneralNames, but thankfully there is an api for this */ - ret = mbedtls_x509_get_subject_alt_name_ext(&p, p + len, &general_names); - if (ret) { - ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret)); - goto cleanup; - } - - /* iterate over all the GeneralNames */ - iter = &general_names; - while (iter) { - ret = mbedtls_x509_parse_subject_alt_name(&iter->buf, &san); - if (ret && (ret != MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE)) { - ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret)); - goto cleanup; - } - - if (san.type == MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER) { - /* found an URI */ - tmp = realloc(*uris, (*uri_count + 1) * sizeof **uris); - if (!tmp) { - ERRMEM; - ret = 1; - mbedtls_x509_free_subject_alt_name(&san); - goto cleanup; - } - *uris = tmp; - - *uris[*uri_count] = strndup((const char *)san.san.unstructured_name.p, san.san.unstructured_name.len); - if (!*uris[*uri_count]) { - ERRMEM; - ret = 1; - mbedtls_x509_free_subject_alt_name(&san); - goto cleanup; - } - ++(*uri_count); - } - - mbedtls_x509_free_subject_alt_name(&san); - iter = iter->next; - } - - } else if (ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG) { - /* failed to parse it, but not because it's optional */ - ERR(NULL, "Failed to parse CRL distribution points extension (%s).", nc_get_mbedtls_str_err(ret)); - goto cleanup; - } + /* parse the distribution points and obtain the uris */ + ret = nc_server_tls_parse_crl_dist_points(&p, len, uris, uri_count); + if (ret) { + goto cleanup; } } cert = cert->next; From 97fec8bd7c7dd827875004994bf394e4ef3e112b Mon Sep 17 00:00:00 2001 From: roman Date: Tue, 14 May 2024 11:06:38 +0200 Subject: [PATCH 53/54] cmake UPDATE set mbedtls min version to 3.5.0 --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 1000d44d..8aa0ad23 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -234,7 +234,7 @@ check_include_file("termios.h" HAVE_TERMIOS) if(ENABLE_SSH_TLS) # dependencies - mbedTLS (higher preference) or OpenSSL - find_package(MbedTLS 3.5.2) + find_package(MbedTLS 3.5.0) if (MBEDTLS_FOUND) # dependencies - mbedtls set(HAVE_MBEDTLS TRUE) From e458cb5ecad625a753dcd321883476054564c69f Mon Sep 17 00:00:00 2001 From: roman Date: Tue, 14 May 2024 11:23:34 +0200 Subject: [PATCH 54/54] readme UPDATE mention mbedtls in requirements --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4cd37d78..3d4d965f 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ the `distro` directory. * crypt(3) * [libyang](https://github.com/CESNET/libyang) * libssh >= 0.9.5 (for SSH support) -* OpenSSL >= 3.0.0 (for TLS support) +* OpenSSL >= 3.0.0 or MbedTLS >= 3.5.0 (for TLS support) * curl >= 7.30.0 #### Optional