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 }} diff --git a/CMakeLists.txt b/CMakeLists.txt index 419cdbfd..8aa0ad23 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -210,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 @@ -231,10 +233,23 @@ 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}) + # dependencies - mbedTLS (higher preference) or OpenSSL + find_package(MbedTLS 3.5.0) + if (MBEDTLS_FOUND) + # dependencies - mbedtls + set(HAVE_MBEDTLS TRUE) + list(APPEND libsrc src/session_mbedtls.c) + include_directories(${MBEDTLS_INCLUDE_DIRS}) + target_link_libraries(netconf2 ${MBEDTLS_LIBRARIES}) + list(APPEND CMAKE_REQUIRED_LIBRARIES ${MBEDTLS_LIBRARIES}) + else() + # dependencies - openssl + find_package(OpenSSL 3.0.0 REQUIRED) + list(APPEND libsrc src/session_openssl.c) + include_directories(${OPENSSL_INCLUDE_DIR}) + target_link_libraries(netconf2 ${OPENSSL_LIBRARIES}) + list(APPEND CMAKE_REQUIRED_LIBRARIES ${OPENSSL_LIBRARIES}) + endif() # dependencies - libssh find_package(LibSSH 0.9.5 REQUIRED) @@ -301,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) diff --git a/CMakeModules/FindMbedTLS.cmake b/CMakeModules/FindMbedTLS.cmake new file mode 100644 index 00000000..17b96d7c --- /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_DIRS - 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_DIRS) + # 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_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_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_DIRS MBEDTLS_LIBRARIES) +endif() 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 diff --git a/src/config.h.in b/src/config.h.in index 2920b1db..d366e0d9 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_MBEDTLS + /* * Location of installed YANG modules on the system */ diff --git a/src/io.c b/src/io.c index e7366fb3..daae0232 100644 --- a/src/io.c +++ b/src/io.c @@ -28,11 +28,6 @@ #include #include -#ifdef NC_ENABLED_SSH_TLS -# include -# include -#endif /* NC_ENABLED_SSH_TLS */ - #include #include "compat.h" @@ -42,6 +37,7 @@ #include "netconf.h" #include "session.h" #include "session_p.h" +#include "session_wrapper.h" const char *nc_msgtype2str[] = { "error", @@ -57,36 +53,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) { @@ -141,7 +107,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) { @@ -163,42 +129,11 @@ 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; - } + case NC_TI_TLS: + 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 */ @@ -462,7 +397,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) { @@ -483,8 +418,8 @@ nc_read_poll(struct nc_session *session, int io_timeout) fds.revents = 0; } break; - case NC_TI_OPENSSL: - ret = SSL_pending(session->ti.tls); + case NC_TI_TLS: + ret = nc_tls_get_num_pending_bytes_wrap(session->ti.tls.session); if (ret) { /* some buffered TLS data available */ ret = 1; @@ -492,7 +427,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: @@ -589,10 +524,10 @@ 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: - fds.fd = SSL_get_fd(session->ti.tls); + case NC_TI_TLS: + fds.fd = nc_tls_get_fd_wrap(session); break; #endif /* NC_ENABLED_SSH_TLS */ default: @@ -638,10 +573,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; } @@ -675,7 +606,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."); @@ -692,31 +623,11 @@ nc_write(struct nc_session *session, const void *buf, uint32_t count) return -1; } 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; - } + 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 */ + return -1; } break; #endif /* NC_ENABLED_SSH_TLS */ diff --git a/src/server_config.c b/src/server_config.c index 0207d75c..e01ee50d 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" @@ -679,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; @@ -700,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; @@ -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); @@ -888,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 */ @@ -988,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 */ @@ -1147,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 */ @@ -1201,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); @@ -1211,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); @@ -1277,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); @@ -1287,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); @@ -2669,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; @@ -2683,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; @@ -2711,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; @@ -2725,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; @@ -3240,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.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. diff --git a/src/server_config_util.c b/src/server_config_util.c index b5f7e06e..f94cd740 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_rsa_pubkey_param_to_bin(void *bn, unsigned char **bin, int *bin_len) { int ret = 0; unsigned char *bin_tmp = NULL; @@ -261,12 +228,11 @@ 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); - NC_CHECK_ERRMEM_RET(!bin_tmp, 1); - /* convert to binary */ - *bin_len = BN_bn2bin(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) { @@ -288,31 +254,30 @@ 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_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, *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; 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; } /* 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; } @@ -343,21 +308,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,18 +333,20 @@ 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_params_wrap(pkey, &p, &p_grp); + 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); + /* 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); @@ -419,10 +375,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,153 +382,99 @@ 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; } 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); 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_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_pem_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_pem_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_ssh2_pubkey(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,33 +510,34 @@ 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_spki_pubkey(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); + 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; @@ -682,22 +581,23 @@ 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_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(f, 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); @@ -705,10 +605,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,191 +613,157 @@ 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_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; } 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, FILE *f_privkey, char **privkey, EVP_PKEY **pkey) +nc_server_config_util_get_privkey_libtls(const char *privkey_path, char **privkey, void **pkey) { - int ret = 0, len; - BIO *bio = NULL; - char *priv_b64 = NULL; - - NC_CHECK_ARG_RET(NULL, privkey_path, f_privkey, privkey, pkey, 1); + void *pkey_tmp; + char *privkey_tmp; - 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; - } + NC_CHECK_ARG_RET(NULL, privkey_path, privkey, pkey, 1); - /* 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; - } + *privkey = *pkey = NULL; - /* 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_privkey_file_wrap(privkey_path); + 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_privkey_pem_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; char *priv = NULL; + char *privkey_header = NULL; + size_t header_len = 0; NC_CHECK_ARG_RET(NULL, privkey_path, privkey_format, privkey, pkey, 1); @@ -912,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, f_privkey, &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 */ @@ -942,16 +812,26 @@ 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 */ - *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 \"%s\" failed.", privkey_path); + goto cleanup; + } cleanup: if (f_privkey) { fclose(f_privkey); } + free(privkey_header); free(priv); return ret; } @@ -961,7 +841,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 +849,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 +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(priv_pkey, pubkey); + ret = nc_server_config_util_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_pkey_to_spki_pubkey(pkey, pubkey); } } else { if (wanted_pubkey_format == NC_PUBKEY_FORMAT_SSH) { @@ -999,7 +879,7 @@ 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; } @@ -1013,11 +893,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"; @@ -1051,11 +931,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"; @@ -1129,10 +1009,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 { @@ -1145,7 +1025,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/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" diff --git a/src/session.c b/src/session.c index 7cb1e7fa..8304a4d3 100644 --- a/src/session.c +++ b/src/session.c @@ -38,11 +38,7 @@ #ifdef NC_ENABLED_SSH_TLS #include -#include -#include -#include -#include -#include +#include "session_wrapper.h" #endif /* NC_ENABLED_SSH_TLS */ @@ -158,85 +154,16 @@ 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; + unsigned char *bin = NULL, *tmp; - /* 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; goto cleanup; } @@ -244,18 +171,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_tls_is_der_subpubkey_wrap(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; } @@ -803,7 +728,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) { @@ -868,18 +793,25 @@ 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); + case NC_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); + 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) { - X509_free(session->opts.server.client_cert); + 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..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 */ + 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.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 */ 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 d33778f0..8fa63655 100644 --- a/src/session_client_tls.c +++ b/src/session_client_tls.c @@ -20,19 +20,19 @@ #include #include +#include #include #include #include -#include -#include -#include +#include "compat.h" #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 +40,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 +47,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 +77,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 +145,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 +213,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; } @@ -365,168 +259,163 @@ 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 -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; + char *err; - /* 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 = nc_tls_verify_error_string_wrap(verify); + ERR(NULL, "Server certificate error (%s).", err); + free(err); + } - 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_client_tls_print_connect_err_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, 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_tls_config_new_wrap(NC_CLIENT); + if (!tls_cfg) { + goto fail; + } + + /* opaque CA/CRL certificate store */ + cert_store = nc_tls_cert_store_new_wrap(); + if (!cert_store) { + 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); + /* 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; + } - 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 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 the crl store */ + if (nc_client_tls_load_crl_wrap(crl_store, opts->crl_file, opts->crl_dir)) { + goto fail; } } -cleanup: - X509_VERIFY_PARAM_free(vpm); - return rc; -} + /* set client's verify mode flags */ + nc_client_tls_set_verify_wrap(tls_cfg); -static int -nc_client_tls_connect_check(int connect_ret, SSL *tls, const char *peername) -{ - int verify; + /* init TLS context and store data which may be needed later in it */ + if (nc_tls_init_ctx_wrap(sock, cli_cert, cli_pkey, cert_store, crl_store, tls_ctx)) { + goto fail; + } - /* 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)); + /* memory is managed by context now */ + cli_cert = cli_pkey = cert_store = crl_store = NULL; + + /* setup config from ctx */ + if (nc_tls_setup_config_from_ctx_wrap(tls_ctx, NC_CLIENT, tls_cfg)) { + 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, sock_tmp)) == 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_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; } 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,54 +434,32 @@ 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_TLS; + 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; } - ctx = session->ctx; /* NETCONF handshake */ if (nc_handshake_io(session) != NC_MSG_HELLO) { @@ -604,7 +471,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"); @@ -614,32 +481,43 @@ 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; - session->ti_type = NC_TI_OPENSSL; - session->ti.tls = tls; + + /* fill the session */ + 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; + } + 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; } - ctx = session->ctx; /* NETCONF handshake */ if (nc_handshake_io(session) != NC_MSG_HELLO) { @@ -651,73 +529,17 @@ 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 */ + /* store information into session */ session->host = strdup(host); session->port = port; session->username = strdup("certificate-based"); -cleanup: - if (!session) { - SSL_free(tls); - close(sock); - } return session; + +fail: + nc_session_free(session, NULL); + nc_tls_ctx_destroy_wrap(&tls_ctx); + return NULL; } diff --git a/src/session_mbedtls.c b/src/session_mbedtls.c new file mode 100644 index 00000000..5022ce70 --- /dev/null +++ b/src/session_mbedtls.c @@ -0,0 +1,1888 @@ +/** + * @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 +#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 + +/** + * @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) +{ + 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; +} + +/** + * @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) +{ + 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; +} + +/** + * @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) +{ + mbedtls_ctr_drbg_free(ctr_drbg); + free(ctr_drbg); + if (entropy) { + mbedtls_entropy_free(entropy); + free(entropy); + } +} + +/** + * @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) +{ + 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) +{ + 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 session failed (%s).", nc_get_mbedtls_str_err(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_tls_config_new_wrap(int UNUSED(side)) +{ + 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(void) +{ + 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); +} + +/** + * @brief Create a new private key context. + * + * @return New private key context or NULL. + */ +static void * +nc_tls_pkey_new_wrap(void) +{ + 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(void) +{ + /* 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); +} + +void * +nc_tls_crl_store_new_wrap(void) +{ + 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_store) +{ + mbedtls_x509_crl_free(crl_store); + free(crl_store); +} + +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).", nc_get_mbedtls_str_err(rc)); + nc_tls_cert_destroy_wrap(cert); + return NULL; + } + + return cert; +} + +int +nc_tls_add_cert_to_store_wrap(void *cert, void *cert_store) +{ + mbedtls_x509_crt *iter; + + /* store is a linked list */ + iter = cert_store; + while (iter->next) { + iter = iter->next; + } + iter->next = cert; + + return 0; +} + +void * +nc_tls_pem_to_privkey_wrap(const char *privkey_data) +{ + int rc = 0; + mbedtls_pk_context *pkey = NULL; + mbedtls_ctr_drbg_context *ctr_drbg = NULL; + mbedtls_entropy_context *entropy = NULL; + + rc = nc_tls_rng_new(&ctr_drbg, &entropy); + if (rc) { + goto cleanup; + } + + pkey = nc_tls_pkey_new_wrap(); + if (!pkey) { + 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)); + goto cleanup; + } + +cleanup: + if (rc) { + nc_tls_privkey_destroy_wrap(pkey); + pkey = NULL; + } + nc_tls_rng_destroy(ctr_drbg, entropy); + return pkey; +} + +int +nc_tls_import_crl_path_wrap(const char *path, void *crl_store) +{ + int rc; + + rc = mbedtls_x509_crl_parse_file(crl_store, path); + if (rc) { + ERR(NULL, "Failed to import CRL from file \"%s\" (%s).", path, nc_get_mbedtls_str_err(rc)); + return 1; + } + + return 0; +} + +int +nc_server_tls_add_crl_to_store_wrap(const unsigned char *crl_data, size_t size, void *crl_store) +{ + int rc; + + /* 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 + 1); + if (!rc) { + /* success, it was PEM */ + return 0; + } + + /* failed to parse it */ + ERR(NULL, "Reading downloaded CRL failed."); + return 1; +} + +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; +} + +/** + * @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) +{ + 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; +} + +/** + * @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) +{ + int ret = 0; + struct nc_tls_verify_cb_data *data = cb_data; + char *err; + + if (!*flags) { + /* in-built verification was successful */ + 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 + * 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, case 1) */ + 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, 0, 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; + } + } + + if (ret == -1) { + /* fatal error */ + 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) { + /* chain verify failed, but peer cert can still match */ + return 0; + } else { + /* 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_wrap(void *tls_cfg, struct nc_tls_verify_cb_data *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); +} + +void +nc_client_tls_set_verify_wrap(void *tls_cfg) +{ + mbedtls_ssl_conf_authmode(tls_cfg, MBEDTLS_SSL_VERIFY_REQUIRED); +} + +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)); +} + +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_tls_get_num_sans_wrap(void *sans) +{ + 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}; + const mbedtls_x509_buf *ip; + + *san_value = NULL; + *san_type = NC_TLS_CTN_UNKNOWN; + + /* find the SAN */ + iter = sans; + for (i = 0; i < idx; i++) { + iter = iter->next; + } + + /* parse it */ + rc = mbedtls_x509_parse_subject_alt_name(&iter->buf, &san); + if (rc && (rc != MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE)) { + 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; + } + +cleanup: + mbedtls_x509_free_subject_alt_name(&san); + return ret; +} + +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).", nc_get_mbedtls_str_err(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).", nc_get_mbedtls_str_err(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).", nc_get_mbedtls_str_err(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).", nc_get_mbedtls_str_err(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).", nc_get_mbedtls_str_err(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).", nc_get_mbedtls_str_err(rc)); + return 1; + } + + 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) +{ + 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; +} + +/** + * @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) +{ + 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 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 +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 rc; + } +} + +int +nc_client_tls_handshake_step_wrap(void *tls_session, int sock) +{ + int rc = 0; + struct pollfd pfd = {sock, 0, 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)) { + /* 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; + } +} + +void +nc_tls_ctx_destroy_wrap(struct nc_tls_ctx *tls_ctx) +{ + 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); +} + +void * +nc_tls_import_privkey_file_wrap(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_pkey_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 from file \"%s\" failed (%s).", privkey_path, nc_get_mbedtls_str_err(rc)); + nc_tls_privkey_destroy_wrap(pkey); + return NULL; + } + return pkey; +} + +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; + + NC_CHECK_ARG_RET(NULL, cert_path, key_path, cert, pkey, 1); + + 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, nc_get_mbedtls_str_err(ret)); + goto cleanup; + } + + pk = nc_tls_import_privkey_file_wrap(key_path); + if (!pk) { + ret = 1; + goto cleanup; + } + + *cert = c; + c = NULL; + *pkey = pk; + +cleanup: + nc_tls_cert_destroy_wrap(c); + 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, 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, nc_get_mbedtls_str_err(rc)); + return 1; + } + + return 0; +} + +int +nc_client_tls_load_crl_wrap(void *crl_store, const char *file_path, const char *dir_path) +{ + int rc, ret = 0; + DIR *dir = NULL; + struct dirent *entry; + struct stat st = {0}; + 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, nc_get_mbedtls_str_err(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)); + return 1; + } + + while ((entry = readdir(dir))) { + if (!strcmp(entry->d_name, ".") || !strcmp(entry->d_name, "..")) { + /* skip current and parent directory */ + continue; + } + + 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) { + /* broken symbolic link, ignore */ + free(path); + path = NULL; + continue; + } else { + ERR(NULL, "Failed to get information about \"%s\" (%s).", path, strerror(errno)); + ret = 1; + goto cleanup; + } + } + + if (!S_ISREG(st.st_mode)) { + /* not a regular file, ignore */ + free(path); + path = NULL; + continue; + } + + rc = mbedtls_x509_crl_parse_file(crl_store, path); + if (rc) { + WRN(NULL, "Loading CRL from file \"%s\" failed (%s), skipping.", path, nc_get_mbedtls_str_err(rc)); + } + + free(path); + path = NULL; + } + } + +cleanup: + free(path); + if (dir) { + closedir(dir); + } + return ret; +} + +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).", nc_get_mbedtls_str_err(rc)); + return 1; + } + + return 0; +} + +int +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)) { + 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 = cert; + tls_ctx->pkey = pkey; + tls_ctx->cert_store = cert_store; + tls_ctx->crl_store = crl_store; + return 0; +} + +int +nc_tls_setup_config_from_ctx_wrap(struct nc_tls_ctx *tls_ctx, int side, void *tls_cfg) +{ + 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).", 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 +nc_tls_get_verify_result_wrap(void *tls_session) +{ + return mbedtls_ssl_get_verify_result(tls_session); +} + +char * +nc_tls_verify_error_string_wrap(uint32_t err_code) +{ + return nc_tls_get_verify_err_str(err_code); +} + +void +nc_client_tls_print_connect_err_wrap(int connect_ret, const char *peername, void *UNUSED(tls_session)) +{ + const char *err = nc_get_mbedtls_str_err(connect_ret); + + if (err) { + ERR(NULL, "TLS connection to \"%s\" failed (%s).", peername, err); + } else { + ERR(NULL, "TLS connection to \"%s\" failed.", peername); + } +} + +void +nc_server_tls_print_accept_err_wrap(int accept_ret, void *UNUSED(tls_session)) +{ + 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_tls_is_der_subpubkey_wrap(unsigned char *der, long len) +{ + int ret; + mbedtls_pk_context *pkey; + + pkey = nc_tls_pkey_new_wrap(); + if (!pkey) { + return -1; + } + + ret = mbedtls_pk_parse_subpubkey(&der, der + len, pkey); + nc_tls_privkey_destroy_wrap(pkey); + + return !ret; +} + +int +nc_base64_decode_wrap(const char *base64, unsigned char **bin) +{ + size_t size; + int rc; + + /* 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); + + /* 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; + } + + return size; +} + +int +nc_base64_encode_wrap(const unsigned char *bin, size_t len, char **base64) +{ + size_t size; + int rc; + + 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); + + 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; + } + + 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).", nc_get_mbedtls_str_err(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).", nc_get_mbedtls_str_err(rc)); + rc = -1; + break; + } + } + + return rc; +} + +int +nc_tls_get_num_pending_bytes_wrap(void *tls_session) +{ + return mbedtls_ssl_get_bytes_avail(tls_session); +} + +int +nc_tls_get_fd_wrap(const struct nc_session *session) +{ + return session->ti.tls.ctx.sock ? *session->ti.tls.ctx.sock : -1; +} + +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 */ + ERR(NULL, "Sending TLS close notify failed (%s).", nc_get_mbedtls_str_err(rc)); + return; + } + } +} + +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, nc_get_mbedtls_str_err(rc)); + nc_tls_cert_destroy_wrap(c); + return NULL; + } + + return c; +} + +char * +nc_tls_export_privkey_pem_wrap(void *pkey) +{ + int rc; + char *pem; + size_t size = 128; + + 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; + pem = nc_realloc(pem, size); + NC_CHECK_ERRMEM_RET(!pem, NULL); + } + if (rc < 0) { + ERR(NULL, "Exporting private key to PEM format failed (%s).", nc_get_mbedtls_str_err(rc)); + free(pem); + return NULL; + } + + return pem; +} + +char * +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; + } + + 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_pem_wrap(void *pkey) +{ + int rc; + char *pem; + size_t size = 128; + + 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; + pem = nc_realloc(pem, size); + NC_CHECK_ERRMEM_RET(!pem, NULL); + } + if (rc < 0) { + ERR(NULL, "Exporting public key to PEM format failed (%s).", nc_get_mbedtls_str_err(rc)); + free(pem); + return NULL; + } + + return pem; +} + +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).", nc_get_mbedtls_str_err(rc)); + goto fail; + } + + *e = exp; + *n = mod; + return 0; + +fail: + mbedtls_mpi_free(exp); + mbedtls_mpi_free(mod); + free(exp); + free(mod); + 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) +{ + 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; + + /* 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_params_wrap(void *pkey, void **q, void **q_grp) +{ + 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); + + /* 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; + } + + *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; + } + buf_len <<= 1; + buf = nc_realloc(buf, buf_len); + NC_CHECK_ERRMEM_RET(!buf, 1); + } + if (rc) { + ERR(NULL, "Failed to write EC public key binary (%s).", nc_get_mbedtls_str_err(rc)); + free(buf); + return 1; + } + + *bin = buf; + *bin_len = out_len; + return 0; +} + +void +nc_tls_ec_point_destroy_wrap(void *p) +{ + mbedtls_ecp_point_free(p); + free(p); +} + +void +nc_tls_ec_group_destroy_wrap(void *grp) +{ + mbedtls_ecp_group_free(grp); + free(grp); +} + +int +nc_tls_mpi2bin_wrap(void *mpi, unsigned char **bin, int *bin_len) +{ + int rc; + unsigned char *buf; + int buf_len; + + buf_len = mbedtls_mpi_size(mpi); + buf = malloc(buf_len); + NC_CHECK_ERRMEM_RET(!buf, 1); + + rc = mbedtls_mpi_write_binary(mpi, buf, buf_len); + if (rc) { + ERR(NULL, "Failed to convert MPI to binary (%s).", nc_get_mbedtls_str_err(rc)); + free(buf); + return 1; + } + + *bin = buf; + *bin_len = buf_len; + return 0; +} + +void * +nc_tls_import_pubkey_file_wrap(const char *pubkey_path) +{ + int rc = 0; + mbedtls_pk_context *pk = NULL; + + 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, nc_get_mbedtls_str_err(rc)); + nc_tls_privkey_destroy_wrap(pk); + return NULL; + } + + 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) +{ + 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).", nc_get_mbedtls_str_err(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).", nc_get_mbedtls_str_err(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).", nc_get_mbedtls_str_err(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).", 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).", nc_get_mbedtls_str_err(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).", 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).", nc_get_mbedtls_str_err(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; + } + + /* 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; + } + +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); +} diff --git a/src/session_openssl.c b/src/session_openssl.c new file mode 100644 index 00000000..3fd88980 --- /dev/null +++ b/src/session_openssl.c @@ -0,0 +1,1457 @@ +/** + * @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 +#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 session 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_tls_config_new_wrap(int side) +{ + SSL_CTX *tls_cfg; + + if ((side != NC_SERVER) && (side != NC_CLIENT)) { + ERRINT; + return NULL; + } + + 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; +} + +void +nc_tls_config_destroy_wrap(void *tls_cfg) +{ + SSL_CTX_free(tls_cfg); +} + +void * +nc_tls_cert_new_wrap(void) +{ + 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_destroy_wrap(void *pkey) +{ + EVP_PKEY_free(pkey); +} + +void * +nc_tls_cert_store_new_wrap(void) +{ + 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(void) +{ + return nc_tls_cert_store_new_wrap(); +} + +void +nc_tls_crl_store_destroy_wrap(void *crl_store) +{ + X509_STORE_free(crl_store); +} + +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_add_cert_to_store_wrap(void *cert, void *cert_store) +{ + int rc; + + /* on success increases ref count to cert, so free it */ + rc = X509_STORE_add_cert(cert_store, 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; +} + +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_import_crl_path_wrap(const char *path, void *crl_store) +{ + int ret = 0, rc; + X509_CRL *crl = NULL; + FILE *f; + + f = fopen(path, "r"); + if (!f) { + ERR(NULL, "Unable to open CRL file \"%s\".", 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.", path); + ret = 1; + goto cleanup; + } + +ok: + 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; + } + +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 *crl_store) +{ + int ret = 0; + X509_CRL *crl = NULL; + BIO *bio = NULL; + + 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(crl_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; +} + +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; +} + +/** + * @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) +{ + int ret = 0, depth, err; + struct nc_tls_verify_cb_data *data; + SSL *ssl; + SSL_CTX *ctx; + X509 *cert; + + /* 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 */ + 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, 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 + * 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) || (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); + } 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 { + 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 */ + 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) { + /* chain verify failed */ + return 1; + } else { + /* peer cert did not match */ + return 0; + } + } +} + +void +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_CTX_set_verify(tls_cfg, SSL_VERIFY_PEER, NULL); +} + +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); +} + +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_tls_get_num_sans_wrap(void *sans) +{ + return sk_GENERAL_NAME_num(sans); +} + +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; + + *san_value = NULL; + *san_type = NC_TLS_CTN_UNKNOWN; + + /* find the san */ + san = sk_GENERAL_NAME_value(sans, idx); + if (!san) { + return -1; + } + + /* 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; + } + } 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; + } + } 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 ret; +} + +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 UNUSED(sock)) +{ + 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; + + 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); + 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 *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())); + 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_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; +} + +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; +} + +/** + * @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) +{ + 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 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; + } + + /* 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 */ + 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); +} + +char * +nc_tls_verify_error_string_wrap(uint32_t err_code) +{ + return strdup(X509_verify_cert_error_string(err_code)); +} + +void +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: + 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_err_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_tls_is_der_subpubkey_wrap(unsigned char *der, long len) +{ + int ret; + EVP_PKEY *pkey; + + pkey = d2i_PUBKEY(NULL, (const unsigned char **)&der, len); + if (pkey) { + /* success */ + ret = 1; + } else { + /* fail */ + ret = 0; + } + + EVP_PKEY_free(pkey); + return ret; +} + +int +nc_base64_decode_wrap(const char *base64, unsigned char **bin) +{ + int ret; + + *bin = malloc((strlen(base64) / 4) * 3); + NC_CHECK_ERRMEM_RET(!*bin, -1); + + 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; + } + return ret; +} + +int +nc_base64_encode_wrap(const unsigned char *bin, size_t len, char **base64) +{ + int ret, size; + + /* 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; + } + + *base64 = malloc(size); + NC_CHECK_ERRMEM_RET(!*base64, -1); + + 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; + } + + 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) +{ + 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_tls_get_err_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_tls_get_err_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_get_num_pending_bytes_wrap(void *tls_session) +{ + return SSL_pending(tls_session); +} + +int +nc_tls_get_fd_wrap(const struct nc_session *session) +{ + return session->ti.tls.session ? SSL_get_fd(session->ti.tls.session) : -1; +} + +void +nc_tls_close_notify_wrap(void *tls_session) +{ + SSL_shutdown(tls_session); +} + +void * +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())); + } + + 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_privkey_pem_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_pem_wrap(void *cert) +{ + 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_X509(bio, cert)) { + ERR(NULL, "Exporting the certificate 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_pubkey_pem_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_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 = 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())); + 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; +} + +void +nc_tls_destroy_mpi_wrap(void *mpi) +{ + BN_free(mpi); +} + +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_params_wrap(void *pkey, void **q, void **UNUSED(q_grp)) +{ + 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())); + 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(q)); + NC_CHECK_ERRMEM_RET(!*bin, 1); + + /* convert to binary */ + *bin_len = BN_bn2bin(q, *bin); + return 0; +} + +void +nc_tls_ec_point_destroy_wrap(void *p) +{ + BN_free(p); +} + +void +nc_tls_ec_group_destroy_wrap(void *UNUSED(grp)) +{ + return; +} + +int +nc_tls_mpi2bin_wrap(void *mpi, unsigned char **bin, int *bin_len) +{ + /* 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 * +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; +} + +int +nc_server_tls_get_crl_distpoint_uris_wrap(void *cert_store, char ***uris, int *uri_count) +{ + int ret = 0, i, j, k, gtype; + + 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; + 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; +} + +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); +} diff --git a/src/session_p.h b/src/session_p.h index ae81d866..f8ed2635 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,15 +271,16 @@ 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). */ 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 */ }; @@ -352,15 +353,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 +633,11 @@ 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. * diff --git a/src/session_server.c b/src/session_server.c index 94dd5d50..dbe434af 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, @@ -1600,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); @@ -1649,11 +1650,11 @@ 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: - r = SSL_pending(session->ti.tls); + case NC_TI_TLS: + 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 = 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; @@ -1999,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 */ @@ -2291,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) { @@ -2301,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; @@ -2481,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; @@ -2492,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.h b/src/session_server.h index d386b625..b6059019 100644 --- a/src/session_server.h +++ b/src/session_server.h @@ -24,17 +24,16 @@ 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,14 +552,13 @@ 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. * * 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 e73dfe22..f7fc74dc 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; @@ -1011,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; @@ -1023,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; } /** @@ -1037,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; @@ -1046,8 +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) { - ERR(NULL, "Unable to decode base64."); + if (nc_base64_decode_wrap(base64, &bin) == -1) { ret = 1; goto cleanup; } @@ -1267,7 +1264,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; @@ -1713,7 +1710,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."); @@ -1817,7 +1814,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; @@ -1875,7 +1872,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/src/session_server_tls.c b/src/session_server_tls.c index 2b562b1b..4ac37361 100644 --- a/src/session_server_tls.c +++ b/src/session_server_tls.c @@ -24,287 +24,245 @@ #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; + char *buf = NULL; + void *cert; - 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); - return NULL; - } - - out = PEM_read_bio_X509(bio, NULL, NULL, NULL); - if (!out) { - free(buf); - BIO_free(bio); + ERRMEM; return NULL; } + cert = nc_tls_pem_to_cert_wrap(buf); free(buf); - BIO_free(bio); - return out; + return cert; } -static EVP_PKEY * -base64der_to_privatekey(const char *in, const char *key_str) +static void * +nc_base64der_to_privkey(const char *in, const char *key_str) { - EVP_PKEY *out; - char *buf; - BIO *bio; + char *buf = NULL; + void *pkey; - if (in == NULL) { - return NULL; - } + NC_CHECK_ARG_RET(NULL, in, NULL); - if (!key_str) { - /* avoid writing (null) for possibly unknown key formats */ - key_str = ""; - } 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; } - bio = BIO_new_mem_buf(buf, strlen(buf)); - if (!bio) { - free(buf); - return NULL; + + pkey = nc_tls_pem_to_privkey_wrap(buf); + free(buf); + return pkey; +} + +static 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]); } + sprintf(hex + (i * 3), "%02x", digest[i]); - out = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); - if (!out) { - free(buf); - BIO_free(bio); + return hex; +} + +static 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; } - 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) +static char * +nc_server_tls_sha1(void *cert) { - ASN1_BIT_STRING *bitstr1, *bitstr2; + int rc; + unsigned int buf_len = 20; + unsigned char buf[buf_len]; - bitstr1 = X509_get0_pubkey_bitstr(cert1); - bitstr2 = X509_get0_pubkey_bitstr(cert2); + /* compute SHA-1 hash of cert and store it in buf */ + rc = nc_server_tls_sha1_wrap(cert, buf); + if (rc) { + return NULL; + } - if (!bitstr1 || !bitstr2 || (bitstr1->length != bitstr2->length) || - memcmp(bitstr1->data, bitstr2->data, bitstr1->length)) { - return 0; + /* convert the hash to hex */ + return nc_server_tls_digest_to_hex(buf, buf_len); +} + +static 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; } - 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) +static char * +nc_server_tls_sha256(void *cert) { - STACK_OF(GENERAL_NAME) * san_names; - GENERAL_NAME *san_name; - ASN1_OCTET_STRING *ip; - int i, san_count; - char *subject, *common_name; + int rc; + unsigned int buf_len = 32; + unsigned char buf[buf_len]; - *username = NULL; + /* compute SHA-256 hash of cert and store it in buf */ + rc = nc_server_tls_sha256_wrap(cert, buf); + if (rc) { + return 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; - } + /* convert the hash to hex */ + return nc_server_tls_digest_to_hex(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); +static char * +nc_server_tls_sha384(void *cert) +{ + int rc; + unsigned int buf_len = 48; + unsigned char buf[buf_len]; - /* 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; - } + /* compute SHA-384 hash of cert and store it in buf */ + rc = nc_server_tls_sha384_wrap(cert, buf); + if (rc) { + return NULL; + } - /* 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; - } + /* convert the hash to hex */ + return nc_server_tls_digest_to_hex(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); +static char * +nc_server_tls_sha512(void *cert) +{ + int rc; + unsigned int buf_len = 64; + unsigned char buf[buf_len]; - 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 +270,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 +281,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; } - - /* 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 +399,106 @@ 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 +nc_server_tls_get_username_from_cert(void *cert, NC_TLS_CTN_MAPTYPE map_type, char **username) { - 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; - } + char *subject, *cn, *san_value = NULL, rdn_separator; + void *sans; + int i, nsans = 0, rc; + NC_TLS_CTN_MAPTYPE san_type = 0; - /* 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; - } +#ifdef HAVE_LIBMEDTLS + rdn_separator = ','; +#else + rdn_separator = '/'; +#endif - 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; + if (map_type == NC_TLS_CTN_COMMON_NAME) { + subject = nc_server_tls_get_subject_wrap(cert); + if (!subject) { + return -1; } - } - /* 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; - } + cn = strstr(subject, "CN="); + if (!cn) { + WRN(NULL, "Certificate does not include the commonName field."); + free(subject); + return 1; } - } -cleanup: - X509_STORE_CTX_free(store_ctx); - X509_OBJECT_free(obj); - return ret; -} + /* 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); -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; + 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; + } - *certs = NULL; - *cert_count = 0; + /* got a type that we dont care about */ + continue; + } - /* lookup name */ - for (i = 0; i < ts->cert_bag_count; i++) { - if (!strcmp(referenced_name, ts->cert_bags[i].name)) { - break; + if ((map_type == NC_TLS_CTN_SAN_ANY) || (map_type == san_type)) { + /* found a match */ + *username = san_value; + break; + } + free(san_value); } - } - if (i == ts->cert_bag_count) { - ERR(NULL, "Truststore entry \"%s\" not found.", referenced_name); - return -1; + 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; + } } - *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 +512,147 @@ 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); + /* 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) { - /* 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_peer_cert(void *peer_cert, struct nc_server_tls_opts *opts) { - 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 rc; 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())); + rc = _nc_server_tls_verify_peer_cert(peer_cert, &opts->ee_certs); + if (!rc) { 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); - } + if (opts->referenced_endpt_name) { + if (nc_server_get_referenced_endpt(opts->referenced_endpt_name, &referenced_endpt)) { + ERRINT; + return -1; + } - /* 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) { + rc = _nc_server_tls_verify_peer_cert(peer_cert, &referenced_endpt->opts.tls->ee_certs); + if (!rc) { 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; - } - } + return 1; +} - /* no match, fail */ - ERR(session, "Cert verify: fail (%s).", X509_verify_cert_error_string(X509_STORE_CTX_get_error(x509_ctx))); +int +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; + struct nc_server_tls_opts *opts = cb_data->opts; + struct nc_session *session = cb_data->session; + struct nc_endpt *referenced_endpt; + + if (session->username) { + /* already verified */ return 0; } - /* print cert verify info */ - depth = X509_STORE_CTX_get_error_depth(x509_ctx); - VRB(session, "Cert verify: depth %d.", depth); + 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; + } - 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; + 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 (!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); + 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; + /* get matching ctn entries */ + 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; + /* 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_GOTO(!session->username, ret = -1, cleanup); + } else { + 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; + } } - 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 (session->username && 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 +669,15 @@ 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; + void *cert = NULL; + void *pkey = NULL; - NC_CHECK_ARG_RET(NULL, tls_ctx, opts, -1); + *srv_cert = *srv_pkey = NULL; /* get data needed for setting the server cert */ if (opts->store == NC_STORE_LOCAL) { @@ -877,155 +687,32 @@ 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) { ERR(NULL, "Server certificate not configured."); - ret = -1; - goto cleanup; + return 1; } - /* 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_privkey(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,127 +732,86 @@ 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_CHECK_ARG_RET(session, handle, data, -1); - - *handle = NULL; - - *handle = curl_easy_init(); - if (!*handle) { - ERR(session, "Initializing CURL failed."); - return -1; - } - - if (curl_easy_setopt(*handle, CURLOPT_WRITEFUNCTION, nc_server_tls_curl_cb)) { - ERR(session, "Setting curl callback failed."); - return -1; - } - - if (curl_easy_setopt(*handle, CURLOPT_WRITEDATA, data)) { - ERR(session, "Setting curl callback data failed."); - return -1; - } - - return 0; -} - -static int -nc_server_tls_curl_fetch(struct nc_session *session, CURL *handle, const char *url) +nc_server_tls_curl_fetch(CURL *handle, const char *url) { char err_buf[CURL_ERROR_SIZE]; /* set uri */ if (curl_easy_setopt(handle, CURLOPT_URL, url)) { - ERR(session, "Setting URI \"%s\" to download CRL from failed.", url); - return -1; + ERR(NULL, "Setting URI \"%s\" to download CRL from failed.", url); + return 1; } /* set err buf */ if (curl_easy_setopt(handle, CURLOPT_ERRORBUFFER, err_buf)) { - ERR(session, "Setting CURL error buffer option failed."); - return -1; + ERR(NULL, "Setting CURL error buffer option failed."); + return 1; } /* download */ if (curl_easy_perform(handle)) { - ERR(session, "Downloading CRL from \"%s\" failed (%s).", url, err_buf); - return -1; + ERR(NULL, "Downloading CRL from \"%s\" failed (%s).", url, err_buf); + return 1; } return 0; } static int -nc_server_tls_add_crl_to_store(struct nc_session *session, struct nc_curl_data *downloaded, X509_STORE *store) +nc_server_tls_curl_init(CURL **handle, struct nc_curl_data *data) { - int ret = 0; - X509_CRL *crl = NULL; - BIO *bio = NULL; + NC_CHECK_ARG_RET(NULL, handle, data, -1); - /* try DER first */ - crl = d2i_X509_CRL(NULL, (const unsigned char **) &downloaded->data, downloaded->size); - if (crl) { - /* it was DER */ - goto ok; - } + *handle = NULL; - /* 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; - goto cleanup; + *handle = curl_easy_init(); + if (!*handle) { + ERR(NULL, "Initializing CURL failed."); + return 1; } - /* 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; - goto cleanup; + if (curl_easy_setopt(*handle, CURLOPT_WRITEFUNCTION, nc_server_tls_curl_cb)) { + ERR(NULL, "Setting curl callback failed."); + return 1; } -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; + if (curl_easy_setopt(*handle, CURLOPT_WRITEDATA, data)) { + ERR(NULL, "Setting curl callback data failed."); + return 1; } - /* ok */ - ret = 0; -cleanup: - X509_CRL_free(crl); - BIO_free(bio); - return ret; + return 0; } static int -nc_server_tls_crl_url(struct nc_session *session, const char *url, X509_STORE *store) +nc_server_tls_crl_path(const char *path, void *crl_store) +{ + return nc_tls_import_crl_path_wrap(path, crl_store); +} + +static int +nc_server_tls_crl_url(const char *url, 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, crl_store); if (ret) { goto cleanup; } @@ -1176,153 +822,125 @@ nc_server_tls_crl_url(struct nc_session *session, const char *url, X509_STORE *s } static int -nc_server_tls_crl_cert_ext(struct nc_session *session, X509_STORE *cert_store, X509_STORE *crl_store) +nc_server_tls_crl_cert_ext(void *cert_store, void *crl_store) { - int ret = 0, i, j, k, gtype; + int ret = 0; 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; + char **uris = NULL; + int uri_count = 0, i; /* init curl */ - ret = nc_server_tls_curl_init(session, &handle, &downloaded); + ret = nc_server_tls_curl_init(&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; + /* 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; } - /* 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 */ + 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; } - /* 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; - } + /* 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, 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_tls_store_set_crl(struct nc_session *session, struct nc_server_tls_opts *opts, X509_STORE *store) +int +nc_server_tls_load_crl(struct nc_server_tls_opts *opts, void *cert_store, void *crl_store) { - if (!opts->crl_store) { - /* first call on this endpoint */ - opts->crl_store = X509_STORE_new(); - NC_CHECK_ERRMEM_GOTO(!opts->crl_store, , fail); - } - if (opts->crl_path) { - if (nc_server_tls_crl_path(session, opts->crl_path, opts->crl_store)) { - goto fail; + if (nc_server_tls_crl_path(opts->crl_path, crl_store)) { + return 1; } } else if (opts->crl_url) { - if (nc_server_tls_crl_url(session, opts->crl_url, opts->crl_store)) { - goto fail; + if (nc_server_tls_crl_url(opts->crl_url, crl_store)) { + return 1; } } else { - if (nc_server_tls_crl_cert_ext(session, store, opts->crl_store)) { - goto fail; + if (nc_server_tls_crl_cert_ext(cert_store, crl_store)) { + return 1; } } return 0; +} -fail: - return -1; +int +nc_server_tls_load_trusted_certs(struct nc_cert_grouping *ca_certs, void *cert_store) +{ + struct nc_certificate *certs; + uint16_t i, cert_count; + void *cert; + + 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++) { + /* 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; + } + } + + 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; + char *err; /* 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."); + } else if (verify) { + err = nc_tls_verify_error_string_wrap(verify); + ERR(NULL, "Client certificate error (%s).", err); + free(err); } 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_err_wrap(accept_ret, tls_session); } return accept_ret; @@ -1331,142 +949,145 @@ 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; - 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_tls_config_new_wrap(NC_SERVER); + 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; + ERR(session, "Referenced endpoint \"%s\" not found.", opts->referenced_endpt_name); + goto fail; } - 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); - - /* set certificate authority certs */ - if (nc_tls_store_set_trusted_certs(cert_store, &opts->ca_certs)) { - goto error; - } + 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 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; + /* 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 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; + /* 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; } } - session->ti_type = NC_TI_OPENSSL; - session->ti.tls = SSL_new(tls_ctx); + /* set supported cipher suites */ + if (opts->ciphers) { + nc_server_tls_set_cipher_suites_wrap(tls_cfg, opts->ciphers); + } - /* context can be freed already, trusted certs must be freed manually */ - SSL_CTX_free(tls_ctx); - tls_ctx = NULL; + /* set verify flags, callback and its data */ + nc_server_tls_set_verify_wrap(tls_cfg, &cb_data); - if (!session->ti.tls) { - ERR(session, "Failed to create TLS structure from context."); - goto error; + /* init TLS context and store data which may be needed later in it */ + if (nc_tls_init_ctx_wrap(sock, srv_cert, srv_pkey, cert_store, crl_store, &session->ti.tls.ctx)) { + goto fail; } - /* 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); - } + /* memory is managed by context now */ + srv_cert = srv_pkey = cert_store = crl_store = NULL; + + /* setup config from ctx */ + if (nc_tls_setup_config_from_ctx_wrap(&session->ti.tls.ctx, NC_SERVER, tls_cfg)) { + goto fail; } + session->ti.tls.config = tls_cfg; + tls_cfg = NULL; - /* 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); + /* fill session data and create TLS session from config */ + session->ti_type = NC_TI_TLS; + if (!(session->ti.tls.session = nc_tls_session_new_wrap(session->ti.tls.config))) { + goto fail; } - SSL_set_fd(session->ti.tls, sock); + /* set session fd */ + nc_server_tls_set_fd_wrap(session->ti.tls.session, sock, &session->ti.tls.ctx); + 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_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; + } else { + return -1; + } } diff --git a/src/session_wrapper.h b/src/session_wrapper.h new file mode 100644 index 00000000..273bc2c2 --- /dev/null +++ b/src/session_wrapper.h @@ -0,0 +1,733 @@ +/** + * @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_ + +#include + +#include "config.h" + +#ifdef HAVE_MBEDTLS + +#include +#include +#include +#include +#include +#include + +/** + * @brief Context from which a TLS session may be created. + */ +struct nc_tls_ctx { + 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 + +#include +#include +#include + +/** + * @brief Context from which a TLS session may be created. + */ +struct nc_tls_ctx { + 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; /**< NETCONF session. */ + struct nc_server_tls_opts *opts; /**< TLS server options. */ + struct nc_ctn_data { + 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; +}; + +/** + * @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); + +/** + * @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(void); + +/** + * @brief Destroys a TLS certificate. + * + * @param[in] cert TLS certificate to destroy. + */ +void nc_tls_cert_destroy_wrap(void *cert); + +/** + * @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(void); + +/** + * @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); + +/** + * @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); + +/** + * @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); + +/** + * @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); + +/** + * @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); + +/** + * @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] 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 trusted, 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); + +/** + * @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); + +#ifdef NC_ENABLED_SSH_TLS + +/** + * @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); + +#endif + +/** + * @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); + +/** + * @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); + +/** + * @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); + +/** + * @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); + +/** + * @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); + +/** + * @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); + +/** + * @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); + +/** + * @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); + +/** + * @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); + +/** + * @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. + * + * @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, 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); + +/** + * @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); + +/** + * @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); + +/** + * @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); + +/** + * @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); + +/** + * @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); + +/** + * @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); + +/** + * @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); + +/** + * @brief Destroy an EC point. + * + * @param[in] p EC point. + */ +void nc_tls_ec_point_destroy_wrap(void *p); + +/** + * @brief Destroy an EC group. + * + * @param[in] grp EC group. + */ +void nc_tls_ec_group_destroy_wrap(void *grp); + +/** + * @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 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 a2896fc6..a0ce5295 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); @@ -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_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 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);